import moment from 'moment-timezone'; import {Resource} from 'ember-could-get-used-to-this'; import {TrackedArray} from 'tracked-built-ins'; import {action} from '@ember/object'; import {didCancel, task} from 'ember-concurrency'; import {inject as service} from '@ember/service'; import {tracked} from '@glimmer/tracking'; export default class MembersEventsFetcher extends Resource { @service ajax; @service ghostPaths; @service store; @service feature; @tracked data = new TrackedArray([]); @tracked isLoading = false; @tracked isError = false; @tracked errorMessage = null; @tracked hasReachedEnd = false; /** * Keep track whether we have multiple newsletters (required for parsing events) */ @tracked hasMultipleNewsletters = null; cursor = null; get value() { return { isLoading: this.isLoading, isError: this.isError, errorMessage: this.errorMessage, data: this.data, loadNextPage: this.loadNextPage, hasReachedEnd: this.hasReachedEnd, hasMultipleNewsletters: this.hasMultipleNewsletters }; } async setup() { this.cursor = moment.utc().format('YYYY-MM-DD HH:mm:ss'); let filter = `data.created_at:<'${this.cursor}'`; if (this.args.named.filter) { filter += `+${this.args.named.filter}`; } // Can't get this working with Promise.all, somehow results in an infinite loop try { await this.loadEventsTask.perform({filter}); await this.loadMultipleNewslettersTask.perform(); } catch (e) { if (!didCancel(e)) { // re-throw the non-cancelation error throw e; } } } @action loadNextPage() { // NOTE: assumes data is always ordered by created_at desc const lastEvent = this.data[this.data.length - 1]; if (!lastEvent?.data?.created_at) { this.hasReachedEnd = true; return; } const cursor = moment.utc(lastEvent.data.created_at).format('YYYY-MM-DD HH:mm:ss'); if (cursor === this.cursor) { this.hasReachedEnd = true; return; } this.cursor = cursor; let filter = `data.created_at:<'${this.cursor}'`; if (this.args.named.filter) { filter += `+${this.args.named.filter}`; } this.loadEventsTask.perform({filter}); } /** * We need to know whether we have multiple newsletters so we can hide/show the newsletter name */ @task *loadMultipleNewslettersTask() { try { const res = yield this.store.query('newsletter', {filter: 'status:active', include: 'none', limit: 1}); const newsletterCount = res.meta.pagination.total; this.hasMultipleNewsletters = newsletterCount > 1; } catch (e) { // Default to true (harms the least) this.hasMultipleNewsletters = true; console.error(e); // eslint-disable-line } } @task *loadEventsTask(queryParams) { try { this.isLoading = true; const url = this.ghostPaths.url.api('members/events'); const data = Object.assign({}, queryParams, {limit: this.args.named.pageSize}); const {events} = yield this.ajax.request(url, {data}); if (events.length < data.limit) { this.hasReachedEnd = true; } this.data.push(...events); // todo: remove all condition block when backend will be ready if (this.feature.suppressionList && !queryParams.filter.includes('email_delivered_event')) { const memberId = this.args.named.memberId; if (!this.args.named.memberId) { return; } const member = yield this.store.findRecord('member', memberId); if (member.email === 'spam@member.test') { this.data.unshift(mockData('email_delivered_event')); this.data.unshift(mockData('email_complaint_event')); } if (member.email === 'fail@member.test') { this.data.unshift(mockData('email_failed_event')); } } } catch (e) { this.isError = true; const errorMessage = e.payload?.errors?.[0]?.message; if (errorMessage) { this.errorMessage = errorMessage; } // TODO: log to Sentry console.error(e); // eslint-disable-line } finally { this.isLoading = false; } } } function mockData(eventType) { return ({ type: eventType, data: { id: '6375cc411ebedb499bef54c7', member_id: '63737a1719675aed3b7cc988', created_at: moment.utc(), member: { id: '63737a1719675aed3b7cc988', uuid: '5c753e47-9f49-43ad-86d4-c5c0168519a2', email: 'spam@member.test', status: 'free', name: 'Spam', expertise: null, note: null, geolocation: null, enable_comment_notifications: true, email_count: 6, email_opened_count: 2, email_open_rate: 33, last_seen_at: '2022-11-16T04:44:19.000Z', last_commented_at: null, created_at: '2022-11-15T11:37:59.000Z', updated_at: '2022-11-17T05:33:13.000Z', avatar_image: 'https://www.gravatar.com/avatar/0e0d23869265932bca724acda6c7e529?s=250&r=g&d=blank' }, email: { id: '6375cc411ebedb499bef54c4', post_id: '6375cc3e1ebedb499bef54b5', uuid: '01f393bd-b487-4540-995f-d09ad47059d8', status: 'submitted', recipient_filter: 'all', error: null, error_data: '[]', email_count: 2, delivered_count: 2, opened_count: 0, failed_count: 0, subject: 'Spam test', from: '"local"', reply_to: 'noreply@localhost', html: '\n\n\n\n\n\n\nSpam test\n\n\n\n\n \n \n\n \n \n\n \n \n \n \n \n\n \n\n \n\n\n', plaintext: null, track_opens: true, track_clicks: true, submitted_at: '2022-11-17T05:53:05.000Z', newsletter_id: '123', created_at: '2022-11-17T05:53:05.000Z', updated_at: '2022-11-17T05:53:07.000Z', feedback_enabled: true } } }); }