Merge branch 'charlesmchen/conversationUpdateEdgeCases_'

pull/1/head
Matthew Chen 8 years ago
commit 72fba87746

@ -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 > 0.f);
return self.viewItem.audioDurationSeconds;
}
- (AudioPlaybackState)audioPlaybackState

@ -2883,26 +2883,29 @@ 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) {
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 +2916,24 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
break;
}
case YapDatabaseViewChangeMove: {
DDLogVerbose(@"YapDatabaseViewChangeMove: %@, %@, %@",
DDLogVerbose(@"YapDatabaseViewChangeMove: %@, %@, %@, %zd",
rowChange.collectionKey,
rowChange.indexPath,
rowChange.newIndexPath);
[self.collectionView deleteItemsAtIndexPaths:@[ rowChange.indexPath ]];
[self.collectionView insertItemsAtIndexPaths:@[ rowChange.newIndexPath ]];
rowChange.newIndexPath,
rowChange.finalIndex);
[self.collectionView moveItemAtIndexPath:rowChange.indexPath toIndexPath: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;
}
}
@ -2937,33 +2943,63 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
// as they may affect which cells show "date" headers or "status" footers.
NSMutableArray<NSIndexPath *> *rowsToReload = [NSMutableArray new];
for (NSNumber *row in rowsThatChangedSize) {
DDLogVerbose(@"rowsToReload: %@", row);
[rowsToReload addObject:[NSIndexPath indexPathForRow:row.integerValue inSection:0]];
}
if (rowsToReload.count > 0) {
[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);
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<YapDatabaseViewRowChange *> *)rowChanges
{
OWSAssert(rowChanges);
for (YapDatabaseViewRowChange *rowChange in rowChanges) {
switch (rowChange.type) {
case YapDatabaseViewChangeMove:
// "Move" changes cannot be safely performed using
// [UICollectionView performBatchUpdates:]. This appears to be a
// bug in YapDatabase.
return YES;
default:
break;
}
}
return NO;
}
- (BOOL)shouldAnimateRowUpdates:(NSArray<YapDatabaseViewRowChange *> *)rowChanges
@ -2987,13 +3023,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 +3038,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;
@ -3316,20 +3352,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;
}

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

@ -47,6 +47,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
@property (nonatomic) AudioPlaybackState audioPlaybackState;
@property (nonatomic) CGFloat audioProgressSeconds;
@property (nonatomic) CGFloat 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;

@ -40,6 +40,14 @@ NS_ASSUME_NONNULL_BEGIN
OWSAssert(thread);
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.)"
actionBlock:^{
[DebugUIMessages sendTextMessages:10 thread:thread];
@ -241,10 +249,6 @@ NS_ASSUME_NONNULL_BEGIN
actionBlock:^{
[DebugUIMessages injectFakeIncomingMessages:1000 thread:thread];
}],
[OWSTableItem itemWithTitle:@"Perform 100 random actions"
actionBlock:^{
[DebugUIMessages performRandomActions:100 thread:thread];
}],
] mutableCopy];
if ([thread isKindOfClass:[TSContactThread class]]) {
TSContactThread *contactThread = (TSContactThread *)thread;
@ -267,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
@ -935,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
@ -1095,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];
@ -1150,144 +1173,151 @@ NS_ASSUME_NONNULL_BEGIN
+ (void)performRandomActionInThread:(TSThread *)thread
counter:(int)counter
{
typedef void (^ActionBlock)(void);
typedef void (^ActionBlock)(YapDatabaseReadWriteTransaction *transaction);
NSArray<ActionBlock> *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<NSNumber *> *messageIndices = [NSMutableArray new];
for (NSUInteger messageIdx =0; messageIdx < messageCount; messageIdx++) {
[messageIndices addObject:@(messageIdx)];
}
NSMutableArray<TSInteraction *> *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<NSNumber *> *messageIndices = [NSMutableArray new];
for (NSUInteger messageIdx = 0; messageIdx < messageCount; messageIdx++) {
[messageIndices addObject:@(messageIdx)];
}
NSMutableArray<TSInteraction *> *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<NSNumber *> *messageIndices = [NSMutableArray new];
for (NSUInteger i = 0; i < count && i < messageCount; i++) {
NSUInteger messageIdx = messageCount - (1 + i);
[messageIndices addObject:@(messageIdx)];
}
NSMutableArray<TSInteraction *> *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<NSNumber *> *messageIndices = [NSMutableArray new];
for (NSUInteger i = 0; i < count && i < messageCount; i++) {
NSUInteger messageIdx = messageCount - (1 + i);
[messageIndices addObject:@(messageIdx)];
}
NSMutableArray<TSInteraction *> *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<NSNumber *> *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<NSNumber *> *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<TSInteraction *> *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<TSInteraction *> *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);
@ -1295,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);
@ -1324,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
@ -1354,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);
@ -1362,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

@ -4,8 +4,10 @@
#import "DataSource.h"
#import "TSAttachment.h"
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#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

@ -396,7 +396,7 @@ NS_ASSUME_NONNULL_BEGIN
}
}
- (CGSize)ensureCachedImageSizeWithTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction
- (CGSize)imageSize
{
OWSAssert([NSThread isMainThread]);
@ -408,8 +408,7 @@ NS_ASSUME_NONNULL_BEGIN
self.cachedImageWidth = @(imageSize.width);
self.cachedImageHeight = @(imageSize.height);
void (^updateDataStore)() = ^(YapDatabaseReadWriteTransaction *transaction) {
OWSAssert(transaction);
[self.dbReadWriteConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
NSString *collection = [[self class] collection];
TSAttachmentStream *latestInstance = [transaction objectForKey:self.uniqueId inCollection:collection];
@ -418,37 +417,16 @@ 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);
}
};
if (transaction) {
updateDataStore(transaction);
} else {
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
updateDataStore(transaction);
}];
}
}];
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]);
@ -469,7 +447,7 @@ NS_ASSUME_NONNULL_BEGIN
}
}
- (CGFloat)ensureCachedAudioDurationSecondsWithTransaction:(YapDatabaseReadWriteTransaction *_Nullable)transaction
- (CGFloat)audioDurationSeconds
{
OWSAssert([NSThread isMainThread]);
@ -480,46 +458,23 @@ NS_ASSUME_NONNULL_BEGIN
CGFloat audioDurationSeconds = [self calculateAudioDurationSeconds];
self.cachedAudioDurationSeconds = @(audioDurationSeconds);
void (^updateDataStore)() = ^(YapDatabaseReadWriteTransaction *transaction) {
OWSAssert(transaction);
[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; 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);
}
};
if (transaction) {
updateDataStore(transaction);
} else {
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
updateDataStore(transaction);
}];
}
}];
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

@ -363,7 +363,9 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
= (NSArray<TSOutgoingMessage *> *)[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?

Loading…
Cancel
Save