diff --git a/src/App/Platforms/iOS/AppDelegate.cs b/src/App/Platforms/iOS/AppDelegate.cs index 98d55b3ba..af4d6667b 100644 --- a/src/App/Platforms/iOS/AppDelegate.cs +++ b/src/App/Platforms/iOS/AppDelegate.cs @@ -54,10 +54,6 @@ namespace Bit.iOS _stateService = ServiceContainer.Resolve("stateService"); _eventService = ServiceContainer.Resolve("eventService"); - //LoadApplication(new App.App(null)); - //iOSCoreHelpers.AppearanceAdjustments(); - //ZXing.Net.Mobile.Forms.iOS.Platform.Init(); - ConnectToWatchIfNeededAsync().FireAndForget(); _broadcasterService.Subscribe(nameof(AppDelegate), async (message) => @@ -219,7 +215,11 @@ namespace Bit.iOS public override void DidEnterBackground(UIApplication uiApplication) { - _stateService?.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime()); + if (_stateService != null && _deviceActionService != null) + { + _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime()); + } + _messagingService?.Send("slept"); base.DidEnterBackground(uiApplication); } diff --git a/src/Core/App.xaml.cs b/src/Core/App.xaml.cs index 38709023f..7b18301ec 100644 --- a/src/Core/App.xaml.cs +++ b/src/Core/App.xaml.cs @@ -35,6 +35,8 @@ namespace Bit.App private readonly IAccountsManager _accountsManager; private readonly IPushNotificationService _pushNotificationService; private readonly IConfigService _configService; + private readonly ILogger _logger; + private static bool _isResumed; // these variables are static because the app is launching new activities on notification click, creating new instances of App. private static bool _pendingCheckPasswordlessLoginRequests; @@ -155,6 +157,7 @@ namespace Bit.App _accountsManager = ServiceContainer.Resolve("accountsManager"); _pushNotificationService = ServiceContainer.Resolve(); _configService = ServiceContainer.Resolve(); + _logger = ServiceContainer.Resolve(); _accountsManager.Init(() => Options, this); @@ -169,7 +172,7 @@ namespace Bit.App var confirmed = true; var confirmText = string.IsNullOrWhiteSpace(details.ConfirmText) ? AppResources.Ok : details.ConfirmText; - MainThread.BeginInvokeOnMainThread(async () => + await MainThread.InvokeOnMainThreadAsync(async () => { if (!string.IsNullOrWhiteSpace(details.CancelText)) { @@ -183,20 +186,16 @@ namespace Bit.App _messagingService.Send("showDialogResolve", new Tuple(details.DialogId, confirmed)); }); } +#if IOS else if (message.Command == AppHelpers.RESUMED_MESSAGE_COMMAND) { - if (DeviceInfo.Platform == DevicePlatform.iOS) - { - ResumedAsync().FireAndForget(); - } + ResumedAsync().FireAndForget(); } else if (message.Command == "slept") { - if (DeviceInfo.Platform == DevicePlatform.iOS) - { - await SleptAsync(); - } + await SleptAsync(); } +#endif else if (message.Command == "migrated") { await Task.Delay(1000); @@ -213,7 +212,7 @@ namespace Bit.App Options.OtpData = new OtpData((string)message.Data); } - MainThread.InvokeOnMainThreadAsync(async () => + await MainThread.InvokeOnMainThreadAsync(async () => { if (MainPage is TabsPage tabsPage) { @@ -249,7 +248,7 @@ namespace Bit.App } else if (message.Command == "convertAccountToKeyConnector") { - MainThread.BeginInvokeOnMainThread(async () => + await MainThread.InvokeOnMainThreadAsync(async () => { await MainPage.Navigation.PushModalAsync( new NavigationPage(new RemoveMasterPasswordPage())); @@ -257,7 +256,7 @@ namespace Bit.App } else if (message.Command == Constants.ForceUpdatePassword) { - MainThread.BeginInvokeOnMainThread(async () => + await MainThread.InvokeOnMainThreadAsync(async () => { await MainPage.Navigation.PushModalAsync( new NavigationPage(new UpdateTempPasswordPage())); @@ -373,40 +372,52 @@ namespace Bit.App protected override async void OnStart() { - System.Diagnostics.Debug.WriteLine("XF App: OnStart"); - _isResumed = true; - await ClearCacheIfNeededAsync(); - Prime(); - if (string.IsNullOrWhiteSpace(Options.Uri)) + try { - var updated = await AppHelpers.PerformUpdateTasksAsync(_syncService, _deviceActionService, - _stateService); - if (!updated) + System.Diagnostics.Debug.WriteLine("XF App: OnStart"); + _isResumed = true; + await ClearCacheIfNeededAsync(); + Prime(); + if (string.IsNullOrWhiteSpace(Options.Uri)) { - SyncIfNeeded(); + var updated = await AppHelpers.PerformUpdateTasksAsync(_syncService, _deviceActionService, + _stateService); + if (!updated) + { + SyncIfNeeded(); + } } - } - if (_pendingCheckPasswordlessLoginRequests) - { - _messagingService.Send(Constants.PasswordlessLoginRequestKey); - } - if (DeviceInfo.Platform == DevicePlatform.Android) - { + if (_pendingCheckPasswordlessLoginRequests) + { + _messagingService.Send(Constants.PasswordlessLoginRequestKey); + } + #if ANDROID await _vaultTimeoutService.CheckVaultTimeoutAsync(); // Reset delay on every start _vaultTimeoutService.DelayLockAndLogoutMs = null; - } + #endif - await _configService.GetAsync(); - _messagingService.Send("startEventTimer"); + await _configService.GetAsync(); + _messagingService.Send("startEventTimer"); + } + catch (Exception ex) + { + _logger?.Exception(ex); + throw; + } } +#if ANDROID protected override async void OnSleep() +#else + protected override void OnSleep() +#endif { - System.Diagnostics.Debug.WriteLine("XF App: OnSleep"); - _isResumed = false; - if (DeviceInfo.Platform == DevicePlatform.Android) + try { + System.Diagnostics.Debug.WriteLine("XF App: OnSleep"); + _isResumed = false; +#if ANDROID var isLocked = await _vaultTimeoutService.IsLockedAsync(); if (!isLocked) { @@ -417,20 +428,34 @@ namespace Bit.App ClearAutofillUri(); } await SleptAsync(); +#endif + } + catch (Exception ex) + { + _logger?.Exception(ex); + throw; } } protected override void OnResume() { - System.Diagnostics.Debug.WriteLine("XF App: OnResume"); - _isResumed = true; - if (_pendingCheckPasswordlessLoginRequests) - { - _messagingService.Send(Constants.PasswordlessLoginRequestKey); - } - if (DeviceInfo.Platform == DevicePlatform.Android) + try { + System.Diagnostics.Debug.WriteLine("XF App: OnResume"); + _isResumed = true; + if (_pendingCheckPasswordlessLoginRequests) + { + _messagingService.Send(Constants.PasswordlessLoginRequestKey); + } +#if ANDROID ResumedAsync().FireAndForget(); +#endif + + } + catch (Exception ex) + { + _logger?.Exception(ex); + throw; } } @@ -517,14 +542,22 @@ namespace Bit.App { MainThread.BeginInvokeOnMainThread(() => { - Options.Uri = null; - if (isLocked) + try { - App.MainPage = new NavigationPage(new LockPage()); + Options.Uri = null; + if (isLocked) + { + App.MainPage = new NavigationPage(new LockPage()); + } + else + { + App.MainPage = new TabsPage(); + } } - else + catch (Exception ex) { - App.MainPage = new TabsPage(); + LoggerHelper.LogEvenIfCantBeResolved(ex); + throw; } }); }); @@ -549,7 +582,7 @@ namespace Bit.App ThemeManager.SetTheme(Resources); RequestedThemeChanged += (s, a) => { - UpdateThemeAsync(); + UpdateThemeAsync().FireAndForget(); }; _isResumed = true; #if IOS @@ -568,11 +601,18 @@ namespace Bit.App } Task.Run(async () => { - var lastSync = await _syncService.GetLastSyncAsync(); - if (lastSync == null || ((DateTime.UtcNow - lastSync) > TimeSpan.FromMinutes(30))) + try { - await Task.Delay(1000); - await _syncService.FullSyncAsync(false); + var lastSync = await _syncService.GetLastSyncAsync(); + if (lastSync == null || ((DateTime.UtcNow - lastSync) > TimeSpan.FromMinutes(30))) + { + await Task.Delay(1000); + await _syncService.FullSyncAsync(false); + } + } + catch (Exception ex) + { + _logger.Exception(ex); } }); } diff --git a/src/Core/Pages/Accounts/LockPage.xaml.cs b/src/Core/Pages/Accounts/LockPage.xaml.cs index 0cf421abe..6861f7bc2 100644 --- a/src/Core/Pages/Accounts/LockPage.xaml.cs +++ b/src/Core/Pages/Accounts/LockPage.xaml.cs @@ -4,6 +4,7 @@ using Bit.App.Utilities; using Bit.Core; using Bit.Core.Abstractions; using Bit.Core.Utilities; +using Bit.Core.Services; namespace Bit.App.Pages { @@ -26,16 +27,24 @@ namespace Bit.App.Pages _vm = BindingContext as LockPageViewModel; _vm.CheckPendingAuthRequests = checkPendingAuthRequests; _vm.Page = this; - _vm.UnlockedAction = () => MainThread.BeginInvokeOnMainThread(async () => await UnlockedAsync()); + _vm.UnlockedAction = () => MainThread.BeginInvokeOnMainThread(async () => + { + try + { + await UnlockedAsync(); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + throw; + } + }); - if (DeviceInfo.Platform == DevicePlatform.iOS) - { - ToolbarItems.Add(_moreItem); - } - else - { - ToolbarItems.Add(_logOut); - } +#if IOS + ToolbarItems.Add(_moreItem); +#else + ToolbarItems.Add(_logOut); +#endif } public Entry SecretEntry @@ -65,52 +74,60 @@ namespace Bit.App.Pages protected override async void OnAppearing() { - base.OnAppearing(); - _broadcasterService.Subscribe(nameof(LockPage), message => + try { - if (message.Command == Constants.ClearSensitiveFields) + base.OnAppearing(); + _broadcasterService.Subscribe(nameof(LockPage), message => { - MainThread.BeginInvokeOnMainThread(_vm.ResetPinPasswordFields); - } - }); - if (_appeared) - { - return; - } - - _appeared = true; - _mainContent.Content = _mainLayout; - - //Workaround: This delay allows the Avatar to correctly load on iOS. The cause of this issue is also likely connected with the race conditions issue when using loading modals in iOS - await Task.Delay(50); - - _accountAvatar?.OnAppearing(); - - _vm.AvatarImageSource = await GetAvatarImageSourceAsync(); - - await _vm.InitAsync(); - - _vm.FocusSecretEntry += PerformFocusSecretEntry; - - if (!_vm.BiometricEnabled) - { - RequestFocus(SecretEntry); - } - else - { - if (!_vm.HasMasterPassword && !_vm.PinEnabled) - { - _passwordGrid.IsVisible = false; - _unlockButton.IsVisible = false; - } - if (_autoPromptBiometric) - { - var tasks = Task.Run(async () => + if (message.Command == Constants.ClearSensitiveFields) { - await Task.Delay(500); - MainThread.BeginInvokeOnMainThread(async () => await _vm.PromptBiometricAsync()); - }); + MainThread.BeginInvokeOnMainThread(_vm.ResetPinPasswordFields); + } + }); + if (_appeared) + { + return; } + + _appeared = true; + _mainContent.Content = _mainLayout; + + //Workaround: This delay allows the Avatar to correctly load on iOS. The cause of this issue is also likely connected with the race conditions issue when using loading modals in iOS + await Task.Delay(50); + + _accountAvatar?.OnAppearing(); + + _vm.AvatarImageSource = await GetAvatarImageSourceAsync(); + + await _vm.InitAsync(); + + _vm.FocusSecretEntry += PerformFocusSecretEntry; + + if (!_vm.BiometricEnabled) + { + RequestFocus(SecretEntry); + } + else + { + if (!_vm.HasMasterPassword && !_vm.PinEnabled) + { + _passwordGrid.IsVisible = false; + _unlockButton.IsVisible = false; + } + if (_autoPromptBiometric) + { + var tasks = Task.Run(async () => + { + await Task.Delay(500); + await MainThread.InvokeOnMainThreadAsync(async () => await _vm.PromptBiometricAsync()); + }); + } + } + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + throw; } } @@ -167,27 +184,44 @@ namespace Bit.App.Pages private async void Biometric_Clicked(object sender, EventArgs e) { - if (DoOnce()) + try { - await _vm.PromptBiometricAsync(); + if (DoOnce()) + { + await _vm.PromptBiometricAsync(); + } + + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + throw; } } private async void More_Clicked(object sender, System.EventArgs e) { - await _accountListOverlay.HideAsync(); - - if (!DoOnce()) + try { - return; + await _accountListOverlay.HideAsync(); + + if (!DoOnce()) + { + return; + } + + var selection = await DisplayActionSheet(AppResources.Options, + AppResources.Cancel, null, AppResources.LogOut); + + if (selection == AppResources.LogOut) + { + await _vm.LogOutAsync(); + } } - - var selection = await DisplayActionSheet(AppResources.Options, - AppResources.Cancel, null, AppResources.LogOut); - - if (selection == AppResources.LogOut) + catch (Exception ex) { - await _vm.LogOutAsync(); + LoggerHelper.LogEvenIfCantBeResolved(ex); + throw; } } diff --git a/src/Core/Pages/BaseContentPage.cs b/src/Core/Pages/BaseContentPage.cs index fcb1ca1f9..d04c8a852 100644 --- a/src/Core/Pages/BaseContentPage.cs +++ b/src/Core/Pages/BaseContentPage.cs @@ -14,6 +14,7 @@ namespace Bit.App.Pages { private readonly LazyResolve _stateService = new LazyResolve(); private readonly LazyResolve _deviceActionService = new LazyResolve(); + private readonly LazyResolve _logger = new LazyResolve(); protected int ShowModalAnimationDelay = 400; protected int ShowPageAnimationDelay = 100; @@ -48,22 +49,38 @@ namespace Bit.App.Pages protected override async void OnNavigatedTo(NavigatedToEventArgs args) { - base.OnNavigatedTo(args); - - if (IsThemeDirty) + try { - UpdateOnThemeChanged(); + base.OnNavigatedTo(args); + + if (IsThemeDirty) + { + try + { + await UpdateOnThemeChanged(); + } + catch (Exception ex) + { + Core.Services.LoggerHelper.LogEvenIfCantBeResolved(ex); + // Don't rethrow on theme changed so the user can still continue on the app. + } + } + + await SaveActivityAsync(); + + if (ShouldCheckToPreventOnNavigatedToCalledTwice && _hasInitedOnNavigatedTo) + { + return; + } + _hasInitedOnNavigatedTo = true; + + await InitOnNavigatedToAsync(); } - - await SaveActivityAsync(); - - if (ShouldCheckToPreventOnNavigatedToCalledTwice && _hasInitedOnNavigatedTo) + catch (Exception ex) { - return; + Core.Services.LoggerHelper.LogEvenIfCantBeResolved(ex); + throw; } - _hasInitedOnNavigatedTo = true; - - await InitOnNavigatedToAsync(); } protected virtual Task InitOnNavigatedToAsync() => Task.CompletedTask; @@ -116,29 +133,37 @@ namespace Bit.App.Pages { async Task DoWorkAsync() { - await workFunction.Invoke(); - if (sourceView != null) + try { - if (targetView != null) + await workFunction.Invoke(); + if (sourceView != null) { - targetView.Content = sourceView; - } - else - { - Content = sourceView; + if (targetView != null) + { + targetView.Content = sourceView; + } + else + { + Content = sourceView; + } } } + catch (Exception ex) + { + _logger.Value.Exception(ex); + throw; + } } - if (DeviceInfo.Platform == DevicePlatform.iOS) - { - await DoWorkAsync(); - return; - } + +#if IOS + await DoWorkAsync(); +#else await Task.Run(async () => { await Task.Delay(fromModal ? ShowModalAnimationDelay : ShowPageAnimationDelay); MainThread.BeginInvokeOnMainThread(async () => await DoWorkAsync()); }); +#endif } protected void RequestFocus(InputView input) diff --git a/src/Core/Pages/Generator/GeneratorHistoryPageViewModel.cs b/src/Core/Pages/Generator/GeneratorHistoryPageViewModel.cs index ec03c7451..8ec43fcdb 100644 --- a/src/Core/Pages/Generator/GeneratorHistoryPageViewModel.cs +++ b/src/Core/Pages/Generator/GeneratorHistoryPageViewModel.cs @@ -42,7 +42,7 @@ namespace Bit.App.Pages public async Task InitAsync() { var history = await _passwordGenerationService.GetHistoryAsync(); - Device.BeginInvokeOnMainThread(() => + MainThread.BeginInvokeOnMainThread(() => { History.ResetWithRange(history ?? new List()); ShowNoData = History.Count == 0; @@ -66,7 +66,7 @@ namespace Bit.App.Pages { try { - await Device.InvokeOnMainThreadAsync(() => History.ResetWithRange(new List())); + await MainThread.InvokeOnMainThreadAsync(() => History.ResetWithRange(new List())); await InitAsync(); } diff --git a/src/Core/Pages/Generator/GeneratorPage.xaml.cs b/src/Core/Pages/Generator/GeneratorPage.xaml.cs index e0d336642..8ddb61dc3 100644 --- a/src/Core/Pages/Generator/GeneratorPage.xaml.cs +++ b/src/Core/Pages/Generator/GeneratorPage.xaml.cs @@ -15,6 +15,7 @@ namespace Bit.App.Pages public partial class GeneratorPage : BaseContentPage, IThemeDirtablePage { private readonly IBroadcasterService _broadcasterService; + private readonly ILogger _logger; private GeneratorPageViewModel _vm; private readonly bool _fromTabPage; @@ -26,6 +27,8 @@ namespace Bit.App.Pages _tabsPage = tabsPage; InitializeComponent(); _broadcasterService = ServiceContainer.Resolve(); + _logger = ServiceContainer.Resolve(); + _vm = BindingContext as GeneratorPageViewModel; _vm.Page = this; _fromTabPage = fromTabPage; @@ -35,26 +38,21 @@ namespace Bit.App.Pages _vm.EmailWebsite = emailWebsite; _vm.EditMode = editMode; _vm.IosExtension = appOptions?.IosExtension ?? false; - // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes - var isIos = Device.RuntimePlatform == Device.iOS; + if (selectAction != null) { - if (isIos) - { - ToolbarItems.Add(_closeItem); - } +#if IOS + ToolbarItems.Add(_closeItem); +#endif ToolbarItems.Add(_selectItem); } else { - if (isIos) - { - ToolbarItems.Add(_moreItem); - } - else - { - ToolbarItems.Add(_historyItem); - } +#if IOS + ToolbarItems.Add(_moreItem); +#else + ToolbarItems.Add(_historyItem); +#endif } _typePicker.On().SetUpdateMode(UpdateMode.WhenFinished); _passwordTypePicker.On().SetUpdateMode(UpdateMode.WhenFinished); @@ -71,22 +69,30 @@ namespace Bit.App.Pages protected async override void OnAppearing() { - base.OnAppearing(); - - lblPassword.IsVisible = true; - - if (!_fromTabPage) + try { - await InitAsync(); - } + base.OnAppearing(); - _broadcasterService.Subscribe(nameof(GeneratorPage), (message) => - { - if (message.Command is ThemeManager.UPDATED_THEME_MESSAGE_KEY) + lblPassword.IsVisible = true; + + if (!_fromTabPage) { - Device.BeginInvokeOnMainThread(() => _vm.RedrawPassword()); + await InitAsync(); } - }); + + _broadcasterService.Subscribe(nameof(GeneratorPage), (message) => + { + if (message.Command is ThemeManager.UPDATED_THEME_MESSAGE_KEY) + { + MainThread.BeginInvokeOnMainThread(() => _vm.RedrawPassword()); + } + }); + } + catch (Exception ex) + { + _logger.Exception(ex); + throw; + } } protected override void OnDisappearing() @@ -100,27 +106,35 @@ namespace Bit.App.Pages protected override bool OnBackButtonPressed() { - // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes - if (Device.RuntimePlatform == Device.Android && _tabsPage != null) +#if ANDROID + if (_tabsPage != null) { _tabsPage.ResetToVaultPage(); return true; } +#endif return base.OnBackButtonPressed(); } private async void More_Clicked(object sender, EventArgs e) { - if (!DoOnce()) + try { - return; + if (!DoOnce()) + { + return; + } + var selection = await DisplayActionSheet(AppResources.Options, AppResources.Cancel, + null, AppResources.PasswordHistory); + if (selection == AppResources.PasswordHistory) + { + var page = new GeneratorHistoryPage(); + await Navigation.PushModalAsync(new Microsoft.Maui.Controls.NavigationPage(page)); + } } - var selection = await DisplayActionSheet(AppResources.Options, AppResources.Cancel, - null, AppResources.PasswordHistory); - if (selection == AppResources.PasswordHistory) + catch (Exception ex) { - var page = new GeneratorHistoryPage(); - await Navigation.PushModalAsync(new Microsoft.Maui.Controls.NavigationPage(page)); + _logger.Exception(ex); } } @@ -144,7 +158,7 @@ namespace Bit.App.Pages { await base.UpdateOnThemeChanged(); - await Device.InvokeOnMainThreadAsync(() => + await MainThread.InvokeOnMainThreadAsync(() => { if (_vm != null) { diff --git a/src/Core/Pages/Send/SendGroupingsPage/SendGroupingsPage.xaml.cs b/src/Core/Pages/Send/SendGroupingsPage/SendGroupingsPage.xaml.cs index e9b0eacdd..f7320d8fb 100644 --- a/src/Core/Pages/Send/SendGroupingsPage/SendGroupingsPage.xaml.cs +++ b/src/Core/Pages/Send/SendGroupingsPage/SendGroupingsPage.xaml.cs @@ -18,6 +18,7 @@ namespace Bit.App.Pages private readonly ISyncService _syncService; private readonly IVaultTimeoutService _vaultTimeoutService; private readonly ISendService _sendService; + private readonly ILogger _logger; private readonly SendGroupingsPageViewModel _vm; private readonly string _pageName; @@ -33,6 +34,8 @@ namespace Bit.App.Pages _syncService = ServiceContainer.Resolve("syncService"); _vaultTimeoutService = ServiceContainer.Resolve("vaultTimeoutService"); _sendService = ServiceContainer.Resolve("sendService"); + _logger = ServiceContainer.Resolve(); + _vm = BindingContext as SendGroupingsPageViewModel; _vm.Page = this; _vm.MainPage = mainPage; @@ -43,85 +46,89 @@ namespace Bit.App.Pages _vm.PageTitle = pageTitle; } - // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes - if (Device.RuntimePlatform == Device.iOS) +#if IOS + _absLayout.Children.Remove(_fab); + if (type == null) { - _absLayout.Children.Remove(_fab); - if (type == null) - { - ToolbarItems.Add(_aboutIconItem); - } - ToolbarItems.Add(_addItem); - } - else - { - ToolbarItems.Add(_syncItem); - ToolbarItems.Add(_lockItem); - ToolbarItems.Add(_aboutTextItem); + ToolbarItems.Add(_aboutIconItem); } + ToolbarItems.Add(_addItem); +#else + ToolbarItems.Add(_syncItem); + ToolbarItems.Add(_lockItem); + ToolbarItems.Add(_aboutTextItem); +#endif } protected override async void OnAppearing() { - base.OnAppearing(); - if (_syncService.SyncInProgress) + try { - IsBusy = true; - } - - _broadcasterService.Subscribe(_pageName, async (message) => - { - try + base.OnAppearing(); + if (_syncService.SyncInProgress) { - if (message.Command == "syncStarted") - { - Device.BeginInvokeOnMainThread(() => IsBusy = true); - } - else if (message.Command == "syncCompleted" || message.Command == "sendUpdated") - { - await Task.Delay(500); - Device.BeginInvokeOnMainThread(() => - { - IsBusy = false; - if (_vm.LoadedOnce) - { - var task = _vm.LoadAsync(); - } - }); - } + IsBusy = true; } - catch (Exception ex) - { - LoggerHelper.LogEvenIfCantBeResolved(ex); - } - }); - await LoadOnAppearedAsync(_mainLayout, false, async () => - { - if (!_syncService.SyncInProgress || (await _sendService.GetAllAsync()).Any()) + _broadcasterService.Subscribe(_pageName, async (message) => { try { - await _vm.LoadAsync(); + if (message.Command == "syncStarted") + { + MainThread.BeginInvokeOnMainThread(() => IsBusy = true); + } + else if (message.Command == "syncCompleted" || message.Command == "sendUpdated") + { + await Task.Delay(500); + await MainThread.InvokeOnMainThreadAsync(() => + { + IsBusy = false; + if (_vm.LoadedOnce) + { + var task = _vm.LoadAsync(); + } + }); + } } - catch (Exception e) when (e.Message.Contains("No key.")) + catch (Exception ex) { - await Task.Delay(1000); - await _vm.LoadAsync(); + LoggerHelper.LogEvenIfCantBeResolved(ex); } - } - else - { - await Task.Delay(5000); - if (!_vm.Loaded) - { - await _vm.LoadAsync(); - } - } + }); - AdjustToolbar(); - await CheckAddRequest(); - }, _mainContent); + await LoadOnAppearedAsync(_mainLayout, false, async () => + { + if (!_syncService.SyncInProgress || (await _sendService.GetAllAsync()).Any()) + { + try + { + await _vm.LoadAsync(); + } + catch (Exception e) when (e.Message.Contains("No key.")) + { + await Task.Delay(1000); + await _vm.LoadAsync(); + } + } + else + { + await Task.Delay(5000); + if (!_vm.Loaded) + { + await _vm.LoadAsync(); + } + } + + AdjustToolbar(); + await CheckAddRequest(); + }, _mainContent); + } + catch (Exception ex) + { + _logger.Exception(ex); + throw; + } } protected override void OnDisappearing() diff --git a/src/Core/Pages/Send/SendGroupingsPage/SendGroupingsPageViewModel.cs b/src/Core/Pages/Send/SendGroupingsPage/SendGroupingsPageViewModel.cs index 1050e223d..239f9421e 100644 --- a/src/Core/Pages/Send/SendGroupingsPage/SendGroupingsPageViewModel.cs +++ b/src/Core/Pages/Send/SendGroupingsPage/SendGroupingsPageViewModel.cs @@ -1,22 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Bit.App.Abstractions; -using Bit.Core.Resources.Localization; +using Bit.App.Abstractions; using Bit.App.Utilities; -using Bit.Core; using Bit.Core.Abstractions; using Bit.Core.Enums; using Bit.Core.Models.View; +using Bit.Core.Resources.Localization; using Bit.Core.Utilities; -using DeviceType = Bit.Core.Enums.DeviceType; -using Microsoft.Maui.Networking; -using Microsoft.Maui.Devices; -using Microsoft.Maui.Controls; -using Microsoft.Maui; - namespace Bit.App.Pages { public class SendGroupingsPageViewModel : BaseViewModel @@ -117,28 +106,38 @@ namespace Bit.App.Pages { return; } - var authed = await _stateService.IsAuthenticatedAsync(); - if (!authed) + + try { - return; + var authed = await _stateService.IsAuthenticatedAsync(); + if (!authed) + { + return; + } + if (await _vaultTimeoutService.IsLockedAsync()) + { + return; + } + if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing) + { + SyncRefreshing = true; + await _syncService.FullSyncAsync(false); + return; + } + + _doingLoad = true; + LoadedOnce = true; + ShowNoData = false; + Loading = true; + ShowList = false; + SendEnabled = !await AppHelpers.IsSendDisabledByPolicyAsync(); } - if (await _vaultTimeoutService.IsLockedAsync()) + catch (Exception ex) { - return; - } - if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing) - { - SyncRefreshing = true; - await _syncService.FullSyncAsync(false); - return; + _logger.Value.Exception(ex); + throw; } - _doingLoad = true; - LoadedOnce = true; - ShowNoData = false; - Loading = true; - ShowList = false; - SendEnabled = !await AppHelpers.IsSendDisabledByPolicyAsync(); var groupedSends = new List(); var page = Page as SendGroupingsPage; @@ -146,8 +145,11 @@ namespace Bit.App.Pages { await LoadDataAsync(); - // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes - var uppercaseGroupNames = Device.RuntimePlatform == Device.iOS; +#if IOS + var uppercaseGroupNames = true; +#else + var uppercaseGroupNames = false; +#endif if (MainPage) { groupedSends.Add(new SendGroupingsPageListGroup( @@ -208,6 +210,11 @@ namespace Bit.App.Pages } } } + catch (Exception ex) + { + _logger.Value.Exception(ex); + throw; + } finally { _doingLoad = false; @@ -315,14 +322,22 @@ namespace Bit.App.Pages private async void SendOptionsAsync(SendView send) { - if ((Page as BaseContentPage).DoOnce()) + try { - var selection = await AppHelpers.SendListOptions(Page, send); - if (selection == AppResources.RemovePassword || selection == AppResources.Delete) + if ((Page as BaseContentPage).DoOnce()) { - await LoadAsync(); + var selection = await AppHelpers.SendListOptions(Page, send); + if (selection == AppResources.RemovePassword || selection == AppResources.Delete) + { + await LoadAsync(); + } } } + catch (Exception ex) + { + _logger.Value.Exception(ex); + throw; + } } } } diff --git a/src/Core/Pages/Settings/ExportVaultPage.xaml.cs b/src/Core/Pages/Settings/ExportVaultPage.xaml.cs index 4d7a4276c..be8d8f94e 100644 --- a/src/Core/Pages/Settings/ExportVaultPage.xaml.cs +++ b/src/Core/Pages/Settings/ExportVaultPage.xaml.cs @@ -29,7 +29,7 @@ namespace Bit.App.Pages { if (message.Command == "selectSaveFileResult") { - Device.BeginInvokeOnMainThread(() => + MainThread.BeginInvokeOnMainThread(() => { var data = message.Data as Tuple; if (data == null) diff --git a/src/Core/Pages/Settings/LoginPasswordlessRequestsListPage.xaml.cs b/src/Core/Pages/Settings/LoginPasswordlessRequestsListPage.xaml.cs index c3c25583c..0a68ab640 100644 --- a/src/Core/Pages/Settings/LoginPasswordlessRequestsListPage.xaml.cs +++ b/src/Core/Pages/Settings/LoginPasswordlessRequestsListPage.xaml.cs @@ -49,12 +49,10 @@ namespace Bit.App.Pages private void UpdatePlaceholder() { - // TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes - if (Device.RuntimePlatform == Device.Android) - { - MainThread.BeginInvokeOnMainThread(() => - _emptyPlaceholder.Source = ImageSource.FromFile(ThemeManager.UsingLightTheme ? "empty_login_requests" : "empty_login_requests_dark")); - } +#if ANDROID + MainThread.BeginInvokeOnMainThread(() => + _emptyPlaceholder.Source = ImageSource.FromFile(ThemeManager.UsingLightTheme ? "empty_login_requests" : "empty_login_requests_dark")); +#endif } } } diff --git a/src/Core/Pages/TabsPage.cs b/src/Core/Pages/TabsPage.cs index c9d02e320..0371b44a9 100644 --- a/src/Core/Pages/TabsPage.cs +++ b/src/Core/Pages/TabsPage.cs @@ -10,6 +10,7 @@ using Bit.Core.Abstractions; using Bit.Core.Models.Data; using Bit.Core.Models.Domain; using Bit.Core.Utilities; +using Bit.Core.Services; namespace Bit.App.Pages { @@ -87,21 +88,29 @@ namespace Bit.App.Pages protected override async void OnAppearing() { - base.OnAppearing(); - _broadcasterService.Subscribe(nameof(TabsPage), async (message) => + try { - if (message.Command == "syncCompleted") + base.OnAppearing(); + _broadcasterService.Subscribe(nameof(TabsPage), async (message) => { - MainThread.BeginInvokeOnMainThread(async () => await UpdateVaultButtonTitleAsync()); + if (message.Command == "syncCompleted") + { + MainThread.BeginInvokeOnMainThread(async () => await UpdateVaultButtonTitleAsync()); + } + }); + await UpdateVaultButtonTitleAsync(); + if (await _keyConnectorService.UserNeedsMigrationAsync()) + { + _messagingService.Send("convertAccountToKeyConnector"); } - }); - await UpdateVaultButtonTitleAsync(); - if (await _keyConnectorService.UserNeedsMigrationAsync()) - { - _messagingService.Send("convertAccountToKeyConnector"); - } - await ForcePasswordResetIfNeededAsync(); + await ForcePasswordResetIfNeededAsync(); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + throw; + } } private async Task ForcePasswordResetIfNeededAsync() @@ -174,23 +183,31 @@ namespace Bit.App.Pages protected override async void OnCurrentPageChanged() { - if (CurrentPage is NavigationPage navPage) + try { - if (_groupingsPage?.RootPage is GroupingsPage groupingsPage) + if (CurrentPage is NavigationPage navPage) { - await groupingsPage.HideAccountSwitchingOverlayAsync(); - } + if (_groupingsPage?.RootPage is GroupingsPage groupingsPage) + { + await groupingsPage.HideAccountSwitchingOverlayAsync(); + } - _messagingService.Send(ThemeManager.UPDATED_THEME_MESSAGE_KEY); - if (navPage.RootPage is GroupingsPage) - { - // Load something? - } - else if (navPage.RootPage is GeneratorPage genPage) - { - await genPage.InitAsync(); + _messagingService.Send(ThemeManager.UPDATED_THEME_MESSAGE_KEY); + if (navPage.RootPage is GroupingsPage) + { + // Load something? + } + else if (navPage.RootPage is GeneratorPage genPage) + { + await genPage.InitAsync(); + } } } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + throw; + } } public void OnPageReselected() diff --git a/src/Core/Pages/Vault/AttachmentsPage.xaml.cs b/src/Core/Pages/Vault/AttachmentsPage.xaml.cs index 319a2ffdd..9e56148db 100644 --- a/src/Core/Pages/Vault/AttachmentsPage.xaml.cs +++ b/src/Core/Pages/Vault/AttachmentsPage.xaml.cs @@ -33,7 +33,7 @@ namespace Bit.App.Pages { if (message.Command == "selectFileResult") { - Device.BeginInvokeOnMainThread(() => + MainThread.BeginInvokeOnMainThread(() => { var data = message.Data as Tuple; _vm.FileData = data.Item1; diff --git a/src/Core/Pages/Vault/CipherAddEditPage.xaml.cs b/src/Core/Pages/Vault/CipherAddEditPage.xaml.cs index 792e4a63a..1ff0be66c 100644 --- a/src/Core/Pages/Vault/CipherAddEditPage.xaml.cs +++ b/src/Core/Pages/Vault/CipherAddEditPage.xaml.cs @@ -7,6 +7,7 @@ using Bit.Core.Enums; using Bit.Core.Utilities; using Microsoft.Maui.Controls.PlatformConfiguration; using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific; +using Bit.Core.Services; namespace Bit.App.Pages { @@ -263,8 +264,16 @@ namespace Bit.App.Pages { MainThread.BeginInvokeOnMainThread(async () => { - await Navigation.PopModalAsync(); - await _vm.UpdateTotpKeyAsync(key); + try + { + await Navigation.PopModalAsync(); + await _vm.UpdateTotpKeyAsync(key); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + throw; + } }); }); diff --git a/src/Core/Pages/Vault/CipherDetailsPage.xaml.cs b/src/Core/Pages/Vault/CipherDetailsPage.xaml.cs index 79a783011..f10fbee58 100644 --- a/src/Core/Pages/Vault/CipherDetailsPage.xaml.cs +++ b/src/Core/Pages/Vault/CipherDetailsPage.xaml.cs @@ -63,12 +63,12 @@ namespace Bit.App.Pages { if (message.Command == "syncStarted") { - Device.BeginInvokeOnMainThread(() => IsBusy = true); + MainThread.BeginInvokeOnMainThread(() => IsBusy = true); } else if (message.Command == "syncCompleted") { await Task.Delay(500); - Device.BeginInvokeOnMainThread(() => + MainThread.BeginInvokeOnMainThread(() => { IsBusy = false; if (message.Data is Dictionary data && data.ContainsKey("successfully")) @@ -83,7 +83,7 @@ namespace Bit.App.Pages } else if (message.Command == "selectSaveFileResult") { - Device.BeginInvokeOnMainThread(() => + MainThread.BeginInvokeOnMainThread(() => { var data = message.Data as Tuple; if (data == null) diff --git a/src/Core/Pages/Vault/CiphersPageViewModel.cs b/src/Core/Pages/Vault/CiphersPageViewModel.cs index 44c786fd4..8a30deb8a 100644 --- a/src/Core/Pages/Vault/CiphersPageViewModel.cs +++ b/src/Core/Pages/Vault/CiphersPageViewModel.cs @@ -109,52 +109,59 @@ namespace Bit.App.Pages var cts = new CancellationTokenSource(); Task.Run(async () => { - List ciphers = null; - var searchable = !string.IsNullOrWhiteSpace(searchText) && searchText.Length > 1; - var shouldShowAllWhenEmpty = ShowAllIfSearchTextEmpty && string.IsNullOrEmpty(searchText); - if (searchable || shouldShowAllWhenEmpty) + try { - if (timeout != null) + List ciphers = null; + var searchable = !string.IsNullOrWhiteSpace(searchText) && searchText.Length > 1; + var shouldShowAllWhenEmpty = ShowAllIfSearchTextEmpty && string.IsNullOrEmpty(searchText); + if (searchable || shouldShowAllWhenEmpty) { - await Task.Delay(timeout.Value); - } - if (searchText != (Page as CiphersPage).SearchBar.Text - && - !shouldShowAllWhenEmpty) - { - return; - } + if (timeout != null) + { + await Task.Delay(timeout.Value); + } + if (searchText != (Page as CiphersPage).SearchBar.Text + && + !shouldShowAllWhenEmpty) + { + return; + } - previousCts?.Cancel(); - try - { - var vaultFilteredCiphers = await GetAllCiphersAsync(); - if (!shouldShowAllWhenEmpty) + previousCts?.Cancel(); + try { - ciphers = await _searchService.SearchCiphersAsync(searchText, - Filter ?? (c => c.IsDeleted == Deleted), vaultFilteredCiphers, cts.Token); + var vaultFilteredCiphers = await GetAllCiphersAsync(); + if (!shouldShowAllWhenEmpty) + { + ciphers = await _searchService.SearchCiphersAsync(searchText, + Filter ?? (c => c.IsDeleted == Deleted), vaultFilteredCiphers, cts.Token); + } + else + { + ciphers = vaultFilteredCiphers; + } + cts.Token.ThrowIfCancellationRequested(); } - else + catch (OperationCanceledException) { - ciphers = vaultFilteredCiphers; + return; } - cts.Token.ThrowIfCancellationRequested(); } - catch (OperationCanceledException) + if (ciphers == null) { - return; + ciphers = new List(); } + MainThread.BeginInvokeOnMainThread(() => + { + Ciphers.ResetWithRange(ciphers.Select(c => new CipherItemViewModel(c, _websiteIconsEnabled)).ToList()); + ShowNoData = !shouldShowAllWhenEmpty && searchable && Ciphers.Count == 0; + ShowList = (searchable || shouldShowAllWhenEmpty) && !ShowNoData; + }); } - if (ciphers == null) + catch (Exception ex) { - ciphers = new List(); + _logger.Exception(ex); } - MainThread.BeginInvokeOnMainThread(() => - { - Ciphers.ResetWithRange(ciphers.Select(c => new CipherItemViewModel(c, _websiteIconsEnabled)).ToList()); - ShowNoData = !shouldShowAllWhenEmpty && searchable && Ciphers.Count == 0; - ShowList = (searchable || shouldShowAllWhenEmpty) && !ShowNoData; - }); }, cts.Token); _searchCancellationTokenSource = cts; } diff --git a/src/Core/Pages/Vault/GroupingsPage/GroupingsPage.xaml.cs b/src/Core/Pages/Vault/GroupingsPage/GroupingsPage.xaml.cs index 137626807..46d65d87c 100644 --- a/src/Core/Pages/Vault/GroupingsPage/GroupingsPage.xaml.cs +++ b/src/Core/Pages/Vault/GroupingsPage/GroupingsPage.xaml.cs @@ -1,9 +1,9 @@ using Bit.App.Abstractions; using Bit.App.Controls; -using Bit.Core.Resources.Localization; using Bit.Core.Abstractions; using Bit.Core.Enums; using Bit.Core.Models.Data; +using Bit.Core.Resources.Localization; using Bit.Core.Services; using Bit.Core.Utilities; @@ -19,6 +19,7 @@ namespace Bit.App.Pages private readonly ICipherService _cipherService; private readonly IDeviceActionService _deviceActionService; private readonly IPlatformUtilsService _platformUtilsService; + private readonly ILogger _logger; private readonly GroupingsPageViewModel _vm; private readonly string _pageName; @@ -39,6 +40,8 @@ namespace Bit.App.Pages _cipherService = ServiceContainer.Resolve("cipherService"); _deviceActionService = ServiceContainer.Resolve("deviceActionService"); _platformUtilsService = ServiceContainer.Resolve("platformUtilsService"); + _logger = ServiceContainer.Resolve(); + _vm = BindingContext as GroupingsPageViewModel; _vm.Page = this; _vm.MainPage = mainPage; @@ -57,17 +60,14 @@ namespace Bit.App.Pages _vm.VaultFilterDescription = vaultFilterSelection; } - if (DeviceInfo.Platform == DevicePlatform.iOS) - { - _absLayout.Children.Remove(_fab); - ToolbarItems.Add(_addItem); - } - else - { - ToolbarItems.Add(_syncItem); - ToolbarItems.Add(_lockItem); - ToolbarItems.Add(_exitItem); - } +#if IOS + _absLayout.Children.Remove(_fab); + ToolbarItems.Add(_addItem); +#else + ToolbarItems.Add(_syncItem); + ToolbarItems.Add(_lockItem); + ToolbarItems.Add(_exitItem); +#endif if (deleted || showTotp) { _absLayout.Children.Remove(_fab); @@ -81,107 +81,115 @@ namespace Bit.App.Pages protected override async void OnAppearing() { - base.OnAppearing(); - if (_syncService.SyncInProgress) + try { - IsBusy = true; - } - - _accountAvatar?.OnAppearing(); - if (_vm.MainPage) - { - _vm.AvatarImageSource = await GetAvatarImageSourceAsync(); - } - - _broadcasterService.Subscribe(_pageName, async (message) => - { - try + base.OnAppearing(); + if (_syncService.SyncInProgress) { - if (message.Command == "syncStarted") + IsBusy = true; + } + + _accountAvatar?.OnAppearing(); + if (_vm.MainPage) + { + _vm.AvatarImageSource = await GetAvatarImageSourceAsync(); + } + + _broadcasterService.Subscribe(_pageName, async (message) => + { + try { - MainThread.BeginInvokeOnMainThread(() => IsBusy = true); - } - else if (message.Command == "syncCompleted") - { - await Task.Delay(500); - if (_vm.MainPage) + if (message.Command == "syncStarted") { - _vm.AvatarImageSource = await GetAvatarImageSourceAsync(); + MainThread.BeginInvokeOnMainThread(() => IsBusy = true); } - MainThread.BeginInvokeOnMainThread(() => + else if (message.Command == "syncCompleted") { - IsBusy = false; - if (_vm.LoadedOnce) + await Task.Delay(500); + if (_vm.MainPage) { - var task = _vm.LoadAsync(); + _vm.AvatarImageSource = await GetAvatarImageSourceAsync(); } - }); + MainThread.BeginInvokeOnMainThread(() => + { + IsBusy = false; + if (_vm.LoadedOnce) + { + var task = _vm.LoadAsync(); + } + }); + } } - } - catch (Exception ex) - { - LoggerHelper.LogEvenIfCantBeResolved(ex); - } - }); - - await LoadOnAppearedAsync(_mainLayout, false, async () => - { - if (_previousPage == null) - { - if (!_syncService.SyncInProgress || (await _cipherService.GetAllAsync()).Any()) + catch (Exception ex) { - try - { - await _vm.LoadAsync(); - } - catch (Exception e) when (e.Message.Contains("No key.")) - { - await Task.Delay(1000); - await _vm.LoadAsync(); - } + LoggerHelper.LogEvenIfCantBeResolved(ex); } - else + }); + + await LoadOnAppearedAsync(_mainLayout, false, async () => + { + if (_previousPage == null) { - await Task.Delay(5000); - if (!_vm.Loaded) + if (!_syncService.SyncInProgress || (await _cipherService.GetAllAsync()).Any()) { - await _vm.LoadAsync(); + try + { + await _vm.LoadAsync(); + } + catch (Exception e) when (e.Message.Contains("No key.")) + { + await Task.Delay(1000); + await _vm.LoadAsync(); + } + } + else + { + await Task.Delay(5000); + if (!_vm.Loaded) + { + await _vm.LoadAsync(); + } } } - } - await ShowPreviousPageAsync(); - AdjustToolbar(); - }, _mainContent); + await ShowPreviousPageAsync(); + AdjustToolbar(); + }, _mainContent); - if (!_vm.MainPage) - { - return; - } - - // Push registration - var lastPushRegistration = await _stateService.GetPushLastRegistrationDateAsync(); - lastPushRegistration = lastPushRegistration.GetValueOrDefault(DateTime.MinValue); - if (DeviceInfo.Platform == DevicePlatform.iOS) - { - var pushPromptShow = await _stateService.GetPushInitialPromptShownAsync(); - if (!pushPromptShow.GetValueOrDefault(false)) + if (!_vm.MainPage) { - await _stateService.SetPushInitialPromptShownAsync(true); - await DisplayAlert(AppResources.EnableAutomaticSyncing, AppResources.PushNotificationAlert, - AppResources.OkGotIt); + return; } - if (!pushPromptShow.GetValueOrDefault(false) || - DateTime.UtcNow - lastPushRegistration > TimeSpan.FromDays(1)) + + // Push registration + var lastPushRegistration = await _stateService.GetPushLastRegistrationDateAsync(); + lastPushRegistration = lastPushRegistration.GetValueOrDefault(DateTime.MinValue); + if (DeviceInfo.Platform == DevicePlatform.iOS) { - await _pushNotificationService.RegisterAsync(); + var pushPromptShow = await _stateService.GetPushInitialPromptShownAsync(); + if (!pushPromptShow.GetValueOrDefault(false)) + { + await _stateService.SetPushInitialPromptShownAsync(true); + await DisplayAlert(AppResources.EnableAutomaticSyncing, AppResources.PushNotificationAlert, + AppResources.OkGotIt); + } + if (!pushPromptShow.GetValueOrDefault(false) || + DateTime.UtcNow - lastPushRegistration > TimeSpan.FromDays(1)) + { + await _pushNotificationService.RegisterAsync(); + } + } + else if (DeviceInfo.Platform == DevicePlatform.Android) + { + if (DateTime.UtcNow - lastPushRegistration > TimeSpan.FromDays(1)) + { + await _pushNotificationService.RegisterAsync(); + } } } - else if (DeviceInfo.Platform == DevicePlatform.Android) + catch (Exception ex) { - if (DateTime.UtcNow - lastPushRegistration > TimeSpan.FromDays(1)) - { - await _pushNotificationService.RegisterAsync(); - } + _logger.Exception(ex); + throw; } } @@ -195,14 +203,22 @@ namespace Bit.App.Pages return false; } - protected override async void OnDisappearing() + protected override void OnDisappearing() { - base.OnDisappearing(); - IsBusy = false; - _vm.StopCiphersTotpTick().FireAndForget(); - _broadcasterService.Unsubscribe(_pageName); - _vm.DisableRefreshing(); - _accountAvatar?.OnDisappearing(); + try + { + base.OnDisappearing(); + IsBusy = false; + _vm.StopCiphersTotpTick().FireAndForget(); + _broadcasterService.Unsubscribe(_pageName); + _vm.DisableRefreshing(); + _accountAvatar?.OnDisappearing(); + } + catch (Exception ex) + { + _logger.Exception(ex); + throw; + } } private async void RowSelected(object sender, SelectionChangedEventArgs e) @@ -264,45 +280,80 @@ namespace Bit.App.Pages private async void Search_Clicked(object sender, EventArgs e) { - await _accountListOverlay.HideAsync(); - if (DoOnce()) + try { - var page = new CiphersPage(_vm.Filter, _vm.MainPage ? null : _vm.PageTitle, deleted: _vm.Deleted); - await Navigation.PushModalAsync(new NavigationPage(page)); + await _accountListOverlay.HideAsync(); + if (DoOnce()) + { + var page = new CiphersPage(_vm.Filter, _vm.MainPage ? null : _vm.PageTitle, deleted: _vm.Deleted); + await Navigation.PushModalAsync(new NavigationPage(page)); + } + } + catch (Exception ex) + { + _logger.Exception(ex); } } private async void Sync_Clicked(object sender, EventArgs e) { - await _accountListOverlay.HideAsync(); - await _vm.SyncAsync(); + try + { + await _accountListOverlay.HideAsync(); + await _vm.SyncAsync(); + } + catch (Exception ex) + { + _logger.Exception(ex); + } } private async void Lock_Clicked(object sender, EventArgs e) { - await _accountListOverlay.HideAsync(); - await _vaultTimeoutService.LockAsync(true, true); + try + { + await _accountListOverlay.HideAsync(); + await _vaultTimeoutService.LockAsync(true, true); + } + catch (Exception ex) + { + _logger.Exception(ex); + } } private async void Exit_Clicked(object sender, EventArgs e) { - await _accountListOverlay.HideAsync(); - await _vm.ExitAsync(); + try + { + await _accountListOverlay.HideAsync(); + await _vm.ExitAsync(); + } + catch (Exception ex) + { + _logger.Exception(ex); + } } private async void AddButton_Clicked(object sender, EventArgs e) { - var skipAction = _accountListOverlay.IsVisible && DeviceInfo.Platform == DevicePlatform.Android; - await _accountListOverlay.HideAsync(); - if (skipAction) + try { - // Account list in the process of closing via tapping on invisible FAB, skip this attempt - return; + var skipAction = _accountListOverlay.IsVisible && DeviceInfo.Platform == DevicePlatform.Android; + await _accountListOverlay.HideAsync(); + if (skipAction) + { + // Account list in the process of closing via tapping on invisible FAB, skip this attempt + return; + } + if (!_vm.Deleted && DoOnce()) + { + var page = new CipherAddEditPage(null, _vm.Type, _vm.FolderId, _vm.CollectionId, _vm.GetVaultFilterOrgId()); + await Navigation.PushModalAsync(new NavigationPage(page)); + } } - if (!_vm.Deleted && DoOnce()) + catch (Exception ex) { - var page = new CipherAddEditPage(null, _vm.Type, _vm.FolderId, _vm.CollectionId, _vm.GetVaultFilterOrgId()); - await Navigation.PushModalAsync(new NavigationPage(page)); + _logger.Exception(ex); } } diff --git a/src/Core/Pages/Vault/GroupingsPage/GroupingsPageViewModel.cs b/src/Core/Pages/Vault/GroupingsPage/GroupingsPageViewModel.cs index 801eb261c..a249b0b35 100644 --- a/src/Core/Pages/Vault/GroupingsPage/GroupingsPageViewModel.cs +++ b/src/Core/Pages/Vault/GroupingsPage/GroupingsPageViewModel.cs @@ -166,41 +166,52 @@ namespace Bit.App.Pages { return; } - var authed = await _stateService.IsAuthenticatedAsync(); - if (!authed) - { - return; - } - if (await _vaultTimeoutService.IsLockedAsync()) - { - return; - } - if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing) - { - SyncRefreshing = true; - await _syncService.SyncPasswordlessLoginRequestsAsync(); - await _syncService.FullSyncAsync(false); - return; - } - _deviceActionService.SetScreenCaptureAllowedAsync().FireAndForget(); - - await InitVaultFilterAsync(MainPage); - if (MainPage) + try { - PageTitle = ShowVaultFilter ? AppResources.Vaults : AppResources.MyVault; + var authed = await _stateService.IsAuthenticatedAsync(); + if (!authed) + { + return; + } + if (await _vaultTimeoutService.IsLockedAsync()) + { + return; + } + if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing) + { + SyncRefreshing = true; + await _syncService.SyncPasswordlessLoginRequestsAsync(); + await _syncService.FullSyncAsync(false); + return; + } + + _deviceActionService.SetScreenCaptureAllowedAsync().FireAndForget(); + + await InitVaultFilterAsync(MainPage); + if (MainPage) + { + PageTitle = ShowVaultFilter ? AppResources.Vaults : AppResources.MyVault; + } + + _doingLoad = true; + LoadedOnce = true; + ShowNoData = false; + Loading = true; + ShowList = false; + ShowAddCipherButton = !Deleted; + + _websiteIconsEnabled = await _stateService.GetDisableFaviconAsync() != true; + } + catch (Exception ex) + { + _logger.Exception(ex); + throw; } - _doingLoad = true; - LoadedOnce = true; - ShowNoData = false; - Loading = true; - ShowList = false; - ShowAddCipherButton = !Deleted; var groupedItems = new List(); var page = Page as GroupingsPage; - _websiteIconsEnabled = await _stateService.GetDisableFaviconAsync() != true; try { await LoadDataAsync(); @@ -307,11 +318,6 @@ namespace Bit.App.Pages await MainThread.InvokeOnMainThreadAsync(() => { -#if IOS - // HACK: [PS-536] Fix to avoid blank list after back navigation on unlocking with previous page info - // because of update to XF v5.0.0.2401 - GroupedItems.Clear(); -#endif GroupedItems.ReplaceRange(items); }); } @@ -335,23 +341,22 @@ namespace Bit.App.Pages await MainThread.InvokeOnMainThreadAsync(() => { - if (groupedItems.Any()) - { -#if IOS - // HACK: [PS-536] Fix to avoid blank list after back navigation on unlocking with previous page info - // because of update to XF v5.0.0.2401 - GroupedItems.Clear(); -#endif - GroupedItems.ReplaceRange(new List { new GroupingsPageHeaderListItem(groupedItems[0].Name, groupedItems[0].ItemCount) }); - GroupedItems.AddRange(items); - } - else + if (!groupedItems.Any()) { GroupedItems.Clear(); + return; } + + GroupedItems.ReplaceRange(new List { new GroupingsPageHeaderListItem(groupedItems[0].Name, groupedItems[0].ItemCount) }); + GroupedItems.AddRange(items); }); } } + catch (Exception ex) + { + _logger.Exception(ex); + throw; + } finally { _doingLoad = false; diff --git a/src/Core/Pages/Vault/PasswordHistoryPageViewModel.cs b/src/Core/Pages/Vault/PasswordHistoryPageViewModel.cs index bfc699ba9..3ab5a182a 100644 --- a/src/Core/Pages/Vault/PasswordHistoryPageViewModel.cs +++ b/src/Core/Pages/Vault/PasswordHistoryPageViewModel.cs @@ -42,7 +42,7 @@ namespace Bit.App.Pages { var cipher = await _cipherService.GetAsync(CipherId); var decCipher = await cipher.DecryptAsync(); - Device.BeginInvokeOnMainThread(() => + MainThread.BeginInvokeOnMainThread(() => { History.ResetWithRange(decCipher.PasswordHistory ?? new List()); ShowNoData = History.Count == 0; diff --git a/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs b/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs index 53161a44c..e01502558 100644 --- a/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs +++ b/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs @@ -204,7 +204,18 @@ namespace Bit.iOS.Core.Controllers var tasks = Task.Run(async () => { await Task.Delay(500); - NSRunLoop.Main.BeginInvokeOnMainThread(async () => await PromptBiometricAsync()); + NSRunLoop.Main.BeginInvokeOnMainThread(async () => + { + try + { + await PromptBiometricAsync(); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + throw; + } + }); }); } } diff --git a/src/iOS.Core/Services/ClipboardService.cs b/src/iOS.Core/Services/ClipboardService.cs index 0db6b1150..ae72ef194 100644 --- a/src/iOS.Core/Services/ClipboardService.cs +++ b/src/iOS.Core/Services/ClipboardService.cs @@ -31,7 +31,7 @@ namespace Bit.iOS.Core.Services var dictArr = new NSDictionary[1]; dictArr[0] = new NSDictionary(new NSString(UTType.UTF8PlainText), new NSString(text)); - Device.BeginInvokeOnMainThread(() => UIPasteboard.General.SetItems(dictArr, new UIPasteboardOptions + MainThread.BeginInvokeOnMainThread(() => UIPasteboard.General.SetItems(dictArr, new UIPasteboardOptions { LocalOnly = true, ExpirationDate = clearSeconds > 0 ? NSDate.FromTimeIntervalSinceNow(clearSeconds) : null