index.tsx 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import React, { useEffect, useLayoutEffect, useState } from "react";
  2. import { EVENT_LOAD, trackEvent } from "../analytics";
  3. import { LoadingMessage } from "../components/LoadingMessage";
  4. import { TopErrorBoundary } from "../components/TopErrorBoundary";
  5. import { EVENT } from "../constants";
  6. import {
  7. getTotalStorageSize,
  8. importFromLocalStorage,
  9. importUsernameFromLocalStorage,
  10. saveToLocalStorage,
  11. saveUsernameToLocalStorage,
  12. } from "../data/localStorage";
  13. import { ImportedDataState } from "../data/types";
  14. import { ExcalidrawElement } from "../element/types";
  15. import Excalidraw from "../packages/excalidraw/index";
  16. import { SAVE_TO_LOCAL_STORAGE_TIMEOUT } from "../time_constants";
  17. import { AppState } from "../types";
  18. import { debounce } from "../utils";
  19. const saveDebounced = debounce(
  20. (elements: readonly ExcalidrawElement[], state: AppState) => {
  21. saveToLocalStorage(elements, state);
  22. },
  23. SAVE_TO_LOCAL_STORAGE_TIMEOUT,
  24. );
  25. const onUsernameChange = (username: string) => {
  26. saveUsernameToLocalStorage(username);
  27. };
  28. const onBlur = () => {
  29. saveDebounced.flush();
  30. };
  31. export default function ExcalidrawApp() {
  32. const [dimensions, setDimensions] = useState({
  33. width: window.innerWidth,
  34. height: window.innerHeight,
  35. });
  36. useLayoutEffect(() => {
  37. const onResize = () => {
  38. setDimensions({
  39. width: window.innerWidth,
  40. height: window.innerHeight,
  41. });
  42. };
  43. window.addEventListener("resize", onResize);
  44. return () => window.removeEventListener("resize", onResize);
  45. }, []);
  46. const [initialState, setInitialState] = useState<{
  47. data: ImportedDataState;
  48. user: {
  49. name: string | null;
  50. };
  51. } | null>(null);
  52. useEffect(() => {
  53. const storageSize = getTotalStorageSize();
  54. if (storageSize) {
  55. trackEvent(EVENT_LOAD, "storage", "size", storageSize);
  56. } else {
  57. trackEvent(EVENT_LOAD, "first time");
  58. }
  59. setInitialState({
  60. data: importFromLocalStorage(),
  61. user: {
  62. name: importUsernameFromLocalStorage(),
  63. },
  64. });
  65. }, []);
  66. useEffect(() => {
  67. window.addEventListener(EVENT.UNLOAD, onBlur, false);
  68. window.addEventListener(EVENT.BLUR, onBlur, false);
  69. return () => {
  70. window.removeEventListener(EVENT.UNLOAD, onBlur, false);
  71. window.removeEventListener(EVENT.BLUR, onBlur, false);
  72. };
  73. }, []);
  74. return initialState ? (
  75. <TopErrorBoundary>
  76. <Excalidraw
  77. width={dimensions.width}
  78. height={dimensions.height}
  79. onChange={saveDebounced}
  80. initialData={initialState.data}
  81. user={initialState.user}
  82. onUsernameChange={onUsernameChange}
  83. />
  84. </TopErrorBoundary>
  85. ) : (
  86. <LoadingMessage />
  87. );
  88. }