Introduce "useIntersectionObserver" hook
This commit is contained in:
51
src/modules/core/hooks/useIntersectionObserver.tsx
Normal file
51
src/modules/core/hooks/useIntersectionObserver.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { RefObject, useLayoutEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export const useIntersectionObserver = (
|
||||||
|
ref: RefObject<HTMLElement> | HTMLElement | undefined | null,
|
||||||
|
callback: IntersectionObserverCallback,
|
||||||
|
{
|
||||||
|
ignoreInitialObserve = false,
|
||||||
|
root,
|
||||||
|
rootMargin,
|
||||||
|
threshold,
|
||||||
|
}: IntersectionObserverInit & { ignoreInitialObserve?: boolean } = {},
|
||||||
|
): (() => void) => {
|
||||||
|
const [disconnect, setDisconnect] = useState<() => void>(() => {});
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
const element = ref instanceof HTMLElement ? ref : ref?.current;
|
||||||
|
|
||||||
|
if (!element) {
|
||||||
|
return () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets immediately observed once on initial render
|
||||||
|
let isInitialObserve = true;
|
||||||
|
const intersectionObserver = new IntersectionObserver(
|
||||||
|
(...args) => {
|
||||||
|
if (ignoreInitialObserve && isInitialObserve) {
|
||||||
|
isInitialObserve = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(...args);
|
||||||
|
},
|
||||||
|
{ root, rootMargin, threshold },
|
||||||
|
);
|
||||||
|
intersectionObserver.observe(element);
|
||||||
|
|
||||||
|
setDisconnect(() => () => intersectionObserver.disconnect());
|
||||||
|
|
||||||
|
return () => intersectionObserver.disconnect();
|
||||||
|
}, [ref, callback, ignoreInitialObserve, root, rootMargin, threshold]);
|
||||||
|
|
||||||
|
return disconnect;
|
||||||
|
};
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useLayoutEffect } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { ReaderControls } from '@/modules/reader/services/ReaderControls.ts';
|
import { ReaderControls } from '@/modules/reader/services/ReaderControls.ts';
|
||||||
import { ReadingDirection, ReadingMode } from '@/modules/reader/types/Reader.types.ts';
|
import { ReadingDirection, ReadingMode } from '@/modules/reader/types/Reader.types.ts';
|
||||||
import {
|
import {
|
||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
} from '@/modules/reader/utils/ReaderSettings.utils.tsx';
|
} from '@/modules/reader/utils/ReaderSettings.utils.tsx';
|
||||||
import { READING_DIRECTION_TO_THEME_DIRECTION } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
import { READING_DIRECTION_TO_THEME_DIRECTION } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||||
import { getOptionForDirection } from '@/modules/theme/services/ThemeCreator.ts';
|
import { getOptionForDirection } from '@/modules/theme/services/ThemeCreator.ts';
|
||||||
|
import { useIntersectionObserver } from '@/modules/core/hooks/useIntersectionObserver.tsx';
|
||||||
|
|
||||||
interface ElementIntersection {
|
interface ElementIntersection {
|
||||||
start: boolean;
|
start: boolean;
|
||||||
@@ -188,17 +189,11 @@ export const useReaderInfiniteScrollUpdateChapter = (
|
|||||||
openChapter: ReturnType<typeof ReaderControls.useOpenChapter>,
|
openChapter: ReturnType<typeof ReaderControls.useOpenChapter>,
|
||||||
image: HTMLElement | null,
|
image: HTMLElement | null,
|
||||||
) => {
|
) => {
|
||||||
useLayoutEffect(() => {
|
useIntersectionObserver(
|
||||||
if (!image || !isContinuousReadingMode(readingMode) || chapterToOpenId === undefined) {
|
image,
|
||||||
return () => {};
|
useCallback(
|
||||||
}
|
|
||||||
|
|
||||||
// gets immediately observed once on initial render
|
|
||||||
let isInitialObserve = true;
|
|
||||||
const intersectionObserver = new IntersectionObserver(
|
|
||||||
(entries) => {
|
(entries) => {
|
||||||
if (isInitialObserve) {
|
if (!isContinuousReadingMode(readingMode) || chapterToOpenId === undefined) {
|
||||||
isInitialObserve = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,23 +230,25 @@ export const useReaderInfiniteScrollUpdateChapter = (
|
|||||||
openChapter(chapterId, false, false);
|
openChapter(chapterId, false, false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
[
|
||||||
|
pageType,
|
||||||
|
chapterId,
|
||||||
|
chapterToOpenId,
|
||||||
|
isCurrentChapter,
|
||||||
|
isChapterToOpenVisible,
|
||||||
|
readingMode,
|
||||||
|
readingDirection,
|
||||||
|
openChapter,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
useMemo(
|
||||||
|
() => ({
|
||||||
threshold: [OPEN_CHAPTER_INTERSECTION_RATIO],
|
threshold: [OPEN_CHAPTER_INTERSECTION_RATIO],
|
||||||
rootMargin: pageType === 'first' ? '0px 0px -10px 0px' : '-10px 0px 0px 0px',
|
rootMargin: pageType === 'first' ? '0px 0px -10px 0px' : '-10px 0px 0px 0px',
|
||||||
},
|
// gets immediately observed once on initial render
|
||||||
);
|
ignoreInitialObserve: true,
|
||||||
intersectionObserver.observe(image);
|
}),
|
||||||
|
[pageType],
|
||||||
return () => intersectionObserver.unobserve(image);
|
),
|
||||||
}, [
|
);
|
||||||
pageType,
|
|
||||||
chapterId,
|
|
||||||
chapterToOpenId,
|
|
||||||
isCurrentChapter,
|
|
||||||
isChapterToOpenVisible,
|
|
||||||
readingMode,
|
|
||||||
readingDirection,
|
|
||||||
openChapter,
|
|
||||||
image,
|
|
||||||
]);
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user