added the intro message.

there is still a bug that on entering the chat dialog, and on refreshing, the chat scroll moves to the bottom instead of to the top
This commit is contained in:
Eyal 2020-08-22 01:43:52 +03:00
parent 76038e7bfa
commit 75b6167565
5 changed files with 172 additions and 32 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="match_parent"
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"
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"
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

@ -9,7 +9,7 @@ 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.helpers.bindView
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
@ -40,7 +40,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.tavern_chat_item), uuid, isTavern)
ChatRecyclerMessageViewHolder(parent.inflate(R.layout.tavern_chat_item), uuid, isTavern)
}
}
@ -49,7 +49,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

@ -8,11 +8,14 @@ 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?) : PagedListAdapter<ChatMessage, ChatRecyclerViewHolder>(DIFF_CALLBACK) {
class InboxAdapter(private var user: User?, private var replyToUser : Member) : PagedListAdapter<ChatMessage, ChatRecyclerViewHolder>(DIFF_CALLBACK) {
private var expandedMessageId: String? = null
private val likeMessageEvents = PublishSubject.create<ChatMessage>()
@ -22,24 +25,49 @@ class InboxAdapter(private var user: User?) : PagedListAdapter<ChatMessage, Chat
private val replyMessageEvents = PublishSubject.create<String>()
private val copyMessageEvents = PublishSubject.create<ChatMessage>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatRecyclerViewHolder {
return ChatRecyclerViewHolder(parent.inflate(R.layout.tavern_chat_item), user?.id ?: "", false)
override fun getItemViewType(position: Int): Int {
return when (position == super.getItemCount()) {
true -> 1
false -> 0
}
}
override fun onBindViewHolder(holder: ChatRecyclerViewHolder, position: Int) {
val message = getItem(position) ?: return
override fun getItemCount(): Int {
return super.getItemCount() + 1
}
holder.bind(message,
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatRecyclerViewHolder {
return if (viewType == 1) ChatRecyclerIntroViewHolder(parent.inflate(R.layout.tavern_chat_intro_item), replyToUser.id!!)
else ChatRecyclerMessageViewHolder(parent.inflate(R.layout.tavern_chat_item), user?.id ?: "", false)
}
fun getFirstMessage() : ChatMessage
{
var firstMessage = ChatMessage()
return firstMessage
}
override fun onBindViewHolder(holder: ChatRecyclerViewHolder, position: Int) {
val firstMessage : Boolean = getItemViewType(position) == 1
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 getLikeMessageFlowable(): Flowable<ChatMessage> {

View file

@ -68,21 +68,25 @@ class InboxMessageListFragment : BaseMainFragment(), androidx.swiperefreshlayout
val layoutManager = androidx.recyclerview.widget.LinearLayoutManager(this.getActivity())
recyclerView.layoutManager = layoutManager
chatAdapter = InboxAdapter(user)
viewModel?.messages?.observe(this.viewLifecycleOwner, Observer { chatAdapter?.submitList(it) })
viewModel?.getMemberData()?.observe(this.viewLifecycleOwner, Observer {
activity?.binding?.toolbarTitle?.text = it?.profile?.name
})
recyclerView.adapter = chatAdapter
recyclerView.itemAnimator = SafeDefaultItemAnimator()
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()))
}
compositeSubscription.add(apiClient.getMember(replyToUserUUID!!).subscribe( Consumer
{ member ->
chatAdapter = InboxAdapter(user, member)
viewModel?.messages?.observe(this.viewLifecycleOwner, Observer { chatAdapter?.submitList(it) })
viewModel?.getMemberData()?.observe(this.viewLifecycleOwner, Observer {
activity?.binding?.toolbarTitle?.text = it?.profile?.name
})
recyclerView.adapter = chatAdapter
recyclerView.itemAnimator = SafeDefaultItemAnimator()
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()))
}
}))
chatBarView.sendAction = { sendMessage(it) }
chatBarView.maxChatLength = configManager.maxChatLength()

View file

@ -28,8 +28,36 @@ import com.habitrpg.android.habitica.ui.views.social.UsernameLabel
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 avatarView: AvatarView by bindView(R.id.avatar_view)
private val displayNameView: UsernameLabel by bindView(R.id.display_name_textview)
private val sublineTextView: TextView by bindView(R.id.subline_textview)
private val messageText: HabiticaEmojiTextView by bindView(R.id.message_text)
var onOpenProfile: ((String) -> Unit)? = null
init {
avatarView.setOnClickListener { onOpenProfile?.invoke(replyToUUID) }
displayNameView.setOnClickListener { onOpenProfile?.invoke(replyToUUID) }
sublineTextView.setOnClickListener { onOpenProfile?.invoke(replyToUUID) }
}
fun bind(member: Member) {
avatarView.setAvatar(member)
displayNameView.username = member.displayName
displayNameView.tier = member.contributor?.level ?: 0
sublineTextView.text = "@" + member.username
}
}
class ChatRecyclerMessageViewHolder(itemView: View, private var userId: String, private val isTavern: Boolean) : ChatRecyclerViewHolder(itemView) {
private val messageWrapper: ViewGroup by bindView(R.id.message_wrapper)
private val avatarView: AvatarView by bindView(R.id.avatar_view)