Files
RenewCMS/web/src/composables/useEditorTranslation.ts
T

150 lines
3.7 KiB
TypeScript

import { watch, type Ref } from 'vue'
import { useRoute, useRouter, type LocationQueryRaw } from 'vue-router'
import { locale, t } from '@/i18n'
import { useLocalizedOverlay } from '@/i18n/presentation'
import AddTranslationDialog, {
type AddTranslationResult,
} from '@/components/AddTranslationDialog.vue'
import type { Article, CoverageEntry } from '@/composables/useArticles'
import { ADD_TRANSLATION_DIALOG as DIALOG_NAME } from '@/lib/addTranslationLocation'
import { holds } from '@/lib/editorCapabilities'
import type { VersionRow } from '@/lib/editorVersions'
import { languageName, type SiteLanguage } from '@/lib/siteLanguages'
interface TranslationOptions {
article: Ref<Article | null>
loaded: Ref<boolean>
languages: () => SiteLanguage[]
refetchLanguages: () => Promise<SiteLanguage[]>
switchTo: (row: VersionRow, announcement?: string) => Promise<void>
onGone: () => void
}
function versionRowFor(id: number, lang: string): VersionRow {
return {
key: `version-${id}`,
lang,
name: languageName(lang, locale.value),
code: lang.toUpperCase(),
articleId: id,
title: '',
current: false,
online: false,
readOnly: false,
languageDisabled: false,
addTranslation: false,
}
}
function entryOf(created: Article, lang: string): CoverageEntry {
return {
id: created.id,
lang,
title: created.title,
is_online: created.is_online,
language_enabled: true,
capabilities: created.capabilities,
}
}
export function useEditorTranslation(options: TranslationOptions) {
const route = useRoute()
const router = useRouter()
const overlay = useLocalizedOverlay()
let instance: ReturnType<typeof overlay.create> | null = null
function strippedQuery(): LocationQueryRaw {
const rest = { ...route.query }
delete rest.dialog
delete rest.lang
return rest
}
function stripParameters() {
if (route.query.dialog !== undefined || route.query.lang !== undefined) {
return router.replace({ query: strippedQuery() })
}
return undefined
}
function open(lang?: string) {
return router.push({
query: { ...route.query, dialog: DIALOG_NAME, ...(lang ? { lang } : {}) },
})
}
async function settle(result: AddTranslationResult | undefined) {
if (!result) {
await stripParameters()
return
}
if (result.kind === 'gone') {
await stripParameters()
options.onGone()
return
}
await stripParameters()
if (result.kind === 'open') {
await options.switchTo(versionRowFor(result.id, result.lang))
return
}
const current = options.article.value
if (current) {
options.article.value = {
...current,
translations: [
...(current.translations ?? []),
entryOf(result.article, result.lang),
],
}
}
const name = languageName(result.lang, locale.value)
await options.switchTo(
versionRowFor(result.article.id, result.lang),
t('{0} draft created', [name]),
)
}
async function show(lang: string | undefined) {
const created = overlay.create(AddTranslationDialog, {
props: {
get article() {
return options.article.value!
},
get languages() {
return options.languages()
},
initialLang: lang,
refetchLanguages: options.refetchLanguages,
},
})
instance = created
const result = (await created.open()) as AddTranslationResult | undefined
instance = null
await settle(result)
}
watch(
() => [route.query.dialog, route.query.lang, options.loaded.value] as const,
([dialog, lang, loaded]) => {
if (!loaded) {
return
}
if (dialog !== DIALOG_NAME) {
instance?.close()
return
}
if (!holds(options.article.value?.capabilities, 'create_translation')) {
stripParameters()
return
}
if (!instance) {
show(typeof lang === 'string' ? lang : undefined)
}
},
{ immediate: true },
)
return { open }
}