diff --git a/src/App.svelte b/src/App.svelte index 0b2c2045db435edcfafd059410bdfbe85d0a5817..f32688988736bcd4dacce89da0e1c8875d561d87 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -3,7 +3,9 @@ import { fetchDialogSet } from "./utils"; import Debugger from "./Debugger.svelte"; import type { Dialog } from "./types"; -import SavingComponent from "./SavingComponent.svelte"; + import SavingComponent from "./SavingComponent.svelte"; + + const gameStateServerBaseUrl = "http://localhost:4000/"; const dialogSetPromise = fetchDialogSet(); let currentDialog: Dialog; @@ -15,7 +17,7 @@ import SavingComponent from "./SavingComponent.svelte"; <br /> <Debugger {dialogSet} bind:currentDialog /> - <SavingComponent/> + <SavingComponent {gameStateServerBaseUrl} /> {:catch _error} <h3>Oh no :(</h3> <p> diff --git a/src/SavingComponent.svelte b/src/SavingComponent.svelte index b1aee2c817e7ec9a04ed04890538d4bda18383ec..0af4a6d712238dbbfd77e1f7fa5a587e8eb8977a 100644 --- a/src/SavingComponent.svelte +++ b/src/SavingComponent.svelte @@ -1,15 +1,48 @@ +<script lang="ts"> + import { identity } from "svelte/internal"; + import { gameFactsStore } from "./gameFacts"; + + import { GameStateServerClient } from "./gameStateServerClient"; + import type { GameState } from "./types"; + + export let gameStateServerBaseUrl: string; + $: gameStateServerClient = new GameStateServerClient(gameStateServerBaseUrl); + let pretixOrderCode: string = "XXXYYY"; + + async function handleSaveGameState(): Promise<void> { + const gameState: GameState = { + gameFacts: $gameFactsStore, + }; + gameStateServerClient.saveState(pretixOrderCode, gameState); + } + + async function handleLoadGameState(): Promise<void> { + const newGameState = await gameStateServerClient.loadState(pretixOrderCode); + $gameFactsStore = newGameState.gameFacts; + } +</script> + <div> - <input type="text" placeholder="Your Pretix order code" maxlength="5" minlength="5" > - <button style="width: 25rem;" - >💾 Save game (not implemented yet)</button - > + <input + type="text" + placeholder="Your Pretix order code" + maxlength="5" + minlength="5" + bind:value={pretixOrderCode} + /> + <button style="width: 10rem;" on:click|preventDefault={handleSaveGameState}> + 💾 Save game + </button> + <button style="width: 10rem;" on:click|preventDefault={handleLoadGameState}> + ▶️ Load game + </button> </div> + <style> - div { - border: 1px solid; - padding: 10px; - padding-bottom: 0px; - margin: 5px; - } - </style> - \ No newline at end of file + div { + border: 1px solid; + padding: 10px; + padding-bottom: 0px; + margin: 5px; + } +</style> diff --git a/src/gameStateServerClient.ts b/src/gameStateServerClient.ts new file mode 100644 index 0000000000000000000000000000000000000000..cd93d70f0df47ca024bdb6c5141479cfb5b2b280 --- /dev/null +++ b/src/gameStateServerClient.ts @@ -0,0 +1,48 @@ +import { GameState, isValidGameState } from "./types"; + +export class GameStateServerClient { + _apiBaseUrl: string; + constructor(apiBaseUrl: string) { + this._apiBaseUrl = apiBaseUrl; + } + + async saveState(id: string, gameState: GameState): Promise<boolean> { + try { + await fetch(this._apiBaseUrl + "/game/state/" + id, { + method: "POST", + mode: "cors", + cache: "no-cache", + credentials: "omit", + headers: { + "Content-Type": "application/json", + }, + referrerPolicy: "no-referrer", + body: JSON.stringify(gameState), + }); + return true; + } catch (saveStateToServerError) { + console.error( + "Failed to save state to server. The following info may help to solve this problem:", + saveStateToServerError + ); + return false; + } + } + + async loadState(id: string): Promise<GameState> { + try { + const rawResponse = await fetch(this._apiBaseUrl + "/game/state/" + id, { + method: "GET", + mode: "cors", + cache: "no-cache", + credentials: "omit", + referrerPolicy: "no-referrer", + }); + const decodedResponse = await rawResponse.json(); + if (!isValidGameState(decodedResponse)) { + throw new TypeError("State from server is not a valid gameState"); + } + return decodedResponse; + } catch (loadGameStateFromServerError) {} + } +} diff --git a/src/types.ts b/src/types.ts index 3a82a881598582e3292dd12b90afacafd0b2fdaf..fc40a9a63f4ef38b225b2606533555f610ba53ee 100644 --- a/src/types.ts +++ b/src/types.ts @@ -97,3 +97,16 @@ export interface DialogOption { */ forbiddenFacts?: String[]; } + +export interface GameState { + gameFacts: String[]; +} + +export function isValidGameState(input: unknown): input is GameState { + if (typeof input !== "object") return false; + return ( + input["gameFacts"] !== undefined && + Array.isArray(input["gameFacts"]) && + input["gameFacts"].every((gameFact) => typeof gameFact === "string") + ); +}