mirror of
https://github.com/bitwarden/mobile
synced 2025-12-10 13:23:39 +00:00
[SG-416] Updates to Bitwarden Authenticator (Feature Branch) (#2041)
* Initial commit of new TOTP page * Revert config files from previous commit This reverts commitb02c58e362. * clear extra code and fix build * add tab page * add authentication view cell * add toolbar icons * got the countdown working * enable context loading and vm init * PS-70 Added toggle to quickly filter TOTP cypher items and show their details, Added new text resource * PS-70 removed old authentication tab * removed unnecessary code on vm * fixed formatting * PS-70 Added circular progress to the OTP count down * PS-70 Fixed grid cell width. Added red progress at 20 percent. Refactored circular progress view. * PS-70 Added new props to custom control. * PS-70 show toggle only if it's premium * PS-70 removed unnecessary code * PS-70 add copy to clipboard. * PS-70 show upgrade to premium text on details to free user. * PS-70 added text labels to resource files * PS-70 Renamed TOTP to Totp to have consistency in naming. Removed a11y text of switch because android was overlapping text. * PS-70 added new UI to enter code manually in the QR Code scanner screen. Changed existing labels on scanner screen. * PS-70 Splited totp code to adjust spacing. * PS-70 Added scanner square corner overlay. Added scanning animation. Added scan success animation. * PS-70 let zxing scanner camera feed on until screen is closed. * PS-70 fixed scanner animation for android devices * PS-70 added vibrate permission to manifest. refactored scanpage code. added manual authentication key feature in scanner. * PS-70 fixed totp cell title label font * PS-70 added copy button to totp edit cipher. Added row button when totp is null. * PS-70 changed labels on manual scanner screen * PS-70 Added label on top of button to solve UI bug. * PS-70 Fixed android button overlapping bug by adding button styling to a Frame view and placing a label inside. Fixed Color on scanner page. * PS-70 Added frame styling for iOS, since frame view has different base configuration for android and iOS. * PS-70 fixed font clipping bug on iOS * PS-70 removed shadow for newer versions of android * PS-70 code format * PS-70 removed update to premium uri launch * PS-70 PR fix for AppResource vs code behind generation. * PS-70 changed premium required label. fixed bug when to show premium required label. * [SSG-416] Removed the dashes from free user and just left the Premium subscription required. * [SSG-416] removed unnecessary changes to the TabsPage file * [SSG-416] removed unnecessary using. * [SSG-416] Updated ViewPageViewModel and code refactoring. * [SG-416] Updated scanner mode toggle text color to be inline with figma designs * [SSG-416] Mobile PR Fixes * [SSG-416] Add to remove a11y text from totp toggle because on android it places an helper text next to the switch making it invisible. Also removed from the label because it already reads the text from the label * [SSG-416] run dotnet tool run dotnet-format * [SSG-416] PR fixes * Revert "[SSG-416] PR fixes" This reverts commit2f2b90acee. * [PS-416] Fixed a bug where the item details page was not updating after saving. * [SG-416] Authenticator toggle remake (#2027) * [SG-416] Removed toggle to TOTP. Added on MainPage new entry to go to screen with TOTP codes. Added filter for TOTP codes to be used when searching. * [SG-416] Removed unnecessary code. Added nav back if there is only 1 cipher with totp code and the user removes it. * [SG-416] Run dotnet format tool * [SG-416] PR fixes * [SG-416] PR Fixes. Manifest formatting. Add try catch. Extracted method and added null protection. * [SG-416] Make TOTP codes appear above favourites. * [SG-416] PR fixes. Show error dialog. Co-authored-by: Carlos J. Muentes <42616259+cmuentes@users.noreply.github.com> Co-authored-by: Jacob Fink <jfink@bitwarden.com>
This commit is contained in:
committed by
GitHub
parent
3d9555d420
commit
e829279e29
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Controls;
|
||||
using Bit.App.Resources;
|
||||
@@ -29,13 +31,16 @@ namespace Bit.App.Pages
|
||||
private bool _showList;
|
||||
private bool _websiteIconsEnabled;
|
||||
private bool _syncRefreshing;
|
||||
private bool _showTotpFilter;
|
||||
private bool _totpFilterEnable;
|
||||
private string _noDataText;
|
||||
private List<CipherView> _allCiphers;
|
||||
private Dictionary<string, int> _folderCounts = new Dictionary<string, int>();
|
||||
private Dictionary<string, int> _collectionCounts = new Dictionary<string, int>();
|
||||
private Dictionary<CipherType, int> _typeCounts = new Dictionary<CipherType, int>();
|
||||
private int _deletedCount = 0;
|
||||
|
||||
private CancellationTokenSource _totpTickCts;
|
||||
private Task _totpTickTask;
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly IFolderService _folderService;
|
||||
private readonly ICollectionService _collectionService;
|
||||
@@ -74,6 +79,9 @@ namespace Bit.App.Pages
|
||||
await LoadAsync();
|
||||
});
|
||||
CipherOptionsCommand = new Command<CipherView>(CipherOptionsAsync);
|
||||
VaultFilterCommand = new AsyncCommand(VaultFilterOptionsAsync,
|
||||
onException: ex => _logger.Exception(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
|
||||
AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, _logger)
|
||||
{
|
||||
@@ -94,6 +102,7 @@ namespace Bit.App.Pages
|
||||
&& NoFolderCiphers.Count < NoFolderListSize
|
||||
&& (Collections is null || !Collections.Any());
|
||||
public List<CipherView> Ciphers { get; set; }
|
||||
public List<CipherView> TOTPCiphers { get; set; }
|
||||
public List<CipherView> FavoriteCiphers { get; set; }
|
||||
public List<CipherView> NoFolderCiphers { get; set; }
|
||||
public List<FolderView> Folders { get; set; }
|
||||
@@ -151,9 +160,12 @@ namespace Bit.App.Pages
|
||||
get => _websiteIconsEnabled;
|
||||
set => SetProperty(ref _websiteIconsEnabled, value);
|
||||
}
|
||||
|
||||
public bool ShowTotp
|
||||
{
|
||||
get => _showTotpFilter;
|
||||
set => SetProperty(ref _showTotpFilter, value);
|
||||
}
|
||||
public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; }
|
||||
|
||||
public ObservableRangeCollection<IGroupingsPageListItem> GroupedItems { get; set; }
|
||||
public Command RefreshCommand { get; set; }
|
||||
public Command<CipherView> CipherOptionsCommand { get; set; }
|
||||
@@ -188,13 +200,14 @@ namespace Bit.App.Pages
|
||||
{
|
||||
PageTitle = ShowVaultFilter ? AppResources.Vaults : AppResources.MyVault;
|
||||
}
|
||||
|
||||
var canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||
_doingLoad = true;
|
||||
LoadedOnce = true;
|
||||
ShowNoData = false;
|
||||
Loading = true;
|
||||
ShowList = false;
|
||||
ShowAddCipherButton = !Deleted;
|
||||
|
||||
var groupedItems = new List<GroupingsPageListGroup>();
|
||||
var page = Page as GroupingsPage;
|
||||
|
||||
@@ -218,6 +231,8 @@ namespace Bit.App.Pages
|
||||
}
|
||||
if (MainPage)
|
||||
{
|
||||
AddTotpGroupItem(canAccessPremium, groupedItems, uppercaseGroupNames);
|
||||
|
||||
groupedItems.Add(new GroupingsPageListGroup(
|
||||
AppResources.Types, 4, uppercaseGroupNames, !hasFavorites)
|
||||
{
|
||||
@@ -274,10 +289,12 @@ namespace Bit.App.Pages
|
||||
}
|
||||
if (Ciphers?.Any() ?? false)
|
||||
{
|
||||
var ciphersListItems = Ciphers.Where(c => c.IsDeleted == Deleted)
|
||||
.Select(c => new GroupingsPageListItem { Cipher = c }).ToList();
|
||||
groupedItems.Add(new GroupingsPageListGroup(ciphersListItems, AppResources.Items,
|
||||
ciphersListItems.Count, uppercaseGroupNames, !MainPage && !groupedItems.Any()));
|
||||
CreateCipherGroupedItems(groupedItems);
|
||||
}
|
||||
if (ShowTotp && (!TOTPCiphers?.Any() ?? false))
|
||||
{
|
||||
Page.Navigation.PopAsync();
|
||||
return;
|
||||
}
|
||||
if (ShowNoFolderCipherGroup)
|
||||
{
|
||||
@@ -365,6 +382,60 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
private void AddTotpGroupItem(bool canAccessPremium, List<GroupingsPageListGroup> groupedItems, bool uppercaseGroupNames)
|
||||
{
|
||||
if (canAccessPremium && TOTPCiphers?.Any() == true)
|
||||
{
|
||||
groupedItems.Insert(0, new GroupingsPageListGroup(
|
||||
AppResources.Totp, 1, uppercaseGroupNames, false)
|
||||
{
|
||||
new GroupingsPageListItem
|
||||
{
|
||||
IsTotpCode = true,
|
||||
Type = CipherType.Login,
|
||||
ItemCount = TOTPCiphers.Count().ToString("N0")
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateCipherGroupedItems(List<GroupingsPageListGroup> groupedItems)
|
||||
{
|
||||
var uppercaseGroupNames = _deviceActionService.DeviceType == DeviceType.iOS;
|
||||
_totpTickCts?.Cancel();
|
||||
if (ShowTotp)
|
||||
{
|
||||
var ciphersListItems = TOTPCiphers.Select(c => new GroupingsPageTOTPListItem(c, true)).ToList();
|
||||
groupedItems.Add(new GroupingsPageListGroup(ciphersListItems, AppResources.Items,
|
||||
ciphersListItems.Count, uppercaseGroupNames, !MainPage && !groupedItems.Any()));
|
||||
|
||||
StartCiphersTotpTick(ciphersListItems);
|
||||
}
|
||||
else
|
||||
{
|
||||
var ciphersListItems = Ciphers.Where(c => c.IsDeleted == Deleted)
|
||||
.Select(c => new GroupingsPageListItem { Cipher = c }).ToList();
|
||||
groupedItems.Add(new GroupingsPageListGroup(ciphersListItems, AppResources.Items,
|
||||
ciphersListItems.Count, uppercaseGroupNames, !MainPage && !groupedItems.Any()));
|
||||
}
|
||||
}
|
||||
|
||||
private void StartCiphersTotpTick(List<GroupingsPageTOTPListItem> ciphersListItems)
|
||||
{
|
||||
_totpTickCts?.Cancel();
|
||||
_totpTickCts = new CancellationTokenSource();
|
||||
_totpTickTask = new TimerTask(logger, () => ciphersListItems.ForEach(i => i.TotpTickAsync()), _totpTickCts).RunPeriodic();
|
||||
}
|
||||
|
||||
public async Task StopCiphersTotpTick()
|
||||
{
|
||||
_totpTickCts?.Cancel();
|
||||
if (_totpTickTask != null)
|
||||
{
|
||||
await _totpTickTask;
|
||||
}
|
||||
}
|
||||
|
||||
public void DisableRefreshing()
|
||||
{
|
||||
Refreshing = false;
|
||||
@@ -425,6 +496,13 @@ namespace Bit.App.Pages
|
||||
await Page.Navigation.PushAsync(page);
|
||||
}
|
||||
|
||||
public async Task SelectTotpCodesAsync()
|
||||
{
|
||||
var page = new GroupingsPage(false, CipherType.Login, null, null, AppResources.VerificationCodes, _vaultFilterSelection, null,
|
||||
false, true);
|
||||
await Page.Navigation.PushAsync(page);
|
||||
}
|
||||
|
||||
public async Task ExitAsync()
|
||||
{
|
||||
var confirmed = await _platformUtilsService.ShowDialogAsync(AppResources.ExitConfirmation,
|
||||
@@ -462,6 +540,7 @@ namespace Bit.App.Pages
|
||||
NoDataText = AppResources.NoItems;
|
||||
_allCiphers = await GetAllCiphersAsync();
|
||||
HasCiphers = _allCiphers.Any();
|
||||
TOTPCiphers = _allCiphers.Where(c => c.IsDeleted == Deleted && c.Type == CipherType.Login && !string.IsNullOrEmpty(c.Login?.Totp)).ToList();
|
||||
FavoriteCiphers?.Clear();
|
||||
NoFolderCiphers?.Clear();
|
||||
_folderCounts.Clear();
|
||||
@@ -487,6 +566,10 @@ namespace Bit.App.Pages
|
||||
Filter = c => c.IsDeleted;
|
||||
NoDataText = AppResources.NoItemsTrash;
|
||||
}
|
||||
else if (ShowTotp)
|
||||
{
|
||||
Filter = c => c.Type == CipherType.Login && !c.IsDeleted && !string.IsNullOrEmpty(c.Login?.Totp);
|
||||
}
|
||||
else if (Type != null)
|
||||
{
|
||||
Filter = c => c.Type == Type.Value && !c.IsDeleted;
|
||||
|
||||
Reference in New Issue
Block a user