065534819c
Build and Deploy Verso / deploy (push) Successful in 14m4s
- Convert: backend now returns parentFolderId+isNew; frontend calls dispatchCreateDoc directly so the new file appears without a page refresh - Set as main: infer compiler from file extension and POST both rootDocId and compiler together; updateProject propagates the change to the editor settings dropdown and project list immediately Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
199 lines
6.4 KiB
TypeScript
199 lines
6.4 KiB
TypeScript
import { useCallback } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import * as eventTracking from '../../../../infrastructure/event-tracking'
|
|
import { useProjectContext } from '@/shared/context/project-context'
|
|
import { postJSON } from '@/infrastructure/fetch-json'
|
|
import type { ProjectCompiler } from '@ol-types/project-settings'
|
|
|
|
import {
|
|
DropdownDivider,
|
|
DropdownItem,
|
|
} from '@/shared/components/dropdown/dropdown-menu'
|
|
import { useFileTreeActionable } from '../../contexts/file-tree-actionable'
|
|
import { useFileTreeSelectable } from '../../contexts/file-tree-selectable'
|
|
import { useFileTreeData } from '@/shared/context/file-tree-data-context'
|
|
import { useFileTreeMainContext } from '../../contexts/file-tree-main'
|
|
import { findInTree } from '../../util/find-in-tree'
|
|
import useConvertDoc from '@/features/ide-react/hooks/use-convert-doc'
|
|
import getMeta from '@/utils/meta'
|
|
import { isValidTeXFile } from '@/main/is-valid-tex-file'
|
|
|
|
const COMPILER_BY_EXT: Record<string, ProjectCompiler> = {
|
|
tex: 'pdflatex',
|
|
rtex: 'pdflatex',
|
|
ltx: 'pdflatex',
|
|
rnw: 'pdflatex',
|
|
typ: 'typst',
|
|
qmd: 'quarto',
|
|
rmd: 'quarto',
|
|
}
|
|
|
|
function FileTreeItemMenuItems() {
|
|
const { t } = useTranslation()
|
|
|
|
const {
|
|
canRename,
|
|
canDelete,
|
|
canCreate,
|
|
startRenaming,
|
|
startDeleting,
|
|
startCreatingFolder,
|
|
startCreatingDocOrFile,
|
|
startUploadingDocOrFile,
|
|
downloadPath,
|
|
selectedFileName,
|
|
} = useFileTreeActionable()
|
|
|
|
const { project, projectId, updateProject } = useProjectContext()
|
|
const projectOwner = project?.owner?._id
|
|
const rootDocId = project?.rootDocId
|
|
|
|
const { fileTreeData, fileTreeReadOnly } = useFileTreeData()
|
|
const { selectedEntityIds } = useFileTreeSelectable()
|
|
const { contextMenuEntityId } = useFileTreeMainContext()
|
|
const selectedEntityId =
|
|
selectedEntityIds.size === 1 ? Array.from(selectedEntityIds)[0] : null
|
|
|
|
// Use context-menu-target entity for convert/set-as-main; falls back to selection
|
|
const convertEntityId = contextMenuEntityId ?? selectedEntityId
|
|
const convertEntity = convertEntityId
|
|
? findInTree(fileTreeData, convertEntityId)
|
|
: null
|
|
const isConvertableDoc = convertEntity?.type === 'doc'
|
|
const convertEntityName = convertEntity?.entity.name ?? null
|
|
|
|
const enablePandocConversions =
|
|
getMeta('ol-ExposedSettings')?.enablePandocConversions
|
|
|
|
const canConvertToTypst =
|
|
enablePandocConversions &&
|
|
!fileTreeReadOnly &&
|
|
isConvertableDoc &&
|
|
convertEntityName?.endsWith('.tex')
|
|
const canConvertToLatex =
|
|
enablePandocConversions &&
|
|
!fileTreeReadOnly &&
|
|
isConvertableDoc &&
|
|
convertEntityName?.endsWith('.typ')
|
|
|
|
const canShowSetAsMain =
|
|
!fileTreeReadOnly &&
|
|
isConvertableDoc &&
|
|
!!convertEntityId &&
|
|
convertEntityId !== rootDocId &&
|
|
!!convertEntityName &&
|
|
isValidTeXFile(convertEntityName)
|
|
|
|
const handleSetAsMain = useCallback(async () => {
|
|
if (!convertEntityId || !convertEntityName) return
|
|
const ext = convertEntityName.split('.').pop()?.toLowerCase() ?? ''
|
|
const newCompiler = COMPILER_BY_EXT[ext]
|
|
const body: Record<string, string> = { rootDocId: convertEntityId }
|
|
if (newCompiler && newCompiler !== project?.compiler) body.compiler = newCompiler
|
|
await postJSON(`/project/${projectId}/settings`, { body })
|
|
const update: Record<string, string> = { rootDocId: convertEntityId }
|
|
if (newCompiler) update.compiler = newCompiler
|
|
updateProject(update)
|
|
}, [convertEntityId, convertEntityName, project, projectId, updateProject])
|
|
|
|
const { convert: convertToTypst } = useConvertDoc('typst', convertEntityId)
|
|
const { convert: convertToLatex } = useConvertDoc('latex', convertEntityId)
|
|
|
|
const downloadWithAnalytics = useCallback(() => {
|
|
// we are only interested in downloads of bib files WRT analytics, for the purposes of promoting the tpr integrations
|
|
if (selectedFileName?.endsWith('.bib')) {
|
|
eventTracking.sendMB('download-bib-file', { projectOwner })
|
|
}
|
|
}, [selectedFileName, projectOwner])
|
|
|
|
const createWithAnalytics = useCallback(() => {
|
|
eventTracking.sendMB('new-file-click', { location: 'file-menu' })
|
|
startCreatingDocOrFile()
|
|
}, [startCreatingDocOrFile])
|
|
|
|
const uploadWithAnalytics = useCallback(() => {
|
|
eventTracking.sendMB('upload-click', { location: 'file-menu' })
|
|
startUploadingDocOrFile()
|
|
}, [startUploadingDocOrFile])
|
|
|
|
return (
|
|
<>
|
|
{canRename ? (
|
|
<li role="none">
|
|
<DropdownItem onClick={startRenaming}>{t('rename')}</DropdownItem>
|
|
</li>
|
|
) : null}
|
|
{downloadPath ? (
|
|
<li role="none">
|
|
<DropdownItem
|
|
href={downloadPath}
|
|
onClick={downloadWithAnalytics}
|
|
download={selectedFileName ?? undefined}
|
|
>
|
|
{t('download')}
|
|
</DropdownItem>
|
|
</li>
|
|
) : null}
|
|
{canShowSetAsMain ? (
|
|
<>
|
|
<DropdownDivider />
|
|
<li role="none">
|
|
<DropdownItem onClick={handleSetAsMain}>
|
|
{t('set_as_main_document')}
|
|
</DropdownItem>
|
|
</li>
|
|
</>
|
|
) : null}
|
|
{(canConvertToTypst || canConvertToLatex) ? (
|
|
<>
|
|
<DropdownDivider />
|
|
{canConvertToTypst && (
|
|
<li role="none">
|
|
<DropdownItem onClick={convertToTypst}>
|
|
{t('convert_to_typst')}
|
|
</DropdownItem>
|
|
</li>
|
|
)}
|
|
{canConvertToLatex && (
|
|
<li role="none">
|
|
<DropdownItem onClick={convertToLatex}>
|
|
{t('convert_to_latex')}
|
|
</DropdownItem>
|
|
</li>
|
|
)}
|
|
</>
|
|
) : null}
|
|
{canDelete ? (
|
|
<>
|
|
<DropdownDivider />
|
|
<li role="none">
|
|
<DropdownItem onClick={startDeleting}>{t('delete')}</DropdownItem>
|
|
</li>
|
|
</>
|
|
) : null}
|
|
{canCreate ? (
|
|
<>
|
|
<DropdownDivider />
|
|
<li role="none">
|
|
<DropdownItem onClick={createWithAnalytics}>
|
|
{t('new_file')}
|
|
</DropdownItem>
|
|
</li>
|
|
<li role="none">
|
|
<DropdownItem onClick={startCreatingFolder}>
|
|
{t('new_folder')}
|
|
</DropdownItem>
|
|
</li>
|
|
<li role="none">
|
|
<DropdownItem onClick={uploadWithAnalytics}>
|
|
{t('upload')}
|
|
</DropdownItem>
|
|
</li>
|
|
</>
|
|
) : null}
|
|
</>
|
|
)
|
|
}
|
|
|
|
export default FileTreeItemMenuItems
|