editor-context.tsx 6.0 KB

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