feat(server): wait five minutes before sending email on new album item (#12223)
Album update jobs will now wait five minutes to send. If a new image is added while that job is pending, the old job will be cancelled, and a new one will be enqueued for a minute. This is to prevent a flood of notifications by dragging in images directly to the album, which adds them to the album one at a time. Album updates now include a list of users to email, which is generally everybody except the updater. If somebody else updates the album within that minute, both people will get an album update email in a minute, as they both added images and the other should be notified.
This commit is contained in:
@@ -5,9 +5,11 @@ import { AlbumEntity } from 'src/entities/album.entity';
|
||||
import { ArgOf } from 'src/interfaces/event.interface';
|
||||
import {
|
||||
IEmailJob,
|
||||
IEntityJob,
|
||||
INotifyAlbumInviteJob,
|
||||
INotifyAlbumUpdateJob,
|
||||
INotifySignupJob,
|
||||
JobItem,
|
||||
JobName,
|
||||
JobStatus,
|
||||
} from 'src/interfaces/job.interface';
|
||||
@@ -21,6 +23,8 @@ import { getPreferences } from 'src/utils/preferences';
|
||||
|
||||
@Injectable()
|
||||
export class NotificationService extends BaseService {
|
||||
private static albumUpdateEmailDelayMs = 300_000;
|
||||
|
||||
@OnEvent({ name: 'config.update' })
|
||||
onConfigUpdate({ oldConfig, newConfig }: ArgOf<'config.update'>) {
|
||||
this.eventRepository.clientBroadcast('on_config_update');
|
||||
@@ -100,8 +104,30 @@ export class NotificationService extends BaseService {
|
||||
}
|
||||
|
||||
@OnEvent({ name: 'album.update' })
|
||||
async onAlbumUpdate({ id, updatedBy }: ArgOf<'album.update'>) {
|
||||
await this.jobRepository.queue({ name: JobName.NOTIFY_ALBUM_UPDATE, data: { id, senderId: updatedBy } });
|
||||
async onAlbumUpdate({ id, recipientIds }: ArgOf<'album.update'>) {
|
||||
// if recipientIds is empty, album likely only has one user part of it, don't queue notification if so
|
||||
if (recipientIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const job: JobItem = {
|
||||
name: JobName.NOTIFY_ALBUM_UPDATE,
|
||||
data: { id, recipientIds, delay: NotificationService.albumUpdateEmailDelayMs },
|
||||
};
|
||||
|
||||
const previousJobData = await this.jobRepository.removeJob(id, JobName.NOTIFY_ALBUM_UPDATE);
|
||||
if (previousJobData && this.isAlbumUpdateJob(previousJobData)) {
|
||||
for (const id of previousJobData.recipientIds) {
|
||||
if (!recipientIds.includes(id)) {
|
||||
recipientIds.push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
await this.jobRepository.queue(job);
|
||||
}
|
||||
|
||||
private isAlbumUpdateJob(job: IEntityJob): job is INotifyAlbumUpdateJob {
|
||||
return 'recipientIds' in job;
|
||||
}
|
||||
|
||||
@OnEvent({ name: 'album.invite' })
|
||||
@@ -228,7 +254,7 @@ export class NotificationService extends BaseService {
|
||||
return JobStatus.SUCCESS;
|
||||
}
|
||||
|
||||
async handleAlbumUpdate({ id, senderId }: INotifyAlbumUpdateJob) {
|
||||
async handleAlbumUpdate({ id, recipientIds }: INotifyAlbumUpdateJob) {
|
||||
const album = await this.albumRepository.getById(id, { withAssets: false });
|
||||
|
||||
if (!album) {
|
||||
@@ -240,7 +266,9 @@ export class NotificationService extends BaseService {
|
||||
return JobStatus.SKIPPED;
|
||||
}
|
||||
|
||||
const recipients = [...album.albumUsers.map((user) => user.user), owner].filter((user) => user.id !== senderId);
|
||||
const recipients = [...album.albumUsers.map((user) => user.user), owner].filter((user) =>
|
||||
recipientIds.includes(user.id),
|
||||
);
|
||||
const attachment = await this.getAlbumThumbnailAttachment(album);
|
||||
|
||||
const { server } = await this.getConfig({ withCache: false });
|
||||
|
||||
Reference in New Issue
Block a user