Skip to content

Commit

Permalink
Customize and upgrade i18n plugin #1868 (#2286)
Browse files Browse the repository at this point in the history
* Upgrade plugin and add lang field #1868

* Change _lang to lang #1868

* ♻️ Change _lang to lang #1868

* ♻️ Add lang-wise lane #1868

* ♻️ Change desk structure #1868

* 💬 Title change #1868

* Revert "💬 Title change #1868"

This reverts commit a65605f.

* Revert "♻️ Change desk structure #1868"

This reverts commit 0fa669a.

* Revert "♻️ Add lang-wise lane #1868"

This reverts commit ca15974.

* 🐛 Fix footer, siteMenu #1868

* 🐛 Add 'delete translation' action #1868

* 🐛 Fix document list param #1868

* 🐛 Missing lang field on subMenu #1868

* 🐛 Correct filter #1868

* ⬆️ Upgrade plugin #1868

* Fix issues

* Some more fixes

* Custom delete action #1836

* ✨ Enable duplicate option #2056

* Use custom plugin #1868

* Change scripts folder #1868

* 🚨 Lint errors #1868

* Mistake commit #1868

* 🚨 Ignore eslint for sanity cli module #1868

* Fix slug and uncomment #1868

* Same sluf fix for news #1868

* Missed redirects #1868

* 🐛 Fix error in slug query when params is undefined #1868

* 🔇 Remove logs #1868

* 🐛 Fix error with slug #1868

* 🐛 Fix language switch button for single translations #1868

* 🐛 Missed document type in script #1868

* 🐛 Fix date picker not popping #1848

* 🐛 Disable comments in sanity studio #1848

* 🐛 Use unstable as comments #1868

* ✨ Add country flag #1868

* 🐛 Allow translation metdata to be duplicated #1868
  • Loading branch information
padms authored Jul 8, 2024
1 parent 97f21ee commit 1805adb
Show file tree
Hide file tree
Showing 72 changed files with 4,779 additions and 2,329 deletions.
8 changes: 7 additions & 1 deletion sanityv3/.eslintrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
{
"rules": {
"import/default": "off",
"@typescript-eslint/no-explicit-any": "off"
"@typescript-eslint/no-explicit-any": "off",
"import/no-unresolved": [
"error",
{
"ignore": ["^sanity/.+"]
}
]
}
}
40 changes: 40 additions & 0 deletions sanityv3/actions/CustomDuplicateAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useToast } from '@sanity/ui'
import { DocumentActionComponent, DocumentActionDescription, DocumentActionProps, DocumentActionsContext } from 'sanity'
import { defaultLanguage } from '../languages'
import { apiVersion } from '../sanity.client'

export function createCustomDuplicateAction(originalAction: DocumentActionComponent, context: DocumentActionsContext) {
const CustumDuplicateAction = (props: DocumentActionProps) => {
const originalResult = originalAction(props) as DocumentActionDescription
const toast = useToast()
return {
...originalResult,
onHandle: () => {
context
.getClient({ apiVersion: apiVersion })
.fetch(/* groq */ `*[_id match '*'+$id][0]{lang}`, { id: context.documentId })
.then((result) => {
if (result?.lang == defaultLanguage.name) {
// allow duplicate action only on base language
originalResult.onHandle && originalResult.onHandle()
} else {
toast.push({
duration: 7000,
status: 'error',
title: 'Cannot duplicate the translation.',
})
}
})
.catch((error) => {
console.log(error)
toast.push({
duration: 7000,
status: 'error',
title: 'Failed to duplicate',
})
})
},
}
}
return CustumDuplicateAction
}
122 changes: 122 additions & 0 deletions sanityv3/actions/customDelete/DeleteTranslationAction.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { TrashIcon } from '@sanity/icons'
import { type ButtonTone, useToast } from '@sanity/ui'
import { useCallback, useState } from 'react'
import { type DocumentActionComponent, type SanityDocument, useClient } from 'sanity'
import DeleteTranslationDialog from './components/DeleteTranslationDialog'
import DeleteTranslationFooter from './components/DeleteTranslationFooter'
import { useDocumentInternationalizationContext } from '@equinor/document-internationalization'
import { apiVersion } from '../../sanity.client'
import { defaultLanguage } from '../../languages'
import { Patch, Transaction } from '@sanity/client'

const TRANSLATIONS_ARRAY_NAME = 'translations'
export const DeleteTranslationAction: DocumentActionComponent = (props) => {
const { languageField } = useDocumentInternationalizationContext()

const doc = props.draft || props.published
const documentLanguage = doc ? doc[languageField] : null
const isDefaultLanguageDocument = defaultLanguage.name === documentLanguage

const { id: documentId } = props
const [isDialogOpen, setDialogOpen] = useState(false)
const [translations, setTranslations] = useState<SanityDocument[]>([])
const [hasOtherReferences, setHasOtherReferences] = useState<boolean>(false)
const onClose = useCallback(() => setDialogOpen(false), [])

const toast = useToast()
const client = useClient({ apiVersion: apiVersion })

const unsetAndDeleteCurrentTranslation = (tx: Transaction) => {
//unset current translation reference from metadata
translations.forEach((translation) => {
tx.patch(translation._id, (patch: Patch) =>
patch.unset([`${TRANSLATIONS_ARRAY_NAME}[_key == "${documentLanguage}"]`]),
)
})

// delete the current document
tx.delete(documentId)
tx.delete(`drafts.${documentId}`)
}

const unsetAndDeleteDefaultWithMetaData = (tx: Transaction) => {
//unset translation references array in metadata
translations.forEach((translation) => {
tx.patch(translation._id, (patch) => patch.unset([TRANSLATIONS_ARRAY_NAME]))
})

// delete each of the translations and delete metadata
if (translations.length > 0) {
translations.forEach((translation: any) => {
translation.translations.forEach((it: any) => {
tx.delete(it.id)
tx.delete(`drafts.${it.id}`)
})
tx.delete(translation._id)
// Shouldn't exist as this document type is in liveEdit
tx.delete(`drafts.${translation._id}`)
})
} else {
//simply a doc without metadata.. so delete it.

tx.delete(documentId)
tx.delete(`drafts.${documentId}`)
}
}
// Remove translation reference and delete document in one transaction
const onProceed = useCallback(() => {
const tx = client.transaction()
if (isDefaultLanguageDocument) {
unsetAndDeleteDefaultWithMetaData(tx)
} else {
unsetAndDeleteCurrentTranslation(tx)
}

tx.commit()
.then(() => {
onClose()
toast.push({
status: 'success',
title: 'Deleted document and translations',
})
})
.catch((err) => {
toast.push({
status: 'error',
title: 'Failed to delete document and translations',
description: err.message,
})
})
}, [client, translations, documentId, onClose, toast])

return {
label: `Delete translation...`,
disabled: !doc || !documentLanguage,
icon: TrashIcon,
tone: 'critical' as ButtonTone,
onHandle: () => {
setDialogOpen(true)
},
dialog: isDialogOpen && {
type: 'dialog',
onClose,
header: 'Delete translation',
content: doc ? (
<DeleteTranslationDialog
doc={doc}
documentId={documentId}
setTranslations={setTranslations}
setHasOtherReferences={setHasOtherReferences}
/>
) : null,
footer: (
<DeleteTranslationFooter
onClose={onClose}
onProceed={onProceed}
translations={translations}
disable={hasOtherReferences}
/>
),
},
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Preview, useSchema } from 'sanity'
import { Feedback } from 'sanity-plugin-utils'

type DocumentPreviewProps = {
value: unknown
type: string
}

// Wrapper of Preview just so that the schema type is satisfied by schema.get()
export default function DocumentPreview(props: DocumentPreviewProps) {
const schema = useSchema()

const { type, value } = props
const schemaType = schema.get(type)
if (!schemaType) {
return <Feedback tone="critical" title="Schema type not found" />
}

return <Preview value={value} schemaType={schemaType} />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { Card, Flex, Spinner, Stack, Text } from '@sanity/ui'
import { useEffect, useMemo, useState } from 'react'
import { SanityDocument } from 'sanity'
import { useClient } from 'sanity'
import { separateReferences } from './seperateReferences'
import { apiVersion } from '../../../../sanity.client'
import { defaultLanguage } from '../../../../languages'
import DocumentPreview from './DocumentPreview'

type DeleteTranslationDialogProps = {
doc: SanityDocument
documentId: string
setTranslations: (translations: SanityDocument[]) => void
setHasOtherReferences: (hasOtherReferences: boolean) => void
}

export default function DeleteTranslationDialog(props: DeleteTranslationDialogProps) {
const { doc, documentId, setTranslations, setHasOtherReferences } = props

const client = useClient({ apiVersion: apiVersion })
const [data, setData] = useState<SanityDocument[]>()

const getData = async () => {
const data = await client.fetch<SanityDocument[]>(
/* groq */ `*[_type == "translation.metadata" && references($id)]{
...,
_id,
translations[]{
"id":@.value._ref,
"doc": @.value->
}
}{
...,
translations[]{
...,
"dependants": *[references(^.id) && _type!= "translation.metadata"]
}
}`,
{ id: documentId },
)
setLoading(false)
setData(data)
}
const [loading, setLoading] = useState<boolean>(true)
// Get all references and check if any of them are translations metadata

if (loading) getData()
const { translations, otherReferences, translatedDocs } = useMemo(() => separateReferences(data), [data])

useEffect(() => {
setTranslations(translations)
}, [setTranslations, translations])
useEffect(() => {
setHasOtherReferences(otherReferences.length > 0)
}, [setHasOtherReferences, otherReferences])

if (loading) {
return (
<Flex padding={4} align="center" justify="center">
<Spinner />
</Flex>
)
}

return (
<Stack space={4}>
{translations && translations.length > 0 && doc.lang === defaultLanguage.name ? (
<>
<Text>Delete this document and its translations?</Text>
{translatedDocs.length > 0 &&
translatedDocs.map((it) => <DocumentPreview key={it._id} value={it} type={it._type} />)}
</>
) : (
<Text>Delete this document?</Text>
)}
{otherReferences.length > 0 && (
<>
<Card borderTop />
<>
You may not be able to delete this document as there are other published references to this document (or its
translations).
</>
{otherReferences.map((it) => (
<DocumentPreview key={it._id} value={it} type={it._type} />
))}
</>
)}
</Stack>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { SanityDocument } from 'sanity'

export function separateReferences(data: SanityDocument[] | null = []): {
translations: SanityDocument[]
translatedDocs: SanityDocument[]
otherReferences: SanityDocument[]
} {
const translations: SanityDocument[] = []
const otherReferences: SanityDocument[] = []
const translatedDocs: SanityDocument[] = []

if (data && data.length > 0) {
data.forEach((translationMetadata: SanityDocument) => {
translations.push(translationMetadata)
;(translationMetadata.translations as SanityDocument[]).forEach((translation: SanityDocument) => {
if (translation.doc) translatedDocs.push(translation.doc as SanityDocument)
otherReferences.push(...(translation.dependants as SanityDocument[]))
})
})
}

return { translations, otherReferences, translatedDocs }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Button, Grid } from '@sanity/ui'

type DeleteTranslationFooterProps = {
translations: unknown[]
disable: boolean
onClose: () => void
onProceed: () => void
}

export default function DeleteTranslationFooter(props: DeleteTranslationFooterProps) {
const { translations, onClose, onProceed, disable } = props
return (
<Grid columns={2} gap={2}>
<Button text="Cancel" onClick={onClose} mode="ghost" />
<Button
text={translations && translations.length > 0 ? `Delete document and translations` : `Delete document`}
onClick={onProceed}
tone="critical"
disabled={disable}
/>
</Grid>
)
}
Loading

0 comments on commit 1805adb

Please sign in to comment.