Commit a2e5be2e authored by Leonardo Aramaki's avatar Leonardo Aramaki

Add read receipt to messages.

parent 3955c8d3
...@@ -4,6 +4,7 @@ import android.graphics.Color ...@@ -4,6 +4,7 @@ import android.graphics.Color
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
import android.view.View import android.view.View
import androidx.core.view.isVisible import androidx.core.view.isVisible
import chat.rocket.android.R
import chat.rocket.android.chatroom.viewmodel.MessageViewModel import chat.rocket.android.chatroom.viewmodel.MessageViewModel
import chat.rocket.android.util.extensions.setVisible import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.widget.emoji.EmojiReactionListener import chat.rocket.android.widget.emoji.EmojiReactionListener
...@@ -40,6 +41,18 @@ class MessageViewHolder( ...@@ -40,6 +41,18 @@ class MessageViewHolder(
text_edit_indicator.isVisible = !it.isSystemMessage() && it.editedBy != null text_edit_indicator.isVisible = !it.isSystemMessage() && it.editedBy != null
image_star_indicator.isVisible = it.starred?.isNotEmpty() ?: false image_star_indicator.isVisible = it.starred?.isNotEmpty() ?: false
} }
if (data.unread == null) {
read_receipt_view.setVisible(false)
} else {
read_receipt_view.setImageResource(
if (data.unread == true) {
R.drawable.ic_check_unread_24dp
} else {
R.drawable.ic_check_read_24dp
}
)
read_receipt_view.setVisible(true)
}
} }
} }
} }
\ No newline at end of file
...@@ -223,7 +223,8 @@ class ChatRoomPresenter @Inject constructor( ...@@ -223,7 +223,8 @@ class ChatRoomPresenter @Inject constructor(
type = null, type = null,
updatedAt = null, updatedAt = null,
urls = null, urls = null,
isTemporary = true isTemporary = true,
unread = true
) )
try { try {
messagesRepository.save(newMessage) messagesRepository.save(newMessage)
......
...@@ -14,7 +14,8 @@ data class AudioAttachmentViewModel( ...@@ -14,7 +14,8 @@ data class AudioAttachmentViewModel(
override var reactions: List<ReactionViewModel>, override var reactions: List<ReactionViewModel>,
override var nextDownStreamMessage: BaseViewModel<*>? = null, override var nextDownStreamMessage: BaseViewModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false,
override var unread: Boolean? = null
) : BaseFileAttachmentViewModel<AudioAttachment> { ) : BaseFileAttachmentViewModel<AudioAttachment> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.AUDIO_ATTACHMENT.viewType get() = BaseViewModel.ViewType.AUDIO_ATTACHMENT.viewType
......
...@@ -16,7 +16,8 @@ data class AuthorAttachmentViewModel( ...@@ -16,7 +16,8 @@ data class AuthorAttachmentViewModel(
override var reactions: List<ReactionViewModel>, override var reactions: List<ReactionViewModel>,
override var nextDownStreamMessage: BaseViewModel<*>? = null, override var nextDownStreamMessage: BaseViewModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false,
override var unread: Boolean? = null
) : BaseAttachmentViewModel<AuthorAttachment> { ) : BaseAttachmentViewModel<AuthorAttachment> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.AUTHOR_ATTACHMENT.viewType get() = BaseViewModel.ViewType.AUTHOR_ATTACHMENT.viewType
......
...@@ -13,6 +13,7 @@ interface BaseViewModel<out T> { ...@@ -13,6 +13,7 @@ interface BaseViewModel<out T> {
var nextDownStreamMessage: BaseViewModel<*>? var nextDownStreamMessage: BaseViewModel<*>?
var preview: Message? var preview: Message?
var isTemporary: Boolean var isTemporary: Boolean
var unread: Boolean?
enum class ViewType(val viewType: Int) { enum class ViewType(val viewType: Int) {
MESSAGE(0), MESSAGE(0),
......
...@@ -15,7 +15,8 @@ data class ColorAttachmentViewModel( ...@@ -15,7 +15,8 @@ data class ColorAttachmentViewModel(
override var reactions: List<ReactionViewModel>, override var reactions: List<ReactionViewModel>,
override var nextDownStreamMessage: BaseViewModel<*>? = null, override var nextDownStreamMessage: BaseViewModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false,
override var unread: Boolean? = null
) : BaseAttachmentViewModel<ColorAttachment> { ) : BaseAttachmentViewModel<ColorAttachment> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.COLOR_ATTACHMENT.viewType get() = BaseViewModel.ViewType.COLOR_ATTACHMENT.viewType
......
...@@ -15,7 +15,8 @@ data class GenericFileAttachmentViewModel( ...@@ -15,7 +15,8 @@ data class GenericFileAttachmentViewModel(
override var reactions: List<ReactionViewModel>, override var reactions: List<ReactionViewModel>,
override var nextDownStreamMessage: BaseViewModel<*>? = null, override var nextDownStreamMessage: BaseViewModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false,
override var unread: Boolean? = null
) : BaseFileAttachmentViewModel<GenericFileAttachment> { ) : BaseFileAttachmentViewModel<GenericFileAttachment> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.GENERIC_FILE_ATTACHMENT.viewType get() = BaseViewModel.ViewType.GENERIC_FILE_ATTACHMENT.viewType
......
...@@ -14,7 +14,8 @@ data class ImageAttachmentViewModel( ...@@ -14,7 +14,8 @@ data class ImageAttachmentViewModel(
override var reactions: List<ReactionViewModel>, override var reactions: List<ReactionViewModel>,
override var nextDownStreamMessage: BaseViewModel<*>? = null, override var nextDownStreamMessage: BaseViewModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false,
override var unread: Boolean? = null
) : BaseFileAttachmentViewModel<ImageAttachment> { ) : BaseFileAttachmentViewModel<ImageAttachment> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.IMAGE_ATTACHMENT.viewType get() = BaseViewModel.ViewType.IMAGE_ATTACHMENT.viewType
......
...@@ -15,7 +15,8 @@ data class MessageAttachmentViewModel( ...@@ -15,7 +15,8 @@ data class MessageAttachmentViewModel(
override var nextDownStreamMessage: BaseViewModel<*>? = null, override var nextDownStreamMessage: BaseViewModel<*>? = null,
var messageLink: String? = null, var messageLink: String? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false,
override var unread: Boolean? = null
) : BaseViewModel<Message> { ) : BaseViewModel<Message> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.MESSAGE_ATTACHMENT.viewType get() = BaseViewModel.ViewType.MESSAGE_ATTACHMENT.viewType
......
...@@ -11,7 +11,8 @@ data class MessageReplyViewModel( ...@@ -11,7 +11,8 @@ data class MessageReplyViewModel(
override var nextDownStreamMessage: BaseViewModel<*>?, override var nextDownStreamMessage: BaseViewModel<*>?,
override var preview: Message?, override var preview: Message?,
override var isTemporary: Boolean = false, override var isTemporary: Boolean = false,
override val message: Message override val message: Message,
override var unread: Boolean? = null
) : BaseViewModel<MessageReply> { ) : BaseViewModel<MessageReply> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.MESSAGE_REPLY.viewType get() = BaseViewModel.ViewType.MESSAGE_REPLY.viewType
......
...@@ -16,7 +16,8 @@ data class MessageViewModel( ...@@ -16,7 +16,8 @@ data class MessageViewModel(
override var nextDownStreamMessage: BaseViewModel<*>? = null, override var nextDownStreamMessage: BaseViewModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
var isFirstUnread: Boolean, var isFirstUnread: Boolean,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false,
override var unread: Boolean? = null
) : BaseMessageViewModel<Message> { ) : BaseMessageViewModel<Message> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.MESSAGE.viewType get() = BaseViewModel.ViewType.MESSAGE.viewType
......
...@@ -15,7 +15,8 @@ data class UrlPreviewViewModel( ...@@ -15,7 +15,8 @@ data class UrlPreviewViewModel(
override var reactions: List<ReactionViewModel>, override var reactions: List<ReactionViewModel>,
override var nextDownStreamMessage: BaseViewModel<*>? = null, override var nextDownStreamMessage: BaseViewModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false,
override var unread: Boolean? = null
) : BaseViewModel<Url> { ) : BaseViewModel<Url> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.URL_PREVIEW.viewType get() = BaseViewModel.ViewType.URL_PREVIEW.viewType
......
...@@ -14,7 +14,8 @@ data class VideoAttachmentViewModel( ...@@ -14,7 +14,8 @@ data class VideoAttachmentViewModel(
override var reactions: List<ReactionViewModel>, override var reactions: List<ReactionViewModel>,
override var nextDownStreamMessage: BaseViewModel<*>? = null, override var nextDownStreamMessage: BaseViewModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false,
override var unread: Boolean? = null
) : BaseFileAttachmentViewModel<VideoAttachment> { ) : BaseFileAttachmentViewModel<VideoAttachment> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.VIDEO_ATTACHMENT.viewType get() = BaseViewModel.ViewType.VIDEO_ATTACHMENT.viewType
......
...@@ -22,6 +22,8 @@ import chat.rocket.android.server.domain.GetCurrentServerInteractor ...@@ -22,6 +22,8 @@ import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.TokenRepository import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.domain.baseUrl import chat.rocket.android.server.domain.baseUrl
import chat.rocket.android.server.domain.hasShowLastMessage
import chat.rocket.android.server.domain.messageReadReceiptEnabled
import chat.rocket.android.server.domain.useRealName import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.util.extensions.avatarUrl import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.isNotNullNorEmpty import chat.rocket.android.util.extensions.isNotNullNorEmpty
...@@ -232,7 +234,7 @@ class ViewModelMapper @Inject constructor( ...@@ -232,7 +234,7 @@ class ViewModelMapper @Inject constructor(
ColorAttachmentViewModel(attachmentUrl = url, id = id, color = color.color, ColorAttachmentViewModel(attachmentUrl = url, id = id, color = color.color,
text = text, message = message, rawData = attachment, text = text, message = message, rawData = attachment,
messageId = message.id, reactions = getReactions(message), messageId = message.id, reactions = getReactions(message),
preview = message.copy(message = content.message)) preview = message.copy(message = content.message), unread = message.unread)
} }
} }
...@@ -260,7 +262,7 @@ class ViewModelMapper @Inject constructor( ...@@ -260,7 +262,7 @@ class ViewModelMapper @Inject constructor(
AuthorAttachmentViewModel(attachmentUrl = url, id = id, name = authorName, AuthorAttachmentViewModel(attachmentUrl = url, id = id, name = authorName,
icon = authorIcon, fields = fieldsText, message = message, rawData = attachment, icon = authorIcon, fields = fieldsText, message = message, rawData = attachment,
messageId = message.id, reactions = getReactions(message), messageId = message.id, reactions = getReactions(message),
preview = message.copy(message = content.message)) preview = message.copy(message = content.message), unread = message.unread)
} }
} }
...@@ -278,7 +280,7 @@ class ViewModelMapper @Inject constructor( ...@@ -278,7 +280,7 @@ class ViewModelMapper @Inject constructor(
return MessageAttachmentViewModel(message = content, rawData = message, return MessageAttachmentViewModel(message = content, rawData = message,
messageId = message.id, time = time, senderName = attachmentAuthor, messageId = message.id, time = time, senderName = attachmentAuthor,
content = attachmentText, isPinned = message.pinned, reactions = getReactions(message), content = attachmentText, isPinned = message.pinned, reactions = getReactions(message),
preview = message.copy(message = content.message)) preview = message.copy(message = content.message), unread = message.unread)
} }
private fun mapFileAttachment(message: Message, attachment: FileAttachment): BaseViewModel<*>? { private fun mapFileAttachment(message: Message, attachment: FileAttachment): BaseViewModel<*>? {
...@@ -338,17 +340,24 @@ class ViewModelMapper @Inject constructor( ...@@ -338,17 +340,24 @@ class ViewModelMapper @Inject constructor(
} }
private suspend fun mapMessage(message: Message): MessageViewModel = withContext(CommonPool) { private suspend fun mapMessage(message: Message): MessageViewModel = withContext(CommonPool) {
val senderUsername = message.sender?.username
val sender = getSenderName(message) val sender = getSenderName(message)
val time = getTime(message.timestamp) val time = getTime(message.timestamp)
val avatar = getUserAvatar(message) val avatar = getUserAvatar(message)
val preview = mapMessagePreview(message) val preview = mapMessagePreview(message)
val isTemp = message.isTemporary ?: false val isTemp = message.isTemporary ?: false
println("setting MESSAGE_READ_RECEIPT_STORE_USERS set to ${settings.messageReadReceiptEnabled()}")
val unread = if (settings.messageReadReceiptEnabled() && currentUsername == senderUsername) {
message.unread ?: false
} else {
null
}
val content = getContent(stripMessageQuotes(message)) val content = getContent(stripMessageQuotes(message))
MessageViewModel(message = stripMessageQuotes(message), rawData = message, MessageViewModel(message = stripMessageQuotes(message), rawData = message,
messageId = message.id, avatar = avatar!!, time = time, senderName = sender, messageId = message.id, avatar = avatar!!, time = time, senderName = sender,
content = content, isPinned = message.pinned, reactions = getReactions(message), content = content, isPinned = message.pinned, reactions = getReactions(message),
isFirstUnread = false, preview = preview, isTemporary = isTemp) isFirstUnread = false, preview = preview, isTemporary = isTemp, unread = unread)
} }
private fun mapMessagePreview(message: Message): Message { private fun mapMessagePreview(message: Message): Message {
...@@ -411,7 +420,7 @@ class ViewModelMapper @Inject constructor( ...@@ -411,7 +420,7 @@ class ViewModelMapper @Inject constructor(
} }
val username = message.sender?.username ?: "?" val username = message.sender?.username ?: "?"
return baseUrl?.let { return baseUrl.let {
baseUrl.avatarUrl(username) baseUrl.avatarUrl(username)
} }
} }
......
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#1D74F5"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF1D74F5"
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" />
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#999999"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF999999"
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" />
</vector>
...@@ -19,8 +19,8 @@ ...@@ -19,8 +19,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:visibility="gone" android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible"> tools:visibility="visible">
...@@ -62,6 +62,7 @@ ...@@ -62,6 +62,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:orientation="horizontal" android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/layout_avatar" app:layout_constraintStart_toEndOf="@+id/layout_avatar"
app:layout_constraintTop_toBottomOf="@+id/new_messages_notif"> app:layout_constraintTop_toBottomOf="@+id/new_messages_notif">
...@@ -100,6 +101,14 @@ ...@@ -100,6 +101,14 @@
android:src="@drawable/ic_action_message_star_24dp" android:src="@drawable/ic_action_message_star_24dp"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible" /> tools:visibility="visible" />
<ImageView
android:id="@+id/read_receipt_view"
android:layout_width="24dp"
android:layout_height="24dp"
android:visibility="gone"
app:srcCompat="@drawable/ic_check_unread_24dp"
tools:visibility="visible" />
</LinearLayout> </LinearLayout>
<TextView <TextView
...@@ -109,10 +118,10 @@ ...@@ -109,10 +118,10 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="2dp" android:layout_marginBottom="2dp"
android:layout_marginTop="5dp" android:layout_marginTop="5dp"
app:layout_constraintStart_toStartOf="@+id/top_container" android:textDirection="locale"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/top_container"
app:layout_constraintTop_toBottomOf="@+id/top_container" app:layout_constraintTop_toBottomOf="@+id/top_container"
android:textDirection="locale"
tools:text="This is a multiline chat message from Bertie that will take more than just one line of text. I have sure that everything is amazing!" /> tools:text="This is a multiline chat message from Bertie that will take more than just one line of text. I have sure that everything is amazing!" />
<include <include
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment