wip: optimizations
This commit is contained in:
@@ -25,12 +25,14 @@ import {
|
||||
mergeMap,
|
||||
catchError,
|
||||
withLatestFrom,
|
||||
switchMap
|
||||
switchMap,
|
||||
filter
|
||||
} from 'rxjs/operators';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { MessagesActions } from './messages.actions';
|
||||
import { selectCurrentUser } from '../users/users.selectors';
|
||||
import { selectCurrentRoom, selectSavedRooms } from '../rooms/rooms.selectors';
|
||||
import { RoomsActions } from '../rooms/rooms.actions';
|
||||
import { selectMessagesEntities } from './messages.selectors';
|
||||
import { RealtimeSessionFacade } from '../../core/realtime';
|
||||
import { DatabaseService } from '../../infrastructure/persistence';
|
||||
@@ -40,6 +42,7 @@ import { AttachmentFacade } from '../../domains/attachment';
|
||||
import { hasDedicatedChatEmbed } from '../../domains/chat/domain/rules/link-embed.rules';
|
||||
import { LinkMetadataService } from '../../domains/chat/application/services/link-metadata.service';
|
||||
import { TimeSyncService } from '../../core/services/time-sync.service';
|
||||
import { PlatformService } from '../../core/platform';
|
||||
import {
|
||||
DELETED_MESSAGE_CONTENT,
|
||||
Message,
|
||||
@@ -52,6 +55,8 @@ import { resolveRoomPermission } from '../../domains/access-control';
|
||||
import { dispatchIncomingMessage, IncomingMessageContext } from './messages-incoming.handlers';
|
||||
|
||||
const INITIAL_ROOM_MESSAGE_LIMIT = 30;
|
||||
/** Cap on simultaneous browser-cache prefetches for apps with many saved rooms. */
|
||||
const PREFETCH_CONCURRENCY = 3;
|
||||
|
||||
@Injectable()
|
||||
export class MessagesEffects {
|
||||
@@ -63,14 +68,26 @@ export class MessagesEffects {
|
||||
private readonly webrtc = inject(RealtimeSessionFacade);
|
||||
private readonly timeSync = inject(TimeSyncService);
|
||||
private readonly linkMetadata = inject(LinkMetadataService);
|
||||
private readonly platform = inject(PlatformService);
|
||||
|
||||
/** Loads messages for a room from the local database, hydrating reactions. */
|
||||
loadMessages$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(MessagesActions.loadMessages),
|
||||
withLatestFrom(this.store.select(selectCurrentRoom)),
|
||||
switchMap(([{ roomId }, currentRoom]) =>
|
||||
from(this.loadInitialMessages(roomId, currentRoom)).pipe(
|
||||
withLatestFrom(
|
||||
this.store.select(selectCurrentRoom),
|
||||
this.store.select(selectSavedRooms)
|
||||
),
|
||||
switchMap(([
|
||||
{ roomId },
|
||||
currentRoom,
|
||||
savedRooms
|
||||
]) => {
|
||||
const targetRoom = currentRoom?.id === roomId
|
||||
? currentRoom
|
||||
: savedRooms.find((room) => room.id === roomId) ?? null;
|
||||
|
||||
return from(this.loadInitialMessages(roomId, targetRoom)).pipe(
|
||||
mergeMap(async (messages) => {
|
||||
const hydrated = await hydrateMessages(messages, this.db);
|
||||
|
||||
@@ -78,18 +95,73 @@ export class MessagesEffects {
|
||||
this.attachments.rememberMessageRoom(message.id, message.roomId);
|
||||
}
|
||||
|
||||
void this.attachments.requestAutoDownloadsForRoom(roomId);
|
||||
|
||||
return MessagesActions.loadMessagesSuccess({ messages: hydrated });
|
||||
}),
|
||||
catchError((error) =>
|
||||
of(MessagesActions.loadMessagesFailure({ error: error.message }))
|
||||
)
|
||||
);
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Background-prefetch initial messages for every saved room after the
|
||||
* rooms list loads in browser. Electron avoids this path because startup
|
||||
* IPC prefetch competes with foreground room switches. Results are merged
|
||||
* into the messages slice via `upsertMany`, leaving the active-room loading
|
||||
* flag untouched.
|
||||
*/
|
||||
prefetchSavedRoomsOnLoad$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(RoomsActions.loadRoomsSuccess),
|
||||
filter(() => this.platform.isBrowser),
|
||||
mergeMap(({ rooms }) =>
|
||||
from(rooms).pipe(
|
||||
mergeMap(
|
||||
(room) => of(MessagesActions.prefetchRoomMessages({ roomId: room.id })),
|
||||
PREFETCH_CONCURRENCY
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
prefetchRoomMessages$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(MessagesActions.prefetchRoomMessages),
|
||||
withLatestFrom(this.store.select(selectSavedRooms)),
|
||||
mergeMap(
|
||||
([{ roomId }, savedRooms]) =>
|
||||
from(this.fetchRoomMessagesForPrefetch(roomId, savedRooms.find((room) => room.id === roomId) ?? null)),
|
||||
PREFETCH_CONCURRENCY
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
private async fetchRoomMessagesForPrefetch(roomId: string, targetRoom: Room | null) {
|
||||
try {
|
||||
const messages = await this.loadInitialMessages(roomId, targetRoom);
|
||||
const hydrated = await hydrateMessages(messages, this.db);
|
||||
|
||||
for (const message of hydrated) {
|
||||
this.attachments.rememberMessageRoom(message.id, message.roomId);
|
||||
}
|
||||
|
||||
return MessagesActions.prefetchRoomMessagesSuccess({ messages: hydrated });
|
||||
} catch (error) {
|
||||
reportDebuggingError(
|
||||
this.debugging,
|
||||
'MessagesEffects.prefetchRoomMessages',
|
||||
'Failed to prefetch room messages',
|
||||
{ roomId },
|
||||
error
|
||||
);
|
||||
|
||||
return MessagesActions.prefetchRoomMessagesSuccess({ messages: [] });
|
||||
}
|
||||
}
|
||||
|
||||
/** Paginates older messages from the local DB for scroll-up history loading. */
|
||||
loadOlderMessages$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
@@ -119,9 +191,9 @@ export class MessagesEffects {
|
||||
)
|
||||
);
|
||||
|
||||
private async loadInitialMessages(roomId: string, currentRoom: Room | null): Promise<Message[]> {
|
||||
const textChannels = currentRoom?.id === roomId
|
||||
? (currentRoom.channels ?? []).filter((channel) => channel.type === 'text')
|
||||
private async loadInitialMessages(roomId: string, targetRoom: Room | null): Promise<Message[]> {
|
||||
const textChannels = targetRoom?.id === roomId
|
||||
? (targetRoom.channels ?? []).filter((channel) => channel.type === 'text')
|
||||
: [];
|
||||
|
||||
if (textChannels.length <= 1) {
|
||||
|
||||
Reference in New Issue
Block a user