| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- import { useEffect, useState } from "react";
- import { Divider, message, Result, Space, Tag } from "antd";
- import { get, post } from "../../request";
- import store from "../../store";
- import { IArticleDataResponse, IArticleResponse } from "../api/Article";
- import ArticleView, { IFirstAnthology } from "./ArticleView";
- import { ICourseCurrUserResponse } from "../api/Course";
- import { ICourseUser, signIn } from "../../reducers/course-user";
- import { ITextbook, refresh } from "../../reducers/current-course";
- import ExerciseList from "./ExerciseList";
- import ExerciseAnswer from "../course/ExerciseAnswer";
- import "./article.css";
- import TocTree from "./TocTree";
- import PaliText from "../template/Wbw/PaliText";
- import ArticleSkeleton from "./ArticleSkeleton";
- import { modeChange } from "../../reducers/article-mode";
- import { IViewRequest, IViewStoreResponse } from "../api/view";
- import {
- IRecentRequest,
- IRecentResponse,
- } from "../../pages/studio/recent/list";
- import { ITocPathNode } from "../corpus/TocPath";
- export type ArticleMode = "read" | "edit" | "wbw";
- export type ArticleType =
- | "article"
- | "chapter"
- | "para"
- | "cs-para"
- | "sent"
- | "sim"
- | "page"
- | "textbook"
- | "exercise"
- | "exercise-list"
- | "sent-original"
- | "sent-commentary"
- | "sent-nissaya"
- | "sent-translation"
- | "term";
- /**
- * 每种article type 对应的路由参数
- * article/id?anthology=id&channel=id1,id2&mode=ArticleMode
- * chapter/book-para?channel=id1,id2&mode=ArticleMode
- * para/book?par=para1,para2&channel=id1,id2&mode=ArticleMode
- * cs-para/book-para?channel=id1,id2&mode=ArticleMode
- * sent/id?channel=id1,id2&mode=ArticleMode
- * sim/id?channel=id1,id2&mode=ArticleMode
- * textbook/articleId?course=id&mode=ArticleMode
- * exercise/articleId?course=id&exercise=id&username=name&mode=ArticleMode
- * exercise-list/articleId?course=id&exercise=id&mode=ArticleMode
- * sent-original/id
- */
- interface IWidget {
- type?: ArticleType;
- articleId?: string;
- book?: string | null;
- para?: string | null;
- channelId?: string | null;
- anthologyId?: string | null;
- courseId?: string;
- exerciseId?: string;
- userName?: string;
- mode?: ArticleMode | null;
- active?: boolean;
- onArticleChange?: Function;
- onFinal?: Function;
- onLoad?: Function;
- onAnthologySelect?: Function;
- }
- const ArticleWidget = ({
- type,
- book,
- para,
- channelId,
- articleId,
- anthologyId,
- courseId,
- exerciseId,
- userName,
- mode = "read",
- active = false,
- onArticleChange,
- onFinal,
- onLoad,
- onAnthologySelect,
- }: IWidget) => {
- const [articleData, setArticleData] = useState<IArticleDataResponse>();
- const [articleHtml, setArticleHtml] = useState<string[]>(["<span />"]);
- const [extra, setExtra] = useState(<></>);
- const [showSkeleton, setShowSkeleton] = useState(true);
- const [unauthorized, setUnauthorized] = useState(false);
- const [remains, setRemains] = useState(false);
- const channels = channelId?.split("_");
- useEffect(() => {
- /**
- * 由课本进入查询当前用户的权限和channel
- */
- if (
- type === "textbook" ||
- type === "exercise" ||
- type === "exercise-list"
- ) {
- if (typeof articleId !== "undefined") {
- const id = articleId.split("_");
- get<ICourseCurrUserResponse>(`/v2/course-curr?course_id=${id[0]}`).then(
- (response) => {
- console.log("course user", response);
- if (response.ok) {
- const it: ICourseUser = {
- channelId: response.data.channel_id,
- role: response.data.role,
- };
- store.dispatch(signIn(it));
- /**
- * redux发布课程信息
- */
- const ic: ITextbook = {
- courseId: id[0],
- articleId: id[1],
- };
- store.dispatch(refresh(ic));
- }
- }
- );
- }
- }
- }, [articleId, type]);
- useEffect(() => {
- //发布mode变更
- console.log("发布mode变更", mode);
- store.dispatch(modeChange(mode as ArticleMode));
- }, [mode]);
- const srcDataMode = mode === "edit" || mode === "wbw" ? "edit" : "read";
- useEffect(() => {
- console.log("srcDataMode", srcDataMode);
- if (!active) {
- return;
- }
- if (typeof type !== "undefined") {
- let url = "";
- switch (type) {
- case "chapter":
- if (typeof articleId !== "undefined") {
- url = `/v2/corpus-chapter/${articleId}?mode=${srcDataMode}`;
- url += channelId ? `&channels=${channelId}` : "";
- }
- break;
- case "para":
- const _book = book ? book : articleId;
- url = `/v2/corpus?view=para&book=${_book}&par=${para}&mode=${srcDataMode}`;
- url += channelId ? `&channels=${channelId}` : "";
- break;
- case "article":
- if (typeof articleId !== "undefined") {
- url = `/v2/article/${articleId}?mode=${srcDataMode}`;
- url += channelId ? `&channel=${channelId}` : "";
- url += anthologyId ? `&anthology=${anthologyId}` : "";
- }
- break;
- case "textbook":
- if (typeof articleId !== "undefined") {
- url = `/v2/article/${articleId}?view=textbook&course=${courseId}&mode=${srcDataMode}`;
- }
- break;
- case "exercise":
- if (typeof articleId !== "undefined") {
- url = `/v2/article/${articleId}?mode=${srcDataMode}&course=${courseId}&exercise=${exerciseId}&user=${userName}`;
- setExtra(
- <ExerciseAnswer
- courseId={courseId}
- articleId={articleId}
- exerciseId={exerciseId}
- />
- );
- }
- break;
- case "exercise-list":
- if (typeof articleId !== "undefined") {
- url = `/v2/article/${articleId}?mode=${srcDataMode}&course=${courseId}&exercise=${exerciseId}`;
- setExtra(
- <ExerciseList
- courseId={courseId}
- articleId={articleId}
- exerciseId={exerciseId}
- />
- );
- }
- break;
- default:
- if (typeof articleId !== "undefined") {
- url = `/v2/corpus/${type}/${articleId}/${srcDataMode}?mode=${srcDataMode}`;
- url += channelId ? `&channel=${channelId}` : "";
- }
- break;
- }
- console.log("article url", url);
- setShowSkeleton(true);
- if (typeof articleId !== "undefined") {
- const param = {
- mode: srcDataMode,
- channel: channelId !== null ? channelId : undefined,
- book: book !== null ? book : undefined,
- para: para !== null ? para : undefined,
- };
- post<IRecentRequest, IRecentResponse>("/v2/recent", {
- type: type,
- article_id: articleId,
- param: JSON.stringify(param),
- }).then((json) => {
- console.log("recent", json);
- });
- }
- get<IArticleResponse>(url)
- .then((json) => {
- console.log("article", json);
- if (json.ok) {
- setArticleData(json.data);
- if (json.data.html) {
- setArticleHtml([json.data.html]);
- } else if (json.data.content) {
- setArticleHtml([json.data.content]);
- }
- if (json.data.from) {
- setRemains(true);
- }
- setShowSkeleton(false);
- setExtra(
- <TocTree
- treeData={json.data.toc?.map((item) => {
- const strTitle = item.title ? item.title : item.pali_title;
- const key = item.key
- ? item.key
- : `${item.book}-${item.paragraph}`;
- const progress = item.progress?.map((item, id) => (
- <Tag key={id}>{Math.round(item * 100) + "%"}</Tag>
- ));
- return {
- key: key,
- title: (
- <Space>
- <PaliText
- text={strTitle === "" ? "[unnamed]" : strTitle}
- />
- {progress}
- </Space>
- ),
- level: item.level,
- };
- })}
- onSelect={(keys: string[]) => {
- console.log(keys);
- if (
- typeof onArticleChange !== "undefined" &&
- keys.length > 0
- ) {
- onArticleChange(keys[0]);
- }
- }}
- />
- );
- switch (type) {
- case "chapter":
- if (typeof articleId === "string" && channelId) {
- const [book, para] = articleId?.split("-");
- post<IViewRequest, IViewStoreResponse>("/v2/view", {
- target_type: type,
- book: parseInt(book),
- para: parseInt(para),
- channel: channelId,
- mode: srcDataMode,
- }).then((json) => {
- console.log("view", json.data);
- });
- }
- break;
- default:
- break;
- }
- if (typeof onLoad !== "undefined") {
- onLoad(json.data);
- }
- } else {
- setShowSkeleton(false);
- setUnauthorized(true);
- message.error(json.message);
- }
- })
- .catch((e) => {
- console.error(e);
- });
- }
- }, [
- active,
- type,
- articleId,
- srcDataMode,
- book,
- para,
- channelId,
- anthologyId,
- courseId,
- exerciseId,
- userName,
- ]);
- const getNextPara = (next: IArticleDataResponse): void => {
- if (
- typeof next.paraId === "undefined" ||
- typeof next.mode === "undefined" ||
- typeof next.from === "undefined" ||
- typeof next.to === "undefined"
- ) {
- setRemains(false);
- return;
- }
- let url = `/v2/corpus-chapter/${next.paraId}?mode=${next.mode}`;
- url += `&from=${next.from}`;
- url += `&to=${next.to}`;
- url += channels ? `&channels=${channels}` : "";
- console.log("lazy load", url);
- get<IArticleResponse>(url).then((json) => {
- if (json.ok) {
- if (typeof json.data.content === "string") {
- const content: string = json.data.content;
- setArticleData((origin) => {
- if (origin) {
- origin.from = json.data.from;
- }
- return origin;
- });
- setArticleHtml((origin) => {
- return [...origin, content];
- });
- }
- //getNextPara(json.data);
- }
- });
- return;
- };
- //const comment = <CommentListCard resId={articleData?.uid} resType="article" />
- let anthology: IFirstAnthology | undefined;
- if (articleData?.anthology_count && articleData.anthology_first) {
- anthology = {
- id: articleData.anthology_first.uid,
- title: articleData.anthology_first.title,
- count: articleData?.anthology_count,
- };
- }
- return (
- <div>
- {showSkeleton ? (
- <ArticleSkeleton />
- ) : unauthorized ? (
- <Result
- status="403"
- title="无权访问"
- subTitle="您无权访问该内容。您可能没有登录,或者内容的所有者没有给您所需的权限。"
- extra={<></>}
- />
- ) : (
- <ArticleView
- id={articleData?.uid}
- title={articleData?.title}
- subTitle={articleData?.subtitle}
- summary={articleData?.summary}
- content={articleData ? articleData.content : ""}
- html={articleHtml}
- path={articleData?.path}
- created_at={articleData?.created_at}
- updated_at={articleData?.updated_at}
- channels={channels}
- type={type}
- articleId={articleId}
- remains={remains}
- anthology={anthology}
- onEnd={() => {
- if (type === "chapter" && articleData) {
- getNextPara(articleData);
- }
- }}
- onPathChange={(
- node: ITocPathNode,
- e: React.MouseEvent<HTMLSpanElement | HTMLAnchorElement, MouseEvent>
- ) => {
- if (typeof onArticleChange !== "undefined") {
- const newArticle = node.key
- ? node.key
- : `${node.book}-${node.paragraph}`;
- const target = e.ctrlKey || e.metaKey ? "_blank" : "self";
- onArticleChange(newArticle, target);
- }
- }}
- onAnthologySelect={(id: string) => {
- if (typeof onAnthologySelect !== "undefined") {
- onAnthologySelect(id);
- }
- }}
- />
- )}
- <Divider />
- {extra}
- <Divider />
- </div>
- );
- };
- export default ArticleWidget;
|