Kaynağa Gözat

Added Progress window for posting

Kenric Nugteren 3 gün önce
ebeveyn
işleme
de4e47be75

+ 2 - 0
InABox.Core/Postable/IPoster.cs

@@ -17,6 +17,8 @@ namespace InABox.Core
         where TSettings : PosterSettings
     {
         TSettings Settings { get; set; }
+
+        IPosterDispatcher Dispatcher { get; set; }
     }
 
     /// <summary>

+ 17 - 0
InABox.Core/Postable/PosterEngine.cs

@@ -39,6 +39,8 @@ namespace InABox.Core
     public interface IPosterEngine<TPostable>
         where TPostable : Entity, IPostable, IRemotable, IPersistent, new()
     {
+        IPosterDispatcher Dispatcher { get; set; }
+
         IPostResult<TPostable>? Process(IDataModel<TPostable> model);
     }
 
@@ -68,6 +70,17 @@ namespace InABox.Core
             Poster = CreatePoster();
         }
 
+        private IPosterDispatcher? _dispatcher;
+        public IPosterDispatcher Dispatcher
+        {
+            get => _dispatcher ?? IPosterDispatcher.Default;
+            set
+            {
+                Poster.Dispatcher = value;
+                _dispatcher = value;
+            }
+        }
+
         private static readonly Type? PosterType = PosterUtils.GetPoster(typeof(TPoster));
 
         protected virtual TPoster CreatePoster()
@@ -84,6 +97,7 @@ namespace InABox.Core
         }
 
         private TSettings? _settings;
+
         protected TSettings GetSettings()
         {
             _settings ??= PosterUtils.LoadPosterSettings<TPostable, TSettings>();
@@ -135,6 +149,7 @@ namespace InABox.Core
 
         public IPostResult<TPostable>? Process(IDataModel<TPostable> model)
         {
+            Dispatcher.Report("Loading data");
             if (!BeforePost(model))
             {
                 return null;
@@ -160,8 +175,10 @@ namespace InABox.Core
 
             try
             {
+                Dispatcher.Report("Processing data");
                 var result = DoProcess(model);
                 AfterPost(model, result);
+                Dispatcher.Report("Saving items");
 
                 Client.Save(result.PostedEntities, "Posted by user.");
 

+ 49 - 4
InABox.Core/Postable/PosterUtils.cs

@@ -6,9 +6,43 @@ using System.Linq;
 using System.Reflection;
 using System.Runtime;
 using System.Text;
+using System.Threading.Tasks;
 
 namespace InABox.Core
 {
+    public interface IPosterDispatcher
+    {
+        public static IPosterDispatcher Default { get; } = new DefaultDispatcher();
+
+        Task ExecuteAsync(Action action);
+
+        Task<T> ExecuteAsync<T>(Func<T> func);
+
+        void Execute(Action action) => ExecuteAsync(action).Wait();
+
+        T Execute<T>(Func<T> func) => ExecuteAsync(func).Result;
+
+        void Report(string report);
+
+        private class DefaultDispatcher : IPosterDispatcher
+        {
+            public Task ExecuteAsync(Action action)
+            {
+                action();
+                return Task.CompletedTask;
+            }
+
+            public Task<T> ExecuteAsync<T>(Func<T> func)
+            {
+                return Task.FromResult(func());
+            }
+
+            public void Report(string report)
+            {
+            }
+        }
+    }
+
     public static class PosterUtils
     {
 
@@ -237,8 +271,8 @@ namespace InABox.Core
         public static IPosterEngine<T> CreateEngine<T>()
             where T : Entity, IPostable, IRemotable, IPersistent, new()
         {
-            var engine = GetEngine<T>();
-            return (Activator.CreateInstance(engine) as IPosterEngine<T>)!;
+            var engineType = GetEngine<T>();
+            return (Activator.CreateInstance(engineType) as IPosterEngine<T>)!;
         }
 
         #endregion
@@ -305,10 +339,21 @@ namespace InABox.Core
         /// <exception cref="PostCancelledException">If the post has been cancelled by the user.</exception>
         /// <returns><see langword="null"/> if post was unsuccessful.</returns>
         /// 
-        public static IPostResult<T>? Process<T>(IDataModel<T> model)
+        public static IPostResult<T>? Process<T>(IDataModel<T> model, Action<IPosterEngine<T>>? beforeProcess, Action<IPosterEngine<T>>? afterProcess)
             where T : Entity, IPostable, IRemotable, IPersistent, new()
         {
-            return CreateEngine<T>().Process(model);
+            var engine = CreateEngine<T>();
+            beforeProcess?.Invoke(engine);
+            IPostResult<T>? result;
+            try
+            {
+                result = engine.Process(model);
+            }
+            finally
+            {
+                afterProcess?.Invoke(engine);
+            }
+            return result;
         }
 
         /// <summary>

+ 18 - 19
InABox.Poster.CSV/CSVPosterEngine.cs

@@ -78,28 +78,27 @@ public class CSVPosterEngine<TPostable> : PosterEngine<TPostable, ICSVPoster<TPo
             results = Poster.Process(model);
         }
 
-        var dlg = new SaveFileDialog()
+        var filename = Dispatcher.Execute(() =>
         {
-            FileName = settings.DefaultOutputFile,
-            Filter = "CSV Files (*.csv)|*.csv"
-        };
+            var dlg = new SaveFileDialog()
+            {
+                FileName = settings.DefaultOutputFile,
+                Filter = "CSV Files (*.csv)|*.csv"
+            };
+            return dlg.ShowDialog() == true ? dlg.FileName : null;
+        }) ?? throw new PostCancelledException();
 
-        if(dlg.ShowDialog() == true)
-        {
-            using var writer = new StreamWriter(dlg.FileName);
-            using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
-            csv.Context.RegisterClassMap(results.ClassMap.ClassMap);
+        Dispatcher.Report("Saving CSV file");
 
-            var method = typeof(CsvWriter).GetMethods()
-                .Where(x => x.Name == nameof(CsvWriter.WriteRecords) && x.GetGenericArguments().Length == 1).First()
-                .MakeGenericMethod(results.Type);
-            method.Invoke(csv, new object?[] { results.Records });
-            return results;
-        }
-        else
-        {
-            throw new PostCancelledException();
-        }
+        using var writer = new StreamWriter(filename);
+        using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
+        csv.Context.RegisterClassMap(results.ClassMap.ClassMap);
+
+        var method = typeof(CsvWriter).GetMethods()
+            .Where(x => x.Name == nameof(CsvWriter.WriteRecords) && x.GetGenericArguments().Length == 1).First()
+            .MakeGenericMethod(results.Type);
+        method.Invoke(csv, new object?[] { results.Records });
+        return results;
     }
 
     public override void AfterPost(IDataModel<TPostable> model, IPostResult<TPostable> result)

+ 7 - 6
InABox.Poster.MYOB/MYOBPosterEngine.cs

@@ -104,7 +104,7 @@ public static partial class MYOBPosterEngine
         return _connectionData;
     }
 
-    public static MYOBConnectionData GetConnectionData()
+    public static MYOBConnectionData GetConnectionData(IPosterDispatcher dispatcher)
     {
         if(_connectionData is MYOBConnectionData data)
         {
@@ -114,7 +114,8 @@ public static partial class MYOBPosterEngine
         var configuration = new ApiConfiguration(DEV_KEY, SECRET_KEY, REDIRECT_URL);
         var authService = new OAuthService(configuration);
 
-        if(!GetAuthorisationCode(configuration, out var code))
+        string? code = null;
+        if(!dispatcher.Execute(() => GetAuthorisationCode(configuration, out code)))
         {
             throw new PostCancelledException();
         }
@@ -167,7 +168,7 @@ public abstract class MYOBPosterEngine<TPostable, TPoster, TSettings> :
 
     protected void LoadConnectionData()
     {
-        var data = MYOBPosterEngine.GetConnectionData();
+        var data = MYOBPosterEngine.GetConnectionData(Dispatcher);
 
         var globalSettings = GetGlobalSettings();
         if(data.CompanyFile is null || data.CompanyFile.Id != globalSettings.CompanyFile.ID)
@@ -175,7 +176,7 @@ public abstract class MYOBPosterEngine<TPostable, TPoster, TSettings> :
             CompanyFile? file;
             if(globalSettings.CompanyFile.ID == Guid.Empty)
             {
-                file = MYOBCompanyFileSelectionDialog.SelectCompanyFile();
+                file = MYOBCompanyFileSelectionDialog.SelectCompanyFile(Dispatcher);
                 if(file is null)
                 {
                     throw new PostCancelledException();
@@ -198,7 +199,7 @@ public abstract class MYOBPosterEngine<TPostable, TPoster, TSettings> :
                     Password = globalSettings.CompanyFilePassword,
                     NoCredentials = globalSettings.NoCredentials
                 };
-                if (DynamicGridUtils.EditObject(credentials, customiseGrid: grid =>
+                if (Dispatcher.Execute(() => DynamicGridUtils.EditObject(credentials, customiseGrid: grid =>
                 {
                     grid.OnValidate += (grid, items, errors) =>
                     {
@@ -210,7 +211,7 @@ public abstract class MYOBPosterEngine<TPostable, TPoster, TSettings> :
                             errors.Add("[UserID] cannot be blank");
                         }
                     };
-                }))
+                })))
                 {
                     globalSettings.NoCredentials = credentials.NoCredentials;
                     globalSettings.CompanyFileUserID = credentials.UserID;

+ 1 - 1
InABox.Poster.MYOB/UI/MYOBCompanyFileEditor.cs

@@ -89,7 +89,7 @@ public class MYOBCompanyFileEditorControl : DynamicEnclosedEditorControl<MYOBCom
     {
         if (e.OriginalSource != Select) return;
 
-        var file = MYOBCompanyFileSelectionDialog.SelectCompanyFile();
+        var file = MYOBCompanyFileSelectionDialog.SelectCompanyFile(IPosterDispatcher.Default);
         if(file is not null)
         {
             Value.ID = file.Id;

+ 4 - 3
InABox.Poster.MYOB/UI/MYOBCompanyFileSelectionDialog.xaml.cs

@@ -1,4 +1,5 @@
-using MYOB.AccountRight.SDK.Contracts;
+using InABox.Core;
+using MYOB.AccountRight.SDK.Contracts;
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
@@ -85,9 +86,9 @@ public partial class MYOBCompanyFileSelectionDialog : Window, INotifyPropertyCha
         Close();
     }
 
-    public static CompanyFile? SelectCompanyFile()
+    public static CompanyFile? SelectCompanyFile(IPosterDispatcher dispatcher)
     {
-        var data = MYOBPosterEngine.GetConnectionData();
+        var data = MYOBPosterEngine.GetConnectionData(dispatcher);
         var window = new MYOBCompanyFileSelectionDialog();
 
         var files = data.CompanyFileService.GetRange();

+ 4 - 3
InABox.Poster.Xero/XeroPosterEngine.cs

@@ -40,7 +40,7 @@ public static partial class XeroPosterEngine
         return _connectionData;
     }
 
-    public static async Task<XeroConnectionData> GetConnectionData()
+    public static async Task<XeroConnectionData> GetConnectionData(IPosterDispatcher dispatcher)
     {
         if(_connectionData is XeroConnectionData data)
         {
@@ -69,7 +69,8 @@ public static partial class XeroPosterEngine
         string codeVerifier = new string(randomChars);
 
         var link = client.BuildLoginUriPkce(codeVerifier);
-        if (!GetCode(link, state, out var code))
+        string? code = null;
+        if (!await dispatcher.ExecuteAsync(() => GetCode(link, state, out code)))
         {
             throw new PostCancelledException();
         }
@@ -184,7 +185,7 @@ public class XeroPosterEngine<TPostable, TSettings> : BasePosterEngine<TPostable
 
     protected override IPostResult<TPostable> DoProcess(IDataModel<TPostable> model)
     {
-        Poster.ConnectionData = XeroPosterEngine.GetConnectionData().Result;
+        Poster.ConnectionData = XeroPosterEngine.GetConnectionData(Dispatcher).Result;
         return Poster.Process(model);
     }