Add following list
This commit is contained in:
@@ -39,6 +39,7 @@ 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
|
||||
@@ -239,6 +240,10 @@ fun FeedScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
key(true) {
|
||||
feedListViewModel.initiate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,12 @@ import ac.cord.auxiliary.compose.repository.NostrRepository
|
||||
import ac.cord.auxiliary.compose.ui.composable.navigation.routes.Route
|
||||
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.FollowersListViewModel
|
||||
import ac.cord.auxiliary.compose.ui.view.model.FollowingListViewModel
|
||||
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.state.FeedListUIState
|
||||
import ac.cord.auxiliary.compose.ui.view.state.FollowersListUIState
|
||||
import ac.cord.auxiliary.compose.ui.view.state.FollowingListUIState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -230,6 +232,10 @@ fun MetadataEventDetail(
|
||||
onNavigateToEvent = onNavigateToEvent
|
||||
)
|
||||
}
|
||||
|
||||
key(true) {
|
||||
feedListViewModel.initiate()
|
||||
}
|
||||
}
|
||||
|
||||
MetadataEventDetailType.Following -> {
|
||||
@@ -249,10 +255,32 @@ fun MetadataEventDetail(
|
||||
onNavigateToEvent = onNavigateToEvent
|
||||
)
|
||||
}
|
||||
|
||||
key(true) {
|
||||
followingListViewModel.initiate()
|
||||
}
|
||||
}
|
||||
MetadataEventDetailType.Followers -> {
|
||||
val followersListViewModel: FollowersListViewModel = viewModel(
|
||||
key = metadataEventDetailType.name,
|
||||
factory = FollowersListViewModel.factory(
|
||||
initialFeedListUIState = FollowersListUIState.Loading,
|
||||
publicKey = localNostrEvent.nostrEvent.pubKey,
|
||||
synchronizationFilter = metadataEventDetailViewModel.getSynchronizationFilter(metadataEventDetailType),
|
||||
nostrRepository = nostrRepository
|
||||
),
|
||||
)
|
||||
|
||||
Text("Implementing logic to see followers")
|
||||
|
||||
key(followersListViewModel.followersListUIState) {
|
||||
followersListViewModel.RenderFeed(
|
||||
onNavigateToEvent = onNavigateToEvent
|
||||
)
|
||||
}
|
||||
|
||||
key(true) {
|
||||
followersListViewModel.initiate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class FeedListViewModel(
|
||||
|
||||
val isSynchronizationPending: MutableState<Boolean> = mutableStateOf(false)
|
||||
|
||||
init {
|
||||
fun initiate() {
|
||||
logger.d("init")
|
||||
scheduleSynchronization()
|
||||
observeLocalNostrFeed()
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
package ac.cord.auxiliary.compose.ui.view.model
|
||||
|
||||
import ac.cord.auxiliary.compose.database.model.SynchronizeNostrEventRequest
|
||||
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.ui.composable.navigation.routes.NostrEventDetailRoute
|
||||
import ac.cord.auxiliary.compose.ui.composable.navigation.routes.Route
|
||||
import ac.cord.auxiliary.compose.ui.view.state.FollowersListUIState
|
||||
import androidx.compose.foundation.clickable
|
||||
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.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
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.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.initializer
|
||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class FollowersListViewModel(
|
||||
initialFollowingListUIState: FollowersListUIState,
|
||||
val publicKey: String,
|
||||
val synchronizationFilter: SynchronizationFilter,
|
||||
val nostrRepository: NostrRepository
|
||||
): ViewModel() {
|
||||
val logger = Logger.withTag(TAG)
|
||||
var followersListUIState: FollowersListUIState by mutableStateOf(initialFollowingListUIState)
|
||||
private set
|
||||
|
||||
val isSynchronizationPending: MutableState<Boolean> = mutableStateOf(false)
|
||||
|
||||
fun initiate() {
|
||||
logger.d("initiate")
|
||||
scheduleSynchronization()
|
||||
observeLocalNostrFeed()
|
||||
observeSynchronizeNostrEventRequestByPurposeAndStatusCount()
|
||||
}
|
||||
|
||||
fun observeLocalNostrFeed() {
|
||||
logger.d("observeLocalNostrFeed")
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
nostrRepository.observeProfileFollowers(
|
||||
publicKey
|
||||
).collect { localFollowers ->
|
||||
followersListUIState = FollowersListUIState.Loaded(
|
||||
followers = localFollowers?.followers ?: emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun scheduleSynchronization() {
|
||||
logger.d("scheduleSynchronization")
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
// Sync Notifications... might want to also run this in the background
|
||||
nostrRepository.queueSynchronizeNostrEvent(
|
||||
Relays.eventPublishRelaySet.take(1).map { normalizedRelay -> // TODO: Sync from all the publish relays...
|
||||
SynchronizeNostrEventRequest(
|
||||
purpose = "feed",
|
||||
synchronizationFilters = arrayOf(
|
||||
synchronizationFilter
|
||||
),
|
||||
relayURL = normalizedRelay.url
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun observeSynchronizeNostrEventRequestByPurposeAndStatusCount() {
|
||||
logger.d("observeSynchronizeNostrEventRequestByPurposeAndStatusCount")
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
nostrRepository.observeSynchronizeNostrEventRequestByPurposeAndStatusCount("feed").collect { count ->
|
||||
logger.d("Pending/Processing Synchronization Request: $count")
|
||||
isSynchronizationPending.value = count > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun RenderFeed(
|
||||
onNavigateToEvent: (Route) -> Unit
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
when (val followersListUIState = followersListUIState) {
|
||||
FollowersListUIState.Error -> {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Spacer(
|
||||
modifier = Modifier.height(50.dp)
|
||||
)
|
||||
Text(
|
||||
text = "Something went wrong",
|
||||
)
|
||||
}
|
||||
}
|
||||
is FollowersListUIState.Loaded -> {
|
||||
if (followersListUIState.followers.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 = followersListUIState.followers,
|
||||
key = { profile -> profile.publicKey }
|
||||
) { follower ->
|
||||
Column(
|
||||
modifier = Modifier.clickable {
|
||||
onNavigateToEvent.invoke(
|
||||
NostrEventDetailRoute(
|
||||
follower.nostrEventId
|
||||
)
|
||||
)
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
text = follower.displayName ?: follower.userName ?: follower.userName ?: follower.publicKey
|
||||
)
|
||||
|
||||
follower.nip05?.let {
|
||||
Text(
|
||||
text = follower.nip05
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
FollowersListUIState.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 {
|
||||
const val TAG = "FeedListViewModel"
|
||||
|
||||
fun factory(
|
||||
initialFeedListUIState: FollowersListUIState = FollowersListUIState.Loading,
|
||||
publicKey: String,
|
||||
synchronizationFilter: SynchronizationFilter,
|
||||
nostrRepository: NostrRepository,
|
||||
): ViewModelProvider.Factory = viewModelFactory {
|
||||
initializer {
|
||||
FollowersListViewModel(
|
||||
initialFollowingListUIState = initialFeedListUIState,
|
||||
publicKey = publicKey,
|
||||
synchronizationFilter = synchronizationFilter,
|
||||
nostrRepository = nostrRepository
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import ac.cord.auxiliary.compose.nostr.Relays
|
||||
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.FollowingListUIState
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@@ -50,13 +49,12 @@ class FollowingListViewModel(
|
||||
|
||||
val isSynchronizationPending: MutableState<Boolean> = mutableStateOf(false)
|
||||
|
||||
init {
|
||||
fun initiate() {
|
||||
logger.d("init")
|
||||
scheduleSynchronization()
|
||||
observeLocalNostrFeed()
|
||||
observeSynchronizeNostrEventRequestByPurposeAndStatusCount()
|
||||
}
|
||||
|
||||
fun observeLocalNostrFeed() {
|
||||
logger.d("observeLocalNostrFeed")
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
@@ -64,7 +62,7 @@ class FollowingListViewModel(
|
||||
publicKey
|
||||
).collect { localFollowing ->
|
||||
followingListUIState = FollowingListUIState.Loaded(
|
||||
profilesBeingFollowed = localFollowing?.following ?: emptyList()
|
||||
following = localFollowing?.following ?: emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -122,7 +120,7 @@ class FollowingListViewModel(
|
||||
}
|
||||
}
|
||||
is FollowingListUIState.Loaded -> {
|
||||
if (feedListUIState.profilesBeingFollowed.isEmpty()) {
|
||||
if (feedListUIState.following.isEmpty()) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
@@ -141,7 +139,7 @@ class FollowingListViewModel(
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||
) {
|
||||
items(
|
||||
items = feedListUIState.profilesBeingFollowed,
|
||||
items = feedListUIState.following,
|
||||
key = { profile -> profile.publicKey }
|
||||
) { profile ->
|
||||
Column(
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package ac.cord.auxiliary.compose.ui.view.state
|
||||
|
||||
import ac.cord.auxiliary.compose.database.model.Profile
|
||||
|
||||
sealed interface FollowersListUIState {
|
||||
data class Loaded(
|
||||
val followers: List<Profile>
|
||||
): FollowersListUIState
|
||||
|
||||
data object Error: FollowersListUIState
|
||||
data object Loading: FollowersListUIState
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import ac.cord.auxiliary.compose.database.model.Profile
|
||||
|
||||
sealed interface FollowingListUIState {
|
||||
data class Loaded(
|
||||
val profilesBeingFollowed: List<Profile>
|
||||
val following: List<Profile>
|
||||
): FollowingListUIState
|
||||
|
||||
data object Error: FollowingListUIState
|
||||
|
||||
Reference in New Issue
Block a user