mirror of
https://github.com/docmost/docmost.git
synced 2026-05-07 06:23:06 +08:00
support beforeCursor/prevCursor
This commit is contained in:
@@ -18,7 +18,6 @@ import {
|
||||
IconFileDescription,
|
||||
IconSearch,
|
||||
IconCheck,
|
||||
IconSparkles,
|
||||
} from "@tabler/icons-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDebouncedValue } from "@mantine/hooks";
|
||||
@@ -26,7 +25,7 @@ import { useGetSpacesQuery } from "@/features/space/queries/space-query";
|
||||
import { useLicense } from "@/ee/hooks/use-license";
|
||||
import classes from "./search-spotlight-filters.module.css";
|
||||
import { isCloud } from "@/lib/config.ts";
|
||||
import { useAtom } from "jotai/index";
|
||||
import { useAtom } from "jotai";
|
||||
import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts";
|
||||
|
||||
interface SearchSpotlightFiltersProps {
|
||||
@@ -53,7 +52,6 @@ export function SearchSpotlightFilters({
|
||||
const [workspace] = useAtom(workspaceAtom);
|
||||
|
||||
const { data: spacesData } = useGetSpacesQuery({
|
||||
page: 1,
|
||||
limit: 100,
|
||||
query: debouncedSpaceQuery,
|
||||
});
|
||||
@@ -265,7 +263,9 @@ export function SearchSpotlightFilters({
|
||||
contentType !== option.value &&
|
||||
handleFilterChange("contentType", option.value)
|
||||
}
|
||||
disabled={option.disabled || (isAiMode && option.value === "attachment")}
|
||||
disabled={
|
||||
option.disabled || (isAiMode && option.value === "attachment")
|
||||
}
|
||||
>
|
||||
<Group flex="1" gap="xs">
|
||||
<div>
|
||||
@@ -275,11 +275,13 @@ export function SearchSpotlightFilters({
|
||||
{t("Enterprise")}
|
||||
</Badge>
|
||||
)}
|
||||
{!option.disabled && isAiMode && option.value === "attachment" && (
|
||||
<Text size="xs" mt={4}>
|
||||
{t("Ask AI not available for attachments")}
|
||||
</Text>
|
||||
)}
|
||||
{!option.disabled &&
|
||||
isAiMode &&
|
||||
option.value === "attachment" && (
|
||||
<Text size="xs" mt={4}>
|
||||
{t("Ask AI not available for attachments")}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
{contentType === option.value && <IconCheck size={20} />}
|
||||
</Group>
|
||||
|
||||
@@ -15,7 +15,7 @@ import { AvatarIconType } from "@/features/attachments/types/attachment.types.ts
|
||||
|
||||
export default function SpaceGrid() {
|
||||
const { t } = useTranslation();
|
||||
const { data, isLoading } = useGetSpacesQuery({ page: 1, limit: 10 });
|
||||
const { data, isLoading } = useGetSpacesQuery({ limit: 10 });
|
||||
|
||||
const cards = data?.items.slice(0, 9).map((space, index) => (
|
||||
<Card
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export interface QueryParams {
|
||||
query?: string;
|
||||
cursor?: string;
|
||||
beforeCursor?: string;
|
||||
limit?: number;
|
||||
adminView?: boolean;
|
||||
}
|
||||
@@ -32,6 +33,7 @@ export type IPaginationMeta = {
|
||||
hasNextPage: boolean;
|
||||
hasPrevPage: boolean;
|
||||
nextCursor: string | null;
|
||||
prevCursor: string | null;
|
||||
};
|
||||
export type IPagination<T> = {
|
||||
items: T[];
|
||||
|
||||
@@ -206,7 +206,8 @@ export class PageService {
|
||||
|
||||
return executeWithCursorPagination(query, {
|
||||
perPage: 250,
|
||||
after: pagination.cursor,
|
||||
cursor: pagination.cursor,
|
||||
beforeCursor: pagination.beforeCursor,
|
||||
fields: [
|
||||
{ expression: 'position', direction: 'asc', orderModifier: (ob) => ob.collate('C').asc() },
|
||||
{ expression: 'id', direction: 'asc' },
|
||||
|
||||
@@ -66,7 +66,8 @@ export class WorkspaceInvitationService {
|
||||
|
||||
return executeWithCursorPagination(query, {
|
||||
perPage: pagination.limit,
|
||||
after: pagination.cursor,
|
||||
cursor: pagination.cursor,
|
||||
beforeCursor: pagination.beforeCursor,
|
||||
fields: [{ expression: 'id', direction: 'asc' }],
|
||||
parseCursor: (cursor) => ({ id: cursor.id }),
|
||||
});
|
||||
|
||||
@@ -110,9 +110,10 @@ type CursorPaginationResultRow<
|
||||
|
||||
type CursorPaginationMeta = {
|
||||
limit: number;
|
||||
hasNextPage?: boolean;
|
||||
hasPrevPage?: boolean;
|
||||
hasNextPage: boolean;
|
||||
hasPrevPage: boolean;
|
||||
nextCursor: string | null;
|
||||
prevCursor: string | null;
|
||||
};
|
||||
|
||||
export type CursorPaginationResult<
|
||||
@@ -133,8 +134,8 @@ export async function executeWithCursorPagination<
|
||||
qb: SelectQueryBuilder<DB, TB, O>,
|
||||
opts: {
|
||||
perPage: number;
|
||||
after?: string;
|
||||
before?: string;
|
||||
cursor?: string;
|
||||
beforeCursor?: string;
|
||||
cursorPerRow?: TCursorKey;
|
||||
fields: TFields;
|
||||
encodeCursor?: CursorEncoder<DB, TB, O, TFields>;
|
||||
@@ -219,10 +220,10 @@ export async function executeWithCursorPagination<
|
||||
});
|
||||
}
|
||||
|
||||
if (opts.after) qb = applyCursor(qb, opts.after, 'asc');
|
||||
if (opts.before) qb = applyCursor(qb, opts.before, 'desc');
|
||||
if (opts.cursor) qb = applyCursor(qb, opts.cursor, 'asc');
|
||||
if (opts.beforeCursor) qb = applyCursor(qb, opts.beforeCursor, 'desc');
|
||||
|
||||
const reversed = !!opts.before && !opts.after;
|
||||
const reversed = !!opts.beforeCursor && !opts.cursor;
|
||||
|
||||
for (const { expression, direction, orderModifier } of fields) {
|
||||
qb = qb.orderBy(
|
||||
@@ -234,8 +235,7 @@ export async function executeWithCursorPagination<
|
||||
|
||||
const rows = await qb.limit(opts.perPage + 1).execute();
|
||||
|
||||
const hasNextPage = reversed ? true : rows.length > opts.perPage;
|
||||
const hasPrevPage = !reversed ? undefined : rows.length > opts.perPage;
|
||||
const hasNextPage = rows.length > opts.perPage;
|
||||
|
||||
// If we fetched an extra row to determine if we have a next page, that
|
||||
// shouldn't be in the returned results
|
||||
@@ -243,10 +243,11 @@ export async function executeWithCursorPagination<
|
||||
|
||||
if (reversed) rows.reverse();
|
||||
|
||||
//const startRow = rows[0];
|
||||
const startRow = rows[0];
|
||||
const endRow = rows[rows.length - 1];
|
||||
|
||||
//const startCursor = startRow ? generateCursor(startRow) : null;
|
||||
const hasPrevPage = !!opts.cursor;
|
||||
const prevCursor = hasPrevPage && startRow ? generateCursor(startRow) : null;
|
||||
const nextCursor = hasNextPage && endRow ? generateCursor(endRow) : null;
|
||||
|
||||
return {
|
||||
@@ -263,8 +264,9 @@ export async function executeWithCursorPagination<
|
||||
meta: {
|
||||
limit: opts.perPage,
|
||||
hasNextPage,
|
||||
hasPrevPage: hasPrevPage ?? !!opts.after,
|
||||
hasPrevPage,
|
||||
nextCursor,
|
||||
prevCursor,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9,11 +9,6 @@ import {
|
||||
} from 'class-validator';
|
||||
|
||||
export class PaginationOptions {
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
page = 1;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@IsPositive()
|
||||
@@ -25,6 +20,10 @@ export class PaginationOptions {
|
||||
@IsString()
|
||||
cursor?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
beforeCursor?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
query: string;
|
||||
|
||||
@@ -41,7 +41,8 @@ export class CommentRepo {
|
||||
|
||||
return executeWithCursorPagination(query, {
|
||||
perPage: pagination.limit,
|
||||
after: pagination.cursor,
|
||||
cursor: pagination.cursor,
|
||||
beforeCursor: pagination.beforeCursor,
|
||||
fields: [{ expression: 'id', direction: 'asc' }],
|
||||
parseCursor: (cursor) => ({ id: cursor.id }),
|
||||
});
|
||||
|
||||
@@ -62,7 +62,8 @@ export class GroupUserRepo {
|
||||
|
||||
const result = await executeWithCursorPagination(query, {
|
||||
perPage: pagination.limit,
|
||||
after: pagination.cursor,
|
||||
cursor: pagination.cursor,
|
||||
beforeCursor: pagination.beforeCursor,
|
||||
fields: [{ expression: 'users.id', direction: 'asc', key: 'id' }],
|
||||
parseCursor: (cursor) => ({ id: cursor.id }),
|
||||
});
|
||||
|
||||
@@ -127,7 +127,8 @@ export class GroupRepo {
|
||||
const query = this.db.selectFrom(baseQuery.as('sub')).selectAll('sub');
|
||||
return executeWithCursorPagination(query, {
|
||||
perPage: pagination.limit,
|
||||
after: pagination.cursor,
|
||||
cursor: pagination.cursor,
|
||||
beforeCursor: pagination.beforeCursor,
|
||||
fields: [
|
||||
{
|
||||
expression: 'sub.memberCount',
|
||||
|
||||
@@ -69,7 +69,8 @@ export class PageHistoryRepo {
|
||||
|
||||
return executeWithCursorPagination(query, {
|
||||
perPage: pagination.limit,
|
||||
after: pagination.cursor,
|
||||
cursor: pagination.cursor,
|
||||
beforeCursor: pagination.beforeCursor,
|
||||
fields: [{ expression: 'id', direction: 'desc' }],
|
||||
parseCursor: (cursor) => ({ id: cursor.id }),
|
||||
});
|
||||
|
||||
@@ -285,7 +285,8 @@ export class PageRepo {
|
||||
|
||||
return executeWithCursorPagination(query, {
|
||||
perPage: pagination.limit,
|
||||
after: pagination.cursor,
|
||||
cursor: pagination.cursor,
|
||||
beforeCursor: pagination.beforeCursor,
|
||||
fields: [
|
||||
{ expression: 'updatedAt', direction: 'desc' },
|
||||
{ expression: 'id', direction: 'desc' },
|
||||
@@ -307,7 +308,8 @@ export class PageRepo {
|
||||
|
||||
return executeWithCursorPagination(query, {
|
||||
perPage: pagination.limit,
|
||||
after: pagination.cursor,
|
||||
cursor: pagination.cursor,
|
||||
beforeCursor: pagination.beforeCursor,
|
||||
fields: [
|
||||
{ expression: 'updatedAt', direction: 'desc' },
|
||||
{ expression: 'id', direction: 'desc' },
|
||||
@@ -347,7 +349,8 @@ export class PageRepo {
|
||||
|
||||
return executeWithCursorPagination(query, {
|
||||
perPage: pagination.limit,
|
||||
after: pagination.cursor,
|
||||
cursor: pagination.cursor,
|
||||
beforeCursor: pagination.beforeCursor,
|
||||
fields: [
|
||||
{ expression: 'deletedAt', direction: 'desc' },
|
||||
{ expression: 'id', direction: 'desc' },
|
||||
|
||||
@@ -147,7 +147,8 @@ export class ShareRepo {
|
||||
|
||||
return executeWithCursorPagination(query, {
|
||||
perPage: pagination.limit,
|
||||
after: pagination.cursor,
|
||||
cursor: pagination.cursor,
|
||||
beforeCursor: pagination.beforeCursor,
|
||||
fields: [
|
||||
{ expression: 'updatedAt', direction: 'desc' },
|
||||
{ expression: 'id', direction: 'desc' },
|
||||
|
||||
@@ -141,7 +141,8 @@ export class SpaceMemberRepo {
|
||||
|
||||
const result = await executeWithCursorPagination(query, {
|
||||
perPage: pagination.limit,
|
||||
after: pagination.cursor,
|
||||
cursor: pagination.cursor,
|
||||
beforeCursor: pagination.beforeCursor,
|
||||
fields: [
|
||||
{ expression: 'sub.isGroup', direction: 'desc', key: 'isGroup' },
|
||||
{ expression: 'sub.createdAt', direction: 'asc', key: 'createdAt' },
|
||||
@@ -262,7 +263,8 @@ export class SpaceMemberRepo {
|
||||
|
||||
return executeWithCursorPagination(query, {
|
||||
perPage: pagination.limit,
|
||||
after: pagination.cursor,
|
||||
cursor: pagination.cursor,
|
||||
beforeCursor: pagination.beforeCursor,
|
||||
fields: [{ expression: 'id', direction: 'asc' }],
|
||||
parseCursor: (cursor) => ({ id: cursor.id }),
|
||||
});
|
||||
|
||||
@@ -128,7 +128,8 @@ export class SpaceRepo {
|
||||
|
||||
return executeWithCursorPagination(query, {
|
||||
perPage: pagination.limit,
|
||||
after: pagination.cursor,
|
||||
cursor: pagination.cursor,
|
||||
beforeCursor: pagination.beforeCursor,
|
||||
fields: [{ expression: 'id', direction: 'asc' }],
|
||||
parseCursor: (cursor) => ({ id: cursor.id }),
|
||||
});
|
||||
|
||||
@@ -163,7 +163,8 @@ export class UserRepo {
|
||||
|
||||
return executeWithCursorPagination(query, {
|
||||
perPage: pagination.limit,
|
||||
after: pagination.cursor,
|
||||
cursor: pagination.cursor,
|
||||
beforeCursor: pagination.beforeCursor,
|
||||
fields: [{ expression: 'id', direction: 'asc' }],
|
||||
parseCursor: (cursor) => ({ id: cursor.id }),
|
||||
});
|
||||
|
||||
+1
-1
Submodule apps/server/src/ee updated: fb7257cb4c...9888832270
@@ -60,7 +60,8 @@ export class FileTaskController {
|
||||
|
||||
return executeWithCursorPagination(query, {
|
||||
perPage: pagination.limit,
|
||||
after: pagination.cursor,
|
||||
cursor: pagination.cursor,
|
||||
beforeCursor: pagination.beforeCursor,
|
||||
fields: [{ expression: 'id', direction: 'desc' }],
|
||||
parseCursor: (cursor) => ({ id: cursor.id }),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user