FollowSetScreen: -Make a separate component for the title and description.
- Fix onBroadcastList, making sure to get the latest changes to the set before broadcast. -Make a separate component for FollowSet list items, and make the delete button look a bit better(?). NostrUserListVM: - Minor cleanup.
This commit is contained in:
-1
@@ -230,7 +230,6 @@ class NostrUserListFeedViewModel(
|
|||||||
private val bundler = BundledUpdate(2000, Dispatchers.IO)
|
private val bundler = BundledUpdate(2000, Dispatchers.IO)
|
||||||
|
|
||||||
override fun invalidateData(ignoreIfDoing: Boolean) {
|
override fun invalidateData(ignoreIfDoing: Boolean) {
|
||||||
// refresh()
|
|
||||||
bundler.invalidate(ignoreIfDoing) {
|
bundler.invalidate(ignoreIfDoing) {
|
||||||
// adds the time to perform the refresh into this delay
|
// adds the time to perform the refresh into this delay
|
||||||
// holding off new updates in case of heavy refresh routines.
|
// holding off new updates in case of heavy refresh routines.
|
||||||
|
|||||||
+131
-76
@@ -21,7 +21,10 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.followsets
|
package com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.followsets
|
||||||
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.imePadding
|
import androidx.compose.foundation.layout.imePadding
|
||||||
@@ -29,6 +32,7 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.DropdownMenu
|
||||||
@@ -52,7 +56,10 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.model.User
|
import com.vitorpamplona.amethyst.model.User
|
||||||
@@ -65,8 +72,9 @@ 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.ListVisibility
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.NostrUserListFeedViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.NostrUserListFeedViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.BackButton
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.BackButton
|
||||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
|
||||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.StdPadding
|
||||||
import com.vitorpamplona.quartz.nip51Lists.peopleList.PeopleListEvent
|
import com.vitorpamplona.quartz.nip51Lists.peopleList.PeopleListEvent
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -74,7 +82,6 @@ import kotlinx.coroutines.launch
|
|||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun FollowSetScreen(
|
fun FollowSetScreen(
|
||||||
// TODO: Investigate passing follow set properties rather than follow set object.
|
|
||||||
selectedSetIdentifier: String,
|
selectedSetIdentifier: String,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
navigator: INav,
|
navigator: INav,
|
||||||
@@ -88,60 +95,44 @@ fun FollowSetScreen(
|
|||||||
val followSetState by followSetViewModel.feedContent.collectAsState()
|
val followSetState by followSetViewModel.feedContent.collectAsState()
|
||||||
val uiScope = rememberCoroutineScope()
|
val uiScope = rememberCoroutineScope()
|
||||||
|
|
||||||
val selectedSet by remember(followSetState) {
|
val selectedSetState =
|
||||||
derivedStateOf {
|
remember(followSetState) {
|
||||||
uiScope.launch {
|
derivedStateOf {
|
||||||
delay(500L)
|
uiScope.launch {
|
||||||
}
|
delay(500L)
|
||||||
val note =
|
}
|
||||||
followSetViewModel.getFollowSetNote(
|
val note =
|
||||||
selectedSetIdentifier,
|
followSetViewModel.getFollowSetNote(
|
||||||
accountViewModel.account,
|
selectedSetIdentifier,
|
||||||
)
|
accountViewModel.account,
|
||||||
|
|
||||||
if (note != null) {
|
|
||||||
val event = note.event as PeopleListEvent
|
|
||||||
println("Found list, with title: ${event.nameOrTitle()}")
|
|
||||||
val selectedFollowSet =
|
|
||||||
FollowSet.mapEventToSet(
|
|
||||||
event,
|
|
||||||
accountViewModel.account.signer,
|
|
||||||
)
|
)
|
||||||
return@derivedStateOf selectedFollowSet
|
|
||||||
} else {
|
if (note != null) {
|
||||||
null
|
val event = note.event as PeopleListEvent
|
||||||
|
println("Found list, with title: ${event.nameOrTitle()}")
|
||||||
|
val selectedFollowSet =
|
||||||
|
FollowSet.mapEventToSet(
|
||||||
|
event,
|
||||||
|
accountViewModel.account.signer,
|
||||||
|
)
|
||||||
|
return@derivedStateOf selectedFollowSet
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
BackHandler { navigator.popBack() }
|
BackHandler { navigator.popBack() }
|
||||||
|
|
||||||
when {
|
when {
|
||||||
selectedSet != null -> {
|
selectedSetState.value != null -> {
|
||||||
// TODO: Investigate moving the mapping function to a VM.(related to above TODO).
|
val selectedSet = selectedSetState.value
|
||||||
val users = selectedSet!!.profileList.mapToUsers(accountViewModel).filterNotNull()
|
val users = selectedSet!!.profileList.mapToUsers(accountViewModel).filterNotNull()
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = {
|
title = {
|
||||||
Row(
|
TitleAndDescription(followSet = selectedSet)
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = selectedSet!!.title,
|
|
||||||
)
|
|
||||||
Icon(
|
|
||||||
painter =
|
|
||||||
painterResource(
|
|
||||||
when (selectedSet!!.listVisibility) {
|
|
||||||
ListVisibility.Public -> R.drawable.ic_public
|
|
||||||
ListVisibility.Private -> R.drawable.lock
|
|
||||||
ListVisibility.Mixed -> R.drawable.format_list_bulleted_type
|
|
||||||
},
|
|
||||||
),
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
BackButton(
|
BackButton(
|
||||||
@@ -149,13 +140,18 @@ fun FollowSetScreen(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
// TODO: Fix onSaveList and onBroadcastList
|
|
||||||
ListActionsMenuButton(
|
ListActionsMenuButton(
|
||||||
onSaveList = {},
|
onBroadcastList = {
|
||||||
onBroadcastList = {},
|
val updatedSetNote =
|
||||||
|
followSetViewModel.getFollowSetNote(
|
||||||
|
selectedSet.identifierTag,
|
||||||
|
accountViewModel.account,
|
||||||
|
)
|
||||||
|
accountViewModel.broadcast(updatedSetNote!!)
|
||||||
|
},
|
||||||
onDeleteList = {
|
onDeleteList = {
|
||||||
followSetViewModel.deleteFollowSet(
|
followSetViewModel.deleteFollowSet(
|
||||||
selectedSet!!,
|
selectedSet,
|
||||||
accountViewModel.account,
|
accountViewModel.account,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -183,7 +179,7 @@ fun FollowSetScreen(
|
|||||||
onDeleteUser = {
|
onDeleteUser = {
|
||||||
followSetViewModel.removeUserFromSet(
|
followSetViewModel.removeUserFromSet(
|
||||||
it,
|
it,
|
||||||
selectedSet!!,
|
selectedSet,
|
||||||
accountViewModel.account,
|
accountViewModel.account,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -193,7 +189,7 @@ fun FollowSetScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedSet == null -> {
|
selectedSetState.value == null -> {
|
||||||
accountViewModel.toastManager.toast(
|
accountViewModel.toastManager.toast(
|
||||||
"Follow Set Error",
|
"Follow Set Error",
|
||||||
"Could not find requested follow set",
|
"Could not find requested follow set",
|
||||||
@@ -206,6 +202,46 @@ fun FollowSetScreen(
|
|||||||
|
|
||||||
fun Set<String>.mapToUsers(accountViewModel: AccountViewModel): List<User?> = map { accountViewModel.checkGetOrCreateUser(it) }
|
fun Set<String>.mapToUsers(accountViewModel: AccountViewModel): List<User?> = map { accountViewModel.checkGetOrCreateUser(it) }
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TitleAndDescription(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
followSet: FollowSet,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = followSet.title,
|
||||||
|
)
|
||||||
|
Spacer(modifier = StdHorzSpacer)
|
||||||
|
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
|
||||||
|
},
|
||||||
|
),
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (followSet.description != null) {
|
||||||
|
Text(
|
||||||
|
text = followSet.description,
|
||||||
|
fontSize = 18.sp,
|
||||||
|
fontWeight = FontWeight.Thin,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun FollowSetListView(
|
private fun FollowSetListView(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
@@ -222,30 +258,60 @@ private fun FollowSetListView(
|
|||||||
state = listState,
|
state = listState,
|
||||||
) {
|
) {
|
||||||
itemsIndexed(followSetList, key = { _, item -> item.pubkeyHex }) { _, item ->
|
itemsIndexed(followSetList, key = { _, item -> item.pubkeyHex }) { _, item ->
|
||||||
Row {
|
FollowSetListItem(
|
||||||
IconButton(
|
modifier = Modifier.animateItem(),
|
||||||
onClick = {
|
user = item,
|
||||||
onDeleteUser(item.pubkeyHex)
|
accountViewModel = accountViewModel,
|
||||||
},
|
nav = nav,
|
||||||
) {
|
onDeleteUser = onDeleteUser,
|
||||||
Icon(
|
)
|
||||||
imageVector = Icons.Default.Delete,
|
}
|
||||||
contentDescription = null,
|
}
|
||||||
)
|
}
|
||||||
}
|
|
||||||
UserCompose(item, accountViewModel = accountViewModel, nav = nav)
|
@Composable
|
||||||
HorizontalDivider(
|
fun FollowSetListItem(
|
||||||
thickness = DividerThickness,
|
modifier: Modifier = Modifier,
|
||||||
|
user: User,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: INav,
|
||||||
|
onDeleteUser: (String) -> Unit,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
Row {
|
||||||
|
UserCompose(
|
||||||
|
user,
|
||||||
|
overallModifier = StdPadding.weight(1f, fill = false),
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
nav = nav,
|
||||||
|
)
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
onDeleteUser(user.pubkeyHex)
|
||||||
|
},
|
||||||
|
modifier =
|
||||||
|
StdPadding
|
||||||
|
.align(Alignment.CenterVertically)
|
||||||
|
.background(
|
||||||
|
color = MaterialTheme.colorScheme.errorContainer,
|
||||||
|
shape = RoundedCornerShape(size = 5.dp),
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Delete,
|
||||||
|
contentDescription = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
HorizontalDivider()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ListActionsMenuButton(
|
fun ListActionsMenuButton(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onSaveList: () -> Unit,
|
|
||||||
onBroadcastList: () -> Unit,
|
onBroadcastList: () -> Unit,
|
||||||
onDeleteList: () -> Unit,
|
onDeleteList: () -> Unit,
|
||||||
) {
|
) {
|
||||||
@@ -258,7 +324,6 @@ fun ListActionsMenuButton(
|
|||||||
ListActionsMenu(
|
ListActionsMenu(
|
||||||
onCloseMenu = { isActionListOpen.value = false },
|
onCloseMenu = { isActionListOpen.value = false },
|
||||||
isOpen = isActionListOpen.value,
|
isOpen = isActionListOpen.value,
|
||||||
onSaveList = onSaveList,
|
|
||||||
onBroadcastList = onBroadcastList,
|
onBroadcastList = onBroadcastList,
|
||||||
onDeleteList = onDeleteList,
|
onDeleteList = onDeleteList,
|
||||||
)
|
)
|
||||||
@@ -270,7 +335,6 @@ fun ListActionsMenu(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onCloseMenu: () -> Unit,
|
onCloseMenu: () -> Unit,
|
||||||
isOpen: Boolean,
|
isOpen: Boolean,
|
||||||
onSaveList: () -> Unit,
|
|
||||||
onBroadcastList: () -> Unit,
|
onBroadcastList: () -> Unit,
|
||||||
onDeleteList: () -> Unit,
|
onDeleteList: () -> Unit,
|
||||||
) {
|
) {
|
||||||
@@ -278,15 +342,6 @@ fun ListActionsMenu(
|
|||||||
expanded = isOpen,
|
expanded = isOpen,
|
||||||
onDismissRequest = onCloseMenu,
|
onDismissRequest = onCloseMenu,
|
||||||
) {
|
) {
|
||||||
DropdownMenuItem(
|
|
||||||
text = {
|
|
||||||
Text("Save Changes")
|
|
||||||
},
|
|
||||||
onClick = {
|
|
||||||
onSaveList()
|
|
||||||
onCloseMenu()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = {
|
text = {
|
||||||
Text("Broadcast List")
|
Text("Broadcast List")
|
||||||
|
|||||||
Reference in New Issue
Block a user