Skip to content

Commit 78c0d80

Browse files
committed
Merge branch 'main' into UIdesign_v4
2 parents cd0a66a + 0168a99 commit 78c0d80

9 files changed

Lines changed: 109 additions & 42 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 & 15 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");
@@ -135,10 +132,6 @@ on.pause = () => {
135132
resetTimer();
136133
};
137134

138-
on.load_board = (boardTemplate) => {
139-
board = boardTemplate;
140-
};
141-
142135
on.resize = (newBoardSize) => {
143136
boardSize = newBoardSize;
144137
};
@@ -203,17 +196,12 @@ on.placetemplate = (newBoard) => {
203196

204197
on.sizechange(boardSize);
205198

206-
saveButton.onclick = async () => {
199+
on.save_board = async () => {
207200
window.parent.postMessage({ type: "save_board", data: board }, "*");
208201
};
209202

210-
loadButton.onclick = async () => {
211-
window.parent.postMessage({ type: "request:load_board" }, "*");
212-
};
213-
214-
on.load_board = (loadedBoard) => {
215-
console.log("on.load_board");
216-
board = loadedBoard;
203+
on.apply_board = (newBoard) => {
204+
board = newBoard;
217205
renderBoard();
218206
generationChange(0);
219207
resetTimer();

src/routes/+page.svelte

Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@
3131
let generationFigure = $state(0);
3232
let sizeValue = $state(20);
3333
34+
type SaveState = { saving: false } | { saving: true; boardData: boolean[][] };
35+
let saveState: SaveState = $state({ saving: false });
36+
let boardNameInput = $state("");
37+
3438
onMount(() => {
3539
const handleMessage = (event: MessageEvent) => {
3640
if (event.data.type === "generation_change") {
@@ -55,30 +59,38 @@
5559
5660
onMount(() => {
5761
const handler = async (event: MessageEvent<unknown>) => {
58-
console.log("handler call");
5962
const data = event.data as
6063
| { type: "unknown event" }
61-
| { type: "save_board"; data: boolean[][] }
62-
| { type: "request:load_board" };
64+
| { type: "save_board"; data: boolean[][] };
6365
if (data.type === "save_board") {
64-
console.log("board saved!");
65-
await saveBoard(data.data);
66-
return;
67-
}
68-
69-
if (data.type === "request:load_board") {
70-
console.log("loaded board");
71-
const board = await loadBoard();
72-
if (board) {
73-
sendEvent("load_board", board);
74-
}
66+
saveState = { saving: true, boardData: data.data };
67+
boardNameInput = "";
7568
return;
7669
}
7770
};
7871
7972
window.addEventListener("message", handler);
8073
return () => window.removeEventListener("message", handler);
8174
});
75+
76+
async function handleSave() {
77+
if (!saveState.saving) return;
78+
79+
const name = boardNameInput.trim() === "" ? "Unnamed Board" : boardNameInput.trim();
80+
81+
await saveBoard({ board: saveState.boardData, name: name });
82+
83+
saveState = { saving: false };
84+
boardNameInput = "";
85+
}
86+
87+
async function handleLoad() {
88+
const board = await loadBoard();
89+
if (board) {
90+
sendEvent("apply_board", board);
91+
}
92+
return;
93+
}
8294
</script>
8395

8496
<div class="navbar bg-[#E0E0E0] shadow-sm">
@@ -165,6 +177,26 @@
165177
</div>
166178
</div>
167179

180+
<input type="checkbox" class="modal-toggle" bind:checked={saveState.saving} />
181+
<div class="modal" class:modal-open={saveState.saving}>
182+
<div class="modal-box">
183+
<h3 class="font-bold text-lg">盤面を保存</h3>
184+
<p class="py-4">保存する盤面に名前を付けてください(任意)。</p>
185+
<input
186+
type="text"
187+
placeholder="盤面名を入力"
188+
class="input input-bordered w-full max-w-xs"
189+
bind:value={boardNameInput}
190+
/>
191+
<div class="modal-action">
192+
<button class="btn" onclick={() => (saveState = { saving: false })}>キャンセル</button>
193+
<button class="btn btn-primary" onclick={handleSave} disabled={!saveState.saving}>
194+
保存
195+
</button>
196+
</div>
197+
</div>
198+
</div>
199+
168200
<input type="checkbox" class="modal-toggle" bind:checked={resetModalOpen} />
169201
<div class="modal" class:modal-open={resetModalOpen}>
170202
<div class="modal-box">
@@ -291,18 +323,41 @@
291323
<img class="size-6" src={icons.RightArrow} alt="Right Arrow" />
292324
</div>
293325

326+
<div class="font-bold text-black absolute right-143">Board:</div>
327+
328+
<button
329+
class="btn btn-ghost hover:bg-[rgb(220,220,220)] text-black fixed right-125 bottom-1"
330+
onclick={() => {
331+
isProgress = false;
332+
sendEvent("save_board");
333+
}}
334+
>
335+
Save
336+
</button>
337+
338+
<button
339+
class="btn btn-ghost hover:bg-[rgb(220,220,220)] text-black fixed right-109 bottom-1"
340+
onclick={() => {
341+
isProgress = false;
342+
sendEvent("pause");
343+
handleLoad();
344+
}}
345+
>
346+
Load
347+
</button>
348+
294349
<button
295-
class="btn btn-ghost hover:bg-[rgb(220,220,220)] text-black fixed right-114 bottom-1"
350+
class="btn btn-ghost hover:bg-[rgb(220,220,220)] text-black fixed right-92 bottom-1"
296351
onclick={() => {
297352
isProgress = false;
298353
sendEvent("boardreset");
299354
}}
300355
>
301-
Reset board
356+
Reset
302357
</button>
303358

304359
<button
305-
class="btn btn-ghost hover:bg-[rgb(220,220,220)] text-black fixed right-90 bottom-1"
360+
class="btn btn-ghost hover:bg-[rgb(220,220,220)] text-black fixed right-70 bottom-1"
306361
onclick={() => {
307362
isProgress = false;
308363
sendEvent("boardrandom");

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)