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:
@@ -3,6 +3,7 @@ import type { MessageWithUser, Reaction, ReadState } from '@backspace/shared';
|
||||
import { wsSend } from '../hooks/useWebSocket';
|
||||
import { isDmChannel, getChannelOrigin, getApiForOrigin, useServerStore } from './serverStore';
|
||||
import { useAuthStore } from './authStore';
|
||||
import { normalizeMessageAssets } from '../utils/assetUrls';
|
||||
|
||||
const MAX_MESSAGES_PER_CHANNEL = 200;
|
||||
const MAX_CACHED_CHANNELS = 20;
|
||||
@@ -140,6 +141,11 @@ export const useChatStore = create<ChatState>((set, get) => ({
|
||||
? await client.dm.messages(channelId)
|
||||
: await client.channels.messages(channelId);
|
||||
|
||||
// Normalize remote asset URLs (avatars, attachment filenames)
|
||||
if (origin) {
|
||||
for (const msg of messages) normalizeMessageAssets(msg, origin);
|
||||
}
|
||||
|
||||
set((state) => {
|
||||
const newMessages = new Map(state.messages);
|
||||
newMessages.set(channelId, messages as MessageWithUser[]);
|
||||
@@ -170,6 +176,11 @@ export const useChatStore = create<ChatState>((set, get) => ({
|
||||
? await client.dm.messages(channelId, oldestMessage.id)
|
||||
: await client.channels.messages(channelId, oldestMessage.id);
|
||||
|
||||
// Normalize remote asset URLs (avatars, attachment filenames)
|
||||
if (origin) {
|
||||
for (const msg of olderMessages) normalizeMessageAssets(msg, origin);
|
||||
}
|
||||
|
||||
set((state) => {
|
||||
const newMessages = new Map(state.messages);
|
||||
const current = newMessages.get(channelId) ?? [];
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { create } from 'zustand';
|
||||
import type { Server, Channel, MemberWithUser, ServerWithChannelsAndMembers, Role, ServerFolder, DmChannel, User } from '@backspace/shared';
|
||||
import { api, BackspaceApiClient } from '../api/client';
|
||||
import { resolveAssetUrl, normalizeUserAssets } from '../utils/assetUrls';
|
||||
|
||||
// ─── Instance-aware types ─────────────────────────────────────────────────────
|
||||
|
||||
@@ -124,6 +125,13 @@ export const useServerStore = create<ServerState>((set, get) => ({
|
||||
const client = getApiForOrigin(origin);
|
||||
|
||||
const detail = await client.servers.get(serverId);
|
||||
// Normalize remote asset URLs (avatars, server icon)
|
||||
if (origin) {
|
||||
if (detail.icon) detail.icon = resolveAssetUrl(detail.icon, origin) ?? detail.icon;
|
||||
for (const member of detail.members) {
|
||||
normalizeUserAssets(member.user, origin);
|
||||
}
|
||||
}
|
||||
set({
|
||||
currentServerId: serverId,
|
||||
channels: detail.channels.sort((a, b) => a.position - b.position),
|
||||
|
||||
Reference in New Issue
Block a user