Signal-iOS/SignalServiceKit/Contacts/TSThread.m

260 lines
8.6 KiB
Objective-C

//
// Copyright 2017 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
#import "TSThread.h"
#import "OWSDisappearingMessagesConfiguration.h"
#import "OWSReadTracking.h"
#import "TSIncomingMessage.h"
#import "TSInfoMessage.h"
#import "TSInteraction.h"
#import "TSInvalidIdentityKeyReceivingErrorMessage.h"
#import "TSOutgoingMessage.h"
#import <SignalServiceKit/SignalServiceKit-Swift.h>
@import Intents;
NS_ASSUME_NONNULL_BEGIN
@interface TSThread ()
@property (nonatomic, nullable) NSDate *creationDate;
@property (nonatomic) BOOL isArchivedObsolete;
@property (nonatomic) BOOL isMarkedUnreadObsolete;
@property (atomic) uint64_t mutedUntilTimestampObsolete;
@property (nonatomic, nullable) NSDate *mutedUntilDateObsolete;
@property (nonatomic) uint64_t lastVisibleSortIdObsolete;
@property (nonatomic) double lastVisibleSortIdOnScreenPercentageObsolete;
@end
#pragma mark -
@implementation TSThread
- (instancetype)initWithUniqueId:(NSString *)uniqueId
{
self = [super initWithUniqueId:uniqueId];
if (self) {
_creationDate = [NSDate date];
_messageDraft = nil;
_conversationColorNameObsolete = @"Obsolete";
}
return self;
}
// --- CODE GENERATION MARKER
// This snippet is generated by /Scripts/sds_codegen/sds_generate.py. Do not manually edit it, instead run
// `sds_codegen.sh`.
// clang-format off
- (instancetype)initWithGrdbId:(int64_t)grdbId
uniqueId:(NSString *)uniqueId
conversationColorNameObsolete:(NSString *)conversationColorNameObsolete
creationDate:(nullable NSDate *)creationDate
editTargetTimestamp:(nullable NSNumber *)editTargetTimestamp
isArchivedObsolete:(BOOL)isArchivedObsolete
isMarkedUnreadObsolete:(BOOL)isMarkedUnreadObsolete
lastDraftInteractionRowId:(uint64_t)lastDraftInteractionRowId
lastDraftUpdateTimestamp:(uint64_t)lastDraftUpdateTimestamp
lastInteractionRowId:(uint64_t)lastInteractionRowId
lastSentStoryTimestamp:(nullable NSNumber *)lastSentStoryTimestamp
lastVisibleSortIdObsolete:(uint64_t)lastVisibleSortIdObsolete
lastVisibleSortIdOnScreenPercentageObsolete:(double)lastVisibleSortIdOnScreenPercentageObsolete
mentionNotificationMode:(TSThreadMentionNotificationMode)mentionNotificationMode
messageDraft:(nullable NSString *)messageDraft
messageDraftBodyRanges:(nullable MessageBodyRanges *)messageDraftBodyRanges
mutedUntilDateObsolete:(nullable NSDate *)mutedUntilDateObsolete
mutedUntilTimestampObsolete:(uint64_t)mutedUntilTimestampObsolete
shouldThreadBeVisible:(BOOL)shouldThreadBeVisible
storyViewMode:(TSThreadStoryViewMode)storyViewMode
{
self = [super initWithGrdbId:grdbId
uniqueId:uniqueId];
if (!self) {
return self;
}
_conversationColorNameObsolete = conversationColorNameObsolete;
_creationDate = creationDate;
_editTargetTimestamp = editTargetTimestamp;
_isArchivedObsolete = isArchivedObsolete;
_isMarkedUnreadObsolete = isMarkedUnreadObsolete;
_lastDraftInteractionRowId = lastDraftInteractionRowId;
_lastDraftUpdateTimestamp = lastDraftUpdateTimestamp;
_lastInteractionRowId = lastInteractionRowId;
_lastSentStoryTimestamp = lastSentStoryTimestamp;
_lastVisibleSortIdObsolete = lastVisibleSortIdObsolete;
_lastVisibleSortIdOnScreenPercentageObsolete = lastVisibleSortIdOnScreenPercentageObsolete;
_mentionNotificationMode = mentionNotificationMode;
_messageDraft = messageDraft;
_messageDraftBodyRanges = messageDraftBodyRanges;
_mutedUntilDateObsolete = mutedUntilDateObsolete;
_mutedUntilTimestampObsolete = mutedUntilTimestampObsolete;
_shouldThreadBeVisible = shouldThreadBeVisible;
_storyViewMode = storyViewMode;
return self;
}
// clang-format on
// --- CODE GENERATION MARKER
- (nullable instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (!self) {
return self;
}
// renamed `hasEverHadMessage` -> `shouldThreadBeVisible`
if (!_shouldThreadBeVisible) {
NSNumber *_Nullable legacy_hasEverHadMessage = [coder decodeObjectForKey:@"hasEverHadMessage"];
if (legacy_hasEverHadMessage != nil) {
_shouldThreadBeVisible = legacy_hasEverHadMessage.boolValue;
}
}
if (_conversationColorNameObsolete.length == 0) {
_conversationColorNameObsolete = @"Obsolete";
}
NSDate *_Nullable lastMessageDate = [coder decodeObjectOfClass:NSDate.class forKey:@"lastMessageDate"];
NSDate *_Nullable archivalDate = [coder decodeObjectOfClass:NSDate.class forKey:@"archivalDate"];
_isArchivedByLegacyTimestampForSorting = [self.class legacyIsArchivedWithLastMessageDate:lastMessageDate
archivalDate:archivalDate];
if ([coder decodeObjectForKey:@"archivedAsOfMessageSortId"] != nil) {
OWSAssertDebug(!_isArchivedObsolete);
_isArchivedObsolete = YES;
}
return self;
}
- (void)anyDidInsertWithTransaction:(DBWriteTransaction *)transaction
{
[super anyDidInsertWithTransaction:transaction];
[ThreadAssociatedData createFor:self.uniqueId transaction:transaction];
if (self.shouldThreadBeVisible && ![SSKPreferences hasSavedThreadWithTransaction:transaction]) {
[SSKPreferences setHasSavedThread:YES transaction:transaction];
}
[self _anyDidInsertWithTx:transaction];
[SSKEnvironment.shared.modelReadCachesRef.threadReadCache didInsertOrUpdateThread:self transaction:transaction];
}
- (void)anyDidUpdateWithTransaction:(DBWriteTransaction *)transaction
{
[super anyDidUpdateWithTransaction:transaction];
if (self.shouldThreadBeVisible && ![SSKPreferences hasSavedThreadWithTransaction:transaction]) {
[SSKPreferences setHasSavedThread:YES transaction:transaction];
}
[SSKEnvironment.shared.modelReadCachesRef.threadReadCache didInsertOrUpdateThread:self transaction:transaction];
[PinnedThreadManagerObjcBridge handleUpdatedThread:self transaction:transaction];
}
- (BOOL)isNoteToSelf
{
return NO;
}
- (NSString *)colorSeed
{
return self.uniqueId;
}
#pragma mark - To be subclassed.
- (NSArray<SignalServiceAddress *> *)recipientAddressesWithSneakyTransaction
{
__block NSArray<SignalServiceAddress *> *recipientAddresses;
[SSKEnvironment.shared.databaseStorageRef readWithBlock:^(
DBReadTransaction *transaction) { recipientAddresses = [self recipientAddressesWithTransaction:transaction]; }];
return recipientAddresses;
}
- (NSArray<SignalServiceAddress *> *)recipientAddressesWithTransaction:(DBReadTransaction *)transaction
{
OWSAbstractMethod();
return @[];
}
- (BOOL)hasSafetyNumbers
{
return NO;
}
#pragma mark - Interactions
- (nullable TSInteraction *)lastInteractionForInboxWithTransaction:(DBReadTransaction *)transaction
{
OWSAssertDebug(transaction);
return [[[InteractionFinder alloc] initWithThreadUniqueId:self.uniqueId]
mostRecentInteractionForInboxWithTransaction:transaction];
}
- (nullable TSInteraction *)firstInteractionAtOrAroundSortId:(uint64_t)sortId
transaction:(DBReadTransaction *)transaction
{
OWSAssertDebug(transaction);
return
[[[InteractionFinder alloc] initWithThreadUniqueId:self.uniqueId] firstInteractionAtOrAroundSortId:sortId
transaction:transaction];
}
#pragma mark - Archival
+ (BOOL)legacyIsArchivedWithLastMessageDate:(nullable NSDate *)lastMessageDate
archivalDate:(nullable NSDate *)archivalDate
{
if (!archivalDate) {
return NO;
}
if (!lastMessageDate) {
return YES;
}
return [archivalDate compare:lastMessageDate] != NSOrderedAscending;
}
#pragma mark - Merging
- (void)mergeFrom:(TSThread *)otherThread
{
self.shouldThreadBeVisible = self.shouldThreadBeVisible || otherThread.shouldThreadBeVisible;
self.lastInteractionRowId = MAX(self.lastInteractionRowId, otherThread.lastInteractionRowId);
// Copy the draft if this thread doesn't have one. We always assign both
// values if we assign one of them since they're related.
if (self.messageDraft == nil) {
self.messageDraft = otherThread.messageDraft;
self.messageDraftBodyRanges = otherThread.messageDraftBodyRanges;
self.lastDraftInteractionRowId = otherThread.lastDraftInteractionRowId;
self.lastDraftUpdateTimestamp = otherThread.lastDraftUpdateTimestamp;
}
}
@end
NS_ASSUME_NONNULL_END