fix: DM avatar color and reactions in federation + explore/server discovery

- Fix DM welcome header avatar using home identity for correct gradient color
- Register DM channel IDs in channelOriginMap so federated DM operations
  (reactions, messages, typing) route to the correct instance
- Pass origin when creating DM channels from friends list and WS events
- Add server discovery/explore page with public server listings
- Add server visibility and description fields
This commit is contained in:
Jannis Braun
2026-03-04 18:02:43 +01:00
parent 72d1fab930
commit 6e44a4ef2f
29 changed files with 1614 additions and 108 deletions
+39
View File
@@ -28,6 +28,8 @@ import type {
InstanceStreamingLimits,
InstanceInfoResponse,
VerifyPasswordResponse,
ExploreServer,
JoinRequest,
} from '@backspace/shared';
export class BackspaceApiClient {
@@ -115,6 +117,15 @@ export class BackspaceApiClient {
info: () => Promise<InstanceInfoResponse>;
};
readonly explore: {
list: (q?: string, limit?: number, offset?: number) => Promise<{ servers: ExploreServer[]; total: number; discoveryEnabled: boolean }>;
publicJoin: (serverId: string) => Promise<ServerWithChannelsAndMembers>;
requestJoin: (serverId: string, message?: string) => Promise<JoinRequest>;
getJoinRequests: (serverId: string, status?: string) => Promise<{ requests: JoinRequest[] }>;
decideJoinRequest: (serverId: string, requestId: string, action: 'accept' | 'decline') => Promise<JoinRequest>;
myJoinRequests: (status?: string) => Promise<{ requests: JoinRequest[] }>;
};
constructor(baseUrl: string, getToken: () => string | null) {
async function request<T>(
method: string,
@@ -287,6 +298,34 @@ export class BackspaceApiClient {
this.instance = {
info: () => request<InstanceInfoResponse>('GET', '/instance/info', undefined, false),
};
this.explore = {
list: (q?: string, limit = 50, offset = 0) => {
const params = new URLSearchParams();
if (q) params.set('q', q);
params.set('limit', String(limit));
params.set('offset', String(offset));
return request<{ servers: ExploreServer[]; total: number; discoveryEnabled: boolean }>(
'GET', `/servers/explore?${params}`
);
},
publicJoin: (serverId: string) =>
request<ServerWithChannelsAndMembers>('POST', `/servers/${serverId}/public-join`),
requestJoin: (serverId: string, message?: string) =>
request<JoinRequest>('POST', `/servers/${serverId}/request-join`, message ? { message } : {}),
getJoinRequests: (serverId: string, status?: string) => {
const params = new URLSearchParams();
if (status) params.set('status', status);
return request<{ requests: JoinRequest[] }>('GET', `/servers/${serverId}/join-requests?${params}`);
},
decideJoinRequest: (serverId: string, requestId: string, action: 'accept' | 'decline') =>
request<JoinRequest>('PATCH', `/servers/${serverId}/join-requests/${requestId}`, { action }),
myJoinRequests: (status?: string) => {
const params = new URLSearchParams();
if (status) params.set('status', status);
return request<{ requests: JoinRequest[] }>('GET', `/users/@me/join-requests?${params}`);
},
};
}
}