CustomStats.tsx 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import { Stats } from "@excalidraw/excalidraw";
  2. import { copyTextToSystemClipboard } from "@excalidraw/excalidraw/clipboard";
  3. import {
  4. DEFAULT_VERSION,
  5. debounce,
  6. getVersion,
  7. nFormatter,
  8. } from "@excalidraw/common";
  9. import { t } from "@excalidraw/excalidraw/i18n";
  10. import { useEffect, useState } from "react";
  11. import type { NonDeletedExcalidrawElement } from "@excalidraw/element/types";
  12. import type { UIAppState } from "@excalidraw/excalidraw/types";
  13. import {
  14. getElementsStorageSize,
  15. getTotalStorageSize,
  16. } from "./data/localStorage";
  17. type StorageSizes = { scene: number; total: number };
  18. const STORAGE_SIZE_TIMEOUT = 500;
  19. const getStorageSizes = debounce((cb: (sizes: StorageSizes) => void) => {
  20. cb({
  21. scene: getElementsStorageSize(),
  22. total: getTotalStorageSize(),
  23. });
  24. }, STORAGE_SIZE_TIMEOUT);
  25. type Props = {
  26. setToast: (message: string) => void;
  27. elements: readonly NonDeletedExcalidrawElement[];
  28. appState: UIAppState;
  29. };
  30. const CustomStats = (props: Props) => {
  31. const [storageSizes, setStorageSizes] = useState<StorageSizes>({
  32. scene: 0,
  33. total: 0,
  34. });
  35. useEffect(() => {
  36. getStorageSizes((sizes) => {
  37. setStorageSizes(sizes);
  38. });
  39. }, [props.elements, props.appState]);
  40. useEffect(() => () => getStorageSizes.cancel(), []);
  41. const version = getVersion();
  42. let hash;
  43. let timestamp;
  44. if (version !== DEFAULT_VERSION) {
  45. timestamp = version.slice(0, 16).replace("T", " ");
  46. hash = version.slice(21);
  47. } else {
  48. timestamp = t("stats.versionNotAvailable");
  49. }
  50. return (
  51. <Stats.StatsRows order={-1}>
  52. <Stats.StatsRow heading>{t("stats.version")}</Stats.StatsRow>
  53. <Stats.StatsRow
  54. style={{ textAlign: "center", cursor: "pointer" }}
  55. onClick={async () => {
  56. try {
  57. await copyTextToSystemClipboard(getVersion());
  58. props.setToast(t("toast.copyToClipboard"));
  59. } catch {}
  60. }}
  61. title={t("stats.versionCopy")}
  62. >
  63. {timestamp}
  64. <br />
  65. {hash}
  66. </Stats.StatsRow>
  67. <Stats.StatsRow heading>{t("stats.storage")}</Stats.StatsRow>
  68. <Stats.StatsRow columns={2}>
  69. <div>{t("stats.scene")}</div>
  70. <div>{nFormatter(storageSizes.scene, 1)}</div>
  71. </Stats.StatsRow>
  72. <Stats.StatsRow columns={2}>
  73. <div>{t("stats.total")}</div>
  74. <div>{nFormatter(storageSizes.total, 1)}</div>
  75. </Stats.StatsRow>
  76. </Stats.StatsRows>
  77. );
  78. };
  79. export default CustomStats;