mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
292 lines
8.6 KiB
Objective-C
292 lines
8.6 KiB
Objective-C
//
|
|
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
|
//
|
|
|
|
#import "TSInteraction.h"
|
|
#import "TSDatabaseSecondaryIndexes.h"
|
|
#import "TSThread.h"
|
|
#import "TSGroupThread.h"
|
|
#import <SessionProtocolKit/NSDate+OWS.h>
|
|
#import <SignalUtilitiesKit/SignalUtilitiesKit-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_Offer:
|
|
return @"OWSInteractionType_Offer";
|
|
case OWSInteractionType_TypingIndicator:
|
|
return @"OWSInteractionType_TypingIndicator";
|
|
}
|
|
}
|
|
|
|
@interface TSInteraction ()
|
|
|
|
@property (nonatomic) uint64_t sortId;
|
|
|
|
@end
|
|
|
|
@implementation TSInteraction
|
|
|
|
@synthesize timestamp = _timestamp;
|
|
|
|
+ (NSArray<TSInteraction *> *)interactionsWithTimestamp:(uint64_t)timestamp
|
|
ofClass:(Class)clazz
|
|
withTransaction:(YapDatabaseReadTransaction *)transaction
|
|
{
|
|
OWSAssertDebug(timestamp > 0);
|
|
|
|
// Accept any interaction.
|
|
return [self interactionsWithTimestamp:timestamp
|
|
filter:^(TSInteraction *interaction) {
|
|
return [interaction isKindOfClass:clazz];
|
|
}
|
|
withTransaction:transaction];
|
|
}
|
|
|
|
+ (NSArray<TSInteraction *> *)interactionsWithTimestamp:(uint64_t)timestamp
|
|
filter:(BOOL (^_Nonnull)(TSInteraction *))filter
|
|
withTransaction:(YapDatabaseReadTransaction *)transaction
|
|
{
|
|
OWSAssertDebug(timestamp > 0);
|
|
|
|
NSMutableArray<TSInteraction *> *interactions = [NSMutableArray new];
|
|
|
|
[TSDatabaseSecondaryIndexes
|
|
enumerateMessagesWithTimestamp:timestamp
|
|
withBlock:^(NSString *collection, NSString *key, BOOL *stop) {
|
|
TSInteraction *interaction =
|
|
[TSInteraction fetchObjectWithUniqueID:key transaction:transaction];
|
|
if (!filter(interaction)) {
|
|
return;
|
|
}
|
|
[interactions addObject:interaction];
|
|
}
|
|
usingTransaction:transaction];
|
|
|
|
return [interactions copy];
|
|
}
|
|
|
|
+ (NSString *)collection {
|
|
return @"TSInteraction";
|
|
}
|
|
|
|
- (instancetype)initInteractionWithUniqueId:(NSString *)uniqueId
|
|
timestamp:(uint64_t)timestamp
|
|
inThread:(TSThread *)thread
|
|
{
|
|
OWSAssertDebug(timestamp > 0);
|
|
|
|
self = [super initWithUniqueId:uniqueId];
|
|
|
|
if (!self) {
|
|
return self;
|
|
}
|
|
|
|
_timestamp = timestamp;
|
|
_uniqueThreadId = thread.uniqueId;
|
|
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initInteractionWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread
|
|
{
|
|
OWSAssertDebug(timestamp > 0);
|
|
|
|
self = [super initWithUniqueId:[[NSUUID UUID] UUIDString]];
|
|
|
|
if (!self) {
|
|
return self;
|
|
}
|
|
|
|
_timestamp = timestamp;
|
|
_uniqueThreadId = thread.uniqueId;
|
|
_receivedAtTimestamp = [NSDate ows_millisecondTimeStamp];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (nullable instancetype)initWithCoder:(NSCoder *)coder
|
|
{
|
|
self = [super initWithCoder:coder];
|
|
if (!self) {
|
|
return nil;
|
|
}
|
|
|
|
// 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 decodeObjectForKey:@"receivedAtDate"];
|
|
if (!receivedAtDate) {
|
|
receivedAtDate = [coder decodeObjectForKey:@"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;
|
|
}
|
|
|
|
#pragma mark Thread
|
|
|
|
- (TSThread *)thread
|
|
{
|
|
return [TSThread fetchObjectWithUniqueID:self.uniqueThreadId];
|
|
}
|
|
|
|
- (TSThread *)threadWithTransaction:(YapDatabaseReadTransaction *)transaction
|
|
{
|
|
return [TSThread fetchObjectWithUniqueID:self.uniqueThreadId transaction:transaction];
|
|
}
|
|
|
|
- (void)touchThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
|
{
|
|
[transaction touchObjectForKey:self.uniqueThreadId inCollection:[TSThread collection]];
|
|
}
|
|
|
|
- (void)applyChangeToSelfAndLatestCopy:(YapDatabaseReadWriteTransaction *)transaction
|
|
changeBlock:(void (^)(id))changeBlock
|
|
{
|
|
OWSAssertDebug(transaction);
|
|
|
|
[super applyChangeToSelfAndLatestCopy:transaction changeBlock:changeBlock];
|
|
[self touchThreadWithTransaction:transaction];
|
|
}
|
|
|
|
#pragma mark Date operations
|
|
|
|
- (uint64_t)timestampForUI
|
|
{
|
|
if (_shouldUseServerTime) {
|
|
return _receivedAtTimestamp;
|
|
}
|
|
return _timestamp;
|
|
}
|
|
|
|
- (uint64_t)timestampForLegacySorting
|
|
{
|
|
return self.timestamp;
|
|
}
|
|
|
|
- (void)setServerTimestampToReceivedTimestamp:(uint64_t)receivedAtTimestamp
|
|
{
|
|
_shouldUseServerTime = YES;
|
|
_receivedAtTimestamp = receivedAtTimestamp;
|
|
}
|
|
|
|
- (NSDate *)receivedAtDate
|
|
{
|
|
return [NSDate ows_dateWithMillisecondsSince1970:self.receivedAtTimestamp];
|
|
}
|
|
|
|
- (NSComparisonResult)compareForSorting:(TSInteraction *)other
|
|
{
|
|
OWSAssertDebug(other);
|
|
|
|
// Sort the messages by the sender's timestamp (Signal uses sortId)
|
|
uint64_t sortId1 = self.timestamp;
|
|
uint64_t sortId2 = other.timestamp;
|
|
|
|
// In open groups messages should be sorted by their server timestamp. `sortId` represents the order in which messages
|
|
// were processed. Since in the open group poller we sort messages by their server timestamp, sorting by `sortId` is
|
|
// effectively the same as sorting by server timestamp.
|
|
if (self.thread.isGroupThread) {
|
|
TSGroupThread *thread = (TSGroupThread *)self.thread;
|
|
if (thread.isPublicChat) {
|
|
sortId1 = self.sortId;
|
|
sortId2 = other.sortId;
|
|
}
|
|
}
|
|
|
|
if (sortId1 > sortId2) {
|
|
return NSOrderedDescending;
|
|
} else if (sortId1 < sortId2) {
|
|
return NSOrderedAscending;
|
|
} else {
|
|
return NSOrderedSame;
|
|
}
|
|
}
|
|
|
|
- (OWSInteractionType)interactionType
|
|
{
|
|
OWSFailDebug(@"unknown interaction type.");
|
|
|
|
return OWSInteractionType_Unknown;
|
|
}
|
|
|
|
- (NSString *)description
|
|
{
|
|
return [NSString stringWithFormat:@"%@ in thread: %@ timestamp: %lu",
|
|
[super description],
|
|
self.uniqueThreadId,
|
|
(unsigned long)self.timestamp];
|
|
}
|
|
|
|
- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
|
{
|
|
if (!self.uniqueId) {
|
|
OWSFailDebug(@"Missing uniqueId.");
|
|
self.uniqueId = [NSUUID new].UUIDString;
|
|
}
|
|
if (self.sortId == 0) {
|
|
self.sortId = [SSKIncrementingIdFinder nextIdWithKey:[TSInteraction collection] transaction:transaction];
|
|
}
|
|
|
|
[super saveWithTransaction:transaction];
|
|
|
|
TSThread *fetchedThread = [self threadWithTransaction:transaction];
|
|
|
|
[fetchedThread updateWithLastMessage:self transaction:transaction];
|
|
}
|
|
|
|
- (void)removeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
|
{
|
|
[super removeWithTransaction:transaction];
|
|
|
|
[self touchThreadWithTransaction:transaction];
|
|
}
|
|
|
|
- (BOOL)isDynamicInteraction
|
|
{
|
|
return NO;
|
|
}
|
|
|
|
#pragma mark - sorting migration
|
|
|
|
- (void)saveNextSortIdWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
|
{
|
|
if (self.sortId != 0) {
|
|
// This could happen if something else in our startup process saved the interaction
|
|
// e.g. another migration ran.
|
|
// During the migration, since we're enumerating the interactions in the proper order,
|
|
// we want to ignore any previously assigned sortId
|
|
self.sortId = 0;
|
|
}
|
|
[self saveWithTransaction:transaction];
|
|
}
|
|
|
|
@end
|
|
|
|
NS_ASSUME_NONNULL_END
|