diff --git a/src/helpers/utils.js b/src/helpers/utils.js index 7e56cda89..4a26306aa 100644 --- a/src/helpers/utils.js +++ b/src/helpers/utils.js @@ -55,4 +55,22 @@ export const compareEmail = (email1, email2, options = { UNIQUE_GMAIL_VALIDATION */ export const compareHandles = (handle1, handle2) => ( (handle1 || '').toLowerCase() === (handle2 || '').toLowerCase() -) \ No newline at end of file +) + +// remove empty object, null/undefined value and empty string recursively from passed obj +const deepClean = obj => _.transform(obj, (result, value, key) => { + const isCollection = _.isObject(value) + const cleaned = isCollection ? deepClean(value) : value + // exclude if empty object, null, undefined or empty string + if ((isCollection && _.isEmpty(cleaned)) || _.isNil(value) || value === '') { + return + } + _.isArray(result) ? result.push(cleaned) : (result[key] = cleaned) +}) + +/** + * Helper method to clean given object from null, undefined or empty property. + * + * @param {Object} obj the object to clean + */ +export const clean = obj => _.isObject(obj) ? deepClean(obj) : obj diff --git a/src/projects/detail/components/EditProjectForm/EditProjectForm.jsx b/src/projects/detail/components/EditProjectForm/EditProjectForm.jsx index 584045f7b..c5b7b2375 100644 --- a/src/projects/detail/components/EditProjectForm/EditProjectForm.jsx +++ b/src/projects/detail/components/EditProjectForm/EditProjectForm.jsx @@ -23,6 +23,7 @@ import { STEP_VISIBILITY, STEP_STATE, } from '../../../../helpers/wizardHelper' +import { clean } from '../../../../helpers/utils' import './EditProjectForm.scss' @@ -151,6 +152,17 @@ class EditProjectForm extends Component { template: updatedTemplate, project: hidedSomeNodes ? nextProps.project : this.state.project, }) + + // re-check again if any hidden values when an option is deselected + const updatedProject = clean(removeValuesOfHiddenNodes(updatedTemplate, nextProps.project)) + const skipProperties = ['members', 'invites'] + const clearUpdatedProject = clean(_.omit(updatedProject, [...skipProperties, 'isDirty'])) + const clearUpdatedNonDirtyProject = clean(_.omit(nextProps.projectNonDirty, skipProperties)) + const isDirty = !_.isEqual(clearUpdatedProject, clearUpdatedNonDirtyProject) + // update the state, always use this flag to check if changed + this.setState({ + isProjectDirty: isDirty, + }) } } } @@ -206,7 +218,7 @@ class EditProjectForm extends Component { } isChanged() { - return !!this.props.project.isDirty + return !!this.state.isProjectDirty } enableButton() { diff --git a/src/projects/reducers/project.js b/src/projects/reducers/project.js index e90c5e021..1abf506ec 100644 --- a/src/projects/reducers/project.js +++ b/src/projects/reducers/project.js @@ -25,6 +25,7 @@ import { } from '../../config/constants' import _ from 'lodash' import update from 'react-addons-update' +import { clean } from '../../helpers/utils' const initialState = { isLoading: true, @@ -631,13 +632,18 @@ export const projectState = function (state=initialState, action) { if (key === 'screens' || key === 'features' || key === 'capabilities') { return srcValue// srcValue contains the changed values from action payload } + + // project's name might contain ampersand + if (key === 'name') { + return _.escape(srcValue) + } } ) // dont' compare this properties as they could be not added to `projectNonDirty` // or mutated somewhere in the app - const skipProperties = ['members'] - const clearUpdatedProject = _.omit(updatedProject, skipProperties) - const clearUpdatedNonDirtyProject = _.omit(state.projectNonDirty, skipProperties) + const skipProperties = ['members', 'invites'] + const clearUpdatedProject = clean(_.omit(updatedProject, skipProperties)) + const clearUpdatedNonDirtyProject = clean(_.omit(state.projectNonDirty, skipProperties)) if (!_.isEqual(clearUpdatedProject, clearUpdatedNonDirtyProject)) { updatedProject.isDirty = true }