Persist signal accounts (and their embedded Contact)

// FREEBIE
pull/1/head
Michael Kirk 7 years ago
parent 9cea6971ba
commit 7ea4b85a2a

@ -881,7 +881,6 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
[OWSProfileManager.sharedManager fetchLocalUsersProfile]; [OWSProfileManager.sharedManager fetchLocalUsersProfile];
[[OWSReadReceiptManager sharedManager] prepareCachedValues]; [[OWSReadReceiptManager sharedManager] prepareCachedValues];
[[Environment getCurrent].contactsManager loadLastKnownContactRecipientIds];
} }
- (void)registrationStateDidChange - (void)registrationStateDidChange

@ -171,7 +171,7 @@ NS_ASSUME_NONNULL_BEGIN
OWSAssert([self.thread isKindOfClass:[TSContactThread class]]); OWSAssert([self.thread isKindOfClass:[TSContactThread class]]);
TSContactThread *contactThread = (TSContactThread *)self.thread; TSContactThread *contactThread = (TSContactThread *)self.thread;
NSString *recipientId = contactThread.contactIdentifier; NSString *recipientId = contactThread.contactIdentifier;
return [self.contactsManager.lastKnownContactRecipientIds containsObject:recipientId]; return [self.contactsManager signalAccountForRecipientId:recipientId] != nil;
} }
#pragma mark - ContactEditingDelegate #pragma mark - ContactEditingDelegate

@ -30,33 +30,10 @@ extern NSString *const OWSContactsManagerSignalAccountsDidChangeNotification;
@property (atomic, readonly) NSDictionary<NSString *, Contact *> *allContactsMap; @property (atomic, readonly) NSDictionary<NSString *, Contact *> *allContactsMap;
// signalAccountMap and signalAccounts hold the same data. // order of the signalAccounts array respects the systems contact sorting preference
// signalAccountMap is for lookup. signalAccounts contains the accounts
// ordered by display order.
@property (atomic, readonly) NSDictionary<NSString *, SignalAccount *> *signalAccountMap;
@property (atomic, readonly) NSArray<SignalAccount *> *signalAccounts; @property (atomic, readonly) NSArray<SignalAccount *> *signalAccounts;
// This value is cached and is available immediately, before system contacts
// fetch or contacts intersection.
//
// In some cases, its better if our UI reflects these values
// which haven't been updated yet rather than assume that
// we have no contacts until the first contacts intersection
// successfully completes.
//
// This significantly improves the user experience when:
//
// * No contacts intersection has completed because the app has just launched.
// * Contacts intersection can't complete due to an unreliable connection or
// the contacts intersection rate limit.
@property (atomic, readonly) NSArray<NSString *> *lastKnownContactRecipientIds;
- (nullable SignalAccount *)signalAccountForRecipientId:(NSString *)recipientId; - (nullable SignalAccount *)signalAccountForRecipientId:(NSString *)recipientId;
- (Contact *)getOrBuildContactForPhoneIdentifier:(NSString *)identifier;
- (void)loadLastKnownContactRecipientIds;
#pragma mark - System Contact Fetching #pragma mark - System Contact Fetching
// Must call `requestSystemContactsOnce` before accessing this method // Must call `requestSystemContactsOnce` before accessing this method

@ -23,7 +23,6 @@ NSString *const kTSStorageManager_AccountDisplayNames = @"kTSStorageManager_Acco
NSString *const kTSStorageManager_AccountFirstNames = @"kTSStorageManager_AccountFirstNames"; NSString *const kTSStorageManager_AccountFirstNames = @"kTSStorageManager_AccountFirstNames";
NSString *const kTSStorageManager_AccountLastNames = @"kTSStorageManager_AccountLastNames"; NSString *const kTSStorageManager_AccountLastNames = @"kTSStorageManager_AccountLastNames";
NSString *const kTSStorageManager_OWSContactsManager = @"kTSStorageManager_OWSContactsManager"; NSString *const kTSStorageManager_OWSContactsManager = @"kTSStorageManager_OWSContactsManager";
NSString *const kTSStorageManager_lastKnownContactRecipientIds = @"lastKnownContactRecipientIds";
@interface OWSContactsManager () <SystemContactsFetcherDelegate> @interface OWSContactsManager () <SystemContactsFetcherDelegate>
@ -34,7 +33,6 @@ NSString *const kTSStorageManager_lastKnownContactRecipientIds = @"lastKnownCont
@property (atomic) NSDictionary<NSString *, Contact *> *allContactsMap; @property (atomic) NSDictionary<NSString *, Contact *> *allContactsMap;
@property (atomic) NSArray<SignalAccount *> *signalAccounts; @property (atomic) NSArray<SignalAccount *> *signalAccounts;
@property (atomic) NSDictionary<NSString *, SignalAccount *> *signalAccountMap; @property (atomic) NSDictionary<NSString *, SignalAccount *> *signalAccountMap;
@property (atomic) NSArray<NSString *> *lastKnownContactRecipientIds;
@property (nonatomic, readonly) SystemContactsFetcher *systemContactsFetcher; @property (nonatomic, readonly) SystemContactsFetcher *systemContactsFetcher;
@property (atomic) NSDictionary<NSString *, NSString *> *cachedAccountNameMap; @property (atomic) NSDictionary<NSString *, NSString *> *cachedAccountNameMap;
@ -57,7 +55,6 @@ NSString *const kTSStorageManager_lastKnownContactRecipientIds = @"lastKnownCont
_allContactsMap = @{}; _allContactsMap = @{};
_signalAccountMap = @{}; _signalAccountMap = @{};
_signalAccounts = @[]; _signalAccounts = @[];
_lastKnownContactRecipientIds = @[];
_systemContactsFetcher = [SystemContactsFetcher new]; _systemContactsFetcher = [SystemContactsFetcher new];
_systemContactsFetcher.delegate = self; _systemContactsFetcher.delegate = self;
@ -68,18 +65,6 @@ NSString *const kTSStorageManager_lastKnownContactRecipientIds = @"lastKnownCont
return self; return self;
} }
- (void)loadLastKnownContactRecipientIds
{
[TSStorageManager.sharedManager.newDatabaseConnection readWithBlock:^(
YapDatabaseReadTransaction *_Nonnull transaction) {
NSArray<NSString *> *_Nullable value = [transaction objectForKey:kTSStorageManager_lastKnownContactRecipientIds
inCollection:kTSStorageManager_OWSContactsManager];
if (value) {
self.lastKnownContactRecipientIds = value;
}
}];
}
#pragma mark - System Contact Fetching #pragma mark - System Contact Fetching
// Request contacts access if you haven't asked recently. // Request contacts access if you haven't asked recently.
@ -245,16 +230,18 @@ NSString *const kTSStorageManager_lastKnownContactRecipientIds = @"lastKnownCont
} }
} }
NSArray<NSString *> *lastKnownContactRecipientIds = [signalAccountMap allKeys];
[TSStorageManager.sharedManager.newDatabaseConnection [TSStorageManager.sharedManager.newDatabaseConnection
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[transaction setObject:lastKnownContactRecipientIds // TODO we can be more efficient here.
forKey:kTSStorageManager_lastKnownContactRecipientIds // - only save the ones that changed
inCollection:kTSStorageManager_OWSContactsManager]; // - only remove the ones which no longer exist
[transaction removeAllObjectsInCollection:[SignalAccount collection]];
for (SignalAccount *signalAccount in signalAccounts) {
[signalAccount saveWithTransaction:transaction];
}
}]; }];
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
self.lastKnownContactRecipientIds = lastKnownContactRecipientIds;
self.signalAccountMap = [signalAccountMap copy]; self.signalAccountMap = [signalAccountMap copy];
self.signalAccounts = [signalAccounts copy]; self.signalAccounts = [signalAccounts copy];
@ -682,25 +669,22 @@ NSString *const kTSStorageManager_lastKnownContactRecipientIds = @"lastKnownCont
{ {
OWSAssert(recipientId.length > 0); OWSAssert(recipientId.length > 0);
return self.signalAccountMap[recipientId]; SignalAccount *signalAccount = self.signalAccountMap[recipientId];
}
- (Contact *)getOrBuildContactForPhoneIdentifier:(NSString *)identifier // If contact intersection hasn't completed, it might exist on disk
{ // even if it doesn't exist in memory yet.
Contact *savedContact = self.allContactsMap[identifier]; if (!signalAccount) {
if (savedContact) { signalAccount = [SignalAccount fetchObjectWithUniqueID:recipientId];
return savedContact;
} else {
return [[Contact alloc] initWithContactWithFirstName:self.unknownContactName
andLastName:nil
andUserTextPhoneNumbers:@[ identifier ]
andImage:nil
andContactID:0];
} }
return signalAccount;
} }
- (UIImage * _Nullable)imageForPhoneIdentifier:(NSString * _Nullable)identifier { - (UIImage * _Nullable)imageForPhoneIdentifier:(NSString * _Nullable)identifier {
Contact *contact = self.allContactsMap[identifier]; Contact *contact = self.allContactsMap[identifier];
if (!contact) {
contact = [self signalAccountForRecipientId:identifier].contact;
}
// Prefer the contact image from the local address book if available // Prefer the contact image from the local address book if available
UIImage *_Nullable image = contact.image; UIImage *_Nullable image = contact.image;

@ -388,7 +388,7 @@ NS_ASSUME_NONNULL_BEGIN
shouldHaveAddToProfileWhitelistOffer = NO; shouldHaveAddToProfileWhitelistOffer = NO;
} }
BOOL isContact = [contactsManager.lastKnownContactRecipientIds containsObject:recipientId]; BOOL isContact = [contactsManager signalAccountForRecipientId:recipientId] != nil;
if (isContact) { if (isContact) {
// Only create "add to contacts" offers for non-contacts. // Only create "add to contacts" offers for non-contacts.
shouldHaveAddToContactsOffer = NO; shouldHaveAddToContactsOffer = NO;

@ -3,6 +3,7 @@
// //
#import <AddressBook/AddressBook.h> #import <AddressBook/AddressBook.h>
#import <Mantle/MTLModel.h>
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@ -19,7 +20,7 @@ NS_ASSUME_NONNULL_BEGIN
@class SignalRecipient; @class SignalRecipient;
@class YapDatabaseReadTransaction; @class YapDatabaseReadTransaction;
@interface Contact : NSObject @interface Contact : MTLModel
@property (nullable, readonly, nonatomic) NSString *firstName; @property (nullable, readonly, nonatomic) NSString *firstName;
@property (nullable, readonly, nonatomic) NSString *lastName; @property (nullable, readonly, nonatomic) NSString *lastName;
@ -30,13 +31,13 @@ NS_ASSUME_NONNULL_BEGIN
@property (readonly, nonatomic) NSArray<NSString *> *userTextPhoneNumbers; @property (readonly, nonatomic) NSArray<NSString *> *userTextPhoneNumbers;
@property (readonly, nonatomic) NSArray<NSString *> *emails; @property (readonly, nonatomic) NSArray<NSString *> *emails;
@property (readonly, nonatomic) NSString *uniqueId; @property (readonly, nonatomic) NSString *uniqueId;
@property (nonatomic, readonly) BOOL isSignalContact;
#if TARGET_OS_IOS #if TARGET_OS_IOS
@property (nullable, readonly, nonatomic) UIImage *image; @property (nullable, readonly, nonatomic) UIImage *image;
@property (readonly, nonatomic) ABRecordID recordID; @property (readonly, nonatomic) ABRecordID recordID;
@property (nullable, nonatomic, readonly) CNContact *cnContact; @property (nullable, nonatomic, readonly) CNContact *cnContact;
#endif // TARGET_OS_IOS #endif // TARGET_OS_IOS
- (BOOL)isSignalContact;
- (NSArray<SignalRecipient *> *)signalRecipientsWithTransaction:(YapDatabaseReadTransaction *)transaction; - (NSArray<SignalRecipient *> *)signalRecipientsWithTransaction:(YapDatabaseReadTransaction *)transaction;
// TODO: Remove this method. // TODO: Remove this method.
- (NSArray<NSString *> *)textSecureIdentifiers; - (NSArray<NSString *> *)textSecureIdentifiers;

@ -2,6 +2,8 @@
// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// //
#import "TSYapDatabaseObject.h"
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@class Contact; @class Contact;
@ -14,10 +16,7 @@ NS_ASSUME_NONNULL_BEGIN
// multiple instances of SignalAccount. // multiple instances of SignalAccount.
// * For non-contacts, the contact property will be nil. // * For non-contacts, the contact property will be nil.
// //
// New instances of SignalAccount for active accounts are @interface SignalAccount : TSYapDatabaseObject
// created every time we do a contacts intersection (e.g.
// in response to a change to the device contacts).
@interface SignalAccount : NSObject
// An E164 value identifying the signal account. // An E164 value identifying the signal account.
// //

@ -46,6 +46,11 @@ NS_ASSUME_NONNULL_BEGIN
return [SignalRecipient recipientWithTextSecureIdentifier:self.recipientId withTransaction:transaction]; return [SignalRecipient recipientWithTextSecureIdentifier:self.recipientId withTransaction:transaction];
} }
- (nullable NSString *)uniqueId
{
return _recipientId;
}
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

Loading…
Cancel
Save