239 lines
6.8 KiB
JavaScript
239 lines
6.8 KiB
JavaScript
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
import {
|
|
TAG_FILTER_UNTAGGED,
|
|
resolveDocumentRowKey,
|
|
isDocumentRowKey,
|
|
} from './appLayoutUtils';
|
|
|
|
const useDocumentsSearch = ({
|
|
api,
|
|
assetManager,
|
|
token,
|
|
selectedFolder,
|
|
navigate,
|
|
locationPathname,
|
|
isDocumentsRoute,
|
|
selectionHelpers,
|
|
searchIncludeDescendants,
|
|
documentsSortField,
|
|
documentsSortDirection,
|
|
notifyApiError,
|
|
setLoading,
|
|
setSearchIncludeDescendants,
|
|
}) => {
|
|
const [searchQuery, setSearchQuery] = useState('');
|
|
const [activeTagFilters, setActiveTagFilters] = useState([]);
|
|
const [activeCorrespondentFilters, setActiveCorrespondentFilters] = useState([]);
|
|
const [searchResults, setSearchResults] = useState(null);
|
|
const [searchLoading, setSearchLoading] = useState(false);
|
|
|
|
const toggleTagFilter = useCallback((tagId) => {
|
|
if (!tagId) return;
|
|
setActiveTagFilters((previous) => {
|
|
if (tagId === TAG_FILTER_UNTAGGED) {
|
|
return previous.includes(TAG_FILTER_UNTAGGED) ? [] : [TAG_FILTER_UNTAGGED];
|
|
}
|
|
const sanitized = previous.filter((id) => id !== TAG_FILTER_UNTAGGED);
|
|
if (sanitized.includes(tagId)) {
|
|
return sanitized.filter((id) => id !== tagId);
|
|
}
|
|
return sanitized.concat([tagId]);
|
|
});
|
|
}, []);
|
|
|
|
const toggleCorrespondentFilter = useCallback((correspondentId) => {
|
|
setActiveCorrespondentFilters((previous) => {
|
|
if (!correspondentId) {
|
|
return [];
|
|
}
|
|
return previous.includes(correspondentId) ? [] : [correspondentId];
|
|
});
|
|
}, []);
|
|
|
|
const isFilterActive = useMemo(
|
|
() =>
|
|
searchQuery.trim().length > 0
|
|
|| activeTagFilters.length > 0
|
|
|| activeCorrespondentFilters.length > 0,
|
|
[searchQuery, activeTagFilters, activeCorrespondentFilters],
|
|
);
|
|
|
|
const clearFilters = useCallback(() => {
|
|
setSearchQuery('');
|
|
setActiveTagFilters([]);
|
|
setActiveCorrespondentFilters([]);
|
|
setSearchLoading(false);
|
|
setSearchIncludeDescendants(true);
|
|
}, [
|
|
setSearchIncludeDescendants,
|
|
]);
|
|
|
|
const handleSearchChange = useCallback((value) => {
|
|
setSearchQuery(value);
|
|
}, []);
|
|
|
|
const handleSearchSubmit = useCallback(() => {
|
|
if (!navigate) return;
|
|
const targetFolder = selectedFolder && selectedFolder !== 'root' ? selectedFolder : 'root';
|
|
const targetPath = targetFolder === 'root' ? '/documents' : `/documents/folder/${targetFolder}`;
|
|
if (!isDocumentsRoute || locationPathname !== targetPath) {
|
|
navigate(targetPath, { replace: false });
|
|
}
|
|
}, [navigate, selectedFolder, isDocumentsRoute, locationPathname]);
|
|
|
|
useEffect(() => {
|
|
if (!token) return undefined;
|
|
|
|
if (!isFilterActive) {
|
|
setSearchResults(null);
|
|
setSearchLoading(false);
|
|
return undefined;
|
|
}
|
|
|
|
let cancelled = false;
|
|
let started = false;
|
|
setSearchLoading(true);
|
|
|
|
const debounce = setTimeout(async () => {
|
|
started = true;
|
|
setLoading(true);
|
|
try {
|
|
const params = {};
|
|
const trimmedQuery = searchQuery.trim();
|
|
if (trimmedQuery.length) {
|
|
params.query = trimmedQuery;
|
|
}
|
|
if (activeTagFilters.length) {
|
|
const onlyUntagged = activeTagFilters.length === 1
|
|
&& activeTagFilters[0] === TAG_FILTER_UNTAGGED;
|
|
if (onlyUntagged) {
|
|
params.tags = 'none';
|
|
} else {
|
|
const tagIds = activeTagFilters.filter((id) => id !== TAG_FILTER_UNTAGGED);
|
|
if (tagIds.length) {
|
|
params.tags = tagIds.join(',');
|
|
}
|
|
}
|
|
}
|
|
if (activeCorrespondentFilters.length) {
|
|
params.correspondents = activeCorrespondentFilters.join(',');
|
|
}
|
|
const folderIdentifier = selectedFolder === 'root' ? null : selectedFolder;
|
|
if (folderIdentifier) {
|
|
params.folder_id = folderIdentifier;
|
|
}
|
|
if (!searchIncludeDescendants) {
|
|
params.include_descendants = false;
|
|
}
|
|
if (documentsSortField) {
|
|
params.sort = documentsSortField;
|
|
}
|
|
if (documentsSortDirection) {
|
|
params.dir = documentsSortDirection;
|
|
}
|
|
const { data } = await api.get('/documents', { params });
|
|
if (cancelled) return;
|
|
|
|
const results = assetManager.hydrateDocuments(data || []);
|
|
setSearchResults(results);
|
|
|
|
if (!results.length) {
|
|
setSearchLoading(false);
|
|
selectionHelpers.setSelectedEntries([]);
|
|
selectionHelpers.setFocusedDocumentId(null);
|
|
selectionHelpers.selectionOrderRef.current = [];
|
|
selectionHelpers.setSelectionOrder([]);
|
|
selectionHelpers.selectionAnchorRef.current = null;
|
|
return;
|
|
}
|
|
|
|
const resultKeys = results
|
|
.map((doc) => resolveDocumentRowKey(doc.id))
|
|
.filter(Boolean);
|
|
|
|
let targetKey = null;
|
|
let nextSelectionKeys = [];
|
|
|
|
selectionHelpers.setSelectedEntries((previous) => {
|
|
const previousDocKeys = previous.filter(isDocumentRowKey);
|
|
const filtered = previousDocKeys.filter((key) => resultKeys.includes(key));
|
|
if (filtered.length) {
|
|
targetKey = filtered[filtered.length - 1];
|
|
nextSelectionKeys = filtered;
|
|
return filtered;
|
|
}
|
|
targetKey = null;
|
|
nextSelectionKeys = [];
|
|
return [];
|
|
});
|
|
|
|
selectionHelpers.selectionOrderRef.current = nextSelectionKeys;
|
|
selectionHelpers.setSelectionOrder(nextSelectionKeys);
|
|
|
|
selectionHelpers.setFocusedDocumentId((previous) => {
|
|
if (previous && resultKeys.includes(resolveDocumentRowKey(previous))) {
|
|
return previous;
|
|
}
|
|
return null;
|
|
});
|
|
|
|
selectionHelpers.selectionAnchorRef.current = targetKey;
|
|
} catch (error) {
|
|
if (cancelled) return;
|
|
notifyApiError(error, 'Search failed. Please try again.');
|
|
setSearchResults(null);
|
|
} finally {
|
|
if (!cancelled && started) {
|
|
setLoading(false);
|
|
setSearchLoading(false);
|
|
}
|
|
}
|
|
}, 300);
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
clearTimeout(debounce);
|
|
if (started) {
|
|
setLoading(false);
|
|
setSearchLoading(false);
|
|
}
|
|
};
|
|
}, [
|
|
api,
|
|
token,
|
|
isFilterActive,
|
|
searchQuery,
|
|
activeTagFilters,
|
|
activeCorrespondentFilters,
|
|
searchIncludeDescendants,
|
|
documentsSortField,
|
|
documentsSortDirection,
|
|
selectedFolder,
|
|
notifyApiError,
|
|
assetManager,
|
|
selectionHelpers,
|
|
setLoading,
|
|
]);
|
|
|
|
return {
|
|
searchQuery,
|
|
setSearchQuery,
|
|
searchResults,
|
|
setSearchResults,
|
|
searchLoading,
|
|
setSearchLoading,
|
|
activeTagFilters,
|
|
setActiveTagFilters,
|
|
activeCorrespondentFilters,
|
|
setActiveCorrespondentFilters,
|
|
toggleTagFilter,
|
|
toggleCorrespondentFilter,
|
|
isFilterActive,
|
|
clearFilters,
|
|
handleSearchChange,
|
|
handleSearchSubmit,
|
|
};
|
|
};
|
|
|
|
export default useDocumentsSearch;
|