Rework the contact offers.

// FREEBIE
pull/1/head
Matthew Chen 8 years ago
parent 5f2f8ec6d8
commit 98eb4693c5

@ -177,7 +177,7 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
// we need to bump this constant. // we need to bump this constant.
// //
// We added a number of database views in v2.13.0. // We added a number of database views in v2.13.0.
NSString *kLastVersionWithDatabaseViewChange = @"2.16.0"; NSString *kLastVersionWithDatabaseViewChange = @"2.13.0";
BOOL mayNeedUpgrade = ([TSAccountManager isRegistered] && lastLaunchedAppVersion BOOL mayNeedUpgrade = ([TSAccountManager isRegistered] && lastLaunchedAppVersion
&& (!lastCompletedLaunchAppVersion || && (!lastCompletedLaunchAppVersion ||
[VersionMigrations isVersion:lastCompletedLaunchAppVersion [VersionMigrations isVersion:lastCompletedLaunchAppVersion

@ -8,17 +8,19 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSContactOffersInteraction : TSInteraction @interface OWSContactOffersInteraction : TSInteraction
//@property (atomic, readonly) BOOL hasMoreUnseenMessages; @property (nonatomic, readonly) BOOL hasBlockOffer;
// @property (nonatomic, readonly) BOOL hasAddToContactsOffer;
//@property (atomic, readonly) NSUInteger missingUnseenSafetyNumberChangeCount; @property (nonatomic, readonly) BOOL hasAddToProfileWhitelistOffer;
@property (nonatomic, readonly) NSString *contactId;
- (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; - (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithTimestamp:(uint64_t)timestamp - (instancetype)initWithTimestamp:(uint64_t)timestamp
thread:(TSThread *)thread thread:(TSThread *)thread
// hasMoreUnseenMessages:(BOOL)hasMoreUnseenMessages hasBlockOffer:(BOOL)hasBlockOffer
// missingUnseenSafetyNumberChangeCount:(NSUInteger)missingUnseenSafetyNumberChangeCount hasAddToContactsOffer:(BOOL)hasAddToContactsOffer
NS_DESIGNATED_INITIALIZER; hasAddToProfileWhitelistOffer:(BOOL)hasAddToProfileWhitelistOffer
contactId:(NSString *)contactId NS_DESIGNATED_INITIALIZER;
@end @end

@ -6,14 +6,6 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@interface OWSContactOffersInteraction ()
//@property (atomic) BOOL hasMoreUnseenMessages;
@end
#pragma mark -
@implementation OWSContactOffersInteraction @implementation OWSContactOffersInteraction
- (instancetype)initWithCoder:(NSCoder *)coder - (instancetype)initWithCoder:(NSCoder *)coder
@ -21,9 +13,12 @@ NS_ASSUME_NONNULL_BEGIN
return [super initWithCoder:coder]; return [super initWithCoder:coder];
} }
- (instancetype)initWithTimestamp:(uint64_t)timestamp thread:(TSThread *)thread - (instancetype)initWithTimestamp:(uint64_t)timestamp
// hasMoreUnseenMessages:(BOOL)hasMoreUnseenMessages thread:(TSThread *)thread
// missingUnseenSafetyNumberChangeCount:(NSUInteger)missingUnseenSafetyNumberChangeCount hasBlockOffer:(BOOL)hasBlockOffer
hasAddToContactsOffer:(BOOL)hasAddToContactsOffer
hasAddToProfileWhitelistOffer:(BOOL)hasAddToProfileWhitelistOffer
contactId:(NSString *)contactId
{ {
self = [super initWithTimestamp:timestamp inThread:thread]; self = [super initWithTimestamp:timestamp inThread:thread];
@ -31,8 +26,11 @@ NS_ASSUME_NONNULL_BEGIN
return self; return self;
} }
// _hasMoreUnseenMessages = hasMoreUnseenMessages; _hasBlockOffer = hasBlockOffer;
// _missingUnseenSafetyNumberChangeCount = missingUnseenSafetyNumberChangeCount; _hasAddToContactsOffer = hasAddToContactsOffer;
_hasAddToProfileWhitelistOffer = hasAddToProfileWhitelistOffer;
OWSAssert(contactId.length > 0);
_contactId = contactId;
return self; return self;
} }

@ -158,7 +158,7 @@ NS_ASSUME_NONNULL_BEGIN
return [cachedSize CGSizeValue]; return [cachedSize CGSizeValue];
} }
CGSize result = [self.referenceContactOffersCell bubbleSizeForInteraction:interaction CGSize result = [self.referenceUnreadIndicatorCell bubbleSizeForInteraction:interaction
collectionViewWidth:layout.collectionView.width]; collectionViewWidth:layout.collectionView.width];
[self.cache setObject:[NSValue valueWithCGSize:result] forKey:cacheKey]; [self.cache setObject:[NSValue valueWithCGSize:result] forKey:cacheKey];
@ -178,7 +178,7 @@ NS_ASSUME_NONNULL_BEGIN
return [cachedSize CGSizeValue]; return [cachedSize CGSizeValue];
} }
CGSize result = [self.referenceUnreadIndicatorCell bubbleSizeForInteraction:interaction CGSize result = [self.referenceContactOffersCell bubbleSizeForInteraction:interaction
collectionViewWidth:layout.collectionView.width]; collectionViewWidth:layout.collectionView.width];
[self.cache setObject:[NSValue valueWithCGSize:result] forKey:cacheKey]; [self.cache setObject:[NSValue valueWithCGSize:result] forKey:cacheKey];

@ -153,10 +153,9 @@ NS_ASSUME_NONNULL_BEGIN
const int kMaxBlockOfferOutgoingMessageCount = 10; const int kMaxBlockOfferOutgoingMessageCount = 10;
// Find any "dynamic" interactions and safety number changes. // Find any "dynamic" interactions and safety number changes.
__block OWSAddToContactsOfferMessage *existingAddToContactsOffer = nil; NSMutableArray<TSInteraction *> *interactionsToDelete = [NSMutableArray new];
__block OWSAddToProfileWhitelistOfferMessage *existingOWSAddToProfileWhitelistOffer = nil;
__block OWSUnknownContactBlockOfferMessage *existingBlockOffer = nil;
__block TSUnreadIndicatorInteraction *existingUnreadIndicator = nil; __block TSUnreadIndicatorInteraction *existingUnreadIndicator = nil;
__block OWSContactOffersInteraction *existingContactOffers = nil;
NSMutableArray<TSInvalidIdentityKeyErrorMessage *> *blockingSafetyNumberChanges = [NSMutableArray new]; NSMutableArray<TSInvalidIdentityKeyErrorMessage *> *blockingSafetyNumberChanges = [NSMutableArray new];
NSMutableArray<TSInteraction *> *nonBlockingSafetyNumberChanges = [NSMutableArray new]; NSMutableArray<TSInteraction *> *nonBlockingSafetyNumberChanges = [NSMutableArray new];
// We use different views for performance reasons. // We use different views for performance reasons.
@ -166,17 +165,27 @@ NS_ASSUME_NONNULL_BEGIN
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(!existingBlockOffer); [interactionsToDelete addObject:object];
existingBlockOffer = (OWSUnknownContactBlockOfferMessage *)object;
} else if ([object isKindOfClass:[OWSAddToContactsOfferMessage class]]) { } else if ([object isKindOfClass:[OWSAddToContactsOfferMessage class]]) {
OWSAssert(!existingAddToContactsOffer); [interactionsToDelete addObject:object];
existingAddToContactsOffer = (OWSAddToContactsOfferMessage *)object;
} else if ([object isKindOfClass:[OWSAddToProfileWhitelistOfferMessage class]]) { } else if ([object isKindOfClass:[OWSAddToProfileWhitelistOfferMessage class]]) {
OWSAssert(!existingOWSAddToProfileWhitelistOffer); [interactionsToDelete addObject:object];
existingOWSAddToProfileWhitelistOffer = (OWSAddToProfileWhitelistOfferMessage *)object;
} else if ([object isKindOfClass:[TSUnreadIndicatorInteraction class]]) { } else if ([object isKindOfClass:[TSUnreadIndicatorInteraction class]]) {
OWSAssert(!existingUnreadIndicator); OWSAssert(!existingUnreadIndicator);
if (existingUnreadIndicator) {
// There should never be more than one unread indicator in
// a given thread, but if there is, discard all but one.
[interactionsToDelete addObject:existingUnreadIndicator];
}
existingUnreadIndicator = (TSUnreadIndicatorInteraction *)object; existingUnreadIndicator = (TSUnreadIndicatorInteraction *)object;
} else if ([object isKindOfClass:[OWSContactOffersInteraction class]]) {
OWSAssert(!existingContactOffers);
if (existingContactOffers) {
// There should never be more than one "contact offers" in
// a given thread, but if there is, discard all but one.
[interactionsToDelete addObject:existingContactOffers];
}
existingContactOffers = (OWSContactOffersInteraction *)object;
} else if ([object isKindOfClass:[TSInvalidIdentityKeyErrorMessage class]]) { } else if ([object isKindOfClass:[TSInvalidIdentityKeyErrorMessage class]]) {
[blockingSafetyNumberChanges addObject:object]; [blockingSafetyNumberChanges addObject:object];
} else if ([object isKindOfClass:[TSErrorMessage class]]) { } else if ([object isKindOfClass:[TSErrorMessage class]]) {
@ -189,6 +198,11 @@ NS_ASSUME_NONNULL_BEGIN
} }
}]; }];
for (TSInteraction *interaction in interactionsToDelete) {
DDLogDebug(@"Cleaning up interaction: %@", [interaction class]);
[interaction removeWithTransaction:transaction];
}
// Determine if there are "unread" messages in this conversation. // Determine if there are "unread" messages in this conversation.
// If we've been passed a firstUnseenInteractionTimestampParameter, // If we've been passed a firstUnseenInteractionTimestampParameter,
// just use that value in order to preserve continuity of the // just use that value in order to preserve continuity of the
@ -326,6 +340,8 @@ NS_ASSUME_NONNULL_BEGIN
shouldHaveAddToContactsOffer = NO; shouldHaveAddToContactsOffer = NO;
// Only create block offers in 1:1 conversations. // Only create block offers in 1:1 conversations.
shouldHaveBlockOffer = NO; shouldHaveBlockOffer = NO;
// Only create profile whitelist offers in 1:1 conversations.
shouldHaveAddToProfileWhitelistOffer = NO;
} else { } else {
NSString *recipientId = ((TSContactThread *)thread).contactIdentifier; NSString *recipientId = ((TSContactThread *)thread).contactIdentifier;
@ -352,6 +368,8 @@ NS_ASSUME_NONNULL_BEGIN
shouldHaveAddToContactsOffer = NO; shouldHaveAddToContactsOffer = NO;
// Only create block offers for non-contacts. // Only create block offers for non-contacts.
shouldHaveBlockOffer = NO; shouldHaveBlockOffer = NO;
// Don't create profile whitelist offers for non-contacts.
shouldHaveAddToProfileWhitelistOffer = NO;
} }
} }
} }
@ -359,6 +377,7 @@ NS_ASSUME_NONNULL_BEGIN
if (!firstMessage) { if (!firstMessage) {
shouldHaveAddToContactsOffer = NO; shouldHaveAddToContactsOffer = NO;
shouldHaveBlockOffer = NO; shouldHaveBlockOffer = NO;
shouldHaveAddToProfileWhitelistOffer = NO;
} }
if (outgoingMessageCount > kMaxBlockOfferOutgoingMessageCount) { if (outgoingMessageCount > kMaxBlockOfferOutgoingMessageCount) {
@ -393,11 +412,12 @@ NS_ASSUME_NONNULL_BEGIN
} }
} }
BOOL shouldHaveContactOffers
= (shouldHaveBlockOffer || shouldHaveAddToContactsOffer || shouldHaveAddToProfileWhitelistOffer);
// 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 kAddToProfileWhitelistOfferOffset = -4; const int kUnreadIndicatorOffset = -2;
const int kBlockOfferOffset = -3; const int kContactOffersOffset = -1;
const int kAddToContactsOfferOffset = -2;
const int kUnreadIndicatorOfferOffset = -1;
// We want the offers to be the first interactions in their // We want the offers to be the first interactions in their
// conversation's timeline, so we back-date them to slightly before // conversation's timeline, so we back-date them to slightly before
@ -406,74 +426,48 @@ NS_ASSUME_NONNULL_BEGIN
long long startOfConversationTimestamp long long startOfConversationTimestamp
= (long long)(firstMessage ? firstMessage.timestampForSorting : 1000); = (long long)(firstMessage ? firstMessage.timestampForSorting : 1000);
if (existingBlockOffer && !shouldHaveBlockOffer) { uint64_t contactOffersTimestamp = (uint64_t)(startOfConversationTimestamp + kContactOffersOffset);
DDLogInfo(@"%@ Removing block offer: %@ (%llu)",
self.tag,
existingBlockOffer.uniqueId,
existingBlockOffer.timestampForSorting);
[existingBlockOffer removeWithTransaction:transaction];
} else if (!existingBlockOffer && shouldHaveBlockOffer) {
DDLogInfo(@"Creating block offer for unknown contact");
uint64_t blockOfferTimestamp = (uint64_t)(startOfConversationTimestamp + kBlockOfferOffset);
NSString *recipientId = ((TSContactThread *)thread).contactIdentifier;
TSMessage *offerMessage = // If the contact offers' properties have changed, discard the current
[OWSUnknownContactBlockOfferMessage unknownContactBlockOfferMessage:blockOfferTimestamp // one and create a new one.
thread:thread if (existingContactOffers) {
contactId:recipientId]; if (existingContactOffers.hasBlockOffer != shouldHaveBlockOffer
[offerMessage saveWithTransaction:transaction]; || existingContactOffers.hasAddToContactsOffer != shouldHaveAddToContactsOffer
|| existingContactOffers.hasAddToProfileWhitelistOffer != shouldHaveAddToProfileWhitelistOffer) {
DDLogInfo(@"%@ Creating block offer: %@ (%llu)", DDLogInfo(@"%@ Removing stale contact offers: %@ (%llu)",
self.tag, self.tag,
offerMessage.uniqueId, existingContactOffers.uniqueId,
offerMessage.timestampForSorting); existingContactOffers.timestampForSorting);
// Preserve the timestamp of the existing "contact offers" so that
// we replace it in the same position in the timeline.
contactOffersTimestamp = existingContactOffers.timestamp;
[existingContactOffers removeWithTransaction:transaction];
existingContactOffers = nil;
}
} }
if (existingAddToContactsOffer && !shouldHaveAddToContactsOffer) { if (existingContactOffers && !shouldHaveContactOffers) {
DDLogInfo(@"%@ Removing 'add to contacts' offer: %@ (%llu)", DDLogInfo(@"%@ Removing contact offers: %@ (%llu)",
self.tag, self.tag,
existingAddToContactsOffer.uniqueId, existingContactOffers.uniqueId,
existingAddToContactsOffer.timestampForSorting); existingContactOffers.timestampForSorting);
[existingAddToContactsOffer removeWithTransaction:transaction]; [existingContactOffers removeWithTransaction:transaction];
} else if (!existingAddToContactsOffer && shouldHaveAddToContactsOffer) { } else if (!existingContactOffers && shouldHaveContactOffers) {
DDLogInfo(@"%@ Creating 'add to contacts' offer for unknown contact", self.tag);
uint64_t offerTimestamp = (uint64_t)(startOfConversationTimestamp + kAddToContactsOfferOffset);
NSString *recipientId = ((TSContactThread *)thread).contactIdentifier; NSString *recipientId = ((TSContactThread *)thread).contactIdentifier;
TSMessage *offerMessage = [OWSAddToContactsOfferMessage addToContactsOfferMessage:offerTimestamp TSInteraction *offersMessage =
[[OWSContactOffersInteraction alloc] initWithTimestamp:contactOffersTimestamp
thread:thread thread:thread
hasBlockOffer:shouldHaveBlockOffer
hasAddToContactsOffer:shouldHaveAddToContactsOffer
hasAddToProfileWhitelistOffer:shouldHaveAddToProfileWhitelistOffer
contactId:recipientId]; contactId:recipientId];
[offerMessage saveWithTransaction:transaction]; [offersMessage saveWithTransaction:transaction];
DDLogInfo(@"%@ Creating 'add to contacts' offer: %@ (%llu)",
self.tag,
offerMessage.uniqueId,
offerMessage.timestampForSorting);
}
if (existingOWSAddToProfileWhitelistOffer && !shouldHaveAddToProfileWhitelistOffer) {
DDLogInfo(@"%@ Removing 'add to profile whitelist' offer: %@ (%llu)",
self.tag,
existingOWSAddToProfileWhitelistOffer.uniqueId,
existingOWSAddToProfileWhitelistOffer.timestampForSorting);
[existingOWSAddToProfileWhitelistOffer removeWithTransaction:transaction];
} else if (!existingOWSAddToProfileWhitelistOffer && shouldHaveAddToProfileWhitelistOffer) {
DDLogInfo(@"%@ Creating 'add to profile whitelist' offer", self.tag);
uint64_t offerTimestamp = (uint64_t)(startOfConversationTimestamp + kAddToProfileWhitelistOfferOffset);
TSMessage *offerMessage =
[OWSAddToProfileWhitelistOfferMessage addToProfileWhitelistOfferMessage:offerTimestamp thread:thread];
[offerMessage saveWithTransaction:transaction];
DDLogInfo(@"%@ Creating 'add to profile whitelist' offer: %@ (%llu)", DDLogInfo(@"%@ Creating contact offers: %@ (%llu)",
self.tag, self.tag,
offerMessage.uniqueId, offersMessage.uniqueId,
offerMessage.timestampForSorting); offersMessage.timestampForSorting);
} }
BOOL shouldHaveUnreadIndicator BOOL shouldHaveUnreadIndicator
@ -490,8 +484,8 @@ NS_ASSUME_NONNULL_BEGIN
// message in the conversation timeline... // message in the conversation timeline...
// //
// ...unless we have a fixed timestamp for the unread indicator. // ...unless we have a fixed timestamp for the unread indicator.
uint64_t indicatorTimestamp = (uint64_t)( uint64_t indicatorTimestamp
(long long)interactionAfterUnreadIndicator.timestampForSorting + kUnreadIndicatorOfferOffset); = (uint64_t)((long long)interactionAfterUnreadIndicator.timestampForSorting + kUnreadIndicatorOffset);
if (indicatorTimestamp && existingUnreadIndicator.timestampForSorting == indicatorTimestamp) { if (indicatorTimestamp && existingUnreadIndicator.timestampForSorting == indicatorTimestamp) {
// Keep the existing indicator; it is in the correct position. // Keep the existing indicator; it is in the correct position.

@ -115,20 +115,24 @@ NS_ASSUME_NONNULL_BEGIN
- (NSString *)subtitleForInteraction:(OWSContactOffersInteraction *)interaction - (NSString *)subtitleForInteraction:(OWSContactOffersInteraction *)interaction
{ {
if (!interaction.hasMoreUnseenMessages) {
return nil; return nil;
} // if (!interaction.hasMoreUnseenMessages) {
NSString *subtitleFormat = (interaction.missingUnseenSafetyNumberChangeCount > 0 // return nil;
? NSLocalizedString(@"MESSAGES_VIEW_UNREAD_INDICATOR_HAS_MORE_UNSEEN_MESSAGES_FORMAT", // }
@"Messages that indicates that there are more unseen messages that be revealed by tapping the 'load " // NSString *subtitleFormat = (interaction.missingUnseenSafetyNumberChangeCount > 0
@"earlier messages' button. Embeds {{the name of the 'load earlier messages' button}}") // ? NSLocalizedString(@"MESSAGES_VIEW_UNREAD_INDICATOR_HAS_MORE_UNSEEN_MESSAGES_FORMAT",
: NSLocalizedString( // @"Messages that indicates that there are more unseen messages that be revealed by tapping the
@"MESSAGES_VIEW_UNREAD_INDICATOR_HAS_MORE_UNSEEN_MESSAGES_AND_SAFETY_NUMBER_CHANGES_FORMAT", // 'load "
@"Messages that indicates that there are more unseen messages including safety number changes that " // @"earlier messages' button. Embeds {{the name of the 'load earlier messages' button}}")
@"be revealed by tapping the 'load earlier messages' button. Embeds {{the name of the 'load earlier " // : NSLocalizedString(
@"messages' button}}.")); // @"MESSAGES_VIEW_UNREAD_INDICATOR_HAS_MORE_UNSEEN_MESSAGES_AND_SAFETY_NUMBER_CHANGES_FORMAT",
NSString *loadMoreButtonName = [NSBundle jsq_localizedStringForKey:@"load_earlier_messages"]; // @"Messages that indicates that there are more unseen messages including safety number changes
return [NSString stringWithFormat:subtitleFormat, loadMoreButtonName]; // that "
// @"be revealed by tapping the 'load earlier messages' button. Embeds {{the name of the 'load
// earlier "
// @"messages' button}}."));
// NSString *loadMoreButtonName = [NSBundle jsq_localizedStringForKey:@"load_earlier_messages"];
// return [NSString stringWithFormat:subtitleFormat, loadMoreButtonName];
} }
- (CGFloat)subtitleHMargin - (CGFloat)subtitleHMargin

@ -144,7 +144,7 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic
[self registerMessageDatabaseViewWithName:TSUnseenDatabaseViewExtensionName [self registerMessageDatabaseViewWithName:TSUnseenDatabaseViewExtensionName
viewGrouping:viewGrouping viewGrouping:viewGrouping
version:@"2" version:@"1"
async:YES]; async:YES];
} }

Loading…
Cancel
Save