Skip to content

Instantly share code, notes, and snippets.

@SimeonGriggs
Created December 16, 2022 15:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SimeonGriggs/0b65936d35717bc9bdc05e49f9a93e3f to your computer and use it in GitHub Desktop.
Save SimeonGriggs/0b65936d35717bc9bdc05e49f9a93e3f to your computer and use it in GitHub Desktop.
Sanity Studio v3 Document Action to "de-reference" a document by patching all documents that reference the current one. Incredibly dangerous.
import {useState, useEffect, useCallback} from 'react'
import {useToast, Card, Button, Stack, Text, Code} from '@sanity/ui'
import {extractWithPath} from '@sanity/mutator'
import {
Preview,
DocumentActionProps,
SanityDocument,
useClient,
useSchema,
pathToString,
} from 'sanity'
type Extraction = {
path: string[]
value: any
}
export function Dereference(props: DocumentActionProps) {
const {id, onComplete} = props
const [dialogOpen, setDialogOpen] = useState(false)
const client = useClient({apiVersion: '2022-12-16'})
const [references, setReferences] = useState<{doc: SanityDocument; paths: Extraction[]}[]>([])
const schema = useSchema()
const toast = useToast()
const handleDereference = useCallback(() => {
const transaction = client.transaction()
references.forEach((ref) => {
ref.paths.forEach((path) => {
transaction.patch(ref.doc._id, {
unset: [pathToString(path.path)],
})
})
})
transaction.commit().then(() => {
onComplete()
setDialogOpen(false)
toast.push({
status: 'success',
title: 'Dereferenced!',
})
})
}, [client, onComplete, references, toast])
useEffect(() => {
if (!references.length) {
client.fetch(`*[references($id)]`, {id}).then((res) => {
if (!res || !res.length) {
return
}
const refs = res.map((doc: SanityDocument) => {
const path = `.._ref`
const paths = extractWithPath(path, doc).filter((p) => p.value === id)
return {paths, doc}
})
if (refs.length) {
setReferences(refs)
}
})
}
}, [id, client, references.length])
return {
disabled: references.length < 1,
label: 'Dereference',
onHandle: () => setDialogOpen(true),
dialog: dialogOpen && {
type: 'modal',
title: 'Dereference',
content: (
<Stack space={3}>
<Card border padding={3}>
<Stack space={5}>
{references.map((ref) => {
const docSchema = schema.get(ref.doc._type)
return (
<Stack key={ref.doc._id} space={3}>
<Card borderBottom paddingBottom={3}>
{docSchema ? (
<Preview value={ref.doc} schemaType={docSchema} />
) : (
<Code>{ref.doc._id}</Code>
)}
</Card>
<Text>
{ref.paths.length} {ref.paths.length === 1 ? `Reference` : `References`}
</Text>
{ref.paths.map((path) => (
<Code key={pathToString(path.path)}>{pathToString(path.path)}</Code>
))}
</Stack>
)
})}
</Stack>
</Card>
<Button
onClick={handleDereference}
tone="critical"
text={`Dereference from ${references.length} Document(s)`}
/>
</Stack>
),
onClose: () => {
onComplete()
setDialogOpen(false)
},
},
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment