From 4b60c3e20b5daa7a71bcef29c15cdeaf9caf5779 Mon Sep 17 00:00:00 2001 From: KotlinGeekDev Date: Thu, 25 Sep 2025 21:36:34 +0100 Subject: [PATCH] Refactor: From List -> Set, and Viewmodel rename as well. Move FollowSet and related classes to model package. --- .../nip51Lists/followSets}/FollowSet.kt | 20 +++---- .../model/nip51Lists/followSets/NostrSet.kt | 52 ++++++++++++++++++ .../nip51Lists/followSets/SetVisibility.kt | 47 ++++++++++++++++ .../loggedIn/lists/CustomListsScreen.kt | 26 ++++----- .../ui/screen/loggedIn/lists/CustomSetItem.kt | 16 +++--- ...ollowSetState.kt => FollowSetFeedState.kt} | 12 +++-- ...ViewModel.kt => FollowSetFeedViewModel.kt} | 28 +++++----- .../screen/loggedIn/lists/ListVisibility.kt | 27 ---------- .../ui/screen/loggedIn/lists/NostrList.kt | 32 ----------- .../lists/followsets/FollowSetScreen.kt | 24 ++++----- .../followsets/FollowSetsManagementDialog.kt | 54 +++++++++---------- 11 files changed, 192 insertions(+), 146 deletions(-) rename amethyst/src/main/java/com/vitorpamplona/amethyst/{ui/screen/loggedIn/lists => model/nip51Lists/followSets}/FollowSet.kt (84%) create mode 100644 amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/followSets/NostrSet.kt create mode 100644 amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/followSets/SetVisibility.kt rename amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/{FollowSetState.kt => FollowSetFeedState.kt} (83%) rename amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/{NostrUserListFeedViewModel.kt => FollowSetFeedViewModel.kt} (90%) delete mode 100644 amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/ListVisibility.kt delete mode 100644 amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/NostrList.kt diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/FollowSet.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/followSets/FollowSet.kt similarity index 84% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/FollowSet.kt rename to amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/followSets/FollowSet.kt index 58982ebc31..59f5ed34c7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/FollowSet.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/followSets/FollowSet.kt @@ -18,7 +18,7 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.amethyst.ui.screen.loggedIn.lists +package com.vitorpamplona.amethyst.model.nip51Lists.followSets import androidx.compose.runtime.Stable import com.vitorpamplona.quartz.nip01Core.core.value @@ -31,9 +31,9 @@ data class FollowSet( val identifierTag: String, val title: String, val description: String?, - val visibility: ListVisibility, - val profileList: Set, -) : NostrList(listVisibility = visibility, content = profileList) { + val visibility: SetVisibility, + val profiles: Set, +) : NostrSet(setVisibility = visibility, content = profiles) { companion object { fun mapEventToSet( event: PeopleListEvent, @@ -53,16 +53,16 @@ data class FollowSet( identifierTag = dTag, title = listTitle, description = listDescription, - visibility = ListVisibility.Private, - profileList = privateFollows.toSet(), + visibility = SetVisibility.Private, + profiles = privateFollows.toSet(), ) } else if (publicFollows.isNotEmpty() && privateFollows.isEmpty()) { FollowSet( identifierTag = dTag, title = listTitle, description = listDescription, - visibility = ListVisibility.Public, - profileList = publicFollows.toSet(), + visibility = SetVisibility.Public, + profiles = publicFollows.toSet(), ) } else { // Follow set is empty, so assume public. Why? Nostr limitation. @@ -71,8 +71,8 @@ data class FollowSet( identifierTag = dTag, title = listTitle, description = listDescription, - visibility = ListVisibility.Public, - profileList = publicFollows.toSet(), + visibility = SetVisibility.Public, + profiles = publicFollows.toSet(), ) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/followSets/NostrSet.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/followSets/NostrSet.kt new file mode 100644 index 0000000000..a32258cd76 --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/followSets/NostrSet.kt @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.model.nip51Lists.followSets + +/** + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +sealed class NostrSet( + val setVisibility: SetVisibility, + val content: Collection, +) + +class CuratedBookmarkSet( + val name: String, + val visibility: SetVisibility, + val setItems: List, +) : NostrSet(visibility, setItems) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/followSets/SetVisibility.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/followSets/SetVisibility.kt new file mode 100644 index 0000000000..ce418b099a --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/followSets/SetVisibility.kt @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.model.nip51Lists.followSets + +/** + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +enum class SetVisibility { + Public, + Private, + Mixed, +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/CustomListsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/CustomListsScreen.kt index ca09f2cbb2..9a600f9e72 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/CustomListsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/CustomListsScreen.kt @@ -59,6 +59,8 @@ import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.model.nip51Lists.followSets.FollowSet +import com.vitorpamplona.amethyst.model.nip51Lists.followSets.SetVisibility import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.routes.Route @@ -76,10 +78,10 @@ fun ListsAndSetsScreen( accountViewModel: AccountViewModel, nav: INav, ) { - val followSetsViewModel: NostrUserListFeedViewModel = + val followSetsViewModel: FollowSetFeedViewModel = viewModel( - key = "NostrUserListFeedViewModel", - factory = NostrUserListFeedViewModel.Factory(accountViewModel.account), + key = "FollowSetFeedViewModel", + factory = FollowSetFeedViewModel.Factory(accountViewModel.account), ) ListsAndSetsScreen( @@ -91,7 +93,7 @@ fun ListsAndSetsScreen( @Composable fun ListsAndSetsScreen( - followSetsViewModel: NostrUserListFeedViewModel, + followSetsViewModel: FollowSetFeedViewModel, accountViewModel: AccountViewModel, nav: INav, ) { @@ -117,8 +119,8 @@ fun ListsAndSetsScreen( refresh = { followSetsViewModel.invalidateData() }, - addItem = { title: String, description: String?, listType: ListVisibility -> - val isSetPrivate = listType == ListVisibility.Private + addItem = { title: String, description: String?, listType: SetVisibility -> + val isSetPrivate = listType == SetVisibility.Private followSetsViewModel.addFollowSet( setName = title, setDescription = description, @@ -149,9 +151,9 @@ fun ListsAndSetsScreen( @Composable fun CustomListsScreen( - followSetState: FollowSetState, + followSetFeedState: FollowSetFeedState, refresh: () -> Unit, - addItem: (title: String, description: String?, listType: ListVisibility) -> Unit, + addItem: (title: String, description: String?, listType: SetVisibility) -> Unit, openItem: (identifier: String) -> Unit, renameItem: (followSet: FollowSet, newName: String) -> Unit, deleteItem: (followSet: FollowSet) -> Unit, @@ -195,10 +197,10 @@ fun CustomListsScreen( // TODO: Show components based on current tab FollowSetFabsAndMenu( onAddPrivateSet = { name: String, description: String? -> - addItem(name, description, ListVisibility.Private) + addItem(name, description, SetVisibility.Private) }, onAddPublicSet = { name: String, description: String? -> - addItem(name, description, ListVisibility.Public) + addItem(name, description, SetVisibility.Public) }, ) }, @@ -216,7 +218,7 @@ fun CustomListsScreen( when (page) { 0 -> FollowSetFeedView( - followSetState = followSetState, + followSetFeedState = followSetFeedState, onRefresh = refresh, onOpenItem = openItem, onRenameItem = renameItem, @@ -410,7 +412,7 @@ private fun SetItemPreview() { identifierTag = "00001-2222", title = "Sample List Title", description = "Sample List Description", - visibility = ListVisibility.Mixed, + visibility = SetVisibility.Mixed, emptySet(), ) ThemeComparisonColumn { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/CustomSetItem.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/CustomSetItem.kt index 26cdf279e4..4d75c61dbe 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/CustomSetItem.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/CustomSetItem.kt @@ -55,6 +55,8 @@ import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.model.nip51Lists.followSets.FollowSet +import com.vitorpamplona.amethyst.model.nip51Lists.followSets.SetVisibility import com.vitorpamplona.amethyst.ui.components.ClickableBox import com.vitorpamplona.amethyst.ui.note.VerticalDotsIcon import com.vitorpamplona.amethyst.ui.stringRes @@ -98,7 +100,7 @@ fun CustomSetItem( selected = true, onClick = {}, label = { - Text(text = "${followSet.profileList.size}") + Text(text = "${followSet.profiles.size}") }, leadingIcon = { Icon( @@ -121,9 +123,9 @@ fun CustomSetItem( followSet.visibility.let { val text by derivedStateOf { when (it) { - ListVisibility.Public -> stringRes(context, R.string.follow_set_type_public) - ListVisibility.Private -> stringRes(context, R.string.follow_set_type_private) - ListVisibility.Mixed -> stringRes(context, R.string.follow_set_type_mixed) + SetVisibility.Public -> stringRes(context, R.string.follow_set_type_public) + SetVisibility.Private -> stringRes(context, R.string.follow_set_type_private) + SetVisibility.Mixed -> stringRes(context, R.string.follow_set_type_mixed) } } Column( @@ -135,9 +137,9 @@ fun CustomSetItem( painter = painterResource( when (it) { - ListVisibility.Public -> R.drawable.ic_public - ListVisibility.Private -> R.drawable.lock - ListVisibility.Mixed -> R.drawable.format_list_bulleted_type + SetVisibility.Public -> R.drawable.ic_public + SetVisibility.Private -> R.drawable.lock + SetVisibility.Mixed -> R.drawable.format_list_bulleted_type }, ), contentDescription = stringRes(R.string.follow_set_type_description, text), diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/FollowSetState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/FollowSetFeedState.kt similarity index 83% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/FollowSetState.kt rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/FollowSetFeedState.kt index d39a661108..59109582b6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/FollowSetState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/FollowSetFeedState.kt @@ -20,16 +20,18 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.lists -sealed class FollowSetState { - data object Loading : FollowSetState() +import com.vitorpamplona.amethyst.model.nip51Lists.followSets.FollowSet + +sealed class FollowSetFeedState { + data object Loading : FollowSetFeedState() data class Loaded( val feed: List, - ) : FollowSetState() + ) : FollowSetFeedState() - data object Empty : FollowSetState() + data object Empty : FollowSetFeedState() data class FeedError( val errorMessage: String, - ) : FollowSetState() + ) : FollowSetFeedState() } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/NostrUserListFeedViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/FollowSetFeedViewModel.kt similarity index 90% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/NostrUserListFeedViewModel.kt rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/FollowSetFeedViewModel.kt index 7350255645..c921816cf5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/NostrUserListFeedViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/FollowSetFeedViewModel.kt @@ -30,6 +30,8 @@ import androidx.lifecycle.viewModelScope import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.AddressableNote import com.vitorpamplona.amethyst.model.LocalCache +import com.vitorpamplona.amethyst.model.nip51Lists.followSets.FollowSet +import com.vitorpamplona.amethyst.model.nip51Lists.followSets.SetVisibility import com.vitorpamplona.amethyst.service.checkNotInMainThread import com.vitorpamplona.amethyst.ui.dal.FeedFilter import com.vitorpamplona.amethyst.ui.dal.FollowSetFeedFilter @@ -49,12 +51,11 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import java.util.UUID -// TODO Update: Rename this to be used only for follow sets, and create separate VMs for bookmark sets, etc. -class NostrUserListFeedViewModel( +class FollowSetFeedViewModel( val dataSource: FeedFilter, ) : ViewModel(), InvalidatableContent { - private val _feedContent = MutableStateFlow(FollowSetState.Loading) + private val _feedContent = MutableStateFlow(FollowSetFeedState.Loading) val feedContent = _feedContent.asStateFlow() fun refresh() { @@ -67,9 +68,8 @@ class NostrUserListFeedViewModel( noteIdentifier: String, account: Account, ): AddressableNote? { -// checkNotInMainThread() val potentialNote = - runBlocking(Dispatchers.IO) { account.getFollowSetNotes() } + runBlocking(Dispatchers.IO) { account.followSetsState.getFollowSetNotes() } .find { it.dTag() == noteIdentifier } return potentialNote } @@ -79,7 +79,7 @@ class NostrUserListFeedViewModel( account: Account, ): Boolean { val potentialNote = - runBlocking(viewModelScope.coroutineContext) { account.getFollowSetNotes() } + runBlocking(viewModelScope.coroutineContext) { account.followSetsState.getFollowSetNotes() } .find { (it.event as PeopleListEvent).nameOrTitle() == setName } return potentialNote != null } @@ -94,7 +94,7 @@ class NostrUserListFeedViewModel( val newSets = dataSource.loadTop().toImmutableList() - if (oldFeedState is FollowSetState.Loaded) { + if (oldFeedState is FollowSetFeedState.Loaded) { val oldFeedList = oldFeedState.feed.toImmutableList() // Using size as a proxy for has changed. if (!equalImmutableLists(newSets, oldFeedList)) { @@ -108,7 +108,7 @@ class NostrUserListFeedViewModel( this.javaClass.simpleName, "refreshSuspended: Error loading or refreshing feed -> ${e.message}", ) - _feedContent.update { FollowSetState.FeedError(e.message.toString()) } + _feedContent.update { FollowSetFeedState.FeedError(e.message.toString()) } } finally { isRefreshing.value = false } @@ -190,7 +190,7 @@ class NostrUserListFeedViewModel( PeopleListEvent.addUser( earlierVersion = followSetEvent, pubKeyHex = userProfileHex, - isPrivate = followSet.visibility == ListVisibility.Private, + isPrivate = followSet.visibility == SetVisibility.Private, signer = account.signer, ) { account.sendMyPublicAndPrivateOutbox(it) @@ -223,9 +223,9 @@ class NostrUserListFeedViewModel( private fun updateFeed(sets: ImmutableList) { if (sets.isNotEmpty()) { - _feedContent.update { FollowSetState.Loaded(sets) } + _feedContent.update { FollowSetFeedState.Loaded(sets) } } else { - _feedContent.update { FollowSetState.Empty } + _feedContent.update { FollowSetFeedState.Empty } } } @@ -244,7 +244,7 @@ class NostrUserListFeedViewModel( init { Log.d("Init", this.javaClass.simpleName) - Log.d(this.javaClass.simpleName, " FollowSetState : ${_feedContent.value}") + Log.d(this.javaClass.simpleName, " FollowSetFeedState : ${_feedContent.value}") collectorJob = viewModelScope.launch(Dispatchers.IO) { LocalCache.live.newEventBundles.collect { newNotes -> @@ -266,8 +266,8 @@ class NostrUserListFeedViewModel( val account: Account, ) : ViewModelProvider.Factory { override fun create(modelClass: Class): T = - NostrUserListFeedViewModel( - FollowSetFeedFilter(account), + FollowSetFeedViewModel( + FollowSetFeedFilter(account.followSetsState), ) as T } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/ListVisibility.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/ListVisibility.kt deleted file mode 100644 index 8948cd93b0..0000000000 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/ListVisibility.kt +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) 2025 Vitor Pamplona - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.vitorpamplona.amethyst.ui.screen.loggedIn.lists - -enum class ListVisibility { - Public, - Private, - Mixed, -} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/NostrList.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/NostrList.kt deleted file mode 100644 index e51dff97a9..0000000000 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/NostrList.kt +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) 2025 Vitor Pamplona - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.vitorpamplona.amethyst.ui.screen.loggedIn.lists - -sealed class NostrList( - val listVisibility: ListVisibility, - val content: Collection, -) - -class CuratedBookmarkList( - val name: String, - val visibility: ListVisibility, - val listItems: List, -) : NostrList(visibility, listItems) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/followsets/FollowSetScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/followsets/FollowSetScreen.kt index df54be0f13..d0b930e80e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/followsets/FollowSetScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/followsets/FollowSetScreen.kt @@ -67,14 +67,14 @@ import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.User +import com.vitorpamplona.amethyst.model.nip51Lists.followSets.FollowSet +import com.vitorpamplona.amethyst.model.nip51Lists.followSets.SetVisibility import com.vitorpamplona.amethyst.ui.components.ClickableBox import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.note.UserCompose import com.vitorpamplona.amethyst.ui.note.VerticalDotsIcon import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.FollowSet -import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.ListVisibility -import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.NostrUserListFeedViewModel +import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.FollowSetFeedViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.BackButton import com.vitorpamplona.amethyst.ui.theme.ButtonBorder import com.vitorpamplona.amethyst.ui.theme.FeedPadding @@ -92,10 +92,10 @@ fun FollowSetScreen( accountViewModel: AccountViewModel, navigator: INav, ) { - val followSetViewModel: NostrUserListFeedViewModel = + val followSetViewModel: FollowSetFeedViewModel = viewModel( - key = "NostrUserListFeedViewModel", - factory = NostrUserListFeedViewModel.Factory(accountViewModel.account), + key = "FollowSetFeedViewModel", + factory = FollowSetFeedViewModel.Factory(accountViewModel.account), ) FollowSetScreen(selectedSetIdentifier, followSetViewModel, accountViewModel, navigator) @@ -105,7 +105,7 @@ fun FollowSetScreen( @Composable fun FollowSetScreen( selectedSetIdentifier: String, - followSetViewModel: NostrUserListFeedViewModel, + followSetViewModel: FollowSetFeedViewModel, accountViewModel: AccountViewModel, navigator: INav, ) { @@ -144,7 +144,7 @@ fun FollowSetScreen( when { selectedSetState.value != null -> { val selectedSet = selectedSetState.value - val users = selectedSet!!.profileList.mapToUsers(accountViewModel).filterNotNull() + val users = selectedSet!!.profiles.mapToUsers(accountViewModel).filterNotNull() Scaffold( topBar = { TopAppBar( @@ -235,10 +235,10 @@ fun TitleAndDescription( Icon( painter = painterResource( - when (followSet.listVisibility) { - ListVisibility.Public -> R.drawable.ic_public - ListVisibility.Private -> R.drawable.lock - ListVisibility.Mixed -> R.drawable.format_list_bulleted_type + when (followSet.setVisibility) { + SetVisibility.Public -> R.drawable.ic_public + SetVisibility.Private -> R.drawable.lock + SetVisibility.Mixed -> R.drawable.format_list_bulleted_type }, ), contentDescription = null, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/followsets/FollowSetsManagementDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/followsets/FollowSetsManagementDialog.kt index b0629981c6..2cc2c35f8f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/followsets/FollowSetsManagementDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/followsets/FollowSetsManagementDialog.kt @@ -78,13 +78,13 @@ import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.LocalCache +import com.vitorpamplona.amethyst.model.nip51Lists.followSets.SetVisibility import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.FollowSetState -import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.ListVisibility +import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.FollowSetFeedState +import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.FollowSetFeedViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.NewSetCreationDialog -import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.NostrUserListFeedViewModel import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.theme.ButtonBorder import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer @@ -97,10 +97,10 @@ fun FollowSetsManagementDialog( accountViewModel: AccountViewModel, navigator: INav, ) { - val followSetViewModel: NostrUserListFeedViewModel = + val followSetViewModel: FollowSetFeedViewModel = viewModel( - key = "NostrUserListFeedViewModel", - factory = NostrUserListFeedViewModel.Factory(accountViewModel.account), + key = "FollowSetFeedViewModel", + factory = FollowSetFeedViewModel.Factory(accountViewModel.account), ) FollowSetsManagementDialog(userHex, followSetViewModel, accountViewModel.account, navigator) @@ -110,7 +110,7 @@ fun FollowSetsManagementDialog( @Composable fun FollowSetsManagementDialog( userHex: String, - followSetsViewModel: NostrUserListFeedViewModel, + followSetsViewModel: FollowSetFeedViewModel, account: Account, navigator: INav, ) { @@ -164,17 +164,17 @@ fun FollowSetsManagementDialog( .imePadding(), ) { when (followSetsState) { - is FollowSetState.Loaded -> { - val lists = (followSetsState as FollowSetState.Loaded).feed + is FollowSetFeedState.Loaded -> { + val lists = (followSetsState as FollowSetFeedState.Loaded).feed lists.forEachIndexed { index, list -> Spacer(StdVertSpacer) FollowSetItem( modifier = Modifier.fillMaxWidth(), listHeader = list.title, - listVisibility = list.visibility, + setVisibility = list.visibility, userName = userInfo.toBestDisplayName(), - isUserInList = list.profileList.contains(userHex), + isUserInList = list.profiles.contains(userHex), onRemoveUser = { Log.d( "Amethyst", @@ -187,7 +187,7 @@ fun FollowSetsManagementDialog( ) Log.d( "Amethyst", - "Updated List. New size: ${list.profileList.size}", + "Updated List. New size: ${list.profiles.size}", ) }, onAddUser = { @@ -198,28 +198,28 @@ fun FollowSetsManagementDialog( followSetsViewModel.addUserToSet(userHex, list, account) Log.d( "Amethyst", - "Updated List. New size: ${list.profileList.size}", + "Updated List. New size: ${list.profiles.size}", ) }, ) } } - FollowSetState.Empty -> { + FollowSetFeedState.Empty -> { EmptyOrNoneFound { followSetsViewModel.refresh() } } - is FollowSetState.FeedError -> { - val errorMsg = (followSetsState as FollowSetState.FeedError).errorMessage + is FollowSetFeedState.FeedError -> { + val errorMsg = (followSetsState as FollowSetFeedState.FeedError).errorMessage ErrorMessage(errorMsg) { followSetsViewModel.refresh() } } - FollowSetState.Loading -> { + FollowSetFeedState.Loading -> { Loading() } } - if (followSetsState != FollowSetState.Loading) { + if (followSetsState != FollowSetFeedState.Loading) { FollowSetsCreationMenu( userName = userInfo.toBestDisplayName(), onSetCreate = { setName, setIsPrivate, description -> @@ -304,7 +304,7 @@ private fun ErrorMessage( fun FollowSetItem( modifier: Modifier = Modifier, listHeader: String, - listVisibility: ListVisibility, + setVisibility: SetVisibility, userName: String, isUserInList: Boolean, onAddUser: () -> Unit, @@ -330,21 +330,21 @@ fun FollowSetItem( ) { Text(listHeader, fontWeight = FontWeight.Bold) Spacer(modifier = StdHorzSpacer) - listVisibility.let { + setVisibility.let { val text by derivedStateOf { when (it) { - ListVisibility.Public -> stringRes(context, R.string.follow_set_type_public) - ListVisibility.Private -> stringRes(context, R.string.follow_set_type_private) - ListVisibility.Mixed -> stringRes(context, R.string.follow_set_type_mixed) + SetVisibility.Public -> stringRes(context, R.string.follow_set_type_public) + SetVisibility.Private -> stringRes(context, R.string.follow_set_type_private) + SetVisibility.Mixed -> stringRes(context, R.string.follow_set_type_mixed) } } Icon( painter = painterResource( - when (listVisibility) { - ListVisibility.Public -> R.drawable.ic_public - ListVisibility.Private -> R.drawable.lock - ListVisibility.Mixed -> R.drawable.format_list_bulleted_type + when (setVisibility) { + SetVisibility.Public -> R.drawable.ic_public + SetVisibility.Private -> R.drawable.lock + SetVisibility.Mixed -> R.drawable.format_list_bulleted_type }, ), contentDescription = stringRes(R.string.follow_set_type_description, text),