Ajout des sockets pour les ecritures et connection dans les channels
This commit is contained in:
parent
1a97f720a7
commit
19e66750ad
6 changed files with 175 additions and 28 deletions
|
@ -4,7 +4,7 @@
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import ProfileInfo from "$lib/components/ui/ProfileInfo.svelte"; // Importer le composant ProfileInfo
|
import ProfileInfo from "$lib/components/ui/ProfileInfo.svelte"; // Importer le composant ProfileInfo
|
||||||
|
|
||||||
export let myMessage: boolean; // Si c'est le message de l'utilisateur courant
|
export let userId: string; // Si c'est le message de l'utilisateur courant
|
||||||
|
|
||||||
export let message = null; // Contenu du message
|
export let message = null; // Contenu du message
|
||||||
|
|
||||||
|
@ -13,6 +13,8 @@
|
||||||
|
|
||||||
let user = null;
|
let user = null;
|
||||||
|
|
||||||
|
let myMessage;
|
||||||
|
|
||||||
async function fetchUser() {
|
async function fetchUser() {
|
||||||
const res = await fetch(`/api/users/${message.user.id}`, {
|
const res = await fetch(`/api/users/${message.user.id}`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
@ -47,7 +49,7 @@
|
||||||
fetchUser();
|
fetchUser();
|
||||||
updateElapsed(); // Calcul initial
|
updateElapsed(); // Calcul initial
|
||||||
const interval = setInterval(updateElapsed, 1000); // Mise à jour toutes les secondes
|
const interval = setInterval(updateElapsed, 1000); // Mise à jour toutes les secondes
|
||||||
|
myMessage = message.user.id === userId; // Vérifier si c'est le message de l'utilisateur courant
|
||||||
return () => {
|
return () => {
|
||||||
clearInterval(interval); // Nettoyage lors du démontage
|
clearInterval(interval); // Nettoyage lors du démontage
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let profilePicture: string; // URL de l'image de profil
|
export let user;
|
||||||
export let username: string; // Pseudo de l'utilisateur
|
|
||||||
export let status: string = "En ligne"; // Statut par défaut
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex items-center gap-4 p-3 cursor-pointer hover:bg-gray-100 rounded-lg border border-gray-300 shadow-sm">
|
<div class="flex items-center gap-4 p-3 cursor-pointer hover:bg-gray-100 rounded-lg border border-gray-300 shadow-sm">
|
||||||
<img
|
<img
|
||||||
src={profilePicture}
|
src={`http://localhost:5173/${user.profilePicture}`}
|
||||||
alt="Profile"
|
alt="Profile"
|
||||||
class="h-12 w-12 rounded-full border border-gray-300"
|
class="h-12 w-12 rounded-full border border-gray-300"
|
||||||
/>
|
/>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<span class="font-medium text-gray-800">{username}</span>
|
<span class="font-medium text-gray-800">{user.username}</span>
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
<span class="text-xs text-gray-500">{status}</span>
|
<span class="text-xs text-gray-500">{user.state}</span>
|
||||||
{#if status === "En ligne"}
|
{#if user.state === "En ligne"}
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-3 w-3 text-green-500"
|
class="h-3 w-3 text-green-500"
|
||||||
|
@ -29,7 +27,7 @@
|
||||||
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
|
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
{:else if status === "Writing"}
|
{:else if status === "Ecrit"}
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-3 w-3 text-blue-500"
|
class="h-3 w-3 text-blue-500"
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { io } from "socket.io-client";
|
||||||
// Initialisation de la socket
|
// Initialisation de la socket
|
||||||
export const initSocket = () => {
|
export const initSocket = () => {
|
||||||
const socketInstance = io("http://localhost:5173");
|
const socketInstance = io("http://localhost:5173");
|
||||||
|
let socketId = null;
|
||||||
|
|
||||||
return socketInstance;
|
return socketInstance
|
||||||
}
|
}
|
|
@ -7,18 +7,27 @@ export async function load({ fetch, params, locals }) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const messages = await res.json();
|
const messages = await res.json();
|
||||||
|
const resUser = await fetch(`/api/users/${locals.userId}`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const user = await resUser.json();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
messages,
|
messages,
|
||||||
channelId: params.id,
|
channelId: params.id,
|
||||||
userId: locals.userId
|
userId: locals.userId,
|
||||||
|
user: user
|
||||||
}
|
}
|
||||||
}catch (error) {
|
}catch (error) {
|
||||||
console.error('Erreur lors du chargement des messages:', error);
|
console.error('Erreur lors du chargement des messages:', error);
|
||||||
return {
|
return {
|
||||||
messages: [],
|
messages: [],
|
||||||
channelId: params.id,
|
channelId: params.id,
|
||||||
userId: locals.userId
|
userId: locals.userId,
|
||||||
|
user: {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,13 +4,16 @@
|
||||||
import PaperPlane from "svelte-radix/PaperPlane.svelte";
|
import PaperPlane from "svelte-radix/PaperPlane.svelte";
|
||||||
import Message from "$lib/components/Message.svelte";
|
import Message from "$lib/components/Message.svelte";
|
||||||
import UserChat from '$lib/components/ui/UserChat.svelte';
|
import UserChat from '$lib/components/ui/UserChat.svelte';
|
||||||
import { onMount, tick } from 'svelte';
|
import { onMount, tick, onDestroy } from 'svelte';
|
||||||
import { initSocket } from '$lib/stores/socket';
|
import { initSocket } from '$lib/stores/socket';
|
||||||
import { ArrowLeft } from 'lucide-svelte';
|
import { ArrowLeft } from 'lucide-svelte';
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
export let messages = data.messages.messages;
|
export let messages = data.messages.messages;
|
||||||
export let users = data.users;
|
let user = data.user;
|
||||||
|
|
||||||
|
const socket = initSocket(); // Initialiser le socket
|
||||||
|
let users= [];
|
||||||
|
|
||||||
|
|
||||||
let isAtBottom = true;
|
let isAtBottom = true;
|
||||||
|
@ -24,8 +27,6 @@
|
||||||
activeProfileId = id;
|
activeProfileId = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
let socket = initSocket(); // Initialiser le socket
|
|
||||||
|
|
||||||
async function sendMessage() {
|
async function sendMessage() {
|
||||||
// Appel API pour envoyer le message
|
// Appel API pour envoyer le message
|
||||||
const response = await fetch(`/api/channels/${data.channelId}/messages`, {
|
const response = await fetch(`/api/channels/${data.channelId}/messages`, {
|
||||||
|
@ -69,7 +70,6 @@
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const newMessages = await response.json();
|
const newMessages = await response.json();
|
||||||
console.log(newMessages);
|
|
||||||
if(newMessages.messages.length <= 0){
|
if(newMessages.messages.length <= 0){
|
||||||
console.log('Pas d\'autres anciens messages');
|
console.log('Pas d\'autres anciens messages');
|
||||||
return;
|
return;
|
||||||
|
@ -108,6 +108,20 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let stopWritingTimeout;
|
||||||
|
|
||||||
|
function handleWriting() {
|
||||||
|
clearTimeout(stopWritingTimeout);
|
||||||
|
socket.emit('writing', { userId: data.userId, channelId: data.channelId });
|
||||||
|
stopWritingTimeout = setTimeout(() => {
|
||||||
|
handleStopWriting();
|
||||||
|
}, 2000); // Attendre 2 secondes d'inactivité avant d'émettre stop-writing
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleStopWriting() {
|
||||||
|
socket.emit('stop-writing', { userId: data.userId, channelId: data.channelId });
|
||||||
|
}
|
||||||
|
|
||||||
async function scrollToBottom() {
|
async function scrollToBottom() {
|
||||||
if (scrollContainer && isAtBottom) {
|
if (scrollContainer && isAtBottom) {
|
||||||
await tick();
|
await tick();
|
||||||
|
@ -117,7 +131,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(async () => {
|
||||||
if (scrollContainer) {
|
if (scrollContainer) {
|
||||||
const observer = new MutationObserver(async () => {
|
const observer = new MutationObserver(async () => {
|
||||||
await scrollToBottom();
|
await scrollToBottom();
|
||||||
|
@ -138,12 +152,70 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
socket.emit('leave-channel', { userId: data.userId, channelId: data.channelId });
|
||||||
|
socket.disconnect(); // Déconnexion propre du socket
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ecoute des événements socket
|
||||||
socket.on("new-message", async (message) => {
|
socket.on("new-message", async (message) => {
|
||||||
messages = [...messages , message ];
|
messages = [...messages, message]; // Ajouter le message à l'historique
|
||||||
await tick();
|
await tick();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on("load-users-channel", async (us) => {
|
||||||
|
users = us;
|
||||||
|
await tick();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("connect", () => {
|
||||||
|
socket.emit('new-user-join', { user:{ ...user, socketId:socket.id, state:"En ligne" }, channelId: data.channelId });
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('user-writing', async (userId) => {
|
||||||
|
console.log('user-writing reçu pour userId:', userId);
|
||||||
|
|
||||||
|
// On met à jour l'état de l'utilisateur
|
||||||
|
users = users.map((u) => {
|
||||||
|
if (u.id === userId) {
|
||||||
|
// Mettre à jour le state
|
||||||
|
return { ...u, state: "Ecrit" }; // On recrée l'objet pour garantir la réactivité
|
||||||
|
}
|
||||||
|
return u;
|
||||||
|
});
|
||||||
|
|
||||||
|
// On recrée une nouvelle référence du tableau `users`
|
||||||
|
users = [...users]; // Cela force Svelte à détecter le changement dans la liste
|
||||||
|
|
||||||
|
console.log('Utilisateurs après mise à jour de l\'état:', users);
|
||||||
|
|
||||||
|
// Forcer une mise à jour avec tick
|
||||||
|
await tick();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('user-stop-writing', async (userId) => {
|
||||||
|
console.log('user-stop-writing reçu pour userId:', userId);
|
||||||
|
|
||||||
|
// On met à jour l'état de l'utilisateur
|
||||||
|
users = users.map((u) => {
|
||||||
|
if (u.id === userId) {
|
||||||
|
// Mettre à jour le state
|
||||||
|
return { ...u, state: "En ligne" }; // On recrée l'objet pour garantir la réactivité
|
||||||
|
}
|
||||||
|
return u;
|
||||||
|
});
|
||||||
|
|
||||||
|
// On recrée une nouvelle référence du tableau `users`
|
||||||
|
users = [...users]; // Cela force Svelte à détecter le changement dans la liste
|
||||||
|
|
||||||
|
console.log('Utilisateurs après mise à jour de l\'état:', users);
|
||||||
|
|
||||||
|
// Forcer une mise à jour avec tick
|
||||||
|
await tick();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="h-full flex">
|
<div class="h-full flex">
|
||||||
|
@ -154,11 +226,9 @@
|
||||||
<h2 class="text-3xl font-bold">Utilisateurs</h2>
|
<h2 class="text-3xl font-bold">Utilisateurs</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col m-5 gap-2">
|
<div class="flex flex-col m-5 gap-2">
|
||||||
{#each users as user}
|
{#each users as u (u.id)}
|
||||||
<UserChat
|
<UserChat
|
||||||
profilePicture={user.profilePicture}
|
user={u}
|
||||||
username={user.username}
|
|
||||||
status={user.status}
|
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
@ -178,7 +248,7 @@
|
||||||
{#if messages !== undefined && messages.length > 0}
|
{#if messages !== undefined && messages.length > 0}
|
||||||
{#each messages as message}
|
{#each messages as message}
|
||||||
<Message
|
<Message
|
||||||
myMessage={data.userId === message.user.id}
|
userId={data.userId}
|
||||||
message={message}
|
message={message}
|
||||||
activeProfileId={activeProfileId}
|
activeProfileId={activeProfileId}
|
||||||
setActiveProfile={setActiveProfile}
|
setActiveProfile={setActiveProfile}
|
||||||
|
@ -191,7 +261,14 @@
|
||||||
|
|
||||||
<!-- Input pour envoyer un message -->
|
<!-- Input pour envoyer un message -->
|
||||||
<div class="px-10 py-5 w-full flex gap-2 border-t">
|
<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..." bind:value={messageText} on:keypress={handleEnter}/>
|
<Textarea
|
||||||
|
class="h-16 resize-none flex-grow"
|
||||||
|
placeholder="Écrivez un message..."
|
||||||
|
bind:value={messageText}
|
||||||
|
on:keypress={handleEnter}
|
||||||
|
on:input={handleWriting}
|
||||||
|
on:blur={handleStopWriting}
|
||||||
|
/>
|
||||||
<Button size="icon" class="h-16 w-16 bg-blue-500 hover:bg-blue-600 h-full" on:click={sendMessage}>
|
<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" />
|
<PaperPlane class="h-6 w-6" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -3,6 +3,10 @@ import { type ViteDevServer, defineConfig } from 'vite';
|
||||||
|
|
||||||
import { Server } from 'socket.io'
|
import { Server } from 'socket.io'
|
||||||
|
|
||||||
|
function isView(obj) {
|
||||||
|
return obj instanceof DataView || (obj && obj.buffer instanceof ArrayBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
const webSocketServer = {
|
const webSocketServer = {
|
||||||
name: 'webSocketServer',
|
name: 'webSocketServer',
|
||||||
configureServer(server: ViteDevServer) {
|
configureServer(server: ViteDevServer) {
|
||||||
|
@ -10,14 +14,70 @@ const webSocketServer = {
|
||||||
|
|
||||||
const io = new Server(server.httpServer)
|
const io = new Server(server.httpServer)
|
||||||
|
|
||||||
|
let channelsUsers = {};
|
||||||
|
|
||||||
io.on('connection', (socket) => {
|
io.on('connection', (socket) => {
|
||||||
socket.on('new-channel', (channel) => {
|
socket.on('new-channel', (channel) => {
|
||||||
io.emit('new-channel', channel)
|
io.emit('new-channel', channel)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Écouter les messages
|
||||||
socket.on('new-message', (message) => {
|
socket.on('new-message', (message) => {
|
||||||
console.log('Diffudion du nouveau message', message,"<= ici");
|
io.emit('new-message', message); // Diffusion du message
|
||||||
io.emit('new-message', message)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on('new-user-join', (data) => {
|
||||||
|
const {user, channelId } = data;
|
||||||
|
if (!channelsUsers[channelId]) {
|
||||||
|
channelsUsers[channelId] = [];
|
||||||
|
}
|
||||||
|
if (!channelsUsers[channelId].find((u) => u.id === user.id)) {
|
||||||
|
// Ajouter l'utilisateur à la liste des utilisateurs du canal avec son socketId
|
||||||
|
channelsUsers[channelId].push(user);
|
||||||
|
}
|
||||||
|
socket.join(`channel:${channelId}`);
|
||||||
|
io.to(`channel:${channelId}`).emit('load-users-channel', channelsUsers[channelId]);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('leave-channel', (data) => {
|
||||||
|
const { userId, channelId } = data;
|
||||||
|
|
||||||
|
if (channelsUsers[channelId]) {
|
||||||
|
// Supprimez l'utilisateur du canal
|
||||||
|
channelsUsers[channelId] = channelsUsers[channelId].filter((u) => u.id !== userId);
|
||||||
|
|
||||||
|
io.to(`channel:${channelId}`).emit('load-users-channel', channelsUsers[channelId]);
|
||||||
|
console.log(`Utilisateur ${userId} a quitté le canal ${channelId}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('disconnect', () => {
|
||||||
|
console.log('Déconnexion du client');
|
||||||
|
for (const channelId in channelsUsers) {
|
||||||
|
channelsUsers[channelId] = channelsUsers[channelId].filter((u) => u.socketId !== socket.id);
|
||||||
|
io.to(`channel:${channelId}`).emit('load-users-channel', channelsUsers[channelId]);
|
||||||
|
}
|
||||||
|
console.log('Utilisateurs connectés:', channelsUsers);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('writing', (data) => {
|
||||||
|
const { userId, channelId } = data;
|
||||||
|
const us = channelsUsers[channelId]?.find((u) => u.id === userId);
|
||||||
|
if (us) {
|
||||||
|
us.state = "Ecrit";
|
||||||
|
io.to(`channel:${channelId}`).emit('user-writing', userId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('stop-writing', (data) => {
|
||||||
|
const { userId, channelId } = data;
|
||||||
|
const us = channelsUsers[channelId]?.find((u) => u.id === userId);
|
||||||
|
if (us) {
|
||||||
|
us.state = "En ligne";
|
||||||
|
io.to(`channel:${channelId}`).emit('user-stop-writing', userId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue