bring back host from the main branch
This commit is contained in:
parent
3b254c1aa1
commit
d584e58207
19 changed files with 395 additions and 0 deletions
29
src/routes/kahootclone/host/+page.svelte
Normal file
29
src/routes/kahootclone/host/+page.svelte
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<script>
|
||||||
|
import PlayingDisplay from "./components/DuringGame/display.svelte";
|
||||||
|
|
||||||
|
import LobbyDisplay from "./components/lobby/display.svelte";
|
||||||
|
import { Status,gamePin } from "./logic/HostsData.svelte.js";
|
||||||
|
import { AutoUpdatePlayersList } from "./logic/UpdatePlayersList.js";
|
||||||
|
import { GetCurrentPlayers } from "./logic/GetCurrentPlayers.js";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
gamePin.v = new URLSearchParams(new URL(window.location.href).search).get("gamepin");
|
||||||
|
|
||||||
|
|
||||||
|
GetCurrentPlayers(gamePin.v);
|
||||||
|
AutoUpdatePlayersList(gamePin.v);
|
||||||
|
});
|
||||||
|
</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"
|
||||||
|
>
|
||||||
|
{#if Status.v == "lobby"}
|
||||||
|
<LobbyDisplay/>
|
||||||
|
{:else if Status.v == "started"}
|
||||||
|
<PlayingDisplay />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,13 @@
|
||||||
|
<script>
|
||||||
|
import { PeopleAwnseredQ, Totalplayers } from "../../logic/HostsData.svelte.js";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="mt-2 mb-3 flex w-full flex-col rounded-2xl border-2 border-green-400 p-2">
|
||||||
|
<h3>{PeopleAwnseredQ.v} out of {Totalplayers.v} have answered the question</h3>
|
||||||
|
<div class="flex-1 rounded-full border-2 border-gray-600">
|
||||||
|
<div
|
||||||
|
class="h-4 rounded-full bg-green-600 transition-all duration-500"
|
||||||
|
style="width: {(PeopleAwnseredQ.v / Totalplayers.v) * 100}%;"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,24 @@
|
||||||
|
<script>
|
||||||
|
import { CurrentQuestionDetails } from "../../../logic/HostsData.svelte.js";
|
||||||
|
|
||||||
|
import { AnswersSymbolAndColorScheme } from "$lib/config.js";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="mt-5 grid grid-cols-2 gap-5 gap-x-3">
|
||||||
|
{#each CurrentQuestionDetails.v.answers as answer, index}
|
||||||
|
<div class="flex">
|
||||||
|
<input type="radio" name="question" class="sr-only" />
|
||||||
|
<label
|
||||||
|
for="O{index}"
|
||||||
|
style="
|
||||||
|
--border-color: {AnswersSymbolAndColorScheme[index].Color};
|
||||||
|
--bg-color: {AnswersSymbolAndColorScheme[index].Color};
|
||||||
|
"
|
||||||
|
class="w-full rounded-lg border-[5px] border-[var(--border-color)] bg-[var(--bg-color)] pt-1 pr-2 pb-1 pl-2 text-center text-3xl transition-all"
|
||||||
|
>
|
||||||
|
<i class="nf {AnswersSymbolAndColorScheme[index].Symbol}"></i>
|
||||||
|
{answer}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
|
@ -0,0 +1,13 @@
|
||||||
|
<script>
|
||||||
|
import { currentQuestion, totalQuetions } from "../../../logic/HostsData.svelte.js";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="mt-5 mb-2 flex w-full items-center justify-center gap-3">
|
||||||
|
<h3>Question {currentQuestion.v + 1} of {totalQuetions.v}</h3>
|
||||||
|
<div class="flex-1 rounded-full border-2 border-gray-600">
|
||||||
|
<div
|
||||||
|
class="h-4 rounded-full bg-green-600 transition-all duration-700"
|
||||||
|
style="width: {(currentQuestion.v / totalQuetions.v) * 100}%;"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script>
|
||||||
|
import Question from "./text/Quetion.svelte";
|
||||||
|
import Awnsers from "./Awnsers.svelte";
|
||||||
|
import ProgressBar from "./ProgressBar.svelte";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ProgressBar />
|
||||||
|
<Question />
|
||||||
|
|
||||||
|
|
||||||
|
<Awnsers />
|
|
@ -0,0 +1,24 @@
|
||||||
|
<script>
|
||||||
|
import { currentQuestion, CurrentQuestionDetails } from "../../../../logic/HostsData.svelte.js";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1 class="m-[0] text-center text-5xl">
|
||||||
|
Q{currentQuestion.v + 1}. {CurrentQuestionDetails.v.question}
|
||||||
|
</h1>
|
||||||
|
{#if CurrentQuestionDetails.v.media != null}
|
||||||
|
<div class="mb-3 flex items-center justify-center">
|
||||||
|
{#if CurrentQuestionDetails.v.media.match(/\.(mp4|webm|ogg|mov|avi|mkv)$/i)}
|
||||||
|
<video
|
||||||
|
src={CurrentQuestionDetails.v.media}
|
||||||
|
class="max-h-[300px] max-w-[500px] rounded-lg"
|
||||||
|
controls
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<img
|
||||||
|
src={CurrentQuestionDetails.v.media}
|
||||||
|
class="max-h-[300px] max-w-[500px] rounded-lg"
|
||||||
|
alt="Question media"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<script>
|
||||||
|
import PeopleAwnsered from "./PeopleAwnsered.svelte";
|
||||||
|
import Display from "./awnseringQuetions/display.svelte";
|
||||||
|
import TimeLeft from "./timeLeft.svelte";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1 class="m-[0] text-7xl">HOSTING</h1>
|
||||||
|
<Display />
|
||||||
|
<PeopleAwnsered />
|
||||||
|
<TimeLeft />
|
|
@ -0,0 +1,15 @@
|
||||||
|
<script>
|
||||||
|
import { TotalTimeLeft, timeLeft } from "../../logic/HostsData.svelte.js";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if TotalTimeLeft.v != null}
|
||||||
|
<div class="mt-2 mb-3 flex w-full flex-col rounded-2xl border-2 border-green-400 p-2">
|
||||||
|
<h3>{timeLeft.v}sec out of {TotalTimeLeft.v}secs is left</h3>
|
||||||
|
<div class="flex-1 rounded-full border-2 border-gray-600">
|
||||||
|
<div
|
||||||
|
class="h-4 rounded-full bg-green-600 transition-all duration-500"
|
||||||
|
style="width: {(timeLeft.v / TotalTimeLeft.v) * 100}%;"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
|
@ -0,0 +1,6 @@
|
||||||
|
<script>
|
||||||
|
let props = $props();
|
||||||
|
let playerName = props.playerName;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<span class="m-[0] rounded-xl bg-gray-700 pt-1 pr-2 pb-0 pl-2 font-mono text-3xl">{playerName}</span>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<script>
|
||||||
|
import { players } from "../../../logic/HostsData.svelte.js";
|
||||||
|
import PlayerBadge from "./playerBadge.svelte";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1 class="m-[0] mt-10 text-6xl">Players Joined:</h1>
|
||||||
|
<h1 class="m-[0] text-4xl text-gray-400">(Total Players: {players.v.length})</h1>
|
||||||
|
<div class="mt-2 flex flex-wrap justify-center gap-2">
|
||||||
|
{#each players.v as playerName}
|
||||||
|
<PlayerBadge {playerName} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<script>
|
||||||
|
import { startGame } from "../../../logic/startGame.js";
|
||||||
|
|
||||||
|
let props = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="mt-5 cursor-pointer rounded-2xl bg-green-700 p-2 text-4xl transition-all hover:scale-110 hover:-rotate-10"
|
||||||
|
onclick={() => {
|
||||||
|
startGame(props.gamePin);
|
||||||
|
}}>Start the game</button
|
||||||
|
>
|
15
src/routes/kahootclone/host/components/lobby/display.svelte
Normal file
15
src/routes/kahootclone/host/components/lobby/display.svelte
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<script>
|
||||||
|
import StartGame from "./buttons/startGame.svelte";
|
||||||
|
import Players from "./PlayersGUI/players.svelte";
|
||||||
|
|
||||||
|
import { gamePin } from "../../logic/HostsData.svelte.js";
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<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.v}
|
||||||
|
</h1>
|
||||||
|
<Players />
|
||||||
|
<StartGame gamePin={gamePin.v} />
|
7
src/routes/kahootclone/host/logic/GameOver.js
Normal file
7
src/routes/kahootclone/host/logic/GameOver.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { supabase } from "$lib/supabase.js";
|
||||||
|
|
||||||
|
export async function GameOver(GamePin) {
|
||||||
|
await supabase.from("games").update({ status: `completed` }).eq("gamepin", GamePin);
|
||||||
|
|
||||||
|
window.location.replace("/kahootclone/results?gamepin=" + GamePin + "&playerID=host-null");
|
||||||
|
}
|
17
src/routes/kahootclone/host/logic/GetCurrentPlayers.js
Normal file
17
src/routes/kahootclone/host/logic/GetCurrentPlayers.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { supabase } from "$lib/supabase.js";
|
||||||
|
import toast from "svelte-5-french-toast";
|
||||||
|
import { players } from "./HostsData.svelte.js";
|
||||||
|
|
||||||
|
export async function GetCurrentPlayers(gamePin) {
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from("players")
|
||||||
|
.select("playername")
|
||||||
|
.eq("gameid", Number(gamePin));
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
toast.error("Error fetching players: " + error.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
players.v = data ? data.map((player) => player.playername) : [];
|
||||||
|
}
|
15
src/routes/kahootclone/host/logic/HostsData.svelte.js
Normal file
15
src/routes/kahootclone/host/logic/HostsData.svelte.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
export let players = $state({ v: [] });
|
||||||
|
export let Status = $state({ v: "lobby" });
|
||||||
|
export let questions = { v: {} };
|
||||||
|
|
||||||
|
export let currentQuestion = $state({ v: 0 });
|
||||||
|
export let totalQuetions = $state({ v: 3 });
|
||||||
|
export let PeopleAwnseredQ = $state({ v: 0 });
|
||||||
|
export let Totalplayers = $state({ v: 3 });
|
||||||
|
|
||||||
|
export let CurrentQuestionDetails = $state({ v: {} });
|
||||||
|
|
||||||
|
export let gamePin = $state({ v: "" });
|
||||||
|
|
||||||
|
export let timeLeft = $state({ v: 0 });
|
||||||
|
export let TotalTimeLeft = $state({ v: 0 });
|
26
src/routes/kahootclone/host/logic/UpdatePlayersList.js
Normal file
26
src/routes/kahootclone/host/logic/UpdatePlayersList.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { supabase } from "$lib/supabase.js";
|
||||||
|
import { players } from "./HostsData.svelte.js";
|
||||||
|
|
||||||
|
export let LobbyConnection;
|
||||||
|
|
||||||
|
function onNewPlayer(Newplayers) {
|
||||||
|
players.v.push(Newplayers.playername);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function AutoUpdatePlayersList(gamePin) {
|
||||||
|
LobbyConnection = supabase
|
||||||
|
.channel("players-realtime")
|
||||||
|
.on(
|
||||||
|
"postgres_changes",
|
||||||
|
{
|
||||||
|
event: "INSERT",
|
||||||
|
schema: "public",
|
||||||
|
table: "players",
|
||||||
|
filter: `gameid=eq.${gamePin}`,
|
||||||
|
},
|
||||||
|
(payload) => {
|
||||||
|
onNewPlayer(payload.new);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
}
|
85
src/routes/kahootclone/host/logic/WaitForAwnser.js
Normal file
85
src/routes/kahootclone/host/logic/WaitForAwnser.js
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import { supabase } from "$lib/supabase.js";
|
||||||
|
import { onNewPlayerAwnsered } from "./onNewPlayerAwnsered.js";
|
||||||
|
import {
|
||||||
|
currentQuestion,
|
||||||
|
questions,
|
||||||
|
CurrentQuestionDetails,
|
||||||
|
TotalTimeLeft,
|
||||||
|
timeLeft,
|
||||||
|
PeopleAwnseredQ,
|
||||||
|
totalQuetions,
|
||||||
|
} from "./HostsData.svelte.js";
|
||||||
|
import { GameOver } from "./GameOver.js";
|
||||||
|
|
||||||
|
let WaitingForAwnserConection;
|
||||||
|
let TimeLimitInterval;
|
||||||
|
|
||||||
|
export async function WaitForAwnser(questionid, gamePin) {
|
||||||
|
if (questionid != 0) {
|
||||||
|
await supabase.removeChannel(WaitingForAwnserConection);
|
||||||
|
clearInterval(TimeLimitInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
await supabase
|
||||||
|
.from("games")
|
||||||
|
.update({ status: `question-${currentQuestion.v}` })
|
||||||
|
.eq("gamepin", gamePin);
|
||||||
|
|
||||||
|
WaitingForAwnserConection = supabase
|
||||||
|
.channel("answeredby-realtime")
|
||||||
|
.on(
|
||||||
|
"postgres_changes",
|
||||||
|
{
|
||||||
|
event: "INSERT",
|
||||||
|
schema: "public",
|
||||||
|
table: "answeredby",
|
||||||
|
filter: `questionid=eq.${questions.v[questionid].id}`,
|
||||||
|
},
|
||||||
|
(payload) => {
|
||||||
|
onNewPlayerAwnsered(gamePin);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
|
||||||
|
const { data: questionsData } = await supabase
|
||||||
|
.from("questions")
|
||||||
|
.select("*")
|
||||||
|
.eq("gameid", Number(gamePin))
|
||||||
|
.order("id", { ascending: true });
|
||||||
|
|
||||||
|
const { data: answers } = await supabase
|
||||||
|
.from("answers")
|
||||||
|
.select("content")
|
||||||
|
.eq("questionid", Number(questionsData[currentQuestion.v].id))
|
||||||
|
.order("id", { ascending: true });
|
||||||
|
|
||||||
|
CurrentQuestionDetails.v = {
|
||||||
|
question: questionsData[currentQuestion.v].questionstext,
|
||||||
|
correctAnswer: questionsData[currentQuestion.v].correctanswer,
|
||||||
|
answers: answers.map((answer) => answer.content),
|
||||||
|
questionid: questionsData[currentQuestion.v].id,
|
||||||
|
media: questionsData[currentQuestion.v].media || null,
|
||||||
|
timeLimit: questionsData[currentQuestion.v].timelimit,
|
||||||
|
};
|
||||||
|
|
||||||
|
TotalTimeLeft.v = CurrentQuestionDetails.v.timeLimit;
|
||||||
|
timeLeft.v = CurrentQuestionDetails.v.timeLimit;
|
||||||
|
|
||||||
|
if (TotalTimeLeft.v != null) {
|
||||||
|
TimeLimitInterval = setInterval(() => {
|
||||||
|
if (timeLeft.v > 0) {
|
||||||
|
timeLeft.v--;
|
||||||
|
} else {
|
||||||
|
supabase.removeChannel(WaitingForAwnserConection);
|
||||||
|
currentQuestion.v++;
|
||||||
|
if (currentQuestion.v == totalQuetions.v) {
|
||||||
|
//GameOver(gamePin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PeopleAwnseredQ.v = 0;
|
||||||
|
|
||||||
|
WaitForAwnser(currentQuestion.v, gamePin);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
23
src/routes/kahootclone/host/logic/onNewPlayerAwnsered.js
Normal file
23
src/routes/kahootclone/host/logic/onNewPlayerAwnsered.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import {
|
||||||
|
Totalplayers,
|
||||||
|
PeopleAwnseredQ,
|
||||||
|
currentQuestion,
|
||||||
|
totalQuetions,
|
||||||
|
} from "./HostsData.svelte.js";
|
||||||
|
import { GameOver } from "./GameOver.js";
|
||||||
|
import { WaitForAwnser } from "./WaitForAwnser.js";
|
||||||
|
|
||||||
|
export async function onNewPlayerAwnsered(GamePin) {
|
||||||
|
PeopleAwnseredQ.v++;
|
||||||
|
|
||||||
|
if (PeopleAwnseredQ.v == Totalplayers.v) {
|
||||||
|
currentQuestion.v++;
|
||||||
|
if (currentQuestion.v == totalQuetions.v) {
|
||||||
|
GameOver(GamePin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PeopleAwnseredQ.v = 0;
|
||||||
|
|
||||||
|
WaitForAwnser(currentQuestion.v, GamePin);
|
||||||
|
}
|
||||||
|
}
|
38
src/routes/kahootclone/host/logic/startGame.js
Normal file
38
src/routes/kahootclone/host/logic/startGame.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { supabase } from "$lib/supabase.js";
|
||||||
|
import { LobbyConnection } from "./UpdatePlayersList.js";
|
||||||
|
import { questions, Status, Totalplayers, totalQuetions, players } from "./HostsData.svelte.js";
|
||||||
|
import { WaitForAwnser } from "./WaitForAwnser.js";
|
||||||
|
import toast from "svelte-5-french-toast";
|
||||||
|
|
||||||
|
export async function startGame(gamePin) {
|
||||||
|
if (players.v.length == 0) {
|
||||||
|
toast.error("you need at least 1 person to start the game!");
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
await supabase.removeChannel(LobbyConnection);
|
||||||
|
|
||||||
|
Status.v = "started";
|
||||||
|
|
||||||
|
const { data } = await supabase
|
||||||
|
.from("questions")
|
||||||
|
.select("*")
|
||||||
|
.eq("gameid", Number(gamePin))
|
||||||
|
.order("id", { ascending: true });
|
||||||
|
|
||||||
|
questions.v = data;
|
||||||
|
|
||||||
|
totalQuetions.v = data.length;
|
||||||
|
|
||||||
|
const { data: playersData } = await supabase
|
||||||
|
.from("players")
|
||||||
|
.select("id")
|
||||||
|
.eq("gameid", Number(gamePin))
|
||||||
|
.order("id", { ascending: true });
|
||||||
|
|
||||||
|
Totalplayers.v = playersData.length;
|
||||||
|
|
||||||
|
WaitForAwnser(0, gamePin);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue