mattermost-mobile/app/components/files_search/file_results.tsx
Lucas Reis 722871d165
[MM-48225] Reduce Padding/Margins from Files Tab and moving the Search Results to the FlatList (#7430)
* feat: reducing the padding/margins of tabs and filter icons on the Search Page

* feat: adds the results text inside the post_results and file_results

* feat: changes search results to use translation

* feat: updates text on search results to use FormattedText

* feat: removing text wrapper

* feat: removing margins and using an ItemSeparator to separate the rows of the file results

* feat: refactor spacing on post results and unnecessary top

* review: externalizing Separator on the file_results

* feat: add marginTop in all File components used inside the <Files> component
2023-07-25 10:11:37 +02:00

195 lines
6.7 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useCallback, useMemo, useState} from 'react';
import {FlatList, type ListRenderItemInfo, type StyleProp, type ViewStyle, View} from 'react-native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import NoResults from '@components/files_search/no_results';
import FormattedText from '@components/formatted_text';
import NoResultsWithTerm from '@components/no_results_with_term';
import {useTheme} from '@context/theme';
import {useIsTablet} from '@hooks/device';
import {useImageAttachments} from '@hooks/files';
import {
getChannelNamesWithID,
getFileInfosIndexes,
getNumberFileMenuOptions,
getOrderedFileInfos,
getOrderedGalleryItems,
} from '@utils/files';
import {openGalleryAtIndex} from '@utils/gallery';
import {TabTypes} from '@utils/search';
import {preventDoubleTap} from '@utils/tap';
import {makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
import {showMobileOptionsBottomSheet} from './file_options/mobile_options';
import Toasts from './file_options/toasts';
import FileResult from './file_result';
import type ChannelModel from '@typings/database/models/servers/channel';
import type {GalleryAction} from '@typings/screens/gallery';
const getStyles = makeStyleSheetFromTheme((theme: Theme) => ({
resultsNumber: {
...typography('Heading', 300),
padding: 20,
color: theme.centerChannelColor,
},
}));
type Props = {
canDownloadFiles: boolean;
fileChannels: ChannelModel[];
fileInfos: FileInfo[];
paddingTop: StyleProp<ViewStyle>;
publicLinkEnabled: boolean;
searchValue: string;
isChannelFiles?: boolean;
isFilterEnabled?: boolean;
}
const galleryIdentifier = 'search-files-location';
const separatorStyle = {height: 10};
const Separator = () => <View style={separatorStyle}/>;
const FileResults = ({
canDownloadFiles,
fileChannels,
fileInfos,
paddingTop,
publicLinkEnabled,
searchValue,
isChannelFiles,
isFilterEnabled,
}: Props) => {
const theme = useTheme();
const styles = getStyles(theme);
const insets = useSafeAreaInsets();
const isTablet = useIsTablet();
const [action, setAction] = useState<GalleryAction>('none');
const [lastViewedFileInfo, setLastViewedFileInfo] = useState<FileInfo | undefined>(undefined);
const containerStyle = useMemo(() => ([paddingTop, {flexGrow: 1}]), [paddingTop]);
const numOptions = getNumberFileMenuOptions(canDownloadFiles, publicLinkEnabled);
const {images: imageAttachments, nonImages: nonImageAttachments} = useImageAttachments(fileInfos, publicLinkEnabled);
const filesForGallery = useMemo(() => imageAttachments.concat(nonImageAttachments), [imageAttachments, nonImageAttachments]);
const channelNames = useMemo(() => getChannelNamesWithID(fileChannels), [fileChannels]);
const orderedFileInfos = useMemo(() => getOrderedFileInfos(filesForGallery), [filesForGallery]);
const fileInfosIndexes = useMemo(() => getFileInfosIndexes(orderedFileInfos), [orderedFileInfos]);
const orderedGalleryItems = useMemo(() => getOrderedGalleryItems(orderedFileInfos), [orderedFileInfos]);
const onPreviewPress = useCallback(preventDoubleTap((idx: number) => {
openGalleryAtIndex(galleryIdentifier, idx, orderedGalleryItems);
}), [orderedGalleryItems]);
const updateFileForGallery = (idx: number, file: FileInfo) => {
'worklet';
orderedFileInfos[idx] = file;
};
const onOptionsPress = useCallback((fInfo: FileInfo) => {
setLastViewedFileInfo(fInfo);
if (!isTablet) {
showMobileOptionsBottomSheet({
fileInfo: fInfo,
insets,
numOptions,
setAction,
theme,
});
}
}, [insets, isTablet, numOptions, theme]);
const renderItem = useCallback(({item}: ListRenderItemInfo<FileInfo>) => {
let channelName: string | undefined;
if (!isChannelFiles && item.channel_id) {
channelName = channelNames[item.channel_id];
}
return (
<FileResult
canDownloadFiles={canDownloadFiles}
channelName={channelName}
fileInfo={item}
index={fileInfosIndexes[item.id!] || 0}
key={`${item.id}-${item.name}`}
numOptions={numOptions}
onOptionsPress={onOptionsPress}
onPress={onPreviewPress}
publicLinkEnabled={publicLinkEnabled}
setAction={setAction}
updateFileForGallery={updateFileForGallery}
/>
);
}, [
(orderedFileInfos.length === 1) && orderedFileInfos[0].mime_type,
canDownloadFiles,
channelNames,
fileInfosIndexes,
onPreviewPress,
onOptionsPress,
numOptions,
publicLinkEnabled,
]);
const noResults = useMemo(() => {
if (!searchValue && isChannelFiles) {
return (
<NoResults isFilterEnabled={isFilterEnabled}/>
);
}
return (
<NoResultsWithTerm
term={searchValue}
type={TabTypes.FILES}
/>
);
}, [searchValue]);
return (
<>
<FlatList
ListHeaderComponent={
<FormattedText
style={styles.resultsNumber}
id='mobile.search.results'
defaultMessage='{count} search {count, plural, one {result} other {results}}'
values={{count: orderedFileInfos.length}}
/>
}
ItemSeparatorComponent={Separator}
ListEmptyComponent={noResults}
contentContainerStyle={containerStyle}
data={orderedFileInfos}
indicatorStyle='black'
initialNumToRender={10}
//@ts-expect-error key not defined in types
listKey={'files'}
maxToRenderPerBatch={5}
nestedScrollEnabled={true}
refreshing={false}
removeClippedSubviews={true}
renderItem={renderItem}
scrollEventThrottle={16}
scrollToOverflowEnabled={true}
showsVerticalScrollIndicator={true}
testID='search_results.post_list.flat_list'
/>
<Toasts
action={action}
fileInfo={lastViewedFileInfo}
setAction={setAction}
/>
</>
);
};
export default FileResults;