Skip to content

Commit 0168a99

Browse files
authored
Merge pull request #22 from ut-code/Database-v2
Save,Loadボタンをsveltに移しました 盤面に名前を付ける機能を追加しました
2 parents ce87a83 + 5334d2a commit 0168a99

9 files changed

Lines changed: 105 additions & 41 deletions

File tree

bun.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
},
4040
"dependencies": {
4141
"@prisma/adapter-pg": "^6.19.0",
42-
"@prisma/client": "^6.19.0"
42+
"@prisma/client": "^6.19.0",
43+
"valibot": "^1.1.0"
4344
}
4445
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-- CreateTable
2+
CREATE TABLE "BoardState" (
3+
"id" SERIAL NOT NULL,
4+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
5+
"boardData" JSONB NOT NULL,
6+
"boardName" TEXT NOT NULL,
7+
8+
CONSTRAINT "BoardState_pkey" PRIMARY KEY ("id")
9+
);

prisma/schema.prisma

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ model BoardState {
1919
id Int @id @default(autoincrement())
2020
createdAt DateTime @default(now())
2121
boardData Json
22+
boardName String
2223
}

src/iframe/life-game.html

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,6 @@
1313
"
1414
>
1515
<table id="game-board" style="border-collapse: collapse"></table>
16-
17-
<button id="saveButton">SAVE</button>
18-
<button id="loadButton">LOAD</button>
19-
2016
<script src="./life-game.js"></script>
2117
</body>
2218
</html>

src/iframe/life-game.js

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@ function isNextAlive(around, self) {
2525
return false;
2626
}
2727

28-
const saveButton = document.getElementById("saveButton");
29-
const loadButton = document.getElementById("loadButton");
30-
3128
//Boardの初期化
3229
let board = Array.from({ length: boardSize }, () => Array.from({ length: boardSize }, () => false));
3330
const table = document.getElementById("game-board");
@@ -131,10 +128,6 @@ on.pause = () => {
131128
clearInterval(timerId);
132129
};
133130

134-
on.load_board = (boardTemplate) => {
135-
board = boardTemplate;
136-
};
137-
138131
on.resize = (newBoardSize) => {
139132
boardSize = newBoardSize;
140133
};
@@ -191,18 +184,12 @@ on.stateupdate = () => {
191184

192185
on.sizechange(boardSize);
193186

194-
saveButton.onclick = async () => {
187+
on.save_board = async () => {
195188
window.parent.postMessage({ type: "save_board", data: board }, "*");
196189
};
197190

198-
loadButton.onclick = async () => {
199-
window.parent.postMessage({ type: "request:load_board" }, "*");
200-
};
201-
202-
on.load_board = (loadedBoard) => {
203-
console.log("on.load_board");
204-
board = loadedBoard;
191+
on.apply_board = (newBoard) => {
192+
board = newBoard;
205193
renderBoard();
206194
generationChange(0);
207-
stop();
208195
};

src/routes/+page.svelte

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@
3333
let generationFigure = $state(0);
3434
let sizeInputValue = $state(20);
3535
36+
type SaveState = { saving: false } | { saving: true; boardData: boolean[][] };
37+
let saveState: SaveState = $state({ saving: false });
38+
let boardNameInput = $state("");
39+
3640
onMount(() => {
3741
const handleMessage = (event: MessageEvent) => {
3842
if (event.data.type === "patternError") {
@@ -60,30 +64,38 @@
6064
6165
onMount(() => {
6266
const handler = async (event: MessageEvent<unknown>) => {
63-
console.log("handler call");
6467
const data = event.data as
6568
| { type: "unknown event" }
66-
| { type: "save_board"; data: boolean[][] }
67-
| { type: "request:load_board" };
69+
| { type: "save_board"; data: boolean[][] };
6870
if (data.type === "save_board") {
69-
console.log("board saved!");
70-
await saveBoard(data.data);
71-
return;
72-
}
73-
74-
if (data.type === "request:load_board") {
75-
console.log("loaded board");
76-
const board = await loadBoard();
77-
if (board) {
78-
sendEvent("load_board", board);
79-
}
71+
saveState = { saving: true, boardData: data.data };
72+
boardNameInput = "";
8073
return;
8174
}
8275
};
8376
8477
window.addEventListener("message", handler);
8578
return () => window.removeEventListener("message", handler);
8679
});
80+
81+
async function handleSave() {
82+
if (!saveState.saving) return;
83+
84+
const name = boardNameInput.trim() === "" ? "Unnamed Board" : boardNameInput.trim();
85+
86+
await saveBoard({ board: saveState.boardData, name: name });
87+
88+
saveState = { saving: false };
89+
boardNameInput = "";
90+
}
91+
92+
async function handleLoad() {
93+
const board = await loadBoard();
94+
if (board) {
95+
sendEvent("apply_board", board);
96+
}
97+
return;
98+
}
8799
</script>
88100

89101
<div class="navbar bg-[#E0E0E0] shadow-sm">
@@ -146,6 +158,26 @@
146158
</div>
147159
</div>
148160

161+
<input type="checkbox" class="modal-toggle" bind:checked={saveState.saving} />
162+
<div class="modal" class:modal-open={saveState.saving}>
163+
<div class="modal-box">
164+
<h3 class="font-bold text-lg">盤面を保存</h3>
165+
<p class="py-4">保存する盤面に名前を付けてください(任意)。</p>
166+
<input
167+
type="text"
168+
placeholder="盤面名を入力"
169+
class="input input-bordered w-full max-w-xs"
170+
bind:value={boardNameInput}
171+
/>
172+
<div class="modal-action">
173+
<button class="btn" onclick={() => (saveState = { saving: false })}>キャンセル</button>
174+
<button class="btn btn-primary" onclick={handleSave} disabled={!saveState.saving}>
175+
保存
176+
</button>
177+
</div>
178+
</div>
179+
</div>
180+
149181
<input type="checkbox" class="modal-toggle" bind:checked={resetModalOpen} />
150182
<div class="modal" class:modal-open={resetModalOpen}>
151183
<div class="modal-box">
@@ -256,7 +288,7 @@
256288
</div>
257289

258290
<button
259-
class="btn btn-ghost hover:bg-[rgb(220,220,220)] ml-100 text-black"
291+
class="btn btn-ghost hover:bg-[rgb(220,220,220)] ml-50 text-black"
260292
onclick={() => {
261293
isProgress = false;
262294
sendEvent("boardreset");
@@ -275,6 +307,27 @@
275307
Random
276308
</button>
277309

310+
<button
311+
class="btn btn-ghost hover:bg-[rgb(220,220,220)] text-black"
312+
onclick={() => {
313+
isProgress = false;
314+
sendEvent("save_board");
315+
}}
316+
>
317+
Save
318+
</button>
319+
320+
<button
321+
class="btn btn-ghost hover:bg-[rgb(220,220,220)] text-black"
322+
onclick={() => {
323+
isProgress = false;
324+
sendEvent("pause");
325+
handleLoad();
326+
}}
327+
>
328+
Load
329+
</button>
330+
278331
<button
279332
class="btn btn-ghost hover:bg-[rgb(220,220,220)] ml-5 text-black"
280333
onclick={() => {

src/routes/api.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
export async function saveBoard(board: boolean[][]) {
1+
export async function saveBoard(data: { board: boolean[][]; name: string }) {
22
try {
33
const response = await fetch("/api/board", {
44
method: "POST",
55
headers: {
66
"Content-Type": "application/json",
77
},
8-
body: JSON.stringify(board),
8+
body: JSON.stringify(data),
99
});
1010

1111
if (!response.ok) {
@@ -33,7 +33,6 @@ export async function loadBoard(): Promise<boolean[][] | undefined> {
3333

3434
const loadedBoard = await response.json();
3535

36-
console.log("fetched board:", loadedBoard);
3736
return loadedBoard as boolean[][]; // TODO: add proper types
3837
} catch (err) {
3938
console.error("読込エラー:", err);

src/routes/api/board/+server.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,27 @@
11
import { json } from "@sveltejs/kit";
22
import { prisma } from "@/lib/prisma.server.ts";
3+
import * as v from "valibot";
4+
5+
const BoardSchema = v.object({
6+
board: v.array(v.array(v.boolean())),
7+
name: v.pipe(v.string(), v.minLength(1, "盤面名は必須です。")),
8+
});
39

410
export async function POST({ request }) {
5-
const boardData = await request.json();
11+
let body;
12+
try {
13+
body = v.parse(BoardSchema, await request.json());
14+
} catch (error) {
15+
console.error("Request validation failed:", error);
16+
return json({ message: "無効なリクエストデータです。" }, { status: 400 });
17+
}
18+
19+
const { board, name } = body;
620

721
const newState = await prisma.boardState.create({
822
data: {
9-
boardData: boardData,
23+
boardData: board,
24+
boardName: name,
1025
},
1126
});
1227

0 commit comments

Comments
 (0)