From c04e4559b3a6cca99873fade24555814637814ad Mon Sep 17 00:00:00 2001 From: SessionHero01 <180888785+SessionHero01@users.noreply.github.com> Date: Wed, 15 Jan 2025 16:37:10 +1100 Subject: [PATCH] Integrate the group member status change (#874) --- .../v2/mention/MentionViewModel.kt | 5 +- .../securesms/dependencies/ConfigFactory.kt | 2 - .../groups/BaseGroupMembersViewModel.kt | 31 ++++++----- .../securesms/groups/GroupManagerV2Impl.kt | 3 +- .../groups/handler/AdminStateSync.kt | 4 +- .../handler/RemoveGroupMemberHandler.kt | 26 +++++---- libsession-util/libsession-util | 2 +- libsession-util/src/main/cpp/group_keys.cpp | 46 ++++++++-------- .../src/main/cpp/group_members.cpp | 21 +++++--- .../loki/messenger/libsession_util/Config.kt | 14 +++++ .../libsession_util/util/GroupMember.kt | 53 ++++++++++--------- 11 files changed, 122 insertions(+), 85 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/mention/MentionViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/mention/MentionViewModel.kt index eadd9d35c4..eadfd0f81c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/mention/MentionViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/mention/MentionViewModel.kt @@ -26,6 +26,7 @@ import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext +import network.loki.messenger.libsession_util.allWithStatus import org.session.libsession.messaging.contacts.Contact import org.session.libsession.utilities.ConfigFactoryProtocol import org.session.libsignal.utilities.AccountId @@ -114,7 +115,9 @@ class MentionViewModel( } } else if (recipient.isGroupV2Recipient) { configFactory.withGroupConfigs(AccountId(recipient.address.serialize())) { - it.groupMembers.all().filterTo(hashSetOf()) { it.isAdminOrBeingPromoted } + it.groupMembers.allWithStatus() + .filter { (member, status) -> member.isAdminOrBeingPromoted(status) } + .mapTo(hashSetOf()) { (member, _) -> member.accountId.toString() } } } else { emptySet() diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt index f550abd789..2180d4a8ea 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt @@ -5,7 +5,6 @@ import dagger.Lazy import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.launch import network.loki.messenger.libsession_util.ConfigBase @@ -49,7 +48,6 @@ import org.session.libsignal.crypto.ecc.DjbECPublicKey import org.session.libsignal.utilities.AccountId import org.session.libsignal.utilities.Hex import org.session.libsignal.utilities.IdPrefix -import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.toHexString import org.thoughtcrime.securesms.configs.ConfigToDatabaseSync import org.thoughtcrime.securesms.database.ConfigDatabase diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/BaseGroupMembersViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/BaseGroupMembersViewModel.kt index 93c96ff13a..574f01e2f4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/BaseGroupMembersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/BaseGroupMembersViewModel.kt @@ -6,7 +6,6 @@ import androidx.lifecycle.viewModelScope import dagger.assisted.AssistedFactory import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine @@ -17,6 +16,7 @@ import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext import network.loki.messenger.R +import network.loki.messenger.libsession_util.allWithStatus import network.loki.messenger.libsession_util.util.GroupDisplayInfo import network.loki.messenger.libsession_util.util.GroupMember import org.session.libsession.database.StorageProtocol @@ -50,14 +50,16 @@ abstract class BaseGroupMembersViewModel ( val displayInfo = storage.getClosedGroupDisplayInfo(groupId.hexString) ?: return@withContext null - val memberState = storage.getMembers(groupId.hexString) - .map { member -> + val memberState = configFactory.withGroupConfigs(groupId) { it.groupMembers.allWithStatus() } + .map { (member, status) -> createGroupMember( member = member, + status = status, myAccountId = currentUserId, amIAdmin = displayInfo.isUserAdmin, ) } + .toList() displayInfo to sortMembers(memberState, currentUserId) } @@ -70,6 +72,7 @@ abstract class BaseGroupMembersViewModel ( private fun createGroupMember( member: GroupMember, + status: GroupMember.Status, myAccountId: AccountId, amIAdmin: Boolean, ): GroupMemberState { @@ -80,7 +83,7 @@ abstract class BaseGroupMembersViewModel ( member.getMemberName(configFactory) } - val highlightStatus = member.status in EnumSet.of( + val highlightStatus = status in EnumSet.of( GroupMember.Status.INVITE_FAILED, GroupMember.Status.PROMOTION_FAILED ) @@ -89,17 +92,17 @@ abstract class BaseGroupMembersViewModel ( accountId = member.accountId, name = name, canRemove = amIAdmin && member.accountId != myAccountId - && !member.isAdminOrBeingPromoted && !member.removed, + && !member.isAdminOrBeingPromoted(status) && !member.isRemoved(status), canPromote = amIAdmin && member.accountId != myAccountId - && !member.isAdminOrBeingPromoted && !member.removed, + && !member.isAdminOrBeingPromoted(status) && !member.isRemoved(status), canResendPromotion = amIAdmin && member.accountId != myAccountId - && member.status == GroupMember.Status.PROMOTION_FAILED && !member.removed, + && status == GroupMember.Status.PROMOTION_FAILED && !member.isRemoved(status), canResendInvite = amIAdmin && member.accountId != myAccountId - && !member.removed - && (member.status == GroupMember.Status.INVITE_SENT || member.status == GroupMember.Status.INVITE_FAILED), - status = member.status?.takeIf { !isMyself }, // Status is only meant for other members + && !member.isRemoved(status) + && (status == GroupMember.Status.INVITE_SENT || status == GroupMember.Status.INVITE_FAILED), + status = status.takeIf { !isMyself }, // Status is only meant for other members highlightStatus = highlightStatus, - showAsAdmin = member.isAdminOrBeingPromoted, + showAsAdmin = member.isAdminOrBeingPromoted(status), clickable = !isMyself ) } @@ -147,10 +150,10 @@ data class GroupMemberState( 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.INVENT_SENDING -> 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_SENDING -> context.resources.getQuantityString(R.plurals.adminSendingPromotion, 1) GroupMember.Status.PROMOTION_SENT -> context.getString(R.string.adminPromotionSent) GroupMember.Status.REMOVED, GroupMember.Status.REMOVED_UNKNOWN, @@ -158,6 +161,8 @@ fun GroupMember.Status.getLabel(context: Context): String { GroupMember.Status.INVITE_UNKNOWN, GroupMember.Status.INVITE_ACCEPTED, + GroupMember.Status.INVITE_NOT_SENT, + GroupMember.Status.PROMOTION_NOT_SENT, GroupMember.Status.PROMOTION_UNKNOWN, GroupMember.Status.PROMOTION_ACCEPTED -> "" } diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2Impl.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2Impl.kt index 5ea00b2a5e..5b0e94abcd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2Impl.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2Impl.kt @@ -218,7 +218,8 @@ class GroupManagerV2Impl @Inject constructor( for (newMember in newMembers) { val toSet = configs.groupMembers.get(newMember.hexString) ?.also { existing -> - if (existing.status == GroupMember.Status.INVITE_FAILED || existing.status == GroupMember.Status.INVITE_SENT) { + val status = configs.groupMembers.status(existing) + if (status == GroupMember.Status.INVITE_FAILED || status == GroupMember.Status.INVITE_SENT) { existing.setSupplement(shareHistory) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/handler/AdminStateSync.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/handler/AdminStateSync.kt index 3fbb906170..77b14ce680 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/handler/AdminStateSync.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/handler/AdminStateSync.kt @@ -3,14 +3,12 @@ package org.thoughtcrime.securesms.groups.handler import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.launch import network.loki.messenger.libsession_util.util.GroupInfo import network.loki.messenger.libsession_util.util.GroupMember import org.session.libsession.utilities.ConfigFactoryProtocol import org.session.libsession.utilities.ConfigUpdateNotification import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsession.utilities.getGroup import org.session.libsignal.utilities.AccountId import java.util.EnumSet import javax.inject.Inject @@ -71,7 +69,7 @@ class AdminStateSync @Inject constructor( private fun isMemberPromotionPending(groupId: AccountId, localNumber: String): Boolean { return configFactory.withGroupConfigs(groupId) { groupConfigs -> - val status = groupConfigs.groupMembers.get(localNumber)?.status + val status = groupConfigs.groupMembers.get(localNumber)?.let(groupConfigs.groupMembers::status) status != null && status in EnumSet.of( GroupMember.Status.PROMOTION_SENT, GroupMember.Status.PROMOTION_FAILED, diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/handler/RemoveGroupMemberHandler.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/handler/RemoveGroupMemberHandler.kt index f7e713c155..f5fc97b0aa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/handler/RemoveGroupMemberHandler.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/handler/RemoveGroupMemberHandler.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch import network.loki.messenger.R import network.loki.messenger.libsession_util.ReadableGroupKeysConfig +import network.loki.messenger.libsession_util.allWithStatus import network.loki.messenger.libsession_util.util.GroupMember import network.loki.messenger.libsession_util.util.Sodium import org.session.libsession.database.MessageDataProvider @@ -105,7 +106,10 @@ class RemoveGroupMemberHandler @Inject constructor( val groupAuth = OwnedSwarmAuth.ofClosedGroup(groupAccountId, adminKey) val (pendingRemovals, batchCalls) = configFactory.withGroupConfigs(groupAccountId) { configs -> - val pendingRemovals = configs.groupMembers.all().filter { it.removed } + val pendingRemovals = configs.groupMembers.allWithStatus() + .filter { (member, status) -> member.isRemoved(status) } + .toList() + if (pendingRemovals.isEmpty()) { // Skip if there are no pending removals return@withGroupConfigs pendingRemovals to emptyList() @@ -124,8 +128,8 @@ class RemoveGroupMemberHandler @Inject constructor( calls += checkNotNull( SnodeAPI.buildAuthenticatedRevokeSubKeyBatchRequest( groupAdminAuth = groupAuth, - subAccountTokens = pendingRemovals.map { - configs.groupKeys.getSubAccountToken(it.accountId) + subAccountTokens = pendingRemovals.map { (member, _) -> + configs.groupKeys.getSubAccountToken(member.accountId) } ) ) { "Fail to create a revoke request" } @@ -135,7 +139,7 @@ class RemoveGroupMemberHandler @Inject constructor( namespace = Namespace.REVOKED_GROUP_MESSAGES(), message = buildGroupKickMessage( groupAccountId.hexString, - pendingRemovals, + pendingRemovals.map { it.first }, configs.groupKeys, adminKey ), @@ -143,7 +147,7 @@ class RemoveGroupMemberHandler @Inject constructor( ) // Call No 3. Conditionally send the `GroupUpdateDeleteMemberContent` - if (pendingRemovals.any { it.shouldRemoveMessages }) { + if (pendingRemovals.any { (member, status) -> member.shouldRemoveMessages(status) }) { calls += SnodeAPI.buildAuthenticatedStoreBatchInfo( namespace = Namespace.CLOSED_GROUP_MESSAGES(), message = buildDeleteGroupMemberContentMessage( @@ -151,8 +155,8 @@ class RemoveGroupMemberHandler @Inject constructor( groupAccountId = groupAccountId.hexString, memberSessionIDs = pendingRemovals .asSequence() - .filter { it.shouldRemoveMessages } - .map { it.accountIdString() }, + .filter { (member, status) -> member.shouldRemoveMessages(status) } + .map { (member, _) -> member.accountIdString() }, ), auth = groupAuth, ) @@ -179,8 +183,8 @@ class RemoveGroupMemberHandler @Inject constructor( // The essential part of the operation has been successful once we get to this point, // now we can go ahead and update the configs configFactory.withMutableGroupConfigs(groupAccountId) { configs -> - pendingRemovals.forEach { - configs.groupMembers.erase(it.accountIdString()) + pendingRemovals.forEach { (member, _) -> + configs.groupMembers.erase(member.accountIdString()) } configs.rekey() } @@ -191,12 +195,12 @@ class RemoveGroupMemberHandler @Inject constructor( // Try to delete members' message. It's ok to fail as they will be re-tried in different // cases (a.k.a the GroupUpdateDeleteMemberContent message handling) and could be by different admins. - val deletingMessagesForMembers = pendingRemovals.filter { it.shouldRemoveMessages } + val deletingMessagesForMembers = pendingRemovals.filter { (member, status) -> member.shouldRemoveMessages(status) } if (deletingMessagesForMembers.isNotEmpty()) { val threadId = storage.getThreadId(Address.fromSerialized(groupAccountId.hexString)) if (threadId != null) { val until = clock.currentTimeMills() - for (member in deletingMessagesForMembers) { + for ((member, _) in deletingMessagesForMembers) { try { messageDataProvider.markUserMessagesAsDeleted( threadId = threadId, diff --git a/libsession-util/libsession-util b/libsession-util/libsession-util index 43b1c6c341..b1bd153a4e 160000 --- a/libsession-util/libsession-util +++ b/libsession-util/libsession-util @@ -1 +1 @@ -Subproject commit 43b1c6c341ee8739a8678c631d0713136dbfd05f +Subproject commit b1bd153a4ef7214f60e3a5f4ce7d939e6ac22024 diff --git a/libsession-util/src/main/cpp/group_keys.cpp b/libsession-util/src/main/cpp/group_keys.cpp index 31d6a4fe5a..c877fd785e 100644 --- a/libsession-util/src/main/cpp/group_keys.cpp +++ b/libsession-util/src/main/cpp/group_keys.cpp @@ -21,33 +21,35 @@ Java_network_loki_messenger_libsession_1util_GroupKeysConfig_00024Companion_newI jbyteArray initial_dump, jlong info_pointer, jlong members_pointer) { - std::lock_guard lock{util::util_mutex_}; - auto user_key_bytes = util::ustring_from_bytes(env, user_secret_key); - auto pub_key_bytes = util::ustring_from_bytes(env, group_public_key); - std::optional secret_key_optional{std::nullopt}; - std::optional initial_dump_optional{std::nullopt}; + return jni_utils::run_catching_cxx_exception_or_throws(env, [=] { + std::lock_guard lock{util::util_mutex_}; + auto user_key_bytes = util::ustring_from_bytes(env, user_secret_key); + auto pub_key_bytes = util::ustring_from_bytes(env, group_public_key); + std::optional secret_key_optional{std::nullopt}; + std::optional initial_dump_optional{std::nullopt}; - if (group_secret_key && env->GetArrayLength(group_secret_key) > 0) { - auto secret_key_bytes = util::ustring_from_bytes(env, group_secret_key); - secret_key_optional = secret_key_bytes; - } + if (group_secret_key && env->GetArrayLength(group_secret_key) > 0) { + auto secret_key_bytes = util::ustring_from_bytes(env, group_secret_key); + secret_key_optional = secret_key_bytes; + } - if (initial_dump && env->GetArrayLength(initial_dump) > 0) { - auto initial_dump_bytes = util::ustring_from_bytes(env, initial_dump); - initial_dump_optional = initial_dump_bytes; - } + if (initial_dump && env->GetArrayLength(initial_dump) > 0) { + auto initial_dump_bytes = util::ustring_from_bytes(env, initial_dump); + initial_dump_optional = initial_dump_bytes; + } - auto info = reinterpret_cast(info_pointer); - auto members = reinterpret_cast(members_pointer); + auto info = reinterpret_cast(info_pointer); + auto members = reinterpret_cast(members_pointer); - auto* keys = new session::config::groups::Keys(user_key_bytes, - pub_key_bytes, - secret_key_optional, - initial_dump_optional, - *info, - *members); + auto* keys = new session::config::groups::Keys(user_key_bytes, + pub_key_bytes, + secret_key_optional, + initial_dump_optional, + *info, + *members); - return reinterpret_cast(keys); + return reinterpret_cast(keys); + }); } extern "C" diff --git a/libsession-util/src/main/cpp/group_members.cpp b/libsession-util/src/main/cpp/group_members.cpp index f12bcab7eb..4179e89608 100644 --- a/libsession-util/src/main/cpp/group_members.cpp +++ b/libsession-util/src/main/cpp/group_members.cpp @@ -149,12 +149,6 @@ Java_network_loki_messenger_libsession_1util_util_GroupMember_setRemoved(JNIEnv ptrToMember(env, thiz)->set_removed(also_remove_messages); } -extern "C" -JNIEXPORT jint JNICALL -Java_network_loki_messenger_libsession_1util_util_GroupMember_statusInt(JNIEnv *env, jobject thiz) { - return static_cast(ptrToMember(env, thiz)->status()); -} - extern "C" JNIEXPORT void JNICALL Java_network_loki_messenger_libsession_1util_util_GroupMember_setName(JNIEnv *env, jobject thiz, @@ -225,3 +219,18 @@ Java_network_loki_messenger_libsession_1util_util_GroupMember_setSupplement(JNIE ptrToMember(env, thiz)->supplement = supplement; } + +extern "C" +JNIEXPORT jint JNICALL +Java_network_loki_messenger_libsession_1util_GroupMembersConfig_statusInt(JNIEnv *env, jobject thiz, + jobject group_member) { + return static_cast(ptrToMembers(env, thiz)->get_status(*ptrToMember(env, group_member))); +} +extern "C" +JNIEXPORT void JNICALL +Java_network_loki_messenger_libsession_1util_GroupMembersConfig_setPendingSend(JNIEnv *env, + jobject thiz, + jstring pub_key_hex, + jboolean pending) { + ptrToMembers(env, thiz)->set_pending_send(util::string_from_jstring(env, pub_key_hex), pending); +} \ No newline at end of file diff --git a/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt b/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt index 4201b224e5..5f5c5cf2b9 100644 --- a/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt +++ b/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt @@ -384,12 +384,19 @@ class GroupInfoConfig private constructor(pointer: Long): ConfigBase(pointer), M interface ReadableGroupMembersConfig: ReadableConfig { fun all(): List fun get(pubKeyHex: String): GroupMember? + fun status(groupMember: GroupMember): GroupMember.Status +} + +fun ReadableGroupMembersConfig.allWithStatus(): Sequence> { + return all().asSequence().map { it to status(it) } } interface MutableGroupMembersConfig : ReadableGroupMembersConfig, MutableConfig { fun getOrConstruct(pubKeyHex: String): GroupMember fun set(groupMember: GroupMember) fun erase(pubKeyHex: String): Boolean + + fun setPendingSend(pubKeyHex: String, pending: Boolean) } class GroupMembersConfig private constructor(pointer: Long): ConfigBase(pointer), MutableGroupMembersConfig { @@ -411,6 +418,13 @@ class GroupMembersConfig private constructor(pointer: Long): ConfigBase(pointer) external override fun get(pubKeyHex: String): GroupMember? external override fun getOrConstruct(pubKeyHex: String): GroupMember external override fun set(groupMember: GroupMember) + external override fun setPendingSend(pubKeyHex: String, pending: Boolean) + + private external fun statusInt(groupMember: GroupMember): Int + override fun status(groupMember: GroupMember): GroupMember.Status { + val statusInt = statusInt(groupMember) + return GroupMember.Status.entries.first { it.nativeValue == statusInt } + } } sealed class ConfigSig(pointer: Long) : Config(pointer) diff --git a/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/GroupMember.kt b/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/GroupMember.kt index 6fc086d0e8..caba9ef0a6 100644 --- a/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/GroupMember.kt +++ b/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/GroupMember.kt @@ -31,9 +31,6 @@ class GroupMember private constructor( external fun setRemoved(alsoRemoveMessages: Boolean) - private external fun statusInt(): Int - val status: Status? get() = Status.entries.firstOrNull { it.nativeValue == statusInt() } - external fun profilePic(): UserPic? external fun setProfilePic(pic: UserPic) @@ -60,37 +57,43 @@ class GroupMember private constructor( destroy() } - val removed: Boolean - get() = status in EnumSet.of(Status.REMOVED, Status.REMOVED_UNKNOWN, Status.REMOVED_INCLUDING_MESSAGES) + fun isRemoved(status: Status): Boolean { + return status in EnumSet.of(Status.REMOVED, Status.REMOVED_UNKNOWN, Status.REMOVED_INCLUDING_MESSAGES) + } - val isAdminOrBeingPromoted: Boolean - get() = admin || status in EnumSet.of(Status.PROMOTION_SENT, Status.PROMOTION_ACCEPTED) + fun isAdminOrBeingPromoted(status: Status): Boolean { + return admin || status in EnumSet.of(Status.PROMOTION_SENT, Status.PROMOTION_ACCEPTED) + } - val inviteFailed: Boolean - get() = status == Status.INVITE_FAILED + fun inviteFailed(status: Status): Boolean { + return status == Status.INVITE_FAILED + } - val shouldRemoveMessages: Boolean - get() = status == Status.REMOVED_INCLUDING_MESSAGES + fun shouldRemoveMessages(status: Status): Boolean { + return status == Status.REMOVED_INCLUDING_MESSAGES + } enum class Status(val nativeValue: Int) { INVITE_UNKNOWN(0), INVITE_NOT_SENT(1), - INVITE_FAILED(2), - INVITE_SENT(3), - INVITE_ACCEPTED(4), - - PROMOTION_UNKNOWN(5), - PROMOTION_NOT_SENT(6), - PROMOTION_FAILED(7), - PROMOTION_SENT(8), - PROMOTION_ACCEPTED(9), - - REMOVED_UNKNOWN(10), - REMOVED(11), - REMOVED_INCLUDING_MESSAGES(12); + INVENT_SENDING(2), + INVITE_FAILED(3), + INVITE_SENT(4), + INVITE_ACCEPTED(5), + + PROMOTION_UNKNOWN(6), + PROMOTION_NOT_SENT(7), + PROMOTION_SENDING(8), + PROMOTION_FAILED(9), + PROMOTION_SENT(10), + PROMOTION_ACCEPTED(11), + + REMOVED_UNKNOWN(12), + REMOVED(13), + REMOVED_INCLUDING_MESSAGES(14); } override fun toString(): String { - return "GroupMember(name=$name, admin=$admin, supplement=$supplement, status=$status)" + return "GroupMember(name=$name, admin=$admin, supplement=$supplement)" } } \ No newline at end of file