2
0

SentEdit.tsx 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. import { Affix } from "antd";
  2. import { useEffect, useRef, useState } from "react";
  3. import type { IStudio } from "../auth/Studio";
  4. import type { IUser } from "../auth/User";
  5. import type { IChannel } from "../channel/Channel";
  6. import type { TContentType } from "../discussion/DiscussionCreate";
  7. import type { ITocPathNode } from "../corpus/TocPath";
  8. import SentContent from "./SentEdit/SentContent";
  9. import SentTab from "./SentEdit/SentTab";
  10. import type { IWbw } from "./Wbw/WbwWord";
  11. import type { ArticleMode } from "../article/Article";
  12. import type { TChannelType } from "../../api/Channel";
  13. import { useAppSelector } from "../../hooks";
  14. import { currFocus } from "../../reducers/focus";
  15. import type { ISentenceData } from "../../api/Corpus";
  16. import "./SentEdit/style.css";
  17. import SentCell from "./SentEdit/SentCell";
  18. import { settingInfo } from "../../reducers/setting";
  19. import { GetUserSetting } from "../auth/setting/default";
  20. import { SENTENCE_FIX_WIDTH } from "../../types/article";
  21. import { useSetting } from "../../hooks/useSetting";
  22. export interface IResNumber {
  23. translation?: number;
  24. nissaya?: number;
  25. commentary?: number;
  26. origin?: number;
  27. sim?: number;
  28. }
  29. export interface ISentenceId {
  30. book: number;
  31. para: number;
  32. wordStart: number;
  33. wordEnd: number;
  34. }
  35. export const toISentence = (apiData: ISentenceData): ISentence => {
  36. return {
  37. id: apiData.id,
  38. content: apiData.content,
  39. contentType: apiData.content_type,
  40. html: apiData.html,
  41. book: apiData.book,
  42. para: apiData.paragraph,
  43. wordStart: apiData.word_start,
  44. wordEnd: apiData.word_end,
  45. editor: apiData.editor,
  46. studio: apiData.studio,
  47. channel: apiData.channel,
  48. updateAt: apiData.updated_at,
  49. acceptor: apiData.acceptor,
  50. prEditAt: apiData.pr_edit_at,
  51. forkAt: apiData.fork_at,
  52. suggestionCount: apiData.suggestionCount,
  53. };
  54. };
  55. export interface IWidgetSentEditInner {
  56. id: string;
  57. book: number;
  58. para: number;
  59. wordStart: number;
  60. wordEnd: number;
  61. channels?: string[];
  62. origin?: ISentence[];
  63. translation?: ISentence[];
  64. commentaries?: ISentence[];
  65. answer?: ISentence;
  66. path?: ITocPathNode[];
  67. layout?: "row" | "column";
  68. tranNum?: number;
  69. nissayaNum?: number;
  70. commNum?: number;
  71. originNum: number;
  72. simNum?: number;
  73. compact?: boolean;
  74. mode?: ArticleMode;
  75. showWbwProgress?: boolean;
  76. readonly?: boolean;
  77. wbwProgress?: number;
  78. wbwScore?: number;
  79. onTranslationChange?: (data: ISentence) => void;
  80. }
  81. export const SentEditInner = ({
  82. id,
  83. book,
  84. para,
  85. wordStart,
  86. wordEnd,
  87. ___channels,
  88. origin,
  89. translation,
  90. answer,
  91. path,
  92. layout = "column",
  93. tranNum,
  94. nissayaNum,
  95. commNum,
  96. originNum,
  97. simNum,
  98. compact = false,
  99. mode,
  100. showWbwProgress = false,
  101. readonly = false,
  102. commentaries,
  103. onTranslationChange,
  104. }: IWidgetSentEditInner) => {
  105. const [wbwData, setWbwData] = useState<IWbw[]>();
  106. const [magicDict, setMagicDict] = useState<string>();
  107. const [magicDictLoading, setMagicDictLoading] = useState(false);
  108. const [isCompact, setIsCompact] = useState(compact);
  109. const [articleMode, setArticleMode] = useState<ArticleMode | undefined>(mode);
  110. const [loadedRes, setLoadedRes] = useState<IResNumber>();
  111. const [isFocus, setIsFocus] = useState(false);
  112. const focus = useAppSelector(currFocus);
  113. const divRef = useRef<HTMLDivElement>(null);
  114. const [affix, setAffix] = useState<boolean>(false);
  115. const settings = useAppSelector(settingInfo);
  116. const [commentaryLayout, setCommentaryLayout] = useState("column");
  117. const rootFixed = useSetting("setting.layout.root.fixed");
  118. useEffect(() => {
  119. const layoutCommentary = GetUserSetting(
  120. "setting.layout.commentary",
  121. settings
  122. );
  123. layoutCommentary &&
  124. typeof layoutCommentary === "string" &&
  125. setCommentaryLayout(layoutCommentary);
  126. }, [settings]);
  127. useEffect(() => {
  128. if (focus) {
  129. if (focus.focus?.type === "sentence") {
  130. if (focus.focus.id === id) {
  131. setIsFocus(true);
  132. divRef.current?.scrollIntoView({
  133. behavior: "smooth",
  134. block: "nearest",
  135. inline: "nearest",
  136. });
  137. } else {
  138. setIsFocus(false);
  139. }
  140. }
  141. } else {
  142. setIsFocus(false);
  143. }
  144. }, [focus, id]);
  145. useEffect(() => {
  146. const validRes = (value: ISentence, type: TChannelType) =>
  147. value.channel.type === type &&
  148. value.content &&
  149. value.content.trim().length > 0;
  150. if (translation) {
  151. const res = {
  152. translation: translation.filter((value) =>
  153. validRes(value, "translation")
  154. ).length,
  155. nissaya: translation.filter((value) => validRes(value, "nissaya"))
  156. .length,
  157. commentary: translation.filter((value) => validRes(value, "commentary"))
  158. .length,
  159. };
  160. setLoadedRes(res);
  161. }
  162. }, [translation]);
  163. useEffect(() => {
  164. const content = origin?.find(
  165. (value) => value.contentType === "json"
  166. )?.content;
  167. if (content) {
  168. setWbwData(JSON.parse(content));
  169. }
  170. }, []);
  171. const channelsId = translation?.map((item) => item.channel.id);
  172. const content = (
  173. <SentContent
  174. sid={id}
  175. book={book}
  176. para={para}
  177. wordStart={wordStart}
  178. wordEnd={wordEnd}
  179. origin={origin}
  180. translation={translation}
  181. answer={answer}
  182. layout={layout}
  183. magicDict={magicDict}
  184. compact={isCompact}
  185. mode={articleMode}
  186. wbwProgress={showWbwProgress}
  187. readonly={readonly}
  188. onWbwChange={(data: IWbw[]) => {
  189. setWbwData(data);
  190. }}
  191. onMagicDictDone={() => {
  192. setMagicDictLoading(false);
  193. setMagicDict(undefined);
  194. }}
  195. onTranslationChange={onTranslationChange}
  196. />
  197. );
  198. return (
  199. <div
  200. ref={divRef}
  201. className={`sent-edit-inner` + (isFocus ? " sent-focus" : "")}
  202. style={{
  203. display: commentaryLayout === "column" ? "block" : "flex",
  204. width: commentaryLayout === "column" ? "100%" : SENTENCE_FIX_WIDTH,
  205. }}
  206. >
  207. <div>
  208. {affix || rootFixed === true ? (
  209. <Affix offsetTop={44}>
  210. <div className="affix">{content}</div>
  211. </Affix>
  212. ) : (
  213. content
  214. )}
  215. <div
  216. style={{
  217. width: commentaryLayout === "column" ? "unset" : SENTENCE_FIX_WIDTH,
  218. }}
  219. >
  220. <SentTab
  221. id={id}
  222. book={book}
  223. para={para}
  224. wordStart={wordStart}
  225. wordEnd={wordEnd}
  226. channelsId={channelsId}
  227. path={path}
  228. tranNum={tranNum}
  229. nissayaNum={nissayaNum}
  230. commNum={commNum}
  231. originNum={originNum}
  232. simNum={simNum}
  233. loadedRes={loadedRes}
  234. wbwData={wbwData}
  235. origin={origin}
  236. magicDictLoading={magicDictLoading}
  237. compact={isCompact}
  238. mode={articleMode}
  239. onMagicDict={(type: string) => {
  240. setMagicDict(type);
  241. setMagicDictLoading(true);
  242. }}
  243. onCompact={(value: boolean) => setIsCompact(value)}
  244. onModeChange={(value: ArticleMode | undefined) =>
  245. setArticleMode(value)
  246. }
  247. onAffix={() => setAffix(!affix)}
  248. />
  249. </div>
  250. </div>
  251. <div className="pcd_sent_commentary">
  252. {commentaries?.map((item, id) => {
  253. return (
  254. <SentCell
  255. value={item}
  256. key={id}
  257. isPr={false}
  258. editMode={item.openInEditMode}
  259. />
  260. );
  261. })}
  262. </div>
  263. </div>
  264. );
  265. };
  266. interface IWidgetSentEdit {
  267. props: string;
  268. }
  269. const Widget = ({ props }: IWidgetSentEdit) => {
  270. const prop = JSON.parse(atob(props)) as IWidgetSentEditInner;
  271. //console.log("sent data", prop);
  272. return <SentEditInner {...prop} />;
  273. };
  274. export default Widget;