diff --git a/ghost/admin/app/controllers/member.js b/ghost/admin/app/controllers/member.js
index bdaca12866..03a7ce06a6 100644
--- a/ghost/admin/app/controllers/member.js
+++ b/ghost/admin/app/controllers/member.js
@@ -20,12 +20,9 @@ export default class MemberController extends Controller {
@tracked isLoading = false;
@tracked showDeleteMemberModal = false;
@tracked showImpersonateMemberModal = false;
- @tracked showUnsavedChangesModal = false;
@tracked modalLabel = null;
@tracked showLabelModal = false;
- leaveScreenTransition = null;
-
constructor() {
super(...arguments);
this._availableLabels = this.store.peekAll('label');
@@ -135,37 +132,6 @@ export default class MemberController extends Controller {
});
}
- @action
- toggleUnsavedChangesModal(transition) {
- let leaveTransition = this.leaveScreenTransition;
-
- if (!transition && this.showUnsavedChangesModal) {
- this.leaveScreenTransition = null;
- this.showUnsavedChangesModal = false;
- return;
- }
-
- if (!leaveTransition || transition.targetName === leaveTransition.targetName) {
- this.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.showUnsavedChangesModal = true;
- }
- }
-
- @action
- leaveScreen() {
- this.member.rollbackAttributes();
- return this.leaveScreenTransition.retry();
- }
-
// Tasks -------------------------------------------------------------------
@task({drop: true})
diff --git a/ghost/admin/app/routes/member.js b/ghost/admin/app/routes/member.js
index 9d3dfd9437..905b70be4e 100644
--- a/ghost/admin/app/routes/member.js
+++ b/ghost/admin/app/routes/member.js
@@ -1,9 +1,11 @@
import AdminRoute from 'ghost-admin/routes/admin';
+import ConfirmUnsavedChangesModal from '../components/modals/confirm-unsaved-changes';
import {action} from '@ember/object';
import {inject as service} from '@ember/service';
export default class MembersRoute extends AdminRoute {
@service feature;
+ @service modals;
@service router;
_requiresBackgroundRefresh = true;
@@ -11,7 +13,6 @@ export default class MembersRoute extends AdminRoute {
constructor() {
super(...arguments);
this.router.on('routeWillChange', (transition) => {
- this.showUnsavedChangesModal(transition);
this.closeImpersonateModal(transition);
});
}
@@ -36,10 +37,10 @@ export default class MembersRoute extends AdminRoute {
}
deactivate() {
- super.deactivate(...arguments);
- // clean up newly created records and revert unsaved changes to existing
- this.controller.member.rollbackAttributes();
this._requiresBackgroundRefresh = true;
+
+ this.confirmModal = null;
+ this.hasConfirmed = false;
}
@action
@@ -47,27 +48,48 @@ export default class MembersRoute extends AdminRoute {
this.controller.save();
}
- titleToken() {
- return this.controller.member.name;
- }
+ @action
+ async willTransition(transition) {
+ if (this.hasConfirmed) {
+ return true;
+ }
- showUnsavedChangesModal(transition) {
- if (transition.from && transition.from.name === this.routeName && transition.targetName) {
- let {controller} = this;
+ transition.abort();
- // member.changedAttributes is always true for new members but number of changed attrs is reliable
- let isChanged = Object.keys(controller.member.changedAttributes()).length > 0;
+ // wait for any existing confirm modal to be closed before allowing transition
+ if (this.confirmModal) {
+ return;
+ }
- if (!controller.member.isDeleted && isChanged) {
- transition.abort();
- controller.toggleUnsavedChangesModal(transition);
- return;
- }
+ if (this.controller.saveTask?.isRunning) {
+ await this.controller.saveTask.last;
+ }
+
+ const shouldLeave = await this.confirmUnsavedChanges();
+
+ if (shouldLeave) {
+ this.controller.model.rollbackAttributes();
+ this.hasConfirmed = true;
+ return transition.retry();
}
}
+ async confirmUnsavedChanges() {
+ if (this.controller.model?.hasDirtyAttributes) {
+ this.confirmModal = this.modals
+ .open(ConfirmUnsavedChangesModal)
+ .finally(() => {
+ this.confirmModal = null;
+ });
+
+ return this.confirmModal;
+ }
+
+ return true;
+ }
+
closeImpersonateModal(transition) {
- // If user navigates away with forward or back button, ensure returning to page
+ // If user navigates away with forward or back button, ensure returning to page
// hides modal
if (transition.from && transition.from.name === this.routeName && transition.targetName) {
let {controller} = this;
@@ -75,4 +97,8 @@ export default class MembersRoute extends AdminRoute {
controller.closeImpersonateMemberModal(transition);
}
}
+
+ titleToken() {
+ return this.controller.member.name;
+ }
}
diff --git a/ghost/admin/app/templates/member.hbs b/ghost/admin/app/templates/member.hbs
index 12b6f5b6e1..2a958a48d7 100644
--- a/ghost/admin/app/templates/member.hbs
+++ b/ghost/admin/app/templates/member.hbs
@@ -68,14 +68,6 @@
-{{#if this.showUnsavedChangesModal}}
-
-{{/if}}
-
{{#if this.showDeleteMemberModal}}