import React, { forwardRef, ForwardRefRenderFunction, useEffect, useImperativeHandle, useRef, useState, } from 'react'; import { useTranslation } from 'react-i18next'; import { ISelectableAll } from '~/client/interfaces/selectable-all'; import AppContainer from '~/client/services/AppContainer'; import { toastSuccess } from '~/client/util/apiNotification'; import { IFormattedSearchResult, IPageWithSearchMeta } from '~/interfaces/search'; import { OnDeletedFunction } from '~/interfaces/ui'; import { useIsGuestUser, useIsSearchServiceConfigured, useIsSearchServiceReachable } from '~/stores/context'; import { usePageDeleteModal } from '~/stores/modal'; import { usePageTreeTermManager } from '~/stores/page-listing'; import { ForceHideMenuItems } from '../Common/Dropdown/PageItemControl'; import { SearchResultContent } from '../SearchPage/SearchResultContent'; import { SearchResultList } from '../SearchPage/SearchResultList'; // https://regex101.com/r/brrkBu/1 const highlightKeywordsSplitter = new RegExp('"[^"]+"|[^\u{20}\u{3000}]+', 'ug'); export interface IReturnSelectedPageIds { getSelectedPageIds?: () => Set, } type Props = { appContainer: AppContainer, pages?: IPageWithSearchMeta[], searchingKeyword?: string, forceHideMenuItems?: ForceHideMenuItems, onSelectedPagesByCheckboxesChanged?: (selectedCount: number, totalCount: number) => void, searchControl: React.ReactNode, searchResultListHead: React.ReactElement, searchPager: React.ReactNode, } const SearchPageBaseSubstance: ForwardRefRenderFunction = (props:Props, ref) => { const { appContainer, pages, searchingKeyword, forceHideMenuItems, onSelectedPagesByCheckboxesChanged, searchControl, searchResultListHead, searchPager, } = props; const searchResultListRef = useRef(null); const { data: isGuestUser } = useIsGuestUser(); const { data: isSearchServiceConfigured } = useIsSearchServiceConfigured(); const { data: isSearchServiceReachable } = useIsSearchServiceReachable(); const [selectedPageIdsByCheckboxes] = useState>(new Set()); // const [allPageIds] = useState>(new Set()); const [selectedPageWithMeta, setSelectedPageWithMeta] = useState(); // publish selectAll() useImperativeHandle(ref, () => ({ selectAll: () => { const instance = searchResultListRef.current; if (instance != null) { instance.selectAll(); } if (pages != null) { pages.forEach(page => selectedPageIdsByCheckboxes.add(page.data._id)); } }, deselectAll: () => { const instance = searchResultListRef.current; if (instance != null) { instance.deselectAll(); } selectedPageIdsByCheckboxes.clear(); }, getSelectedPageIds: () => { return selectedPageIdsByCheckboxes; }, })); const checkboxChangedHandler = (isChecked: boolean, pageId: string) => { if (pages == null || pages.length === 0) { return; } if (isChecked) { selectedPageIdsByCheckboxes.add(pageId); } else { selectedPageIdsByCheckboxes.delete(pageId); } if (onSelectedPagesByCheckboxesChanged != null) { onSelectedPagesByCheckboxesChanged(selectedPageIdsByCheckboxes.size, pages.length); } }; // select first item on load useEffect(() => { if (selectedPageWithMeta == null && pages != null && pages.length > 0) { setSelectedPageWithMeta(pages[0]); } }, [pages, selectedPageWithMeta]); // reset selectedPageIdsByCheckboxes useEffect(() => { if (pages == null) { return; } if (pages.length > 0) { selectedPageIdsByCheckboxes.clear(); } if (onSelectedPagesByCheckboxesChanged != null) { onSelectedPagesByCheckboxesChanged(selectedPageIdsByCheckboxes.size, pages.length); } }, [onSelectedPagesByCheckboxesChanged, pages, selectedPageIdsByCheckboxes]); if (!isSearchServiceConfigured) { return (

Search service is not configured in this system.

); } if (!isSearchServiceReachable) { return (

Search service occures errors. Please contact to administrators of this system.

); } const highlightKeywords = searchingKeyword != null ? searchingKeyword.match(highlightKeywordsSplitter) ?? undefined : undefined; return (
{searchControl}
{/* Loading */} { pages == null && (
) } {/* Loaded */} { pages != null && ( <>
{searchResultListHead}
{ pages.length > 0 && (
setSelectedPageWithMeta(page)} onCheckboxChanged={checkboxChangedHandler} />
) }
{searchPager}
) }
{ selectedPageWithMeta != null && ( )}
); }; type VoidFunction = () => void; export const usePageDeleteModalForBulkDeletion = ( data: IFormattedSearchResult | undefined, ref: React.MutableRefObject<(ISelectableAll & IReturnSelectedPageIds) | null>, onDeleted?: OnDeletedFunction, ): VoidFunction => { const { t } = useTranslation(); const { open: openDeleteModal } = usePageDeleteModal(); // for PageTree mutation const { advance: advancePt } = usePageTreeTermManager(); return () => { if (data == null) { return; } const instance = ref.current; if (instance == null || instance.getSelectedPageIds == null) { return; } const selectedPageIds = instance.getSelectedPageIds(); if (selectedPageIds.size === 0) { return; } const selectedPages = data.data .filter(pageWithMeta => selectedPageIds.has(pageWithMeta.data._id)); openDeleteModal(selectedPages, { onDeleted: (...args) => { const path = args[0]; const isCompletely = args[2]; if (path == null || isCompletely == null) { toastSuccess(t('deleted_page')); } else { toastSuccess(t('deleted_pages_completely', { path })); } advancePt(); if (onDeleted != null) { onDeleted(...args); } }, }); }; }; export const SearchPageBase = forwardRef(SearchPageBaseSubstance);