Commit 65434c1d authored by Leonardo Aramaki's avatar Leonardo Aramaki

Add a preview message to all viewmodels with a summarized content of the...

Add a preview message to all viewmodels with a summarized content of the original message to fit properly in places like replies, quotes or the main chat list
parent 202c69db
...@@ -288,7 +288,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -288,7 +288,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
view.showReplyingAction( view.showReplyingAction(
username = user, username = user,
replyMarkdown = "[ ]($serverUrl/$room/$roomName?msg=$id) $mention ", replyMarkdown = "[ ]($serverUrl/$room/$roomName?msg=$id) $mention ",
quotedMessage = m.message quotedMessage = mapper.map(message).last().preview?.message ?: ""
) )
} }
} }
......
...@@ -12,8 +12,9 @@ data class AudioAttachmentViewModel( ...@@ -12,8 +12,9 @@ data class AudioAttachmentViewModel(
override val attachmentTitle: CharSequence, override val attachmentTitle: CharSequence,
override val id: Long, override val id: Long,
override var reactions: List<ReactionViewModel>, override var reactions: List<ReactionViewModel>,
override var nextDownStreamMessage: BaseViewModel<*>? = null override var nextDownStreamMessage: BaseViewModel<*>? = null,
) : BaseFileAttachmentViewModel<AudioAttachment> { override var preview: Message? = null
) : BaseFileAttachmentViewModel<AudioAttachment> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.AUDIO_ATTACHMENT.viewType get() = BaseViewModel.ViewType.AUDIO_ATTACHMENT.viewType
override val layoutId: Int override val layoutId: Int
......
...@@ -11,6 +11,7 @@ interface BaseViewModel<out T> { ...@@ -11,6 +11,7 @@ interface BaseViewModel<out T> {
val layoutId: Int val layoutId: Int
var reactions: List<ReactionViewModel> var reactions: List<ReactionViewModel>
var nextDownStreamMessage: BaseViewModel<*>? var nextDownStreamMessage: BaseViewModel<*>?
var preview: Message?
enum class ViewType(val viewType: Int) { enum class ViewType(val viewType: Int) {
MESSAGE(0), MESSAGE(0),
......
...@@ -12,7 +12,8 @@ data class ImageAttachmentViewModel( ...@@ -12,7 +12,8 @@ data class ImageAttachmentViewModel(
override val attachmentTitle: CharSequence, override val attachmentTitle: CharSequence,
override val id: Long, override val id: Long,
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
) : BaseFileAttachmentViewModel<ImageAttachment> { ) : BaseFileAttachmentViewModel<ImageAttachment> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.IMAGE_ATTACHMENT.viewType get() = BaseViewModel.ViewType.IMAGE_ATTACHMENT.viewType
......
...@@ -13,7 +13,8 @@ data class MessageAttachmentViewModel( ...@@ -13,7 +13,8 @@ data class MessageAttachmentViewModel(
val isPinned: Boolean, val isPinned: Boolean,
override var reactions: List<ReactionViewModel>, override var reactions: List<ReactionViewModel>,
override var nextDownStreamMessage: BaseViewModel<*>? = null, override var nextDownStreamMessage: BaseViewModel<*>? = null,
var messageLink: String? = null var messageLink: String? = null,
override var preview: Message? = 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
......
...@@ -14,6 +14,7 @@ data class MessageViewModel( ...@@ -14,6 +14,7 @@ data class MessageViewModel(
override val isPinned: Boolean, override val isPinned: Boolean,
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,
var isFirstUnread: Boolean var isFirstUnread: Boolean
) : BaseMessageViewModel<Message> { ) : BaseMessageViewModel<Message> {
override val viewType: Int override val viewType: Int
......
...@@ -13,7 +13,8 @@ data class UrlPreviewViewModel( ...@@ -13,7 +13,8 @@ data class UrlPreviewViewModel(
val description: CharSequence?, val description: CharSequence?,
val thumbUrl: String?, val thumbUrl: String?,
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
) : BaseViewModel<Url> { ) : BaseViewModel<Url> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.URL_PREVIEW.viewType get() = BaseViewModel.ViewType.URL_PREVIEW.viewType
......
...@@ -12,7 +12,8 @@ data class VideoAttachmentViewModel( ...@@ -12,7 +12,8 @@ data class VideoAttachmentViewModel(
override val attachmentTitle: CharSequence, override val attachmentTitle: CharSequence,
override val id: Long, override val id: Long,
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
) : BaseFileAttachmentViewModel<VideoAttachment> { ) : BaseFileAttachmentViewModel<VideoAttachment> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.VIDEO_ATTACHMENT.viewType get() = BaseViewModel.ViewType.VIDEO_ATTACHMENT.viewType
......
...@@ -11,7 +11,10 @@ import chat.rocket.android.R ...@@ -11,7 +11,10 @@ import chat.rocket.android.R
import chat.rocket.android.helper.MessageParser import chat.rocket.android.helper.MessageParser
import chat.rocket.android.helper.UrlHelper import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.baseUrl
import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.widget.emoji.EmojiParser import chat.rocket.android.widget.emoji.EmojiParser
import chat.rocket.core.TokenRepository import chat.rocket.core.TokenRepository
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
...@@ -28,7 +31,6 @@ import javax.inject.Inject ...@@ -28,7 +31,6 @@ import javax.inject.Inject
class ViewModelMapper @Inject constructor(private val context: Context, class ViewModelMapper @Inject constructor(private val context: Context,
private val parser: MessageParser, private val parser: MessageParser,
private val messagesRepository: MessagesRepository,
tokenRepository: TokenRepository, tokenRepository: TokenRepository,
localRepository: LocalRepository, localRepository: LocalRepository,
serverInteractor: GetCurrentServerInteractor, serverInteractor: GetCurrentServerInteractor,
...@@ -67,6 +69,7 @@ class ViewModelMapper @Inject constructor(private val context: Context, ...@@ -67,6 +69,7 @@ class ViewModelMapper @Inject constructor(private val context: Context,
} }
mapMessage(message).let { mapMessage(message).let {
if (list.size > 0) { it.preview = list[0].preview }
list.add(it) list.add(it)
} }
...@@ -87,10 +90,10 @@ class ViewModelMapper @Inject constructor(private val context: Context, ...@@ -87,10 +90,10 @@ class ViewModelMapper @Inject constructor(private val context: Context,
val description = url.meta?.description val description = url.meta?.description
return UrlPreviewViewModel(message, url, message.id, title, hostname, description, thumb, return UrlPreviewViewModel(message, url, message.id, title, hostname, description, thumb,
getReactions(message)) getReactions(message), preview = message.copy(message = url.url))
} }
private fun mapAttachment(message: Message, attachment: Attachment): BaseViewModel<*>? { private suspend fun mapAttachment(message: Message, attachment: Attachment): BaseViewModel<*>? {
return when (attachment) { return when (attachment) {
is FileAttachment -> mapFileAttachment(message, attachment) is FileAttachment -> mapFileAttachment(message, attachment)
is MessageAttachment -> mapMessageAttachment(message, attachment) is MessageAttachment -> mapMessageAttachment(message, attachment)
...@@ -98,19 +101,20 @@ class ViewModelMapper @Inject constructor(private val context: Context, ...@@ -98,19 +101,20 @@ class ViewModelMapper @Inject constructor(private val context: Context,
} }
} }
private fun mapMessageAttachment(message: Message, attachment: MessageAttachment): MessageAttachmentViewModel { private suspend fun mapMessageAttachment(message: Message, attachment: MessageAttachment): MessageAttachmentViewModel {
val attachmentAuthor = attachment.author!! val attachmentAuthor = attachment.author!!
val time = getTime(attachment.timestamp!!) val time = getTime(attachment.timestamp!!)
val attachmentText = when (attachment.attachments.orEmpty().firstOrNull()) { val attachmentText = when (attachment.attachments.orEmpty().firstOrNull()) {
is ImageAttachment -> context.getString(R.string.msg_quote_photo) is ImageAttachment -> context.getString(R.string.msg_preview_photo)
is VideoAttachment -> context.getString(R.string.msg_quote_video) is VideoAttachment -> context.getString(R.string.msg_preview_video)
is AudioAttachment -> context.getString(R.string.msg_quote_audio) is AudioAttachment -> context.getString(R.string.msg_preview_audio)
else -> attachment.text ?: "" else -> attachment.text ?: ""
} }
val content = stripMessageQuotes(message)
return MessageAttachmentViewModel(message = getMessageWithoutQuoteMarkdown(message), 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))
} }
private fun mapFileAttachment(message: Message, attachment: FileAttachment): BaseViewModel<*>? { private fun mapFileAttachment(message: Message, attachment: FileAttachment): BaseViewModel<*>? {
...@@ -119,11 +123,14 @@ class ViewModelMapper @Inject constructor(private val context: Context, ...@@ -119,11 +123,14 @@ class ViewModelMapper @Inject constructor(private val context: Context,
val id = attachmentId(message, attachment) val id = attachmentId(message, attachment)
return when (attachment) { return when (attachment) {
is ImageAttachment -> ImageAttachmentViewModel(message, attachment, message.id, is ImageAttachment -> ImageAttachmentViewModel(message, attachment, message.id,
attachmentUrl, attachmentTitle, id, getReactions(message)) attachmentUrl, attachmentTitle, id, getReactions(message),
preview = message.copy(message = context.getString(R.string.msg_preview_photo)))
is VideoAttachment -> VideoAttachmentViewModel(message, attachment, message.id, is VideoAttachment -> VideoAttachmentViewModel(message, attachment, message.id,
attachmentUrl, attachmentTitle, id, getReactions(message)) attachmentUrl, attachmentTitle, id, getReactions(message),
preview = message.copy(message = context.getString(R.string.msg_preview_video)))
is AudioAttachment -> AudioAttachmentViewModel(message, attachment, message.id, is AudioAttachment -> AudioAttachmentViewModel(message, attachment, message.id,
attachmentUrl, attachmentTitle, id, getReactions(message)) attachmentUrl, attachmentTitle, id, getReactions(message),
preview = message.copy(message = context.getString(R.string.msg_preview_audio)))
else -> null else -> null
} }
} }
...@@ -167,12 +174,20 @@ class ViewModelMapper @Inject constructor(private val context: Context, ...@@ -167,12 +174,20 @@ class ViewModelMapper @Inject constructor(private val context: Context,
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 content = getContent(context, getMessageWithoutQuoteMarkdown(message)) val content = getContent(stripMessageQuotes(message))
MessageViewModel(message = getMessageWithoutQuoteMarkdown(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) isFirstUnread = false, preview = preview)
}
private suspend fun mapMessagePreview(message: Message): Message {
return when (message.isSystemMessage()) {
false -> stripMessageQuotes(message)
true -> message.copy(message = getSystemMessage(message).toString())
}
} }
private fun getReactions(message: Message): List<ReactionViewModel> { private fun getReactions(message: Message): List<ReactionViewModel> {
...@@ -194,7 +209,7 @@ class ViewModelMapper @Inject constructor(private val context: Context, ...@@ -194,7 +209,7 @@ class ViewModelMapper @Inject constructor(private val context: Context,
return reactions ?: emptyList() return reactions ?: emptyList()
} }
private fun getMessageWithoutQuoteMarkdown(message: Message): Message { private suspend fun stripMessageQuotes(message: Message): Message {
val baseUrl = settings.baseUrl() val baseUrl = settings.baseUrl()
return message.copy( return message.copy(
message = message.message.replace("\\[\\s\\]\\($baseUrl.*\\)".toRegex(), "").trim() message = message.message.replace("\\[\\s\\]\\($baseUrl.*\\)".toRegex(), "").trim()
...@@ -225,14 +240,14 @@ class ViewModelMapper @Inject constructor(private val context: Context, ...@@ -225,14 +240,14 @@ class ViewModelMapper @Inject constructor(private val context: Context,
private fun getTime(timestamp: Long) = DateTimeHelper.getTime(DateTimeHelper.getLocalDateTime(timestamp)) private fun getTime(timestamp: Long) = DateTimeHelper.getTime(DateTimeHelper.getLocalDateTime(timestamp))
private suspend fun getContent(context: Context, message: Message): CharSequence { private suspend fun getContent(message: Message): CharSequence {
return when (message.isSystemMessage()) { return when (message.isSystemMessage()) {
true -> getSystemMessage(message, context) true -> getSystemMessage(message)
false -> parser.renderMarkdown(message, currentUsername) false -> parser.renderMarkdown(message, currentUsername)
} }
} }
private fun getSystemMessage(message: Message, context: Context): CharSequence { private fun getSystemMessage(message: Message): CharSequence {
val content = when (message.type) { val content = when (message.type) {
//TODO: Add implementation for Welcome type. //TODO: Add implementation for Welcome type.
is MessageType.MessageRemoved -> context.getString(R.string.message_removed) is MessageType.MessageRemoved -> context.getString(R.string.message_removed)
......
package chat.rocket.android.chatrooms.presentation package chat.rocket.android.chatrooms.presentation
import chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.main.presentation.MainNavigator import chat.rocket.android.main.presentation.MainNavigator
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
...@@ -30,6 +31,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -30,6 +31,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
private val getChatRoomsInteractor: GetChatRoomsInteractor, private val getChatRoomsInteractor: GetChatRoomsInteractor,
private val saveChatRoomsInteractor: SaveChatRoomsInteractor, private val saveChatRoomsInteractor: SaveChatRoomsInteractor,
private val refreshSettingsInteractor: RefreshSettingsInteractor, private val refreshSettingsInteractor: RefreshSettingsInteractor,
private val viewModelMapper: ViewModelMapper,
settingsRepository: SettingsRepository, settingsRepository: SettingsRepository,
factory: ConnectionManagerFactory) { factory: ConnectionManagerFactory) {
private val manager: ConnectionManager = factory.create(serverInteractor.get()!!) private val manager: ConnectionManager = factory.create(serverInteractor.get()!!)
...@@ -89,9 +91,9 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -89,9 +91,9 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
val chatRoomsCombined = mutableListOf<ChatRoom>() val chatRoomsCombined = mutableListOf<ChatRoom>()
chatRoomsCombined.addAll(usersToChatRooms(users)) chatRoomsCombined.addAll(usersToChatRooms(users))
chatRoomsCombined.addAll(roomsToChatRooms(rooms)) chatRoomsCombined.addAll(roomsToChatRooms(rooms))
view.updateChatRooms(chatRoomsCombined) view.updateChatRooms(getChatRoomsWithPreviews(chatRoomsCombined.toList()))
} else { } else {
view.updateChatRooms(roomList) view.updateChatRooms(getChatRoomsWithPreviews(roomList))
} }
} catch (ex: RocketChatException) { } catch (ex: RocketChatException) {
Timber.e(ex) Timber.e(ex)
...@@ -125,7 +127,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -125,7 +127,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
val sortedRooms = sortRooms(chatRooms) val sortedRooms = sortRooms(chatRooms)
Timber.d("Loaded rooms: ${sortedRooms.size}") Timber.d("Loaded rooms: ${sortedRooms.size}")
saveChatRoomsInteractor.save(currentServer, sortedRooms) saveChatRoomsInteractor.save(currentServer, sortedRooms)
return sortedRooms return getChatRoomsWithPreviews(sortedRooms)
} }
private fun sortRooms(chatRooms: List<ChatRoom>): List<ChatRoom> { private fun sortRooms(chatRooms: List<ChatRoom>): List<ChatRoom> {
...@@ -136,7 +138,17 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -136,7 +138,17 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
private fun updateRooms() { private fun updateRooms() {
Timber.d("Updating Rooms") Timber.d("Updating Rooms")
launch { launch {
view.updateChatRooms(getChatRoomsInteractor.get(currentServer)) view.updateChatRooms(getChatRoomsWithPreviews(getChatRoomsInteractor.get(currentServer)))
}
}
private suspend fun getChatRoomsWithPreviews(chatRooms: List<ChatRoom>): List<ChatRoom> {
return chatRooms.map {
if (it.lastMessage != null) {
it.copy(lastMessage = viewModelMapper.map(it.lastMessage!!).last().preview)
} else {
it
}
} }
} }
......
...@@ -69,9 +69,9 @@ ...@@ -69,9 +69,9 @@
<string name="msg_new_password">Informe a nova senha</string> <string name="msg_new_password">Informe a nova senha</string>
<string name="msg_confirm_password">Confirme a nova senha</string> <string name="msg_confirm_password">Confirme a nova senha</string>
<string name="msg_unread_messages">Mensagens não lidas</string> <string name="msg_unread_messages">Mensagens não lidas</string>
<string name="msg_quote_video">Vídeo</string> <string name="msg_preview_video">Vídeo</string>
<string name="msg_quote_audio">Audio</string> <string name="msg_preview_audio">Audio</string>
<string name="msg_quote_photo">Foto</string> <string name="msg_preview_photo">Foto</string>
<!-- System messages --> <!-- System messages -->
<string name="message_room_name_changed">Nome da sala alterado para: %1$s por %2$s</string> <string name="message_room_name_changed">Nome da sala alterado para: %1$s por %2$s</string>
......
...@@ -70,9 +70,9 @@ ...@@ -70,9 +70,9 @@
<string name="msg_new_password">Enter New Password</string> <string name="msg_new_password">Enter New Password</string>
<string name="msg_confirm_password">Confirm New Password</string> <string name="msg_confirm_password">Confirm New Password</string>
<string name="msg_unread_messages">Unread messages</string> <string name="msg_unread_messages">Unread messages</string>
<string name="msg_quote_video">Video</string> <string name="msg_preview_video">Video</string>
<string name="msg_quote_audio">Audio</string> <string name="msg_preview_audio">Audio</string>
<string name="msg_quote_photo">Photo</string> <string name="msg_preview_photo">Photo</string>
<!-- System messages --> <!-- System messages -->
<string name="message_room_name_changed">Room name changed to: %1$s by %2$s</string> <string name="message_room_name_changed">Room name changed to: %1$s by %2$s</string>
......
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