mirror of
https://github.com/bitwarden/mobile
synced 2026-01-04 01:23:15 +00:00
Account Switching (#1807)
* Account Switching (#1720) * Account switching * WIP * wip * wip * updates to send test logic * fixed Send tests * fixes for theme handling on account switching and re-adding existing account * switch fixes * fixes * fixes * cleanup * vault timeout fixes * account list status enhancements * logout fixes and token handling improvements * merge latest (#1727) * remove duplicate dependency * fix for initial login token storage paradox (#1730) * Fix avatar color update toolbar item issue on iOS for account switching (#1735) * Updated account switching menu UI (#1733) * updated account switching menu UI * additional changes * add key suffix to constant * GetFirstLetters method tweaks * Fix crash on account switching when logging out when having more than user at a time (#1740) * single account migration to multi-account on app update (#1741) * Account Switching Tap to dismiss (#1743) * Added tap to dismiss on the Account switching overlay and improved a bit the code * Fix account switching overlay background transparent on the proper place * Fixed transparent background and the shadow on the account switching overlay * Fix iOS top space on Account switching list overlay after modal (#1746) * Fix top space added to Account switching list overlay after closing modal * Fix top space added to Account switching list overlay after closing modal on lock, login and home views just in case we add modals in the future there as well * Usability: dismiss account list on certain events (#1748) * dismiss account list on certain events * use new FireAndForget method for back button logic * Create and use Account Switching overlay control (#1753) * Added Account switching overlay control and its own ViewModel and refactored accordingly * Fix account switching Accounts list binding update * Implemented dismiss account switching overlay when changing tabs and when selecting the same tab. Also updated the deprecated listener on CustomTabbedRenderer on Android (#1755) * Overriden Equals on AvatarImageSource so it doesn't get set multiple times when it's the same image thus producing blinking on tab chaged (#1756) * Usability improvements for logout on vault timeout (#1781) * accountswitching fixes (#1784) * Fix for invalid PIN lock state when switching accounts (#1792) * fix for pin lock flow * named tuple values and updated async * clear send service cache on account switch (#1796) * Global theme and account removal (#1793) * Global theme and account removal * remove redundant call to hide account list overlay * cleanup and additional tweaks * add try/catch to remove account dialog flow Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
This commit is contained in:
@@ -4,12 +4,12 @@ using Bit.App.Pages;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Services;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Enums;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
@@ -18,7 +18,6 @@ namespace Bit.App
|
||||
{
|
||||
public partial class App : Application
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly IStateService _stateService;
|
||||
@@ -26,7 +25,6 @@ namespace Bit.App
|
||||
private readonly ISyncService _syncService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IAuthService _authService;
|
||||
private readonly IStorageService _storageService;
|
||||
private readonly IStorageService _secureStorageService;
|
||||
private readonly IDeviceActionService _deviceActionService;
|
||||
|
||||
@@ -40,7 +38,6 @@ namespace Bit.App
|
||||
Current = this;
|
||||
return;
|
||||
}
|
||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
@@ -48,7 +45,6 @@ namespace Bit.App
|
||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
|
||||
@@ -85,8 +81,12 @@ namespace Bit.App
|
||||
}
|
||||
else if (message.Command == "logout")
|
||||
{
|
||||
var extras = message.Data as Tuple<string, bool, bool>;
|
||||
var userId = extras?.Item1;
|
||||
var userInitiated = extras?.Item2;
|
||||
var expired = extras?.Item3;
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
await LogOutAsync((message.Data as bool?).GetValueOrDefault()));
|
||||
await LogOutAsync(userId, userInitiated, expired));
|
||||
}
|
||||
else if (message.Command == "loggedOut")
|
||||
{
|
||||
@@ -107,6 +107,18 @@ namespace Bit.App
|
||||
await SleptAsync();
|
||||
}
|
||||
}
|
||||
else if (message.Command == "addAccount")
|
||||
{
|
||||
await AddAccount();
|
||||
}
|
||||
else if (message.Command == "accountAdded")
|
||||
{
|
||||
await UpdateThemeAsync();
|
||||
}
|
||||
else if (message.Command == "switchedAccount")
|
||||
{
|
||||
await SwitchedAccountAsync();
|
||||
}
|
||||
else if (message.Command == "migrated")
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
@@ -168,7 +180,7 @@ namespace Bit.App
|
||||
if (string.IsNullOrWhiteSpace(Options.Uri))
|
||||
{
|
||||
var updated = await AppHelpers.PerformUpdateTasksAsync(_syncService, _deviceActionService,
|
||||
_storageService);
|
||||
_stateService);
|
||||
if (!updated)
|
||||
{
|
||||
SyncIfNeeded();
|
||||
@@ -192,7 +204,7 @@ namespace Bit.App
|
||||
var isLocked = await _vaultTimeoutService.IsLockedAsync();
|
||||
if (!isLocked)
|
||||
{
|
||||
await _storageService.SaveAsync(Constants.LastActiveTimeKey, _deviceActionService.GetActiveTime());
|
||||
await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
|
||||
}
|
||||
SetTabsPageFromAutofill(isLocked);
|
||||
await SleptAsync();
|
||||
@@ -233,7 +245,7 @@ namespace Bit.App
|
||||
{
|
||||
await Device.InvokeOnMainThreadAsync(() =>
|
||||
{
|
||||
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources);
|
||||
ThemeManager.SetTheme(Current.Resources);
|
||||
_messagingService.Send("updatedTheme");
|
||||
});
|
||||
}
|
||||
@@ -246,27 +258,70 @@ namespace Bit.App
|
||||
new System.Globalization.UmAlQuraCalendar();
|
||||
}
|
||||
|
||||
private async Task LogOutAsync(bool expired)
|
||||
private async Task LogOutAsync(string userId, bool? userInitiated, bool? expired)
|
||||
{
|
||||
await AppHelpers.LogOutAsync();
|
||||
await AppHelpers.LogOutAsync(userId, userInitiated.GetValueOrDefault(true));
|
||||
await SetMainPageAsync();
|
||||
_authService.LogOut(() =>
|
||||
{
|
||||
Current.MainPage = new HomePage();
|
||||
if (expired)
|
||||
if (expired.GetValueOrDefault())
|
||||
{
|
||||
_platformUtilsService.ShowToast("warning", null, AppResources.LoginExpired);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async Task AddAccount()
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
Options.HideAccountSwitcher = false;
|
||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||
});
|
||||
}
|
||||
|
||||
private async Task SwitchedAccountAsync()
|
||||
{
|
||||
await AppHelpers.OnAccountSwitchAsync();
|
||||
var shouldTimeout = await _vaultTimeoutService.ShouldTimeoutAsync();
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
if (shouldTimeout)
|
||||
{
|
||||
await _vaultTimeoutService.ExecuteTimeoutActionAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await SetMainPageAsync();
|
||||
}
|
||||
await Task.Delay(50);
|
||||
await UpdateThemeAsync();
|
||||
});
|
||||
}
|
||||
|
||||
private async Task SetMainPageAsync()
|
||||
{
|
||||
var authed = await _userService.IsAuthenticatedAsync();
|
||||
var authed = await _stateService.IsAuthenticatedAsync();
|
||||
if (authed)
|
||||
{
|
||||
if (await _vaultTimeoutService.IsLockedAsync())
|
||||
var isLocked = await _vaultTimeoutService.IsLockedAsync();
|
||||
var shouldTimeout = await _vaultTimeoutService.ShouldTimeoutAsync();
|
||||
if (isLocked || shouldTimeout)
|
||||
{
|
||||
Current.MainPage = new NavigationPage(new LockPage(Options));
|
||||
var vaultTimeoutAction = await _stateService.GetVaultTimeoutActionAsync();
|
||||
if (vaultTimeoutAction == VaultTimeoutAction.Logout)
|
||||
{
|
||||
// TODO implement orgIdentifier flow to SSO Login page, same as email flow below
|
||||
// var orgIdentifier = await _stateService.GetOrgIdentifierAsync();
|
||||
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
Options.HideAccountSwitcher = await _stateService.GetActiveUserIdAsync() == null;
|
||||
Current.MainPage = new NavigationPage(new LoginPage(email, Options));
|
||||
}
|
||||
else
|
||||
{
|
||||
Current.MainPage = new NavigationPage(new LockPage(Options));
|
||||
}
|
||||
}
|
||||
else if (Options.FromAutofillFramework && Options.SaveType.HasValue)
|
||||
{
|
||||
@@ -287,13 +342,25 @@ namespace Bit.App
|
||||
}
|
||||
else
|
||||
{
|
||||
Current.MainPage = new HomePage(Options);
|
||||
Options.HideAccountSwitcher = await _stateService.GetActiveUserIdAsync() == null;
|
||||
if (await _vaultTimeoutService.IsLoggedOutByTimeoutAsync())
|
||||
{
|
||||
// TODO implement orgIdentifier flow to SSO Login page, same as email flow below
|
||||
// var orgIdentifier = await _stateService.GetOrgIdentifierAsync();
|
||||
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
Current.MainPage = new NavigationPage(new LoginPage(email, Options));
|
||||
}
|
||||
else
|
||||
{
|
||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ClearCacheIfNeededAsync()
|
||||
{
|
||||
var lastClear = await _storageService.GetAsync<DateTime?>(Constants.LastFileCacheClearKey);
|
||||
var lastClear = await _stateService.GetLastFileCacheClearAsync();
|
||||
if ((DateTime.UtcNow - lastClear.GetValueOrDefault(DateTime.MinValue)).TotalDays >= 1)
|
||||
{
|
||||
var task = Task.Run(() => _deviceActionService.ClearCacheAsync());
|
||||
@@ -336,12 +403,12 @@ namespace Bit.App
|
||||
{
|
||||
InitializeComponent();
|
||||
SetCulture();
|
||||
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources);
|
||||
ThemeManager.SetTheme(Current.Resources);
|
||||
Current.RequestedThemeChanged += (s, a) =>
|
||||
{
|
||||
UpdateThemeAsync();
|
||||
};
|
||||
Current.MainPage = new HomePage();
|
||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||
var mainPageTask = SetMainPageAsync();
|
||||
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
|
||||
}
|
||||
@@ -365,10 +432,9 @@ namespace Bit.App
|
||||
|
||||
private async Task LockedAsync(bool autoPromptBiometric)
|
||||
{
|
||||
await _stateService.PurgeAsync();
|
||||
if (autoPromptBiometric && Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
var vaultTimeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
||||
var vaultTimeout = await _stateService.GetVaultTimeoutAsync();
|
||||
if (vaultTimeout == 0)
|
||||
{
|
||||
autoPromptBiometric = false;
|
||||
@@ -398,7 +464,7 @@ namespace Bit.App
|
||||
}
|
||||
}
|
||||
}
|
||||
await _storageService.SaveAsync(Constants.PreviousPageKey, lastPageBeforeLock);
|
||||
await _stateService.SetPreviousPageInfoAsync(lastPageBeforeLock);
|
||||
var lockPage = new LockPage(Options, autoPromptBiometric);
|
||||
Device.BeginInvokeOnMainThread(() => Current.MainPage = new NavigationPage(lockPage));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user