Improve feed logic on profile detail
This commit is contained in:
@@ -22,6 +22,9 @@ interface ConnectionDao {
|
|||||||
@Query("SELECT * FROM Connection WHERE destinationPublicKey = :destinationPublicKey")
|
@Query("SELECT * FROM Connection WHERE destinationPublicKey = :destinationPublicKey")
|
||||||
fun getPublicKeyFollowers(destinationPublicKey: String): LocalFollowers?
|
fun getPublicKeyFollowers(destinationPublicKey: String): LocalFollowers?
|
||||||
|
|
||||||
|
@Query("SELECT * FROM Connection WHERE destinationPublicKey = :destinationPublicKey")
|
||||||
|
fun observe(destinationPublicKey: String): LocalFollowers?
|
||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
suspend fun insertPlaceholderProfile(connection: Connection)
|
suspend fun insertPlaceholderProfile(connection: Connection)
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package ac.cord.auxiliary.compose.database.dao
|
|||||||
|
|
||||||
import ac.cord.auxiliary.compose.database.model.Profile
|
import ac.cord.auxiliary.compose.database.model.Profile
|
||||||
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalProfile
|
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalProfile
|
||||||
|
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalProfileWithFollowers
|
||||||
|
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalProfileWithFollowing
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
@@ -18,6 +20,12 @@ interface ProfileDao {
|
|||||||
@Query("SELECT * FROM Profile WHERE publicKey = :publicKey")
|
@Query("SELECT * FROM Profile WHERE publicKey = :publicKey")
|
||||||
fun getProfileByPublicKey(publicKey: String): Profile?
|
fun getProfileByPublicKey(publicKey: String): Profile?
|
||||||
|
|
||||||
|
@Query("SELECT * FROM NostrEvent WHERE kind = 0 AND pubKey = :publicKey")
|
||||||
|
fun observeProfileWithFollowersByPublicKey(publicKey: String): LocalProfileWithFollowers?
|
||||||
|
|
||||||
|
@Query("SELECT * FROM NostrEvent WHERE kind = 0 AND pubKey = :publicKey")
|
||||||
|
fun observeProfileWithFollowingByPublicKey(publicKey: String): LocalProfileWithFollowing?
|
||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
suspend fun insertPlaceholderProfile(profile: Profile)
|
suspend fun insertPlaceholderProfile(profile: Profile)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package ac.cord.auxiliary.compose.database.model.intermdiate
|
||||||
|
|
||||||
|
import ac.cord.auxiliary.compose.database.model.Connection
|
||||||
|
import ac.cord.auxiliary.compose.database.model.NostrEvent
|
||||||
|
import ac.cord.auxiliary.compose.database.model.Profile
|
||||||
|
import androidx.room.Embedded
|
||||||
|
import androidx.room.Junction
|
||||||
|
import androidx.room.Relation
|
||||||
|
|
||||||
|
data class LocalProfileWithFollowers(
|
||||||
|
@Embedded val nostrEvent: NostrEvent,
|
||||||
|
|
||||||
|
@Relation(
|
||||||
|
parentColumn = "pubKey",
|
||||||
|
entityColumn = "publicKey"
|
||||||
|
)
|
||||||
|
val profile: Profile,
|
||||||
|
|
||||||
|
@Relation(
|
||||||
|
parentColumn = "pubKey",
|
||||||
|
entityColumn = "publicKey",
|
||||||
|
associateBy = Junction(
|
||||||
|
value = Connection::class,
|
||||||
|
parentColumn = "destinationPublicKey",
|
||||||
|
entityColumn = "sourcePublicKey"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val followers: List<Profile>
|
||||||
|
)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package ac.cord.auxiliary.compose.database.model.intermdiate
|
||||||
|
|
||||||
|
import ac.cord.auxiliary.compose.database.model.Connection
|
||||||
|
import ac.cord.auxiliary.compose.database.model.NostrEvent
|
||||||
|
import ac.cord.auxiliary.compose.database.model.Profile
|
||||||
|
import androidx.room.Embedded
|
||||||
|
import androidx.room.Junction
|
||||||
|
import androidx.room.Relation
|
||||||
|
|
||||||
|
data class LocalProfileWithFollowing(
|
||||||
|
@Embedded val nostrEvent: NostrEvent,
|
||||||
|
|
||||||
|
@Relation(
|
||||||
|
parentColumn = "pubKey",
|
||||||
|
entityColumn = "publicKey"
|
||||||
|
)
|
||||||
|
val profile: Profile,
|
||||||
|
|
||||||
|
@Relation(
|
||||||
|
parentColumn = "pubKey",
|
||||||
|
entityColumn = "publicKey",
|
||||||
|
associateBy = Junction(
|
||||||
|
value = Connection::class,
|
||||||
|
parentColumn = "sourcePublicKey",
|
||||||
|
entityColumn = "destinationPublicKey"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val followers: List<Profile>
|
||||||
|
)
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package ac.cord.auxiliary.compose.ui.composable
|
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.repository.NostrRepository
|
||||||
import ac.cord.auxiliary.compose.ui.composable.widgets.LoadingDataIndicator
|
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.composable.widgets.feed.FeedListItem
|
||||||
@@ -43,6 +45,16 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
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)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -56,6 +68,23 @@ fun FeedScreen(
|
|||||||
val feedListViewModel: FeedListViewModel = viewModel(
|
val feedListViewModel: FeedListViewModel = viewModel(
|
||||||
factory = FeedListViewModel.factory(
|
factory = FeedListViewModel.factory(
|
||||||
initialFeedListUIState = initialFeedListUIState,
|
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
|
nostrRepository = nostrRepository
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import ac.cord.auxiliary.compose.ui.composable.navigation.routes.NostrEventDetai
|
|||||||
import ac.cord.auxiliary.compose.ui.composable.navigation.routes.Route
|
import ac.cord.auxiliary.compose.ui.composable.navigation.routes.Route
|
||||||
import ac.cord.auxiliary.compose.ui.composable.widgets.feed.ListView
|
import ac.cord.auxiliary.compose.ui.composable.widgets.feed.ListView
|
||||||
import ac.cord.auxiliary.compose.ui.theme.AuxTheme
|
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.MetadataEventDetailType
|
import ac.cord.auxiliary.compose.ui.view.model.MetadataEventDetailType
|
||||||
import ac.cord.auxiliary.compose.ui.view.model.MetadataEventDetailViewModel
|
import ac.cord.auxiliary.compose.ui.view.model.MetadataEventDetailViewModel
|
||||||
import ac.cord.auxiliary.compose.ui.view.model.SearchResultType
|
import ac.cord.auxiliary.compose.ui.view.model.SearchResultType
|
||||||
@@ -216,81 +217,40 @@ fun MetadataEventDetail(
|
|||||||
HorizontalPager(
|
HorizontalPager(
|
||||||
state = metadataEventDetailViewModel.pagerState,
|
state = metadataEventDetailViewModel.pagerState,
|
||||||
) { pageIndex ->
|
) { pageIndex ->
|
||||||
|
when (MetadataEventDetailType.entries[pageIndex]) {
|
||||||
|
MetadataEventDetailType.Posts, MetadataEventDetailType.Replies,
|
||||||
|
MetadataEventDetailType.Articles, MetadataEventDetailType.Zaps,
|
||||||
|
MetadataEventDetailType.Photos, MetadataEventDetailType.Videos,
|
||||||
|
MetadataEventDetailType.Bookmarks, MetadataEventDetailType.Reports, MetadataEventDetailType.Relays -> {
|
||||||
|
val feedListViewModel: FeedListViewModel = viewModel(
|
||||||
|
factory = FeedListViewModel.factory(
|
||||||
|
initialFeedListUIState = FeedListUIState.Loading,
|
||||||
|
synchronizationFilter = metadataEventDetailViewModel.getSynchronizationFilter(),
|
||||||
|
nostrRepository = nostrRepository
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
Column(
|
feedListViewModel.RenderFeed(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
onNavigateToEvent = onNavigateToEvent
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
)
|
||||||
) {
|
}
|
||||||
when (val feedListUIState = metadataEventDetailViewModel.feedListUIState) {
|
|
||||||
FeedListUIState.Error -> {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
|
||||||
) {
|
|
||||||
Spacer(
|
|
||||||
modifier = Modifier.height(50.dp)
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = "Something went wrong",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is FeedListUIState.Loaded -> {
|
|
||||||
if (feedListUIState.localNostrEvents.isEmpty()) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
|
||||||
) {
|
|
||||||
Spacer(
|
|
||||||
modifier = Modifier.height(50.dp)
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = "No events were found",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LazyColumn(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
|
||||||
) {
|
|
||||||
items(
|
|
||||||
items = feedListUIState.localNostrEvents,
|
|
||||||
key = { localNostrEvent -> localNostrEvent.nostrEvent.id}
|
|
||||||
) { localNostrEvent ->
|
|
||||||
localNostrEvent.ListView(
|
|
||||||
onNavigateToEvent = { nostrEventId ->
|
|
||||||
onNavigateToEvent.invoke(
|
|
||||||
NostrEventDetailRoute(
|
|
||||||
nostrEventId = nostrEventId
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
MetadataEventDetailType.Following -> {
|
||||||
FeedListUIState.Loading -> {
|
|
||||||
Column(
|
Text("Implementing logic to see following")
|
||||||
modifier = Modifier.fillMaxWidth(),
|
}
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
MetadataEventDetailType.Followers -> {
|
||||||
) {
|
|
||||||
Spacer(
|
Text("Implementing logic to see followers")
|
||||||
modifier = Modifier.height(50.dp)
|
}
|
||||||
)
|
}
|
||||||
Text(
|
|
||||||
text = "Loading ${MetadataEventDetailType.entries[pageIndex].name.lowercase()}",
|
|
||||||
textAlign = TextAlign.Center
|
LaunchedEffect(true) {
|
||||||
)
|
metadataEventDetailViewModel.updateMetadataEventDetailType(
|
||||||
Spacer(
|
MetadataEventDetailType.Posts
|
||||||
modifier = Modifier.height(50.dp)
|
) {
|
||||||
)
|
|
||||||
|
|
||||||
LoadingIndicator()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,29 @@ import ac.cord.auxiliary.compose.database.model.types.SynchronizationFilter
|
|||||||
import ac.cord.auxiliary.compose.managers.SeedManager
|
import ac.cord.auxiliary.compose.managers.SeedManager
|
||||||
import ac.cord.auxiliary.compose.nostr.Relays
|
import ac.cord.auxiliary.compose.nostr.Relays
|
||||||
import ac.cord.auxiliary.compose.repository.NostrRepository
|
import ac.cord.auxiliary.compose.repository.NostrRepository
|
||||||
|
import ac.cord.auxiliary.compose.ui.composable.navigation.routes.NostrEventDetailRoute
|
||||||
|
import ac.cord.auxiliary.compose.ui.composable.navigation.routes.Route
|
||||||
|
import ac.cord.auxiliary.compose.ui.composable.widgets.feed.ListView
|
||||||
import ac.cord.auxiliary.compose.ui.view.state.FeedListUIState
|
import ac.cord.auxiliary.compose.ui.view.state.FeedListUIState
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
|
import androidx.compose.material3.LoadingIndicator
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
@@ -33,6 +51,7 @@ import kotlinx.coroutines.launch
|
|||||||
|
|
||||||
class FeedListViewModel(
|
class FeedListViewModel(
|
||||||
initialFeedListUIState: FeedListUIState,
|
initialFeedListUIState: FeedListUIState,
|
||||||
|
val synchronizationFilter: SynchronizationFilter,
|
||||||
val nostrRepository: NostrRepository
|
val nostrRepository: NostrRepository
|
||||||
): ViewModel() {
|
): ViewModel() {
|
||||||
val logger = Logger.withTag(TAG)
|
val logger = Logger.withTag(TAG)
|
||||||
@@ -58,7 +77,9 @@ class FeedListViewModel(
|
|||||||
if (localProfile?.profile == null) {
|
if (localProfile?.profile == null) {
|
||||||
feedListUIState = FeedListUIState.Error
|
feedListUIState = FeedListUIState.Error
|
||||||
} else {
|
} else {
|
||||||
nostrRepository.observeNostrFeed().collect { localNostrEvents ->
|
nostrRepository.observeNostrFeed(
|
||||||
|
synchronizationFilter
|
||||||
|
).collect { localNostrEvents ->
|
||||||
feedListUIState = FeedListUIState.Loaded(
|
feedListUIState = FeedListUIState.Loaded(
|
||||||
localNostrEvents = localNostrEvents
|
localNostrEvents = localNostrEvents
|
||||||
)
|
)
|
||||||
@@ -77,23 +98,7 @@ class FeedListViewModel(
|
|||||||
SynchronizeNostrEventRequest(
|
SynchronizeNostrEventRequest(
|
||||||
purpose = "feed",
|
purpose = "feed",
|
||||||
synchronizationFilters = arrayOf(
|
synchronizationFilters = arrayOf(
|
||||||
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
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
relayURL = normalizedRelay.url
|
relayURL = normalizedRelay.url
|
||||||
)
|
)
|
||||||
@@ -112,16 +117,101 @@ class FeedListViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
|
@Composable
|
||||||
|
fun RenderFeed(
|
||||||
|
onNavigateToEvent: (Route) -> Unit
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
when (val feedListUIState = feedListUIState) {
|
||||||
|
FeedListUIState.Error -> {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier.height(50.dp)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "Something went wrong",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is FeedListUIState.Loaded -> {
|
||||||
|
if (feedListUIState.localNostrEvents.isEmpty()) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier.height(50.dp)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "No events were found",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||||
|
) {
|
||||||
|
items(
|
||||||
|
items = feedListUIState.localNostrEvents,
|
||||||
|
key = { localNostrEvent -> localNostrEvent.nostrEvent.id}
|
||||||
|
) { localNostrEvent ->
|
||||||
|
localNostrEvent.ListView(
|
||||||
|
onNavigateToEvent = { nostrEventId ->
|
||||||
|
onNavigateToEvent.invoke(
|
||||||
|
NostrEventDetailRoute(
|
||||||
|
nostrEventId = nostrEventId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
FeedListUIState.Loading -> {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier.height(50.dp)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "Loading",
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier.height(50.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
LoadingIndicator()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "FeedListViewModel"
|
const val TAG = "FeedListViewModel"
|
||||||
|
|
||||||
fun factory(
|
fun factory(
|
||||||
initialFeedListUIState: FeedListUIState = FeedListUIState.Loading,
|
initialFeedListUIState: FeedListUIState = FeedListUIState.Loading,
|
||||||
|
synchronizationFilter: SynchronizationFilter,
|
||||||
nostrRepository: NostrRepository,
|
nostrRepository: NostrRepository,
|
||||||
): ViewModelProvider.Factory = viewModelFactory {
|
): ViewModelProvider.Factory = viewModelFactory {
|
||||||
initializer {
|
initializer {
|
||||||
FeedListViewModel(
|
FeedListViewModel(
|
||||||
initialFeedListUIState = initialFeedListUIState,
|
initialFeedListUIState = initialFeedListUIState,
|
||||||
|
synchronizationFilter = synchronizationFilter,
|
||||||
nostrRepository = nostrRepository
|
nostrRepository = nostrRepository
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,198 +42,148 @@ class MetadataEventDetailViewModel(
|
|||||||
): ViewModel() {
|
): ViewModel() {
|
||||||
val logger = Logger.withTag(TAG)
|
val logger = Logger.withTag(TAG)
|
||||||
|
|
||||||
|
|
||||||
var feedJob: Job? = null
|
|
||||||
|
|
||||||
var metadataEventDetailType: MetadataEventDetailType by mutableStateOf(initialMetadataEventDetailType)
|
var metadataEventDetailType: MetadataEventDetailType by mutableStateOf(initialMetadataEventDetailType)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var feedListUIState: FeedListUIState by mutableStateOf(FeedListUIState.Loading)
|
|
||||||
private set
|
|
||||||
|
|
||||||
fun updateMetadataEventDetailType (metadataEventDetailType: MetadataEventDetailType, navigateToPage: () -> Unit) {
|
fun updateMetadataEventDetailType (metadataEventDetailType: MetadataEventDetailType, navigateToPage: () -> Unit) {
|
||||||
this.metadataEventDetailType = metadataEventDetailType
|
this.metadataEventDetailType = metadataEventDetailType
|
||||||
|
|
||||||
initiateMetadataFeed(this.metadataEventDetailType)
|
|
||||||
navigateToPage.invoke()
|
navigateToPage.invoke()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun initiateMetadataFeed(metadataEventDetailType: MetadataEventDetailType = this.metadataEventDetailType) {
|
fun getSynchronizationFilter() = when (metadataEventDetailType) {
|
||||||
feedListUIState = FeedListUIState.Loading
|
MetadataEventDetailType.Posts -> {
|
||||||
|
SynchronizationFilter(
|
||||||
logger.d("initiateMetadataFeed")
|
authors = arrayOf(
|
||||||
val synchronizationFilter = when (metadataEventDetailType) {
|
publicKey
|
||||||
MetadataEventDetailType.Posts -> {
|
),
|
||||||
SynchronizationFilter(
|
kinds = arrayOf(
|
||||||
authors = arrayOf(
|
TextNoteEvent.KIND,
|
||||||
publicKey
|
RepostEvent.KIND,
|
||||||
),
|
),
|
||||||
kinds = arrayOf(
|
limit = 50
|
||||||
TextNoteEvent.KIND,
|
)
|
||||||
RepostEvent.KIND,
|
|
||||||
),
|
|
||||||
limit = 50
|
|
||||||
)
|
|
||||||
}
|
|
||||||
MetadataEventDetailType.Replies -> {
|
|
||||||
SynchronizationFilter(
|
|
||||||
authors = arrayOf(
|
|
||||||
publicKey
|
|
||||||
),
|
|
||||||
kinds = arrayOf(
|
|
||||||
TextNoteEvent.KIND,
|
|
||||||
ReactionEvent.KIND,
|
|
||||||
),
|
|
||||||
// TODO: replyTo is set...
|
|
||||||
limit = 50
|
|
||||||
)
|
|
||||||
}
|
|
||||||
MetadataEventDetailType.Articles -> {
|
|
||||||
SynchronizationFilter(
|
|
||||||
authors = arrayOf(
|
|
||||||
publicKey
|
|
||||||
),
|
|
||||||
kinds = arrayOf(
|
|
||||||
LongTextNoteEvent.KIND,
|
|
||||||
),
|
|
||||||
limit = 50
|
|
||||||
)
|
|
||||||
}
|
|
||||||
MetadataEventDetailType.Followers -> {
|
|
||||||
SynchronizationFilter(
|
|
||||||
authors = arrayOf(
|
|
||||||
publicKey
|
|
||||||
),
|
|
||||||
kinds = arrayOf(
|
|
||||||
ContactListEvent.KIND,
|
|
||||||
),
|
|
||||||
// TODO: Check users with hexKey
|
|
||||||
limit = 50
|
|
||||||
)
|
|
||||||
}
|
|
||||||
MetadataEventDetailType.Following -> {
|
|
||||||
SynchronizationFilter(
|
|
||||||
authors = arrayOf(
|
|
||||||
publicKey
|
|
||||||
),
|
|
||||||
kinds = arrayOf(
|
|
||||||
ContactListEvent.KIND,
|
|
||||||
),
|
|
||||||
limit = 50
|
|
||||||
)
|
|
||||||
}
|
|
||||||
MetadataEventDetailType.Zaps -> {
|
|
||||||
SynchronizationFilter(
|
|
||||||
authors = arrayOf(
|
|
||||||
publicKey
|
|
||||||
),
|
|
||||||
kinds = arrayOf(
|
|
||||||
LnZapEvent.KIND,
|
|
||||||
),
|
|
||||||
// TODO: Zaps user has received...
|
|
||||||
limit = 50
|
|
||||||
)
|
|
||||||
}
|
|
||||||
MetadataEventDetailType.Photos -> {
|
|
||||||
SynchronizationFilter(
|
|
||||||
authors = arrayOf(
|
|
||||||
publicKey
|
|
||||||
),
|
|
||||||
kinds = arrayOf(
|
|
||||||
TextNoteEvent.KIND,
|
|
||||||
),
|
|
||||||
// TODO: filter for tags of pictures...
|
|
||||||
limit = 50
|
|
||||||
)
|
|
||||||
}
|
|
||||||
MetadataEventDetailType.Videos -> {
|
|
||||||
SynchronizationFilter(
|
|
||||||
authors = arrayOf(
|
|
||||||
publicKey
|
|
||||||
),
|
|
||||||
kinds = arrayOf(
|
|
||||||
TextNoteEvent.KIND,
|
|
||||||
),
|
|
||||||
// TODO: filter for tags of videos...
|
|
||||||
limit = 50
|
|
||||||
)
|
|
||||||
}
|
|
||||||
MetadataEventDetailType.Bookmarks -> {
|
|
||||||
SynchronizationFilter(
|
|
||||||
authors = arrayOf(
|
|
||||||
publicKey
|
|
||||||
),
|
|
||||||
kinds = arrayOf(
|
|
||||||
BookmarkListEvent.KIND,
|
|
||||||
),
|
|
||||||
limit = 50
|
|
||||||
)
|
|
||||||
}
|
|
||||||
MetadataEventDetailType.Reports -> {
|
|
||||||
SynchronizationFilter(
|
|
||||||
authors = arrayOf(
|
|
||||||
publicKey
|
|
||||||
),
|
|
||||||
kinds = arrayOf(
|
|
||||||
ReportEvent.KIND,
|
|
||||||
),
|
|
||||||
limit = 50
|
|
||||||
)
|
|
||||||
}
|
|
||||||
MetadataEventDetailType.Relays -> {
|
|
||||||
SynchronizationFilter(
|
|
||||||
authors = arrayOf(
|
|
||||||
publicKey
|
|
||||||
),
|
|
||||||
kinds = arrayOf(
|
|
||||||
RelayFeedsListEvent.KIND,
|
|
||||||
),
|
|
||||||
limit = 50
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
MetadataEventDetailType.Replies -> {
|
||||||
syncFeed(synchronizationFilter)
|
SynchronizationFilter(
|
||||||
loadFeed(synchronizationFilter)
|
authors = arrayOf(
|
||||||
}
|
publicKey
|
||||||
|
),
|
||||||
|
kinds = arrayOf(
|
||||||
|
TextNoteEvent.KIND,
|
||||||
private fun syncFeed(synchronizationFilter: SynchronizationFilter) {
|
ReactionEvent.KIND,
|
||||||
logger.d("syncFeed")
|
),
|
||||||
|
// TODO: replyTo is set...
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
limit = 50
|
||||||
// Sync Notifications... might want to also run this in the background
|
)
|
||||||
nostrRepository.queueSynchronizeNostrEvent(
|
}
|
||||||
Relays.eventFinderRelaySet.take(1).map { normalizedRelay -> // TODO: Sync from all relays instead of 1
|
MetadataEventDetailType.Articles -> {
|
||||||
SynchronizeNostrEventRequest(
|
SynchronizationFilter(
|
||||||
purpose = "search",
|
authors = arrayOf(
|
||||||
synchronizationFilters = arrayOf(
|
publicKey
|
||||||
synchronizationFilter
|
),
|
||||||
),
|
kinds = arrayOf(
|
||||||
relayURL = normalizedRelay.url
|
LongTextNoteEvent.KIND,
|
||||||
)
|
),
|
||||||
}
|
limit = 50
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MetadataEventDetailType.Followers -> {
|
||||||
|
SynchronizationFilter(
|
||||||
|
authors = arrayOf(
|
||||||
|
publicKey
|
||||||
|
),
|
||||||
|
kinds = arrayOf(
|
||||||
|
ContactListEvent.KIND,
|
||||||
|
),
|
||||||
|
// TODO: Check users with hexKey
|
||||||
|
limit = 50
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MetadataEventDetailType.Following -> {
|
||||||
|
SynchronizationFilter(
|
||||||
|
authors = arrayOf(
|
||||||
|
publicKey
|
||||||
|
),
|
||||||
|
kinds = arrayOf(
|
||||||
|
ContactListEvent.KIND,
|
||||||
|
),
|
||||||
|
limit = 50
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MetadataEventDetailType.Zaps -> {
|
||||||
|
SynchronizationFilter(
|
||||||
|
authors = arrayOf(
|
||||||
|
publicKey
|
||||||
|
),
|
||||||
|
kinds = arrayOf(
|
||||||
|
LnZapEvent.KIND,
|
||||||
|
),
|
||||||
|
// TODO: Zaps user has received...
|
||||||
|
limit = 50
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MetadataEventDetailType.Photos -> {
|
||||||
|
SynchronizationFilter(
|
||||||
|
authors = arrayOf(
|
||||||
|
publicKey
|
||||||
|
),
|
||||||
|
kinds = arrayOf(
|
||||||
|
TextNoteEvent.KIND,
|
||||||
|
),
|
||||||
|
// TODO: filter for tags of pictures...
|
||||||
|
limit = 50
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MetadataEventDetailType.Videos -> {
|
||||||
|
SynchronizationFilter(
|
||||||
|
authors = arrayOf(
|
||||||
|
publicKey
|
||||||
|
),
|
||||||
|
kinds = arrayOf(
|
||||||
|
TextNoteEvent.KIND,
|
||||||
|
),
|
||||||
|
// TODO: filter for tags of videos...
|
||||||
|
limit = 50
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MetadataEventDetailType.Bookmarks -> {
|
||||||
|
SynchronizationFilter(
|
||||||
|
authors = arrayOf(
|
||||||
|
publicKey
|
||||||
|
),
|
||||||
|
kinds = arrayOf(
|
||||||
|
BookmarkListEvent.KIND,
|
||||||
|
),
|
||||||
|
limit = 50
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MetadataEventDetailType.Reports -> {
|
||||||
|
SynchronizationFilter(
|
||||||
|
authors = arrayOf(
|
||||||
|
publicKey
|
||||||
|
),
|
||||||
|
kinds = arrayOf(
|
||||||
|
ReportEvent.KIND,
|
||||||
|
),
|
||||||
|
limit = 50
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MetadataEventDetailType.Relays -> {
|
||||||
|
SynchronizationFilter(
|
||||||
|
authors = arrayOf(
|
||||||
|
publicKey
|
||||||
|
),
|
||||||
|
kinds = arrayOf(
|
||||||
|
RelayFeedsListEvent.KIND,
|
||||||
|
),
|
||||||
|
limit = 50
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadFeed(synchronizationFilter: SynchronizationFilter) {
|
|
||||||
logger.d("loadFeed")
|
|
||||||
|
|
||||||
feedJob?.cancel()
|
|
||||||
feedJob = viewModelScope.launch(Dispatchers.IO) {
|
|
||||||
// This is something not taking into consideration the results we are actually looking for...
|
|
||||||
nostrRepository.observeNostrFeed(synchronizationFilter).collect { localNostrEvents ->
|
|
||||||
feedListUIState = FeedListUIState.Loaded(
|
|
||||||
localNostrEvents = localNostrEvents
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "SearchViewModel"
|
const val TAG = "MetadataEventDetailViewModel"
|
||||||
|
|
||||||
fun factory(
|
fun factory(
|
||||||
initialMetadataEventDetailType: MetadataEventDetailType = MetadataEventDetailType.Posts,
|
initialMetadataEventDetailType: MetadataEventDetailType = MetadataEventDetailType.Posts,
|
||||||
|
|||||||
Reference in New Issue
Block a user