Unverified Commit df93748c authored by Filipe de Lima Brito's avatar Filipe de Lima Brito Committed by GitHub

Merge pull request #776 from filipedelimabrito/fix/message-composer-becomes-empty-with-new-messages

[NEW][FIX] Prevents losing all entered text when a new message arrives on the chat
parents 518c28a9 64388f43
......@@ -37,8 +37,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
private val mapper: MessageViewModelMapper) {
private val client = factory.create(serverInteractor.get()!!)
private var subId: String? = null
private val settings: Map<String, Value<Any>> = getSettingsInteractor.get(serverInteractor.get()!!)!!
private var settings: Map<String, Value<Any>> = getSettingsInteractor.get(serverInteractor.get()!!)!!
private val stateChannel = Channel<State>()
fun loadMessages(chatRoomId: String, chatRoomType: String, offset: Long = 0) {
......@@ -71,23 +70,23 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
fun sendMessage(chatRoomId: String, text: String, messageId: String?) {
launchUI(strategy) {
view.disableMessageInput()
view.disableSendMessageButton()
try {
// ignore message for now, will receive it on the stream
val message = if (messageId == null) {
client.sendMessage(chatRoomId, text)
} else {
client.updateMessage(chatRoomId, messageId, text)
}
// ignore message for now, will receive it on the stream
view.enableMessageInput(clear = true)
view.clearMessageComposition()
} catch (ex: Exception) {
ex.printStackTrace()
ex.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
view.enableMessageInput()
} finally {
view.enableSendMessageButton()
}
}
}
......
......@@ -80,9 +80,21 @@ interface ChatRoomView : LoadingView, MessageView {
*/
fun showEditingAction(roomId: String, messageId: String, text: String)
fun disableMessageInput()
/**
* Disabling the send message button avoids the user tap this button multiple
* times to send a same message.
*/
fun disableSendMessageButton()
fun enableMessageInput(clear: Boolean = false)
/**
* Enables the send message button.
*/
fun enableSendMessageButton()
/**
* Clears the message composition.
*/
fun clearMessageComposition()
fun showInvalidFileSize(fileSize: Int, maxFileSize: Int)
}
\ No newline at end of file
......@@ -21,9 +21,12 @@ import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.helper.MessageParser
import chat.rocket.android.util.extensions.*
import dagger.android.support.AndroidSupportInjection
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.fragment_chat_room.*
import kotlinx.android.synthetic.main.message_attachment_options.*
import kotlinx.android.synthetic.main.message_composer.*
import kotlinx.android.synthetic.main.message_list.*
import timber.log.Timber
import javax.inject.Inject
fun newInstance(chatRoomId: String, chatRoomName: String, chatRoomType: String, isChatRoomReadOnly: Boolean): Fragment {
......@@ -57,12 +60,15 @@ class ChatRoomFragment : Fragment(), ChatRoomView {
private var citation: String? = null
private var editingMessageId: String? = null
private val compositeDisposable = CompositeDisposable()
private var playComposeMessageButtonsAnimation = true
// For reveal and unreveal anim.
private val hypotenuse by lazy { Math.hypot(root_layout.width.toDouble(), root_layout.height.toDouble()).toFloat() }
private val max by lazy { Math.max(layout_message_attachment_options.width.toDouble(), layout_message_attachment_options.height.toDouble()).toFloat() }
private val centerX by lazy { recycler_view.right }
private val centerY by lazy { recycler_view.bottom }
val handler = Handler()
private val handler = Handler()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
......@@ -85,13 +91,17 @@ class ChatRoomFragment : Fragment(), ChatRoomView {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
presenter.loadMessages(chatRoomId, chatRoomType)
setupComposer()
setupRecyclerView()
setupFab()
setupMessageComposer()
setupActionSnackbar()
}
override fun onDestroyView() {
presenter.unsubscribeMessages()
handler.removeCallbacksAndMessages(null)
unsubscribeTextMessage()
super.onDestroyView()
}
......@@ -156,20 +166,23 @@ class ChatRoomFragment : Fragment(), ChatRoomView {
override fun showInvalidFileMessage() = showMessage(getString(R.string.msg_invalid_file))
override fun showNewMessage(message: MessageViewModel) {
text_message.textContent = ""
adapter.addItem(message)
recycler_view.smoothScrollToPosition(0)
recycler_view.scrollToPosition(0)
}
override fun disableMessageInput() {
override fun disableSendMessageButton() {
button_send.isEnabled = false
text_message.isEnabled = false
}
override fun enableMessageInput(clear: Boolean) {
override fun enableSendMessageButton() {
button_send.isEnabled = true
text_message.isEnabled = true
if (clear) text_message.textContent = ""
}
override fun clearMessageComposition() {
citation = null
editingMessageId = null
text_message.textContent = ""
actionSnackbar.dismiss()
}
override fun dispatchUpdateMessage(index: Int, message: MessageViewModel) {
......@@ -227,35 +240,43 @@ class ChatRoomFragment : Fragment(), ChatRoomView {
showMessage(getString(R.string.max_file_size_exceeded, fileSize, maxFileSize))
}
private fun setupComposer() {
private fun setupRecyclerView() {
recycler_view.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
Timber.i("Scrolling vertically: $dy")
if (!recyclerView.canScrollVertically(1)) {
button_fab.hide()
} else {
if (dy > 0 && !button_fab.isVisible()) {
button_fab.show()
} else if (dy < 0 && button_fab.isVisible()) {
button_fab.hide()
}
}
}
})
}
private fun setupFab() {
button_fab.setOnClickListener {
recycler_view.scrollToPosition(0)
button_fab.hide()
}
}
private fun setupMessageComposer() {
if (isChatRoomReadOnly) {
text_room_is_read_only.setVisible(true)
input_container.setVisible(false)
} else {
var playAnimation = true
text_message.asObservable(0)
.subscribe({ t ->
if (t.isNotEmpty() && playAnimation) {
button_show_attachment_options.fadeInOrOut(1F, 0F, 120)
button_send.fadeInOrOut(0F, 1F, 120)
playAnimation = false
}
if (t.isEmpty()) {
button_send.fadeInOrOut(1F, 0F, 120)
button_show_attachment_options.fadeInOrOut(0F, 1F, 120)
playAnimation = true
}
})
subscribeTextMessage()
button_send.setOnClickListener {
var textMessage = citation ?: ""
textMessage += text_message.textContent
sendMessage(textMessage)
clearActionMessage()
var message = citation ?: ""
message += text_message.textContent
sendMessage(message)
}
button_show_attachment_options.setOnClickListener {
if (layout_message_attachment_options.isShown) {
hideAttachmentOptions()
......@@ -264,7 +285,9 @@ class ChatRoomFragment : Fragment(), ChatRoomView {
}
}
view_dim.setOnClickListener { hideAttachmentOptions() }
view_dim.setOnClickListener {
hideAttachmentOptions()
}
button_files.setOnClickListener {
handler.postDelayed({
......@@ -281,15 +304,35 @@ class ChatRoomFragment : Fragment(), ChatRoomView {
private fun setupActionSnackbar() {
actionSnackbar = ActionSnackbar.make(message_list_container, parser = parser)
actionSnackbar.cancelView.setOnClickListener({
clearActionMessage()
clearMessageComposition()
})
}
private fun clearActionMessage() {
citation = null
editingMessageId = null
text_message.text.clear()
actionSnackbar.dismiss()
private fun subscribeTextMessage() {
val disposable = text_message.asObservable(0)
.subscribe({ t -> setupComposeMessageButtons(t) })
compositeDisposable.add(disposable)
}
private fun unsubscribeTextMessage() {
if (!compositeDisposable.isDisposed) {
compositeDisposable.dispose()
}
}
private fun setupComposeMessageButtons(charSequence: CharSequence) {
if (charSequence.isNotEmpty() && playComposeMessageButtonsAnimation) {
button_show_attachment_options.fadeOut(1F, 0F, 120)
button_send.fadeIn(0F, 1F, 120)
playComposeMessageButtonsAnimation = false
}
if (charSequence.isEmpty()) {
button_send.fadeOut(1F, 0F, 120)
button_show_attachment_options.fadeIn(0F, 1F, 120)
playComposeMessageButtonsAnimation = true
}
}
private fun showAttachmentOptions() {
......
package chat.rocket.android.chatrooms.ui
import DateTimeHelper
import DrawableHelper
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.util.extensions.inflate
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.textContent
import chat.rocket.common.model.RoomType
import chat.rocket.core.model.ChatRoom
import com.facebook.drawee.view.SimpleDraweeView
import kotlinx.android.synthetic.main.avatar.view.*
import kotlinx.android.synthetic.main.item_chat.view.*
import kotlinx.android.synthetic.main.unread_messages_badge.view.*
class ChatRoomsAdapter(private val context: Context,
private val listener: (ChatRoom) -> Unit) : RecyclerView.Adapter<ChatRoomsAdapter.ViewHolder>() {
......
......@@ -12,7 +12,7 @@ fun View.rotateBy(value: Float, duration: Long = 200) {
.start()
}
fun View.fadeInOrOut(startValue: Float, finishValue: Float, duration: Long = 200) {
fun View.fadeIn(startValue: Float, finishValue: Float, duration: Long = 200) {
animate()
.alpha(startValue)
.setDuration(duration)
......@@ -24,11 +24,22 @@ fun View.fadeInOrOut(startValue: Float, finishValue: Float, duration: Long = 200
.setInterpolator(AccelerateInterpolator()).start()
}).start()
if (startValue > finishValue) {
setVisible(false)
} else {
setVisible(true)
}
setVisible(true)
}
fun View.fadeOut(startValue: Float, finishValue: Float, duration: Long = 200) {
animate()
.alpha(startValue)
.setDuration(duration)
.setInterpolator(DecelerateInterpolator())
.withEndAction({
animate()
.alpha(finishValue)
.setDuration(duration)
.setInterpolator(AccelerateInterpolator()).start()
}).start()
setVisible(false)
}
fun View.circularRevealOrUnreveal(centerX: Int, centerY: Int, startRadius: Float, endRadius: Float, duration: Long = 600) {
......
......@@ -21,6 +21,10 @@ fun View.setVisible(visible: Boolean) {
}
}
fun View.isVisible(): Boolean {
return visibility == View.VISIBLE
}
fun ViewGroup.inflate(@LayoutRes resource: Int): View = LayoutInflater.from(context).inflate(resource, this, false)
fun AppCompatActivity.addFragment(tag: String, layoutId: Int, newInstance: () -> Fragment) {
......
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF010101"
android:pathData="M20,12l-1.41,-1.41L13,16.17V4h-2v12.17l-5.58,-5.59L4,12l8,8 8,-8z" />
</vector>
\ No newline at end of file
......@@ -4,8 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:layout_height="match_parent">
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
......@@ -20,16 +19,23 @@
<FrameLayout
android:id="@+id/message_list_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:layout_above="@+id/layout_message_composer">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
<include
android:id="@+id/layout_message_list"
layout="@layout/message_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical" />
android:layout_height="match_parent" />
</FrameLayout>
<include
android:id="@+id/layout_message_composer"
layout="@layout/message_composer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" />
<View
android:id="@+id/view_dim"
android:layout_width="match_parent"
......@@ -45,14 +51,6 @@
android:layout_height="wrap_content"
android:layout_above="@+id/layout_message_composer"
android:layout_margin="5dp"
android:visibility="gone"
tools:visibility="visible" />
<include
android:id="@+id/layout_message_composer"
layout="@layout/message_composer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" />
android:visibility="gone" />
</RelativeLayout>
......@@ -60,23 +60,16 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/guideline_one"
app:layout_constraintRight_toRightOf="@+id/text_total_unread_messages"
app:layout_constraintRight_toRightOf="@+id/layout_unread_messages_badge"
tools:text="11:45" />
<TextView
android:id="@+id/text_total_unread_messages"
style="@style/TextAppearance.AppCompat.Caption"
<include
android:id="@+id/layout_unread_messages_badge"
layout="@layout/unread_messages_badge"
android:layout_width="20dp"
android:layout_height="20dp"
android:background="@drawable/style_total_unread_messages"
android:gravity="center"
android:textColor="@color/white"
android:textSize="10sp"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/guideline_two"
app:layout_constraintRight_toRightOf="parent"
tools:text="99+"
tools:visibility="visible" />
app:layout_constraintRight_toRightOf="parent" />
<android.support.constraint.Guideline
android:id="@+id/guideline_one"
......
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/button_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:src="@drawable/ic_arrow_downward_24dp"
android:theme="@style/Theme.AppCompat"
android:tint="@color/gray_material"
android:visibility="invisible"
app:backgroundTint="@color/white"
app:fabSize="mini"
app:layout_anchor="@id/recycler_view"
app:layout_anchorGravity="bottom|end" />
</android.support.design.widget.CoordinatorLayout>
\ 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="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/text_total_unread_messages"
style="@style/TextAppearance.AppCompat.Caption"
android:layout_width="20dp"
android:layout_height="20dp"
android:background="@drawable/style_total_unread_messages"
android:gravity="center"
android:textColor="@color/white"
android:textSize="10sp"
android:visibility="gone"
tools:text="99+"
tools:visibility="visible" />
</LinearLayout>
\ 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