Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add global search #785

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion src/api/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
} from '../types.js';
import { verifyAuthentication } from './axios.js';
import {
AccessibleItemSearchParams,
GET_OWN_ITEMS_ROUTE,
GET_RECYCLED_ITEMS_DATA_ROUTE,
ItemChildrenParams,
Expand All @@ -38,6 +39,7 @@ import {
buildPostItemWithThumbnailRoute,
buildRecycleItemsRoute,
buildRestoreItemsRoute,
buildSearchItems,
} from './routes.js';

export const getItem = (
Expand Down Expand Up @@ -67,7 +69,7 @@ export const getOwnItems = async ({
);

export const getAccessibleItems = async (
params: ItemSearchParams,
params: AccessibleItemSearchParams,
pagination: PaginationParams,
{ API_HOST, axios }: PartialQueryConfigForApi,
) =>
Expand All @@ -79,6 +81,19 @@ export const getAccessibleItems = async (
.then(({ data }) => data),
);

export const searchItems = async (
params: ItemSearchParams,
pagination: PaginationParams,
{ API_HOST, axios }: PartialQueryConfigForApi,
) =>
verifyAuthentication(() =>
axios
.get<
Paginated<DiscriminatedItem>
>(`${API_HOST}/${buildSearchItems(params, pagination)}`)
.then(({ data }) => data),
);

export type PostItemPayloadType = Partial<DiscriminatedItem> &
Pick<DiscriminatedItem, 'type' | 'name'> &
Partial<{
Expand Down
33 changes: 31 additions & 2 deletions src/api/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const SHORT_LINKS_ROUTE = `${ITEMS_ROUTE}/short-links`;
export const SHORT_LINKS_LIST_ROUTE = `${SHORT_LINKS_ROUTE}/list`;
export const EMBEDDED_LINKS_ROUTE = `${ITEMS_ROUTE}/embedded-links/metadata`;

export type ItemSearchParams =
export type AccessibleItemSearchParams =
| {
creatorId?: Member['id'];
name?: string;
Expand All @@ -54,13 +54,30 @@ export type ItemSearchParams =
}
| undefined;

export type ItemSearchParams =
| {
creatorId?: Member['id'];
keywords?: string[];
ordering?: 'desc' | 'asc';
sortBy?:
| 'rank'
| 'item.name'
| 'item.type'
| 'item.creator.name'
| 'item.created_at'
| 'item.updated_at';
permissions?: PermissionLevel[];
types?: UnionOfConst<typeof ItemType>[];
}
| undefined;

export type ItemChildrenParams = {
ordered?: boolean;
types?: UnionOfConst<typeof ItemType>[];
};

export const buildGetAccessibleItems = (
params: ItemSearchParams,
params: AccessibleItemSearchParams,
pagination: PaginationParams,
) =>
`${ITEMS_ROUTE}/accessible${qs.stringify(
Expand All @@ -71,6 +88,18 @@ export const buildGetAccessibleItems = (
},
)}`;

export const buildSearchItems = (
params: ItemSearchParams,
pagination: PaginationParams,
) =>
`${ITEMS_ROUTE}/search${qs.stringify(
{ ...params, ...pagination },
{
arrayFormat: 'repeat',
addQueryPrefix: true,
},
)}`;

export const buildPostItemRoute = (parentId?: UUID) => {
let url = ITEMS_ROUTE;
if (parentId) {
Expand Down
11 changes: 9 additions & 2 deletions src/config/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,10 @@ export const itemKeys = {
// shared items
shared: () => [...itemKeys.all, 'shared'] as const,

search: (args: {
search: (args: ItemSearchParams) =>
[...itemKeys.all, 'search', args] as const,

publishedSearch: (args: {
query?: string;
categories?: Category['id'][][];
isPublishedRoot?: boolean;
Expand All @@ -151,7 +154,11 @@ export const itemKeys = {
highlightPostTag?: string;
page?: number;
}) =>
[...itemKeys.all, 'search', { isPublishedRoot: false, ...args }] as const,
[
...itemKeys.all,
'publishedSearch',
{ isPublishedRoot: false, ...args },
] as const,

published: () => {
const publishedBaseKey = [...itemKeys.all, 'collections'] as const;
Expand Down
26 changes: 24 additions & 2 deletions src/hooks/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ import {

import { splitRequestByIdsAndReturn } from '../api/axios.js';
import * as Api from '../api/item.js';
import { ItemChildrenParams, ItemSearchParams } from '../api/routes.js';
import {
AccessibleItemSearchParams,
ItemChildrenParams,
ItemSearchParams,
} from '../api/routes.js';
import {
CONSTANT_KEY_STALE_TIME_MILLISECONDS,
DEFAULT_THUMBNAIL_SIZE,
Expand Down Expand Up @@ -51,7 +55,7 @@ const config = (
* @returns
*/
useAccessibleItems: (
params?: ItemSearchParams,
params?: AccessibleItemSearchParams,
pagination?: PaginationParams,
) => {
const queryClient = useQueryClient();
Expand Down Expand Up @@ -292,6 +296,24 @@ const config = (
});
},

useSearchItems: (
args: ItemSearchParams,
pagination: PaginationParams,
options: { enabled?: boolean } = { enabled: true },
) =>
useInfiniteQuery({
queryKey: itemKeys.search(args),
queryFn: ({ pageParam }) =>
Api.searchItems(
args,
{ page: pageParam ?? 1, ...pagination },
queryConfig,
),
getNextPageParam: (_lastPage, pages) => pages.length + 1,
enabled: options.enabled,
refetchOnWindowFocus: () => false,
}),

/**
* @deprecated use url alternative when possible
* @param id itemId to download content from
Expand Down
10 changes: 5 additions & 5 deletions src/hooks/search.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ describe('Published Search Hook', () => {
const categories = [['mycategoryid']];
const hook = () =>
hooks.useSearchPublishedItems({ query, categories, page: 1 });
const key = itemKeys.search({
const key = itemKeys.publishedSearch({
query,
categories,
page: 1,
Expand All @@ -127,7 +127,7 @@ describe('Published Search Hook', () => {

it(`does not fetch if no query nor categories is provided`, async () => {
const hook = () => hooks.useSearchPublishedItems({});
const key = itemKeys.search({ page: 1 });
const key = itemKeys.publishedSearch({ page: 1 });
const endpoints = [{ route, response: RESPONSE }];
const { data, isFetched } = await mockHook({
endpoints,
Expand All @@ -148,7 +148,7 @@ describe('Published Search Hook', () => {
const isPublishedRoot = true;

const spy = vi.spyOn(axios, 'post');
const key = itemKeys.search({
const key = itemKeys.publishedSearch({
isPublishedRoot,
query,
categories,
Expand Down Expand Up @@ -199,7 +199,7 @@ describe('Published Search Hook', () => {
const isPublishedRoot = true;
const page = 3;
const spy = vi.spyOn(axios, 'post');
const key = itemKeys.search({
const key = itemKeys.publishedSearch({
isPublishedRoot,
query,
categories,
Expand Down Expand Up @@ -248,7 +248,7 @@ describe('Published Search Hook', () => {
const query = 'some string';
const categories = [['mycategoryid']];
const isPublishedRoot = true;
const key = itemKeys.search({
const key = itemKeys.publishedSearch({
isPublishedRoot,
query,
categories,
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default (queryConfig: QueryClientConfig) => {
} & Api.MeiliSearchProps) => {
const debouncedQuery = useDebounce(query, 500);
return useQuery({
queryKey: itemKeys.search({
queryKey: itemKeys.publishedSearch({
query: debouncedQuery,
categories,
isPublishedRoot,
Expand Down