1
0
mirror of https://github.com/bitwarden/mobile.git synced 2024-06-25 10:26:02 +02: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
{
bool SyncInProgress { get; }
Task<bool> SyncAsync(string id);
Task<bool> SyncDeleteFolderAsync(string id);
Task<bool> SyncDeleteSiteAsync(string id);

View File

@ -290,6 +290,15 @@ namespace Bit.App
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 XLabs.Ioc;
namespace Bit.App.Controls
{
public class ExtendedContentPage : ContentPage
{
private ISyncService _syncService;
public ExtendedContentPage()
{
_syncService = Resolver.Resolve<ISyncService>();
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.Connectivity.Abstractions;
using System.Collections.Generic;
using Bit.App.Models;
namespace Bit.App.Pages
{
@ -25,6 +24,7 @@ namespace Bit.App.Pages
private readonly IUserDialogs _userDialogs;
private readonly IConnectivity _connectivity;
private readonly IClipboardService _clipboardService;
private readonly ISyncService _syncService;
private readonly IPushNotification _pushNotification;
private readonly ISettings _settings;
private readonly bool _favorites;
@ -37,6 +37,7 @@ namespace Bit.App.Pages
_connectivity = Resolver.Resolve<IConnectivity>();
_userDialogs = Resolver.Resolve<IUserDialogs>();
_clipboardService = Resolver.Resolve<IClipboardService>();
_syncService = Resolver.Resolve<ISyncService>();
_pushNotification = Resolver.Resolve<IPushNotification>();
_settings = Resolver.Resolve<ISettings>();
@ -52,9 +53,12 @@ namespace Bit.App.Pages
private void Init()
{
MessagingCenter.Subscribe<Application>(Application.Current, "SyncCompleted", async (sender) =>
MessagingCenter.Subscribe<Application, bool>(Application.Current, "SyncCompleted", async (sender, success) =>
{
await FetchAndLoadVaultAsync();
if(success)
{
await FetchAndLoadVaultAsync();
}
});
if(!_favorites)
@ -80,8 +84,8 @@ namespace Bit.App.Pages
Search = new SearchBar
{
Placeholder = "Search vault...",
BackgroundColor = Color.FromHex("efeff4")
Placeholder = "Search vault",
BackgroundColor = Color.FromHex("E8E8ED")
};
Search.TextChanged += SearchBar_TextChanged;
Search.SearchButtonPressed += SearchBar_SearchButtonPressed;
@ -96,7 +100,7 @@ namespace Bit.App.Pages
private void SearchBar_SearchButtonPressed(object sender, EventArgs e)
{
FilterResults(((SearchBar)sender).Text);
FilterResultsBackground(((SearchBar)sender).Text);
}
private void SearchBar_TextChanged(object sender, TextChangedEventArgs e)
@ -108,30 +112,38 @@ namespace Bit.App.Pages
return;
}
FilterResults(e.NewTextValue);
FilterResultsBackground(e.NewTextValue);
}
private void FilterResultsBackground(string searchFilter)
{
Task.Run(async () =>
{
if(!string.IsNullOrWhiteSpace(searchFilter))
{
await Task.Delay(300);
if(searchFilter != Search.Text)
{
return;
}
}
FilterResults(searchFilter);
});
}
private void FilterResults(string searchFilter)
{
Task.Run(async () =>
if(string.IsNullOrWhiteSpace(searchFilter))
{
await Task.Delay(300);
if(searchFilter != Search.Text)
{
return;
}
if(string.IsNullOrWhiteSpace(searchFilter))
{
LoadFolders(Sites);
}
else
{
searchFilter = searchFilter.ToLower();
var filteredSites = Sites.Where(s => s.Name.ToLower().Contains(searchFilter) || s.Username.ToLower().Contains(searchFilter));
LoadFolders(filteredSites);
}
});
LoadFolders(Sites);
}
else
{
searchFilter = searchFilter.ToLower();
var filteredSites = Sites.Where(s => s.Name.ToLower().Contains(searchFilter) || s.Username.ToLower().Contains(searchFilter));
LoadFolders(filteredSites);
}
}
protected async override void OnAppearing()
@ -161,6 +173,11 @@ namespace Bit.App.Pages
private async Task FetchAndLoadVaultAsync()
{
if(PresentationFolders.Count > 0 && _syncService.SyncInProgress)
{
return;
}
await Task.Run(async () =>
{
var foldersTask = _folderService.GetAllAsync();
@ -173,7 +190,7 @@ namespace Bit.App.Pages
Folders = folders.Select(f => new VaultListPageModel.Folder(f));
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
{
private const string LastSyncKey = "lastSync";
private int _syncsInProgress = 0;
private readonly ICipherApiRepository _cipherApiRepository;
private readonly IFolderApiRepository _folderApiRepository;
@ -40,6 +41,8 @@ namespace Bit.App.Services
_settings = settings;
}
public bool SyncInProgress => _syncsInProgress > 0;
public async Task<bool> SyncAsync(string id)
{
if(!_authService.IsAuthenticated)
@ -47,9 +50,12 @@ namespace Bit.App.Services
return false;
}
SyncStarted();
var cipher = await _cipherApiRepository.GetByIdAsync(id);
if(!cipher.Succeeded)
{
SyncCompleted(false);
return false;
}
@ -80,10 +86,11 @@ namespace Bit.App.Services
}
break;
default:
SyncCompleted(false);
return false;
}
BroadcastSyncCompleted();
SyncCompleted(true);
return true;
}
@ -94,8 +101,10 @@ namespace Bit.App.Services
return false;
}
SyncStarted();
await _folderRepository.DeleteAsync(id);
BroadcastSyncCompleted();
SyncCompleted(true);
return true;
}
@ -106,8 +115,10 @@ namespace Bit.App.Services
return false;
}
SyncStarted();
await _siteRepository.DeleteAsync(id);
BroadcastSyncCompleted();
SyncCompleted(true);
return true;
}
@ -118,10 +129,13 @@ namespace Bit.App.Services
return false;
}
SyncStarted();
var now = DateTime.UtcNow;
var ciphers = await _cipherApiRepository.GetAsync();
if(!ciphers.Succeeded)
{
SyncCompleted(false);
return false;
}
@ -131,11 +145,12 @@ namespace Bit.App.Services
if(folderTask.Exception != null || siteTask.Exception != null)
{
SyncCompleted(false);
return false;
}
_settings.AddOrUpdateValue(LastSyncKey, now);
BroadcastSyncCompleted();
SyncCompleted(true);
return true;
}
@ -153,9 +168,12 @@ namespace Bit.App.Services
return await FullSyncAsync();
}
SyncStarted();
var ciphers = await _cipherApiRepository.GetByRevisionDateWithHistoryAsync(lastSync.Value);
if(!ciphers.Succeeded)
{
SyncCompleted(false);
return false;
}
@ -166,11 +184,12 @@ namespace Bit.App.Services
await Task.WhenAll(siteTask, folderTask, deleteTask);
if(folderTask.Exception != null || siteTask.Exception != null || deleteTask.Exception != null)
{
SyncCompleted(false);
return false;
}
_settings.AddOrUpdateValue(LastSyncKey, now);
BroadcastSyncCompleted();
SyncCompleted(true);
return true;
}
@ -245,9 +264,16 @@ namespace Bit.App.Services
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<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.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) =>
{

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\CustomLabelRenderer.cs" />
<Compile Include="Controls\ExtendedEditorRenderer.cs" />
<Compile Include="Controls\CustomSearchBarRenderer.cs" />
<Compile Include="Controls\ExtendedSwitchCellRenderer.cs" />
<Compile Include="Controls\ListViewRenderer.cs" />
<Compile Include="Controls\ExtendedViewCellRenderer.cs" />