toolbar-header.tsx 5.2 KB

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