import * as React from "react"; import classnames from "classnames"; import { marked } from "marked"; import { UiLogicError } from "@/common"; import { HttpNetworkError } from "@/http/common"; import { getHttpTimelineClient, HttpTimelinePostInfo } from "@/http/timeline"; import { useUser } from "@/services/user"; import Skeleton from "@/views/common/Skeleton"; import LoadFailReload from "@/views/common/LoadFailReload"; const TextView: React.FC = (props) => { const { post, className, style } = props; const [text, setText] = React.useState(null); const [error, setError] = React.useState<"offline" | "error" | null>(null); const [reloadKey, setReloadKey] = React.useState(0); React.useEffect(() => { let subscribe = true; setText(null); setError(null); void getHttpTimelineClient() .getPostDataAsString(post.timelineOwnerV2, post.timelineNameV2, post.id) .then( (data) => { if (subscribe) setText(data); }, (error) => { if (subscribe) { if (error instanceof HttpNetworkError) { setError("offline"); } else { setError("error"); } } }, ); return () => { subscribe = false; }; }, [post.timelineOwnerV2, post.timelineNameV2, post.id, reloadKey]); if (error != null) { return ( setReloadKey(reloadKey + 1)} /> ); } else if (text == null) { return ; } else { return (
{text}
); } }; const ImageView: React.FC = (props) => { const { post, className, style } = props; useUser(); return ( ); }; const MarkdownView: React.FC = (props) => { const { post, className, style } = props; const [markdown, setMarkdown] = React.useState(null); const [error, setError] = React.useState<"offline" | "error" | null>(null); const [reloadKey, setReloadKey] = React.useState(0); React.useEffect(() => { let subscribe = true; setMarkdown(null); setError(null); void getHttpTimelineClient() .getPostDataAsString(post.timelineOwnerV2, post.timelineNameV2, post.id) .then( (data) => { if (subscribe) setMarkdown(data); }, (error) => { if (subscribe) { if (error instanceof HttpNetworkError) { setError("offline"); } else { setError("error"); } } }, ); return () => { subscribe = false; }; }, [post.timelineOwnerV2, post.timelineNameV2, post.id, reloadKey]); const markdownHtml = React.useMemo(() => { if (markdown == null) return null; return marked.parse(markdown, { mangle: false, headerIds: false, }); }, [markdown]); if (error != null) { return ( setReloadKey(reloadKey + 1)} /> ); } else if (markdown == null) { return ; } else { if (markdownHtml == null) { throw new UiLogicError("Markdown is not null but markdown html is."); } return (
); } }; export interface TimelinePostContentViewProps { post: HttpTimelinePostInfo; className?: string; style?: React.CSSProperties; } const viewMap: Record> = { "text/plain": TextView, "text/markdown": MarkdownView, "image/png": ImageView, "image/jpeg": ImageView, "image/gif": ImageView, "image/webp": ImageView, }; const TimelinePostContentView: React.FC = ( props, ) => { const { post, className, style } = props; const type = post.dataList[0].kind; if (type in viewMap) { const View = viewMap[type]; return ; } else { // TODO: i18n console.error("Unknown post type", post); return
Error, unknown post type!
; } }; export default TimelinePostContentView;