Signal-iOS/Signal/src/ViewControllers/ThreadSettings/GroupViewHelper.swift

160 lines
4.8 KiB
Swift

//
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import SignalServiceKit
import SignalUI
protocol GroupViewHelperDelegate: AnyObject {
func groupViewHelperDidUpdateGroup()
var currentGroupModel: TSGroupModel? { get }
var fromViewController: UIViewController? { get }
}
// MARK: -
class GroupViewHelper: Dependencies {
weak var delegate: GroupViewHelperDelegate?
let threadViewModel: ThreadViewModel
var thread: TSThread {
return threadViewModel.threadRecord
}
var fromViewController: UIViewController? {
return delegate?.fromViewController
}
init(threadViewModel: ThreadViewModel) {
self.threadViewModel = threadViewModel
}
// MARK: - Accessors
// Don't use this method directly;
// Use canEditConversationAttributes or canEditConversationMembership instead.
private func canLocalUserEditConversation(v2AccessTypeBlock: (GroupAccess) -> GroupV2Access) -> Bool {
if threadViewModel.hasPendingMessageRequest {
return false
}
guard isLocalUserFullMember else {
return false
}
guard let groupThread = thread as? TSGroupThread else {
// Both users can edit contact threads.
return true
}
guard !isGroupV1Thread else {
return false
}
guard !threadViewModel.isBlocked else {
return false
}
guard let groupModelV2 = groupThread.groupModel as? TSGroupModelV2 else {
// All users can edit v1 groups.
return true
}
guard let localAddress = DependenciesBridge.shared.tsAccountManager.localIdentifiersWithMaybeSneakyTransaction?.aciAddress else {
owsFailDebug("Missing localAddress.")
return false
}
// Use the block to pick the access type: attributes or members?
let access = v2AccessTypeBlock(groupModelV2.access)
switch access {
case .unknown:
owsFailDebug("Unknown access.")
return false
case .unsatisfiable:
owsFailDebug("Invalid access.")
return false
case .any:
return true
case .member:
return groupModelV2.groupMembership.isFullMember(localAddress)
case .administrator:
return (groupModelV2.groupMembership.isFullMemberAndAdministrator(localAddress))
}
}
var isGroupV1Thread: Bool {
thread.isGroupV1Thread
}
// Can local user edit conversation attributes:
//
// * DM state
// * Group title (if group)
// * Group avatar (if group)
var canEditConversationAttributes: Bool {
return canLocalUserEditConversation { groupAccess in
return groupAccess.attributes
}
}
// Can local user edit group membership.
var canEditConversationMembership: Bool {
return canLocalUserEditConversation { groupAccess in
return groupAccess.members
}
}
// Can local user edit group access and message send permission.
var canEditPermissions: Bool {
guard let groupThread = thread as? TSGroupThread else {
return false
}
return (!threadViewModel.hasPendingMessageRequest &&
groupThread.isGroupV2Thread &&
groupThread.isLocalUserFullMemberAndAdministrator)
}
var canRevokePendingInvites: Bool {
guard let groupThread = thread as? TSGroupThread else {
return false
}
return (!threadViewModel.hasPendingMessageRequest &&
groupThread.isGroupV2Thread &&
groupThread.isLocalUserFullMemberAndAdministrator)
}
var canResendInvites: Bool {
return (!threadViewModel.hasPendingMessageRequest && isLocalUserFullMember)
}
var canApproveMemberRequests: Bool {
guard let groupThread = thread as? TSGroupThread else {
return false
}
return (!threadViewModel.hasPendingMessageRequest &&
groupThread.isGroupV2Thread &&
groupThread.isLocalUserFullMemberAndAdministrator)
}
var isLocalUserFullMember: Bool {
guard let groupThread = thread as? TSGroupThread else {
return true
}
return groupThread.isLocalUserFullMember
}
var isLocalUserFullOrInvitedMember: Bool {
guard let groupThread = thread as? TSGroupThread else {
return true
}
return groupThread.isLocalUserFullOrInvitedMember
}
func isFullOrInvitedMember(_ address: SignalServiceAddress) -> Bool {
guard let groupThread = thread as? TSGroupThread else {
return false
}
let groupMembership = groupThread.groupModel.groupMembership
return groupMembership.isFullMember(address) || groupMembership.isInvitedMember(address)
}
}