Fix profile avatar flicker

- We were clobbering our saved avatar filepath.
- Our "should notify" check was too aggressive.
- Only fetch profiles when entering a conversation.
- Only fetch profiles in main app

Also added (a little) debounce time to debug profile fetching.

// FREEBIE
This commit is contained in:
sdkjfhsdkjhfsdlkjhfsdf 2017-12-21 16:58:29 -06:00
parent 34d2df8d67
commit 14723f3e7f
6 changed files with 25 additions and 112 deletions

View File

@ -76,8 +76,6 @@ extern const NSUInteger kOWSProfileManager_MaxAvatarDiameter;
- (nullable UIImage *)profileAvatarForRecipientId:(NSString *)recipientId;
- (void)refreshProfileForRecipientId:(NSString *)recipientId;
- (void)updateProfileForRecipientId:(NSString *)recipientId
profileNameEncrypted:(nullable NSData *)profileNameEncrypted
avatarUrlPath:(nullable NSString *)avatarUrlPath;

View File

@ -722,6 +722,7 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
OWSUserProfile *userProfile =
[OWSUserProfile getOrBuildUserProfileForRecipientId:recipientId dbConnection:self.dbConnection];
OWSAssert(userProfile);
if (userProfile.profileKey && [userProfile.profileKey.keyData isEqual:profileKey.keyData]) {
// Ignore redundant update.
@ -734,7 +735,9 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
avatarFileName:nil
dbConnection:self.dbConnection
completion:^{
[self refreshProfileForRecipientId:recipientId ignoreThrottling:YES];
[ProfileFetcherJob runWithRecipientId:recipientId
networkManager:self.networkManager
ignoreThrottling:YES];
}];
});
}
@ -751,6 +754,7 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
OWSUserProfile *userProfile =
[OWSUserProfile getOrBuildUserProfileForRecipientId:recipientId dbConnection:self.dbConnection];
OWSAssert(userProfile);
return userProfile.profileKey;
}
@ -758,25 +762,24 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
{
OWSAssert(recipientId.length > 0);
[self refreshProfileForRecipientId:recipientId];
OWSUserProfile *userProfile =
[OWSUserProfile getOrBuildUserProfileForRecipientId:recipientId dbConnection:self.dbConnection];
return userProfile.profileName;
return self.localUserProfile.profileName;
}
- (nullable UIImage *)profileAvatarForRecipientId:(NSString *)recipientId
{
OWSAssert(recipientId.length > 0);
[self refreshProfileForRecipientId:recipientId];
OWSUserProfile *userProfile =
[OWSUserProfile getOrBuildUserProfileForRecipientId:recipientId dbConnection:self.dbConnection];
if (userProfile.avatarFileName.length > 0) {
return [self loadProfileAvatarWithFilename:userProfile.avatarFileName];
} else if (userProfile.avatarUrlPath.length > 0) {
}
if (userProfile.avatarUrlPath.length > 0) {
[self downloadAvatarForUserProfile:userProfile];
}
@ -892,46 +895,6 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
});
}
- (void)refreshProfileForRecipientId:(NSString *)recipientId
{
[self refreshProfileForRecipientId:recipientId ignoreThrottling:NO];
}
- (void)refreshProfileForRecipientId:(NSString *)recipientId ignoreThrottling:(BOOL)ignoreThrottling
{
OWSAssert(recipientId.length > 0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
OWSUserProfile *userProfile =
[OWSUserProfile getOrBuildUserProfileForRecipientId:recipientId dbConnection:self.dbConnection];
if (!userProfile.profileKey) {
// There's no point in fetching the profile for a user
// if we don't have their profile key; we won't be able
// to decrypt it.
return;
}
// Throttle and debounce the updates.
const NSTimeInterval kMaxRefreshFrequency = 5 * kMinuteInterval;
if (userProfile.lastUpdateDate
&& fabs([userProfile.lastUpdateDate timeIntervalSinceNow]) < kMaxRefreshFrequency) {
// This profile was updated recently or already has an update in flight.
return;
}
[userProfile updateWithLastUpdateDate:[NSDate new]
dbConnection:self.dbConnection
completion:^{
dispatch_async(dispatch_get_main_queue(), ^{
[ProfileFetcherJob runWithRecipientId:recipientId
networkManager:self.networkManager
ignoreThrottling:ignoreThrottling];
});
}];
});
}
- (void)updateProfileForRecipientId:(NSString *)recipientId
profileNameEncrypted:(nullable NSData *)profileNameEncrypted
avatarUrlPath:(nullable NSString *)avatarUrlPath;
@ -959,8 +922,7 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
[userProfile updateWithProfileName:profileName
avatarUrlPath:avatarUrlPath
avatarFileName:nil
lastUpdateDate:[NSDate new]
avatarFileName:userProfile.avatarFileName // use existing file name if already downloaded
dbConnection:self.dbConnection
completion:nil];
@ -978,7 +940,6 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
// downloaded the latest avatar by downloadAvatarForUserProfile.
[localUserProfile updateWithProfileName:profileName
avatarUrlPath:avatarUrlPath
lastUpdateDate:[NSDate new]
dbConnection:self.dbConnection
completion:nil];
}

View File

@ -32,12 +32,6 @@ extern NSString *const kLocalProfileUniqueId;
// This filename is relative to OWSProfileManager.profileAvatarsDirPath.
@property (atomic, readonly, nullable) NSString *avatarFileName;
// This should reflect when either:
//
// * The last successful update finished.
// * The current in-flight update began.
@property (atomic, readonly, nullable) NSDate *lastUpdateDate;
- (instancetype)init NS_UNAVAILABLE;
+ (OWSUserProfile *)getOrBuildUserProfileForRecipientId:(NSString *)recipientId
@ -62,14 +56,6 @@ extern NSString *const kLocalProfileUniqueId;
- (void)updateWithProfileName:(nullable NSString *)profileName
avatarUrlPath:(nullable NSString *)avatarUrlPath
avatarFileName:(nullable NSString *)avatarFileName
lastUpdateDate:(nullable NSDate *)lastUpdateDate
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion;
- (void)updateWithProfileName:(nullable NSString *)profileName
avatarUrlPath:(nullable NSString *)avatarUrlPath
lastUpdateDate:(nullable NSDate *)lastUpdateDate
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion;
@ -82,10 +68,6 @@ extern NSString *const kLocalProfileUniqueId;
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion;
- (void)updateWithLastUpdateDate:(nullable NSDate *)lastUpdateDate
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion;
- (void)clearWithProfileKey:(OWSAES256Key *)profileKey
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion;

View File

@ -29,7 +29,6 @@ NSString *const kLocalProfileUniqueId = @"kLocalProfileUniqueId";
@property (atomic, nullable) NSString *profileName;
@property (atomic, nullable) NSString *avatarUrlPath;
@property (atomic, nullable) NSString *avatarFileName;
@property (atomic, nullable) NSDate *lastUpdateDate;
@end
@ -187,32 +186,12 @@ NSString *const kLocalProfileUniqueId = @"kLocalProfileUniqueId";
- (void)updateWithProfileName:(nullable NSString *)profileName
avatarUrlPath:(nullable NSString *)avatarUrlPath
avatarFileName:(nullable NSString *)avatarFileName
lastUpdateDate:(nullable NSDate *)lastUpdateDate
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion
{
[self applyChanges:^(OWSUserProfile *userProfile) {
[userProfile setProfileName:[profileName ows_stripped]];
[userProfile setAvatarUrlPath:avatarUrlPath];
[userProfile setAvatarFileName:avatarFileName];
[userProfile setLastUpdateDate:lastUpdateDate];
}
functionName:__PRETTY_FUNCTION__
dbConnection:dbConnection
completion:completion];
}
- (void)updateWithProfileName:(nullable NSString *)profileName
avatarUrlPath:(nullable NSString *)avatarUrlPath
lastUpdateDate:(nullable NSDate *)lastUpdateDate
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion
{
[self applyChanges:^(OWSUserProfile *userProfile) {
[userProfile setProfileName:[profileName ows_stripped]];
[userProfile setAvatarUrlPath:avatarUrlPath];
[userProfile setLastUpdateDate:lastUpdateDate];
}
functionName:__PRETTY_FUNCTION__
dbConnection:dbConnection
@ -263,18 +242,6 @@ NSString *const kLocalProfileUniqueId = @"kLocalProfileUniqueId";
completion:completion];
}
- (void)updateWithLastUpdateDate:(nullable NSDate *)lastUpdateDate
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion
{
[self applyChanges:^(OWSUserProfile *userProfile) {
[userProfile setLastUpdateDate:lastUpdateDate];
}
functionName:__PRETTY_FUNCTION__
dbConnection:dbConnection
completion:completion];
}
- (void)clearWithProfileKey:(OWSAES256Key *)profileKey
dbConnection:(YapDatabaseConnection *)dbConnection
completion:(nullable OWSUserProfileCompletion)completion;
@ -284,7 +251,6 @@ NSString *const kLocalProfileUniqueId = @"kLocalProfileUniqueId";
[userProfile setProfileName:nil];
[userProfile setAvatarUrlPath:nil];
[userProfile setAvatarFileName:nil];
[userProfile setLastUpdateDate:nil];
}
functionName:__PRETTY_FUNCTION__
dbConnection:dbConnection
@ -338,15 +304,14 @@ NSString *const kLocalProfileUniqueId = @"kLocalProfileUniqueId";
// This should only be used in verbose, developer-only logs.
- (NSString *)debugDescription
{
return [NSString stringWithFormat:@"%@ %p %@ %zd %@ %@ %@ %f",
return [NSString stringWithFormat:@"%@ %p %@ %zd %@ %@ %@",
self.logTag,
self,
self.recipientId,
self.profileKey.keyData.length,
self.profileName,
self.avatarUrlPath,
self.avatarFileName,
self.lastUpdateDate.timeIntervalSinceNow];
self.avatarFileName];
}
@end

View File

@ -38,6 +38,13 @@ public class ProfileFetcherJob: NSObject {
public func run(recipientIds: [String]) {
AssertIsOnMainThread()
if (!CurrentAppContext().isMainApp) {
// Only refresh profiles in the MainApp to decrease the chance of missed SN notifications
// in the AppExtension for our users who choose not to verify contacts.
owsFail("Should only fetch profiles in the main app")
return
}
DispatchQueue.main.async {
for recipientId in recipientIds {
self.updateProfile(recipientId: recipientId)
@ -74,11 +81,11 @@ public class ProfileFetcherJob: NSObject {
if !ignoreThrottling {
if let lastDate = ProfileFetcherJob.fetchDateMap[recipientId] {
let lastTimeInterval = fabs(lastDate.timeIntervalSinceNow)
// Don't check a profile more often than every N minutes.
// Don't check a profile more often than every N seconds.
//
// Only throttle profile fetch in production builds in order to
// facilitate debugging.
let kGetProfileMaxFrequencySeconds = _isDebugAssertConfiguration() ? 0 : 60.0 * 5.0
// Throttle less in debug to make it easier to test problems
// with our fetching logic.
let kGetProfileMaxFrequencySeconds = _isDebugAssertConfiguration() ? 60 : 60.0 * 5.0
guard lastTimeInterval > kGetProfileMaxFrequencySeconds else {
return Promise(error: ProfileFetcherJobError.throttled(lastTimeInterval: lastTimeInterval))
}

View File

@ -206,7 +206,7 @@ public class ShareViewController: UINavigationController, ShareViewDelegate, SAE
// We don't need to use OWSOrphanedDataCleaner in the SAE.
OWSProfileManager.shared().fetchLocalUsersProfile()
// We don't need to fetch the local profile in the SAE
OWSReadReceiptManager.shared().prepareCachedValues()