From a5753fa3657115cd107c336eae988368588380ba Mon Sep 17 00:00:00 2001 From: 1ilit <1ilit@proton.me> Date: Sun, 27 Jul 2025 16:12:27 +0400 Subject: [PATCH] separate share and versions --- src/api/gists.js | 20 +- src/components/EditorHeader/ControlPanel.jsx | 2 + src/components/EditorHeader/Modal/Share.jsx | 11 +- .../EditorHeader/SideSheet/Revisions.jsx | 174 +++++++++++++----- .../EditorHeader/SideSheet/Sidesheet.jsx | 10 +- src/components/Workspace.jsx | 26 ++- src/i18n/locales/en.js | 3 +- 7 files changed, 181 insertions(+), 65 deletions(-) diff --git a/src/api/gists.js b/src/api/gists.js index 87c15e9..bac5f5a 100644 --- a/src/api/gists.js +++ b/src/api/gists.js @@ -1,11 +1,12 @@ import axios from "axios"; -const filename = "share.json"; -const description = "drawDB diagram"; +export const SHARE_FILENAME = "share.json"; +export const VERSION_FILENAME = "versionned.json"; +const description = "drawDB diagram"; const baseUrl = import.meta.env.VITE_BACKEND_URL; -export async function create(content) { +export async function create(filename, content) { const res = await axios.post(`${baseUrl}/gists`, { public: false, filename, @@ -16,7 +17,7 @@ export async function create(content) { return res.data.data.id; } -export async function patch(gistId, content) { +export async function patch(gistId, filename, content) { await axios.patch(`${baseUrl}/gists/${gistId}`, { filename, content, @@ -49,3 +50,14 @@ export async function getVersion(gistId, sha) { return res.data; } + +export async function getCommitsWithFile(gistId, file, perPage = 20, page = 1) { + const res = await axios.get(`${baseUrl}/gists/${gistId}/file-versions/${file}`, { + params: { + per_page: perPage, + page, + }, + }); + + return res.data; +} \ No newline at end of file diff --git a/src/components/EditorHeader/ControlPanel.jsx b/src/components/EditorHeader/ControlPanel.jsx index 2c5a968..f45e32b 100644 --- a/src/components/EditorHeader/ControlPanel.jsx +++ b/src/components/EditorHeader/ControlPanel.jsx @@ -1513,6 +1513,8 @@ export default function ControlPanel({ /> setSidesheet(SIDESHEET.NONE)} /> diff --git a/src/components/EditorHeader/Modal/Share.jsx b/src/components/EditorHeader/Modal/Share.jsx index 5826697..71346a7 100644 --- a/src/components/EditorHeader/Modal/Share.jsx +++ b/src/components/EditorHeader/Modal/Share.jsx @@ -13,7 +13,7 @@ import { } from "../../../hooks"; import { databases } from "../../../data/databases"; import { MODAL } from "../../../data/constants"; -import { create, del, patch } from "../../../api/gists"; +import { create, patch, SHARE_FILENAME } from "../../../api/gists"; export default function Share({ title, setModal }) { const { t } = useTranslation(); @@ -55,24 +55,23 @@ export default function Share({ title, setModal }) { const unshare = useCallback(async () => { try { - await del(gistId); - setGistId(""); + await patch(gistId, SHARE_FILENAME, undefined); setModal(MODAL.NONE); } catch (e) { console.error(e); setError(e); } - }, [gistId, setGistId, setModal]); + }, [gistId, setModal]); useEffect(() => { const updateOrGenerateLink = async () => { try { setLoading(true); if (!gistId || gistId === "") { - const id = await create(diagramToString()); + const id = await create(SHARE_FILENAME, diagramToString()); setGistId(id); } else { - await patch(gistId, diagramToString()); + await patch(gistId, SHARE_FILENAME, diagramToString()); } } catch (e) { console.error(e); diff --git a/src/components/EditorHeader/SideSheet/Revisions.jsx b/src/components/EditorHeader/SideSheet/Revisions.jsx index 4a312ef..fc091f3 100644 --- a/src/components/EditorHeader/SideSheet/Revisions.jsx +++ b/src/components/EditorHeader/SideSheet/Revisions.jsx @@ -7,19 +7,63 @@ import { IconChevronRight, IconChevronLeft, } from "@douyinfe/semi-icons"; -import { getCommits, getVersion } from "../../../api/gists"; +import { + create, + getCommitsWithFile, + getVersion, + patch, + VERSION_FILENAME, +} from "../../../api/gists"; import { DateTime } from "luxon"; -import { useAreas, useDiagram, useLayout } from "../../../hooks"; +import { + useAreas, + useDiagram, + useEnums, + useLayout, + useNotes, + useTransform, + useTypes, +} from "../../../hooks"; +import { databases } from "../../../data/databases"; -export default function Revisions({ open }) { +export default function Revisions({ open, title, setTitle }) { const { gistId, setVersion } = useContext(IdContext); - const { setAreas } = useAreas(); + const { areas, setAreas } = useAreas(); const { setLayout } = useLayout(); - const { setTables, setRelationships } = useDiagram(); + const { database, tables, relationships, setTables, setRelationships } = + useDiagram(); + const { notes, setNotes } = useNotes(); + const { types, setTypes } = useTypes(); + const { enums, setEnums } = useEnums(); + const { transform } = useTransform(); const { t, i18n } = useTranslation(); const [isLoading, setIsLoading] = useState(false); const [revisions, setRevisions] = useState([]); + const diagramToString = useCallback(() => { + return JSON.stringify({ + title, + tables, + relationships: relationships, + notes: notes, + subjectAreas: areas, + database: database, + ...(databases[database].hasTypes && { types: types }), + ...(databases[database].hasEnums && { enums: enums }), + transform: transform, + }); + }, [ + areas, + notes, + tables, + relationships, + database, + title, + enums, + types, + transform, + ]); + const loadVersion = useCallback( async (sha) => { try { @@ -27,26 +71,49 @@ export default function Revisions({ open }) { setVersion(sha); setLayout((prev) => ({ ...prev, readOnly: true })); - const content = version.data.files["share.json"].content; + const content = version.data.files[VERSION_FILENAME].content; const parsedDiagram = JSON.parse(content); setTables(parsedDiagram.tables); setRelationships(parsedDiagram.relationships); setAreas(parsedDiagram.subjectAreas); + setNotes(parsedDiagram.notes); + setTitle(parsedDiagram.title); + + if (databases[database].hasTypes) { + setTypes(parsedDiagram.types); + } + + if (databases[database].hasEnums) { + setEnums(parsedDiagram.enums); + } } catch (e) { console.log(e); Toast.error("failed_to_load_diagram"); } }, - [gistId, setTables, setRelationships, setAreas, setVersion, setLayout], + [ + gistId, + setTables, + setRelationships, + setAreas, + setVersion, + setLayout, + database, + setNotes, + setTypes, + setEnums, + setTitle, + ], ); - useEffect(() => { - const getRevisions = async (gistId) => { + const getRevisions = useCallback( + async (gistId) => { try { setIsLoading(true); - const { data } = await getCommits(gistId); + const { data } = await getCommitsWithFile(gistId, VERSION_FILENAME); + setRevisions( data.filter((version) => version.change_status.total !== 0), ); @@ -56,55 +123,72 @@ export default function Revisions({ open }) { } finally { setIsLoading(false); } - }; + }, + [t], + ); + const recordVersion = async () => { + try { + if (gistId) { + console.log(gistId) + await patch(gistId, VERSION_FILENAME, diagramToString()); + } else { + await create(VERSION_FILENAME, diagramToString()); + } + await getRevisions(gistId); + } catch (e) { + Toast.error("failed_to_record_version"); + } + }; + + useEffect(() => { if (gistId && open) { getRevisions(gistId); } - }, [gistId, t, open]); - - if (gistId && isLoading) { - return ( -
- -
{t("loading")}
-
- ); - } + }, [gistId, getRevisions, open]); return (
} title="Previous" /> - } title="Next" />
- {!gistId &&
{t("no_saved_revisions")}
} - {gistId && ( -
- - {revisions.map((r, i) => ( - loadVersion(r.version)} - title={ -
- {`${t("version")} ${revisions.length - i}`} - {r.version.substring(0, 7)} -
- } - description={`${t("commited_at")} ${DateTime.fromISO( - r.committed_at, - ) - .setLocale(i18n.language) - .toLocaleString(DateTime.DATETIME_MED)}`} - icon={} - /> - ))} -
+ {isLoading ? ( +
+ +
{t("loading")}
+ ) : ( + <> + {!gistId &&
{t("no_saved_revisions")}
} + {gistId && ( +
+ + {revisions.map((r, i) => ( + loadVersion(r.version)} + title={ +
+ {`${t("version")} ${revisions.length - i}`} + {r.version.substring(0, 7)} +
+ } + description={`${t("commited_at")} ${DateTime.fromISO( + r.committed_at, + ) + .setLocale(i18n.language) + .toLocaleString(DateTime.DATETIME_MED)}`} + icon={} + /> + ))} +
+
+ )} + )}
); diff --git a/src/components/EditorHeader/SideSheet/Sidesheet.jsx b/src/components/EditorHeader/SideSheet/Sidesheet.jsx index f3784aa..5bfc469 100644 --- a/src/components/EditorHeader/SideSheet/Sidesheet.jsx +++ b/src/components/EditorHeader/SideSheet/Sidesheet.jsx @@ -5,7 +5,7 @@ import Todo from "./Todo"; import Revisions from "./Revisions"; import { useTranslation } from "react-i18next"; -export default function Sidesheet({ type, onClose }) { +export default function Sidesheet({ type, title, setTitle, onClose }) { const { t } = useTranslation(); function getTitle(type) { @@ -28,7 +28,13 @@ export default function Sidesheet({ type, onClose }) { case SIDESHEET.TODO: return ; case SIDESHEET.REVISIONS: - return ; + return ( + + ); default: break; } diff --git a/src/components/Workspace.jsx b/src/components/Workspace.jsx index 873b83c..089da21 100644 --- a/src/components/Workspace.jsx +++ b/src/components/Workspace.jsx @@ -25,7 +25,7 @@ import { useTranslation } from "react-i18next"; import { databases } from "../data/databases"; import { isRtl } from "../i18n/utils/rtl"; import { useSearchParams } from "react-router-dom"; -import { get } from "../api/gists"; +import { get, SHARE_FILENAME } from "../api/gists"; export const IdContext = createContext({ gistId: "", @@ -293,7 +293,7 @@ export default function WorkSpace() { const loadFromGist = async (shareId) => { try { const { data } = await get(shareId); - const parsedDiagram = JSON.parse(data.files["share.json"].content); + const parsedDiagram = JSON.parse(data.files[SHARE_FILENAME].content); setUndoStack([]); setRedoStack([]); setGistId(shareId); @@ -372,6 +372,12 @@ export default function WorkSpace() { searchParams, ]); + const returnToCurrentDiagram = async () => { + await load(); + setLayout((prev) => ({ ...prev, readOnly: false })); + setVersion(null); + }; + useEffect(() => { if ( tables?.length === 0 && @@ -447,15 +453,20 @@ export default function WorkSpace() { {version && ( -
setShowRestoreModal(true)} - > +
+
)} {!(layout.sidebar || layout.toolbar || layout.header) && ( @@ -521,7 +532,8 @@ export default function WorkSpace() { onCancel={() => setShowRestoreModal(false)} title={ - {t("restore_version")} + {" "} + {t("restore_version")} } okText={t("continue")} diff --git a/src/i18n/locales/en.js b/src/i18n/locales/en.js index 62f4584..83ca257 100644 --- a/src/i18n/locales/en.js +++ b/src/i18n/locales/en.js @@ -267,7 +267,8 @@ const en = { read_only: "Read only", continue: "Continue", restore_version: "Restore version", - restore_warning: "Loading another version will overwrite the current changes." + restore_warning: "Loading another version will overwrite the current changes.", + return_to_current: "Return to diagram" }, };