This commit is contained in:
Me
2023-01-15 20:28:26 +01:00
59 changed files with 1948 additions and 706 deletions

View File

@@ -1,10 +1,26 @@
<script>
import { link } from "svelte-spa-router";
import Header from '../pieces/Header.svelte';
</script>
<h1>We are sorry!</h1>
<p>This isn't a url that we use.</p>
<p>Go home you're drunk.</p>
<a href="/" use:link>
<h2>Take me home →</h2>
</a>
<div class="background-pages">
<div class="content">
<h1>We are sorry!</h1>
<p>This isn't a url that we use.</p>
<p>Go home you're drunk.</p>
</div>
</div>
<style>
.content {
left: 50%;
position: absolute;
-ms-transform: translateX(-50%);
transform: translateX(-50%);
}
</style>

View File

@@ -9,22 +9,8 @@
user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`)
.then((resp) => resp.json())
// i mean i could do a failed to load user or some shit, maybe with a .catch or something? but atm why bother
console.log('User var');
console.log(user);
// if (user && user.statusCode && user.statusCode === 403) {
// console.log('user not logged in')
// }
// if (user && user.username) {
// console.log('we have a user');
// }
// user === undefined doesn't work cuz we declared user in the scope of onMount
// if (user === undefined) {
if (user && user.statusCode && user.statusCode === 403) {
console.log('on mount no user, returned status code 403 so logging out of userStore')
// userLogout(); // which i think should delete any previous local storage
}
});
@@ -34,8 +20,6 @@
console.log('you are now logged in');
}
// i could prolly put this in it's own compoent, i seem to use it in several places... or maybe just some JS? like no need for html
// we could .then( () => replace('/') ) need the func so TS compatible...
const logout = async() => {
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/auth/logout`, {
method: 'POST',
@@ -46,108 +30,71 @@
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<header class="grid-container">
<h1>Potato Pong</h1>
<nav>
<!-- {#if user !== undefined} -->
{#if user && user.username}
<div on:click={ () => (push('/profile')) }>Profile</div>
<div on:click={logout}>Logout</div>
{:else}
<div on:click={login}>Login</div>
{/if}
</nav>
<h2>
<div>Welcome to</div>
<div>Potato Pong</div>
</h2>
</header>
<Canvas/>
<!-- doesn't work :( -->
<!-- <svelte:body style="overflow-y: hidden"/> -->
<div class="container">
<div class="splash-page">
{#if user && user.username}
<button class="button-in" on:click={ () => (push('/profile')) }>Profile</button>
<button class="button-out" on:click={logout}>Logout</button>
{:else}
<button class="button-in" on:click={login}>Login</button>
{/if}
</div>
</div>
<style>
@font-face {
font-family: "Bit5x3";
src:
url("/fonts/Bit5x3.woff2") format("woff2"),
local("Bit5x3"),
url("/fonts/Bit5x3.woff") format("woff");
font-weight: normal;
font-style: normal;
font-display: swap;
}
/* :global(body) {
overflow-y: hidden;
} */
header {
/* didn't work... */
overflow-y: hidden;
}
.container {
height: 100%;
width: 100%;
position: relative;
background-image: url('/img/SPLASH_PAGE_BACKGROUND.png');
background-position: center center;
background-repeat: no-repeat;
background-attachment: fixed;
background-size: cover;
}
/* The actually important stuff */
.splash-page {
margin: 0;
position: absolute;
top: 80%;
-ms-transform: translateY(-80%);
transform: translateY(-80%);
left: 50%;
-ms-transform: translateX(-50%);
transform: translateX(-50%);
}
.grid-container{
position: absolute;
left: 0;
top: 0;
.button-in {
background-color: #8c0000;
border-color: #071013;
border-width: 2px;
color: white;
font-family: "Bit5x3";
font-size: x-large;
padding: 10px;
}
box-sizing: border-box;
width: 100%;
height: 100%;
/* max-height: 100%; */
white-space: nowrap;
/* padding-bottom: 0; */
margin-bottom: 0px;
overflow: hidden;
padding: 20px 40px;
margin: 0px;
.button-out {
background-color: #008c8c;
border-color: #071013;
border-width: 2px;
color: white;
font-family: "Bit5x3";
font-size: x-large;
padding: 10px;
}
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-template-rows: 1fr 1fr 1fr 1fr 1fr;
align-items: center;
}
header h1{
grid-column: 1 / 7;
grid-row: 1;
/* grid-column: span 6; */
/* tmp? */
padding: 20px;
border: 1px solid bisque;
}
header nav{
/* make it a flexbox? */
grid-column: 7 / 13;
grid-row: 1;
justify-self: end;
/* tmp? */
padding: 20px;
border: 1px solid bisque;
}
header h2{
grid-row: 3;
grid-column: 5 / span 4;
justify-self: center;
/* tmp */
border: 1px solid black;
z-index: 3;
}
header h2 div{
font-size: 2em;
/* color: red; */
}
nav div {
display: inline;
color: bisque;
font-weight: bold;
}
nav > div {
padding-left: 1em;
/* didn't quite work, applies to both for some reason */
/* nav:first-child doesn't work either*/
}
nav div:hover{
text-decoration: underline;
cursor: pointer;
}
</style>

View File

@@ -4,11 +4,13 @@
import { fade, fly } from 'svelte/transition';
import Header from '../../pieces/Header.svelte';
import { fetchAvatar } from "../../pieces/utils";
import { fetchUser, fetchAllUsers, fetchAvatar } from "../../pieces/utils";
import * as pong from "./client/pong";
import { gameState } from "./client/ws";
import { invited_username } from '../../pieces/store_invitation';
//user's stuff
let user;
let allUsers;
@@ -36,11 +38,21 @@
const watchMatchStartIntervalRate = 111;
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() );
user = await fetchUser();
allUsers = await fetchAllUsers();
if (!user) {
showError = true;
errorMessage = "User load failed";
return;
}
options.playerOneUsername = user.username;
if ($invited_username) {
options.isSomeoneIsInvited = true;
options.playerTwoUsername = $invited_username;
invited_username.set("");
}
})
onDestroy( async() => {
@@ -268,29 +280,38 @@
<button class="pong_button" on:click={fetchInvitations}>Show invitations</button>
<fieldset in:fly="{{ y: 10, duration: 1000 }}">
<legend>game options</legend>
<div>
<label for="multi_balls">
<input type="checkbox" id="multi_balls" name="multi_balls" bind:checked={options.multi_balls}>
<label for="multi_balls">Multiples balls</label>
</div>
<div>
Multiples balls
</label>
<label for="moving_walls">
<input type="checkbox" id="moving_walls" name="moving_walls" bind:checked={options.moving_walls}>
<label for="moving_walls">Moving walls</label>
</div>
Moving walls
</label>
<div>
<p>sound :</p>
<input type="radio" id="sound_on" name="sound_selector" bind:group={options.sound} value="on">
<label for="sound_on">on</label>
<input type="radio" id="sound_off" name="sound_selector" bind:group={options.sound} value="off">
<label for="sound_off">off</label>
</div>
<div>
<input type="checkbox" id="isSomeoneIsInvited" bind:checked={options.isSomeoneIsInvited}>
<label for="moving_walls">Invite a friend</label>
sound :
<label for="sound_on">
<input type="radio" id="sound_on" name="sound_selector" bind:group={options.sound} value="on">
on
</label>
<label for="sound_off">
<input type="radio" id="sound_off" name="sound_selector" bind:group={options.sound} value="off">
off
</label>
</div>
<label for="invitation_checkbox">
<input type="checkbox" id="invitation_checkbox" bind:checked={options.isSomeoneIsInvited}>
Invite a friend
</label>
{#if options.isSomeoneIsInvited}
<select bind:value={options.playerTwoUsername}>
{#each allUsers as user }
<option value={user.username}>{user.username}</option>
{#each allUsers as invitedUser }
<option value={invitedUser.username}>{invitedUser.username}</option>
{/each}
</select>
{/if}
@@ -339,11 +360,16 @@
}
#game_page {
margin: 0;
padding: 20px;
background-color: #222425;
position: relative;
width: 100%;
height: 100%;
height: auto;
display: flex;
flex-direction: column;
flex-grow: 1;
}
#game_option {
margin-top: 20px;
}
#canvas_container {
margin-top: 20px;

View File

@@ -6,15 +6,11 @@
import Header from '../../pieces/Header.svelte';
import MatchListElem from "../../pieces/MatchListElem.svelte";
import type { Match } from "../../pieces/Match";
import { fetchAvatar } from "../../pieces/utils";
import { fetchUser, fetchAllUsers, fetchAvatar } from "../../pieces/utils";
import * as pongSpectator from "./client/pongSpectator";
import { gameState } from "./client/ws";
//user's stuff
let user;
let allUsers;
let playerOneAvatar;
let playerTwoAvatar;
@@ -30,13 +26,7 @@
const watchGameStateIntervalRate = 142;
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() );
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;
matchList = await fetchMatchList();
})
onDestroy( async() => {
@@ -82,12 +72,25 @@
async function resetPage() {
hiddenGame = true;
pongSpectator.destroy();
fetchMatchList();
matchList = await fetchMatchList();
};
async function fetchMatchList() {
matchList = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/match/all`)
.then( x => x.json() );
async function fetchMatchList()
{
return fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/match/all`)
.then((response) => {
if (!response.ok) {
throw new Error("All matchs not retrieved");
}
return response.json();
})
.then((body) => {
return body;
})
.catch((error) => {
console.log("catch fetchMatchList: ", error);
return [];
});
};
</script>
@@ -131,13 +134,19 @@
<fieldset>
<legend>options</legend>
<button class="pong_button" on:click={fetchMatchList}>Reload</button>
<div>
<p>sound :</p>
<input type="radio" id="sound_on" name="sound_selector" bind:group={sound} value="on">
<label for="sound_on">on</label>
<input type="radio" id="sound_off" name="sound_selector" bind:group={sound} value="off">
<label for="sound_off">off</label>
sound :
<label for="sound_on">
<input type="radio" id="sound_on" name="sound_selector" bind:group={sound} value="on">
on
</label>
<label for="sound_off">
<input type="radio" id="sound_off" name="sound_selector" bind:group={sound} value="off">
off
</label>
</div>
</fieldset>
{#if matchList.length !== 0}
<menu id="match_list">

View File

@@ -28,7 +28,7 @@
</script>
<Header />
<br />
<div class="background-pages">
<div class="principal-div">
<table class="stats-table">
<thead>
@@ -59,6 +59,7 @@
</tbody>
</table>
</div>
</div>
<style>
@@ -71,7 +72,6 @@
margin-left: auto;
margin-right: auto;
font-size: 0.9em;
font-family: sans-serif;
min-width: 400px;
}

View File

@@ -59,14 +59,20 @@ function start_after_countdown()
abortControllerKeydown = new AbortController();
window.addEventListener(
'keydown',
(e) => { pong.addKey(e.key); },
(e) => {
e.preventDefault();
pong.addKey(e.key);
},
{signal: abortControllerKeydown.signal}
);
abortControllerKeyup = new AbortController();
window.addEventListener(
'keyup',
(e) => { pong.deleteKey(e.key); },
(e) => {
e.preventDefault();
pong.deleteKey(e.key);
},
{signal: abortControllerKeyup.signal}
);

View File

@@ -19,6 +19,7 @@
<Chat color="bisque"/>
<div class="background-pages">
<div class="outer">
{#if user !== undefined}
<GenerateUserDisplay user={user}/>
@@ -28,13 +29,13 @@
<div>Failed to load current</div>
{/if}
</div>
</div>
<style>
div.outer{
max-width: 960px;
margin: 40px auto;
max-width: 100%;
text-align: center;
padding-bottom: 10px;
}
</style>

View File

@@ -24,22 +24,10 @@
onMount( async() => {
// DO I ACTUALLY NEED TO ON MOUNT ALL THIS STUFF?
// ALSO I COULD JUST USE THE FUNCITONS I MADE...
// yea no idea what
// i mean do i fetch user? i will for now
user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`)
.then( (x) => x.json() );
fetchAll();
// ok this shit works!
// const interval = setInterval(() => {
// fetchAll();
// }, 1000);
// return () => clearInterval(interval);
});
const fetchAll = async() => {
@@ -234,6 +222,7 @@
</script>
<div class="background-pages">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="top-grid">
@@ -353,7 +342,7 @@
</div>
</div>
</div>
<style>
/* ok so i want a 12 column grid with a sidebar */
@@ -368,7 +357,14 @@
div.sidebar-list{
grid-column: 1 / span 2;
background: white;
background: #FB8B24;
padding: 1vw;
max-width: 100%;
max-height: 100%;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
border-right: 4px solid #071013;
}
div.sidebar-item{
@@ -393,7 +389,7 @@
}
div.placeholder{
color: darkgray;
color: white;
text-align: center;
}
@@ -402,7 +398,7 @@
}
div.tip{
color: darkgray;
color: white;
font-size: 0.8em;
font-weight: bold;
}

View File

@@ -106,6 +106,7 @@
<main>
<div class="background-pages">
<h2>Look! You can change stuff</h2>
<div class="cards">
<Card>
@@ -114,7 +115,7 @@
<div class="label">New Username</div>
<!-- it really hates {user.username} and ${user.username} -->
<!-- <input type="text" placeholder="current username: ${user.username}" bind:value={set.username}> -->
<input type="text" placeholder="current username: {nameTmp}" bind:value={set.username}>
<input type="text" placeholder="{nameTmp}" bind:value={set.username}>
<div class="success">{success.username}</div>
<div class="error">{errors.username}</div>
</div>
@@ -144,7 +145,7 @@
</form>
</Card>
</div>
</div>
</main>
@@ -158,6 +159,8 @@
grid-gap: 20px; */
}
div.cards{
display: grid;
grid-template-columns: 1fr 1fr;
@@ -190,6 +193,7 @@
.inline-check{
display: inline;
color: #333;
}

View File

@@ -21,126 +21,84 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
<header>
<div>
<img src="/img/potato_logo.png" alt="Potato Pong Logo" on:click={() => (push('/'))}>
<h1>Potato Pong</h1>
</div>
<nav>
<button class:selected="{current === '/game'}" on:click={() => (push('/game'))}>Play</button>
<button class:selected="{current === '/spectator'}" on:click={() => (push('/spectator'))}>Spectate</button>
<button class:selected="{current === '/ranking'}" on:click={() => (push('/ranking'))}>Ranking</button>
<div class="header">
<img src="/img/logo_potato.png" alt="Potato Pong Logo" on:click={() => (push('/'))}>
<div class="center">
<button class:selected="{current === '/game'}" on:click={() => (push('/game'))}>Play</button>
<button class:selected="{current === '/spectator'}" on:click={() => (push('/spectator'))}>Spectate</button>
<button class:selected="{current === '/ranking'}" on:click={() => (push('/ranking'))}>Ranking</button>
<button class:selected="{current === '/profile'}" on:click={() => (push('/profile'))}>My Profile</button>
<button class:selected="{current === '/profile/friends'}" on:click={() => (push('/profile/friends'))}>Friends</button>
<button on:click={handleClickLogout}>Log Out</button>
</nav>
</header>
<button class:selected="{current === '/profile/friends'}" on:click={() => (push('/profile/friends'))}>Friends</button>
</div>
<button class="logout" on:click={handleClickLogout}>Log Out</button>
</div>
<style>
/* See "possible_fonts.css" for more font options... */
@font-face {
font-family: 'Bondi';
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');
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;
}
.selected {
background-color: chocolate;
.header div.center button.selected {
text-decoration: underline;
/* TMP so it's obvious but we need to pick good colors */
background-color: #2B3A67;
}
.header div.center button {
background-color: #8c0000;
border-color: #071013;
color: white;
font-family: "Bit5x3";
border-width: 2px;
font-size: 2vw;
}
/* There is a bunch of unncessary shit in here... why so many flex grids, why is everything the same class? just seemed easier but... */
header{
/* background: #f7f7f7; */
background: #618174;
/* padding: 20px; */
margin: 0;
/* does nothing so far... */
/* display: flex; */
.header button.logout {
background-color: #008c8c;
border-color: #071013;
color: white;
font-family: "Bit5x3";
border-width: 2px;
font-size: 2vw;
}
header{
/* for some reason this doesn't do shit! */
position: sticky;
/* position: absolute; */
display: flex;
justify-content: space-between;
flex-wrap: wrap;
/* display: grid; */
/* grid-template-columns: 1fr 1fr 1fr; */
.header {
resize: vertical;
overflow: hidden;
background: #FB8B24;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
border-bottom: 4px solid #071013;
position: relative;
display: block;
padding: 10px;
}
/* Headers */
h1{
font-family: 'Bondi';
}
h1{
margin: 0;
text-align: left;
/* max-width: 40px; */
/* this helped with the weird extra space under the image... */
display: flex;
.header div.center {
width: fit-content;
justify-self: center;
align-self: center;
position: absolute;
left: 50%;
display: inline-block;
-ms-transform: translateX(-50%);
transform: translateX(-50%);
}
/* Images */
.header button.logout {
float: right;
}
img{
.header img{
cursor: pointer;
max-width: 40px;
padding: 7px 20px;
justify-self: left;
max-width: 10%;
float: left;
}
header div {
display: flex;
justify-self: center;
align-self: center;
}
nav{
display: flex;
/* justify-content: flex-end; */
justify-content: space-evenly;
flex-wrap: wrap;
}
nav button{
margin: 7px 20px;
/* margin: 7px auto; */
/* padding: 5px; */
border-radius: 4px;
}
@media screen and (max-width: 350px) {
nav button{
width: 100%;
}
header div{
flex-wrap: wrap;
display: flex;
justify-content: center;
}
}
</style>

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import Layouts from './Chat_layouts.svelte';
import { init_socket } from './Socket_init';
import { init_socket } from './Socket_chat';
export let color = "transparent";

View File

@@ -1,7 +1,5 @@
<script lang="ts">
import Debug from './tmp_debug.svelte';
import { layout } from './Store_chat';
import ChatBox from './Chat_box_css.svelte';
@@ -11,10 +9,11 @@
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 PasswordLayout from './Layout_password.svelte';
import CreateLayout from './Layout_create.svelte';
import MuteLayout from './Layout_mute.svelte';
import UserLayout from './Layout_user.svelte';
import InviteLayout from './Layout_invite.svelte';
import Button from './Element_button.svelte';
@@ -29,8 +28,8 @@
*/
function set_layouts($layout)
{
console.log("layouts:", layouts);
console.log("layout:", $layout);
//console.log("layouts:", layouts);
//console.log("layout:", $layout);
if ($layout.length === 0)
layout.set(layouts[0]);
else if ($layout === "close")
@@ -41,7 +40,7 @@
layouts = [$layout, "home"];
else
layouts = [$layout, layouts[0]];
console.log("- layouts:", layouts);
//console.log("- layouts:", layouts);
}
$: set_layouts($layout);
@@ -67,8 +66,17 @@
{:else if $layout === "room_set"}
<RoomsetLayout back={layouts[1]} />
{:else if $layout === "protected"}
<ProtectedLayout back={layouts[1]} />
{:else if $layout === "password"}
<PasswordLayout back={layouts[1]} />
{:else if $layout === "add_password"}
<PasswordLayout back={layouts[1]} mode="add" />
{:else if $layout === "change_password"}
<PasswordLayout back={layouts[1]} mode="change" />
{:else if $layout === "remove_password"}
<PasswordLayout back={layouts[1]} mode="remove" />
{:else if $layout === "create"}
<CreateLayout back={layouts[1]} />
@@ -79,12 +87,12 @@
{:else if $layout === "user"}
<UserLayout back={layouts[1]} />
{:else if $layout === "invite"}
<InviteLayout back={layouts[1]} />
{/if}
</ChatBox>
<!-- TMP DEBUG -->
<!-- <Debug bind:layouts /> -->
<style></style>

View File

@@ -2,7 +2,6 @@
<Button
bind:layout
new_layout=""
on_click={}
my_class=""
my_title=""
>
@@ -17,7 +16,6 @@
export let my_class = "";
export let my_title = "";
export let new_layout = "";
export let on_click = "";
function update_layout() {
layout.set(new_layout);
@@ -25,7 +23,7 @@
</script>
<button on:click={update_layout} on:click={on_click} title={my_title} class={my_class}>
<button on:click={update_layout} on:click title={my_title} class={my_class}>
<p><slot></slot></p>
</button>

View File

@@ -1,6 +0,0 @@
export interface Room
{
name: string;
type: "public" | "protected" | "private" | "direct" | "user";
users?: string[];
}

View File

@@ -3,6 +3,7 @@
import { msgs, layout, allowed_chars } from './Store_chat';
import { change_room, create_room } from './Request_rooms';
import { onMount } from 'svelte';
import { FetchResponse } from './Types_chat';
import Button from './Element_button.svelte';
import Warning from './Element_warning.svelte';
@@ -13,18 +14,16 @@
onMount(async() => {
let response = await fetch('/api/v2/chat/allowedchars');
let data = await response.json();
console.log("data:", data);
allowed_chars = data.chars;
//regex = new RegExp(`^[a-zA-Z0-9\\s${allowed_chars}]+$`);
});
let room_name: string;
let room_type: string;
let is_protected = false;
let room_password: string;
let response = {
status: 0,
message: "",
}
let response: FetchResponse;
let show_error = false;
async function handleSubmit(evt)
{
@@ -36,12 +35,18 @@
let room = {
name: room_name,
type: room_type,
protection: is_protected,
};
if (is_protected === true)
room.password = room_password;
// send the new room
response = await create_room(room);
// go to room
if (response.status === 200)
if (response.status >= 300 || response.error)
show_error = response.error;
else
await change_room(response.room);
}
@@ -67,7 +72,7 @@
<!-- panel_create -->
<div class="panel panel_create __border_top">
<form on:submit|preventDefault={handleSubmit}>
{#if response.status >= 300}
{#if show_error}
<Warning content={response.message}/>
{/if}
<!-- name: -->
@@ -82,21 +87,19 @@
<input id="chat_public" bind:group={room_type} type="radio" name="room_type" value="public" required>
</label>
<!-- [ ] private -->
<label for="chat_private" class="_radio hide">
<label for="chat_private" class="_radio">
<p>private</p>
<input id="chat_private" bind:group={room_type} type="radio" name="room_type" value="private" required>
</label>
<!-- [ ] protected -->
<label for="chat_protected" class="_radio hide">
<label for="chat_protected" class="_checkbox">
<p>protected</p>
<input id="chat_protected" bind:group={room_type} type="radio" name="room_type" value="protected" required>
<input id="chat_protected" bind:checked={is_protected} type="checkbox" name="room_type" value="protected">
</label>
<!-- [x] protected -->
{#if room_type === 'protected'}
<div>
<label for="chat_pswd"><p>choose a password :</p></label>
<input id="chat_pswd" bind:value={room_password} type="password" placeholder="minimum 8 characters" minlength="8" name="password" required>
</div>
{#if is_protected === true}
<label for="chat_pswd">choose a password :</label>
<input id="chat_pswd" bind:value={room_password} type="password" placeholder="minimum 8 characters" minlength="8" name="password" required>
{/if}
<input type="submit" value="&#x2BA1">
</form>
@@ -119,24 +122,25 @@
/ auto 1fr auto ;
}
/* temp
*/
.hide {
display: none !important;
}
/* radio elements style check
*/
.panel label._radio {
.panel label {
display: inline;
margin: 10px 0px 0px auto;
padding-right: 10px;
cursor: pointer;
padding-right: 10px;
margin: 10px 0px 10px auto;
}
.panel label._radio * {
.panel label._radio {
margin-bottom: 0px;
}
.panel label._checkbox {
margin-top: 20px;
}
.panel label * {
display: inline;
}
.panel label._radio p {
.panel label p {
margin-top: 0px;
margin-bottom: 0px;
}

View File

@@ -1,6 +1,6 @@
<script>
import { layout, msgs, user } from './Store_chat';
import { layout, msgs, user, current_room } from './Store_chat';
import { change_room, get_room_messages, get_my_rooms } from './Request_rooms';
import { onMount } from 'svelte';
import Button from './Element_button.svelte';
@@ -13,8 +13,16 @@
console.log("inside go_to_room");
console.log("room:", room);
await change_room(room);
await get_room_messages();
if (room.protection && !room.allowed)
{
await current_room.set(room);
layout.set("password");
}
else
{
await change_room(room);
await get_room_messages();
}
}
</script>
@@ -47,8 +55,8 @@
<p>rooms are loading...</p>
{:then rooms}
{#each rooms as room}
<Button my_class="list" on_click={function() {go_to_room(room)}}>
{room.name}
<Button my_class="list" on:click={function() {go_to_room(room)}}>
{room.client_name}
</Button>
{/each}
{/await}

View File

@@ -0,0 +1,84 @@
<script lang="ts">
import { layout, user, current_room } from './Store_chat';
import { get_all_users, invite_user } from './Request_rooms';
import Button from './Element_button.svelte';
export let back = "";
let users = get_all_users();
// invite user in this room
async function invite_this_user(username: string)
{
console.log("inside invite_this_user");
invite_user(username);
layout.set("room");
}
</script>
<div class="grid_box">
<!-- back -->
<Button new_layout={back} my_class="back icon" my_title="go back {back}">
back
</Button>
<!-- new -->
<Button my_class="invite deactivate">
invite
</Button>
<!-- close -->
<Button new_layout="close" my_class="close icon">
close
</Button>
<!-- room_name -->
<Button my_class="room_name deactivate __border_top">
{$current_room.name}
</Button>
<!-- panel_new -->
<div class="panel panel_invite __border_top">
<p>invite someone in this room :</p>
<div>
<div class="__show_if_only_child">
<p class="__center">/ there is no one to invite yet /</p>
</div>
{#await users}
<p>users are loading...</p>
{:then users}
{#each users as user}
<Button my_class="list" on:click={function() {invite_this_user(user.username)}}>
{user.username}
</Button>
{/each}
{/await}
</div>
</div>
</div>
<style>
/* grid layout "new"
*/
.grid_box :global(.back ) {grid-area: back;}
.grid_box :global(.invite ) {grid-area: invite;}
.grid_box :global(.close ) {grid-area: close;}
.grid_box :global(.room_name ) {grid-area: room_name;}
.grid_box :global(.panel_invite) {grid-area: panel_invite;}
.grid_box {
grid:
' back invite close ' auto
' room_name room_name room_name ' auto
' panel_invite panel_invite panel_invite ' 1fr
/ auto 1fr auto ;
}
</style>

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { layout, msgs, user, socket } from './Store_chat';
import { layout, msgs, user, socket, current_room } from './Store_chat';
import { join_room, change_room, get_room_messages, get_all_rooms } from './Request_rooms';
import Button from './Element_button.svelte';
@@ -13,10 +13,14 @@
{
console.log("inside join_room");
console.log("room:", room);
const updated_room = await join_room(room);
console.log("updated room:", updated_room);
await change_room(updated_room);
if (updated_room.protection)
{
current_room.set(updated_room);
layout.set("password");
}
else
await change_room(updated_room);
}
</script>
@@ -52,7 +56,7 @@
<p>rooms are loading...</p>
{:then rooms}
{#each rooms as room}
<Button my_class="list" on_click={function() {join_rooms(room)}}>
<Button my_class="list" on:click={function() {join_rooms(room)}}>
{room.name}
</Button>
{/each}

View File

@@ -0,0 +1,121 @@
<script lang="ts">
import { layout, current_room } from './Store_chat';
import { change_room, validate_password, change_password, add_password, remove_password } from './Request_rooms';
import { FetchResponse } from './Types_chat';
import Button from './Element_button.svelte';
import Warning from './Element_warning.svelte';
export let back = "";
export let mode = "validate";
let password_state = "";
if (mode === 'change')
password_state = "new";
if (mode === 'remove')
password_state = "current";
let room_password: string;
let room_old_password: string;
let response: FetchResponse;
let show_error = false;
async function handleSubmit(evt)
{
console.log("in handleSubmit");
let formIsValid = evt.target.checkValidity();
if (!formIsValid)
return;
let room = {
name: $current_room.name,
type: $current_room.type,
password: room_password,
};
room.protection = true;
if (mode === 'remove')
room.protection = false;
// send password
if (mode === 'validate')
response = await validate_password(room);
if (mode === 'add')
response = await add_password(room);
if (mode === 'change')
response = await change_password(room, room_old_password);
if (mode === 'remove')
response = await remove_password(room);
// go to room
if (response.status >= 300 || response.error)
show_error = response.error;
else
await change_room(response.room);
}
</script>
<div class="grid_box">
<!-- back -->
<Button new_layout={back} my_class="back icon" my_title="go back {back}">
back
</Button>
<!-- room_name -->
<Button my_class="room_name deactivate">
{$current_room.name}
</Button>
<!-- close -->
<Button new_layout="close" my_class="close icon">
close
</Button>
<!-- panel_password -->
<div class="panel panel_password __border_top">
<p class="title __center">this room is protected</p>
<form on:submit|preventDefault={handleSubmit}>
{#if show_error}
<Warning content={response.message}/>
{/if}
{#if mode === 'change'}
<label for="chat_old_pswd"><p>enter old password :</p></label>
<input id="chat_old_pswd" bind:value={room_old_password} type="password" placeholder="minimum 8 characters" minlength="8" name="old_password" required>
{/if}
<label for="chat_pswd"><p>enter {password_state} password :</p></label>
<input id="chat_pswd" bind:value={room_password} type="password" placeholder="minimum 8 characters" minlength="8" name="password" required>
<input type="submit" value="&#x2BA1">
</form>
</div>
</div>
<style>
/* grid layout "password"
*/
.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_password) {grid-area: panel_password;}
.grid_box {
grid:
' back room_name close ' auto
' panel_password panel_password panel_password ' 1fr
/ auto 1fr auto ;
}
/* submit
*/
form input[type=submit] {
margin-top: 20px;
}
</style>

View File

@@ -1,65 +0,0 @@
<script>
import { layout } from './Store_chat';
import Button from './Element_button.svelte';
export let back = "";
</script>
<div class="grid_box">
<!-- back -->
<Button new_layout={back} my_class="back icon" my_title="go back {back}">
back
</Button>
<!-- room_name -->
<Button my_class="room_name deactivate">
&lt;room_name&gt;
</Button>
<!-- close -->
<Button 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="&#x2BA1">
</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>

View File

@@ -1,6 +1,6 @@
<script>
import { layout, socket, msgs, add_msg, current_room_name } from './Store_chat';
import { layout, socket, msgs, add_msg, current_room } from './Store_chat';
import Button from './Element_button.svelte';
import Msg from './Element_msg.svelte';
@@ -42,7 +42,7 @@
<!-- room_name -->
<Button new_layout="room_set" my_class="room_name transparent">
{$current_room_name}
{$current_room.name}
</Button>
<!-- close -->
@@ -71,7 +71,7 @@
</div>
<!-- send -->
<Button my_class="send" on_click={send_msg}>
<Button my_class="send" on:click={send_msg}>
send
</Button>

View File

@@ -1,22 +1,24 @@
<script>
import { layout, current_room_name } from './Store_chat';
import { get_room_users, user_leave_room } from './Request_rooms';
import { layout, current_room } from './Store_chat';
import { get_room_users, leave_room } from './Request_rooms';
import Button from './Element_button.svelte';
export let back = "";
let users = get_room_users();
console.log("current_room:", $current_room);
function user_profile()
{
console/log("in user_profile");
console.log("in user_profile");
}
function leave_room()
function user_leave_room()
{
console.log("in leave_room");
user_leave_room();
leave_room();
layout.set("home");
}
@@ -31,7 +33,7 @@
<!-- room_name -->
<Button my_class="room_name deactivate">
{$current_room_name}
{$current_room.name}
</Button>
<!-- close -->
@@ -41,9 +43,27 @@
<!-- panel_room_set -->
<div class="panel panel_room_set __border_top">
<Button on_click={leave_room}>
leave
</Button>
{#if $current_room.type !== "direct"}
<Button on:click={user_leave_room}>
leave
</Button>
<Button new_layout="invite">
invite someone
</Button>
{/if}
{#if $current_room.protection }
<p class="__center">this room is password protected</p>
<Button new_layout="change_password">
change password
</Button>
<Button new_layout="remove_password">
remove password
</Button>
{:else}
<Button new_layout="add_password">
add password
</Button>
{/if}
<p>room users :</p>
<div class="room_users">
<div class="__show_if_only_child">
@@ -53,7 +73,7 @@
<p>list of users is loading...</p>
{:then users}
{#each users as user}
<Button new_layout="user" my_class="list" on_click={user_profile}>
<Button new_layout="user" my_class="list" on:click={user_profile}>
{user}
</Button>
{/each}

View File

@@ -8,6 +8,14 @@
let mute = "mute";
let block = "block";
import { push } from "svelte-spa-router";
import { invited_username } from '../store_invitation';
function game_invitation()
{
const usernamePLACEHOLDER = "hulamy";
invited_username.set(usernamePLACEHOLDER);
push("/game");
}
</script>
@@ -41,7 +49,7 @@
<Button>
view profile
</Button>
<Button>
<Button on_click={() => game_invitation()}>
game invitation
</Button>
<Button>

View File

@@ -1,12 +1,14 @@
import { msgs, user, layout, socket, current_room_name } from './Store_chat';
import type { Room } from './Interface_chat';
import { msgs, user, layout, socket, current_room } from './Store_chat';
import { Room, FetchResponse, FetchMethod } from './Types_chat';
import { fetch_chat_request, set_client_name_on_room, fill_fetch_response } from './Request_utils';
export async function get_room_messages()
{
console.log("in get_room_messages");
const response = await fetch('/api/v2/chat/messages');
const data = await response.json();
const messages = data.messages;
let response: FetchResponse = await fetch_chat_request('messages', FetchMethod.GET);
const messages = response.messages;
if (messages === null)
return;
@@ -24,102 +26,141 @@ export async function create_room(room: Room)
{
console.log("in create_room");
// send the new room
const response = await fetch('/api/v2/chat/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(room),
});
console.log("room sent to create:", room);
let response: FetchResponse = await fetch_chat_request('create', FetchMethod.POST, room);
console.log("room returned from create:", response.room);
// get response status and message
let response_status = response.status;
let data = await response.json();
let response_message = "";
return {
status: response_status,
message: data.message,
room: data.room,
};
return response;
}
export async function join_room(room: Room)
{
console.log("in join_room");
const response = await fetch('/api/v2/chat/join', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(room),
});
let data = await response.json();
console.log("room sent to join:", room);
let response: FetchResponse = await fetch_chat_request('join', FetchMethod.POST, room);
console.log("room returned from join:", response.room);
return data.room;
return response.room;
}
export async function change_room(room: Room)
{
console.log("in change_room");
console.log("room:", room);
const response = await fetch('/api/v2/chat/change', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(room),
});
let data = await response.json();
console.log("room sent to change:", room);
let response: FetchResponse = await fetch_chat_request('change', FetchMethod.POST, room);
console.log("room returned from change:", response.room);
await get_room_messages();
let room_name = data.room.name;
if (room.type === 'direct')
{
room_name === room.users[0];
if (room_name === user.username)
room_name === room.users[1];
}
current_room_name.set(room_name);
set_client_name_on_room(room);
current_room.set(room);
layout.set("room");
}
export async function validate_password(room: Room)
{
console.log("in validate_password");
console.log("room sent to validate password:", room);
let response: FetchResponse = await fetch_chat_request('passwordauth', FetchMethod.POST, room);
console.log("room returned from validate password:", response.room);
return response;
}
export async function add_password(room: Room)
{
console.log("in add_password");
console.log("room sent to add password:", room);
let response: FetchResponse = await fetch_chat_request('addpassword', FetchMethod.POST, room);
console.log("room returned from add password:", response.room);
return response;
}
export async function change_password(room: Room, old_password: string)
{
console.log("in send_password");
let request_body =
{
room: room,
old_password: old_password,
}
console.log("room sent to change password:", room);
let response: FetchResponse = await fetch_chat_request('changepassword', FetchMethod.POST, request_body);
console.log("room returned from change password:", response.room);
return response;
}
export async function remove_password(room: Room)
{
console.log("in send_password");
console.log("room sent to remove password:", room);
let response: FetchResponse = await fetch_chat_request('removepassword', FetchMethod.DELETE, room);
console.log("room returned from remove password:", response.room);
return response;
}
export async function invite_user(user_name: string)
{
console.log("in invite_user");
let response: FetchResponse = await fetch_chat_request('invite', FetchMethod.POST, {username: user_name});
await get_room_messages();
}
export async function get_my_rooms()
{
console.log("in get_my_rooms");
const response = await fetch('/api/v2/chat/myrooms');
const data = await response.json();
console.log("data.rooms:", data.rooms);
let response: FetchResponse = await fetch_chat_request('myrooms', FetchMethod.GET);
return data.rooms;
let rooms = response.rooms.map(room => set_client_name_on_room(room));
return rooms;
}
export async function get_all_rooms()
{
console.log("in get_all_rooms");
const response = await fetch('/api/v2/chat/allrooms');
const data = await response.json();
let response: FetchResponse = await fetch_chat_request('allrooms', FetchMethod.GET);
return data.rooms;
return response.rooms;
}
export async function get_room_users()
{
console.log("in get_room_users");
const response = await fetch('/api/v2/chat/roomusers');
const data = await response.json();
let response: FetchResponse = await fetch_chat_request('roomusers', FetchMethod.GET);
return data.users;
return response.users;
}
export async function user_leave_room()
export async function get_all_users()
{
console.log("in get_all_users");
let response: FetchResponse = await fetch_chat_request('users', FetchMethod.GET);
return response.users;
}
export async function leave_room()
{
console.log("in leave_room");
const response = await fetch('/api/v2/chat/removeuser', {
method: 'DELETE',
});
let response: FetchResponse = await fetch_chat_request('leave', FetchMethod.DELETE);
}

View File

@@ -0,0 +1,59 @@
import { user } from './Store_chat';
import { Room, FetchResponse, FetchInit, FetchMethod } from './Types_chat';
export async function fetch_chat_request(route: string, fetchMethod: FetchMethod, param?: any)
{
console.log("in fetch_chat_request");
let response: FetchResponse = { status: 0 };
let fetch_params: FetchInit = {
method: fetchMethod,
headers: { 'Content-Type': 'application/json' },
}
if (param)
fetch_params.body = JSON.stringify(param);
try
{
const resp = await fetch(`/api/v2/chat/${route}`, fetch_params);
response.status = resp.status;
let data = await resp.json();
fill_fetch_response(response, data);
if (!resp.ok)
throw new Error(data.message);
}
catch (error)
{
console.error(error.message);
}
return response;
}
export function set_client_name_on_room(room: Room)
{
console.log("in set_client_name_on_room");
if (room.type === 'direct')
{
room.client_name = room.users[0];
if (room.client_name === user.username)
room.client_name = room.users[1];
}
else
room.client_name = room.name;
return room;
}
export function fill_fetch_response(response: FetchResponse, data: any)
{
console.log("in fill_fetch_response");
Object.keys(data).forEach(key =>
{
response[key] = data[key];
});
}

View File

@@ -1,6 +1,6 @@
import io from 'socket.io-client';
import { set_socket, set_user } from './Store_chat';
import { socket_events } from './Socket_events';
import { user, msgs } from './Store_chat';
const address = `http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}`;
@@ -21,6 +21,23 @@ export async function init_socket()
});
set_socket(socket);
socket_states(socket);
socket_events(socket);
}
function socket_events(socket)
{
socket.on('message', function(from, message)
{
console.log("received msg:", message, from);
if (from === user.username)
from = "me";
msgs.update(msgs => [...msgs, { name: from, message: message }]);
});
}
function socket_states(socket)
{
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"); });
@@ -33,7 +50,5 @@ export async function init_socket()
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"); });
socket_events(socket);
}

View File

@@ -1,12 +0,0 @@
import { user, msgs } from './Store_chat';
export function socket_events(socket)
{
socket.on('message', function(from, message)
{
console.log("received msg:", message, from);
if (from === user.username)
from = "me";
msgs.update(msgs => [...msgs, { name: from, message: message }]);
});
}

View File

@@ -1,8 +1,13 @@
import { writable } from 'svelte/store';
import { Room } from './Types_chat';
export let msgs = writable([]);
export let layout = writable("close");
export let current_room_name = writable("");
export let current_room: Room = writable({
name: "",
type: "",
protection: false,
});
export let user;
export let socket;

View File

@@ -0,0 +1,33 @@
export interface Room
{
name: string;
type: "public" | "private" | "direct" | "user";
users?: string[];
client_name?: string;
protection: boolean;
allowed?: boolean;
}
export interface FetchResponse
{
status: number;
error?: boolean;
code?: string;
message?: string;
room?: Room;
}
export interface FetchInit
{
method: string;
headers: any;
body?: string;
}
export enum FetchMethod
{
POST = 'POST',
GET = 'GET',
DELETE = 'DELETE',
}

View File

@@ -1,27 +0,0 @@
<script>
export let layout = "";
export let layouts = [];
</script>
<div style="display: flex; flex-direction: column; font-size: 12px; position: fixed; top: 20px; left: 20px; background-color: white;">
<p>temp, for testing :</p>
<button on:click={function(){layout = "close" }}>close</button>
<button on:click={function(){layout = "home" }}>home</button>
<button on:click={function(){layout = "room" }}>room</button>
<button on:click={function(){layout = "new" }}>new</button>
<button on:click={function(){layout = "settings" }}>settings</button>
<button on:click={function(){layout = "room_set" }}>room_set</button>
<button on:click={function(){layout = "protected"}}>protected</button>
<button on:click={function(){layout = "create" }}>create</button>
<button on:click={function(){layout = "mute" }}>mute</button>
<button on:click={function(){
layouts = ["settings", "settings"];
layout = "user";
}}>user from settings</button>
<button on:click={function(){
layouts = ["room_set", "room_set"];
layout = "user";
}}>user from room_set</button>
</div>

View File

@@ -0,0 +1,4 @@
import { writable } from 'svelte/store';
export const invited_username = writable("");

View File

@@ -13,5 +13,42 @@ export async function fetchAvatar(username: string)
})
.catch((error) => {
console.log("catch fetchAvatar: ", error);
return `http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/img/default.png`;
});
}
export async function fetchUser()
{
return fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`)
.then((response) => {
if (!response.ok) {
throw new Error("User not retrieved");
}
return response.json();
})
.then((body) => {
return body;
})
.catch((error) => {
console.log("catch fetchUser: ", error);
return null;
});
}
export async function fetchAllUsers()
{
return fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/all`)
.then((response) => {
if (!response.ok) {
throw new Error("All Users not retrieved");
}
return response.json();
})
.then((body) => {
return body;
})
.catch((error) => {
console.log("catch fetchAllUsers: ", error);
return [];
});
}