feat: Update how messages load and sync, allow plugins to import messages
All checks were successful
Queue Release Build / prepare (push) Successful in 23s
Deploy Web Apps / deploy (push) Successful in 7m36s
Queue Release Build / build-windows (push) Successful in 28m3s
Queue Release Build / build-linux (push) Successful in 44m14s
Queue Release Build / finalize (push) Successful in 39s
All checks were successful
Queue Release Build / prepare (push) Successful in 23s
Deploy Web Apps / deploy (push) Successful in 7m36s
Queue Release Build / build-windows (push) Successful in 28m3s
Queue Release Build / build-linux (push) Successful in 44m14s
Queue Release Build / finalize (push) Successful in 39s
This commit is contained in:
@@ -17,6 +17,7 @@ import type {
|
||||
User
|
||||
} from '../../../../shared-kernel';
|
||||
import { MessagesActions } from '../../../../store/messages/messages.actions';
|
||||
import { CHUNK_SIZE, chunkArray } from '../../../../store/messages/messages.helpers';
|
||||
import { selectCurrentRoomMessages } from '../../../../store/messages/messages.selectors';
|
||||
import { RoomsActions } from '../../../../store/rooms/rooms.actions';
|
||||
import {
|
||||
@@ -27,6 +28,8 @@ import {
|
||||
} from '../../../../store/rooms/rooms.selectors';
|
||||
import { UsersActions } from '../../../../store/users/users.actions';
|
||||
import { selectAllUsers, selectCurrentUser } from '../../../../store/users/users.selectors';
|
||||
import { defaultChannels } from '../../../../store/rooms/room-channels.defaults';
|
||||
import { isChannelNameTaken, normalizeChannelName } from '../../../../store/rooms/room-channels.rules';
|
||||
import type {
|
||||
PluginApiAvatarUpdate,
|
||||
PluginApiActionContext,
|
||||
@@ -77,11 +80,11 @@ export class PluginClientApiService {
|
||||
channels: {
|
||||
addAudioChannel: (request) => {
|
||||
requireCapability('channels.manage');
|
||||
this.store.dispatch(RoomsActions.addChannel({ channel: createChannel(request, 'voice') }));
|
||||
this.addPluginManagedChannel(pluginId, createChannel(request, 'voice'));
|
||||
},
|
||||
addTextChannel: (request) => {
|
||||
requireCapability('channels.manage');
|
||||
this.store.dispatch(RoomsActions.addChannel({ channel: createChannel(request, 'text') }));
|
||||
this.addPluginManagedChannel(pluginId, createChannel(request, 'text'));
|
||||
},
|
||||
addVideoChannel: (request) => {
|
||||
requireCapability('channels.manage');
|
||||
@@ -743,9 +746,86 @@ export class PluginClientApiService {
|
||||
}
|
||||
|
||||
this.store.dispatch(MessagesActions.syncMessages({ messages: normalizedMessages }));
|
||||
|
||||
// Broadcast imported history to peers in CHUNK_SIZE batches so they don't
|
||||
// depend on the inventory-limited background sync to discover bulk imports.
|
||||
for (const chunk of chunkArray(normalizedMessages, CHUNK_SIZE)) {
|
||||
this.voice.broadcastMessage({
|
||||
type: 'chat-sync-batch',
|
||||
roomId,
|
||||
messages: chunk
|
||||
} as unknown as ChatEvent);
|
||||
}
|
||||
|
||||
this.logger.info(pluginId, 'Historical messages imported', { count: normalizedMessages.length });
|
||||
}
|
||||
|
||||
private addPluginManagedChannel(pluginId: string, channel: Channel): void {
|
||||
const room = this.currentRoom();
|
||||
const currentUser = this.currentUser();
|
||||
|
||||
if (!room || !currentUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isOwner = room.hostId === currentUser.id || room.hostId === currentUser.oderId;
|
||||
const isServerAdmin = currentUser.role === 'admin' || currentUser.role === 'host';
|
||||
const canManageChannels = resolveRoomPermission(room, currentUser, 'manageChannels');
|
||||
|
||||
if (!isOwner && !isServerAdmin && !canManageChannels) {
|
||||
this.logger.warn(pluginId, 'Plugin channel creation denied by room permissions', {
|
||||
channelId: channel.id,
|
||||
roomId: room.id
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const existingChannels = room.channels ?? defaultChannels();
|
||||
const normalizedName = normalizeChannelName(channel.name);
|
||||
const channelExists = existingChannels.some((entry) => entry.id === channel.id) ||
|
||||
isChannelNameTaken(existingChannels, normalizedName, channel.type);
|
||||
|
||||
if (!normalizedName || channelExists) {
|
||||
return;
|
||||
}
|
||||
|
||||
const channels = [
|
||||
...existingChannels,
|
||||
{ ...channel,
|
||||
name: normalizedName }
|
||||
];
|
||||
|
||||
this.store.dispatch(RoomsActions.updateRoom({ roomId: room.id,
|
||||
changes: { channels } }));
|
||||
|
||||
void this.db.updateRoom(room.id, { channels }).catch((error: unknown) => {
|
||||
this.logger.warn(pluginId, 'Failed to persist plugin-created channel', error);
|
||||
});
|
||||
|
||||
this.realtime.broadcastMessage({
|
||||
type: 'channels-update',
|
||||
roomId: room.id,
|
||||
channels
|
||||
});
|
||||
|
||||
this.serverDirectory.updateServer(room.id, {
|
||||
actingRole: isOwner ? 'host' : undefined,
|
||||
channels,
|
||||
currentOwnerId: currentUser.id
|
||||
}, {
|
||||
sourceId: room.sourceId,
|
||||
sourceUrl: room.sourceUrl
|
||||
}).subscribe({
|
||||
error: () => {}
|
||||
});
|
||||
|
||||
this.logger.info(pluginId, 'Plugin channel created', {
|
||||
channelId: channel.id,
|
||||
roomId: room.id
|
||||
});
|
||||
}
|
||||
|
||||
private persistPluginMessageUpdate(pluginId: string, messageId: string, updates: Partial<Message>): void {
|
||||
void this.db.updateMessage(messageId, updates).catch((error: unknown) => {
|
||||
this.logger.warn(pluginId, 'Failed to persist plugin message update', error);
|
||||
|
||||
Reference in New Issue
Block a user