feat(android): Settings — My Requests + Admin tiles

Audit v2 reachability gap: Requests screen was orphaned (no entry
point) and admins had no Settings-side affordance for admin tools.
Both now surface as ListTile-style cards in the Settings stack with
Lucide chevron + leading icon, matching Flutter's layout. Admin tile
gated on SettingsState.isAdmin (sourced from AuthController.
currentUser, plumbed through the combine() chain).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-26 19:59:20 -04:00
parent 8a2279d5df
commit f7c3bd2dcf
2 changed files with 68 additions and 0 deletions
@@ -1,9 +1,11 @@
package com.fabledsword.minstrel.settings.ui
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@@ -26,15 +28,22 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavHostController
import com.composables.icons.lucide.ChevronRight
import com.composables.icons.lucide.ListMusic
import com.composables.icons.lucide.LogOut
import com.composables.icons.lucide.Lucide
import com.composables.icons.lucide.Shield
import com.fabledsword.minstrel.BuildConfig
import com.fabledsword.minstrel.nav.Admin
import com.fabledsword.minstrel.nav.Requests
import com.fabledsword.minstrel.nav.Settings as SettingsRoute
import com.fabledsword.minstrel.nav.ServerUrl
import com.fabledsword.minstrel.shared.widgets.MainAppBarActions
@@ -82,6 +91,20 @@ fun SettingsScreen(
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
AccountCard(username = state.username, serverUrl = state.serverUrl)
NavTile(
icon = Lucide.ListMusic,
title = "My requests",
subtitle = "Track what you've asked Minstrel to add",
onClick = { navController.navigate(Requests) },
)
if (state.isAdmin) {
NavTile(
icon = Lucide.Shield,
title = "Admin",
subtitle = "Manage requests, quarantine, and users",
onClick = { navController.navigate(Admin) },
)
}
AppearanceCard(themeMode = themeMode, onPick = themeVm::setThemeMode)
StorageCard()
AboutCard()
@@ -90,6 +113,49 @@ fun SettingsScreen(
}
}
@Composable
private fun NavTile(
icon: ImageVector,
title: String,
subtitle: String,
onClick: () -> Unit,
) {
ElevatedCard(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onClick),
) {
Row(
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
imageVector = icon,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSurface,
)
Spacer(Modifier.size(16.dp))
Column(modifier = Modifier.weight(1f)) {
Text(
text = title,
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onSurface,
)
Text(
text = subtitle,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
Icon(
imageVector = Lucide.ChevronRight,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
}
}
@Composable
private fun AccountCard(username: String, serverUrl: String) {
ElevatedCard(modifier = Modifier.fillMaxWidth()) {
@@ -16,6 +16,7 @@ import javax.inject.Inject
data class SettingsState(
val serverUrl: String = "",
val username: String = "",
val isAdmin: Boolean = false,
val isSigningOut: Boolean = false,
val signedOut: Boolean = false,
)
@@ -45,6 +46,7 @@ class SettingsViewModel @Inject constructor(
SettingsState(
serverUrl = url,
username = user?.username.orEmpty(),
isAdmin = user?.isAdmin == true,
isSigningOut = t.isSigningOut,
signedOut = t.signedOut,
)