|
1 | | -import type { RoomType, RoomRouteData } from '@rocket.chat/core-typings'; |
2 | 1 | import { RouterContext } from '@rocket.chat/ui-contexts'; |
3 | | -import type { |
4 | | - RouterContextValue, |
5 | | - RouteName, |
6 | | - LocationPathname, |
7 | | - RouteParameters, |
8 | | - SearchParameters, |
9 | | - To, |
10 | | - RouteObject, |
11 | | - LocationSearch, |
12 | | -} from '@rocket.chat/ui-contexts'; |
13 | | -import { FlowRouter } from 'meteor/ostrio:flow-router-extra'; |
14 | | -import { Tracker } from 'meteor/tracker'; |
| 2 | +import type { RouterContextValue } from '@rocket.chat/ui-contexts'; |
15 | 3 | import type { ReactNode } from 'react'; |
16 | 4 |
|
17 | | -import { appLayout } from '../lib/appLayout'; |
18 | | -import { roomCoordinator } from '../lib/rooms/roomCoordinator'; |
19 | | -import { queueMicrotask } from '../lib/utils/queueMicrotask'; |
| 5 | +import { Router } from '../router'; |
20 | 6 |
|
21 | | -const subscribers = new Set<() => void>(); |
22 | | - |
23 | | -const listenToRouteChange = () => { |
24 | | - FlowRouter.watchPathChange(); |
25 | | - subscribers.forEach((onRouteChange) => onRouteChange()); |
26 | | -}; |
27 | | - |
28 | | -let computation: Tracker.Computation | undefined; |
29 | | - |
30 | | -queueMicrotask(() => { |
31 | | - computation = Tracker.autorun(listenToRouteChange); |
32 | | -}); |
33 | | - |
34 | | -const subscribeToRouteChange = (onRouteChange: () => void): (() => void) => { |
35 | | - subscribers.add(onRouteChange); |
36 | | - |
37 | | - computation?.invalidate(); |
38 | | - |
39 | | - return () => { |
40 | | - subscribers.delete(onRouteChange); |
41 | | - |
42 | | - if (subscribers.size === 0) { |
43 | | - queueMicrotask(() => computation?.stop()); |
44 | | - } |
45 | | - }; |
46 | | -}; |
47 | | - |
48 | | -const getLocationPathname = () => FlowRouter.current().path.replace(/\?.*/, '') as LocationPathname; |
49 | | - |
50 | | -const getLocationSearch = () => location.search as LocationSearch; |
51 | | - |
52 | | -const getRouteParameters = () => (FlowRouter.current().params ?? {}) as RouteParameters; |
53 | | - |
54 | | -const getSearchParameters = () => (FlowRouter.current().queryParams ?? {}) as SearchParameters; |
55 | | - |
56 | | -const getRouteName = () => FlowRouter.current().route?.name as RouteName | undefined; |
57 | | - |
58 | | -const encodeSearchParameters = (searchParameters: SearchParameters) => { |
59 | | - const search = new URLSearchParams(); |
60 | | - |
61 | | - for (const [key, value] of Object.entries(searchParameters)) { |
62 | | - search.append(key, value); |
63 | | - } |
64 | | - |
65 | | - const searchString = search.toString(); |
66 | | - |
67 | | - return searchString ? `?${searchString}` : ''; |
68 | | -}; |
69 | | - |
70 | | -const buildRoutePath = (to: To): LocationPathname | `${LocationPathname}?${LocationSearch}` => { |
71 | | - if (typeof to === 'string') { |
72 | | - return to; |
73 | | - } |
74 | | - |
75 | | - if ('pathname' in to) { |
76 | | - const { pathname, search = {} } = to; |
77 | | - return (pathname + encodeSearchParameters(search)) as LocationPathname | `${LocationPathname}?${LocationSearch}`; |
78 | | - } |
79 | | - |
80 | | - if ('pattern' in to) { |
81 | | - const { pattern, params = {}, search = {} } = to; |
82 | | - return Tracker.nonreactive(() => FlowRouter.path(pattern, params, search)) as |
83 | | - | LocationPathname |
84 | | - | `${LocationPathname}?${LocationSearch}`; |
85 | | - } |
86 | | - |
87 | | - if ('name' in to) { |
88 | | - const { name, params = {}, search = {} } = to; |
89 | | - return Tracker.nonreactive(() => FlowRouter.path(name, params, search)) as LocationPathname | `${LocationPathname}?${LocationSearch}`; |
90 | | - } |
91 | | - |
92 | | - throw new Error('Invalid route'); |
93 | | -}; |
94 | | - |
95 | | -const navigate = ( |
96 | | - toOrDelta: To | number, |
97 | | - options?: { |
98 | | - replace?: boolean; |
99 | | - }, |
100 | | -) => { |
101 | | - if (typeof toOrDelta === 'number') { |
102 | | - history.go(toOrDelta); |
103 | | - return; |
104 | | - } |
105 | | - |
106 | | - const path = buildRoutePath(toOrDelta); |
107 | | - const state = { path }; |
108 | | - |
109 | | - if (options?.replace) { |
110 | | - history.replaceState(state, '', path); |
111 | | - } else { |
112 | | - history.pushState(state, '', path); |
113 | | - } |
114 | | - |
115 | | - dispatchEvent(new PopStateEvent('popstate', { state })); |
116 | | -}; |
117 | | - |
118 | | -const routes: RouteObject[] = []; |
119 | | -const routesSubscribers = new Set<() => void>(); |
120 | | - |
121 | | -const updateFlowRouter = () => { |
122 | | - if (FlowRouter._initialized) { |
123 | | - FlowRouter._updateCallbacks(); |
124 | | - FlowRouter._page.dispatch(new FlowRouter._page.Context(FlowRouter._current.path)); |
125 | | - return; |
126 | | - } |
127 | | - |
128 | | - FlowRouter.initialize({ |
129 | | - hashbang: false, |
130 | | - page: { |
131 | | - click: true, |
132 | | - }, |
133 | | - }); |
134 | | -}; |
135 | | - |
136 | | -const defineRoutes = (routes: RouteObject[]) => { |
137 | | - const flowRoutes = routes.map((route) => { |
138 | | - if (route.path === '*') { |
139 | | - FlowRouter.notFound = { |
140 | | - action: () => appLayout.render(<>{route.element}</>), |
141 | | - }; |
142 | | - |
143 | | - return FlowRouter.notFound; |
144 | | - } |
145 | | - |
146 | | - return FlowRouter.route(route.path, { |
147 | | - name: route.id, |
148 | | - action: () => appLayout.render(<>{route.element}</>), |
149 | | - }); |
150 | | - }); |
151 | | - |
152 | | - routes.push(...routes); |
153 | | - const index = routes.length - 1; |
154 | | - |
155 | | - updateFlowRouter(); |
156 | | - routesSubscribers.forEach((onRoutesChange) => onRoutesChange()); |
157 | | - |
158 | | - return () => { |
159 | | - flowRoutes.forEach((flowRoute) => { |
160 | | - FlowRouter._routes = FlowRouter._routes.filter((r) => r !== flowRoute); |
161 | | - if ('name' in flowRoute && flowRoute.name) { |
162 | | - delete FlowRouter._routesMap[flowRoute.name]; |
163 | | - } else { |
164 | | - FlowRouter.notFound = { |
165 | | - action: () => appLayout.render(<></>), |
166 | | - }; |
167 | | - } |
168 | | - }); |
169 | | - |
170 | | - if (index !== -1) { |
171 | | - routes.splice(index, 1); |
172 | | - } |
173 | | - |
174 | | - updateFlowRouter(); |
175 | | - routesSubscribers.forEach((onRoutesChange) => onRoutesChange()); |
176 | | - }; |
177 | | -}; |
178 | | - |
179 | | -const getRoutes = () => routes; |
180 | | - |
181 | | -const subscribeToRoutesChange = (onRoutesChange: () => void): (() => void) => { |
182 | | - routesSubscribers.add(onRoutesChange); |
183 | | - |
184 | | - onRoutesChange(); |
185 | | - |
186 | | - return () => { |
187 | | - routesSubscribers.delete(onRoutesChange); |
188 | | - }; |
189 | | -}; |
190 | | - |
191 | | -/** @deprecated */ |
192 | | -export const router: RouterContextValue = { |
193 | | - subscribeToRouteChange, |
194 | | - getLocationPathname, |
195 | | - getLocationSearch, |
196 | | - getRouteParameters, |
197 | | - getSearchParameters, |
198 | | - getRouteName, |
199 | | - buildRoutePath, |
200 | | - navigate, |
201 | | - defineRoutes, |
202 | | - getRoutes, |
203 | | - subscribeToRoutesChange, |
204 | | - getRoomRoute(roomType: RoomType, routeData: RoomRouteData) { |
205 | | - return { path: roomCoordinator.getRouteLink(roomType, routeData) || '/' }; |
206 | | - }, |
207 | | -}; |
| 7 | +/** @deprecated consume it from the `RouterContext` instead */ |
| 8 | +export const router: RouterContextValue = new Router(); |
208 | 9 |
|
209 | 10 | type RouterProviderProps = { |
210 | 11 | children?: ReactNode; |
|
0 commit comments