Unify the underlying implementation for follow sets and use it in both profile actions and CustomListsScreen.

This commit is contained in:
KotlinGeekDev
2025-03-21 13:04:16 +01:00
parent fa7425e114
commit c802c395a6
5 changed files with 87 additions and 36 deletions
@@ -226,8 +226,14 @@ fun CustomListItem(
) )
} }
followSet.isPrivate.let { followSet.type.let {
val text by derivedStateOf { if (it) "Private" else "Public" } val text by derivedStateOf {
when (it) {
ListType.Public -> "Public"
ListType.Private -> "Private"
ListType.Mixed -> "Mixed"
}
}
Column( Column(
// modifier = modifier.weight(1f), // modifier = modifier.weight(1f),
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
@@ -236,7 +242,11 @@ fun CustomListItem(
Icon( Icon(
painter = painter =
painterResource( painterResource(
if (followSet.isPrivate) R.drawable.incognito else R.drawable.ic_public, when (it) {
ListType.Public -> R.drawable.ic_public
ListType.Private -> R.drawable.incognito
ListType.Mixed -> R.drawable.format_list_bulleted_type
},
), ),
contentDescription = "Icon for $text List", contentDescription = "Icon for $text List",
) )
@@ -251,7 +261,7 @@ fun CustomListItem(
private fun ListItemPreview() { private fun ListItemPreview() {
val sampleFollowSet = val sampleFollowSet =
FollowSet( FollowSet(
isPrivate = false, type = ListType.Mixed,
title = "Sample List Title", title = "Sample List Title",
description = "Sample List Description", description = "Sample List Description",
emptySet(), emptySet(),
@@ -25,8 +25,8 @@ import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip51Lists.PeopleListEvent import com.vitorpamplona.quartz.nip51Lists.PeopleListEvent
@Stable @Stable
class FollowSet( data class FollowSet(
val isPrivate: Boolean, val type: ListType,
val title: String, val title: String,
val description: String?, val description: String?,
val profileList: Set<String>, val profileList: Set<String>,
@@ -44,14 +44,14 @@ class FollowSet(
event.privateTaggedUsers(signer) { userList -> privateFollows.addAll(userList) } event.privateTaggedUsers(signer) { userList -> privateFollows.addAll(userList) }
return if (publicFollows.isEmpty() && privateFollows.isNotEmpty()) { return if (publicFollows.isEmpty() && privateFollows.isNotEmpty()) {
FollowSet( FollowSet(
isPrivate = true, type = ListType.Public,
title = listTitle, title = listTitle,
description = listDescription, description = listDescription,
profileList = privateFollows.toSet(), profileList = privateFollows.toSet(),
) )
} else if (publicFollows.isNotEmpty() && privateFollows.isEmpty()) { } else if (publicFollows.isNotEmpty() && privateFollows.isEmpty()) {
FollowSet( FollowSet(
isPrivate = false, type = ListType.Private,
title = listTitle, title = listTitle,
description = listDescription, description = listDescription,
profileList = publicFollows.toSet(), profileList = publicFollows.toSet(),
@@ -0,0 +1,27 @@
/**
* Copyright (c) 2024 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 ListType {
Public,
Private,
Mixed,
}
@@ -61,6 +61,8 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.PopupProperties import androidx.compose.ui.window.PopupProperties
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.FollowSet
import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.ListType
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
@@ -70,9 +72,9 @@ import kotlinx.coroutines.launch
@Composable @Composable
fun FollowSetsActionMenu( fun FollowSetsActionMenu(
userHex: String, userHex: String,
followLists: List<FollowInfo>, followLists: List<FollowSet>,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
addUser: (followListItemIndex: Int, list: FollowInfo) -> Unit, addUser: (followListItemIndex: Int, list: FollowSet) -> Unit,
removeUser: (followListItemIndex: Int) -> Unit, removeUser: (followListItemIndex: Int) -> Unit,
) { ) {
val (isMenuOpen, setMenuValue) = remember { mutableStateOf(false) } val (isMenuOpen, setMenuValue) = remember { mutableStateOf(false) }
@@ -130,17 +132,17 @@ fun FollowSetsActionMenu(
text = { text = {
FollowSetItem( FollowSetItem(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
listHeader = list.name, listHeader = list.title,
listIsPublic = !list.isPrivate, listType = list.type,
isUserInList = list.memberList.contains(userHex), isUserInList = list.profileList.contains(userHex),
onRemoveUser = { onRemoveUser = {
removeUser(index) removeUser(index)
}, },
onAddUser = { onAddUser = {
println("List contains user -> ${list.memberList.contains(userHex)}") println("List contains user -> ${list.profileList.contains(userHex)}")
println("Adding user to List -> ${list.name}") println("Adding user to List -> ${list.title}")
addUser(index, list) addUser(index, list)
println("List contains user -> ${list.memberList.contains(userHex)}") println("List contains user -> ${list.profileList.contains(userHex)}")
}, },
) )
}, },
@@ -169,17 +171,18 @@ private fun DropDownMenuHeader(
} }
} }
data class FollowInfo( fun generateFollowLists(): List<FollowSet> =
val name: String,
val isPrivate: Boolean,
val memberList: List<String> = listOf(),
)
fun generateFollowLists(): List<FollowInfo> =
List(10) { index: Int -> List(10) { index: Int ->
FollowInfo( FollowSet(
name = "List No $index", type =
isPrivate = index % 2 == 0, when {
index % 2 == 0 -> ListType.Private
index in listOf(3, 7, 9) -> ListType.Mixed
else -> ListType.Public
},
title = "List No $index",
description = null,
profileList = emptySet(),
) )
} }
@@ -187,7 +190,7 @@ fun generateFollowLists(): List<FollowInfo> =
fun FollowSetItem( fun FollowSetItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
listHeader: String, listHeader: String,
listIsPublic: Boolean, listType: ListType,
isUserInList: Boolean, isUserInList: Boolean,
onAddUser: () -> Unit, onAddUser: () -> Unit,
onRemoveUser: () -> Unit, onRemoveUser: () -> Unit,
@@ -278,8 +281,15 @@ fun FollowSetItem(
} }
} }
listIsPublic.let { listType.let {
val text by derivedStateOf { if (!it) "Private" else "Public" } val text by derivedStateOf {
when (it) {
ListType.Public -> "Public"
ListType.Private -> "Private"
ListType.Mixed -> "Mixed"
}
}
Column( Column(
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
@@ -287,7 +297,11 @@ fun FollowSetItem(
Icon( Icon(
painter = painter =
painterResource( painterResource(
if (!it) R.drawable.incognito else R.drawable.ic_public, when (it) {
ListType.Public -> R.drawable.ic_public
ListType.Private -> R.drawable.incognito
ListType.Mixed -> R.drawable.format_list_bulleted_type
},
), ),
contentDescription = "Icon for $text List", contentDescription = "Icon for $text List",
) )
@@ -59,15 +59,15 @@ fun ProfileActions(
followLists = tempFollowLists, followLists = tempFollowLists,
addUser = { index, list -> addUser = { index, list ->
Log.d("Amethyst", "ProfileActions: Updating list ...") Log.d("Amethyst", "ProfileActions: Updating list ...")
val newList = tempFollowLists[index].memberList + baseUser.pubkeyHex val newList = tempFollowLists[index].profileList + baseUser.pubkeyHex
tempFollowLists[index] = tempFollowLists[index].copy(memberList = newList) tempFollowLists[index] = tempFollowLists[index].copy(profileList = newList)
println("Updated List. New size: ${tempFollowLists[index].memberList.size}") println("Updated List. New size: ${tempFollowLists[index].profileList.size}")
}, },
removeUser = { index -> removeUser = { index ->
Log.d("Amethyst", "ProfileActions: Updating list ...") Log.d("Amethyst", "ProfileActions: Updating list ...")
val newList = tempFollowLists[index].memberList - baseUser.pubkeyHex val newList = tempFollowLists[index].profileList - baseUser.pubkeyHex
tempFollowLists[index] = tempFollowLists[index].copy(memberList = newList) tempFollowLists[index] = tempFollowLists[index].copy(profileList = newList)
println("Updated List. New size: ${tempFollowLists[index].memberList.size}") println("Updated List. New size: ${tempFollowLists[index].profileList.size}")
}, },
) )
} }