Cleaned up membersActivityFeed labs flag (#2309)

no issue

- `membersActivityFeed` is GA so we don't need any of the conditionals or now-unused code
This commit is contained in:
Kevin Ansfield 2022-03-30 12:43:52 +01:00 committed by GitHub
parent f6a8febbb4
commit 4bfc7529d6
18 changed files with 16 additions and 419 deletions

View File

@ -1231,3 +1231,4 @@ add|ember-template-lint|no-action|290|48|290|48|9ba0927837b1ab440136aff1d92f0b69
add|ember-template-lint|no-action|291|45|291|45|3c80294f3c8009bf5e59ff31d44e18cbc4c0ab89|1647993600000|1650582000000|1653174000000|app/components/settings/members-email.hbs
add|ember-template-lint|no-action|148|60|148|60|9ba0927837b1ab440136aff1d92f0b694752dea8|1648080000000|1650668400000|1653260400000|app/components/settings/members-email.hbs
add|ember-template-lint|no-action|149|57|149|57|3c80294f3c8009bf5e59ff31d44e18cbc4c0ab89|1648080000000|1650668400000|1653260400000|app/components/settings/members-email.hbs
remove|ember-template-lint|no-implicit-this|2|50|2|50|bd48f6e7d00782426eb9404abb796a62e1b1fddd|1646611200000|1649199600000|1651791600000|app/components/member/activity-feed.hbs

View File

@ -6,7 +6,7 @@
</h4>
</h4>
<div class="content">
{{#let (members-event-fetcher filter=(if (feature "membersActivityFeed") (members-event-filter excludeEmailEvents=true)) pageSize=5) as |eventsFetcher|}}
{{#let (members-event-fetcher filter=(members-event-filter excludeEmailEvents=true) pageSize=5) as |eventsFetcher|}}
{{#if eventsFetcher.isLoading}}
Loading...
{{/if}}
@ -55,11 +55,9 @@
{{/if}}
</div>
{{#if (feature "membersActivityFeed")}}
<div class="footer">
<LinkTo @route="members-activity" @query={{reset-query-params "members-activity"}}>See all activity →</LinkTo>
</div>
{{/if}}
{{/unless}}
{{/let}}
</div>

View File

@ -2,7 +2,7 @@
<div class="gh-dashboard-box activity gh-list" data-test-dashboard-member-activity>
<h4 class="gh-dashboard5-metric is-split">Recent activity</h4>
<div class="content">
{{#let (members-event-fetcher filter=(if (feature "membersActivityFeed") (members-event-filter excludeEmailEvents=true)) pageSize=5) as |eventsFetcher|}}
{{#let (members-event-fetcher filter=(members-event-filter excludeEmailEvents=true) pageSize=5) as |eventsFetcher|}}
{{#if eventsFetcher.isError}}
<p class="error">
There was an error loading events
@ -49,12 +49,10 @@
{{/if}}
</div>
{{#if (feature "membersActivityFeed")}}
<div class="footer">
<LinkTo @route="members-activity" @query={{reset-query-params "members-activity"}}>See all activity →</LinkTo>
</div>
{{/if}}
{{/if}}
{{/let}}
</div>
</div>

View File

@ -1,9 +0,0 @@
<div class="gh-member-feed-row" ...attributes>
<span class="gh-member-feed-activity midgrey" data-tooltip={{@activity.tooltip}}>{{svg-jar @activity.icon class=@activity.iconClass}}</span>
{{#if @activity.email}}
<GhEmailPreviewLink @data={{@activity.email}} class="gh-member-feed-title">{{@activity.message}}</GhEmailPreviewLink>
{{else}}
<span class="gh-member-feed-title">{{@activity.message}}</span>
{{/if}}
<span class="gh-member-feed-date">{{moment-format @activity.timestamp "D MMM YYYY"}}</span>
</div>

View File

@ -1,43 +0,0 @@
{{#if this.activities}}<h4 class="gh-main-section-header small bn">Activity</h4>{{/if}}
<div class="gh-main-section-content grey {{if this.activities "" "mt8"}}">
<div class="gh-member-feed {{if this.activities "" "gh-member-feed-no-data"}}" ...attributes>
<div class="flex-auto flex flex-column items-stretch {{if this.activities "justify-between" "h-100 justify-center"}}">
<div>
{{#if this.activities}}
{{#each this.firstActivities as |activity|}}
<GhMemberActivityFeedItem @activity={{activity}} />
{{/each}}
{{#liquid-if this.isShowingAll class="show-overflow"}}
{{#each this.remainingActivities as |activity|}}
<GhMemberActivityFeedItem @activity={{activity}} />
{{/each}}
{{/liquid-if}}
{{#if (feature "membersActivityFeed")}}
<LinkTo class="gh-btn gh-member-btn-expandfeed" @route="members-activity" @query={{hash excludedEvents=null member=@member.id}}><span>View all activity</span></LinkTo>
{{else}}
{{#if (and this.remainingActivities (not this.isShowingAll))}}
<button
type="button"
class="gh-btn gh-member-btn-expandfeed"
data-test-button="view-all-activity"
{{on "click" this.showAll}}
>
<span>View all activity</span>
</button>
{{/if}}
{{/if}}
{{else}}
<div class="gh-members-no-data gh-members-no-list">
<div class="lightgrey">{{svg-jar "no-data-list"}}</div>
<h4>Activity</h4>
<p>
All events related to this member will be shown here.
</p>
</div>
{{/if}}
</div>
</div>
</div>
</div>

View File

@ -1,94 +0,0 @@
import Component from '@glimmer/component';
import {action} from '@ember/object';
import {tracked} from '@glimmer/tracking';
class MemberActivity {
eventProperties = {
sent: {
icon: 'send-email',
iconClass: 'midgrey',
tooltip: 'Received email'
},
opened: {
icon: 'eye',
iconClass: 'green-d2',
tooltip: 'Opened email'
},
failed: {
icon: 'cross-circle',
iconClass: 'red-d2',
tooltip: 'Email delivery failed'
}
};
constructor(props) {
Object.assign(this, props);
}
get icon() {
return this.eventProperties[this.event].icon;
}
get iconClass() {
return this.eventProperties[this.event].iconClass;
}
get tooltip() {
return this.eventProperties[this.event].tooltip;
}
get message() {
if (this.email) {
return this.email.subject;
}
return this.eventProperties[this.event].message;
}
}
export default class MemberActivityFeedComponent extends Component {
@tracked isShowingAll = false;
get activities() {
const activities = [];
(this.args.emailRecipients || []).forEach((emailRecipient) => {
if (emailRecipient.openedAtUTC) {
activities.push(new MemberActivity({
event: 'opened',
email: emailRecipient.email,
timestamp: emailRecipient.openedAtUTC
}));
} else if (emailRecipient.failedAtUTC) {
activities.push(new MemberActivity({
event: 'failed',
email: emailRecipient.email,
timestamp: emailRecipient.failedAtUTC
}));
} else if (emailRecipient.processedAtUTC) {
activities.push(new MemberActivity({
event: 'sent',
email: emailRecipient.email,
timestamp: emailRecipient.processedAtUTC
}));
}
});
return activities.sort((a, b) => {
return b.timestamp.valueOf() - a.timestamp.valueOf();
});
}
get firstActivities() {
return this.activities.slice(0, 5);
}
get remainingActivities() {
return this.activities.slice(5, this.activities.length);
}
@action
showAll() {
this.isShowingAll = true;
}
}

View File

@ -303,14 +303,7 @@
{{/if}}
{{/if}}
{{#if (feature "membersActivityFeed")}}
<Member::ActivityFeed @member={{this.member}} />
{{else}}
<GhMemberActivityFeed
@member={{this.member}}
@emailRecipients={{this.member.emailRecipients}}
/>
{{/if}}
</div>
</section>

View File

@ -1,6 +1,6 @@
{{#if @member.isNew}}
<div class="gh-main-section-content grey {{if eventsFetcher.data "" "mt8"}}">
<div class="gh-member-feed gh-member-feed-labs gh-member-feed-no-data">
<div class="gh-main-section-content grey">
<div class="gh-member-feed gh-member-feed-no-data">
<Member::ActivityFeedEmpty />
</div>
</div>
@ -8,7 +8,7 @@
{{#let (members-event-fetcher filter=(members-event-filter member=@member.id) pageSize=5) as |eventsFetcher|}}
{{#if eventsFetcher.data}}<h4 class="gh-main-section-header small bn">Activity</h4>{{/if}}
<div class="gh-main-section-content grey {{if eventsFetcher.data "" "mt8"}}">
<div class="gh-member-feed gh-member-feed-labs {{if eventsFetcher.data "" "gh-member-feed-no-data"}}" ...attributes>
<div class="gh-member-feed {{if eventsFetcher.data "" "gh-member-feed-no-data"}}" ...attributes>
<div class="flex-auto flex flex-column items-stretch {{if eventsFetcher.data "justify-between" "h-100 justify-center"}}">
<div>
{{#if eventsFetcher.isLoading}}
@ -16,7 +16,7 @@
{{else if eventsFetcher.data}}
{{#each eventsFetcher.data as |rawEvent|}}
{{#let (parse-member-event rawEvent) as |event|}}
<div class="gh-member-feed-row gh-member-feed-row-labs">
<div class="gh-member-feed-row">
<div class="gh-member-feed-container">
<div class="gh-member-feed-icon">
{{svg-jar event.icon class=event.iconClass}}

View File

@ -12,20 +12,10 @@ export default class DashboardController extends Controller {
@service settings;
@service whatsNew;
@tracked topMembersData = null;
@tracked topMembersError = null;
@tracked topMembersLoading = false;
@tracked whatsNewEntries = null;
@tracked whatsNewEntriesLoading = null;
@tracked whatsNewEntriesError = null;
get topMembersDataHasOpenRates() {
return this.topMembersData && this.topMembersData.find((member) => {
return member.emailOpenRate !== null;
});
}
get showMembersData() {
return this.settings.get('membersSignupAccess') !== 'none';
}
@ -43,40 +33,10 @@ export default class DashboardController extends Controller {
}
initialise() {
this.loadTopMembers();
this.loadWhatsNew();
this.checkMemberCountTask.perform();
}
loadTopMembers() {
if (this.feature.membersActivityFeed) {
return;
}
this.topMembersLoading = true;
let query = {
filter: 'email_open_rate:-null',
order: 'email_open_rate desc',
limit: 5
};
this.store.query('member', query).then((result) => {
if (!result.length) {
return this.store.query('member', {
filter: 'status:paid',
order: 'created_at asc',
limit: 5
});
}
return result;
}).then((result) => {
this.topMembersData = result;
this.topMembersLoading = false;
}).catch((error) => {
this.topMembersError = error;
this.topMembersLoading = false;
});
}
loadWhatsNew() {
this.whatsNewEntriesLoading = true;
this.whatsNew.fetchLatest.perform().then(() => {

View File

@ -194,7 +194,7 @@ export default class MemberController extends Controller {
this.member = yield this.store.queryRecord('member', {
id: memberId,
include: 'email_recipients,products'
include: 'products'
});
this.isLoading = false;

View File

@ -19,11 +19,7 @@ export default class MembersRoute extends AdminRoute {
this._requiresBackgroundRefresh = false;
if (params.member_id) {
if (this.feature.membersActivityFeed) {
return this.store.queryRecord('member', {id: params.member_id, include: 'products'});
} else {
return this.store.queryRecord('member', {id: params.member_id, include: 'email_recipients,products'});
}
} else {
return this.store.createRecord('member');
}

View File

@ -1,14 +1,4 @@
import AdminRoute from 'ghost-admin/routes/admin';
import {inject as service} from '@ember/service';
export default class MembersActivityRoute extends AdminRoute {
@service feature;
beforeModel() {
super.beforeModel(...arguments);
if (!this.feature.membersActivityFeed) {
return this.transitionTo('home');
}
}
}

View File

@ -81,9 +81,6 @@ export default class FeatureService extends Service {
@feature('tweetGridCard')
tweetGridCard;
@feature('membersActivityFeed')
membersActivityFeed;
@feature('improvedOnboarding')
improvedOnboarding;

View File

@ -550,107 +550,6 @@ a.gh-dashboard-container {
max-height: 150px;
}
/* Top members */
.gh-dashboard-area.top-members {
grid-column: 3 / 4;
grid-row: 1 / 3;
}
.gh-dashboard-area.top-members .gh-dashboard-list {
justify-content: space-between;
}
.gh-dashboard-top-members {
display: flex;
flex-direction: column;
align-items: stretch;
list-style: none;
padding: 0;
margin: 8px 0 0;
width: 100%;
}
.gh-dashboard-top-member {
display: flex;
align-items: center;
flex-grow: 1;
width: 100%;
justify-content: stretch;
margin: 8px 0;
padding: 0;
}
.gh-dashboard-top-member .member-details {
display: flex;
align-items: center;
flex-grow: 1;
}
.gh-dashboard-top-member .gh-member-gravatar {
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.2);
width: 34px;
height: 34px;
}
.gh-dashboard-top-member .gh-member-initials {
width: 34px;
height: 34px;
}
.gh-dashboard-top-member .gh-member-list-avatar {
font-size: 1.5rem;
font-weight: 500;
text-align: center;
line-height: 1;
}
.gh-dashboard-top-member .name,
.gh-dashboard-top-member .email {
font-size: 1.4rem;
font-weight: 500;
color: var(--black);
}
.gh-dashboard-top-member .open-rate {
white-space: nowrap;
margin-left: 12px;
color: var(--midgrey);
font-size: 1.3rem;
text-align: right;
line-height: 1.35em;
}
.gh-dashboard-top-member .open-rate span {
color: var(--midlightgrey);
font-size: 1.2rem;
}
.gh-dashboard-top-members-footer {
display: flex;
justify-content: flex-end;
border-top: 1px solid var(--whitegrey-d2);
padding-top: 12px;
margin-bottom: -4px;
margin-top: 8px;
}
.gh-dashboard-top-members-footer a {
display: flex;
align-items: center;
color: var(--black);
font-weight: 500;
}
.gh-dashboard-top-members-footer a svg {
width: 12px;
height: 12px;
margin-left: 4px;
}
.gh-dashboard-top-members-footer a svg path {
fill: var(--black);
}
.gh-dashboard-header-container {
display: flex;
align-items: center;

View File

@ -1015,10 +1015,6 @@ textarea.gh-member-details-textarea {
}
.gh-member-feed {
margin: -12px 0 -8px;
}
.gh-member-settings .gh-member-feed-labs {
margin: 0;
padding: 16px 20px;
background: var(--white);
@ -1036,14 +1032,9 @@ textarea.gh-member-details-textarea {
.gh-member-feed-row {
display: flex;
align-items: center;
height: 52px;
padding: 0;
}
.gh-member-feed-row-labs {
height: unset;
}
.gh-member-feed-activity {
display: flex;
align-items: center;
@ -1096,10 +1087,6 @@ textarea.gh-member-details-textarea {
color: var(--darkgrey);
}
.gh-member-btn-expandfeed {
margin-top: 16px;
}
.gh-member-feed-row:first-child .gh-member-feed-container {
padding-top: 0;
}

View File

@ -133,58 +133,6 @@
<section class="gh-dashboard-area members-activity">
{{#if this.showMembersData}}
{{#if (not (feature "membersActivityFeed"))}}
{{#if this.topMembersData}}
<div class="gh-dashboard-box grey top-members">
<div class="gh-dashboard-header-container">
<h4 class="gh-dashboard-header">Top members</h4>
{{#if this.topMembersDataHasOpenRates}}
<h4 class="gh-dashboard-header secondary">Open rate</h4>
{{else}}
<h4 class="gh-dashboard-header secondary">Member since</h4>
{{/if}}
</div>
<div class="gh-dashboard-list">
{{#if this.topMembersLoading}}
Loading...
{{else}}
{{#if this.topMembersError}}
<p class="tc">
There was an error loading member events.
<code class="hidden">{{this.events.error.message}}</code>
</p>
{{else}}
<ul class="gh-dashboard-top-members">
{{#each this.topMembersData as |member|}}
<li class="gh-dashboard-top-member">
<LinkTo class="member-details" @route="member" @model="{{member.id}}">
<GhMemberAvatar @member={{member}} @containerClass="w9 h9 mr3 flex-shrink-0" />
{{#if member.name}}
<span class="name">{{member.name}}</span>
{{else}}
<span class="email">{{member.email}}</span>
{{/if}}
</LinkTo>
{{#if member.emailOpenRate}}
<span class="open-rate">{{member.emailOpenRate}}%</span>
{{else}}
<span class="open-rate">
{{moment-format member.createdAtUTC "D MMM YYYY"}}
</span>
{{/if}}
</li>
{{/each}}
</ul>
{{/if}}
{{/if}}
<div class="gh-dashboard-top-members-footer">
<LinkTo @route="members">See all members {{svg-jar "arrow-right"}}</LinkTo>
</div>
</div>
</div>
{{/if}}
{{/if}}
<Dashboard::LatestMemberActivity />
{{/if}}

View File

@ -283,19 +283,6 @@
</div>
</div>
</div>
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Members activity feed</h4>
<p class="gh-expandable-description">
Separate activity feed screen showing all member activity.
</p>
</div>
<div class="for-switch">
<GhFeatureFlag @flag="membersActivityFeed" />
</div>
</div>
</div>
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>

View File

@ -1,7 +1,6 @@
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
import {currentURL} from '@ember/test-helpers';
import {describe, it} from 'mocha';
import {disableLabsFlag, enableLabsFlag} from '../helpers/labs-flag';
import {expect} from 'chai';
import {setupApplicationTest} from 'ember-mocha';
import {setupMirage} from 'ember-cli-mirage/test-support';
@ -11,10 +10,6 @@ describe('Acceptance: Members activity', function () {
const hooks = setupApplicationTest();
setupMirage(hooks);
beforeEach(function () {
enableLabsFlag(this.server, 'membersActivityFeed');
});
it('redirects when not authenticated', async function () {
await invalidateSession();
await visit('/members-activity');
@ -44,11 +39,5 @@ describe('Acceptance: Members activity', function () {
await visit('/members-activity');
expect(currentURL()).to.equal('/members-activity');
});
it('requires feature flag', async function () {
disableLabsFlag(this.server, 'membersActivityFeed');
await visit('/members-activity');
expect(currentURL()).to.equal('/dashboard');
});
});
});