Start timer for expiring message based on when read receipt was sent

// FREEBIE
pull/1/head
Michael Kirk 7 years ago
parent dfb2a034af
commit 754549adf1

@ -3278,7 +3278,7 @@ NS_ASSUME_NONNULL_BEGIN
attachmentIds:@[]
expiresInSeconds:0
quotedMessage:nil];
[message markAsReadWithTransaction:transaction sendReadReceipt:NO];
[message markAsReadNowWithSendReadReceipt:NO transaction:transaction];
break;
}
case 1: {
@ -3316,7 +3316,7 @@ NS_ASSUME_NONNULL_BEGIN
]
expiresInSeconds:0
quotedMessage:nil];
[message markAsReadWithTransaction:transaction sendReadReceipt:NO];
[message markAsReadNowWithSendReadReceipt:NO transaction:transaction];
break;
}
case 3: {
@ -3767,7 +3767,7 @@ NS_ASSUME_NONNULL_BEGIN
attachmentIds:[NSMutableArray new]
expiresInSeconds:0
quotedMessage:nil];
[message markAsReadWithTransaction:transaction sendReadReceipt:NO];
[message markAsReadNowWithSendReadReceipt:NO transaction:transaction];
}
{
TSOutgoingMessage *message =
@ -4105,7 +4105,7 @@ NS_ASSUME_NONNULL_BEGIN
attachmentIds:attachmentIds
expiresInSeconds:0
quotedMessage:quotedMessage];
[message markAsReadWithTransaction:transaction sendReadReceipt:NO];
[message markAsReadNowWithSendReadReceipt:NO transaction:transaction];
return message;
}

@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, nullable, readonly) NSNumber *unreadIndicatorPosition;
// If there are unseen messages in the thread, this is the timestamp
// of the oldest unseen messaage.
// of the oldest unseen message.
//
// Once we enter messages view, we mark all messages read, so we need
// a snapshot of what the first unread message was when we entered the

@ -3,6 +3,7 @@
//
#import "TSThread.h"
#import "NSDate+OWS.h"
#import "OWSPrimaryStorage.h"
#import "OWSReadTracking.h"
#import "TSDatabaseView.h"
@ -230,7 +231,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
for (id<OWSReadTracking> message in [self unseenMessagesWithTransaction:transaction]) {
[message markAsReadWithTransaction:transaction sendReadReceipt:YES];
[message markAsReadAtTimestamp:[NSDate ows_millisecondTimeStamp] sendReadReceipt:YES transaction:transaction];
}
// Just to be defensive, we'll also check for unread messages.

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "TSYapDatabaseObject.h"
@ -9,12 +9,15 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSLinkedDeviceReadReceipt : TSYapDatabaseObject
@property (nonatomic, readonly) NSString *senderId;
@property (nonatomic, readonly) uint64_t timestamp;
@property (nonatomic, readonly) uint64_t messageIdTimestamp;
@property (nonatomic, readonly) uint64_t readTimestamp;
- (instancetype)initWithSenderId:(NSString *)senderId timestamp:(uint64_t)timestamp;
- (instancetype)initWithSenderId:(NSString *)senderId
messageIdTimestamp:(uint64_t)messageIdtimestamp
readTimestamp:(uint64_t)readTimestamp;
+ (nullable OWSLinkedDeviceReadReceipt *)findLinkedDeviceReadReceiptWithSenderId:(NSString *)senderId
timestamp:(uint64_t)timestamp
messageIdTimestamp:(uint64_t)messageIdTimestamp
transaction:
(YapDatabaseReadTransaction *)transaction;

@ -6,39 +6,75 @@
NS_ASSUME_NONNULL_BEGIN
@interface OWSLinkedDeviceReadReceipt ()
// FIXME remove this `timestamp` property and migrate in initWithCoder.
@property (nonatomic, readonly) uint64_t timestamp;
@end
@implementation OWSLinkedDeviceReadReceipt
- (instancetype)initWithSenderId:(NSString *)senderId timestamp:(uint64_t)timestamp
- (instancetype)initWithSenderId:(NSString *)senderId
messageIdTimestamp:(uint64_t)messageIdTimestamp
readTimestamp:(uint64_t)readTimestamp
{
OWSAssert(senderId.length > 0 && timestamp > 0);
OWSAssert(senderId.length > 0 && messageIdTimestamp > 0);
self = [super initWithUniqueId:[OWSLinkedDeviceReadReceipt uniqueIdForSenderId:senderId timestamp:timestamp]];
NSString *receiptId =
[OWSLinkedDeviceReadReceipt uniqueIdForSenderId:senderId messageIdTimestamp:messageIdTimestamp];
self = [super initWithUniqueId:receiptId];
if (!self) {
return self;
}
_senderId = senderId;
_timestamp = timestamp;
_messageIdTimestamp = messageIdTimestamp;
_readTimestamp = readTimestamp;
return self;
}
+ (NSString *)uniqueIdForSenderId:(NSString *)senderId timestamp:(uint64_t)timestamp
- (nullable instancetype)initWithCoder:(NSCoder *)coder
{
OWSAssert(senderId.length > 0 && timestamp > 0);
self = [super initWithCoder:coder];
if (!self) {
return self;
}
if (!_messageIdTimestamp) {
// FIXME to remove this legacy `timestamp` property, we need to figure out exactly how MTL encodes uint64_t.
// e.g. can we just do something like: `((NSNumber *)[coder decodeObjectForKey:@"timestamp"]).unsignedLongLong`
_messageIdTimestamp = _timestamp;
}
return [NSString stringWithFormat:@"%@-%llu", senderId, timestamp];
// For legacy early LinkedDeviceReadReceipts, before we were tracking read time, we assume the message was read as
// soon as it was sent. This is always going to be at least a little earlier than it was actually read, but we have
// nothing better to choose, and by the very fact that we're receiving a read receipt, we have good reason to
// believe they read the message on the other device.
if (_readTimestamp == 0) {
_readTimestamp = _timestamp;
}
return self;
}
+ (NSString *)uniqueIdForSenderId:(NSString *)senderId messageIdTimestamp:(uint64_t)messageIdTimestamp
{
OWSAssert(senderId.length > 0 && messageIdTimestamp > 0);
return [NSString stringWithFormat:@"%@-%llu", senderId, messageIdTimestamp];
}
+ (nullable OWSLinkedDeviceReadReceipt *)findLinkedDeviceReadReceiptWithSenderId:(NSString *)senderId
timestamp:(uint64_t)timestamp
messageIdTimestamp:(uint64_t)messageIdTimestamp
transaction:
(YapDatabaseReadTransaction *)transaction
{
OWSAssert(transaction);
return [OWSLinkedDeviceReadReceipt fetchObjectWithUniqueID:[self uniqueIdForSenderId:senderId timestamp:timestamp]
transaction:transaction];
NSString *receiptId =
[OWSLinkedDeviceReadReceipt uniqueIdForSenderId:senderId messageIdTimestamp:messageIdTimestamp];
return [OWSLinkedDeviceReadReceipt fetchObjectWithUniqueID:receiptId transaction:transaction];
}
@end

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "OWSReadReceiptsForLinkedDevicesMessage.h"
@ -35,7 +35,7 @@ NS_ASSUME_NONNULL_BEGIN
OWSSignalServiceProtosSyncMessageReadBuilder *readProtoBuilder =
[OWSSignalServiceProtosSyncMessageReadBuilder new];
[readProtoBuilder setSender:readReceipt.senderId];
[readProtoBuilder setTimestamp:readReceipt.timestamp];
[readProtoBuilder setTimestamp:readReceipt.messageIdTimestamp];
[syncMessageBuilder addRead:[readProtoBuilder build]];
}

@ -179,13 +179,19 @@ NSUInteger TSErrorMessageSchemaVersion = 1;
#pragma mark - OWSReadTracking
- (uint64_t)expireStartedAt
{
return 0;
}
- (BOOL)shouldAffectUnreadCounts
{
return NO;
}
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
sendReadReceipt:(BOOL)sendReadReceipt
- (void)markAsReadAtTimestamp:(uint64_t)readTimestamp
sendReadReceipt:(BOOL)sendReadReceipt
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(transaction);

@ -74,6 +74,10 @@ NS_ASSUME_NONNULL_BEGIN
- (NSString *)messageAuthorId;
// convenience method for expiring a message which was just read
- (void)markAsReadNowWithSendReadReceipt:(BOOL)sendReadReceipt
transaction:(YapDatabaseReadWriteTransaction *)transaction;
@end
NS_ASSUME_NONNULL_END

@ -3,6 +3,7 @@
//
#import "TSIncomingMessage.h"
#import "NSDate+OWS.h"
#import "NSNotificationCenter+OWS.h"
#import "OWSDisappearingMessagesConfiguration.h"
#import "OWSDisappearingMessagesJob.h"
@ -134,24 +135,37 @@ NS_ASSUME_NONNULL_BEGIN
return YES;
}
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
sendReadReceipt:(BOOL)sendReadReceipt
- (void)markAsReadNowWithSendReadReceipt:(BOOL)sendReadReceipt
transaction:(YapDatabaseReadWriteTransaction *)transaction;
{
[self markAsReadAtTimestamp:[NSDate ows_millisecondTimeStamp]
sendReadReceipt:sendReadReceipt
transaction:transaction];
}
- (void)markAsReadAtTimestamp:(uint64_t)readTimestamp
sendReadReceipt:(BOOL)sendReadReceipt
transaction:(YapDatabaseReadWriteTransaction *)transaction;
{
OWSAssert(transaction);
if (_read) {
if (_read && readTimestamp <= self.expireStartedAt) {
return;
}
DDLogDebug(
@"%@ marking as read uniqueId: %@ which has timestamp: %llu", self.logTag, self.uniqueId, self.timestamp);
NSTimeInterval secondsAgoRead = ([NSDate ows_millisecondTimeStamp] - readTimestamp) / 1000;
DDLogDebug(@"%@ marking uniqueId: %@ which has timestamp: %llu as read: %f seconds ago",
self.logTag,
self.uniqueId,
self.timestamp,
secondsAgoRead);
_read = YES;
[self saveWithTransaction:transaction];
[self touchThreadWithTransaction:transaction];
[[OWSDisappearingMessagesJob sharedJob] startExpirationForMessage:self
transaction:transaction];
[[OWSDisappearingMessagesJob sharedJob] startAnyExpirationForMessage:self
expirationStartedAt:readTimestamp
transaction:transaction];
if (sendReadReceipt) {
[OWSReadReceiptManager.sharedManager messageWasReadLocally:self];

@ -129,8 +129,14 @@ NSUInteger TSInfoMessageSchemaVersion = 1;
return NO;
}
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
sendReadReceipt:(BOOL)sendReadReceipt
- (uint64_t)expireStartedAt
{
return 0;
}
- (void)markAsReadAtTimestamp:(uint64_t)readTimestamp
sendReadReceipt:(BOOL)sendReadReceipt
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(transaction);

@ -18,8 +18,9 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)init NS_UNAVAILABLE;
//+ (void)setExpirationsForThread:(TSThread *)thread;
- (void)startExpirationForMessage:(TSMessage *)message
transaction:(YapDatabaseReadWriteTransaction *_Nonnull)transaction;
- (void)startAnyExpirationForMessage:(TSMessage *)message
expirationStartedAt:(uint64_t)expirationStartedAt
transaction:(YapDatabaseReadWriteTransaction *_Nonnull)transaction;
- (void)setExpirationForMessage:(TSMessage *)message
expirationStartedAt:(uint64_t)expirationStartedAt

@ -166,8 +166,9 @@ void AssertIsOnDisappearingMessagesQueue()
return deletedCount;
}
- (void)startExpirationForMessage:(TSMessage *)message
transaction:(YapDatabaseReadWriteTransaction *_Nonnull)transaction
- (void)startAnyExpirationForMessage:(TSMessage *)message
expirationStartedAt:(uint64_t)expirationStartedAt
transaction:(YapDatabaseReadWriteTransaction *_Nonnull)transaction
{
if (!message.isExpiringMessage) {
return;
@ -180,9 +181,7 @@ void AssertIsOnDisappearingMessagesQueue()
return;
}
[self setExpirationForMessage:message
expirationStartedAt:[NSDate ows_millisecondTimeStamp]
transaction:transaction];
[self setExpirationForMessage:message expirationStartedAt:expirationStartedAt transaction:transaction];
}
- (void)setExpirationForMessage:(TSMessage *)message
@ -195,8 +194,8 @@ void AssertIsOnDisappearingMessagesQueue()
return;
}
int startedSecondsAgo = [NSDate new].timeIntervalSince1970 - expirationStartedAt / 1000.0;
DDLogDebug(@"%@ Starting expiration for message read %d seconds ago", self.logTag, startedSecondsAgo);
NSTimeInterval startedSecondsAgo = ([NSDate ows_millisecondTimeStamp] - expirationStartedAt) / 1000.0;
DDLogDebug(@"%@ Starting expiration for message read %f seconds ago", self.logTag, startedSecondsAgo);
// Don't clobber if multiple actions simultaneously triggered expiration.
if (message.expireStartedAt == 0 || message.expireStartedAt > expirationStartedAt) {
@ -211,27 +210,27 @@ void AssertIsOnDisappearingMessagesQueue()
}];
}
- (void)setExpirationsForThread:(TSThread *)thread transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSBackgroundTask *_Nullable backgroundTask = [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__];
uint64_t now = [NSDate ows_millisecondTimeStamp];
[self.disappearingMessagesFinder
enumerateUnstartedExpiringMessagesInThread:thread
block:^(TSMessage *_Nonnull message) {
DDLogWarn(@"%@ Starting expiring message which should have already "
@"been started.",
self.logTag);
// specify "now" in case D.M. have since been disabled, but we have
// existing unstarted expiring messages that still need to expire.
[self setExpirationForMessage:message
expirationStartedAt:now
transaction:transaction];
}
transaction:transaction];
backgroundTask = nil;
}
//- (void)setExpirationsForThread:(TSThread *)thread transaction:(YapDatabaseReadWriteTransaction *)transaction
//{
// OWSBackgroundTask *_Nullable backgroundTask = [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__];
//
// uint64_t now = [NSDate ows_millisecondTimeStamp];
// [self.disappearingMessagesFinder
// enumerateUnstartedExpiringMessagesInThread:thread
// block:^(TSMessage *_Nonnull message) {
// DDLogWarn(@"%@ Starting expiring message which should have already "
// @"been started.",
// self.logTag);
// // specify "now" in case D.M. have since been disabled, but we have
// // existing unstarted expiring messages that still need to expire.
// [self setExpirationForMessage:message
// expirationStartedAt:now
// transaction:transaction];
// }
// transaction:transaction];
//
// backgroundTask = nil;
//}
- (void)becomeConsistentWithConfigurationForMessage:(TSMessage *)message
contactsManager:(id<ContactsManagerProtocol>)contactsManager

@ -708,8 +708,8 @@ NS_ASSUME_NONNULL_BEGIN
});
} else if (syncMessage.read.count > 0) {
DDLogInfo(@"%@ Received %ld read receipt(s)", self.logTag, (u_long)syncMessage.read.count);
[OWSReadReceiptManager.sharedManager processReadReceiptsFromLinkedDevice:syncMessage.read
readTimestamp:envelope.timestamp
transaction:transaction];
} else if (syncMessage.hasVerified) {
DDLogInfo(@"%@ Received verification state for %@", self.logTag, syncMessage.verified.destination);
@ -1074,7 +1074,6 @@ NS_ASSUME_NONNULL_BEGIN
OWSAssert(transaction);
OWSAssert([TSAccountManager isRegistered]);
NSString *localNumber = [TSAccountManager localNumber];
if (!thread) {
OWSFail(@"%@ Can't finalize without thread", self.logTag);
@ -1087,12 +1086,10 @@ NS_ASSUME_NONNULL_BEGIN
[incomingMessage saveWithTransaction:transaction];
// Any messages sent from the current user - from this device or another - should be
// automatically marked as read.
BOOL shouldMarkMessageAsRead = [envelope.source isEqualToString:localNumber];
if (shouldMarkMessageAsRead) {
// Any messages sent from the current user - from this device or another - should be automatically marked as read.
if ([envelope.source isEqualToString:TSAccountManager.localNumber]) {
// Don't send a read receipt for messages sent by ourselves.
[incomingMessage markAsReadWithTransaction:transaction sendReadReceipt:NO];
[incomingMessage markAsReadAtTimestamp:envelope.timestamp sendReadReceipt:NO transaction:transaction];
}
TSQuotedMessage *_Nullable quotedMessage = incomingMessage.quotedMessage;

@ -7,6 +7,7 @@
#import "ContactsUpdater.h"
#import "NSData+keyVersionByte.h"
#import "NSData+messagePadding.h"
#import "NSDate+OWS.h"
#import "NSError+MessageSending.h"
#import "OWSBackgroundTask.h"
#import "OWSBlockingManager.h"
@ -1053,8 +1054,9 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[[OWSDisappearingMessagesJob sharedJob] startExpirationForMessage:message
transaction:transaction];
[[OWSDisappearingMessagesJob sharedJob] startAnyExpirationForMessage:message
expirationStartedAt:[NSDate ows_millisecondTimeStamp]
transaction:transaction];
}];
}

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
@ -8,8 +8,8 @@ NS_ASSUME_NONNULL_BEGIN
@class TSIncomingMessage;
@class TSOutgoingMessage;
@class TSThread;
@class YapDatabaseReadWriteTransaction;
@class YapDatabaseReadTransaction;
@class YapDatabaseReadWriteTransaction;
extern NSString *const kIncomingMessageMarkedAsReadNotification;
@ -50,6 +50,7 @@ extern NSString *const kIncomingMessageMarkedAsReadNotification;
#pragma mark - Linked Device Read Receipts
- (void)processReadReceiptsFromLinkedDevice:(NSArray<OWSSignalServiceProtosSyncMessageRead *> *)readReceiptProtos
readTimestamp:(uint64_t)readTimestamp
transaction:(YapDatabaseReadWriteTransaction *)transaction;
- (void)applyEarlyReadReceiptsForIncomingMessage:(TSIncomingMessage *)message

@ -4,6 +4,7 @@
#import "OWSReadReceiptManager.h"
#import "AppReadiness.h"
#import "NSDate+OWS.h"
#import "NSNotificationCenter+OWS.h"
#import "OWSLinkedDeviceReadReceipt.h"
#import "OWSMessageSender.h"
@ -289,6 +290,7 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self markAsReadBeforeTimestamp:timestamp
thread:thread
readTimestamp:[NSDate ows_millisecondTimeStamp]
wasLocal:YES
transaction:transaction];
}];
@ -307,10 +309,12 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
OWSAssert(messageAuthorId.length > 0);
OWSLinkedDeviceReadReceipt *newReadReceipt =
[[OWSLinkedDeviceReadReceipt alloc] initWithSenderId:messageAuthorId timestamp:message.timestamp];
[[OWSLinkedDeviceReadReceipt alloc] initWithSenderId:messageAuthorId
messageIdTimestamp:message.timestamp
readTimestamp:[NSDate ows_millisecondTimeStamp]];
OWSLinkedDeviceReadReceipt *_Nullable oldReadReceipt = self.toLinkedDevicesReadReceiptMap[threadUniqueId];
if (oldReadReceipt && oldReadReceipt.timestamp > newReadReceipt.timestamp) {
if (oldReadReceipt && oldReadReceipt.messageIdTimestamp > newReadReceipt.messageIdTimestamp) {
// If there's an existing "linked device" read receipt for the same thread with
// a newer timestamp, discard this "linked device" read receipt.
DDLogVerbose(@"%@ Ignoring redundant read receipt for linked devices.", self.logTag);
@ -426,12 +430,13 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
OWSLinkedDeviceReadReceipt *_Nullable readReceipt =
[OWSLinkedDeviceReadReceipt findLinkedDeviceReadReceiptWithSenderId:senderId
timestamp:timestamp
messageIdTimestamp:timestamp
transaction:transaction];
if (!readReceipt) {
return;
}
[message markAsReadWithTransaction:transaction sendReadReceipt:NO];
[message markAsReadAtTimestamp:readReceipt.readTimestamp sendReadReceipt:NO transaction:transaction];
[readReceipt removeWithTransaction:transaction];
[transaction addCompletionQueue:nil
@ -443,6 +448,7 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
}
- (void)processReadReceiptsFromLinkedDevice:(NSArray<OWSSignalServiceProtosSyncMessageRead *> *)readReceiptProtos
readTimestamp:(uint64_t)readTimestamp
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(readReceiptProtos);
@ -450,41 +456,53 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
for (OWSSignalServiceProtosSyncMessageRead *readReceiptProto in readReceiptProtos) {
NSString *_Nullable senderId = readReceiptProto.sender;
uint64_t timestamp = readReceiptProto.timestamp;
BOOL isValid = senderId.length > 0 && timestamp > 0;
if (!isValid) {
uint64_t messageIdTimestamp = readReceiptProto.timestamp;
if (senderId.length == 0) {
OWSProdLogAndFail(@"%@ in %s senderId was unexpectedly nil", self.logTag, __PRETTY_FUNCTION__);
continue;
}
if (messageIdTimestamp == 0) {
OWSProdLogAndFail(@"%@ in %s messageIdTimstamp was unexpectedly 0", self.logTag, __PRETTY_FUNCTION__);
continue;
}
NSArray<TSIncomingMessage *> *messages = (NSArray<TSIncomingMessage *> *) [TSInteraction interactionsWithTimestamp:timestamp
ofClass:[TSIncomingMessage class]
withTransaction:transaction];
NSArray<TSIncomingMessage *> *messages
= (NSArray<TSIncomingMessage *> *)[TSInteraction interactionsWithTimestamp:messageIdTimestamp
ofClass:[TSIncomingMessage class]
withTransaction:transaction];
if (messages.count > 0) {
for (TSIncomingMessage *message in messages) {
NSTimeInterval secondsSinceRead = [NSDate new].timeIntervalSince1970 - readTimestamp / 1000;
OWSAssert([message isKindOfClass:[TSIncomingMessage class]]);
[self markAsReadOnLinkedDevice:message
transaction:transaction];
DDLogDebug(@"%@ read on linked device %f seconds ago", self.logTag, secondsSinceRead);
[self markAsReadOnLinkedDevice:message readTimestamp:readTimestamp transaction:transaction];
}
} else {
// Received read receipt for unknown incoming message.
// Persist in case we receive the incoming message later.
OWSLinkedDeviceReadReceipt *readReceipt = [[OWSLinkedDeviceReadReceipt alloc] initWithSenderId:senderId timestamp:timestamp];
OWSLinkedDeviceReadReceipt *readReceipt =
[[OWSLinkedDeviceReadReceipt alloc] initWithSenderId:senderId
messageIdTimestamp:messageIdTimestamp
readTimestamp:readTimestamp];
[readReceipt saveWithTransaction:transaction];
}
}
}
- (void)markAsReadOnLinkedDevice:(TSIncomingMessage *)message
readTimestamp:(uint64_t)readTimestamp
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(message);
OWSAssert(transaction);
// Use timestampForSorting which reflects local sort order, rather than timestamp
// Use `timestampForSorting` which reflects local received order, rather than `timestamp`
// which reflect sender time.
[self markAsReadBeforeTimestamp:message.timestampForSorting
thread:[message threadWithTransaction:transaction]
readTimestamp:readTimestamp
wasLocal:NO
transaction:transaction];
}
@ -493,15 +511,16 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
- (void)markAsReadBeforeTimestamp:(uint64_t)timestamp
thread:(TSThread *)thread
readTimestamp:(uint64_t)readTimestamp
wasLocal:(BOOL)wasLocal
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(timestamp > 0);
OWSAssert(thread);
OWSAssert(transaction);
NSMutableArray<id<OWSReadTracking>> *interactions = [NSMutableArray new];
NSMutableArray<id<OWSReadTracking>> *newlyReadList = [NSMutableArray new];
[[TSDatabaseView unseenDatabaseViewExtension:transaction]
enumerateRowsInGroup:thread.uniqueId
usingBlock:^(NSString *collection,
@ -529,25 +548,26 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
OWSAssert(!possiblyRead.read);
if (!possiblyRead.read) {
[interactions addObject:possiblyRead];
[newlyReadList addObject:possiblyRead];
} else if (readTimestamp < possiblyRead.expireStartedAt) {
[newlyReadList addObject:possiblyRead];
}
}];
if (interactions.count < 1) {
if (newlyReadList.count < 1) {
return;
}
if (wasLocal) {
DDLogError(@"Marking %zd messages as read locally.", interactions.count);
DDLogError(@"Marking %zu messages as read locally.", newlyReadList.count);
} else {
DDLogError(@"Marking %zd messages as read by linked device.", interactions.count);
DDLogError(@"Marking %zu messages as read by linked device.", newlyReadList.count);
}
for (id<OWSReadTracking> readItem in newlyReadList) {
[readItem markAsReadAtTimestamp:readTimestamp sendReadReceipt:wasLocal transaction:transaction];
for (id<OWSReadTracking> possiblyRead in interactions) {
[possiblyRead markAsReadWithTransaction:transaction sendReadReceipt:wasLocal];
if ([possiblyRead isKindOfClass:[TSIncomingMessage class]]) {
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)possiblyRead;
if ([readItem isKindOfClass:[TSIncomingMessage class]]) {
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)readItem;
[transaction addCompletionQueue:nil
completionBlock:^{
[[NSNotificationCenter defaultCenter]

@ -15,15 +15,18 @@
*/
@property (nonatomic, readonly, getter=wasRead) BOOL read;
@property (nonatomic, readonly) uint64_t expireStartedAt;
@property (nonatomic, readonly) uint64_t timestampForSorting;
@property (nonatomic, readonly) NSString *uniqueThreadId;
- (BOOL)shouldAffectUnreadCounts;
/**
* Used both for *responding* to a remote read receipt and in response to the local user's activity.
*/
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
sendReadReceipt:(BOOL)sendReadReceipt;
- (void)markAsReadAtTimestamp:(uint64_t)readTimestamp
sendReadReceipt:(BOOL)sendReadReceipt
transaction:(YapDatabaseReadWriteTransaction *)transaction;
@end

@ -89,14 +89,21 @@ NSUInteger TSCallCurrentSchemaVersion = 1;
#pragma mark - OWSReadTracking
- (uint64_t)expireStartedAt
{
return 0;
}
- (BOOL)shouldAffectUnreadCounts
{
return YES;
}
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
sendReadReceipt:(BOOL)sendReadReceipt
- (void)markAsReadAtTimestamp:(uint64_t)readTimestamp
sendReadReceipt:(BOOL)sendReadReceipt
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(transaction);
if (_read) {
@ -126,7 +133,7 @@ NSUInteger TSCallCurrentSchemaVersion = 1;
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[self saveWithTransaction:transaction];
// redraw any thread-related unread count UI.
[self touchThreadWithTransaction:transaction];
}];

Loading…
Cancel
Save