Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(structure): navigate to version when document only has versions #8759

Draft
wants to merge 10 commits into
base: next
Choose a base branch
from
Draft
1 change: 1 addition & 0 deletions packages/sanity/src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export {
RELEASES_STUDIO_CLIENT_OPTIONS,
useActiveReleases,
useArchivedReleases,
useDocumentVersionList,
useDocumentVersions,
useIsReleaseActive,
useReleasesIds,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ const StyledVersionInlineBadge = styled.span<{$tone?: BadgeTone}>((props) => {
color: var(--card-badge-${$tone ?? 'default'}-fg-color);
background-color: var(--card-badge-${$tone ?? 'default'}-bg-color);
border-radius: 3px;
text-decoration: none;
padding: 0px 2px;
text-decoration: none;
font-weight: 500;
`
})
Expand Down
1 change: 1 addition & 0 deletions packages/sanity/src/core/releases/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './useDocumentVersionList'
export * from './useDocumentVersions'
export * from './useIsReleaseActive'
export * from './useVersionOperations'
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {useMemo} from 'react'

import {getVersionFromId, isVersionId} from '../../util/draftUtils'
import {type ReleaseDocument, type ReleaseType} from '../store/types'
import {useActiveReleases} from '../store/useActiveReleases'
import {getReleaseIdFromReleaseDocumentId} from '../util/getReleaseIdFromReleaseDocumentId'
import {useDocumentVersions} from './useDocumentVersions'

const orderedReleaseTypes: ReleaseType[] = ['asap', 'scheduled', 'undecided']

interface useDocumentVersionListState {
sortedDocumentList: ReleaseDocument[]
onlyHasVersions: boolean
}

export const useDocumentVersionList = ({
documentId,
}: {
documentId: string
}): useDocumentVersionListState => {
const {data: releases} = useActiveReleases()
const {data: documentVersions} = useDocumentVersions({documentId})

const sortedDocumentList = releases
.filter(({_id}) => {
return documentVersions.some(
(id) => getVersionFromId(id) === getReleaseIdFromReleaseDocumentId(_id),
)
})
.sort((a, b) => {
return (
orderedReleaseTypes.indexOf(a.metadata.releaseType) -
orderedReleaseTypes.indexOf(b.metadata.releaseType)
)
})

const onlyHasVersions =
documentVersions &&
documentVersions.length > 0 &&
!documentVersions.some((version) => !isVersionId(version))

return useMemo(
() => ({
sortedDocumentList: sortedDocumentList,
onlyHasVersions,
}),
[sortedDocumentList, onlyHasVersions],
)
}
8 changes: 8 additions & 0 deletions packages/sanity/src/structure/i18n/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ const structureLocaleStrings = defineLocalesResources('structure', {
'This reference has been removed since you opened it.',
/** The text that appears for the action button to add the current document to the global release */
'banners.release.action.add-to-release': 'Add to release',
/** The text that appears for the action button to add the current document to the global release */
'banners.release.action.open-to-edit': 'Open release to edit',
/** The text for the banner that appears when a document only has versions but is in a draft or published pinned release */
'banners.release.navigate-to-edit-description': 'The document only exists in the',
/** The text for the banner that appears when a document only has versions but is in a draft or published pinned release */
'banners.release.navigate-to-edit-description-end_one': 'release',
/** The text for the banner that appears when a document only has versions but is in a draft or published pinned release */
'banners.release.navigate-to-edit-description-end_other': 'releases',
/** The text for the banner that appears when a document is not in the current global release */
'banners.release.not-in-release': 'Not in the <VersionBadge>{{title}}</VersionBadge> release.',

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
getDraftId,
getExpandOperations,
getPublishedId,
getReleaseIdFromReleaseDocumentId,
getVersionFromId,
isGoingToUnpublish,
isPublishedPerspective,
Expand All @@ -39,6 +40,8 @@ import {
useCopyPaste,
useDocumentOperation,
useDocumentValuePermissions,
useDocumentVersionList,
useDocumentVersions,
useEditState,
useFormState,
useInitialValue,
Expand Down Expand Up @@ -126,6 +129,7 @@ export const DocumentPaneProvider = memo((props: DocumentPaneProviderProps) => {
const params = useUnique(paneRouter.params) || EMPTY_PARAMS

const perspective = usePerspective()
const {data: documentVersions} = useDocumentVersions({documentId})

const {isReleaseLocked, selectedReleaseId, selectedPerspectiveName} = useMemo(() => {
// TODO: COREL - Remove this after updating sanity-assist to use <PerspectiveProvider>
Expand Down Expand Up @@ -169,9 +173,34 @@ export const DocumentPaneProvider = memo((props: DocumentPaneProviderProps) => {
const initialValue = useUnique(initialValueRaw)
const isInitialValueLoading = initialValue.loading

// if it only has versions then we need to make sure that whatever the first document that is allowed
// is a version document, but also that it has the right order
// this will make sure that then the right document appears and so does the right chip within the document header
const {sortedDocumentList, onlyHasVersions} = useDocumentVersionList({documentId})
const firstVersion =
sortedDocumentList.length > 0
? documentVersions.find(
(id) =>
getVersionFromId(id) === getReleaseIdFromReleaseDocumentId(sortedDocumentList[0]._id),
)
: undefined

const {patch} = useDocumentOperation(documentId, documentType, selectedReleaseId)
const schemaType = schema.get(documentType) as ObjectSchemaType | undefined
const editState = useEditState(documentId, documentType, 'default', selectedReleaseId)
const id = useMemo(() => {
if (selectedReleaseId) {
return selectedReleaseId
}
// check if the selected version is the only version, if it isn't and it doesn't exist in hte release
// then it needs to use the documentVersions
if (!documentVersions || !onlyHasVersions) {
return selectedReleaseId
}
return getVersionFromId(firstVersion ?? '')
}, [documentVersions, onlyHasVersions, selectedReleaseId, firstVersion])

const editState = useEditState(documentId, documentType, 'default', id)

const {validation: validationRaw} = useValidationStatus(
documentId,
documentType,
Expand All @@ -190,6 +219,10 @@ export const DocumentPaneProvider = memo((props: DocumentPaneProviderProps) => {
(liveEdit ? initialValue.value : {_id: documentId, _type: documentType})
)
}
// if no version is selected, but there is only version, it should default to the version version it finds
if (!selectedPerspectiveName && onlyHasVersions) {
return editState.version || editState.draft || editState.published || initialValue.value
}
return editState.draft || editState.published || initialValue.value
}, [
documentId,
Expand All @@ -199,6 +232,7 @@ export const DocumentPaneProvider = memo((props: DocumentPaneProviderProps) => {
editState.version,
initialValue.value,
liveEdit,
onlyHasVersions,
selectedPerspectiveName,
selectedReleaseId,
])
Expand Down Expand Up @@ -607,6 +641,10 @@ export const DocumentPaneProvider = memo((props: DocumentPaneProviderProps) => {
return true
}

if (!selectedPerspectiveName && onlyHasVersions) {
return true
}

return (
willBeUnpublished ||
!ready ||
Expand All @@ -633,6 +671,7 @@ export const DocumentPaneProvider = memo((props: DocumentPaneProviderProps) => {
selectedPerspectiveName,
selectedReleaseId,
value._id,
onlyHasVersions,
willBeUnpublished,
ready,
revisionId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import {
getVersionFromId,
isReleaseDocument,
isReleaseScheduledOrScheduling,
isVersionId,
type ReleaseDocument,
ScrollContainer,
useDocumentVersions,
usePerspective,
VirtualizerScrollInstanceProvider,
} from 'sanity'
Expand All @@ -26,6 +28,7 @@ import {
import {AddToReleaseBanner} from './banners/AddToReleaseBanner'
import {ArchivedReleaseDocumentBanner} from './banners/ArchivedReleaseDocumentBanner'
import {DraftLiveEditBanner} from './banners/DraftLiveEditBanner'
import {OpenReleaseToEditBanner} from './banners/OpenReleaseToEditBanner'
import {ScheduledReleaseBanner} from './banners/ScheduledReleaseBanner'
import {UnpublishedDocumentBanner} from './banners/UnpublishedDocumentBanner'
import {FormView} from './documentViews'
Expand Down Expand Up @@ -80,6 +83,7 @@ export const DocumentPanel = function DocumentPanel(props: DocumentPanelProps) {
const [_portalElement, setPortalElement] = useState<HTMLDivElement | null>(null)
const [documentScrollElement, setDocumentScrollElement] = useState<HTMLDivElement | null>(null)
const formContainerElement = useRef<HTMLDivElement | null>(null)
const {data: documentVersions} = useDocumentVersions({documentId})

const requiredPermission = value._createdAt ? 'update' : 'create'

Expand Down Expand Up @@ -150,18 +154,30 @@ export const DocumentPanel = function DocumentPanel(props: DocumentPanelProps) {
if (params?.historyVersion) {
return <ArchivedReleaseDocumentBanner />
}
const isCreatingDocument = displayed && !displayed._createdAt
const isScheduledRelease =
isReleaseDocument(selectedPerspective) && isReleaseScheduledOrScheduling(selectedPerspective)

if (isScheduledRelease) {
return <ScheduledReleaseBanner currentRelease={selectedPerspective as ReleaseDocument} />
}
const isDraftOrPublish: boolean =
selectedPerspective === 'drafts' || selectedPerspective === 'published'
const onlyHasVersions =
documentVersions && documentVersions.length > 0 && isVersionId(documentVersions[0])

if (isDraftOrPublish && onlyHasVersions) {
return (
<OpenReleaseToEditBanner
documentId={displayed?._id ?? documentId}
documentVersions={documentVersions}
/>
)
}

if (
displayed?._id &&
getVersionFromId(displayed._id) !== selectedReleaseId &&
ready &&
!isCreatingDocument
!isDraftOrPublish
) {
return (
<AddToReleaseBanner
Expand Down Expand Up @@ -205,6 +221,7 @@ export const DocumentPanel = function DocumentPanel(props: DocumentPanelProps) {
params?.historyVersion,
displayed,
selectedPerspective,
documentVersions,
selectedReleaseId,
ready,
activeView.type,
Expand All @@ -213,8 +230,8 @@ export const DocumentPanel = function DocumentPanel(props: DocumentPanelProps) {
isPermissionsLoading,
permissions?.granted,
requiredPermission,
value._id,
documentId,
value._id,
schemaType,
])

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import {type ReleaseId} from '@sanity/client'
import {Flex, Text} from '@sanity/ui'
import {useCallback, useMemo} from 'react'
import {
getReleaseTone,
getVersionFromId,
useActiveReleases,
useSetPerspective,
useTranslation,
VersionInlineBadge,
} from 'sanity'
import {structureLocaleNamespace} from 'sanity/structure'

import {Button} from '../../../../../ui-components'
import {Banner} from './Banner'

export function OpenReleaseToEditBanner({
documentId,
documentVersions,
}: {
documentId: string
documentVersions: string[]
}): React.JSX.Element {
const {data: activeReleases} = useActiveReleases()
const setPerspective = useSetPerspective()
const releaseId = getVersionFromId(documentId) ?? ''
const currentVersion = useMemo(
() => activeReleases.find((version) => version._id.includes(releaseId)),
[activeReleases, releaseId],
)

const documentVersionsTitleList = useMemo(
() =>
activeReleases
.filter((version) => {
return documentVersions.find((release) => {
const r = getVersionFromId(release) ?? ''
return version._id.includes(r)
})
})
.map((version) => version.metadata.title),
[activeReleases, documentVersions],
)
const tone = currentVersion && getReleaseTone(currentVersion)
const {t} = useTranslation(structureLocaleNamespace)

const handleGoToEdit = useCallback(async () => {
setPerspective(releaseId as ReleaseId)
}, [releaseId, setPerspective])

return (
<Banner
tone={tone}
paddingY={0}
content={
<Flex direction={'row'} align="center" justify="space-between" flex={1}>
<Text size={1}>
<Flex direction={'row'} gap={1}>
{t('banners.release.navigate-to-edit-description')}
{documentVersionsTitleList.map((title) => (
<VersionInlineBadge key={`${title}${documentId}`}>{title}</VersionInlineBadge>
))}
{t('banners.release.navigate-to-edit-description-end', {
count: documentVersionsTitleList.length,
})}
</Flex>
</Text>

<Button
text={t('banners.release.action.open-to-edit')}
tone={tone}
onClick={handleGoToEdit}
/>
</Flex>
}
/>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export const DocumentPerspectiveList = memo(function DocumentPerspectiveList() {
const {editState, displayed, documentType, documentId} = useDocumentPane()
const {data: documentVersions} = useDocumentVersions({documentId})
const isCreatingDocument = displayed && !displayed._createdAt
const onlyHasVersions = !editState?.published && !editState?.draft && documentVersions?.length > 0

const filteredReleases: FilterReleases = useMemo(() => {
if (!documentVersions) return {notCurrentReleases: [], currentReleases: [], inCreation: null}
Expand Down Expand Up @@ -230,14 +231,21 @@ export const DocumentPerspectiveList = memo(function DocumentPerspectiveList() {
])

const isDraftDisabled: boolean = useMemo(() => {
// Draft is disabled when the document has no published or draft but has versions
if (onlyHasVersions || (isCreatingDocument && selectedReleaseId)) {
return true
}

// Draft is disabled when we are creating a new document inside a release
// or when the document is live edit and there is no draft
if (editState?.draft) return false
if (!editState?.draft && !isLiveEdit) {
return false
}

if (isCreatingDocument && selectedReleaseId) return true
if (isLiveEdit) return true
return false
}, [editState?.draft, isCreatingDocument, isLiveEdit, selectedReleaseId])
}, [editState?.draft, isCreatingDocument, isLiveEdit, onlyHasVersions, selectedReleaseId])

return (
<>
Expand Down
Loading
Loading