using System; using System.Threading.Tasks; using System.Windows.Input; using Bit.Core.Abstractions; using Bit.Core.Utilities; using Xamarin.CommunityToolkit.ObjectModel; using Xamarin.Forms; namespace Bit.App.Controls { public partial class AccountSwitchingOverlayView : ContentView { public static readonly BindableProperty MainPageProperty = BindableProperty.Create( nameof(MainPage), typeof(ContentPage), typeof(AccountSwitchingOverlayView), defaultBindingMode: BindingMode.OneWay); public static readonly BindableProperty MainFabProperty = BindableProperty.Create( nameof(MainFab), typeof(View), typeof(AccountSwitchingOverlayView), defaultBindingMode: BindingMode.OneWay); public ContentPage MainPage { get => (ContentPage)GetValue(MainPageProperty); set => SetValue(MainPageProperty, value); } public View MainFab { get => (View)GetValue(MainFabProperty); set => SetValue(MainFabProperty, value); } readonly LazyResolve _logger = new LazyResolve("logger"); public AccountSwitchingOverlayView() { InitializeComponent(); ToggleVisibililtyCommand = new AsyncCommand(ToggleVisibilityAsync, onException: ex => _logger.Value.Exception(ex), allowsMultipleExecutions: false); SelectAccountCommand = new AsyncCommand(SelectAccountAsync, onException: ex => _logger.Value.Exception(ex), allowsMultipleExecutions: false); LongPressAccountCommand = new AsyncCommand(LongPressAccountAsync, onException: ex => _logger.Value.Exception(ex), allowsMultipleExecutions: false); } public AccountSwitchingOverlayViewModel ViewModel => BindingContext as AccountSwitchingOverlayViewModel; public ICommand ToggleVisibililtyCommand { get; } public ICommand SelectAccountCommand { get; } public ICommand LongPressAccountCommand { get; } public int AccountListRowHeight => Device.RuntimePlatform == Device.Android ? 74 : 70; public bool LongPressAccountEnabled { get; set; } = true; public Action AfterHide { get; set; } public async Task ToggleVisibilityAsync() { if (IsVisible) { await HideAsync(); } else { await ShowAsync(); } } public async Task ShowAsync() { if (ViewModel == null) { return; } await ViewModel.RefreshAccountViewsAsync(); await Device.InvokeOnMainThreadAsync(async () => { // start listView in default (off-screen) position await _accountListContainer.TranslateTo(0, _accountListContainer.Height * -1, 0); // re-measure in case accounts have been removed without changing screens if (ViewModel.AccountViews != null) { _accountListView.HeightRequest = AccountListRowHeight * ViewModel.AccountViews.Count; } // set overlay opacity to zero before making visible and start fade-in Opacity = 0; IsVisible = true; this.FadeTo(1, 100); if (Device.RuntimePlatform == Device.Android && MainFab != null) { // start fab fade-out MainFab.FadeTo(0, 200); } // slide account list into view await _accountListContainer.TranslateTo(0, 0, 200, Easing.SinOut); }); } public async Task HideAsync() { if (!IsVisible) { // already hidden, don't animate again return; } // Not all animations are awaited. This is intentional to allow multiple simultaneous animations. await Device.InvokeOnMainThreadAsync(async () => { // start overlay fade-out this.FadeTo(0, 200); if (Device.RuntimePlatform == Device.Android && MainFab != null) { // start fab fade-in MainFab.FadeTo(1, 200); } // slide account list out of view await _accountListContainer.TranslateTo(0, _accountListContainer.Height * -1, 200, Easing.SinIn); // remove overlay IsVisible = false; AfterHide?.Invoke(); }); } private async void FreeSpaceOverlay_Tapped(object sender, EventArgs e) { try { await HideAsync(); } catch (Exception ex) { _logger.Value.Exception(ex); } } private async Task SelectAccountAsync(AccountViewCellViewModel item) { try { await Task.Delay(100); await HideAsync(); ViewModel?.SelectAccountCommand?.Execute(item); } catch (Exception ex) { _logger.Value.Exception(ex); } } private async Task LongPressAccountAsync(AccountViewCellViewModel item) { if (!LongPressAccountEnabled || !item.IsAccount) { return; } try { await Task.Delay(100); await HideAsync(); ViewModel?.LongPressAccountCommand?.Execute( new Tuple(MainPage, item)); } catch (Exception ex) { _logger.Value.Exception(ex); } } } }