mirror of
https://github.com/docmost/docmost.git
synced 2026-05-10 00:13:36 +08:00
feat: page update notifications (#2074)
* feat: watchers notification and email preferences * fix: email copy * digests * clean up * fix * clean up * move backlinks queue-up to history processor * fix * fix keys * feat: group notifications * filter * adjust email digest window
This commit is contained in:
@@ -11,6 +11,7 @@ import { ExpressionBuilder } from 'kysely';
|
||||
import { DB } from '@docmost/db/types/db';
|
||||
import { jsonObjectFrom } from 'kysely/helpers/postgres';
|
||||
import { SpaceMemberRepo } from '@docmost/db/repos/space/space-member.repo';
|
||||
import { NotificationTab, NotificationType } from '../../../core/notification/notification.constants';
|
||||
|
||||
@Injectable()
|
||||
export class NotificationRepo {
|
||||
@@ -27,8 +28,12 @@ export class NotificationRepo {
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async findByUserId(userId: string, pagination: PaginationOptions) {
|
||||
const query = this.db
|
||||
async findByUserId(
|
||||
userId: string,
|
||||
pagination: PaginationOptions,
|
||||
type: NotificationTab = 'all',
|
||||
) {
|
||||
let query = this.db
|
||||
.selectFrom('notifications')
|
||||
.selectAll('notifications')
|
||||
.select((eb) => this.withActor(eb))
|
||||
@@ -42,6 +47,12 @@ export class NotificationRepo {
|
||||
]),
|
||||
);
|
||||
|
||||
if (type === 'direct') {
|
||||
query = query.where('type', '!=', NotificationType.PAGE_UPDATED);
|
||||
} else if (type === 'updates') {
|
||||
query = query.where('type', '=', NotificationType.PAGE_UPDATED);
|
||||
}
|
||||
|
||||
return executeWithCursorPagination(query, {
|
||||
perPage: pagination.limit,
|
||||
cursor: pagination.cursor,
|
||||
@@ -138,6 +149,29 @@ export class NotificationRepo {
|
||||
.execute();
|
||||
}
|
||||
|
||||
async getRecentlyNotifiedUserIds(
|
||||
userIds: string[],
|
||||
pageId: string,
|
||||
type: string,
|
||||
withinHours: number,
|
||||
): Promise<Set<string>> {
|
||||
if (userIds.length === 0) return new Set();
|
||||
|
||||
const cutoff = new Date(Date.now() - withinHours * 60 * 60 * 1000);
|
||||
|
||||
const rows = await this.db
|
||||
.selectFrom('notifications')
|
||||
.select('userId')
|
||||
.where('userId', 'in', userIds)
|
||||
.where('pageId', '=', pageId)
|
||||
.where('type', '=', type)
|
||||
.where('createdAt', '>', cutoff)
|
||||
.groupBy('userId')
|
||||
.execute();
|
||||
|
||||
return new Set(rows.map((r) => r.userId));
|
||||
}
|
||||
|
||||
withActor(eb: ExpressionBuilder<DB, 'notifications'>) {
|
||||
return jsonObjectFrom(
|
||||
eb
|
||||
|
||||
@@ -13,6 +13,7 @@ import { PaginationOptions } from '../../pagination/pagination-options';
|
||||
import { executeWithCursorPagination } from '@docmost/db/pagination/cursor-pagination';
|
||||
import { ExpressionBuilder, sql } from 'kysely';
|
||||
import { jsonObjectFrom } from 'kysely/helpers/postgres';
|
||||
import { NotificationSettingKey } from '../../../core/notification/notification.constants';
|
||||
|
||||
@Injectable()
|
||||
export class UserRepo {
|
||||
@@ -191,6 +192,24 @@ export class UserRepo {
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
async updateNotificationSetting(
|
||||
userId: string,
|
||||
settingKey: NotificationSettingKey,
|
||||
settingValue: boolean,
|
||||
) {
|
||||
return await this.db
|
||||
.updateTable('users')
|
||||
.set({
|
||||
settings: sql`COALESCE(settings, '{}'::jsonb)
|
||||
|| jsonb_build_object('notifications', COALESCE(settings->'notifications', '{}'::jsonb)
|
||||
|| jsonb_build_object(${sql.lit(settingKey)}, ${sql.lit(settingValue)}))`,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where('id', '=', userId)
|
||||
.returning(this.baseFields)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
withUserMfa(eb: ExpressionBuilder<DB, 'users'>) {
|
||||
return jsonObjectFrom(
|
||||
eb
|
||||
|
||||
Reference in New Issue
Block a user