Add a placeholder chatmessagelist view mode.
This commit is contained in:
@@ -13,6 +13,10 @@ class DatabaseChatRepository(
|
||||
): ChatRepository {
|
||||
val logger = Logger.withTag(TAG)
|
||||
|
||||
override suspend fun getChatRoomListByPublicKey(publicKey: String): List<LocalChatRoom> {
|
||||
return database.chatRoomDao().getChatRoomListByUserPublicKey(publicKey)
|
||||
}
|
||||
|
||||
override suspend fun getChatRoomByIdentifier(id: String): LocalChatRoom? {
|
||||
return database.chatRoomDao().findChatRoomById(id)
|
||||
}
|
||||
|
||||
@@ -692,10 +692,6 @@ class DatabaseNostrRepository(
|
||||
return database.profileDao().observeProfileWithFollowingByPublicKey(publicKey)
|
||||
}
|
||||
|
||||
override suspend fun getChatRoomListByPublicKey(publicKey: String): List<LocalChatRoom> {
|
||||
return database.chatRoomDao().getChatRoomListByUserPublicKey(publicKey)
|
||||
}
|
||||
|
||||
override suspend fun getRecentSearches(): List<RecentSearch> {
|
||||
return database.recentSearchDao().getAllRecentSearches()
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import ac.cord.auxiliary.compose.database.model.ChatRoom
|
||||
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalChatRoom
|
||||
|
||||
interface ChatRepository {
|
||||
suspend fun getChatRoomListByPublicKey(publicKey: String): List<LocalChatRoom>
|
||||
|
||||
suspend fun getChatRoomByIdentifier(id: String): LocalChatRoom?
|
||||
|
||||
suspend fun getOrCreateChatRoom(publicKey: String, defaultSubject: String? = null): LocalChatRoom?
|
||||
@@ -11,9 +13,13 @@ interface ChatRepository {
|
||||
fun updateChatRoomSubject(publicKey: String, subject: String): ChatRoom?
|
||||
|
||||
companion object {
|
||||
val NO_OP_CHAT_REPOSITORY = object: ChatRepository {
|
||||
val NO_OP_CHAT_REPOSITORY = object: ChatRepository {
|
||||
override suspend fun getChatRoomListByPublicKey(publicKey: String): List<LocalChatRoom> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override suspend fun getChatRoomByIdentifier(id: String): LocalChatRoom? {
|
||||
TODO("Not yet implemented")
|
||||
return null
|
||||
}
|
||||
|
||||
override suspend fun getOrCreateChatRoom(publicKey: String, defaultSubject: String?): LocalChatRoom? {
|
||||
|
||||
@@ -124,8 +124,6 @@ interface NostrRepository {
|
||||
|
||||
suspend fun observeProfileWithFollowing(publicKey: String): Flow<LocalProfileWithFollowing?>
|
||||
|
||||
suspend fun getChatRoomListByPublicKey(publicKey: String): List<LocalChatRoom>
|
||||
|
||||
suspend fun getRecentSearches(): List<RecentSearch>
|
||||
|
||||
suspend fun removeRecentSearch(recentSearch: RecentSearch)
|
||||
@@ -298,10 +296,6 @@ interface NostrRepository {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun getChatRoomListByPublicKey(publicKey: String): List<LocalChatRoom> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun getRecentSearches(): List<RecentSearch> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ import ac.cord.auxiliary.compose.repository.ChatRepository
|
||||
import ac.cord.auxiliary.compose.repository.NostrRepository
|
||||
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.ChatRoomDetailMessageListViewModel
|
||||
import ac.cord.auxiliary.compose.ui.view.model.ChatRoomDetailViewModel
|
||||
import ac.cord.auxiliary.compose.ui.view.state.ChatRoomDetailMessageListUIState
|
||||
import ac.cord.auxiliary.compose.ui.view.state.ChatRoomDetailUIState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@@ -53,6 +55,7 @@ import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -96,6 +99,14 @@ fun ChatRoomDetailScreen(
|
||||
is ChatRoomDetailUIState.Loaded -> {
|
||||
val textFieldState = rememberTextFieldState()
|
||||
|
||||
val chatRoomDetailMessageListViewModel: ChatRoomDetailMessageListViewModel = viewModel(
|
||||
factory = ChatRoomDetailMessageListViewModel.factory(
|
||||
localChatRoom = chatRoomDetailUIState.localChatRoom,
|
||||
nostrRepository = nostrRepository,
|
||||
chatRepository = chatRepository
|
||||
)
|
||||
)
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
@@ -118,12 +129,19 @@ fun ChatRoomDetailScreen(
|
||||
modifier = Modifier.padding(innerPadding).fillMaxSize()
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
modifier = Modifier.weight(1f).fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
text = "Messages will appear here..."
|
||||
)
|
||||
key(true) {
|
||||
chatRoomDetailMessageListViewModel.RenderMessages(
|
||||
onNavigateToDirectMessageDetail = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
key(true) {
|
||||
chatRoomDetailMessageListViewModel.initiate()
|
||||
}
|
||||
|
||||
// TODO: Check that we have direct message relays for this user...
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
|
||||
@@ -3,6 +3,7 @@ package ac.cord.auxiliary.compose.ui.composable
|
||||
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.repository.ChatRepository
|
||||
import ac.cord.auxiliary.compose.repository.NostrRepository
|
||||
import ac.cord.auxiliary.compose.ui.composable.navigation.routes.ChatRoomDetailRoute
|
||||
import ac.cord.auxiliary.compose.ui.composable.navigation.routes.NostrEventDetailRoute
|
||||
@@ -61,6 +62,7 @@ fun HomeScreen(
|
||||
onNavigateToWriteNewNote: () -> Unit,
|
||||
onNavigateToSearch: () -> Unit,
|
||||
nostrRepository: NostrRepository,
|
||||
chatRepository: ChatRepository
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
@@ -221,7 +223,8 @@ fun HomeScreen(
|
||||
homeScreenTypeType,
|
||||
profileWithFollowing = homeScreenUIState.profileWithFollowing
|
||||
),
|
||||
nostrRepository = nostrRepository
|
||||
nostrRepository = nostrRepository,
|
||||
chatRepository = chatRepository
|
||||
),
|
||||
)
|
||||
|
||||
@@ -285,7 +288,8 @@ It has survived not only five centuries, but also the leap into electronic types
|
||||
onNavigateToWriteNewNote = {},
|
||||
onNavigateToSearch = {},
|
||||
onNavigateToDirectMessageDetail = {},
|
||||
nostrRepository = NostrRepository.NO_OP_NOSTR_REPOSITORY
|
||||
nostrRepository = NostrRepository.NO_OP_NOSTR_REPOSITORY,
|
||||
chatRepository = ChatRepository.NO_OP_CHAT_REPOSITORY
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +344,8 @@ fun AuxNavHost(
|
||||
route = chatRoomDetailRoute
|
||||
)
|
||||
},
|
||||
nostrRepository = databaseNostrRepository
|
||||
nostrRepository = databaseNostrRepository,
|
||||
chatRepository = databaseChatRepository
|
||||
)
|
||||
}
|
||||
composable<BlankRoute> {
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
package ac.cord.auxiliary.compose.ui.view.model
|
||||
|
||||
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalChatRoom
|
||||
import ac.cord.auxiliary.compose.repository.ChatRepository
|
||||
import ac.cord.auxiliary.compose.repository.NostrRepository
|
||||
import ac.cord.auxiliary.compose.ui.composable.navigation.routes.ChatRoomDetailRoute
|
||||
import ac.cord.auxiliary.compose.ui.composable.navigation.routes.Route
|
||||
import ac.cord.auxiliary.compose.ui.view.state.ChatRoomDetailMessageListUIState
|
||||
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.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
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 ChatRoomDetailMessageListViewModel(
|
||||
initialChatRoomDetailMessageListUIState: ChatRoomDetailMessageListUIState,
|
||||
val localChatRoom: LocalChatRoom,
|
||||
val nostrRepository: NostrRepository,
|
||||
val chatRepository: ChatRepository
|
||||
): ViewModel() {
|
||||
val logger = Logger.withTag(TAG)
|
||||
var chatRoomDetailMessageListUIState: ChatRoomDetailMessageListUIState by mutableStateOf(initialChatRoomDetailMessageListUIState)
|
||||
private set
|
||||
|
||||
fun initiate() {
|
||||
logger.d("init")
|
||||
scheduleSynchronization()
|
||||
observeChatRoomFeed()
|
||||
}
|
||||
|
||||
fun observeChatRoomFeed() {
|
||||
logger.d("observeChatRoomFeed")
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
chatRepository.getChatRoomListByPublicKey(
|
||||
localChatRoom.chatRoom.userPublicKey
|
||||
).let { chatRooms ->
|
||||
logger.d("ChatRoomList: $chatRooms")
|
||||
chatRoomDetailMessageListUIState = ChatRoomDetailMessageListUIState.Loaded(
|
||||
chatRoomList = chatRooms
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun scheduleSynchronization() {
|
||||
logger.d("scheduleSynchronization")
|
||||
// viewModelScope.launch(Dispatchers.IO) {
|
||||
// // Sync Notifications... might want to also run this in the background
|
||||
// nostrRepository.queueNegentropySynchronizeRequest(
|
||||
// Relays.DefaultDMRelayList.shuffled().map { normalizedRelayUrl ->
|
||||
// NegentropySynchronizeRequest(
|
||||
// id = NegentropySynchronizeRequest.computeId(
|
||||
// relayURL = normalizedRelayUrl.url,
|
||||
// synchronizationFilter = synchronizationFilter
|
||||
// ),
|
||||
// purpose = "chat",
|
||||
// synchronizationFilter = synchronizationFilter,
|
||||
// relayURL = normalizedRelayUrl.url,
|
||||
// level = 0
|
||||
// )
|
||||
// }
|
||||
// )
|
||||
// }
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun RenderMessages(
|
||||
onNavigateToDirectMessageDetail: (Route) -> Unit
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
when (val chatRoomDetailMessageListUIState = this@ChatRoomDetailMessageListViewModel.chatRoomDetailMessageListUIState) {
|
||||
ChatRoomDetailMessageListUIState.Error -> {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Spacer(
|
||||
modifier = Modifier.height(50.dp)
|
||||
)
|
||||
Text(
|
||||
text = "Something went wrong",
|
||||
)
|
||||
}
|
||||
}
|
||||
is ChatRoomDetailMessageListUIState.Loaded -> {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||
) {
|
||||
|
||||
if (chatRoomDetailMessageListUIState.chatRoomList.isEmpty()) {
|
||||
Text(
|
||||
text = "No messages. Go to a profile and send them a message."
|
||||
)
|
||||
} else {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
reverseLayout = true
|
||||
) {
|
||||
items(
|
||||
items = chatRoomDetailMessageListUIState.chatRoomList,
|
||||
key = { it.chatRoom.id }
|
||||
) { localChatRoom ->
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = {
|
||||
onNavigateToDirectMessageDetail.invoke(
|
||||
ChatRoomDetailRoute(
|
||||
directMessageIdentifier = localChatRoom.chatRoom.id
|
||||
)
|
||||
)
|
||||
}
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(20.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
if (localChatRoom.participants.size == 1) {
|
||||
Text(
|
||||
text = "Note to Self (${localChatRoom.participants.first().profile?.humanReadableNameOrPubkey()})"
|
||||
)
|
||||
} else {
|
||||
// Remove the active user publicKey from participants...
|
||||
val participantNames = localChatRoom.participants.filter { it.participant.participantPublicKey != localChatRoom.chatRoom.userPublicKey }.map { it.profile?.humanReadableNameOrPubkey() ?: "unknown participant" }
|
||||
|
||||
Text(
|
||||
text = localChatRoom.chatRoom.subject ?: participantNames.joinToString()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ChatRoomDetailMessageListUIState.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 = "ChatRoomDetailMessageListViewModel"
|
||||
|
||||
fun factory(
|
||||
initialChatRoomDetailMessageListUIState: ChatRoomDetailMessageListUIState = ChatRoomDetailMessageListUIState.Loading,
|
||||
localChatRoom: LocalChatRoom,
|
||||
nostrRepository: NostrRepository,
|
||||
chatRepository: ChatRepository
|
||||
): ViewModelProvider.Factory = viewModelFactory {
|
||||
initializer {
|
||||
ChatRoomDetailMessageListViewModel(
|
||||
initialChatRoomDetailMessageListUIState = initialChatRoomDetailMessageListUIState,
|
||||
localChatRoom = localChatRoom,
|
||||
nostrRepository = nostrRepository,
|
||||
chatRepository = chatRepository
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package ac.cord.auxiliary.compose.ui.view.model
|
||||
import ac.cord.auxiliary.compose.database.model.NegentropySynchronizeRequest
|
||||
import ac.cord.auxiliary.compose.database.model.types.SynchronizationFilter
|
||||
import ac.cord.auxiliary.compose.nostr.Relays
|
||||
import ac.cord.auxiliary.compose.repository.ChatRepository
|
||||
import ac.cord.auxiliary.compose.repository.NostrRepository
|
||||
import ac.cord.auxiliary.compose.ui.composable.navigation.routes.ChatRoomDetailRoute
|
||||
import ac.cord.auxiliary.compose.ui.composable.navigation.routes.Route
|
||||
@@ -42,7 +43,8 @@ class ChatRoomListViewModel(
|
||||
initialChatRoomListUIState: ChatRoomListUIState,
|
||||
val publicKey: String,
|
||||
val synchronizationFilter: SynchronizationFilter,
|
||||
val nostrRepository: NostrRepository
|
||||
val nostrRepository: NostrRepository,
|
||||
val chatRepository: ChatRepository
|
||||
): ViewModel() {
|
||||
val logger = Logger.withTag(TAG)
|
||||
var chatRoomListUIState: ChatRoomListUIState by mutableStateOf(initialChatRoomListUIState)
|
||||
@@ -57,7 +59,7 @@ class ChatRoomListViewModel(
|
||||
fun observeChatRoomFeed() {
|
||||
logger.d("observeChatRoomFeed")
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
nostrRepository.getChatRoomListByPublicKey(
|
||||
chatRepository.getChatRoomListByPublicKey(
|
||||
publicKey
|
||||
).let { chatRooms ->
|
||||
logger.d("ChatRoomList: $chatRooms")
|
||||
@@ -202,13 +204,15 @@ class ChatRoomListViewModel(
|
||||
publicKey: String,
|
||||
synchronizationFilter: SynchronizationFilter,
|
||||
nostrRepository: NostrRepository,
|
||||
chatRepository: ChatRepository
|
||||
): ViewModelProvider.Factory = viewModelFactory {
|
||||
initializer {
|
||||
ChatRoomListViewModel(
|
||||
initialChatRoomListUIState = initialChatRoomListUIState,
|
||||
publicKey = publicKey,
|
||||
synchronizationFilter = synchronizationFilter,
|
||||
nostrRepository = nostrRepository
|
||||
nostrRepository = nostrRepository,
|
||||
chatRepository = chatRepository
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package ac.cord.auxiliary.compose.ui.view.state
|
||||
|
||||
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalChatRoom
|
||||
|
||||
sealed interface ChatRoomDetailMessageListUIState {
|
||||
data class Loaded(
|
||||
val chatRoomList: List<LocalChatRoom>
|
||||
): ChatRoomDetailMessageListUIState
|
||||
|
||||
data object Error: ChatRoomDetailMessageListUIState
|
||||
data object Loading: ChatRoomDetailMessageListUIState
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user