Skip to content

Commit

Permalink
Almost finished with OfflineFirstChatRepository, but I need to figure…
Browse files Browse the repository at this point in the history
… out a way to get the ChatMessage Models while also keeping the Synchronizer repository agnostic

Signed-off-by: rapterjet2004 <juliuslinus1@gmail.com>
  • Loading branch information
rapterjet2004 committed Jun 13, 2024
1 parent bc8f5e3 commit 59ab096
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,21 @@ import com.nextcloud.talk.chat.data.ChatMessageRepository
import com.nextcloud.talk.chat.data.ChatRepository
import com.nextcloud.talk.chat.data.model.ChatMessageModel
import com.nextcloud.talk.data.database.dao.ChatMessagesDao
import com.nextcloud.talk.data.database.mappers.asEntity
import com.nextcloud.talk.data.database.mappers.asModel
import com.nextcloud.talk.data.database.model.ChatMessageEntity
import com.nextcloud.talk.data.sync.Synchronizer
import com.nextcloud.talk.data.sync.changeListSync
import com.nextcloud.talk.models.json.chat.ChatMessage
import com.nextcloud.talk.utils.preferences.AppPreferences
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject

class OfflineFirstChatRepository @Inject constructor(
private val chatDao: ChatMessagesDao,
private val chatRepository: ChatRepository
// TODO implement and inject datastore
private val network: ChatRepository,
private val datastore: AppPreferences
) : ChatMessageRepository {
override fun getMessages(id: Long): Flow<List<ChatMessageModel>> =
chatDao.getMessagesForConversation(id).map {
Expand All @@ -32,22 +35,27 @@ class OfflineFirstChatRepository @Inject constructor(
override fun getMessage(id: Long): Flow<ChatMessageModel> =
chatDao.getChatMessage(id).map(ChatMessageEntity::asModel)

override suspend fun syncWith(synchronizer: Synchronizer): Boolean =
suspend fun getMessagesFromServer(lastReadId: Int): List<ChatMessage> {
val credentials = "" // TODO
val url = "" // TODO
// TODO figure out how to get this
return listOf()
}

override suspend fun syncWith(syncId: Long, synchronizer: Synchronizer): Boolean =
synchronizer.changeListSync(
versionReader = {
// TODO get the lastReadChatMessageId from the datastore conversationId->lastReadId pairing
return@changeListSync 0L
},
modelFetcher = { lastReadId ->
// TODO pull messages from the server using the last id
return@changeListSync listOf()
modelFetcher = {
val lastReadId = datastore.getLastReadId(syncId, 0)
return@changeListSync getMessagesFromServer(lastReadId)
},
versionUpdater = { newLastReadId ->
// TODO update the datastore conversationId->lastReadId pairing
datastore.saveLastReadId(syncId, newLastReadId)
},
modelDeleter = chatDao::deleteChatMessages,
modelUpdater = { changedIds ->
// TODO chatDao.upsert(json models mapped -> entity)
modelUpdater = { model ->
chatDao.upsertChatMessages(
model.filterIsInstance<ChatMessage>().map { it.asEntity() }
)
}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface SyncableModel {
/**
* Model identifier.
*/
var changedId: Long
var changedId: Int

/**
* Model deletion checker.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ interface ChatMessagesDao {
WHERE id in (:messageIds)
"""
)
fun deleteChatMessages(messageIds: List<Long>)
fun deleteChatMessages(messageIds: List<Int>)

@Update
fun updateChatMessage(message: ChatMessageEntity)
Expand Down
34 changes: 15 additions & 19 deletions app/src/main/java/com/nextcloud/talk/data/sync/SyncUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,12 @@ import kotlin.coroutines.cancellation.CancellationException
*/
interface Synchronizer {

/**
* Updates the version to be synced.
*/
suspend fun updateVersion(update: (Long) -> Unit)
// TODO include any other helper functions here that the Synchronizer needs

/**
* Syntactic sugar to call [Syncable.syncWith] while omitting the synchronizer argument
*/
suspend fun Syncable.sync() = this@sync.syncWith(this@Synchronizer)
suspend fun Syncable.sync(syncId: Long) = this@sync.syncWith(syncId, this@Synchronizer)
}

/**
Expand All @@ -35,9 +32,12 @@ interface Synchronizer {
interface Syncable {
/**
* Synchronizes the local database backing the repository with the network.
* Takes in a [syncId], usually a model identifier like conversationId, that is needed
* to retrieve other metadata from dataStore
*
* Returns if the sync was successful or not.
*/
suspend fun syncWith(synchronizer: Synchronizer): Boolean
suspend fun syncWith(syncId: Long, synchronizer: Synchronizer): Boolean
}

/**
Expand All @@ -60,7 +60,6 @@ private suspend fun <T> suspendRunCatching(block: suspend () -> T): Result<T> =

/**
* Utility function for syncing a repository with the network.
* [versionReader] Gets the current version of the model that needs to be synced
* [modelFetcher] Fetches the change list for the model
* [versionUpdater] Updates the version after a successful sync
* [modelDeleter] Deletes models by consuming the ids of the models that have been deleted.
Expand All @@ -70,15 +69,13 @@ private suspend fun <T> suspendRunCatching(block: suspend () -> T): Result<T> =
* implementation must guarantee this.
*/
suspend fun Synchronizer.changeListSync(
versionReader: () -> Long,
modelFetcher: suspend (Long) -> List<SyncableModel>,
versionUpdater: (Long) -> Unit,
modelDeleter: suspend (List<Long>) -> Unit,
modelUpdater: suspend (List<Long>) -> Unit
modelFetcher: suspend () -> List<SyncableModel>,
versionUpdater: (Int) -> Unit,
modelDeleter: suspend (List<Int>) -> Unit,
modelUpdater: suspend (List<SyncableModel>) -> Unit
) = suspendRunCatching {
// Fetch the change list since last sync (akin to a git fetch)
val currentVersion = versionReader()
val changeList = modelFetcher(currentVersion)
val changeList = modelFetcher()
if (changeList.isEmpty()) return@suspendRunCatching true

// Splits the models marked for deletion from the ones that are updated or new
Expand All @@ -87,12 +84,11 @@ suspend fun Synchronizer.changeListSync(
// Delete models that have been deleted server-side
modelDeleter(deleted.map(SyncableModel::changedId))

// Using the change list, pull down and upsert the changes (akin to a git pull)
modelUpdater(updated.map(SyncableModel::changedId))
// Using the fetch list, pull down and upsert the changes (akin to a git pull)
modelUpdater(updated)

// Update the last synced version (akin to updating local git HEAD)
val latestVersion = changeList.last().changedId
updateVersion {
versionUpdater(latestVersion)
}

versionUpdater(latestVersion)
}.isSuccess
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import com.bluelinelabs.logansquare.annotation.JsonIgnore
import com.bluelinelabs.logansquare.annotation.JsonObject
import com.nextcloud.talk.R
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
import com.nextcloud.talk.data.changeListVersion.SyncableModel
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.json.chat.ChatUtils.Companion.getParsedMessage
import com.nextcloud.talk.models.json.converters.EnumSystemMessageTypeConverter
Expand All @@ -28,9 +29,6 @@ import kotlinx.parcelize.Parcelize
import java.security.MessageDigest
import java.util.Date

// TODO refactor this to become ChatMessageJson. I want ChatMessageJson to become ChatMessage.
// i did it in reverse -_-

@Parcelize
@JsonObject
data class ChatMessage(
Expand Down Expand Up @@ -146,9 +144,13 @@ data class ChatMessage(

var hiddenByCollapse: Boolean = false,

var openWhenDownloaded: Boolean = true
var openWhenDownloaded: Boolean = true,

override var changedId: Int = jsonMessageId,

override var markedForDeletion: Boolean = "comment_deleted" == messageType

) : Parcelable, MessageContentType, MessageContentType.Image {
) : Parcelable, MessageContentType, MessageContentType.Image, SyncableModel {

var extractedUrlToPreview: String? = null

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,5 +164,9 @@ public interface AppPreferences {

Float[] getWaveFormFromFile(String filename);

void saveLastReadId(long conversationId, int lastReadId);

int getLastReadId(long conversationId, int defaultValue);

void clear();
}
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,19 @@ class AppPreferencesImpl(val context: Context) : AppPreferences {
return if (string.isNotEmpty()) string.convertStringToArray() else floatArrayOf().toTypedArray()
}

override fun saveLastReadId(conversationId: Long, lastReadId: Int) {
runBlocking<Unit> {
async {
writeString(conversationId.toString(), lastReadId.toString())
}
}
}

override fun getLastReadId(conversationId: Long, defaultValue: Int): Int {
val lastReadId = runBlocking { async { readString(conversationId.toString()).first() } }.getCompleted()
return if (lastReadId.isNotEmpty()) lastReadId.toInt() else defaultValue
}

override fun clear() {}

private suspend fun writeString(key: String, value: String) =
Expand Down

0 comments on commit 59ab096

Please sign in to comment.