Browse Source

* Made popup more rapid & checked their contents

* some narnaud fixes
master
vvandenb 2 years ago
parent
commit
5b7f842a6b
  1. 18
      back/src/chat/chat.service.ts
  2. 5
      back/src/users/users.service.ts
  3. 86
      front/src/App.svelte
  4. 27
      front/src/components/Alert/Alert.svelte
  5. 33
      front/src/components/Alert/content.ts
  6. 160
      front/src/components/Channels.svelte
  7. 59
      front/src/components/Chat.svelte
  8. 4
      front/src/components/Friends.svelte
  9. 4
      front/src/components/Profile.svelte

18
back/src/chat/chat.service.ts

@ -26,12 +26,16 @@ export class ChatService {
let newChannel: Channel
if (channel.isDM) {
if (channel.otherDMedUsername === undefined || channel.otherDMedUsername === null) {
throw new BadRequestException('No other user specified')
}
const otherUser: User | null = await this.usersService.findUserByName(
channel.otherDMedUsername
)
if (otherUser == null) throw new BadRequestException(`User #${channel.otherDMedUsername} not found`)
if (user.blocked.some((usr: User) => usr.ftId === otherUser.ftId))
if (user.blocked.some((usr: User) => usr.ftId === otherUser.ftId)) {
throw new BadRequestException(`User ${otherUser.username} is blocked`)
}
if (otherUser.id === user.id) throw new BadRequestException('Cannot DM yourself')
const channels = await this.getChannelsForUser(user.id)
@ -55,7 +59,7 @@ export class ChatService {
newChannel.isPrivate = channel.isPrivate
newChannel.password = await this.hash(channel.password)
newChannel.isDM = false
console.log("New channel: ", JSON.stringify(newChannel))
console.log('New channel: ', JSON.stringify(newChannel))
}
return await this.ChannelRepository.save(newChannel)
}
@ -74,10 +78,10 @@ export class ChatService {
async hash(password: string): Promise<string> {
if (!password) return ''
password = await bcrypt.hash(
password,
Number(process.env.HASH_SALT)
)
password = await bcrypt.hash(
password,
Number(process.env.HASH_SALT)
)
return password
}
@ -159,7 +163,7 @@ export class ChatService {
await this.ChannelRepository.save(channel)
}
async removeChannel(channelId: number): Promise<void> {
async removeChannel (channelId: number): Promise<void> {
await this.ChannelRepository.remove(await this.getFullChannel(channelId))
}

5
back/src/users/users.service.ts

@ -32,6 +32,7 @@ export class UsersService {
// WARNING: socketKey isn't removed here. it must be done before
// any return from it in a route.
async findUserByName (username: string): Promise<User> {
if (username === undefined || username === null) throw new BadRequestException('No username specified.')
const user = await this.usersRepository.findOne({
where: { username },
relations: { results: true }
@ -132,8 +133,8 @@ export class UsersService {
}
})
let r = 1
let ret: Array<User> = []
for (let usr of leaderboard.filter((user) => user.matchs !== 0)) {
const ret: User[] = []
for (const usr of leaderboard.filter((user) => user.matchs !== 0)) {
usr.rank = r++
await this.usersRepository.save(usr)
ret.push(usr)

86
front/src/App.svelte

@ -21,33 +21,81 @@
import MatchHistory from "./components/MatchHistory.svelte";
import Friends, { addFriend } from "./components/Friends.svelte";
import Chat from "./components/Chat.svelte";
import Channels, {openDirectChat} from "./components/Channels.svelte";
import Channels, { formatChannelNames, getDMs } from "./components/Channels.svelte";
import Leaderboard from "./components/Leaderboard.svelte";
import { popup } from "./components/Alert/content";
import { popup, show_popup } from "./components/Alert/content";
import Pong from "./components/Pong/Pong.svelte";
import type { ChannelsType } from "./components/Channels.svelte";
import { store, getUser, login, verify } from "./Auth";
import { store, getUser, login, verify, API_URL } from "./Auth";
import { get } from "svelte/store";
import type { CreateChannelDto } from "./components/dtos/create-channel.dto";
// Single Page Application config
let appState: string = APPSTATE.HOME;
history.replaceState({ appState: "" }, "", "/");
window.onpopstate = (e: PopStateEvent) => {
if (e.state) {
appState = e.state.appState;
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)
await formatChannelNames(DMChannel)
setAppState(APPSTATE.CHANNELS + "#" + DMChannel[0].name)
} else {
console.log("Creating DMChannel: " + get(store).username + "&" + DMUsername)
const body: CreateChannelDto = {
name: "none",
owner: get(store).ftId,
password: "",
isPrivate: true,
isDM: true,
otherDMedUsername: DMUsername
}
fetch(API_URL + "/channels", {
credentials: "include",
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
}).then(async () => {
const response = await getDMs(DMUsername)
if (response && response.ok) {
DMChannel = await response.json();
if (DMChannel.length != 0) {
await formatChannelNames(DMChannel)
setAppState(APPSTATE.CHANNELS + "#" + DMChannel[0].name)
} else {
show_popup("Error: Couldn't create DM.", false)
}
} else {
show_popup("Error: Couldn't create DM.", false)
}
}).catch(() => {
show_popup("Error: Couldn't create DM.", false)
})
}
};
function resetAppState() {
setAppState(APPSTATE.HOME);
}
// Single Page Application config
let appState: string = APPSTATE.HOME;
function setAppState(newState: APPSTATE | string) {
if (newState === appState) return;
if (newState === appState) return;
history.pushState({ appState: newState, prevState: appState }, "", newState);
appState = newState;
}
function resetAppState() {
setAppState(APPSTATE.HOME);
}
onMount(() => {
history.replaceState({ appState: "" }, "", "/");
window.onpopstate = (e: PopStateEvent) => {
if (e.state) {
appState = e.state.appState;
}
};
getUser();
});
setInterval(() => {
@ -94,7 +142,13 @@
</script>
<div>
<Modal show={$popup}>
<Modal show={$popup} transitionWindowProps={
{
duration: 200,
easing: (t) => t * (2 - t),
}
}
>
{#if $store === null}
<div class="login-div">
<h3 class="test">Please log in with 42 api to access the website.</h3>
@ -130,6 +184,7 @@
on:keydown={() => setAppState(APPSTATE.CHANNELS)}
>
<Chat
{appState}
{setAppState}
bind:channel={selectedChannel}
on:view-profile={openIdProfile}
@ -157,7 +212,6 @@
{#if appState == APPSTATE.FRIENDS}
<div on:click={resetAppState} on:keydown={resetAppState}>
<Friends
{setAppState}
on:view-profile={openIdProfile}
on:invite-to-game={pong.inviteToGame}
/>

27
front/src/components/Alert/Alert.svelte

@ -1,15 +1,23 @@
<script lang="ts">
import { onMount, onDestroy } from "svelte";
import { get } from "svelte/store";
import { content, popup } from "./content";
export let message: string;
export let form = true;
export let passwordInput = false;
export let onCancel = () => {};
export let onOkay = () => {};
import { content, popup } from "./content";
let value = '';
let onChange = () => {
let value = "";
onMount(() => {
$content = "";
};
});
onDestroy(() => {
popup.set(null);
});
function _onCancel() {
onCancel();
@ -19,14 +27,10 @@
function _onOkay() {
onOkay();
if (form)
$content = value;
else
$content = "ok"
if (form) $content = value;
else $content = "ok";
popup.set(null);
}
$: onChange();
</script>
<div>
@ -64,8 +68,7 @@
input {
width: 100%;
text-align: center;
word-wrap:break-word;
word-wrap: break-word;
}
.buttons {

33
front/src/components/Alert/content.ts

@ -5,25 +5,26 @@ export const popup = writable(null)
import { bind } from 'svelte-simple-modal';
let val;
export async function show_popup(message, form = true, passwordInput = false) {
popup.set(bind(Alert__SvelteComponent_, {
message,
form,
passwordInput
}))
await waitForCondition()
popup.set(bind(Alert__SvelteComponent_, {
message,
form,
passwordInput
}))
await waitForCondition()
}
export async function waitForCondition() {
const unsub = popup.subscribe((value) => {val = value})
async function checkFlag() {
if (val == null) {
unsub()
await new Promise(resolve => setTimeout(resolve, 100));
} else {
await new Promise(resolve => setTimeout(resolve, 1000));
return await checkFlag();
}
const unsub = popup.subscribe((value) => { val = value })
async function checkFlag() {
if (val == null) {
unsub()
await new Promise(resolve => setTimeout(resolve, 100))
} else {
await new Promise(resolve => setTimeout(resolve, 200))
return await checkFlag()
}
return await checkFlag()
}
return await checkFlag()
}

160
front/src/components/Channels.svelte

@ -1,16 +1,11 @@
<script lang="ts" context="module">
import { content, show_popup } from './Alert/content'
import { onMount } from "svelte";
import { get } from "svelte/store"
import { content, popup, show_popup } from './Alert/content'
import { onMount, tick } from "svelte";
import { API_URL, store } from "../Auth";
import type User from "./Profile.svelte";
import { APPSTATE } from "../App.svelte";
import type { CreateChannelDto } from './dtos/create-channel.dto';
import type { IdDto, PasswordDto } from './dtos/updateUser.dto';
export let appState: string;
export let setAppState: (newState: APPSTATE | string) => void;
export interface ChannelsType {
id: number;
name: string;
@ -73,64 +68,15 @@
}
export 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;
}
export 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)
await formatChannelNames(DMChannel)
setAppState(APPSTATE.CHANNELS + "#" + DMChannel[0].name)
} else {
console.log("Creating DMChannel: " + get(store).username + "&" + DMUsername)
const body: CreateChannelDto = {
name: "none",
owner: get(store).ftId,
password: "",
isPrivate: true,
isDM: true,
otherDMedUsername: DMUsername
}
fetch(API_URL + "/channels", {
credentials: "include",
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
}).then(async () => {
const response = await getDMs(DMUsername)
if (response && response.ok) {
DMChannel = await response.json();
if (DMChannel.length != 0) {
await formatChannelNames(DMChannel)
setAppState(APPSTATE.CHANNELS + "#" + DMChannel[0].name)
} else {
show_popup("Error: Couldn't create DM.", false)
}
} else {
show_popup("Error: Couldn't create DM.", false)
}
}).catch(() => {
show_popup("Error: Couldn't create DM.", false)
})
}
const res = await fetch(API_URL + "/channels/dms/" + username, {
credentials: "include",
mode: "cors",
})
if (res.ok)
return res;
else
return null;
}
</script>
<script lang="ts">
@ -162,48 +108,49 @@
export let onSelectChannel: (channel: ChannelsType) => void;
const createChannel = async () => {
let name: string;
let password = "";
await show_popup("Enter a name for the new channel:")
name = $content;
const name: string = $content;
if (name === "") return;
if (name.includes("#")) {
await show_popup("Channel name cannot contain #", false)
await show_popup("Channel name cannot contain #", false)
return;
}
if (name) {
if (channelMode === 'protected'){
await show_popup("Enter a password for the new channel:", true, true)
password = $content
if (password == "") {
await show_popup("Password is required #", false)
return ;
}
}
name = "🚪 " + name;
const body: CreateChannelDto = {
name: name,
owner: $store.ftId,
password: password,
isPrivate: channelMode === "private",
isDM: false,
otherDMedUsername: "",
};
const response = await fetch(API_URL + "/channels", {
credentials: "include",
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!response.ok) {
const error = await response.json();
await show_popup(error.message, false)
if (channels.some((chan) => chan.name === name)) {
await show_popup("A channel with this name already exist", false)
return;
}
if (channelMode === 'protected'){
await show_popup("Enter a password for the new channel:", true, true)
password = $content
if (password == "") {
await show_popup("Password is required #", false)
return ;
}
getChannels()
} else await show_popup("Channel name is required", false)
}
const editedName = "🚪 " + name;
const body: CreateChannelDto = {
name: editedName,
owner: $store.ftId,
password: password,
isPrivate: channelMode === "private",
isDM: false,
otherDMedUsername: "",
};
const response = await fetch(API_URL + "/channels", {
credentials: "include",
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!response.ok) {
const error = await response.json();
await show_popup(error.message, false)
}
getChannels()
};
//--------------------------------------------------------------------------------/
@ -228,8 +175,9 @@
const inviteChannel = async (id: number) => {
await show_popup("Enter the username of the user you want to invite");
let string = $content
const response = await fetch(API_URL + "/users/" + string + "/byname", {
const username = $content;
if (username === "") return;
const response = await fetch(API_URL + "/users/" + username + "/byname", {
credentials: "include",
method: "GET",
mode: "cors",
@ -265,8 +213,10 @@
const changePassword = async (id: number) => {
await show_popup("Enter the new password for this channel (leave empty to remove password) :", true, true);
const newPassword = $content;
if (newPassword === "") return;
const body: PasswordDto = {
password: $content
password: newPassword
}
const response = await fetch(API_URL + "/channels/" + id + "/password", {
credentials: "include",
@ -280,8 +230,10 @@
if (!response.ok) {
const error = await response.json();
await show_popup(error.message, false)
} else
} else {
getChannels()
await show_popup("Password updated", false)
}
};
//--------------------------------------------------------------------------------/

59
front/src/components/Chat.svelte

@ -5,18 +5,17 @@
import { show_popup, content } from "./Alert/content";
import { APPSTATE } from "../App.svelte";
import type User from "./Profile.svelte";
import type { ChannelsType, ChatMessage, ChatMessageServer } from "./Channels.svelte";
import { formatChannelNames, type ChannelsType, type ChatMessage, type ChatMessageServer } from "./Channels.svelte";
import type { IdDto, MuteDto } from "./dtos/updateUser.dto";
import type { ConnectionDto } from "./dtos/connection.dto";
import type { CreateMessageDto } from "./dtos/create-message.dto";
import type { kickUserDto } from "./dtos/kickUser.dto";
</script>
<script lang="ts">
export let channel: ChannelsType;
export let messages: Array<ChatMessage> = [];
export let appState: string;
export let setAppState: (newState: APPSTATE | string) => void;
let socket: Socket;
@ -25,10 +24,30 @@
let blockedUsers: Array<User> = [];
let chatMembers: Array<User> = [];
async function getCurrentChannel() {
const res = await fetch(API_URL + "/channels", {
credentials: "include",
mode: "cors",
});
if (res.ok) {
const newChannels: Array<ChannelsType> = await res.json();
await formatChannelNames(newChannels);
newChannels.forEach((newChannel) => {
const urlSplit = appState.split("#", 2)
if (urlSplit.length > 1) {
const currentChannelName = appState.split("#", 2)[1];
if (newChannel.name === currentChannelName) {
channel = newChannel;
}
}
});
}
}
onMount(async () => {
socket = io(API_URL);
socket.connect();
await getFullChannel();
await getCurrentChannel();
if (!channel) setAppState(APPSTATE.CHANNELS);
if (!channel.password) {
const data: ConnectionDto = {
@ -39,10 +58,15 @@
socket.emit("joinChannel", data);
} else {
await show_popup("Channel is protected, enter password:", true, true);
const password = $content
if (password === "") {
setAppState(APPSTATE.CHANNELS);
return
}
const data: ConnectionDto = {
UserId: $store.ftId,
ChannelId: channel.id,
pwd: $content,
pwd: password,
};
socket.emit("joinChannel", data);
}
@ -84,21 +108,11 @@
setAppState(APPSTATE.HOME);
})
console.log("Try to join channel: ", $store.ftId, channel.id, $content);
console.log("Try to join channel: ", $store.ftId, channel.id);
});
const dispatch = createEventDispatcher();
async function getFullChannel() {
const response = await fetch(API_URL + "/channels/" + channel.id, {
credentials: "include",
mode: "cors",
});
if (response.ok)
channel = await response.json();
else
console.log("Error while refreshing channel");
}
async function getMembers() {
if (!channel) return;
let res = await fetch(API_URL + "/users/blocked/", {
@ -155,7 +169,7 @@
"Enter a time for which the user will be banned from this channel"
);
const duration = $content;
if (duration == "") return;
if (duration === "") return
const body: MuteDto = {
data: [target.ftId, duration]
}
@ -208,6 +222,8 @@
const muteUser = async (username: string) => {
await show_popup("Enter mute duration in seconds");
const muteDuration = $content;
if (muteDuration === "") return;
let response = await fetch(API_URL + "/users/" + username + "/byname", {
credentials: "include",
mode: "cors",
@ -215,7 +231,7 @@
const target = await response.json();
if (response.ok) {
const body: MuteDto = {
data: [target.ftId, +$content]
data: [target.ftId, muteDuration]
}
response = await fetch(API_URL + "/channels/" + channel.id + "/mute", {
credentials: "include",
@ -355,11 +371,8 @@
<button on:click={() => banUser(member.username)}> ban </button>
<button on:click={() => kickUser(member.username)}> kick </button>
<button on:click={() => muteUser(member.username)}> mute </button>
{#if channel.admins.some((usr) => usr.username == member.username)}
<button on:click={() => removeAdminUser(member.username)}> demote </button>
{:else}
<button on:click={() => adminUser(member.username)}> promote </button>
{/if}
<button on:click={() => removeAdminUser(member.username)}> demote </button>
<button on:click={() => adminUser(member.username)}> promote </button>
</p>
</li>
{/each}

4
front/src/components/Friends.svelte

@ -2,10 +2,8 @@
import { onMount, onDestroy } from "svelte";
import { API_URL, store } from "../Auth";
import { show_popup } from "./Alert/content";
import type { APPSTATE } from "../App.svelte";
import { createEventDispatcher } from "svelte";
export interface Friend {
username: string;
status: "online" | "offline" | "in a game";
@ -33,10 +31,8 @@
</script>
<script lang="ts">
const dispatch = createEventDispatcher();
export let setAppState: (newState: APPSTATE | string) => void;
let friends: Friend[] = [];
let invits: Friend[] = [];
let friendsInterval: ReturnType<typeof setInterval>;

4
front/src/components/Profile.svelte

@ -155,7 +155,7 @@
<div class="profile" on:click|stopPropagation on:keydown|stopPropagation>
<h3>===| <mark>{username}'s Profile</mark> |===</h3>
<div class="profile-header">
{#if edit}
{#if !edit}
<img src={`${API_URL}/users/${user.ftId}/avatar`} alt="avatar" class="profile-img" />
{:else}
<form
@ -184,7 +184,7 @@
<div class="profile-body">
{#if !edit}
<p>
<button on:click={() => dispatch("send-message")}>Send PM</button>
<button on:click={() => dispatch("send-message", username)}>Send PM</button>
<button on:click={() => dispatch("invite-to-game", username)}>
Invite to Game
</button>

Loading…
Cancel
Save