toolbar-header.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import React, { ElementType } from 'react'
  2. import { useTranslation } from 'react-i18next'
  3. import MenuButton from './menu-button'
  4. import CobrandingLogo from './cobranding-logo'
  5. import BackToProjectsButton from './back-to-projects-button'
  6. import UpgradePrompt from './upgrade-prompt'
  7. import ChatToggleButton from './chat-toggle-button'
  8. import LayoutDropdownButton from './layout-dropdown-button'
  9. import OnlineUsersWidget from './online-users-widget'
  10. import ProjectNameEditableLabel from './project-name-editable-label'
  11. import TrackChangesToggleButton from './track-changes-toggle-button'
  12. import HistoryToggleButton from './history-toggle-button'
  13. import ShareProjectButton from './share-project-button'
  14. import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
  15. import BackToEditorButton from './back-to-editor-button'
  16. import getMeta from '@/utils/meta'
  17. import { isSplitTestEnabled } from '@/utils/splitTestUtils'
  18. import { canUseNewEditor } from '@/features/ide-redesign/utils/new-editor-utils'
  19. import TryNewEditorButton from '../try-new-editor-button'
  20. import { OnlineUser } from '@/features/ide-react/context/online-users-context'
  21. import { Cobranding } from '../../../../../types/cobranding'
  22. const [publishModalModules] = importOverleafModules('publishModal') as {
  23. import: { default: ElementType }
  24. path: string
  25. }[]
  26. const PublishButton = publishModalModules?.import.default
  27. const offlineModeToolbarButtons = importOverleafModules(
  28. 'offlineModeToolbarButtons'
  29. ) as {
  30. import: { default: ElementType }
  31. path: string
  32. }[]
  33. // double opt-in
  34. const enableROMirrorOnClient =
  35. isSplitTestEnabled('ro-mirror-on-client') &&
  36. new URLSearchParams(window.location.search).get('ro-mirror-on-client') ===
  37. 'enabled'
  38. export type ToolbarHeaderProps = {
  39. cobranding: Cobranding | undefined
  40. onShowLeftMenuClick: () => void
  41. chatIsOpen: boolean
  42. toggleChatOpen: () => void
  43. reviewPanelOpen: boolean
  44. toggleReviewPanelOpen: (e: React.MouseEvent) => void
  45. historyIsOpen: boolean
  46. toggleHistoryOpen: () => void
  47. unreadMessageCount: number
  48. onlineUsers: OnlineUser[]
  49. goToUser: (user: OnlineUser) => void
  50. isRestrictedTokenMember: boolean | undefined
  51. hasPublishPermissions: boolean
  52. chatVisible: boolean
  53. projectName: string
  54. renameProject: (name: string) => void
  55. hasRenamePermissions: boolean
  56. openShareModal: () => void
  57. trackChangesVisible: boolean | undefined
  58. }
  59. const ToolbarHeader = React.memo(function ToolbarHeader({
  60. cobranding,
  61. onShowLeftMenuClick,
  62. chatIsOpen,
  63. toggleChatOpen,
  64. reviewPanelOpen,
  65. toggleReviewPanelOpen,
  66. historyIsOpen,
  67. toggleHistoryOpen,
  68. unreadMessageCount,
  69. onlineUsers,
  70. goToUser,
  71. isRestrictedTokenMember,
  72. hasPublishPermissions,
  73. chatVisible,
  74. projectName,
  75. renameProject,
  76. hasRenamePermissions,
  77. openShareModal,
  78. trackChangesVisible,
  79. }: ToolbarHeaderProps) {
  80. const chatEnabled = getMeta('ol-capabilities')?.includes('chat')
  81. const { t } = useTranslation()
  82. const shouldDisplayPublishButton = hasPublishPermissions && PublishButton
  83. return (
  84. <header
  85. className="toolbar toolbar-header"
  86. role="navigation"
  87. aria-label={t('project_layout_sharing_submission')}
  88. >
  89. <div className="toolbar-left">
  90. <MenuButton onClick={onShowLeftMenuClick} />
  91. {cobranding && cobranding.logoImgUrl && (
  92. <CobrandingLogo {...cobranding} />
  93. )}
  94. <BackToProjectsButton />
  95. {enableROMirrorOnClient &&
  96. offlineModeToolbarButtons.map(
  97. ({ path, import: { default: OfflineModeToolbarButton } }) => {
  98. return <OfflineModeToolbarButton key={path} />
  99. }
  100. )}
  101. </div>
  102. {getMeta('ol-showUpgradePrompt') && (
  103. <div className="d-flex align-items-center">
  104. <UpgradePrompt />
  105. </div>
  106. )}
  107. <ProjectNameEditableLabel
  108. className="toolbar-center"
  109. projectName={projectName}
  110. hasRenamePermissions={hasRenamePermissions}
  111. onChange={renameProject}
  112. />
  113. <div className="toolbar-right">
  114. {canUseNewEditor() && <TryNewEditorButton />}
  115. <OnlineUsersWidget onlineUsers={onlineUsers} goToUser={goToUser} />
  116. {historyIsOpen ? (
  117. <div className="d-flex align-items-center">
  118. <BackToEditorButton onClick={toggleHistoryOpen} />
  119. </div>
  120. ) : (
  121. <>
  122. {trackChangesVisible && (
  123. <TrackChangesToggleButton
  124. onMouseDown={toggleReviewPanelOpen}
  125. disabled={historyIsOpen}
  126. trackChangesIsOpen={reviewPanelOpen}
  127. />
  128. )}
  129. <ShareProjectButton onClick={openShareModal} />
  130. {shouldDisplayPublishButton && (
  131. <PublishButton cobranding={cobranding} />
  132. )}
  133. {!isRestrictedTokenMember && (
  134. <HistoryToggleButton onClick={toggleHistoryOpen} />
  135. )}
  136. <LayoutDropdownButton />
  137. {chatEnabled && chatVisible && (
  138. <ChatToggleButton
  139. chatIsOpen={chatIsOpen}
  140. onClick={toggleChatOpen}
  141. unreadMessageCount={unreadMessageCount}
  142. />
  143. )}
  144. </>
  145. )}
  146. </div>
  147. </header>
  148. )
  149. })
  150. export default ToolbarHeader