Path: blob/main/Signal/src/ViewControllers/ThreadSettings/GroupViewHelper.swift
1 views
//
// 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 {
weak var delegate: GroupViewHelperDelegate?
let threadViewModel: ThreadViewModel
let memberLabelCoordinator: MemberLabelCoordinator?
var thread: TSThread {
return threadViewModel.threadRecord
}
var fromViewController: UIViewController? {
return delegate?.fromViewController
}
init(threadViewModel: ThreadViewModel, memberLabelCoordinator: MemberLabelCoordinator?) {
self.threadViewModel = threadViewModel
self.memberLabelCoordinator = memberLabelCoordinator
}
// 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)
// * Pinned Messages (if group)
var canEditConversationAttributes: Bool {
return canLocalUserEditConversation { groupAccess in
return groupAccess.attributes
}
}
var canEditMemberLabels: Bool {
guard BuildFlags.MemberLabel.send else {
return false
}
return canLocalUserEditConversation { groupAccess in
return groupAccess.memberLabels
}
}
// 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.groupModel.groupMembership.isLocalUserFullMemberAndAdministrator
}
var canRevokePendingInvites: Bool {
guard let groupThread = thread as? TSGroupThread else {
return false
}
return
!threadViewModel.hasPendingMessageRequest
&& groupThread.isGroupV2Thread
&& groupThread.groupModel.groupMembership.isLocalUserFullMemberAndAdministrator
}
var isTerminatedGroup: Bool {
guard
let groupThread = thread as? TSGroupThread,
let groupModelV2 = groupThread.groupModel as? TSGroupModelV2
else {
return false
}
return groupModelV2.isTerminated
}
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.groupModel.groupMembership.isLocalUserFullMemberAndAdministrator
}
var isLocalUserFullMember: Bool {
guard let groupThread = thread as? TSGroupThread else {
return true
}
return groupThread.groupModel.groupMembership.isLocalUserFullMember
}
var isLocalUserFullOrInvitedMember: Bool {
guard let groupThread = thread as? TSGroupThread else {
return true
}
return groupThread.groupModel.groupMembership.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)
}
}