Merge branch 'charlesmchen/tapQuotedReplies'

pull/1/head
Matthew Chen 7 years ago
commit 4655c74320

@ -12,6 +12,7 @@ NS_ASSUME_NONNULL_BEGIN
@class TSInteraction;
@class TSMessage;
@class TSOutgoingMessage;
@class TSQuotedMessage;
@protocol ConversationViewCellDelegate <NSObject>
@ -26,6 +27,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)didTapFailedIncomingAttachment:(ConversationViewItem *)viewItem
attachmentPointer:(TSAttachmentPointer *)attachmentPointer;
- (void)didTapFailedOutgoingMessage:(TSOutgoingMessage *)message;
- (void)didTapQuotedMessage:(ConversationViewItem *)viewItem quotedMessage:(TSQuotedMessage *)quotedMessage;
- (void)didPanWithGestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer
viewItem:(ConversationViewItem *)conversationItem;

@ -515,7 +515,11 @@ NS_ASSUME_NONNULL_BEGIN
[self handleMediaTapGesture];
break;
case OWSMessageGestureLocation_QuotedReply:
// TODO:
if (self.message.quotedMessage) {
[self.delegate didTapQuotedMessage:self.viewItem quotedMessage:self.message.quotedMessage];
} else {
OWSFail(@"%@ Missing quoted message.", self.logTag)
}
break;
}
}

@ -90,6 +90,7 @@
#import <SignalServiceKit/TSQuotedMessage.h>
#import <SignalServiceKit/Threading.h>
#import <YapDatabase/YapDatabase.h>
#import <YapDatabase/YapDatabaseAutoView.h>
#import <YapDatabase/YapDatabaseViewChange.h>
#import <YapDatabase/YapDatabaseViewConnection.h>
@ -1511,32 +1512,37 @@ typedef enum : NSUInteger {
}
static const CGFloat kThreshold = 50.f;
if (self.collectionView.contentOffset.y < kThreshold) {
[self loadMoreMessages];
[self loadAnotherPageOfMessages];
}
}
- (void)loadMoreMessages
- (void)loadAnotherPageOfMessages
{
BOOL hasEarlierUnseenMessages = self.dynamicInteractions.hasMoreUnseenMessages;
[self loadNMoreMessages:kYapDatabasePageSize];
// Dont auto-scroll afterloading more messagesunless we havemore unseen messages.
//
// Otherwise, tapping on "load more messages" autoscrolls you downward which is completely wrong.
if (hasEarlierUnseenMessages) {
[self scrollToUnreadIndicatorAnimated];
}
}
- (void)loadNMoreMessages:(NSUInteger)numberOfMessagesToLoad
{
// We want to restore the current scroll state after we update the range, update
// the dynamic interactions and re-layout. Here we take a "before" snapshot.
CGFloat scrollDistanceToBottom = self.safeContentHeight - self.collectionView.contentOffset.y;
self.lastRangeLength = MIN(self.lastRangeLength + kYapDatabasePageSize, (NSUInteger) kYapDatabaseRangeMaxLength);
self.lastRangeLength = MIN(self.lastRangeLength + numberOfMessagesToLoad, (NSUInteger)kYapDatabaseRangeMaxLength);
[self resetMappings];
[self.layout prepareLayout];
self.collectionView.contentOffset = CGPointMake(0, self.safeContentHeight - scrollDistanceToBottom);
// Dont auto-scroll afterloading more messagesunless we havemore unseen messages.
//
// Otherwise, tapping on "load more messages" autoscrolls you downward which is completely wrong.
if (hasEarlierUnseenMessages) {
[self scrollToUnreadIndicatorAnimated];
}
}
- (void)updateShowLoadMoreHeader
@ -2124,6 +2130,136 @@ typedef enum : NSUInteger {
[self handleUnsentMessageTap:message];
}
- (void)didTapQuotedMessage:(ConversationViewItem *)viewItem quotedMessage:(TSQuotedMessage *)quotedMessage
{
OWSAssertIsOnMainThread();
OWSAssert(viewItem);
OWSAssert(quotedMessage);
OWSAssert(quotedMessage.timestamp > 0);
OWSAssert(quotedMessage.authorId.length > 0);
// We try to find the index of the item within the current thread's
// interactions that includes the "quoted interaction".
//
// NOTE: There are two indices:
//
// * The "group index" of the member of the database views group at
// the db conneciton's current checkpoint.
// * The "index row/section" in the message mapping.
//
// NOTE: Since the range _IS NOT_ filtered by author,
// and timestamp collisions are possible, it's possible
// for:
//
// * The range to include more than the "quoted interaction".
// * The range to be non-empty but NOT include the "quoted interaction",
// although this would be a bug.
__block TSInteraction *_Nullable quotedInteraction;
__block NSUInteger threadInteractionCount = 0;
__block NSNumber *_Nullable groupIndex = nil;
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
quotedInteraction = [ThreadUtil findInteractionInThreadByTimestamp:quotedMessage.timestamp
authorId:quotedMessage.authorId
threadUniqueId:self.thread.uniqueId
transaction:transaction];
if (!quotedInteraction) {
return;
}
YapDatabaseAutoViewTransaction *_Nullable extension =
[transaction extension:TSMessageDatabaseViewExtensionName];
if (!extension) {
OWSFail(@"%@ Couldn't load view.", self.logTag);
return;
}
threadInteractionCount = [extension numberOfItemsInGroup:self.thread.uniqueId];
groupIndex = [self findGroupIndexOfThreadInteraction:quotedInteraction transaction:transaction];
}];
if (!quotedInteraction || !groupIndex) {
DDLogError(@"%@ Couldn't find message quoted in quoted reply.", self.logTag);
return;
}
NSUInteger indexRow = 0;
NSUInteger indexSection = 0;
BOOL isInMappings = [self.messageMappings getRow:&indexRow
section:&indexSection
forIndex:groupIndex.unsignedIntegerValue
inGroup:self.thread.uniqueId];
if (!isInMappings) {
NSInteger desiredWindowSize = MAX(0, 1 + (NSInteger)threadInteractionCount - groupIndex.integerValue);
NSUInteger oldLoadWindowSize = [self.messageMappings numberOfItemsInGroup:self.thread.uniqueId];
NSInteger additionalItemsToLoad = MAX(0, desiredWindowSize - (NSInteger)oldLoadWindowSize);
if (additionalItemsToLoad < 1) {
DDLogError(@"%@ Couldn't determine how to load quoted reply.", self.logTag);
return;
}
// Try to load more messages so that the quoted message
// is in the load window.
//
// This may fail if the quoted message is very old, in which
// case we'll load the max number of messages.
[self loadNMoreMessages:(NSUInteger)additionalItemsToLoad];
// `loadNMoreMessages` will reset the mapping and possibly
// integrate new changes, so we need to reload the "group index"
// of the quoted message.
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
groupIndex = [self findGroupIndexOfThreadInteraction:quotedInteraction transaction:transaction];
}];
if (!quotedInteraction || !groupIndex) {
DDLogError(@"%@ Failed to find quoted reply in group.", self.logTag);
return;
}
isInMappings = [self.messageMappings getRow:&indexRow
section:&indexSection
forIndex:groupIndex.unsignedIntegerValue
inGroup:self.thread.uniqueId];
if (!isInMappings) {
DDLogError(@"%@ Could not load quoted reply into mapping.", self.logTag);
return;
}
}
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:(NSInteger)indexRow inSection:(NSInteger)indexSection];
[self.collectionView scrollToItemAtIndexPath:indexPath
atScrollPosition:UICollectionViewScrollPositionTop
animated:YES];
// TODO: Highlight the quoted message?
}
- (nullable NSNumber *)findGroupIndexOfThreadInteraction:(TSInteraction *)interaction
transaction:(YapDatabaseReadTransaction *)transaction
{
OWSAssert(interaction);
OWSAssert(transaction);
YapDatabaseAutoViewTransaction *_Nullable extension = [transaction extension:TSMessageDatabaseViewExtensionName];
if (!extension) {
OWSFail(@"%@ Couldn't load view.", self.logTag);
return nil;
}
NSUInteger groupIndex = 0;
BOOL foundInGroup =
[extension getGroup:nil index:&groupIndex forKey:interaction.uniqueId inCollection:TSInteraction.collection];
if (!foundInGroup) {
DDLogError(@"%@ Couldn't find quoted message in group.", self.logTag);
return nil;
}
return @(groupIndex);
}
- (void)showMetadataViewForViewItem:(ConversationViewItem *)conversationItem
{
OWSAssertIsOnMainThread();

@ -12,6 +12,7 @@ NS_ASSUME_NONNULL_BEGIN
@class TSInteraction;
@class TSThread;
@class YapDatabaseConnection;
@class YapDatabaseReadTransaction;
@interface ThreadDynamicInteractions : NSObject
@ -113,6 +114,13 @@ NS_ASSUME_NONNULL_BEGIN
+ (void)deleteAllContent;
#pragma mark - Find Content
+ (nullable TSInteraction *)findInteractionInThreadByTimestamp:(uint64_t)timestamp
authorId:(NSString *)authorId
threadUniqueId:(NSString *)threadUniqueId
transaction:(YapDatabaseReadTransaction *)transaction;
@end
NS_ASSUME_NONNULL_END

@ -668,6 +668,55 @@ NS_ASSUME_NONNULL_BEGIN
DDLogInfo(@"%@ Deleted %zd/%zd objects from: %@", self.logTag, count, uniqueIds.count, collection);
}
#pragma mark - Find Content
+ (nullable TSInteraction *)findInteractionInThreadByTimestamp:(uint64_t)timestamp
authorId:(NSString *)authorId
threadUniqueId:(NSString *)threadUniqueId
transaction:(YapDatabaseReadTransaction *)transaction
{
OWSAssert(timestamp > 0);
OWSAssert(authorId.length > 0);
NSString *localNumber = [TSAccountManager localNumber];
if (localNumber.length < 1) {
OWSFail(@"%@ missing long number.", self.logTag);
return nil;
}
NSArray<TSInteraction *> *interactions =
[TSInteraction interactionsWithTimestamp:timestamp
filter:^(TSInteraction *interaction) {
NSString *_Nullable messageAuthorId = nil;
if ([interaction isKindOfClass:[TSIncomingMessage class]]) {
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)interaction;
messageAuthorId = incomingMessage.authorId;
} else if ([interaction isKindOfClass:[TSOutgoingMessage class]]) {
messageAuthorId = localNumber;
}
if (messageAuthorId.length < 1) {
return NO;
}
if (![authorId isEqualToString:messageAuthorId]) {
return NO;
}
if (![interaction.uniqueThreadId isEqualToString:threadUniqueId]) {
return NO;
}
return YES;
}
withTransaction:transaction];
if (interactions.count < 1) {
return nil;
}
if (interactions.count > 1) {
// In case of collision, take the first.
DDLogError(@"%@ more than one matching interaction in thread.", self.logTag);
}
return interactions.firstObject;
}
@end
NS_ASSUME_NONNULL_END

@ -41,11 +41,11 @@ typedef NS_ENUM(NSInteger, OWSInteractionType) {
+ (NSArray<TSInteraction *> *)interactionsWithTimestamp:(uint64_t)timestamp
ofClass:(Class)clazz
withTransaction:(YapDatabaseReadWriteTransaction *)transaction;
withTransaction:(YapDatabaseReadTransaction *)transaction;
+ (NSArray<TSInteraction *> *)interactionsWithTimestamp:(uint64_t)timestamp
filter:(BOOL (^_Nonnull)(TSInteraction *))filter
withTransaction:(YapDatabaseReadWriteTransaction *)transaction;
withTransaction:(YapDatabaseReadTransaction *)transaction;
- (NSDate *)dateForSorting;
- (uint64_t)timestampForSorting;

@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (NSArray<TSInteraction *> *)interactionsWithTimestamp:(uint64_t)timestamp
ofClass:(Class)clazz
withTransaction:(YapDatabaseReadWriteTransaction *)transaction
withTransaction:(YapDatabaseReadTransaction *)transaction
{
OWSAssert(timestamp > 0);
@ -28,7 +28,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (NSArray<TSInteraction *> *)interactionsWithTimestamp:(uint64_t)timestamp
filter:(BOOL (^_Nonnull)(TSInteraction *))filter
withTransaction:(YapDatabaseReadWriteTransaction *)transaction
withTransaction:(YapDatabaseReadTransaction *)transaction
{
OWSAssert(timestamp > 0);

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import <YapDatabase/YapDatabaseSecondaryIndex.h>
@ -11,6 +11,6 @@
+ (void)enumerateMessagesWithTimestamp:(uint64_t)timestamp
withBlock:(void (^)(NSString *collection, NSString *key, BOOL *stop))block
usingTransaction:(YapDatabaseReadWriteTransaction *)transaction;
usingTransaction:(YapDatabaseReadTransaction *)transaction;
@end

@ -1,9 +1,8 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "TSDatabaseSecondaryIndexes.h"
#import "TSInteraction.h"
#define TSTimeStampSQLiteIndex @"messagesTimeStamp"
@ -34,7 +33,8 @@
+ (void)enumerateMessagesWithTimestamp:(uint64_t)timestamp
withBlock:(void (^)(NSString *collection, NSString *key, BOOL *stop))block
usingTransaction:(YapDatabaseReadWriteTransaction *)transaction {
usingTransaction:(YapDatabaseReadTransaction *)transaction
{
NSString *formattedString = [NSString stringWithFormat:@"WHERE %@ = %lld", TSTimeStampSQLiteIndex, timestamp];
YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:formattedString];
[[transaction ext:@"idx"] enumerateKeysMatchingQuery:query usingBlock:block];

Loading…
Cancel
Save