88#import " TSUnreadIndicatorInteraction.h"
99#import < SignalServiceKit/NSDate+millisecondTimeStamp.h>
1010#import < SignalServiceKit/OWSAddToContactsOfferMessage.h>
11+ #import < SignalServiceKit/OWSAddToProfileWhitelistOfferMessage.h>
1112#import < SignalServiceKit/OWSBlockingManager.h>
1213#import < SignalServiceKit/OWSDisappearingMessagesConfiguration.h>
1314#import < SignalServiceKit/OWSMessageSender.h>
@@ -151,6 +152,7 @@ + (ThreadDynamicInteractions *)ensureDynamicInteractionsForThread:(TSThread *)th
151152
152153 // Find any "dynamic" interactions and safety number changes.
153154 __block OWSAddToContactsOfferMessage *existingAddToContactsOffer = nil ;
155+ __block OWSAddToProfileWhitelistOfferMessage *existingOWSAddToProfileWhitelistOffer = nil ;
154156 __block OWSUnknownContactBlockOfferMessage *existingBlockOffer = nil ;
155157 __block TSUnreadIndicatorInteraction *existingUnreadIndicator = nil ;
156158 NSMutableArray <TSInvalidIdentityKeyErrorMessage *> *blockingSafetyNumberChanges = [NSMutableArray new ];
@@ -167,6 +169,9 @@ + (ThreadDynamicInteractions *)ensureDynamicInteractionsForThread:(TSThread *)th
167169 } else if ([object isKindOfClass: [OWSAddToContactsOfferMessage class ]]) {
168170 OWSAssert (!existingAddToContactsOffer);
169171 existingAddToContactsOffer = (OWSAddToContactsOfferMessage *)object;
172+ } else if ([object isKindOfClass: [OWSAddToProfileWhitelistOfferMessage class ]]) {
173+ OWSAssert (!existingOWSAddToProfileWhitelistOffer);
174+ existingOWSAddToProfileWhitelistOffer = (OWSAddToProfileWhitelistOfferMessage *)object;
170175 } else if ([object isKindOfClass: [TSUnreadIndicatorInteraction class ]]) {
171176 OWSAssert (!existingUnreadIndicator);
172177 existingUnreadIndicator = (TSUnreadIndicatorInteraction *)object;
@@ -311,6 +316,7 @@ + (ThreadDynamicInteractions *)ensureDynamicInteractionsForThread:(TSThread *)th
311316
312317 BOOL shouldHaveBlockOffer = YES ;
313318 BOOL shouldHaveAddToContactsOffer = YES ;
319+ BOOL shouldHaveAddToProfileWhitelistOffer = YES ;
314320
315321 BOOL isContactThread = [thread isKindOfClass: [TSContactThread class ]];
316322 if (!isContactThread) {
@@ -326,6 +332,8 @@ + (ThreadDynamicInteractions *)ensureDynamicInteractionsForThread:(TSThread *)th
326332 shouldHaveAddToContactsOffer = NO ;
327333 // Don't bother to block self.
328334 shouldHaveBlockOffer = NO ;
335+ // Don't bother adding self to profile whitelist.
336+ shouldHaveAddToProfileWhitelistOffer = NO ;
329337 } else {
330338 if ([[blockingManager blockedPhoneNumbers ] containsObject: recipientId]) {
331339 // Only create "add to contacts" offers for users which are not already blocked.
@@ -361,7 +369,15 @@ + (ThreadDynamicInteractions *)ensureDynamicInteractionsForThread:(TSThread *)th
361369 shouldHaveBlockOffer = NO ;
362370 }
363371
372+ if (![OWSProfileManager.sharedManager hasLocalProfile ] ||
373+ [OWSProfileManager.sharedManager isThreadInProfileWhitelist: thread]) {
374+ // Don't show offer if thread is local user hasn't configured their profile.
375+ // Don't show offer if thread is already in profile whitelist.
376+ shouldHaveAddToProfileWhitelistOffer = NO ;
377+ }
378+
364379 // We use these offset to control the ordering of the offers and indicators.
380+ const int kAddToProfileWhitelistOfferOffset = -4 ;
365381 const int kBlockOfferOffset = -3 ;
366382 const int kAddToContactsOfferOffset = -2 ;
367383 const int kUnreadIndicatorOfferOffset = -1 ;
@@ -371,7 +387,6 @@ + (ThreadDynamicInteractions *)ensureDynamicInteractionsForThread:(TSThread *)th
371387 self.tag ,
372388 existingBlockOffer.uniqueId ,
373389 existingBlockOffer.timestampForSorting );
374- ;
375390 [existingBlockOffer removeWithTransaction: transaction];
376391 } else if (!existingBlockOffer && shouldHaveBlockOffer) {
377392 DDLogInfo (@" Creating block offer for unknown contact" );
@@ -402,7 +417,7 @@ + (ThreadDynamicInteractions *)ensureDynamicInteractionsForThread:(TSThread *)th
402417 [existingAddToContactsOffer removeWithTransaction: transaction];
403418 } else if (!existingAddToContactsOffer && shouldHaveAddToContactsOffer) {
404419
405- DDLogInfo (@" Creating 'add to contacts' offer for unknown contact" );
420+ DDLogInfo (@" %@ Creating 'add to contacts' offer for unknown contact" , self. tag );
406421
407422 // We want the offer to be the first interaction in their
408423 // conversation's timeline, so we back-date it to slightly before
@@ -422,6 +437,32 @@ + (ThreadDynamicInteractions *)ensureDynamicInteractionsForThread:(TSThread *)th
422437 offerMessage.timestampForSorting );
423438 }
424439
440+ if (existingOWSAddToProfileWhitelistOffer && !shouldHaveAddToProfileWhitelistOffer) {
441+ DDLogInfo (@" %@ Removing 'add to profile whitelist' offer: %@ (%llu )" ,
442+ self.tag ,
443+ existingOWSAddToProfileWhitelistOffer.uniqueId ,
444+ existingOWSAddToProfileWhitelistOffer.timestampForSorting );
445+ [existingOWSAddToProfileWhitelistOffer removeWithTransaction: transaction];
446+ } else if (!existingOWSAddToProfileWhitelistOffer && shouldHaveAddToProfileWhitelistOffer) {
447+
448+ DDLogInfo (@" %@ Creating 'add to profile whitelist' offer" , self.tag );
449+
450+ // We want the offer to be the first interaction in their
451+ // conversation's timeline, so we back-date it to slightly before
452+ // the first incoming message (which we know is the first message).
453+ uint64_t offerTimestamp
454+ = (uint64_t )((long long )firstMessage.timestampForSorting + kAddToProfileWhitelistOfferOffset );
455+
456+ TSMessage *offerMessage =
457+ [OWSAddToProfileWhitelistOfferMessage addToProfileWhitelistOfferMessage: offerTimestamp thread: thread];
458+ [offerMessage saveWithTransaction: transaction];
459+
460+ DDLogInfo (@" %@ Creating 'add to profile whitelist' offer: %@ (%llu )" ,
461+ self.tag ,
462+ offerMessage.uniqueId ,
463+ offerMessage.timestampForSorting );
464+ }
465+
425466 BOOL shouldHaveUnreadIndicator
426467 = (interactionAfterUnreadIndicator && !hideUnreadMessagesIndicator && threadMessageCount > 1 );
427468 if (!shouldHaveUnreadIndicator) {
0 commit comments