import React, { useCallback, useRef, useState } from 'react'; import { BackHandler, NativeScrollEvent, NativeSyntheticEvent, View, } from 'react-native'; import { useQuery } from '@realm/react'; import { useTheme } from 'react-native-paper'; import { useDispatch, useSelector } from 'react-redux'; import { FlashList } from '@shopify/flash-list'; import { ParamListBase, useFocusEffect, useNavigation, } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import styles from '../styles'; import { ROUTE, SORT_DIRECTION, memesSortQuery } from '../types'; import { RootState, setNavVisible } from '../state'; import { Meme } from '../database'; import { HideableHeader, MemesHeader, MemesList } from '../components'; const Memes = () => { const { colors } = useTheme(); const { navigate } = useNavigation>(); const sort = useSelector((state: RootState) => state.memes.sort); const sortDirection = useSelector( (state: RootState) => state.memes.sortDirection, ); const favoritesOnly = useSelector( (state: RootState) => state.memes.favoritesOnly, ); const filter = useSelector((state: RootState) => state.memes.filter); const navVisisble = useSelector( (state: RootState) => state.navigation.navVisible, ); const dispatch = useDispatch(); const [flashListPadding, setFlashListPadding] = useState(0); const [search, setSearch] = useState(''); const memes = useQuery( Meme.schema.name, collectionIn => { let collection = collectionIn; const tokens = search .match(/"[^"]+"|\S+/gi) ?.map(token => token.replaceAll(/["']/g, '')); const tags = tokens ?.filter(token => token.startsWith('#')) .map(tag => tag.slice(1)); const words = tokens?.filter(token => !token.startsWith('#')); const tagsQuery = tags ?.map((tag, index) => `ANY tags.name CONTAINS[c] $${index}`) .join(' OR '); const wordsQuery = words ?.map((word, index) => `title CONTAINS[c] $${index}`) .join(' OR '); if (favoritesOnly) collection = collection.filtered('isFavorite == true'); if (filter) collection = collection.filtered('type == $0', filter); if (tags && tagsQuery) { collection = collection.filtered(tagsQuery, ...tags); } if (words && wordsQuery) { collection = collection.filtered(wordsQuery, ...words); } collection = collection.sorted( memesSortQuery(sort), sortDirection === SORT_DIRECTION.DESCENDING, ); return collection; }, [sort, sortDirection, favoritesOnly, filter, search], ); const [scrollOffset, setScrollOffset] = useState(0); const handleScroll = (event: NativeSyntheticEvent) => { const currentOffset = event.nativeEvent.contentOffset.y; if (currentOffset <= 150) { dispatch(setNavVisible(true)); } else { const diff = currentOffset - scrollOffset; if (Math.abs(diff) > 50) { dispatch(setNavVisible(diff < 0)); } } setScrollOffset(currentOffset); }; const flashListRef = useRef>(null); useFocusEffect( useCallback(() => { const handleBackPress = () => { if (scrollOffset <= 0) return false; flashListRef.current?.scrollToOffset({ offset: 0, animated: true }); return true; }; BackHandler.addEventListener('hardwareBackPress', handleBackPress); return () => BackHandler.removeEventListener('hardwareBackPress', handleBackPress); }, [scrollOffset]), ); useFocusEffect( useCallback(() => { dispatch(setNavVisible(true)); }, [dispatch]), ); return ( { setFlashListPadding(event.nativeEvent.layout.height); }} /> { navigate(ROUTE.MEME_VIEW, { ids: memes.map(meme => meme.id.toHexString()), index, }); }} /> ); }; export default Memes;