From 057732f00e7966e4c9a5d9a562c1cc8a8d6f0afc Mon Sep 17 00:00:00 2001 From: Julien Froidefond Date: Wed, 4 Feb 2026 11:05:33 +0100 Subject: [PATCH] feat: enhance real-time weather session updates by broadcasting user information and syncing local state in WeatherCard component --- src/actions/weather.ts | 57 ++++++++++++++++++++++++-- src/components/weather/WeatherCard.tsx | 12 +++++- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/actions/weather.ts b/src/actions/weather.ts index 4342912..0a72ce0 100644 --- a/src/actions/weather.ts +++ b/src/actions/weather.ts @@ -3,6 +3,8 @@ import { revalidatePath } from 'next/cache'; import { auth } from '@/lib/auth'; import * as weatherService from '@/services/weather'; +import { getUserById } from '@/services/auth'; +import { broadcastToWeatherSession } from '@/app/api/weather/[id]/subscribe/route'; // ============================================ // Session Actions @@ -37,14 +39,29 @@ export async function updateWeatherSession( try { await weatherService.updateWeatherSession(sessionId, authSession.user.id, data); + // Get user info for broadcast + const user = await getUserById(authSession.user.id); + if (!user) { + return { success: false, error: 'Utilisateur non trouvé' }; + } + // Emit event for real-time sync - await weatherService.createWeatherSessionEvent( + const event = await weatherService.createWeatherSessionEvent( sessionId, authSession.user.id, 'SESSION_UPDATED', data ); + // Broadcast immediately via SSE + broadcastToWeatherSession(sessionId, { + type: 'SESSION_UPDATED', + payload: data, + userId: authSession.user.id, + user: { id: user.id, name: user.name, email: user.email }, + timestamp: event.createdAt, + }); + revalidatePath(`/weather/${sessionId}`); revalidatePath('/weather'); revalidatePath('/sessions'); @@ -100,9 +117,15 @@ export async function createOrUpdateWeatherEntry( try { const entry = await weatherService.createOrUpdateWeatherEntry(sessionId, authSession.user.id, data); + // Get user info for broadcast + const user = await getUserById(authSession.user.id); + if (!user) { + return { success: false, error: 'Utilisateur non trouvé' }; + } + // Emit event for real-time sync const eventType = entry.createdAt.getTime() === entry.updatedAt.getTime() ? 'ENTRY_CREATED' : 'ENTRY_UPDATED'; - await weatherService.createWeatherSessionEvent( + const event = await weatherService.createWeatherSessionEvent( sessionId, authSession.user.id, eventType, @@ -113,6 +136,19 @@ export async function createOrUpdateWeatherEntry( } ); + // Broadcast immediately via SSE + broadcastToWeatherSession(sessionId, { + type: eventType, + payload: { + entryId: entry.id, + userId: entry.userId, + ...data, + }, + userId: authSession.user.id, + user: { id: user.id, name: user.name, email: user.email }, + timestamp: event.createdAt, + }); + revalidatePath(`/weather/${sessionId}`); return { success: true, data: entry }; } catch (error) { @@ -136,14 +172,29 @@ export async function deleteWeatherEntry(sessionId: string) { try { await weatherService.deleteWeatherEntry(sessionId, authSession.user.id); + // Get user info for broadcast + const user = await getUserById(authSession.user.id); + if (!user) { + return { success: false, error: 'Utilisateur non trouvé' }; + } + // Emit event for real-time sync - await weatherService.createWeatherSessionEvent( + const event = await weatherService.createWeatherSessionEvent( sessionId, authSession.user.id, 'ENTRY_DELETED', { userId: authSession.user.id } ); + // Broadcast immediately via SSE + broadcastToWeatherSession(sessionId, { + type: 'ENTRY_DELETED', + payload: { userId: authSession.user.id }, + userId: authSession.user.id, + user: { id: user.id, name: user.name, email: user.email }, + timestamp: event.createdAt, + }); + revalidatePath(`/weather/${sessionId}`); return { success: true }; } catch (error) { diff --git a/src/components/weather/WeatherCard.tsx b/src/components/weather/WeatherCard.tsx index 74d5c36..fbc20e8 100644 --- a/src/components/weather/WeatherCard.tsx +++ b/src/components/weather/WeatherCard.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState, useTransition } from 'react'; +import { useState, useTransition, useEffect } from 'react'; import { createOrUpdateWeatherEntry } from '@/actions/weather'; import { Avatar } from '@/components/ui/Avatar'; import { Textarea } from '@/components/ui/Textarea'; @@ -61,6 +61,16 @@ export function WeatherCard({ sessionId, currentUserId, entry, canEdit }: Weathe const isCurrentUser = entry.userId === currentUserId; const canEditThis = canEdit && isCurrentUser; + // Sync local state with props when they change (e.g., from SSE refresh) + useEffect(() => { + // eslint-disable-next-line react-hooks/set-state-in-effect + setNotes(entry.notes || ''); + setPerformanceEmoji(entry.performanceEmoji || null); + setMoralEmoji(entry.moralEmoji || null); + setFluxEmoji(entry.fluxEmoji || null); + setValueCreationEmoji(entry.valueCreationEmoji || null); + }, [entry.notes, entry.performanceEmoji, entry.moralEmoji, entry.fluxEmoji, entry.valueCreationEmoji]); + function handleEmojiChange(axis: 'performance' | 'moral' | 'flux' | 'valueCreation', emoji: string | null) { if (!canEditThis) return;