bitwarden-mobile/src/App/Pages/Send/SendGroupingsPage/SendGroupingsPageViewModel.cs

341 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Essentials;
using Xamarin.Forms;
using DeviceType = Bit.Core.Enums.DeviceType;
namespace Bit.App.Pages
{
public class SendGroupingsPageViewModel : BaseViewModel
{
private bool _sendEnabled;
private bool _refreshing;
private bool _doingLoad;
private bool _loading;
private bool _loaded;
private bool _showNoData;
private bool _showList;
private bool _syncRefreshing;
private string _noDataText;
private List<SendView> _allSends;
private Dictionary<SendType, int> _typeCounts = new Dictionary<SendType, int>();
private readonly ISendService _sendService;
private readonly ISyncService _syncService;
private readonly IStateService _stateService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly IDeviceActionService _deviceActionService;
private readonly IPlatformUtilsService _platformUtilsService;
public SendGroupingsPageViewModel()
{
_sendService = ServiceContainer.Resolve<ISendService>("sendService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
Loading = true;
PageTitle = AppResources.Send;
GroupedSends = new ObservableRangeCollection<ISendGroupingsPageListItem>();
RefreshCommand = new Command(async () =>
{
Refreshing = true;
await LoadAsync();
});
SendOptionsCommand = new Command<SendView>(SendOptionsAsync);
}
public bool MainPage { get; set; }
public SendType? Type { get; set; }
public Func<SendView, bool> Filter { get; set; }
public bool HasSends { get; set; }
public List<SendView> Sends { get; set; }
public bool SendEnabled
{
get => _sendEnabled;
set => SetProperty(ref _sendEnabled, value);
}
public bool Refreshing
{
get => _refreshing;
set => SetProperty(ref _refreshing, value);
}
public bool SyncRefreshing
{
get => _syncRefreshing;
set => SetProperty(ref _syncRefreshing, value);
}
public bool Loading
{
get => _loading;
set => SetProperty(ref _loading, value);
}
public bool Loaded
{
get => _loaded;
set => SetProperty(ref _loaded, value);
}
public bool ShowNoData
{
get => _showNoData;
set => SetProperty(ref _showNoData, value);
}
public string NoDataText
{
get => _noDataText;
set => SetProperty(ref _noDataText, value);
}
public bool ShowList
{
get => _showList;
set => SetProperty(ref _showList, value);
}
public ObservableRangeCollection<ISendGroupingsPageListItem> GroupedSends { get; set; }
public Command RefreshCommand { get; set; }
public Command<SendView> SendOptionsCommand { get; set; }
public bool LoadedOnce { get; set; }
public async Task LoadAsync()
{
if (_doingLoad)
{
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();
var groupedSends = new List<SendGroupingsPageListGroup>();
var page = Page as SendGroupingsPage;
try
{
await LoadDataAsync();
var uppercaseGroupNames = _deviceActionService.DeviceType == DeviceType.iOS;
if (MainPage)
{
groupedSends.Add(new SendGroupingsPageListGroup(
AppResources.Types, 0, uppercaseGroupNames, true)
{
new SendGroupingsPageListItem
{
Type = SendType.Text,
ItemCount = (_typeCounts.ContainsKey(SendType.Text) ?
_typeCounts[SendType.Text] : 0).ToString("N0")
},
new SendGroupingsPageListItem
{
Type = SendType.File,
ItemCount = (_typeCounts.ContainsKey(SendType.File) ?
_typeCounts[SendType.File] : 0).ToString("N0")
},
});
}
if (Sends?.Any() ?? false)
{
var sendsListItems = Sends.Select(s => new SendGroupingsPageListItem
{
Send = s,
ShowOptions = SendEnabled
}).ToList();
groupedSends.Add(new SendGroupingsPageListGroup(sendsListItems,
MainPage ? AppResources.AllSends : AppResources.Sends, sendsListItems.Count,
uppercaseGroupNames, !MainPage));
}
// TODO: refactor this
if (Device.RuntimePlatform == Device.Android
||
GroupedSends.Any())
{
var items = new List<ISendGroupingsPageListItem>();
foreach (var itemGroup in groupedSends)
{
items.Add(new SendGroupingsPageHeaderListItem(itemGroup.Name, itemGroup.ItemCount));
items.AddRange(itemGroup);
}
GroupedSends.ReplaceRange(items);
}
else
{
// HACK: we need this on iOS, so that it doesn't crash when adding coming from an empty list
var first = true;
var items = new List<ISendGroupingsPageListItem>();
foreach (var itemGroup in groupedSends)
{
if (!first)
{
items.Add(new SendGroupingsPageHeaderListItem(itemGroup.Name, itemGroup.ItemCount));
}
else
{
first = false;
}
items.AddRange(itemGroup);
}
if (groupedSends.Any())
{
GroupedSends.ReplaceRange(new List<ISendGroupingsPageListItem> { new SendGroupingsPageHeaderListItem(groupedSends[0].Name, groupedSends[0].ItemCount) });
GroupedSends.AddRange(items);
}
else
{
GroupedSends.Clear();
}
}
}
finally
{
_doingLoad = false;
Loaded = true;
Loading = false;
ShowNoData = (MainPage && !HasSends) || !groupedSends.Any();
ShowList = !ShowNoData;
DisableRefreshing();
}
}
public void DisableRefreshing()
{
Refreshing = false;
SyncRefreshing = false;
}
public async Task SelectSendAsync(SendView send)
{
var page = new SendAddEditPage(null, send.Id);
await Page.Navigation.PushModalAsync(new NavigationPage(page));
}
public async Task SelectTypeAsync(SendType type)
{
string title = null;
switch (type)
{
case SendType.Text:
title = AppResources.TypeText;
break;
case SendType.File:
title = AppResources.TypeFile;
break;
default:
break;
}
var page = new SendGroupingsPage(false, type, title);
await Page.Navigation.PushAsync(page);
}
public async Task SyncAsync()
{
if (Connectivity.NetworkAccess == NetworkAccess.None)
{
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
AppResources.InternetConnectionRequiredTitle);
return;
}
await _deviceActionService.ShowLoadingAsync(AppResources.Syncing);
try
{
await _syncService.FullSyncAsync(false, true);
await _deviceActionService.HideLoadingAsync();
_platformUtilsService.ShowToast("success", null, AppResources.SyncingComplete);
}
catch
{
await _deviceActionService.HideLoadingAsync();
_platformUtilsService.ShowToast("error", null, AppResources.SyncingFailed);
}
}
public void ShowAbout()
{
_platformUtilsService.LaunchUri("https://bitwarden.com/products/send/");
}
private async Task LoadDataAsync()
{
NoDataText = AppResources.NoSends;
_allSends = await _sendService.GetAllDecryptedAsync();
HasSends = _allSends.Any();
_typeCounts.Clear();
Filter = null;
if (MainPage)
{
Sends = _allSends;
foreach (var c in _allSends)
{
if (_typeCounts.ContainsKey(c.Type))
{
_typeCounts[c.Type] = _typeCounts[c.Type] + 1;
}
else
{
_typeCounts.Add(c.Type, 1);
}
}
}
else
{
if (Type != null)
{
Filter = c => c.Type == Type.Value;
}
else
{
PageTitle = AppResources.AllSends;
}
Sends = Filter != null ? _allSends.Where(Filter).ToList() : _allSends;
}
}
private async void SendOptionsAsync(SendView send)
{
if ((Page as BaseContentPage).DoOnce())
{
var selection = await AppHelpers.SendListOptions(Page, send);
if (selection == AppResources.RemovePassword || selection == AppResources.Delete)
{
await LoadAsync();
}
}
}
}
}