feat: account switch from bottom bar profile item (#1053)

This commit is contained in:
Diego Beraldin 2024-06-28 14:03:53 +02:00 committed by GitHub
parent 6906a3ccc5
commit 4139e2d397
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 82 additions and 30 deletions

View File

@ -82,6 +82,7 @@ kotlin {
implementation(projects.unit.drawer) implementation(projects.unit.drawer)
implementation(projects.unit.editcommunity) implementation(projects.unit.editcommunity)
implementation(projects.unit.instanceinfo) implementation(projects.unit.instanceinfo)
implementation(projects.unit.manageaccounts)
implementation(projects.unit.manageban) implementation(projects.unit.manageban)
implementation(projects.unit.medialist) implementation(projects.unit.medialist)
implementation(projects.unit.managesubscriptions) implementation(projects.unit.managesubscriptions)
@ -124,9 +125,15 @@ kotlin {
android { android {
namespace = "com.github.diegoberaldin.raccoonforlemmy" namespace = "com.github.diegoberaldin.raccoonforlemmy"
compileSdk = libs.versions.android.targetSdk.get().toInt() compileSdk =
libs.versions.android.targetSdk
.get()
.toInt()
defaultConfig { defaultConfig {
minSdk = libs.versions.android.minSdk.get().toInt() minSdk =
libs.versions.android.minSdk
.get()
.toInt()
} }
} }
dependencies { dependencies {

View File

@ -41,12 +41,14 @@ import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getDrawerCoor
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.feature.home.ui.HomeTab import com.github.diegoberaldin.raccoonforlemmy.feature.home.ui.HomeTab
import com.github.diegoberaldin.raccoonforlemmy.feature.inbox.ui.InboxTab import com.github.diegoberaldin.raccoonforlemmy.feature.inbox.ui.InboxTab
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.ui.ProfileTab import com.github.diegoberaldin.raccoonforlemmy.feature.profile.ui.ProfileTab
import com.github.diegoberaldin.raccoonforlemmy.feature.search.ui.ExploreTab import com.github.diegoberaldin.raccoonforlemmy.feature.search.ui.ExploreTab
import com.github.diegoberaldin.raccoonforlemmy.feature.settings.ui.SettingsTab import com.github.diegoberaldin.raccoonforlemmy.feature.settings.ui.SettingsTab
import com.github.diegoberaldin.raccoonforlemmy.ui.navigation.TabNavigationItem import com.github.diegoberaldin.raccoonforlemmy.ui.navigation.TabNavigationItem
import com.github.diegoberaldin.raccoonforlemmy.unit.manageaccounts.ManageAccountsScreen
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
@ -246,6 +248,15 @@ internal object MainScreen : Screen {
tab = ProfileTab, tab = ProfileTab,
withText = titleVisible, withText = titleVisible,
customIconUrl = uiState.customProfileUrl, customIconUrl = uiState.customProfileUrl,
onLongPress =
if (uiState.isLogged) {
rememberCallback {
val screen = ManageAccountsScreen()
navigationCoordinator.showBottomSheet(screen)
}
} else {
null
},
) )
TabNavigationItem( TabNavigationItem(
tab = InboxTab, tab = InboxTab,

View File

@ -15,6 +15,7 @@ interface MainScreenMviModel :
data class UiState( data class UiState(
val bottomBarOffsetHeightPx: Float = 0f, val bottomBarOffsetHeightPx: Float = 0f,
val customProfileUrl: String? = null, val customProfileUrl: String? = null,
val isLogged: Boolean = false,
) )
sealed interface Effect { sealed interface Effect {

View File

@ -50,7 +50,8 @@ class MainViewModel(
}.launchIn(this) }.launchIn(this)
identityRepository.isLogged identityRepository.isLogged
.onEach { .onEach { isLogged ->
updateState { it.copy(isLogged = isLogged ?: false) }
updateCustomProfileIcon() updateCustomProfileIcon()
}.launchIn(this) }.launchIn(this)
} }

View File

@ -1,5 +1,8 @@
package com.github.diegoberaldin.raccoonforlemmy.ui.navigation package com.github.diegoberaldin.raccoonforlemmy.ui.navigation
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
@ -19,6 +22,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import cafe.adriel.voyager.navigator.tab.LocalTabNavigator import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
import cafe.adriel.voyager.navigator.tab.Tab import cafe.adriel.voyager.navigator.tab.Tab
@ -38,6 +42,7 @@ internal fun RowScope.TabNavigationItem(
tab: Tab, tab: Tab,
withText: Boolean = true, withText: Boolean = true,
customIconUrl: String? = null, customIconUrl: String? = null,
onLongPress: (() -> Unit)? = null,
) { ) {
val tabNavigator = LocalTabNavigator.current val tabNavigator = LocalTabNavigator.current
val navigationCoordinator = remember { getNavigationCoordinator() } val navigationCoordinator = remember { getNavigationCoordinator() }
@ -49,30 +54,52 @@ internal fun RowScope.TabNavigationItem(
MaterialTheme.colorScheme.outline MaterialTheme.colorScheme.outline
} }
val interactionSource = remember { MutableInteractionSource() }
fun handleClick() {
tabNavigator.current = tab
val section =
when (tab) {
ExploreTab -> TabNavigationSection.Explore
ProfileTab -> TabNavigationSection.Profile
InboxTab -> TabNavigationSection.Inbox
SettingsTab -> TabNavigationSection.Settings
else -> TabNavigationSection.Home
}
navigationCoordinator.setCurrentSection(section)
}
NavigationBarItem( NavigationBarItem(
onClick = { onClick = ::handleClick,
tabNavigator.current = tab
val section =
when (tab) {
ExploreTab -> TabNavigationSection.Explore
ProfileTab -> TabNavigationSection.Profile
InboxTab -> TabNavigationSection.Inbox
SettingsTab -> TabNavigationSection.Settings
else -> TabNavigationSection.Home
}
navigationCoordinator.setCurrentSection(section)
},
selected = tabNavigator.current == tab, selected = tabNavigator.current == tab,
interactionSource = interactionSource,
icon = { icon = {
val content = @Composable { val content = @Composable {
if (customIconUrl != null) { if (customIconUrl != null) {
val iconSize = IconSize.m val iconSize = IconSize.m
CustomImage( CustomImage(
url = customIconUrl, url = customIconUrl,
modifier = modifier =
Modifier Modifier
.size(iconSize) .size(iconSize)
.clip(RoundedCornerShape(iconSize / 2)), .clip(RoundedCornerShape(iconSize / 2))
.pointerInput(Unit) {
detectTapGestures(
onPress = { offset ->
val press = PressInteraction.Press(offset)
interactionSource.emit(press)
tryAwaitRelease()
interactionSource.emit(PressInteraction.Release(press))
},
onTap = {
handleClick()
},
onLongPress = {
onLongPress?.invoke()
},
)
},
) )
} else { } else {
Icon( Icon(

View File

@ -28,6 +28,7 @@ import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInParent import androidx.compose.ui.layout.positionInParent
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.DpOffset
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.IconSize import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.IconSize
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
@ -57,13 +58,12 @@ internal fun AccountItem(
Row( Row(
modifier = modifier =
modifier modifier
.fillMaxWidth()
.onClick( .onClick(
onClick = { onClick = {
onClick?.invoke() onClick?.invoke()
}, },
).padding( ).padding(
horizontal = Spacing.m, horizontal = Spacing.s,
vertical = Spacing.s, vertical = Spacing.s,
), ),
horizontalArrangement = Arrangement.spacedBy(Spacing.s), horizontalArrangement = Arrangement.spacedBy(Spacing.s),
@ -74,7 +74,6 @@ internal fun AccountItem(
CustomImage( CustomImage(
modifier = modifier =
Modifier Modifier
.padding(Spacing.xxxs)
.size(IconSize.l) .size(IconSize.l)
.clip(RoundedCornerShape(IconSize.l / 2)), .clip(RoundedCornerShape(IconSize.l / 2)),
url = avatar, url = avatar,
@ -85,7 +84,10 @@ internal fun AccountItem(
} else { } else {
Box(modifier = Modifier.size(IconSize.l)) Box(modifier = Modifier.size(IconSize.l))
} }
Text( Text(
modifier = Modifier.fillMaxWidth(0.8f),
maxLines = 1,
text = text =
buildString { buildString {
append(account.username) append(account.username)
@ -93,6 +95,8 @@ internal fun AccountItem(
append(account.instance) append(account.instance)
}, },
color = fullColor, color = fullColor,
style = MaterialTheme.typography.bodyMedium,
overflow = TextOverflow.Ellipsis,
) )
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
@ -108,12 +112,12 @@ internal fun AccountItem(
Box { Box {
Icon( Icon(
modifier = modifier =
Modifier.size(IconSize.m) Modifier
.size(IconSize.m)
.padding(Spacing.xs) .padding(Spacing.xs)
.onGloballyPositioned { .onGloballyPositioned {
optionsOffset = it.positionInParent() optionsOffset = it.positionInParent()
} }.onClick(
.onClick(
onClick = { onClick = {
optionsMenuOpen = true optionsMenuOpen = true
}, },

View File

@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -48,13 +49,14 @@ class ManageAccountsScreen : Screen {
var indexToDelete by remember { mutableStateOf<Int?>(null) } var indexToDelete by remember { mutableStateOf<Int?>(null) }
LaunchedEffect(model) { LaunchedEffect(model) {
model.effects.onEach { effect -> model.effects
when (effect) { .onEach { effect ->
ManageAccountsMviModel.Effect.Close -> { when (effect) {
navigationCoordinator.hideBottomSheet() ManageAccountsMviModel.Effect.Close -> {
navigationCoordinator.hideBottomSheet()
}
} }
} }.launchIn(this)
}.launchIn(this)
} }
Column( Column(
@ -63,8 +65,6 @@ class ManageAccountsScreen : Screen {
.windowInsetsPadding(WindowInsets.navigationBars) .windowInsetsPadding(WindowInsets.navigationBars)
.padding( .padding(
top = Spacing.s, top = Spacing.s,
start = Spacing.s,
end = Spacing.s,
bottom = Spacing.m, bottom = Spacing.m,
), ),
verticalArrangement = Arrangement.spacedBy(Spacing.s), verticalArrangement = Arrangement.spacedBy(Spacing.s),
@ -78,6 +78,7 @@ class ManageAccountsScreen : Screen {
) { ) {
itemsIndexed(uiState.accounts) { idx, account -> itemsIndexed(uiState.accounts) { idx, account ->
AccountItem( AccountItem(
modifier = Modifier.fillMaxWidth(),
account = account, account = account,
autoLoadImages = uiState.autoLoadImages, autoLoadImages = uiState.autoLoadImages,
onClick = { onClick = {