Commit c8eb71b6 authored by Leonardo Aramaki's avatar Leonardo Aramaki

Implement capability to view reactions

parent c1871a62
package chat.rocket.android.chatroom.adapter
import android.support.v7.widget.GridLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.MenuItem
import android.view.View
......@@ -13,10 +14,10 @@ import ru.whalemare.sheetmenu.extension.inflate
import ru.whalemare.sheetmenu.extension.toList
abstract class BaseViewHolder<T : BaseViewModel<*>>(
itemView: View,
private val listener: ActionsListener
itemView: View,
private val listener: ActionsListener
) : RecyclerView.ViewHolder(itemView),
MenuItem.OnMenuItemClickListener {
MenuItem.OnMenuItemClickListener {
var data: T? = null
init {
......@@ -26,6 +27,20 @@ abstract class BaseViewHolder<T : BaseViewModel<*>>(
fun bind(data: T) {
this.data = data
bindViews(data)
bindReactions()
}
private fun bindReactions() {
data?.let {
if (it.isTailMessage) {
val recyclerView = itemView.findViewById(R.id.recycler_view_reactions) as RecyclerView
val adapter = MessageReactionsAdapter()
val manager = GridLayoutManager(itemView.context, 6)
recyclerView.layoutManager = manager
recyclerView.adapter = adapter
adapter.addReactions(it.reactions)
}
}
}
abstract fun bindViews(data: T)
......
......@@ -61,6 +61,24 @@ class ChatRoomAdapter(
}
override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int) {
if (holder !is MessageViewHolder) {
if (position + 1 < itemCount) {
val messageAbove = dataSet[position + 1]
if (messageAbove.messageId == dataSet[position].messageId) {
dataSet[position].isTailMessage = true
}
}
} else {
if (position == 0) {
dataSet[0].isTailMessage = true
}
else if (position - 1 > 0) {
if (dataSet[position - 1].messageId != dataSet[position].messageId) {
dataSet[position].isTailMessage = true
}
}
}
when (holder) {
is MessageViewHolder -> holder.bind(dataSet[position] as MessageViewModel)
is ImageAttachmentViewHolder -> holder.bind(dataSet[position] as ImageAttachmentViewModel)
......
package chat.rocket.android.chatroom.adapter
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import chat.rocket.android.R
import chat.rocket.android.chatroom.adapter.MessageReactionsAdapter.MessageReactionsViewHolder
import chat.rocket.android.chatroom.viewmodel.ReactionViewModel
import chat.rocket.android.dagger.DaggerLocalComponent
import chat.rocket.android.infrastructure.LocalRepository
import java.util.concurrent.CopyOnWriteArrayList
import javax.inject.Inject
class MessageReactionsAdapter : RecyclerView.Adapter<MessageReactionsViewHolder>() {
private val reactions = CopyOnWriteArrayList<ReactionViewModel>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageReactionsViewHolder {
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.item_reaction, parent, false)
return MessageReactionsViewHolder(view)
}
override fun onBindViewHolder(holder: MessageReactionsViewHolder, position: Int) {
holder.bind(reactions[position])
}
override fun getItemCount() = reactions.size
fun addReactions(reactions: List<ReactionViewModel>) {
this.reactions.clear()
this.reactions.addAllAbsent(reactions)
notifyItemRangeInserted(0, reactions.size)
}
class MessageReactionsViewHolder(view: View) : RecyclerView.ViewHolder(view) {
@Inject lateinit var localRepository: LocalRepository
init {
DaggerLocalComponent.builder()
.context(itemView.context)
.build()
.inject(this)
}
fun bind(reaction: ReactionViewModel) {
with(itemView) {
val emojiTextView = findViewById<TextView>(R.id.text_emoji)
val countTextView = findViewById<TextView>(R.id.text_count)
emojiTextView.text = reaction.shortname
countTextView.text = reaction.count.toString()
val myself = localRepository.get(LocalRepository.USERNAME_KEY)
if (reaction.usernames.contains(myself)) {
val context = itemView.context
val resources = context.resources
countTextView.setTextColor(resources.getColor(R.color.colorAccent))
}
}
}
}
}
\ No newline at end of file
......@@ -10,7 +10,9 @@ data class AudioAttachmentViewModel(
override val messageId: String,
override val attachmentUrl: String,
override val attachmentTitle: CharSequence,
override val id: Long
override val id: Long,
override val reactions: List<ReactionViewModel>,
override var isTailMessage: Boolean = false
) : BaseFileAttachmentViewModel<AudioAttachment> {
override val viewType: Int
get() = BaseViewModel.ViewType.AUDIO_ATTACHMENT.viewType
......
......@@ -9,6 +9,8 @@ interface BaseViewModel<out T> {
val messageId: String
val viewType: Int
val layoutId: Int
val reactions: List<ReactionViewModel>
var isTailMessage: Boolean
enum class ViewType(val viewType: Int) {
MESSAGE(0),
......
......@@ -10,7 +10,9 @@ data class ImageAttachmentViewModel(
override val messageId: String,
override val attachmentUrl: String,
override val attachmentTitle: CharSequence,
override val id: Long
override val id: Long,
override val reactions: List<ReactionViewModel>,
override var isTailMessage: Boolean = false
) : BaseFileAttachmentViewModel<ImageAttachment> {
override val viewType: Int
get() = BaseViewModel.ViewType.IMAGE_ATTACHMENT.viewType
......
......@@ -11,7 +11,9 @@ data class MessageViewModel(
override val time: CharSequence,
override val senderName: CharSequence,
override val content: CharSequence,
override val isPinned: Boolean
override val isPinned: Boolean,
override val reactions: List<ReactionViewModel>,
override var isTailMessage: Boolean = false
) : BaseMessageViewModel<Message> {
override val viewType: Int
get() = BaseViewModel.ViewType.MESSAGE.viewType
......
package chat.rocket.android.chatroom.viewmodel
data class ReactionViewModel(
val shortname: CharSequence,
val count: Int,
val usernames: List<String> = emptyList()
)
\ No newline at end of file
......@@ -5,13 +5,15 @@ import chat.rocket.core.model.Message
import chat.rocket.core.model.url.Url
data class UrlPreviewViewModel(
override val message: Message,
override val rawData: Url,
override val messageId: String,
val title: CharSequence?,
val hostname: String,
val description: CharSequence?,
val thumbUrl: String?
override val message: Message,
override val rawData: Url,
override val messageId: String,
val title: CharSequence?,
val hostname: String,
val description: CharSequence?,
val thumbUrl: String?,
override val reactions: List<ReactionViewModel>,
override var isTailMessage: Boolean = false
) : BaseViewModel<Url> {
override val viewType: Int
get() = BaseViewModel.ViewType.URL_PREVIEW.viewType
......
......@@ -10,7 +10,9 @@ data class VideoAttachmentViewModel(
override val messageId: String,
override val attachmentUrl: String,
override val attachmentTitle: CharSequence,
override val id: Long
override val id: Long,
override val reactions: List<ReactionViewModel>,
override var isTailMessage: Boolean = false
) : BaseFileAttachmentViewModel<VideoAttachment> {
override val viewType: Int
get() = BaseViewModel.ViewType.VIDEO_ATTACHMENT.viewType
......
......@@ -14,6 +14,7 @@ import chat.rocket.android.helper.MessageParser
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.*
import chat.rocket.android.widget.emoji.EmojiParser
import chat.rocket.core.TokenRepository
import chat.rocket.core.model.Message
import chat.rocket.core.model.MessageType
......@@ -83,7 +84,8 @@ class ViewModelMapper @Inject constructor(private val context: Context,
val title = url.meta?.title
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))
}
private fun mapAttachment(message: Message, attachment: Attachment): BaseViewModel<*>? {
......@@ -99,11 +101,11 @@ class ViewModelMapper @Inject constructor(private val context: Context,
val id = "${message.id}_${attachment.titleLink}".hashCode().toLong()
return when (attachment) {
is ImageAttachment -> ImageAttachmentViewModel(message, attachment, message.id,
attachmentUrl, attachmentTitle ?: "", id)
attachmentUrl, attachmentTitle ?: "", id, getReactions(message))
is VideoAttachment -> VideoAttachmentViewModel(message, attachment, message.id,
attachmentUrl, attachmentTitle ?: "", id)
attachmentUrl, attachmentTitle ?: "", id, getReactions(message))
is AudioAttachment -> AudioAttachmentViewModel(message, attachment, message.id,
attachmentUrl, attachmentTitle ?: "", id)
attachmentUrl, attachmentTitle ?: "", id, getReactions(message))
else -> null
}
}
......@@ -147,7 +149,22 @@ class ViewModelMapper @Inject constructor(private val context: Context,
val content = getContent(context, message, quote)
MessageViewModel(message = message, rawData = message, messageId = message.id,
avatar = avatar!!, time = time, senderName = sender,
content = content, isPinned = message.pinned)
content = content, isPinned = message.pinned, reactions = getReactions(message))
}
private fun getReactions(message: Message): List<ReactionViewModel> {
val reactions = message.reactions?.let {
val list = mutableListOf<ReactionViewModel>()
it.getShortNames().forEach { shortname ->
val usernames = it.getUsernames(shortname) ?: emptyList()
val count = usernames.size
list.add(
ReactionViewModel(EmojiParser.parse(shortname), count, usernames)
)
}
list
}
return reactions ?: emptyList()
}
private fun getSenderName(message: Message): CharSequence {
......
package chat.rocket.android.dagger
import android.content.Context
import chat.rocket.android.chatroom.adapter.MessageReactionsAdapter
import chat.rocket.android.dagger.module.LocalModule
import dagger.BindsInstance
import dagger.Component
import javax.inject.Singleton
@Singleton
@Component(modules = [LocalModule::class])
interface LocalComponent {
@Component.Builder
interface Builder {
@BindsInstance
fun context(applicationContext: Context): Builder
fun build(): LocalComponent
}
fun inject(adapter: MessageReactionsAdapter.MessageReactionsViewHolder)
/*@Component.Builder
abstract class Builder : AndroidInjector.Builder<RocketChatApplication>()*/
}
package chat.rocket.android.dagger.module
import android.content.Context
import android.content.SharedPreferences
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.SharedPrefsLocalRepository
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
@Module
class LocalModule {
@Provides
fun provideSharedPreferences(context: Context): SharedPreferences {
return context.getSharedPreferences("rocket.chat", Context.MODE_PRIVATE)
}
@Provides
@Singleton
fun provideLocalRepository(prefs: SharedPreferences): LocalRepository {
return SharedPrefsLocalRepository(prefs)
}
}
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="20.0"
android:viewportWidth="20.0">
<path
android:fillColor="#868585"
android:fillType="evenOdd"
android:pathData="M12,8m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0" />
<path
android:fillColor="#868585"
android:fillType="evenOdd"
android:pathData="M8,8m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0" />
<path
android:fillType="evenOdd"
android:pathData="M10,3a7,7 0,1 0,0 14,7 7,0 0,0 7,-7M7.172,12.328a4,4 0,0 0,5.656 0"
android:strokeColor="#868585"
android:strokeWidth="1.5" />
<path
android:fillType="evenOdd"
android:pathData="M16.2,1.2v5.2m-2.6,-2.6h5.2"
android:strokeColor="#868585"
android:strokeLineCap="square"
android:strokeWidth="1.5" />
</vector>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
android:width="24dp"
android:height="24dp" />
<solid android:color="#efeeee" />
<corners android:radius="4dp"/>
</shape>
\ No newline at end of file
......@@ -47,11 +47,18 @@
style="@style/Message.TextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="2dp"
android:layout_marginTop="5dp"
app:layout_constraintLeft_toLeftOf="@id/top_container"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/top_container"
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 layout="@layout/layout_reactions"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="@+id/text_content"
app:layout_constraintStart_toStartOf="@+id/text_content"
app:layout_constraintTop_toBottomOf="@+id/text_content" />
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/rounded_background"
android:orientation="horizontal">
<TextView
android:id="@+id/text_emoji"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:ellipsize="end"
android:paddingLeft="4dp"
android:paddingStart="4dp"
android:textColor="#868585"
android:textSize="14sp"
tools:text=":)" />
<TextView
android:id="@+id/text_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingBottom="4dp"
android:paddingEnd="4dp"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:paddingStart="4dp"
android:paddingTop="4dp"
android:textColor="#868585"
android:textSize="14sp"
android:textStyle="bold"
tools:text="12" />
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recycler_view_reactions"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
\ No newline at end of file
......@@ -5,9 +5,9 @@
android:id="@+id/attachment_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="72dp"
android:orientation="vertical"
android:paddingEnd="@dimen/screen_edge_left_and_right_margins"
android:orientation="vertical">
android:paddingStart="72dp">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_attachment"
......@@ -38,4 +38,9 @@
android:layout_height="wrap_content"
tools:text="Filename.png" />
<include
layout="@layout/layout_reactions"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/url_preview_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="72dp"
android:paddingEnd="24dp">
android:paddingEnd="24dp"
android:paddingStart="72dp">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_preview"
android:layout_width="70dp"
android:layout_height="50dp"
app:actualImageScaleType="centerCrop"/>
app:actualImageScaleType="centerCrop" />
<TextView
android:id="@+id/text_host"
......@@ -21,26 +20,34 @@
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textColor="@color/colorSecondaryText"
tools:text="www.uol.com.br"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/image_preview" />
app:layout_constraintStart_toEndOf="@+id/image_preview"
tools:text="www.uol.com.br" />
<TextView
android:id="@+id/text_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="@color/colorAccent"
tools:text="Web page title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/text_host"
app:layout_constraintTop_toBottomOf="@id/text_host"/>
app:layout_constraintTop_toBottomOf="@id/text_host"
tools:text="Web page title" />
<TextView
android:id="@+id/text_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
tools:text="description"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/text_host"
app:layout_constraintTop_toBottomOf="@id/text_title"/>
app:layout_constraintTop_toBottomOf="@id/text_title"
tools:text="description" />
<include
layout="@layout/layout_reactions"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="@+id/text_description"
app:layout_constraintStart_toStartOf="@+id/text_description"
app:layout_constraintTop_toBottomOf="@+id/text_description" />
</android.support.constraint.ConstraintLayout>
\ 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