more group terminate checks

This commit is contained in:
kate-signal 2026-03-20 16:01:45 -04:00 committed by GitHub
parent 6fc62a30ab
commit 44a4d869ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 56 additions and 24 deletions

View File

@ -22,9 +22,8 @@ extension TSContactThread {
extension TSGroupThread {
var canCall: Bool {
return
isGroupV2Thread
&& groupMembership.isLocalUserFullMember
return isGroupV2Thread
&& groupMembership.isLocalUserFullMember
&& !isTerminatedGroup
}
}

View File

@ -132,6 +132,7 @@ class CallLinkApprovalRequestDetailsSheet: OWSTableSheetViewController {
)
.filter(\.groupModel.groupMembership.isLocalUserFullMember)
.filter(\.shouldThreadBeVisible)
.filter { !$0.isTerminatedGroup }
let contactTitle = ConversationHeaderBuilder.threadAttributedString(
threadName: self.approvalRequest.name,

View File

@ -1127,7 +1127,7 @@ extension CVComponentThreadDetails {
}
let groupThreads = TSGroupThread.groupThreads(with: contactThread.contactAddress, transaction: tx)
let mutualGroupNames = groupThreads.filter { $0.groupModel.groupMembership.isLocalUserFullMember && $0.shouldThreadBeVisible }.map { $0.groupNameOrDefault }
let mutualGroupNames = groupThreads.filter { $0.groupModel.groupMembership.isLocalUserFullMember && $0.shouldThreadBeVisible && !$0.isTerminatedGroup }.map { $0.groupNameOrDefault }
let isMessageRequest = contactThread.hasPendingMessageRequest(transaction: tx)

View File

@ -274,6 +274,9 @@ extension ConversationViewController: CVComponentDelegate {
if thread.isGroupThread, !thread.isLocalUserFullMemberOfThread {
return false
}
if thread.isGroupThread, thread.isTerminatedGroup {
return false
}
if self.threadViewModel.hasPendingMessageRequest {
return false
}
@ -1367,7 +1370,7 @@ extension ConversationViewController: CVComponentDelegate {
return
}
if let groupThread = self.thread as? TSGroupThread, !groupThread.groupModel.groupMembership.isLocalUserFullMember {
if let groupThread = self.thread as? TSGroupThread, !groupThread.groupModel.groupMembership.isLocalUserFullMember || groupThread.isTerminatedGroup {
return
}

View File

@ -85,17 +85,7 @@ public extension ConversationViewController {
}
func refreshCallState() {
let groupThread = thread as? TSGroupThread
guard
let groupThread,
let groupModelV2 = groupThread.groupModel as? TSGroupModelV2,
!groupModelV2.isTerminated
else {
return
}
if let groupId = try? groupThread.groupIdentifier {
if let groupId = try? (thread as? TSGroupThread)?.groupIdentifier {
Task {
await SSKEnvironment.shared.groupCallManagerRef.peekGroupCallAndUpdateThread(
forGroupId: groupId,

View File

@ -249,6 +249,10 @@ extension ConversationViewController: ContextMenuInteractionDelegate {
return false
}
guard !threadViewModel.threadRecord.isTerminatedGroup else {
return false
}
switch interaction {
case let outgoingMessage as TSOutgoingMessage:
if outgoingMessage.wasRemotelyDeleted { return false }

View File

@ -388,6 +388,7 @@ class ContactAboutSheet: StackSheetViewController {
)
.filter(\.groupModel.groupMembership.isLocalUserFullMember)
.filter(\.shouldThreadBeVisible)
.filter { !$0.isTerminatedGroup }
// We don't want to show "no groups in common",
// so return nil instead of an empty array.
.nilIfEmpty

View File

@ -876,7 +876,7 @@ class ConversationSettingsViewController: OWSTableViewController2, BadgeCollecti
self.mutualGroupThreads = TSGroupThread.groupThreads(
with: contactThread.contactAddress,
transaction: transaction,
).filter { $0.groupModel.groupMembership.isLocalUserFullMember && $0.shouldThreadBeVisible }
).filter { $0.groupModel.groupMembership.isLocalUserFullMember && $0.shouldThreadBeVisible && !$0.isTerminatedGroup }
}
}

View File

@ -87,7 +87,10 @@ public class GroupCallManager {
return
}
guard groupThread.groupModel.groupMembership.isLocalUserFullMember else {
guard
groupThread.groupModel.groupMembership.isLocalUserFullMember,
!groupThread.isTerminatedGroup
else {
logger.info("Cleaning up unended calls for non-member thread")
await self.databaseStorage.awaitableWrite { tx in
_ = self.cleanUpUnendedCallMessagesAsNecessary(

View File

@ -101,6 +101,10 @@ private class LocalUserLeaveGroupJobRunner: JobRunner {
guard groupModel.groupMembership.isLocalUserFullMember else {
return
}
guard !groupModel.isTerminated else {
return
}
let groupSendEndorsementStore = DependenciesBridge.shared.groupSendEndorsementStore
let combinedEndorsement = SSKEnvironment.shared.databaseStorageRef.read { tx in
return try? groupSendEndorsementStore.fetchCombinedEndorsement(groupThreadId: threadId, tx: tx)

View File

@ -43,7 +43,11 @@ public extension TSInfoMessage {
}
for groupThread in TSGroupThread.groupThreads(with: address, transaction: transaction) {
guard groupThread.groupModel.groupMembership.isLocalUserFullMember, groupThread.shouldThreadBeVisible else {
guard
groupThread.groupModel.groupMembership.isLocalUserFullMember,
groupThread.shouldThreadBeVisible,
!groupThread.isTerminatedGroup
else {
continue
}
saveProfileUpdateMessage(thread: groupThread)

View File

@ -1058,6 +1058,10 @@ public final class MessageReceiver {
Logger.info("Ignoring message from not in group user \(envelope.sourceAci)")
return nil
}
guard !groupModel.isTerminated else {
Logger.info("Ignoring message for terminated group")
return nil
}
return groupThread
}

View File

@ -873,6 +873,11 @@ public class MessageSender {
guard groupModel.groupMembership.isLocalUserFullMember else {
throw OWSGenericError("can't send because we're not a member")
}
guard !groupModel.isTerminated else {
throw OWSGenericError("can't send because group is ended")
}
// We can't send most messages to GV2 "announcement-only" threads if we're
// not an admin. See also `processFlaglessDataMessage`.
guard

View File

@ -418,7 +418,7 @@ public class OWSIdentityManagerImpl: OWSIdentityManager {
)
contactThreadMessage.anyInsert(transaction: tx)
for groupThread in TSGroupThread.groupThreads(with: SignalServiceAddress(serviceId), transaction: tx) {
for groupThread in TSGroupThread.groupThreads(with: SignalServiceAddress(serviceId), transaction: tx).filter({ !$0.isTerminatedGroup }) {
TSErrorMessage.nonblockingIdentityChange(
thread: groupThread,
address: SignalServiceAddress(serviceId),

View File

@ -543,6 +543,7 @@ public class OWSMessageDecrypter {
) { thread, stop in
guard thread.isGroupV2Thread else { return }
guard thread.groupModel.groupMembership.isLocalUserFullMember else { return }
guard !thread.isTerminatedGroup else { return }
stop.pointee = true
needsReactiveProfileKeyMessage = true
}

View File

@ -144,4 +144,14 @@ public extension TSThread {
}
return groupThread.groupModel.groupMembership.isLocalUserFullMember
}
var isTerminatedGroup: Bool {
guard
let groupThread = self as? TSGroupThread,
let groupModelV2 = groupThread.groupModel as? TSGroupModelV2
else {
return false
}
return groupModelV2.isTerminated
}
}

View File

@ -55,7 +55,7 @@ public extension TSThread {
}
}
if let groupThread = self as? TSGroupThread {
guard groupThread.groupModel.groupMembership.isLocalUserFullMember else {
guard groupThread.groupModel.groupMembership.isLocalUserFullMember, !isTerminatedGroup else {
return false
}
}

View File

@ -429,7 +429,10 @@ public struct StoryConversationItem {
) -> Self? {
let backingItem: StoryConversationItem.ItemType? = {
if let groupThread = thread as? TSGroupThread {
guard groupThread.groupModel.groupMembership.isLocalUserFullMember else {
guard
groupThread.groupModel.groupMembership.isLocalUserFullMember,
!groupThread.isTerminatedGroup
else {
return nil
}
return .groupStory(GroupConversationItem(

View File

@ -488,7 +488,7 @@ extension RecipientPickerViewController {
return nil
case .groupsThatUserIsMemberOfWhenSearching:
groupThreads = searchResults.groupThreads.filter { thread in
thread.groupModel.groupMembership.isLocalUserFullMember
thread.groupModel.groupMembership.isLocalUserFullMember && !thread.isTerminatedGroup
}
case .allGroupsWhenSearching:
groupThreads = searchResults.groupThreads