From 40fa4f2ebdafd02702b6985bbf0d02fc1ac9f8a2 Mon Sep 17 00:00:00 2001 From: rlittlesii <6969701+RLittlesII@users.noreply.github.com> Date: Sun, 26 Jan 2020 02:36:25 -0600 Subject: [PATCH 1/3] fix: NavigateBack losing values on the NavigationStack --- src/ReactiveUI.XamForms/RoutedViewHost.cs | 91 +++++++++++++---------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/src/ReactiveUI.XamForms/RoutedViewHost.cs b/src/ReactiveUI.XamForms/RoutedViewHost.cs index 1d6c114352..7c01588da6 100644 --- a/src/ReactiveUI.XamForms/RoutedViewHost.cs +++ b/src/ReactiveUI.XamForms/RoutedViewHost.cs @@ -6,13 +6,13 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Reactive; +using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reactive.Threading.Tasks; using System.Reflection; using Splat; using Xamarin.Forms; -#pragma warning disable RCS1090 // Call 'ConfigureAwait(false)'. namespace ReactiveUI.XamForms { /// @@ -20,7 +20,8 @@ namespace ReactiveUI.XamForms /// /// /// - public class RoutedViewHost : NavigationPage, IActivatableView + [SuppressMessage("Readability", "RCS1090: Call 'ConfigureAwait(false)", Justification = "This class interacts with the UI thread.")] + public class RoutedViewHost : NavigationPage, IActivatableView, IEnableLogger { /// /// The router bindable property. @@ -38,13 +39,13 @@ public class RoutedViewHost : NavigationPage, IActivatableView /// You *must* register an IScreen class representing your App's main Screen. public RoutedViewHost() { - this.WhenActivated(new Action>(d => + this.WhenActivated(disposable => { bool currentlyPopping = false; bool popToRootPending = false; bool userInstigated = false; - d(this.WhenAnyObservable(x => x.Router.NavigationChanged) + this.WhenAnyObservable(x => x.Router.NavigationChanged) .Where(_ => Router.NavigationStack.Count == 0) .Select(x => { @@ -54,18 +55,22 @@ public RoutedViewHost() popToRootPending = true; return x; }) - .Subscribe()); - - var previousCount = - this.WhenAnyObservable(x => x.Router.NavigationChanged) - .CountChanged() - .ObserveOn(Router.Scheduler) - .Select(_ => Router.NavigationStack.Count) - .StartWith(Router.NavigationStack.Count); - - var currentCount = previousCount.Skip(1); + .Subscribe() + .DisposeWith(disposable); + + Router.NavigationChanged + .CountChanged() + .Select(_ => Router.NavigationStack.Count) + .StartWith(Router.NavigationStack.Count) + .Buffer(2, 1) + .Select(counts => new + { + Delta = counts[0] - counts[1], + Current = counts[1], - d(Observable.Zip(previousCount, currentCount, (previous, current) => new { Delta = previous - current, Current = current }) + // cache current viewmodel as it might change if some other Navigation command is executed midway + CurrentViewModel = Router.GetCurrentViewModel() + }) .Where(_ => !userInstigated) .Where(x => x.Delta > 0) .SelectMany( @@ -94,14 +99,19 @@ public RoutedViewHost() finally { currentlyPopping = false; - ((IViewFor)CurrentPage).ViewModel = Router.GetCurrentViewModel(); + if (CurrentPage is IViewFor page && x.CurrentViewModel != null) + { + page.ViewModel = x.CurrentViewModel; + } } return Unit.Default; }) - .Subscribe()); + .Subscribe() + .DisposeWith(disposable); - d(this.WhenAnyObservable(x => x.Router.Navigate) + Router + .Navigate .SelectMany(_ => PageForViewModel(Router.GetCurrentViewModel())) .SelectMany(async page => { @@ -125,20 +135,21 @@ public RoutedViewHost() popToRootPending = false; return page; }) - .Subscribe()); + .Subscribe() + .DisposeWith(disposable); var poppingEvent = Observable.FromEvent, Unit>( - eventHandler => - { - void Handler(object sender, NavigationEventArgs e) => eventHandler(Unit.Default); - return Handler; - }, - x => Popped += x, - x => Popped -= x); + eventHandler => + { + void Handler(object sender, NavigationEventArgs e) => eventHandler(Unit.Default); + return Handler; + }, + x => Popped += x, + x => Popped -= x); // NB: Catch when the user hit back as opposed to the application // requesting Back via NavigateBack - d(poppingEvent + poppingEvent .Where(_ => !currentlyPopping && Router != null) .Subscribe(_ => { @@ -146,19 +157,23 @@ public RoutedViewHost() try { - if (Router.NavigationStack.Count > 1) - { - Router.NavigationStack.RemoveAt(Router.NavigationStack.Count - 1); - } + Router.NavigationStack.RemoveAt(Router.NavigationStack.Count - 1); } finally { userInstigated = false; } - ((IViewFor)CurrentPage).ViewModel = Router.GetCurrentViewModel(); - })); - })); + var vm = Router.GetCurrentViewModel(); + if (CurrentPage is IViewFor page && vm != null) + { + // don't replace view model if vm is null + page.ViewModel = vm; + } + }) + .DisposeWith(disposable); + }); + var screen = Locator.Current.GetService(); if (screen == null) { @@ -169,8 +184,8 @@ public RoutedViewHost() this.WhenAnyValue(x => x.Router) .SelectMany(router => - router - .NavigationStack + { + return router.NavigationStack .ToObservable() .Select(x => (Page)ViewLocator.Current.ResolveView(x)) .SelectMany(x => PushAsync(x).ToObservable()) @@ -184,7 +199,8 @@ public RoutedViewHost() ((IViewFor)CurrentPage).ViewModel = vm; CurrentPage.Title = vm.UrlPathSegment; - })) + }); + }) .Subscribe(); } @@ -227,4 +243,3 @@ protected IObservable PageForViewModel(IRoutableViewModel vm) } } } -#pragma warning restore RCS1090 // Call 'ConfigureAwait(false)'. From da03218c3776a3607270b41389270d0ce8553214 Mon Sep 17 00:00:00 2001 From: rlittlesii <6969701+RLittlesII@users.noreply.github.com> Date: Sun, 26 Jan 2020 14:47:26 -0600 Subject: [PATCH 2/3] Added Concat() --- src/ReactiveUI.XamForms/RoutedViewHost.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ReactiveUI.XamForms/RoutedViewHost.cs b/src/ReactiveUI.XamForms/RoutedViewHost.cs index 7c01588da6..160e6135f3 100644 --- a/src/ReactiveUI.XamForms/RoutedViewHost.cs +++ b/src/ReactiveUI.XamForms/RoutedViewHost.cs @@ -135,6 +135,7 @@ public RoutedViewHost() popToRootPending = false; return page; }) + .Concat() .Subscribe() .DisposeWith(disposable); From 877ddce4530d23f55992f36c7cb07b382160cdba Mon Sep 17 00:00:00 2001 From: rlittlesii <6969701+RLittlesII@users.noreply.github.com> Date: Sun, 26 Jan 2020 14:50:39 -0600 Subject: [PATCH 3/3] fix Concat() --- src/ReactiveUI.XamForms/RoutedViewHost.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ReactiveUI.XamForms/RoutedViewHost.cs b/src/ReactiveUI.XamForms/RoutedViewHost.cs index 160e6135f3..dfe370c53b 100644 --- a/src/ReactiveUI.XamForms/RoutedViewHost.cs +++ b/src/ReactiveUI.XamForms/RoutedViewHost.cs @@ -73,7 +73,7 @@ public RoutedViewHost() }) .Where(_ => !userInstigated) .Where(x => x.Delta > 0) - .SelectMany( + .Select( async x => { // XF doesn't provide a means of navigating back more than one screen at a time apart from navigating right back to the root page @@ -107,6 +107,7 @@ public RoutedViewHost() return Unit.Default; }) + .Concat() .Subscribe() .DisposeWith(disposable); @@ -135,7 +136,6 @@ public RoutedViewHost() popToRootPending = false; return page; }) - .Concat() .Subscribe() .DisposeWith(disposable);