diff --git a/src/Unlimotion.Desktop/Program.cs b/src/Unlimotion.Desktop/Program.cs index 5e020f7..4e993f6 100644 --- a/src/Unlimotion.Desktop/Program.cs +++ b/src/Unlimotion.Desktop/Program.cs @@ -88,6 +88,7 @@ public static void Main(string[] args) var gitSection = configuration.GetSection("Git"); gitSection.GetSection(nameof(GitSettings.BackupEnabled)).Set(false); + gitSection.GetSection(nameof(GitSettings.ShowStatusToasts)).Set(gitSettings.ShowStatusToasts); gitSection.GetSection(nameof(GitSettings.UserName)).Set(gitSettings.UserName); gitSection.GetSection(nameof(GitSettings.Password)).Set(gitSettings.Password); @@ -102,23 +103,27 @@ public static void Main(string[] args) gitSection.GetSection(nameof(GitSettings.CommitterEmail)).Set(gitSettings.CommitterEmail); } - var pullJob = JobBuilder.Create() - .WithIdentity("GitPullJob", "GitPullJob") - .Build(); - var pushJob = JobBuilder.Create() - .WithIdentity("GitPushJob", "GitPushJob") - .Build(); - - var pullTrigger = GenerateTriggerBySecondsInterval("PullTrigger", "GitPullJob", - gitSettings.PullIntervalSeconds); - var pushTrigger = GenerateTriggerBySecondsInterval("PushTrigger", "GitPushJob", - gitSettings.PushIntervalSeconds); + var taskRepository = Locator.Current.GetService(); + taskRepository.Initiated += (sender, eventArgs) => + { + var pullJob = JobBuilder.Create() + .WithIdentity("GitPullJob", "Git") + .Build(); + var pushJob = JobBuilder.Create() + .WithIdentity("GitPushJob", "Git") + .Build(); + + var pullTrigger = GenerateTriggerBySecondsInterval("PullTrigger", "GitPullJob", + gitSettings.PullIntervalSeconds); + var pushTrigger = GenerateTriggerBySecondsInterval("PushTrigger", "GitPushJob", + gitSettings.PushIntervalSeconds); - scheduler.ScheduleJob(pullJob, pullTrigger); - scheduler.ScheduleJob(pushJob, pushTrigger); + scheduler.ScheduleJob(pullJob, pullTrigger); + scheduler.ScheduleJob(pushJob, pushTrigger); - if (gitSettings.BackupEnabled) - scheduler.Start(); + if (gitSettings.BackupEnabled) + scheduler.Start(); + }; BuildAvaloniaApp() .StartWithClassicDesktopLifetime(args); @@ -141,7 +146,6 @@ private static ITrigger GenerateTriggerBySecondsInterval(string name, string gro { return TriggerBuilder.Create() .WithIdentity(name, group) - .StartNow() .WithSimpleSchedule(x => x .WithIntervalInSeconds(seconds) .RepeatForever()) diff --git a/src/Unlimotion.ViewModel/FileDbWatcher.cs b/src/Unlimotion.ViewModel/FileDbWatcher.cs index d1da54f..1ed986a 100644 --- a/src/Unlimotion.ViewModel/FileDbWatcher.cs +++ b/src/Unlimotion.ViewModel/FileDbWatcher.cs @@ -58,6 +58,9 @@ public void AddIgnoredTask(string taskId) private void OnError(object sender, ErrorEventArgs e) { Debug.WriteLine("Error in FileWatcher"); + var notify = Locator.Current.GetService(); + notify?.ErrorToast(e.GetException().Message); + } private FileSystemEventHandler CreateThrottledEventHandler( diff --git a/src/Unlimotion.ViewModel/INotificationManagerWrapper.cs b/src/Unlimotion.ViewModel/INotificationManagerWrapper.cs index ad07ff3..33613d3 100644 --- a/src/Unlimotion.ViewModel/INotificationManagerWrapper.cs +++ b/src/Unlimotion.ViewModel/INotificationManagerWrapper.cs @@ -5,4 +5,8 @@ namespace Unlimotion.ViewModel; public interface INotificationManagerWrapper { void Ask(string header, string message, Action yesAction, Action noAction = null); + + void ErrorToast(string message); + + void SuccessToast(string message); } \ No newline at end of file diff --git a/src/Unlimotion.ViewModel/ITaskRepository.cs b/src/Unlimotion.ViewModel/ITaskRepository.cs index 6da0d9e..a0621d5 100644 --- a/src/Unlimotion.ViewModel/ITaskRepository.cs +++ b/src/Unlimotion.ViewModel/ITaskRepository.cs @@ -8,6 +8,7 @@ public interface ITaskRepository { SourceCache Tasks { get; } void Init(); + event EventHandler Initiated; Task Remove(string itemId, bool deleteFile); Task Save(TaskItem item); Task Load(string itemId); diff --git a/src/Unlimotion.ViewModel/MainWindowViewModel.cs b/src/Unlimotion.ViewModel/MainWindowViewModel.cs index 822de10..2d13712 100644 --- a/src/Unlimotion.ViewModel/MainWindowViewModel.cs +++ b/src/Unlimotion.ViewModel/MainWindowViewModel.cs @@ -829,6 +829,7 @@ public TaskWrapperViewModel FindTaskWrapperViewModel(TaskItemViewModel taskItemV public DateFilter LastCreatedDateFilter { get; set; } = new(); public static ReadOnlyObservableCollection DateFilterDefinitions { get; set; } = DateFilterDefinition.GetDefinitions(); + public object ToastNotificationManager { get; set; } } [AddINotifyPropertyChangedInterface] diff --git a/src/Unlimotion.ViewModel/SettingsViewModel.cs b/src/Unlimotion.ViewModel/SettingsViewModel.cs index e2ff4b9..d576438 100644 --- a/src/Unlimotion.ViewModel/SettingsViewModel.cs +++ b/src/Unlimotion.ViewModel/SettingsViewModel.cs @@ -56,6 +56,12 @@ public bool GitBackupEnabled get => _gitSettings.GetSection(nameof(GitSettings.BackupEnabled)).Get(); set => _gitSettings.GetSection(nameof(GitSettings.BackupEnabled)).Set(value); } + + public bool GitShowStatusToasts + { + get => _gitSettings.GetSection(nameof(GitSettings.ShowStatusToasts)).Get(); + set => _gitSettings.GetSection(nameof(GitSettings.ShowStatusToasts)).Set(value); + } public string GitUserName { diff --git a/src/Unlimotion.ViewModel/TaskRepository.cs b/src/Unlimotion.ViewModel/TaskRepository.cs index 160b1bc..8f55146 100644 --- a/src/Unlimotion.ViewModel/TaskRepository.cs +++ b/src/Unlimotion.ViewModel/TaskRepository.cs @@ -14,6 +14,8 @@ public class TaskRepository : ITaskRepository { private readonly ITaskStorage taskStorage; private readonly IDatabaseWatcher? dbWatcher; + public event EventHandler Initiated; + private Dictionary> blockedById { get; set; } private IObservable> rootFilter; public SourceCache Tasks { get; private set; } @@ -99,6 +101,8 @@ private void Init(IEnumerable items) taskStorage.Updating += TaskStorageOnUpdating; dbWatcher.OnUpdated += DbWatcherOnUpdated; } + + OnInited(); } private void TaskStorageOnUpdating(object sender, TaskStorageUpdateEventArgs e) @@ -184,5 +188,10 @@ public TaskItemViewModel Clone(TaskItem clone, params TaskItemViewModel[] destin this.Tasks.AddOrUpdate(task); return task; } + + protected virtual void OnInited() + { + Initiated?.Invoke(this, EventArgs.Empty); + } } } diff --git a/src/Unlimotion.ViewModel/TaskStorageSettings.cs b/src/Unlimotion.ViewModel/TaskStorageSettings.cs index 73a7c9b..f44b4a6 100644 --- a/src/Unlimotion.ViewModel/TaskStorageSettings.cs +++ b/src/Unlimotion.ViewModel/TaskStorageSettings.cs @@ -17,6 +17,7 @@ public class TaskStorageSettings public class GitSettings { public bool BackupEnabled { get; set; } = false; + public bool ShowStatusToasts { get; set; } = true; public string UserName { get; set; } = "YourEmail"; public string Password { get; set; } = "YourToken"; diff --git a/src/Unlimotion/App.axaml b/src/Unlimotion/App.axaml index cc01739..a942495 100644 --- a/src/Unlimotion/App.axaml +++ b/src/Unlimotion/App.axaml @@ -67,5 +67,6 @@ - + + diff --git a/src/Unlimotion/App.axaml.cs b/src/Unlimotion/App.axaml.cs index fd57496..51aac8d 100644 --- a/src/Unlimotion/App.axaml.cs +++ b/src/Unlimotion/App.axaml.cs @@ -5,10 +5,12 @@ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; +using Avalonia.Notification; #if LIVE using Live.Avalonia; #endif using ReactiveUI; +using Splat; using Unlimotion.ViewModel; using Unlimotion.Views; @@ -47,7 +49,7 @@ public override void OnFrameworkInitializationCompleted() { desktop.MainWindow = new MainWindow { - DataContext = new MainWindowViewModel(), + DataContext = GetMainWindowViewModel(), }; } @@ -59,7 +61,17 @@ public override void OnFrameworkInitializationCompleted() { singleViewPlatform.MainView = new MainControl { - DataContext = new MainWindowViewModel(), + DataContext = GetMainWindowViewModel(), + }; + } + + MainWindowViewModel GetMainWindowViewModel() + { + var notificationMessageManager = new NotificationMessageManager(); + Locator.CurrentMutable.RegisterConstant(notificationMessageManager); + return new MainWindowViewModel + { + ToastNotificationManager = notificationMessageManager }; } diff --git a/src/Unlimotion/NotificationManagerWrapper.cs b/src/Unlimotion/NotificationManagerWrapper.cs index a0533b3..e212736 100644 --- a/src/Unlimotion/NotificationManagerWrapper.cs +++ b/src/Unlimotion/NotificationManagerWrapper.cs @@ -1,7 +1,10 @@ using DialogHostAvalonia; using System; using System.Windows.Input; +using Avalonia.Notification; +using Avalonia.Threading; using ReactiveUI; +using Splat; namespace Unlimotion; @@ -20,6 +23,35 @@ public void Ask(string header, string message, Action yesAction, Action noAction var id = DialogHost.Show(askViewModel); askViewModel.CloseAction = () => DialogHost.GetDialogSession("Ask")?.Close(false); } + + + public void ErrorToast(string message) + { + var notify = Locator.Current.GetService(); + Dispatcher.UIThread.InvokeAsync(() => + { + notify?.CreateMessage() + .Background("#DC483D") + .HasMessage(message) + .Dismiss().WithDelay(TimeSpan.FromSeconds(7)) + .WithCloseButton() + .Queue(); + }); + } + + public void SuccessToast(string message) + { + var notify = Locator.Current.GetService(); + Dispatcher.UIThread.InvokeAsync(() => + { + notify?.CreateMessage() + .Background("#008800") + .HasMessage(message) + .Dismiss().WithDelay(TimeSpan.FromSeconds(7)) + .WithCloseButton() + .Queue(); + }); + } } public class AskViewModel diff --git a/src/Unlimotion/Services/BackupViaGitService.cs b/src/Unlimotion/Services/BackupViaGitService.cs index fa81c77..cf871e1 100644 --- a/src/Unlimotion/Services/BackupViaGitService.cs +++ b/src/Unlimotion/Services/BackupViaGitService.cs @@ -33,7 +33,10 @@ public List Refs() } } } - catch (Exception ex) { } + catch (Exception ex) + { + ShowUiError(ex.Message); + } return result; } @@ -52,7 +55,10 @@ public List Remotes() result.Add(remote.Name); } } - catch (Exception ex) { } + catch (Exception ex) + { + ShowUiError(ex.Message); + } return result; } @@ -67,6 +73,7 @@ public void Push(string msg) using var repo = new Repository(GetRepositoryPath(settings.repositoryPath)); + ShowUiMessage("Start Git Push"); if (repo.RetrieveStatus().IsDirty) { Commands.Checkout(repo, settings.git.PushRefSpec); @@ -76,6 +83,8 @@ public void Push(string msg) var committer = new Signature(settings.git.CommitterName, settings.git.CommitterEmail, DateTime.Now); repo.Commit(msg, committer, committer); + + ShowUiMessage("Commit Created"); } var options = new PushOptions @@ -96,12 +105,13 @@ public void Push(string msg) try { repo.Network.Push(repo.Network.Remotes[settings.git.RemoteName], settings.git.PushRefSpec, options); + ShowUiMessage("Push Successful"); } catch (Exception e) { var errorMessage = $"Can't push the remote repository, because {e.Message}"; Debug.WriteLine(errorMessage); - new Thread(() => ShowUiError(errorMessage)).Start(); + ShowUiError(errorMessage); } } } @@ -118,6 +128,7 @@ public void Pull() var refSpecs = repo.Network.Remotes[settings.git.RemoteName].FetchRefSpecs.Select(x => x.Specification); + ShowUiMessage("Start Git Pull"); Commands.Fetch(repo, settings.git.RemoteName, refSpecs, new FetchOptions { CredentialsProvider = (_, _, _) => @@ -144,12 +155,13 @@ public void Pull() try { repo.Merge(remoteBranch, signature, new MergeOptions()); + ShowUiMessage("Merge Successful"); } catch (Exception e) { var errorMessage = $"Can't merge remote branch to local branch, because {e.Message}"; Debug.WriteLine(errorMessage); - new Thread(() => ShowUiError(errorMessage)).Start(); + ShowUiError(errorMessage); } if (stash != null) @@ -157,6 +169,7 @@ public void Pull() var stashIndex = repo.Stashes.ToList().IndexOf(stash); var applyStatus = repo.Stashes.Apply(stashIndex); + ShowUiMessage("Stash Applied"); if (applyStatus == StashApplyStatus.Applied) repo.Stashes.Remove(stashIndex); } @@ -164,7 +177,7 @@ public void Pull() if (repo.Index.Conflicts.Any()) { const string errorMessage = "Fix conflicts and then commit the result"; - new Thread(() => ShowUiError(errorMessage)).Start(); + ShowUiError(errorMessage); } } } @@ -190,11 +203,18 @@ private static (GitSettings git, string? repositoryPath) GetSettings() private static void ShowUiError(string message) { - Dispatcher.UIThread.InvokeAsync(() => + Debug.WriteLine($"Git error: {message} at {DateTime.Now}"); + var notify = Locator.Current.GetService(); + notify?.ErrorToast(message); + } + + private static void ShowUiMessage(string message) + { + var settings = GetSettings(); + if (settings.git.ShowStatusToasts) { - var notificationManager = Locator.Current.GetService(); - notificationManager?.Ask("Git Error", message, - () => Debug.WriteLine($"User read the git error {message} at {DateTime.Now}")); - }); + var notify = Locator.Current.GetService(); + notify?.SuccessToast(message); + } } } \ No newline at end of file diff --git a/src/Unlimotion/Unlimotion.csproj b/src/Unlimotion/Unlimotion.csproj index 5dc0e90..cdc9454 100644 --- a/src/Unlimotion/Unlimotion.csproj +++ b/src/Unlimotion/Unlimotion.csproj @@ -26,7 +26,8 @@ - + + diff --git a/src/Unlimotion/Views/MainWindow.axaml b/src/Unlimotion/Views/MainWindow.axaml index ee56800..b92a9eb 100644 --- a/src/Unlimotion/Views/MainWindow.axaml +++ b/src/Unlimotion/Views/MainWindow.axaml @@ -6,50 +6,57 @@ xmlns:views="clr-namespace:Unlimotion.Views" xmlns:dialogHostAvalonia="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia" xmlns:unlimotion="clr-namespace:Unlimotion" + xmlns:controls="clr-namespace:Avalonia.Notification.Controls;assembly=Notification.Avalonia" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Unlimotion.Views.MainWindow" Icon="/Assets/Unlimotion.ico" Title="{Binding Title}" x:DataType="viewModel:MainWindowViewModel"> - - - - - - - + + + + + + + - + - - - - - - - -