@burdenoff/microfe-conversations 2026.529.1 → 2026.531.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/conversations/ConversationsAdminRoutes.js +19 -21
- package/dist/conversations/ConversationsAdminRoutes.js.map +1 -1
- package/dist/conversations/hooks/useConversationQueries.js +46 -36
- package/dist/conversations/hooks/useConversationQueries.js.map +1 -1
- package/dist/conversations/hooks/useMessageQueries.js +2 -1
- package/dist/conversations/hooks/useMessageQueries.js.map +1 -1
- package/dist/conversations/hooks/useMessageSubscriptions.js +22 -23
- package/dist/conversations/hooks/useMessageSubscriptions.js.map +1 -1
- package/dist/conversations/pages/ConversationAdminPages.js +60 -38
- package/dist/conversations/pages/ConversationAdminPages.js.map +1 -1
- package/dist/conversations/pages/ConversationDetailPage.js +237 -179
- package/dist/conversations/pages/ConversationDetailPage.js.map +1 -1
- package/dist/generated/wspace-operations.js +82 -58
- package/dist/generated/wspace-operations.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConversationDetailPage.js","names":[],"sources":["../../../src/conversations/pages/ConversationDetailPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { useEventBus } from \"@burdenoff/fe-libs/shared/events\";\nimport { useNavigate, useParams } from \"react-router-dom\";\nimport {\n Archive,\n ArrowLeft,\n BellOff,\n Bell,\n ExternalLink,\n Hash,\n Loader2,\n MessageCircle,\n MoreVertical,\n Pin,\n PinOff,\n SendHorizontal,\n Users,\n X,\n} from \"lucide-react\";\nimport { MessageBubble } from \"../components/MessageBubble\";\nimport {\n useGetConversation,\n useConversationActions,\n} from \"../hooks/useConversationQueries\";\nimport {\n useGetMessages,\n useSendMessage,\n useEditMessage,\n useMessageActions,\n} from \"../hooks/useMessageQueries\";\nimport { useMessageStreamForConversation } from \"../hooks/useMessageSubscriptions\";\nimport { useConversations } from \"../providers/ConversationsProvider\";\nimport { useConversationsStore } from \"../store/conversationsStore\";\nimport { cn } from \"../utils/cn\";\nimport { PermissionGuard } from \"../components/PermissionGuard\";\nimport { CONVERSATIONS_PERMISSIONS } from \"../constants/permissions\";\nimport { nativeImpact, nativeNotify } from \"../../utils/nativeBridge\";\nimport type { Message } from \"../types\";\n\nconst MESSAGES_PAGE_SIZE = 100;\n\nexport function ConversationDetailPage() {\n const { conversationId } = useParams<{ conversationId: string }>();\n const navigate = useNavigate();\n const { basePath, currentUser } = useConversations();\n const bus = useEventBus();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const {\n clearMessageDraft,\n isParticipantsPanelOpen,\n messageDrafts,\n setMessageDraft,\n toggleParticipantsPanel,\n } = useConversationsStore();\n\n const [messageInput, setMessageInput] = useState(\n messageDrafts[conversationId ?? \"\"] ?? \"\",\n );\n const [locallySentMessages, setLocallySentMessages] = useState<Message[]>([]);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n\n const {\n conversation,\n loading: conversationLoading,\n error: conversationError,\n } = useGetConversation(conversationId ?? \"\");\n const {\n messages,\n loading: messagesLoading,\n error: messagesError,\n } = useGetMessages(conversationId ?? \"\", {\n first: MESSAGES_PAGE_SIZE,\n });\n // Subscribe to live message events so the list updates without polling.\n // Apollo cache merges by id; the hook handles append / evict explicitly.\n useMessageStreamForConversation(conversationId);\n const { sendMessage, loading: sendingMessage } = useSendMessage();\n const { editMessage, loading: editingMessage } = useEditMessage();\n const {\n deleteMessage,\n addReaction,\n removeReaction,\n pinMessage,\n unpinMessage,\n } = useMessageActions();\n const {\n archiveConversation,\n pinConversation,\n unpinConversation,\n muteConversation,\n unmuteConversation,\n } = useConversationActions();\n\n const [moreMenuOpen, setMoreMenuOpen] = useState(false);\n const [editingMessageId, setEditingMessageId] = useState<string | null>(null);\n const [editingContent, setEditingContent] = useState(\"\");\n const [replyingTo, setReplyingTo] = useState<{\n id: string;\n content: string;\n sender: string;\n } | null>(null);\n const moreMenuRef = useRef<HTMLDivElement>(null);\n\n const currentUserId = currentUser?.id ?? \"\";\n const sourceContext = conversation?.sourceContext;\n\n const displayedMessages = useMemo(() => {\n const byId = new Map<string, Message>();\n for (const message of locallySentMessages) {\n if (message.conversationId !== conversationId) continue;\n byId.set(message.id, message);\n }\n for (const message of messages) {\n byId.set(message.id, message);\n }\n return Array.from(byId.values()).sort(\n (a, b) =>\n new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(),\n );\n }, [conversationId, locallySentMessages, messages]);\n\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\n }, [displayedMessages.length]);\n\n useEffect(() => {\n setLocallySentMessages([]);\n }, [conversationId]);\n\n useEffect(() => {\n if (locallySentMessages.length === 0 || messages.length === 0) return;\n const serverIds = new Set(messages.map((message) => message.id));\n setLocallySentMessages((current) => {\n const pending = current.filter((message) => !serverIds.has(message.id));\n return pending.length === current.length ? current : pending;\n });\n }, [locallySentMessages.length, messages]);\n\n useEffect(() => {\n if (!conversationId) return;\n if (messageInput.trim()) {\n setMessageDraft(conversationId, messageInput);\n return;\n }\n clearMessageDraft(conversationId);\n }, [messageInput, conversationId, setMessageDraft, clearMessageDraft]);\n\n const participants = useMemo(() => {\n const byAuthor = new Map<string, { id: string; name: string }>();\n for (const message of displayedMessages) {\n if (!message.authorId || byAuthor.has(message.authorId)) continue;\n byAuthor.set(message.authorId, {\n id: message.authorId,\n name: message.sender.name || message.authorId,\n });\n }\n return Array.from(byAuthor.values());\n }, [displayedMessages]);\n\n const handleSendMessage = useCallback(async () => {\n if (!conversationId || !messageInput.trim() || sendingMessage) return;\n const message = await sendMessage({\n conversationId,\n content: messageInput.trim(),\n type: \"TEXT\",\n parentMessageId: replyingTo?.id,\n refetchFirst: MESSAGES_PAGE_SIZE,\n });\n if (!message) return;\n setLocallySentMessages((current) => [...current, message]);\n bus.emit(\"conversations.message.sent\", {\n messageId: message.id,\n conversationId,\n });\n setMessageInput(\"\");\n setReplyingTo(null);\n clearMessageDraft(conversationId);\n }, [\n bus,\n conversationId,\n messageInput,\n sendingMessage,\n sendMessage,\n clearMessageDraft,\n replyingTo,\n ]);\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (event.key !== \"Enter\" || event.shiftKey) return;\n event.preventDefault();\n void handleSendMessage();\n },\n [handleSendMessage],\n );\n\n // Emit opened event so sinks (analytics/audit) can record the view\n useEffect(() => {\n if (!conversationId) return;\n bus.emit(\"conversations.conversation.opened\", { conversationId });\n }, [bus, conversationId]);\n\n // Close more-menu on outside click\n useEffect(() => {\n if (!moreMenuOpen) return;\n const handler = (e: MouseEvent) => {\n if (\n moreMenuRef.current &&\n !moreMenuRef.current.contains(e.target as Node)\n ) {\n setMoreMenuOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", handler);\n return () => document.removeEventListener(\"mousedown\", handler);\n }, [moreMenuOpen]);\n\n const handleReply = useCallback(\n (message: { id: string; content: string; sender: { name: string } }) => {\n setReplyingTo({\n id: message.id,\n content: message.content,\n sender: message.sender.name,\n });\n },\n [],\n );\n\n const handleReact = useCallback(\n async (messageId: string, emoji: string) => {\n try {\n await addReaction(messageId, emoji);\n } catch {\n // If already reacted, toggle off\n await removeReaction(messageId, emoji).catch(() => {});\n }\n },\n [addReaction, removeReaction],\n );\n\n const handleEdit = useCallback((message: { id: string; content: string }) => {\n setEditingMessageId(message.id);\n setEditingContent(message.content);\n }, []);\n\n const handleEditSubmit = useCallback(async () => {\n if (!editingMessageId || !editingContent.trim() || editingMessage) return;\n await editMessage(editingMessageId, editingContent.trim());\n setEditingMessageId(null);\n setEditingContent(\"\");\n }, [editingMessageId, editingContent, editingMessage, editMessage]);\n\n const handleEditCancel = useCallback(() => {\n setEditingMessageId(null);\n setEditingContent(\"\");\n }, []);\n\n const handleDelete = useCallback(\n async (messageId: string) => {\n nativeImpact(\"medium\");\n try {\n await deleteMessage(messageId);\n nativeNotify(\"success\");\n } catch (error) {\n nativeNotify(\"error\");\n throw error;\n }\n },\n [deleteMessage],\n );\n\n const handleTogglePinMessage = useCallback(\n async (messageId: string, isPinned: boolean) => {\n if (isPinned) {\n await unpinMessage(messageId);\n return;\n }\n await pinMessage(messageId);\n },\n [pinMessage, unpinMessage],\n );\n\n const handlePin = useCallback(async () => {\n if (!conversationId) return;\n if (conversation?.isPinned) {\n await unpinConversation(conversationId);\n } else {\n await pinConversation(conversationId);\n }\n setMoreMenuOpen(false);\n }, [\n conversationId,\n conversation?.isPinned,\n pinConversation,\n unpinConversation,\n ]);\n\n const handleMute = useCallback(async () => {\n if (!conversationId) return;\n if (conversation?.isMuted) {\n await unmuteConversation(conversationId);\n } else {\n await muteConversation(conversationId);\n }\n setMoreMenuOpen(false);\n }, [\n conversationId,\n conversation?.isMuted,\n muteConversation,\n unmuteConversation,\n ]);\n\n const handleArchive = useCallback(async () => {\n if (!conversationId) return;\n await archiveConversation(conversationId);\n bus.emit(\"conversations.conversation.archived\", { conversationId });\n setMoreMenuOpen(false);\n navigate(basePath || \"/conversations\");\n }, [archiveConversation, basePath, bus, conversationId, navigate]);\n\n const handleOpenContext = useCallback(() => {\n if (!sourceContext?.contextHref) return;\n try {\n const url = new URL(sourceContext.contextHref);\n if (url.origin === window.location.origin) {\n navigate(`${url.pathname}${url.search}${url.hash}`);\n return;\n }\n } catch {\n // Fall through to full navigation for relative or invalid URLs.\n }\n\n window.location.assign(sourceContext.contextHref);\n }, [navigate, sourceContext?.contextHref]);\n\n if (!conversation && conversationLoading) {\n return (\n <div className=\"flex items-center justify-center h-[calc(100vh-64px)] text-sm text-muted-foreground\">\n {tr(\"conversations.loading\", \"Loading conversation...\")}\n </div>\n );\n }\n\n if (!conversation || conversationError) {\n return (\n <div className=\"flex flex-col items-center justify-center h-[calc(100vh-64px)] text-center gap-3 px-4\">\n <MessageCircle className=\"size-8 text-muted-foreground\" />\n <p className=\"text-sm text-muted-foreground\">\n {conversationError\n ? tr(\n \"conversations.unavailable\",\n \"We couldn't load this conversation. Please try again.\",\n )\n : tr(\"conversations.notFound\", \"Conversation not found\")}\n </p>\n <button\n type=\"button\"\n onClick={() => navigate(basePath || \"/conversations\")}\n className=\"px-3 py-2 text-sm font-medium rounded-lg border border-border hover:bg-muted/50\"\n >\n {tr(\"conversations.back\", \"Back to conversations\")}\n </button>\n </div>\n );\n }\n\n return (\n <div className=\"relative flex h-[calc(100vh-64px)] bg-background\">\n <div className=\"flex-1 flex flex-col min-w-0\">\n <header\n data-tour=\"conversation-detail-header\"\n className=\"flex items-center justify-between px-4 py-3 border-b border-border bg-background/95 backdrop-blur-sm\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <button\n type=\"button\"\n onClick={() => navigate(basePath || \"/conversations\")}\n className=\"flex min-h-10 min-w-10 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground\"\n >\n <ArrowLeft size={18} />\n </button>\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-1.5\">\n {conversation.type === \"CHANNEL\" && (\n <Hash\n size={14}\n className=\"text-muted-foreground flex-shrink-0\"\n />\n )}\n <h2 className=\"text-sm font-semibold text-foreground truncate\">\n {conversation.title}\n </h2>\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {conversation.type === \"DIRECT\"\n ? tr(\n \"conversations.directConversation\",\n \"Direct conversation\",\n )\n : conversation.type === \"CHANNEL\"\n ? `${tr(\"conversations.channel\", \"Channel\")} · ${conversation.memberCount} ${tr(\"conversations.membersCountLabel\", \"members\")}`\n : conversation.type === \"ANNOUNCEMENT\"\n ? `${tr(\"conversations.announcement\", \"Announcement\")} · ${conversation.memberCount} ${tr(\"conversations.membersCountLabel\", \"members\")}`\n : `${conversation.memberCount} ${tr(\"conversations.membersCountLabel\", \"members\")}`}\n </p>\n </div>\n </div>\n\n <div className=\"flex shrink-0 items-center gap-1\">\n {sourceContext?.contextHref && (\n <HeaderButton\n dataTour=\"conversation-open-context\"\n icon={<ExternalLink size={16} />}\n label={tr(\"conversations.openContext\", \"Open context\")}\n onClick={handleOpenContext}\n />\n )}\n {conversation.isGroup && (\n <HeaderButton\n dataTour=\"conversation-participants-button\"\n icon={<Users size={16} />}\n label={tr(\"conversations.participants\", \"Participants\")}\n active={isParticipantsPanelOpen}\n onClick={toggleParticipantsPanel}\n />\n )}\n <div className=\"relative\" ref={moreMenuRef}>\n <HeaderButton\n icon={<MoreVertical size={16} />}\n label={tr(\"conversations.moreOptions\", \"More options\")}\n active={moreMenuOpen}\n onClick={() => setMoreMenuOpen((prev) => !prev)}\n />\n {moreMenuOpen && (\n <div className=\"absolute right-0 top-full mt-1 w-48 bg-popover border border-border rounded-lg shadow-lg z-50 py-1 animate-in fade-in-0 zoom-in-95 duration-100\">\n <DropdownItem\n icon={\n conversation.isPinned ? (\n <PinOff size={14} />\n ) : (\n <Pin size={14} />\n )\n }\n label={\n conversation.isPinned\n ? tr(\n \"conversations.unpinConversation\",\n \"Unpin conversation\",\n )\n : tr(\n \"conversations.pinConversation\",\n \"Pin conversation\",\n )\n }\n onClick={() => void handlePin()}\n />\n <DropdownItem\n icon={\n conversation.isMuted ? (\n <Bell size={14} />\n ) : (\n <BellOff size={14} />\n )\n }\n label={\n conversation.isMuted\n ? tr(\n \"conversations.unmuteConversation\",\n \"Unmute conversation\",\n )\n : tr(\n \"conversations.muteConversation\",\n \"Mute conversation\",\n )\n }\n onClick={() => void handleMute()}\n />\n <div className=\"h-px bg-border my-1\" />\n <DropdownItem\n icon={<Archive size={14} />}\n label={tr(\n \"conversations.archiveConversation\",\n \"Archive conversation\",\n )}\n onClick={() => void handleArchive()}\n destructive\n />\n </div>\n )}\n </div>\n </div>\n </header>\n\n <div className=\"flex-1 overflow-y-auto py-4 space-y-0.5\">\n {sourceContext?.pagePath && (\n <div className=\"mx-4 mb-4 rounded-lg border border-border bg-muted/20 px-3 py-2 text-xs text-muted-foreground\">\n <span className=\"font-medium text-foreground\">\n {sourceContext.contextTitle || sourceContext.pagePath}\n </span>\n {sourceContext.entityType && sourceContext.entityId\n ? ` · ${sourceContext.entityType} ${sourceContext.entityId}`\n : \"\"}\n </div>\n )}\n {messagesError && (\n <div className=\"mx-4 mb-4 rounded-lg border border-status-warning-border bg-status-warning-bg px-3 py-2 text-sm text-status-warning-text\">\n {tr(\n \"conversations.messagesUnavailable\",\n \"Messages couldn't be loaded right now. The conversation header and actions are still available.\",\n )}\n </div>\n )}\n {messagesLoading && (\n <div className=\"mx-4 mb-4 flex items-center gap-2 rounded-lg border border-border bg-muted/20 px-3 py-2 text-sm text-muted-foreground\">\n <Loader2 size={14} className=\"animate-spin\" />\n {tr(\"conversations.loadingMessages\", \"Loading messages...\")}\n </div>\n )}\n {!messagesLoading &&\n !messagesError &&\n displayedMessages.length === 0 && (\n <div className=\"mx-4 mb-4 rounded-lg border border-dashed border-border bg-muted/20 px-3 py-6 text-center text-sm text-muted-foreground\">\n {tr(\n \"conversations.noMessagesYet\",\n \"No messages yet. Start the conversation below.\",\n )}\n </div>\n )}\n {displayedMessages.map((message, index) => {\n const prevMessage = displayedMessages[index - 1];\n const showAvatar =\n !prevMessage ||\n prevMessage.authorId !== message.authorId ||\n prevMessage.type === \"SYSTEM\";\n\n return (\n <div key={message.id}>\n {editingMessageId === message.id ? (\n <div className=\"px-4 py-2\">\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground mb-1\">\n <span>\n {tr(\"conversations.editingMessage\", \"Editing message\")}\n </span>\n <button\n type=\"button\"\n onClick={handleEditCancel}\n className=\"hover:text-foreground\"\n >\n <X size={12} />\n </button>\n </div>\n <div className=\"flex items-end gap-2\">\n <textarea\n aria-label=\"Edit message\"\n value={editingContent}\n onChange={(e) => setEditingContent(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n void handleEditSubmit();\n }\n if (e.key === \"Escape\") handleEditCancel();\n }}\n className=\"flex-1 px-3 py-2 text-sm rounded-lg border border-border bg-muted/30 resize-none focus:outline-none focus:ring-2 focus:ring-ring\"\n rows={2}\n />\n <button\n type=\"button\"\n onClick={() => void handleEditSubmit()}\n disabled={!editingContent.trim() || editingMessage}\n className=\"px-3 py-2 text-xs font-medium rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50\"\n >\n {tr(\"conversations.save\", \"Save\")}\n </button>\n </div>\n </div>\n ) : (\n <MessageBubble\n message={message}\n isOwnMessage={message.authorId === currentUserId}\n showAvatar={showAvatar}\n showSender={showAvatar}\n onReply={handleReply}\n onReact={(id, emoji) => void handleReact(id, emoji)}\n onEdit={handleEdit}\n onDelete={(id) => void handleDelete(id)}\n onTogglePin={(id, isPinned) =>\n void handleTogglePinMessage(id, isPinned)\n }\n />\n )}\n </div>\n );\n })}\n <div ref={messagesEndRef} />\n </div>\n\n <div\n data-tour=\"conversation-message-composer\"\n className=\"border-t border-border px-4 py-3 bg-background\"\n >\n {/* Reply-to indicator */}\n {replyingTo && (\n <div className=\"flex items-center gap-2 mb-2 px-3 py-1.5 rounded-lg bg-muted/50 text-xs text-muted-foreground\">\n <span>\n {tr(\"conversations.replyingTo\", \"Replying to\")}{\" \"}\n <strong className=\"text-foreground\">{replyingTo.sender}</strong>\n : {replyingTo.content.slice(0, 80)}\n {replyingTo.content.length > 80 ? \"…\" : \"\"}\n </span>\n <button\n type=\"button\"\n onClick={() => setReplyingTo(null)}\n aria-label={tr(\"conversations.cancelReply\", \"Cancel reply\")}\n className=\"ml-auto flex min-h-9 min-w-9 items-center justify-center rounded-md hover:bg-muted/70 hover:text-foreground\"\n >\n <X size={12} />\n </button>\n </div>\n )}\n <div className=\"flex items-end gap-2\">\n <div className=\"flex-1 relative\">\n <textarea\n aria-label={tr(\n \"conversations.messageComposer\",\n \"Message composer\",\n )}\n value={messageInput}\n onChange={(event) => setMessageInput(event.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={`${tr(\"conversations.messageConversationPrefix\", \"Message\")} ${conversation.title}...`}\n rows={1}\n className={cn(\n \"w-full px-4 py-2.5 text-sm rounded-xl border border-border bg-muted/30\",\n \"resize-none overflow-hidden placeholder:text-muted-foreground\",\n \"focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent max-h-32\",\n )}\n style={{\n height: \"auto\",\n minHeight: \"40px\",\n }}\n onInput={(event) => {\n const target = event.target as HTMLTextAreaElement;\n target.style.height = \"auto\";\n target.style.height = `${Math.min(target.scrollHeight, 128)}px`;\n }}\n />\n </div>\n\n <button\n type=\"button\"\n aria-label={tr(\"conversations.sendMessage\", \"Send message\")}\n onClick={() => void handleSendMessage()}\n disabled={!messageInput.trim() || sendingMessage}\n className={cn(\n \"p-2.5 rounded-xl transition-all duration-150\",\n messageInput.trim() && !sendingMessage\n ? \"bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm\"\n : \"bg-muted text-muted-foreground cursor-not-allowed\",\n )}\n >\n {sendingMessage ? (\n <Loader2 size={18} className=\"animate-spin\" />\n ) : (\n <SendHorizontal size={18} />\n )}\n </button>\n </div>\n </div>\n </div>\n\n {isParticipantsPanelOpen && conversation.isGroup && (\n <aside className=\"absolute inset-y-0 right-0 z-20 w-full max-w-72 border-l border-border bg-background shadow-lg animate-in slide-in-from-right-4 duration-200 md:relative md:z-auto md:w-64 md:shadow-none\">\n <div className=\"p-4\">\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"text-sm font-semibold text-foreground\">\n {tr(\"conversations.members\", \"Members\")} (\n {participants.length || conversation.memberCount})\n </h3>\n <button\n type=\"button\"\n onClick={toggleParticipantsPanel}\n className=\"p-1 rounded text-muted-foreground hover:text-foreground\"\n >\n <Pin size={14} />\n </button>\n </div>\n\n <div className=\"space-y-1\">\n {participants.map((participant) => (\n <div\n key={participant.id}\n className=\"flex items-center gap-2.5 px-2 py-1.5 rounded-lg hover:bg-muted/50\"\n >\n <div className=\"size-7 rounded-full bg-muted flex items-center justify-center text-xs font-medium text-muted-foreground\">\n {participant.name\n .split(\" \")\n .map((word) => word[0])\n .join(\"\")\n .slice(0, 2)}\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-xs font-medium text-foreground truncate\">\n {participant.name}\n </p>\n <p className=\"text-[10px] text-muted-foreground\">\n {tr(\"conversations.member\", \"Member\")}\n </p>\n </div>\n </div>\n ))}\n </div>\n </div>\n </aside>\n )}\n </div>\n );\n}\n\nfunction DropdownItem({\n icon,\n label,\n onClick,\n destructive,\n}: {\n icon: React.ReactNode;\n label: string;\n onClick: () => void;\n destructive?: boolean;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className={cn(\n \"flex items-center gap-2.5 w-full px-3 py-2 text-sm transition-colors text-left\",\n destructive\n ? \"text-destructive hover:bg-destructive/10\"\n : \"text-foreground hover:bg-muted/50\",\n )}\n >\n {icon}\n <span>{label}</span>\n </button>\n );\n}\n\nfunction HeaderButton({\n icon,\n label,\n active,\n dataTour,\n onClick,\n}: {\n icon: React.ReactNode;\n label: string;\n active?: boolean;\n dataTour?: string;\n onClick?: () => void;\n}) {\n return (\n <PermissionGuard\n permission={CONVERSATIONS_PERMISSIONS.CONVERSATION_READ}\n allowUnavailablePermissions\n >\n <button\n data-tour={dataTour}\n type=\"button\"\n onClick={onClick}\n aria-label={label}\n title={label}\n className={cn(\n \"flex min-h-10 min-w-10 items-center justify-center rounded-lg transition-colors\",\n active\n ? \"bg-primary/10 text-primary\"\n : \"text-muted-foreground hover:text-foreground hover:bg-muted/50\",\n )}\n >\n {icon}\n </button>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ConversationDetailPage.js","names":[],"sources":["../../../src/conversations/pages/ConversationDetailPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { useEventBus } from \"@burdenoff/fe-libs/shared/events\";\nimport { useNavigate, useParams } from \"react-router-dom\";\nimport {\n Archive,\n ArrowLeft,\n BellOff,\n Bell,\n ExternalLink,\n Hash,\n LogOut,\n Loader2,\n MessageCircle,\n MoreVertical,\n Pin,\n PinOff,\n SendHorizontal,\n Users,\n X,\n} from \"lucide-react\";\nimport { MessageBubble } from \"../components/MessageBubble\";\nimport {\n useGetConversation,\n useConversationActions,\n} from \"../hooks/useConversationQueries\";\nimport {\n useGetMessages,\n useSendMessage,\n useEditMessage,\n useMessageActions,\n} from \"../hooks/useMessageQueries\";\nimport { useMessageStreamForConversation } from \"../hooks/useMessageSubscriptions\";\nimport { useConversations } from \"../providers/ConversationsProvider\";\nimport { useConversationsStore } from \"../store/conversationsStore\";\nimport { cn } from \"../utils/cn\";\nimport { PermissionGuard } from \"../components/PermissionGuard\";\nimport { CONVERSATIONS_PERMISSIONS } from \"../constants/permissions\";\nimport { nativeImpact, nativeNotify } from \"../../utils/nativeBridge\";\nimport type { Message } from \"../types\";\n\nconst MESSAGES_PAGE_SIZE = 100;\nconst MESSAGE_REFRESH_FALLBACK_MS = 30_000;\n\nexport function ConversationDetailPage() {\n const { conversationId } = useParams<{ conversationId: string }>();\n const navigate = useNavigate();\n const { basePath, currentUser } = useConversations();\n const bus = useEventBus();\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t],\n );\n const {\n clearMessageDraft,\n isParticipantsPanelOpen,\n messageDrafts,\n setMessageDraft,\n toggleParticipantsPanel,\n } = useConversationsStore();\n\n const [messageInput, setMessageInput] = useState(\n messageDrafts[conversationId ?? \"\"] ?? \"\",\n );\n const [locallySentMessages, setLocallySentMessages] = useState<Message[]>([]);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n\n const {\n conversation,\n members: conversationMembers,\n membersTotalCount,\n loading: conversationLoading,\n error: conversationError,\n } = useGetConversation(conversationId ?? \"\");\n const {\n messages,\n loading: messagesLoading,\n error: messagesError,\n refetch: refetchMessages,\n } = useGetMessages(conversationId ?? \"\", {\n first: MESSAGES_PAGE_SIZE,\n pollInterval: MESSAGE_REFRESH_FALLBACK_MS,\n });\n // Subscribe to live message events; the query has a light polling fallback for\n // local/federated transports that keep the stream open but miss events.\n useMessageStreamForConversation(conversationId, MESSAGES_PAGE_SIZE);\n const { sendMessage, loading: sendingMessage } = useSendMessage();\n const { editMessage, loading: editingMessage } = useEditMessage();\n const {\n deleteMessage,\n addReaction,\n removeReaction,\n pinMessage,\n unpinMessage,\n } = useMessageActions();\n const {\n archiveConversation,\n pinConversation,\n unpinConversation,\n muteConversation,\n unmuteConversation,\n leaveConversation,\n } = useConversationActions();\n\n const [moreMenuOpen, setMoreMenuOpen] = useState(false);\n const [editingMessageId, setEditingMessageId] = useState<string | null>(null);\n const [editingContent, setEditingContent] = useState(\"\");\n const [replyingTo, setReplyingTo] = useState<{\n id: string;\n content: string;\n sender: string;\n } | null>(null);\n const moreMenuRef = useRef<HTMLDivElement>(null);\n\n const currentUserId = currentUser?.id ?? \"\";\n const sourceContext = conversation?.sourceContext;\n\n const displayedMessages = useMemo(() => {\n const byId = new Map<string, Message>();\n for (const message of locallySentMessages) {\n if (message.conversationId !== conversationId) continue;\n byId.set(message.id, message);\n }\n for (const message of messages) {\n byId.set(message.id, message);\n }\n return Array.from(byId.values()).sort(\n (a, b) =>\n new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(),\n );\n }, [conversationId, locallySentMessages, messages]);\n\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\n }, [displayedMessages.length]);\n\n useEffect(() => {\n setLocallySentMessages([]);\n }, [conversationId]);\n\n useEffect(() => {\n if (locallySentMessages.length === 0 || messages.length === 0) return;\n const serverIds = new Set(messages.map((message) => message.id));\n setLocallySentMessages((current) => {\n const pending = current.filter((message) => !serverIds.has(message.id));\n return pending.length === current.length ? current : pending;\n });\n }, [locallySentMessages.length, messages, setLocallySentMessages]);\n\n useEffect(() => {\n if (!conversationId) return;\n if (messageInput.trim()) {\n setMessageDraft(conversationId, messageInput);\n return;\n }\n clearMessageDraft(conversationId);\n }, [messageInput, conversationId, setMessageDraft, clearMessageDraft]);\n\n const messageAuthorNames = useMemo(() => {\n const byAuthor = new Map<string, string>();\n for (const message of displayedMessages) {\n if (!message.authorId || byAuthor.has(message.authorId)) continue;\n if (!message.sender.name) continue;\n byAuthor.set(message.authorId, message.sender.name);\n }\n return byAuthor;\n }, [displayedMessages]);\n\n const participants = useMemo(() => {\n if (conversationMembers.length > 0) {\n return conversationMembers\n .filter((member) => member.status === \"ACTIVE\" && !member.leftAt)\n .map((member) => ({\n id: member.userId,\n name:\n messageAuthorNames.get(member.userId) ||\n member.customTitle ||\n tr(\"conversations.member\", \"Member\"),\n role: member.role,\n }));\n }\n\n return Array.from(messageAuthorNames.entries()).map(([id, name]) => ({\n id,\n name,\n role: tr(\"conversations.member\", \"Member\"),\n }));\n }, [conversationMembers, messageAuthorNames, tr]);\n\n const hiddenParticipantsCount = conversation?.memberCount\n ? Math.max(conversation.memberCount - participants.length, 0)\n : 0;\n\n const handleSendMessage = useCallback(async () => {\n if (!conversationId || !messageInput.trim() || sendingMessage) return;\n const message = await sendMessage({\n conversationId,\n content: messageInput.trim(),\n type: \"TEXT\",\n parentMessageId: replyingTo?.id,\n refetchFirst: MESSAGES_PAGE_SIZE,\n });\n if (!message) return;\n setLocallySentMessages((current) => [...current, message]);\n bus.emit(\"conversations.message.sent\", {\n messageId: message.id,\n conversationId,\n });\n setMessageInput(\"\");\n setReplyingTo(null);\n clearMessageDraft(conversationId);\n }, [\n bus,\n conversationId,\n messageInput,\n sendingMessage,\n sendMessage,\n clearMessageDraft,\n replyingTo,\n ]);\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (event.key !== \"Enter\" || event.shiftKey) return;\n event.preventDefault();\n void handleSendMessage();\n },\n [handleSendMessage],\n );\n\n // Emit opened event so sinks (analytics/audit) can record the view\n useEffect(() => {\n if (!conversationId) return;\n bus.emit(\"conversations.conversation.opened\", { conversationId });\n }, [bus, conversationId]);\n\n // Close more-menu on outside click\n useEffect(() => {\n if (!moreMenuOpen) return;\n const handler = (e: MouseEvent) => {\n if (\n moreMenuRef.current &&\n !moreMenuRef.current.contains(e.target as Node)\n ) {\n setMoreMenuOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", handler);\n return () => document.removeEventListener(\"mousedown\", handler);\n }, [moreMenuOpen]);\n\n const handleReply = useCallback(\n (message: { id: string; content: string; sender: { name: string } }) => {\n setReplyingTo({\n id: message.id,\n content: message.content,\n sender: message.sender.name,\n });\n },\n [],\n );\n\n const handleReact = useCallback(\n async (messageId: string, emoji: string) => {\n try {\n await addReaction(messageId, emoji);\n } catch {\n // If already reacted, toggle off\n await removeReaction(messageId, emoji).catch(() => {});\n }\n },\n [addReaction, removeReaction],\n );\n\n const handleEdit = useCallback((message: { id: string; content: string }) => {\n setEditingMessageId(message.id);\n setEditingContent(message.content);\n }, []);\n\n const handleEditSubmit = useCallback(async () => {\n if (!editingMessageId || !editingContent.trim() || editingMessage) return;\n await editMessage(editingMessageId, editingContent.trim());\n setEditingMessageId(null);\n setEditingContent(\"\");\n }, [editingMessageId, editingContent, editingMessage, editMessage]);\n\n const handleEditCancel = useCallback(() => {\n setEditingMessageId(null);\n setEditingContent(\"\");\n }, []);\n\n const handleDelete = useCallback(\n async (messageId: string) => {\n const confirmed = window.confirm(\n tr(\n \"conversations.confirmDeleteMessage\",\n \"Delete this message? This action cannot be undone.\",\n ),\n );\n if (!confirmed) return;\n nativeImpact(\"medium\");\n try {\n await deleteMessage(messageId);\n void refetchMessages();\n nativeNotify(\"success\");\n } catch (error) {\n console.error(\"[microfe-conversations] message delete failed\", error);\n nativeNotify(\"error\");\n }\n },\n [deleteMessage, refetchMessages, tr],\n );\n\n const handleTogglePinMessage = useCallback(\n async (messageId: string, isPinned: boolean) => {\n if (isPinned) {\n await unpinMessage(messageId);\n return;\n }\n await pinMessage(messageId);\n },\n [pinMessage, unpinMessage],\n );\n\n const handlePin = useCallback(async () => {\n if (!conversationId) return;\n if (conversation?.isPinned) {\n await unpinConversation(conversationId);\n } else {\n await pinConversation(conversationId);\n }\n setMoreMenuOpen(false);\n }, [\n conversationId,\n conversation?.isPinned,\n pinConversation,\n unpinConversation,\n ]);\n\n const handleMute = useCallback(async () => {\n if (!conversationId) return;\n if (conversation?.isMuted) {\n await unmuteConversation(conversationId);\n } else {\n await muteConversation(conversationId);\n }\n setMoreMenuOpen(false);\n }, [\n conversationId,\n conversation?.isMuted,\n muteConversation,\n unmuteConversation,\n ]);\n\n const handleArchive = useCallback(async () => {\n if (!conversationId) return;\n try {\n await archiveConversation(conversationId);\n bus.emit(\"conversations.conversation.archived\", { conversationId });\n setMoreMenuOpen(false);\n navigate(basePath || \"/conversations\");\n } catch (error) {\n console.error(\n \"[microfe-conversations] conversation archive failed\",\n error,\n );\n nativeNotify(\"error\");\n }\n }, [archiveConversation, basePath, bus, conversationId, navigate]);\n\n const handleLeave = useCallback(async () => {\n if (!conversationId) return;\n const confirmed = window.confirm(\n tr(\n \"conversations.confirmLeaveConversation\",\n \"Leave this conversation? You will lose access unless someone adds you again.\",\n ),\n );\n if (!confirmed) return;\n try {\n await leaveConversation(conversationId);\n if (currentUserId) {\n bus.emit(\"conversations.member.removed\", {\n conversationId,\n userId: currentUserId,\n });\n }\n setMoreMenuOpen(false);\n navigate(basePath || \"/conversations\");\n } catch (error) {\n console.error(\"[microfe-conversations] conversation leave failed\", error);\n nativeNotify(\"error\");\n }\n }, [\n basePath,\n bus,\n conversationId,\n currentUserId,\n leaveConversation,\n navigate,\n tr,\n ]);\n\n const handleOpenContext = useCallback(() => {\n if (!sourceContext?.contextHref) return;\n try {\n const url = new URL(sourceContext.contextHref);\n if (url.origin === window.location.origin) {\n navigate(`${url.pathname}${url.search}${url.hash}`);\n return;\n }\n } catch {\n // Fall through to full navigation for relative or invalid URLs.\n }\n\n window.location.assign(sourceContext.contextHref);\n }, [navigate, sourceContext?.contextHref]);\n\n if (!conversation && conversationLoading) {\n return (\n <div className=\"flex items-center justify-center h-[calc(100vh-64px)] text-sm text-muted-foreground\">\n {tr(\"conversations.loading\", \"Loading conversation...\")}\n </div>\n );\n }\n\n if (!conversation || conversationError) {\n return (\n <div className=\"flex flex-col items-center justify-center h-[calc(100vh-64px)] text-center gap-3 px-4\">\n <MessageCircle className=\"size-8 text-muted-foreground\" />\n <p className=\"text-sm text-muted-foreground\">\n {conversationError\n ? tr(\n \"conversations.unavailable\",\n \"We couldn't load this conversation. Please try again.\",\n )\n : tr(\"conversations.notFound\", \"Conversation not found\")}\n </p>\n <button\n type=\"button\"\n onClick={() => navigate(basePath || \"/conversations\")}\n className=\"px-3 py-2 text-sm font-medium rounded-lg border border-border hover:bg-muted/50\"\n >\n {tr(\"conversations.back\", \"Back to conversations\")}\n </button>\n </div>\n );\n }\n\n return (\n <div className=\"relative flex h-[calc(100vh-64px)] bg-background\">\n <div className=\"flex-1 flex flex-col min-w-0\">\n <header\n data-tour=\"conversation-detail-header\"\n className=\"flex items-center justify-between px-4 py-3 border-b border-border bg-background/95 backdrop-blur-sm\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <button\n type=\"button\"\n onClick={() => navigate(basePath || \"/conversations\")}\n className=\"flex min-h-10 min-w-10 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground\"\n >\n <ArrowLeft size={18} />\n </button>\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-1.5\">\n {conversation.type === \"CHANNEL\" && (\n <Hash\n size={14}\n className=\"text-muted-foreground flex-shrink-0\"\n />\n )}\n <h2 className=\"text-sm font-semibold text-foreground truncate\">\n {conversation.title}\n </h2>\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {conversation.type === \"DIRECT\"\n ? tr(\n \"conversations.directConversation\",\n \"Direct conversation\",\n )\n : conversation.type === \"CHANNEL\"\n ? `${tr(\"conversations.channel\", \"Channel\")} · ${conversation.memberCount} ${tr(\"conversations.membersCountLabel\", \"members\")}`\n : conversation.type === \"ANNOUNCEMENT\"\n ? `${tr(\"conversations.announcement\", \"Announcement\")} · ${conversation.memberCount} ${tr(\"conversations.membersCountLabel\", \"members\")}`\n : `${conversation.memberCount} ${tr(\"conversations.membersCountLabel\", \"members\")}`}\n </p>\n </div>\n </div>\n\n <div className=\"flex shrink-0 items-center gap-1\">\n {sourceContext?.contextHref && (\n <HeaderButton\n dataTour=\"conversation-open-context\"\n icon={<ExternalLink size={16} />}\n label={tr(\"conversations.openContext\", \"Open context\")}\n onClick={handleOpenContext}\n />\n )}\n {conversation.isGroup && (\n <HeaderButton\n dataTour=\"conversation-participants-button\"\n icon={<Users size={16} />}\n label={tr(\"conversations.participants\", \"Participants\")}\n active={isParticipantsPanelOpen}\n onClick={toggleParticipantsPanel}\n />\n )}\n <div className=\"relative\" ref={moreMenuRef}>\n <HeaderButton\n icon={<MoreVertical size={16} />}\n label={tr(\"conversations.moreOptions\", \"More options\")}\n active={moreMenuOpen}\n onClick={() => setMoreMenuOpen((prev) => !prev)}\n />\n {moreMenuOpen && (\n <div className=\"absolute right-0 top-full mt-1 w-48 bg-popover border border-border rounded-lg shadow-lg z-50 py-1 animate-in fade-in-0 zoom-in-95 duration-100\">\n <DropdownItem\n icon={\n conversation.isPinned ? (\n <PinOff size={14} />\n ) : (\n <Pin size={14} />\n )\n }\n label={\n conversation.isPinned\n ? tr(\n \"conversations.unpinConversation\",\n \"Unpin conversation\",\n )\n : tr(\n \"conversations.pinConversation\",\n \"Pin conversation\",\n )\n }\n onClick={() => void handlePin()}\n />\n <DropdownItem\n icon={\n conversation.isMuted ? (\n <Bell size={14} />\n ) : (\n <BellOff size={14} />\n )\n }\n label={\n conversation.isMuted\n ? tr(\n \"conversations.unmuteConversation\",\n \"Unmute conversation\",\n )\n : tr(\n \"conversations.muteConversation\",\n \"Mute conversation\",\n )\n }\n onClick={() => void handleMute()}\n />\n <div className=\"h-px bg-border my-1\" />\n <DropdownItem\n icon={<Archive size={14} />}\n label={tr(\n \"conversations.archiveConversation\",\n \"Archive conversation\",\n )}\n onClick={() => void handleArchive()}\n destructive\n />\n {conversation.isGroup && (\n <DropdownItem\n icon={<LogOut size={14} />}\n label={tr(\n \"conversations.leaveConversation\",\n \"Leave conversation\",\n )}\n onClick={() => void handleLeave()}\n destructive\n />\n )}\n </div>\n )}\n </div>\n </div>\n </header>\n\n <div className=\"flex-1 overflow-y-auto py-4 space-y-0.5\">\n {sourceContext?.pagePath && (\n <div className=\"mx-4 mb-4 rounded-lg border border-border bg-muted/20 px-3 py-2 text-xs text-muted-foreground\">\n <span className=\"font-medium text-foreground\">\n {sourceContext.contextTitle || sourceContext.pagePath}\n </span>\n {sourceContext.entityType && sourceContext.entityId\n ? ` · ${sourceContext.entityType} ${sourceContext.entityId}`\n : \"\"}\n </div>\n )}\n {messagesError && (\n <div className=\"mx-4 mb-4 rounded-lg border border-status-warning-border bg-status-warning-bg px-3 py-2 text-sm text-status-warning-text\">\n {tr(\n \"conversations.messagesUnavailable\",\n \"Messages couldn't be loaded right now. The conversation header and actions are still available.\",\n )}\n </div>\n )}\n {messagesLoading && (\n <div className=\"mx-4 mb-4 flex items-center gap-2 rounded-lg border border-border bg-muted/20 px-3 py-2 text-sm text-muted-foreground\">\n <Loader2 size={14} className=\"animate-spin\" />\n {tr(\"conversations.loadingMessages\", \"Loading messages...\")}\n </div>\n )}\n {!messagesLoading &&\n !messagesError &&\n displayedMessages.length === 0 && (\n <div className=\"mx-4 mb-4 rounded-lg border border-dashed border-border bg-muted/20 px-3 py-6 text-center text-sm text-muted-foreground\">\n {tr(\n \"conversations.noMessagesYet\",\n \"No messages yet. Start the conversation below.\",\n )}\n </div>\n )}\n {displayedMessages.map((message, index) => {\n const prevMessage = displayedMessages[index - 1];\n const showAvatar =\n !prevMessage ||\n prevMessage.authorId !== message.authorId ||\n prevMessage.type === \"SYSTEM\";\n\n return (\n <div key={message.id}>\n {editingMessageId === message.id ? (\n <div className=\"px-4 py-2\">\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground mb-1\">\n <span>\n {tr(\"conversations.editingMessage\", \"Editing message\")}\n </span>\n <button\n type=\"button\"\n onClick={handleEditCancel}\n className=\"hover:text-foreground\"\n >\n <X size={12} />\n </button>\n </div>\n <div className=\"flex items-end gap-2\">\n <textarea\n aria-label=\"Edit message\"\n value={editingContent}\n onChange={(e) => setEditingContent(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n void handleEditSubmit();\n }\n if (e.key === \"Escape\") handleEditCancel();\n }}\n className=\"flex-1 px-3 py-2 text-sm rounded-lg border border-border bg-muted/30 resize-none focus:outline-none focus:ring-2 focus:ring-ring\"\n rows={2}\n />\n <button\n type=\"button\"\n onClick={() => void handleEditSubmit()}\n disabled={!editingContent.trim() || editingMessage}\n className=\"px-3 py-2 text-xs font-medium rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50\"\n >\n {tr(\"conversations.save\", \"Save\")}\n </button>\n </div>\n </div>\n ) : (\n <MessageBubble\n message={message}\n isOwnMessage={message.authorId === currentUserId}\n showAvatar={showAvatar}\n showSender={showAvatar}\n onReply={handleReply}\n onReact={(id, emoji) => void handleReact(id, emoji)}\n onEdit={handleEdit}\n onDelete={(id) => void handleDelete(id)}\n onTogglePin={(id, isPinned) =>\n void handleTogglePinMessage(id, isPinned)\n }\n />\n )}\n </div>\n );\n })}\n <div ref={messagesEndRef} />\n </div>\n\n <div\n data-tour=\"conversation-message-composer\"\n className=\"border-t border-border px-4 py-3 bg-background\"\n >\n {/* Reply-to indicator */}\n {replyingTo && (\n <div className=\"flex items-center gap-2 mb-2 px-3 py-1.5 rounded-lg bg-muted/50 text-xs text-muted-foreground\">\n <span>\n {tr(\"conversations.replyingTo\", \"Replying to\")}{\" \"}\n <strong className=\"text-foreground\">{replyingTo.sender}</strong>\n : {replyingTo.content.slice(0, 80)}\n {replyingTo.content.length > 80 ? \"…\" : \"\"}\n </span>\n <button\n type=\"button\"\n onClick={() => setReplyingTo(null)}\n aria-label={tr(\"conversations.cancelReply\", \"Cancel reply\")}\n className=\"ml-auto flex min-h-9 min-w-9 items-center justify-center rounded-md hover:bg-muted/70 hover:text-foreground\"\n >\n <X size={12} />\n </button>\n </div>\n )}\n <div className=\"flex items-end gap-2\">\n <div className=\"flex-1 relative\">\n <textarea\n aria-label={tr(\n \"conversations.messageComposer\",\n \"Message composer\",\n )}\n value={messageInput}\n onChange={(event) => setMessageInput(event.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={`${tr(\"conversations.messageConversationPrefix\", \"Message\")} ${conversation.title}...`}\n rows={1}\n className={cn(\n \"w-full px-4 py-2.5 text-sm rounded-xl border border-border bg-muted/30\",\n \"resize-none overflow-hidden placeholder:text-muted-foreground\",\n \"focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent max-h-32\",\n )}\n style={{\n height: \"auto\",\n minHeight: \"40px\",\n }}\n onInput={(event) => {\n const target = event.target as HTMLTextAreaElement;\n target.style.height = \"auto\";\n target.style.height = `${Math.min(target.scrollHeight, 128)}px`;\n }}\n />\n </div>\n\n <button\n type=\"button\"\n aria-label={tr(\"conversations.sendMessage\", \"Send message\")}\n onClick={() => void handleSendMessage()}\n disabled={!messageInput.trim() || sendingMessage}\n className={cn(\n \"p-2.5 rounded-xl transition-all duration-150\",\n messageInput.trim() && !sendingMessage\n ? \"bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm\"\n : \"bg-muted text-muted-foreground cursor-not-allowed\",\n )}\n >\n {sendingMessage ? (\n <Loader2 size={18} className=\"animate-spin\" />\n ) : (\n <SendHorizontal size={18} />\n )}\n </button>\n </div>\n </div>\n </div>\n\n {isParticipantsPanelOpen && conversation.isGroup && (\n <aside className=\"absolute inset-y-0 right-0 z-20 w-full max-w-72 border-l border-border bg-background shadow-lg animate-in slide-in-from-right-4 duration-200 md:relative md:z-auto md:w-64 md:shadow-none\">\n <div className=\"p-4\">\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"text-sm font-semibold text-foreground\">\n {tr(\"conversations.members\", \"Members\")} (\n {conversation.memberCount ?? participants.length})\n </h3>\n <button\n type=\"button\"\n onClick={toggleParticipantsPanel}\n className=\"p-1 rounded text-muted-foreground hover:text-foreground\"\n >\n <Pin size={14} />\n </button>\n </div>\n\n <div className=\"space-y-1\">\n {participants.map((participant) => (\n <div\n key={participant.id}\n className=\"flex items-center gap-2.5 px-2 py-1.5 rounded-lg hover:bg-muted/50\"\n >\n <div className=\"size-7 rounded-full bg-muted flex items-center justify-center text-xs font-medium text-muted-foreground\">\n {participant.name\n .split(\" \")\n .map((word) => word[0])\n .join(\"\")\n .slice(0, 2)}\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-xs font-medium text-foreground truncate\">\n {participant.name}\n </p>\n <p className=\"text-[10px] text-muted-foreground\">\n {participant.role}\n </p>\n </div>\n </div>\n ))}\n {hiddenParticipantsCount > 0 && (\n <p className=\"px-2 pt-2 text-xs text-muted-foreground\">\n {tr(\"conversations.moreMembersPrefix\", \"and\")}{\" \"}\n {hiddenParticipantsCount}{\" \"}\n {tr(\"conversations.moreMembersSuffix\", \"more\")}\n </p>\n )}\n </div>\n </div>\n </aside>\n )}\n </div>\n );\n}\n\nfunction DropdownItem({\n icon,\n label,\n onClick,\n destructive,\n}: {\n icon: React.ReactNode;\n label: string;\n onClick: () => void;\n destructive?: boolean;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className={cn(\n \"flex items-center gap-2.5 w-full px-3 py-2 text-sm transition-colors text-left\",\n destructive\n ? \"text-destructive hover:bg-destructive/10\"\n : \"text-foreground hover:bg-muted/50\",\n )}\n >\n {icon}\n <span>{label}</span>\n </button>\n );\n}\n\nfunction HeaderButton({\n icon,\n label,\n active,\n dataTour,\n onClick,\n}: {\n icon: React.ReactNode;\n label: string;\n active?: boolean;\n dataTour?: string;\n onClick?: () => void;\n}) {\n return (\n <PermissionGuard\n permission={CONVERSATIONS_PERMISSIONS.CONVERSATION_READ}\n allowUnavailablePermissions\n >\n <button\n data-tour={dataTour}\n type=\"button\"\n onClick={onClick}\n aria-label={label}\n title={label}\n className={cn(\n \"flex min-h-10 min-w-10 items-center justify-center rounded-lg transition-colors\",\n active\n ? \"bg-primary/10 text-primary\"\n : \"text-muted-foreground hover:text-foreground hover:bg-muted/50\",\n )}\n >\n {icon}\n </button>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
|
|
@@ -126,14 +126,23 @@ e`
|
|
|
126
126
|
success
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
|
-
|
|
129
|
+
`;
|
|
130
|
+
var l = e`
|
|
130
131
|
mutation LeaveConversation($conversationId: ID!) {
|
|
131
132
|
leaveConversation(conversationId: $conversationId) {
|
|
132
133
|
conversationId
|
|
133
134
|
success
|
|
134
135
|
}
|
|
135
136
|
}
|
|
136
|
-
|
|
137
|
+
`;
|
|
138
|
+
function u(e) {
|
|
139
|
+
let r = {
|
|
140
|
+
...n,
|
|
141
|
+
...e
|
|
142
|
+
};
|
|
143
|
+
return t.useMutation(l, r);
|
|
144
|
+
}
|
|
145
|
+
e`
|
|
137
146
|
mutation UpdateParticipantRole($input: UpdateParticipantRoleInput!) {
|
|
138
147
|
updateParticipantRole(input: $input) {
|
|
139
148
|
id
|
|
@@ -148,7 +157,7 @@ e`
|
|
|
148
157
|
}
|
|
149
158
|
}
|
|
150
159
|
`;
|
|
151
|
-
var
|
|
160
|
+
var d = e`
|
|
152
161
|
mutation SendMessage($input: SendMessageInput!) {
|
|
153
162
|
sendMessage(input: $input) {
|
|
154
163
|
message {
|
|
@@ -201,14 +210,14 @@ var l = e`
|
|
|
201
210
|
}
|
|
202
211
|
}
|
|
203
212
|
`;
|
|
204
|
-
function
|
|
213
|
+
function f(e) {
|
|
205
214
|
let r = {
|
|
206
215
|
...n,
|
|
207
216
|
...e
|
|
208
217
|
};
|
|
209
|
-
return t.useMutation(
|
|
218
|
+
return t.useMutation(d, r);
|
|
210
219
|
}
|
|
211
|
-
var
|
|
220
|
+
var p = e`
|
|
212
221
|
mutation EditMessage($input: EditMessageInput!) {
|
|
213
222
|
editMessage(input: $input) {
|
|
214
223
|
id
|
|
@@ -226,14 +235,14 @@ var d = e`
|
|
|
226
235
|
}
|
|
227
236
|
}
|
|
228
237
|
`;
|
|
229
|
-
function
|
|
238
|
+
function m(e) {
|
|
230
239
|
let r = {
|
|
231
240
|
...n,
|
|
232
241
|
...e
|
|
233
242
|
};
|
|
234
|
-
return t.useMutation(
|
|
243
|
+
return t.useMutation(p, r);
|
|
235
244
|
}
|
|
236
|
-
var
|
|
245
|
+
var h = e`
|
|
237
246
|
mutation DeleteMessage($id: ID!, $hard: Boolean) {
|
|
238
247
|
deleteMessage(id: $id, hard: $hard) {
|
|
239
248
|
messageId
|
|
@@ -241,14 +250,14 @@ var p = e`
|
|
|
241
250
|
}
|
|
242
251
|
}
|
|
243
252
|
`;
|
|
244
|
-
function
|
|
253
|
+
function g(e) {
|
|
245
254
|
let r = {
|
|
246
255
|
...n,
|
|
247
256
|
...e
|
|
248
257
|
};
|
|
249
|
-
return t.useMutation(
|
|
258
|
+
return t.useMutation(h, r);
|
|
250
259
|
}
|
|
251
|
-
var
|
|
260
|
+
var _ = e`
|
|
252
261
|
mutation AddReaction($messageId: ID!, $emoji: String!) {
|
|
253
262
|
addReaction(messageId: $messageId, emoji: $emoji) {
|
|
254
263
|
id
|
|
@@ -259,14 +268,14 @@ var h = e`
|
|
|
259
268
|
}
|
|
260
269
|
}
|
|
261
270
|
`;
|
|
262
|
-
function
|
|
271
|
+
function v(e) {
|
|
263
272
|
let r = {
|
|
264
273
|
...n,
|
|
265
274
|
...e
|
|
266
275
|
};
|
|
267
|
-
return t.useMutation(
|
|
276
|
+
return t.useMutation(_, r);
|
|
268
277
|
}
|
|
269
|
-
var
|
|
278
|
+
var y = e`
|
|
270
279
|
mutation RemoveReaction($messageId: ID!, $emoji: String!) {
|
|
271
280
|
removeReaction(messageId: $messageId, emoji: $emoji) {
|
|
272
281
|
messageId
|
|
@@ -275,98 +284,98 @@ var _ = e`
|
|
|
275
284
|
}
|
|
276
285
|
}
|
|
277
286
|
`;
|
|
278
|
-
function
|
|
287
|
+
function b(e) {
|
|
279
288
|
let r = {
|
|
280
289
|
...n,
|
|
281
290
|
...e
|
|
282
291
|
};
|
|
283
|
-
return t.useMutation(
|
|
292
|
+
return t.useMutation(y, r);
|
|
284
293
|
}
|
|
285
|
-
var
|
|
294
|
+
var x = e`
|
|
286
295
|
mutation MuteConversation($conversationId: ID!) {
|
|
287
296
|
muteConversation(conversationId: $conversationId) {
|
|
288
297
|
id
|
|
289
298
|
}
|
|
290
299
|
}
|
|
291
300
|
`;
|
|
292
|
-
function
|
|
301
|
+
function S(e) {
|
|
293
302
|
let r = {
|
|
294
303
|
...n,
|
|
295
304
|
...e
|
|
296
305
|
};
|
|
297
|
-
return t.useMutation(
|
|
306
|
+
return t.useMutation(x, r);
|
|
298
307
|
}
|
|
299
|
-
var
|
|
308
|
+
var C = e`
|
|
300
309
|
mutation PinConversation($conversationId: ID!) {
|
|
301
310
|
pinConversation(conversationId: $conversationId) {
|
|
302
311
|
id
|
|
303
312
|
}
|
|
304
313
|
}
|
|
305
314
|
`;
|
|
306
|
-
function
|
|
315
|
+
function w(e) {
|
|
307
316
|
let r = {
|
|
308
317
|
...n,
|
|
309
318
|
...e
|
|
310
319
|
};
|
|
311
|
-
return t.useMutation(
|
|
320
|
+
return t.useMutation(C, r);
|
|
312
321
|
}
|
|
313
|
-
var
|
|
322
|
+
var T = e`
|
|
314
323
|
mutation PinMessageByHook($messageId: ID!) {
|
|
315
324
|
pinMessage(messageId: $messageId) {
|
|
316
325
|
id
|
|
317
326
|
}
|
|
318
327
|
}
|
|
319
328
|
`;
|
|
320
|
-
function
|
|
329
|
+
function E(e) {
|
|
321
330
|
let r = {
|
|
322
331
|
...n,
|
|
323
332
|
...e
|
|
324
333
|
};
|
|
325
|
-
return t.useMutation(
|
|
334
|
+
return t.useMutation(T, r);
|
|
326
335
|
}
|
|
327
|
-
var
|
|
336
|
+
var D = e`
|
|
328
337
|
mutation UnmuteConversation($conversationId: ID!) {
|
|
329
338
|
unmuteConversation(conversationId: $conversationId) {
|
|
330
339
|
id
|
|
331
340
|
}
|
|
332
341
|
}
|
|
333
342
|
`;
|
|
334
|
-
function
|
|
343
|
+
function O(e) {
|
|
335
344
|
let r = {
|
|
336
345
|
...n,
|
|
337
346
|
...e
|
|
338
347
|
};
|
|
339
|
-
return t.useMutation(
|
|
348
|
+
return t.useMutation(D, r);
|
|
340
349
|
}
|
|
341
|
-
var
|
|
350
|
+
var k = e`
|
|
342
351
|
mutation UnpinConversation($conversationId: ID!) {
|
|
343
352
|
unpinConversation(conversationId: $conversationId) {
|
|
344
353
|
id
|
|
345
354
|
}
|
|
346
355
|
}
|
|
347
356
|
`;
|
|
348
|
-
function
|
|
357
|
+
function A(e) {
|
|
349
358
|
let r = {
|
|
350
359
|
...n,
|
|
351
360
|
...e
|
|
352
361
|
};
|
|
353
|
-
return t.useMutation(
|
|
362
|
+
return t.useMutation(k, r);
|
|
354
363
|
}
|
|
355
|
-
var
|
|
364
|
+
var j = e`
|
|
356
365
|
mutation UnpinMessageByHook($messageId: ID!) {
|
|
357
366
|
unpinMessage(messageId: $messageId) {
|
|
358
367
|
id
|
|
359
368
|
}
|
|
360
369
|
}
|
|
361
370
|
`;
|
|
362
|
-
function
|
|
371
|
+
function M(e) {
|
|
363
372
|
let r = {
|
|
364
373
|
...n,
|
|
365
374
|
...e
|
|
366
375
|
};
|
|
367
|
-
return t.useMutation(
|
|
376
|
+
return t.useMutation(j, r);
|
|
368
377
|
}
|
|
369
|
-
var
|
|
378
|
+
var N = e`
|
|
370
379
|
mutation UpdateConversationByHook($input: UpdateConversationInput!) {
|
|
371
380
|
updateConversation(input: $input) {
|
|
372
381
|
success
|
|
@@ -399,14 +408,14 @@ var j = e`
|
|
|
399
408
|
}
|
|
400
409
|
}
|
|
401
410
|
`;
|
|
402
|
-
function
|
|
411
|
+
function P(e) {
|
|
403
412
|
let r = {
|
|
404
413
|
...n,
|
|
405
414
|
...e
|
|
406
415
|
};
|
|
407
|
-
return t.useMutation(
|
|
416
|
+
return t.useMutation(N, r);
|
|
408
417
|
}
|
|
409
|
-
var
|
|
418
|
+
var F = e`
|
|
410
419
|
query GetConversation($id: ID!) {
|
|
411
420
|
conversation(id: $id) {
|
|
412
421
|
id
|
|
@@ -442,6 +451,21 @@ var N = e`
|
|
|
442
451
|
customAvatar
|
|
443
452
|
}
|
|
444
453
|
memberCount
|
|
454
|
+
members(first: 25) {
|
|
455
|
+
totalCount
|
|
456
|
+
edges {
|
|
457
|
+
node {
|
|
458
|
+
id
|
|
459
|
+
userId
|
|
460
|
+
role
|
|
461
|
+
status
|
|
462
|
+
joinedAt
|
|
463
|
+
leftAt
|
|
464
|
+
customTitle
|
|
465
|
+
customAvatar
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
445
469
|
unreadCount
|
|
446
470
|
mentionCount
|
|
447
471
|
features {
|
|
@@ -458,14 +482,14 @@ var N = e`
|
|
|
458
482
|
}
|
|
459
483
|
}
|
|
460
484
|
`;
|
|
461
|
-
function
|
|
485
|
+
function I(e) {
|
|
462
486
|
let r = {
|
|
463
487
|
...n,
|
|
464
488
|
...e
|
|
465
489
|
};
|
|
466
|
-
return t.useQuery(
|
|
490
|
+
return t.useQuery(F, r);
|
|
467
491
|
}
|
|
468
|
-
var
|
|
492
|
+
var L = e`
|
|
469
493
|
query GetConversations($first: Int, $after: String, $filter: ConversationFilter, $sort: ConversationSort) {
|
|
470
494
|
conversations(first: $first, after: $after, filter: $filter, sort: $sort) {
|
|
471
495
|
edges {
|
|
@@ -525,14 +549,14 @@ var F = e`
|
|
|
525
549
|
}
|
|
526
550
|
}
|
|
527
551
|
`;
|
|
528
|
-
function
|
|
552
|
+
function R(e) {
|
|
529
553
|
let r = {
|
|
530
554
|
...n,
|
|
531
555
|
...e
|
|
532
556
|
};
|
|
533
|
-
return t.useQuery(
|
|
557
|
+
return t.useQuery(L, r);
|
|
534
558
|
}
|
|
535
|
-
var
|
|
559
|
+
var z = e`
|
|
536
560
|
query GetMessages($conversationId: ID!, $first: Int, $after: String, $before: String, $filter: MessageFilter) {
|
|
537
561
|
messages(
|
|
538
562
|
conversationId: $conversationId
|
|
@@ -614,12 +638,12 @@ var L = e`
|
|
|
614
638
|
}
|
|
615
639
|
}
|
|
616
640
|
`;
|
|
617
|
-
function
|
|
641
|
+
function B(e) {
|
|
618
642
|
let r = {
|
|
619
643
|
...n,
|
|
620
644
|
...e
|
|
621
645
|
};
|
|
622
|
-
return t.useQuery(
|
|
646
|
+
return t.useQuery(z, r);
|
|
623
647
|
}
|
|
624
648
|
e`
|
|
625
649
|
query GetPresence($userIds: [ID!]!) {
|
|
@@ -695,7 +719,7 @@ e`
|
|
|
695
719
|
}
|
|
696
720
|
}
|
|
697
721
|
`;
|
|
698
|
-
var
|
|
722
|
+
var V = e`
|
|
699
723
|
query SearchConversations($query: String!, $first: Int, $after: String, $filter: ConversationFilter) {
|
|
700
724
|
searchConversations(
|
|
701
725
|
query: $query
|
|
@@ -751,12 +775,12 @@ var z = e`
|
|
|
751
775
|
}
|
|
752
776
|
}
|
|
753
777
|
`;
|
|
754
|
-
function
|
|
778
|
+
function H(e) {
|
|
755
779
|
let r = {
|
|
756
780
|
...n,
|
|
757
781
|
...e
|
|
758
782
|
};
|
|
759
|
-
return t.useQuery(
|
|
783
|
+
return t.useQuery(V, r);
|
|
760
784
|
}
|
|
761
785
|
e`
|
|
762
786
|
query SearchMessagesByHook($query: String!, $conversationId: ID, $first: Int) {
|
|
@@ -818,7 +842,7 @@ e`
|
|
|
818
842
|
}
|
|
819
843
|
}
|
|
820
844
|
`;
|
|
821
|
-
var
|
|
845
|
+
var U = e`
|
|
822
846
|
subscription ConversationAdded {
|
|
823
847
|
conversationAdded {
|
|
824
848
|
id
|
|
@@ -833,7 +857,7 @@ var V = e`
|
|
|
833
857
|
updatedAt
|
|
834
858
|
}
|
|
835
859
|
}
|
|
836
|
-
`,
|
|
860
|
+
`, W = e`
|
|
837
861
|
subscription ConversationUpdated($conversationId: ID!) {
|
|
838
862
|
conversationUpdated(conversationId: $conversationId) {
|
|
839
863
|
changes
|
|
@@ -849,14 +873,14 @@ var V = e`
|
|
|
849
873
|
}
|
|
850
874
|
}
|
|
851
875
|
}
|
|
852
|
-
`,
|
|
876
|
+
`, G = e`
|
|
853
877
|
subscription ConversationDeleted($conversationId: ID!) {
|
|
854
878
|
conversationDeleted(conversationId: $conversationId) {
|
|
855
879
|
conversationId
|
|
856
880
|
deletedBy
|
|
857
881
|
}
|
|
858
882
|
}
|
|
859
|
-
`,
|
|
883
|
+
`, K = e`
|
|
860
884
|
subscription ParticipantChanged($conversationId: ID!) {
|
|
861
885
|
participantChanged(conversationId: $conversationId) {
|
|
862
886
|
changeType
|
|
@@ -876,7 +900,7 @@ var V = e`
|
|
|
876
900
|
}
|
|
877
901
|
}
|
|
878
902
|
}
|
|
879
|
-
`,
|
|
903
|
+
`, q = e`
|
|
880
904
|
subscription MessageAdded($conversationId: ID!) {
|
|
881
905
|
messageAdded(conversationId: $conversationId) {
|
|
882
906
|
id
|
|
@@ -937,7 +961,7 @@ var V = e`
|
|
|
937
961
|
}
|
|
938
962
|
}
|
|
939
963
|
}
|
|
940
|
-
`,
|
|
964
|
+
`, J = e`
|
|
941
965
|
subscription MessageUpdated($conversationId: ID!) {
|
|
942
966
|
messageUpdated(conversationId: $conversationId) {
|
|
943
967
|
id
|
|
@@ -952,7 +976,7 @@ var V = e`
|
|
|
952
976
|
updatedAt
|
|
953
977
|
}
|
|
954
978
|
}
|
|
955
|
-
`,
|
|
979
|
+
`, Y = e`
|
|
956
980
|
subscription MessageDeleted($conversationId: ID!) {
|
|
957
981
|
messageDeleted(conversationId: $conversationId) {
|
|
958
982
|
conversationId
|
|
@@ -1008,6 +1032,6 @@ e`
|
|
|
1008
1032
|
}
|
|
1009
1033
|
`;
|
|
1010
1034
|
//#endregion
|
|
1011
|
-
export {
|
|
1035
|
+
export { U as ConversationAddedDocument, G as ConversationDeletedDocument, W as ConversationUpdatedDocument, z as GetMessagesDocument, q as MessageAddedDocument, Y as MessageDeletedDocument, J as MessageUpdatedDocument, K as ParticipantChangedDocument, v as useAddReactionMutation, o as useArchiveConversationMutation, i as useCreateConversationMutation, g as useDeleteMessageMutation, m as useEditMessageMutation, I as useGetConversationQuery, R as useGetConversationsQuery, B as useGetMessagesQuery, u as useLeaveConversationMutation, S as useMuteConversationMutation, w as usePinConversationMutation, E as usePinMessageByHookMutation, b as useRemoveReactionMutation, H as useSearchConversationsQuery, f as useSendMessageMutation, c as useUnarchiveConversationMutation, O as useUnmuteConversationMutation, A as useUnpinConversationMutation, M as useUnpinMessageByHookMutation, P as useUpdateConversationByHookMutation };
|
|
1012
1036
|
|
|
1013
1037
|
//# sourceMappingURL=wspace-operations.js.map
|