mirror of
https://github.com/LiveFastEatTrashRaccoon/RaccoonForLemmy.git
synced 2025-02-03 07:57:39 +01:00
feat: profile side menu (#1081)
This commit is contained in:
parent
daf82cee84
commit
99a4e1594c
@ -73,11 +73,12 @@ fun DraggableSideMenu(
|
||||
}
|
||||
|
||||
LaunchedEffect(draggableState) {
|
||||
snapshotFlow { draggableState.currentValue }.onEach { value: SlideAnchorPosition ->
|
||||
if (value == SlideAnchorPosition.Closed) {
|
||||
onDismiss?.invoke()
|
||||
}
|
||||
}.launchIn(this)
|
||||
snapshotFlow { draggableState.currentValue }
|
||||
.onEach { value: SlideAnchorPosition ->
|
||||
if (value == SlideAnchorPosition.Closed) {
|
||||
onDismiss?.invoke()
|
||||
}
|
||||
}.launchIn(this)
|
||||
}
|
||||
|
||||
Box(
|
||||
@ -90,17 +91,12 @@ fun DraggableSideMenu(
|
||||
x = draggableState.requireOffset().roundToInt(),
|
||||
y = 0,
|
||||
)
|
||||
}
|
||||
.anchoredDraggable(
|
||||
}.anchoredDraggable(
|
||||
state = draggableState,
|
||||
orientation = Orientation.Horizontal,
|
||||
)
|
||||
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp))
|
||||
).background(MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp))
|
||||
.padding(
|
||||
top = Spacing.xxl,
|
||||
bottom = Spacing.m,
|
||||
end = Spacing.s,
|
||||
start = Spacing.s,
|
||||
),
|
||||
) {
|
||||
content()
|
||||
|
@ -2,6 +2,8 @@ package com.github.diegoberaldin.raccoonforlemmy.feature.profile.di
|
||||
|
||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.main.ProfileMainMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.main.ProfileMainViewModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.menu.ProfileSideMenuMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.menu.ProfileSideMenuViewModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.notlogged.ProfileNotLoggedMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.notlogged.ProfileNotLoggedViewModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.login.di.loginModule
|
||||
@ -27,4 +29,9 @@ val profileTabModule =
|
||||
identityRepository = get(),
|
||||
)
|
||||
}
|
||||
factory<ProfileSideMenuMviModel> {
|
||||
ProfileSideMenuViewModel(
|
||||
lemmyValueCache = get(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,7 @@ import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.Logout
|
||||
import androidx.compose.material.icons.filled.ManageAccounts
|
||||
import androidx.compose.material.icons.automirrored.filled.MenuOpen
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
@ -51,6 +50,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotific
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalPixel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.menu.ProfileSideMenuScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.notlogged.ProfileNotLoggedScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.drafts.DraftsScreen
|
||||
import com.github.diegoberaldin.raccoonforlemmy.unit.filteredcontents.FilteredContentsScreen
|
||||
@ -91,7 +91,8 @@ internal object ProfileMainScreen : Tab {
|
||||
var logoutConfirmDialogOpen by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(notificationCenter) {
|
||||
notificationCenter.subscribe(NotificationCenterEvent.ModeratorZoneActionSelected::class)
|
||||
notificationCenter
|
||||
.subscribe(NotificationCenterEvent.ModeratorZoneActionSelected::class)
|
||||
.onEach {
|
||||
val action = it.value.toModeratorZoneAction()
|
||||
when (action) {
|
||||
@ -110,41 +111,45 @@ internal object ProfileMainScreen : Tab {
|
||||
}
|
||||
}.launchIn(this)
|
||||
|
||||
notificationCenter.subscribe(NotificationCenterEvent.ProfileSideMenuAction::class).onEach { evt ->
|
||||
navigationCoordinator.closeSideMenu()
|
||||
notificationCenter
|
||||
.subscribe(NotificationCenterEvent.ProfileSideMenuAction::class)
|
||||
.onEach { evt ->
|
||||
navigationCoordinator.closeSideMenu()
|
||||
|
||||
when (evt) {
|
||||
NotificationCenterEvent.ProfileSideMenuAction.ManageAccounts -> {
|
||||
navigationCoordinator.showBottomSheet(ManageAccountsScreen())
|
||||
}
|
||||
when (evt) {
|
||||
NotificationCenterEvent.ProfileSideMenuAction.ManageAccounts -> {
|
||||
navigationCoordinator.showBottomSheet(ManageAccountsScreen())
|
||||
}
|
||||
|
||||
NotificationCenterEvent.ProfileSideMenuAction.ManageSubscriptions -> {
|
||||
navigationCoordinator.pushScreen(ManageSubscriptionsScreen())
|
||||
}
|
||||
NotificationCenterEvent.ProfileSideMenuAction.ManageSubscriptions -> {
|
||||
navigationCoordinator.pushScreen(ManageSubscriptionsScreen())
|
||||
}
|
||||
|
||||
NotificationCenterEvent.ProfileSideMenuAction.Bookmarks -> {
|
||||
val screen = FilteredContentsScreen(type = FilteredContentsType.Bookmarks.toInt())
|
||||
navigationCoordinator.pushScreen(screen)
|
||||
}
|
||||
NotificationCenterEvent.ProfileSideMenuAction.Bookmarks -> {
|
||||
val screen =
|
||||
FilteredContentsScreen(type = FilteredContentsType.Bookmarks.toInt())
|
||||
navigationCoordinator.pushScreen(screen)
|
||||
}
|
||||
|
||||
NotificationCenterEvent.ProfileSideMenuAction.Drafts -> {
|
||||
navigationCoordinator.pushScreen(DraftsScreen())
|
||||
}
|
||||
NotificationCenterEvent.ProfileSideMenuAction.Drafts -> {
|
||||
navigationCoordinator.pushScreen(DraftsScreen())
|
||||
}
|
||||
|
||||
NotificationCenterEvent.ProfileSideMenuAction.Votes -> {
|
||||
val screen = FilteredContentsScreen(type = FilteredContentsType.Votes.toInt())
|
||||
navigationCoordinator.pushScreen(screen)
|
||||
}
|
||||
NotificationCenterEvent.ProfileSideMenuAction.Votes -> {
|
||||
val screen =
|
||||
FilteredContentsScreen(type = FilteredContentsType.Votes.toInt())
|
||||
navigationCoordinator.pushScreen(screen)
|
||||
}
|
||||
|
||||
NotificationCenterEvent.ProfileSideMenuAction.ModeratorZone -> {
|
||||
navigationCoordinator.showBottomSheet(ModeratorZoneBottomSheet())
|
||||
}
|
||||
NotificationCenterEvent.ProfileSideMenuAction.ModeratorZone -> {
|
||||
navigationCoordinator.showBottomSheet(ModeratorZoneBottomSheet())
|
||||
}
|
||||
|
||||
NotificationCenterEvent.ProfileSideMenuAction.Logout -> {
|
||||
logoutConfirmDialogOpen = true
|
||||
NotificationCenterEvent.ProfileSideMenuAction.Logout -> {
|
||||
logoutConfirmDialogOpen = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}.launchIn(this)
|
||||
}.launchIn(this)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
@ -152,9 +157,10 @@ internal object ProfileMainScreen : Tab {
|
||||
topBar = {
|
||||
val maxTopInset = Dimensions.maxTopBarInset.toLocalPixel()
|
||||
var topInset by remember { mutableStateOf(maxTopInset) }
|
||||
snapshotFlow { topAppBarState.collapsedFraction }.onEach {
|
||||
topInset = maxTopInset * (1 - it)
|
||||
}.launchIn(scope)
|
||||
snapshotFlow { topAppBarState.collapsedFraction }
|
||||
.onEach {
|
||||
topInset = maxTopInset * (1 - it)
|
||||
}.launchIn(scope)
|
||||
|
||||
TopAppBar(
|
||||
windowInsets =
|
||||
@ -194,26 +200,15 @@ internal object ProfileMainScreen : Tab {
|
||||
.padding(horizontal = Spacing.xs)
|
||||
.onClick(
|
||||
onClick = {
|
||||
notificationCenter.send(NotificationCenterEvent.ProfileSideMenuAction.ManageAccounts)
|
||||
navigationCoordinator.openSideMenu(
|
||||
ProfileSideMenuScreen(),
|
||||
)
|
||||
},
|
||||
),
|
||||
imageVector = Icons.Default.ManageAccounts,
|
||||
imageVector = Icons.AutoMirrored.Default.MenuOpen,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
Icon(
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(horizontal = Spacing.xs)
|
||||
.onClick(
|
||||
onClick = {
|
||||
logoutConfirmDialogOpen = true
|
||||
},
|
||||
),
|
||||
imageVector = Icons.AutoMirrored.Default.Logout,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
@ -224,8 +219,7 @@ internal object ProfileMainScreen : Tab {
|
||||
Modifier
|
||||
.padding(
|
||||
top = padding.calculateTopPadding(),
|
||||
)
|
||||
.nestedScroll(fabNestedScrollConnection)
|
||||
).nestedScroll(fabNestedScrollConnection)
|
||||
.then(
|
||||
if (settings.hideNavigationBarWhileScrolling) {
|
||||
Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
@ -250,7 +244,9 @@ internal object ProfileMainScreen : Tab {
|
||||
CurrentScreen()
|
||||
val navigator = LocalTabNavigator.current
|
||||
LaunchedEffect(model) {
|
||||
model.uiState.map { s -> s.logged }.distinctUntilChanged()
|
||||
model.uiState
|
||||
.map { s -> s.logged }
|
||||
.distinctUntilChanged()
|
||||
.onEach { logged ->
|
||||
val index =
|
||||
when (logged) {
|
||||
|
@ -1,15 +1,20 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.unit.myaccount
|
||||
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.menu
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.Logout
|
||||
import androidx.compose.material.icons.filled.Book
|
||||
import androidx.compose.material.icons.filled.Bookmark
|
||||
import androidx.compose.material.icons.filled.Drafts
|
||||
import androidx.compose.material.icons.filled.ManageAccounts
|
||||
import androidx.compose.material.icons.filled.Shield
|
||||
import androidx.compose.material.icons.filled.ThumbsUpDown
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.SettingsRow
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
||||
@ -17,13 +22,15 @@ import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotific
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback
|
||||
|
||||
@Composable
|
||||
internal fun ProfileActionMenu(
|
||||
internal fun ProfileMenuContent(
|
||||
modifier: Modifier = Modifier,
|
||||
isModerator: Boolean = false,
|
||||
) {
|
||||
val notificationCenter = remember { getNotificationCenter() }
|
||||
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.m),
|
||||
) {
|
||||
SettingsRow(
|
||||
title = LocalStrings.current.navigationDrawerTitleSubscriptions,
|
||||
@ -68,5 +75,25 @@ internal fun ProfileActionMenu(
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
SettingsRow(
|
||||
title = LocalStrings.current.manageAccountsTitle,
|
||||
icon = Icons.Default.ManageAccounts,
|
||||
onTap =
|
||||
rememberCallback {
|
||||
notificationCenter.send(NotificationCenterEvent.ProfileSideMenuAction.ManageAccounts)
|
||||
},
|
||||
)
|
||||
|
||||
HorizontalDivider()
|
||||
|
||||
SettingsRow(
|
||||
title = LocalStrings.current.actionLogout,
|
||||
icon = Icons.AutoMirrored.Default.Logout,
|
||||
onTap =
|
||||
rememberCallback {
|
||||
notificationCenter.send(NotificationCenterEvent.ProfileSideMenuAction.Logout)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.menu
|
||||
|
||||
import cafe.adriel.voyager.core.model.ScreenModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
|
||||
|
||||
interface ProfileSideMenuMviModel :
|
||||
MviModel<ProfileSideMenuMviModel.Intent, ProfileSideMenuMviModel.State, ProfileSideMenuMviModel.Effect>,
|
||||
ScreenModel {
|
||||
sealed interface Intent
|
||||
|
||||
data class State(
|
||||
val isModerator: Boolean = false,
|
||||
)
|
||||
|
||||
sealed interface Effect
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.menu
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.surfaceColorAtElevation
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cafe.adriel.voyager.core.screen.Screen
|
||||
import cafe.adriel.voyager.koin.getScreenModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback
|
||||
|
||||
class ProfileSideMenuScreen : Screen {
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val model = getScreenModel<ProfileSideMenuMviModel>()
|
||||
val navigationCoordinator = remember { getNavigationCoordinator() }
|
||||
val uiState by model.uiState.collectAsState()
|
||||
|
||||
Scaffold(
|
||||
contentColor = MaterialTheme.colorScheme.onBackground,
|
||||
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp),
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
colors =
|
||||
TopAppBarDefaults.topAppBarColors().copy(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp),
|
||||
),
|
||||
title = {},
|
||||
actions = {
|
||||
Icon(
|
||||
modifier =
|
||||
Modifier.padding(horizontal = Spacing.xs).onClick(
|
||||
onClick =
|
||||
rememberCallback {
|
||||
navigationCoordinator.closeSideMenu()
|
||||
},
|
||||
),
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
) { padding ->
|
||||
ProfileMenuContent(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.padding(
|
||||
top = padding.calculateTopPadding(),
|
||||
start = Spacing.m,
|
||||
end = Spacing.m,
|
||||
),
|
||||
isModerator = uiState.isModerator,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.feature.profile.menu
|
||||
|
||||
import cafe.adriel.voyager.core.model.screenModelScope
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.LemmyValueCache
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ProfileSideMenuViewModel(
|
||||
private val lemmyValueCache: LemmyValueCache,
|
||||
) : DefaultMviModel<ProfileSideMenuMviModel.Intent, ProfileSideMenuMviModel.State, ProfileSideMenuMviModel.Effect>(
|
||||
ProfileSideMenuMviModel.State(),
|
||||
),
|
||||
ProfileSideMenuMviModel {
|
||||
init {
|
||||
screenModelScope.launch {
|
||||
lemmyValueCache.isCurrentUserModerator
|
||||
.onEach { isModerator ->
|
||||
updateState {
|
||||
it.copy(
|
||||
isModerator = isModerator,
|
||||
)
|
||||
}
|
||||
}.launchIn(this)
|
||||
}
|
||||
}
|
||||
}
|
@ -81,7 +81,6 @@ class CommunityInfoScreen(
|
||||
topBar = {
|
||||
val title = uiState.community.readableName(uiState.preferNicknames)
|
||||
TopAppBar(
|
||||
modifier = Modifier.padding(top = Spacing.s),
|
||||
colors =
|
||||
TopAppBarDefaults.topAppBarColors().copy(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp),
|
||||
@ -112,12 +111,9 @@ class CommunityInfoScreen(
|
||||
LazyColumn(
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(
|
||||
top = padding.calculateTopPadding(),
|
||||
)
|
||||
.fillMaxSize()
|
||||
.padding(
|
||||
top = Spacing.s,
|
||||
top = padding.calculateTopPadding(),
|
||||
start = Spacing.m,
|
||||
end = Spacing.m,
|
||||
),
|
||||
@ -138,7 +134,10 @@ class CommunityInfoScreen(
|
||||
DetailInfoItem(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
icon = Icons.Default.Cake,
|
||||
title = uiState.community.creationDate?.prettifyDate().orEmpty(),
|
||||
title =
|
||||
uiState.community.creationDate
|
||||
?.prettifyDate()
|
||||
.orEmpty(),
|
||||
)
|
||||
DetailInfoItem(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
|
@ -167,18 +167,6 @@ object ProfileLoggedScreen : Tab {
|
||||
)
|
||||
}
|
||||
}
|
||||
item {
|
||||
ProfileActionMenu(
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(
|
||||
top = Spacing.xs,
|
||||
bottom = Spacing.s,
|
||||
).fillMaxWidth(),
|
||||
isModerator = uiState.isModerator,
|
||||
)
|
||||
HorizontalDivider()
|
||||
}
|
||||
item {
|
||||
SectionSelector(
|
||||
modifier = Modifier.padding(bottom = Spacing.s),
|
||||
|
@ -85,7 +85,6 @@ class UserInfoScreen(
|
||||
topBar = {
|
||||
val title = uiState.user.readableName(uiState.preferNicknames)
|
||||
TopAppBar(
|
||||
modifier = Modifier.padding(top = Spacing.s),
|
||||
colors =
|
||||
TopAppBarDefaults.topAppBarColors().copy(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp),
|
||||
@ -119,8 +118,6 @@ class UserInfoScreen(
|
||||
.fillMaxSize()
|
||||
.padding(
|
||||
top = padding.calculateTopPadding(),
|
||||
).padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.m,
|
||||
end = Spacing.m,
|
||||
),
|
||||
|
Loading…
x
Reference in New Issue
Block a user