Remove map in FollowSetFeedFilter, in favor of retrieving note from cache if modification is needed. Amend FollowSet to carry the address of the note. Add Refreshable for the Lists screen.
This commit is contained in:
@@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.ui.dal
|
|||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.vitorpamplona.amethyst.model.Account
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.FollowSet
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.FollowSet
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.coroutines.cancellation.CancellationException
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
@@ -30,12 +29,10 @@ import kotlin.coroutines.cancellation.CancellationException
|
|||||||
class FollowSetFeedFilter(
|
class FollowSetFeedFilter(
|
||||||
val account: Account,
|
val account: Account,
|
||||||
) : FeedFilter<FollowSet>() {
|
) : FeedFilter<FollowSet>() {
|
||||||
private val followSetEventPairs: MutableMap<AddressableNote, FollowSet> = mutableMapOf()
|
|
||||||
|
|
||||||
override fun feedKey(): String = account.userProfile().pubkeyHex
|
override fun feedKey(): String = account.userProfile().pubkeyHex
|
||||||
|
|
||||||
override fun feed(): List<FollowSet> {
|
override fun feed(): List<FollowSet> {
|
||||||
val userFollowSets = account.userProfile().followSets
|
val userFollowSets = account.userProfile().followSetNotes
|
||||||
if (userFollowSets.isEmpty()) {
|
if (userFollowSets.isEmpty()) {
|
||||||
account.scope.launch {
|
account.scope.launch {
|
||||||
try {
|
try {
|
||||||
@@ -47,14 +44,7 @@ class FollowSetFeedFilter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateFollowSetEventPairs(userFollowSets)
|
val followSets = userFollowSets.map { account.mapNoteToFollowSet(it) }
|
||||||
return followSetEventPairs.values.toList()
|
return followSets
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateFollowSetEventPairs(notes: Set<AddressableNote>) {
|
|
||||||
notes.forEach { note ->
|
|
||||||
val noteAndSetPair = note to account.mapNoteToFollowSet(note)
|
|
||||||
followSetEventPairs.putIfAbsent(noteAndSetPair.first, noteAndSetPair.second)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+47
-7
@@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.lists
|
|||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -42,6 +43,7 @@ import androidx.compose.runtime.DisposableEffect
|
|||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
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.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
@@ -60,6 +62,7 @@ import com.vitorpamplona.amethyst.R
|
|||||||
import com.vitorpamplona.amethyst.ui.feeds.FeedEmpty
|
import com.vitorpamplona.amethyst.ui.feeds.FeedEmpty
|
||||||
import com.vitorpamplona.amethyst.ui.feeds.FeedError
|
import com.vitorpamplona.amethyst.ui.feeds.FeedError
|
||||||
import com.vitorpamplona.amethyst.ui.feeds.LoadingFeed
|
import com.vitorpamplona.amethyst.ui.feeds.LoadingFeed
|
||||||
|
import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.TopBarWithBackButton
|
import com.vitorpamplona.amethyst.ui.navigation.TopBarWithBackButton
|
||||||
import com.vitorpamplona.amethyst.ui.screen.NostrUserFollowSetFeedViewModel
|
import com.vitorpamplona.amethyst.ui.screen.NostrUserFollowSetFeedViewModel
|
||||||
@@ -71,6 +74,9 @@ import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
|||||||
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
|
||||||
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn
|
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn
|
||||||
|
import com.vitorpamplona.quartz.nip51Lists.PeopleListEvent
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ListsScreen(
|
fun ListsScreen(
|
||||||
@@ -83,6 +89,7 @@ fun ListsScreen(
|
|||||||
factory = NostrUserFollowSetFeedViewModel.Factory(accountViewModel.account),
|
factory = NostrUserFollowSetFeedViewModel.Factory(accountViewModel.account),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val currentCoroutineScope = rememberCoroutineScope()
|
||||||
val lifeCycleOwner = LocalLifecycleOwner.current
|
val lifeCycleOwner = LocalLifecycleOwner.current
|
||||||
|
|
||||||
DisposableEffect(lifeCycleOwner) {
|
DisposableEffect(lifeCycleOwner) {
|
||||||
@@ -109,6 +116,17 @@ fun ListsScreen(
|
|||||||
refresh = {
|
refresh = {
|
||||||
followSetsViewModel.invalidateData()
|
followSetsViewModel.invalidateData()
|
||||||
},
|
},
|
||||||
|
openItem = {
|
||||||
|
currentCoroutineScope.launch(Dispatchers.IO) {
|
||||||
|
val note = followSetsViewModel.getFollowSetAddressable(it, accountViewModel.account)
|
||||||
|
if (note != null) {
|
||||||
|
val event = note.event as PeopleListEvent
|
||||||
|
println("Found list, with title: ${event.nameOrTitle()}")
|
||||||
|
} else {
|
||||||
|
println("No corresponding note found for this list.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
accountViewModel,
|
accountViewModel,
|
||||||
nav,
|
nav,
|
||||||
)
|
)
|
||||||
@@ -118,6 +136,7 @@ fun ListsScreen(
|
|||||||
fun CustomListsScreen(
|
fun CustomListsScreen(
|
||||||
followSetState: FollowSetState,
|
followSetState: FollowSetState,
|
||||||
refresh: () -> Unit,
|
refresh: () -> Unit,
|
||||||
|
openItem: (identifier: String) -> Unit,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
@@ -130,7 +149,11 @@ fun CustomListsScreen(
|
|||||||
TopBarWithBackButton(stringRes(R.string.my_lists), nav::popBack)
|
TopBarWithBackButton(stringRes(R.string.my_lists), nav::popBack)
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
Column(Modifier.padding(it).fillMaxHeight()) {
|
Column(
|
||||||
|
Modifier
|
||||||
|
.padding(it)
|
||||||
|
.fillMaxHeight(),
|
||||||
|
) {
|
||||||
when (followSetState) {
|
when (followSetState) {
|
||||||
FollowSetState.Loading -> LoadingFeed()
|
FollowSetState.Loading -> LoadingFeed()
|
||||||
|
|
||||||
@@ -139,6 +162,7 @@ fun CustomListsScreen(
|
|||||||
FollowListLoaded(
|
FollowListLoaded(
|
||||||
loadedFeedState = followSetFeed,
|
loadedFeedState = followSetFeed,
|
||||||
onRefresh = refresh,
|
onRefresh = refresh,
|
||||||
|
onItemClick = openItem,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,16 +188,26 @@ fun FollowListLoaded(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
loadedFeedState: List<FollowSet>,
|
loadedFeedState: List<FollowSet>,
|
||||||
onRefresh: () -> Unit = {},
|
onRefresh: () -> Unit = {},
|
||||||
|
onItemClick: (String) -> Unit = {},
|
||||||
) {
|
) {
|
||||||
Log.d("FollowSetComposable", "FollowListLoaded: Follow Set size: ${loadedFeedState.size}")
|
Log.d("FollowSetComposable", "FollowListLoaded: Follow Set size: ${loadedFeedState.size}")
|
||||||
|
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
LazyColumn(
|
RefresheableBox(
|
||||||
state = listState,
|
onRefresh = onRefresh,
|
||||||
contentPadding = FeedPadding,
|
|
||||||
) {
|
) {
|
||||||
itemsIndexed(loadedFeedState, key = { _, item -> item.title }) { index, set ->
|
LazyColumn(
|
||||||
CustomListItem(followSet = set)
|
state = listState,
|
||||||
|
contentPadding = FeedPadding,
|
||||||
|
) {
|
||||||
|
itemsIndexed(loadedFeedState, key = { _, item -> item.identifierTag }) { index, set ->
|
||||||
|
CustomListItem(
|
||||||
|
followSet = set,
|
||||||
|
onFollowSetClick = {
|
||||||
|
onItemClick(set.identifierTag)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,10 +216,12 @@ fun FollowListLoaded(
|
|||||||
fun CustomListItem(
|
fun CustomListItem(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
followSet: FollowSet,
|
followSet: FollowSet,
|
||||||
|
onFollowSetClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier =
|
modifier =
|
||||||
modifier
|
modifier
|
||||||
|
.clickable(onClick = onFollowSetClick)
|
||||||
.border(
|
.border(
|
||||||
width = Dp.Hairline,
|
width = Dp.Hairline,
|
||||||
color = Color.Gray,
|
color = Color.Gray,
|
||||||
@@ -261,15 +297,19 @@ fun CustomListItem(
|
|||||||
private fun ListItemPreview() {
|
private fun ListItemPreview() {
|
||||||
val sampleFollowSet =
|
val sampleFollowSet =
|
||||||
FollowSet(
|
FollowSet(
|
||||||
visibility = ListVisibility.Mixed,
|
identifierTag = "00001-2222",
|
||||||
title = "Sample List Title",
|
title = "Sample List Title",
|
||||||
description = "Sample List Description",
|
description = "Sample List Description",
|
||||||
|
visibility = ListVisibility.Mixed,
|
||||||
emptySet(),
|
emptySet(),
|
||||||
)
|
)
|
||||||
ThemeComparisonColumn {
|
ThemeComparisonColumn {
|
||||||
CustomListItem(
|
CustomListItem(
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
sampleFollowSet,
|
sampleFollowSet,
|
||||||
|
onFollowSetClick = {
|
||||||
|
println("follow set: ${sampleFollowSet.identifierTag}")
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-3
@@ -26,9 +26,10 @@ import com.vitorpamplona.quartz.nip51Lists.PeopleListEvent
|
|||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
data class FollowSet(
|
data class FollowSet(
|
||||||
val visibility: ListVisibility,
|
val identifierTag: String,
|
||||||
val title: String,
|
val title: String,
|
||||||
val description: String?,
|
val description: String?,
|
||||||
|
val visibility: ListVisibility,
|
||||||
val profileList: Set<String>,
|
val profileList: Set<String>,
|
||||||
) : NostrList(listVisibility = visibility, content = profileList) {
|
) : NostrList(listVisibility = visibility, content = profileList) {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -36,6 +37,7 @@ data class FollowSet(
|
|||||||
event: PeopleListEvent,
|
event: PeopleListEvent,
|
||||||
signer: NostrSigner,
|
signer: NostrSigner,
|
||||||
): FollowSet {
|
): FollowSet {
|
||||||
|
val address = event.address()
|
||||||
val dTag = event.dTag()
|
val dTag = event.dTag()
|
||||||
val listTitle = event.nameOrTitle() ?: dTag
|
val listTitle = event.nameOrTitle() ?: dTag
|
||||||
val listDescription = event.description() ?: ""
|
val listDescription = event.description() ?: ""
|
||||||
@@ -44,16 +46,18 @@ data 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(
|
||||||
visibility = ListVisibility.Private,
|
identifierTag = address.toValue(),
|
||||||
title = listTitle,
|
title = listTitle,
|
||||||
description = listDescription,
|
description = listDescription,
|
||||||
|
visibility = ListVisibility.Private,
|
||||||
profileList = privateFollows.toSet(),
|
profileList = privateFollows.toSet(),
|
||||||
)
|
)
|
||||||
} else if (publicFollows.isNotEmpty() && privateFollows.isEmpty()) {
|
} else if (publicFollows.isNotEmpty() && privateFollows.isEmpty()) {
|
||||||
FollowSet(
|
FollowSet(
|
||||||
visibility = ListVisibility.Public,
|
identifierTag = address.toValue(),
|
||||||
title = listTitle,
|
title = listTitle,
|
||||||
description = listDescription,
|
description = listDescription,
|
||||||
|
visibility = ListVisibility.Public,
|
||||||
profileList = publicFollows.toSet(),
|
profileList = publicFollows.toSet(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user