public async Task Show(ScreenDefinition <TViewModel> screen, string parameter = null, ViewModelCreator <TViewModel> viewModelCreator = null) { var screenInstance = new ScreenInstance <TViewModel>(screen, parameter, viewModelCreator); var result = _navigationGraph.FindBestStack(_navigationStack, screenInstance).ToList(); System.Diagnostics.Debug.WriteLine($"Navigating to {screen.RelativeRoute}"); System.Diagnostics.Debug.WriteLine($"\tUse stack: {string.Join(", ", result.Select(x => x.ToString()))}"); await UpdateNavigationStack(result); }
public IList <ScreenInstance <TViewModel> > FindBestStack(List <ScreenInstance <TViewModel> > currentStack, ScreenInstance <TViewModel> screen) { if (!_nodePerScreenIds.TryGetValue(screen.Definition.Id, out var destinationNode)) { throw new InvalidOperationException("This screen has not been registered"); } var destinationScreen = new ScreenNode <TViewModel>(destinationNode, screen); var currentScreenStack = currentStack.ConvertAll(x => new ScreenNode <TViewModel>(_nodePerScreenIds[x.Definition.Id], x)); // If nothing is on navigation stack, // we need to find a path in the tree to get to the screen, a simple BFS will works, just need to take the multi entry points into account // Else // We need to find the shortest path to go from the current screen to the other // correct way links must have priority (but if a down links is taken, only down links can taken after that) // can only take up links that are in the navigation stack return(FindPathToScreenWithBFS(currentScreenStack, destinationScreen).ConvertAll(x => x.ScreenInstance)); }
public IList <ScreenInstance <TViewModel> > FindWithRoute(string route) { if (string.IsNullOrWhiteSpace(route)) { throw new InvalidOperationException("Route must not be empty or null"); } //TODO: can set up a cache with found route in order to help the system string[] routeParts = route.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); (List <ScreenInstance <TViewModel> > resultNodes, _, bool resultAmbiguity) = FindBest(_entryPoints, routeParts, 0); if (resultAmbiguity) { throw new InvalidOperationException($"Ambiguous route {route}, specify parameter name if needed"); } return(resultNodes); (List <ScreenInstance <TViewModel> > nodes, int score, bool ambiguity) FindBest(List <Node <TViewModel> > nodes, string[] parts, int index) { (List <ScreenInstance <TViewModel> > nodes, int score)result = (null, 0); bool ambiguity = false; for (int i = 0; i < nodes.Count; i++) { (List <ScreenInstance <TViewModel> > nodes, int score, bool ambiguity)proposal = FindForRoute(nodes[i], parts, index); if (proposal.nodes is null || result.score > proposal.score) { continue; } if (result.score == proposal.score && result.score > 0) { ambiguity = true; continue; } ambiguity = proposal.ambiguity; result = (proposal.nodes, proposal.score); } return(result.nodes, result.score, ambiguity); } (List <ScreenInstance <TViewModel> > nodes, int score, bool ambiguity) FindForRoute(Node <TViewModel> currentNode, string[] parts, int index) { (int currentNodeScore, string routeParameter) = RoutePartScore(currentNode, parts[index]); if (currentNodeScore == 0) { return(null, 0, false); } var current = new ScreenInstance <TViewModel>(currentNode.Screen, routeParameter, null); if (index + 1 == parts.Length) { return(new List <ScreenInstance <TViewModel> > { current }, currentNodeScore, false); } (List <ScreenInstance <TViewModel> > nodes, int score, bool ambiguity)result = FindBest(currentNode.NextNodes, parts, index + 1); result.nodes.Insert(0, current); return(result); } (int score, string parameter) RoutePartScore(Node <TViewModel> node, string part) { if (node.Screen.IsParameterRoute) { if (part.StartsWith($"{node.Screen.ParameterName}:")) { // if parameter name match, the score is higher than general scoring return(3, part.Substring(node.Screen.ParameterName.Length + 1)); } // this is a parameterized route so it can match but with a lower score return(1, part); } if (part == node.Screen.RelativeRoute) { return(4, null); } return(0, null); } }
protected bool Equals(ScreenInstance <TViewModel> other) { return(Equals(Definition.Id, other.Definition.Id) && string.Equals(Parameter, other.Parameter)); }