From b7c4d559dc58765a1de0ee2a433d3f6088c39a34 Mon Sep 17 00:00:00 2001 From: jbsession Date: Fri, 23 Jan 2026 16:32:03 +0800 Subject: [PATCH 1/6] Added member promotion update --- .../messaging/groups/GroupManagerV2.kt | 2 + .../conversation/v2/ConversationActivityV2.kt | 14 +++---- .../groups/BaseGroupMembersViewModel.kt | 40 +++++++++++++++---- .../securesms/groups/GroupManagerV2Impl.kt | 12 ++++++ .../securesms/groups/GroupMembersViewModel.kt | 4 +- .../groups/ManageGroupAdminsViewModel.kt | 6 ++- .../groups/ManageGroupMembersViewModel.kt | 2 +- .../groups/PromoteMembersViewModel.kt | 3 +- 8 files changed, 62 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/org/session/libsession/messaging/groups/GroupManagerV2.kt b/app/src/main/java/org/session/libsession/messaging/groups/GroupManagerV2.kt index 286e069ca6..9434203acd 100644 --- a/app/src/main/java/org/session/libsession/messaging/groups/GroupManagerV2.kt +++ b/app/src/main/java/org/session/libsession/messaging/groups/GroupManagerV2.kt @@ -61,6 +61,8 @@ interface GroupManagerV2 { suspend fun leaveGroup(groupId: AccountId, deleteGroup : Boolean = false) suspend fun promoteMember(group: AccountId, members: List, isRepromote: Boolean) + suspend fun manuallyAcceptPromotion(groupId : AccountId) + suspend fun handleInvitation( groupId: AccountId, groupName: String, diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index dfa9fb93ce..56a5777258 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -657,15 +657,13 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate, } lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.conversationReloadNotification - .collect { - if (!firstLoad.get()) { - LoaderManager.getInstance(this@ConversationActivityV2) - .restartLoader(0, null, this@ConversationActivityV2) - } + viewModel.conversationReloadNotification + .collect { + if (!firstLoad.get()) { + LoaderManager.getInstance(this@ConversationActivityV2) + .restartLoader(0, null, this@ConversationActivityV2) } - } + } } setupMentionView() 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 4a2dbbdc37..4c644f01a2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/BaseGroupMembersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/BaseGroupMembersViewModel.kt @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.groups import android.content.Context import android.widget.Toast +import androidx.compose.ui.input.key.Key.Companion.G import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.qualifiers.ApplicationContext @@ -13,18 +14,23 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.take import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import network.loki.messenger.R import network.loki.messenger.libsession_util.allWithStatus import network.loki.messenger.libsession_util.util.GroupMember import org.session.libsession.database.StorageProtocol +import org.session.libsession.messaging.groups.GroupManagerV2 import org.session.libsession.utilities.Address import org.session.libsession.utilities.Address.Companion.toAddress import org.session.libsession.utilities.ConfigFactoryProtocol @@ -45,10 +51,12 @@ abstract class BaseGroupMembersViewModel( private val configFactory: ConfigFactoryProtocol, private val avatarUtils: AvatarUtils, private val recipientRepository: RecipientRepository, + private val groupManager: GroupManagerV2, ) : ViewModel() { private val groupId = groupAddress.accountId // Output: the source-of-truth group information. Other states are derived from this. + @OptIn(FlowPreview::class) protected val groupInfo: StateFlow>?> = (configFactory.configUpdateNotifications .filter { @@ -65,21 +73,27 @@ abstract class BaseGroupMembersViewModel( val displayInfo = storage.getClosedGroupDisplayInfo(groupId.hexString) ?: return@withContext null - val rawMembers = configFactory.withGroupConfigs(groupId) { it.groupMembers.allWithStatus() } + val rawMembers = + configFactory.withGroupConfigs(groupId) { it.groupMembers.allWithStatus() } val memberState = mutableListOf() for ((member, status) in rawMembers) { - memberState.add(createGroupMember( - member = member, status = status, - shouldShowProBadge = recipientRepository.getRecipient(member.accountId().toAddress()).shouldShowProBadge, - myAccountId = currentUserId, - amIAdmin = displayInfo.isUserAdmin - )) + memberState.add( + createGroupMember( + member = member, status = status, + shouldShowProBadge = recipientRepository.getRecipient( + member.accountId().toAddress() + ).shouldShowProBadge, + myAccountId = currentUserId, + amIAdmin = displayInfo.isUserAdmin + ) + ) } displayInfo to sortMembers(memberState, currentUserId) } - }.stateIn(viewModelScope, SharingStarted.Eagerly, null) + }.debounce(200) + .stateIn(viewModelScope, SharingStarted.Eagerly, null) // Current group name (for header / text, if needed) val groupName: StateFlow = groupInfo @@ -129,6 +143,16 @@ abstract class BaseGroupMembersViewModel( } .stateIn(viewModelScope, SharingStarted.Lazily, emptyList()) + init { + viewModelScope.launch { + groupInfo + .map { it?.second.orEmpty() } + .map { members -> members.firstOrNull { it.isSelf }?.status /* or status */ } + .debounce(200L) + .collectLatest { groupManager.manuallyAcceptPromotion(groupId) } + } + } + fun onSearchQueryChanged(query: String) { mutableSearchQuery.value = query } 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 aa28e2c98e..2dcb983560 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2Impl.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2Impl.kt @@ -625,6 +625,18 @@ class GroupManagerV2Impl @Inject constructor( } } + override suspend fun manuallyAcceptPromotion(groupId: AccountId) { + val currentUserId = checkNotNull(storage.getUserPublicKey()) { "User public key is null" } + val groupIsAdmin = configFactory.getGroup(groupId)?.hasAdminKey() ?: return + + configFactory.withMutableGroupConfigs(groupId) { groupConfigs -> + groupConfigs.groupMembers.get(currentUserId)?.let { member -> + member.setPromotionAccepted() + groupConfigs.groupMembers.set(member) + } + } + } + /** * Mark this member as "removed" in the group config. * diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMembersViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMembersViewModel.kt index 976092aa0c..dd004ac67c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMembersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMembersViewModel.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import org.session.libsession.database.StorageProtocol +import org.session.libsession.messaging.groups.GroupManagerV2 import org.session.libsession.utilities.Address import org.session.libsession.utilities.ConfigFactoryProtocol import org.session.libsignal.utilities.AccountId @@ -30,7 +31,8 @@ class GroupMembersViewModel @AssistedInject constructor( configFactory: ConfigFactoryProtocol, avatarUtils: AvatarUtils, recipientRepository: RecipientRepository, -) : BaseGroupMembersViewModel(address, context, storage, configFactory, avatarUtils, recipientRepository) { + groupManager : GroupManagerV2 +) : BaseGroupMembersViewModel(address, context, storage, configFactory, avatarUtils, recipientRepository, groupManager) { private val _navigationActions = Channel() val navigationActions get() = _navigationActions.receiveAsFlow() diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupAdminsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupAdminsViewModel.kt index 46394ebd10..4f42f35dcb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupAdminsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupAdminsViewModel.kt @@ -12,6 +12,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update @@ -54,7 +55,8 @@ class ManageGroupAdminsViewModel @AssistedInject constructor( storage = storage, configFactory = configFactory, avatarUtils = avatarUtils, - recipientRepository = recipientRepository + recipientRepository = recipientRepository, + groupManager = groupManager ) { private val groupId = groupAddress.accountId @@ -84,7 +86,7 @@ class ManageGroupAdminsViewModel @AssistedInject constructor( init { // Build footer from selected admins + collapsed state viewModelScope.launch { - kotlinx.coroutines.flow.combine( + combine( selectedAdmins, footerCollapsed, ::buildFooterState diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupMembersViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupMembersViewModel.kt index eceee18611..c582571edf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupMembersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupMembersViewModel.kt @@ -51,7 +51,7 @@ class ManageGroupMembersViewModel @AssistedInject constructor( private val groupManager: GroupManagerV2, private val recipientRepository: RecipientRepository, avatarUtils: AvatarUtils, -) : BaseGroupMembersViewModel(groupAddress, context, storage, configFactory, avatarUtils, recipientRepository) { +) : BaseGroupMembersViewModel(groupAddress, context, storage, configFactory, avatarUtils, recipientRepository, groupManager) { private val groupId = groupAddress.accountId // Output: whether we should show the "add members" button diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/PromoteMembersViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/PromoteMembersViewModel.kt index 9cd0f408bd..a28fc1a26c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/PromoteMembersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/PromoteMembersViewModel.kt @@ -43,7 +43,8 @@ class PromoteMembersViewModel @AssistedInject constructor( storage = storage, configFactory = configFactory, avatarUtils = avatarUtils, - recipientRepository = recipientRepository + recipientRepository = recipientRepository, + groupManager = groupManager ) { private val groupId = groupAddress.accountId From 31a4a8ce56abcc7570155e0a2aab56d201a9be9c Mon Sep 17 00:00:00 2001 From: jbsession Date: Mon, 26 Jan 2026 14:09:30 +0800 Subject: [PATCH 2/6] Cleanup --- .../libsession/messaging/groups/GroupManagerV2.kt | 2 +- .../securesms/groups/BaseGroupMembersViewModel.kt | 11 +++++------ .../securesms/groups/GroupManagerV2Impl.kt | 5 +++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/session/libsession/messaging/groups/GroupManagerV2.kt b/app/src/main/java/org/session/libsession/messaging/groups/GroupManagerV2.kt index 9434203acd..a6be77bd63 100644 --- a/app/src/main/java/org/session/libsession/messaging/groups/GroupManagerV2.kt +++ b/app/src/main/java/org/session/libsession/messaging/groups/GroupManagerV2.kt @@ -61,7 +61,7 @@ interface GroupManagerV2 { suspend fun leaveGroup(groupId: AccountId, deleteGroup : Boolean = false) suspend fun promoteMember(group: AccountId, members: List, isRepromote: Boolean) - suspend fun manuallyAcceptPromotion(groupId : AccountId) + suspend fun resolvePromotionAccept(groupId : AccountId) suspend fun handleInvitation( groupId: AccountId, 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 4c644f01a2..db178c380a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/BaseGroupMembersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/BaseGroupMembersViewModel.kt @@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.groups import android.content.Context import android.widget.Toast -import androidx.compose.ui.input.key.Key.Companion.G import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.qualifiers.ApplicationContext @@ -18,12 +17,10 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.flow.take import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import network.loki.messenger.R @@ -147,9 +144,11 @@ abstract class BaseGroupMembersViewModel( viewModelScope.launch { groupInfo .map { it?.second.orEmpty() } - .map { members -> members.firstOrNull { it.isSelf }?.status /* or status */ } + .map { members -> members.firstOrNull { it.isSelf && it.showAsAdmin }?.status /* or status */ } + .distinctUntilChanged() + .filter { it == GroupMember.Status.PROMOTION_SENT } .debounce(200L) - .collectLatest { groupManager.manuallyAcceptPromotion(groupId) } + .collectLatest { groupManager.resolvePromotionAccept(groupId) } } } @@ -198,7 +197,7 @@ abstract class BaseGroupMembersViewModel( canResendInvite = amIAdmin && memberAccountId != myAccountId && !member.isRemoved(status) && (status == GroupMember.Status.INVITE_SENT || status == GroupMember.Status.INVITE_FAILED), - status = status.takeIf { !isMyself }, // Status is only meant for other members + status = status, highlightStatus = highlightStatus, showAsAdmin = member.isAdminOrBeingPromoted(status), showProBadge = shouldShowProBadge, 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 2dcb983560..0c7f248084 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2Impl.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2Impl.kt @@ -625,9 +625,10 @@ class GroupManagerV2Impl @Inject constructor( } } - override suspend fun manuallyAcceptPromotion(groupId: AccountId) { + override suspend fun resolvePromotionAccept(groupId: AccountId) { val currentUserId = checkNotNull(storage.getUserPublicKey()) { "User public key is null" } - val groupIsAdmin = configFactory.getGroup(groupId)?.hasAdminKey() ?: return + + if(configFactory.getGroup(groupId)?.hasAdminKey() != true) return configFactory.withMutableGroupConfigs(groupId) { groupConfigs -> groupConfigs.groupMembers.get(currentUserId)?.let { member -> From 7539f5d12632f2ef6d1d2445f4a49d67289ba931 Mon Sep 17 00:00:00 2001 From: jbsession Date: Tue, 27 Jan 2026 16:22:50 +0800 Subject: [PATCH 3/6] Cleanups --- .../messaging/groups/GroupManagerV2.kt | 2 -- .../groups/BaseGroupMembersViewModel.kt | 18 ++---------------- .../securesms/groups/GroupManagerV2Impl.kt | 13 ------------- .../securesms/groups/GroupMembersViewModel.kt | 5 +---- .../groups/ManageGroupAdminsViewModel.kt | 6 +----- .../groups/ManageGroupMembersViewModel.kt | 2 +- .../groups/PromoteMembersViewModel.kt | 7 +------ 7 files changed, 6 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/org/session/libsession/messaging/groups/GroupManagerV2.kt b/app/src/main/java/org/session/libsession/messaging/groups/GroupManagerV2.kt index a6be77bd63..286e069ca6 100644 --- a/app/src/main/java/org/session/libsession/messaging/groups/GroupManagerV2.kt +++ b/app/src/main/java/org/session/libsession/messaging/groups/GroupManagerV2.kt @@ -61,8 +61,6 @@ interface GroupManagerV2 { suspend fun leaveGroup(groupId: AccountId, deleteGroup : Boolean = false) suspend fun promoteMember(group: AccountId, members: List, isRepromote: Boolean) - suspend fun resolvePromotionAccept(groupId : AccountId) - suspend fun handleInvitation( groupId: AccountId, groupName: String, 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 db178c380a..e27e6f1d68 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/BaseGroupMembersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/BaseGroupMembersViewModel.kt @@ -48,7 +48,6 @@ abstract class BaseGroupMembersViewModel( private val configFactory: ConfigFactoryProtocol, private val avatarUtils: AvatarUtils, private val recipientRepository: RecipientRepository, - private val groupManager: GroupManagerV2, ) : ViewModel() { private val groupId = groupAddress.accountId @@ -89,8 +88,7 @@ abstract class BaseGroupMembersViewModel( displayInfo to sortMembers(memberState, currentUserId) } - }.debounce(200) - .stateIn(viewModelScope, SharingStarted.Eagerly, null) + }.stateIn(viewModelScope, SharingStarted.Eagerly, null) // Current group name (for header / text, if needed) val groupName: StateFlow = groupInfo @@ -140,18 +138,6 @@ abstract class BaseGroupMembersViewModel( } .stateIn(viewModelScope, SharingStarted.Lazily, emptyList()) - init { - viewModelScope.launch { - groupInfo - .map { it?.second.orEmpty() } - .map { members -> members.firstOrNull { it.isSelf && it.showAsAdmin }?.status /* or status */ } - .distinctUntilChanged() - .filter { it == GroupMember.Status.PROMOTION_SENT } - .debounce(200L) - .collectLatest { groupManager.resolvePromotionAccept(groupId) } - } - } - fun onSearchQueryChanged(query: String) { mutableSearchQuery.value = query } @@ -197,7 +183,7 @@ abstract class BaseGroupMembersViewModel( canResendInvite = amIAdmin && memberAccountId != myAccountId && !member.isRemoved(status) && (status == GroupMember.Status.INVITE_SENT || status == GroupMember.Status.INVITE_FAILED), - status = status, + status = status.takeIf { !isMyself }, // Status is only meant for other members highlightStatus = highlightStatus, showAsAdmin = member.isAdminOrBeingPromoted(status), showProBadge = shouldShowProBadge, 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 0c7f248084..aa28e2c98e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2Impl.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2Impl.kt @@ -625,19 +625,6 @@ class GroupManagerV2Impl @Inject constructor( } } - override suspend fun resolvePromotionAccept(groupId: AccountId) { - val currentUserId = checkNotNull(storage.getUserPublicKey()) { "User public key is null" } - - if(configFactory.getGroup(groupId)?.hasAdminKey() != true) return - - configFactory.withMutableGroupConfigs(groupId) { groupConfigs -> - groupConfigs.groupMembers.get(currentUserId)?.let { member -> - member.setPromotionAccepted() - groupConfigs.groupMembers.set(member) - } - } - } - /** * Mark this member as "removed" in the group config. * diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMembersViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMembersViewModel.kt index dd004ac67c..83c25243bb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMembersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMembersViewModel.kt @@ -13,13 +13,11 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import org.session.libsession.database.StorageProtocol -import org.session.libsession.messaging.groups.GroupManagerV2 import org.session.libsession.utilities.Address import org.session.libsession.utilities.ConfigFactoryProtocol import org.session.libsignal.utilities.AccountId import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 import org.thoughtcrime.securesms.database.RecipientRepository -import org.thoughtcrime.securesms.pro.ProStatusManager import org.thoughtcrime.securesms.util.AvatarUtils @@ -31,8 +29,7 @@ class GroupMembersViewModel @AssistedInject constructor( configFactory: ConfigFactoryProtocol, avatarUtils: AvatarUtils, recipientRepository: RecipientRepository, - groupManager : GroupManagerV2 -) : BaseGroupMembersViewModel(address, context, storage, configFactory, avatarUtils, recipientRepository, groupManager) { +) : BaseGroupMembersViewModel(address, context, storage, configFactory, avatarUtils, recipientRepository) { private val _navigationActions = Channel() val navigationActions get() = _navigationActions.receiveAsFlow() diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupAdminsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupAdminsViewModel.kt index 4f42f35dcb..39dcfe406c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupAdminsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupAdminsViewModel.kt @@ -10,11 +10,8 @@ import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import network.loki.messenger.R @@ -55,8 +52,7 @@ class ManageGroupAdminsViewModel @AssistedInject constructor( storage = storage, configFactory = configFactory, avatarUtils = avatarUtils, - recipientRepository = recipientRepository, - groupManager = groupManager + recipientRepository = recipientRepository ) { private val groupId = groupAddress.accountId diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupMembersViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupMembersViewModel.kt index c582571edf..eceee18611 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupMembersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupMembersViewModel.kt @@ -51,7 +51,7 @@ class ManageGroupMembersViewModel @AssistedInject constructor( private val groupManager: GroupManagerV2, private val recipientRepository: RecipientRepository, avatarUtils: AvatarUtils, -) : BaseGroupMembersViewModel(groupAddress, context, storage, configFactory, avatarUtils, recipientRepository, groupManager) { +) : BaseGroupMembersViewModel(groupAddress, context, storage, configFactory, avatarUtils, recipientRepository) { private val groupId = groupAddress.accountId // Output: whether we should show the "add members" button diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/PromoteMembersViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/PromoteMembersViewModel.kt index a28fc1a26c..3720e1a9e2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/PromoteMembersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/PromoteMembersViewModel.kt @@ -9,16 +9,13 @@ import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import network.loki.messenger.R import org.session.libsession.database.StorageProtocol -import org.session.libsession.messaging.groups.GroupManagerV2 import org.session.libsession.utilities.Address import org.session.libsession.utilities.ConfigFactoryProtocol import org.session.libsession.utilities.StringSubstitutionConstants.COUNT_KEY @@ -34,7 +31,6 @@ class PromoteMembersViewModel @AssistedInject constructor( @ApplicationContext private val context: Context, storage: StorageProtocol, private val configFactory: ConfigFactoryProtocol, - private val groupManager: GroupManagerV2, private val recipientRepository: RecipientRepository, avatarUtils: AvatarUtils, ) : BaseGroupMembersViewModel( @@ -43,8 +39,7 @@ class PromoteMembersViewModel @AssistedInject constructor( storage = storage, configFactory = configFactory, avatarUtils = avatarUtils, - recipientRepository = recipientRepository, - groupManager = groupManager + recipientRepository = recipientRepository ) { private val groupId = groupAddress.accountId From b218c1c341dc8b87fe4c3802fdd53916b816a61a Mon Sep 17 00:00:00 2001 From: jbsession Date: Wed, 28 Jan 2026 10:22:51 +0800 Subject: [PATCH 4/6] Revert "Cleanups" This reverts commit 7539f5d12632f2ef6d1d2445f4a49d67289ba931. --- .../messaging/groups/GroupManagerV2.kt | 2 ++ .../groups/BaseGroupMembersViewModel.kt | 18 ++++++++++++++++-- .../securesms/groups/GroupManagerV2Impl.kt | 13 +++++++++++++ .../securesms/groups/GroupMembersViewModel.kt | 5 ++++- .../groups/ManageGroupAdminsViewModel.kt | 6 +++++- .../groups/ManageGroupMembersViewModel.kt | 2 +- .../groups/PromoteMembersViewModel.kt | 7 ++++++- 7 files changed, 47 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/session/libsession/messaging/groups/GroupManagerV2.kt b/app/src/main/java/org/session/libsession/messaging/groups/GroupManagerV2.kt index 286e069ca6..a6be77bd63 100644 --- a/app/src/main/java/org/session/libsession/messaging/groups/GroupManagerV2.kt +++ b/app/src/main/java/org/session/libsession/messaging/groups/GroupManagerV2.kt @@ -61,6 +61,8 @@ interface GroupManagerV2 { suspend fun leaveGroup(groupId: AccountId, deleteGroup : Boolean = false) suspend fun promoteMember(group: AccountId, members: List, isRepromote: Boolean) + suspend fun resolvePromotionAccept(groupId : AccountId) + suspend fun handleInvitation( groupId: AccountId, groupName: String, 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 e27e6f1d68..db178c380a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/BaseGroupMembersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/BaseGroupMembersViewModel.kt @@ -48,6 +48,7 @@ abstract class BaseGroupMembersViewModel( private val configFactory: ConfigFactoryProtocol, private val avatarUtils: AvatarUtils, private val recipientRepository: RecipientRepository, + private val groupManager: GroupManagerV2, ) : ViewModel() { private val groupId = groupAddress.accountId @@ -88,7 +89,8 @@ abstract class BaseGroupMembersViewModel( displayInfo to sortMembers(memberState, currentUserId) } - }.stateIn(viewModelScope, SharingStarted.Eagerly, null) + }.debounce(200) + .stateIn(viewModelScope, SharingStarted.Eagerly, null) // Current group name (for header / text, if needed) val groupName: StateFlow = groupInfo @@ -138,6 +140,18 @@ abstract class BaseGroupMembersViewModel( } .stateIn(viewModelScope, SharingStarted.Lazily, emptyList()) + init { + viewModelScope.launch { + groupInfo + .map { it?.second.orEmpty() } + .map { members -> members.firstOrNull { it.isSelf && it.showAsAdmin }?.status /* or status */ } + .distinctUntilChanged() + .filter { it == GroupMember.Status.PROMOTION_SENT } + .debounce(200L) + .collectLatest { groupManager.resolvePromotionAccept(groupId) } + } + } + fun onSearchQueryChanged(query: String) { mutableSearchQuery.value = query } @@ -183,7 +197,7 @@ abstract class BaseGroupMembersViewModel( canResendInvite = amIAdmin && memberAccountId != myAccountId && !member.isRemoved(status) && (status == GroupMember.Status.INVITE_SENT || status == GroupMember.Status.INVITE_FAILED), - status = status.takeIf { !isMyself }, // Status is only meant for other members + status = status, highlightStatus = highlightStatus, showAsAdmin = member.isAdminOrBeingPromoted(status), showProBadge = shouldShowProBadge, 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 aa28e2c98e..0c7f248084 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2Impl.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2Impl.kt @@ -625,6 +625,19 @@ class GroupManagerV2Impl @Inject constructor( } } + override suspend fun resolvePromotionAccept(groupId: AccountId) { + val currentUserId = checkNotNull(storage.getUserPublicKey()) { "User public key is null" } + + if(configFactory.getGroup(groupId)?.hasAdminKey() != true) return + + configFactory.withMutableGroupConfigs(groupId) { groupConfigs -> + groupConfigs.groupMembers.get(currentUserId)?.let { member -> + member.setPromotionAccepted() + groupConfigs.groupMembers.set(member) + } + } + } + /** * Mark this member as "removed" in the group config. * diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMembersViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMembersViewModel.kt index 83c25243bb..dd004ac67c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMembersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMembersViewModel.kt @@ -13,11 +13,13 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import org.session.libsession.database.StorageProtocol +import org.session.libsession.messaging.groups.GroupManagerV2 import org.session.libsession.utilities.Address import org.session.libsession.utilities.ConfigFactoryProtocol import org.session.libsignal.utilities.AccountId import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 import org.thoughtcrime.securesms.database.RecipientRepository +import org.thoughtcrime.securesms.pro.ProStatusManager import org.thoughtcrime.securesms.util.AvatarUtils @@ -29,7 +31,8 @@ class GroupMembersViewModel @AssistedInject constructor( configFactory: ConfigFactoryProtocol, avatarUtils: AvatarUtils, recipientRepository: RecipientRepository, -) : BaseGroupMembersViewModel(address, context, storage, configFactory, avatarUtils, recipientRepository) { + groupManager : GroupManagerV2 +) : BaseGroupMembersViewModel(address, context, storage, configFactory, avatarUtils, recipientRepository, groupManager) { private val _navigationActions = Channel() val navigationActions get() = _navigationActions.receiveAsFlow() diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupAdminsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupAdminsViewModel.kt index 39dcfe406c..4f42f35dcb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupAdminsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupAdminsViewModel.kt @@ -10,8 +10,11 @@ import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import network.loki.messenger.R @@ -52,7 +55,8 @@ class ManageGroupAdminsViewModel @AssistedInject constructor( storage = storage, configFactory = configFactory, avatarUtils = avatarUtils, - recipientRepository = recipientRepository + recipientRepository = recipientRepository, + groupManager = groupManager ) { private val groupId = groupAddress.accountId diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupMembersViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupMembersViewModel.kt index eceee18611..c582571edf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupMembersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupMembersViewModel.kt @@ -51,7 +51,7 @@ class ManageGroupMembersViewModel @AssistedInject constructor( private val groupManager: GroupManagerV2, private val recipientRepository: RecipientRepository, avatarUtils: AvatarUtils, -) : BaseGroupMembersViewModel(groupAddress, context, storage, configFactory, avatarUtils, recipientRepository) { +) : BaseGroupMembersViewModel(groupAddress, context, storage, configFactory, avatarUtils, recipientRepository, groupManager) { private val groupId = groupAddress.accountId // Output: whether we should show the "add members" button diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/PromoteMembersViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/PromoteMembersViewModel.kt index 3720e1a9e2..a28fc1a26c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/PromoteMembersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/PromoteMembersViewModel.kt @@ -9,13 +9,16 @@ import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import network.loki.messenger.R import org.session.libsession.database.StorageProtocol +import org.session.libsession.messaging.groups.GroupManagerV2 import org.session.libsession.utilities.Address import org.session.libsession.utilities.ConfigFactoryProtocol import org.session.libsession.utilities.StringSubstitutionConstants.COUNT_KEY @@ -31,6 +34,7 @@ class PromoteMembersViewModel @AssistedInject constructor( @ApplicationContext private val context: Context, storage: StorageProtocol, private val configFactory: ConfigFactoryProtocol, + private val groupManager: GroupManagerV2, private val recipientRepository: RecipientRepository, avatarUtils: AvatarUtils, ) : BaseGroupMembersViewModel( @@ -39,7 +43,8 @@ class PromoteMembersViewModel @AssistedInject constructor( storage = storage, configFactory = configFactory, avatarUtils = avatarUtils, - recipientRepository = recipientRepository + recipientRepository = recipientRepository, + groupManager = groupManager ) { private val groupId = groupAddress.accountId From b456530510908fe10d6739fe7af89bb7b4e6966a Mon Sep 17 00:00:00 2001 From: jbsession Date: Wed, 28 Jan 2026 12:59:28 +0800 Subject: [PATCH 5/6] Update promotion message handling --- .../messaging/groups/GroupManagerV2.kt | 2 -- .../groups/BaseGroupMembersViewModel.kt | 23 ++------------- .../securesms/groups/GroupManagerV2Impl.kt | 29 +++++++------------ .../securesms/groups/GroupMembersViewModel.kt | 7 ++--- .../groups/ManageGroupAdminsViewModel.kt | 6 +--- .../groups/ManageGroupMembersViewModel.kt | 2 +- .../groups/PromoteMembersViewModel.kt | 8 +---- 7 files changed, 19 insertions(+), 58 deletions(-) diff --git a/app/src/main/java/org/session/libsession/messaging/groups/GroupManagerV2.kt b/app/src/main/java/org/session/libsession/messaging/groups/GroupManagerV2.kt index a6be77bd63..286e069ca6 100644 --- a/app/src/main/java/org/session/libsession/messaging/groups/GroupManagerV2.kt +++ b/app/src/main/java/org/session/libsession/messaging/groups/GroupManagerV2.kt @@ -61,8 +61,6 @@ interface GroupManagerV2 { suspend fun leaveGroup(groupId: AccountId, deleteGroup : Boolean = false) suspend fun promoteMember(group: AccountId, members: List, isRepromote: Boolean) - suspend fun resolvePromotionAccept(groupId : AccountId) - suspend fun handleInvitation( groupId: AccountId, groupName: String, 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 db178c380a..00cc6d8028 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/BaseGroupMembersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/BaseGroupMembersViewModel.kt @@ -13,10 +13,8 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart @@ -27,7 +25,6 @@ import network.loki.messenger.R import network.loki.messenger.libsession_util.allWithStatus import network.loki.messenger.libsession_util.util.GroupMember import org.session.libsession.database.StorageProtocol -import org.session.libsession.messaging.groups.GroupManagerV2 import org.session.libsession.utilities.Address import org.session.libsession.utilities.Address.Companion.toAddress import org.session.libsession.utilities.ConfigFactoryProtocol @@ -47,8 +44,7 @@ abstract class BaseGroupMembersViewModel( private val storage: StorageProtocol, private val configFactory: ConfigFactoryProtocol, private val avatarUtils: AvatarUtils, - private val recipientRepository: RecipientRepository, - private val groupManager: GroupManagerV2, + private val recipientRepository: RecipientRepository ) : ViewModel() { private val groupId = groupAddress.accountId @@ -89,8 +85,7 @@ abstract class BaseGroupMembersViewModel( displayInfo to sortMembers(memberState, currentUserId) } - }.debounce(200) - .stateIn(viewModelScope, SharingStarted.Eagerly, null) + }.stateIn(viewModelScope, SharingStarted.Eagerly, null) // Current group name (for header / text, if needed) val groupName: StateFlow = groupInfo @@ -140,18 +135,6 @@ abstract class BaseGroupMembersViewModel( } .stateIn(viewModelScope, SharingStarted.Lazily, emptyList()) - init { - viewModelScope.launch { - groupInfo - .map { it?.second.orEmpty() } - .map { members -> members.firstOrNull { it.isSelf && it.showAsAdmin }?.status /* or status */ } - .distinctUntilChanged() - .filter { it == GroupMember.Status.PROMOTION_SENT } - .debounce(200L) - .collectLatest { groupManager.resolvePromotionAccept(groupId) } - } - } - fun onSearchQueryChanged(query: String) { mutableSearchQuery.value = query } @@ -197,7 +180,7 @@ abstract class BaseGroupMembersViewModel( canResendInvite = amIAdmin && memberAccountId != myAccountId && !member.isRemoved(status) && (status == GroupMember.Status.INVITE_SENT || status == GroupMember.Status.INVITE_FAILED), - status = status, + status = status.takeIf { !isMyself }, // status is only for other members highlightStatus = highlightStatus, showAsAdmin = member.isAdminOrBeingPromoted(status), showProBadge = shouldShowProBadge, 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 0c7f248084..030921bdb3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2Impl.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2Impl.kt @@ -6,6 +6,7 @@ import com.squareup.phrase.Phrase import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.supervisorScope @@ -44,6 +45,7 @@ import org.session.libsession.snode.OwnedSwarmAuth import org.session.libsession.snode.SnodeMessage import org.session.libsession.snode.model.BatchResponse import org.session.libsession.utilities.Address +import org.session.libsession.utilities.Address.Companion.toAddress import org.session.libsession.utilities.StringSubstitutionConstants.GROUP_NAME_KEY import org.session.libsession.utilities.getGroup import org.session.libsession.utilities.recipients.Recipient @@ -520,12 +522,15 @@ class GroupManagerV2Impl @Inject constructor( // Update the group member's promotion status members.asSequence() .mapNotNull { configs.groupMembers.get(it.hexString) } - .onEach(GroupMember::setPromoted) + .onEach(GroupMember::setPromotionSent) .forEach(configs.groupMembers::set) configs.groupInfo.getName() } + // Ensure this push is complete before promotion messages go out + configFactory.waitUntilGroupConfigsPushed(group) + // Build a group update message to the group telling members someone has been promoted val timestamp = clock.currentTimeMillis() val signature = ED25519.sign( @@ -589,10 +594,11 @@ class GroupManagerV2Impl @Inject constructor( promotedByMemberIDs.asSequence() .mapNotNull { (member, result) -> configs.groupMembers.get(member.hexString)?.apply { - if (result.isSuccess) { - setPromotionSent() - } else { - setPromotionFailed() + if (result.isFailure) { + configs.groupMembers.get(member.hexString)?.let { member -> + member.setPromotionFailed() + configs.groupMembers.set(member) + } } } } @@ -625,19 +631,6 @@ class GroupManagerV2Impl @Inject constructor( } } - override suspend fun resolvePromotionAccept(groupId: AccountId) { - val currentUserId = checkNotNull(storage.getUserPublicKey()) { "User public key is null" } - - if(configFactory.getGroup(groupId)?.hasAdminKey() != true) return - - configFactory.withMutableGroupConfigs(groupId) { groupConfigs -> - groupConfigs.groupMembers.get(currentUserId)?.let { member -> - member.setPromotionAccepted() - groupConfigs.groupMembers.set(member) - } - } - } - /** * Mark this member as "removed" in the group config. * diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMembersViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMembersViewModel.kt index dd004ac67c..c4ba642cc8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMembersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMembersViewModel.kt @@ -13,13 +13,11 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import org.session.libsession.database.StorageProtocol -import org.session.libsession.messaging.groups.GroupManagerV2 import org.session.libsession.utilities.Address import org.session.libsession.utilities.ConfigFactoryProtocol import org.session.libsignal.utilities.AccountId import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 import org.thoughtcrime.securesms.database.RecipientRepository -import org.thoughtcrime.securesms.pro.ProStatusManager import org.thoughtcrime.securesms.util.AvatarUtils @@ -30,9 +28,8 @@ class GroupMembersViewModel @AssistedInject constructor( storage: StorageProtocol, configFactory: ConfigFactoryProtocol, avatarUtils: AvatarUtils, - recipientRepository: RecipientRepository, - groupManager : GroupManagerV2 -) : BaseGroupMembersViewModel(address, context, storage, configFactory, avatarUtils, recipientRepository, groupManager) { + recipientRepository: RecipientRepository +) : BaseGroupMembersViewModel(address, context, storage, configFactory, avatarUtils, recipientRepository) { private val _navigationActions = Channel() val navigationActions get() = _navigationActions.receiveAsFlow() diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupAdminsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupAdminsViewModel.kt index 4f42f35dcb..39dcfe406c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupAdminsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupAdminsViewModel.kt @@ -10,11 +10,8 @@ import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import network.loki.messenger.R @@ -55,8 +52,7 @@ class ManageGroupAdminsViewModel @AssistedInject constructor( storage = storage, configFactory = configFactory, avatarUtils = avatarUtils, - recipientRepository = recipientRepository, - groupManager = groupManager + recipientRepository = recipientRepository ) { private val groupId = groupAddress.accountId diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupMembersViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupMembersViewModel.kt index c582571edf..eceee18611 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupMembersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ManageGroupMembersViewModel.kt @@ -51,7 +51,7 @@ class ManageGroupMembersViewModel @AssistedInject constructor( private val groupManager: GroupManagerV2, private val recipientRepository: RecipientRepository, avatarUtils: AvatarUtils, -) : BaseGroupMembersViewModel(groupAddress, context, storage, configFactory, avatarUtils, recipientRepository, groupManager) { +) : BaseGroupMembersViewModel(groupAddress, context, storage, configFactory, avatarUtils, recipientRepository) { private val groupId = groupAddress.accountId // Output: whether we should show the "add members" button diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/PromoteMembersViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/PromoteMembersViewModel.kt index a28fc1a26c..fafdb4c9a9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/PromoteMembersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/PromoteMembersViewModel.kt @@ -9,16 +9,13 @@ import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import network.loki.messenger.R import org.session.libsession.database.StorageProtocol -import org.session.libsession.messaging.groups.GroupManagerV2 import org.session.libsession.utilities.Address import org.session.libsession.utilities.ConfigFactoryProtocol import org.session.libsession.utilities.StringSubstitutionConstants.COUNT_KEY @@ -34,7 +31,6 @@ class PromoteMembersViewModel @AssistedInject constructor( @ApplicationContext private val context: Context, storage: StorageProtocol, private val configFactory: ConfigFactoryProtocol, - private val groupManager: GroupManagerV2, private val recipientRepository: RecipientRepository, avatarUtils: AvatarUtils, ) : BaseGroupMembersViewModel( @@ -43,10 +39,8 @@ class PromoteMembersViewModel @AssistedInject constructor( storage = storage, configFactory = configFactory, avatarUtils = avatarUtils, - recipientRepository = recipientRepository, - groupManager = groupManager + recipientRepository = recipientRepository ) { - private val groupId = groupAddress.accountId private val _mutableSelectedMembers = MutableStateFlow(emptySet()) val selectedMembers: StateFlow> = _mutableSelectedMembers From 8e4deeccc224b6f8e4c3fe721be946352779aa36 Mon Sep 17 00:00:00 2001 From: jbsession Date: Wed, 28 Jan 2026 13:16:25 +0800 Subject: [PATCH 6/6] Change requests --- .../conversation/v2/ConversationActivityV2.kt | 14 ++++++++------ .../securesms/groups/GroupManagerV2Impl.kt | 6 +++++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 56a5777258..dfa9fb93ce 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -657,13 +657,15 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate, } lifecycleScope.launch { - viewModel.conversationReloadNotification - .collect { - if (!firstLoad.get()) { - LoaderManager.getInstance(this@ConversationActivityV2) - .restartLoader(0, null, this@ConversationActivityV2) + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.conversationReloadNotification + .collect { + if (!firstLoad.get()) { + LoaderManager.getInstance(this@ConversationActivityV2) + .restartLoader(0, null, this@ConversationActivityV2) + } } - } + } } setupMentionView() 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 030921bdb3..b1805de9e7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2Impl.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2Impl.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeout +import kotlinx.coroutines.withTimeoutOrNull import network.loki.messenger.R import network.loki.messenger.libsession_util.ED25519 import network.loki.messenger.libsession_util.Namespace @@ -77,6 +78,7 @@ import org.thoughtcrime.securesms.util.SessionMetaProtocol import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Singleton +import kotlin.time.Duration.Companion.seconds private const val TAG = "GroupManagerV2Impl" @@ -529,7 +531,9 @@ class GroupManagerV2Impl @Inject constructor( } // Ensure this push is complete before promotion messages go out - configFactory.waitUntilGroupConfigsPushed(group) + withTimeoutOrNull(10.seconds) { + configFactory.waitUntilGroupConfigsPushed(group) + } // Build a group update message to the group telling members someone has been promoted val timestamp = clock.currentTimeMillis()