Commit d3681935 authored by Florent Chehab's avatar Florent Chehab

refactor(smart actions parameters) : BREAKING & tweaks

* Created `RequestedParams` class with builder to create request parameters object in a standard way,
* All the generic actions only take an instance of this object now,
* All dynamic parametrization of the request params now happens in `apiParams`
* This enables an ultra smart magic piece of logic to auto refresh the data from the server if the props / state of the component has changed since the last request. (the requestParams object are now stored in the redux store and we can compare theme 😄 ). And also not to make duplicate queries.
* Updated doc accordingly,

Tweaks/fixes:
* use of `lodash/isEqual` to deep compare objects
* Removed now useless behiavor
parent 6b4719a2
Pipeline #38664 passed with stages
in 3 minutes and 16 seconds
......@@ -222,23 +222,24 @@ const mapDispatchToProps = (dispatch) => {
The `getActions` function will give you access to all of the following functions (which are defined in the `CrudActions` class -- `frontend/src/redux/api/CrudActions.js`):
- `readAll(params={})`
- `readOne(id, params={})`
- `create(data, onSuccessCallback = (newData) => { }, params = {})`
- `update(id, data, onSuccessCallback = (newData) => { }, params = {})`
- `delete(id, onSuccessCallback = () => { }, params = {})`
- `readAll(params=RequestParams.Builder.build())`
- `readOne(params)`
- `create(params)`
- `update(params)`
- `delete(params)`
?> `params` **must be an instance of `RequestParams`**, which is a helper class defined in the project. This class comes with a handy `Builder` static class (say hello to the Builder design pattern) to help you parameterize your requests. Here is a quick summary of the functions provided by the builder (all are *optional*):
?> `params` is an object that accepts two keys: `queryParams` and `endPointAttr`. In most cases it should be created through the `getQueryParams` and `getEndPointAttr` (and `mapDispatchToProps`; see just before [this section](Application/Frontend/redux?id=words-on-updating-data-on-the-api)) functions of your components.
* `withId(id)`: specify the id that will be added to the url (eg: `withId(1)` => `/endpoint/1`)
* `withData(data)`: specify the payload that will go with request (useful for creating/updating models instances)
* `withQueryParam(key, value)`: add a *query param* to the request object (eg: `withQueryParam("currency", "CHF")` will result in the request `/endpoint?currency=CHF`); you can chain multiple `withQueryParam`.
* `withEndPointAttrs(endPointAttrs)`: `endPointAttrs` should be an array of the endpoint attributes to add to the endpoint (`withEndPointAttrs([10, 11])` will render as `/endpoint/10/11/`).
* ` withOnSuccessCallback(callback)`: register a callback that will be called when the request is successful. *The data returned by the server will be passed as parameter to this callback.*
* `build()`: to conclude the building process :smile:
> * `queryParams` should be an object that maps the fields to the values you want to filter on (`queryParams = {university: 1, country: 2}` will render as `/api/endpoint?university=1&country=2`).
> * `endPointAttr` should be an array of the endpoint attributes to add to the endpoint (`endPointAttr = [10, 11]` will render as `/api/endpoint/10/11/`)
?> :information_desk_person: All those functions can be chained.
?> `id` should be the id of the object your want to read, update or delete.
?> `data` would be the expected content of the model instance
?> `onSuccessCallback` is a callback called if the action is successful. The returned data from the API is given to it as a parameter. **It is useful to ease some interactions inside your components: you can boycott redux in some way.**
!> Never forget the `.build()` at the end of your chain.
You also have actions to clear the failures if you need:
......@@ -257,7 +258,7 @@ And actions related to invalidating the data:
!> Not all actions might be performed on any given endpoint: your request my get rejected by the backend depending on the viewset's `permission_classes`, the object, the user who sent the request, etc.
?> :information_desk_person: invalidating data will usually trigger a refresh of that data with a new API call. This behavior is implementer in `CustomComponentForApi`
?> :information_desk_person: invalidating data will usually trigger a refresh of that data with a new API call. This behavior is implemented in `CustomComponentForApi`
?> Do you recall that an update to the redux store will be propagated to the components that imported that portion of the store (concerned by the update). So they will be updated on their own every time new data comes in, like magic! :confetti_ball:
......@@ -305,9 +306,36 @@ const mapDispatchToProps = (dispatch) => {
!> **The function that will be used to dispatch the action associated with fetching the data for `propName` must be identified by `api.propName` in the `mapDispatchToProps` function.**
<div id="customGettersApi"></div>
#### Dynamic parametrization of the requests
Often, you will need to access data on the API based on a value stored in the `props` of your components. As a result you need to generate a new `RequestParams` object on demand.
:warning: To do so, in your component you should define he **`apiParams`** property like follow:
```js
apiParams = {
countryDri: ({props, state}) => RequestParams.Builder.withQueryParam("countries", props.countryId).build()
};
```
`apiParams` is an object with keys the `propName` (we have been talking about before) and value a function that accepts an object and that **must return** a `RequestParams` instance.
Then, automatically and when needed, a new `RequestParams` will be built by the internal functions of `CustomComponentForApi`; as you can see this *mapper* function takes as an argument an object with two keys that corresponds to the current `state` and `props` of the object.
When using `apiParams`, your `mapDispatchToProps` should look like this:
```js
const mapDispatchToProps = (dispatch) => {
return {
api: {
countryDri: (params) => dispatch(getActions("countryDri").readAll(params)),
},
};
};
```
Your function must take one argument (`params`) (that will be automatically built) and pass it down to the action like a breeze.
?> :information_desk_person: All the subfunctions you define in `mapDispatchToProps` under the `api` key will be given one object parameter you may want to use. It is composed as follow: `{props, params}`. `props` will be an object corresponding to the `props` of the *connected* component. `params` is built with the `getQueryParams` and `getEndPointAttr` you may wish to override in your connected components. **`params` shall be *passed on* for filtering to work correctly**.
?> :information_desk_person: For all "dynamic" attributes (defined inside `apiParams`) some awesome magical behaviors will be automatically inherited, such as the fact that a new request will be made to the server if the parametrization has changed (e.g. if a `prop` has changed) without needing to detect it yourself (only on ). :tada: This behavior is only present on `ComponentDidMount` react hook. If you want to activate it on `ComponentDidUpdate`, you should set `enableSmartDataRefreshOnComponentDidUpdate = true` as a component property.
That's all :confetti_ball:.
......
......@@ -9,6 +9,7 @@ import CustomComponentForAPI from "./CustomComponentForAPI";
import "typeface-roboto";
import getActions from "../../redux/api/getActions";
import {RequestParams} from "../../redux/api/RequestParams";
const siteSettings = {
typography: {
......@@ -76,7 +77,7 @@ const mapDispatchToProps = (dispatch) => {
api: {
// __AppUserId is defined in the html rendered by django
// eslint-disable-next-line no-undef
userData: () => dispatch(getActions("userData").readOne(__AppUserId)),
userData: () => dispatch(getActions("userData").readOne(RequestParams.Builder.withId(__AppUserId).build())),
},
};
};
......
import getActions from "../../redux/api/getActions";
import { openFullScreenDialog, closeFullScreenDialog } from "../../redux/actions/fullScreenDialog";
import {RequestParams} from "../../redux/api/RequestParams";
/**
* Function to create the mapDispatchToProps function for editor in a "generic way"
......@@ -13,13 +14,22 @@ export default function getMapDispatchToPropsForEditor(name) {
let lastSave = "update";
return {
// eslint-disable-next-line no-unused-vars
saveData: (data, onSuccessCallback = (newData) => { }) => {
saveData: (data, onSuccessCallback = new Function()) => {
if ("id" in data) { // it's an update
lastSave = "update";
dispatch(getActions(name).update(data.id, data, onSuccessCallback));
const params = RequestParams.Builder
.withId(data.id)
.withData(data)
.withOnSuccessCallback(onSuccessCallback)
.build();
dispatch(getActions(name).update(params));
} else { // it's a create
lastSave = "create";
dispatch(getActions(name).create(data, onSuccessCallback));
const params = RequestParams.Builder
.withData(data)
.withOnSuccessCallback(onSuccessCallback)
.build();
dispatch(getActions(name).create(params));
}
},
openFullScreenDialog: (innerNodes) => dispatch(openFullScreenDialog(innerNodes)),
......
import {Component} from "react";
import PropTypes from "prop-types";
import areSameObjects from "../../utils/areSameObjects";
import isEqual from "lodash/isEqual";
import renderFieldsMixIn from "./renderFieldsMixIn";
import CustomError from "../common/CustomError";
......@@ -58,7 +58,8 @@ class Form extends Component {
* with resetting it for some reason
* @type {object.<string, Field>}
*/
fields = Object();
fields = {};
/**
* Array containing the possible form level errors
* @abstract
......@@ -142,7 +143,7 @@ class Form extends Component {
// we need to compare objects (ie JSON objects) differently
if (typeof cmp1 === "object") {
return !areSameObjects(cmp1, cmp2);
return !isEqual(cmp1, cmp2);
} else {
return cmp1 !== cmp2;
}
......
......@@ -22,7 +22,7 @@ class MultiSelectField extends Field {
constructor(props) {
super(props);
this.optionsByValue = Object();
this.optionsByValue = {};
props.options.map((opt) => this.optionsByValue[opt.value] = opt.label);
}
......
......@@ -16,7 +16,6 @@ import {Redirect} from "react-router-dom";
import getActions from "../../redux/api/getActions";
import {saveUniversityBeingViewed} from "../../redux/actions/universityPage";
import compose from "recompose/compose";
import {withStyles} from "@material-ui/core";
import {withErrorBoundary} from "../common/ErrorBoundary";
......@@ -24,6 +23,7 @@ import {APP_ROUTES} from "../../config/appRoutes";
import CustomNavLink from "../common/CustomNavLink";
import CustomLink from "../common/CustomLink";
let previousUnivId = -1;
/**
* Component holding the page with the university details
......@@ -34,23 +34,22 @@ import CustomLink from "../common/CustomLink";
*/
class PageUniversity extends CustomComponentForAPI {
componentDidUpdate(prevProps, prevState, snapshot) {
super.componentDidUpdate(prevProps, prevState, snapshot);
if (this.props.universities.readSucceeded.readAt) {
// we have the university data
const universities = this.getLatestReadData("universities"),
{match, universityBeingViewed} = this.props,
requestedUniversity = match.params.univId;
componentDidMount() {
super.componentDidMount();
const requestedUnivId = this.getUnivIdFromProps();
if (requestedUniversity != "undefined"
&& universities.find(univ => univ.id == requestedUniversity)
&& requestedUniversity != universityBeingViewed) {
this.props.saveUniversityInView(requestedUniversity);
}
if (requestedUnivId && requestedUnivId !== "previousOne") {
previousUnivId = requestedUnivId;
}
}
componentDidUpdate(prevProps, prevState, snapshot) {
super.componentDidUpdate(prevProps, prevState, snapshot);
}
getUnivIdFromProps() {
return this.props.match.params.univId;
}
renderUniversityNotFound() {
return (
......@@ -82,7 +81,8 @@ class PageUniversity extends CustomComponentForAPI {
return (
<Paper className={this.props.classes.paper}>
<Typography>
C'est la première fois que vous consulter cet onglet. Nous vous invitons à <CustomLink to={APP_ROUTES.map}>parcourir les universités</CustomLink> dans un
C'est la première fois que vous consulter cet onglet. Nous vous invitons à <CustomLink to={APP_ROUTES.map}>parcourir
les universités</CustomLink> dans un
premier temps. 😁
</Typography>
</Paper>
......@@ -99,14 +99,14 @@ class PageUniversity extends CustomComponentForAPI {
}
customRender() {
const {match, universityBeingViewed} = this.props,
const {match} = this.props,
requestedUnivId = match.params.univId,
tabName = match.params.tabName,
universities = this.getLatestReadData("universities");
if (requestedUnivId === "previousOne" || typeof requestedUnivId === "undefined") {
if (universityBeingViewed != null) {
return this.renderUniversityUndefinedButHavePrevious(universityBeingViewed);
if (previousUnivId !== -1) {
return this.renderUniversityUndefinedButHavePrevious(previousUnivId);
} else {
return this.renderFirstTimeHere();
}
......@@ -121,17 +121,14 @@ class PageUniversity extends CustomComponentForAPI {
}
PageUniversity.propTypes = {
universityBeingViewed: PropTypes.string,
universities: PropTypes.object.isRequired,
match: PropTypes.object.isRequired,
saveUniversityInView: PropTypes.func.isRequired,
classes: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => {
return {
universities: state.api.universitiesAll,
universityBeingViewed: state.app.universityBeingViewed
};
};
......@@ -140,7 +137,6 @@ const mapDispatchToProps = (dispatch) => {
api: {
universities: () => dispatch(getActions("universities").readAll()),
},
saveUniversityInView: (univId) => dispatch(saveUniversityBeingViewed(univId))
};
};
......
......@@ -26,11 +26,12 @@ import ExpansionPanel from "@material-ui/core/ExpansionPanel/index";
import ExpansionPanelSummary from "@material-ui/core/ExpansionPanelSummary/index";
import ExpansionPanelDetails from "@material-ui/core/ExpansionPanelDetails/index";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import areSameObjects from "../../../utils/areSameObjects";
import isEqual from "lodash/isEqual";
import defaultSiteTheme from "../../../config/defaultTheme.json";
import Divider from "@material-ui/core/Divider/index";
import TextLink from "../../common/TextLink";
import {deepCopy} from "../../../utils/deepCopy";
import {RequestParams} from "../../../redux/api/RequestParams";
const isRgb = string => /#?([0-9a-f]{6})/i.test(string);
......@@ -254,7 +255,7 @@ class ColorTool extends CustomComponentForAPI {
customRender() {
const {classes} = this.props,
themeForDemo = getTheme(this.state.theme),
hasChanges = !areSameObjects(this.state.theme, this.getInitialTheme()),
hasChanges = !isEqual(this.state.theme, this.getInitialTheme()),
darkModeActivated = this.state.theme.mode === "dark";
return (
......@@ -310,11 +311,11 @@ const mapStateToProps = (state) => {
const mapDispatchToProps = (dispatch) => {
return {
// eslint-disable-next-line no-undef
saveUserDataToServer: (data) => dispatch(getActions("userData").update(__AppUserId, data)),
saveUserDataToServer: (data) => dispatch(getActions("userData").update(RequestParams.Builder.withId(__AppUserId).withData(data).build())),
api: {
// __AppUserId is defined in the html rendered by django
// eslint-disable-next-line no-undef
userData: () => dispatch(getActions("userData").readOne(__AppUserId)), // id not needed userData
userData: () => dispatch(getActions("userData").readOne(RequestParams.Builder.withId(__AppUserId).build())), // id not needed userData
},
};
};
......
......@@ -14,6 +14,7 @@ import ModuleGroupWrapper from "./common/ModuleGroupWrapper";
import CountryDriEditor from "../editors/CountryDriEditor";
import getActions from "../../../redux/api/getActions";
import {withUnivInfo} from "../common/withUnivInfo";
import {RequestParams} from "../../../redux/api/RequestParams";
// eslint-disable-next-line no-unused-vars
......@@ -29,10 +30,9 @@ function renderCore(rawModelData, classes, outsideData) {
}
class CountryDri extends Module {
/**
* @override
*/
getQueryParams = (propName) => propName === "countryDri" ? ({ countries: this.props.countryId }) : undefined;
apiParams = {
countryDri: ({props}) => RequestParams.Builder.withQueryParam("countries", props.countryId).build(),
};
customRender() {
const countryDriItems = this.getOnlyReadData("countryDri"),
......@@ -86,7 +86,7 @@ const mapStateToProps = (state) => {
const mapDispatchToProps = (dispatch) => {
return {
api: {
countryDri: ({ params }) => dispatch(getActions("countryDri").readAll(params)),
countryDri: (params) => dispatch(getActions("countryDri").readAll(params)),
},
invalidateData: () => dispatch(getActions("countryDri").invalidateAll())
};
......
......@@ -16,6 +16,7 @@ import CountryScholarshipEditor from "../editors/CountryScholarshipEditor";
import getActions from "../../../redux/api/getActions";
import {withUnivInfo} from "../common/withUnivInfo";
import {RequestParams} from "../../../redux/api/RequestParams";
// eslint-disable-next-line no-unused-vars
......@@ -43,11 +44,9 @@ function renderCore(rawModelData, classes, outsideData) {
class CountryScholarships extends Module {
/**
* @override
*/
getQueryParams = (propName) => propName === "countryScholarships" ? ({countries: this.props.countryId}) : undefined;
apiParams = {
countryScholarships: ({props}) => RequestParams.Builder.withQueryParam("countries", props.countryId).build(),
};
customRender() {
const countryScholarshipsItems = this.getOnlyReadData("countryScholarships"),
......@@ -111,7 +110,7 @@ const mapStateToProps = (state) => {
const mapDispatchToProps = (dispatch) => {
return {
api: {
countryScholarships: ({params}) => dispatch(getActions("countryScholarships").readAll(params)),
countryScholarships: (params) => dispatch(getActions("countryScholarships").readAll(params)),
},
invalidateData: () => dispatch(getActions("countryScholarships").invalidateAll())
};
......
......@@ -2,7 +2,7 @@ import React from "react";
import PropTypes from "prop-types";
import withStyles from "@material-ui/core/styles/withStyles";
import compose from "recompose/compose";
import { connect } from "react-redux";
import {connect} from "react-redux";
import Markdown from "../../common/Markdown";
......@@ -15,6 +15,7 @@ import UniversityDriEditor from "../editors/UniversityDriEditor";
import getActions from "../../../redux/api/getActions";
import {withUnivInfo} from "../common/withUnivInfo";
import {RequestParams} from "../../../redux/api/RequestParams";
// eslint-disable-next-line no-unused-vars
const styles = theme => ({
......@@ -22,8 +23,7 @@ const styles = theme => ({
// eslint-disable-next-line no-unused-vars
function renderCore(rawModelData, classes, outsideData) {
const univDri = rawModelData;
const { comment } = univDri;
const { comment } = rawModelData;
return (
<Markdown source={comment} />
......@@ -32,11 +32,9 @@ function renderCore(rawModelData, classes, outsideData) {
class UniversityDri extends Module {
/**
* @override
*/
getQueryParams = (propName) => propName === "universityDri" ? ({ universities: this.props.univId }) : undefined;
apiParams = {
universityDri: ({props}) => RequestParams.Builder.withQueryParam("universities", props.univId).build(),
};
customRender() {
const { universities, classes } = this.props,
......@@ -90,7 +88,7 @@ const mapStateToProps = (state) => {
const mapDispatchToProps = (dispatch) => {
return {
api: {
universityDri: ({ params }) => dispatch(getActions("universityDri").readAll(params)),
universityDri: (params) => dispatch(getActions("universityDri").readAll(params)),
},
invalidateData: () => dispatch(getActions("universityDri").invalidateAll())
};
......
......@@ -18,6 +18,7 @@ import UniversityGeneralEditor from "../editors/UniversityGeneralEditor";
import getActions from "../../../redux/api/getActions";
import {withUnivInfo} from "../common/withUnivInfo";
import {RequestParams} from "../../../redux/api/RequestParams";
// eslint-disable-next-line no-unused-vars
const styles = theme => ({
......@@ -58,6 +59,10 @@ function renderCore(rawModelData, classes, outsideData) {
class UniversityGeneral extends Module {
apiParams = {
university: ({props}) => RequestParams.Builder.withId(props.univId).build(),
};
customRender() {
const univInfos = this.getLatestReadData("university");
const { classes } = this.props;
......@@ -96,7 +101,7 @@ const mapStateToProps = (state) => {
const mapDispatchToProps = (dispatch) => {
return {
api: {
university: ({ props }) => dispatch(getActions("universities").readOne(props.univId)),
university: (params) => dispatch(getActions("universities").readOne(params)),
},
invalidateData: () => dispatch(getActions("universities").invalidateOne())
};
......
......@@ -13,6 +13,7 @@ import Scholarship from "./common/Scholarship";
import UniversityScholarshipEditor from "../editors/UniversityScholarshipEditor";
import getActions from "../../../redux/api/getActions";
import {withUnivInfo} from "../common/withUnivInfo";
import {RequestParams} from "../../../redux/api/RequestParams";
// eslint-disable-next-line no-unused-vars
const styles = theme => ({
......@@ -40,10 +41,9 @@ function renderCore(rawModelData, classes, outsideData) {
class UniversityScholarships extends Module {
/**
* @override
*/
getQueryParams = (propName) => propName === "universityScholarships" ? ({ universities: this.props.univId, }) : undefined;
apiParams = {
universityScholarships: ({props}) => RequestParams.Builder.withQueryParam("universities", props.univId).build(),
};
customRender() {
const univScholarshipsItems = this.getOnlyReadData("universityScholarships"),
......@@ -101,7 +101,7 @@ const mapStateToProps = (state) => {
const mapDispatchToProps = (dispatch) => {
return {
api: {
universityScholarships: ({ params }) => dispatch(getActions("universityScholarships").readAll(params)),
universityScholarships: (params) => dispatch(getActions("universityScholarships").readAll(params)),
},
invalidateData: () => dispatch(getActions("universityScholarships").invalidateAll())
};
......
......@@ -2,7 +2,7 @@ import React from "react";
import PropTypes from "prop-types";
import withStyles from "@material-ui/core/styles/withStyles";
import compose from "recompose/compose";
import { connect } from "react-redux";
import {connect} from "react-redux";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
......@@ -24,6 +24,7 @@ import UniversitySemestersDatesEditor from "../editors/UniversitySemestersDatesE
import getActions from "../../../redux/api/getActions";
import {withUnivInfo} from "../common/withUnivInfo";
import {RequestParams} from "../../../redux/api/RequestParams";
const styles = theme => ({
root: {
......@@ -51,8 +52,7 @@ function convertDateStrToStr(date) {
}
function renderCore(rawModelData, classes) {
const semestersDates = rawModelData;
let { autumn_begin, autumn_end, spring_begin, spring_end, comment } = semestersDates;
let { autumn_begin, autumn_end, spring_begin, spring_end, comment } = rawModelData;
autumn_begin = convertDateStrToStr(autumn_begin);
autumn_end = convertDateStrToStr(autumn_end);
......@@ -104,6 +104,9 @@ function renderCore(rawModelData, classes) {
class UniversitySemestersDates extends Module {
apiParams = {
universitySemestersDates: ({props}) => RequestParams.Builder.withId(props.univId).build(),
};
customRender() {
const semestersDates = this.getLatestReadData("universitySemestersDates");
......@@ -137,7 +140,7 @@ const mapStateToProps = (state) => {
const mapDispatchToProps = (dispatch) => {
return {
api: {
universitySemestersDates: ({ props }) => dispatch(getActions("universitiesSemestersDates").readOne(props.univId)),
universitySemestersDates: (params) => dispatch(getActions("universitiesSemestersDates").readOne(params)),
},
invalidateData: () => dispatch(getActions("universitiesSemestersDates").invalidateOne())
};
......
......@@ -25,6 +25,7 @@ import dateTimeStrToStr from "../../../../utils/dateTimeStrToStr";
import editorStyle from "../../../editor/editorStyle";
import getActions from "../../../../redux/api/getActions";
import {RequestParams} from "../../../../redux/api/RequestParams";
......@@ -38,19 +39,14 @@ class History extends CustomComponentForAPI {
// Store the version that is being viewed
state = {
versionInView: 1
}
};
/**
* @override
*/
getEndPointAttr = (propName) => {
if (propName === "versions") {
const { contentTypeId, id } = this.props.modelInfo;
return [contentTypeId, id];
} else {
return undefined;
}
}
apiParams = {
versions: ({props}) => {
const { contentTypeId, id } = props.modelInfo;
return RequestParams.Builder.withEndPointAttrs([contentTypeId, id]).build();
},
};
/**
* Move to next or previous version with step
......@@ -251,7 +247,7 @@ const mapStateToProps = (state) => {
const mapDispatchToProps = (dispatch) => {
return {
api: {
versions: ({ params }) => dispatch(getActions("versions").readAll(params)),
versions: (params) => dispatch(getActions("versions").readAll(params)),
},
resetVersions: () => dispatch(getActions("versions").invalidateAll()),
openFullScreenDialog: (innerNodes) => dispatch(openFullScreenDialog(innerNodes)),
......
import PropTypes from "prop-types";
import CustomComponentForAPI from "../../../common/CustomComponentForAPI";
/**
* @class Module
* @extends {CustomComponentForAPI}
* @extends React.Component
* @abstract
*/
class Module extends CustomComponentForAPI {
componentWillUnmount() {
this.props.invalidateData();
}
componentDidUpdate(prevProps, prevState, snapshot) {
super.componentDidUpdate(prevProps, prevState, snapshot);
......@@ -20,9 +15,6 @@ class Module extends CustomComponentForAPI {
}
}
Module.propTypes = {
invalidateData: PropTypes.func.isRequired,
};
Module.propTypes = {};
export default Module;
......@@ -18,6 +18,7 @@ import getActions from "../../../../redux/api/getActions";
import editorStyle from "../../../editor/editorStyle";
import Loading from "../../../common/Loading";
import {RequestParams} from "../../../../redux/api/RequestParams";
......@@ -29,17 +30,12 @@ import Loading from "../../../common/Loading";
*/
class PendingModeration extends CustomComponentForAPI {
/**
* @override
*/
getEndPointAttr = (propName) => {
if (propName === "pendingModeration") {
const { contentTypeId, id } = this.props.modelInfo;
return [contentTypeId, id];
} else {
return undefined;
}
}
apiParams = {
pendingModeration: ({props}) => {
const { contentTypeId, id } = props.modelInfo;
return RequestParams.Builder.withEndPointAttrs([contentTypeId, id]).build();
},
};
/**
* Force update to force call of componentDidUpdate en mount and force the rendering of the moderation panel.
......@@ -199,7 +195,7 @@ const mapStateToProps = (state) => {
const mapDispatchToProps = (dispatch) => {
return {
api: {
pendingModeration: ({ params }) => dispatch(getActions("pendingModerationObj").readAll(params)),
pendingModeration: (params) => dispatch(getActions("pendingModerationObj").readAll(params)),
},
resetPendingModeration: () => dispatch(getActions("pendingModerationObj").invalidateAll()),
openFullScreenDialog: (innerNodes) => dispatch(openFullScreenDialog(innerNodes)),
......
......@@ -14,6 +14,7 @@ import {compose} from "recompose";
import UserInfoEditor from "./UserInfoEditor";
import CreateIcon from "@material-ui/icons/Create";
import {classNames} from "../../utils/classNames";
import {RequestParams} from "../../redux/api/RequestParams";
function TypographyWithIcon(props) {
return (
......@@ -38,7 +39,6 @@ TypographyWithIcon.propTypes = {
typographyClass: PropTypes.string.isRequired,
};
let lastUserId = -1;
/**
* Component used to display the user information
......@@ -48,6 +48,13 @@ let lastUserId = -1;
*/
class UserInfo extends CustomComponentForAPI {
apiParams = {
"user": ({props}) =>
RequestParams.Builder
.withId(props.userId)
.build(),
};
state = {editorOpen: false};
openEditorPanel() {
......@@ -58,25 +65,6 @@ class UserInfo extends CustomComponentForAPI {
this.setState({editorOpen: false});
}
check() {
const userId = parseInt(this.props.userId);
if (lastUserId !== userId) {
// we make sure to update the data if the userId changes
lastUserId = userId;
this.props