Improve chat handling

This commit is contained in:
Phillip Thelen 2022-01-10 11:22:05 +01:00
parent 6e4994052b
commit b1bc6c5a17
10 changed files with 130 additions and 28 deletions

View file

@ -60,7 +60,6 @@ class SocialRepositoryImpl(localRepository: SocialLocalRepository, apiClient: Ap
chatMessage
}
.toList()
.doOnSuccess { localRepository.saveChatMessages(groupId, it) }
}
override fun getGroupChat(groupId: String): Flowable<out List<ChatMessage>> {
@ -94,7 +93,6 @@ class SocialRepositoryImpl(localRepository: SocialLocalRepository, apiClient: Ap
val liked = chatMessage.userLikesMessage(userID)
localRepository.likeMessage(chatMessage, userID, !liked)
return apiClient.likeMessage(chatMessage.groupId ?: "", chatMessage.id)
.doOnError { localRepository.likeMessage(chatMessage, userID, liked) }
}
override fun deleteMessage(chatMessage: ChatMessage): Flowable<Void> {
@ -111,11 +109,6 @@ class SocialRepositoryImpl(localRepository: SocialLocalRepository, apiClient: Ap
postChatMessageResult.message.groupId = groupId
postChatMessageResult
}
.doOnNext { postChatMessageResult ->
if (postChatMessageResult != null) {
localRepository.save(postChatMessageResult.message)
}
}
}
override fun postGroupChat(groupId: String, message: String): Flowable<PostChatMessageResult> {

View file

@ -1,11 +1,13 @@
package com.habitrpg.android.habitica.models.inventory
import com.google.gson.annotations.SerializedName
import com.habitrpg.android.habitica.models.BaseMainObject
import com.habitrpg.android.habitica.models.BaseObject
import io.realm.RealmModel
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class Equipment : RealmObject(), BaseObject {
open class Equipment : RealmObject(), BaseMainObject {
var value: Double = 0.toDouble()
var type: String? = ""
@ -25,4 +27,11 @@ open class Equipment : RealmObject(), BaseObject {
var twoHanded = false
var mystery = ""
var gearSet = ""
override val realmClass: Class<out RealmModel>
get() = Equipment::class.java
override val primaryIdentifier: String?
get() = key
override val primaryIdentifierName: String
get() = "key"
}

View file

@ -2,13 +2,15 @@ package com.habitrpg.android.habitica.models.members
import com.google.gson.annotations.SerializedName
import com.habitrpg.android.habitica.models.Avatar
import com.habitrpg.android.habitica.models.BaseMainObject
import com.habitrpg.android.habitica.models.BaseObject
import com.habitrpg.android.habitica.models.social.UserParty
import com.habitrpg.android.habitica.models.user.*
import io.realm.RealmModel
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class Member : RealmObject(), Avatar, BaseObject {
open class Member : RealmObject(), Avatar, BaseMainObject {
@PrimaryKey
@SerializedName("_id")
@ -58,4 +60,10 @@ open class Member : RealmObject(), Avatar, BaseObject {
override val sleep: Boolean
get() = preferences?.sleep ?: false
override val realmClass: Class<out RealmModel>
get() = Member::class.java
override val primaryIdentifier: String?
get() = id
override val primaryIdentifierName: String
get() = "id"
}

View file

@ -1,14 +1,16 @@
package com.habitrpg.android.habitica.models.social
import com.habitrpg.android.habitica.models.BaseMainObject
import com.habitrpg.android.habitica.models.BaseObject
import com.habitrpg.android.habitica.models.tasks.TasksOrder
import com.habitrpg.android.habitica.models.user.User
import io.realm.RealmModel
import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
import java.util.*
open class Challenge : RealmObject(), BaseObject {
open class Challenge : RealmObject(), BaseMainObject {
@PrimaryKey
var id: String? = null
@ -58,6 +60,13 @@ open class Challenge : RealmObject(), BaseObject {
return map
}
override val realmClass: Class<out RealmModel>
get() = Challenge::class.java
override val primaryIdentifier: String?
get() = id
override val primaryIdentifierName: String
get() = "id"
override fun equals(other: Any?): Boolean {
return if (other?.javaClass == Challenge::class.java && this.id != null) {
this.id == (other as Challenge).id

View file

@ -1,13 +1,49 @@
package com.habitrpg.android.habitica.ui.adapter
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.DiffUtil
import com.habitrpg.android.habitica.models.BaseMainObject
import com.habitrpg.android.habitica.models.BaseObject
abstract class BaseRecyclerViewAdapter<T, VH : RecyclerView.ViewHolder> : RecyclerView.Adapter<VH>() {
open class DiffCallback<T: BaseMainObject>(protected val oldList: List<BaseMainObject>, protected val newList: List<BaseMainObject>) :
DiffUtil.Callback() {
override fun getOldListSize(): Int {
return oldList.size
}
override fun getNewListSize(): Int {
return newList.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].primaryIdentifier == newList[newItemPosition].primaryIdentifier
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldList[oldItemPosition]
val newItem = newList[newItemPosition]
return oldItem == newItem
}
}
abstract class BaseRecyclerViewAdapter<T: BaseMainObject, VH: RecyclerView.ViewHolder> : RecyclerView.Adapter<VH>() {
open fun getDiffCallback(oldList: List<T>, newList: List<T>): DiffCallback<T>? {
return null
}
var data: List<T> = emptyList()
set(value) {
val diffCallback = getDiffCallback(data, value)
field = value
notifyDataSetChanged()
if (diffCallback != null) {
val diffResult = DiffUtil.calculateDiff(diffCallback)
diffResult.dispatchUpdatesTo(this)
} else {
notifyDataSetChanged()
}
}
override fun getItemCount(): Int {

View file

@ -7,14 +7,31 @@ import androidx.recyclerview.widget.RecyclerView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.SystemChatMessageBinding
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.models.BaseMainObject
import com.habitrpg.android.habitica.models.BaseObject
import com.habitrpg.android.habitica.models.social.ChatMessage
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.adapter.BaseRecyclerViewAdapter
import com.habitrpg.android.habitica.ui.adapter.DiffCallback
import com.habitrpg.android.habitica.ui.viewHolders.ChatRecyclerMessageViewHolder
import io.reactivex.rxjava3.core.BackpressureStrategy
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.subjects.PublishSubject
class ChatDiffCallback(oldList: List<BaseMainObject>, newList: List<BaseMainObject>) :
DiffCallback<ChatMessage>(oldList, newList) {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].primaryIdentifier == newList[newItemPosition].primaryIdentifier
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldList[oldItemPosition] as ChatMessage
val newItem = newList[newItemPosition] as ChatMessage
return oldItem.likeCount == newItem.likeCount && oldItem.id == newItem.id
}
}
class ChatRecyclerViewAdapter(user: User?, private val isTavern: Boolean) : BaseRecyclerViewAdapter<ChatMessage, RecyclerView.ViewHolder>() {
internal var user = user
set(value) {
@ -31,6 +48,13 @@ class ChatRecyclerViewAdapter(user: User?, private val isTavern: Boolean) : Base
private val replyMessageEvents = PublishSubject.create<String>()
private val copyMessageEvents = PublishSubject.create<ChatMessage>()
override fun getDiffCallback(
oldList: List<ChatMessage>,
newList: List<ChatMessage>
): DiffCallback<ChatMessage> {
return ChatDiffCallback(oldList, newList)
}
init {
this.uuid = user?.id ?: ""
}

View file

@ -20,7 +20,6 @@ class InboxAdapter(private var user: User?, private var replyToUser: Member) : P
private val NORMAL_MESSAGE = 1
private var expandedMessageId: String? = null
private val likeMessageEvents = PublishSubject.create<ChatMessage>()
private val userLabelClickEvents = PublishSubject.create<String>()
private val deleteMessageEvents = PublishSubject.create<ChatMessage>()
private val flagMessageEvents = PublishSubject.create<ChatMessage>()
@ -64,7 +63,6 @@ class InboxAdapter(private var user: User?, private var replyToUser: Member) : P
expandedMessageId == message.id
)
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) }

View file

@ -95,7 +95,12 @@ class ChatFragment : BaseFragment<FragmentChatBinding>() {
}
})
viewModel?.getChatMessages()?.subscribe({ this.setChatMessages(it) }, RxErrorHandler.handleEmptyError())?.let { compositeSubscription.add(it) }
viewModel?.chatmessages?.observe(
viewLifecycleOwner,
{
setChatMessages(it)
}
)
binding?.chatBarView?.onCommunityGuidelinesAccepted = {
viewModel?.updateUser("flags.communityGuidelinesAccepted", true)
@ -204,7 +209,7 @@ class ChatFragment : BaseFragment<FragmentChatBinding>() {
private fun setChatMessages(chatMessages: List<ChatMessage>) {
chatAdapter?.data = chatMessages
viewModel?.socialRepository?.getUnmanagedCopy(chatMessages)?.let { binding?.chatBarView?.chatMessages = it }
binding?.chatBarView?.chatMessages = chatMessages
viewModel?.gotNewMessages = true

View file

@ -50,7 +50,7 @@ class ChatRecyclerIntroViewHolder(itemView: View, replyToUUID: String) : ChatRec
}
}
class ChatRecyclerMessageViewHolder(itemView: View, private var userId: String, private val isTavern: Boolean) : ChatRecyclerViewHolder(itemView) {
class ChatRecyclerMessageViewHolder(itemView: View, private var userId: String, private val isGroupChat: Boolean) : ChatRecyclerViewHolder(itemView) {
val binding = ChatItemBinding.bind(itemView)
val context: Context = itemView.context
@ -216,7 +216,7 @@ class ChatRecyclerMessageViewHolder(itemView: View, private var userId: String,
}
private fun setLikeProperties() {
binding.likeBackgroundLayout.visibility = if (isTavern) View.VISIBLE else View.INVISIBLE
binding.likeBackgroundLayout.visibility = if (isGroupChat) View.VISIBLE else View.INVISIBLE
@SuppressLint("SetTextI18n")
binding.tvLikes.text = "+" + chatMessage?.likeCount

View file

@ -54,6 +54,13 @@ open class GroupViewModel(initializeComponent: Boolean) : BaseViewModel(initiali
MutableLiveData<Boolean?>()
}
private val _chatMessages: MutableLiveData<List<ChatMessage>> by lazy {
MutableLiveData<List<ChatMessage>>(listOf())
}
val chatmessages: LiveData<List<ChatMessage>> by lazy {
_chatMessages
}
protected val groupIDSubject = BehaviorSubject.create<Optional<String>>()
val groupIDFlowable: Flowable<Optional<String>> = groupIDSubject.toFlowable(BackpressureStrategy.BUFFER)
var gotNewMessages: Boolean = false
@ -143,12 +150,6 @@ open class GroupViewModel(initializeComponent: Boolean) : BaseViewModel(initiali
)
}
fun getChatMessages(): Flowable<List<ChatMessage>> {
return groupIDFlowable
.filterMapEmpty()
.flatMap { socialRepository.getGroupChat(it) }
}
fun retrieveGroup(function: (() -> Unit)?) {
if (groupID?.isNotEmpty() == true) {
disposable.add(
@ -237,17 +238,35 @@ open class GroupViewModel(initializeComponent: Boolean) : BaseViewModel(initiali
}
fun likeMessage(message: ChatMessage) {
disposable.add(socialRepository.likeMessage(message).subscribe({ }, RxErrorHandler.handleEmptyError()))
val index = _chatMessages.value?.indexOf(message) ?: return
disposable.add(socialRepository.likeMessage(message).subscribe(
{
val list = _chatMessages.value?.toMutableList()
list?.set(index, it)
_chatMessages.postValue(list)
}, RxErrorHandler.handleEmptyError()))
}
fun deleteMessage(chatMessage: ChatMessage) {
disposable.add(socialRepository.deleteMessage(chatMessage).subscribe({ }, RxErrorHandler.handleEmptyError()))
val oldIndex = _chatMessages.value?.indexOf(chatMessage) ?: return
val list = _chatMessages.value?.toMutableList()
list?.remove(chatMessage)
_chatMessages.postValue(list)
disposable.add(socialRepository.deleteMessage(chatMessage).subscribe({
}, {
list?.add(oldIndex, chatMessage)
_chatMessages.postValue(list)
RxErrorHandler.reportError(it)
}))
}
fun postGroupChat(chatText: String, onComplete: () -> Unit, onError: () -> Unit) {
groupIDSubject.value?.value?.let {
socialRepository.postGroupChat(it, chatText).subscribe(
groupIDSubject.value?.value?.let { groupID ->
socialRepository.postGroupChat(groupID, chatText).subscribe(
{
val list = _chatMessages.value?.toMutableList()
list?.add(0, it.message)
_chatMessages.postValue(list)
onComplete()
},
{ error ->
@ -270,6 +289,7 @@ open class GroupViewModel(initializeComponent: Boolean) : BaseViewModel(initiali
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_chatMessages.postValue(it)
onComplete()
},
RxErrorHandler.handleEmptyError()