Last active
May 19, 2018 04:12
-
-
Save PureWeen/96057f517fc51051ff6f321ddf61c281 to your computer and use it in GitHub Desktop.
RxUI Routing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface IParScreen | |
{ | |
/// <summary> | |
/// The Router associated with this Screen. | |
/// </summary> | |
IParRoutingState Router { get; } | |
} | |
public interface IParRoutingState | |
{ | |
IParRoutableViewModel ModalNavigationViewModel { get; } | |
/// <summary> | |
/// Navigates back to the previous element in the stack. | |
/// </summary> | |
ReactiveCommand<Unit, Unit> NavigateBack { get; } | |
/// <summary> | |
/// Navigates to the a new element in the stack - the Execute parameter | |
/// must be a ViewModel that implements IParRoutableViewModel. | |
/// </summary> | |
ReactiveCommand<IParRoutableViewModel, IParRoutableViewModel> Navigate { get; } | |
/// <summary> | |
/// Navigates to a new element and resets the navigation stack (i.e. the | |
/// new ViewModel will now be the only element in the stack) - the | |
/// Execute parameter must be a ViewModel that implements | |
/// IParRoutableViewModel. | |
/// </summary> | |
ReactiveCommand<IParRoutableViewModel[], IParRoutableViewModel> NavigateAndReset { get; } | |
ReactiveCommand<IParRoutableViewModel, IParRoutableViewModel> NavigateModal { get; } | |
// IObservable<IParRoutableViewModel> CurrentViewModel { get; } | |
IReadOnlyList<IParRoutableViewModel> NavigationStack { get; } | |
} | |
[DataContract] | |
public class ParRoutingState : ReactiveObject, IParRoutingState | |
{ | |
//[DataMember] ReactiveList<IParRoutableViewModel> _NavigationStack; | |
[IgnoreDataMember] IParRoutableViewModel _ModalNavigationViewModel; | |
/* | |
/// <summary> | |
/// Represents the current navigation stack, the last element in the | |
/// collection being the currently visible ViewModel. | |
/// </summary> | |
[IgnoreDataMember] | |
public ReactiveList<IParRoutableViewModel> NavigationStack { | |
get => _NavigationStack; | |
protected set => _NavigationStack = value; | |
} | |
*/ | |
[IgnoreDataMember] | |
public IReadOnlyList<IParRoutableViewModel> NavigationStack => | |
NavigationForm | |
.Navigation | |
.NavigationStack | |
.Select(x => (IParRoutableViewModel)x.BindingContext) | |
.ToImmutableList(); | |
[IgnoreDataMember] | |
public IParRoutableViewModel ModalNavigationViewModel => | |
NavigationForm | |
.Navigation | |
.ModalStack | |
.Select(x => (IParRoutableViewModel)x.BindingContext) | |
.FirstOrDefault(); | |
[IgnoreDataMember] private IScheduler _scheduler; | |
/// <summary> | |
/// The scheduler used for commands. Defaults to <c>SchedulerFactory.Dispatcher</c>. | |
/// </summary> | |
[IgnoreDataMember] | |
public IScheduler Scheduler { | |
get => this._scheduler; | |
set { | |
if (this._scheduler == value) return; | |
this._scheduler = value; | |
this.setupRx(); | |
} | |
} | |
/// <summary> | |
/// Navigates back to the previous element in the stack. | |
/// </summary> | |
[IgnoreDataMember] | |
public ReactiveCommand<Unit, Unit> NavigateBack { get; protected set; } | |
/// <summary> | |
/// Navigates to the a new element in the stack - the Execute parameter | |
/// must be a ViewModel that implements IParRoutableViewModel. | |
/// </summary> | |
[IgnoreDataMember] | |
public ReactiveCommand<IParRoutableViewModel, IParRoutableViewModel> Navigate { get; protected set; } | |
/// <summary> | |
/// Navigates to a new element and resets the navigation stack (i.e. the | |
/// new ViewModel will now be the only element in the stack) - the | |
/// Execute parameter must be a ViewModel that implements | |
/// IParRoutableViewModel. | |
/// </summary> | |
[IgnoreDataMember] | |
public ReactiveCommand<IParRoutableViewModel[], IParRoutableViewModel> NavigateAndReset { get; protected set; } | |
[IgnoreDataMember] | |
public ReactiveCommand<IParRoutableViewModel, IParRoutableViewModel> NavigateModal { get; protected set; } | |
[OnDeserialized] | |
void setupRx(StreamingContext sc) { setupRx(); } | |
private MainView NavigationForm => MainView.Instance; | |
void setupRx() | |
{ | |
_scheduler = this._scheduler ?? Locator.Current.GetService<ISchedulerFactory>().Dispatcher; | |
NavigateBack = | |
ReactiveCommand.CreateFromTask(async () => | |
{ | |
if (ModalNavigationViewModel != null) | |
await NavigationForm.Navigation.PopModalAsync(); | |
await NavigationForm.PopAsync(); | |
}, | |
outputScheduler: this.Scheduler); | |
Navigate = ReactiveCommand.CreateFromTask<IParRoutableViewModel, IParRoutableViewModel>(async x => { | |
var page = NavigationForm.PageForViewModel(x); | |
await NavigationForm.PushAsync(page); | |
return x; | |
}, | |
outputScheduler: this.Scheduler); | |
NavigateAndReset = ReactiveCommand.CreateFromTask<IParRoutableViewModel[], IParRoutableViewModel>( | |
async newStack => { | |
var pages = | |
newStack | |
.Select(x=> NavigationForm.PageForViewModel(x)) | |
.ToArray(); | |
if (ModalNavigationViewModel != null) | |
{ | |
await NavigationForm.Navigation.PopModalAsync(); | |
} | |
NavigationForm.Navigation.InsertPageBefore(pages.Last(), | |
NavigationForm.Navigation.NavigationStack[0]); | |
await NavigationForm.Navigation.PopToRootAsync(); | |
for (int i = 0; i < pages.Length - 1; i++) | |
{ | |
NavigationForm.Navigation.InsertPageBefore(pages[i], pages.Last()); | |
} | |
return newStack.Last(); | |
}, | |
outputScheduler: this.Scheduler); | |
NavigateModal = ReactiveCommand.CreateFromTask<IParRoutableViewModel, IParRoutableViewModel>( | |
async x => { | |
if (x == null) | |
{ | |
if (ModalNavigationViewModel == null) | |
return null; | |
var modalPage = await NavigationForm.Navigation | |
.PopModalAsync(); | |
return (IParRoutableViewModel) modalPage.BindingContext; | |
} | |
var page = NavigationForm.PageForViewModel(x); | |
await NavigationForm.Navigation.PushModalAsync(page); | |
return x; | |
}, | |
outputScheduler: this.Scheduler); | |
NavigateBack | |
.ThrownExceptions | |
.Subscribe((Exception exc) => | |
{ | |
this.LogException("NavigateBack", exc); | |
}); | |
Navigate | |
.ThrownExceptions | |
.Subscribe((Exception exc) => | |
{ | |
this.LogException("Navigate", exc); | |
}); | |
NavigateAndReset | |
.ThrownExceptions | |
.Subscribe((Exception exc) => | |
{ | |
this.LogException("NavigateAndReset", exc); | |
}); | |
NavigateModal | |
.ThrownExceptions | |
.Subscribe((Exception exc) => | |
{ | |
this.LogException("NavigateModal", exc); | |
}); | |
} | |
[IgnoreDataMember] | |
static ParRoutingState _instance; | |
public ParRoutingState() | |
{ | |
// _NavigationStack = new ReactiveList<IParRoutableViewModel>(); | |
setupRx(); | |
if (_instance != null) | |
throw new Exception("Main Page View Model already instantiated"); | |
_instance = this; | |
} | |
} | |
public interface IParRoutableViewModel : | |
IReactiveObject, | |
INotifyPropertyChanged, | |
IEnableLogger | |
{ | |
/// <summary> | |
/// A string token representing the current ViewModel, such as 'login' or 'user' | |
/// </summary> | |
string UrlPathSegment { get; } | |
/// <summary> | |
/// The IScreen that this ViewModel is currently being shown in. This | |
/// is usually passed into the ViewModel in the Constructor and saved | |
/// as a ReadOnly Property. | |
/// </summary> | |
IParScreen HostScreen { get; } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment