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 78 : 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 14 : GetPathFromNameResult getPathFromName(
70 : String nameToMatch, {
71 : required Map<String, String> pathParameters,
72 : required GetNewParentPathResult parentPathResult,
73 : required Map<String, String> remainingPathParameters,
74 : }) {
75 14 : final childNameResults = <GetPathFromNameResult>[];
76 :
77 : // Check if any subroute matches the name
78 28 : for (var vRouteElement in _subroutes) {
79 14 : childNameResults.add(
80 14 : vRouteElement.getPathFromName(
81 : nameToMatch,
82 : pathParameters: pathParameters,
83 : parentPathResult: parentPathResult,
84 : remainingPathParameters: remainingPathParameters,
85 : ),
86 : );
87 28 : if (childNameResults.last is ValidNameResult) {
88 12 : 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 7 : final pathParamsNameErrors = PathParamsErrorsNameResult(
96 : name: nameToMatch,
97 7 : values: childNameResults.fold<List<PathParamsError>>(
98 7 : <PathParamsError>[],
99 7 : (previousValue, element) {
100 7 : return previousValue +
101 19 : ((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 14 : if (pathParamsNameErrors.values.isNotEmpty) {
109 : return pathParamsNameErrors;
110 : }
111 :
112 : // Else try to find a NullPathError
113 7 : if (childNameResults.indexWhere(
114 21 : (childNameResult) => childNameResult is NullPathErrorNameResult) !=
115 7 : -1) {
116 2 : return NullPathErrorNameResult(name: nameToMatch);
117 : }
118 :
119 : // Else return a NotFoundError
120 7 : 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 FoundPopResult(
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 : missingPathParams:
148 : (parentPathResult as PathParamsErrorNewParentPath)
149 3 : .pathParameters,
150 : ),
151 : ],
152 : );
153 : }
154 : }
155 :
156 29 : final popErrorResults = <GetPathFromPopResult>[];
157 :
158 : // Try to pop from the stackedRoutes
159 58 : for (var vRouteElement in _subroutes) {
160 29 : GetPathFromPopResult childPopResult = vRouteElement.getPathFromPop(
161 : elementToPop,
162 : pathParameters: pathParameters,
163 : parentPathResult: parentPathResult,
164 : );
165 29 : if (childPopResult is FoundPopResult) {
166 46 : if (popWithSubRoute && childPopResult.didPop) {
167 : // if the nestedRoute popped, we should pop too
168 12 : if (parentPathResult is ValidParentPathResult) {
169 12 : return FoundPopResult(
170 12 : path: parentPathResult.path,
171 : didPop: true,
172 : poppedVRouteElements:
173 36 : <VRouteElement>[this] + childPopResult.poppedVRouteElements,
174 : );
175 : } else {
176 0 : assert(parentPathResult is PathParamsErrorNewParentPath);
177 0 : popErrorResults.add(
178 0 : PathParamsPopErrors(
179 0 : values: [
180 0 : MissingPathParamsError(
181 0 : pathParams: pathParameters.keys.toList(),
182 : missingPathParams:
183 : (parentPathResult as PathParamsErrorNewParentPath)
184 0 : .pathParameters,
185 : ),
186 : ],
187 : ),
188 : );
189 : }
190 : } else {
191 25 : return FoundPopResult(
192 25 : path: childPopResult.path,
193 : didPop: false,
194 25 : poppedVRouteElements: childPopResult.poppedVRouteElements,
195 : );
196 : }
197 : } else {
198 10 : popErrorResults.add(childPopResult);
199 : }
200 : }
201 :
202 : // If we don't have any valid result
203 :
204 : // If some stackedRoute returned PathParamsPopError, aggregate them
205 10 : final pathParamsPopErrors = PathParamsPopErrors(
206 10 : values: popErrorResults.fold<List<MissingPathParamsError>>(
207 10 : <MissingPathParamsError>[],
208 10 : (previousValue, element) {
209 10 : return previousValue +
210 24 : ((element is PathParamsPopErrors) ? element.values : []);
211 : },
212 : ),
213 : );
214 :
215 : // If there was any PathParamsPopError, we have some pathParamsPopErrors.values
216 : // and therefore should return this
217 20 : if (pathParamsPopErrors.values.isNotEmpty) {
218 : return pathParamsPopErrors;
219 : }
220 :
221 : // If none of the stackedRoutes popped, this did not pop, and there was no path parameters issue, return not found
222 : // This should never happen
223 6 : return ErrorNotFoundGetPathFromPopResult();
224 : }
225 : }
|