diff --git a/src/App/App.csproj b/src/App/App.csproj index 66b694b9e..8db515103 100644 --- a/src/App/App.csproj +++ b/src/App/App.csproj @@ -76,12 +76,15 @@ + + + @@ -115,11 +118,11 @@ - + - + diff --git a/src/App/Models/Page/SettingsFolderPageModel.cs b/src/App/Models/Page/SettingsFolderPageModel.cs new file mode 100644 index 000000000..646070a9c --- /dev/null +++ b/src/App/Models/Page/SettingsFolderPageModel.cs @@ -0,0 +1,14 @@ +namespace Bit.App.Models.Page +{ + public class SettingsFolderPageModel + { + public SettingsFolderPageModel(Folder folder) + { + Id = folder.Id; + Name = folder.Name?.Decrypt(); + } + + public string Id { get; set; } + public string Name { get; set; } + } +} diff --git a/src/App/Pages/MainPage.cs b/src/App/Pages/MainPage.cs index ea1828761..0657c8fd8 100644 --- a/src/App/Pages/MainPage.cs +++ b/src/App/Pages/MainPage.cs @@ -13,7 +13,7 @@ namespace Bit.App.Pages TintColor = Color.FromHex("ffffff"); var settingsNavigation = new ExtendedNavigationPage(new SettingsPage()); - var vaultNavigation = new ExtendedNavigationPage(new VaultListPage()); + var vaultNavigation = new ExtendedNavigationPage(new VaultListSitesPage()); var syncNavigation = new ExtendedNavigationPage(new SyncPage()); vaultNavigation.Title = AppResources.MyVault; diff --git a/src/App/Pages/SettingsAddFolderPage.cs b/src/App/Pages/SettingsAddFolderPage.cs new file mode 100644 index 000000000..ae8dc00e1 --- /dev/null +++ b/src/App/Pages/SettingsAddFolderPage.cs @@ -0,0 +1,100 @@ +using System; +using Acr.UserDialogs; +using Bit.App.Abstractions; +using Bit.App.Controls; +using Bit.App.Models; +using Bit.App.Resources; +using Plugin.Connectivity.Abstractions; +using Xamarin.Forms; +using XLabs.Ioc; + +namespace Bit.App.Pages +{ + public class SettingsAddFolderPage : ContentPage + { + private readonly IFolderService _folderService; + private readonly IUserDialogs _userDialogs; + private readonly IConnectivity _connectivity; + + public SettingsAddFolderPage() + { + _folderService = Resolver.Resolve(); + _userDialogs = Resolver.Resolve(); + _connectivity = Resolver.Resolve(); + + Init(); + } + + private void Init() + { + var nameCell = new FormEntryCell(AppResources.Name); + + var mainTable = new ExtendedTableView + { + Intent = TableIntent.Settings, + EnableScrolling = false, + HasUnevenRows = true, + EnableSelection = false, + Root = new TableRoot + { + new TableSection() + { + nameCell + } + } + }; + + if(Device.OS == TargetPlatform.iOS) + { + mainTable.RowHeight = -1; + mainTable.EstimatedRowHeight = 70; + } + + var saveToolBarItem = new ToolbarItem(AppResources.Save, null, async () => + { + if(!_connectivity.IsConnected) + { + AlertNoConnection(); + return; + } + + if(string.IsNullOrWhiteSpace(nameCell.Entry.Text)) + { + await DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired, AppResources.Name), AppResources.Ok); + return; + } + + var folder = new Folder + { + Name = nameCell.Entry.Text.Encrypt() + }; + + var saveTask = _folderService.SaveAsync(folder); + _userDialogs.ShowLoading("Saving...", MaskType.Black); + await saveTask; + + _userDialogs.HideLoading(); + await Navigation.PopModalAsync(); + _userDialogs.SuccessToast(nameCell.Entry.Text, "New folder created."); + }, ToolbarItemOrder.Default, 0); + + Title = "Add Folder"; + Content = mainTable; + ToolbarItems.Add(saveToolBarItem); + if(Device.OS == TargetPlatform.iOS) + { + ToolbarItems.Add(new DismissModalToolBarItem(this, "Cancel")); + } + + if(!_connectivity.IsConnected) + { + AlertNoConnection(); + } + } + + private void AlertNoConnection() + { + DisplayAlert(AppResources.InternetConnectionRequiredTitle, AppResources.InternetConnectionRequiredMessage, AppResources.Ok); + } + } +} diff --git a/src/App/Pages/SettingsEditFolderPage.cs b/src/App/Pages/SettingsEditFolderPage.cs new file mode 100644 index 000000000..dda8d9030 --- /dev/null +++ b/src/App/Pages/SettingsEditFolderPage.cs @@ -0,0 +1,106 @@ +using System; +using Acr.UserDialogs; +using Bit.App.Abstractions; +using Bit.App.Controls; +using Bit.App.Resources; +using Plugin.Connectivity.Abstractions; +using Xamarin.Forms; +using XLabs.Ioc; + +namespace Bit.App.Pages +{ + public class SettingsEditFolderPage : ContentPage + { + private readonly string _folderId; + private readonly IFolderService _folderService; + private readonly IUserDialogs _userDialogs; + private readonly IConnectivity _connectivity; + + public SettingsEditFolderPage(string folderId) + { + _folderId = folderId; + _folderService = Resolver.Resolve(); + _userDialogs = Resolver.Resolve(); + _connectivity = Resolver.Resolve(); + + Init(); + } + + private void Init() + { + var folder = _folderService.GetByIdAsync(_folderId).GetAwaiter().GetResult(); + if(folder == null) + { + // TODO: handle error. navigate back? should never happen... + return; + } + + var nameCell = new FormEntryCell(AppResources.Name); + nameCell.Entry.Text = folder.Name.Decrypt(); + + var mainTable = new ExtendedTableView + { + Intent = TableIntent.Settings, + EnableScrolling = false, + HasUnevenRows = true, + EnableSelection = false, + Root = new TableRoot + { + new TableSection() + { + nameCell + } + } + }; + + if(Device.OS == TargetPlatform.iOS) + { + mainTable.RowHeight = -1; + mainTable.EstimatedRowHeight = 70; + } + + var saveToolBarItem = new ToolbarItem(AppResources.Save, null, async () => + { + if(!_connectivity.IsConnected) + { + AlertNoConnection(); + return; + } + + if(string.IsNullOrWhiteSpace(nameCell.Entry.Text)) + { + await DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired, AppResources.Name), AppResources.Ok); + return; + } + + folder.Name = nameCell.Entry.Text.Encrypt(); + + var saveTask = _folderService.SaveAsync(folder); + _userDialogs.ShowLoading("Saving...", MaskType.Black); + await saveTask; + + _userDialogs.HideLoading(); + await Navigation.PopModalAsync(); + _userDialogs.SuccessToast(nameCell.Entry.Text, "Folder updated."); + }, ToolbarItemOrder.Default, 0); + + Title = "Edit Folder"; + Content = mainTable; + ToolbarItems.Add(saveToolBarItem); + if(Device.OS == TargetPlatform.iOS) + { + ToolbarItems.Add(new DismissModalToolBarItem(this, "Cancel")); + } + + if(!_connectivity.IsConnected) + { + AlertNoConnection(); + } + } + + private void AlertNoConnection() + { + DisplayAlert(AppResources.InternetConnectionRequiredTitle, AppResources.InternetConnectionRequiredMessage, AppResources.Ok); + } + } +} diff --git a/src/App/Pages/SettingsListFoldersPage.cs b/src/App/Pages/SettingsListFoldersPage.cs new file mode 100644 index 000000000..63b5c671e --- /dev/null +++ b/src/App/Pages/SettingsListFoldersPage.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using Acr.UserDialogs; +using Bit.App.Abstractions; +using Bit.App.Controls; +using Bit.App.Models.Page; +using Bit.App.Resources; +using Xamarin.Forms; +using XLabs.Ioc; + +namespace Bit.App.Pages +{ + public class SettingsListFoldersPage : ContentPage + { + private readonly IFolderService _folderService; + private readonly IUserDialogs _userDialogs; + public SettingsListFoldersPage() + { + _folderService = Resolver.Resolve(); + _userDialogs = Resolver.Resolve(); + + Init(); + } + + public ObservableCollection Folders { get; private set; } = new ObservableCollection(); + + private void Init() + { + ToolbarItems.Add(new AddFolderToolBarItem(this)); + + var listView = new ListView + { + ItemsSource = Folders, + SeparatorColor = Color.FromHex("d2d6de") + }; + listView.ItemSelected += FolderSelected; + listView.ItemTemplate = new DataTemplate(() => new SettingsFolderListViewCell(this)); + + Title = "Folders"; + Content = listView; + } + + protected override void OnAppearing() + { + base.OnAppearing(); + LoadFoldersAsync().Wait(); + } + + private async Task LoadFoldersAsync() + { + Folders.Clear(); + + var folders = await _folderService.GetAllAsync(); + foreach(var folder in folders) + { + var f = new SettingsFolderPageModel(folder); + Folders.Add(f); + } + } + + private void FolderSelected(object sender, SelectedItemChangedEventArgs e) + { + var folder = e.SelectedItem as SettingsFolderPageModel; + var page = new ExtendedNavigationPage(new SettingsEditFolderPage(folder.Id)); + Navigation.PushModalAsync(page); + } + + private class AddFolderToolBarItem : ToolbarItem + { + private readonly SettingsListFoldersPage _page; + + public AddFolderToolBarItem(SettingsListFoldersPage page) + { + _page = page; + Text = AppResources.Add; + Icon = "ion-plus"; + Clicked += ClickedItem; + } + + private async void ClickedItem(object sender, EventArgs e) + { + var page = new ExtendedNavigationPage(new SettingsAddFolderPage()); + await _page.Navigation.PushModalAsync(page); + } + } + + private class SettingsFolderListViewCell : ExtendedTextCell + { + public SettingsFolderListViewCell(SettingsListFoldersPage page) + { + this.SetBinding(TextProperty, s => s.Name); + TextColor = Color.FromHex("333333"); + } + } + } +} diff --git a/src/App/Pages/SettingsPage.cs b/src/App/Pages/SettingsPage.cs index 1ef4b3a89..24f372163 100644 --- a/src/App/Pages/SettingsPage.cs +++ b/src/App/Pages/SettingsPage.cs @@ -4,59 +4,134 @@ using Bit.App.Resources; using Xamarin.Forms; using XLabs.Ioc; using Bit.App.Controls; +using Acr.UserDialogs; namespace Bit.App.Pages { public class SettingsPage : ContentPage { private readonly IAuthService _authService; + private readonly IUserDialogs _userDialogs; public SettingsPage() { _authService = Resolver.Resolve(); + _userDialogs = Resolver.Resolve(); Init(); } private void Init() { - var foldersCell = new ExtendedTextCell { Text = "Folders", ShowDisclousure = true }; + var touchIdCell = new SwitchCell + { + Text = "Use Touch ID" + }; + touchIdCell.Tapped += TouchIdCell_Tapped; + + var lockOnExitCell = new SwitchCell + { + Text = "Lock Immediately On Exit" + }; + lockOnExitCell.Tapped += LockOnExitCell_Tapped; + + var changeMasterPasswordCell = new ExtendedTextCell + { + Text = "Change Master Password" + }; + changeMasterPasswordCell.Tapped += ChangeMasterPasswordCell_Tapped; + + var foldersCell = new ExtendedTextCell + { + Text = "Folders", + ShowDisclousure = true + }; foldersCell.Tapped += FoldersCell_Tapped; + var lockCell = new ExtendedTextCell + { + Text = "Lock" + }; + lockCell.Tapped += LockCell_Tapped; + + var logOutCell = new ExtendedTextCell + { + Text = "Log Out" + }; + logOutCell.Tapped += LogOutCell_Tapped; + var table = new ExtendedTableView { - EnableScrolling = false, + EnableScrolling = true, Intent = TableIntent.Menu, Root = new TableRoot { + new TableSection("Security") + { + touchIdCell, + lockOnExitCell, + changeMasterPasswordCell + }, new TableSection("Manage Folders") { foldersCell + }, + new TableSection("Current Session") + { + lockCell, + logOutCell } } }; - var logoutButton = new Button + var scrollView = new ScrollView { - Text = AppResources.LogOut, - Command = new Command(() => - { - _authService.LogOut(); - Application.Current.MainPage = new LoginNavigationPage(); - }) + Content = table }; - var stackLayout = new StackLayout { }; - stackLayout.Children.Add(table); - stackLayout.Children.Add(logoutButton); - Title = AppResources.Settings; - Content = stackLayout; + Content = table; + } + + private void LockCell_Tapped(object sender, EventArgs e) + { + + } + + private async void LogOutCell_Tapped(object sender, EventArgs e) + { + if(!await _userDialogs.ConfirmAsync("Are you sure you want to log out?", null, AppResources.Yes, AppResources.Cancel)) + { + return; + } + + _authService.LogOut(); + Application.Current.MainPage = new LoginNavigationPage(); + } + + private void LockOnExitCell_Tapped(object sender, EventArgs e) + { + + } + + private async void ChangeMasterPasswordCell_Tapped(object sender, EventArgs e) + { + if(!await _userDialogs.ConfirmAsync("You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?", null, AppResources.Yes, AppResources.Cancel)) + { + return; + } + + Device.OpenUri(new Uri("https://vault.bitwarden.com")); + } + + private void TouchIdCell_Tapped(object sender, EventArgs e) + { + } private void FoldersCell_Tapped(object sender, EventArgs e) { - + Navigation.PushAsync(new SettingsListFoldersPage()); } } } diff --git a/src/App/Pages/VaultAddFolderPage.cs b/src/App/Pages/VaultAddFolderPage.cs deleted file mode 100644 index aeb2f898f..000000000 --- a/src/App/Pages/VaultAddFolderPage.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection.Emit; -using System.Text; -using System.Threading.Tasks; -using Acr.UserDialogs; -using Bit.App.Abstractions; -using Bit.App.Models; -using Bit.App.Resources; -using Plugin.Connectivity.Abstractions; -using Xamarin.Forms; -using XLabs.Ioc; - -namespace Bit.App.Pages -{ - public class VaultAddFolderPage : ContentPage - { - public VaultAddFolderPage() - { - var cryptoService = Resolver.Resolve(); - var folderService = Resolver.Resolve(); - var userDialogs = Resolver.Resolve(); - var connectivity = Resolver.Resolve(); - - var nameEntry = new Entry(); - - var stackLayout = new StackLayout(); - stackLayout.Children.Add(new Label { Text = AppResources.Name }); - stackLayout.Children.Add(nameEntry); - - var scrollView = new ScrollView - { - Content = stackLayout, - Orientation = ScrollOrientation.Vertical - }; - - var saveToolBarItem = new ToolbarItem(AppResources.Save, null, async () => - { - if(!connectivity.IsConnected) - { - AlertNoConnection(); - return; - } - - if(string.IsNullOrWhiteSpace(nameEntry.Text)) - { - await DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired, AppResources.Name), AppResources.Ok); - return; - } - - var folder = new Folder - { - Name = nameEntry.Text.Encrypt() - }; - - var saveTask = folderService.SaveAsync(folder); - userDialogs.ShowLoading("Saving...", MaskType.Black); - await saveTask; - - userDialogs.HideLoading(); - await Navigation.PopAsync(); - userDialogs.SuccessToast(nameEntry.Text, "New folder created."); - }, ToolbarItemOrder.Default, 0); - - Title = "Add Folder"; - Content = scrollView; - ToolbarItems.Add(saveToolBarItem); - - if(!connectivity.IsConnected) - { - AlertNoConnection(); - } - } - - public void AlertNoConnection() - { - DisplayAlert(AppResources.InternetConnectionRequiredTitle, AppResources.InternetConnectionRequiredMessage, AppResources.Ok); - } - } -} diff --git a/src/App/Pages/VaultAddSitePage.cs b/src/App/Pages/VaultAddSitePage.cs index a7f08efb2..d79c6527d 100644 --- a/src/App/Pages/VaultAddSitePage.cs +++ b/src/App/Pages/VaultAddSitePage.cs @@ -120,7 +120,7 @@ namespace Bit.App.Pages await saveTask; _userDialogs.HideLoading(); - await Navigation.PopAsync(); + await Navigation.PopModalAsync(); _userDialogs.SuccessToast(nameCell.Entry.Text, "New site created."); }, ToolbarItemOrder.Default, 0); @@ -142,15 +142,6 @@ namespace Bit.App.Pages { DisplayAlert(AppResources.InternetConnectionRequiredTitle, AppResources.InternetConnectionRequiredMessage, AppResources.Ok); } - - private class FormEntryStackLayout : StackLayout - { - public FormEntryStackLayout() - { - Padding = new Thickness(15, 15, 15, 0); - BackgroundColor = Color.White; - } - } } } diff --git a/src/App/Pages/VaultListPage.cs b/src/App/Pages/VaultListSitesPage.cs similarity index 96% rename from src/App/Pages/VaultListPage.cs rename to src/App/Pages/VaultListSitesPage.cs index f5873e0cb..3f37e4d9b 100644 --- a/src/App/Pages/VaultListPage.cs +++ b/src/App/Pages/VaultListSitesPage.cs @@ -12,14 +12,14 @@ using XLabs.Ioc; namespace Bit.App.Pages { - public class VaultListPage : ContentPage + public class VaultListSitesPage : ContentPage { private readonly IFolderService _folderService; private readonly ISiteService _siteService; private readonly IUserDialogs _userDialogs; private readonly IClipboardService _clipboardService; - public VaultListPage() + public VaultListSitesPage() { _folderService = Resolver.Resolve(); _siteService = Resolver.Resolve(); @@ -145,9 +145,9 @@ namespace Bit.App.Pages private class AddSiteToolBarItem : ToolbarItem { - private readonly VaultListPage _page; + private readonly VaultListSitesPage _page; - public AddSiteToolBarItem(VaultListPage page) + public AddSiteToolBarItem(VaultListSitesPage page) { _page = page; Text = AppResources.Add; @@ -164,7 +164,7 @@ namespace Bit.App.Pages private class VaultListViewCell : ExtendedTextCell { - public VaultListViewCell(VaultListPage page) + public VaultListViewCell(VaultListSitesPage page) { var moreAction = new MenuItem { Text = AppResources.More }; moreAction.SetBinding(MenuItem.CommandParameterProperty, new Binding(".")); @@ -181,13 +181,12 @@ namespace Bit.App.Pages TextColor = Color.FromHex("333333"); DetailColor = Color.FromHex("777777"); - BackgroundColor = Color.White; } } private class VaultListHeaderViewCell : ViewCell { - public VaultListHeaderViewCell(VaultListPage page) + public VaultListHeaderViewCell(VaultListSitesPage page) { var image = new Image {