Browse Source

front

master
Pheuw1 2 years ago
parent
commit
da76ca3f17
  1. 4
      back/volume/src/chat/chat.service.ts
  2. 28
      back/volume/src/users/users.controller.ts
  3. 38
      front/volume/package-lock.json
  4. 3
      front/volume/package.json
  5. 81
      front/volume/src/components/Channels.svelte
  6. 334
      front/volume/src/components/Chat.svelte

4
back/volume/src/chat/chat.service.ts

@ -1,4 +1,4 @@
import { Inject, Injectable, NotFoundException } from '@nestjs/common' import { Injectable, NotFoundException } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm' import { InjectRepository } from '@nestjs/typeorm'
import { Repository } from 'typeorm' import { Repository } from 'typeorm'
@ -69,7 +69,7 @@ export class ChatService {
channel.muted = channel.muted.filter((data) => { channel.muted = channel.muted.filter((data) => {
return data[0] - Date.now() > 0 return data[0] - Date.now() > 0
}) })
this.ChannelRepository.save(channel) void this.ChannelRepository.save(channel)
}) })
} }

28
back/volume/src/users/users.controller.ts

@ -17,9 +17,10 @@ import {
import { FileInterceptor } from '@nestjs/platform-express' import { FileInterceptor } from '@nestjs/platform-express'
import { diskStorage } from 'multer' import { diskStorage } from 'multer'
import { type User } from './entity/user.entity' import { type User } from "./entity/user.entity";
import { UsersService } from './users.service' import { UsersService } from "./users.service";
import { UserDto, AvatarUploadDto } from './dto/user.dto' import { UserDto, AvatarUploadDto } from "./dto/user.dto";
import { PongService } from "src/pong/pong.service";
import { AuthenticatedGuard } from 'src/auth/42-auth.guard' import { AuthenticatedGuard } from 'src/auth/42-auth.guard'
import { Profile42 } from 'src/auth/42.decorator' import { Profile42 } from 'src/auth/42.decorator'
@ -36,20 +37,19 @@ export class UsersController {
@Post('block/:id') @Post('block/:id')
@UseGuards(AuthenticatedGuard) @UseGuards(AuthenticatedGuard)
async blockUser ( @Post("block/:id")
@Profile42() profile: Profile, async blockUser(@Profile42() profile :Profile, @Param('id') id:number) {
@Param('id', ParseIntPipe) id: number const user = await this.usersService.findUser(id) as User
) {
const user = (await this.usersService.findUser(id)) as User
user.blocked.push((await this.usersService.findUser(+profile.id)) as User) user.blocked.push((await this.usersService.findUser(+profile.id)) as User)
this.usersService.save(user) this.usersService.save(user)
} }
@Post('unblock/:id') @Post('unblock/:id')
@UseGuards(AuthenticatedGuard) @UseGuards(AuthenticatedGuard)
async unblockUser (@Param('id', ParseIntPipe) id: number) { @Post("unblock/:id")
const user = (await this.usersService.findUser(id)) as User async unblockUser(@Profile42() profile :Profile, @Param('id') id:number) {
user.blocked = user.blocked.filter((usr: User) => { const user = await this.usersService.findUser(id) as User
user.blocked = user.blocked.filter((usr: User) => {
return usr.id !== id return usr.id !== id
}) })
this.usersService.save(user) this.usersService.save(user)
@ -100,10 +100,10 @@ export class UsersController {
} }
}) })
) )
@ApiConsumes('multipart/form-data') @ApiConsumes("multipart/form-data")
@ApiBody({ @ApiBody({
description: 'A new avatar for the user', description: "A new avatar for the user",
type: AvatarUploadDto type: AvatarUploadDto,
}) })
async changeAvatar ( async changeAvatar (
@Profile42() profile: Profile, @Profile42() profile: Profile,

38
front/volume/package-lock.json

@ -18,7 +18,8 @@
}, },
"devDependencies": { "devDependencies": {
"prettier": "^2.8.4", "prettier": "^2.8.4",
"svelte-check": "^2.10.3" "svelte-check": "^2.10.3",
"svelte-select": "^5.5.2"
} }
}, },
"node_modules/@esbuild/android-arm": { "node_modules/@esbuild/android-arm": {
@ -351,6 +352,21 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/@floating-ui/core": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.4.tgz",
"integrity": "sha512-SQOeVbMwb1di+mVWWJLpsUTToKfqVNioXys011beCAhyOIFtS+GQoW4EQSneuxzmQKddExDwQ+X0hLl4lJJaSQ==",
"dev": true
},
"node_modules/@floating-ui/dom": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.2.4.tgz",
"integrity": "sha512-4+k+BLhtWj+peCU60gp0+rHeR8+Ohqx6kjJf/lHMnJ8JD5Qj6jytcq1+SZzRwD7rvHKRhR7TDiWWddrNrfwQLg==",
"dev": true,
"dependencies": {
"@floating-ui/core": "^1.2.3"
}
},
"node_modules/@jridgewell/resolve-uri": { "node_modules/@jridgewell/resolve-uri": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
@ -1340,6 +1356,16 @@
"svelte": "^3.24.0" "svelte": "^3.24.0"
} }
}, },
"node_modules/svelte-floating-ui": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/svelte-floating-ui/-/svelte-floating-ui-1.2.8.tgz",
"integrity": "sha512-8Ifi5CD2Ui7FX7NjJRmutFtXjrB8T/FMNoS2H8P81t5LHK4I9G4NIs007rLWG/nRl7y+zJUXa3tWuTjYXw/O5A==",
"dev": true,
"dependencies": {
"@floating-ui/core": "^1.1.0",
"@floating-ui/dom": "^1.1.0"
}
},
"node_modules/svelte-hmr": { "node_modules/svelte-hmr": {
"version": "0.15.1", "version": "0.15.1",
"resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.1.tgz", "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.1.tgz",
@ -1426,6 +1452,16 @@
"sourcemap-codec": "^1.4.8" "sourcemap-codec": "^1.4.8"
} }
}, },
"node_modules/svelte-select": {
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/svelte-select/-/svelte-select-5.5.2.tgz",
"integrity": "sha512-uvTOJyrrD5OnSCAv9A+gLtIeeYHL5PUw3YpnuBkmfp7a3IHUyjgNNM4GIf462Lh9QEEEM3nD0R0E+G/Jp1iq1w==",
"dev": true,
"dependencies": {
"@floating-ui/dom": "^1.2.1",
"svelte-floating-ui": "1.2.8"
}
},
"node_modules/to-regex-range": { "node_modules/to-regex-range": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",

3
front/volume/package.json

@ -12,7 +12,8 @@
}, },
"devDependencies": { "devDependencies": {
"prettier": "^2.8.4", "prettier": "^2.8.4",
"svelte-check": "^2.10.3" "svelte-check": "^2.10.3",
"svelte-select": "^5.5.2"
}, },
"dependencies": { "dependencies": {
"@sveltejs/vite-plugin-svelte": "^2.0.2", "@sveltejs/vite-plugin-svelte": "^2.0.2",

81
front/volume/src/components/Channels.svelte

@ -1,4 +1,5 @@
<script lang="ts" context="module"> <script lang="ts" context="module">
export interface ChannelsType { export interface ChannelsType {
id: number; id: number;
name: string; name: string;
@ -9,10 +10,14 @@
import { onMount } from "svelte"; import { onMount } from "svelte";
import { API_URL, store } from "../Auth"; import { API_URL, store } from "../Auth";
import { io } from "../socket"; import { io } from "../socket";
import Select from 'svelte-select';
</script> </script>
<script lang="ts"> <script lang="ts">
//--------------------------------------------------------------------------------/ //--------------------------------------------------------------------------------/
let channelMode = "";
const channelOptions = ['public','private','direct'];
const joinChannel = async (id: number) => { const joinChannel = async (id: number) => {
io.emit("joinChannel", id, $store.ftId); io.emit("joinChannel", id, $store.ftId);
@ -21,7 +26,6 @@
let channels: Array<ChannelsType> = []; let channels: Array<ChannelsType> = [];
onMount(async () => { onMount(async () => {
const res = await fetch(API_URL + "/channels", { const res = await fetch(API_URL + "/channels", {
cors: "include",
credentials: "include", credentials: "include",
mode: "cors", mode: "cors",
}); });
@ -44,15 +48,9 @@
const createChannel = async () => { const createChannel = async () => {
const name = prompt("Enter a name for the new channel:"); const name = prompt("Enter a name for the new channel:");
if (name) { if (name) {
const privacy = prompt(
"Enter a privacy setting for the new channel (public/private):"
);
if (privacy !== "public" && privacy !== "private") {
alert("Invalid privacy setting");
return;
}
let password = ""; let password = "";
password = prompt("Enter a password for the new channel:"); if (channelMode !== 'direct')
password = prompt("Enter a password for the new channel:");
const response = await fetch(API_URL + "/channels", { const response = await fetch(API_URL + "/channels", {
credentials: "include", credentials: "include",
method: "POST", method: "POST",
@ -64,7 +62,7 @@
name: name, name: name,
owner: $store.ftId, owner: $store.ftId,
password: password, password: password,
isPrivate: privacy === "private", isPrivate: channelMode === "private",
}), }),
}); });
if (response.ok) { if (response.ok) {
@ -157,20 +155,31 @@
{#each channels.slice(0, 10) as _channels} {#each channels.slice(0, 10) as _channels}
<li> <li>
<span>{_channels.name}</span> <span>{_channels.name}</span>
<button on:click={() => selectChat(_channels.id)}>Enter</button> <button on:click={() => selectChat(_channels.id)}>🔌</button>
<button <button
on:click={() => removeChannel(_channels.id)} on:click={() => removeChannel(_channels.id)}
on:keydown={() => removeChannel(_channels.id)}>delete</button on:keydown={() => removeChannel(_channels.id)}>🗑️</button
> >
<button on:click={() => inviteChannel(_channels.id)}>invite</button> <button on:click={() => inviteChannel(_channels.id)}>🤝</button>
<button on:click={() => changePassword(_channels.id)} <button on:click={() => changePassword(_channels.id)}
>Set - Change - Remove Password</button >Edit Password</button
> >
</li>{/each} </li>{/each}
{:else} {:else}
<p>No channels available</p> <p>No channels available</p>
{/if} {/if}
<button on:click={createChannel}>Create Channel</button> <div>
<select bind:value={channelMode} >
{#each channelOptions as option}
<option value={option} selected={channelMode === option}>
{option}
</option>
{/each}
</select>
{#if channelMode!= ''}
<button class="button" on:click={createChannel}>Create Channel</button>
{/if}
</div>
</div> </div>
</div> </div>
</div> </div>
@ -194,4 +203,44 @@
padding: 1rem; padding: 1rem;
width: 300px; width: 300px;
} }
select {
width: 100%;
height: 15%;
padding: 5px;
border-radius: 4px;
background: #eee;
border: none;
outline: grey;
display: inline-block;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
cursor: pointer;
}
.button {
color: white;
margin:0 auto;
margin: auto;
width: 45%;
height: 15%;
padding: 5px;
border-radius: 4px;
background: #6B8E23;
border: none;
outline: grey;
display:block;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
cursor: pointer;
}
span {
color: rgb(0, 0, 0);
font-size: 150%; /* Taille de la police en pourcentage */
position: relative; /* Positionnement relatif */
padding: 10px;
top: 2px;
}
</style> </style>

334
front/volume/src/components/Chat.svelte

@ -1,34 +1,30 @@
<script lang="ts" context="module"> <script lang="ts" context="module">
export interface chatMessagesType { export interface chatMessagesType {
id: number; id: number;
author: string; author: string;
text: string; text: string;
} }
import { createEventDispatcher, onDestroy, onMount } from "svelte"; import { createEventDispatcher, onDestroy, onMount } from "svelte";
import { store, API_URL } from "../Auth"; import { store, API_URL } from "../Auth";
import { io } from "../socket"; import { io } from "../socket"
import type { ChannelsType } from "./Channels.svelte"; import type { ChannelsType } from "./Channels.svelte";
import type User from "./Profile.svelte"; import type User from "./Profile.svelte";
</script> </script>
//--------------------------------------------------------------------------------/
<script lang="ts"> <script lang="ts">
let blockedUsers: Array<User> = [];
let chatMembers: Array<User> = [];
let chatMessages: Array<chatMessagesType> = [];
export let channel: ChannelsType;
let newText = "";
onMount(async () => {
let res = await fetch(API_URL + "/users/" + $store.ftId + "/blocked", {
credentials: "include",
mode: "cors",
});
if (res.ok) blockedUsers = await res.json();
res = await fetch(API_URL + "/channels/" + channel.id + "/members", { let blockedUsers: Array<User> = [];
credentials: "include", let chatMembers: Array<User> = [];
mode: "cors", let chatMessages: Array<chatMessagesType> = [];
}); export let channel: ChannelsType;
if (res.ok) chatMembers = await res.json(); let newText = "";
onMount(async () => {
let res = await fetch(API_URL + "/users/" + $store.ftId + "/blocked", {
credentials: "include",
mode: "cors",
});
if (res.ok) blockedUsers = await res.json();
io.on("messages", (msgs: Array<chatMessagesType>) => { io.on("messages", (msgs: Array<chatMessagesType>) => {
chatMessages = msgs; chatMessages = msgs;
@ -228,159 +224,161 @@
//--------------------------------------------------------------------------------/ //--------------------------------------------------------------------------------/
</script> </script>
<div class="overlay"> <div class="overlay" >
<div class="chat" on:click|stopPropagation on:keydown|stopPropagation> <div class="chat" on:click|stopPropagation on:keydown|stopPropagation>
<div class="messages"> <div class="messages" >
{#each chatMessages as message} { #each chatMessages as message }
<p class="message"> <p class="message" >
{#if !blockedUsers.filter((user) => user.username == message.author).length} { #if !blockedUsers.filter((user) => user.username == message.author).length }
<span <span
class="message-name" class="message-name"
on:click={() => openProfile(message.author)} on:click = {() => openProfile(message.author)}
on:keydown={() => openProfile(message.author)} on:keydown = {() => openProfile(message.author)}
style="cursor: pointer;" style = "cursor: pointer;"
> >
{message.author} { message.author }
</span>: {message.text} </span>: {message.text}
{/if} {
</p> /if}
{/each} </p>
</div> {
{#if showProfileMenu} /each}
<div </div>
{ #if showProfileMenu }
<div
class="profile-menu" class="profile-menu"
on:click|stopPropagation on:click|stopPropagation
on:keydown|stopPropagation on:keydown|stopPropagation
> >
<ul> <ul>
<li> <li>
<button on:click={() => dispatch("send-message", selectedUser)}> <button on:click = {() => dispatch("send-message", selectedUser)
Send Message }
</button> > Send Message </button
</li> >
<li> </li>
<button on:click={() => dispatch("view-profile", selectedUser)}> <li>
View Profile <button on:click = {() => dispatch("view-profile", selectedUser)
</button> }
</li> > View Profile </button
<li> >
<button on:click={() => dispatch("add-friend", selectedUser)}> </li>
Add Friend <li>
</button> <button on:click = {() => dispatch("add-friend", selectedUser)}
</li> > Add Friend </button
<li> >
<button on:click={() => dispatch("invite-to-game", selectedUser)}> </li>
Invite to Game <li>
</button> <button on:click = {() => dispatch("invite-to-game", selectedUser)}
</li> > Invite to Game </button
<li> >
{#if !blockedUsers.filter((user) => (user.username = selectedUser)).length} </li>
<button on:click={() => blockUser(selectedUser)}> <li>
Block User { #if !blockedUsers.filter((user) => user.username = selectedUser).length }
</button> <button on:click = {() => blockUser(selectedUser)}> Block User </button>
{:else} {:else }
<button on:click={() => unblockUser(selectedUser)}> <button on:click = {() => unblockUser(selectedUser)}> Unblock User </button>
Unblock User {
</button> /if}
{/if} </li>
</li> <li> <button on:click = { closeProfileMenu } > Close </button></li >
<li><button on:click={closeProfileMenu}> Close </button></li> </ul>
</ul> </div>
</div> {
{/if} /if}
<form on:submit|preventDefault={sendMessage}> <form on:submit|preventDefault={ sendMessage }>
<input type="text" placeholder="Type a message..." bind:value={newText} /> <input type="text" placeholder = "Type a message..." bind:value={ newText } />
<button> <button>
<img src="img/send.png" alt="send" /> <img src="img/send.png" alt = "send" />
</button> </button>
</form> </form>
<button <button
on:click|stopPropagation={toggleChatMembers} on:click|stopPropagation={ toggleChatMembers }
on:keydown|stopPropagation on:keydown|stopPropagation > Chat Members </button
> >
Chat Members { #if showChatMembers }
</button> <div
{#if showChatMembers} class="chatMembers"
<div on:click|stopPropagation
class="chatMembers" on:keydown|stopPropagation
on:click|stopPropagation >
on:keydown|stopPropagation <div>
> <ul>
<div> { #each chatMembers as member }
<ul> <li>
{#each chatMembers as member} <p>
<li> { member.username }
<p> <button on:click = {() => banUser(member.username)
{member.username} }> ban </button>
<button on:click={() => banUser(member.username)}> <button on:click = {() => kickUser(member.username)
ban }
</button> > kick </button
<button on:click={() => kickUser(member.username)}> >
kick <button on:click = {() => muteUser(member.username)}
</button> > mute </button
<button on:click={() => muteUser(member.username)}> >
mute <button on:click = {() => adminUser(member.username)}
</button> > promote </button
<button on:click={() => adminUser(member.username)}> >
promote <button on:click = {() => removeAdminUser(member.username)}
</button> > demote </button
<button on:click={() => removeAdminUser(member.username)}> >
demote </p>
</button> <p>
</p> -----------------------------------------------------------------------------------
<p> </p>
----------------------------------------------------------------------------------- </li>
</p> {
</li> /each}
{/each} </ul>
</ul> </div>
</div> </div>
</div> {
{/if} /if}
</div> </div>
</div> </div>
<style> <style>
.overlay { .overlay {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: rgba(0, 0, 0, 0.5); background-color: rgba(0, 0, 0, 0.5);
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.chat { .chat {
background-color: #fff; background-color: #fff;
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 5px; border-radius: 5px;
padding: 1rem; padding: 1rem;
width: 300px; width: 300px;
} }
.messages { .messages {
height: 200px; height: 200px;
overflow-y: scroll; overflow-y: scroll;
} }
.chatMembers { .chatMembers {
position: absolute; position: absolute;
background-color: #fff; background-color: #fff;
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 5px; border-radius: 5px;
padding: 1rem; padding: 1rem;
max-height: 100px; max-height: 100px;
overflow-y: scroll; overflow-y: scroll;
} }
.chatMembers ul { .chatMembers ul {
list-style: none; list-style: none;
padding: 0; padding: 0;
margin: 0; margin: 0;
} }
.chatMembers button { .chatMembers button {
width: 6rem; width: 6rem;

Loading…
Cancel
Save