Skip to content
This repository has been archived by the owner on Mar 28, 2023. It is now read-only.

Settings invalid data #754

Merged
merged 3 commits into from
Sep 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions js/languages/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,10 @@
},
"settings": {
"settingsLabel": "Settings",
"unrecognizedModelErrsWarning": {
"title": "Unable to save your settings",
"body": "We are unable to save the settings for this tab because there are unrecognized errors. They are probably coming from invalid values in another tab. Please save out the the other tabs in order to identify the field with an error. The error will be displayed above the field. After fixing the error, make sure to click Save on the tab the error was in. Afterwards, you may return to this tab to complete the save of this tab."
},
"generalTab": {
"helperLanguage": "Display interface elements in",
"helperCountry": "The country you primarily reside in",
Expand Down
5 changes: 4 additions & 1 deletion js/models/BaseModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ export default class extends Model {
(attrs = {})[key] = val;
}

// take a snapshot of the attrs provided to this method
const setAttrs = JSON.parse(JSON.stringify(attrs));

const previousAttrs = this.toJSON();

// todo: will it break things if we unset a nested attribute?
Expand Down Expand Up @@ -148,7 +151,7 @@ export default class extends Model {
// account nested models, we'll fire our own event if any part of the
// model (including nested parts) change.
if (!_.isEqual(this.toJSON(), previousAttrs)) {
this.trigger('someChange', this, {});
this.trigger('someChange', this, { setAttrs });
}

return superSet;
Expand Down
39 changes: 28 additions & 11 deletions js/views/modals/Settings/Addresses.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ export default class extends baseVw {
});

this.settings = app.settings.clone();
this.listenTo(this.settings, 'sync', (md, resp, syncOpts) => {
// Since different tabs are working off different parts of
// the settings model, to not overwrite each other, we'll only
// update fields that our tab has changed.
app.settings.set(syncOpts.attrs);
});

// Sync our clone with any changes made to the global settings model.
this.listenTo(app.settings, 'someChange', (md, opts) =>
this.settings.set(opts.setAttrs));

// Sync the global settings model with any changes we save via our clone.
this.listenTo(this.settings, 'sync', (md, resp, opts) =>
app.settings.set(this.settings.toJSON(opts.attrs)));

this.addressForm = this.createChild(AddressesForm, { model: new ShippingAddress() });

Expand All @@ -38,7 +40,14 @@ export default class extends baseVw {
const shippingAddresses = this.settings.get('shippingAddresses');
const removeIndex = shippingAddresses.indexOf(address);

shippingAddresses.remove(address);
this.settings.set({}, { validate: true });

if (!this.settings.validationError) {
shippingAddresses.remove(address);
} else {
this.trigger('unrecognizedModelError', this, [this.settings]);
return;
}

const save = this.settings.save({ shippingAddresses: shippingAddresses.toJSON() }, {
attrs: { shippingAddresses: shippingAddresses.toJSON() },
Expand Down Expand Up @@ -95,8 +104,9 @@ export default class extends baseVw {

model.set(formData);
model.set(formData, { validate: true });
this.settings.set({}, { validate: true });

if (!model.validationError) {
if (!this.settings.validationError) {
const shippingAddresses = this.settings.get('shippingAddresses');

shippingAddresses.push(model);
Expand All @@ -107,6 +117,7 @@ export default class extends baseVw {
});

if (save) {
this.$btnAddAddress.addClass('processing');
const truncatedName = model.get('name').slice(0, 30);

const msg = {
Expand Down Expand Up @@ -158,10 +169,16 @@ export default class extends baseVw {

// render so errors are shown / cleared
this.addressForm.render();
if (!model.validationError) this.$btnAddAddress.addClass('processing');

const $firstFormErr = this.$('.js-formContainer .errorList:first');
if ($firstFormErr.length) $firstFormErr[0].scrollIntoViewIfNeeded();
if (this.settings.validationError) {
const $firstFormErr = this.$('.js-formContainer .errorList:first');

if ($firstFormErr.length) {
$firstFormErr[0].scrollIntoViewIfNeeded();
} else {
this.trigger('unrecognizedModelError', this, [this.settings]);
}
}
}

get $btnAddAddress() {
Expand Down
34 changes: 24 additions & 10 deletions js/views/modals/Settings/General.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,22 @@ export default class extends baseVw {

this.settings = app.settings.clone();

this.listenTo(this.settings, 'sync', (md, resp, syncOpts) => {
// Since different tabs are working off different parts of
// the settings model, to not overwrite each other, we'll only
// update fields that our tab has changed.
app.settings.set(syncOpts.attrs);
});
// Sync our clone with any changes made to the global settings model.
this.listenTo(app.settings, 'someChange',
(md, opts) => this.settings.set(opts.setAttrs));

// Sync the global settings model with any changes we save via our clone.
this.listenTo(this.settings, 'sync', (md, resp, opts) => app.settings.set(opts.attrs));

this.localSettings = app.localSettings.clone();

// Sync our clone with any changes made to the global local settings model.
this.listenTo(this.localSettings, 'sync',
(md, resp, opts) => app.localSettings.set(this.localSettings.toJSON(opts.attrs)));

// Sync the global local settings model with any changes we save via our clone.
this.listenTo(this.localSettings, 'sync',
() => app.localSettings.set(this.localSettings.toJSON()));
(md, resp, opts) => app.localSettings.set(opts.attrs));

this.countryList = getTranslatedCountries(app.settings.get('language'));
this.currencyList = getTranslatedCurrencies(app.settings.get('language'));
Expand Down Expand Up @@ -107,12 +112,21 @@ export default class extends baseVw {
}

this.render();

if (!this.localSettings.validationError && !this.settings.validationError) {
this.$btnSave.addClass('processing');
} else {
const $firstErr = this.$('.errorList:first');

if ($firstErr.length) {
$firstErr[0].scrollIntoViewIfNeeded();
} else {
const models = [];
if (this.localSettings.validationError) models.push(this.localSettings);
if (this.settings.validationError) models.push(this.settings);
this.trigger('unrecognizedModelError', this, models);
}
}

const $firstErr = this.$('.errorList:first');
if ($firstErr.length) $firstErr[0].scrollIntoViewIfNeeded();
}

get $btnSave() {
Expand Down
21 changes: 18 additions & 3 deletions js/views/modals/Settings/Moderation.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ export default class extends baseVw {

this.profile = app.profile.clone();

// Sync our clone with any changes made to the global profile.
this.listenTo(app.profile, 'someChange',
(md, opts) => this.profile.set(opts.setAttrs));

// Sync the global profile with any changes we save via our clone.
this.listenTo(this.profile, 'sync',
(md, resp, opts) => app.profile.set(this.profile.toJSON(opts.attrs)));

if (this.profile.get('moderatorInfo')) {
this.moderator = this.profile.get('moderatorInfo');
} else {
Expand Down Expand Up @@ -117,10 +125,17 @@ export default class extends baseVw {
// render so errrors are shown / cleared
this.render();

if (save) this.$btnSave.addClass('processing');
if (save) {
this.$btnSave.addClass('processing');
} else {
const $firstErr = this.$('.errorList:first');

const $firstErr = this.$('.errorList:first');
if ($firstErr.length) $firstErr[0].scrollIntoViewIfNeeded();
if ($firstErr.length) {
$firstErr[0].scrollIntoViewIfNeeded();
} else {
this.trigger('unrecognizedModelError', this, [this.profile]);
}
}
}

changeFeeType(e) {
Expand Down
23 changes: 19 additions & 4 deletions js/views/modals/Settings/Page.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@ export default class extends baseVw {
this.headerMinHeight = 700;

this.profile = app.profile.clone();
this.listenTo(this.profile, 'sync', () => app.profile.set(this.profile.toJSON()));

// Sync our clone with any changes made to the global profile.
this.listenTo(app.profile, 'someChange',
(md, opts) => this.profile.set(opts.setAttrs));

// Sync the global profile with any changes we save via our clone.
this.listenTo(this.profile, 'sync',
(md, resp, syncOpts) => app.profile.set(this.profile.toJSON(syncOpts.attrs)));

this.socialAccounts = this.createChild(SocialAccounts, {
collection: this.profile.get('contactInfo').get('social'),
Expand Down Expand Up @@ -223,10 +230,18 @@ export default class extends baseVw {
}

this.render();
if (save) this.$btnSave.addClass('processing');

const $firstErr = this.$('.errorList:first');
if ($firstErr.length) $firstErr[0].scrollIntoViewIfNeeded();
if (save) {
this.$btnSave.addClass('processing');
} else {
const $firstErr = this.$('.errorList:first');

if ($firstErr.length) {
$firstErr[0].scrollIntoViewIfNeeded();
} else {
this.trigger('unrecognizedModelError', this, [this.profile]);
}
}
}

get $btnSave() {
Expand Down
14 changes: 14 additions & 0 deletions js/views/modals/Settings/Settings.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import $ from 'jquery';
import app from '../../../app';
import { openSimpleMessage } from '../../modals/SimpleMessage';
import loadTemplate from '../../../utils/loadTemplate';
import BaseModal from '../BaseModal';
import General from './General';
Expand Down Expand Up @@ -55,6 +56,18 @@ export default class extends BaseModal {
this.selectTab(targ);
}

onUnrecognizedModelError(tabView, models = []) {
const errors = models.map(md => {
const errObj = md.validationError || {};
return Object.keys(errObj).map(key => `${key}: ${errObj[key]}`);
});

const body = app.polyglot.t('settings.unrecognizedModelErrsWarning.body') +
(errors.length ? `<br><br>${errors.join('<br> ')}` : '');

openSimpleMessage(app.polyglot.t('settings.unrecognizedModelErrsWarning.title'), body);
}

selectTab(targ, options = {}) {
const tabViewName = targ.data('tab');
let tabView = this.tabViewCache[tabViewName];
Expand All @@ -68,6 +81,7 @@ export default class extends BaseModal {
tabView = this.createChild(this.tabViews[tabViewName]);
this.tabViewCache[tabViewName] = tabView;
tabView.render();
this.listenTo(tabView, 'unrecognizedModelError', this.onUnrecognizedModelError);
}

this.$tabContent.append(tabView.$el);
Expand Down
42 changes: 33 additions & 9 deletions js/views/modals/Settings/Store.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,25 @@ export default class extends baseVw {
});

this.profile = app.profile.clone();

// Sync our clone with any changes made to the global profile.
this.listenTo(app.profile, 'someChange',
(md, opts) => this.profile.set(opts.setAttrs));

// Sync the global profile with any changes we save via our clone.
this.listenTo(this.profile, 'sync',
(md, resp, opts) => app.profile.set(this.profile.toJSON(opts.attrs)));

this.settings = app.settings.clone();

// Sync our clone with any changes made to the global settings model.
this.listenTo(app.settings, 'someChange',
(md, opts) => this.settings.set(opts.setAttrs));

// Sync the global settings model with any changes we save via our clone.
this.listenTo(this.settings, 'sync',
(md, resp, opts) => app.settings.set(this.settings.toJSON(opts.attrs)));

this.currentMods = this.settings.get('storeModerators');

this.modsSelected = new Moderators(null, {
Expand Down Expand Up @@ -104,9 +122,6 @@ export default class extends baseVw {
this.listenTo(this.modsAvailable, 'doneLoading', () => {
this.doneLoading(this.$modListAvailable);
});

this.listenTo(this.profile, 'sync', () => app.profile.set(this.profile.toJSON()));
this.listenTo(this.settings, 'sync', () => app.settings.set(this.settings.toJSON()));
}

events() {
Expand Down Expand Up @@ -292,9 +307,13 @@ export default class extends baseVw {
const settingsFormData = this.getSettingsData();

this.profile.set(profileFormData);
this.profile.set(profileFormData, { validate: true });
this.settings.set(settingsFormData);
this.settings.set(settingsFormData, { validate: true });

if (!this.profile.validationError && !this.settings.validationError) {
this.$btnSave.addClass('processing');

const msg = {
msg: app.polyglot.t('settings.storeTab.status.saving'),
type: 'message',
Expand Down Expand Up @@ -366,13 +385,18 @@ export default class extends baseVw {
setTimeout(() => statusMessage.remove(), 3000);
this.render();
});
}
if (!this.profile.validationError && !this.settings.validationError) {
this.$btnSave.addClass('processing');
}
} else {
const $firstErr = this.$('.errorList:first:not(.hide)');

const $firstErr = this.$('.errorList:first');
if ($firstErr.length) $firstErr[0].scrollIntoViewIfNeeded();
if ($firstErr.length) {
$firstErr[0].scrollIntoViewIfNeeded();
} else {
const models = [];
if (this.profile.validationError) models.push(this.profile);
if (this.settings.validationError) models.push(this.settings);
this.trigger('unrecognizedModelError', this, models);
}
}
}

get $btnSave() {
Expand Down
Loading