diff --git a/android/app/src/main/java/com/fabledsword/minstrel/shared/widgets/AppBarActionsViewModel.kt b/android/app/src/main/java/com/fabledsword/minstrel/shared/widgets/AppBarActionsViewModel.kt new file mode 100644 index 00000000..e93242e6 --- /dev/null +++ b/android/app/src/main/java/com/fabledsword/minstrel/shared/widgets/AppBarActionsViewModel.kt @@ -0,0 +1,33 @@ +package com.fabledsword.minstrel.shared.widgets + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.fabledsword.minstrel.auth.AuthController +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import javax.inject.Inject + +private const val SHARE_STOP_TIMEOUT_MS = 5_000L + +/** + * Backs [MainAppBarActions] with the current user's admin flag so the + * overflow "Admin" item appears for every admin without each shell + * screen having to plumb the value through. Sourced from + * [AuthController.currentUser] so it stays live across sign-in, + * sign-out, and role changes pushed via rehydration. + */ +@HiltViewModel +class AppBarActionsViewModel @Inject constructor( + authController: AuthController, +) : ViewModel() { + val isAdmin: StateFlow = authController.currentUser + .map { it?.isAdmin == true } + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(SHARE_STOP_TIMEOUT_MS), + initialValue = false, + ) +} diff --git a/android/app/src/main/java/com/fabledsword/minstrel/shared/widgets/MainAppBarActions.kt b/android/app/src/main/java/com/fabledsword/minstrel/shared/widgets/MainAppBarActions.kt index 98526999..a1707c59 100644 --- a/android/app/src/main/java/com/fabledsword/minstrel/shared/widgets/MainAppBarActions.kt +++ b/android/app/src/main/java/com/fabledsword/minstrel/shared/widgets/MainAppBarActions.kt @@ -11,6 +11,8 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavHostController import com.composables.icons.lucide.House import com.composables.icons.lucide.LibraryBig @@ -33,7 +35,9 @@ import com.fabledsword.minstrel.nav.Settings as SettingsRoute * [Home] [Library] [Search] [ ⋮ Playlists / Discover / Settings / Admin ] * * The icon for the screen the user is currently on is suppressed so - * the action set looks contextual. + * the action set looks contextual. The "Admin" overflow item is + * sourced internally from [AppBarActionsViewModel] so admins see it + * everywhere without each call site having to plumb the flag. * * `currentRouteName` is a soft string match against the destination * route name (route classes serialize to their FQN by default in @@ -43,8 +47,9 @@ import com.fabledsword.minstrel.nav.Settings as SettingsRoute fun MainAppBarActions( navController: NavHostController, currentRouteName: String?, - isAdmin: Boolean = false, + viewModel: AppBarActionsViewModel = hiltViewModel(), ) { + val isAdmin by viewModel.isAdmin.collectAsStateWithLifecycle() Row { if (currentRouteName != Home::class.qualifiedName) { IconButton(onClick = { navController.navigate(Home) }) {