enhancement: community in profile menu (#1086)

This commit is contained in:
Diego Beraldin 2024-07-06 17:09:20 +02:00 committed by GitHub
parent 7885f6e1ae
commit e0f6072313
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 99 additions and 63 deletions

View File

@ -229,6 +229,8 @@ sealed interface NotificationCenterEvent {
data object ModeratorZone : ProfileSideMenuAction data object ModeratorZone : ProfileSideMenuAction
data object CreateCommunity : ProfileSideMenuAction
data object Logout : ProfileSideMenuAction data object Logout : ProfileSideMenuAction
} }

View File

@ -36,7 +36,7 @@
"url": "https://github.com/reusityback" "url": "https://github.com/reusityback"
}, },
{ {
"title": "outer_air", "title": "outerair",
"avatar": "https://hosted.weblate.org/avatar/128/outer_air.png", "avatar": "https://hosted.weblate.org/avatar/128/outer_air.png",
"subtitle": "Brazilian Portuguese translation", "subtitle": "Brazilian Portuguese translation",
"url": "https://hosted.weblate.org/user/outer_air" "url": "https://hosted.weblate.org/user/outer_air"
@ -48,7 +48,7 @@
"url": "https://github.com/Tmpod" "url": "https://github.com/Tmpod"
}, },
{ {
"title": "Cloudless", "title": "Cloudless",
"avatar": "https://lemmy.cafe/pictrs/image/e2f1f086-fe5a-4823-8676-e270b5554992.webp?format=webp", "avatar": "https://lemmy.cafe/pictrs/image/e2f1f086-fe5a-4823-8676-e270b5554992.webp?format=webp",
"subtitle": "Traditional Chinese (TW, HK) translation", "subtitle": "Traditional Chinese (TW, HK) translation",
"url": "https://lemmy.cafe/u/cloudless" "url": "https://lemmy.cafe/u/cloudless"

View File

@ -139,10 +139,21 @@ A special thanks goes to all those who contributed so far (in nearly chronologic
- [u/heyazorin](https://lemmy.ml/u/heyazorin) - [u/heyazorin](https://lemmy.ml/u/heyazorin)
- [u/thegiddystitcher](https://lemm.ee/u/thegiddystitcher) - [u/thegiddystitcher](https://lemm.ee/u/thegiddystitcher)
- [u/SgtAStrawberry](https://lemmy.world/u/SgtAStrawberry) - [u/SgtAStrawberry](https://lemmy.world/u/SgtAStrawberry)
- [outerair](https://hosted.weblate.org/user/outer_air)
- [u/Wild_Mastic](https://lemmy.world/u/Wild_Mastic) - [u/Wild_Mastic](https://lemmy.world/u/Wild_Mastic)
- [reusityback](https://github.com/reusityback) - [reusityback](https://github.com/reusityback)
- [u/fisco](https://lemmy.ml/u/fisco) - [u/fisco](https://lemmy.ml/u/fisco)
- [u/Suoko](https://feddit.it/u/Suoko)
- [u/Mannivu](https://feddit.it/u/Mannivu)
- [u/Kir](https://feddit.it/u/Kir)
- [u/NicKoehler](https://feddit.it/u/NicKoehler) - [u/NicKoehler](https://feddit.it/u/NicKoehler)
- [Tmpod](https://github.com/Tmpod)
- [u/cloudless](https://lemmy.cafe/u/cloudless)
- [u/squirrel](https://discuss.tchncs.de/u/squirrel)
- [gallegonovato](https://hosted.weblate.org/user/gallegonovato)
- [Edanas](https://hosted.weblate.org/user/Edanas)
- [u/sag](https://lemm.ee/u/sag)
- [u/am41](https://sh.itjust.works/u/am41)
- all those who reported feedback and ideas through the Lemmy community, GitHub issues, emails, - all those who reported feedback and ideas through the Lemmy community, GitHub issues, emails,
private messages, homing pigeons and every other imaginable media. private messages, homing pigeons and every other imaginable media.

View File

@ -64,6 +64,7 @@ kotlin {
implementation(projects.unit.createcomment) implementation(projects.unit.createcomment)
implementation(projects.unit.createpost) implementation(projects.unit.createpost)
implementation(projects.unit.drafts) implementation(projects.unit.drafts)
implementation(projects.unit.editcommunity)
implementation(projects.unit.filteredcontents) implementation(projects.unit.filteredcontents)
implementation(projects.unit.login) implementation(projects.unit.login)
implementation(projects.unit.manageaccounts) implementation(projects.unit.manageaccounts)
@ -90,8 +91,14 @@ kotlin {
android { android {
namespace = "com.github.diegoberaldin.raccoonforlemmy.feature.profile" namespace = "com.github.diegoberaldin.raccoonforlemmy.feature.profile"
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()
} }
} }

View File

@ -53,6 +53,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalPixel
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.menu.ProfileSideMenuScreen import com.github.diegoberaldin.raccoonforlemmy.feature.profile.menu.ProfileSideMenuScreen
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.notlogged.ProfileNotLoggedScreen import com.github.diegoberaldin.raccoonforlemmy.feature.profile.notlogged.ProfileNotLoggedScreen
import com.github.diegoberaldin.raccoonforlemmy.unit.drafts.DraftsScreen import com.github.diegoberaldin.raccoonforlemmy.unit.drafts.DraftsScreen
import com.github.diegoberaldin.raccoonforlemmy.unit.editcommunity.EditCommunityScreen
import com.github.diegoberaldin.raccoonforlemmy.unit.filteredcontents.FilteredContentsScreen import com.github.diegoberaldin.raccoonforlemmy.unit.filteredcontents.FilteredContentsScreen
import com.github.diegoberaldin.raccoonforlemmy.unit.filteredcontents.FilteredContentsType import com.github.diegoberaldin.raccoonforlemmy.unit.filteredcontents.FilteredContentsType
import com.github.diegoberaldin.raccoonforlemmy.unit.filteredcontents.toInt import com.github.diegoberaldin.raccoonforlemmy.unit.filteredcontents.toInt
@ -148,6 +149,12 @@ internal object ProfileMainScreen : Tab {
NotificationCenterEvent.ProfileSideMenuAction.Logout -> { NotificationCenterEvent.ProfileSideMenuAction.Logout -> {
logoutConfirmDialogOpen = true logoutConfirmDialogOpen = true
} }
NotificationCenterEvent.ProfileSideMenuAction.CreateCommunity -> {
navigationCoordinator.pushScreen(
EditCommunityScreen(),
)
}
} }
}.launchIn(this) }.launchIn(this)
} }

View File

@ -7,6 +7,7 @@ import androidx.compose.material.icons.automirrored.filled.Logout
import androidx.compose.material.icons.filled.Book import androidx.compose.material.icons.filled.Book
import androidx.compose.material.icons.filled.Bookmark import androidx.compose.material.icons.filled.Bookmark
import androidx.compose.material.icons.filled.Drafts import androidx.compose.material.icons.filled.Drafts
import androidx.compose.material.icons.filled.GroupAdd
import androidx.compose.material.icons.filled.ManageAccounts import androidx.compose.material.icons.filled.ManageAccounts
import androidx.compose.material.icons.filled.Shield import androidx.compose.material.icons.filled.Shield
import androidx.compose.material.icons.filled.ThumbsUpDown import androidx.compose.material.icons.filled.ThumbsUpDown
@ -25,6 +26,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallb
internal fun ProfileMenuContent( internal fun ProfileMenuContent(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
isModerator: Boolean = false, isModerator: Boolean = false,
canCreateCommunity: Boolean = false,
) { ) {
val notificationCenter = remember { getNotificationCenter() } val notificationCenter = remember { getNotificationCenter() }
@ -75,6 +77,16 @@ internal fun ProfileMenuContent(
}, },
) )
} }
if (canCreateCommunity) {
SettingsRow(
title = LocalStrings.current.actionCreateCommunity,
icon = Icons.Default.GroupAdd,
onTap =
rememberCallback {
notificationCenter.send(NotificationCenterEvent.ProfileSideMenuAction.CreateCommunity)
},
)
}
SettingsRow( SettingsRow(
title = LocalStrings.current.manageAccountsTitle, title = LocalStrings.current.manageAccountsTitle,

View File

@ -10,6 +10,7 @@ interface ProfileSideMenuMviModel :
data class State( data class State(
val isModerator: Boolean = false, val isModerator: Boolean = false,
val canCreateCommunity: Boolean = false,
) )
sealed interface Effect sealed interface Effect

View File

@ -69,6 +69,7 @@ class ProfileSideMenuScreen : Screen {
end = Spacing.m, end = Spacing.m,
), ),
isModerator = uiState.isModerator, isModerator = uiState.isModerator,
canCreateCommunity = uiState.canCreateCommunity,
) )
} }
} }

View File

@ -3,6 +3,7 @@ package com.github.diegoberaldin.raccoonforlemmy.feature.profile.menu
import cafe.adriel.voyager.core.model.screenModelScope import cafe.adriel.voyager.core.model.screenModelScope
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.LemmyValueCache import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.LemmyValueCache
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -23,6 +24,21 @@ class ProfileSideMenuViewModel(
) )
} }
}.launchIn(this) }.launchIn(this)
combine(
lemmyValueCache.isCurrentUserAdmin,
lemmyValueCache.isCommunityCreationAdminOnly,
) { isAdmin, isCommunityCreationAdminOnly ->
updateState {
it.copy(
canCreateCommunity =
if (isCommunityCreationAdminOnly) {
isAdmin
} else {
true
},
)
}
}.launchIn(this)
} }
} }
} }

View File

@ -1,10 +1,14 @@
package com.github.diegoberaldin.raccoonforlemmy package com.github.diegoberaldin.raccoonforlemmy
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.DrawerDefaults
import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Surface
import androidx.compose.material3.DrawerValue import androidx.compose.material3.DrawerValue
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalDrawerSheet import androidx.compose.material3.ModalDrawerSheet
@ -56,6 +60,7 @@ 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.persistence.di.getAccountRepository import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getAccountRepository
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository 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.toLanguageDirection import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLanguageDirection
import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalDp import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalDp
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.di.getApiConfigurationRepository import com.github.diegoberaldin.raccoonforlemmy.domain.identity.di.getApiConfigurationRepository
@ -336,6 +341,26 @@ fun App(onLoadingFinished: () -> Unit = {}) {
} }
} }
// scrim for draggable side menu
AnimatedVisibility(
modifier = Modifier.fillMaxSize(),
visible = sideMenuOpened,
) {
Surface(
modifier =
Modifier
.onClick(
onClick = {
navigationCoordinator.closeSideMenu()
},
),
color = DrawerDefaults.scrimColor,
) {
Box(modifier = Modifier.fillMaxSize())
}
}
// draggable side menu
DraggableSideMenu( DraggableSideMenu(
availableWidth = screenWidth.toLocalDp(), availableWidth = screenWidth.toLocalDp(),
opened = sideMenuOpened, opened = sideMenuOpened,

View File

@ -22,7 +22,6 @@ import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.imageUrl
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.CommentRepository import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.CommentRepository
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.LemmyValueCache import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.LemmyValueCache
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.PostRepository import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.PostRepository
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.SiteRepository
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
@ -38,7 +37,6 @@ class FilteredContentsViewModel(
private val identityRepository: IdentityRepository, private val identityRepository: IdentityRepository,
private val postRepository: PostRepository, private val postRepository: PostRepository,
private val commentRepository: CommentRepository, private val commentRepository: CommentRepository,
private val siteRepository: SiteRepository,
private val imagePreloadManager: ImagePreloadManager, private val imagePreloadManager: ImagePreloadManager,
private val hapticFeedback: HapticFeedback, private val hapticFeedback: HapticFeedback,
private val notificationCenter: NotificationCenter, private val notificationCenter: NotificationCenter,

View File

@ -20,7 +20,6 @@ val filteredContentsModule =
postPaginationManager = get(), postPaginationManager = get(),
commentPaginationManager = get(), commentPaginationManager = get(),
postNavigationManager = get(), postNavigationManager = get(),
siteRepository = get(),
lemmyValueCache = get(), lemmyValueCache = get(),
) )
} }

View File

@ -53,14 +53,12 @@ kotlin {
implementation(projects.core.persistence) implementation(projects.core.persistence)
implementation(projects.core.utils) implementation(projects.core.utils)
implementation(projects.unit.multicommunity)
implementation(projects.domain.identity) implementation(projects.domain.identity)
implementation(projects.domain.lemmy.data) implementation(projects.domain.lemmy.data)
implementation(projects.domain.lemmy.pagination) implementation(projects.domain.lemmy.pagination)
implementation(projects.domain.lemmy.repository) implementation(projects.domain.lemmy.repository)
implementation(projects.unit.editcommunity) implementation(projects.unit.multicommunity)
} }
} }
val commonTest by getting { val commonTest by getting {

View File

@ -15,13 +15,21 @@ interface ManageSubscriptionsMviModel :
data object HapticIndication : Intent data object HapticIndication : Intent
data class Unsubscribe(val id: Long) : Intent data class Unsubscribe(
val id: Long,
) : Intent
data class DeleteMultiCommunity(val id: Long) : Intent data class DeleteMultiCommunity(
val id: Long,
) : Intent
data class ToggleFavorite(val id: Long) : Intent data class ToggleFavorite(
val id: Long,
) : Intent
data class SetSearch(val value: String) : Intent data class SetSearch(
val value: String,
) : Intent
data object LoadNextPage : Intent data object LoadNextPage : Intent
} }
@ -36,7 +44,6 @@ interface ManageSubscriptionsMviModel :
val searchText: String = "", val searchText: String = "",
val canFetchMore: Boolean = true, val canFetchMore: Boolean = true,
val loading: Boolean = false, val loading: Boolean = false,
val canCreateCommunity: Boolean = false,
) )
sealed interface Effect { sealed interface Effect {

View File

@ -75,7 +75,6 @@ import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigation
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallbackArgs import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallbackArgs
import com.github.diegoberaldin.raccoonforlemmy.unit.editcommunity.EditCommunityScreen
import com.github.diegoberaldin.raccoonforlemmy.unit.multicommunity.detail.MultiCommunityScreen import com.github.diegoberaldin.raccoonforlemmy.unit.multicommunity.detail.MultiCommunityScreen
import com.github.diegoberaldin.raccoonforlemmy.unit.multicommunity.editor.MultiCommunityEditorScreen import com.github.diegoberaldin.raccoonforlemmy.unit.multicommunity.editor.MultiCommunityEditorScreen
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
@ -247,39 +246,6 @@ class ManageSubscriptionsScreen : Screen {
state = lazyListState, state = lazyListState,
verticalArrangement = Arrangement.spacedBy(Spacing.xxs), verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
) { ) {
if (uiState.canCreateCommunity) {
// create community header
item {
Row(
modifier =
Modifier.fillMaxWidth().padding(horizontal = Spacing.s),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
modifier = Modifier.padding(vertical = Spacing.xs),
text = LocalStrings.current.actionCreateCommunity,
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onBackground,
)
Spacer(modifier = Modifier.weight(1f))
Icon(
modifier =
Modifier
.padding(horizontal = Spacing.xs)
.onClick(
onClick = {
navigatorCoordinator.pushScreen(
EditCommunityScreen(),
)
},
),
imageVector = Icons.Default.AddCircle,
contentDescription = null,
tint = MaterialTheme.colorScheme.onBackground,
)
}
}
}
item { item {
Row( Row(
modifier = Modifier.fillMaxWidth().padding(horizontal = Spacing.s), modifier = Modifier.fillMaxWidth().padding(horizontal = Spacing.s),

View File

@ -16,9 +16,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.utils.vibrate.HapticFeedbac
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.IdentityRepository import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.IdentityRepository
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.CommunityRepository import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.CommunityRepository
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.LemmyValueCache
import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
@ -37,7 +35,6 @@ class ManageSubscriptionsViewModel(
private val communityPaginationManager: CommunityPaginationManager, private val communityPaginationManager: CommunityPaginationManager,
private val hapticFeedback: HapticFeedback, private val hapticFeedback: HapticFeedback,
private val notificationCenter: NotificationCenter, private val notificationCenter: NotificationCenter,
private val lemmyValueCache: LemmyValueCache,
) : DefaultMviModel<ManageSubscriptionsMviModel.Intent, ManageSubscriptionsMviModel.UiState, ManageSubscriptionsMviModel.Effect>( ) : DefaultMviModel<ManageSubscriptionsMviModel.Intent, ManageSubscriptionsMviModel.UiState, ManageSubscriptionsMviModel.Effect>(
initialState = ManageSubscriptionsMviModel.UiState(), initialState = ManageSubscriptionsMviModel.UiState(),
), ),
@ -64,16 +61,6 @@ class ManageSubscriptionsViewModel(
handleCommunityUpdate(evt.value) handleCommunityUpdate(evt.value)
}.launchIn(this) }.launchIn(this)
// determine whether community creation is available
combine(
lemmyValueCache.isCurrentUserAdmin,
lemmyValueCache.isCommunityCreationAdminOnly,
) { isAdmin, isCommunityCreationAdminOnly ->
updateState {
it.copy(canCreateCommunity = isAdmin || !isCommunityCreationAdminOnly)
}
}.launchIn(this)
uiState uiState
.map { it.searchText } .map { it.searchText }
.distinctUntilChanged() .distinctUntilChanged()

View File

@ -20,7 +20,6 @@ val manageSubscriptionsModule =
settingsRepository = get(), settingsRepository = get(),
favoriteCommunityRepository = get(), favoriteCommunityRepository = get(),
communityPaginationManager = get(), communityPaginationManager = get(),
lemmyValueCache = get(),
) )
} }
} }