editor-context.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import {
  2. createContext,
  3. useCallback,
  4. useContext,
  5. useEffect,
  6. useMemo,
  7. } from 'react'
  8. import PropTypes from 'prop-types'
  9. import useScopeValue from '../hooks/use-scope-value'
  10. import useBrowserWindow from '../hooks/use-browser-window'
  11. import { useIdeContext } from './ide-context'
  12. import { useProjectContext } from './project-context'
  13. import { useDetachContext } from './detach-context'
  14. import getMeta from '../../utils/meta'
  15. import { useUserContext } from './user-context'
  16. export const EditorContext = createContext()
  17. EditorContext.Provider.propTypes = {
  18. value: PropTypes.shape({
  19. cobranding: PropTypes.shape({
  20. logoImgUrl: PropTypes.string.isRequired,
  21. brandVariationName: PropTypes.string.isRequired,
  22. brandVariationId: PropTypes.number.isRequired,
  23. brandId: PropTypes.number.isRequired,
  24. brandVariationHomeUrl: PropTypes.string.isRequired,
  25. publishGuideHtml: PropTypes.string,
  26. partner: PropTypes.string,
  27. brandedMenu: PropTypes.bool,
  28. submitBtnHtml: PropTypes.string,
  29. }),
  30. hasPremiumCompile: PropTypes.bool,
  31. loading: PropTypes.bool,
  32. renameProject: PropTypes.func.isRequired,
  33. showSymbolPalette: PropTypes.bool,
  34. toggleSymbolPalette: PropTypes.func,
  35. insertSymbol: PropTypes.func,
  36. isProjectOwner: PropTypes.bool,
  37. isRestrictedTokenMember: PropTypes.bool,
  38. permissionsLevel: PropTypes.oneOf(['readOnly', 'readAndWrite', 'owner']),
  39. }),
  40. }
  41. export function EditorProvider({ children, settings }) {
  42. const ide = useIdeContext()
  43. const { id: userId } = useUserContext()
  44. const { role } = useDetachContext()
  45. const { owner, features } = useProjectContext({
  46. owner: PropTypes.shape({
  47. _id: PropTypes.string.isRequired,
  48. }),
  49. features: PropTypes.shape({
  50. compileGroup: PropTypes.string,
  51. }),
  52. })
  53. const cobranding = useMemo(() => {
  54. if (window.brandVariation) {
  55. return {
  56. logoImgUrl: window.brandVariation.logo_url,
  57. brandVariationName: window.brandVariation.name,
  58. brandVariationId: window.brandVariation.id,
  59. brandId: window.brandVariation.brand_id,
  60. brandVariationHomeUrl: window.brandVariation.home_url,
  61. publishGuideHtml: window.brandVariation.publish_guide_html,
  62. partner: window.brandVariation.partner,
  63. brandedMenu: window.brandVariation.branded_menu,
  64. submitBtnHtml: window.brandVariation.submit_button_html,
  65. }
  66. } else {
  67. return undefined
  68. }
  69. }, [])
  70. const [loading] = useScopeValue('state.loading')
  71. const [projectName, setProjectName] = useScopeValue('project.name')
  72. const [permissionsLevel] = useScopeValue('permissionsLevel')
  73. const [showSymbolPalette] = useScopeValue('editor.showSymbolPalette')
  74. const [toggleSymbolPalette] = useScopeValue('editor.toggleSymbolPalette')
  75. useEffect(() => {
  76. if (ide?.socket) {
  77. ide.socket.on('projectNameUpdated', setProjectName)
  78. return () =>
  79. ide.socket.removeListener('projectNameUpdated', setProjectName)
  80. }
  81. }, [ide?.socket, setProjectName])
  82. const renameProject = useCallback(
  83. newName => {
  84. setProjectName(oldName => {
  85. if (oldName !== newName) {
  86. settings.saveProjectSettings({ name: newName }).catch(response => {
  87. setProjectName(oldName)
  88. const { data, status } = response
  89. if (status === 400) {
  90. return ide.showGenericMessageModal('Error renaming project', data)
  91. } else {
  92. return ide.showGenericMessageModal(
  93. 'Error renaming project',
  94. 'Please try again in a moment'
  95. )
  96. }
  97. })
  98. }
  99. return newName
  100. })
  101. },
  102. [settings, ide, setProjectName]
  103. )
  104. const { setTitle } = useBrowserWindow()
  105. useEffect(() => {
  106. const parts = []
  107. if (role === 'detached') {
  108. parts.push('[PDF]')
  109. }
  110. if (projectName) {
  111. parts.push(projectName)
  112. parts.push('-')
  113. }
  114. parts.push('Online LaTeX Editor')
  115. parts.push(window.ExposedSettings.appName)
  116. const title = parts.join(' ')
  117. setTitle(title)
  118. }, [projectName, setTitle, role])
  119. const insertSymbol = useCallback(symbol => {
  120. window.dispatchEvent(
  121. new CustomEvent('editor:insert-symbol', {
  122. detail: symbol,
  123. })
  124. )
  125. }, [])
  126. const value = useMemo(
  127. () => ({
  128. cobranding,
  129. hasPremiumCompile: features?.compileGroup === 'priority',
  130. loading,
  131. renameProject,
  132. permissionsLevel,
  133. isProjectOwner: owner?._id === userId,
  134. isRestrictedTokenMember: getMeta('ol-isRestrictedTokenMember'),
  135. showSymbolPalette,
  136. toggleSymbolPalette,
  137. insertSymbol,
  138. }),
  139. [
  140. cobranding,
  141. features?.compileGroup,
  142. owner,
  143. userId,
  144. loading,
  145. renameProject,
  146. permissionsLevel,
  147. showSymbolPalette,
  148. toggleSymbolPalette,
  149. insertSymbol,
  150. ]
  151. )
  152. return (
  153. <EditorContext.Provider value={value}>{children}</EditorContext.Provider>
  154. )
  155. }
  156. EditorProvider.propTypes = {
  157. children: PropTypes.any,
  158. settings: PropTypes.object,
  159. }
  160. export function useEditorContext(propTypes) {
  161. const context = useContext(EditorContext)
  162. if (!context) {
  163. throw new Error('useEditorContext is only available inside EditorProvider')
  164. }
  165. PropTypes.checkPropTypes(propTypes, context, 'data', 'EditorContext.Provider')
  166. return context
  167. }