fix(web): /register reachable for bootstrap admin (closes #376) #32

Merged
bvandeusen merged 262 commits from dev into main 2026-05-08 18:01:39 -04:00
2 changed files with 53 additions and 1 deletions
Showing only changes of commit 05c7d922c4 - Show all commits
@@ -38,6 +38,7 @@ import com.fabledsword.minstrel.nav.ArtistDetail
import com.fabledsword.minstrel.player.PlayerController import com.fabledsword.minstrel.player.PlayerController
import com.fabledsword.minstrel.shared.widgets.EmptyState import com.fabledsword.minstrel.shared.widgets.EmptyState
import com.fabledsword.minstrel.shared.widgets.HorizontalScrollRow import com.fabledsword.minstrel.shared.widgets.HorizontalScrollRow
import com.fabledsword.minstrel.shared.widgets.LikeButton
import com.fabledsword.minstrel.shared.widgets.LoadingCentered import com.fabledsword.minstrel.shared.widgets.LoadingCentered
import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold
import com.fabledsword.minstrel.shared.widgets.TrackCoverThumb import com.fabledsword.minstrel.shared.widgets.TrackCoverThumb
@@ -88,6 +89,16 @@ class LikedTabViewModel @Inject constructor(
runCatching { repository.refreshIds() } runCatching { repository.refreshIds() }
} }
/**
* Unlike a track from inline heart-tap. The LikesRepository write
* routes through the MutationQueue so offline taps replay later.
*/
fun unlikeTrack(trackId: String) {
viewModelScope.launch {
repository.toggleLike(LikesRepository.ENTITY_TRACK, trackId, liked = false)
}
}
val uiState: StateFlow<LikedTabUiState> = combine( val uiState: StateFlow<LikedTabUiState> = combine(
repository.observeLikedArtists(), repository.observeLikedArtists(),
repository.observeLikedAlbums(), repository.observeLikedAlbums(),
@@ -148,6 +159,7 @@ fun LikedTab(
onTrackClick = { index -> onTrackClick = { index ->
viewModel.playTracks(s.sections.tracks, index) viewModel.playTracks(s.sections.tracks, index)
}, },
onUnlike = viewModel::unlikeTrack,
onNavigateToAlbum = { id -> navController.navigate(AlbumDetail(id)) }, onNavigateToAlbum = { id -> navController.navigate(AlbumDetail(id)) },
onNavigateToArtist = { id -> navController.navigate(ArtistDetail(id)) }, onNavigateToArtist = { id -> navController.navigate(ArtistDetail(id)) },
) )
@@ -162,6 +174,7 @@ private fun LikedContent(
onArtistClick: (String) -> Unit, onArtistClick: (String) -> Unit,
onAlbumClick: (String) -> Unit, onAlbumClick: (String) -> Unit,
onTrackClick: (Int) -> Unit, onTrackClick: (Int) -> Unit,
onUnlike: (String) -> Unit,
onNavigateToAlbum: (String) -> Unit, onNavigateToAlbum: (String) -> Unit,
onNavigateToArtist: (String) -> Unit, onNavigateToArtist: (String) -> Unit,
) { ) {
@@ -202,6 +215,7 @@ private fun LikedContent(
track = track, track = track,
nowPlaying = track.id == playingTrackId, nowPlaying = track.id == playingTrackId,
onClick = { onTrackClick(index) }, onClick = { onTrackClick(index) },
onUnlike = { onUnlike(track.id) },
onNavigateToAlbum = onNavigateToAlbum, onNavigateToAlbum = onNavigateToAlbum,
onNavigateToArtist = onNavigateToArtist, onNavigateToArtist = onNavigateToArtist,
) )
@@ -239,6 +253,7 @@ private fun LikedTrackRow(
track: TrackRef, track: TrackRef,
nowPlaying: Boolean, nowPlaying: Boolean,
onClick: () -> Unit, onClick: () -> Unit,
onUnlike: () -> Unit,
onNavigateToAlbum: (String) -> Unit, onNavigateToAlbum: (String) -> Unit,
onNavigateToArtist: (String) -> Unit, onNavigateToArtist: (String) -> Unit,
) { ) {
@@ -272,6 +287,7 @@ private fun LikedTrackRow(
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
) )
} }
LikeButton(liked = true, onToggle = onUnlike)
TrackActionsButton( TrackActionsButton(
track = track, track = track,
onNavigateToAlbum = onNavigateToAlbum, onNavigateToAlbum = onNavigateToAlbum,
@@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
@@ -26,10 +27,14 @@ import androidx.compose.material3.SegmentedButton
import androidx.compose.material3.SegmentedButtonDefaults import androidx.compose.material3.SegmentedButtonDefaults
import androidx.compose.material3.SingleChoiceSegmentedButtonRow import androidx.compose.material3.SingleChoiceSegmentedButtonRow
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
@@ -61,6 +66,7 @@ fun SettingsScreen(
) { ) {
val state by viewModel.state.collectAsStateWithLifecycle() val state by viewModel.state.collectAsStateWithLifecycle()
val themeMode by themeVm.themeMode.collectAsStateWithLifecycle() val themeMode by themeVm.themeMode.collectAsStateWithLifecycle()
var showSignOutConfirm by remember { mutableStateOf(false) }
LaunchedEffect(state.signedOut) { LaunchedEffect(state.signedOut) {
if (state.signedOut) { if (state.signedOut) {
@@ -118,9 +124,39 @@ fun SettingsScreen(
AppearanceCard(themeMode = themeMode, onPick = themeVm::setThemeMode) AppearanceCard(themeMode = themeMode, onPick = themeVm::setThemeMode)
StorageCard() StorageCard()
AboutCard() AboutCard()
SignOutButton(isSigningOut = state.isSigningOut, onSignOut = viewModel::signOut) SignOutButton(
isSigningOut = state.isSigningOut,
onSignOut = { showSignOutConfirm = true },
)
} }
} }
if (showSignOutConfirm) {
AlertDialog(
onDismissRequest = { showSignOutConfirm = false },
title = { Text("Sign out?") },
text = {
Text(
"Sign out of this server? Your downloaded music and " +
"settings on this device will be removed.",
)
},
confirmButton = {
Button(
onClick = {
showSignOutConfirm = false
viewModel.signOut()
},
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.errorContainer,
contentColor = MaterialTheme.colorScheme.onErrorContainer,
),
) { Text("Sign out") }
},
dismissButton = {
TextButton(onClick = { showSignOutConfirm = false }) { Text("Cancel") }
},
)
}
} }
@Composable @Composable