Line data Source code
1 : part of '../main.dart';
2 :
3 : /// If the VRouteElement has a single [VRouteElement] as a child, it should instantiate this class
4 : ///
5 : /// What is does is implementing [buildRoute], [buildPathFromName] and [buildPathFromPop] methods
6 : mixin VRouteElementSingleSubRoute on VRouteElement {
7 : /// The list of possible routes
8 : /// Only one will be chosen to be displayed
9 : List<VRouteElement> buildRoutes();
10 :
11 80 : late final List<VRouteElement> _subroutes = buildRoutes();
12 :
13 : /// Describes whether we should we pop this [VRouteElement]
14 : /// if the [VRouteElement] of the subroute pops
15 19 : bool get popWithSubRoute => true;
16 :
17 : /// [buildRoute] must return [VRoute] if it constitute (which its subroutes or not) a valid
18 : /// route given the input parameters
19 : /// [VRoute] should describe this valid route
20 : ///
21 : ///
22 : /// [vPathRequestData] contains all the information about the original request coming
23 : /// from [VRouter]
24 : /// It should not be changed and should be given as-is to its subroutes
25 : ///
26 : /// [parentRemainingPath] is the part on which to base any local path
27 : /// WARNING: [parentRemainingPath] is null if the parent did not match the path
28 : /// in which case only absolute path should be tested.
29 : ///
30 : /// [parentPathParameters] are the path parameters of every [VRouteElement] above this
31 : /// one in the route
32 : ///
33 : /// [buildRoute] basically just checks for a match in stackedRoutes and if any
34 : /// adds this [VRouteElement] to the [VRoute]
35 : ///
36 : /// For more info on buildRoute, see [VRouteElement.buildRoute]
37 25 : VRoute? buildRoute(
38 : VPathRequestData vPathRequestData, {
39 : required VPathMatch parentVPathMatch,
40 : }) {
41 : VRoute? childVRoute;
42 50 : for (var vRouteElement in _subroutes) {
43 25 : childVRoute = vRouteElement.buildRoute(
44 : vPathRequestData,
45 : parentVPathMatch: parentVPathMatch,
46 : );
47 : if (childVRoute != null) {
48 25 : return VRoute(
49 25 : vRouteElementNode: VRouteElementNode(
50 : this,
51 : localPath: null,
52 25 : stackedVRouteElementNode: childVRoute.vRouteElementNode,
53 : ),
54 25 : pages: childVRoute.pages,
55 25 : pathParameters: childVRoute.pathParameters,
56 75 : vRouteElements: <VRouteElement>[this] + childVRoute.vRouteElements,
57 : );
58 : }
59 : }
60 :
61 : return null;
62 : }
63 :
64 : /// This function takes a name and tries to find the path corresponding to
65 : /// the route matching this name
66 : ///
67 : /// The deeper nested the route the better
68 : /// The given path parameters have to include at least every path parameters of the final path
69 21 : GetPathFromNameResult getPathFromName(
70 : String nameToMatch, {
71 : required Map<String, String> pathParameters,
72 : required GetNewParentPathResult parentPathResult,
73 : required Map<String, String> remainingPathParameters,
74 : }) {
75 21 : final childNameResults = <GetPathFromNameResult>[];
76 :
77 : // Check if any subroute matches the name
78 41 : for (var vRouteElement in _subroutes) {
79 20 : childNameResults.add(
80 20 : vRouteElement.getPathFromName(
81 : nameToMatch,
82 : pathParameters: pathParameters,
83 : parentPathResult: parentPathResult,
84 : remainingPathParameters: remainingPathParameters,
85 : ),
86 : );
87 40 : if (childNameResults.last is ValidNameResult) {
88 18 : return childNameResults.last;
89 : }
90 : }
91 :
92 : // If we don't have any valid result
93 :
94 : // If some stackedRoute returned PathParamsPopError, aggregate them
95 16 : final pathParamsNameErrors = PathParamsErrorsNameResult(
96 : name: nameToMatch,
97 16 : values: childNameResults.fold<List<PathParamsError>>(
98 16 : <PathParamsError>[],
99 10 : (previousValue, element) {
100 10 : return previousValue +
101 25 : ((element is PathParamsErrorsNameResult) ? element.values : []);
102 : },
103 : ),
104 : );
105 :
106 : // If there was any PathParamsPopError, we have some pathParamsPopErrors.values
107 : // and therefore should return this
108 32 : if (pathParamsNameErrors.values.isNotEmpty) {
109 : return pathParamsNameErrors;
110 : }
111 :
112 : // Else try to find a NullPathError
113 : if (childNameResults
114 52 : .indexWhere((childNameResult) => childNameResult is NullPathErrorNameResult) !=
115 16 : -1) {
116 2 : return NullPathErrorNameResult(name: nameToMatch);
117 : }
118 :
119 : // Else return a NotFoundError
120 16 : return NotFoundErrorNameResult(name: nameToMatch);
121 : }
122 :
123 : /// [GetPathFromPopResult.didPop] is true if this [VRouteElement] popped
124 : /// [GetPathFromPopResult.extendedPath] is null if this path can't be the right one according to
125 : /// the path parameters
126 : /// [GetPathFromPopResult] is null when this [VRouteElement] does not pop AND none of
127 : /// its stackedRoutes popped
128 29 : GetPathFromPopResult getPathFromPop(
129 : VRouteElement elementToPop, {
130 : required Map<String, String> pathParameters,
131 : required GetNewParentPathResult parentPathResult,
132 : }) {
133 : // If vRouteElement is this, then this is the element to pop so we return null
134 29 : if (elementToPop == this) {
135 9 : if (parentPathResult is ValidParentPathResult) {
136 9 : return ValidPopResult(
137 9 : path: parentPathResult.path,
138 : didPop: true,
139 9 : poppedVRouteElements: [this],
140 : );
141 : } else {
142 3 : assert(parentPathResult is PathParamsErrorNewParentPath);
143 3 : return PathParamsPopErrors(
144 3 : values: [
145 3 : MissingPathParamsError(
146 6 : pathParams: pathParameters.keys.toList(),
147 3 : missingPathParams: (parentPathResult as PathParamsErrorNewParentPath).pathParameters,
148 : ),
149 : ],
150 : );
151 : }
152 : }
153 :
154 29 : final popErrorResults = <GetPathFromPopResult>[];
155 :
156 : // Try to pop from the stackedRoutes
157 58 : for (var vRouteElement in _subroutes) {
158 29 : GetPathFromPopResult childPopResult = vRouteElement.getPathFromPop(
159 : elementToPop,
160 : pathParameters: pathParameters,
161 : parentPathResult: parentPathResult,
162 : );
163 29 : if (childPopResult is ValidPopResult) {
164 46 : if (popWithSubRoute && childPopResult.didPop) {
165 : // if the nestedRoute popped, we should pop too
166 12 : if (parentPathResult is ValidParentPathResult) {
167 12 : return ValidPopResult(
168 12 : path: parentPathResult.path,
169 : didPop: true,
170 : poppedVRouteElements:
171 36 : <VRouteElement>[this] + childPopResult.poppedVRouteElements,
172 : );
173 : } else {
174 0 : assert(parentPathResult is PathParamsErrorNewParentPath);
175 0 : popErrorResults.add(
176 0 : PathParamsPopErrors(
177 0 : values: [
178 0 : MissingPathParamsError(
179 0 : pathParams: pathParameters.keys.toList(),
180 : missingPathParams:
181 0 : (parentPathResult as PathParamsErrorNewParentPath).pathParameters,
182 : ),
183 : ],
184 : ),
185 : );
186 : }
187 : } else {
188 25 : return ValidPopResult(
189 25 : path: childPopResult.path,
190 : didPop: false,
191 25 : poppedVRouteElements: childPopResult.poppedVRouteElements,
192 : );
193 : }
194 : } else {
195 10 : popErrorResults.add(childPopResult);
196 : }
197 : }
198 :
199 : // If we don't have any valid result
200 :
201 : // If some stackedRoute returned PathParamsPopError, aggregate them
202 10 : final pathParamsPopErrors = PathParamsPopErrors(
203 10 : values: popErrorResults.fold<List<MissingPathParamsError>>(
204 10 : <MissingPathParamsError>[],
205 10 : (previousValue, element) {
206 34 : return previousValue + ((element is PathParamsPopErrors) ? element.values : []);
207 : },
208 : ),
209 : );
210 :
211 : // If there was any PathParamsPopError, we have some pathParamsPopErrors.values
212 : // and therefore should return this
213 20 : if (pathParamsPopErrors.values.isNotEmpty) {
214 : return pathParamsPopErrors;
215 : }
216 :
217 : // If none of the stackedRoutes popped, this did not pop, and there was no path parameters issue, return not found
218 : // This should never happen
219 6 : return ErrorNotFoundGetPathFromPopResult();
220 : }
221 : }
|