fix: normalize federated asset URLs in explore and social stores
Resolve remote instance avatars and space icons at the data boundary so they render correctly instead of 404ing against the home instance.
This commit is contained in:
@@ -335,12 +335,14 @@ function handleEvent(origin: string, event: ServerEvent): void {
|
|||||||
// ─── Social events (all origins — federation) ──────────────────────────
|
// ─── Social events (all origins — federation) ──────────────────────────
|
||||||
|
|
||||||
case 'friend_request_received': {
|
case 'friend_request_received': {
|
||||||
|
if (!isHome && event.request.user) normalizeUserAssets(event.request.user, origin);
|
||||||
const { addIncomingRequest } = useSocialStore.getState();
|
const { addIncomingRequest } = useSocialStore.getState();
|
||||||
addIncomingRequest(event.request, origin);
|
addIncomingRequest(event.request, origin);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'friend_request_accepted': {
|
case 'friend_request_accepted': {
|
||||||
|
if (!isHome) normalizeUserAssets(event.friend, origin);
|
||||||
const { addFriendFromAccepted } = useSocialStore.getState();
|
const { addFriendFromAccepted } = useSocialStore.getState();
|
||||||
addFriendFromAccepted(event.friend, event.requestId, origin);
|
addFriendFromAccepted(event.friend, event.requestId, origin);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import type { ExploreSpace, JoinRequest, SpaceWithChannelsAndMembers } from '@backspace/shared';
|
import type { ExploreSpace, JoinRequest, SpaceWithChannelsAndMembers } from '@backspace/shared';
|
||||||
import { api } from '../api/client';
|
import { api } from '../api/client';
|
||||||
|
import { resolveAssetUrl } from '../utils/assetUrls';
|
||||||
import { useInstanceStore } from './instanceStore';
|
import { useInstanceStore } from './instanceStore';
|
||||||
import { useSpaceStore } from './spaceStore';
|
import { useSpaceStore } from './spaceStore';
|
||||||
|
|
||||||
@@ -89,6 +90,9 @@ export const useExploreStore = create<ExploreState>((set, get) => ({
|
|||||||
const key = `${space.id}:${origin}`;
|
const key = `${space.id}:${origin}`;
|
||||||
if (seen.has(key)) continue;
|
if (seen.has(key)) continue;
|
||||||
seen.add(key);
|
seen.add(key);
|
||||||
|
if (origin && space.icon) {
|
||||||
|
space.icon = resolveAssetUrl(space.icon, origin) ?? space.icon;
|
||||||
|
}
|
||||||
allSpaces.push({ ...space, _instanceOrigin: origin, joined: space.joined ?? false });
|
allSpaces.push({ ...space, _instanceOrigin: origin, joined: space.joined ?? false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { create } from 'zustand';
|
|||||||
import type { Friend, FriendRequest, User } from '@backspace/shared';
|
import type { Friend, FriendRequest, User } from '@backspace/shared';
|
||||||
import { api } from '../api/client';
|
import { api } from '../api/client';
|
||||||
import { useInstanceStore } from './instanceStore';
|
import { useInstanceStore } from './instanceStore';
|
||||||
|
import { normalizeUserAssets } from '../utils/assetUrls';
|
||||||
|
|
||||||
// ─── Tagged types (origin tracking for federation) ───────────────────────────
|
// ─── Tagged types (origin tracking for federation) ───────────────────────────
|
||||||
|
|
||||||
@@ -66,6 +67,7 @@ export const useSocialStore = create<SocialState>((set, get) => ({
|
|||||||
const key = `${friend.id}:${origin}`;
|
const key = `${friend.id}:${origin}`;
|
||||||
if (seen.has(key)) continue;
|
if (seen.has(key)) continue;
|
||||||
seen.add(key);
|
seen.add(key);
|
||||||
|
if (origin) normalizeUserAssets(friend, origin);
|
||||||
allFriends.push({ ...friend, _instanceOrigin: origin });
|
allFriends.push({ ...friend, _instanceOrigin: origin });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,6 +101,7 @@ export const useSocialStore = create<SocialState>((set, get) => ({
|
|||||||
const key = `${request.id}:${origin}`;
|
const key = `${request.id}:${origin}`;
|
||||||
if (seen.has(key)) continue;
|
if (seen.has(key)) continue;
|
||||||
seen.add(key);
|
seen.add(key);
|
||||||
|
if (origin && request.user) normalizeUserAssets(request.user, origin);
|
||||||
allRequests.push({ ...request, _instanceOrigin: origin });
|
allRequests.push({ ...request, _instanceOrigin: origin });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,22 +220,30 @@ export const useSocialStore = create<SocialState>((set, get) => ({
|
|||||||
const instances = useInstanceStore.getState().instances;
|
const instances = useInstanceStore.getState().instances;
|
||||||
const connectedInstances = instances.filter(i => i.status === 'connected');
|
const connectedInstances = instances.filter(i => i.status === 'connected');
|
||||||
|
|
||||||
const results = await Promise.allSettled([
|
// Pair each promise with its origin for asset normalization
|
||||||
api.social.search(query),
|
const searches: { promise: Promise<User[]>; origin: string }[] = [
|
||||||
...connectedInstances.map(inst => inst.api.social.search(query)),
|
{ promise: api.social.search(query), origin: '' },
|
||||||
]);
|
...connectedInstances.map(inst => ({
|
||||||
|
promise: inst.api.social.search(query),
|
||||||
|
origin: inst.origin,
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
|
||||||
|
const results = await Promise.allSettled(searches.map(s => s.promise));
|
||||||
|
|
||||||
const allUsers: User[] = [];
|
const allUsers: User[] = [];
|
||||||
const seen = new Set<string>();
|
const seen = new Set<string>();
|
||||||
|
|
||||||
for (const result of results) {
|
results.forEach((result, i) => {
|
||||||
if (result.status !== 'fulfilled') continue;
|
if (result.status !== 'fulfilled') return;
|
||||||
|
const origin = searches[i]!.origin;
|
||||||
for (const user of result.value) {
|
for (const user of result.value) {
|
||||||
if (seen.has(user.id)) continue;
|
if (seen.has(user.id)) continue;
|
||||||
seen.add(user.id);
|
seen.add(user.id);
|
||||||
|
if (origin) normalizeUserAssets(user, origin);
|
||||||
allUsers.push(user);
|
allUsers.push(user);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
return allUsers;
|
return allUsers;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
Reference in New Issue
Block a user