Merge branch 'master' into hugo
4
Makefile
@@ -1,5 +1,7 @@
|
||||
DOCKERCOMPOSEPATH=./srcs/docker-compose.yml
|
||||
|
||||
all : up
|
||||
|
||||
#dev allow hot reload.
|
||||
up:
|
||||
@bash ./make_env.sh
|
||||
@@ -11,8 +13,6 @@ start:
|
||||
docker compose -f ${DOCKERCOMPOSEPATH} start
|
||||
docker logs --follow nestjs
|
||||
|
||||
all : up
|
||||
|
||||
re: down up
|
||||
|
||||
down:
|
||||
|
||||
@@ -26,7 +26,7 @@ services:
|
||||
volumes:
|
||||
- ./requirements/nestjs/api_back/src:/usr/app/src
|
||||
- ./requirements/nestjs/api_back/test:/usr/app/test/
|
||||
- nestjs_photos_volume:/usr/app/src/uploads/avatars
|
||||
- nestjs_photos_volume:/usr/app/uploads/avatars
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgresql
|
||||
|
||||
@@ -6,6 +6,7 @@ import * as ev from "../../shared_js/class/Event.js"
|
||||
import * as en from "../../shared_js/enums.js"
|
||||
|
||||
export class Client {
|
||||
role: en.ClientRole;
|
||||
socket: WebSocket;
|
||||
id: string; // same as "socket.id"
|
||||
isAlive: boolean = true;
|
||||
@@ -23,9 +24,8 @@ export class ClientPlayer extends Client {
|
||||
inputBuffer: ev.EventInput = new ev.EventInput();
|
||||
lastInputId: number = 0;
|
||||
racket: Racket;
|
||||
constructor(socket: WebSocket, id: string, racket: Racket) {
|
||||
constructor(socket: WebSocket, id: string) {
|
||||
super(socket, id);
|
||||
this.racket = racket;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,6 +76,8 @@ async function clientAnnounceListener(this: WebSocket, data: string)
|
||||
if (msg.role === en.ClientRole.player)
|
||||
{
|
||||
const announce: ev.ClientAnnouncePlayer = <ev.ClientAnnouncePlayer>msg;
|
||||
const player = clientsMap.get(this.id) as ClientPlayer;
|
||||
player.role = msg.role;
|
||||
|
||||
const body = {
|
||||
playerOneUsername: announce.username,
|
||||
@@ -106,8 +108,6 @@ async function clientAnnounceListener(this: WebSocket, data: string)
|
||||
clientTerminate(clientsMap.get(this.id));
|
||||
return;
|
||||
}
|
||||
|
||||
const player = clientsMap.get(this.id) as ClientPlayer;
|
||||
player.matchOptions = announce.matchOptions;
|
||||
player.token = announce.token;
|
||||
player.username = announce.username;
|
||||
@@ -126,13 +126,15 @@ async function clientAnnounceListener(this: WebSocket, data: string)
|
||||
else if (msg.role === en.ClientRole.spectator)
|
||||
{
|
||||
const announce: ev.ClientAnnounceSpectator = <ev.ClientAnnounceSpectator>msg;
|
||||
const spectator = clientsMap.get(this.id) as ClientSpectator;
|
||||
spectator.role = msg.role;
|
||||
|
||||
const gameSession = gameSessionsMap.get(announce.gameSessionId);
|
||||
if (!gameSession) {
|
||||
this.send(JSON.stringify( new ev.EventError("invalid gameSessionId") ));
|
||||
clientTerminate(clientsMap.get(this.id));
|
||||
return;
|
||||
}
|
||||
const spectator = clientsMap.get(this.id) as ClientSpectator;
|
||||
spectator.gameSession = gameSession;
|
||||
gameSession.spectatorsMap.set(spectator.id, spectator);
|
||||
spectator.socket.once("message", spectatorReadyConfirmationListener);
|
||||
@@ -419,7 +421,7 @@ const pingInterval = setInterval( () => {
|
||||
}, 4200);
|
||||
|
||||
|
||||
export function clientTerminate(client: Client)
|
||||
export async function clientTerminate(client: Client)
|
||||
{
|
||||
client.socket.terminate();
|
||||
if (client.gameSession)
|
||||
@@ -439,6 +441,23 @@ export function clientTerminate(client: Client)
|
||||
else if (privateMatchmakingMap.has(client.id)) {
|
||||
privateMatchmakingMap.delete(client.id);
|
||||
}
|
||||
|
||||
if (client.role === en.ClientRole.player)
|
||||
{
|
||||
const player = client as ClientPlayer;
|
||||
console.log("/resetuserstatus " + player.username);
|
||||
const response = await fetch(c.addressBackEnd + "/game/gameserver/resetuserstatus",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({username: player.username})
|
||||
});
|
||||
if (!response.ok) {
|
||||
console.log("/resetuserstatus " + player.username + " failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Response } from 'express';
|
||||
import { TwoFaDto } from './dto/2fa.dto';
|
||||
import { UsersService } from 'src/users/users.service';
|
||||
import { User } from 'src/users/entities/user.entity';
|
||||
import { STATUS } from 'src/common/constants/constants';
|
||||
|
||||
@Controller('auth')
|
||||
export class AuthenticationController {
|
||||
@@ -36,6 +37,7 @@ export class AuthenticationController {
|
||||
console.log('On redirige');
|
||||
const user : User = request.user
|
||||
if (user.isEnabledTwoFactorAuth === false || user.isTwoFactorAuthenticated === true){
|
||||
this.userService.updateStatus(user.id, STATUS.CONNECTED)
|
||||
console.log('ON VA VERS PROFILE');
|
||||
return response.status(200).redirect('http://' + process.env.WEBSITE_HOST + ':' + process.env.WEBSITE_PORT + '/#/profile');
|
||||
}
|
||||
@@ -51,7 +53,7 @@ export class AuthenticationController {
|
||||
@UseGuards(AuthenticateGuard)
|
||||
logout(@Req() request, @Res() response, @Next() next) {
|
||||
this.userService.setIsTwoFactorAuthenticatedWhenLogout(request.user.id);
|
||||
this.userService.updateStatus(request.user.id, 'disconnected');
|
||||
this.userService.updateStatus(request.user.id, STATUS.DISCONNECTED);
|
||||
request.logout(function(err) {
|
||||
if (err) { return next(err); }
|
||||
response.redirect('/');
|
||||
@@ -83,6 +85,7 @@ export class AuthenticationController {
|
||||
throw new UnauthorizedException('Wrong Code.');
|
||||
await this.userService.authenticateUserWith2FA(request.user.id);
|
||||
console.log('ON REDIRIGE');
|
||||
this.userService.updateStatus(user.id, STATUS.CONNECTED)
|
||||
return response.status(200).redirect('http://' + process.env.WEBSITE_HOST + ':' + process.env.WEBSITE_PORT + '/#/profile');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,4 +101,9 @@ export class GameController {
|
||||
{
|
||||
return this.gameService.destroySession(token);
|
||||
}
|
||||
|
||||
@Post('gameserver/resetuserstatus')
|
||||
async resetUserStatus(@Body('username') username){
|
||||
this.gameService.resetStatus(username);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,12 +92,17 @@ export class GameService {
|
||||
}
|
||||
this.userRepository.save(user);
|
||||
}
|
||||
// if (grantTicketDto.isGameIsWithInvitation === true && user.status !== STATUS.IN_GAME) // WIP: need to fix STATUS.IN_GAME
|
||||
if (grantTicketDto.isGameIsWithInvitation === true)
|
||||
{
|
||||
const secondUser : Partial<User> = await this.userService.findOne(grantTicketDto.playerTwoUsername)
|
||||
if (!secondUser || secondUser.username === user.username)
|
||||
return res.status(HttpStatus.NOT_FOUND).json({message : "User not found OR you want to play with yourself."});
|
||||
if (grantTicketDto.playerTwoUsername === user.username) {
|
||||
return res.status(HttpStatus.BAD_REQUEST).json({message : "You cant play against yourself."});
|
||||
}
|
||||
const secondUser : User = await this.userRepository.createQueryBuilder('user')
|
||||
.where("user.username = :username", {username : grantTicketDto.playerTwoUsername})
|
||||
.getOne();
|
||||
if (!secondUser) {
|
||||
return res.status(HttpStatus.NOT_FOUND).json({message : "Invited user not found"});
|
||||
}
|
||||
const encryptedTextToReturn = await this.encryptToken(user.username + '_' + secondUser.username + '_'
|
||||
+ grantTicketDto.gameOptions + '_' + grantTicketDto.isGameIsWithInvitation + '_' + new Date())
|
||||
const tok = this.tokenGameRepository.create(grantTicketDto);
|
||||
@@ -106,9 +111,9 @@ export class GameService {
|
||||
tok.token = encryptedTextToReturn;
|
||||
this.tokenGameRepository.save(tok);
|
||||
this.userService.updateStatus(user.id, STATUS.IN_POOL)
|
||||
this.userService.updateStatus(secondUser.id, STATUS.IN_POOL)
|
||||
return res.status(HttpStatus.OK).json({ token : encryptedTextToReturn });
|
||||
}
|
||||
// else if (grantTicketDto.isGameIsWithInvitation === false && user.status !== STATUS.IN_GAME) { // WIP: need to fix STATUS.IN_GAME
|
||||
else if (grantTicketDto.isGameIsWithInvitation === false) {
|
||||
const encryptedTextToReturn = await this.encryptToken(user.username + '_'
|
||||
+ grantTicketDto.gameOptions + '_' + grantTicketDto.isGameIsWithInvitation + '_' + new Date())
|
||||
@@ -142,13 +147,11 @@ export class GameService {
|
||||
const userOne : User = await this.userRepository.createQueryBuilder('user')
|
||||
.where("user.username = :username", {username : tokenGame.playerOneUsername})
|
||||
.getOne();
|
||||
this.userService.updateStatus(userOne.id, STATUS.IN_GAME)
|
||||
const userTwo : User = await this.userRepository.createQueryBuilder('user')
|
||||
.where("user.username = :username", {username : tokenGame.playerTwoUsername})
|
||||
.getOne();
|
||||
this.deleteToken(userOne)
|
||||
this.deleteToken(userTwo)
|
||||
this.userService.updateStatus(userTwo.id, STATUS.IN_GAME)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -168,7 +171,6 @@ export class GameService {
|
||||
const user : User = await this.userRepository.createQueryBuilder('user')
|
||||
.where("user.username = :username", {username : tokenGame.playerOneUsername})
|
||||
.getOne();
|
||||
this.userService.updateStatus(user.id, STATUS.IN_GAME)
|
||||
this.deleteToken(user)
|
||||
return true;
|
||||
}
|
||||
@@ -259,6 +261,14 @@ export class GameService {
|
||||
this.gameRepository.save(game);
|
||||
if (!game)
|
||||
return HttpStatus.INTERNAL_SERVER_ERROR
|
||||
const playerOne : User = await this.userRepository.createQueryBuilder('user')
|
||||
.where("user.username = :username", {username : creategameDto.playerOneUsername})
|
||||
.getOne();
|
||||
const playerTwo : User = await this.userRepository.createQueryBuilder('user')
|
||||
.where("user.username = :username", {username : creategameDto.playerTwoUsername})
|
||||
.getOne();
|
||||
this.userService.updateStatus(playerOne.id, STATUS.IN_GAME)
|
||||
this.userService.updateStatus(playerTwo.id, STATUS.IN_GAME)
|
||||
console.log("200 retourné pour la création de partie")
|
||||
return HttpStatus.OK
|
||||
}
|
||||
@@ -280,6 +290,8 @@ export class GameService {
|
||||
const playerTwo = await this.userRepository.findOneBy({username : game.playerTwoUsername})
|
||||
if (!playerOne || !playerTwo)
|
||||
return new HttpException("Internal Server Error. Impossible to update the database", HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
this.userService.updateStatus(playerOne.id, STATUS.CONNECTED);
|
||||
this.userService.updateStatus(playerTwo.id, STATUS.CONNECTED);
|
||||
if (game.playerOneUsernameResult === game.playerTwoUsernameResult)
|
||||
{
|
||||
this.userService.incrementDraws(playerOne.id)
|
||||
@@ -296,9 +308,14 @@ export class GameService {
|
||||
this.userService.incrementVictories(playerOne.id)
|
||||
this.userService.incrementDefeats(playerTwo.id)
|
||||
}
|
||||
this.userService.updateStatus(playerOne.id, STATUS.CONNECTED)
|
||||
this.userService.updateStatus(playerTwo.id, STATUS.CONNECTED)
|
||||
return HttpStatus.OK
|
||||
}
|
||||
|
||||
async resetStatus(username : string){
|
||||
const user : User = await this.userRepository.findOneBy({username : username})
|
||||
if (!user)
|
||||
return HttpStatus.NOT_FOUND;
|
||||
this.userService.updateStatus(user.id, STATUS.CONNECTED);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,11 +13,6 @@ body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
/* tmp? */
|
||||
background: bisque;
|
||||
|
||||
/* overflow-x: hidden; */
|
||||
/* This seems to have fixed my pages that are too long */
|
||||
/* but now i can't scroll anywhere ... */
|
||||
/* overflow-y: hidden; */
|
||||
}
|
||||
|
||||
a {
|
||||
|
||||
BIN
srcs/requirements/svelte/api_front/public/img/BACKGROUND.png
Normal file
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 407 KiB |
|
Before Width: | Height: | Size: 675 KiB |
|
Before Width: | Height: | Size: 20 KiB |
BIN
srcs/requirements/svelte/api_front/public/img/logo_potato.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
srcs/requirements/svelte/api_front/public/img/potato_only.png
Normal file
|
After Width: | Height: | Size: 181 KiB |
|
Before Width: | Height: | Size: 430 KiB |
@@ -1 +0,0 @@
|
||||
<svg id="visual" viewBox="0 0 900 150" width="900" height="150" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"><path d="M0 36L21.5 33.7C43 31.3 86 26.7 128.8 32.5C171.7 38.3 214.3 54.7 257.2 63.5C300 72.3 343 73.7 385.8 65.7C428.7 57.7 471.3 40.3 514.2 30.5C557 20.7 600 18.3 642.8 25.8C685.7 33.3 728.3 50.7 771.2 59.5C814 68.3 857 68.7 878.5 68.8L900 69L900 0L878.5 0C857 0 814 0 771.2 0C728.3 0 685.7 0 642.8 0C600 0 557 0 514.2 0C471.3 0 428.7 0 385.8 0C343 0 300 0 257.2 0C214.3 0 171.7 0 128.8 0C86 0 43 0 21.5 0L0 0Z" fill="#618174" stroke-linecap="round" stroke-linejoin="miter"></path></svg>
|
||||
|
Before Width: | Height: | Size: 645 B |
@@ -9,22 +9,8 @@
|
||||
user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`)
|
||||
.then((resp) => resp.json())
|
||||
|
||||
// i mean i could do a failed to load user or some shit, maybe with a .catch or something? but atm why bother
|
||||
|
||||
console.log('User var');
|
||||
console.log(user);
|
||||
// if (user && user.statusCode && user.statusCode === 403) {
|
||||
// console.log('user not logged in')
|
||||
// }
|
||||
// if (user && user.username) {
|
||||
// console.log('we have a user');
|
||||
// }
|
||||
|
||||
// user === undefined doesn't work cuz we declared user in the scope of onMount
|
||||
// if (user === undefined) {
|
||||
if (user && user.statusCode && user.statusCode === 403) {
|
||||
console.log('on mount no user, returned status code 403 so logging out of userStore')
|
||||
// userLogout(); // which i think should delete any previous local storage
|
||||
}
|
||||
|
||||
});
|
||||
@@ -34,8 +20,6 @@
|
||||
console.log('you are now logged in');
|
||||
}
|
||||
|
||||
// i could prolly put this in it's own compoent, i seem to use it in several places... or maybe just some JS? like no need for html
|
||||
// we could .then( () => replace('/') ) need the func so TS compatible...
|
||||
const logout = async() => {
|
||||
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/auth/logout`, {
|
||||
method: 'POST',
|
||||
@@ -46,108 +30,71 @@
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<header class="grid-container">
|
||||
<h1>Potato Pong</h1>
|
||||
<nav>
|
||||
<!-- {#if user !== undefined} -->
|
||||
{#if user && user.username}
|
||||
<div on:click={ () => (push('/profile')) }>Profile</div>
|
||||
<div on:click={logout}>Logout</div>
|
||||
{:else}
|
||||
<div on:click={login}>Login</div>
|
||||
{/if}
|
||||
</nav>
|
||||
<h2>
|
||||
<div>Welcome to</div>
|
||||
<div>Potato Pong</div>
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<Canvas/>
|
||||
|
||||
<!-- doesn't work :( -->
|
||||
<!-- <svelte:body style="overflow-y: hidden"/> -->
|
||||
<div class="container">
|
||||
<div class="splash-page">
|
||||
{#if user && user.username}
|
||||
<button class="button-in" on:click={ () => (push('/profile')) }>Profile</button>
|
||||
<button class="button-out" on:click={logout}>Logout</button>
|
||||
{:else}
|
||||
<button class="button-in" on:click={login}>Login</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Bit5x3";
|
||||
src:
|
||||
url("/fonts/Bit5x3.woff2") format("woff2"),
|
||||
local("Bit5x3"),
|
||||
url("/fonts/Bit5x3.woff") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/* :global(body) {
|
||||
overflow-y: hidden;
|
||||
} */
|
||||
|
||||
header {
|
||||
/* didn't work... */
|
||||
overflow-y: hidden;
|
||||
}
|
||||
.container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
background-image: url('/img/SPLASH_PAGE_BACKGROUND.png');
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
|
||||
/* The actually important stuff */
|
||||
.splash-page {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 80%;
|
||||
-ms-transform: translateY(-80%);
|
||||
transform: translateY(-80%);
|
||||
left: 50%;
|
||||
-ms-transform: translateX(-50%);
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.grid-container{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
.button-in {
|
||||
background-color: #8c0000;
|
||||
border-color: black;
|
||||
border-width: 4px;
|
||||
color: white;
|
||||
font-family: "Bit5x3";
|
||||
font-size: x-large;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/* max-height: 100%; */
|
||||
white-space: nowrap;
|
||||
/* padding-bottom: 0; */
|
||||
margin-bottom: 0px;
|
||||
overflow: hidden;
|
||||
padding: 20px 40px;
|
||||
margin: 0px;
|
||||
.button-out {
|
||||
background-color: #008c8c;
|
||||
border-color: black;
|
||||
border-width: 4px;
|
||||
color: white;
|
||||
font-family: "Bit5x3";
|
||||
font-size: x-large;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
grid-template-rows: 1fr 1fr 1fr 1fr 1fr;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
header h1{
|
||||
grid-column: 1 / 7;
|
||||
grid-row: 1;
|
||||
/* grid-column: span 6; */
|
||||
/* tmp? */
|
||||
padding: 20px;
|
||||
border: 1px solid bisque;
|
||||
}
|
||||
header nav{
|
||||
/* make it a flexbox? */
|
||||
grid-column: 7 / 13;
|
||||
grid-row: 1;
|
||||
justify-self: end;
|
||||
/* tmp? */
|
||||
padding: 20px;
|
||||
border: 1px solid bisque;
|
||||
}
|
||||
|
||||
header h2{
|
||||
grid-row: 3;
|
||||
grid-column: 5 / span 4;
|
||||
justify-self: center;
|
||||
/* tmp */
|
||||
border: 1px solid black;
|
||||
z-index: 3;
|
||||
}
|
||||
header h2 div{
|
||||
font-size: 2em;
|
||||
/* color: red; */
|
||||
}
|
||||
|
||||
nav div {
|
||||
display: inline;
|
||||
color: bisque;
|
||||
font-weight: bold;
|
||||
}
|
||||
nav > div {
|
||||
padding-left: 1em;
|
||||
/* didn't quite work, applies to both for some reason */
|
||||
/* nav:first-child doesn't work either*/
|
||||
}
|
||||
nav div:hover{
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -268,25 +268,34 @@
|
||||
<button class="pong_button" on:click={fetchInvitations}>Show invitations</button>
|
||||
<fieldset in:fly="{{ y: 10, duration: 1000 }}">
|
||||
<legend>game options</legend>
|
||||
<div>
|
||||
|
||||
<label for="multi_balls">
|
||||
<input type="checkbox" id="multi_balls" name="multi_balls" bind:checked={options.multi_balls}>
|
||||
<label for="multi_balls">Multiples balls</label>
|
||||
</div>
|
||||
<div>
|
||||
Multiples balls
|
||||
</label>
|
||||
|
||||
<label for="moving_walls">
|
||||
<input type="checkbox" id="moving_walls" name="moving_walls" bind:checked={options.moving_walls}>
|
||||
<label for="moving_walls">Moving walls</label>
|
||||
</div>
|
||||
Moving walls
|
||||
</label>
|
||||
|
||||
<div>
|
||||
<p>sound :</p>
|
||||
<input type="radio" id="sound_on" name="sound_selector" bind:group={options.sound} value="on">
|
||||
<label for="sound_on">on</label>
|
||||
<input type="radio" id="sound_off" name="sound_selector" bind:group={options.sound} value="off">
|
||||
<label for="sound_off">off</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="isSomeoneIsInvited" bind:checked={options.isSomeoneIsInvited}>
|
||||
<label for="moving_walls">Invite a friend</label>
|
||||
sound :
|
||||
<label for="sound_on">
|
||||
<input type="radio" id="sound_on" name="sound_selector" bind:group={options.sound} value="on">
|
||||
on
|
||||
</label>
|
||||
<label for="sound_off">
|
||||
<input type="radio" id="sound_off" name="sound_selector" bind:group={options.sound} value="off">
|
||||
off
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<label for="invitation_checkbox">
|
||||
<input type="checkbox" id="invitation_checkbox" bind:checked={options.isSomeoneIsInvited}>
|
||||
Invite a friend
|
||||
</label>
|
||||
|
||||
{#if options.isSomeoneIsInvited}
|
||||
<select bind:value={options.playerTwoUsername}>
|
||||
{#each allUsers as user }
|
||||
|
||||
@@ -131,13 +131,19 @@
|
||||
<fieldset>
|
||||
<legend>options</legend>
|
||||
<button class="pong_button" on:click={fetchMatchList}>Reload</button>
|
||||
|
||||
<div>
|
||||
<p>sound :</p>
|
||||
<input type="radio" id="sound_on" name="sound_selector" bind:group={sound} value="on">
|
||||
<label for="sound_on">on</label>
|
||||
<input type="radio" id="sound_off" name="sound_selector" bind:group={sound} value="off">
|
||||
<label for="sound_off">off</label>
|
||||
sound :
|
||||
<label for="sound_on">
|
||||
<input type="radio" id="sound_on" name="sound_selector" bind:group={sound} value="on">
|
||||
on
|
||||
</label>
|
||||
<label for="sound_off">
|
||||
<input type="radio" id="sound_off" name="sound_selector" bind:group={sound} value="off">
|
||||
off
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
{#if matchList.length !== 0}
|
||||
<menu id="match_list">
|
||||
|
||||