TryGhost/Ghost

View on GitHub
ghost/admin/app/helpers/parse-member-event.js

Summary

Maintainability
F
4 days
Test Coverage
import Helper from '@ember/component/helper';
import moment from 'moment-timezone';
import {getNonDecimal, getSymbol} from 'ghost-admin/utils/currency';
import {ghPluralize} from 'ghost-admin/helpers/gh-pluralize';
import {inject as service} from '@ember/service';

export default class ParseMemberEventHelper extends Helper {
    @service feature;
    @service utils;
    @service membersUtils;

    compute([event, hasMultipleNewsletters]) {
        const subject = event.data.member ? (event.data.member.name || event.data.member.email) : (event.data.name || event.data.email || '');
        const icon = this.getIcon(event);
        const action = this.getAction(event, hasMultipleNewsletters);
        const info = this.getInfo(event);
        const description = this.getDescription(event);

        const join = this.getJoin(event);
        const object = this.getObject(event);
        const url = this.getURL(event);
        const route = this.getRoute(event);
        const timestamp = moment(event.data.created_at);
        const source = this.getSource(event);

        return {
            memberId: event.data.member_id ?? event.data.member?.id,
            member: event.data.member,
            emailId: event.data.email_id,
            email: event.data.email,
            icon,
            subject,
            action,
            join,
            object,
            source,
            info,
            description,
            url,
            route,
            timestamp
        };
    }

    /* internal helper functions */
    getIcon(event) {
        let icon;

        if (event.type === 'login_event') {
            icon = 'logged-in';
        }

        if (event.type === 'payment_event') {
            icon = 'subscriptions';
        }

        if (event.type === 'newsletter_event') {
            if (event.data.subscribed) {
                icon = 'subscribed-to-email';
            } else {
                icon = 'unsubscribed-from-email';
            }
        }

        if (event.type === 'subscription_event') {
            icon = 'subscriptions';

            if (event.data.type === 'canceled') {
                icon = 'canceled-subscription';
            }
        }

        if (event.type === 'signup_event' || (event.type === 'subscription_event' && event.data.type === 'created' && event.data.signup)) {
            icon = 'signed-up';
        }

        if (event.type === 'email_opened_event') {
            icon = 'opened-email';
        }

        if (event.type === 'email_sent_event') {
            icon = 'sent-email';
        }

        if (event.type === 'email_delivered_event') {
            icon = 'received-email';
        }

        if (event.type === 'email_failed_event') {
            icon = 'email-delivery-failed';
        }

        if (event.type === 'email_complaint_event') {
            icon = 'email-delivery-spam';
        }

        if (event.type === 'comment_event') {
            icon = 'comment';
        }

        if (event.type === 'click_event' || event.type === 'aggregated_click_event') {
            icon = 'click';
        }

        if (event.type === 'feedback_event') {
            if (event.data.score === 1) {
                icon = 'more-like-this';
            } else {
                icon = 'less-like-this';
            }
        }

        if (event.type === 'donation_event') {
            icon = 'subscriptions';
        }

        return 'event-' + icon;
    }

    getAction(event, hasMultipleNewsletters) {
        if (event.type === 'signup_event' || (event.type === 'subscription_event' && event.data.type === 'created' && event.data.signup)) {
            return 'signed up';
        }

        if (event.type === 'login_event') {
            return 'logged in';
        }

        if (event.type === 'payment_event') {
            return 'made payment';
        }

        if (event.type === 'newsletter_event') {
            let newsletter = 'newsletter';
            if (hasMultipleNewsletters && event.data.newsletter && event.data.newsletter.name) {
                newsletter = event.data.newsletter.name;
            }

            if (event.data.subscribed) {
                return 'subscribed to ' + newsletter;
            } else {
                return 'unsubscribed from ' + newsletter;
            }
        }

        if (event.type === 'subscription_event') {
            if (event.data.type === 'created') {
                return 'started paid subscription';
            }
            if (event.data.type === 'updated') {
                return 'changed paid subscription';
            }
            if (event.data.type === 'canceled') {
                return 'canceled paid subscription';
            }
            if (event.data.type === 'reactivated') {
                return 'reactivated paid subscription';
            }
            if (event.data.type === 'expired') {
                return 'ended paid subscription';
            }

            return 'changed paid subscription';
        }

        if (event.type === 'email_opened_event') {
            return 'opened email';
        }

        if (event.type === 'email_sent_event') {
            return 'sent email';
        }

        if (event.type === 'email_delivered_event') {
            return 'received email';
        }

        if (event.type === 'email_failed_event') {
            return 'bounced email';
        }

        if (event.type === 'email_complaint_event') {
            return 'email flagged as spam';
        }

        if (event.type === 'comment_event') {
            if (event.data.parent) {
                return 'replied to comment';
            }
            return 'commented';
        }

        if (event.type === 'click_event') {
            return 'clicked link in email';
        }

        if (event.type === 'aggregated_click_event') {
            if (event.data.count.clicks <= 1) {
                return 'clicked link in email';
            }
            return `clicked ${ghPluralize(event.data.count.clicks, 'link')} in email`;
        }

        if (event.type === 'feedback_event') {
            if (event.data.score === 1) {
                return 'more like this';
            }
            return 'less like this';
        }

        if (event.type === 'donation_event') {
            return `Made a one-time payment`;
        }
    }

    /**
     * When we need to append the action and object in one sentence, you can add extra words here.
     * E.g.,
     *   action: 'Signed up'.
     *   object: 'My blog post'
     * When both words need to get appended, we'll add 'on'
     *  -> do this by returning 'on' in getJoin()
     * This string is not added when action and object are in a separate table column, or when the getObject/getURL is empty
     */
    getJoin() {
        return '–';
    }

    /**
     * Clickable object, shown between action and info, or in a separate column in some views
     */
    getObject(event) {
        if (event.type === 'signup_event' || event.type === 'subscription_event' || event.type === 'donation_event') {
            if (event.data.attribution?.title) {
                return event.data.attribution.title;
            }
        }

        if (event.type === 'comment_event') {
            if (event.data.post) {
                return event.data.post.title;
            }
        }

        if (event.type === 'click_event' || event.type === 'feedback_event') {
            if (event.data.post) {
                return event.data.post.title;
            }
        }

        return '';
    }

    /**
     * Clickable object, shown between action and info, or in a separate column in some views
     */
    getSource(event) {
        if (event.data?.attribution?.referrer_source) {
            return {
                name: event.data.attribution.referrer_source,
                url: event.data.attribution.referrer_url ?? null
            };
        }

        return null;
    }

    getInfo(event) {
        if (event.type === 'subscription_event') {
            let mrrDelta = getNonDecimal(event.data.mrr_delta, event.data.currency);
            if (mrrDelta === 0) {
                return;
            }
            const symbol = getSymbol(event.data.currency);

            if (event.data.type === 'created') {
                const sign = mrrDelta > 0 ? '' : '-';
                const tierName = this.membersUtils.hasMultipleTiers ? (event.data.tierName ?? 'Paid') : 'Paid';
                return `${tierName} ${sign}${symbol}${Math.abs(mrrDelta)}/month`;
            }
            const sign = mrrDelta > 0 ? '+' : '-';
            return `MRR ${sign}${symbol}${Math.abs(mrrDelta)}`;
        }

        if (event.type === 'signup_event' && this.membersUtils.paidMembersEnabled) {
            return 'Free';
        }

        if (event.type === 'donation_event') {
            const symbol = getSymbol(event.data.currency);
            const formattedAmount = symbol + getNonDecimal(event.data.amount, event.data.currency);
            return formattedAmount;
        }

        return;
    }

    getDescription(event) {
        if (event.type === 'click_event') {
            // Clean URL
            try {
                return this.utils.cleanTrackedUrl(event.data.link.to, true);
            } catch (e) {
                // Invalid URL
            }
            return event.data.link.to;
        }
        return;
    }

    /**
     * Make the object clickable
     */
    getURL(event) {
        if (['comment_event', 'click_event', 'feedback_event'].includes(event.type)) {
            if (event.data.post) {
                return event.data.post.url;
            }
        }

        if (['signup_event', 'subscription_event', 'donation_event'].includes(event.type)) {
            if (event.data.attribution && event.data.attribution.url) {
                return event.data.attribution.url;
            }
        }
        return;
    }

    /**
     * Get internal route props for a clickable object
     */
    getRoute(event) {
        if (['comment_event', 'click_event', 'feedback_event'].includes(event.type)) {
            if (event.data.post) {
                return {
                    name: 'posts.analytics',
                    model: event.data.post.id
                };
            }
        }

        if (['signup_event', 'subscription_event'].includes(event.type)) {
            if (event.data.attribution_type === 'post') {
                return {
                    name: 'posts.analytics',
                    model: event.data.attribution_id
                };
            }
        }
        return;
    }
}