Updated members routes/controllers with Octane idioms
no issue - ran [`ember-native-class-codemod`](https://github.com/ember-codemods/ember-native-class-codemod) on members-related files - updated files to remove need for `@classic` decorator where possible - switched to tracked properties - removed usage of `this.get/set/toggleProperty` etc - swapped usage of `{{action 'foo'}}` for `{{this.foo}}`
This commit is contained in:
parent
9250d7939b
commit
e80fa137db
|
@ -16,6 +16,10 @@ module.exports = {
|
||||||
'plugin:ghost/ember'
|
'plugin:ghost/ember'
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
|
// octane 🏎
|
||||||
|
'ghost/ember/classic-decorator-hooks': 'error',
|
||||||
|
'ghost/ember/classic-decorator-no-classic-methods': 'error',
|
||||||
|
|
||||||
// disable linting of `this.get` until there's a reliable autofix
|
// disable linting of `this.get` until there's a reliable autofix
|
||||||
'ghost/ember/use-ember-get-and-set': 'off',
|
'ghost/ember/use-ember-get-and-set': 'off',
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/* global key */
|
/* global key */
|
||||||
import Component from '@ember/component';
|
import Component from '@ember/component';
|
||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
|
import classic from 'ember-classic-decorator';
|
||||||
import fallbackIfUndefined from '../utils/computed-fallback-if-undefined';
|
import fallbackIfUndefined from '../utils/computed-fallback-if-undefined';
|
||||||
import {A, isArray} from '@ember/array';
|
import {A, isArray} from '@ember/array';
|
||||||
import {action, computed, get} from '@ember/object';
|
import {action, computed, get} from '@ember/object';
|
||||||
|
@ -19,6 +20,7 @@ const {Handlebars} = Ember;
|
||||||
const BACKSPACE = 8;
|
const BACKSPACE = 8;
|
||||||
const TAB = 9;
|
const TAB = 9;
|
||||||
|
|
||||||
|
@classic
|
||||||
@tagName('')
|
@tagName('')
|
||||||
class GhTokenInput extends Component {
|
class GhTokenInput extends Component {
|
||||||
// public attrs
|
// public attrs
|
||||||
|
|
|
@ -50,7 +50,7 @@ export default class Trigger extends EmberPowerSelectMultipleTrigger {
|
||||||
if (typeof lastSelection === 'string') {
|
if (typeof lastSelection === 'string') {
|
||||||
this.args.select.actions.search(lastSelection);
|
this.args.select.actions.search(lastSelection);
|
||||||
} else {
|
} else {
|
||||||
let searchField = this.get('searchField');
|
let searchField = this.searchField;
|
||||||
assert('`{{power-select-multiple}}` requires a `searchField` when the options are not strings to remove options using backspace', searchField);
|
assert('`{{power-select-multiple}}` requires a `searchField` when the options are not strings to remove options using backspace', searchField);
|
||||||
this.args.select.actions.search(get(lastSelection, searchField));
|
this.args.select.actions.search(get(lastSelection, searchField));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,95 +1,111 @@
|
||||||
import Controller from '@ember/controller';
|
import Controller, {inject as controller} from '@ember/controller';
|
||||||
import EmberObject from '@ember/object';
|
import EmberObject, {action, defineProperty} from '@ember/object';
|
||||||
import boundOneWay from 'ghost-admin/utils/bound-one-way';
|
import boundOneWay from 'ghost-admin/utils/bound-one-way';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import {alias} from '@ember/object/computed';
|
import {alias} from '@ember/object/computed';
|
||||||
import {computed, defineProperty} from '@ember/object';
|
|
||||||
import {inject as controller} from '@ember/controller';
|
|
||||||
import {inject as service} from '@ember/service';
|
import {inject as service} from '@ember/service';
|
||||||
import {task} from 'ember-concurrency';
|
import {task} from 'ember-concurrency-decorators';
|
||||||
|
import {tracked} from '@glimmer/tracking';
|
||||||
|
|
||||||
const SCRATCH_PROPS = ['name', 'email', 'note'];
|
const SCRATCH_PROPS = ['name', 'email', 'note'];
|
||||||
|
|
||||||
export default Controller.extend({
|
export default class MemberController extends Controller {
|
||||||
members: controller(),
|
@controller members;
|
||||||
session: service(),
|
@service session;
|
||||||
dropdown: service(),
|
@service dropdown;
|
||||||
notifications: service(),
|
@service notifications;
|
||||||
router: service(),
|
@service router;
|
||||||
store: service(),
|
@service store;
|
||||||
|
|
||||||
showImpersonateMemberModal: false,
|
@tracked isLoading = false;
|
||||||
|
@tracked showDeleteMemberModal = false;
|
||||||
|
@tracked showImpersonateMemberModal = false;
|
||||||
|
@tracked showUnsavedChangesModal = false;
|
||||||
|
|
||||||
member: alias('model'),
|
leaveScreenTransition = null;
|
||||||
|
|
||||||
scratchMember: computed('member', function () {
|
// Computed properties -----------------------------------------------------
|
||||||
|
|
||||||
|
@alias('model') member;
|
||||||
|
|
||||||
|
get scratchMember() {
|
||||||
let scratchMember = EmberObject.create({member: this.member});
|
let scratchMember = EmberObject.create({member: this.member});
|
||||||
SCRATCH_PROPS.forEach(prop => defineProperty(scratchMember, prop, boundOneWay(`member.${prop}`)));
|
SCRATCH_PROPS.forEach(prop => defineProperty(scratchMember, prop, boundOneWay(`member.${prop}`)));
|
||||||
return scratchMember;
|
return scratchMember;
|
||||||
}),
|
}
|
||||||
|
|
||||||
subscribedAt: computed('member.createdAtUTC', function () {
|
get subscribedAt() {
|
||||||
let memberSince = moment(this.member.createdAtUTC).from(moment());
|
let memberSince = moment(this.member.createdAtUTC).from(moment());
|
||||||
let createdDate = moment(this.member.createdAtUTC).format('MMM DD, YYYY');
|
let createdDate = moment(this.member.createdAtUTC).format('MMM DD, YYYY');
|
||||||
return `${createdDate} (${memberSince})`;
|
return `${createdDate} (${memberSince})`;
|
||||||
}),
|
}
|
||||||
|
|
||||||
actions: {
|
// Actions -----------------------------------------------------------------
|
||||||
setProperty(propKey, value) {
|
|
||||||
this._saveMemberProperty(propKey, value);
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleDeleteMemberModal() {
|
@action
|
||||||
this.toggleProperty('showDeleteMemberModal');
|
setProperty(propKey, value) {
|
||||||
},
|
this._saveMemberProperty(propKey, value);
|
||||||
|
}
|
||||||
|
|
||||||
toggleImpersonateMemberModal() {
|
@action
|
||||||
this.toggleProperty('showImpersonateMemberModal');
|
toggleDeleteMemberModal() {
|
||||||
},
|
this.showDeleteMemberModal = !this.showDeleteMemberModal;
|
||||||
|
}
|
||||||
|
|
||||||
save() {
|
@action
|
||||||
return this.save.perform();
|
toggleImpersonateMemberModal() {
|
||||||
},
|
this.showImpersonateMemberModal = !this.showImpersonateMemberModal;
|
||||||
|
}
|
||||||
|
|
||||||
deleteMember() {
|
@action
|
||||||
return this.member.destroyRecord().then(() => {
|
save() {
|
||||||
return this.transitionToRoute('members');
|
return this.saveTask.perform();
|
||||||
}, (error) => {
|
}
|
||||||
return this.notifications.showAPIError(error, {key: 'member.delete'});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleUnsavedChangesModal(transition) {
|
@action
|
||||||
let leaveTransition = this.leaveScreenTransition;
|
deleteMember() {
|
||||||
|
return this.member.destroyRecord().then(() => {
|
||||||
|
return this.transitionToRoute('members');
|
||||||
|
}, (error) => {
|
||||||
|
return this.notifications.showAPIError(error, {key: 'member.delete'});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!transition && this.showUnsavedChangesModal) {
|
@action
|
||||||
this.set('leaveScreenTransition', null);
|
toggleUnsavedChangesModal(transition) {
|
||||||
this.set('showUnsavedChangesModal', false);
|
let leaveTransition = this.leaveScreenTransition;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!leaveTransition || transition.targetName === leaveTransition.targetName) {
|
if (!transition && this.showUnsavedChangesModal) {
|
||||||
this.set('leaveScreenTransition', transition);
|
this.leaveScreenTransition = null;
|
||||||
|
this.showUnsavedChangesModal = false;
|
||||||
// if a save is running, wait for it to finish then transition
|
return;
|
||||||
if (this.save.isRunning) {
|
|
||||||
return this.save.last.then(() => {
|
|
||||||
transition.retry();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// we genuinely have unsaved data, show the modal
|
|
||||||
this.set('showUnsavedChangesModal', true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
leaveScreen() {
|
|
||||||
this.member.rollbackAttributes();
|
|
||||||
return this.leaveScreenTransition.retry();
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
save: task(function* () {
|
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})
|
||||||
|
*saveTask() {
|
||||||
let {member, scratchMember} = this;
|
let {member, scratchMember} = this;
|
||||||
|
|
||||||
// if Cmd+S is pressed before the field loses focus make sure we're
|
// if Cmd+S is pressed before the field loses focus make sure we're
|
||||||
|
@ -109,18 +125,20 @@ export default Controller.extend({
|
||||||
this.notifications.showAPIError(error, {key: 'member.save'});
|
this.notifications.showAPIError(error, {key: 'member.save'});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).drop(),
|
}
|
||||||
|
|
||||||
fetchMember: task(function* (memberId) {
|
@task
|
||||||
this.set('isLoading', true);
|
*fetchMemberTask(memberId) {
|
||||||
|
this.isLoading = true;
|
||||||
|
|
||||||
let member = yield this.store.findRecord('member', memberId, {
|
this.member = yield this.store.findRecord('member', memberId, {
|
||||||
reload: true
|
reload: true
|
||||||
});
|
});
|
||||||
|
|
||||||
this.set('member', member);
|
this.isLoading = false;
|
||||||
this.set('isLoading', false);
|
}
|
||||||
}),
|
|
||||||
|
// Private -----------------------------------------------------------------
|
||||||
|
|
||||||
_saveMemberProperty(propKey, newValue) {
|
_saveMemberProperty(propKey, newValue) {
|
||||||
let currentValue = this.member.get(propKey);
|
let currentValue = this.member.get(propKey);
|
||||||
|
@ -136,4 +154,4 @@ export default Controller.extend({
|
||||||
|
|
||||||
this.member.set(propKey, newValue);
|
this.member.set(propKey, newValue);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -1,43 +1,46 @@
|
||||||
import Controller from '@ember/controller';
|
import Controller from '@ember/controller';
|
||||||
import ghostPaths from 'ghost-admin/utils/ghost-paths';
|
import ghostPaths from 'ghost-admin/utils/ghost-paths';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import {computed} from '@ember/object';
|
import {A} from '@ember/array';
|
||||||
import {get} from '@ember/object';
|
import {action} from '@ember/object';
|
||||||
import {pluralize} from 'ember-inflector';
|
import {pluralize} from 'ember-inflector';
|
||||||
import {inject as service} from '@ember/service';
|
import {inject as service} from '@ember/service';
|
||||||
import {task} from 'ember-concurrency';
|
import {task} from 'ember-concurrency-decorators';
|
||||||
|
import {tracked} from '@glimmer/tracking';
|
||||||
|
|
||||||
/* eslint-disable ghost/ember/alias-model-in-controller */
|
export default class MembersController extends Controller {
|
||||||
export default Controller.extend({
|
@service store;
|
||||||
store: service(),
|
|
||||||
|
|
||||||
queryParams: ['label'],
|
queryParams = ['label'];
|
||||||
|
|
||||||
label: null,
|
@tracked searchText = '';
|
||||||
members: null,
|
@tracked label = null;
|
||||||
searchText: '',
|
@tracked members = null;
|
||||||
modalLabel: null,
|
@tracked modalLabel = null;
|
||||||
showLabelModal: false,
|
@tracked showLabelModal = false;
|
||||||
|
|
||||||
_hasLoadedLabels: false,
|
@tracked _availableLabels = A([]);
|
||||||
_availableLabels: null,
|
|
||||||
|
|
||||||
init() {
|
hasLoadedLabels = false;
|
||||||
this._super(...arguments);
|
|
||||||
this.set('members', this.store.peekAll('member'));
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
this.members = this.store.peekAll('member');
|
||||||
this._availableLabels = this.store.peekAll('label');
|
this._availableLabels = this.store.peekAll('label');
|
||||||
},
|
}
|
||||||
|
|
||||||
showLoader: computed('filteredMembers.length', 'fetchMembers.isRunning', function () {
|
// Computed properties -----------------------------------------------------
|
||||||
return (!this.get('filteredMembers.length') && this.get('fetchMembers.isRunning'));
|
|
||||||
}),
|
|
||||||
|
|
||||||
listHeader: computed('selectedLabel', 'searchText', function () {
|
get showLoader() {
|
||||||
|
return (!this.filteredMembers.length && this.fetchMembersTask.isRunning);
|
||||||
|
}
|
||||||
|
|
||||||
|
get listHeader() {
|
||||||
let {searchText, selectedLabel, filteredMembers} = this;
|
let {searchText, selectedLabel, filteredMembers} = this;
|
||||||
if (searchText) {
|
if (searchText) {
|
||||||
return 'Search result';
|
return 'Search result';
|
||||||
}
|
}
|
||||||
if (this.fetchMembers.lastSuccessful) {
|
if (this.fetchMembersTask.lastSuccessful) {
|
||||||
let count = pluralize(filteredMembers.length, 'member');
|
let count = pluralize(filteredMembers.length, 'member');
|
||||||
if (selectedLabel && selectedLabel.slug) {
|
if (selectedLabel && selectedLabel.slug) {
|
||||||
if (filteredMembers.length > 1) {
|
if (filteredMembers.length > 1) {
|
||||||
|
@ -49,44 +52,40 @@ export default Controller.extend({
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
return 'Loading...';
|
return 'Loading...';
|
||||||
}),
|
}
|
||||||
|
|
||||||
showingAll: computed('label', 'searchText', function () {
|
get showingAll() {
|
||||||
let {searchText, label} = this;
|
return !this.searchText && !this.label;
|
||||||
|
}
|
||||||
|
|
||||||
return !searchText && !label;
|
get availableLabels() {
|
||||||
}),
|
|
||||||
|
|
||||||
availableLabels: computed('_availableLabels.@each.isNew', function () {
|
|
||||||
let labels = this._availableLabels
|
let labels = this._availableLabels
|
||||||
.filter(label => !label.get('isNew'))
|
.filter(label => !label.isNew)
|
||||||
.filter(label => label.get('id') !== null)
|
.filter(label => label.id !== null)
|
||||||
.sort((labelA, labelB) => labelA.name.localeCompare(labelB.name, undefined, {ignorePunctuation: true}));
|
.sort((labelA, labelB) => labelA.name.localeCompare(labelB.name, undefined, {ignorePunctuation: true}));
|
||||||
let options = labels.toArray();
|
let options = labels.toArray();
|
||||||
|
|
||||||
options.unshiftObject({name: 'All labels', slug: null});
|
options.unshiftObject({name: 'All labels', slug: null});
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}),
|
}
|
||||||
|
|
||||||
selectedLabel: computed('label', 'availableLabels', function () {
|
get selectedLabel() {
|
||||||
let label = this.get('label');
|
let {label, availableLabels} = this;
|
||||||
let labels = this.get('availableLabels');
|
return availableLabels.findBy('slug', label);
|
||||||
|
}
|
||||||
|
|
||||||
return labels.findBy('slug', label);
|
get labelModalData() {
|
||||||
}),
|
let label = this.modalLabel;
|
||||||
|
let labels = this.availableLabels;
|
||||||
labelModalData: computed('modalLabel', 'availableLabels', function () {
|
|
||||||
let label = this.get('modalLabel');
|
|
||||||
let labels = this.get('availableLabels');
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
label,
|
label,
|
||||||
labels
|
labels
|
||||||
};
|
};
|
||||||
}),
|
}
|
||||||
|
|
||||||
filteredMembers: computed('members.@each.{name,email}', 'searchText', 'label', function () {
|
get filteredMembers() {
|
||||||
let {members, searchText, label} = this;
|
let {members, searchText, label} = this;
|
||||||
searchText = searchText.toLowerCase();
|
searchText = searchText.toLowerCase();
|
||||||
|
|
||||||
|
@ -106,59 +105,69 @@ export default Controller.extend({
|
||||||
return _label.slug === label;
|
return _label.slug === label;
|
||||||
});
|
});
|
||||||
}).sort((a, b) => {
|
}).sort((a, b) => {
|
||||||
return b.get('createdAtUTC').valueOf() - a.get('createdAtUTC').valueOf();
|
return b.createdAtUTC.valueOf() - a.createdAtUTC.valueOf();
|
||||||
});
|
});
|
||||||
|
|
||||||
return filtered;
|
return filtered;
|
||||||
}),
|
}
|
||||||
|
|
||||||
actions: {
|
// Actions -----------------------------------------------------------------
|
||||||
exportData() {
|
|
||||||
let exportUrl = ghostPaths().url.api('members/csv');
|
|
||||||
let downloadURL = `${exportUrl}?limit=all`;
|
|
||||||
let iframe = document.getElementById('iframeDownload');
|
|
||||||
|
|
||||||
if (!iframe) {
|
@action
|
||||||
iframe = document.createElement('iframe');
|
exportData() {
|
||||||
iframe.id = 'iframeDownload';
|
let exportUrl = ghostPaths().url.api('members/csv');
|
||||||
iframe.style.display = 'none';
|
let downloadURL = `${exportUrl}?limit=all`;
|
||||||
document.body.append(iframe);
|
let iframe = document.getElementById('iframeDownload');
|
||||||
}
|
|
||||||
iframe.setAttribute('src', downloadURL);
|
|
||||||
},
|
|
||||||
changeLabel(label, e) {
|
|
||||||
if (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
}
|
|
||||||
this.set('label', get(label, 'slug'));
|
|
||||||
},
|
|
||||||
addLabel(e) {
|
|
||||||
if (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
}
|
|
||||||
const newLabel = this.store.createRecord('label');
|
|
||||||
this.set('modalLabel', newLabel);
|
|
||||||
this.toggleProperty('showLabelModal');
|
|
||||||
},
|
|
||||||
editLabel(label, e) {
|
|
||||||
if (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
}
|
|
||||||
let labels = this.get('availableLabels');
|
|
||||||
|
|
||||||
let modalLabel = labels.findBy('slug', label);
|
if (!iframe) {
|
||||||
this.set('modalLabel', modalLabel);
|
iframe = document.createElement('iframe');
|
||||||
this.toggleProperty('showLabelModal');
|
iframe.id = 'iframeDownload';
|
||||||
},
|
iframe.style.display = 'none';
|
||||||
toggleLabelModal() {
|
document.body.append(iframe);
|
||||||
this.toggleProperty('showLabelModal');
|
|
||||||
}
|
}
|
||||||
},
|
iframe.setAttribute('src', downloadURL);
|
||||||
|
}
|
||||||
|
|
||||||
fetchMembers: task(function* () {
|
@action
|
||||||
|
changeLabel(label, e) {
|
||||||
|
if (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
this.label = label.slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
addLabel(e) {
|
||||||
|
if (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
const newLabel = this.store.createRecord('label');
|
||||||
|
this.modalLabel = newLabel;
|
||||||
|
this.showLabelModal = !this.showLabelModal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
editLabel(label, e) {
|
||||||
|
if (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
let modalLabel = this.availableLabels.findBy('slug', label);
|
||||||
|
this.modalLabel = modalLabel;
|
||||||
|
this.showLabelModal = !this.showLabelModal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
toggleLabelModal() {
|
||||||
|
this.showLabelModal = !this.showLabelModal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tasks -------------------------------------------------------------------
|
||||||
|
|
||||||
|
@task
|
||||||
|
*fetchMembersTask() {
|
||||||
let newFetchDate = new Date();
|
let newFetchDate = new Date();
|
||||||
|
|
||||||
if (this._hasFetchedAll) {
|
if (this._hasFetchedAll) {
|
||||||
|
@ -178,5 +187,5 @@ export default Controller.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
this._lastFetchDate = newFetchDate;
|
this._lastFetchDate = newFetchDate;
|
||||||
})
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
import Controller from '@ember/controller';
|
import Controller from '@ember/controller';
|
||||||
|
import {action} from '@ember/object';
|
||||||
import {inject as controller} from '@ember/controller';
|
import {inject as controller} from '@ember/controller';
|
||||||
import {inject as service} from '@ember/service';
|
import {inject as service} from '@ember/service';
|
||||||
|
|
||||||
/* eslint-disable ghost/ember/alias-model-in-controller */
|
export default class ImportController extends Controller {
|
||||||
export default Controller.extend({
|
@controller members;
|
||||||
members: controller(),
|
@service router;
|
||||||
router: service(),
|
|
||||||
|
|
||||||
actions: {
|
@action
|
||||||
fetchNewMembers() {
|
fetchNewMembers() {
|
||||||
this.members.fetchMembers.perform();
|
this.members.fetchMembersTask.perform();
|
||||||
},
|
|
||||||
|
|
||||||
close() {
|
|
||||||
this.router.transitionTo('members');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
@action
|
||||||
|
close() {
|
||||||
|
this.router.transitionTo('members');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,24 +1,26 @@
|
||||||
import AuthenticatedRoute from 'ghost-admin/routes/authenticated';
|
import AuthenticatedRoute from 'ghost-admin/routes/authenticated';
|
||||||
import CurrentUserSettings from 'ghost-admin/mixins/current-user-settings';
|
import CurrentUserSettings from 'ghost-admin/mixins/current-user-settings';
|
||||||
|
import classic from 'ember-classic-decorator';
|
||||||
|
import {action} from '@ember/object';
|
||||||
import {inject as service} from '@ember/service';
|
import {inject as service} from '@ember/service';
|
||||||
|
|
||||||
export default AuthenticatedRoute.extend(CurrentUserSettings, {
|
@classic
|
||||||
router: service(),
|
export default class MembersRoute extends AuthenticatedRoute.extend(CurrentUserSettings) {
|
||||||
|
@service router;
|
||||||
|
|
||||||
_requiresBackgroundRefresh: true,
|
_requiresBackgroundRefresh = true;
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this._super(...arguments);
|
super.init(...arguments);
|
||||||
this.router.on('routeWillChange', (transition) => {
|
this.router.on('routeWillChange', (transition) => {
|
||||||
this.showUnsavedChangesModal(transition);
|
this.showUnsavedChangesModal(transition);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
beforeModel() {
|
beforeModel() {
|
||||||
this._super(...arguments);
|
super.beforeModel(...arguments);
|
||||||
return this.get('session.user')
|
return this.session.user.then(this.transitionAuthor());
|
||||||
.then(this.transitionAuthor());
|
}
|
||||||
},
|
|
||||||
|
|
||||||
model(params) {
|
model(params) {
|
||||||
this._requiresBackgroundRefresh = false;
|
this._requiresBackgroundRefresh = false;
|
||||||
|
@ -28,33 +30,30 @@ export default AuthenticatedRoute.extend(CurrentUserSettings, {
|
||||||
} else {
|
} else {
|
||||||
return this.store.createRecord('member');
|
return this.store.createRecord('member');
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
setupController(controller, member) {
|
setupController(controller, member) {
|
||||||
this._super(...arguments);
|
super.setupController(...arguments);
|
||||||
if (this._requiresBackgroundRefresh) {
|
if (this._requiresBackgroundRefresh) {
|
||||||
controller.fetchMember.perform(member.get('id'));
|
controller.fetchMemberTask.perform(member.get('id'));
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
deactivate() {
|
deactivate() {
|
||||||
this._super(...arguments);
|
super.deactivate(...arguments);
|
||||||
|
|
||||||
// clean up newly created records and revert unsaved changes to existing
|
// clean up newly created records and revert unsaved changes to existing
|
||||||
this.controller.member.rollbackAttributes();
|
this.controller.member.rollbackAttributes();
|
||||||
|
|
||||||
this._requiresBackgroundRefresh = true;
|
this._requiresBackgroundRefresh = true;
|
||||||
},
|
}
|
||||||
|
|
||||||
actions: {
|
@action
|
||||||
save() {
|
save() {
|
||||||
this.controller.send('save');
|
this.controller.save();
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
titleToken() {
|
titleToken() {
|
||||||
return this.controller.get('member.name');
|
return this.controller.member.name;
|
||||||
},
|
}
|
||||||
|
|
||||||
showUnsavedChangesModal(transition) {
|
showUnsavedChangesModal(transition) {
|
||||||
if (transition.from && transition.from.name === this.routeName && transition.targetName) {
|
if (transition.from && transition.from.name === this.routeName && transition.targetName) {
|
||||||
|
@ -65,9 +64,9 @@ export default AuthenticatedRoute.extend(CurrentUserSettings, {
|
||||||
|
|
||||||
if (!controller.member.isDeleted && isChanged) {
|
if (!controller.member.isDeleted && isChanged) {
|
||||||
transition.abort();
|
transition.abort();
|
||||||
controller.send('toggleUnsavedChangesModal', transition);
|
controller.toggleUnsavedChangesModal(transition);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import MemberRoute from '../member';
|
import MemberRoute from '../member';
|
||||||
|
|
||||||
export default MemberRoute.extend({
|
export default class NewMemberRoute extends MemberRoute {
|
||||||
controllerName: 'member',
|
controllerName = 'member';
|
||||||
templateName: 'member'
|
templateName = 'member';
|
||||||
});
|
}
|
||||||
|
|
|
@ -1,45 +1,44 @@
|
||||||
import AuthenticatedRoute from 'ghost-admin/routes/authenticated';
|
import AuthenticatedRoute from 'ghost-admin/routes/authenticated';
|
||||||
import {inject as service} from '@ember/service';
|
import {inject as service} from '@ember/service';
|
||||||
|
|
||||||
export default AuthenticatedRoute.extend({
|
export default class MembersRoute extends AuthenticatedRoute {
|
||||||
config: service(),
|
@service config;
|
||||||
|
|
||||||
queryParams: {
|
queryParams = {
|
||||||
label: {refreshModel: true}
|
label: {refreshModel: true}
|
||||||
},
|
};
|
||||||
|
|
||||||
// redirect to posts screen if:
|
// redirect to posts screen if:
|
||||||
// - TODO: members is disabled?
|
// - TODO: members is disabled?
|
||||||
// - logged in user isn't owner/admin
|
// - logged in user isn't owner/admin
|
||||||
beforeModel() {
|
beforeModel() {
|
||||||
this._super(...arguments);
|
super.beforeModel(...arguments);
|
||||||
|
|
||||||
return this.session.user.then((user) => {
|
return this.session.user.then((user) => {
|
||||||
if (!user.isOwnerOrAdmin) {
|
if (!user.isOwnerOrAdmin) {
|
||||||
return this.transitionTo('home');
|
return this.transitionTo('home');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
// trigger a background load of labels for filter dropdown
|
// trigger a background load of labels for filter dropdown
|
||||||
setupController(controller) {
|
setupController(controller) {
|
||||||
this._super(...arguments);
|
super.setupController(...arguments);
|
||||||
controller.fetchMembers.perform();
|
controller.fetchMembersTask.perform();
|
||||||
if (!controller._hasLoadedLabels) {
|
if (!controller.hasLoadedLabels) {
|
||||||
this.store.query('label', {limit: 'all'}).then(() => {
|
this.store.query('label', {limit: 'all'}).then(() => {
|
||||||
controller._hasLoadedLabels = true;
|
controller.hasLoadedLabels = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
deactivate() {
|
deactivate() {
|
||||||
this._super(...arguments);
|
super.deactivate(...arguments);
|
||||||
this.controller.modalLabel && this.controller.modalLabel.rollbackAttributes();
|
this.controller.modalLabel && this.controller.modalLabel.rollbackAttributes();
|
||||||
},
|
}
|
||||||
|
|
||||||
buildRouteInfoMetadata() {
|
buildRouteInfoMetadata() {
|
||||||
return {
|
return {
|
||||||
titleToken: 'Members'
|
titleToken: 'Members'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import Route from '@ember/routing/route';
|
import Route from '@ember/routing/route';
|
||||||
|
|
||||||
export default Route.extend({
|
export default class MembersImportRoute extends Route {}
|
||||||
});
|
|
||||||
|
|
|
@ -15,13 +15,13 @@
|
||||||
{{#unless this.member.isNew}}
|
{{#unless this.member.isNew}}
|
||||||
<button
|
<button
|
||||||
class="gh-btn gh-btn-white gh-btn-icon mr2"
|
class="gh-btn gh-btn-white gh-btn-icon mr2"
|
||||||
{{on "click" (action "toggleImpersonateMemberModal")}}>
|
{{on "click" this.toggleImpersonateMemberModal}}>
|
||||||
<span>Impersonate</span>
|
<span>Impersonate</span>
|
||||||
</button>
|
</button>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<GhTaskButton @class="gh-btn gh-btn-blue gh-btn-icon" @type="button" @task={{this.save}} @autoReset={{true}} @data-test-button="save" />
|
<GhTaskButton @class="gh-btn gh-btn-blue gh-btn-icon" @type="button" @task={{this.saveTask}} @autoReset={{true}} @data-test-button="save" />
|
||||||
</section>
|
</section>
|
||||||
</GhCanvasHeader>
|
</GhCanvasHeader>
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@
|
||||||
<GhMemberSettingsForm
|
<GhMemberSettingsForm
|
||||||
@member={{this.member}}
|
@member={{this.member}}
|
||||||
@scratchMember={{this.scratchMember}}
|
@scratchMember={{this.scratchMember}}
|
||||||
@setProperty={{action "setProperty"}}
|
@setProperty={{this.setProperty}}
|
||||||
@isLoading={{this.isLoading}} />
|
@isLoading={{this.isLoading}} />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="gh-btn gh-btn-red gh-btn-icon mt3"
|
class="gh-btn gh-btn-red gh-btn-icon mt3"
|
||||||
{{on "click" (action "toggleDeleteMemberModal")}}
|
{{on "click" this.toggleDeleteMemberModal}}
|
||||||
data-test-button="delete-member"
|
data-test-button="delete-member"
|
||||||
>
|
>
|
||||||
<span>Delete member</span>
|
<span>Delete member</span>
|
||||||
|
@ -86,8 +86,8 @@
|
||||||
{{#if this.showUnsavedChangesModal}}
|
{{#if this.showUnsavedChangesModal}}
|
||||||
<GhFullscreenModal
|
<GhFullscreenModal
|
||||||
@modal="leave-settings"
|
@modal="leave-settings"
|
||||||
@confirm={{action "leaveScreen"}}
|
@confirm={{this.leaveScreen}}
|
||||||
@close={{action "toggleUnsavedChangesModal"}}
|
@close={{this.toggleUnsavedChangesModal}}
|
||||||
@modifier="action wide" />
|
@modifier="action wide" />
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
@ -95,8 +95,8 @@
|
||||||
<GhFullscreenModal
|
<GhFullscreenModal
|
||||||
@modal="delete-member"
|
@modal="delete-member"
|
||||||
@model={{this.member}}
|
@model={{this.member}}
|
||||||
@confirm={{action "deleteMember"}}
|
@confirm={{this.deleteMember}}
|
||||||
@close={{action "toggleDeleteMemberModal"}}
|
@close={{this.toggleDeleteMemberModal}}
|
||||||
@modifier="action wide" />
|
@modifier="action wide" />
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
@ -104,6 +104,6 @@
|
||||||
<GhFullscreenModal
|
<GhFullscreenModal
|
||||||
@modal="impersonate-member"
|
@modal="impersonate-member"
|
||||||
@model={{this.member}}
|
@model={{this.member}}
|
||||||
@close={{action "toggleImpersonateMemberModal"}}
|
@close={{this.toggleImpersonateMemberModal}}
|
||||||
@modifier="action wide" />
|
@modifier="action wide" />
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
<GhMembersContentfilter
|
<GhMembersContentfilter
|
||||||
@selectedLabel={{this.selectedLabel}}
|
@selectedLabel={{this.selectedLabel}}
|
||||||
@availableLabels={{this.availableLabels}}
|
@availableLabels={{this.availableLabels}}
|
||||||
@onLabelChange={{action "changeLabel"}}
|
@onLabelChange={{this.changeLabel}}
|
||||||
@onLabelAdd={{action "addLabel"}}
|
@onLabelAdd={{this.addLabel}}
|
||||||
@onLabelEdit={{action "editLabel"}}
|
@onLabelEdit={{this.editLabel}}
|
||||||
/>
|
/>
|
||||||
<div class="relative gh-members-header-search">
|
<div class="relative gh-members-header-search">
|
||||||
{{svg-jar "search" class="gh-input-search-icon"}}
|
{{svg-jar "search" class="gh-input-search-icon"}}
|
||||||
|
@ -34,9 +34,9 @@
|
||||||
</LinkTo>
|
</LinkTo>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="#" {{action 'exportData'}} class="mr2">
|
<button class="mr2" {{on "click" this.exportData}}>
|
||||||
<span>Export all members</span>
|
<span>Export all members</span>
|
||||||
</a>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</GhDropdown>
|
</GhDropdown>
|
||||||
</span>
|
</span>
|
||||||
|
@ -93,7 +93,7 @@
|
||||||
<GhFullscreenModal
|
<GhFullscreenModal
|
||||||
@modal="members-label-form"
|
@modal="members-label-form"
|
||||||
@model={{this.labelModalData}}
|
@model={{this.labelModalData}}
|
||||||
@close={{action "toggleLabelModal"}}
|
@close={{this.toggleLabelModal}}
|
||||||
@modifier="action wide"
|
@modifier="action wide"
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
|
@ -52,6 +52,7 @@
|
||||||
"ember-ajax": "5.0.0",
|
"ember-ajax": "5.0.0",
|
||||||
"ember-assign-helper": "0.2.0",
|
"ember-assign-helper": "0.2.0",
|
||||||
"ember-auto-import": "1.5.3",
|
"ember-auto-import": "1.5.3",
|
||||||
|
"ember-classic-decorator": "1.0.8",
|
||||||
"ember-cli": "3.18.0",
|
"ember-cli": "3.18.0",
|
||||||
"ember-cli-app-version": "3.2.0",
|
"ember-cli-app-version": "3.2.0",
|
||||||
"ember-cli-babel": "7.20.0",
|
"ember-cli-babel": "7.20.0",
|
||||||
|
|
18
yarn.lock
18
yarn.lock
|
@ -837,7 +837,7 @@
|
||||||
globals "^11.1.0"
|
globals "^11.1.0"
|
||||||
lodash "^4.17.13"
|
lodash "^4.17.13"
|
||||||
|
|
||||||
"@babel/types@^7.1.6", "@babel/types@^7.3.2", "@babel/types@^7.3.4", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.7.2", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0", "@babel/types@^7.9.5", "@babel/types@^7.9.6":
|
"@babel/types@^7.1.6", "@babel/types@^7.3.2", "@babel/types@^7.3.4", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.7.2", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0", "@babel/types@^7.9.5", "@babel/types@^7.9.6":
|
||||||
version "7.9.6"
|
version "7.9.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7"
|
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7"
|
||||||
integrity sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==
|
integrity sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==
|
||||||
|
@ -2269,6 +2269,14 @@ babel-plugin-ember-modules-api-polyfill@^2.13.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
ember-rfc176-data "^0.3.13"
|
ember-rfc176-data "^0.3.13"
|
||||||
|
|
||||||
|
babel-plugin-filter-imports@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/babel-plugin-filter-imports/-/babel-plugin-filter-imports-3.0.0.tgz#a849683837ad29960da17492fb32789ab6b09a11"
|
||||||
|
integrity sha512-p/chjzVTgCxUqyLM0q/pfWVZS7IJTwGQMwNg0LOvuQpKiTftQgZDtkGB8XvETnUw19rRcL7bJCTopSwibTN2tA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/types" "^7.4.0"
|
||||||
|
lodash "^4.17.11"
|
||||||
|
|
||||||
babel-plugin-filter-imports@^4.0.0:
|
babel-plugin-filter-imports@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/babel-plugin-filter-imports/-/babel-plugin-filter-imports-4.0.0.tgz#068f8da15236a96a9602c36dc6f4a6eeca70a4f4"
|
resolved "https://registry.yarnpkg.com/babel-plugin-filter-imports/-/babel-plugin-filter-imports-4.0.0.tgz#068f8da15236a96a9602c36dc6f4a6eeca70a4f4"
|
||||||
|
@ -5195,6 +5203,14 @@ ember-basic-dropdown@^3.0.1:
|
||||||
ember-maybe-in-element "^0.4.0"
|
ember-maybe-in-element "^0.4.0"
|
||||||
ember-truth-helpers "2.1.0"
|
ember-truth-helpers "2.1.0"
|
||||||
|
|
||||||
|
ember-classic-decorator@1.0.8:
|
||||||
|
version "1.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/ember-classic-decorator/-/ember-classic-decorator-1.0.8.tgz#e290e5b0b1a31a569587a85a9c5c7a2f1242cabb"
|
||||||
|
integrity sha512-IsCDJ7rLsrFjYtgi9UXUmjzUQJaaJzmy/gKwGGtZ6kZwT5yhzSbScRi0P6Cb0guJPtlMMCE0sAQpJRbXmBb/gA==
|
||||||
|
dependencies:
|
||||||
|
babel-plugin-filter-imports "^3.0.0"
|
||||||
|
ember-cli-babel "^7.11.1"
|
||||||
|
|
||||||
ember-cli-app-version@3.2.0:
|
ember-cli-app-version@3.2.0:
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/ember-cli-app-version/-/ember-cli-app-version-3.2.0.tgz#7b9ad0e1b63ae0518648356ee24c703e922bc26e"
|
resolved "https://registry.yarnpkg.com/ember-cli-app-version/-/ember-cli-app-version-3.2.0.tgz#7b9ad0e1b63ae0518648356ee24c703e922bc26e"
|
||||||
|
|
Loading…
Reference in New Issue