From d0f6dd7fef416cfc262e763ac957d148a5120de8 Mon Sep 17 00:00:00 2001 From: Kevin Ansfield Date: Thu, 13 Jan 2022 13:16:13 +0000 Subject: [PATCH] Refactored custom integration creation and limits modals refs https://github.com/TryGhost/Team/issues/559 - switched to new ember-promise-modals pattern - removed controller and template in favor of opening modals directly from the route - removed unused `mousedown` event handlers - they are only necessary when an input blur would trigger validation errors - fixed Enter key not triggering create action by adding an `{{on-key "Enter"}}` event handler to the name input - fixed scroll not resetting to top of integrations screens when navigating between them by adding `{{scroll-top}}` element modifier to the main content sections --- .../app/components/modal-new-integration.hbs | 47 -------------- .../app/components/modal-new-integration.js | 59 ----------------- ...upgrade-custom-integrations-host-limit.hbs | 20 ------ ...-upgrade-custom-integrations-host-limit.js | 16 ----- .../modals/limits/custom-integration.hbs | 22 +++++++ .../modals/new-custom-integration.hbs | 48 ++++++++++++++ .../modals/new-custom-integration.js | 64 +++++++++++++++++++ .../controllers/settings/integrations/new.js | 27 -------- .../app/routes/settings/integrations/new.js | 60 ++++++++++------- .../app/templates/settings/integration.hbs | 2 +- .../app/templates/settings/integrations.hbs | 2 +- .../templates/settings/integrations/new.hbs | 14 ---- 12 files changed, 173 insertions(+), 208 deletions(-) delete mode 100644 ghost/admin/app/components/modal-new-integration.hbs delete mode 100644 ghost/admin/app/components/modal-new-integration.js delete mode 100644 ghost/admin/app/components/modal-upgrade-custom-integrations-host-limit.hbs delete mode 100644 ghost/admin/app/components/modal-upgrade-custom-integrations-host-limit.js create mode 100644 ghost/admin/app/components/modals/limits/custom-integration.hbs create mode 100644 ghost/admin/app/components/modals/new-custom-integration.hbs create mode 100644 ghost/admin/app/components/modals/new-custom-integration.js delete mode 100644 ghost/admin/app/controllers/settings/integrations/new.js delete mode 100644 ghost/admin/app/templates/settings/integrations/new.hbs diff --git a/ghost/admin/app/components/modal-new-integration.hbs b/ghost/admin/app/components/modal-new-integration.hbs deleted file mode 100644 index 6603b87201..0000000000 --- a/ghost/admin/app/components/modal-new-integration.hbs +++ /dev/null @@ -1,47 +0,0 @@ - -{{!-- disable mouseDown so it doesn't trigger focus-out validations --}} - - - - - diff --git a/ghost/admin/app/components/modal-new-integration.js b/ghost/admin/app/components/modal-new-integration.js deleted file mode 100644 index ae39e0ec0b..0000000000 --- a/ghost/admin/app/components/modal-new-integration.js +++ /dev/null @@ -1,59 +0,0 @@ -import ModalComponent from 'ghost-admin/components/modal-base'; -import {alias} from '@ember/object/computed'; -import {A as emberA} from '@ember/array'; -import {isHostLimitError} from 'ghost-admin/services/ajax'; -import {isInvalidError} from 'ember-ajax/errors'; -import {inject as service} from '@ember/service'; -import {task} from 'ember-concurrency'; - -export default ModalComponent.extend({ - router: service(), - feature: service(), - - errorMessage: null, - - confirm() {}, - - integration: alias('model'), - actions: { - updateName(name) { - this.integration.set('name', name); - this.integration.set('hasValidated', emberA()); - this.integration.errors.clear(); - }, - - confirm() { - return this.createIntegration.perform(); - } - }, - - createIntegration: task(function* () { - try { - let integration = yield this.confirm(); - this.router.transitionTo('settings.integration', integration); - } catch (error) { - // TODO: server-side validation errors should be serialized - // properly so that errors are added to model.errors automatically - if (error && isInvalidError(error)) { - let [firstError] = error.payload.errors; - let {message} = firstError; - - if (message && message.match(/name/i)) { - this.get('integration.errors').add('name', message); - this.get('integration.hasValidated').pushObject('name'); - return; - } - } - - if (isHostLimitError(error)) { - this.set('errorMessage', error.payload.errors[0].context); - return; - } - - // bubble up to the global error handler - if (error) { - throw error; - } - } - }).drop() -}); diff --git a/ghost/admin/app/components/modal-upgrade-custom-integrations-host-limit.hbs b/ghost/admin/app/components/modal-upgrade-custom-integrations-host-limit.hbs deleted file mode 100644 index 717b9ecb32..0000000000 --- a/ghost/admin/app/components/modal-upgrade-custom-integrations-host-limit.hbs +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/ghost/admin/app/components/modal-upgrade-custom-integrations-host-limit.js b/ghost/admin/app/components/modal-upgrade-custom-integrations-host-limit.js deleted file mode 100644 index 99c3e17ee7..0000000000 --- a/ghost/admin/app/components/modal-upgrade-custom-integrations-host-limit.js +++ /dev/null @@ -1,16 +0,0 @@ -import ModalComponent from 'ghost-admin/components/modal-base'; -import {inject as service} from '@ember/service'; - -export default ModalComponent.extend({ - router: service(), - - actions: { - upgrade() { - this.router.transitionTo('pro'); - }, - - confirm() { - this.send('upgrade'); - } - } -}); diff --git a/ghost/admin/app/components/modals/limits/custom-integration.hbs b/ghost/admin/app/components/modals/limits/custom-integration.hbs new file mode 100644 index 0000000000..bebe878dd5 --- /dev/null +++ b/ghost/admin/app/components/modals/limits/custom-integration.hbs @@ -0,0 +1,22 @@ + \ No newline at end of file diff --git a/ghost/admin/app/components/modals/new-custom-integration.hbs b/ghost/admin/app/components/modals/new-custom-integration.hbs new file mode 100644 index 0000000000..fd966e2857 --- /dev/null +++ b/ghost/admin/app/components/modals/new-custom-integration.hbs @@ -0,0 +1,48 @@ + \ No newline at end of file diff --git a/ghost/admin/app/components/modals/new-custom-integration.js b/ghost/admin/app/components/modals/new-custom-integration.js new file mode 100644 index 0000000000..3b36af9b29 --- /dev/null +++ b/ghost/admin/app/components/modals/new-custom-integration.js @@ -0,0 +1,64 @@ +import Component from '@glimmer/component'; +import {A} from '@ember/array'; +import {action} from '@ember/object'; +import {isHostLimitError} from 'ghost-admin/services/ajax'; +import {isInvalidError} from 'ember-ajax/errors'; +import {inject as service} from '@ember/service'; +import {task} from 'ember-concurrency-decorators'; +import {tracked} from '@glimmer/tracking'; + +export default class NewCustomIntegrationModalComponent extends Component { + @service router; + @service store; + + @tracked errorMessage; + + constructor() { + super(...arguments); + this.integration = this.store.createRecord('integration'); + } + + willDestroy() { + super.willDestroy(...arguments); + this.integration.rollbackAttributes(); + } + + @action + updateName(inputEvent) { + this.integration.set('name', inputEvent.target.value); + this.integration.set('hasValidated', A()); + this.integration.errors.clear(); + } + + @task({drop: true}) + *createIntegrationTask() { + try { + const integration = yield this.integration.save(); + this.router.transitionTo('settings.integration', integration); + return true; + } catch (error) { + // TODO: server-side validation errors should be serialized + // properly so that errors are added to model.errors automatically + if (error && isInvalidError(error)) { + let [firstError] = error.payload.errors; + let {message} = firstError; + + if (message && message.match(/name/i)) { + this.integration.errors.add('name', message); + this.integration.hasValidated.pushObject('name'); + return; + } + } + + if (isHostLimitError(error)) { + this.errorMessage = error.payload.errors[0].context; + return; + } + + // bubble up to the global error handler + if (error) { + throw error; + } + } + } +} diff --git a/ghost/admin/app/controllers/settings/integrations/new.js b/ghost/admin/app/controllers/settings/integrations/new.js deleted file mode 100644 index 737c3ec88e..0000000000 --- a/ghost/admin/app/controllers/settings/integrations/new.js +++ /dev/null @@ -1,27 +0,0 @@ -import Controller from '@ember/controller'; -import {alias} from '@ember/object/computed'; -import {computed} from '@ember/object'; - -export default Controller.extend({ - integration: alias('model.integration'), - hostLimitError: alias('model.hostLimitError'), - - showUpgradeModal: computed('hostLimitError', function () { - if (this.hostLimitError) { - return true; - } - - return false; - }), - - actions: { - save() { - return this.integration.save(); - }, - - cancel() { - // 'new' route's dectivate hook takes care of rollback - this.transitionToRoute('settings.integrations'); - } - } -}); diff --git a/ghost/admin/app/routes/settings/integrations/new.js b/ghost/admin/app/routes/settings/integrations/new.js index 507eea8b20..84dac0d8cb 100644 --- a/ghost/admin/app/routes/settings/integrations/new.js +++ b/ghost/admin/app/routes/settings/integrations/new.js @@ -1,31 +1,45 @@ -import RSVP from 'rsvp'; import Route from '@ember/routing/route'; +import {action} from '@ember/object'; import {inject as service} from '@ember/service'; -export default Route.extend({ - limit: service(), +export default class NewIntegrationRoute extends Route { + @service limit; + @service modals; - model() { - if (this.limit.limiter - && this.limit.limiter.isLimited('customIntegrations')) { - return RSVP.hash({ - integration: this.store.createRecord('integration'), - hostLimitError: this.limit.limiter.errorIfWouldGoOverLimit('customIntegrations') - .then(() => null) - .catch((error) => { - return error; - }) - }); - } else { - return RSVP.hash({ - integration: this.store.createRecord('integration'), - hostLimitError: null - }); + modal = null; + + async model() { + if (this.limit.limiter?.isLimited('customIntegrations')) { + try { + await this.limit.limiter.errorIfWouldGoOverLimit('customIntegrations'); + } catch (error) { + this.modal = this.modals.open('modals/limits/custom-integration', { + message: error.message + }, { + beforeClose: this.beforeModalClose + }); + return; + } } - }, + + this.modal = this.modals.open('modals/new-custom-integration', {}, { + beforeClose: this.beforeModalClose + }); + } deactivate() { - this._super(...arguments); - this.controller.integration.rollbackAttributes(); + // ensure we don't try to redirect on modal close if we're already transitioning away + this.isLeaving = true; + this.modal?.close(); + + this.modal = null; + this.isLeaving = false; } -}); + + @action + beforeModalClose() { + if (!this.isLeaving) { + this.transitionTo('settings.integrations'); + } + } +} diff --git a/ghost/admin/app/templates/settings/integration.hbs b/ghost/admin/app/templates/settings/integration.hbs index 9226728401..e14c855ead 100644 --- a/ghost/admin/app/templates/settings/integration.hbs +++ b/ghost/admin/app/templates/settings/integration.hbs @@ -13,7 +13,7 @@ -
+

Configuration

diff --git a/ghost/admin/app/templates/settings/integrations.hbs b/ghost/admin/app/templates/settings/integrations.hbs index c8e6f26f93..59b7431a58 100644 --- a/ghost/admin/app/templates/settings/integrations.hbs +++ b/ghost/admin/app/templates/settings/integrations.hbs @@ -7,7 +7,7 @@ -