From 59cdb222d5a2e0486f21a86e9d4e09455bbba5ff Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Wed, 5 Nov 2025 10:51:10 -0500 Subject: [PATCH 1/2] Removes the Mute List from the All Follows TopNav option --- .../vitorpamplona/amethyst/model/Account.kt | 14 +++---- .../nip51Lists/peopleList/PeopleListsState.kt | 18 +++++--- .../serverList/MergedFollowListsState.kt | 41 +++++++++++++------ .../amethyst/ui/screen/TopNavFilterState.kt | 8 ++-- .../lists/display/PeopleListViewModel.kt | 10 ++--- .../lists/list/ListOfPeopleListsScreen.kt | 12 +++--- .../lists/memberEdit/EditPeopleListScreen.kt | 8 ++-- 7 files changed, 68 insertions(+), 43 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index 794a38bc1a..25b8f346c0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -286,8 +286,8 @@ class Account( val peopleListDecryptionCache = PeopleListDecryptionCache(signer) val blockPeopleList = BlockPeopleListState(signer, cache, peopleListDecryptionCache, scope) - val peopleListsState = PeopleListsState(signer, cache, peopleListDecryptionCache, scope) - val followListsState = FollowListsState(signer, cache, scope) + val peopleLists = PeopleListsState(signer, cache, peopleListDecryptionCache, scope) + val followLists = FollowListsState(signer, cache, scope) val hiddenUsers = HiddenUsersState(muteList.flow, blockPeopleList.flow, scope, settings) @@ -325,7 +325,7 @@ class Account( val followsPerRelay = FollowsPerOutboxRelay(kind3FollowList, blockedRelayList, proxyRelayList, cache, scope).flow // Merges all follow lists to create a single All Follows feed. - val allFollows = MergedFollowListsState(kind3FollowList, peopleListsState, hashtagList, geohashList, communityList, scope) + val allFollows = MergedFollowListsState(kind3FollowList, peopleLists, followLists, hashtagList, geohashList, communityList, scope) val privateDMDecryptionCache = PrivateDMCache(signer) val privateZapsDecryptionCache = PrivateZapCache(signer) @@ -1780,8 +1780,8 @@ class Account( logTime("Account ${userProfile().toBestDisplayName()} newEventBundle Update with ${newNotes.size} new notes") { upgradeAttestations() newNotesPreProcessor.runNew(newNotes) - peopleListsState.newNotes(newNotes) - followListsState.newNotes(newNotes) + peopleLists.newNotes(newNotes) + followLists.newNotes(newNotes) } } } @@ -1790,8 +1790,8 @@ class Account( cache.live.deletedEventBundles.collect { deletedNotes -> logTime("Account ${userProfile().toBestDisplayName()} deletedEventBundle Update with ${deletedNotes.size} new notes") { newNotesPreProcessor.runDeleted(deletedNotes) - peopleListsState.deletedNotes(deletedNotes) - followListsState.deletedNotes(deletedNotes) + peopleLists.deletedNotes(deletedNotes) + followLists.deletedNotes(deletedNotes) } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/peopleList/PeopleListsState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/peopleList/PeopleListsState.kt index 47bce8c59a..1244d611bd 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/peopleList/PeopleListsState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/peopleList/PeopleListsState.kt @@ -94,10 +94,20 @@ class PeopleListsState( suspend fun List.mapToUserIdSet() = this.map { it.userIdSet() }.flattenToSet() - val allPeopleListProfiles: StateFlow> = + suspend fun List.mapGoodUsersToIdSet() = + this + .mapNotNull { + if (it.dTag() != PeopleListEvent.BLOCK_LIST_D_TAG) { + it.userIdSet() + } else { + null + } + }.flattenToSet() + + val allGoodPeopleListProfiles: StateFlow> = latestLists - .map { it.mapToUserIdSet() } - .onStart { emit(latestLists.value.mapToUserIdSet()) } + .map { it.mapGoodUsersToIdSet() } + .onStart { emit(latestLists.value.mapGoodUsersToIdSet()) } .flowOn(Dispatchers.IO) .stateIn(scope, SharingStarted.Eagerly, emptySet()) @@ -131,8 +141,6 @@ class PeopleListsState( ) } - fun isUserInFollowSets(user: User): Boolean = allPeopleListProfiles.value.contains(user.pubkeyHex) - fun DeletionEvent.hasDeletedAnyPeopleList() = deleteAddressesWithKind(PeopleListEvent.KIND) || deletesAnyEventIn(peopleListsEventIds.value) fun hasItemInNoteList(notes: Set): Boolean = diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/serverList/MergedFollowListsState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/serverList/MergedFollowListsState.kt index 78f1461ce9..c9434c50b5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/serverList/MergedFollowListsState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/serverList/MergedFollowListsState.kt @@ -24,8 +24,10 @@ import androidx.compose.runtime.Immutable import com.vitorpamplona.amethyst.model.nip02FollowLists.Kind3FollowListState import com.vitorpamplona.amethyst.model.nip51Lists.geohashLists.GeohashListState import com.vitorpamplona.amethyst.model.nip51Lists.hashtagLists.HashtagListState +import com.vitorpamplona.amethyst.model.nip51Lists.peopleList.FollowListsState import com.vitorpamplona.amethyst.model.nip51Lists.peopleList.PeopleListsState import com.vitorpamplona.amethyst.model.nip72Communities.CommunityListState +import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip72ModCommunities.follow.tags.CommunityTag import com.vitorpamplona.quartz.nip73ExternalIds.location.GeohashId import com.vitorpamplona.quartz.nip73ExternalIds.topics.HashtagId @@ -41,7 +43,8 @@ import kotlinx.coroutines.flow.stateIn class MergedFollowListsState( val kind3List: Kind3FollowListState, - val followSetList: PeopleListsState, + val peopleList: PeopleListsState, + val followList: FollowListsState, val hashtagList: HashtagListState, val geohashList: GeohashListState, val communityList: CommunityListState, @@ -63,13 +66,14 @@ class MergedFollowListsState( fun mergeLists( kind3: Kind3FollowListState.Kind3Follows, - followSetProfiles: Set, + peopleListProfiles: Set, + followListProfiles: Set, hashtags: Set, geohashes: Set, community: Set, ): AllFollows = AllFollows( - authors = kind3.authors + followSetProfiles, + authors = kind3.authors + peopleListProfiles + followListProfiles, hashtags = hashtags, geotags = geohashes, communities = community.mapTo(mutableSetOf()) { it.address.toValue() }, @@ -77,17 +81,29 @@ class MergedFollowListsState( val flow: StateFlow = combine( - kind3List.flow, - followSetList.allPeopleListProfiles, - hashtagList.flow, - geohashList.flow, - communityList.flow, - ::mergeLists, - ).onStart { + listOf( + kind3List.flow, + peopleList.allGoodPeopleListProfiles, + followList.allPeopleListProfiles, + hashtagList.flow, + geohashList.flow, + communityList.flow, + ), + ) { args -> + mergeLists( + args[0] as Kind3FollowListState.Kind3Follows, + args[1] as Set, + args[2] as Set, + args[3] as Set, + args[4] as Set, + args[5] as Set, + ) + }.onStart { emit( mergeLists( kind3List.flow.value, - followSetList.allPeopleListProfiles.value, + peopleList.allGoodPeopleListProfiles.value, + followList.allPeopleListProfiles.value, hashtagList.flow.value, geohashList.flow.value, communityList.flow.value, @@ -100,7 +116,8 @@ class MergedFollowListsState( SharingStarted.Eagerly, mergeLists( kind3List.flow.value, - followSetList.allPeopleListProfiles.value, + peopleList.allGoodPeopleListProfiles.value, + followList.allPeopleListProfiles.value, hashtagList.flow.value, geohashList.flow.value, communityList.flow.value, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/TopNavFilterState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/TopNavFilterState.kt index c3d2577575..3d6f89fc13 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/TopNavFilterState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/TopNavFilterState.kt @@ -148,14 +148,14 @@ class TopNavFilterState( val livePeopleListsFlow: Flow> = combine( - account.peopleListsState.peopleListNotes, - account.followListsState.followListNotes, + account.peopleLists.peopleListNotes, + account.followLists.followListNotes, ::mergePeopleLists, ).onStart { emit( mergePeopleLists( - account.peopleListsState.peopleListNotes.value, - account.followListsState.followListNotes.value, + account.peopleLists.peopleListNotes.value, + account.followLists.followListNotes.value, ), ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/display/PeopleListViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/display/PeopleListViewModel.kt index 3a967980f8..d36a0fb86e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/display/PeopleListViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/display/PeopleListViewModel.kt @@ -55,7 +55,7 @@ class PeopleListViewModel : ViewModel() { selectedDTag .transformLatest { emitAll( - account.peopleListsState.selectListFlow(it).flowOn(Dispatchers.IO), + account.peopleLists.selectListFlow(it).flowOn(Dispatchers.IO), ) }.flowOn(Dispatchers.IO) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null) @@ -73,23 +73,23 @@ class PeopleListViewModel : ViewModel() { } suspend fun deleteFollowSet() { - account.peopleListsState.deleteFollowSet(selectedDTag.value, account) + account.peopleLists.deleteFollowSet(selectedDTag.value, account) } - fun loadNote(): AddressableNote? = account.peopleListsState.getPeopleListNote(selectedDTag.value) + fun loadNote(): AddressableNote? = account.peopleLists.getPeopleListNote(selectedDTag.value) suspend fun removeUserFromSet( user: User, isPrivate: Boolean, ) { - account.peopleListsState.removeUserFromSet(user, isPrivate, selectedDTag.value, account) + account.peopleLists.removeUserFromSet(user, isPrivate, selectedDTag.value, account) } suspend fun addUserToSet( user: User, isPrivate: Boolean, ) { - account.peopleListsState.addUserToSet(user, selectedDTag.value, isPrivate, account) + account.peopleLists.addUserToSet(user, selectedDTag.value, isPrivate, account) } fun hasUserFlow( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/list/ListOfPeopleListsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/list/ListOfPeopleListsScreen.kt index 40dc823dde..24becb801c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/list/ListOfPeopleListsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/list/ListOfPeopleListsScreen.kt @@ -61,10 +61,10 @@ fun ListOfPeopleListsScreen( nav: INav, ) { ListOfPeopleListsScreen( - listFlow = accountViewModel.account.peopleListsState.uiListFlow, + listFlow = accountViewModel.account.peopleLists.uiListFlow, addItem = { title: String, description: String? -> accountViewModel.runIOCatching { - accountViewModel.account.peopleListsState.addFollowList( + accountViewModel.account.peopleLists.addFollowList( listName = title, listDescription = description, account = accountViewModel.account, @@ -76,7 +76,7 @@ fun ListOfPeopleListsScreen( }, renameItem = { followSet, newValue -> accountViewModel.runIOCatching { - accountViewModel.account.peopleListsState.renameFollowList( + accountViewModel.account.peopleLists.renameFollowList( newName = newValue, peopleList = followSet, account = accountViewModel.account, @@ -85,7 +85,7 @@ fun ListOfPeopleListsScreen( }, changeItemDescription = { followSet, newDescription -> accountViewModel.runIOCatching { - accountViewModel.account.peopleListsState.modifyFollowSetDescription( + accountViewModel.account.peopleLists.modifyFollowSetDescription( newDescription = newDescription, peopleList = followSet, account = accountViewModel.account, @@ -94,7 +94,7 @@ fun ListOfPeopleListsScreen( }, cloneItem = { followSet, customName, customDescription -> accountViewModel.runIOCatching { - accountViewModel.account.peopleListsState.cloneFollowSet( + accountViewModel.account.peopleLists.cloneFollowSet( currentPeopleList = followSet, customCloneName = customName, customCloneDescription = customDescription, @@ -104,7 +104,7 @@ fun ListOfPeopleListsScreen( }, deleteItem = { followSet -> accountViewModel.runIOCatching { - accountViewModel.account.peopleListsState.deleteFollowSet( + accountViewModel.account.peopleLists.deleteFollowSet( identifierTag = followSet.identifierTag, account = accountViewModel.account, ) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/memberEdit/EditPeopleListScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/memberEdit/EditPeopleListScreen.kt index 172a3a2d81..609de3ebe6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/memberEdit/EditPeopleListScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/memberEdit/EditPeopleListScreen.kt @@ -150,7 +150,7 @@ private fun FollowSetManagementScreenBody( accountViewModel: AccountViewModel, nav: INav, ) { - val followSetsState by accountViewModel.account.peopleListsState.uiListFlow + val followSetsState by accountViewModel.account.peopleLists.uiListFlow .collectAsStateWithLifecycle() if (followSetsState.isEmpty()) { @@ -168,7 +168,7 @@ private fun FollowSetManagementScreenBody( userIsPublicMember = list.publicMembers.contains(userToAddOrRemove), onRemoveUser = { accountViewModel.runIOCatching { - accountViewModel.account.peopleListsState.removeUserFromSet( + accountViewModel.account.peopleLists.removeUserFromSet( userToAddOrRemove, isPrivate = list.privateMembers.contains(userToAddOrRemove), list.identifierTag, @@ -178,7 +178,7 @@ private fun FollowSetManagementScreenBody( }, onAddUserToList = { userShouldBePrivate -> accountViewModel.runIOCatching { - accountViewModel.account.peopleListsState.addUserToSet( + accountViewModel.account.peopleLists.addUserToSet( userToAddOrRemove, list.identifierTag, userShouldBePrivate, @@ -196,7 +196,7 @@ private fun FollowSetManagementScreenBody( userName = userToAddOrRemove.toBestDisplayName(), onSetCreate = { setName, memberShouldBePrivate, description -> accountViewModel.runIOCatching { - accountViewModel.account.peopleListsState.addFollowList( + accountViewModel.account.peopleLists.addFollowList( listName = setName, listDescription = description, isPrivate = memberShouldBePrivate, From 8168d353dd39c451d3876732b1594c767d9f93e9 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Wed, 5 Nov 2025 14:11:15 -0500 Subject: [PATCH 2/2] Improves the look of the list of lists of people screen --- .../lists/list/ListOfPeopleListFeedView.kt | 4 +- .../lists/list/ListOfPeopleListsScreen.kt | 42 +-- .../loggedIn/lists/list/PeopleListItem.kt | 272 +++++++++++------- .../vitorpamplona/amethyst/ui/theme/Shape.kt | 3 + amethyst/src/main/res/values/strings.xml | 1 + 5 files changed, 180 insertions(+), 142 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/list/ListOfPeopleListFeedView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/list/ListOfPeopleListFeedView.kt index d6756ee562..2cf015bfcb 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/list/ListOfPeopleListFeedView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/list/ListOfPeopleListFeedView.kt @@ -40,7 +40,7 @@ import com.vitorpamplona.amethyst.model.nip51Lists.peopleList.PeopleList import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.theme.DividerThickness import com.vitorpamplona.amethyst.ui.theme.FeedPadding -import com.vitorpamplona.amethyst.ui.theme.Size30dp +import com.vitorpamplona.amethyst.ui.theme.Size40dp import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer import kotlinx.coroutines.flow.StateFlow @@ -83,7 +83,7 @@ fun AllPeopleListFeedView( @Composable fun AllPeopleListFeedEmpty(message: String = stringRes(R.string.feed_is_empty)) { Column( - Modifier.fillMaxSize().padding(horizontal = Size30dp), + Modifier.fillMaxSize().padding(horizontal = Size40dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, ) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/list/ListOfPeopleListsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/list/ListOfPeopleListsScreen.kt index 24becb801c..387ffd5d9a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/list/ListOfPeopleListsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/list/ListOfPeopleListsScreen.kt @@ -42,7 +42,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.nip51Lists.peopleList.PeopleList @@ -52,7 +51,6 @@ import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer -import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn import kotlinx.coroutines.flow.StateFlow @Composable @@ -131,9 +129,7 @@ fun ListOfPeopleListsScreen( }, floatingActionButton = { PeopleListFabsAndMenu( - onAddSet = { name: String, description: String? -> - addItem(name, description) - }, + onAddSet = addItem, ) }, ) { paddingValues -> @@ -142,8 +138,6 @@ fun ListOfPeopleListsScreen( .padding( top = paddingValues.calculateTopPadding(), bottom = paddingValues.calculateBottomPadding(), - start = 10.dp, - end = 10.dp, ).fillMaxHeight(), ) { AllPeopleListFeedView( @@ -257,37 +251,3 @@ fun NewPeopleListCreationDialog( }, ) } - -@Preview(showSystemUi = true) -@Composable -private fun PeopleListItemPreview() { - val samplePeopleList = - PeopleList( - identifierTag = "00001-2222", - title = "Sample List Title", - description = "Sample List Description", - emptySet(), - emptySet(), - ) - ThemeComparisonColumn { - PeopleListItem( - modifier = Modifier, - samplePeopleList, - onClick = { - println("follow set: ${samplePeopleList.identifierTag}") - }, - onRename = { - println("Follow set new name: $it") - }, - onDescriptionChange = { description -> - println("The follow set's description has been changed to $description") - }, - onClone = { newName, newDesc -> - println("The follow set has been cloned, and has custom name: $newName, Desc: $newDesc") - }, - onDelete = { - println(" The follow set ${samplePeopleList.title} has been deleted.") - }, - ) - } -} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/list/PeopleListItem.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/list/PeopleListItem.kt index 4001cb7dab..84bbf9d864 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/list/PeopleListItem.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/lists/list/PeopleListItem.kt @@ -22,18 +22,21 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.list import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.People +import androidx.compose.material.icons.outlined.Groups import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.FilterChip import androidx.compose.material3.Icon +import androidx.compose.material3.ListItem import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.runtime.Composable @@ -41,7 +44,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString @@ -49,19 +51,110 @@ import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.withStyle +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.model.LocalCache +import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.nip51Lists.peopleList.PeopleList import com.vitorpamplona.amethyst.ui.components.ClickableBox import com.vitorpamplona.amethyst.ui.note.VerticalDotsIcon import com.vitorpamplona.amethyst.ui.stringRes -import com.vitorpamplona.amethyst.ui.theme.ButtonBorder import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer +import com.vitorpamplona.amethyst.ui.theme.Font10SP +import com.vitorpamplona.amethyst.ui.theme.NoSoTinyBorders +import com.vitorpamplona.amethyst.ui.theme.Size10Modifier +import com.vitorpamplona.amethyst.ui.theme.Size50ModifierOffset10 import com.vitorpamplona.amethyst.ui.theme.Size5dp +import com.vitorpamplona.amethyst.ui.theme.SpacedBy2dp import com.vitorpamplona.amethyst.ui.theme.SpacedBy5dp -import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer -import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer +import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn + +@Preview() +@Composable +private fun PeopleListItemPreview() { + val user1: User = LocalCache.getOrCreateUser("460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c") + val user2: User = LocalCache.getOrCreateUser("ca89cb11f1c75d5b6622268ff43d2288ea8b2cb5b9aa996ff9ff704fc904b78b") + val user3: User = LocalCache.getOrCreateUser("7eb29c126b3628077e2e3d863b917a56b74293aa9d8a9abc26a40ba3f2866baf") + + val samplePeopleList1 = + PeopleList( + identifierTag = "00001-2222", + title = "Sample List Title", + description = "Sample List Description", + emptySet(), + emptySet(), + ) + + val samplePeopleList2 = + PeopleList( + identifierTag = "00001-2222", + title = "Sample List Title", + description = "Sample List Description", + setOf(user1, user3), + emptySet(), + ) + + val samplePeopleList3 = + PeopleList( + identifierTag = "00001-2222", + title = "Sample List Title", + description = "Sample List Description", + emptySet(), + setOf(user1, user3), + ) + + val samplePeopleList4 = + PeopleList( + identifierTag = "00001-2222", + title = "Sample List Title", + description = "Sample List Description", + setOf(user3), + setOf(user1, user2, user3), + ) + + ThemeComparisonColumn { + Column { + PeopleListItem( + modifier = Modifier, + peopleList = samplePeopleList1, + onClick = {}, + onRename = {}, + onDescriptionChange = { }, + onClone = { newName, newDesc -> }, + onDelete = {}, + ) + PeopleListItem( + modifier = Modifier, + peopleList = samplePeopleList2, + onClick = {}, + onRename = {}, + onDescriptionChange = { }, + onClone = { newName, newDesc -> }, + onDelete = {}, + ) + PeopleListItem( + modifier = Modifier, + peopleList = samplePeopleList3, + onClick = {}, + onRename = {}, + onDescriptionChange = { }, + onClone = { newName, newDesc -> }, + onDelete = {}, + ) + PeopleListItem( + modifier = Modifier, + peopleList = samplePeopleList4, + onClick = {}, + onRename = {}, + onDescriptionChange = { }, + onClone = { newName, newDesc -> }, + onDelete = {}, + ) + } + } +} @Composable fun PeopleListItem( @@ -73,112 +166,93 @@ fun PeopleListItem( onClone: (customName: String?, customDescription: String?) -> Unit, onDelete: () -> Unit, ) { - val context = LocalContext.current - Row( - modifier = - modifier - .clickable(onClick = onClick), - ) { - Row( - modifier = - Modifier - .padding(bottom = 12.dp) - .weight(1f), - verticalAlignment = Alignment.CenterVertically, - ) { - Column( - modifier = Modifier.weight(1f), - verticalArrangement = Arrangement.Center, + ListItem( + modifier = modifier.clickable(onClick = onClick), + headlineContent = { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, ) { + Text(peopleList.title) + + Column( + modifier = NoSoTinyBorders, + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.End, + ) { + PeopleListOptionsButton( + peopleListName = peopleList.title, + peopleListDescription = peopleList.description, + onListRename = onRename, + onListDescriptionChange = onDescriptionChange, + onListCloneCreate = onClone, + onListDelete = onDelete, + ) + } + } + }, + supportingContent = { + Text( + peopleList.description ?: "", + overflow = TextOverflow.Ellipsis, + maxLines = 2, + ) + }, + leadingContent = { + Box(contentAlignment = Alignment.Center) { + Icon( + imageVector = Icons.Outlined.Groups, + contentDescription = stringRes(R.string.follow_set_icon_description), + modifier = Size50ModifierOffset10, + ) Row( + modifier = Modifier.align(Alignment.BottomCenter).offset(y = (-5).dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = SpacedBy5dp, ) { - Icon( - painter = painterResource(R.drawable.format_list_bulleted_type), - contentDescription = stringRes(R.string.follow_set_icon_description), - ) - Text(peopleList.title, fontWeight = FontWeight.Bold) if (peopleList.publicMembers.isEmpty() && peopleList.privateMembers.isEmpty()) { - FilterChip( - selected = true, - onClick = {}, - label = { - Text(text = stringRes(R.string.follow_set_empty_label)) - }, - leadingIcon = { - Icon( - imageVector = Icons.Default.People, - contentDescription = null, - ) - }, - shape = ButtonBorder, + Text( + text = stringRes(R.string.follow_set_empty_label2), + fontSize = Font10SP, ) - } - if (peopleList.publicMembers.isNotEmpty()) { - val publicMemberSize = peopleList.publicMembers.size - FilterChip( - selected = true, - onClick = {}, - label = { - Text(text = "$publicMemberSize") - }, - leadingIcon = { - Icon( - painterResource(R.drawable.ic_public), - contentDescription = null, - ) - }, - shape = ButtonBorder, - ) - Spacer(modifier = StdHorzSpacer) - } - if (peopleList.privateMembers.isNotEmpty()) { - val privateMemberSize = peopleList.privateMembers.size - FilterChip( - selected = true, - onClick = {}, - label = { - Text(text = "$privateMemberSize") - }, - leadingIcon = { + } else { + if (peopleList.privateMembers.isNotEmpty()) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = SpacedBy2dp, + ) { Icon( painterResource(R.drawable.lock), + modifier = Size10Modifier, contentDescription = null, ) - }, - shape = ButtonBorder, - ) + Text( + text = peopleList.privateMembers.size.toString(), + fontSize = Font10SP, + ) + } + } + if (peopleList.publicMembers.isNotEmpty()) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = SpacedBy2dp, + ) { + Icon( + painterResource(R.drawable.ic_public), + modifier = Size10Modifier, + contentDescription = null, + ) + Text( + text = peopleList.publicMembers.size.toString(), + fontSize = Font10SP, + ) + } + } } } - Spacer(modifier = StdVertSpacer) - Text( - peopleList.description ?: "", - fontWeight = FontWeight.Light, - overflow = TextOverflow.Ellipsis, - maxLines = 2, - ) } - } - - Column( - modifier = - Modifier - .padding(start = 5.dp) - .padding(vertical = 7.dp), - verticalArrangement = Arrangement.Top, - horizontalAlignment = Alignment.End, - ) { - PeopleListOptionsButton( - peopleListName = peopleList.title, - peopleListDescription = peopleList.description, - onListRename = onRename, - onListDescriptionChange = onDescriptionChange, - onListCloneCreate = onClone, - onListDelete = onDelete, - ) - } - } + }, + ) } @Composable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt index 9a0d02a04c..0b6c36a204 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt @@ -30,6 +30,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -371,3 +372,5 @@ val SpacedBy5dp = Arrangement.spacedBy(Size5dp) val SpacedBy10dp = Arrangement.spacedBy(Size10dp) val PopupUpEffect = RoundedCornerShape(0.dp, 0.dp, 15.dp, 15.dp) + +val Size50ModifierOffset10 = Modifier.size(50.dp).offset(y = (-10).dp) diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index 4dc54cf4a2..ac4aac39d2 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -536,6 +536,7 @@ member members No members + Empty %1$s is not in this list Your Lists No follow lists were found, or you don\'t have any follow lists. Tap below to refresh, or use the menu to create one.