feat: enhance real-time weather session updates by broadcasting user information and syncing local state in WeatherCard component
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 6m14s
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 6m14s
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user