2019-04-19 23:45:16 +03:00
|
|
|
|
using Bit.App.Resources;
|
|
|
|
|
using Bit.Core.Abstractions;
|
2019-04-24 23:50:34 +03:00
|
|
|
|
using Bit.Core.Enums;
|
2019-04-19 23:45:16 +03:00
|
|
|
|
using Bit.Core.Models.Domain;
|
|
|
|
|
using Bit.Core.Models.View;
|
2019-03-29 19:52:57 +03:00
|
|
|
|
using Bit.Core.Utilities;
|
2019-03-29 06:52:33 +03:00
|
|
|
|
using System.Collections.Generic;
|
2019-04-19 23:45:16 +03:00
|
|
|
|
using System.Linq;
|
2019-03-29 19:52:57 +03:00
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using Xamarin.Forms;
|
2019-03-29 06:52:33 +03:00
|
|
|
|
|
|
|
|
|
namespace Bit.App.Pages
|
|
|
|
|
{
|
2019-03-29 20:24:44 +03:00
|
|
|
|
public class GroupingsPageViewModel : BaseViewModel
|
2019-03-29 06:52:33 +03:00
|
|
|
|
{
|
2019-03-29 19:52:57 +03:00
|
|
|
|
private bool _loading = false;
|
2019-04-19 23:45:16 +03:00
|
|
|
|
private bool _loaded = false;
|
2019-04-25 06:28:41 +03:00
|
|
|
|
private bool _showAddCipherButton = false;
|
|
|
|
|
private bool _showNoData = false;
|
|
|
|
|
private bool _showList = false;
|
|
|
|
|
private string _noDataText;
|
2019-04-19 23:45:16 +03:00
|
|
|
|
private List<CipherView> _allCiphers;
|
|
|
|
|
|
|
|
|
|
private readonly ICipherService _cipherService;
|
|
|
|
|
private readonly IFolderService _folderService;
|
|
|
|
|
private readonly ICollectionService _collectionService;
|
|
|
|
|
private readonly ISyncService _syncService;
|
2019-03-29 19:52:57 +03:00
|
|
|
|
|
2019-03-29 20:24:44 +03:00
|
|
|
|
public GroupingsPageViewModel()
|
2019-03-29 06:52:33 +03:00
|
|
|
|
{
|
2019-04-19 23:45:16 +03:00
|
|
|
|
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
|
|
|
|
_folderService = ServiceContainer.Resolve<IFolderService>("folderService");
|
|
|
|
|
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
|
|
|
|
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
|
|
|
|
|
2019-04-24 23:50:34 +03:00
|
|
|
|
PageTitle = AppResources.MyVault;
|
2019-04-19 23:45:16 +03:00
|
|
|
|
GroupedItems = new ExtendedObservableCollection<GroupingsPageListGroup>();
|
2019-03-29 19:52:57 +03:00
|
|
|
|
LoadCommand = new Command(async () => await LoadAsync());
|
2019-04-25 06:28:41 +03:00
|
|
|
|
AddCipherCommand = new Command(() => { /* TODO */ });
|
2019-03-29 19:52:57 +03:00
|
|
|
|
}
|
|
|
|
|
|
2019-04-19 23:45:16 +03:00
|
|
|
|
public bool ShowFavorites { get; set; } = true;
|
|
|
|
|
public bool ShowFolders { get; set; } = true;
|
|
|
|
|
public bool ShowCollections { get; set; } = true;
|
2019-04-24 23:50:34 +03:00
|
|
|
|
public bool MainPage { get; set; }
|
|
|
|
|
public CipherType? Type { get; set; }
|
|
|
|
|
public string FolderId { get; set; }
|
|
|
|
|
public string CollectionId { get; set; }
|
2019-04-19 23:45:16 +03:00
|
|
|
|
|
|
|
|
|
public List<CipherView> Ciphers { get; set; }
|
|
|
|
|
public List<CipherView> FavoriteCiphers { get; set; }
|
|
|
|
|
public List<CipherView> NoFolderCiphers { get; set; }
|
|
|
|
|
public List<FolderView> Folders { get; set; }
|
|
|
|
|
public List<TreeNode<FolderView>> NestedFolders { get; set; }
|
|
|
|
|
public List<Core.Models.View.CollectionView> Collections { get; set; }
|
|
|
|
|
public List<TreeNode<Core.Models.View.CollectionView>> NestedCollections { get; set; }
|
|
|
|
|
|
2019-03-29 19:52:57 +03:00
|
|
|
|
public bool Loading
|
|
|
|
|
{
|
|
|
|
|
get => _loading;
|
|
|
|
|
set => SetProperty(ref _loading, value);
|
|
|
|
|
}
|
2019-04-19 23:45:16 +03:00
|
|
|
|
public bool Loaded
|
2019-03-29 19:52:57 +03:00
|
|
|
|
{
|
2019-04-19 23:45:16 +03:00
|
|
|
|
get => _loaded;
|
2019-04-25 06:28:41 +03:00
|
|
|
|
set => SetProperty(ref _loaded, value);
|
|
|
|
|
}
|
|
|
|
|
public bool ShowAddCipherButton
|
|
|
|
|
{
|
|
|
|
|
get => _showAddCipherButton;
|
|
|
|
|
set => SetProperty(ref _showAddCipherButton, 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);
|
2019-04-19 23:45:16 +03:00
|
|
|
|
}
|
|
|
|
|
public ExtendedObservableCollection<GroupingsPageListGroup> GroupedItems { get; set; }
|
|
|
|
|
public Command LoadCommand { get; set; }
|
2019-04-25 06:28:41 +03:00
|
|
|
|
public Command AddCipherCommand { get; set; }
|
2019-03-29 19:52:57 +03:00
|
|
|
|
|
2019-04-19 23:45:16 +03:00
|
|
|
|
public async Task LoadAsync()
|
|
|
|
|
{
|
2019-04-25 06:28:41 +03:00
|
|
|
|
ShowNoData = false;
|
|
|
|
|
Loading = true;
|
|
|
|
|
ShowList = false;
|
|
|
|
|
ShowAddCipherButton = true;
|
|
|
|
|
var groupedItems = new List<GroupingsPageListGroup>();
|
|
|
|
|
|
2019-03-29 19:52:57 +03:00
|
|
|
|
try
|
|
|
|
|
{
|
2019-04-24 23:50:34 +03:00
|
|
|
|
await LoadDataAsync();
|
2019-04-19 23:45:16 +03:00
|
|
|
|
|
|
|
|
|
var favListItems = FavoriteCiphers?.Select(c => new GroupingsPageListItem { Cipher = c }).ToList();
|
2019-04-24 23:50:34 +03:00
|
|
|
|
var ciphersListItems = Ciphers?.Select(c => new GroupingsPageListItem { Cipher = c }).ToList();
|
2019-04-19 23:45:16 +03:00
|
|
|
|
var folderListItems = NestedFolders?.Select(f => new GroupingsPageListItem { Folder = f.Node }).ToList();
|
|
|
|
|
var collectionListItems = NestedCollections?.Select(c =>
|
|
|
|
|
new GroupingsPageListItem { Collection = c.Node }).ToList();
|
|
|
|
|
|
|
|
|
|
if(favListItems?.Any() ?? false)
|
2019-03-29 19:52:57 +03:00
|
|
|
|
{
|
2019-04-23 00:08:37 +03:00
|
|
|
|
groupedItems.Add(new GroupingsPageListGroup(favListItems, AppResources.Favorites,
|
|
|
|
|
Device.RuntimePlatform == Device.iOS));
|
2019-04-19 23:45:16 +03:00
|
|
|
|
}
|
|
|
|
|
if(folderListItems?.Any() ?? false)
|
|
|
|
|
{
|
2019-04-23 00:08:37 +03:00
|
|
|
|
groupedItems.Add(new GroupingsPageListGroup(folderListItems, AppResources.Folders,
|
|
|
|
|
Device.RuntimePlatform == Device.iOS));
|
2019-04-19 23:45:16 +03:00
|
|
|
|
}
|
|
|
|
|
if(collectionListItems?.Any() ?? false)
|
|
|
|
|
{
|
2019-04-23 00:08:37 +03:00
|
|
|
|
groupedItems.Add(new GroupingsPageListGroup(collectionListItems, AppResources.Collections,
|
|
|
|
|
Device.RuntimePlatform == Device.iOS));
|
2019-04-19 23:45:16 +03:00
|
|
|
|
}
|
2019-04-24 23:50:34 +03:00
|
|
|
|
if(ciphersListItems?.Any() ?? false)
|
|
|
|
|
{
|
|
|
|
|
groupedItems.Add(new GroupingsPageListGroup(ciphersListItems, AppResources.Items,
|
|
|
|
|
Device.RuntimePlatform == Device.iOS));
|
|
|
|
|
}
|
2019-04-19 23:45:16 +03:00
|
|
|
|
GroupedItems.ResetWithRange(groupedItems);
|
2019-03-29 19:52:57 +03:00
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
2019-04-25 06:28:41 +03:00
|
|
|
|
ShowNoData = !groupedItems.Any();
|
|
|
|
|
ShowList = !ShowNoData;
|
2019-04-19 23:45:16 +03:00
|
|
|
|
Loaded = true;
|
2019-04-25 06:28:41 +03:00
|
|
|
|
Loading = false;
|
2019-04-19 23:45:16 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-24 18:23:03 +03:00
|
|
|
|
public async Task SelectCipherAsync(CipherView cipher)
|
|
|
|
|
{
|
|
|
|
|
var page = new ViewPage(cipher.Id);
|
|
|
|
|
await Page.Navigation.PushModalAsync(new NavigationPage(page));
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-24 23:50:34 +03:00
|
|
|
|
public async Task SelectFolderAsync(CipherType type)
|
2019-04-19 23:45:16 +03:00
|
|
|
|
{
|
2019-04-24 23:50:34 +03:00
|
|
|
|
string title = null;
|
|
|
|
|
switch(Type.Value)
|
2019-04-19 23:45:16 +03:00
|
|
|
|
{
|
2019-04-24 23:50:34 +03:00
|
|
|
|
case CipherType.Login:
|
|
|
|
|
title = AppResources.Logins;
|
|
|
|
|
break;
|
|
|
|
|
case CipherType.SecureNote:
|
|
|
|
|
title = AppResources.SecureNotes;
|
|
|
|
|
break;
|
|
|
|
|
case CipherType.Card:
|
|
|
|
|
title = AppResources.Cards;
|
|
|
|
|
break;
|
|
|
|
|
case CipherType.Identity:
|
|
|
|
|
title = AppResources.Identities;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2019-04-19 23:45:16 +03:00
|
|
|
|
}
|
2019-04-24 23:50:34 +03:00
|
|
|
|
var page = new GroupingsPage(false, type, null, null, title);
|
|
|
|
|
await Page.Navigation.PushAsync(page);
|
2019-04-19 23:45:16 +03:00
|
|
|
|
}
|
|
|
|
|
|
2019-04-24 23:50:34 +03:00
|
|
|
|
public async Task SelectFolderAsync(FolderView folder)
|
2019-04-19 23:45:16 +03:00
|
|
|
|
{
|
2019-04-24 23:50:34 +03:00
|
|
|
|
var page = new GroupingsPage(false, null, folder.Id ?? "none", null, folder.Name);
|
|
|
|
|
await Page.Navigation.PushAsync(page);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task SelectCollectionAsync(Core.Models.View.CollectionView collection)
|
|
|
|
|
{
|
|
|
|
|
var page = new GroupingsPage(false, null, null, collection.Id, collection.Name);
|
|
|
|
|
await Page.Navigation.PushAsync(page);
|
2019-04-19 23:45:16 +03:00
|
|
|
|
}
|
2019-03-29 19:52:57 +03:00
|
|
|
|
|
2019-04-24 23:50:34 +03:00
|
|
|
|
private async Task LoadDataAsync()
|
2019-04-19 23:45:16 +03:00
|
|
|
|
{
|
2019-04-25 06:28:41 +03:00
|
|
|
|
NoDataText = AppResources.NoItems;
|
2019-04-19 23:45:16 +03:00
|
|
|
|
_allCiphers = await _cipherService.GetAllDecryptedAsync();
|
2019-04-24 23:50:34 +03:00
|
|
|
|
if(MainPage)
|
2019-04-19 23:45:16 +03:00
|
|
|
|
{
|
2019-04-24 23:50:34 +03:00
|
|
|
|
if(ShowFolders)
|
|
|
|
|
{
|
|
|
|
|
Folders = await _folderService.GetAllDecryptedAsync();
|
|
|
|
|
NestedFolders = await _folderService.GetAllNestedAsync();
|
|
|
|
|
}
|
|
|
|
|
if(ShowCollections)
|
2019-04-19 23:45:16 +03:00
|
|
|
|
{
|
2019-04-24 23:50:34 +03:00
|
|
|
|
Collections = await _collectionService.GetAllDecryptedAsync();
|
|
|
|
|
NestedCollections = await _collectionService.GetAllNestedAsync(Collections);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach(var c in _allCiphers)
|
|
|
|
|
{
|
|
|
|
|
if(c.Favorite)
|
2019-04-19 23:45:16 +03:00
|
|
|
|
{
|
2019-04-24 23:50:34 +03:00
|
|
|
|
if(FavoriteCiphers == null)
|
|
|
|
|
{
|
|
|
|
|
FavoriteCiphers = new List<CipherView>();
|
|
|
|
|
}
|
|
|
|
|
FavoriteCiphers.Add(c);
|
|
|
|
|
}
|
|
|
|
|
if(c.FolderId == null)
|
|
|
|
|
{
|
|
|
|
|
if(NoFolderCiphers == null)
|
|
|
|
|
{
|
|
|
|
|
NoFolderCiphers = new List<CipherView>();
|
|
|
|
|
}
|
|
|
|
|
NoFolderCiphers.Add(c);
|
2019-04-19 23:45:16 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-24 23:50:34 +03:00
|
|
|
|
FavoriteCiphers = _allCiphers.Where(c => c.Favorite).ToList();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(Type != null)
|
2019-04-19 23:45:16 +03:00
|
|
|
|
{
|
2019-04-24 23:50:34 +03:00
|
|
|
|
Ciphers = _allCiphers.Where(c => c.Type == Type.Value).ToList();
|
|
|
|
|
}
|
|
|
|
|
else if(FolderId != null)
|
|
|
|
|
{
|
2019-04-25 06:28:41 +03:00
|
|
|
|
NoDataText = AppResources.NoItemsFolder;
|
2019-04-24 23:50:34 +03:00
|
|
|
|
FolderId = FolderId == "none" ? null : FolderId;
|
|
|
|
|
if(FolderId != null)
|
|
|
|
|
{
|
|
|
|
|
var folderNode = await _folderService.GetNestedAsync(FolderId);
|
|
|
|
|
if(folderNode?.Node != null)
|
|
|
|
|
{
|
|
|
|
|
PageTitle = folderNode.Node.Name;
|
|
|
|
|
NestedFolders = (folderNode.Children?.Count ?? 0) > 0 ? folderNode.Children : null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2019-04-19 23:45:16 +03:00
|
|
|
|
{
|
2019-04-24 23:50:34 +03:00
|
|
|
|
PageTitle = AppResources.FolderNone;
|
2019-04-19 23:45:16 +03:00
|
|
|
|
}
|
2019-04-24 23:50:34 +03:00
|
|
|
|
Ciphers = _allCiphers.Where(c => c.FolderId == FolderId).ToList();
|
|
|
|
|
}
|
|
|
|
|
else if(CollectionId != null)
|
|
|
|
|
{
|
2019-04-25 06:28:41 +03:00
|
|
|
|
ShowAddCipherButton = false;
|
|
|
|
|
NoDataText = AppResources.NoItemsCollection;
|
2019-04-24 23:50:34 +03:00
|
|
|
|
var collectionNode = await _collectionService.GetNestedAsync(CollectionId);
|
|
|
|
|
if(collectionNode?.Node != null)
|
|
|
|
|
{
|
|
|
|
|
PageTitle = collectionNode.Node.Name;
|
|
|
|
|
}
|
|
|
|
|
Ciphers = _allCiphers.Where(c => c.CollectionIds?.Contains(CollectionId) ?? false).ToList();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
PageTitle = AppResources.AllItems;
|
|
|
|
|
Ciphers = _allCiphers;
|
2019-04-19 23:45:16 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-29 06:52:33 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|