Show the feed for who we are following
This commit is contained in:
@@ -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<HexKey>,
|
||||
): Flow<List<LocalNostrEvent>>
|
||||
|
||||
@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<Kind>,
|
||||
authors: Array<HexKey>,
|
||||
): Flow<List<LocalNostrEvent>>
|
||||
|
||||
@Query("SELECT * FROM NostrEvent WHERE tags LIKE '%' || :publicKey || '%' AND kind in (:kinds) ORDER BY createdAt DESC")
|
||||
fun observePublicKeyMentionedNostrEvents(
|
||||
kinds: Array<Kind>,
|
||||
publicKey: HexKey
|
||||
): Flow<List<LocalNostrEvent>>
|
||||
|
||||
@RawQuery
|
||||
fun observePublicKeyFollowingNostrEvents(
|
||||
query: RoomRawQuery
|
||||
): Flow<List<LocalNostrEvent>>
|
||||
|
||||
@Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) ORDER BY savedAt DESC")
|
||||
fun getAllNostrEvents(kinds: Array<Kind>): List<NostrEvent>
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<FeedRoute> {
|
||||
FeedScreen(
|
||||
initialFeedListUIState = FeedListUIState.Loading,
|
||||
onNavigateToEvent = { nostrEventId ->
|
||||
HomeScreen(
|
||||
onNavigateToEvent = { eventRoute ->
|
||||
navController.navigate(
|
||||
route = NostrEventDetailRoute(
|
||||
nostrEventId = nostrEventId
|
||||
)
|
||||
route = eventRoute
|
||||
)
|
||||
},
|
||||
onNavigateToWriteNewNote = {
|
||||
|
||||
@@ -208,5 +208,6 @@ class FeedListViewModel(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user