CustomStats.tsx 2.5 KB

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