index.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import React, { useState, useLayoutEffect, useEffect } from "react";
  2. import { LoadingMessage } from "../components/LoadingMessage";
  3. import { TopErrorBoundary } from "../components/TopErrorBoundary";
  4. import Excalidraw from "../excalidraw-embed/index";
  5. import {
  6. importFromLocalStorage,
  7. importUsernameFromLocalStorage,
  8. saveToLocalStorage,
  9. saveUsernameToLocalStorage,
  10. } from "../data/localStorage";
  11. import { debounce } from "../utils";
  12. import { SAVE_TO_LOCAL_STORAGE_TIMEOUT } from "../time_constants";
  13. import { EVENT } from "../constants";
  14. import { ImportedDataState } from "../data/types";
  15. import { ExcalidrawElement } from "../element/types";
  16. import { AppState } from "../types";
  17. const saveDebounced = debounce(
  18. (elements: readonly ExcalidrawElement[], state: AppState) => {
  19. saveToLocalStorage(elements, state);
  20. },
  21. SAVE_TO_LOCAL_STORAGE_TIMEOUT,
  22. );
  23. const onUsernameChange = (username: string) => {
  24. saveUsernameToLocalStorage(username);
  25. };
  26. const onBlur = () => {
  27. saveDebounced.flush();
  28. };
  29. export default function ExcalidrawApp() {
  30. // dimensions
  31. // ---------------------------------------------------------------------------
  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. // initial state
  47. // ---------------------------------------------------------------------------
  48. const [initialState, setInitialState] = useState<{
  49. data: ImportedDataState;
  50. user: {
  51. name: string | null;
  52. };
  53. } | null>(null);
  54. useEffect(() => {
  55. setInitialState({
  56. data: importFromLocalStorage(),
  57. user: {
  58. name: importUsernameFromLocalStorage(),
  59. },
  60. });
  61. }, []);
  62. // blur/unload
  63. // ---------------------------------------------------------------------------
  64. useEffect(() => {
  65. window.addEventListener(EVENT.UNLOAD, onBlur, false);
  66. window.addEventListener(EVENT.BLUR, onBlur, false);
  67. return () => {
  68. window.removeEventListener(EVENT.UNLOAD, onBlur, false);
  69. window.removeEventListener(EVENT.BLUR, onBlur, false);
  70. };
  71. }, []);
  72. // ---------------------------------------------------------------------------
  73. if (!initialState) {
  74. return <LoadingMessage />;
  75. }
  76. return (
  77. <TopErrorBoundary>
  78. <Excalidraw
  79. width={dimensions.width}
  80. height={dimensions.height}
  81. onChange={saveDebounced}
  82. initialData={initialState.data}
  83. user={initialState.user}
  84. onUsernameChange={onUsernameChange}
  85. />
  86. </TopErrorBoundary>
  87. );
  88. }