From 822acd458581981356d31316b300ea2304f7c1d9 Mon Sep 17 00:00:00 2001 From: Vladislav Shkodin Date: Sun, 21 Mar 2021 18:13:03 +0200 Subject: [PATCH] chore: refactoring deploys (#5135) --- .../netlify-cms-core/src/actions/deploys.js | 86 -------------- .../netlify-cms-core/src/actions/deploys.ts | 105 ++++++++++++++++++ .../src/components/Editor/Editor.js | 2 +- .../src/components/Editor/EditorInterface.js | 2 +- .../src/components/Editor/EditorToolbar.js | 9 +- .../netlify-cms-core/src/reducers/deploys.js | 46 -------- .../netlify-cms-core/src/reducers/deploys.ts | 50 +++++++++ packages/netlify-cms-core/src/types/redux.ts | 3 +- 8 files changed, 161 insertions(+), 142 deletions(-) delete mode 100644 packages/netlify-cms-core/src/actions/deploys.js create mode 100644 packages/netlify-cms-core/src/actions/deploys.ts delete mode 100644 packages/netlify-cms-core/src/reducers/deploys.js create mode 100644 packages/netlify-cms-core/src/reducers/deploys.ts diff --git a/packages/netlify-cms-core/src/actions/deploys.js b/packages/netlify-cms-core/src/actions/deploys.js deleted file mode 100644 index a956d196..00000000 --- a/packages/netlify-cms-core/src/actions/deploys.js +++ /dev/null @@ -1,86 +0,0 @@ -import { actions as notifActions } from 'redux-notifications'; -import { currentBackend } from 'coreSrc/backend'; -import { selectDeployPreview } from 'Reducers'; - -const { notifSend } = notifActions; - -export const DEPLOY_PREVIEW_REQUEST = 'DEPLOY_PREVIEW_REQUEST'; -export const DEPLOY_PREVIEW_SUCCESS = 'DEPLOY_PREVIEW_SUCCESS'; -export const DEPLOY_PREVIEW_FAILURE = 'DEPLOY_PREVIEW_FAILURE'; - -export function deployPreviewLoading(collection, slug) { - return { - type: DEPLOY_PREVIEW_REQUEST, - payload: { - collection: collection.get('name'), - slug, - }, - }; -} - -export function deployPreviewLoaded(collection, slug, { url, status }) { - return { - type: DEPLOY_PREVIEW_SUCCESS, - payload: { - collection: collection.get('name'), - slug, - url, - status, - }, - }; -} - -export function deployPreviewError(collection, slug) { - return { - type: DEPLOY_PREVIEW_FAILURE, - payload: { - collection: collection.get('name'), - slug, - }, - }; -} - -/** - * Requests a deploy preview object from the registered backend. - */ -export function loadDeployPreview(collection, slug, entry, published, opts) { - return async (dispatch, getState) => { - const state = getState(); - const backend = currentBackend(state.config); - - // Exit if currently fetching - const deployState = selectDeployPreview(state, collection, slug); - if (deployState && deployState.get('isFetching')) { - return; - } - - dispatch(deployPreviewLoading(collection, slug)); - - try { - /** - * `getDeploy` is for published entries, while `getDeployPreview` is for - * unpublished entries. - */ - const deploy = published - ? backend.getDeploy(collection, slug, entry) - : await backend.getDeployPreview(collection, slug, entry, opts); - if (deploy) { - return dispatch(deployPreviewLoaded(collection, slug, deploy)); - } - return dispatch(deployPreviewError(collection, slug)); - } catch (error) { - console.error(error); - dispatch( - notifSend({ - message: { - details: error.message, - key: 'ui.toast.onFailToLoadDeployPreview', - }, - kind: 'danger', - dismissAfter: 8000, - }), - ); - dispatch(deployPreviewError(collection, slug)); - } - }; -} diff --git a/packages/netlify-cms-core/src/actions/deploys.ts b/packages/netlify-cms-core/src/actions/deploys.ts new file mode 100644 index 00000000..b9661c5f --- /dev/null +++ b/packages/netlify-cms-core/src/actions/deploys.ts @@ -0,0 +1,105 @@ +import { actions as notifActions } from 'redux-notifications'; +import { ThunkDispatch } from 'redux-thunk'; +import { AnyAction } from 'redux'; +import { currentBackend } from '../backend'; +import { selectDeployPreview } from '../reducers'; +import { Collection, Entry, State } from '../types/redux'; + +const { notifSend } = notifActions; + +export const DEPLOY_PREVIEW_REQUEST = 'DEPLOY_PREVIEW_REQUEST'; +export const DEPLOY_PREVIEW_SUCCESS = 'DEPLOY_PREVIEW_SUCCESS'; +export const DEPLOY_PREVIEW_FAILURE = 'DEPLOY_PREVIEW_FAILURE'; + +function deployPreviewLoading(collection: string, slug: string) { + return { + type: DEPLOY_PREVIEW_REQUEST, + payload: { + collection, + slug, + }, + } as const; +} + +function deployPreviewLoaded( + collection: string, + slug: string, + deploy: { url: string | undefined; status: string }, +) { + const { url, status } = deploy; + return { + type: DEPLOY_PREVIEW_SUCCESS, + payload: { + collection, + slug, + url, + status, + }, + } as const; +} + +function deployPreviewError(collection: string, slug: string) { + return { + type: DEPLOY_PREVIEW_FAILURE, + payload: { + collection, + slug, + }, + } as const; +} + +/** + * Requests a deploy preview object from the registered backend. + */ +export function loadDeployPreview( + collection: Collection, + slug: string, + entry: Entry, + published: boolean, + opts?: { maxAttempts?: number; interval?: number }, +) { + return async (dispatch: ThunkDispatch, getState: () => State) => { + const state = getState(); + const backend = currentBackend(state.config); + const collectionName = collection.get('name'); + + // Exit if currently fetching + const deployState = selectDeployPreview(state, collectionName, slug); + if (deployState && deployState.isFetching) { + return; + } + + dispatch(deployPreviewLoading(collectionName, slug)); + + try { + /** + * `getDeploy` is for published entries, while `getDeployPreview` is for + * unpublished entries. + */ + const deploy = published + ? backend.getDeploy(collection, slug, entry) + : await backend.getDeployPreview(collection, slug, entry, opts); + if (deploy) { + return dispatch(deployPreviewLoaded(collectionName, slug, deploy)); + } + return dispatch(deployPreviewError(collectionName, slug)); + } catch (error) { + console.error(error); + dispatch( + notifSend({ + message: { + details: error.message, + key: 'ui.toast.onFailToLoadDeployPreview', + }, + kind: 'danger', + dismissAfter: 8000, + }), + ); + dispatch(deployPreviewError(collectionName, slug)); + } + }; +} + +export type DeploysAction = ReturnType< + typeof deployPreviewLoading | typeof deployPreviewLoaded | typeof deployPreviewError +>; diff --git a/packages/netlify-cms-core/src/components/Editor/Editor.js b/packages/netlify-cms-core/src/components/Editor/Editor.js index 102a770f..516d8e54 100644 --- a/packages/netlify-cms-core/src/components/Editor/Editor.js +++ b/packages/netlify-cms-core/src/components/Editor/Editor.js @@ -64,7 +64,7 @@ export class Editor extends React.Component { deleteUnpublishedEntry: PropTypes.func.isRequired, logoutUser: PropTypes.func.isRequired, loadEntries: PropTypes.func.isRequired, - deployPreview: ImmutablePropTypes.map, + deployPreview: PropTypes.object, loadDeployPreview: PropTypes.func.isRequired, currentStatus: PropTypes.string, user: PropTypes.object, diff --git a/packages/netlify-cms-core/src/components/Editor/EditorInterface.js b/packages/netlify-cms-core/src/components/Editor/EditorInterface.js index 7007a0ea..a8ba42a5 100644 --- a/packages/netlify-cms-core/src/components/Editor/EditorInterface.js +++ b/packages/netlify-cms-core/src/components/Editor/EditorInterface.js @@ -422,7 +422,7 @@ EditorInterface.propTypes = { isModification: PropTypes.bool, currentStatus: PropTypes.string, onLogoutClick: PropTypes.func.isRequired, - deployPreview: ImmutablePropTypes.map, + deployPreview: PropTypes.object, loadDeployPreview: PropTypes.func.isRequired, draftKey: PropTypes.string.isRequired, t: PropTypes.func.isRequired, diff --git a/packages/netlify-cms-core/src/components/Editor/EditorToolbar.js b/packages/netlify-cms-core/src/components/Editor/EditorToolbar.js index df3695ac..161168de 100644 --- a/packages/netlify-cms-core/src/components/Editor/EditorToolbar.js +++ b/packages/netlify-cms-core/src/components/Editor/EditorToolbar.js @@ -4,7 +4,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import { css } from '@emotion/core'; import styled from '@emotion/styled'; import { translate } from 'react-polyglot'; -import { Map } from 'immutable'; import { Link } from 'react-router-dom'; import { Icon, @@ -244,7 +243,7 @@ class EditorToolbar extends React.Component { isModification: PropTypes.bool, currentStatus: PropTypes.string, onLogoutClick: PropTypes.func.isRequired, - deployPreview: ImmutablePropTypes.map, + deployPreview: PropTypes.object, loadDeployPreview: PropTypes.func.isRequired, t: PropTypes.func.isRequired, editorBackLink: PropTypes.string.isRequired, @@ -276,15 +275,13 @@ class EditorToolbar extends React.Component { }; renderDeployPreviewControls = label => { - const { deployPreview = Map(), loadDeployPreview, t } = this.props; - const url = deployPreview.get('url'); - const status = deployPreview.get('status'); + const { deployPreview = {}, loadDeployPreview, t } = this.props; + const { url, status, isFetching } = deployPreview; if (!status) { return; } - const isFetching = deployPreview.get('isFetching'); const deployPreviewReady = status === 'SUCCESS' && !isFetching; return ( diff --git a/packages/netlify-cms-core/src/reducers/deploys.js b/packages/netlify-cms-core/src/reducers/deploys.js deleted file mode 100644 index db29d4c8..00000000 --- a/packages/netlify-cms-core/src/reducers/deploys.js +++ /dev/null @@ -1,46 +0,0 @@ -import { Map, fromJS } from 'immutable'; -import { - DEPLOY_PREVIEW_REQUEST, - DEPLOY_PREVIEW_SUCCESS, - DEPLOY_PREVIEW_FAILURE, -} from 'Actions/deploys'; - -function deploys(state = Map({ deploys: Map() }), action) { - switch (action.type) { - case DEPLOY_PREVIEW_REQUEST: { - const { collection, slug } = action.payload; - return state.setIn(['deploys', `${collection}.${slug}`, 'isFetching'], true); - } - - case DEPLOY_PREVIEW_SUCCESS: { - const { collection, slug, url, status } = action.payload; - return state.setIn( - ['deploys', `${collection}.${slug}`], - fromJS({ - isFetching: false, - url, - status, - }), - ); - } - - case DEPLOY_PREVIEW_FAILURE: { - const { collection, slug } = action.payload; - return state.setIn( - ['deploys', `${collection}.${slug}`], - fromJS({ - isFetching: false, - }), - ); - } - - default: - return state; - } -} - -export function selectDeployPreview(state, collection, slug) { - return state.getIn(['deploys', `${collection}.${slug}`]); -} - -export default deploys; diff --git a/packages/netlify-cms-core/src/reducers/deploys.ts b/packages/netlify-cms-core/src/reducers/deploys.ts new file mode 100644 index 00000000..8d245fad --- /dev/null +++ b/packages/netlify-cms-core/src/reducers/deploys.ts @@ -0,0 +1,50 @@ +import { produce } from 'immer'; +import { + DEPLOY_PREVIEW_REQUEST, + DEPLOY_PREVIEW_SUCCESS, + DEPLOY_PREVIEW_FAILURE, + DeploysAction, +} from '../actions/deploys'; + +export type Deploys = { + [key: string]: { + isFetching: boolean; + url?: string; + status?: string; + }; +}; + +const defaultState: Deploys = {}; + +const deploys = produce((state: Deploys, action: DeploysAction) => { + switch (action.type) { + case DEPLOY_PREVIEW_REQUEST: { + const { collection, slug } = action.payload; + const key = `${collection}.${slug}`; + state[key] = state[key] || {}; + state[key].isFetching = true; + break; + } + + case DEPLOY_PREVIEW_SUCCESS: { + const { collection, slug, url, status } = action.payload; + const key = `${collection}.${slug}`; + state[key].isFetching = false; + state[key].url = url; + state[key].status = status; + break; + } + + case DEPLOY_PREVIEW_FAILURE: { + const { collection, slug } = action.payload; + state[`${collection}.${slug}`].isFetching = false; + break; + } + } +}, defaultState); + +export function selectDeployPreview(state: Deploys, collection: string, slug: string) { + return state[`${collection}.${slug}`]; +} + +export default deploys; diff --git a/packages/netlify-cms-core/src/types/redux.ts b/packages/netlify-cms-core/src/types/redux.ts index b37e7cb3..c3da40f5 100644 --- a/packages/netlify-cms-core/src/types/redux.ts +++ b/packages/netlify-cms-core/src/types/redux.ts @@ -6,6 +6,7 @@ import { MediaFile as BackendMediaFile } from '../backend'; import { Auth } from '../reducers/auth'; import { Status } from '../reducers/status'; import { Medias } from '../reducers/medias'; +import { Deploys } from '../reducers/deploys'; export type CmsBackendType = | 'azure' @@ -510,8 +511,6 @@ export type Entries = StaticallyTypedRecord<{ viewStyle: string; }>; -export type Deploys = StaticallyTypedRecord<{}>; - export type EditorialWorkflow = StaticallyTypedRecord<{ pages: Pages & PagesObject; entities: Entities & EntitiesObject;