diff --git a/Habitica/res/layout/fragment_party_detail.xml b/Habitica/res/layout/fragment_party_detail.xml
index 1655ff4e6..96cf154a6 100644
--- a/Habitica/res/layout/fragment_party_detail.xml
+++ b/Habitica/res/layout/fragment_party_detail.xml
@@ -202,6 +202,10 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
+
diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml
index 92f524b3c..a62075004 100644
--- a/Habitica/res/values/strings.xml
+++ b/Habitica/res/values/strings.xml
@@ -1385,6 +1385,7 @@
Unlock %s gear and skills
Rescind Invite
Rescinded
+ Pending Invite
- You
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt
index c30554774..e25973c5b 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt
@@ -286,6 +286,10 @@ interface ApiService {
@POST("groups/{gid}/quests/invite/{questKey}")
suspend fun inviteToQuest(@Path("gid") groupId: String, @Path("questKey") questKey: String): HabitResponse
+ @GET("groups/{gid}/invites")
+ suspend fun getGroupInvites(@Path("gid") groupId: String,
+ @Query("includeAllPublicFields") includeAllPublicFields: Boolean?): HabitResponse>
+
@POST("groups/{gid}/quests/abort")
suspend fun abortQuest(@Path("gid") groupId: String): HabitResponse
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt
index 440bb8e27..d5c2dc78c 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt
@@ -278,4 +278,5 @@ interface ApiClient {
suspend fun getHallMember(userId: String): Member?
suspend fun markTaskNeedsWork(taskID: String, userID: String): Task?
suspend fun retrievePartySeekingUsers(page: Int) : List?
+ suspend fun getGroupInvites(groupId: String, includeAllPublicFields: Boolean?): List?
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/SocialRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/SocialRepository.kt
index e11bd825c..ef50e34f9 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/SocialRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/SocialRepository.kt
@@ -123,4 +123,5 @@ interface SocialRepository : BaseRepository {
fun getMember(userID: String?): Flow
suspend fun updateMember(memberID: String, key: String, value: Any?): Member?
suspend fun retrievePartySeekingUsers(page: Int = 0): List?
+ suspend fun retrievegroupInvites(id: String, includeAllPublicFields: Boolean): List?
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt
index da4f50614..a1f15560c 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt
@@ -578,6 +578,10 @@ class ApiClientImpl(
return process { apiService.rejectGroupInvite(groupId) }
}
+ override suspend fun getGroupInvites(groupId: String, includeAllPublicFields: Boolean?): List? {
+ return process { apiService.getGroupInvites(groupId, includeAllPublicFields) }
+ }
+
override suspend fun acceptQuest(groupId: String): Void? {
return process { apiService.acceptQuest(groupId) }
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/SocialRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/SocialRepositoryImpl.kt
index 2babb5f00..bfdf2d0d6 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/SocialRepositoryImpl.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/SocialRepositoryImpl.kt
@@ -275,6 +275,9 @@ class SocialRepositoryImpl(
}
}
+ override suspend fun retrievegroupInvites(id: String, includeAllPublicFields: Boolean) = apiClient.getGroupInvites(id, includeAllPublicFields)
+
+
override suspend fun retrieveMemberWithUsername(username: String?, fromHall: Boolean): Member? {
return retrieveMember(username, fromHall)
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt
index 8c6f4922b..ad7389c0c 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt
@@ -213,7 +213,7 @@ class UserRepositoryImpl(
override suspend fun sendPasswordResetEmail(email: String) = apiClient.sendPasswordResetEmail(email)
override suspend fun updateLoginName(newLoginName: String, password: String?): User? {
- if (password != null && password.isNotEmpty()) {
+ if (!password.isNullOrEmpty()) {
apiClient.updateLoginName(newLoginName.trim(), password.trim())
} else {
apiClient.updateUsername(newLoginName.trim())
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyDetailFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyDetailFragment.kt
index d6ee9757b..7c3f75808 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyDetailFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyDetailFragment.kt
@@ -7,6 +7,7 @@ import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.widget.AppCompatEditText
+import androidx.compose.foundation.layout.Column
import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
@@ -30,10 +31,13 @@ import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
import com.habitrpg.android.habitica.ui.fragments.inventory.items.ItemDialogFragment
import com.habitrpg.android.habitica.ui.helpers.dismissKeyboard
+import com.habitrpg.android.habitica.ui.theme.HabiticaTheme
import com.habitrpg.android.habitica.ui.viewHolders.GroupMemberViewHolder
import com.habitrpg.android.habitica.ui.viewmodels.PartyViewModel
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
+import com.habitrpg.android.habitica.ui.views.LoadingButtonState
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
+import com.habitrpg.android.habitica.ui.views.social.PartySeekingListItem
import com.habitrpg.common.habitica.extensions.DataBindingUtils
import com.habitrpg.common.habitica.extensions.dpToPx
import com.habitrpg.common.habitica.extensions.loadImage
@@ -55,7 +59,10 @@ class PartyDetailFragment : BaseFragment() {
override var binding: FragmentPartyDetailBinding? = null
- override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentPartyDetailBinding {
+ override fun createBinding(
+ inflater: LayoutInflater,
+ container: ViewGroup?
+ ): FragmentPartyDetailBinding {
return FragmentPartyDetailBinding.inflate(inflater, container, false)
}
@@ -110,6 +117,28 @@ class PartyDetailFragment : BaseFragment() {
userRepository.retrieveUser(false, true)
}
}
+ binding?.invitesWrapper?.setContent {
+ HabiticaTheme {
+ val invitedMembers = viewModel?.pendingInvites
+ Column {
+ for (invitedMember in (invitedMembers ?: emptyList())) {
+ val state = viewModel?.pendingInviteStates?.getOrDefault(
+ invitedMember.id,
+ LoadingButtonState.CONTENT
+ ) ?: LoadingButtonState.CONTENT
+ PartySeekingListItem(
+ user = invitedMember,
+ inviteState = state,
+ isInvited = state != LoadingButtonState.SUCCESS,
+ showHeader = true,
+ showExtendedInfo = false,
+ onInvite = {
+ viewModel?.rescindInvite(invitedMember)
+ })
+ }
+ }
+ }
+ }
viewModel?.getGroupData()?.observe(viewLifecycleOwner) { updateParty(it) }
viewModel?.user?.observe(viewLifecycleOwner) { updateUser(it) }
@@ -138,7 +167,8 @@ class PartyDetailFragment : BaseFragment() {
binding?.questImageWrapper?.visibility = View.VISIBLE
lifecycleScope.launch(Dispatchers.Main) {
delay(500)
- val content = inventoryRepository.getQuestContent(party.quest?.key ?: "").firstOrNull()
+ val content =
+ inventoryRepository.getQuestContent(party.quest?.key ?: "").firstOrNull()
if (content != null) {
updateQuestContent(content)
}
@@ -239,17 +269,36 @@ class PartyDetailFragment : BaseFragment() {
}
binding?.questImageWrapper?.alpha = 1.0f
binding?.questProgressView?.alpha = 1.0f
- context?.let { binding?.questParticipationView?.setTextColor(ContextCompat.getColor(it, R.color.text_quad)) }
+ context?.let {
+ binding?.questParticipationView?.setTextColor(
+ ContextCompat.getColor(
+ it,
+ R.color.text_quad
+ )
+ )
+ }
if (viewModel?.isQuestActive == true) {
binding?.questProgressView?.visibility = View.VISIBLE
- binding?.questProgressView?.setData(questContent, viewModel?.getGroupData()?.value?.quest?.progress)
+ binding?.questProgressView?.setData(
+ questContent,
+ viewModel?.getGroupData()?.value?.quest?.progress
+ )
val questParticipants = viewModel?.getGroupData()?.value?.quest?.members
if (questParticipants?.find { it.key == viewModel?.userViewModel?.userID } != null) {
- binding?.questParticipationView?.text = context?.getString(R.string.number_participants, questParticipants.size)
+ binding?.questParticipationView?.text =
+ context?.getString(R.string.number_participants, questParticipants.size)
} else {
- binding?.questParticipationView?.text = context?.getString(R.string.not_participating)
- context?.let { binding?.questParticipationView?.setTextColor(ContextCompat.getColor(it, R.color.red_10)) }
+ binding?.questParticipationView?.text =
+ context?.getString(R.string.not_participating)
+ context?.let {
+ binding?.questParticipationView?.setTextColor(
+ ContextCompat.getColor(
+ it,
+ R.color.red_10
+ )
+ )
+ }
binding?.questImageWrapper?.alpha = 0.5f
binding?.questProgressView?.alpha = 0.5f
}
@@ -257,7 +306,8 @@ class PartyDetailFragment : BaseFragment() {
binding?.questProgressView?.visibility = View.GONE
val members = viewModel?.getGroupData()?.value?.quest?.members
val responded = members?.filter { it.isParticipating != null }
- binding?.questParticipationView?.text = context?.getString(R.string.number_responded, responded?.size, members?.size)
+ binding?.questParticipationView?.text =
+ context?.getString(R.string.number_responded, responded?.size, members?.size)
}
}
@@ -294,7 +344,8 @@ class PartyDetailFragment : BaseFragment() {
val factory = LayoutInflater.from(context)
val newMessageView = factory.inflate(R.layout.profile_new_message_dialog, null)
- val emojiEditText = newMessageView.findViewById(R.id.edit_new_message_text)
+ val emojiEditText =
+ newMessageView.findViewById(R.id.edit_new_message_text)
val newMessageTitle = newMessageView.findViewById(R.id.new_message_title)
newMessageTitle.text = String.format(getString(R.string.profile_send_message_to), username)
@@ -306,13 +357,17 @@ class PartyDetailFragment : BaseFragment() {
(activity as? MainActivity)?.snackbarContainer?.let { it1 ->
HabiticaSnackbar.showSnackbar(
it1,
- String.format(getString(R.string.profile_message_sent_to), username), HabiticaSnackbar.SnackbarDisplayType.NORMAL
+ String.format(getString(R.string.profile_message_sent_to), username),
+ HabiticaSnackbar.SnackbarDisplayType.NORMAL
)
}
}
activity?.dismissKeyboard()
}
- addMessageDialog?.addButton(android.R.string.cancel, false) { _, _ -> activity?.dismissKeyboard() }
+ addMessageDialog?.addButton(
+ android.R.string.cancel,
+ false
+ ) { _, _ -> activity?.dismissKeyboard() }
addMessageDialog?.setAdditionalContentView(newMessageView)
addMessageDialog?.show()
}
@@ -325,7 +380,8 @@ class PartyDetailFragment : BaseFragment() {
(activity as? MainActivity)?.snackbarContainer?.let { it1 ->
HabiticaSnackbar.showSnackbar(
it1,
- String.format(getString(R.string.transferred_ownership), displayName), HabiticaSnackbar.SnackbarDisplayType.NORMAL
+ String.format(getString(R.string.transferred_ownership), displayName),
+ HabiticaSnackbar.SnackbarDisplayType.NORMAL
)
}
}
@@ -333,7 +389,12 @@ class PartyDetailFragment : BaseFragment() {
}
dialog?.addButton(android.R.string.cancel, false) { _, _ -> activity?.dismissKeyboard() }
dialog?.setTitle(context?.getString(R.string.transfer_ownership_confirm))
- dialog?.setMessage(context?.getString(R.string.transfer_ownership_confirm_message, displayName))
+ dialog?.setMessage(
+ context?.getString(
+ R.string.transfer_ownership_confirm_message,
+ displayName
+ )
+ )
dialog?.show()
}
@@ -345,7 +406,8 @@ class PartyDetailFragment : BaseFragment() {
(activity as? MainActivity)?.snackbarContainer?.let { it1 ->
HabiticaSnackbar.showSnackbar(
it1,
- String.format(getString(R.string.removed_member), displayName), HabiticaSnackbar.SnackbarDisplayType.NORMAL
+ String.format(getString(R.string.removed_member), displayName),
+ HabiticaSnackbar.SnackbarDisplayType.NORMAL
)
}
}
@@ -370,7 +432,8 @@ class PartyDetailFragment : BaseFragment() {
lifecycleScope.launchCatching {
userRepository.getUser().collect {
it?.challenges?.forEach { membership ->
- val challenge = challengeRepository.getChallenge(membership.challengeID).firstOrNull()
+ val challenge =
+ challengeRepository.getChallenge(membership.challengeID).firstOrNull()
if (challenge != null && challenge.groupId == viewModel?.groupID) {
groupChallenges.add(challenge)
}
@@ -396,7 +459,11 @@ class PartyDetailFragment : BaseFragment() {
MainNavigationController.navigate(R.id.noPartyFragment)
}
}
- alert.addButton(R.string.leave_challenges_delete_tasks, false, isDestructive = true) { _, _ ->
+ alert.addButton(
+ R.string.leave_challenges_delete_tasks,
+ false,
+ isDestructive = true
+ ) { _, _ ->
viewModel?.leaveGroup(groupChallenges, false) {
parentFragmentManager.popBackStack()
MainNavigationController.navigate(R.id.noPartyFragment)
@@ -408,7 +475,11 @@ class PartyDetailFragment : BaseFragment() {
val alert = HabiticaAlertDialog(context)
alert.setTitle(R.string.leave_party_confirmation)
alert.setMessage(R.string.rejoin_party)
- alert.addButton(R.string.leave, isPrimary = true, isDestructive = true) { _, _ ->
+ alert.addButton(
+ R.string.leave,
+ isPrimary = true,
+ isDestructive = true
+ ) { _, _ ->
viewModel?.leaveGroup(groupChallenges, false) {
parentFragmentManager.popBackStack()
MainNavigationController.navigate(R.id.noPartyFragment)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartySeekingFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartySeekingFragment.kt
index 401066fe1..27e0c156b 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartySeekingFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartySeekingFragment.kt
@@ -210,7 +210,7 @@ fun PartySeekingView(
user = it,
inviteState =viewModel.inviteStates[it.id] ?: LoadingButtonState.CONTENT,
isInvited = viewModel.successfulInvites.contains(it.id),
- modifier = Modifier.animateItemPlacement()
+ modifier = Modifier.animateItemPlacement().padding(horizontal = 14.dp)
) { member ->
scope.launchCatching({
viewModel.inviteStates[member.id] = LoadingButtonState.FAILED
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/GroupViewModel.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/GroupViewModel.kt
index e6f3117d6..2e6dbc1d8 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/GroupViewModel.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/GroupViewModel.kt
@@ -1,6 +1,8 @@
package com.habitrpg.android.habitica.ui.viewmodels
import android.os.Bundle
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.mutableStateMapOf
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.asLiveData
@@ -14,12 +16,14 @@ import com.habitrpg.android.habitica.models.members.Member
import com.habitrpg.android.habitica.models.social.Challenge
import com.habitrpg.android.habitica.models.social.ChatMessage
import com.habitrpg.android.habitica.models.social.Group
+import com.habitrpg.android.habitica.ui.views.LoadingButtonState
import com.habitrpg.common.habitica.helpers.ExceptionHandler
import com.habitrpg.common.habitica.helpers.launchCatching
import com.habitrpg.common.habitica.models.notifications.NewChatMessageData
import dagger.hilt.android.lifecycle.HiltViewModel
import io.realm.kotlin.toFlow
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.filterNotNull
@@ -29,8 +33,10 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import retrofit2.HttpException
import javax.inject.Inject
+import kotlin.time.DurationUnit
+import kotlin.time.toDuration
-enum class GroupViewType(internal val order : String) {
+enum class GroupViewType(internal val order: String) {
PARTY("party"),
GUILD("guild"),
TAVERN("tavern")
@@ -39,17 +45,17 @@ enum class GroupViewType(internal val order : String) {
@OptIn(ExperimentalCoroutinesApi::class)
@HiltViewModel
open class GroupViewModel @Inject constructor(
- userRepository : UserRepository,
- userViewModel : MainUserViewModel,
- val challengeRepository : ChallengeRepository,
- val socialRepository : SocialRepository,
- val notificationsManager : NotificationsManager
+ userRepository: UserRepository,
+ userViewModel: MainUserViewModel,
+ val challengeRepository: ChallengeRepository,
+ val socialRepository: SocialRepository,
+ val notificationsManager: NotificationsManager
) : BaseViewModel(userRepository, userViewModel) {
protected val groupIDState = MutableStateFlow(null)
- val groupIDFlow : Flow = groupIDState
+ val groupIDFlow: Flow = groupIDState
- var groupViewType : GroupViewType? = null
+ var groupViewType: GroupViewType? = null
private val groupFlow = groupIDFlow
.filterNotNull()
@@ -67,21 +73,21 @@ open class GroupViewModel @Inject constructor(
.map { it != null }
private val isMemberData = isMemberFlow.asLiveData()
- private val _chatMessages : MutableLiveData> by lazy {
+ private val _chatMessages: MutableLiveData> by lazy {
MutableLiveData>(listOf())
}
- val chatmessages : LiveData> by lazy {
+ val chatmessages: LiveData> by lazy {
_chatMessages
}
- var gotNewMessages : Boolean = false
+ var gotNewMessages: Boolean = false
override fun onCleared() {
socialRepository.close()
super.onCleared()
}
- fun setGroupID(groupID : String) {
+ fun setGroupID(groupID: String) {
if (groupID == groupIDState.value) return
groupIDState.value = groupID
@@ -95,22 +101,25 @@ open class GroupViewModel @Inject constructor(
}
}
- val groupID : String?
+ val groupID: String?
get() = groupIDState.value
- val isMember : Boolean
+ val isMember: Boolean
get() = isMemberData.value ?: false
- val leaderID : String?
+ val leaderID: String?
get() = group.value?.leaderID
- val isLeader : Boolean
+ val isLeader: Boolean
get() = user.value?.id == leaderID
- val isPublicGuild : Boolean
+ val isPublicGuild: Boolean
get() = group.value?.privacy == "public"
- fun getGroupData() : LiveData = group
- fun getLeaderData() : LiveData = leader
- fun getIsMemberData() : LiveData = isMemberData
+ val pendingInvites = mutableStateListOf()
+ val pendingInviteStates = mutableStateMapOf()
- fun retrieveGroup(function : (() -> Unit)?) {
+ fun getGroupData(): LiveData = group
+ fun getLeaderData(): LiveData = leader
+ fun getIsMemberData(): LiveData = isMemberData
+
+ fun retrieveGroup(function: (() -> Unit)?) {
if (groupID?.isNotEmpty() == true) {
viewModelScope.launch(
ExceptionHandler.coroutine {
@@ -122,19 +131,23 @@ open class GroupViewModel @Inject constructor(
val group = socialRepository.retrieveGroup(groupID ?: "")
if (groupViewType == GroupViewType.PARTY) {
socialRepository.retrievePartyMembers(group?.id ?: "", true)
+ val invites =
+ socialRepository.retrievegroupInvites(group?.id ?: "", true) ?: emptyList()
+ pendingInvites.clear()
+ pendingInvites.addAll(invites)
}
function?.invoke()
}
}
}
- fun inviteToGroup(inviteData : HashMap) {
+ fun inviteToGroup(inviteData: HashMap) {
viewModelScope.launchCatching {
socialRepository.inviteToGroup(group.value?.id ?: "", inviteData)
}
}
- fun updateOrCreateGroup(bundle : Bundle?) {
+ fun updateOrCreateGroup(bundle: Bundle?) {
viewModelScope.launch(ExceptionHandler.coroutine()) {
if (group.value == null) {
socialRepository.createGroup(
@@ -157,9 +170,9 @@ open class GroupViewModel @Inject constructor(
}
fun leaveGroup(
- groupChallenges : List,
- keepChallenges : Boolean = true,
- function : (() -> Unit)? = null
+ groupChallenges: List,
+ keepChallenges: Boolean = true,
+ function: (() -> Unit)? = null
) {
if (!keepChallenges) {
viewModelScope.launchCatching {
@@ -175,14 +188,14 @@ open class GroupViewModel @Inject constructor(
}
}
- fun joinGroup(id : String? = null, function : (() -> Unit)? = null) {
+ fun joinGroup(id: String? = null, function: (() -> Unit)? = null) {
viewModelScope.launchCatching {
socialRepository.joinGroup(id ?: groupID)
function?.invoke()
}
}
- fun rejectGroupInvite(id : String? = null) {
+ fun rejectGroupInvite(id: String? = null) {
groupID?.let {
viewModelScope.launchCatching {
socialRepository.rejectGroupInvite(id ?: it)
@@ -200,7 +213,7 @@ open class GroupViewModel @Inject constructor(
}
}
- fun likeMessage(message : ChatMessage) {
+ fun likeMessage(message: ChatMessage) {
viewModelScope.launchCatching {
val message = socialRepository.likeMessage(message)
val index = _chatMessages.value?.indexOfFirst { it.id == message?.id }
@@ -216,7 +229,7 @@ open class GroupViewModel @Inject constructor(
}
}
- fun deleteMessage(chatMessage : ChatMessage) {
+ fun deleteMessage(chatMessage: ChatMessage) {
val oldIndex = _chatMessages.value?.indexOf(chatMessage) ?: return
val list = _chatMessages.value?.toMutableList()
list?.remove(chatMessage)
@@ -232,7 +245,7 @@ open class GroupViewModel @Inject constructor(
}
}
- fun postGroupChat(chatText : String, onComplete : () -> Unit, onError : () -> Unit) {
+ fun postGroupChat(chatText: String, onComplete: () -> Unit, onError: () -> Unit) {
groupID?.let { groupID ->
viewModelScope.launch(
ExceptionHandler.coroutine {
@@ -251,7 +264,7 @@ open class GroupViewModel @Inject constructor(
}
}
- fun retrieveGroupChat(onComplete : () -> Unit) {
+ fun retrieveGroupChat(onComplete: () -> Unit) {
var groupID = groupID
if (groupViewType == GroupViewType.PARTY) {
groupID = "party"
@@ -267,7 +280,7 @@ open class GroupViewModel @Inject constructor(
}
}
- fun updateGroup(bundle : Bundle?) {
+ fun updateGroup(bundle: Bundle?) {
viewModelScope.launch(ExceptionHandler.coroutine()) {
socialRepository.updateGroup(
group.value,
@@ -278,4 +291,16 @@ open class GroupViewModel @Inject constructor(
)
}
}
+
+ fun rescindInvite(invitedMember: Member) {
+ pendingInviteStates[invitedMember.id] = LoadingButtonState.LOADING
+ viewModelScope.launchCatching({
+ pendingInviteStates[invitedMember.id] = LoadingButtonState.FAILED
+ }) {
+ socialRepository.removeMemberFromGroup(groupID ?: "", invitedMember.id)
+ pendingInviteStates[invitedMember.id] = LoadingButtonState.SUCCESS
+ delay(1.toDuration(DurationUnit.SECONDS))
+ pendingInvites.remove(invitedMember)
+ }
+ }
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/social/PartySeekingListItem.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/social/PartySeekingListItem.kt
index 6f130a113..4a85215ef 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/social/PartySeekingListItem.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/social/PartySeekingListItem.kt
@@ -46,13 +46,14 @@ fun PartySeekingListItem(
modifier : Modifier = Modifier,
inviteState : LoadingButtonState = LoadingButtonState.LOADING,
isInvited: Boolean = false,
+ showHeader: Boolean = false,
+ showExtendedInfo: Boolean = true,
onInvite : (Member) -> Unit
) {
Column(
modifier
.fillMaxWidth()
- .padding(horizontal = 14.dp)
- .padding(bottom = 4.dp)
+ .padding(bottom = 6.dp)
.background(HabiticaTheme.colors.windowBackground, HabiticaTheme.shapes.large)
.padding(14.dp)
) {
@@ -68,6 +69,14 @@ fun PartySeekingListItem(
verticalArrangement = Arrangement.Top,
modifier = Modifier.fillMaxWidth()
) {
+ if (showHeader) {
+ Text(
+ stringResource(R.string.pending_invite).uppercase(),
+ fontSize = 12.sp,
+ color = HabiticaTheme.colors.textQuad,
+ modifier = Modifier.padding(bottom = 4.dp)
+ )
+ }
ProvideTextStyle(value = TextStyle(fontSize = 14.sp)) {
ComposableUsernameLabel(
user.displayName,
@@ -101,18 +110,22 @@ fun PartySeekingListItem(
hasClass = user.hasClass
)
}
- Text(
- stringResource(R.string.x_checkins, user.loginIncentives),
- fontWeight = FontWeight.Medium,
- fontSize = 14.sp,
- color = HabiticaTheme.colors.textPrimary
- )
- Text(
- Locale(user.preferences?.language ?: "en").getDisplayName(Locale.getDefault()),
- fontWeight = FontWeight.Medium,
- fontSize = 14.sp,
- color = HabiticaTheme.colors.textPrimary
- )
+ if (showExtendedInfo) {
+ Text(
+ stringResource(R.string.x_checkins, user.loginIncentives),
+ fontWeight = FontWeight.Medium,
+ fontSize = 14.sp,
+ color = HabiticaTheme.colors.textPrimary
+ )
+ Text(
+ Locale(
+ user.preferences?.language ?: "en"
+ ).getDisplayName(Locale.getDefault()),
+ fontWeight = FontWeight.Medium,
+ fontSize = 14.sp,
+ color = HabiticaTheme.colors.textPrimary
+ )
+ }
}
}
InviteButton(