editor-navigation-toolbar-root.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  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 { useProjectContext } from '../../../shared/context/project-context'
  8. import * as eventTracking from '../../../infrastructure/event-tracking'
  9. const projectContextPropTypes = {
  10. name: PropTypes.string.isRequired,
  11. features: PropTypes.shape({
  12. trackChangesVisible: PropTypes.bool,
  13. }).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. function isOpentoString(open) {
  38. return open ? 'open' : 'close'
  39. }
  40. const EditorNavigationToolbarRoot = React.memo(
  41. function EditorNavigationToolbarRoot({
  42. onlineUsersArray,
  43. openDoc,
  44. openShareProjectModal,
  45. }) {
  46. const {
  47. name: projectName,
  48. features: { trackChangesVisible },
  49. } = useProjectContext(projectContextPropTypes)
  50. const {
  51. cobranding,
  52. loading,
  53. isRestrictedTokenMember,
  54. renameProject,
  55. permissionsLevel,
  56. } = useEditorContext(editorContextPropTypes)
  57. const {
  58. chatIsOpen,
  59. setChatIsOpen,
  60. reviewPanelOpen,
  61. setReviewPanelOpen,
  62. view,
  63. setView,
  64. setLeftMenuShown,
  65. } = useLayoutContext(layoutContextPropTypes)
  66. const { markMessagesAsRead, unreadMessageCount } =
  67. useChatContext(chatContextPropTypes)
  68. const toggleChatOpen = useCallback(() => {
  69. if (!chatIsOpen) {
  70. markMessagesAsRead()
  71. }
  72. eventTracking.sendMB('navigation-clicked-chat', {
  73. action: isOpentoString(!chatIsOpen),
  74. })
  75. setChatIsOpen(value => !value)
  76. }, [chatIsOpen, setChatIsOpen, markMessagesAsRead])
  77. const toggleReviewPanelOpen = useCallback(
  78. event => {
  79. event.preventDefault()
  80. eventTracking.sendMB('navigation-clicked-review', {
  81. action: isOpentoString(!reviewPanelOpen),
  82. })
  83. setReviewPanelOpen(value => !value)
  84. },
  85. [reviewPanelOpen, setReviewPanelOpen]
  86. )
  87. const toggleHistoryOpen = useCallback(() => {
  88. const action = view === 'history' ? 'close' : 'open'
  89. eventTracking.sendMB('navigation-clicked-history', { action })
  90. setView(view === 'history' ? 'editor' : 'history')
  91. }, [view, setView])
  92. const openShareModal = useCallback(() => {
  93. eventTracking.sendMB('navigation-clicked-share')
  94. openShareProjectModal()
  95. }, [openShareProjectModal])
  96. const onShowLeftMenuClick = useCallback(() => {
  97. eventTracking.sendMB('navigation-clicked-menu')
  98. setLeftMenuShown(value => !value)
  99. }, [setLeftMenuShown])
  100. const goToUser = useCallback(
  101. user => {
  102. if (user.doc && typeof user.row === 'number') {
  103. openDoc(user.doc, { gotoLine: user.row + 1 })
  104. }
  105. },
  106. [openDoc]
  107. )
  108. // using {display: 'none'} as 1:1 migration from Angular's ng-hide. Using
  109. // `loading ? null : <ToolbarHeader/>` causes UI glitches
  110. return (
  111. <ToolbarHeader
  112. style={loading ? { display: 'none' } : {}}
  113. cobranding={cobranding}
  114. onShowLeftMenuClick={onShowLeftMenuClick}
  115. chatIsOpen={chatIsOpen}
  116. unreadMessageCount={unreadMessageCount}
  117. toggleChatOpen={toggleChatOpen}
  118. reviewPanelOpen={reviewPanelOpen}
  119. toggleReviewPanelOpen={toggleReviewPanelOpen}
  120. historyIsOpen={view === 'history'}
  121. toggleHistoryOpen={toggleHistoryOpen}
  122. onlineUsers={onlineUsersArray}
  123. goToUser={goToUser}
  124. isRestrictedTokenMember={isRestrictedTokenMember}
  125. hasPublishPermissions={
  126. permissionsLevel === 'owner' || permissionsLevel === 'readAndWrite'
  127. }
  128. projectName={projectName}
  129. renameProject={renameProject}
  130. hasRenamePermissions={permissionsLevel === 'owner'}
  131. openShareModal={openShareModal}
  132. trackChangesVisible={trackChangesVisible}
  133. />
  134. )
  135. }
  136. )
  137. EditorNavigationToolbarRoot.propTypes = {
  138. onlineUsersArray: PropTypes.array.isRequired,
  139. openDoc: PropTypes.func.isRequired,
  140. openShareProjectModal: PropTypes.func.isRequired,
  141. }
  142. export default EditorNavigationToolbarRoot