[SG-912] Modify the mobile app to retrieve the user's avatar color (#2284)

* [SG-912] Modify the mobile app to retrieve the user's avatar color (#2277)

* work: baseline

* fix: dont use profile for store

* fiix: use userid in key

* fix: lookup on AccountView list create

* fix my own bad advice + tweaks

* Autosync the updated translations (#2279)

* fix my own bad advice + tweaks

* fiix: use userid in key

* [PS-1352] Fix ignore diacritics in search (#2044)

* Fix ignore diacritics in search

This change updates the search function to ignore diacritical marks in search results. Marks are stripped from both the search input and results.

* Removed logs, added null or whitespace validation and improved formatting


* [PS-2145] add rainsee browser series support (#2272)

* fix: lookup on AccountView list create

* Autosync the updated translations (#2279)

* fix my own bad advice + tweaks

* fix: single state grab is cool
This commit is contained in:
Brandon Maharaj 2023-01-12 13:27:10 -05:00 committed by GitHub
parent 4f4953206e
commit 6102a0c115
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 43 additions and 10 deletions

View file

@ -14,7 +14,7 @@ namespace Bit.App.Controls
{
AccountView = accountView;
AvatarImageSource = ServiceContainer.Resolve<IAvatarImageSourcePool>("avatarImageSourcePool")
?.GetOrCreateAvatar(AccountView.UserId, AccountView.Name, AccountView.Email);
?.GetOrCreateAvatar(AccountView.UserId, AccountView.Name, AccountView.Email, AccountView.AvatarColor);
}
public AccountView AccountView

View file

@ -13,6 +13,7 @@ namespace Bit.App.Controls
{
private readonly string _text;
private readonly string _id;
private readonly string _color;
public override bool Equals(object obj)
{
@ -23,7 +24,7 @@ namespace Bit.App.Controls
if (obj is AvatarImageSource avatar)
{
return avatar._id == _id && avatar._text == _text;
return avatar._id == _id && avatar._text == _text && avatar._color == _color;
}
return base.Equals(obj);
@ -31,7 +32,7 @@ namespace Bit.App.Controls
public override int GetHashCode() => _id?.GetHashCode() ?? _text?.GetHashCode() ?? -1;
public AvatarImageSource(string userId = null, string name = null, string email = null)
public AvatarImageSource(string userId = null, string name = null, string email = null, string color = null)
{
_id = userId;
_text = name;
@ -39,6 +40,7 @@ namespace Bit.App.Controls
{
_text = email;
}
_color = color;
}
public override Func<CancellationToken, Task<Stream>> Stream => GetStreamAsync;
@ -71,7 +73,7 @@ namespace Bit.App.Controls
chars = upperCaseText = _text.ToUpper();
}
var bgColor = CoreHelpers.StringToColor(_id ?? upperCaseText, "#33ffffff");
var bgColor = _color ?? CoreHelpers.StringToColor(_id ?? upperCaseText, "#33ffffff");
var textColor = CoreHelpers.TextColorFromBgColor(bgColor);
var size = 50;

View file

@ -5,19 +5,19 @@ namespace Bit.App.Controls
{
public interface IAvatarImageSourcePool
{
AvatarImageSource GetOrCreateAvatar(string userId, string name, string email);
AvatarImageSource GetOrCreateAvatar(string userId, string name, string email, string color);
}
public class AvatarImageSourcePool : IAvatarImageSourcePool
{
private readonly ConcurrentDictionary<string, AvatarImageSource> _cache = new ConcurrentDictionary<string, AvatarImageSource>();
public AvatarImageSource GetOrCreateAvatar(string userId, string name, string email)
public AvatarImageSource GetOrCreateAvatar(string userId, string name, string email, string color)
{
var key = $"{userId}{name}{email}";
var key = $"{userId}{name}{email}{color}";
if (!_cache.TryGetValue(key, out var avatar))
{
avatar = new AvatarImageSource(userId, name, email);
avatar = new AvatarImageSource(userId, name, email, color);
if (!_cache.TryAdd(key, avatar)
&&
!_cache.TryGetValue(key, out avatar)) // If add fails another thread created the avatar in between the first try get and the try add.

View file

@ -129,8 +129,8 @@ namespace Bit.App.Pages
{
if (useCurrentActiveAccount)
{
return new AvatarImageSource(await _stateService.GetActiveUserIdAsync(),
await _stateService.GetNameAsync(), await _stateService.GetEmailAsync());
var user = await _stateService.GetActiveUserCustomDataAsync(a => (a?.Profile?.UserId, a?.Profile?.Name, a?.Profile?.Email, a?.Profile?.AvatarColor));
return new AvatarImageSource(user.UserId, user.Name, user.Email, user.AvatarColor);
}
return new AvatarImageSource();
}

View file

@ -108,6 +108,10 @@ namespace Bit.App.Pages
else if (message.Command == "syncCompleted")
{
await Task.Delay(500);
if (_vm.MainPage)
{
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
}
Device.BeginInvokeOnMainThread(() =>
{
IsBusy = false;

View file

@ -163,5 +163,9 @@ namespace Bit.Core.Abstractions
Task<bool> GetShouldConnectToWatchAsync(string userId = null);
Task SetShouldConnectToWatchAsync(bool shouldConnect, string userId = null);
Task<bool> GetLastUserShouldConnectToWatchAsync();
Task SetAvatarColorAsync(string value, string userId = null);
Task<string> GetAvatarColorAsync(string userId = null);
}
}

View file

@ -48,6 +48,7 @@ namespace Bit.Core.Models.Domain
KdfIterations = copy.KdfIterations;
EmailVerified = copy.EmailVerified;
HasPremiumPersonally = copy.HasPremiumPersonally;
AvatarColor = copy.AvatarColor;
}
public string UserId;
@ -55,6 +56,7 @@ namespace Bit.Core.Models.Domain
public string Name;
public string Stamp;
public string OrgIdentifier;
public string AvatarColor;
public KdfType? KdfType;
public int? KdfIterations;
public bool? EmailVerified;

View file

@ -19,5 +19,6 @@ namespace Bit.Core.Models.Response
public bool ForcePasswordReset { get; set; }
public List<ProfileOrganizationResponse> Organizations { get; set; }
public bool UsesKeyConnector { get; set; }
public string AvatarColor { get; set; }
}
}

View file

@ -20,6 +20,7 @@ namespace Bit.Core.Models.View
UserId = a.Profile?.UserId;
Email = a.Profile?.Email;
Name = a.Profile?.Name;
AvatarColor = a.Profile?.AvatarColor;
if (!string.IsNullOrWhiteSpace(a.Settings?.EnvironmentUrls?.WebVault))
{
Hostname = CoreHelpers.GetHostname(a.Settings?.EnvironmentUrls?.WebVault);
@ -37,5 +38,6 @@ namespace Bit.Core.Models.View
public string Email { get; set; }
public string Name { get; set; }
public string Hostname { get; set; }
public string AvatarColor { get; set; }
}
}

View file

@ -1305,6 +1305,23 @@ namespace Bit.Core.Services
var key = Constants.PasswordlessLoginNotificationKey;
await SetValueAsync(key, value, options);
}
public async Task SetAvatarColorAsync(string value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
var account = await GetAccountAsync(reconciledOptions);
account.Profile.AvatarColor = value;
await SaveAccountAsync(account, reconciledOptions);
}
public async Task<string> GetAvatarColorAsync(string userId = null)
{
return (await GetAccountAsync(
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultStorageOptionsAsync())
))?.Profile?.AvatarColor;
}
// Helpers
private async Task<T> GetValueAsync<T>(string key, StorageOptions options)

View file

@ -335,6 +335,7 @@ namespace Bit.Core.Services
await _organizationService.ReplaceAsync(organizations);
await _stateService.SetEmailVerifiedAsync(response.EmailVerified);
await _stateService.SetNameAsync(response.Name);
await _stateService.SetAvatarColorAsync(response.AvatarColor);
await _keyConnectorService.SetUsesKeyConnector(response.UsesKeyConnector);
}