update notif , now it is cursor based

This commit is contained in:
2025-12-20 20:24:39 +03:30
parent 6318c5b8ab
commit fae38c427d
3 changed files with 56 additions and 23 deletions
@@ -8,6 +8,7 @@ import { AdminAuthGuard } from '../../auth/guards/adminAuth.guard';
import { RestId } from '../../../common/decorators/rest-id.decorator';
import { UpdatePreferenceDto } from '../dto/update-preference.dto';
import { AdminId } from 'src/common/decorators/admin-id.decorator';
import { API_HEADER_SLUG } from 'src/common/constants/index';
@ApiTags('notifications')
@Controller()
@@ -21,20 +22,23 @@ export class NotificationsController {
@ApiBearerAuth()
@Get('public/notifications')
@ApiOperation({ summary: 'Get user restaurant notifications' })
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiQuery({ name: 'limit', required: false, type: Number })
async getUserNotifications(@UserId() userId: string, @RestId() restaurantId: string, @Query('limit') limit?: number) {
@ApiHeader(API_HEADER_SLUG)
@ApiQuery({ name: 'limit', required: false, type: Number, description: 'Number of items to return (default: 50)' })
@ApiQuery({ name: 'cursor', required: false, type: String, description: 'Cursor for pagination (ID of the last item from previous page)' })
@ApiQuery({ name: 'status', required: false, enum: ['seen', 'unseen'], description: 'Filter by notification status' })
async getUserNotifications(
@UserId() userId: string,
@RestId() restaurantId: string,
@Query('limit') limit?: number,
@Query('cursor') cursor?: string,
@Query('status') status?: 'seen' | 'unseen',
) {
return await this.notificationService.findByUserAndRestaurant(
userId,
restaurantId,
limit ? parseInt(limit.toString(), 10) : 50,
cursor,
status,
);
}
@@ -23,5 +23,5 @@ export class Notification extends BaseEntity {
content!: string;
@Property({ nullable: true })
sentAt?: Date;
seenAt?: Date;
}
@@ -1,5 +1,5 @@
import { Injectable, Logger, NotFoundException } from '@nestjs/common';
import { EntityManager } from '@mikro-orm/postgresql';
import { EntityManager, FilterQuery } from '@mikro-orm/postgresql';
import { Notification } from '../entities/notification.entity';
import { NotificationPreferenceService } from './notification-preference.service';
import { NotificationQueueService } from './notification-queue.service';
@@ -108,15 +108,44 @@ export class NotificationService {
);
}
async findByUserAndRestaurant(userId: string, restaurantId: string, limit = 50): Promise<Notification[]> {
return this.em.find(
Notification,
{ user: { id: userId }, restaurant: { id: restaurantId } },
{
orderBy: { createdAt: 'DESC' },
limit,
},
);
async findByUserAndRestaurant(
userId: string,
restaurantId: string,
limit = 50,
cursor?: string,
status?: 'seen' | 'unseen',
): Promise<{ data: Notification[]; nextCursor: string | null }> {
const where: FilterQuery<Notification> = {
user: { id: userId },
restaurant: { id: restaurantId },
};
// Filter by status (seen/unseen)
if (status === 'seen') {
where.seenAt = { $ne: null };
} else if (status === 'unseen') {
where.seenAt = null;
}
// Cursor-based pagination: if cursor is provided, get items with id < cursor (since ULIDs are time-ordered)
if (cursor) {
where.id = { $lt: cursor };
}
const notifications = await this.em.find(Notification, where, {
orderBy: { createdAt: 'DESC', id: 'DESC' },
limit: limit + 1, // Fetch one extra to determine if there's a next page
});
// Check if there's a next page
const hasNextPage = notifications.length > limit;
const data = hasNextPage ? notifications.slice(0, limit) : notifications;
const nextCursor = hasNextPage && data.length > 0 ? data[data.length - 1].id : null;
return {
data,
nextCursor,
};
}
async readNotificationAdmin(id: string, adminId: string, restaurantId: string): Promise<void> {
@@ -128,7 +157,7 @@ export class NotificationService {
if (!notification) {
throw new NotFoundException('Notification not found');
}
notification.sentAt = new Date();
notification.seenAt = new Date();
await this.em.persistAndFlush(notification);
}
async readNotificationAsUser(id: string, userId: string, restaurantId: string): Promise<void> {
@@ -140,7 +169,7 @@ export class NotificationService {
if (!notification) {
throw new NotFoundException('Notification not found');
}
notification.sentAt = new Date();
notification.seenAt = new Date();
await this.em.persistAndFlush(notification);
}