From b6625df4edb7d16e0177a8d927ca0f5bbd08bd75 Mon Sep 17 00:00:00 2001 From: Jordan Lawrence Date: Mon, 9 Dec 2024 19:12:45 +0000 Subject: [PATCH 01/12] feat: showing documents in archived and published releases --- .../ReleaseMenuButton/ReleaseMenuButton.tsx | 2 +- .../tool/detail/useBundleDocuments.ts | 139 ++++++++++++++++-- 2 files changed, 131 insertions(+), 10 deletions(-) diff --git a/packages/sanity/src/core/releases/tool/components/ReleaseMenuButton/ReleaseMenuButton.tsx b/packages/sanity/src/core/releases/tool/components/ReleaseMenuButton/ReleaseMenuButton.tsx index 5a5dfb0582b..13898f9162a 100644 --- a/packages/sanity/src/core/releases/tool/components/ReleaseMenuButton/ReleaseMenuButton.tsx +++ b/packages/sanity/src/core/releases/tool/components/ReleaseMenuButton/ReleaseMenuButton.tsx @@ -177,7 +177,7 @@ export const ReleaseMenuButton = ({ignoreCTA, release}: ReleaseMenuButtonProps) const confirmActionDialog = useMemo(() => { if (!selectedAction) return null - const {confirmDialog, ...actionValues} = RELEASE_ACTION_MAP[selectedAction] + const {confirmDialog} = RELEASE_ACTION_MAP[selectedAction] if (!confirmDialog) return null diff --git a/packages/sanity/src/core/releases/tool/detail/useBundleDocuments.ts b/packages/sanity/src/core/releases/tool/detail/useBundleDocuments.ts index c1c570ee16c..270611c2ba3 100644 --- a/packages/sanity/src/core/releases/tool/detail/useBundleDocuments.ts +++ b/packages/sanity/src/core/releases/tool/detail/useBundleDocuments.ts @@ -1,9 +1,10 @@ import {isValidationErrorMarker, type SanityDocument} from '@sanity/types' import {uuid} from '@sanity/uuid' +import {isBefore} from 'date-fns' import {useMemo} from 'react' import {useObservable} from 'react-rx' -import {combineLatest, of} from 'rxjs' -import {filter, map, startWith, switchAll, switchMap} from 'rxjs/operators' +import {combineLatest, from, of} from 'rxjs' +import {filter, map, mergeMap, startWith, switchAll, switchMap, take, toArray} from 'rxjs/operators' import {mergeMapArray} from 'rxjs-mergemap-array' import {DEFAULT_STUDIO_CLIENT_OPTIONS} from 'sanity' @@ -12,7 +13,7 @@ import {getPreviewValueWithFallback, prepareForPreview} from '../../../preview' import {useSource} from '../../../studio' import {getPublishedId} from '../../../util/draftUtils' import {validateDocumentWithReferences, type ValidationStatus} from '../../../validation' -import {useDocumentPreviewStore} from '../../index' +import {getReleaseIdFromReleaseDocumentId, useDocumentPreviewStore, useReleases} from '../../index' export interface DocumentValidationStatus extends ValidationStatus { hasError: boolean @@ -25,17 +26,24 @@ export interface DocumentInRelease { previewValues: {isLoading: boolean; values: ReturnType} } -export function useBundleDocuments(release: string): { +export function useBundleDocuments(releaseId: string): { loading: boolean results: DocumentInRelease[] } { - const groqFilter = `_id in path("versions.${release}.*")` + const groqFilter = `_id in path("versions.${releaseId}.*")` const documentPreviewStore = useDocumentPreviewStore() const {getClient, i18n} = useSource() const schema = useSchema() - const observableClient = useClient(DEFAULT_STUDIO_CLIENT_OPTIONS).observable + const {data: releases, archivedReleases} = useReleases() - const observable = useMemo(() => { + const release = releases + .concat(archivedReleases) + .find((candidate) => getReleaseIdFromReleaseDocumentId(candidate._id) === releaseId) + const client = useClient(DEFAULT_STUDIO_CLIENT_OPTIONS) + const observableClient = client.observable + const {dataset} = client.config() + + const activeReleaseDocumentsObservable = useMemo(() => { return documentPreviewStore.unstable_observeDocumentIdSet(groqFilter).pipe( map((state) => (state.documentIds || []) as string[]), mergeMapArray((id) => { @@ -91,7 +99,7 @@ export function useBundleDocuments(release: string): { getPreviewValueWithFallback({ value: document, version: version.snapshot, - perspective: release, + perspective: `bundle.${releaseId}`, }), schemaType, ), @@ -113,7 +121,120 @@ export function useBundleDocuments(release: string): { }), map((results) => ({loading: false, results})), ) - }, [observableClient, documentPreviewStore, getClient, groqFilter, i18n, release, schema]) + }, [documentPreviewStore, groqFilter, i18n, getClient, schema, observableClient, releaseId]) + + const publishedReleaseDocumentsObservable = useMemo(() => { + if (!release?.finalDocumentStates?.length) return of({loading: false, results: []}) + + const documentStates = release.finalDocumentStates + + return combineLatest([ + from(documentStates).pipe( + mergeMap(({id: documentId}) => { + const document$ = observableClient + .request<{documents: DocumentInRelease['document'][]}>({ + url: `/data/history/${dataset}/documents/${documentId}?lastRevision=true`, + }) + .pipe(map(({documents: [document]}) => document)) + + const previewValues$ = document$.pipe( + switchMap((document) => { + const schemaType = schema.get(document._type) + if (!schemaType) { + throw new Error(`Schema type not found for document type ${document._type}`) + } + + return documentPreviewStore.observeForPreview(document, schemaType).pipe( + take(1), + // eslint-disable-next-line max-nested-callbacks + map((version) => ({ + isLoading: false, + values: prepareForPreview( + getPreviewValueWithFallback({ + value: document, + version: version.snapshot || document, + perspective: `bundle.${releaseId}`, + }), + schemaType, + ), + })), + startWith({isLoading: true, values: {}}), + ) + }), + filter(({isLoading}) => !isLoading), + ) + + return combineLatest([document$, previewValues$]).pipe( + map(([document, previewValues]) => ({ + document, + previewValues, + memoKey: uuid(), + validation: {validation: [], hasError: false, isValidating: false}, + })), + ) + }), + toArray(), + ), + + documentPreviewStore + .unstable_observeDocuments(documentStates.map(({id}) => `${getPublishedId(id)}`)) + .pipe(take(1), filter(Boolean)), + ]).pipe( + // Combine results from both streams + map(([processedDocuments, observedDocuments]) => + processedDocuments.map((result) => { + const documentId = result.document._id + + const getIsNewDocument = () => { + // Check if the document exists in the observedDocuments array + const publishedDocumentExists = observedDocuments.find( + // eslint-disable-next-line max-nested-callbacks + (observedDoc) => observedDoc?._id === getPublishedId(documentId), + ) + + // this means that the pub doc has now been deleted... + // or potentially was deleted as part of this release + if (!publishedDocumentExists) return true + + const releasePublishAt = release.publishAt + if (!releasePublishAt) return false + + const publishedCreatedAtDate = new Date(publishedDocumentExists._createdAt) + const releasePublishAtDate = new Date(releasePublishAt) + + return !isBefore(publishedCreatedAtDate, releasePublishAtDate) + } + + return { + ...result, + document: {...result.document, isNewDocument: getIsNewDocument()}, + } + }), + ), + map((results) => ({ + loading: false, + results, + })), + ) + }, [ + dataset, + documentPreviewStore, + observableClient, + release?.finalDocumentStates, + release?.publishAt, + releaseId, + schema, + ]) + + const observable = useMemo(() => { + if (!release) return of({loading: true, results: []}) + + if (release.state === 'published' || release.state === 'archived') { + return publishedReleaseDocumentsObservable + } + + return activeReleaseDocumentsObservable + }, [activeReleaseDocumentsObservable, publishedReleaseDocumentsObservable, release]) return useObservable(observable, {loading: true, results: []}) } From 7e4cb90b9c57f953778a0cb3e8fa49a3d8fe52a5 Mon Sep 17 00:00:00 2001 From: Jordan Lawrence Date: Mon, 9 Dec 2024 19:13:12 +0000 Subject: [PATCH 02/12] feat: hiding document actions when release is not active --- .../releases/tool/detail/ReleaseSummary.tsx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx b/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx index 59e6c1a1b09..1ac36c52f25 100644 --- a/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx +++ b/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx @@ -42,15 +42,16 @@ export function ReleaseSummary(props: ReleaseSummaryProps) { [documents, documentsHistory], ) - const renderRowActions: ({datum}: {datum: BundleDocumentRow | unknown}) => JSX.Element = - useCallback( - ({datum}) => { - const document = datum as BundleDocumentRow + const renderRowActions: React.FC<{datum: BundleDocumentRow | unknown}> = useCallback( + ({datum}) => { + if (release.state !== 'active') return null - return - }, - [release.metadata.title], - ) + const document = datum as BundleDocumentRow + + return + }, + [release.metadata.title, release.state], + ) const documentTableColumnDefs = useMemo( () => getDocumentTableColumnDefs(release._id, t), From 86ed8fa07a0afb4d3e38c9a321bc4ce40ff6fa19 Mon Sep 17 00:00:00 2001 From: Jordan Lawrence Date: Mon, 9 Dec 2024 19:19:58 +0000 Subject: [PATCH 03/12] feat: hiding document action for archived and published release documents --- .../releases/tool/detail/ReleaseSummary.tsx | 4 +- .../documentTable/DocumentTableColumnDefs.tsx | 86 ++++++++++--------- 2 files changed, 48 insertions(+), 42 deletions(-) diff --git a/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx b/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx index 1ac36c52f25..28a0021d1d6 100644 --- a/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx +++ b/packages/sanity/src/core/releases/tool/detail/ReleaseSummary.tsx @@ -54,8 +54,8 @@ export function ReleaseSummary(props: ReleaseSummaryProps) { ) const documentTableColumnDefs = useMemo( - () => getDocumentTableColumnDefs(release._id, t), - [release._id, t], + () => getDocumentTableColumnDefs(release._id, release.state, t), + [release._id, release.state, t], ) const filterRows = useCallback( diff --git a/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx b/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx index 190817202af..fbf624db31f 100644 --- a/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx +++ b/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx @@ -8,6 +8,7 @@ import {Tooltip} from '../../../../../ui-components/tooltip' import {UserAvatar} from '../../../../components' import {RelativeTime} from '../../../../components/RelativeTime' import {useSchema} from '../../../../hooks' +import {type ReleaseState} from '../../../store' import {isGoingToUnpublish} from '../../../util/isGoingToUnpublish' import {ReleaseDocumentPreview} from '../../components/ReleaseDocumentPreview' import {Headers} from '../../components/Table/TableHeader' @@ -46,54 +47,59 @@ const MemoDocumentType = memo( (prev, next) => prev.type === next.type, ) -export const getDocumentTableColumnDefs: ( - releaseId: string, - t: TFunction<'releases', undefined>, -) => Column[] = (releaseId, t) => [ - { - id: 'action', - width: 100, - header: (props) => ( - - - - ), - cell: ({cellProps, datum}) => { - const willBeUnpublished = isGoingToUnpublish(datum.document) - const actionBadge = () => { - if (willBeUnpublished) { - return ( - - {t('table-body.action.unpublish')} - - ) - } - if (datum.document.publishedDocumentExists) { - return ( - - {t('table-body.action.change')} - - ) - } - +const documentActionColumn: (t: TFunction<'releases', undefined>) => Column = ( + t, +) => ({ + id: 'action', + width: 100, + header: (props) => ( + + + + ), + cell: ({cellProps, datum}) => { + const willBeUnpublished = isGoingToUnpublish(datum.document) + const actionBadge = () => { + if (willBeUnpublished) { return ( - - {t('table-body.action.add')} + + {t('table-body.action.unpublish')} + + ) + } + if (datum.document.publishedDocumentExists) { + return ( + + {t('table-body.action.change')} ) } return ( - - {actionBadge()} - + + {t('table-body.action.add')} + ) - }, + } + + return ( + + {actionBadge()} + + ) }, +}) + +export const getDocumentTableColumnDefs: ( + releaseId: string, + releaseState: ReleaseState, + t: TFunction<'releases', undefined>, +) => Column[] = (releaseId, releaseState, t) => [ + /** + * Hiding action for archived and published releases of v1.0 + * This will be added once Events API has reverse order lookup supported + */ + ...(releaseState === 'archived' || releaseState === 'published' ? [] : [documentActionColumn(t)]), { id: 'document._type', width: 100, From 8e7ceb01c3528ed4a85a76934360899433d34009 Mon Sep 17 00:00:00 2001 From: Jordan Lawrence Date: Tue, 10 Dec 2024 11:10:45 +0000 Subject: [PATCH 04/12] refactor: removing check for existing document in published --- .../tool/detail/useBundleDocuments.ts | 124 ++++++------------ 1 file changed, 42 insertions(+), 82 deletions(-) diff --git a/packages/sanity/src/core/releases/tool/detail/useBundleDocuments.ts b/packages/sanity/src/core/releases/tool/detail/useBundleDocuments.ts index 270611c2ba3..63840d6e301 100644 --- a/packages/sanity/src/core/releases/tool/detail/useBundleDocuments.ts +++ b/packages/sanity/src/core/releases/tool/detail/useBundleDocuments.ts @@ -1,6 +1,5 @@ import {isValidationErrorMarker, type SanityDocument} from '@sanity/types' import {uuid} from '@sanity/uuid' -import {isBefore} from 'date-fns' import {useMemo} from 'react' import {useObservable} from 'react-rx' import {combineLatest, from, of} from 'rxjs' @@ -128,89 +127,51 @@ export function useBundleDocuments(releaseId: string): { const documentStates = release.finalDocumentStates - return combineLatest([ - from(documentStates).pipe( - mergeMap(({id: documentId}) => { - const document$ = observableClient - .request<{documents: DocumentInRelease['document'][]}>({ - url: `/data/history/${dataset}/documents/${documentId}?lastRevision=true`, - }) - .pipe(map(({documents: [document]}) => document)) - - const previewValues$ = document$.pipe( - switchMap((document) => { - const schemaType = schema.get(document._type) - if (!schemaType) { - throw new Error(`Schema type not found for document type ${document._type}`) - } - - return documentPreviewStore.observeForPreview(document, schemaType).pipe( - take(1), - // eslint-disable-next-line max-nested-callbacks - map((version) => ({ - isLoading: false, - values: prepareForPreview( - getPreviewValueWithFallback({ - value: document, - version: version.snapshot || document, - perspective: `bundle.${releaseId}`, - }), - schemaType, - ), - })), - startWith({isLoading: true, values: {}}), - ) - }), - filter(({isLoading}) => !isLoading), - ) - - return combineLatest([document$, previewValues$]).pipe( - map(([document, previewValues]) => ({ - document, - previewValues, - memoKey: uuid(), - validation: {validation: [], hasError: false, isValidating: false}, - })), - ) - }), - toArray(), - ), - - documentPreviewStore - .unstable_observeDocuments(documentStates.map(({id}) => `${getPublishedId(id)}`)) - .pipe(take(1), filter(Boolean)), - ]).pipe( - // Combine results from both streams - map(([processedDocuments, observedDocuments]) => - processedDocuments.map((result) => { - const documentId = result.document._id - - const getIsNewDocument = () => { - // Check if the document exists in the observedDocuments array - const publishedDocumentExists = observedDocuments.find( - // eslint-disable-next-line max-nested-callbacks - (observedDoc) => observedDoc?._id === getPublishedId(documentId), - ) - - // this means that the pub doc has now been deleted... - // or potentially was deleted as part of this release - if (!publishedDocumentExists) return true + return from(documentStates).pipe( + mergeMap(({id: documentId}) => { + const document$ = observableClient + .request<{documents: DocumentInRelease['document'][]}>({ + url: `/data/history/${dataset}/documents/${documentId}?lastRevision=true`, + }) + .pipe(map(({documents: [document]}) => document)) - const releasePublishAt = release.publishAt - if (!releasePublishAt) return false - - const publishedCreatedAtDate = new Date(publishedDocumentExists._createdAt) - const releasePublishAtDate = new Date(releasePublishAt) + const previewValues$ = document$.pipe( + switchMap((document) => { + const schemaType = schema.get(document._type) + if (!schemaType) { + throw new Error(`Schema type not found for document type ${document._type}`) + } - return !isBefore(publishedCreatedAtDate, releasePublishAtDate) - } + return documentPreviewStore.observeForPreview(document, schemaType).pipe( + take(1), + // eslint-disable-next-line max-nested-callbacks + map((version) => ({ + isLoading: false, + values: prepareForPreview( + getPreviewValueWithFallback({ + value: document, + version: version.snapshot || document, + perspective: `bundle.${releaseId}`, + }), + schemaType, + ), + })), + startWith({isLoading: true, values: {}}), + ) + }), + filter(({isLoading}) => !isLoading), + ) - return { - ...result, - document: {...result.document, isNewDocument: getIsNewDocument()}, - } - }), - ), + return combineLatest([document$, previewValues$]).pipe( + map(([document, previewValues]) => ({ + document, + previewValues, + memoKey: uuid(), + validation: {validation: [], hasError: false, isValidating: false}, + })), + ) + }), + toArray(), map((results) => ({ loading: false, results, @@ -221,7 +182,6 @@ export function useBundleDocuments(releaseId: string): { documentPreviewStore, observableClient, release?.finalDocumentStates, - release?.publishAt, releaseId, schema, ]) From 3939307c1280ff86612fffb4821c2cf604fa6c1d Mon Sep 17 00:00:00 2001 From: Jordan Lawrence Date: Tue, 10 Dec 2024 12:17:05 +0000 Subject: [PATCH 05/12] fix: preventing linking to version doc when release is archived/published --- .../tool/components/ReleaseDocumentPreview.tsx | 11 +++++++++-- .../documentTable/DocumentTableColumnDefs.tsx | 13 ++++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/sanity/src/core/releases/tool/components/ReleaseDocumentPreview.tsx b/packages/sanity/src/core/releases/tool/components/ReleaseDocumentPreview.tsx index b1f406405b7..7010145e7ba 100644 --- a/packages/sanity/src/core/releases/tool/components/ReleaseDocumentPreview.tsx +++ b/packages/sanity/src/core/releases/tool/components/ReleaseDocumentPreview.tsx @@ -53,9 +53,16 @@ export function ReleaseDocumentPreview({ [documentPresence], ) + const preview = ( + + ) + + /** @todo revision deeplink support for archived and published version docs */ + if (revision) return preview + return ( - - + + {preview} ) } diff --git a/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx b/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx index fbf624db31f..c40acb8d5ca 100644 --- a/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx +++ b/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx @@ -20,15 +20,18 @@ const MemoReleaseDocumentPreview = memo( function MemoReleaseDocumentPreview({ item, releaseId, + revision, }: { item: DocumentInRelease releaseId: string + revision?: string }) { return ( ( - + ), }, From 097793f110a49c7e56bc6c278807399512d3a904 Mon Sep 17 00:00:00 2001 From: Jordan Lawrence Date: Tue, 10 Dec 2024 12:24:48 +0000 Subject: [PATCH 06/12] fix: preventing linking to version doc when release is archived/published --- .../core/releases/tool/components/ReleaseDocumentPreview.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/sanity/src/core/releases/tool/components/ReleaseDocumentPreview.tsx b/packages/sanity/src/core/releases/tool/components/ReleaseDocumentPreview.tsx index 7010145e7ba..1003b035fd6 100644 --- a/packages/sanity/src/core/releases/tool/components/ReleaseDocumentPreview.tsx +++ b/packages/sanity/src/core/releases/tool/components/ReleaseDocumentPreview.tsx @@ -15,6 +15,7 @@ interface ReleaseDocumentPreviewProps { releaseId: string previewValues: PreviewValue isLoading: boolean + revision?: string hasValidationError?: boolean } @@ -24,6 +25,7 @@ export function ReleaseDocumentPreview({ releaseId, previewValues, isLoading, + revision, hasValidationError, }: ReleaseDocumentPreviewProps) { const documentPresence = useDocumentPresence(documentId) From e5e17ab60317669004f64b1147008e13e50b17f7 Mon Sep 17 00:00:00 2001 From: Jordan Lawrence Date: Tue, 10 Dec 2024 12:25:34 +0000 Subject: [PATCH 07/12] fix: preventing linking to version doc when release is archived/published --- .../core/releases/tool/components/ReleaseDocumentPreview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sanity/src/core/releases/tool/components/ReleaseDocumentPreview.tsx b/packages/sanity/src/core/releases/tool/components/ReleaseDocumentPreview.tsx index 1003b035fd6..b98615fb18f 100644 --- a/packages/sanity/src/core/releases/tool/components/ReleaseDocumentPreview.tsx +++ b/packages/sanity/src/core/releases/tool/components/ReleaseDocumentPreview.tsx @@ -63,7 +63,7 @@ export function ReleaseDocumentPreview({ if (revision) return preview return ( - + {preview} ) From 577843f082da119a48a1488be51c81738bda9bbe Mon Sep 17 00:00:00 2001 From: Jordan Lawrence Date: Wed, 11 Dec 2024 11:29:33 +0000 Subject: [PATCH 08/12] feat: deeplinking to published or archived versions --- .../components/ReleaseDocumentPreview.tsx | 44 ++++++++++++------- .../documentTable/DocumentTableColumnDefs.tsx | 17 ++++--- .../detail/review/DocumentReviewHeader.tsx | 1 - 3 files changed, 37 insertions(+), 25 deletions(-) diff --git a/packages/sanity/src/core/releases/tool/components/ReleaseDocumentPreview.tsx b/packages/sanity/src/core/releases/tool/components/ReleaseDocumentPreview.tsx index b98615fb18f..09c10d7b7b4 100644 --- a/packages/sanity/src/core/releases/tool/components/ReleaseDocumentPreview.tsx +++ b/packages/sanity/src/core/releases/tool/components/ReleaseDocumentPreview.tsx @@ -6,7 +6,7 @@ import {IntentLink} from 'sanity/router' import {DocumentPreviewPresence} from '../../../presence' import {SanityDefaultPreview} from '../../../preview/components/SanityDefaultPreview' import {getPublishedId} from '../../../util/draftUtils' -import {useDocumentPresence} from '../../index' +import {type ReleaseState, useDocumentPresence} from '../../index' import {getReleaseIdFromReleaseDocumentId} from '../../util/getReleaseIdFromReleaseDocumentId' interface ReleaseDocumentPreviewProps { @@ -15,8 +15,8 @@ interface ReleaseDocumentPreviewProps { releaseId: string previewValues: PreviewValue isLoading: boolean - revision?: string - hasValidationError?: boolean + releaseState?: ReleaseState + documentRevision?: string } export function ReleaseDocumentPreview({ @@ -25,11 +25,24 @@ export function ReleaseDocumentPreview({ releaseId, previewValues, isLoading, - revision, - hasValidationError, + releaseState, + documentRevision, }: ReleaseDocumentPreviewProps) { const documentPresence = useDocumentPresence(documentId) + const intentParams = useMemo(() => { + if (releaseState !== 'published' && releaseState !== 'archived') return {} + + const rev = releaseState === 'archived' ? '@lastEdited' : '@lastPublished' + + return { + rev, + inspect: 'sanity/structure/history', + historyEvent: documentRevision, + historyVersion: getReleaseIdFromReleaseDocumentId(releaseId), + } + }, [documentRevision, releaseId, releaseState]) + const LinkComponent = useMemo( () => // eslint-disable-next-line @typescript-eslint/no-shadow @@ -41,13 +54,21 @@ export function ReleaseDocumentPreview({ params={{ id: getPublishedId(documentId), type: documentTypeName, + ...intentParams, }} - searchParams={[['perspective', getReleaseIdFromReleaseDocumentId(releaseId)]]} + searchParams={[ + [ + 'perspective', + releaseState === 'published' + ? 'published' + : getReleaseIdFromReleaseDocumentId(releaseId), + ], + ]} ref={ref} /> ) }), - [documentId, documentTypeName, releaseId], + [documentId, documentTypeName, intentParams, releaseId, releaseState], ) const previewPresence = useMemo( @@ -55,16 +76,9 @@ export function ReleaseDocumentPreview({ [documentPresence], ) - const preview = ( - - ) - - /** @todo revision deeplink support for archived and published version docs */ - if (revision) return preview - return ( - {preview} + ) } diff --git a/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx b/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx index c40acb8d5ca..6eea0d27510 100644 --- a/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx +++ b/packages/sanity/src/core/releases/tool/detail/documentTable/DocumentTableColumnDefs.tsx @@ -20,21 +20,23 @@ const MemoReleaseDocumentPreview = memo( function MemoReleaseDocumentPreview({ item, releaseId, - revision, + releaseState, + documentRevision, }: { item: DocumentInRelease releaseId: string - revision?: string + releaseState?: ReleaseState + documentRevision?: string }) { return ( ) }, @@ -135,11 +137,8 @@ export const getDocumentTableColumnDefs: ( ), diff --git a/packages/sanity/src/core/releases/tool/detail/review/DocumentReviewHeader.tsx b/packages/sanity/src/core/releases/tool/detail/review/DocumentReviewHeader.tsx index ea52a516b25..5fc47d787d8 100644 --- a/packages/sanity/src/core/releases/tool/detail/review/DocumentReviewHeader.tsx +++ b/packages/sanity/src/core/releases/tool/detail/review/DocumentReviewHeader.tsx @@ -55,7 +55,6 @@ export function DocumentReviewHeader({ releaseId={releaseId} previewValues={previewValues} isLoading={isLoading} - hasValidationError={validation?.hasError} /> From 839f0a9b426865805b50d1df6ef187c070b0570d Mon Sep 17 00:00:00 2001 From: Jordan Lawrence Date: Wed, 11 Dec 2024 11:35:39 +0000 Subject: [PATCH 09/12] feat: doc form banner clarifies if release was published or archived --- packages/sanity/src/structure/i18n/resources.ts | 5 ++++- .../banners/ArchivedReleaseDocumentBanner.tsx | 8 +++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/sanity/src/structure/i18n/resources.ts b/packages/sanity/src/structure/i18n/resources.ts index 59ecdc7b92b..d95552f1ae9 100644 --- a/packages/sanity/src/structure/i18n/resources.ts +++ b/packages/sanity/src/structure/i18n/resources.ts @@ -90,7 +90,7 @@ const structureLocaleStrings = defineLocalesResources('structure', { 'This document has live edit enabled and cannot be unpublished', /** Description for the archived release banner, rendered when viewing the history of a version document from the publihed view */ 'banners.archived-release.description': - "You are viewing a read-only document that was published as part of a release. It can't be edited", + "You are viewing a read-only document that was archived as part of a release. It can't be edited", /** The text for the restore button on the deleted document banner */ 'banners.deleted-document-banner.restore-button.text': 'Restore most recent revision', /** The text content for the deleted document banner */ @@ -124,6 +124,9 @@ const structureLocaleStrings = defineLocalesResources('structure', { 'banners.permission-check-banner.request-permission-button.sent': 'Editor request sent', /** The text for the request permission button that appears for viewer roles */ 'banners.permission-check-banner.request-permission-button.text': 'Ask to edit', + /** Description for the archived release banner, rendered when viewing the history of a version document from the publihed view */ + 'banners.published-release.description': + "You are viewing a read-only document that was published as part of a release. It can't be edited", /** The text for the reload button */ 'banners.reference-changed-banner.reason-changed.reload-button.text': 'Reload reference', /** The text for the reference change banner if the reason is that the reference has been changed */ diff --git a/packages/sanity/src/structure/panes/document/documentPanel/banners/ArchivedReleaseDocumentBanner.tsx b/packages/sanity/src/structure/panes/document/documentPanel/banners/ArchivedReleaseDocumentBanner.tsx index 1357050b1a5..4bed1b3d870 100644 --- a/packages/sanity/src/structure/panes/document/documentPanel/banners/ArchivedReleaseDocumentBanner.tsx +++ b/packages/sanity/src/structure/panes/document/documentPanel/banners/ArchivedReleaseDocumentBanner.tsx @@ -31,6 +31,12 @@ export function ArchivedReleaseDocumentBanner(): JSX.Element { (r) => getReleaseIdFromReleaseDocumentId(r._id) === params?.historyVersion, ) }, [archivedReleases, params?.historyVersion]) + + const description = + release?.state === 'published' + ? 'banners.published-release.description' + : 'banners.archived-release.description' + return ( { if (!release) return children From b939ef464cbc573372209be6bc0ed57a42daf791 Mon Sep 17 00:00:00 2001 From: Jordan Lawrence Date: Wed, 11 Dec 2024 12:20:52 +0000 Subject: [PATCH 10/12] feat: showing archived chips when history viewing an archived version --- .../components/documentHeader/VersionChip.tsx | 53 ++++++++++++------ .../perspective/DocumentPerspectiveList.tsx | 55 +++++++++++++++---- 2 files changed, 82 insertions(+), 26 deletions(-) diff --git a/packages/sanity/src/core/releases/components/documentHeader/VersionChip.tsx b/packages/sanity/src/core/releases/components/documentHeader/VersionChip.tsx index c9307a004c5..c7cbb4ea57f 100644 --- a/packages/sanity/src/core/releases/components/documentHeader/VersionChip.tsx +++ b/packages/sanity/src/core/releases/components/documentHeader/VersionChip.tsx @@ -10,33 +10,49 @@ import { useRef, useState, } from 'react' -import {styled} from 'styled-components' +import {css, styled} from 'styled-components' import {Button, Popover, Tooltip} from '../../../../ui-components' import {getVersionId} from '../../../util/draftUtils' import {useVersionOperations} from '../../hooks/useVersionOperations' -import {type ReleaseDocument} from '../../store/types' +import {type ReleaseDocument, type ReleaseState} from '../../store/types' import {getReleaseIdFromReleaseDocumentId} from '../../util/getReleaseIdFromReleaseDocumentId' import {DiscardVersionDialog} from '../dialog/DiscardVersionDialog' import {ReleaseAvatar} from '../ReleaseAvatar' import {VersionContextMenu} from './contextMenu/VersionContextMenu' import {CopyToNewReleaseDialog} from './dialog/CopyToNewReleaseDialog' -const Chip = styled(Button)` - border-radius: 9999px !important; - transition: none; - text-decoration: none !important; - cursor: pointer; +interface ChipStyleProps { + $isArchived?: boolean +} - // target enabled state - &:not([data-disabled='true']) { - --card-border-color: var(--card-badge-default-bg-color); - } +const Chip = styled(Button)( + ({$isArchived}) => + ` + border-radius: 9999px !important; + transition: none; + text-decoration: none !important; + cursor: pointer; - &[data-disabled='true'] { - color: var(--card-muted-fg-color); - } -` + // target enabled state + &:not([data-disabled='true']) { + --card-border-color: var(--card-badge-default-bg-color); + } + + &[data-disabled='true'] { + color: var(--card-muted-fg-color); + cursor: default; + + // archived will be disabled but should have bg color + ${ + $isArchived && + css` + background-color: var(--card-badge-default-bg-color); + ` + } + } + `, +) /** * @internal @@ -56,6 +72,7 @@ export const VersionChip = memo(function VersionChip(props: { documentType: string menuReleaseId: string fromRelease: string + releaseState?: ReleaseState isVersion: boolean disabled?: boolean } @@ -75,6 +92,7 @@ export const VersionChip = memo(function VersionChip(props: { documentType, menuReleaseId, fromRelease, + releaseState, isVersion, disabled: contextMenuDisabled = false, }, @@ -162,6 +180,8 @@ export const VersionChip = memo(function VersionChip(props: { } as HTMLElement }, [contextMenuPoint]) + const contextMenuHandler = disabled ? undefined : handleContextMenu + return ( <> @@ -178,7 +198,8 @@ export const VersionChip = memo(function VersionChip(props: { tone={tone} icon={} iconRight={locked && LockIcon} - onContextMenu={handleContextMenu} + onContextMenu={contextMenuHandler} + $isArchived={releaseState === 'archived'} /> diff --git a/packages/sanity/src/structure/panes/document/documentPanel/header/perspective/DocumentPerspectiveList.tsx b/packages/sanity/src/structure/panes/document/documentPanel/header/perspective/DocumentPerspectiveList.tsx index e686d75d805..32b0034ff94 100644 --- a/packages/sanity/src/structure/panes/document/documentPanel/header/perspective/DocumentPerspectiveList.tsx +++ b/packages/sanity/src/structure/panes/document/documentPanel/header/perspective/DocumentPerspectiveList.tsx @@ -2,6 +2,7 @@ import {Text} from '@sanity/ui' import {memo, useCallback, useMemo} from 'react' import { formatRelativeLocalePublishDate, + getReleaseIdFromReleaseDocumentId, getReleaseTone, getVersionFromId, isReleaseScheduledOrScheduling, @@ -14,6 +15,7 @@ import { VersionChip, versionDocumentExists, } from 'sanity' +import {usePaneRouter} from 'sanity/structure' import {useDocumentPane} from '../../../useDocumentPane' @@ -66,18 +68,19 @@ export const DocumentPerspectiveList = memo(function DocumentPerspectiveList() { const {selectedPerspectiveName} = usePerspective() const {t} = useTranslation() const {setPerspective} = usePerspective() + const {params} = usePaneRouter() const dateTimeFormat = useDateTimeFormat({ dateStyle: 'medium', timeStyle: 'short', }) - const {data: releases, loading} = useReleases() + const {data: releases, loading, archivedReleases} = useReleases() const {documentVersions, editState, displayed, documentType} = useDocumentPane() const filteredReleases: FilterReleases = useMemo(() => { if (!documentVersions) return {notCurrentReleases: [], currentReleases: []} - return releases.reduce( + const activeReleases = releases.reduce( (acc: FilterReleases, release) => { const versionDocExists = versionDocumentExists(documentVersions, release._id) if (versionDocExists) { @@ -89,7 +92,21 @@ export const DocumentPerspectiveList = memo(function DocumentPerspectiveList() { }, {notCurrentReleases: [], currentReleases: []}, ) - }, [documentVersions, releases]) + + // without historyVersion, version is not in an archived release + if (!params?.historyVersion) return activeReleases + + const archivedRelease = archivedReleases.find( + (r) => getReleaseIdFromReleaseDocumentId(r._id) === params?.historyVersion, + ) + + // only for explicitly archived releases; published releases use published perspective + if (archivedRelease?.state === 'archived') { + activeReleases.currentReleases.push(archivedRelease) + } + + return activeReleases + }, [archivedReleases, documentVersions, params?.historyVersion, releases]) const handleBundleChange = useCallback( (bundleId: string) => () => { @@ -107,6 +124,19 @@ export const DocumentPerspectiveList = memo(function DocumentPerspectiveList() { return !editState?.published }, [editState?.liveEdit, editState?.published]) + const getIsReleaseSelected = useCallback( + (release: ReleaseDocument): {selected: boolean; disabled?: boolean} => { + if (!params?.historyVersion) + return {selected: release.name === getVersionFromId(displayed?._id || '')} + + const isReleaseHistoryMatch = + getReleaseIdFromReleaseDocumentId(release._id) === params.historyVersion + + return {selected: isReleaseHistoryMatch, disabled: isReleaseHistoryMatch} + }, + [displayed?._id, params?.historyVersion], + ) + return ( <> } - selected={release.name === getVersionFromId(displayed?._id || '')} + {...getIsReleaseSelected(release)} + // selected + // disabled={false} onClick={handleBundleChange(release.name)} text={release.metadata.title || t('release.placeholder-untitled-release')} tone={getReleaseTone(release)} @@ -223,6 +257,7 @@ export const DocumentPerspectiveList = memo(function DocumentPerspectiveList() { releasesLoading: loading, documentType: documentType, fromRelease: release.name, + releaseState: release.state, isVersion: true, }} /> From 886ccf7704192730105166fb7b6e5cc750f13046 Mon Sep 17 00:00:00 2001 From: Jordan Lawrence Date: Wed, 11 Dec 2024 12:45:49 +0000 Subject: [PATCH 11/12] refactor: using simple releaseId for perspective rather than bundle prefix --- .../src/core/releases/tool/detail/useBundleDocuments.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sanity/src/core/releases/tool/detail/useBundleDocuments.ts b/packages/sanity/src/core/releases/tool/detail/useBundleDocuments.ts index 63840d6e301..1f4673ba8ab 100644 --- a/packages/sanity/src/core/releases/tool/detail/useBundleDocuments.ts +++ b/packages/sanity/src/core/releases/tool/detail/useBundleDocuments.ts @@ -98,7 +98,7 @@ export function useBundleDocuments(releaseId: string): { getPreviewValueWithFallback({ value: document, version: version.snapshot, - perspective: `bundle.${releaseId}`, + perspective: releaseId, }), schemaType, ), @@ -151,7 +151,7 @@ export function useBundleDocuments(releaseId: string): { getPreviewValueWithFallback({ value: document, version: version.snapshot || document, - perspective: `bundle.${releaseId}`, + perspective: releaseId, }), schemaType, ), From fd9d30e4fb3fcc6c6998eb452cd1c8886216025c Mon Sep 17 00:00:00 2001 From: Jordan Lawrence Date: Wed, 11 Dec 2024 12:58:16 +0000 Subject: [PATCH 12/12] refactor: simplifying method to generate perspective list state --- .../header/perspective/DocumentPerspectiveList.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/sanity/src/structure/panes/document/documentPanel/header/perspective/DocumentPerspectiveList.tsx b/packages/sanity/src/structure/panes/document/documentPanel/header/perspective/DocumentPerspectiveList.tsx index 32b0034ff94..df3a5201870 100644 --- a/packages/sanity/src/structure/panes/document/documentPanel/header/perspective/DocumentPerspectiveList.tsx +++ b/packages/sanity/src/structure/panes/document/documentPanel/header/perspective/DocumentPerspectiveList.tsx @@ -124,7 +124,7 @@ export const DocumentPerspectiveList = memo(function DocumentPerspectiveList() { return !editState?.published }, [editState?.liveEdit, editState?.published]) - const getIsReleaseSelected = useCallback( + const getReleaseChipState = useCallback( (release: ReleaseDocument): {selected: boolean; disabled?: boolean} => { if (!params?.historyVersion) return {selected: release.name === getVersionFromId(displayed?._id || '')} @@ -243,9 +243,7 @@ export const DocumentPerspectiveList = memo(function DocumentPerspectiveList() { } - {...getIsReleaseSelected(release)} - // selected - // disabled={false} + {...getReleaseChipState(release)} onClick={handleBundleChange(release.name)} text={release.metadata.title || t('release.placeholder-untitled-release')} tone={getReleaseTone(release)}