|
@@ -121,6 +121,7 @@ import {
|
|
|
CANVAS_ONLY_ACTIONS,
|
|
|
DEFAULT_VERTICAL_ALIGN,
|
|
|
GRID_SIZE,
|
|
|
+ LOCAL_STORAGE_KEY_COLLAB_FORCE_FLAG,
|
|
|
} from "../constants";
|
|
|
import {
|
|
|
INITAL_SCENE_UPDATE_TIMEOUT,
|
|
@@ -356,6 +357,39 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|
|
this.onSceneUpdated();
|
|
|
};
|
|
|
|
|
|
+ private shouldForceLoadScene(
|
|
|
+ scene: ResolutionType<typeof loadScene>,
|
|
|
+ ): boolean {
|
|
|
+ if (!scene.elements.length) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ const roomMatch = getCollaborationLinkData(window.location.href);
|
|
|
+
|
|
|
+ if (!roomMatch) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ const collabForceLoadFlag = localStorage.getItem(
|
|
|
+ LOCAL_STORAGE_KEY_COLLAB_FORCE_FLAG,
|
|
|
+ );
|
|
|
+ if (collabForceLoadFlag) {
|
|
|
+ try {
|
|
|
+ const {
|
|
|
+ room: previousRoom,
|
|
|
+ timestamp,
|
|
|
+ }: { room: string; timestamp: number } = JSON.parse(
|
|
|
+ collabForceLoadFlag,
|
|
|
+ );
|
|
|
+ // if loading same room as the one previously unloaded within 15sec
|
|
|
+ // force reload without prompting
|
|
|
+ if (previousRoom === roomMatch[1] && Date.now() - timestamp < 15000) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ } catch {}
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
private initializeScene = async () => {
|
|
|
const searchParams = new URLSearchParams(window.location.search);
|
|
|
const id = searchParams.get("id");
|
|
@@ -363,20 +397,28 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|
|
/^#json=([0-9]+),([a-zA-Z0-9_-]+)$/,
|
|
|
);
|
|
|
|
|
|
- const isCollaborationScene = getCollaborationLinkData(window.location.href);
|
|
|
+ let scene = await loadScene(null);
|
|
|
+
|
|
|
+ let isCollaborationScene = !!getCollaborationLinkData(window.location.href);
|
|
|
+ const isExternalScene = !!(id || jsonMatch || isCollaborationScene);
|
|
|
|
|
|
- if (!isCollaborationScene) {
|
|
|
- let scene: ResolutionType<typeof loadScene> | undefined;
|
|
|
- // Backwards compatibility with legacy url format
|
|
|
- if (id) {
|
|
|
- scene = await loadScene(id);
|
|
|
- } else if (jsonMatch) {
|
|
|
- scene = await loadScene(jsonMatch[1], jsonMatch[2]);
|
|
|
+ if (isExternalScene) {
|
|
|
+ if (
|
|
|
+ this.shouldForceLoadScene(scene) ||
|
|
|
+ window.confirm(t("alerts.loadSceneOverridePrompt"))
|
|
|
+ ) {
|
|
|
+ // Backwards compatibility with legacy url format
|
|
|
+ if (id) {
|
|
|
+ scene = await loadScene(id);
|
|
|
+ } else if (jsonMatch) {
|
|
|
+ scene = await loadScene(jsonMatch[1], jsonMatch[2]);
|
|
|
+ }
|
|
|
+ if (!isCollaborationScene) {
|
|
|
+ window.history.replaceState({}, "Excalidraw", window.location.origin);
|
|
|
+ }
|
|
|
} else {
|
|
|
- scene = await loadScene(null);
|
|
|
- }
|
|
|
- if (scene) {
|
|
|
- this.syncActionResult(scene);
|
|
|
+ isCollaborationScene = false;
|
|
|
+ window.history.replaceState({}, "Excalidraw", window.location.origin);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -384,9 +426,10 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|
|
this.setState({ isLoading: false });
|
|
|
}
|
|
|
|
|
|
- // run this last else the `isLoading` state
|
|
|
if (isCollaborationScene) {
|
|
|
this.initializeSocketClient({ showLoadingState: true });
|
|
|
+ } else if (scene) {
|
|
|
+ this.syncActionResult(scene);
|
|
|
}
|
|
|
};
|
|
|
|
|
@@ -515,6 +558,15 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|
|
}
|
|
|
|
|
|
private beforeUnload = withBatchedUpdates((event: BeforeUnloadEvent) => {
|
|
|
+ if (this.state.isCollaborating && this.portal.roomID) {
|
|
|
+ localStorage.setItem(
|
|
|
+ LOCAL_STORAGE_KEY_COLLAB_FORCE_FLAG,
|
|
|
+ JSON.stringify({
|
|
|
+ timestamp: Date.now(),
|
|
|
+ room: this.portal.roomID,
|
|
|
+ }),
|
|
|
+ );
|
|
|
+ }
|
|
|
if (
|
|
|
this.state.isCollaborating &&
|
|
|
globalSceneState.getElements().length > 0
|