[SES-2930] - Show the correct toast message when invitation fails (#900)

* Universal message handling for failing to send invitation

* Comment
pull/1709/head
SessionHero01 3 months ago committed by GitHub
parent ab2bc2ff2b
commit c9c2ccb044
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -230,7 +230,12 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
for (Map.Entry<String,String> entry : parameters.entrySet()) {
builder.put(entry.getKey(), entry.getValue());
}
Toast.makeText(getApplicationContext(), builder.format(), toastLength).show();
Toast.makeText(this, builder.format(), toastLength).show();
}
@Override
public void toast(@NonNull CharSequence message, int toastLength) {
Toast.makeText(this, message, toastLength).show();
}
@Override

@ -61,10 +61,7 @@ abstract class AppBindings {
class ToasterModule {
@Provides
@Singleton
fun provideToaster(@ApplicationContext context: Context) = Toaster { stringRes, toastLength, parameters ->
val string = context.getString(stringRes, parameters)
Toast.makeText(context, string, toastLength).show()
}
fun provideToaster(@ApplicationContext context: Context) = (context as org.thoughtcrime.securesms.ApplicationContext)
}
@EntryPoint

@ -18,6 +18,7 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import network.loki.messenger.R
import org.session.libsession.database.StorageProtocol
import org.session.libsession.messaging.groups.GroupInviteException
import org.session.libsession.messaging.groups.GroupManagerV2
import org.session.libsession.utilities.ConfigFactoryProtocol
import org.session.libsignal.utilities.AccountId
@ -78,7 +79,13 @@ class EditGroupViewModel @AssistedInject constructor(
get() = groupInfo.value?.second?.mapTo(hashSetOf()) { it.accountId }.orEmpty()
fun onContactSelected(contacts: Set<AccountId>) {
performGroupOperation {
performGroupOperation(errorMessage = { err ->
if (err is GroupInviteException) {
err.format(context, storage).toString()
} else {
null
}
}) {
groupManager.inviteMembers(
groupId,
contacts.toList(),
@ -163,7 +170,7 @@ class EditGroupViewModel @AssistedInject constructor(
* This is a helper function that encapsulates the common error handling and progress tracking.
*/
private fun performGroupOperation(
genericErrorMessage: (() -> String?)? = null,
errorMessage: ((Throwable) -> String?)? = null,
operation: suspend () -> Unit) {
viewModelScope.launch {
mutableInProgress.value = true
@ -178,7 +185,7 @@ class EditGroupViewModel @AssistedInject constructor(
try {
task.await()
} catch (e: Exception) {
mutableError.value = genericErrorMessage?.invoke()
mutableError.value = errorMessage?.invoke(e)
?: context.getString(R.string.errorUnknown)
} finally {
mutableInProgress.value = false

@ -24,6 +24,7 @@ import network.loki.messenger.libsession_util.util.UserPic
import org.session.libsession.database.MessageDataProvider
import org.session.libsession.database.StorageProtocol
import org.session.libsession.database.userAuth
import org.session.libsession.messaging.groups.GroupInviteException
import org.session.libsession.messaging.groups.GroupManagerV2
import org.session.libsession.messaging.groups.GroupScope
import org.session.libsession.messaging.jobs.InviteContactsJob
@ -279,17 +280,24 @@ class GroupManagerV2Impl @Inject constructor(
// Make sure every request is successful
response.requireAllRequestsSuccessful("Failed to invite members")
} catch (e: Exception) {
// Update every member's status to "invite failed"
configFactory.withMutableGroupConfigs(group) { configs ->
// Update every member's status to "invite failed" and return group name
val groupName = configFactory.withMutableGroupConfigs(group) { configs ->
for (newMember in newMembers) {
configs.groupMembers.get(newMember.hexString)?.apply {
setInviteFailed()
configs.groupMembers.set(this)
}
}
configs.groupInfo.getName().orEmpty()
}
throw e
throw GroupInviteException(
isPromotion = false,
inviteeAccountIds = newMembers.map { it.hexString },
groupName = groupName,
underlying = e
)
}
// Send the invitation message to the new members
@ -1159,7 +1167,7 @@ class GroupManagerV2Impl @Inject constructor(
sentTimestamp = timestamp
}
MessageSender.send(message, Destination.ClosedGroup(groupId.hexString), false)
MessageSender.send(message, Address.fromSerialized(groupId.hexString))
storage.deleteGroupInfoMessages(groupId, UpdateMessageData.Kind.GroupExpirationUpdated::class.java)
storage.insertGroupInfoChange(message, groupId)

@ -0,0 +1,61 @@
package org.session.libsession.messaging.groups
import android.content.Context
import com.squareup.phrase.Phrase
import org.session.libsession.R
import org.session.libsession.database.StorageProtocol
import org.session.libsession.utilities.StringSubstitutionConstants.COUNT_KEY
import org.session.libsession.utilities.StringSubstitutionConstants.GROUP_NAME_KEY
import org.session.libsession.utilities.StringSubstitutionConstants.NAME_KEY
import org.session.libsession.utilities.StringSubstitutionConstants.OTHER_NAME_KEY
import org.session.libsession.utilities.truncateIdForDisplay
/**
* Exception that occurs during a group invite.
*
* @param isPromotion Whether the invite was a promotion.
* @param inviteeAccountIds The account IDs of the invitees that failed.
* @param groupName The name of the group.
* @param underlying The underlying exception.
*/
class GroupInviteException(
val isPromotion: Boolean,
val inviteeAccountIds: List<String>,
val groupName: String,
underlying: Throwable
) : RuntimeException(underlying) {
init {
check(inviteeAccountIds.isNotEmpty()) {
"Can not fail a group invite if there are no invitees"
}
}
fun format(context: Context, storage: StorageProtocol): CharSequence {
val getInviteeName = { accountId: String ->
storage.getContactWithAccountID(accountId)?.name ?: truncateIdForDisplay(accountId)
}
val first = inviteeAccountIds.first().let(getInviteeName)
val second = inviteeAccountIds.getOrNull(1)?.let(getInviteeName)
val third = inviteeAccountIds.getOrNull(2)?.let(getInviteeName)
if (second != null && third != null) {
return Phrase.from(context, if (isPromotion) R.string.adminPromotionFailedDescriptionMultiple else R.string.groupInviteFailedMultiple)
.put(NAME_KEY, first)
.put(COUNT_KEY, inviteeAccountIds.size - 1)
.put(GROUP_NAME_KEY, groupName)
.format()
} else if (second != null) {
return Phrase.from(context, if (isPromotion) R.string.adminPromotionFailedDescriptionTwo else R.string.groupInviteFailedTwo)
.put(NAME_KEY, first)
.put(OTHER_NAME_KEY, second)
.put(GROUP_NAME_KEY, groupName)
.format()
} else {
return Phrase.from(context, if (isPromotion) R.string.adminPromotionFailedDescription else R.string.groupInviteFailedUser)
.put(NAME_KEY, first)
.put(GROUP_NAME_KEY, groupName)
.format()
}
}
}

@ -9,6 +9,7 @@ import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.withContext
import org.session.libsession.R
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.groups.GroupInviteException
import org.session.libsession.messaging.messages.Destination
import org.session.libsession.messaging.messages.control.GroupUpdated
import org.session.libsession.messaging.sending_receiving.MessageSender
@ -111,51 +112,15 @@ class InviteContactsJob(val groupSessionId: String, val memberSessionIds: Array<
// show the failure toast
val storage = MessagingModuleConfiguration.shared.storage
val toaster = MessagingModuleConfiguration.shared.toaster
when (failures.size) {
1 -> {
val (memberId, _) = failures.first()
val firstString = storage.getContactWithAccountID(memberId)?.name
?: truncateIdForDisplay(memberId)
withContext(Dispatchers.Main) {
toaster.toast(R.string.groupInviteFailedUser, Toast.LENGTH_LONG,
mapOf(
NAME_KEY to firstString,
GROUP_NAME_KEY to groupName.orEmpty()
)
)
}
}
2 -> {
val (first, second) = failures
val firstString = first.first.let { storage.getContactWithAccountID(it) }?.name
?: truncateIdForDisplay(first.first)
val secondString = second.first.let { storage.getContactWithAccountID(it) }?.name
?: truncateIdForDisplay(second.first)
withContext(Dispatchers.Main) {
toaster.toast(R.string.groupInviteFailedTwo, Toast.LENGTH_LONG,
mapOf(
NAME_KEY to firstString,
OTHER_NAME_KEY to secondString,
GROUP_NAME_KEY to groupName.orEmpty()
)
)
}
}
else -> {
val first = failures.first()
val firstString = first.first.let { storage.getContactWithAccountID(it) }?.name
?: truncateIdForDisplay(first.first)
val remaining = failures.size - 1
withContext(Dispatchers.Main) {
toaster.toast(R.string.groupInviteFailedMultiple, Toast.LENGTH_LONG,
mapOf(
NAME_KEY to firstString,
OTHER_NAME_KEY to remaining.toString(),
GROUP_NAME_KEY to groupName.orEmpty()
)
)
}
GroupInviteException(
isPromotion = false,
inviteeAccountIds = failures.map { it.first },
groupName = groupName.orEmpty(),
underlying = failures.first().second.exceptionOrNull()!!,
).format(MessagingModuleConfiguration.shared.context, storage).let {
withContext(Dispatchers.Main) {
toaster.toast(it, Toast.LENGTH_LONG)
}
}
}

@ -2,6 +2,7 @@ package org.session.libsession.utilities
import androidx.annotation.StringRes
fun interface Toaster {
interface Toaster {
fun toast(@StringRes stringRes: Int, toastLength: Int, parameters: Map<String, String>)
fun toast(message: CharSequence, toastLength: Int)
}
Loading…
Cancel
Save