/// <summary> /// Gets whether the navigator from a <see cref="SectionsNavigatorRequestType.ReportSectionStateChanged"/> request is active. /// </summary> /// <param name="sectionsNavigatorState"></param> /// <param name="stackNavigator">The stack navigator that caused the <see cref="SectionsNavigatorRequestType.ReportSectionStateChanged"/> request if it's active. Null otherwhise.</param> /// <returns>Returns true when the <see cref="IStackNavigator"/> that caused the current <see cref="SectionsNavigatorRequestType.ReportSectionStateChanged"/> is currently active. False otherwise.</returns> public static bool IsStackNavigatorFromSectionsRequestActive(this SectionsNavigatorState sectionsNavigatorState, out IStackNavigator stackNavigator) { var sectionsNavigatorRequest = sectionsNavigatorState.LastRequest; var modalName = sectionsNavigatorRequest.ModalName; if (modalName != null) { stackNavigator = sectionsNavigatorState.Modals.FirstOrDefault(m => m.Name == modalName); if (stackNavigator == null) { return(false); } else { return(stackNavigator == sectionsNavigatorState.ActiveModal); } } else { var sectionName = sectionsNavigatorRequest.SectionName; stackNavigator = sectionsNavigatorState.Sections[sectionName]; // Don't consider the section active if a Modal is active. return(stackNavigator == sectionsNavigatorState.ActiveSection && sectionsNavigatorState.ActiveModal == null); } }
public SectionsNavigatorState( SectionsNavigatorState state, NavigatorRequestState lastRequestState, SectionsNavigatorRequest lastRequest) : this(state.Sections, state.ActiveSection, state.Modals, lastRequestState, lastRequest) { }
/// <summary> /// Creates a new instance of <see cref="SectionsNavigatorBase"/>. /// </summary> /// <param name="defaultSections">The sections reachable by <see cref="SetActiveSection(CancellationToken, SectionsNavigatorRequest)"/> mapped by their name.</param> public SectionsNavigatorBase(IReadOnlyDictionary <string, ISectionStackNavigator> defaultSections) { _logger = GetLogger(); State = new SectionsNavigatorState( sections: defaultSections ?? new Dictionary <string, ISectionStackNavigator>(), activeSection: null, modals: new List <IModalStackNavigator>(), lastRequestState: NavigatorRequestState.Processed, lastRequest: null); foreach (var section in defaultSections.Values) { section.StateChanged += OnSectionStateChanged; } }
public SectionsNavigatorEventArgs(SectionsNavigatorState previousState, SectionsNavigatorState currentState) { PreviousState = previousState; CurrentState = currentState; }
/// <summary> /// Gets the type of the next page ViewModel assuming the current <paramref name="sectionsNavigatorState"/> has a <see cref="NavigatorRequestState.Processing"/> request. /// Since the next page doesn't exist at that time, it's calculated using the request's type, parameters, and the current state. /// </summary> public static Type GetNextViewModelType(this SectionsNavigatorState sectionsNavigatorState) { if (sectionsNavigatorState.LastRequestState != NavigatorRequestState.Processing) { throw new InvalidOperationException("Can't get the next ViewModel on a processed request."); } var currentVM = sectionsNavigatorState.GetActiveStackNavigator()?.State.Stack.LastOrDefault()?.Request.ViewModelType; var sectionsRequest = sectionsNavigatorState.LastRequest; switch (sectionsRequest.RequestType) { case SectionsNavigatorRequestType.ReportSectionStateChanged: return(GetNextFromReportSectionStateChanged()); case SectionsNavigatorRequestType.OpenModal: return(GetNextFromOpenModal()); case SectionsNavigatorRequestType.CloseModal: return(GetNextFromCloseModal()); case SectionsNavigatorRequestType.SetActiveSection: return(GetNextFromSetActiveSection()); default: throw new NotSupportedException($"The request type {sectionsRequest.RequestType} is not supported."); } Type GetNextFromReportSectionStateChanged() { if (sectionsNavigatorState.IsStackNavigatorFromSectionsRequestActive(out var stackNavigator)) { // If the current request is on the active frame, we know that the request will change the next page. var stackRequest = stackNavigator.State.LastRequest; switch (stackRequest.RequestType) { case StackNavigatorRequestType.NavigateForward: return(stackRequest.ViewModelType); case StackNavigatorRequestType.NavigateBack: return(stackNavigator.State.Stack[stackNavigator.State.Stack.Count - 2].Request.ViewModelType); case StackNavigatorRequestType.Clear: return(null); case StackNavigatorRequestType.RemoveEntry: // RemoveEntry is only used to remove previous pages (not the current page), so it doesn't change the current VM. return(currentVM); default: throw new NotSupportedException($"The request type {stackRequest.RequestType} is not supported."); } } else { // If the current request isn't on the active frame, then it's done in the background and doesn't affect the active page return(currentVM); } } Type GetNextFromOpenModal() { if (sectionsRequest.ModalPriority.HasValue) { // If the priority is specified, the new modal could be opened behind an existing one. if ((sectionsNavigatorState.ActiveModal?.Priority ?? 0) >= sectionsRequest.ModalPriority.Value) { // If the priority of the new modal is above the active one's, the new modal will be the active one. return(sectionsRequest.NewModalStackNavigationRequest.ViewModelType); } else { // If not, the new modal opens in background. return(currentVM); } } else { // If the priority isn't specified, the new modal will be the active one. return(sectionsRequest.NewModalStackNavigationRequest.ViewModelType); } } Type GetNextFromCloseModal() { var closingModal = sectionsNavigatorState.Modals.FirstOrDefault(m => m.Name == sectionsRequest.ModalName || m.Priority == sectionsRequest.ModalPriority) ?? sectionsNavigatorState.ActiveModal; if (closingModal == sectionsNavigatorState.ActiveModal) { // If the active modal is closing, the next stack is either another modal or a section. var modalUnderTheClosingOne = sectionsNavigatorState.Modals.FirstOrDefault(m => m.Priority < closingModal.Priority); if (modalUnderTheClosingOne == null) { return(sectionsNavigatorState.ActiveSection.State.Stack.LastOrDefault()?.Request.ViewModelType); } else { return(modalUnderTheClosingOne.State.Stack.LastOrDefault()?.Request.ViewModelType); } } else { // If not, the modal closes in background and the next ViewModel doesn't change. return(currentVM); } } Type GetNextFromSetActiveSection() { if (sectionsNavigatorState.Modals.Any()) { // If there are modals, changing sections doesn't change the active VM. return(currentVM); } else { if (sectionsNavigatorState.Sections.TryGetValue(sectionsRequest.SectionName, out var sectionNavigator)) { // If there is a section associated to the request, return that section's current VM. return(sectionNavigator.State.Stack.LastOrDefault()?.Request.ViewModelType); } else { // If not, the request will fail. We fallback to the current VM. return(currentVM); } } } }
/// <summary> /// Gets whether there is an active ViewModel in a <see cref="SectionsNavigatorState"/>. /// </summary> /// <param name="sectionsNavigatorState"></param> /// <returns>True if there is an active ViewModel. False otherwise.</returns> public static bool HasActiveViewModel(this SectionsNavigatorState sectionsNavigatorState) { return((sectionsNavigatorState.ActiveModal?.State.Stack.Any() ?? false) || (sectionsNavigatorState.ActiveSection?.State.Stack.Any() ?? false)); }
/// <summary> /// Gets the ViewModel type of the last item of the active <see cref="IStackNavigator"/> of this <see cref="SectionsNavigatorState"/>. /// </summary> public static Type GetLastViewModelType(this SectionsNavigatorState state) { return(state.GetActiveStackNavigator()?.State.Stack.LastOrDefault()?.ViewModel.GetType()); }
/// <summary> /// Gets the active <see cref="IStackNavigator"/> of this <see cref="SectionsNavigatorState"/>. /// The result can be null is no navigator is active. /// </summary> public static IStackNavigator GetActiveStackNavigator(this SectionsNavigatorState sectionsNavigatorState) { return((IStackNavigator)sectionsNavigatorState.ActiveModal ?? sectionsNavigatorState.ActiveSection); }