expose resetScene and getSceneElementsIncludingDeleted && move excalidrawApp to excalidraw-app folder (#2272)

Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
Aakansha Doshi 2020-10-25 19:39:57 +05:30 committed by GitHub
parent 1845b5e32c
commit e916d7f6f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 191 additions and 182 deletions

View File

@ -271,6 +271,10 @@ export type PointerDownState = Readonly<{
export type ExcalidrawImperativeAPI =
| {
updateScene: InstanceType<typeof App>["updateScene"];
resetScene: InstanceType<typeof App>["resetScene"];
getSceneElementsIncludingDeleted: InstanceType<
typeof App
>["getSceneElementsIncludingDeleted"];
}
| undefined;
@ -306,6 +310,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
if (forwardedRef && "current" in forwardedRef) {
forwardedRef.current = {
updateScene: this.updateScene,
resetScene: this.resetScene,
getSceneElementsIncludingDeleted: this.getSceneElementsIncludingDeleted,
};
}
this.scene = new Scene();

View File

@ -0,0 +1,108 @@
import React, { useState, useLayoutEffect, useEffect } from "react";
import { LoadingMessage } from "../components/LoadingMessage";
import { TopErrorBoundary } from "../components/TopErrorBoundary";
import Excalidraw from "../excalidraw-embed/index";
import {
importFromLocalStorage,
importUsernameFromLocalStorage,
saveToLocalStorage,
saveUsernameToLocalStorage,
} from "../data/localStorage";
import { debounce } from "../utils";
import { SAVE_TO_LOCAL_STORAGE_TIMEOUT } from "../time_constants";
import { EVENT } from "../constants";
import { ImportedDataState } from "../data/types";
import { ExcalidrawElement } from "../element/types";
import { AppState } from "../types";
const saveDebounced = debounce(
(elements: readonly ExcalidrawElement[], state: AppState) => {
saveToLocalStorage(elements, state);
},
SAVE_TO_LOCAL_STORAGE_TIMEOUT,
);
const onUsernameChange = (username: string) => {
saveUsernameToLocalStorage(username);
};
const onBlur = () => {
saveDebounced.flush();
};
export default function ExcalidrawApp() {
// dimensions
// ---------------------------------------------------------------------------
const [dimensions, setDimensions] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
useLayoutEffect(() => {
const onResize = () => {
setDimensions({
width: window.innerWidth,
height: window.innerHeight,
});
};
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
}, []);
// initial state
// ---------------------------------------------------------------------------
const [initialState, setInitialState] = useState<{
data: ImportedDataState;
user: {
name: string | null;
};
} | null>(null);
useEffect(() => {
setInitialState({
data: importFromLocalStorage(),
user: {
name: importUsernameFromLocalStorage(),
},
});
}, []);
// blur/unload
// ---------------------------------------------------------------------------
useEffect(() => {
window.addEventListener(EVENT.UNLOAD, onBlur, false);
window.addEventListener(EVENT.BLUR, onBlur, false);
return () => {
window.removeEventListener(EVENT.UNLOAD, onBlur, false);
window.removeEventListener(EVENT.BLUR, onBlur, false);
};
}, []);
// ---------------------------------------------------------------------------
if (!initialState) {
return <LoadingMessage />;
}
return (
<TopErrorBoundary>
<Excalidraw
width={dimensions.width}
height={dimensions.height}
onChange={saveDebounced}
initialData={initialState.data}
user={initialState.user}
onUsernameChange={onUsernameChange}
/>
</TopErrorBoundary>
);
}

31
src/excalidraw-app/pwa.ts Normal file
View File

@ -0,0 +1,31 @@
import { register as registerServiceWorker } from "../serviceWorker";
import { EVENT } from "../constants";
// On Apple mobile devices add the proprietary app icon and splashscreen markup.
// No one should have to do this manually, and eventually this annoyance will
// go away once https://bugs.webkit.org/show_bug.cgi?id=183937 is fixed.
if (
/\b(iPad|iPhone|iPod)\b/.test(navigator.userAgent) &&
!matchMedia("(display-mode: standalone)").matches
) {
import(/* webpackChunkName: "pwacompat" */ "pwacompat");
}
registerServiceWorker({
onUpdate: (registration) => {
const waitingServiceWorker = registration.waiting;
if (waitingServiceWorker) {
waitingServiceWorker.addEventListener(
EVENT.STATE_CHANGE,
(event: Event) => {
const target = event.target as ServiceWorker;
const state = target.state as ServiceWorkerState;
if (state === "activated") {
window.location.reload();
}
},
);
waitingServiceWorker.postMessage({ type: "SKIP_WAITING" });
}
},
});

View File

@ -0,0 +1,39 @@
import * as Sentry from "@sentry/browser";
import * as SentryIntegrations from "@sentry/integrations";
const SentryEnvHostnameMap: { [key: string]: string } = {
"excalidraw.com": "production",
"vercel.app": "staging",
};
const REACT_APP_DISABLE_SENTRY =
process.env.REACT_APP_DISABLE_SENTRY === "true";
// Disable Sentry locally or inside the Docker to avoid noise/respect privacy
const onlineEnv =
!REACT_APP_DISABLE_SENTRY &&
Object.keys(SentryEnvHostnameMap).find(
(item) => window.location.hostname.indexOf(item) >= 0,
);
Sentry.init({
dsn: onlineEnv
? "https://7bfc596a5bf945eda6b660d3015a5460@sentry.io/5179260"
: undefined,
environment: onlineEnv ? SentryEnvHostnameMap[onlineEnv] : undefined,
release: process.env.REACT_APP_GIT_SHA,
ignoreErrors: [
"undefined is not an object (evaluating 'window.__pad.performLoop')", // Only happens on Safari, but spams our servers. Doesn't break anything
],
integrations: [
new SentryIntegrations.CaptureConsole({
levels: ["error"],
}),
],
beforeSend(event) {
if (event.request?.url) {
event.request.url = event.request.url.replace(/#.*$/, "");
}
return event;
},
});

2
src/global.d.ts vendored
View File

@ -10,7 +10,7 @@ interface Document {
interface Window {
ClipboardItem: any;
__EXCALIDRAW_SHA__: string;
__EXCALIDRAW_SHA__: string | undefined;
}
// https://github.com/facebook/create-react-app/blob/ddcb7d5/packages/react-scripts/lib/react-app.d.ts

View File

@ -1,185 +1,10 @@
import React, { useState, useLayoutEffect, useEffect } from "react";
import React from "react";
import ReactDOM from "react-dom";
import * as Sentry from "@sentry/browser";
import * as SentryIntegrations from "@sentry/integrations";
import ExcalidrawApp from "./excalidraw-app";
import { EVENT } from "./constants";
import { TopErrorBoundary } from "./components/TopErrorBoundary";
import Excalidraw from "./excalidraw-embed/index";
import { register as registerServiceWorker } from "./serviceWorker";
import "./excalidraw-app/pwa";
import "./excalidraw-app/sentry";
import { debounce } from "./utils";
import {
importFromLocalStorage,
importUsernameFromLocalStorage,
saveUsernameToLocalStorage,
saveToLocalStorage,
} from "./data/localStorage";
window.__EXCALIDRAW_SHA__ = process.env.REACT_APP_GIT_SHA;
import { SAVE_TO_LOCAL_STORAGE_TIMEOUT } from "./time_constants";
import { ImportedDataState } from "./data/types";
import { LoadingMessage } from "./components/LoadingMessage";
import { ExcalidrawElement } from "./element/types";
import { AppState } from "./types";
// On Apple mobile devices add the proprietary app icon and splashscreen markup.
// No one should have to do this manually, and eventually this annoyance will
// go away once https://bugs.webkit.org/show_bug.cgi?id=183937 is fixed.
if (
/\b(iPad|iPhone|iPod)\b/.test(navigator.userAgent) &&
!matchMedia("(display-mode: standalone)").matches
) {
import(/* webpackChunkName: "pwacompat" */ "pwacompat");
}
const SentryEnvHostnameMap: { [key: string]: string } = {
"excalidraw.com": "production",
"vercel.app": "staging",
};
const REACT_APP_DISABLE_SENTRY =
process.env.REACT_APP_DISABLE_SENTRY === "true";
const REACT_APP_GIT_SHA = process.env.REACT_APP_GIT_SHA as string;
// Disable Sentry locally or inside the Docker to avoid noise/respect privacy
const onlineEnv =
!REACT_APP_DISABLE_SENTRY &&
Object.keys(SentryEnvHostnameMap).find(
(item) => window.location.hostname.indexOf(item) >= 0,
);
Sentry.init({
dsn: onlineEnv
? "https://7bfc596a5bf945eda6b660d3015a5460@sentry.io/5179260"
: undefined,
environment: onlineEnv ? SentryEnvHostnameMap[onlineEnv] : undefined,
release: REACT_APP_GIT_SHA,
ignoreErrors: [
"undefined is not an object (evaluating 'window.__pad.performLoop')", // Only happens on Safari, but spams our servers. Doesn't break anything
],
integrations: [
new SentryIntegrations.CaptureConsole({
levels: ["error"],
}),
],
beforeSend(event) {
if (event.request?.url) {
event.request.url = event.request.url.replace(/#.*$/, "");
}
return event;
},
});
window.__EXCALIDRAW_SHA__ = REACT_APP_GIT_SHA;
const saveDebounced = debounce(
(elements: readonly ExcalidrawElement[], state: AppState) => {
saveToLocalStorage(elements, state);
},
SAVE_TO_LOCAL_STORAGE_TIMEOUT,
);
const onUsernameChange = (username: string) => {
saveUsernameToLocalStorage(username);
};
const onBlur = () => {
saveDebounced.flush();
};
function ExcalidrawApp() {
// dimensions
// ---------------------------------------------------------------------------
const [dimensions, setDimensions] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
useLayoutEffect(() => {
const onResize = () => {
setDimensions({
width: window.innerWidth,
height: window.innerHeight,
});
};
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
}, []);
// initial state
// ---------------------------------------------------------------------------
const [initialState, setInitialState] = useState<{
data: ImportedDataState;
user: {
name: string | null;
};
} | null>(null);
useEffect(() => {
setInitialState({
data: importFromLocalStorage(),
user: {
name: importUsernameFromLocalStorage(),
},
});
}, []);
// blur/unload
// ---------------------------------------------------------------------------
useEffect(() => {
window.addEventListener(EVENT.UNLOAD, onBlur, false);
window.addEventListener(EVENT.BLUR, onBlur, false);
return () => {
window.removeEventListener(EVENT.UNLOAD, onBlur, false);
window.removeEventListener(EVENT.BLUR, onBlur, false);
};
}, []);
// ---------------------------------------------------------------------------
if (!initialState) {
return <LoadingMessage />;
}
return (
<TopErrorBoundary>
<Excalidraw
width={dimensions.width}
height={dimensions.height}
offsetLeft={0}
offsetTop={0}
onChange={saveDebounced}
initialData={initialState.data}
user={initialState.user}
onUsernameChange={onUsernameChange}
/>
</TopErrorBoundary>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<ExcalidrawApp />, rootElement);
registerServiceWorker({
onUpdate: (registration) => {
const waitingServiceWorker = registration.waiting;
if (waitingServiceWorker) {
waitingServiceWorker.addEventListener(
EVENT.STATE_CHANGE,
(event: Event) => {
const target = event.target as ServiceWorker;
const state = target.state as ServiceWorkerState;
if (state === "activated") {
window.location.reload();
}
},
);
waitingServiceWorker.postMessage({ type: "SKIP_WAITING" });
}
},
});
ReactDOM.render(<ExcalidrawApp />, document.getElementById("root"));