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:
Kevin Ansfield 2020-05-20 14:55:41 +01:00
parent 9250d7939b
commit e80fa137db
14 changed files with 289 additions and 242 deletions

View File

@ -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',

View File

@ -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

View File

@ -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));
} }

View File

@ -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);
} }
}); }

View File

@ -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;
}) }
}); }

View File

@ -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');
}
}

View File

@ -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;
} }
} }
} }
}); }

View File

@ -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';
}); }

View File

@ -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'
}; };
} }
}
});

View File

@ -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 {}
});

View File

@ -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}}

View File

@ -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}}

View File

@ -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",

View File

@ -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"