feat: fetch tracks on-demand, logout button

This commit is contained in:
2026-05-14 03:03:17 +02:00
parent e2ab026e84
commit 83e77f3d0c
7 changed files with 92 additions and 2 deletions

View File

@@ -128,7 +128,7 @@ class LockstepViewModel(
app.setSpotifyAccessTokenForApi(token)
return withContext(Dispatchers.IO) {
try {
app.playlistRepository.syncInitial()
app.playlistRepository.syncDelta(false)
null
} catch (e: LockstepApiException) {
e.message ?: "Sync failed"
@@ -138,6 +138,27 @@ class LockstepViewModel(
}
}
suspend fun syncPlaylistDetailForLibraryOpen(playlistId: String): String? {
val token = spotifyAccessToken.value
if (token.isNullOrBlank()) {
return "Not signed in"
}
app.setSpotifyAccessTokenForApi(token)
return withContext(Dispatchers.IO) {
if (app.playlistRepository.getTracks(playlistId).isNotEmpty()) {
return@withContext null
}
try {
app.playlistRepository.syncPlaylistDetail(playlistId)
null
} catch (e: LockstepApiException) {
e.message ?: "Load failed"
} catch (e: IOException) {
e.message ?: "Load failed"
}
}
}
fun completeOnboarding() {
viewModelScope.launch {
prefs.setOnboardingComplete(true)
@@ -150,6 +171,14 @@ class LockstepViewModel(
}
}
/** Clears the Spotify token and shows the first-run flow again so you can sign in with a fresh token. */
fun logoutSpotifyAndRestartOnboarding() {
viewModelScope.launch {
prefs.setSpotifyAccessToken(null)
prefs.setOnboardingComplete(false)
}
}
private suspend fun upsertPairing(
playlistId: String,
trackId: String,

View File

@@ -80,6 +80,7 @@ fun LockstepAppNavHost(
}
composable(Routes.Settings) {
SettingsScreen(
viewModel = viewModel,
onBack = { navController.popBackStack() },
)
}

View File

@@ -83,6 +83,11 @@ fun LibraryScreen(
.fillMaxWidth()
.clickable {
scope.launch {
val err = viewModel.syncPlaylistDetailForLibraryOpen(playlist.id)
if (err != null) {
Toast.makeText(context, err, Toast.LENGTH_LONG).show()
return@launch
}
val hasPaired =
viewModel.hasPairedTracks(playlist.id)
onPlaylistSelected(playlist, hasPaired)

View File

@@ -6,6 +6,8 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@@ -17,11 +19,13 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import at.lockstep.player.LockstepViewModel
import at.lockstep.player.R
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SettingsScreen(
viewModel: LockstepViewModel,
onBack: () -> Unit,
modifier: Modifier = Modifier,
) {
@@ -51,6 +55,21 @@ fun SettingsScreen(
text = context.getString(R.string.settings_stub_body),
style = MaterialTheme.typography.bodyLarge,
)
Text(
text = context.getString(R.string.settings_logout_spotify_help),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
Button(
onClick = { viewModel.logoutSpotifyAndRestartOnboarding() },
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.errorContainer,
contentColor = MaterialTheme.colorScheme.onErrorContainer,
),
) {
Text(text = context.getString(R.string.settings_logout_spotify))
}
}
}
}

View File

@@ -26,6 +26,8 @@
<string name="settings_title">Settings</string>
<string name="settings_stub_body">More controls will land here in a later milestone.</string>
<string name="settings_logout_spotify">Sign out of Spotify</string>
<string name="settings_logout_spotify_help">Clears your stored access token and returns to the welcome steps so you can log in again. Use this if the app gets HTTP 401 from the server.</string>
<string name="pairing_title">Pair local MP3s</string>
<string name="pairing_choose_folder">Choose folder of MP3s</string>