From 404e57798349db4b9a2714fab552374892e68480 Mon Sep 17 00:00:00 2001 From: Linrador Date: Wed, 6 Aug 2025 20:41:25 +0200 Subject: [PATCH] update --- .../admin/teams/[teamId]/TeamAdminClient.tsx | 4 +- .../api/user/invitations/[action]/route.ts | 26 +++++++ src/app/components/NotificationCenter.tsx | 2 +- .../{SSEManager.tsx => SSEListener.tsx} | 3 +- src/app/components/TeamMemberView.tsx | 67 ++----------------- src/app/hooks/useTeamManager.tsx | 2 + src/app/layout.tsx | 4 +- src/app/lib/sse-server-client.ts | 2 +- src/app/lib/useSSEStore.ts | 11 +++ 9 files changed, 51 insertions(+), 70 deletions(-) rename src/app/components/{SSEManager.tsx => SSEListener.tsx} (96%) diff --git a/src/app/admin/teams/[teamId]/TeamAdminClient.tsx b/src/app/admin/teams/[teamId]/TeamAdminClient.tsx index 67bfc8e..ea3fc8f 100644 --- a/src/app/admin/teams/[teamId]/TeamAdminClient.tsx +++ b/src/app/admin/teams/[teamId]/TeamAdminClient.tsx @@ -8,14 +8,14 @@ import { useSession } from 'next-auth/react' import LoadingSpinner from '@/app/components/LoadingSpinner' import TeamMemberView from '@/app/components/TeamMemberView' -import { useTeamManager } from '@/app/hooks/useTeamManager' +//import { useTeamManager } from '@/app/hooks/useTeamManager' export default function TeamAdminClient({ teamId }: { teamId: string }) { const [refetchKey, setRefetchKey] = useState() const { data: session } = useSession() // jetzt wird die ID korrekt übergeben ➜ /api/team/[id] - const teamManager = useTeamManager({ teamId, refetchKey }, null) + //const teamManager = useTeamManager({ teamId, refetchKey }, null) if (teamManager.isLoading) return diff --git a/src/app/api/user/invitations/[action]/route.ts b/src/app/api/user/invitations/[action]/route.ts index 9a9b418..8f0dd07 100644 --- a/src/app/api/user/invitations/[action]/route.ts +++ b/src/app/api/user/invitations/[action]/route.ts @@ -140,6 +140,32 @@ export async function POST( where: { steamId, actionData: invitationId }, data: { read: true, actionType: null, actionData: null }, }) + + + // 1. Teamdaten laden (inkl. Leader) + const team = await prisma.team.findUnique({ + where: { id: teamId }, + select: { leader: true }, + }) + + // 2. Admins holen + const admins = await prisma.user.findMany({ + where: { isAdmin: true }, + select: { steamId: true }, + }) + + // 3. Zielnutzer: Leader + Admins + const targetUserIds = [ + team?.leader, + ...admins.map(admin => admin.steamId), + ].filter(Boolean) // entfernt null/undefined + + // 4. SSE senden + await sendServerSSEMessage({ + type: 'team-updated', + teamId, + targetUserIds, + }) return NextResponse.json({ message: 'Einladung gelöscht' }) } diff --git a/src/app/components/NotificationCenter.tsx b/src/app/components/NotificationCenter.tsx index ea4ed9f..9d1260d 100644 --- a/src/app/components/NotificationCenter.tsx +++ b/src/app/components/NotificationCenter.tsx @@ -28,7 +28,7 @@ export default function NotificationCenter() { const [notifications, setNotifications] = useState([]) const [open, setOpen] = useState(false) const { source, connect } = useSSE() - const { markAllAsRead, markOneAsRead, handleInviteAction } = useTeamManager({}, null) + //const { markAllAsRead, markOneAsRead, handleInviteAction } = useTeamManager({}, null) const router = useRouter() const [previewText, setPreviewText] = useState(null) const [showPreview, setShowPreview] = useState(false) diff --git a/src/app/components/SSEManager.tsx b/src/app/components/SSEListener.tsx similarity index 96% rename from src/app/components/SSEManager.tsx rename to src/app/components/SSEListener.tsx index ac9e596..79f0649 100644 --- a/src/app/components/SSEManager.tsx +++ b/src/app/components/SSEListener.tsx @@ -4,7 +4,7 @@ import { useSession } from 'next-auth/react' import { useEffect } from 'react' import { useSSE } from '@/app/lib/useSSEStore' -export default function SSEManager() { +export default function SSEListener() { const { data: session } = useSession() const connect = useSSE((s) => s.connect) const disconnect = useSSE((s) => s.disconnect) @@ -18,6 +18,7 @@ export default function SSEManager() { eventSource.onmessage = (event) => { try { + console.error('[SSE] Nachricht empfangen:', event.data) const data = JSON.parse(event.data) switch (data.type) { diff --git a/src/app/components/TeamMemberView.tsx b/src/app/components/TeamMemberView.tsx index 1d039cd..bff0209 100644 --- a/src/app/components/TeamMemberView.tsx +++ b/src/app/components/TeamMemberView.tsx @@ -25,6 +25,7 @@ import Button from './Button' import Image from 'next/image' import TeamPremierRankBadge from './TeamPremierRankBadge' import Link from 'next/link' +import { useWebSocketListener } from '../hooks/useWebSocketListener' type Props = { team: Team | null @@ -63,7 +64,7 @@ export default function TeamMemberView({ adminMode = false, }: Props) { const { data: session } = useSession() - const { source, connect } = useSSE() + const { source } = useSSE() const [kickCandidate, setKickCandidate] = useState(null) const [promoteCandidate, setPromoteCandidate] = useState(null) @@ -72,7 +73,6 @@ export default function TeamMemberView({ const canManage = adminMode || isLeader const canInvite = isLeader && !adminMode const canAddDirect = adminMode - //const { leaveTeam, reloadTeam, renameTeam, revokeInvitation } = useTeamManager({}, null) const [showRenameModal, setShowRenameModal] = useState(false) const [showDeleteModal, setShowDeleteModal] = useState(false) const [isEditingName, setIsEditingName] = useState(false) @@ -84,11 +84,7 @@ export default function TeamMemberView({ const [saveSuccess, setSaveSuccess] = useState(false) const [invitedPlayers, setInvitedPlayers] = useState([]) - useEffect(() => { - if (session?.user?.steamId) { - connect(session.user.steamId) - } - }, [session?.user?.steamId]) + useWebSocketListener('ws-team-update', () => console.log("yeah?")) useEffect(() => { setTeamState(team) @@ -105,61 +101,6 @@ export default function TeamMemberView({ } }, [team]) - - useEffect(() => { - if (!source || !teamState?.id) return - - const handleMessage = (e: MessageEvent) => { - try { - const data = JSON.parse(e.data) - const relevant = [ - 'team-updated', - 'team-leader-changed', - 'team-leader-self', - 'team-member-joined', - 'team-member-left', - 'team-kick', - 'team-kick-other', - 'team-renamed', - 'team-logo-updated', - ] - if (data.teamId !== teamState.id || !relevant.includes(data.type)) return - - /* EIN Aufruf genügt – holt Team + Spieler + setzt States */ - fetch(`/api/team/${encodeURIComponent(data.teamId)}`) - .then(r => r.json()) - .then(({ team }) => { - if (!team) return - setTeamState(team) - setactivePlayers(team.activePlayers.sort((a: Player, b: Player) => a.name.localeCompare(b.name))) - setInactivePlayers(team.inactivePlayers.sort((a: Player, b: Player) => a.name.localeCompare(b.name))) - setInvitedPlayers(team.invitedPlayers.sort((a: Player, b: Player) => a.name.localeCompare(b.name))) - }) - } catch (err) { - console.error('SSE parse error:', err) - } - } - - const eventNames = [ - 'team-updated', - 'team-leader-changed', - 'team-leader-self', - 'team-member-joined', - 'team-member-left', - 'team-kick', - 'team-kick-other', - 'team-renamed', - 'team-logo-updated', - ] - eventNames.forEach(evt => source.addEventListener(evt, handleMessage)) - source.onmessage = handleMessage - - return () => { - eventNames.forEach(evt => source.removeEventListener(evt, handleMessage)) - source.onmessage = null - } - }, [source, teamState?.id, reloadTeam]) - const handleDragStart = (event: any) => { const id = event.active.id const item = activePlayers.find(p => p.steamId === id) || inactivePlayers.find(p => p.steamId === id) @@ -558,7 +499,7 @@ export default function TeamMemberView({
- {invitedPlayers.map((player: Player) => ( + {invitedPlayers.map((player: InvitedPlayer) => ( - + {/* Sidebar und Content direkt nebeneinander */} {children} diff --git a/src/app/lib/sse-server-client.ts b/src/app/lib/sse-server-client.ts index 80cd66a..7d8654f 100644 --- a/src/app/lib/sse-server-client.ts +++ b/src/app/lib/sse-server-client.ts @@ -1,9 +1,9 @@ +// sse-server-client.ts const host = 'localhost' export async function sendServerSSEMessage(message: any) { try { - console.log('[SSE Client] Nachricht senden:', message) await fetch(`http://${host}:3001/send`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, diff --git a/src/app/lib/useSSEStore.ts b/src/app/lib/useSSEStore.ts index 119c7fc..a9506f6 100644 --- a/src/app/lib/useSSEStore.ts +++ b/src/app/lib/useSSEStore.ts @@ -17,11 +17,22 @@ export const useSSE = create((set, get) => { const source = new EventSource(`http://localhost:3001/events?steamId=${steamId}`) source.onopen = () => { + console.log('[SSE] Verbunden!') set({ source, isConnected: true }) } source.onmessage = (event) => { console.log('[SSE] Nachricht:', event.data) + try { + const data = JSON.parse(event.data) + + // Zentrale Weiterleitung aller Events, die ein "type" haben + if (data?.type) { + window.dispatchEvent(new CustomEvent(`sse-${data.type}`, { detail: data })) + } + } catch (err) { + console.error('[SSE] Ungültige Nachricht:', event.data) + } } source.addEventListener('notification', (event) => {