This repository has been archived on 2025-07-31. You can view files and clone it, but cannot push or open issues or pull requests.
Files
terminally-online/src/screens/memes.tsx
2023-07-24 21:55:36 +03:00

159 lines
4.5 KiB
TypeScript

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<NativeStackNavigationProp<ParamListBase>>();
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>(
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<NativeScrollEvent>) => {
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<FlashList<Meme>>(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 (
<View
style={[
styles.paddingHorizontal,
styles.fullSize,
{ backgroundColor: colors.background },
]}>
<HideableHeader visible={navVisisble}>
<MemesHeader
search={search}
setSearch={setSearch}
onLayout={event => {
setFlashListPadding(event.nativeEvent.layout.height);
}}
/>
</HideableHeader>
<MemesList
memes={memes}
flashListRef={flashListRef}
flashListPadding={flashListPadding}
handleScroll={handleScroll}
focusMeme={(index: number) => {
navigate(ROUTE.MEME_VIEW, {
ids: memes.map(meme => meme.id.toHexString()),
index,
});
}}
/>
</View>
);
};
export default Memes;