editor-context.tsx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import {
  2. createContext,
  3. Dispatch,
  4. FC,
  5. SetStateAction,
  6. useCallback,
  7. useContext,
  8. useEffect,
  9. useMemo,
  10. useState,
  11. } from 'react'
  12. import useBrowserWindow from '../hooks/use-browser-window'
  13. import { useProjectContext } from './project-context'
  14. import { useDetachContext } from './detach-context'
  15. import getMeta from '../../utils/meta'
  16. import { useUserContext } from './user-context'
  17. import { saveProjectSettings } from '@/features/editor-left-menu/utils/api'
  18. import { useModalsContext } from '@/features/ide-react/context/modals-context'
  19. import { WritefullAPI } from './types/writefull-instance'
  20. import { Cobranding } from '../../../../types/cobranding'
  21. import { SymbolWithCharacter } from '../../../../modules/symbol-palette/frontend/js/data/symbols'
  22. export const EditorContext = createContext<
  23. | {
  24. cobranding?: Cobranding
  25. hasPremiumCompile?: boolean
  26. renameProject: (newName: string) => void
  27. insertSymbol?: (symbol: SymbolWithCharacter) => void
  28. isProjectOwner: boolean
  29. isRestrictedTokenMember?: boolean
  30. isPendingEditor: boolean
  31. hasPremiumSuggestion: boolean
  32. setHasPremiumSuggestion: (value: boolean) => void
  33. setPremiumSuggestionResetDate: (date: Date) => void
  34. premiumSuggestionResetDate: Date
  35. writefullInstance: WritefullAPI | null
  36. setWritefullInstance: (instance: WritefullAPI) => void
  37. showUpgradeModal: boolean
  38. setShowUpgradeModal: Dispatch<SetStateAction<boolean>>
  39. }
  40. | undefined
  41. >(undefined)
  42. export const EditorProvider: FC<React.PropsWithChildren> = ({ children }) => {
  43. const { id: userId, featureUsage } = useUserContext()
  44. const { role } = useDetachContext()
  45. const { showGenericMessageModal } = useModalsContext()
  46. const {
  47. features,
  48. projectId,
  49. project,
  50. name: projectName,
  51. updateProject,
  52. } = useProjectContext()
  53. const { owner, members } = project || {}
  54. const cobranding = useMemo(() => {
  55. const brandVariation = getMeta('ol-brandVariation')
  56. return (
  57. brandVariation && {
  58. logoImgUrl: brandVariation.logo_url,
  59. brandVariationName: brandVariation.name,
  60. brandVariationId: brandVariation.id,
  61. brandId: brandVariation.brand_id,
  62. brandVariationHomeUrl: brandVariation.home_url,
  63. publishGuideHtml: brandVariation.publish_guide_html,
  64. partner: brandVariation.partner,
  65. brandedMenu: brandVariation.branded_menu,
  66. submitBtnHtml: brandVariation.submit_button_html,
  67. submitBtnHtmlNoBreaks: brandVariation.submit_button_html_no_br,
  68. }
  69. )
  70. }, [])
  71. const [hasPremiumSuggestion, setHasPremiumSuggestion] = useState<boolean>(
  72. () => {
  73. return Boolean(
  74. featureUsage?.aiErrorAssistant &&
  75. featureUsage?.aiErrorAssistant.remainingUsage > 0
  76. )
  77. }
  78. )
  79. const [premiumSuggestionResetDate, setPremiumSuggestionResetDate] =
  80. useState<Date>(() => {
  81. return featureUsage?.aiErrorAssistant?.resetDate
  82. ? new Date(featureUsage.aiErrorAssistant.resetDate)
  83. : new Date()
  84. })
  85. const [showUpgradeModal, setShowUpgradeModal] = useState(false)
  86. const isPendingEditor = useMemo(
  87. () =>
  88. Boolean(
  89. members?.some(
  90. member =>
  91. member._id === userId &&
  92. (member.pendingEditor || member.pendingReviewer)
  93. )
  94. ),
  95. [members, userId]
  96. )
  97. const renameProject = useCallback(
  98. (newName: string) => {
  99. const oldName = projectName
  100. if (newName !== oldName) {
  101. updateProject({ name: newName })
  102. saveProjectSettings(projectId, { name: newName }).catch(
  103. (response: any) => {
  104. updateProject({ name: oldName })
  105. const { data, status } = response
  106. showGenericMessageModal(
  107. 'Error renaming project',
  108. status === 400 ? data : 'Please try again in a moment'
  109. )
  110. }
  111. )
  112. }
  113. },
  114. [projectName, updateProject, projectId, showGenericMessageModal]
  115. )
  116. const { setTitle } = useBrowserWindow()
  117. useEffect(() => {
  118. const parts = []
  119. if (role === 'detached') {
  120. parts.push('[PDF]')
  121. }
  122. if (projectName) {
  123. parts.push(projectName)
  124. parts.push('-')
  125. }
  126. parts.push('Online LaTeX Editor')
  127. parts.push(getMeta('ol-ExposedSettings').appName)
  128. const title = parts.join(' ')
  129. setTitle(title)
  130. }, [projectName, setTitle, role])
  131. const insertSymbol = useCallback((symbol: SymbolWithCharacter) => {
  132. window.dispatchEvent(
  133. new CustomEvent('editor:insert-symbol', {
  134. detail: symbol,
  135. })
  136. )
  137. }, [])
  138. const [writefullInstance, setWritefullInstance] =
  139. useState<WritefullAPI | null>(null)
  140. const value = useMemo(
  141. () => ({
  142. cobranding,
  143. hasPremiumCompile: features?.compileGroup === 'priority',
  144. renameProject,
  145. isProjectOwner: owner?._id === userId,
  146. isRestrictedTokenMember: getMeta('ol-isRestrictedTokenMember'),
  147. isPendingEditor,
  148. insertSymbol,
  149. hasPremiumSuggestion,
  150. setHasPremiumSuggestion,
  151. premiumSuggestionResetDate,
  152. setPremiumSuggestionResetDate,
  153. writefullInstance,
  154. setWritefullInstance,
  155. showUpgradeModal,
  156. setShowUpgradeModal,
  157. }),
  158. [
  159. cobranding,
  160. features?.compileGroup,
  161. owner,
  162. userId,
  163. renameProject,
  164. isPendingEditor,
  165. insertSymbol,
  166. hasPremiumSuggestion,
  167. setHasPremiumSuggestion,
  168. premiumSuggestionResetDate,
  169. setPremiumSuggestionResetDate,
  170. writefullInstance,
  171. setWritefullInstance,
  172. showUpgradeModal,
  173. setShowUpgradeModal,
  174. ]
  175. )
  176. return (
  177. <EditorContext.Provider value={value}>{children}</EditorContext.Provider>
  178. )
  179. }
  180. export function useEditorContext() {
  181. const context = useContext(EditorContext)
  182. if (!context) {
  183. throw new Error('useEditorContext is only available inside EditorProvider')
  184. }
  185. return context
  186. }