Merge branch 'dev' into features/message_route

This commit is contained in:
Nabil Ould Hamou 2024-12-04 14:04:37 +01:00 committed by GitHub
commit aff8d0c067
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 151 additions and 39 deletions

View file

@ -1,5 +1,4 @@
<script lang="ts">
import { onMount } from "svelte";
import Alert from "$lib/components/ui/Alert.svelte"; // Importer le composant Alert
export let show = false;
@ -9,6 +8,8 @@
let showAlert = false;
let alertMessage = "";
export let socket;
let chatName = "";
const createChat = async () => {
@ -27,12 +28,15 @@
const data = await response.json();
alertMessage = `Le chat "${data.name}" a été créé avec succès.`;
chatName = ""; // Réinitialiser
socket.emit("new-channel", data);
onClose?.(); // Fermer le composant après création
} else {
alertMessage = "Une erreur est survenue lors de la création du chat.";
response.json().then(error => {
alertMessage = error.error;
});
}
} catch (err) {
alertMessage = "Erreur réseau ou serveur.";
alertMessage = err;
}
showAlert = true;
@ -42,10 +46,6 @@
}
};
const closeAlert = () => {
showAlert = false;
};
// Fonction pour détecter le clic en dehors
let createChatRef: HTMLElement | null = null;
@ -75,7 +75,7 @@
</div>
{/if}
<Alert show={showAlert} message={alertMessage} onClose={closeAlert} />
<Alert show={showAlert} message={alertMessage} onClose={() => (showAlert = false)} />
<style>
.fixed {

View file

@ -1,4 +1,6 @@
<script>
import Button from '$lib/components/ui/button/button.svelte';
export let user = {
pseudo: '',
prenom: '',
@ -7,8 +9,24 @@
profilePictureUrl: '', // Ajouter l'URL de l'image de profil
}; // Infos utilisateur
export let show = false; // Contrôle si la carte est visible
export let onClose = () => {
}; // Fonction pour fermer la carte
export let onClose = () => {}; // Fonction pour fermer la carte
const disconnect = async () => {
try {
// Envoyer une requête POST à l'endpoint /disconnect
const response = await fetch('/disconnect', {
method: 'POST',
});
// Vérifier si la déconnexion a réussi (ici, on se base sur le code de redirection)
if (response.redirected) {
// Si la redirection est effectuée, vous pouvez rediriger manuellement côté client
window.location.href = response.url;
}
} catch (error) {
console.error('Erreur lors de la déconnexion:', error);
}
};
</script>
{#if show}
@ -21,6 +39,7 @@
</div>
<p>{user.prenom} {user.nom}</p>
<p>{user.description}</p>
<Button on:click={disconnect}>Deconnecter</Button>
</div>
</div>
{/if}

17
src/lib/stores/socket.ts Normal file
View file

@ -0,0 +1,17 @@
import { io } from "socket.io-client";
// Initialisation de la socket
export const initSocket = () => {
const socketInstance = io("http://localhost:5173");
// Événements globaux de connexion
socketInstance.on("connect", () => {
console.log("Connected to Socket.IO server:", socketInstance.id);
});
socketInstance.on("disconnect", () => {
console.log("Disconnected from Socket.IO server");
});
return socketInstance;
}

View file

@ -1,6 +1,9 @@
import { redirect } from '@sveltejs/kit';
import { initSocket } from "$lib/stores/socket";
export async function load({ locals, url }) {
const token = locals.token;
if (token == undefined && url.pathname !== "/") {

View file

@ -1,5 +1,5 @@
import { type Actions } from '@sveltejs/kit';
import { redirect, error } from '@sveltejs/kit';
import { redirect, error, fail } from '@sveltejs/kit';
import logger from '$lib/logger';
export async function load({locals}) {
@ -9,7 +9,7 @@ export async function load({locals}) {
}
export const actions: Actions = {
login: async ({request, fetch, cookies, locals}) => {
login: async ({request, fetch, cookies}) => {
const formData = await request.formData();
const response = await fetch('/api/auth/login', {
@ -38,8 +38,7 @@ export const actions: Actions = {
return redirect(302, "/chats");
} else {
return error(400, data.message);
return fail(400, { error: data.message });
}
},
@ -65,8 +64,7 @@ export const actions: Actions = {
return redirect(302, "/chats");
} else {
return error(400, data.message);
return fail(400, { error: data.message });
}
}
}

View file

@ -1,10 +1,27 @@
<script lang="ts">
import Alert from "$lib/components/ui/Alert.svelte";
import { Label } from "$lib/components/ui/label";
import { Button } from "$lib/components/ui/button";
import { Input } from "$lib/components/ui/input";
import * as Card from "$lib/components/ui/card";
import { enhance } from '$app/forms';
import * as Tabs from "$lib/components/ui/tabs";
let { data, form } = $props();
import { writable } from 'svelte/store';
const showAlert = writable(false);
const alertMessage = writable("");
$effect(() => {
// Manipuler l'état via les stores, ce qui est plus réactif
if (form?.error) {
alertMessage.set(form.error);
showAlert.set(true);
} else {
showAlert.set(false);
}
});
</script>
@ -70,3 +87,5 @@
</Tabs.Root>
</div>
<Alert message={$alertMessage} show={$showAlert} onClose={() => ($showAlert = false)} />

View file

@ -26,7 +26,7 @@ export async function POST({request}) {
logger.debug(`Found user with email (${email}) in database`);
try {
if (await argon2.verify(user.password, password)) {
logger.debug(`Password for user ${user.email} is correct.`);
// @ts-ignore
const token = jwt.sign(user, process.env.JWT_SECRET, { expiresIn: "1h" });
logger.debug(`Generated a JWT token for user ${user.email}.`)
@ -39,7 +39,7 @@ export async function POST({request}) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
logger.error(e);
return error(500, {message: "Erreur interne."});
return error(500, {message: e.body.message});
}

View file

@ -18,12 +18,24 @@ export async function GET({ params, url }) {
include: {
messages: {
take: 1, // Récupère le dernier message
orderBy: { createdAt: 'desc' }, // Trie par date décroissante
orderBy: { createdAt: 'desc' },// Trie par date décroissante
// as lastMessage not list last message
},
},
});
console.log(canaux);
canaux = canaux.map((canaux) => {
return {
...canaux,
lastMessage: canaux.messages.length > 0 ? canaux.messages[0] : null,
messages: undefined
};
});
canaux = sortChannels(canaux);
console.log(canaux);
return json(canaux);
@ -50,7 +62,16 @@ export async function GET({ params, url }) {
},
});
canaux = canaux.map((canaux) => {
return {
...canaux,
lastMessage: canaux.messages.length > 0 ? canaux.messages[0] : null,
messages: undefined
};
});
canaux = sortChannels(canaux);
console.log(canaux);
logger.debug('Caching channels with EX of 3600 secs');
await redisClient.set('channels', JSON.stringify(canaux), { EX: 3600 });
@ -69,7 +90,7 @@ export async function POST({ request }) {
const { name } = await request.json();
try {
const canal = await prisma.channel.create({
let canal = await prisma.channel.create({
data: {
name
},
@ -79,13 +100,16 @@ export async function POST({ request }) {
const cachedChanels = await redisClient.get('channels');
let channels = cachedChanels != null ? JSON.parse(cachedChanels) : [];
console.log(channels);
console.log(canal);
canal = {
...canal,
lastMessage: null,
messages: undefined
}
channels.push(canal);
channels = sortChannels(channels);
console.log(channels);
logger.debug(`Added channel (${canal.id}) to channels cache.`);
await redisClient.set('channels', JSON.stringify(channels), { EX: 600 });
@ -100,11 +124,16 @@ export async function POST({ request }) {
}
function sortChannels(channels) {
return channels.sort((a, b) => {
// Vérifie si 'a.messages' existe et est un tableau, sinon utilise la date de création du canal
const lastMessageA = Array.isArray(a.messages) && a.messages.length > 0 ? a.messages[0]?.createdAt : a.createdAt;
const lastMessageB = Array.isArray(b.messages) && b.messages.length > 0 ? b.messages[0]?.createdAt : b.createdAt;
channels = channels.map((channel) => {
return {
...channel,
lastUpdate : channel.lastMessage != null ? channel.lastMessage.createdAt : channel.createdAt
};
});
return new Date(lastMessageB).getTime() - new Date(lastMessageA).getTime();
return channels.sort((a, b) => {
return new Date(b.lastUpdate) - new Date(a.lastUpdate);
});
}

View file

@ -6,7 +6,7 @@
import ProfileCard from "$lib/components/ui/ProfileCard.svelte"; // Importer le composant ProfileCard
import CreateChat from "$lib/components/ui/CreateChat.svelte"; // Importer le composant CreateChat
import { formatDistanceToNow } from "$lib/utils/date.js";
import { io } from 'socket.io-client';
import { initSocket } from "$lib/stores/socket";
let showProfileCard = false; // État pour afficher ou masquer le ProfileCard
let showCreateChat = false; // État pour afficher ou masquer CreateChat
@ -18,6 +18,12 @@
profilePictureUrl: 'path/to/profile-picture.jpg', // URL de l'image de profil
};
let socket = initSocket(); // Initialiser le socket
socket.on("new-channel", (channel) => {
channels = [channel, ...channels];
});
function openProfileCard() {
console.log('openProfileCard');
showProfileCard = true; // Inverser l'état pour afficher/masquer le ProfilCard
@ -85,12 +91,17 @@
<div class="flex flex-col gap-4 overflow-y-auto">
{#each channels as channel}
<ChatItem id={channel.id} title={channel.name} lastMessage={channel.lastMessage} time={formatDistanceToNow(channel.createdAt)} />
<ChatItem
id={channel.id}
title={channel.name}
lastMessage={channel.lastMessage ? channel.lastMessage.text : "Ecrire le premier message"}
time={formatDistanceToNow(channel.lastUpdate)}
/>
{/each}
</div>
</div>
<CreateChat show={showCreateChat} onClose={closeCreateChat} />
<CreateChat show={showCreateChat} socket={socket} onClose={closeCreateChat} />
<ProfileCard {user} show={showProfileCard} onClose={closeProfileCard} />
<style>

View file

@ -1,4 +1,4 @@
export async function load({ fetch, params }) {
export async function load({ fetch, params, locals }) {
try {
const res = await fetch(`/api/channels/${params.id}/messages?page=1`, {
method: 'GET',
@ -7,15 +7,18 @@ export async function load({ fetch, params }) {
}
});
const messages = await res.json();
console.log(messages);
return {
messages,
channelId: params.id,
userId: locals.userId
}
}catch (error) {
console.error('Erreur lors du chargement des messages:', error);
return {
messages: [],
channelId: params.id,
userId: locals.userId
};
}
}

View file

@ -6,7 +6,7 @@
import UserChat from '$lib/components/ui/UserChat.svelte';
export let data;
export let messages = data.messages;
export let messages = data.messages.messages;
export let users = data.users;
let messageText = '';
@ -18,11 +18,12 @@
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ user: data.userId, text: messageText }),
body: JSON.stringify({ userId: data.userId, text: messageText }),
});
if (response.ok) {
messageText = '';
// Envoyer le message avec les sockets (à implémenter)
console.log('Message envoyé avec succès');
}else{
console.log('Erreur lors de l\'envoi du message');
@ -53,7 +54,7 @@
<!-- Afficher les messages (mock d'un utilisateur sélectionné ou aucun message par défaut) -->
{#if messages.length > 0}
{#each messages as message}
<Message username={message.username} messageContent={message.messageContent} />
<Message username={message.user.username} messageContent={message.text} />
{/each}
{:else}
<div class="text-center text-gray-500 mt-10">Sélectionnez un message le chat est vide.</div>
@ -62,7 +63,7 @@
<!-- Input pour envoyer un message -->
<div class="px-10 py-5 w-full flex gap-2 border-t">
<Textarea class="h-16 resize-none flex-grow" placeholder="Écrivez un message..." value={messageText}/>
<Textarea class="h-16 resize-none flex-grow" placeholder="Écrivez un message..." bind:value={messageText}/>
<Button size="icon" class="h-16 w-16 bg-blue-500 hover:bg-blue-600 h-full" on:click={sendMessage}>
<PaperPlane class="h-6 w-6" />
</Button>

View file

@ -0,0 +1,10 @@
import { redirect } from '@sveltejs/kit';
export async function POST({ cookies }) {
// Supprimer les cookies "token" et "UID"
cookies.delete("token", { path: '/' });
cookies.delete("UID", { path: '/' });
// Rediriger vers la page d'accueil ou la page de connexion après déconnexion
throw redirect(303, '/');
}

View file

@ -11,8 +11,10 @@ const webSocketServer = {
const io = new Server(server.httpServer)
io.on('connection', (socket) => {
socket.emit('eventFromServer', 'Hello, World 👋')
})
socket.on('new-channel', (channel) => {
io.emit('new-channel', channel)
});
});
}
}