Files
overleaf-cep/services/web/modules/zotero/frontend/js/components/zotero-create-file.tsx
T

168 lines
4.5 KiB
TypeScript

import { useTranslation } from 'react-i18next'
import { FormEventHandler, useEffect, useState, useCallback } from 'react'
import { getJSON } from '@/infrastructure/fetch-json'
import { useFileTreeActionable } from '@/features/file-tree/contexts/file-tree-actionable'
import { useFileTreeCreateForm } from '@/features/file-tree/contexts/file-tree-create-form'
import { useFileTreeMainContext } from '@/features/file-tree/contexts/file-tree-main'
import FileTreeModalCreateFileMode from '@/features/file-tree/components/file-tree-create/file-tree-modal-create-file-mode'
import ErrorMessage from '@/features/file-tree/components/file-tree-create/error-message'
import OLFormGroup from '@/shared/components/ol/ol-form-group'
import OLFormLabel from '@/shared/components/ol/ol-form-label'
import OLFormControl from '@/shared/components/ol/ol-form-control'
import OLFormSelect from '@/shared/components/ol/ol-form-select'
import OLNotification from '@/shared/components/ol/ol-notification'
type ZoteroGroup = {
id: string
name: string
}
export function CreateFileMode() {
const { refProviders } = useFileTreeMainContext()
const isLinked = (refProviders as Record<string, boolean>)?.zotero
if (!isLinked) {
return null
}
return (
<FileTreeModalCreateFileMode
mode="zotero"
icon="library_books"
label="From Zotero"
/>
)
}
export function CreateFilePane() {
const { newFileCreateMode } = useFileTreeActionable()
if (newFileCreateMode !== 'zotero') {
return null
}
return <ZoteroCreateFilePane />
}
function ZoteroCreateFilePane() {
const { t } = useTranslation()
const { setValid } = useFileTreeCreateForm()
const { finishCreatingLinkedFile, error, inFlight } = useFileTreeActionable()
const [groups, setGroups] = useState<ZoteroGroup[]>([])
const [selectedGroupId, setSelectedGroupId] = useState<string>('')
const [name, setName] = useState('zotero.bib')
const [loadingGroups, setLoadingGroups] = useState(true)
const [groupsError, setGroupsError] = useState('')
useEffect(() => {
setValid(!!name && !loadingGroups)
}, [setValid, name, loadingGroups])
useEffect(() => {
let cancelled = false
setLoadingGroups(true)
setGroupsError('')
getJSON('/zotero/groups')
.then((data: { groups: ZoteroGroup[] }) => {
if (cancelled) return
setGroups(data.groups || [])
setLoadingGroups(false)
})
.catch(() => {
if (cancelled) return
setGroupsError(t('zotero_groups_loading_error'))
setLoadingGroups(false)
})
return () => {
cancelled = true
}
}, [t])
const handleSubmit: FormEventHandler = useCallback(
event => {
event.preventDefault()
if (!name || loadingGroups || inFlight) {
return
}
const data: Record<string, string> = {}
if (selectedGroupId) {
data.zoteroGroupId = selectedGroupId
}
finishCreatingLinkedFile({
name,
provider: 'zotero',
data,
})
},
[
name,
loadingGroups,
inFlight,
selectedGroupId,
finishCreatingLinkedFile,
]
)
return (
<form
className="form-controls"
id="create-file"
noValidate
onSubmit={handleSubmit}
>
{groupsError && (
<OLNotification type="error" content={groupsError} className="mb-3" />
)}
<OLFormGroup controlId="zotero-file-name">
<OLFormLabel>{t('file_name')}</OLFormLabel>
<OLFormControl
type="text"
placeholder="zotero.bib"
required
value={name}
disabled={inFlight}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setName(e.target.value)
}
/>
</OLFormGroup>
<OLFormGroup controlId="zotero-library-select">
<OLFormLabel>Library</OLFormLabel>
{loadingGroups ? (
<p className="text-muted">{t('loading')}...</p>
) : (
<OLFormSelect
id="zotero-library-select"
value={selectedGroupId}
disabled={inFlight}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
setSelectedGroupId(e.target.value)
}
>
<option value="">My Library</option>
{groups.map(g => (
<option key={g.id} value={g.id}>
{g.name}
</option>
))}
</OLFormSelect>
)}
</OLFormGroup>
{error && <ErrorMessage error={error} />}
</form>
)
}