/// <summary> /// Calls <see cref="StackNavigatorExtensions.Navigate{TViewModel}(IStackNavigator, CancellationToken, Func{TViewModel}, bool)"/> on this <see cref="ISectionsNavigator"/>'s active stack navigator. /// </summary> /// <param name="sectionsNavigator">The sections navigator.</param> /// <param name="ct">The cancellation token.</param> /// <param name="viewModelProvider">The method to invoke to instanciate the ViewModel.</param> /// <param name="suppressTransition">Whether to suppress the navigation transition.</param> public static async Task <TViewModel> Navigate <TViewModel>(this ISectionsNavigator sectionsNavigator, CancellationToken ct, Func <TViewModel> viewModelProvider, bool suppressTransition = false) where TViewModel : INavigableViewModel { var navigator = GetActiveStackNavigator(sectionsNavigator); return(await navigator.Navigate(ct, viewModelProvider, suppressTransition)); }
/// <summary> /// Opens a new modal. /// </summary> /// <typeparam name="TViewModel">The type of the view model.</typeparam> /// <param name="sectionsNavigator">The sections navigator.</param> /// <param name="ct"></param> /// <param name="viewModelProvider">The method invoked to instanciate the new ViewModel.</param> /// <param name="priority">The modal's priority.</param> /// <param name="name">The modal's name.</param> /// <returns>The newly created ViewModel instance.</returns> public static async Task <TViewModel> OpenModal <TViewModel>(this ISectionsNavigator sectionsNavigator, CancellationToken ct, Func <TViewModel> viewModelProvider, int?priority = null, string name = null) where TViewModel : INavigableViewModel { var modalNavigator = await sectionsNavigator.OpenModal(ct, SectionsNavigatorRequest.GetOpenModalRequest(StackNavigatorRequest.GetNavigateRequest(viewModelProvider, suppressTransition: true), name, priority)); // Note that modalNavigator can be null if the OpenModal gets cancelled. return((TViewModel)modalNavigator?.GetActiveViewModel()); }
/// <summary> /// Gets an observable of the last page type from currently active section. /// The observable pushes a value whenever a navigation request is processed with the type of the last page ViewModel. /// </summary> /// <returns>An observable of types.</returns> public static IObservable <Type> ObserveActiveSectionLastPageType(this ISectionsNavigator sectionsNavigator) { return(sectionsNavigator .ObserveStateChanged() .Where(args => args.EventArgs.CurrentState.LastRequestState == NavigatorRequestState.Processed) .Select(args => { var state = args.EventArgs.CurrentState; return state.ActiveSection?.State.Stack.LastOrDefault()?.ViewModel.GetType(); }) .StartWith(sectionsNavigator.State.ActiveSection?.State.Stack.LastOrDefault()?.ViewModel.GetType())); }
/// <summary> /// Executes back action depending on top-most frame state. /// </summary> /// <remarks> /// Priorities: /// <list type="number"> /// <item>Navigates back within the modal, if possible.</item> /// <item>Closes the modal, if possible.</item> /// <item>Navigates back within a section.</item> /// </list> /// This is useful when dealing with hardware back buttons. /// </remarks> /// <param name="sectionsNavigator">The sections navigator.</param> /// <param name="ct">The cancellation token.</param> public static async Task NavigateBackOrCloseModal(this ISectionsNavigator sectionsNavigator, CancellationToken ct) { if (sectionsNavigator.State.ActiveModal?.CanNavigateBack() ?? false) { await sectionsNavigator.State.ActiveModal.NavigateBack(ct); } else if (sectionsNavigator.State.ActiveModal != null) { await sectionsNavigator.CloseModal(ct); } else if (sectionsNavigator.State.ActiveSection?.CanNavigateBack() ?? false) { await sectionsNavigator.State.ActiveSection.NavigateBack(ct); } else { throw new InvalidOperationException($"Failed to NavigateBack or CloseModal. The active section '{sectionsNavigator.State.ActiveSection?.Name ?? "null"}' can't currently navigate back and there are no modals to close."); } }
/// <summary> /// Creates a new instance of <see cref="SectionsNavigatorToStackNavigatorAdapter"/> from the specified adaptee. /// </summary> /// <param name="sectionsNavigator">The sections navigator to adapt into a <see cref="IStackNavigator"/>.</param> public SectionsNavigatorToStackNavigatorAdapter(ISectionsNavigator sectionsNavigator) { _sectionsNavigator = sectionsNavigator; }
/// <summary> /// Gets an observable sequence that produces values whenever <see cref="ISectionsNavigator.StateChanged"/> is raised, pushing only the <see cref="SectionsNavigatorEventArgs.CurrentState"/> value. /// </summary> /// <param name="navigator">The sections navigator.</param> /// <returns>An observable sequence of <see cref="SectionsNavigatorState"/>.</returns> public static IObservable <SectionsNavigatorState> ObserveCurrentState(this ISectionsNavigator navigator) { return(navigator .ObserveStateChanged() .Select(pattern => pattern.EventArgs.CurrentState)); }
/// <summary> /// Gets an observable sequence that produces values whenever <see cref="ISectionsNavigator.StateChanged"/> is raised. /// </summary> /// <param name="navigator">The sections navigator.</param> /// <returns>An observable sequence of <see cref="EventPattern{SectionsNavigatorEventArgs}"/>.</returns> public static IObservable <EventPattern <SectionsNavigatorEventArgs> > ObserveStateChanged(this ISectionsNavigator navigator) { return(Observable .FromEventPattern <SectionsNavigatorStateChangedEventHandler, SectionsNavigatorEventArgs>( handler => navigator.StateChanged += handler, handler => navigator.StateChanged -= handler )); }
/// <summary> /// Closes the top-most modal. /// </summary> /// <param name="sectionsNavigator">The sections navigator.</param> /// <param name="ct">The cancellation token.</param> public static async Task CloseModal(this ISectionsNavigator sectionsNavigator, CancellationToken ct) { await sectionsNavigator.CloseModal(ct, SectionsNavigatorRequest.GetCloseModalRequest(modalPriority: null)); }
/// <summary> /// Sets the active section using the provided section name and navigates. /// </summary> /// <typeparam name="TViewModel">The type of the view model.</typeparam> /// <param name="sectionsNavigator">The sections navigator.</param> /// <param name="ct">The cancellation token.</param> /// <param name="sectionName">The name of the section to set as active.</param> /// <param name="viewModelProvider">The method to make the view model instance. It will be invoked only if necessary.</param> /// <param name="returnToRoot">When this is true, the navigator will navigate back to the view model matching the type <typeparamref name="TViewModel"/>.</param> /// <returns>The stack navigator of the active section.</returns> public static async Task <ISectionStackNavigator> SetActiveSection <TViewModel>(this ISectionsNavigator sectionsNavigator, CancellationToken ct, string sectionName, Func <TViewModel> viewModelProvider, bool returnToRoot = false) where TViewModel : INavigableViewModel { if (ct.IsCancellationRequested) { typeof(SectionsNavigatorExtensions).Log().LogWarning($"Canceled 'SetActiveSection' operation to '{typeof(TViewModel).Name}' because of cancellation token."); return(null); } // No cancellation beyond this point. ct = CancellationToken.None; var sectionNavigator = sectionsNavigator.State.Sections[sectionName]; if (sectionNavigator.State.Stack.LastOrDefault() == null) { // Create the default page if there's nothing in the section. await sectionNavigator.Navigate(ct, StackNavigatorRequest.GetNavigateRequest(viewModelProvider, suppressTransition: true)); } else if (returnToRoot && sectionNavigator.State.Stack.Last().ViewModel.GetType() != typeof(TViewModel)) { if (sectionNavigator.State.Stack.Any(e => e.ViewModel.GetType() == typeof(TViewModel))) { // If the stack contains the root page of the section, remove all other entries and navigate back to it. var indexesToRemove = sectionNavigator.State.Stack .Select((entry, index) => (entry, index)) .Where(t => t.entry.ViewModel.GetType() != typeof(TViewModel) && t.index < sectionNavigator.State.Stack.Count - 1) .Select(t => t.index) .ToList(); await sectionNavigator.RemoveEntries(ct, indexesToRemove); await sectionNavigator.NavigateBack(ct); } else { // If the section root page isn't in the stack, clear everything and navigate to it. await sectionNavigator.Navigate(ct, StackNavigatorRequest.GetNavigateRequest(viewModelProvider, suppressTransition: true, clearBackStack: true)); } } return(await sectionsNavigator.SetActiveSection(ct, SectionsNavigatorRequest.GetSetActiveSectionRequest(sectionName))); }
/// <summary> /// Sets the active section using the provided section name. /// </summary> /// <param name="sectionsNavigator">The sections navigator.</param> /// <param name="ct">The cancellation token.</param> /// <param name="sectionName">The name of the section to set as active.</param> /// <returns>The stack navigator of the active section.</returns> public static Task <ISectionStackNavigator> SetActiveSection(this ISectionsNavigator sectionsNavigator, CancellationToken ct, string sectionName) { return(sectionsNavigator.SetActiveSection(ct, SectionsNavigatorRequest.GetSetActiveSectionRequest(sectionName))); }
/// <summary> /// Calls <see cref="StackNavigatorExtensions.GetActiveViewModel(IStackNavigator)"/> on this <see cref="ISectionsNavigator"/>'s active stack navigator. /// </summary> /// <param name="sectionsNavigator">The sections navigator.</param> public static INavigableViewModel GetActiveViewModel(this ISectionsNavigator sectionsNavigator) { var navigator = GetActiveStackNavigator(sectionsNavigator); return(navigator.GetActiveViewModel()); }
/// <summary> /// Calls <see cref="StackNavigatorExtensions.RemovePrevious(IStackNavigator, CancellationToken)"/> on this <see cref="ISectionsNavigator"/>'s active stack navigator. /// </summary> /// <param name="sectionsNavigator">The sections navigator.</param> /// <param name="ct">The cancellation token.</param> public static async Task RemovePrevious(this ISectionsNavigator sectionsNavigator, CancellationToken ct) { var navigator = GetActiveStackNavigator(sectionsNavigator); await navigator.RemovePrevious(ct); }
/// <summary> /// Calls <see cref="IStackNavigator.NavigateBack(CancellationToken)"/> on this <see cref="ISectionsNavigator"/>'s active stack navigator. /// </summary> /// <param name="sectionsNavigator">The sections navigator.</param> /// <param name="ct">The cancellation token.</param> public static async Task NavigateBack(this ISectionsNavigator sectionsNavigator, CancellationToken ct) { var navigator = GetActiveStackNavigator(sectionsNavigator); await navigator.NavigateBack(ct); }
/// <summary> /// Gets the active <see cref="IStackNavigator"/> of this <see cref="ISectionsNavigator"/>. /// </summary> /// <remarks> /// The result can be null if no navigator is active. /// </remarks> /// <param name="sectionsNavigator">The sections navigator.</param> public static IStackNavigator GetActiveStackNavigator(this ISectionsNavigator sectionsNavigator) { return(sectionsNavigator.State.GetActiveStackNavigator()); }
/// <summary> /// Gets whether the <see cref="ISectionsNavigator"/> can navigate back or close a modal. /// </summary> /// <remarks> /// This is useful when dealing with hardware back buttons. /// </remarks> /// <param name="sectionsNavigator">The sections navigator.</param> /// <returns>True if the navigator can navigate back or close a modal. False otherwise.</returns> public static bool CanNavigateBackOrCloseModal(this ISectionsNavigator sectionsNavigator) { return((sectionsNavigator.State.ActiveSection?.CanNavigateBack() ?? false) || sectionsNavigator.State.Modals.Any()); }