now people can join and leave the game
This commit is contained in:
parent
bf582efc46
commit
f83f50d9b2
12 changed files with 408 additions and 22 deletions
2
.env.example
Normal file
2
.env.example
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
VITE_SUPABASE_URL=https://yourproject.supabase.co
|
||||||
|
VITE_SUPABASE_ANON_KEY=your-service-role-key
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -3,5 +3,6 @@
|
||||||
"editor.inlineSuggest.enabled": true,
|
"editor.inlineSuggest.enabled": true,
|
||||||
"editor.quickSuggestions": {
|
"editor.quickSuggestions": {
|
||||||
"strings": true
|
"strings": true
|
||||||
}
|
},
|
||||||
|
"cSpell.words": ["SUPABASE"]
|
||||||
}
|
}
|
||||||
|
|
135
package-lock.json
generated
135
package-lock.json
generated
|
@ -7,6 +7,9 @@
|
||||||
"": {
|
"": {
|
||||||
"name": "kahootclone",
|
"name": "kahootclone",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
|
"dependencies": {
|
||||||
|
"@supabase/supabase-js": "^2.49.4"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-auto": "^4.0.0",
|
"@sveltejs/adapter-auto": "^4.0.0",
|
||||||
"@sveltejs/kit": "^2.16.0",
|
"@sveltejs/kit": "^2.16.0",
|
||||||
|
@ -1000,6 +1003,73 @@
|
||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@supabase/auth-js": {
|
||||||
|
"version": "2.69.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.69.1.tgz",
|
||||||
|
"integrity": "sha512-FILtt5WjCNzmReeRLq5wRs3iShwmnWgBvxHfqapC/VoljJl+W8hDAyFmf1NVw3zH+ZjZ05AKxiKxVeb0HNWRMQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@supabase/node-fetch": "^2.6.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@supabase/functions-js": {
|
||||||
|
"version": "2.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.4.tgz",
|
||||||
|
"integrity": "sha512-WL2p6r4AXNGwop7iwvul2BvOtuJ1YQy8EbOd0dhG1oN1q8el/BIRSFCFnWAMM/vJJlHWLi4ad22sKbKr9mvjoA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@supabase/node-fetch": "^2.6.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@supabase/node-fetch": {
|
||||||
|
"version": "2.6.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz",
|
||||||
|
"integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "4.x || >=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@supabase/postgrest-js": {
|
||||||
|
"version": "1.19.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.19.4.tgz",
|
||||||
|
"integrity": "sha512-O4soKqKtZIW3olqmbXXbKugUtByD2jPa8kL2m2c1oozAO11uCcGrRhkZL0kVxjBLrXHE0mdSkFsMj7jDSfyNpw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@supabase/node-fetch": "^2.6.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@supabase/realtime-js": {
|
||||||
|
"version": "2.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.11.2.tgz",
|
||||||
|
"integrity": "sha512-u/XeuL2Y0QEhXSoIPZZwR6wMXgB+RQbJzG9VErA3VghVt7uRfSVsjeqd7m5GhX3JR6dM/WRmLbVR8URpDWG4+w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@supabase/node-fetch": "^2.6.14",
|
||||||
|
"@types/phoenix": "^1.5.4",
|
||||||
|
"@types/ws": "^8.5.10",
|
||||||
|
"ws": "^8.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@supabase/storage-js": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@supabase/node-fetch": "^2.6.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@supabase/supabase-js": {
|
||||||
|
"version": "2.49.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.49.4.tgz",
|
||||||
|
"integrity": "sha512-jUF0uRUmS8BKt37t01qaZ88H9yV1mbGYnqLeuFWLcdV+x1P4fl0yP9DGtaEhFPZcwSom7u16GkLEH9QJZOqOkw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@supabase/auth-js": "2.69.1",
|
||||||
|
"@supabase/functions-js": "2.4.4",
|
||||||
|
"@supabase/node-fetch": "2.6.15",
|
||||||
|
"@supabase/postgrest-js": "1.19.4",
|
||||||
|
"@supabase/realtime-js": "2.11.2",
|
||||||
|
"@supabase/storage-js": "2.7.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@sveltejs/acorn-typescript": {
|
"node_modules/@sveltejs/acorn-typescript": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.5.tgz",
|
||||||
|
@ -1383,6 +1453,27 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "22.15.17",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.17.tgz",
|
||||||
|
"integrity": "sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw==",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~6.21.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/phoenix": {
|
||||||
|
"version": "1.6.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz",
|
||||||
|
"integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/ws": {
|
||||||
|
"version": "8.18.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
|
||||||
|
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/accepts": {
|
"node_modules/accepts": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
||||||
|
@ -4029,6 +4120,11 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||||
|
},
|
||||||
"node_modules/type-check": {
|
"node_modules/type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||||
|
@ -4057,6 +4153,11 @@
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "6.21.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||||
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="
|
||||||
|
},
|
||||||
"node_modules/unpipe": {
|
"node_modules/unpipe": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
|
@ -4205,6 +4306,20 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
@ -4238,6 +4353,26 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"node_modules/ws": {
|
||||||
|
"version": "8.18.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz",
|
||||||
|
"integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": ">=5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yallist": {
|
"node_modules/yallist": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
|
||||||
|
|
|
@ -25,5 +25,8 @@
|
||||||
"svelte": "^5.0.0",
|
"svelte": "^5.0.0",
|
||||||
"tailwindcss": "^4.0.0",
|
"tailwindcss": "^4.0.0",
|
||||||
"vite": "^6.0.0"
|
"vite": "^6.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@supabase/supabase-js": "^2.49.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
// place files you want to import through the `$lib` alias in this folder.
|
|
6
src/lib/supabase.js
Normal file
6
src/lib/supabase.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { createClient } from '@supabase/supabase-js';
|
||||||
|
|
||||||
|
export const supabase = createClient(
|
||||||
|
import.meta.env.VITE_SUPABASE_URL,
|
||||||
|
import.meta.env.VITE_SUPABASE_ANON_KEY
|
||||||
|
);
|
|
@ -1,27 +1,37 @@
|
||||||
<script>
|
<script>
|
||||||
let questions = $state([
|
import { supabase } from '$lib/supabase';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
let questions = [
|
||||||
{
|
{
|
||||||
name: '',
|
name: '',
|
||||||
answers: ['', '', '', ''],
|
answers: ['', '', '', ''],
|
||||||
correctAnswer: undefined
|
correctAnswer: undefined
|
||||||
}
|
}
|
||||||
]);
|
];
|
||||||
|
|
||||||
function startGame() {
|
async function startGame() {
|
||||||
if (questions.some((question) => question.name === '')) {
|
if (questions.some((q) => q.name === '')) return alert('Please fill all questions');
|
||||||
alert('Please fill in the question for each question.');
|
if (questions.some((q) => q.answers.some((a) => a === ''))) return alert('Fill all options');
|
||||||
return;
|
if (questions.some((q) => q.correctAnswer === undefined))
|
||||||
}
|
return alert('Select correct answers');
|
||||||
if (questions.some((question) => question.answers.some((answer) => answer === ''))) {
|
|
||||||
alert('Please fill in each of the options for each question.');
|
const gamePin = Math.floor(Math.random() * 1000000)
|
||||||
return;
|
.toString()
|
||||||
}
|
.padStart(6, '0');
|
||||||
if (questions.some((question) => question.correctAnswer === undefined)) {
|
|
||||||
alert('Please select a correct answer for each question.');
|
const { error } = await supabase.from('games').insert({
|
||||||
|
gamePIN: gamePin,
|
||||||
|
gameStatus: 'lobby',
|
||||||
|
questions: questions,
|
||||||
|
players: []
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
alert('Failed to create game: ' + error.message + '\n\nPlease try again.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
alert('Game started!');
|
goto('/host/' + gamePin);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
6
src/routes/host/[gamePin]/+page.js
Normal file
6
src/routes/host/[gamePin]/+page.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// Example of what your +page.js or +page.server.js might need
|
||||||
|
export function load({ params }) {
|
||||||
|
return {
|
||||||
|
gamePin: params.gamePin
|
||||||
|
};
|
||||||
|
}
|
78
src/routes/host/[gamePin]/+page.svelte
Normal file
78
src/routes/host/[gamePin]/+page.svelte
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
<script>
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
import { supabase } from '$lib/supabase';
|
||||||
|
|
||||||
|
export let data;
|
||||||
|
const gamePin = data.gamePin;
|
||||||
|
const name = $page.state?.name;
|
||||||
|
|
||||||
|
let players = [];
|
||||||
|
|
||||||
|
// Subscribe to realtime changes in players
|
||||||
|
function subscribeToPlayers() {
|
||||||
|
const channel = supabase
|
||||||
|
.channel(`game-${gamePin}`)
|
||||||
|
.on(
|
||||||
|
'postgres_changes',
|
||||||
|
{
|
||||||
|
event: 'UPDATE',
|
||||||
|
schema: 'public',
|
||||||
|
table: 'games',
|
||||||
|
filter: `gamePIN=eq.${gamePin}`
|
||||||
|
},
|
||||||
|
(payload) => {
|
||||||
|
players = payload.new.players || [];
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
const { data: gameData } = await supabase
|
||||||
|
.from('games')
|
||||||
|
.select('players')
|
||||||
|
.eq('gamePIN', Number(gamePin))
|
||||||
|
.maybeSingle();
|
||||||
|
|
||||||
|
if (gameData?.players) {
|
||||||
|
players = gameData.players;
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribeToPlayers();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="bg-grey-900 flex h-full items-center justify-center">
|
||||||
|
<div
|
||||||
|
class="flex max-w-[700px] flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg"
|
||||||
|
>
|
||||||
|
<h1 class="m-[0] text-9xl">HOSTING</h1>
|
||||||
|
<h1 class="m-[0] text-7xl">Game Pin:</h1>
|
||||||
|
<h1 class="m-[0] rounded-2xl bg-gray-700 pt-1.5 pr-2 pb-0 pl-2 font-mono text-5xl">
|
||||||
|
{gamePin}
|
||||||
|
</h1>
|
||||||
|
<h1 class="m-[0] mt-10 text-6xl">Players Joined:</h1>
|
||||||
|
<h1 class="m-[0] text-4xl text-gray-400">(Total Players: {players.length})</h1>
|
||||||
|
<div class="mt-2 flex flex-wrap justify-center gap-2">
|
||||||
|
{#each players as player}
|
||||||
|
<span class="m-[0] rounded-xl bg-gray-700 pt-1 pr-2 pb-0 pl-2 font-mono text-3xl"
|
||||||
|
>{player.name}</span
|
||||||
|
>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="mt-5 cursor-pointer rounded-2xl bg-green-700 p-2 text-4xl transition-all hover:scale-110 hover:-rotate-10"
|
||||||
|
on:click={() => {
|
||||||
|
if (players.length > 0) {
|
||||||
|
if (confirm('Are you sure you want to start the game?')) {
|
||||||
|
// Logic to start the game
|
||||||
|
alert('Game started!'); // Replace with actual game start logic
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert('You need at least one player to start the game.');
|
||||||
|
}
|
||||||
|
}}>Start the game</button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -1,19 +1,72 @@
|
||||||
<script>
|
<script>
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
import { supabase } from '$lib/supabase';
|
||||||
|
|
||||||
|
let pin;
|
||||||
|
let name;
|
||||||
|
let Checking = false;
|
||||||
|
|
||||||
|
async function validateGamePin() {
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('games')
|
||||||
|
.select('gamePIN')
|
||||||
|
.eq('gamePIN', Number(pin))
|
||||||
|
.maybeSingle();
|
||||||
|
|
||||||
|
// Return true if valid
|
||||||
|
return data !== null && !error;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function joinGame() {
|
||||||
|
if (!pin || !name) {
|
||||||
|
alert('Please fill in the game pin and your name.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Checking = true;
|
||||||
|
|
||||||
|
const isValid = await validateGamePin();
|
||||||
|
if (!isValid) {
|
||||||
|
alert('Invalid game pin. Please try again.');
|
||||||
|
Checking = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
goto('/play/' + pin, {
|
||||||
|
state: {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="bg-grey-900 flex h-full items-center justify-center">
|
<div class="bg-grey-900 flex h-full items-center justify-center">
|
||||||
<div class="flex flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg">
|
<div class="flex flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg">
|
||||||
<h1 class="m-[0] mb-3 text-5xl">Join a game here</h1>
|
<h1 class="m-[0] mb-3 text-5xl">Join a game here</h1>
|
||||||
<input placeholder="Enter game pin" class="rounded-lg bg-gray-800 p-2 text-center text-white" />
|
|
||||||
|
<input
|
||||||
|
placeholder="Enter game pin"
|
||||||
|
class="rounded-lg bg-gray-800 p-2 text-center text-white"
|
||||||
|
bind:value={pin}
|
||||||
|
/>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
placeholder="Enter your name"
|
placeholder="Enter your name"
|
||||||
|
bind:value={name}
|
||||||
class="rounded-lg bg-gray-800 p-2 text-center text-white"
|
class="rounded-lg bg-gray-800 p-2 text-center text-white"
|
||||||
/>
|
/>
|
||||||
<button
|
|
||||||
class="cursor-pointer rounded-full bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
|
{#if !Checking}
|
||||||
>Join the game</button
|
<button
|
||||||
>
|
class="mt-4 cursor-pointer rounded-full bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
|
||||||
|
on:click={joinGame}
|
||||||
|
>
|
||||||
|
Join game
|
||||||
|
</button>
|
||||||
|
{:else}<button
|
||||||
|
class="mt-4 cursor-pointer rounded-full bg-gray-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
|
||||||
|
>
|
||||||
|
Checking if pin is valid...
|
||||||
|
</button>{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
6
src/routes/play/[gamePin]/+page.js
Normal file
6
src/routes/play/[gamePin]/+page.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// Example of what your +page.js or +page.server.js might need
|
||||||
|
export function load({ params }) {
|
||||||
|
return {
|
||||||
|
gamePin: params.gamePin
|
||||||
|
};
|
||||||
|
}
|
87
src/routes/play/[gamePin]/+page.svelte
Normal file
87
src/routes/play/[gamePin]/+page.svelte
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
<script>
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
import { supabase } from '$lib/supabase';
|
||||||
|
|
||||||
|
export let data;
|
||||||
|
const gamePin = data.gamePin;
|
||||||
|
const name = $page.state?.name;
|
||||||
|
|
||||||
|
let players = [];
|
||||||
|
|
||||||
|
async function addPlayer() {
|
||||||
|
const { data: gameData, error } = await supabase
|
||||||
|
.from('games')
|
||||||
|
.select('players')
|
||||||
|
.eq('gamePIN', Number(gamePin))
|
||||||
|
.maybeSingle();
|
||||||
|
|
||||||
|
let updatedPlayers = gameData.players || [];
|
||||||
|
|
||||||
|
const alreadyExists = updatedPlayers.some((p) => p.name === name);
|
||||||
|
if (!alreadyExists) {
|
||||||
|
updatedPlayers.push({ name: name, score: 0 });
|
||||||
|
|
||||||
|
const { error: updateError } = await supabase
|
||||||
|
.from('games')
|
||||||
|
.update({ players: updatedPlayers })
|
||||||
|
.eq('gamePIN', Number(gamePin));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe to realtime changes in players
|
||||||
|
function subscribeToPlayers() {
|
||||||
|
const channel = supabase
|
||||||
|
.channel(`game-${gamePin}`)
|
||||||
|
.on(
|
||||||
|
'postgres_changes',
|
||||||
|
{
|
||||||
|
event: 'UPDATE',
|
||||||
|
schema: 'public',
|
||||||
|
table: 'games',
|
||||||
|
filter: `gamePIN=eq.${gamePin}`
|
||||||
|
},
|
||||||
|
(payload) => {
|
||||||
|
players = payload.new.players || [];
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
await addPlayer();
|
||||||
|
|
||||||
|
const { data: gameData } = await supabase
|
||||||
|
.from('games')
|
||||||
|
.select('players')
|
||||||
|
.eq('gamePIN', Number(gamePin))
|
||||||
|
.maybeSingle();
|
||||||
|
|
||||||
|
if (gameData?.players) {
|
||||||
|
players = gameData.players;
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribeToPlayers();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="bg-grey-900 flex h-full items-center justify-center">
|
||||||
|
<div
|
||||||
|
class="flex max-w-[700px] flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg"
|
||||||
|
>
|
||||||
|
<h1 class="m-[0] text-9xl">PLAYING</h1>
|
||||||
|
<h1 class="m-[0] text-7xl">Game Pin:</h1>
|
||||||
|
<h1 class="m-[0] rounded-2xl bg-gray-700 pt-1.5 pr-2 pb-0 pl-2 font-mono text-5xl">
|
||||||
|
{gamePin}
|
||||||
|
</h1>
|
||||||
|
<h1 class="m-[0] mt-10 text-6xl">Players Joined:</h1>
|
||||||
|
<h1 class="m-[0] text-4xl text-gray-400">(Total Players: {players.length})</h1>
|
||||||
|
<div class="mt-2 flex flex-wrap justify-center gap-2">
|
||||||
|
{#each players as player}
|
||||||
|
<span class="m-[0] rounded-xl bg-gray-700 pt-1 pr-2 pb-0 pl-2 font-mono text-3xl"
|
||||||
|
>{player.name}</span
|
||||||
|
>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
Loading…
Add table
Add a link
Reference in a new issue