[ENG-1078] Fix pagination (#1299)

* fix 'load more' breaking

* paginate all paginated queries by model id

* arrays start at 0 stupid
This commit is contained in:
Brendan Allan 2023-09-05 16:11:04 +08:00 committed by GitHub
parent a92d6c2faf
commit 2d1ce9af03
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 81 additions and 61 deletions

View file

@ -203,7 +203,7 @@ pub enum FilePathObjectCursor {
#[derive(Deserialize, Type, Debug)]
#[serde(rename_all = "camelCase")]
pub enum FilePathCursorVariant {
None(file_path::pub_id::Type),
None,
Name(CursorOrderItem<String>),
// SizeInBytes(CursorOrderItem<Vec<u8>>),
DateCreated(CursorOrderItem<DateTime<FixedOffset>>),
@ -222,7 +222,7 @@ pub struct FilePathCursor {
#[derive(Deserialize, Type, Debug)]
#[serde(rename_all = "camelCase")]
pub enum ObjectCursor {
None(object::pub_id::Type),
None,
DateAccessed(CursorOrderItem<DateTime<FixedOffset>>),
Kind(CursorOrderItem<i32>),
}
@ -256,10 +256,10 @@ impl ObjectOrder {
#[derive(Deserialize, Type, Debug)]
#[serde(rename_all = "camelCase")]
pub enum OrderAndPagination<TOrder, TCursor> {
pub enum OrderAndPagination<TId, TOrder, TCursor> {
OrderOnly(TOrder),
Offset { offset: i32, order: Option<TOrder> },
Cursor(TCursor),
Cursor { id: TId, cursor: TCursor },
}
#[derive(Deserialize, Type, Debug, Default, Clone, Copy)]
@ -396,7 +396,8 @@ pub fn mount() -> AlphaRouter<Ctx> {
struct FilePathSearchArgs {
take: u8,
#[specta(optional)]
order_and_pagination: Option<OrderAndPagination<FilePathOrder, FilePathCursor>>,
order_and_pagination:
Option<OrderAndPagination<file_path::id::Type, FilePathOrder, FilePathCursor>>,
#[serde(default)]
filter: FilePathFilterArgs,
#[serde(default = "default_group_directories")]
@ -442,7 +443,7 @@ pub fn mount() -> AlphaRouter<Ctx> {
query = query.order_by(order.into_param())
}
}
OrderAndPagination::Cursor(cursor) => {
OrderAndPagination::Cursor { id, cursor } => {
// This may seem dumb but it's vital!
// If we're grouping by directories + all directories have been fetched,
// we don't want to include them in the results.
@ -457,10 +458,21 @@ pub fn mount() -> AlphaRouter<Ctx> {
($field:ident, $item:ident) => {{
let item = $item;
query.add_where(match item.order {
SortOrder::Asc => file_path::$field::gt(item.data),
SortOrder::Desc => file_path::$field::lt(item.data),
});
let data = item.data.clone();
query.add_where(or![
match item.order {
SortOrder::Asc => file_path::$field::gt(data),
SortOrder::Desc => file_path::$field::lt(data),
},
prisma_client_rust::and![
file_path::$field::equals(Some(item.data)),
match item.order {
SortOrder::Asc => file_path::id::gt(id),
SortOrder::Desc => file_path::id::lt(id),
}
]
]);
query = query
.order_by(file_path::$field::order(item.order.into()));
@ -468,8 +480,8 @@ pub fn mount() -> AlphaRouter<Ctx> {
}
match cursor.variant {
FilePathCursorVariant::None(item) => {
query = query.cursor(file_path::pub_id::equals(item));
FilePathCursorVariant::None => {
query.add_where(file_path::id::gt(id));
}
FilePathCursorVariant::Name(item) => arm!(name, item),
FilePathCursorVariant::DateCreated(item) => {
@ -511,8 +523,8 @@ pub fn mount() -> AlphaRouter<Ctx> {
}
};
query = query
.order_by(file_path::pub_id::order(prisma::SortOrder::Asc));
query =
query.order_by(file_path::id::order(prisma::SortOrder::Asc));
}
}
}
@ -574,7 +586,8 @@ pub fn mount() -> AlphaRouter<Ctx> {
struct ObjectSearchArgs {
take: u8,
#[specta(optional)]
order_and_pagination: Option<OrderAndPagination<ObjectOrder, ObjectCursor>>,
order_and_pagination:
Option<OrderAndPagination<object::id::Type, ObjectOrder, ObjectCursor>>,
#[serde(default)]
filter: ObjectFilterArgs,
}
@ -607,15 +620,26 @@ pub fn mount() -> AlphaRouter<Ctx> {
query = query.order_by(order.into_param())
}
}
OrderAndPagination::Cursor(cursor) => {
OrderAndPagination::Cursor { id, cursor } => {
macro_rules! arm {
($field:ident, $item:ident) => {{
let item = $item;
query.add_where(match item.order {
SortOrder::Asc => object::$field::gt(item.data),
SortOrder::Desc => object::$field::lt(item.data),
});
let data = item.data.clone();
query.add_where(or![
match item.order {
SortOrder::Asc => object::$field::gt(data),
SortOrder::Desc => object::$field::lt(data),
},
prisma_client_rust::and![
object::$field::equals(Some(item.data)),
match item.order {
SortOrder::Asc => object::id::gt(id),
SortOrder::Desc => object::id::lt(id),
}
]
]);
query = query
.order_by(object::$field::order(item.order.into()));
@ -623,8 +647,8 @@ pub fn mount() -> AlphaRouter<Ctx> {
}
match cursor {
ObjectCursor::None(item) => {
query = query.cursor(object::pub_id::equals(item));
ObjectCursor::None => {
query.add_where(object::id::gt(id));
}
ObjectCursor::Kind(item) => arm!(kind, item),
ObjectCursor::DateAccessed(item) => arm!(date_accessed, item),

View file

@ -0,0 +1,3 @@
export * from './useExplorerInfiniteQuery';
export * from './usePathsInfiniteQuery';
export * from './useObjectsInfiniteQuery';

View file

@ -0,0 +1,10 @@
import { UseInfiniteQueryOptions } from '@tanstack/react-query';
import { ExplorerItem, LibraryConfigWrapped, SearchData } from '@sd/client';
import { Ordering } from '../store';
import { UseExplorerSettings } from '../useExplorer';
export type UseExplorerInfiniteQueryArgs<TArg, TOrder extends Ordering> = {
library: LibraryConfigWrapped;
arg: TArg;
settings: UseExplorerSettings<TOrder>;
} & Pick<UseInfiniteQueryOptions<SearchData<ExplorerItem>>, 'enabled'>;

View file

@ -1,27 +1,19 @@
import { UseInfiniteQueryOptions, useInfiniteQuery } from '@tanstack/react-query';
import { useInfiniteQuery } from '@tanstack/react-query';
import {
ExplorerItem,
LibraryConfigWrapped,
ObjectCursor,
ObjectOrder,
ObjectSearchArgs,
OrderAndPagination,
SearchData,
useRspcLibraryContext
} from '@sd/client';
import { getExplorerStore } from './store';
import { UseExplorerSettings } from './useExplorer';
import { UseExplorerInfiniteQueryArgs } from './useExplorerInfiniteQuery';
export function useObjectsInfiniteQuery({
library,
arg,
settings,
...args
}: {
library: LibraryConfigWrapped;
arg: ObjectSearchArgs;
settings: UseExplorerSettings<ObjectOrder>;
} & Pick<UseInfiniteQueryOptions<SearchData<ExplorerItem>>, 'enabled'>) {
}: UseExplorerInfiniteQueryArgs<ObjectSearchArgs, ObjectOrder>) {
const ctx = useRspcLibraryContext();
const explorerSettings = settings.useSettingsSnapshot();
@ -35,14 +27,14 @@ export function useObjectsInfiniteQuery({
const cItem: Extract<ExplorerItem, { type: 'Object' }> = pageParam;
const { order } = explorerSettings;
let orderAndPagination: OrderAndPagination<ObjectOrder, ObjectCursor> | undefined;
let orderAndPagination: (typeof arg)['orderAndPagination'];
if (!cItem) {
if (order) orderAndPagination = { orderOnly: order };
} else {
let cursor: ObjectCursor | undefined;
if (!order) cursor = { none: [] };
if (!order) cursor = 'none';
else if (cItem) {
const direction = order.value;
@ -61,7 +53,7 @@ export function useObjectsInfiniteQuery({
}
}
if (cursor) orderAndPagination = { cursor };
if (cursor) orderAndPagination = { cursor: { cursor, id: cItem.item.id } };
}
arg.orderAndPagination = orderAndPagination;
@ -70,7 +62,7 @@ export function useObjectsInfiniteQuery({
},
getNextPageParam: (lastPage) => {
if (lastPage.items.length < arg.take) return undefined;
else return lastPage.items[arg.take];
else return lastPage.items[arg.take - 1];
},
...args
});

View file

@ -1,29 +1,21 @@
import { UseInfiniteQueryOptions, useInfiniteQuery } from '@tanstack/react-query';
import { useInfiniteQuery } from '@tanstack/react-query';
import {
ExplorerItem,
FilePathCursor,
FilePathCursorVariant,
FilePathObjectCursor,
FilePathOrder,
FilePathSearchArgs,
LibraryConfigWrapped,
OrderAndPagination,
SearchData,
useRspcLibraryContext
} from '@sd/client';
import { getExplorerStore } from './store';
import { UseExplorerSettings } from './useExplorer';
import { getExplorerStore } from '../store';
import { UseExplorerInfiniteQueryArgs } from './useExplorerInfiniteQuery';
export function usePathsInfiniteQuery({
library,
arg,
settings,
...args
}: {
library: LibraryConfigWrapped;
arg: FilePathSearchArgs;
settings: UseExplorerSettings<FilePathOrder>;
} & Pick<UseInfiniteQueryOptions<SearchData<ExplorerItem>>, 'enabled'>) {
}: UseExplorerInfiniteQueryArgs<FilePathSearchArgs, FilePathOrder>) {
const ctx = useRspcLibraryContext();
const explorerSettings = settings.useSettingsSnapshot();
@ -37,14 +29,14 @@ export function usePathsInfiniteQuery({
const cItem: Extract<ExplorerItem, { type: 'Path' }> = pageParam;
const { order } = explorerSettings;
let orderAndPagination: OrderAndPagination<FilePathOrder, FilePathCursor> | undefined;
let orderAndPagination: (typeof arg)['orderAndPagination'];
if (!cItem) {
if (order) orderAndPagination = { orderOnly: order };
} else {
let variant: FilePathCursorVariant | undefined;
if (!order) variant = { none: [] };
if (!order) variant = 'none';
else if (cItem) {
switch (order.field) {
case 'name': {
@ -136,7 +128,7 @@ export function usePathsInfiniteQuery({
if (variant)
orderAndPagination = {
cursor: { variant, isDir: cItem.item.is_dir }
cursor: { cursor: { variant, isDir: cItem.item.is_dir }, id: cItem.item.id }
};
}
@ -146,7 +138,7 @@ export function usePathsInfiniteQuery({
},
getNextPageParam: (lastPage) => {
if (lastPage.items.length < arg.take) return undefined;
else return lastPage.items[arg.take];
else return lastPage.items[arg.take - 1];
},
onSuccess: () => getExplorerStore().resetNewThumbnails(),
...args

View file

@ -18,9 +18,9 @@ import { useKeyDeleteFile, useZodRouteParams } from '~/hooks';
import Explorer from '../Explorer';
import { ExplorerContextProvider } from '../Explorer/Context';
import { DefaultTopBarOptions } from '../Explorer/TopBarOptions';
import { usePathsInfiniteQuery } from '../Explorer/queries';
import { createDefaultExplorerSettings, filePathOrderingKeysSchema } from '../Explorer/store';
import { UseExplorerSettings, useExplorer, useExplorerSettings } from '../Explorer/useExplorer';
import { usePathsInfiniteQuery } from '../Explorer/usePathsInfiniteQuery';
import { useExplorerSearchParams } from '../Explorer/util';
import { TopBarPortal } from '../TopBar/Portal';
import LocationOptions from './LocationOptions';

View file

@ -11,14 +11,13 @@ import {
useLibraryQuery,
useRspcLibraryContext
} from '@sd/client';
import { useObjectsInfiniteQuery, usePathsInfiniteQuery } from '../Explorer/queries';
import {
createDefaultExplorerSettings,
filePathOrderingKeysSchema,
objectOrderingKeysSchema
} from '../Explorer/store';
import { useExplorer, useExplorerSettings } from '../Explorer/useExplorer';
import { useObjectsInfiniteQuery } from '../Explorer/useObjectsInfiniteQuery';
import { usePathsInfiniteQuery } from '../Explorer/usePathsInfiniteQuery';
import { usePageLayoutContext } from '../PageLayout/Context';
export const IconForCategory: Partial<Record<Category, string>> = {

View file

@ -168,7 +168,7 @@ export type FilePath = { id: number; pub_id: number[]; is_dir: boolean | null; c
export type FilePathCursor = { isDir: boolean; variant: FilePathCursorVariant }
export type FilePathCursorVariant = { none: number[] } | { name: CursorOrderItem<string> } | { dateCreated: CursorOrderItem<string> } | { dateModified: CursorOrderItem<string> } | { dateIndexed: CursorOrderItem<string> } | { object: FilePathObjectCursor }
export type FilePathCursorVariant = "none" | { name: CursorOrderItem<string> } | { dateCreated: CursorOrderItem<string> } | { dateModified: CursorOrderItem<string> } | { dateIndexed: CursorOrderItem<string> } | { object: FilePathObjectCursor }
export type FilePathFilterArgs = { locationId?: number | null; search?: string | null; extension?: string | null; createdAt?: OptionalRange<string>; path?: string | null; object?: ObjectFilterArgs | null }
@ -176,7 +176,7 @@ export type FilePathObjectCursor = { dateAccessed: CursorOrderItem<string> } | {
export type FilePathOrder = { field: "name"; value: SortOrder } | { field: "sizeInBytes"; value: SortOrder } | { field: "dateCreated"; value: SortOrder } | { field: "dateModified"; value: SortOrder } | { field: "dateIndexed"; value: SortOrder } | { field: "object"; value: ObjectOrder }
export type FilePathSearchArgs = { take: number; orderAndPagination?: OrderAndPagination<FilePathOrder, FilePathCursor> | null; filter?: FilePathFilterArgs; groupDirectories?: boolean }
export type FilePathSearchArgs = { take: number; orderAndPagination?: OrderAndPagination<number, FilePathOrder, FilePathCursor> | null; filter?: FilePathFilterArgs; groupDirectories?: boolean }
export type FilePathWithObject = { id: number; pub_id: number[]; is_dir: boolean | null; cas_id: string | null; integrity_checksum: string | null; location_id: number | null; materialized_path: string | null; name: string | null; extension: string | null; size_in_bytes: string | null; size_in_bytes_bytes: number[] | null; inode: number[] | null; device: number[] | null; object_id: number | null; key_id: number | null; date_created: string | null; date_modified: string | null; date_indexed: string | null; object: Object | null }
@ -307,7 +307,7 @@ export type NotificationId = { type: "library"; id: [string, number] } | { type:
export type Object = { id: number; pub_id: number[]; kind: number | null; key_id: number | null; hidden: boolean | null; favorite: boolean | null; important: boolean | null; note: string | null; date_created: string | null; date_accessed: string | null }
export type ObjectCursor = { none: number[] } | { dateAccessed: CursorOrderItem<string> } | { kind: CursorOrderItem<number> }
export type ObjectCursor = "none" | { dateAccessed: CursorOrderItem<string> } | { kind: CursorOrderItem<number> }
export type ObjectFilterArgs = { favorite?: boolean | null; hidden?: ObjectHiddenFilter; dateAccessed?: MaybeNot<string | null> | null; kind?: number[]; tags?: number[]; category?: Category | null }
@ -315,7 +315,7 @@ export type ObjectHiddenFilter = "exclude" | "include"
export type ObjectOrder = { field: "dateAccessed"; value: SortOrder } | { field: "kind"; value: SortOrder }
export type ObjectSearchArgs = { take: number; orderAndPagination?: OrderAndPagination<ObjectOrder, ObjectCursor> | null; filter?: ObjectFilterArgs }
export type ObjectSearchArgs = { take: number; orderAndPagination?: OrderAndPagination<number, ObjectOrder, ObjectCursor> | null; filter?: ObjectFilterArgs }
export type ObjectValidatorArgs = { id: number; path: string }
@ -329,7 +329,7 @@ export type OperatingSystem = "Windows" | "Linux" | "MacOS" | "Ios" | "Android"
export type OptionalRange<T> = { from: T | null; to: T | null }
export type OrderAndPagination<TOrder, TCursor> = { orderOnly: TOrder } | { offset: { offset: number; order: TOrder | null } } | { cursor: TCursor }
export type OrderAndPagination<TId, TOrder, TCursor> = { orderOnly: TOrder } | { offset: { offset: number; order: TOrder | null } } | { cursor: { id: TId; cursor: TCursor } }
export type Orientation = "Normal" | "MirroredHorizontal" | "CW90" | "MirroredVertical" | "MirroredHorizontalAnd270CW" | "MirroredHorizontalAnd90CW" | "CW180" | "CW270"