// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import {LinearGradient, type LinearGradientProps} from 'expo-linear-gradient'; import React, {useCallback, useMemo, useState} from 'react'; import {FlatList, View, type ListRenderItemInfo, type NativeSyntheticEvent, type NativeScrollEvent} from 'react-native'; import Animated from 'react-native-reanimated'; import {GalleryInit} from '@context/gallery'; import {useTheme} from '@context/theme'; import {useChannelBookmarkFiles} from '@hooks/files'; import {usePreventDoubleTap} from '@hooks/utils'; import ChannelBookmarkModel from '@typings/database/models/servers/channel_bookmark'; import {fileToGalleryItem, openGalleryAtIndex} from '@utils/gallery'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import AddBookmark from './add_bookmark'; import ChannelBookmark from './channel_bookmark'; type Props = { bookmarks: ChannelBookmarkModel[]; canAddBookmarks: boolean; canDeleteBookmarks: boolean; canDownloadFiles: boolean; canEditBookmarks: boolean; canUploadFiles: boolean; channelId: string; currentUserId: string; enableSecureFilePreview: boolean; publicLinkEnabled: boolean; showInInfo: boolean; separator?: boolean; } const GRADIENT_LOCATIONS: LinearGradientProps['locations'] = [0, 0.64, 1]; const SCROLL_OFFSET = 10; const isCloseToBottom = ({layoutMeasurement, contentOffset, contentSize}: NativeScrollEvent) => { return layoutMeasurement.width + contentOffset.x <= contentSize.width - SCROLL_OFFSET; }; const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ container: { flexDirection: 'row', }, separator: { height: 1, backgroundColor: changeOpacity(theme.centerChannelColor, 0.08), marginTop: 8, marginBottom: 24, }, emptyItemSeparator: { width: 20, }, addContainer: { width: 40, alignContent: 'center', }, gradient: { height: 48, width: 68, position: 'absolute', right: 0, }, channelView: { paddingHorizontal: 8, }, })); const ChannelBookmarks = ({ bookmarks, canAddBookmarks, canDeleteBookmarks, canDownloadFiles, canEditBookmarks, canUploadFiles, channelId, currentUserId, enableSecureFilePreview, publicLinkEnabled, showInInfo, separator = true, }: Props) => { const galleryIdentifier = `${channelId}-bookmarks`; const theme = useTheme(); const styles = getStyleSheet(theme); const files = useChannelBookmarkFiles(bookmarks); const [allowEndFade, setAllowEndFade] = useState(true); const attachmentIndex = useCallback((fileId: string) => { return files.findIndex((file) => file.id === fileId) || 0; }, [files]); const handlePreviewPress = usePreventDoubleTap(useCallback((idx: number) => { if (files.length) { const items = files.map((f) => fileToGalleryItem(f, f.user_id, undefined, 0, f.id)); openGalleryAtIndex(galleryIdentifier, idx, items); } }, [files, galleryIdentifier])); const renderItem = useCallback(({item}: ListRenderItemInfo) => { return ( ); }, [ canDeleteBookmarks, canDownloadFiles, canEditBookmarks, enableSecureFilePreview, galleryIdentifier, attachmentIndex, handlePreviewPress, publicLinkEnabled, ]); const renderItemSeparator = useCallback(() => ( ), [styles.emptyItemSeparator]); const onScrolled = useCallback((e: NativeSyntheticEvent) => { setAllowEndFade(isCloseToBottom(e.nativeEvent)); }, []); const gradientColors: LinearGradientProps['colors'] = useMemo(() => [ theme.centerChannelBg, changeOpacity(theme.centerChannelBg, 0.6458), changeOpacity(theme.centerChannelBg, 0), ], [theme]); if (!bookmarks.length && showInInfo && canAddBookmarks) { return ( ); } if (bookmarks.length) { return ( {canAddBookmarks && {allowEndFade && } } {separator && } ); } return null; }; export default ChannelBookmarks;