feat: paginate and merge frequent activities (#274)

* feat: paginate and merge frequent activities

* feat: due date activities

* fix: requested changes fixes

* refactor: slight reshuffle and improve types

* chore: translations

---------

Co-authored-by: Henry <henry_ball@hotmail.co.uk>
This commit is contained in:
Ridham Khandar
2025-12-16 14:37:08 +05:30
committed by GitHub
parent 3cb40d0f9a
commit 57186b6b4d
40 changed files with 1515 additions and 884 deletions

View File

@@ -1,4 +1,4 @@
import { and, asc, desc, eq, gt, inArray, isNull, sql } from "drizzle-orm";
import { and, asc, desc, eq, gt, inArray, isNull, lt, sql } from "drizzle-orm";
import type { dbClient } from "@kan/db/client";
import {
@@ -9,6 +9,7 @@ import {
cardToWorkspaceMembers,
checklistItems,
checklists,
comments,
labels,
lists,
workspaceMembers,

View File

@@ -1,6 +1,8 @@
import { and, asc, eq, gt, inArray, isNull, or } from "drizzle-orm";
import type { dbClient } from "@kan/db/client";
import type { ActivityType } from "@kan/db/schema";
import { cardActivities } from "@kan/db/schema";
import { cardActivities, comments } from "@kan/db/schema";
import { generateUID } from "@kan/shared/utils";
export const create = async (
@@ -89,3 +91,114 @@ export const bulkCreate = async (
return results;
};
export const getPaginatedActivities = async (
db: dbClient,
cardId: number,
options?: {
limit?: number;
cursor?: Date; // createdAt cursor for pagination
},
) => {
const limit = options?.limit ?? 20;
const cursor = options?.cursor;
const validComments = await db
.select({ id: comments.id })
.from(comments)
.where(and(eq(comments.cardId, cardId), isNull(comments.deletedAt)));
const validCommentIds = validComments.map((comment) => comment.id);
const activities = await db.query.cardActivities.findMany({
columns: {
publicId: true,
type: true,
createdAt: true,
fromIndex: true,
toIndex: true,
fromTitle: true,
toTitle: true,
fromDescription: true,
toDescription: true,
fromDueDate: true,
toDueDate: true,
},
where: and(
eq(cardActivities.cardId, cardId),
cursor ? gt(cardActivities.createdAt, cursor) : undefined,
or(
isNull(cardActivities.commentId),
inArray(cardActivities.commentId, validCommentIds),
),
),
with: {
fromList: {
columns: {
publicId: true,
name: true,
index: true,
},
},
toList: {
columns: {
publicId: true,
name: true,
index: true,
},
},
label: {
columns: {
publicId: true,
name: true,
},
},
member: {
columns: {
publicId: true,
},
with: {
user: {
columns: {
id: true,
name: true,
email: true,
},
},
},
},
user: {
columns: {
id: true,
name: true,
email: true,
},
},
comment: {
columns: {
publicId: true,
comment: true,
createdBy: true,
updatedAt: true,
deletedAt: true,
},
},
},
orderBy: asc(cardActivities.createdAt), // required for merging and pagination
limit: limit + 1, // fetch one extra to check if there are more
});
const hasMore = activities.length > limit;
const items = activities.slice(0, limit);
const nextCursor = hasMore ? items[items.length - 1]?.createdAt : undefined;
return {
activities: items,
hasMore,
nextCursor,
};
};
export type PaginatedActivitiesResult = Awaited<
ReturnType<typeof getPaginatedActivities>
>;