feat: Theme studio v2

This commit is contained in:
2026-04-27 03:02:13 +02:00
parent 11c2588e45
commit 1b91eacb5b
52 changed files with 2792 additions and 844 deletions

View File

@@ -36,6 +36,7 @@ import { RoomsActions } from '../../../store/rooms/rooms.actions';
import { DatabaseService } from '../../../infrastructure/persistence';
import { NotificationsFacade } from '../../../domains/notifications';
import { type ServerInfo, ServerDirectoryFacade } from '../../../domains/server-directory';
import { ThemeNodeDirective } from '../../../domains/theme';
import { hasRoomBanForUser } from '../../../domains/access-control';
import {
ConfirmDialogComponent,
@@ -54,6 +55,7 @@ import {
ContextMenuComponent,
LeaveServerDialogComponent,
NgOptimizedImage,
ThemeNodeDirective,
UserBarComponent
],
viewProviders: [provideIcons({ lucidePlus })],
@@ -221,8 +223,7 @@ export class ServersRailComponent {
return;
this.joinPasswordError.set(null);
this.savedRoomJoinRequests.next({ room,
password: this.joinPassword() });
this.savedRoomJoinRequests.next({ room, password: this.joinPassword() });
}
isRoomMarkedBanned(room: Room): boolean {
@@ -264,10 +265,12 @@ export class ServersRailComponent {
const isCurrentRoom = this.currentRoom()?.id === ctx.id;
this.store.dispatch(RoomsActions.forgetRoom({
roomId: ctx.id,
nextOwnerKey: result.nextOwnerKey
}));
this.store.dispatch(
RoomsActions.forgetRoom({
roomId: ctx.id,
nextOwnerKey: result.nextOwnerKey
})
);
if (isCurrentRoom) {
this.router.navigate(['/search']);
@@ -354,8 +357,7 @@ export class ServersRailComponent {
this.prepareVoiceContext(room);
this.closePasswordDialog();
this.store.dispatch(RoomsActions.setSignalServerReconnecting({ isReconnecting: false }));
this.store.dispatch(RoomsActions.viewServer({ room,
skipBanCheck: true }));
this.store.dispatch(RoomsActions.viewServer({ room, skipBanCheck: true }));
}
private requestJoinInBackground(room: Room, password?: string) {
@@ -377,13 +379,17 @@ export class ServersRailComponent {
return EMPTY;
}
return this.serverDirectory.requestJoin({
roomId: room.id,
userId: currentUserId,
userPublicKey: currentUser?.oderId || currentUserId,
displayName: currentUser?.displayName || 'Anonymous',
password: password?.trim() || undefined
}, joinTarget.selector)
return this.serverDirectory
.requestJoin(
{
roomId: room.id,
userId: currentUserId,
userPublicKey: currentUser?.oderId || currentUserId,
displayName: currentUser?.displayName || 'Anonymous',
password: password?.trim() || undefined
},
joinTarget.selector
)
.pipe(
tap((response) => {
this.closePasswordDialog();
@@ -447,22 +453,22 @@ export class ServersRailComponent {
const errorCode = serverError?.error?.errorCode;
const status = serverError?.status;
return errorCode === 'SERVER_NOT_FOUND'
|| status === 0
|| status === 404
|| (typeof status === 'number' && status >= 500);
return errorCode === 'SERVER_NOT_FOUND' || status === 0 || status === 404 || (typeof status === 'number' && status >= 500);
}
private toRoomRefreshChanges(room: Room, server: ServerInfo, signalingUrl?: string): Partial<Room> {
const resolvedSource = this.serverDirectory.normaliseRoomSignalSource({
sourceId: server.sourceId ?? room.sourceId,
sourceName: server.sourceName ?? room.sourceName,
sourceUrl: server.sourceUrl ?? room.sourceUrl,
signalingUrl,
fallbackName: server.sourceName ?? room.sourceName ?? room.name
}, {
ensureEndpoint: true
});
const resolvedSource = this.serverDirectory.normaliseRoomSignalSource(
{
sourceId: server.sourceId ?? room.sourceId,
sourceName: server.sourceName ?? room.sourceName,
sourceUrl: server.sourceUrl ?? room.sourceUrl,
signalingUrl,
fallbackName: server.sourceName ?? room.sourceName ?? room.name
},
{
ensureEndpoint: true
}
);
return {
name: server.name,
@@ -472,15 +478,10 @@ export class ServersRailComponent {
userCount: server.userCount,
maxUsers: server.maxUsers,
hasPassword:
typeof server.hasPassword === 'boolean'
? server.hasPassword
: (typeof room.hasPassword === 'boolean' ? room.hasPassword : !!room.password),
typeof server.hasPassword === 'boolean' ? server.hasPassword : typeof room.hasPassword === 'boolean' ? room.hasPassword : !!room.password,
isPrivate: server.isPrivate,
createdAt: server.createdAt,
channels:
Array.isArray(server.channels) && server.channels.length > 0
? server.channels
: room.channels,
channels: Array.isArray(server.channels) && server.channels.length > 0 ? server.channels : room.channels,
...resolvedSource
};
}
@@ -489,26 +490,33 @@ export class ServersRailComponent {
room: Room;
selector: ReturnType<ServerDirectoryFacade['buildRoomSignalSelector']>;
}> {
let resolvedRoom = this.applyResolvedRoomSource(room, this.serverDirectory.normaliseRoomSignalSource({
sourceId: room.sourceId,
sourceName: room.sourceName,
sourceUrl: room.sourceUrl,
fallbackName: room.sourceName ?? room.name
}, {
ensureEndpoint: !!room.sourceUrl
}));
let selector = this.serverDirectory.buildRoomSignalSelector({
sourceId: resolvedRoom.sourceId,
sourceName: resolvedRoom.sourceName,
sourceUrl: resolvedRoom.sourceUrl,
fallbackName: resolvedRoom.sourceName ?? resolvedRoom.name
}, {
ensureEndpoint: !!resolvedRoom.sourceUrl
});
let resolvedRoom = this.applyResolvedRoomSource(
room,
this.serverDirectory.normaliseRoomSignalSource(
{
sourceId: room.sourceId,
sourceName: room.sourceName,
sourceUrl: room.sourceUrl,
fallbackName: room.sourceName ?? room.name
},
{
ensureEndpoint: !!room.sourceUrl
}
)
);
let selector = this.serverDirectory.buildRoomSignalSelector(
{
sourceId: resolvedRoom.sourceId,
sourceName: resolvedRoom.sourceName,
sourceUrl: resolvedRoom.sourceUrl,
fallbackName: resolvedRoom.sourceName ?? resolvedRoom.name
},
{
ensureEndpoint: !!resolvedRoom.sourceUrl
}
);
const authoritativeServer = selector
? await firstValueFrom(this.serverDirectory.getServer(room.id, selector))
: null;
const authoritativeServer = selector ? await firstValueFrom(this.serverDirectory.getServer(room.id, selector)) : null;
if (!authoritativeServer) {
return {
@@ -517,24 +525,30 @@ export class ServersRailComponent {
};
}
const authoritativeSource = this.serverDirectory.normaliseRoomSignalSource({
sourceId: authoritativeServer.sourceId ?? resolvedRoom.sourceId,
sourceName: authoritativeServer.sourceName ?? resolvedRoom.sourceName,
sourceUrl: authoritativeServer.sourceUrl ?? resolvedRoom.sourceUrl,
fallbackName: authoritativeServer.sourceName ?? resolvedRoom.sourceName ?? resolvedRoom.name
}, {
ensureEndpoint: !!(authoritativeServer.sourceUrl ?? resolvedRoom.sourceUrl)
});
const authoritativeSource = this.serverDirectory.normaliseRoomSignalSource(
{
sourceId: authoritativeServer.sourceId ?? resolvedRoom.sourceId,
sourceName: authoritativeServer.sourceName ?? resolvedRoom.sourceName,
sourceUrl: authoritativeServer.sourceUrl ?? resolvedRoom.sourceUrl,
fallbackName: authoritativeServer.sourceName ?? resolvedRoom.sourceName ?? resolvedRoom.name
},
{
ensureEndpoint: !!(authoritativeServer.sourceUrl ?? resolvedRoom.sourceUrl)
}
);
resolvedRoom = this.applyResolvedRoomSource(resolvedRoom, authoritativeSource);
selector = this.serverDirectory.buildRoomSignalSelector({
sourceId: resolvedRoom.sourceId,
sourceName: resolvedRoom.sourceName,
sourceUrl: resolvedRoom.sourceUrl,
fallbackName: resolvedRoom.sourceName ?? resolvedRoom.name
}, {
ensureEndpoint: !!resolvedRoom.sourceUrl
});
selector = this.serverDirectory.buildRoomSignalSelector(
{
sourceId: resolvedRoom.sourceId,
sourceName: resolvedRoom.sourceName,
sourceUrl: resolvedRoom.sourceUrl,
fallbackName: resolvedRoom.sourceName ?? resolvedRoom.name
},
{
ensureEndpoint: !!resolvedRoom.sourceUrl
}
);
return {
room: resolvedRoom,
@@ -550,22 +564,20 @@ export class ServersRailComponent {
sourceUrl: source.sourceUrl
};
if (
room.sourceId === nextRoom.sourceId
&& room.sourceName === nextRoom.sourceName
&& room.sourceUrl === nextRoom.sourceUrl
) {
if (room.sourceId === nextRoom.sourceId && room.sourceName === nextRoom.sourceName && room.sourceUrl === nextRoom.sourceUrl) {
return room;
}
this.store.dispatch(RoomsActions.updateRoom({
roomId: room.id,
changes: {
sourceId: nextRoom.sourceId,
sourceName: nextRoom.sourceName,
sourceUrl: nextRoom.sourceUrl
}
}));
this.store.dispatch(
RoomsActions.updateRoom({
roomId: room.id,
changes: {
sourceId: nextRoom.sourceId,
sourceName: nextRoom.sourceName,
sourceUrl: nextRoom.sourceUrl
}
})
);
return nextRoom;
}