Commit 5665fb60 authored by Florent Chehab's avatar Florent Chehab

Cleaned module wrapper / editor

Removed hacks
Tweaks
And changes coherent with last commit

Group Module broken again !
parent 1bae06b2
......@@ -146,6 +146,7 @@ const mapStateToProps = (state) => {
return {
countries: state.api.countriesAll,
currencies: state.api.currenciesAll,
serverModerationStatus: state.api.serverModerationStatusSpecific,
};
};
......@@ -154,6 +155,7 @@ const mapDispatchToProps = (dispatch) => {
api: {
countries: () => dispatch(getActions("countries").readAll()),
currencies: () => dispatch(getActions("currencies").readAll()),
serverModerationStatus: () => dispatch(getActions("serverModerationStatus").readSpecific("")), // not needed for server moderation status
},
};
};
......
......@@ -17,8 +17,7 @@ import TextField from "./fields/TextField";
import MultiSelectField from "./fields/MultiSelectField";
import NumberField from "./fields/NumberField";
import store from "../../redux/store";
import { getLatestRead } from "../../redux/api/utils";
import { getLatestReadDataFromStore } from "../../redux/api/utils";
export default {
/**
......@@ -33,7 +32,7 @@ export default {
renderObjModerationLevelField() {
// hack to access directly the store and get the value we need.
const userData = getLatestRead(store.getState().api.userDataSpecific).data,
const userData = getLatestReadDataFromStore("userDataSpecific"),
possibleObjModeration = getObjModerationLevel(userData.owner_level, true);
if (possibleObjModeration.length > 1) {
return (
......
......@@ -54,6 +54,17 @@ class Editor extends Component {
*/
extraFieldMappings = []
/**
* Creates an instance of Editor. and subscribes to the module wrapper so that it can access its functions.
*
* @param {object} props
* @memberof Editor
*/
constructor(props) {
super(props);
props.subscribeToModuleWrapper(this);
}
/**
* Function that extracts the modelData from the raw one.
*
......@@ -116,7 +127,8 @@ class Editor extends Component {
handleSaveEditorRequest() {
const getFormError = this.getFormError();
if (!getFormError.status) { // no error, we can save if necessary
if (this.props.forceSave || this.formHasChanges()) {
if (this.formHasChanges()) {
// Copy the model data and copy above the data from the form
// So that we don't forget anything.
this.performSave(
......@@ -183,7 +195,6 @@ class Editor extends Component {
if (this.formHasChanges()) {
this.alertChangesNotSaved();
} else {
this.notifyNoChangesDetected();
this.closeEditor(false);
}
}
......@@ -196,7 +207,7 @@ class Editor extends Component {
*/
closeEditor(somethingWasSaved) {
this.props.closeFullScreenDialog();
this.props.handleEditorWasClosed(somethingWasSaved);
this.props.closeEditorPanel(somethingWasSaved);
}
......@@ -217,16 +228,6 @@ class Editor extends Component {
this.props.openFullScreenDialog(this.renderEditor());
}
// To handle moderation, we use the editor in "hacky" manner
// with the `dataToSave` props, which triggers a save.
const { dataToSave } = this.props;
if (dataToSave) {
this.props.handleEditorWasClosed();
this.performSave(dataToSave);
}
// end of hacky behavior.
// we make to notify if saving to the server has errors
const { savingHasError } = this.props;
if (savingHasError.failed && !this.state.alert.open) {
......@@ -364,17 +365,15 @@ class Editor extends Component {
Editor.propTypes = {
classes: PropTypes.object.isRequired,
savingHasError: PropTypes.object.isRequired,
clearSaveError: PropTypes.func.isRequired,
lastUpdateTimeInModel: PropTypes.string,
lastSaveTime: PropTypes.number,
forceSave: PropTypes.bool.isRequired,
open: PropTypes.bool.isRequired, // should the editor be opened
subscribeToModuleWrapper: PropTypes.func.isRequired,
__apiAttr: PropTypes.oneOf([PropTypes.number, PropTypes.string, ""]),
rawModelData: PropTypes.object.isRequired,
open: PropTypes.bool.isRequired, // should the editor be opened
handleEditorWasClosed: PropTypes.func.isRequired,
dataToSave: PropTypes.object,
closeEditorPanel: PropTypes.func.isRequired,
// props added in subclasses but are absolutely required to handle redux
savingHasError: PropTypes.object.isRequired,
clearSaveError: PropTypes.func.isRequired,
lastUpdateTimeInModel: PropTypes.string,
openFullScreenDialog: PropTypes.func.isRequired,
closeFullScreenDialog: PropTypes.func.isRequired,
saveData: PropTypes.func.isRequired,
......@@ -385,10 +384,9 @@ Editor.propTypes = {
Editor.defaultProps = {
open: false,
forceSave: false,
__apiAttr: "",
// eslint-disable-next-line no-console
handleEditorWasClosed: () => console.error("Dev forgot something...")
closeEditorPanel: () => console.error("Dev forgot something...")
};
export default Editor;
......@@ -62,8 +62,7 @@ class History extends Component {
return [];
} else {
return this.props.versions.readSucceeded.data
.map((el) => { return el.data; }) // get the data inside the version
.slice(1); // the first (ie 0) version is the one already displayed
.map((el) => { return el.data; }); // get the data inside the version
}
}
......@@ -100,21 +99,28 @@ class History extends Component {
&& lastVersion.id == id;
}
/**
* As soon as the History is added to the DOM, we make sure to update it,
* To force the call to `componentDidUpdate`.
*
* @memberof Editor
*/
componentDidMount() {
this.forceUpdate();
}
/**
* Extends function to open panel in redux and load Data
*
* @memberof History
*/
componentDidUpdate() {
if (this.props.open) {
// already open the panel
this.props.openFullScreenDialog(this.renderPanel());
// Load the data as necessary
if (!this.dataIsReady() && !this.props.versions.isLoading) {
const { contentTypeId, id } = this.getContentTypeAndId();
this.props.readVersions(contentTypeId, id);
}
// Load the data as necessary
if (!this.dataIsReady() && !this.props.versions.isLoading) {
const { contentTypeId, id } = this.getContentTypeAndId();
this.props.readVersions(contentTypeId, id);
}
this.props.openFullScreenDialog(this.renderPanel());
}
/**
......@@ -230,7 +236,7 @@ class History extends Component {
variant='outlined'
color="primary"
className={this.props.classes.editButton}
onClick={() => this.props.handleEditFromVersion(rawModelData)}
onClick={() => this.props.editFromVersion(rawModelData)}
>
Éditer à partir de cette version
</Button>
......@@ -247,11 +253,10 @@ class History extends Component {
History.propTypes = {
classes: PropTypes.object.isRequired,
handleEditFromVersion: PropTypes.func.isRequired,
editFromVersion: PropTypes.func.isRequired,
readVersions: PropTypes.func.isRequired,
closeHistoryPanel: PropTypes.func.isRequired,
resetVersions: PropTypes.func.isRequired,
open: PropTypes.bool.isRequired,
versions: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
openFullScreenDialog: PropTypes.func.isRequired,
......@@ -261,7 +266,6 @@ History.propTypes = {
};
History.defaultProps = {
open: false,
// eslint-disable-next-line no-console
closeHistoryPanel: () => console.error("Dev forgot something..."),
};
......
import React from "react";
import React, { Component } 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 { getLatestReadDataFromStore } from "../../../../redux/api/utils";
import Typography from "@material-ui/core/Typography";
import Paper from "@material-ui/core/Paper";
......@@ -14,10 +14,8 @@ import Divider from "@material-ui/core/Divider";
import AddIcon from "@material-ui/icons/Add";
import getActions from "../../../../redux/api/getActions";
import green from "@material-ui/core/colors/green";
import CustomComponentForAPI from "../../../common/CustomComponentForAPI";
const styles = theme => ({
root: {
......@@ -47,7 +45,7 @@ const styles = theme => ({
* @extends {CustomComponentForAPI}
* @extends React.Component
*/
class ModuleGroupWrapper extends CustomComponentForAPI {
class ModuleGroupWrapper extends Component {
state = {
editorOpen: false,
};
......@@ -56,25 +54,24 @@ class ModuleGroupWrapper extends CustomComponentForAPI {
this.setState({ editorOpen: true });
}
handleEditorWasClosed = (somethingWasSaved = false) => {
closeEditorPanel = (somethingWasSaved = false) => {
this.setState({ editorOpen: false });
if (somethingWasSaved) {
this.props.invalidateGroup();
}
};
customRender() {
render() {
const { classes, groupTitle, endPoint } = this.props,
userCanPostTo = this.getReadData("userData").owner_can_post_to,
userCanPostTo = getLatestReadDataFromStore("userDataSpecific").owner_can_post_to,
disabled = userCanPostTo.indexOf(endPoint) < 0;
return <></>; // TODO NOW remove
return (
<Paper className={classes.root}>
<this.props.editor
{...this.props.propsForEditor}
open={this.state.editorOpen}
handleEditorWasClosed={this.handleEditorWasClosed}
userData={this.props.userData}
closeEditorPanel={this.closeEditorPanel}
/>
<Grid container spacing={8} alignItems='center'>
......@@ -114,26 +111,10 @@ ModuleGroupWrapper.propTypes = {
classes: PropTypes.object.isRequired,
propsForEditor: PropTypes.object.isRequired,
children: PropTypes.node.isRequired,
userData: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => {
return {
userData: state.api.userDataSpecific
};
};
const mapDispatchToProps = (dispatch) => {
return {
api: {
userData: () => dispatch(getActions("userData").readSpecific("")), // Id not required for user data
}
};
};
export default compose(
withStyles(styles, { withTheme: true }),
connect(mapStateToProps, mapDispatchToProps)
)(ModuleGroupWrapper);
......@@ -36,15 +36,22 @@ const styles = theme => ({
*/
class PendingModeration extends Component {
/**
* Force update to force call of componentDidUpdate en mount and force the rendering of the moderation panel.
*
* @memberof PendingModeration
*/
componentDidMount(){
this.forceUpdate();
}
/**
* Customization to handle opening the app full screen dialog when needed.
*
* @memberof PendingModeration
*/
componentDidUpdate(prevProps) {
if (this.props.open && !prevProps.open) { // a bit of rendering optimization
this.props.openFullScreenDialog(this.renderPendingModerationPanel());
}
componentDidUpdate() {
this.props.openFullScreenDialog(this.renderPendingModerationPanel());
}
/**
......@@ -75,7 +82,7 @@ class PendingModeration extends Component {
variant='outlined'
color="primary"
className={this.props.classes.editButton}
onClick={() => this.props.handleEditPendingModeration(rawModelData)}
onClick={() => this.props.editFromPendingModeration(rawModelData)}
>
Éditer à partir de cette version
</Button>
......@@ -87,7 +94,7 @@ class PendingModeration extends Component {
color="primary"
disabled={!this.props.userCanModerate}
className={this.props.classes.editButton}
onClick={() => this.props.handleApproveModeration(rawModelData)}
onClick={() => this.props.moderatePendingModeration(rawModelData)}
>
Valider cette version
</Button>
......@@ -103,8 +110,8 @@ class PendingModeration extends Component {
* @memberof PendingModeration
*/
closePanel() {
this.props.handleClosePendingModeration();
this.props.closeFullScreenDialog();
this.props.closePendingModerationPanel();
}
/**
......@@ -146,10 +153,9 @@ class PendingModeration extends Component {
PendingModeration.propTypes = {
classes: PropTypes.object.isRequired,
handleEditPendingModeration: PropTypes.func.isRequired,
handleClosePendingModeration: PropTypes.func.isRequired,
handleApproveModeration: PropTypes.func.isRequired,
open: PropTypes.bool.isRequired,
editFromPendingModeration: PropTypes.func.isRequired,
closePendingModerationPanel: PropTypes.func.isRequired,
moderatePendingModeration: PropTypes.func.isRequired,
userCanModerate: PropTypes.bool.isRequired,
openFullScreenDialog: PropTypes.func.isRequired,
closeFullScreenDialog: PropTypes.func.isRequired,
......@@ -157,9 +163,8 @@ PendingModeration.propTypes = {
};
PendingModeration.defaultProps = {
open: false,
// eslint-disable-next-line no-console
handleClosePendingModeration: () => console.error("Dev forgot something...")
closePendingModerationPanel: () => console.error("Dev forgot something...")
};
const mapDispatchToProps = (dispatch) => {
......
......@@ -18,7 +18,7 @@ import Tooltip from "@material-ui/core/Tooltip";
export default function renderFirstRow(userCanModerate) {
const { classes, theme, rawModelData } = this.props,
nbVersions = Math.max(0, rawModelData.nb_versions - 1),
nbVersions = Math.max(0, rawModelData.nb_versions),
{ pending_moderation } = rawModelData;
let nbPendingModeration = 0;
......@@ -42,7 +42,7 @@ export default function renderFirstRow(userCanModerate) {
<Tooltip title={moderTooltip} placement="top">
<div style={{ display: "inline-block" }}> {/* Needed to fire events for the tooltip when below is disabled! when below is disabled!! */}
<MyBadge className={classes.badge} badgeContent={nbPendingModeration} color="secondary">
<IconButton aria-label="Modération" disabled={moderClass == "disabled" || moderClass == "green"} onClick={this.handleOpenPendingModeration} className={classes.button}>
<IconButton aria-label="Modération" disabled={moderClass == "disabled" || moderClass == "green"} onClick={() => this.handleOpenPendingModeration()} className={classes.button}>
<VerifiedUserIcon className={classes[moderClass]} />
</IconButton>
</MyBadge>
......@@ -51,7 +51,7 @@ export default function renderFirstRow(userCanModerate) {
<Tooltip title={editTooltip} placement="top">
<div style={{ display: "inline-block" }}> {/* Needed to fire events for the tooltip when below is disabled!! */}
<IconButton aria-label="Éditer" className={classes.button} disabled={editClass == "disabled"} onClick={this.handleEditCurrent}>
<IconButton aria-label="Éditer" className={classes.button} disabled={editClass == "disabled"} onClick={() => this.editCurrent()}>
<CreateIcon className={classes[editClass]} />
</IconButton>
</div>
......@@ -60,7 +60,7 @@ export default function renderFirstRow(userCanModerate) {
<Tooltip title={versionTooltip} placement="top">
<div style={{ display: "inline-block" }}> {/* Needed to fire events for the tooltip when below is disabled!! */}
<MyBadge className={classes.badge} badgeContent={nbVersions} color="secondary">
<IconButton aria-label="Restorer" disabled={versionClass == "disabled"} className={classes.button} onClick={this.handleOpenHistory}>
<IconButton aria-label="Restorer" disabled={versionClass == "disabled"} className={classes.button} onClick={() => this.setState({ historyOpen: true })}>
<SettingsBackRestoreIcon className={classes[versionClass]} />
</IconButton>
</MyBadge>
......
......@@ -10,15 +10,6 @@ import CrudReducers from "./CrudReducers";
let apiActionsTmp = {};
let apiReducersTmp = {};
// Simple APIs outside rest framework
// TODO merge with the shared file
const otherAPI = [
{
name: "serverModerationStatus",
api_end_point: "serverModerationStatus"
}
];
/**
* Create api actions and reducers for given the info in each arr obj
* Add those to apiActions and apiReducers objects
......@@ -48,7 +39,6 @@ function addAPIs(arr) {
});
}
addAPIs(otherAPI);
addAPIs(API_CONFIG);
export const apiActions = apiActionsTmp;
......
/**
* This file contains utilities linked to the use of the API.
*/
import store from "../store";
// Stores the name of the reducers/actions that result in read data
export const successActionsWithReads = ["readSucceeded", "createSucceeded", "updateSucceeded"];
......@@ -19,3 +20,18 @@ export function getLatestRead(stateExtract) {
(prev, curr) => prev.readAt < curr.readAt ? curr : prev,
{ readAt: 0 });
}
/**
* Function that returns the latest read data directly from the store.
* To be used with parsimony.
*
* It assumes that the data has already been fetched at some point.
*
* @export
* @param {string} entry eg: userDataSpecific
* @returns
*/
export function getLatestReadDataFromStore(entry){
return getLatestRead(store.getState().api[entry]).data;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment