SentEdit.tsx 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  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. wbwProgress?: boolean;
  93. readonly?: boolean;
  94. }
  95. export const SentEditInner = ({
  96. id,
  97. book,
  98. para,
  99. wordStart,
  100. wordEnd,
  101. channels,
  102. origin,
  103. translation,
  104. answer,
  105. path,
  106. layout = "column",
  107. tranNum,
  108. nissayaNum,
  109. commNum,
  110. originNum,
  111. simNum,
  112. compact = false,
  113. mode,
  114. wbwProgress = false,
  115. readonly = false,
  116. }: IWidgetSentEditInner) => {
  117. const [wbwData, setWbwData] = useState<IWbw[]>();
  118. const [magicDict, setMagicDict] = useState<string>();
  119. const [magicDictLoading, setMagicDictLoading] = useState(false);
  120. const [isCompact, setIsCompact] = useState(compact);
  121. const [articleMode, setArticleMode] = useState<ArticleMode | undefined>(mode);
  122. const [loadedRes, setLoadedRes] = useState<IResNumber>();
  123. const [isFocus, setIsFocus] = useState(false);
  124. const focus = useAppSelector(currFocus);
  125. const divRef = useRef<HTMLDivElement>(null);
  126. const [affix, setAffix] = useState<boolean>(false);
  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={wbwProgress}
  187. readonly={readonly}
  188. onWbwChange={(data: IWbw[]) => {
  189. setWbwData(data);
  190. }}
  191. onMagicDictDone={() => {
  192. setMagicDictLoading(false);
  193. setMagicDict(undefined);
  194. }}
  195. />
  196. );
  197. return (
  198. <Card
  199. ref={divRef}
  200. bodyStyle={{ paddingBottom: 0, paddingLeft: 0, paddingRight: 0 }}
  201. style={{
  202. border: isFocus
  203. ? "2px solid rgb(0 0 200 / 50%)"
  204. : "1px solid rgb(128 128 128 / 10%)",
  205. marginTop: 4,
  206. borderRadius: 6,
  207. backgroundColor: "rgb(255 255 255 / 8%)",
  208. width: "100%",
  209. }}
  210. size="small"
  211. >
  212. {affix ? (
  213. <Affix offsetTop={44}>
  214. <div style={{ backgroundColor: "white" }}>{content}</div>
  215. </Affix>
  216. ) : (
  217. content
  218. )}
  219. <SentTab
  220. id={id}
  221. book={book}
  222. para={para}
  223. wordStart={wordStart}
  224. wordEnd={wordEnd}
  225. channelsId={channelsId}
  226. path={path}
  227. tranNum={tranNum}
  228. nissayaNum={nissayaNum}
  229. commNum={commNum}
  230. originNum={originNum}
  231. simNum={simNum}
  232. loadedRes={loadedRes}
  233. wbwData={wbwData}
  234. magicDictLoading={magicDictLoading}
  235. compact={isCompact}
  236. mode={articleMode}
  237. onMagicDict={(type: string) => {
  238. setMagicDict(type);
  239. setMagicDictLoading(true);
  240. }}
  241. onCompact={(value: boolean) => setIsCompact(value)}
  242. onModeChange={(value: ArticleMode | undefined) => setArticleMode(value)}
  243. onAffix={() => setAffix(!affix)}
  244. />
  245. </Card>
  246. );
  247. };
  248. interface IWidgetSentEdit {
  249. props: string;
  250. }
  251. const Widget = ({ props }: IWidgetSentEdit) => {
  252. const prop = JSON.parse(atob(props)) as IWidgetSentEditInner;
  253. //console.log("sent data", prop);
  254. return <SentEditInner {...prop} />;
  255. };
  256. export default Widget;