Move “can local user leave” check to membership
This commit is contained in:
parent
3b60ab1530
commit
0632af7391
@ -777,6 +777,7 @@
|
||||
509085BC2C498D3600409B85 /* LinkPreviewFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 509085BB2C498D3500409B85 /* LinkPreviewFetcher.swift */; };
|
||||
509085BE2C49C29400409B85 /* PaddingBucket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 509085BD2C49C29400409B85 /* PaddingBucket.swift */; };
|
||||
509085C02C49C2A500409B85 /* PaddingBucketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 509085BF2C49C2A500409B85 /* PaddingBucketTest.swift */; };
|
||||
5090B1A12F8D7239003F029D /* GroupMembershipTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5090B1A02F8D7239003F029D /* GroupMembershipTest.swift */; };
|
||||
50925DEA2DA86E3A00DAB484 /* GroupMessageProcessorJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50925DE92DA86E3A00DAB484 /* GroupMessageProcessorJob.swift */; };
|
||||
50925DEC2DA87AEF00DAB484 /* GroupMessageProcessorJobTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50925DEB2DA87AEF00DAB484 /* GroupMessageProcessorJobTest.swift */; };
|
||||
5094D5052EE3A6780041F402 /* AttachmentLimits.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5094D5042EE3A6780041F402 /* AttachmentLimits.swift */; };
|
||||
@ -5052,6 +5053,7 @@
|
||||
509085BB2C498D3500409B85 /* LinkPreviewFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkPreviewFetcher.swift; sourceTree = "<group>"; };
|
||||
509085BD2C49C29400409B85 /* PaddingBucket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaddingBucket.swift; sourceTree = "<group>"; };
|
||||
509085BF2C49C2A500409B85 /* PaddingBucketTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaddingBucketTest.swift; sourceTree = "<group>"; };
|
||||
5090B1A02F8D7239003F029D /* GroupMembershipTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupMembershipTest.swift; sourceTree = "<group>"; };
|
||||
50925DE92DA86E3A00DAB484 /* GroupMessageProcessorJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupMessageProcessorJob.swift; sourceTree = "<group>"; };
|
||||
50925DEB2DA87AEF00DAB484 /* GroupMessageProcessorJobTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupMessageProcessorJobTest.swift; sourceTree = "<group>"; };
|
||||
5094D5042EE3A6780041F402 /* AttachmentLimits.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentLimits.swift; sourceTree = "<group>"; };
|
||||
@ -14334,6 +14336,7 @@
|
||||
F94261E2289B1B5400460798 /* Groups */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5090B1A02F8D7239003F029D /* GroupMembershipTest.swift */,
|
||||
F94261E3289B1B5400460798 /* GroupModelsTest.swift */,
|
||||
);
|
||||
name = Groups;
|
||||
@ -20202,6 +20205,7 @@
|
||||
F97217FE28DCBC5100113D9F /* GRDBSchemaMigratorTest.swift in Sources */,
|
||||
D979CC5E2AD618EA006AAC49 /* GroupCallRecordManagerTest.swift in Sources */,
|
||||
D91F0B4F2B193A7A0086DB30 /* GroupCallRecordRingUpdateDelegateTest.swift in Sources */,
|
||||
5090B1A12F8D7239003F029D /* GroupMembershipTest.swift in Sources */,
|
||||
5075C21729CA1EE700A260D2 /* GroupMemberUpdaterTest.swift in Sources */,
|
||||
50925DEC2DA87AEF00DAB484 /* GroupMessageProcessorJobTest.swift in Sources */,
|
||||
F9426251289B1B5500460798 /* GroupModelsTest.swift in Sources */,
|
||||
|
||||
@ -687,10 +687,7 @@ class ConversationSettingsViewController: OWSTableViewController2, BadgeCollecti
|
||||
let groupThread = thread as? TSGroupThread,
|
||||
let groupModel = groupThread.groupModel as? TSGroupModelV2,
|
||||
let localAci = DependenciesBridge.shared.tsAccountManager.localIdentifiersWithMaybeSneakyTransaction?.aci,
|
||||
GroupManager.canLocalUserLeaveGroupWithoutChoosingNewAdmin(
|
||||
localAci: localAci,
|
||||
groupMembership: groupModel.groupMembership,
|
||||
)
|
||||
groupModel.groupMembership.canLocalUserLeaveGroupWithoutChoosingNewAdmin(localAci: localAci)
|
||||
{
|
||||
LeaveGroupCoordinator(
|
||||
groupThread: groupThread,
|
||||
|
||||
@ -30,12 +30,7 @@ class LeaveGroupCoordinator: ReplaceAdminViewControllerDelegate {
|
||||
// Retain self for the lifetime of rootViewController.
|
||||
ObjectRetainer.retainObject(self, forLifetimeOf: rootViewController)
|
||||
|
||||
if
|
||||
GroupManager.canLocalUserLeaveGroupWithoutChoosingNewAdmin(
|
||||
localAci: localAci,
|
||||
groupMembership: groupModel.groupMembership,
|
||||
)
|
||||
{
|
||||
if groupModel.groupMembership.canLocalUserLeaveGroupWithoutChoosingNewAdmin(localAci: localAci) {
|
||||
showLeaveGroupConfirmAlert(
|
||||
fromViewController: rootViewController,
|
||||
replacementAdminAci: nil,
|
||||
|
||||
@ -70,34 +70,6 @@ public class GroupManager: NSObject {
|
||||
return isV1GroupId(groupId) || isV2GroupId(groupId)
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
public static func canLocalUserLeaveGroupWithoutChoosingNewAdmin(
|
||||
localAci: Aci,
|
||||
groupMembership: GroupMembership,
|
||||
) -> Bool {
|
||||
let fullMembers = Set(groupMembership.fullMembers.compactMap { $0.serviceId as? Aci })
|
||||
let fullMemberAdmins = Set(groupMembership.fullMemberAdministrators.compactMap { $0.serviceId as? Aci })
|
||||
return canLocalUserLeaveGroupWithoutChoosingNewAdmin(
|
||||
localAci: localAci,
|
||||
fullMembers: fullMembers,
|
||||
admins: fullMemberAdmins,
|
||||
)
|
||||
}
|
||||
|
||||
public static func canLocalUserLeaveGroupWithoutChoosingNewAdmin(
|
||||
localAci: Aci,
|
||||
fullMembers: Set<Aci>,
|
||||
admins: Set<Aci>,
|
||||
) -> Bool {
|
||||
// If the current user is the only admin and they're not the only member of
|
||||
// the group, then they must select a new admin.
|
||||
if Set([localAci]) == admins, Set([localAci]) != fullMembers {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MARK: - Group Models
|
||||
|
||||
/// Confirms that a given address supports V2 groups.
|
||||
|
||||
@ -585,6 +585,28 @@ public class GroupMembership: NSObject, NSSecureCoding {
|
||||
|
||||
// MARK: -
|
||||
|
||||
public func canLocalUserLeaveGroupWithoutChoosingNewAdmin(localAci: Aci) -> Bool {
|
||||
let fullMembers = Set(self.fullMembers.compactMap { $0.serviceId as? Aci })
|
||||
let fullMemberAdmins = Set(self.fullMemberAdministrators.compactMap { $0.serviceId as? Aci })
|
||||
return Self.canLocalUserLeaveGroupWithoutChoosingNewAdmin(
|
||||
localAci: localAci,
|
||||
fullMembers: fullMembers,
|
||||
admins: fullMemberAdmins,
|
||||
)
|
||||
}
|
||||
|
||||
static func canLocalUserLeaveGroupWithoutChoosingNewAdmin(
|
||||
localAci: Aci,
|
||||
fullMembers: Set<Aci>,
|
||||
admins: Set<Aci>,
|
||||
) -> Bool {
|
||||
// If there's already another admin or we're the only member, we can leave
|
||||
// without selecting a new admin.
|
||||
return Set([localAci]) != admins || Set([localAci]) == fullMembers
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
/// Is this user's profile key exposed to the group?
|
||||
public func hasProfileKeyInGroup(serviceId: ServiceId) -> Bool {
|
||||
guard let memberState = memberStates[SignalServiceAddress(serviceId)] else {
|
||||
|
||||
33
SignalServiceKit/tests/Groups/GroupMembershipTest.swift
Normal file
33
SignalServiceKit/tests/Groups/GroupMembershipTest.swift
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// Copyright 2026 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import LibSignalClient
|
||||
import Testing
|
||||
|
||||
@testable import SignalServiceKit
|
||||
|
||||
struct GroupMembershipTest {
|
||||
private static let localAci = LocalIdentifiers.forUnitTests.aci
|
||||
private static let otherAci = Aci.constantForTesting("00000000-0000-4000-8000-000000000000")
|
||||
@Test(arguments: [
|
||||
(true, [], []),
|
||||
(true, [Self.localAci], [Self.localAci]),
|
||||
(false, [Self.localAci], [Self.localAci, Self.otherAci]),
|
||||
(true, [Self.localAci, Self.otherAci], [Self.localAci, Self.otherAci]),
|
||||
(true, [], [Self.localAci]),
|
||||
(true, [], [Self.localAci, Self.otherAci]),
|
||||
(true, [Self.otherAci], [Self.localAci, Self.otherAci]),
|
||||
])
|
||||
func testCanLocalUserLeaveGroup(testCase: (canLeave: Bool, admins: [Aci], members: [Aci])) {
|
||||
let localAci = Self.localAci
|
||||
let canLeave = GroupMembership.canLocalUserLeaveGroupWithoutChoosingNewAdmin(
|
||||
localAci: localAci,
|
||||
fullMembers: Set(testCase.members),
|
||||
admins: Set(testCase.admins),
|
||||
)
|
||||
#expect(canLeave == testCase.canLeave)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user