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
@Inject
lateinit var serviceDispatchingAndroidInjector: DispatchingAndroidInjector<Service>
companion object {
lateinit var instance: RocketChatApplication
}
override fun onCreate() {
super.onCreate()
......@@ -34,6 +38,7 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
setupFresco()
setupTimber()
instance = this
}
private fun setupFresco() {
......
package chat.rocket.android.chatroom.presentation
import chat.rocket.android.chatroom.viewmodel.MessageViewModelMapper
import chat.rocket.android.core.lifecycle.CancelStrategy
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.util.launchUI
import chat.rocket.common.model.BaseRoom
......@@ -11,6 +13,7 @@ import chat.rocket.core.internal.realtime.unsubscibre
import chat.rocket.core.internal.rest.messages
import chat.rocket.core.internal.rest.sendMessage
import chat.rocket.core.model.Message
import chat.rocket.core.model.Value
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.launch
import timber.log.Timber
......@@ -18,11 +21,17 @@ import javax.inject.Inject
class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
private val strategy: CancelStrategy,
getSettingsInteractor: GetSettingsInteractor,
private val serverInteractor: GetCurrentServerInteractor,
factory: RocketChatClientFactory) {
private val client = factory.create(serverInteractor.get()!!)
private val roomMessages = ArrayList<Message>()
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) {
launchUI(strategy) {
......@@ -32,7 +41,8 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
synchronized(roomMessages) {
roomMessages.addAll(messages)
}
view.showMessages(messages, serverInteractor.get()!!)
val messagesViewModels = MessageViewModelMapper.mapToViewModelList(messages, settings)
view.showMessages(messagesViewModels, serverInteractor.get()!!)
} catch (ex: Exception) {
ex.printStackTrace()
ex.message?.let {
......@@ -97,16 +107,17 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
private fun updateMessage(streamedMessage: Message) {
launchUI(strategy) {
val viewModelStreamedMessage = MessageViewModelMapper.mapToViewModel(streamedMessage, settings)
synchronized(roomMessages) {
val index = roomMessages.indexOfFirst { msg -> msg.id == streamedMessage.id }
if (index != -1) {
Timber.d("Updatind message at $index")
roomMessages[index] = streamedMessage
view.dispatchUpdateMessage(index, streamedMessage)
view.dispatchUpdateMessage(index, viewModelStreamedMessage)
} else {
Timber.d("Adding new message")
roomMessages.add(0, streamedMessage)
view.showNewMessage(streamedMessage)
view.showNewMessage(viewModelStreamedMessage)
}
}
}
......
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.MessageView
import chat.rocket.core.model.Message
interface ChatRoomView : LoadingView, MessageView {
......@@ -12,7 +12,7 @@ interface ChatRoomView : LoadingView, MessageView {
* @param dataSet The data set to show.
* @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.
......@@ -26,14 +26,14 @@ interface ChatRoomView : LoadingView, MessageView {
*
* @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.
*
* @param index The index of the changed message
*/
fun dispatchUpdateMessage(index: Int, message: Message)
fun dispatchUpdateMessage(index: Int, message: MessageViewModel)
fun disableMessageInput()
......
package chat.rocket.android.chatroom.ui
import DateTimeHelper
import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
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.setVisibility
import chat.rocket.android.util.textContent
import chat.rocket.common.util.ifNull
import chat.rocket.core.model.Message
import com.facebook.drawee.view.SimpleDraweeView
import kotlinx.android.synthetic.main.avatar.view.*
import kotlinx.android.synthetic.main.item_message.view.*
class ChatRoomAdapter(private val context: Context,
private val serverUrl: String) : RecyclerView.Adapter<ChatRoomAdapter.ViewHolder>() {
class ChatRoomAdapter(private val serverUrl: String) : RecyclerView.Adapter<ChatRoomAdapter.ViewHolder>() {
init {
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])
......@@ -39,18 +34,18 @@ class ChatRoomAdapter(private val context: Context,
override fun getItemViewType(position: Int): Int = position
fun addDataSet(dataSet: List<Message>) {
fun addDataSet(dataSet: List<MessageViewModel>) {
val previousDataSetSize = this.dataSet.size
this.dataSet.addAll(previousDataSetSize, dataSet)
notifyItemRangeInserted(previousDataSetSize, dataSet.size)
}
fun addItem(message: Message) {
fun addItem(message: MessageViewModel) {
dataSet.add(0, message)
notifyItemInserted(0)
}
fun updateItem(index: Int, message: Message) {
fun updateItem(index: Int, message: MessageViewModel) {
dataSet[index] = message
notifyItemChanged(index)
}
......@@ -59,33 +54,19 @@ class ChatRoomAdapter(private val context: Context,
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)
bindUserName(message, text_user_name)
bindTime(message, text_message_time)
bindContent(message, text_content)
text_user_name.text = message.sender
text_message_time.text = message.time
text_content.text = message.content
}
private fun bindUserAvatar(message: Message, drawee: SimpleDraweeView, imageUnknownAvatar: ImageView) = message.sender?.username.let {
drawee.setImageURI(UrlHelper.getAvatarUrl(serverUrl, it.toString()))
private fun bindUserAvatar(message: MessageViewModel, drawee: SimpleDraweeView, imageUnknownAvatar: ImageView) = message.getAvatarUrl(serverUrl).let {
drawee.setImageURI(it.toString())
}.ifNull {
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
import chat.rocket.android.R
import chat.rocket.android.chatroom.presentation.ChatRoomPresenter
import chat.rocket.android.chatroom.presentation.ChatRoomView
import chat.rocket.android.chatroom.viewmodel.MessageViewModel
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.util.inflate
import chat.rocket.android.util.setVisibility
import chat.rocket.android.util.textContent
import chat.rocket.core.model.Message
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_chat_room.*
import kotlinx.android.synthetic.main.message_composer.*
......@@ -76,10 +76,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView {
super.onDestroyView()
}
override fun showMessages(dataSet: List<Message>, serverUrl: String) {
override fun showMessages(dataSet: List<MessageViewModel>, serverUrl: String) {
activity?.apply {
if (recycler_view.adapter == null) {
adapter = ChatRoomAdapter(this, serverUrl)
adapter = ChatRoomAdapter(serverUrl)
recycler_view.adapter = adapter
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true)
recycler_view.layoutManager = linearLayoutManager
......@@ -103,7 +103,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView {
}
}
override fun showNewMessage(message: Message) {
override fun showNewMessage(message: MessageViewModel) {
text_message.textContent = ""
adapter.addItem(message)
recycler_view.smoothScrollToPosition(0)
......@@ -120,7 +120,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView {
if (clear) text_message.textContent = ""
}
override fun dispatchUpdateMessage(index: Int, message: Message) {
override fun dispatchUpdateMessage(index: Int, message: MessageViewModel) {
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 @@
<string name="msg_you">Você</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>
\ No newline at end of file
......@@ -42,4 +42,13 @@
<string name="msg_you">You</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>
\ 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