import * as React from "react"; import classnames from "classnames"; import { useScrollToBottom } from "@/utilities/hooks"; import { HubConnectionState } from "@microsoft/signalr"; import { HttpForbiddenError, HttpNetworkError, HttpNotFoundError, } from "@/http/common"; import { getHttpTimelineClient, HttpTimelineInfo, HttpTimelinePostInfo, } from "@/http/timeline"; import { useUser } from "@/services/user"; import { getTimelinePostUpdate$ } from "@/services/timeline"; import TimelinePostListView from "./TimelinePostListView"; import TimelineEmptyItem from "./TimelineEmptyItem"; import TimelineLoading from "./TimelineLoading"; import TimelinePostEdit from "./TimelinePostEdit"; import TimelinePostEditNoLogin from "./TimelinePostEditNoLogin"; import TimelineCard from "./TimelineCard"; import "./Timeline.css"; export interface TimelineProps { className?: string; style?: React.CSSProperties; timelineOwner: string; timelineName: string; } const Timeline: React.FC = (props) => { const { timelineOwner, timelineName, className, style } = props; const user = useUser(); const [timeline, setTimeline] = React.useState(null); const [posts, setPosts] = React.useState(null); const [signalrState, setSignalrState] = React.useState( HubConnectionState.Connecting ); const [error, setError] = React.useState< "offline" | "forbid" | "notfound" | "error" | null >(null); const [currentPage, setCurrentPage] = React.useState(1); const [totalPage, setTotalPage] = React.useState(0); const [timelineReloadKey, setTimelineReloadKey] = React.useState(0); const [postsReloadKey, setPostsReloadKey] = React.useState(0); const updateTimeline = (): void => setTimelineReloadKey((o) => o + 1); const updatePosts = (): void => setPostsReloadKey((o) => o + 1); React.useEffect(() => { setTimeline(null); setPosts(null); setError(null); setSignalrState(HubConnectionState.Connecting); }, [timelineOwner, timelineName]); React.useEffect(() => { getHttpTimelineClient() .getTimeline(timelineOwner, timelineName) .then( (t) => { setTimeline(t); }, (error) => { if (error instanceof HttpNetworkError) { setError("offline"); } else if (error instanceof HttpForbiddenError) { setError("forbid"); } else if (error instanceof HttpNotFoundError) { setError("notfound"); } else { console.error(error); setError("error"); } } ); }, [timelineOwner, timelineName, timelineReloadKey]); React.useEffect(() => { getHttpTimelineClient() .listPost(timelineOwner, timelineName, 1) .then( (page) => { setPosts( page.items.filter((p): p is HttpTimelinePostInfo => !p.deleted) ); setTotalPage(page.totalPageCount); }, (error) => { if (error instanceof HttpNetworkError) { setError("offline"); } else if (error instanceof HttpForbiddenError) { setError("forbid"); } else if (error instanceof HttpNotFoundError) { setError("notfound"); } else { console.error(error); setError("error"); } } ); }, [timelineOwner, timelineName, postsReloadKey]); React.useEffect(() => { const timelinePostUpdate$ = getTimelinePostUpdate$( timelineOwner, timelineName ); const subscription = timelinePostUpdate$.subscribe(({ update, state }) => { if (update) { setPostsReloadKey((o) => o + 1); } setSignalrState(state); }); return () => { subscription.unsubscribe(); }; }, [timelineOwner, timelineName]); useScrollToBottom(() => { console.log(`Load page ${currentPage + 1}.`); setCurrentPage(currentPage + 1); void getHttpTimelineClient() .listPost(timelineOwner, timelineName, currentPage + 1) .then( (page) => { const ps = page.items.filter( (p): p is HttpTimelinePostInfo => !p.deleted ); setPosts((old) => [...(old ?? []), ...ps]); }, (error) => { if (error instanceof HttpNetworkError) { setError("offline"); } else if (error instanceof HttpForbiddenError) { setError("forbid"); } else if (error instanceof HttpNotFoundError) { setError("notfound"); } else { console.error(error); setError("error"); } } ); }, currentPage < totalPage); if (error === "offline") { return (
Offline.
); } else if (error === "notfound") { return (
Not exist.
); } else if (error === "forbid") { return (
Forbid.
); } else if (error === "error") { return (
Error.
); } return ( <> {timeline == null && posts == null && } {timeline && ( )} {posts && (
{timeline?.postable ? ( ) : user == null ? ( ) : null}
)} ); }; export default Timeline;