editor-navigation-toolbar-root.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import React, { useCallback } from 'react'
  2. import PropTypes from 'prop-types'
  3. import ToolbarHeader from './toolbar-header'
  4. import { useEditorContext } from '../../../shared/context/editor-context'
  5. import { useChatContext } from '../../chat/context/chat-context'
  6. import { useLayoutContext } from '../../../shared/context/layout-context'
  7. import { useUserContext } from '../../../shared/context/user-context'
  8. import { useProjectContext } from '../../../shared/context/project-context'
  9. const userContextPropTypes = {
  10. id: PropTypes.string,
  11. }
  12. const projectContextPropTypes = {
  13. name: PropTypes.string.isRequired,
  14. }
  15. const editorContextPropTypes = {
  16. cobranding: PropTypes.object,
  17. loading: PropTypes.bool,
  18. isRestrictedTokenMember: PropTypes.bool,
  19. renameProject: PropTypes.func.isRequired,
  20. isProjectOwner: PropTypes.bool,
  21. permissionsLevel: PropTypes.string,
  22. }
  23. const layoutContextPropTypes = {
  24. chatIsOpen: PropTypes.bool,
  25. setChatIsOpen: PropTypes.func.isRequired,
  26. reviewPanelOpen: PropTypes.bool,
  27. setReviewPanelOpen: PropTypes.func.isRequired,
  28. view: PropTypes.string,
  29. setView: PropTypes.func.isRequired,
  30. setLeftMenuShown: PropTypes.func.isRequired,
  31. pdfLayout: PropTypes.string.isRequired,
  32. }
  33. const chatContextPropTypes = {
  34. markMessagesAsRead: PropTypes.func.isRequired,
  35. unreadMessageCount: PropTypes.number.isRequired,
  36. }
  37. const EditorNavigationToolbarRoot = React.memo(
  38. function EditorNavigationToolbarRoot({
  39. onlineUsersArray,
  40. openDoc,
  41. openShareProjectModal,
  42. }) {
  43. const user = useUserContext(userContextPropTypes)
  44. const { name: projectName } = useProjectContext(projectContextPropTypes)
  45. const {
  46. cobranding,
  47. loading,
  48. isRestrictedTokenMember,
  49. renameProject,
  50. isProjectOwner,
  51. permissionsLevel,
  52. } = useEditorContext(editorContextPropTypes)
  53. const {
  54. chatIsOpen,
  55. setChatIsOpen,
  56. reviewPanelOpen,
  57. setReviewPanelOpen,
  58. view,
  59. setView,
  60. setLeftMenuShown,
  61. pdfLayout,
  62. } = useLayoutContext(layoutContextPropTypes)
  63. const { markMessagesAsRead, unreadMessageCount } = useChatContext(
  64. chatContextPropTypes
  65. )
  66. const toggleChatOpen = useCallback(() => {
  67. if (!chatIsOpen) {
  68. markMessagesAsRead()
  69. }
  70. setChatIsOpen(value => !value)
  71. }, [chatIsOpen, setChatIsOpen, markMessagesAsRead])
  72. const toggleReviewPanelOpen = useCallback(
  73. () => setReviewPanelOpen(value => !value),
  74. [setReviewPanelOpen]
  75. )
  76. const toggleHistoryOpen = useCallback(() => {
  77. setView(view === 'history' ? 'editor' : 'history')
  78. }, [view, setView])
  79. const togglePdfView = useCallback(() => {
  80. setView(view === 'pdf' ? 'editor' : 'pdf')
  81. }, [view, setView])
  82. const openShareModal = useCallback(() => {
  83. openShareProjectModal(isProjectOwner)
  84. }, [openShareProjectModal, isProjectOwner])
  85. const onShowLeftMenuClick = useCallback(
  86. () => setLeftMenuShown(value => !value),
  87. [setLeftMenuShown]
  88. )
  89. const goToUser = useCallback(
  90. user => {
  91. if (user.doc && typeof user.row === 'number') {
  92. openDoc(user.doc, { gotoLine: user.row + 1 })
  93. }
  94. },
  95. [openDoc]
  96. )
  97. // the existing angular implementation prevents collaborators from updating a
  98. // project's name, but the backend allows that with the following logic:
  99. // `const hasRenamePermissions = permissionsLevel === 'owner' || permissionsLevel === 'readAndWrite'`
  100. // See https://github.com/overleaf/issues/issues/4492
  101. const hasRenamePermissions = permissionsLevel === 'owner'
  102. // using {display: 'none'} as 1:1 migration from Angular's ng-hide. Using
  103. // `loading ? null : <ToolbarHeader/>` causes UI glitches
  104. return (
  105. <ToolbarHeader
  106. style={loading ? { display: 'none' } : {}}
  107. cobranding={cobranding}
  108. onShowLeftMenuClick={onShowLeftMenuClick}
  109. chatIsOpen={chatIsOpen}
  110. unreadMessageCount={unreadMessageCount}
  111. toggleChatOpen={toggleChatOpen}
  112. reviewPanelOpen={reviewPanelOpen}
  113. toggleReviewPanelOpen={toggleReviewPanelOpen}
  114. historyIsOpen={view === 'history'}
  115. toggleHistoryOpen={toggleHistoryOpen}
  116. onlineUsers={onlineUsersArray}
  117. goToUser={goToUser}
  118. isRestrictedTokenMember={isRestrictedTokenMember}
  119. isAnonymousUser={user == null}
  120. projectName={projectName}
  121. renameProject={renameProject}
  122. hasRenamePermissions={hasRenamePermissions}
  123. openShareModal={openShareModal}
  124. pdfViewIsOpen={view === 'pdf'}
  125. pdfButtonIsVisible={pdfLayout === 'flat'}
  126. togglePdfView={togglePdfView}
  127. />
  128. )
  129. }
  130. )
  131. EditorNavigationToolbarRoot.propTypes = {
  132. onlineUsersArray: PropTypes.array.isRequired,
  133. openDoc: PropTypes.func.isRequired,
  134. openShareProjectModal: PropTypes.func.isRequired,
  135. }
  136. export default EditorNavigationToolbarRoot