feat: normalize remote asset URLs and route UI actions by instance origin
Phase 5 of multi-instance federation. Adds a two-layer fix: Layer 1 — Data ingestion normalization: Remote instance user avatars, server icons, and attachment filenames are rewritten to absolute URLs when entering the app (via WebSocket events or API responses), so all downstream components render them correctly without changes. Layer 2 — Outbound action routing: wsSend calls (voice join/leave/status, typing) and file uploads in UI components now route through the correct instance based on the active channel's origin.
This commit is contained in:
@@ -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}`);
|
||||
};
|
||||
|
||||
|
||||
@@ -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() {
|
||||
<button
|
||||
onClick={() => {
|
||||
useVoiceStore.getState().setCurrentVoiceChannel(currentChannelId);
|
||||
wsSend({ type: 'voice_join', channelId: currentChannelId });
|
||||
wsSend({ type: 'voice_join', channelId: currentChannelId }, getChannelOrigin(currentChannelId));
|
||||
}}
|
||||
className="relative z-10 px-8 py-3 bg-accent-primary hover:bg-accent-primary-hover text-white font-semibold rounded-full transition-all text-[15px] shadow-[0_4px_20px_rgba(124,108,246,0.3)]"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user