Unverified Commit f09e71c4 authored by Lucio Maciel's avatar Lucio Maciel Committed by GitHub

Merge pull request #711 from RocketChat/system-messages-map

[NEW] Render system messages
parents b0d03bea fb715293
...@@ -25,6 +25,10 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -25,6 +25,10 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
@Inject @Inject
lateinit var serviceDispatchingAndroidInjector: DispatchingAndroidInjector<Service> lateinit var serviceDispatchingAndroidInjector: DispatchingAndroidInjector<Service>
companion object {
lateinit var instance: RocketChatApplication
}
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
...@@ -34,6 +38,7 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -34,6 +38,7 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
setupFresco() setupFresco()
setupTimber() setupTimber()
instance = this
} }
private fun setupFresco() { private fun setupFresco() {
......
package chat.rocket.android.chatroom.presentation package chat.rocket.android.chatroom.presentation
import chat.rocket.android.chatroom.viewmodel.MessageViewModelMapper
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.launchUI import chat.rocket.android.util.launchUI
import chat.rocket.common.model.BaseRoom import chat.rocket.common.model.BaseRoom
...@@ -11,6 +13,7 @@ import chat.rocket.core.internal.realtime.unsubscibre ...@@ -11,6 +13,7 @@ import chat.rocket.core.internal.realtime.unsubscibre
import chat.rocket.core.internal.rest.messages import chat.rocket.core.internal.rest.messages
import chat.rocket.core.internal.rest.sendMessage import chat.rocket.core.internal.rest.sendMessage
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import chat.rocket.core.model.Value
import kotlinx.coroutines.experimental.CommonPool import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.launch import kotlinx.coroutines.experimental.launch
import timber.log.Timber import timber.log.Timber
...@@ -18,11 +21,17 @@ import javax.inject.Inject ...@@ -18,11 +21,17 @@ import javax.inject.Inject
class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
getSettingsInteractor: GetSettingsInteractor,
private val serverInteractor: GetCurrentServerInteractor, private val serverInteractor: GetCurrentServerInteractor,
factory: RocketChatClientFactory) { factory: RocketChatClientFactory) {
private val client = factory.create(serverInteractor.get()!!) private val client = factory.create(serverInteractor.get()!!)
private val roomMessages = ArrayList<Message>() private val roomMessages = ArrayList<Message>()
private var subId: String? = null private var subId: String? = null
private var settings: Map<String, Value<Any>>? = null
init {
settings = getSettingsInteractor.get(serverInteractor.get()!!)
}
fun loadMessages(chatRoomId: String, chatRoomType: String, offset: Int = 0) { fun loadMessages(chatRoomId: String, chatRoomType: String, offset: Int = 0) {
launchUI(strategy) { launchUI(strategy) {
...@@ -32,7 +41,8 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -32,7 +41,8 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
synchronized(roomMessages) { synchronized(roomMessages) {
roomMessages.addAll(messages) roomMessages.addAll(messages)
} }
view.showMessages(messages, serverInteractor.get()!!) val messagesViewModels = MessageViewModelMapper.mapToViewModelList(messages, settings)
view.showMessages(messagesViewModels, serverInteractor.get()!!)
} catch (ex: Exception) { } catch (ex: Exception) {
ex.printStackTrace() ex.printStackTrace()
ex.message?.let { ex.message?.let {
...@@ -97,16 +107,17 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -97,16 +107,17 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
private fun updateMessage(streamedMessage: Message) { private fun updateMessage(streamedMessage: Message) {
launchUI(strategy) { launchUI(strategy) {
val viewModelStreamedMessage = MessageViewModelMapper.mapToViewModel(streamedMessage, settings)
synchronized(roomMessages) { synchronized(roomMessages) {
val index = roomMessages.indexOfFirst { msg -> msg.id == streamedMessage.id } val index = roomMessages.indexOfFirst { msg -> msg.id == streamedMessage.id }
if (index != -1) { if (index != -1) {
Timber.d("Updatind message at $index") Timber.d("Updatind message at $index")
roomMessages[index] = streamedMessage roomMessages[index] = streamedMessage
view.dispatchUpdateMessage(index, streamedMessage) view.dispatchUpdateMessage(index, viewModelStreamedMessage)
} else { } else {
Timber.d("Adding new message") Timber.d("Adding new message")
roomMessages.add(0, streamedMessage) roomMessages.add(0, streamedMessage)
view.showNewMessage(streamedMessage) view.showNewMessage(viewModelStreamedMessage)
} }
} }
} }
......
package chat.rocket.android.chatroom.presentation package chat.rocket.android.chatroom.presentation
import chat.rocket.android.chatroom.viewmodel.MessageViewModel
import chat.rocket.android.core.behaviours.LoadingView import chat.rocket.android.core.behaviours.LoadingView
import chat.rocket.android.core.behaviours.MessageView import chat.rocket.android.core.behaviours.MessageView
import chat.rocket.core.model.Message
interface ChatRoomView : LoadingView, MessageView { interface ChatRoomView : LoadingView, MessageView {
...@@ -12,7 +12,7 @@ interface ChatRoomView : LoadingView, MessageView { ...@@ -12,7 +12,7 @@ interface ChatRoomView : LoadingView, MessageView {
* @param dataSet The data set to show. * @param dataSet The data set to show.
* @param serverUrl The server URL. * @param serverUrl The server URL.
*/ */
fun showMessages(dataSet: List<Message>, serverUrl: String) fun showMessages(dataSet: List<MessageViewModel>, serverUrl: String)
/** /**
* Send a message to a chat room. * Send a message to a chat room.
...@@ -26,14 +26,14 @@ interface ChatRoomView : LoadingView, MessageView { ...@@ -26,14 +26,14 @@ interface ChatRoomView : LoadingView, MessageView {
* *
* @param message The (recent) message sent to a chat room. * @param message The (recent) message sent to a chat room.
*/ */
fun showNewMessage(message: Message) fun showNewMessage(message: MessageViewModel)
/** /**
* Dispatch a update to the recycler views adapter about a changed message. * Dispatch a update to the recycler views adapter about a changed message.
* *
* @param index The index of the changed message * @param index The index of the changed message
*/ */
fun dispatchUpdateMessage(index: Int, message: Message) fun dispatchUpdateMessage(index: Int, message: MessageViewModel)
fun disableMessageInput() fun disableMessageInput()
......
package chat.rocket.android.chatroom.ui package chat.rocket.android.chatroom.ui
import DateTimeHelper
import android.content.Context
import android.support.v7.widget.RecyclerView import android.support.v7.widget.RecyclerView
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.helper.UrlHelper import chat.rocket.android.chatroom.viewmodel.MessageViewModel
import chat.rocket.android.util.inflate import chat.rocket.android.util.inflate
import chat.rocket.android.util.setVisibility import chat.rocket.android.util.setVisibility
import chat.rocket.android.util.textContent
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import chat.rocket.core.model.Message
import com.facebook.drawee.view.SimpleDraweeView import com.facebook.drawee.view.SimpleDraweeView
import kotlinx.android.synthetic.main.avatar.view.* import kotlinx.android.synthetic.main.avatar.view.*
import kotlinx.android.synthetic.main.item_message.view.* import kotlinx.android.synthetic.main.item_message.view.*
class ChatRoomAdapter(private val context: Context, class ChatRoomAdapter(private val serverUrl: String) : RecyclerView.Adapter<ChatRoomAdapter.ViewHolder>() {
private val serverUrl: String) : RecyclerView.Adapter<ChatRoomAdapter.ViewHolder>() {
init { init {
setHasStableIds(true) setHasStableIds(true)
} }
val dataSet = ArrayList<Message>() val dataSet = ArrayList<MessageViewModel>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(parent.inflate(R.layout.item_message)) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =
ViewHolder(parent.inflate(R.layout.item_message), serverUrl)
override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(dataSet[position]) override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(dataSet[position])
...@@ -39,18 +34,18 @@ class ChatRoomAdapter(private val context: Context, ...@@ -39,18 +34,18 @@ class ChatRoomAdapter(private val context: Context,
override fun getItemViewType(position: Int): Int = position override fun getItemViewType(position: Int): Int = position
fun addDataSet(dataSet: List<Message>) { fun addDataSet(dataSet: List<MessageViewModel>) {
val previousDataSetSize = this.dataSet.size val previousDataSetSize = this.dataSet.size
this.dataSet.addAll(previousDataSetSize, dataSet) this.dataSet.addAll(previousDataSetSize, dataSet)
notifyItemRangeInserted(previousDataSetSize, dataSet.size) notifyItemRangeInserted(previousDataSetSize, dataSet.size)
} }
fun addItem(message: Message) { fun addItem(message: MessageViewModel) {
dataSet.add(0, message) dataSet.add(0, message)
notifyItemInserted(0) notifyItemInserted(0)
} }
fun updateItem(index: Int, message: Message) { fun updateItem(index: Int, message: MessageViewModel) {
dataSet[index] = message dataSet[index] = message
notifyItemChanged(index) notifyItemChanged(index)
} }
...@@ -59,33 +54,19 @@ class ChatRoomAdapter(private val context: Context, ...@@ -59,33 +54,19 @@ class ChatRoomAdapter(private val context: Context,
return dataSet[position].id.hashCode().toLong() return dataSet[position].id.hashCode().toLong()
} }
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { class ViewHolder(itemView: View, val serverUrl: String) : RecyclerView.ViewHolder(itemView) {
fun bind(message: Message) = with(itemView) { fun bind(message: MessageViewModel) = with(itemView) {
bindUserAvatar(message, image_avatar, image_unknown_avatar) bindUserAvatar(message, image_avatar, image_unknown_avatar)
bindUserName(message, text_user_name) text_user_name.text = message.sender
bindTime(message, text_message_time) text_message_time.text = message.time
bindContent(message, text_content) text_content.text = message.content
} }
private fun bindUserAvatar(message: Message, drawee: SimpleDraweeView, imageUnknownAvatar: ImageView) = message.sender?.username.let { private fun bindUserAvatar(message: MessageViewModel, drawee: SimpleDraweeView, imageUnknownAvatar: ImageView) = message.getAvatarUrl(serverUrl).let {
drawee.setImageURI(UrlHelper.getAvatarUrl(serverUrl, it.toString())) drawee.setImageURI(it.toString())
}.ifNull { }.ifNull {
imageUnknownAvatar.setVisibility(true) imageUnknownAvatar.setVisibility(true)
} }
private fun bindUserName(message: Message, textView: TextView) = message.sender?.username.let {
textView.textContent = it.toString()
}.ifNull {
textView.textContent = context.getString(R.string.msg_unknown)
}
private fun bindTime(message: Message, textView: TextView) {
textView.textContent = DateTimeHelper.getTime(DateTimeHelper.getLocalDateTime(message.timestamp))
}
private fun bindContent(message: Message, textView: TextView) {
textView.textContent = message.message
}
} }
} }
\ No newline at end of file
...@@ -12,11 +12,11 @@ import android.widget.Toast ...@@ -12,11 +12,11 @@ import android.widget.Toast
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatroom.presentation.ChatRoomPresenter import chat.rocket.android.chatroom.presentation.ChatRoomPresenter
import chat.rocket.android.chatroom.presentation.ChatRoomView import chat.rocket.android.chatroom.presentation.ChatRoomView
import chat.rocket.android.chatroom.viewmodel.MessageViewModel
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.util.inflate import chat.rocket.android.util.inflate
import chat.rocket.android.util.setVisibility import chat.rocket.android.util.setVisibility
import chat.rocket.android.util.textContent import chat.rocket.android.util.textContent
import chat.rocket.core.model.Message
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_chat_room.* import kotlinx.android.synthetic.main.fragment_chat_room.*
import kotlinx.android.synthetic.main.message_composer.* import kotlinx.android.synthetic.main.message_composer.*
...@@ -76,10 +76,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView { ...@@ -76,10 +76,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView {
super.onDestroyView() super.onDestroyView()
} }
override fun showMessages(dataSet: List<Message>, serverUrl: String) { override fun showMessages(dataSet: List<MessageViewModel>, serverUrl: String) {
activity?.apply { activity?.apply {
if (recycler_view.adapter == null) { if (recycler_view.adapter == null) {
adapter = ChatRoomAdapter(this, serverUrl) adapter = ChatRoomAdapter(serverUrl)
recycler_view.adapter = adapter recycler_view.adapter = adapter
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true) val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true)
recycler_view.layoutManager = linearLayoutManager recycler_view.layoutManager = linearLayoutManager
...@@ -103,7 +103,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView { ...@@ -103,7 +103,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView {
} }
} }
override fun showNewMessage(message: Message) { override fun showNewMessage(message: MessageViewModel) {
text_message.textContent = "" text_message.textContent = ""
adapter.addItem(message) adapter.addItem(message)
recycler_view.smoothScrollToPosition(0) recycler_view.smoothScrollToPosition(0)
...@@ -120,7 +120,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView { ...@@ -120,7 +120,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView {
if (clear) text_message.textContent = "" if (clear) text_message.textContent = ""
} }
override fun dispatchUpdateMessage(index: Int, message: Message) { override fun dispatchUpdateMessage(index: Int, message: MessageViewModel) {
adapter.updateItem(index, message) adapter.updateItem(index, message)
} }
......
package chat.rocket.android.chatroom.viewmodel
import DateTimeHelper
import android.content.Context
import android.graphics.Color
import android.graphics.Typeface
import android.text.SpannableString
import android.text.style.ForegroundColorSpan
import android.text.style.StyleSpan
import chat.rocket.android.R
import chat.rocket.android.app.RocketChatApplication
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.server.domain.USE_REALNAME
import chat.rocket.core.model.Message
import chat.rocket.core.model.MessageType.*
import chat.rocket.core.model.Value
data class MessageViewModel(private val message: Message,
private val settings: Map<String, Value<Any>>?) {
val id: String = message.id
val time: CharSequence
val sender: CharSequence
val content: CharSequence
init {
sender = getSenderName()
content = getContent(RocketChatApplication.instance)
time = getTime()
}
fun getAvatarUrl(serverUrl: String): String? {
return message.sender?.username.let {
return@let UrlHelper.getAvatarUrl(serverUrl, it.toString())
}
}
fun getTime() = DateTimeHelper.getTime(DateTimeHelper.getLocalDateTime(message.timestamp))
fun getSenderName(): CharSequence {
val useRealName = settings?.get(USE_REALNAME)?.value as Boolean
val username = message.sender?.username
val realName = message.sender?.name
val senderName = if (useRealName) realName else username
return if (senderName == null) username.toString() else senderName.toString()
}
fun getContent(context: Context): CharSequence {
val contentMessage: CharSequence
when (message.type) {
//TODO: Add implementation for Welcome type.
MESSAGE_REMOVED -> contentMessage = getSystemMessage(context.getString(R.string.message_removed))
USER_JOINED -> contentMessage = getSystemMessage(context.getString(R.string.message_user_joined_channel))
USER_LEFT -> contentMessage = getSystemMessage(context.getString(R.string.message_user_left))
USER_ADDED -> contentMessage = getSystemMessage(
context.getString(R.string.message_user_added_by, message.message, message.sender?.username))
ROOM_NAME_CHANGED -> contentMessage = getSystemMessage(
context.getString(R.string.message_room_name_changed, message.message, message.sender?.username))
USER_REMOVED -> contentMessage = getSystemMessage(
context.getString(R.string.message_user_removed_by, message.message, message.sender?.username))
else -> contentMessage = getNormalMessage()
}
return contentMessage
}
private fun getNormalMessage() = message.message
private fun getSystemMessage(content: String): CharSequence {
val spannableMsg = SpannableString(content)
spannableMsg.setSpan(StyleSpan(Typeface.ITALIC), 0, spannableMsg.length,
0)
spannableMsg.setSpan(ForegroundColorSpan(Color.GRAY), 0, spannableMsg.length,
0)
val username = message.sender?.username
val message = message.message
val usernameTextStartIndex = if (username != null) content.indexOf(username) else -1
val usernameTextEndIndex = if (username != null) usernameTextStartIndex + username.length else -1
val messageTextStartIndex = if (message.isNotEmpty()) content.indexOf(message) else -1
val messageTextEndIndex = messageTextStartIndex + message.length
if (usernameTextStartIndex > -1) {
spannableMsg.setSpan(StyleSpan(Typeface.BOLD_ITALIC), usernameTextStartIndex, usernameTextEndIndex,
0)
}
if (messageTextStartIndex > -1) {
spannableMsg.setSpan(StyleSpan(Typeface.BOLD_ITALIC), messageTextStartIndex, messageTextEndIndex,
0)
}
return spannableMsg
}
}
\ No newline at end of file
package chat.rocket.android.chatroom.viewmodel
import chat.rocket.core.model.Message
import chat.rocket.core.model.Value
object MessageViewModelMapper {
suspend fun mapToViewModel(message: Message, settings: Map<String, Value<Any>>?) = MessageViewModel(message, settings)
suspend fun mapToViewModelList(messageList: List<Message>, settings: Map<String, Value<Any>>?): List<MessageViewModel> {
val vmList = mutableListOf<MessageViewModel>()
for (msg in messageList) {
vmList.add(MessageViewModel(msg, settings))
}
return vmList
}
}
\ No newline at end of file
...@@ -40,4 +40,13 @@ ...@@ -40,4 +40,13 @@
<string name="msg_you">Você</string> <string name="msg_you">Você</string>
<string name="msg_unknown">Desconhecido</string> <string name="msg_unknown">Desconhecido</string>
<!-- System messages -->
<string name="message_room_name_changed">Nome da sala alterado para: %1$s por %2$s</string>
<string name="message_user_added_by">Usuário %1$s adicionado por %2$s</string>
<string name="message_user_removed_by">Usuário %1$s removido por %2$s</string>
<string name="message_user_left">Saiu da sala.</string>
<string name="message_user_joined_channel">Entrou no sala.</string>
<string name="message_welcome">Bem-vindo, %s</string>
<string name="message_removed">Mensagem removida</string>
</resources> </resources>
\ No newline at end of file
...@@ -42,4 +42,13 @@ ...@@ -42,4 +42,13 @@
<string name="msg_you">You</string> <string name="msg_you">You</string>
<string name="msg_unknown">Unknown</string> <string name="msg_unknown">Unknown</string>
<!-- System messages -->
<string name="message_room_name_changed">Room name changed to: %1$s by %2$s</string>
<string name="message_user_added_by">User %1$s added by %2$s</string>
<string name="message_user_removed_by">User %1$s removed by %2$s</string>
<string name="message_user_left">Has left the channel.</string>
<string name="message_user_joined_channel">Has joined the channel.</string>
<string name="message_welcome">Welcome %s</string>
<string name="message_removed">Message removed</string>
</resources> </resources>
\ No newline at end of file
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