mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-05-18 03:39:00 +00:00
Merge pull request #1355 from laserzt/issue-1132-hide-boss-damage
issue #1302: "Messages: Add initial message" fix
This commit is contained in:
commit
d86704c080
6 changed files with 196 additions and 41 deletions
80
Habitica/res/layout/tavern_chat_intro_item.xml
Normal file
80
Habitica/res/layout/tavern_chat_intro_item.xml
Normal 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>
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue