diff --git a/app/src/main/java/chat/rocket/android/chatroom/adapter/BaseViewHolder.kt b/app/src/main/java/chat/rocket/android/chatroom/adapter/BaseViewHolder.kt index 68b420e7d5..3af85f18ef 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/adapter/BaseViewHolder.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/adapter/BaseViewHolder.kt @@ -18,13 +18,12 @@ import chat.rocket.android.util.extensions.toList import chat.rocket.core.model.Message import chat.rocket.core.model.isSystemMessage import com.google.android.flexbox.FlexDirection -import com.google.android.flexbox.FlexWrap import com.google.android.flexbox.FlexboxLayoutManager import com.google.android.flexbox.JustifyContent abstract class BaseViewHolder>( itemView: View, - private val listener: ActionsListener, + private val listener: ActionsListener? = null, var reactionListener: EmojiReactionListener? = null ) : RecyclerView.ViewHolder(itemView), MenuItem.OnMenuItemClickListener { @@ -63,7 +62,7 @@ abstract class BaseViewHolder>( } override fun onReactionLongClicked(shortname: String, isCustom: Boolean, url: String?, usernames: List) { - reactionListener?.onReactionLongClicked(shortname, isCustom,url, usernames) + reactionListener?.onReactionLongClicked(shortname, isCustom, url, usernames) } } @@ -119,7 +118,7 @@ abstract class BaseViewHolder>( } internal fun setupActionMenu(view: View) { - if (listener.isActionsEnabled()) { + if (listener?.isActionsEnabled() == true) { view.setOnClickListener(onClickListener) if (view is ViewGroup) { for (child in view.children) { @@ -133,7 +132,7 @@ abstract class BaseViewHolder>( override fun onMenuItemClick(item: MenuItem): Boolean { data?.let { - listener.onActionSelected(item, it.message) + listener?.onActionSelected(item, it.message) } return true } diff --git a/app/src/main/java/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt b/app/src/main/java/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt index d574952c00..049f747974 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt @@ -14,6 +14,7 @@ import chat.rocket.android.chatroom.uimodel.AttachmentUiModel import chat.rocket.android.chatroom.uimodel.BaseUiModel import chat.rocket.android.chatroom.uimodel.MessageReplyUiModel import chat.rocket.android.chatroom.uimodel.MessageUiModel +import chat.rocket.android.chatroom.uimodel.SystemMessageUiModel import chat.rocket.android.chatroom.uimodel.UrlPreviewUiModel import chat.rocket.android.chatroom.uimodel.toViewType import chat.rocket.android.emoji.EmojiReactionListener @@ -24,7 +25,6 @@ import chat.rocket.core.model.attachment.actions.Action import chat.rocket.core.model.attachment.actions.ButtonAction import chat.rocket.core.model.isSystemMessage import timber.log.Timber -import java.security.InvalidParameterException class ChatRoomAdapter( private val roomId: String? = null, @@ -49,6 +49,13 @@ class ChatRoomAdapter( MessageViewHolder( view, actionsListener, + reactionListener + ) { userId -> navigator?.toUserDetails(userId) } + } + BaseUiModel.ViewType.SYSTEM_MESSAGE -> { + val view = parent.inflate(R.layout.item_system_message) + SystemMessageViewHolder( + view, reactionListener, { userId -> navigator?.toUserDetails(userId) }, { @@ -81,9 +88,6 @@ class ChatRoomAdapter( actionSelectListener?.openDirectMessage(roomName, permalink) } } - else -> { - throw InvalidParameterException("TODO - implement for ${viewType.toViewType()}") - } } } @@ -112,13 +116,14 @@ class ChatRoomAdapter( when (holder) { is MessageViewHolder -> holder.bind(dataSet[position] as MessageUiModel) - is UrlPreviewViewHolder -> { + is SystemMessageViewHolder -> + holder.bind(dataSet[position] as SystemMessageUiModel) + is UrlPreviewViewHolder -> holder.bind(dataSet[position] as UrlPreviewUiModel) - } - is MessageReplyViewHolder -> - holder.bind(dataSet[position] as MessageReplyUiModel) is AttachmentViewHolder -> holder.bind(dataSet[position] as AttachmentUiModel) + is MessageReplyViewHolder -> + holder.bind(dataSet[position] as MessageReplyUiModel) } } diff --git a/app/src/main/java/chat/rocket/android/chatroom/adapter/MessageViewHolder.kt b/app/src/main/java/chat/rocket/android/chatroom/adapter/MessageViewHolder.kt index 7773d483d3..22a83afc43 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/adapter/MessageViewHolder.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/adapter/MessageViewHolder.kt @@ -10,7 +10,6 @@ import androidx.core.view.isVisible import chat.rocket.android.R import chat.rocket.android.chatroom.uimodel.MessageUiModel import chat.rocket.android.emoji.EmojiReactionListener -import chat.rocket.core.model.MessageType import chat.rocket.core.model.isSystemMessage import com.bumptech.glide.load.resource.gif.GifDrawable import kotlinx.android.synthetic.main.avatar.view.* @@ -20,8 +19,7 @@ class MessageViewHolder( itemView: View, listener: ActionsListener, reactionListener: EmojiReactionListener? = null, - private val avatarListener: (String) -> Unit, - private val joinVideoCallListener: (View) -> Unit + private val avatarListener: (String) -> Unit ) : BaseViewHolder(itemView, listener, reactionListener), Drawable.Callback { init { @@ -53,9 +51,6 @@ class MessageViewHolder( text_content.text_content.text = data.content - button_join_video_call.isVisible = data.message.type is MessageType.JitsiCallStarted - button_join_video_call.setOnClickListener { joinVideoCallListener(it) } - image_avatar.setImageURI(data.avatar) text_content.setTextColor(if (data.isTemporary) Color.GRAY else Color.BLACK) diff --git a/app/src/main/java/chat/rocket/android/chatroom/adapter/SystemMessageViewHolder.kt b/app/src/main/java/chat/rocket/android/chatroom/adapter/SystemMessageViewHolder.kt new file mode 100644 index 0000000000..aa2d4bdd2b --- /dev/null +++ b/app/src/main/java/chat/rocket/android/chatroom/adapter/SystemMessageViewHolder.kt @@ -0,0 +1,50 @@ +package chat.rocket.android.chatroom.adapter + +import android.graphics.Color +import android.text.method.LinkMovementMethod +import android.view.View +import androidx.core.view.isVisible +import chat.rocket.android.chatroom.uimodel.SystemMessageUiModel +import chat.rocket.android.emoji.EmojiReactionListener +import chat.rocket.core.model.MessageType +import kotlinx.android.synthetic.main.avatar.view.* +import kotlinx.android.synthetic.main.item_system_message.view.* + +class SystemMessageViewHolder( + itemView: View, + reactionListener: EmojiReactionListener? = null, + private val avatarListener: (String) -> Unit, + private val joinVideoCallListener: (View) -> Unit +) : BaseViewHolder(itemView, null, reactionListener) { + + init { + with(itemView) { + setupActionMenu(message_container) + text_content.movementMethod = LinkMovementMethod() + } + } + + override fun bindViews(data: SystemMessageUiModel) { + with(itemView) { + day.text = data.currentDayMarkerText + day_marker_layout.isVisible = data.showDayMarker + + new_messages_notif.isVisible = data.isFirstUnread + + text_sender.text = data.senderName + text_content.text_content.text = data.content + + button_join_video_call.isVisible = data.message.type is MessageType.JitsiCallStarted + button_join_video_call.setOnClickListener { joinVideoCallListener(it) } + + image_avatar.setImageURI(data.avatar) + text_content.setTextColor(if (data.isTemporary) Color.GRAY else Color.BLACK) + + setOnClickListener { + data.message.sender?.id?.let { userId -> + avatarListener(userId) + } + } + } + } +} diff --git a/app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomPresenter.kt b/app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomPresenter.kt index 9d45891e8f..df5aa08653 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomPresenter.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomPresenter.kt @@ -172,7 +172,6 @@ class ChatRoomPresenter @Inject constructor( ) } - /*FIXME: Get chat role can cause unresponsive problems especially on slower connections We are updating the room again after the first step so that initial messages get loaded in and the system appears more responsive. Something should be diff --git a/app/src/main/java/chat/rocket/android/chatroom/uimodel/SystemMessageUiModel.kt b/app/src/main/java/chat/rocket/android/chatroom/uimodel/SystemMessageUiModel.kt new file mode 100644 index 0000000000..9c058fb1c1 --- /dev/null +++ b/app/src/main/java/chat/rocket/android/chatroom/uimodel/SystemMessageUiModel.kt @@ -0,0 +1,31 @@ +package chat.rocket.android.chatroom.uimodel + +import chat.rocket.android.R +import chat.rocket.core.model.Message + +data class SystemMessageUiModel( + override val message: Message, + override val rawData: Message, + override val messageId: String, + override val avatar: String, + override val time: CharSequence, + override val senderName: CharSequence, + override val content: CharSequence, + override val isPinned: Boolean, + override var currentDayMarkerText: String, + override var showDayMarker: Boolean, + override var reactions: List, + override var nextDownStreamMessage: BaseUiModel<*>? = null, + override var preview: Message? = null, + override var unread: Boolean? = null, + var isFirstUnread: Boolean, + override var isTemporary: Boolean = false, + override var menuItemsToHide: MutableList = mutableListOf(), + override var permalink: String, + val subscriptionId: String +) : BaseMessageUiModel { + override val viewType: Int + get() = BaseUiModel.ViewType.SYSTEM_MESSAGE.viewType + override val layoutId: Int + get() = R.layout.item_system_message +} \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/chatroom/uimodel/UiModelMapper.kt b/app/src/main/java/chat/rocket/android/chatroom/uimodel/UiModelMapper.kt index 4a1be35286..39cdaf16d9 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/uimodel/UiModelMapper.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/uimodel/UiModelMapper.kt @@ -140,15 +140,23 @@ class UiModelMapper @Inject constructor( list.addAll(it) } - mapMessage(message, chatRoom).let { - if (list.isNotEmpty()) { - it.preview = list.first().preview + if (message.isSystemMessage()) { + mapSystemMessage(message, chatRoom).let { + if (list.isNotEmpty()) { + it.preview = list.first().preview + } + list.add(it) + } + } else { + mapMessage(message, chatRoom).let { + if (list.isNotEmpty()) { + it.preview = list.first().preview + } + list.add(it) } - list.add(it) } } - for (i in list.size - 1 downTo 0) { val next = if (i - 1 < 0) null else list[i - 1] list[i].nextDownStreamMessage = next @@ -217,11 +225,20 @@ class UiModelMapper @Inject constructor( val list = ArrayList>() getChatRoomAsync(message.roomId)?.let { chatRoom -> - mapMessage(message, chatRoom).let { - if (list.isNotEmpty()) { - it.preview = list.first().preview + if (message.isSystemMessage()) { + mapSystemMessage(message, chatRoom).let { + if (list.isNotEmpty()) { + it.preview = list.first().preview + } + list.add(it) + } + } else { + mapMessage(message, chatRoom).let { + if (list.isNotEmpty()) { + it.preview = list.first().preview + } + list.add(it) } - list.add(it) } message.attachments?.forEach { @@ -268,6 +285,34 @@ class UiModelMapper @Inject constructor( senderUsername != currentUsername } + private suspend fun mapSystemMessage( + message: Message, + chatRoom: ChatRoom + ): SystemMessageUiModel = withContext(Dispatchers.IO) { + val sender = getSenderName(message) + val time = getTime(message.timestamp) + val avatar = getUserAvatar(message) + val preview = mapMessagePreview(message) + val synced = message.synced + val unread = if (settings.messageReadReceiptEnabled()) { + message.unread ?: false + } else { + null + } + + val localDateTime = DateTimeHelper.getLocalDateTime(message.timestamp) + val dayMarkerText = DateTimeHelper.getFormattedDateForMessages(localDateTime, context) + val permalink = messageHelper.createPermalink(message, chatRoom, false) + + val content = getContent(stripMessageQuotes(message)) + SystemMessageUiModel(message = stripMessageQuotes(message), rawData = message, + messageId = message.id, avatar = avatar!!, time = time, senderName = sender, + content = content, isPinned = message.pinned, currentDayMarkerText = dayMarkerText, + showDayMarker = false, reactions = getReactions(message), isFirstUnread = false, + preview = preview, isTemporary = !synced, unread = unread, permalink = permalink, + subscriptionId = chatRoom.subscriptionId) + } + private fun mapMessageReply(message: Message, chatRoom: ChatRoom): MessageReplyUiModel { val name = message.sender?.name val roomName = diff --git a/app/src/main/res/layout/avatar.xml b/app/src/main/res/layout/avatar.xml index 64e38375cc..f400752124 100644 --- a/app/src/main/res/layout/avatar.xml +++ b/app/src/main/res/layout/avatar.xml @@ -1,9 +1,8 @@ - + android:layout_height="match_parent"> - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/avatar_small.xml b/app/src/main/res/layout/avatar_small.xml new file mode 100644 index 0000000000..2b729850de --- /dev/null +++ b/app/src/main/res/layout/avatar_small.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_file_attachment.xml b/app/src/main/res/layout/item_file_attachment.xml index 9c9830aea0..2083e7f134 100644 --- a/app/src/main/res/layout/item_file_attachment.xml +++ b/app/src/main/res/layout/item_file_attachment.xml @@ -12,7 +12,6 @@ android:paddingEnd="@dimen/screen_edge_left_and_right_padding" android:paddingStart="@dimen/screen_edge_left_and_right_padding"> - - + tools:text="UserNameWithVeryLongName" /> -