feat: Add Scroll-to-Top Functionality in Chapter List (Closes #1470)
[skip ci]
This commit is contained in:
@@ -195,20 +195,6 @@ export const PluginListItem = memo(
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.flex} />
|
||||
<Button
|
||||
title={getString('browseScreen.latest')}
|
||||
textColor={theme.primary}
|
||||
onPress={handleLatestPress}
|
||||
/>
|
||||
{item.hasSettings ? (
|
||||
<IconButtonV2
|
||||
name="cog-outline"
|
||||
size={22}
|
||||
color={theme.primary}
|
||||
onPress={handleSettingsPress}
|
||||
theme={theme}
|
||||
/>
|
||||
) : null}
|
||||
{item.hasUpdate || __DEV__ ? (
|
||||
<IconButtonV2
|
||||
name="download-outline"
|
||||
@@ -218,6 +204,20 @@ export const PluginListItem = memo(
|
||||
theme={theme}
|
||||
/>
|
||||
) : null}
|
||||
{item.hasSettings ? (
|
||||
<IconButtonV2
|
||||
name="cog-outline"
|
||||
size={22}
|
||||
color={theme.primary}
|
||||
onPress={handleSettingsPress}
|
||||
theme={theme}
|
||||
/>
|
||||
) : null}
|
||||
<Button
|
||||
title={getString('browseScreen.latest')}
|
||||
textColor={theme.primary}
|
||||
onPress={handleLatestPress}
|
||||
/>
|
||||
</Pressable>
|
||||
</Swipeable>
|
||||
<ConfirmationDialog
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
import { getString } from '@strings/translations';
|
||||
import { showToast } from '@utils/showToast';
|
||||
import {
|
||||
Dimensions,
|
||||
NativeSyntheticEvent,
|
||||
NativeScrollEvent,
|
||||
RefreshControl,
|
||||
@@ -108,6 +109,7 @@ const NovelScreenList = ({
|
||||
const { downloadQueue, downloadChapter } = useDownload();
|
||||
|
||||
const [isFabExtended, setIsFabExtended] = useState(true);
|
||||
const [showScrollToTop, setShowScrollToTop] = useState(false);
|
||||
|
||||
const novelBottomSheetRef = useRef<BottomSheetModalMethods>(null);
|
||||
const trackerSheetRef = useRef<BottomSheetModalMethods>(null);
|
||||
@@ -122,6 +124,9 @@ const NovelScreenList = ({
|
||||
if (useFabForContinueReading && lastRead) {
|
||||
setIsFabExtended(currentScrollPosition <= 0);
|
||||
}
|
||||
|
||||
const screenHeight = Dimensions.get('window').height;
|
||||
setShowScrollToTop(currentScrollPosition > screenHeight / 2);
|
||||
};
|
||||
|
||||
const onRefresh = async () => {
|
||||
@@ -230,6 +235,10 @@ const NovelScreenList = ({
|
||||
});
|
||||
};
|
||||
|
||||
const scrollToTop = () => {
|
||||
listRef.current?.scrollToOffset({ offset: 0, animated: true });
|
||||
};
|
||||
|
||||
const setCustomNovelCover = async () => {
|
||||
if (!novel || novel.id === 'NO_ID') {
|
||||
return;
|
||||
@@ -381,6 +390,20 @@ const NovelScreenList = ({
|
||||
showChapterTitles={showChapterTitles}
|
||||
/>
|
||||
<TrackSheet bottomSheetRef={trackerSheetRef} novel={novel} />
|
||||
{showScrollToTop && (
|
||||
<AnimatedFAB
|
||||
style={[
|
||||
styles.scrollToTopFab,
|
||||
{ backgroundColor: theme.surface, marginBottom: bottomInset },
|
||||
]}
|
||||
color={theme.primary}
|
||||
icon="arrow-up"
|
||||
label=""
|
||||
extended={false}
|
||||
onPress={scrollToTop}
|
||||
visible={showScrollToTop}
|
||||
/>
|
||||
)}
|
||||
{useFabForContinueReading && (lastRead || chapters[0]) ? (
|
||||
<AnimatedFAB
|
||||
style={[
|
||||
@@ -429,6 +452,10 @@ const styles = StyleSheet.create({
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
scrollToTopFab: {
|
||||
bottom: 16,
|
||||
position: 'absolute',
|
||||
},
|
||||
});
|
||||
|
||||
export default React.memo(NovelScreenList);
|
||||
|
||||
Reference in New Issue
Block a user