localStorage.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import { ExcalidrawElement } from "../element/types";
  2. import { AppState, LibraryItems } from "../types";
  3. import { clearAppStateForLocalStorage, getDefaultAppState } from "../appState";
  4. import { restore } from "./restore";
  5. const LOCAL_STORAGE_KEY = "excalidraw";
  6. const LOCAL_STORAGE_KEY_STATE = "excalidraw-state";
  7. const LOCAL_STORAGE_KEY_COLLAB = "excalidraw-collab";
  8. const LOCAL_STORAGE_KEY_LIBRARY = "excalidraw-library";
  9. let _LATEST_LIBRARY_ITEMS: LibraryItems | null = null;
  10. export const loadLibrary = (): Promise<LibraryItems> => {
  11. return new Promise(async (resolve) => {
  12. if (_LATEST_LIBRARY_ITEMS) {
  13. return resolve(JSON.parse(JSON.stringify(_LATEST_LIBRARY_ITEMS)));
  14. }
  15. try {
  16. const data = localStorage.getItem(LOCAL_STORAGE_KEY_LIBRARY);
  17. if (!data) {
  18. return resolve([]);
  19. }
  20. const items = (JSON.parse(data) as LibraryItems).map(
  21. (elements) => restore({ elements, appState: null }).elements,
  22. ) as Mutable<LibraryItems>;
  23. // clone to ensure we don't mutate the cached library elements in the app
  24. _LATEST_LIBRARY_ITEMS = JSON.parse(JSON.stringify(items));
  25. resolve(items);
  26. } catch (e) {
  27. console.error(e);
  28. resolve([]);
  29. }
  30. });
  31. };
  32. export const saveLibrary = (items: LibraryItems) => {
  33. const prevLibraryItems = _LATEST_LIBRARY_ITEMS;
  34. try {
  35. const serializedItems = JSON.stringify(items);
  36. // cache optimistically so that consumers have access to the latest
  37. // immediately
  38. _LATEST_LIBRARY_ITEMS = JSON.parse(serializedItems);
  39. localStorage.setItem(LOCAL_STORAGE_KEY_LIBRARY, serializedItems);
  40. } catch (e) {
  41. _LATEST_LIBRARY_ITEMS = prevLibraryItems;
  42. console.error(e);
  43. }
  44. };
  45. export const saveUsernameToLocalStorage = (username: string) => {
  46. try {
  47. localStorage.setItem(
  48. LOCAL_STORAGE_KEY_COLLAB,
  49. JSON.stringify({ username }),
  50. );
  51. } catch (error) {
  52. // Unable to access window.localStorage
  53. console.error(error);
  54. }
  55. };
  56. export const importUsernameFromLocalStorage = (): string | null => {
  57. try {
  58. const data = localStorage.getItem(LOCAL_STORAGE_KEY_COLLAB);
  59. if (data) {
  60. return JSON.parse(data).username;
  61. }
  62. } catch (error) {
  63. // Unable to access localStorage
  64. console.error(error);
  65. }
  66. return null;
  67. };
  68. export const saveToLocalStorage = (
  69. elements: readonly ExcalidrawElement[],
  70. appState: AppState,
  71. ) => {
  72. try {
  73. localStorage.setItem(
  74. LOCAL_STORAGE_KEY,
  75. JSON.stringify(elements.filter((element) => !element.isDeleted)),
  76. );
  77. localStorage.setItem(
  78. LOCAL_STORAGE_KEY_STATE,
  79. JSON.stringify(clearAppStateForLocalStorage(appState)),
  80. );
  81. } catch (error) {
  82. // Unable to access window.localStorage
  83. console.error(error);
  84. }
  85. };
  86. export const importFromLocalStorage = () => {
  87. let savedElements = null;
  88. let savedState = null;
  89. try {
  90. savedElements = localStorage.getItem(LOCAL_STORAGE_KEY);
  91. savedState = localStorage.getItem(LOCAL_STORAGE_KEY_STATE);
  92. } catch (error) {
  93. // Unable to access localStorage
  94. console.error(error);
  95. }
  96. let elements = [];
  97. if (savedElements) {
  98. try {
  99. elements = JSON.parse(savedElements);
  100. } catch (error) {
  101. console.error(error);
  102. // Do nothing because elements array is already empty
  103. }
  104. }
  105. let appState = null;
  106. if (savedState) {
  107. try {
  108. appState = {
  109. ...getDefaultAppState(),
  110. ...clearAppStateForLocalStorage(
  111. JSON.parse(savedState) as Partial<AppState>,
  112. ),
  113. };
  114. } catch (error) {
  115. console.error(error);
  116. // Do nothing because appState is already null
  117. }
  118. }
  119. return { elements, appState };
  120. };