From 09325a71263adf48e3a07da1f68932f982430ca6 Mon Sep 17 00:00:00 2001 From: Tim Arney Date: Mon, 12 Sep 2022 23:15:45 -0400 Subject: [PATCH] Editor: Add link to revisions (#12197) Co-authored-by: Jonny Harris Co-authored-by: Pascal Birchler --- includes/Admin/Editor.php | 3 + includes/REST_API/Stories_Controller.php | 5 - includes/templates/admin/edit-story.php | 1 + packages/design-system/src/icons/history.svg | 3 + packages/design-system/src/icons/index.js | 1 + .../src/app/story/actions/useSaveStory.js | 2 + .../src/app/story/effects/useLoadStory.js | 2 + .../src/api/constants/index.js | 1 + packages/wp-story-editor/src/api/story.js | 9 +- .../src/api/utils/transformStoryResponse.js | 4 + .../documentPane/publish/publish.js | 103 ++++++++++++++---- .../documentPane/publish/test/publish.js | 14 ++- 12 files changed, 122 insertions(+), 26 deletions(-) create mode 100644 packages/design-system/src/icons/history.svg diff --git a/includes/Admin/Editor.php b/includes/Admin/Editor.php index 4052ef7f562b..9a437d61ebac 100644 --- a/includes/Admin/Editor.php +++ b/includes/Admin/Editor.php @@ -342,6 +342,8 @@ public function get_editor_settings(): array { admin_url( 'edit.php' ) ); + $revision_url = admin_url( 'revision.php' ); + $dashboard_settings_url = add_query_arg( [ 'post_type' => $this->story_post_type->get_slug(), @@ -394,6 +396,7 @@ public function get_editor_settings(): array { 'postType' => $this->story_post_type->get_slug(), 'storyId' => $story_id, 'dashboardLink' => $dashboard_url, + 'revisionLink' => $revision_url, 'dashboardSettingsLink' => $dashboard_settings_url, 'generalSettingsLink' => $general_settings_url, 'cdnURL' => trailingslashit( WEBSTORIES_CDN_URL ), diff --git a/includes/REST_API/Stories_Controller.php b/includes/REST_API/Stories_Controller.php index 057852596f13..837a8bc2ef29 100644 --- a/includes/REST_API/Stories_Controller.php +++ b/includes/REST_API/Stories_Controller.php @@ -515,12 +515,7 @@ protected function add_response_headers( WP_REST_Response $response, WP_REST_Req * @phpstan-return Links */ protected function prepare_links( $post ): array { - // Workaround so that WP_REST_Posts_Controller::prepare_links() does not call wp_get_post_revisions(), - // avoiding a currently unneeded database query. - // TODO(#85): Remove if proper revisions support is ever needed. - remove_post_type_support( $this->post_type, 'revisions' ); $links = parent::prepare_links( $post ); - add_post_type_support( $this->post_type, 'revisions' ); $links = $this->add_post_locking_link( $links, $post ); $links = $this->add_publisher_logo_link( $links, $post ); diff --git a/includes/templates/admin/edit-story.php b/includes/templates/admin/edit-story.php index a85b5f60576f..4a8616e9edc5 100644 --- a/includes/templates/admin/edit-story.php +++ b/includes/templates/admin/edit-story.php @@ -119,6 +119,7 @@ 'permalink_template', 'style_presets', 'password', + '_links', ] ) ), diff --git a/packages/design-system/src/icons/history.svg b/packages/design-system/src/icons/history.svg new file mode 100644 index 000000000000..3d25d370355e --- /dev/null +++ b/packages/design-system/src/icons/history.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/index.js b/packages/design-system/src/icons/index.js index 495fb6fa94eb..e9c20c9abab5 100644 --- a/packages/design-system/src/icons/index.js +++ b/packages/design-system/src/icons/index.js @@ -144,3 +144,4 @@ export { default as TurningLine } from './turning_line.svg'; export { default as Union } from './union.svg'; export { default as Video } from './video.svg'; export { default as RemoveMask } from './remove_mask.svg'; +export { default as History } from './history.svg'; diff --git a/packages/story-editor/src/app/story/actions/useSaveStory.js b/packages/story-editor/src/app/story/actions/useSaveStory.js index 8e5165c9d524..f4889d51c5e6 100644 --- a/packages/story-editor/src/app/story/actions/useSaveStory.js +++ b/packages/story-editor/src/app/story/actions/useSaveStory.js @@ -96,6 +96,7 @@ function useSaveStory({ storyId, pages, story, updateStory }) { slug, link, previewLink, + revisions, editLink: newEditLink, embedPostLink, featuredMedia, @@ -109,6 +110,7 @@ function useSaveStory({ storyId, pages, story, updateStory }) { editLink: newEditLink, embedPostLink, featuredMedia, + revisions, }; updateStory({ properties }); diff --git a/packages/story-editor/src/app/story/effects/useLoadStory.js b/packages/story-editor/src/app/story/effects/useLoadStory.js index 9cd0c47e4eac..7f96809be109 100644 --- a/packages/story-editor/src/app/story/effects/useLoadStory.js +++ b/packages/story-editor/src/app/story/effects/useLoadStory.js @@ -54,6 +54,7 @@ function loadStory(storyId, post, restore, clearHistory) { publisherLogo, taxonomies, terms, + revisions, } = post; const date = @@ -112,6 +113,7 @@ function loadStory(storyId, post, restore, clearHistory) { previewLink, editLink, embedPostLink, + revisions, currentStoryStyles: { colors: storyData?.currentStoryStyles?.colors ? getUniquePresets(storyData.currentStoryStyles.colors) diff --git a/packages/wp-story-editor/src/api/constants/index.js b/packages/wp-story-editor/src/api/constants/index.js index b4afa3e1fe09..6d52c2a10de7 100644 --- a/packages/wp-story-editor/src/api/constants/index.js +++ b/packages/wp-story-editor/src/api/constants/index.js @@ -32,6 +32,7 @@ export const STORY_FIELDS = [ 'permalink_template', 'style_presets', 'password', + '_links', ].join(','); export const STORY_EMBED = 'wp:lockuser,author,wp:publisherlogo,wp:term'; diff --git a/packages/wp-story-editor/src/api/story.js b/packages/wp-story-editor/src/api/story.js index ceb6497a37b2..c36350804b17 100644 --- a/packages/wp-story-editor/src/api/story.js +++ b/packages/wp-story-editor/src/api/story.js @@ -108,6 +108,7 @@ export function saveStoryById(config, story) { 'link', 'preview_link', 'edit_link', + '_links', 'embed_post_link', 'story_poster', ].join(','), @@ -119,7 +120,12 @@ export function saveStoryById(config, story) { data: storySaveData, method: 'POST', }).then((data) => { - const { story_poster: storyPoster, ...rest } = data; + const { story_poster: storyPoster, _links: links = {}, ...rest } = data; + + const revisions = { + count: links?.['version-history']?.[0]?.count, + id: links?.['predecessor-version']?.[0]?.id, + }; const featuredMedia = storyPoster ? { @@ -138,6 +144,7 @@ export function saveStoryById(config, story) { return { ...snakeToCamelCaseObjectKeys(rest), featuredMedia, + revisions, }; }); } diff --git a/packages/wp-story-editor/src/api/utils/transformStoryResponse.js b/packages/wp-story-editor/src/api/utils/transformStoryResponse.js index c997b2278581..e2c516db4733 100644 --- a/packages/wp-story-editor/src/api/utils/transformStoryResponse.js +++ b/packages/wp-story-editor/src/api/utils/transformStoryResponse.js @@ -61,6 +61,10 @@ function transformStoryResponse(post) { url: embedded?.['wp:publisherlogo']?.[0]?.source_url || '', }, taxonomies: links?.['wp:term']?.map(({ taxonomy }) => taxonomy) || [], + revisions: { + count: links?.['version-history']?.[0]?.count, + id: links?.['predecessor-version']?.[0]?.id, + }, terms: embedded?.['wp:term'] || [], }; diff --git a/packages/wp-story-editor/src/components/documentPane/publish/publish.js b/packages/wp-story-editor/src/components/documentPane/publish/publish.js index e8ce4958cfa1..e97501974193 100644 --- a/packages/wp-story-editor/src/components/documentPane/publish/publish.js +++ b/packages/wp-story-editor/src/components/documentPane/publish/publish.js @@ -27,7 +27,12 @@ import { } from '@googleforcreators/react'; import styled from 'styled-components'; import { getExtensionsFromMimeType } from '@googleforcreators/media'; -import { __, sprintf, translateToExclusiveList } from '@googleforcreators/i18n'; +import { + __, + _n, + sprintf, + translateToExclusiveList, +} from '@googleforcreators/i18n'; import { Link, Text, @@ -49,6 +54,8 @@ import { useHighlights, useSidebar, } from '@googleforcreators/story-editor'; +import { useFeature } from 'flagged'; +import { addQueryArgs } from '@googleforcreators/url'; /** * Internal dependencies */ @@ -116,6 +123,24 @@ const LogoImg = styled.img` max-height: 96px; `; +const RevisionsWrapper = styled.div` + width: 100%; + margin-bottom: 16px; + margin-top: 20px; +`; + +const RevisionsLabel = styled.div` + display: inline-block; + margin-left: 20px; + margin-right: 20px; +`; + +const LabelIconWrapper = styled.div` + position: absolute; + display: inline-block; + margin-left: -5px; +`; + function PublishPanel({ nameOverride }) { const { state: { users }, @@ -131,8 +156,11 @@ function PublishPanel({ nameOverride }) { dashboardSettingsLink, capabilities: { hasUploadMediaAction, canManageSettings }, MediaUpload, + revisionLink, } = useConfig(); + const improvedAutosaves = useFeature('improvedAutosaves'); + const allowedImageFileTypes = useMemo( () => allowedImageMimeTypes @@ -156,25 +184,31 @@ function PublishPanel({ nameOverride }) { }) ); - const { featuredMedia, publisherLogo, updateStory, capabilities } = useStory( - ({ - state: { - story: { - featuredMedia = { id: 0, url: '', height: 0, width: 0 }, - publisherLogo = { id: 0, url: '', height: 0, width: 0 }, + const { featuredMedia, publisherLogo, updateStory, capabilities, revisions } = + useStory( + ({ + state: { + story: { + featuredMedia = { id: 0, url: '', height: 0, width: 0 }, + publisherLogo = { id: 0, url: '', height: 0, width: 0 }, + revisions, + }, + capabilities, }, - capabilities, - }, - actions: { updateStory }, - }) => { - return { - featuredMedia, - publisherLogo, - updateStory, - capabilities, - }; - } - ); + actions: { updateStory }, + }) => { + return { + featuredMedia, + publisherLogo, + updateStory, + capabilities, + revisions, + }; + } + ); + + const revisionCount = revisions?.count ? revisions?.count : 0; + const revisionId = revisions?.id ? revisions?.id : 0; const handleChangePoster = useCallback( /** @@ -414,6 +448,37 @@ function PublishPanel({ nameOverride }) { + {improvedAutosaves && revisionCount >= 1 ? ( + + + + ) : null} ); diff --git a/packages/wp-story-editor/src/components/documentPane/publish/test/publish.js b/packages/wp-story-editor/src/components/documentPane/publish/test/publish.js index 48405116f8ea..aa5e837f3168 100644 --- a/packages/wp-story-editor/src/components/documentPane/publish/test/publish.js +++ b/packages/wp-story-editor/src/components/documentPane/publish/test/publish.js @@ -26,6 +26,7 @@ import { SidebarContext, } from '@googleforcreators/story-editor'; import { renderWithTheme } from '@googleforcreators/test-utils'; +import { useFeature } from 'flagged'; /** * Internal dependencies @@ -37,6 +38,8 @@ jest.mock('./../../../../api/publisherLogos', () => ({ addPublisherLogo: jest.fn().mockResolvedValue([]), })); +jest.mock('flagged'); + function MediaUpload({ render }) { const open = jest.fn(); return render(open); @@ -59,6 +62,7 @@ function arrange( date: '2020-01-01T20:20:20', modified: '2020-01-01T20:20:19', featuredMedia: { id: 0, url: '', height: 0, width: 0 }, + revisions: { count: 8, id: 189 }, publisherLogo: { id: 0, url: '', height: 0, width: 0 }, status: 'draft', }, @@ -71,6 +75,7 @@ function arrange( allowedMimeTypes: { image: ['image/png', 'image/jpeg', 'image/jpg', 'image/gif'], }, + revisionLink: 'http://example.com', apiCallbacks: { getAuthors: jest.fn().mockResolvedValue({}), }, @@ -114,6 +119,7 @@ describe('PublishPanel', () => { JSON.stringify({ isCollapsed: false }) ); MockDate.set('2020-07-15T12:00:00+00:00'); + useFeature.mockImplementation(() => true); }); afterAll(() => { @@ -125,9 +131,15 @@ describe('PublishPanel', () => { arrange(); const publishPanel = screen.getByText('Publishing'); const publisherLogo = screen.getByText('Publisher Logo'); - + const revisionsText = screen.getByText('8 Revisions'); + const revisionsLink = screen.getByRole('link', { name: 'Browse' }); await waitFor(() => expect(publishPanel).toBeDefined()); await waitFor(() => expect(publisherLogo).toBeDefined()); + await waitFor(() => expect(revisionsText).toBeDefined()); + expect(revisionsLink).toHaveAttribute( + 'href', + 'http://example.com/?revision=189' + ); }); it('should display Author field if authors available', async () => {