mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-04-09 04:18:11 +02:00
Moves follow list states to the AccountViewModel
This commit is contained in:
parent
c64d179f7f
commit
6acd3ca091
@ -3149,6 +3149,17 @@ class Account(
|
||||
}
|
||||
}
|
||||
|
||||
fun getAllPeopleLists(): List<AddressableNote> = getAllPeopleLists(keyPair.pubKeyHex)
|
||||
|
||||
fun getAllPeopleLists(pubkey: HexKey): List<AddressableNote> =
|
||||
LocalCache.addressables
|
||||
.filter { _, addressableNote ->
|
||||
val event = (addressableNote.event as? PeopleListEvent)
|
||||
event != null &&
|
||||
event.pubKey == pubkey &&
|
||||
(event.hasAnyTaggedUser() || event.publicAndPrivateUserCache?.isNotEmpty() == true)
|
||||
}
|
||||
|
||||
fun setHideDeleteRequestDialog() {
|
||||
hideDeleteRequestDialog = true
|
||||
saveable.invalidateData()
|
||||
|
@ -51,8 +51,6 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
@ -69,20 +67,14 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.map
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import coil.Coil
|
||||
import coil.compose.AsyncImage
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.model.GLOBAL_FOLLOWS
|
||||
import com.vitorpamplona.amethyst.model.KIND3_FOLLOWS
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.service.NostrAccountDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrChannelDataSource
|
||||
@ -100,7 +92,6 @@ import com.vitorpamplona.amethyst.service.NostrSingleUserDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrThreadDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrVideoDataSource
|
||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.note.AmethystIcon
|
||||
@ -117,6 +108,15 @@ import com.vitorpamplona.amethyst.ui.note.UserCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||
import com.vitorpamplona.amethyst.ui.note.types.LongCommunityHeader
|
||||
import com.vitorpamplona.amethyst.ui.note.types.ShortCommunityHeader
|
||||
import com.vitorpamplona.amethyst.ui.screen.CodeName
|
||||
import com.vitorpamplona.amethyst.ui.screen.CodeNameType
|
||||
import com.vitorpamplona.amethyst.ui.screen.CommunityName
|
||||
import com.vitorpamplona.amethyst.ui.screen.FollowListState
|
||||
import com.vitorpamplona.amethyst.ui.screen.GeoHashName
|
||||
import com.vitorpamplona.amethyst.ui.screen.HashtagName
|
||||
import com.vitorpamplona.amethyst.ui.screen.Name
|
||||
import com.vitorpamplona.amethyst.ui.screen.PeopleListName
|
||||
import com.vitorpamplona.amethyst.ui.screen.ResourceName
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.DislayGeoTagHeader
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.GeoHashActionOptions
|
||||
@ -142,27 +142,11 @@ import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import com.vitorpamplona.ammolite.relays.Client
|
||||
import com.vitorpamplona.ammolite.relays.RelayPool
|
||||
import com.vitorpamplona.quartz.events.ChatroomKey
|
||||
import com.vitorpamplona.quartz.events.ContactListEvent
|
||||
import com.vitorpamplona.quartz.events.DeletionEvent
|
||||
import com.vitorpamplona.quartz.events.MuteListEvent
|
||||
import com.vitorpamplona.quartz.events.PeopleListEvent
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combineTransform
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.transformLatest
|
||||
|
||||
@Composable
|
||||
fun AppTopBar(
|
||||
followLists: FollowListViewModel,
|
||||
navEntryState: State<NavBackStackEntry?>,
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
@ -184,14 +168,14 @@ fun AppTopBar(
|
||||
derivedStateOf { navEntryState.value?.arguments?.getString("id") }
|
||||
}
|
||||
|
||||
RenderTopRouteBar(currentRoute, id, followLists, openDrawer, accountViewModel, nav, navPopBack)
|
||||
RenderTopRouteBar(currentRoute, id, accountViewModel.feedStates.feedListOptions, openDrawer, accountViewModel, nav, navPopBack)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RenderTopRouteBar(
|
||||
currentRoute: String?,
|
||||
id: String?,
|
||||
followLists: FollowListViewModel,
|
||||
followLists: FollowListState,
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
@ -458,7 +442,7 @@ private fun ChannelTopBar(
|
||||
|
||||
@Composable
|
||||
fun StoriesTopBar(
|
||||
followLists: FollowListViewModel,
|
||||
followLists: FollowListState,
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
@ -477,7 +461,7 @@ fun StoriesTopBar(
|
||||
|
||||
@Composable
|
||||
fun HomeTopBar(
|
||||
followLists: FollowListViewModel,
|
||||
followLists: FollowListState,
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
@ -500,7 +484,7 @@ fun HomeTopBar(
|
||||
|
||||
@Composable
|
||||
fun NotificationTopBar(
|
||||
followLists: FollowListViewModel,
|
||||
followLists: FollowListState,
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
@ -519,7 +503,7 @@ fun NotificationTopBar(
|
||||
|
||||
@Composable
|
||||
fun DiscoveryTopBar(
|
||||
followLists: FollowListViewModel,
|
||||
followLists: FollowListState,
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
@ -609,7 +593,7 @@ private fun LoggedInUserPictureDrawer(
|
||||
|
||||
@Composable
|
||||
fun FollowListWithRoutes(
|
||||
followListsModel: FollowListViewModel,
|
||||
followListsModel: FollowListState,
|
||||
listName: String,
|
||||
onChange: (CodeName) -> Unit,
|
||||
) {
|
||||
@ -624,7 +608,7 @@ fun FollowListWithRoutes(
|
||||
|
||||
@Composable
|
||||
fun FollowListWithoutRoutes(
|
||||
followListsModel: FollowListViewModel,
|
||||
followListsModel: FollowListState,
|
||||
listName: String,
|
||||
onChange: (CodeName) -> Unit,
|
||||
) {
|
||||
@ -637,191 +621,6 @@ fun FollowListWithoutRoutes(
|
||||
)
|
||||
}
|
||||
|
||||
enum class CodeNameType {
|
||||
HARDCODED,
|
||||
PEOPLE_LIST,
|
||||
ROUTE,
|
||||
}
|
||||
|
||||
abstract class Name {
|
||||
abstract fun name(): String
|
||||
|
||||
open fun name(context: Context) = name()
|
||||
}
|
||||
|
||||
class GeoHashName(
|
||||
val geoHashTag: String,
|
||||
) : Name() {
|
||||
override fun name() = "/g/$geoHashTag"
|
||||
}
|
||||
|
||||
class HashtagName(
|
||||
val hashTag: String,
|
||||
) : Name() {
|
||||
override fun name() = "#$hashTag"
|
||||
}
|
||||
|
||||
class ResourceName(
|
||||
val resourceId: Int,
|
||||
) : Name() {
|
||||
override fun name() = " $resourceId " // Space to make sure it goes first
|
||||
|
||||
override fun name(context: Context) = stringRes(context, resourceId)
|
||||
}
|
||||
|
||||
class PeopleListName(
|
||||
val note: AddressableNote,
|
||||
) : Name() {
|
||||
override fun name() = (note.event as? PeopleListEvent)?.nameOrTitle() ?: note.dTag() ?: ""
|
||||
}
|
||||
|
||||
class CommunityName(
|
||||
val note: AddressableNote,
|
||||
) : Name() {
|
||||
override fun name() = "/n/${(note.dTag() ?: "")}"
|
||||
}
|
||||
|
||||
@Immutable data class CodeName(
|
||||
val code: String,
|
||||
val name: Name,
|
||||
val type: CodeNameType,
|
||||
)
|
||||
|
||||
@Stable
|
||||
class FollowListViewModel(
|
||||
val account: Account,
|
||||
) : ViewModel() {
|
||||
val kind3Follow =
|
||||
CodeName(KIND3_FOLLOWS, ResourceName(R.string.follow_list_kind3follows), CodeNameType.HARDCODED)
|
||||
val globalFollow =
|
||||
CodeName(GLOBAL_FOLLOWS, ResourceName(R.string.follow_list_global), CodeNameType.HARDCODED)
|
||||
val muteListFollow =
|
||||
CodeName(
|
||||
MuteListEvent.blockListFor(account.userProfile().pubkeyHex),
|
||||
ResourceName(R.string.follow_list_mute_list),
|
||||
CodeNameType.HARDCODED,
|
||||
)
|
||||
val defaultLists = persistentListOf(kind3Follow, globalFollow, muteListFollow)
|
||||
|
||||
val livePeopleListsFlow: Flow<List<CodeName>> by lazy {
|
||||
flow {
|
||||
checkNotInMainThread()
|
||||
|
||||
emit(getPeopleLists())
|
||||
emitAll(livePeopleListsFlowObservable)
|
||||
}
|
||||
}
|
||||
|
||||
fun getPeopleLists(): List<CodeName> =
|
||||
LocalCache.addressables
|
||||
.mapNotNull { _, addressableNote ->
|
||||
val event = (addressableNote.event as? PeopleListEvent)
|
||||
// Has to have an list
|
||||
if (
|
||||
event != null &&
|
||||
event.pubKey == account.userProfile().pubkeyHex &&
|
||||
(event.hasAnyTaggedUser() || event.publicAndPrivateUserCache?.isNotEmpty() == true)
|
||||
) {
|
||||
CodeName(event.address().toTag(), PeopleListName(addressableNote), CodeNameType.PEOPLE_LIST)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}.sortedBy { it.name.name() }
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val livePeopleListsFlowObservable: Flow<List<CodeName>> =
|
||||
LocalCache.live.newEventBundles.transformLatest { newNotes ->
|
||||
val hasNewList =
|
||||
newNotes.any {
|
||||
val noteEvent = it.event
|
||||
|
||||
noteEvent?.pubKey() == account.userProfile().pubkeyHex &&
|
||||
(
|
||||
(
|
||||
noteEvent is PeopleListEvent ||
|
||||
noteEvent is MuteListEvent ||
|
||||
noteEvent is ContactListEvent
|
||||
) ||
|
||||
(
|
||||
noteEvent is DeletionEvent &&
|
||||
(
|
||||
noteEvent.deleteEvents().any {
|
||||
LocalCache.getNoteIfExists(it)?.event is PeopleListEvent
|
||||
} ||
|
||||
noteEvent.deleteAddresses().any {
|
||||
it.kind == PeopleListEvent.KIND
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (hasNewList) {
|
||||
emit(getPeopleLists())
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val liveKind3FollowsFlow: Flow<List<CodeName>> =
|
||||
account.liveKind3FollowsFlow.transformLatest {
|
||||
checkNotInMainThread()
|
||||
|
||||
val communities =
|
||||
it.communities.mapNotNull {
|
||||
LocalCache.checkGetOrCreateAddressableNote(it)?.let { communityNote ->
|
||||
CodeName(
|
||||
"Community/${communityNote.idHex}",
|
||||
CommunityName(communityNote),
|
||||
CodeNameType.ROUTE,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val hashtags =
|
||||
it.hashtags.map {
|
||||
CodeName("Hashtag/$it", HashtagName(it), CodeNameType.ROUTE)
|
||||
}
|
||||
|
||||
val geotags =
|
||||
it.geotags.map {
|
||||
CodeName("Geohash/$it", GeoHashName(it), CodeNameType.ROUTE)
|
||||
}
|
||||
|
||||
emit(
|
||||
(communities + hashtags + geotags).sortedBy { it.name.name() },
|
||||
)
|
||||
}
|
||||
|
||||
private val _kind3GlobalPeopleRoutes =
|
||||
combineTransform(livePeopleListsFlow, liveKind3FollowsFlow) { myLivePeopleListsFlow, myLiveKind3FollowsFlow ->
|
||||
checkNotInMainThread()
|
||||
emit(
|
||||
listOf(listOf(kind3Follow, globalFollow), myLivePeopleListsFlow, myLiveKind3FollowsFlow, listOf(muteListFollow))
|
||||
.flatten()
|
||||
.toImmutableList(),
|
||||
)
|
||||
}
|
||||
val kind3GlobalPeopleRoutes = _kind3GlobalPeopleRoutes.flowOn(Dispatchers.IO).stateIn(viewModelScope, SharingStarted.Eagerly, defaultLists)
|
||||
|
||||
private val _kind3GlobalPeople =
|
||||
combineTransform(livePeopleListsFlow, liveKind3FollowsFlow) { myLivePeopleListsFlow, myLiveKind3FollowsFlow ->
|
||||
checkNotInMainThread()
|
||||
emit(
|
||||
listOf(listOf(kind3Follow, globalFollow), myLivePeopleListsFlow, listOf(muteListFollow))
|
||||
.flatten()
|
||||
.toImmutableList(),
|
||||
)
|
||||
}
|
||||
|
||||
val kind3GlobalPeople = _kind3GlobalPeople.flowOn(Dispatchers.IO).stateIn(viewModelScope, SharingStarted.Eagerly, defaultLists)
|
||||
|
||||
class Factory(
|
||||
val account: Account,
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <FollowListViewModel : ViewModel> create(modelClass: Class<FollowListViewModel>): FollowListViewModel = FollowListViewModel(account) as FollowListViewModel
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SimpleTextSpinner(
|
||||
placeholderCode: String,
|
||||
|
@ -0,0 +1,240 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.model.GLOBAL_FOLLOWS
|
||||
import com.vitorpamplona.amethyst.model.KIND3_FOLLOWS
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.quartz.events.ContactListEvent
|
||||
import com.vitorpamplona.quartz.events.DeletionEvent
|
||||
import com.vitorpamplona.quartz.events.MuteListEvent
|
||||
import com.vitorpamplona.quartz.events.PeopleListEvent
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combineTransform
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.transformLatest
|
||||
|
||||
@Stable
|
||||
class FollowListState(
|
||||
val account: Account,
|
||||
val viewModelScope: CoroutineScope,
|
||||
) {
|
||||
val kind3Follow =
|
||||
CodeName(
|
||||
KIND3_FOLLOWS,
|
||||
ResourceName(R.string.follow_list_kind3follows),
|
||||
CodeNameType.HARDCODED,
|
||||
)
|
||||
val globalFollow =
|
||||
CodeName(GLOBAL_FOLLOWS, ResourceName(R.string.follow_list_global), CodeNameType.HARDCODED)
|
||||
val muteListFollow =
|
||||
CodeName(
|
||||
MuteListEvent.blockListFor(account.userProfile().pubkeyHex),
|
||||
ResourceName(R.string.follow_list_mute_list),
|
||||
CodeNameType.HARDCODED,
|
||||
)
|
||||
val defaultLists = persistentListOf(kind3Follow, globalFollow, muteListFollow)
|
||||
|
||||
fun getPeopleLists(): List<CodeName> =
|
||||
account
|
||||
.getAllPeopleLists()
|
||||
.map {
|
||||
CodeName(
|
||||
it.idHex,
|
||||
PeopleListName(it),
|
||||
CodeNameType.PEOPLE_LIST,
|
||||
)
|
||||
}.sortedBy { it.name.name() }
|
||||
|
||||
val livePeopleListsFlow = MutableStateFlow(emptyList<CodeName>())
|
||||
|
||||
fun updateFeedWith(newNotes: Set<Note>) {
|
||||
checkNotInMainThread()
|
||||
|
||||
val hasNewList =
|
||||
newNotes.any {
|
||||
val noteEvent = it.event
|
||||
|
||||
noteEvent?.pubKey() == account.userProfile().pubkeyHex &&
|
||||
(
|
||||
(
|
||||
noteEvent is PeopleListEvent ||
|
||||
noteEvent is MuteListEvent ||
|
||||
noteEvent is ContactListEvent
|
||||
) ||
|
||||
(
|
||||
noteEvent is DeletionEvent &&
|
||||
(
|
||||
noteEvent.deleteEvents().any {
|
||||
LocalCache.getNoteIfExists(it)?.event is PeopleListEvent
|
||||
} ||
|
||||
noteEvent.deleteAddresses().any {
|
||||
it.kind == PeopleListEvent.KIND
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (hasNewList) {
|
||||
livePeopleListsFlow.tryEmit(getPeopleLists())
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val liveKind3FollowsFlow: Flow<List<CodeName>> =
|
||||
account.liveKind3Follows.transformLatest {
|
||||
checkNotInMainThread()
|
||||
|
||||
val communities =
|
||||
it.communities.mapNotNull {
|
||||
LocalCache.checkGetOrCreateAddressableNote(it)?.let { communityNote ->
|
||||
CodeName(
|
||||
"Community/${communityNote.idHex}",
|
||||
CommunityName(communityNote),
|
||||
CodeNameType.ROUTE,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val hashtags =
|
||||
it.hashtags.map {
|
||||
CodeName("Hashtag/$it", HashtagName(it), CodeNameType.ROUTE)
|
||||
}
|
||||
|
||||
val geotags =
|
||||
it.geotags.map {
|
||||
CodeName("Geohash/$it", GeoHashName(it), CodeNameType.ROUTE)
|
||||
}
|
||||
|
||||
emit(
|
||||
(communities + hashtags + geotags).sortedBy { it.name.name() },
|
||||
)
|
||||
}
|
||||
|
||||
private val _kind3GlobalPeopleRoutes =
|
||||
combineTransform(
|
||||
livePeopleListsFlow,
|
||||
liveKind3FollowsFlow,
|
||||
) { myLivePeopleListsFlow, myLiveKind3FollowsFlow ->
|
||||
checkNotInMainThread()
|
||||
emit(
|
||||
listOf(
|
||||
listOf(kind3Follow, globalFollow),
|
||||
myLivePeopleListsFlow,
|
||||
myLiveKind3FollowsFlow,
|
||||
listOf(muteListFollow),
|
||||
).flatten().toImmutableList(),
|
||||
)
|
||||
}
|
||||
|
||||
private val _kind3GlobalPeople =
|
||||
combineTransform(
|
||||
livePeopleListsFlow,
|
||||
liveKind3FollowsFlow,
|
||||
) { myLivePeopleListsFlow, myLiveKind3FollowsFlow ->
|
||||
checkNotInMainThread()
|
||||
emit(
|
||||
listOf(
|
||||
listOf(kind3Follow, globalFollow),
|
||||
myLivePeopleListsFlow,
|
||||
listOf(muteListFollow),
|
||||
).flatten().toImmutableList(),
|
||||
)
|
||||
}
|
||||
|
||||
val kind3GlobalPeopleRoutes = _kind3GlobalPeopleRoutes.flowOn(Dispatchers.Default).stateIn(viewModelScope, SharingStarted.Eagerly, defaultLists)
|
||||
val kind3GlobalPeople = _kind3GlobalPeople.flowOn(Dispatchers.Default).stateIn(viewModelScope, SharingStarted.Eagerly, defaultLists)
|
||||
|
||||
suspend fun initializeSuspend() {
|
||||
checkNotInMainThread()
|
||||
|
||||
livePeopleListsFlow.emit(getPeopleLists())
|
||||
}
|
||||
}
|
||||
|
||||
enum class CodeNameType {
|
||||
HARDCODED,
|
||||
PEOPLE_LIST,
|
||||
ROUTE,
|
||||
}
|
||||
|
||||
abstract class Name {
|
||||
abstract fun name(): String
|
||||
|
||||
open fun name(context: Context) = name()
|
||||
}
|
||||
|
||||
class GeoHashName(
|
||||
val geoHashTag: String,
|
||||
) : Name() {
|
||||
override fun name() = "/g/$geoHashTag"
|
||||
}
|
||||
|
||||
class HashtagName(
|
||||
val hashTag: String,
|
||||
) : Name() {
|
||||
override fun name() = "#$hashTag"
|
||||
}
|
||||
|
||||
class ResourceName(
|
||||
val resourceId: Int,
|
||||
) : Name() {
|
||||
override fun name() = " $resourceId " // Space to make sure it goes first
|
||||
|
||||
override fun name(context: Context) = stringRes(context, resourceId)
|
||||
}
|
||||
|
||||
class PeopleListName(
|
||||
val note: AddressableNote,
|
||||
) : Name() {
|
||||
override fun name() = (note.event as? PeopleListEvent)?.nameOrTitle() ?: note.dTag() ?: ""
|
||||
}
|
||||
|
||||
class CommunityName(
|
||||
val note: AddressableNote,
|
||||
) : Name() {
|
||||
override fun name() = "/n/${(note.dTag() ?: "")}"
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class CodeName(
|
||||
val code: String,
|
||||
val name: Name,
|
||||
val type: CodeNameType,
|
||||
)
|
@ -34,6 +34,7 @@ import com.vitorpamplona.amethyst.ui.dal.HomeNewThreadFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.dal.VideoFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.feeds.FeedContentState
|
||||
import com.vitorpamplona.amethyst.ui.screen.FollowListState
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.CardFeedContentState
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.NotificationSummaryState
|
||||
|
||||
@ -57,8 +58,11 @@ class AccountFeedContentStates(
|
||||
val notifications = CardFeedContentState(NotificationFeedFilter(accountViewModel.account), accountViewModel.viewModelScope)
|
||||
val notificationSummary = NotificationSummaryState(accountViewModel.account)
|
||||
|
||||
val feedListOptions = FollowListState(accountViewModel.account, accountViewModel.viewModelScope)
|
||||
|
||||
suspend fun init() {
|
||||
notificationSummary.initializeSuspend()
|
||||
feedListOptions.initializeSuspend()
|
||||
}
|
||||
|
||||
fun updateFeedsWith(newNotes: Set<Note>) {
|
||||
@ -78,6 +82,8 @@ class AccountFeedContentStates(
|
||||
|
||||
notifications.updateFeedWith(newNotes)
|
||||
notificationSummary.invalidateInsertData(newNotes)
|
||||
|
||||
feedListOptions.updateFeedWith(newNotes)
|
||||
}
|
||||
|
||||
fun destroy() {
|
||||
@ -97,5 +103,7 @@ class AccountFeedContentStates(
|
||||
|
||||
notifications.destroy()
|
||||
notificationSummary.destroy()
|
||||
|
||||
// feedListOptions.destroy()
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,6 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
@ -91,7 +90,6 @@ import com.vitorpamplona.amethyst.ui.navigation.AppBottomBar
|
||||
import com.vitorpamplona.amethyst.ui.navigation.AppNavigation
|
||||
import com.vitorpamplona.amethyst.ui.navigation.AppTopBar
|
||||
import com.vitorpamplona.amethyst.ui.navigation.DrawerContent
|
||||
import com.vitorpamplona.amethyst.ui.navigation.FollowListViewModel
|
||||
import com.vitorpamplona.amethyst.ui.navigation.Route
|
||||
import com.vitorpamplona.amethyst.ui.navigation.Route.Companion.InvertedLayouts
|
||||
import com.vitorpamplona.amethyst.ui.navigation.getRouteWithArguments
|
||||
@ -166,12 +164,6 @@ fun MainScreen(
|
||||
DisplayErrorMessages(accountViewModel)
|
||||
DisplayNotifyMessages(accountViewModel, nav)
|
||||
|
||||
val followListsViewModel: FollowListViewModel =
|
||||
viewModel(
|
||||
key = "FollowListViewModel",
|
||||
factory = FollowListViewModel.Factory(accountViewModel.account),
|
||||
)
|
||||
|
||||
val navBottomRow =
|
||||
remember(navController, accountViewModel) {
|
||||
{ route: Route, selected: Boolean ->
|
||||
@ -228,7 +220,6 @@ fun MainScreen(
|
||||
navPopBack = navPopBack,
|
||||
openDrawer = { scope.launch { drawerState.open() } },
|
||||
accountStateViewModel = accountStateViewModel,
|
||||
followListsViewModel = followListsViewModel,
|
||||
sharedPreferencesViewModel = sharedPreferencesViewModel,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
@ -265,7 +256,6 @@ private fun MainScaffold(
|
||||
navPopBack: () -> Unit,
|
||||
openDrawer: () -> Unit,
|
||||
accountStateViewModel: AccountStateViewModel,
|
||||
followListsViewModel: FollowListViewModel,
|
||||
sharedPreferencesViewModel: SharedPreferencesViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
@ -350,7 +340,6 @@ private fun MainScaffold(
|
||||
) { isVisible ->
|
||||
if (isVisible) {
|
||||
AppTopBar(
|
||||
followListsViewModel,
|
||||
navState,
|
||||
openDrawer,
|
||||
accountViewModel,
|
||||
|
Loading…
x
Reference in New Issue
Block a user