Ajout de la pagination coter client

This commit is contained in:
Bilal Dieumegard 2024-12-04 15:04:39 +01:00
parent 0c92acc24b
commit 818b10a32d
5 changed files with 144 additions and 65 deletions

View file

@ -1,8 +1,10 @@
<script lang="ts">
import { formatDistanceToNow } from '$lib/utils/date';
export let id: string; // ID du chat
export let title: string; // Nom ou titre du chat
export let lastMessage: string; // Dernier message affiché
export let time: string; // Heure du dernier message
export let createdAt: string; // Heure du dernier message
</script>
<a href={`/chats/${id}`} class="chat-item p-4 border rounded-md hover:bg-gray-100 cursor-pointer flex justify-between items-center">
@ -10,7 +12,7 @@
<p class="font-semibold text-lg">{title}</p>
<p class="text-sm text-gray-500 truncate">{lastMessage}</p>
</div>
<p class="text-xs text-gray-400">{time}</p>
<p class="text-xs text-gray-400">{formatDistanceToNow(createdAt)}</p>
</a>
<style>

View file

@ -6,58 +6,70 @@ import logger from '$lib/logger';
export async function GET({ params, url }) {
const channelId = params.id;
// @ts-ignore
const limit = parseInt(url.searchParams.get('limit'))-1 || 19; // Limit -1 sinon ça retourne un message de trop
// @ts-ignore
const page = parseInt(url.searchParams.get('page')) || 1;
// Gestion des paramètres de pagination
const limit = parseInt(url.searchParams.get('limit') || '20'); // Par défaut, 20 messages
const page = parseInt(url.searchParams.get('page') || '1'); // Par défaut, page 1
const offset = (page - 1) * limit;
const start = offset; // Début de la plage
const stop = offset + limit - 1; // Fin de la plage inclusif
let redisMessages = await redisClient.zRange(`channel:${channelId}:messages`, offset, limit, { REV: true });
if (redisMessages != null) {
logger.debug(`Cache entry found, fetching messages for channel (${channelId}) from cache`);
const messages = redisMessages.length != 0 ? await redisClient.mGet(redisMessages).then(
(messages) => messages.map((m) => JSON.parse(m))
) : [];
try {
// Essayer de récupérer les messages du cache Redis
logger.debug(`Tentative de récupération des messages du cache pour le channel : ${channelId}`);
let redisMessages = await redisClient.zRange(`channel:${channelId}:messages`, start, stop, { REV: true });
return json({limit: limit+1, page, messages});
}
if (redisMessages && redisMessages.length > 0) {
logger.debug(`Messages trouvés dans le cache pour le channel : ${channelId}`);
const messages = await redisClient.mGet(redisMessages).then(
(messages) => messages.map((m) => JSON.parse(m))
);
const messages = await prisma.message.findMany({
where: {
channelId
},
select: {
id: true,
createdAt: true,
text: true,
user: {
select: {
id: true,
username: true,
surname: true,
name: true,
profilePicture: true
}
return json({ limit, page, messages });
} else {
logger.debug(`Aucun message trouvé dans le cache, récupération depuis MongoDB pour le channel : ${channelId}`);
}
// Si aucun message n'est trouvé dans Redis, charger depuis MongoDB
const messagesFromDB = await prisma.message.findMany({
where: { channelId },
select: {
id: true,
createdAt: true,
text: true,
user: {
select: {
id: true,
username: true,
surname: true,
name: true,
profilePicture: true,
},
},
},
orderBy: [{ createdAt: 'desc' }],
take: limit,
skip: offset,
});
if (messagesFromDB.length > 0) {
// Stocker les messages dans Redis
for (const message of messagesFromDB) {
await redisClient.set(`message:${message.id}`, JSON.stringify(message));
await redisClient.zAdd(`channel:${channelId}:messages`, {
score: new Date(message.createdAt).getTime(),
value: `message:${message.id}`,
});
}
},
orderBy: [
{ createdAt: 'desc' }
],
});
for (const m of messages) {
await redisClient.set(`message:${m.id}`, JSON.stringify(m));
// @ts-ignore
await redisClient.zAdd(`channel:${channelId}:messages`, {score: Date.parse(m.createdAt), value: `message:${m.id}`});
logger.debug(`Messages ajoutés au cache Redis pour le channel : ${channelId}`);
}
return json({ limit, page, messages: messagesFromDB });
} catch (err) {
logger.error(`Erreur lors de la récupération des messages : ${err.message}`);
return json({ error: 'Erreur lors de la récupération des messages' }, { status: 500 });
}
redisMessages = await redisClient.zRange(`channel:${channelId}:messages`, offset, limit, { REV: true });
const mgetMessages =await redisClient.mGet(redisMessages).then(
(messages) => messages.map((m) => JSON.parse(<string>m))
);
return json({limit: limit+1, page, messages: mgetMessages});
}
export async function POST({ params, request }) {
@ -65,7 +77,7 @@ export async function POST({ params, request }) {
const { userId, text } = await request.json();
try {
// Créer un nouveau message dans la base de données
// Créer un nouveau message dans MongoDB
const newMessage = await prisma.message.create({
data: {
userId,
@ -82,39 +94,43 @@ export async function POST({ params, request }) {
username: true,
surname: true,
name: true,
profilePicture: true
}
}
profilePicture: true,
},
},
},
});
// Ajouter le message dans Redis
await redisClient.set(`message:${newMessage.id}`, JSON.stringify(newMessage));
await redisClient.zAdd(`channel:${channelId}:messages`, {score: Date.parse(newMessage.createdAt), value: `message:${newMessage.id}`});
await redisClient.zAdd(`channel:${channelId}:messages`, {
score: new Date(newMessage.createdAt).getTime(),
value: `message:${newMessage.id}`,
});
logger.debug(`Nouveau message ajouté pour le channel : ${channelId}`);
return json(newMessage, { status: 201 });
} catch (err) {
console.error(err);
logger.error(`Erreur lors de la création du message : ${err.message}`);
return json({ error: 'Erreur lors de la création du message' }, { status: 500 });
}
}
export async function DELETE({ params, request }) {
const channelId = params.id;
const { messageId } = await request.json();
try {
// Supprimer le message de la base de données
await prisma.message.delete({
where: { id: messageId },
});
// Supprimer le message dans MongoDB
await prisma.message.delete({ where: { id: messageId } });
// Supprimer le message dans Redis
await redisClient.del(`message:${messageId}`);
await redisClient.zRem(`channel:${channelId}:messages`, `message:${messageId}`);
logger.debug(`Message supprimé pour le channel : ${channelId}`);
return json({ message: 'Message supprimé avec succès' });
} catch (err) {
console.error(err);
logger.error(`Erreur lors de la suppression du message : ${err.message}`);
return json({ error: 'Erreur lors de la suppression du message' }, { status: 500 });
}
}

View file

@ -95,7 +95,7 @@
id={channel.id}
title={channel.name}
lastMessage={channel.lastMessage ? channel.lastMessage.text : "Ecrire le premier message"}
time={formatDistanceToNow(channel.lastUpdate)}
createdAt={channel.lastUpdate}
/>
{/each}
</div>

View file

@ -1,6 +1,6 @@
export async function load({ fetch, params, locals }) {
try {
const res = await fetch(`/api/channels/${params.id}/messages?page=1`, {
const res = await fetch(`/api/channels/${params.id}/messages?page=1&limit=10`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'

View file

@ -4,6 +4,9 @@
import PaperPlane from "svelte-radix/PaperPlane.svelte";
import Message from "$lib/components/Message.svelte";
import UserChat from '$lib/components/ui/UserChat.svelte';
import { tick } from 'svelte';
import { formatDistanceToNow } from '$lib/utils/date.ts';
export let data;
export let messages = data.messages.messages;
@ -30,6 +33,55 @@
}
}
let currentPage = 1;
let isLoading = false;
let scrollContainer: HTMLElement;
async function loadMoreMessages() {
if (isLoading) return;
isLoading = true;
// Sauvegarder la hauteur actuelle
const previousHeight = scrollContainer.scrollHeight;
try {
const response = await fetch(`/api/channels/${data.channelId}/messages?page=${currentPage + 1}&limit=10`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (response.ok) {
const newMessages = await response.json();
if(newMessages.messages.length <= 0){
console.log('Pas d\'autres anciens messages');
return;
}
messages = [...newMessages.messages, ...messages]; // Ajouter les nouveaux messages en haut
currentPage++;
} else {
console.error('Erreur lors du chargement des anciens messages');
}
} catch (error) {
console.error('Erreur réseau lors du chargement des messages:', error);
} finally {
isLoading = false;
// Réajuster la position de défilement
await tick(); // Attendre la mise à jour du DOM
const newHeight = scrollContainer.scrollHeight;
scrollContainer.scrollTop = newHeight - previousHeight;
}
}
function handleScroll(event: Event) {
const container = event.target as HTMLElement;
if (container.scrollTop === 0 && !isLoading) {
loadMoreMessages();
}
}
</script>
<div class="h-full flex">
@ -50,11 +102,18 @@
<!-- Chat principal (colonne droite) -->
<div class="flex-1 flex flex-col h-full">
<!-- Messages -->
<div class="m-10 flex flex-col gap-5 overflow-y-auto flex-grow ">
<div
class="m-10 flex flex-col gap-5 overflow-y-auto flex-grow "
bind:this={scrollContainer}
on:scroll={handleScroll}
>
{#if isLoading}
<div class="loading-indicator">Chargement...</div>
{/if}
<!-- 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.user.username} messageContent={message.text} />
<Message username={message.user.username} messageContent={message.text} createdAt={message.createdAt} />
{/each}
{:else}
<div class="text-center text-gray-500 mt-10">Sélectionnez un message le chat est vide.</div>
@ -75,7 +134,9 @@
.h-full {
height: 100%;
}
.selected {
background-color: #e2e8f0;
.loading-indicator {
text-align: center;
padding: 10px;
color: gray;
}
</style>