Browse Source

*offline update fix

*can show profiles and invit from chat
master
Arnaud 2 years ago
committed by nicolas-arnaud
parent
commit
d376508a55
  1. 2
      back/volume/src/users/users.service.ts
  2. 2
      docker-compose.yml
  3. 61
      front/volume/src/App.svelte
  4. 37
      front/volume/src/components/Chat2.svelte
  5. 26
      front/volume/src/components/Friends.svelte
  6. 1
      front/volume/src/components/Leaderboard.svelte
  7. 122
      front/volume/src/components/Profile.svelte

2
back/volume/src/users/users.service.ts

@ -30,7 +30,7 @@ export class UsersService {
return user return user
} }
@Cron('0 */60 * * *') @Cron('0 * * * * *')
async updateStatus() { async updateStatus() {
let users = await this.usersRepository.find({}) let users = await this.usersRepository.find({})
users.forEach((usr) => { users.forEach((usr) => {

2
docker-compose.yml

@ -31,7 +31,7 @@ services:
image: postgres image: postgres
ports: [5432:5432] ports: [5432:5432]
volumes: volumes:
- /data/postgres:/data/postgres - ./postgres:/var/lib/postgresql/data
networks: [transcendence] networks: [transcendence]
restart: always restart: always
env_file: .env env_file: .env

61
front/volume/src/App.svelte

@ -4,8 +4,8 @@
import Profile from "./components/Profile.svelte"; import Profile from "./components/Profile.svelte";
import MatchHistory from "./components/MatchHistory.svelte"; import MatchHistory from "./components/MatchHistory.svelte";
import type { Match } from "./components/MatchHistory.svelte"; import type { Match } from "./components/MatchHistory.svelte";
import Friends from "./components/Friends.svelte"; import Friends,{addFriend} from "./components/Friends.svelte";
import type { Friend } from "./components/Friends.svelte"; import type { Friend} from "./components/Friends.svelte";
import Spectate from "./components/Spectate.svelte"; import Spectate from "./components/Spectate.svelte";
import type { SpectateType } from "./components/Spectate.svelte"; import type { SpectateType } from "./components/Spectate.svelte";
import Pong from "./components/Pong/Pong.svelte"; import Pong from "./components/Pong/Pong.svelte";
@ -13,6 +13,7 @@
import Channels from "./components/Channels.svelte"; import Channels from "./components/Channels.svelte";
import type { ChannelsType } from "./components/Channels.svelte"; import type { ChannelsType } from "./components/Channels.svelte";
import Leaderboard from "./components/Leaderboard.svelte"; import Leaderboard from "./components/Leaderboard.svelte";
import type { Player } from "./components/Leaderboard.svelte"
import { store, getUser, login, logout, API_URL } from "./Auth"; import { store, getUser, login, logout, API_URL } from "./Auth";
@ -30,6 +31,21 @@
isProfileOpen = true; isProfileOpen = true;
} }
let userProfile;
let isIdProfileOpen = false;
async function openIdProfile(event) {
console.log("Opening profile: " + event.detail)
isIdProfileOpen = true;
const res = await fetch(API_URL + "/user/" + event.detail, {
method: "get",
mode: "cors",
cache: "no-cache",
redirect: "follow",
referrerPolicy: "no-referrer",
});
userProfile = await res.json();
}
// HISTORY // HISTORY
let isHistoryOpen = false; let isHistoryOpen = false;
@ -112,10 +128,20 @@
selectedChannel = channel; selectedChannel = channel;
}; };
let leaderboard: Player[] = [];
export async function getLeader(): Promise<Player[]> {
let response = await fetch(API_URL + "/leader", {
credentials: "include",
mode: "cors",
});
return await response.json();
}
// LEADERBOARD // LEADERBOARD
let isLeaderboardOpen = false; let isLeaderboardOpen = false;
function clickLeaderboard() { async function clickLeaderboard() {
isLeaderboardOpen = true; isLeaderboardOpen = true;
leaderboard = await getLeader();
} }
</script> </script>
@ -139,7 +165,8 @@
on:click={() => (selectedChannel = undefined)} on:click={() => (selectedChannel = undefined)}
on:keydown={() => (selectedChannel = undefined)} on:keydown={() => (selectedChannel = undefined)}
> >
<Chat2 chatMessages={selectedChannel.messages} /> <Chat2 chatMessages={selectedChannel.messages}
on:view-profile={openIdProfile} on:add-friend={addFriend} />
</div> </div>
{/if} {/if}
{#if !selectedChannel} {#if !selectedChannel}
@ -164,7 +191,7 @@
on:click={() => (isLeaderboardOpen = false)} on:click={() => (isLeaderboardOpen = false)}
on:keydown={() => (isLeaderboardOpen = false)} on:keydown={() => (isLeaderboardOpen = false)}
> >
<Leaderboard /> <Leaderboard {leaderboard}/>
</div> </div>
{/if} {/if}
{#if isFriendOpen} {#if isFriendOpen}
@ -173,7 +200,10 @@
isFriendOpen = false; isFriendOpen = false;
clearInterval(friendsInterval) clearInterval(friendsInterval)
}} }}
on:keydown={() => (isFriendOpen = false)} on:keydown={() => {
isFriendOpen = false;
clearInterval(friendsInterval)
}}
> >
<Friends {friends} {invits} /> <Friends {friends} {invits} />
</div> </div>
@ -192,12 +222,19 @@
on:keydown={() => (isProfileOpen = false)} on:keydown={() => (isProfileOpen = false)}
> >
<Profile <Profile
username={$store.username} user = {$store}
wins={$store.wins} edit = 1
losses={$store.looses} />
winrate={$store.winrate} </div>
rank={$store.rank} {/if}
is2faEnabled={false} {#if isIdProfileOpen}
<div
on:click={() => (isIdProfileOpen = false)}
on:keydown={() => (isIdProfileOpen = false)}
>
<Profile
user = {userProfile}
edit = 0
/> />
</div> </div>
{/if} {/if}

37
front/volume/src/components/Chat2.svelte

@ -1,10 +1,11 @@
<script lang="ts" context="module"> <script lang="ts" context="module">
export interface chatMessagesType { export interface chatMessagesType {
id: number; id: number;
name: string; author: string;
text: string; text: string;
} }
import { createEventDispatcher, onMount } from "svelte"; import { createEventDispatcher, onMount } from "svelte";
import { store } from "../Auth"
</script> </script>
<script lang="ts"> <script lang="ts">
@ -12,7 +13,7 @@
if (newText !== "") { if (newText !== "") {
const newMessage = { const newMessage = {
id: chatMessages.length + 1, id: chatMessages.length + 1,
name: "You", author: $store.username,
text: newText, text: newText,
}; };
chatMessages = [...chatMessages.slice(-5 + 1), newMessage]; chatMessages = [...chatMessages.slice(-5 + 1), newMessage];
@ -30,14 +31,14 @@
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
let showProfileMenu = false; let showProfileMenu = false;
let selectedUserId = null; let selectedUser = null;
function openProfile(id : number) { function openProfile(username : string) {
showProfileMenu = true; showProfileMenu = true;
selectedUserId = id; selectedUser = username;
} }
function closeProfileMenu() { function closeProfileMenu() {
showProfileMenu = false; showProfileMenu = false;
selectedUserId = null; selectedUser = "";
} }
onMount(closeProfileMenu) onMount(closeProfileMenu)
</script> </script>
@ -49,11 +50,11 @@
<p class="message"> <p class="message">
<span <span
class="message-name" class="message-name"
on:click={openProfile(message.id)} on:click={openProfile(message.author)}
on:keydown={openProfile(message.id)} on:keydown={openProfile(message.author)}
style="cursor: pointer;" style="cursor: pointer;"
> >
{message.name} {message.author}
</span>: {message.text} </span>: {message.text}
</p> </p>
{/each} {/each}
@ -62,16 +63,16 @@
<div class="profile-menu" on:click|stopPropagation on:keydown|stopPropagation> <div class="profile-menu" on:click|stopPropagation on:keydown|stopPropagation>
<ul> <ul>
<!-- if admin <!-- if admin
<li><button on:click={() => dispatch('delete-user', selectedUserId)}>Delete User</button></li> <li><button on:click={() => dispatch('delete-user', selectedUser)}>Delete User</button></li>
<li><button on:click={() => dispatch('ban-user', selectedUserId)}>Ban User</button></li> <li><button on:click={() => dispatch('ban-user', selectedUser)}>Ban User</button></li>
<li><button on:click={() => dispatch('mute-user', selectedUserId)}>Mute User</button></li> <li><button on:click={() => dispatch('mute-user', selectedUser)}>Mute User</button></li>
<li><button on:click={() => dispatch('promote-user', selectedUserId)}>Promote User</button></li> <li><button on:click={() => dispatch('promote-user', selectedUser)}>Promote User</button></li>
--> -->
<li><button on:click={() => dispatch('send-message', selectedUserId)}>Send Message</button></li> <li><button on:click={() => dispatch('send-message', selectedUser)}>Send Message</button></li>
<li><button on:click={() => dispatch('view-profile', selectedUserId)}>View Profile</button></li> <li><button on:click={() => dispatch('view-profile', selectedUser)}>View Profile</button></li>
<li><button on:click={() => dispatch('add-friend', selectedUserId)}>Add Friend</button></li> <li><button on:click={() => dispatch('add-friend', selectedUser)}>Add Friend</button></li>
<li><button on:click={() => dispatch('invite-to-game', selectedUserId)}>Invite to Game</button></li> <li><button on:click={() => dispatch('invite-to-game', selectedUser)}>Invite to Game</button></li>
<li><button on:click={() => dispatch('block-user', selectedUserId)}>Block User</button></li> <li><button on:click={() => dispatch('block-user', selectedUser)}>Block User</button></li>
<li><button on:click={closeProfileMenu}>Close</button></li> <li><button on:click={closeProfileMenu}>Close</button></li>
</ul> </ul>
</div> </div>

26
front/volume/src/components/Friends.svelte

@ -4,22 +4,13 @@
status: "online" | "offline" | "in a game"; status: "online" | "offline" | "in a game";
ftId: number; ftId: number;
} }
</script> export async function addFriend(event: any) {
<script lang="ts">
import { API_URL } from "../Auth";
export let friends: Friend[];
export let invits: Friend[];
async function addFriend(event: any) {
console.log(typeof event); console.log(typeof event);
event.preventDefault(); event.preventDefault();
const usernameInput = event.target.querySelector('input[type="text"]'); const username = event.target ?
console.log(usernameInput); event.target.querySelector('input[type="text"]').value
: event.detail;
const username = usernameInput.value;
let response = await fetch(API_URL + "/user/" + username, { let response = await fetch(API_URL + "/user/" + username, {
credentials: "include", credentials: "include",
@ -36,11 +27,18 @@
} else { } else {
console.log("Unknown user."); console.log("Unknown user.");
} }
usernameInput.value = "";
alert("Trying to add friend: " + username); alert("Trying to add friend: " + username);
} }
</script> </script>
<script lang="ts">
import { API_URL } from "../Auth";
export let friends: Friend[];
export let invits: Friend[];
</script>
<div class="overlay"> <div class="overlay">
<div class="friends" on:click|stopPropagation on:keydown|stopPropagation> <div class="friends" on:click|stopPropagation on:keydown|stopPropagation>
<div> <div>

1
front/volume/src/components/Leaderboard.svelte

@ -3,6 +3,7 @@
username: string; username: string;
wins: number; wins: number;
losses: number; losses: number;
winrate: number;
rank: number; rank: number;
} }
</script> </script>

122
front/volume/src/components/Profile.svelte

@ -1,13 +1,15 @@
<script lang="ts"> <script lang="ts">
import { API_URL, store, logout } from "../Auth"; import { API_URL, store, logout } from "../Auth";
export let username = ""; export let edit = false
export let wins = 0; export let user = {
export let losses = 0; username: "",
export let winrate = 0; wins: 0,
export let elo = 0; losses: 0,
export let rank = -1; winrate: 0,
export let is2faEnabled = false; rank: -1,
is2faEnabled: false
}
async function handleSubmit() { async function handleSubmit() {
let response = await fetch(API_URL, { let response = await fetch(API_URL, {
@ -24,8 +26,9 @@
async function handle2fa(event: Event) { async function handle2fa(event: Event) {
event.preventDefault(); event.preventDefault();
alert("Trying to " + (is2faEnabled ? "disable" : "enable") + " 2FA"); alert("Trying to " + (user.is2faEnabled ? "disable" : "enable") + " 2FA");
} }
function submitAvatar() { function submitAvatar() {
let form: HTMLFormElement = <HTMLFormElement>( let form: HTMLFormElement = <HTMLFormElement>(
document.getElementById("upload_avatar") document.getElementById("upload_avatar")
@ -36,49 +39,44 @@
<div class="overlay"> <div class="overlay">
<div class="profile" on:click|stopPropagation on:keydown|stopPropagation> <div class="profile" on:click|stopPropagation on:keydown|stopPropagation>
<h3>===| <mark>{user.username}'s Profile</mark> |===</h3>
<div class="profile-header"> <div class="profile-header">
<form {#if edit == 0}
action={API_URL + "/avatar"} <img src={API_URL + "/avatar"} alt="avatar" class="profile-img" />
method="post" {:else}
enctype="multipart/form-data" <form
id="upload_avatar" action={API_URL + "/avatar"}
> method="post"
<div class="input-avatar"> enctype="multipart/form-data"
<label for="avatar-input"> id="upload_avatar"
<img src={API_URL + "/avatar"} alt="avatar" class="profile-img" /> >
</label> <input type="file" id="avatar-input" name="avatar" on:change={submitAvatar} />
<input </form>
type="file" <label class="img-class" for="avatar-input">
id="avatar-input" <img src={API_URL + "/avatar"} alt="avatar"/>
name="avatar" </label>
on:change={submitAvatar} {/if}
/>
</div>
</form>
</div> </div>
<div class="profile-body"> <div class="profile-body">
<form on:submit|preventDefault={handleSubmit}> <p>Wins: {user.wins}</p>
<div class="username"> <p>Losses: {user.losses}</p>
<label for="username">Username</label> <p>Winrate: {user.winrate}%</p>
<input type="text" id="username" bind:value={username} /> <p>Rank: {user.rank}</p>
<button type="submit">Submit</button> {#if edit == 1}
</div> <form id="username-form" class="username" on:submit|preventDefault={handleSubmit}>
</form> <input type="text" id="username" bind:value={user.username} />
<p>Wins: {wins}</p> <button type="submit" class="username" form="username-form">Change</button>
<p>Losses: {losses}</p> </form>
<p>Winrate: {winrate}%</p> <button type="button" on:click={handle2fa}>
<p>Elo : {elo}</p> {#if user.is2faEnabled}
<p>Rank: {rank}</p> Disable 2FA
<form class="two-factor-auth" on:submit={handle2fa}> {:else}
<button type="submit"> Enable user.2FA
{#if is2faEnabled} {/if}
Disable 2FA </button>
{:else} <button id="logout" type="button" on:click={logout}>Log Out</button>
Enable 2FA {/if}
{/if}
</button>
</form>
<button type="button" on:click={logout}>Log Out</button>
</div> </div>
</div> </div>
</div> </div>
@ -110,17 +108,39 @@
align-items: center; align-items: center;
} }
.profile-img { .profile-header {
width: 80px; width: 80px;
height: 80px; height: 80px;
margin-right: 1rem; margin:auto;
justify-content: center;
} }
.two-factor-auth { .two-factor-auth {
margin-top: 1rem; margin-top: 1rem;
} }
.input-avatar > input { #avatar-input {
display: none; display: none;
} }
.profile > h3 {
display: flex;
justify-content: center;
}
.profile-body > p {
display: flex;
justify-content: center;
}
.profile-body > img {
display: flex;
justify-content: center;
}
.username {
text-align: center;
}
#logout {
float: right;
}
</style> </style>

Loading…
Cancel
Save