merge with master but css broken

This commit is contained in:
simplonco
2023-01-18 05:55:05 +01:00
45 changed files with 1413 additions and 1359 deletions

View File

@@ -380,9 +380,9 @@
}
},
"node_modules/anymatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"dependencies": {
"normalize-path": "^3.0.0",
@@ -622,9 +622,9 @@
}
},
"node_modules/engine.io-parser": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz",
"integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==",
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz",
"integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==",
"engines": {
"node": ">=10.0.0"
}
@@ -666,9 +666,9 @@
}
},
"node_modules/fastq": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
"integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
"integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
"dev": true,
"dependencies": {
"reusify": "^1.0.4"
@@ -1556,18 +1556,18 @@
}
},
"node_modules/svelte": {
"version": "3.53.1",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.53.1.tgz",
"integrity": "sha512-Q4/hHkktZogGhN5iqxqSi9sjEVoe/NbIxX4hXEHoasTxj+TxEQVAq66LnDMdAZxjmsodkoI5F3slqsS68U7FNw==",
"version": "3.55.1",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.55.1.tgz",
"integrity": "sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==",
"dev": true,
"engines": {
"node": ">= 8"
}
},
"node_modules/svelte-check": {
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-2.9.2.tgz",
"integrity": "sha512-DRi8HhnCiqiGR2YF9ervPGvtoYrheE09cXieCTEqeTPOTJzfoa54Py8rovIBv4bH4n5HgZYIyTQ3DDLHQLl2uQ==",
"version": "2.10.3",
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-2.10.3.tgz",
"integrity": "sha512-Nt1aWHTOKFReBpmJ1vPug0aGysqPwJh2seM1OvICfM2oeyaA62mOiy5EvkXhltGfhCcIQcq2LoE0l1CwcWPjlw==",
"dev": true,
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.9",
@@ -1664,9 +1664,9 @@
}
},
"node_modules/terser": {
"version": "5.15.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz",
"integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==",
"version": "5.16.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz",
"integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==",
"dev": true,
"dependencies": {
"@jridgewell/source-map": "^0.3.2",
@@ -1716,9 +1716,9 @@
"dev": true
},
"node_modules/typescript": {
"version": "4.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz",
"integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==",
"version": "4.9.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
"dev": true,
"bin": {
"tsc": "bin/tsc",

View File

@@ -26,6 +26,16 @@ body {
font-display: swap;
}
@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;
}
a {
color: rgb(0,100,200);

View File

@@ -4,18 +4,17 @@
import { location } from 'svelte-spa-router';
import Chat from './pieces/chat/Chat.svelte';
import Header from './pieces/Header.svelte';
import { showHeader } from './pieces/store_showHeader';
const conditionsFailed = (event) => {
console.error('conditionsFailed event', event.detail);
// i mean i guess i can just leave this in there permanently?
// replace('/unauthorized-access');
replace('/');
};
const conditionsFailed = (event) => {
console.error('conditionsFailed event', event.detail);
// replace('/unauthorized-access');
replace('/');
};
</script>
{#if ($location !== '/')}
{#if ($location !== '/' && $showHeader)}
<Header/>
{/if}

View File

@@ -1,5 +1,4 @@
<script lang="ts">
import { onMount } from "svelte";
import { push } from "svelte-spa-router";
let qrCodeImg;
@@ -57,21 +56,18 @@
</script>
<!-- ask Hugo but for Reactive page might want to put main in a div or something? -->
<main>
<h1>2FA Sign In</h1>
<p>use google authenticator</p>
{#await fetchQrCodeImg}
<p>Please Wait...</p>
{:then data}
<!-- What the hell is data? ask Cherif -->
<img src={qrCodeImg} alt="A QRCodeImg you must scan with google authenticator" id="qrcodeImg" />
<form on:submit|preventDefault={submitCode}>
<input id="code" bind:value={qrCode} type="text" placeholder="Input Code"/>
<button type="submit">Send</button>
</form>
{#if wrongCode}
<!-- shoudl i do this in the Net Ninja way? like it's "" until an error happens? -->
<div class="error">
{wrongCode}
</div>
@@ -83,18 +79,13 @@
<style>
main {
/* display: flex; */
/* align-items: center; */
text-align: center;
padding-top: 40px;
padding-bottom: 40px;
/* max-width: 500px; */
}
form {
/* max-width: 330px; */
padding-top: 15px;
}
form input {

View File

@@ -9,6 +9,7 @@
import { gameState } from "./client/ws";
import { invited_username } from '../../pieces/store_invitation';
import { showHeader } from '../../pieces/store_showHeader';
//user's stuff
let user;
@@ -61,6 +62,7 @@
clearTimeout(value);
});
pong.destroy();
setHiddenGame(true);
})
function resetPage() {
@@ -73,6 +75,12 @@
pong.destroy();
};
function leaveMatch() {
clearInterval(watchMatchStartInterval);
clearInterval(watchGameStateInterval);
resetPage();
};
const initGame = async() =>
{
showWaitPage = true;
@@ -239,20 +247,18 @@
});
}
function leaveMatch() {
clearInterval(watchMatchStartInterval);
clearInterval(watchGameStateInterval);
resetPage();
};
let game_page_class = "";
function setHiddenGame(value: boolean)
{
if (value) {
if (value === true) {
game_page_class = "";
window.document.body.classList.remove('dim_background');
showHeader.set(true);
}
else {
game_page_class = "dim_background";
window.document.body.classList.add('dim_background');
showHeader.set(false);
}
hiddenGame = value;
}
@@ -299,11 +305,11 @@
{#if gameState.matchStarted && !gameState.matchEnded}
<div class="div_game">
<button class="pong_button" on:click={leaveMatch}>forfeit</button>
<button class="pong_button margin_top" on:click={leaveMatch}>forfeit</button>
</div>
{:else if !gameState.matchStarted}
<div class="div_game">
<button class="pong_button" on:click={leaveMatch}>leave matchmaking</button>
<button class="pong_button margin_top" on:click={leaveMatch}>leave matchmaking</button>
</div>
{/if}
{/if}
@@ -323,7 +329,7 @@
{#if hiddenGame}
{#if showGameOptions}
<div class="div_game" id="game_options">
<button class="pong_button" on:click={fetchInvitations}>Show invitations</button>
<button class="pong_button game_options_button" on:click={fetchInvitations}>Show invitations</button>
<fieldset in:fly="{{ y: 10, duration: 1000 }}">
<legend>game options</legend>
@@ -370,7 +376,7 @@
{#if showInvitations}
<div class="div_game" id="game_invitations">
<button class="pong_button" on:click={switchToGameOptions}>Play a Game</button>
<button class="pong_button game_options_button" on:click={switchToGameOptions}>Play a Game</button>
<fieldset in:fly="{{ y: 10, duration: 1000 }}">
<legend>invitations</legend>
<button class="pong_button" on:click={fetchInvitations}>Reload</button>
@@ -390,23 +396,28 @@
{/if}
{/if}
<div id="preload_font">.</div>
</div> <!-- div "game_page" -->
<style>
@font-face {
#preload_font {
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;
opacity:0;
height:0;
width:0;
display:inline-block;
}
.dim_background {
background-color: #222;
}
:global(body.dim_background) {
background-color: #222;
}
#game_page {
margin: 0;
position: relative;
@@ -417,8 +428,9 @@
flex-grow: 1;
}
#game_options, #game_invitations {
margin-top: 20px;
.game_options_button {
margin-top: 15px;
margin-bottom: 15px;
}
#canvas_container {
@@ -429,11 +441,9 @@
/* overflow: hidden; */
}
canvas {
/* background-color: #ff0000; */
background-color: #333;
max-width: 75vw;
font-family: "Bit5x3";
/* max-height: 100vh; */
background-color: #333;
max-width: 65vw;
width: 80%;
}
@@ -459,10 +469,12 @@ canvas {
padding: 10px;
}
.avatar {
min-height: 100px;
min-width: 100px;
max-width: 100px;
max-height: 100px;
height: 5vw;
width: 5vw;
margin-top: 2vw;
margin-bottom: -2vw;
}
button.margin_top {
margin-top: 1vw;
}
</style>

View File

@@ -3,13 +3,15 @@
import { onMount, onDestroy } from "svelte";
import { fade, fly } from 'svelte/transition';
import MatchListElem from "../../pieces/MatchListElem.svelte";
import type { Match } from "../../pieces/Match";
import MatchOngoingElem from "../../pieces/MatchOngoingElem.svelte";
import type { MatchOngoing } from "../../pieces/Match";
import { fetchAvatar } from "../../pieces/utils";
import * as pongSpectator from "./client/pongSpectator";
import { gameState } from "./client/ws";
import { showHeader } from '../../pieces/store_showHeader';
let playerOneAvatar = "";
let playerTwoAvatar = "";
@@ -20,13 +22,13 @@
//html boolean for pages
let hiddenGame = true;
let matchList: Match[] = [];
let matchList: MatchOngoing[] = [];
let watchGameStateInterval;
const watchGameStateIntervalRate = 142;
let timeoutArr = [];
onMount( async() => {
matchList = await fetchMatchList();
await fetchMatchList();
})
onDestroy( async() => {
@@ -35,9 +37,21 @@
clearTimeout(value);
});
pongSpectator.destroy();
setHiddenGame(true);
})
async function initGameSpectator(match: Match)
async function resetPage() {
setHiddenGame(true);
pongSpectator.destroy();
await fetchMatchList();
};
function leaveMatch() {
clearInterval(watchGameStateInterval);
resetPage();
};
async function initGameSpectator(match: MatchOngoing)
{
watchGameStateInterval = setInterval(watchGameState, watchGameStateIntervalRate);
pongSpectator.init(match.gameOptions, sound, gameAreaId, match.gameServerIdOfTheMatch);
@@ -68,20 +82,9 @@
}
}
function leaveMatch() {
clearInterval(watchGameStateInterval);
resetPage();
};
async function resetPage() {
setHiddenGame(true);
pongSpectator.destroy();
matchList = await fetchMatchList();
};
async function fetchMatchList()
{
return fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/match/all`)
matchList = await 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");
@@ -100,11 +103,15 @@
let game_page_class = "";
function setHiddenGame(value: boolean)
{
if (value) {
if (value === true) {
game_page_class = "";
window.document.body.classList.remove('dim_background');
showHeader.set(true);
}
else {
game_page_class = "dim_background";
window.document.body.classList.add('dim_background');
showHeader.set(false);
}
hiddenGame = value;
}
@@ -137,7 +144,7 @@
{#if !gameState.matchEnded}
<div class="div_game">
<button class="pong_button" on:click={leaveMatch}>leave</button>
<button class="pong_button margin_top" on:click={leaveMatch}>leave</button>
</div>
{/if}
{/if}
@@ -145,7 +152,7 @@
<!-- -->
{#if hiddenGame}
<div class="div_game" in:fly="{{ y: 10, duration: 1000 }}">
<div class="div_game game_options" in:fly="{{ y: 10, duration: 1000 }}">
<fieldset>
<legend>options</legend>
<button class="pong_button" on:click={fetchMatchList}>Reload</button>
@@ -164,44 +171,65 @@
</fieldset>
{#if matchList.length !== 0}
<menu id="match_list">
<!-- <menu id="match_list"> -->
{#each matchList as match}
<MatchListElem match={match} on:click={(e) => initGameSpectator(match)} />
<MatchOngoingElem match={match} on:click={(e) => initGameSpectator(match)} />
<br>
{/each}
</menu>
<!-- </menu> -->
{:else}
<p>no match ongoing</p>
{/if}
</div>
{/if}
<div id="preload_font">.</div>
</div> <!-- div "game_page" -->
<!-- -->
<style>
@font-face {
#preload_font {
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;
opacity:0;
height:0;
width:0;
display:inline-block;
}
.dim_background {
background-color: #222;
}
#game_page {
:global(body.dim_background) {
background-color: #222;
}
/* Possible rollback, demander à Hugo */
/* #game_page {
margin: 0;
padding: 20px;
position: relative;
width: 100%;
height: 100%;
box-sizing: border-box;
} */
#game_page {
margin: 0;
position: relative;
width: 100%;
height: auto;
display: flex;
flex-direction: column;
flex-grow: 1;
}
.game_options {
margin-top: 15px;
margin-bottom: 15px;
}
#canvas_container {
margin-top: 20px;
text-align: center;
@@ -210,14 +238,11 @@
/* overflow: hidden; */
}
canvas {
/* background-color: #ff0000; */
background-color: #333333;
max-width: 75vw;
font-family: "Bit5x3";
/* max-height: 100vh; */
background-color: #333;
max-width: 65vw;
width: 80%;
}
.div_game {
text-align: center;
font-family: "PressStart2P";
@@ -239,15 +264,16 @@ canvas {
font-size: 1vw;
padding: 10px;
}
#match_list {
font-family: 'Courier New', Courier, monospace;
font-size: 1vw;
}
/* #match_list {
text-align: center;
} */
.avatar {
min-height: 100px;
min-width: 100px;
max-width: 100px;
max-height: 100px;
height: 5vw;
width: 5vw;
margin-top: 2vw;
margin-bottom: -2vw;
}
button.margin_top {
margin-top: 1vw;
}
</style>

View File

@@ -2,6 +2,7 @@
import { onMount } from 'svelte';
import GenerateUserDisplay from '../../pieces/GenerateUserDisplay.svelte';
import MatchHistory from '../../pieces/MatchHistory.svelte';
import { push } from 'svelte-spa-router';
import { fetchUser } from "../../pieces/utils";
@@ -16,8 +17,9 @@
<div class="background-pages">
<div class="outer">
{#if user !== undefined}
<button id="setting_button" on:click={() => (push('/profile/settings'))}>Profile Settings</button>
<GenerateUserDisplay user={user}/>
<button on:click={() => (push('/profile/settings'))}>Profile Settings</button>
<MatchHistory user={user}/>
{:else}
<h2>Sorry</h2>
<div>Failed to load current</div>
@@ -27,10 +29,20 @@
<style>
div.outer{
max-width: 100%;
text-align: center;
padding-bottom: 10px;
}
div.outer{
max-width: 100%;
text-align: center;
padding-bottom: 10px;
}
#setting_button {
margin-top: 1vw;
font-family: "PressStart2P";
background-color: #618174;
border-color: #071013;
color: white;
border-width: 2px;
font-size: 1vw;
}
</style>

View File

@@ -6,6 +6,7 @@
import { onMount } from 'svelte';
import GenerateUserDisplay from '../../pieces/GenerateUserDisplay.svelte';
import MatchHistory from '../../pieces/MatchHistory.svelte';
import { fetchUser } from "../../pieces/utils";
export let params;
@@ -18,13 +19,20 @@
</script>
<div class="background-pages">
{#if oneUser}
<GenerateUserDisplay user={oneUser}/>
{:else}
<h2>Sorry</h2>
<div>Failed to load user {params.username}</div>
{/if}
<!-- This is where i might toss in an Invite to Game button ? -->
{#if oneUser}
<GenerateUserDisplay user={oneUser}/>
<MatchHistory user={oneUser}/>
{:else}
<h2>Sorry</h2>
<div>Failed to load user {params.username}</div>
{/if}
</div>
<style>
h2 {
text-align: center;
}
div {
text-align: center;
}
</style>

View File

@@ -11,9 +11,8 @@
let avatar, newAvatar;
let uploadAvatarSuccess = false;
// tbh i might not need this...
let set = { username: '', tfa: false };
let nameTmp; // annoying...
let nameTmp;
const errors = { username: '', checkbox: '', avatar: ''};
let success = {username: '', avatar: '' };
@@ -24,7 +23,6 @@
if (!user) {
console.log('User did not load, something more official should prolly happen')
}
// i don't unerstand why this is necessary but it really doesn't like it otherwise
nameTmp = user.username;
set.tfa = user.isEnabledTwoFactorAuth;
@@ -51,7 +49,7 @@
.then((response) => {
if (!response.ok) {
success.username = ''
errors.username = "Use [a-zA-Z0-9] and - _ .";
errors.username = "Max length : 50 . Use [a-zA-Z0-9] and - _ .";
if (response.status === 409) {
errors.username = `${set.username} is already in use, pick a different one.`;
}
@@ -82,8 +80,6 @@
const data = new FormData();
data.append("file", newAvatar[0]);
// tmp
console.log(data);
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/avatar`,
{
@@ -93,9 +89,11 @@
)
.then((response) => {
if (!response.ok) {
success.avatar = ''
errors.avatar = response.statusText;
throw new Error("HTTP " + response.status);
}
errors.avatar = ''
uploadAvatarSuccess = true;
success.avatar = 'Your avatar has been updated';
})
@@ -106,6 +104,23 @@
avatar = await fetchAvatar(user.username);
}
const deleteAccount = async() => {
console.log("deleting account")
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`, {
method: "DELETE",
})
.then((response) => {
if (!response.ok) {
throw new Error("HTTP " + response.status);
}
console.log("account deleted")
push('/');
})
.catch((error) => {
console.log("catch unable to delete: ", error);
});
}
</script>
@@ -117,8 +132,6 @@
<form on:submit|preventDefault={settingsHandler}>
<div class="form-field">
<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="{nameTmp}" bind:value={set.username}>
<div class="success">{success.username}</div>
<div class="error">{errors.username}</div>
@@ -133,62 +146,48 @@
</Card>
<Card>
<!-- ok so it can't actually show us the file we upload until we've uploaded it... -->
{#if avatar}
<img class="avatar" src={avatar} alt="your avatar"/>
{/if}
<form on:submit|preventDefault={uploadAvatar}>
<!-- not convinced this has any utility. -->
<!-- <input type="text" bind:value={postVar} placeholder={"choose your file"}/> -->
<div class="form-field">
<div class="label">Pick a new Avatar</div>
<input type="file" bind:files={newAvatar}/>
<div class="error">{errors.avatar}</div>
<div class="success">{success.avatar}</div>
</div>
<Button type={!newAvatar ? "primary" : "secondary"}>Upload Avatar</Button>
</form>
</Card>
</div>
<Button type="primary" on:click={() => deleteAccount()}>Delete Account</Button>
</div>
</main>
<style>
main {
text-align: center;
/* display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 20px; */
}
div.cards{
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 20px;
/* text-align: center; */
margin-bottom: 10px;
}
img {
/* we want the image to display as the same size no matter the resolution */
/* max-width: fit-content; */
/* max-width: 100px; */
width: 60px;
/* ok this works but i'm guessing i'll need to add some @media ... */
/* padding: 10px; */
/* this doesn't work either when nothing loads and it's just the words... don't love that */
}
form {
/* max-width: 500px; */
text-align: center;
}
.form-field {
padding: 10px;
color: #333;
}
.label {
@@ -200,7 +199,6 @@
color: #333;
}
.error{
font-size: 1vw;
font-weight: bold;

View File

@@ -1,11 +1,15 @@
<script lang="ts">
import { onMount } from "svelte";
import { onMount } from "svelte";
import Button from "../../pieces/Button.svelte";
import DisplayAUser from "../../pieces/DisplayAUser.svelte";
import Tabs from "../../pieces/Tabs.svelte";
import { fetchUser, fetchAllUsers, fetchAvatar } from "../../pieces/utils";
let user;
import { clickOutside } from '../../pieces/clickOutside'
import { push } from "svelte-spa-router";
let user;
let allUsers = [];
let myFriendships = [];
let requestsMade, requestsRecieved;
@@ -21,6 +25,11 @@
"Blocked Users",
];
let activeTabItem: string = "All Users";
let loadedUser;
let showModal = false;
onMount(async () => {
user = await fetchUser();
@@ -28,7 +37,7 @@
fetchAll();
});
const fetchAll = async () => {
const fetchAll = async () => {
// no need to await i think it can load in the background
fetchAllUsers_Wrapper();
fetchMyFriendships();
@@ -51,12 +60,14 @@
}
};
// it's more like fetch friendships
// it's more like fetch friendships
// then i need to extract the users
const fetchMyFriendships = async () => {
myFriendships = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/myfriends`)
.then((response) => {
if (!response.ok) {
if (response.status === 404)
return []
throw new Error("HTTP " + response.status);
}
return response.json();
@@ -71,6 +82,8 @@
requestsMade = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/pending`)
.then((response) => {
if (!response.ok) {
if (response.status === 404)
return []
throw new Error("HTTP " + response.status);
}
return response.json();
@@ -81,10 +94,12 @@
});
};
const fetchRequestsReceived = async () => {
const fetchRequestsReceived = async () => {
requestsRecieved = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/received`)
.then((response) => {
if (!response.ok) {
if (response.status === 404)
return []
throw new Error("HTTP " + response.status);
}
return response.json();
@@ -99,6 +114,8 @@
blockedUsers = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/blocked`)
.then((response) => {
if (!response.ok) {
if (response.status === 404)
return []
throw new Error("HTTP " + response.status);
}
return response.json();
@@ -110,22 +127,24 @@
};
/**** END OF MAIN FETCH ****/
// returns everything but BLOCKED
const fetchFriendshipFull = async (aUsername) => {
// returns everything but BLOCKED
const fetchFriendshipFull = async (aUsername) => {
friendshipStatusFull = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/myfriends?username=${aUsername}`)
.then((response) => {
if (!response.ok) {
if (response.status === 404)
return []
throw new Error("HTTP " + response.status);
}
}
return response.json();
})
.catch((error) => {
.catch((error) => {
console.log("catch fetchFriendshipFull: ", error);
return [];
});
};
const sendFriendRequest = async (aUsername) => {
const sendFriendRequest = async (aUsername) => {
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/relations`,
{
method: "POST",
@@ -152,9 +171,14 @@
const viewAUser = async (aUsername) => {
usernameBeingViewed = aUsername;
await fetchFriendshipFull(aUsername);
showModal = true;
};
const acceptFriendRequest = async (relationshipId) => {
const unViewAUser = () => {
showModal = false;
}
const acceptFriendRequest = async (relationshipId) => {
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/relations/${relationshipId}/accept`,
{
method: "PATCH",
@@ -174,7 +198,7 @@
activeTabItem = activeTabItem;
};
const declineFriendRequest = async (relationshipId) => {
const declineFriendRequest = async (relationshipId) => {
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/relations/${relationshipId}/decline`,
{
method: "PATCH",
@@ -194,7 +218,7 @@
activeTabItem = activeTabItem;
};
const unfriend = async (relationshipId) => {
const unfriend = async (relationshipId) => {
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/relations/${relationshipId}`,
{
method: "DELETE",
@@ -218,7 +242,7 @@
activeTabItem = activeTabItem;
};
const blockANonFriendUser = async (aUsername) => {
const blockANonFriendUser = async (aUsername) => {
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/relations`,
{
method: "POST",
@@ -245,7 +269,7 @@
activeTabItem = activeTabItem;
};
const blockAFriend = async (relationshipId) => {
const blockAFriend = async (relationshipId) => {
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/relations/${relationshipId}/block`,
{
method: "PATCH"
@@ -268,13 +292,14 @@
activeTabItem = activeTabItem;
};
const unblockAUser = async (relationshipId) => {
const unblockAUser = async (relationshipId) => {
// it's basically the same as unfriending someone cuz unfriending them means the relationship is deleted
await unfriend(relationshipId);
await fetchAll();
activeTabItem = activeTabItem;
};
const switchTab = async (e) => {
const switchTab = async (e) => {
activeTabItem = e.detail;
if (activeTabItem === "All Users") {
await fetchAllUsers_Wrapper();
@@ -286,79 +311,104 @@
await fetchBlockedUsers();
console.log("fetching blocked users");
}
if (usernameBeingViewed)
if (usernameBeingViewed) {
await fetchAllUsers_Wrapper();
fetchFriendshipFull(usernameBeingViewed);
}
};
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="background-pages">
<div class="top-grid">
<!-- let tabItems: string[] = ['All Users', 'My Friends', 'Friend Requests']
let activeTabItem: string = 'All Users'; -->
<div class="sidebar-list">
<Tabs items={tabItems} activeItem={activeTabItem} size="small" on:tabChange={switchTab}/>
<div class="sidebar-list background-pages">
<Tabs items={tabItems} activeItem={activeTabItem} size="default" on:tabChange={switchTab}/>
{#if activeTabItem === 'All Users' && allUsers}
<h3>{activeTabItem}</h3>
{#if Object.keys(allUsers).length === 0}
<div class="tip">You are alone on this platform...</div>
{/if}
<!-- does this work? -->
<!-- {#each allUsers as aUser (aUser.username)} -->
<!-- {#each allUsers as aUser (aUser.id)} -->
{#each allUsers as aUser}
<div class="sidebar-item" on:click={() => viewAUser(aUser.username)}>{aUser.username}</div>
<!-- i could make an indicator component? like green for connected or something?
i could use words but color them?
i could make it so if they're in a game -->
<div class="status sidebar-item">{aUser.status}</div>
<div class="user" on:click={() => viewAUser(aUser.username)}>
{#await fetchAvatar(aUser.username) then avatar}
<img class="avatar sidebar-item" src="{avatar}" alt="user avatar">
{:catch error}
<p class="error">Avatar was unable to load</p>
{/await}
<div class="sidebar-item">{aUser.username}</div>
<div class="status sidebar-item">{aUser.status}</div>
</div>
<br>
{/each}
{:else if activeTabItem === 'My Friends' && myFriendships}
<h3>{activeTabItem}</h3>
{#if Object.keys(myFriendships).length === 0}
<div class="tip">You don't have any Friends... Yet!</div>
{/if}
{#each myFriendships as aFriendship}
{#if aFriendship.senderUsername !== user.username}
<div class="sidebar-item" on:click={() => viewAUser(aFriendship.senderUsername)}>{aFriendship.senderUsername}</div>
{:else if aFriendship.receiverUsername !== user.username}
<div class="sidebar-item" on:click={() => viewAUser(aFriendship.receiverUsername)}>{aFriendship.receiverUsername}</div>
{/if}
{#if aFriendship.senderUsername !== user.username}
<div class="user" on:click={() => viewAUser(aFriendship.senderUsername)}>
{#await fetchAvatar(aFriendship.senderUsername) then avatar}
<img class="avatar sidebar-item" src="{avatar}" alt="user avatar">
{:catch error}
<p class="error">Avatar was unable to load</p>
{/await}
<div class="sidebar-item">{aFriendship.senderUsername}</div>
</div>
{:else if aFriendship.receiverUsername !== user.username}
<div class="user" on:click={() => viewAUser(aFriendship.receiverUsername)}>
{#await fetchAvatar(aFriendship.receiverUsername) then avatar}
<img class="avatar sidebar-item" src="{avatar}" alt="user avatar">
{:catch error}
<p class="error">Avatar was unable to load</p>
{/await}
<div class="sidebar-item">{aFriendship.receiverUsername}</div>
</div>
{/if}
<br>
{/each}
{:else if activeTabItem === 'Friend Requests' && requestsRecieved}
<h3>{activeTabItem}</h3>
{#if Object.keys(requestsRecieved).length === 0}
<div class="tip">You don't have any Friend Requests</div>
{/if}
{#each requestsRecieved as aUser}
<div class="sidebar-item" on:click={() => viewAUser(aUser.senderUsername)}>{aUser.senderUsername}</div>
<div class="status sidebar-item">{aUser.status}</div>
<div class="user" on:click={() => viewAUser(aUser.senderUsername)}>
{#await fetchAvatar(aUser.senderUsername) then avatar}
<img class="avatar sidebar-item" src="{avatar}" alt="user avatar">
{:catch error}
<p class="error">Avatar was unable to load</p>
{/await}
<div class="sidebar-item">{aUser.senderUsername}</div>
<div class="status sidebar-item">{aUser.status}</div>
</div>
<br>
{/each}
{:else if activeTabItem === 'Blocked Users' && blockedUsers}
<h3>{activeTabItem}</h3>
<!-- seems a little excessive... maybe a lighter way of doing this? doesn't seem like it, i hate it but at least only happens sometimes.default... -->
{#if Object.keys(blockedUsers).length === 0}
<div class="tip">You have not Blocked any Users</div>
{/if}
{#each blockedUsers as aUser}
<div class="sidebar-item" on:click={() => viewAUser(aUser.receiverUsername)}>{aUser.receiverUsername}</div>
<div class="status sidebar-item">{aUser.status}</div>
<div class="user" on:click={() => viewAUser(aUser.receiverUsername)}>
{#await fetchAvatar(aUser.receiverUsername) then avatar}
<img class="avatar sidebar-item" src="{avatar}" alt="user avatar">
{:catch error}
<p class="error">Avatar was unable to load</p>
{/await}
<div class="sidebar-item">{aUser.receiverUsername}</div>
<div class="status sidebar-item">{aUser.status}</div>
</div>
<br>
{/each}
{/if}
</div>
<div class="main-display">
{#if usernameBeingViewed}
<DisplayAUser aUsername={usernameBeingViewed}/>
{#if showModal && usernameBeingViewed}
<div class="backdrop"></div>
<div class="box background-pages" use:clickOutside on:outclick={() => unViewAUser()}>
<DisplayAUser aUsername={usernameBeingViewed} bind:loaded={loadedUser}/>
{#if loadedUser === true}
<div class="buttons-area">
{#if friendshipStatusFull && friendshipStatusFull.id}
{#if friendshipStatusFull.status === 'R'}
@@ -391,32 +441,17 @@
<Button on:click={() => blockANonFriendUser(usernameBeingViewed)}>Block User</Button>
{/if}
</div>
{:else}
<div class="placeholder">
<h1>Click on a user!</h1>
<h4>You'll see them displayed here</h4>
</div>
{/if}
{/if}
<button on:click={() => push(`/profile/users/${usernameBeingViewed}`)}>Profile Page</button>
<button on:click={() => unViewAUser()}>Close</button>
</div>
</div>
{/if}
</div>
<style>
/* ok so i want a 12 column grid with a sidebar */
div.top-grid{
display: grid;
grid-template-columns: repeat(8, 1fr);
/* max-height: calc(100vh - 30vh); */
height: 85vh;
/* margin: 0; */
}
div.sidebar-list{
grid-column: 1 / span 2;
div.sidebar-list{
background: #FB8B24;
padding: 1vw;
font-size: smaller;
@@ -428,17 +463,19 @@
border-right: 4px solid #071013;
border-bottom: 4px solid #071013;
overflow-wrap: break-word;
text-align: center;
}
div.sidebar-item{
/* yea i mean that seems fine... */
display: inline-block;
/* somehting about the buttons too, smaller ? */
text-align: center;
}
div.status{
font-size: 0.6em;
font-weight: bold;
}
/* selector attributes to get only divs with .a-user */
/* you gotta be careful with Svelte cuz it tacks classes on to the end of basically all elems! */
div[class^="sidebar-item"]:hover{
@@ -447,14 +484,33 @@
cursor: pointer;
}
div.main-display{
grid-column: 3 / span 10;
/* Modal Stuff */
.backdrop {
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
background: rgba(0,0,0,0.50)
}
div.placeholder{
color: white;
text-align: center;
}
.box {
--width: 70vw;
--height: 60vh;
position: absolute;
width: var(--width);
/* height: var(--height); */
height: auto;
left: calc(50% - var(--width) / 2);
top: calc(50% - var(--height) / 2);
align-items: center;
padding: 8px;
border: 2px solid white;
border-radius: 7px;
text-align: center;
font-weight: bold;
}
div.buttons-area{
text-align: center;
@@ -466,10 +522,22 @@
font-weight: bold;
}
img.avatar {
width: 60px;
}
.error{
font-size: 0.8em;
div.user {
cursor: pointer;
}
div.tile {
margin: 5px;
}
.error{
font-size: 0.5em;
font-weight: bold;
color: red;
color: rgb(152, 20, 20);
}
</style>

View File

@@ -7,8 +7,6 @@
background: white;
padding: 20px;
border-radius: 6px;
/* this softens the corners */
box-shadow: 0px 2px 4px rgba(0,0,0,0.1);
}
</style>

View File

@@ -4,14 +4,28 @@
import GenerateUserDisplay from './GenerateUserDisplay.svelte';
import { fetchUser } from "../pieces/utils";
export let aUsername;
let aUser;
export let aUsername;
export let loaded = false;
let aUser;
onMount( async() => {
loaded = false;
aUser = await fetchUser(aUsername);
})
$: aUsername, fetchUser(aUsername);
const updateUser = async(aUsername) => {
loaded = false;
aUser = await fetchUser(aUsername);
};
$: aUsername, updateUser(aUsername);
$: {
if (!aUser)
loaded = false;
else
loaded = true;
}
</script>
@@ -23,3 +37,13 @@
<div>Failed to load user {aUsername}</div>
{/if}
</div>
<style>
h2 {
text-align: center;
}
div {
text-align: center;
}
</style>

View File

@@ -1,35 +1,17 @@
<script lang="ts">
import { onMount } from 'svelte';
import { fetchAvatar } from "./utils";
import { fetchAvatar } from "./utils.js";
export let user;
let rank = '';
let avatar;
// avatar needs to be updated!!!
console.log('Generate User Display, BEFORE on mount ' + avatar)
console.log('user:')
console.log({...user})
// console.log(user)
let errors = {avatar: ''};
onMount( async() => {
avatar = await fetchAvatar(user.username);
})
/**** THIS IS BASICALLY ALL THE RANK LOGIC ERIC HAS MADE ****/
if (user.loseGame > user.winGame) {
rank = 'Bitch Ass Loser!'
} else if (user.loseGame === user.winGame) {
if (user.stats.loseGame > user.stats.winGame) {
rank = "Come on, you can do better"
} else if (user.stats.loseGame === user.stats.winGame) {
rank = 'Fine i guess...'
} else {
rank = 'Yea you da Boss!'
rank = 'You da Boss!'
}
// Glittery Stars and such for Rank
let index = 0, interval = 1000;
@@ -60,14 +42,15 @@
</script>
<!-- is this if excessive? -->
<div class="outer">
{#if user}
<main>
<!-- <img class="icon" src="img/default_user_icon.png" alt="default user icon"> -->
<!-- <img class="icon" src="{user.image_url}" alt="default user icon"> -->
<img class="avatar" src="{avatar}" alt="user avatar">
<div class="error">{errors.avatar}</div>
{#await fetchAvatar(user.username) then avatar}
<img class="avatar" src="{avatar}" alt="user avatar">
{:catch error}
<p class="errorA">Avatar was unable to load</p>
{/await}
<div class="username">{user.username}</div>
<div class="rank">Rank:
<span class="glitter">
@@ -91,7 +74,7 @@
</div>
<section class="main-stats">
<h4>Match Statistics</h4>
<p>Victories: {user.stats.winGame}</p>
<p>Wins: {user.stats.winGame}</p>
<p>Losses: {user.stats.loseGame}</p>
<p>Draws: {user.stats.drawGame}</p>
<p class="highlight">Total: {user.stats.totalGame}</p>
@@ -107,19 +90,23 @@
div.outer{
max-width: 960px;
margin: 40px auto;
margin-top: 1vw;
}
/* The main part */
main{
max-width: 960px;
margin: 40px auto;
margin-top: 1vw;
text-align: center;
}
/* Normal CSS stuff */
.avatar{
max-width: 150px;
/* padding: 5px; */
width: 15vw;
height: 15vw;
max-width: 130px;
max-height: 130px;
margin-bottom: 1vw;
}
/* The variable rich section */
@@ -127,10 +114,8 @@
max-width: 600px;
margin: 40px auto;
text-align: center;
/* i think i want to use a grid? */
display: grid;
grid-template-columns: repeat(3, 1fr);
/* not sure about this, maybe top should be larger? */
grid-template-rows: repeat(3, 1fr);
}
@@ -152,10 +137,10 @@
font-weight: bold;
}
.error{
font-size: 1vw;
.errorA{
font-size: 0.5em;
font-weight: bold;
color: red;
color: rgb(152, 20, 20);
}
.highlight {
@@ -180,8 +165,6 @@
}
/* Glittery Star Stuff */

View File

@@ -2,11 +2,6 @@
import { push } from "svelte-spa-router";
import { location } from 'svelte-spa-router';
// no need, it's just for links
import active from 'svelte-spa-router/active'
// or i could leave them all and not display if they're active?
$: current = $location;
let handleClickLogout = async () =>
@@ -100,4 +95,10 @@
max-width: 10%;
float: left;
}
button:hover{
cursor: pointer;
background-color: rgb(0, 166, 255) !important;
}
</style>

View File

@@ -2,9 +2,18 @@
import type { MatchOptions } from "../pages/game/client/pongSpectator";
export { MatchOptions } from "../pages/game/client/pongSpectator";
export class Match {
export class MatchOngoing {
gameServerIdOfTheMatch: string;
gameOptions: MatchOptions;
playerOneUsername: string;
playerTwoUsername: string;
}
export class MatchHistory {
id: number;
date: Date;
playerOneUsername: string;
playerTwoUsername: string;
playerOneResult: number;
playerTwoResult: number;
}

View File

@@ -0,0 +1,162 @@
<script lang="ts">
import { onMount, onDestroy } from "svelte";
import { fetchAvatar } from "./utils.js";
import type { MatchHistory } from "./Match";
export let user;
let matchList: MatchHistory[] = [];
onMount( async() => {
matchList = await fetchMatchList(user.username);
console.log(matchList);
// matchList = await mockMatchList(user.username);
})
async function fetchMatchList(username: string)
{
let url = `http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/match/history?username=${username}`;
return fetch(url)
.then((response) => {
if (!response.ok) {
throw new Error("HTTP " + response.status);
}
return response.json();
})
.catch((error) => {
console.log("catch fetchMatchList: ", error);
return [];
});
}
async function mockMatchList(username?: string)
{
return [
{
playerOneUsername: "hulamy",
playerTwoUsername: "lperrey",
playerOneResult: 5,
playerTwoResult: 11,
date: new Date()
},
{
playerOneUsername: "hulamy",
playerTwoUsername: "lperrey",
playerOneResult: 14,
playerTwoResult: 12,
date: new Date()
},
{
playerOneUsername: "hulamy",
playerTwoUsername: "lperrey",
playerOneResult: 0,
playerTwoResult: 0,
date: new Date()
},
{
playerOneUsername: "hulamy",
playerTwoUsername: "lperrey",
playerOneResult: 3,
playerTwoResult: 0,
date: new Date()
},
]
}
</script>
<br />
<div class="background-pages">
<div class="principal-div">
<table class="stats-table">
<thead>
<tr>
<th>#</th>
<th>Player One</th>
<th></th>
<th>VS</th>
<th></th>
<th>Player Two</th>
<th>Result</th>
</tr>
</thead>
<tbody>
{#each matchList as match, i}
<tr>
<th>{i + 1}</th>
{#await fetchAvatar(match.playerOneUsername) then avatar}
<td>
<img class="avatar" src="{avatar}" alt="avatarOne">
<br>
{match.playerOneUsername}
</td>
{/await}
<td>{match.playerOneResult}</td>
<td>VS</td>
<td>{match.playerTwoResult}</td>
{#await fetchAvatar(match.playerTwoUsername) then avatar}
<td>
<img class="avatar" src="{avatar}" alt="avatarTwo">
<br>
{match.playerTwoUsername}
</td>
{/await}
{#if match.playerOneResult === match.playerTwoResult}
<th>DRAW</th>
{:else if (user.username === match.playerOneUsername && match.playerOneResult > match.playerTwoResult)
|| (user.username === match.playerTwoUsername && match.playerTwoResult > match.playerOneResult)}
<th>WIN</th>
{:else}
<th>LOSE</th>
{/if}
</tr>
{/each}
</tbody>
</table>
</div>
</div>
<style>
.avatar {
width: 3vw;
height: 3vw;
}
.principal-div {
display: flex;
justify-content: center;
align-items: center;
}
.stats-table {
margin-left: auto;
margin-right: auto;
font-size: 1vw;
min-width: 400px;
}
.stats-table thead tr {
background-color: #618174;
color: #ffffff;
}
.stats-table th,
.stats-table td {
padding: 12px 15px;
size: 10vw;
max-width: 10vw;
overflow-wrap: break-word;
}
.stats-table tbody tr {
border-bottom: 1px solid #dddddd;
}
.stats-table tbody tr:nth-of-type(even) {
background-color: #555;
}
.stats-table tbody tr:last-of-type {
border-bottom: 2px solid #618174;
}
</style>

View File

@@ -1,38 +0,0 @@
<script lang="ts">
import { onMount, onDestroy } from "svelte";
import { Match, MatchOptions} from "./Match";
export let match: Match;
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>

View File

@@ -0,0 +1,56 @@
<script lang="ts">
import { onMount, onDestroy } from "svelte";
import { MatchOngoing, MatchOptions} from "./Match";
import { fetchAvatar } from "./utils.js";
export let match: MatchOngoing;
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>
<!-- -->
<div>
{#await fetchAvatar(match.playerOneUsername) then avatar}
<img class="avatar" src="{avatar}" alt="avatarOne">
{/await}
<button on:click class="match_elem">
'{match.playerOneUsername}' VS '{match.playerTwoUsername}'
<br/>
[{matchOptionsString}]
</button>
{#await fetchAvatar(match.playerTwoUsername) then avatar}
<img class="avatar" src="{avatar}" alt="avatarTwo">
{/await}
</div>
<!-- -->
<style>
.avatar {
width: 3vw;
height: 3vw;
margin-bottom: -1vw;
}
.match_elem {
margin: 1vw;
}
</style>

View File

@@ -1,40 +0,0 @@
<script lang="ts">
// trying something...
// const element = document.getElementById("bottom-half");
export let element;
// is this too JS for me?
const scrollTo= () => {
element.scrollIntoView({behavior: "smooth"});
console.log('supposed to scroll');
// Maybe eventually i can do this in a more svelte way, on:scroll={someEvent} ... whatever
};
</script>
<!-- <svelte:window bind:scrollY={y}/> -->
<!-- wait actually i don't want this... -->
<!-- <svelte:window on:scroll={handleOnScroll} /> -->
<!-- a div instead? -->
<!-- <button>Login</button> -->
<div class="to-login" on:click={scrollTo}>Login</div>
<style>
.to-login{
display: inline-block;
/* color: bisque; */
color: blue;
margin-left: 10px;
}
.to-login:hover{
font-weight: bold;
color: black;
cursor: pointer;
}
</style>

View File

@@ -3,7 +3,7 @@
export let items;
export let activeItem;
export let size = 'medium';
// big, medium, small
// big, medium, small, default (responsive)
const dispatch = createEventDispatcher();
@@ -11,11 +11,9 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="tabs" class:size={size}>
<!-- creates a list, can be done other ways -->
<ul>
{#each items as item}
<li class={size} on:click={() => dispatch('tabChange', item)}>
<!-- the class active is attributed if the condition is met -->
<div class:active={activeItem === item}>{item}</div>
</li>
{/each}
@@ -23,12 +21,9 @@
</div>
<style>
/* .tabs{
margin-bottom: 40px;
} */
.tab.big{
margin-bottom: 50px;
/* guessing at size */
}
.tab.medium{
margin-bottom: 40px;
@@ -36,7 +31,6 @@
}
.tab.small{
margin-bottom: 10px;
/* need it small */
}
ul{
display: flex;
@@ -45,15 +39,12 @@
list-style-type: none;
}
li{
/* margin: 0 16px; */
/* font-size: 18px; */
color: #555;
cursor: pointer;
}
li.big{
margin: 0 20px;
font-size: 22px;
/* guessing at size */
}
li.medium{
margin: 0 16px;
@@ -63,13 +54,36 @@
li.small{
margin: 8px;
font-size: 10px;
/* need it small */
}
.active{
color: #d91b42;
border-bottom: 2px solid #d91b42;
padding-bottom: 8px;
}
li div:hover {
color: rgb(0, 166, 255) !important;
}
/* default is medium */
.tab.default {
margin-bottom: 40px;
}
li.default{
margin: 0 16px;
font-size: 18px;
}
@media screen and (max-width: 700px) {
/* they turn .small */
.tab.default {
margin-bottom: 10px;
}
li.default {
margin: 8px;
font-size: 10px;
}
}
</style>

View File

@@ -0,0 +1,15 @@
export function clickOutside(node) {
const handleClick = (event) => {
if (!node.contains(event.target)) {
node.dispatchEvent(new CustomEvent("outclick"));
}
};
document.addEventListener("click", handleClick, true);
return {
destroy() {
document.removeEventListener("click", handleClick, true);
}
};
}

View File

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

View File

@@ -47,6 +47,8 @@ export async function fetchAllUsers()
return fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/all`)
.then((response) => {
if (!response.ok) {
if (response.status === 404)
return [];
throw new Error("HTTP " + response.status);
}
return response.json();

View File

@@ -5,8 +5,6 @@ import ProfileSettings from '../pages/profile/ProfileSettings.svelte';
import ProfileUsers from '../pages/profile/ProfileUsers.svelte';
import ProfileDisplayOneUser from "../pages/profile/ProfileDisplayOneUser.svelte";
import DisplayAUser from '../pieces/DisplayAUser.svelte';
// establishing the prefix here very clearly so we can have a coherent repeatable structure
export const prefix = '/profile';
@@ -15,6 +13,5 @@ export const profileRoutes = {
'/settings': ProfileSettings,
'/users': ProfileUsers,
'/users/:username': ProfileDisplayOneUser,
// '/users/:username': DisplayAUser,
'*': NotFound
};