Merge pull request #1355 from laserzt/issue-1132-hide-boss-damage

issue #1302: "Messages: Add initial message" fix
This commit is contained in:
Phillip Thelen 2020-09-18 10:10:47 +02:00 committed by GitHub
commit d86704c080
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 196 additions and 41 deletions

View file

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
android:layout_height="200dp"
android:gravity="center_horizontal"
android:orientation="vertical">
<com.habitrpg.android.habitica.ui.AvatarView
android:id="@+id/avatar_view"
android:layout_width="87dp"
android:layout_height="69dp"
android:layout_gravity="center_horizontal|top"
android:visibility="visible"
app:showBackground="false"
app:showMount="false"
app:showPet="false"
tools:visibility="visible" />
<TextView
android:id="@+id/filler_1"
android:layout_width="match_parent"
android:layout_height="16dp"
android:layout_gravity="bottom|center_horizontal"
android:ellipsize="end"
android:lines="1"
android:singleLine="true"
android:textAlignment="center"
android:textColor="@color/gray_300"
android:textSize="12sp" />
<com.habitrpg.android.habitica.ui.views.social.UsernameLabel
android:id="@+id/display_name_textview"
android:layout_width="wrap_content"
android:layout_height="24dp"
android:baselineAligned="false"
android:clipToPadding="false"
android:textAlignment="center"
android:visibility="visible" />
<TextView
android:id="@+id/subline_textview"
android:layout_width="match_parent"
android:layout_height="24dp"
android:layout_gravity="center_horizontal"
android:ellipsize="end"
android:lines="1"
android:singleLine="true"
android:textAlignment="center"
android:textColor="@color/gray_300"
android:textSize="12sp"
tools:text="@Username" />
<TextView
android:id="@+id/filler_2"
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_gravity="bottom|center_horizontal"
android:ellipsize="end"
android:lines="1"
android:singleLine="true"
android:textAlignment="center"
android:textColor="@color/gray_300"
android:textSize="12sp" />
<com.habitrpg.android.habitica.ui.views.HabiticaEmojiTextView
android:id="@+id/message_text"
android:layout_width="348dp"
android:layout_height="42dp"
android:layout_gravity="center"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:lineSpacingMultiplier="1.0"
android:text="Start chatting below! Remember to be friendly and follow the Community Guidelines."
android:textAlignment="center"
android:textColor="@color/gray_300"
tools:text="Start chatting below! Remember to be friendly and follow the Community Guidelines." />
</LinearLayout>

View file

@ -8,7 +8,7 @@ import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.models.social.ChatMessage
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.viewHolders.ChatRecyclerViewHolder
import com.habitrpg.android.habitica.ui.viewHolders.ChatRecyclerMessageViewHolder
import io.reactivex.BackpressureStrategy
import io.reactivex.Flowable
import io.reactivex.subjects.PublishSubject
@ -39,7 +39,7 @@ class ChatRecyclerViewAdapter(data: OrderedRealmCollection<ChatMessage>?, autoUp
return if (viewType == 0) {
SystemChatMessageViewHolder(parent.inflate(R.layout.system_chat_message))
} else {
ChatRecyclerViewHolder(parent.inflate(R.layout.chat_item), uuid, isTavern)
ChatRecyclerMessageViewHolder(parent.inflate(R.layout.chat_item), uuid, isTavern)
}
}
@ -48,7 +48,7 @@ class ChatRecyclerViewAdapter(data: OrderedRealmCollection<ChatMessage>?, autoUp
if (data[position].isSystemMessage) {
(holder as? SystemChatMessageViewHolder)?.bind(data[position])
} else {
val chatHolder = holder as? ChatRecyclerViewHolder ?: return
val chatHolder = holder as? ChatRecyclerMessageViewHolder ?: return
val message = data[position]
chatHolder.bind(message,
uuid,

View file

@ -1,6 +1,8 @@
package com.habitrpg.android.habitica.ui.adapter.social
import android.view.ViewGroup
import androidx.paging.DataSource
import androidx.paging.PagedList
import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil
import com.habitrpg.android.habitica.R
@ -8,13 +10,18 @@ import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.models.social.ChatMessage
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.viewHolders.ChatRecyclerViewHolder
import com.habitrpg.android.habitica.ui.viewHolders.ChatRecyclerIntroViewHolder
import com.habitrpg.android.habitica.ui.viewHolders.ChatRecyclerMessageViewHolder
import io.reactivex.BackpressureStrategy
import io.reactivex.Flowable
import io.reactivex.subjects.PublishSubject
import com.habitrpg.android.habitica.models.members.Member
class InboxAdapter(private var user: User?, private var replyToUser : Member) : PagedListAdapter<ChatMessage, ChatRecyclerViewHolder>(DIFF_CALLBACK) {
private val FIRST_MESSAGE = 0
private val NORMAL_MESSAGE = 1
class InboxAdapter(private var user: User?) : PagedListAdapter<ChatMessage, ChatRecyclerViewHolder>(DIFF_CALLBACK) {
private var expandedMessageId: String? = null
private val likeMessageEvents = PublishSubject.create<ChatMessage>()
private val userLabelClickEvents = PublishSubject.create<String>()
private val deleteMessageEvents = PublishSubject.create<ChatMessage>()
@ -22,24 +29,49 @@ class InboxAdapter(private var user: User?) : PagedListAdapter<ChatMessage, Chat
private val replyMessageEvents = PublishSubject.create<String>()
private val copyMessageEvents = PublishSubject.create<ChatMessage>()
private fun isPositionIntroMessage(position: Int) : Boolean {
return (position == super.getItemCount() - 1)
}
override fun getItemViewType(position: Int): Int {
return if (isPositionIntroMessage(position)) FIRST_MESSAGE else NORMAL_MESSAGE
}
override fun getItemId(position: Int): Long {
return if (isPositionIntroMessage(position)) -1 else super.getItemId(position)
}
override fun getItem(position: Int) : ChatMessage? {
return if (isPositionIntroMessage(position)) ChatMessage() else super.getItem(position)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatRecyclerViewHolder {
return ChatRecyclerViewHolder(parent.inflate(R.layout.chat_item), user?.id ?: "", false)
return if (viewType == FIRST_MESSAGE) ChatRecyclerIntroViewHolder(parent.inflate(R.layout.tavern_chat_intro_item), replyToUser.id!!)
else ChatRecyclerMessageViewHolder(parent.inflate(R.layout.chat_item), user?.id ?: "", false)
}
override fun onBindViewHolder(holder: ChatRecyclerViewHolder, position: Int) {
val message = getItem(position) ?: return
holder.bind(message,
val firstMessage : Boolean = getItemViewType(position) == FIRST_MESSAGE
if (firstMessage) {
val introHolder = holder as ChatRecyclerIntroViewHolder
introHolder.bind(replyToUser)
introHolder.onOpenProfile = { userLabelClickEvents.onNext(it) }
}
else {
val message : ChatMessage = getItem(position) ?: return
val messageHolder = holder as ChatRecyclerMessageViewHolder
messageHolder.bind(message,
user?.id ?: "",
user,
expandedMessageId == message.id)
holder.onShouldExpand = { expandMessage(message.id, position) }
holder.onLikeMessage = { likeMessageEvents.onNext(it) }
holder.onOpenProfile = { userLabelClickEvents.onNext(it) }
holder.onReply = { replyMessageEvents.onNext(it) }
holder.onCopyMessage = { copyMessageEvents.onNext(it) }
holder.onFlagMessage = { flagMessageEvents.onNext(it) }
holder.onDeleteMessage = { deleteMessageEvents.onNext(it) }
messageHolder.onShouldExpand = { expandMessage(message.id, position) }
messageHolder.onLikeMessage = { likeMessageEvents.onNext(it) }
messageHolder.onOpenProfile = { userLabelClickEvents.onNext(it) }
messageHolder.onReply = { replyMessageEvents.onNext(it) }
messageHolder.onCopyMessage = { copyMessageEvents.onNext(it) }
messageHolder.onFlagMessage = { flagMessageEvents.onNext(it) }
messageHolder.onDeleteMessage = { deleteMessageEvents.onNext(it) }
}
}
fun getUserLabelClickFlowable(): Flowable<String> {
@ -59,6 +91,8 @@ class InboxAdapter(private var user: User?) : PagedListAdapter<ChatMessage, Chat
}
private fun expandMessage(id: String, position: Int) {
if (isPositionIntroMessage(position))
return
expandedMessageId = if (expandedMessageId == id) {
null
} else {

View file

@ -6,7 +6,10 @@ import android.content.Context
import android.os.Bundle
import android.view.*
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.paging.DataSource
import androidx.paging.PagedList
import com.habitrpg.android.habitica.MainNavDirections
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
@ -27,6 +30,9 @@ import com.habitrpg.android.habitica.ui.viewmodels.InboxViewModelFactory
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar.Companion.showSnackbar
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.Action
import io.reactivex.functions.Consumer
import java.lang.Exception
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -67,22 +73,22 @@ class InboxMessageListFragment : BaseMainFragment<FragmentInboxMessageListBindin
val layoutManager = androidx.recyclerview.widget.LinearLayoutManager(this.getActivity())
binding?.recyclerView?.layoutManager = layoutManager
compositeSubscription.add(apiClient.getMember(replyToUserUUID!!).subscribe( Consumer
{ member ->
chatAdapter = InboxAdapter(user, member)
viewModel?.messages?.observe(this.viewLifecycleOwner, Observer { chatAdapter?.submitList(it) })
chatAdapter = InboxAdapter(user)
viewModel?.messages?.observe(this.viewLifecycleOwner, { chatAdapter?.submitList(it) })
viewModel?.getMemberData()?.observe(this.viewLifecycleOwner, {
activity?.binding?.toolbarTitle?.text = it?.profile?.name
})
binding?.recyclerView?.adapter = chatAdapter
binding?.recyclerView?.itemAnimator = SafeDefaultItemAnimator()
chatAdapter?.let { adapter ->
compositeSubscription.add(adapter.getUserLabelClickFlowable().subscribe({
FullProfileActivity.open(it)
}, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(adapter.getDeleteMessageFlowable().subscribe({ this.showDeleteConfirmationDialog(it) }, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(adapter.getFlagMessageClickFlowable().subscribe({ this.showFlagConfirmationDialog(it) }, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(adapter.getCopyMessageFlowable().subscribe({ this.copyMessageToClipboard(it) }, RxErrorHandler.handleEmptyError()))
}
chatAdapter?.let { adapter ->
compositeSubscription.add(adapter.getUserLabelClickFlowable().subscribe(Consumer<String> {
FullProfileActivity.open(it)
}, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(adapter.getDeleteMessageFlowable().subscribe(Consumer { this.showDeleteConfirmationDialog(it) }, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(adapter.getFlagMessageClickFlowable().subscribe(Consumer { this.showFlagConfirmationDialog(it) }, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(adapter.getCopyMessageFlowable().subscribe(Consumer { this.copyMessageToClipboard(it) }, RxErrorHandler.handleEmptyError()))
}
}))
binding?.chatBarView?.sendAction = { sendMessage(it) }
binding?.chatBarView?.maxChatLength = configManager.maxChatLength()

View file

@ -21,8 +21,31 @@ import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
import io.reactivex.Maybe
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import com.habitrpg.android.habitica.models.members.Member
class ChatRecyclerViewHolder(itemView: View, private var userId: String, private val isTavern: Boolean) : RecyclerView.ViewHolder(itemView) {
open class ChatRecyclerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {}
class ChatRecyclerIntroViewHolder(itemView: View, replyToUUID: String) : ChatRecyclerViewHolder(itemView) {
private val binding = TavernChatIntroItemBinding.bind(itemView)
var onOpenProfile: ((String) -> Unit)? = null
init {
binding.avatarView.setOnClickListener { onOpenProfile?.invoke(replyToUUID) }
binding.displayNameTextview.setOnClickListener { onOpenProfile?.invoke(replyToUUID) }
binding.sublineTextview.setOnClickListener { onOpenProfile?.invoke(replyToUUID) }
}
fun bind(member: Member) {
binding.avatarView.setAvatar(member)
binding.displayNameTextview.username = member.displayName
binding.displayNameTextview.tier = member.contributor?.level ?: 0
binding.sublineTextview.text = "@" + member.username
}
}
class ChatRecyclerMessageViewHolder(itemView: View, private var userId: String, private val isTavern: Boolean) : ChatRecyclerViewHolder(itemView) {
val binding = ChatItemBinding.bind(itemView)
val context: Context = itemView.context

View file

@ -36,7 +36,7 @@ class InboxViewModel(recipientID: String?, recipientUsername: String?) : BaseVie
.setEnablePlaceholders(false)
.build()
private val dataSourceFactory = MessagesDataSourceFactory(socialRepository, recipientID)
private val dataSourceFactory = MessagesDataSourceFactory(socialRepository, recipientID, ChatMessage())
val messages: LiveData<PagedList<ChatMessage>> = dataSourceFactory.toLiveData(config)
private val member: MutableLiveData<Member?> by lazy {
MutableLiveData<Member?>()
@ -85,7 +85,7 @@ class InboxViewModel(recipientID: String?, recipientUsername: String?) : BaseVie
}
}
private class MessagesDataSource(val socialRepository: SocialRepository, var recipientID: String?):
private class MessagesDataSource(val socialRepository: SocialRepository, var recipientID: String?, var footer : ChatMessage?):
PositionalDataSource<ChatMessage>() {
private var lastFetchWasEnd = false
override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<ChatMessage>) {
@ -97,9 +97,16 @@ private class MessagesDataSource(val socialRepository: SocialRepository, var rec
if (recipientID?.isNotBlank() != true) { return@launch }
val page = ceil(params.startPosition.toFloat() / params.loadSize.toFloat()).toInt()
socialRepository.retrieveInboxMessages(recipientID ?: "", page)
.subscribe({
if (it.size != 10) lastFetchWasEnd = true
callback.onResult(it)
.subscribe(Consumer {
if (it.size < 10) {
lastFetchWasEnd = true
if (footer != null)
callback.onResult(it.plusElement(footer!!))
else
callback.onResult(it)
}
else
callback.onResult(it)
}, RxErrorHandler.handleEmptyError())
}
}
@ -115,23 +122,28 @@ private class MessagesDataSource(val socialRepository: SocialRepository, var rec
if (recipientID?.isNotBlank() != true) { return@flatMapPublisher Flowable.just(it) }
socialRepository.retrieveInboxMessages(recipientID ?: "", 0)
.doOnNext {
messages -> if (messages.size != 10) lastFetchWasEnd = true
messages -> if (messages.size < 10) {
lastFetchWasEnd = true
}
}
} else {
Flowable.just(it)
}
}
.subscribe({
callback.onResult(it, 0)
.subscribe(Consumer {
if (it.size < 10 && footer != null)
callback.onResult(it.plusElement(footer!!), 0)
else
callback.onResult(it, 0)
}, RxErrorHandler.handleEmptyError())
}
}
}
private class MessagesDataSourceFactory(val socialRepository: SocialRepository, var recipientID: String?) :
private class MessagesDataSourceFactory(val socialRepository: SocialRepository, var recipientID: String?, val footer : ChatMessage?) :
DataSource.Factory<Int, ChatMessage>() {
val sourceLiveData = MutableLiveData<MessagesDataSource>()
var latestSource: MessagesDataSource = MessagesDataSource(socialRepository, recipientID)
var latestSource: MessagesDataSource = MessagesDataSource(socialRepository, recipientID, footer)
fun updateRecipientID(newID: String?) {
recipientID = newID
@ -139,7 +151,7 @@ private class MessagesDataSourceFactory(val socialRepository: SocialRepository,
}
override fun create(): DataSource<Int, ChatMessage> {
latestSource = MessagesDataSource(socialRepository, recipientID)
latestSource = MessagesDataSource(socialRepository, recipientID, footer)
sourceLiveData.postValue(latestSource)
return latestSource
}