Commit deeac58b authored by Leonardo Aramaki's avatar Leonardo Aramaki

Implement reacting to messages

parent 88feb831
......@@ -4,10 +4,13 @@ import android.view.View
import chat.rocket.android.chatroom.viewmodel.AudioAttachmentViewModel
import chat.rocket.android.player.PlayerActivity
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.widget.emoji.EmojiReactionListener
import kotlinx.android.synthetic.main.message_attachment.view.*
class AudioAttachmentViewHolder(itemView: View, listener: ActionsListener)
: BaseViewHolder<AudioAttachmentViewModel>(itemView, listener) {
class AudioAttachmentViewHolder(itemView: View,
listener: ActionsListener,
reactionListener: EmojiReactionListener? = null)
: BaseViewHolder<AudioAttachmentViewModel>(itemView, listener, reactionListener) {
init {
with(itemView) {
......
package chat.rocket.android.chatroom.adapter
import android.content.Context
import android.support.v7.widget.GridLayoutManager
import android.support.v7.widget.RecyclerView
import android.util.DisplayMetrics
import android.view.MenuItem
import android.view.View
import android.view.WindowManager
import chat.rocket.android.R
import chat.rocket.android.chatroom.ui.bottomsheet.BottomSheetMenu
import chat.rocket.android.chatroom.ui.bottomsheet.adapter.ActionListAdapter
import chat.rocket.android.chatroom.viewmodel.BaseViewModel
import chat.rocket.android.widget.emoji.Emoji
import chat.rocket.android.widget.emoji.EmojiReactionListener
import chat.rocket.core.model.Message
import chat.rocket.core.model.isSystemMessage
import com.google.android.flexbox.FlexDirection
......@@ -21,7 +19,8 @@ import ru.whalemare.sheetmenu.extension.toList
abstract class BaseViewHolder<T : BaseViewModel<*>>(
itemView: View,
private val listener: ActionsListener
private val listener: ActionsListener,
var reactionListener: EmojiReactionListener? = null
) : RecyclerView.ViewHolder(itemView),
MenuItem.OnMenuItemClickListener {
var data: T? = null
......@@ -48,6 +47,11 @@ abstract class BaseViewHolder<T : BaseViewModel<*>>(
}
if (it.nextDownStreamMessage == null) {
adapter.listener = object : EmojiReactionListener {
override fun onEmojiReactionAdded(messageId: String, emoji: Emoji) {
reactionListener?.onEmojiReactionAdded(messageId, emoji)
}
}
val context = itemView.context
val manager = FlexboxLayoutManager(context, FlexDirection.ROW)
recyclerView.layoutManager = manager
......
......@@ -7,6 +7,7 @@ import chat.rocket.android.R
import chat.rocket.android.chatroom.presentation.ChatRoomPresenter
import chat.rocket.android.chatroom.viewmodel.*
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.widget.emoji.EmojiReactionListener
import chat.rocket.core.model.Message
import timber.log.Timber
import java.security.InvalidParameterException
......@@ -15,7 +16,8 @@ class ChatRoomAdapter(
private val roomType: String,
private val roomName: String,
private val presenter: ChatRoomPresenter?,
private val enableActions: Boolean = true
private val enableActions: Boolean = true,
private val reactionListener: EmojiReactionListener? = null
) : RecyclerView.Adapter<BaseViewHolder<*>>() {
private val dataSet = ArrayList<BaseViewModel<*>>()
......@@ -28,23 +30,23 @@ class ChatRoomAdapter(
return when (viewType.toViewType()) {
BaseViewModel.ViewType.MESSAGE -> {
val view = parent.inflate(R.layout.item_message)
MessageViewHolder(view, actionsListener)
MessageViewHolder(view, actionsListener, reactionListener)
}
BaseViewModel.ViewType.IMAGE_ATTACHMENT -> {
val view = parent.inflate(R.layout.message_attachment)
ImageAttachmentViewHolder(view, actionsListener)
ImageAttachmentViewHolder(view, actionsListener, reactionListener)
}
BaseViewModel.ViewType.AUDIO_ATTACHMENT -> {
val view = parent.inflate(R.layout.message_attachment)
AudioAttachmentViewHolder(view, actionsListener)
AudioAttachmentViewHolder(view, actionsListener, reactionListener)
}
BaseViewModel.ViewType.VIDEO_ATTACHMENT -> {
val view = parent.inflate(R.layout.message_attachment)
VideoAttachmentViewHolder(view, actionsListener)
VideoAttachmentViewHolder(view, actionsListener, reactionListener)
}
BaseViewModel.ViewType.URL_PREVIEW -> {
val view = parent.inflate(R.layout.message_url_preview)
UrlPreviewViewHolder(view, actionsListener)
UrlPreviewViewHolder(view, actionsListener, reactionListener)
}
else -> {
throw InvalidParameterException("TODO - implement for ${viewType.toViewType()}")
......
......@@ -2,11 +2,14 @@ package chat.rocket.android.chatroom.adapter
import android.view.View
import chat.rocket.android.chatroom.viewmodel.ImageAttachmentViewModel
import chat.rocket.android.widget.emoji.EmojiReactionListener
import com.stfalcon.frescoimageviewer.ImageViewer
import kotlinx.android.synthetic.main.message_attachment.view.*
class ImageAttachmentViewHolder(itemView: View, listener: ActionsListener)
: BaseViewHolder<ImageAttachmentViewModel>(itemView, listener) {
class ImageAttachmentViewHolder(itemView: View,
listener: ActionsListener,
reactionListener: EmojiReactionListener? = null)
: BaseViewHolder<ImageAttachmentViewModel>(itemView, listener, reactionListener) {
init {
with(itemView) {
......
package chat.rocket.android.chatroom.adapter
import android.support.design.widget.TabLayout
import android.support.v4.view.ViewPager
import android.support.v7.widget.RecyclerView
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.PopupWindow
import android.widget.TextView
import chat.rocket.android.R
import chat.rocket.android.chatroom.viewmodel.ReactionViewModel
import chat.rocket.android.dagger.DaggerLocalComponent
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.widget.emoji.*
import chat.rocket.android.widget.emoji.Emoji
import chat.rocket.android.widget.emoji.EmojiKeyboardListenerAdapter
import chat.rocket.android.widget.emoji.EmojiPickerPopup
import chat.rocket.android.widget.emoji.EmojiReactionListener
import java.util.concurrent.CopyOnWriteArrayList
import javax.inject.Inject
......@@ -25,6 +24,7 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>()
}
private val reactions = CopyOnWriteArrayList<ReactionViewModel>()
var listener: EmojiReactionListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
......@@ -32,7 +32,7 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>()
return when (viewType) {
ADD_REACTION_VIEW_TYPE -> {
view = inflater.inflate(R.layout.item_add_reaction, parent, false)
AddReactionViewHolder(view)
AddReactionViewHolder(view, listener)
}
else -> {
view = inflater.inflate(R.layout.item_reaction, parent, false)
......@@ -97,41 +97,17 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>()
}
}
class AddReactionViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private lateinit var viewPager: ViewPager
private lateinit var tabLayout: TabLayout
class AddReactionViewHolder(view: View, val listener: EmojiReactionListener?) : RecyclerView.ViewHolder(view) {
fun bind(messageId: String) {
itemView as ImageView
itemView.setOnClickListener {
val ep = EmojiPickerPopup(itemView.context)
ep.show()
}
}
private fun setupViewPager() {
itemView.context.let {
viewPager.adapter = CategoryPagerAdapter(object : EmojiKeyboardPopup.Listener {
override fun onNonEmojiKeyPressed(keyCode: Int) {
// do nothing
}
val emojiPickerPopup = EmojiPickerPopup(itemView.context)
emojiPickerPopup.listener = object : EmojiKeyboardListenerAdapter() {
override fun onEmojiAdded(emoji: Emoji) {
EmojiRepository.addToRecents(emoji)
listener?.onEmojiReactionAdded(messageId, emoji)
}
})
for (category in EmojiCategory.values()) {
val tab = tabLayout.getTabAt(category.ordinal)
val tabView = LayoutInflater.from(it).inflate(R.layout.emoji_picker_tab, null)
tab?.setCustomView(tabView)
val textView = tabView.findViewById(R.id.image_category) as ImageView
textView.setImageResource(category.resourceIcon())
}
val currentTab = if (EmojiRepository.getRecents().isEmpty()) EmojiCategory.PEOPLE.ordinal else
EmojiCategory.RECENTS.ordinal
viewPager.setCurrentItem(currentTab)
emojiPickerPopup.show()
}
}
}
......
......@@ -3,13 +3,15 @@ package chat.rocket.android.chatroom.adapter
import android.text.method.LinkMovementMethod
import android.view.View
import chat.rocket.android.chatroom.viewmodel.MessageViewModel
import chat.rocket.android.widget.emoji.EmojiReactionListener
import kotlinx.android.synthetic.main.avatar.view.*
import kotlinx.android.synthetic.main.item_message.view.*
class MessageViewHolder(
itemView: View,
listener: ActionsListener
) : BaseViewHolder<MessageViewModel>(itemView, listener) {
listener: ActionsListener,
reactionListener: EmojiReactionListener? = null
) : BaseViewHolder<MessageViewModel>(itemView, listener, reactionListener) {
init {
with(itemView) {
......
......@@ -6,10 +6,13 @@ import android.view.View
import chat.rocket.android.chatroom.viewmodel.UrlPreviewViewModel
import chat.rocket.android.util.extensions.content
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.widget.emoji.EmojiReactionListener
import kotlinx.android.synthetic.main.message_url_preview.view.*
class UrlPreviewViewHolder(itemView: View, listener: ActionsListener)
: BaseViewHolder<UrlPreviewViewModel>(itemView, listener) {
class UrlPreviewViewHolder(itemView: View,
listener: ActionsListener,
reactionListener: EmojiReactionListener? = null)
: BaseViewHolder<UrlPreviewViewModel>(itemView, listener, reactionListener) {
init {
with(itemView) {
......
......@@ -4,10 +4,13 @@ import android.view.View
import chat.rocket.android.chatroom.viewmodel.VideoAttachmentViewModel
import chat.rocket.android.player.PlayerActivity
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.widget.emoji.EmojiReactionListener
import kotlinx.android.synthetic.main.message_attachment.view.*
class VideoAttachmentViewHolder(itemView: View, listener: ActionsListener)
: BaseViewHolder<VideoAttachmentViewModel>(itemView, listener) {
class VideoAttachmentViewHolder(itemView: View,
listener: ActionsListener,
reactionListener: EmojiReactionListener? = null)
: BaseViewHolder<VideoAttachmentViewModel>(itemView, listener, reactionListener) {
init {
with(itemView) {
......
......@@ -348,7 +348,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
fun react(messageId: String, emoji: String) {
launchUI(strategy) {
try {
client.react(messageId, emoji)
client.react(messageId, emoji.removeSurrounding(":"))
} catch (ex: RocketChatException) {
Timber.e(ex)
}
......
......@@ -23,10 +23,7 @@ import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.helper.MessageParser
import chat.rocket.android.util.extensions.*
import chat.rocket.android.widget.emoji.ComposerEditText
import chat.rocket.android.widget.emoji.Emoji
import chat.rocket.android.widget.emoji.EmojiKeyboardPopup
import chat.rocket.android.widget.emoji.EmojiParser
import chat.rocket.android.widget.emoji.*
import chat.rocket.core.internal.realtime.State
import dagger.android.support.AndroidSupportInjection
import io.reactivex.disposables.CompositeDisposable
......@@ -54,7 +51,7 @@ private const val BUNDLE_CHAT_ROOM_TYPE = "chat_room_type"
private const val BUNDLE_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only"
private const val REQUEST_CODE_FOR_PERFORM_SAF = 42
class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiReactionListener {
@Inject lateinit var presenter: ChatRoomPresenter
@Inject lateinit var parser: MessageParser
private lateinit var adapter: ChatRoomAdapter
......@@ -154,7 +151,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
override fun showMessages(dataSet: List<BaseViewModel<*>>) {
activity?.apply {
if (recycler_view.adapter == null) {
adapter = ChatRoomAdapter(chatRoomType, chatRoomName, presenter)
adapter = ChatRoomAdapter(chatRoomType, chatRoomName, presenter,
reactionListener = this@ChatRoomFragment)
recycler_view.adapter = adapter
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true)
linearLayoutManager.stackFromEnd = true
......@@ -281,6 +279,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
}
}
override fun onEmojiReactionAdded(messageId: String, emoji: Emoji) {
presenter.react(messageId, emoji.shortname)
}
private fun setReactionButtonIcon(@DrawableRes drawableId: Int) {
button_add_reaction.setImageResource(drawableId)
button_add_reaction.setTag(drawableId)
......
......@@ -9,10 +9,10 @@ import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import chat.rocket.android.R
import chat.rocket.android.widget.emoji.EmojiKeyboardPopup.Listener
import java.util.*
class CategoryPagerAdapter(val listener: Listener) : PagerAdapter() {
class CategoryPagerAdapter(val listener: EmojiKeyboardListener) : PagerAdapter() {
override fun isViewFromObject(view: View, obj: Any): Boolean {
return view == obj
}
......@@ -46,7 +46,7 @@ class CategoryPagerAdapter(val listener: Listener) : PagerAdapter() {
override fun getPageTitle(position: Int) = EmojiCategory.values()[position].textIcon()
class EmojiAdapter(val spanCount: Int, val listener: Listener) : RecyclerView.Adapter<EmojiRowViewHolder>() {
class EmojiAdapter(val spanCount: Int, val listener: EmojiKeyboardListener) : RecyclerView.Adapter<EmojiRowViewHolder>() {
private var emojis = Collections.emptyList<Emoji>()
fun addEmojis(emojis: List<Emoji>) {
......@@ -66,7 +66,7 @@ class CategoryPagerAdapter(val listener: Listener) : PagerAdapter() {
override fun getItemCount(): Int = emojis.size
}
class EmojiRowViewHolder(itemView: View, val itemCount: Int, val spanCount: Int, val listener: Listener) : RecyclerView.ViewHolder(itemView) {
class EmojiRowViewHolder(itemView: View, val itemCount: Int, val spanCount: Int, val listener: EmojiKeyboardListener) : RecyclerView.ViewHolder(itemView) {
private val emojiView: TextView = itemView.findViewById(R.id.emoji)
fun bind(emoji: Emoji) {
......
package chat.rocket.android.widget.emoji
interface EmojiKeyboardListener {
/**
* When an emoji is selected on the picker.
*
* @param emoji The selected emoji
*/
fun onEmojiAdded(emoji: Emoji)
/**
* When backspace key is clicked.
*
* @param keyCode The key code pressed as defined
*
* @see android.view.KeyEvent
*/
fun onNonEmojiKeyPressed(keyCode: Int)
}
\ No newline at end of file
package chat.rocket.android.widget.emoji
abstract class EmojiKeyboardListenerAdapter : EmojiKeyboardListener {
override fun onEmojiAdded(emoji: Emoji) {
// this space is for rent
}
override fun onNonEmojiKeyPressed(keyCode: Int) {
// this space is for rent
}
}
\ No newline at end of file
......@@ -21,7 +21,7 @@ class EmojiKeyboardPopup(context: Context, view: View) : OverKeyboardPopupWindow
private lateinit var searchView: View
private lateinit var backspaceView: View
private lateinit var parentContainer: ViewGroup
var listener: Listener? = null
var listener: EmojiKeyboardListener? = null
companion object {
const val PREF_EMOJI_RECENTS = "PREF_EMOJI_RECENTS"
......@@ -55,20 +55,17 @@ class EmojiKeyboardPopup(context: Context, view: View) : OverKeyboardPopupWindow
private fun setupViewPager() {
context.let {
val callback = when (it) {
is Listener -> it
is EmojiKeyboardListener -> it
else -> {
val fragments = (it as AppCompatActivity).supportFragmentManager.fragments
if (fragments == null || fragments.size == 0 || !(fragments[0] is Listener)) {
if (fragments == null || fragments.size == 0 || !(fragments[0] is EmojiKeyboardListener)) {
throw IllegalStateException("activity/fragment should implement Listener interface")
}
fragments[0] as Listener
fragments[0] as EmojiKeyboardListener
}
}
viewPager.adapter = CategoryPagerAdapter(object : Listener {
override fun onNonEmojiKeyPressed(keyCode: Int) {
// do nothing
}
viewPager.adapter = CategoryPagerAdapter(object : EmojiKeyboardListenerAdapter() {
override fun onEmojiAdded(emoji: Emoji) {
EmojiRepository.addToRecents(emoji)
callback.onEmojiAdded(emoji)
......@@ -78,14 +75,14 @@ class EmojiKeyboardPopup(context: Context, view: View) : OverKeyboardPopupWindow
for (category in EmojiCategory.values()) {
val tab = tabLayout.getTabAt(category.ordinal)
val tabView = LayoutInflater.from(context).inflate(R.layout.emoji_picker_tab, null)
tab?.setCustomView(tabView)
tab?.customView = tabView
val textView = tabView.findViewById(R.id.image_category) as ImageView
textView.setImageResource(category.resourceIcon())
}
val currentTab = if (EmojiRepository.getRecents().isEmpty()) EmojiCategory.PEOPLE.ordinal else
EmojiCategory.RECENTS.ordinal
viewPager.setCurrentItem(currentTab)
viewPager.currentItem = currentTab
}
}
......@@ -132,22 +129,4 @@ class EmojiKeyboardPopup(context: Context, view: View) : OverKeyboardPopupWindow
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
}
}
interface Listener {
/**
* When an emoji is selected on the picker.
*
* @param emoji The selected emoji
*/
fun onEmojiAdded(emoji: Emoji)
/**
* When backspace key is clicked.
*
* @param keyCode The key code pressed as defined
*
* @see android.view.KeyEvent
*/
fun onNonEmojiKeyPressed(keyCode: Int)
}
}
\ No newline at end of file
......@@ -15,6 +15,7 @@ import chat.rocket.android.R
class EmojiPickerPopup(context: Context) : Dialog(context) {
private lateinit var viewPager: ViewPager
private lateinit var tabLayout: TabLayout
var listener: EmojiKeyboardListener? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
......@@ -25,22 +26,23 @@ class EmojiPickerPopup(context: Context) : Dialog(context) {
tabLayout = findViewById(R.id.tabs)
tabLayout.setupWithViewPager(viewPager)
setupViewPager()
setSize()
}
private fun setSize() {
val lp = WindowManager.LayoutParams()
lp.copyFrom(window.attributes)
val dialogWidth = lp.width
val dialogHeight = lp.height
window.setLayout(dialogWidth, context.resources.getDimensionPixelSize(R.dimen.picker_popup_height))
val dialogHeight = context.resources.getDimensionPixelSize(R.dimen.picker_popup_height)
window.setLayout(dialogWidth, dialogHeight)
}
private fun setupViewPager() {
viewPager.adapter = CategoryPagerAdapter(object : EmojiKeyboardPopup.Listener {
override fun onNonEmojiKeyPressed(keyCode: Int) {
// do nothing
}
viewPager.adapter = CategoryPagerAdapter(object : EmojiKeyboardListenerAdapter() {
override fun onEmojiAdded(emoji: Emoji) {
EmojiRepository.addToRecents(emoji)
dismiss()
listener?.onEmojiAdded(emoji)
}
})
......
package chat.rocket.android.widget.emoji
interface EmojiReactionListener {
/**
* Callback when an emoji is picked in respect to message by the given id.
*
* @param messageId The id of the message being reacted.
* @param emoji The emoji used to react.
*/
fun onEmojiReactionAdded(messageId: String, emoji: Emoji)
}
\ 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