From 30d928ddded8fc83745bccfefa8f3e105eb455b4 Mon Sep 17 00:00:00 2001 From: Nell Date: Sun, 5 Jul 2026 20:02:45 +0200 Subject: [PATCH] init --- frontend/src/pages/server/channel/index.vue | 42 ++++++++++++++++----- frontend/src/plugins/events.ts | 19 ++++++++++ frontend/src/stores/gateway.ts | 8 +++- frontend/src/stores/message.ts | 31 ++++++++++++++- src/repositories/message.rs | 21 ++++++++++- src/repositories/types.rs | 8 +++- src/routes/message/dto.rs | 14 ++++++- src/routes/message/handlers.rs | 17 +++------ src/routes/message/mapper.rs | 13 ++++++- 9 files changed, 145 insertions(+), 28 deletions(-) create mode 100644 frontend/src/plugins/events.ts diff --git a/frontend/src/pages/server/channel/index.vue b/frontend/src/pages/server/channel/index.vue index 89d4419..f249555 100644 --- a/frontend/src/pages/server/channel/index.vue +++ b/frontend/src/pages/server/channel/index.vue @@ -1,8 +1,8 @@ + \ No newline at end of file diff --git a/frontend/src/plugins/events.ts b/frontend/src/plugins/events.ts new file mode 100644 index 0000000..417aef6 --- /dev/null +++ b/frontend/src/plugins/events.ts @@ -0,0 +1,19 @@ +export const bus = new EventTarget(); + +export function emitGatewayEvent(namespace: string, action: string, content: any) { + // On construit le nom de l'événement de manière cohérente : gateway:message + const eventName = `gateway:${namespace.toLowerCase()}`; + bus.dispatchEvent(new CustomEvent(eventName, {detail: {action, content}})); +} + +export function onGatewayEvent(namespace: string, callback: (payload: { action: string, content: any }) => void) { + const eventName = `gateway:${namespace.toLowerCase()}`; + const wrapper = (e: Event) => { + const customEvent = e as CustomEvent; + callback(customEvent.detail); + }; + bus.addEventListener(eventName, wrapper); + + // Retourne une fonction pour se désabonner facilement si besoin + return () => bus.removeEventListener(eventName, wrapper); +} \ No newline at end of file diff --git a/frontend/src/stores/gateway.ts b/frontend/src/stores/gateway.ts index d413437..5a6d130 100644 --- a/frontend/src/stores/gateway.ts +++ b/frontend/src/stores/gateway.ts @@ -1,5 +1,6 @@ import {defineStore} from 'pinia'; import {useAppStore} from "@/stores/app.ts"; +import {emitGatewayEvent} from "@/plugins/events.ts"; type GatewayStatus = 'disconnected' | 'connecting' | 'connected' | 'error' @@ -81,7 +82,12 @@ export const useGatewayStore = defineStore('gateway', { }, async handleMessage(rawData: string) { - console.log("[ws]message received", rawData) + try { + const data = JSON.parse(rawData) + emitGatewayEvent(data.namespace, data.action, data.content) + } catch (error) { + console.error('Error parsing WebSocket message:', error) + } }, async scheduleReconnect() { diff --git a/frontend/src/stores/message.ts b/frontend/src/stores/message.ts index a49c066..eba81f9 100644 --- a/frontend/src/stores/message.ts +++ b/frontend/src/stores/message.ts @@ -1,5 +1,6 @@ import {defineStore} from "pinia"; import {useApi} from "@/composables/useApi.ts"; +import {onGatewayEvent} from "@/plugins/events.ts"; interface Message { id: string; @@ -49,7 +50,7 @@ export const useMessageStore = defineStore("message", { const newMessage = await response.json(); // Ajout local immédiat (optimistic update) - this.messages.push(newMessage); + // this.messages.push(newMessage); } catch (error) { console.error("Erreur lors de l'envoi du message:", error); throw error; @@ -59,4 +60,30 @@ export const useMessageStore = defineStore("message", { this.messages = []; } } -}); \ No newline at end of file +}); + +onGatewayEvent("Message", (payload) => { + const store = useMessageStore(); + switch (payload.action) { + case "add": + const exists = store.messages.some(m => m.id === payload.content.id); + if (!exists) { + store.messages.push(payload.content); + } + break; + case "update": + const updateIndex = store.messages.findIndex(m => m.id === payload.content.id); + if (updateIndex !== -1) { + store.messages[updateIndex] = payload.content; + } + break; + case "remove": + const removeIndex = store.messages.findIndex(m => m.id === payload.content); + if (removeIndex !== -1) { + store.messages.splice(removeIndex, 1); + } + break; + default: + console.warn("Action non gérée :", payload.action); + } +}) \ No newline at end of file diff --git a/src/repositories/message.rs b/src/repositories/message.rs index 9853a49..5c1601c 100644 --- a/src/repositories/message.rs +++ b/src/repositories/message.rs @@ -1,6 +1,7 @@ +use super::types::MessageFilter; use crate::models::message; use crate::repositories::{AnyResult, RepositoryContext}; -use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter}; +use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect}; use std::sync::Arc; #[derive(Clone, Debug)] @@ -19,6 +20,24 @@ impl MessageRepository { .await?) } + pub async fn filter(&self, filter: MessageFilter) -> AnyResult> { + let mut query = message::Entity::find(); + + if let Some(channel_id) = filter.channel_id { + query = query.filter(message::Column::ChannelId.eq(channel_id)); + } + + if let Some(before_id) = filter.before_id { + query = query.filter(message::Column::Id.lt(before_id)); + } + + if let Some(limit) = filter.limit { + query = query.order_by_desc(message::Column::Id).limit(limit); + } + + Ok(query.all(&self.context.db).await?) + } + pub async fn get_by_channel(&self, channel_id: uuid::Uuid) -> AnyResult> { Ok(message::Entity::find() .filter(message::Column::ChannelId.eq(channel_id)) diff --git a/src/repositories/types.rs b/src/repositories/types.rs index c976bc7..aef3198 100644 --- a/src/repositories/types.rs +++ b/src/repositories/types.rs @@ -17,4 +17,10 @@ impl ServerExplorerItem { pub struct ServerTree { pub items: Vec, -} \ No newline at end of file +} + +pub struct MessageFilter { + pub channel_id: Option, + pub before_id: Option, + pub limit: Option, +} diff --git a/src/routes/message/dto.rs b/src/routes/message/dto.rs index 677cbe4..646055d 100644 --- a/src/routes/message/dto.rs +++ b/src/routes/message/dto.rs @@ -27,6 +27,18 @@ pub struct UpdateMessageRequest { } #[derive(serde::Deserialize, utoipa::IntoParams)] -pub struct MessageFilters { +pub struct MessageQueryParams { pub channel_id: Option, + pub before_id: Option, + pub limit: Option, +} + +impl Default for MessageQueryParams { + fn default() -> Self { + Self { + channel_id: None, + before_id: None, + limit: Some(50), + } + } } diff --git a/src/routes/message/handlers.rs b/src/routes/message/handlers.rs index 50d8964..508651d 100644 --- a/src/routes/message/handlers.rs +++ b/src/routes/message/handlers.rs @@ -1,4 +1,4 @@ -use super::dto::{CreateMessageRequest, MessageFilters, MessageResponse, UpdateMessageRequest}; +use super::dto::{CreateMessageRequest, MessageQueryParams, MessageResponse, UpdateMessageRequest}; use crate::core::state::AppState; use crate::http::context::CurrentUser; use crate::http::error::HTTPError; @@ -19,23 +19,16 @@ use uuid::Uuid; (status = 500, description = "Erreur interne du serveur") ), params( - MessageFilters + MessageQueryParams ), tag = "Messages" )] pub async fn get_all( State(state): State, - Query(filters): Query, + Query(filters): Query, ) -> Result>, HTTPError> { - let messages = if let Some(channel_id) = filters.channel_id { - state - .repositories - .message - .get_by_channel(channel_id) - .await? - } else { - state.repositories.message.get_all().await? - }; + let params = mapper::query_params_to_message_filter(filters); + let messages = state.repositories.message.filter(params).await?; Ok(Json( messages .into_iter() diff --git a/src/routes/message/mapper.rs b/src/routes/message/mapper.rs index c2fde94..b365f11 100644 --- a/src/routes/message/mapper.rs +++ b/src/routes/message/mapper.rs @@ -1,5 +1,8 @@ use crate::models::message; -use crate::routes::message::dto::{CreateMessageRequest, MessageResponse, UpdateMessageRequest}; +use crate::repositories::types::MessageFilter; +use crate::routes::message::dto::{ + CreateMessageRequest, MessageQueryParams, MessageResponse, UpdateMessageRequest, +}; use chrono::Utc; use sea_orm::Set; use uuid::Uuid; @@ -42,3 +45,11 @@ pub fn update_request_to_am( reply_to_id: Set(model.reply_to_id), } } + +pub fn query_params_to_message_filter(params: MessageQueryParams) -> MessageFilter { + MessageFilter { + channel_id: params.channel_id, + before_id: params.before_id, + limit: params.limit, + } +}