Modification de la page de chat et des requetes de création de channel

This commit is contained in:
Bilal Dieumegard 2024-12-02 19:53:40 +01:00
parent cbf953a25b
commit 45eba3eb1d
6 changed files with 180 additions and 45 deletions

View file

@ -1,15 +1,47 @@
<script lang="ts"> <script lang="ts">
import * as Card from "$lib/components/ui/card"; import * as Card from "$lib/components/ui/card";
import { formatDistanceToNow } from "$lib/utils/date.js";
export let username; export let username: string;
export let messageContent; export let messageContent: string;
export let profilePicture: string | null; // Peut être null
export let createdAt: string; // Date de création du message
// Image par défaut si profilePicture est null
let defaultProfilePicture = "/images/default-profile.png";
</script> </script>
<Card.Root> <Card.Root class="relative">
<Card.Header> <Card.Header class="flex items-center flex-row justify-between">
<Card.Title>{username}</Card.Title> <!-- Image de profil collée à gauche -->
</Card.Header> <div class="flex flex-row gap-3 items-center">
<Card.Content> <img
src={profilePicture || defaultProfilePicture}
alt="Profile Picture"
class="h-10 w-10 rounded-full border border-gray-300"
/>
<!-- Section contenant le pseudo -->
<div class="flex flex-col">
<Card.Title class="text-gray-800 text-sm sm:text-base md:text-lg truncate">
{username}
</Card.Title>
</div>
</div>
<span class="text-xs sm:text-sm md:text-base text-gray-500 items-top">
{formatDistanceToNow(createdAt)}
</span>
</Card.Header>
<!-- Contenu du message -->
<Card.Content>
<p class="text-sm sm:text-base md:text-lg text-gray-700">
{messageContent} {messageContent}
</Card.Content> </p>
</Card.Root> </Card.Content>
</Card.Root>
<style>
img {
object-fit: cover; /* Assure un bon rendu des images */
}
</style>

View file

@ -0,0 +1,58 @@
<script lang="ts">
export let profilePicture: string; // URL de l'image de profil
export let username: string; // Pseudo de l'utilisateur
export let status: string = "En ligne"; // Statut par défaut
</script>
<div class="flex items-center gap-4 p-3 cursor-pointer hover:bg-gray-100 rounded-lg border border-gray-300 shadow-sm">
<img
src={profilePicture}
alt="Profile"
class="h-12 w-12 rounded-full border border-gray-300"
/>
<div class="flex flex-col">
<span class="font-medium text-gray-800">{username}</span>
<div class="flex items-center gap-1">
<span class="text-xs text-gray-500">{status}</span>
{#if status === "En ligne"}
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-3 w-3 text-green-500"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
/>
</svg>
{:else}
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-3 w-3 text-gray-500"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
/>
</svg>
{/if}
</div>
</div>
</div>
<style>
/* Ajout d'une animation subtile lors du survol */
div:hover {
background-color: #f3f4f6;
transition: background-color 0.2s ease-in-out;
}
</style>

View file

@ -23,12 +23,7 @@ export async function GET({ params, url }) {
}, },
}); });
canaux = canaux.sort((a, b) => { canaux = sortChannels(canaux);
const lastMessageA = a.messages[0]?.createdAt || a.createdAt;
const lastMessageB = b.messages[0]?.createdAt || b.createdAt;
return new Date(lastMessageB).getTime() - new Date(lastMessageA).getTime();
});
return json(canaux); return json(canaux);
@ -55,12 +50,7 @@ export async function GET({ params, url }) {
}, },
}); });
canaux = canaux.sort((a, b) => { canaux = sortChannels(canaux);
const lastMessageA = a.messages[0]?.createdAt || a.createdAt;
const lastMessageB = b.messages[0]?.createdAt || b.createdAt;
return new Date(lastMessageB).getTime() - new Date(lastMessageA).getTime();
});
logger.debug('Caching channels with EX of 3600 secs'); logger.debug('Caching channels with EX of 3600 secs');
await redisClient.set('channels', JSON.stringify(canaux), { EX: 3600 }); await redisClient.set('channels', JSON.stringify(canaux), { EX: 3600 });
@ -81,28 +71,40 @@ export async function POST({ request }) {
try { try {
const canal = await prisma.channel.create({ const canal = await prisma.channel.create({
data: { data: {
name, name
createdAt: new Date(),
}, },
}); });
logger.debug('Creating a new channel in database with id ' + canal.id); logger.debug('Creating a new channel in database with id ' + canal.id);
const cachedChanels = await redisClient.get('channels'); const cachedChanels = await redisClient.get('channels');
const channels = cachedChanels != null ? JSON.parse(cachedChanels) : [];
let channels = cachedChanels != null ? JSON.parse(cachedChanels) : [];
console.log(channels);
console.log(canal);
channels.push(canal); channels.push(canal);
channels = sortChannels(channels);
console.log(channels);
logger.debug(`Added channel (${canal.id}) to channels cache.`); logger.debug(`Added channel (${canal.id}) to channels cache.`);
await redisClient.set('channels', JSON.stringify(channels), { EX: 600 }); await redisClient.set('channels', JSON.stringify(channels), { EX: 600 });
return json(canal, { status: 201 }); return json(canal, { status: 201 });
} catch (err) { } catch (err) {
console.log(err);
logger.error(err); logger.error(err);
return json({ error: 'Erreur lors de la création du canal' }, { status: 500 }); return json({ error: 'Erreur lors de la création du canal' }, { status: 500 });
} }
} }
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;
return new Date(lastMessageB).getTime() - new Date(lastMessageA).getTime();
});
}

View file

@ -104,21 +104,21 @@ export async function DELETE({ params }) {
} }
// Fonction pour mettre à jour tous les caches des messages // Fonction pour mettre à jour tous les caches des messages
function updateCaches(channelId: string) { async function updateCaches(channelId: string) {
let page : number = 1; let page : number = 1;
let limit : number = 10; let limit : number = 10;
let offset : number = (page - 1) * limit; let offset : number = (page - 1) * limit;
while (true) { while (true) {
const cacheKey = `channel:${channelId}:messages:page:${page}:limit:${limit}`; const cacheKey = `channel:${channelId}:messages:page:${page}`;
const cachedMessages = await redisClient.get(cacheKey); const cachedMessages = await redisClient.get(cacheKey);
if (!cachedMessages) { if (!cachedMessages) {
break; break;
} }
const totalMessages = await prisma.message.count({ const totalMessages = await prisma.message.count({
where: { canalId }, where: { channelId },
}); });
const messages = await prisma.message.findMany({ const messages = await prisma.message.findMany({
where: { canalId }, where: { channelId },
include: { include: {
user: { user: {
select: { id: true, pseudo: true }, select: { id: true, pseudo: true },

View file

@ -5,7 +5,7 @@ export async function load({ fetch, params }) {
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
} }
} });
const messages = await res.json(); const messages = await res.json();
return { return {
messages messages

View file

@ -1,23 +1,66 @@
<script lang="ts"> <script lang="ts">
import Textarea from "../../../lib/components/ui/textarea/textarea.svelte"; import Textarea from "../../../lib/components/ui/textarea/textarea.svelte";
import { Button } from "$lib/components/ui/button"; import { Button } from "$lib/components/ui/button";
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';
export let data; export let data;
export let messages = data.messages; export let messages = data.messages;
export let users = data.users; // Liste des utilisateurs
let selectedUser = null; // Utilisateur actuellement sélectionné
</script> </script>
<div class="h-full"> <div class="h-full flex">
<div class="px-10 flex flex-col gap-5 h-[90%]"> <!-- Liste des utilisateurs (colonne gauche) -->
<Message username="Yanax" messageContent="Salut les amis" /> <div class="w-1/4 bg-gray-100 border-r overflow-y-auto">
<Message username="Luxray" messageContent="Salut Yanax" /> <h2 class="text-3xl font-bold px-4 mt-5">Utilisateurs</h2>
<div class="flex flex-col m-5 gap-2">
<UserChat
profilePicture="/images/default-profile.png"
username="Luxray555"
status="En ligne"
/>
{#each users as user}
<UserChat
profilePicture={user.profilePicture}
username={user.username}
status={user.status}
/>
{/each}
</div>
</div> </div>
<div class="fixed bottom-5 px-10 w-full flex gap-2">
<Textarea class="h-20 resize-none" placeholder="Messsage..." /> <!-- Chat principal (colonne droite) -->
<Button size="icon" class="h-20 w-20"> <div class="flex-1 flex flex-col h-full">
<PaperPlane class="h-18 w-18" /> <!-- Messages -->
</Button> <div class="m-10 flex flex-col gap-5 overflow-y-auto flex-grow ">
<!-- Afficher les messages (mock d'un utilisateur sélectionné ou aucun message par défaut) -->
<Message username="Luxray555" messageContent="Sélectionnez un message pour commencer à discuter." />
{#if messages.length > 0}
{#each messages as message}
<Message username={message.username} messageContent={message.messageContent} />
{/each}
{:else}
<div class="text-center text-gray-500 mt-10">Sélectionnez un message le chat est vide.</div>
{/if}
</div>
<!-- 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..." />
<Button size="icon" class="h-16 w-16 bg-blue-500 hover:bg-blue-600 h-full">
<PaperPlane class="h-6 w-6" />
</Button>
</div>
</div> </div>
</div> </div>
<style>
.h-full {
height: 100%;
}
.selected {
background-color: #e2e8f0;
}
</style>