`@typescript-eslint/member-ordering` is off repo-wide because bulk member reordering breaks Angular inject()/field init order, so the per-file disables are dead weight. Removing them left a blank first line, stripped here too.
127 lines
4.1 KiB
TypeScript
127 lines
4.1 KiB
TypeScript
import { Injectable, inject } from '@angular/core';
|
|
import { createEffect } from '@ngrx/effects';
|
|
import { Store } from '@ngrx/store';
|
|
import {
|
|
EMPTY,
|
|
from,
|
|
merge,
|
|
Observable
|
|
} from 'rxjs';
|
|
import {
|
|
distinctUntilChanged,
|
|
map,
|
|
mergeMap
|
|
} from 'rxjs/operators';
|
|
import { selectCurrentUser } from '../../../store/users/users.selectors';
|
|
import {
|
|
ChatEvent,
|
|
CustomEmoji,
|
|
CustomEmojiSummaryItem,
|
|
CustomEmojiTransferManifest
|
|
} from '../../../shared-kernel';
|
|
import { RealtimeSessionFacade } from '../../../core/realtime';
|
|
import { CustomEmojiService } from './custom-emoji.service';
|
|
|
|
@Injectable()
|
|
export class CustomEmojiSyncEffects {
|
|
private readonly customEmoji = inject(CustomEmojiService);
|
|
private readonly store = inject(Store);
|
|
private readonly webrtc = inject(RealtimeSessionFacade);
|
|
private readonly incomingEvents$ = merge(
|
|
this.webrtc.onMessageReceived,
|
|
this.webrtc.onSignalingMessage as Observable<ChatEvent>
|
|
);
|
|
|
|
currentUserLoad$ = createEffect(
|
|
() => this.store.select(selectCurrentUser).pipe(
|
|
map((user) => user?.id ?? null),
|
|
distinctUntilChanged(),
|
|
mergeMap((userId) => from(this.customEmoji.loadForUser(userId)).pipe(mergeMap(() => EMPTY)))
|
|
),
|
|
{ dispatch: false }
|
|
);
|
|
|
|
peerConnectedSummary$ = createEffect(
|
|
() => this.webrtc.onPeerConnected.pipe(
|
|
mergeMap((peerId) => from(this.customEmoji.sendSummaryToPeer(peerId)).pipe(mergeMap(() => EMPTY)))
|
|
),
|
|
{ dispatch: false }
|
|
);
|
|
|
|
incomingCustomEmojiEvents$ = createEffect(
|
|
() => this.incomingEvents$.pipe(
|
|
mergeMap((event) => {
|
|
if (event.type === 'message' || event.type === 'chat-message') {
|
|
if (!event.fromPeerId || typeof event.message?.content !== 'string') {
|
|
return EMPTY;
|
|
}
|
|
|
|
return from(this.customEmoji.requestMissingFromMessageContent(event.fromPeerId, event.message.content)).pipe(mergeMap(() => EMPTY));
|
|
}
|
|
|
|
if (event.type === 'chat-sync-batch' || event.type === 'chat-sync-full') {
|
|
if (!event.fromPeerId || !Array.isArray(event.messages)) {
|
|
return EMPTY;
|
|
}
|
|
|
|
return from(Promise.all(event.messages.map((message) => this.customEmoji.requestMissingFromMessageContent(
|
|
event.fromPeerId as string,
|
|
message.content
|
|
)))).pipe(mergeMap(() => EMPTY));
|
|
}
|
|
|
|
switch (event.type) {
|
|
case 'custom-emoji-summary': {
|
|
if (!event.fromPeerId || !Array.isArray(event.customEmojiSummaries)) {
|
|
return EMPTY;
|
|
}
|
|
|
|
const ids = this.customEmoji.missingFromSummary(event.customEmojiSummaries as CustomEmojiSummaryItem[]);
|
|
|
|
if (ids.length === 0) {
|
|
return EMPTY;
|
|
}
|
|
|
|
this.webrtc.sendToPeer(event.fromPeerId, {
|
|
type: 'custom-emoji-request',
|
|
ids
|
|
});
|
|
|
|
return EMPTY;
|
|
}
|
|
|
|
case 'custom-emoji-request':
|
|
if (!event.fromPeerId || !Array.isArray(event.ids)) {
|
|
return EMPTY;
|
|
}
|
|
|
|
return from(this.customEmoji.sendRequestedToPeer(event.fromPeerId, event.ids)).pipe(mergeMap(() => EMPTY));
|
|
|
|
case 'custom-emoji-full':
|
|
if (event.customEmoji) {
|
|
return from(this.customEmoji.saveRemoteEmoji(event.customEmoji as CustomEmoji)).pipe(mergeMap(() => EMPTY));
|
|
}
|
|
|
|
if (!event.customEmojiTransfer || typeof event.total !== 'number') {
|
|
return EMPTY;
|
|
}
|
|
|
|
this.customEmoji.receiveTransferStart(event.customEmojiTransfer as CustomEmojiTransferManifest, event.total);
|
|
return EMPTY;
|
|
|
|
case 'custom-emoji-chunk':
|
|
if (!event.customEmojiId || typeof event.index !== 'number' || typeof event.total !== 'number' || typeof event.data !== 'string') {
|
|
return EMPTY;
|
|
}
|
|
|
|
return from(this.customEmoji.receiveTransferChunk(event.customEmojiId, event.index, event.total, event.data)).pipe(mergeMap(() => EMPTY));
|
|
|
|
default:
|
|
return EMPTY;
|
|
}
|
|
})
|
|
),
|
|
{ dispatch: false }
|
|
);
|
|
}
|