diff --git a/deep-sea-stories/packages/backend/src/controllers/peers.ts b/deep-sea-stories/packages/backend/src/controllers/peers.ts index 94a0575..e8e6209 100644 --- a/deep-sea-stories/packages/backend/src/controllers/peers.ts +++ b/deep-sea-stories/packages/backend/src/controllers/peers.ts @@ -1,4 +1,7 @@ -import type { RoomId } from '@fishjam-cloud/js-server-sdk'; +import { + RoomNotFoundException, + type RoomId, +} from '@fishjam-cloud/js-server-sdk'; import { TRPCError } from '@trpc/server'; import { GameRoomFullError } from '../domain/errors.js'; import { GameRoom } from '../game/room.js'; @@ -9,30 +12,34 @@ import { publicProcedure } from '../trpc.js'; export const createPeer = publicProcedure .input(createPeerInputSchema) .mutation(async ({ ctx, input }) => { - const room = await ctx.fishjam.getRoom(input.roomId as RoomId); - if (!room) { - throw new Error(`Room with id ${input.roomId} does not exist`); - } + try { + const room = await ctx.fishjam.getRoom(input.roomId as RoomId); - let gameRoom = roomService.getGameRoom(room.id); - if (!gameRoom) { - gameRoom = new GameRoom(ctx.fishjam, ctx.notifierService, room.id); - roomService.setGameRoom(room.id, gameRoom); - } + let gameRoom = roomService.getGameRoom(room.id); + if (!gameRoom) { + gameRoom = new GameRoom(ctx.fishjam, ctx.notifierService, room.id); + roomService.setGameRoom(room.id, gameRoom); + } - try { const { peer, peerToken } = await gameRoom.addPlayer(input.name); return { peer, token: peerToken, }; - } catch (error) { - if (error instanceof GameRoomFullError) { + } catch (e) { + if (e instanceof RoomNotFoundException) { + console.warn(`Room ${input.roomId} not found`); + throw new TRPCError({ + code: 'NOT_FOUND', + message: `Room does not exist`, + }); + } + if (e instanceof GameRoomFullError) { throw new TRPCError({ - code: 'BAD_REQUEST', - message: error.message, + code: 'CONFLICT', + message: e.message, }); } - throw error; + throw e; } }); diff --git a/deep-sea-stories/packages/backend/src/game/room.ts b/deep-sea-stories/packages/backend/src/game/room.ts index f459f9a..30e260d 100644 --- a/deep-sea-stories/packages/backend/src/game/room.ts +++ b/deep-sea-stories/packages/backend/src/game/room.ts @@ -200,28 +200,28 @@ export class GameRoom { this.gameTimeoutId = null; } - if (this.gameSession) { - await this.gameSession.stopGame(wait); - try { + try { + if (this.gameSession) { + await this.gameSession.stopGame(wait); await this.fishjamClient.deletePeer( this.roomId, this.gameSession.agentId, ); - this.gameSession = null; - } catch (e) { - if (!(e instanceof PeerNotFoundException)) throw e; } + } catch (e) { + if (!(e instanceof PeerNotFoundException)) throw e; + } finally { + this.gameSession = null; + this.story = undefined; + + this.notifierService.emitNotification(this.roomId, { + type: 'gameEnded' as const, + timestamp: Date.now(), + }); + + this.gameStarted = false; + console.log(`Stopped game for room ${this.roomId}`); } - - this.story = undefined; - - this.notifierService.emitNotification(this.roomId, { - type: 'gameEnded' as const, - timestamp: Date.now(), - }); - - this.gameStarted = false; - console.log(`Stopped game for room ${this.roomId}`); } private async createFishjamAgent() { diff --git a/deep-sea-stories/packages/web/src/components/AgentModeToggle.tsx b/deep-sea-stories/packages/web/src/components/AgentModeToggle.tsx index d7470ed..40c2e42 100644 --- a/deep-sea-stories/packages/web/src/components/AgentModeToggle.tsx +++ b/deep-sea-stories/packages/web/src/components/AgentModeToggle.tsx @@ -1,9 +1,9 @@ -import { EarOff, Headphones } from 'lucide-react'; +import { Check, CircleX, EarOff, Headphones } from 'lucide-react'; import { type FC, useEffect, useState } from 'react'; -import { toast } from 'sonner'; import { useTRPCClient } from '@/contexts/trpc'; import { useAgentEvents } from '@/hooks/useAgentEvents'; import { Button } from './ui/button'; +import { toast } from './ui/sonner'; type AgentModeToggleProps = { roomId: string; @@ -40,10 +40,11 @@ const AgentModeToggle: FC = ({ roomId }) => { muted: !isAiMuted, }); - toast.success(!isAiMuted ? 'Agent deafened' : 'Agent listening'); + toast(!isAiMuted ? 'Agent deafened' : 'Agent listening', Check); } catch (error) { - console.error('Failed to toggle AI mode:', error); - toast.error('Failed to toggle mode'); + console.error(error); + const verb = isAiMuted ? 'undeafen' : 'deafen'; + toast(`Failed to ${verb} agent`, CircleX); } finally { setIsMutating(false); } diff --git a/deep-sea-stories/packages/web/src/components/StorySelectionPanel.tsx b/deep-sea-stories/packages/web/src/components/StorySelectionPanel.tsx index 8eb2a08..74203cf 100644 --- a/deep-sea-stories/packages/web/src/components/StorySelectionPanel.tsx +++ b/deep-sea-stories/packages/web/src/components/StorySelectionPanel.tsx @@ -1,5 +1,5 @@ import type { StoryData } from '@deep-sea-stories/common'; -import { Check } from 'lucide-react'; +import { Check, X } from 'lucide-react'; import type { FC } from 'react'; import { useEffect, useState } from 'react'; import { useTRPCClient } from '@/contexts/trpc'; @@ -38,7 +38,7 @@ const StorySelectionPanel: FC = ({ setStories(fetchedStories); } catch (error) { console.error('Failed to fetch stories:', error); - toast('Failed to load stories', Check); + toast('Failed to load stories', X); } finally { setIsLoadingStories(false); } @@ -63,7 +63,7 @@ const StorySelectionPanel: FC = ({ } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Failed to select story'; - toast(`Error: ${errorMessage}`, Check); + toast(errorMessage, X); } finally { setIsStarting(false); } diff --git a/deep-sea-stories/packages/web/src/components/ui/sonner.tsx b/deep-sea-stories/packages/web/src/components/ui/sonner.tsx index 58c871f..55cc79a 100644 --- a/deep-sea-stories/packages/web/src/components/ui/sonner.tsx +++ b/deep-sea-stories/packages/web/src/components/ui/sonner.tsx @@ -33,7 +33,7 @@ const Toaster = ({ ...props }: ToasterProps) => { toastOptions={{ classNames: { toast: - 'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg', + 'group toast group-[.toaster]:text-foreground group-[.toaster]:border-border w-full items-center justify-center flex group-[.toaster]:shadow-lg', description: 'group-[.toast]:text-muted-foreground', actionButton: 'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground', diff --git a/deep-sea-stories/packages/web/src/views/HomeView.tsx b/deep-sea-stories/packages/web/src/views/HomeView.tsx index 2ed3ac9..a25ea28 100644 --- a/deep-sea-stories/packages/web/src/views/HomeView.tsx +++ b/deep-sea-stories/packages/web/src/views/HomeView.tsx @@ -4,6 +4,8 @@ import Footer from '@/components/Footer'; import TitleBar from '@/components/TitleBar'; import { Button } from '@/components/ui/button'; import { useTRPCClient } from '@/contexts/trpc'; +import { toast } from '@/components/ui/sonner'; +import { CircleX } from 'lucide-react'; export default function HomeView() { const navigate = useNavigate(); @@ -16,7 +18,8 @@ export default function HomeView() { const room = await trpcClient.createRoom.mutate(); navigate(`/${room.id}`); } catch (error) { - console.error('Failed to create room:', error); + toast('Failed to create room', CircleX); + console.error(error); } finally { setIsLoading(false); } diff --git a/deep-sea-stories/packages/web/src/views/JoinView.tsx b/deep-sea-stories/packages/web/src/views/JoinView.tsx index cb34763..53e3772 100644 --- a/deep-sea-stories/packages/web/src/views/JoinView.tsx +++ b/deep-sea-stories/packages/web/src/views/JoinView.tsx @@ -4,7 +4,7 @@ import { useInitializeDevices, useMicrophone, } from '@fishjam-cloud/react-client'; -import { Camera, Mic, User } from 'lucide-react'; +import { Camera, CircleX, Mic, User } from 'lucide-react'; import type { FC } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react'; import { DeviceSelect } from '@/components/DeviceSelect'; @@ -66,13 +66,16 @@ const JoinView: FC = ({ roomId }) => { peerMetadata: { name }, }); } catch (error) { - if ( - error instanceof TRPCClientError && - error.data?.code === 'BAD_REQUEST' - ) { + if (!(error instanceof TRPCClientError)) { + console.error(error); + return; + } + + if (error.data?.code === 'BAD_REQUEST') { toast(error.message, User); + } else { + toast(error.message, CircleX); } - console.error(error); } }, [trpcClient, roomId, joinRoom, name]);