From b33af464c3a7300b63c916d29bcb921349762b77 Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Fri, 24 Apr 2026 02:42:50 +0200 Subject: [PATCH] Show the feed for who we are following --- .../compose/database/dao/NostrEventDao.kt | 15 +- .../repository/DatabaseNostrRepository.kt | 10 + .../compose/ui/composable/FeedScreen.kt | 266 ------------------ .../compose/ui/composable/HomeScreen.kt | 216 ++++++++++++++ .../ui/composable/navigation/AuxNavHost.kt | 11 +- .../ui/view/model/FeedListViewModel.kt | 1 + .../compose/ui/view/model/HomeViewModel.kt | 137 +++++++++ .../ui/view/state/HomeScreenUIState.kt | 15 + 8 files changed, 397 insertions(+), 274 deletions(-) delete mode 100644 composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/FeedScreen.kt create mode 100644 composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/HomeScreen.kt create mode 100755 composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/model/HomeViewModel.kt create mode 100755 composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/state/HomeScreenUIState.kt diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/NostrEventDao.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/NostrEventDao.kt index 829f7cc9..e93542ac 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/NostrEventDao.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/NostrEventDao.kt @@ -6,6 +6,8 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy.Companion.IGNORE import androidx.room.Query +import androidx.room.RawQuery +import androidx.room.RoomRawQuery import androidx.room.Upsert import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.Kind @@ -29,12 +31,23 @@ interface NostrEventDao { ids: Array, ): Flow> - @Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) AND pubKey in (:authors) ORDER BY createdAt DESC") + @Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) AND pubKey in (:authors) ORDER BY createdAt DESC LIMIT 50") fun observeAuthoredNostrEvents( kinds: Array, authors: Array, ): Flow> + @Query("SELECT * FROM NostrEvent WHERE tags LIKE '%' || :publicKey || '%' AND kind in (:kinds) ORDER BY createdAt DESC") + fun observePublicKeyMentionedNostrEvents( + kinds: Array, + publicKey: HexKey + ): Flow> + + @RawQuery + fun observePublicKeyFollowingNostrEvents( + query: RoomRawQuery + ): Flow> + @Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) ORDER BY savedAt DESC") fun getAllNostrEvents(kinds: Array): List diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/repository/DatabaseNostrRepository.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/repository/DatabaseNostrRepository.kt index 4dec5115..32f8d464 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/repository/DatabaseNostrRepository.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/repository/DatabaseNostrRepository.kt @@ -22,6 +22,7 @@ import ac.cord.auxiliary.compose.database.model.types.SynchronizationFilter import ac.cord.auxiliary.compose.nostr.Relays import ac.cord.auxiliary.compose.repository.NostrRepository import ac.cord.auxiliary.compose.repository.RelayRepository +import androidx.room.RoomRawQuery import co.touchlab.kermit.Logger import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.Kind @@ -376,6 +377,15 @@ class DatabaseNostrRepository( authors = synchronizationFilter.authors ) } + synchronizationFilter.kinds != null && synchronizationFilter.tags?.contains("p") == true && synchronizationFilter.tags["p"]?.first()?.isNotEmpty() == true -> { + logger.d("observePublicKeyMentionedNostrEvents: $synchronizationFilter") + database.nostrEventDao().observePublicKeyMentionedNostrEvents( + kinds = arrayOf( + TextNoteEvent.KIND, + ), + publicKey = synchronizationFilter.tags["p"]?.first()!! + ) + } else -> { logger.d("observeNostrEvents: $synchronizationFilter") database.nostrEventDao().observeNostrEvents( diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/FeedScreen.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/FeedScreen.kt deleted file mode 100644 index 076c4c3a..00000000 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/FeedScreen.kt +++ /dev/null @@ -1,266 +0,0 @@ -package ac.cord.auxiliary.compose.ui.composable - -import ac.cord.auxiliary.compose.database.model.types.SynchronizationFilter -import ac.cord.auxiliary.compose.managers.SeedManager -import ac.cord.auxiliary.compose.repository.NostrRepository -import ac.cord.auxiliary.compose.ui.composable.widgets.LoadingDataIndicator -import ac.cord.auxiliary.compose.ui.composable.widgets.feed.FeedListItem -import ac.cord.auxiliary.compose.ui.theme.AuxTheme -import ac.cord.auxiliary.compose.ui.view.model.FeedListViewModel -import ac.cord.auxiliary.compose.ui.view.state.FeedListUIState -import androidx.compose.foundation.horizontalScroll -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.rememberScrollState -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowDropDown -import androidx.compose.material.icons.filled.Create -import androidx.compose.material.icons.filled.Person -import androidx.compose.material.icons.filled.Search -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi -import androidx.compose.material3.ExtendedFloatingActionButton -import androidx.compose.material3.FilterChip -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.LoadingIndicator -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults -import androidx.compose.runtime.Composable -import androidx.compose.runtime.key -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.lifecycle.viewmodel.compose.viewModel -import com.vitorpamplona.quartz.nip01Core.core.HexKey -import com.vitorpamplona.quartz.nip01Core.core.toHexKey -import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent -import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent -import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent -import com.vitorpamplona.quartz.nip18Reposts.RepostEvent -import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent -import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent -import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent -import com.vitorpamplona.quartz.nip96FileStorage.config.FileServersEvent -import com.vitorpamplona.quartz.nipB7Blossom.BlossomServersEvent - -@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) -@Composable -fun FeedScreen( - initialFeedListUIState: FeedListUIState, - onNavigateToEvent: (HexKey) -> Unit, - onNavigateToWriteNewNote: () -> Unit, - onNavigateToSearch: () -> Unit, - nostrRepository: NostrRepository, -) { - val feedListViewModel: FeedListViewModel = viewModel( - factory = FeedListViewModel.factory( - initialFeedListUIState = initialFeedListUIState, - synchronizationFilter = SynchronizationFilter( - kinds = arrayOf( - TextNoteEvent.KIND, - RepostEvent.KIND, - ReactionEvent.KIND, - LnZapEvent.KIND, - ContactListEvent.KIND, - AdvertisedRelayListEvent.KIND, - PrivateDmEvent.KIND, - BlossomServersEvent.KIND, - FileServersEvent.KIND - ), - tags = mapOf( - Pair("p", listOf(SeedManager.activePublicKey().toHexKey())) - ), - limit = 50 - ), - nostrRepository = nostrRepository - ), - ) - - Scaffold( - modifier = Modifier, - topBar = { - TopAppBar( - colors = TopAppBarDefaults.topAppBarColors( - containerColor = MaterialTheme.colorScheme.primaryContainer, - titleContentColor = MaterialTheme.colorScheme.primary - ), - title = { - Text( - "Aux" - ) - }, - navigationIcon = { - IconButton( - onClick = { - - } - ) { - Icon( - Icons.Default.Person, - contentDescription = "Account" - ) - } - }, - actions = { - IconButton( - onClick = { - onNavigateToSearch.invoke() - } - ) { - Icon( - Icons.Default.Search, - contentDescription = "Search" - ) - } - } - ) - - }, - floatingActionButton = { - ExtendedFloatingActionButton( - onClick = onNavigateToWriteNewNote, - icon = { - Icon( - Icons.Default.Create, - contentDescription = "Write" - ) - }, - text = { - Text("Write") - } - ) - } - ) { innerPadding -> - Column( - modifier = Modifier.padding(innerPadding).fillMaxSize() - ) { - when(val feedListUIState = feedListViewModel.feedListUIState) { - is FeedListUIState.Loading -> { - LoadingDataIndicator() - } - FeedListUIState.Error -> { - Text("Something went wrong") - } - is FeedListUIState.Loaded -> { - LazyColumn( - modifier = Modifier.fillMaxWidth().fillMaxSize(), - verticalArrangement = Arrangement.spacedBy(5.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - stickyHeader { - Surface() { - Row( - modifier = Modifier.fillMaxWidth().padding(10.dp) - .horizontalScroll( - rememberScrollState() - ), - - horizontalArrangement = Arrangement.spacedBy(10.dp) - ){ - - FilterChip( - selected = true, - onClick = {}, - label = { - Text("All") - } - ) - - FilterChip( - selected = false, - onClick = {}, - label = { - Text("Kind") - Icon( - Icons.Default.ArrowDropDown, - contentDescription = "Author Filter" - ) - } - ) - - FilterChip( - selected = false, - onClick = {}, - label = { - Text("Author") - Icon( - Icons.Default.ArrowDropDown, - contentDescription = "Author Filter" - ) - } - ) - - FilterChip( - selected = false, - onClick = {}, - label = { - Text("Unread") - Icon( - Icons.Default.ArrowDropDown, - contentDescription = "Author Filter" - ) - } - ) - } - } - - } - if (feedListViewModel.isSynchronizationPending.value) { - item { - Spacer( - modifier = Modifier.height(30.dp) - ) - LoadingIndicator() - } - } - items( - items = feedListUIState.localNostrEvents, - key = { localNostrEvent -> localNostrEvent.nostrEvent.id } - ) { localNostrEvent -> - FeedListItem( - localNostrEvent = localNostrEvent, - onNavigateToEvent = onNavigateToEvent - ) - } - } - } - } - } - - key(true) { - feedListViewModel.initiate() - } - } -} - -@Preview -@Composable -private fun FeedScreenPreview() { - AuxTheme { - Surface( - modifier = Modifier.padding(20.dp) - ) { - FeedScreen( - initialFeedListUIState = FeedListUIState.Loaded(emptyList()), - onNavigateToEvent = {}, - onNavigateToWriteNewNote = {}, - onNavigateToSearch = {}, - nostrRepository = NostrRepository.NO_OP_NOSTR_REPOSITORY - ) - } - } -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/HomeScreen.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/HomeScreen.kt new file mode 100644 index 00000000..3588b299 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/HomeScreen.kt @@ -0,0 +1,216 @@ +package ac.cord.auxiliary.compose.ui.composable + +import ac.cord.auxiliary.compose.repository.NostrRepository +import ac.cord.auxiliary.compose.ui.composable.navigation.routes.Route +import ac.cord.auxiliary.compose.ui.composable.widgets.LoadingDataIndicator +import ac.cord.auxiliary.compose.ui.theme.AuxTheme +import ac.cord.auxiliary.compose.ui.view.model.FeedListViewModel +import ac.cord.auxiliary.compose.ui.view.model.HomeScreenType +import ac.cord.auxiliary.compose.ui.view.model.HomeViewModel +import ac.cord.auxiliary.compose.ui.view.state.FeedListUIState +import ac.cord.auxiliary.compose.ui.view.state.HomeScreenUIState +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Create +import androidx.compose.material.icons.filled.Person +import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.ExtendedFloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.PrimaryScrollableTabRow +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface +import androidx.compose.material3.Tab +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.key +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) +@Composable +fun HomeScreen( + onNavigateToEvent: (Route) -> Unit, + onNavigateToWriteNewNote: () -> Unit, + onNavigateToSearch: () -> Unit, + nostrRepository: NostrRepository, +) { + val scope = rememberCoroutineScope() + + val homeScreenViewModel: HomeViewModel = viewModel( + factory = HomeViewModel.factory( + nostrRepository = nostrRepository, + pagerState = rememberPagerState { HomeScreenType.entries.size } + ), + ) + + Scaffold( + modifier = Modifier, + topBar = { + TopAppBar( + colors = TopAppBarDefaults.topAppBarColors( + containerColor = MaterialTheme.colorScheme.primaryContainer, + titleContentColor = MaterialTheme.colorScheme.primary + ), + title = { + Text( + "Aux" + ) + }, + navigationIcon = { + IconButton( + onClick = { + + } + ) { + Icon( + Icons.Default.Person, + contentDescription = "Account" + ) + } + }, + actions = { + IconButton( + onClick = { + onNavigateToSearch.invoke() + } + ) { + Icon( + Icons.Default.Search, + contentDescription = "Search" + ) + } + } + ) + + }, + floatingActionButton = { + ExtendedFloatingActionButton( + onClick = onNavigateToWriteNewNote, + icon = { + Icon( + Icons.Default.Create, + contentDescription = "Write" + ) + }, + text = { + Text("Write") + } + ) + } + ) { innerPadding -> + Column( + modifier = Modifier.padding(innerPadding).fillMaxSize() + ) { + when(val homeScreenUIState = homeScreenViewModel.homeScreenUIState) { + HomeScreenUIState.Error -> { + Text("Something went wrong") + } + is HomeScreenUIState.Loaded -> { + PrimaryScrollableTabRow( + modifier = Modifier.padding(10.dp), + edgePadding = 10.dp, + selectedTabIndex = homeScreenViewModel.pagerState.currentPage, + ) { + HomeScreenType.entries.forEachIndexed { index, destination -> + Tab( + selected = homeScreenViewModel.pagerState.currentPage == index, + onClick = { + scope.launch { + homeScreenViewModel.pagerState.animateScrollToPage(destination.ordinal) + } + }, + text = { + Text( + text = destination.name, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + ) + } + } + + Column( + modifier = Modifier.weight(1f) + ) { + HorizontalPager( + state = homeScreenViewModel.pagerState, + ) { pageIndex -> + when (val homeScreenTypeType = HomeScreenType.entries[pageIndex]) { + HomeScreenType.Following, HomeScreenType.Mentions -> { + val feedListViewModel: FeedListViewModel = viewModel( + key = homeScreenTypeType.name, + factory = FeedListViewModel.factory( + initialFeedListUIState = FeedListUIState.Loading, + synchronizationFilter = homeScreenViewModel.getSynchronizationFilter( + homeScreenTypeType, + profileWithFollowing = homeScreenUIState.profileWithFollowing + ), + nostrRepository = nostrRepository + ), + ) + + key(feedListViewModel.feedListUIState) { + feedListViewModel.RenderFeed( + onNavigateToEvent = onNavigateToEvent + ) + } + + key(true) { + feedListViewModel.initiate() + } + } + + HomeScreenType.Messages -> { + Text("Messaging coming soon.") + } + } + } + } + } + HomeScreenUIState.Loading -> { + LoadingDataIndicator() + } + } + + + + } + + key(true) { + homeScreenViewModel.initiate() + } + } +} + +@Preview +@Composable +private fun HomeScreenPreview() { + AuxTheme { + Surface( + modifier = Modifier.padding(20.dp) + ) { + HomeScreen( + onNavigateToEvent = {}, + onNavigateToWriteNewNote = {}, + onNavigateToSearch = {}, + nostrRepository = NostrRepository.NO_OP_NOSTR_REPOSITORY + ) + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/navigation/AuxNavHost.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/navigation/AuxNavHost.kt index 2eb2107e..f8124218 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/navigation/AuxNavHost.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/navigation/AuxNavHost.kt @@ -5,7 +5,7 @@ import ac.cord.auxiliary.compose.database.repository.DatabaseNostrRepository import ac.cord.auxiliary.compose.database.repository.DatabaseSearchRepository import ac.cord.auxiliary.compose.managers.DatabaseManager import ac.cord.auxiliary.compose.ui.composable.CreateProfileScreen -import ac.cord.auxiliary.compose.ui.composable.FeedScreen +import ac.cord.auxiliary.compose.ui.composable.HomeScreen import ac.cord.auxiliary.compose.ui.composable.ImplementationPendingScreen import ac.cord.auxiliary.compose.ui.composable.LandingScreen import ac.cord.auxiliary.compose.ui.composable.LoadingScreen @@ -302,13 +302,10 @@ fun AuxNavHost( ) } composable { - FeedScreen( - initialFeedListUIState = FeedListUIState.Loading, - onNavigateToEvent = { nostrEventId -> + HomeScreen( + onNavigateToEvent = { eventRoute -> navController.navigate( - route = NostrEventDetailRoute( - nostrEventId = nostrEventId - ) + route = eventRoute ) }, onNavigateToWriteNewNote = { diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/model/FeedListViewModel.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/model/FeedListViewModel.kt index 72b75e97..cdd06201 100755 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/model/FeedListViewModel.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/model/FeedListViewModel.kt @@ -208,5 +208,6 @@ class FeedListViewModel( ) } } + } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/model/HomeViewModel.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/model/HomeViewModel.kt new file mode 100755 index 00000000..7a809f9d --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/model/HomeViewModel.kt @@ -0,0 +1,137 @@ +package ac.cord.auxiliary.compose.ui.view.model + +import ac.cord.auxiliary.compose.database.model.intermdiate.LocalProfileWithFollowing +import ac.cord.auxiliary.compose.database.model.types.SynchronizationFilter +import ac.cord.auxiliary.compose.managers.SeedManager +import ac.cord.auxiliary.compose.repository.NostrRepository +import ac.cord.auxiliary.compose.ui.view.state.HomeScreenUIState +import androidx.compose.foundation.pager.PagerState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import co.touchlab.kermit.Logger +import com.vitorpamplona.quartz.nip01Core.core.toHexKey +import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent +import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent +import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent +import com.vitorpamplona.quartz.nip18Reposts.RepostEvent +import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent +import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent +import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent +import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent +import com.vitorpamplona.quartz.nip96FileStorage.config.FileServersEvent +import com.vitorpamplona.quartz.nipB7Blossom.BlossomServersEvent +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO +import kotlinx.coroutines.launch + +enum class HomeScreenType { + Following, Mentions, Messages +} + +class HomeViewModel( + val initialHomeScreenUIState: HomeScreenUIState = HomeScreenUIState.Loading, + val nostrRepository: NostrRepository, + val pagerState: PagerState +): ViewModel() { + + val logger = Logger.withTag(TAG) + + + var homeScreenUIState: HomeScreenUIState by mutableStateOf(initialHomeScreenUIState) + private set + + fun initiate() { + observeActiveUserProfileWithFollowing() + } + + fun observeActiveUserProfileWithFollowing() { + logger.d("observeActiveUserProfileWithFollowing") + viewModelScope.launch(Dispatchers.IO) { + val activePublicKey = SeedManager.activePublicKey().toHexKey() + + nostrRepository.observeProfileWithFollowing( + activePublicKey + ).collect { profileWithFollowing -> + homeScreenUIState = if (profileWithFollowing != null) { + HomeScreenUIState.Loaded( + profileWithFollowing = profileWithFollowing + ) + } else { + HomeScreenUIState.Error + } + } + } + } + + fun getSynchronizationFilter( + homeScreenType: HomeScreenType, + profileWithFollowing: LocalProfileWithFollowing, + ): SynchronizationFilter { + val publicKey = SeedManager.activePublicKey().toHexKey() + + return when (homeScreenType) { + HomeScreenType.Following -> { + SynchronizationFilter( + authors = profileWithFollowing.following.map { it.publicKey }.toTypedArray(), + kinds = arrayOf( + TextNoteEvent.KIND, + RepostEvent.KIND, + ), + limit = 50 + ) + } + HomeScreenType.Mentions -> { + SynchronizationFilter( + kinds = arrayOf( + TextNoteEvent.KIND, + RepostEvent.KIND, + ReactionEvent.KIND, + LnZapEvent.KIND, + ContactListEvent.KIND, + AdvertisedRelayListEvent.KIND, + PrivateDmEvent.KIND, + BlossomServersEvent.KIND, + FileServersEvent.KIND + ), + tags = mapOf( + Pair("p", listOf(publicKey)) + ), + limit = 50 + ) + } + HomeScreenType.Messages -> { + SynchronizationFilter( + authors = arrayOf( + publicKey + ), + kinds = arrayOf( + LongTextNoteEvent.KIND, + ), + limit = 50 + ) + } + } + } + + companion object { + const val TAG = "HomeViewModel" + + fun factory( + nostrRepository: NostrRepository, + pagerState: PagerState + ): ViewModelProvider.Factory = viewModelFactory { + initializer { + HomeViewModel( + nostrRepository = nostrRepository, + pagerState = pagerState, + ) + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/state/HomeScreenUIState.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/state/HomeScreenUIState.kt new file mode 100755 index 00000000..f235d1c4 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/view/state/HomeScreenUIState.kt @@ -0,0 +1,15 @@ +package ac.cord.auxiliary.compose.ui.view.state + +import ac.cord.auxiliary.compose.database.model.Connection +import ac.cord.auxiliary.compose.database.model.intermdiate.LocalProfileWithFollowing + + +sealed interface HomeScreenUIState { + data class Loaded( + val profileWithFollowing: LocalProfileWithFollowing + ): HomeScreenUIState + + data object Error: HomeScreenUIState + data object Loading: HomeScreenUIState +} +