mirror of https://github.com/oxen-io/session-ios
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
192 lines
5.6 KiB
Matlab
192 lines
5.6 KiB
Matlab
8 years ago
|
//
|
||
|
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||
|
//
|
||
|
|
||
|
#import "OWSReadReceiptManager.h"
|
||
|
#import "OWSMessageSender.h"
|
||
|
#import "OWSReadReceipt.h"
|
||
|
#import "OWSReadReceiptsMessage.h"
|
||
|
#import "TSContactThread.h"
|
||
|
#import "TSDatabaseView.h"
|
||
|
#import "TSIncomingMessage.h"
|
||
|
#import "TextSecureKitEnv.h"
|
||
|
#import "Threading.h"
|
||
|
|
||
|
NS_ASSUME_NONNULL_BEGIN
|
||
|
|
||
|
@interface OWSReadReceiptManager ()
|
||
|
|
||
|
@property (nonatomic, readonly) TSStorageManager *storageManager;
|
||
|
@property (nonatomic, readonly) OWSMessageSender *messageSender;
|
||
|
|
||
|
// A map of "thread unique id"-to-"read receipt" for read receipts that
|
||
|
// we will send to our linked devices.
|
||
|
//
|
||
|
// Should only be accessed while synchronized on the OWSReadReceiptManager.
|
||
|
@property (nonatomic, readonly) NSMutableDictionary<NSString *, OWSReadReceipt *> *toLinkedDevicesReadReceiptMap;
|
||
|
|
||
|
// Should only be accessed while synchronized on the OWSReadReceiptManager.
|
||
|
@property (nonatomic) BOOL isProcessing;
|
||
|
|
||
|
@end
|
||
|
|
||
|
#pragma mark -
|
||
|
|
||
|
@implementation OWSReadReceiptManager
|
||
|
|
||
|
+ (instancetype)sharedManager
|
||
|
{
|
||
|
static OWSReadReceiptManager *sharedMyManager = nil;
|
||
|
static dispatch_once_t onceToken;
|
||
|
dispatch_once(&onceToken, ^{
|
||
|
sharedMyManager = [[self alloc] initDefault];
|
||
|
});
|
||
|
return sharedMyManager;
|
||
|
}
|
||
|
|
||
|
- (instancetype)initDefault
|
||
|
{
|
||
|
OWSMessageSender *messageSender = [TextSecureKitEnv sharedEnv].messageSender;
|
||
|
|
||
|
return [self initWithMessageSender:messageSender];
|
||
|
}
|
||
|
|
||
|
- (instancetype)initWithMessageSender:(OWSMessageSender *)messageSender
|
||
|
{
|
||
|
self = [super init];
|
||
|
|
||
|
if (!self) {
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
_messageSender = messageSender;
|
||
|
|
||
|
_toLinkedDevicesReadReceiptMap = [NSMutableDictionary new];
|
||
|
|
||
|
OWSSingletonAssert();
|
||
|
|
||
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||
|
selector:@selector(databaseViewRegistrationComplete)
|
||
|
name:kNSNotificationName_DatabaseViewRegistrationComplete
|
||
|
object:nil];
|
||
|
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
- (void)dealloc
|
||
|
{
|
||
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||
|
}
|
||
|
|
||
|
- (void)databaseViewRegistrationComplete
|
||
|
{
|
||
|
[self scheduleProcessing];
|
||
|
}
|
||
|
|
||
|
// Schedules a processing pass, unless one is already scheduled.
|
||
|
- (void)scheduleProcessing
|
||
|
{
|
||
|
DispatchMainThreadSafe(^{
|
||
|
@synchronized(self)
|
||
|
{
|
||
|
if ([TSDatabaseView hasPendingViewRegistrations]) {
|
||
|
return;
|
||
|
}
|
||
|
if (self.isProcessing) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
self.isProcessing = YES;
|
||
|
|
||
|
// Process read receipts every N seconds.
|
||
|
//
|
||
|
// We want a value high enough to allow us to effectively deduplicate,
|
||
|
// read receipts without being so high that we risk not sending read
|
||
|
// receipts due to app exit.
|
||
|
const CGFloat kProcessingFrequencySeconds = 3.f;
|
||
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kProcessingFrequencySeconds * NSEC_PER_SEC)),
|
||
|
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
|
||
|
^{
|
||
|
[self process];
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
- (void)process
|
||
|
{
|
||
|
@synchronized(self)
|
||
|
{
|
||
|
self.isProcessing = NO;
|
||
|
|
||
|
NSArray<OWSReadReceipt *> *readReceiptsToSend = [[self.toLinkedDevicesReadReceiptMap allValues] copy];
|
||
|
[self.toLinkedDevicesReadReceiptMap removeAllObjects];
|
||
|
if (readReceiptsToSend.count > 0) {
|
||
|
OWSReadReceiptsMessage *message = [[OWSReadReceiptsMessage alloc] initWithReadReceipts:readReceiptsToSend];
|
||
|
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
[self.messageSender sendMessage:message
|
||
|
success:^{
|
||
|
DDLogInfo(@"%@ Successfully sent %zd read receipt to linked devices.",
|
||
|
self.tag,
|
||
|
readReceiptsToSend.count);
|
||
|
}
|
||
|
failure:^(NSError *error) {
|
||
|
DDLogError(@"%@ Failed to send read receipt to linked devices with error: %@", self.tag, error);
|
||
|
}];
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (void)messageWasReadLocally:(TSIncomingMessage *)message;
|
||
|
{
|
||
|
@synchronized(self)
|
||
|
{
|
||
|
NSString *threadUniqueId = message.thread.uniqueId;
|
||
|
OWSAssert(threadUniqueId.length > 0);
|
||
|
|
||
|
// Only groupthread sets authorId, thus this crappy code.
|
||
|
// TODO Refactor so that ALL incoming messages have an authorId.
|
||
|
NSString *messageAuthorId;
|
||
|
if (message.authorId) {
|
||
|
// Group Thread
|
||
|
messageAuthorId = message.authorId;
|
||
|
} else {
|
||
|
// Contact Thread
|
||
|
messageAuthorId = [TSContactThread contactIdFromThreadId:message.uniqueThreadId];
|
||
|
}
|
||
|
OWSAssert(messageAuthorId.length > 0);
|
||
|
|
||
|
OWSReadReceipt *newReadReceipt =
|
||
|
[[OWSReadReceipt alloc] initWithSenderId:messageAuthorId timestamp:message.timestamp];
|
||
|
|
||
|
OWSReadReceipt *_Nullable oldReadReceipt = self.toLinkedDevicesReadReceiptMap[threadUniqueId];
|
||
|
if (oldReadReceipt && oldReadReceipt.timestamp > newReadReceipt.timestamp) {
|
||
|
// If there's an existing read receipt for the same thread with
|
||
|
// a newer timestamp, discard the new read receipt.
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
self.toLinkedDevicesReadReceiptMap[threadUniqueId] = newReadReceipt;
|
||
|
|
||
|
[self scheduleProcessing];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#pragma mark - Logging
|
||
|
|
||
|
+ (NSString *)tag
|
||
|
{
|
||
|
return [NSString stringWithFormat:@"[%@]", self.class];
|
||
|
}
|
||
|
|
||
|
- (NSString *)tag
|
||
|
{
|
||
|
return self.class.tag;
|
||
|
}
|
||
|
|
||
|
@end
|
||
|
|
||
|
NS_ASSUME_NONNULL_END
|