import React, {
useCallback, useEffect, useMemo, useRef, useState,
} from 'react';
import { useTranslation } from 'next-i18next';
import { useRouter } from 'next/router';
import { ISelectableAll, ISelectableAndIndeterminatable } from '~/client/interfaces/selectable-all';
import { IFormattedSearchResult } from '~/interfaces/search';
import { useIsSearchServiceReachable, useShowPageLimitationL } from '~/stores/context';
import { ISearchConditions, ISearchConfigurations, useSWRxSearch } from '~/stores/search';
import { NotAvailableForGuest } from './NotAvailableForGuest';
import { NotAvailableForReadOnlyUser } from './NotAvailableForReadOnlyUser';
import PaginationWrapper from './PaginationWrapper';
import { OperateAllControl } from './SearchPage/OperateAllControl';
import SearchControl from './SearchPage/SearchControl';
import { IReturnSelectedPageIds, SearchPageBase, usePageDeleteModalForBulkDeletion } from './SearchPage/SearchPageBase';
// TODO: replace with "customize:showPageLimitationS"
const INITIAL_PAGIONG_SIZE = 20;
/**
* SearchResultListHead
*/
type SearchResultListHeadProps = {
searchResult: IFormattedSearchResult,
searchingKeyword: string,
offset: number,
pagingSize: number,
onPagingSizeChanged: (size: number) => void,
}
const SearchResultListHead = React.memo((props: SearchResultListHeadProps): JSX.Element => {
const { t } = useTranslation();
const {
searchResult, searchingKeyword, offset, pagingSize,
onPagingSizeChanged,
} = props;
const { took, total, hitsCount } = searchResult.meta;
const leftNum = offset + 1;
const rightNum = offset + hitsCount;
if (total === 0) {
return (
0 {t('search_result.page_number_unit')}
);
}
return (
{t('search_result.result_meta')}
{`${searchingKeyword}`}
{`${leftNum}-${rightNum}`} / {total}
{ took != null && (
// blackout 70px rectangle in VRT
({took}ms)
) }
);
});
SearchResultListHead.displayName = 'SearchResultListHead';
export const SearchPage = (): JSX.Element => {
const { t } = useTranslation();
const { data: showPageLimitationL } = useShowPageLimitationL();
const router = useRouter();
// parse URL Query
const queries = router.query.q;
const initQ = (Array.isArray(queries) ? queries.join(' ') : queries) ?? '';
const [keyword, setKeyword] = useState(initQ);
const [offset, setOffset] = useState(0);
const [limit, setLimit] = useState(showPageLimitationL ?? INITIAL_PAGIONG_SIZE);
const [configurationsByControl, setConfigurationsByControl] = useState>({});
const selectAllControlRef = useRef(null);
const searchPageBaseRef = useRef(null);
const { data: isSearchServiceReachable } = useIsSearchServiceReachable();
const { data, conditions, mutate } = useSWRxSearch(keyword, null, {
...configurationsByControl,
offset,
limit,
});
const searchInvokedHandler = useCallback((_keyword: string, newConfigurations: Partial) => {
setKeyword(_keyword);
setOffset(0);
setConfigurationsByControl(newConfigurations);
}, []);
const selectAllCheckboxChangedHandler = useCallback((isChecked: boolean) => {
const instance = searchPageBaseRef.current;
if (instance == null) {
return;
}
if (isChecked) {
instance.selectAll();
}
else {
instance.deselectAll();
}
}, []);
const selectedPagesByCheckboxesChangedHandler = useCallback((selectedCount: number, totalCount: number) => {
const instance = selectAllControlRef.current;
if (instance == null) {
return;
}
if (selectedCount === 0) {
instance.deselect();
}
else if (selectedCount === totalCount) {
instance.select();
}
else {
instance.setIndeterminate();
}
}, []);
const pagingSizeChangedHandler = useCallback((pagingSize: number) => {
setOffset(0);
setLimit(pagingSize);
mutate();
}, [mutate]);
const pagingNumberChangedHandler = useCallback((activePage: number) => {
setOffset((activePage - 1) * limit);
mutate();
}, [limit, mutate]);
const initialSearchConditions: Partial = useMemo(() => {
return {
keyword: initQ,
limit: INITIAL_PAGIONG_SIZE,
};
}, [initQ]);
// for bulk deletion
const deleteAllButtonClickedHandler = usePageDeleteModalForBulkDeletion(data, searchPageBaseRef, () => mutate());
// push state
useEffect(() => {
const newUrl = new URL('/_search', 'http://example.com');
newUrl.searchParams.append('q', keyword);
window.history.pushState('', `Search - ${keyword}`, `${newUrl.pathname}${newUrl.search}`);
}, [keyword]);
const hitsCount = data?.meta.hitsCount;
const allControl = useMemo(() => {
const isDisabled = hitsCount === 0;
return (
);
}, [deleteAllButtonClickedHandler, hitsCount, selectAllCheckboxChangedHandler, t]);
const searchControl = useMemo(() => {
if (!isSearchServiceReachable) {
return <>>;
}
return (
);
}, [allControl, initialSearchConditions, isSearchServiceReachable, searchInvokedHandler]);
const searchResultListHead = useMemo(() => {
if (data == null) {
return <>>;
}
return (
);
}, [data, keyword, limit, offset, pagingSizeChangedHandler]);
const searchPager = useMemo(() => {
// when pager is not needed
if (data == null || data.meta.hitsCount === data.meta.total) {
return <>>;
}
const { total } = data.meta;
const { offset, limit } = conditions;
return (
);
}, [conditions, data, pagingNumberChangedHandler]);
return (
);
};