SentEdit.tsx 6.9 KB

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