add redux-thunk to admin

Signed-off-by: HichamELBSI <elabbassih@gmail.com>
This commit is contained in:
HichamELBSI 2021-01-21 20:00:45 +01:00
parent 58a7665c63
commit 95ed2ee412
21 changed files with 448 additions and 213 deletions

View File

@ -0,0 +1,52 @@
{
"routes": [
{
"method": "GET",
"path": "/newcollections",
"handler": "newcollection.find",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/newcollections/count",
"handler": "newcollection.count",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/newcollections/:id",
"handler": "newcollection.findOne",
"config": {
"policies": []
}
},
{
"method": "POST",
"path": "/newcollections",
"handler": "newcollection.create",
"config": {
"policies": []
}
},
{
"method": "PUT",
"path": "/newcollections/:id",
"handler": "newcollection.update",
"config": {
"policies": []
}
},
{
"method": "DELETE",
"path": "/newcollections/:id",
"handler": "newcollection.delete",
"config": {
"policies": []
}
}
]
}

View File

@ -0,0 +1,8 @@
'use strict';
/**
* Read the documentation (https://strapi.io/documentation/developer-docs/latest/concepts/controllers.html#core-controllers)
* to customize this controller
*/
module.exports = {};

View File

@ -0,0 +1,8 @@
'use strict';
/**
* Read the documentation (https://strapi.io/documentation/developer-docs/latest/concepts/models.html#lifecycle-hooks)
* to customize this model
*/
module.exports = {};

View File

@ -0,0 +1,24 @@
{
"kind": "collectionType",
"collectionName": "newcollections",
"info": {
"name": "newcollection"
},
"options": {
"increments": true,
"timestamps": true,
"draftAndPublish": false
},
"attributes": {
"title": {
"default": "dazdazdzad",
"private": true,
"regex": "dzadaz",
"maxLength": 120,
"unique": true,
"minLength": 10,
"type": "text",
"required": true
}
}
}

View File

@ -0,0 +1,8 @@
'use strict';
/**
* Read the documentation (https://strapi.io/documentation/developer-docs/latest/concepts/services.html#core-services)
* to customize this service
*/
module.exports = {};

View File

@ -60,10 +60,11 @@ import plugins from './plugins';
const strapi = Strapi(); const strapi = Strapi();
const initialState = {}; const initialState = {};
const store = configureStore(initialState, history);
const { dispatch } = store;
const MOUNT_NODE = document.getElementById('app') || document.createElement('div'); const MOUNT_NODE = document.getElementById('app') || document.createElement('div');
const store = configureStore(initialState, strapi);
const { dispatch } = store;
Object.keys(plugins).forEach(current => { Object.keys(plugins).forEach(current => {
const registerPlugin = plugin => { const registerPlugin = plugin => {
return plugin; return plugin;
@ -78,6 +79,7 @@ Object.keys(plugins).forEach(current => {
registerField: strapi.fieldApi.registerField, registerField: strapi.fieldApi.registerField,
registerPlugin, registerPlugin,
settingsBaseURL: SETTINGS_BASE_URL || '/settings', settingsBaseURL: SETTINGS_BASE_URL || '/settings',
middlewares: strapi.middlewares,
}); });
const pluginTradsPrefixed = languages.reduce((acc, lang) => { const pluginTradsPrefixed = languages.reduce((acc, lang) => {
@ -97,7 +99,6 @@ Object.keys(plugins).forEach(current => {
}, {}); }, {});
// Inject plugins reducers // Inject plugins reducers
console.log(plugin.reducers);
const pluginReducers = plugin.reducers || {}; const pluginReducers = plugin.reducers || {};
Object.keys(pluginReducers).forEach(reducerName => { Object.keys(pluginReducers).forEach(reducerName => {
@ -106,7 +107,7 @@ Object.keys(plugins).forEach(current => {
try { try {
merge(translationMessages, pluginTradsPrefixed); merge(translationMessages, pluginTradsPrefixed);
dispatch(pluginLoaded(plugin)); // dispatch(pluginLoaded(plugin));
} catch (err) { } catch (err) {
console.log({ err }); console.log({ err });
} }

View File

@ -6,17 +6,26 @@ import { createStore, applyMiddleware, compose } from 'redux';
import { fromJS } from 'immutable'; import { fromJS } from 'immutable';
// import { routerMiddleware } from 'react-router-redux'; // import { routerMiddleware } from 'react-router-redux';
import createSagaMiddleware from 'redux-saga'; import createSagaMiddleware from 'redux-saga';
import thunkMiddleware from 'redux-thunk';
import createReducer from './reducers'; import createReducer from './reducers';
const sagaMiddleware = createSagaMiddleware(); const sagaMiddleware = createSagaMiddleware();
export default function configureStore(initialState = {}) { export default function configureStore(initialState = {}, strapi) {
// Create the store with two middlewares // Create the store with two middlewares
// 1. sagaMiddleware: Makes redux-sagas work // 1. sagaMiddleware: Makes redux-sagas work
// 2. routerMiddleware: Syncs the location/URL path to the state // 2. routerMiddleware: Syncs the location/URL path to the state
const middlewares = [sagaMiddleware]; const middlewares = [sagaMiddleware];
const thunkMiddlewares = [thunkMiddleware.withExtraArgument(initialState)];
const enhancers = [applyMiddleware(...middlewares)]; // Add the plugins middlewares
console.log('loop')
for (let i in strapi.middlewares.middlewares) {
console.log(i);
// thunkMiddlewares.push(app.middlewares[i]());
}
const enhancers = [applyMiddleware(...middlewares, ...thunkMiddlewares)];
// If Redux DevTools Extension is installed use it, otherwise use Redux compose // If Redux DevTools Extension is installed use it, otherwise use Redux compose
/* eslint-disable no-underscore-dangle */ /* eslint-disable no-underscore-dangle */
@ -25,19 +34,15 @@ export default function configureStore(initialState = {}) {
typeof window === 'object' && typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// TODO Try to remove when `react-router-redux` is out of beta, LOCATION_CHANGE should not be fired more than once after hot reloading // TODO Try to remove when `react-router-redux` is out of beta, LOCATION_CHANGE should not be fired more than once after hot reloading
// Prevent recomputing reducers for `replaceReducer` // Prevent recomputing reducers for `replaceReducer`
shouldHotReload: false, shouldHotReload: false,
name: 'Strapi - Dashboard', name: 'Strapi - Dashboard',
}) })
: compose; : compose;
/* eslint-enable */ /* eslint-enable */
const store = createStore( const store = createStore(createReducer(), fromJS(initialState), composeEnhancers(...enhancers));
createReducer(),
fromJS(initialState),
composeEnhancers(...enhancers),
);
// Extensions // Extensions
store.runSaga = sagaMiddleware.run; store.runSaga = sagaMiddleware.run;

View File

@ -249,6 +249,8 @@ export class Admin extends React.Component {
updatePlugin, updatePlugin,
} = this.props; } = this.props;
console.log(plugins);
// We need the admin data in order to make the initializers work // We need the admin data in order to make the initializers work
if (this.showLoader()) { if (this.showLoader()) {
return ( return (

View File

@ -0,0 +1,16 @@
import { cloneDeep } from 'lodash';
class MiddleWares {
middlewares = [];
add(middleware) {
console.log('add: ', middleware);
this.middlewares.push(middleware);
}
get middlewares() {
return cloneDeep(this.middlewares);
}
}
export default () => new MiddleWares();

View File

@ -1,9 +1,12 @@
import ComponentApi from './ComponentApi'; import ComponentApi from './ComponentApi';
import FieldApi from './FieldApi'; import FieldApi from './FieldApi';
import MiddleWares from './MiddleWares';
class Strapi { class Strapi {
componentApi = ComponentApi(); componentApi = ComponentApi();
middlewares = MiddleWares();
fieldApi = FieldApi(); fieldApi = FieldApi();
} }

View File

@ -10,7 +10,6 @@ import checkStore from './checkStore';
export function injectReducerFactory(store, isValid) { export function injectReducerFactory(store, isValid) {
return function injectReducer(key, reducer) { return function injectReducer(key, reducer) {
// console.log(key, reducer);
if (!isValid) checkStore(store); if (!isValid) checkStore(store);
invariant( invariant(

View File

@ -93,6 +93,7 @@
"redux": "^4.0.1", "redux": "^4.0.1",
"redux-immutable": "^4.0.0", "redux-immutable": "^4.0.0",
"redux-saga": "^0.16.0", "redux-saga": "^0.16.0",
"redux-thunk": "2.3.0",
"reselect": "^4.0.0", "reselect": "^4.0.0",
"sanitize.css": "^4.1.0", "sanitize.css": "^4.1.0",
"semver": "7.3.4", "semver": "7.3.4",

View File

@ -0,0 +1,15 @@
export const ADD_ATTRIBUTE = 'ContentTypeBuilder/DataManagerProvider/ADD_ATTRIBUTE';
export const ADD_CREATED_COMPONENT_TO_DYNAMIC_ZONE = 'ContentTypeBuilder/DataManagerProvider/ADD_CREATED_COMPONENT_TO_DYNAMIC_ZONE';
export const CANCEL_CHANGES = 'ContentTypeBuilder/DataManagerProvider/CANCEL_CHANGES';
export const CHANGE_DYNAMIC_ZONE_COMPONENTS = 'ContentTypeBuilder/DataManagerProvider/CHANGE_DYNAMIC_ZONE_COMPONENTS';
export const CREATE_SCHEMA = 'ContentTypeBuilder/DataManagerProvider/CREATE_SCHEMA';
export const CREATE_COMPONENT_SCHEMA = 'ContentTypeBuilder/DataManagerProvider/CREATE_COMPONENT_SCHEMA';
export const DELETE_NOT_SAVED_TYPE = 'ContentTypeBuilder/DataManagerProvider/DELETE_NOT_SAVED_TYPE';
export const EDIT_ATTRIBUTE = 'ContentTypeBuilder/DataManagerProvider/EDIT_ATTRIBUTE';
export const GET_DATA_SUCCEEDED = 'ContentTypeBuilder/DataManagerProvider/GET_DATA_SUCCEEDED';
export const RELOAD_PLUGIN = 'ContentTypeBuilder/DataManagerProvider/RELOAD_PLUGIN';
export const REMOVE_FIELD_FROM_DISPLAYED_COMPONENT = 'ContentTypeBuilder/DataManagerProvider/REMOVE_FIELD_FROM_DISPLAYED_COMPONENT';
export const REMOVE_COMPONENT_FROM_DYNAMIC_ZONE = 'ContentTypeBuilder/DataManagerProvider/REMOVE_COMPONENT_FROM_DYNAMIC_ZONE';
export const REMOVE_FIELD = 'ContentTypeBuilder/DataManagerProvider/REMOVE_FIELD';
export const SET_MODIFIED_DATA = 'ContentTypeBuilder/DataManagerProvider/SET_MODIFIED_DATA';
export const UPDATE_SCHEMA = 'ContentTypeBuilder/DataManagerProvider/UPDATE_SCHEMA';

View File

@ -1,4 +1,4 @@
import React, { memo, useEffect, useMemo, useReducer, useState, useRef } from 'react'; import React, { memo, useEffect, useMemo, useState, useRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { get, groupBy, set, size } from 'lodash'; import { get, groupBy, set, size } from 'lodash';
import { import {
@ -8,13 +8,12 @@ import {
PopUpWarning, PopUpWarning,
} from 'strapi-helper-plugin'; } from 'strapi-helper-plugin';
import { useHistory, useLocation, useRouteMatch, Redirect } from 'react-router-dom'; import { useHistory, useLocation, useRouteMatch, Redirect } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import DataManagerContext from '../../contexts/DataManagerContext'; import DataManagerContext from '../../contexts/DataManagerContext';
import getTrad from '../../utils/getTrad'; import getTrad from '../../utils/getTrad';
import makeUnique from '../../utils/makeUnique'; import makeUnique from '../../utils/makeUnique';
import pluginId from '../../pluginId'; import pluginId from '../../pluginId';
import FormModal from '../FormModal'; import FormModal from '../FormModal';
import init from './init';
import reducer, { initialState } from './reducer';
import createDataObject from './utils/createDataObject'; import createDataObject from './utils/createDataObject';
import createModifiedDataSchema, { import createModifiedDataSchema, {
orderAllDataAttributesWithImmutable, orderAllDataAttributesWithImmutable,
@ -30,8 +29,29 @@ import {
sortContentType, sortContentType,
} from './utils/cleanData'; } from './utils/cleanData';
import {
ADD_ATTRIBUTE,
ADD_CREATED_COMPONENT_TO_DYNAMIC_ZONE,
CANCEL_CHANGES,
CHANGE_DYNAMIC_ZONE_COMPONENTS,
CREATE_SCHEMA,
CREATE_COMPONENT_SCHEMA,
DELETE_NOT_SAVED_TYPE,
EDIT_ATTRIBUTE,
GET_DATA_SUCCEEDED,
RELOAD_PLUGIN,
REMOVE_FIELD_FROM_DISPLAYED_COMPONENT,
REMOVE_COMPONENT_FROM_DYNAMIC_ZONE,
REMOVE_FIELD,
SET_MODIFIED_DATA,
UPDATE_SCHEMA,
} from './constants';
import makeSelectDataManagerProvider from './selectors';
const DataManagerProvider = ({ allIcons, children }) => { const DataManagerProvider = ({ allIcons, children }) => {
const [reducerState, dispatch] = useReducer(reducer, initialState, init); const dataManagerProviderSelector = useMemo(makeSelectDataManagerProvider, []);
const dispatch = useDispatch();
const reducerState = useSelector(state => dataManagerProviderSelector(state), []);
const [infoModals, toggleInfoModal] = useState({ cancel: false }); const [infoModals, toggleInfoModal] = useState({ cancel: false });
const { const {
autoReload, autoReload,
@ -49,7 +69,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
initialData, initialData,
modifiedData, modifiedData,
reservedNames, reservedNames,
} = reducerState.toJS(); } = reducerState;
const { pathname } = useLocation(); const { pathname } = useLocation();
const { push } = useHistory(); const { push } = useHistory();
const contentTypeMatch = useRouteMatch(`/plugins/${pluginId}/content-types/:uid`); const contentTypeMatch = useRouteMatch(`/plugins/${pluginId}/content-types/:uid`);
@ -96,7 +116,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
}); });
dispatch({ dispatch({
type: 'GET_DATA_SUCCEEDED', type: GET_DATA_SUCCEEDED,
components: orderedComponents.get('components'), components: orderedComponents.get('components'),
contentTypes: orderedContenTypes.get('components'), contentTypes: orderedContenTypes.get('components'),
reservedNames, reservedNames,
@ -143,7 +163,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
initialAttribute, initialAttribute,
shouldAddComponentToData = false shouldAddComponentToData = false
) => { ) => {
const actionType = isEditing ? 'EDIT_ATTRIBUTE' : 'ADD_ATTRIBUTE'; const actionType = isEditing ? EDIT_ATTRIBUTE : ADD_ATTRIBUTE;
dispatch({ dispatch({
type: actionType, type: actionType,
@ -157,7 +177,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
const addCreatedComponentToDynamicZone = (dynamicZoneTarget, componentsToAdd) => { const addCreatedComponentToDynamicZone = (dynamicZoneTarget, componentsToAdd) => {
dispatch({ dispatch({
type: 'ADD_CREATED_COMPONENT_TO_DYNAMIC_ZONE', type: ADD_CREATED_COMPONENT_TO_DYNAMIC_ZONE,
dynamicZoneTarget, dynamicZoneTarget,
componentsToAdd, componentsToAdd,
}); });
@ -165,7 +185,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
const cancelChanges = () => { const cancelChanges = () => {
toggleModalCancel(); toggleModalCancel();
dispatch({ type: 'CANCEL_CHANGES' }); dispatch({ type: CANCEL_CHANGES });
}; };
const createSchema = ( const createSchema = (
@ -175,7 +195,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
componentCategory, componentCategory,
shouldAddComponentToData = false shouldAddComponentToData = false
) => { ) => {
const type = schemaType === 'contentType' ? 'CREATE_SCHEMA' : 'CREATE_COMPONENT_SCHEMA'; const type = schemaType === 'contentType' ? CREATE_SCHEMA : CREATE_COMPONENT_SCHEMA;
dispatch({ dispatch({
type, type,
@ -189,7 +209,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
const changeDynamicZoneComponents = (dynamicZoneTarget, newComponents) => { const changeDynamicZoneComponents = (dynamicZoneTarget, newComponents) => {
dispatch({ dispatch({
type: 'CHANGE_DYNAMIC_ZONE_COMPONENTS', type: CHANGE_DYNAMIC_ZONE_COMPONENTS,
dynamicZoneTarget, dynamicZoneTarget,
newComponents, newComponents,
}); });
@ -197,7 +217,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
const removeAttribute = (mainDataKey, attributeToRemoveName, componentUid = '') => { const removeAttribute = (mainDataKey, attributeToRemoveName, componentUid = '') => {
const type = const type =
mainDataKey === 'components' ? 'REMOVE_FIELD_FROM_DISPLAYED_COMPONENT' : 'REMOVE_FIELD'; mainDataKey === 'components' ? REMOVE_FIELD_FROM_DISPLAYED_COMPONENT : REMOVE_FIELD;
if (mainDataKey === 'contentType') { if (mainDataKey === 'contentType') {
emitEvent('willDeleteFieldOfContentType'); emitEvent('willDeleteFieldOfContentType');
@ -231,7 +251,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
await updatePermissions(); await updatePermissions();
// Reload the plugin so the cycle is new again // Reload the plugin so the cycle is new again
dispatch({ type: 'RELOAD_PLUGIN' }); dispatch({ type: RELOAD_PLUGIN });
// Refetch all the data // Refetch all the data
getDataRef.current(); getDataRef.current();
} }
@ -268,7 +288,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
// Here we just need to reset the components to the initial ones and also the content types // Here we just need to reset the components to the initial ones and also the content types
// Doing so will trigging a url change since the type doesn't exist in either the contentTypes or the components // Doing so will trigging a url change since the type doesn't exist in either the contentTypes or the components
// so the modified and the initial data will also be reset in the useEffect... // so the modified and the initial data will also be reset in the useEffect...
dispatch({ type: 'DELETE_NOT_SAVED_TYPE' }); dispatch({ type: DELETE_NOT_SAVED_TYPE });
return; return;
} }
@ -278,7 +298,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
await request(requestURL, { method: 'DELETE' }, true); await request(requestURL, { method: 'DELETE' }, true);
// Reload the plugin so the cycle is new again // Reload the plugin so the cycle is new again
dispatch({ type: 'RELOAD_PLUGIN' }); dispatch({ type: RELOAD_PLUGIN });
// Refetch the permissions // Refetch the permissions
await updatePermissions(); await updatePermissions();
@ -315,7 +335,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
await updatePermissions(); await updatePermissions();
// Reload the plugin so the cycle is new again // Reload the plugin so the cycle is new again
dispatch({ type: 'RELOAD_PLUGIN' }); dispatch({ type: RELOAD_PLUGIN });
// Refetch all the data // Refetch all the data
getDataRef.current(); getDataRef.current();
} catch (err) { } catch (err) {
@ -356,7 +376,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
const removeComponentFromDynamicZone = (dzName, componentToRemoveIndex) => { const removeComponentFromDynamicZone = (dzName, componentToRemoveIndex) => {
dispatch({ dispatch({
type: 'REMOVE_COMPONENT_FROM_DYNAMIC_ZONE', type: REMOVE_COMPONENT_FROM_DYNAMIC_ZONE,
dzName, dzName,
componentToRemoveIndex, componentToRemoveIndex,
}); });
@ -387,7 +407,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
size(get(schemaToSet, 'schema.attributes', {})) === 0; size(get(schemaToSet, 'schema.attributes', {})) === 0;
dispatch({ dispatch({
type: 'SET_MODIFIED_DATA', type: SET_MODIFIED_DATA,
schemaToSet: dataShape, schemaToSet: dataShape,
hasJustCreatedSchema, hasJustCreatedSchema,
}); });
@ -466,7 +486,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
} }
// Reload the plugin so the cycle is new again // Reload the plugin so the cycle is new again
dispatch({ type: 'RELOAD_PLUGIN' }); dispatch({ type: RELOAD_PLUGIN });
// Refetch all the data // Refetch all the data
getDataRef.current(); getDataRef.current();
} catch (err) { } catch (err) {
@ -502,7 +522,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
const updateSchema = (data, schemaType, componentUID) => { const updateSchema = (data, schemaType, componentUID) => {
dispatch({ dispatch({
type: 'UPDATE_SCHEMA', type: UPDATE_SCHEMA,
data, data,
schemaType, schemaType,
uid: componentUID, uid: componentUID,

View File

@ -1,5 +0,0 @@
function init(initialState) {
return initialState;
}
export default init;

View File

@ -3,7 +3,25 @@ import { get, has } from 'lodash';
import makeUnique from '../../utils/makeUnique'; import makeUnique from '../../utils/makeUnique';
import retrieveComponentsFromSchema from './utils/retrieveComponentsFromSchema'; import retrieveComponentsFromSchema from './utils/retrieveComponentsFromSchema';
const initialState = fromJS({ import {
ADD_ATTRIBUTE,
ADD_CREATED_COMPONENT_TO_DYNAMIC_ZONE,
CANCEL_CHANGES,
CHANGE_DYNAMIC_ZONE_COMPONENTS,
CREATE_SCHEMA,
CREATE_COMPONENT_SCHEMA,
DELETE_NOT_SAVED_TYPE,
EDIT_ATTRIBUTE,
GET_DATA_SUCCEEDED,
RELOAD_PLUGIN,
REMOVE_FIELD_FROM_DISPLAYED_COMPONENT,
REMOVE_COMPONENT_FROM_DYNAMIC_ZONE,
REMOVE_FIELD,
SET_MODIFIED_DATA,
UPDATE_SCHEMA,
} from './constants';
const initialState = {
components: {}, components: {},
contentTypes: {}, contentTypes: {},
initialComponents: {}, initialComponents: {},
@ -13,7 +31,7 @@ const initialState = fromJS({
reservedNames: {}, reservedNames: {},
isLoading: true, isLoading: true,
isLoadingForDataToBeSet: true, isLoadingForDataToBeSet: true,
}); };
const ONE_SIDE_RELATIONS = ['oneWay', 'manyWay']; const ONE_SIDE_RELATIONS = ['oneWay', 'manyWay'];
@ -65,9 +83,11 @@ const addComponentsToState = (state, componentToAddUid, objToUpdate) => {
return newObj; return newObj;
}; };
const reducer = (state, action) => { const reducer = (jsonState = initialState, action) => {
const state = fromJS(jsonState);
switch (action.type) { switch (action.type) {
case 'ADD_ATTRIBUTE': { case ADD_ATTRIBUTE: {
const { const {
attributeToSet: { name, ...rest }, attributeToSet: { name, ...rest },
forTarget, forTarget,
@ -122,24 +142,28 @@ const reducer = (state, action) => {
} }
return existingCompos; return existingCompos;
}); })
.toJS();
} }
case 'ADD_CREATED_COMPONENT_TO_DYNAMIC_ZONE': { case ADD_CREATED_COMPONENT_TO_DYNAMIC_ZONE: {
const { dynamicZoneTarget, componentsToAdd } = action; const { dynamicZoneTarget, componentsToAdd } = action;
return state.updateIn( return state
['modifiedData', 'contentType', 'schema', 'attributes', dynamicZoneTarget, 'components'], .updateIn(
list => { ['modifiedData', 'contentType', 'schema', 'attributes', dynamicZoneTarget, 'components'],
return list.concat(componentsToAdd); list => {
} return list.concat(componentsToAdd);
); }
)
.toJS();
} }
case 'CANCEL_CHANGES': { case CANCEL_CHANGES: {
return state return state
.update('modifiedData', () => state.get('initialData')) .update('modifiedData', () => state.get('initialData'))
.update('components', () => state.get('initialComponents')); .update('components', () => state.get('initialComponents'))
.toJS();
} }
case 'CHANGE_DYNAMIC_ZONE_COMPONENTS': { case CHANGE_DYNAMIC_ZONE_COMPONENTS: {
const { dynamicZoneTarget, newComponents } = action; const { dynamicZoneTarget, newComponents } = action;
return state return state
@ -155,10 +179,11 @@ const reducer = (state, action) => {
}, old); }, old);
return componentsSchema; return componentsSchema;
}); })
.toJS();
} }
case 'CREATE_SCHEMA': { case CREATE_SCHEMA: {
const newSchema = { const newSchema = {
uid: action.uid, uid: action.uid,
isTemporary: true, isTemporary: true,
@ -168,9 +193,9 @@ const reducer = (state, action) => {
}, },
}; };
return state.updateIn(['contentTypes', action.uid], () => fromJS(newSchema)); return state.updateIn(['contentTypes', action.uid], () => fromJS(newSchema)).toJS();
} }
case 'CREATE_COMPONENT_SCHEMA': { case CREATE_COMPONENT_SCHEMA: {
const newSchema = { const newSchema = {
uid: action.uid, uid: action.uid,
isTemporary: true, isTemporary: true,
@ -184,18 +209,20 @@ const reducer = (state, action) => {
if (action.shouldAddComponentToData) { if (action.shouldAddComponentToData) {
return state return state
.updateIn(['components', action.uid], () => fromJS(newSchema)) .updateIn(['components', action.uid], () => fromJS(newSchema))
.updateIn(['modifiedData', 'components', action.uid], () => fromJS(newSchema)); .updateIn(['modifiedData', 'components', action.uid], () => fromJS(newSchema))
.toJS();
} }
return state.updateIn(['components', action.uid], () => fromJS(newSchema)); return state.updateIn(['components', action.uid], () => fromJS(newSchema)).toJS();
} }
case 'DELETE_NOT_SAVED_TYPE': { case DELETE_NOT_SAVED_TYPE: {
// Doing so will also reset the modified and the initial data // Doing so will also reset the modified and the initial data
return state return state
.update('contentTypes', () => state.get('initialContentTypes')) .update('contentTypes', () => state.get('initialContentTypes'))
.update('components', () => state.get('initialComponents')); .update('components', () => state.get('initialComponents'))
.toJS();
} }
case 'EDIT_ATTRIBUTE': { case EDIT_ATTRIBUTE: {
const { const {
attributeToSet: { name, ...rest }, attributeToSet: { name, ...rest },
forTarget, forTarget,
@ -209,132 +236,134 @@ const reducer = (state, action) => {
? [forTarget] ? [forTarget]
: [forTarget, targetUid]; : [forTarget, targetUid];
return newState.updateIn(['modifiedData', ...pathToDataToEdit, 'schema'], obj => { return newState
let oppositeAttributeNameToRemove = null; .updateIn(['modifiedData', ...pathToDataToEdit, 'schema'], obj => {
let oppositeAttributeNameToUpdate = null; let oppositeAttributeNameToRemove = null;
let oppositeAttributeNameToCreateBecauseOfNatureChange = null; let oppositeAttributeNameToUpdate = null;
let oppositeAttributeToCreate = null; let oppositeAttributeNameToCreateBecauseOfNatureChange = null;
let oppositeAttributeToCreate = null;
const newObj = OrderedMap( const newObj = OrderedMap(
obj obj
.get('attributes') .get('attributes')
.keySeq() .keySeq()
.reduce((acc, current) => { .reduce((acc, current) => {
const isEditingCurrentAttribute = current === initialAttributeName; const isEditingCurrentAttribute = current === initialAttributeName;
if (isEditingCurrentAttribute) { if (isEditingCurrentAttribute) {
const currentUid = state.getIn(['modifiedData', ...pathToDataToEdit, 'uid']); const currentUid = state.getIn(['modifiedData', ...pathToDataToEdit, 'uid']);
const isEditingRelation = has(initialAttribute, 'nature'); const isEditingRelation = has(initialAttribute, 'nature');
const didChangeTargetRelation = initialAttribute.target !== rest.target; const didChangeTargetRelation = initialAttribute.target !== rest.target;
const didCreateInternalRelation = rest.target === currentUid; const didCreateInternalRelation = rest.target === currentUid;
const nature = rest.nature; const nature = rest.nature;
const initialNature = initialAttribute.nature; const initialNature = initialAttribute.nature;
const hadInternalRelation = initialAttribute.target === currentUid; const hadInternalRelation = initialAttribute.target === currentUid;
const didChangeRelationNature = initialAttribute.nature !== nature; const didChangeRelationNature = initialAttribute.nature !== nature;
const shouldRemoveOppositeAttributeBecauseOfTargetChange = const shouldRemoveOppositeAttributeBecauseOfTargetChange =
didChangeTargetRelation && didChangeTargetRelation &&
!didCreateInternalRelation && !didCreateInternalRelation &&
hadInternalRelation && hadInternalRelation &&
isEditingRelation; isEditingRelation;
const shouldRemoveOppositeAttributeBecauseOfNatureChange = const shouldRemoveOppositeAttributeBecauseOfNatureChange =
didChangeRelationNature && didChangeRelationNature &&
hadInternalRelation && hadInternalRelation &&
['oneWay', 'manyWay'].includes(nature) && ['oneWay', 'manyWay'].includes(nature) &&
isEditingRelation; isEditingRelation;
const shouldUpdateOppositeAttributeBecauseOfNatureChange = const shouldUpdateOppositeAttributeBecauseOfNatureChange =
!ONE_SIDE_RELATIONS.includes(initialNature) && !ONE_SIDE_RELATIONS.includes(initialNature) &&
!ONE_SIDE_RELATIONS.includes(nature) && !ONE_SIDE_RELATIONS.includes(nature) &&
hadInternalRelation && hadInternalRelation &&
didCreateInternalRelation && didCreateInternalRelation &&
isEditingRelation; isEditingRelation;
const shouldCreateOppositeAttributeBecauseOfNatureChange = const shouldCreateOppositeAttributeBecauseOfNatureChange =
ONE_SIDE_RELATIONS.includes(initialNature) && ONE_SIDE_RELATIONS.includes(initialNature) &&
!ONE_SIDE_RELATIONS.includes(nature) && !ONE_SIDE_RELATIONS.includes(nature) &&
hadInternalRelation && hadInternalRelation &&
didCreateInternalRelation && didCreateInternalRelation &&
isEditingRelation; isEditingRelation;
const shouldCreateOppositeAttributeBecauseOfTargetChange = const shouldCreateOppositeAttributeBecauseOfTargetChange =
didChangeTargetRelation && didChangeTargetRelation &&
didCreateInternalRelation && didCreateInternalRelation &&
!ONE_SIDE_RELATIONS.includes(nature); !ONE_SIDE_RELATIONS.includes(nature);
// Update the opposite attribute name so it is removed at the end of the loop // Update the opposite attribute name so it is removed at the end of the loop
if (
shouldRemoveOppositeAttributeBecauseOfTargetChange ||
shouldRemoveOppositeAttributeBecauseOfNatureChange
) {
oppositeAttributeNameToRemove = initialAttribute.targetAttribute;
}
// Set the opposite attribute that will be updated when the loop attribute matches the name
if (
shouldUpdateOppositeAttributeBecauseOfNatureChange ||
shouldCreateOppositeAttributeBecauseOfNatureChange ||
shouldCreateOppositeAttributeBecauseOfTargetChange
) {
oppositeAttributeNameToUpdate = initialAttribute.targetAttribute;
oppositeAttributeNameToCreateBecauseOfNatureChange = rest.targetAttribute;
oppositeAttributeToCreate = {
nature: getOppositeNature(rest.nature),
target: rest.target,
unique: rest.unique,
// Leave this if we allow the required on the relation
// required: rest.required,
dominant: rest.nature === 'manyToMany' ? !rest.dominant : null,
targetAttribute: name,
columnName: rest.targetColumnName,
targetColumnName: rest.columnName,
};
// First update the current attribute with the value
acc[name] = fromJS(rest);
// Then (if needed) create the opposite attribute the case is changing the relation from
// We do it here so keep the order of the attributes
// oneWay || manyWay to something another relation
if ( if (
shouldRemoveOppositeAttributeBecauseOfTargetChange ||
shouldRemoveOppositeAttributeBecauseOfNatureChange
) {
oppositeAttributeNameToRemove = initialAttribute.targetAttribute;
}
// Set the opposite attribute that will be updated when the loop attribute matches the name
if (
shouldUpdateOppositeAttributeBecauseOfNatureChange ||
shouldCreateOppositeAttributeBecauseOfNatureChange || shouldCreateOppositeAttributeBecauseOfNatureChange ||
shouldCreateOppositeAttributeBecauseOfTargetChange shouldCreateOppositeAttributeBecauseOfTargetChange
) { ) {
acc[oppositeAttributeNameToCreateBecauseOfNatureChange] = fromJS( oppositeAttributeNameToUpdate = initialAttribute.targetAttribute;
oppositeAttributeToCreate oppositeAttributeNameToCreateBecauseOfNatureChange = rest.targetAttribute;
);
oppositeAttributeToCreate = null; oppositeAttributeToCreate = {
oppositeAttributeNameToCreateBecauseOfNatureChange = null; nature: getOppositeNature(rest.nature),
target: rest.target,
unique: rest.unique,
// Leave this if we allow the required on the relation
// required: rest.required,
dominant: rest.nature === 'manyToMany' ? !rest.dominant : null,
targetAttribute: name,
columnName: rest.targetColumnName,
targetColumnName: rest.columnName,
};
// First update the current attribute with the value
acc[name] = fromJS(rest);
// Then (if needed) create the opposite attribute the case is changing the relation from
// We do it here so keep the order of the attributes
// oneWay || manyWay to something another relation
if (
shouldCreateOppositeAttributeBecauseOfNatureChange ||
shouldCreateOppositeAttributeBecauseOfTargetChange
) {
acc[oppositeAttributeNameToCreateBecauseOfNatureChange] = fromJS(
oppositeAttributeToCreate
);
oppositeAttributeToCreate = null;
oppositeAttributeNameToCreateBecauseOfNatureChange = null;
}
return acc;
} }
return acc; acc[name] = fromJS(rest);
} else if (current === oppositeAttributeNameToUpdate) {
acc[oppositeAttributeNameToCreateBecauseOfNatureChange] = fromJS(
oppositeAttributeToCreate
);
} else {
acc[current] = obj.getIn(['attributes', current]);
} }
acc[name] = fromJS(rest); return acc;
} else if (current === oppositeAttributeNameToUpdate) { }, {})
acc[oppositeAttributeNameToCreateBecauseOfNatureChange] = fromJS( );
oppositeAttributeToCreate
);
} else {
acc[current] = obj.getIn(['attributes', current]);
}
return acc; let updatedObj;
}, {})
);
let updatedObj; // Remove the opposite attribute
if (oppositeAttributeNameToRemove !== null) {
updatedObj = newObj.remove(oppositeAttributeNameToRemove);
} else {
updatedObj = newObj;
}
// Remove the opposite attribute return obj.set('attributes', updatedObj);
if (oppositeAttributeNameToRemove !== null) { })
updatedObj = newObj.remove(oppositeAttributeNameToRemove); .toJS();
} else {
updatedObj = newObj;
}
return obj.set('attributes', updatedObj);
});
} }
case 'GET_DATA_SUCCEEDED': { case GET_DATA_SUCCEEDED: {
return state return state
.update('components', () => fromJS(action.components)) .update('components', () => fromJS(action.components))
.update('initialComponents', () => fromJS(action.components)) .update('initialComponents', () => fromJS(action.components))
@ -342,33 +371,38 @@ const reducer = (state, action) => {
.update('contentTypes', () => fromJS(action.contentTypes)) .update('contentTypes', () => fromJS(action.contentTypes))
.update('reservedNames', () => fromJS(action.reservedNames)) .update('reservedNames', () => fromJS(action.reservedNames))
.update('isLoading', () => false); .update('isLoading', () => false)
.toJS();
} }
case 'RELOAD_PLUGIN': case RELOAD_PLUGIN:
return initialState; return initialState;
case 'REMOVE_FIELD_FROM_DISPLAYED_COMPONENT': { case REMOVE_FIELD_FROM_DISPLAYED_COMPONENT: {
const { attributeToRemoveName, componentUid } = action; const { attributeToRemoveName, componentUid } = action;
return state.removeIn([ return state
'modifiedData', .removeIn([
'components', 'modifiedData',
componentUid, 'components',
'schema', componentUid,
'attributes', 'schema',
attributeToRemoveName, 'attributes',
]); attributeToRemoveName,
])
.toJS();
} }
case 'REMOVE_COMPONENT_FROM_DYNAMIC_ZONE': case REMOVE_COMPONENT_FROM_DYNAMIC_ZONE:
return state.removeIn([ return state
'modifiedData', .removeIn([
'contentType', 'modifiedData',
'schema', 'contentType',
'attributes', 'schema',
action.dzName, 'attributes',
'components', action.dzName,
action.componentToRemoveIndex, 'components',
]); action.componentToRemoveIndex,
case 'REMOVE_FIELD': { ])
.toJS();
case REMOVE_FIELD: {
const { mainDataKey, attributeToRemoveName } = action; const { mainDataKey, attributeToRemoveName } = action;
const pathToAttributes = ['modifiedData', mainDataKey, 'schema', 'attributes']; const pathToAttributes = ['modifiedData', mainDataKey, 'schema', 'attributes'];
const pathToAttributeToRemove = [...pathToAttributes, attributeToRemoveName]; const pathToAttributeToRemove = [...pathToAttributes, attributeToRemoveName];
@ -389,21 +423,25 @@ const reducer = (state, action) => {
if (shouldRemoveOppositeAttribute) { if (shouldRemoveOppositeAttribute) {
return state return state
.removeIn(pathToAttributeToRemove) .removeIn(pathToAttributeToRemove)
.removeIn([...pathToAttributes, targetAttribute]); .removeIn([...pathToAttributes, targetAttribute])
.toJS();
} }
} }
return state.removeIn(pathToAttributeToRemove).updateIn([...pathToAttributes], attributes => { return state
return attributes.keySeq().reduce((acc, current) => { .removeIn(pathToAttributeToRemove)
if (acc.getIn([current, 'targetField']) === attributeToRemoveName) { .updateIn([...pathToAttributes], attributes => {
return acc.removeIn([current, 'targetField']); return attributes.keySeq().reduce((acc, current) => {
} if (acc.getIn([current, 'targetField']) === attributeToRemoveName) {
return acc.removeIn([current, 'targetField']);
}
return acc; return acc;
}, attributes); }, attributes);
}); })
.toJS();
} }
case 'SET_MODIFIED_DATA': { case SET_MODIFIED_DATA: {
let newState = state let newState = state
.update('isLoadingForDataToBeSet', () => false) .update('isLoadingForDataToBeSet', () => false)
.update('initialData', () => fromJS(action.schemaToSet)) .update('initialData', () => fromJS(action.schemaToSet))
@ -417,9 +455,9 @@ const reducer = (state, action) => {
.update('contentTypes', () => state.get('initialContentTypes')); .update('contentTypes', () => state.get('initialContentTypes'));
} }
return newState; return newState.toJS();
} }
case 'UPDATE_SCHEMA': { case UPDATE_SCHEMA: {
const { const {
data: { name, collectionName, category, icon, kind }, data: { name, collectionName, category, icon, kind },
schemaType, schemaType,
@ -449,10 +487,10 @@ const reducer = (state, action) => {
}); });
} }
return newState; return newState.toJS();
} }
default: default:
return state; return state.toJS();
} }
}; };

View File

@ -0,0 +1,24 @@
import { createSelector } from 'reselect';
import pluginId from '../../pluginId';
import { initialState } from './reducer';
/**
* Direct selector to the dataManagerProvider state domain
*/
const dataManagerProviderDomain = () => state => state.get(`${pluginId}_dataManagerProvider`) || initialState;
/**
* Other specific selectors
*/
/**
* Default selector used by dataManagerProvider
*/
const makeSelectDataManagerProvider = () =>
createSelector(dataManagerProviderDomain(), substate => {
return substate;
});
export default makeSelectDataManagerProvider;
export { dataManagerProviderDomain };

View File

@ -28,8 +28,6 @@ describe('CTB | containers | FormModal | reducer | actions', () => {
}) })
); );
console.log(expected.toJS());
expect(reducer(state, action)).toEqual(expected); expect(reducer(state, action)).toEqual(expected);
}); });

View File

@ -74,5 +74,16 @@ export default strapi => {
}, },
}; };
const modalOnChangeMiddleware = () => {
return ({ dispatch, getState }) => next => action => {
if (action.type === 'ContentTypeBuilder/FormModal/ON_CHANGE') {
console.log(action);
}
return next(action);
};
};
strapi.middlewares.add(modalOnChangeMiddleware);
return strapi.registerPlugin(plugin); return strapi.registerPlugin(plugin);
}; };

View File

@ -1,8 +1,10 @@
import formModalReducer from './containers/FormModal/reducer'; import formModalReducer from './containers/FormModal/reducer';
import dataManagerProvider from './containers/DataManagerProvider/reducer';
import pluginId from './pluginId'; import pluginId from './pluginId';
const reducers = { const reducers = {
[`${pluginId}_formModal`]: formModalReducer, [`${pluginId}_formModal`]: formModalReducer,
[`${pluginId}_dataManagerProvider`]: dataManagerProvider,
}; };
export default reducers; export default reducers;

View File

@ -16202,6 +16202,11 @@ redux-saga@^0.16.0:
resolved "https://registry.yarnpkg.com/redux-saga/-/redux-saga-0.16.2.tgz#993662e86bc945d8509ac2b8daba3a8c615cc971" resolved "https://registry.yarnpkg.com/redux-saga/-/redux-saga-0.16.2.tgz#993662e86bc945d8509ac2b8daba3a8c615cc971"
integrity sha512-iIjKnRThI5sKPEASpUvySemjzwqwI13e3qP7oLub+FycCRDysLSAOwt958niZW6LhxfmS6Qm1BzbU70w/Koc4w== integrity sha512-iIjKnRThI5sKPEASpUvySemjzwqwI13e3qP7oLub+FycCRDysLSAOwt958niZW6LhxfmS6Qm1BzbU70w/Koc4w==
redux-thunk@2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==
redux@^4.0.1, redux@^4.0.4: redux@^4.0.1, redux@^4.0.4:
version "4.0.5" version "4.0.5"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"