“Add to contacts” offer.

// FREEBIE
pull/1/head
Matthew Chen 8 years ago
parent 7b70fe674a
commit bc63389d23

@ -646,6 +646,9 @@ typedef enum : NSUInteger {
@property (nonatomic) NSTimer *scrollLaterTimer; @property (nonatomic) NSTimer *scrollLaterTimer;
@property (nonatomic, readonly) ContactsViewHelper *contactsViewHelper; @property (nonatomic, readonly) ContactsViewHelper *contactsViewHelper;
@property (nonatomic, nullable) ThreadOffersAndIndicators *offersAndIndicators;
@property (nonatomic) BOOL hasClearedUnreadMessagesIndicator;
@end @end
@ -744,8 +747,6 @@ typedef enum : NSUInteger {
_composeOnOpen = keyboardOnViewAppearing; _composeOnOpen = keyboardOnViewAppearing;
_callOnOpen = callOnViewAppearing; _callOnOpen = callOnViewAppearing;
[ThreadUtil createUnreadMessagesIndicatorIfNecessary:thread storageManager:self.storageManager];
[self markAllMessagesAsRead]; [self markAllMessagesAsRead];
[self.uiDatabaseConnection beginLongLivedReadTransaction]; [self.uiDatabaseConnection beginLongLivedReadTransaction];
@ -1000,11 +1001,6 @@ typedef enum : NSUInteger {
repeats:NO]; repeats:NO];
} }
- (void)clearUnreadMessagesIndicator
{
[ThreadUtil clearUnreadMessagesIndicator:self.thread storageManager:self.storageManager];
}
- (NSIndexPath *_Nullable)indexPathOfUnreadMessagesIndicator - (NSIndexPath *_Nullable)indexPathOfUnreadMessagesIndicator
{ {
int numberOfMessages = (int)[self.messageMappings numberOfItemsInGroup:self.thread.uniqueId]; int numberOfMessages = (int)[self.messageMappings numberOfItemsInGroup:self.thread.uniqueId];
@ -1625,6 +1621,7 @@ typedef enum : NSUInteger {
} else { } else {
[ThreadUtil sendMessageWithText:text inThread:self.thread messageSender:self.messageSender]; [ThreadUtil sendMessageWithText:text inThread:self.thread messageSender:self.messageSender];
} }
self.lastMessageSentDate = [NSDate new]; self.lastMessageSentDate = [NSDate new];
[self clearUnreadMessagesIndicator]; [self clearUnreadMessagesIndicator];
@ -2739,13 +2736,33 @@ typedef enum : NSUInteger {
{ {
OWSAssert([NSThread isMainThread]); OWSAssert([NSThread isMainThread]);
if ([self.thread isKindOfClass:[TSContactThread class]]) { self.offersAndIndicators =
TSContactThread *contactThread = (TSContactThread *)self.thread; [ThreadUtil ensureThreadOffersAndIndicators:self.thread
[ThreadUtil ensureThreadOffersAndIndicators:contactThread
storageManager:self.storageManager storageManager:self.storageManager
contactsManager:self.contactsManager contactsManager:self.contactsManager
blockingManager:self.blockingManager]; blockingManager:self.blockingManager
hideUnreadMessagesIndicator:self.hasClearedUnreadMessagesIndicator
fixedUnreadIndicatorTimestamp:(self.offersAndIndicators.unreadIndicator
? @(self.offersAndIndicators.unreadIndicator.timestamp)
: nil)];
}
- (void)clearUnreadMessagesIndicator
{
OWSAssert([NSThread isMainThread]);
if (self.hasClearedUnreadMessagesIndicator) {
// ensureThreadOffersAndIndicators is slightly
// expensive, so make sure we don't call it
// unneccesarily.
return;
} }
// Once we've cleared the unread messages indicator,
// make sure we don't show it again.
self.hasClearedUnreadMessagesIndicator = YES;
[self ensureThreadOffersAndIndicators];
} }
#pragma mark - Attachment Picking: Documents #pragma mark - Attachment Picking: Documents

@ -12,6 +12,16 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@class TSUnreadIndicatorInteraction;
@interface ThreadOffersAndIndicators : NSObject
@property (nonatomic, nullable) TSUnreadIndicatorInteraction *unreadIndicator;
@end
#pragma mark -
@interface ThreadUtil : NSObject @interface ThreadUtil : NSObject
+ (void)sendMessageWithText:(NSString *)text + (void)sendMessageWithText:(NSString *)text
@ -22,13 +32,18 @@ NS_ASSUME_NONNULL_BEGIN
inThread:(TSThread *)thread inThread:(TSThread *)thread
messageSender:(OWSMessageSender *)messageSender; messageSender:(OWSMessageSender *)messageSender;
+ (void)ensureThreadOffersAndIndicators:(TSContactThread *)contactThread // This method will create and/or remove any offers and indicators
storageManager:(TSStorageManager *)storageManager // necessary for this thread.
contactsManager:(OWSContactsManager *)contactsManager //
blockingManager:(OWSBlockingManager *)blockingManager; // * If hideUnreadMessagesIndicator is YES, there will be no "unread indicator".
// * Otherwise, if fixedUnreadIndicatorTimestamp is non-null, there will be a "unread indicator".
+ (void)createUnreadMessagesIndicatorIfNecessary:(TSThread *)thread storageManager:(TSStorageManager *)storageManager; // * Otherwise, there will be a "unread indicator" if there is one unread message.
+ (void)clearUnreadMessagesIndicator:(TSThread *)thread storageManager:(TSStorageManager *)storageManager; + (ThreadOffersAndIndicators *)ensureThreadOffersAndIndicators:(TSThread *)thread
storageManager:(TSStorageManager *)storageManager
contactsManager:(OWSContactsManager *)contactsManager
blockingManager:(OWSBlockingManager *)blockingManager
hideUnreadMessagesIndicator:(BOOL)hideUnreadMessagesIndicator
fixedUnreadIndicatorTimestamp:(NSNumber *_Nullable)fixedUnreadIndicatorTimestamp;
@end @end

@ -18,6 +18,12 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@implementation ThreadOffersAndIndicators
@end
#pragma mark -
@implementation ThreadUtil @implementation ThreadUtil
+ (void)sendMessageWithText:(NSString *)text inThread:(TSThread *)thread messageSender:(OWSMessageSender *)messageSender + (void)sendMessageWithText:(NSString *)text inThread:(TSThread *)thread messageSender:(OWSMessageSender *)messageSender
@ -75,37 +81,45 @@ NS_ASSUME_NONNULL_BEGIN
}]; }];
} }
+ (void)ensureThreadOffersAndIndicators:(TSContactThread *)contactThread + (ThreadOffersAndIndicators *)ensureThreadOffersAndIndicators:(TSThread *)thread
storageManager:(TSStorageManager *)storageManager storageManager:(TSStorageManager *)storageManager
contactsManager:(OWSContactsManager *)contactsManager contactsManager:(OWSContactsManager *)contactsManager
blockingManager:(OWSBlockingManager *)blockingManager blockingManager:(OWSBlockingManager *)blockingManager
hideUnreadMessagesIndicator:(BOOL)hideUnreadMessagesIndicator
fixedUnreadIndicatorTimestamp:(NSNumber *_Nullable)fixedUnreadIndicatorTimestamp
{ {
OWSAssert(contactThread); OWSAssert(thread);
OWSAssert(storageManager); OWSAssert(storageManager);
OWSAssert(contactsManager); OWSAssert(contactsManager);
OWSAssert(blockingManager); OWSAssert(blockingManager);
ThreadOffersAndIndicators *result = [ThreadOffersAndIndicators new];
[storageManager.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [storageManager.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
const int kMaxBlockOfferOutgoingMessageCount = 10; const int kMaxBlockOfferOutgoingMessageCount = 10;
__block OWSAddToContactsOfferMessage *addToContactsOffer = nil; __block OWSAddToContactsOfferMessage *addToContactsOffer = nil;
__block OWSUnknownContactBlockOfferMessage *blockOffer = nil; __block OWSUnknownContactBlockOfferMessage *blockOffer = nil;
__block TSUnreadIndicatorInteraction *unreadIndicator = nil;
__block TSIncomingMessage *firstIncomingMessage = nil; __block TSIncomingMessage *firstIncomingMessage = nil;
__block TSOutgoingMessage *firstOutgoingMessage = nil; __block TSOutgoingMessage *firstOutgoingMessage = nil;
__block TSMessage *firstUnreadMessage = nil;
__block long outgoingMessageCount = 0; __block long outgoingMessageCount = 0;
[[transaction ext:TSMessageDatabaseViewExtensionName] [[transaction ext:TSMessageDatabaseViewExtensionName]
enumerateRowsInGroup:contactThread.uniqueId enumerateRowsInGroup:thread.uniqueId
usingBlock:^( usingBlock:^(
NSString *collection, NSString *key, id object, id metadata, NSUInteger index, BOOL *stop) { NSString *collection, NSString *key, id object, id metadata, NSUInteger index, BOOL *stop) {
if ([object isKindOfClass:[OWSUnknownContactBlockOfferMessage class]]) { if ([object isKindOfClass:[OWSUnknownContactBlockOfferMessage class]]) {
OWSAssert(!blockOffer); OWSAssert(!blockOffer);
blockOffer = (OWSUnknownContactBlockOfferMessage *)object; blockOffer = (OWSUnknownContactBlockOfferMessage *)object;
} else if ([object isKindOfClass:[OWSAddToContactsOfferMessage class]]) { } else if ([object isKindOfClass:[OWSAddToContactsOfferMessage class]]) {
OWSAssert(!addToContactsOffer); OWSAssert(!addToContactsOffer);
addToContactsOffer = (OWSAddToContactsOfferMessage *)object; addToContactsOffer = (OWSAddToContactsOfferMessage *)object;
} else if ([object isKindOfClass:[TSUnreadIndicatorInteraction class]]) {
OWSAssert(!unreadIndicator);
unreadIndicator = (TSUnreadIndicatorInteraction *)object;
} else if ([object isKindOfClass:[TSIncomingMessage class]]) { } else if ([object isKindOfClass:[TSIncomingMessage class]]) {
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)object; TSIncomingMessage *incomingMessage = (TSIncomingMessage *)object;
if (!firstIncomingMessage) { if (!firstIncomingMessage) {
@ -115,6 +129,16 @@ NS_ASSUME_NONNULL_BEGIN
compare:[incomingMessage receiptDateForSorting]] compare:[incomingMessage receiptDateForSorting]]
== NSOrderedAscending); == NSOrderedAscending);
} }
if (!incomingMessage.wasRead) {
if (!firstUnreadMessage) {
firstUnreadMessage = incomingMessage;
} else {
OWSAssert([[firstUnreadMessage receiptDateForSorting]
compare:[incomingMessage receiptDateForSorting]]
== NSOrderedAscending);
}
}
} else if ([object isKindOfClass:[TSOutgoingMessage class]]) { } else if ([object isKindOfClass:[TSOutgoingMessage class]]) {
TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)object; TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)object;
if (!firstOutgoingMessage) { if (!firstOutgoingMessage) {
@ -138,19 +162,30 @@ NS_ASSUME_NONNULL_BEGIN
BOOL shouldHaveBlockOffer = YES; BOOL shouldHaveBlockOffer = YES;
BOOL shouldHaveAddToContactsOffer = YES; BOOL shouldHaveAddToContactsOffer = YES;
if ([[blockingManager blockedPhoneNumbers] containsObject:contactThread.contactIdentifier]) {
// Only create offers for users which are not already blocked.
shouldHaveAddToContactsOffer = NO;
// Only create block offers for users which are not already blocked.
shouldHaveBlockOffer = NO;
}
SignalAccount *signalAccount = contactsManager.signalAccountMap[contactThread.contactIdentifier]; BOOL isContactThread = [thread isKindOfClass:[TSContactThread class]];
if (signalAccount) { if (!isContactThread) {
// Only create offers for non-contacts. // Only create "add to contacts" offers in 1:1 conversations.
shouldHaveAddToContactsOffer = NO; shouldHaveAddToContactsOffer = NO;
// Only create block offers for non-contacts. // Only create block offers in 1:1 conversations.
shouldHaveBlockOffer = NO; shouldHaveBlockOffer = NO;
} else {
NSString *recipientId = ((TSContactThread *)thread).contactIdentifier;
if ([[blockingManager blockedPhoneNumbers] containsObject:recipientId]) {
// Only create "add to contacts" offers for users which are not already blocked.
shouldHaveAddToContactsOffer = NO;
// Only create block offers for users which are not already blocked.
shouldHaveBlockOffer = NO;
}
SignalAccount *signalAccount = contactsManager.signalAccountMap[recipientId];
if (signalAccount) {
// Only create "add to contacts" offers for non-contacts.
shouldHaveAddToContactsOffer = NO;
// Only create block offers for non-contacts.
shouldHaveBlockOffer = NO;
}
} }
if (!firstMessage) { if (!firstMessage) {
@ -176,8 +211,7 @@ NS_ASSUME_NONNULL_BEGIN
// We use these offset to control the ordering of the offers and indicators. // We use these offset to control the ordering of the offers and indicators.
const int kBlockOfferOffset = -3; const int kBlockOfferOffset = -3;
const int kAddToContactsOfferOffset = -2; const int kAddToContactsOfferOffset = -2;
// TODO: const int kUnreadIndicatorOfferOffset = -1;
// const int kUnseenIndicatorOfferOffset = -1;
if (blockOffer && !shouldHaveBlockOffer) { if (blockOffer && !shouldHaveBlockOffer) {
[blockOffer removeWithTransaction:transaction]; [blockOffer removeWithTransaction:transaction];
@ -188,11 +222,12 @@ NS_ASSUME_NONNULL_BEGIN
// conversation's timeline, so we back-date it to slightly before // conversation's timeline, so we back-date it to slightly before
// the first incoming message (which we know is the first message). // the first incoming message (which we know is the first message).
uint64_t blockOfferTimestamp = (uint64_t)((long long)firstMessage.timestamp + kBlockOfferOffset); uint64_t blockOfferTimestamp = (uint64_t)((long long)firstMessage.timestamp + kBlockOfferOffset);
NSString *recipientId = ((TSContactThread *)thread).contactIdentifier;
TSMessage *offerMessage = TSMessage *offerMessage =
[OWSUnknownContactBlockOfferMessage unknownContactBlockOfferMessage:blockOfferTimestamp [OWSUnknownContactBlockOfferMessage unknownContactBlockOfferMessage:blockOfferTimestamp
thread:contactThread thread:thread
contactId:contactThread.contactIdentifier]; contactId:recipientId];
[offerMessage saveWithTransaction:transaction]; [offerMessage saveWithTransaction:transaction];
} }
@ -206,89 +241,50 @@ NS_ASSUME_NONNULL_BEGIN
// conversation's timeline, so we back-date it to slightly before // conversation's timeline, so we back-date it to slightly before
// the first incoming message (which we know is the first message). // the first incoming message (which we know is the first message).
uint64_t offerTimestamp = (uint64_t)((long long)firstMessage.timestamp + kAddToContactsOfferOffset); uint64_t offerTimestamp = (uint64_t)((long long)firstMessage.timestamp + kAddToContactsOfferOffset);
NSString *recipientId = ((TSContactThread *)thread).contactIdentifier;
TSMessage *offerMessage = TSMessage *offerMessage = [OWSAddToContactsOfferMessage addToContactsOfferMessage:offerTimestamp
[OWSAddToContactsOfferMessage addToContactsOfferMessage:offerTimestamp thread:thread
thread:contactThread contactId:recipientId];
contactId:contactThread.contactIdentifier];
[offerMessage saveWithTransaction:transaction]; [offerMessage saveWithTransaction:transaction];
} }
}];
}
+ (void)createUnreadMessagesIndicatorIfNecessary:(TSThread *)thread storageManager:(TSStorageManager *)storageManager
{
OWSAssert(thread);
OWSAssert(storageManager);
[storageManager.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
NSMutableArray *indicators = [NSMutableArray new];
__block TSMessage *firstUnreadMessage = nil;
[[transaction ext:TSMessageDatabaseViewExtensionName]
enumerateRowsInGroup:thread.uniqueId
usingBlock:^(
NSString *collection, NSString *key, id object, id metadata, NSUInteger index, BOOL *stop) {
if ([object isKindOfClass:[TSUnreadIndicatorInteraction class]]) { BOOL shouldHaveUnreadIndicator
[indicators addObject:object]; = ((firstUnreadMessage != nil || fixedUnreadIndicatorTimestamp != nil) && !hideUnreadMessagesIndicator);
} else if ([object isKindOfClass:[TSIncomingMessage class]]) { if (!shouldHaveUnreadIndicator) {
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)object; if (unreadIndicator) {
if (!incomingMessage.wasRead) { [unreadIndicator removeWithTransaction:transaction];
if (!firstUnreadMessage) { }
firstUnreadMessage = incomingMessage; } else {
} else { // We want the block offer to appear just before the first unread incoming
OWSAssert([[firstUnreadMessage receiptDateForSorting] // message in the conversation timeline...
compare:[incomingMessage receiptDateForSorting]] //
== NSOrderedAscending); // ...unless we have a fixed timestamp for the unread indicator.
} uint64_t indicatorTimestamp = (uint64_t)(fixedUnreadIndicatorTimestamp
} ? [fixedUnreadIndicatorTimestamp longLongValue]
} : ((long long)firstUnreadMessage.timestamp + kUnreadIndicatorOfferOffset));
}];
if (indicatorTimestamp && unreadIndicator.timestamp == indicatorTimestamp) {
for (TSUnreadIndicatorInteraction *indicator in indicators) { // Keep the existing indicator; it is in the correct position.
[indicator removeWithTransaction:transaction];
result.unreadIndicator = unreadIndicator;
} else {
if (unreadIndicator) {
[unreadIndicator removeWithTransaction:transaction];
}
DDLogInfo(@"%@ Creating TSUnreadIndicatorInteraction", self.tag);
TSUnreadIndicatorInteraction *indicator =
[[TSUnreadIndicatorInteraction alloc] initWithTimestamp:indicatorTimestamp thread:thread];
[indicator saveWithTransaction:transaction];
result.unreadIndicator = indicator;
}
} }
BOOL shouldHaveIndicator = firstUnreadMessage != nil;
if (!shouldHaveIndicator) {
return;
}
DDLogInfo(@"%@ Creating TSUnreadIndicatorInteraction", self.tag);
// We want the block offer to appear just before the first unread incoming
// message in the conversation timeline.
uint64_t indicatorTimestamp = firstUnreadMessage.timestamp - 1;
TSUnreadIndicatorInteraction *indicator =
[[TSUnreadIndicatorInteraction alloc] initWithTimestamp:indicatorTimestamp thread:thread];
[indicator saveWithTransaction:transaction];
}]; }];
}
+ (void)clearUnreadMessagesIndicator:(TSThread *)thread storageManager:(TSStorageManager *)storageManager
{
OWSAssert(thread);
OWSAssert(storageManager);
[storageManager.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
NSMutableArray *indicators = [NSMutableArray new];
[[transaction ext:TSMessageDatabaseViewExtensionName]
enumerateRowsInGroup:thread.uniqueId
usingBlock:^(
NSString *collection, NSString *key, id object, id metadata, NSUInteger index, BOOL *stop) {
if ([object isKindOfClass:[TSUnreadIndicatorInteraction class]]) { return result;
[indicators addObject:object];
}
}];
for (TSUnreadIndicatorInteraction *indicator in indicators) {
[indicator removeWithTransaction:transaction];
}
}];
} }
#pragma mark - Logging #pragma mark - Logging

Loading…
Cancel
Save