Show message lists

This commit is contained in:
Kgothatso Ngako
2026-05-06 00:58:08 +02:00
parent a268a9ab71
commit c57fc80082
6 changed files with 343 additions and 7 deletions

View File

@@ -12,8 +12,10 @@ 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.model.MessageListViewModel
import ac.cord.auxiliary.compose.ui.view.state.FeedListUIState
import ac.cord.auxiliary.compose.ui.view.state.HomeScreenUIState
import ac.cord.auxiliary.compose.ui.view.state.MessageListUIState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
@@ -208,9 +210,29 @@ fun HomeScreen(
feedListViewModel.initiate()
}
}
HomeScreenType.Messages -> {
Text("Messaging coming soon.")
val messageListViewModel: MessageListViewModel = viewModel(
key = homeScreenTypeType.name,
factory = MessageListViewModel.factory(
initialMessageListUIState = MessageListUIState.Loading,
publicKey = homeScreenUIState.profileWithFollowing.profile.publicKey,
synchronizationFilter = homeScreenViewModel.getSynchronizationFilter(
homeScreenTypeType,
profileWithFollowing = homeScreenUIState.profileWithFollowing
),
nostrRepository = nostrRepository
),
)
key(messageListViewModel.messageListUIState) {
messageListViewModel.RenderFeed(
onNavigateToEvent = onNavigateToEvent
)
}
key(true) {
messageListViewModel.initiate()
}
}
}
}

View File

@@ -180,7 +180,7 @@ class FollowersListViewModel(
}
companion object {
const val TAG = "FeedListViewModel"
const val TAG = "FollowersListViewModel"
fun factory(
initialFeedListUIState: FollowersListUIState = FollowersListUIState.Loading,

View File

@@ -179,7 +179,7 @@ class FollowingListViewModel(
}
companion object {
const val TAG = "FeedListViewModel"
const val TAG = "FollowingListViewModel"
fun factory(
initialFeedListUIState: FollowingListUIState = FollowingListUIState.Loading,

View File

@@ -120,10 +120,8 @@ class HomeViewModel(
publicKey
),
kinds = arrayOf(
LongTextNoteEvent.KIND,
ContactListEvent.KIND,
),
since = twelveHoursAgo,
until = now,
limit = 50
)
}

View File

@@ -0,0 +1,303 @@
package ac.cord.auxiliary.compose.ui.view.model
import ac.cord.auxiliary.compose.database.model.NegentropySynchronizeRequest
import ac.cord.auxiliary.compose.database.model.NostrEvent
import ac.cord.auxiliary.compose.database.model.Profile
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalProfileWithFollowing
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.HomeScreen
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.state.FollowingListUIState
import ac.cord.auxiliary.compose.ui.view.state.HomeScreenUIState
import ac.cord.auxiliary.compose.ui.view.state.MessageListUIState
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.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.LoadingIndicator
import androidx.compose.material3.Surface
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.tooling.preview.Preview
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 com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
class MessageListViewModel(
initialMessageListUIState: MessageListUIState,
val publicKey: String,
val synchronizationFilter: SynchronizationFilter,
val nostrRepository: NostrRepository
): ViewModel() {
val logger = Logger.withTag(TAG)
var messageListUIState: MessageListUIState by mutableStateOf(initialMessageListUIState)
private set
val isSynchronizationPending: MutableState<Boolean> = mutableStateOf(false)
fun initiate() {
logger.d("init")
// scheduleSynchronization()
observeLocalNostrFeed()
// observeSynchronizeNostrEventRequestByPurposeAndStatusCount()
}
fun observeLocalNostrFeed() {
logger.d("observeLocalNostrFeed")
viewModelScope.launch(Dispatchers.IO) {
nostrRepository.observeProfileWithFollowing(
publicKey
).collect {
messageListUIState = MessageListUIState.Loaded(
messageList = emptyList()
)
}
}
}
fun scheduleSynchronization() {
logger.d("scheduleSynchronization")
viewModelScope.launch(Dispatchers.IO) {
// Sync Notifications... might want to also run this in the background
nostrRepository.queueNegentropySynchronizeRequest(
Relays.negentropicRelaySet.shuffled().map { normalizedRelayUrl ->
NegentropySynchronizeRequest(
id = NegentropySynchronizeRequest.computeId(
relayURL = normalizedRelayUrl.url,
synchronizationFilter = synchronizationFilter
),
purpose = "following",
synchronizationFilter = synchronizationFilter,
relayURL = normalizedRelayUrl.url,
level = 0
)
}
)
}
}
fun observeSynchronizeNostrEventRequestByPurposeAndStatusCount() {
logger.d("observeSynchronizeNostrEventRequestByPurposeAndStatusCount")
viewModelScope.launch(Dispatchers.IO) {
nostrRepository.observeSynchronizeNostrEventRequestByPurposeAndStatusCount("feed").distinctUntilChanged().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 feedListUIState = messageListUIState) {
MessageListUIState.Error -> {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(
modifier = Modifier.height(50.dp)
)
Text(
text = "Something went wrong",
)
}
}
is MessageListUIState.Loaded -> {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
Text(
text = "Messages"
)
Card(
modifier = Modifier.fillMaxWidth(),
onClick = {
}
) {
Column(
modifier = Modifier.padding(20.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = "Steve Biko"
)
}
}
Card(
modifier = Modifier.fillMaxWidth(),
onClick = {
}
) {
Column(
modifier = Modifier.padding(20.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Chris Hani"
)
}
}
Card(
modifier = Modifier.fillMaxWidth(),
onClick = {
}
) {
Column(
modifier = Modifier.padding(20.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Desmond Tutu"
)
}
}
Card(
modifier = Modifier.fillMaxWidth(),
onClick = {
}
) {
Column(
modifier = Modifier.padding(20.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Hip Hop Pantsula"
)
}
}
Card(
modifier = Modifier.fillMaxWidth(),
onClick = {
}
) {
Column(
modifier = Modifier.padding(20.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Knee Cap"
)
}
}
}
// if (feedListUIState.messageList.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.messageList,
// key = { profile -> profile.publicKey }
// ) { profile ->
// profile.RenderAsListItem(
// onNavigateToEvent = onNavigateToEvent
// )
// }
// }
// }
}
MessageListUIState.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 = "MessageListViewModel"
fun factory(
initialMessageListUIState: MessageListUIState = MessageListUIState.Loading,
publicKey: String,
synchronizationFilter: SynchronizationFilter,
nostrRepository: NostrRepository,
): ViewModelProvider.Factory = viewModelFactory {
initializer {
MessageListViewModel(
initialMessageListUIState = initialMessageListUIState,
publicKey = publicKey,
synchronizationFilter = synchronizationFilter,
nostrRepository = nostrRepository
)
}
}
}
}

View File

@@ -0,0 +1,13 @@
package ac.cord.auxiliary.compose.ui.view.state
import ac.cord.auxiliary.compose.database.model.Profile
sealed interface MessageListUIState {
data class Loaded(
val messageList: List<Profile>
): MessageListUIState
data object Error: MessageListUIState
data object Loading: MessageListUIState
}