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

View File

@ -6,17 +6,26 @@ import { createStore, applyMiddleware, compose } from 'redux';
import { fromJS } from 'immutable';
// import { routerMiddleware } from 'react-router-redux';
import createSagaMiddleware from 'redux-saga';
import thunkMiddleware from 'redux-thunk';
import createReducer from './reducers';
const sagaMiddleware = createSagaMiddleware();
export default function configureStore(initialState = {}) {
export default function configureStore(initialState = {}, strapi) {
// Create the store with two middlewares
// 1. sagaMiddleware: Makes redux-sagas work
// 2. routerMiddleware: Syncs the location/URL path to the state
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
/* eslint-disable no-underscore-dangle */
@ -25,19 +34,15 @@ export default function configureStore(initialState = {}) {
typeof window === 'object' &&
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
// Prevent recomputing reducers for `replaceReducer`
shouldHotReload: false,
name: 'Strapi - Dashboard',
})
// 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`
shouldHotReload: false,
name: 'Strapi - Dashboard',
})
: compose;
/* eslint-enable */
const store = createStore(
createReducer(),
fromJS(initialState),
composeEnhancers(...enhancers),
);
const store = createStore(createReducer(), fromJS(initialState), composeEnhancers(...enhancers));
// Extensions
store.runSaga = sagaMiddleware.run;

View File

@ -249,6 +249,8 @@ export class Admin extends React.Component {
updatePlugin,
} = this.props;
console.log(plugins);
// We need the admin data in order to make the initializers work
if (this.showLoader()) {
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 FieldApi from './FieldApi';
import MiddleWares from './MiddleWares';
class Strapi {
componentApi = ComponentApi();
middlewares = MiddleWares();
fieldApi = FieldApi();
}

View File

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

View File

@ -93,6 +93,7 @@
"redux": "^4.0.1",
"redux-immutable": "^4.0.0",
"redux-saga": "^0.16.0",
"redux-thunk": "2.3.0",
"reselect": "^4.0.0",
"sanitize.css": "^4.1.0",
"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 { get, groupBy, set, size } from 'lodash';
import {
@ -8,13 +8,12 @@ import {
PopUpWarning,
} from 'strapi-helper-plugin';
import { useHistory, useLocation, useRouteMatch, Redirect } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import DataManagerContext from '../../contexts/DataManagerContext';
import getTrad from '../../utils/getTrad';
import makeUnique from '../../utils/makeUnique';
import pluginId from '../../pluginId';
import FormModal from '../FormModal';
import init from './init';
import reducer, { initialState } from './reducer';
import createDataObject from './utils/createDataObject';
import createModifiedDataSchema, {
orderAllDataAttributesWithImmutable,
@ -30,8 +29,29 @@ import {
sortContentType,
} 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 [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 {
autoReload,
@ -49,7 +69,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
initialData,
modifiedData,
reservedNames,
} = reducerState.toJS();
} = reducerState;
const { pathname } = useLocation();
const { push } = useHistory();
const contentTypeMatch = useRouteMatch(`/plugins/${pluginId}/content-types/:uid`);
@ -96,7 +116,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
});
dispatch({
type: 'GET_DATA_SUCCEEDED',
type: GET_DATA_SUCCEEDED,
components: orderedComponents.get('components'),
contentTypes: orderedContenTypes.get('components'),
reservedNames,
@ -143,7 +163,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
initialAttribute,
shouldAddComponentToData = false
) => {
const actionType = isEditing ? 'EDIT_ATTRIBUTE' : 'ADD_ATTRIBUTE';
const actionType = isEditing ? EDIT_ATTRIBUTE : ADD_ATTRIBUTE;
dispatch({
type: actionType,
@ -157,7 +177,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
const addCreatedComponentToDynamicZone = (dynamicZoneTarget, componentsToAdd) => {
dispatch({
type: 'ADD_CREATED_COMPONENT_TO_DYNAMIC_ZONE',
type: ADD_CREATED_COMPONENT_TO_DYNAMIC_ZONE,
dynamicZoneTarget,
componentsToAdd,
});
@ -165,7 +185,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
const cancelChanges = () => {
toggleModalCancel();
dispatch({ type: 'CANCEL_CHANGES' });
dispatch({ type: CANCEL_CHANGES });
};
const createSchema = (
@ -175,7 +195,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
componentCategory,
shouldAddComponentToData = false
) => {
const type = schemaType === 'contentType' ? 'CREATE_SCHEMA' : 'CREATE_COMPONENT_SCHEMA';
const type = schemaType === 'contentType' ? CREATE_SCHEMA : CREATE_COMPONENT_SCHEMA;
dispatch({
type,
@ -189,7 +209,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
const changeDynamicZoneComponents = (dynamicZoneTarget, newComponents) => {
dispatch({
type: 'CHANGE_DYNAMIC_ZONE_COMPONENTS',
type: CHANGE_DYNAMIC_ZONE_COMPONENTS,
dynamicZoneTarget,
newComponents,
});
@ -197,7 +217,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
const removeAttribute = (mainDataKey, attributeToRemoveName, componentUid = '') => {
const type =
mainDataKey === 'components' ? 'REMOVE_FIELD_FROM_DISPLAYED_COMPONENT' : 'REMOVE_FIELD';
mainDataKey === 'components' ? REMOVE_FIELD_FROM_DISPLAYED_COMPONENT : REMOVE_FIELD;
if (mainDataKey === 'contentType') {
emitEvent('willDeleteFieldOfContentType');
@ -231,7 +251,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
await updatePermissions();
// Reload the plugin so the cycle is new again
dispatch({ type: 'RELOAD_PLUGIN' });
dispatch({ type: RELOAD_PLUGIN });
// Refetch all the data
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
// 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...
dispatch({ type: 'DELETE_NOT_SAVED_TYPE' });
dispatch({ type: DELETE_NOT_SAVED_TYPE });
return;
}
@ -278,7 +298,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
await request(requestURL, { method: 'DELETE' }, true);
// Reload the plugin so the cycle is new again
dispatch({ type: 'RELOAD_PLUGIN' });
dispatch({ type: RELOAD_PLUGIN });
// Refetch the permissions
await updatePermissions();
@ -315,7 +335,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
await updatePermissions();
// Reload the plugin so the cycle is new again
dispatch({ type: 'RELOAD_PLUGIN' });
dispatch({ type: RELOAD_PLUGIN });
// Refetch all the data
getDataRef.current();
} catch (err) {
@ -356,7 +376,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
const removeComponentFromDynamicZone = (dzName, componentToRemoveIndex) => {
dispatch({
type: 'REMOVE_COMPONENT_FROM_DYNAMIC_ZONE',
type: REMOVE_COMPONENT_FROM_DYNAMIC_ZONE,
dzName,
componentToRemoveIndex,
});
@ -387,7 +407,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
size(get(schemaToSet, 'schema.attributes', {})) === 0;
dispatch({
type: 'SET_MODIFIED_DATA',
type: SET_MODIFIED_DATA,
schemaToSet: dataShape,
hasJustCreatedSchema,
});
@ -466,7 +486,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
}
// Reload the plugin so the cycle is new again
dispatch({ type: 'RELOAD_PLUGIN' });
dispatch({ type: RELOAD_PLUGIN });
// Refetch all the data
getDataRef.current();
} catch (err) {
@ -502,7 +522,7 @@ const DataManagerProvider = ({ allIcons, children }) => {
const updateSchema = (data, schemaType, componentUID) => {
dispatch({
type: 'UPDATE_SCHEMA',
type: UPDATE_SCHEMA,
data,
schemaType,
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 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: {},
contentTypes: {},
initialComponents: {},
@ -13,7 +31,7 @@ const initialState = fromJS({
reservedNames: {},
isLoading: true,
isLoadingForDataToBeSet: true,
});
};
const ONE_SIDE_RELATIONS = ['oneWay', 'manyWay'];
@ -65,9 +83,11 @@ const addComponentsToState = (state, componentToAddUid, objToUpdate) => {
return newObj;
};
const reducer = (state, action) => {
const reducer = (jsonState = initialState, action) => {
const state = fromJS(jsonState);
switch (action.type) {
case 'ADD_ATTRIBUTE': {
case ADD_ATTRIBUTE: {
const {
attributeToSet: { name, ...rest },
forTarget,
@ -122,24 +142,28 @@ const reducer = (state, action) => {
}
return existingCompos;
});
})
.toJS();
}
case 'ADD_CREATED_COMPONENT_TO_DYNAMIC_ZONE': {
case ADD_CREATED_COMPONENT_TO_DYNAMIC_ZONE: {
const { dynamicZoneTarget, componentsToAdd } = action;
return state.updateIn(
['modifiedData', 'contentType', 'schema', 'attributes', dynamicZoneTarget, 'components'],
list => {
return list.concat(componentsToAdd);
}
);
return state
.updateIn(
['modifiedData', 'contentType', 'schema', 'attributes', dynamicZoneTarget, 'components'],
list => {
return list.concat(componentsToAdd);
}
)
.toJS();
}
case 'CANCEL_CHANGES': {
case CANCEL_CHANGES: {
return state
.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;
return state
@ -155,10 +179,11 @@ const reducer = (state, action) => {
}, old);
return componentsSchema;
});
})
.toJS();
}
case 'CREATE_SCHEMA': {
case CREATE_SCHEMA: {
const newSchema = {
uid: action.uid,
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 = {
uid: action.uid,
isTemporary: true,
@ -184,18 +209,20 @@ const reducer = (state, action) => {
if (action.shouldAddComponentToData) {
return state
.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
return state
.update('contentTypes', () => state.get('initialContentTypes'))
.update('components', () => state.get('initialComponents'));
.update('components', () => state.get('initialComponents'))
.toJS();
}
case 'EDIT_ATTRIBUTE': {
case EDIT_ATTRIBUTE: {
const {
attributeToSet: { name, ...rest },
forTarget,
@ -209,132 +236,134 @@ const reducer = (state, action) => {
? [forTarget]
: [forTarget, targetUid];
return newState.updateIn(['modifiedData', ...pathToDataToEdit, 'schema'], obj => {
let oppositeAttributeNameToRemove = null;
let oppositeAttributeNameToUpdate = null;
let oppositeAttributeNameToCreateBecauseOfNatureChange = null;
let oppositeAttributeToCreate = null;
return newState
.updateIn(['modifiedData', ...pathToDataToEdit, 'schema'], obj => {
let oppositeAttributeNameToRemove = null;
let oppositeAttributeNameToUpdate = null;
let oppositeAttributeNameToCreateBecauseOfNatureChange = null;
let oppositeAttributeToCreate = null;
const newObj = OrderedMap(
obj
.get('attributes')
.keySeq()
.reduce((acc, current) => {
const isEditingCurrentAttribute = current === initialAttributeName;
const newObj = OrderedMap(
obj
.get('attributes')
.keySeq()
.reduce((acc, current) => {
const isEditingCurrentAttribute = current === initialAttributeName;
if (isEditingCurrentAttribute) {
const currentUid = state.getIn(['modifiedData', ...pathToDataToEdit, 'uid']);
const isEditingRelation = has(initialAttribute, 'nature');
const didChangeTargetRelation = initialAttribute.target !== rest.target;
const didCreateInternalRelation = rest.target === currentUid;
const nature = rest.nature;
const initialNature = initialAttribute.nature;
const hadInternalRelation = initialAttribute.target === currentUid;
const didChangeRelationNature = initialAttribute.nature !== nature;
const shouldRemoveOppositeAttributeBecauseOfTargetChange =
didChangeTargetRelation &&
!didCreateInternalRelation &&
hadInternalRelation &&
isEditingRelation;
const shouldRemoveOppositeAttributeBecauseOfNatureChange =
didChangeRelationNature &&
hadInternalRelation &&
['oneWay', 'manyWay'].includes(nature) &&
isEditingRelation;
const shouldUpdateOppositeAttributeBecauseOfNatureChange =
!ONE_SIDE_RELATIONS.includes(initialNature) &&
!ONE_SIDE_RELATIONS.includes(nature) &&
hadInternalRelation &&
didCreateInternalRelation &&
isEditingRelation;
const shouldCreateOppositeAttributeBecauseOfNatureChange =
ONE_SIDE_RELATIONS.includes(initialNature) &&
!ONE_SIDE_RELATIONS.includes(nature) &&
hadInternalRelation &&
didCreateInternalRelation &&
isEditingRelation;
const shouldCreateOppositeAttributeBecauseOfTargetChange =
didChangeTargetRelation &&
didCreateInternalRelation &&
!ONE_SIDE_RELATIONS.includes(nature);
if (isEditingCurrentAttribute) {
const currentUid = state.getIn(['modifiedData', ...pathToDataToEdit, 'uid']);
const isEditingRelation = has(initialAttribute, 'nature');
const didChangeTargetRelation = initialAttribute.target !== rest.target;
const didCreateInternalRelation = rest.target === currentUid;
const nature = rest.nature;
const initialNature = initialAttribute.nature;
const hadInternalRelation = initialAttribute.target === currentUid;
const didChangeRelationNature = initialAttribute.nature !== nature;
const shouldRemoveOppositeAttributeBecauseOfTargetChange =
didChangeTargetRelation &&
!didCreateInternalRelation &&
hadInternalRelation &&
isEditingRelation;
const shouldRemoveOppositeAttributeBecauseOfNatureChange =
didChangeRelationNature &&
hadInternalRelation &&
['oneWay', 'manyWay'].includes(nature) &&
isEditingRelation;
const shouldUpdateOppositeAttributeBecauseOfNatureChange =
!ONE_SIDE_RELATIONS.includes(initialNature) &&
!ONE_SIDE_RELATIONS.includes(nature) &&
hadInternalRelation &&
didCreateInternalRelation &&
isEditingRelation;
const shouldCreateOppositeAttributeBecauseOfNatureChange =
ONE_SIDE_RELATIONS.includes(initialNature) &&
!ONE_SIDE_RELATIONS.includes(nature) &&
hadInternalRelation &&
didCreateInternalRelation &&
isEditingRelation;
const shouldCreateOppositeAttributeBecauseOfTargetChange =
didChangeTargetRelation &&
didCreateInternalRelation &&
!ONE_SIDE_RELATIONS.includes(nature);
// 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
// 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
) {
acc[oppositeAttributeNameToCreateBecauseOfNatureChange] = fromJS(
oppositeAttributeToCreate
);
oppositeAttributeNameToUpdate = initialAttribute.targetAttribute;
oppositeAttributeNameToCreateBecauseOfNatureChange = rest.targetAttribute;
oppositeAttributeToCreate = null;
oppositeAttributeNameToCreateBecauseOfNatureChange = null;
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 (
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);
} else if (current === oppositeAttributeNameToUpdate) {
acc[oppositeAttributeNameToCreateBecauseOfNatureChange] = fromJS(
oppositeAttributeToCreate
);
} else {
acc[current] = obj.getIn(['attributes', current]);
}
return acc;
}, {})
);
return acc;
}, {})
);
let updatedObj;
let updatedObj;
// Remove the opposite attribute
if (oppositeAttributeNameToRemove !== null) {
updatedObj = newObj.remove(oppositeAttributeNameToRemove);
} else {
updatedObj = newObj;
}
// Remove the opposite attribute
if (oppositeAttributeNameToRemove !== null) {
updatedObj = newObj.remove(oppositeAttributeNameToRemove);
} else {
updatedObj = newObj;
}
return obj.set('attributes', updatedObj);
});
return obj.set('attributes', updatedObj);
})
.toJS();
}
case 'GET_DATA_SUCCEEDED': {
case GET_DATA_SUCCEEDED: {
return state
.update('components', () => fromJS(action.components))
.update('initialComponents', () => fromJS(action.components))
@ -342,33 +371,38 @@ const reducer = (state, action) => {
.update('contentTypes', () => fromJS(action.contentTypes))
.update('reservedNames', () => fromJS(action.reservedNames))
.update('isLoading', () => false);
.update('isLoading', () => false)
.toJS();
}
case 'RELOAD_PLUGIN':
case RELOAD_PLUGIN:
return initialState;
case 'REMOVE_FIELD_FROM_DISPLAYED_COMPONENT': {
case REMOVE_FIELD_FROM_DISPLAYED_COMPONENT: {
const { attributeToRemoveName, componentUid } = action;
return state.removeIn([
'modifiedData',
'components',
componentUid,
'schema',
'attributes',
attributeToRemoveName,
]);
return state
.removeIn([
'modifiedData',
'components',
componentUid,
'schema',
'attributes',
attributeToRemoveName,
])
.toJS();
}
case 'REMOVE_COMPONENT_FROM_DYNAMIC_ZONE':
return state.removeIn([
'modifiedData',
'contentType',
'schema',
'attributes',
action.dzName,
'components',
action.componentToRemoveIndex,
]);
case 'REMOVE_FIELD': {
case REMOVE_COMPONENT_FROM_DYNAMIC_ZONE:
return state
.removeIn([
'modifiedData',
'contentType',
'schema',
'attributes',
action.dzName,
'components',
action.componentToRemoveIndex,
])
.toJS();
case REMOVE_FIELD: {
const { mainDataKey, attributeToRemoveName } = action;
const pathToAttributes = ['modifiedData', mainDataKey, 'schema', 'attributes'];
const pathToAttributeToRemove = [...pathToAttributes, attributeToRemoveName];
@ -389,21 +423,25 @@ const reducer = (state, action) => {
if (shouldRemoveOppositeAttribute) {
return state
.removeIn(pathToAttributeToRemove)
.removeIn([...pathToAttributes, targetAttribute]);
.removeIn([...pathToAttributes, targetAttribute])
.toJS();
}
}
return state.removeIn(pathToAttributeToRemove).updateIn([...pathToAttributes], attributes => {
return attributes.keySeq().reduce((acc, current) => {
if (acc.getIn([current, 'targetField']) === attributeToRemoveName) {
return acc.removeIn([current, 'targetField']);
}
return state
.removeIn(pathToAttributeToRemove)
.updateIn([...pathToAttributes], attributes => {
return attributes.keySeq().reduce((acc, current) => {
if (acc.getIn([current, 'targetField']) === attributeToRemoveName) {
return acc.removeIn([current, 'targetField']);
}
return acc;
}, attributes);
});
return acc;
}, attributes);
})
.toJS();
}
case 'SET_MODIFIED_DATA': {
case SET_MODIFIED_DATA: {
let newState = state
.update('isLoadingForDataToBeSet', () => false)
.update('initialData', () => fromJS(action.schemaToSet))
@ -417,9 +455,9 @@ const reducer = (state, action) => {
.update('contentTypes', () => state.get('initialContentTypes'));
}
return newState;
return newState.toJS();
}
case 'UPDATE_SCHEMA': {
case UPDATE_SCHEMA: {
const {
data: { name, collectionName, category, icon, kind },
schemaType,
@ -449,10 +487,10 @@ const reducer = (state, action) => {
});
}
return newState;
return newState.toJS();
}
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);
});

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

View File

@ -1,8 +1,10 @@
import formModalReducer from './containers/FormModal/reducer';
import dataManagerProvider from './containers/DataManagerProvider/reducer';
import pluginId from './pluginId';
const reducers = {
[`${pluginId}_formModal`]: formModalReducer,
[`${pluginId}_dataManagerProvider`]: dataManagerProvider,
};
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"
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:
version "4.0.5"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"