mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-19 08:09:12 +01:00
Handle network and permissions errors when saving group member label.
This commit is contained in:
@@ -863,7 +863,7 @@ class ConversationSettingsFragment :
|
||||
navController.safeNavigate(action)
|
||||
},
|
||||
onDisabledClicked = {
|
||||
Snackbar.make(requireView(), R.string.ConversationSettingsFragment__only_admins_can_add_member_labels, Snackbar.LENGTH_SHORT).show()
|
||||
Snackbar.make(requireView(), R.string.GroupMemberLabel__error_no_edit_permission, Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package org.thoughtcrime.securesms.groups;
|
||||
|
||||
public final class GroupInsufficientRightsException extends GroupChangeException {
|
||||
|
||||
GroupInsufficientRightsException(Throwable throwable) {
|
||||
public GroupInsufficientRightsException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -42,6 +44,7 @@ import androidx.core.os.bundleOf
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import org.signal.core.ui.compose.AllDevicePreviews
|
||||
import org.signal.core.ui.compose.Buttons
|
||||
import org.signal.core.ui.compose.CircularProgressWrapper
|
||||
import org.signal.core.ui.compose.ClearableTextField
|
||||
import org.signal.core.ui.compose.ComposeFragment
|
||||
import org.signal.core.ui.compose.Previews
|
||||
@@ -87,6 +90,7 @@ class MemberLabelFragment : ComposeFragment(), ReactWithAnyEmojiBottomSheetDialo
|
||||
override fun FragmentContent() {
|
||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
val backPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
|
||||
val callbacks = remember {
|
||||
object : MemberLabelUiCallbacks {
|
||||
@@ -102,16 +106,34 @@ class MemberLabelFragment : ComposeFragment(), ReactWithAnyEmojiBottomSheetDialo
|
||||
}
|
||||
}
|
||||
|
||||
val networkErrorMessage = stringResource(R.string.GroupMemberLabel__error_cant_save_no_network)
|
||||
val noPermissionErrorMessage = stringResource(R.string.GroupMemberLabel__error_no_edit_permission)
|
||||
|
||||
LaunchedEffect(uiState.saveState) {
|
||||
if (uiState.saveState is SaveState.Success) {
|
||||
backPressedDispatcher?.onBackPressed()
|
||||
viewModel.onSaveStateConsumed()
|
||||
when (uiState.saveState) {
|
||||
is SaveState.Success -> {
|
||||
backPressedDispatcher?.onBackPressed()
|
||||
viewModel.onSaveStateConsumed()
|
||||
}
|
||||
|
||||
is SaveState.NetworkError -> {
|
||||
snackbarHostState.showSnackbar(networkErrorMessage)
|
||||
viewModel.onSaveStateConsumed()
|
||||
}
|
||||
|
||||
is SaveState.InsufficientRights -> {
|
||||
snackbarHostState.showSnackbar(noPermissionErrorMessage)
|
||||
viewModel.onSaveStateConsumed()
|
||||
}
|
||||
|
||||
is SaveState.InProgress, null -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
MemberLabelScreenUi(
|
||||
state = uiState,
|
||||
callbacks = callbacks
|
||||
callbacks = callbacks,
|
||||
snackbarHostState = snackbarHostState
|
||||
)
|
||||
}
|
||||
|
||||
@@ -127,13 +149,15 @@ class MemberLabelFragment : ComposeFragment(), ReactWithAnyEmojiBottomSheetDialo
|
||||
@Composable
|
||||
private fun MemberLabelScreenUi(
|
||||
state: MemberLabelUiState,
|
||||
callbacks: MemberLabelUiCallbacks
|
||||
callbacks: MemberLabelUiCallbacks,
|
||||
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }
|
||||
) {
|
||||
Scaffolds.Settings(
|
||||
title = stringResource(R.string.GroupMemberLabel__title),
|
||||
onNavigationClick = callbacks::onClosePressed,
|
||||
navigationIcon = SignalIcons.X.imageVector,
|
||||
navigationContentDescription = stringResource(R.string.GroupMemberLabel__accessibility_close_screen)
|
||||
navigationContentDescription = stringResource(R.string.GroupMemberLabel__accessibility_close_screen),
|
||||
snackbarHost = { SnackbarHost(snackbarHostState) }
|
||||
) { paddingValues ->
|
||||
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
@@ -191,13 +215,17 @@ private fun MemberLabelScreenUi(
|
||||
}
|
||||
}
|
||||
|
||||
SaveButton(
|
||||
enabled = state.isSaveEnabled,
|
||||
onClick = callbacks::onSaveClicked,
|
||||
CircularProgressWrapper(
|
||||
isLoading = state.saveState is SaveState.InProgress,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(end = 24.dp, bottom = 16.dp)
|
||||
)
|
||||
) {
|
||||
SaveButton(
|
||||
enabled = state.isSaveEnabled,
|
||||
onClick = callbacks::onSaveClicked
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.groups.GroupManager
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
import org.whispersystems.signalservice.api.NetworkResult
|
||||
|
||||
/**
|
||||
* Handles the retrieval and modification of group member labels.
|
||||
@@ -52,12 +53,6 @@ class MemberLabelRepository private constructor(
|
||||
@WorkerThread
|
||||
fun getLabelJava(groupId: GroupId.V2, recipient: Recipient): MemberLabel? = runBlocking { getLabel(groupId, recipient) }
|
||||
|
||||
/**
|
||||
* Checks whether the [Recipient] has permission to set their member label in the given group (blocking version for Java compatibility).
|
||||
*/
|
||||
@WorkerThread
|
||||
fun canSetLabelJava(groupId: GroupId.V2, recipient: Recipient): Boolean = runBlocking { canSetLabel(groupId, recipient) }
|
||||
|
||||
/**
|
||||
* Gets the member label for a specific recipient in the group.
|
||||
*/
|
||||
@@ -117,13 +112,15 @@ class MemberLabelRepository private constructor(
|
||||
/**
|
||||
* Sets the group member label for the current user.
|
||||
*/
|
||||
suspend fun setLabel(groupId: GroupId.V2, label: MemberLabel): Unit = withContext(Dispatchers.IO) {
|
||||
suspend fun setLabel(groupId: GroupId.V2, label: MemberLabel): NetworkResult<Unit> = withContext(Dispatchers.IO) {
|
||||
if (!RemoteConfig.sendMemberLabels) {
|
||||
throw IllegalStateException("Set member label not allowed due to remote config.")
|
||||
}
|
||||
|
||||
val sanitizedLabel = label.sanitized()
|
||||
GroupManager.updateMemberLabel(context, groupId, sanitizedLabel.text, sanitizedLabel.emoji.orEmpty())
|
||||
NetworkResult.fromFetch {
|
||||
GroupManager.updateMemberLabel(context, groupId, sanitizedLabel.text, sanitizedLabel.emoji.orEmpty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,11 @@ import org.signal.core.util.StringUtil
|
||||
import org.signal.core.util.concurrent.SignalDispatchers
|
||||
import org.thoughtcrime.securesms.conversation.colors.NameColor
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.groups.GroupInsufficientRightsException
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.MemberLabelUiState.SaveState
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.whispersystems.signalservice.api.NetworkResult
|
||||
|
||||
class MemberLabelViewModel(
|
||||
private val memberLabelRepo: MemberLabelRepository = MemberLabelRepository.instance,
|
||||
@@ -97,7 +99,7 @@ class MemberLabelViewModel(
|
||||
}
|
||||
|
||||
val currentState = internalUiState.value
|
||||
memberLabelRepo.setLabel(
|
||||
val result = memberLabelRepo.setLabel(
|
||||
groupId = groupId,
|
||||
label = MemberLabel(
|
||||
emoji = currentState.labelEmoji.ifEmpty { null },
|
||||
@@ -105,8 +107,24 @@ class MemberLabelViewModel(
|
||||
)
|
||||
)
|
||||
|
||||
val newSaveState: SaveState = when (result) {
|
||||
is NetworkResult.Success -> SaveState.Success
|
||||
|
||||
is NetworkResult.NetworkError<*> -> SaveState.NetworkError
|
||||
|
||||
is NetworkResult.ApplicationError<*> -> {
|
||||
if (result.throwable is GroupInsufficientRightsException) {
|
||||
SaveState.InsufficientRights
|
||||
} else {
|
||||
throw result.throwable
|
||||
}
|
||||
}
|
||||
|
||||
is NetworkResult.StatusCodeError<*> -> throw result.exception
|
||||
}
|
||||
|
||||
internalUiState.update {
|
||||
it.copy(saveState = SaveState.Success)
|
||||
it.copy(saveState = newSaveState)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,5 +160,7 @@ data class MemberLabelUiState(
|
||||
sealed interface SaveState {
|
||||
data object InProgress : SaveState
|
||||
data object Success : SaveState
|
||||
data object NetworkError : SaveState
|
||||
data object InsufficientRights : SaveState
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user