70 lines
2.2 KiB
TypeScript
70 lines
2.2 KiB
TypeScript
/**
|
|
* GraphicalEditorKeepAlive
|
|
*
|
|
* Keeps the GraphicalEditorPage mounted across route changes so the canvas
|
|
* state, SSE connections, and editor context survive navigation to ANY page
|
|
* (other features, admin, settings, etc.).
|
|
*
|
|
* Persistence is scoped per `(mandateId, instanceId)`: when the user switches
|
|
* to a DIFFERENT mandate or instance via the navigator, the previous editor
|
|
* mount is discarded and a fresh page is mounted. Otherwise stale state from
|
|
* mandate A leaks into mandate B and saves end up hitting the wrong tenant
|
|
* (HTTP 404 / "not found").
|
|
*
|
|
* Implementation: feeds the cached `(mandate, instance)` tuple into both
|
|
* `props` and `key`. React reuses the mount as long as the tuple stays
|
|
* identical and unmounts/remounts on change.
|
|
*/
|
|
|
|
import React, { useRef } from 'react';
|
|
import { useLocation } from 'react-router-dom';
|
|
import { GraphicalEditorPage } from './GraphicalEditorPage';
|
|
|
|
const _GE_EDITOR_ROUTE_RE = /\/mandates\/([^/]+)\/graphicalEditor\/([^/]+)\/editor/;
|
|
|
|
interface GraphicalEditorKeepAliveProps {
|
|
isVisible: boolean;
|
|
}
|
|
|
|
export const GraphicalEditorKeepAlive: React.FC<GraphicalEditorKeepAliveProps> = ({ isVisible }) => {
|
|
const location = useLocation();
|
|
const cachedMandateIdRef = useRef<string>('');
|
|
const cachedInstanceIdRef = useRef<string>('');
|
|
const hasEverMountedRef = useRef(false);
|
|
|
|
const match = location.pathname.match(_GE_EDITOR_ROUTE_RE);
|
|
if (match?.[1] && match?.[2]) {
|
|
cachedMandateIdRef.current = match[1];
|
|
cachedInstanceIdRef.current = match[2];
|
|
hasEverMountedRef.current = true;
|
|
}
|
|
|
|
if (!hasEverMountedRef.current) return null;
|
|
|
|
const mandateId = cachedMandateIdRef.current;
|
|
const instanceId = cachedInstanceIdRef.current;
|
|
const scopeKey = `${mandateId}:${instanceId}`;
|
|
|
|
return (
|
|
<div
|
|
style={{
|
|
display: isVisible ? 'flex' : 'none',
|
|
flexDirection: 'column',
|
|
position: 'absolute',
|
|
top: 'var(--mobile-topbar-height, 0px)',
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
overflow: 'hidden',
|
|
}}
|
|
>
|
|
<GraphicalEditorPage
|
|
key={scopeKey}
|
|
persistentInstanceId={instanceId}
|
|
persistentMandateId={mandateId}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default GraphicalEditorKeepAlive;
|