narnaud
2 years ago
39 changed files with 1702 additions and 1209 deletions
@ -0,0 +1,3 @@ |
|||||
|
POSTGRES_USER: postgres_usr |
||||
|
POSTGRES_PASSWORD: postgres_pw |
||||
|
POSTGRES_DB: transcendence |
@ -1,5 +1,23 @@ |
|||||
node_modules |
* |
||||
package-lock.json |
|
||||
yarn.lock |
!.gitignore |
||||
|
!LICENCE |
||||
|
!README.md |
||||
|
|
||||
|
!Makefile |
||||
|
!docker-compose.yml |
||||
|
!.env |
||||
|
|
||||
|
!cont/ |
||||
|
!cont/front |
||||
|
!cont/back |
||||
|
!cont/front/* |
||||
|
!cont/back/* |
||||
|
|
||||
|
|
||||
|
!volumes/ |
||||
|
!volumes/front |
||||
|
!volumes/back |
||||
|
!volumes/front/* |
||||
|
!volumes/back/* |
||||
|
|
||||
public |
|
@ -1,8 +0,0 @@ |
|||||
{ |
|
||||
"singleQuote": true, |
|
||||
"trailingComma": "none", |
|
||||
"semi": true, |
|
||||
"printWidth": 120, |
|
||||
"tabWidth": 4, |
|
||||
"useTabs": true |
|
||||
} |
|
@ -0,0 +1,30 @@ |
|||||
|
NAME = transcendence |
||||
|
USER = gavaniwast |
||||
|
|
||||
|
CERT = volumes/ssl/$(NAME).pem |
||||
|
|
||||
|
all: clean start |
||||
|
|
||||
|
$(CERT): |
||||
|
openssl req -newkey rsa:4096 -x509 -sha256 -days 365 -nodes \
|
||||
|
-out $(NAME).pem -keyout $(NAME).pem \
|
||||
|
-subj "/C=FR/ST=Nice/L=Nice/O=42/OU=student/CN=$(NAME)_$(USER)/" |
||||
|
mkdir -p volumes/ssl |
||||
|
mv $(NAME).pem volumes/ssl |
||||
|
|
||||
|
start: $(CERT) |
||||
|
sudo docker compose -f docker-compose.yml up --build |
||||
|
|
||||
|
debug: $(CERT) |
||||
|
sudo BUILDKIT_PROGRESS=plain docker compose -f docker-compose.yml up --build |
||||
|
|
||||
|
stop: |
||||
|
sudo docker compose -f docker-compose.yml down |
||||
|
|
||||
|
clean: stop |
||||
|
sudo docker system prune -f |
||||
|
|
||||
|
fclean: stop |
||||
|
sudo docker system prune -af --volumes |
||||
|
|
||||
|
re: fclean start |
@ -0,0 +1,6 @@ |
|||||
|
FROM alpine:3.15 |
||||
|
|
||||
|
RUN apk update && apk upgrade && apk add --no-cache npm \ |
||||
|
&& npm install -g @nestjs/cli |
||||
|
|
||||
|
WORKDIR /var/html/ |
@ -0,0 +1,8 @@ |
|||||
|
FROM alpine:3.15 |
||||
|
|
||||
|
RUN apk update && apk upgrade && apk add npm nginx |
||||
|
|
||||
|
RUN mkdir -p /var/ssl |
||||
|
|
||||
|
WORKDIR /var/www/html |
||||
|
ENTRYPOINT npm install && npm build && npm preview |
@ -0,0 +1,31 @@ |
|||||
|
version: '3.8' |
||||
|
|
||||
|
networks: |
||||
|
transcendence: |
||||
|
|
||||
|
services: |
||||
|
front: |
||||
|
container_name: front |
||||
|
build: cont/front/ |
||||
|
env_file: .env |
||||
|
depends_on: [postgres, back] |
||||
|
ports: [80:80] |
||||
|
volumes: [./volumes/front:/var/www/html] |
||||
|
networks: [transcendence] |
||||
|
restart: always |
||||
|
back: |
||||
|
container_name: back |
||||
|
build: cont/back/ |
||||
|
env_file: .env |
||||
|
depends_on: [postgres] |
||||
|
ports: [3030:3030] |
||||
|
networks: [transcendence] |
||||
|
volumes: [./volumes/back:/var/www/html] |
||||
|
restart: always |
||||
|
postgres: |
||||
|
container_name: postgres |
||||
|
image: postgres |
||||
|
ports: [5432:5432] |
||||
|
restart: always |
||||
|
env_file: .env |
||||
|
|
@ -1,28 +0,0 @@ |
|||||
{ |
|
||||
"name": "svelte-app", |
|
||||
"version": "1.0.0", |
|
||||
"private": true, |
|
||||
"scripts": { |
|
||||
"build": "rollup -c", |
|
||||
"dev": "rollup -c -w", |
|
||||
"start": "sirv public --no-clear" |
|
||||
}, |
|
||||
"devDependencies": { |
|
||||
"@rollup/plugin-commonjs": "^17.0.0", |
|
||||
"@rollup/plugin-node-resolve": "^11.0.0", |
|
||||
"@tsconfig/svelte": "^3.0.0", |
|
||||
"rollup": "^2.3.4", |
|
||||
"rollup-plugin-css-only": "^3.1.0", |
|
||||
"rollup-plugin-livereload": "^2.0.0", |
|
||||
"rollup-plugin-svelte": "^7.0.0", |
|
||||
"rollup-plugin-terser": "^7.0.0", |
|
||||
"svelte": "^3.0.0" |
|
||||
}, |
|
||||
"dependencies": { |
|
||||
"@rollup/plugin-typescript": "^11.0.0", |
|
||||
"sirv-cli": "^2.0.0", |
|
||||
"svelte-check": "^3.0.3", |
|
||||
"svelte-preprocess": "^5.0.1", |
|
||||
"svelte-routing": "^1.6.0" |
|
||||
} |
|
||||
} |
|
@ -1,63 +0,0 @@ |
|||||
html, body { |
|
||||
position: relative; |
|
||||
width: 100%; |
|
||||
height: 100%; |
|
||||
} |
|
||||
|
|
||||
body { |
|
||||
color: #333; |
|
||||
margin: 0; |
|
||||
padding: 8px; |
|
||||
box-sizing: border-box; |
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; |
|
||||
} |
|
||||
|
|
||||
a { |
|
||||
color: rgb(0,100,200); |
|
||||
text-decoration: none; |
|
||||
} |
|
||||
|
|
||||
a:hover { |
|
||||
text-decoration: underline; |
|
||||
} |
|
||||
|
|
||||
a:visited { |
|
||||
color: rgb(0,80,160); |
|
||||
} |
|
||||
|
|
||||
label { |
|
||||
display: block; |
|
||||
} |
|
||||
|
|
||||
input, button, select, textarea { |
|
||||
font-family: inherit; |
|
||||
font-size: inherit; |
|
||||
-webkit-padding: 0.4em 0; |
|
||||
padding: 0.4em; |
|
||||
margin: 0 0 0.5em 0; |
|
||||
box-sizing: border-box; |
|
||||
border: 1px solid #ccc; |
|
||||
border-radius: 2px; |
|
||||
} |
|
||||
|
|
||||
input:disabled { |
|
||||
color: #ccc; |
|
||||
} |
|
||||
|
|
||||
button { |
|
||||
color: #333; |
|
||||
background-color: #f4f4f4; |
|
||||
outline: none; |
|
||||
} |
|
||||
|
|
||||
button:disabled { |
|
||||
color: #999; |
|
||||
} |
|
||||
|
|
||||
button:not(:disabled):active { |
|
||||
background-color: #ddd; |
|
||||
} |
|
||||
|
|
||||
button:focus { |
|
||||
border-color: #666; |
|
||||
} |
|
Before Width: | Height: | Size: 9.1 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 9.0 KiB |
@ -1,17 +0,0 @@ |
|||||
<!DOCTYPE html> |
|
||||
<html lang="en"> |
|
||||
<head> |
|
||||
<meta charset='utf-8'> |
|
||||
<meta name='viewport' content='width=device-width,initial-scale=1'> |
|
||||
|
|
||||
<title>Pong</title> |
|
||||
|
|
||||
<link rel='icon' type='image/png' href='/img/pog.jpg'> |
|
||||
<link rel='stylesheet' href='/global.css'> |
|
||||
<link rel='stylesheet' href='/build/bundle.css'> |
|
||||
<script defer src='/build/bundle.js'></script> |
|
||||
</head> |
|
||||
|
|
||||
<body> |
|
||||
</body> |
|
||||
</html> |
|
@ -1,84 +0,0 @@ |
|||||
import svelte from "rollup-plugin-svelte"; |
|
||||
import commonjs from "@rollup/plugin-commonjs"; |
|
||||
import resolve from "@rollup/plugin-node-resolve"; |
|
||||
import livereload from "rollup-plugin-livereload"; |
|
||||
import { terser } from "rollup-plugin-terser"; |
|
||||
import css from "rollup-plugin-css-only"; |
|
||||
import autoPreprocess from "svelte-preprocess"; |
|
||||
import typescript from "@rollup/plugin-typescript"; |
|
||||
|
|
||||
const production = !process.env.ROLLUP_WATCH; |
|
||||
|
|
||||
function serve() { |
|
||||
let server; |
|
||||
|
|
||||
function toExit() { |
|
||||
if (server) server.kill(0); |
|
||||
} |
|
||||
|
|
||||
return { |
|
||||
writeBundle() { |
|
||||
if (server) return; |
|
||||
server = require("child_process").spawn( |
|
||||
"npm", |
|
||||
["run", "start", "--", "--dev"], |
|
||||
{ |
|
||||
stdio: ["ignore", "inherit", "inherit"], |
|
||||
shell: true, |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
process.on("SIGTERM", toExit); |
|
||||
process.on("exit", toExit); |
|
||||
}, |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
export default { |
|
||||
input: "src/main.ts", |
|
||||
output: { |
|
||||
sourcemap: true, |
|
||||
format: "iife", |
|
||||
name: "app", |
|
||||
file: "public/build/bundle.js", |
|
||||
}, |
|
||||
plugins: [ |
|
||||
svelte({ |
|
||||
preprocess: autoPreprocess(), |
|
||||
compilerOptions: { |
|
||||
// enable run-time checks when not in production
|
|
||||
dev: !production, |
|
||||
}, |
|
||||
}), |
|
||||
typescript({ sourceMap: !production }), |
|
||||
// we'll extract any component CSS out into
|
|
||||
// a separate file - better for performance
|
|
||||
css({ output: "bundle.css" }), |
|
||||
|
|
||||
// If you have external dependencies installed from
|
|
||||
// npm, you'll most likely need these plugins. In
|
|
||||
// some cases you'll need additional configuration -
|
|
||||
// consult the documentation for details:
|
|
||||
// https://github.com/rollup/plugins/tree/master/packages/commonjs
|
|
||||
resolve({ |
|
||||
browser: true, |
|
||||
dedupe: ["svelte"], |
|
||||
}), |
|
||||
commonjs(), |
|
||||
|
|
||||
// In dev mode, call `npm run start` once
|
|
||||
// the bundle has been generated
|
|
||||
!production && serve(), |
|
||||
|
|
||||
// Watch the `public` directory and refresh the
|
|
||||
// browser on changes when not in production
|
|
||||
!production && livereload("public"), |
|
||||
|
|
||||
// If we're building for production (npm run build
|
|
||||
// instead of npm run dev), minify
|
|
||||
production && terser(), |
|
||||
], |
|
||||
watch: { |
|
||||
clearScreen: false, |
|
||||
}, |
|
||||
}; |
|
@ -1,121 +0,0 @@ |
|||||
// @ts-check
|
|
||||
|
|
||||
/** This script modifies the project to support TS code in .svelte files like: |
|
||||
|
|
||||
<script lang="ts"> |
|
||||
export let name: string; |
|
||||
</script> |
|
||||
|
|
||||
As well as validating the code for CI. |
|
||||
*/ |
|
||||
|
|
||||
/** To work on this script: |
|
||||
rm -rf test-template template && git clone sveltejs/template test-template && node scripts/setupTypeScript.js test-template |
|
||||
*/ |
|
||||
|
|
||||
const fs = require("fs") |
|
||||
const path = require("path") |
|
||||
const { argv } = require("process") |
|
||||
|
|
||||
const projectRoot = argv[2] || path.join(__dirname, "..") |
|
||||
|
|
||||
// Add deps to pkg.json
|
|
||||
const packageJSON = JSON.parse(fs.readFileSync(path.join(projectRoot, "package.json"), "utf8")) |
|
||||
packageJSON.devDependencies = Object.assign(packageJSON.devDependencies, { |
|
||||
"svelte-check": "^2.0.0", |
|
||||
"svelte-preprocess": "^4.0.0", |
|
||||
"@rollup/plugin-typescript": "^8.0.0", |
|
||||
"typescript": "^4.0.0", |
|
||||
"tslib": "^2.0.0", |
|
||||
"@tsconfig/svelte": "^2.0.0" |
|
||||
}) |
|
||||
|
|
||||
// Add script for checking
|
|
||||
packageJSON.scripts = Object.assign(packageJSON.scripts, { |
|
||||
"check": "svelte-check --tsconfig ./tsconfig.json" |
|
||||
}) |
|
||||
|
|
||||
// Write the package JSON
|
|
||||
fs.writeFileSync(path.join(projectRoot, "package.json"), JSON.stringify(packageJSON, null, " ")) |
|
||||
|
|
||||
// mv src/main.js to main.ts - note, we need to edit rollup.config.js for this too
|
|
||||
const beforeMainJSPath = path.join(projectRoot, "src", "main.js") |
|
||||
const afterMainTSPath = path.join(projectRoot, "src", "main.ts") |
|
||||
fs.renameSync(beforeMainJSPath, afterMainTSPath) |
|
||||
|
|
||||
// Switch the app.svelte file to use TS
|
|
||||
const appSveltePath = path.join(projectRoot, "src", "App.svelte") |
|
||||
let appFile = fs.readFileSync(appSveltePath, "utf8") |
|
||||
appFile = appFile.replace("<script>", '<script lang="ts">') |
|
||||
appFile = appFile.replace("export let name;", 'export let name: string;') |
|
||||
fs.writeFileSync(appSveltePath, appFile) |
|
||||
|
|
||||
// Edit rollup config
|
|
||||
const rollupConfigPath = path.join(projectRoot, "rollup.config.js") |
|
||||
let rollupConfig = fs.readFileSync(rollupConfigPath, "utf8") |
|
||||
|
|
||||
// Edit imports
|
|
||||
rollupConfig = rollupConfig.replace(`'rollup-plugin-terser';`, `'rollup-plugin-terser';
|
|
||||
import sveltePreprocess from 'svelte-preprocess'; |
|
||||
import typescript from '@rollup/plugin-typescript';`)
|
|
||||
|
|
||||
// Replace name of entry point
|
|
||||
rollupConfig = rollupConfig.replace(`'src/main.js'`, `'src/main.ts'`) |
|
||||
|
|
||||
// Add preprocessor
|
|
||||
rollupConfig = rollupConfig.replace( |
|
||||
'compilerOptions:', |
|
||||
'preprocess: sveltePreprocess({ sourceMap: !production }),\n\t\t\tcompilerOptions:' |
|
||||
); |
|
||||
|
|
||||
// Add TypeScript
|
|
||||
rollupConfig = rollupConfig.replace( |
|
||||
'commonjs(),', |
|
||||
'commonjs(),\n\t\ttypescript({\n\t\t\tsourceMap: !production,\n\t\t\tinlineSources: !production\n\t\t}),' |
|
||||
); |
|
||||
fs.writeFileSync(rollupConfigPath, rollupConfig) |
|
||||
|
|
||||
// Add TSConfig
|
|
||||
const tsconfig = `{
|
|
||||
"extends": "@tsconfig/svelte/tsconfig.json", |
|
||||
|
|
||||
"include": ["src/**/*"], |
|
||||
"exclude": ["node_modules/*", "__sapper__/*", "public/*"] |
|
||||
}` |
|
||||
const tsconfigPath = path.join(projectRoot, "tsconfig.json") |
|
||||
fs.writeFileSync(tsconfigPath, tsconfig) |
|
||||
|
|
||||
// Add global.d.ts
|
|
||||
const dtsPath = path.join(projectRoot, "src", "global.d.ts") |
|
||||
fs.writeFileSync(dtsPath, `/// <reference types="svelte" />`) |
|
||||
|
|
||||
// Delete this script, but not during testing
|
|
||||
if (!argv[2]) { |
|
||||
// Remove the script
|
|
||||
fs.unlinkSync(path.join(__filename)) |
|
||||
|
|
||||
// Check for Mac's DS_store file, and if it's the only one left remove it
|
|
||||
const remainingFiles = fs.readdirSync(path.join(__dirname)) |
|
||||
if (remainingFiles.length === 1 && remainingFiles[0] === '.DS_store') { |
|
||||
fs.unlinkSync(path.join(__dirname, '.DS_store')) |
|
||||
} |
|
||||
|
|
||||
// Check if the scripts folder is empty
|
|
||||
if (fs.readdirSync(path.join(__dirname)).length === 0) { |
|
||||
// Remove the scripts folder
|
|
||||
fs.rmdirSync(path.join(__dirname)) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Adds the extension recommendation
|
|
||||
fs.mkdirSync(path.join(projectRoot, ".vscode"), { recursive: true }) |
|
||||
fs.writeFileSync(path.join(projectRoot, ".vscode", "extensions.json"), `{
|
|
||||
"recommendations": ["svelte.svelte-vscode"] |
|
||||
} |
|
||||
`)
|
|
||||
|
|
||||
console.log("Converted to TypeScript.") |
|
||||
|
|
||||
if (fs.existsSync(path.join(projectRoot, "node_modules"))) { |
|
||||
console.log("\nYou will need to re-run your dependency manager to get started.") |
|
||||
} |
|
@ -1,82 +0,0 @@ |
|||||
<script lang="ts"> |
|
||||
import Navbar from './components/NavBar.svelte'; |
|
||||
import Profile from './components/Profile.svelte'; |
|
||||
import MatchHistory from './components/MatchHistory.svelte'; |
|
||||
import type { Match } from './components/MatchHistory.svelte'; |
|
||||
import Friends from './components/Friends.svelte'; |
|
||||
import type { Friend } from './components/Friends.svelte'; |
|
||||
import Spectate from './components/Spectate.svelte'; |
|
||||
import type { SpectateType } from './components/Spectate.svelte'; |
|
||||
import Play from './components/Play.svelte'; |
|
||||
import Pong from './components/Pong/Pong.svelte'; |
|
||||
|
|
||||
let isProfileOpen = false; |
|
||||
function clickProfile() { |
|
||||
isProfileOpen = true; |
|
||||
} |
|
||||
let isHistoryOpen = false; |
|
||||
function clickHistory() { |
|
||||
isHistoryOpen = true; |
|
||||
} |
|
||||
let matches: Array<Match> = [ |
|
||||
{ winner: 'Alice', loser: 'Bob', points: -5, rank: '22' }, |
|
||||
{ winner: 'Alice', loser: 'Bob', points: 10, rank: '24' }, |
|
||||
{ winner: 'Alice', loser: 'Bob', points: 10, rank: '24' }, |
|
||||
{ winner: 'Alice', loser: 'Bob', points: 7, rank: '23' }, |
|
||||
{ winner: 'Alice', loser: 'Bob', points: 10, rank: '24' }, |
|
||||
{ winner: 'Alice', loser: 'Bob', points: 10, rank: '24' } |
|
||||
]; |
|
||||
let isFriendOpen = false; |
|
||||
function clickFriends() { |
|
||||
isFriendOpen = true; |
|
||||
} |
|
||||
let friends: Array<Friend> = [ |
|
||||
{ username: 'Alice', status: 'online' }, |
|
||||
{ username: 'Bob', status: 'online' }, |
|
||||
{ username: 'Charlie', status: 'offline' }, |
|
||||
{ username: 'Dave', status: 'offline' }, |
|
||||
{ username: 'Eve', status: 'in a game' }, |
|
||||
{ username: 'Frank', status: 'online' } |
|
||||
]; |
|
||||
let isSpectateOpen = false; |
|
||||
function clickSpectate() { |
|
||||
isSpectateOpen = true; |
|
||||
} |
|
||||
let spectate: Array<SpectateType> = [ |
|
||||
{ player1: 'Alice', player2: 'Bob', id: '1' }, |
|
||||
{ player1: 'Alice', player2: 'Bob', id: '4' }, |
|
||||
{ player1: 'Alice', player2: 'Bob', id: '6' }, |
|
||||
{ player1: 'Alice', player2: 'Bob', id: '8' }, |
|
||||
{ player1: 'Alice', player2: 'Bob', id: '2' }, |
|
||||
{ player1: 'Alice', player2: 'Bob', id: '3' } |
|
||||
]; |
|
||||
</script> |
|
||||
|
|
||||
<main> |
|
||||
<Navbar {clickProfile} {clickHistory} {clickFriends} {clickSpectate} /> |
|
||||
{#if isSpectateOpen} |
|
||||
<div on:click={() => (isSpectateOpen = false)} on:keydown={() => (isSpectateOpen = false)}> |
|
||||
<Spectate {spectate} /> |
|
||||
</div> |
|
||||
{/if} |
|
||||
{#if isFriendOpen} |
|
||||
<div on:click={() => (isFriendOpen = false)} on:keydown={() => (isFriendOpen = false)}> |
|
||||
<Friends {friends} /> |
|
||||
</div> |
|
||||
{/if} |
|
||||
{#if isHistoryOpen} |
|
||||
<div on:click={() => (isHistoryOpen = false)} on:keydown={() => (isHistoryOpen = false)}> |
|
||||
<MatchHistory {matches} /> |
|
||||
</div> |
|
||||
{/if} |
|
||||
{#if isProfileOpen} |
|
||||
<div on:click={() => (isProfileOpen = false)} on:keydown={() => (isProfileOpen = false)}> |
|
||||
<Profile username="Alice" wins={10} losses={5} elo={256} rank={23} is2faEnabled={false} /> |
|
||||
</div> |
|
||||
{/if} |
|
||||
<Play /> |
|
||||
<Pong /> |
|
||||
</main> |
|
||||
|
|
||||
<style> |
|
||||
</style> |
|
@ -1,80 +0,0 @@ |
|||||
<script lang="ts" context="module"> |
|
||||
export interface Friend { |
|
||||
username: string; |
|
||||
status: 'online' | 'offline' | 'in a game'; |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<script lang="ts"> |
|
||||
export let friends: Array<Friend> = []; |
|
||||
async function addFriend(event: any) { |
|
||||
console.log(typeof event); |
|
||||
|
|
||||
event.preventDefault(); |
|
||||
const usernameInput = event.target.querySelector('input[type="text"]'); |
|
||||
console.log(usernameInput); |
|
||||
|
|
||||
const username = usernameInput.value; |
|
||||
// const response = await fetch('', { |
|
||||
// method: 'POST', |
|
||||
// headers: { |
|
||||
// 'Content-Type': 'application/json' |
|
||||
// }, |
|
||||
// body: JSON.stringify({ username }) |
|
||||
// }); |
|
||||
// if (response.ok) { |
|
||||
// console.log('Friend added successfully'); |
|
||||
// } else { |
|
||||
// console.log('Failed to add friend'); |
|
||||
// } |
|
||||
// usernameInput.value = ''; |
|
||||
alert('Trying to add friend' + username); |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<div class="overlay"> |
|
||||
<div class="friends" on:click|stopPropagation on:keydown|stopPropagation> |
|
||||
<div> |
|
||||
{#if friends.length > 0} |
|
||||
<h2>Monkey friends</h2> |
|
||||
{#each friends.slice(0, 10) as friend} |
|
||||
<li> |
|
||||
<span>{friend.username} is {friend.status}</span> |
|
||||
</li> |
|
||||
{/each} |
|
||||
{:else} |
|
||||
<p>No friends to display</p> |
|
||||
{/if} |
|
||||
<div> |
|
||||
<h3>Add a friend</h3> |
|
||||
<form on:submit={addFriend}> |
|
||||
<input type="text" /> |
|
||||
<button type="submit">Add</button> |
|
||||
</form> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<style> |
|
||||
.overlay { |
|
||||
position: fixed; |
|
||||
top: 0; |
|
||||
left: 0; |
|
||||
width: 100%; |
|
||||
height: 100%; |
|
||||
background-color: rgba(0, 0, 0, 0.5); |
|
||||
z-index: 9998; |
|
||||
display: flex; |
|
||||
justify-content: center; |
|
||||
align-items: center; |
|
||||
} |
|
||||
|
|
||||
.friends { |
|
||||
background-color: #fff; |
|
||||
border: 1px solid #ccc; |
|
||||
border-radius: 5px; |
|
||||
padding: 1rem; |
|
||||
width: 300px; |
|
||||
} |
|
||||
</style> |
|
@ -1,58 +0,0 @@ |
|||||
<script lang="ts" context="module"> |
|
||||
export interface Match { |
|
||||
winner: string; |
|
||||
loser: string; |
|
||||
points: number; |
|
||||
rank: string; |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<script lang="ts"> |
|
||||
export let matches: Array<Match> = []; |
|
||||
</script> |
|
||||
|
|
||||
<div class="overlay"> |
|
||||
<div class="history" on:click|stopPropagation on:keydown|stopPropagation> |
|
||||
<div> |
|
||||
{#if matches.length > 0} |
|
||||
<h2>Last 10 monkey games</h2> |
|
||||
{#each matches.slice(0, 10) as match} |
|
||||
<li> |
|
||||
<span>{match.winner} 1 - 0 {match.loser}</span> |
|
||||
{#if match.points > 0} |
|
||||
<span>+{match.points}</span> |
|
||||
{:else} |
|
||||
<span>{match.points}</span> |
|
||||
{/if} |
|
||||
<span>MP | rank #{match.rank}</span> |
|
||||
</li> |
|
||||
{/each} |
|
||||
{:else} |
|
||||
<p>No matches to display</p> |
|
||||
{/if} |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<style> |
|
||||
.overlay { |
|
||||
position: fixed; |
|
||||
top: 0; |
|
||||
left: 0; |
|
||||
width: 100%; |
|
||||
height: 100%; |
|
||||
background-color: rgba(0, 0, 0, 0.5); |
|
||||
z-index: 9998; |
|
||||
display: flex; |
|
||||
justify-content: center; |
|
||||
align-items: center; |
|
||||
} |
|
||||
|
|
||||
.history { |
|
||||
background-color: #fff; |
|
||||
border: 1px solid #ccc; |
|
||||
border-radius: 5px; |
|
||||
padding: 1rem; |
|
||||
width: 300px; |
|
||||
} |
|
||||
</style> |
|
@ -1,106 +0,0 @@ |
|||||
<script lang="ts"> |
|
||||
export let links = [ |
|
||||
{ text: 'Home', url: 'img/pong.png' }, |
|
||||
{ text: 'Spectate' }, |
|
||||
{ text: 'Chat' }, |
|
||||
{ text: 'History' }, |
|
||||
{ text: 'Friends' }, |
|
||||
{ text: 'Profile' } |
|
||||
]; |
|
||||
export let clickProfile = () => {}; |
|
||||
export let clickHistory = () => {}; |
|
||||
export let clickFriends = () => {}; |
|
||||
export let clickSpectate = () => {}; |
|
||||
</script> |
|
||||
|
|
||||
<nav class="navigation-bar"> |
|
||||
<ul> |
|
||||
{#each links as link} |
|
||||
{#if link.text === 'Spectate'} |
|
||||
<li> |
|
||||
<button on:click={clickSpectate}> |
|
||||
<p>Spectate</p> |
|
||||
</button> |
|
||||
</li> |
|
||||
{/if} |
|
||||
{#if link.text === 'Friends'} |
|
||||
<li> |
|
||||
<button on:click={clickFriends}> |
|
||||
<p>Friends</p> |
|
||||
</button> |
|
||||
</li> |
|
||||
{/if} |
|
||||
{#if link.text === 'Profile'} |
|
||||
<li> |
|
||||
<button on:click={clickProfile}> |
|
||||
<img src="img/profileicon.png" alt="profile icon" /> |
|
||||
</button> |
|
||||
</li> |
|
||||
{/if} |
|
||||
{#if link.text === 'History'} |
|
||||
<li> |
|
||||
<button on:click={clickHistory}> |
|
||||
<p>History</p> |
|
||||
</button> |
|
||||
</li> |
|
||||
{/if} |
|
||||
{#if link.text === 'Home'} |
|
||||
<li> |
|
||||
<a href="/"> |
|
||||
<img src="img/pong.png" alt="home-icon" /> |
|
||||
</a> |
|
||||
</li> |
|
||||
{/if} |
|
||||
{/each} |
|
||||
</ul> |
|
||||
</nav> |
|
||||
|
|
||||
<style> |
|
||||
.navigation-bar { |
|
||||
display: flex; |
|
||||
justify-content: center; |
|
||||
align-items: center; |
|
||||
background-color: #f5f5f5; |
|
||||
padding: 1rem; |
|
||||
} |
|
||||
|
|
||||
.navigation-bar ul { |
|
||||
display: flex; |
|
||||
justify-content: center; |
|
||||
align-items: center; |
|
||||
list-style: none; |
|
||||
padding: 0; |
|
||||
} |
|
||||
|
|
||||
.navigation-bar li { |
|
||||
margin: 0 1rem; |
|
||||
} |
|
||||
|
|
||||
.navigation-bar img { |
|
||||
width: 2rem; |
|
||||
height: 2rem; |
|
||||
} |
|
||||
|
|
||||
.navigation-bar button { |
|
||||
border: none; |
|
||||
} |
|
||||
|
|
||||
@media (max-width: 768px) { |
|
||||
.navigation-bar { |
|
||||
flex-direction: column; |
|
||||
align-items: stretch; |
|
||||
} |
|
||||
|
|
||||
.navigation-bar ul { |
|
||||
flex-direction: column; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
} |
|
||||
|
|
||||
.navigation-bar li { |
|
||||
margin: 0; |
|
||||
padding: 1rem; |
|
||||
text-align: center; |
|
||||
} |
|
||||
} |
|
||||
</style> |
|
@ -1,38 +0,0 @@ |
|||||
<script lang="ts"> |
|
||||
// import { navigate } from 'svelte-routing'; |
|
||||
|
|
||||
// function handleMatchmakingClick() { |
|
||||
// navigate('/matchmaking'); |
|
||||
// } |
|
||||
|
|
||||
// function handlePlayWithFriendClick() { |
|
||||
// navigate('/play-with-friend'); |
|
||||
// } |
|
||||
</script> |
|
||||
|
|
||||
<!-- dr --> |
|
||||
<main> |
|
||||
<h1>Choose a gamemode</h1> |
|
||||
<!-- <button on:click={handleMatchmakingClick}>Matchmaking</button> |
|
||||
<button on:click={handlePlayWithFriendClick}>Play with a friend</button> --> |
|
||||
<button>Matchmaking</button> |
|
||||
<button>Play with a friend</button> |
|
||||
</main> |
|
||||
|
|
||||
<style> |
|
||||
main { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
align-items: center; |
|
||||
} |
|
||||
|
|
||||
h1 { |
|
||||
margin-bottom: 2rem; |
|
||||
} |
|
||||
|
|
||||
button { |
|
||||
font-size: 1.5rem; |
|
||||
padding: 1rem 2rem; |
|
||||
margin-bottom: 1rem; |
|
||||
} |
|
||||
</style> |
|
@ -1,15 +0,0 @@ |
|||||
import { Point, Rect } from './utils'; |
|
||||
|
|
||||
export class Ball { |
|
||||
rect: Rect; |
|
||||
speed: Point; |
|
||||
color: string | CanvasGradient | CanvasPattern = 'white'; |
|
||||
|
|
||||
constructor(spawn: Point, size: Point = new Point(20, 20), speed: Point = new Point(10, 2)) { |
|
||||
this.rect = new Rect(spawn, size); |
|
||||
} |
|
||||
|
|
||||
draw(context: CanvasRenderingContext2D) { |
|
||||
this.rect.draw(context, this.color); |
|
||||
} |
|
||||
} |
|
@ -1,80 +0,0 @@ |
|||||
import { Ball } from './Ball'; |
|
||||
import { GAME_EVENTS } from './constants'; |
|
||||
import type { GameInfo, GameUpdate } from './constants'; |
|
||||
import { Paddle } from './Paddle'; |
|
||||
import { Player } from './Player'; |
|
||||
import { formatWebsocketData, Point } from './utils'; |
|
||||
|
|
||||
const BG_COLOR = 'black'; |
|
||||
|
|
||||
export class Game { |
|
||||
canvas: HTMLCanvasElement; |
|
||||
context: CanvasRenderingContext2D; |
|
||||
ball: Ball; |
|
||||
players: Player[]; |
|
||||
my_paddle: Paddle; |
|
||||
|
|
||||
constructor(canvas: HTMLCanvasElement, context: CanvasRenderingContext2D) { |
|
||||
this.canvas = canvas; |
|
||||
this.context = context; |
|
||||
this.players = []; |
|
||||
this.my_paddle = null; |
|
||||
} |
|
||||
|
|
||||
setInfo(data: GameInfo) { |
|
||||
this.canvas.width = data.mapSize.x; |
|
||||
this.canvas.height = data.mapSize.y; |
|
||||
|
|
||||
this.ball = new Ball(new Point(this.canvas.width / 2, this.canvas.height / 2), data.ballSize); |
|
||||
|
|
||||
const paddle1: Paddle = new Paddle(new Point(data.playerXOffset, this.canvas.height / 2), data.paddleSize); |
|
||||
const paddle2: Paddle = new Paddle( |
|
||||
new Point(this.canvas.width - data.playerXOffset, this.canvas.height / 2), |
|
||||
data.paddleSize |
|
||||
); |
|
||||
this.players = [new Player(paddle1), new Player(paddle2)]; |
|
||||
this.my_paddle = this.players[data.yourPaddleIndex].paddle; |
|
||||
} |
|
||||
|
|
||||
start(socket: WebSocket) { |
|
||||
if (this.my_paddle) { |
|
||||
this.canvas.addEventListener('mousemove', (e) => { |
|
||||
this.my_paddle.move(e); |
|
||||
socket.send( |
|
||||
formatWebsocketData(GAME_EVENTS.PLAYER_MOVE, { |
|
||||
position: this.my_paddle.rect.center |
|
||||
}) |
|
||||
); |
|
||||
}); |
|
||||
console.log('Game started!'); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
update(data: GameUpdate) { |
|
||||
if (this.players[0].paddle != this.my_paddle) { |
|
||||
this.players[0].paddle.rect.center = data.paddlesPositions[0]; |
|
||||
} |
|
||||
if (this.players[1].paddle != this.my_paddle) { |
|
||||
this.players[1].paddle.rect.center = data.paddlesPositions[1]; |
|
||||
} |
|
||||
this.ball.rect.center = data.ballPosition; |
|
||||
for (let i = 0; i < data.scores.length; i++) { |
|
||||
this.players[i].score = data.scores[i]; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
draw() { |
|
||||
this.context.fillStyle = BG_COLOR; |
|
||||
this.context.fillRect(0, 0, this.canvas.width, this.canvas.height); |
|
||||
|
|
||||
this.players.forEach((p) => p.draw(this.context)); |
|
||||
this.ball.draw(this.context); |
|
||||
|
|
||||
const max_width = 50; |
|
||||
this.context.font = '50px Arial'; |
|
||||
const text_width = this.context.measureText('0').width; |
|
||||
const text_offset = 50; |
|
||||
this.players[0].drawScore(this.canvas.width / 2 - (text_width + text_offset), max_width, this.context); |
|
||||
this.players[1].drawScore(this.canvas.width / 2 + text_offset, max_width, this.context); |
|
||||
} |
|
||||
} |
|
@ -1,29 +0,0 @@ |
|||||
import { Point, Rect } from './utils'; |
|
||||
|
|
||||
export class Paddle { |
|
||||
rect: Rect; |
|
||||
color: string | CanvasGradient | CanvasPattern = 'white'; |
|
||||
|
|
||||
constructor(spawn: Point, size: Point = new Point(6, 100)) { |
|
||||
this.rect = new Rect(spawn, size); |
|
||||
} |
|
||||
|
|
||||
draw(context: CanvasRenderingContext2D) { |
|
||||
this.rect.draw(context, this.color); |
|
||||
} |
|
||||
|
|
||||
move(e: MouseEvent) { |
|
||||
const canvas = e.target as HTMLCanvasElement; |
|
||||
const rect = canvas.getBoundingClientRect(); |
|
||||
const new_y = ((e.clientY - rect.top) * canvas.height) / rect.height; |
|
||||
|
|
||||
const offset: number = this.rect.size.y / 2; |
|
||||
if (new_y - offset < 0) { |
|
||||
this.rect.center.y = offset; |
|
||||
} else if (new_y + offset > canvas.height) { |
|
||||
this.rect.center.y = canvas.height - offset; |
|
||||
} else { |
|
||||
this.rect.center.y = new_y; |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,19 +0,0 @@ |
|||||
import type { Paddle } from './Paddle'; |
|
||||
|
|
||||
export class Player { |
|
||||
paddle: Paddle; |
|
||||
score: number; |
|
||||
|
|
||||
constructor(paddle: Paddle) { |
|
||||
this.paddle = paddle; |
|
||||
this.score = 0; |
|
||||
} |
|
||||
|
|
||||
draw(context: CanvasRenderingContext2D) { |
|
||||
this.paddle.draw(context); |
|
||||
} |
|
||||
|
|
||||
drawScore(score_position_x: number, max_width: number, context: CanvasRenderingContext2D) { |
|
||||
context.fillText(this.score.toString(), score_position_x, 50, max_width); |
|
||||
} |
|
||||
} |
|
@ -1,56 +0,0 @@ |
|||||
<script lang="ts"> |
|
||||
import { GAME_EVENTS } from './constants'; |
|
||||
import { Game } from './Game'; |
|
||||
import { formatWebsocketData } from './utils'; |
|
||||
|
|
||||
const FPS = 144; |
|
||||
const SERVER_URL = 'ws://localhost:3001'; |
|
||||
|
|
||||
let socket: WebSocket; |
|
||||
|
|
||||
//Get canvas and its context |
|
||||
window.onload = () => { |
|
||||
const canvas: HTMLCanvasElement = document.getElementById('pong_canvas') as HTMLCanvasElement; |
|
||||
if (canvas) { |
|
||||
const context: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D; |
|
||||
if (context) { |
|
||||
setupSocket(canvas, context); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
function setupSocket(canvas: HTMLCanvasElement, context: CanvasRenderingContext2D) { |
|
||||
socket = new WebSocket(SERVER_URL); |
|
||||
const game = new Game(canvas, context); |
|
||||
socket.onmessage = function (e) { |
|
||||
const event_json = JSON.parse(e.data); |
|
||||
const event = event_json.event; |
|
||||
const data = event_json.data; |
|
||||
|
|
||||
if (event == GAME_EVENTS.START_GAME) { |
|
||||
game.start(socket); |
|
||||
} else if (event == GAME_EVENTS.GAME_TICK) { |
|
||||
game.update(data); |
|
||||
} else if (event == GAME_EVENTS.GET_GAME_INFO) { |
|
||||
game.setInfo(data); |
|
||||
setInterval(() => { |
|
||||
game.draw(); |
|
||||
}, 1000 / FPS); |
|
||||
console.log('Game loaded!'); |
|
||||
} else { |
|
||||
console.log('Unknown event from server: ' + event); |
|
||||
} |
|
||||
}; |
|
||||
socket.onopen = () => { |
|
||||
console.log('Connected to game server!'); |
|
||||
socket.send(formatWebsocketData(GAME_EVENTS.GET_GAME_INFO)); |
|
||||
}; |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<div> |
|
||||
<button on:click={() => socket.send(formatWebsocketData(GAME_EVENTS.START_GAME))}>Start game</button> |
|
||||
<br /> |
|
||||
<br /> |
|
||||
<canvas id="pong_canvas" /> |
|
||||
</div> |
|
@ -1,32 +0,0 @@ |
|||||
import { Point } from './utils'; |
|
||||
|
|
||||
export const GAME_EVENTS = { |
|
||||
START_GAME: 'START_GAME', |
|
||||
GAME_TICK: 'GAME_TICK', |
|
||||
PLAYER_MOVE: 'PLAYER_MOVE', |
|
||||
GET_GAME_INFO: 'GET_GAME_INFO' |
|
||||
}; |
|
||||
|
|
||||
export interface GameInfo extends GameInfoConstants { |
|
||||
yourPaddleIndex: number; |
|
||||
} |
|
||||
export interface GameInfoConstants { |
|
||||
mapSize: Point; |
|
||||
paddleSize: Point; |
|
||||
playerXOffset: number; |
|
||||
ballSize: Point; |
|
||||
winScore: number; |
|
||||
} |
|
||||
export const gameInfoConstants: GameInfoConstants = { |
|
||||
mapSize: new Point(600, 400), |
|
||||
paddleSize: new Point(6, 50), |
|
||||
playerXOffset: 50, |
|
||||
ballSize: new Point(20, 20), |
|
||||
winScore: 2 |
|
||||
}; |
|
||||
|
|
||||
export interface GameUpdate { |
|
||||
paddlesPositions: Point[]; |
|
||||
ballPosition: Point; |
|
||||
scores: number[]; |
|
||||
} |
|
@ -1,88 +0,0 @@ |
|||||
export class Point { |
|
||||
x: number; |
|
||||
y: number; |
|
||||
|
|
||||
constructor(x: number, y: number) { |
|
||||
this.x = x; |
|
||||
this.y = y; |
|
||||
} |
|
||||
|
|
||||
//Returns a new point
|
|
||||
add(other: Point) { |
|
||||
return new Point(this.x + other.x, this.y + other.y); |
|
||||
} |
|
||||
|
|
||||
//Modifies `this` point
|
|
||||
add_inplace(other: Point) { |
|
||||
this.x += other.x; |
|
||||
this.y += other.y; |
|
||||
} |
|
||||
|
|
||||
clone(): Point { |
|
||||
return new Point(this.x, this.y); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
export class Rect { |
|
||||
center: Point; |
|
||||
size: Point; |
|
||||
|
|
||||
constructor(center: Point, size: Point) { |
|
||||
this.center = center; |
|
||||
this.size = size; |
|
||||
} |
|
||||
|
|
||||
draw(context: CanvasRenderingContext2D, color: string | CanvasGradient | CanvasPattern) { |
|
||||
const offset: Point = new Point(this.size.x / 2, this.size.y / 2); |
|
||||
|
|
||||
context.fillStyle = color; |
|
||||
context.fillRect(this.center.x - offset.x, this.center.y - offset.y, this.size.x, this.size.y); |
|
||||
} |
|
||||
|
|
||||
//True if `this` rect contains `other` rect in the x-axis
|
|
||||
contains_x(other: Rect): boolean { |
|
||||
const offset: number = this.size.x / 2; |
|
||||
const offset_other: number = other.size.x / 2; |
|
||||
|
|
||||
if ( |
|
||||
this.center.x - offset <= other.center.x - offset_other && |
|
||||
this.center.x + offset >= other.center.x + offset_other |
|
||||
) |
|
||||
return true; |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
//True if `this` rect contains `other` rect in the y-axis
|
|
||||
contains_y(other: Rect): boolean { |
|
||||
const offset: number = this.size.y / 2; |
|
||||
const offset_other: number = other.size.y / 2; |
|
||||
|
|
||||
if ( |
|
||||
this.center.y - offset <= other.center.y - offset_other && |
|
||||
this.center.y + offset >= other.center.y + offset_other |
|
||||
) |
|
||||
return true; |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
collides(other: Rect): boolean { |
|
||||
const offset: Point = new Point(this.size.x / 2, this.size.y / 2); |
|
||||
const offset_other: Point = new Point(other.size.x / 2, other.size.y / 2); |
|
||||
|
|
||||
if ( |
|
||||
this.center.x - offset.x < other.center.x + offset_other.x && |
|
||||
this.center.x + offset.x > other.center.x - offset_other.x && |
|
||||
this.center.y - offset.y < other.center.y + offset_other.y && |
|
||||
this.center.y + offset.y > other.center.y - offset_other.y |
|
||||
) |
|
||||
return true; |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
export function formatWebsocketData(event: string, data?: any): string { |
|
||||
return JSON.stringify({ |
|
||||
event, |
|
||||
data |
|
||||
}); |
|
||||
} |
|
@ -1,109 +0,0 @@ |
|||||
<script lang="ts"> |
|
||||
export let username = ''; |
|
||||
export let realname = ''; |
|
||||
export let wins = 0; |
|
||||
export let losses = 0; |
|
||||
export let elo = 0; |
|
||||
export let rank = -1; |
|
||||
export let is2faEnabled = false; |
|
||||
async function handleSubmit(event: Event) { |
|
||||
event.preventDefault(); |
|
||||
// const response = await fetch('', { |
|
||||
// method: 'POST', |
|
||||
// headers: { |
|
||||
// 'Content-Type': 'application/json' |
|
||||
// }, |
|
||||
// body: JSON.stringify({ |
|
||||
// username |
|
||||
// }) |
|
||||
// }); |
|
||||
// if (response.ok) { |
|
||||
// console.log('username updated'); |
|
||||
// } |
|
||||
// else { |
|
||||
// console.log('username update failed'); |
|
||||
// } |
|
||||
alert('Trying to update username to ' + username); |
|
||||
} |
|
||||
async function handleAvatarUpload(event: Event) { |
|
||||
event.preventDefault(); |
|
||||
alert('Trying to upload avatar'); |
|
||||
} |
|
||||
async function handle2fa(event: Event) { |
|
||||
event.preventDefault(); |
|
||||
alert('Trying to ' + (is2faEnabled ? 'disable' : 'enable') + ' 2FA'); |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<div class="overlay"> |
|
||||
<div class="profile" on:click|stopPropagation on:keydown|stopPropagation> |
|
||||
<div class="profile-header"> |
|
||||
<img class="profile-img" src="img/profileicon.png" alt="Profile Icon" /> |
|
||||
<h3>{realname}</h3> |
|
||||
<form on:submit={handleAvatarUpload}> |
|
||||
<button type="submit">Upload Avatar</button> |
|
||||
</form> |
|
||||
</div> |
|
||||
<div class="profile-body"> |
|
||||
<form on:submit={handleSubmit}> |
|
||||
<div class="username"> |
|
||||
<label for="username">Username</label> |
|
||||
<input type="text" id="username" bind:value={username} /> |
|
||||
</div> |
|
||||
<button type="submit">Submit</button> |
|
||||
</form> |
|
||||
<p>Wins: {wins}</p> |
|
||||
<p>Losses: {losses}</p> |
|
||||
<p>Winrate: {(wins / (wins + losses)) * 100}%</p> |
|
||||
<p>Elo : {elo}</p> |
|
||||
<p>Rank: {rank}</p> |
|
||||
<form class="two-factor-auth" on:submit={handle2fa}> |
|
||||
<button type="submit"> |
|
||||
{#if is2faEnabled} |
|
||||
Disable 2FA |
|
||||
{:else} |
|
||||
Enable 2FA |
|
||||
{/if} |
|
||||
</button> |
|
||||
</form> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<style> |
|
||||
.overlay { |
|
||||
position: fixed; |
|
||||
top: 0; |
|
||||
left: 0; |
|
||||
width: 100%; |
|
||||
height: 100%; |
|
||||
background-color: rgba(0, 0, 0, 0.5); |
|
||||
z-index: 9998; |
|
||||
display: flex; |
|
||||
justify-content: center; |
|
||||
align-items: center; |
|
||||
} |
|
||||
|
|
||||
.profile { |
|
||||
background-color: #fff; |
|
||||
border: 1px solid #ccc; |
|
||||
border-radius: 5px; |
|
||||
padding: 1rem; |
|
||||
width: 300px; |
|
||||
} |
|
||||
|
|
||||
.profile-header { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
} |
|
||||
|
|
||||
.profile-img { |
|
||||
width: 50px; |
|
||||
height: 50px; |
|
||||
margin-right: 1rem; |
|
||||
} |
|
||||
|
|
||||
.two-factor-auth { |
|
||||
margin-top: 1rem; |
|
||||
} |
|
||||
</style> |
|
@ -1,53 +0,0 @@ |
|||||
<script lang="ts" context="module"> |
|
||||
export interface SpectateType { |
|
||||
id: string; |
|
||||
player1: string; |
|
||||
player2: string; |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<script lang="ts"> |
|
||||
export let spectate: Array<SpectateType> = []; |
|
||||
export let watch = () => {}; |
|
||||
</script> |
|
||||
|
|
||||
<div class="overlay"> |
|
||||
<div class="spectate" on:click|stopPropagation on:keydown|stopPropagation> |
|
||||
<div> |
|
||||
{#if spectate.length > 0} |
|
||||
<h2>Monkey spectating</h2> |
|
||||
{#each spectate.slice(0, 10) as _spectate} |
|
||||
<li> |
|
||||
<span>{_spectate.player1} VS {_spectate.player2}</span> |
|
||||
<button on:click={() => watch(_spectate.id)}>Spectate</button> |
|
||||
</li> |
|
||||
{/each} |
|
||||
{:else} |
|
||||
<p>No matches to spectate</p> |
|
||||
{/if} |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<style> |
|
||||
.overlay { |
|
||||
position: fixed; |
|
||||
top: 0; |
|
||||
left: 0; |
|
||||
width: 100%; |
|
||||
height: 100%; |
|
||||
background-color: rgba(0, 0, 0, 0.5); |
|
||||
z-index: 9998; |
|
||||
display: flex; |
|
||||
justify-content: center; |
|
||||
align-items: center; |
|
||||
} |
|
||||
|
|
||||
.spectate { |
|
||||
background-color: #fff; |
|
||||
border: 1px solid #ccc; |
|
||||
border-radius: 5px; |
|
||||
padding: 1rem; |
|
||||
width: 300px; |
|
||||
} |
|
||||
</style> |
|
@ -1,7 +0,0 @@ |
|||||
import App from './App.svelte'; |
|
||||
|
|
||||
const app = new App({ |
|
||||
target: document.body, |
|
||||
}); |
|
||||
|
|
||||
export default app; |
|
@ -1,32 +0,0 @@ |
|||||
{ |
|
||||
"compilerOptions": { |
|
||||
"target": "es6", |
|
||||
"module": "es6", |
|
||||
"strict": true, |
|
||||
"noImplicitAny": true, |
|
||||
"jsx": "preserve", |
|
||||
"importHelpers": true, |
|
||||
"moduleResolution": "node", |
|
||||
"esModuleInterop": true, |
|
||||
"allowSyntheticDefaultImports": true, |
|
||||
"sourceMap": true, |
|
||||
"baseUrl": ".", |
|
||||
"types": [ |
|
||||
"svelte" |
|
||||
], |
|
||||
"experimentalDecorators": true, |
|
||||
"emitDecoratorMetadata": true, |
|
||||
"skipDefaultLibCheck": true, |
|
||||
"strictNullChecks": false, |
|
||||
"declaration": false, |
|
||||
"downlevelIteration": true |
|
||||
}, |
|
||||
"include": [ |
|
||||
"src/**/*" |
|
||||
], |
|
||||
"exclude": [ |
|
||||
"node_modules/*", |
|
||||
"public/*" |
|
||||
], |
|
||||
"extends": "@tsconfig/svelte/tsconfig.json" |
|
||||
} |
|
@ -0,0 +1,47 @@ |
|||||
|
# Svelte + TS + Vite |
||||
|
|
||||
|
This template should help get you started developing with Svelte and TypeScript in Vite. |
||||
|
|
||||
|
## Recommended IDE Setup |
||||
|
|
||||
|
[VS Code](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). |
||||
|
|
||||
|
## Need an official Svelte framework? |
||||
|
|
||||
|
Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more. |
||||
|
|
||||
|
## Technical considerations |
||||
|
|
||||
|
**Why use this over SvelteKit?** |
||||
|
|
||||
|
- It brings its own routing solution which might not be preferable for some users. |
||||
|
- It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app. |
||||
|
|
||||
|
This template contains as little as possible to get started with Vite + TypeScript + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project. |
||||
|
|
||||
|
Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate. |
||||
|
|
||||
|
**Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?** |
||||
|
|
||||
|
Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information. |
||||
|
|
||||
|
**Why include `.vscode/extensions.json`?** |
||||
|
|
||||
|
Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project. |
||||
|
|
||||
|
**Why enable `allowJs` in the TS template?** |
||||
|
|
||||
|
While `allowJs: false` would indeed prevent the use of `.js` files in the project, it does not prevent the use of JavaScript syntax in `.svelte` files. In addition, it would force `checkJs: false`, bringing the worst of both worlds: not being able to guarantee the entire codebase is TypeScript, and also having worse typechecking for the existing JavaScript. In addition, there are valid use cases in which a mixed codebase may be relevant. |
||||
|
|
||||
|
**Why is HMR not preserving my local component state?** |
||||
|
|
||||
|
HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/rixo/svelte-hmr#svelte-hmr). |
||||
|
|
||||
|
If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR. |
||||
|
|
||||
|
```ts |
||||
|
// store.ts |
||||
|
// An extremely simple external store |
||||
|
import { writable } from 'svelte/store' |
||||
|
export default writable(0) |
||||
|
``` |
@ -0,0 +1,17 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="en"> |
||||
|
|
||||
|
<head> |
||||
|
<meta charset='utf-8'> |
||||
|
<meta name='viewport' content='width=device-width,initial-scale=1'> |
||||
|
<title>Pong</title> |
||||
|
|
||||
|
<link rel='icon' type='image/png' href='/img/pog.jpg'> |
||||
|
<link rel='stylesheet' href='/global.css'> |
||||
|
<script type="module" src="src/main.ts"></script> |
||||
|
</head> |
||||
|
|
||||
|
<body> |
||||
|
</body> |
||||
|
|
||||
|
</html> |
File diff suppressed because it is too large
@ -0,0 +1,21 @@ |
|||||
|
{ |
||||
|
"name": "Transcendence", |
||||
|
"private": true, |
||||
|
"version": "0.0.0", |
||||
|
"type": "module", |
||||
|
"scripts": { |
||||
|
"dev": "vite", |
||||
|
"build": "vite build", |
||||
|
"preview": "vite preview", |
||||
|
"check": "svelte-check --tsconfig ./tsconfig.json" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"@sveltejs/vite-plugin-svelte": "^2.0.2", |
||||
|
"@tsconfig/svelte": "^3.0.0", |
||||
|
"svelte": "^3.55.1", |
||||
|
"svelte-check": "^2.10.3", |
||||
|
"tslib": "^2.5.0", |
||||
|
"typescript": "^4.9.3", |
||||
|
"vite": "^4.1.0" |
||||
|
} |
||||
|
} |
@ -0,0 +1,7 @@ |
|||||
|
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' |
||||
|
|
||||
|
export default { |
||||
|
// Consult https://svelte.dev/docs#compile-time-svelte-preprocess
|
||||
|
// for more information about preprocessors
|
||||
|
preprocess: vitePreprocess(), |
||||
|
} |
@ -0,0 +1,21 @@ |
|||||
|
{ |
||||
|
"extends": "@tsconfig/svelte/tsconfig.json", |
||||
|
"compilerOptions": { |
||||
|
"target": "ESNext", |
||||
|
"useDefineForClassFields": true, |
||||
|
"module": "ESNext", |
||||
|
"resolveJsonModule": true, |
||||
|
/** |
||||
|
* Typecheck JS in `.svelte` and `.js` files by default. |
||||
|
* Disable checkJs if you'd like to use dynamic types in JS. |
||||
|
* Note that setting allowJs false does not prevent the use |
||||
|
* of JS in `.svelte` files. |
||||
|
*/ |
||||
|
"allowJs": true, |
||||
|
"checkJs": true, |
||||
|
"isolatedModules": true |
||||
|
}, |
||||
|
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"], |
||||
|
"exclude": ["node_modules/*", "public/*"], |
||||
|
"references": [{ "path": "./tsconfig.node.json" }] |
||||
|
} |
@ -0,0 +1,8 @@ |
|||||
|
{ |
||||
|
"compilerOptions": { |
||||
|
"composite": true, |
||||
|
"module": "ESNext", |
||||
|
"moduleResolution": "Node" |
||||
|
}, |
||||
|
"include": ["vite.config.ts"] |
||||
|
} |
@ -0,0 +1,7 @@ |
|||||
|
import { defineConfig } from 'vite' |
||||
|
import { svelte } from '@sveltejs/vite-plugin-svelte' |
||||
|
|
||||
|
// https://vitejs.dev/config/
|
||||
|
export default defineConfig({ |
||||
|
plugins: [svelte()], |
||||
|
}) |
Loading…
Reference in new issue