From cf6cfe87b46a2a3eb2913209092ab4c5639e75c3 Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 11 Jun 2020 17:27:15 +0800 Subject: feat(front): Service worker is coming! --- .../src/app/timeline/TimelinePageTemplateUI.tsx | 212 +++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 Timeline/ClientApp/src/app/timeline/TimelinePageTemplateUI.tsx (limited to 'Timeline/ClientApp/src/app/timeline/TimelinePageTemplateUI.tsx') diff --git a/Timeline/ClientApp/src/app/timeline/TimelinePageTemplateUI.tsx b/Timeline/ClientApp/src/app/timeline/TimelinePageTemplateUI.tsx new file mode 100644 index 00000000..d74fffc4 --- /dev/null +++ b/Timeline/ClientApp/src/app/timeline/TimelinePageTemplateUI.tsx @@ -0,0 +1,212 @@ +import React from 'react'; +import { Spinner } from 'reactstrap'; +import { useTranslation } from 'react-i18next'; +import { Subject, fromEvent } from 'rxjs'; + +import { getAlertHost } from '../common/alert-service'; + +import Timeline, { + TimelinePostInfoEx, + TimelineDeleteCallback, +} from './Timeline'; +import AppBar from '../common/AppBar'; +import TimelinePostEdit, { TimelinePostSendCallback } from './TimelinePostEdit'; +import CollapseButton from '../common/CollapseButton'; + +export interface TimelineCardComponentProps { + timeline: TTimeline; + onManage?: (item: TManageItems | 'property') => void; + onMember: () => void; + className?: string; + onHeight?: (height: number) => void; +} + +export interface TimelinePageTemplateUIProps< + TTimeline extends { name: string }, + TManageItems +> { + avatarKey?: string | number; + timeline?: TTimeline; + posts?: TimelinePostInfoEx[] | 'forbid'; + CardComponent: React.ComponentType< + TimelineCardComponentProps + >; + onMember: () => void; + onManage?: (item: TManageItems | 'property') => void; + onPost?: TimelinePostSendCallback; + onDelete: TimelineDeleteCallback; + error?: string; +} + +export default function TimelinePageTemplateUI< + TTimeline extends { name: string }, + TEditItems +>( + props: TimelinePageTemplateUIProps +): React.ReactElement | null { + const { timeline } = props; + + const { t } = useTranslation(); + + const bottomSpaceRef = React.useRef(null); + + const onPostEditHeightChange = React.useCallback((height: number): void => { + const { current: bottomSpaceDiv } = bottomSpaceRef; + if (bottomSpaceDiv != null) { + bottomSpaceDiv.style.height = `${height}px`; + } + if (height === 0) { + const alertHost = getAlertHost(); + if (alertHost != null) { + alertHost.style.removeProperty('margin-bottom'); + } + } else { + const alertHost = getAlertHost(); + if (alertHost != null) { + alertHost.style.marginBottom = `${height}px`; + } + } + }, []); + + const resizeSubject = React.useMemo(() => new Subject(), []); + const triggerResizeEvent = React.useCallback(() => { + resizeSubject.next(null); + }, [resizeSubject]); + + 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), + resizeSubject.subscribe(() => { + if (scrollToBottom) { + window.scrollTo(0, document.body.scrollHeight); + } + }), + ]; + + return () => { + subscriptions.forEach((s) => s.unsubscribe()); + }; + }, [resizeSubject, timeline, props.posts]); + + const [cardHeight, setCardHeight] = React.useState(0); + + const onCardHeightChange = React.useCallback((height: number) => { + setCardHeight(height); + }, []); + + const genCardCollapseLocalStorageKey = (timelineName: string): string => + `timeline.${timelineName}.cardCollapse`; + + const cardCollapseLocalStorageKey = + timeline != null ? genCardCollapseLocalStorageKey(timeline.name) : null; + + const [infoCardCollapse, setInfoCardCollapse] = React.useState(true); + React.useEffect(() => { + if (cardCollapseLocalStorageKey != null) { + const savedCollapse = + window.localStorage.getItem(cardCollapseLocalStorageKey) === 'true'; + setInfoCardCollapse(savedCollapse); + } + }, [cardCollapseLocalStorageKey]); + + let body: React.ReactElement; + + if (props.error != null) { + body =

{t(props.error)}

; + } else { + if (timeline != null) { + let timelineBody: React.ReactElement; + if (props.posts != null) { + if (props.posts === 'forbid') { + timelineBody = ( +

{t('timeline.messageCantSee')}

+ ); + } else { + timelineBody = ( + + ); + if (props.onPost != null) { + timelineBody = ( + <> + {timelineBody} +
+ + + ); + } + } + } else { + timelineBody = ( +
+ +
+ ); + } + const { CardComponent } = props; + + body = ( + <> +
+ { + const newState = !infoCardCollapse; + setInfoCardCollapse(newState); + window.localStorage.setItem( + genCardCollapseLocalStorageKey(timeline.name), + newState.toString() + ); + }} + className="float-right m-1 info-card-collapse-button text-orange" + /> + +
+ {timelineBody} + + ); + } else { + body = ( +
+ +
+ ); + } + } + + return ( + <> + +
+
+ {body} +
+ + ); +} -- cgit v1.2.3