fix: restore build and stabilize E2E cross-signal behavior
Revert the automated member-ordering pass that broke Angular field init (TS2729) and disable that rule until a safe reorder strategy exists. Fix modal/confirm dialog i18n defaults via template fallbacks, search all active endpoints (including offline), register foreign rooms with actor owner IDs, sync profile display names from avatar summaries, and guard dm-chat when a private call converts to a group conversation. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
* The giant `incomingMessages$` switch-case has been replaced by a
|
||||
* handler registry in `messages-incoming.handlers.ts`.
|
||||
*/
|
||||
/* eslint-disable @typescript-eslint/member-ordering */
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import {
|
||||
Actions,
|
||||
@@ -63,6 +64,18 @@ const PREFETCH_CONCURRENCY = 3;
|
||||
|
||||
@Injectable()
|
||||
export class MessagesEffects {
|
||||
private readonly actions$ = inject(Actions);
|
||||
private readonly store = inject(Store);
|
||||
private readonly db = inject(DatabaseService);
|
||||
private readonly debugging = inject(DebuggingService);
|
||||
private readonly attachments = inject(AttachmentFacade);
|
||||
private readonly customEmoji = inject(CustomEmojiService);
|
||||
private readonly webrtc = inject(RealtimeSessionFacade);
|
||||
private readonly timeSync = inject(TimeSyncService);
|
||||
private readonly linkMetadata = inject(LinkMetadataService);
|
||||
private readonly platform = inject(PlatformService);
|
||||
private readonly i18n = inject(AppI18nService);
|
||||
private readonly messageRevisions = inject(MessageRevisionService);
|
||||
|
||||
/** Loads messages for a room from the local database, hydrating reactions. */
|
||||
loadMessages$ = createEffect(() =>
|
||||
@@ -133,6 +146,29 @@ export class MessagesEffects {
|
||||
)
|
||||
);
|
||||
|
||||
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(
|
||||
@@ -162,6 +198,29 @@ export class MessagesEffects {
|
||||
)
|
||||
);
|
||||
|
||||
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) {
|
||||
return this.db.getMessages(roomId, INITIAL_ROOM_MESSAGE_LIMIT, 0, textChannels[0]?.id);
|
||||
}
|
||||
|
||||
const channelMessageSets = await Promise.all(
|
||||
textChannels.map((channel) => this.db.getMessages(roomId, INITIAL_ROOM_MESSAGE_LIMIT, 0, channel.id))
|
||||
);
|
||||
const messagesById = new Map<string, Message>();
|
||||
|
||||
for (const messages of channelMessageSets) {
|
||||
for (const message of messages) {
|
||||
messagesById.set(message.id, message);
|
||||
}
|
||||
}
|
||||
|
||||
return [...messagesById.values()].sort((first, second) => first.timestamp - second.timestamp);
|
||||
}
|
||||
|
||||
/** Constructs a new message, persists it locally, and broadcasts to all peers. */
|
||||
sendMessage$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
@@ -687,78 +746,7 @@ export class MessagesEffects {
|
||||
)
|
||||
);
|
||||
|
||||
private readonly actions$ = inject(Actions);
|
||||
|
||||
private readonly store = inject(Store);
|
||||
|
||||
private readonly db = inject(DatabaseService);
|
||||
|
||||
private readonly debugging = inject(DebuggingService);
|
||||
|
||||
private readonly attachments = inject(AttachmentFacade);
|
||||
|
||||
private readonly customEmoji = inject(CustomEmojiService);
|
||||
|
||||
private readonly webrtc = inject(RealtimeSessionFacade);
|
||||
|
||||
private readonly timeSync = inject(TimeSyncService);
|
||||
|
||||
private readonly linkMetadata = inject(LinkMetadataService);
|
||||
|
||||
private readonly platform = inject(PlatformService);
|
||||
|
||||
private readonly i18n = inject(AppI18nService);
|
||||
|
||||
private readonly messageRevisions = inject(MessageRevisionService);
|
||||
|
||||
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: [] });
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
return this.db.getMessages(roomId, INITIAL_ROOM_MESSAGE_LIMIT, 0, textChannels[0]?.id);
|
||||
}
|
||||
|
||||
const channelMessageSets = await Promise.all(
|
||||
textChannels.map((channel) => this.db.getMessages(roomId, INITIAL_ROOM_MESSAGE_LIMIT, 0, channel.id))
|
||||
);
|
||||
const messagesById = new Map<string, Message>();
|
||||
|
||||
for (const messages of channelMessageSets) {
|
||||
for (const message of messages) {
|
||||
messagesById.set(message.id, message);
|
||||
}
|
||||
}
|
||||
|
||||
return [...messagesById.values()].sort((first, second) => first.timestamp - second.timestamp);
|
||||
}
|
||||
|
||||
private trackBackgroundOperation(task: Promise<unknown> | unknown, message: string, payload: Record<string, unknown>): void {
|
||||
trackDebuggingTaskFailure(task, this.debugging, 'messages', message, payload);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user