Skip to content

Instantly share code, notes, and snippets.

@PureWeen
Last active May 19, 2018 04:12
Show Gist options
  • Save PureWeen/96057f517fc51051ff6f321ddf61c281 to your computer and use it in GitHub Desktop.
Save PureWeen/96057f517fc51051ff6f321ddf61c281 to your computer and use it in GitHub Desktop.
RxUI Routing
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