[Gekidou - MM-44929] Search Screen - Always fetch filter results from server (#6366)
Co-authored-by: Daniel Espino García <larkox@gmail.com>
This commit is contained in:
parent
45f76cbc50
commit
2230fe8a70
5 changed files with 86 additions and 40 deletions
|
|
@ -35,7 +35,7 @@ const Files: Record<string, string[]> = {
|
|||
PATCH_TYPES: ['patch'],
|
||||
PDF_TYPES: ['pdf'],
|
||||
PRESENTATION_TYPES: ['ppt', 'pptx', 'odp'],
|
||||
SPREADSHEET_TYPES: ['xls, xlsx', 'csv', 'ods'],
|
||||
SPREADSHEET_TYPES: ['xls', 'xlsx', 'csv', 'ods'],
|
||||
TEXT_TYPES: ['txt', 'rtf'],
|
||||
VIDEO_TYPES: ['mp4', 'avi', 'webm', 'mkv', 'wmv', 'mpg', 'mov', 'flv', 'ogm', 'mpeg'],
|
||||
WORD_TYPES: ['doc', 'docx', 'odt'],
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {useIsTablet} from '@hooks/device';
|
|||
import {t} from '@i18n';
|
||||
import BottomSheetContent from '@screens/bottom_sheet/content';
|
||||
import {dismissBottomSheet} from '@screens/navigation';
|
||||
import {FileFilter} from '@utils/file';
|
||||
import {FileFilter, FileFilters} from '@utils/file';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
|
|
@ -43,35 +43,35 @@ const data: FilterItem[] = [
|
|||
{
|
||||
id: t('screen.search.results.filter.all_file_types'),
|
||||
defaultMessage: 'All file types',
|
||||
filterType: 'all',
|
||||
filterType: FileFilters.ALL,
|
||||
}, {
|
||||
id: t('screen.search.results.filter.documents'),
|
||||
defaultMessage: 'Documents',
|
||||
filterType: 'documents',
|
||||
filterType: FileFilters.DOCUMENTS,
|
||||
}, {
|
||||
id: t('screen.search.results.filter.spreadsheets'),
|
||||
defaultMessage: 'Spreadsheets',
|
||||
filterType: 'spreadsheets',
|
||||
filterType: FileFilters.SPREADSHEETS,
|
||||
}, {
|
||||
id: t('screen.search.results.filter.presentations'),
|
||||
defaultMessage: 'Presentations',
|
||||
filterType: 'presentations',
|
||||
filterType: FileFilters.PRESENTATIONS,
|
||||
}, {
|
||||
id: t('screen.search.results.filter.code'),
|
||||
defaultMessage: 'Code',
|
||||
filterType: 'code',
|
||||
filterType: FileFilters.CODE,
|
||||
}, {
|
||||
id: t('screen.search.results.filter.images'),
|
||||
defaultMessage: 'Images',
|
||||
filterType: 'images',
|
||||
filterType: FileFilters.IMAGES,
|
||||
}, {
|
||||
id: t('screen.search.results.filter.audio'),
|
||||
defaultMessage: 'Audio',
|
||||
filterType: 'audio',
|
||||
filterType: FileFilters.AUDIO,
|
||||
}, {
|
||||
id: t('screen.search.results.filter.videos'),
|
||||
defaultMessage: 'Videos',
|
||||
filterType: 'videos',
|
||||
filterType: FileFilters.VIDEOS,
|
||||
separator: false,
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import Badge from '@components/badge';
|
|||
import CompassIcon from '@components/compass_icon';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {bottomSheet} from '@screens/navigation';
|
||||
import {FileFilter} from '@utils/file';
|
||||
import {FileFilter, FileFilters} from '@utils/file';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import Filter from './filter';
|
||||
|
|
@ -68,7 +68,7 @@ const Header = ({
|
|||
const filesText = intl.formatMessage({id: 'screen.search.header.files', defaultMessage: 'Files'});
|
||||
|
||||
const showFilterIcon = selectedTab === 'files';
|
||||
const hasFilters = selectedFilter !== 'all';
|
||||
const hasFilters = selectedFilter !== FileFilters.ALL;
|
||||
|
||||
const handleMessagesPress = useCallback(() => {
|
||||
onTabSelect('messages');
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import NavigationHeader from '@components/navigation_header';
|
|||
import RoundedHeaderContext from '@components/rounded_header_context';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useCollapsibleHeader} from '@hooks/header';
|
||||
import {FileFilter, filterFiles} from '@utils/file';
|
||||
import {FileFilter, FileFilters, filterFileExtensions} from '@utils/file';
|
||||
|
||||
import Results from './results';
|
||||
import Header, {SelectTab} from './results/header';
|
||||
|
|
@ -46,14 +46,23 @@ const SearchScreen = ({teamId}: Props) => {
|
|||
|
||||
const [searchValue, setSearchValue] = useState<string>(searchTerm);
|
||||
const [selectedTab, setSelectedTab] = useState<SelectTab>('messages');
|
||||
const [filter, setFilter] = useState<FileFilter>('all');
|
||||
const [filter, setFilter] = useState<FileFilter>(FileFilters.ALL);
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [lastSearchedValue, setLastSearchedValue] = useState('');
|
||||
|
||||
const [postIds, setPostIds] = useState<string[]>(emptyPostResults);
|
||||
const [fileInfos, setFileInfos] = useState<FileInfo[]>(emptyFileResults);
|
||||
const [filteredFileInfos, setFilteredFileInfos] = useState<FileInfo[]>(emptyFileResults);
|
||||
|
||||
const getSearchParams = useCallback((filterValue?: FileFilter) => {
|
||||
const terms = filterValue ? lastSearchedValue : searchValue;
|
||||
const fileExtensions = filterFileExtensions(filterValue || filter);
|
||||
const extensionTerms = fileExtensions ? ' ' + fileExtensions : '';
|
||||
return {
|
||||
terms: terms + extensionTerms,
|
||||
is_or_search: true,
|
||||
};
|
||||
}, [filter, lastSearchedValue, searchValue]);
|
||||
|
||||
const handleSearch = useCallback((debounce(async () => {
|
||||
// execute the search for the text in the navigation text box
|
||||
|
|
@ -62,13 +71,9 @@ const SearchScreen = ({teamId}: Props) => {
|
|||
// - updated recent createdAt if exists??
|
||||
|
||||
setLoading(true);
|
||||
setFilter(FileFilters.ALL);
|
||||
setLastSearchedValue(searchValue);
|
||||
|
||||
const searchParams: PostSearchParams | FileSearchParams = {
|
||||
terms: searchValue,
|
||||
is_or_search: true,
|
||||
};
|
||||
|
||||
const searchParams = getSearchParams();
|
||||
const [postResults, fileResults] = await Promise.all([
|
||||
searchPosts(serverUrl, searchParams),
|
||||
searchFiles(serverUrl, teamId, searchParams),
|
||||
|
|
@ -85,14 +90,21 @@ const SearchScreen = ({teamId}: Props) => {
|
|||
scrollRef.current?.scrollToOffset({offset, animated: true});
|
||||
};
|
||||
|
||||
const handleFilterChange = useCallback(async (filterValue: FileFilter) => {
|
||||
setLoading(true);
|
||||
setFilter(filterValue);
|
||||
const searchParams = getSearchParams(filterValue);
|
||||
const fileResults = await searchFiles(serverUrl, teamId, searchParams);
|
||||
const fileInfosResult = fileResults?.file_infos && Object.values(fileResults?.file_infos);
|
||||
setFileInfos(fileInfosResult?.length ? fileInfosResult : emptyFileResults);
|
||||
|
||||
setLoading(false);
|
||||
}, [lastSearchedValue]);
|
||||
|
||||
useEffect(() => {
|
||||
setSearchValue(searchTerm);
|
||||
}, [searchTerm]);
|
||||
|
||||
useEffect(() => {
|
||||
setFilteredFileInfos(filterFiles(fileInfos, filter));
|
||||
}, [filter, fileInfos]);
|
||||
|
||||
const {scrollPaddingTop, scrollRef, scrollValue, onScroll, headerHeight, hideHeader} = useCollapsibleHeader<FlatList>(true, onSnap);
|
||||
|
||||
const animated = useAnimatedStyle(() => {
|
||||
|
|
@ -118,15 +130,21 @@ const SearchScreen = ({teamId}: Props) => {
|
|||
};
|
||||
}, [headerHeight, lastSearchedValue]);
|
||||
|
||||
const onClear = useCallback(() => {
|
||||
setSearchValue('');
|
||||
setLastSearchedValue('');
|
||||
setFilter(FileFilters.ALL);
|
||||
}, [filter]);
|
||||
|
||||
let header = null;
|
||||
if (lastSearchedValue) {
|
||||
header = (
|
||||
<Header
|
||||
onTabSelect={setSelectedTab}
|
||||
onFilterChanged={setFilter}
|
||||
onFilterChanged={handleFilterChange}
|
||||
numberMessages={postIds.length}
|
||||
selectedTab={selectedTab}
|
||||
numberFiles={Object.keys(filteredFileInfos).length}
|
||||
numberFiles={fileInfos.length}
|
||||
selectedFilter={filter}
|
||||
/>
|
||||
);
|
||||
|
|
@ -146,6 +164,7 @@ const SearchScreen = ({teamId}: Props) => {
|
|||
blurOnSubmit={true}
|
||||
placeholder={intl.formatMessage({id: 'screen.search.placeholder', defaultMessage: 'Search messages & files'})}
|
||||
defaultValue={searchValue}
|
||||
onClear={onClear}
|
||||
/>
|
||||
<SafeAreaView
|
||||
style={styles.flex}
|
||||
|
|
@ -160,7 +179,7 @@ const SearchScreen = ({teamId}: Props) => {
|
|||
selectedTab={selectedTab}
|
||||
searchValue={lastSearchedValue}
|
||||
postIds={postIds}
|
||||
fileInfos={filteredFileInfos}
|
||||
fileInfos={fileInfos}
|
||||
scrollRef={scrollRef}
|
||||
onScroll={onScroll}
|
||||
scrollPaddingTop={scrollPaddingTop}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import Permissions, {PERMISSIONS} from 'react-native-permissions';
|
|||
|
||||
import {Files} from '@constants';
|
||||
import {generateId} from '@utils/general';
|
||||
import keyMirror from '@utils/key_mirror';
|
||||
import {deleteEntititesFile, getIOSAppGroupDetails} from '@utils/mattermost_managed';
|
||||
import {hashCode} from '@utils/security';
|
||||
|
||||
|
|
@ -24,7 +25,17 @@ const EXTRACT_TYPE_REGEXP = /^\s*([^;\s]*)(?:;|\s|$)/;
|
|||
const CONTENT_DISPOSITION_REGEXP = /inline;filename=".*\.([a-z]+)";/i;
|
||||
const DEFAULT_SERVER_MAX_FILE_SIZE = 50 * 1024 * 1024;// 50 Mb
|
||||
|
||||
export type FileFilter = 'all' | 'documents' | 'spreadsheets'| 'presentations' | 'code' | 'images' | 'audio' | 'videos'
|
||||
export const FileFilters = keyMirror({
|
||||
ALL: null,
|
||||
DOCUMENTS: null,
|
||||
SPREADSHEETS: null,
|
||||
PRESENTATIONS: null,
|
||||
CODE: null,
|
||||
IMAGES: null,
|
||||
AUDIO: null,
|
||||
VIDEOS: null,
|
||||
});
|
||||
export type FileFilter = keyof typeof FileFilters
|
||||
|
||||
export const GENERAL_SUPPORTED_DOCS_FORMAT = [
|
||||
'application/json',
|
||||
|
|
@ -60,20 +71,36 @@ const SUPPORTED_VIDEO_FORMAT = Platform.select({
|
|||
const types: Record<string, string> = {};
|
||||
const extensions: Record<string, readonly string[]> = {};
|
||||
|
||||
export function filterFiles<T extends FileModel | FileInfo>(files: T[], filter: FileFilter) {
|
||||
export function filterFileExtensions(filter: FileFilter): string {
|
||||
let searchTerms: string[] = [];
|
||||
switch (filter) {
|
||||
case 'all':
|
||||
return files;
|
||||
case 'videos':
|
||||
return files.filter((f) => isVideo(f));
|
||||
case 'documents':
|
||||
return files.filter((f) => isDocument(f));
|
||||
case 'images':
|
||||
return files.filter((f) => isImage(f));
|
||||
case FileFilters.ALL:
|
||||
return '';
|
||||
case FileFilters.DOCUMENTS:
|
||||
searchTerms = Files.DOCUMENT_TYPES;
|
||||
break;
|
||||
case FileFilters.SPREADSHEETS:
|
||||
searchTerms = Files.SPREADSHEET_TYPES;
|
||||
break;
|
||||
case FileFilters.PRESENTATIONS:
|
||||
searchTerms = Files.PRESENTATION_TYPES;
|
||||
break;
|
||||
case FileFilters.CODE:
|
||||
searchTerms = Files.CODE_TYPES;
|
||||
break;
|
||||
case FileFilters.IMAGES:
|
||||
searchTerms = Files.IMAGE_TYPES;
|
||||
break;
|
||||
case FileFilters.AUDIO:
|
||||
searchTerms = Files.AUDIO_TYPES;
|
||||
break;
|
||||
case FileFilters.VIDEOS:
|
||||
searchTerms = Files.VIDEO_TYPES;
|
||||
break;
|
||||
default:
|
||||
// TODO create the rest of the filters
|
||||
return files.filter((f) => !isVideo(f) && !isDocument(f) && !isImage(f));
|
||||
return '';
|
||||
}
|
||||
return 'ext:' + searchTerms.join(' ext:');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue