Foundation for getOrCreate chat room
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "6eec904dd8f91ab435301e23425eb536",
|
||||
"identityHash": "10c21ff08b8acfda26aa17864d1e940e",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "BroadcastNostrEventReceipt",
|
||||
@@ -380,7 +380,7 @@
|
||||
},
|
||||
{
|
||||
"tableName": "ChatRoom",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `userPublicKey` TEXT NOT NULL, `subject` TEXT, `initialGiftWrapUnsignedId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`userPublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`initialGiftWrapUnsignedId`) REFERENCES `GiftWrapPayload`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `userPublicKey` TEXT NOT NULL, `subject` TEXT, `initialGiftWrapUnsignedId` TEXT, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`userPublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`initialGiftWrapUnsignedId`) REFERENCES `GiftWrapPayload`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
@@ -402,8 +402,7 @@
|
||||
{
|
||||
"fieldPath": "initialGiftWrapUnsignedId",
|
||||
"columnName": "initialGiftWrapUnsignedId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "createdAt",
|
||||
@@ -2544,7 +2543,7 @@
|
||||
],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6eec904dd8f91ab435301e23425eb536')"
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '10c21ff08b8acfda26aa17864d1e940e')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import ac.cord.auxiliary.compose.database.dao.NegentropySynchronizeResultDao
|
||||
import ac.cord.auxiliary.compose.database.dao.NostrDao
|
||||
import ac.cord.auxiliary.compose.database.dao.NostrEventDao
|
||||
import ac.cord.auxiliary.compose.database.dao.NostrEventRelayDao
|
||||
import ac.cord.auxiliary.compose.database.dao.NostrNip17Dao
|
||||
import ac.cord.auxiliary.compose.database.dao.PostDao
|
||||
import ac.cord.auxiliary.compose.database.dao.ProfileDao
|
||||
import ac.cord.auxiliary.compose.database.dao.QuotedRelationDao
|
||||
@@ -123,6 +124,8 @@ abstract class AuxDatabase: RoomDatabase() {
|
||||
|
||||
abstract fun nostrEventRelayDao(): NostrEventRelayDao
|
||||
|
||||
abstract fun nostrNip17Dao(): NostrNip17Dao
|
||||
|
||||
abstract fun postDao(): PostDao
|
||||
|
||||
abstract fun profileDao(): ProfileDao
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package ac.cord.auxiliary.compose.database.dao
|
||||
|
||||
import ac.cord.auxiliary.compose.database.AuxDatabase
|
||||
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalChatRoom
|
||||
import androidx.room3.Dao
|
||||
import androidx.room3.Transaction
|
||||
|
||||
@Dao
|
||||
abstract class NostrNip17Dao(
|
||||
private val database: AuxDatabase
|
||||
) {
|
||||
|
||||
@Transaction
|
||||
open suspend fun getOrCreateChatRoom(publicKey: String): LocalChatRoom? {
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@ data class ChatRoom(
|
||||
// TODO: Add a LastChatMessageRelation
|
||||
// val lastChatMessageId: String? = null,
|
||||
// Might want to focus on the other last messages...
|
||||
val initialGiftWrapUnsignedId: String,
|
||||
val initialGiftWrapUnsignedId: String? = null,
|
||||
|
||||
override val createdAt: Instant = Clock.System.now(),
|
||||
override val updatedAt: Instant = createdAt,
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package ac.cord.auxiliary.compose.database.repository
|
||||
|
||||
import ac.cord.auxiliary.compose.database.AuxDatabase
|
||||
import ac.cord.auxiliary.compose.database.model.ChatRoom
|
||||
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalChatRoom
|
||||
import ac.cord.auxiliary.compose.repository.ChatRepository
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
class DatabaseChatRepository(
|
||||
private val database: AuxDatabase,
|
||||
private val scope: CoroutineScope
|
||||
): ChatRepository {
|
||||
|
||||
override suspend fun getOrCreateChatRoom(publicKey: String): LocalChatRoom? {
|
||||
return database.nostrNip17Dao().getOrCreateChatRoom(
|
||||
publicKey
|
||||
)
|
||||
}
|
||||
|
||||
override fun updateChatRoomSubject(
|
||||
publicKey: String,
|
||||
subject: String
|
||||
): ChatRoom? {
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ac.cord.auxiliary.compose.repository
|
||||
|
||||
import ac.cord.auxiliary.compose.database.model.ChatRoom
|
||||
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalChatRoom
|
||||
|
||||
interface ChatRepository {
|
||||
suspend fun getOrCreateChatRoom(publicKey: String): LocalChatRoom?
|
||||
|
||||
fun updateChatRoomSubject(publicKey: String, subject: String): ChatRoom?
|
||||
|
||||
companion object {
|
||||
val NO_OP_CHAT_REPOSITORY = object: ChatRepository {
|
||||
override suspend fun getOrCreateChatRoom(publicKey: String): LocalChatRoom? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun updateChatRoomSubject(publicKey: String, subject: String): ChatRoom? {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,28 @@
|
||||
package ac.cord.auxiliary.compose.ui.composable
|
||||
|
||||
import ac.cord.auxiliary.compose.database.model.ChatRoom
|
||||
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.widgets.LoadingDataIndicator
|
||||
import ac.cord.auxiliary.compose.ui.theme.AuxTheme
|
||||
import ac.cord.auxiliary.compose.ui.view.model.ChatRoomDetailViewModel
|
||||
import ac.cord.auxiliary.compose.ui.view.state.ChatRoomDetailUIState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.material3.BottomAppBar
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
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.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -23,26 +31,65 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import kotlin.time.Clock
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ChatRoomDetailScreen(
|
||||
nostrRepository: NostrRepository
|
||||
initialChatRoomDetailUIState: ChatRoomDetailUIState = ChatRoomDetailUIState.Loading,
|
||||
nostrRepository: NostrRepository,
|
||||
chatRepository: ChatRepository
|
||||
) {
|
||||
val chatRoomDetailViewModel: ChatRoomDetailViewModel = viewModel(
|
||||
factory = ChatRoomDetailViewModel.factory(
|
||||
nostrRepository = nostrRepository
|
||||
publicKey = "",
|
||||
initialChatRoomDetailUIState = initialChatRoomDetailUIState,
|
||||
nostrRepository = nostrRepository,
|
||||
chatRepository = chatRepository
|
||||
)
|
||||
)
|
||||
|
||||
Scaffold { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier.padding(innerPadding)
|
||||
) {
|
||||
when (val chatRoomDetailUIState = chatRoomDetailViewModel.chatRoomDetailUIState) {
|
||||
ChatRoomDetailUIState.Error -> {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Spacer(
|
||||
modifier = Modifier.height(50.dp)
|
||||
)
|
||||
Text(
|
||||
text = "Something went wrong",
|
||||
)
|
||||
}
|
||||
}
|
||||
is ChatRoomDetailUIState.Loaded -> {
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(chatRoomDetailUIState.localChatRoom.chatRoom.subject ?: "Message")
|
||||
}
|
||||
)
|
||||
},
|
||||
bottomBar = {
|
||||
BottomAppBar {
|
||||
Text("Message actions coming soon...")
|
||||
}
|
||||
}
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier.padding(innerPadding)
|
||||
) {
|
||||
Text("Messages will appear here...")
|
||||
}
|
||||
}
|
||||
}
|
||||
ChatRoomDetailUIState.Loading -> {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth().padding(
|
||||
10.dp
|
||||
20.dp
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(20.dp)
|
||||
@@ -75,7 +122,9 @@ fun ChatRoomDetailScreen(
|
||||
}
|
||||
|
||||
LaunchedEffect(true) {
|
||||
chatRoomDetailViewModel.queueSynchronization()
|
||||
if (initialChatRoomDetailUIState == ChatRoomDetailUIState.Loading) {
|
||||
chatRoomDetailViewModel.initiateChatRoomDetail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +136,18 @@ private fun UnqueuedProfileSynchronizationScreenPreview() {
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
ChatRoomDetailScreen(
|
||||
nostrRepository = NostrRepository.NO_OP_NOSTR_REPOSITORY
|
||||
initialChatRoomDetailUIState = ChatRoomDetailUIState.Loaded(
|
||||
localChatRoom = LocalChatRoom(
|
||||
chatRoom = ChatRoom(
|
||||
id = "",
|
||||
userPublicKey = "",
|
||||
subject = "sdfa",
|
||||
initialGiftWrapUnsignedId = ""
|
||||
)
|
||||
)
|
||||
),
|
||||
nostrRepository = NostrRepository.NO_OP_NOSTR_REPOSITORY,
|
||||
chatRepository = ChatRepository.NO_OP_CHAT_REPOSITORY
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ac.cord.auxiliary.compose.ui.composable.navigation
|
||||
|
||||
import ac.cord.auxiliary.compose.AuxGlobal
|
||||
import ac.cord.auxiliary.compose.database.repository.DatabaseChatRepository
|
||||
import ac.cord.auxiliary.compose.database.repository.DatabaseNostrRepository
|
||||
import ac.cord.auxiliary.compose.database.repository.DatabaseSearchRepository
|
||||
import ac.cord.auxiliary.compose.managers.DatabaseManager
|
||||
@@ -88,6 +89,11 @@ fun AuxNavHost(
|
||||
database = databaseManager.auxDatabase,
|
||||
applicationIOScope
|
||||
)
|
||||
val databaseChatRepository = DatabaseChatRepository(
|
||||
database = databaseManager.auxDatabase,
|
||||
applicationIOScope
|
||||
)
|
||||
|
||||
val searchRepository = DatabaseSearchRepository(
|
||||
database = databaseManager.auxDatabase
|
||||
)
|
||||
@@ -353,7 +359,8 @@ fun AuxNavHost(
|
||||
}
|
||||
composable<ChatRoomDetailRoute> {
|
||||
ChatRoomDetailScreen(
|
||||
nostrRepository = databaseNostrRepository
|
||||
nostrRepository = databaseNostrRepository,
|
||||
chatRepository = databaseChatRepository
|
||||
)
|
||||
}
|
||||
composable<SearchRoute> {
|
||||
|
||||
@@ -1,30 +1,31 @@
|
||||
package ac.cord.auxiliary.compose.ui.view.model
|
||||
|
||||
import ac.cord.auxiliary.compose.repository.ChatRepository
|
||||
import ac.cord.auxiliary.compose.repository.NostrRepository
|
||||
import ac.cord.auxiliary.compose.ui.view.state.ChatRoomDetailUIState
|
||||
import androidx.compose.runtime.MutableState
|
||||
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 kotlinx.coroutines.launch
|
||||
|
||||
class ChatRoomDetailViewModel(
|
||||
val nostrRepository: NostrRepository
|
||||
val publicKey: String,
|
||||
initialChatRoomDetailUIState: ChatRoomDetailUIState,
|
||||
val nostrRepository: NostrRepository,
|
||||
val chatRepository: ChatRepository
|
||||
): ViewModel() {
|
||||
companion object {
|
||||
private const val TAG = "DirectMessageDetailViewModel"
|
||||
|
||||
fun factory(
|
||||
nostrRepository: NostrRepository
|
||||
): ViewModelProvider.Factory = viewModelFactory {
|
||||
initializer {
|
||||
ChatRoomDetailViewModel(
|
||||
nostrRepository = nostrRepository
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
var chatRoomDetailUIState: ChatRoomDetailUIState by mutableStateOf(initialChatRoomDetailUIState)
|
||||
private set
|
||||
|
||||
|
||||
|
||||
private val logger = Logger.withTag(TAG)
|
||||
|
||||
@@ -33,4 +34,43 @@ class ChatRoomDetailViewModel(
|
||||
fun queueSynchronization() {
|
||||
logger.d { "queueSynchronization" }
|
||||
}
|
||||
|
||||
// TODO: Render MessageList...
|
||||
fun initiateChatRoomDetail() {
|
||||
// TODO: GetOrCreateChatRoom
|
||||
viewModelScope.launch {
|
||||
val localChatRoom = chatRepository.getOrCreateChatRoom(publicKey)
|
||||
|
||||
chatRoomDetailUIState = if (localChatRoom == null) {
|
||||
ChatRoomDetailUIState.Error
|
||||
} else {
|
||||
ChatRoomDetailUIState.Loaded(
|
||||
localChatRoom = localChatRoom
|
||||
)
|
||||
}
|
||||
|
||||
queueSynchronization()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ChatRoomDetailViewModel"
|
||||
|
||||
fun factory(
|
||||
publicKey: String,
|
||||
initialChatRoomDetailUIState: ChatRoomDetailUIState = ChatRoomDetailUIState.Loading,
|
||||
nostrRepository: NostrRepository,
|
||||
chatRepository: ChatRepository
|
||||
): ViewModelProvider.Factory = viewModelFactory {
|
||||
initializer {
|
||||
ChatRoomDetailViewModel(
|
||||
publicKey = publicKey,
|
||||
initialChatRoomDetailUIState = initialChatRoomDetailUIState,
|
||||
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 ChatRoomDetailUIState {
|
||||
data class Loaded(
|
||||
val localChatRoom: LocalChatRoom
|
||||
): ChatRoomDetailUIState
|
||||
|
||||
data object Error: ChatRoomDetailUIState
|
||||
data object Loading: ChatRoomDetailUIState
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user