Browse Source

Fully switch to the new contact API

This should improve performance significantly as it avoids many unnecessary sync transactions. It also makes the code more readable.
pull/472/head
Niels Andriesse 10 months ago
parent
commit
519ffa4405
  1. 4
      Session/Meta/AppDelegate.m
  2. 11
      SessionMessagingKit/Database/Storage+Contacts.swift
  3. 10
      SessionMessagingKit/Database/Storage+Shared.swift
  4. 35
      SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift
  5. 2
      SessionMessagingKit/Storage.swift
  6. 71
      SessionMessagingKit/To Do/OWSUserProfile.h
  7. 467
      SessionMessagingKit/To Do/OWSUserProfile.m
  8. 7
      SessionMessagingKit/To Do/ProfileManagerProtocol.h
  9. 2
      SessionMessagingKit/Utilities/FullTextSearchFinder.swift
  10. 5
      SessionShareExtension/ShareVC.swift
  11. 4
      SignalUtilitiesKit/Database/Storage+Conformances.swift
  12. 14
      SignalUtilitiesKit/Messaging/ConfigurationMessage+Convenience.swift
  13. 6
      SignalUtilitiesKit/To Do/OWSProfileManager.h
  14. 564
      SignalUtilitiesKit/To Do/OWSProfileManager.m

4
Session/Meta/AppDelegate.m

@ -445,9 +445,6 @@ static NSTimeInterval launchStartedAt;
[SNConfiguration performMainSetup];
// TODO: Once "app ready" logic is moved into AppSetup, move this line there.
[self.profileManager ensureLocalProfileCached];
// Note that this does much more than set a flag;
// it will also run all deferred blocks.
[AppReadiness setAppIsReady];
@ -533,7 +530,6 @@ static NSTimeInterval launchStartedAt;
// Start running the disappearing messages job in case the newly registered user
// enables this feature
[self.disappearingMessagesJob startIfNecessary];
[self.profileManager ensureLocalProfileCached];
// For non-legacy users, read receipts are on by default.
[self.readReceiptManager setAreReadReceiptsEnabled:YES];

11
SessionMessagingKit/Database/Storage+Contacts.swift

@ -17,12 +17,21 @@ extension Storage {
@objc(setContact:usingTransaction:)
public func setContact(_ contact: Contact, using transaction: Any) {
let oldContact = getContact(with: contact.sessionID)
let transaction = transaction as! YapDatabaseReadWriteTransaction
if contact.sessionID == getUserHexEncodedPublicKey() {
contact.isTrusted = true // Always trust ourselves
}
transaction.setObject(contact, forKey: contact.sessionID, inCollection: Storage.contactCollection)
transaction.addCompletionQueue(DispatchQueue.main) {
// Delete old profile picture if needed
if let oldProfilePictureFileName = oldContact?.profilePictureFileName {
let path = OWSUserProfile.profileAvatarFilepath(withFilename: oldProfilePictureFileName)
DispatchQueue.global(qos: .default).async {
OWSFileSystem.deleteFileIfExists(path)
}
}
// Post notification
let notificationCenter = NotificationCenter.default
notificationCenter.post(name: .contactUpdated, object: contact.sessionID)
if contact.sessionID == getUserHexEncodedPublicKey() {
@ -34,7 +43,7 @@ extension Storage {
}
}
public func getAllContacts() -> Set<Contact> {
@objc public func getAllContacts() -> Set<Contact> {
var result: Set<Contact> = []
Storage.read { transaction in
transaction.enumerateRows(inCollection: Storage.contactCollection) { _, object, _, _ in

10
SessionMessagingKit/Database/Storage+Shared.swift

@ -40,16 +40,6 @@ extension Storage {
var result: Contact?
Storage.read { transaction in
result = Storage.shared.getContact(with: userPublicKey)
// HACK: Apparently it's still possible for the user's contact info to be missing
if result == nil, let profile = OWSUserProfile.fetch(uniqueId: kLocalProfileUniqueId, transaction: transaction),
let userPublicKey = Storage.shared.getUserPublicKey() {
let user = Contact(sessionID: userPublicKey)
user.name = profile.profileName
user.profilePictureURL = profile.avatarUrlPath
user.profilePictureFileName = profile.avatarFileName
user.profilePictureEncryptionKey = profile.profileKey
result = user
}
}
return result
}

35
SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift

@ -181,24 +181,24 @@ extension MessageReceiver {
let storage = SNMessagingKitConfiguration.shared.storage
let transaction = transaction as! YapDatabaseReadWriteTransaction
// Profile
let userProfile = SNMessagingKitConfiguration.shared.storage.getUserProfile(using: transaction)
updateProfileIfNeeded(publicKey: userPublicKey, name: message.displayName, profilePictureURL: message.profilePictureURL,
profileKey: given(message.profileKey) { OWSAES256Key(data: $0)! }, sentTimestamp: message.sentTimestamp!, transaction: transaction)
transaction.addCompletionQueue(DispatchQueue.main) {
SSKEnvironment.shared.profileManager.downloadAvatar(for: userProfile)
let userProfile = storage.getUser() ?? Contact(sessionID: userPublicKey)
SSKEnvironment.shared.profileManager.downloadAvatar(forUserProfile: userProfile)
}
// Initial configuration sync
if !UserDefaults.standard[.hasSyncedInitialConfiguration] {
UserDefaults.standard[.hasSyncedInitialConfiguration] = true
NotificationCenter.default.post(name: .initialConfigurationMessageReceived, object: nil)
// Contacts
for contact in message.contacts {
let sessionID = contact.publicKey!
let userProfile = OWSUserProfile.getOrBuild(forRecipientId: sessionID, transaction: transaction)
userProfile.profileKey = given(contact.profileKey) { OWSAES256Key(data: $0)! }
userProfile.avatarUrlPath = contact.profilePictureURL
userProfile.profileName = contact.displayName
userProfile.save(with: transaction)
for contactInfo in message.contacts {
let sessionID = contactInfo.publicKey!
let contact = Contact(sessionID: sessionID)
contact.profilePictureEncryptionKey = given(contactInfo.profileKey) { OWSAES256Key(data: $0)! }
contact.profilePictureURL = contactInfo.profilePictureURL
contact.name = contactInfo.displayName
Storage.shared.setContact(contact, using: transaction)
let thread = TSContactThread.getOrCreateThread(withContactSessionID: sessionID, transaction: transaction)
thread.shouldBeVisible = true
thread.save(with: transaction)
@ -302,13 +302,10 @@ extension MessageReceiver {
private static func updateProfileIfNeeded(publicKey: String, name: String?, profilePictureURL: String?,
profileKey: OWSAES256Key?, sentTimestamp: UInt64, transaction: YapDatabaseReadWriteTransaction) {
let isCurrentUser = (publicKey == getUserHexEncodedPublicKey())
let profileManager = SSKEnvironment.shared.profileManager
let userDefaults = UserDefaults.standard
let owsProfile = isCurrentUser ? SNMessagingKitConfiguration.shared.storage.getUserProfile(using: transaction)
: OWSUserProfile.fetch(uniqueId: publicKey, transaction: transaction) // Old API
let contact = Storage.shared.getContact(with: publicKey) ?? Contact(sessionID: publicKey) // New API
// Name
if let name = name, (name != owsProfile?.profileName || contact.name != owsProfile?.profileName) {
if let name = name, name != contact.name {
let shouldUpdate: Bool
if isCurrentUser {
shouldUpdate = given(userDefaults[.lastDisplayNameUpdate]) { sentTimestamp > UInt64($0.timeIntervalSince1970 * 1000) } ?? true
@ -317,17 +314,14 @@ extension MessageReceiver {
}
if shouldUpdate {
if isCurrentUser {
owsProfile?.profileName = name
userDefaults[.lastDisplayNameUpdate] = Date(timeIntervalSince1970: TimeInterval(sentTimestamp / 1000))
} else {
profileManager.updateProfileForContact(withID: publicKey, displayName: name, with: transaction)
}
contact.name = name
}
}
// Profile picture & profile key
if let profileKey = profileKey, let profilePictureURL = profilePictureURL, profileKey.keyData.count == kAES256_KeyByteLength,
(profileKey != owsProfile?.profileKey || contact.profilePictureEncryptionKey != owsProfile?.profileKey) {
profileKey != contact.profilePictureEncryptionKey {
let shouldUpdate: Bool
if isCurrentUser {
shouldUpdate = given(userDefaults[.lastProfilePictureUpdate]) { sentTimestamp > UInt64($0.timeIntervalSince1970 * 1000) } ?? true
@ -336,20 +330,13 @@ extension MessageReceiver {
}
if shouldUpdate {
if isCurrentUser {
owsProfile?.avatarUrlPath = profilePictureURL
owsProfile?.profileKey = profileKey
userDefaults[.lastProfilePictureUpdate] = Date(timeIntervalSince1970: TimeInterval(sentTimestamp / 1000))
} else {
profileManager.setProfileKeyData(profileKey.keyData, forRecipientId: publicKey, avatarURL: profilePictureURL)
}
contact.profilePictureURL = profilePictureURL
contact.profilePictureEncryptionKey = profileKey
}
}
// Persist changes
if isCurrentUser { // In the case where it's someone else the profile will already be saved (updateProfileForContact and setProfileKeyData to that internally)
owsProfile?.save(with: transaction)
}
Storage.shared.setContact(contact, using: transaction)
}

2
SessionMessagingKit/Storage.swift

@ -17,7 +17,7 @@ public protocol SessionMessagingKitStorageProtocol {
func getUserKeyPair() -> ECKeyPair?
func getUserED25519KeyPair() -> Box.KeyPair?
func getUser() -> Contact?
func getUserProfile(using transaction: Any) -> OWSUserProfile
func getAllContacts() -> Set<Contact>
// MARK: - Closed Groups

71
SessionMessagingKit/To Do/OWSUserProfile.h

@ -6,83 +6,12 @@
NS_ASSUME_NONNULL_BEGIN
typedef void (^OWSUserProfileCompletion)(void);
@class OWSAES256Key;
extern NSString *const kNSNotificationName_LocalProfileDidChange;
extern NSString *const kNSNotificationName_OtherUsersProfileWillChange;
extern NSString *const kNSNotificationName_OtherUsersProfileDidChange;
extern NSString *const kNSNotificationKey_ProfileRecipientId;
extern NSString *const kNSNotificationKey_ProfileGroupId;
extern NSString *const kLocalProfileUniqueId;
// This class should be completely thread-safe.
//
// It is critical for coherency that all DB operations for this
// class should be done on OWSProfileManager's dbConnection.
@interface OWSUserProfile : TSYapDatabaseObject
@property (atomic, readonly) NSString *recipientId;
@property (atomic, nullable) OWSAES256Key *profileKey;
@property (atomic, nullable) NSString *profileName;
@property (atomic, nullable) NSString *avatarUrlPath;
// This filename is relative to OWSProfileManager.profileAvatarsDirPath.
@property (atomic, readonly, nullable) NSString *avatarFileName;
- (instancetype)init NS_UNAVAILABLE;
+ (OWSUserProfile *)getOrBuildUserProfileForRecipientId:(NSString *)recipientId
dbConnection:(YapDatabaseConnection *)dbConnection;
+ (OWSUserProfile *)getOrBuildUserProfileForRecipientId:(NSString *)recipientId transaction:(YapDatabaseReadWriteTransaction *)transaction;
+ (BOOL)localUserProfileExists:(YapDatabaseConnection *)dbConnection;
#pragma mark - Update With... Methods
- (void)updateWithProfileName:(nullable NSString *)profileName
avatarUrlPath:(nullable NSString *)avatarUrlPath
avatarFileName:(nullable NSString *)avatarFileName
transaction:(YapDatabaseReadWriteTransaction *)transaction
completion:(nullable OWSUserProfileCompletion)completion;
- (void)updateWithProfileName:(nullable NSString *)profileName
avatarUrlPath:(nullable NSString *)avatarUrlPath
avatarFileName:(nullable NSString *)avatarFileName
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion;
- (void)updateWithProfileName:(nullable NSString *)profileName
avatarUrlPath:(nullable NSString *)avatarUrlPath
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion;
- (void)updateWithAvatarUrlPath:(nullable NSString *)avatarUrlPath
avatarFileName:(nullable NSString *)avatarFileName
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion;
- (void)updateWithAvatarFileName:(nullable NSString *)avatarFileName
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion;
- (void)updateWithProfileKey:(OWSAES256Key *)profileKey
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion;
- (void)updateWithProfileKey:(OWSAES256Key *)profileKey
transaction:(YapDatabaseReadWriteTransaction *)transaction
completion:(nullable OWSUserProfileCompletion)completion;
- (void)clearWithProfileKey:(OWSAES256Key *)profileKey
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion;
#pragma mark - Profile Avatars Directory
+ (NSString *)profileAvatarFilepathWithFilename:(NSString *)filename;
+ (nullable NSError *)migrateToSharedData;
+ (NSString *)legacyProfileAvatarsDirPath;

467
SessionMessagingKit/To Do/OWSUserProfile.m

@ -19,456 +19,15 @@
NS_ASSUME_NONNULL_BEGIN
NSString *const kNSNotificationName_LocalProfileDidChange = @"kNSNotificationName_LocalProfileDidChange";
NSString *const kNSNotificationName_OtherUsersProfileWillChange = @"kNSNotificationName_OtherUsersProfileWillChange";
NSString *const kNSNotificationName_OtherUsersProfileDidChange = @"kNSNotificationName_OtherUsersProfileDidChange";
NSString *const kNSNotificationKey_ProfileRecipientId = @"kNSNotificationKey_ProfileRecipientId";
NSString *const kNSNotificationKey_ProfileGroupId = @"kNSNotificationKey_ProfileGroupId";
NSString *const kLocalProfileUniqueId = @"kLocalProfileUniqueId";
@interface OWSUserProfile ()
@property (atomic, nullable) NSString *avatarFileName;
@end
#pragma mark -
@implementation OWSUserProfile
@synthesize avatarUrlPath = _avatarUrlPath;
@synthesize avatarFileName = _avatarFileName;
@synthesize profileName = _profileName;
+ (NSString *)collection
{
// Legacy class name.
return @"UserProfile";
}
+ (OWSUserProfile *)getOrBuildUserProfileForRecipientId:(NSString *)recipientId
dbConnection:(YapDatabaseConnection *)dbConnection
{
__block OWSUserProfile *userProfile;
[dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
userProfile = [OWSUserProfile fetchObjectWithUniqueID:recipientId transaction:transaction];
}];
if (userProfile != nil) {
return userProfile;
}
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
userProfile = [OWSUserProfile getOrBuildUserProfileForRecipientId:recipientId transaction:transaction];
}];
return userProfile;
}
+ (OWSUserProfile *)getOrBuildUserProfileForRecipientId:(NSString *)recipientId transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSUserProfile *userProfile = [OWSUserProfile fetchObjectWithUniqueID:recipientId transaction:transaction];
if (!userProfile) {
userProfile = [[OWSUserProfile alloc] initWithRecipientId:recipientId];
if ([recipientId isEqualToString:kLocalProfileUniqueId]) {
[userProfile updateWithProfileKey:[OWSAES256Key generateRandomKey]
transaction:transaction
completion:nil];
}
}
return userProfile;
}
+ (BOOL)localUserProfileExists:(YapDatabaseConnection *)dbConnection
{
__block BOOL result = NO;
[dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
result = [OWSUserProfile fetchObjectWithUniqueID:kLocalProfileUniqueId transaction:transaction] != nil;
}];
return result;
}
- (instancetype)initWithRecipientId:(NSString *)recipientId
{
self = [super initWithUniqueId:recipientId];
if (!self) {
return self;
}
_recipientId = recipientId;
return self;
}
#pragma mark - Dependencies
- (TSAccountManager *)tsAccountManager
{
return SSKEnvironment.shared.tsAccountManager;
}
#pragma mark -
- (NSString *)sessionID
{
if ([self.recipientId isEqual:kLocalProfileUniqueId]) {
return [SNGeneralUtilities getUserPublicKey];
} else {
return self.recipientId;
}
}
- (nullable NSString *)avatarUrlPath
{
@synchronized(self)
{
return _avatarUrlPath;
}
}
- (void)setAvatarUrlPath:(nullable NSString *)avatarUrlPath
{
@synchronized(self)
{
BOOL didChange = ![NSObject isNullableObject:_avatarUrlPath equalTo:avatarUrlPath];
_avatarUrlPath = avatarUrlPath;
if (didChange) {
// If the avatarURL changed, the avatarFileName can't be valid.
// Clear it.
self.avatarFileName = nil;
}
}
}
- (nullable NSString *)avatarFileName
{
@synchronized(self) {
return _avatarFileName;
}
}
- (void)setAvatarFileName:(nullable NSString *)avatarFileName
{
@synchronized(self) {
BOOL didChange = ![NSObject isNullableObject:_avatarFileName equalTo:avatarFileName];
if (!didChange) {
return;
}
if (_avatarFileName) {
NSString *oldAvatarFilePath = [OWSUserProfile profileAvatarFilepathWithFilename:_avatarFileName];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[OWSFileSystem deleteFileIfExists:oldAvatarFilePath];
});
}
_avatarFileName = avatarFileName;
}
}
#pragma mark - Update With... Methods
// Similar in spirit to [TSYapDatabaseObject applyChangeToSelfAndLatestCopy],
// but with significant differences.
//
// * We save if this entity is not in the database.
// * We skip redundant saves by diffing.
// * We kick off multi-device synchronization.
// * We fire "did change" notifications.
- (void)applyChanges:(void (^)(id))changeBlock
functionName:(const char *)functionName
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion
{
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self applyChanges:changeBlock functionName:functionName transaction:transaction completion:completion];
}];
}
- (void)applyChanges:(void (^)(id))changeBlock
functionName:(const char *)functionName
transaction:(YapDatabaseReadWriteTransaction *)transaction
completion:(nullable OWSUserProfileCompletion)completion
{
// self might be the latest instance, so take a "before" snapshot
// before any changes have been made.
__block NSDictionary *beforeSnapshot = [self.dictionaryValue copy];
changeBlock(self);
BOOL didChange = YES;
NSString *collection = [[self class] collection];
OWSUserProfile *_Nullable latestInstance = [transaction objectForKey:self.uniqueId inCollection:collection];
if (latestInstance) {
// If self is NOT the latest instance, take a new "before" snapshot
// before updating.
if (self != latestInstance) {
beforeSnapshot = [latestInstance.dictionaryValue copy];
}
changeBlock(latestInstance);
NSDictionary *afterSnapshot = [latestInstance.dictionaryValue copy];
if ([beforeSnapshot isEqual:afterSnapshot]) {
didChange = NO;
} else {
[latestInstance saveWithTransaction:transaction];
}
} else {
[self saveWithTransaction:transaction];
}
if (completion) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), completion);
}
if (!didChange) {
return;
}
NSString *masterDeviceHexEncodedPublicKey = [NSUserDefaults.standardUserDefaults stringForKey:@"masterDeviceHexEncodedPublicKey"];
BOOL isLocalUserProfile = [self.recipientId isEqualToString:kLocalProfileUniqueId] || (masterDeviceHexEncodedPublicKey != nil && [self.recipientId isEqualToString:masterDeviceHexEncodedPublicKey]);
dispatch_async(dispatch_get_main_queue(), ^{
if (isLocalUserProfile) {
[[NSNotificationCenter defaultCenter] postNotificationNameAsync:kNSNotificationName_LocalProfileDidChange
object:nil
userInfo:nil];
} else {
[[NSNotificationCenter defaultCenter]
postNotificationNameAsync:kNSNotificationName_OtherUsersProfileWillChange
object:nil
userInfo:@{
kNSNotificationKey_ProfileRecipientId : self.recipientId,
}];
[[NSNotificationCenter defaultCenter]
postNotificationNameAsync:kNSNotificationName_OtherUsersProfileDidChange
object:nil
userInfo:@{
kNSNotificationKey_ProfileRecipientId : self.recipientId,
}];
}
});
}
- (void)updateWithProfileName:(nullable NSString *)profileName
avatarUrlPath:(nullable NSString *)avatarUrlPath
avatarFileName:(nullable NSString *)avatarFileName
transaction:(YapDatabaseReadWriteTransaction *)transaction
completion:(nullable OWSUserProfileCompletion)completion
{
[self applyChanges:^(OWSUserProfile *userProfile) {
[userProfile setProfileName:[profileName ows_stripped]];
// Always setAvatarUrlPath: before you setAvatarFileName: since
// setAvatarUrlPath: may clear the avatar filename.
[userProfile setAvatarUrlPath:avatarUrlPath];
[userProfile setAvatarFileName:avatarFileName];
}
functionName:__PRETTY_FUNCTION__
transaction:transaction
completion:completion];
SNContact *contact = [LKStorage.shared getContactWithSessionID:self.sessionID] ?: [[SNContact alloc] initWithSessionID:self.sessionID];
contact.name = [profileName ows_stripped];
contact.profilePictureURL = avatarUrlPath;
contact.profilePictureFileName = avatarFileName;
[LKStorage.shared setContact:contact usingTransaction:transaction];
}
- (void)updateWithProfileName:(nullable NSString *)profileName
avatarUrlPath:(nullable NSString *)avatarUrlPath
avatarFileName:(nullable NSString *)avatarFileName
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion
{
[self applyChanges:^(OWSUserProfile *userProfile) {
[userProfile setProfileName:[profileName ows_stripped]];
// Always setAvatarUrlPath: before you setAvatarFileName: since
// setAvatarUrlPath: may clear the avatar filename.
[userProfile setAvatarUrlPath:avatarUrlPath];
[userProfile setAvatarFileName:avatarFileName];
}
functionName:__PRETTY_FUNCTION__
dbConnection:dbConnection
completion:completion];
SNContact *contact = [LKStorage.shared getContactWithSessionID:self.sessionID] ?: [[SNContact alloc] initWithSessionID:self.sessionID];
contact.name = [profileName ows_stripped];
contact.profilePictureURL = avatarUrlPath;
contact.profilePictureFileName = avatarFileName;
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[LKStorage.shared setContact:contact usingTransaction:transaction];
}];
}
- (void)updateWithProfileName:(nullable NSString *)profileName
avatarUrlPath:(nullable NSString *)avatarUrlPath
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion
{
[self applyChanges:^(OWSUserProfile *userProfile) {
[userProfile setProfileName:[profileName ows_stripped]];
[userProfile setAvatarUrlPath:avatarUrlPath];
}
functionName:__PRETTY_FUNCTION__
dbConnection:dbConnection
completion:completion];
SNContact *contact = [LKStorage.shared getContactWithSessionID:self.sessionID] ?: [[SNContact alloc] initWithSessionID:self.sessionID];
contact.name = [profileName ows_stripped];
contact.profilePictureURL = avatarUrlPath;
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[LKStorage.shared setContact:contact usingTransaction:transaction];
}];
}
- (void)updateWithAvatarUrlPath:(nullable NSString *)avatarUrlPath
avatarFileName:(nullable NSString *)avatarFileName
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion
{
[self applyChanges:^(OWSUserProfile *userProfile) {
// Always setAvatarUrlPath: before you setAvatarFileName: since
// setAvatarUrlPath: may clear the avatar filename.
[userProfile setAvatarUrlPath:avatarUrlPath];
[userProfile setAvatarFileName:avatarFileName];
}
functionName:__PRETTY_FUNCTION__
dbConnection:dbConnection
completion:completion];
SNContact *contact = [LKStorage.shared getContactWithSessionID:self.sessionID] ?: [[SNContact alloc] initWithSessionID:self.sessionID];
contact.profilePictureURL = avatarUrlPath;
contact.profilePictureFileName = avatarFileName;
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[LKStorage.shared setContact:contact usingTransaction:transaction];
}];
}
- (void)updateWithAvatarFileName:(nullable NSString *)avatarFileName
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion
{
[self applyChanges:^(OWSUserProfile *userProfile) {
[userProfile setAvatarFileName:avatarFileName];
}
functionName:__PRETTY_FUNCTION__
dbConnection:dbConnection
completion:completion];
SNContact *contact = [LKStorage.shared getContactWithSessionID:self.sessionID] ?: [[SNContact alloc] initWithSessionID:self.sessionID];
contact.profilePictureFileName = avatarFileName;
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[LKStorage.shared setContact:contact usingTransaction:transaction];
}];
}
- (void)clearWithProfileKey:(OWSAES256Key *)profileKey
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion
{
[self applyChanges:^(OWSUserProfile *userProfile) {
[userProfile setProfileKey:profileKey];
// [userProfile setProfileName:nil]; - Loki disabled until we include profile name inside the encrypted profile from the url
// Always setAvatarUrlPath: before you setAvatarFileName: since
// setAvatarUrlPath: may clear the avatar filename.
[userProfile setAvatarUrlPath:nil];
[userProfile setAvatarFileName:nil];
}
functionName:__PRETTY_FUNCTION__
dbConnection:dbConnection
completion:completion];
}
- (void)updateWithProfileKey:(OWSAES256Key *)profileKey
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion
{
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self updateWithProfileKey:profileKey transaction:transaction completion:completion];
}];
}
- (void)updateWithProfileKey:(OWSAES256Key *)profileKey
transaction:(YapDatabaseReadWriteTransaction *)transaction
completion:(nullable OWSUserProfileCompletion)completion
{
[self applyChanges:^(OWSUserProfile *userProfile) {
[userProfile setProfileKey:profileKey];
}
functionName:__PRETTY_FUNCTION__
transaction:transaction
completion:completion];
SNContact *contact = [LKStorage.shared getContactWithSessionID:self.sessionID] ?: [[SNContact alloc] initWithSessionID:self.sessionID];
contact.profilePictureEncryptionKey = profileKey;
[LKStorage.shared setContact:contact usingTransaction:transaction];
}
#pragma mark - Database Connection Accessors
- (YapDatabaseConnection *)dbReadConnection
{
return TSYapDatabaseObject.dbReadConnection;
}
+ (YapDatabaseConnection *)dbReadConnection
{
return TSYapDatabaseObject.dbReadConnection;
}
- (YapDatabaseConnection *)dbReadWriteConnection
{
return TSYapDatabaseObject.dbReadWriteConnection;
}
+ (YapDatabaseConnection *)dbReadWriteConnection
{
return TSYapDatabaseObject.dbReadWriteConnection;
}
// This should only be used in verbose, developer-only logs.
- (NSString *)debugDescription
{
return [NSString stringWithFormat:@"%@ %p %@ %lu %@ %@ %@",
@"OWSUserProfile",
self,
self.recipientId,
(unsigned long)self.profileKey.keyData.length,
self.profileName,
self.avatarUrlPath,
self.avatarFileName];
}
- (nullable NSString *)profileName
{
@synchronized(self)
{
return _profileName.filterStringForDisplay;
}
}
- (void)setProfileName:(nullable NSString *)profileName
{
@synchronized(self)
{
_profileName = profileName.filterStringForDisplay;
}
}
#pragma mark - Profile Avatars Directory
+ (NSString *)profileAvatarFilepathWithFilename:(NSString *)filename
{
if (filename.length <= 0) { return @""; };
@ -504,8 +63,6 @@ NSString *const kLocalProfileUniqueId = @"kLocalProfileUniqueId";
return profileAvatarsDirPath;
}
// TODO: We may want to clean up this directory in the "orphan cleanup" logic.
+ (void)resetProfileStorage
{
NSError *error;
@ -517,22 +74,14 @@ NSString *const kLocalProfileUniqueId = @"kLocalProfileUniqueId";
NSString *profileAvatarsDirPath = self.profileAvatarsDirPath;
NSMutableSet<NSString *> *profileAvatarFilePaths = [NSMutableSet new];
[OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
[OWSUserProfile
enumerateCollectionObjectsWithTransaction:transaction
usingBlock:^(id object, BOOL *stop) {
if (![object isKindOfClass:[OWSUserProfile class]]) {
return;
}
OWSUserProfile *userProfile = object;
if (!userProfile.avatarFileName) {
return;
}
NSString *filePath = [profileAvatarsDirPath
stringByAppendingPathComponent:userProfile.avatarFileName];
[profileAvatarFilePaths addObject:filePath];
}];
}];
NSSet<SNContact *> *allContacts = [LKStorage.shared getAllContacts];
for (SNContact *contact in allContacts) {
if (contact.profilePictureFileName == nil) { continue; }
NSString *filePath = [profileAvatarsDirPath stringByAppendingPathComponent:contact.profilePictureFileName];
[profileAvatarFilePaths addObject:filePath];
}
return [profileAvatarFilePaths copy];
}

7
SessionMessagingKit/To Do/ProfileManagerProtocol.h

@ -5,7 +5,7 @@
@class OWSAES256Key;
@class TSThread;
@class YapDatabaseReadWriteTransaction;
@class OWSUserProfile;
@class SNContact;
NS_ASSUME_NONNULL_BEGIN
@ -13,7 +13,6 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - Local Profile
- (void)ensureLocalProfileCached;
- (void)updateServiceWithProfileName:(nullable NSString *)localProfileName avatarURL:(nullable NSString *)avatarURL;
#pragma mark - Other User's Profiles
@ -21,12 +20,10 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable NSData *)profileKeyDataForRecipientId:(NSString *)recipientId;
- (void)setProfileKeyData:(NSData *)profileKeyData forRecipientId:(NSString *)recipientId;
- (void)setProfileKeyData:(NSData *)profileKeyData forRecipientId:(NSString *)recipientId avatarURL:(nullable NSString *)avatarURL;
- (void)updateProfileForContactWithID:(NSString *)contactID displayName:(NSString *)displayName with:(YapDatabaseReadWriteTransaction *)transaction;
- (void)ensureProfileCachedForContactWithID:(NSString *)contactID with:(YapDatabaseReadWriteTransaction *)transaction;
#pragma mark - Other
- (void)downloadAvatarForUserProfile:(OWSUserProfile *)userProfile;
- (void)downloadAvatarForUserProfile:(SNContact *)userProfile;
@end

2
SessionMessagingKit/Utilities/FullTextSearchFinder.swift

@ -177,7 +177,7 @@ public class FullTextSearchFinder: NSObject {
}
private static let recipientIndexer: SearchIndexer<String> = SearchIndexer { (recipientId: String, transaction: YapDatabaseReadTransaction) in
let displayName = OWSUserProfile.fetch(uniqueId: recipientId, transaction: transaction)?.profileName ?? recipientId
let displayName = Storage.shared.getContact(with: recipientId)?.displayName(for: Contact.Context.regular) ?? recipientId
return "\(recipientId) \(displayName)"
}

5
SessionShareExtension/ShareVC.swift

@ -103,9 +103,6 @@ final class ShareVC : UINavigationController, ShareViewDelegate, AppModeManagerD
Logger.debug("")
// TODO: Once "app ready" logic is moved into AppSetup, move this line there.
OWSProfileManager.shared().ensureLocalProfileCached()
// Note that this does much more than set a flag;
// it will also run all deferred blocks.
AppReadiness.setAppIsReady()
@ -123,8 +120,6 @@ final class ShareVC : UINavigationController, ShareViewDelegate, AppModeManagerD
// We don't need to use OWSMessageReceiver in the SAE.
// We don't need to use OWSBatchMessageProcessor in the SAE.
OWSProfileManager.shared().ensureLocalProfileCached()
// We don't need to use OWSOrphanDataCleaner in the SAE.
// We don't need to fetch the local profile in the SAE

4
SignalUtilitiesKit/Database/Storage+Conformances.swift

@ -5,8 +5,4 @@ extension Storage : SessionMessagingKitStorageProtocol, SessionSnodeKitStoragePr
let transaction = transaction as! YapDatabaseReadWriteTransaction
OWSPrimaryStorage.shared().updateMessageIDCollectionByPruningMessagesWithIDs(messageIDs, in: transaction)
}
public func getUserProfile(using transaction: Any) -> OWSUserProfile {
return OWSProfileManager.shared().getLocalUserProfile(with: transaction as! YapDatabaseReadWriteTransaction)
}
}

14
SignalUtilitiesKit/Messaging/ConfigurationMessage+Convenience.swift

@ -31,18 +31,18 @@ extension ConfigurationMessage {
default: break
}
}
OWSUserProfile.enumerateCollectionObjects(with: transaction) { object, stop in
guard let profile = object as? OWSUserProfile, let displayName = profile.profileName else { return }
let publicKey = profile.recipientId
var truncatedContacts = storage.getAllContacts()
if truncatedContacts.count > 200 { truncatedContacts = Set(Array(truncatedContacts)[0..<200]) }
truncatedContacts.forEach { contact in
let publicKey = contact.sessionID
let threadID = TSContactThread.threadID(fromContactSessionID: publicKey)
guard let thread = TSContactThread.fetch(uniqueId: threadID, transaction: transaction), thread.shouldBeVisible
&& !SSKEnvironment.shared.blockingManager.isRecipientIdBlocked(publicKey) else { return }
let profilePictureURL = profile.avatarUrlPath
let profileKey = profile.profileKey?.keyData
let contact = ConfigurationMessage.Contact(publicKey: publicKey, displayName: displayName,
let profilePictureURL = contact.profilePictureURL
let profileKey = contact.profilePictureEncryptionKey?.keyData
let contact = ConfigurationMessage.Contact(publicKey: publicKey, displayName: contact.name ?? publicKey,
profilePictureURL: profilePictureURL, profileKey: profileKey)
contacts.insert(contact)
guard contactCount < 200 else { stop.pointee = true; return }
contactCount += 1
}
}

6
SignalUtilitiesKit/To Do/OWSProfileManager.h

@ -35,8 +35,6 @@ extern const NSUInteger kOWSProfileManager_MaxAvatarDiameter;
// hasLocalProfile is true if there is a local profile with a name or avatar.
- (BOOL)hasLocalProfile;
- (OWSUserProfile *)getLocalUserProfileWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
// This method is used to update the "local profile" state on the client
// and the service. Client state is only updated if service state is
// successfully updated.
@ -61,11 +59,9 @@ extern const NSUInteger kOWSProfileManager_MaxAvatarDiameter;
profileNameEncrypted:(nullable NSData *)profileNameEncrypted
avatarUrlPath:(nullable NSString *)avatarUrlPath;
- (void)ensureProfileCachedForContactWithID:(NSString *)contactID with:(YapDatabaseReadWriteTransaction *)transaction;
#pragma mark - Other
- (void)downloadAvatarForUserProfile:(OWSUserProfile *)userProfile;
- (void)downloadAvatarForUserProfile:(SNContact *)contact;
@end

564
SignalUtilitiesKit/To Do/OWSProfileManager.m

@ -45,9 +45,6 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
// This property can be accessed on any thread, while synchronized on self.
@property (atomic, readonly) OWSUserProfile *localUserProfile;
// This property can be accessed on any thread, while synchronized on self.
@property (atomic, readonly) NSCache<NSString *, UIImage *> *profileAvatarImageCache;
@ -62,8 +59,6 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
// Writes should happen off the main thread, wherever possible.
@implementation OWSProfileManager
@synthesize localUserProfile = _localUserProfile;
+ (instancetype)sharedManager
{
return SSKEnvironment.shared.profileManager;
@ -87,12 +82,6 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
OWSSingletonAssert();
[AppReadiness runNowOrWhenAppDidBecomeReady:^{
[self rotateLocalProfileKeyIfNecessary];
}];
[self observeNotifications];
return self;
}
@ -101,14 +90,6 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)observeNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(blockListDidChange:)
name:kNSNotificationName_BlockListDidChange
object:nil];
}
#pragma mark - Dependencies
- (TSAccountManager *)tsAccountManager
@ -126,78 +107,6 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
return SSKEnvironment.shared.blockingManager;
}
#pragma mark - User Profile Accessor
- (void)ensureLocalProfileCached
{
// Since localUserProfile can create a transaction, we want to make sure it's not called for the first
// time unexpectedly (e.g. in a nested transaction.)
__unused OWSUserProfile *profile = [self localUserProfile];
}
#pragma mark - Local Profile
- (OWSUserProfile *)localUserProfile
{
if (_localUserProfile) { return _localUserProfile; }
__block OWSUserProfile *userProfile;
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
userProfile = [self getLocalUserProfileWithTransaction:transaction];
}];
return userProfile;
}
- (OWSUserProfile *)getLocalUserProfileWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
@synchronized(self)
{
if (!_localUserProfile) {
_localUserProfile = [OWSUserProfile getOrBuildUserProfileForRecipientId:kLocalProfileUniqueId transaction:transaction];
}
}
OWSAssertDebug(_localUserProfile.profileKey);
return _localUserProfile;
}
- (BOOL)localProfileExists
{
return [OWSUserProfile localUserProfileExists:self.dbConnection];
}
- (OWSAES256Key *)localProfileKey
{
OWSAssertDebug(self.localUserProfile.profileKey.keyData.length == kAES256_KeyByteLength);
return self.localUserProfile.profileKey;
}
- (BOOL)hasLocalProfile
{
return (self.localProfileName.length > 0 || self.localProfileAvatarImage != nil);
}
- (nullable NSString *)localProfileName
{
return self.localUserProfile.profileName;
}
- (nullable UIImage *)localProfileAvatarImage
{
return [self loadProfileAvatarWithFilename:self.localUserProfile.avatarFileName];
}
- (nullable NSData *)localProfileAvatarData
{
NSString *_Nullable filename = self.localUserProfile.avatarFileName;
if (filename.length < 1) {
return nil;
}
return [self loadProfileDataWithFilename:filename];
}
- (void)updateLocalProfileName:(nullable NSString *)profileName
avatarImage:(nullable UIImage *)avatarImage
success:(void (^)(void))successBlockParameter
@ -232,27 +141,29 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
[self updateServiceWithProfileName:profileName
avatarUrl:avatarUrlPath
success:^{
OWSUserProfile *userProfile = self.localUserProfile;
SNContact *userProfile = [LKStorage.shared getUser];
OWSAssertDebug(userProfile);
[userProfile updateWithProfileName:profileName
avatarUrlPath:avatarUrlPath
avatarFileName:avatarFileName
dbConnection:self.dbConnection
completion:^{
if (avatarFileName) {
[self updateProfileAvatarCache:avatarImage filename:avatarFileName];
}
successBlock();
}];
userProfile.name = profileName;
userProfile.profilePictureURL = avatarUrlPath;
userProfile.profilePictureFileName = avatarFileName;
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[LKStorage.shared setContact:userProfile usingTransaction:transaction];
} completion:^{
if (avatarFileName != nil) {
[self updateProfileAvatarCache:avatarImage filename:avatarFileName];
}
successBlock();
}];
}
failure:^(NSError *error) {
failureBlock(error);
}];
};
OWSUserProfile *userProfile = self.localUserProfile;
SNContact *userProfile = [LKStorage.shared getUser];
OWSAssertDebug(userProfile);
if (avatarImage) {
@ -277,7 +188,7 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
failure:^(NSError *error) {
failureBlock(error);
}];
} else if (userProfile.avatarUrlPath) {
} else if (userProfile.profilePictureURL) {
OWSLogVerbose(@"Updating local profile on service with cleared avatar.");
[self uploadAvatarToService:nil
success:^(NSString *_Nullable avatarUrlPath) {
@ -292,11 +203,6 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
}
}
- (nullable NSString *)profilePictureURL
{
return self.localUserProfile.avatarUrlPath;
}
- (void)writeAvatarToDisk:(UIImage *)avatar
success:(void (^)(NSData *data, NSString *fileName))successBlock
failure:(ProfileManagerFailureBlock)failureBlock {
@ -367,8 +273,13 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
[promise.thenOn(dispatch_get_main_queue(), ^(NSString *fileID) {
NSString *downloadURL = [NSString stringWithFormat:@"%@/files/%@", SNFileServerAPIV2.server, fileID];
[NSUserDefaults.standardUserDefaults setObject:[NSDate new] forKey:@"lastProfilePictureUpload"];
[self.localUserProfile updateWithProfileKey:newProfileKey dbConnection:self.dbConnection completion:^{
successBlock(downloadURL);
SNContact *user = [LKStorage.shared getUser];
user.profilePictureEncryptionKey = newProfileKey;
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[LKStorage.shared setContact:user usingTransaction:transaction];
} completion:^{
successBlock(downloadURL);
}];
})
.catchOn(dispatch_get_main_queue(), ^(id result) {
@ -376,7 +287,11 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
// to be invoked with the fulfilled promise's value as the error. The below
// is a quick and dirty workaround.
if ([result isKindOfClass:NSString.class]) {
[self.localUserProfile updateWithProfileKey:newProfileKey dbConnection:self.dbConnection completion:^{
SNContact *user = [LKStorage.shared getUser];
user.profilePictureEncryptionKey = newProfileKey;
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[LKStorage.shared setContact:user usingTransaction:transaction];
} completion:^{
successBlock(result);
}];
} else {
@ -385,7 +300,13 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
}) retainUntilComplete];
} else {
// Update our profile key and set the url to nil if avatar data is nil
[self.localUserProfile updateWithProfileKey:newProfileKey dbConnection:self.dbConnection completion:^{
SNContact *user = [LKStorage.shared getUser];
user.profilePictureEncryptionKey = newProfileKey;
user.profilePictureURL = nil;
user.profilePictureFileName = nil;
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[LKStorage.shared setContact:user usingTransaction:transaction];
} completion:^{
successBlock(nil);
}];
}
@ -438,217 +359,18 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
return [groupId copy];
}
- (void)rotateLocalProfileKeyIfNecessary {
[self rotateLocalProfileKeyIfNecessaryWithSuccess:^{ } failure:^(NSError *error) { }];
}
- (void)rotateLocalProfileKeyIfNecessaryWithSuccess:(dispatch_block_t)success
failure:(ProfileManagerFailureBlock)failure {
OWSAssertDebug(AppReadiness.isAppReady);
if (!self.tsAccountManager.isRegistered) {
success();
return;
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSMutableSet<NSString *> *whitelistedRecipientIds = [NSMutableSet new];
NSMutableSet<NSData *> *whitelistedGroupIds = [NSMutableSet new];
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
[whitelistedRecipientIds
addObjectsFromArray:[transaction allKeysInCollection:kOWSProfileManager_UserWhitelistCollection]];
NSArray<NSString *> *whitelistedGroupKeys =
[transaction allKeysInCollection:kOWSProfileManager_GroupWhitelistCollection];
for (NSString *groupKey in whitelistedGroupKeys) {
NSData *_Nullable groupId = [self groupIdForGroupKey:groupKey];
if (!groupId) {
OWSFailDebug(@"Couldn't parse group key: %@.", groupKey);
continue;
}
[whitelistedGroupIds addObject:groupId];
// Note we don't add `group.recipientIds` to the `whitelistedRecipientIds`.
//
// Whenever we message a contact, be it in a 1:1 thread or in a group thread,
// we add them to the contact whitelist, so there's no reason to redundnatly
// add them here.
//
// Furthermore, doing so would cause the following problem:
// - Alice is in group Book Club
// - Add Book Club to your profile white list
// - Message Book Club, which also adds Alice to your profile whitelist.
// - Block Alice, but not Book Club
//
// Now, at this point we'd want to rotate our profile key once, since Alice has
// it via BookClub.
//
// However, after we did. The next time we check if we should rotate our profile
// key, adding all `group.recipientIds` to `whitelistedRecipientIds` here, would
// include Alice, and we'd rotate our profile key every time this method is called.
}
}];
NSString *_Nullable localNumber = [TSAccountManager localNumber];
if (localNumber) {
[whitelistedRecipientIds removeObject:localNumber];
} else {
OWSFailDebug(@"Missing localNumber");
}
NSSet<NSString *> *blockedRecipientIds = [NSSet setWithArray:self.blockingManager.blockedPhoneNumbers];
NSSet<NSData *> *blockedGroupIds = [NSSet setWithArray:self.blockingManager.blockedGroupIds];
// Find the users and groups which are both a) blocked b) may have our current profile key.
NSMutableSet<NSString *> *intersectingRecipientIds = [blockedRecipientIds mutableCopy];
[intersectingRecipientIds intersectSet:whitelistedRecipientIds];
NSMutableSet<NSData *> *intersectingGroupIds = [blockedGroupIds mutableCopy];
[intersectingGroupIds intersectSet:whitelistedGroupIds];
BOOL isProfileKeySharedWithBlocked = (intersectingRecipientIds.count > 0 || intersectingGroupIds.count > 0);
if (!isProfileKeySharedWithBlocked) {
// No need to rotate the profile key.
return success();
}
[self rotateProfileKeyWithIntersectingRecipientIds:intersectingRecipientIds
intersectingGroupIds:intersectingGroupIds
success:success
failure:failure];
});
}
- (void)rotateProfileKeyWithIntersectingRecipientIds:(NSSet<NSString *> *)intersectingRecipientIds
intersectingGroupIds:(NSSet<NSData *> *)intersectingGroupIds
success:(dispatch_block_t)success
failure:(ProfileManagerFailureBlock)failure {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Rotate the profile key
OWSLogInfo(@"Rotating the profile key.");
// Make copies of the current local profile state.
OWSUserProfile *localUserProfile = self.localUserProfile;
NSString *_Nullable oldProfileName = localUserProfile.profileName;
NSString *_Nullable oldAvatarFileName = localUserProfile.avatarFileName;
NSData *_Nullable oldAvatarData = [self profileAvatarDataForRecipientId:self.tsAccountManager.localNumber];
// Rotate the stored profile key.
AnyPromise *promise = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
[self.localUserProfile updateWithProfileKey:[OWSAES256Key generateRandomKey]
dbConnection:self.dbConnection
completion:^{
// The value doesn't matter, we just need any non-NSError value.
resolve(@(1));
}];
}];
// Try to re-upload our profile name, if any.
//
// This may fail.
promise = promise.then(^(id value) {
if (oldProfileName.length < 1) {
return [AnyPromise promiseWithValue:@(1)];
}
return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
[self updateServiceWithProfileName:oldProfileName
avatarUrl:localUserProfile.avatarUrlPath
success:^{
OWSLogInfo(@"Update to profile name succeeded.");
// The value doesn't matter, we just need any non-NSError value.