Use consolidated member state and add pending removal support (#813)

* Use consolidated member state and add pending removal support

* Updated libsession-util
pull/1706/head
SessionHero01 4 months ago committed by GitHub
parent 819b6e1056
commit bd2cbeb8e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -24,6 +24,7 @@ import org.session.libsession.utilities.ConfigFactoryProtocol
import org.session.libsession.utilities.ConfigUpdateNotification
import org.session.libsession.utilities.getMemberName
import org.session.libsignal.utilities.AccountId
import java.util.EnumSet
abstract class BaseGroupMembersViewModel (
@ -32,48 +33,35 @@ abstract class BaseGroupMembersViewModel (
private val storage: StorageProtocol,
private val configFactory: ConfigFactoryProtocol
) : ViewModel() {
// 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.
protected val memberPendingState = MutableStateFlow<Map<AccountId, MemberPendingState>>(emptyMap())
// Output: the source-of-truth group information. Other states are derived from this.
protected val groupInfo: StateFlow<Pair<GroupDisplayInfo, List<GroupMemberState>>?> =
combine(
configFactory.configUpdateNotifications
.filter {
it is ConfigUpdateNotification.GroupConfigsUpdated && it.groupId == groupId ||
it is ConfigUpdateNotification.UserConfigsMerged
}
.onStart { emit(ConfigUpdateNotification.GroupConfigsUpdated(groupId)) },
memberPendingState
) { _, pending ->
withContext(Dispatchers.Default) {
val currentUserId = AccountId(checkNotNull(storage.getUserPublicKey()) {
"User public key is null"
})
val displayInfo = storage.getClosedGroupDisplayInfo(groupId.hexString)
?: return@withContext null
val members = storage.getMembers(groupId.hexString)
.filterTo(mutableListOf()) { !it.removed }
val memberState = members.map { member ->
createGroupMember(
member = member,
myAccountId = currentUserId,
amIAdmin = displayInfo.isUserAdmin,
pendingState = pending[member.accountId]
)
}
displayInfo to sortMembers(memberState, currentUserId)
configFactory.configUpdateNotifications
.filter {
it is ConfigUpdateNotification.GroupConfigsUpdated && it.groupId == groupId ||
it is ConfigUpdateNotification.UserConfigsMerged
}
}.stateIn(viewModelScope, SharingStarted.Eagerly, null)
.onStart { emit(ConfigUpdateNotification.GroupConfigsUpdated(groupId)) }
.map { _ ->
withContext(Dispatchers.Default) {
val currentUserId = AccountId(checkNotNull(storage.getUserPublicKey()) {
"User public key is null"
})
val displayInfo = storage.getClosedGroupDisplayInfo(groupId.hexString)
?: return@withContext null
val memberState = storage.getMembers(groupId.hexString)
.map { member ->
createGroupMember(
member = member,
myAccountId = currentUserId,
amIAdmin = displayInfo.isUserAdmin,
)
}
displayInfo to sortMembers(memberState, currentUserId)
}
}.stateIn(viewModelScope, SharingStarted.Eagerly, null)
// Output: the list of the members and their state in the group.
val members: StateFlow<List<GroupMemberState>> = groupInfo
@ -84,54 +72,14 @@ abstract class BaseGroupMembersViewModel (
member: GroupMember,
myAccountId: AccountId,
amIAdmin: Boolean,
pendingState: MemberPendingState?
): GroupMemberState {
var status = GroupMemberStatus.MEMBER
var highlightStatus = false
var name = member.getMemberName(configFactory)
var isMyself = false
when {
member.accountIdString() == myAccountId.hexString -> {
name = context.getString(R.string.you)
isMyself = true
}
member.removed -> {
status = GroupMemberStatus.REMOVED
}
pendingState == MemberPendingState.Inviting -> {
status = GroupMemberStatus.INVITE_SENDING
}
val name = member.getMemberName(configFactory)
val isMyself = member.accountId == myAccountId
pendingState == MemberPendingState.Promoting -> {
status = GroupMemberStatus.PROMOTION_SENDING
}
member.status == GroupMember.Status.PROMOTION_SENT -> {
status = GroupMemberStatus.PROMOTION_SENT
}
member.status == GroupMember.Status.INVITE_SENT -> {
status = GroupMemberStatus.INVITE_SENT
}
member.status == GroupMember.Status.INVITE_FAILED -> {
status = GroupMemberStatus.INVITE_FAILED
highlightStatus = true
}
member.status == GroupMember.Status.REMOVED ||
member.status == GroupMember.Status.REMOVED_INCLUDING_MESSAGES -> {
status = GroupMemberStatus.REMOVAL_PENDING
}
member.status == GroupMember.Status.PROMOTION_FAILED -> {
status = GroupMemberStatus.PROMOTION_FAILED
highlightStatus = true
}
}
val highlightStatus = member.status in EnumSet.of(
GroupMember.Status.INVITE_FAILED,
GroupMember.Status.PROMOTION_FAILED
)
return GroupMemberState(
accountId = member.accountId,
@ -145,7 +93,7 @@ abstract class BaseGroupMembersViewModel (
canResendInvite = amIAdmin && member.accountId != myAccountId
&& !member.removed
&& (member.status == GroupMember.Status.INVITE_SENT || member.status == GroupMember.Status.INVITE_FAILED),
status = status,
status = member.status?.takeIf { !isMyself }, // Status is only meant for other members
highlightStatus = highlightStatus,
showAsAdmin = member.isAdminOrBeingPromoted,
clickable = !isMyself
@ -156,11 +104,11 @@ abstract class BaseGroupMembersViewModel (
members.sortedWith(
compareBy<GroupMemberState>{
when (it.status) {
GroupMemberStatus.INVITE_FAILED -> 0 // Failed invite comes first
GroupMemberStatus.INVITE_SENDING -> 1 // then "Sending invite"
GroupMemberStatus.INVITE_SENT -> 2 // then "Invite sent"
GroupMemberStatus.PROMOTION_SENDING -> 3 // then "Sending promotion"
GroupMemberStatus.PROMOTION_SENT -> 4 // then "Promotion sent"
GroupMember.Status.INVITE_FAILED -> 0 // Failed invite comes first
GroupMember.Status.INVITE_NOT_SENT -> 1 // then "Sending invite"
GroupMember.Status.INVITE_SENT -> 2 // then "Invite sent"
GroupMember.Status.PROMOTION_NOT_SENT -> 3 // then "Sending promotion"
GroupMember.Status.PROMOTION_SENT -> 4 // then "Promotion sent"
else -> 5
}
}
@ -174,17 +122,12 @@ abstract class BaseGroupMembersViewModel (
interface Factory {
fun create(groupId: AccountId): EditGroupViewModel
}
protected enum class MemberPendingState {
Inviting,
Promoting,
}
}
data class GroupMemberState(
val accountId: AccountId,
val name: String,
val status: GroupMemberStatus,
val status: GroupMember.Status?,
val highlightStatus: Boolean,
val showAsAdmin: Boolean,
val canResendInvite: Boolean,
@ -196,29 +139,22 @@ data class GroupMemberState(
val canEdit: Boolean get() = canRemove || canPromote || canResendInvite || canResendPromotion
}
enum class GroupMemberStatus{
INVITE_FAILED,
INVITE_SENDING,
INVITE_SENT,
PROMOTION_FAILED,
PROMOTION_SENDING,
PROMOTION_SENT,
REMOVAL_PENDING,
REMOVED,
MEMBER;
// Function to get the label dynamically using the context
fun getLabel(context: Context): String {
return when (this) {
INVITE_FAILED -> context.getString(R.string.groupInviteFailed)
INVITE_SENDING -> context.resources.getQuantityString(R.plurals.groupInviteSending, 1)
INVITE_SENT -> context.getString(R.string.groupInviteSent)
PROMOTION_FAILED -> context.getString(R.string.adminPromotionFailed)
PROMOTION_SENDING -> context.resources.getQuantityString(R.plurals.adminSendingPromotion, 1)
PROMOTION_SENT -> context.getString(R.string.adminPromotionSent)
REMOVAL_PENDING -> context.getString(R.string.groupPendingRemoval)
REMOVED, MEMBER -> ""
}
// Function to get the label dynamically using the context
fun GroupMember.Status.getLabel(context: Context): String {
return when (this) {
GroupMember.Status.INVITE_FAILED -> context.getString(R.string.groupInviteFailed)
GroupMember.Status.INVITE_NOT_SENT -> context.resources.getQuantityString(R.plurals.groupInviteSending, 1)
GroupMember.Status.INVITE_SENT -> context.getString(R.string.groupInviteSent)
GroupMember.Status.PROMOTION_FAILED -> context.getString(R.string.adminPromotionFailed)
GroupMember.Status.PROMOTION_NOT_SENT -> context.resources.getQuantityString(R.plurals.adminSendingPromotion, 1)
GroupMember.Status.PROMOTION_SENT -> context.getString(R.string.adminPromotionSent)
GroupMember.Status.REMOVED,
GroupMember.Status.REMOVED_UNKNOWN,
GroupMember.Status.REMOVED_INCLUDING_MESSAGES -> context.getString(R.string.groupPendingRemoval)
GroupMember.Status.INVITE_UNKNOWN,
GroupMember.Status.INVITE_ACCEPTED,
GroupMember.Status.PROMOTION_UNKNOWN,
GroupMember.Status.PROMOTION_ACCEPTED -> ""
}
}

@ -80,21 +80,11 @@ class EditGroupViewModel @AssistedInject constructor(
fun onContactSelected(contacts: Set<AccountId>) {
performGroupOperation {
try {
// Mark the contacts as pending
memberPendingState.update { states ->
states + contacts.associateWith { MemberPendingState.Inviting }
}
groupManager.inviteMembers(
groupId,
contacts.toList(),
shareHistory = false
)
} finally {
// Remove pending state (so the real state will be revealed)
memberPendingState.update { states -> states - contacts }
}
groupManager.inviteMembers(
groupId,
contacts.toList(),
shareHistory = false
)
}
}
@ -104,15 +94,7 @@ class EditGroupViewModel @AssistedInject constructor(
fun onPromoteContact(memberSessionId: AccountId) {
performGroupOperation {
try {
memberPendingState.update { states ->
states + (memberSessionId to MemberPendingState.Promoting)
}
groupManager.promoteMember(groupId, listOf(memberSessionId))
} finally {
memberPendingState.update { states -> states - memberSessionId }
}
groupManager.promoteMember(groupId, listOf(memberSessionId))
}
}

@ -227,6 +227,7 @@ class GroupManagerV2Impl @Inject constructor(
member.setSupplement(shareHistory)
}
toSet.setInvited()
configs.groupMembers.set(toSet)
}
@ -270,7 +271,7 @@ class GroupManagerV2Impl @Inject constructor(
configFactory.withMutableGroupConfigs(group) { configs ->
for (newMember in newMembers) {
configs.groupMembers.get(newMember.hexString)?.apply {
setInvited(failed = true)
setInviteFailed()
configs.groupMembers.set(this)
}
}
@ -478,7 +479,15 @@ class GroupManagerV2Impl @Inject constructor(
members: List<AccountId>
): Unit = withContext(dispatcher + SupervisorJob()) {
val adminKey = requireAdminAccess(group)
val groupName = configFactory.withGroupConfigs(group) { it.groupInfo.getName() }
val groupName = configFactory.withMutableGroupConfigs(group) { configs ->
// Update the group member's promotion status
members.asSequence()
.mapNotNull { configs.groupMembers.get(it.hexString) }
.onEach(GroupMember::setPromoted)
.forEach(configs.groupMembers::set)
configs.groupInfo.getName()
}
// Send out the promote message to the members concurrently
val promoteMessage = GroupUpdated(
@ -800,7 +809,7 @@ class GroupManagerV2Impl @Inject constructor(
val member = configs.groupMembers.get(sender.hexString)
if (member != null) {
configs.groupMembers.set(member.apply {
setAccepted()
setInviteAccepted()
})
} else {
Log.e(TAG, "User wasn't in the group membership to add!")

@ -42,12 +42,13 @@ import com.squareup.phrase.Phrase
import kotlinx.serialization.Serializable
import network.loki.messenger.BuildConfig
import network.loki.messenger.R
import network.loki.messenger.libsession_util.util.GroupMember
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.groups.GroupMemberStatus
import org.thoughtcrime.securesms.groups.getLabel
import org.thoughtcrime.securesms.ui.AlertDialog
import org.thoughtcrime.securesms.ui.DialogButtonModel
import org.thoughtcrime.securesms.ui.GetString
@ -425,7 +426,7 @@ fun EditMemberItem(
MemberItem(
accountId = member.accountId,
title = member.name,
subtitle = member.status.getLabel(LocalContext.current),
subtitle = member.status?.getLabel(LocalContext.current),
subtitleColor = if (member.highlightStatus) {
LocalColors.current.danger
} else {
@ -451,7 +452,7 @@ private fun EditGroupPreview3() {
val oneMember = GroupMemberState(
accountId = AccountId("05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234"),
name = "Test User",
status = GroupMemberStatus.INVITE_SENT,
status = GroupMember.Status.INVITE_SENT,
highlightStatus = false,
canPromote = true,
canRemove = true,
@ -463,7 +464,7 @@ private fun EditGroupPreview3() {
val twoMember = GroupMemberState(
accountId = AccountId("05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1235"),
name = "Test User 2",
status = GroupMemberStatus.PROMOTION_FAILED,
status = GroupMember.Status.PROMOTION_FAILED,
highlightStatus = true,
canPromote = true,
canRemove = true,
@ -475,7 +476,7 @@ private fun EditGroupPreview3() {
val threeMember = GroupMemberState(
accountId = AccountId("05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1236"),
name = "Test User 3",
status = GroupMemberStatus.MEMBER,
status = null,
highlightStatus = false,
canPromote = true,
canRemove = true,
@ -525,7 +526,7 @@ private fun EditGroupPreview() {
val oneMember = GroupMemberState(
accountId = AccountId("05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234"),
name = "Test User",
status = GroupMemberStatus.INVITE_SENT,
status = GroupMember.Status.INVITE_SENT,
highlightStatus = false,
canPromote = true,
canRemove = true,
@ -537,7 +538,7 @@ private fun EditGroupPreview() {
val twoMember = GroupMemberState(
accountId = AccountId("05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1235"),
name = "Test User 2",
status = GroupMemberStatus.PROMOTION_FAILED,
status = GroupMember.Status.PROMOTION_FAILED,
highlightStatus = true,
canPromote = true,
canRemove = true,
@ -549,7 +550,7 @@ private fun EditGroupPreview() {
val threeMember = GroupMemberState(
accountId = AccountId("05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1236"),
name = "Test User 3",
status = GroupMemberStatus.MEMBER,
status = null,
highlightStatus = false,
canPromote = true,
canRemove = true,
@ -599,7 +600,7 @@ private fun EditGroupEditNamePreview() {
val oneMember = GroupMemberState(
accountId = AccountId("05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234"),
name = "Test User",
status = GroupMemberStatus.INVITE_SENT,
status = GroupMember.Status.INVITE_SENT,
highlightStatus = false,
canPromote = true,
canRemove = true,
@ -611,7 +612,7 @@ private fun EditGroupEditNamePreview() {
val twoMember = GroupMemberState(
accountId = AccountId("05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1235"),
name = "Test User 2",
status = GroupMemberStatus.PROMOTION_FAILED,
status = GroupMember.Status.PROMOTION_FAILED,
highlightStatus = true,
canPromote = true,
canRemove = true,
@ -623,7 +624,7 @@ private fun EditGroupEditNamePreview() {
val threeMember = GroupMemberState(
accountId = AccountId("05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1236"),
name = "Test User 3",
status = GroupMemberStatus.MEMBER,
status = null,
highlightStatus = false,
canPromote = true,
canRemove = true,

@ -1,62 +1,26 @@
package org.thoughtcrime.securesms.groups.compose
import android.widget.Toast
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.squareup.phrase.Phrase
import network.loki.messenger.BuildConfig
import network.loki.messenger.R
import org.session.libsession.utilities.StringSubstitutionConstants.GROUP_NAME_KEY
import org.session.libsession.utilities.StringSubstitutionConstants.NAME_KEY
import network.loki.messenger.libsession_util.util.GroupMember
import org.session.libsignal.utilities.AccountId
import org.thoughtcrime.securesms.groups.GroupMemberState
import org.thoughtcrime.securesms.groups.GroupMemberStatus
import org.thoughtcrime.securesms.groups.GroupMembersViewModel
import org.thoughtcrime.securesms.ui.AlertDialog
import org.thoughtcrime.securesms.ui.DialogButtonModel
import org.thoughtcrime.securesms.ui.GetString
import org.thoughtcrime.securesms.ui.components.ActionSheet
import org.thoughtcrime.securesms.ui.components.ActionSheetItemData
import org.thoughtcrime.securesms.groups.getLabel
import org.thoughtcrime.securesms.ui.components.BackAppBar
import org.thoughtcrime.securesms.ui.components.PrimaryOutlineButton
import org.thoughtcrime.securesms.ui.components.SessionOutlinedTextField
import org.thoughtcrime.securesms.ui.qaTag
import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.theme.LocalType
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
@Composable
@ -98,7 +62,7 @@ fun GroupMembers(
MemberItem(
accountId = member.accountId,
title = member.name,
subtitle = member.status.getLabel(LocalContext.current),
subtitle = member.status?.getLabel(LocalContext.current),
subtitleColor = if (member.highlightStatus) {
LocalColors.current.danger
} else {
@ -120,7 +84,7 @@ private fun EditGroupPreview() {
val oneMember = GroupMemberState(
accountId = AccountId("05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234"),
name = "Test User",
status = GroupMemberStatus.INVITE_SENT,
status = GroupMember.Status.INVITE_SENT,
highlightStatus = false,
canPromote = true,
canRemove = true,
@ -132,7 +96,7 @@ private fun EditGroupPreview() {
val twoMember = GroupMemberState(
accountId = AccountId("05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1235"),
name = "Test User 2",
status = GroupMemberStatus.PROMOTION_FAILED,
status = GroupMember.Status.PROMOTION_FAILED,
highlightStatus = true,
canPromote = true,
canRemove = true,
@ -144,7 +108,7 @@ private fun EditGroupPreview() {
val threeMember = GroupMemberState(
accountId = AccountId("05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1236"),
name = "Test User 3",
status = GroupMemberStatus.MEMBER,
status = null,
highlightStatus = false,
canPromote = true,
canRemove = true,

@ -1 +1 @@
Subproject commit 1ed9170c7329512f78b25cb2bcb7ecaa9a416d39
Subproject commit 43b1c6c341ee8739a8678c631d0713136dbfd05f

@ -89,16 +89,29 @@ Java_network_loki_messenger_libsession_1util_GroupMembersConfig_set(JNIEnv *env,
extern "C"
JNIEXPORT void JNICALL
Java_network_loki_messenger_libsession_1util_util_GroupMember_setInvited(JNIEnv *env,
jobject thiz,
jboolean failed) {
ptrToMember(env, thiz)->set_invited(failed);
jobject thiz) {
ptrToMember(env, thiz)->invite_status = session::config::groups::STATUS_NOT_SENT;
}
extern "C"
JNIEXPORT void JNICALL
Java_network_loki_messenger_libsession_1util_util_GroupMember_setInviteSent(JNIEnv *env,
jobject thiz) {
ptrToMember(env, thiz)->set_invite_sent();
}
extern "C"
JNIEXPORT void JNICALL
Java_network_loki_messenger_libsession_1util_util_GroupMember_setInviteFailed(JNIEnv *env,
jobject thiz) {
ptrToMember(env, thiz)->set_invite_failed();
}
extern "C"
JNIEXPORT void JNICALL
Java_network_loki_messenger_libsession_1util_util_GroupMember_setAccepted(JNIEnv *env,
Java_network_loki_messenger_libsession_1util_util_GroupMember_setInviteAccepted(JNIEnv *env,
jobject thiz) {
ptrToMember(env, thiz)->set_accepted();
ptrToMember(env, thiz)->set_invite_accepted();
}
extern "C"
@ -210,4 +223,5 @@ Java_network_loki_messenger_libsession_1util_util_GroupMember_setSupplement(JNIE
jobject thiz,
jboolean supplement) {
ptrToMember(env, thiz)->supplement = supplement;
}
}

@ -135,7 +135,7 @@ inline jobject serialize_closed_group_info(JNIEnv* env, session::config::group_i
jclass group_info_class = env->FindClass("network/loki/messenger/libsession_util/util/GroupInfo$ClosedGroupInfo");
jmethodID constructor = env->GetMethodID(group_info_class, "<init>","(Lorg/session/libsignal/utilities/AccountId;[B[BJZLjava/lang/String;Z)V");
jobject return_object = env->NewObject(group_info_class,constructor,
session_id, admin_bytes, auth_bytes, (jlong)info.priority, info.invited, name, info.isDestroyed());
session_id, admin_bytes, auth_bytes, (jlong)info.priority, info.invited, name, info.is_destroyed());
return return_object;
}
@ -167,7 +167,7 @@ inline session::config::group_info deserialize_closed_group_info(JNIEnv* env, jo
group_info.invited = env->GetBooleanField(info_serialized, invited_field);
group_info.name = name;
if (env->GetBooleanField(info_serialized, destroy_field)) {
group_info.markDestroyed();
group_info.mark_destroyed();
}
return group_info;

@ -9,19 +9,26 @@ import java.util.EnumSet
* Note: unlike a read-only data class, this class is mutable and it is not thread-safe
* in general. You have to synchronize access to it if you are going to use it in multiple threads.
*/
class GroupMember private constructor(@Suppress("CanBeParameter") private val nativePtr: Long) {
class GroupMember private constructor(
// Constructed and used by native code.
@Suppress("CanBeParameter") private val nativePtr: Long
) {
init {
if (nativePtr == 0L) {
throw NullPointerException("Native pointer is null")
}
}
external fun setInvited(failed: Boolean = false)
external fun setAccepted()
external fun setInvited()
external fun setInviteSent()
external fun setInviteFailed()
external fun setInviteAccepted()
external fun setPromoted()
external fun setPromotionSent()
external fun setPromotionFailed()
external fun setPromotionAccepted()
external fun setRemoved(alsoRemoveMessages: Boolean)
private external fun statusInt(): Int

@ -91,7 +91,11 @@ class InviteContactsJob(val groupSessionId: String, val memberSessionIds: Array<
configs.withMutableGroupConfigs(sessionId) { configs ->
results.forEach { (memberSessionId, result) ->
configs.groupMembers.get(memberSessionId)?.let { member ->
member.setInvited(failed = result.isFailure)
if (result.isFailure) {
member.setInviteFailed()
} else {
member.setInviteSent()
}
configs.groupMembers.set(member)
}
}

Loading…
Cancel
Save