From 658746093deec074023deb90753effab35643983 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 17 Nov 2017 10:49:34 -0500 Subject: [PATCH 1/5] Use finalIndex in row changes. --- .../ConversationViewController.m | 43 +++++++++++-------- .../ViewControllers/DebugUI/DebugUIMessages.m | 4 ++ 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 1b874ca7f..956efb530 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -2889,20 +2889,25 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { for (YapDatabaseViewRowChange *rowChange in rowChanges) { switch (rowChange.type) { case YapDatabaseViewChangeDelete: { - DDLogVerbose(@"YapDatabaseViewChangeDelete: %@, %@", rowChange.collectionKey, rowChange.indexPath); + DDLogVerbose(@"YapDatabaseViewChangeDelete: %@, %@, %zd", + rowChange.collectionKey, + rowChange.indexPath, + rowChange.finalIndex); [self.collectionView deleteItemsAtIndexPaths:@[ rowChange.indexPath ]]; YapCollectionKey *collectionKey = rowChange.collectionKey; OWSAssert(collectionKey.key.length > 0); break; } case YapDatabaseViewChangeInsert: { - DDLogVerbose( - @"YapDatabaseViewChangeInsert: %@, %@", rowChange.collectionKey, rowChange.newIndexPath); + DDLogVerbose(@"YapDatabaseViewChangeInsert: %@, %@, %zd", + rowChange.collectionKey, + rowChange.newIndexPath, + rowChange.finalIndex); [self.collectionView insertItemsAtIndexPaths:@[ rowChange.newIndexPath ]]; // We don't want to reload a row that we just inserted. - [rowsThatChangedSize removeObject:@(rowChange.newIndexPath.row)]; + [rowsThatChangedSize removeObject:@(rowChange.finalIndex)]; - ConversationViewItem *_Nullable viewItem = [self viewItemForIndex:rowChange.newIndexPath.row]; + ConversationViewItem *_Nullable viewItem = [self viewItemForIndex:(NSInteger)rowChange.finalIndex]; if ([viewItem.interaction isKindOfClass:[TSOutgoingMessage class]]) { TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)viewItem.interaction; if (!outgoingMessage.isFromLinkedDevice) { @@ -2913,21 +2918,25 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { break; } case YapDatabaseViewChangeMove: { - DDLogVerbose(@"YapDatabaseViewChangeMove: %@, %@, %@", + DDLogVerbose(@"YapDatabaseViewChangeMove: %@, %@, %@, %zd", rowChange.collectionKey, rowChange.indexPath, - rowChange.newIndexPath); + rowChange.newIndexPath, + rowChange.finalIndex); [self.collectionView deleteItemsAtIndexPaths:@[ rowChange.indexPath ]]; [self.collectionView insertItemsAtIndexPaths:@[ rowChange.newIndexPath ]]; // We don't want to reload a row that we just moved. - [rowsThatChangedSize removeObject:@(rowChange.newIndexPath.row)]; + [rowsThatChangedSize removeObject:@(rowChange.finalIndex)]; break; } case YapDatabaseViewChangeUpdate: { - DDLogVerbose(@"YapDatabaseViewChangeUpdate: %@, %@", rowChange.collectionKey, rowChange.indexPath); + DDLogVerbose(@"YapDatabaseViewChangeUpdate: %@, %@, %zd", + rowChange.collectionKey, + rowChange.indexPath, + rowChange.finalIndex); [self.collectionView reloadItemsAtIndexPaths:@[ rowChange.indexPath ]]; // We don't want to reload a row that we've already reloaded. - [rowsThatChangedSize removeObject:@(rowChange.indexPath.row)]; + [rowsThatChangedSize removeObject:@(rowChange.finalIndex)]; break; } } @@ -2987,13 +2996,13 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { break; case YapDatabaseViewChangeInsert: { isOnlyUpdatingLastOutgoingMessage = NO; - ConversationViewItem *_Nullable viewItem = [self viewItemForIndex:rowChange.newIndexPath.row]; + ConversationViewItem *_Nullable viewItem = [self viewItemForIndex:(NSInteger)rowChange.finalIndex]; if ([viewItem.interaction isKindOfClass:[TSOutgoingMessage class]] - && rowChange.newIndexPath.row >= (NSInteger)oldViewItemCount) { + && rowChange.finalIndex >= oldViewItemCount) { continue; } - if (!lastNonUpdateRow || lastNonUpdateRow.integerValue < rowChange.newIndexPath.row) { - lastNonUpdateRow = @(rowChange.newIndexPath.row); + if (!lastNonUpdateRow || lastNonUpdateRow.unsignedIntegerValue < rowChange.finalIndex) { + lastNonUpdateRow = @(rowChange.finalIndex); } } case YapDatabaseViewChangeMove: @@ -3002,13 +3011,13 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { if (!lastNonUpdateRow || lastNonUpdateRow.integerValue < rowChange.indexPath.row) { lastNonUpdateRow = @(rowChange.indexPath.row); } - if (!lastNonUpdateRow || lastNonUpdateRow.integerValue < rowChange.newIndexPath.row) { - lastNonUpdateRow = @(rowChange.newIndexPath.row); + if (!lastNonUpdateRow || lastNonUpdateRow.unsignedIntegerValue < rowChange.finalIndex) { + lastNonUpdateRow = @(rowChange.finalIndex); } break; case YapDatabaseViewChangeUpdate: { isOnlyInsertingNewOutgoingMessages = NO; - ConversationViewItem *_Nullable viewItem = [self viewItemForIndex:rowChange.indexPath.row]; + ConversationViewItem *_Nullable viewItem = [self viewItemForIndex:(NSInteger)rowChange.finalIndex]; if (![viewItem.interaction isKindOfClass:[TSOutgoingMessage class]] || rowChange.indexPath.row != (NSInteger)(oldViewItemCount - 1)) { isOnlyUpdatingLastOutgoingMessage = NO; diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index f40e35f72..177b03067 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -245,6 +245,10 @@ NS_ASSUME_NONNULL_BEGIN actionBlock:^{ [DebugUIMessages performRandomActions:100 thread:thread]; }], + [OWSTableItem itemWithTitle:@"Perform 1,000 random actions" + actionBlock:^{ + [DebugUIMessages performRandomActions:1000 thread:thread]; + }], ] mutableCopy]; if ([thread isKindOfClass:[TSContactThread class]]) { TSContactThread *contactThread = (TSContactThread *)thread; From 6d4a05bbea22e9500e4f7bebc1b94c41e4d1bbc1 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 17 Nov 2017 11:56:48 -0500 Subject: [PATCH 2/5] Improving handling of edge cases in conversation view. --- .../Cells/OWSAudioMessageView.m | 9 +- .../ConversationViewController.m | 159 +++++- .../ConversationView/ConversationViewItem.h | 2 +- .../ConversationView/ConversationViewItem.m | 14 +- .../ViewControllers/DebugUI/DebugUIMessages.m | 452 ++++++++++-------- .../Messages/Attachments/TSAttachmentStream.h | 8 +- .../Messages/Attachments/TSAttachmentStream.m | 127 ++--- .../src/Messages/OWSReadReceiptManager.m | 4 +- 8 files changed, 440 insertions(+), 335 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.m index 270c7b781..076bfcda5 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.m @@ -62,12 +62,9 @@ NS_ASSUME_NONNULL_BEGIN - (CGFloat)audioDurationSeconds { - NSNumber *_Nullable audioDurationSeconds = self.viewItem.audioDurationSeconds; - if (!audioDurationSeconds) { - audioDurationSeconds = @([self.attachmentStream audioDurationSecondsWithoutTransaction]); - self.viewItem.audioDurationSeconds = audioDurationSeconds; - } - return [audioDurationSeconds floatValue]; + OWSAssert(self.viewItem.audioDurationSeconds); + + return [self.viewItem.audioDurationSeconds floatValue]; } - (AudioPlaybackState)audioPlaybackState diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 956efb530..0c7cf3758 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -97,7 +97,7 @@ static const int kYapDatabasePageSize = 50; static const int kYapDatabaseMaxPageCount = 500; // Never show more than 6*50 = 300 messages in conversation view when user // arrives. -static const int kYapDatabaseMaxInitialPageCount = 6; +static const int kYapDatabaseMaxInitialPageCount = 500; static const int kConversationInitialMaxRangeSize = kYapDatabasePageSize * kYapDatabaseMaxInitialPageCount; static const int kYapDatabaseRangeMaxLength = kYapDatabasePageSize * kYapDatabaseMaxPageCount; static const int kYapDatabaseRangeMinLength = 0; @@ -2883,8 +2883,6 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { // b) is inserting new interactions. __block BOOL scrollToBottom = wasAtBottom; - BOOL shouldAnimateUpdates = [self shouldAnimateRowUpdates:rowChanges oldViewItemCount:oldViewItemCount]; - void (^batchUpdates)(void) = ^{ for (YapDatabaseViewRowChange *rowChange in rowChanges) { switch (rowChange.type) { @@ -2893,6 +2891,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { rowChange.collectionKey, rowChange.indexPath, rowChange.finalIndex); + [DDLog flushLog]; [self.collectionView deleteItemsAtIndexPaths:@[ rowChange.indexPath ]]; YapCollectionKey *collectionKey = rowChange.collectionKey; OWSAssert(collectionKey.key.length > 0); @@ -2903,6 +2902,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { rowChange.collectionKey, rowChange.newIndexPath, rowChange.finalIndex); + [DDLog flushLog]; [self.collectionView insertItemsAtIndexPaths:@[ rowChange.newIndexPath ]]; // We don't want to reload a row that we just inserted. [rowsThatChangedSize removeObject:@(rowChange.finalIndex)]; @@ -2923,8 +2923,8 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { rowChange.indexPath, rowChange.newIndexPath, rowChange.finalIndex); - [self.collectionView deleteItemsAtIndexPaths:@[ rowChange.indexPath ]]; - [self.collectionView insertItemsAtIndexPaths:@[ rowChange.newIndexPath ]]; + [DDLog flushLog]; + [self.collectionView moveItemAtIndexPath:rowChange.indexPath toIndexPath:rowChange.newIndexPath]; // We don't want to reload a row that we just moved. [rowsThatChangedSize removeObject:@(rowChange.finalIndex)]; break; @@ -2934,6 +2934,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { rowChange.collectionKey, rowChange.indexPath, rowChange.finalIndex); + [DDLog flushLog]; [self.collectionView reloadItemsAtIndexPaths:@[ rowChange.indexPath ]]; // We don't want to reload a row that we've already reloaded. [rowsThatChangedSize removeObject:@(rowChange.finalIndex)]; @@ -2946,40 +2947,151 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { // as they may affect which cells show "date" headers or "status" footers. NSMutableArray *rowsToReload = [NSMutableArray new]; for (NSNumber *row in rowsThatChangedSize) { + DDLogVerbose(@"rowsToReload: %@", row); [rowsToReload addObject:[NSIndexPath indexPathForRow:row.integerValue inSection:0]]; } if (rowsToReload.count > 0) { + [DDLog flushLog]; [self.collectionView reloadItemsAtIndexPaths:rowsToReload]; } }; - void (^batchUpdatesCompletion)(BOOL) = ^(BOOL finished) { - OWSAssert([NSThread isMainThread]); - if (!finished) { - DDLogInfo(@"%@ performBatchUpdates did not finish", self.logTag); - } + DDLogVerbose(@"self.viewItems.count: %zd -> %zd", oldViewItemCount, self.viewItems.count); + [DDLog flushLog]; + BOOL shouldReloadCollection = [self shouldReloadCollection:rowChanges]; + if (shouldReloadCollection) { + [UIView performWithoutAnimation:^{ + [self.collectionView reloadData]; + }]; [self updateLastVisibleTimestamp]; + } else { + BOOL shouldAnimateUpdates = [self shouldAnimateRowUpdates:rowChanges oldViewItemCount:oldViewItemCount]; + void (^batchUpdatesCompletion)(BOOL) = ^(BOOL finished) { + OWSAssert([NSThread isMainThread]); - if (scrollToBottom) { - [self scrollToBottomAnimated:shouldAnimateScrollToBottom && shouldAnimateUpdates]; - } - }; + if (!finished) { + DDLogInfo(@"%@ performBatchUpdates did not finish", self.logTag); + } - if (shouldAnimateUpdates) { - [self.collectionView performBatchUpdates:batchUpdates completion:batchUpdatesCompletion]; - } else { - [UIView performWithoutAnimation:^{ + [self updateLastVisibleTimestamp]; + + if (scrollToBottom) { + [self scrollToBottomAnimated:shouldAnimateScrollToBottom && shouldAnimateUpdates]; + } + }; + if (shouldAnimateUpdates) { [self.collectionView performBatchUpdates:batchUpdates completion:batchUpdatesCompletion]; - }]; + } else { + [UIView performWithoutAnimation:^{ + [self.collectionView performBatchUpdates:batchUpdates completion:batchUpdatesCompletion]; + }]; + } } } +- (BOOL)shouldReloadCollection:(NSArray *)rowChanges +{ + OWSAssert(rowChanges); + + BOOL hasDeletes = NO; + BOOL hasInserts = NO; + BOOL hasMoves = NO; + BOOL hasUpdates = NO; + for (YapDatabaseViewRowChange *rowChange in rowChanges) { + switch (rowChange.type) { + case YapDatabaseViewChangeDelete: + DDLogVerbose(@"? YapDatabaseViewChangeDelete: %@, %@, %zd", + rowChange.collectionKey, + rowChange.indexPath, + rowChange.finalIndex); + [DDLog flushLog]; + hasDeletes = YES; + break; + case YapDatabaseViewChangeInsert: + DDLogVerbose(@"...YapDatabaseViewChangeInsert: %@, %@, %zd", + rowChange.collectionKey, + rowChange.newIndexPath, + rowChange.finalIndex); + [DDLog flushLog]; + hasInserts = YES; + break; + case YapDatabaseViewChangeMove: + DDLogVerbose(@"...YapDatabaseViewChangeMove: %@, %@, %@, %zd", + rowChange.collectionKey, + rowChange.indexPath, + rowChange.newIndexPath, + rowChange.finalIndex); + [DDLog flushLog]; + hasMoves = YES; + break; + case YapDatabaseViewChangeUpdate: + DDLogVerbose(@"...YapDatabaseViewChangeUpdate: %@, %@, %zd", + rowChange.collectionKey, + rowChange.indexPath, + rowChange.finalIndex); + [DDLog flushLog]; + hasUpdates = YES; + break; + } + } + if (hasMoves) { + // "Move" changes cannot be safely performed using + // [UICollectionView performBatchUpdates:]. This appears to be a + // bug in YapDatabase. + return YES; + } + // if (hasDeletes && hasInserts) { + // return YES; + // } + // if (hasDeletes && hasUpdates) { + // return YES; + // } + // if (hasInserts && hasUpdates) { + // return YES; + // } + return NO; +} + - (BOOL)shouldAnimateRowUpdates:(NSArray *)rowChanges oldViewItemCount:(NSUInteger)oldViewItemCount { OWSAssert(rowChanges); + // for (YapDatabaseViewRowChange *rowChange in rowChanges) { + // switch (rowChange.type) { + // case YapDatabaseViewChangeDelete: + // DDLogVerbose(@"...YapDatabaseViewChangeDelete: %@, %@, %zd", + // rowChange.collectionKey, + // rowChange.indexPath, + // rowChange.finalIndex); + // [DDLog flushLog]; + // break; + // case YapDatabaseViewChangeInsert: + // DDLogVerbose(@"...YapDatabaseViewChangeInsert: %@, %@, %zd", + // rowChange.collectionKey, + // rowChange.newIndexPath, + // rowChange.finalIndex); + // [DDLog flushLog]; + // break; + // case YapDatabaseViewChangeMove: + // DDLogVerbose(@"...YapDatabaseViewChangeMove: %@, %@, %@, %zd", + // rowChange.collectionKey, + // rowChange.indexPath, + // rowChange.newIndexPath, + // rowChange.finalIndex); + // [DDLog flushLog]; + // break; + // case YapDatabaseViewChangeUpdate: + // DDLogVerbose(@"...YapDatabaseViewChangeUpdate: %@, %@, %zd", + // rowChange.collectionKey, + // rowChange.indexPath, + // rowChange.finalIndex); + // [DDLog flushLog]; + // break; + // } + // } + // If user sends a new outgoing message, don't animate the change. BOOL isOnlyInsertingNewOutgoingMessages = YES; BOOL isOnlyUpdatingLastOutgoingMessage = YES; @@ -3325,20 +3437,23 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { [self presentViewController:actionSheetController animated:true completion:nil]; } -- (NSIndexPath *)lastVisibleIndexPath +- (nullable NSIndexPath *)lastVisibleIndexPath { - NSIndexPath *lastVisibleIndexPath = nil; + NSIndexPath *_Nullable lastVisibleIndexPath = nil; for (NSIndexPath *indexPath in [self.collectionView indexPathsForVisibleItems]) { if (!lastVisibleIndexPath || indexPath.row > lastVisibleIndexPath.row) { lastVisibleIndexPath = indexPath; } } + if (lastVisibleIndexPath && lastVisibleIndexPath.row >= self.viewItems.count) { + return (self.viewItems.count > 0 ? [NSIndexPath indexPathForRow:self.viewItems.count - 1 inSection:0] : nil); + } return lastVisibleIndexPath; } - (nullable ConversationViewItem *)lastVisibleViewItem { - NSIndexPath *lastVisibleIndexPath = [self lastVisibleIndexPath]; + NSIndexPath *_Nullable lastVisibleIndexPath = [self lastVisibleIndexPath]; if (!lastVisibleIndexPath) { return nil; } diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h index ae315ef61..8d0c19d6d 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h @@ -72,7 +72,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); @property (nonatomic, weak) OWSAudioMessageView *lastAudioMessageView; -@property (nonatomic, nullable) NSNumber *audioDurationSeconds; +@property (nonatomic, readonly, nullable) NSNumber *audioDurationSeconds; - (CGFloat)audioProgressSeconds; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index a3492ab01..99be46755 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -47,6 +47,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) @property (nonatomic) AudioPlaybackState audioPlaybackState; @property (nonatomic) CGFloat audioProgressSeconds; +@property (nonatomic, nullable) NSNumber *audioDurationSeconds; #pragma mark - View State @@ -251,9 +252,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) OWSAssert([NSThread isMainThread]); self.audioProgressSeconds = progress; - if (duration > 0) { - self.audioDurationSeconds = @(duration); - } [self.lastAudioMessageView updateContents]; } @@ -390,13 +388,19 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) self.messageCellType = OWSMessageCellType_GenericAttachment; return; } - self.contentSize = [self.attachmentStream imageSizeWithoutTransaction]; + self.contentSize = [self.attachmentStream imageSize]; if (self.contentSize.width <= 0 || self.contentSize.height <= 0) { self.messageCellType = OWSMessageCellType_GenericAttachment; } return; } else if ([self.attachmentStream isAudio]) { - self.messageCellType = OWSMessageCellType_Audio; + CGFloat audioDurationSeconds = [self.attachmentStream audioDurationSeconds]; + if (audioDurationSeconds > 0) { + self.audioDurationSeconds = @(audioDurationSeconds); + self.messageCellType = OWSMessageCellType_Audio; + } else { + self.messageCellType = OWSMessageCellType_GenericAttachment; + } return; } else { self.messageCellType = OWSMessageCellType_GenericAttachment; diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index 177b03067..499878b50 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -40,6 +40,14 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert(thread); NSMutableArray *items = [@[ + [OWSTableItem itemWithTitle:@"Perform 100 random actions" + actionBlock:^{ + [DebugUIMessages performRandomActions:100 thread:thread]; + }], + [OWSTableItem itemWithTitle:@"Perform 1,000 random actions" + actionBlock:^{ + [DebugUIMessages performRandomActions:1000 thread:thread]; + }], [OWSTableItem itemWithTitle:@"Send 10 messages (1/sec.)" actionBlock:^{ [DebugUIMessages sendTextMessages:10 thread:thread]; @@ -241,14 +249,6 @@ NS_ASSUME_NONNULL_BEGIN actionBlock:^{ [DebugUIMessages injectFakeIncomingMessages:1000 thread:thread]; }], - [OWSTableItem itemWithTitle:@"Perform 100 random actions" - actionBlock:^{ - [DebugUIMessages performRandomActions:100 thread:thread]; - }], - [OWSTableItem itemWithTitle:@"Perform 1,000 random actions" - actionBlock:^{ - [DebugUIMessages performRandomActions:1000 thread:thread]; - }], ] mutableCopy]; if ([thread isKindOfClass:[TSContactThread class]]) { TSContactThread *contactThread = (TSContactThread *)thread; @@ -271,10 +271,14 @@ NS_ASSUME_NONNULL_BEGIN + (void)sendTextMessageInThread:(TSThread *)thread counter:(int)counter { + DDLogInfo(@"%@ sendTextMessageInThread: %d", self.logTag, counter); + [DDLog flushLog]; + NSString *randomText = [self randomText]; NSString *text = [[[@(counter) description] stringByAppendingString:@" "] stringByAppendingString:randomText]; OWSMessageSender *messageSender = [Environment getCurrent].messageSender; - [ThreadUtil sendMessageWithText:text inThread:thread messageSender:messageSender]; + TSOutgoingMessage *message = [ThreadUtil sendMessageWithText:text inThread:thread messageSender:messageSender]; + DDLogError(@"%@ sendTextMessageInThread timestamp: %llu.", self.logTag, message.timestamp); } + (void)sendTextMessages:(int)counter thread:(TSThread *)thread @@ -939,83 +943,96 @@ NS_ASSUME_NONNULL_BEGIN + (void)sendFakeMessages:(NSUInteger)counter thread:(TSThread *)thread { - [TSStorageManager.sharedManager.dbReadWriteConnection readWriteWithBlock:^( - YapDatabaseReadWriteTransaction *transaction) { - for (NSUInteger i = 0; i < counter; i++) { - NSString *randomText = [self randomText]; - switch (arc4random_uniform(4)) { - case 0: { - TSIncomingMessage *message = - [[TSIncomingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] - inThread:thread - authorId:@"+19174054215" - sourceDeviceId:0 - messageBody:randomText]; - [message markAsReadWithTransaction:transaction sendReadReceipt:NO updateExpiration:NO]; - break; - } - case 1: { - TSOutgoingMessage *message = - [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] - inThread:thread - messageBody:randomText]; - [message saveWithTransaction:transaction]; - break; - } - case 2: { - UInt32 filesize = 64; - TSAttachmentPointer *pointer = - [[TSAttachmentPointer alloc] initWithServerId:237391539706350548 - key:[self createRandomNSDataOfSize:filesize] - digest:nil - byteCount:filesize - contentType:@"audio/mp3" - relay:@"" - sourceFilename:@"test.mp3" - attachmentType:TSAttachmentTypeDefault]; - [pointer saveWithTransaction:transaction]; - TSIncomingMessage *message = - [[TSIncomingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] - inThread:thread - authorId:@"+19174054215" - sourceDeviceId:0 - messageBody:nil - attachmentIds:@[ - pointer.uniqueId, - ] - expiresInSeconds:0]; - [message markAsReadWithTransaction:transaction sendReadReceipt:NO updateExpiration:NO]; - break; - } - case 3: { - TSOutgoingMessage *message = - [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] - inThread:thread - isVoiceMessage:NO - expiresInSeconds:0]; - - NSString *filename = @"test.mp3"; - UInt32 filesize = 16; - - TSAttachmentStream *attachmentStream = [[TSAttachmentStream alloc] initWithContentType:@"audio/mp3" - byteCount:filesize - sourceFilename:filename]; - - NSError *error; - BOOL success = [attachmentStream writeData:[self createRandomNSDataOfSize:filesize] error:&error]; - OWSAssert(success && !error); - - [attachmentStream saveWithTransaction:transaction]; - [message.attachmentIds addObject:attachmentStream.uniqueId]; - if (filename) { - message.attachmentFilenameMap[attachmentStream.uniqueId] = filename; - } - [message saveWithTransaction:transaction]; - break; + [TSStorageManager.sharedManager.dbReadWriteConnection + readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + [self sendFakeMessages:counter thread:thread transaction:transaction]; + }]; +} + ++ (void)sendFakeMessages:(NSUInteger)counter + thread:(TSThread *)thread + transaction:(YapDatabaseReadWriteTransaction *)transaction +{ + DDLogInfo(@"%@ sendFakeMessages: %zd", self.logTag, counter); + + for (NSUInteger i = 0; i < counter; i++) { + NSString *randomText = [self randomText]; + switch (arc4random_uniform(4)) { + case 0: { + TSIncomingMessage *message = + [[TSIncomingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] + inThread:thread + authorId:@"+19174054215" + sourceDeviceId:0 + messageBody:randomText]; + DDLogError(@"%@ sendFakeMessages incoming timestamp: %llu.", self.logTag, message.timestamp); + [message markAsReadWithTransaction:transaction sendReadReceipt:NO updateExpiration:NO]; + break; + } + case 1: { + TSOutgoingMessage *message = + [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] + inThread:thread + messageBody:randomText]; + DDLogError(@"%@ sendFakeMessages outgoing timestamp: %llu.", self.logTag, message.timestamp); + [message saveWithTransaction:transaction]; + break; + } + case 2: { + UInt32 filesize = 64; + TSAttachmentPointer *pointer = + [[TSAttachmentPointer alloc] initWithServerId:237391539706350548 + key:[self createRandomNSDataOfSize:filesize] + digest:nil + byteCount:filesize + contentType:@"audio/mp3" + relay:@"" + sourceFilename:@"test.mp3" + attachmentType:TSAttachmentTypeDefault]; + [pointer saveWithTransaction:transaction]; + TSIncomingMessage *message = + [[TSIncomingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] + inThread:thread + authorId:@"+19174054215" + sourceDeviceId:0 + messageBody:nil + attachmentIds:@[ + pointer.uniqueId, + ] + expiresInSeconds:0]; + DDLogError(@"%@ sendFakeMessages incoming attachment timestamp: %llu.", self.logTag, message.timestamp); + [message markAsReadWithTransaction:transaction sendReadReceipt:NO updateExpiration:NO]; + break; + } + case 3: { + TSOutgoingMessage *message = + [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] + inThread:thread + isVoiceMessage:NO + expiresInSeconds:0]; + DDLogError(@"%@ sendFakeMessages outgoing attachment timestamp: %llu.", self.logTag, message.timestamp); + + NSString *filename = @"test.mp3"; + UInt32 filesize = 16; + + TSAttachmentStream *attachmentStream = [[TSAttachmentStream alloc] initWithContentType:@"audio/mp3" + byteCount:filesize + sourceFilename:filename]; + + NSError *error; + BOOL success = [attachmentStream writeData:[self createRandomNSDataOfSize:filesize] error:&error]; + OWSAssert(success && !error); + + [attachmentStream saveWithTransaction:transaction]; + [message.attachmentIds addObject:attachmentStream.uniqueId]; + if (filename) { + message.attachmentFilenameMap[attachmentStream.uniqueId] = filename; } + [message saveWithTransaction:transaction]; + break; } } - }]; + } } + (void)sendTinyAttachments:(int)counter thread:(TSThread *)thread @@ -1099,6 +1116,8 @@ NS_ASSUME_NONNULL_BEGIN { OWSAssert(thread); + DDLogInfo(@"%@ injectIncomingMessageInThread: %d", self.logTag, counter); + NSString *randomText = [self randomText]; NSString *text = [[[@(counter) description] stringByAppendingString:@" "] stringByAppendingString:randomText]; @@ -1154,144 +1173,151 @@ NS_ASSUME_NONNULL_BEGIN + (void)performRandomActionInThread:(TSThread *)thread counter:(int)counter { - typedef void (^ActionBlock)(void); + typedef void (^ActionBlock)(YapDatabaseReadWriteTransaction *transaction); NSArray *actionBlocks = @[ - ^{ - [self injectIncomingMessageInThread:thread counter:counter]; + ^(YapDatabaseReadWriteTransaction *transaction) { + // injectIncomingMessageInThread doesn't take a transaction. + dispatch_async(dispatch_get_main_queue(), ^{ + [self injectIncomingMessageInThread:thread counter:counter]; + }); }, - ^{ - [self sendTextMessageInThread:thread counter:counter]; + ^(YapDatabaseReadWriteTransaction *transaction) { + // sendTextMessageInThread doesn't take a transaction. + dispatch_async(dispatch_get_main_queue(), ^{ + [self sendTextMessageInThread:thread counter:counter]; + }); }, - ^{ + ^(YapDatabaseReadWriteTransaction *transaction) { NSUInteger messageCount = (NSUInteger)(1 + arc4random_uniform(4)); - [self sendFakeMessages:messageCount thread:thread]; + [self sendFakeMessages:messageCount thread:thread transaction:transaction]; }, - ^{ + ^(YapDatabaseReadWriteTransaction *transaction) { NSUInteger messageCount = (NSUInteger)(1 + arc4random_uniform(4)); - [self deleteRandomMessages:messageCount thread:thread]; + [self deleteRandomMessages:messageCount thread:thread transaction:transaction]; }, - ^{ + ^(YapDatabaseReadWriteTransaction *transaction) { NSUInteger messageCount = (NSUInteger)(1 + arc4random_uniform(4)); - [self deleteLastMessages:messageCount thread:thread]; + [self deleteLastMessages:messageCount thread:thread transaction:transaction]; }, - ^{ + ^(YapDatabaseReadWriteTransaction *transaction) { NSUInteger messageCount = (NSUInteger)(1 + arc4random_uniform(4)); - [self deleteRandomRecentMessages:messageCount thread:thread]; + [self deleteRandomRecentMessages:messageCount thread:thread transaction:transaction]; }, - ^{ + ^(YapDatabaseReadWriteTransaction *transaction) { NSUInteger messageCount = (NSUInteger)(1 + arc4random_uniform(4)); - [self insertAndDeleteNewOutgoingMessages:messageCount thread:thread]; + [self insertAndDeleteNewOutgoingMessages:messageCount thread:thread transaction:transaction]; }, - ^{ + ^(YapDatabaseReadWriteTransaction *transaction) { NSUInteger messageCount = (NSUInteger)(1 + arc4random_uniform(4)); - [self resurrectNewOutgoingMessages1:messageCount thread:thread]; + [self resurrectNewOutgoingMessages1:messageCount thread:thread transaction:transaction]; }, - ^{ + ^(YapDatabaseReadWriteTransaction *transaction) { NSUInteger messageCount = (NSUInteger)(1 + arc4random_uniform(4)); - [self resurrectNewOutgoingMessages2:messageCount thread:thread]; + [self resurrectNewOutgoingMessages2:messageCount thread:thread transaction:transaction]; }, ]; - ActionBlock actionBlock = actionBlocks[(NSUInteger) arc4random_uniform((uint32_t) actionBlocks.count)]; - actionBlock(); + [TSStorageManager.sharedManager.dbReadWriteConnection + readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + int actionCount = 1 + (int)arc4random_uniform(3); + for (int actionIdx = 0; actionIdx < actionCount; actionIdx++) { + ActionBlock actionBlock = actionBlocks[(NSUInteger)arc4random_uniform((uint32_t)actionBlocks.count)]; + actionBlock(transaction); + } + }]; } -+ (void)deleteRandomMessages:(NSUInteger)count thread:(TSThread *)thread ++ (void)deleteRandomMessages:(NSUInteger)count + thread:(TSThread *)thread + transaction:(YapDatabaseReadWriteTransaction *)transaction { DDLogInfo(@"%@ deleteRandomMessages: %zd", self.logTag, count); - [TSStorageManager.sharedManager.dbReadWriteConnection readWriteWithBlock:^( - YapDatabaseReadWriteTransaction *transaction) { - - YapDatabaseViewTransaction *interactionsByThread = [transaction ext:TSMessageDatabaseViewExtensionName]; - NSUInteger messageCount = [interactionsByThread numberOfItemsInGroup:thread.uniqueId]; - - NSMutableArray *messageIndices = [NSMutableArray new]; - for (NSUInteger messageIdx =0; messageIdx < messageCount; messageIdx++) { - [messageIndices addObject:@(messageIdx)]; - } - NSMutableArray *interactions = [NSMutableArray new]; - for (NSUInteger i =0; i < count && messageIndices.count > 0; i++) { - NSUInteger idx = (NSUInteger) arc4random_uniform((uint32_t) messageIndices.count); - NSNumber *messageIdx = messageIndices[idx]; - [messageIndices removeObjectAtIndex:idx]; - - TSInteraction *_Nullable interaction = + YapDatabaseViewTransaction *interactionsByThread = [transaction ext:TSMessageDatabaseViewExtensionName]; + NSUInteger messageCount = [interactionsByThread numberOfItemsInGroup:thread.uniqueId]; + + NSMutableArray *messageIndices = [NSMutableArray new]; + for (NSUInteger messageIdx = 0; messageIdx < messageCount; messageIdx++) { + [messageIndices addObject:@(messageIdx)]; + } + NSMutableArray *interactions = [NSMutableArray new]; + for (NSUInteger i = 0; i < count && messageIndices.count > 0; i++) { + NSUInteger idx = (NSUInteger)arc4random_uniform((uint32_t)messageIndices.count); + NSNumber *messageIdx = messageIndices[idx]; + [messageIndices removeObjectAtIndex:idx]; + + TSInteraction *_Nullable interaction = [interactionsByThread objectAtIndex:messageIdx.unsignedIntegerValue inGroup:thread.uniqueId]; - OWSAssert(interaction); - [interactions addObject:interaction]; - } - - for (TSInteraction *interaction in interactions) { - [interaction removeWithTransaction:transaction]; - } - }]; + OWSAssert(interaction); + [interactions addObject:interaction]; + } + + for (TSInteraction *interaction in interactions) { + [interaction removeWithTransaction:transaction]; + } } -+ (void)deleteLastMessages:(NSUInteger)count thread:(TSThread *)thread ++ (void)deleteLastMessages:(NSUInteger)count + thread:(TSThread *)thread + transaction:(YapDatabaseReadWriteTransaction *)transaction { DDLogInfo(@"%@ deleteLastMessages", self.logTag); + YapDatabaseViewTransaction *interactionsByThread = [transaction ext:TSMessageDatabaseViewExtensionName]; + NSUInteger messageCount = (NSUInteger)[interactionsByThread numberOfItemsInGroup:thread.uniqueId]; - [TSStorageManager.sharedManager.dbReadWriteConnection readWriteWithBlock:^( - YapDatabaseReadWriteTransaction *transaction) { - - YapDatabaseViewTransaction *interactionsByThread = [transaction ext:TSMessageDatabaseViewExtensionName]; - NSUInteger messageCount = (NSInteger)[interactionsByThread numberOfItemsInGroup:thread.uniqueId]; - - NSMutableArray *messageIndices = [NSMutableArray new]; - for (NSUInteger i = 0; i < count && i < messageCount; i++) { - NSUInteger messageIdx = messageCount - (1 + i); - [messageIndices addObject:@(messageIdx)]; - } - NSMutableArray *interactions = [NSMutableArray new]; - for (NSNumber *messageIdx in messageIndices) { - TSInteraction *_Nullable interaction = - [interactionsByThread objectAtIndex:messageIdx.unsignedIntegerValue inGroup:thread.uniqueId]; - OWSAssert(interaction); - [interactions addObject:interaction]; - } - for (TSInteraction *interaction in interactions) { - [interaction removeWithTransaction:transaction]; - } - }]; + NSMutableArray *messageIndices = [NSMutableArray new]; + for (NSUInteger i = 0; i < count && i < messageCount; i++) { + NSUInteger messageIdx = messageCount - (1 + i); + [messageIndices addObject:@(messageIdx)]; + } + NSMutableArray *interactions = [NSMutableArray new]; + for (NSNumber *messageIdx in messageIndices) { + TSInteraction *_Nullable interaction = + [interactionsByThread objectAtIndex:messageIdx.unsignedIntegerValue inGroup:thread.uniqueId]; + OWSAssert(interaction); + [interactions addObject:interaction]; + } + for (TSInteraction *interaction in interactions) { + [interaction removeWithTransaction:transaction]; + } } -+ (void)deleteRandomRecentMessages:(NSUInteger)count thread:(TSThread *)thread ++ (void)deleteRandomRecentMessages:(NSUInteger)count + thread:(TSThread *)thread + transaction:(YapDatabaseReadWriteTransaction *)transaction { DDLogInfo(@"%@ deleteRandomRecentMessages: %zd", self.logTag, count); - [TSStorageManager.sharedManager.dbReadWriteConnection readWriteWithBlock:^( - YapDatabaseReadWriteTransaction *transaction) { - - YapDatabaseViewTransaction *interactionsByThread = [transaction ext:TSMessageDatabaseViewExtensionName]; - NSInteger messageCount = (NSInteger) [interactionsByThread numberOfItemsInGroup:thread.uniqueId]; - - NSMutableArray *messageIndices = [NSMutableArray new]; - const NSInteger kRecentMessageCount = 10; - for (NSInteger i =0; i < kRecentMessageCount; i++) { - NSInteger messageIdx = messageCount - (1 + i); - if (messageIdx >= 0) { - [messageIndices addObject:@(messageIdx)]; - } + YapDatabaseViewTransaction *interactionsByThread = [transaction ext:TSMessageDatabaseViewExtensionName]; + NSInteger messageCount = (NSInteger)[interactionsByThread numberOfItemsInGroup:thread.uniqueId]; + + NSMutableArray *messageIndices = [NSMutableArray new]; + const NSInteger kRecentMessageCount = 10; + for (NSInteger i = 0; i < kRecentMessageCount; i++) { + NSInteger messageIdx = messageCount - (1 + i); + if (messageIdx >= 0) { + [messageIndices addObject:@(messageIdx)]; } - NSMutableArray *interactions = [NSMutableArray new]; - for (NSUInteger i =0; i < count && messageIndices.count > 0; i++) { - NSUInteger idx = (NSUInteger) arc4random_uniform((uint32_t) messageIndices.count); - NSNumber *messageIdx = messageIndices[idx]; - [messageIndices removeObjectAtIndex:idx]; - - TSInteraction *_Nullable interaction = + } + NSMutableArray *interactions = [NSMutableArray new]; + for (NSUInteger i = 0; i < count && messageIndices.count > 0; i++) { + NSUInteger idx = (NSUInteger)arc4random_uniform((uint32_t)messageIndices.count); + NSNumber *messageIdx = messageIndices[idx]; + [messageIndices removeObjectAtIndex:idx]; + + TSInteraction *_Nullable interaction = [interactionsByThread objectAtIndex:messageIdx.unsignedIntegerValue inGroup:thread.uniqueId]; - OWSAssert(interaction); - [interactions addObject:interaction]; - } - for (TSInteraction *interaction in interactions) { - [interaction removeWithTransaction:transaction]; - } - }]; + OWSAssert(interaction); + [interactions addObject:interaction]; + } + for (TSInteraction *interaction in interactions) { + [interaction removeWithTransaction:transaction]; + } } -+ (void)insertAndDeleteNewOutgoingMessages:(NSUInteger)count thread:(TSThread *)thread ++ (void)insertAndDeleteNewOutgoingMessages:(NSUInteger)count + thread:(TSThread *)thread + transaction:(YapDatabaseReadWriteTransaction *)transaction { DDLogInfo(@"%@ insertAndDeleteNewOutgoingMessages: %zd", self.logTag, count); @@ -1299,28 +1325,28 @@ NS_ASSUME_NONNULL_BEGIN for (NSUInteger i =0; i < count; i++) { NSString *text = [self randomText]; OWSDisappearingMessagesConfiguration *configuration = - [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId]; + [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId transaction:transaction]; TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:text attachmentIds:[NSMutableArray new] expiresInSeconds:(configuration.isEnabled ? configuration.durationSeconds : 0)]; + DDLogError(@"%@ insertAndDeleteNewOutgoingMessages timestamp: %llu.", self.logTag, message.timestamp); [messages addObject:message]; } - - [TSStorageManager.sharedManager.dbReadWriteConnection readWriteWithBlock:^( - YapDatabaseReadWriteTransaction *transaction) { - for (TSOutgoingMessage *message in messages) { - [message saveWithTransaction:transaction]; - } - for (TSOutgoingMessage *message in messages) { - [message removeWithTransaction:transaction]; - } - }]; + + for (TSOutgoingMessage *message in messages) { + [message saveWithTransaction:transaction]; + } + for (TSOutgoingMessage *message in messages) { + [message removeWithTransaction:transaction]; + } } -+ (void)resurrectNewOutgoingMessages1:(NSUInteger)count thread:(TSThread *)thread ++ (void)resurrectNewOutgoingMessages1:(NSUInteger)count + thread:(TSThread *)thread + transaction:(YapDatabaseReadWriteTransaction *)initialTransaction { DDLogInfo(@"%@ resurrectNewOutgoingMessages1.1: %zd", self.logTag, count); @@ -1328,22 +1354,22 @@ NS_ASSUME_NONNULL_BEGIN for (NSUInteger i =0; i < count; i++) { NSString *text = [self randomText]; OWSDisappearingMessagesConfiguration *configuration = - [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId]; + [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId + transaction:initialTransaction]; TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:text attachmentIds:[NSMutableArray new] expiresInSeconds:(configuration.isEnabled ? configuration.durationSeconds : 0)]; + DDLogError(@"%@ resurrectNewOutgoingMessages1 timestamp: %llu.", self.logTag, message.timestamp); [messages addObject:message]; } - - [TSStorageManager.sharedManager.dbReadWriteConnection readWriteWithBlock:^( - YapDatabaseReadWriteTransaction *transaction) { - for (TSOutgoingMessage *message in messages) { - [message saveWithTransaction:transaction]; - } - }]; + + for (TSOutgoingMessage *message in messages) { + [message saveWithTransaction:initialTransaction]; + } + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ DDLogInfo(@"%@ resurrectNewOutgoingMessages1.2: %zd", self.logTag, count); [TSStorageManager.sharedManager.dbReadWriteConnection @@ -1358,7 +1384,9 @@ NS_ASSUME_NONNULL_BEGIN }); } -+ (void)resurrectNewOutgoingMessages2:(NSUInteger)count thread:(TSThread *)thread ++ (void)resurrectNewOutgoingMessages2:(NSUInteger)count + thread:(TSThread *)thread + transaction:(YapDatabaseReadWriteTransaction *)initialTransaction { DDLogInfo(@"%@ resurrectNewOutgoingMessages2.1: %zd", self.logTag, count); @@ -1366,23 +1394,23 @@ NS_ASSUME_NONNULL_BEGIN for (NSUInteger i =0; i < count; i++) { NSString *text = [self randomText]; OWSDisappearingMessagesConfiguration *configuration = - [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId]; + [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId + transaction:initialTransaction]; TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:text attachmentIds:[NSMutableArray new] expiresInSeconds:(configuration.isEnabled ? configuration.durationSeconds : 0)]; + DDLogError(@"%@ resurrectNewOutgoingMessages2 timestamp: %llu.", self.logTag, message.timestamp); [messages addObject:message]; } - - [TSStorageManager.sharedManager.dbReadWriteConnection readWriteWithBlock:^( - YapDatabaseReadWriteTransaction *transaction) { - for (TSOutgoingMessage *message in messages) { - [message updateWithMessageState:TSOutgoingMessageStateAttemptingOut transaction:transaction]; - [message saveWithTransaction:transaction]; - } - }]; + + for (TSOutgoingMessage *message in messages) { + [message updateWithMessageState:TSOutgoingMessageStateAttemptingOut transaction:initialTransaction]; + [message saveWithTransaction:initialTransaction]; + } + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ DDLogInfo(@"%@ resurrectNewOutgoingMessages2.2: %zd", self.logTag, count); [TSStorageManager.sharedManager.dbReadWriteConnection diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.h b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.h index 521374ccd..ac8f9c490 100644 --- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.h +++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.h @@ -4,8 +4,10 @@ #import "DataSource.h" #import "TSAttachment.h" + #if TARGET_OS_IPHONE #import + #endif NS_ASSUME_NONNULL_BEGIN @@ -51,11 +53,9 @@ NS_ASSUME_NONNULL_BEGIN + (void)deleteAttachments; + (NSString *)attachmentsFolder; -- (CGSize)imageSizeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction; -- (CGSize)imageSizeWithoutTransaction; +- (CGSize)imageSize; -- (CGFloat)audioDurationSecondsWithTransaction:(YapDatabaseReadWriteTransaction *)transaction; -- (CGFloat)audioDurationSecondsWithoutTransaction; +- (CGFloat)audioDurationSeconds; @end diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m index 96d04d21b..ed6c5230e 100644 --- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m +++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m @@ -396,7 +396,7 @@ NS_ASSUME_NONNULL_BEGIN } } -- (CGSize)ensureCachedImageSizeWithTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction +- (CGSize)imageSize { OWSAssert([NSThread isMainThread]); @@ -408,68 +408,48 @@ NS_ASSUME_NONNULL_BEGIN self.cachedImageWidth = @(imageSize.width); self.cachedImageHeight = @(imageSize.height); - void (^updateDataStore)() = ^(YapDatabaseReadWriteTransaction *transaction) { - OWSAssert(transaction); - - NSString *collection = [[self class] collection]; - TSAttachmentStream *latestInstance = [transaction objectForKey:self.uniqueId inCollection:collection]; - if (latestInstance) { - latestInstance.cachedImageWidth = @(imageSize.width); - latestInstance.cachedImageHeight = @(imageSize.height); - [latestInstance saveWithTransaction:transaction]; - } else { - // This message has not yet been saved; do nothing. - OWSFail(@"%@ Attachment not yet saved.", self.logTag); - } - }; - - if (transaction) { - updateDataStore(transaction); - } else { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - updateDataStore(transaction); + + NSString *collection = [[self class] collection]; + TSAttachmentStream *latestInstance = [transaction objectForKey:self.uniqueId inCollection:collection]; + if (latestInstance) { + latestInstance.cachedImageWidth = @(imageSize.width); + latestInstance.cachedImageHeight = @(imageSize.height); + [latestInstance saveWithTransaction:transaction]; + } else { + // This message has not yet been saved; do nothing. + OWSFail(@"%@ Attachment not yet saved.", self.logTag); + } }]; - } + }); return imageSize; } -- (CGSize)imageSizeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert([NSThread isMainThread]); - OWSAssert(transaction); - - return [self ensureCachedImageSizeWithTransaction:transaction]; -} - -- (CGSize)imageSizeWithoutTransaction -{ - OWSAssert([NSThread isMainThread]); - - return [self ensureCachedImageSizeWithTransaction:nil]; -} - - (CGFloat)calculateAudioDurationSeconds { OWSAssert([NSThread isMainThread]); OWSAssert([self isAudio]); - NSError *error; - AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:self.mediaURL error:&error]; - if (error && [error.domain isEqualToString:NSOSStatusErrorDomain] - && (error.code == kAudioFileInvalidFileError || error.code == kAudioFileStreamError_InvalidFile)) { - // Ignore "invalid audio file" errors. - return 0.f; - } - if (!error) { - return (CGFloat)[audioPlayer duration]; - } else { - OWSFail(@"Could not find audio duration: %@", self.mediaURL); - return 0; - } + return 0; + + // NSError *error; + // AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:self.mediaURL error:&error]; + // if (error && [error.domain isEqualToString:NSOSStatusErrorDomain] + // && (error.code == kAudioFileInvalidFileError || error.code == kAudioFileStreamError_InvalidFile)) { + // // Ignore "invalid audio file" errors. + // return 0.f; + // } + // if (!error) { + // return (CGFloat)[audioPlayer duration]; + // } else { + // OWSFail(@"Could not find audio duration: %@", self.mediaURL); + // return 0; + // } } -- (CGFloat)ensureCachedAudioDurationSecondsWithTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction +- (CGFloat)audioDurationSeconds { OWSAssert([NSThread isMainThread]); @@ -480,46 +460,25 @@ NS_ASSUME_NONNULL_BEGIN CGFloat audioDurationSeconds = [self calculateAudioDurationSeconds]; self.cachedAudioDurationSeconds = @(audioDurationSeconds); - void (^updateDataStore)() = ^(YapDatabaseReadWriteTransaction *transaction) { - OWSAssert(transaction); - - NSString *collection = [[self class] collection]; - TSAttachmentStream *latestInstance = [transaction objectForKey:self.uniqueId inCollection:collection]; - if (latestInstance) { - latestInstance.cachedAudioDurationSeconds = @(audioDurationSeconds); - [latestInstance saveWithTransaction:transaction]; - } else { - // This message has not yet been saved; do nothing. - OWSFail(@"%@ Attachment not yet saved.", self.logTag); - } - }; - - if (transaction) { - updateDataStore(transaction); - } else { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - updateDataStore(transaction); + NSString *collection = [[self class] collection]; + TSAttachmentStream *latestInstance = [transaction objectForKey:self.uniqueId inCollection:collection]; + if (latestInstance) { + latestInstance.cachedAudioDurationSeconds = @(audioDurationSeconds); + [latestInstance saveWithTransaction:transaction]; + } else { + // This message has not yet been saved or has been deleted; do nothing. + // This isn't an error per se, but these race conditions should be + // _very_ rare. + OWSFail(@"%@ Attachment not yet saved.", self.logTag); + } }]; - } + }); return audioDurationSeconds; } -- (CGFloat)audioDurationSecondsWithTransaction:(YapDatabaseReadWriteTransaction *)transaction -{ - OWSAssert([NSThread isMainThread]); - OWSAssert(transaction); - - return [self ensureCachedAudioDurationSecondsWithTransaction:transaction]; -} - -- (CGFloat)audioDurationSecondsWithoutTransaction -{ - OWSAssert([NSThread isMainThread]); - - return [self ensureCachedAudioDurationSecondsWithTransaction:nil]; -} - @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/OWSReadReceiptManager.m b/SignalServiceKit/src/Messages/OWSReadReceiptManager.m index 3b94df19e..091a61197 100644 --- a/SignalServiceKit/src/Messages/OWSReadReceiptManager.m +++ b/SignalServiceKit/src/Messages/OWSReadReceiptManager.m @@ -363,7 +363,9 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE = (NSArray *)[TSInteraction interactionsWithTimestamp:sentTimestamp ofClass:[TSOutgoingMessage class] withTransaction:transaction]; - OWSAssert(messages.count <= 1); + if (messages.count > 1) { + OWSFail(@"%@ More than one matching message with timestamp: %llu.", self.logTag, sentTimestamp); + } if (messages.count > 0) { // 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 45c7d80d977271848a432086b990a9fa39d4505c Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 17 Nov 2017 15:10:08 -0500 Subject: [PATCH 3/5] Improving handling of edge cases in conversation view. --- .../ConversationViewController.m | 97 ++----------------- .../Messages/Attachments/TSAttachmentStream.m | 28 +++--- 2 files changed, 19 insertions(+), 106 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 0c7cf3758..c7febd917 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -97,7 +97,7 @@ static const int kYapDatabasePageSize = 50; static const int kYapDatabaseMaxPageCount = 500; // Never show more than 6*50 = 300 messages in conversation view when user // arrives. -static const int kYapDatabaseMaxInitialPageCount = 500; +static const int kYapDatabaseMaxInitialPageCount = 6; static const int kConversationInitialMaxRangeSize = kYapDatabasePageSize * kYapDatabaseMaxInitialPageCount; static const int kYapDatabaseRangeMaxLength = kYapDatabasePageSize * kYapDatabaseMaxPageCount; static const int kYapDatabaseRangeMinLength = 0; @@ -2891,7 +2891,6 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { rowChange.collectionKey, rowChange.indexPath, rowChange.finalIndex); - [DDLog flushLog]; [self.collectionView deleteItemsAtIndexPaths:@[ rowChange.indexPath ]]; YapCollectionKey *collectionKey = rowChange.collectionKey; OWSAssert(collectionKey.key.length > 0); @@ -2902,7 +2901,6 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { rowChange.collectionKey, rowChange.newIndexPath, rowChange.finalIndex); - [DDLog flushLog]; [self.collectionView insertItemsAtIndexPaths:@[ rowChange.newIndexPath ]]; // We don't want to reload a row that we just inserted. [rowsThatChangedSize removeObject:@(rowChange.finalIndex)]; @@ -2923,7 +2921,6 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { rowChange.indexPath, rowChange.newIndexPath, rowChange.finalIndex); - [DDLog flushLog]; [self.collectionView moveItemAtIndexPath:rowChange.indexPath toIndexPath:rowChange.newIndexPath]; // We don't want to reload a row that we just moved. [rowsThatChangedSize removeObject:@(rowChange.finalIndex)]; @@ -2934,7 +2931,6 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { rowChange.collectionKey, rowChange.indexPath, rowChange.finalIndex); - [DDLog flushLog]; [self.collectionView reloadItemsAtIndexPaths:@[ rowChange.indexPath ]]; // We don't want to reload a row that we've already reloaded. [rowsThatChangedSize removeObject:@(rowChange.finalIndex)]; @@ -2951,13 +2947,11 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { [rowsToReload addObject:[NSIndexPath indexPathForRow:row.integerValue inSection:0]]; } if (rowsToReload.count > 0) { - [DDLog flushLog]; [self.collectionView reloadItemsAtIndexPaths:rowsToReload]; } }; DDLogVerbose(@"self.viewItems.count: %zd -> %zd", oldViewItemCount, self.viewItems.count); - [DDLog flushLog]; BOOL shouldReloadCollection = [self shouldReloadCollection:rowChanges]; if (shouldReloadCollection) { @@ -2994,62 +2988,17 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { { OWSAssert(rowChanges); - BOOL hasDeletes = NO; - BOOL hasInserts = NO; - BOOL hasMoves = NO; - BOOL hasUpdates = NO; for (YapDatabaseViewRowChange *rowChange in rowChanges) { switch (rowChange.type) { - case YapDatabaseViewChangeDelete: - DDLogVerbose(@"? YapDatabaseViewChangeDelete: %@, %@, %zd", - rowChange.collectionKey, - rowChange.indexPath, - rowChange.finalIndex); - [DDLog flushLog]; - hasDeletes = YES; - break; - case YapDatabaseViewChangeInsert: - DDLogVerbose(@"...YapDatabaseViewChangeInsert: %@, %@, %zd", - rowChange.collectionKey, - rowChange.newIndexPath, - rowChange.finalIndex); - [DDLog flushLog]; - hasInserts = YES; - break; case YapDatabaseViewChangeMove: - DDLogVerbose(@"...YapDatabaseViewChangeMove: %@, %@, %@, %zd", - rowChange.collectionKey, - rowChange.indexPath, - rowChange.newIndexPath, - rowChange.finalIndex); - [DDLog flushLog]; - hasMoves = YES; - break; - case YapDatabaseViewChangeUpdate: - DDLogVerbose(@"...YapDatabaseViewChangeUpdate: %@, %@, %zd", - rowChange.collectionKey, - rowChange.indexPath, - rowChange.finalIndex); - [DDLog flushLog]; - hasUpdates = YES; + // "Move" changes cannot be safely performed using + // [UICollectionView performBatchUpdates:]. This appears to be a + // bug in YapDatabase. + return YES; + default: break; } } - if (hasMoves) { - // "Move" changes cannot be safely performed using - // [UICollectionView performBatchUpdates:]. This appears to be a - // bug in YapDatabase. - return YES; - } - // if (hasDeletes && hasInserts) { - // return YES; - // } - // if (hasDeletes && hasUpdates) { - // return YES; - // } - // if (hasInserts && hasUpdates) { - // return YES; - // } return NO; } @@ -3058,40 +3007,6 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { { OWSAssert(rowChanges); - // for (YapDatabaseViewRowChange *rowChange in rowChanges) { - // switch (rowChange.type) { - // case YapDatabaseViewChangeDelete: - // DDLogVerbose(@"...YapDatabaseViewChangeDelete: %@, %@, %zd", - // rowChange.collectionKey, - // rowChange.indexPath, - // rowChange.finalIndex); - // [DDLog flushLog]; - // break; - // case YapDatabaseViewChangeInsert: - // DDLogVerbose(@"...YapDatabaseViewChangeInsert: %@, %@, %zd", - // rowChange.collectionKey, - // rowChange.newIndexPath, - // rowChange.finalIndex); - // [DDLog flushLog]; - // break; - // case YapDatabaseViewChangeMove: - // DDLogVerbose(@"...YapDatabaseViewChangeMove: %@, %@, %@, %zd", - // rowChange.collectionKey, - // rowChange.indexPath, - // rowChange.newIndexPath, - // rowChange.finalIndex); - // [DDLog flushLog]; - // break; - // case YapDatabaseViewChangeUpdate: - // DDLogVerbose(@"...YapDatabaseViewChangeUpdate: %@, %@, %zd", - // rowChange.collectionKey, - // rowChange.indexPath, - // rowChange.finalIndex); - // [DDLog flushLog]; - // break; - // } - // } - // If user sends a new outgoing message, don't animate the change. BOOL isOnlyInsertingNewOutgoingMessages = YES; BOOL isOnlyUpdatingLastOutgoingMessage = YES; diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m index ed6c5230e..f6479e585 100644 --- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m +++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m @@ -432,21 +432,19 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert([NSThread isMainThread]); OWSAssert([self isAudio]); - return 0; - - // NSError *error; - // AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:self.mediaURL error:&error]; - // if (error && [error.domain isEqualToString:NSOSStatusErrorDomain] - // && (error.code == kAudioFileInvalidFileError || error.code == kAudioFileStreamError_InvalidFile)) { - // // Ignore "invalid audio file" errors. - // return 0.f; - // } - // if (!error) { - // return (CGFloat)[audioPlayer duration]; - // } else { - // OWSFail(@"Could not find audio duration: %@", self.mediaURL); - // return 0; - // } + NSError *error; + AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:self.mediaURL error:&error]; + if (error && [error.domain isEqualToString:NSOSStatusErrorDomain] + && (error.code == kAudioFileInvalidFileError || error.code == kAudioFileStreamError_InvalidFile)) { + // Ignore "invalid audio file" errors. + return 0.f; + } + if (!error) { + return (CGFloat)[audioPlayer duration]; + } else { + OWSFail(@"Could not find audio duration: %@", self.mediaURL); + return 0; + } } - (CGFloat)audioDurationSeconds From b3d17ea19282ebca74b9937484d2a72d69e4de27 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 17 Nov 2017 15:14:46 -0500 Subject: [PATCH 4/5] Improving handling of edge cases in conversation view. --- .../src/Messages/Attachments/TSAttachmentStream.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m index f6479e585..bf8b4bdeb 100644 --- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m +++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m @@ -418,7 +418,9 @@ NS_ASSUME_NONNULL_BEGIN latestInstance.cachedImageHeight = @(imageSize.height); [latestInstance saveWithTransaction:transaction]; } else { - // This message has not yet been saved; do nothing. + // This message has not yet been saved or has been deleted; do nothing. + // This isn't an error per se, but these race conditions should be + // _very_ rare. OWSFail(@"%@ Attachment not yet saved.", self.logTag); } }]; From d8ae5841d6d12de30db4b47e4628d1ba1c8d8335 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 20 Nov 2017 14:50:43 -0500 Subject: [PATCH 5/5] Respond to CR. // FREEBIE --- .../Cells/OWSAudioMessageView.m | 4 +- .../ConversationView/ConversationViewItem.h | 2 +- .../ConversationView/ConversationViewItem.m | 4 +- .../Messages/Attachments/TSAttachmentStream.m | 60 +++++++++---------- 4 files changed, 33 insertions(+), 37 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.m index 076bfcda5..22e749602 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSAudioMessageView.m @@ -62,9 +62,9 @@ NS_ASSUME_NONNULL_BEGIN - (CGFloat)audioDurationSeconds { - OWSAssert(self.viewItem.audioDurationSeconds); + OWSAssert(self.viewItem.audioDurationSeconds > 0.f); - return [self.viewItem.audioDurationSeconds floatValue]; + return self.viewItem.audioDurationSeconds; } - (AudioPlaybackState)audioPlaybackState diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h index 8d0c19d6d..bfece31d8 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h @@ -72,7 +72,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); @property (nonatomic, weak) OWSAudioMessageView *lastAudioMessageView; -@property (nonatomic, readonly, nullable) NSNumber *audioDurationSeconds; +@property (nonatomic, readonly) CGFloat audioDurationSeconds; - (CGFloat)audioProgressSeconds; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index 99be46755..d920a1ed3 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -47,7 +47,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) @property (nonatomic) AudioPlaybackState audioPlaybackState; @property (nonatomic) CGFloat audioProgressSeconds; -@property (nonatomic, nullable) NSNumber *audioDurationSeconds; +@property (nonatomic) CGFloat audioDurationSeconds; #pragma mark - View State @@ -396,7 +396,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) } else if ([self.attachmentStream isAudio]) { CGFloat audioDurationSeconds = [self.attachmentStream audioDurationSeconds]; if (audioDurationSeconds > 0) { - self.audioDurationSeconds = @(audioDurationSeconds); + self.audioDurationSeconds = audioDurationSeconds; self.messageCellType = OWSMessageCellType_Audio; } else { self.messageCellType = OWSMessageCellType_GenericAttachment; diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m index bf8b4bdeb..5c6a74003 100644 --- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m +++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m @@ -408,23 +408,21 @@ NS_ASSUME_NONNULL_BEGIN self.cachedImageWidth = @(imageSize.width); self.cachedImageHeight = @(imageSize.height); - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - - NSString *collection = [[self class] collection]; - TSAttachmentStream *latestInstance = [transaction objectForKey:self.uniqueId inCollection:collection]; - if (latestInstance) { - latestInstance.cachedImageWidth = @(imageSize.width); - latestInstance.cachedImageHeight = @(imageSize.height); - [latestInstance saveWithTransaction:transaction]; - } else { - // This message has not yet been saved or has been deleted; do nothing. - // This isn't an error per se, but these race conditions should be - // _very_ rare. - OWSFail(@"%@ Attachment not yet saved.", self.logTag); - } - }]; - }); + [self.dbReadWriteConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + + NSString *collection = [[self class] collection]; + TSAttachmentStream *latestInstance = [transaction objectForKey:self.uniqueId inCollection:collection]; + if (latestInstance) { + latestInstance.cachedImageWidth = @(imageSize.width); + latestInstance.cachedImageHeight = @(imageSize.height); + [latestInstance saveWithTransaction:transaction]; + } else { + // This message has not yet been saved or has been deleted; do nothing. + // This isn't an error per se, but these race conditions should be + // _very_ rare. + OWSFail(@"%@ Attachment not yet saved.", self.logTag); + } + }]; return imageSize; } @@ -460,21 +458,19 @@ NS_ASSUME_NONNULL_BEGIN CGFloat audioDurationSeconds = [self calculateAudioDurationSeconds]; self.cachedAudioDurationSeconds = @(audioDurationSeconds); - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - NSString *collection = [[self class] collection]; - TSAttachmentStream *latestInstance = [transaction objectForKey:self.uniqueId inCollection:collection]; - if (latestInstance) { - latestInstance.cachedAudioDurationSeconds = @(audioDurationSeconds); - [latestInstance saveWithTransaction:transaction]; - } else { - // This message has not yet been saved or has been deleted; do nothing. - // This isn't an error per se, but these race conditions should be - // _very_ rare. - OWSFail(@"%@ Attachment not yet saved.", self.logTag); - } - }]; - }); + [self.dbReadWriteConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + NSString *collection = [[self class] collection]; + TSAttachmentStream *latestInstance = [transaction objectForKey:self.uniqueId inCollection:collection]; + if (latestInstance) { + latestInstance.cachedAudioDurationSeconds = @(audioDurationSeconds); + [latestInstance saveWithTransaction:transaction]; + } else { + // This message has not yet been saved or has been deleted; do nothing. + // This isn't an error per se, but these race conditions should be + // _very_ rare. + OWSFail(@"%@ Attachment not yet saved.", self.logTag); + } + }]; return audioDurationSeconds; }