diff --git a/apps/flipcash/shared/chat/src/main/kotlin/com/flipcash/shared/chat/ChatCoordinator.kt b/apps/flipcash/shared/chat/src/main/kotlin/com/flipcash/shared/chat/ChatCoordinator.kt index e925c4e75..ceeaa1e44 100644 --- a/apps/flipcash/shared/chat/src/main/kotlin/com/flipcash/shared/chat/ChatCoordinator.kt +++ b/apps/flipcash/shared/chat/src/main/kotlin/com/flipcash/shared/chat/ChatCoordinator.kt @@ -246,6 +246,7 @@ class ChatCoordinator @Inject constructor( type = PointerType.READ, userId = selfId, value = messageId, + timestamp = Clock.System.now(), ) memberDataSource.updatePointers(chatId, pointer) _state.update { state -> diff --git a/apps/flipcash/shared/persistence/db/src/main/kotlin/com/flipcash/app/persistence/converters/ChatTypeConverters.kt b/apps/flipcash/shared/persistence/db/src/main/kotlin/com/flipcash/app/persistence/converters/ChatTypeConverters.kt index b55abb5a2..0c20c9a28 100644 --- a/apps/flipcash/shared/persistence/db/src/main/kotlin/com/flipcash/app/persistence/converters/ChatTypeConverters.kt +++ b/apps/flipcash/shared/persistence/db/src/main/kotlin/com/flipcash/app/persistence/converters/ChatTypeConverters.kt @@ -90,6 +90,7 @@ data class MessagePointerSerialized( val type: String, val userIdHex: String, val value: Long, + val timestampEpochSeconds: Long = 0L, ) @Serializable diff --git a/apps/flipcash/shared/persistence/sources/src/main/kotlin/com/flipcash/app/persistence/sources/mapper/chat/ChatEntityMapper.kt b/apps/flipcash/shared/persistence/sources/src/main/kotlin/com/flipcash/app/persistence/sources/mapper/chat/ChatEntityMapper.kt index 3dcbbb5fc..e42d65762 100644 --- a/apps/flipcash/shared/persistence/sources/src/main/kotlin/com/flipcash/app/persistence/sources/mapper/chat/ChatEntityMapper.kt +++ b/apps/flipcash/shared/persistence/sources/src/main/kotlin/com/flipcash/app/persistence/sources/mapper/chat/ChatEntityMapper.kt @@ -211,12 +211,14 @@ private fun MessagePointer.toSerialized(): MessagePointerSerialized = MessagePoi type = type.name, userIdHex = userId.hexEncodedString(), value = value, + timestampEpochSeconds = timestamp.epochSeconds, ) private fun MessagePointerSerialized.toDomain(): MessagePointer = MessagePointer( type = PointerType.entries.firstOrNull { it.name == type } ?: PointerType.UNKNOWN, userId = userIdHex.hexToIdExt(), value = value, + timestamp = Instant.fromEpochSeconds(timestampEpochSeconds), ) private fun UserProfile.toSerialized(): UserProfileSerialized = UserProfileSerialized( diff --git a/definitions/flipcash/protos/src/main/proto/messaging/v1/model.proto b/definitions/flipcash/protos/src/main/proto/messaging/v1/model.proto index e38ebb7e3..f4b53492d 100644 --- a/definitions/flipcash/protos/src/main/proto/messaging/v1/model.proto +++ b/definitions/flipcash/protos/src/main/proto/messaging/v1/model.proto @@ -115,6 +115,9 @@ message Pointer { // Everything at or before this message ID is considered to have the state // inferred by the type of pointer. MessageId value = 3 [(validate.rules).message.required = true]; + + // Timestamp the pointer was last advanced at + google.protobuf.Timestamp ts = 4 [(validate.rules).timestamp.required = true]; } message MessageIdBatch { diff --git a/services/flipcash/src/main/kotlin/com/flipcash/services/internal/network/extensions/ProtobufToLocal.kt b/services/flipcash/src/main/kotlin/com/flipcash/services/internal/network/extensions/ProtobufToLocal.kt index 107742113..0ffa8dbe8 100644 --- a/services/flipcash/src/main/kotlin/com/flipcash/services/internal/network/extensions/ProtobufToLocal.kt +++ b/services/flipcash/src/main/kotlin/com/flipcash/services/internal/network/extensions/ProtobufToLocal.kt @@ -137,6 +137,7 @@ internal fun MessagingModel.Pointer.toPointer(): MessagePointer { type = type.toPointerType(), userId = userId.toId(), value = value.value, + timestamp = Instant.fromEpochSeconds(ts.seconds, ts.nanos), ) } diff --git a/services/flipcash/src/main/kotlin/com/flipcash/services/models/chat/MessagePointer.kt b/services/flipcash/src/main/kotlin/com/flipcash/services/models/chat/MessagePointer.kt index b5e583a0b..bec82fefa 100644 --- a/services/flipcash/src/main/kotlin/com/flipcash/services/models/chat/MessagePointer.kt +++ b/services/flipcash/src/main/kotlin/com/flipcash/services/models/chat/MessagePointer.kt @@ -1,11 +1,13 @@ package com.flipcash.services.models.chat import com.getcode.opencode.model.core.ID +import kotlin.time.Instant data class MessagePointer( val type: PointerType, val userId: ID, val value: Long, + val timestamp: Instant, ) enum class PointerType { diff --git a/services/flipcash/src/test/kotlin/com/flipcash/services/models/chat/DomainModelsTest.kt b/services/flipcash/src/test/kotlin/com/flipcash/services/models/chat/DomainModelsTest.kt index 3e80d6471..e7428e967 100644 --- a/services/flipcash/src/test/kotlin/com/flipcash/services/models/chat/DomainModelsTest.kt +++ b/services/flipcash/src/test/kotlin/com/flipcash/services/models/chat/DomainModelsTest.kt @@ -57,7 +57,7 @@ class DomainModelsTest { ChatMessage(1, null, listOf(MessageContent.Text("hi")), Instant.fromEpochSeconds(0), 1) ), pointerUpdates = listOf( - MessagePointer(PointerType.READ, listOf(1.toByte()), 5) + MessagePointer(PointerType.READ, listOf(1.toByte()), 5, Instant.fromEpochSeconds(0)) ), typingNotifications = listOf( TypingNotification(listOf(1.toByte()), TypingState.STARTED_TYPING) @@ -91,7 +91,7 @@ class DomainModelsTest { val member = ChatMember( userId = listOf(1.toByte()), userProfile = UserProfile("Test", emptyList(), null, null), - pointers = listOf(MessagePointer(PointerType.READ, listOf(1.toByte()), 10)), + pointers = listOf(MessagePointer(PointerType.READ, listOf(1.toByte()), 10, Instant.fromEpochSeconds(0))), ) assertEquals("Test", member.userProfile.displayName) assertEquals(1, member.pointers.size)