286 lines
8.4 KiB
Objective-C
286 lines
8.4 KiB
Objective-C
//
|
|
// Copyright 2017 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
#import "TSInteraction.h"
|
|
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
NSString *NSStringFromOWSInteractionType(OWSInteractionType value)
|
|
{
|
|
switch (value) {
|
|
case OWSInteractionType_Unknown:
|
|
return @"OWSInteractionType_Unknown";
|
|
case OWSInteractionType_IncomingMessage:
|
|
return @"OWSInteractionType_IncomingMessage";
|
|
case OWSInteractionType_OutgoingMessage:
|
|
return @"OWSInteractionType_OutgoingMessage";
|
|
case OWSInteractionType_Error:
|
|
return @"OWSInteractionType_Error";
|
|
case OWSInteractionType_Call:
|
|
return @"OWSInteractionType_Call";
|
|
case OWSInteractionType_Info:
|
|
return @"OWSInteractionType_Info";
|
|
case OWSInteractionType_ThreadDetails:
|
|
return @"OWSInteractionType_ThreadDetails";
|
|
case OWSInteractionType_TypingIndicator:
|
|
return @"OWSInteractionType_TypingIndicator";
|
|
case OWSInteractionType_UnreadIndicator:
|
|
return @"OWSInteractionType_UnreadIndicator";
|
|
case OWSInteractionType_DateHeader:
|
|
return @"OWSInteractionType_DateHeader";
|
|
case OWSInteractionType_UnknownThreadWarning:
|
|
return @"OWSInteractionType_UnknownThreadWarning";
|
|
case OWSInteractionType_DefaultDisappearingMessageTimer:
|
|
return @"OWSInteractionType_DefaultDisappearingMessageTimer";
|
|
case OWSInteractionType_CollapseSet:
|
|
return @"OWSInteractionType_CollapseSet";
|
|
}
|
|
}
|
|
|
|
// MARK: -
|
|
|
|
@interface TSInteraction ()
|
|
|
|
@property (nonatomic) uint64_t sortId;
|
|
@property (nonatomic) uint64_t receivedAtTimestamp;
|
|
@property (nonatomic) uint64_t timestamp;
|
|
|
|
@end
|
|
|
|
// MARK: -
|
|
|
|
@implementation TSInteraction
|
|
|
|
- (instancetype)initWithCustomUniqueId:(NSString *)uniqueId
|
|
timestamp:(uint64_t)timestamp
|
|
receivedAtTimestamp:(uint64_t)receivedAtTimestamp
|
|
thread:(TSThread *)thread
|
|
{
|
|
self = [super initWithUniqueId:uniqueId];
|
|
|
|
if (!self) {
|
|
return self;
|
|
}
|
|
|
|
_timestamp = timestamp;
|
|
_receivedAtTimestamp = receivedAtTimestamp;
|
|
_uniqueThreadId = thread.uniqueId;
|
|
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
|
receivedAtTimestamp:(uint64_t)receivedAtTimestamp
|
|
thread:(TSThread *)thread
|
|
{
|
|
// Use a sequential UUID for interaction inserts, as an optimization for the
|
|
// corresponding insert into the index on `uniqueId`. See comments about
|
|
// UUIDv7 for more.
|
|
NSString *uniqueId = [[NSUUID sequential] UUIDString];
|
|
self = [super initWithUniqueId:uniqueId];
|
|
|
|
if (!self) {
|
|
return self;
|
|
}
|
|
|
|
_timestamp = timestamp;
|
|
_receivedAtTimestamp = receivedAtTimestamp;
|
|
_uniqueThreadId = thread.uniqueId;
|
|
|
|
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
|
|
receivedAtTimestamp:(uint64_t)receivedAtTimestamp
|
|
sortId:(uint64_t)sortId
|
|
timestamp:(uint64_t)timestamp
|
|
uniqueThreadId:(NSString *)uniqueThreadId
|
|
{
|
|
self = [super initWithGrdbId:grdbId
|
|
uniqueId:uniqueId];
|
|
|
|
if (!self) {
|
|
return self;
|
|
}
|
|
|
|
_receivedAtTimestamp = receivedAtTimestamp;
|
|
_sortId = sortId;
|
|
_timestamp = timestamp;
|
|
_uniqueThreadId = uniqueThreadId;
|
|
|
|
return self;
|
|
}
|
|
|
|
// clang-format on
|
|
|
|
// --- CODE GENERATION MARKER
|
|
|
|
- (void)encodeWithCoder:(NSCoder *)coder
|
|
{
|
|
[self encodeIdsWithCoder:coder];
|
|
[coder encodeObject:[self valueForKey:@"receivedAtTimestamp"] forKey:@"receivedAtTimestamp"];
|
|
[coder encodeObject:[self valueForKey:@"sortId"] forKey:@"sortId"];
|
|
[coder encodeObject:[self valueForKey:@"timestamp"] forKey:@"timestamp"];
|
|
NSString *uniqueThreadId = self.uniqueThreadId;
|
|
if (uniqueThreadId != nil) {
|
|
[coder encodeObject:uniqueThreadId forKey:@"uniqueThreadId"];
|
|
}
|
|
}
|
|
|
|
- (nullable instancetype)initWithCoder:(NSCoder *)coder
|
|
{
|
|
self = [super initWithCoder:coder];
|
|
if (!self) {
|
|
return self;
|
|
}
|
|
self->_receivedAtTimestamp = [(NSNumber *)[coder decodeObjectOfClass:[NSNumber class]
|
|
forKey:@"receivedAtTimestamp"] unsignedLongLongValue];
|
|
self->_sortId = [(NSNumber *)[coder decodeObjectOfClass:[NSNumber class] forKey:@"sortId"] unsignedLongValue];
|
|
self->_timestamp = [(NSNumber *)[coder decodeObjectOfClass:[NSNumber class]
|
|
forKey:@"timestamp"] unsignedLongLongValue];
|
|
self->_uniqueThreadId = [coder decodeObjectOfClass:[NSString class] forKey:@"uniqueThreadId"];
|
|
|
|
// Previously the receivedAtTimestamp field lived on TSMessage, but we've moved it up
|
|
// to the TSInteraction superclass.
|
|
if (_receivedAtTimestamp == 0) {
|
|
// Upgrade from the older "TSMessage.receivedAtDate" and "TSMessage.receivedAt" properties if
|
|
// necessary.
|
|
NSDate *receivedAtDate = [coder decodeObjectOfClass:[NSDate class] forKey:@"receivedAtDate"];
|
|
if (!receivedAtDate) {
|
|
receivedAtDate = [coder decodeObjectOfClass:[NSDate class] forKey:@"receivedAt"];
|
|
}
|
|
|
|
if (receivedAtDate) {
|
|
_receivedAtTimestamp = [NSDate ows_millisecondsSince1970ForDate:receivedAtDate];
|
|
}
|
|
|
|
// For TSInteractions which are not TSMessage's, the timestamp *is* the receivedAtTimestamp
|
|
if (_receivedAtTimestamp == 0) {
|
|
_receivedAtTimestamp = _timestamp;
|
|
}
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (NSUInteger)hash
|
|
{
|
|
NSUInteger result = [super hash];
|
|
result ^= self.receivedAtTimestamp;
|
|
result ^= self.sortId;
|
|
result ^= self.timestamp;
|
|
result ^= self.uniqueThreadId.hash;
|
|
return result;
|
|
}
|
|
|
|
- (BOOL)isEqual:(id)other
|
|
{
|
|
if (![super isEqual:other]) {
|
|
return NO;
|
|
}
|
|
TSInteraction *typedOther = (TSInteraction *)other;
|
|
if (self.receivedAtTimestamp != typedOther.receivedAtTimestamp) {
|
|
return NO;
|
|
}
|
|
if (self.sortId != typedOther.sortId) {
|
|
return NO;
|
|
}
|
|
if (self.timestamp != typedOther.timestamp) {
|
|
return NO;
|
|
}
|
|
if (![NSObject isObject:self.uniqueThreadId equalToObject:typedOther.uniqueThreadId]) {
|
|
return NO;
|
|
}
|
|
return YES;
|
|
}
|
|
|
|
#pragma mark Thread
|
|
|
|
- (nullable TSThread *)threadWithTx:(DBReadTransaction *)tx
|
|
{
|
|
if (self.uniqueThreadId == nil) {
|
|
// This might be true for a few legacy interactions enqueued in the message
|
|
// sender. The message sender will handle this case.
|
|
return nil;
|
|
}
|
|
|
|
// However, it's also possible that the thread doesn't exist.
|
|
return [TSThread fetchViaCacheObjCWithUniqueId:self.uniqueThreadId transaction:tx];
|
|
}
|
|
|
|
#pragma mark Date operations
|
|
|
|
- (NSDate *)receivedAtDate
|
|
{
|
|
return [NSDate ows_dateWithMillisecondsSince1970:self.receivedAtTimestamp];
|
|
}
|
|
|
|
- (NSDate *)timestampDate
|
|
{
|
|
return [NSDate ows_dateWithMillisecondsSince1970:self.timestamp];
|
|
}
|
|
|
|
- (OWSInteractionType)interactionType
|
|
{
|
|
OWSFailDebug(@"unknown interaction type.");
|
|
|
|
return OWSInteractionType_Unknown;
|
|
}
|
|
|
|
- (NSString *)description
|
|
{
|
|
return [NSString
|
|
stringWithFormat:@"%@ in thread: %@ timestamp: %llu", [super description], self.uniqueThreadId, self.timestamp];
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (BOOL)isDynamicInteraction
|
|
{
|
|
return NO;
|
|
}
|
|
|
|
#pragma mark - sorting migration
|
|
|
|
- (void)replaceSortId:(uint64_t)sortId
|
|
{
|
|
_sortId = sortId;
|
|
}
|
|
|
|
#if TESTABLE_BUILD
|
|
|
|
- (void)replaceTimestamp:(uint64_t)timestamp transaction:(DBWriteTransaction *)transaction
|
|
{
|
|
[self anyUpdateWithTransaction:transaction
|
|
block:^(TSInteraction *interaction) { interaction.timestamp = timestamp; }];
|
|
}
|
|
|
|
- (void)replaceReceivedAtTimestamp:(uint64_t)receivedAtTimestamp
|
|
{
|
|
self.receivedAtTimestamp = receivedAtTimestamp;
|
|
}
|
|
|
|
- (void)replaceReceivedAtTimestamp:(uint64_t)receivedAtTimestamp transaction:(DBWriteTransaction *)transaction
|
|
{
|
|
[self anyUpdateWithTransaction:transaction
|
|
block:^(TSInteraction *interaction) {
|
|
interaction.receivedAtTimestamp = receivedAtTimestamp;
|
|
}];
|
|
}
|
|
#endif
|
|
|
|
@end
|
|
|
|
NS_ASSUME_NONNULL_END
|