ui-nyla/src/pages/views/graphicalEditor/GraphicalEditorKeepAlive.tsx
2026-04-26 08:31:31 +02:00

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;