mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-24 19:33:02 +03:00
🐛 Fixed missing unsaved changes modal for member newsletters (#15564)
closes: https://github.com/TryGhost/Ghost/issues/15507 - manually handle relationship changes detection labels and newsletters - add `dirtyAttributes` controller property - return newsletters and labels dirty attributes status
This commit is contained in:
parent
b88a54fb71
commit
48b033f1e1
@ -24,6 +24,9 @@ export default class MemberController extends Controller {
|
||||
@tracked modalLabel = null;
|
||||
@tracked showLabelModal = false;
|
||||
|
||||
_previousLabels = null;
|
||||
_previousNewsletters = null;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this._availableLabels = this.store.peekAll('label');
|
||||
@ -39,6 +42,18 @@ export default class MemberController extends Controller {
|
||||
this.model = member;
|
||||
}
|
||||
|
||||
get dirtyAttributes() {
|
||||
return this._hasDirtyAttributes();
|
||||
}
|
||||
|
||||
get _labels() {
|
||||
return this.member.get('labels').map(label => label.name);
|
||||
}
|
||||
|
||||
get _newsletters() {
|
||||
return this.member.get('newsletters').map(newsletter => newsletter.id);
|
||||
}
|
||||
|
||||
get labelModalData() {
|
||||
let label = this.modalLabel;
|
||||
let labels = this.availableLabels;
|
||||
@ -75,6 +90,12 @@ export default class MemberController extends Controller {
|
||||
|
||||
// Actions -----------------------------------------------------------------
|
||||
|
||||
@action
|
||||
setInitialRelationshipValues() {
|
||||
this._previousLabels = this._labels;
|
||||
this._previousNewsletters = this._newsletters;
|
||||
}
|
||||
|
||||
@action
|
||||
toggleLabelModal() {
|
||||
this.showLabelModal = !this.showLabelModal;
|
||||
@ -139,6 +160,8 @@ export default class MemberController extends Controller {
|
||||
member.updateLabels();
|
||||
this.members.refreshData();
|
||||
|
||||
this.setInitialRelationshipValues();
|
||||
|
||||
// replace 'member.new' route with 'member' route
|
||||
this.replaceRoute('member', member);
|
||||
|
||||
@ -171,6 +194,8 @@ export default class MemberController extends Controller {
|
||||
include: 'tiers'
|
||||
});
|
||||
|
||||
this.setInitialRelationshipValues();
|
||||
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
@ -190,4 +215,36 @@ export default class MemberController extends Controller {
|
||||
|
||||
this.member[propKey] = newValue;
|
||||
}
|
||||
|
||||
_hasDirtyAttributes() {
|
||||
let member = this.member;
|
||||
|
||||
if (!member) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// member.labels is an array so hasDirtyAttributes doesn't pick up
|
||||
// changes unless the array ref is changed.
|
||||
// use sort() to sort of detect same item is re-added
|
||||
let currentLabels = (this._labels.sort() || []).join(', ');
|
||||
let previousLabels = (this._previousLabels.sort() || []).join(', ');
|
||||
if (currentLabels !== previousLabels) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// member.newsletters is an array so hasDirtyAttributes doesn't pick up
|
||||
// changes unless the array ref is changed
|
||||
// use sort() to sort of detect same item is re-enabled
|
||||
let currentNewsletters = (this._newsletters.sort() || []).join(', ');
|
||||
let previousNewsletters = (this._previousNewsletters.sort() || []).join(', ');
|
||||
if (currentNewsletters !== previousNewsletters) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// we've covered all the non-tracked cases we care about so fall
|
||||
// back on Ember Data's default dirty attribute checks
|
||||
let {hasDirtyAttributes} = member;
|
||||
|
||||
return hasDirtyAttributes;
|
||||
}
|
||||
}
|
||||
|
@ -48,42 +48,39 @@ export default class MembersRoute extends AdminRoute {
|
||||
|
||||
@action
|
||||
async willTransition(transition) {
|
||||
if (this.hasConfirmed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
transition.abort();
|
||||
let hasDirtyAttributes = this.controller.dirtyAttributes;
|
||||
|
||||
// wait for any existing confirm modal to be closed before allowing transition
|
||||
if (this.confirmModal) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.controller.saveTask?.isRunning) {
|
||||
await this.controller.saveTask.last;
|
||||
}
|
||||
if (!this.hasConfirmed && hasDirtyAttributes) {
|
||||
transition.abort();
|
||||
|
||||
const shouldLeave = await this.confirmUnsavedChanges();
|
||||
if (this.controller.saveTask?.isRunning) {
|
||||
await this.controller.saveTask.last;
|
||||
transition.retry();
|
||||
}
|
||||
|
||||
if (shouldLeave) {
|
||||
this.controller.model.rollbackAttributes();
|
||||
this.hasConfirmed = true;
|
||||
return transition.retry();
|
||||
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;
|
||||
});
|
||||
this.confirmModal = this.modals
|
||||
.open(ConfirmUnsavedChangesModal)
|
||||
.finally(() => {
|
||||
this.confirmModal = null;
|
||||
});
|
||||
|
||||
return this.confirmModal;
|
||||
}
|
||||
|
||||
return true;
|
||||
return this.confirmModal;
|
||||
}
|
||||
|
||||
closeImpersonateModal(transition) {
|
||||
|
Loading…
Reference in New Issue
Block a user