diff --git a/src/App/App.csproj b/src/App/App.csproj index 679143e3b..d1cb1ead4 100644 --- a/src/App/App.csproj +++ b/src/App/App.csproj @@ -38,6 +38,9 @@ GeneratorPage.xaml + + CollectionsPage.xaml + SharePage.xaml diff --git a/src/App/Pages/Vault/AddEditPage.xaml.cs b/src/App/Pages/Vault/AddEditPage.xaml.cs index 6c51a4ec7..2b8075d37 100644 --- a/src/App/Pages/Vault/AddEditPage.xaml.cs +++ b/src/App/Pages/Vault/AddEditPage.xaml.cs @@ -124,11 +124,12 @@ namespace Bit.App.Pages } } - private void Collections_Clicked(object sender, System.EventArgs e) + private async void Collections_Clicked(object sender, System.EventArgs e) { if(DoOnce()) { - // TODO + var page = new CollectionsPage(_vm.CipherId); + await Navigation.PushModalAsync(new NavigationPage(page)); } } } diff --git a/src/App/Pages/Vault/CollectionsPage.xaml b/src/App/Pages/Vault/CollectionsPage.xaml new file mode 100644 index 000000000..6f8031af0 --- /dev/null +++ b/src/App/Pages/Vault/CollectionsPage.xaml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/App/Pages/Vault/CollectionsPage.xaml.cs b/src/App/Pages/Vault/CollectionsPage.xaml.cs new file mode 100644 index 000000000..13d08385f --- /dev/null +++ b/src/App/Pages/Vault/CollectionsPage.xaml.cs @@ -0,0 +1,37 @@ +using Xamarin.Forms; + +namespace Bit.App.Pages +{ + public partial class CollectionsPage : BaseContentPage + { + private CollectionsPageViewModel _vm; + + public CollectionsPage(string cipherId) + { + InitializeComponent(); + _vm = BindingContext as CollectionsPageViewModel; + _vm.Page = this; + _vm.CipherId = cipherId; + SetActivityIndicator(); + } + + protected override async void OnAppearing() + { + base.OnAppearing(); + await LoadOnAppearedAsync(_scrollView, true, () => _vm.LoadAsync()); + } + + protected override void OnDisappearing() + { + base.OnDisappearing(); + } + + private async void Save_Clicked(object sender, System.EventArgs e) + { + if(DoOnce()) + { + await _vm.SubmitAsync(); + } + } + } +} diff --git a/src/App/Pages/Vault/CollectionsPageViewModel.cs b/src/App/Pages/Vault/CollectionsPageViewModel.cs new file mode 100644 index 000000000..b854a6402 --- /dev/null +++ b/src/App/Pages/Vault/CollectionsPageViewModel.cs @@ -0,0 +1,87 @@ +using Bit.App.Abstractions; +using Bit.App.Resources; +using Bit.Core.Abstractions; +using Bit.Core.Exceptions; +using Bit.Core.Models.Domain; +using Bit.Core.Models.View; +using Bit.Core.Utilities; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Bit.App.Pages +{ + public class CollectionsPageViewModel : BaseViewModel + { + private readonly IDeviceActionService _deviceActionService; + private readonly ICipherService _cipherService; + private readonly ICollectionService _collectionService; + private readonly IPlatformUtilsService _platformUtilsService; + private CipherView _cipher; + private Cipher _cipherDomain; + private bool _hasCollections; + + public CollectionsPageViewModel() + { + _deviceActionService = ServiceContainer.Resolve("deviceActionService"); + _cipherService = ServiceContainer.Resolve("cipherService"); + _platformUtilsService = ServiceContainer.Resolve("platformUtilsService"); + _collectionService = ServiceContainer.Resolve("collectionService"); + Collections = new ExtendedObservableCollection(); + PageTitle = AppResources.Collections; + } + + public string CipherId { get; set; } + public ExtendedObservableCollection Collections { get; set; } + public bool HasCollections + { + get => _hasCollections; + set => SetProperty(ref _hasCollections, value); + } + + public async Task LoadAsync() + { + _cipherDomain = await _cipherService.GetAsync(CipherId); + var collectionIds = _cipherDomain.CollectionIds; + _cipher = await _cipherDomain.DecryptAsync(); + var allCollections = await _collectionService.GetAllDecryptedAsync(); + var collections = allCollections + .Where(c => !c.ReadOnly && c.OrganizationId == _cipher.OrganizationId) + .Select(c => new CollectionViewModel + { + Collection = c, + Checked = collectionIds.Contains(c.Id) + }).ToList(); + Collections.ResetWithRange(collections); + HasCollections = Collections.Any(); + } + + public async Task SubmitAsync() + { + if(!Collections.Any(c => c.Checked)) + { + await Page.DisplayAlert(AppResources.AnErrorHasOccurred, AppResources.SelectOneCollection, + AppResources.Ok); + return false; + } + + _cipherDomain.CollectionIds = new HashSet( + Collections.Where(c => c.Checked).Select(c => c.Collection.Id)); + try + { + await _deviceActionService.ShowLoadingAsync(AppResources.Saving); + await _cipherService.SaveCollectionsWithServerAsync(_cipherDomain); + await _deviceActionService.HideLoadingAsync(); + _platformUtilsService.ShowToast("success", null, AppResources.ItemUpdated); + await Page.Navigation.PopModalAsync(); + return true; + } + catch(ApiException e) + { + await _deviceActionService.HideLoadingAsync(); + await Page.DisplayAlert(AppResources.AnErrorHasOccurred, e.Error.GetSingleMessage(), AppResources.Ok); + } + return false; + } + } +} diff --git a/src/App/Pages/Vault/SharePage.xaml.cs b/src/App/Pages/Vault/SharePage.xaml.cs index ca29240e3..37b4be847 100644 --- a/src/App/Pages/Vault/SharePage.xaml.cs +++ b/src/App/Pages/Vault/SharePage.xaml.cs @@ -6,7 +6,7 @@ namespace Bit.App.Pages { private SharePageViewModel _vm; - public SharePage(string cipherId = null) + public SharePage(string cipherId) { InitializeComponent(); _vm = BindingContext as SharePageViewModel; diff --git a/src/App/Pages/Vault/ViewPage.xaml.cs b/src/App/Pages/Vault/ViewPage.xaml.cs index 9e63fe94e..92930ecf6 100644 --- a/src/App/Pages/Vault/ViewPage.xaml.cs +++ b/src/App/Pages/Vault/ViewPage.xaml.cs @@ -130,11 +130,12 @@ namespace Bit.App.Pages } } - private void Collections_Clicked(object sender, System.EventArgs e) + private async void Collections_Clicked(object sender, System.EventArgs e) { if(DoOnce()) { - // TODO + var page = new CollectionsPage(_vm.CipherId); + await Navigation.PushModalAsync(new NavigationPage(page)); } } }