Merge pull request #27316 from overleaf/dp-file-menu-options

Add copy and submit project options to new editor file menu

GitOrigin-RevId: 7f402d96f278f2b084375441089b286adaa731b8
This commit is contained in:
David
2025-08-20 11:25:48 +01:00
committed by Copybot
parent c1471567ce
commit 975d1ee250
5 changed files with 56 additions and 31 deletions

View File

@@ -1,8 +1,12 @@
import React from 'react'
import React, { useCallback } from 'react'
import { useProjectContext } from '../../../shared/context/project-context'
import withErrorBoundary from '../../../infrastructure/error-boundary'
import CloneProjectModal from './clone-project-modal'
type ProjectCopyResponse = {
project_id: string
}
const EditorCloneProjectModalWrapper = React.memo(
function EditorCloneProjectModalWrapper({
show,
@@ -11,9 +15,15 @@ const EditorCloneProjectModalWrapper = React.memo(
}: {
show: boolean
handleHide: () => void
openProject: ({ project_id }: { project_id: string }) => void
openProject: (projectId: string) => void
}) {
const { project, tags: projectTags } = useProjectContext()
const handleAfterCloned = useCallback(
({ project_id: projectId }: ProjectCopyResponse) => {
openProject(projectId)
},
[openProject]
)
if (!project) {
// wait for useProjectContext
@@ -23,7 +33,7 @@ const EditorCloneProjectModalWrapper = React.memo(
<CloneProjectModal
handleHide={handleHide}
show={show}
handleAfterCloned={openProject}
handleAfterCloned={handleAfterCloned}
projectId={project._id}
projectName={project.name}
projectTags={projectTags}

View File

@@ -2,24 +2,13 @@ import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import EditorCloneProjectModalWrapper from '../../clone-project-modal/components/editor-clone-project-modal-wrapper'
import LeftMenuButton from './left-menu-button'
import { useLocation } from '../../../shared/hooks/use-location'
import * as eventTracking from '../../../infrastructure/event-tracking'
type ProjectCopyResponse = {
project_id: string
}
import useOpenProject from '@/shared/hooks/use-open-project'
export default function ActionsCopyProject() {
const [showModal, setShowModal] = useState(false)
const { t } = useTranslation()
const location = useLocation()
const openProject = useCallback(
({ project_id: projectId }: ProjectCopyResponse) => {
location.assign(`/project/${projectId}`)
},
[location]
)
const openProject = useOpenProject()
const handleShowModal = useCallback(() => {
eventTracking.sendMB('left-menu-copy')

View File

@@ -1,28 +1,17 @@
import EditorCloneProjectModalWrapper from '@/features/clone-project-modal/components/editor-clone-project-modal-wrapper'
import OLDropdownMenuItem from '@/shared/components/ol/ol-dropdown-menu-item'
import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics'
import { useLocation } from '@/shared/hooks/use-location'
import useOpenProject from '@/shared/hooks/use-open-project'
import getMeta from '@/utils/meta'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
type ProjectCopyResponse = {
project_id: string
}
export const DuplicateProject = () => {
const { sendEvent } = useEditorAnalytics()
const { t } = useTranslation()
const [showModal, setShowModal] = useState(false)
const location = useLocation()
const anonymous = getMeta('ol-anonymous')
const openProject = useCallback(
({ project_id: projectId }: ProjectCopyResponse) => {
location.assign(`/project/${projectId}`)
},
[location]
)
const openProject = useOpenProject()
const handleShowModal = useCallback(() => {
sendEvent('copy-project', { location: 'project-title-dropdown' })

View File

@@ -27,6 +27,9 @@ import { useDetachCompileContext as useCompileContext } from '@/shared/context/d
import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics'
import { useProjectSettingsContext } from '@/features/editor-left-menu/context/project-settings-context'
import { useSurveyUrl } from '../../hooks/use-survey-url'
import getMeta from '@/utils/meta'
import EditorCloneProjectModalWrapper from '@/features/clone-project-modal/components/editor-clone-project-modal-wrapper'
import useOpenProject from '@/shared/hooks/use-open-project'
export const ToolbarMenuBar = () => {
const { t } = useTranslation()
@@ -38,6 +41,10 @@ export const ToolbarMenuBar = () => {
const { pdfUrl } = useCompileContext()
const wordCountEnabled = pdfUrl || isSplitTestEnabled('word-count-client')
const [showWordCountModal, setShowWordCountModal] = useState(false)
const [showCloneProjectModal, setShowCloneProjectModal] = useState(false)
const openProject = useOpenProject()
const anonymous = getMeta('ol-anonymous')
useCommandProvider(
() => [
@@ -58,16 +65,26 @@ export const ToolbarMenuBar = () => {
},
id: 'word_count',
},
{
type: 'command',
label: t('make_a_copy'),
disabled: anonymous,
handler: () => {
setShowCloneProjectModal(true)
},
id: 'copy_project',
},
],
[t, setView, view, wordCountEnabled]
[t, setView, view, wordCountEnabled, anonymous]
)
const fileMenuStructure: MenuStructure = useMemo(
() => [
{
id: 'file-file-tree',
children: ['new_file', 'new_folder', 'upload_file'],
children: ['new_file', 'new_folder', 'upload_file', 'copy_project'],
},
{ id: 'file-tools', children: ['show_version_history', 'word_count'] },
{ id: 'submit', children: ['submit-project'] },
{
id: 'file-download',
children: ['download-as-source-zip', 'download-pdf'],
@@ -286,6 +303,11 @@ export const ToolbarMenuBar = () => {
show={showWordCountModal}
handleHide={() => setShowWordCountModal(false)}
/>
<EditorCloneProjectModalWrapper
show={showCloneProjectModal}
handleHide={() => setShowCloneProjectModal(false)}
openProject={openProject}
/>
</>
)
}

View File

@@ -0,0 +1,15 @@
import { useCallback } from 'react'
import { useLocation } from './use-location'
export default function useOpenProject() {
const location = useLocation()
const openProject = useCallback(
(projectId: string) => {
location.assign(`/project/${projectId}`)
},
[location]
)
return openProject
}