Skip to content

Commit

Permalink
Initial concepts for Chat Screens / Kotlin 2.1 / Updated other versions
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffdcamp committed Dec 19, 2024
1 parent e312bf5 commit 180725b
Show file tree
Hide file tree
Showing 35 changed files with 1,428 additions and 16 deletions.
6 changes: 5 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,9 @@ dependencies {
implementation(libs.workmanagertools)

// Database
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.room.ktx)
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.room.paging)
ksp(libs.androidx.room.compiler)
implementation(libs.dbtools.room)

Expand All @@ -277,6 +278,9 @@ dependencies {
implementation(libs.ktor.client.content.negotiation)
implementation(libs.ktor.client.resources)

// Paging
implementation(libs.androidx.paging.compose)

// Logging
implementation(libs.kermit)
implementation(libs.kermit.crashlytics)
Expand Down
16 changes: 14 additions & 2 deletions app/src/main/kotlin/org/jdc/template/model/db/main/MainDatabase.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package org.jdc.template.model.db.main

import androidx.room.AutoMigration
import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import org.dbtools.android.room.DatabaseViewQuery
import org.jdc.template.model.db.converter.KotlinDateTimeTextConverter
import org.jdc.template.model.db.main.chatmessage.ChatMessageDao
import org.jdc.template.model.db.main.chatmessage.ChatMessageEntity
import org.jdc.template.model.db.main.chatthread.ChatThreadDao
import org.jdc.template.model.db.main.chatthread.ChatThreadEntity
import org.jdc.template.model.db.main.directoryitem.DirectoryItemDao
import org.jdc.template.model.db.main.directoryitem.DirectoryItemEntityView
import org.jdc.template.model.db.main.household.HouseholdDao
Expand All @@ -15,19 +20,26 @@ import org.jdc.template.model.db.main.individual.IndividualEntity
@Database(
entities = [
IndividualEntity::class,
HouseholdEntity::class
HouseholdEntity::class,
ChatThreadEntity::class,
ChatMessageEntity::class
],
views = [
DirectoryItemEntityView::class
],
version = 3
autoMigrations = [
AutoMigration(from = 3, to = 4)
],
version = 4
)
@TypeConverters(KotlinDateTimeTextConverter::class)
abstract class MainDatabase : RoomDatabase() {

abstract fun individualDao(): IndividualDao
abstract fun householdDao(): HouseholdDao
abstract fun directoryItemDao(): DirectoryItemDao
abstract fun chatThreadDao(): ChatThreadDao
abstract fun chatMessageDao(): ChatMessageDao

companion object {
const val DATABASE_NAME = "main.db"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.jdc.template.model.db.main.chatmessage

import androidx.paging.PagingSource
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import org.jdc.template.model.domain.inline.ChatMessageId
import org.jdc.template.model.domain.inline.ChatThreadId

@Dao
interface ChatMessageDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(entity: ChatMessageEntity): Long

@Query("DELETE FROM ChatMessage WHERE id = :id")
suspend fun deleteById(id: ChatMessageId)

@Query("SELECT * FROM ChatMessage WHERE id = :id")
suspend fun findById(id: ChatMessageId): ChatMessageEntity?

@Query("SELECT * FROM ChatMessage WHERE chatThreadId = :chatThreadId ORDER BY createdDate DESC")
fun findAllPaging(chatThreadId: ChatThreadId): PagingSource<Int, ChatMessageEntity>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.jdc.template.model.db.main.chatmessage

import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import org.jdc.template.model.db.main.chatthread.ChatThreadEntity
import org.jdc.template.model.domain.inline.ChatMessageId
import org.jdc.template.model.domain.inline.ChatThreadId
import org.jdc.template.model.domain.inline.IndividualId
import java.util.UUID

@Entity(
tableName = "ChatMessage",
foreignKeys = [
ForeignKey(
entity = ChatThreadEntity::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("chatThreadId"),
deferred = true,
onDelete = ForeignKey.CASCADE
)
]
)
data class ChatMessageEntity(
@PrimaryKey
val id: ChatMessageId = ChatMessageId(UUID.randomUUID().toString()),
val chatThreadId: ChatThreadId,
val individualId: IndividualId,
val message: String,

val createdDate: Instant = Clock.System.now(),
val lastModified: Instant = Clock.System.now(),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.jdc.template.model.db.main.chatthread

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import kotlinx.coroutines.flow.Flow
import org.jdc.template.model.domain.ChatThreadListItem
import org.jdc.template.model.domain.inline.ChatThreadId

@Dao
interface ChatThreadDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(entity: ChatThreadEntity): Long

@Query("SELECT * FROM ChatThread WHERE id = :id")
suspend fun findById(id: ChatThreadId): ChatThreadEntity?

@Query("DELETE FROM ChatThread WHERE id = :id")
suspend fun deleteById(id: ChatThreadId)

@Query("SELECT * FROM ChatThread ORDER BY lastModified DESC")
fun findAllFlow(): Flow<List<ChatThreadEntity>>

@Query("SELECT id, name FROM ChatThread ORDER BY lastModified DESC")
fun findAllChatThreadListItemFlow(): Flow<List<ChatThreadListItem>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.jdc.template.model.db.main.chatthread

import androidx.room.Entity
import androidx.room.PrimaryKey
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import org.jdc.template.model.domain.inline.ChatThreadId
import org.jdc.template.model.domain.inline.IndividualId
import java.util.UUID

@Entity("ChatThread")
data class ChatThreadEntity(
@PrimaryKey
val id: ChatThreadId = ChatThreadId(UUID.randomUUID().toString()),
val name: String,
val ownerIndividualId: IndividualId,

val createdDate: Instant = Clock.System.now(),
val lastModified: Instant = Clock.System.now(),
)
18 changes: 18 additions & 0 deletions app/src/main/kotlin/org/jdc/template/model/domain/ChatMessage.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.jdc.template.model.domain

import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import org.jdc.template.model.domain.inline.ChatMessageId
import org.jdc.template.model.domain.inline.ChatThreadId
import org.jdc.template.model.domain.inline.IndividualId
import java.util.UUID

data class ChatMessage(
val id: ChatMessageId = ChatMessageId(UUID.randomUUID().toString()),
val chatThreadId: ChatThreadId,
val individualId: IndividualId,
val message: String,

val createdDate: Instant = Clock.System.now(),
val lastModified: Instant = Clock.System.now(),
)
10 changes: 10 additions & 0 deletions app/src/main/kotlin/org/jdc/template/model/domain/ChatThread.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.jdc.template.model.domain

import org.jdc.template.model.domain.inline.ChatThreadId
import org.jdc.template.model.domain.inline.IndividualId

data class ChatThread(
val id: ChatThreadId,
val name: String,
val ownerIndividualId: IndividualId,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.jdc.template.model.domain

import org.jdc.template.model.domain.inline.ChatThreadId

data class ChatThreadListItem(
val id: ChatThreadId,
val name: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,14 @@ value class CreatedTime(val value: Instant)

@JvmInline
value class LastModifiedTime(val value: Instant)

@JvmInline
@Serializable
value class ChatThreadId(val value: String)

@JvmInline
value class ChatMessageId(val value: String)

@JvmInline
value class ChatImageId(val value: String)

Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package org.jdc.template.model.repository

import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.map
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.launch
import org.jdc.template.inject.ApplicationScope
import org.jdc.template.inject.IoDispatcher
import org.jdc.template.model.db.main.MainDatabaseWrapper
import org.jdc.template.model.db.main.chatmessage.ChatMessageEntity
import org.jdc.template.model.db.main.chatthread.ChatThreadEntity
import org.jdc.template.model.domain.ChatMessage
import org.jdc.template.model.domain.ChatThread
import org.jdc.template.model.domain.ChatThreadListItem
import org.jdc.template.model.domain.inline.ChatMessageId
import org.jdc.template.model.domain.inline.ChatThreadId
import org.jdc.template.model.domain.inline.IndividualId
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class ChatRepository
@Inject constructor(
private val mainDatabaseWrapper: MainDatabaseWrapper,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
@ApplicationScope private val appScope: CoroutineScope,
) {
private fun mainDatabase() = mainDatabaseWrapper.getDatabase()
private fun chatThreadDao() = mainDatabase().chatThreadDao()
private fun chatMessageDao() = mainDatabase().chatMessageDao()

fun getChatThreadListFlow(): Flow<List<ChatThreadListItem>> = chatThreadDao().findAllChatThreadListItemFlow()

fun getPagingAllMessagesFlow(chatThreadId: ChatThreadId): Flow<PagingData<ChatMessage>> {
val config = PagingConfig(pageSize = 20, initialLoadSize = 20, enablePlaceholders = false)

return Pager(config) {
chatMessageDao().findAllPaging(chatThreadId)
}.flow.mapLatest { page: PagingData<ChatMessageEntity> ->
page.map { entity ->
entity.toChatMessage()
}
}
}

fun sendMessageAsync(chatThreadId: ChatThreadId, individualId: IndividualId, message: String) = appScope.launch(ioDispatcher) {
val newMessage = ChatMessageEntity(
chatThreadId = chatThreadId,
individualId = individualId,
message = message
)

chatMessageDao().insert(newMessage)
}

suspend fun deleteMessage(chatMessageId: ChatMessageId) {
chatMessageDao().deleteById(chatMessageId)
}

suspend fun getChatThreadById(chatThreadId: ChatThreadId): ChatThread? = chatThreadDao().findById(chatThreadId)?.toChatThread()
suspend fun saveNewChatThread(chatThread: ChatThread) {
chatThreadDao().insert(chatThread.toEntity())
}
}

private fun ChatMessageEntity.toChatMessage() = ChatMessage(
id = id,
chatThreadId = chatThreadId,
individualId = individualId,
message = message,
createdDate = createdDate,
lastModified = lastModified
)

private fun ChatThreadEntity.toChatThread() = ChatThread(
id = id,
name = name,
ownerIndividualId = ownerIndividualId
)

private fun ChatThread.toEntity() = ChatThreadEntity(
id = id,
name = name,
ownerIndividualId = ownerIndividualId
)
10 changes: 10 additions & 0 deletions app/src/main/kotlin/org/jdc/template/ui/navigation/NavTypeMaps.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.jdc.template.ui.navigation

import android.os.Bundle
import androidx.navigation.NavType
import org.jdc.template.model.domain.inline.ChatThreadId
import org.jdc.template.model.domain.inline.IndividualId

/**
Expand Down Expand Up @@ -36,4 +37,13 @@ object NavTypeMaps {
return IndividualId(value)
}
}

val ChatThreadIdNavType = object : NavType<ChatThreadId>(
isNullableAllowed = false
) {
override fun put(bundle: Bundle, key: String, value: ChatThreadId) = bundle.putString(key, serializeAsValue(value))
override fun get(bundle: Bundle, key: String): ChatThreadId? = bundle.getString(key)?.let { parseValue(it) }
override fun serializeAsValue(value: ChatThreadId): String = value.value
override fun parseValue(value: String) = ChatThreadId(value)
}
}
Loading

0 comments on commit 180725b

Please sign in to comment.