diff options
Diffstat (limited to 'Timeline/ClientApp/src')
-rw-r--r-- | Timeline/ClientApp/src/app/timeline/Timeline.tsx | 13 | ||||
-rw-r--r-- | Timeline/ClientApp/src/app/timeline/TimelinePageTemplateUI.tsx | 66 |
2 files changed, 56 insertions, 23 deletions
diff --git a/Timeline/ClientApp/src/app/timeline/Timeline.tsx b/Timeline/ClientApp/src/app/timeline/Timeline.tsx index 35d6490b..0a68c5db 100644 --- a/Timeline/ClientApp/src/app/timeline/Timeline.tsx +++ b/Timeline/ClientApp/src/app/timeline/Timeline.tsx @@ -1,6 +1,5 @@ import React from 'react'; import clsx from 'clsx'; -import { Container } from 'reactstrap'; import { TimelinePostInfo } from '../data/timeline'; import { useUser } from '../data/user'; @@ -19,6 +18,7 @@ export interface TimelineProps { posts: TimelinePostInfoEx[]; onDelete: TimelineDeleteCallback; onResize?: () => void; + containerRef?: React.Ref<HTMLDivElement>; } const Timeline: React.FC<TimelineProps> = (props) => { @@ -56,9 +56,12 @@ const Timeline: React.FC<TimelineProps> = (props) => { }, [posts, onDelete]); return ( - <Container - fluid - className={clsx('d-flex flex-column position-relative', props.className)} + <div + ref={props.containerRef} + className={clsx( + 'container-fluid d-flex flex-column position-relative', + props.className + )} > <div className="timeline-enter-animation-mask" /> {(() => { @@ -92,7 +95,7 @@ const Timeline: React.FC<TimelineProps> = (props) => { ); }); })()} - </Container> + </div> ); }; diff --git a/Timeline/ClientApp/src/app/timeline/TimelinePageTemplateUI.tsx b/Timeline/ClientApp/src/app/timeline/TimelinePageTemplateUI.tsx index 275e9ae0..5eb6a310 100644 --- a/Timeline/ClientApp/src/app/timeline/TimelinePageTemplateUI.tsx +++ b/Timeline/ClientApp/src/app/timeline/TimelinePageTemplateUI.tsx @@ -69,29 +69,58 @@ export default function TimelinePageTemplateUI< } }, []); + const timelineRef = React.useRef<HTMLDivElement | null>(null); + const [getResizeEvent, triggerResizeEvent] = useEventEmiiter(); React.useEffect(() => { - let scrollToBottom = true; - const disableScrollToBottom = (): void => { - scrollToBottom = false; - }; - - const subscriptions = [ - fromEvent(window, 'wheel').subscribe(disableScrollToBottom), - fromEvent(window, 'pointerdown').subscribe(disableScrollToBottom), - fromEvent(window, 'keydown').subscribe(disableScrollToBottom), - getResizeEvent().subscribe(() => { - if (scrollToBottom) { + const { current: timelineElement } = timelineRef; + if (timelineElement != null) { + let loadingScrollToBottom = true; + let pinBottom = false; + + const isAtBottom = (): boolean => + window.innerHeight + window.scrollY + 10 >= document.body.scrollHeight; + + const disableLoadingScrollToBottom = (): void => { + loadingScrollToBottom = false; + if (isAtBottom()) pinBottom = true; + }; + + const checkAndScrollToBottom = (): void => { + if (loadingScrollToBottom || pinBottom) { window.scrollTo(0, document.body.scrollHeight); } - }), - ]; - - return () => { - subscriptions.forEach((s) => s.unsubscribe()); - }; - }, [getResizeEvent, timeline, props.posts]); + }; + + const subscriptions = [ + fromEvent(timelineElement, 'wheel').subscribe( + disableLoadingScrollToBottom + ), + fromEvent(timelineElement, 'pointerdown').subscribe( + disableLoadingScrollToBottom + ), + fromEvent(timelineElement, 'keydown').subscribe( + disableLoadingScrollToBottom + ), + fromEvent(window, 'scroll').subscribe(() => { + if (loadingScrollToBottom) return; + + if (isAtBottom()) { + pinBottom = true; + } else { + pinBottom = false; + } + }), + fromEvent(window, 'resize').subscribe(checkAndScrollToBottom), + getResizeEvent().subscribe(checkAndScrollToBottom), + ]; + + return () => { + subscriptions.forEach((s) => s.unsubscribe()); + }; + } + }, [getResizeEvent, triggerResizeEvent, timeline, props.posts]); const [cardHeight, setCardHeight] = React.useState<number>(0); @@ -125,6 +154,7 @@ export default function TimelinePageTemplateUI< } else { timelineBody = ( <Timeline + containerRef={timelineRef} posts={props.posts} onDelete={props.onDelete} onResize={triggerResizeEvent} |