diff --git a/core/commonui/lemmyui/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/core/commonui/lemmyui/Options.kt b/core/commonui/lemmyui/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/core/commonui/lemmyui/Options.kt index 96278fa5b..fce6eb202 100644 --- a/core/commonui/lemmyui/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/core/commonui/lemmyui/Options.kt +++ b/core/commonui/lemmyui/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/core/commonui/lemmyui/Options.kt @@ -69,4 +69,6 @@ sealed class OptionId( data object PurgeCreator : OptionId(29) data object Restore : OptionId(30) + + data object ManageTags : OptionId(31) } diff --git a/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailMviModel.kt b/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailMviModel.kt index ce39017c3..ad49e4618 100644 --- a/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailMviModel.kt +++ b/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailMviModel.kt @@ -7,6 +7,7 @@ import com.livefast.eattrash.raccoonforlemmy.core.appearance.data.VoteFormat import com.livefast.eattrash.raccoonforlemmy.core.architecture.MviModel import com.livefast.eattrash.raccoonforlemmy.core.commonui.lemmyui.UserDetailSection import com.livefast.eattrash.raccoonforlemmy.core.persistence.data.ActionOnSwipe +import com.livefast.eattrash.raccoonforlemmy.core.persistence.data.UserTagModel import com.livefast.eattrash.raccoonforlemmy.domain.lemmy.data.CommentModel import com.livefast.eattrash.raccoonforlemmy.domain.lemmy.data.PostModel import com.livefast.eattrash.raccoonforlemmy.domain.lemmy.data.SortType @@ -66,6 +67,10 @@ interface UserDetailMviModel : data object BlockInstance : Intent data object WillOpenDetail : Intent + + data class UpdateTags( + val ids: List, + ) : Intent } data class UiState( @@ -99,6 +104,8 @@ interface UserDetailMviModel : val actionsOnSwipeToEndPosts: List = emptyList(), val actionsOnSwipeToStartComments: List = emptyList(), val actionsOnSwipeToEndComments: List = emptyList(), + val currentUserTagIds: List = emptyList(), + val availableUserTags: List = emptyList(), ) sealed interface Effect { diff --git a/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailScreen.kt b/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailScreen.kt index 089e83a25..d3329cd8c 100644 --- a/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailScreen.kt +++ b/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailScreen.kt @@ -88,6 +88,7 @@ import com.livefast.eattrash.raccoonforlemmy.core.commonui.lemmyui.PostCardPlace import com.livefast.eattrash.raccoonforlemmy.core.commonui.lemmyui.UserDetailSection import com.livefast.eattrash.raccoonforlemmy.core.commonui.lemmyui.UserHeader import com.livefast.eattrash.raccoonforlemmy.core.commonui.lemmyui.di.getFabNestedScrollConnection +import com.livefast.eattrash.raccoonforlemmy.core.commonui.modals.AssignUserTagBottomSheet import com.livefast.eattrash.raccoonforlemmy.core.commonui.modals.CustomModalBottomSheet import com.livefast.eattrash.raccoonforlemmy.core.commonui.modals.CustomModalBottomSheetItem import com.livefast.eattrash.raccoonforlemmy.core.commonui.modals.SortBottomSheet @@ -170,6 +171,7 @@ class UserDetailScreen( var shareBottomSheetUrls by remember { mutableStateOf?>(null) } var sortBottomSheetOpened by remember { mutableStateOf(false) } var copyPostBottomSheet by remember { mutableStateOf(null) } + var manageTagsBottomSheetOpened by remember { mutableStateOf(false) } LaunchedEffect(model) { model.effects @@ -285,6 +287,13 @@ class UserDetailScreen( LocalStrings.current.adminActionPurge, ) } + if (uiState.isLogged) { + this += + Option( + OptionId.ManageTags, + LocalStrings.current.userTagsTitle, + ) + } } var optionsExpanded by remember { mutableStateOf(false) } var optionsOffset by remember { mutableStateOf(Offset.Zero) } @@ -363,6 +372,10 @@ class UserDetailScreen( navigationCoordinator.pushScreen(screen) } + OptionId.ManageTags -> { + manageTagsBottomSheetOpened = true + } + else -> Unit } }, @@ -1277,5 +1290,19 @@ class UserDetailScreen( }, ) } + + if (manageTagsBottomSheetOpened) { + AssignUserTagBottomSheet( + tags = uiState.availableUserTags, + initiallyCheckedIds = uiState.currentUserTagIds, + onDismiss = { + manageTagsBottomSheetOpened = false + }, + onSelect = { ids -> + manageTagsBottomSheetOpened = false + model.reduce(UserDetailMviModel.Intent.UpdateTags(ids)) + }, + ) + } } } diff --git a/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailViewModel.kt b/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailViewModel.kt index 2d1dfe2ff..64211bba0 100644 --- a/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailViewModel.kt +++ b/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailViewModel.kt @@ -6,7 +6,9 @@ import com.livefast.eattrash.raccoonforlemmy.core.architecture.DefaultMviModel import com.livefast.eattrash.raccoonforlemmy.core.commonui.lemmyui.UserDetailSection import com.livefast.eattrash.raccoonforlemmy.core.notifications.NotificationCenter import com.livefast.eattrash.raccoonforlemmy.core.notifications.NotificationCenterEvent +import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.AccountRepository import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.SettingsRepository +import com.livefast.eattrash.raccoonforlemmy.core.persistence.repository.UserTagRepository import com.livefast.eattrash.raccoonforlemmy.core.utils.imageload.ImagePreloadManager import com.livefast.eattrash.raccoonforlemmy.core.utils.share.ShareHelper import com.livefast.eattrash.raccoonforlemmy.core.utils.vibrate.HapticFeedback @@ -56,6 +58,8 @@ class UserDetailViewModel( private val shareHelper: ShareHelper, private val hapticFeedback: HapticFeedback, private val settingsRepository: SettingsRepository, + private val userTagRepository: UserTagRepository, + private val accountRepository: AccountRepository, private val notificationCenter: NotificationCenter, private val imagePreloadManager: ImagePreloadManager, private val getSortTypesUseCase: GetSortTypesUseCase, @@ -81,6 +85,15 @@ class UserDetailViewModel( updateState { it.copy(user = user) } + + val accountId = accountRepository.getActive()?.id + if (accountId != null) { + val allTags = userTagRepository.getAll(accountId) + updateState { + it.copy(availableUserTags = allTags) + } + } + refreshCurrentUserTags() } themeRepository.postLayout .onEach { layout -> @@ -258,6 +271,8 @@ class UserDetailViewModel( val state = postPaginationManager.extractState() postNavigationManager.push(state) } + + is UserDetailMviModel.Intent.UpdateTags -> updateTags(intent.ids) } } @@ -596,4 +611,49 @@ class UserDetailViewModel( } } } + + private suspend fun refreshCurrentUserTags() { + val accountId = accountRepository.getActive()?.id ?: return + val user = uiState.value.user + val currentTags = + userTagRepository.getBelonging( + accountId = accountId, + username = user.readableHandle, + ) + updateState { + it.copy( + currentUserTagIds = currentTags.mapNotNull { tag -> tag.id }, + ) + } + } + + private fun updateTags(ids: List) { + screenModelScope.launch { + val accountId = accountRepository.getActive()?.id ?: return@launch + val username = uiState.value.user.readableHandle + val currentTagIds = + userTagRepository + .getTags( + username = username, + accountId = accountId, + ).mapNotNull { it.id } + + val idsToRemove = currentTagIds.filter { it !in ids } + for (id in idsToRemove) { + userTagRepository.removeMember( + username = username, + userTagId = id, + ) + } + + val idsToAdd = ids.filter { it !in currentTagIds } + for (id in idsToAdd) { + userTagRepository.addMember( + username = username, + userTagId = id, + ) + } + refreshCurrentUserTags() + } + } } diff --git a/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/di/UserDetailModule.kt b/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/di/UserDetailModule.kt index c38aec83e..08dbe2586 100644 --- a/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/di/UserDetailModule.kt +++ b/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/di/UserDetailModule.kt @@ -31,6 +31,8 @@ val userDetailModule = shareHelper = instance(), hapticFeedback = instance(), settingsRepository = instance(), + userTagRepository = instance(), + accountRepository = instance(), notificationCenter = instance(), imagePreloadManager = instance(), getSortTypesUseCase = instance(),