diff --git a/packages/web/src/components/chat/Message.tsx b/packages/web/src/components/chat/Message.tsx index c32defc3..91fc8c39 100644 --- a/packages/web/src/components/chat/Message.tsx +++ b/packages/web/src/components/chat/Message.tsx @@ -268,14 +268,15 @@ export function Message({ message, isCompact, isFirstInGroup }: MessageProps) {
{message.attachments.map((att) => { const isImage = att.mimetype.startsWith('image/'); + const attUrl = att.filename.startsWith('http') ? att.filename : `/api/uploads/${att.filename}`; if (isImage) { return (
{att.originalName} openImagePreview(`/api/uploads/${att.filename}`)} + onClick={() => openImagePreview(attUrl)} loading="lazy" />
@@ -284,7 +285,7 @@ export function Message({ message, isCompact, isFirstInGroup }: MessageProps) { return ( diff --git a/packages/web/src/components/chat/MessageInput.tsx b/packages/web/src/components/chat/MessageInput.tsx index 2dd2d941..e15dc7cb 100644 --- a/packages/web/src/components/chat/MessageInput.tsx +++ b/packages/web/src/components/chat/MessageInput.tsx @@ -1,8 +1,7 @@ import React, { useState, useRef, useCallback, useMemo, useEffect } from 'react'; import { useChatStore } from '../../stores/chatStore'; -import { isDmChannel, useServerStore } from '../../stores/serverStore'; +import { isDmChannel, getChannelOrigin, getApiForOrigin, useServerStore } from '../../stores/serverStore'; import { wsSend } from '../../hooks/useWebSocket'; -import { api } from '../../api/client'; import { MentionPopover } from './MentionPopover'; import type { MemberWithUser } from '@backspace/shared'; @@ -61,7 +60,7 @@ export function MessageInput({ channelId, channelName }: MessageInputProps) { if (isDm) { wsSend({ type: 'dm_typing_start', dmChannelId: channelId }); } else { - wsSend({ type: 'typing_start', channelId }); + wsSend({ type: 'typing_start', channelId }, getChannelOrigin(channelId)); } typingTimeoutRef.current = setTimeout(() => { typingTimeoutRef.current = undefined; @@ -75,10 +74,11 @@ export function MessageInput({ channelId, channelName }: MessageInputProps) { setIsUploading(true); setMentionState(null); try { - // Upload files first + // Upload files first — route to the correct instance for this channel const attachmentIds: string[] = []; + const uploadClient = getApiForOrigin(getChannelOrigin(channelId)); for (const file of files) { - const attachment = await api.uploads.upload(file); + const attachment = await uploadClient.uploads.upload(file); attachmentIds.push(attachment.id); } diff --git a/packages/web/src/components/layout/ChannelSidebar.tsx b/packages/web/src/components/layout/ChannelSidebar.tsx index f248d8dd..e2022f19 100644 --- a/packages/web/src/components/layout/ChannelSidebar.tsx +++ b/packages/web/src/components/layout/ChannelSidebar.tsx @@ -1,6 +1,6 @@ import React, { useState, useRef, useEffect, useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; -import { useServerStore } from '../../stores/serverStore'; +import { useServerStore, getChannelOrigin } from '../../stores/serverStore'; import { useChatStore } from '../../stores/chatStore'; import { useUIStore } from '../../stores/uiStore'; import { useAuthStore } from '../../stores/authStore'; @@ -38,7 +38,8 @@ export function ChannelSidebar() { // Broadcast mute status via WebSocket so non-joined users can see it const willBeMuted = !isMuted; const { isCameraOn, isScreenSharing } = useVoiceStore.getState(); - wsSend({ type: 'voice_status', isMuted: willBeMuted, isDeafened, isCameraOn, isScreenSharing }); + const voiceOrigin = currentVoiceChannelId ? getChannelOrigin(currentVoiceChannelId) : ''; + wsSend({ type: 'voice_status', isMuted: willBeMuted, isDeafened, isCameraOn, isScreenSharing }, voiceOrigin); }; const handleDeafenToggle = async () => { @@ -51,7 +52,8 @@ export function ChannelSidebar() { // Broadcast status via WebSocket so non-joined users can see it const willBeMuted = willDeafen ? true : false; const { isCameraOn, isScreenSharing } = useVoiceStore.getState(); - wsSend({ type: 'voice_status', isMuted: willBeMuted, isDeafened: willDeafen, isCameraOn, isScreenSharing }); + const voiceOrigin2 = currentVoiceChannelId ? getChannelOrigin(currentVoiceChannelId) : ''; + wsSend({ type: 'voice_status', isMuted: willBeMuted, isDeafened: willDeafen, isCameraOn, isScreenSharing }, voiceOrigin2); if (room) { try { // Broadcast deafen state to other participants via LiveKit data channel @@ -91,7 +93,7 @@ export function ChannelSidebar() { return; } setCurrentVoiceChannel(channelId); - wsSend({ type: 'voice_join', channelId }); + wsSend({ type: 'voice_join', channelId }, getChannelOrigin(channelId)); navigate(`/channels/${currentServerId}/${channelId}`); }; diff --git a/packages/web/src/components/layout/MainContent.tsx b/packages/web/src/components/layout/MainContent.tsx index f6ee6fb5..205fa28b 100644 --- a/packages/web/src/components/layout/MainContent.tsx +++ b/packages/web/src/components/layout/MainContent.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState, useCallback } from 'react'; -import { useServerStore } from '../../stores/serverStore'; +import { useServerStore, getChannelOrigin } from '../../stores/serverStore'; import { useChatStore } from '../../stores/chatStore'; import { useUIStore } from '../../stores/uiStore'; import { useAuthStore } from '../../stores/authStore'; @@ -258,7 +258,7 @@ export function MainContent() {