Skip to content
Snippets Groups Projects

3459235: Add POC of urlParams

Closed Lee Rowlands requested to merge issue/experience_builder-3459235:url-params-state into 0.x
5 unresolved threads

Track UI state using URL params, allows deep linking.

@jessebaker this is the kind of thing I was eluding to instead of a router.

It looks like there's some hard-coded stuff in the state that isn't being respected - e.g. the load event is clobbering the selected component - but its a good start - panning/zooming etc is deep-linking.

Merge request reports

Loading
Loading

Activity

Filter activity
  • Approvals
  • Assignees & reviewers
  • Comments (from bots)
  • Comments (from users)
  • Commits & branches
  • Edits
  • Labels
  • Lock status
  • Mentions
  • Merge request status
  • Tracking
29 30 // Infer the `RootState` type from the root reducer
30 31 export type RootState = ReturnType<typeof rootReducer>;
31 32
33 let timeout: number | null;
34
32 35 // The store setup is wrapped in `makeStore` to allow reuse
33 36 // when setting up tests that need the same store config
34 export const makeStore = (preloadedState?: Partial<RootState>) => {
37 export const makeStore = (preloadedState?: Partial<RootState>, thisWindow?: PartialWindow) => {
  • 44 previewApi.middleware
    45 ];
    46 if (thisWindow) {
    47 const urlParamListener = urlParamListenerFactory(thisWindow);
    48 additionalMiddlewares.push(urlParamListener.middleware);
    49 }
    50 return getDefaultMiddleware().prepend(additionalMiddlewares)
    51 },
    45 52 preloadedState,
    46 53 });
    47 54 // configure listeners using the provided defaults
    48 55 // optional, but required for `refetchOnFocus`/`refetchOnReconnect` behaviors
    49 56 setupListeners(store.dispatch);
    57 // Keep UI state in sync with back/forward from user.
    58 if (thisWindow !== undefined) {
    59 thisWindow.addEventListener('popstate', () => {
  • 11 } from "@/features/ui/uiSlice";
    12
    13 export type PartialWindow = Pick<Window, 'location' | 'history' | 'addEventListener' | 'setTimeout' | 'clearTimeout'>;
    14
    15 export type UrlParamStartListening = TypedStartListening<RootState, AppDispatch, PartialWindow>
    16
    17 /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
    18 const urlParamListenerFactory = (thisWindow: PartialWindow) => {
    19 const urlParamListener = createListenerMiddleware({extra: thisWindow});
    20
    21 const startUrlParamListening = urlParamListener.startListening as UrlParamStartListening;
    22
    23 let timeout: number | null;
    24
    25 startUrlParamListening({
    26 matcher: isAnyOf(setSelectedComponent, setPrimaryPanelOpen, setContextualPanelOpen, setCanvasViewPort, canvasViewPortZoomIn, canvasViewPortZoomOut),
  • 20
    21 const startUrlParamListening = urlParamListener.startListening as UrlParamStartListening;
    22
    23 let timeout: number | null;
    24
    25 startUrlParamListening({
    26 matcher: isAnyOf(setSelectedComponent, setPrimaryPanelOpen, setContextualPanelOpen, setCanvasViewPort, canvasViewPortZoomIn, canvasViewPortZoomOut),
    27 effect: async (action, listenerApi) => {
    28 const filterState = selectUiState(listenerApi.getState());
    29 const {history, location, setTimeout, clearTimeout} = listenerApi.extra;
    30 if (timeout) {
    31 clearTimeout(timeout);
    32 timeout = null;
    33 }
    34 // Debounce this by 300ms.
    35 timeout = setTimeout(() => history.pushState(null, '', `${location.pathname}?${asUrlSearchParams(filterState).toString()}`), 300);
  • 71 canvasViewport.y = Number(params.get('y'));
    72 }
    73 if (params.get('scale')) {
    74 canvasViewport.scale = Number(params.get('scale'));
    75 }
    76 let selectedComponent = uiState.selectedComponent;
    77 if (params.get('component')) {
    78 selectedComponent = params.get('component') || undefined;
    79 }
    80 let primaryPanelOpen = uiState.primaryPanelOpen;
    81 if (params.get('primary-panel')) {
    82 primaryPanelOpen = Boolean(params.get('primary-panel'));
    83 }
    84 let contextualPanelOpen = uiState.contextualPanelOpen
    85 if (params.get('contextual-panel')) {
    86 contextualPanelOpen = Boolean(params.get('contextual-panel'));
    • Comment on lines +62 to +86
      Author Maintainer

      for some reason the props on the currentState were immutable, possibly because of the use of immer and not return new instances in the reducers, so I had to put these into variables instead of mutating the passed object :shrug: it works but its not as clean as what we normally do in client work

    • IDK about the reason behind the immutability, but this suggests this MR should document why those are immutable, so that we can justify @larowlan's work-around. :pray:

    • Please register or sign in to reply
  • closed

  • !90 (merged) was merged.

  • Please register or sign in to reply
    Loading