diff --git a/backend/game.go b/backend/game.go
index 34f106b..e74ff7c 100644
--- a/backend/game.go
+++ b/backend/game.go
@@ -2,6 +2,7 @@ package backend
import (
"context"
+ "errors"
"fmt"
"os"
"path"
@@ -50,7 +51,7 @@ func (g *Game) AddGame(name, savePath string, isFile bool) uuid.UUID {
return id
}
-func (g *Game) RemoveGame(id uuid.UUID) {
+func (g *Game) RemoveGame(id uuid.UUID) error {
newList := []GameSingle{}
for _, game := range g.JsonGame.Data {
if game.ID == id {
@@ -58,10 +59,15 @@ func (g *Game) RemoveGame(id uuid.UUID) {
}
newList = append(newList, game)
}
+ if len(newList) == len(g.JsonGame.Data) {
+ errorMsg := fmt.Sprintf("no element deleted: %v", id)
+ return errors.New(errorMsg)
+ }
g.JsonGame.Data = newList
g.updateJson()
g.removeGameDir(id)
g.logf("Deleted: %v", id)
+ return nil
}
func (g *Game) ReadGames() (*JsonGame, error) {
diff --git a/backend/save.go b/backend/save.go
index 54833c3..58a7983 100644
--- a/backend/save.go
+++ b/backend/save.go
@@ -66,6 +66,14 @@ func (s *Save) LoadSave(saveID, gameID uuid.UUID) error {
return nil
}
+func (s *Save) OverwriteSave(saveID, gameID uuid.UUID) error {
+ err := s.copyGameContent(gameID, saveID)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
func (s *Save) ReadSaves() (*JsonSave, error) {
saveJson, err := utils.ReadConfigFrom[JsonSave](s.filename)
return saveJson, err
@@ -134,9 +142,6 @@ func (s *Save) copySaveContent(saveID, gameID uuid.UUID) error {
if err != nil {
return err
}
- if err = os.RemoveAll(gamePath); err != nil {
- return err
- }
if err = utils.CopyDir(savePath, gamePath); err != nil {
return err
}
diff --git a/backend/utils/copy-dir.go b/backend/utils/copy-dir.go
index 6b79a01..50e5ff8 100644
--- a/backend/utils/copy-dir.go
+++ b/backend/utils/copy-dir.go
@@ -1,9 +1,15 @@
package utils
import (
+ "os"
+
cp "github.com/otiai10/copy"
)
func CopyDir(src, dst string) error {
+ // hard copy (remove target dir and then copy)
+ if err := os.RemoveAll(dst); err != nil {
+ return err
+ }
return cp.Copy(src, dst)
}
diff --git a/frontend/src/components/QuickSaveChip.tsx b/frontend/src/components/QuickSaveChip.tsx
new file mode 100644
index 0000000..7f6126b
--- /dev/null
+++ b/frontend/src/components/QuickSaveChip.tsx
@@ -0,0 +1,55 @@
+import { Button, Chip } from "@material-tailwind/react";
+import clsx from "clsx";
+import { FaFolderOpen } from "react-icons/fa6";
+import WithTooltip from "@/components/WithTooltip";
+
+type QuickSaveChipProps = {
+ enabled: boolean;
+ onClose: () => void;
+ onOpen: () => void;
+};
+
+const QuickSaveChip = (props: QuickSaveChipProps) => {
+ return (
+
+
+ Quick Save
+
+
+
+
+
+ }
+ onClose={() => props.onClose()}
+ icon={
+
+ }
+ />
+
+ );
+};
+
+export default QuickSaveChip;
diff --git a/frontend/src/components/Save.tsx b/frontend/src/components/Save.tsx
new file mode 100644
index 0000000..3eda6de
--- /dev/null
+++ b/frontend/src/components/Save.tsx
@@ -0,0 +1,77 @@
+import { Button, Typography } from "@material-tailwind/react";
+import { FaDownload, FaFolderOpen, FaTrash, FaUpload } from "react-icons/fa6";
+import { type SaveSingle } from "@/hooks/useSave";
+import WithTooltip from "@/components/WithTooltip";
+
+type SavePropsCallback = () => Promise | void;
+
+type SaveProps = {
+ data: SaveSingle;
+ onLoad: SavePropsCallback;
+ onSave: SavePropsCallback;
+ onDelete: SavePropsCallback;
+ onOpenDirectory: SavePropsCallback;
+};
+
+const Save = (props: SaveProps) => {
+ const formatDate = (dateString: string) => {
+ const date = new Date(dateString);
+ const intlDate = new Intl.DateTimeFormat("en-US").format(date);
+ return intlDate;
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
{props.data.Name}
+
+
+
+ {formatDate(props.data.CreatedAt)}
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Save;
diff --git a/frontend/src/components/WithTooltip.tsx b/frontend/src/components/WithTooltip.tsx
new file mode 100644
index 0000000..f8f5809
--- /dev/null
+++ b/frontend/src/components/WithTooltip.tsx
@@ -0,0 +1,26 @@
+import { Tooltip, Typography } from "@material-tailwind/react";
+import { type ReactElement } from "react";
+
+type WithTooltipProps = {
+ children: ReactElement;
+ content: string;
+ placement: string;
+};
+
+const WithTooltip = (props: WithTooltipProps) => {
+ return (
+
+ {props.content}
+
+ }
+ placement={props.placement}
+ className="border border-blue-gray-50 bg-white shadow-xl shadow-black/10"
+ >
+ {props.children}
+
+ );
+};
+
+export default WithTooltip;
diff --git a/frontend/src/hooks/useSave.tsx b/frontend/src/hooks/useSave.tsx
index ee81880..102c5b8 100644
--- a/frontend/src/hooks/useSave.tsx
+++ b/frontend/src/hooks/useSave.tsx
@@ -6,6 +6,7 @@ import {
GetSaves,
LoadQuickSave,
LoadSave,
+ OverwriteSave,
RemoveQuickSave,
RemoveSave,
} from "@wailsjs/go/backend/Save";
@@ -70,6 +71,11 @@ const useSave = (props?: Pick) => {
mutationFn: (s: Pick) => LoadQuickSave(s.GameID),
});
+ const { mutateAsync: overwriteSave } = useMutation({
+ mutationFn: (s: Pick) =>
+ OverwriteSave(s.ID, s.GameID),
+ });
+
return {
querySaves,
queryQuickSave,
@@ -79,6 +85,7 @@ const useSave = (props?: Pick) => {
removeQuickSave,
loadSave,
loadQuickSave,
+ overwriteSave,
};
};
diff --git a/frontend/src/pages/Game.tsx b/frontend/src/pages/Game.tsx
index dc122d1..7f28282 100644
--- a/frontend/src/pages/Game.tsx
+++ b/frontend/src/pages/Game.tsx
@@ -4,14 +4,13 @@ import {
Card,
CardBody,
CardHeader,
- Chip,
Input,
Typography,
} from "@material-tailwind/react";
import { useParams } from "react-router-dom";
import { type SubmitHandler, useForm } from "react-hook-form";
import clsx from "clsx";
-import { FaFolderOpen, FaPencil, FaTrash, FaUpload } from "react-icons/fa6";
+import { FaFolderOpen, FaPencil } from "react-icons/fa6";
import { OpenQuickSaveDir, OpenSaveDir } from "@wailsjs/go/backend/Save";
import { toast } from "react-toastify";
import { useCallback, useState } from "react";
@@ -22,6 +21,9 @@ import LightningSave from "@/components/LightningSave";
import useSave from "@/hooks/useSave";
import useEvents from "@/hooks/useEvents";
import DialogGameForm from "@/components/DialogGameForm";
+import Save from "@/components/Save";
+import WithTooltip from "@/components/WithTooltip";
+import QuickSaveChip from "@/components/QuickSaveChip";
type GameQueryParams = {
id: string;
@@ -43,6 +45,7 @@ const Game = () => {
removeQuickSave,
loadSave,
loadQuickSave,
+ overwriteSave,
} = useSave({
GameID: gameID,
});
@@ -73,6 +76,11 @@ const Game = () => {
toast.info("Loaded");
};
+ const handleOverwrite = async (saveID: string) => {
+ await overwriteSave({ ID: saveID, GameID: gameID });
+ toast.info("Saved");
+ };
+
const handleDelete = (saveID: string) =>
removeSave({ ID: saveID, GameID: gameID });
@@ -93,56 +101,9 @@ const Game = () => {
const handleOpenQuickSaveDirectory = () => OpenQuickSaveDir(gameID);
- const formatDate = (dateString: string) => {
- const date = new Date(dateString);
- const intlDate = new Intl.DateTimeFormat("en-US").format(date);
- return intlDate;
- };
-
useEvents({ type: "quickSave", cb: handleQuickSave });
useEvents({ type: "quickLoad", cb: handleQuickLoad });
- const chipValue = (
-
- Quick Save
-
-
-
- );
-
- const getQuickSaveChip = () => (
-
- removeQuickSave({ GameID: gameID })}
- icon={
-
- }
- />
-
- );
-
useMenuMiddleItem(
,
);
@@ -154,24 +115,28 @@ const Game = () => {
-
-
-
+
+
+
+
+
+
+
@@ -184,7 +149,11 @@ const Game = () => {
+