diff --git a/ghost/admin/app/components/gh-member-avatar.js b/ghost/admin/app/components/gh-member-avatar.js index abffe1d1b4..4f1ec98979 100644 --- a/ghost/admin/app/components/gh-member-avatar.js +++ b/ghost/admin/app/components/gh-member-avatar.js @@ -1,5 +1,4 @@ import Component from '@glimmer/component'; -import {get} from '@ember/object'; import {htmlSafe} from '@ember/template'; const stringToHslColor = function (str, saturation, lightness) { @@ -16,15 +15,14 @@ export default class GhMemberAvatarComponent extends Component { get memberName() { let {member} = this.args; - // can be given a proxy object from a sparse array so get is required - return get(member, 'name') || get(member, 'email') || 'NM'; + return member?.name || member?.email || 'NM'; } get avatarImage() { let {member} = this.args; // to cover both ways avatar image is returned depending on where member data comes from - return get(member, 'avatar_image') || get(member, 'avatarImage') || null; + return member?.avatar_image || member?.avatarImage || null; } get backgroundStyle() { diff --git a/ghost/admin/app/components/gh-members-list-item-loading.hbs b/ghost/admin/app/components/gh-members-list-item-loading.hbs new file mode 100644 index 0000000000..6442223d5a --- /dev/null +++ b/ghost/admin/app/components/gh-members-list-item-loading.hbs @@ -0,0 +1,12 @@ + +
+
+
+
+
+
+
+ {{#each @filterColumns}} +
+ {{/each}} + \ No newline at end of file diff --git a/ghost/admin/app/components/gh-members-list-item.hbs b/ghost/admin/app/components/gh-members-list-item.hbs index 42cf9af6a2..08e98c0990 100644 --- a/ghost/admin/app/components/gh-members-list-item.hbs +++ b/ghost/admin/app/components/gh-members-list-item.hbs @@ -1,82 +1,69 @@ - {{#if @member.is_loading}} -
-
-
-
-
-
-
- {{#each @filterColumns}} -
- {{/each}} - {{else}} - -
- -
-

{{or @member.name @member.email}}

- {{#if @member.name}} -

{{@member.email}}

- {{/if}} -
+ +
+ +
+

{{or @member.name @member.email}}

+ {{#if @member.name}} +

{{@member.email}}

+ {{/if}}
- - {{#if this.hasMultipleTiers}} - - {{#if (not (is-empty @member.status))}} - {{capitalize @member.status}} - {{else}} - - - {{/if}} -
{{this.tiers}}
-
- {{else}} - - {{#if (not (is-empty @member.status))}} - {{capitalize @member.status}} - {{else}} - - - {{/if}} - - {{/if}} - {{#if @newsletterEnabled}} - {{#if (feature "emailAnalytics")}} - - {{#if (not (is-empty @member.emailOpenRate))}} - {{@member.emailOpenRate}}% - {{else}} - N/A - {{/if}} - - {{/if}} - {{/if}} - - - {{#if (and @member.geolocation @member.geolocation.country)}} - {{#if (and (eq @member.geolocation.country_code "US") @member.geolocation.region)}} - {{@member.geolocation.region}}, US - {{else}} - {{#if @member.geolocation.country}} - {{@member.geolocation.country}} - {{else}} - Unknown - {{/if}} - {{/if}} +
+
+ {{#if this.hasMultipleTiers}} + + {{#if (not (is-empty @member.status))}} + {{capitalize @member.status}} {{else}} - Unknown + - + {{/if}} +
{{this.tiers}}
+
+ {{else}} + + {{#if (not (is-empty @member.status))}} + {{capitalize @member.status}} + {{else}} + - {{/if}} - - - {{#if @member.createdAtUTC}} -
{{moment-format (moment-site-tz @member.createdAtUTC) "DD MMM YYYY"}}
-
{{moment-from-now @member.createdAtUTC}}
- {{/if}} -
- - {{#each @filterColumns as |filterColumn|}} - - {{/each}} {{/if}} + {{#if @newsletterEnabled}} + {{#if (feature "emailAnalytics")}} + + {{#if (not (is-empty @member.emailOpenRate))}} + {{@member.emailOpenRate}}% + {{else}} + N/A + {{/if}} + + {{/if}} + {{/if}} + + + {{#if (and @member.geolocation @member.geolocation.country)}} + {{#if (and (eq @member.geolocation.country_code "US") @member.geolocation.region)}} + {{@member.geolocation.region}}, US + {{else}} + {{#if @member.geolocation.country}} + {{@member.geolocation.country}} + {{else}} + Unknown + {{/if}} + {{/if}} + {{else}} + Unknown + {{/if}} + + + + {{#if @member.createdAtUTC}} +
{{moment-format (moment-site-tz @member.createdAtUTC) "DD MMM YYYY"}}
+
{{moment-from-now @member.createdAtUTC}}
+ {{/if}} +
+ + {{#each @filterColumns as |filterColumn|}} + + {{/each}} diff --git a/ghost/admin/app/components/gh-members-list-item.js b/ghost/admin/app/components/gh-members-list-item.js index 9330a59c51..4b3d22db8b 100644 --- a/ghost/admin/app/components/gh-members-list-item.js +++ b/ghost/admin/app/components/gh-members-list-item.js @@ -1,5 +1,4 @@ import Component from '@glimmer/component'; -import {get} from '@ember/object'; import {inject as service} from '@ember/service'; export default class GhMembersListItem extends Component { @@ -14,7 +13,7 @@ export default class GhMembersListItem extends Component { } get tiers() { - const tierData = get(this.args.member, 'tiers') || []; + const tierData = this.args.member?.tiers || []; return tierData.map(tier => tier.name).join(', '); } } diff --git a/ghost/admin/app/components/member/newsletter-preference.js b/ghost/admin/app/components/member/newsletter-preference.js index d31ad6a397..9f957b8d05 100644 --- a/ghost/admin/app/components/member/newsletter-preference.js +++ b/ghost/admin/app/components/member/newsletter-preference.js @@ -1,5 +1,5 @@ import Component from '@glimmer/component'; -import {action, get} from '@ember/object'; +import {action} from '@ember/object'; import {tracked} from '@glimmer/tracking'; export default class MembersNewsletterPreference extends Component { @@ -15,7 +15,7 @@ export default class MembersNewsletterPreference extends Component { return { name: d.name, description: d.description, - subscribed: !!this.args.member?.get('newsletters')?.find((n) => { + subscribed: !!this.args.member?.newsletters?.find((n) => { return n.id === d.id; }), id: d.id, @@ -34,14 +34,12 @@ export default class MembersNewsletterPreference extends Component { return d.id === newsletter.id; }); - // get() is required because member can be a proxy object when loaded - // directly from the members list if (!event.target.checked) { - updatedNewsletters = get(this.args.member, 'newsletters').filter((d) => { + updatedNewsletters = this.args.member.newsletters.filter((d) => { return d.id !== newsletter.id; }); } else { - updatedNewsletters = get(this.args.member, 'newsletters').filter((d) => { + updatedNewsletters = this.args.member.newsletters.filter((d) => { return d.id !== newsletter.id; }).concat(selectedNewsletter); } diff --git a/ghost/admin/app/controllers/member.js b/ghost/admin/app/controllers/member.js index 96857501b2..563b9f11be 100644 --- a/ghost/admin/app/controllers/member.js +++ b/ghost/admin/app/controllers/member.js @@ -68,9 +68,8 @@ export default class MemberController extends Controller { } get subscribedAt() { - // member can be a proxy object in a sparse array so .get is required - let memberSince = moment(this.member.get('createdAtUTC')).from(moment()); - let createdDate = moment(this.member.get('createdAtUTC')).format('D MMM YYYY'); + let memberSince = moment(this.member.createdAtUTC).from(moment()); + let createdDate = moment(this.member.createdAtUTC).format('D MMM YYYY'); return `${createdDate} (${memberSince})`; } @@ -133,7 +132,7 @@ export default class MemberController extends Controller { // if Cmd+S is pressed before the field loses focus make sure we're // saving the intended property values let scratchProps = scratchMember.getProperties(SCRATCH_PROPS); - member.setProperties(scratchProps); + Object.assign(member, scratchProps); try { yield member.save(); @@ -178,7 +177,7 @@ export default class MemberController extends Controller { // Private ----------------------------------------------------------------- _saveMemberProperty(propKey, newValue) { - let currentValue = this.member.get(propKey); + let currentValue = this.member[propKey]; if (newValue && typeof newValue === 'string') { newValue = newValue.trim(); @@ -189,6 +188,6 @@ export default class MemberController extends Controller { return; } - this.member.set(propKey, newValue); + this.member[propKey] = newValue; } } diff --git a/ghost/admin/app/routes/member.js b/ghost/admin/app/routes/member.js index 905b70be4e..b1795592a2 100644 --- a/ghost/admin/app/routes/member.js +++ b/ghost/admin/app/routes/member.js @@ -30,9 +30,7 @@ export default class MembersRoute extends AdminRoute { setupController(controller, member) { super.setupController(...arguments); if (this._requiresBackgroundRefresh) { - // `member` is passed directly in `` so it can be a proxy - // object used by the sparse list requiring the use of .get() - controller.fetchMemberTask.perform(member.get('id')); + controller.fetchMemberTask.perform(member.id); } } diff --git a/ghost/admin/app/routes/offer.js b/ghost/admin/app/routes/offer.js index 1f2bf1643c..d90111cffa 100644 --- a/ghost/admin/app/routes/offer.js +++ b/ghost/admin/app/routes/offer.js @@ -23,9 +23,7 @@ export default class OffersRoute extends AdminRoute { super.setupController(...arguments); if (this._requiresBackgroundRefresh) { - // `offer` is passed directly in `` so it can be a proxy - // object used by the sparse list requiring the use of .get() - controller.fetchOfferTask.perform(offer.get('id')); + controller.fetchOfferTask.perform(offer.id); } } diff --git a/ghost/admin/app/templates/members.hbs b/ghost/admin/app/templates/members.hbs index 7d821b1165..903c9a944b 100644 --- a/ghost/admin/app/templates/members.hbs +++ b/ghost/admin/app/templates/members.hbs @@ -126,12 +126,19 @@ - + {{#if member.is_loading}} + + {{else}} + + {{/if}}