2017-02-07 21:09:04 +01:00
|
|
|
//
|
2019-02-09 00:20:48 +01:00
|
|
|
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
2017-02-07 21:09:04 +01:00
|
|
|
//
|
2015-12-07 03:31:43 +01:00
|
|
|
|
|
|
|
#import "TSInteraction.h"
|
|
|
|
#import "TSDatabaseSecondaryIndexes.h"
|
2016-07-28 01:58:49 +02:00
|
|
|
#import "TSThread.h"
|
2020-06-05 02:38:44 +02:00
|
|
|
#import <SessionCoreKit/NSDate+OWS.h>
|
2020-06-05 05:43:06 +02:00
|
|
|
#import <SessionServiceKit/SessionServiceKit-Swift.h>
|
2015-12-07 03:31:43 +01:00
|
|
|
|
2017-05-04 23:49:44 +02:00
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
|
2018-06-19 22:12:16 +02:00
|
|
|
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";
|
2018-11-01 16:39:04 +01:00
|
|
|
case OWSInteractionType_TypingIndicator:
|
|
|
|
return @"OWSInteractionType_TypingIndicator";
|
2018-06-19 22:12:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-03 23:41:43 +02:00
|
|
|
@interface TSInteraction ()
|
|
|
|
|
|
|
|
@property (nonatomic) uint64_t sortId;
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
2015-12-07 03:31:43 +01:00
|
|
|
@implementation TSInteraction
|
|
|
|
|
2018-09-20 22:10:35 +02:00
|
|
|
+ (NSArray<TSInteraction *> *)interactionsWithTimestamp:(uint64_t)timestamp
|
|
|
|
ofClass:(Class)clazz
|
|
|
|
withTransaction:(YapDatabaseReadTransaction *)transaction
|
2017-09-22 05:42:42 +02:00
|
|
|
{
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(timestamp > 0);
|
2017-09-22 05:42:42 +02:00
|
|
|
|
|
|
|
// Accept any interaction.
|
2018-09-20 22:10:35 +02:00
|
|
|
return [self interactionsWithTimestamp:timestamp
|
|
|
|
filter:^(TSInteraction *interaction) {
|
|
|
|
return [interaction isKindOfClass:clazz];
|
|
|
|
}
|
|
|
|
withTransaction:transaction];
|
2017-09-22 05:42:42 +02:00
|
|
|
}
|
|
|
|
|
2018-09-20 22:10:35 +02:00
|
|
|
+ (NSArray<TSInteraction *> *)interactionsWithTimestamp:(uint64_t)timestamp
|
|
|
|
filter:(BOOL (^_Nonnull)(TSInteraction *))filter
|
|
|
|
withTransaction:(YapDatabaseReadTransaction *)transaction
|
2017-09-22 05:42:42 +02:00
|
|
|
{
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(timestamp > 0);
|
2017-09-22 05:42:42 +02:00
|
|
|
|
|
|
|
NSMutableArray<TSInteraction *> *interactions = [NSMutableArray new];
|
2015-12-07 03:31:43 +01:00
|
|
|
|
|
|
|
[TSDatabaseSecondaryIndexes
|
2018-09-20 22:10:35 +02:00
|
|
|
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];
|
2015-12-07 03:31:43 +01:00
|
|
|
|
2017-09-22 05:42:42 +02:00
|
|
|
return [interactions copy];
|
2015-12-07 03:31:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSString *)collection {
|
|
|
|
return @"TSInteraction";
|
|
|
|
}
|
|
|
|
|
2018-11-01 16:39:04 +01:00
|
|
|
- (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;
|
|
|
|
}
|
|
|
|
|
2018-09-20 22:10:35 +02:00
|
|
|
- (instancetype)initInteractionWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread
|
2016-08-22 22:09:58 +02:00
|
|
|
{
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(timestamp > 0);
|
2017-09-21 23:25:13 +02:00
|
|
|
|
2018-11-07 18:00:34 +01:00
|
|
|
self = [super initWithUniqueId:[[NSUUID UUID] UUIDString]];
|
2016-08-22 22:09:58 +02:00
|
|
|
|
|
|
|
if (!self) {
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
_timestamp = timestamp;
|
|
|
|
_uniqueThreadId = thread.uniqueId;
|
2018-10-03 23:41:43 +02:00
|
|
|
_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;
|
|
|
|
}
|
|
|
|
}
|
2016-08-22 22:09:58 +02:00
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2017-02-07 21:09:04 +01:00
|
|
|
#pragma mark Thread
|
|
|
|
|
2016-08-22 22:09:58 +02:00
|
|
|
- (TSThread *)thread
|
|
|
|
{
|
|
|
|
return [TSThread fetchObjectWithUniqueID:self.uniqueThreadId];
|
|
|
|
}
|
|
|
|
|
2018-03-12 23:04:16 +01:00
|
|
|
- (TSThread *)threadWithTransaction:(YapDatabaseReadTransaction *)transaction
|
2017-09-29 03:12:02 +02:00
|
|
|
{
|
|
|
|
return [TSThread fetchObjectWithUniqueID:self.uniqueThreadId transaction:transaction];
|
|
|
|
}
|
|
|
|
|
2017-02-07 21:09:04 +01:00
|
|
|
- (void)touchThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
|
|
|
{
|
2019-02-09 00:20:48 +01:00
|
|
|
[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];
|
2017-02-07 21:09:04 +01:00
|
|
|
}
|
|
|
|
|
2015-12-07 03:31:43 +01:00
|
|
|
#pragma mark Date operations
|
|
|
|
|
2018-10-03 23:41:43 +02:00
|
|
|
- (uint64_t)timestampForLegacySorting
|
2017-05-25 18:17:45 +02:00
|
|
|
{
|
|
|
|
return self.timestamp;
|
2017-02-16 21:39:56 +01:00
|
|
|
}
|
|
|
|
|
2018-10-03 23:41:43 +02:00
|
|
|
- (NSDate *)receivedAtDate
|
|
|
|
{
|
|
|
|
return [NSDate ows_dateWithMillisecondsSince1970:self.receivedAtTimestamp];
|
|
|
|
}
|
|
|
|
|
2017-05-26 16:32:13 +02:00
|
|
|
- (NSComparisonResult)compareForSorting:(TSInteraction *)other
|
|
|
|
{
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(other);
|
2017-05-26 16:32:13 +02:00
|
|
|
|
2020-04-20 08:53:40 +02:00
|
|
|
// Loki: Sort the messages by the sender's timestamp (Signal uses sortId)
|
2020-01-08 01:23:19 +01:00
|
|
|
uint64_t sortId1 = self.timestamp;
|
|
|
|
uint64_t sortId2 = other.timestamp;
|
2017-05-26 16:32:13 +02:00
|
|
|
|
2018-10-03 23:41:43 +02:00
|
|
|
if (sortId1 > sortId2) {
|
2017-05-26 16:32:13 +02:00
|
|
|
return NSOrderedDescending;
|
2018-10-03 23:41:43 +02:00
|
|
|
} else if (sortId1 < sortId2) {
|
2017-05-26 16:32:13 +02:00
|
|
|
return NSOrderedAscending;
|
|
|
|
} else {
|
|
|
|
return NSOrderedSame;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-10 22:13:54 +02:00
|
|
|
- (OWSInteractionType)interactionType
|
|
|
|
{
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSFailDebug(@"unknown interaction type.");
|
2017-10-10 22:13:54 +02:00
|
|
|
|
|
|
|
return OWSInteractionType_Unknown;
|
|
|
|
}
|
|
|
|
|
2018-04-21 19:00:58 +02:00
|
|
|
- (NSString *)description
|
|
|
|
{
|
2018-05-07 18:32:31 +02:00
|
|
|
return [NSString stringWithFormat:@"%@ in thread: %@ timestamp: %lu",
|
|
|
|
[super description],
|
|
|
|
self.uniqueThreadId,
|
|
|
|
(unsigned long)self.timestamp];
|
2015-12-07 03:31:43 +01:00
|
|
|
}
|
|
|
|
|
2018-10-03 23:41:43 +02:00
|
|
|
- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
|
|
|
{
|
2015-12-07 03:31:43 +01:00
|
|
|
if (!self.uniqueId) {
|
2018-12-17 21:20:14 +01:00
|
|
|
OWSFailDebug(@"Missing uniqueId.");
|
|
|
|
self.uniqueId = [NSUUID new].UUIDString;
|
2015-12-07 03:31:43 +01:00
|
|
|
}
|
2018-10-03 23:41:43 +02:00
|
|
|
if (self.sortId == 0) {
|
|
|
|
self.sortId = [SSKIncrementingIdFinder nextIdWithKey:[TSInteraction collection] transaction:transaction];
|
|
|
|
}
|
2015-12-07 03:31:43 +01:00
|
|
|
|
|
|
|
[super saveWithTransaction:transaction];
|
|
|
|
|
2018-10-03 23:41:43 +02:00
|
|
|
TSThread *fetchedThread = [self threadWithTransaction:transaction];
|
2015-12-07 03:31:43 +01:00
|
|
|
|
|
|
|
[fetchedThread updateWithLastMessage:self transaction:transaction];
|
|
|
|
}
|
|
|
|
|
2017-07-27 17:55:47 +02:00
|
|
|
- (void)removeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
|
|
|
{
|
|
|
|
[super removeWithTransaction:transaction];
|
|
|
|
|
|
|
|
[self touchThreadWithTransaction:transaction];
|
|
|
|
}
|
|
|
|
|
2017-05-23 16:12:04 +02:00
|
|
|
- (BOOL)isDynamicInteraction
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2018-10-03 23:41:43 +02:00
|
|
|
#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];
|
|
|
|
}
|
|
|
|
|
2015-12-07 03:31:43 +01:00
|
|
|
@end
|
2017-05-04 23:49:44 +02:00
|
|
|
|
|
|
|
NS_ASSUME_NONNULL_END
|