diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 98c0e5d..7be2c22 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -5,7 +5,7 @@ import lifeGameJS from "@/iframe/life-game.js?raw"; import patterns from "$lib/board-templates"; import * as icons from "$lib/icons/index.ts"; - import { loadBoard, saveBoard } from "./api.ts"; + import { saveBoard, fetchBoardList, loadBoardById, type BoardListItem } from "./api.ts"; let editingCode = $state(lifeGameJS); let appliedCode = $state(lifeGameJS); @@ -23,9 +23,14 @@ let generationFigure = $state(0); let sizeValue = $state(20); - type SaveState = { saving: false } | { saving: true; boardData: boolean[][] }; + type SaveState = { saving: false } | { saving: true; boardData: boolean[][]; boardName: string }; let saveState: SaveState = $state({ saving: false }); - let boardNameInput = $state(""); + + type LoadState = + | { state: "closed" } + | { state: "loading" } + | { state: "list"; list: BoardListItem[] }; + let loadState: LoadState = $state({ state: "closed" }); type OngoingEvent = | "play" @@ -56,8 +61,7 @@ break; } case "save_board": { - saveState = { saving: true, boardData: event.data.data as boolean[][] }; - boardNameInput = ""; + saveState = { saving: true, boardData: event.data.data as boolean[][], boardName: "" }; break; } default: { @@ -81,20 +85,32 @@ async function handleSave() { if (!saveState.saving) return; - const name = boardNameInput.trim() === "" ? "Unnamed Board" : boardNameInput.trim(); + const name = saveState.boardName.trim() === "" ? "Unnamed Board" : saveState.boardName.trim(); await saveBoard({ board: saveState.boardData, name: name }, isJapanese); saveState = { saving: false }; - boardNameInput = ""; } async function handleLoad() { - const board = await loadBoard(isJapanese); + loadState = { state: "loading" }; + + const list = await fetchBoardList(isJapanese); + + if (list) { + loadState = { state: "list", list }; + } else { + loadState = { state: "closed" }; + } + } + + async function selectBoard(id: number) { + loadState = { state: "closed" }; + + const board = await loadBoardById(id, isJapanese); if (board) { sendEvent("apply_board", board); } - return; } @@ -196,34 +212,87 @@ - - +
{ isProgress = false; + sendEvent("pause"); sendEvent("save_board"); }} > diff --git a/src/routes/api.ts b/src/routes/api.ts index 711c995..0c126da 100644 --- a/src/routes/api.ts +++ b/src/routes/api.ts @@ -32,7 +32,13 @@ export async function saveBoard(data: { board: boolean[][]; name: string }, isJa } } -export async function loadBoard(isJapanese: boolean): Promise { +export type BoardListItem = { + id: number; + boardName: string; + createdAt: string; +}; + +export async function fetchBoardList(isJapanese: boolean): Promise { try { const response = await fetch("/api/board"); @@ -52,9 +58,46 @@ export async function loadBoard(isJapanese: boolean): Promise { + try { + const response = await fetch(`/api/board?id=${id}`); + + if (!response.ok) { + if (response.status === 404) { + if (isJapanese) { + throw new Error("指定されたIDのデータが見つかりません。"); + } else { + throw new Error("The specified ID data was not found."); + } + } else { + if (isJapanese) { + throw new Error("サーバーとの通信に失敗しました。"); + } else { + throw new Error("Failed to communicate with the server."); + } + } + } + const loadedBoard = await response.json(); - return loadedBoard as boolean[][]; // TODO: add proper types + return loadedBoard as boolean[][]; } catch (err) { if (isJapanese) { console.error("読込エラー:", err); diff --git a/src/routes/api/board/+server.ts b/src/routes/api/board/+server.ts index 9270476..d79eeed 100644 --- a/src/routes/api/board/+server.ts +++ b/src/routes/api/board/+server.ts @@ -28,17 +28,43 @@ export async function POST({ request }) { return json(newState, { status: 201 }); } -export async function GET() { - // データベースから一番「最後」に保存されたデータを1件だけ探す - const latestState = await prisma.boardState.findFirst({ - orderBy: { - createdAt: "desc", // 作成日時(createdAt)の降順(desc)で並び替え - }, - }); +export async function GET({ url }) { + const boardId = url.searchParams.get("id"); - if (!latestState) { - return json({ message: "No state found" }, { status: 404 }); - } + if (boardId) { + //IDが指定された場合、そのIDの盤面を返す + const id = parseInt(boardId, 10); + if (isNaN(id)) { + return json({ message: "無効なIDです。" }, { status: 400 }); + } + + const state = await prisma.boardState.findUnique({ + where: { id: id }, + select: { boardData: true }, + }); - return json(latestState.boardData); + if (!state) { + return json({ message: `ID: ${id} の盤面は見つかりません。` }, { status: 404 }); + } + + return json(state.boardData); + } else { + //IDが指定されなかった場合、全ての盤面のリストを返す + const allStates = await prisma.boardState.findMany({ + orderBy: { + createdAt: "desc", + }, + select: { + id: true, + boardName: true, + createdAt: true, + }, + }); + + if (!allStates || allStates.length === 0) { + return json({ message: "No state found" }, { status: 404 }); + } + + return json(allStates); + } }