260 lines
8.6 KiB
Objective-C
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
|