Path: blob/main/SignalServiceKit/Messages/Interactions/TSInteraction.m
1 views
//1// Copyright 2017 Signal Messenger, LLC2// SPDX-License-Identifier: AGPL-3.0-only3//45#import "TSInteraction.h"6#import <SignalServiceKit/SignalServiceKit-Swift.h>78NS_ASSUME_NONNULL_BEGIN910NSString *NSStringFromOWSInteractionType(OWSInteractionType value)11{12switch (value) {13case OWSInteractionType_Unknown:14return @"OWSInteractionType_Unknown";15case OWSInteractionType_IncomingMessage:16return @"OWSInteractionType_IncomingMessage";17case OWSInteractionType_OutgoingMessage:18return @"OWSInteractionType_OutgoingMessage";19case OWSInteractionType_Error:20return @"OWSInteractionType_Error";21case OWSInteractionType_Call:22return @"OWSInteractionType_Call";23case OWSInteractionType_Info:24return @"OWSInteractionType_Info";25case OWSInteractionType_ThreadDetails:26return @"OWSInteractionType_ThreadDetails";27case OWSInteractionType_TypingIndicator:28return @"OWSInteractionType_TypingIndicator";29case OWSInteractionType_UnreadIndicator:30return @"OWSInteractionType_UnreadIndicator";31case OWSInteractionType_DateHeader:32return @"OWSInteractionType_DateHeader";33case OWSInteractionType_UnknownThreadWarning:34return @"OWSInteractionType_UnknownThreadWarning";35case OWSInteractionType_DefaultDisappearingMessageTimer:36return @"OWSInteractionType_DefaultDisappearingMessageTimer";37case OWSInteractionType_CollapseSet:38return @"OWSInteractionType_CollapseSet";39}40}4142// MARK: -4344@interface TSInteraction ()4546@property (nonatomic) uint64_t sortId;47@property (nonatomic) uint64_t receivedAtTimestamp;48@property (nonatomic) uint64_t timestamp;4950@end5152// MARK: -5354@implementation TSInteraction5556- (instancetype)initWithCustomUniqueId:(NSString *)uniqueId57timestamp:(uint64_t)timestamp58receivedAtTimestamp:(uint64_t)receivedAtTimestamp59thread:(TSThread *)thread60{61self = [super initWithUniqueId:uniqueId];6263if (!self) {64return self;65}6667_timestamp = timestamp;68_receivedAtTimestamp = receivedAtTimestamp;69_uniqueThreadId = thread.uniqueId;7071return self;72}7374- (instancetype)initWithTimestamp:(uint64_t)timestamp75receivedAtTimestamp:(uint64_t)receivedAtTimestamp76thread:(TSThread *)thread77{78// Use a sequential UUID for interaction inserts, as an optimization for the79// corresponding insert into the index on `uniqueId`. See comments about80// UUIDv7 for more.81NSString *uniqueId = [[NSUUID sequential] UUIDString];82self = [super initWithUniqueId:uniqueId];8384if (!self) {85return self;86}8788_timestamp = timestamp;89_receivedAtTimestamp = receivedAtTimestamp;90_uniqueThreadId = thread.uniqueId;9192return self;93}9495// --- CODE GENERATION MARKER9697// This snippet is generated by /Scripts/sds_codegen/sds_generate.py. Do not manually edit it, instead run98// `sds_codegen.sh`.99100// clang-format off101102- (instancetype)initWithGrdbId:(int64_t)grdbId103uniqueId:(NSString *)uniqueId104receivedAtTimestamp:(uint64_t)receivedAtTimestamp105sortId:(uint64_t)sortId106timestamp:(uint64_t)timestamp107uniqueThreadId:(NSString *)uniqueThreadId108{109self = [super initWithGrdbId:grdbId110uniqueId:uniqueId];111112if (!self) {113return self;114}115116_receivedAtTimestamp = receivedAtTimestamp;117_sortId = sortId;118_timestamp = timestamp;119_uniqueThreadId = uniqueThreadId;120121return self;122}123124// clang-format on125126// --- CODE GENERATION MARKER127128- (void)encodeWithCoder:(NSCoder *)coder129{130[self encodeIdsWithCoder:coder];131[coder encodeObject:[self valueForKey:@"receivedAtTimestamp"] forKey:@"receivedAtTimestamp"];132[coder encodeObject:[self valueForKey:@"sortId"] forKey:@"sortId"];133[coder encodeObject:[self valueForKey:@"timestamp"] forKey:@"timestamp"];134NSString *uniqueThreadId = self.uniqueThreadId;135if (uniqueThreadId != nil) {136[coder encodeObject:uniqueThreadId forKey:@"uniqueThreadId"];137}138}139140- (nullable instancetype)initWithCoder:(NSCoder *)coder141{142self = [super initWithCoder:coder];143if (!self) {144return self;145}146self->_receivedAtTimestamp = [(NSNumber *)[coder decodeObjectOfClass:[NSNumber class]147forKey:@"receivedAtTimestamp"] unsignedLongLongValue];148self->_sortId = [(NSNumber *)[coder decodeObjectOfClass:[NSNumber class] forKey:@"sortId"] unsignedLongValue];149self->_timestamp = [(NSNumber *)[coder decodeObjectOfClass:[NSNumber class]150forKey:@"timestamp"] unsignedLongLongValue];151self->_uniqueThreadId = [coder decodeObjectOfClass:[NSString class] forKey:@"uniqueThreadId"];152153// Previously the receivedAtTimestamp field lived on TSMessage, but we've moved it up154// to the TSInteraction superclass.155if (_receivedAtTimestamp == 0) {156// Upgrade from the older "TSMessage.receivedAtDate" and "TSMessage.receivedAt" properties if157// necessary.158NSDate *receivedAtDate = [coder decodeObjectOfClass:[NSDate class] forKey:@"receivedAtDate"];159if (!receivedAtDate) {160receivedAtDate = [coder decodeObjectOfClass:[NSDate class] forKey:@"receivedAt"];161}162163if (receivedAtDate) {164_receivedAtTimestamp = [NSDate ows_millisecondsSince1970ForDate:receivedAtDate];165}166167// For TSInteractions which are not TSMessage's, the timestamp *is* the receivedAtTimestamp168if (_receivedAtTimestamp == 0) {169_receivedAtTimestamp = _timestamp;170}171}172173return self;174}175176- (NSUInteger)hash177{178NSUInteger result = [super hash];179result ^= self.receivedAtTimestamp;180result ^= self.sortId;181result ^= self.timestamp;182result ^= self.uniqueThreadId.hash;183return result;184}185186- (BOOL)isEqual:(id)other187{188if (![super isEqual:other]) {189return NO;190}191TSInteraction *typedOther = (TSInteraction *)other;192if (self.receivedAtTimestamp != typedOther.receivedAtTimestamp) {193return NO;194}195if (self.sortId != typedOther.sortId) {196return NO;197}198if (self.timestamp != typedOther.timestamp) {199return NO;200}201if (![NSObject isObject:self.uniqueThreadId equalToObject:typedOther.uniqueThreadId]) {202return NO;203}204return YES;205}206207#pragma mark Thread208209- (nullable TSThread *)threadWithTx:(DBReadTransaction *)tx210{211if (self.uniqueThreadId == nil) {212// This might be true for a few legacy interactions enqueued in the message213// sender. The message sender will handle this case.214return nil;215}216217// However, it's also possible that the thread doesn't exist.218return [TSThread fetchViaCacheObjCWithUniqueId:self.uniqueThreadId transaction:tx];219}220221#pragma mark Date operations222223- (NSDate *)receivedAtDate224{225return [NSDate ows_dateWithMillisecondsSince1970:self.receivedAtTimestamp];226}227228- (NSDate *)timestampDate229{230return [NSDate ows_dateWithMillisecondsSince1970:self.timestamp];231}232233- (OWSInteractionType)interactionType234{235OWSFailDebug(@"unknown interaction type.");236237return OWSInteractionType_Unknown;238}239240- (NSString *)description241{242return [NSString243stringWithFormat:@"%@ in thread: %@ timestamp: %llu", [super description], self.uniqueThreadId, self.timestamp];244}245246#pragma mark -247248- (BOOL)isDynamicInteraction249{250return NO;251}252253#pragma mark - sorting migration254255- (void)replaceSortId:(uint64_t)sortId256{257_sortId = sortId;258}259260#if TESTABLE_BUILD261262- (void)replaceTimestamp:(uint64_t)timestamp transaction:(DBWriteTransaction *)transaction263{264[self anyUpdateWithTransaction:transaction265block:^(TSInteraction *interaction) { interaction.timestamp = timestamp; }];266}267268- (void)replaceReceivedAtTimestamp:(uint64_t)receivedAtTimestamp269{270self.receivedAtTimestamp = receivedAtTimestamp;271}272273- (void)replaceReceivedAtTimestamp:(uint64_t)receivedAtTimestamp transaction:(DBWriteTransaction *)transaction274{275[self anyUpdateWithTransaction:transaction276block:^(TSInteraction *interaction) {277interaction.receivedAtTimestamp = receivedAtTimestamp;278}];279}280#endif281282@end283284NS_ASSUME_NONNULL_END285286287