Merge branch 'charlesmchen/addToContactsOffer'

pull/1/head
Matthew Chen 8 years ago
commit 6a1cf55358

@ -134,7 +134,7 @@ CHECKOUT OPTIONS:
:commit: 7054e4b13ee5bcd6d524adb6dc9a726e8c466308
:git: https://github.com/WhisperSystems/JSQMessagesViewController.git
SignalServiceKit:
:commit: 485af7e8170dc1bb55245d94d991c10eda4979e0
:commit: cbeafac20ebb0437baf8982381c1980db276681f
:git: https://github.com/WhisperSystems/SignalServiceKit.git
SocketRocket:
:commit: 877ac7438be3ad0b45ef5ca3969574e4b97112bf

@ -1066,23 +1066,23 @@
457F3AC01D14A0F700C51351 /* Models */ = {
isa = PBXGroup;
children = (
B62D53F41A23CC8B009AAF82 /* TSMessageAdapters */,
453D28B51D32BA5F00D523F0 /* OWSDisplayedMessage.h */,
453D28B61D32BA5F00D523F0 /* OWSDisplayedMessage.m */,
45CD81EE1DC030E7004C9430 /* AccountManager.swift */,
45DF5DF11DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift */,
45666EC41D99483D008FE134 /* OWSAvatarBuilder.h */,
45666EC51D99483D008FE134 /* OWSAvatarBuilder.m */,
45C681B51D305A580050903A /* OWSCall.h */,
45C681B61D305A580050903A /* OWSCall.m */,
453D28B81D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.h */,
453D28B91D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m */,
458E38351D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.h */,
458E38361D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m */,
45855F351D9498A40084F340 /* OWSContactAvatarBuilder.h */,
45855F361D9498A40084F340 /* OWSContactAvatarBuilder.m */,
45666EC41D99483D008FE134 /* OWSAvatarBuilder.h */,
45666EC51D99483D008FE134 /* OWSAvatarBuilder.m */,
458E38351D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.h */,
458E38361D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m */,
453D28B51D32BA5F00D523F0 /* OWSDisplayedMessage.h */,
453D28B61D32BA5F00D523F0 /* OWSDisplayedMessage.m */,
45666EC71D994C0D008FE134 /* OWSGroupAvatarBuilder.h */,
45666EC81D994C0D008FE134 /* OWSGroupAvatarBuilder.m */,
45CD81EE1DC030E7004C9430 /* AccountManager.swift */,
45DF5DF11DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift */,
453D28B81D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.h */,
453D28B91D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m */,
B62D53F41A23CC8B009AAF82 /* TSMessageAdapters */,
);
path = Models;
sourceTree = "<group>";

@ -7,6 +7,7 @@
#import "AttachmentSharing.h"
#import "BlockListUIUtils.h"
#import "BlockListViewController.h"
#import "ContactsViewHelper.h"
#import "DebugUITableViewController.h"
#import "Environment.h"
#import "FingerprintViewController.h"
@ -26,7 +27,6 @@
#import "OWSMessageCollectionViewCell.h"
#import "OWSMessagesBubblesSizeCalculator.h"
#import "OWSOutgoingMessageCollectionViewCell.h"
#import "OWSUnknownContactBlockOfferMessage.h"
#import "OWSUnreadIndicatorCell.h"
#import "PropertyListPreferences.h"
#import "Signal-Swift.h"
@ -63,12 +63,14 @@
#import <SignalServiceKit/ContactsUpdater.h>
#import <SignalServiceKit/MimeTypeUtil.h>
#import <SignalServiceKit/NSTimer+OWS.h>
#import <SignalServiceKit/OWSAddToContactsOfferMessage.h>
#import <SignalServiceKit/OWSAttachmentsProcessor.h>
#import <SignalServiceKit/OWSBlockingManager.h>
#import <SignalServiceKit/OWSDisappearingMessagesConfiguration.h>
#import <SignalServiceKit/OWSFingerprint.h>
#import <SignalServiceKit/OWSFingerprintBuilder.h>
#import <SignalServiceKit/OWSMessageSender.h>
#import <SignalServiceKit/OWSUnknownContactBlockOfferMessage.h>
#import <SignalServiceKit/SignalRecipient.h>
#import <SignalServiceKit/TSAccountManager.h>
#import <SignalServiceKit/TSInvalidIdentityKeySendingErrorMessage.h>
@ -586,7 +588,10 @@ typedef enum : NSUInteger {
OWSMessagesToolbarContentDelegate,
OWSConversationSettingsViewDelegate,
UIDocumentMenuDelegate,
UIDocumentPickerDelegate> {
UIDocumentPickerDelegate,
ContactsViewHelperDelegate,
ContactEditingDelegate,
CNContactViewControllerDelegate> {
UIImage *tappedImage;
BOOL isGroupConversation;
}
@ -640,6 +645,11 @@ typedef enum : NSUInteger {
@property (nonatomic) NSDate *lastMessageSentDate;
@property (nonatomic) NSTimer *scrollLaterTimer;
@property (nonatomic, readonly) ContactsViewHelper *contactsViewHelper;
@property (nonatomic, nullable) ThreadOffersAndIndicators *offersAndIndicators;
@property (nonatomic) BOOL hasClearedUnreadMessagesIndicator;
@end
@implementation MessagesViewController
@ -694,6 +704,7 @@ typedef enum : NSUInteger {
_messagesManager = [TSMessagesManager sharedManager];
_networkManager = [TSNetworkManager sharedManager];
_blockingManager = [OWSBlockingManager sharedManager];
_contactsViewHelper = [[ContactsViewHelper alloc] initWithDelegate:self];
[self addNotificationListeners];
}
@ -736,7 +747,9 @@ typedef enum : NSUInteger {
_composeOnOpen = keyboardOnViewAppearing;
_callOnOpen = callOnViewAppearing;
[ThreadUtil createUnreadMessagesIndicatorIfNecessary:thread storageManager:self.storageManager];
// We need to create the "unread indicator" before we mark
// all messages as read.
[self ensureThreadOffersAndIndicators];
[self markAllMessagesAsRead];
@ -813,14 +826,6 @@ typedef enum : NSUInteger {
self.automaticallyScrollsToMostRecentMessage = NO;
[self initializeToolbars];
if ([self.thread isKindOfClass:[TSContactThread class]]) {
TSContactThread *contactThread = (TSContactThread *)self.thread;
[ThreadUtil createBlockOfferIfNecessary:contactThread
storageManager:self.storageManager
contactsManager:self.contactsManager
blockingManager:self.blockingManager];
}
}
- (void)viewDidLayoutSubviews
@ -940,6 +945,9 @@ typedef enum : NSUInteger {
{
[super viewWillAppear:animated];
// In case we're dismissing a CNContactViewController which requires default system appearance
[UIUtil applySignalAppearence];
// Since we're using a custom back button, we have to do some extra work to manage the interactivePopGestureRecognizer
self.navigationController.interactivePopGestureRecognizer.delegate = self;
@ -949,6 +957,8 @@ typedef enum : NSUInteger {
[self toggleObservers:YES];
[self ensureThreadOffersAndIndicators];
// Triggering modified notification renders "call notification" when leaving full screen call view
[self.thread touch];
@ -995,11 +1005,6 @@ typedef enum : NSUInteger {
repeats:NO];
}
- (void)clearUnreadMessagesIndicator
{
[ThreadUtil clearUnreadMessagesIndicator:self.thread storageManager:self.storageManager];
}
- (NSIndexPath *_Nullable)indexPathOfUnreadMessagesIndicator
{
int numberOfMessages = (int)[self.messageMappings numberOfItemsInGroup:self.thread.uniqueId];
@ -1620,6 +1625,7 @@ typedef enum : NSUInteger {
} else {
[ThreadUtil sendMessageWithText:text inThread:self.thread messageSender:self.messageSender];
}
self.lastMessageSentDate = [NSDate new];
[self clearUnreadMessagesIndicator];
@ -2311,6 +2317,9 @@ typedef enum : NSUInteger {
case TSErrorMessageAdapter:
[self handleErrorMessageTap:(TSErrorMessage *)interaction];
break;
case TSInfoMessageAdapter:
[self handleInfoMessageTap:(TSInfoMessage *)interaction];
break;
case TSCallAdapter:
case TSUnreadIndicatorAdapter:
break;
@ -2549,6 +2558,15 @@ typedef enum : NSUInteger {
}
}
- (void)handleInfoMessageTap:(TSInfoMessage *)message
{
if ([message isKindOfClass:[OWSAddToContactsOfferMessage class]]) {
[self tappedAddToContactsOfferMessage:(OWSAddToContactsOfferMessage *)message];
} else {
DDLogInfo(@"%@ Unhandled tap for info message:%@", self.tag, message);
}
}
- (void)tappedCorruptedMessage:(TSErrorMessage *)message
{
@ -2666,6 +2684,89 @@ typedef enum : NSUInteger {
[self presentViewController:actionSheetController animated:YES completion:nil];
}
- (void)tappedAddToContactsOfferMessage:(OWSAddToContactsOfferMessage *)errorMessage
{
if (!self.contactsManager.supportsContactEditing) {
DDLogError(@"%@ Contact editing not supported", self.tag);
OWSAssert(NO);
return;
}
if (![self.thread isKindOfClass:[TSContactThread class]]) {
DDLogError(@"%@ unexpected thread: %@ in %s", self.tag, self.thread, __PRETTY_FUNCTION__);
OWSAssert(NO);
return;
}
TSContactThread *contactThread = (TSContactThread *)self.thread;
[self.contactsViewHelper presentContactViewControllerForRecipientId:contactThread.contactIdentifier
fromViewController:self
editImmediately:YES];
}
#pragma mark - ContactEditingDelegate
- (void)didFinishEditingContact
{
DDLogDebug(@"%@ %s", self.tag, __PRETTY_FUNCTION__);
[self dismissViewControllerAnimated:NO completion:nil];
}
#pragma mark - CNContactViewControllerDelegate
- (void)contactViewController:(CNContactViewController *)viewController
didCompleteWithContact:(nullable CNContact *)contact
{
if (contact) {
// Saving normally returns you to the "Show Contact" view
// which we're not interested in, so we skip it here. There is
// an unfortunate blip of the "Show Contact" view on slower devices.
DDLogDebug(@"%@ completed editing contact.", self.tag);
[self dismissViewControllerAnimated:NO completion:nil];
} else {
DDLogDebug(@"%@ canceled editing contact.", self.tag);
[self dismissViewControllerAnimated:YES completion:nil];
}
}
#pragma mark - ContactsViewHelperDelegate
- (void)contactsViewHelperDidUpdateContacts
{
[self ensureThreadOffersAndIndicators];
}
- (void)ensureThreadOffersAndIndicators
{
OWSAssert([NSThread isMainThread]);
self.offersAndIndicators =
[ThreadUtil ensureThreadOffersAndIndicators:self.thread
storageManager:self.storageManager
contactsManager:self.contactsManager
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 somewhat expensive
// so we don't want to call it unnecessarily.
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

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

@ -7,6 +7,7 @@
#import "Signal-Swift.h"
#import "TSUnreadIndicatorInteraction.h"
#import <SignalServiceKit/NSDate+millisecondTimeStamp.h>
#import <SignalServiceKit/OWSAddToContactsOfferMessage.h>
#import <SignalServiceKit/OWSBlockingManager.h>
#import <SignalServiceKit/OWSDisappearingMessagesConfiguration.h>
#import <SignalServiceKit/OWSMessageSender.h>
@ -17,6 +18,12 @@
NS_ASSUME_NONNULL_BEGIN
@implementation ThreadOffersAndIndicators
@end
#pragma mark -
@implementation ThreadUtil
+ (void)sendMessageWithText:(NSString *)text inThread:(TSThread *)thread messageSender:(OWSMessageSender *)messageSender
@ -74,44 +81,45 @@ NS_ASSUME_NONNULL_BEGIN
}];
}
+ (void)createBlockOfferIfNecessary:(TSContactThread *)contactThread
storageManager:(TSStorageManager *)storageManager
contactsManager:(OWSContactsManager *)contactsManager
blockingManager:(OWSBlockingManager *)blockingManager
+ (ThreadOffersAndIndicators *)ensureThreadOffersAndIndicators:(TSThread *)thread
storageManager:(TSStorageManager *)storageManager
contactsManager:(OWSContactsManager *)contactsManager
blockingManager:(OWSBlockingManager *)blockingManager
hideUnreadMessagesIndicator:(BOOL)hideUnreadMessagesIndicator
fixedUnreadIndicatorTimestamp:(NSNumber *_Nullable)fixedUnreadIndicatorTimestamp
{
OWSAssert(contactThread);
OWSAssert(thread);
OWSAssert(storageManager);
OWSAssert(contactsManager);
OWSAssert(blockingManager);
if ([[blockingManager blockedPhoneNumbers] containsObject:contactThread.contactIdentifier]) {
// Only create block offers for users which are not already blocked.
return;
}
SignalAccount *signalAccount = contactsManager.signalAccountMap[contactThread.contactIdentifier];
if (signalAccount) {
// Only create block offers for non-contacts.
return;
}
ThreadOffersAndIndicators *result = [ThreadOffersAndIndicators new];
[storageManager.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
const int kMaxOutgoingMessageCount = 10;
const int kMaxBlockOfferOutgoingMessageCount = 10;
__block OWSAddToContactsOfferMessage *existingAddToContactsOffer = nil;
__block OWSUnknownContactBlockOfferMessage *existingBlockOffer = nil;
__block TSUnreadIndicatorInteraction *existingUnreadIndicator = nil;
__block TSIncomingMessage *firstIncomingMessage = nil;
__block TSOutgoingMessage *firstOutgoingMessage = nil;
__block TSIncomingMessage *firstUnreadMessage = nil;
__block long outgoingMessageCount = 0;
__block BOOL hasUnknownContactBlockOffer = NO;
[[transaction ext:TSMessageDatabaseViewExtensionName]
enumerateRowsInGroup:contactThread.uniqueId
enumerateRowsInGroup:thread.uniqueId
usingBlock:^(
NSString *collection, NSString *key, id object, id metadata, NSUInteger index, BOOL *stop) {
if ([object isKindOfClass:[OWSUnknownContactBlockOfferMessage class]]) {
hasUnknownContactBlockOffer = YES;
// If there already is a block offer, abort.
*stop = YES;
OWSAssert(!existingBlockOffer);
existingBlockOffer = (OWSUnknownContactBlockOfferMessage *)object;
} else if ([object isKindOfClass:[OWSAddToContactsOfferMessage class]]) {
OWSAssert(!existingAddToContactsOffer);
existingAddToContactsOffer = (OWSAddToContactsOfferMessage *)object;
} else if ([object isKindOfClass:[TSUnreadIndicatorInteraction class]]) {
OWSAssert(!existingUnreadIndicator);
existingUnreadIndicator = (TSUnreadIndicatorInteraction *)object;
} else if ([object isKindOfClass:[TSIncomingMessage class]]) {
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)object;
if (!firstIncomingMessage) {
@ -121,6 +129,16 @@ NS_ASSUME_NONNULL_BEGIN
compare:[incomingMessage receiptDateForSorting]]
== NSOrderedAscending);
}
if (!incomingMessage.wasRead) {
if (!firstUnreadMessage) {
firstUnreadMessage = incomingMessage;
} else {
OWSAssert([[firstUnreadMessage receiptDateForSorting]
compare:[incomingMessage receiptDateForSorting]]
== NSOrderedAscending);
}
}
} else if ([object isKindOfClass:[TSOutgoingMessage class]]) {
TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)object;
if (!firstOutgoingMessage) {
@ -131,26 +149,53 @@ NS_ASSUME_NONNULL_BEGIN
== NSOrderedAscending);
}
outgoingMessageCount++;
if (outgoingMessageCount > kMaxOutgoingMessageCount) {
// If the user has sent more than N interactions, abort.
*stop = YES;
}
}
}];
if (!firstIncomingMessage && !firstOutgoingMessage) {
// If the thread has no interactions, abort.
return;
TSMessage *firstMessage = firstIncomingMessage;
if (!firstMessage
|| (firstOutgoingMessage &&
[[firstOutgoingMessage receiptDateForSorting] compare:[firstMessage receiptDateForSorting]]
== NSOrderedAscending)) {
firstMessage = firstOutgoingMessage;
}
BOOL shouldHaveBlockOffer = YES;
BOOL shouldHaveAddToContactsOffer = YES;
BOOL isContactThread = [thread isKindOfClass:[TSContactThread class]];
if (!isContactThread) {
// Only create "add to contacts" offers in 1:1 conversations.
shouldHaveAddToContactsOffer = NO;
// Only create block offers in 1:1 conversations.
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 (outgoingMessageCount > kMaxOutgoingMessageCount) {
// If the user has sent more than N messages, abort.
return;
if (!firstMessage) {
shouldHaveAddToContactsOffer = NO;
shouldHaveBlockOffer = NO;
}
if (hasUnknownContactBlockOffer) {
// If there already is a block offer, abort.
return;
if (outgoingMessageCount > kMaxBlockOfferOutgoingMessageCount) {
// If the user has sent more than N messages, don't show a block offer.
shouldHaveBlockOffer = NO;
}
BOOL hasOutgoingBeforeIncomingInteraction = (firstOutgoingMessage
@ -159,99 +204,87 @@ NS_ASSUME_NONNULL_BEGIN
== NSOrderedAscending));
if (hasOutgoingBeforeIncomingInteraction) {
// If there is an outgoing message before an incoming message
// the local user initiated this conversation, abort.
return;
// the local user initiated this conversation, don't show a block offer.
shouldHaveBlockOffer = NO;
}
DDLogInfo(@"Creating block offer for unknown contact");
// We use these offset to control the ordering of the offers and indicators.
const int kBlockOfferOffset = -3;
const int kAddToContactsOfferOffset = -2;
const int kUnreadIndicatorOfferOffset = -1;
if (existingBlockOffer && !shouldHaveBlockOffer) {
[existingBlockOffer removeWithTransaction:transaction];
} else if (!existingBlockOffer && shouldHaveBlockOffer) {
DDLogInfo(@"Creating block offer for unknown contact");
// We want the block offer to be the first interaction in their
// conversation's timeline, so we back-date it to slightly before
// the first incoming message (which we know is the first message).
uint64_t blockOfferTimestamp = (uint64_t)((long long)firstMessage.timestamp + kBlockOfferOffset);
NSString *recipientId = ((TSContactThread *)thread).contactIdentifier;
TSMessage *offerMessage =
[OWSUnknownContactBlockOfferMessage unknownContactBlockOfferMessage:blockOfferTimestamp
thread:thread
contactId:recipientId];
[offerMessage saveWithTransaction:transaction];
}
// We want the block offer to be the first interaction in their
// conversation's timeline, so we back-date it to slightly before
// the first incoming message (which we know is the first message).
TSIncomingMessage *firstMessage = firstIncomingMessage;
uint64_t blockOfferTimestamp = firstMessage.timestamp - 1;
if (existingAddToContactsOffer && !shouldHaveAddToContactsOffer) {
[existingAddToContactsOffer removeWithTransaction:transaction];
} else if (!existingAddToContactsOffer && shouldHaveAddToContactsOffer) {
TSErrorMessage *errorMessage =
[OWSUnknownContactBlockOfferMessage unknownContactBlockOfferMessage:blockOfferTimestamp
thread:contactThread
contactId:contactThread.contactIdentifier];
[errorMessage saveWithTransaction:transaction];
}];
}
DDLogInfo(@"Creating 'add to contacts' offer for unknown contact");
+ (void)createUnreadMessagesIndicatorIfNecessary:(TSThread *)thread storageManager:(TSStorageManager *)storageManager
{
OWSAssert(thread);
OWSAssert(storageManager);
// We want the offer to be the first interaction in their
// conversation's timeline, so we back-date it to slightly before
// the first incoming message (which we know is the first message).
uint64_t offerTimestamp = (uint64_t)((long long)firstMessage.timestamp + kAddToContactsOfferOffset);
NSString *recipientId = ((TSContactThread *)thread).contactIdentifier;
[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]]) {
[indicators addObject:object];
} else if ([object isKindOfClass:[TSIncomingMessage class]]) {
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)object;
if (!incomingMessage.wasRead) {
if (!firstUnreadMessage) {
firstUnreadMessage = incomingMessage;
} else {
OWSAssert([[firstUnreadMessage receiptDateForSorting]
compare:[incomingMessage receiptDateForSorting]]
== NSOrderedAscending);
}
}
}
}];
for (TSUnreadIndicatorInteraction *indicator in indicators) {
[indicator removeWithTransaction:transaction];
TSMessage *offerMessage = [OWSAddToContactsOfferMessage addToContactsOfferMessage:offerTimestamp
thread:thread
contactId:recipientId];
[offerMessage saveWithTransaction:transaction];
}
BOOL shouldHaveIndicator = firstUnreadMessage != nil;
if (!shouldHaveIndicator) {
return;
BOOL shouldHaveUnreadIndicator
= ((firstUnreadMessage != nil || fixedUnreadIndicatorTimestamp != nil) && !hideUnreadMessagesIndicator);
if (!shouldHaveUnreadIndicator) {
if (existingUnreadIndicator) {
[existingUnreadIndicator removeWithTransaction:transaction];
}
} else {
// We want the block offer to appear just before the first unread incoming
// message in the conversation timeline...
//
// ...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 && existingUnreadIndicator.timestamp == indicatorTimestamp) {
// Keep the existing indicator; it is in the correct position.
result.unreadIndicator = existingUnreadIndicator;
} else {
if (existingUnreadIndicator) {
[existingUnreadIndicator removeWithTransaction:transaction];
}
DDLogInfo(@"%@ Creating TSUnreadIndicatorInteraction", self.tag);
TSUnreadIndicatorInteraction *indicator =
[[TSUnreadIndicatorInteraction alloc] initWithTimestamp:indicatorTimestamp thread:thread];
[indicator saveWithTransaction:transaction];
result.unreadIndicator = indicator;
}
}
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]]) {
[indicators addObject:object];
}
}];
for (TSUnreadIndicatorInteraction *indicator in indicators) {
[indicator removeWithTransaction:transaction];
}
}];
return result;
}
#pragma mark - Logging

@ -16,6 +16,9 @@
/* Title for the 'add group member' view. */
"ADD_GROUP_MEMBER_VIEW_TITLE" = "Add Member";
/* No comment provided by engineer. */
"ADD_TO_CONTACTS_OFFER" = "Would you like to add this user to your contacts?";
/* The label for the 'discard' button in alerts and action sheets. */
"ALERT_DISCARD_BUTTON" = "Discard";

Loading…
Cancel
Save