Sync push

This commit is contained in:
OmniaX-Dev 2026-06-24 07:54:59 +02:00
parent 9eb2bc7fda
commit d4e6b2e8cc
3 changed files with 41 additions and 18 deletions

1
.gitignore vendored
View file

@ -13,6 +13,7 @@
.externalNativeBuild
.cxx
local.properties
/app/release
# Signing / secrets
*.jks

View file

@ -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
}
)
}

View file

@ -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()
)
}
}