Improving handling of edge cases in conversation view.

This commit is contained in:
Matthew Chen 2017-11-17 11:56:48 -05:00
parent 658746093d
commit 6d4a05bbea
8 changed files with 438 additions and 333 deletions

View File

@ -62,12 +62,9 @@ NS_ASSUME_NONNULL_BEGIN
- (CGFloat)audioDurationSeconds - (CGFloat)audioDurationSeconds
{ {
NSNumber *_Nullable audioDurationSeconds = self.viewItem.audioDurationSeconds; OWSAssert(self.viewItem.audioDurationSeconds);
if (!audioDurationSeconds) {
audioDurationSeconds = @([self.attachmentStream audioDurationSecondsWithoutTransaction]); return [self.viewItem.audioDurationSeconds floatValue];
self.viewItem.audioDurationSeconds = audioDurationSeconds;
}
return [audioDurationSeconds floatValue];
} }
- (AudioPlaybackState)audioPlaybackState - (AudioPlaybackState)audioPlaybackState

View File

@ -97,7 +97,7 @@ static const int kYapDatabasePageSize = 50;
static const int kYapDatabaseMaxPageCount = 500; static const int kYapDatabaseMaxPageCount = 500;
// Never show more than 6*50 = 300 messages in conversation view when user // Never show more than 6*50 = 300 messages in conversation view when user
// arrives. // arrives.
static const int kYapDatabaseMaxInitialPageCount = 6; static const int kYapDatabaseMaxInitialPageCount = 500;
static const int kConversationInitialMaxRangeSize = kYapDatabasePageSize * kYapDatabaseMaxInitialPageCount; static const int kConversationInitialMaxRangeSize = kYapDatabasePageSize * kYapDatabaseMaxInitialPageCount;
static const int kYapDatabaseRangeMaxLength = kYapDatabasePageSize * kYapDatabaseMaxPageCount; static const int kYapDatabaseRangeMaxLength = kYapDatabasePageSize * kYapDatabaseMaxPageCount;
static const int kYapDatabaseRangeMinLength = 0; static const int kYapDatabaseRangeMinLength = 0;
@ -2883,8 +2883,6 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
// b) is inserting new interactions. // b) is inserting new interactions.
__block BOOL scrollToBottom = wasAtBottom; __block BOOL scrollToBottom = wasAtBottom;
BOOL shouldAnimateUpdates = [self shouldAnimateRowUpdates:rowChanges oldViewItemCount:oldViewItemCount];
void (^batchUpdates)(void) = ^{ void (^batchUpdates)(void) = ^{
for (YapDatabaseViewRowChange *rowChange in rowChanges) { for (YapDatabaseViewRowChange *rowChange in rowChanges) {
switch (rowChange.type) { switch (rowChange.type) {
@ -2893,6 +2891,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
rowChange.collectionKey, rowChange.collectionKey,
rowChange.indexPath, rowChange.indexPath,
rowChange.finalIndex); rowChange.finalIndex);
[DDLog flushLog];
[self.collectionView deleteItemsAtIndexPaths:@[ rowChange.indexPath ]]; [self.collectionView deleteItemsAtIndexPaths:@[ rowChange.indexPath ]];
YapCollectionKey *collectionKey = rowChange.collectionKey; YapCollectionKey *collectionKey = rowChange.collectionKey;
OWSAssert(collectionKey.key.length > 0); OWSAssert(collectionKey.key.length > 0);
@ -2903,6 +2902,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
rowChange.collectionKey, rowChange.collectionKey,
rowChange.newIndexPath, rowChange.newIndexPath,
rowChange.finalIndex); rowChange.finalIndex);
[DDLog flushLog];
[self.collectionView insertItemsAtIndexPaths:@[ rowChange.newIndexPath ]]; [self.collectionView insertItemsAtIndexPaths:@[ rowChange.newIndexPath ]];
// We don't want to reload a row that we just inserted. // We don't want to reload a row that we just inserted.
[rowsThatChangedSize removeObject:@(rowChange.finalIndex)]; [rowsThatChangedSize removeObject:@(rowChange.finalIndex)];
@ -2923,8 +2923,8 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
rowChange.indexPath, rowChange.indexPath,
rowChange.newIndexPath, rowChange.newIndexPath,
rowChange.finalIndex); rowChange.finalIndex);
[self.collectionView deleteItemsAtIndexPaths:@[ rowChange.indexPath ]]; [DDLog flushLog];
[self.collectionView insertItemsAtIndexPaths:@[ rowChange.newIndexPath ]]; [self.collectionView moveItemAtIndexPath:rowChange.indexPath toIndexPath:rowChange.newIndexPath];
// We don't want to reload a row that we just moved. // We don't want to reload a row that we just moved.
[rowsThatChangedSize removeObject:@(rowChange.finalIndex)]; [rowsThatChangedSize removeObject:@(rowChange.finalIndex)];
break; break;
@ -2934,6 +2934,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
rowChange.collectionKey, rowChange.collectionKey,
rowChange.indexPath, rowChange.indexPath,
rowChange.finalIndex); rowChange.finalIndex);
[DDLog flushLog];
[self.collectionView reloadItemsAtIndexPaths:@[ rowChange.indexPath ]]; [self.collectionView reloadItemsAtIndexPaths:@[ rowChange.indexPath ]];
// We don't want to reload a row that we've already reloaded. // We don't want to reload a row that we've already reloaded.
[rowsThatChangedSize removeObject:@(rowChange.finalIndex)]; [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. // as they may affect which cells show "date" headers or "status" footers.
NSMutableArray<NSIndexPath *> *rowsToReload = [NSMutableArray new]; NSMutableArray<NSIndexPath *> *rowsToReload = [NSMutableArray new];
for (NSNumber *row in rowsThatChangedSize) { for (NSNumber *row in rowsThatChangedSize) {
DDLogVerbose(@"rowsToReload: %@", row);
[rowsToReload addObject:[NSIndexPath indexPathForRow:row.integerValue inSection:0]]; [rowsToReload addObject:[NSIndexPath indexPathForRow:row.integerValue inSection:0]];
} }
if (rowsToReload.count > 0) { if (rowsToReload.count > 0) {
[DDLog flushLog];
[self.collectionView reloadItemsAtIndexPaths:rowsToReload]; [self.collectionView reloadItemsAtIndexPaths:rowsToReload];
} }
}; };
void (^batchUpdatesCompletion)(BOOL) = ^(BOOL finished) {
OWSAssert([NSThread isMainThread]);
if (!finished) { DDLogVerbose(@"self.viewItems.count: %zd -> %zd", oldViewItemCount, self.viewItems.count);
DDLogInfo(@"%@ performBatchUpdates did not finish", self.logTag); [DDLog flushLog];
}
[self updateLastVisibleTimestamp]; BOOL shouldReloadCollection = [self shouldReloadCollection:rowChanges];
if (shouldReloadCollection) {
if (scrollToBottom) {
[self scrollToBottomAnimated:shouldAnimateScrollToBottom && shouldAnimateUpdates];
}
};
if (shouldAnimateUpdates) {
[self.collectionView performBatchUpdates:batchUpdates completion:batchUpdatesCompletion];
} else {
[UIView performWithoutAnimation:^{ [UIView performWithoutAnimation:^{
[self.collectionView performBatchUpdates:batchUpdates completion:batchUpdatesCompletion]; [self.collectionView reloadData];
}]; }];
[self updateLastVisibleTimestamp];
} else {
BOOL shouldAnimateUpdates = [self shouldAnimateRowUpdates:rowChanges oldViewItemCount:oldViewItemCount];
void (^batchUpdatesCompletion)(BOOL) = ^(BOOL finished) {
OWSAssert([NSThread isMainThread]);
if (!finished) {
DDLogInfo(@"%@ performBatchUpdates did not finish", self.logTag);
}
[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<YapDatabaseViewRowChange *> *)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<YapDatabaseViewRowChange *> *)rowChanges - (BOOL)shouldAnimateRowUpdates:(NSArray<YapDatabaseViewRowChange *> *)rowChanges
oldViewItemCount:(NSUInteger)oldViewItemCount oldViewItemCount:(NSUInteger)oldViewItemCount
{ {
OWSAssert(rowChanges); 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. // If user sends a new outgoing message, don't animate the change.
BOOL isOnlyInsertingNewOutgoingMessages = YES; BOOL isOnlyInsertingNewOutgoingMessages = YES;
BOOL isOnlyUpdatingLastOutgoingMessage = YES; BOOL isOnlyUpdatingLastOutgoingMessage = YES;
@ -3325,20 +3437,23 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
[self presentViewController:actionSheetController animated:true completion:nil]; [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]) { for (NSIndexPath *indexPath in [self.collectionView indexPathsForVisibleItems]) {
if (!lastVisibleIndexPath || indexPath.row > lastVisibleIndexPath.row) { if (!lastVisibleIndexPath || indexPath.row > lastVisibleIndexPath.row) {
lastVisibleIndexPath = indexPath; 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; return lastVisibleIndexPath;
} }
- (nullable ConversationViewItem *)lastVisibleViewItem - (nullable ConversationViewItem *)lastVisibleViewItem
{ {
NSIndexPath *lastVisibleIndexPath = [self lastVisibleIndexPath]; NSIndexPath *_Nullable lastVisibleIndexPath = [self lastVisibleIndexPath];
if (!lastVisibleIndexPath) { if (!lastVisibleIndexPath) {
return nil; return nil;
} }

View File

@ -72,7 +72,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);
@property (nonatomic, weak) OWSAudioMessageView *lastAudioMessageView; @property (nonatomic, weak) OWSAudioMessageView *lastAudioMessageView;
@property (nonatomic, nullable) NSNumber *audioDurationSeconds; @property (nonatomic, readonly, nullable) NSNumber *audioDurationSeconds;
- (CGFloat)audioProgressSeconds; - (CGFloat)audioProgressSeconds;

View File

@ -47,6 +47,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
@property (nonatomic) AudioPlaybackState audioPlaybackState; @property (nonatomic) AudioPlaybackState audioPlaybackState;
@property (nonatomic) CGFloat audioProgressSeconds; @property (nonatomic) CGFloat audioProgressSeconds;
@property (nonatomic, nullable) NSNumber *audioDurationSeconds;
#pragma mark - View State #pragma mark - View State
@ -251,9 +252,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
OWSAssert([NSThread isMainThread]); OWSAssert([NSThread isMainThread]);
self.audioProgressSeconds = progress; self.audioProgressSeconds = progress;
if (duration > 0) {
self.audioDurationSeconds = @(duration);
}
[self.lastAudioMessageView updateContents]; [self.lastAudioMessageView updateContents];
} }
@ -390,13 +388,19 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
self.messageCellType = OWSMessageCellType_GenericAttachment; self.messageCellType = OWSMessageCellType_GenericAttachment;
return; return;
} }
self.contentSize = [self.attachmentStream imageSizeWithoutTransaction]; self.contentSize = [self.attachmentStream imageSize];
if (self.contentSize.width <= 0 || self.contentSize.height <= 0) { if (self.contentSize.width <= 0 || self.contentSize.height <= 0) {
self.messageCellType = OWSMessageCellType_GenericAttachment; self.messageCellType = OWSMessageCellType_GenericAttachment;
} }
return; return;
} else if ([self.attachmentStream isAudio]) { } 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; return;
} else { } else {
self.messageCellType = OWSMessageCellType_GenericAttachment; self.messageCellType = OWSMessageCellType_GenericAttachment;

View File

@ -40,6 +40,14 @@ NS_ASSUME_NONNULL_BEGIN
OWSAssert(thread); OWSAssert(thread);
NSMutableArray<OWSTableItem *> *items = [@[ NSMutableArray<OWSTableItem *> *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.)" [OWSTableItem itemWithTitle:@"Send 10 messages (1/sec.)"
actionBlock:^{ actionBlock:^{
[DebugUIMessages sendTextMessages:10 thread:thread]; [DebugUIMessages sendTextMessages:10 thread:thread];
@ -241,14 +249,6 @@ NS_ASSUME_NONNULL_BEGIN
actionBlock:^{ actionBlock:^{
[DebugUIMessages injectFakeIncomingMessages:1000 thread:thread]; [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]; ] mutableCopy];
if ([thread isKindOfClass:[TSContactThread class]]) { if ([thread isKindOfClass:[TSContactThread class]]) {
TSContactThread *contactThread = (TSContactThread *)thread; TSContactThread *contactThread = (TSContactThread *)thread;
@ -271,10 +271,14 @@ NS_ASSUME_NONNULL_BEGIN
+ (void)sendTextMessageInThread:(TSThread *)thread counter:(int)counter + (void)sendTextMessageInThread:(TSThread *)thread counter:(int)counter
{ {
DDLogInfo(@"%@ sendTextMessageInThread: %d", self.logTag, counter);
[DDLog flushLog];
NSString *randomText = [self randomText]; NSString *randomText = [self randomText];
NSString *text = [[[@(counter) description] stringByAppendingString:@" "] stringByAppendingString:randomText]; NSString *text = [[[@(counter) description] stringByAppendingString:@" "] stringByAppendingString:randomText];
OWSMessageSender *messageSender = [Environment getCurrent].messageSender; 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 + (void)sendTextMessages:(int)counter thread:(TSThread *)thread
@ -939,83 +943,96 @@ NS_ASSUME_NONNULL_BEGIN
+ (void)sendFakeMessages:(NSUInteger)counter thread:(TSThread *)thread + (void)sendFakeMessages:(NSUInteger)counter thread:(TSThread *)thread
{ {
[TSStorageManager.sharedManager.dbReadWriteConnection readWriteWithBlock:^( [TSStorageManager.sharedManager.dbReadWriteConnection
YapDatabaseReadWriteTransaction *transaction) { readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
for (NSUInteger i = 0; i < counter; i++) { [self sendFakeMessages:counter thread:thread transaction:transaction];
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"; + (void)sendFakeMessages:(NSUInteger)counter
UInt32 filesize = 16; thread:(TSThread *)thread
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
DDLogInfo(@"%@ sendFakeMessages: %zd", self.logTag, counter);
TSAttachmentStream *attachmentStream = [[TSAttachmentStream alloc] initWithContentType:@"audio/mp3" for (NSUInteger i = 0; i < counter; i++) {
byteCount:filesize NSString *randomText = [self randomText];
sourceFilename:filename]; 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);
NSError *error; NSString *filename = @"test.mp3";
BOOL success = [attachmentStream writeData:[self createRandomNSDataOfSize:filesize] error:&error]; UInt32 filesize = 16;
OWSAssert(success && !error);
[attachmentStream saveWithTransaction:transaction]; TSAttachmentStream *attachmentStream = [[TSAttachmentStream alloc] initWithContentType:@"audio/mp3"
[message.attachmentIds addObject:attachmentStream.uniqueId]; byteCount:filesize
if (filename) { sourceFilename:filename];
message.attachmentFilenameMap[attachmentStream.uniqueId] = filename;
} NSError *error;
[message saveWithTransaction:transaction]; BOOL success = [attachmentStream writeData:[self createRandomNSDataOfSize:filesize] error:&error];
break; 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 + (void)sendTinyAttachments:(int)counter thread:(TSThread *)thread
@ -1099,6 +1116,8 @@ NS_ASSUME_NONNULL_BEGIN
{ {
OWSAssert(thread); OWSAssert(thread);
DDLogInfo(@"%@ injectIncomingMessageInThread: %d", self.logTag, counter);
NSString *randomText = [self randomText]; NSString *randomText = [self randomText];
NSString *text = [[[@(counter) description] stringByAppendingString:@" "] stringByAppendingString:randomText]; NSString *text = [[[@(counter) description] stringByAppendingString:@" "] stringByAppendingString:randomText];
@ -1154,144 +1173,151 @@ NS_ASSUME_NONNULL_BEGIN
+ (void)performRandomActionInThread:(TSThread *)thread + (void)performRandomActionInThread:(TSThread *)thread
counter:(int)counter counter:(int)counter
{ {
typedef void (^ActionBlock)(void); typedef void (^ActionBlock)(YapDatabaseReadWriteTransaction *transaction);
NSArray<ActionBlock> *actionBlocks = @[ NSArray<ActionBlock> *actionBlocks = @[
^{ ^(YapDatabaseReadWriteTransaction *transaction) {
[self injectIncomingMessageInThread:thread counter:counter]; // injectIncomingMessageInThread doesn't take a transaction.
dispatch_async(dispatch_get_main_queue(), ^{
[self injectIncomingMessageInThread:thread counter:counter];
});
}, },
^{ ^(YapDatabaseReadWriteTransaction *transaction) {
[self sendTextMessageInThread:thread counter:counter]; // 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)); 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)); 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)); 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)); 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)); 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)); 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)); 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)]; [TSStorageManager.sharedManager.dbReadWriteConnection
actionBlock(); 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); DDLogInfo(@"%@ deleteRandomMessages: %zd", self.logTag, count);
[TSStorageManager.sharedManager.dbReadWriteConnection readWriteWithBlock:^( YapDatabaseViewTransaction *interactionsByThread = [transaction ext:TSMessageDatabaseViewExtensionName];
YapDatabaseReadWriteTransaction *transaction) { NSUInteger messageCount = [interactionsByThread numberOfItemsInGroup:thread.uniqueId];
YapDatabaseViewTransaction *interactionsByThread = [transaction ext:TSMessageDatabaseViewExtensionName]; NSMutableArray<NSNumber *> *messageIndices = [NSMutableArray new];
NSUInteger messageCount = [interactionsByThread numberOfItemsInGroup:thread.uniqueId]; for (NSUInteger messageIdx = 0; messageIdx < messageCount; messageIdx++) {
[messageIndices addObject:@(messageIdx)];
NSMutableArray<NSNumber *> *messageIndices = [NSMutableArray new]; }
for (NSUInteger messageIdx =0; messageIdx < messageCount; messageIdx++) { NSMutableArray<TSInteraction *> *interactions = [NSMutableArray new];
[messageIndices addObject:@(messageIdx)]; for (NSUInteger i = 0; i < count && messageIndices.count > 0; i++) {
} NSUInteger idx = (NSUInteger)arc4random_uniform((uint32_t)messageIndices.count);
NSMutableArray<TSInteraction *> *interactions = [NSMutableArray new]; NSNumber *messageIdx = messageIndices[idx];
for (NSUInteger i =0; i < count && messageIndices.count > 0; i++) { [messageIndices removeObjectAtIndex:idx];
NSUInteger idx = (NSUInteger) arc4random_uniform((uint32_t) messageIndices.count);
NSNumber *messageIdx = messageIndices[idx]; TSInteraction *_Nullable interaction =
[messageIndices removeObjectAtIndex:idx];
TSInteraction *_Nullable interaction =
[interactionsByThread objectAtIndex:messageIdx.unsignedIntegerValue inGroup:thread.uniqueId]; [interactionsByThread objectAtIndex:messageIdx.unsignedIntegerValue inGroup:thread.uniqueId];
OWSAssert(interaction); OWSAssert(interaction);
[interactions addObject:interaction]; [interactions addObject:interaction];
} }
for (TSInteraction *interaction in interactions) { for (TSInteraction *interaction in interactions) {
[interaction removeWithTransaction:transaction]; [interaction removeWithTransaction:transaction];
} }
}];
} }
+ (void)deleteLastMessages:(NSUInteger)count thread:(TSThread *)thread + (void)deleteLastMessages:(NSUInteger)count
thread:(TSThread *)thread
transaction:(YapDatabaseReadWriteTransaction *)transaction
{ {
DDLogInfo(@"%@ deleteLastMessages", self.logTag); DDLogInfo(@"%@ deleteLastMessages", self.logTag);
YapDatabaseViewTransaction *interactionsByThread = [transaction ext:TSMessageDatabaseViewExtensionName];
NSUInteger messageCount = (NSUInteger)[interactionsByThread numberOfItemsInGroup:thread.uniqueId];
[TSStorageManager.sharedManager.dbReadWriteConnection readWriteWithBlock:^( NSMutableArray<NSNumber *> *messageIndices = [NSMutableArray new];
YapDatabaseReadWriteTransaction *transaction) { for (NSUInteger i = 0; i < count && i < messageCount; i++) {
NSUInteger messageIdx = messageCount - (1 + i);
YapDatabaseViewTransaction *interactionsByThread = [transaction ext:TSMessageDatabaseViewExtensionName]; [messageIndices addObject:@(messageIdx)];
NSUInteger messageCount = (NSInteger)[interactionsByThread numberOfItemsInGroup:thread.uniqueId]; }
NSMutableArray<TSInteraction *> *interactions = [NSMutableArray new];
NSMutableArray<NSNumber *> *messageIndices = [NSMutableArray new]; for (NSNumber *messageIdx in messageIndices) {
for (NSUInteger i = 0; i < count && i < messageCount; i++) { TSInteraction *_Nullable interaction =
NSUInteger messageIdx = messageCount - (1 + i); [interactionsByThread objectAtIndex:messageIdx.unsignedIntegerValue inGroup:thread.uniqueId];
[messageIndices addObject:@(messageIdx)]; OWSAssert(interaction);
} [interactions addObject:interaction];
NSMutableArray<TSInteraction *> *interactions = [NSMutableArray new]; }
for (NSNumber *messageIdx in messageIndices) { for (TSInteraction *interaction in interactions) {
TSInteraction *_Nullable interaction = [interaction removeWithTransaction:transaction];
[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); DDLogInfo(@"%@ deleteRandomRecentMessages: %zd", self.logTag, count);
[TSStorageManager.sharedManager.dbReadWriteConnection readWriteWithBlock:^( YapDatabaseViewTransaction *interactionsByThread = [transaction ext:TSMessageDatabaseViewExtensionName];
YapDatabaseReadWriteTransaction *transaction) { NSInteger messageCount = (NSInteger)[interactionsByThread numberOfItemsInGroup:thread.uniqueId];
YapDatabaseViewTransaction *interactionsByThread = [transaction ext:TSMessageDatabaseViewExtensionName]; NSMutableArray<NSNumber *> *messageIndices = [NSMutableArray new];
NSInteger messageCount = (NSInteger) [interactionsByThread numberOfItemsInGroup:thread.uniqueId]; const NSInteger kRecentMessageCount = 10;
for (NSInteger i = 0; i < kRecentMessageCount; i++) {
NSMutableArray<NSNumber *> *messageIndices = [NSMutableArray new]; NSInteger messageIdx = messageCount - (1 + i);
const NSInteger kRecentMessageCount = 10; if (messageIdx >= 0) {
for (NSInteger i =0; i < kRecentMessageCount; i++) { [messageIndices addObject:@(messageIdx)];
NSInteger messageIdx = messageCount - (1 + i);
if (messageIdx >= 0) {
[messageIndices addObject:@(messageIdx)];
}
} }
NSMutableArray<TSInteraction *> *interactions = [NSMutableArray new]; }
for (NSUInteger i =0; i < count && messageIndices.count > 0; i++) { NSMutableArray<TSInteraction *> *interactions = [NSMutableArray new];
NSUInteger idx = (NSUInteger) arc4random_uniform((uint32_t) messageIndices.count); for (NSUInteger i = 0; i < count && messageIndices.count > 0; i++) {
NSNumber *messageIdx = messageIndices[idx]; NSUInteger idx = (NSUInteger)arc4random_uniform((uint32_t)messageIndices.count);
[messageIndices removeObjectAtIndex:idx]; NSNumber *messageIdx = messageIndices[idx];
[messageIndices removeObjectAtIndex:idx];
TSInteraction *_Nullable interaction =
TSInteraction *_Nullable interaction =
[interactionsByThread objectAtIndex:messageIdx.unsignedIntegerValue inGroup:thread.uniqueId]; [interactionsByThread objectAtIndex:messageIdx.unsignedIntegerValue inGroup:thread.uniqueId];
OWSAssert(interaction); OWSAssert(interaction);
[interactions addObject:interaction]; [interactions addObject:interaction];
} }
for (TSInteraction *interaction in interactions) { for (TSInteraction *interaction in interactions) {
[interaction removeWithTransaction:transaction]; [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); DDLogInfo(@"%@ insertAndDeleteNewOutgoingMessages: %zd", self.logTag, count);
@ -1299,28 +1325,28 @@ NS_ASSUME_NONNULL_BEGIN
for (NSUInteger i =0; i < count; i++) { for (NSUInteger i =0; i < count; i++) {
NSString *text = [self randomText]; NSString *text = [self randomText];
OWSDisappearingMessagesConfiguration *configuration = OWSDisappearingMessagesConfiguration *configuration =
[OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId]; [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId transaction:transaction];
TSOutgoingMessage *message = TSOutgoingMessage *message =
[[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
inThread:thread inThread:thread
messageBody:text messageBody:text
attachmentIds:[NSMutableArray new] attachmentIds:[NSMutableArray new]
expiresInSeconds:(configuration.isEnabled ? configuration.durationSeconds : 0)]; expiresInSeconds:(configuration.isEnabled ? configuration.durationSeconds : 0)];
DDLogError(@"%@ insertAndDeleteNewOutgoingMessages timestamp: %llu.", self.logTag, message.timestamp);
[messages addObject:message]; [messages addObject:message];
} }
[TSStorageManager.sharedManager.dbReadWriteConnection readWriteWithBlock:^( for (TSOutgoingMessage *message in messages) {
YapDatabaseReadWriteTransaction *transaction) { [message saveWithTransaction:transaction];
for (TSOutgoingMessage *message in messages) { }
[message saveWithTransaction:transaction]; for (TSOutgoingMessage *message in messages) {
} [message removeWithTransaction: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); DDLogInfo(@"%@ resurrectNewOutgoingMessages1.1: %zd", self.logTag, count);
@ -1328,22 +1354,22 @@ NS_ASSUME_NONNULL_BEGIN
for (NSUInteger i =0; i < count; i++) { for (NSUInteger i =0; i < count; i++) {
NSString *text = [self randomText]; NSString *text = [self randomText];
OWSDisappearingMessagesConfiguration *configuration = OWSDisappearingMessagesConfiguration *configuration =
[OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId]; [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId
transaction:initialTransaction];
TSOutgoingMessage *message = TSOutgoingMessage *message =
[[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
inThread:thread inThread:thread
messageBody:text messageBody:text
attachmentIds:[NSMutableArray new] attachmentIds:[NSMutableArray new]
expiresInSeconds:(configuration.isEnabled ? configuration.durationSeconds : 0)]; expiresInSeconds:(configuration.isEnabled ? configuration.durationSeconds : 0)];
DDLogError(@"%@ resurrectNewOutgoingMessages1 timestamp: %llu.", self.logTag, message.timestamp);
[messages addObject:message]; [messages addObject:message];
} }
[TSStorageManager.sharedManager.dbReadWriteConnection readWriteWithBlock:^( for (TSOutgoingMessage *message in messages) {
YapDatabaseReadWriteTransaction *transaction) { [message saveWithTransaction:initialTransaction];
for (TSOutgoingMessage *message in messages) { }
[message saveWithTransaction:transaction];
}
}];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 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); DDLogInfo(@"%@ resurrectNewOutgoingMessages1.2: %zd", self.logTag, count);
[TSStorageManager.sharedManager.dbReadWriteConnection [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); DDLogInfo(@"%@ resurrectNewOutgoingMessages2.1: %zd", self.logTag, count);
@ -1366,23 +1394,23 @@ NS_ASSUME_NONNULL_BEGIN
for (NSUInteger i =0; i < count; i++) { for (NSUInteger i =0; i < count; i++) {
NSString *text = [self randomText]; NSString *text = [self randomText];
OWSDisappearingMessagesConfiguration *configuration = OWSDisappearingMessagesConfiguration *configuration =
[OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId]; [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId
transaction:initialTransaction];
TSOutgoingMessage *message = TSOutgoingMessage *message =
[[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
inThread:thread inThread:thread
messageBody:text messageBody:text
attachmentIds:[NSMutableArray new] attachmentIds:[NSMutableArray new]
expiresInSeconds:(configuration.isEnabled ? configuration.durationSeconds : 0)]; expiresInSeconds:(configuration.isEnabled ? configuration.durationSeconds : 0)];
DDLogError(@"%@ resurrectNewOutgoingMessages2 timestamp: %llu.", self.logTag, message.timestamp);
[messages addObject:message]; [messages addObject:message];
} }
[TSStorageManager.sharedManager.dbReadWriteConnection readWriteWithBlock:^( for (TSOutgoingMessage *message in messages) {
YapDatabaseReadWriteTransaction *transaction) { [message updateWithMessageState:TSOutgoingMessageStateAttemptingOut transaction:initialTransaction];
for (TSOutgoingMessage *message in messages) { [message saveWithTransaction:initialTransaction];
[message updateWithMessageState:TSOutgoingMessageStateAttemptingOut transaction:transaction]; }
[message saveWithTransaction:transaction];
}
}];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 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); DDLogInfo(@"%@ resurrectNewOutgoingMessages2.2: %zd", self.logTag, count);
[TSStorageManager.sharedManager.dbReadWriteConnection [TSStorageManager.sharedManager.dbReadWriteConnection

View File

@ -4,8 +4,10 @@
#import "DataSource.h" #import "DataSource.h"
#import "TSAttachment.h" #import "TSAttachment.h"
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#endif #endif
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@ -51,11 +53,9 @@ NS_ASSUME_NONNULL_BEGIN
+ (void)deleteAttachments; + (void)deleteAttachments;
+ (NSString *)attachmentsFolder; + (NSString *)attachmentsFolder;
- (CGSize)imageSizeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction; - (CGSize)imageSize;
- (CGSize)imageSizeWithoutTransaction;
- (CGFloat)audioDurationSecondsWithTransaction:(YapDatabaseReadWriteTransaction *)transaction; - (CGFloat)audioDurationSeconds;
- (CGFloat)audioDurationSecondsWithoutTransaction;
@end @end

View File

@ -396,7 +396,7 @@ NS_ASSUME_NONNULL_BEGIN
} }
} }
- (CGSize)ensureCachedImageSizeWithTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction - (CGSize)imageSize
{ {
OWSAssert([NSThread isMainThread]); OWSAssert([NSThread isMainThread]);
@ -408,68 +408,48 @@ NS_ASSUME_NONNULL_BEGIN
self.cachedImageWidth = @(imageSize.width); self.cachedImageWidth = @(imageSize.width);
self.cachedImageHeight = @(imageSize.height); self.cachedImageHeight = @(imageSize.height);
void (^updateDataStore)() = ^(YapDatabaseReadWriteTransaction *transaction) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
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 {
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [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; 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 - (CGFloat)calculateAudioDurationSeconds
{ {
OWSAssert([NSThread isMainThread]); OWSAssert([NSThread isMainThread]);
OWSAssert([self isAudio]); OWSAssert([self isAudio]);
NSError *error; return 0;
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:self.mediaURL error:&error];
if (error && [error.domain isEqualToString:NSOSStatusErrorDomain] // NSError *error;
&& (error.code == kAudioFileInvalidFileError || error.code == kAudioFileStreamError_InvalidFile)) { // AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:self.mediaURL error:&error];
// Ignore "invalid audio file" errors. // if (error && [error.domain isEqualToString:NSOSStatusErrorDomain]
return 0.f; // && (error.code == kAudioFileInvalidFileError || error.code == kAudioFileStreamError_InvalidFile)) {
} // // Ignore "invalid audio file" errors.
if (!error) { // return 0.f;
return (CGFloat)[audioPlayer duration]; // }
} else { // if (!error) {
OWSFail(@"Could not find audio duration: %@", self.mediaURL); // return (CGFloat)[audioPlayer duration];
return 0; // } else {
} // OWSFail(@"Could not find audio duration: %@", self.mediaURL);
// return 0;
// }
} }
- (CGFloat)ensureCachedAudioDurationSecondsWithTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction - (CGFloat)audioDurationSeconds
{ {
OWSAssert([NSThread isMainThread]); OWSAssert([NSThread isMainThread]);
@ -480,46 +460,25 @@ NS_ASSUME_NONNULL_BEGIN
CGFloat audioDurationSeconds = [self calculateAudioDurationSeconds]; CGFloat audioDurationSeconds = [self calculateAudioDurationSeconds];
self.cachedAudioDurationSeconds = @(audioDurationSeconds); self.cachedAudioDurationSeconds = @(audioDurationSeconds);
void (^updateDataStore)() = ^(YapDatabaseReadWriteTransaction *transaction) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
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 {
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [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; 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 @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@ -363,7 +363,9 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
= (NSArray<TSOutgoingMessage *> *)[TSInteraction interactionsWithTimestamp:sentTimestamp = (NSArray<TSOutgoingMessage *> *)[TSInteraction interactionsWithTimestamp:sentTimestamp
ofClass:[TSOutgoingMessage class] ofClass:[TSOutgoingMessage class]
withTransaction:transaction]; 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) { if (messages.count > 0) {
// 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?