Signal-iOS/SignalServiceKit/src/Contacts/Threads/TSGroupThread+OWS.swift
2023-04-20 14:11:51 -07:00

171 lines
6.0 KiB
Swift

//
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
@objc
public extension TSGroupThread {
var groupId: Data { groupModel.groupId }
var groupMembership: GroupMembership {
groupModel.groupMembership
}
var isLocalUserMemberOfAnyKind: Bool {
groupMembership.isLocalUserMemberOfAnyKind
}
var isLocalUserFullMember: Bool {
groupMembership.isLocalUserFullMember
}
var isLocalUserInvitedMember: Bool {
groupMembership.isLocalUserInvitedMember
}
var isLocalUserRequestingMember: Bool {
groupMembership.isLocalUserRequestingMember
}
var isLocalUserFullOrInvitedMember: Bool {
groupMembership.isLocalUserFullOrInvitedMember
}
var isLocalUserFullMemberAndAdministrator: Bool {
groupMembership.isLocalUserFullMemberAndAdministrator
}
static let groupThreadUniqueIdPrefix = "g"
private static let uniqueIdMappingStore = SDSKeyValueStore(collection: "TSGroupThread.uniqueIdMappingStore")
private static func mappingKey(forGroupId groupId: Data) -> String {
groupId.hexadecimalString
}
private static func existingThreadId(forGroupId groupId: Data,
transaction: SDSAnyReadTransaction) -> String? {
owsAssertDebug(!groupId.isEmpty)
let mappingKey = self.mappingKey(forGroupId: groupId)
return uniqueIdMappingStore.getString(mappingKey, transaction: transaction)
}
static func threadId(forGroupId groupId: Data,
transaction: SDSAnyReadTransaction) -> String {
owsAssertDebug(!groupId.isEmpty)
if let threadUniqueId = existingThreadId(forGroupId: groupId, transaction: transaction) {
return threadUniqueId
}
return defaultThreadId(forGroupId: groupId)
}
static func defaultThreadId(forGroupId groupId: Data) -> String {
owsAssertDebug(!groupId.isEmpty)
return groupThreadUniqueIdPrefix + groupId.base64EncodedString()
}
private static func setThreadId(_ threadUniqueId: String,
forGroupId groupId: Data,
transaction: SDSAnyWriteTransaction) {
owsAssertDebug(!groupId.isEmpty)
let mappingKey = self.mappingKey(forGroupId: groupId)
if let existingThreadUniqueId = uniqueIdMappingStore.getString(mappingKey, transaction: transaction) {
// Don't overwrite existing mapping; but verify.
owsAssertDebug(threadUniqueId == existingThreadUniqueId)
return
}
uniqueIdMappingStore.setString(threadUniqueId, key: mappingKey, transaction: transaction)
}
// Used to update the mapping whenever we know of an existing
// group-id-to-thread-unique-id pair.
static func setGroupIdMapping(_ threadUniqueId: String,
forGroupId groupId: Data,
transaction: SDSAnyWriteTransaction) {
owsAssertDebug(!groupId.isEmpty)
setThreadId(threadUniqueId, forGroupId: groupId, transaction: transaction)
if GroupManager.isV1GroupId(groupId) {
guard let v2GroupId = groupsV2.v2GroupId(forV1GroupId: groupId) else {
owsFailDebug("Couldn't derive v2GroupId.")
return
}
setThreadId(threadUniqueId, forGroupId: v2GroupId, transaction: transaction)
} else if GroupManager.isV2GroupId(groupId) {
// Do nothing.
} else {
owsFailDebug("Invalid group id: \(groupId.hexadecimalString)")
}
}
// Used to update the mapping for a given group id.
//
// * Uses existing threads/mapping if possible.
// * If a v1 group id, it also update the mapping for the v2 group id.
static func ensureGroupIdMapping(forGroupId groupId: Data,
transaction: SDSAnyWriteTransaction) {
owsAssertDebug(!groupId.isEmpty)
guard GroupManager.isValidGroupIdOfAnyKind(groupId) else {
return
}
let buildThreadUniqueId = { () -> String in
if let threadUniqueId = existingThreadId(forGroupId: groupId,
transaction: transaction) {
return threadUniqueId
}
if GroupManager.isV1GroupId(groupId) {
if let v2GroupId = groupsV2.v2GroupId(forV1GroupId: groupId) {
if let threadUniqueId = existingThreadId(forGroupId: v2GroupId,
transaction: transaction) {
return threadUniqueId
}
} else {
owsFailDebug("Couldn't derive v2GroupId.")
}
}
return defaultThreadId(forGroupId: groupId)
}
let threadUniqueId = buildThreadUniqueId()
setGroupIdMapping(threadUniqueId, forGroupId: groupId, transaction: transaction)
}
/// Posted when the group associated with this thread adds or removes members.
///
/// The object is the group's unique ID as a string. Note that NotificationCenter dispatches by
/// object identity rather than equality, so any observer should register for *all* membership
/// changes and then filter the notifications they receive as needed.
static let membershipDidChange = Notification.Name("TSGroupThread.membershipDidChange")
func updateGroupMemberRecords(transaction: SDSAnyWriteTransaction) {
let groupMemberUpdater = DependenciesBridge.shared.groupMemberUpdater
groupMemberUpdater.updateRecords(groupThread: self, transaction: transaction.asV2Write)
}
}
// MARK: -
@objc
public extension TSThread {
var isLocalUserFullMemberOfThread: Bool {
guard let groupThread = self as? TSGroupThread else {
return true
}
return groupThread.groupMembership.isLocalUserFullMember
}
}