feat: server image

This commit is contained in:
2026-04-29 18:54:08 +02:00
parent 3d81c34159
commit e1ac1d1bc0
27 changed files with 1340 additions and 615 deletions

View File

@@ -4,11 +4,7 @@ import { normalizeRoomAccessControl } from '../../domains/access-control';
import { type ServerInfo } from '../../domains/server-directory';
import { RoomsActions } from './rooms.actions';
import { defaultChannels } from './room-channels.defaults';
import {
isChannelNameTaken,
normalizeChannelName,
normalizeRoomChannels
} from './room-channels.rules';
import { isChannelNameTaken, normalizeChannelName, normalizeRoomChannels } from './room-channels.rules';
import { pruneRoomMembers } from './room-members.helpers';
/** Deduplicate rooms by id, keeping the last occurrence */
@@ -35,9 +31,7 @@ function enrichRoom(room: Room): Room {
function resolveActiveTextChannelId(channels: Room['channels'], currentActiveChannelId: string): string {
const textChannels = (channels ?? []).filter((channel) => channel.type === 'text');
return textChannels.some((channel) => channel.id === currentActiveChannelId)
? currentActiveChannelId
: (textChannels[0]?.id ?? 'general');
return textChannels.some((channel) => channel.id === currentActiveChannelId) ? currentActiveChannelId : (textChannels[0]?.id ?? 'general');
}
function getDefaultTextChannelId(room: Room): string {
@@ -47,7 +41,7 @@ function getDefaultTextChannelId(room: Room): string {
/** Upsert a room into a saved-rooms list (add or replace by id) */
function upsertRoom(savedRooms: Room[], room: Room): Room[] {
const normalizedRoom = enrichRoom(room);
const idx = savedRooms.findIndex(existingRoom => existingRoom.id === room.id);
const idx = savedRooms.findIndex((existingRoom) => existingRoom.id === room.id);
if (idx >= 0) {
const updated = [...savedRooms];
@@ -250,8 +244,7 @@ export const roomsReducer = createReducer(
})),
on(RoomsActions.updateRoomSettingsSuccess, (state, { roomId, settings }) => {
const baseRoom = state.savedRooms.find((savedRoom) => savedRoom.id === roomId)
|| (state.currentRoom?.id === roomId ? state.currentRoom : null);
const baseRoom = state.savedRooms.find((savedRoom) => savedRoom.id === roomId) || (state.currentRoom?.id === roomId ? state.currentRoom : null);
if (!baseRoom) {
return {
@@ -270,9 +263,9 @@ export const roomsReducer = createReducer(
hasPassword:
typeof settings.hasPassword === 'boolean'
? settings.hasPassword
: (typeof settings.password === 'string'
: typeof settings.password === 'string'
? settings.password.trim().length > 0
: baseRoom.hasPassword),
: baseRoom.hasPassword,
maxUsers: settings.maxUsers
});
@@ -330,33 +323,28 @@ export const roomsReducer = createReducer(
// Update room
on(RoomsActions.updateRoom, (state, { roomId, changes }) => {
const baseRoom = state.savedRooms.find((savedRoom) => savedRoom.id === roomId)
|| (state.currentRoom?.id === roomId ? state.currentRoom : null);
const baseRoom = state.savedRooms.find((savedRoom) => savedRoom.id === roomId) || (state.currentRoom?.id === roomId ? state.currentRoom : null);
if (!baseRoom)
return state;
if (!baseRoom) return state;
const updatedRoom = enrichRoom({ ...baseRoom,
...changes });
const updatedRoom = enrichRoom({ ...baseRoom, ...changes });
return {
...state,
currentRoom: state.currentRoom?.id === roomId ? updatedRoom : state.currentRoom,
savedRooms: upsertRoom(state.savedRooms, updatedRoom),
activeChannelId: state.currentRoom?.id === roomId
? resolveActiveTextChannelId(updatedRoom.channels, state.activeChannelId)
: state.activeChannelId
activeChannelId:
state.currentRoom?.id === roomId ? resolveActiveTextChannelId(updatedRoom.channels, state.activeChannelId) : state.activeChannelId
};
}),
// Update server icon success
on(RoomsActions.updateServerIconSuccess, (state, { roomId, icon, iconUpdatedAt }) => {
if (state.currentRoom?.id !== roomId)
return state;
const baseRoom = state.savedRooms.find((savedRoom) => savedRoom.id === roomId) || (state.currentRoom?.id === roomId ? state.currentRoom : null);
const updatedRoom = enrichRoom({ ...state.currentRoom,
icon,
iconUpdatedAt });
if (!baseRoom) return state;
const updatedRoom = enrichRoom({ ...baseRoom, icon, iconUpdatedAt });
return {
...state,
@@ -365,13 +353,18 @@ export const roomsReducer = createReducer(
};
}),
on(RoomsActions.receiveSearchServerIcon, (state, { roomId, icon, iconUpdatedAt }) => ({
...state,
searchResults: state.searchResults.map((server) =>
server.id === roomId && (!server.icon || (server.iconUpdatedAt ?? 0) < iconUpdatedAt) ? { ...server, icon, iconUpdatedAt } : server
)
})),
// Receive room update
on(RoomsActions.receiveRoomUpdate, (state, { room }) => {
if (!state.currentRoom)
return state;
if (!state.currentRoom) return state;
const updatedRoom = enrichRoom({ ...state.currentRoom,
...room });
const updatedRoom = enrichRoom({ ...state.currentRoom, ...room });
return {
...state,
@@ -410,27 +403,17 @@ export const roomsReducer = createReducer(
})),
on(RoomsActions.addChannel, (state, { channel }) => {
if (!state.currentRoom)
return state;
if (!state.currentRoom) return state;
const existing = state.currentRoom.channels || defaultChannels();
const normalizedName = normalizeChannelName(channel.name);
if (
!normalizedName
|| existing.some((entry) => entry.id === channel.id)
|| isChannelNameTaken(existing, normalizedName, channel.type)
) {
if (!normalizedName || existing.some((entry) => entry.id === channel.id) || isChannelNameTaken(existing, normalizedName, channel.type)) {
return state;
}
const updatedChannels = [
...existing,
{ ...channel,
name: normalizedName }
];
const updatedRoom = { ...state.currentRoom,
channels: updatedChannels };
const updatedChannels = [...existing, { ...channel, name: normalizedName }];
const updatedRoom = { ...state.currentRoom, channels: updatedChannels };
return {
...state,
@@ -441,13 +424,11 @@ export const roomsReducer = createReducer(
}),
on(RoomsActions.removeChannel, (state, { channelId }) => {
if (!state.currentRoom)
return state;
if (!state.currentRoom) return state;
const existing = state.currentRoom.channels || defaultChannels();
const updatedChannels = existing.filter(channel => channel.id !== channelId);
const updatedRoom = { ...state.currentRoom,
channels: updatedChannels };
const updatedChannels = existing.filter((channel) => channel.id !== channelId);
const updatedRoom = { ...state.currentRoom, channels: updatedChannels };
return {
...state,
@@ -458,8 +439,7 @@ export const roomsReducer = createReducer(
}),
on(RoomsActions.renameChannel, (state, { channelId, name }) => {
if (!state.currentRoom)
return state;
if (!state.currentRoom) return state;
const existing = state.currentRoom.channels || defaultChannels();
const normalizedName = normalizeChannelName(name);
@@ -469,10 +449,8 @@ export const roomsReducer = createReducer(
return state;
}
const updatedChannels = existing.map(channel => channel.id === channelId ? { ...channel,
name: normalizedName } : channel);
const updatedRoom = { ...state.currentRoom,
channels: updatedChannels };
const updatedChannels = existing.map((channel) => (channel.id === channelId ? { ...channel, name: normalizedName } : channel));
const updatedRoom = { ...state.currentRoom, channels: updatedChannels };
return {
...state,