Modification de la page de chat et des requetes de création de channel
This commit is contained in:
parent
cbf953a25b
commit
45eba3eb1d
6 changed files with 180 additions and 45 deletions
|
@ -1,15 +1,47 @@
|
|||
<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 messageContent;
|
||||
export let username: string;
|
||||
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>
|
||||
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>{username}</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<Card.Root class="relative">
|
||||
<Card.Header class="flex items-center flex-row justify-between">
|
||||
<!-- Image de profil collée à gauche -->
|
||||
<div class="flex flex-row gap-3 items-center">
|
||||
<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}
|
||||
</Card.Content>
|
||||
</p>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
|
||||
<style>
|
||||
img {
|
||||
object-fit: cover; /* Assure un bon rendu des images */
|
||||
}
|
||||
</style>
|
||||
|
|
58
src/lib/components/ui/UserChat.svelte
Normal file
58
src/lib/components/ui/UserChat.svelte
Normal 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>
|
|
@ -23,12 +23,7 @@ export async function GET({ params, url }) {
|
|||
},
|
||||
});
|
||||
|
||||
canaux = canaux.sort((a, b) => {
|
||||
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();
|
||||
});
|
||||
canaux = sortChannels(canaux);
|
||||
|
||||
return json(canaux);
|
||||
|
||||
|
@ -55,12 +50,7 @@ export async function GET({ params, url }) {
|
|||
},
|
||||
});
|
||||
|
||||
canaux = canaux.sort((a, b) => {
|
||||
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();
|
||||
});
|
||||
canaux = sortChannels(canaux);
|
||||
|
||||
logger.debug('Caching channels with EX of 3600 secs');
|
||||
await redisClient.set('channels', JSON.stringify(canaux), { EX: 3600 });
|
||||
|
@ -81,28 +71,40 @@ export async function POST({ request }) {
|
|||
try {
|
||||
const canal = await prisma.channel.create({
|
||||
data: {
|
||||
name,
|
||||
createdAt: new Date(),
|
||||
name
|
||||
},
|
||||
});
|
||||
logger.debug('Creating a new channel in database with id ' + canal.id);
|
||||
|
||||
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 = sortChannels(channels);
|
||||
console.log(channels);
|
||||
|
||||
logger.debug(`Added channel (${canal.id}) to channels cache.`);
|
||||
await redisClient.set('channels', JSON.stringify(channels), { EX: 600 });
|
||||
|
||||
return json(canal, { status: 201 });
|
||||
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
logger.error(err);
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -104,21 +104,21 @@ export async function DELETE({ params }) {
|
|||
}
|
||||
|
||||
// Fonction pour mettre à jour tous les caches des messages
|
||||
function updateCaches(channelId: string) {
|
||||
async function updateCaches(channelId: string) {
|
||||
let page : number = 1;
|
||||
let limit : number = 10;
|
||||
let offset : number = (page - 1) * limit;
|
||||
while (true) {
|
||||
const cacheKey = `channel:${channelId}:messages:page:${page}:limit:${limit}`;
|
||||
const cacheKey = `channel:${channelId}:messages:page:${page}`;
|
||||
const cachedMessages = await redisClient.get(cacheKey);
|
||||
if (!cachedMessages) {
|
||||
break;
|
||||
}
|
||||
const totalMessages = await prisma.message.count({
|
||||
where: { canalId },
|
||||
where: { channelId },
|
||||
});
|
||||
const messages = await prisma.message.findMany({
|
||||
where: { canalId },
|
||||
where: { channelId },
|
||||
include: {
|
||||
user: {
|
||||
select: { id: true, pseudo: true },
|
||||
|
|
|
@ -5,7 +5,7 @@ export async function load({ fetch, params }) {
|
|||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}
|
||||
});
|
||||
const messages = await res.json();
|
||||
return {
|
||||
messages
|
||||
|
|
|
@ -1,23 +1,66 @@
|
|||
<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 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 messages = data.messages;
|
||||
|
||||
export let users = data.users; // Liste des utilisateurs
|
||||
let selectedUser = null; // Utilisateur actuellement sélectionné
|
||||
</script>
|
||||
|
||||
<div class="h-full">
|
||||
<div class="px-10 flex flex-col gap-5 h-[90%]">
|
||||
<Message username="Yanax" messageContent="Salut les amis" />
|
||||
<Message username="Luxray" messageContent="Salut Yanax" />
|
||||
<div class="h-full flex">
|
||||
<!-- Liste des utilisateurs (colonne gauche) -->
|
||||
<div class="w-1/4 bg-gray-100 border-r overflow-y-auto">
|
||||
<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 class="fixed bottom-5 px-10 w-full flex gap-2">
|
||||
<Textarea class="h-20 resize-none" placeholder="Messsage..." />
|
||||
<Button size="icon" class="h-20 w-20">
|
||||
<PaperPlane class="h-18 w-18" />
|
||||
</Button>
|
||||
|
||||
<!-- 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 ">
|
||||
<!-- 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>
|
||||
|
||||
<style>
|
||||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
.selected {
|
||||
background-color: #e2e8f0;
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Add table
Reference in a new issue