modal.tsx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. import { useCallback, useMemo } from 'react';
  2. import { SWRResponse } from 'swr';
  3. import Linker from '~/client/models/Linker';
  4. import MarkdownTable from '~/client/models/MarkdownTable';
  5. import { BookmarkFolderItems } from '~/interfaces/bookmark-info';
  6. import { IPageToDeleteWithMeta, IPageToRenameWithMeta } from '~/interfaces/page';
  7. import {
  8. OnDuplicatedFunction, OnRenamedFunction, OnDeletedFunction, OnPutBackedFunction, onDeletedBookmarkFolderFunction,
  9. } from '~/interfaces/ui';
  10. import { IUserGroupHasId } from '~/interfaces/user';
  11. import loggerFactory from '~/utils/logger';
  12. import { useStaticSWR } from './use-static-swr';
  13. const logger = loggerFactory('growi:stores:modal');
  14. /*
  15. * PageCreateModal
  16. */
  17. type CreateModalStatus = {
  18. isOpened: boolean,
  19. path?: string,
  20. }
  21. type CreateModalStatusUtils = {
  22. open(path?: string): Promise<CreateModalStatus | undefined>
  23. close(): Promise<CreateModalStatus | undefined>
  24. }
  25. export const usePageCreateModal = (status?: CreateModalStatus): SWRResponse<CreateModalStatus, Error> & CreateModalStatusUtils => {
  26. const initialData: CreateModalStatus = { isOpened: false };
  27. const swrResponse = useStaticSWR<CreateModalStatus, Error>('pageCreateModalStatus', status, { fallbackData: initialData });
  28. return {
  29. ...swrResponse,
  30. open: (path?: string) => swrResponse.mutate({ isOpened: true, path }),
  31. close: () => swrResponse.mutate({ isOpened: false }),
  32. };
  33. };
  34. /*
  35. * PageDeleteModal
  36. */
  37. export type IDeleteModalOption = {
  38. onDeleted?: OnDeletedFunction,
  39. }
  40. type DeleteModalStatus = {
  41. isOpened: boolean,
  42. pages?: IPageToDeleteWithMeta[],
  43. opts?: IDeleteModalOption,
  44. }
  45. type DeleteModalStatusUtils = {
  46. open(
  47. pages?: IPageToDeleteWithMeta[],
  48. opts?: IDeleteModalOption,
  49. ): Promise<DeleteModalStatus | undefined>,
  50. close(): Promise<DeleteModalStatus | undefined>,
  51. }
  52. export const usePageDeleteModal = (status?: DeleteModalStatus): SWRResponse<DeleteModalStatus, Error> & DeleteModalStatusUtils => {
  53. const initialData: DeleteModalStatus = {
  54. isOpened: false,
  55. pages: [],
  56. };
  57. const swrResponse = useStaticSWR<DeleteModalStatus, Error>('deleteModalStatus', status, { fallbackData: initialData });
  58. return {
  59. ...swrResponse,
  60. open: (
  61. pages?: IPageToDeleteWithMeta[],
  62. opts?: IDeleteModalOption,
  63. ) => swrResponse.mutate({
  64. isOpened: true, pages, opts,
  65. }),
  66. close: () => swrResponse.mutate({ isOpened: false }),
  67. };
  68. };
  69. /*
  70. * EmptyTrashModal
  71. */
  72. type IEmptyTrashModalOption = {
  73. onEmptiedTrash?: () => void,
  74. canDeleteAllPages: boolean,
  75. }
  76. type EmptyTrashModalStatus = {
  77. isOpened: boolean,
  78. pages?: IPageToDeleteWithMeta[],
  79. opts?: IEmptyTrashModalOption,
  80. }
  81. type EmptyTrashModalStatusUtils = {
  82. open(
  83. pages?: IPageToDeleteWithMeta[],
  84. opts?: IEmptyTrashModalOption,
  85. ): Promise<EmptyTrashModalStatus | undefined>,
  86. close(): Promise<EmptyTrashModalStatus | undefined>,
  87. }
  88. export const useEmptyTrashModal = (status?: EmptyTrashModalStatus): SWRResponse<EmptyTrashModalStatus, Error> & EmptyTrashModalStatusUtils => {
  89. const initialData: EmptyTrashModalStatus = {
  90. isOpened: false,
  91. pages: [],
  92. };
  93. const swrResponse = useStaticSWR<EmptyTrashModalStatus, Error>('emptyTrashModalStatus', status, { fallbackData: initialData });
  94. return {
  95. ...swrResponse,
  96. open: (
  97. pages?: IPageToDeleteWithMeta[],
  98. opts?: IEmptyTrashModalOption,
  99. ) => swrResponse.mutate({
  100. isOpened: true, pages, opts,
  101. }),
  102. close: () => swrResponse.mutate({ isOpened: false }),
  103. };
  104. };
  105. /*
  106. * PageDuplicateModal
  107. */
  108. export type IPageForPageDuplicateModal = {
  109. pageId: string,
  110. path: string
  111. }
  112. export type IDuplicateModalOption = {
  113. onDuplicated?: OnDuplicatedFunction,
  114. }
  115. type DuplicateModalStatus = {
  116. isOpened: boolean,
  117. page?: IPageForPageDuplicateModal,
  118. opts?: IDuplicateModalOption,
  119. }
  120. type DuplicateModalStatusUtils = {
  121. open(
  122. page?: IPageForPageDuplicateModal,
  123. opts?: IDuplicateModalOption
  124. ): Promise<DuplicateModalStatus | undefined>
  125. close(): Promise<DuplicateModalStatus | undefined>
  126. }
  127. export const usePageDuplicateModal = (status?: DuplicateModalStatus): SWRResponse<DuplicateModalStatus, Error> & DuplicateModalStatusUtils => {
  128. const initialData: DuplicateModalStatus = { isOpened: false };
  129. const swrResponse = useStaticSWR<DuplicateModalStatus, Error>('duplicateModalStatus', status, { fallbackData: initialData });
  130. return {
  131. ...swrResponse,
  132. open: (
  133. page?: IPageForPageDuplicateModal,
  134. opts?: IDuplicateModalOption,
  135. ) => swrResponse.mutate({ isOpened: true, page, opts }),
  136. close: () => swrResponse.mutate({ isOpened: false }),
  137. };
  138. };
  139. /*
  140. * PageRenameModal
  141. */
  142. export type IRenameModalOption = {
  143. onRenamed?: OnRenamedFunction,
  144. }
  145. type RenameModalStatus = {
  146. isOpened: boolean,
  147. page?: IPageToRenameWithMeta,
  148. opts?: IRenameModalOption
  149. }
  150. type RenameModalStatusUtils = {
  151. open(
  152. page?: IPageToRenameWithMeta,
  153. opts?: IRenameModalOption
  154. ): Promise<RenameModalStatus | undefined>
  155. close(): Promise<RenameModalStatus | undefined>
  156. }
  157. export const usePageRenameModal = (status?: RenameModalStatus): SWRResponse<RenameModalStatus, Error> & RenameModalStatusUtils => {
  158. const initialData: RenameModalStatus = { isOpened: false };
  159. const swrResponse = useStaticSWR<RenameModalStatus, Error>('renameModalStatus', status, { fallbackData: initialData });
  160. return {
  161. ...swrResponse,
  162. open: (
  163. page?: IPageToRenameWithMeta,
  164. opts?: IRenameModalOption,
  165. ) => swrResponse.mutate({
  166. isOpened: true, page, opts,
  167. }),
  168. close: () => swrResponse.mutate({ isOpened: false }),
  169. };
  170. };
  171. /*
  172. * PutBackPageModal
  173. */
  174. export type IPageForPagePutBackModal = {
  175. pageId: string,
  176. path: string
  177. }
  178. export type IPutBackPageModalOption = {
  179. onPutBacked?: OnPutBackedFunction,
  180. }
  181. type PutBackPageModalStatus = {
  182. isOpened: boolean,
  183. page?: IPageForPagePutBackModal,
  184. opts?: IPutBackPageModalOption,
  185. }
  186. type PutBackPageModalUtils = {
  187. open(
  188. page?: IPageForPagePutBackModal,
  189. opts?: IPutBackPageModalOption,
  190. ): Promise<PutBackPageModalStatus | undefined>
  191. close(): Promise<PutBackPageModalStatus | undefined>
  192. }
  193. export const usePutBackPageModal = (status?: PutBackPageModalStatus): SWRResponse<PutBackPageModalStatus, Error> & PutBackPageModalUtils => {
  194. const initialData: PutBackPageModalStatus = useMemo(() => ({
  195. isOpened: false,
  196. page: { pageId: '', path: '' },
  197. }), []);
  198. const swrResponse = useStaticSWR<PutBackPageModalStatus, Error>('putBackPageModalStatus', status, { fallbackData: initialData });
  199. return {
  200. ...swrResponse,
  201. open: (
  202. page: IPageForPagePutBackModal, opts?: IPutBackPageModalOption,
  203. ) => swrResponse.mutate({
  204. isOpened: true, page, opts,
  205. }),
  206. close: () => swrResponse.mutate({ isOpened: false, page: { pageId: '', path: '' } }),
  207. };
  208. };
  209. /*
  210. * PagePresentationModal
  211. */
  212. type PresentationModalStatus = {
  213. isOpened: boolean,
  214. }
  215. type PresentationModalStatusUtils = {
  216. open(): Promise<PresentationModalStatus | undefined>
  217. close(): Promise<PresentationModalStatus | undefined>
  218. }
  219. export const usePagePresentationModal = (
  220. status?: PresentationModalStatus,
  221. ): SWRResponse<PresentationModalStatus, Error> & PresentationModalStatusUtils => {
  222. const initialData: PresentationModalStatus = {
  223. isOpened: false,
  224. };
  225. const swrResponse = useStaticSWR<PresentationModalStatus, Error>('presentationModalStatus', status, { fallbackData: initialData });
  226. return {
  227. ...swrResponse,
  228. open: () => swrResponse.mutate({ isOpened: true }, { revalidate: true }),
  229. close: () => swrResponse.mutate({ isOpened: false }),
  230. };
  231. };
  232. /*
  233. * PrivateLegacyPagesMigrationModal
  234. */
  235. export type ILegacyPrivatePage = { pageId: string, path: string };
  236. export type PrivateLegacyPagesMigrationModalSubmitedHandler = (pages: ILegacyPrivatePage[], isRecursively?: boolean) => void;
  237. type PrivateLegacyPagesMigrationModalStatus = {
  238. isOpened: boolean,
  239. pages?: ILegacyPrivatePage[],
  240. onSubmited?: PrivateLegacyPagesMigrationModalSubmitedHandler,
  241. }
  242. type PrivateLegacyPagesMigrationModalStatusUtils = {
  243. open(pages: ILegacyPrivatePage[], onSubmited?: PrivateLegacyPagesMigrationModalSubmitedHandler): Promise<PrivateLegacyPagesMigrationModalStatus | undefined>,
  244. close(): Promise<PrivateLegacyPagesMigrationModalStatus | undefined>,
  245. }
  246. export const usePrivateLegacyPagesMigrationModal = (
  247. status?: PrivateLegacyPagesMigrationModalStatus,
  248. ): SWRResponse<PrivateLegacyPagesMigrationModalStatus, Error> & PrivateLegacyPagesMigrationModalStatusUtils => {
  249. const initialData: PrivateLegacyPagesMigrationModalStatus = {
  250. isOpened: false,
  251. pages: [],
  252. };
  253. const swrResponse = useStaticSWR<PrivateLegacyPagesMigrationModalStatus, Error>('privateLegacyPagesMigrationModal', status, { fallbackData: initialData });
  254. return {
  255. ...swrResponse,
  256. open: (pages, onSubmited?) => swrResponse.mutate({
  257. isOpened: true, pages, onSubmited,
  258. }),
  259. close: () => swrResponse.mutate({ isOpened: false, pages: [], onSubmited: undefined }),
  260. };
  261. };
  262. /*
  263. * DescendantsPageListModal
  264. */
  265. type DescendantsPageListModalStatus = {
  266. isOpened: boolean,
  267. path?: string,
  268. }
  269. type DescendantsPageListUtils = {
  270. open(path: string): Promise<DescendantsPageListModalStatus | undefined>
  271. close(): Promise<DuplicateModalStatus | undefined>
  272. }
  273. export const useDescendantsPageListModal = (
  274. status?: DescendantsPageListModalStatus,
  275. ): SWRResponse<DescendantsPageListModalStatus, Error> & DescendantsPageListUtils => {
  276. const initialData: DescendantsPageListModalStatus = { isOpened: false };
  277. const swrResponse = useStaticSWR<DescendantsPageListModalStatus, Error>('descendantsPageListModalStatus', status, { fallbackData: initialData });
  278. return {
  279. ...swrResponse,
  280. open: (path: string) => swrResponse.mutate({ isOpened: true, path }),
  281. close: () => swrResponse.mutate({ isOpened: false }),
  282. };
  283. };
  284. /*
  285. * PageAccessoriesModal
  286. */
  287. export const PageAccessoriesModalContents = {
  288. PageHistory: 'PageHistory',
  289. Attachment: 'Attachment',
  290. ShareLink: 'ShareLink',
  291. } as const;
  292. export type PageAccessoriesModalContents = typeof PageAccessoriesModalContents[keyof typeof PageAccessoriesModalContents];
  293. type PageAccessoriesModalStatus = {
  294. isOpened: boolean,
  295. activatedContents?: PageAccessoriesModalContents,
  296. }
  297. type PageAccessoriesModalUtils = {
  298. open(activatedContents: PageAccessoriesModalContents): void
  299. close(): void
  300. }
  301. export const usePageAccessoriesModal = (): SWRResponse<PageAccessoriesModalStatus, Error> & PageAccessoriesModalUtils => {
  302. const initialStatus = { isOpened: false };
  303. const swrResponse = useStaticSWR<PageAccessoriesModalStatus, Error>('pageAccessoriesModalStatus', undefined, { fallbackData: initialStatus });
  304. return {
  305. ...swrResponse,
  306. open: (activatedContents: PageAccessoriesModalContents) => {
  307. if (swrResponse.data == null) {
  308. return;
  309. }
  310. swrResponse.mutate({
  311. isOpened: true,
  312. activatedContents,
  313. });
  314. },
  315. close: () => {
  316. if (swrResponse.data == null) {
  317. return;
  318. }
  319. swrResponse.mutate({ isOpened: false });
  320. },
  321. };
  322. };
  323. /*
  324. * UpdateUserGroupConfirmModal
  325. */
  326. type UpdateUserGroupConfirmModalStatus = {
  327. isOpened: boolean,
  328. targetGroup?: IUserGroupHasId,
  329. updateData?: Partial<IUserGroupHasId>,
  330. onConfirm?: (targetGroup: IUserGroupHasId, updateData: Partial<IUserGroupHasId>, forceUpdateParents: boolean) => any,
  331. }
  332. type UpdateUserGroupConfirmModalUtils = {
  333. open(targetGroup: IUserGroupHasId, updateData: Partial<IUserGroupHasId>, onConfirm?: (...args: any[]) => any): Promise<void>,
  334. close(): Promise<void>,
  335. }
  336. export const useUpdateUserGroupConfirmModal = (): SWRResponse<UpdateUserGroupConfirmModalStatus, Error> & UpdateUserGroupConfirmModalUtils => {
  337. const initialStatus: UpdateUserGroupConfirmModalStatus = { isOpened: false };
  338. const swrResponse = useStaticSWR<UpdateUserGroupConfirmModalStatus, Error>('updateParentConfirmModal', undefined, { fallbackData: initialStatus });
  339. return {
  340. ...swrResponse,
  341. async open(targetGroup: IUserGroupHasId, updateData: Partial<IUserGroupHasId>, onConfirm?: (...args: any[]) => any) {
  342. await swrResponse.mutate({
  343. isOpened: true, targetGroup, updateData, onConfirm,
  344. });
  345. },
  346. async close() {
  347. await swrResponse.mutate({ isOpened: false });
  348. },
  349. };
  350. };
  351. /*
  352. * ShortcutsModal
  353. */
  354. type ShortcutsModalStatus = {
  355. isOpened: boolean,
  356. }
  357. type ShortcutsModalUtils = {
  358. open(): void,
  359. close(): void,
  360. }
  361. export const useShortcutsModal = (): SWRResponse<ShortcutsModalStatus, Error> & ShortcutsModalUtils => {
  362. const initialStatus: ShortcutsModalStatus = { isOpened: false };
  363. const swrResponse = useStaticSWR<ShortcutsModalStatus, Error>('shortcutsModal', undefined, { fallbackData: initialStatus });
  364. return {
  365. ...swrResponse,
  366. open() {
  367. swrResponse.mutate({ isOpened: true });
  368. },
  369. close() {
  370. swrResponse.mutate({ isOpened: false });
  371. },
  372. };
  373. };
  374. /*
  375. * DrawioModal
  376. */
  377. type DrawioModalSaveHandler = (drawioMxFile: string) => void;
  378. type DrawioModalStatus = {
  379. isOpened: boolean,
  380. drawioMxFile: string,
  381. onSave?: DrawioModalSaveHandler,
  382. }
  383. type DrawioModalStatusUtils = {
  384. open(
  385. drawioMxFile: string,
  386. onSave?: DrawioModalSaveHandler,
  387. ): void,
  388. close(): void,
  389. }
  390. export const useDrawioModal = (status?: DrawioModalStatus): SWRResponse<DrawioModalStatus, Error> & DrawioModalStatusUtils => {
  391. const initialData: DrawioModalStatus = {
  392. isOpened: false,
  393. drawioMxFile: '',
  394. };
  395. const swrResponse = useStaticSWR<DrawioModalStatus, Error>('drawioModalStatus', status, { fallbackData: initialData });
  396. const { mutate } = swrResponse;
  397. const open = useCallback((drawioMxFile: string, onSave?: DrawioModalSaveHandler): void => {
  398. mutate({ isOpened: true, drawioMxFile, onSave });
  399. }, [mutate]);
  400. const close = useCallback((): void => {
  401. mutate({ isOpened: false, drawioMxFile: '', onSave: undefined });
  402. }, [mutate]);
  403. return {
  404. ...swrResponse,
  405. open,
  406. close,
  407. };
  408. };
  409. /*
  410. * HandsonTableModal
  411. */
  412. type HandsonTableModalSaveHandler = (table: MarkdownTable) => void;
  413. type HandsontableModalStatus = {
  414. isOpened: boolean,
  415. table: MarkdownTable,
  416. // TODO: Define editor type
  417. editor?: any,
  418. autoFormatMarkdownTable?: boolean,
  419. // onSave is passed only when editing table directly from the page.
  420. onSave?: HandsonTableModalSaveHandler
  421. }
  422. type HandsontableModalStatusUtils = {
  423. open(
  424. table: MarkdownTable,
  425. editor?: any,
  426. autoFormatMarkdownTable?: boolean,
  427. onSave?: HandsonTableModalSaveHandler
  428. ): void
  429. close(): void
  430. }
  431. const defaultMarkdownTable = () => {
  432. return new MarkdownTable(
  433. [
  434. ['col1', 'col2', 'col3'],
  435. ['', '', ''],
  436. ['', '', ''],
  437. ],
  438. {
  439. align: ['', '', ''],
  440. },
  441. );
  442. };
  443. export const useHandsontableModal = (status?: HandsontableModalStatus): SWRResponse<HandsontableModalStatus, Error> & HandsontableModalStatusUtils => {
  444. const initialData: HandsontableModalStatus = {
  445. isOpened: false,
  446. table: defaultMarkdownTable(),
  447. editor: undefined,
  448. autoFormatMarkdownTable: false,
  449. };
  450. const swrResponse = useStaticSWR<HandsontableModalStatus, Error>('handsontableModalStatus', status, { fallbackData: initialData });
  451. const { mutate } = swrResponse;
  452. const open = useCallback((table: MarkdownTable, editor?: any, autoFormatMarkdownTable?: boolean, onSave?: HandsonTableModalSaveHandler): void => {
  453. mutate({
  454. isOpened: true, table, editor, autoFormatMarkdownTable, onSave,
  455. });
  456. }, [mutate]);
  457. const close = useCallback((): void => {
  458. mutate({
  459. isOpened: false, table: defaultMarkdownTable(), editor: undefined, autoFormatMarkdownTable: false, onSave: undefined,
  460. });
  461. }, [mutate]);
  462. return {
  463. ...swrResponse,
  464. open,
  465. close,
  466. };
  467. };
  468. /*
  469. * ConflictDiffModal
  470. */
  471. type ConflictDiffModalStatus = {
  472. isOpened: boolean,
  473. }
  474. type ConflictDiffModalUtils = {
  475. open(): void,
  476. close(): void,
  477. }
  478. export const useConflictDiffModal = (): SWRResponse<ConflictDiffModalStatus, Error> & ConflictDiffModalUtils => {
  479. const initialStatus: ConflictDiffModalStatus = { isOpened: false };
  480. const swrResponse = useStaticSWR<ConflictDiffModalStatus, Error>('conflictDiffModal', undefined, { fallbackData: initialStatus });
  481. return Object.assign(swrResponse, {
  482. open: () => {
  483. swrResponse.mutate({ isOpened: true });
  484. },
  485. close: () => {
  486. swrResponse.mutate({ isOpened: false });
  487. },
  488. });
  489. };
  490. /*
  491. * BookmarkFolderDeleteModal
  492. */
  493. export type IDeleteBookmarkFolderModalOption = {
  494. onDeleted?: onDeletedBookmarkFolderFunction,
  495. }
  496. type DeleteBookmarkFolderModalStatus = {
  497. isOpened: boolean,
  498. bookmarkFolder?: BookmarkFolderItems,
  499. opts?: IDeleteBookmarkFolderModalOption,
  500. }
  501. type DeleteModalBookmarkFolderStatusUtils = {
  502. open(
  503. bookmarkFolder?: BookmarkFolderItems,
  504. opts?: IDeleteBookmarkFolderModalOption,
  505. ): Promise<DeleteBookmarkFolderModalStatus | undefined>,
  506. close(): Promise<DeleteBookmarkFolderModalStatus | undefined>,
  507. }
  508. export const useBookmarkFolderDeleteModal = (status?: DeleteBookmarkFolderModalStatus):
  509. SWRResponse<DeleteBookmarkFolderModalStatus, Error> & DeleteModalBookmarkFolderStatusUtils => {
  510. const initialData: DeleteBookmarkFolderModalStatus = {
  511. isOpened: false,
  512. };
  513. const swrResponse = useStaticSWR<DeleteBookmarkFolderModalStatus, Error>('deleteBookmarkFolderModalStatus', status, { fallbackData: initialData });
  514. return {
  515. ...swrResponse,
  516. open: (
  517. bookmarkFolder?: BookmarkFolderItems,
  518. opts?: IDeleteBookmarkFolderModalOption,
  519. ) => swrResponse.mutate({
  520. isOpened: true, bookmarkFolder, opts,
  521. }),
  522. close: () => swrResponse.mutate({ isOpened: false }),
  523. };
  524. };
  525. /*
  526. * TemplateModal
  527. */
  528. type TemplateModalStatus = {
  529. isOpened: boolean,
  530. onSubmit?: (templateText: string) => void
  531. }
  532. type TemplateModalUtils = {
  533. open(onSubmit: (templateText: string) => void): void,
  534. close(): void,
  535. }
  536. export const useTemplateModal = (): SWRResponse<TemplateModalStatus, Error> & TemplateModalUtils => {
  537. const initialStatus: TemplateModalStatus = { isOpened: false };
  538. const swrResponse = useStaticSWR<TemplateModalStatus, Error>('templateModal', undefined, { fallbackData: initialStatus });
  539. return Object.assign(swrResponse, {
  540. open: (onSubmit: (templateText: string) => void) => {
  541. swrResponse.mutate({ isOpened: true, onSubmit });
  542. },
  543. close: () => {
  544. swrResponse.mutate({ isOpened: false });
  545. },
  546. });
  547. };
  548. /*
  549. * LinkEditModal
  550. */
  551. type LinkEditModalStatus = {
  552. isOpened: boolean,
  553. defaultMarkdownLink?: Linker,
  554. onSave?: (linkText: string) => void
  555. }
  556. type LinkEditModalUtils = {
  557. open(defaultMarkdownLink: Linker, onSave: (linkText: string) => void): void,
  558. close(): void,
  559. }
  560. export const useLinkEditModal = (): SWRResponse<LinkEditModalStatus, Error> & LinkEditModalUtils => {
  561. const initialStatus: LinkEditModalStatus = { isOpened: false };
  562. const swrResponse = useStaticSWR<LinkEditModalStatus, Error>('linkEditModal', undefined, { fallbackData: initialStatus });
  563. return Object.assign(swrResponse, {
  564. open: (defaultMarkdownLink: Linker, onSave: (linkText: string) => void) => {
  565. swrResponse.mutate({ isOpened: true, defaultMarkdownLink, onSave });
  566. },
  567. close: () => {
  568. swrResponse.mutate({ isOpened: false });
  569. },
  570. });
  571. };