From 78121c6f8869f99577e2ab5b2cf1d9e9fd87dd2a Mon Sep 17 00:00:00 2001 From: Adrian Smijulj Date: Tue, 10 Sep 2024 16:03:22 +0200 Subject: [PATCH] fix: improve UI/UX around deletion of entry revisions (#4260) --- .../RevisionDeletedSnackbarMessage.tsx | 28 ++++++++++ .../ContentEntry/useRevision.tsx | 52 ++++++++++++++++--- 2 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/RevisionDeletedSnackbarMessage.tsx diff --git a/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/RevisionDeletedSnackbarMessage.tsx b/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/RevisionDeletedSnackbarMessage.tsx new file mode 100644 index 00000000000..53609cb3991 --- /dev/null +++ b/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/RevisionDeletedSnackbarMessage.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import { CmsContentEntry, CmsContentEntryRevision } from "@webiny/app-headless-cms-common/types"; + +interface RevisionDeletedSnackbarMessageProps { + deletedRevision: CmsContentEntry; + newLatestRevision?: CmsContentEntryRevision; +} + +export const RevisionDeletedSnackbarMessage = ({ + deletedRevision, + newLatestRevision +}: RevisionDeletedSnackbarMessageProps) => { + if (newLatestRevision) { + return ( + + Successfully deleted revision #{deletedRevision.meta.version}. + Redirecting to revision #{newLatestRevision.meta.version}... + + ); + } + + return ( + + Successfully deleted last revision #{deletedRevision.meta.version}. + Redirecting to list of entries... + + ); +}; diff --git a/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/useRevision.tsx b/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/useRevision.tsx index ef6e42b923c..6ec1da8b353 100644 --- a/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/useRevision.tsx +++ b/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntry/useRevision.tsx @@ -4,6 +4,7 @@ import { useHandlers } from "@webiny/app/hooks/useHandlers"; import { useSnackbar } from "@webiny/app-admin/hooks/useSnackbar"; import { CmsContentEntry } from "~/types"; import { + CmsEntriesListRevisionsQueryResponse, CmsEntryCreateFromMutationResponse, CmsEntryCreateFromMutationVariables, createCreateFromMutation, @@ -13,6 +14,7 @@ import { useApolloClient, useCms } from "~/admin/hooks"; import { useContentEntry } from "~/admin/views/contentEntries/hooks/useContentEntry"; import { getFetchPolicy } from "~/utils/getFetchPolicy"; import { useRecords } from "@webiny/app-aco"; +import { RevisionDeletedSnackbarMessage } from "./RevisionDeletedSnackbarMessage"; interface CreateRevisionHandler { (id?: string): Promise; @@ -57,7 +59,7 @@ export const useRevision = ({ revision }: UseRevisionProps) => { const client = useApolloClient(); const { modelId } = contentModel; - const { updateRecordInCache } = useRecords(); + const { updateRecordInCache, removeRecordFromCache } = useRecords(); const { CREATE_REVISION } = useMemo(() => { return { @@ -129,7 +131,7 @@ export const useRevision = ({ revision }: UseRevisionProps) => { async (id): Promise => { setLoading(true); - const { error, entry: targetRevision } = await deleteEntry({ + const { error } = await deleteEntry({ model: contentModel, entry, id: id || entry.id @@ -142,11 +144,49 @@ export const useRevision = ({ revision }: UseRevisionProps) => { return; } - // Redirect to the first revision in the list of all entry revisions. - history.push( - `/cms/content-entries/${modelId}?id=` + - encodeURIComponent(targetRevision!.id) + // We need the list of all revisions of the entry to find the new latest revision. + const cachedEntryRevisions = + client.cache.readQuery({ + query: createRevisionsQuery(contentModel), + variables: { + id: entry.entryId + } + }); + + // The `revisions.data` array contains all revisions of the entry, ordered from + // the latest to the oldest. The first element in the array is the latest revision. + // What we're doing here is finding the latest revision that is not the current one. + const newLatestRevision = cachedEntryRevisions?.revisions.data.find( + revision => revision.meta.version !== entry.meta.version ); + + // 1. Update ACO cache. + if (newLatestRevision) { + // Make sure the new latest revision is in the cache. This is important because + // this way we get to see the change in the ACO entry list. + updateRecordInCache(newLatestRevision); + } else { + // Like in the above case, we need to remove the entry from the cache. And again, + // this is important because this way we get to see the change in the ACO entry list. + removeRecordFromCache(entry.id); + } + + // 2. Show a snackbar message. + showSnackbar( + + ); + + // 3. Redirect to the new latest revision or the list of all revisions. + let redirectTarget = `/cms/content-entries/${modelId}`; + if (newLatestRevision) { + // Redirect to the first revision in the list of all entry revisions. + redirectTarget += `?id=${encodeURIComponent(newLatestRevision.id)}`; + } + + history.push(redirectTarget); }, publishRevision: ({ entry }): PublishRevisionHandler =>