cleanup
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user