diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/BugsnagService.cs b/src/ui/windows/TogglDesktop/TogglDesktop/BugsnagService.cs index 71b298df49..f28b0155a3 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/BugsnagService.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/BugsnagService.cs @@ -51,24 +51,26 @@ public static void Init() }); }); - Toggl.OnError += delegate(string errmsg, bool user_error) - { - Toggl.Debug(errmsg); - try - { - if (!user_error && bugsnag.Configuration.ReleaseStage != "development") - NotifyBugsnag(new Exception(errmsg)); - } - catch (Exception ex) - { - Toggl.Debug("Could not check if can notify bugsnag: " + ex); - } - }; + Toggl.OnError.Subscribe(x => OnError(x.errorMessage, x.isUserError)); Application.ThreadException += Application_ThreadException; AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; } + private static void OnError(string errorMessage, bool isUserError) + { + Toggl.Debug(errorMessage); + try + { + if (!isUserError && bugsnag.Configuration.ReleaseStage != "development") + NotifyBugsnag(new Exception(errorMessage)); + } + catch (Exception ex) + { + Toggl.Debug("Could not check if can notify bugsnag: " + ex); + } + } + private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { NotifyBugsnag(e.ExceptionObject as Exception); diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/Program.cs b/src/ui/windows/TogglDesktop/TogglDesktop/Program.cs index 7cf5787de6..390eeb9cf6 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/Program.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/Program.cs @@ -1,5 +1,7 @@ using System; using System.Diagnostics; +using System.Reactive.Linq; +using System.Reactive.Subjects; using System.Reflection; using System.Windows.Interop; using System.Windows.Media; @@ -10,10 +12,8 @@ namespace TogglDesktop static class Program { private static SingleInstanceManager singleInstanceManager; - public static ulong UserId { - get; - private set; - } + private static readonly BehaviorSubject UserIdSubject = new BehaviorSubject(0); + public static ulong UserId => UserIdSubject.Value; public static bool IsLoggedIn => UserId > 0; [STAThread] @@ -29,9 +29,7 @@ static void Main(string[] args) private static void OnBeforeStartup() { - Toggl.OnLogin += delegate (bool open, ulong user_id) { - UserId = user_id; - }; + Toggl.OnLogin.Select(x => x.userId).Subscribe(UserIdSubject); BugsnagService.Init(); singleInstanceManager.BeforeStartup -= OnBeforeStartup; } diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/Toggl.cs b/src/ui/windows/TogglDesktop/TogglDesktop/Toggl.cs index f2a0c25cba..4612127f7d 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/Toggl.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/Toggl.cs @@ -4,6 +4,9 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Reactive; +using System.Reactive.Linq; +using System.Reactive.Subjects; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Input; @@ -67,102 +70,6 @@ public enum DownloadStatus #endregion - #region callback delegates - - public delegate void DisplayApp( - bool open); - - public delegate void DisplayOverlay( - Int64 type); - - public delegate void DisplayError( - string errmsg, - bool user_error); - - public delegate void DisplaySyncState( - SyncState state); - - public delegate void DisplayUnsyncedItems( - Int64 count); - - public delegate void DisplayOnlineState( - OnlineState state); - - public delegate void DisplayURL( - string url); - - public delegate void DisplayLogin( - bool open, - UInt64 user_id); - - public delegate void DisplayReminder( - string title, - string informative_text); - - public delegate void DisplayTimeEntryList( - bool open, - List list, - bool show_load_more_button); - - public delegate void DisplayAutocomplete( - List list); - - public delegate void DisplayViewItems( - List list); - - public delegate void DisplayTimeEntryEditor( - bool open, - TogglTimeEntryView te, - string focused_field_name); - - public delegate void DisplaySettings( - bool open, - TogglSettingsView settings); - - public delegate void DisplayRunningTimerState( - TogglTimeEntryView te); - - public delegate void DisplayStoppedTimerState(); - - public delegate void DisplayIdleNotification( - string guid, - string since, - string duration, - Int64 started, - string description); - - public delegate void DisplayAutotrackerRules( - List rules, string[] terms); - - public delegate void DisplayAutotrackerNotification( - string projectName, ulong projectId, ulong taskId); - - public delegate void DisplayUpdateDownloadState( - string url, DownloadStatus status); - - public delegate void DisplayProjectColors( - string[] colors, ulong count); - - public delegate void DisplayCountries( - List list); - - public delegate void DisplayPromotion( - long id); - - public delegate void DisplayObmExperiment( - ulong id, bool included, bool seenBefore); - - public delegate void DisplayPomodoro( - string title, string informativeText); - - public delegate void DisplayPomodoroBreak( - string title, string informativeText); - - public delegate void DisplayInAppNotification( - string title, string text, string button, string url); - - #endregion - #region api calls public static void LoadMore() @@ -584,11 +491,6 @@ public static string UserEmail() return toggl_get_user_email(ctx); } - public static void FullSync() - { - toggl_fullsync(ctx); - } - public static void Sync() { OnManualSync(); @@ -757,39 +659,108 @@ public static void TrackExpandAllDays() #endregion - #region callback events - - public static event DisplayApp OnApp = delegate { }; - public static event DisplayOverlay OnOverlay = delegate { }; - public static event DisplayError OnError = delegate { }; - public static event DisplayOnlineState OnOnlineState = delegate { }; - public static event DisplayLogin OnLogin = delegate { }; - public static event DisplayReminder OnReminder = delegate { }; - public static event DisplayTimeEntryList OnTimeEntryList = delegate { }; - public static event DisplayAutocomplete OnTimeEntryAutocomplete = delegate { }; - public static event DisplayAutocomplete OnMinitimerAutocomplete = delegate { }; - public static event DisplayAutocomplete OnProjectAutocomplete = delegate { }; - public static event DisplayTimeEntryEditor OnTimeEntryEditor = delegate { }; - public static event DisplayViewItems OnWorkspaceSelect = delegate { }; - public static event DisplayViewItems OnClientSelect = delegate { }; - public static event DisplayViewItems OnTags = delegate { }; - public static event DisplaySettings OnSettings = delegate { }; - public static event DisplayRunningTimerState OnRunningTimerState = delegate { }; - public static event DisplayStoppedTimerState OnStoppedTimerState = delegate { }; - public static event DisplayURL OnURL = delegate { }; - public static event DisplayIdleNotification OnIdleNotification = delegate { }; - public static event DisplayAutotrackerRules OnAutotrackerRules = delegate { }; - public static event DisplayAutotrackerNotification OnAutotrackerNotification = delegate { }; - public static event DisplaySyncState OnDisplaySyncState = delegate { }; - public static event DisplayUnsyncedItems OnDisplayUnsyncedItems = delegate { }; - public static event DisplayUpdateDownloadState OnDisplayUpdateDownloadState = delegate { }; - public static event DisplayProjectColors OnDisplayProjectColors = delegate { }; - public static event DisplayCountries OnDisplayCountries = delegate { }; - public static event DisplayPromotion OnDisplayPromotion = delegate { }; - public static event DisplayObmExperiment OnDisplayObmExperiment = delegate { }; - public static event DisplayPomodoro OnDisplayPomodoro = delegate { }; - public static event DisplayPomodoroBreak OnDisplayPomodoroBreak = delegate { }; - public static event DisplayInAppNotification OnDisplayInAppNotification = delegate { }; + #region library events as observables + + private static readonly Subject _onApp = new Subject(); + public static readonly IObservable OnApp = _onApp.AsObservable(); + + private static readonly Subject _onOverlay = new Subject(); + public static readonly IObservable OnOverlay = _onOverlay.AsObservable(); + + private static readonly Subject<(string, bool)> _onError = new Subject<(string,bool)>(); + public static readonly IObservable<(string errorMessage, bool isUserError)> OnError = _onError.AsObservable(); + + private static readonly Subject _onOnlineState = new Subject(); + public static readonly IObservable OnOnlineState = _onOnlineState.AsObservable(); + + private static readonly Subject<(bool, ulong)> _onLogin = new Subject<(bool, ulong)>(); + public static readonly IObservable<(bool open, ulong userId)> OnLogin = _onLogin.AsObservable(); + + private static readonly Subject<(string, string)> _onReminder = new Subject<(string, string)>(); + public static readonly IObservable<(string title, string informativeText)> OnReminder = _onReminder.AsObservable(); + + private static readonly Subject<(bool, List, bool)> _onTimeEntryList = new Subject<(bool, List, bool)>(); + public static readonly IObservable<(bool open, List list, bool showLoadMore)> OnTimeEntryList =_onTimeEntryList.AsObservable(); + + private static readonly Subject> _onTimeEntryAutocomplete = new Subject>(); + public static readonly IObservable> OnTimeEntryAutocomplete = _onTimeEntryAutocomplete.AsObservable(); + + private static readonly Subject> _onMinitimerAutocomplete = new Subject>(); + public static readonly IObservable> OnMinitimerAutocomplete = _onMinitimerAutocomplete.AsObservable(); + + private static readonly Subject> _onProjectAutocomplete = new Subject>(); + public static readonly IObservable> OnProjectAutocomplete = _onProjectAutocomplete.AsObservable(); + + private static readonly Subject<(bool, TogglTimeEntryView, string)> _onTimeEntryEditor = new Subject<(bool, TogglTimeEntryView, string)>(); + public static readonly IObservable<(bool open, TogglTimeEntryView timeEntry, string focusedFieldName)> + OnTimeEntryEditor = _onTimeEntryEditor.AsObservable(); + + private static readonly Subject> _onWorkspaceSelect = new Subject>(); + public static readonly IObservable> OnWorkspaceSelect = _onWorkspaceSelect.AsObservable(); + + private static readonly Subject> _onClientSelect = new Subject>(); + public static readonly IObservable> OnClientSelect = _onClientSelect.AsObservable(); + + private static readonly Subject> _onTags = new Subject>(); + public static readonly IObservable> OnTags = _onTags.AsObservable(); + + private static readonly Subject<(bool, TogglSettingsView)> _onSettings = new Subject<(bool, TogglSettingsView)>(); + public static readonly IObservable<(bool open, TogglSettingsView settings)> OnSettings = _onSettings.AsObservable(); + + private static readonly Subject _onRunningTimerState = new Subject(); + public static readonly IObservable OnRunningTimerState = _onRunningTimerState.AsObservable(); + + private static readonly Subject _onStoppedTimerState = new Subject(); + public static readonly IObservable OnStoppedTimerState = _onStoppedTimerState.AsObservable(); + + private static readonly Subject _onURL = new Subject(); + public static readonly IObservable OnURL = _onURL.AsObservable(); + + private static readonly Subject<(string, string, string, long, string)> _onIdleNotification = new Subject<(string, string, string, long, string)>(); + public static readonly IObservable<(string, string, string, long, string)> OnIdleNotification = _onIdleNotification.AsObservable(); + + private static readonly Subject> _onAutotrackerRules = new Subject>(); + public static readonly IObservable> OnAutotrackerRules = _onAutotrackerRules.AsObservable(); + + private static readonly Subject<(string, ulong, ulong)> _onAutotrackerNotification = new Subject<(string, ulong, ulong)>(); + public static readonly IObservable<(string projectName, ulong projectId, ulong taskId)> OnAutotrackerNotification = + _onAutotrackerNotification.AsObservable(); + + private static readonly Subject _onDisplaySyncState = new Subject(); + public static readonly IObservable OnDisplaySyncState = _onDisplaySyncState.AsObservable(); + + private static readonly Subject _onDisplayUnsyncedItems = new Subject(); + public static readonly IObservable OnDisplayUnsyncedItems = _onDisplayUnsyncedItems.AsObservable(); + + private static readonly Subject<(string, DownloadStatus)> _onDisplayUpdateDownloadState = new Subject<(string, DownloadStatus)>(); + public static readonly IObservable<(string url, DownloadStatus status)> OnDisplayUpdateDownloadState = + _onDisplayUpdateDownloadState.AsObservable(); + + private static readonly Subject _onDisplayProjectColors = new Subject(); + public static readonly IObservable OnDisplayProjectColors = _onDisplayProjectColors.AsObservable(); + + private static readonly Subject> _onDisplayCountries = new Subject>(); + public static readonly IObservable> OnDisplayCountries = _onDisplayCountries.AsObservable(); + + private static readonly Subject _onDisplayPromotion = new Subject(); + public static readonly IObservable OnDisplayPromotion = _onDisplayPromotion.AsObservable(); + + private static readonly Subject<(ulong, bool, bool)> _onDisplayObmExperiment = new Subject<(ulong, bool, bool)>(); + public static readonly IObservable<(ulong id, bool included, bool seenBefore)> OnDisplayObmExperiment = + _onDisplayObmExperiment.AsObservable(); + + private static readonly Subject<(string title, string informativeText)> _onDisplayPomodoro = new Subject<(string title, string informativeText)>(); + public static readonly IObservable<(string title, string informativeText)> OnDisplayPomodoro = + _onDisplayPomodoro.AsObservable(); + + private static readonly Subject<(string title, string informativeText)> _onDisplayPomodoroBreak = new Subject<(string title, string informativeText)>(); + public static readonly IObservable<(string title, string informativeText)> OnDisplayPomodoroBreak = + _onDisplayPomodoroBreak.AsObservable(); + + private static readonly Subject<(string title, string button, string text, string url)> _onDisplayInAppNotification = + new Subject<(string title, string button, string text, string url)>(); + public static readonly IObservable<(string title, string button, string text, string url)> OnDisplayInAppNotification = + _onDisplayInAppNotification.AsObservable(); private static void listenToLibEvents() { @@ -797,7 +768,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnApp")) { - OnApp(open); + _onApp.OnNext(open); } }); @@ -805,7 +776,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnOverlay")) { - OnOverlay(type); + _onOverlay.OnNext(type); } }); @@ -813,7 +784,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnError, user_error: {1}, message: {0}", errmsg, user_error)) { - OnError(errmsg, user_error); + _onError.OnNext((errmsg, user_error)); } }); @@ -821,14 +792,14 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnDisplaySyncState, state: {0}", state)) { - OnDisplaySyncState((SyncState)state); + _onDisplaySyncState.OnNext((SyncState)state); } }); toggl_on_unsynced_items(ctx, count => { using (Performance.Measure("Calling OnDisplayUnsyncedItems, count: {0}", count)) { - OnDisplayUnsyncedItems(count); + _onDisplayUnsyncedItems.OnNext(count); } }); @@ -836,7 +807,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnOnlineState, state: {0}", state)) { - OnOnlineState((OnlineState)state); + _onOnlineState.OnNext((OnlineState)state); } }); @@ -844,7 +815,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnLogin")) { - OnLogin(open, user_id); + _onLogin.OnNext((open, user_id)); } }); @@ -852,7 +823,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnReminder, title: {0}", title)) { - OnReminder(title, informative_text); + _onReminder.OnNext((title, informative_text)); } }); @@ -860,7 +831,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnTimeEntryList, open: {0}", open)) { - OnTimeEntryList(open, convertToTimeEntryList(first), show_load_more_button); + _onTimeEntryList.OnNext((open, convertToTimeEntryList(first), show_load_more_button)); } }); @@ -868,7 +839,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnTimeEntryAutocomplete")) { - OnTimeEntryAutocomplete(convertToAutocompleteList(first)); + _onTimeEntryAutocomplete.OnNext(convertToAutocompleteList(first)); } }); @@ -876,7 +847,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnMinitimerAutocomplete")) { - OnMinitimerAutocomplete(convertToAutocompleteList(first)); + _onMinitimerAutocomplete.OnNext(convertToAutocompleteList(first)); } }); @@ -884,7 +855,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnProjectAutocomplete")) { - OnProjectAutocomplete(convertToAutocompleteList(first)); + _onProjectAutocomplete.OnNext(convertToAutocompleteList(first)); } }); @@ -892,7 +863,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnTimeEntryEditor, focused field: {0}", focused_field_name)) { - OnTimeEntryEditor(open, marshalStruct(te), focused_field_name); + _onTimeEntryEditor.OnNext((open, marshalStruct(te), focused_field_name)); } }); @@ -900,7 +871,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnWorkspaceSelect")) { - OnWorkspaceSelect(convertToViewItemList(first)); + _onWorkspaceSelect.OnNext(convertToViewItemList(first)); } }); @@ -908,7 +879,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnClientSelect")) { - OnClientSelect(convertToViewItemList(first)); + _onClientSelect.OnNext(convertToViewItemList(first)); } }); @@ -916,7 +887,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnTags")) { - OnTags(convertToViewItemList(first)); + _onTags.OnNext(convertToViewItemList(first)); } }); @@ -924,7 +895,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnSettings")) { - OnSettings(open, marshalStruct(settings)); + _onSettings.OnNext((open, marshalStruct(settings))); } }); @@ -934,13 +905,13 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnStoppedTimerState")) { - OnStoppedTimerState(); + _onStoppedTimerState.OnNext(Unit.Default); return; } } using (Performance.Measure("Calling OnRunningTimerState")) { - OnRunningTimerState(marshalStruct(te)); + _onRunningTimerState.OnNext(marshalStruct(te)); } }); @@ -948,7 +919,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnURL")) { - OnURL(url); + _onURL.OnNext(url); } }); @@ -956,7 +927,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnIdleNotification")) { - OnIdleNotification(guid, since, duration, started, description); + _onIdleNotification.OnNext((guid, since, duration, started, description)); } }); @@ -964,7 +935,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnAutotrackerRules")) { - OnAutotrackerRules(convertToAutotrackerEntryList(first), list); + _onAutotrackerRules.OnNext(convertToAutotrackerEntryList(first)); } }); @@ -972,7 +943,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnAutotrackerNotification")) { - OnAutotrackerNotification(name, project_id, task_id); + _onAutotrackerNotification.OnNext((name, project_id, task_id)); } }); @@ -980,7 +951,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnUpdateDownloadState, v: {0}, state: {1}", version, state)) { - OnDisplayUpdateDownloadState(version, (DownloadStatus)state); + _onDisplayUpdateDownloadState.OnNext((version, (DownloadStatus)state)); } }); @@ -988,7 +959,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnProjectColors, count: {0}", count)) { - OnDisplayProjectColors(colors, count); + _onDisplayProjectColors.OnNext(colors); } }); @@ -996,7 +967,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnCountries")) { - OnDisplayCountries(convertToCountryList(first)); + _onDisplayCountries.OnNext(convertToCountryList(first)); } }); @@ -1004,7 +975,7 @@ private static void listenToLibEvents() { using (Performance.Measure("Calling OnDisplayPromotino, id: {0}", id)) { - OnDisplayPromotion(id); + _onDisplayPromotion.OnNext(id); } }); @@ -1014,28 +985,28 @@ private static void listenToLibEvents() "Calling OnDisplatObmExperiment, id: {0}, included: {1}, seen: {2}", id, included, seenBefore)) { - OnDisplayObmExperiment(id, included, seenBefore); + _onDisplayObmExperiment.OnNext((id, included, seenBefore)); } }); toggl_on_pomodoro(ctx, (title, text) => { using (Performance.Measure("Calling OnDisplayPomodoro")) { - OnDisplayPomodoro(title, text); + _onDisplayPomodoro.OnNext((title, text)); } }); toggl_on_pomodoro_break(ctx, (title, text) => { using (Performance.Measure("Calling OnDisplayPomodoroBreak")) { - OnDisplayPomodoroBreak(title, text); + _onDisplayPomodoroBreak.OnNext((title, text)); } }); toggl_on_message(ctx, (title, text, button, url) => { using (Performance.Measure("Calling OnDisplayInAppNotification")) { - OnDisplayInAppNotification(title, text, button, url); + _onDisplayInAppNotification.OnNext((title, text, button, url)); } }); } @@ -1193,7 +1164,7 @@ private static void installPendingUpdates() catch (Exception e) { BugsnagService.NotifyBugsnag(e); - Toggl.OnError?.Invoke($"Unable to delete the file: {file.FullName}. Delete this file manually.", false); + _onError.OnNext(($"Unable to delete the file: {file.FullName}. Delete this file manually.", false)); } } @@ -1229,7 +1200,7 @@ private static Action createUpdateAction() catch (Exception e) { BugsnagService.NotifyBugsnag(e); - Toggl.OnError?.Invoke($"Unable to delete the file: {file.FullName}. Delete this file manually.", false); + _onError.OnNext(($"Unable to delete the file: {file.FullName}. Delete this file manually.", false)); } } return null; @@ -1252,7 +1223,7 @@ private static Action createUpdateAction() catch (Win32Exception e) { BugsnagService.NotifyBugsnag(e); - Toggl.OnError?.Invoke("Unable to run the installer. Please update manually.", false); + _onError.OnNext(("Unable to run the installer. Please update manually.", false)); return; } @@ -1263,7 +1234,7 @@ private static Action createUpdateAction() } else { - Toggl.OnError?.Invoke("Unable to run the installer. Please update manually.", false); + _onError.OnNext(("Unable to run the installer. Please update manually.", false)); } }; } @@ -1476,12 +1447,12 @@ public static Int64 UnixFromDateTime(DateTime value) public static void NewError(string errmsg, bool user_error) { - OnError(errmsg, user_error); + _onError.OnNext((errmsg, user_error)); } public static void OpenInBrowser(string url) { - OnURL(url); + _onURL.OnNext(url); } public static void ShowErrorAndNotify(string errmsg, Exception ex) diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/ExperimentManager.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/ExperimentManager.cs index ac22dd0094..3bd491b4b8 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/ExperimentManager.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/ExperimentManager.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reactive.Linq; namespace TogglDesktop.Experiments { @@ -22,7 +23,7 @@ public ExperimentManager(MainWindow mainWindow) { this.mainWindow = mainWindow; - Toggl.OnDisplayObmExperiment += this.onDisplayObmExperiment; + Toggl.OnDisplayObmExperiment.ObserveOnDispatcher().Subscribe(this.onDisplayObmExperiment); if (this.experiments.Any(e => e.Id == 0)) { @@ -40,10 +41,9 @@ public IEnumerable CurrentExperumentIds get { return this.experiments.Select(e => e.Id); } } - private void onDisplayObmExperiment(ulong id, bool included, bool seenBefore) + private void onDisplayObmExperiment((ulong id, bool included, bool seenBefore) x) { - if (this.mainWindow.TryBeginInvoke(this.onDisplayObmExperiment, id, included, seenBefore)) - return; + var (id, included, seenBefore) = x; var experiment = this.experiments.FirstOrDefault(e => e.Id == id); diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/implementations/Experiment101.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/implementations/Experiment101.cs index 96ba9151d0..f5b4ee4eb7 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/implementations/Experiment101.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/implementations/Experiment101.cs @@ -1,4 +1,7 @@  +using System; +using System.Reactive.Linq; + namespace TogglDesktop.Experiments { sealed class Experiment101 : ExperimentBase @@ -13,7 +16,7 @@ public Experiment101() protected override void runIncluded(ExperimentParameters parameters) { this.parameters = parameters; - Toggl.OnRunningTimerState += this.onRunningTimerState; + Toggl.OnRunningTimerState.Take(1).Subscribe(this.onRunningTimerState); } private void onRunningTimerState(Toggl.TogglTimeEntryView te) @@ -21,9 +24,6 @@ private void onRunningTimerState(Toggl.TogglTimeEntryView te) ExperimentHacks.RemoveEmptyStateFirstLine(this, this.parameters); this.parameters.TutorialManager.ActivateScreen(); Toggl.SendObmAction(this.Id, "seen"); - - Toggl.OnRunningTimerState -= this.onRunningTimerState; } - } } \ No newline at end of file diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/implementations/Experiment101Screen.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/implementations/Experiment101Screen.xaml.cs index d13fbe8342..9a5d1afc4b 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/implementations/Experiment101Screen.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/implementations/Experiment101Screen.xaml.cs @@ -1,10 +1,12 @@ - +using System; +using System.Reactive.Disposables; using System.Windows; namespace TogglDesktop.Experiments { public partial class Experiment101Screen { + private IDisposable _subscription; public Experiment101Screen() { this.InitializeComponent(); @@ -15,24 +17,14 @@ protected override void initialise() Toggl.SetManualMode(false); Toggl.ViewTimeEntryList(); - Toggl.OnStoppedTimerState += this.onStoppedTimerState; - Toggl.OnTimeEntryEditor += this.onTimeEntryEditor; + _subscription = new CompositeDisposable( + Toggl.OnStoppedTimerState.Subscribe(_ => this.quitTutorial()), + Toggl.OnTimeEntryEditor.Subscribe(_ => this.quitTutorial())); } protected override void cleanup() { - Toggl.OnStoppedTimerState -= this.onStoppedTimerState; - Toggl.OnTimeEntryEditor -= this.onTimeEntryEditor; - } - - private void onTimeEntryEditor(bool open, Toggl.TogglTimeEntryView te, string f) - { - this.quitTutorial(); - } - - private void onStoppedTimerState() - { - this.quitTutorial(); + _subscription.Dispose(); } private void closeButtonClick(object sender, RoutedEventArgs e) diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/implementations/Experiment98Screen1.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/implementations/Experiment98Screen1.xaml.cs index 2efa474d96..55715b3664 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/implementations/Experiment98Screen1.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/implementations/Experiment98Screen1.xaml.cs @@ -1,4 +1,6 @@  +using System; +using System.Reactive.Linq; using System.Windows; namespace TogglDesktop.Experiments @@ -10,19 +12,20 @@ public Experiment98Screen1() this.InitializeComponent(); } + private IDisposable _subscription; protected override void initialise() { Toggl.SetManualMode(false); Toggl.ViewTimeEntryList(); - Toggl.OnRunningTimerState += this.onRunningTimerState; + _subscription = Toggl.OnRunningTimerState.Subscribe(this.onRunningTimerState); Toggl.SendObmAction(98, "seen_1"); } protected override void cleanup() { - Toggl.OnRunningTimerState -= this.onRunningTimerState; + _subscription.Dispose(); } private void onRunningTimerState(Toggl.TogglTimeEntryView te) @@ -34,14 +37,12 @@ private void closeButtonClick(object sender, RoutedEventArgs e) { this.quitTutorial(); - Toggl.OnRunningTimerState += this.openSecondScreenDelayed; + Toggl.OnRunningTimerState.Take(1).Subscribe(this.openSecondScreenDelayed); } private void openSecondScreenDelayed(Toggl.TogglTimeEntryView te) { this.activateScreen(); - - Toggl.OnRunningTimerState -= this.openSecondScreenDelayed; } } } diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/implementations/Experiment98Screen2.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/implementations/Experiment98Screen2.xaml.cs index 794590a032..f0a0f14b1f 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/implementations/Experiment98Screen2.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/implementations/Experiment98Screen2.xaml.cs @@ -1,10 +1,12 @@ - +using System; +using System.Reactive.Disposables; using System.Windows; namespace TogglDesktop.Experiments { public partial class Experiment98Screen2 { + private IDisposable _subscription; public Experiment98Screen2() { this.InitializeComponent(); @@ -12,27 +14,17 @@ public Experiment98Screen2() protected override void initialise() { - Toggl.OnStoppedTimerState += this.onStoppedTimerState; - Toggl.OnTimeEntryEditor += this.onTimeEntryEditor; + _subscription = new CompositeDisposable( + Toggl.OnStoppedTimerState.Subscribe(_ => this.quitTutorial()), + Toggl.OnTimeEntryEditor.Subscribe(_ => this.quitTutorial())); Toggl.SendObmAction(98, "seen_2"); } protected override void cleanup() { - Toggl.OnStoppedTimerState -= this.onStoppedTimerState; - Toggl.OnTimeEntryEditor -= this.onTimeEntryEditor; + _subscription.Dispose(); } - - private void onStoppedTimerState() - { - this.quitTutorial(); - } - private void onTimeEntryEditor(bool open, Toggl.TogglTimeEntryView te, string focused_field_name) - { - this.quitTutorial(); - } - private void closeButtonClick(object sender, RoutedEventArgs e) { this.quitTutorial(); diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/implementations/Experiment99Screen.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/implementations/Experiment99Screen.xaml.cs index ac7a2b9a46..6d1eb32d15 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/implementations/Experiment99Screen.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Experiments/implementations/Experiment99Screen.xaml.cs @@ -1,4 +1,4 @@ - +using System; using System.Windows; namespace TogglDesktop.Experiments @@ -10,25 +10,20 @@ public Experiment99Screen() this.InitializeComponent(); } + private IDisposable _subscription; protected override void initialise() { Toggl.SetManualMode(false); Toggl.ViewTimeEntryList(); - Toggl.OnStoppedTimerState += this.onStoppedTimerState; + _subscription = Toggl.OnStoppedTimerState.Subscribe(_ => this.quitTutorial()); } protected override void cleanup() { - Toggl.OnStoppedTimerState -= this.onStoppedTimerState; + _subscription.Dispose(); } - private void onStoppedTimerState() - { - this.quitTutorial(); - } - - private void closeButtonClick(object sender, RoutedEventArgs e) { this.quitTutorial(); diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Notifications/NotificationManager.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Notifications/NotificationManager.cs index 3619456ee9..5106454820 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Notifications/NotificationManager.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Notifications/NotificationManager.cs @@ -1,4 +1,5 @@ using System; +using System.Reactive.Linq; using System.Windows; using System.Windows.Controls.Primitives; using Hardcodet.Wpf.TaskbarNotification; @@ -15,16 +16,15 @@ public NotificationManager(TaskbarIcon taskbarIcon, Window parentWindow) _taskbarIcon = taskbarIcon; _parentWindow = parentWindow; - Toggl.OnReminder += OnReminder; - Toggl.OnAutotrackerNotification += OnAutotrackerNotification; - Toggl.OnDisplayPomodoro += OnDisplayPomodoro; - Toggl.OnDisplayPomodoroBreak += OnDisplayPomodoro; + Toggl.OnReminder.ObserveOnDispatcher().Subscribe(OnReminder); + Toggl.OnAutotrackerNotification.ObserveOnDispatcher().Subscribe(OnAutotrackerNotification); + Toggl.OnDisplayPomodoro.ObserveOnDispatcher().Subscribe(OnDisplayPomodoro); + Toggl.OnDisplayPomodoroBreak.ObserveOnDispatcher().Subscribe(OnDisplayPomodoro); } - private void OnReminder(string title, string informativeText) + private void OnReminder((string title, string informativeText) x) { - if (_parentWindow.TryBeginInvoke(OnReminder, title, informativeText)) - return; + var (title, informativeText) = x; void StartButtonClick() { @@ -47,10 +47,9 @@ void StartButtonClick() } } - private void OnDisplayPomodoro(string title, string informativeText) + private void OnDisplayPomodoro((string title, string informativeText) x) { - if (_parentWindow.TryBeginInvoke(OnDisplayPomodoro, title, informativeText)) - return; + var (title, informativeText) = x; void StartNewButtonClick() { @@ -84,10 +83,9 @@ void ContinueLatestButtonClick() } } - private void OnAutotrackerNotification(string projectName, ulong projectId, ulong taskId) + private void OnAutotrackerNotification((string projectName, ulong projectId, ulong taskId) x) { - if (_parentWindow.TryBeginInvoke(OnAutotrackerNotification, projectName, projectId, taskId)) - return; + var (projectName, projectId, taskId) = x; void StartButtonClick() { diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen2.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen2.xaml.cs index 9d4576d566..9b6d5ae9fb 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen2.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen2.xaml.cs @@ -1,4 +1,6 @@  +using System; + namespace TogglDesktop.Tutorial { public partial class BasicTutorialScreen2 @@ -8,15 +10,16 @@ public BasicTutorialScreen2() this.InitializeComponent(); } + private IDisposable _subscription; protected override void initialise() { - Toggl.OnRunningTimerState += this.onRunningTimerState; + _subscription = Toggl.OnRunningTimerState.Subscribe(this.onRunningTimerState); this.tutorialManager.Timer.DescriptionTextBoxTextChanged += this.onDescriptionTextChanged; } protected override void cleanup() { - Toggl.OnRunningTimerState -= this.onRunningTimerState; + _subscription.Dispose(); this.tutorialManager.Timer.DescriptionTextBoxTextChanged -= this.onDescriptionTextChanged; } @@ -32,6 +35,5 @@ private void onDescriptionTextChanged(object sender, string text) this.activateScreen(); } - } } diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen3.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen3.xaml.cs index e13f2a6227..485bd6eba6 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen3.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen3.xaml.cs @@ -1,4 +1,5 @@ - +using System; + namespace TogglDesktop.Tutorial { public partial class BasicTutorialScreen3 @@ -8,14 +9,15 @@ public BasicTutorialScreen3() this.InitializeComponent(); } + private IDisposable _subscription; protected override void initialise() { - Toggl.OnRunningTimerState += this.onRunningTimerState; + _subscription = Toggl.OnRunningTimerState.Subscribe(onRunningTimerState); } protected override void cleanup() { - Toggl.OnRunningTimerState -= this.onRunningTimerState; + _subscription.Dispose(); } private void onRunningTimerState(Toggl.TogglTimeEntryView te) diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen4.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen4.xaml.cs index 7fb60af929..928348f8c0 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen4.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen4.xaml.cs @@ -1,8 +1,12 @@  +using System; +using System.Reactive.Disposables; + namespace TogglDesktop.Tutorial { public partial class BasicTutorialScreen4 { + private IDisposable _subscription; public BasicTutorialScreen4() { this.InitializeComponent(); @@ -10,26 +14,14 @@ public BasicTutorialScreen4() protected override void initialise() { - Toggl.OnTimeEntryEditor += this.onTimeEntryEditor; - Toggl.OnStoppedTimerState += this.onStoppedTimerState; + _subscription = new CompositeDisposable( + Toggl.OnTimeEntryEditor.Subscribe(_ => this.activateScreen()), + Toggl.OnStoppedTimerState.Subscribe(_ => this.activateScreen())); } protected override void cleanup() { - Toggl.OnTimeEntryEditor -= this.onTimeEntryEditor; - Toggl.OnStoppedTimerState -= this.onStoppedTimerState; + _subscription.Dispose(); } - - private void onTimeEntryEditor(bool open, Toggl.TogglTimeEntryView te, string focusedFieldName) - { - this.activateScreen(); - } - - private void onStoppedTimerState() - { - this.activateScreen(); - } - - } } diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen5.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen5.xaml.cs index 633ec2e153..a29ebd186b 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen5.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen5.xaml.cs @@ -1,10 +1,12 @@ - -using System.Collections.Generic; +using System; +using System.Reactive.Disposables; +using System.Reactive.Linq; namespace TogglDesktop.Tutorial { public partial class BasicTutorialScreen5 { + private IDisposable _subscription; public BasicTutorialScreen5() { this.InitializeComponent(); @@ -12,29 +14,14 @@ public BasicTutorialScreen5() protected override void initialise() { - Toggl.OnTimeEntryList += this.onTimeEntryList; - Toggl.OnStoppedTimerState += this.onStoppedTimerState; + _subscription = new CompositeDisposable( + Toggl.OnTimeEntryList.Where(x => x.open).Subscribe(_ => this.activateScreen()), + Toggl.OnStoppedTimerState.Subscribe(_ => this.activateScreen())); } protected override void cleanup() { - Toggl.OnTimeEntryList -= this.onTimeEntryList; - Toggl.OnStoppedTimerState -= this.onStoppedTimerState; + _subscription.Dispose(); } - - private void onTimeEntryList(bool open, List list, bool showLoadMoreButton) - { - if (!open) - return; - - this.activateScreen(); - } - - - private void onStoppedTimerState() - { - this.activateScreen(); - } - } } diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen6.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen6.xaml.cs index eaea3c4ecc..40a68062f2 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen6.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen6.xaml.cs @@ -1,4 +1,4 @@ - +using System; namespace TogglDesktop.Tutorial { public partial class BasicTutorialScreen6 @@ -8,19 +8,15 @@ public BasicTutorialScreen6() this.InitializeComponent(); } + private IDisposable _subscription; protected override void initialise() { - Toggl.OnStoppedTimerState += this.onStoppedTimerState; + _subscription = Toggl.OnStoppedTimerState.Subscribe(_ => this.activateScreen()); } protected override void cleanup() { - Toggl.OnStoppedTimerState -= this.onStoppedTimerState; - } - - private void onStoppedTimerState() - { - this.activateScreen(); + _subscription.Dispose(); } } } diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen7.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen7.xaml.cs index 4a2f599489..d5ecc7bf9e 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen7.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/Tutorial/BasicTutorialScreen7.xaml.cs @@ -1,10 +1,12 @@  -using System.Windows; +using System; +using System.Reactive.Disposables; namespace TogglDesktop.Tutorial { public partial class BasicTutorialScreen7 { + private IDisposable _subscription; public BasicTutorialScreen7() { this.InitializeComponent(); @@ -12,23 +14,19 @@ public BasicTutorialScreen7() protected override void initialise() { - Toggl.OnRunningTimerState += this.onRunningTimerState; - Toggl.OnTimeEntryEditor += this.onTimerEntryEditor; + _subscription = new CompositeDisposable( + Toggl.OnRunningTimerState.Subscribe(this.onRunningTimerState), + Toggl.OnTimeEntryEditor.Subscribe(_ => this.quitTutorial())); } protected override void cleanup() { - Toggl.OnRunningTimerState -= this.onRunningTimerState; - Toggl.OnTimeEntryEditor -= this.onTimerEntryEditor; + _subscription.Dispose(); } private void onRunningTimerState(Toggl.TogglTimeEntryView te) { this.quitTutorial(); } - private void onTimerEntryEditor(bool open, Toggl.TogglTimeEntryView te, string focusedFieldName) - { - this.quitTutorial(); - } } } diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/ViewModels/LoginViewModel.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/ViewModels/LoginViewModel.cs index d7a6f5511f..7bf952acc6 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/ViewModels/LoginViewModel.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/ViewModels/LoginViewModel.cs @@ -35,8 +35,10 @@ public LoginViewModel(Action refreshLoginBindings, Action refreshSignupBindings) { _refreshLoginBindings = refreshLoginBindings; _refreshSignupBindings = refreshSignupBindings; - Toggl.OnDisplayCountries += OnDisplayCountries; - Toggl.OnSettings += OnSettings; + Toggl.OnDisplayCountries + .ObserveOnDispatcher() + .ToPropertyEx(this, x => x.Countries); + Toggl.OnSettings.Subscribe(x => _httpClientFactory = HttpClientFactoryFromProxySettings(x.settings)); this.WhenAnyValue(x => x.SelectedConfirmAction, x => x == ConfirmAction.LogIn ? "Log in" : "Sign up") .ToPropertyEx(this, x => x.ConfirmButtonText); @@ -66,11 +68,10 @@ public LoginViewModel(Action refreshLoginBindings, Action refreshSignupBindings) public ReactiveCommand ConfirmGoogleLoginSignupCommand { get; } public IObservable IsLoginSignupExecuting { get; } - [Reactive] - public IList Countries { get; private set; } + public IList Countries { [ObservableAsProperty] get; } [Reactive] - public CountryViewModel SelectedCountry { get; set; } + public Toggl.TogglCountryView SelectedCountry { get; set; } [Reactive] public ConfirmAction SelectedConfirmAction { get; set; } @@ -119,7 +120,7 @@ private void EnsureValidationApplied() "A password is required"); _selectedCountryValidation = this.ValidationRule( x => x.SelectedCountry, - selectedCountry => selectedCountry != null, + selectedCountry => selectedCountry.ID > 0, "Please select country"); _isTosCheckedValidation = this.ValidationRule( x => x.IsTosChecked, @@ -215,20 +216,9 @@ private async void ConfirmGoogleLoginSignup() } } - private void OnDisplayCountries(List list) - { - var countriesVm = list.Select(c => new CountryViewModel(c)).ToArray(); - Dispatcher.CurrentDispatcher.Invoke(() => { Countries = countriesVm; }); - } - - private void OnSettings(bool open, Toggl.TogglSettingsView settings) - { - _httpClientFactory = HttpClientFactoryFromProxySettings(settings); - } - public async Task GoogleSignupAsync() { - await GoogleAuth(accessToken => Toggl.GoogleSignup(accessToken, SelectedCountry?.ID ?? default), "Signup"); + await GoogleAuth(accessToken => Toggl.GoogleSignup(accessToken, SelectedCountry.ID), "Signup"); } public async Task GoogleLoginAsync() @@ -292,7 +282,7 @@ private async Task ConfirmAsync(Func confirmAc { var email = Email; var password = Password; - var selectedCountryId = SelectedCountry?.ID ?? -1; + var selectedCountryId = SelectedCountry.ID; return await Task.Run(() => confirmAction(email, password, selectedCountryId)); } @@ -319,18 +309,6 @@ private static HttpClientFactory HttpClientFactoryFromProxySettings(Toggl.TogglS return proxyHttpClientFactory; } - public class CountryViewModel - { - private readonly Toggl.TogglCountryView _countryView; - public CountryViewModel(Toggl.TogglCountryView countryView) - { - _countryView = countryView; - } - - public string Name => _countryView.Name; - public long ID => _countryView.ID; - } - private class ProxySupportedHttpClientFactory : HttpClientFactory { public bool UseProxy { get; set; } diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/ViewModels/PreferencesWindowViewModel.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/ViewModels/PreferencesWindowViewModel.cs index b370f67e70..3c3fe27fe6 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/ViewModels/PreferencesWindowViewModel.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/ViewModels/PreferencesWindowViewModel.cs @@ -44,10 +44,7 @@ public PreferencesWindowViewModel(ShowMessageBoxDelegate showMessageBox, Action { _showMessageBox = showMessageBox; _closePreferencesWindow = closePreferencesWindow; - var isLoggedIn = Observable.FromEvent( - onNext => (open, userId) => { onNext(userId != 0); }, - x => Toggl.OnLogin += x, - x => Toggl.OnLogin -= x); + var isLoggedIn = Toggl.OnLogin.Select(x => x.userId != 0); ClearCacheCommand = ReactiveCommand.Create(ClearCache, isLoggedIn.ObserveOnDispatcher()); this.WhenAnyValue(x => x.ShowHideToggl) diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/AutotrackerNotification.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/AutotrackerNotification.xaml.cs new file mode 100644 index 0000000000..74335220fc --- /dev/null +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/AutotrackerNotification.xaml.cs @@ -0,0 +1,41 @@ +using System; +using System.Reactive.Linq; +using System.Windows; +using System.Windows.Controls.Primitives; +using System.Windows.Input; +using Hardcodet.Wpf.TaskbarNotification; + +namespace TogglDesktop +{ + public partial class AutotrackerNotification + { + private ulong projectId; + private ulong taskId; + + public AutotrackerNotification(TaskbarIcon icon, MainWindow mainWindow) + : base(icon, mainWindow) + { + this.InitializeComponent(); + Toggl.OnAutotrackerNotification.ObserveOnDispatcher().Subscribe(this.onAutotrackerNotification); + } + + private void onAutotrackerNotification((string projectName, ulong projectId, ulong taskId) x) + { + var (projectName, projectId, taskId) = x; + this.Message = @$"Start tracking ""{projectName}""?"; + this.projectId = projectId; + this.taskId = taskId; + + this.RemoveFromParent(); + + _icon.ShowNotification(this, PopupAnimation.Slide, TimeSpan.FromSeconds(6)); + } + + private void onStartButtonClick(object sender, RoutedEventArgs e) + { + Close(); + Toggl.Start("", "", this.taskId, this.projectId, null, null); + _parentWindow.ShowOnTop(); + } + } +} diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/AutotrackerSettings.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/AutotrackerSettings.xaml.cs index b2373244e0..8baa8f9bd6 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/AutotrackerSettings.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/AutotrackerSettings.xaml.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Reactive.Linq; using System.Windows; using System.Windows.Input; using TogglDesktop.AutoCompletion; @@ -17,38 +18,15 @@ public AutotrackerSettings() { this.InitializeComponent(); - Toggl.OnAutotrackerRules += this.onAutotrackerRules; - Toggl.OnProjectAutocomplete += this.onProjectAutocomplete; + Toggl.OnAutotrackerRules.ObserveOnDispatcher().Subscribe(this.fillRules); + Toggl.OnProjectAutocomplete + .Select(AutoCompleteControllers.ForProjects) + .ObserveOnDispatcher() + .Subscribe(this.projectAutoComplete.SetController); } - #region toggl events - - private void onAutotrackerRules(List rules, string[] terms) - { - if (this.TryBeginInvoke(this.onAutotrackerRules, rules, terms)) - return; - - this.fill(rules, terms); - } - - private void onProjectAutocomplete(List list) - { - if (this.TryBeginInvoke(this.onProjectAutocomplete, list)) - return; - - this.projectAutoComplete.SetController(AutoCompleteControllers.ForProjects(list)); - } - - #endregion - #region filling from data - private void fill(List rules, string[] terms) - { - // this.termAutoComplete.SetController(AutoCompleteControllers.ForStrings(terms)); - this.fillRules(rules); - } - private void fillRules(List rules) { this.rulesPanel.Children.Clear(); diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/MiniTimer.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/MiniTimer.xaml.cs index d7fc4afd27..d879964ffc 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/MiniTimer.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/MiniTimer.xaml.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Reactive; +using System.Reactive.Linq; using System.Windows; using System.Windows.Input; using System.Windows.Threading; @@ -24,9 +26,12 @@ public MiniTimer() this.setupSecondsTimer(); - Toggl.OnMinitimerAutocomplete += this.onMiniTimerAutocomplete; - Toggl.OnRunningTimerState += this.onRunningTimerState; - Toggl.OnStoppedTimerState += this.onStoppedTimerState; + Toggl.OnMinitimerAutocomplete + .Select(AutoCompleteControllers.ForTimer) + .ObserveOnDispatcher() + .Subscribe(this.descriptionAutoComplete.SetController); + Toggl.OnRunningTimerState.ObserveOnDispatcher().Subscribe(this.onRunningTimerState); + Toggl.OnStoppedTimerState.ObserveOnDispatcher().Subscribe(this.onStoppedTimerState); this.resetUIState(false, true); } @@ -50,24 +55,18 @@ private void setupSecondsTimer() #region toggl events - private void onStoppedTimerState() + private void onStoppedTimerState(Unit _) { - if (this.TryBeginInvoke(this.onStoppedTimerState)) - return; - using (Performance.Measure("timer responding to OnStoppedTimerState")) { this.secondsTimer.IsEnabled = false; this.resetUIState(false); - this.runningTimeEntry = default(Toggl.TogglTimeEntryView); + this.runningTimeEntry = default; } } private void onRunningTimerState(Toggl.TogglTimeEntryView te) { - if (this.TryBeginInvoke(this.onRunningTimerState, te)) - return; - using (Performance.Measure("timer responding to OnRunningTimerState")) { this.runningTimeEntry = te; @@ -76,17 +75,6 @@ private void onRunningTimerState(Toggl.TogglTimeEntryView te) } } - private void onMiniTimerAutocomplete(List list) - { - if (this.TryBeginInvoke(this.onMiniTimerAutocomplete, list)) - return; - - using (Performance.Measure("timer building auto complete controller, {0} items", list.Count)) - { - this.descriptionAutoComplete.SetController(AutoCompleteControllers.ForTimer(list)); - } - } - #endregion #region ui events diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/PomodoroNotification.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/PomodoroNotification.xaml.cs new file mode 100644 index 0000000000..fe7c5e4a49 --- /dev/null +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/PomodoroNotification.xaml.cs @@ -0,0 +1,54 @@ +using System; +using System.Reactive.Linq; +using System.Windows; +using System.Windows.Controls.Primitives; +using Hardcodet.Wpf.TaskbarNotification; + +namespace TogglDesktop +{ + public partial class PomodoroNotification + { + public PomodoroNotification(TaskbarIcon icon, MainWindow mainWindow) + : base(icon, mainWindow) + { + this.InitializeComponent(); + + Toggl.OnDisplayPomodoro.ObserveOnDispatcher().Subscribe(this.onDisplayPomodoro); + Toggl.OnDisplayPomodoroBreak.ObserveOnDispatcher().Subscribe(this.onDisplayPomodoro); + } + + private void onDisplayPomodoro((string title, string informativeText) x) + { + var (title, informativeText) = x; + + this.Message = informativeText; + this.Title = title; + + this.RemoveFromParent(); + + if (!_icon.ShowNotification(this, PopupAnimation.Slide, null)) + { + _icon.ShowBalloonTip(title, informativeText, Properties.Resources.toggl, largeIcon: true); + } + else + { + System.Media.SystemSounds.Asterisk.Play(); + } + } + + private void onContinueButtonClick(object sender, RoutedEventArgs e) + { + Close(); + Toggl.ContinueLatest(true); + } + + private void onStartNewButtonClick(object sender, RoutedEventArgs e) + { + Close(); + var guid = Toggl.Start("", "", 0, 0, "", "", true); + _parentWindow.ShowOnTop(); + if (guid != null) + Toggl.Edit(guid, true, Toggl.Description); + } + } +} diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/ProjectColorPicker.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/ProjectColorPicker.xaml.cs index 2ddc6f402d..08df458a65 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/ProjectColorPicker.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/ProjectColorPicker.xaml.cs @@ -1,5 +1,6 @@  using System; +using System.Reactive.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; @@ -34,14 +35,11 @@ public ProjectColorPicker() this.DataContext = this; this.InitializeComponent(); - Toggl.OnDisplayProjectColors += this.onDisplayProjectColors; + Toggl.OnDisplayProjectColors.ObserveOnDispatcher().Subscribe(this.onDisplayProjectColors); } - private void onDisplayProjectColors(string[] strings, ulong count) + private void onDisplayProjectColors(string[] strings) { - if (this.TryBeginInvoke(this.onDisplayProjectColors, strings, count)) - return; - this.colors = strings; this.list.ItemsSource = strings; } diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/ReminderNotification.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/ReminderNotification.xaml.cs new file mode 100644 index 0000000000..80f8f61846 --- /dev/null +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/ReminderNotification.xaml.cs @@ -0,0 +1,42 @@ +using System; +using System.Reactive.Linq; +using System.Windows; +using System.Windows.Controls.Primitives; +using Hardcodet.Wpf.TaskbarNotification; + +namespace TogglDesktop +{ + public partial class ReminderNotification + { + public ReminderNotification(TaskbarIcon icon, MainWindow mainWindow) + : base(icon, mainWindow) + { + InitializeComponent(); + Toggl.OnReminder.ObserveOnDispatcher().Subscribe(onReminder); + } + + private void onReminder((string title, string informativeText) x) + { + var (title, informativeText) = x; + Title = title; + Message = informativeText; + + this.RemoveFromParent(); + + if (!_icon.ShowNotification(this, PopupAnimation.Slide, TimeSpan.FromSeconds(10))) + { + _icon.ShowBalloonTip(title, informativeText, Properties.Resources.toggl, largeIcon: true); + } + } + + private void onStartButtonClick(object sender, RoutedEventArgs e) + { + Close(); + var guid = Toggl.Start("", "", 0, 0, "", "", true); + _parentWindow.ShowOnTop(); + if (guid != null) + Toggl.Edit(guid, true, Toggl.Description); + } + + } +} \ No newline at end of file diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/StatusBar.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/StatusBar.xaml.cs index 28b619b6b7..aaaa3b8e25 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/StatusBar.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/StatusBar.xaml.cs @@ -1,49 +1,29 @@ using System; +using System.Reactive.Linq; using System.Windows; namespace TogglDesktop { public partial class StatusBar { - private Toggl.OnlineState onlineState; - public StatusBar() { this.InitializeComponent(); - Toggl.OnOnlineState += this.onOnlineState; - Toggl.OnLogin += this.onLogin; + Toggl.OnOnlineState + .CombineLatest(Toggl.OnLogin, GetState) + .ObserveOnDispatcher() + .Subscribe(onOnlineState); } - private void onLogin(bool open, ulong userID) + private static Toggl.OnlineState GetState(Toggl.OnlineState onlineState, (bool open, ulong userId) x) { - if (this.TryBeginInvoke(this.onLogin, open, userID)) - return; - - if (open) - { - this.Hide(); - } + return x.userId == 0 ? Toggl.OnlineState.Online : onlineState; } private void onOnlineState(Toggl.OnlineState state) { - if (this.TryBeginInvoke(this.onOnlineState, state)) - return; - - this.onlineState = state; - this.update(); - } - - private void update() - { - if (!Program.IsLoggedIn) - { - this.Hide(); - return; - } - - switch (this.onlineState) + switch (state) { case Toggl.OnlineState.Online: { diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/SyncingIndicator.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/SyncingIndicator.xaml.cs index b53f4ef90d..8aa7675fd8 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/SyncingIndicator.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/controls/SyncingIndicator.xaml.cs @@ -1,4 +1,5 @@ using System; +using System.Reactive.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Media.Animation; @@ -19,9 +20,9 @@ public SyncingIndicator() this.spinnerAnimation = (Storyboard)this.Resources["RotateSpinner"]; - Toggl.OnDisplaySyncState += this.onDisplaySyncState; - Toggl.OnDisplayUnsyncedItems += this.onDisplayUnsyncedItems; - Toggl.OnLogin += this.onLogin; + Toggl.OnDisplaySyncState.ObserveOnDispatcher().Subscribe(this.onDisplaySyncState); + Toggl.OnDisplayUnsyncedItems.ObserveOnDispatcher().Subscribe(this.onDisplayUnsyncedItems); + Toggl.OnLogin.Where(x => x.open).ObserveOnDispatcher().Subscribe(_ => this.Hide()); Toggl.OnManualSync += this.onManualSync; } @@ -32,31 +33,14 @@ private bool hasSomethingToShow #region toggl events - private void onLogin(bool open, ulong userID) - { - if (this.TryBeginInvoke(this.onLogin, open, userID)) - return; - - if (open) - { - this.Hide(); - } - } - private void onDisplayUnsyncedItems(long count) { - if (this.TryBeginInvoke(this.onDisplayUnsyncedItems, count)) - return; - this.unsyncedItems = count; this.update(); } private void onDisplaySyncState(Toggl.SyncState state) { - if (this.TryBeginInvoke(this.onDisplaySyncState, state)) - return; - this.syncState = state; this.update(); } diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/views/EditView.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/views/EditView.xaml.cs index 76c045ef63..190f9e193f 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/views/EditView.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/views/EditView.xaml.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Reactive.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; @@ -19,7 +20,6 @@ public partial class EditView { private Toggl.TogglTimeEntryView timeEntry; private bool isInNewProjectMode = true; - private List projects; private List clients; private List workspaces; private ulong selectedWorkspaceId; @@ -33,22 +33,33 @@ public EditView() this.DataContext = this; this.InitializeComponent(); - Toggl.OnLogin += this.onLogin; - Toggl.OnTimeEntryEditor += this.onTimeEntryEditor; - Toggl.OnMinitimerAutocomplete += this.onMinitimerAutocomplete; - Toggl.OnProjectAutocomplete += this.onProjectAutocomplete; - Toggl.OnClientSelect += this.onClientSelect; - Toggl.OnTags += this.onTags; - Toggl.OnWorkspaceSelect += this.onWorkspaceSelect; - } - - private void onLogin(bool open, ulong userId) + Toggl.OnLogin.ObserveOnDispatcher().Subscribe(_ => this.onLogin()); + Toggl.OnTimeEntryEditor.ObserveOnDispatcher().Subscribe(this.onTimeEntryEditor); + Toggl.OnMinitimerAutocomplete + .Select(AutoCompleteControllers.ForTimer) + .ObserveOnDispatcher() + .Subscribe(this.descriptionAutoComplete.SetController); + Toggl.OnProjectAutocomplete + .Select(AutoCompleteControllers.ForProjects) + .ObserveOnDispatcher() + .Subscribe(this.projectAutoComplete.SetController); + Toggl.OnClientSelect + .ObserveOnDispatcher() + .Subscribe(list => this.clients = list); + Toggl.OnClientSelect + .Select(AutoCompleteControllers.ForClients) + .ObserveOnDispatcher() + .Subscribe(this.clientAutoComplete.SetController); + Toggl.OnTags + .Select(list => list.Select(item => item.Name)) + .ObserveOnDispatcher() + .Subscribe(tagList.SetKnownTags); + Toggl.OnWorkspaceSelect.ObserveOnDispatcher().Subscribe(this.onWorkspaceSelect); + } + + private void onLogin() { - if (this.TryBeginInvoke(this.onLogin, open, userId)) - return; - this.timeEntry = new Toggl.TogglTimeEntryView(); - this.projects = null; this.clients = null; this.workspaces = null; } @@ -66,10 +77,9 @@ private bool hasTimeEntry() #region from time entry - private void onTimeEntryEditor(bool open, Toggl.TogglTimeEntryView timeEntry, string focusedFieldName) + private void onTimeEntryEditor((bool open, Toggl.TogglTimeEntryView timeEntry, string focusedFieldName) x) { - if (this.TryBeginInvoke(this.onTimeEntryEditor, open, timeEntry, focusedFieldName)) - return; + var (open, timeEntry, focusedFieldName) = x; using (Performance.Measure("filling edit view from OnTimeEntryEditor")) { @@ -225,61 +235,8 @@ private void durationUpdateTimerTick(object sender, string s) #endregion - #region auto completion - - private void onMinitimerAutocomplete(List list) - { - if (this.TryBeginInvoke(this.onMinitimerAutocomplete, list)) - return; - - using (Performance.Measure("building edit view entry auto complete controller, {0} items", list.Count)) - { - this.descriptionAutoComplete.SetController(AutoCompleteControllers.ForTimer(list)); - } - } - - private void onProjectAutocomplete(List list) - { - if (this.TryBeginInvoke(this.onProjectAutocomplete, list)) - return; - - this.projects = list; - - using (Performance.Measure("building edit view project auto complete controller, {0} items", this.projects.Count)) - { - this.projectAutoComplete.SetController(AutoCompleteControllers.ForProjects(list)); - } - } - - private void onClientSelect(List list) - { - if (this.TryBeginInvoke(this.onClientSelect, list)) - return; - - this.clients = list; - - using (Performance.Measure("building edit view client auto complete controller, {0} items", this.clients.Count)) - { - this.clientAutoComplete.SetController(AutoCompleteControllers.ForClients(list)); - } - } - - private void onTags(List list) - { - if (this.TryBeginInvoke(this.onTags, list)) - return; - - using (Performance.Measure("building edit view tags auto complete controller, {0} items", list.Count)) - { - this.tagList.SetKnownTags(list.Select(m => m.Name)); - } - } - private void onWorkspaceSelect(List list) { - if (this.TryBeginInvoke(this.onWorkspaceSelect, list)) - return; - this.workspaces = list; using (Performance.Measure("building edit view workspace auto complete controller, {0} items", list.Count)) @@ -292,8 +249,6 @@ private void onWorkspaceSelect(List list) #endregion - #endregion - #region change data #region time and date diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/views/Timer.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/views/Timer.xaml.cs index e851633f61..3a018f0ad3 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/views/Timer.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/views/Timer.xaml.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Reactive; +using System.Reactive.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; @@ -30,9 +32,12 @@ public Timer() this.setupSecondsTimer(); - Toggl.OnMinitimerAutocomplete += this.onMiniTimerAutocomplete; - Toggl.OnRunningTimerState += this.onRunningTimerState; - Toggl.OnStoppedTimerState += this.onStoppedTimerState; + Toggl.OnMinitimerAutocomplete + .Select(AutoCompleteControllers.ForTimer) + .ObserveOnDispatcher() + .Subscribe(this.descriptionAutoComplete.SetController); + Toggl.OnRunningTimerState.ObserveOnDispatcher().Subscribe(this.onRunningTimerState); + Toggl.OnStoppedTimerState.ObserveOnDispatcher().Subscribe(this.onStoppedTimerState); this.RunningTimeEntrySecondPulse += this.timerTick; @@ -58,11 +63,8 @@ private void setupSecondsTimer() #region toggl events - private void onStoppedTimerState() + private void onStoppedTimerState(Unit _) { - if (this.TryBeginInvoke(this.onStoppedTimerState)) - return; - using (Performance.Measure("timer responding to OnStoppedTimerState")) { this.secondsTimer.IsEnabled = false; @@ -74,9 +76,6 @@ private void onStoppedTimerState() private void onRunningTimerState(Toggl.TogglTimeEntryView te) { - if (this.TryBeginInvoke(this.onRunningTimerState, te)) - return; - using (Performance.Measure("timer responding to OnRunningTimerState")) { this.runningTimeEntry = te; @@ -85,17 +84,6 @@ private void onRunningTimerState(Toggl.TogglTimeEntryView te) } } - private void onMiniTimerAutocomplete(List list) - { - if (this.TryBeginInvoke(this.onMiniTimerAutocomplete, list)) - return; - - using (Performance.Measure("timer building auto complete controller, {0} items", list.Count)) - { - this.descriptionAutoComplete.SetController(AutoCompleteControllers.ForTimer(list)); - } - } - #endregion #region ui events diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/views/TimerEntryListView.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/views/TimerEntryListView.xaml.cs index c0a21fd1ee..a832388a2c 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/views/TimerEntryListView.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/views/TimerEntryListView.xaml.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reactive.Linq; using System.Windows; using System.Windows.Media; using System.Windows.Threading; @@ -15,9 +16,12 @@ public TimerEntryListView() { this.InitializeComponent(); - Toggl.OnTimeEntryEditor += this.onTimeEntryEditor; - Toggl.OnTimeEntryList += this.onTimeEntryList; - Toggl.OnLogin += this.onLogin; + Toggl.OnTimeEntryEditor.Select(x => x.timeEntry.GUID) + .ObserveOnDispatcher() + .Subscribe(this.Entries.SelectEntry); + Toggl.OnTimeEntryList.ObserveOnDispatcher().Subscribe(this.onTimeEntryList); + Toggl.OnLogin.Where(x => x.open || x.userId == 0).ObserveOnDispatcher() + .Subscribe(_ => this.Entries.Children.Clear()); } public Brush TitleBarBrush => this.Timer.Background; @@ -35,37 +39,14 @@ protected override void OnInitialized(EventArgs e) #region toggl events - private void onLogin(bool open, ulong userID) + private void onTimeEntryList((bool open, List list, bool showLoadMoreButton) x) { - if (this.TryBeginInvoke(this.onLogin, open, userID)) - return; - - if (open || userID == 0) - { - this.Entries.Children.Clear(); - } - } - - private void onTimeEntryList(bool open, List list, bool showLoadMoreButton) - { - if (this.TryBeginInvoke(this.onTimeEntryList, open, list, showLoadMoreButton)) - return; + var (open, list, showLoadMoreButton) = x; this.fillTimeEntryList(list); this.Entries.ViewModel.OnTimeEntryList(showLoadMoreButton, list.Count == 0); } - private void onTimeEntryEditor(bool open, Toggl.TogglTimeEntryView te, string focusedFieldName) - { - if (this.TryBeginInvoke(this.onTimeEntryEditor, open, te, focusedFieldName)) - return; - - using (Performance.Measure("highlighting cell in list")) - { - this.Entries.SelectEntry(te.GUID); - } - } - #endregion private void fillTimeEntryList(List list) diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/windows/AboutWindow.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/windows/AboutWindow.xaml.cs index 94d8c0fa46..b33e7e6c20 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/windows/AboutWindow.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/windows/AboutWindow.xaml.cs @@ -1,9 +1,8 @@ using System; -using System.Diagnostics; using System.Linq; +using System.Reactive.Linq; using System.Windows; using System.Windows.Controls; -using System.Windows.Input; namespace TogglDesktop { @@ -24,13 +23,12 @@ public AboutWindow() this.releaseChannelComboBox.ShowOnlyIf(!isUpdatCheckDisabled, true); this.releaseChannelLabel.ShowOnlyIf(!isUpdatCheckDisabled, true); - Toggl.OnDisplayUpdateDownloadState += this.onDisplayUpdateDownloadState; + Toggl.OnDisplayUpdateDownloadState.ObserveOnDispatcher().Subscribe(this.onDisplayUpdateDownloadState); } - private void onDisplayUpdateDownloadState(string version, Toggl.DownloadStatus status) + private void onDisplayUpdateDownloadState((string version, Toggl.DownloadStatus status) x) { - if (this.TryBeginInvoke(this.onDisplayUpdateDownloadState, version, status)) - return; + var (version, status) = x; string format; switch (status) diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/windows/EditViewPopup.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/windows/EditViewPopup.xaml.cs index 2df31ff2bb..a6090fee48 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/windows/EditViewPopup.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/windows/EditViewPopup.xaml.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using System.Reactive.Linq; using System.Windows; using System.Windows.Media.Animation; using TogglDesktop.Diagnostics; @@ -30,15 +31,13 @@ public EditViewPopup() this.MinWidth = this.EditView.MinWidth + 16; this.mainGrid.Width = 0; - Toggl.OnTimeEntryEditor += this.onTimeEntryEditor; - + Toggl.OnTimeEntryEditor.ObserveOnDispatcher().Subscribe(this.onTimeEntryEditor); KeyboardShortcuts.RegisterShortcuts(this); } - private void onTimeEntryEditor(bool open, Toggl.TogglTimeEntryView te, string focusedFieldName) + private void onTimeEntryEditor((bool open, Toggl.TogglTimeEntryView te, string focusedFieldName) x) { - if (this.TryBeginInvoke(this.onTimeEntryEditor, open, te, focusedFieldName)) - return; + var (open, te, focusedFieldName) = x; if (!this.Owner.IsVisible) return; diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/windows/IdleNotificationWindow.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/windows/IdleNotificationWindow.xaml.cs index 586c689d61..371604504f 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/windows/IdleNotificationWindow.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/windows/IdleNotificationWindow.xaml.cs @@ -1,4 +1,6 @@ using System; +using System.Reactive; +using System.Reactive.Linq; using System.Windows; using System.Windows.Input; using ControlzEx; @@ -15,14 +17,13 @@ public partial class IdleNotificationWindow public IdleNotificationWindow() { this.InitializeComponent(); - Toggl.OnIdleNotification += this.onIdleNotification; - Toggl.OnStoppedTimerState += this.onStoppedTimerState; + Toggl.OnIdleNotification.ObserveOnDispatcher().Subscribe(this.onIdleNotification); + Toggl.OnStoppedTimerState.ObserveOnDispatcher().Subscribe(this.onStoppedTimerState); } - private void onIdleNotification(string guid, string since, string duration, long started, string description) + private void onIdleNotification((string guid, string since, string duration, long started, string description) x) { - if (this.TryBeginInvoke(this.onIdleNotification, guid, since, duration, started, description)) - return; + var (guid, since, duration, started, description) = x; this.guid = guid; this.started = started; @@ -43,11 +44,8 @@ protected override void OnDeactivated(EventArgs e) this.Activate(); } - private void onStoppedTimerState() + private void onStoppedTimerState(Unit _) { - if (this.TryBeginInvoke(this.onStoppedTimerState)) - return; - if (this.IsVisible) { this.Hide(); diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/windows/MainWindow.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/windows/MainWindow.xaml.cs index 60e165bf90..9332c08156 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/windows/MainWindow.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/windows/MainWindow.xaml.cs @@ -5,6 +5,7 @@ using System.Drawing; using System.IO; using System.Linq; +using System.Reactive.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows; @@ -234,19 +235,20 @@ private void initializeColorScheme() private void initializeEvents() { - Toggl.OnApp += this.onApp; - Toggl.OnOverlay += this.onOverlay; - Toggl.OnError += this.onError; - Toggl.OnLogin += this.onLogin; - Toggl.OnTimeEntryEditor += this.onTimeEntryEditor; - Toggl.OnTimeEntryList += this.onTimeEntryList; - Toggl.OnOnlineState += this.onOnlineState; - Toggl.OnURL += this.onURL; + Toggl.OnApp.Where(show => show).ObserveOnDispatcher().Subscribe(_ => this.Show()); + Toggl.OnOverlay.ObserveOnDispatcher().Subscribe(this.onOverlay); + Toggl.OnError.ObserveOnDispatcher().Subscribe(this.onError); + Toggl.OnLogin.ObserveOnDispatcher().Subscribe(x => this.onLogin(x.open, x.userId)); + Toggl.OnTimeEntryEditor.Select(x => x.open).ObserveOnDispatcher().Subscribe(this.onTimeEntryEditor); + Toggl.OnTimeEntryList.ObserveOnDispatcher().Subscribe(this.onTimeEntryList); + Toggl.OnOnlineState.ObserveOnDispatcher() + .Subscribe(state => this.updateStatusIcons(state == Toggl.OnlineState.Online)); + Toggl.OnURL.Subscribe(this.onURL); Toggl.OnUserTimeEntryStart += this.onUserTimeEntryStart; - Toggl.OnRunningTimerState += this.onRunningTimerState; - Toggl.OnStoppedTimerState += this.onStoppedTimerState; - Toggl.OnSettings += this.onSettings; - Toggl.OnDisplayInAppNotification += this.onDisplayInAppNotification; + Toggl.OnRunningTimerState.ObserveOnDispatcher().Subscribe(this.setIsTracking); + Toggl.OnStoppedTimerState.ObserveOnDispatcher().Subscribe(_ => this.setIsNotTracking()); + Toggl.OnSettings.ObserveOnDispatcher().Subscribe(this.onSettings); + Toggl.OnDisplayInAppNotification.ObserveOnDispatcher().Subscribe(this.onDisplayInAppNotification); } private void finalInitialisation() @@ -290,35 +292,15 @@ private void trackingWindowSize() #region toggl events - private void onTimeEntryEditor(bool open, Toggl.TogglTimeEntryView te, string focusedFieldName) + private void onTimeEntryEditor(bool open) { - if (this.TryBeginInvoke(this.onTimeEntryEditor, open, te, focusedFieldName)) - return; - this.updateEditPopupLocation(true); - if (open) { this.Show(); } } - private void onStoppedTimerState() - { - if (this.TryBeginInvoke(this.onStoppedTimerState)) - return; - - this.updateTracking(null); - } - - private void onRunningTimerState(Toggl.TogglTimeEntryView te) - { - if (this.TryBeginInvoke(this.onRunningTimerState, te)) - return; - - this.updateTracking(te); - } - private void onUserTimeEntryStart() { this.taskbarIcon.CloseBalloon(); @@ -339,41 +321,23 @@ private void onURL(string url) } } - private void onOnlineState(Toggl.OnlineState state) + private void onTimeEntryList((bool, List, bool) x) { - if (this.TryBeginInvoke(this.onOnlineState, state)) - return; - - this.updateStatusIcons(state == Toggl.OnlineState.Online); - } - - private void onTimeEntryList(bool open, List list, bool showLoadMoreButton) - { - if (this.TryBeginInvoke(this.onTimeEntryList, open, list, showLoadMoreButton)) - return; - + var (open, list, _) = x; if (open) { this.errorBar.Hide(); this.setActiveView(this.timerEntryListView); } - // Get Today's total duration - var i = 0; - for (; i < list.Count; i++) - { - if (list[i].DateHeader == "Today") { - this.trayToolTip.TotalToday = list[i].DateDuration; - return; - } - } + this.trayToolTip.TotalToday = + list.Where(x => x.DateHeader == "Today") + .Select(x => x.DateDuration) + .FirstOrDefault() ?? "0 h 0 min"; } private void onLogin(bool open, ulong userID) { - if (this.TryBeginInvoke(this.onLogin, open, userID)) - return; - if (open) { this.setActiveView(this.loginView); @@ -393,58 +357,36 @@ private void onLogin(bool open, ulong userID) this.taskbarIcon.ToolTipText = $"Toggl - Logged in as {Toggl.UserEmail()}"; } - this.updateTracking(null); + this.setIsNotTracking(); } private void onOverlay(long type) { - - if (this.TryBeginInvoke(this.onOverlay, type)) - return; - this.overlayView.setType((int)type); this.setActiveView(this.overlayView); } - private void onError(string errmsg, bool userError) + private void onError((string, bool) tuple) { - if (this.TryBeginInvoke(this.onError, errmsg, userError)) - return; - - if (this.activeView?.HandlesError(errmsg) != true) + var (errorMessage, _) = tuple; + if (this.activeView?.HandlesError(errorMessage) != true) { - this.errorBar.ShowError(errmsg); - } - } - - private void onApp(bool open) - { - if (this.TryBeginInvoke(this.onApp, open)) - return; - - if (open) - { - this.Show(); + this.errorBar.ShowError(errorMessage); } } - private void onSettings(bool open, Toggl.TogglSettingsView settings) + private void onSettings((bool, Toggl.TogglSettingsView settings) x) { - if (this.TryBeginInvoke(this.onSettings, open, settings)) - return; - - Theme.SetThemeFromSettings(settings.ColorTheme); + Theme.SetThemeFromSettings(x.settings.ColorTheme); this.setGlobalShortcutsFromSettings(); - this.idleDetectionTimer.IsEnabled = settings.UseIdleDetection; - this.Topmost = settings.OnTop; - this.SetManualMode(settings.ManualMode, true); + this.idleDetectionTimer.IsEnabled = x.settings.UseIdleDetection; + this.Topmost = x.settings.OnTop; + this.SetManualMode(x.settings.ManualMode, true); } - private void onDisplayInAppNotification(string title, string text, string button, string url) + private void onDisplayInAppNotification((string title, string text, string button, string url) x) { - if (this.TryBeginInvoke(this.onDisplayInAppNotification, title, text, button, url)) - return; - + var (title, text, button, url) = x; if (inAppNotification == null) { inAppNotification = new InAppNotification @@ -758,32 +700,30 @@ private void updateStatusIcons(bool isOnline) this.taskbarIcon.Icon = new Icon(icon, SystemInformation.SmallIconSize); } - private void updateTracking(Toggl.TogglTimeEntryView? timeEntry) + private void setIsTracking(Toggl.TogglTimeEntryView timeEntry) { - var tracking = timeEntry != null; + this.IsTracking = true; + this.trayToolTip.IsTracking = true; + this.trayToolTip.TimeEntryLabel = timeEntry.ToTrayToolTipTimeEntryLabelViewModel(); + this.trayToolTip.SetDuration(timeEntry); - this.IsTracking = tracking; - this.trayToolTip.IsTracking = tracking; + var description = timeEntry.Description; + this.Title = string.IsNullOrEmpty(description) + ? "Toggl Desktop" + : description + " - Toggl Desktop"; - if (tracking) - { - this.trayToolTip.TimeEntryLabel = timeEntry.Value.ToTrayToolTipTimeEntryLabelViewModel(); - this.trayToolTip.SetDuration(timeEntry.Value); - - var description = timeEntry.Value.Description; + if (this.IsInManualMode) + this.SetManualMode(false); - this.Title = string.IsNullOrEmpty(description) - ? "Toggl Desktop" - : description + " - Toggl Desktop"; + this.updateStatusIcons(true); + } - if (this.IsInManualMode) - this.SetManualMode(false); - } - else - { - this.trayToolTip.TimeEntryLabel = null; - this.Title = "Toggl Desktop"; - } + private void setIsNotTracking() + { + this.IsTracking = false; + this.trayToolTip.IsTracking = false; + this.trayToolTip.TimeEntryLabel = null; + this.Title = "Toggl Desktop"; this.updateStatusIcons(true); } diff --git a/src/ui/windows/TogglDesktop/TogglDesktop/ui/windows/PreferencesWindow.xaml.cs b/src/ui/windows/TogglDesktop/TogglDesktop/ui/windows/PreferencesWindow.xaml.cs index 24beabb156..adf7ec9ba4 100644 --- a/src/ui/windows/TogglDesktop/TogglDesktop/ui/windows/PreferencesWindow.xaml.cs +++ b/src/ui/windows/TogglDesktop/TogglDesktop/ui/windows/PreferencesWindow.xaml.cs @@ -1,6 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.ComponentModel; using System.Linq; +using System.Reactive.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; @@ -36,9 +38,21 @@ public PreferencesWindow() this.InitializeComponent(); ViewModel = new PreferencesWindowViewModel(MessageBox.Show(this), this.Close); - Toggl.OnSettings += this.onSettings; - Toggl.OnLogin += this.onLogin; - Toggl.OnProjectAutocomplete += this.onProjectAutocomplete; + Toggl.OnSettings.ObserveOnDispatcher().Subscribe(this.onSettings); + Toggl.OnLogin + .Select(x => !x.open && x.userId != 0) + .Select(x => x ? Toggl.IsTimelineRecordingEnabled() : (bool?) null) + .ObserveOnDispatcher() + .Subscribe(isRecordTimelineEnabled => + { + this.recordTimelineCheckBox.IsEnabled = isRecordTimelineEnabled.HasValue; + this.recordTimelineCheckBox.IsChecked = isRecordTimelineEnabled == true; + }); + Toggl.OnProjectAutocomplete + .Do(list => this.knownProjects = list) + .Select(AutoCompleteControllers.ForProjects) + .ObserveOnDispatcher() + .Subscribe(this.defaultProjectAutoComplete.SetController); this.Closing += OnClosing; } @@ -48,23 +62,12 @@ private void OnClosing(object sender, CancelEventArgs e) ViewModel.ResetRecordedShortcuts(); } - private void onLogin(bool open, ulong userID) + private void onSettings((bool open, Toggl.TogglSettingsView settings) x) { - if (this.TryBeginInvoke(this.onLogin, open, userID)) - return; - - this.recordTimelineCheckBox.IsEnabled = !open && userID != 0; - this.recordTimelineCheckBox.IsChecked = Toggl.IsTimelineRecordingEnabled(); - } - - private void onSettings(bool open, Toggl.TogglSettingsView settings) - { - if (this.TryBeginInvoke(this.onSettings, open, settings)) - return; - if (this.isSaving) return; + var (open, settings) = x; using (Performance.Measure("filling settings from OnSettings")) { this.updateUI(settings); @@ -77,16 +80,6 @@ private void onSettings(bool open, Toggl.TogglSettingsView settings) this.Activate(); } } - private void onProjectAutocomplete(List list) - { - if (this.TryBeginInvoke(this.onProjectAutocomplete, list)) - return; - - this.knownProjects = list; - - this.defaultProjectAutoComplete.SetController(AutoCompleteControllers.ForProjects(list)); - } - private void updateUI(Toggl.TogglSettingsView settings) {