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.
This commit is contained in:
parent
41fe33525c
commit
519ffa4405
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)"
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
resolve(@(1));
|
||||
}
|
||||
failure:^(NSError *error) {
|
||||
resolve(error);
|
||||
}];
|
||||
}];
|
||||
});
|
||||
|
||||
// Try to re-upload our profile avatar, if any.
|
||||
//
|
||||
// This may fail.
|
||||
promise = promise.then(^(id value) {
|
||||
if (oldAvatarData.length < 1 || oldAvatarFileName.length < 1) {
|
||||
return [AnyPromise promiseWithValue:@(1)];
|
||||
}
|
||||
return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
|
||||
[self uploadAvatarToService:oldAvatarData
|
||||
success:^(NSString *_Nullable avatarUrlPath) {
|
||||
OWSLogInfo(@"Update to profile avatar after profile key rotation succeeded.");
|
||||
// The profile manager deletes the underlying file when updating a profile URL
|
||||
// So we need to copy the underlying file to a new location.
|
||||
NSString *oldPath = [OWSUserProfile profileAvatarFilepathWithFilename:oldAvatarFileName];
|
||||
NSString *newAvatarFilename = [self generateAvatarFilename];
|
||||
NSString *newPath = [OWSUserProfile profileAvatarFilepathWithFilename:newAvatarFilename];
|
||||
NSError *error;
|
||||
[NSFileManager.defaultManager copyItemAtPath:oldPath toPath:newPath error:&error];
|
||||
OWSAssertDebug(!error);
|
||||
|
||||
[self.localUserProfile updateWithAvatarUrlPath:avatarUrlPath
|
||||
avatarFileName:newAvatarFilename
|
||||
dbConnection:self.dbConnection
|
||||
completion:^{
|
||||
// The value doesn't matter, we just need any
|
||||
// non-NSError value.
|
||||
resolve(@(1));
|
||||
}];
|
||||
}
|
||||
failure:^(NSError *error) {
|
||||
OWSLogInfo(@"Update to profile avatar after profile key rotation failed.");
|
||||
resolve(error);
|
||||
}];
|
||||
}];
|
||||
});
|
||||
|
||||
// Try to re-upload our profile avatar, if any.
|
||||
//
|
||||
// This may fail.
|
||||
promise = promise.then(^(id value) {
|
||||
// Remove blocked users and groups from profile whitelist.
|
||||
//
|
||||
// This will always succeed.
|
||||
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[transaction removeObjectsForKeys:intersectingRecipientIds.allObjects
|
||||
inCollection:kOWSProfileManager_UserWhitelistCollection];
|
||||
for (NSData *groupId in intersectingGroupIds) {
|
||||
NSString *groupIdKey = [self groupKeyForGroupId:groupId];
|
||||
[transaction removeObjectForKey:groupIdKey
|
||||
inCollection:kOWSProfileManager_GroupWhitelistCollection];
|
||||
}
|
||||
}];
|
||||
return @(1);
|
||||
});
|
||||
|
||||
// Update account attributes.
|
||||
//
|
||||
// This may fail.
|
||||
promise = promise.then(^(id value) {
|
||||
return [self.tsAccountManager updateAccountAttributes];
|
||||
});
|
||||
|
||||
promise = promise.then(^(id value) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationNameAsync:kNSNotificationName_ProfileKeyDidChange
|
||||
object:nil
|
||||
userInfo:nil];
|
||||
|
||||
success();
|
||||
});
|
||||
promise = promise.catch(^(NSError *error) {
|
||||
if ([error isKindOfClass:[NSError class]]) {
|
||||
failure(error);
|
||||
} else {
|
||||
failure(OWSErrorMakeAssertionError(@"Profile key rotation failure missing error."));
|
||||
}
|
||||
});
|
||||
[promise retainUntilComplete];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)regenerateLocalProfile
|
||||
{
|
||||
OWSUserProfile *userProfile = self.localUserProfile;
|
||||
[userProfile clearWithProfileKey:[OWSAES256Key generateRandomKey] dbConnection:self.dbConnection completion:nil];
|
||||
[[self.tsAccountManager updateAccountAttributes] retainUntilComplete];
|
||||
NSString *userPublicKey = [SNGeneralUtilities getUserPublicKey];
|
||||
SNContact *contact = [LKStorage.shared getContactWithSessionID:userPublicKey];
|
||||
contact.profilePictureEncryptionKey = [OWSAES256Key generateRandomKey];
|
||||
contact.profilePictureURL = nil;
|
||||
contact.profilePictureFileName = nil;
|
||||
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[LKStorage.shared setContact:contact usingTransaction:transaction];
|
||||
} completion:^{
|
||||
[[self.tsAccountManager updateAccountAttributes] retainUntilComplete];
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Other Users' Profiles
|
||||
|
@ -662,22 +384,27 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
|
|||
return;
|
||||
}
|
||||
|
||||
OWSUserProfile *userProfile = [OWSUserProfile getOrBuildUserProfileForRecipientId:recipientId dbConnection:self.dbConnection];
|
||||
SNContact *contact = [LKStorage.shared getContactWithSessionID:recipientId];
|
||||
|
||||
OWSAssertDebug(userProfile);
|
||||
if (userProfile.profileKey && [userProfile.profileKey.keyData isEqual:profileKey.keyData]) {
|
||||
OWSAssertDebug(contact);
|
||||
if (contact.profilePictureEncryptionKey != nil && [contact.profilePictureEncryptionKey.keyData isEqual:profileKey.keyData]) {
|
||||
// Ignore redundant update.
|
||||
return;
|
||||
}
|
||||
|
||||
[userProfile clearWithProfileKey:profileKey
|
||||
dbConnection:self.dbConnection
|
||||
completion:^{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[userProfile updateWithAvatarUrlPath:avatarURL avatarFileName:nil dbConnection:self.dbConnection completion:^{
|
||||
[self downloadAvatarForUserProfile:userProfile];
|
||||
}];
|
||||
});
|
||||
contact.profilePictureEncryptionKey = profileKey;
|
||||
contact.profilePictureURL = nil;
|
||||
contact.profilePictureFileName = nil;
|
||||
|
||||
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[LKStorage.shared setContact:contact usingTransaction:transaction];
|
||||
} completion:^{
|
||||
contact.profilePictureURL = avatarURL;
|
||||
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[LKStorage.shared setContact:contact usingTransaction:transaction];
|
||||
} completion:^{
|
||||
[self downloadAvatarForUserProfile:contact];
|
||||
}];
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
@ -696,42 +423,24 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
|
|||
{
|
||||
OWSAssertDebug(recipientId.length > 0);
|
||||
|
||||
// For "local reads", use the local user profile.
|
||||
OWSUserProfile *userProfile = ([self.tsAccountManager.localNumber isEqualToString:recipientId]
|
||||
? self.localUserProfile
|
||||
: [OWSUserProfile getOrBuildUserProfileForRecipientId:recipientId dbConnection:self.dbConnection]);
|
||||
OWSAssertDebug(userProfile);
|
||||
SNContact *contact = [LKStorage.shared getContactWithSessionID:recipientId];
|
||||
OWSAssertDebug(contact);
|
||||
|
||||
return userProfile.profileKey;
|
||||
}
|
||||
|
||||
- (nullable NSString *)profileNameForRecipientWithID:(NSString *)recipientID transaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
OWSAssertDebug(recipientID.length > 0);
|
||||
|
||||
// For "local reads", use the local user profile.
|
||||
OWSUserProfile *userProfile = [self.tsAccountManager.localNumber isEqualToString:recipientID]
|
||||
? [self getLocalUserProfileWithTransaction:transaction]
|
||||
: [OWSUserProfile getOrBuildUserProfileForRecipientId:recipientID transaction:transaction];
|
||||
|
||||
return userProfile.profileName;
|
||||
return contact.profilePictureEncryptionKey;
|
||||
}
|
||||
|
||||
- (nullable UIImage *)profileAvatarForRecipientId:(NSString *)recipientId
|
||||
{
|
||||
OWSAssertDebug(recipientId.length > 0);
|
||||
|
||||
// For "local reads", use the local user profile.
|
||||
OWSUserProfile *userProfile = ([self.tsAccountManager.localNumber isEqualToString:recipientId]
|
||||
? self.localUserProfile
|
||||
: [OWSUserProfile getOrBuildUserProfileForRecipientId:recipientId dbConnection:self.dbConnection]);
|
||||
|
||||
if (userProfile.avatarFileName.length > 0) {
|
||||
return [self loadProfileAvatarWithFilename:userProfile.avatarFileName];
|
||||
SNContact *contact = [LKStorage.shared getContactWithSessionID:recipientId];
|
||||
|
||||
if (contact.profilePictureFileName != nil && contact.profilePictureFileName.length > 0) {
|
||||
return [self loadProfileAvatarWithFilename:contact.profilePictureFileName];
|
||||
}
|
||||
|
||||
if (userProfile.avatarUrlPath.length > 0) {
|
||||
[self downloadAvatarForUserProfile:userProfile];
|
||||
if (contact.profilePictureURL != nil && contact.profilePictureURL.length > 0) {
|
||||
[self downloadAvatarForUserProfile:contact];
|
||||
}
|
||||
|
||||
return nil;
|
||||
|
@ -741,13 +450,10 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
|
|||
{
|
||||
OWSAssertDebug(recipientId.length > 0);
|
||||
|
||||
// For "local reads", use the local user profile.
|
||||
OWSUserProfile *userProfile = ([self.tsAccountManager.localNumber isEqualToString:recipientId]
|
||||
? self.localUserProfile
|
||||
: [OWSUserProfile getOrBuildUserProfileForRecipientId:recipientId dbConnection:self.dbConnection]);
|
||||
SNContact *contact = [LKStorage.shared getContactWithSessionID:recipientId];
|
||||
|
||||
if (userProfile.avatarFileName.length > 0) {
|
||||
return [self loadProfileDataWithFilename:userProfile.avatarFileName];
|
||||
if (contact.profilePictureFileName != nil && contact.profilePictureFileName.length > 0) {
|
||||
return [self loadProfileDataWithFilename:contact.profilePictureFileName];
|
||||
}
|
||||
|
||||
return nil;
|
||||
|
@ -758,40 +464,42 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
|
|||
return [[NSUUID UUID].UUIDString stringByAppendingPathExtension:@"jpg"];
|
||||
}
|
||||
|
||||
- (void)downloadAvatarForUserProfile:(OWSUserProfile *)userProfile
|
||||
- (void)downloadAvatarForUserProfile:(SNContact *)contact
|
||||
{
|
||||
OWSAssertDebug(userProfile);
|
||||
OWSAssertDebug(contact);
|
||||
|
||||
__block OWSBackgroundTask *backgroundTask = [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__];
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
if (userProfile.avatarUrlPath.length < 1) {
|
||||
OWSLogDebug(@"Skipping downloading avatar for %@ because url is not set", userProfile.recipientId);
|
||||
BOOL hasProfilePictureURL = (contact.profilePictureURL != nil && contact.profilePictureURL.length > 0);
|
||||
if (!hasProfilePictureURL) {
|
||||
OWSLogDebug(@"Skipping downloading avatar for %@ because url is not set", contact.sessionID);
|
||||
return;
|
||||
}
|
||||
NSString *_Nullable avatarUrlPathAtStart = userProfile.avatarUrlPath;
|
||||
NSString *_Nullable avatarUrlPathAtStart = contact.profilePictureURL;
|
||||
|
||||
if (userProfile.profileKey.keyData.length < 1 || userProfile.avatarUrlPath.length < 1) {
|
||||
BOOL hasProfileEncryptionKey = (contact.profilePictureEncryptionKey != nil && contact.profilePictureEncryptionKey.keyData.length > 0);
|
||||
if (!hasProfileEncryptionKey || !hasProfilePictureURL) {
|
||||
return;
|
||||
}
|
||||
|
||||
OWSAES256Key *profileKeyAtStart = userProfile.profileKey;
|
||||
OWSAES256Key *profileKeyAtStart = contact.profilePictureEncryptionKey;
|
||||
|
||||
NSString *fileName = [self generateAvatarFilename];
|
||||
NSString *filePath = [OWSUserProfile profileAvatarFilepathWithFilename:fileName];
|
||||
|
||||
@synchronized(self.currentAvatarDownloads)
|
||||
{
|
||||
if ([self.currentAvatarDownloads containsObject:userProfile.recipientId]) {
|
||||
if ([self.currentAvatarDownloads containsObject:contact.sessionID]) {
|
||||
// Download already in flight; ignore.
|
||||
return;
|
||||
}
|
||||
[self.currentAvatarDownloads addObject:userProfile.recipientId];
|
||||
[self.currentAvatarDownloads addObject:contact.sessionID];
|
||||
}
|
||||
|
||||
OWSLogVerbose(@"downloading profile avatar: %@", userProfile.uniqueId);
|
||||
OWSLogVerbose(@"downloading profile avatar: %@", contact.sessionID);
|
||||
|
||||
NSString *profilePictureURL = userProfile.avatarUrlPath;
|
||||
NSString *profilePictureURL = contact.profilePictureURL;
|
||||
|
||||
NSString *file = [profilePictureURL lastPathComponent];
|
||||
BOOL useOldServer = [profilePictureURL containsString:SNFileServerAPIV2.oldServer];
|
||||
|
@ -800,7 +508,7 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
|
|||
[promise.then(^(NSData *data) {
|
||||
@synchronized(self.currentAvatarDownloads)
|
||||
{
|
||||
[self.currentAvatarDownloads removeObject:userProfile.recipientId];
|
||||
[self.currentAvatarDownloads removeObject:contact.sessionID];
|
||||
}
|
||||
NSData *_Nullable encryptedData = data;
|
||||
NSData *_Nullable decryptedData = [self decryptProfileData:encryptedData profileKey:profileKeyAtStart];
|
||||
|
@ -812,24 +520,28 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
|
|||
}
|
||||
}
|
||||
|
||||
OWSUserProfile *latestUserProfile = [OWSUserProfile getOrBuildUserProfileForRecipientId:userProfile.recipientId dbConnection:self.dbConnection];
|
||||
SNContact *latestContact = [LKStorage.shared getContactWithSessionID:contact.sessionID];
|
||||
|
||||
if (latestUserProfile.profileKey.keyData.length < 1
|
||||
|| ![latestUserProfile.profileKey isEqual:userProfile.profileKey]) {
|
||||
BOOL hasProfileEncryptionKey = (latestContact.profilePictureEncryptionKey != nil
|
||||
&& latestContact.profilePictureEncryptionKey.keyData.length > 0);
|
||||
if (!hasProfileEncryptionKey || ![latestContact.profilePictureEncryptionKey isEqual:contact.profilePictureEncryptionKey]) {
|
||||
OWSLogWarn(@"Ignoring avatar download for obsolete user profile.");
|
||||
} else if (![avatarUrlPathAtStart isEqualToString:latestUserProfile.avatarUrlPath]) {
|
||||
} else if (![avatarUrlPathAtStart isEqualToString:latestContact.profilePictureURL]) {
|
||||
OWSLogInfo(@"avatar url has changed during download");
|
||||
if (latestUserProfile.avatarUrlPath.length > 0) {
|
||||
[self downloadAvatarForUserProfile:latestUserProfile];
|
||||
if (latestContact.profilePictureURL != nil && latestContact.profilePictureURL.length > 0) {
|
||||
[self downloadAvatarForUserProfile:latestContact];
|
||||
}
|
||||
} else if (!encryptedData) {
|
||||
OWSLogError(@"avatar encrypted data for %@ could not be read.", userProfile.recipientId);
|
||||
OWSLogError(@"avatar encrypted data for %@ could not be read.", contact.sessionID);
|
||||
} else if (!decryptedData) {
|
||||
OWSLogError(@"avatar data for %@ could not be decrypted.", userProfile.recipientId);
|
||||
OWSLogError(@"avatar data for %@ could not be decrypted.", contact.sessionID);
|
||||
} else if (!image) {
|
||||
OWSLogError(@"avatar image for %@ could not be loaded.", userProfile.recipientId);
|
||||
OWSLogError(@"avatar image for %@ could not be loaded.", contact.sessionID);
|
||||
} else {
|
||||
[latestUserProfile updateWithAvatarFileName:fileName dbConnection:self.dbConnection completion:nil];
|
||||
latestContact.profilePictureFileName = fileName;
|
||||
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[LKStorage.shared setContact:latestContact usingTransaction:transaction];
|
||||
}];
|
||||
[self updateProfileAvatarCache:image filename:fileName];
|
||||
}
|
||||
|
||||
|
@ -849,64 +561,28 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
|
|||
|
||||
// Ensure decryption, etc. off main thread.
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
OWSUserProfile *userProfile =
|
||||
[OWSUserProfile getOrBuildUserProfileForRecipientId:recipientId dbConnection:self.dbConnection];
|
||||
SNContact *contact = [LKStorage.shared getContactWithSessionID:recipientId];
|
||||
|
||||
NSString *_Nullable localNumber = self.tsAccountManager.localNumber;
|
||||
// If we're updating the profile that corresponds to our local number,
|
||||
// make sure we're using the latest key.
|
||||
if (localNumber && [localNumber isEqualToString:recipientId]) {
|
||||
[userProfile updateWithProfileKey:self.localUserProfile.profileKey
|
||||
dbConnection:self.dbConnection
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
if (!userProfile.profileKey) {
|
||||
return;
|
||||
}
|
||||
if (!contact.profilePictureEncryptionKey) { return; }
|
||||
|
||||
NSString *_Nullable profileName =
|
||||
[self decryptProfileNameData:profileNameEncrypted profileKey:userProfile.profileKey];
|
||||
|
||||
[userProfile updateWithProfileName:profileName
|
||||
avatarUrlPath:avatarUrlPath
|
||||
dbConnection:self.dbConnection
|
||||
completion:nil];
|
||||
|
||||
// If we're updating the profile that corresponds to our local number,
|
||||
// update the local profile as well.
|
||||
if (localNumber && [localNumber isEqualToString:recipientId]) {
|
||||
OWSUserProfile *localUserProfile = self.localUserProfile;
|
||||
OWSAssertDebug(localUserProfile);
|
||||
|
||||
[localUserProfile updateWithProfileName:profileName
|
||||
avatarUrlPath:avatarUrlPath
|
||||
dbConnection:self.dbConnection
|
||||
completion:nil];
|
||||
}
|
||||
[self decryptProfileNameData:profileNameEncrypted profileKey:contact.profilePictureEncryptionKey];
|
||||
|
||||
contact.name = profileName;
|
||||
contact.profilePictureURL = avatarUrlPath;
|
||||
|
||||
// Whenever we change avatarUrlPath, OWSUserProfile clears avatarFileName.
|
||||
// So if avatarUrlPath is set and avatarFileName is not set, we should to
|
||||
// download this avatar. downloadAvatarForUserProfile will de-bounce
|
||||
// downloads.
|
||||
if (userProfile.avatarUrlPath.length > 0 && userProfile.avatarFileName.length < 1) {
|
||||
[self downloadAvatarForUserProfile:userProfile];
|
||||
BOOL hasProfilePictureURL = (contact.profilePictureURL != nil && contact.profilePictureURL.length > 0);
|
||||
BOOL hasProfilePictureFileName = (contact.profilePictureFileName != nil && contact.profilePictureFileName.length > 0);
|
||||
if (hasProfilePictureURL && !hasProfilePictureFileName) {
|
||||
[self downloadAvatarForUserProfile:contact];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)updateProfileForContactWithID:(NSString *)contactID displayName:(NSString *)displayName with:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
OWSUserProfile *userProfile = [OWSUserProfile getOrBuildUserProfileForRecipientId:contactID transaction:transaction];
|
||||
[userProfile updateWithProfileName:displayName avatarUrlPath:userProfile.avatarUrlPath avatarFileName:userProfile.avatarFileName transaction:transaction completion:nil];
|
||||
}
|
||||
|
||||
- (void)ensureProfileCachedForContactWithID:(NSString *)contactID with:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
OWSUserProfile *userProfile = [OWSUserProfile getOrBuildUserProfileForRecipientId:contactID transaction:transaction];
|
||||
[userProfile saveWithTransaction:transaction];
|
||||
}
|
||||
|
||||
- (BOOL)isNullableDataEqual:(NSData *_Nullable)left toData:(NSData *_Nullable)right
|
||||
{
|
||||
if (left == nil && right == nil) {
|
||||
|
@ -983,7 +659,9 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
|
|||
|
||||
- (nullable NSData *)encryptProfileData:(nullable NSData *)data
|
||||
{
|
||||
return [self encryptProfileData:data profileKey:self.localProfileKey];
|
||||
OWSAES256Key *localProfileKey = [LKStorage.shared getUser].profilePictureEncryptionKey;
|
||||
|
||||
return [self encryptProfileData:data profileKey:localProfileKey];
|
||||
}
|
||||
|
||||
- (BOOL)isProfileNameTooLong:(nullable NSString *)profileName
|
||||
|
@ -1010,7 +688,9 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
|
|||
[paddedNameData increaseLengthBy:paddingByteCount];
|
||||
OWSAssertDebug(paddedNameData.length == kOWSProfileManager_NameDataLength);
|
||||
|
||||
return [self encryptProfileData:[paddedNameData copy] profileKey:self.localProfileKey];
|
||||
OWSAES256Key *localProfileKey = [LKStorage.shared getUser].profilePictureEncryptionKey;
|
||||
|
||||
return [self encryptProfileData:[paddedNameData copy] profileKey:localProfileKey];
|
||||
}
|
||||
|
||||
#pragma mark - Avatar Disk Cache
|
||||
|
@ -1061,16 +741,6 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
|
|||
}
|
||||
}
|
||||
|
||||
#pragma mark - Notifications
|
||||
|
||||
- (void)blockListDidChange:(NSNotification *)notification {
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
[AppReadiness runNowOrWhenAppDidBecomeReady:^{
|
||||
[self rotateLocalProfileKeyIfNecessary];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
Loading…
Reference in New Issue