mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-25 11:05:25 +00:00
Fixed crashes leaving un-polled groups
This commit is contained in:
parent
71009c373b
commit
962df473b6
@ -360,7 +360,7 @@ object ConversationMenuHelper {
|
||||
val group = configFactory.withUserConfigs { it.userGroups.getClosedGroup(accountId.hexString) } ?: return
|
||||
val name = configFactory.withGroupConfigs(accountId) {
|
||||
it.groupInfo.getName()
|
||||
}
|
||||
} ?: group.name
|
||||
|
||||
confirmAndLeaveClosedGroup(
|
||||
context = context,
|
||||
@ -412,6 +412,7 @@ object ConversationMenuHelper {
|
||||
|
||||
doLeave()
|
||||
} catch (e: Exception) {
|
||||
Log.e("Conversation", "Error leaving group", e)
|
||||
withContext(Dispatchers.Main) {
|
||||
onLeaveFailed()
|
||||
}
|
||||
|
@ -62,6 +62,7 @@ import org.session.libsession.utilities.GroupUtil
|
||||
import org.session.libsession.utilities.ProfileKeyUtil
|
||||
import org.session.libsession.utilities.SSKEnvironment
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsession.utilities.getClosedGroup
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.session.libsession.utilities.recipients.Recipient.DisappearingState
|
||||
import org.session.libsignal.crypto.ecc.DjbECPublicKey
|
||||
@ -1004,13 +1005,8 @@ open class Storage @Inject constructor(
|
||||
it.groupMembers.all()
|
||||
}
|
||||
|
||||
|
||||
override fun getLibSessionClosedGroup(groupAccountId: String): GroupInfo.ClosedGroupInfo? {
|
||||
return configFactory.withUserConfigs { it.userGroups.getClosedGroup(groupAccountId) }
|
||||
}
|
||||
|
||||
override fun getClosedGroupDisplayInfo(groupAccountId: String): GroupDisplayInfo? {
|
||||
val groupIsAdmin = getLibSessionClosedGroup(groupAccountId)?.hasAdminKey() ?: return null
|
||||
val groupIsAdmin = configFactory.getClosedGroup(AccountId(groupAccountId))?.hasAdminKey() ?: return null
|
||||
|
||||
return configFactory.withGroupConfigs(AccountId(groupAccountId)) { configs ->
|
||||
val info = configs.groupInfo
|
||||
@ -1031,8 +1027,9 @@ open class Storage @Inject constructor(
|
||||
val sentTimestamp = message.sentTimestamp ?: clock.currentTimeMills()
|
||||
val senderPublicKey = message.sender
|
||||
val groupName = configFactory.withGroupConfigs(closedGroup) { it.groupInfo.getName() }
|
||||
?: configFactory.getClosedGroup(closedGroup)?.name
|
||||
|
||||
val updateData = UpdateMessageData.buildGroupUpdate(message, groupName) ?: return null
|
||||
val updateData = UpdateMessageData.buildGroupUpdate(message, groupName.orEmpty()) ?: return null
|
||||
|
||||
return insertUpdateControlMessage(updateData, sentTimestamp, senderPublicKey, closedGroup)
|
||||
}
|
||||
|
@ -5,11 +5,10 @@ import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.compose.setContent
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsignal.utilities.AccountId
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||
import org.thoughtcrime.securesms.groups.compose.EditGroupScreen
|
||||
import org.thoughtcrime.securesms.ui.theme.SessionMaterialTheme
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class EditGroupActivity: PassphraseRequiredActionBarActivity() {
|
||||
@ -28,7 +27,7 @@ class EditGroupActivity: PassphraseRequiredActionBarActivity() {
|
||||
setContent {
|
||||
SessionMaterialTheme {
|
||||
EditGroupScreen(
|
||||
groupSessionId = intent.getStringExtra(EXTRA_GROUP_ID)!!,
|
||||
groupId = AccountId(intent.getStringExtra(EXTRA_GROUP_ID)!!),
|
||||
onFinish = this::finish
|
||||
)
|
||||
}
|
||||
|
@ -1,30 +1,36 @@
|
||||
package org.thoughtcrime.securesms.groups
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.libsession_util.util.GroupDisplayInfo
|
||||
import network.loki.messenger.libsession_util.util.GroupMember
|
||||
import org.session.libsession.database.StorageProtocol
|
||||
import org.session.libsession.messaging.contacts.Contact
|
||||
import org.session.libsession.messaging.groups.GroupManagerV2
|
||||
import org.session.libsession.messaging.jobs.InviteContactsJob
|
||||
import org.session.libsession.messaging.jobs.JobQueue
|
||||
import org.session.libsession.utilities.ConfigUpdateNotification
|
||||
import org.session.libsignal.utilities.AccountId
|
||||
import org.thoughtcrime.securesms.dependencies.ConfigFactory
|
||||
|
||||
@ -32,7 +38,8 @@ const val MAX_GROUP_NAME_LENGTH = 100
|
||||
|
||||
@HiltViewModel(assistedFactory = EditGroupViewModel.Factory::class)
|
||||
class EditGroupViewModel @AssistedInject constructor(
|
||||
@Assisted private val groupSessionId: String,
|
||||
@Assisted private val groupId: AccountId,
|
||||
@ApplicationContext private val context: Context,
|
||||
private val storage: StorageProtocol,
|
||||
configFactory: ConfigFactory,
|
||||
private val groupManager: GroupManagerV2,
|
||||
@ -40,39 +47,52 @@ class EditGroupViewModel @AssistedInject constructor(
|
||||
// Input/Output state
|
||||
private val mutableEditingName = MutableStateFlow<String?>(null)
|
||||
|
||||
// Input: invite/promote member's intermediate states. This is needed because we don't have
|
||||
// a state that we can map into in the config system. The config system only provides "sent", "failed", etc.
|
||||
// The intermediate states are needed to show the user that the operation is in progress, and the
|
||||
// states are limited to the view model (i.e. lost if the user navigates away). This is a trade-off
|
||||
// between the complexity of the config system and the user experience.
|
||||
private val memberPendingState = MutableStateFlow<Map<AccountId, MemberPendingState>>(emptyMap())
|
||||
|
||||
// Output: The name of the group being edited. Null if it's not in edit mode, not to be confused
|
||||
// with empty string, where it's a valid editing state.
|
||||
val editingName: StateFlow<String?> get() = mutableEditingName
|
||||
|
||||
// Output: the source-of-truth group information. Other states are derived from this.
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private val groupInfo: StateFlow<Pair<GroupDisplayInfo, List<GroupMemberState>>?> =
|
||||
(configFactory.configUpdateNotifications as Flow<Any>)
|
||||
.onStart { emit(Unit) }
|
||||
.map {
|
||||
withContext(Dispatchers.Default) {
|
||||
val currentUserId = checkNotNull(storage.getUserPublicKey()) {
|
||||
"User public key is null"
|
||||
combine(
|
||||
configFactory.configUpdateNotifications
|
||||
.filterIsInstance<ConfigUpdateNotification.GroupConfigsUpdated>()
|
||||
.filter { it.groupId == groupId }
|
||||
.onStart { emit(ConfigUpdateNotification.GroupConfigsUpdated(groupId)) },
|
||||
memberPendingState
|
||||
) { _, pending ->
|
||||
withContext(Dispatchers.Default) {
|
||||
val currentUserId = checkNotNull(storage.getUserPublicKey()) {
|
||||
"User public key is null"
|
||||
}
|
||||
|
||||
val displayInfo = storage.getClosedGroupDisplayInfo(groupId.hexString)
|
||||
?: return@withContext null
|
||||
|
||||
val members = storage.getMembers(groupId.hexString)
|
||||
.asSequence()
|
||||
.filter { !it.removed }
|
||||
.mapTo(arrayListOf()) { member ->
|
||||
createGroupMember(
|
||||
member = member,
|
||||
myAccountId = currentUserId,
|
||||
amIAdmin = displayInfo.isUserAdmin,
|
||||
pendingState = pending[AccountId(member.sessionId)]
|
||||
)
|
||||
}
|
||||
|
||||
val displayInfo = storage.getClosedGroupDisplayInfo(groupSessionId)
|
||||
?: return@withContext null
|
||||
sortMembers(members, currentUserId)
|
||||
|
||||
val members = storage.getMembers(groupSessionId)
|
||||
.asSequence()
|
||||
.filter { !it.removed }
|
||||
.mapTo(mutableListOf()) { member ->
|
||||
createGroupMember(
|
||||
member = member,
|
||||
myAccountId = currentUserId,
|
||||
amIAdmin = displayInfo.isUserAdmin,
|
||||
)
|
||||
}
|
||||
|
||||
sortMembers(members, currentUserId)
|
||||
|
||||
displayInfo to members
|
||||
}
|
||||
}.stateIn(viewModelScope, SharingStarted.Eagerly, null)
|
||||
displayInfo to members
|
||||
}
|
||||
}.stateIn(viewModelScope, SharingStarted.Eagerly, null)
|
||||
|
||||
// Output: whether the group name can be edited. This is true if the group is loaded successfully.
|
||||
val canEditGroupName: StateFlow<Boolean> = groupInfo
|
||||
@ -110,6 +130,7 @@ class EditGroupViewModel @AssistedInject constructor(
|
||||
member: GroupMember,
|
||||
myAccountId: String,
|
||||
amIAdmin: Boolean,
|
||||
pendingState: MemberPendingState?
|
||||
): GroupMemberState {
|
||||
var status = ""
|
||||
var highlightStatus = false
|
||||
@ -117,24 +138,32 @@ class EditGroupViewModel @AssistedInject constructor(
|
||||
|
||||
when {
|
||||
member.sessionId == myAccountId -> {
|
||||
name = "You"
|
||||
name = context.getString(R.string.you)
|
||||
}
|
||||
|
||||
pendingState == MemberPendingState.Inviting -> {
|
||||
status = context.getString(R.string.groupInviteSending)
|
||||
}
|
||||
|
||||
pendingState == MemberPendingState.Promoting -> {
|
||||
status = context.getString(R.string.groupInviteSending)
|
||||
}
|
||||
|
||||
member.promotionPending -> {
|
||||
status = "Promotion sent"
|
||||
status = context.getString(R.string.adminPromotionSent)
|
||||
}
|
||||
|
||||
member.invitePending -> {
|
||||
status = "Invite Sent"
|
||||
status = context.getString(R.string.groupInviteSent)
|
||||
}
|
||||
|
||||
member.inviteFailed -> {
|
||||
status = "Invite Failed"
|
||||
status = context.getString(R.string.groupInviteFailed)
|
||||
highlightStatus = true
|
||||
}
|
||||
|
||||
member.promotionFailed -> {
|
||||
status = "Promotion Failed"
|
||||
status = context.getString(R.string.adminPromotionFailed)
|
||||
highlightStatus = true
|
||||
}
|
||||
}
|
||||
@ -145,7 +174,8 @@ class EditGroupViewModel @AssistedInject constructor(
|
||||
canRemove = amIAdmin && member.sessionId != myAccountId && !member.isAdminOrBeingPromoted,
|
||||
canPromote = amIAdmin && member.sessionId != myAccountId && !member.isAdminOrBeingPromoted,
|
||||
canResendPromotion = amIAdmin && member.sessionId != myAccountId && member.promotionFailed,
|
||||
canResendInvite = amIAdmin && member.sessionId != myAccountId && member.inviteFailed,
|
||||
canResendInvite = amIAdmin && member.sessionId != myAccountId &&
|
||||
(member.inviteFailed || member.invitePending),
|
||||
status = status,
|
||||
highlightStatus = highlightStatus
|
||||
)
|
||||
@ -167,30 +197,46 @@ class EditGroupViewModel @AssistedInject constructor(
|
||||
|
||||
fun onContactSelected(contacts: Set<Contact>) {
|
||||
performGroupOperation {
|
||||
groupManager.inviteMembers(
|
||||
AccountId(hexString = groupSessionId),
|
||||
contacts.map { AccountId(it.accountID) },
|
||||
shareHistory = true
|
||||
)
|
||||
try {
|
||||
// Mark the contacts as pending
|
||||
memberPendingState.update { states ->
|
||||
states + contacts.associate { AccountId(it.accountID) to MemberPendingState.Inviting }
|
||||
}
|
||||
|
||||
groupManager.inviteMembers(
|
||||
groupId,
|
||||
contacts.map { AccountId(it.accountID) },
|
||||
shareHistory = false
|
||||
)
|
||||
} finally {
|
||||
// Remove pending state (so the real state will be revealed)
|
||||
memberPendingState.update { states -> states - contacts.mapTo(hashSetOf()) { AccountId(it.accountID) } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onResendInviteClicked(contactSessionId: String) {
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
JobQueue.shared.add(InviteContactsJob(groupSessionId, arrayOf(contactSessionId)))
|
||||
}
|
||||
onContactSelected(setOf(Contact(contactSessionId)))
|
||||
}
|
||||
|
||||
fun onPromoteContact(memberSessionId: String) {
|
||||
performGroupOperation {
|
||||
groupManager.promoteMember(AccountId(groupSessionId), listOf(AccountId(memberSessionId)))
|
||||
try {
|
||||
memberPendingState.update { states ->
|
||||
states + (AccountId(memberSessionId) to MemberPendingState.Promoting)
|
||||
}
|
||||
|
||||
groupManager.promoteMember(groupId, listOf(AccountId(memberSessionId)))
|
||||
} finally {
|
||||
memberPendingState.update { states -> states - AccountId(memberSessionId) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onRemoveContact(contactSessionId: String, removeMessages: Boolean) {
|
||||
performGroupOperation {
|
||||
groupManager.removeMembers(
|
||||
groupAccountId = AccountId(groupSessionId),
|
||||
groupAccountId = groupId,
|
||||
removedMembers = listOf(AccountId(contactSessionId)),
|
||||
removeMessages = removeMessages
|
||||
)
|
||||
@ -223,7 +269,7 @@ class EditGroupViewModel @AssistedInject constructor(
|
||||
|
||||
performGroupOperation {
|
||||
if (!newName.isNullOrBlank()) {
|
||||
groupManager.setName(AccountId(groupSessionId), newName)
|
||||
groupManager.setName(groupId, newName)
|
||||
mutableEditingName.value = null
|
||||
}
|
||||
}
|
||||
@ -238,12 +284,15 @@ class EditGroupViewModel @AssistedInject constructor(
|
||||
*
|
||||
* This is a helper function that encapsulates the common error handling and progress tracking.
|
||||
*/
|
||||
private fun performGroupOperation(operation: suspend () -> Unit) {
|
||||
private fun performGroupOperation(
|
||||
genericErrorMessage: (() -> String?)? = null,
|
||||
operation: suspend () -> Unit) {
|
||||
viewModelScope.launch {
|
||||
mutableInProgress.value = true
|
||||
|
||||
// We need to use GlobalScope here because we don't want
|
||||
// any group operation to be cancelled when the view model is cleared.
|
||||
@Suppress("OPT_IN_USAGE")
|
||||
val task = GlobalScope.async {
|
||||
operation()
|
||||
}
|
||||
@ -251,7 +300,8 @@ class EditGroupViewModel @AssistedInject constructor(
|
||||
try {
|
||||
task.await()
|
||||
} catch (e: Exception) {
|
||||
mutableError.value = e.localizedMessage.orEmpty()
|
||||
mutableError.value = genericErrorMessage?.invoke()
|
||||
?: context.getString(R.string.errorUnknown)
|
||||
} finally {
|
||||
mutableInProgress.value = false
|
||||
}
|
||||
@ -260,10 +310,15 @@ class EditGroupViewModel @AssistedInject constructor(
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(groupSessionId: String): EditGroupViewModel
|
||||
fun create(groupId: AccountId): EditGroupViewModel
|
||||
}
|
||||
}
|
||||
|
||||
private enum class MemberPendingState {
|
||||
Inviting,
|
||||
Promoting,
|
||||
}
|
||||
|
||||
data class GroupMemberState(
|
||||
val accountId: String,
|
||||
val name: String,
|
||||
|
@ -741,7 +741,7 @@ class GroupManagerV2Impl @Inject constructor(
|
||||
// read the group name anymore.
|
||||
val groupName = configFactory.withGroupConfigs(groupId) { configs ->
|
||||
configs.groupInfo.getName()
|
||||
}
|
||||
} ?: group.name
|
||||
|
||||
configFactory.withMutableUserConfigs {
|
||||
it.userGroups.set(
|
||||
|
@ -44,6 +44,7 @@ import kotlinx.serialization.Serializable
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.GROUP_NAME_KEY
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.NAME_KEY
|
||||
import org.session.libsignal.utilities.AccountId
|
||||
import org.thoughtcrime.securesms.groups.EditGroupViewModel
|
||||
import org.thoughtcrime.securesms.groups.GroupMemberState
|
||||
import org.thoughtcrime.securesms.ui.AlertDialog
|
||||
@ -60,12 +61,12 @@ import org.thoughtcrime.securesms.ui.theme.bold
|
||||
|
||||
@Composable
|
||||
fun EditGroupScreen(
|
||||
groupSessionId: String,
|
||||
groupId: AccountId,
|
||||
onFinish: () -> Unit,
|
||||
) {
|
||||
val navController = rememberNavController()
|
||||
val viewModel = hiltViewModel<EditGroupViewModel, EditGroupViewModel.Factory> { factory ->
|
||||
factory.create(groupSessionId)
|
||||
factory.create(groupId)
|
||||
}
|
||||
|
||||
NavHost(navController = navController, startDestination = RouteEditGroup) {
|
||||
|
@ -328,7 +328,7 @@ interface ReadableGroupInfoConfig: ReadableConfig {
|
||||
fun getDeleteAttachmentsBefore(): Long?
|
||||
fun getDeleteBefore(): Long?
|
||||
fun getExpiryTimer(): Long
|
||||
fun getName(): String
|
||||
fun getName(): String?
|
||||
fun getCreated(): Long?
|
||||
fun getProfilePic(): UserPic
|
||||
fun isDestroyed(): Boolean
|
||||
@ -367,7 +367,7 @@ class GroupInfoConfig private constructor(pointer: Long): ConfigBase(pointer), M
|
||||
external override fun getDeleteAttachmentsBefore(): Long?
|
||||
external override fun getDeleteBefore(): Long?
|
||||
external override fun getExpiryTimer(): Long
|
||||
external override fun getName(): String
|
||||
external override fun getName(): String?
|
||||
external override fun getProfilePic(): UserPic
|
||||
external override fun isDestroyed(): Boolean
|
||||
external override fun setCreated(createdAt: Long)
|
||||
|
@ -170,7 +170,6 @@ interface StorageProtocol {
|
||||
|
||||
// Closed Groups
|
||||
fun getMembers(groupPublicKey: String): List<LibSessionGroupMember>
|
||||
fun getLibSessionClosedGroup(groupAccountId: String): GroupInfo.ClosedGroupInfo?
|
||||
fun getClosedGroupDisplayInfo(groupAccountId: String): GroupDisplayInfo?
|
||||
fun insertGroupInfoChange(message: GroupUpdated, closedGroup: AccountId): Long?
|
||||
fun insertGroupInfoLeaving(closedGroup: AccountId): Long?
|
||||
|
@ -20,6 +20,7 @@ import org.session.libsession.snode.utilities.await
|
||||
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.getClosedGroup
|
||||
import org.session.libsession.utilities.truncateIdForDisplay
|
||||
import org.session.libsignal.protos.SignalServiceProtos.DataMessage.GroupUpdateInviteMessage
|
||||
import org.session.libsignal.protos.SignalServiceProtos.DataMessage.GroupUpdateMessage
|
||||
@ -100,6 +101,7 @@ class InviteContactsJob(val groupSessionId: String, val memberSessionIds: Array<
|
||||
}
|
||||
|
||||
val groupName = configs.withGroupConfigs(sessionId) { it.groupInfo.getName() }
|
||||
?: configs.getClosedGroup(sessionId)?.name
|
||||
|
||||
val failures = results.filter { it.second.isFailure }
|
||||
// if there are failed invites, display a message
|
||||
@ -117,7 +119,7 @@ class InviteContactsJob(val groupSessionId: String, val memberSessionIds: Array<
|
||||
toaster.toast(R.string.groupInviteFailedUser, Toast.LENGTH_LONG,
|
||||
mapOf(
|
||||
NAME_KEY to firstString,
|
||||
GROUP_NAME_KEY to groupName
|
||||
GROUP_NAME_KEY to groupName.orEmpty()
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -134,7 +136,7 @@ class InviteContactsJob(val groupSessionId: String, val memberSessionIds: Array<
|
||||
mapOf(
|
||||
NAME_KEY to firstString,
|
||||
OTHER_NAME_KEY to secondString,
|
||||
GROUP_NAME_KEY to groupName
|
||||
GROUP_NAME_KEY to groupName.orEmpty()
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -149,7 +151,7 @@ class InviteContactsJob(val groupSessionId: String, val memberSessionIds: Array<
|
||||
mapOf(
|
||||
NAME_KEY to firstString,
|
||||
OTHER_NAME_KEY to remaining.toString(),
|
||||
GROUP_NAME_KEY to groupName
|
||||
GROUP_NAME_KEY to groupName.orEmpty()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -50,7 +50,6 @@ import org.session.libsignal.utilities.defaultRequiresAuth
|
||||
import org.session.libsignal.utilities.hasNamespaces
|
||||
import org.session.libsignal.utilities.hexEncodedPublicKey
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment as SignalAttachment
|
||||
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview as SignalLinkPreview
|
||||
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel as SignalQuote
|
||||
|
@ -645,7 +645,7 @@ object SnodeAPI {
|
||||
for ((req, resp) in batch.zip(responses.results)) {
|
||||
val result = runCatching {
|
||||
check(resp.code == 200) {
|
||||
"Error with code = ${resp.code}, msg = ${resp.body}"
|
||||
"Error calling \"${req.request.method}\" with code = ${resp.code}, msg = ${resp.body}"
|
||||
}
|
||||
|
||||
JsonUtil.fromJson(resp.body, req.responseType)
|
||||
|
Loading…
Reference in New Issue
Block a user