mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-05-19 20:29:02 +00:00
Improve party viewmodel
This commit is contained in:
parent
38b90ed49c
commit
c5bb9211d2
6 changed files with 309 additions and 15 deletions
|
|
@ -69,6 +69,7 @@ import com.habitrpg.android.habitica.ui.fragments.setup.TaskSetupFragment;
|
|||
import com.habitrpg.android.habitica.ui.fragments.setup.WelcomeFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.skills.SkillTasksRecyclerViewFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.skills.SkillsFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.social.ChatFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.social.ChatListFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.social.GroupInformationFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.social.GuildFragment;
|
||||
|
|
@ -299,4 +300,6 @@ public interface AppComponent {
|
|||
void inject(@NotNull VerifyUsernameActivity verifyUsernameActivity);
|
||||
|
||||
void inject(@NotNull GroupViewModel viewModel);
|
||||
|
||||
void inject(@NotNull ChatFragment chatFragment);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,249 @@
|
|||
package com.habitrpg.android.habitica.ui.fragments.social
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.net.toUri
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.components.AppComponent
|
||||
import com.habitrpg.android.habitica.extensions.notNull
|
||||
import com.habitrpg.android.habitica.helpers.RemoteConfigManager
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.models.social.ChatMessage
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.ui.activities.FullProfileActivity
|
||||
import com.habitrpg.android.habitica.ui.activities.MainActivity
|
||||
import com.habitrpg.android.habitica.ui.adapter.social.ChatRecyclerViewAdapter
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
|
||||
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.PartyViewModel
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar.Companion.showSnackbar
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar.SnackbarDisplayType
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.functions.Consumer
|
||||
import io.realm.RealmResults
|
||||
import kotlinx.android.synthetic.main.fragment_chat.*
|
||||
import kotlinx.android.synthetic.main.tavern_chat_new_entry_item.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
@SuppressLint("ValidFragment")
|
||||
class ChatFragment(private var viewModel: PartyViewModel) : BaseFragment(), SwipeRefreshLayout.OnRefreshListener {
|
||||
|
||||
@Inject
|
||||
lateinit var configManager: RemoteConfigManager
|
||||
|
||||
private var isTavern: Boolean = false
|
||||
internal var layoutManager: LinearLayoutManager? = null
|
||||
internal var groupId: String? = null
|
||||
private var user: User? = null
|
||||
private var userId: String? = null
|
||||
private var chatAdapter: ChatRecyclerViewAdapter? = null
|
||||
private var navigatedOnceToFragment = false
|
||||
private var isScrolledToTop = true
|
||||
private var refreshDisposable: Disposable? = null
|
||||
|
||||
fun configure(groupId: String, user: User?, isTavern: Boolean) {
|
||||
this.groupId = groupId
|
||||
this.user = user
|
||||
if (this.user != null) {
|
||||
this.userId = this.user?.id
|
||||
}
|
||||
this.isTavern = isTavern
|
||||
}
|
||||
|
||||
override fun injectFragment(component: AppComponent) {
|
||||
component.inject(this)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
refreshLayout.setOnRefreshListener(this)
|
||||
|
||||
layoutManager = recyclerView.layoutManager as? androidx.recyclerview.widget.LinearLayoutManager
|
||||
|
||||
if (layoutManager == null) {
|
||||
layoutManager = androidx.recyclerview.widget.LinearLayoutManager(context)
|
||||
recyclerView.layoutManager = layoutManager
|
||||
}
|
||||
|
||||
chatAdapter = ChatRecyclerViewAdapter(null, true, user, true, configManager.enableUsernameRelease())
|
||||
chatAdapter.notNull {adapter ->
|
||||
compositeSubscription.add(adapter.getUserLabelClickFlowable().subscribe(Consumer { userId ->
|
||||
context.notNull { FullProfileActivity.open(it, userId) }
|
||||
}, 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.getReplyMessageEvents().subscribe(Consumer{ setReplyTo(it) }, RxErrorHandler.handleEmptyError()))
|
||||
compositeSubscription.add(adapter.getCopyMessageFlowable().subscribe(Consumer { this.copyMessageToClipboard(it) }, RxErrorHandler.handleEmptyError()))
|
||||
compositeSubscription.add(adapter.getLikeMessageFlowable().subscribe(Consumer { viewModel.likeMessage(it) }, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
|
||||
chatBarView.sendAction = { sendChatMessage(it) }
|
||||
chatBarView.maxChatLength = configManager.maxChatLength()
|
||||
|
||||
recyclerView.adapter = chatAdapter
|
||||
recyclerView.itemAnimator = SafeDefaultItemAnimator()
|
||||
|
||||
compositeSubscription.add(viewModel.getChatMessages().firstElement().subscribe(Consumer<RealmResults<ChatMessage>> { this.setChatMessages(it) }, RxErrorHandler.handleEmptyError()))
|
||||
|
||||
if (user?.flags?.isCommunityGuidelinesAccepted == true) {
|
||||
communityGuidelinesView.visibility = View.GONE
|
||||
} else {
|
||||
communityGuidelinesView.setOnClickListener { _ ->
|
||||
val i = Intent(Intent.ACTION_VIEW)
|
||||
i.data = "https://habitica.com/static/community-guidelines".toUri()
|
||||
context?.startActivity(i)
|
||||
viewModel.updateUser(user, "flags.communityGuidelinesAccepted", true)
|
||||
}
|
||||
}
|
||||
|
||||
recyclerView.addOnScrollListener(object : androidx.recyclerview.widget.RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: androidx.recyclerview.widget.RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
isScrolledToTop = layoutManager?.findFirstVisibleItemPosition() == 0
|
||||
}
|
||||
})
|
||||
|
||||
refresh(false)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
stopAutoRefreshing()
|
||||
}
|
||||
|
||||
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
|
||||
super.setUserVisibleHint(isVisibleToUser)
|
||||
if (isVisibleToUser) {
|
||||
startAutoRefreshing()
|
||||
} else {
|
||||
stopAutoRefreshing()
|
||||
}
|
||||
}
|
||||
|
||||
private fun startAutoRefreshing() {
|
||||
if (refreshDisposable != null && refreshDisposable?.isDisposed != true) {
|
||||
refreshDisposable?.dispose()
|
||||
}
|
||||
refreshDisposable = Observable.interval(30, TimeUnit.SECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(Consumer {
|
||||
refresh(false)
|
||||
}, RxErrorHandler.handleEmptyError())
|
||||
}
|
||||
|
||||
private fun stopAutoRefreshing() {
|
||||
if (refreshDisposable?.isDisposed != true) {
|
||||
refreshDisposable?.dispose()
|
||||
refreshDisposable = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun setReplyTo(username: String?) {
|
||||
val previousText = chatEditText.text.toString()
|
||||
if (previousText.contains("@$username")) {
|
||||
return
|
||||
}
|
||||
chatEditText.setText("@$username $previousText", TextView.BufferType.EDITABLE)
|
||||
}
|
||||
|
||||
override fun onRefresh() {
|
||||
refresh(true)
|
||||
}
|
||||
|
||||
private fun refresh(isUserInitiated: Boolean) {
|
||||
if (isUserInitiated) {
|
||||
refreshLayout.isRefreshing = true
|
||||
}
|
||||
viewModel.retrieveGroupChat {
|
||||
refreshLayout?.isRefreshing = false
|
||||
if (isScrolledToTop) {
|
||||
recyclerView.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setNavigatedToFragment() {
|
||||
navigatedOnceToFragment = true
|
||||
markMessagesAsSeen()
|
||||
}
|
||||
|
||||
private fun markMessagesAsSeen() {
|
||||
if (navigatedOnceToFragment) {
|
||||
viewModel.markMessagesSeen()
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyMessageToClipboard(chatMessage: ChatMessage) {
|
||||
val clipMan = activity?.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
|
||||
val messageText = ClipData.newPlainText("Chat message", chatMessage.text)
|
||||
clipMan?.primaryClip = messageText
|
||||
val activity = activity as? MainActivity
|
||||
if (activity != null) {
|
||||
showSnackbar(activity.floatingMenuWrapper, getString(R.string.chat_message_copied), SnackbarDisplayType.NORMAL)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showFlagConfirmationDialog(chatMessage: ChatMessage) {
|
||||
val context = context
|
||||
if (context != null) {
|
||||
val builder = AlertDialog.Builder(context)
|
||||
builder.setMessage(R.string.chat_flag_confirmation)
|
||||
.setPositiveButton(R.string.flag_confirm) { _, _ ->
|
||||
viewModel.flagMessage(chatMessage) {
|
||||
val activity = activity as? MainActivity
|
||||
activity?.floatingMenuWrapper.notNull {
|
||||
showSnackbar(it, "Flagged message by " + chatMessage.user, SnackbarDisplayType.NORMAL)
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton(R.string.action_cancel) { _, _ -> }
|
||||
builder.show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showDeleteConfirmationDialog(chatMessage: ChatMessage) {
|
||||
val context = context
|
||||
if (context != null) {
|
||||
AlertDialog.Builder(context)
|
||||
.setTitle(R.string.confirm_delete_tag_title)
|
||||
.setMessage(R.string.confirm_delete_tag_message)
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setPositiveButton(android.R.string.yes) { _, _ -> viewModel.deleteMessage(chatMessage) }
|
||||
.setNegativeButton(android.R.string.no, null).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
outState.putString("userId", this.userId)
|
||||
outState.putString("groupId", this.groupId)
|
||||
outState.putBoolean("isTavern", this.isTavern)
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
private fun setChatMessages(chatMessages: RealmResults<ChatMessage>) {
|
||||
chatAdapter?.updateData(chatMessages)
|
||||
recyclerView.scrollToPosition(0)
|
||||
|
||||
viewModel.gotNewMessages = true
|
||||
|
||||
markMessagesAsSeen()
|
||||
}
|
||||
|
||||
private fun sendChatMessage(chatText: String) {
|
||||
viewModel.postGroupChat(chatText) {
|
||||
recyclerView?.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,29 +10,27 @@ import androidx.fragment.app.FragmentPagerAdapter
|
|||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import com.habitrpg.android.habitica.HabiticaBaseApplication
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.components.AppComponent
|
||||
import com.habitrpg.android.habitica.extensions.notNull
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.models.social.Group
|
||||
import com.habitrpg.android.habitica.ui.activities.GroupFormActivity
|
||||
import com.habitrpg.android.habitica.ui.activities.PartyInviteActivity
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
|
||||
import com.habitrpg.android.habitica.ui.fragments.social.ChatFragment
|
||||
import com.habitrpg.android.habitica.ui.fragments.social.ChatListFragment
|
||||
import com.habitrpg.android.habitica.ui.fragments.social.GroupInformationFragment
|
||||
import com.habitrpg.android.habitica.ui.helpers.bindView
|
||||
import com.habitrpg.android.habitica.ui.helpers.resetViews
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.GroupViewType
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.PartyViewModel
|
||||
import io.reactivex.functions.Consumer
|
||||
import java.util.*
|
||||
|
||||
class PartyFragment : BaseMainFragment() {
|
||||
|
||||
private val viewPager: ViewPager? by bindView(R.id.viewPager)
|
||||
private var partyMemberListFragment: PartyMemberListFragment? = null
|
||||
private var chatListFragment: ChatListFragment? = null
|
||||
private var chatFragment: ChatFragment? = null
|
||||
private var viewPagerAdapter: androidx.fragment.app.FragmentPagerAdapter? = null
|
||||
|
||||
private lateinit var viewModel: PartyViewModel
|
||||
|
|
@ -99,7 +97,7 @@ class PartyFragment : BaseMainFragment() {
|
|||
|
||||
partyMemberListFragment?.setPartyId(group.id)
|
||||
|
||||
chatListFragment?.groupId = group.id
|
||||
chatFragment?.groupId = group.id
|
||||
|
||||
this.activity?.invalidateOptionsMenu()
|
||||
}
|
||||
|
|
@ -219,13 +217,10 @@ class PartyFragment : BaseMainFragment() {
|
|||
}
|
||||
}
|
||||
1 -> {
|
||||
if (chatListFragment == null) {
|
||||
chatListFragment = ChatListFragment()
|
||||
if (user?.hasParty() == true) {
|
||||
chatListFragment?.configure(user?.party?.id ?: "", user, false)
|
||||
}
|
||||
if (chatFragment == null) {
|
||||
chatFragment = ChatFragment(viewModel)
|
||||
}
|
||||
fragment = chatListFragment
|
||||
fragment = chatFragment
|
||||
}
|
||||
2 -> {
|
||||
if (partyMemberListFragment == null) {
|
||||
|
|
@ -264,13 +259,13 @@ class PartyFragment : BaseMainFragment() {
|
|||
viewPager?.addOnPageChangeListener(object : androidx.viewpager.widget.ViewPager.OnPageChangeListener {
|
||||
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
|
||||
if (position == 1) {
|
||||
chatListFragment?.setNavigatedToFragment()
|
||||
chatFragment?.setNavigatedToFragment()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPageSelected(position: Int) {
|
||||
if (position == 1) {
|
||||
chatListFragment?.setNavigatedToFragment()
|
||||
chatFragment?.setNavigatedToFragment()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,4 +48,8 @@ abstract class BaseViewModel: ViewModel() {
|
|||
private fun loadUserFromLocal() {
|
||||
disposable.add(userRepository.getUser().observeOn(AndroidSchedulers.mainThread()).subscribe(Consumer { user.value = it }, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
|
||||
fun updateUser(user: User?, path: String, value: Any) {
|
||||
disposable.add(userRepository.updateUser(user, path, value).subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ import com.habitrpg.android.habitica.extensions.*
|
|||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.models.social.ChatMessage
|
||||
import com.habitrpg.android.habitica.models.social.Group
|
||||
import com.habitrpg.android.habitica.ui.activities.MainActivity
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
|
||||
import io.reactivex.BackpressureStrategy
|
||||
import io.reactivex.Flowable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
|
|
@ -41,6 +43,7 @@ open class GroupViewModel: BaseViewModel() {
|
|||
}
|
||||
|
||||
protected val groupIDSubject = BehaviorSubject.create<Optional<String>>()
|
||||
var gotNewMessages: Boolean = false
|
||||
|
||||
override fun inject(component: AppComponent) {
|
||||
component.inject(this)
|
||||
|
|
@ -112,4 +115,43 @@ open class GroupViewModel: BaseViewModel() {
|
|||
fun rejectGroupInvite(groupID: String) {
|
||||
disposable.add(socialRepository.rejectGroupInvite(groupID).subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
|
||||
fun markMessagesSeen() {
|
||||
groupIDSubject.value?.value.notNull {
|
||||
if (groupViewType != GroupViewType.TAVERN && it.isNotEmpty() && gotNewMessages) {
|
||||
socialRepository.markMessagesSeen(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun likeMessage(message: ChatMessage) {
|
||||
disposable.add(socialRepository.likeMessage(message).subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
|
||||
fun flagMessage(chatMessage: ChatMessage, function: () -> Unit) {
|
||||
disposable.add(socialRepository.flagMessage(chatMessage)
|
||||
.subscribe(Consumer {
|
||||
function()
|
||||
}, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
|
||||
fun deleteMessage(chatMessage: ChatMessage) {
|
||||
disposable.add(socialRepository.deleteMessage(chatMessage).subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
|
||||
fun postGroupChat(chatText: String, onComplete: () -> Unit?) {
|
||||
groupIDSubject.value?.value.notNull {
|
||||
disposable.add(socialRepository.postGroupChat(it, chatText).subscribe(Consumer {
|
||||
onComplete()
|
||||
}, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
}
|
||||
|
||||
fun retrieveGroupChat(onComplete: () -> Unit) {
|
||||
groupIDSubject.value?.value.notNull {
|
||||
disposable.add(socialRepository.retrieveGroupChat(it).subscribe(Consumer {
|
||||
onComplete()
|
||||
}, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,16 +2,17 @@ package com.habitrpg.android.habitica.ui.viewmodels
|
|||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Transformations
|
||||
import com.habitrpg.android.habitica.components.AppComponent
|
||||
import com.habitrpg.android.habitica.extensions.notNull
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.models.inventory.Quest
|
||||
import io.reactivex.functions.Consumer
|
||||
import kotlinx.android.synthetic.main.fragment_chat.*
|
||||
|
||||
class PartyViewModel: GroupViewModel() {
|
||||
|
||||
private val quest: MutableLiveData<Quest?> = MutableLiveData()
|
||||
|
||||
private val quest = Transformations.map(getGroupData()) { it?.quest }
|
||||
|
||||
internal val isQuestActive: Boolean
|
||||
get() = quest.value?.active == true
|
||||
|
|
|
|||
Loading…
Reference in a new issue