mirror of
https://github.com/KuekHaoYang/KVideo.git
synced 2026-08-12 23:33:43 +08:00
90 lines
3.2 KiB
TypeScript
90 lines
3.2 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useCallback } from 'react';
|
|
import { usePathname, useSearchParams } from 'next/navigation';
|
|
import { settingsStore } from '@/lib/store/settings-store';
|
|
|
|
/**
|
|
* ScrollPositionManager - Maintains scroll position across navigation and refreshes
|
|
* Uses sessionStorage to persist scroll state per URL
|
|
*/
|
|
export function ScrollPositionManager() {
|
|
const pathname = usePathname();
|
|
const searchParams = useSearchParams();
|
|
|
|
// Create a unique key for the current page including search params
|
|
const getPageKey = useCallback(() => {
|
|
const params = searchParams.toString();
|
|
return `scroll-pos:${pathname}${params ? '?' + params : ''}`;
|
|
}, [pathname, searchParams]);
|
|
|
|
// Restoration logic
|
|
useEffect(() => {
|
|
const settings = settingsStore.getSettings();
|
|
if (!settings.rememberScrollPosition) return;
|
|
|
|
const key = getPageKey();
|
|
const savedPos = sessionStorage.getItem(key);
|
|
|
|
if (savedPos) {
|
|
const position = parseInt(savedPos, 10);
|
|
if (!isNaN(position) && position > 0) {
|
|
// We use multiple attempts to restore scroll because content might be loading dynamically
|
|
// (e.g., search results, movie grids)
|
|
|
|
// Keep trying until we actually scroll there or a timeout occurs
|
|
let attempts = 0;
|
|
const maxAttempts = 10;
|
|
|
|
const tryScroll = () => {
|
|
const currentScroll = window.scrollY;
|
|
window.scrollTo(0, position);
|
|
attempts++;
|
|
|
|
// Verify if we actually reached the target position (with some wiggle room)
|
|
const reached = Math.abs(window.scrollY - position) < 10;
|
|
|
|
if (!reached && attempts < maxAttempts) {
|
|
// If we didn't reach it, it's likely because the page height hasn't caught up yet
|
|
setTimeout(tryScroll, 200);
|
|
}
|
|
};
|
|
|
|
const timerId = setTimeout(tryScroll, 100);
|
|
return () => clearTimeout(timerId);
|
|
}
|
|
}
|
|
}, [getPageKey, pathname, searchParams]); // Run on navigation
|
|
|
|
// Saving logic
|
|
useEffect(() => {
|
|
let timeoutId: NodeJS.Timeout;
|
|
|
|
const handleScroll = () => {
|
|
const settings = settingsStore.getSettings();
|
|
if (!settings.rememberScrollPosition) return;
|
|
|
|
// Debounce saving to avoid excessive writes to sessionStorage
|
|
clearTimeout(timeoutId);
|
|
timeoutId = setTimeout(() => {
|
|
const key = getPageKey();
|
|
// Only save if we have scrolled
|
|
if (window.scrollY > 0) {
|
|
sessionStorage.setItem(key, window.scrollY.toString());
|
|
} else {
|
|
sessionStorage.removeItem(key);
|
|
}
|
|
}, 500);
|
|
};
|
|
|
|
window.addEventListener('scroll', handleScroll, { passive: true });
|
|
|
|
return () => {
|
|
window.removeEventListener('scroll', handleScroll);
|
|
clearTimeout(timeoutId);
|
|
};
|
|
}, [getPageKey]);
|
|
|
|
return null;
|
|
}
|