diff --git a/dev/test-studio/schema/playlist.ts b/dev/test-studio/schema/playlist.ts index 9d89a777f9a..82047b510ff 100644 --- a/dev/test-studio/schema/playlist.ts +++ b/dev/test-studio/schema/playlist.ts @@ -6,7 +6,8 @@ export default defineType({ type: 'document', // eslint-disable-next-line camelcase __experimental_formPreviewTitle: false, - liveEdit: true, + // TODO: How to handle live edit? Push to history with every keypress? + // liveEdit: true, fields: [ { name: 'name', @@ -14,20 +15,12 @@ export default defineType({ type: 'string', }, { - name: 'description', - title: 'Description', - type: 'text', - }, - { - name: 'tracks', - title: 'Tracks', - type: 'array', - of: [{type: 'playlistTrack'}], - }, - { - name: 'image', - title: 'Image', - type: 'image', + name: 'slug', + type: 'slug', + title: 'Slug', + options: { + source: 'name', + }, }, ], }) diff --git a/packages/@sanity/types/src/schema/definition/type/slug.ts b/packages/@sanity/types/src/schema/definition/type/slug.ts index ca6edcc0242..14584739303 100644 --- a/packages/@sanity/types/src/schema/definition/type/slug.ts +++ b/packages/@sanity/types/src/schema/definition/type/slug.ts @@ -9,6 +9,7 @@ import {type BaseSchemaDefinition} from './common' export interface SlugValue { _type: 'slug' current?: string + history?: {slug: string; lastSeen: string; lastSeenPublishedAt: string}[] } /** @public */ diff --git a/packages/sanity/src/core/form/inputs/Slug/SlugInput.tsx b/packages/sanity/src/core/form/inputs/Slug/SlugInput.tsx index 4ca96b2d771..49034cec6b0 100644 --- a/packages/sanity/src/core/form/inputs/Slug/SlugInput.tsx +++ b/packages/sanity/src/core/form/inputs/Slug/SlugInput.tsx @@ -7,9 +7,12 @@ import { type SlugSourceFn, type SlugValue, } from '@sanity/types' -import {Box, Card, Flex, Stack, TextInput} from '@sanity/ui' +import {Box, Card, Code, Flex, Stack, TextInput} from '@sanity/ui' import * as PathUtils from '@sanity/util/paths' +import {uuid} from '@sanity/uuid' import {type FormEvent, useCallback, useMemo} from 'react' +import {useObservable} from 'react-rx' +import {getPublishedId, useDocumentStore, useFormValue} from 'sanity' import {Button} from '../../../../ui-components' import {useTranslation} from '../../../i18n' @@ -54,6 +57,18 @@ async function getNewFromSource( * @beta */ export function SlugInput(props: SlugInputProps) { + const formValue = useFormValue([]) as SanityDocument + const documentStore = useDocumentStore() + const id = getPublishedId(formValue._id) + const type = formValue._type + const observable = useMemo( + () => documentStore.pair.editState(id, type), + [documentStore.pair, id, type], + ) + const {published} = useObservable(observable)! + // @ts-expect-error fix this + const publishedSlug = published?.slug?.current as string | undefined + const getFormValue = useGetFormValue() const {path, value, schemaType, validation, onChange, readOnly, elementProps} = props const sourceField = schemaType.options?.source @@ -66,15 +81,62 @@ export function SlugInput(props: SlugInputProps) { const updateSlug = useCallback( (nextSlug: string) => { if (!nextSlug) { - onChange(PatchEvent.from(unset([]))) + onChange(PatchEvent.from(unset(['current']))) return } onChange( PatchEvent.from([setIfMissing({_type: schemaType.name}), set(nextSlug, ['current'])]), ) + + if (publishedSlug) { + const currentHistory = props.value?.history || [] + + // Check if the current slug is the same as the last published slug + // if it's the same, remove the last published slug from the history array + const lastHistory = currentHistory[currentHistory.length - 1] + if (lastHistory?.slug === nextSlug) { + const nextHistory = currentHistory.slice(0, -1) + if (nextHistory.length === 0) { + onChange(PatchEvent.from([unset(['history'])])) + } else { + onChange(PatchEvent.from([set(nextHistory, ['history'])])) + } + return + } + + if (nextSlug !== publishedSlug) { + // We need to check if the currentHistory includes the last published slug with the lastSeen matching the published._rev + // If it's included, don't do anything + // If it's not included, push the published slug to the history array + const newSlugEntry = { + slug: publishedSlug, + lastSeen: published?._rev, + _key: uuid(), + lastSeenPublishedAt: published?._updatedAt, + } + const exists = currentHistory.find( + (item) => item.slug === newSlugEntry.slug && item.lastSeen === newSlugEntry.lastSeen, + ) + if (exists) return + // Create the history array if it doesn't exist and push the published slug to it + onChange( + PatchEvent.from([ + setIfMissing([], ['history']), + set([...currentHistory, newSlugEntry], ['history']), + ]), + ) + } + } }, - [onChange, schemaType.name], + [ + onChange, + schemaType.name, + publishedSlug, + props.value?.history, + published?._rev, + published?._updatedAt, + ], ) const handleAsyncGenerateSlug = useCallback(() => { @@ -133,6 +195,9 @@ export function SlugInput(props: SlugInputProps) { /> )} + + {JSON.stringify(value, null, 2)} + ) } diff --git a/packages/sanity/src/core/schema/types/slug.ts b/packages/sanity/src/core/schema/types/slug.ts index dd67a1f3d2d..0d269374656 100644 --- a/packages/sanity/src/core/schema/types/slug.ts +++ b/packages/sanity/src/core/schema/types/slug.ts @@ -19,5 +19,32 @@ export default { type: 'string', hidden: true, }, + { + name: 'history', + title: 'Slug history', + type: 'array', + of: [ + { + type: 'object', + fields: [ + { + name: 'slug', + title: 'Slug', + type: 'string', + }, + { + name: 'lastSeen', + title: 'Last seen revision', + type: 'string', + }, + { + name: 'lastSeenPublishedAt', + title: 'Last seen published at', + type: 'datetime', + }, + ], + }, + ], + }, ], }