Respond to CR.

// FREEBIE
This commit is contained in:
Matthew Chen 2017-09-22 15:15:04 -04:00
parent 825503210b
commit b74da07f7e
9 changed files with 121 additions and 73 deletions

View file

@ -3,7 +3,7 @@
// //
#import "OWSReadReceiptsForSenderMessage.h" #import "OWSReadReceiptsForSenderMessage.h"
#import "NSDate+millisecondTimeStamp.h" #import "NSDate+OWS.h"
#import "OWSReadReceipt.h" #import "OWSReadReceipt.h"
#import "OWSSignalServiceProtos.pb.h" #import "OWSSignalServiceProtos.pb.h"
#import "SignalRecipient.h" #import "SignalRecipient.h"
@ -56,8 +56,6 @@ NS_ASSUME_NONNULL_BEGIN
[builder addTimestamp:[messageTimestamp unsignedLongLongValue]]; [builder addTimestamp:[messageTimestamp unsignedLongLongValue]];
} }
// TODO: addLocalProfileKeyIfNecessary.
return [builder build]; return [builder build];
} }

View file

@ -104,17 +104,16 @@ NS_ASSUME_NONNULL_BEGIN
} }
if (outgoingMessage.body.length < 1 && outgoingMessage.attachmentIds.count < 1) { if (outgoingMessage.body.length < 1 && outgoingMessage.attachmentIds.count < 1) {
// TODO: Is this safe? OWSFail(@"Ignoring message transcript for empty message.");
DDLogInfo(@"Ignoring message transcript for empty message.");
return; return;
} }
// TODO: Refactor this logic. // TODO: Refactor this logic. Most of it doesn't belong in `OWSMessageSender`.
[self.messageSender handleMessageSentRemotely:outgoingMessage [self.messageSender handleMessageSentRemotely:outgoingMessage
sentAt:transcript.expirationStartedAt sentAt:transcript.expirationStartedAt
transaction:transaction]; transaction:transaction];
[self.readReceiptManager outgoingMessageFromLinkedDevice:outgoingMessage transaction:transaction]; [self.readReceiptManager updateOutgoingMessageFromLinkedDevice:outgoingMessage transaction:transaction];
[attachmentsProcessor [attachmentsProcessor
fetchAttachmentsForMessage:outgoingMessage fetchAttachmentsForMessage:outgoingMessage

View file

@ -26,8 +26,6 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark Utility Method #pragma mark Utility Method
+ (instancetype)interactionForTimestamp:(uint64_t)timestamp
withTransaction:(YapDatabaseReadWriteTransaction *)transaction;
+ (NSArray<TSInteraction *> *)interactionsWithTimestamp:(uint64_t)timestamp + (NSArray<TSInteraction *> *)interactionsWithTimestamp:(uint64_t)timestamp
ofClass:(Class)clazz ofClass:(Class)clazz
withTransaction:(YapDatabaseReadWriteTransaction *)transaction; withTransaction:(YapDatabaseReadWriteTransaction *)transaction;

View file

@ -12,28 +12,6 @@ NS_ASSUME_NONNULL_BEGIN
@implementation TSInteraction @implementation TSInteraction
+ (instancetype)interactionForTimestamp:(uint64_t)timestamp
withTransaction:(YapDatabaseReadWriteTransaction *)transaction {
OWSAssert(timestamp > 0);
// Accept any interaction.
NSArray<TSInteraction *> *interactions = [self interactionsWithTimestamp:timestamp
withFilter:^(TSInteraction *interaction) {
return YES;
}
withTransaction:transaction];
if (interactions.count < 1) {
// TODO: OWSFail()?
return nil;
}
if (interactions.count > 1) {
DDLogWarn(@"The database contains %zd colliding timestamps at: %lld.", interactions.count, timestamp);
}
TSInteraction *lastInteraction = interactions.lastObject;
return lastInteraction;
}
+ (NSArray<TSInteraction *> *)interactionsWithTimestamp:(uint64_t)timestamp + (NSArray<TSInteraction *> *)interactionsWithTimestamp:(uint64_t)timestamp
ofClass:(Class)clazz ofClass:(Class)clazz
withTransaction:(YapDatabaseReadWriteTransaction *)transaction withTransaction:(YapDatabaseReadWriteTransaction *)transaction
@ -42,14 +20,14 @@ NS_ASSUME_NONNULL_BEGIN
// Accept any interaction. // Accept any interaction.
return [self interactionsWithTimestamp:timestamp return [self interactionsWithTimestamp:timestamp
withFilter:^(TSInteraction *interaction) { filter:^(TSInteraction *interaction) {
return [interaction isKindOfClass:clazz]; return [interaction isKindOfClass:clazz];
} }
withTransaction:transaction]; withTransaction:transaction];
} }
+ (NSArray<TSInteraction *> *)interactionsWithTimestamp:(uint64_t)timestamp + (NSArray<TSInteraction *> *)interactionsWithTimestamp:(uint64_t)timestamp
withFilter:(BOOL (^_Nonnull)(TSInteraction *))filter filter:(BOOL (^_Nonnull)(TSInteraction *))filter
withTransaction:(YapDatabaseReadWriteTransaction *)transaction withTransaction:(YapDatabaseReadWriteTransaction *)transaction
{ {
OWSAssert(timestamp > 0); OWSAssert(timestamp > 0);

View file

@ -176,7 +176,7 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) {
- (void)updateWithWasSentFromLinkedDeviceWithTransaction:(YapDatabaseReadWriteTransaction *)transaction; - (void)updateWithWasSentFromLinkedDeviceWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
- (void)updateWithSingleGroupRecipient:(NSString *)singleGroupRecipient - (void)updateWithSingleGroupRecipient:(NSString *)singleGroupRecipient
transaction:(YapDatabaseReadWriteTransaction *)transaction; transaction:(YapDatabaseReadWriteTransaction *)transaction;
- (void)updateWithReadRecipient:(NSString *)recipientId transaction:(YapDatabaseReadWriteTransaction *)transaction; - (void)updateWithReadRecipientId:(NSString *)recipientId transaction:(YapDatabaseReadWriteTransaction *)transaction;
#pragma mark - Sent Recipients #pragma mark - Sent Recipients

View file

@ -411,7 +411,7 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
}]; }];
} }
- (void)updateWithReadRecipient:(NSString *)recipientId transaction:(YapDatabaseReadWriteTransaction *)transaction - (void)updateWithReadRecipientId:(NSString *)recipientId transaction:(YapDatabaseReadWriteTransaction *)transaction
{ {
OWSAssert(recipientId.length > 0); OWSAssert(recipientId.length > 0);
OWSAssert(transaction); OWSAssert(transaction);

View file

@ -187,14 +187,24 @@ NS_ASSUME_NONNULL_BEGIN
OWSAssert(envelope); OWSAssert(envelope);
OWSAssert(transaction); OWSAssert(transaction);
TSInteraction *interaction = [TSInteraction interactionForTimestamp:envelope.timestamp withTransaction:transaction]; NSArray<TSOutgoingMessage *> *messages
if ([interaction isKindOfClass:[TSOutgoingMessage class]]) { = (NSArray<TSOutgoingMessage *> *)[TSInteraction interactionsWithTimestamp:envelope.timestamp
TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)interaction; ofClass:[TSOutgoingMessage class]
[outgoingMessage updateWithWasDeliveredWithTransaction:transaction]; withTransaction:transaction];
} else { if (messages.count < 1) {
// Desktop currently sends delivery receipts for "unpersisted" messages // Desktop currently sends delivery receipts for "unpersisted" messages
// like group updates, so these errors are expected to a certain extent. // like group updates, so these errors are expected to a certain extent.
DDLogInfo(@"%@ Unexpected message with timestamp: %llu", self.tag, envelope.timestamp); DDLogInfo(@"%@ Missing message for delivery receipt: %llu", self.tag, envelope.timestamp);
} else {
if (messages.count > 1) {
DDLogInfo(@"%@ More than one message (%zd) for delivery receipt: %llu",
self.tag,
messages.count,
envelope.timestamp);
}
for (TSOutgoingMessage *outgoingMessage in messages) {
[outgoingMessage updateWithWasDeliveredWithTransaction:transaction];
}
} }
} }

View file

@ -39,8 +39,8 @@ NS_ASSUME_NONNULL_BEGIN
- (void)processReadReceiptsFromRecipient:(OWSSignalServiceProtosReceiptMessage *)receiptMessage - (void)processReadReceiptsFromRecipient:(OWSSignalServiceProtosReceiptMessage *)receiptMessage
envelope:(OWSSignalServiceProtosEnvelope *)envelope; envelope:(OWSSignalServiceProtosEnvelope *)envelope;
- (void)outgoingMessageFromLinkedDevice:(TSOutgoingMessage *)message - (void)updateOutgoingMessageFromLinkedDevice:(TSOutgoingMessage *)message
transaction:(YapDatabaseReadWriteTransaction *)transaction; transaction:(YapDatabaseReadWriteTransaction *)transaction;
// This method cues this manager: // This method cues this manager:
// //

View file

@ -17,9 +17,81 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@interface TSRecipientReadReceipt : TSYapDatabaseObject
@property (nonatomic, readonly) uint64_t timestamp;
@property (nonatomic, readonly) NSSet<NSString *> *recipientIds;
@end
#pragma mark -
@implementation TSRecipientReadReceipt
- (instancetype)initWithTimestamp:(uint64_t)timestamp
{
OWSAssert(timestamp > 0);
self = [super initWithUniqueId:[TSRecipientReadReceipt uniqueIdForTimestamp:timestamp]];
if (self) {
_timestamp = timestamp;
_recipientIds = [NSSet set];
}
return self;
}
+ (NSString *)uniqueIdForTimestamp:(uint64_t)timestamp
{
return [NSString stringWithFormat:@"%llu", timestamp];
}
- (void)addRecipientId:(NSString *)recipientId
{
NSMutableSet<NSString *> *recipientIdsCopy = [self.recipientIds mutableCopy];
[recipientIdsCopy addObject:recipientId];
_recipientIds = [recipientIdsCopy copy];
}
+ (void)addRecipientId:(NSString *)recipientId
timestamp:(uint64_t)timestamp
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(transaction);
TSRecipientReadReceipt *_Nullable recipientReadReceipt =
[transaction objectForKey:[self uniqueIdForTimestamp:timestamp] inCollection:[self collection]];
if (!recipientReadReceipt) {
recipientReadReceipt = [[TSRecipientReadReceipt alloc] initWithTimestamp:timestamp];
}
[recipientReadReceipt addRecipientId:recipientId];
[recipientReadReceipt saveWithTransaction:transaction];
}
+ (nullable NSSet<NSString *> *)recipientIdsForTimestamp:(uint64_t)timestamp
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(transaction);
TSRecipientReadReceipt *_Nullable recipientReadReceipt =
[transaction objectForKey:[self uniqueIdForTimestamp:timestamp] inCollection:[self collection]];
return recipientReadReceipt.recipientIds;
}
+ (void)removeRecipientIdsForTimestamp:(uint64_t)timestamp transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(transaction);
[transaction removeObjectForKey:[self uniqueIdForTimestamp:timestamp] inCollection:[self collection]];
}
@end
#pragma mark -
NSString *const OWSReadReceiptManagerCollection = @"OWSReadReceiptManagerCollection"; NSString *const OWSReadReceiptManagerCollection = @"OWSReadReceiptManagerCollection";
NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsEnabled"; NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsEnabled";
NSString *const OWSRecipientReadReceiptCollection = @"OWSRecipientReadReceiptCollection";
@interface OWSReadReceiptManager () @interface OWSReadReceiptManager ()
@ -37,7 +109,7 @@ NSString *const OWSRecipientReadReceiptCollection = @"OWSRecipientReadReceiptCol
// we will send to senders. // we will send to senders.
// //
// Should only be accessed while synchronized on the OWSReadReceiptManager. // Should only be accessed while synchronized on the OWSReadReceiptManager.
@property (nonatomic, readonly) NSMutableDictionary<NSString *, NSMutableArray<NSNumber *> *> *toSenderReadReceiptMap; @property (nonatomic, readonly) NSMutableDictionary<NSString *, NSMutableSet<NSNumber *> *> *toSenderReadReceiptMap;
// Should only be accessed while synchronized on the OWSReadReceiptManager. // Should only be accessed while synchronized on the OWSReadReceiptManager.
@property (nonatomic) BOOL isProcessing; @property (nonatomic) BOOL isProcessing;
@ -168,16 +240,17 @@ NSString *const OWSRecipientReadReceiptCollection = @"OWSRecipientReadReceiptCol
}); });
} }
NSArray<OWSReadReceipt *> *readReceiptsToSend = [[self.toLinkedDevicesReadReceiptMap allValues] copy]; NSArray<OWSReadReceipt *> *readReceiptsToSend = [self.toLinkedDevicesReadReceiptMap allValues];
[self.toLinkedDevicesReadReceiptMap removeAllObjects]; [self.toLinkedDevicesReadReceiptMap removeAllObjects];
if (self.toSenderReadReceiptMap.count > 0) { if (self.toSenderReadReceiptMap.count > 0) {
for (NSString *recipientId in self.toSenderReadReceiptMap) { for (NSString *recipientId in self.toSenderReadReceiptMap) {
NSArray<NSNumber *> *timestamps = self.toSenderReadReceiptMap[recipientId]; NSSet<NSNumber *> *timestamps = self.toSenderReadReceiptMap[recipientId];
OWSAssert(timestamps.count > 0); OWSAssert(timestamps.count > 0);
TSThread *thread = [TSContactThread getOrCreateThreadWithContactId:recipientId]; TSThread *thread = [TSContactThread getOrCreateThreadWithContactId:recipientId];
OWSReadReceiptsForSenderMessage *message = OWSReadReceiptsForSenderMessage *message =
[[OWSReadReceiptsForSenderMessage alloc] initWithThread:thread messageTimestamps:timestamps]; [[OWSReadReceiptsForSenderMessage alloc] initWithThread:thread
messageTimestamps:timestamps.allObjects];
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[self.messageSender sendMessage:message [self.messageSender sendMessage:message
@ -274,9 +347,9 @@ NSString *const OWSRecipientReadReceiptCollection = @"OWSRecipientReadReceiptCol
if ([self areReadReceiptsEnabled]) { if ([self areReadReceiptsEnabled]) {
DDLogVerbose(@"%@ Enqueuing read receipt for sender.", self.tag); DDLogVerbose(@"%@ Enqueuing read receipt for sender.", self.tag);
NSMutableArray<NSNumber *> *_Nullable timestamps = self.toSenderReadReceiptMap[messageAuthorId]; NSMutableSet<NSNumber *> *_Nullable timestamps = self.toSenderReadReceiptMap[messageAuthorId];
if (!timestamps) { if (!timestamps) {
timestamps = [NSMutableArray new]; timestamps = [NSMutableSet new];
self.toSenderReadReceiptMap[messageAuthorId] = timestamps; self.toSenderReadReceiptMap[messageAuthorId] = timestamps;
} }
[timestamps addObject:@(message.timestamp)]; [timestamps addObject:@(message.timestamp)];
@ -320,42 +393,34 @@ NSString *const OWSRecipientReadReceiptCollection = @"OWSRecipientReadReceiptCol
// TODO: We might also need to "mark as read by recipient" any older messages // TODO: We might also need to "mark as read by recipient" any older messages
// from us in that thread. Or maybe this state should hang on the thread? // from us in that thread. Or maybe this state should hang on the thread?
for (TSOutgoingMessage *message in messages) { for (TSOutgoingMessage *message in messages) {
[message updateWithReadRecipient:recipientId transaction:transaction]; [message updateWithReadRecipientId:recipientId transaction:transaction];
} }
} else { } else {
// Persist the read receipts so that we can apply them to outgoing messages // Persist the read receipts so that we can apply them to outgoing messages
// that we learn about later through sync messages. // that we learn about later through sync messages.
NSString *storageKey = [NSString stringWithFormat:@"%llu", timestamp]; [TSRecipientReadReceipt addRecipientId:recipientId timestamp:timestamp transaction:transaction];
NSSet<NSString *> *recipientIds =
[transaction objectForKey:storageKey inCollection:OWSRecipientReadReceiptCollection];
NSMutableSet<NSString *> *recipientIdsCopy
= (recipientIds ? [recipientIds mutableCopy] : [NSMutableSet new]);
[recipientIdsCopy addObject:recipientId];
[transaction setObject:recipientIdsCopy
forKey:storageKey
inCollection:OWSRecipientReadReceiptCollection];
} }
} }
}]; }];
}); });
} }
- (void)outgoingMessageFromLinkedDevice:(TSOutgoingMessage *)message - (void)updateOutgoingMessageFromLinkedDevice:(TSOutgoingMessage *)message
transaction:(YapDatabaseReadWriteTransaction *)transaction transaction:(YapDatabaseReadWriteTransaction *)transaction
{ {
OWSAssert(message); OWSAssert(message);
OWSAssert(transaction); OWSAssert(transaction);
NSString *storageKey = [NSString stringWithFormat:@"%llu", message.timestamp]; NSSet<NSString *> *_Nullable recipientIds =
NSSet<NSString *> *recipientIds = [TSRecipientReadReceipt recipientIdsForTimestamp:message.timestamp transaction:transaction];
[transaction objectForKey:storageKey inCollection:OWSRecipientReadReceiptCollection]; if (!recipientIds) {
if (recipientIds) { return;
OWSAssert(recipientIds.count > 0);
for (NSString *recipientId in recipientIds) {
[message updateWithReadRecipient:recipientId transaction:transaction];
}
} }
[transaction removeObjectForKey:storageKey inCollection:OWSRecipientReadReceiptCollection]; OWSAssert(recipientIds.count > 0);
for (NSString *recipientId in recipientIds) {
[message updateWithReadRecipientId:recipientId transaction:transaction];
}
[TSRecipientReadReceipt removeRecipientIdsForTimestamp:message.timestamp transaction:transaction];
} }
#pragma mark - Settings #pragma mark - Settings