[KOE-24] Added per-account GAB resync and synchronous GAB resync.

This commit is contained in:
Patrick Simpson 2017-05-17 17:24:47 +02:00
parent c5fcf62e35
commit edf48b595d
7 changed files with 202 additions and 21 deletions

View File

@ -361,7 +361,7 @@ namespace Acacia.Features.GAB
{
try
{
if (IsGABContactsFolder(folder))
if (IsGABContactsFolder(folder, accounts))
{
Logger.Instance.Debug(this, "FullResync: Deleting contacts folder: {0}", folder.Name);
folder.Delete();
@ -378,16 +378,27 @@ namespace Acacia.Features.GAB
}
// Do the resync
int remaining = _gabsByDomainName.Count;
foreach (GABHandler gab in _gabsByDomainName.Values)
using (completion.Begin())
{
CompletionTracker partCompletion = new CompletionTracker(() => OnGabSyncFinished(gab));
// TODO: merge partCompletion into total completion
Logger.Instance.Debug(this, "FullResync: Starting resync: {0}", gab.DisplayName);
Tasks.Task(partCompletion, this, "FullResync", () =>
foreach (GABHandler gab in _gabsByDomainName.Values)
{
gab.FullResync(partCompletion);
});
// Check if the gab is appropriate for the accounts
if (accounts == null || accounts.Contains(gab.ActiveAccount))
{
completion.Begin();
CompletionTracker partCompletion = new CompletionTracker(() =>
{
OnGabSyncFinished(gab);
completion.End();
});
Logger.Instance.Debug(this, "FullResync: Starting resync: {0}", gab.DisplayName);
Tasks.Task(partCompletion, this, "FullResync", () =>
{
gab.FullResync(partCompletion);
});
}
}
}
}
finally
@ -499,9 +510,30 @@ namespace Acacia.Features.GAB
return GABInfo.Get(folder);
}
public static bool IsGABContactsFolder(IFolder folder)
/// <summary>
/// Checks if the folder is a relevant GAB contacts folder.
/// </summary>
/// <param name="folder">The folder.</param>
/// <param name="accounts">If specified, the folder is considered relevant only if it is the GAB for one of the specified
/// accounts. Otherwise, any GAB folder is considered relevant.</param>
public static bool IsGABContactsFolder(IFolder folder, ZPushAccount[] accounts)
{
return GetGABContactsFolderInfo(folder) != null;
// Check if this is a GAB folder at all
GABInfo gab = GetGABContactsFolderInfo(folder);
if (gab == null)
return false;
// If we don't have a list of accounts, it is what we're looking for
if (accounts == null)
return true;
// Check if the domain is specified
foreach(ZPushAccount account in accounts)
{
if (account.Account.DomainName == gab.Domain)
return true;
}
return false;
}
private void AccountDiscovered(ZPushAccount zpush)

View File

@ -41,6 +41,39 @@ namespace Acacia.Features.SyncState
public class FeatureSyncState : FeatureDisabled, FeatureWithRibbon
{
#region Sync configuration
[AcaciaOption("Sets the period to check synchronisation state if a sync is in progress.")]
public TimeSpan CheckPeriod
{
get { return GetOption(OPTION_CHECK_PERIOD); }
set { SetOption(OPTION_CHECK_PERIOD, value); }
}
private static readonly TimeSpanOption OPTION_CHECK_PERIOD = new TimeSpanOption("CheckPeriod", new TimeSpan(0, 5, 0));
[AcaciaOption("Sets the period to check synchronisation state if a sync is in progress and the dialog is open.")]
public TimeSpan CheckPeriodDialog
{
get { return GetOption(OPTION_CHECK_PERIOD_DIALOG); }
set { SetOption(OPTION_CHECK_PERIOD_DIALOG, value); }
}
private static readonly TimeSpanOption OPTION_CHECK_PERIOD_DIALOG = new TimeSpanOption("CheckPeriodDialog", new TimeSpan(0, 1, 0));
private TimeSpan DelayTime
{
get
{
if (_dialogOpen)
return CheckPeriodDialog;
else
return CheckPeriod;
}
}
private bool _dialogOpen;
#endregion
// TODO: this is largely about progress bars, separate that?
private class SyncStateData : DataProvider
{
@ -152,7 +185,7 @@ namespace Acacia.Features.SyncState
public override void Startup()
{
_state = new SyncStateData(this);
_button = RegisterButton(this, "Progress", true, ShowSyncState, ZPushBehaviour.Disable);
_button = RegisterButton(this, "Progress", true, ShowSyncState, ZPushBehaviour.None);
_button.DataProvider = _state;
// Add a sync task to start checking. If this finds it's not fully synchronised, it will check more often
Watcher.Sync.AddTask(this, Name, CheckSyncState);
@ -306,10 +339,14 @@ namespace Acacia.Features.SyncState
DeviceDetails details = deviceService.Execute(new GetDeviceDetailsRequest());
// Determine the totals
details.Calculate();
details?.Calculate();
// And store with the account
account.SetFeatureData(this, null, details);
// If syncing, check again soon.
if (details?.IsSyncing == true)
Util.Delayed(this, (int)DelayTime.TotalMilliseconds, () => CheckSyncState(account));
}
// Update the total for all accounts
@ -340,7 +377,15 @@ namespace Acacia.Features.SyncState
private void ShowSyncState()
{
new SyncStateDialog(this).ShowDialog();
_dialogOpen = true;
try
{
new SyncStateDialog(this).ShowDialog();
}
finally
{
_dialogOpen = false;
}
}
private class SyncStateImpl : SyncState
@ -362,7 +407,6 @@ namespace Acacia.Features.SyncState
_canResync[(int)ResyncOption.Signatures] = _featureSignatures != null;
_canResync[(int)ResyncOption.ServerData] = ThisAddIn.Instance.Watcher.Sync.Enabled;
_canResync[(int)ResyncOption.Full] = true;
Update();
}
public long Done
@ -399,14 +443,29 @@ namespace Acacia.Features.SyncState
{
if (!CanResync(option))
return true;
_canResync [(int)option] = false;
switch(option)
{
case ResyncOption.GAB:
// TODO: use completion tracker if not synching
_featureGAB.FullResync(null, _accounts);
return false;
if (IsSyncing)
{
// Already syncing, resync GAB asynchronously
_featureGAB.FullResync(null, _accounts);
// Cannot resync again until the dialog is reopened
_canResync[(int)ResyncOption.GAB] = false;
return false;
}
else
{
ProgressDialog.Execute("GABSync",
(ct, tracker) =>
{
_featureGAB.FullResync(tracker, _accounts);
return 0;
}
);
return true;
}
case ResyncOption.Signatures:
ProgressDialog.Execute("SignaturesSync",
(ct) =>

View File

@ -50,6 +50,15 @@ namespace Acacia.Features.SyncState
// Add the accounts
foreach (ZPushAccount account in ThisAddIn.Instance.Watcher.Accounts.GetAccounts())
comboAccounts.Items.Add(account);
// Add a timer to update the UI
Timer timer = new Timer();
timer.Interval = 2500;
timer.Tick += (o, args) =>
{
UpdateUI();
};
timer.Start();
}
private void ShowHint(object sender, KHintButton.HintEventArgs e)
@ -119,6 +128,8 @@ namespace Acacia.Features.SyncState
private void UpdateUI()
{
_syncState.Update();
// Set up the UI
foreach (ResyncOption option in Enum.GetValues(typeof(ResyncOption)))
{
@ -127,7 +138,7 @@ namespace Acacia.Features.SyncState
if (_syncState.IsSyncing)
{
textRemaining.Text = _syncState.Remaining.ToString();
textRemaining.Text = _syncState.Remaining.ToString() + " / " + _syncState.Total.ToString();
progress.Value = (int)(_syncState.Done * 100.0 / _syncState.Total);
}
else

View File

@ -168,6 +168,24 @@ namespace Acacia.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to The global address book is being synchronised..
/// </summary>
internal static string GABSync_Label {
get {
return ResourceManager.GetString("GABSync_Label", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Global Address Book.
/// </summary>
internal static string GABSync_Title {
get {
return ResourceManager.GetString("GABSync_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>

View File

@ -494,4 +494,10 @@
<data name="ServerSync_Title" xml:space="preserve">
<value>Server data</value>
</data>
<data name="GABSync_Label" xml:space="preserve">
<value>The global address book is being synchronised.</value>
</data>
<data name="GABSync_Title" xml:space="preserve">
<value>Global Address Book</value>
</data>
</root>

View File

@ -89,6 +89,61 @@ namespace Acacia.UI
return task.Result;
}
public static ResultType Execute<ResultType>(string resourcePrefix, Func<CancellationToken, CompletionTracker, ResultType> action)
{
// TODO: merge with above
Logger.Instance.Info(typeof(ProgressDialog), "Opening");
// Determine the UI context, creating a new one if required
if (SynchronizationContext.Current == null)
SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
var context = TaskScheduler.FromCurrentSynchronizationContext();
// Create the dialog, so it is available for the task
ProgressDialog dlg = new ProgressDialog();
// Set the strings
dlg.Text = StringUtil.GetResourceString(resourcePrefix + "_Title");
dlg.labelMessage.Text = StringUtil.GetResourceString(resourcePrefix + "_Label");
// Start the task
Exception caught = null;
Task<ResultType> task = null;
// And close the dialog when done
CompletionTracker tracker = new CompletionTracker(() =>
{
// This extra step is needed to go back into the thread context
task.ContinueWith(_ => { dlg._isComplete = true; dlg.DialogResult = DialogResult.OK; }, context);
});
task = Task.Factory.StartNew(
() =>
{
try
{
return action(dlg.cancel.Token, tracker);
}
catch (Exception e)
{
caught = e;
return default(ResultType);
}
},
dlg.cancel.Token);
dlg.task = task;
// Show the dialog
if (dlg.ShowDialog() != DialogResult.OK)
return default(ResultType);
// Rethrow any exception.
// The framework already handles this, but that causes breaks into the debugger
if (caught != null)
throw caught;
// Result the result
return task.Result;
}
private void ProgressDialog_FormClosing(object sender, FormClosingEventArgs e)
{
if (!_isComplete)

View File

@ -125,7 +125,7 @@ namespace Acacia.ZPush
private static bool IsCustomFolder(IFolder folder)
{
return Features.GAB.FeatureGAB.IsGABContactsFolder(folder);
return Features.GAB.FeatureGAB.IsGABContactsFolder(folder, null);
}
private static void HideAllFolders(IStore store)