Add scroll-to-top on back

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
2023-07-19 15:30:10 +03:00
parent 85732e247a
commit 1b2ce96c5e
6 changed files with 110 additions and 63 deletions

View File

@@ -1,11 +1,11 @@
import React, { useEffect, useRef, useState } from 'react';
import React, { useCallback, useRef, useState } from 'react';
import {
StyleSheet,
View,
Text,
NativeSyntheticEvent,
NativeScrollEvent,
Animated,
BackHandler,
} from 'react-native';
import {
Button,
@@ -19,7 +19,12 @@ import {
import { useQuery } from '@realm/react';
import { useDispatch, useSelector } from 'react-redux';
import { FlashList } from '@shopify/flash-list';
import { TagChip } from '../components';
import {
NavigationProp,
useFocusEffect,
useNavigation,
} from '@react-navigation/native';
import { HideableHeader, TagChip } from '../components';
import { Tag } from '../database';
import styles from '../styles';
import {
@@ -37,17 +42,8 @@ import {
tagSortQuery,
} from '../types';
import { getSortIcon } from '../utilities';
import { useDimensions } from '../contexts';
import { NavigationProp, useNavigation } from '@react-navigation/native';
const tagsStyles = StyleSheet.create({
headerView: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
zIndex: 100,
},
headerButtonView: {
height: 50,
},
@@ -74,8 +70,7 @@ const tagsStyles = StyleSheet.create({
const Tags = () => {
const { colors } = useTheme();
const { orientation } = useDimensions();
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
const { navigate } = useNavigation<NavigationProp<RootStackParamList>>();
const sort = useSelector((state: RootState) => state.tags.sort);
const sortDirection = useSelector(
(state: RootState) => state.tags.sortDirection,
@@ -101,16 +96,6 @@ const Tags = () => {
setSortMenuVisible(false);
};
const sortMenuAnim = useRef(new Animated.Value(navVisisble ? 1 : 0)).current;
useEffect(() => {
Animated.timing(sortMenuAnim, {
toValue: navVisisble ? 1 : 0,
duration: navVisisble ? 200 : 150,
useNativeDriver: true,
}).start();
}, [navVisisble, sortMenuAnim]);
const [search, setSearch] = useState('');
const tags = useQuery<Tag>(
@@ -132,17 +117,33 @@ const Tags = () => {
if (currentOffset <= 150) {
dispatch(setNavVisible(true));
setScrollOffset(0);
return;
} else {
const diff = currentOffset - scrollOffset;
if (Math.abs(diff) > 50) dispatch(setNavVisible(diff < 0));
}
const diff = currentOffset - scrollOffset;
if (Math.abs(diff) < 50) return;
dispatch(setNavVisible(diff < 0));
setScrollOffset(currentOffset);
};
const flashListRef = useRef<FlashList<Tag>>(null);
useFocusEffect(
useCallback(() => {
const handleBackPress = () => {
if (scrollOffset > 0) {
flashListRef.current?.scrollToOffset({ offset: 0, animated: true });
return true;
}
return false;
};
BackHandler.addEventListener('hardwareBackPress', handleBackPress);
return () =>
BackHandler.removeEventListener('hardwareBackPress', handleBackPress);
}, [flashListRef, scrollOffset]),
);
return (
<View
style={[
@@ -150,26 +151,7 @@ const Tags = () => {
styles.fullSize,
{ backgroundColor: colors.background },
]}>
<Animated.View
style={[
tagsStyles.headerView,
orientation == 'portrait' && styles.paddingTop,
orientation == 'landscape' && styles.smallPaddingTop,
styles.paddingHorizontal,
{
transform: [
{
translateY: sortMenuAnim.interpolate({
inputRange: [0, 1],
outputRange: [-130, 0],
}),
},
],
},
{
backgroundColor: colors.background,
},
]}>
<HideableHeader visible={navVisisble}>
<Searchbar
placeholder="Search Tags"
value={search}
@@ -209,15 +191,16 @@ const Tags = () => {
</Menu>
</View>
<Divider />
</Animated.View>
</HideableHeader>
<FlashList
ref={flashListRef}
data={tags}
estimatedItemSize={52}
showsVerticalScrollIndicator={false}
renderItem={({ item: tag }) => (
<TouchableRipple
onPress={() =>
navigation.navigate(ROUTE.EDIT_TAG, { id: tag.id.toHexString() })
navigate(ROUTE.EDIT_TAG, { id: tag.id.toHexString() })
}>
<View style={tagsStyles.tagRow}>
<View style={tagsStyles.tagView}>