diff --git a/ghost/admin/app/controllers/member.js b/ghost/admin/app/controllers/member.js index 8348e9fc62..599faae44b 100644 --- a/ghost/admin/app/controllers/member.js +++ b/ghost/admin/app/controllers/member.js @@ -25,7 +25,7 @@ export default Controller.extend({ setProperty(propKey, value) { this._saveMemberProperty(propKey, value); }, - toggleDeleteTagModal() { + toggleDeleteMemberModal() { this.toggleProperty('showDeleteMemberModal'); }, finaliseDeletion() { @@ -35,6 +35,44 @@ export default Controller.extend({ this.members.decrementProperty('meta.pagination.total'); } this.router.transitionTo('members'); + }, + + toggleUnsavedChangesModal(transition) { + let leaveTransition = this.leaveScreenTransition; + + if (!transition && this.showUnsavedChangesModal) { + this.set('leaveScreenTransition', null); + this.set('showUnsavedChangesModal', false); + return; + } + + if (!leaveTransition || transition.targetName === leaveTransition.targetName) { + this.set('leaveScreenTransition', 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('showUnsavedChangesModal', true); + } + }, + + leaveScreen() { + let transition = this.leaveScreenTransition; + + 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 model props + this.member.rollbackAttributes(); + + return transition.retry(); } }, diff --git a/ghost/admin/app/routes/member.js b/ghost/admin/app/routes/member.js index 028bf8b66a..97e474af9b 100644 --- a/ghost/admin/app/routes/member.js +++ b/ghost/admin/app/routes/member.js @@ -1,6 +1,24 @@ 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, { + + router: service(), + + init() { + this._super(...arguments); + this.router.on('routeWillChange', (transition) => { + this.showUnsavedChangesModal(transition); + }); + }, + + beforeModel() { + this._super(...arguments); + return this.get('session.user') + .then(this.transitionAuthor()); + }, -export default AuthenticatedRoute.extend({ model(params) { this._isMemberUpdated = true; return this.store.findRecord('member', params.member_id, { @@ -19,10 +37,31 @@ export default AuthenticatedRoute.extend({ this._super(...arguments); // clear the properties + let {controller} = this; + controller.model.rollbackAttributes(); + this.set('controller.model', null); this._isMemberUpdated = false; }, + actions: { + save() { + this.controller.send('save'); + } + }, + titleToken() { return this.controller.get('member.name'); + }, + + showUnsavedChangesModal(transition) { + if (transition.from && transition.from.name.match(/^member$/) && transition.targetName) { + let {controller} = this; + + if (!controller.member.isDeleted && controller.member.hasDirtyAttributes) { + transition.abort(); + controller.send('toggleUnsavedChangesModal', transition); + return; + } + } } }); diff --git a/ghost/admin/app/templates/member.hbs b/ghost/admin/app/templates/member.hbs index a8210781ef..9f898cfc9b 100644 --- a/ghost/admin/app/templates/member.hbs +++ b/ghost/admin/app/templates/member.hbs @@ -31,7 +31,7 @@ {{gh-member-settings-form member=member setProperty=(action "setProperty") isLoading=this.isLoading - showDeleteTagModal=(action "toggleDeleteTagModal")}} + showDeleteTagModal=(action "toggleDeleteMemberModal")}}