0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-03-18 02:21:47 -05:00

Added feedback events to activity feed (#15639)

fixes https://github.com/TryGhost/Team/issues/2051
fixes https://github.com/TryGhost/Team/issues/2052
This commit is contained in:
Simon Backx 2022-10-17 15:44:18 +02:00 committed by GitHub
parent f78f264982
commit 0bb7538cd1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 122 additions and 25 deletions

View file

@ -22,6 +22,12 @@ export default class MembersActivityEventTypeFilter extends Component {
if (this.settings.commentsEnabled !== 'off') {
extended.push({event: 'comment_event', icon: 'event-comment', name: 'Comments'});
}
if (this.settings.emailTrackClicks) {
extended.push({event: 'click_event', icon: 'event-click', name: 'Clicked link'});
}
if (this.feature.audienceFeedback) {
extended.push({event: 'feedback_event', icon: 'event-more-like-this', name: 'Feedback'});
}
if (this.args.hiddenEvents?.length) {
return extended.filter(t => !this.args.hiddenEvents.includes(t.event));

View file

@ -88,6 +88,14 @@ export default class ParseMemberEventHelper extends Helper {
icon = 'click';
}
if (event.type === 'feedback_event') {
if (event.data.score === 1) {
icon = 'more-like-this';
} else {
icon = 'less-like-this';
}
}
return 'event-' + icon + (this.feature.get('memberAttribution') ? '--feature-attribution' : '');
}
@ -159,6 +167,13 @@ export default class ParseMemberEventHelper extends Helper {
if (event.type === 'click_event') {
return 'clicked link in email';
}
if (event.type === 'feedback_event') {
if (event.data.score === 1) {
return 'more like this';
}
return 'less like this';
}
}
/**
@ -202,7 +217,7 @@ export default class ParseMemberEventHelper extends Helper {
}
}
if (event.type === 'click_event') {
if (event.type === 'click_event' || event.type === 'feedback_event') {
if (event.data.post) {
return event.data.post.title;
}
@ -241,13 +256,7 @@ export default class ParseMemberEventHelper extends Helper {
* Make the object clickable
*/
getURL(event) {
if (event.type === 'comment_event') {
if (event.data.post) {
return event.data.post.url;
}
}
if (event.type === 'click_event') {
if (event.type === 'comment_event' || event.type === 'click_event' || event.type === 'feedback_event') {
if (event.data.post) {
return event.data.post.url;
}

View file

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.2362 7.96951C19.9143 7.80419 19.5914 7.63997 19.2673 7.47683C13.2943 4.45834 13.5867 5.04627 8.47263 5.04627C6.33767 5.04627 5.25213 6.43563 4.6215 8.47041V8.48355L3.06956 13.6633V13.6731C2.99579 13.9158 2.98002 14.1725 3.02352 14.4224C3.06703 14.6724 3.16859 14.9086 3.32003 15.1121C3.47148 15.3156 3.66858 15.4808 3.89548 15.5942C4.12238 15.7077 4.37274 15.7663 4.62643 15.7654H8.61715C8.86575 15.7661 9.11096 15.8232 9.33427 15.9324C9.55759 16.0417 9.75319 16.2002 9.90633 16.396C10.0585 16.5921 10.1634 16.8207 10.2126 17.064C10.2619 17.3073 10.2543 17.5586 10.1904 17.7985L9.47277 20.5066C9.41698 20.7169 9.41186 20.9375 9.45782 21.1502C9.50379 21.3628 9.59952 21.5616 9.73718 21.7301C9.8732 21.8968 10.0459 22.0299 10.2418 22.1189C10.4376 22.2079 10.6514 22.2505 10.8665 22.2434C11.0815 22.2362 11.292 22.1795 11.4815 22.0777C11.6711 21.9758 11.8345 21.8316 11.9592 21.6562L15.9384 15.5387C16.09 15.3274 16.2897 15.1551 16.521 15.036C16.7523 14.917 17.0085 14.8546 17.2686 14.8539H20.2362M20.2371 17.1001V5" stroke="#6C747D" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.2362 7.96951C19.9143 7.80419 19.5914 7.63997 19.2673 7.47683C13.2943 4.45834 13.5867 5.04627 8.47263 5.04627C6.33767 5.04627 5.25213 6.43563 4.6215 8.47041V8.48355L3.06956 13.6633V13.6731C2.99579 13.9158 2.98002 14.1725 3.02352 14.4224C3.06703 14.6724 3.16859 14.9086 3.32003 15.1121C3.47148 15.3156 3.66858 15.4808 3.89548 15.5942C4.12238 15.7077 4.37274 15.7663 4.62643 15.7654H8.61715C8.86575 15.7661 9.11096 15.8232 9.33427 15.9324C9.55759 16.0417 9.75319 16.2002 9.90633 16.396C10.0585 16.5921 10.1634 16.8207 10.2126 17.064C10.2619 17.3073 10.2543 17.5586 10.1904 17.7985L9.47277 20.5066C9.41698 20.7169 9.41186 20.9375 9.45782 21.1502C9.50379 21.3628 9.59952 21.5616 9.73718 21.7301C9.8732 21.8968 10.0459 22.0299 10.2418 22.1189C10.4376 22.2079 10.6514 22.2505 10.8665 22.2434C11.0815 22.2362 11.292 22.1795 11.4815 22.0777C11.6711 21.9758 11.8345 21.8316 11.9592 21.6562L15.9384 15.5387C16.09 15.3274 16.2897 15.1551 16.521 15.036C16.7523 14.917 17.0085 14.8546 17.2686 14.8539H20.2362M20.2371 17.1001V5" stroke="#6C747D" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,3 @@
<svg width="26" height="24" viewBox="0 0 26 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.00089 17.2746C4.32277 17.4399 4.64576 17.6042 4.96983 17.7673C10.9428 20.7858 10.6504 20.1979 15.7645 20.1979C17.8994 20.1979 18.985 18.8085 19.6156 16.7737V16.7606L21.1676 11.5809V11.571C21.2413 11.3283 21.2571 11.0716 21.2136 10.8217C21.1701 10.5718 21.0685 10.3355 20.9171 10.132C20.7656 9.92849 20.5685 9.76335 20.3416 9.64989C20.1147 9.53643 19.8644 9.47781 19.6107 9.47875H15.62C15.3714 9.47806 15.1262 9.42095 14.9028 9.31171C14.6795 9.20247 14.4839 9.04395 14.3308 8.84811C14.1786 8.65203 14.0737 8.42342 14.0245 8.18014C13.9752 7.93686 13.9828 7.68548 14.0467 7.44561L14.7643 4.73751C14.8201 4.52719 14.8252 4.30664 14.7793 4.09396C14.7333 3.88128 14.6376 3.68253 14.4999 3.51402C14.3639 3.34731 14.1912 3.21426 13.9953 3.12524C13.7995 3.03622 13.5857 2.99362 13.3706 3.00077C13.1556 3.00792 12.9451 3.06463 12.7556 3.16647C12.566 3.26832 12.4026 3.41254 12.2779 3.58792L8.29871 9.70538C8.14706 9.91672 7.94738 10.089 7.71611 10.2081C7.48484 10.3271 7.22859 10.3896 6.96847 10.3902H4.00089M4 8.74823V20.2441" stroke="#6C747D" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,3 @@
<svg width="26" height="24" viewBox="0 0 26 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.00089 17.2746C4.32277 17.4399 4.64576 17.6042 4.96983 17.7673C10.9428 20.7858 10.6504 20.1979 15.7645 20.1979C17.8994 20.1979 18.985 18.8085 19.6156 16.7737V16.7606L21.1676 11.5809V11.571C21.2413 11.3283 21.2571 11.0716 21.2136 10.8217C21.1701 10.5718 21.0685 10.3355 20.9171 10.132C20.7656 9.92849 20.5685 9.76335 20.3416 9.64989C20.1147 9.53643 19.8644 9.47781 19.6107 9.47875H15.62C15.3714 9.47806 15.1262 9.42095 14.9028 9.31171C14.6795 9.20247 14.4839 9.04395 14.3308 8.84811C14.1786 8.65203 14.0737 8.42342 14.0245 8.18014C13.9752 7.93686 13.9828 7.68548 14.0467 7.44561L14.7643 4.73751C14.8201 4.52719 14.8252 4.30664 14.7793 4.09396C14.7333 3.88128 14.6376 3.68253 14.4999 3.51402C14.3639 3.34731 14.1912 3.21426 13.9953 3.12524C13.7995 3.03622 13.5857 2.99362 13.3706 3.00077C13.1556 3.00792 12.9451 3.06463 12.7556 3.16647C12.566 3.26832 12.4026 3.41254 12.2779 3.58792L8.29871 9.70538C8.14706 9.91672 7.94738 10.089 7.71611 10.2081C7.48484 10.3271 7.22859 10.3896 6.96847 10.3902H4.00089M4 8.74823V20.2441" stroke="#6C747D" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -2,6 +2,21 @@ const mapComment = require('./comments');
const url = require('../utils/url');
const _ = require('lodash');
const memberFields = [
'id',
'uuid',
'name',
'email',
'avatar_image'
];
const postFields = [
'id',
'uuid',
'title',
'url'
];
const commentEventMapper = (json, frame) => {
return {
...json,
@ -10,26 +25,11 @@ const commentEventMapper = (json, frame) => {
};
const clickEventMapper = (json, frame) => {
const memberFields = [
'id',
'uuid',
'name',
'email',
'avatar_image'
];
const linkFields = [
'from',
'to'
];
const postFields = [
'id',
'uuid',
'title',
'url'
];
const data = json.data;
const response = {};
@ -59,6 +59,35 @@ const clickEventMapper = (json, frame) => {
};
};
const feedbackEventMapper = (json, frame) => {
const feedbackFields = [
'id',
'score',
'created_at'
];
const data = json.data;
const response = _.pick(data, feedbackFields);
if (data.post) {
url.forPost(data.post.id, data.post, frame);
response.post = _.pick(data.post, postFields);
} else {
response.post = null;
}
if (data.member) {
response.member = _.pick(data.member, memberFields);
} else {
response.member = null;
}
return {
...json,
data: response
};
};
function serializeAttribution(attribution) {
if (!attribution) {
return attribution;
@ -82,6 +111,9 @@ const activityFeedMapper = (event, frame) => {
if (event.type === 'click_event') {
return clickEventMapper(event, frame);
}
if (event.type === 'feedback_event') {
return feedbackEventMapper(event, frame);
}
if (event.data?.attribution) {
event.data.attribution = serializeAttribution(event.data.attribution);
}

View file

@ -194,7 +194,8 @@ function createApiInstance(config) {
StripePrice: models.StripePrice,
Product: models.Product,
Settings: models.Settings,
Comment: models.Comment
Comment: models.Comment,
MemberFeedback: models.MemberFeedback
},
stripeAPIService: stripeService.api,
offersAPI: offersService.api,

View file

@ -58,7 +58,8 @@ module.exports = function MembersAPI({
StripePrice,
Product,
Settings,
Comment
Comment,
MemberFeedback
},
stripeAPIService,
offersAPI,
@ -112,6 +113,7 @@ module.exports = function MembersAPI({
MemberCreatedEvent,
SubscriptionCreatedEvent,
MemberLinkClickEvent,
MemberFeedback,
Comment,
labsService,
memberAttributionService

View file

@ -12,6 +12,7 @@ module.exports = class EventRepository {
SubscriptionCreatedEvent,
MemberPaidSubscriptionEvent,
MemberLinkClickEvent,
MemberFeedback,
Comment,
labsService,
memberAttributionService
@ -27,6 +28,7 @@ module.exports = class EventRepository {
this._MemberCreatedEvent = MemberCreatedEvent;
this._SubscriptionCreatedEvent = SubscriptionCreatedEvent;
this._MemberLinkClickEvent = MemberLinkClickEvent;
this._MemberFeedback = MemberFeedback;
this._memberAttributionService = memberAttributionService;
}
@ -56,6 +58,10 @@ module.exports = class EventRepository {
pageActions.push({type: 'email_failed_event', action: 'getEmailFailedEvents'});
}
if (this._labsService.isSet('audienceFeedback')) {
pageActions.push({type: 'feedback_event', action: 'getFeedbackEvents'});
}
let filters = this.getNQLSubset(options.filter);
//Filter events to query
@ -360,6 +366,35 @@ module.exports = class EventRepository {
};
}
async getFeedbackEvents(options = {}, filters = {}) {
options = {
...options,
withRelated: ['member', 'post'],
filter: []
};
if (filters['data.created_at']) {
options.filter.push(filters['data.created_at'].replace(/data.created_at:/g, 'created_at:'));
}
if (filters['data.member_id']) {
options.filter.push(filters['data.member_id'].replace(/data.member_id:/g, 'member_id:'));
}
options.filter = options.filter.join('+');
const {data: models, meta} = await this._MemberFeedback.findPage(options);
const data = models.map((model) => {
return {
type: 'feedback_event',
data: model.toJSON(options)
};
});
return {
data,
meta
};
}
async getEmailDeliveredEvents(options = {}, filters = {}) {
options = {
...options,