0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-06 22:40:14 -05:00

🐛 Fixed members breadcrumbs when not coming from analytics

fixes https://github.com/TryGhost/Team/issues/2404

This change introduces a new 'post' query parameter to the members and member routes.

Previously, the members route would check if the previous route was the analytics page, and then show the breadcrumbs to go back to the analytics page. But when navigating to the members page from the menu, we don't want to show the breadcrumbs. To accomplish this, the routes that point to the members page from the analytics page now specifically pass on the post id in the query parameters. The query parameter is then passed on from the members page to the member page.

`directlyFromAnalytics` is still used in the member route, to know wheter we came from the members page or from the analytics page (changes the breadcrumbs). This doesn't need to go via a query parameter (figured that would make the url too long/complex).

The resetController method is now implemented and resets the filter and/or fromAnalytics post id if required (when going from members to member, we don't want to reset it because the we would lose the filter going back).
This commit is contained in:
Simon Backx 2023-05-04 11:11:08 +02:00
parent b407676b94
commit 17a6217cc7
10 changed files with 67 additions and 70 deletions

View file

@ -1,4 +1,4 @@
<LinkTo @route="member" @model={{@member}} class="gh-list-data middarkgrey f8" data-test-table-data={{this.columnName}}>
<LinkTo @route="member" @model={{@member}} @query={{@query}} class="gh-list-data middarkgrey f8" data-test-table-data={{this.columnName}}>
{{#if this.columnValue}}
<div class={{this.columnValue.class}}>
{{#if this.columnValue.icon}}

View file

@ -1,5 +1,5 @@
<tr data-test-list='members-list-item' data-test-member={{@member.id}}>
<LinkTo @route="member" @model={{@member}} class="gh-list-data" data-test-table-data="details">
<LinkTo @route="member" @model={{@member}} @query={{@query}} class="gh-list-data" data-test-table-data="details">
<div class="flex items-center">
<GhMemberAvatar @member={{@member}} @containerClass="w9 h9 mr3 flex-shrink-0" />
<div class="w-80">
@ -11,7 +11,7 @@
</div>
</LinkTo>
{{#if this.hasMultipleTiers}}
<LinkTo @route="member" @model={{@member}} class="gh-list-data" data-test-table-data="status">
<LinkTo @route="member" @model={{@member}} @query={{@query}} class="gh-list-data" data-test-table-data="status">
{{#if (not (is-empty @member.status))}}
<span>{{capitalize @member.status}}</span>
{{else}}
@ -20,7 +20,7 @@
<div class="midgrey">{{this.tiers}}</div>
</LinkTo>
{{else}}
<LinkTo @route="member" @model={{@member}} class="gh-list-data" data-test-table-data="status">
<LinkTo @route="member" @model={{@member}} @query={{@query}} class="gh-list-data" data-test-table-data="status">
{{#if (not (is-empty @member.status))}}
<span>{{capitalize @member.status}}</span>
{{else}}
@ -29,7 +29,7 @@
</LinkTo>
{{/if}}
{{#if @newsletterEnabled}}
<LinkTo @route="member" @model={{@member}} class="gh-list-data middarkgrey f8 {{unless @member.name "gh-members-list-open-rate-noname"}}" data-test-table-data="open-rate">
<LinkTo @route="member" @model={{@member}} @query={{@query}} class="gh-list-data middarkgrey f8 {{unless @member.name "gh-members-list-open-rate-noname"}}" data-test-table-data="open-rate">
{{#if (not (is-empty @member.emailOpenRate))}}
<span>{{@member.emailOpenRate}}%</span>
{{else}}
@ -38,7 +38,7 @@
</LinkTo>
{{/if}}
<LinkTo @route="member" @model={{@member}} class="gh-list-data middarkgrey f8 {{unless @member.name "gh-members-geolocation-noname"}}" data-test-table-data="location">
<LinkTo @route="member" @model={{@member}} @query={{@query}} class="gh-list-data middarkgrey f8 {{unless @member.name "gh-members-geolocation-noname"}}" data-test-table-data="location">
{{#if (and @member.geolocation @member.geolocation.country)}}
{{#if (and (eq @member.geolocation.country_code "US") @member.geolocation.region)}}
{{@member.geolocation.region}}, US
@ -54,7 +54,7 @@
{{/if}}
</LinkTo>
<LinkTo @route="member" @model={{@member}} class="gh-list-data middarkgrey f8" data-test-table-data="created-at">
<LinkTo @route="member" @model={{@member}} @query={{@query}} class="gh-list-data middarkgrey f8" data-test-table-data="created-at">
{{#if @member.createdAtUTC}}
<div>{{moment-format (moment-site-tz @member.createdAtUTC) "DD MMM YYYY"}}</div>
<div class="midlightgrey gh-members-list-subscribed-moment">{{moment-from-now @member.createdAtUTC}}</div>
@ -62,6 +62,6 @@
</LinkTo>
{{#each @filterColumns as |filterColumn|}}
<Members::ListItemColumn @member={{@member}} @filterColumn={{filterColumn}} />
<Members::ListItemColumn @member={{@member}} @filterColumn={{filterColumn}} @query={{@query}} />
{{/each}}
</tr>

View file

@ -46,7 +46,7 @@
<div class="gh-dashboard-list-item">
<div class="gh-dashboard-list-item-sub">
<GhMemberAvatar @member={{parsedEvent.member}} @containerClass="w6 h6 mr3 flex-shrink-0" />
<LinkTo class="gh-dashboard-list-text" @route="member" @model="{{parsedEvent.memberId}}">{{parsedEvent.subject}}</LinkTo>
<LinkTo class="gh-dashboard-list-text" @route="member" @model="{{parsedEvent.memberId}}" @query={{hash postAnalytics=@post.id}}>{{parsedEvent.subject}}</LinkTo>
</div>
<div class="gh-dashboard-list-item-sub">
{{svg-jar parsedEvent.icon }}
@ -95,7 +95,7 @@
<LinkTo
class="gh-post-activity-feed-pagination-link"
@route="members"
@query={{@linkQuery}}
@query={{hash @linkQuery postAnalytics=@post.id}}
>
{{svg-jar "filter"}}
View members

View file

@ -3,7 +3,7 @@
<LinkTo
class="gh-post-activity-feed-pagination-link"
@route="members"
@query={{hash filterParam=(concat "emails.post_id:" @post.id) }}
@query={{hash filterParam=(concat "emails.post_id:" @post.id) postAnalytics=@post.id }}
>
Sent
</LinkTo>
@ -13,7 +13,7 @@
<LinkTo
class="gh-post-activity-feed-pagination-link"
@route="members"
@query={{hash filterParam=(concat "opened_emails.post_id:" @post.id) }}
@query={{hash filterParam=(concat "opened_emails.post_id:" @post.id) postAnalytics=@post.id }}
>
Opened
</LinkTo>
@ -23,7 +23,7 @@
<LinkTo
class="gh-post-activity-feed-pagination-link"
@route="members"
@query={{hash filterParam=(concat "clicked_links.post_id:" @post.id) }}
@query={{hash filterParam=(concat "clicked_links.post_id:" @post.id) postAnalytics=@post.id }}
>
Clicked
</LinkTo>
@ -34,7 +34,7 @@
<LinkTo
class="gh-post-activity-feed-pagination-link"
@route="members"
@query={{hash filterParam=link.filterParam}}
@query={{hash filterParam=link.filterParam postAnalytics=@post.id}}
>
{{link.label}}
</LinkTo>

View file

@ -19,6 +19,10 @@ export default class MemberController extends Controller {
@service router;
@service store;
queryParams = [
{postAnalytics: 'post'}
];
@tracked isLoading = false;
@tracked showImpersonateMemberModal = false;
@tracked modalLabel = null;
@ -27,8 +31,15 @@ export default class MemberController extends Controller {
_previousLabels = null;
_previousNewsletters = null;
directlyFromAnalytics = false;
fromAnalytics = null;
@tracked directlyFromAnalytics = false;
@tracked postAnalytics = null;
get fromAnalytics() {
if (!this.postAnalytics) {
return null;
}
return [this.postAnalytics];
}
constructor() {
super(...arguments);

View file

@ -44,7 +44,8 @@ export default class MembersController extends Controller {
{paidParam: 'paid'},
{searchParam: 'search'},
{orderParam: 'order'},
{filterParam: 'filter'}
{filterParam: 'filter'},
{postAnalytics: 'post'}
];
@tracked members = A([]);
@ -67,7 +68,14 @@ export default class MembersController extends Controller {
/**
* Flag used to determine if we should return to the analytics page
*/
fromAnalytics = null;
@tracked postAnalytics = null;
get fromAnalytics() {
if (!this.postAnalytics) {
return null;
}
return [this.postAnalytics];
}
paidParams = PAID_PARAMS;

View file

@ -8,8 +8,11 @@ export default class MembersRoute extends AdminRoute {
@service modals;
@service router;
queryParams = {
postAnalytics: {refreshModel: false}
};
_requiresBackgroundRefresh = true;
fromAnalytics = null;
constructor() {
super(...arguments);
@ -39,25 +42,17 @@ export default class MembersRoute extends AdminRoute {
controller.directlyFromAnalytics = false;
if (transition.from?.name === 'posts.analytics') {
// Sadly transition.from.params is not reliable to use (not populated on transitions)
const oldParams = transition.router?.oldState?.params['posts.analytics'] ?? {};
// We need to store analytics in 'this' to have it accessible for the member route
this.fromAnalytics = Object.values(oldParams);
controller.fromAnalytics = this.fromAnalytics;
controller.directlyFromAnalytics = true;
} else if (transition.from?.metadata?.fromAnalytics) {
// Handle returning from member route
const fromAnalytics = transition.from?.metadata.fromAnalytics ?? null;
controller.fromAnalytics = fromAnalytics;
this.fromAnalytics = fromAnalytics;
} else if (transition.from?.name === 'members.index' && transition.from?.parent?.name === 'members') {
const fromAnalytics = transition.from?.parent?.metadata.fromAnalytics ?? null;
controller.fromAnalytics = fromAnalytics;
this.fromAnalytics = fromAnalytics;
} else {
controller.fromAnalytics = null;
this.fromAnalytics = null;
}
}
resetController(controller, isExiting) {
super.resetController(...arguments);
// Make sure we clear
if (isExiting && controller.postAnalytics) {
controller.set('postAnalytics', null);
controller.set('directlyFromAnalytics', false);
}
}
@ -123,10 +118,4 @@ export default class MembersRoute extends AdminRoute {
titleToken() {
return this.controller.member.name;
}
buildRouteInfoMetadata() {
return {
fromAnalytics: this.fromAnalytics
};
}
}

View file

@ -5,14 +5,13 @@ export default class MembersRoute extends AdminRoute {
@service store;
@service feature;
fromAnalytics = false;
queryParams = {
label: {refreshModel: true},
searchParam: {refreshModel: true, replace: true},
paidParam: {refreshModel: true},
orderParam: {refreshModel: true},
filterParam: {refreshModel: true}
filterParam: {refreshModel: true},
postAnalytics: {refreshModel: false}
};
beforeModel() {
@ -26,39 +25,28 @@ export default class MembersRoute extends AdminRoute {
}
// trigger a background load of members plus labels for filter dropdown
setupController(controller, model, transition) {
setupController(controller) {
super.setupController(...arguments);
controller.fetchLabelsTask.perform();
if (transition.from?.name === 'posts.analytics') {
// Sadly transition.from.params is not reliable to use (not populated on transitions)
const oldParams = transition.router?.oldState?.params['posts.analytics'] ?? {};
// We need to store analytics in 'this' to have it accessible for the member route
this.fromAnalytics = Object.values(oldParams);
controller.fromAnalytics = this.fromAnalytics;
} else if (transition.from?.metadata?.fromAnalytics) {
// Handle returning from member route
const fromAnalytics = transition.from?.metadata.fromAnalytics ?? null;
controller.fromAnalytics = fromAnalytics;
this.fromAnalytics = fromAnalytics;
} else {
controller.fromAnalytics = null;
this.fromAnalytics = null;
}
}
resetController() {
resetController(controller, _isExiting, transition) {
super.resetController(...arguments);
// don't reset fromAnalytics here, we need to reuse it. We reset it in setup
//controller.fromAnalytics = null;
if (controller.postAnalytics) {
controller.set('postAnalytics', null);
// Only reset filters if we are not going to member route
// Otherwise the filters will be gone if we return
if (!transition?.to?.name?.startsWith('member')) {
controller.set('filterParam', null);
}
}
}
buildRouteInfoMetadata() {
return {
titleToken: 'Members',
mainClasses: ['gh-main-fullwidth'],
fromAnalytics: this.fromAnalytics
mainClasses: ['gh-main-fullwidth']
};
}
}

View file

@ -13,7 +13,7 @@
{{#unless this.directlyFromAnalytics}}
{{svg-jar "arrow-right-small"}}
<LinkTo @route="members" data-test-link="members-back">
<LinkTo @route="members" data-test-link="members-back" @query={{hash postAnalytics=this.postAnalytics}}>
Members
</LinkTo>
{{/unless}}

View file

@ -154,6 +154,7 @@
@newsletterEnabled={{and (not-eq this.settings.editorDefaultEmailRecipients "disabled") this.settings.emailTrackOpens}}
@member={{member.content}}
@filterColumns={{this.filterColumns}}
@query={{hash postAnalytics=this.postAnalytics}}
data-test-member={{member.id}}
/>
{{/if}}