1
0
mirror of https://github.com/bitwarden/mobile.git synced 2025-02-13 00:41:25 +01:00

Added busy indicator for sync operations. Optimized vault list loading. Customized search bar appearance on iOS.

This commit is contained in:
Kyle Spearrin 2016-07-11 23:55:16 -04:00
parent 635b09de9b
commit f2893e788d
8 changed files with 143 additions and 34 deletions

View File

@ -4,6 +4,7 @@ namespace Bit.App.Abstractions
{ {
public interface ISyncService public interface ISyncService
{ {
bool SyncInProgress { get; }
Task<bool> SyncAsync(string id); Task<bool> SyncAsync(string id);
Task<bool> SyncDeleteFolderAsync(string id); Task<bool> SyncDeleteFolderAsync(string id);
Task<bool> SyncDeleteSiteAsync(string id); Task<bool> SyncDeleteSiteAsync(string id);

View File

@ -290,6 +290,15 @@ namespace Bit.App
new Setter { Property = ListView.SeparatorColorProperty, Value = grayLighter } new Setter { Property = ListView.SeparatorColorProperty, Value = grayLighter }
} }
}); });
// Search Bar
Resources.Add(new Style(typeof(SearchBar))
{
Setters = {
new Setter { Property = SearchBar.CancelButtonColorProperty, Value = primaryColor }
}
});
} }
} }
} }

View File

@ -1,13 +1,30 @@
using System; using Bit.App.Abstractions;
using System;
using Xamarin.Forms; using Xamarin.Forms;
using XLabs.Ioc;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {
public class ExtendedContentPage : ContentPage public class ExtendedContentPage : ContentPage
{ {
private ISyncService _syncService;
public ExtendedContentPage() public ExtendedContentPage()
{ {
_syncService = Resolver.Resolve<ISyncService>();
BackgroundColor = Color.FromHex("efeff4"); BackgroundColor = Color.FromHex("efeff4");
IsBusy = _syncService.SyncInProgress;
MessagingCenter.Subscribe<Application, bool>(Application.Current, "SyncCompleted", (sender, success) =>
{
IsBusy = _syncService.SyncInProgress;
});
MessagingCenter.Subscribe<Application>(Application.Current, "SyncStarted", (sender) =>
{
IsBusy = _syncService.SyncInProgress;
});
} }
} }
} }

View File

@ -14,7 +14,6 @@ using PushNotification.Plugin.Abstractions;
using Plugin.Settings.Abstractions; using Plugin.Settings.Abstractions;
using Plugin.Connectivity.Abstractions; using Plugin.Connectivity.Abstractions;
using System.Collections.Generic; using System.Collections.Generic;
using Bit.App.Models;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
@ -25,6 +24,7 @@ namespace Bit.App.Pages
private readonly IUserDialogs _userDialogs; private readonly IUserDialogs _userDialogs;
private readonly IConnectivity _connectivity; private readonly IConnectivity _connectivity;
private readonly IClipboardService _clipboardService; private readonly IClipboardService _clipboardService;
private readonly ISyncService _syncService;
private readonly IPushNotification _pushNotification; private readonly IPushNotification _pushNotification;
private readonly ISettings _settings; private readonly ISettings _settings;
private readonly bool _favorites; private readonly bool _favorites;
@ -37,6 +37,7 @@ namespace Bit.App.Pages
_connectivity = Resolver.Resolve<IConnectivity>(); _connectivity = Resolver.Resolve<IConnectivity>();
_userDialogs = Resolver.Resolve<IUserDialogs>(); _userDialogs = Resolver.Resolve<IUserDialogs>();
_clipboardService = Resolver.Resolve<IClipboardService>(); _clipboardService = Resolver.Resolve<IClipboardService>();
_syncService = Resolver.Resolve<ISyncService>();
_pushNotification = Resolver.Resolve<IPushNotification>(); _pushNotification = Resolver.Resolve<IPushNotification>();
_settings = Resolver.Resolve<ISettings>(); _settings = Resolver.Resolve<ISettings>();
@ -52,9 +53,12 @@ namespace Bit.App.Pages
private void Init() private void Init()
{ {
MessagingCenter.Subscribe<Application>(Application.Current, "SyncCompleted", async (sender) => MessagingCenter.Subscribe<Application, bool>(Application.Current, "SyncCompleted", async (sender, success) =>
{
if(success)
{ {
await FetchAndLoadVaultAsync(); await FetchAndLoadVaultAsync();
}
}); });
if(!_favorites) if(!_favorites)
@ -80,8 +84,8 @@ namespace Bit.App.Pages
Search = new SearchBar Search = new SearchBar
{ {
Placeholder = "Search vault...", Placeholder = "Search vault",
BackgroundColor = Color.FromHex("efeff4") BackgroundColor = Color.FromHex("E8E8ED")
}; };
Search.TextChanged += SearchBar_TextChanged; Search.TextChanged += SearchBar_TextChanged;
Search.SearchButtonPressed += SearchBar_SearchButtonPressed; Search.SearchButtonPressed += SearchBar_SearchButtonPressed;
@ -96,7 +100,7 @@ namespace Bit.App.Pages
private void SearchBar_SearchButtonPressed(object sender, EventArgs e) private void SearchBar_SearchButtonPressed(object sender, EventArgs e)
{ {
FilterResults(((SearchBar)sender).Text); FilterResultsBackground(((SearchBar)sender).Text);
} }
private void SearchBar_TextChanged(object sender, TextChangedEventArgs e) private void SearchBar_TextChanged(object sender, TextChangedEventArgs e)
@ -108,19 +112,28 @@ namespace Bit.App.Pages
return; return;
} }
FilterResults(e.NewTextValue); FilterResultsBackground(e.NewTextValue);
} }
private void FilterResults(string searchFilter) private void FilterResultsBackground(string searchFilter)
{ {
Task.Run(async () => Task.Run(async () =>
{
if(!string.IsNullOrWhiteSpace(searchFilter))
{ {
await Task.Delay(300); await Task.Delay(300);
if(searchFilter != Search.Text) if(searchFilter != Search.Text)
{ {
return; return;
} }
}
FilterResults(searchFilter);
});
}
private void FilterResults(string searchFilter)
{
if(string.IsNullOrWhiteSpace(searchFilter)) if(string.IsNullOrWhiteSpace(searchFilter))
{ {
LoadFolders(Sites); LoadFolders(Sites);
@ -131,7 +144,6 @@ namespace Bit.App.Pages
var filteredSites = Sites.Where(s => s.Name.ToLower().Contains(searchFilter) || s.Username.ToLower().Contains(searchFilter)); var filteredSites = Sites.Where(s => s.Name.ToLower().Contains(searchFilter) || s.Username.ToLower().Contains(searchFilter));
LoadFolders(filteredSites); LoadFolders(filteredSites);
} }
});
} }
protected async override void OnAppearing() protected async override void OnAppearing()
@ -161,6 +173,11 @@ namespace Bit.App.Pages
private async Task FetchAndLoadVaultAsync() private async Task FetchAndLoadVaultAsync()
{ {
if(PresentationFolders.Count > 0 && _syncService.SyncInProgress)
{
return;
}
await Task.Run(async () => await Task.Run(async () =>
{ {
var foldersTask = _folderService.GetAllAsync(); var foldersTask = _folderService.GetAllAsync();
@ -173,7 +190,7 @@ namespace Bit.App.Pages
Folders = folders.Select(f => new VaultListPageModel.Folder(f)); Folders = folders.Select(f => new VaultListPageModel.Folder(f));
Sites = sites.Select(s => new VaultListPageModel.Site(s)); Sites = sites.Select(s => new VaultListPageModel.Site(s));
LoadFolders(Sites); FilterResults(Search.Text);
}); });
} }

View File

@ -13,6 +13,7 @@ namespace Bit.App.Services
public class SyncService : ISyncService public class SyncService : ISyncService
{ {
private const string LastSyncKey = "lastSync"; private const string LastSyncKey = "lastSync";
private int _syncsInProgress = 0;
private readonly ICipherApiRepository _cipherApiRepository; private readonly ICipherApiRepository _cipherApiRepository;
private readonly IFolderApiRepository _folderApiRepository; private readonly IFolderApiRepository _folderApiRepository;
@ -40,6 +41,8 @@ namespace Bit.App.Services
_settings = settings; _settings = settings;
} }
public bool SyncInProgress => _syncsInProgress > 0;
public async Task<bool> SyncAsync(string id) public async Task<bool> SyncAsync(string id)
{ {
if(!_authService.IsAuthenticated) if(!_authService.IsAuthenticated)
@ -47,9 +50,12 @@ namespace Bit.App.Services
return false; return false;
} }
SyncStarted();
var cipher = await _cipherApiRepository.GetByIdAsync(id); var cipher = await _cipherApiRepository.GetByIdAsync(id);
if(!cipher.Succeeded) if(!cipher.Succeeded)
{ {
SyncCompleted(false);
return false; return false;
} }
@ -80,10 +86,11 @@ namespace Bit.App.Services
} }
break; break;
default: default:
SyncCompleted(false);
return false; return false;
} }
BroadcastSyncCompleted(); SyncCompleted(true);
return true; return true;
} }
@ -94,8 +101,10 @@ namespace Bit.App.Services
return false; return false;
} }
SyncStarted();
await _folderRepository.DeleteAsync(id); await _folderRepository.DeleteAsync(id);
BroadcastSyncCompleted(); SyncCompleted(true);
return true; return true;
} }
@ -106,8 +115,10 @@ namespace Bit.App.Services
return false; return false;
} }
SyncStarted();
await _siteRepository.DeleteAsync(id); await _siteRepository.DeleteAsync(id);
BroadcastSyncCompleted(); SyncCompleted(true);
return true; return true;
} }
@ -118,10 +129,13 @@ namespace Bit.App.Services
return false; return false;
} }
SyncStarted();
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
var ciphers = await _cipherApiRepository.GetAsync(); var ciphers = await _cipherApiRepository.GetAsync();
if(!ciphers.Succeeded) if(!ciphers.Succeeded)
{ {
SyncCompleted(false);
return false; return false;
} }
@ -131,11 +145,12 @@ namespace Bit.App.Services
if(folderTask.Exception != null || siteTask.Exception != null) if(folderTask.Exception != null || siteTask.Exception != null)
{ {
SyncCompleted(false);
return false; return false;
} }
_settings.AddOrUpdateValue(LastSyncKey, now); _settings.AddOrUpdateValue(LastSyncKey, now);
BroadcastSyncCompleted(); SyncCompleted(true);
return true; return true;
} }
@ -153,9 +168,12 @@ namespace Bit.App.Services
return await FullSyncAsync(); return await FullSyncAsync();
} }
SyncStarted();
var ciphers = await _cipherApiRepository.GetByRevisionDateWithHistoryAsync(lastSync.Value); var ciphers = await _cipherApiRepository.GetByRevisionDateWithHistoryAsync(lastSync.Value);
if(!ciphers.Succeeded) if(!ciphers.Succeeded)
{ {
SyncCompleted(false);
return false; return false;
} }
@ -166,11 +184,12 @@ namespace Bit.App.Services
await Task.WhenAll(siteTask, folderTask, deleteTask); await Task.WhenAll(siteTask, folderTask, deleteTask);
if(folderTask.Exception != null || siteTask.Exception != null || deleteTask.Exception != null) if(folderTask.Exception != null || siteTask.Exception != null || deleteTask.Exception != null)
{ {
SyncCompleted(false);
return false; return false;
} }
_settings.AddOrUpdateValue(LastSyncKey, now); _settings.AddOrUpdateValue(LastSyncKey, now);
BroadcastSyncCompleted(); SyncCompleted(true);
return true; return true;
} }
@ -245,9 +264,16 @@ namespace Bit.App.Services
await Task.WhenAll(tasks); await Task.WhenAll(tasks);
} }
private void BroadcastSyncCompleted() private void SyncStarted()
{ {
MessagingCenter.Send(Application.Current, "SyncCompleted"); _syncsInProgress++;
MessagingCenter.Send(Application.Current, "SyncStarted");
}
private void SyncCompleted(bool successfully)
{
_syncsInProgress--;
MessagingCenter.Send(Application.Current, "SyncCompleted", successfully);
} }
} }
} }

View File

@ -50,8 +50,13 @@ namespace Bit.iOS
Resolver.Resolve<IFingerprint>(), Resolver.Resolve<IFingerprint>(),
Resolver.Resolve<ISettings>())); Resolver.Resolve<ISettings>()));
// Appearance stuff
var primaryColor = new UIColor(red: 0.24f, green: 0.55f, blue: 0.74f, alpha: 1.0f);
UINavigationBar.Appearance.ShadowImage = new UIImage(); UINavigationBar.Appearance.ShadowImage = new UIImage();
UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default); UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default);
UIBarButtonItem.AppearanceWhenContainedIn(new Type[] { typeof(UISearchBar) }).TintColor = primaryColor;
MessagingCenter.Subscribe<Xamarin.Forms.Application, ToolsExtensionPage>(Xamarin.Forms.Application.Current, "ShowAppExtension", (sender, page) => MessagingCenter.Subscribe<Xamarin.Forms.Application, ToolsExtensionPage>(Xamarin.Forms.Application.Current, "ShowAppExtension", (sender, page) =>
{ {

View File

@ -0,0 +1,33 @@
using Bit.App.Controls;
using Bit.iOS.Controls;
using System.ComponentModel;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(SearchBar), typeof(CustomSearchBarRenderer))]
namespace Bit.iOS.Controls
{
public class CustomSearchBarRenderer : SearchBarRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
{
base.OnElementChanged(e);
var view = e.NewElement;
if(view != null)
{
Control.SearchBarStyle = UISearchBarStyle.Minimal;
Control.BarStyle = UIBarStyle.BlackTranslucent;
Control.ShowsCancelButton = Control.IsFirstResponder;
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
var view = Element;
Control.ShowsCancelButton = Control.IsFirstResponder;
}
}
}

View File

@ -104,6 +104,7 @@
<Compile Include="Controls\CustomButtonRenderer.cs" /> <Compile Include="Controls\CustomButtonRenderer.cs" />
<Compile Include="Controls\CustomLabelRenderer.cs" /> <Compile Include="Controls\CustomLabelRenderer.cs" />
<Compile Include="Controls\ExtendedEditorRenderer.cs" /> <Compile Include="Controls\ExtendedEditorRenderer.cs" />
<Compile Include="Controls\CustomSearchBarRenderer.cs" />
<Compile Include="Controls\ExtendedSwitchCellRenderer.cs" /> <Compile Include="Controls\ExtendedSwitchCellRenderer.cs" />
<Compile Include="Controls\ListViewRenderer.cs" /> <Compile Include="Controls\ListViewRenderer.cs" />
<Compile Include="Controls\ExtendedViewCellRenderer.cs" /> <Compile Include="Controls\ExtendedViewCellRenderer.cs" />