mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-02-24 08:48:47 +01:00
Further simplify the scrolling logic in Status page
Previous code is too darn complicated and doesn't work in async cases e.g. user scrolled while the status is loading
This commit is contained in:
parent
3506285176
commit
cab06ae936
1 changed files with 55 additions and 67 deletions
|
@ -55,7 +55,9 @@ function StatusPage({ id }) {
|
||||||
};
|
};
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
|
const scrollOffsets = useRef();
|
||||||
const initContext = () => {
|
const initContext = () => {
|
||||||
|
console.debug('initContext', id);
|
||||||
setUIState('loading');
|
setUIState('loading');
|
||||||
let heroTimer;
|
let heroTimer;
|
||||||
|
|
||||||
|
@ -68,15 +70,15 @@ function StatusPage({ id }) {
|
||||||
);
|
);
|
||||||
setStatuses(reallyCachedStatuses);
|
setStatuses(reallyCachedStatuses);
|
||||||
} else {
|
} else {
|
||||||
const heroIndex = statuses.findIndex((s) => s.id === id);
|
// const heroIndex = statuses.findIndex((s) => s.id === id);
|
||||||
if (heroIndex !== -1) {
|
// if (heroIndex !== -1) {
|
||||||
// Case 2: It's in current statuses. Slice off all descendant statuses after the hero status to be safe
|
// // Case 2: It's in current statuses. Slice off all descendant statuses after the hero status to be safe
|
||||||
const slicedStatuses = statuses.slice(0, heroIndex + 1);
|
// const slicedStatuses = statuses.slice(0, heroIndex + 1);
|
||||||
setStatuses(slicedStatuses);
|
// setStatuses(slicedStatuses);
|
||||||
} else {
|
// } else {
|
||||||
// Case 3: Not cached and not in statuses, let's start from scratch
|
// Case 3: Not cached and not in statuses, let's start from scratch
|
||||||
setStatuses([{ id }]);
|
setStatuses([{ id }]);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
|
@ -86,17 +88,7 @@ function StatusPage({ id }) {
|
||||||
const hasStatus = !!snapStates.statuses[id];
|
const hasStatus = !!snapStates.statuses[id];
|
||||||
let heroStatus = snapStates.statuses[id];
|
let heroStatus = snapStates.statuses[id];
|
||||||
if (hasStatus) {
|
if (hasStatus) {
|
||||||
console.log('Hero status is cached');
|
console.debug('Hero status is cached');
|
||||||
// NOTE: This might conflict if the user interacts with the status before the fetch is done, e.g. favouriting it
|
|
||||||
// heroTimer = setTimeout(async () => {
|
|
||||||
// try {
|
|
||||||
// heroStatus = await heroFetch();
|
|
||||||
// states.statuses[id] = heroStatus;
|
|
||||||
// } catch (e) {
|
|
||||||
// // Silent fail if status is cached
|
|
||||||
// console.error(e);
|
|
||||||
// }
|
|
||||||
// }, 1000);
|
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
heroStatus = await heroFetch();
|
heroStatus = await heroFetch();
|
||||||
|
@ -162,6 +154,10 @@ function StatusPage({ id }) {
|
||||||
];
|
];
|
||||||
|
|
||||||
setUIState('default');
|
setUIState('default');
|
||||||
|
scrollOffsets.current = {
|
||||||
|
offsetTop: heroStatusRef.current?.offsetTop,
|
||||||
|
scrollTop: scrollableRef.current?.scrollTop,
|
||||||
|
};
|
||||||
console.log({ allStatuses });
|
console.log({ allStatuses });
|
||||||
setStatuses(allStatuses);
|
setStatuses(allStatuses);
|
||||||
store.session.setJSON('statuses-' + id, allStatuses);
|
store.session.setJSON('statuses-' + id, allStatuses);
|
||||||
|
@ -177,8 +173,40 @@ function StatusPage({ id }) {
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(initContext, [id]);
|
useEffect(initContext, [id]);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!statuses.length) return;
|
||||||
|
const scrollPosition = states.scrollPositions[id];
|
||||||
|
console.debug('scrollPosition', scrollPosition);
|
||||||
|
if (!userInitiated.current && !!scrollPosition) {
|
||||||
|
console.debug('Case 1', {
|
||||||
|
userInitiated: userInitiated.current,
|
||||||
|
scrollPosition,
|
||||||
|
});
|
||||||
|
scrollableRef.current.scrollTop = scrollPosition;
|
||||||
|
} else if (scrollOffsets.current) {
|
||||||
|
const newScrollOffsets = {
|
||||||
|
offsetTop: heroStatusRef.current?.offsetTop,
|
||||||
|
scrollTop: scrollableRef.current?.scrollTop,
|
||||||
|
};
|
||||||
|
const newScrollTop =
|
||||||
|
newScrollOffsets.offsetTop - scrollOffsets.current.offsetTop;
|
||||||
|
console.debug('Case 2', {
|
||||||
|
userInitiated: userInitiated.current,
|
||||||
|
scrollOffsets: scrollOffsets.current,
|
||||||
|
newScrollOffsets,
|
||||||
|
newScrollTop,
|
||||||
|
statuses: [...statuses],
|
||||||
|
});
|
||||||
|
scrollableRef.current.scrollTop = newScrollTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset
|
||||||
|
userInitiated.current = false;
|
||||||
|
scrollOffsets.current = null;
|
||||||
|
}, [statuses]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (snapStates.reloadStatusPage <= 0) return;
|
||||||
// Delete the cache for the context
|
// Delete the cache for the context
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -199,53 +227,13 @@ function StatusPage({ id }) {
|
||||||
})();
|
})();
|
||||||
}, [snapStates.reloadStatusPage]);
|
}, [snapStates.reloadStatusPage]);
|
||||||
|
|
||||||
const firstLoad = useRef(true);
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
useLayoutEffect(() => {
|
// clear all scrollPositions
|
||||||
if (!statuses.length) return;
|
states.scrollPositions = {};
|
||||||
const isLoading = uiState === 'loading';
|
states.reloadStatusPage = 0;
|
||||||
if (userInitiated.current) {
|
};
|
||||||
const hasAncestors = statuses.findIndex((s) => s.id === id) > 0; // Cannot use `ancestor` key because the hero state is dynamic
|
}, []);
|
||||||
if (!isLoading && hasAncestors) {
|
|
||||||
// Case 1: User initiated, has ancestors, after statuses are loaded, SNAP to hero status
|
|
||||||
console.log('Case 1');
|
|
||||||
heroStatusRef.current?.scrollIntoView();
|
|
||||||
} else if (isLoading && statuses.length > 1) {
|
|
||||||
if (firstLoad.current) {
|
|
||||||
// Case 2.1: User initiated, first load, don't smooth scroll anything
|
|
||||||
console.log('Case 2.1');
|
|
||||||
heroStatusRef.current?.scrollIntoView();
|
|
||||||
} else {
|
|
||||||
// Case 2.2: User initiated, while statuses are loading, SMOOTH-SCROLL to hero status
|
|
||||||
console.log('Case 2.2');
|
|
||||||
heroStatusRef.current?.scrollIntoView({
|
|
||||||
behavior: 'smooth',
|
|
||||||
block: 'start',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const scrollPosition = states.scrollPositions[id];
|
|
||||||
if (scrollPosition && scrollableRef.current) {
|
|
||||||
// Case 3: Not user initiated (e.g. back/forward button), restore to saved scroll position
|
|
||||||
console.log('Case 3');
|
|
||||||
scrollableRef.current.scrollTop = scrollPosition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log('No case', {
|
|
||||||
isLoading,
|
|
||||||
userInitiated: userInitiated.current,
|
|
||||||
statusesLength: statuses.length,
|
|
||||||
firstLoad: firstLoad.current,
|
|
||||||
// scrollPosition,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!isLoading) {
|
|
||||||
// Reset user initiated flag after statuses are loaded
|
|
||||||
userInitiated.current = false;
|
|
||||||
firstLoad.current = false;
|
|
||||||
}
|
|
||||||
}, [statuses, uiState]);
|
|
||||||
|
|
||||||
const heroStatus = snapStates.statuses[id];
|
const heroStatus = snapStates.statuses[id];
|
||||||
const heroDisplayName = useMemo(() => {
|
const heroDisplayName = useMemo(() => {
|
||||||
|
|
Loading…
Reference in a new issue