You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
349 lines
9.5 KiB
349 lines
9.5 KiB
<script lang="ts" context="module">
|
|
export enum APPSTATE {
|
|
HOME = "/",
|
|
PROFILE = "/profile",
|
|
HISTORY = "/history",
|
|
HISTORY_ID = "/history_id",
|
|
FRIENDS = "/friends",
|
|
CHANNELS = "/channels",
|
|
LEADERBOARD = "/leaderboard",
|
|
CREATE_GAME = "/create-game",
|
|
MATCHMAKING = "/matchmaking",
|
|
PROFILE_ID = "/profile_id",
|
|
}
|
|
|
|
</script>
|
|
|
|
<script lang="ts">
|
|
import { onMount } from "svelte";
|
|
import Navbar from "./components/NavBar.svelte";
|
|
import Modal from "svelte-simple-modal";
|
|
import Profile from "./components/Profile.svelte";
|
|
import MatchHistory from "./components/MatchHistory.svelte";
|
|
import Friends, { addFriend } from "./components/Friends.svelte";
|
|
import Chat from "./components/Chat.svelte";
|
|
import Channels, { formatChannelNames, type chatMessagesType } from "./components/Channels.svelte";
|
|
import Leaderboard from "./components/Leaderboard.svelte";
|
|
import { popup } from "./components/Alert/content";
|
|
import Pong from "./components/Pong/Pong.svelte";
|
|
import type { ChannelsType } from "./components/Channels.svelte";
|
|
import { API_URL } from "./Auth";
|
|
|
|
import { store, getUser, login, verify } from "./Auth";
|
|
import FakeLogin from "./FakeLogin.svelte";
|
|
|
|
// Single Page Application config
|
|
let appState: string = APPSTATE.HOME;
|
|
|
|
async function updateChat() {
|
|
const urlSplit = appState.split("#", 2)
|
|
if (appState.includes(APPSTATE.CHANNELS) && urlSplit.length > 1) {
|
|
const currentChannelName = appState.split("#", 2)[1];
|
|
fetch(API_URL + "/channels", {
|
|
credentials: "include",
|
|
mode: "cors",
|
|
}).then((res) => {
|
|
res.json().then(async (channels) => {
|
|
await formatChannelNames(channels);
|
|
const channel = channels.find((c: ChannelsType) => c.name === currentChannelName);
|
|
if (channel) {
|
|
//chan.selectChat(channel.id); // disabled as it causes a bug where joining a channel happen twice
|
|
} else {
|
|
alert("Failed loading channel");
|
|
}
|
|
});
|
|
}).catch(() => {
|
|
alert("Failed loading channel");
|
|
});
|
|
}
|
|
}
|
|
|
|
history.replaceState({ appState: "" }, "", "/");
|
|
window.onpopstate = (e: PopStateEvent) => {
|
|
if (e.state) {
|
|
appState = e.state.appState;
|
|
void updateChat(); // why this?
|
|
}
|
|
};
|
|
|
|
function resetAppState() {
|
|
setAppState(APPSTATE.HOME);
|
|
}
|
|
|
|
function setAppState(newState: APPSTATE | string) {
|
|
if (newState === appState) return;
|
|
appState = newState;
|
|
history.pushState({ appState }, "", appState);
|
|
void updateChat();
|
|
}
|
|
|
|
onMount(() => {
|
|
getUser();
|
|
});
|
|
setInterval(() => {
|
|
getUser();
|
|
}, 15000);
|
|
|
|
function clickProfile() {
|
|
profileUsername = $store.username
|
|
setAppState(APPSTATE.PROFILE);
|
|
}
|
|
|
|
let profileUsername: string = "";
|
|
async function openIdProfile(event: CustomEvent<string>) {
|
|
profileUsername = event.detail;
|
|
setAppState(APPSTATE.PROFILE_ID);
|
|
}
|
|
$: console.log(profileUsername)
|
|
|
|
async function getDMs(username: string): Promise<Response | null> {
|
|
const res = await fetch(API_URL + "/channels/dms/" + username, {
|
|
credentials: "include",
|
|
mode: "cors",
|
|
})
|
|
if (res.ok)
|
|
return res;
|
|
else
|
|
return null;
|
|
}
|
|
|
|
let chan: Channels;
|
|
async function openDirectChat(event: CustomEvent<string>) {
|
|
const DMUsername = event.detail;
|
|
let DMChannel: Array<ChannelsType> = [];
|
|
const res = await getDMs(DMUsername)
|
|
if (res && res.ok) {
|
|
DMChannel = await res.json();
|
|
if (DMChannel.length != 0)
|
|
chan.selectChat(DMChannel[0].id);
|
|
} else {
|
|
console.log("Creating DMChannel: " + $store.username + "&" + DMUsername)
|
|
fetch(API_URL + "/channels", {
|
|
credentials: "include",
|
|
method: "POST",
|
|
mode: "cors",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
name: "none",
|
|
owner: $store.ftId,
|
|
password: "",
|
|
isPrivate: true,
|
|
isDM: true,
|
|
otherDMedUsername: DMUsername
|
|
}),
|
|
}).then(async () => {
|
|
const response = await getDMs(DMUsername)
|
|
if (response && response.ok) {
|
|
DMChannel = await response.json();
|
|
if (DMChannel.length != 0) {
|
|
chan.selectChat(DMChannel[0].id);
|
|
} else {
|
|
alert("Error creating DM");
|
|
}
|
|
} else {
|
|
alert("Error creating DM");
|
|
}
|
|
}).catch((error) => {
|
|
alert("Error creating DM");
|
|
})
|
|
}
|
|
}
|
|
|
|
async function clickHistory() {
|
|
setAppState(APPSTATE.HISTORY);
|
|
}
|
|
|
|
async function clickFriends() {
|
|
setAppState(APPSTATE.FRIENDS);
|
|
}
|
|
|
|
async function clickLeaderboard() {
|
|
setAppState(APPSTATE.LEADERBOARD);
|
|
}
|
|
|
|
function clickChannels() {
|
|
setAppState(APPSTATE.CHANNELS);
|
|
}
|
|
let selectedChannel: ChannelsType;
|
|
let selectedMessages: Array<chatMessagesType>;
|
|
const handleSelectChannel = (channel: ChannelsType, messages: Array<chatMessagesType>) => {
|
|
selectedChannel = channel;
|
|
selectedMessages = messages;
|
|
setAppState(APPSTATE.CHANNELS + "#" + channel.name);
|
|
};
|
|
|
|
// GAME
|
|
let pong: Pong;
|
|
let gamePlaying: boolean = false;
|
|
|
|
// FAKE LOGIN
|
|
let usernameFake = "test";
|
|
let ftIdFake = "42";
|
|
let fakemenu = true;
|
|
let fakeUser = false;
|
|
function impersonate() {
|
|
const user = {
|
|
username: usernameFake,
|
|
socketKey: ftIdFake,
|
|
};
|
|
store.set(user);
|
|
fakeUser = true;
|
|
fakemenu = false;
|
|
}
|
|
</script>
|
|
|
|
<div>
|
|
<Modal show={$popup}>
|
|
{#if $store === null}
|
|
<div class="login-div">
|
|
<h3 class="test">Please log in with 42 api to access the website.</h3>
|
|
<img
|
|
class="img-42"
|
|
src="https://translate.intra.42.fr/assets/42_logo-7dfc9110a5319a308863b96bda33cea995046d1731cebb735e41b16255106c12.svg"
|
|
alt="logo_42"
|
|
/>
|
|
<button class="login-button" type="button" on:click={login}>Log In</button
|
|
>
|
|
</div>
|
|
{:else if $store.twoFA === true && $store.isVerified === false}
|
|
<h1><button type="button" on:click={verify}>verify</button></h1>
|
|
{:else}
|
|
|
|
<Navbar
|
|
{clickProfile}
|
|
{clickHistory}
|
|
{clickFriends}
|
|
{clickChannels}
|
|
{clickLeaderboard}
|
|
/>
|
|
{#if appState.includes(`${APPSTATE.CHANNELS}#`)}
|
|
<div
|
|
on:click={() => setAppState(APPSTATE.CHANNELS)}
|
|
on:keydown={() => setAppState(APPSTATE.CHANNELS)}
|
|
>
|
|
<Chat
|
|
channel={selectedChannel}
|
|
messages={selectedMessages}
|
|
on:view-profile={openIdProfile}
|
|
on:add-friend={addFriend}
|
|
on:invite-to-game={pong.inviteToGame}
|
|
on:send-message={openDirectChat}
|
|
on:return-home={resetAppState}
|
|
/>
|
|
</div>
|
|
{/if}
|
|
{#if appState.includes(APPSTATE.CHANNELS)}
|
|
<div
|
|
class="{appState !== APPSTATE.CHANNELS ? 'hidden' : ''}"
|
|
on:click={resetAppState}
|
|
on:keydown={resetAppState}
|
|
>
|
|
<Channels bind:this={chan} onSelectChannel={handleSelectChannel} />
|
|
</div>
|
|
{/if}
|
|
{#if appState === APPSTATE.LEADERBOARD}
|
|
<div on:click={resetAppState} on:keydown={resetAppState}>
|
|
<Leaderboard />
|
|
</div>
|
|
{/if}
|
|
{#if appState === APPSTATE.FRIENDS}
|
|
<div on:click={resetAppState} on:keydown={resetAppState}>
|
|
<Friends />
|
|
</div>
|
|
{/if}
|
|
{#if appState === APPSTATE.HISTORY}
|
|
<div on:click={resetAppState} on:keydown={resetAppState}>
|
|
<MatchHistory />
|
|
</div>
|
|
{/if}
|
|
{#if appState === APPSTATE.HISTORY_ID}
|
|
<div
|
|
on:click={() => setAppState(APPSTATE.PROFILE)}
|
|
on:keydown={() => setAppState(APPSTATE.PROFILE)}
|
|
>
|
|
<MatchHistory username={profileUsername} />
|
|
</div>
|
|
{/if}
|
|
{#if appState === APPSTATE.PROFILE}
|
|
<div on:click={resetAppState} on:keydown={resetAppState}>
|
|
<Profile {gamePlaying} on:view-history={() => setAppState(APPSTATE.HISTORY_ID)} />
|
|
</div>
|
|
{/if}
|
|
{#if appState === APPSTATE.PROFILE_ID}
|
|
<div
|
|
on:click={() =>
|
|
setAppState(APPSTATE.CHANNELS + "#" + selectedChannel.name)}
|
|
on:keydown={() =>
|
|
setAppState(APPSTATE.CHANNELS + "#" + selectedChannel.name)}
|
|
>
|
|
<Profile
|
|
{gamePlaying}
|
|
username={profileUsername}
|
|
on:view-history={() => setAppState(APPSTATE.HISTORY_ID)}
|
|
/>
|
|
</div>
|
|
{/if}
|
|
|
|
{#if fakemenu}
|
|
<FakeLogin bind:username={usernameFake} bind:ftId={ftIdFake} />
|
|
<button on:click={impersonate}>Impersonate</button>
|
|
<button on:click={() => (fakemenu = false)}>No impersonate</button>
|
|
{:else}
|
|
<Pong bind:gamePlaying={gamePlaying} bind:this={pong} {appState} {setAppState} {fakeUser} />
|
|
{/if}
|
|
{/if}
|
|
|
|
</Modal>
|
|
</div>
|
|
|
|
<style>
|
|
:global(body) {
|
|
background-color: #212529;
|
|
color: #e8e6e3;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
.login-div {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 100vh;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.img-42 {
|
|
-webkit-filter: invert(100%);
|
|
filter: invert(100%);
|
|
width: 64px;
|
|
height: 64px;
|
|
}
|
|
|
|
.login-button {
|
|
display: inline-block;
|
|
background-color: #198754;
|
|
border: none;
|
|
color: #fff;
|
|
padding: 0.5rem 1rem;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-size: 1rem;
|
|
transition: background-color 0.2s ease-in-out;
|
|
}
|
|
|
|
.login-button:hover {
|
|
background-color: #157347;
|
|
}
|
|
|
|
.login-button:focus {
|
|
outline: none;
|
|
box-shadow: 0 0 0 2px rgba(25, 135, 84, 0.25);
|
|
}
|
|
|
|
.hidden {
|
|
display: none;
|
|
}
|
|
</style>
|
|
|