chore: refactoring deploys (#5135)

This commit is contained in:
Vladislav Shkodin
2021-03-21 18:13:03 +02:00
committed by GitHub
parent a0ae13a665
commit 822acd4585
8 changed files with 161 additions and 142 deletions

View File

@ -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));
}
};
}

View File

@ -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<State, undefined, AnyAction>, 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
>;

View File

@ -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,

View File

@ -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,

View File

@ -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 (
<PreviewButtonContainer>

View File

@ -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;

View File

@ -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;

View File

@ -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;