diff --git a/app/controllers/settings/integrations/firstpromoter.js b/app/controllers/settings/integrations/firstpromoter.js new file mode 100644 index 000000000..8414c7c72 --- /dev/null +++ b/app/controllers/settings/integrations/firstpromoter.js @@ -0,0 +1,70 @@ +/* eslint-disable ghost/ember/alias-model-in-controller */ +import Controller from '@ember/controller'; +import {inject as service} from '@ember/service'; +import {task} from 'ember-concurrency'; + +export default Controller.extend({ + notifications: service(), + settings: service(), + + leaveSettingsTransition: null, + + actions: { + update(value) { + this.settings.set('firstpromoter', value); + }, + + save() { + this.save.perform(); + }, + + toggleLeaveSettingsModal(transition) { + let leaveTransition = this.leaveSettingsTransition; + + if (!transition && this.showLeaveSettingsModal) { + this.set('leaveSettingsTransition', null); + this.set('showLeaveSettingsModal', false); + return; + } + + if (!leaveTransition || transition.targetName === leaveTransition.targetName) { + this.set('leaveSettingsTransition', transition); + + // if a save is running, wait for it to finish then transition + if (this.save.isRunning) { + return this.save.last.then(() => { + transition.retry(); + }); + } + + // we genuinely have unsaved data, show the modal + this.set('showLeaveSettingsModal', true); + } + }, + + leaveSettings() { + let transition = this.leaveSettingsTransition; + let settings = this.settings; + + if (!transition) { + this.notifications.showAlert('Sorry, there was an error in the application. Please let the Ghost team know what happened.', {type: 'error'}); + return; + } + + // roll back changes on settings model + settings.rollbackAttributes(); + + return transition.retry(); + } + }, + + save: task(function* () { + try { + yield this.settings.validate(); + return yield this.settings.save(); + } catch (error) { + this.notifications.showAPIError(error); + throw error; + } + }).drop() +}); diff --git a/app/models/setting.js b/app/models/setting.js index f80ff75b2..34421f9fc 100644 --- a/app/models/setting.js +++ b/app/models/setting.js @@ -26,6 +26,8 @@ export default Model.extend(ValidationEngine, { slack: attr('slack-settings'), amp: attr('boolean'), ampGtagId: attr('string'), + firstpromoter: attr('boolean'), + firstpromoterId: attr('string'), unsplash: attr('unsplash-settings', { defaultValue() { return {isActive: true}; diff --git a/app/router.js b/app/router.js index 06a0e77a6..9ae86a368 100644 --- a/app/router.js +++ b/app/router.js @@ -59,6 +59,7 @@ Router.map(function () { }); this.route('settings.integrations.slack', {path: '/settings/integrations/slack'}); this.route('settings.integrations.amp', {path: '/settings/integrations/amp'}); + this.route('settings.integrations.firstpromoter', {path: '/settings/integrations/firstpromoter'}); this.route('settings.integrations.unsplash', {path: '/settings/integrations/unsplash'}); this.route('settings.integrations.zapier', {path: '/settings/integrations/zapier'}); diff --git a/app/routes/settings/integrations/firstpromoter.js b/app/routes/settings/integrations/firstpromoter.js new file mode 100644 index 000000000..675b174c3 --- /dev/null +++ b/app/routes/settings/integrations/firstpromoter.js @@ -0,0 +1,39 @@ +import AuthenticatedRoute from 'ghost-admin/routes/authenticated'; +import CurrentUserSettings from 'ghost-admin/mixins/current-user-settings'; +import {inject as service} from '@ember/service'; + +export default AuthenticatedRoute.extend(CurrentUserSettings, { + settings: service(), + + beforeModel() { + this._super(...arguments); + return this.get('session.user') + .then(this.transitionAuthor()) + .then(this.transitionEditor()) + .then(this.settings.reload()); + }, + + actions: { + save() { + this.controller.send('save'); + }, + + willTransition(transition) { + let controller = this.controller; + let modelIsDirty = this.settings.get('hasDirtyAttributes'); + + if (modelIsDirty) { + transition.abort(); + controller.send('toggleLeaveSettingsModal', transition); + return; + } + } + }, + + buildRouteInfoMetadata() { + return { + titleToken: 'FirstPromoter' + }; + } + +}); diff --git a/app/services/settings.js b/app/services/settings.js index 9bb57166d..9af283f2c 100644 --- a/app/services/settings.js +++ b/app/services/settings.js @@ -27,7 +27,7 @@ export default Service.extend(_ProxyMixin, ValidationEngine, { _loadSettings() { if (!this._loadingPromise) { this._loadingPromise = this.store - .queryRecord('setting', {group: 'site,theme,private,members,portal,newsletter,email,amp,labs,slack,unsplash,views'}) + .queryRecord('setting', {group: 'site,theme,private,members,portal,newsletter,email,amp,labs,slack,unsplash,views,firstpromoter'}) .then((settings) => { this._loadingPromise = null; return settings; diff --git a/app/styles/layouts/settings.css b/app/styles/layouts/settings.css index 0415c3f5c..bb408e122 100644 --- a/app/styles/layouts/settings.css +++ b/app/styles/layouts/settings.css @@ -899,6 +899,11 @@ p.theme-validation-details { padding-right: 40px; } +.gh-setting-firstpromoter-liquid { + padding-left: 40px; + padding-right: 40px; +} + .gh-setting-unsplash-checkbox { margin-bottom: 0; } \ No newline at end of file diff --git a/app/templates/settings/integrations.hbs b/app/templates/settings/integrations.hbs index 14380440c..a608e2aa3 100644 --- a/app/templates/settings/integrations.hbs +++ b/app/templates/settings/integrations.hbs @@ -153,7 +153,29 @@ - +
+ +
+
+
+
+

FirstPromoter

+

Launch your member referral program

+
+
+
+
+ {{#if this.settings.firstpromoter}} + Active + {{else}} + Configure + {{/if}} + {{svg-jar "arrow-right"}} +
+
+
+
+
diff --git a/app/templates/settings/integrations/firstpromoter.hbs b/app/templates/settings/integrations/firstpromoter.hbs new file mode 100644 index 000000000..b483cedf1 --- /dev/null +++ b/app/templates/settings/integrations/firstpromoter.hbs @@ -0,0 +1,79 @@ +
+ +

+ Integrations + {{svg-jar "arrow-right"}} + FirstPromoter +

+
+ +
+
+ + {{#if this.showLeaveSettingsModal}} + + {{/if}} + +
+
+
+
+ +
+
+
+

FirstPromoter

+

Launch your own member referral program

+
+
+ +
FirstPromoter configuration
+
+
+
+
Enable FirstPromoter
+
Enable FirstPromoter for tracking referrals
+
+
+
+ +
+
+
+ {{#liquid-if this.settings.firstpromoter class="nl5 nr5"}} +
+
+
FirstPromoter Tracking ID
+
Affiliate and referral tracking, find your ID here
+
+ + + + +
+
+
+ {{/liquid-if}} +
+
+
diff --git a/public/assets/icons/firstpromoter.png b/public/assets/icons/firstpromoter.png new file mode 100644 index 000000000..3252af7d6 Binary files /dev/null and b/public/assets/icons/firstpromoter.png differ diff --git a/public/assets/img/firstpromoter.png b/public/assets/img/firstpromoter.png new file mode 100644 index 000000000..3252af7d6 Binary files /dev/null and b/public/assets/img/firstpromoter.png differ