Sync push
This commit is contained in:
parent
9eb2bc7fda
commit
d4e6b2e8cc
3 changed files with 41 additions and 18 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -13,6 +13,7 @@
|
|||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
/app/release
|
||||
|
||||
# Signing / secrets
|
||||
*.jks
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
package com.keylightpiano.pianotimeline.ui
|
||||
|
||||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.basicMarquee
|
||||
|
|
@ -31,10 +34,12 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
|
|
@ -42,6 +47,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.unit.dp
|
||||
import com.keylightpiano.pianotimeline.R
|
||||
import com.keylightpiano.pianotimeline.domain.Recording
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
private val NotesGreen = Color(0xFF25B300)
|
||||
private val NewBadgeRed = Color(0xFFE53935)
|
||||
|
|
@ -145,15 +151,38 @@ fun RecordingItem(
|
|||
}
|
||||
}
|
||||
|
||||
// Favorite star
|
||||
IconButton(onClick = onFavoriteClick, modifier = Modifier.size(36.dp)) {
|
||||
// Favorite star with a spring "pop" on each tap.
|
||||
val starScale = remember { Animatable(1f) }
|
||||
val starScope = rememberCoroutineScope()
|
||||
IconButton(
|
||||
onClick = {
|
||||
onFavoriteClick()
|
||||
starScope.launch {
|
||||
// Pop up then settle back with a springy overshoot.
|
||||
starScale.snapTo(0.6f)
|
||||
starScale.animateTo(
|
||||
targetValue = 1f,
|
||||
animationSpec = spring(
|
||||
dampingRatio = Spring.DampingRatioMediumBouncy,
|
||||
stiffness = Spring.StiffnessMedium
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.size(36.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (recording.isFavorite) Icons.Filled.Star
|
||||
else Icons.Outlined.StarBorder,
|
||||
contentDescription = stringResource(if (recording.isFavorite) R.string.cd_unfavorite else R.string.cd_favorite),
|
||||
tint = if (recording.isFavorite) Color(0xFFFFC107)
|
||||
else MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.size(20.dp)
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.graphicsLayer {
|
||||
scaleX = starScale.value
|
||||
scaleY = starScale.value
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.withFrameNanos
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
|
|
@ -111,20 +110,13 @@ fun RecordingListScreen(
|
|||
wasRefreshing = isRefreshing
|
||||
}
|
||||
|
||||
// Preserve scroll position across a favorite toggle (which reorders the list).
|
||||
// When the user stars/unstars an item, we snapshot the current top item's
|
||||
// index+offset, then restore it after the list settles so the viewport
|
||||
// doesn't jump to follow the moved item.
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
// Favoriting reorders the list (favorites float to the top). We let the
|
||||
// LazyColumn's animateItem() handle the visual feedback — the toggled card
|
||||
// visibly slides to its new position rather than vanishing under the finger.
|
||||
// We intentionally do NOT force-restore scroll here; the viewport stays put
|
||||
// while items animate to their new spots.
|
||||
val onFavoriteToggle: (Recording) -> Unit = { recording ->
|
||||
val anchorIndex = listState.firstVisibleItemIndex
|
||||
val anchorOffset = listState.firstVisibleItemScrollOffset
|
||||
vm.toggleFavorite(recording)
|
||||
coroutineScope.launch {
|
||||
// Wait one frame for the reordered list to be applied, then restore.
|
||||
withFrameNanos { }
|
||||
listState.scrollToItem(anchorIndex, anchorOffset)
|
||||
}
|
||||
}
|
||||
|
||||
// Deep link: when the VM requests scrolling to a specific entry, find its
|
||||
|
|
@ -202,7 +194,7 @@ fun RecordingListScreen(
|
|||
Icon(
|
||||
imageVector = Icons.Default.Search,
|
||||
contentDescription = stringResource(R.string.cd_search_filter),
|
||||
tint = if (hasActiveFilter) MaterialTheme.colorScheme.primary
|
||||
tint = if (hasActiveFilter || showControls) MaterialTheme.colorScheme.primary
|
||||
else MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
|
|
@ -418,7 +410,8 @@ fun RecordingListScreen(
|
|||
} else null,
|
||||
onCopyLink = {
|
||||
copyLink(context, vm.audioShareUrl(recording))
|
||||
}
|
||||
},
|
||||
modifier = Modifier.animateItem()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue