Пример #1
0
        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);
        }
Пример #2
0
        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));
        }
Пример #3
0
        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);
            }
        }
Пример #4
0
 protected bool Equals(ScreenInstance <TViewModel> other)
 {
     return(Equals(Definition.Id, other.Definition.Id) &&
            string.Equals(Parameter, other.Parameter));
 }