From fe2a7776acb0525d9484b13e884af3dbd34e6efe Mon Sep 17 00:00:00 2001 From: Rita Date: Tue, 22 Oct 2024 15:26:32 +0200 Subject: [PATCH 01/13] refactor(sanity): add useVersionOperations, replace createVersion on dialog and chip --- packages/sanity/src/core/index.ts | 1 + .../components/panes/ReleaseActions.tsx | 1 + .../sanity/src/core/releases/hooks/index.ts | 1 + .../releases/hooks/useVersionOperations.tsx | 63 +++++++++++++++++++ .../header/perspective/VersionChip.tsx | 57 ++--------------- .../dialog/CreateReleaseDialog.tsx | 53 ++-------------- 6 files changed, 76 insertions(+), 100 deletions(-) create mode 100644 packages/sanity/src/core/releases/hooks/useVersionOperations.tsx diff --git a/packages/sanity/src/core/index.ts b/packages/sanity/src/core/index.ts index f33c49bf570..9a6f1591b42 100644 --- a/packages/sanity/src/core/index.ts +++ b/packages/sanity/src/core/index.ts @@ -37,6 +37,7 @@ export { ReleasesMenu, useDocumentVersions, usePerspective, + useVersionOperations, versionDocumentExists, } from './releases' export * from './scheduledPublishing' diff --git a/packages/sanity/src/core/releases/components/panes/ReleaseActions.tsx b/packages/sanity/src/core/releases/components/panes/ReleaseActions.tsx index f628d0a1392..f7cbfb3a523 100644 --- a/packages/sanity/src/core/releases/components/panes/ReleaseActions.tsx +++ b/packages/sanity/src/core/releases/components/panes/ReleaseActions.tsx @@ -52,6 +52,7 @@ export function ReleaseActions(props: ReleaseActionsProps): ReactNode { const telemetry = useTelemetry() const client = useClient(DEFAULT_STUDIO_CLIENT_OPTIONS) + /** @todo remove this when task to tackle footer actions */ const handleAddVersion = useCallback(async () => { if (!documentId) { toast.push({ diff --git a/packages/sanity/src/core/releases/hooks/index.ts b/packages/sanity/src/core/releases/hooks/index.ts index e81a942e603..0ed88aee8c3 100644 --- a/packages/sanity/src/core/releases/hooks/index.ts +++ b/packages/sanity/src/core/releases/hooks/index.ts @@ -1,2 +1,3 @@ export * from './useDocumentVersions' export * from './usePerspective' +export * from './useVersionOperations' diff --git a/packages/sanity/src/core/releases/hooks/useVersionOperations.tsx b/packages/sanity/src/core/releases/hooks/useVersionOperations.tsx new file mode 100644 index 00000000000..83209c99bfe --- /dev/null +++ b/packages/sanity/src/core/releases/hooks/useVersionOperations.tsx @@ -0,0 +1,63 @@ +import {useTelemetry} from '@sanity/telemetry/react' +import {filter, firstValueFrom} from 'rxjs' +import { + AddedVersion, + getCreateVersionOrigin, + getPublishedId, + getVersionFromId, + getVersionId, + useDocumentOperation, + useDocumentStore, + usePerspective, +} from 'sanity' + +export function useVersionOperations( + documentId: string, + documentType: string, +): { + createVersion: (releaseId: string) => void + discardVersion: () => void +} { + const documentStore = useDocumentStore() + const telemetry = useTelemetry() + + const publishedId = getPublishedId(documentId) + const {createVersion} = useDocumentOperation( + publishedId, + documentType, + getVersionFromId(documentId), + ) + const {setPerspective} = usePerspective() + + const handleCreateVersion = async (releaseId: string) => { + // set up the listener before executing to make sure it's successful + const createVersionSuccess = firstValueFrom( + documentStore.pair + .operationEvents(getPublishedId(documentId), documentType) + .pipe(filter((e) => e.op === 'createVersion' && e.type === 'success')), + ) + + const docId = getVersionId(publishedId, releaseId) + + const origin = getCreateVersionOrigin(documentId) + createVersion.execute(docId, origin) + + // only change if the version was created successfully + await createVersionSuccess + setPerspective(releaseId) + + telemetry.log(AddedVersion, { + schemaType: documentType, + documentOrigin: origin, + }) + } + + const handleDiscardVersion = () => { + // eslint-disable-next-line no-console + console.log('working on this - rita :)') + } + return { + createVersion: handleCreateVersion, + discardVersion: handleDiscardVersion, + } +} diff --git a/packages/sanity/src/structure/panes/document/documentPanel/header/perspective/VersionChip.tsx b/packages/sanity/src/structure/panes/document/documentPanel/header/perspective/VersionChip.tsx index 3f991a4443f..38ac154c636 100644 --- a/packages/sanity/src/structure/panes/document/documentPanel/header/perspective/VersionChip.tsx +++ b/packages/sanity/src/structure/panes/document/documentPanel/header/perspective/VersionChip.tsx @@ -1,17 +1,6 @@ -import {useTelemetry} from '@sanity/telemetry/react' import {type BadgeTone, useClickOutsideEvent, useGlobalKeyDown} from '@sanity/ui' import {memo, type MouseEvent, type ReactNode, useCallback, useMemo, useRef, useState} from 'react' -import {filter, firstValueFrom} from 'rxjs' -import { - AddedVersion, - getCreateVersionOrigin, - getPublishedId, - getVersionId, - type ReleaseDocument, - useDocumentOperation, - useDocumentStore, - usePerspective, -} from 'sanity' +import {getVersionId, type ReleaseDocument, useVersionOperations} from 'sanity' import {styled} from 'styled-components' import {ReleaseAvatar} from '../../../../../../core/releases/components/ReleaseAvatar' @@ -75,16 +64,10 @@ export const VersionChip = memo(function VersionChip(props: { const popoverRef = useRef(null) const [isDiscardDialogOpen, setIsDiscardDialogOpen] = useState(false) const [isCreateReleaseDialogOpen, setIsCreateReleaseDialogOpen] = useState(false) - const {setPerspective} = usePerspective() - const publishedId = getPublishedId(documentId) + const docId = isVersion ? getVersionId(documentId, fromRelease) : documentId // operations recognises publish and draft as empty - const operationVersion = isVersion ? fromRelease : '' // operations recognises publish and draft as empty - - const {createVersion} = useDocumentOperation(publishedId, documentType, operationVersion) - - const telemetry = useTelemetry() - const documentStore = useDocumentStore() + const {createVersion} = useVersionOperations(docId, documentType) const close = useCallback(() => setContextMenuPoint(undefined), []) @@ -124,40 +107,10 @@ export const VersionChip = memo(function VersionChip(props: { const handleAddVersion = useCallback( async (targetRelease: string) => { - // set up the listener before executing - const createVersionSuccess = firstValueFrom( - documentStore.pair - .operationEvents(getPublishedId(documentId), documentType) - .pipe(filter((e) => e.op === 'createVersion' && e.type === 'success')), - ) - - const docId = getVersionId(publishedId, targetRelease) - const origin = isVersion ? 'version' : getCreateVersionOrigin(fromRelease) - - createVersion.execute(docId, origin) - - // only change if the version was created successfully - await createVersionSuccess - setPerspective(targetRelease) - - telemetry.log(AddedVersion, { - schemaType: documentType, - documentOrigin: origin, - }) + createVersion(targetRelease) close() }, - [ - createVersion, - documentId, - documentStore.pair, - documentType, - fromRelease, - isVersion, - publishedId, - setPerspective, - telemetry, - close, - ], + [createVersion, close], ) const referenceElement = useMemo(() => { diff --git a/packages/sanity/src/structure/panes/document/documentPanel/header/perspective/dialog/CreateReleaseDialog.tsx b/packages/sanity/src/structure/panes/document/documentPanel/header/perspective/dialog/CreateReleaseDialog.tsx index 5e99f8e504b..6e6a7b06571 100644 --- a/packages/sanity/src/structure/panes/document/documentPanel/header/perspective/dialog/CreateReleaseDialog.tsx +++ b/packages/sanity/src/structure/panes/document/documentPanel/header/perspective/dialog/CreateReleaseDialog.tsx @@ -1,26 +1,18 @@ import {useTelemetry} from '@sanity/telemetry/react' import {type BadgeTone, Box, Flex, Text, useToast} from '@sanity/ui' import {useCallback, useState} from 'react' -import {filter, firstValueFrom} from 'rxjs' import { - AddedVersion, CreatedRelease, createReleaseId, DEFAULT_RELEASE_TYPE, type FormReleaseDocument, - getCreateVersionOrigin, - getPublishedId, - getVersionFromId, - getVersionId, LoadingBlock, Preview, ReleaseAvatar, ReleaseForm, - useDocumentOperation, - useDocumentStore, - usePerspective, useReleaseOperations, useSchema, + useVersionOperations, } from 'sanity' import {Dialog} from '../../../../../../../ui-components' @@ -35,11 +27,9 @@ export function CreateReleaseDialog(props: { const {onClose, documentId, documentType, tone, title} = props const toast = useToast() - const {setPerspective} = usePerspective() const schema = useSchema() const schemaType = schema.get(documentType) - const documentStore = useDocumentStore() const [newReleaseId] = useState(createReleaseId()) const [value, setValue] = useState((): FormReleaseDocument => { @@ -58,48 +48,15 @@ export function CreateReleaseDialog(props: { const telemetry = useTelemetry() const {createRelease} = useReleaseOperations() - const publishedId = getPublishedId(documentId) - - const {createVersion} = useDocumentOperation( - publishedId, - documentType, - getVersionFromId(documentId), - ) - const handleOnChange = useCallback((changedValue: FormReleaseDocument) => { setValue(changedValue) }, []) - const handleAddVersion = useCallback(async () => { - // set up the listener before executing - const createVersionSuccess = firstValueFrom( - documentStore.pair - .operationEvents(getPublishedId(documentId), documentType) - .pipe(filter((e) => e.op === 'createVersion' && e.type === 'success')), - ) - - const docId = getVersionId(publishedId, newReleaseId) - - createVersion.execute(docId, getCreateVersionOrigin(documentId)) + const {createVersion} = useVersionOperations(documentId, documentType) - // only change if the version was created successfully - await createVersionSuccess - setPerspective(newReleaseId) - - telemetry.log(AddedVersion, { - schemaType: documentType, - documentOrigin: origin, - }) - }, [ - createVersion, - documentId, - documentStore.pair, - documentType, - newReleaseId, - publishedId, - setPerspective, - telemetry, - ]) + const handleAddVersion = useCallback(async () => { + createVersion(newReleaseId) + }, [createVersion, newReleaseId]) const handleCreateRelease = useCallback(async () => { try { From efe8ff5de6630464f2a5e3623b43c5a5de6a9b95 Mon Sep 17 00:00:00 2001 From: Jordan Lawrence Date: Tue, 22 Oct 2024 17:36:22 +0100 Subject: [PATCH 02/13] feat: releases overview facelift and calendar added --- .../sanity/src/core/i18n/bundles/studio.ts | 4 +- .../contexts/ReleasesMetadataProvider.tsx | 58 ++--- .../src/core/releases/i18n/resources.ts | 8 +- .../releases/navbar/GlobalPerspectiveMenu.tsx | 2 +- .../releases/plugin/ReleasesStudioLayout.tsx | 4 +- .../releases/tool/components/Table/Table.tsx | 6 +- .../tool/components/Table/TableHeader.tsx | 2 +- .../tool/detail/ReleaseDashboardDetails.tsx | 1 + .../releases/tool/detail/ReleaseSummary.tsx | 5 +- .../tool/overview/ReleaseDocumentsCounter.tsx | 63 ++++++ .../tool/overview/ReleasesOverview.tsx | 198 ++++++++++++------ .../overview/ReleasesOverviewColumnDefs.tsx | 154 +++++++------- .../createReleaseMetadataAggregator.ts | 77 ++++--- .../core/store/release/useReleasesMetadata.ts | 21 +- .../inputs/DateInputs/DatePicker.tsx | 6 +- .../inputs/DateInputs/calendar/Calendar.tsx | 102 ++++++--- 16 files changed, 476 insertions(+), 235 deletions(-) create mode 100644 packages/sanity/src/core/releases/tool/overview/ReleaseDocumentsCounter.tsx diff --git a/packages/sanity/src/core/i18n/bundles/studio.ts b/packages/sanity/src/core/i18n/bundles/studio.ts index 85bfa4b356c..fd83a4ca6d1 100644 --- a/packages/sanity/src/core/i18n/bundles/studio.ts +++ b/packages/sanity/src/core/i18n/bundles/studio.ts @@ -1144,8 +1144,8 @@ export const studioLocaleStrings = defineLocalesResources('studio', { 'release.action.already-in-release': 'Already in release {{title}}', /** Action message for when you click to view all versions you can copy the current document to */ 'release.action.copy-to': 'Copy version to', - /** Action message for creating releases */ - 'release.action.create': 'Create release', + /** Action message for creating new releases */ + 'release.action.create-new': 'New release', /** Action message for when document is already in release */ 'release.action.discard-version': 'Discard version', /** Description for toast when version discarding failed */ diff --git a/packages/sanity/src/core/releases/contexts/ReleasesMetadataProvider.tsx b/packages/sanity/src/core/releases/contexts/ReleasesMetadataProvider.tsx index 97f9311993d..5121f52665f 100644 --- a/packages/sanity/src/core/releases/contexts/ReleasesMetadataProvider.tsx +++ b/packages/sanity/src/core/releases/contexts/ReleasesMetadataProvider.tsx @@ -11,8 +11,8 @@ import {type ReleasesMetadata} from '../../store/release/useReleasesMetadata' */ export interface ReleasesMetadataContextValue { state: MetadataWrapper - addBundleIdsToListener: (slugs: string[]) => void - removeBundleIdsFromListener: (slugs: string[]) => void + addReleaseIdsToListener: (slugs: string[]) => void + removeReleaseIdsFromListener: (slugs: string[]) => void } const DEFAULT_METADATA_STATE: MetadataWrapper = { @@ -21,16 +21,16 @@ const DEFAULT_METADATA_STATE: MetadataWrapper = { loading: false, } -const BundlesMetadataProviderInner = ({children}: {children: React.ReactNode}) => { - const [listenerBundleIds, setListenerBundleIds] = useState([]) +const ReleasesMetadataProviderInner = ({children}: {children: React.ReactNode}) => { + const [listenerReleaseIds, setListenerReleaseIds] = useState([]) const {getMetadataStateForSlugs$} = useReleasesStore() - const [bundlesMetadata, setBundlesMetadata] = useState | null>( + const [releasesMetadata, setReleasesMetadata] = useState | null>( null, ) const memoObservable = useMemo( - () => getMetadataStateForSlugs$(listenerBundleIds), - [getMetadataStateForSlugs$, listenerBundleIds], + () => getMetadataStateForSlugs$(listenerReleaseIds), + [getMetadataStateForSlugs$, listenerReleaseIds], ) const observedResult = useObservable(memoObservable) || DEFAULT_METADATA_STATE @@ -38,32 +38,32 @@ const BundlesMetadataProviderInner = ({children}: {children: React.ReactNode}) = // patch metadata in local state useEffect( () => - setBundlesMetadata((prevBundleMetadata) => { - if (!observedResult.data) return prevBundleMetadata + setReleasesMetadata((prevReleaseMetadata) => { + if (!observedResult.data) return prevReleaseMetadata - return {...(prevBundleMetadata || {}), ...observedResult.data} + return {...(prevReleaseMetadata || {}), ...observedResult.data} }), [observedResult.data], ) - const addBundleIdsToListener = useCallback((addBundleIds: (string | undefined)[]) => { - setListenerBundleIds((prevSlugs) => [ + const addReleaseIdsToListener = useCallback((addReleaseIds: (string | undefined)[]) => { + setListenerReleaseIds((prevSlugs) => [ ...prevSlugs, - ...addBundleIds.filter((bundleId): bundleId is string => typeof bundleId === 'string'), + ...addReleaseIds.filter((releaseId): releaseId is string => typeof releaseId === 'string'), ]) }, []) - const removeBundleIdsFromListener = useCallback((bundleIds: string[]) => { - setListenerBundleIds((prevSlugs) => { + const removeReleaseIdsFromListener = useCallback((releaseIds: string[]) => { + setListenerReleaseIds((prevSlugs) => { const {nextSlugs} = prevSlugs.reduce<{removedSlugs: string[]; nextSlugs: string[]}>( (acc, slug) => { const {removedSlugs, nextSlugs: accNextSlugs} = acc /** - * In cases where multiple consumers are listening to the same bundle id - * the bundle id will appear multiple times in listenerBundleIds array + * In cases where multiple consumers are listening to the same release id + * the release id will appear multiple times in listenerReleaseIds array * removing should only remove 1 instance of the slug and retain all others */ - if (bundleIds.includes(slug) && !removedSlugs.includes(slug)) { + if (releaseIds.includes(slug) && !removedSlugs.includes(slug)) { return {removedSlugs: [...removedSlugs, slug], nextSlugs: accNextSlugs} } return {removedSlugs, nextSlugs: [...accNextSlugs, slug]} @@ -75,16 +75,16 @@ const BundlesMetadataProviderInner = ({children}: {children: React.ReactNode}) = }, []) const context = useMemo<{ - addBundleIdsToListener: (slugs: string[]) => void - removeBundleIdsFromListener: (slugs: string[]) => void + addReleaseIdsToListener: (slugs: string[]) => void + removeReleaseIdsFromListener: (slugs: string[]) => void state: MetadataWrapper }>( () => ({ - addBundleIdsToListener: addBundleIdsToListener, - removeBundleIdsFromListener: removeBundleIdsFromListener, - state: {...observedResult, data: bundlesMetadata}, + addReleaseIdsToListener: addReleaseIdsToListener, + removeReleaseIdsFromListener: removeReleaseIdsFromListener, + state: {...observedResult, data: releasesMetadata}, }), - [addBundleIdsToListener, bundlesMetadata, observedResult, removeBundleIdsFromListener], + [addReleaseIdsToListener, releasesMetadata, observedResult, removeReleaseIdsFromListener], ) return ( @@ -92,22 +92,22 @@ const BundlesMetadataProviderInner = ({children}: {children: React.ReactNode}) = ) } -export const BundlesMetadataProvider = ({children}: {children: React.ReactNode}) => { +export const ReleasesMetadataProvider = ({children}: {children: React.ReactNode}) => { const context = useContext(ReleasesMetadataContext) // Avoid mounting the provider if it's already provided by a parent if (context) return children - return {children} + return {children} } -export const useBundlesMetadataProvider = (): ReleasesMetadataContextValue => { +export const useReleasesMetadataProvider = (): ReleasesMetadataContextValue => { const contextValue = useContext(ReleasesMetadataContext) return ( contextValue || { state: DEFAULT_METADATA_STATE, - addBundleIdsToListener: () => null, - removeBundleIdsFromListener: () => null, + addReleaseIdsToListener: () => null, + removeReleaseIdsFromListener: () => null, } ) } diff --git a/packages/sanity/src/core/releases/i18n/resources.ts b/packages/sanity/src/core/releases/i18n/resources.ts index f0680247452..bc78faae7b5 100644 --- a/packages/sanity/src/core/releases/i18n/resources.ts +++ b/packages/sanity/src/core/releases/i18n/resources.ts @@ -141,18 +141,18 @@ const releasesLocaleStrings = { /** Header for the document table in the release tool - contributors */ 'table-header.contributors': 'Contributors', - /** Header for the document table in the release tool - created */ - 'table-header.created': 'Created', /** Header for the document table in the release tool - type */ 'table-header.type': 'Type', + /** Header for the document table in the release tool - release title */ + 'table-header.title': 'Release', /** Header for the document table in the release tool - action */ 'table-header.action': 'Action', /** Header for the document table in the release tool - title */ 'table-header.documents': 'Documents', /** Header for the document table in the release tool - edited */ 'table-header.edited': 'Edited', - /** Header for the document table in the release tool - published */ - 'table-header.published': 'Published', + /** Header for the document table in the release tool - time */ + 'table-header.time': 'Time', /** Text for toast when release failed to publish */ 'toast.error': "Failed to publish the '{{title}}'", /** Text for toast when release has been published */ diff --git a/packages/sanity/src/core/releases/navbar/GlobalPerspectiveMenu.tsx b/packages/sanity/src/core/releases/navbar/GlobalPerspectiveMenu.tsx index 6a33ccb44b2..c60d32c47a0 100644 --- a/packages/sanity/src/core/releases/navbar/GlobalPerspectiveMenu.tsx +++ b/packages/sanity/src/core/releases/navbar/GlobalPerspectiveMenu.tsx @@ -128,7 +128,7 @@ export function GlobalPerspectiveMenu(): JSX.Element { diff --git a/packages/sanity/src/core/releases/plugin/ReleasesStudioLayout.tsx b/packages/sanity/src/core/releases/plugin/ReleasesStudioLayout.tsx index 41abcbd8b91..5dab17dfd36 100644 --- a/packages/sanity/src/core/releases/plugin/ReleasesStudioLayout.tsx +++ b/packages/sanity/src/core/releases/plugin/ReleasesStudioLayout.tsx @@ -1,6 +1,6 @@ import {type LayoutProps} from '../../config' import {AddonDatasetProvider} from '../../studio' -import {BundlesMetadataProvider} from '../contexts/ReleasesMetadataProvider' +import {ReleasesMetadataProvider} from '../contexts/ReleasesMetadataProvider' export function ReleasesStudioLayout(props: LayoutProps) { // TODO: Replace for useReleasesEnabled @@ -12,7 +12,7 @@ export function ReleasesStudioLayout(props: LayoutProps) { return ( - {props.renderDefault(props)} + {props.renderDefault(props)} ) } diff --git a/packages/sanity/src/core/releases/tool/components/Table/Table.tsx b/packages/sanity/src/core/releases/tool/components/Table/Table.tsx index e1e3d54f994..a203517dad5 100644 --- a/packages/sanity/src/core/releases/tool/components/Table/Table.tsx +++ b/packages/sanity/src/core/releases/tool/components/Table/Table.tsx @@ -51,6 +51,7 @@ export interface TableProps { }) => ReactNode rowProps?: (datum: TableData) => Partial scrollContainerRef: MutableRefObject + hideTableInlinePadding?: boolean } const CustomCard = styled(Card)` @@ -103,6 +104,7 @@ const TableInner = ({ loading = false, rowProps = () => ({}), scrollContainerRef, + hideTableInlinePadding = false, }: TableProps) => { const {searchTerm, sort} = useTableContext() const virtualizerContainerRef = useRef(null) @@ -242,6 +244,8 @@ const TableInner = ({ return } + const maxInlineSize = (!hideTableInlinePadding && theme.sanity.v2?.container[3]) || 0 + return (
({ 'paddingBottom': '110px', 'width': '100%', 'position': 'relative', - '--maxInlineSize': rem(theme.sanity.v2?.container[3] ?? 0), + '--maxInlineSize': rem(maxInlineSize), '--paddingInline': rem(theme.sanity.v2?.space[3] ?? 0), } as CSSProperties } diff --git a/packages/sanity/src/core/releases/tool/components/Table/TableHeader.tsx b/packages/sanity/src/core/releases/tool/components/Table/TableHeader.tsx index ff1febb5863..3a50a909965 100644 --- a/packages/sanity/src/core/releases/tool/components/Table/TableHeader.tsx +++ b/packages/sanity/src/core/releases/tool/components/Table/TableHeader.tsx @@ -57,7 +57,7 @@ const TableHeaderSearch = ({ */ export const TableHeader = ({headers, searchDisabled}: TableHeaderProps) => { return ( - + + {/* make the pin a component that can be reused */}