Fix des bugs de scroll dans la page de message
Fix des bugs de l'api dans le cache pour les messages
This commit is contained in:
parent
caf6e771f8
commit
15b0c91992
4 changed files with 59 additions and 25 deletions
|
@ -1,18 +1,4 @@
|
||||||
services:
|
services:
|
||||||
app:
|
|
||||||
build: .
|
|
||||||
hostname: chat-app
|
|
||||||
restart: always
|
|
||||||
environment:
|
|
||||||
- DATABASE_URL=mongodb://temp-root-username:temp-password@mongodb:27017/chat_projetweb
|
|
||||||
- JWT_SECRET=1f49ba5426afebd6e27eed416d0c31925ca5c4dc39ab4fdea514c8a858312608
|
|
||||||
ports:
|
|
||||||
- "3000:3000"
|
|
||||||
networks:
|
|
||||||
- app_network
|
|
||||||
depends_on:
|
|
||||||
- mongodb
|
|
||||||
- redis
|
|
||||||
|
|
||||||
mongodb:
|
mongodb:
|
||||||
build: ./mongodb_rs
|
build: ./mongodb_rs
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { createClient } from 'redis';
|
import { createClient } from 'redis';
|
||||||
|
|
||||||
const client = await createClient({
|
const client = await createClient({
|
||||||
url: process.env.REDIS_URL || 'redis://redis-server:6379'
|
url: process.env.REDIS_URL || 'redis://localhost:6379'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,13 +14,28 @@ export async function GET({ params, url }) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.debug(`Tentative de récupération des messages du cache pour le channel : ${channelId}`);
|
logger.debug(`Tentative de récupération des messages du cache pour le channel : ${channelId}`);
|
||||||
const redisMessageKeys = await redisClient.zRangeWithScores(
|
let redisMessageKeys = await redisClient.zRangeWithScores(
|
||||||
`channel:${channelId}:messages`,
|
`channel:${channelId}:messages`,
|
||||||
offset,
|
offset,
|
||||||
offset + limit - 1,
|
offset + limit - 1,
|
||||||
{ REV: true }
|
{ REV: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const redisPipelineRemove = redisClient.multi();
|
||||||
|
|
||||||
|
|
||||||
|
for (const messageKey of redisMessageKeys) {
|
||||||
|
// Vérifie si la clé existe dans Redis
|
||||||
|
const messageKeyValue = messageKey.value;
|
||||||
|
const exists = await redisClient.exists(messageKeyValue);
|
||||||
|
if (!exists) {
|
||||||
|
// Supprime la référence expirée dans le zSet
|
||||||
|
redisPipelineRemove.zRem(`channel:${channelId}:messages`, messageKeyValue);
|
||||||
|
redisMessageKeys = redisMessageKeys.filter((key) => key.value !== messageKeyValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await redisPipelineRemove.exec();
|
||||||
|
|
||||||
if (redisMessageKeys.length > 0) {
|
if (redisMessageKeys.length > 0) {
|
||||||
const messages = await Promise.all(
|
const messages = await Promise.all(
|
||||||
redisMessageKeys.map(async (key) => {
|
redisMessageKeys.map(async (key) => {
|
||||||
|
@ -114,7 +129,7 @@ export async function POST({ params, request }) {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ajouter le message dans Redis
|
// Ajouter le message dans Redis
|
||||||
await redisClient.set(`message:${newMessage.id}`, JSON.stringify(newMessage));
|
await redisClient.set(`message:${newMessage.id}`, JSON.stringify(newMessage), {EX: 1800});
|
||||||
await redisClient.zAdd(`channel:${channelId}:messages`, {
|
await redisClient.zAdd(`channel:${channelId}:messages`, {
|
||||||
score: new Date(newMessage.createdAt).getTime(),
|
score: new Date(newMessage.createdAt).getTime(),
|
||||||
value: `message:${newMessage.id}`,
|
value: `message:${newMessage.id}`,
|
||||||
|
@ -125,6 +140,7 @@ export async function POST({ params, request }) {
|
||||||
let channels = cachedChannels ? JSON.parse(cachedChannels) : [];
|
let channels = cachedChannels ? JSON.parse(cachedChannels) : [];
|
||||||
let channel = channels.find((c) => c.id === channelId);
|
let channel = channels.find((c) => c.id === channelId);
|
||||||
if(channel){
|
if(channel){
|
||||||
|
console.log('channel found')
|
||||||
channel.lastMessage = {
|
channel.lastMessage = {
|
||||||
id: newMessage.id,
|
id: newMessage.id,
|
||||||
text: newMessage.text,
|
text: newMessage.text,
|
||||||
|
@ -141,8 +157,8 @@ export async function POST({ params, request }) {
|
||||||
user: newMessage.user,
|
user: newMessage.user,
|
||||||
createdAt: newMessage.createdAt,
|
createdAt: newMessage.createdAt,
|
||||||
}, lastUpdate: newMessage.createdAt, messages: undefined};
|
}, lastUpdate: newMessage.createdAt, messages: undefined};
|
||||||
|
channels = [channel, ...channels];
|
||||||
}
|
}
|
||||||
channels = [channel, ...channels];
|
|
||||||
await redisClient.set('channels', JSON.stringify(channels), { EX: 600 });
|
await redisClient.set('channels', JSON.stringify(channels), { EX: 600 });
|
||||||
|
|
||||||
newMessage.channel = {
|
newMessage.channel = {
|
||||||
|
|
|
@ -77,6 +77,10 @@
|
||||||
}
|
}
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
|
|
||||||
|
const previousMessages = $messagesStore;
|
||||||
|
|
||||||
|
let newMessages = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Calculer la page à charger en fonction du nombre total de messages existants
|
// Calculer la page à charger en fonction du nombre total de messages existants
|
||||||
const totalMessages = $messagesStore.length;
|
const totalMessages = $messagesStore.length;
|
||||||
|
@ -90,7 +94,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const newMessages = await response.json();
|
newMessages = await response.json();
|
||||||
|
|
||||||
if (newMessages.messages.length <= 0) {
|
if (newMessages.messages.length <= 0) {
|
||||||
console.log("Pas d'autres anciens messages");
|
console.log("Pas d'autres anciens messages");
|
||||||
|
@ -116,6 +120,23 @@
|
||||||
console.error("Erreur réseau lors du chargement des messages:", error);
|
console.error("Erreur réseau lors du chargement des messages:", error);
|
||||||
} finally {
|
} finally {
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
|
await tick();
|
||||||
|
const filteredNewMessages = newMessages.messages.filter((msg) => {
|
||||||
|
return !previousMessages.some((m) => m.id === msg.id);
|
||||||
|
});
|
||||||
|
scrollContainer.scrollTo({
|
||||||
|
top: filteredNewMessages.length*300,
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleScroll() {
|
||||||
|
if (scrollContainer) {
|
||||||
|
// Détection quand on est en haut du scroll
|
||||||
|
if (scrollContainer.scrollTop <= 0 && !isLoading) {
|
||||||
|
loadMoreMessages();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +160,7 @@
|
||||||
socket.emit('stop-writing', { userId: data.userId, channelId: data.channelId });
|
socket.emit('stop-writing', { userId: data.userId, channelId: data.channelId });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function scrollToBottom(retries = 3) {
|
async function scrollToBottom(retries = 20) {
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
const attemptScroll = () => {
|
const attemptScroll = () => {
|
||||||
|
@ -151,16 +172,22 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
attemptScroll();
|
// Protéger l'utilisation de requestAnimationFrame
|
||||||
|
if (typeof window !== 'undefined' && typeof requestAnimationFrame === 'function') {
|
||||||
|
attemptScroll();
|
||||||
|
|
||||||
if (retries > 0) {
|
if (retries > 0) {
|
||||||
requestAnimationFrame(() => scrollToBottom(retries - 1));
|
requestAnimationFrame(() => scrollToBottom(retries - 1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
socket.emit('leave-channel', { userId: data.userId, channelId: data.channelId });
|
socket.emit('leave-channel', { userId: data.userId, channelId: data.channelId });
|
||||||
socket.disconnect(); // Déconnexion propre du socket
|
socket.disconnect(); // Déconnexion propre du socket
|
||||||
|
if (scrollContainer) {
|
||||||
|
scrollContainer.removeEventListener('scroll', handleScroll);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ecoute des événements socket
|
// Ecoute des événements socket
|
||||||
|
@ -221,12 +248,16 @@
|
||||||
|
|
||||||
messagesStore.subscribe(async () => {
|
messagesStore.subscribe(async () => {
|
||||||
await tick();
|
await tick();
|
||||||
await scrollToBottom(); // Scroll to the bottom after the message is added
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let firstPageLoad = true;
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
await tick();
|
await tick();
|
||||||
await scrollToBottom();
|
if(firstPageLoad){
|
||||||
|
firstPageLoad = false;
|
||||||
|
await scrollToBottom();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -254,6 +285,7 @@
|
||||||
<div
|
<div
|
||||||
class="m-10 flex flex-col gap-5 overflow-auto flex-grow"
|
class="m-10 flex flex-col gap-5 overflow-auto flex-grow"
|
||||||
bind:this={scrollContainer}
|
bind:this={scrollContainer}
|
||||||
|
on:scroll={handleScroll}
|
||||||
>
|
>
|
||||||
{#if isLoading}
|
{#if isLoading}
|
||||||
<div class="loading-indicator">Chargement...</div>
|
<div class="loading-indicator">Chargement...</div>
|
||||||
|
|
Loading…
Add table
Reference in a new issue