diff --git a/package-lock.json b/package-lock.json index 558d228..9e9ac40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "jspdf": "^3.0.1", "jszip": "^3.10.1", "lexical": "^0.12.5", + "lodash": "^4.17.21", "luxon": "^3.7.1", "nanoid": "^5.1.5", "node-sql-parser": "^5.3.11", @@ -4150,7 +4151,8 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" }, "node_modules/lodash-es": { "version": "4.17.21", diff --git a/package.json b/package.json index f60b0f5..b751feb 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "jspdf": "^3.0.1", "jszip": "^3.10.1", "lexical": "^0.12.5", + "lodash": "^4.17.21", "luxon": "^3.7.1", "nanoid": "^5.1.5", "node-sql-parser": "^5.3.11", diff --git a/src/api/gists.js b/src/api/gists.js index bac5f5a..7fc1d90 100644 --- a/src/api/gists.js +++ b/src/api/gists.js @@ -18,10 +18,12 @@ export async function create(filename, content) { } export async function patch(gistId, filename, content) { - await axios.patch(`${baseUrl}/gists/${gistId}`, { + const { deleted } = await axios.patch(`${baseUrl}/gists/${gistId}`, { filename, content, }); + + return deleted; } export async function del(gistId) { @@ -52,12 +54,15 @@ export async function getVersion(gistId, sha) { } 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, + 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 f45e32b..762e3a0 100644 --- a/src/components/EditorHeader/ControlPanel.jsx +++ b/src/components/EditorHeader/ControlPanel.jsx @@ -1659,10 +1659,10 @@ export default function ControlPanel({ - + diff --git a/src/components/EditorHeader/Modal/Share.jsx b/src/components/EditorHeader/Modal/Share.jsx index 71346a7..7ffc87f 100644 --- a/src/components/EditorHeader/Modal/Share.jsx +++ b/src/components/EditorHeader/Modal/Share.jsx @@ -55,13 +55,16 @@ export default function Share({ title, setModal }) { const unshare = useCallback(async () => { try { - await patch(gistId, SHARE_FILENAME, undefined); + const deleted = await patch(gistId, SHARE_FILENAME, undefined); + if (deleted) { + setGistId(""); + } setModal(MODAL.NONE); } catch (e) { console.error(e); setError(e); } - }, [gistId, setModal]); + }, [gistId, setModal, setGistId]); useEffect(() => { const updateOrGenerateLink = async () => { diff --git a/src/components/EditorHeader/SideSheet/Revisions.jsx b/src/components/EditorHeader/SideSheet/Revisions.jsx deleted file mode 100644 index fc091f3..0000000 --- a/src/components/EditorHeader/SideSheet/Revisions.jsx +++ /dev/null @@ -1,195 +0,0 @@ -import { useCallback, useContext, useEffect, useState } from "react"; -import { IdContext } from "../../Workspace"; -import { useTranslation } from "react-i18next"; -import { Button, IconButton, Spin, Steps, Tag, Toast } from "@douyinfe/semi-ui"; -import { - IconPlus, - IconChevronRight, - IconChevronLeft, -} from "@douyinfe/semi-icons"; -import { - create, - getCommitsWithFile, - getVersion, - patch, - VERSION_FILENAME, -} from "../../../api/gists"; -import { DateTime } from "luxon"; -import { - useAreas, - useDiagram, - useEnums, - useLayout, - useNotes, - useTransform, - useTypes, -} from "../../../hooks"; -import { databases } from "../../../data/databases"; - -export default function Revisions({ open, title, setTitle }) { - const { gistId, setVersion } = useContext(IdContext); - const { areas, setAreas } = useAreas(); - const { setLayout } = useLayout(); - 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 { - const version = await getVersion(gistId, sha); - setVersion(sha); - setLayout((prev) => ({ ...prev, readOnly: true })); - - 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, - database, - setNotes, - setTypes, - setEnums, - setTitle, - ], - ); - - const getRevisions = useCallback( - async (gistId) => { - try { - setIsLoading(true); - const { data } = await getCommitsWithFile(gistId, VERSION_FILENAME); - - setRevisions( - data.filter((version) => version.change_status.total !== 0), - ); - } catch (e) { - console.log(e); - Toast.error(t("oops_smth_went_wrong")); - } 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, getRevisions, open]); - - return ( -
-
- } title="Previous" /> - - } title="Next" /> -
- {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 5bfc469..c0221e7 100644 --- a/src/components/EditorHeader/SideSheet/Sidesheet.jsx +++ b/src/components/EditorHeader/SideSheet/Sidesheet.jsx @@ -2,7 +2,7 @@ import { SideSheet as SemiUISideSheet } from "@douyinfe/semi-ui"; import { SIDESHEET } from "../../../data/constants"; import Timeline from "./Timeline"; import Todo from "./Todo"; -import Revisions from "./Revisions"; +import Versions from "./Versions"; import { useTranslation } from "react-i18next"; export default function Sidesheet({ type, title, setTitle, onClose }) { @@ -14,8 +14,8 @@ export default function Sidesheet({ type, title, setTitle, onClose }) { return t("timeline"); case SIDESHEET.TODO: return t("to_do"); - case SIDESHEET.REVISIONS: - return t("revisions"); + case SIDESHEET.VERSIONS: + return t("versions"); default: break; } @@ -27,9 +27,9 @@ export default function Sidesheet({ type, title, setTitle, onClose }) { return ; case SIDESHEET.TODO: return ; - case SIDESHEET.REVISIONS: + case SIDESHEET.VERSIONS: return ( - { + const previousVersion = await get(gistId); + const previousDiagram = JSON.parse( + previousVersion.data.files[VERSION_FILENAME]?.content, + ); + const currentDiagram = { + title, + tables, + relationships: relationships, + notes: notes, + subjectAreas: areas, + database: database, + ...(databases[database].hasTypes && { types: types }), + ...(databases[database].hasEnums && { enums: enums }), + transform: transform, + }; + + return !_.isEqual(previousDiagram, currentDiagram); + }; + const recordVersion = async () => { try { + const hasChanges = await hasDiagramChanged(); + if (!hasChanges) { + Toast.info(t("no_changes_to_record")); + return; + } if (gistId) { await patch(gistId, VERSION_FILENAME, diagramToString()); } else { - await create(VERSION_FILENAME, diagramToString()); + const id = await create(VERSION_FILENAME, diagramToString()); + setGistId(id); } await getRevisions(gistId); } catch (e) { diff --git a/src/data/constants.js b/src/data/constants.js index 1ac6fd3..8a86e29 100644 --- a/src/data/constants.js +++ b/src/data/constants.js @@ -99,7 +99,7 @@ export const SIDESHEET = { NONE: 0, TODO: 1, TIMELINE: 2, - REVISIONS: 3, + VERSIONS: 3, }; export const DB = { diff --git a/src/i18n/locales/en.js b/src/i18n/locales/en.js index 83ca257..03848f9 100644 --- a/src/i18n/locales/en.js +++ b/src/i18n/locales/en.js @@ -259,16 +259,17 @@ const en = { tab_view: "Tab view", label: "Label", many_side_label: "Many(n) side label", - revisions: "Revisions", - no_saved_revisions: "No saved revisions", version: "Version", + versions: "Versions", + no_saved_versions: "No saved versions", record_version: "Record version", commited_at: "Commited at", read_only: "Read only", continue: "Continue", restore_version: "Restore version", restore_warning: "Loading another version will overwrite the current changes.", - return_to_current: "Return to diagram" + return_to_current: "Return to diagram", + no_changes_to_record: "No changes to record", }, };