seems like the merge worked
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
// routing
|
||||
// may not need {link} here
|
||||
import Router, { link, replace } from "svelte-spa-router";
|
||||
import { primaryRoutes } from "./routes/primaryRoutes.js";
|
||||
import { primaryRoutes } from "./routes/primaryRoutes.js";
|
||||
// import primaryRoutes from "./routes/primaryRoutes.svelte";
|
||||
|
||||
const conditionsFailed = (event) => {
|
||||
@@ -12,22 +12,8 @@
|
||||
replace('/unauthorized-access');
|
||||
};
|
||||
|
||||
// this might be the part where we get rid of localstorage when the app is quit?
|
||||
// onDestroy()
|
||||
// maybe done with cookie
|
||||
|
||||
</script>
|
||||
|
||||
<!-- <h1>Testing</h1> -->
|
||||
<Router routes={primaryRoutes} on:conditionsFailed={conditionsFailed}/>
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
/* doesn't work... */
|
||||
/* body{
|
||||
background: bisque;
|
||||
} */
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
12
srcs/requirements/svelte/api_front/src/main.js
Normal file
12
srcs/requirements/svelte/api_front/src/main.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import App from './App.svelte';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
const app = new App({
|
||||
target: document.body,
|
||||
props: {
|
||||
// name: 'world'
|
||||
}
|
||||
});
|
||||
export default app;
|
||||
//# sourceMappingURL=main.js.map
|
||||
1
srcs/requirements/svelte/api_front/src/main.js.map
Normal file
1
srcs/requirements/svelte/api_front/src/main.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"main.js","sourceRoot":"","sources":["main.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,cAAc,CAAC;AAE/B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC;IACnB,MAAM,EAAE,QAAQ,CAAC,IAAI;IACrB,KAAK,EAAE;IACN,gBAAgB;KAChB;CACD,CAAC,CAAC;AAEH,eAAe,GAAG,CAAC"}
|
||||
@@ -7,4 +7,4 @@ const app = new App({
|
||||
}
|
||||
});
|
||||
|
||||
export default app;
|
||||
export default app;
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
<script lang="ts">
|
||||
|
||||
// import { initDom } from "../game/client/pong.js";
|
||||
import {onMount} from 'svelte';
|
||||
|
||||
onMount(() => {
|
||||
// initDom();
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<body>
|
||||
<div id="div_game_options">
|
||||
<fieldset>
|
||||
<legend>game options</legend>
|
||||
<div>
|
||||
<input type="checkbox" id="multi_balls" name="multi_balls">
|
||||
<label for="multi_balls">multiples balls</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="moving_walls" name="moving_walls">
|
||||
<label for="moving_walls">moving walls</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>sound :</label>
|
||||
<input type="radio" id="sound_on" name="sound_selector" checked>
|
||||
<label for="sound_on">on</label>
|
||||
<input type="radio" id="sound_off" name="sound_selector">
|
||||
<label for="sound_off">off</label>
|
||||
</div>
|
||||
<div>
|
||||
<button id="play_pong_button">PLAY</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div id="div_game_instructions">
|
||||
<h2>--- keys ---</h2>
|
||||
<p>move up: 'w' or 'up arrow'</p>
|
||||
<p>move down: 's' OR 'down arrow'</p>
|
||||
<p>grid on/off: 'g'</p>
|
||||
</div>
|
||||
|
||||
<div id="canvas_container">
|
||||
<!-- <p> =) </p> -->
|
||||
</div>
|
||||
|
||||
<!-- <script src="http://localhost:8080/js/pong.js" type="module" defer></script> -->
|
||||
</body>
|
||||
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Bit5x3";
|
||||
src: url("/fonts/Bit5x3.woff2") format("woff2"),
|
||||
url("/fonts/Bit5x3.woff") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
background-color: #222425;
|
||||
}
|
||||
#canvas_container {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
/* border: dashed rgb(245, 245, 245) 5px; */
|
||||
/* max-height: 80vh; */
|
||||
/* overflow: hidden; */
|
||||
}
|
||||
#div_game_instructions {
|
||||
text-align: center;
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
font-size: large;
|
||||
}
|
||||
#div_game_options {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
font-size: x-large;
|
||||
}
|
||||
#div_game_options fieldset {
|
||||
max-width: 50vw;
|
||||
width: auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#div_game_options fieldset div {
|
||||
padding: 10px;
|
||||
}
|
||||
#play_pong_button {
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
background-color: #333333;
|
||||
font-size: x-large;
|
||||
padding: 10px;
|
||||
}
|
||||
canvas {
|
||||
background-color: #333333;
|
||||
max-width: 75vw;
|
||||
/* max-height: 100vh; */
|
||||
width: 80%;
|
||||
}
|
||||
</style>
|
||||
@@ -2,12 +2,11 @@
|
||||
import Canvas from "../pieces/Canvas.svelte";
|
||||
import { push } from "svelte-spa-router";
|
||||
import { onMount } from 'svelte';
|
||||
import { get } from "svelte/store";
|
||||
|
||||
let user;
|
||||
|
||||
onMount(async () => {
|
||||
user = await fetch('http://transcendance:8080/api/v2/user')
|
||||
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
|
||||
@@ -31,14 +30,14 @@
|
||||
});
|
||||
|
||||
const login = async() => {
|
||||
window.location.href = 'http://transcendance:8080/api/v2/auth';
|
||||
window.location.href = `http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/auth`;
|
||||
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://transcendance:8080/api/v2/auth/logout', {
|
||||
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/auth/logout`, {
|
||||
method: 'POST',
|
||||
});
|
||||
user = undefined;
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
<script lang="ts">
|
||||
|
||||
import { replace } from "svelte-spa-router";
|
||||
|
||||
export let user;
|
||||
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<h1>You made it to Test</h1>
|
||||
<button on:click={ () => (replace('/'))}>Go Home</button>
|
||||
<div>{user}</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
div.wrapper{
|
||||
display: flexbox;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -2,24 +2,13 @@
|
||||
import { onMount } from "svelte";
|
||||
import { push } from "svelte-spa-router";
|
||||
|
||||
// onMount( async() => {
|
||||
// await fetch("http://transcendance:8080/api/v2/auth/2fa/generate",
|
||||
// {
|
||||
// method: 'POST',
|
||||
// })
|
||||
// .then(response => {return response.blob()})
|
||||
// .then(blob => {
|
||||
// const url = URL.createObjectURL(blob);
|
||||
// qrCodeImg = url;
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
let qrCodeImg;
|
||||
let qrCode = "";
|
||||
let wrongCode = "";
|
||||
const fetchQrCodeImg = (async() => {
|
||||
await fetch("http://transcendance:8080/api/v2/auth/2fa/generate",
|
||||
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/auth/2fa/generate`,
|
||||
{
|
||||
method: 'POST',
|
||||
})
|
||||
@@ -31,7 +20,7 @@
|
||||
})()
|
||||
|
||||
const submitCode = async() => {
|
||||
const response = await fetch("http://transcendance:8080/api/v2/auth/2fa/check",
|
||||
const response = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/auth/2fa/check`,
|
||||
{
|
||||
method : 'POST',
|
||||
headers : {
|
||||
|
||||
@@ -1,99 +1,379 @@
|
||||
|
||||
<script>
|
||||
import "public/game/pong.js"
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import Header from '../../pieces/Header.svelte';
|
||||
import { fade, fly } from 'svelte/transition';
|
||||
|
||||
|
||||
import * as pong from "./client/pong";
|
||||
import { gameState } from "./client/ws";
|
||||
|
||||
//user's stuff
|
||||
let user;
|
||||
let allUsers;
|
||||
|
||||
//Game's stuff
|
||||
let optionsAreNotSet = true;
|
||||
const options = new pong.InitOptions();
|
||||
|
||||
//Game's stuff client side only
|
||||
const gameAreaId = "game_area";
|
||||
|
||||
//html boolean for pages
|
||||
let showWaitPage = false;
|
||||
let showInvitations = false;
|
||||
let showGameOption = true;
|
||||
let showError = false;
|
||||
let hiddenGame = true;
|
||||
let showMatchEnded = false;
|
||||
|
||||
let isThereAnyInvitation = false;
|
||||
let invitations = [];
|
||||
|
||||
let waitingMessage = "Please wait..."
|
||||
let errorMessageWhenAttemptingToGetATicket = "";
|
||||
let idOfIntevalCheckTerminationOfTheMatch;
|
||||
|
||||
onMount( async() => {
|
||||
user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`)
|
||||
.then( x => x.json() );
|
||||
allUsers = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/all`)
|
||||
.then( x => x.json() );
|
||||
options.playerOneUsername = user.username;
|
||||
})
|
||||
|
||||
onDestroy( async() => {
|
||||
clearInterval(idOfIntevalCheckTerminationOfTheMatch);
|
||||
pong.destroy();
|
||||
})
|
||||
|
||||
const initGame = async() =>
|
||||
{
|
||||
optionsAreNotSet = false;
|
||||
showWaitPage = true;
|
||||
const matchOptions = pong.computeMatchOptions(options);
|
||||
|
||||
const responseWhenGrantToken = fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/ticket`, {
|
||||
method : "POST",
|
||||
headers : {'Content-Type': 'application/json'},
|
||||
body : JSON.stringify({
|
||||
playerOneUsername : options.playerOneUsername,
|
||||
playerTwoUsername : options.playerTwoUsername,
|
||||
gameOptions : matchOptions,
|
||||
isGameIsWithInvitation : options.isSomeoneIsInvited
|
||||
})
|
||||
})
|
||||
const responseFromServer = await responseWhenGrantToken;
|
||||
const responseInjson = await responseFromServer.json();
|
||||
const token : string = responseInjson.token;
|
||||
showWaitPage = false;
|
||||
console.log("status : " + responseFromServer.status)
|
||||
if (responseFromServer.status != 200)
|
||||
{
|
||||
console.log(responseInjson)
|
||||
console.log("On refuse le ticket");
|
||||
errorMessageWhenAttemptingToGetATicket = responseInjson.message;
|
||||
showError = true;
|
||||
options.reset();
|
||||
options.playerOneUsername = user.username;
|
||||
setTimeout(() => {
|
||||
optionsAreNotSet = true
|
||||
showError = false;
|
||||
// showWaitPage = false // ???
|
||||
}, 5000);
|
||||
}
|
||||
else if (token)
|
||||
{
|
||||
idOfIntevalCheckTerminationOfTheMatch = setInterval(matchTermitation, 1000);
|
||||
// options.isInvitedPerson = false // ???
|
||||
pong.init(options, gameAreaId, token);
|
||||
hiddenGame = false;
|
||||
}
|
||||
// TODO: Un "else" peut-être ? Si pas de token on fait un truc ?
|
||||
// Si on ne rentre pas dans le else if, du coup il ne se passe rien.
|
||||
}
|
||||
|
||||
const initGameForInvitedPlayer = async(invitation : any) =>
|
||||
{
|
||||
optionsAreNotSet = false
|
||||
showWaitPage = true
|
||||
console.log("invitation : ")
|
||||
console.log(invitation)
|
||||
if (invitation.token)
|
||||
{
|
||||
idOfIntevalCheckTerminationOfTheMatch = setInterval(matchTermitation, 1000);
|
||||
options.playerOneUsername = invitation.playerOneUsername;
|
||||
options.playerTwoUsername = invitation.playerTwoUsername;
|
||||
options.isSomeoneIsInvited = true;
|
||||
options.isInvitedPerson = true
|
||||
pong.init(options, gameAreaId, invitation.token);
|
||||
showWaitPage = false
|
||||
hiddenGame = false;
|
||||
}
|
||||
}
|
||||
|
||||
const matchTermitation = () => {
|
||||
console.log("Ping matchTermitation")
|
||||
if (gameState.matchAbort || gameState.matchEnded)
|
||||
{
|
||||
clearInterval(idOfIntevalCheckTerminationOfTheMatch);
|
||||
console.log("matchTermitation was called")
|
||||
showWaitPage = false
|
||||
gameState.matchAbort ?
|
||||
errorMessageWhenAttemptingToGetATicket = "The match has been aborted"
|
||||
: errorMessageWhenAttemptingToGetATicket = "The match is finished !"
|
||||
gameState.matchAbort ? showError = true : showMatchEnded = true;
|
||||
setTimeout(() => {
|
||||
resetPage();
|
||||
errorMessageWhenAttemptingToGetATicket = "";
|
||||
isThereAnyInvitation = false;
|
||||
invitations = []; // ???
|
||||
console.log("matchTermitation : setTimeout")
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const showOptions = () => {
|
||||
showGameOption = true
|
||||
showInvitations = false
|
||||
}
|
||||
|
||||
const showInvitation = async() => {
|
||||
showGameOption = false;
|
||||
showInvitations = true;
|
||||
invitations = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/invitations`)
|
||||
.then(x => x.json())
|
||||
invitations.length !== 0 ? isThereAnyInvitation = true : isThereAnyInvitation = false
|
||||
}
|
||||
|
||||
const rejectInvitation = async(invitation) => {
|
||||
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/decline`, {
|
||||
method: "POST",
|
||||
headers: { 'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
token : invitation.token
|
||||
})
|
||||
})
|
||||
.then(x => x.json())
|
||||
.catch(error => console.log(error))
|
||||
showInvitation()
|
||||
}
|
||||
|
||||
const acceptInvitation = async(invitation : any) => {
|
||||
const res = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/accept`, {
|
||||
method: "POST",
|
||||
headers: { 'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
token : invitation.token
|
||||
})
|
||||
})
|
||||
.then(x => x.json())
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
})
|
||||
if (res.status === 200)
|
||||
{
|
||||
showInvitation()
|
||||
initGameForInvitedPlayer(invitation)
|
||||
}
|
||||
//Au final c'est utile !
|
||||
|
||||
initGameForInvitedPlayer(invitation) // Luke: normal de initGameForInvitedPlayer() sur un "res.status" different de 200 ?
|
||||
}
|
||||
|
||||
function leaveMatch() {
|
||||
resetPage();
|
||||
};
|
||||
|
||||
function resetPage() {
|
||||
hiddenGame = true;
|
||||
optionsAreNotSet = true
|
||||
showError = false;
|
||||
showMatchEnded = false;
|
||||
options.reset();
|
||||
options.playerOneUsername = user.username;
|
||||
pong.destroy();
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<Header />
|
||||
<!-- <div id="game_page"> Replacement for <body>.
|
||||
Might become useless after CSS rework. -->
|
||||
<div id="game_page">
|
||||
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="preload_font">.</div>
|
||||
|
||||
<div id="div_game_options">
|
||||
{#if showMatchEnded === true}
|
||||
<div id="div_game" in:fly="{{ y: 10, duration: 1000 }}">
|
||||
<p>{errorMessageWhenAttemptingToGetATicket}</p>
|
||||
</div>
|
||||
{/if}
|
||||
{#if showError === true}
|
||||
<div id="div_game" in:fly="{{ y: 10, duration: 1000 }}">
|
||||
<fieldset>
|
||||
<legend>game options</legend>
|
||||
<div>
|
||||
<input type="checkbox" id="multi_balls" name="multi_balls">
|
||||
<label for="multi_balls">multiples balls</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="moving_walls" name="moving_walls">
|
||||
<label for="moving_walls">moving walls</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>sound :</label>
|
||||
<input type="radio" id="sound_on" name="sound_selector" checked>
|
||||
<label for="sound_on">on</label>
|
||||
<input type="radio" id="sound_off" name="sound_selector">
|
||||
<label for="sound_off">off</label>
|
||||
</div>
|
||||
<div>
|
||||
<button id="play_pong_button">PLAY</button>
|
||||
</div>
|
||||
<legend>Error</legend>
|
||||
<p>{errorMessageWhenAttemptingToGetATicket}</p>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div id="canvas_container">
|
||||
<!-- <p> =) </p> -->
|
||||
<div id="canvas_container" hidden={hiddenGame}>
|
||||
<canvas id={gameAreaId}/>
|
||||
</div>
|
||||
{#if !hiddenGame}
|
||||
<div id="div_game">
|
||||
<button id="pong_button" on:click={leaveMatch}>forfeit</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<script src="public/game/pong.js" type="module" defer></script>
|
||||
</body>
|
||||
|
||||
{#if showWaitPage === true}
|
||||
<div id="div_game" in:fly="{{ y: 10, duration: 1000 }}">
|
||||
<fieldset>
|
||||
<legend>Connecting to the game...</legend>
|
||||
<p>{waitingMessage}</p>
|
||||
</fieldset>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
|
||||
{#if optionsAreNotSet}
|
||||
{#if showGameOption === true}
|
||||
<div id="game_option">
|
||||
<div id="div_game">
|
||||
<button id="pong_button" on:click={showInvitation}>Show invitations</button>
|
||||
<fieldset>
|
||||
<legend>game options</legend>
|
||||
<div>
|
||||
<input type="checkbox" id="multi_balls" name="multi_balls" bind:checked={options.multi_balls}>
|
||||
<label for="multi_balls">Multiples balls</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="moving_walls" name="moving_walls" bind:checked={options.moving_walls}>
|
||||
<label for="moving_walls">Moving walls</label>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
{#if options.isSomeoneIsInvited === true}
|
||||
<select bind:value={options.playerTwoUsername}>
|
||||
{#each allUsers as user }
|
||||
<option value={user.username}>{user.username}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{/if}
|
||||
<div>
|
||||
<button id="pong_button" on:click={initGame}>PLAY</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if showInvitations}
|
||||
<div id="invitations_options" in:fly="{{ y: 10, duration: 1000 }}">
|
||||
<div id="div_game">
|
||||
<button id="pong_button" on:click={showOptions}>Play a Game</button>
|
||||
<fieldset>
|
||||
<legend>Current invitation(s)</legend>
|
||||
{#if isThereAnyInvitation}
|
||||
{#each invitations as invitation }
|
||||
<div>
|
||||
{invitation.playerOneUsername} has invited you to play a pong !
|
||||
<button id="pong_button" on:click={() => acceptInvitation(invitation)}>V</button>
|
||||
<button id="pong_button" on:click={() => rejectInvitation(invitation)}>X</button>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
{#if isThereAnyInvitation === false}
|
||||
<p>Currently, no one asked to play with you.</p>
|
||||
<button id="pong_button" on:click={showInvitation}>Reload</button>
|
||||
{/if}
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
</div> <!-- div "game_page" -->
|
||||
|
||||
<style>
|
||||
|
||||
@font-face {
|
||||
font-family: "Bit5x3";
|
||||
src: url("/fonts/Bit5x3.woff2") format("woff2"),local("Bit5x3"), url("/fonts/Bit5x3.woff") format("woff");
|
||||
src:
|
||||
url("/fonts/Bit5x3.woff2") format("woff2"),
|
||||
local("Bit5x3"),
|
||||
url("/fonts/Bit5x3.woff") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
#preload_font {
|
||||
font-family: "Bit5x3";
|
||||
opacity:0;
|
||||
height:0;
|
||||
width:0;
|
||||
display:inline-block;
|
||||
}
|
||||
body {
|
||||
#game_page {
|
||||
margin: 0;
|
||||
background-color: #222425;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
#canvas_container {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
/* border: dashed rgb(245, 245, 245) 5px; */
|
||||
/* max-height: 80vh; */
|
||||
/* overflow: hidden; */
|
||||
}
|
||||
#div_game_options {
|
||||
text-align: center;
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
font-size: x-large;
|
||||
}
|
||||
#div_game_options fieldset {
|
||||
max-width: 50vw;
|
||||
width: auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#div_game_options fieldset div {
|
||||
padding: 10px;
|
||||
}
|
||||
#play_pong_button {
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
background-color: #333333;
|
||||
font-size: x-large;
|
||||
padding: 10px;
|
||||
}
|
||||
canvas {
|
||||
/* background-color: #ff0000; */
|
||||
background-color: #333333;
|
||||
max-width: 75vw;
|
||||
/* max-height: 100vh; */
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
#div_game {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
font-size: x-large;
|
||||
}
|
||||
#div_game fieldset {
|
||||
max-width: 50vw;
|
||||
width: auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#div_game fieldset div {
|
||||
padding: 10px;
|
||||
}
|
||||
#pong_button {
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
background-color: #333333;
|
||||
font-size: x-large;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#users_name { /* UNUSED */
|
||||
text-align: center;
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
font-size: x-large;
|
||||
}
|
||||
#error_notification { /* UNUSED */
|
||||
text-align: center;
|
||||
display: block;
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(143, 19, 19);
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import Header from '../../pieces/Header.svelte';
|
||||
import MatchListElem from "../../pieces/MatchListElem.svelte";
|
||||
import { fade, fly } from 'svelte/transition';
|
||||
|
||||
|
||||
import * as pongSpectator from "./client/pongSpectator";
|
||||
import { gameState } from "./client/ws";
|
||||
import { gameSessionIdPLACEHOLDER } from "./shared_js/constants";
|
||||
|
||||
//user's stuff
|
||||
let user;
|
||||
let allUsers;
|
||||
|
||||
//Game's stuff client side only
|
||||
const gameAreaId = "game_area";
|
||||
let sound = "off";
|
||||
// const dummyMatchList = [
|
||||
// {
|
||||
// gameSessionId: gameSessionIdPLACEHOLDER,
|
||||
// matchOptions: pongSpectator.MatchOptions.noOption,
|
||||
// playerOneUsername: "toto",
|
||||
// playerTwoUsername: "bruno",
|
||||
// },
|
||||
// {
|
||||
// gameSessionId: gameSessionIdPLACEHOLDER,
|
||||
// matchOptions: pongSpectator.MatchOptions.multiBalls,
|
||||
// playerOneUsername: "pl1",
|
||||
// playerTwoUsername: "pl2",
|
||||
// },
|
||||
// {
|
||||
// gameSessionId: "id6543",
|
||||
// matchOptions: pongSpectator.MatchOptions.movingWalls | pongSpectator.MatchOptions.multiBalls,
|
||||
// playerOneUsername: "bertand",
|
||||
// playerTwoUsername: "cassandre",
|
||||
// },
|
||||
// {
|
||||
// gameSessionId: "id3452",
|
||||
// matchOptions: pongSpectator.MatchOptions.multiBalls,
|
||||
// playerOneUsername: "madeleine",
|
||||
// playerTwoUsername: "jack",
|
||||
// },
|
||||
// ];
|
||||
let matchList = [];
|
||||
|
||||
//html boolean for pages
|
||||
let hiddenGame = true;
|
||||
let hiddenMatchList = false;
|
||||
|
||||
|
||||
onMount( async() => {
|
||||
user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`)
|
||||
.then( x => x.json() );
|
||||
allUsers = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/all`)
|
||||
.then( x => x.json() );
|
||||
// WIP: fetch for match list here
|
||||
const responseForMatchList = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/match/all`)
|
||||
const jsonForMatchList = await responseForMatchList.json();
|
||||
matchList = jsonForMatchList;
|
||||
console.log("matchList");
|
||||
if (matchList.length <= 0)
|
||||
hiddenMatchList = true;
|
||||
console.log(matchList);
|
||||
})
|
||||
|
||||
onDestroy( async() => {
|
||||
pongSpectator.destroy();
|
||||
})
|
||||
|
||||
async function initGameSpectator(gameSessionId: string, matchOptions: pongSpectator.MatchOptions) {
|
||||
pongSpectator.init(matchOptions, sound, gameAreaId, gameSessionId);
|
||||
hiddenGame = false;
|
||||
};
|
||||
|
||||
function leaveMatch() {
|
||||
resetPage();
|
||||
};
|
||||
|
||||
async function resetPage() {
|
||||
hiddenGame = true;
|
||||
pongSpectator.destroy();
|
||||
// WIP: fetch for match list here
|
||||
matchList = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/match/all`)
|
||||
.then( x => x.json() );
|
||||
console.log("matchList");
|
||||
if (matchList.length <= 0)
|
||||
hiddenMatchList = true;
|
||||
console.log(matchList);
|
||||
};
|
||||
|
||||
</script>
|
||||
<!-- -->
|
||||
|
||||
<Header />
|
||||
<!-- <div id="game_page"> Replacement for <body>.
|
||||
Might become useless after CSS rework. -->
|
||||
<div id="game_page">
|
||||
|
||||
<div id="canvas_container" hidden={hiddenGame}>
|
||||
<canvas id={gameAreaId}/>
|
||||
</div>
|
||||
|
||||
{#if hiddenGame}
|
||||
<div id="div_game">
|
||||
<div id="game_options">
|
||||
<fieldset>
|
||||
{#if hiddenMatchList}
|
||||
<legend>no match available</legend>
|
||||
{:else}
|
||||
<legend>options</legend>
|
||||
<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>
|
||||
</div>
|
||||
<menu id="match_list">
|
||||
{#each matchList as match}
|
||||
<MatchListElem match={match} on:click={(e) => initGameSpectator(match.gameServerIdOfTheMatch, match.gameOptions)} />
|
||||
{/each}
|
||||
</menu>
|
||||
{/if}
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{:else}
|
||||
<div id="div_game">
|
||||
<button id="pong_button" on:click={leaveMatch}>leave match</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
</div> <!-- div "game_page" -->
|
||||
|
||||
<!-- -->
|
||||
<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;
|
||||
}
|
||||
#game_page {
|
||||
margin: 0;
|
||||
background-color: #222425;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
#canvas_container {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
canvas {
|
||||
background-color: #333333;
|
||||
max-width: 75vw;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
#div_game {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
font-size: x-large;
|
||||
}
|
||||
#div_game fieldset {
|
||||
max-width: 50vw;
|
||||
width: auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#div_game fieldset div {
|
||||
padding: 10px;
|
||||
}
|
||||
#match_list {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: large;
|
||||
}
|
||||
#pong_button {
|
||||
font-family: "Bit5x3";
|
||||
color: rgb(245, 245, 245);
|
||||
background-color: #333333;
|
||||
font-size: x-large;
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,74 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import Header from "../../pieces/Header.svelte";
|
||||
|
||||
|
||||
//user's stuff
|
||||
let currentUser;
|
||||
let allUsers = [];
|
||||
let idInterval;
|
||||
onMount( async() => {
|
||||
currentUser = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`)
|
||||
.then( x => x.json() );
|
||||
allUsers = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/ranking`)
|
||||
.then( x => x.json() );
|
||||
idInterval = setInterval(fetchScores, 10000);
|
||||
})
|
||||
|
||||
onDestroy( async() => {
|
||||
clearInterval(idInterval);
|
||||
})
|
||||
|
||||
function fetchScores() {
|
||||
fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/ranking`)
|
||||
.then( x => x.json() )
|
||||
.then( x => allUsers = x );
|
||||
}
|
||||
</script>
|
||||
<Header />
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h1>Ranking</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Username</th>
|
||||
<th scope="col">Win</th>
|
||||
<th scope="col">Lose</th>
|
||||
<th scope="col">Draw</th>
|
||||
<th scope="col">Games Played</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each allUsers as user, i}
|
||||
<tr>
|
||||
<th scope="row">{i + 1}</th>
|
||||
{#if user.username === currentUser.username}
|
||||
<td><b>You ({user.username})</b></td>
|
||||
{:else}
|
||||
<td>{user.username}</td>
|
||||
{/if}
|
||||
<td>{user.stats.winGame}</td>
|
||||
<td>{user.stats.loseGame}</td>
|
||||
<td>{user.stats.drawGame}</td>
|
||||
<td>{user.stats.totalGame}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
import * as c from "./constants.js"
|
||||
|
||||
// export const soundPongArr: HTMLAudioElement[] = [];
|
||||
export const soundPongArr: HTMLAudioElement[] = [
|
||||
new Audio("http://" + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + "/sound/pong/"+1+".ogg"),
|
||||
new Audio("http://" + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + "/sound/pong/"+2+".ogg")
|
||||
];
|
||||
export const soundRoblox = new Audio("http://" + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + "/sound/roblox-oof.ogg");
|
||||
|
||||
export function initAudio(sound: string)
|
||||
{
|
||||
let muteFlag: boolean;
|
||||
if (sound === "on") {
|
||||
muteFlag = false;
|
||||
}
|
||||
else {
|
||||
muteFlag = true;
|
||||
}
|
||||
|
||||
soundPongArr.forEach((value) => {
|
||||
value.volume = c.soundRobloxVolume;
|
||||
value.muted = muteFlag;
|
||||
});
|
||||
soundRoblox.volume = c.soundRobloxVolume;
|
||||
soundRoblox.muted = muteFlag;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
import * as c from ".././constants.js"
|
||||
|
||||
export class GameArea {
|
||||
keys: string[] = [];
|
||||
handleInputInterval: number = 0;
|
||||
gameLoopInterval: number = 0;
|
||||
drawLoopInterval: number = 0;
|
||||
canvas: HTMLCanvasElement;
|
||||
ctx: CanvasRenderingContext2D;
|
||||
constructor(canvas_id: string) {
|
||||
const canvas = document.getElementById("game_area");
|
||||
if (canvas && canvas instanceof HTMLCanvasElement) {
|
||||
this.canvas = canvas;
|
||||
}
|
||||
else {
|
||||
console.log("GameArea init error, invalid canvas_id");
|
||||
return;
|
||||
}
|
||||
this.ctx = this.canvas.getContext("2d") as CanvasRenderingContext2D;
|
||||
this.canvas.width = c.CanvasWidth;
|
||||
this.canvas.height = c.CanvasWidth / c.CanvasRatio;
|
||||
}
|
||||
addKey(key: string) {
|
||||
key = key.toLowerCase();
|
||||
var i = this.keys.indexOf(key);
|
||||
if (i == -1)
|
||||
this.keys.push(key);
|
||||
}
|
||||
deleteKey(key: string) {
|
||||
key = key.toLowerCase();
|
||||
var i = this.keys.indexOf(key);
|
||||
if (i != -1) {
|
||||
this.keys.splice(i, 1);
|
||||
}
|
||||
}
|
||||
clear() {
|
||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
|
||||
import * as c from "../constants.js"
|
||||
import * as en from "../../shared_js/enums.js"
|
||||
import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
|
||||
import { TextElem, TextNumericValue } from "./Text.js";
|
||||
import { RectangleClient, MovingRectangleClient, RacketClient, BallClient, Line } from "./RectangleClient.js";
|
||||
import { GameComponents } from "../../shared_js/class/GameComponents.js";
|
||||
import type { MovingRectangle } from "../../shared_js/class/Rectangle.js";
|
||||
|
||||
class GameComponentsExtensionForClient extends GameComponents {
|
||||
wallTop: RectangleClient | MovingRectangleClient;
|
||||
wallBottom: RectangleClient | MovingRectangleClient;
|
||||
playerLeft: RacketClient;
|
||||
playerRight: RacketClient;
|
||||
ballsArr: BallClient[];
|
||||
constructor(options: en.MatchOptions, ctx: CanvasRenderingContext2D)
|
||||
{
|
||||
super(options);
|
||||
|
||||
// Rackets
|
||||
const basePL = this.playerLeft;
|
||||
const basePR = this.playerRight;
|
||||
this.playerLeft = new RacketClient(
|
||||
basePL.pos, basePL.width, basePL.height, basePL.baseSpeed,
|
||||
ctx, "white");
|
||||
this.playerRight = new RacketClient(
|
||||
basePR.pos, basePR.width, basePR.height, basePR.baseSpeed,
|
||||
ctx, "white");
|
||||
|
||||
// Balls
|
||||
const newBallsArr: BallClient[] = [];
|
||||
this.ballsArr.forEach((ball) => {
|
||||
newBallsArr.push(new BallClient(ball.pos, ball.width, ball.baseSpeed, ball.speedIncrease,
|
||||
ctx, "white")
|
||||
);
|
||||
});
|
||||
this.ballsArr = newBallsArr;
|
||||
|
||||
// Walls
|
||||
if (options & en.MatchOptions.movingWalls)
|
||||
{
|
||||
const baseWT = <MovingRectangle>this.wallTop;
|
||||
const baseWB = <MovingRectangle>this.wallBottom;
|
||||
|
||||
this.wallTop = new MovingRectangleClient(baseWT.pos, baseWT.width, baseWT.height, baseWT.baseSpeed,
|
||||
ctx, "grey");
|
||||
(<MovingRectangleClient>this.wallTop).dir.assign(baseWT.dir.x, baseWT.dir.y);
|
||||
|
||||
this.wallBottom = new MovingRectangleClient(baseWB.pos, baseWB.width, baseWB.height, baseWB.baseSpeed,
|
||||
ctx, "grey");
|
||||
(<MovingRectangleClient>this.wallBottom).dir.assign(baseWB.dir.x, baseWB.dir.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
const baseWT = this.wallTop;
|
||||
const baseWB = this.wallBottom;
|
||||
this.wallTop = new RectangleClient(baseWT.pos, baseWT.width, baseWT.height,
|
||||
ctx, "grey");
|
||||
this.wallBottom = new RectangleClient(baseWB.pos, baseWB.width, baseWB.height,
|
||||
ctx, "grey");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class GameComponentsClient extends GameComponentsExtensionForClient {
|
||||
midLine: Line;
|
||||
scoreLeft: TextNumericValue;
|
||||
scoreRight: TextNumericValue;
|
||||
text1: TextElem;
|
||||
text2: TextElem;
|
||||
text3: TextElem;
|
||||
|
||||
w_grid_mid: RectangleClient;
|
||||
w_grid_u1: RectangleClient;
|
||||
w_grid_d1: RectangleClient;
|
||||
h_grid_mid: RectangleClient;
|
||||
h_grid_u1: RectangleClient;
|
||||
h_grid_d1: RectangleClient;
|
||||
constructor(options: en.MatchOptions, ctx: CanvasRenderingContext2D)
|
||||
{
|
||||
super(options, ctx);
|
||||
let pos = new VectorInteger;
|
||||
// Scores
|
||||
pos.assign(c.w_mid-c.scoreSize*1.6, c.scoreSize*1.5);
|
||||
this.scoreLeft = new TextNumericValue(pos, c.scoreSize, ctx, "white");
|
||||
pos.assign(c.w_mid+c.scoreSize*1.1, c.scoreSize*1.5);
|
||||
this.scoreRight = new TextNumericValue(pos, c.scoreSize, ctx, "white");
|
||||
this.scoreLeft.value = 0;
|
||||
this.scoreRight.value = 0;
|
||||
|
||||
// Text
|
||||
pos.assign(0, c.h_mid);
|
||||
this.text1 = new TextElem(pos, Math.floor(c.w/8), ctx, "white");
|
||||
this.text2 = new TextElem(pos, Math.floor(c.w/24), ctx, "white");
|
||||
this.text3 = new TextElem(pos, Math.floor(c.w/24), ctx, "white");
|
||||
|
||||
// Dotted Midline
|
||||
pos.assign(c.w_mid-c.midLineSize/2, 0+c.wallSize);
|
||||
this.midLine = new Line(pos, c.midLineSize, c.h-c.wallSize*2, ctx, "white", 15);
|
||||
|
||||
// Grid
|
||||
pos.assign(0, c.h_mid);
|
||||
this.w_grid_mid = new RectangleClient(pos, c.w, c.gridSize, ctx, "darkgreen");
|
||||
pos.assign(0, c.h/4);
|
||||
this.w_grid_u1 = new RectangleClient(pos, c.w, c.gridSize, ctx, "darkgreen");
|
||||
pos.assign(0, c.h-c.h/4);
|
||||
this.w_grid_d1 = new RectangleClient(pos, c.w, c.gridSize, ctx, "darkgreen");
|
||||
pos.assign(c.w_mid, 0);
|
||||
this.h_grid_mid = new RectangleClient(pos, c.gridSize, c.h, ctx, "darkgreen");
|
||||
pos.assign(c.w/4, 0);
|
||||
this.h_grid_u1 = new RectangleClient(pos, c.gridSize, c.h, ctx, "darkgreen");
|
||||
pos.assign(c.w-c.w/4, 0);
|
||||
this.h_grid_d1 = new RectangleClient(pos, c.gridSize, c.h, ctx, "darkgreen");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
export class InitOptions {
|
||||
sound = "off";
|
||||
multi_balls = false;
|
||||
moving_walls = false;
|
||||
isSomeoneIsInvited = false;
|
||||
isInvitedPerson = false;
|
||||
playerOneUsername = "";
|
||||
playerTwoUsername = "";
|
||||
reset() {
|
||||
this.sound = "off";
|
||||
this.multi_balls = false;
|
||||
this.moving_walls = false;
|
||||
this.isSomeoneIsInvited = false;
|
||||
this.isInvitedPerson = false;
|
||||
this.playerOneUsername = "";
|
||||
this.playerTwoUsername = "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
import type * as en from "../../shared_js/enums.js"
|
||||
import type * as ev from "../../shared_js/class/Event.js"
|
||||
|
||||
export class InputHistory {
|
||||
input: en.InputEnum;
|
||||
id: number;
|
||||
deltaTime: number;
|
||||
constructor(inputState: ev.EventInput, deltaTime: number) {
|
||||
this.input = inputState.input;
|
||||
this.id = inputState.id;
|
||||
this.deltaTime = deltaTime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
|
||||
import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
|
||||
import type { GraphicComponent } from "../../shared_js/class/interface.js";
|
||||
import { Rectangle, MovingRectangle, Racket, Ball } from "../../shared_js/class/Rectangle.js";
|
||||
import { soundPongArr } from "../audio.js"
|
||||
import { random } from "../utils.js";
|
||||
|
||||
function updateRectangle(this: RectangleClient) {
|
||||
this.ctx.fillStyle = this.color;
|
||||
this.ctx.fillRect(this.pos.x, this.pos.y, this.width, this.height);
|
||||
}
|
||||
|
||||
function clearRectangle(this: RectangleClient, pos?: VectorInteger) {
|
||||
if (pos)
|
||||
this.ctx.clearRect(pos.x, pos.y, this.width, this.height);
|
||||
else
|
||||
this.ctx.clearRect(this.pos.x, this.pos.y, this.width, this.height);
|
||||
}
|
||||
|
||||
export class RectangleClient extends Rectangle implements GraphicComponent {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
color: string;
|
||||
update: () => void;
|
||||
clear: (pos?: VectorInteger) => void;
|
||||
constructor(pos: VectorInteger, width: number, height: number,
|
||||
ctx: CanvasRenderingContext2D, color: string)
|
||||
{
|
||||
super(pos, width, height);
|
||||
this.ctx = ctx;
|
||||
this.color = color;
|
||||
this.update = updateRectangle;
|
||||
this.clear = clearRectangle;
|
||||
}
|
||||
}
|
||||
|
||||
export class MovingRectangleClient extends MovingRectangle implements GraphicComponent {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
color: string;
|
||||
update: () => void;
|
||||
clear: (pos?: VectorInteger) => void;
|
||||
constructor(pos: VectorInteger, width: number, height: number, baseSpeed: number,
|
||||
ctx: CanvasRenderingContext2D, color: string)
|
||||
{
|
||||
super(pos, width, height, baseSpeed);
|
||||
this.ctx = ctx;
|
||||
this.color = color;
|
||||
this.update = updateRectangle;
|
||||
this.clear = clearRectangle;
|
||||
}
|
||||
}
|
||||
|
||||
export class RacketClient extends Racket implements GraphicComponent {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
color: string;
|
||||
update: () => void;
|
||||
clear: (pos?: VectorInteger) => void;
|
||||
constructor(pos: VectorInteger, width: number, height: number, baseSpeed: number,
|
||||
ctx: CanvasRenderingContext2D, color: string)
|
||||
{
|
||||
super(pos, width, height, baseSpeed);
|
||||
this.ctx = ctx;
|
||||
this.color = color;
|
||||
this.update = updateRectangle;
|
||||
this.clear = clearRectangle;
|
||||
}
|
||||
}
|
||||
|
||||
export class BallClient extends Ball implements GraphicComponent {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
color: string;
|
||||
update: () => void;
|
||||
clear: (pos?: VectorInteger) => void;
|
||||
constructor(pos: VectorInteger, size: number, baseSpeed: number, speedIncrease: number,
|
||||
ctx: CanvasRenderingContext2D, color: string)
|
||||
{
|
||||
super(pos, size, baseSpeed, speedIncrease);
|
||||
this.ctx = ctx;
|
||||
this.color = color;
|
||||
this.update = updateRectangle;
|
||||
this.clear = clearRectangle;
|
||||
}
|
||||
bounce(collider?: Rectangle) {
|
||||
this._bounceAlgo(collider);
|
||||
let i = Math.floor(random(0, soundPongArr.length));
|
||||
soundPongArr[ i ].play();
|
||||
console.log(`sound_i=${i}`); // debug log
|
||||
}
|
||||
}
|
||||
|
||||
function updateLine(this: Line) {
|
||||
this.ctx.fillStyle = this.color;
|
||||
let pos: VectorInteger = new VectorInteger;
|
||||
let i = 0;
|
||||
while (i < this.segmentCount)
|
||||
{
|
||||
/* Horizontal Line */
|
||||
// pos.y = this.pos.y;
|
||||
// pos.x = this.pos.x + this.segmentWidth * i;
|
||||
|
||||
/* Vertical Line */
|
||||
pos.x = this.pos.x;
|
||||
pos.y = this.pos.y + this.segmentHeight * i;
|
||||
|
||||
this.ctx.fillRect(pos.x, pos.y, this.segmentWidth, this.segmentHeight);
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
export class Line extends RectangleClient {
|
||||
gapeCount: number = 0;
|
||||
segmentCount: number;
|
||||
segmentWidth: number;
|
||||
segmentHeight: number;
|
||||
constructor(pos: VectorInteger, width: number, height: number,
|
||||
ctx: CanvasRenderingContext2D, color: string, gapeCount?: number)
|
||||
{
|
||||
super(pos, width, height, ctx, color);
|
||||
this.update = updateLine;
|
||||
if (gapeCount)
|
||||
this.gapeCount = gapeCount;
|
||||
this.segmentCount = this.gapeCount * 2 + 1;
|
||||
|
||||
/* Vertical Line */
|
||||
this.segmentWidth = this.width;
|
||||
this.segmentHeight = this.height / this.segmentCount;
|
||||
|
||||
/* Horizontal Line */
|
||||
// this.segmentWidth = this.width / this.segmentCount;
|
||||
// this.segmentHeight = this.height;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
|
||||
import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
|
||||
import type { Component } from "../../shared_js/class/interface.js";
|
||||
|
||||
// conflict with Text
|
||||
export class TextElem implements Component {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
pos: VectorInteger;
|
||||
color: string;
|
||||
size: number;
|
||||
font: string;
|
||||
text: string = "";
|
||||
constructor(pos: VectorInteger, size: number,
|
||||
ctx: CanvasRenderingContext2D, color: string, font: string = "Bit5x3")
|
||||
{
|
||||
// this.pos = Object.assign({}, pos); // create bug, Uncaught TypeError: X is not a function
|
||||
this.pos = new VectorInteger(pos.x, pos.y);
|
||||
this.size = size;
|
||||
this.ctx = ctx;
|
||||
this.color = color;
|
||||
this.font = font;
|
||||
}
|
||||
update() {
|
||||
this.ctx.font = this.size + "px" + " " + this.font;
|
||||
this.ctx.fillStyle = this.color;
|
||||
this.ctx.fillText(this.text, this.pos.x, this.pos.y);
|
||||
}
|
||||
clear() {
|
||||
// clear no very accurate for Text
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics
|
||||
let textMetric = this.ctx.measureText(this.text);
|
||||
// console.log("textMetric.width = "+textMetric.width);
|
||||
// console.log("size = "+this.size);
|
||||
// console.log("x = "+this.pos.x);
|
||||
// console.log("y = "+this.pos.y);
|
||||
this.ctx.clearRect(this.pos.x - 1, this.pos.y-this.size + 1, textMetric.width, this.size);
|
||||
// +1 and -1 because float imprecision (and Math.floor() with VectorInteger dont work for the moment)
|
||||
// (or maybe its textMetric imprecision ?)
|
||||
}
|
||||
}
|
||||
|
||||
export class TextNumericValue extends TextElem {
|
||||
private _value: number = 0;
|
||||
constructor(pos: VectorInteger, size: number,
|
||||
ctx: CanvasRenderingContext2D, color: string, font?: string)
|
||||
{
|
||||
super(pos, size, ctx, color, font);
|
||||
}
|
||||
get value() {
|
||||
return this._value;
|
||||
}
|
||||
set value(v: number) {
|
||||
this._value = v;
|
||||
this.text = v.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
import { w } from "../shared_js/constants.js"
|
||||
export * from "../shared_js/constants.js"
|
||||
|
||||
export const midLineSize = Math.floor(w/150);
|
||||
export const scoreSize = Math.floor(w/16);
|
||||
export const gridSize = Math.floor(w/500);
|
||||
|
||||
// min interval on Firefox seems to be 15. Chrome can go lower.
|
||||
export const handleInputIntervalMS = 15; // millisecond
|
||||
export const sendLoopIntervalMS = 15; // millisecond // unused
|
||||
export const gameLoopIntervalMS = 15; // millisecond
|
||||
export const drawLoopIntervalMS = 15; // millisecond
|
||||
|
||||
export const fixedDeltaTime = gameLoopIntervalMS/1000; // second
|
||||
|
||||
export const soundRobloxVolume = 0.3; // between 0 and 1
|
||||
export const soundPongVolume = 0.3; // between 0 and 1
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
import { pong, gc } from "./global.js"
|
||||
import { gridDisplay } from "./handleInput.js";
|
||||
|
||||
export function drawLoop()
|
||||
{
|
||||
pong.clear();
|
||||
|
||||
if (gridDisplay) {
|
||||
drawGrid();
|
||||
}
|
||||
|
||||
drawStatic();
|
||||
|
||||
gc.text1.update();
|
||||
gc.text2.update();
|
||||
gc.text3.update();
|
||||
|
||||
drawDynamic();
|
||||
}
|
||||
|
||||
function drawDynamic()
|
||||
{
|
||||
gc.scoreLeft.update();
|
||||
gc.scoreRight.update();
|
||||
gc.playerLeft.update();
|
||||
gc.playerRight.update();
|
||||
gc.ballsArr.forEach((ball) => {
|
||||
ball.update();
|
||||
});
|
||||
}
|
||||
|
||||
function drawStatic()
|
||||
{
|
||||
gc.midLine.update();
|
||||
gc.wallTop.update();
|
||||
gc.wallBottom.update();
|
||||
}
|
||||
|
||||
function drawGrid()
|
||||
{
|
||||
gc.w_grid_mid.update();
|
||||
gc.w_grid_u1.update();
|
||||
gc.w_grid_d1.update();
|
||||
|
||||
gc.h_grid_mid.update();
|
||||
gc.h_grid_u1.update();
|
||||
gc.h_grid_d1.update();
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
|
||||
import * as c from "./constants.js";
|
||||
import * as en from "../shared_js/enums.js"
|
||||
import { gc, matchOptions } from "./global.js";
|
||||
import { clientInfo, clientInfoSpectator} from "./ws.js";
|
||||
import { wallsMovements } from "../shared_js/wallsMovement.js";
|
||||
import type { RacketClient } from "./class/RectangleClient.js";
|
||||
import type { VectorInteger } from "../shared_js/class/Vector.js";
|
||||
|
||||
let actual_time: number = Date.now();
|
||||
let last_time: number;
|
||||
let delta_time: number;
|
||||
|
||||
export function gameLoop()
|
||||
{
|
||||
/* last_time = actual_time;
|
||||
actual_time = Date.now();
|
||||
delta_time = (actual_time - last_time) / 1000; */
|
||||
|
||||
delta_time = c.fixedDeltaTime;
|
||||
// console.log(`delta_gameLoop: ${delta_time}`);
|
||||
|
||||
// interpolation
|
||||
// console.log(`dir.y: ${clientInfo.opponent.dir.y}, pos.y: ${clientInfo.opponent.pos.y}, opponentNextPos.y: ${clientInfo.opponentNextPos.y}`);
|
||||
if (clientInfo.opponent.dir.y != 0 ) {
|
||||
racketInterpolation(delta_time, clientInfo.opponent, clientInfo.opponentNextPos);
|
||||
}
|
||||
|
||||
// client prediction
|
||||
gc.ballsArr.forEach((ball) => {
|
||||
ball.moveAndBounce(delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]);
|
||||
});
|
||||
|
||||
if (matchOptions & en.MatchOptions.movingWalls) {
|
||||
wallsMovements(delta_time, gc);
|
||||
}
|
||||
}
|
||||
|
||||
export function gameLoopSpectator()
|
||||
{
|
||||
delta_time = c.fixedDeltaTime;
|
||||
|
||||
// interpolation
|
||||
if (gc.playerLeft.dir.y != 0 ) {
|
||||
racketInterpolation(delta_time, gc.playerLeft, clientInfoSpectator.playerLeftNextPos);
|
||||
}
|
||||
if (gc.playerRight.dir.y != 0 ) {
|
||||
racketInterpolation(delta_time, gc.playerRight, clientInfoSpectator.playerRightNextPos);
|
||||
}
|
||||
|
||||
// client prediction
|
||||
gc.ballsArr.forEach((ball) => {
|
||||
ball.moveAndBounce(delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]);
|
||||
});
|
||||
|
||||
if (matchOptions & en.MatchOptions.movingWalls) {
|
||||
wallsMovements(delta_time, gc);
|
||||
}
|
||||
}
|
||||
|
||||
function racketInterpolation(delta: number, racket: RacketClient, nextPos: VectorInteger)
|
||||
{
|
||||
// interpolation
|
||||
racket.moveAndCollide(delta, [gc.wallTop, gc.wallBottom]);
|
||||
|
||||
if ((racket.dir.y > 0 && racket.pos.y > nextPos.y)
|
||||
|| (racket.dir.y < 0 && racket.pos.y < nextPos.y))
|
||||
{
|
||||
racket.dir.y = 0;
|
||||
racket.pos.y = nextPos.y;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
import * as en from "../shared_js/enums.js";
|
||||
import type { GameArea } from "./class/GameArea.js";
|
||||
import type { GameComponentsClient } from "./class/GameComponentsClient.js";
|
||||
|
||||
export let pong: GameArea;
|
||||
export let gc: GameComponentsClient;
|
||||
export let matchOptions: en.MatchOptions = en.MatchOptions.noOption;
|
||||
|
||||
export function setPong(value: GameArea) {
|
||||
pong = value;
|
||||
}
|
||||
|
||||
export function setGc(value: GameComponentsClient) {
|
||||
gc = value;
|
||||
}
|
||||
|
||||
export function setMatchOptions(value: en.MatchOptions) {
|
||||
matchOptions = value;
|
||||
}
|
||||
|
||||
export let startFunction: () => void;
|
||||
|
||||
export function setStartFunction(value: () => void) {
|
||||
startFunction = value;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
|
||||
import { pong, gc } from "./global.js"
|
||||
import { socket, clientInfo, gameState } from "./ws.js"
|
||||
import * as ev from "../shared_js/class/Event.js"
|
||||
import * as en from "../shared_js/enums.js"
|
||||
import { InputHistory } from "./class/InputHistory.js"
|
||||
import * as c from "./constants.js";
|
||||
|
||||
export let gridDisplay = false;
|
||||
|
||||
let actual_time: number = Date.now();
|
||||
let last_time: number;
|
||||
let delta_time: number;
|
||||
|
||||
const inputState: ev.EventInput = new ev.EventInput();
|
||||
const inputHistoryArr: InputHistory[] = [];
|
||||
|
||||
// test
|
||||
/* export function sendLoop()
|
||||
{
|
||||
socket.send(JSON.stringify(inputState));
|
||||
} */
|
||||
|
||||
export function handleInput()
|
||||
{
|
||||
/* last_time = actual_time;
|
||||
actual_time = Date.now();
|
||||
delta_time = (actual_time - last_time) / 1000; */
|
||||
|
||||
delta_time = c.fixedDeltaTime;
|
||||
// console.log(`delta_time: ${delta_time}`);
|
||||
|
||||
inputState.id = Date.now();
|
||||
inputState.input = en.InputEnum.noInput;
|
||||
|
||||
const keys = pong.keys;
|
||||
if (keys.length !== 0)
|
||||
{
|
||||
if (keys.indexOf("g") != -1)
|
||||
{
|
||||
gridDisplay = !gridDisplay;
|
||||
pong.deleteKey("g");
|
||||
}
|
||||
playerMovements(delta_time, keys);
|
||||
}
|
||||
|
||||
if (!gameState.matchEnded) {
|
||||
socket.send(JSON.stringify(inputState));
|
||||
}
|
||||
// setTimeout(testInputDelay, 100);
|
||||
inputHistoryArr.push(new InputHistory(inputState, delta_time));
|
||||
|
||||
// client prediction
|
||||
if (inputState.input !== en.InputEnum.noInput) {
|
||||
// TODO: peut-etre le mettre dans game loop ?
|
||||
// Attention au delta time dans ce cas !
|
||||
playerMovePrediction(delta_time, inputState.input);
|
||||
}
|
||||
}
|
||||
|
||||
function playerMovements(delta: number, keys: string[])
|
||||
{
|
||||
if (keys.indexOf("w") !== -1 || keys.indexOf("ArrowUp".toLowerCase()) !== -1)
|
||||
{
|
||||
if (keys.indexOf("s") === -1 && keys.indexOf("ArrowDown".toLowerCase()) === -1) {
|
||||
inputState.input = en.InputEnum.up;
|
||||
}
|
||||
}
|
||||
else if (keys.indexOf("s") !== -1 || keys.indexOf("ArrowDown".toLowerCase()) !== -1) {
|
||||
inputState.input = en.InputEnum.down;
|
||||
}
|
||||
}
|
||||
|
||||
function testInputDelay() {
|
||||
socket.send(JSON.stringify(inputState));
|
||||
}
|
||||
|
||||
|
||||
function playerMovePrediction(delta: number, input: en.InputEnum)
|
||||
{
|
||||
// client prediction
|
||||
const racket = clientInfo.racket;
|
||||
if (input === en.InputEnum.up) {
|
||||
racket.dir.y = -1;
|
||||
}
|
||||
else if (input === en.InputEnum.down) {
|
||||
racket.dir.y = 1;
|
||||
}
|
||||
racket.moveAndCollide(delta, [gc.wallTop, gc.wallBottom]);
|
||||
}
|
||||
|
||||
export function repeatInput(lastInputId: number)
|
||||
{
|
||||
// server reconciliation
|
||||
let i = inputHistoryArr.findIndex((value: InputHistory) => {
|
||||
if (value.id === lastInputId) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// console.log(`inputHistory total: ${inputHistoryArr.length}` );
|
||||
inputHistoryArr.splice(0, i+1);
|
||||
// console.log(`inputHistory left: ${inputHistoryArr.length}` );
|
||||
|
||||
inputHistoryArr.forEach((value: InputHistory) => {
|
||||
if (value.input !== en.InputEnum.noInput) {
|
||||
playerMovePrediction(value.deltaTime, value.input);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
import * as c from "./constants.js"
|
||||
import * as en from "../shared_js/enums.js"
|
||||
import { GameArea } from "./class/GameArea.js";
|
||||
import { GameComponentsClient } from "./class/GameComponentsClient.js";
|
||||
import { socket, resetGameState } from "./ws.js";
|
||||
import { initAudio } from "./audio.js";
|
||||
import type { InitOptions } from "./class/InitOptions.js";
|
||||
|
||||
import { pong } from "./global.js"
|
||||
import { setPong, setGc, setMatchOptions } from "./global.js"
|
||||
|
||||
export function computeMatchOptions(options: InitOptions)
|
||||
{
|
||||
let matchOptions = en.MatchOptions.noOption;
|
||||
|
||||
if (options.multi_balls === true) {
|
||||
matchOptions |= en.MatchOptions.multiBalls
|
||||
}
|
||||
if (options.moving_walls === true) {
|
||||
matchOptions |= en.MatchOptions.movingWalls
|
||||
}
|
||||
|
||||
return matchOptions;
|
||||
}
|
||||
|
||||
export function initBase(matchOptions: en.MatchOptions, sound: string, gameAreaId: string)
|
||||
{
|
||||
initAudio(sound);
|
||||
setMatchOptions(matchOptions);
|
||||
setPong(new GameArea(gameAreaId));
|
||||
setGc(new GameComponentsClient(matchOptions, pong.ctx));
|
||||
}
|
||||
|
||||
export function destroyBase()
|
||||
{
|
||||
if (pong)
|
||||
{
|
||||
clearInterval(pong.handleInputInterval);
|
||||
clearInterval(pong.gameLoopInterval);
|
||||
clearInterval(pong.drawLoopInterval);
|
||||
setPong(null);
|
||||
}
|
||||
if (socket && socket.OPEN) {
|
||||
socket.close();
|
||||
}
|
||||
resetGameState();
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
|
||||
import * as c from "./constants.js"
|
||||
import { gc, pong } from "./global.js"
|
||||
import * as en from "../shared_js/enums.js"
|
||||
|
||||
/*
|
||||
before game
|
||||
*/
|
||||
export function error(message: string)
|
||||
{
|
||||
console.log("msg.error()");
|
||||
pong.clear();
|
||||
const text = "error: " + message;
|
||||
console.log(text);
|
||||
gc.text2.clear();
|
||||
gc.text2.pos.assign(c.w*0.2, c.h*0.5);
|
||||
gc.text2.text = text;
|
||||
gc.text2.update();
|
||||
}
|
||||
|
||||
export function matchmaking()
|
||||
{
|
||||
const text = "searching...";
|
||||
console.log(text);
|
||||
gc.text1.clear();
|
||||
gc.text1.pos.assign(c.w*0.2, c.h*0.5);
|
||||
gc.text1.text = text;
|
||||
gc.text1.update();
|
||||
}
|
||||
|
||||
export function matchmakingComplete()
|
||||
{
|
||||
const text = "match found !";
|
||||
console.log(text);
|
||||
gc.text1.clear();
|
||||
gc.text1.pos.assign(c.w*0.15, c.h*0.5);
|
||||
gc.text1.text = text;
|
||||
gc.text1.update();
|
||||
}
|
||||
|
||||
export function matchAbort()
|
||||
{
|
||||
const text = "match abort";
|
||||
console.log(text);
|
||||
gc.text1.clear();
|
||||
gc.text1.pos.assign(c.w*0.15, c.h*0.5);
|
||||
gc.text1.text = text;
|
||||
gc.text1.update();
|
||||
|
||||
setTimeout(() => {
|
||||
gc.text2.pos.assign(c.w*0.44, c.h*0.6);
|
||||
gc.text2.text = "pardon =(";
|
||||
const oriSize = gc.text2.size;
|
||||
gc.text2.size = c.w*0.025;
|
||||
gc.text2.update();
|
||||
gc.text2.size = oriSize;
|
||||
}, 2500);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
in game
|
||||
*/
|
||||
export function win()
|
||||
{
|
||||
gc.text1.pos.assign(c.w*0.415, c.h*0.5);
|
||||
gc.text1.text = "WIN";
|
||||
}
|
||||
|
||||
export function lose()
|
||||
{
|
||||
gc.text1.pos.assign(c.w*0.383, c.h*0.5);
|
||||
gc.text1.text = "LOSE";
|
||||
}
|
||||
|
||||
export function forfeit(playerSide: en.PlayerSide)
|
||||
{
|
||||
if (playerSide === en.PlayerSide.left) {
|
||||
gc.text2.pos.assign(c.w*0.65, c.h*0.42);
|
||||
gc.text3.pos.assign(c.w*0.65, c.h*0.52);
|
||||
}
|
||||
else {
|
||||
gc.text2.pos.assign(c.w*0.09, c.h*0.42);
|
||||
gc.text3.pos.assign(c.w*0.09, c.h*0.52);
|
||||
}
|
||||
setTimeout(() => {
|
||||
gc.text2.text = "par forfait";
|
||||
}, 1500);
|
||||
setTimeout(() => {
|
||||
gc.text3.text = "calme ta joie";
|
||||
}, 3500);
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
|
||||
import * as c from "./constants.js"
|
||||
import { handleInput } from "./handleInput.js";
|
||||
import { gameLoop } from "./gameLoop.js"
|
||||
import { drawLoop } from "./draw.js";
|
||||
import { countdown } from "./utils.js";
|
||||
import { initWebSocket } from "./ws.js";
|
||||
import type { InitOptions } from "./class/InitOptions.js";
|
||||
export { InitOptions } from "./class/InitOptions.js";
|
||||
import { initBase, destroyBase, computeMatchOptions } from "./init.js";
|
||||
export { computeMatchOptions } from "./init.js";
|
||||
|
||||
/* TODO: A way to delay the init of variables, but still use "const" not "let" ? */
|
||||
import { pong, gc } from "./global.js"
|
||||
import { setStartFunction } from "./global.js"
|
||||
|
||||
let abortControllerKeydown: AbortController;
|
||||
let abortControllerKeyup: AbortController;
|
||||
|
||||
export function init(options: InitOptions, gameAreaId: string, token: string)
|
||||
{
|
||||
const matchOptions = computeMatchOptions(options);
|
||||
initBase(matchOptions, options.sound, gameAreaId);
|
||||
|
||||
setStartFunction(start);
|
||||
if (options.isSomeoneIsInvited) {
|
||||
initWebSocket(matchOptions, token, options.playerOneUsername, true, options.playerTwoUsername, options.isInvitedPerson);
|
||||
}
|
||||
else {
|
||||
initWebSocket(matchOptions, token, options.playerOneUsername);
|
||||
}
|
||||
}
|
||||
|
||||
export function destroy()
|
||||
{
|
||||
destroyBase();
|
||||
if (abortControllerKeydown) {
|
||||
abortControllerKeydown.abort();
|
||||
abortControllerKeydown = null;
|
||||
}
|
||||
if (abortControllerKeyup) {
|
||||
abortControllerKeyup.abort();
|
||||
abortControllerKeyup = null;
|
||||
}
|
||||
}
|
||||
|
||||
function start()
|
||||
{
|
||||
gc.text1.pos.assign(c.w*0.5, c.h*0.75);
|
||||
countdown(c.matchStartDelay/1000, (count: number) => {
|
||||
gc.text1.clear();
|
||||
gc.text1.text = `${count}`;
|
||||
gc.text1.update();
|
||||
}, start_after_countdown);
|
||||
}
|
||||
|
||||
function start_after_countdown()
|
||||
{
|
||||
abortControllerKeydown = new AbortController();
|
||||
window.addEventListener(
|
||||
'keydown',
|
||||
(e) => { pong.addKey(e.key); },
|
||||
{signal: abortControllerKeydown.signal}
|
||||
);
|
||||
|
||||
abortControllerKeyup = new AbortController();
|
||||
window.addEventListener(
|
||||
'keyup',
|
||||
(e) => { pong.deleteKey(e.key);},
|
||||
{signal: abortControllerKeyup.signal}
|
||||
);
|
||||
|
||||
resume();
|
||||
}
|
||||
|
||||
function resume()
|
||||
{
|
||||
gc.text1.text = "";
|
||||
pong.handleInputInterval = window.setInterval(handleInput, c.handleInputIntervalMS);
|
||||
// pong.handleInputInterval = window.setInterval(sendLoop, c.sendLoopIntervalMS);
|
||||
pong.gameLoopInterval = window.setInterval(gameLoop, c.gameLoopIntervalMS);
|
||||
pong.drawLoopInterval = window.setInterval(drawLoop, c.drawLoopIntervalMS);
|
||||
}
|
||||
|
||||
function pause() // unused
|
||||
{
|
||||
clearInterval(pong.handleInputInterval);
|
||||
clearInterval(pong.gameLoopInterval);
|
||||
clearInterval(pong.drawLoopInterval);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
|
||||
import * as c from "./constants.js"
|
||||
import type * as en from "../shared_js/enums.js"
|
||||
import { gameLoopSpectator } from "./gameLoop.js"
|
||||
import { drawLoop } from "./draw.js";
|
||||
import { initWebSocketSpectator } from "./ws.js";
|
||||
import { initBase, destroyBase, computeMatchOptions } from "./init.js";
|
||||
export { computeMatchOptions } from "./init.js";
|
||||
export { MatchOptions } from "../shared_js/enums.js"
|
||||
|
||||
/* TODO: A way to delay the init of variables, but still use "const" not "let" ? */
|
||||
import { pong, gc } from "./global.js"
|
||||
import { setStartFunction } from "./global.js"
|
||||
|
||||
|
||||
export function init(matchOptions: en.MatchOptions, sound: string, gameAreaId: string, gameSessionId: string)
|
||||
{
|
||||
initBase(matchOptions, sound, gameAreaId);
|
||||
|
||||
setStartFunction(start);
|
||||
initWebSocketSpectator(gameSessionId);
|
||||
}
|
||||
|
||||
export function destroy()
|
||||
{
|
||||
destroyBase();
|
||||
}
|
||||
|
||||
function start()
|
||||
{
|
||||
resume();
|
||||
}
|
||||
|
||||
function resume()
|
||||
{
|
||||
pong.gameLoopInterval = window.setInterval(gameLoopSpectator, c.gameLoopIntervalMS);
|
||||
pong.drawLoopInterval = window.setInterval(drawLoop, c.drawLoopIntervalMS);
|
||||
}
|
||||
|
||||
function pause() // unused
|
||||
{
|
||||
clearInterval(pong.gameLoopInterval);
|
||||
clearInterval(pong.drawLoopInterval);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
export * from "../shared_js/utils.js"
|
||||
|
||||
export function countdown(count: number, callback?: (count: number) => void, endCallback?: () => void)
|
||||
{
|
||||
console.log("countdown ", count);
|
||||
if (count > 0) {
|
||||
if (callback) {
|
||||
callback(count);
|
||||
}
|
||||
setTimeout(countdown, 1000, --count, callback, endCallback);
|
||||
}
|
||||
else if (endCallback) {
|
||||
endCallback();
|
||||
}
|
||||
}
|
||||
331
srcs/requirements/svelte/api_front/src/pages/game/client/ws.ts
Normal file
331
srcs/requirements/svelte/api_front/src/pages/game/client/ws.ts
Normal file
@@ -0,0 +1,331 @@
|
||||
|
||||
import * as c from "./constants.js"
|
||||
import { gc, matchOptions, startFunction } from "./global.js"
|
||||
import * as ev from "../shared_js/class/Event.js"
|
||||
import * as en from "../shared_js/enums.js"
|
||||
import * as msg from "./message.js";
|
||||
import type { RacketClient } from "./class/RectangleClient.js";
|
||||
import { repeatInput } from "./handleInput.js";
|
||||
import { soundRoblox } from "./audio.js"
|
||||
import { sleep } from "./utils.js";
|
||||
import { Vector, VectorInteger } from "../shared_js/class/Vector.js";
|
||||
|
||||
export const gameState = {
|
||||
matchEnded: false,
|
||||
matchAbort: false
|
||||
}
|
||||
|
||||
export function resetGameState() {
|
||||
gameState.matchEnded = false;
|
||||
gameState.matchAbort = false;
|
||||
}
|
||||
|
||||
class ClientInfo {
|
||||
id = "";
|
||||
side: en.PlayerSide;
|
||||
racket: RacketClient;
|
||||
opponent: RacketClient;
|
||||
opponentNextPos: VectorInteger;
|
||||
}
|
||||
|
||||
class ClientInfoSpectator {
|
||||
// side: en.PlayerSide;
|
||||
/* WIP: playerLeftNextPos and playerRightNextPos could be in clientInfo for simplicity */
|
||||
playerLeftNextPos: VectorInteger;
|
||||
playerRightNextPos: VectorInteger;
|
||||
}
|
||||
|
||||
const wsUrl = "ws://" + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + "/pong";
|
||||
export let socket: WebSocket; /* TODO: A way to still use "const" not "let" ? */
|
||||
export const clientInfo = new ClientInfo();
|
||||
export const clientInfoSpectator = new ClientInfoSpectator(); // WIP, could refactor this
|
||||
|
||||
|
||||
export function initWebSocket(options: en.MatchOptions, token: string, username: string, privateMatch = false, playerTwoUsername?: string, isInvitedPerson? : boolean)
|
||||
{
|
||||
socket = new WebSocket(wsUrl, "json");
|
||||
console.log("Infos from ws.ts : options => " + options + " token => " + token + " username => " + username + " priavte match => " + privateMatch
|
||||
+ " player two => " + playerTwoUsername)
|
||||
socket.addEventListener("open", (event) => {
|
||||
if (privateMatch) {
|
||||
socket.send(JSON.stringify( new ev.ClientAnnouncePlayer(options, token, username, privateMatch, playerTwoUsername, isInvitedPerson) ));
|
||||
}
|
||||
else {
|
||||
socket.send(JSON.stringify( new ev.ClientAnnouncePlayer(options, token, username) ));
|
||||
}
|
||||
});
|
||||
// socket.addEventListener("message", logListener); // for testing purpose
|
||||
socket.addEventListener("message", errorListener);
|
||||
socket.addEventListener("message", preMatchListener);
|
||||
}
|
||||
|
||||
function logListener(this: WebSocket, event: MessageEvent) {
|
||||
console.log("%i: " + event.data, Date.now());
|
||||
}
|
||||
|
||||
function errorListener(this: WebSocket, event: MessageEvent) {
|
||||
const data: ev.ServerEvent = JSON.parse(event.data);
|
||||
if (data.type === en.EventTypes.error) {
|
||||
console.log("actual Error");
|
||||
msg.error((data as ev.EventError).message);
|
||||
}
|
||||
}
|
||||
|
||||
function preMatchListener(this: WebSocket, event: MessageEvent)
|
||||
{
|
||||
const data: ev.ServerEvent = JSON.parse(event.data);
|
||||
switch (data.type) {
|
||||
case en.EventTypes.assignId:
|
||||
clientInfo.id = (<ev.EventAssignId>data).id;
|
||||
break;
|
||||
case en.EventTypes.matchmakingInProgress:
|
||||
msg.matchmaking();
|
||||
break;
|
||||
case en.EventTypes.matchmakingComplete:
|
||||
clientInfo.side = (<ev.EventMatchmakingComplete>data).side;
|
||||
if (clientInfo.side === en.PlayerSide.left)
|
||||
{
|
||||
clientInfo.racket = gc.playerLeft;
|
||||
clientInfo.opponent = gc.playerRight;
|
||||
}
|
||||
else if (clientInfo.side === en.PlayerSide.right)
|
||||
{
|
||||
clientInfo.racket = gc.playerRight;
|
||||
clientInfo.opponent = gc.playerLeft;
|
||||
}
|
||||
clientInfo.opponentNextPos = new VectorInteger(clientInfo.opponent.pos.x, clientInfo.opponent.pos.y);
|
||||
clientInfo.racket.color = "darkgreen"; // for testing purpose
|
||||
socket.send(JSON.stringify( new ev.ClientEvent(en.EventTypes.clientPlayerReady) )); // TODO: set an interval/timeout to resend until matchStart response (in case of network problem)
|
||||
msg.matchmakingComplete();
|
||||
break;
|
||||
case en.EventTypes.matchStart:
|
||||
socket.removeEventListener("message", preMatchListener);
|
||||
socket.addEventListener("message", inGameListener);
|
||||
startFunction();
|
||||
break;
|
||||
case en.EventTypes.matchAbort:
|
||||
gameState.matchAbort = true;
|
||||
socket.removeEventListener("message", preMatchListener);
|
||||
msg.matchAbort();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function inGameListener(this: WebSocket, event: MessageEvent)
|
||||
{
|
||||
const data: ev.ServerEvent = JSON.parse(event.data);
|
||||
switch (data.type) {
|
||||
case en.EventTypes.gameUpdate:
|
||||
// setTimeout(gameUpdate, 500, data as ev.EventGameUpdate); // artificial latency for testing purpose
|
||||
gameUpdate(data as ev.EventGameUpdate);
|
||||
break;
|
||||
case en.EventTypes.scoreUpdate:
|
||||
scoreUpdate(data as ev.EventScoreUpdate);
|
||||
break;
|
||||
case en.EventTypes.matchEnd:
|
||||
matchEnd(data as ev.EventMatchEnd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function gameUpdate(data: ev.EventGameUpdate)
|
||||
{
|
||||
console.log("gameUpdate");
|
||||
|
||||
if (matchOptions & en.MatchOptions.movingWalls) {
|
||||
gc.wallTop.pos.y = data.wallTop.y;
|
||||
gc.wallBottom.pos.y = data.wallBottom.y;
|
||||
}
|
||||
|
||||
data.ballsArr.forEach((ball, i) => {
|
||||
gc.ballsArr[i].pos.assign(ball.x, ball.y);
|
||||
gc.ballsArr[i].dir.assign(ball.dirX, ball.dirY);
|
||||
gc.ballsArr[i].speed = ball.speed;
|
||||
});
|
||||
/* // Equivalent to
|
||||
gc.ballsArr.forEach((ball, i) => {
|
||||
ball.pos.assign(data.ballsArr[i].x, data.ballsArr[i].y);
|
||||
ball.dir.assign(data.ballsArr[i].dirX, data.ballsArr[i].dirY);
|
||||
ball.speed = data.ballsArr[i].speed;
|
||||
}); */
|
||||
|
||||
const predictionPos = new VectorInteger(clientInfo.racket.pos.x, clientInfo.racket.pos.y); // debug
|
||||
|
||||
if (clientInfo.side === en.PlayerSide.left) {
|
||||
clientInfo.racket.pos.assign(clientInfo.racket.pos.x, data.playerLeft.y);
|
||||
}
|
||||
else if (clientInfo.side === en.PlayerSide.right) {
|
||||
clientInfo.racket.pos.assign(clientInfo.racket.pos.x, data.playerRight.y);
|
||||
}
|
||||
|
||||
// interpolation
|
||||
clientInfo.opponent.pos.assign(clientInfo.opponentNextPos.x, clientInfo.opponentNextPos.y);
|
||||
if (clientInfo.side === en.PlayerSide.left) {
|
||||
clientInfo.opponentNextPos.assign(clientInfo.opponent.pos.x, data.playerRight.y);
|
||||
}
|
||||
else if (clientInfo.side === en.PlayerSide.right) {
|
||||
clientInfo.opponentNextPos.assign(clientInfo.opponent.pos.x, data.playerLeft.y);
|
||||
}
|
||||
|
||||
clientInfo.opponent.dir = new Vector(
|
||||
clientInfo.opponentNextPos.x - clientInfo.opponent.pos.x,
|
||||
clientInfo.opponentNextPos.y - clientInfo.opponent.pos.y
|
||||
);
|
||||
|
||||
if (Math.abs(clientInfo.opponent.dir.x) + Math.abs(clientInfo.opponent.dir.y) !== 0) {
|
||||
clientInfo.opponent.dir = clientInfo.opponent.dir.normalized();
|
||||
}
|
||||
|
||||
// server reconciliation
|
||||
repeatInput(data.lastInputId);
|
||||
|
||||
// debug
|
||||
if (clientInfo.racket.pos.y > predictionPos.y + 1
|
||||
|| clientInfo.racket.pos.y < predictionPos.y - 1)
|
||||
{
|
||||
console.log(
|
||||
`Reconciliation error:
|
||||
server y: ${data.playerLeft.y}
|
||||
reconciliation y: ${clientInfo.racket.pos.y}
|
||||
prediction y: ${predictionPos.y}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function scoreUpdate(data: ev.EventScoreUpdate)
|
||||
{
|
||||
// console.log("scoreUpdate");
|
||||
if (clientInfo.side === en.PlayerSide.left && data.scoreRight > gc.scoreRight.value) {
|
||||
soundRoblox.play();
|
||||
}
|
||||
else if (clientInfo.side === en.PlayerSide.right && data.scoreLeft > gc.scoreLeft.value) {
|
||||
soundRoblox.play();
|
||||
}
|
||||
gc.scoreLeft.value = data.scoreLeft;
|
||||
gc.scoreRight.value = data.scoreRight;
|
||||
}
|
||||
|
||||
function matchEnd(data: ev.EventMatchEnd)
|
||||
{
|
||||
gameState.matchEnded = true;
|
||||
socket.close();
|
||||
if (data.winner === clientInfo.side) {
|
||||
msg.win();
|
||||
if (data.forfeit) {
|
||||
msg.forfeit(clientInfo.side);
|
||||
}
|
||||
}
|
||||
else {
|
||||
msg.lose();
|
||||
}
|
||||
}
|
||||
|
||||
/* Spectator */
|
||||
|
||||
export function initWebSocketSpectator(gameSessionId: string)
|
||||
{
|
||||
socket = new WebSocket(wsUrl, "json");
|
||||
socket.addEventListener("open", (event) => {
|
||||
socket.send(JSON.stringify( new ev.ClientAnnounceSpectator(gameSessionId) ));
|
||||
});
|
||||
// socket.addEventListener("message", logListener); // for testing purpose
|
||||
socket.addEventListener("message", errorListener);
|
||||
socket.addEventListener("message", preMatchListenerSpectator);
|
||||
|
||||
clientInfoSpectator.playerLeftNextPos = new VectorInteger(gc.playerLeft.pos.x, gc.playerLeft.pos.y);
|
||||
clientInfoSpectator.playerRightNextPos = new VectorInteger(gc.playerRight.pos.x, gc.playerRight.pos.y);
|
||||
|
||||
}
|
||||
|
||||
export function preMatchListenerSpectator(this: WebSocket, event: MessageEvent)
|
||||
{
|
||||
const data: ev.ServerEvent = JSON.parse(event.data);
|
||||
if (data.type === en.EventTypes.matchStart)
|
||||
{
|
||||
socket.removeEventListener("message", preMatchListenerSpectator);
|
||||
socket.addEventListener("message", inGameListenerSpectator);
|
||||
socket.send(JSON.stringify( new ev.ClientEvent(en.EventTypes.clientSpectatorReady) ));
|
||||
startFunction();
|
||||
}
|
||||
}
|
||||
|
||||
function inGameListenerSpectator(this: WebSocket, event: MessageEvent)
|
||||
{
|
||||
const data: ev.ServerEvent = JSON.parse(event.data);
|
||||
switch (data.type) {
|
||||
case en.EventTypes.gameUpdate:
|
||||
gameUpdateSpectator(data as ev.EventGameUpdate);
|
||||
break;
|
||||
case en.EventTypes.scoreUpdate:
|
||||
scoreUpdateSpectator(data as ev.EventScoreUpdate);
|
||||
break;
|
||||
case en.EventTypes.matchEnd:
|
||||
matchEndSpectator(data as ev.EventMatchEnd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function gameUpdateSpectator(data: ev.EventGameUpdate)
|
||||
{
|
||||
console.log("gameUpdateSpectator");
|
||||
|
||||
if (matchOptions & en.MatchOptions.movingWalls) {
|
||||
gc.wallTop.pos.y = data.wallTop.y;
|
||||
gc.wallBottom.pos.y = data.wallBottom.y;
|
||||
}
|
||||
|
||||
data.ballsArr.forEach((ball, i) => {
|
||||
gc.ballsArr[i].pos.assign(ball.x, ball.y);
|
||||
gc.ballsArr[i].dir.assign(ball.dirX, ball.dirY);
|
||||
gc.ballsArr[i].speed = ball.speed;
|
||||
});
|
||||
|
||||
// interpolation
|
||||
for (const racket of [gc.playerLeft, gc.playerRight])
|
||||
{
|
||||
let nextPos: VectorInteger;
|
||||
if (racket === gc.playerLeft) {
|
||||
nextPos = clientInfoSpectator.playerLeftNextPos;
|
||||
}
|
||||
else {
|
||||
nextPos = clientInfoSpectator.playerRightNextPos;
|
||||
}
|
||||
|
||||
racket.pos.assign(nextPos.x, nextPos.y);
|
||||
if (racket === gc.playerLeft) {
|
||||
nextPos.assign(racket.pos.x, data.playerLeft.y);
|
||||
}
|
||||
else {
|
||||
nextPos.assign(racket.pos.x, data.playerRight.y);
|
||||
}
|
||||
|
||||
racket.dir = new Vector(
|
||||
nextPos.x - racket.pos.x,
|
||||
nextPos.y - racket.pos.y
|
||||
);
|
||||
|
||||
if (Math.abs(racket.dir.x) + Math.abs(racket.dir.y) !== 0) {
|
||||
racket.dir = racket.dir.normalized();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function scoreUpdateSpectator(data: ev.EventScoreUpdate)
|
||||
{
|
||||
console.log("scoreUpdateSpectator");
|
||||
gc.scoreLeft.value = data.scoreLeft;
|
||||
gc.scoreRight.value = data.scoreRight;
|
||||
}
|
||||
|
||||
function matchEndSpectator(data: ev.EventMatchEnd)
|
||||
{
|
||||
console.log("matchEndSpectator");
|
||||
gameState.matchEnded = true;
|
||||
socket.close();
|
||||
// WIP
|
||||
/* msg.win();
|
||||
if (data.forfeit) {
|
||||
msg.forfeit(clientInfo.side);
|
||||
} */
|
||||
}
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
|
||||
import * as en from "../enums.js"
|
||||
|
||||
/* From Server */
|
||||
export class ServerEvent {
|
||||
type: en.EventTypes;
|
||||
constructor(type: en.EventTypes = 0) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventAssignId extends ServerEvent {
|
||||
id: string;
|
||||
constructor(id: string) {
|
||||
super(en.EventTypes.assignId);
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventMatchmakingComplete extends ServerEvent {
|
||||
side: en.PlayerSide;
|
||||
constructor(side: en.PlayerSide) {
|
||||
super(en.EventTypes.matchmakingComplete);
|
||||
this.side = side;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventGameUpdate extends ServerEvent {
|
||||
playerLeft = {
|
||||
y: 0
|
||||
};
|
||||
playerRight = {
|
||||
y: 0
|
||||
};
|
||||
ballsArr: {
|
||||
x: number,
|
||||
y: number,
|
||||
dirX: number,
|
||||
dirY: number,
|
||||
speed: number
|
||||
}[] = [];
|
||||
wallTop? = {
|
||||
y: 0
|
||||
};
|
||||
wallBottom? = {
|
||||
y: 0
|
||||
};
|
||||
lastInputId = 0;
|
||||
constructor() { // TODO: constructor that take GameComponentsServer maybe ?
|
||||
super(en.EventTypes.gameUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
export class EventScoreUpdate extends ServerEvent {
|
||||
scoreLeft: number;
|
||||
scoreRight: number;
|
||||
constructor(scoreLeft: number, scoreRight: number) {
|
||||
super(en.EventTypes.scoreUpdate);
|
||||
this.scoreLeft = scoreLeft;
|
||||
this.scoreRight = scoreRight;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventMatchEnd extends ServerEvent {
|
||||
winner: en.PlayerSide;
|
||||
forfeit: boolean;
|
||||
constructor(winner: en.PlayerSide, forfeit = false) {
|
||||
super(en.EventTypes.matchEnd);
|
||||
this.winner = winner;
|
||||
this.forfeit = forfeit;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventMatchAbort extends ServerEvent {
|
||||
constructor() {
|
||||
super(en.EventTypes.matchAbort);
|
||||
}
|
||||
}
|
||||
|
||||
export class EventError extends ServerEvent {
|
||||
message: string;
|
||||
constructor(message: string) {
|
||||
super(en.EventTypes.error);
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* From Client */
|
||||
export class ClientEvent {
|
||||
type: en.EventTypes; // readonly ?
|
||||
constructor(type: en.EventTypes = 0) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientAnnounce extends ClientEvent {
|
||||
role: en.ClientRole;
|
||||
constructor(role: en.ClientRole) {
|
||||
super(en.EventTypes.clientAnnounce);
|
||||
this.role = role;
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientAnnouncePlayer extends ClientAnnounce {
|
||||
clientId: string; // unused
|
||||
matchOptions: en.MatchOptions;
|
||||
token: string;
|
||||
username: string;
|
||||
privateMatch: boolean;
|
||||
playerTwoUsername?: string;
|
||||
isInvitedPerson? : boolean;
|
||||
constructor(matchOptions: en.MatchOptions, token: string, username: string, privateMatch: boolean = false, playerTwoUsername?: string, isInvitedPerson? : boolean) {
|
||||
super(en.ClientRole.player);
|
||||
this.matchOptions = matchOptions;
|
||||
this.token = token;
|
||||
this.username = username;
|
||||
this.privateMatch = privateMatch;
|
||||
if (isInvitedPerson) {
|
||||
this.isInvitedPerson = isInvitedPerson;
|
||||
}
|
||||
if (playerTwoUsername) {
|
||||
this.playerTwoUsername = playerTwoUsername;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientAnnounceSpectator extends ClientAnnounce {
|
||||
gameSessionId: string;
|
||||
constructor(gameSessionId: string) {
|
||||
super(en.ClientRole.spectator);
|
||||
this.gameSessionId = gameSessionId;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventInput extends ClientEvent {
|
||||
input: en.InputEnum;
|
||||
id: number;
|
||||
constructor(input: en.InputEnum = en.InputEnum.noInput, id: number = 0) {
|
||||
super(en.EventTypes.clientInput);
|
||||
this.input = input;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
|
||||
import * as c from "../constants.js"
|
||||
import * as en from "../../shared_js/enums.js"
|
||||
import { VectorInteger } from "./Vector.js";
|
||||
import { Rectangle, MovingRectangle, Racket, Ball } from "./Rectangle.js";
|
||||
import { random } from "../utils.js";
|
||||
|
||||
export class GameComponents {
|
||||
wallTop: Rectangle | MovingRectangle;
|
||||
wallBottom: Rectangle | MovingRectangle;
|
||||
playerLeft: Racket;
|
||||
playerRight: Racket;
|
||||
ballsArr: Ball[] = [];
|
||||
constructor(options: en.MatchOptions)
|
||||
{
|
||||
const pos = new VectorInteger;
|
||||
|
||||
// Rackets
|
||||
pos.assign(0+c.pw, c.h_mid-c.ph/2);
|
||||
this.playerLeft = new Racket(pos, c.pw, c.ph, c.racketSpeed);
|
||||
pos.assign(c.w-c.pw-c.pw, c.h_mid-c.ph/2);
|
||||
this.playerRight = new Racket(pos, c.pw, c.ph, c.racketSpeed);
|
||||
|
||||
// Balls
|
||||
let ballsCount = 1;
|
||||
if (options & en.MatchOptions.multiBalls) {
|
||||
ballsCount = c.multiBallsCount;
|
||||
}
|
||||
pos.assign(-c.ballSize, -c.ballSize); // ball out =)
|
||||
while (this.ballsArr.length < ballsCount) {
|
||||
this.ballsArr.push(new Ball(pos, c.ballSize, c.ballSpeed, c.ballSpeedIncrease))
|
||||
}
|
||||
this.ballsArr.forEach((ball) => {
|
||||
ball.dir.x = 1;
|
||||
if (random() > 0.5) {
|
||||
ball.dir.x *= -1;
|
||||
}
|
||||
|
||||
ball.dir.y = random(0, 0.2);
|
||||
if (random() > 0.5) {
|
||||
ball.dir.y *= -1;
|
||||
}
|
||||
|
||||
ball.dir = ball.dir.normalized();
|
||||
});
|
||||
|
||||
// Walls
|
||||
if (options & en.MatchOptions.movingWalls) {
|
||||
pos.assign(0, 0);
|
||||
this.wallTop = new MovingRectangle(pos, c.w, c.wallSize, c.movingWallSpeed);
|
||||
(<MovingRectangle>this.wallTop).dir.y = -1;
|
||||
pos.assign(0, c.h-c.wallSize);
|
||||
this.wallBottom = new MovingRectangle(pos, c.w, c.wallSize, c.movingWallSpeed);
|
||||
(<MovingRectangle>this.wallBottom).dir.y = 1;
|
||||
}
|
||||
else {
|
||||
pos.assign(0, 0);
|
||||
this.wallTop = new Rectangle(pos, c.w, c.wallSize);
|
||||
pos.assign(0, c.h-c.wallSize);
|
||||
this.wallBottom = new Rectangle(pos, c.w, c.wallSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
|
||||
import { Vector, VectorInteger } from "./Vector.js";
|
||||
import type { Component, Moving } from "./interface.js";
|
||||
import * as c from "../constants.js"
|
||||
|
||||
export class Rectangle implements Component {
|
||||
pos: VectorInteger;
|
||||
width: number;
|
||||
height: number;
|
||||
constructor(pos: VectorInteger, width: number, height: number) {
|
||||
this.pos = new VectorInteger(pos.x, pos.y);
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
collision(collider: Rectangle): boolean {
|
||||
const thisLeft = this.pos.x;
|
||||
const thisRight = this.pos.x + this.width;
|
||||
const thisTop = this.pos.y;
|
||||
const thisBottom = this.pos.y + this.height;
|
||||
const colliderLeft = collider.pos.x;
|
||||
const colliderRight = collider.pos.x + collider.width;
|
||||
const colliderTop = collider.pos.y;
|
||||
const colliderBottom = collider.pos.y + collider.height;
|
||||
if ((thisBottom < colliderTop)
|
||||
|| (thisTop > colliderBottom)
|
||||
|| (thisRight < colliderLeft)
|
||||
|| (thisLeft > colliderRight)) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class MovingRectangle extends Rectangle implements Moving {
|
||||
dir: Vector = new Vector(0,0);
|
||||
speed: number;
|
||||
readonly baseSpeed: number;
|
||||
constructor(pos: VectorInteger, width: number, height: number, baseSpeed: number) {
|
||||
super(pos, width, height);
|
||||
this.baseSpeed = baseSpeed;
|
||||
this.speed = baseSpeed;
|
||||
}
|
||||
move(delta: number) { // Math.floor WIP until VectorInteger debug
|
||||
// console.log(`delta: ${delta}, speed: ${this.speed}, speed*delta: ${this.speed * delta}`);
|
||||
// this.pos.x += Math.floor(this.dir.x * this.speed * delta);
|
||||
// this.pos.y += Math.floor(this.dir.y * this.speed * delta);
|
||||
this.pos.x += this.dir.x * this.speed * delta;
|
||||
this.pos.y += this.dir.y * this.speed * delta;
|
||||
}
|
||||
moveAndCollide(delta: number, colliderArr: Rectangle[]) {
|
||||
this._moveAndCollideAlgo(delta, colliderArr);
|
||||
}
|
||||
protected _moveAndCollideAlgo(delta: number, colliderArr: Rectangle[]) {
|
||||
let oldPos = new VectorInteger(this.pos.x, this.pos.y);
|
||||
this.move(delta);
|
||||
if (colliderArr.some(this.collision, this)) {
|
||||
this.pos = oldPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Racket extends MovingRectangle {
|
||||
constructor(pos: VectorInteger, width: number, height: number, baseSpeed: number) {
|
||||
super(pos, width, height, baseSpeed);
|
||||
}
|
||||
moveAndCollide(delta: number, colliderArr: Rectangle[]) {
|
||||
// let oldPos = new VectorInteger(this.pos.x, this.pos.y); // debug
|
||||
this._moveAndCollideAlgo(delta, colliderArr);
|
||||
// console.log(`y change: ${this.pos.y - oldPos.y}`);
|
||||
}
|
||||
}
|
||||
|
||||
export class Ball extends MovingRectangle {
|
||||
readonly speedIncrease: number;
|
||||
ballInPlay: boolean = false;
|
||||
constructor(pos: VectorInteger, size: number, baseSpeed: number, speedIncrease: number) {
|
||||
super(pos, size, size, baseSpeed);
|
||||
this.speedIncrease = speedIncrease;
|
||||
}
|
||||
moveAndBounce(delta: number, colliderArr: Rectangle[]) {
|
||||
this.move(delta);
|
||||
let i = colliderArr.findIndex(this.collision, this);
|
||||
if (i != -1)
|
||||
{
|
||||
this.bounce(colliderArr[i]);
|
||||
this.move(delta);
|
||||
}
|
||||
}
|
||||
bounce(collider?: Rectangle) {
|
||||
this._bounceAlgo(collider);
|
||||
}
|
||||
protected _bounceAlgo(collider?: Rectangle) {
|
||||
/* Could be more generic, but testing only Racket is enough,
|
||||
because in Pong collider can only be Racket or Wall. */
|
||||
if (collider instanceof Racket) {
|
||||
this._bounceRacket(collider);
|
||||
}
|
||||
else {
|
||||
this._bounceWall();
|
||||
}
|
||||
}
|
||||
protected _bounceWall() { // Should be enough for Wall
|
||||
this.dir.y = this.dir.y * -1;
|
||||
}
|
||||
protected _bounceRacket(racket: Racket) {
|
||||
this._bounceRacketAlgo(racket);
|
||||
}
|
||||
protected _bounceRacketAlgo(racket: Racket) {
|
||||
this.speed += this.speedIncrease;
|
||||
|
||||
let x = this.dir.x * -1;
|
||||
|
||||
const angleFactorDegree = 60;
|
||||
const angleFactor = angleFactorDegree / 90;
|
||||
const racketHalf = racket.height/2;
|
||||
const ballMid = this.pos.y + this.height/2;
|
||||
const racketMid = racket.pos.y + racketHalf;
|
||||
|
||||
let impact = ballMid - racketMid;
|
||||
const horizontalMargin = racketHalf * 0.15;
|
||||
if (impact < horizontalMargin && impact > -horizontalMargin) {
|
||||
impact = 0;
|
||||
}
|
||||
else if (impact > 0) {
|
||||
impact = impact - horizontalMargin;
|
||||
}
|
||||
else if (impact < 0) {
|
||||
impact = impact + horizontalMargin;
|
||||
}
|
||||
|
||||
let y = impact / (racketHalf - horizontalMargin) * angleFactor;
|
||||
|
||||
this.dir.assign(x, y);
|
||||
// Normalize Vector (for consistency in speed independent of direction)
|
||||
if (c.normalizedSpeed) {
|
||||
this.dir = this.dir.normalized();
|
||||
}
|
||||
// console.log(`x: ${this.dir.x}, y: ${this.dir.y}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
|
||||
export class Vector {
|
||||
x: number;
|
||||
y: number;
|
||||
constructor(x: number = 0, y: number = 0) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
assign(x: number, y: number) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
normalized() : Vector {
|
||||
const normalizationFactor = Math.abs(this.x) + Math.abs(this.y);
|
||||
return new Vector(this.x/normalizationFactor, this.y/normalizationFactor);
|
||||
}
|
||||
}
|
||||
|
||||
export class VectorInteger extends Vector {
|
||||
// PLACEHOLDER
|
||||
// VectorInteger with set/get dont work (No draw on the screen). Why ?
|
||||
}
|
||||
|
||||
/*
|
||||
export class VectorInteger {
|
||||
// private _x: number = 0;
|
||||
// private _y: number = 0;
|
||||
// constructor(x: number = 0, y: number = 0) {
|
||||
// this._x = x;
|
||||
// this._y = y;
|
||||
// }
|
||||
// get x(): number {
|
||||
// return this._x;
|
||||
// }
|
||||
// set x(v: number) {
|
||||
// // this._x = Math.floor(v);
|
||||
// this._x = v;
|
||||
// }
|
||||
// get y(): number {
|
||||
// return this._y;
|
||||
// }
|
||||
// set y(v: number) {
|
||||
// // this._y = Math.floor(v);
|
||||
// this._y = v;
|
||||
// }
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
import type { Vector, VectorInteger } from "./Vector.js";
|
||||
|
||||
export interface Component {
|
||||
pos: VectorInteger;
|
||||
}
|
||||
|
||||
export interface GraphicComponent extends Component {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
color: string;
|
||||
update: () => void;
|
||||
clear: (pos?: VectorInteger) => void;
|
||||
}
|
||||
|
||||
export interface Moving {
|
||||
dir: Vector;
|
||||
speed: number; // pixel per second
|
||||
move(delta: number): void;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
export const CanvasWidth = 1500;
|
||||
export const CanvasRatio = 1.66666;
|
||||
/* ratio 5/3 (1.66) */
|
||||
|
||||
export const w = CanvasWidth;
|
||||
export const h = CanvasWidth / CanvasRatio;
|
||||
export const w_mid = Math.floor(w/2);
|
||||
export const h_mid = Math.floor(h/2);
|
||||
export const pw = Math.floor(w*0.017);
|
||||
export const ph = pw*6;
|
||||
export const ballSize = pw;
|
||||
export const wallSize = Math.floor(w*0.01);
|
||||
export const racketSpeed = Math.floor(w*0.60); // pixel per second
|
||||
export const ballSpeed = Math.floor(w*0.55); // pixel per second
|
||||
export const ballSpeedIncrease = Math.floor(ballSpeed*0.05); // pixel per second
|
||||
|
||||
export const normalizedSpeed = false; // for consistency in speed independent of direction
|
||||
|
||||
export const matchStartDelay = 3000; // millisecond
|
||||
export const newRoundDelay = 1500; // millisecond
|
||||
|
||||
// Game Variantes
|
||||
export const multiBallsCount = 3;
|
||||
export const movingWallPosMax = Math.floor(w*0.12);
|
||||
export const movingWallSpeed = Math.floor(w*0.08);
|
||||
|
||||
|
||||
export const gameSessionIdPLACEHOLDER = "match-id-test-42"; // TESTING SPECTATOR PLACEHOLDER
|
||||
// for testing, force gameSession.id in wsServer.ts->createGameSession()
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
export enum EventTypes {
|
||||
// Class Implemented
|
||||
gameUpdate = 1,
|
||||
scoreUpdate,
|
||||
matchEnd,
|
||||
assignId,
|
||||
matchmakingComplete,
|
||||
error,
|
||||
|
||||
// Generic
|
||||
matchmakingInProgress,
|
||||
matchStart,
|
||||
matchAbort,
|
||||
matchNewRound, // unused
|
||||
matchPause, // unused
|
||||
matchResume, // unused
|
||||
|
||||
// Client
|
||||
clientAnnounce,
|
||||
clientPlayerReady,
|
||||
clientSpectatorReady,
|
||||
clientInput,
|
||||
|
||||
}
|
||||
|
||||
export enum InputEnum {
|
||||
noInput = 0,
|
||||
up = 1,
|
||||
down,
|
||||
}
|
||||
|
||||
export enum PlayerSide {
|
||||
left = 1,
|
||||
right
|
||||
}
|
||||
|
||||
export enum ClientRole {
|
||||
player = 1,
|
||||
spectator
|
||||
}
|
||||
|
||||
export enum MatchOptions {
|
||||
// binary flags, can be mixed
|
||||
noOption = 0b0,
|
||||
multiBalls = 1 << 0,
|
||||
movingWalls = 1 << 1
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
import type { MovingRectangle } from "./class/Rectangle.js";
|
||||
|
||||
export function random(min: number = 0, max: number = 1) {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
export function sleep (ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
export function clamp(n: number, min: number, max: number) : number
|
||||
{
|
||||
if (n < min)
|
||||
n = min;
|
||||
else if (n > max)
|
||||
n = max;
|
||||
return (n);
|
||||
}
|
||||
|
||||
// Typescript hack, unused
|
||||
export function assertMovingRectangle(value: unknown): asserts value is MovingRectangle {
|
||||
// if (value !== MovingRectangle) throw new Error("Not a MovingRectangle");
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
import * as c from "./constants.js";
|
||||
import type { MovingRectangle } from "../shared_js/class/Rectangle.js";
|
||||
import type { GameComponents } from "./class/GameComponents.js";
|
||||
|
||||
export function wallsMovements(delta: number, gc: GameComponents)
|
||||
{
|
||||
const wallTop = <MovingRectangle>gc.wallTop;
|
||||
const wallBottom = <MovingRectangle>gc.wallBottom;
|
||||
if (wallTop.pos.y <= 0 || wallTop.pos.y >= c.movingWallPosMax) {
|
||||
wallTop.dir.y *= -1;
|
||||
}
|
||||
if (wallBottom.pos.y >= c.h-c.wallSize || wallBottom.pos.y <= c.h-c.movingWallPosMax) {
|
||||
wallBottom.dir.y *= -1;
|
||||
}
|
||||
wallTop.moveAndCollide(delta, [gc.playerLeft, gc.playerRight]);
|
||||
wallBottom.moveAndCollide(delta, [gc.playerLeft, gc.playerRight]);
|
||||
}
|
||||
@@ -1,21 +1,24 @@
|
||||
<script lang="ts">
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
import GenerateUserDisplay from '../../pieces/GenerateUserDisplay.svelte';
|
||||
import GenerateUserDisplay from '../../pieces/GenerateUserDisplay.svelte';
|
||||
|
||||
|
||||
import Chat from '../../pieces/chat/Chat.svelte';
|
||||
|
||||
let user;
|
||||
|
||||
onMount( async() => {
|
||||
// console.log('mounting profile display')
|
||||
user = await fetch('http://transcendance:8080/api/v2/user')
|
||||
user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`)
|
||||
.then( (x) => x.json() );
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<Chat color="bisque"/>
|
||||
|
||||
<!-- is this if excessive? -->
|
||||
<div class="outer">
|
||||
<!-- OHHHH i could use #await instead of if and have an nice loading page! -->
|
||||
@@ -28,40 +31,6 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
<div>testing when there's tons of stuff</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
div.outer{
|
||||
@@ -115,7 +84,7 @@
|
||||
/* Glittery Star Stuff */
|
||||
|
||||
|
||||
:root {
|
||||
:root {
|
||||
--purple: rgb(123, 31, 162);
|
||||
--violet: rgb(103, 58, 183);
|
||||
--pink: rgb(244, 143, 177);
|
||||
@@ -126,7 +95,7 @@
|
||||
from {
|
||||
background-position: 0% center;
|
||||
}
|
||||
|
||||
|
||||
to {
|
||||
background-position: -200% center;
|
||||
}
|
||||
@@ -136,7 +105,7 @@
|
||||
from, to {
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
|
||||
50% {
|
||||
transform: scale(1);
|
||||
}
|
||||
@@ -146,7 +115,7 @@
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
|
||||
to {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
@@ -191,7 +160,7 @@
|
||||
var(--purple)
|
||||
);
|
||||
background-size: 200%;
|
||||
|
||||
|
||||
/* Keep these for Safari and chrome */
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
@@ -203,4 +172,4 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
// yea no idea what
|
||||
// i mean do i fetch user? i will for now
|
||||
user = await fetch('http://transcendance:8080/api/v2/user')
|
||||
user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`)
|
||||
.then( (x) => x.json() );
|
||||
|
||||
fetchAll();
|
||||
@@ -53,7 +53,7 @@
|
||||
|
||||
/***** Fetch basic things *****/
|
||||
const fetchAllUsers = async() => {
|
||||
allUsers = await fetch('http://transcendance:8080/api/v2/user/all')
|
||||
allUsers = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/all`)
|
||||
.then( x => x.json() );
|
||||
console.log('got all users ')
|
||||
console.log({...allUsers})
|
||||
@@ -62,28 +62,28 @@
|
||||
// it's more like fetch friendships
|
||||
// then i need to extract the users
|
||||
const fetchMyFriendships = async() => {
|
||||
myFriendships = await fetch('http://transcendance:8080/api/v2/network/myfriends')
|
||||
myFriendships = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/myfriends`)
|
||||
.then( x => x.json() );
|
||||
console.log('got my friends ')
|
||||
console.log({...myFriendships})
|
||||
};
|
||||
|
||||
const fetchRequestsMade = async() => {
|
||||
requestsMade = await fetch('http://transcendance:8080/api/v2/network/pending')
|
||||
requestsMade = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/pending`)
|
||||
.then( x => x.json() );
|
||||
console.log('got requests made ')
|
||||
console.log({...requestsMade})
|
||||
};
|
||||
|
||||
const fetchRequestsReceived = async() => {
|
||||
requestsRecieved = await fetch('http://transcendance:8080/api/v2/network/received')
|
||||
requestsRecieved = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/received`)
|
||||
.then( x => x.json() );
|
||||
console.log('got requests received ')
|
||||
console.log({...requestsRecieved})
|
||||
};
|
||||
|
||||
const fetchBlockedUsers = async() => {
|
||||
blockedUsers = await fetch('http://transcendance:8080/api/v2/network/blocked')
|
||||
blockedUsers = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/blocked`)
|
||||
.then( x => x.json() );
|
||||
console.log('got blocked users, is it empty?')
|
||||
console.log({...blockedUsers})
|
||||
@@ -95,7 +95,7 @@
|
||||
const fetchFriendshipFull = async(aUsername) => {
|
||||
console.log('fetch friendship from a username')
|
||||
console.log(aUsername)
|
||||
friendshipStatusFull = await fetch(`http://transcendance:8080/api/v2/network/myfriends?username=${aUsername}`)
|
||||
friendshipStatusFull = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/myfriends?username=${aUsername}`)
|
||||
.then( x => x.json());
|
||||
console.log({...friendshipStatusFull})
|
||||
};
|
||||
@@ -104,7 +104,7 @@
|
||||
const sendFriendRequest = async(aUsername) => {
|
||||
console.log('sending a friend request')
|
||||
console.log(aUsername)
|
||||
const resp = await fetch("http://transcendance:8080/api/v2/network/relations", {
|
||||
const resp = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/relations`, {
|
||||
method : "POST",
|
||||
headers: { 'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
@@ -134,7 +134,7 @@
|
||||
const acceptFriendRequest = async(relationshipId) => {
|
||||
console.log('accept friend request')
|
||||
|
||||
const resp = await fetch(`http://transcendance:8080/api/v2/network/relations/${relationshipId}/accept`, {
|
||||
const resp = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/relations/${relationshipId}/accept`, {
|
||||
method: "PATCH"})
|
||||
.then( x => x.json());
|
||||
// maybe not the most robust things, not super reusable cuz it depends on outside vars but works for now...
|
||||
@@ -148,7 +148,7 @@
|
||||
|
||||
const declineFriendRequest = async(relationshipId) => {
|
||||
console.log('decline friend request')
|
||||
const resp = await fetch(`http://transcendance:8080/api/v2/network/relations/${relationshipId}/decline`, {
|
||||
const resp = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/relations/${relationshipId}/decline`, {
|
||||
method: "PATCH"})
|
||||
.then( x => x.json());
|
||||
// maybe not the most robust things, not super reusable cuz it depends on outside vars but works for now...
|
||||
@@ -159,7 +159,7 @@
|
||||
|
||||
const unfriend = async(relationshipId) => {
|
||||
console.log('Unfriend')
|
||||
const resp = await fetch(`http://transcendance:8080/api/v2/network/relations/${relationshipId}`, {
|
||||
const resp = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/relations/${relationshipId}`, {
|
||||
method: "DELETE"})
|
||||
.then( x => x.json());
|
||||
|
||||
@@ -177,15 +177,15 @@
|
||||
console.log('Block a non friend user, their username')
|
||||
console.log(aUsername)
|
||||
|
||||
const blockResp = await fetch("http://transcendance:8080/api/v2/network/relations", {
|
||||
method : "POST",
|
||||
headers: { 'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
"receiverUsername": aUsername,
|
||||
"status": "B"
|
||||
})
|
||||
sentFriendRequest = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/relations`, {
|
||||
method : "POST",
|
||||
headers: { 'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
"receiverUsername": aUsername,
|
||||
"status": "B"
|
||||
})
|
||||
.then( x => x.json())
|
||||
})
|
||||
.then( x => x.json())
|
||||
await fetchBlockedUsers();
|
||||
await fetchAllUsers();
|
||||
usernameBeingViewed = undefined;
|
||||
@@ -198,7 +198,7 @@
|
||||
const blockAFriend = async(relationshipId) => {
|
||||
console.log('blocking a friend, the relationshipID')
|
||||
console.log(relationshipId)
|
||||
const resp = await fetch(`http://transcendance:8080/api/v2/network/relations/${relationshipId}/block`, {method: "PATCH"})
|
||||
const resp = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/relations/${relationshipId}/block`, {method: "PATCH"})
|
||||
.then( x => x.json() );
|
||||
console.log('blocked a user response')
|
||||
console.log({...resp})
|
||||
|
||||
@@ -4,55 +4,11 @@
|
||||
import Router from "svelte-spa-router";
|
||||
import { profileRoutes, prefix } from "../../routes/profileRoutes.js";
|
||||
|
||||
// let dispatch = createEventDispatcher();
|
||||
|
||||
|
||||
// what if i did the fetch in ProfilePage rather than in each ProfileDisplay and ProfileSettings
|
||||
// i mean it would update each time no matter what right? cuz onMount? and then i keep the results in
|
||||
// a Store and it's all good right?
|
||||
|
||||
</script>
|
||||
|
||||
<!-- remove ={clickedHome} if you want to forward the event to App.svelte-->
|
||||
<Header />
|
||||
|
||||
<!-- The Wave -->
|
||||
<!-- <div class="spacer layer1"></div> -->
|
||||
|
||||
<div>
|
||||
<Router routes={profileRoutes} {prefix} />
|
||||
</div>
|
||||
|
||||
<!-- <Footer /> -->
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
/* this doesn't work, fucks up all my sub routes */
|
||||
/* div {
|
||||
max-width: 960px;
|
||||
margin: 40px auto;
|
||||
} */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* from Haikei */
|
||||
/* for any Haikei image */
|
||||
|
||||
/* .spacer{
|
||||
aspect-ratio: 900/300;
|
||||
width: 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
} */
|
||||
|
||||
/* the specific image we use, you need both classes */
|
||||
|
||||
/* .layer1{
|
||||
background-image: url('/img/wave-haikei.svg');
|
||||
} */
|
||||
|
||||
</style>
|
||||
@@ -3,6 +3,7 @@
|
||||
import Card from '../../pieces/Card.svelte';
|
||||
import {onMount} from 'svelte';
|
||||
import { push } from 'svelte-spa-router';
|
||||
|
||||
import Button from '../../pieces/Button.svelte';
|
||||
|
||||
let user;
|
||||
@@ -16,7 +17,7 @@
|
||||
let success = {username: '', avatar: '' };
|
||||
|
||||
onMount( async() => {
|
||||
user = await fetch('http://transcendance:8080/api/v2/user')
|
||||
user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`)
|
||||
.then( (x) => x.json() );
|
||||
// do a .catch?
|
||||
|
||||
@@ -33,7 +34,7 @@
|
||||
// console.log('this is what is in the avatar before fetch')
|
||||
// console.log(avatar)
|
||||
|
||||
await fetch("http://transcendance:8080/api/v2/user/avatar", {method: "GET"})
|
||||
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/avatar`, {method: "GET"})
|
||||
.then(response => {return response.blob()})
|
||||
.then(data => {
|
||||
const url = URL.createObjectURL(data);
|
||||
@@ -63,7 +64,7 @@
|
||||
else {
|
||||
errors.username = '';
|
||||
}
|
||||
await fetch('http://transcendance:8080/api/v2/user',{
|
||||
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
@@ -100,15 +101,21 @@
|
||||
// tmp
|
||||
console.log(data);
|
||||
|
||||
await fetch("http://transcendance:8080/api/v2/user/avatar",
|
||||
const responseWhenChangeAvatar = fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/avatar`,
|
||||
{
|
||||
method : 'POST',
|
||||
body : data,
|
||||
})
|
||||
.then(() => uploadAvatarSuccess = true ) // for some reason it needs to be a function, i think a TS thing, not a promis otherwise
|
||||
.then(() => success.avatar = 'Your changes have been saved')
|
||||
.catch(() => errors.avatar = 'Sorry failed to upload your new Avatar' );
|
||||
await fetch("http://transcendance:8080/api/v2/user/avatar", {method: "GET"})
|
||||
const responseFromServer = await responseWhenChangeAvatar;
|
||||
if (responseFromServer.ok === true) {
|
||||
uploadAvatarSuccess = true;
|
||||
success.avatar = 'Your avatar has been updated';
|
||||
}
|
||||
else {
|
||||
errors.avatar = responseFromServer.statusText;
|
||||
}
|
||||
|
||||
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/avatar`, {method: "GET"})
|
||||
.then(response => {return response.blob()})
|
||||
.then(data => {
|
||||
const url = URL.createObjectURL(data);
|
||||
|
||||
@@ -45,4 +45,4 @@
|
||||
background: white;
|
||||
border: 2px solid #45c496;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -8,15 +8,10 @@
|
||||
let user;
|
||||
|
||||
onMount( async() => {
|
||||
// console.log('Display aUser username: '+ aUsername)
|
||||
// http://transcendance:8080/api/v2/user?username=NomDuUserATrouver
|
||||
// user = await fetch(`http://transcendance:8080/api/v2/user?username=${aUsername}`)
|
||||
user = await fetch(`http://transcendance:8080/api/v2/user?username=${aUsername}`)
|
||||
console.log('Display aUser username: '+ aUsername)
|
||||
//`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user?username=NomDuUserATrouve`
|
||||
user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user?username=${aUsername}`)
|
||||
.then( (x) => x.json() );
|
||||
|
||||
// console.log('Display a user: ')
|
||||
// console.log({...user})
|
||||
|
||||
})
|
||||
|
||||
// sadly created an infinite loop
|
||||
@@ -32,7 +27,7 @@
|
||||
// console.log('Display Update aUser username: '+ aUsername)
|
||||
// http://transcendance:8080/api/v2/user?username=NomDuUserATrouver
|
||||
// user = await fetch(`http://transcendance:8080/api/v2/user?username=${aUsername}`)
|
||||
user = await fetch(`http://transcendance:8080/api/v2/user?username=${aUsername}`)
|
||||
user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user?username=${aUsername}`)
|
||||
.then( (x) => x.json() );
|
||||
};
|
||||
|
||||
@@ -43,7 +38,6 @@
|
||||
</script>
|
||||
|
||||
|
||||
<!-- OHHHH i could use #await instead of if and have an nice loading page! -->
|
||||
{#if user !== undefined}
|
||||
<GenerateUserDisplay user={user} primary={true}/>
|
||||
<!-- <GenerateUserDisplay user={user} primary={true}/> -->
|
||||
@@ -55,4 +49,4 @@
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<footer>
|
||||
<div class="copyright">I am official I have a Copyright in 2022</div>
|
||||
<div class="copyright">I am official I have a Copyright in 2023</div>
|
||||
</footer>
|
||||
|
||||
<style>
|
||||
@@ -15,4 +15,4 @@
|
||||
padding: 20px;
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
|
||||
export let user;
|
||||
export let primary;
|
||||
let rank = '';
|
||||
@@ -11,21 +12,15 @@
|
||||
onMount( async() => {
|
||||
// using this for now cuz for some reason there is yet to be a way to fet another person's avatar
|
||||
if (primary) {
|
||||
await fetch("http://transcendance:8080/api/v2/user/avatar", {method: "GET"})
|
||||
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/avatar`, {method: "GET"})
|
||||
.then(response => {return response.blob()})
|
||||
.then(data => {
|
||||
const url = URL.createObjectURL(data);
|
||||
avatar = url;
|
||||
});
|
||||
}
|
||||
// tmp
|
||||
// console.log('mounted Profile Display')
|
||||
// console.log(user);
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
if (user.loseGame > user.winGame) {
|
||||
rank = 'Bitch Ass Loser!'
|
||||
} else if (user.loseGame === user.winGame) {
|
||||
@@ -39,12 +34,10 @@
|
||||
|
||||
let index = 0, interval = 1000;
|
||||
|
||||
const rand = (min, max) =>
|
||||
const rand = (min, max) =>
|
||||
Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
|
||||
// it's unhappy that "star" isn't typeset, no idea what to do about it...
|
||||
const animate = (star) => {
|
||||
// the if seems to have fixed the type issue
|
||||
if (star) {
|
||||
star.style.setProperty("--star-left", `${rand(-10, 100)}%`);
|
||||
star.style.setProperty("--star-top", `${rand(-40, 80)}%`);
|
||||
@@ -55,13 +48,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
// This is the part i invented, it was kinda a fucking nightmare...
|
||||
let stars = [];
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
setTimeout(() => {
|
||||
animate(stars[i]);
|
||||
|
||||
|
||||
setInterval(() => animate(stars[i]), 1000);
|
||||
}, index++ * (interval / 3))
|
||||
}
|
||||
@@ -76,7 +68,7 @@
|
||||
<!-- <img class="icon" src="{user.image_url}" alt="default user icon"> -->
|
||||
<img class="avatar" src="{avatar}" alt="default user icon">
|
||||
<div class="username">{user.username}</div>
|
||||
<div class="rank">Rank:
|
||||
<div class="rank">Rank:
|
||||
<span class="glitter">
|
||||
<span bind:this={stars[0]} class="glitter-star">
|
||||
<svg viewBox="0 0 512 512">
|
||||
@@ -162,7 +154,7 @@
|
||||
/* Glittery Star Stuff */
|
||||
|
||||
|
||||
:root {
|
||||
:root {
|
||||
--purple: rgb(123, 31, 162);
|
||||
--violet: rgb(103, 58, 183);
|
||||
--pink: rgb(244, 143, 177);
|
||||
@@ -173,7 +165,7 @@
|
||||
from {
|
||||
background-position: 0% center;
|
||||
}
|
||||
|
||||
|
||||
to {
|
||||
background-position: -200% center;
|
||||
}
|
||||
@@ -183,7 +175,7 @@
|
||||
from, to {
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
|
||||
50% {
|
||||
transform: scale(1);
|
||||
}
|
||||
@@ -193,7 +185,7 @@
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
|
||||
to {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
@@ -238,7 +230,7 @@
|
||||
var(--purple)
|
||||
);
|
||||
background-size: 200%;
|
||||
|
||||
|
||||
/* Keep these for Safari and chrome */
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
@@ -250,4 +242,4 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -2,17 +2,16 @@
|
||||
import { push } from "svelte-spa-router";
|
||||
import { location } from 'svelte-spa-router';
|
||||
|
||||
|
||||
import active from 'svelte-spa-router/active'
|
||||
// or i could leave them all and not display if they're active?
|
||||
|
||||
|
||||
let handleClickLogout = async () => {
|
||||
await fetch('http://transcendance:8080/api/v2/auth/logout', {
|
||||
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/auth/logout`, {
|
||||
method: 'POST',
|
||||
})
|
||||
// .then(resp => resp.json)
|
||||
// .then((resp) => console.log(resp))
|
||||
.then( () => push('/') ) // i think for TS reasons it has to be a func not direclty push('/') or whatever
|
||||
.then( () => push('/') )
|
||||
console.log('clicked logout header')
|
||||
};
|
||||
|
||||
@@ -24,18 +23,15 @@
|
||||
<img src="/img/potato_logo.png" alt="Potato Pong Logo" on:click={() => (push('/'))}>
|
||||
<h1>Potato Pong</h1>
|
||||
<nav>
|
||||
<button on:click={() => (push('/game'))}>Play</button>
|
||||
<button on:click={() => (push('/spectator'))}>Spectate</button>
|
||||
<button on:click={() => (push('/ranking'))}>Ranking</button>
|
||||
{#if $location !== '/profile'}
|
||||
<button on:click={() => (push('/profile'))}>My Profile</button>
|
||||
{:else if $location === '/profile'}
|
||||
<button on:click={() => (push('/profile/settings'))}>Settings</button>
|
||||
{/if}
|
||||
|
||||
<!-- <button on:click={() => (push('/stream'))}>Stream</button> -->
|
||||
<!-- <button on:click={() => (push('/chat'))}>Chat</button> -->
|
||||
|
||||
<!-- tmp -->
|
||||
<button on:click={() => (push('/profile/friends'))}>Friends</button>
|
||||
<button on:click={() => (push('/test'))}>test</button>
|
||||
<button on:click={handleClickLogout}>Log Out</button>
|
||||
</nav>
|
||||
</header>
|
||||
@@ -52,11 +48,11 @@
|
||||
src:url('/fonts/Bondi.ttf.woff') format('woff'),
|
||||
url('/fonts/Bondi.ttf.svg#Bondi') format('svg'),
|
||||
url('/fonts/Bondi.ttf.eot'),
|
||||
url('/fonts/Bondi.ttf.eot?#iefix') format('embedded-opentype');
|
||||
url('/fonts/Bondi.ttf.eot?#iefix') format('embedded-opentype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* There is a bunch of unncessary shit in here... why so many flex grids, why is everything the same class? just seemed easier but... */
|
||||
|
||||
@@ -116,4 +112,4 @@
|
||||
/* .none{
|
||||
|
||||
} */
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import { MatchOptions } from "../pages/game/client/pongSpectator";
|
||||
|
||||
export let match: {
|
||||
gameServerIdOfTheMatch: string,
|
||||
gameOptions: MatchOptions,
|
||||
playerOneUsername: string,
|
||||
playerTwoUsername: string
|
||||
};
|
||||
|
||||
let matchOptionsString = "";
|
||||
|
||||
onMount( async() => {
|
||||
if (match.gameOptions === MatchOptions.noOption) {
|
||||
matchOptionsString = "standard";
|
||||
}
|
||||
else {
|
||||
if (match.gameOptions & MatchOptions.multiBalls) {
|
||||
matchOptionsString += "multi balls";
|
||||
}
|
||||
if (match.gameOptions & MatchOptions.movingWalls) {
|
||||
if (matchOptionsString) { matchOptionsString += ", "; }
|
||||
matchOptionsString += "moving walls";
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
<!-- -->
|
||||
|
||||
<li>
|
||||
<button on:click>
|
||||
"{match.playerOneUsername}" VS "{match.playerTwoUsername}"
|
||||
<br/> [{matchOptionsString}]
|
||||
</button>
|
||||
</li>
|
||||
|
||||
|
||||
<!-- -->
|
||||
<style>
|
||||
</style>
|
||||
@@ -0,0 +1,58 @@
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
import Layouts from './Chat_layouts.svelte';
|
||||
export let color = "transparent";
|
||||
|
||||
/* web sockets with socket.io
|
||||
*/
|
||||
import { onMount } from 'svelte';
|
||||
import io from 'socket.io-client';
|
||||
const socket = io(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}`, {
|
||||
path: '/chat'
|
||||
});
|
||||
onMount(async => {
|
||||
socket.on('connect', function(){
|
||||
console.log("socket.io connected");
|
||||
});
|
||||
socket.on('disconnect', function(){
|
||||
console.log("socket.io disconnected");
|
||||
});
|
||||
socket.on('connect_error', function(){
|
||||
console.log("socket.io connect_error");
|
||||
});
|
||||
socket.on('connect_timeout', function(){
|
||||
console.log("socket.io connect_timeout");
|
||||
});
|
||||
socket.on('error', function(){
|
||||
console.log("socket.io error");
|
||||
});
|
||||
socket.on('reconnect', function(){
|
||||
console.log("socket.io reconnect");
|
||||
});
|
||||
socket.on('reconnect_attempt', function(){
|
||||
console.log("socket.io reconnect_attempt");
|
||||
});
|
||||
socket.on('reconnecting', function(){
|
||||
console.log("socket.io reconnecting");
|
||||
});
|
||||
socket.on('reconnect_error', function(){
|
||||
console.log("socket.io reconnect_error");
|
||||
});
|
||||
socket.on('reconnect_failed', function(){
|
||||
console.log("socket.io reconnect_failed");
|
||||
});
|
||||
socket.on('ping', function(){
|
||||
console.log("socket.io ping");
|
||||
});
|
||||
socket.on('pong', function(){
|
||||
console.log("socket.io pong");
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<Layouts color={color} />
|
||||
|
||||
<style></style>
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
export let color;
|
||||
export let layout;
|
||||
|
||||
</script>
|
||||
|
||||
<div class="{layout} chat_box" style="background-color: {color};">
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
/* chat_box and default style
|
||||
*/
|
||||
.chat_box {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
padding: 0px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
border: 1px solid black;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * *
|
||||
GLOBAL STYLES
|
||||
*/
|
||||
|
||||
|
||||
/* Hide scrollbar
|
||||
*/
|
||||
.chat_box :global(*) {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
.chat_box :global(*::-webkit-scrollbar) {
|
||||
display: none; /* Chrome, Safari and Opera */
|
||||
}
|
||||
|
||||
|
||||
/* for grid_box and all childrens
|
||||
*/
|
||||
.chat_box :global(.grid_box) {
|
||||
display: grid;
|
||||
margin: 5px;
|
||||
gap: 5px;
|
||||
width: 300px;
|
||||
height: 400px;
|
||||
}
|
||||
.chat_box :global(.grid_box *) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
||||
/* all p
|
||||
*/
|
||||
.chat_box :global(.grid_box p) {
|
||||
padding: 10px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
|
||||
/* all panel
|
||||
*/
|
||||
.chat_box :global(.panel) {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.chat_box :global(.panel > *) {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * *
|
||||
GLOBAL UTILITIES
|
||||
*/
|
||||
|
||||
|
||||
/* __show_if_only_child
|
||||
*/
|
||||
.chat_box :global(.__show_if_only_child) {
|
||||
display: none;
|
||||
}
|
||||
.chat_box :global(.__show_if_only_child:only-child) {
|
||||
display: flex;
|
||||
color: rgb(100, 100, 100);
|
||||
}
|
||||
|
||||
|
||||
/* __center
|
||||
*/
|
||||
.chat_box :global(.__center) {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
|
||||
/* __border_top
|
||||
*/
|
||||
.chat_box :global(.__border_top) {
|
||||
border-top: 1px solid black;
|
||||
}
|
||||
|
||||
|
||||
/* __check_change_next
|
||||
*/
|
||||
.chat_box :global(.__check_change_next:checked ~ .__to_show) {
|
||||
display: flex;
|
||||
}
|
||||
.chat_box :global(.__check_change_next:checked ~ .__to_block),
|
||||
.chat_box :global(.__check_change_next:checked ~ .__to_block *) {
|
||||
pointer-events: none;
|
||||
color: rgb(100, 100, 100);
|
||||
}
|
||||
.chat_box :global(.__to_show) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
|
||||
<!--
|
||||
<Button
|
||||
bind:layout
|
||||
new_layout=""
|
||||
on_click={}
|
||||
my_class=""
|
||||
my_title=""
|
||||
>
|
||||
value
|
||||
</Button>
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
export let my_class = "";
|
||||
export let my_title = "";
|
||||
export let layout = "";
|
||||
export let new_layout = "";
|
||||
export let on_click = "";
|
||||
|
||||
function update_layout() {
|
||||
layout = new_layout;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<button on:click={update_layout} on:click={on_click} title={my_title} class={my_class}>
|
||||
<p><slot></slot></p>
|
||||
</button>
|
||||
|
||||
<style>
|
||||
|
||||
/*
|
||||
- default config
|
||||
- for btn list
|
||||
- for transparent btn
|
||||
- for deactivated btn
|
||||
- for icon
|
||||
- for 3 dots btn
|
||||
- for close btn
|
||||
- for back btn
|
||||
*/
|
||||
|
||||
|
||||
/* default config
|
||||
*/
|
||||
button {
|
||||
padding: 0px;
|
||||
margin: auto;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
border: none;
|
||||
background-color: rgb(220, 220, 220);
|
||||
}
|
||||
button p {
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
}
|
||||
button:hover {
|
||||
background-color: rgb(200, 200, 200);
|
||||
}
|
||||
button:active {
|
||||
background-color: rgb(190, 190, 190);
|
||||
}
|
||||
|
||||
|
||||
/* for btn list
|
||||
*/
|
||||
.list:not(:hover) {
|
||||
background-color: rgb(240, 240, 240);
|
||||
}
|
||||
.list p {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
||||
/* for transparent btn
|
||||
*/
|
||||
.transparent:not(:hover) {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
|
||||
/* for deactivated btn
|
||||
*/
|
||||
.deactivate {
|
||||
background-color: transparent;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
/* for icon
|
||||
*/
|
||||
.icon p {
|
||||
display: none;
|
||||
}
|
||||
.icon:not(:hover) {
|
||||
background-color: transparent;
|
||||
}
|
||||
.icon {
|
||||
width: 30px;
|
||||
height: 100%;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
|
||||
/* for 3 dots btn
|
||||
*/
|
||||
.dots::after {
|
||||
content: '\2807';
|
||||
font-size: 20px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
text-align: center;
|
||||
transform: translateY(-50%);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
/* for close btn
|
||||
*/
|
||||
.close::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: calc(50% - 1px);
|
||||
left: 5px;
|
||||
width: 20px;
|
||||
height: 2px;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
|
||||
/* for back btn
|
||||
*/
|
||||
.back::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: calc(50% - 6px - 1px);
|
||||
left: 6px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-left: 1px solid black;
|
||||
border-bottom: 1px solid black;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
|
||||
/* for blocked user
|
||||
https://www.fileformat.info/info/unicode/category/So/list.htm
|
||||
U+1F512 LOCK 🔒
|
||||
U+1F513 OPEN LOCK 🔓
|
||||
*/
|
||||
.blocked {
|
||||
padding-left: 30px;
|
||||
}
|
||||
.blocked::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: calc(50% - 2px);
|
||||
left: 10px;
|
||||
cursor: pointer;
|
||||
width: 13px;
|
||||
height: 10px;
|
||||
border-radius: 2px;
|
||||
background-color: rgb(110, 110, 110);
|
||||
}
|
||||
.blocked::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: calc(50% - 9px);
|
||||
left: 12px;
|
||||
cursor: pointer;
|
||||
width: 9px;
|
||||
height: 13px;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
border: 3px solid rgb(110, 110, 110);
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
import ChatBox from './Chat_box_css.svelte';
|
||||
|
||||
import CloseLayout from './Layout_close.svelte';
|
||||
import HomeLayout from './Layout_home.svelte';
|
||||
import RoomLayout from './Layout_room.svelte';
|
||||
import NewLayout from './Layout_new.svelte';
|
||||
import SettingsLayout from './Layout_settings.svelte';
|
||||
import RoomsetLayout from './Layout_room_set.svelte';
|
||||
import ProtectedLayout from './Layout_protected.svelte';
|
||||
import CreateLayout from './Layout_create.svelte';
|
||||
import MuteLayout from './Layout_mute.svelte';
|
||||
import UserLayout from './Layout_user.svelte';
|
||||
|
||||
import Button from './Chat_button.svelte';
|
||||
|
||||
/* global variables
|
||||
*/
|
||||
export let color;
|
||||
let room = "";
|
||||
let admin = false;
|
||||
let layout = "close";
|
||||
let layouts = ["home", "home"];
|
||||
|
||||
/* hold previous version of layout, to go back
|
||||
*/
|
||||
function set_layouts(layout)
|
||||
{
|
||||
if (layout === "close")
|
||||
return;
|
||||
if (layout === layouts[0])
|
||||
return;
|
||||
if (layout === layouts[1])
|
||||
layouts = [layout, "home"];
|
||||
else
|
||||
layouts = [layout, layouts[0]];
|
||||
}
|
||||
$: set_layouts(layout);
|
||||
|
||||
</script>
|
||||
|
||||
<ChatBox layout={layout} color={color}>
|
||||
|
||||
{#if layout === "home"}
|
||||
<HomeLayout bind:layout />
|
||||
|
||||
{:else if layout === "close"}
|
||||
<CloseLayout bind:layout />
|
||||
|
||||
{:else if layout === "room"}
|
||||
<RoomLayout bind:layout back={layouts[1]} />
|
||||
|
||||
{:else if layout === "new"}
|
||||
<NewLayout bind:layout back={layouts[1]} />
|
||||
|
||||
{:else if layout === "settings"}
|
||||
<SettingsLayout bind:layout back={layouts[1]} />
|
||||
|
||||
{:else if layout === "room_set"}
|
||||
<RoomsetLayout bind:layout back={layouts[1]} />
|
||||
|
||||
{:else if layout === "protected"}
|
||||
<ProtectedLayout bind:layout back={layouts[1]} />
|
||||
|
||||
{:else if layout === "create"}
|
||||
<CreateLayout bind:layout back={layouts[1]} />
|
||||
|
||||
{:else if layout === "mute"}
|
||||
<MuteLayout bind:layout back={layouts[1]} />
|
||||
|
||||
{:else if layout === "user"}
|
||||
<UserLayout bind:layout back={layouts[1]} />
|
||||
|
||||
{/if}
|
||||
|
||||
</ChatBox>
|
||||
|
||||
<style></style>
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<script>
|
||||
|
||||
export let name;
|
||||
|
||||
</script>
|
||||
|
||||
<div class="chat_msg {name}">
|
||||
<p class="name">{name}</p>
|
||||
<p class="msg"><slot></slot></p>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.chat_msg {
|
||||
/*
|
||||
white-space: pre-wrap;
|
||||
*/
|
||||
margin: 5px auto;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
/* all msg
|
||||
*/
|
||||
.chat_msg {
|
||||
margin-left: 0px;
|
||||
background-color: rgb(210, 210, 210);
|
||||
max-width: 80%;
|
||||
}
|
||||
.chat_msg p {
|
||||
padding: 0px;
|
||||
}
|
||||
.chat_msg p.name {
|
||||
margin: 0px;
|
||||
font-size: 12px;
|
||||
color: rgb(100, 100, 100);
|
||||
}
|
||||
.chat_msg p.msg {
|
||||
margin: 5px 0px;
|
||||
}
|
||||
.chat_msg p.msg :global(*) {
|
||||
display: inline;
|
||||
}
|
||||
/* msg perso
|
||||
*/
|
||||
.chat_msg.me {
|
||||
margin-right: 0px;
|
||||
margin-left: auto;
|
||||
background-color: rgb(210, 110, 10);
|
||||
}
|
||||
.chat_msg.me p.name {
|
||||
display: none;
|
||||
}
|
||||
/* msg server
|
||||
*/
|
||||
.chat_msg.SERVER {
|
||||
margin-left: auto;
|
||||
background-color: transparent;
|
||||
}
|
||||
.chat_msg.SERVER p.name {
|
||||
display: none;
|
||||
}
|
||||
.chat_msg.SERVER p.msg {
|
||||
margin: 0px auto;
|
||||
font-size: 12px;
|
||||
color: rgb(100, 100, 100);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
|
||||
<script>
|
||||
|
||||
import Button from './Chat_button.svelte';
|
||||
export let layout;
|
||||
|
||||
</script>
|
||||
|
||||
<div class="grid_box">
|
||||
<Button bind:layout new_layout="home" my_class="chat">
|
||||
chat
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
/* layout "close"
|
||||
*/
|
||||
.grid_box :global(.chat) {grid-area: chat;}
|
||||
.grid_box {
|
||||
gap: 0px;
|
||||
grid:
|
||||
' chat ' auto
|
||||
/ auto ;
|
||||
}
|
||||
:global(.chat_box.close) .grid_box {
|
||||
margin: 0px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * *
|
||||
GLOBAL STYLES
|
||||
*/
|
||||
|
||||
|
||||
/* Hide scrollbar
|
||||
*/
|
||||
.chat_box :global(*) {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
.chat_box :global(*::-webkit-scrollbar) {
|
||||
display: none; /* Chrome, Safari and Opera */
|
||||
}
|
||||
|
||||
|
||||
/* for grid_box and all childrens
|
||||
*/
|
||||
.chat_box :global(.grid_box) {
|
||||
display: grid;
|
||||
margin: 0px;
|
||||
gap: 5px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.chat_box :global(.grid_box *) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
||||
/* all p
|
||||
*/
|
||||
.chat_box :global(.grid_box p) {
|
||||
padding: 10px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
|
||||
/* all panel
|
||||
*/
|
||||
.chat_box :global(.panel) {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.chat_box :global(.panel > *) {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * *
|
||||
GLOBAL UTILITIES
|
||||
*/
|
||||
|
||||
|
||||
/* __show_if_only_child
|
||||
*/
|
||||
.chat_box :global(.__show_if_only_child) {
|
||||
display: none;
|
||||
}
|
||||
.chat_box :global(.__show_if_only_child:only-child) {
|
||||
display: flex;
|
||||
color: rgb(100, 100, 100);
|
||||
}
|
||||
|
||||
|
||||
/* __center
|
||||
*/
|
||||
.chat_box :global(.__center) {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
|
||||
/* __border_top
|
||||
*/
|
||||
.chat_box :global(.__border_top) {
|
||||
border-top: 1px solid black;
|
||||
}
|
||||
|
||||
|
||||
/* __check_change_next
|
||||
*/
|
||||
.chat_box :global(.__check_change_next:checked ~ .__to_show) {
|
||||
display: flex;
|
||||
}
|
||||
.chat_box :global(.__check_change_next:checked ~ .__to_block),
|
||||
.chat_box :global(.__check_change_next:checked ~ .__to_block *) {
|
||||
pointer-events: none;
|
||||
color: rgb(100, 100, 100);
|
||||
}
|
||||
.chat_box :global(.__to_show) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
|
||||
<script>
|
||||
|
||||
import Button from './Chat_button.svelte';
|
||||
export let layout = "";
|
||||
export let back = "";
|
||||
|
||||
</script>
|
||||
|
||||
<div class="grid_box">
|
||||
|
||||
<!-- back -->
|
||||
<Button bind:layout new_layout={back} my_class="back icon" my_title="go back {back}">
|
||||
back
|
||||
</Button>
|
||||
|
||||
<!-- create -->
|
||||
<Button my_class="create deactivate">
|
||||
create
|
||||
</Button>
|
||||
|
||||
<!-- close -->
|
||||
<Button bind:layout new_layout="close" my_class="close icon">
|
||||
close
|
||||
</Button>
|
||||
|
||||
<!-- panel_create -->
|
||||
<div class="panel panel_create">
|
||||
<form>
|
||||
<!-- name: -->
|
||||
<label for="chat_name"><p>new room name :</p></label>
|
||||
<input id="chat_name" required>
|
||||
<!-- [ ] pubic -->
|
||||
<input id="chat_public" type="radio" name="chat_create_type" checked>
|
||||
<label for="chat_public" class="_radio"><p>public</p></label>
|
||||
<!-- [ ] private -->
|
||||
<input id="chat_private" type="radio" name="chat_create_type">
|
||||
<label for="chat_private" class="_radio"><p>private</p></label>
|
||||
<!-- [ ] protected -->
|
||||
<input id="chat_protected" class="__check_change_next" type="radio" name="chat_create_type">
|
||||
<label for="chat_protected" class="_radio"><p>protected</p></label>
|
||||
<!-- [x] protected -->
|
||||
<div class="__to_show">
|
||||
<label for="chat_pswd"><p>choose a password :</p></label>
|
||||
<input id="chat_pswd" type="password" placeholder="minimum 8 characters" minlength="8">
|
||||
<p>confirm password :</p>
|
||||
<input type="password">
|
||||
</div>
|
||||
<input type="submit" value="⮡">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
/* grid layout "create"
|
||||
*/
|
||||
.grid_box :global(.back ) {grid-area: back;}
|
||||
.grid_box :global(.create ) {grid-area: create;}
|
||||
.grid_box :global(.close ) {grid-area: close;}
|
||||
.grid_box :global(.panel_create) {grid-area: panel_create;}
|
||||
.grid_box {
|
||||
grid:
|
||||
' back create close ' auto
|
||||
' panel_create panel_create panel_create ' 1fr
|
||||
/ auto 1fr auto ;
|
||||
}
|
||||
|
||||
|
||||
/* radio elements style check
|
||||
*/
|
||||
form input[type=radio] {
|
||||
display: none;
|
||||
}
|
||||
form label._radio {
|
||||
margin: 0px 20px 0px auto;
|
||||
padding-right: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
form label._radio p {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
form label._radio::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: calc(50% - 6px);
|
||||
right: 0px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 6px;
|
||||
border: 2px solid rgb(150, 150, 150);
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
}
|
||||
form input[type=radio]:checked
|
||||
+ label._radio::after {
|
||||
background-color: rgb(200, 200, 200);
|
||||
}
|
||||
|
||||
|
||||
/* submit
|
||||
*/
|
||||
form input[type=submit] {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
|
||||
<script>
|
||||
|
||||
import Button from './Chat_button.svelte';
|
||||
export let layout;
|
||||
|
||||
</script>
|
||||
|
||||
<div class="grid_box">
|
||||
|
||||
<!-- settings -->
|
||||
<Button bind:layout new_layout="settings" my_class="settings dots icon">
|
||||
settings
|
||||
</Button>
|
||||
|
||||
<!-- new -->
|
||||
<Button bind:layout new_layout="new" my_class="new transparent">
|
||||
new
|
||||
</Button>
|
||||
|
||||
<!-- close -->
|
||||
<Button bind:layout new_layout="close" my_class="close icon">
|
||||
close
|
||||
</Button>
|
||||
|
||||
<!-- panel home -->
|
||||
<div class="panel panel_home __border_top">
|
||||
<p class="title">list of your rooms :</p>
|
||||
<div class="room_list">
|
||||
<div class="__show_if_only_child">
|
||||
<p class="__center">/ you have no chat room yet /</p>
|
||||
</div>
|
||||
<!-- placeholders
|
||||
<Button bind:layout new_layout="room" my_class="list">
|
||||
a room
|
||||
</Button>
|
||||
<Button bind:layout new_layout="room" my_class="list">
|
||||
another room
|
||||
</Button>
|
||||
<Button bind:layout new_layout="room" my_class="list">
|
||||
placeholder
|
||||
</Button>
|
||||
------------- -->
|
||||
<!-- END placeholders -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
/* grid layout "home"
|
||||
*/
|
||||
.grid_box :global(.settings ) {grid-area: settings;}
|
||||
.grid_box :global(.close ) {grid-area: close;}
|
||||
.grid_box :global(.new ) {grid-area: new;}
|
||||
.grid_box :global(.panel_home) {grid-area: panel_home;}
|
||||
.grid_box {
|
||||
grid:
|
||||
' settings new close ' auto
|
||||
' panel_home panel_home panel_home ' 1fr
|
||||
/ auto 1fr auto ;
|
||||
}
|
||||
|
||||
|
||||
/* panel home
|
||||
*/
|
||||
.panel_home p.title {
|
||||
margin: 10px auto 0px auto;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
|
||||
<script>
|
||||
|
||||
import Button from './Chat_button.svelte';
|
||||
export let layout = "";
|
||||
export let back = "";
|
||||
|
||||
</script>
|
||||
|
||||
<div class="grid_box">
|
||||
|
||||
<!-- back -->
|
||||
<Button bind:layout new_layout={back} my_class="back icon" my_title="go back {back}">
|
||||
back
|
||||
</Button>
|
||||
|
||||
<!-- user -->
|
||||
<Button my_class="user deactivate">
|
||||
<user>
|
||||
</Button>
|
||||
|
||||
<!-- close -->
|
||||
<Button bind:layout new_layout="close" my_class="close icon">
|
||||
close
|
||||
</Button>
|
||||
|
||||
<!-- panel_mute -->
|
||||
|
||||
<!-- MUTE -->
|
||||
<div class="panel panel_mute __border_top">
|
||||
<p class="__center">mute this user for a time :</p>
|
||||
<form>
|
||||
<!-- forever -->
|
||||
<input id="chat_mute_forever" class="__check_change_next" type="checkbox">
|
||||
<label for="chat_mute_forever" class="_checkbox"><p>forever</p></label>
|
||||
<div class="__to_block">
|
||||
<!-- minutes -->
|
||||
<label for="chat_mute_minutes" class="_select">
|
||||
<p>minutes :</p>
|
||||
<select id="chat_mute_minutes">
|
||||
<option value="01">00</option>
|
||||
<option value="01">01</option>
|
||||
<option value="02">02</option>
|
||||
<option value="03">03</option>
|
||||
<option value="04">04</option>
|
||||
<option value="05">05</option>
|
||||
<option value="06">06</option>
|
||||
<option value="07">07</option>
|
||||
<option value="10">10</option>
|
||||
<option value="11">11</option>
|
||||
<option value="12">12</option>
|
||||
<option value="13">13</option>
|
||||
<option value="14">14</option>
|
||||
<option value="15">15</option>
|
||||
<option value="16">16</option>
|
||||
<option value="17">17</option>
|
||||
<option value="20">20</option>
|
||||
<option value="21">21</option>
|
||||
<option value="22">22</option>
|
||||
<option value="23">23</option>
|
||||
<option value="24">24</option>
|
||||
<option value="25">25</option>
|
||||
<option value="26">26</option>
|
||||
<option value="27">27</option>
|
||||
<option value="30">30</option>
|
||||
<option value="31">31</option>
|
||||
<option value="32">32</option>
|
||||
<option value="33">33</option>
|
||||
<option value="34">34</option>
|
||||
<option value="35">35</option>
|
||||
<option value="36">36</option>
|
||||
<option value="37">37</option>
|
||||
<option value="40">40</option>
|
||||
<option value="41">41</option>
|
||||
<option value="42">42</option>
|
||||
<option value="43">43</option>
|
||||
<option value="44">44</option>
|
||||
<option value="45">45</option>
|
||||
<option value="46">46</option>
|
||||
<option value="47">47</option>
|
||||
<option value="50">50</option>
|
||||
<option value="51">51</option>
|
||||
<option value="52">52</option>
|
||||
<option value="53">53</option>
|
||||
<option value="54">54</option>
|
||||
<option value="55">55</option>
|
||||
<option value="56">56</option>
|
||||
<option value="57">57</option>
|
||||
<option value="60">60</option>
|
||||
</select>
|
||||
</label>
|
||||
<!-- hours -->
|
||||
<label for="chat_mute_hours" class="_select">
|
||||
<p>hours :</p>
|
||||
<select id="chat_mute_hours">
|
||||
<option value="01">00</option>
|
||||
<option value="01">01</option>
|
||||
<option value="02">02</option>
|
||||
<option value="03">03</option>
|
||||
<option value="04">04</option>
|
||||
<option value="05">05</option>
|
||||
<option value="06">06</option>
|
||||
<option value="07">07</option>
|
||||
<option value="10">10</option>
|
||||
<option value="11">11</option>
|
||||
<option value="12">12</option>
|
||||
<option value="13">13</option>
|
||||
<option value="14">14</option>
|
||||
<option value="15">15</option>
|
||||
<option value="16">16</option>
|
||||
<option value="17">17</option>
|
||||
<option value="20">20</option>
|
||||
<option value="21">21</option>
|
||||
<option value="22">22</option>
|
||||
<option value="23">23</option>
|
||||
<option value="24">24</option>
|
||||
<option value="25">25</option>
|
||||
<option value="26">26</option>
|
||||
<option value="27">27</option>
|
||||
<option value="30">30</option>
|
||||
<option value="31">31</option>
|
||||
<option value="32">32</option>
|
||||
<option value="33">33</option>
|
||||
<option value="34">34</option>
|
||||
<option value="35">35</option>
|
||||
<option value="36">36</option>
|
||||
<option value="37">37</option>
|
||||
<option value="40">40</option>
|
||||
<option value="41">41</option>
|
||||
<option value="42">42</option>
|
||||
<option value="43">43</option>
|
||||
<option value="44">44</option>
|
||||
<option value="45">45</option>
|
||||
<option value="46">46</option>
|
||||
<option value="47">47</option>
|
||||
<option value="50">50</option>
|
||||
<option value="51">51</option>
|
||||
<option value="52">52</option>
|
||||
<option value="53">53</option>
|
||||
<option value="54">54</option>
|
||||
<option value="55">55</option>
|
||||
<option value="56">56</option>
|
||||
<option value="57">57</option>
|
||||
<option value="60">60</option>
|
||||
</select>
|
||||
</label>
|
||||
<!-- days -->
|
||||
<label for="chat_mute_days" class="_select">
|
||||
<p>days :</p>
|
||||
<select id="chat_mute_days">
|
||||
<option value="00">00</option>
|
||||
<option value="01">01</option>
|
||||
<option value="02">02</option>
|
||||
<option value="03">03</option>
|
||||
<option value="04">04</option>
|
||||
<option value="05">05</option>
|
||||
<option value="06">06</option>
|
||||
<option value="07">07</option>
|
||||
<option value="10">10</option>
|
||||
<option value="11">11</option>
|
||||
<option value="12">12</option>
|
||||
<option value="13">13</option>
|
||||
<option value="14">14</option>
|
||||
<option value="15">15</option>
|
||||
<option value="16">16</option>
|
||||
<option value="17">17</option>
|
||||
<option value="20">20</option>
|
||||
<option value="21">21</option>
|
||||
<option value="22">22</option>
|
||||
<option value="23">23</option>
|
||||
<option value="24">24</option>
|
||||
<option value="25">25</option>
|
||||
<option value="26">26</option>
|
||||
<option value="27">27</option>
|
||||
<option value="30">30</option>
|
||||
<option value="31">31</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<input type="submit" value="⮡">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
/* grid layout "mute"
|
||||
*/
|
||||
.grid_box :global(.back ) {grid-area: back;}
|
||||
|
||||
.grid_box :global(.back ) {grid-area: back;}
|
||||
.grid_box :global(.user ) {grid-area: user;}
|
||||
.grid_box :global(.close ) {grid-area: close;}
|
||||
.grid_box :global(.panel_mute) {grid-area: panel_mute;}
|
||||
.grid_box {
|
||||
grid:
|
||||
' back user close ' auto
|
||||
' panel_mute panel_mute panel_mute ' 1fr
|
||||
/ auto 1fr auto ;
|
||||
}
|
||||
|
||||
|
||||
/* checkbox
|
||||
*/
|
||||
form input[type=checkbox] {
|
||||
display: none;
|
||||
}
|
||||
form label._checkbox {
|
||||
margin: 0px auto 0px 10px;
|
||||
padding-left: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
form label._checkbox::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: calc(50% - 6px);
|
||||
left: 0px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border: 2px solid rgb(150, 150, 150);
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
}
|
||||
form input[type=checkbox]:checked
|
||||
+ label._checkbox::after {
|
||||
background-color: rgb(200, 200, 200);
|
||||
}
|
||||
|
||||
|
||||
/* select
|
||||
*/
|
||||
form label._select {
|
||||
flex-direction: row;
|
||||
}
|
||||
form label._select p {
|
||||
margin: 0px;
|
||||
}
|
||||
form select {
|
||||
margin: auto auto auto 10px;
|
||||
background-color: rgb(220, 220, 220);
|
||||
border: none;
|
||||
padding: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
form select:hover {
|
||||
background-color: rgb(200, 200, 200);
|
||||
}
|
||||
|
||||
|
||||
/* submit
|
||||
*/
|
||||
form input[type=submit] {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
|
||||
<script>
|
||||
|
||||
import Button from './Chat_button.svelte';
|
||||
export let layout = "";
|
||||
export let back = "";
|
||||
|
||||
</script>
|
||||
|
||||
<div class="grid_box">
|
||||
|
||||
<!-- back -->
|
||||
<Button bind:layout new_layout={back} my_class="back icon" my_title="go back {back}">
|
||||
back
|
||||
</Button>
|
||||
|
||||
<!-- new -->
|
||||
<Button my_class="new deactivate">
|
||||
new
|
||||
</Button>
|
||||
|
||||
<!-- close -->
|
||||
<Button bind:layout new_layout="close" my_class="close icon">
|
||||
close
|
||||
</Button>
|
||||
|
||||
<!-- panel_new -->
|
||||
<div class="panel panel_new __border_top">
|
||||
<Button bind:layout new_layout="create" my_class="create">
|
||||
create
|
||||
</Button>
|
||||
<p>join room :</p>
|
||||
<div class="public_rooms">
|
||||
<div class="__show_if_only_child">
|
||||
<p class="__center">/ there are no public rooms yet /</p>
|
||||
</div>
|
||||
<!-- placeholders
|
||||
<Button bind:layout new_layout="room" my_class="list">
|
||||
placeholder
|
||||
</Button>
|
||||
<Button bind:layout new_layout="room" my_class="list">
|
||||
join room
|
||||
</Button>
|
||||
<Button bind:layout new_layout="room" my_class="list">
|
||||
one room
|
||||
</Button>
|
||||
<Button bind:layout new_layout="room" my_class="list">
|
||||
another room
|
||||
</Button>
|
||||
<Button bind:layout new_layout="room" my_class="list">
|
||||
one room
|
||||
</Button>
|
||||
<Button bind:layout new_layout="room" my_class="list">
|
||||
another room
|
||||
</Button>
|
||||
<Button bind:layout new_layout="room" my_class="list">
|
||||
one room
|
||||
</Button>
|
||||
<Button bind:layout new_layout="room" my_class="list">
|
||||
another room
|
||||
</Button>
|
||||
<Button bind:layout new_layout="room" my_class="list">
|
||||
one more room
|
||||
</Button>
|
||||
------------- -->
|
||||
<!-- END placeholders -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
/* grid layout "new"
|
||||
*/
|
||||
.grid_box :global(.back ) {grid-area: back;}
|
||||
.grid_box :global(.new ) {grid-area: new;}
|
||||
.grid_box :global(.close ) {grid-area: close;}
|
||||
.grid_box :global(.panel_new) {grid-area: panel_new;}
|
||||
.grid_box {
|
||||
grid:
|
||||
' back new close ' auto
|
||||
' panel_new panel_new panel_new ' 1fr
|
||||
/ auto 1fr auto ;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
|
||||
<script>
|
||||
|
||||
import Button from './Chat_button.svelte';
|
||||
export let layout = "";
|
||||
export let back = "";
|
||||
|
||||
</script>
|
||||
|
||||
<div class="grid_box">
|
||||
|
||||
<!-- back -->
|
||||
<Button bind:layout new_layout={back} my_class="back icon" my_title="go back {back}">
|
||||
back
|
||||
</Button>
|
||||
|
||||
<!-- room_name -->
|
||||
<Button my_class="room_name deactivate">
|
||||
<room_name>
|
||||
</Button>
|
||||
|
||||
<!-- close -->
|
||||
<Button bind:layout new_layout="close" my_class="close icon">
|
||||
close
|
||||
</Button>
|
||||
|
||||
<!-- panel_protected -->
|
||||
<div class="panel panel_protected __border_top">
|
||||
<p class="title __center">this room is protected</p>
|
||||
<form>
|
||||
<label for="chat_pswd"><p>password :</p></label>
|
||||
<input id="chat_pswd" type="password" required>
|
||||
<input type="submit" value="⮡">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
/* grid layout "protected"
|
||||
*/
|
||||
.grid_box :global(.back ) {grid-area: back;}
|
||||
.grid_box :global(.room_name ) {grid-area: room_name;}
|
||||
.grid_box :global(.close ) {grid-area: close;}
|
||||
.grid_box :global(.panel_protected) {grid-area: panel_protected;}
|
||||
.grid_box {
|
||||
grid:
|
||||
' back room_name close ' auto
|
||||
' panel_protected panel_protected panel_protected ' 1fr
|
||||
/ auto 1fr auto ;
|
||||
}
|
||||
|
||||
|
||||
/* submit
|
||||
*/
|
||||
form input[type=submit] {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
|
||||
<script>
|
||||
|
||||
import Button from './Chat_button.svelte';
|
||||
import Msg from './Chat_msg.svelte';
|
||||
import io from 'socket.io-client';
|
||||
|
||||
export let layout = "";
|
||||
export let back = "";
|
||||
|
||||
let msg = "";
|
||||
let text_area;
|
||||
let msgs = [];
|
||||
|
||||
function add_msg(from, the_msg)
|
||||
{
|
||||
msgs = [...msgs, { content: the_msg, name: from }];
|
||||
}
|
||||
|
||||
function send_msg()
|
||||
{
|
||||
msg = msg.trim();
|
||||
|
||||
if (msg.length > 0) {
|
||||
//socket.emit('sendmsg', msg);
|
||||
add_msg("me", msg);
|
||||
}
|
||||
|
||||
msg = "";
|
||||
text_area.focus();
|
||||
}
|
||||
|
||||
function send_msg_if(evt)
|
||||
{
|
||||
if (evt.shiftKey && evt.key === "Enter")
|
||||
{
|
||||
evt.preventDefault();
|
||||
send_msg();
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="grid_box">
|
||||
|
||||
<!-- back -->
|
||||
<Button bind:layout new_layout={back} my_class="back icon" my_title="go back {back}">
|
||||
back
|
||||
</Button>
|
||||
|
||||
<!-- room_name -->
|
||||
<Button bind:layout new_layout="room_set" my_class="room_name transparent">
|
||||
<room_name>
|
||||
</Button>
|
||||
|
||||
<!-- close -->
|
||||
<Button bind:layout new_layout="close" my_class="close icon">
|
||||
close
|
||||
</Button>
|
||||
|
||||
<!-- msg -->
|
||||
<div class="panel panel_msg">
|
||||
<div class="msg_thread">
|
||||
{#each msgs as msg}
|
||||
<Msg name={msg.name}>{@html msg.content}</Msg>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- write -->
|
||||
<div class="panel_write">
|
||||
<div
|
||||
class="text_area"
|
||||
bind:innerHTML={msg}
|
||||
bind:this={text_area}
|
||||
on:keypress={send_msg_if}
|
||||
contenteditable="true"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<!-- send -->
|
||||
<Button my_class="send" on_click={send_msg}>
|
||||
send
|
||||
</Button>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
/* grid layout "room"
|
||||
*/
|
||||
.grid_box :global(.back ) {grid-area: back;}
|
||||
.grid_box :global(.room_name ) {grid-area: room_name;}
|
||||
.grid_box :global(.close ) {grid-area: close;}
|
||||
.grid_box :global(.panel_msg ) {grid-area: panel_msg;}
|
||||
.grid_box :global(.send ) {grid-area: send;}
|
||||
.grid_box :global(.panel_write) {grid-area: panel_write;}
|
||||
.grid_box {
|
||||
grid:
|
||||
' back room_name room_name close ' auto
|
||||
' panel_msg panel_msg panel_msg panel_msg ' 1fr
|
||||
' panel_write panel_write send send ' auto
|
||||
/ auto 1fr auto auto ;
|
||||
}
|
||||
|
||||
|
||||
/* write area
|
||||
*/
|
||||
.grid_box .panel_write {
|
||||
border: none;
|
||||
overflow: visible;
|
||||
}
|
||||
.grid_box .text_area {
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
background-color: white;
|
||||
border: 1px solid black;
|
||||
}
|
||||
.grid_box .text_area:focus {
|
||||
height: auto;
|
||||
min-height: 100%;
|
||||
max-height: 300px;
|
||||
}
|
||||
.grid_box .panel_write .text_area :global(*) {
|
||||
display: block ruby;
|
||||
}
|
||||
|
||||
|
||||
/* msg area
|
||||
*/
|
||||
.grid_box .panel_msg {
|
||||
flex-direction: column-reverse;
|
||||
border: 1px solid black;
|
||||
}
|
||||
.grid_box .msg_thread {
|
||||
width: 100%;
|
||||
padding: 0px 5px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
|
||||
<script>
|
||||
|
||||
import Button from './Chat_button.svelte';
|
||||
export let layout = "";
|
||||
export let back = "";
|
||||
|
||||
</script>
|
||||
|
||||
<div class="grid_box">
|
||||
|
||||
<!-- back -->
|
||||
<Button bind:layout new_layout={back} my_class="back icon" my_title="go back {back}">
|
||||
back
|
||||
</Button>
|
||||
|
||||
<!-- room_name -->
|
||||
<Button my_class="room_name deactivate">
|
||||
<room_name>
|
||||
</Button>
|
||||
|
||||
<!-- close -->
|
||||
<Button bind:layout new_layout="close" my_class="close icon">
|
||||
close
|
||||
</Button>
|
||||
|
||||
<!-- panel_room_set -->
|
||||
<div class="panel panel_room_set __border_top">
|
||||
<Button bind:layout new_layout="create" my_class="create">
|
||||
leave
|
||||
</Button>
|
||||
<p>room users :</p>
|
||||
<div class="room_users">
|
||||
<div class="__show_if_only_child">
|
||||
<p class="__center">/ there are no public rooms yet /</p>
|
||||
</div>
|
||||
<!-- placeholders
|
||||
------------- -->
|
||||
<Button bind:layout new_layout="user" my_class="list">
|
||||
user 1
|
||||
</Button>
|
||||
<Button bind:layout new_layout="user" my_class="list blocked">
|
||||
user 2
|
||||
</Button>
|
||||
<Button bind:layout new_layout="user" my_class="list">
|
||||
user 3
|
||||
</Button>
|
||||
<Button bind:layout new_layout="user" my_class="list">
|
||||
user 4
|
||||
</Button>
|
||||
<!-- END placeholders -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
/* grid layout "room_set"
|
||||
*/
|
||||
.grid_box :global(.back ) {grid-area: back;}
|
||||
.grid_box :global(.room_name ) {grid-area: room_name;}
|
||||
.grid_box :global(.close ) {grid-area: close;}
|
||||
.grid_box :global(.panel_room_set) {grid-area: panel_room_set;}
|
||||
.grid_box {
|
||||
grid:
|
||||
' back room_name close ' auto
|
||||
' panel_room_set panel_room_set panel_room_set ' 1fr
|
||||
/ auto 1fr auto ;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
|
||||
<script>
|
||||
|
||||
import Button from './Chat_button.svelte';
|
||||
export let layout = "";
|
||||
export let back = "";
|
||||
|
||||
</script>
|
||||
|
||||
<div class="grid_box">
|
||||
|
||||
<!-- back -->
|
||||
<Button bind:layout new_layout={back} my_class="back icon" my_title="go back {back}">
|
||||
back
|
||||
</Button>
|
||||
|
||||
<!-- settings -->
|
||||
<Button my_class="room_name deactivate">
|
||||
settings
|
||||
</Button>
|
||||
|
||||
<!-- close -->
|
||||
<Button bind:layout new_layout="close" my_class="close icon">
|
||||
close
|
||||
</Button>
|
||||
|
||||
<!-- panel_settings -->
|
||||
<div class="panel panel_settings __border_top">
|
||||
<p>blocked users :</p>
|
||||
<div class="blocked_users">
|
||||
<div class="__show_if_only_child">
|
||||
<p class="__center">/ you have blocked no one /</p>
|
||||
</div>
|
||||
<!-- placeholders
|
||||
<Button bind:layout new_layout="user" my_class="list blocked">
|
||||
user 1
|
||||
</Button>
|
||||
<Button bind:layout new_layout="user" my_class="list blocked">
|
||||
user 2
|
||||
</Button>
|
||||
<Button bind:layout new_layout="user" my_class="list blocked">
|
||||
user 3
|
||||
</Button>
|
||||
<Button bind:layout new_layout="user" my_class="list blocked">
|
||||
user 4
|
||||
</Button>
|
||||
------------- -->
|
||||
<!-- END placeholders -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
/* grid layout "settings"
|
||||
*/
|
||||
.grid_box :global(.back ) {grid-area: back;}
|
||||
.grid_box :global(.settings ) {grid-area: settings;}
|
||||
.grid_box :global(.close ) {grid-area: close;}
|
||||
.grid_box :global(.panel_settings) {grid-area: panel_settings;}
|
||||
.grid_box {
|
||||
grid:
|
||||
' back settings close ' auto
|
||||
' panel_settings panel_settings panel_settings ' 1fr
|
||||
/ auto 1fr auto ;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
|
||||
<script>
|
||||
|
||||
import Button from './Chat_button.svelte';
|
||||
export let layout = "";
|
||||
export let back = "";
|
||||
|
||||
let mute = "mute";
|
||||
let block = "block";
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div class="grid_box">
|
||||
|
||||
<!-- back -->
|
||||
<Button bind:layout new_layout={back} my_class="back icon" my_title="go back {back}">
|
||||
back
|
||||
</Button>
|
||||
|
||||
<!-- user -->
|
||||
<Button my_class="user deactivate">
|
||||
<user>
|
||||
</Button>
|
||||
|
||||
<!-- close -->
|
||||
<Button bind:layout new_layout="close" my_class="close icon">
|
||||
close
|
||||
</Button>
|
||||
|
||||
<!-- room_name -->
|
||||
{#if back === "room_set"}
|
||||
<Button my_class="room_name deactivate __border_top">
|
||||
<room_name>
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
<!-- panel_user -->
|
||||
<div class="panel panel_user __border_top">
|
||||
<p class="__center">user options :</p>
|
||||
<Button>
|
||||
view profile
|
||||
</Button>
|
||||
<Button>
|
||||
game invitation
|
||||
</Button>
|
||||
<Button>
|
||||
{block}
|
||||
</Button>
|
||||
|
||||
{#if back === "room_set"}
|
||||
<Button>
|
||||
make admin
|
||||
</Button>
|
||||
<Button>
|
||||
{mute}
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
/* grid layout "user"
|
||||
*/
|
||||
.grid_box :global(.back ) {grid-area: back;}
|
||||
.grid_box :global(.user ) {grid-area: user;}
|
||||
.grid_box :global(.close ) {grid-area: close;}
|
||||
.grid_box :global(.room_name ) {grid-area: room_name;}
|
||||
.grid_box :global(.panel_user) {grid-area: panel_user;}
|
||||
.grid_box {
|
||||
grid:
|
||||
' back user close ' auto
|
||||
' room_name room_name room_name ' auto
|
||||
' panel_user panel_user panel_user ' 1fr
|
||||
/ auto 1fr auto ;
|
||||
}
|
||||
|
||||
|
||||
/* for line height
|
||||
*/
|
||||
.panel_user {
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@@ -4,152 +4,23 @@ import SplashPage from "../pages/SplashPage.svelte";
|
||||
import TwoFactorAuthentication from '../pages/TwoFactorAuthentication.svelte';
|
||||
import UnauthorizedAccessPage from '../pages/UnauthorizedAccessPage.svelte';
|
||||
import { wrap } from 'svelte-spa-router/wrap'
|
||||
import { get } from 'svelte/store';
|
||||
import TestPage from '../pages/TmpTestPage.svelte';
|
||||
import Game from '../pages/game/Game.svelte';
|
||||
import Ranking from '../pages/game/Ranking.svelte';
|
||||
import GameSpectator from '../pages/game/GameSpectator.svelte';
|
||||
|
||||
|
||||
|
||||
// "/article/:title": Article, // this is how you would do parameters!
|
||||
// "/": LoginPage,
|
||||
|
||||
// TMP not using this cuz need to work out how to authentical both 42 and 2FA from the backend
|
||||
|
||||
// export const primaryRoutes = {
|
||||
// '/': SplashPage,
|
||||
// // '/2fa': TwoFactorAuthentication,
|
||||
// '/2fa': wrap({
|
||||
// component: TwoFactorAuthentication,
|
||||
// conditions: [
|
||||
// (detail) => {
|
||||
// // let loggedIn;
|
||||
// // loginStatus.subscribe(value => {
|
||||
// // loggedIn = value;
|
||||
// // });
|
||||
|
||||
// const { fortyTwo, tfa } = get(loginStatus);
|
||||
|
||||
// console.log('condition in /2fa');
|
||||
// // return (loginStatus.fortyTwo && loginStatus.tfa);
|
||||
// // console.log($loginStatus.fortyTwo)
|
||||
// console.log(fortyTwo);
|
||||
// console.log(tfa);
|
||||
// return true;
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
// '/profile': wrap({
|
||||
// component: ProfilePage,
|
||||
// conditions: [
|
||||
// (detail) => {
|
||||
// const { fortyTwo, tfa } = get(loginStatus);
|
||||
// // console.log(fortyTwo);
|
||||
// // console.log(tfa);
|
||||
// // return true;
|
||||
// return (fortyTwo && tfa);
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
// '/profile/*': wrap({
|
||||
// component: ProfilePage,
|
||||
// conditions: [
|
||||
// (detail) => {
|
||||
// const { fortyTwo, tfa } = get(loginStatus);
|
||||
// // console.log(fortyTwo);
|
||||
// // console.log(tfa);
|
||||
// // return true;
|
||||
// return (fortyTwo && tfa);
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
// '/profile': wrap({
|
||||
// // Use a dynamically-loaded component for this
|
||||
// asyncComponent: () => import('./ProfilePage.svelte'),
|
||||
// // Adding one pre-condition that's an async function
|
||||
// conditions: [
|
||||
// async (detail) => {
|
||||
// // Make a network request, which are async operations
|
||||
// const response = await fetch('http://transcendance:8080/api/v2/user')
|
||||
// const data = await response.json()
|
||||
// // Return true to continue loading the component, or false otherwise
|
||||
// if (data.isAdmin) {
|
||||
// return true
|
||||
// }
|
||||
// else {
|
||||
// return false
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
// '/unauthorized-access': UnauthorizedAccessPage,
|
||||
// '*': NotFound
|
||||
// };
|
||||
|
||||
export const primaryRoutes = {
|
||||
'/': SplashPage,
|
||||
'/2fa': TwoFactorAuthentication,
|
||||
// '/test': wrap({
|
||||
// component: TestPage,
|
||||
// conditions: [
|
||||
// (detail) => {
|
||||
// const user = get(userStore); // seems like get(store) is not an option
|
||||
// // // const user = userStore;
|
||||
// // // console.log(fortyTwo);
|
||||
// // // console.log(tfa);
|
||||
// // console.log('in /test what is in user')
|
||||
// // console.log(user)
|
||||
|
||||
// // you moron $userStore is a Svelte Abreviation, this is .JS, duh
|
||||
// // let user = $userStore;
|
||||
// // let user;
|
||||
// // const unsub = userStore.subscribe(value => {
|
||||
// // console.log(value);
|
||||
// // user = value;
|
||||
// // });
|
||||
// console.log('in /test what is in userStore directly')
|
||||
// console.log(user)
|
||||
|
||||
// // return true;
|
||||
// // obvi this doesn't work cuz skips to true after no user...
|
||||
// // you gotta make the condition the true and the everything else false
|
||||
// // if (user && user.statusCode && user.statusCode === 403)
|
||||
// // if (user && user.username) {
|
||||
// if (user !== null) {
|
||||
// unsub();
|
||||
// return true;
|
||||
// } else {
|
||||
// unsub();
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
'/test': wrap({
|
||||
component: TestPage,
|
||||
conditions: [
|
||||
async(detail) => {
|
||||
// THIS SHIT TOTALLY WORKS
|
||||
// Yea in the end this might be the best thing, like also from a Security point of view
|
||||
const user = await fetch('http://transcendance:8080/api/v2/user')
|
||||
.then((resp) => resp.json())
|
||||
|
||||
console.log('in /test what is in user')
|
||||
console.log(user)
|
||||
|
||||
if (user && user.username)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
],
|
||||
// props: {
|
||||
// user: user
|
||||
// }
|
||||
}),
|
||||
'/game': Game,
|
||||
'/spectator': GameSpectator,
|
||||
'/ranking' : Ranking,
|
||||
'/profile': wrap({
|
||||
component: ProfilePage,
|
||||
conditions: [
|
||||
async(detail) => {
|
||||
const user = await fetch('http://transcendance:8080/api/v2/user')
|
||||
const user = await fetch('http://' + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + '/api/v2/user')
|
||||
.then((resp) => resp.json())
|
||||
|
||||
console.log('in /profile what is in user')
|
||||
@@ -166,7 +37,7 @@ export const primaryRoutes = {
|
||||
component: ProfilePage,
|
||||
conditions: [
|
||||
async(detail) => {
|
||||
const user = await fetch('http://transcendance:8080/api/v2/user')
|
||||
const user = await fetch('http://' + process.env.WEBSITE_HOST + ":" + process.env.WEBSITE_PORT + '/api/v2/user')
|
||||
.then((resp) => resp.json())
|
||||
|
||||
console.log('in /profile/* what is in user')
|
||||
@@ -179,82 +50,7 @@ export const primaryRoutes = {
|
||||
}
|
||||
]
|
||||
}),
|
||||
// '/game': wrap({
|
||||
// component: ProfilePage,
|
||||
// conditions: [
|
||||
// async(detail) => {
|
||||
// const user = await fetch('http://transcendance:8080/api/v2/user')
|
||||
// .then((resp) => resp.json())
|
||||
|
||||
// console.log('in /test what is in user')
|
||||
// console.log(user)
|
||||
|
||||
// if (user && user.username)
|
||||
// return true;
|
||||
// else
|
||||
// return false;
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
// '/spectate': wrap({
|
||||
// component: ProfilePage,
|
||||
// conditions: [
|
||||
// async(detail) => {
|
||||
// const user = await fetch('http://transcendance:8080/api/v2/user')
|
||||
// .then((resp) => resp.json())
|
||||
|
||||
// console.log('in /test what is in user')
|
||||
// console.log(user)
|
||||
|
||||
// if (user && user.username)
|
||||
// return true;
|
||||
// else
|
||||
// return false;
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
// '/chat': wrap({
|
||||
// component: ProfilePage,
|
||||
// conditions: [
|
||||
// async(detail) => {
|
||||
// const user = await fetch('http://transcendance:8080/api/v2/user')
|
||||
// .then((resp) => resp.json())
|
||||
|
||||
// console.log('in /test what is in user')
|
||||
// console.log(user)
|
||||
|
||||
// if (user && user.username)
|
||||
// return true;
|
||||
// else
|
||||
// return false;
|
||||
// }
|
||||
// ]
|
||||
// }),
|
||||
'/unauthorized-access': UnauthorizedAccessPage,
|
||||
'*': NotFound
|
||||
};
|
||||
|
||||
// export const primaryRoutes = {
|
||||
// "/": SplashPage,
|
||||
// "/profile": ProfilePage,
|
||||
// "/game": GamePage,
|
||||
// "/chat": ChatPage,
|
||||
// "*": NotFound
|
||||
// };
|
||||
|
||||
|
||||
// i might need to add /profile/* and such to make the nested routers work
|
||||
|
||||
// ok maybe these need to be in their own files?
|
||||
|
||||
|
||||
|
||||
// export const gameRoutes = {
|
||||
// "/": GamePage,
|
||||
// "*": NotFound
|
||||
// };
|
||||
|
||||
// export const chatRoutes = {
|
||||
// "/": ChatPage,
|
||||
// "*": NotFound
|
||||
// };
|
||||
|
||||
Reference in New Issue
Block a user