useJson.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import { decompressFromBase64 } from "lz-string";
  2. import toast from "react-hot-toast";
  3. import { altogic } from "src/api/altogic";
  4. import { defaultJson } from "src/constants/data";
  5. import { saveJson as saveJsonDB } from "src/services/db/json";
  6. import useGraph from "src/store/useGraph";
  7. import { Json } from "src/typings/altogic";
  8. import { create } from "zustand";
  9. interface JsonActions {
  10. setJson: (json: string) => void;
  11. getJson: () => string;
  12. getHasChanges: () => boolean;
  13. fetchJson: (jsonId: string | string[] | undefined) => void;
  14. setError: (hasError: boolean) => void;
  15. setHasChanges: (hasChanges: boolean) => void;
  16. saveJson: (isNew?: boolean) => Promise<string | undefined>;
  17. }
  18. const initialStates = {
  19. data: null as Json | null,
  20. json: "",
  21. loading: true,
  22. hasChanges: false,
  23. hasError: false,
  24. };
  25. function inIframe() {
  26. try {
  27. return window.self !== window.top;
  28. } catch (e) {
  29. return true;
  30. }
  31. }
  32. export type JsonStates = typeof initialStates;
  33. const useJson = create<JsonStates & JsonActions>()((set, get) => ({
  34. ...initialStates,
  35. getJson: () => get().json,
  36. getHasChanges: () => get().hasChanges,
  37. fetchJson: async jsonId => {
  38. const isURL = new RegExp(
  39. /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$/
  40. );
  41. if (typeof jsonId === "string" && isURL.test(jsonId)) {
  42. try {
  43. const res = await fetch(jsonId);
  44. const json = await res.json();
  45. const jsonStr = JSON.stringify(json, null, 2);
  46. useGraph.getState().setGraph(jsonStr);
  47. return set({ json: jsonStr, loading: false });
  48. } catch (error) {
  49. useGraph.getState().setGraph(defaultJson);
  50. set({ json: defaultJson, loading: false });
  51. toast.error("Failed to fetch JSON from URL!");
  52. }
  53. } else if (jsonId) {
  54. const { data, errors } = await altogic.endpoint.get(`json/${jsonId}`, undefined, {
  55. userid: altogic.auth.getUser()?._id,
  56. });
  57. if (!errors) {
  58. const decompressedData = decompressFromBase64(data.json);
  59. if (decompressedData) {
  60. useGraph.getState().setGraph(decompressedData);
  61. return set({
  62. data,
  63. json: decompressedData ?? undefined,
  64. loading: false,
  65. });
  66. }
  67. }
  68. }
  69. if (inIframe()) {
  70. useGraph.getState().setGraph("[]");
  71. return set({ json: "[]", loading: false });
  72. } else {
  73. useGraph.getState().setGraph(defaultJson);
  74. set({ json: defaultJson, loading: false });
  75. }
  76. },
  77. setJson: json => {
  78. useGraph.getState().setGraph(json);
  79. set({ json, hasChanges: true });
  80. },
  81. saveJson: async (isNew = true) => {
  82. try {
  83. const url = new URL(window.location.href);
  84. const params = new URLSearchParams(url.search);
  85. const jsonQuery = params.get("json");
  86. toast.loading("Saving JSON...", { id: "jsonSave" });
  87. const res = await saveJsonDB({ id: isNew ? undefined : jsonQuery, data: get().json });
  88. if (res.errors && res.errors.items.length > 0) throw res.errors;
  89. toast.success("JSON saved to cloud", { id: "jsonSave" });
  90. set({ hasChanges: false });
  91. return res.data._id;
  92. } catch (error: any) {
  93. if (error?.items?.length > 0) {
  94. toast.error(error.items[0].message, { id: "jsonSave", duration: 5000 });
  95. return undefined;
  96. }
  97. toast.error("Failed to save JSON!", { id: "jsonSave" });
  98. return undefined;
  99. }
  100. },
  101. setError: (hasError: boolean) => set({ hasError }),
  102. setHasChanges: (hasChanges: boolean) => set({ hasChanges }),
  103. }));
  104. export default useJson;