diff --git a/apps/desktop/src/lib/conversationFeatures.ts b/apps/desktop/src/lib/conversationFeatures.ts index e534415..2dc52a0 100644 --- a/apps/desktop/src/lib/conversationFeatures.ts +++ b/apps/desktop/src/lib/conversationFeatures.ts @@ -4,6 +4,7 @@ import { type AttachmentHandle, type DecryptedMessage, type PollOption, + type WhiteboardPayload, } from '@chat-app/shared/chat'; export type AttachmentBucket = 'media' | 'audio' | 'files'; @@ -97,6 +98,15 @@ export function createPollPayload(question: string, optionTexts: string[]): stri }); } +export function createWhiteboardPayload(whiteboardId: string): string { + const payload: WhiteboardPayload = { + v: 1, + type: 'whiteboard', + whiteboard_id: whiteboardId, + }; + return serializeMessagePayload(payload); +} + export function summarizePollVotes( options: PollOption[], reactions: ReactionSummaryInput[], diff --git a/apps/desktop/src/pages/ConversationPage.tsx b/apps/desktop/src/pages/ConversationPage.tsx index ade950d..cf66c26 100644 --- a/apps/desktop/src/pages/ConversationPage.tsx +++ b/apps/desktop/src/pages/ConversationPage.tsx @@ -42,7 +42,13 @@ import type { DecryptedMessage } from '@chat-app/shared/chat'; import { useAuth } from '../context/AuthContext'; import { useCall } from '../context/CallContext'; import { useConversationsContext } from '../context/ConversationsContext'; -import { collectConversationAttachments, createPollPayload } from '../lib/conversationFeatures'; +import { + collectConversationAttachments, + createPollPayload, + createWhiteboardPayload, +} from '../lib/conversationFeatures'; +import { WhiteboardModal } from '../components/WhiteboardModal'; +import { createWhiteboard } from '@chat-app/shared/chat'; import { compressImages } from '../lib/imageCompress'; import { ensureInstallId } from '../lib/installId'; import { searchCachedMessages } from '../lib/messageCache'; @@ -161,6 +167,8 @@ export function ConversationPage() { const [mediaDrawerOpen, setMediaDrawerOpen] = useState(false); const [pollDialogOpen, setPollDialogOpen] = useState(false); const [pollSending, setPollSending] = useState(false); + const [openWhiteboardId, setOpenWhiteboardId] = useState(null); + const [creatingWhiteboard, setCreatingWhiteboard] = useState(false); const [pollError, setPollError] = useState(null); const [replyTo, setReplyTo] = useState(null); const [forwardTarget, setForwardTarget] = useState(null); @@ -588,6 +596,23 @@ export function ConversationPage() { [send, replyTo?.id, notifyStopTyping], ); + const handleCreateWhiteboard = useCallback(async () => { + if (!id || creatingWhiteboard) return; + setCreatingWhiteboard(true); + try { + const board = await createWhiteboard(supabase, id); + const payload = createWhiteboardPayload(board.id); + await send(payload, [], replyTo?.id ?? null); + setReplyTo(null); + setStickToBottom(true); + setOpenWhiteboardId(board.id); + } catch (err: unknown) { + setSendError(err instanceof Error ? err.message : 'Whiteboard konnte nicht angelegt werden'); + } finally { + setCreatingWhiteboard(false); + } + }, [id, creatingWhiteboard, send, replyTo?.id]); + async function ingestFiles(files: File[]) { const compressed = await compressImages(files); const next: File[] = []; @@ -981,6 +1006,16 @@ export function ConversationPage() { > +
); } @@ -1459,3 +1501,13 @@ function AttachmentPreview({ ); } + +function WhiteboardIcon(props: React.SVGProps) { + return ( + + + + + ); +}