From 33ffe7b2e4013b98a5efed157721acd71570aea3 Mon Sep 17 00:00:00 2001
From: Sofia Acosta
Date: Mon, 6 Feb 2023 22:12:03 -0600
Subject: [PATCH] partial implementation of luxon
---
api/controllers/AdminController.js | 52 +++++++-------
api/models/event/mixin.js | 20 +++---
api/services/RequestValidation.js | 27 +++----
api/services/Search/util.js | 32 ++++-----
cron.js | 18 ++---
lib/group/digest2/formatData.js | 10 ++-
lib/group/digest2/savedSearches.js | 31 ++++----
lib/group/digest2/util.js | 24 ++++---
package.json | 2 +-
test/unit/services/Search.test.js | 16 ++---
test/unit/services/digest2.test.js | 110 ++++++++++++++---------------
yarn.lock | 17 ++---
12 files changed, 183 insertions(+), 176 deletions(-)
diff --git a/api/controllers/AdminController.js b/api/controllers/AdminController.js
index c172fe68e..a0a093064 100644
--- a/api/controllers/AdminController.js
+++ b/api/controllers/AdminController.js
@@ -1,8 +1,8 @@
-import moment from 'moment-timezone'
+import { DateTime } from 'luxon'
import { merge, transform, sortBy } from 'lodash'
// TODO: this is old and broken
-var rawMetricsQuery = startTime => Promise.props({
+const rawMetricsQuery = startTime => Promise.props({
community: Community.query(q => {
q.select(['id', 'name', 'created_at', 'avatar_url'])
}).query(),
@@ -31,39 +31,39 @@ var rawMetricsQuery = startTime => Promise.props({
module.exports = {
loginAsUser: function (req, res) {
return User.find(req.param('userId'))
- .then(user => UserSession.login(req, user, 'admin'))
- .then(() => res.redirect('/app'))
+ .then(user => UserSession.login(req, user, 'admin'))
+ .then(() => res.redirect('/app'))
},
rawMetrics: function (req, res) {
- const startTime = moment().subtract(3, 'months').toDate()
+ const startTime = DateTime.now().minus({ months: 3 }).toJSDate()
return rawMetricsQuery(startTime)
- .then(props => {
- let result = props.community.reduce((acc, c) => {
- acc[c.id] = merge(c, {events: []})
- return acc
- }, {})
+ .then(props => {
+ let result = props.community.reduce((acc, c) => {
+ acc[c.id] = merge(c, { events: [] })
+ return acc
+ }, {})
- result.none = {id: 'none', name: 'No community', events: []}
+ result.none = { id: 'none', name: 'No community', events: [] }
- ;['user', 'post', 'comment'].forEach(name => {
- props[name].forEach(item => {
- const key = item.community_id || 'none'
- result[key].events.push({
- time: Date.parse(item.created_at),
- user_id: item.user_id || item.id,
- name
+ ;['user', 'post', 'comment'].forEach(name => {
+ props[name].forEach(item => {
+ const key = item.community_id || 'none'
+ result[key].events.push({
+ time: Date.parse(item.created_at),
+ user_id: item.user_id || item.id,
+ name
+ })
})
})
- })
- result = transform(result, (acc, c, k) => {
- if (c.events.length === 0) return
- c.events = sortBy(c.events, 'time')
- acc[k] = c
- }, {})
+ result = transform(result, (acc, c, k) => {
+ if (c.events.length === 0) return
+ c.events = sortBy(c.events, 'time')
+ acc[k] = c
+ }, {})
- res.ok(result)
- })
+ res.ok(result)
+ })
}
}
diff --git a/api/models/event/mixin.js b/api/models/event/mixin.js
index b5bfb46f4..bd7ab5c16 100644
--- a/api/models/event/mixin.js
+++ b/api/models/event/mixin.js
@@ -1,5 +1,5 @@
import { uniq, difference } from 'lodash/fp'
-import moment from 'moment-timezone'
+import { DateTime } from 'luxon'
export default {
isEvent () {
@@ -8,7 +8,7 @@ export default {
eventInvitees: function () {
return this.belongsToMany(User).through(EventInvitation, 'event_id', 'user_id')
- .withPivot('response')
+ .withPivot('response')
},
eventInvitations: function () {
@@ -49,24 +49,26 @@ export default {
prettyEventDates: function (startTime, endTime) {
if (!startTime && !endTime) return null
- const start = moment(startTime)
- const end = moment(endTime)
+ const dateNoYearWithTime = { weekday: 'short', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric' }
+ const dateNoYearNoMonthWithTime = { weekday: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric' }
+ const start = startTime instanceof Date ? DateTime.fromJSDate(startTime) : typeof startTime === 'number' ? DateTime.fromMillis(startTime) : DateTime.fromISO(startTime)
+ const end = endTime instanceof Date ? DateTime.fromJSDate(endTime) : typeof endTime === 'number' ? DateTime.fromMillis(endTime) : DateTime.fromISO(endTime)
- const from = start.format('ddd, MMM D [at] h:mmA')
+ const from = start.toLocaleString(dateNoYearWithTime)
let to = ''
if (endTime) {
if (end.month() !== start.month()) {
- to = end.format(' - ddd, MMM D [at] h:mmA')
+ to = ` - ${end.toLocaleString(dateNoYearWithTime)}`
} else if (end.day() !== start.day()) {
- to = end.format(' - ddd D [at] h:mmA')
+ to = ` - ${end.toLocaleString(dateNoYearNoMonthWithTime)}`
} else {
- to = end.format(' - h:mmA')
+ to = ` - ${end.toLocaleString(DateTime.TIME_WITH_SECONDS)}`
}
}
- return from + to + " UTC"
+ return from + to + ' UTC'
},
createInviteNotifications: async function (userId, inviteeIds) {
diff --git a/api/services/RequestValidation.js b/api/services/RequestValidation.js
index d04f2468c..1057e8c47 100644
--- a/api/services/RequestValidation.js
+++ b/api/services/RequestValidation.js
@@ -1,27 +1,28 @@
-var moment = require('moment-timezone');
+const { DateTime } = require('luxon')
module.exports = {
- requireTimeRange: function(req, res) {
- var valid = true;
+ requireTimeRange: function (req, res) {
+ let valid = true
_.each(['start_time', 'end_time'], function (attr) {
- var value = req.param(attr);
+ const value = req.param(attr)
if (!value) {
- res.badRequest(attr + ' is missing');
- valid = false;
- return false; // break from each
+ res.badRequest(attr + ' is missing')
+ valid = false
+ return false // break from each
}
+ const time = value instanceof Date ? DateTime.fromJSDate(value) : typeof value === 'number' ? DateTime.fromMillis(value) : DateTime.fromISO(value)
- if (!moment(value).isValid()) {
- res.badRequest(attr + ' is not a valid ISO8601 date string');
- valid = false;
- return false; // break from each
+ if (!time.isValid) {
+ res.badRequest(attr + ' is not a valid ISO8601 date string')
+ valid = false
+ return false // break from each
}
- });
+ })
- return valid;
+ return valid
}
}
diff --git a/api/services/Search/util.js b/api/services/Search/util.js
index 1fc117884..227fdb56b 100644
--- a/api/services/Search/util.js
+++ b/api/services/Search/util.js
@@ -1,6 +1,6 @@
import { GraphQLYogaError } from '@graphql-yoga/node'
import { curry, includes, isEmpty, values } from 'lodash'
-import moment from 'moment-timezone'
+import { DateTime } from 'luxon'
import addTermToQueryBuilder from './addTermToQueryBuilder'
export const filterAndSortPosts = curry((opts, q) => {
@@ -32,7 +32,7 @@ export const filterAndSortPosts = curry((opts, q) => {
reactions: 'posts.num_people_reacts',
start_time: 'posts.start_time',
updated: 'posts.updated_at',
- votes: 'posts.num_people_reacts', // Need to remove once Mobile has been ported to reactions
+ votes: 'posts.num_people_reacts' // Need to remove once Mobile has been ported to reactions
}
const sort = sortColumns[sortBy] || values(sortColumns).find(v => v === 'posts.' + sortBy || v === sortBy)
@@ -51,41 +51,41 @@ export const filterAndSortPosts = curry((opts, q) => {
const { CHAT, DISCUSSION, REQUEST, OFFER, PROJECT, EVENT, RESOURCE } = Post.Type
if (isAnnouncement) {
- q.where('announcement', true).andWhere('posts.created_at', '>=', moment().subtract(1, 'month').toDate())
+ q.where('announcement', true).andWhere('posts.created_at', '>=', DateTime.now().minus({ months: 1 }).toJSDate())
}
if (isFulfilled === true) {
q.where(q2 => {
q2.whereNotNull('posts.fulfilled_at')
- .orWhere('posts.end_time', '<', moment().toDate())
+ .orWhere('posts.end_time', '<', DateTime.now().toJSDate())
})
} else if (isFulfilled === false) {
q.whereNull('posts.fulfilled_at')
- .andWhere(q2 => {
- q2.whereNull('posts.end_time')
- .orWhere('posts.end_time', '>=', moment().toDate())
- })
+ .andWhere(q2 => {
+ q2.whereNull('posts.end_time')
+ .orWhere('posts.end_time', '>=', DateTime.now().toJSDate())
+ })
}
if (activePostsOnly) {
q.whereNull('posts.fulfilled_at')
- .andWhere(q2 => {
- q2.whereNull('posts.end_time')
- .orWhere('posts.end_time', '>=', moment().toDate())
- })
+ .andWhere(q2 => {
+ q2.whereNull('posts.end_time')
+ .orWhere('posts.end_time', '>=', DateTime.now().toJSDate())
+ })
}
if (afterTime) {
q.where(q2 =>
q2.where('posts.start_time', '>=', afterTime)
- .orWhere('posts.end_time', '>=', afterTime)
+ .orWhere('posts.end_time', '>=', afterTime)
)
}
if (beforeTime) {
q.where(q2 =>
q2.where('posts.start_time', '<', beforeTime)
- .andWhere('posts.end_time', '<', beforeTime)
+ .andWhere('posts.end_time', '<', beforeTime)
)
}
@@ -111,7 +111,7 @@ export const filterAndSortPosts = curry((opts, q) => {
if (!includes(values(Post.Type), type)) {
throw new GraphQLYogaError(`unknown post type: "${type}"`)
}
- q.where({'posts.type': type})
+ q.where({ 'posts.type': type })
}
if (!isEmpty(search)) {
@@ -144,7 +144,6 @@ export const filterAndSortPosts = curry((opts, q) => {
} else if (sort) {
q.orderBy(sort, order || (sortBy === 'order' ? 'asc' : 'desc'))
}
-
})
export const filterAndSortUsers = curry(({ autocomplete, boundingBox, order, search, sortBy }, q) => {
@@ -186,7 +185,6 @@ export const filterAndSortUsers = curry(({ autocomplete, boundingBox, order, sea
})
export const filterAndSortGroups = curry((opts, q) => {
-
const { search, sortBy = 'name', boundingBox, order } = opts
if (search) {
diff --git a/cron.js b/cron.js
index 3b614aa1b..60c93aeff 100644
--- a/cron.js
+++ b/cron.js
@@ -1,12 +1,12 @@
/* globals Nexudus */
-require("@babel/register")
-var skiff = require('./lib/skiff') // this must be required first
-var moment = require('moment-timezone')
-var rollbar = require('./lib/rollbar')
-var sails = skiff.sails
-var digest2 = require('./lib/group/digest2')
-var Promise = require('bluebird')
-var { red } = require('chalk')
+require('@babel/register')
+const skiff = require('./lib/skiff') // this must be required first
+const { DateTime } = require('luxon')
+const rollbar = require('./lib/rollbar')
+const sails = skiff.sails
+const digest2 = require('./lib/group/digest2')
+const Promise = require('bluebird')
+const { red } = require('chalk')
const savedSearches = require('./lib/group/digest2/savedSearches')
const sendAndLogDigests = type =>
@@ -82,7 +82,7 @@ var runJob = Promise.method(name => {
throw new Error(`Unknown job name: "${name}"`)
}
sails.log.debug(`Running ${name} job`)
- const now = moment.tz('America/Los_Angeles')
+ const now = DateTime.now().setZone('America/Los_Angeles')
return Promise.all(job(now))
})
diff --git a/lib/group/digest2/formatData.js b/lib/group/digest2/formatData.js
index 5e1eb179c..238a18592 100644
--- a/lib/group/digest2/formatData.js
+++ b/lib/group/digest2/formatData.js
@@ -2,8 +2,7 @@
import {
curry, every, filter, find, isEmpty, map, pickBy, sortBy
} from 'lodash/fp'
-import moment from 'moment-timezone'
-import { TextHelpers } from 'hylo-shared'
+import { DateTime } from 'luxon'
const isChat = post => post.get('type') === 'chat'
const isRequest = post => post.get('type') === 'request'
@@ -18,7 +17,12 @@ export const presentAuthor = obj =>
const humanDate = (date) => {
if (!date) return null
- return moment(date).format('MMMM D, YYYY @ h:mma')
+ const formattedDate = date instanceof Date
+ ? DateTime.fromJSDate(date).toLocaleString(DateTime.DATETIME_FULL)
+ : typeof date === 'number'
+ ? DateTime.fromMillis(date).toLocaleString(DateTime.DATETIME_FULL)
+ : DateTime.fromISO(date).toLocaleString(DateTime.DATETIME_FULL)
+ return formattedDate
}
const presentPost = curry((slug, post) => {
diff --git a/lib/group/digest2/savedSearches.js b/lib/group/digest2/savedSearches.js
index 46b7e538a..dfd60281a 100644
--- a/lib/group/digest2/savedSearches.js
+++ b/lib/group/digest2/savedSearches.js
@@ -1,14 +1,21 @@
import { merge, pick, pickBy } from 'lodash/fp'
-import moment from 'moment-timezone'
+import { DateTime } from 'luxon'
import { pluralize } from '../../util/normalize'
import { presentAuthor } from '../digest2/formatData'
import { sendToUser } from '../digest2'
-const humanDate = date => moment(date).format('MMMM D, YYYY')
+const humanDate = date => {
+ const formattedDate = date instanceof Date
+ ? DateTime.fromJSDate(date).toLocaleString(DateTime.DATETIME_FULL)
+ : typeof date === 'number'
+ ? DateTime.fromMillis(date).toLocaleString(DateTime.DATETIME_FULL)
+ : DateTime.fromISO(date).toLocaleString(DateTime.DATETIME_FULL)
+ return formattedDate
+}
const presentPost = async (p, context, slug) => {
- const post = await Post.where({id: p.id}).fetch()
- await post.load(['linkPreview','user'])
+ const post = await Post.where({ id: p.id }).fetch()
+ await post.load(['linkPreview', 'user'])
const { linkPreview } = post.relations
return pickBy(x => x, {
id: post.id,
@@ -58,7 +65,7 @@ const prepareDigestData = async (searchId) => {
const shouldSendData = (data, user, type) => {
const postTypes = ['requests', 'offers', 'events', 'discussions', 'projects', 'resources']
const hasNewPosts = Object.keys(pick(postTypes, data)).some(s => postTypes.includes(s))
- const userSettingMatchesType = user.get('settings')['digest_frequency'] === type
+ const userSettingMatchesType = user.get('settings').digest_frequency === type
return hasNewPosts && userSettingMatchesType
}
@@ -67,13 +74,13 @@ const sendDigest = async (searchId, type) => {
const { lastPostId, user } = data
if (shouldSendData(data, user, type)) return merge(await sendToUser(user, type, data), { lastPostId })
})
- .then(async (sent = {}) => {
- const { lastPostId, success } = sent
- if (success) {
- const search = await SavedSearch.where({ id: searchId }).fetch()
- return await search.updateLastPost(searchId, lastPostId)
- }
- })
+ .then(async (sent = {}) => {
+ const { lastPostId, success } = sent
+ if (success) {
+ const search = await SavedSearch.where({ id: searchId }).fetch()
+ return await search.updateLastPost(searchId, lastPostId)
+ }
+ })
}
export const sendAllDigests = async (type) => {
diff --git a/lib/group/digest2/util.js b/lib/group/digest2/util.js
index 0e4205e8e..9e182a568 100644
--- a/lib/group/digest2/util.js
+++ b/lib/group/digest2/util.js
@@ -1,23 +1,23 @@
-import moment from 'moment-timezone'
+import { DateTime } from 'luxon'
import { includes } from 'lodash'
import { get, pick, some } from 'lodash/fp'
export const defaultTimezone = 'America/Los_Angeles'
export const defaultTimeRange = type => {
- const today = moment.tz(defaultTimezone).startOf('day').add(12, 'hours')
+ const today = DateTime.now().setZone(defaultTimezone).startOf('day').plus({ hours: 12 })
switch (type) {
case 'daily':
- return [today.clone().subtract(1, 'day'), today]
+ return [today.minus({ days: 1 }), today]
case 'weekly':
- return [today.clone().subtract(7, 'day'), today]
+ return [today.minus({ days: 7 }), today]
}
}
export const isValidPostType = q =>
q.where(function () {
this.whereNotIn('posts.type', ['welcome'])
- .orWhere('posts.type', null)
+ .orWhere('posts.type', null)
})
export const relatedUserColumns = (relationName = 'user') => ({
@@ -50,11 +50,13 @@ export const getPostsAndComments = (group, startTime, endTime) =>
q.where('posts.active', true)
q.orderBy('id', 'asc')
})
- .fetch({withRelated: [
- 'post',
- relatedUserColumns(),
- relatedUserColumns('post.user')
- ]})
+ .fetch({
+ withRelated: [
+ 'post',
+ relatedUserColumns(),
+ relatedUserColumns('post.user')
+ ]
+ })
.then(get('models'))
})
@@ -67,7 +69,7 @@ export async function getRecipients (groupId, type) {
const group = await Group.find(groupId)
const recipients = await group.members().query(q => {
q.whereRaw(`users.settings->>'digest_frequency' = '${type}'`)
- q.whereRaw(`(group_memberships.settings->>'sendEmail')::boolean = true`)
+ q.whereRaw('(group_memberships.settings->>\'sendEmail\')::boolean = true')
}).fetch().then(get('models'))
return recipients
diff --git a/package.json b/package.json
index 6177fead1..2efcdec6a 100644
--- a/package.json
+++ b/package.json
@@ -116,11 +116,11 @@
"kue-ui": "^0.1.0",
"link-preview-js": "^3.0.4",
"lodash": "4.17.21",
+ "luxon": "^3.2.1",
"md5": "^2.0.0",
"mime": "^1.3.4",
"mime-types": "^2.1.34",
"minimist": "^1.1.0",
- "moment-timezone": "^0.5.37",
"newrelic": "^7.0.0",
"node-fetch": "^2",
"oidc-provider": "^7.11.3",
diff --git a/test/unit/services/Search.test.js b/test/unit/services/Search.test.js
index 0ed2e640f..5a6b5369c 100644
--- a/test/unit/services/Search.test.js
+++ b/test/unit/services/Search.test.js
@@ -1,4 +1,4 @@
-import moment from 'moment-timezone'
+import { DateTime } from 'luxon'
import { expectEqualQuery } from '../../setup/helpers'
import setup from '../../setup'
@@ -6,11 +6,11 @@ describe('Search', function () {
describe('.forPosts', function () {
// TODO: fix this by reorganizing the search and filter code for posts to join groups_posts in the right place
it.skip('produces the expected SQL for a complex query', function () {
- var startTime = moment('2015-03-24 19:54:12-04:00')
- var endTime = moment('2015-03-31 19:54:12-04:00')
- var tz = moment.tz.guess()
- var startTimeAsString = startTime.tz(tz).format('YYYY-MM-DD HH:mm:ss.SSS')
- var endTimeAsString = endTime.tz(tz).format('YYYY-MM-DD HH:mm:ss.SSS')
+ const startTime = DateTime.fromISO('2015-03-24 19:54:12-04:00')
+ const endTime = DateTime.fromISO('2015-03-31 19:54:12-04:00')
+ const timeZone = DateTime.local().zoneName
+ const startTimeAsString = startTime.setZone(timeZone).toLocaleString({ ...DateTime.DATETIME_SHORT, timeZoneName: 'short' })
+ const endTimeAsString = endTime.setZone(timeZone).toLocaleString({ ...DateTime.DATETIME_SHORT, timeZoneName: 'short' })
const search = Search.forPosts({
limit: 5,
@@ -20,8 +20,8 @@ describe('Search', function () {
follower: 37,
term: 'milk toast',
type: 'request',
- start_time: startTime.toDate(),
- end_time: endTime.toDate(),
+ start_time: startTime.toJSDate(),
+ end_time: endTime.toJSDate(),
sort: 'posts.updated_at'
})
diff --git a/test/unit/services/digest2.test.js b/test/unit/services/digest2.test.js
index 313d25bdd..a12142f75 100644
--- a/test/unit/services/digest2.test.js
+++ b/test/unit/services/digest2.test.js
@@ -1,4 +1,4 @@
-import moment from 'moment-timezone'
+import { DateTime } from 'luxon'
import formatData from '../../../lib/group/digest2/formatData'
import personalizeData from '../../../lib/group/digest2/personalizeData'
import { defaultTimezone, shouldSendData, getRecipients } from '../../../lib/group/digest2/util'
@@ -35,7 +35,7 @@ const u4 = model({
avatar_url: 'http://cnn.com/man.png'
})
-const group = model({slug: 'foo'})
+const group = model({ slug: 'foo' })
const linkPreview = model({
id: '1',
@@ -56,7 +56,7 @@ describe('group digest v2', () => {
post_id: 5,
relations: {
user: u3,
- post: model({id: 5, name: 'Old Post, New Comments', summary: () => 'Old Post, New Comments', details: () => {}, relations: {user: u4}})
+ post: model({ id: 5, name: 'Old Post, New Comments', summary: () => 'Old Post, New Comments', details: () => {}, relations: { user: u4 } })
}
}),
model({
@@ -65,7 +65,7 @@ describe('group digest v2', () => {
post_id: 8,
relations: {
user: u3,
- post: model({id: 8, name: 'Old Post, New Comments', summary: () => 'Old Post, New Comments', details: () => {}, relations: {user: u4}})
+ post: model({ id: 8, name: 'Old Post, New Comments', summary: () => 'Old Post, New Comments', details: () => {}, relations: { user: u4 } })
}
}),
model({
@@ -74,7 +74,7 @@ describe('group digest v2', () => {
post_id: 8,
relations: {
user: u3,
- post: model({id: 8, name: 'Old Post, New Comments', summary: () => 'Old Post, New Comments', details: () => {}, relations: {user: u4}})
+ post: model({ id: 8, name: 'Old Post, New Comments', summary: () => 'Old Post, New Comments', details: () => {}, relations: { user: u4 } })
}
})
@@ -133,16 +133,16 @@ describe('group digest v2', () => {
relations: {
user: u2,
children: collection([
- model({name: 'I need things'}),
- model({name: 'and love'}),
- model({name: 'and more things'})
+ model({ name: 'I need things' }),
+ model({ name: 'and love' }),
+ model({ name: 'and more things' })
])
}
}),
model({
id: 78,
name: '',
- details: () => "A chat!",
+ details: () => 'A chat!',
summary: () => 'A chat!',
type: 'chat',
relations: {
@@ -168,7 +168,7 @@ describe('group digest v2', () => {
id: 5,
title: 'Do you have a dollar?',
user: u1.attributes,
- url: Frontend.Route.post({id: 5}, group),
+ url: Frontend.Route.post({ id: 5 }, group),
comments: [
{
id: 12,
@@ -187,7 +187,7 @@ describe('group digest v2', () => {
id: 6,
title: 'I have cookies!',
user: u2.attributes,
- url: Frontend.Route.post({id: 6}, group),
+ url: Frontend.Route.post({ id: 6 }, group),
comments: []
}
],
@@ -197,7 +197,7 @@ describe('group digest v2', () => {
id: 7,
title: 'Kapow!',
user: u2.attributes,
- url: Frontend.Route.post({id: 7}, group),
+ url: Frontend.Route.post({ id: 7 }, group),
comments: [],
link_preview: omit(linkPreview.attributes, 'id')
}
@@ -209,7 +209,7 @@ describe('group digest v2', () => {
location: 'Home',
when: 'December 17, 1995 @ 6:30pm',
user: u2.attributes,
- url: Frontend.Route.post({id: 76}, group),
+ url: Frontend.Route.post({ id: 76 }, group),
comments: []
}
],
@@ -218,7 +218,7 @@ describe('group digest v2', () => {
id: 77,
title: 'A project with requests',
user: u2.attributes,
- url: Frontend.Route.post({id: 77}, group),
+ url: Frontend.Route.post({ id: 77 }, group),
comments: []
}
],
@@ -226,7 +226,7 @@ describe('group digest v2', () => {
{
id: 8,
title: 'Old Post, New Comments',
- url: Frontend.Route.post({id: 8}, group),
+ url: Frontend.Route.post({ id: 8 }, group),
comments: [
{
id: 13,
@@ -266,10 +266,10 @@ describe('group digest v2', () => {
model({
id: 1,
name: 'Foo!',
- summary: () => `Foo!`,
- details: () => `Edward West & ` +
- `Julia Pope ` +
- `#oakland
`,
+ summary: () => 'Foo!',
+ details: () => 'Edward West & ' +
+ 'Julia Pope ' +
+ '#oakland
',
type: 'request',
relations: {
user: u1
@@ -291,7 +291,7 @@ describe('group digest v2', () => {
`Julia Pope ` +
`#oakland
`,
user: u1.attributes,
- url: Frontend.Route.post({id: 1}, group),
+ url: Frontend.Route.post({ id: 1 }, group),
comments: []
}
],
@@ -304,7 +304,7 @@ describe('group digest v2', () => {
})
it('sets the no_new_activity key if there is no data', () => {
- const data = {posts: [], comments: []}
+ const data = { posts: [], comments: [] }
expect(formatData(group, data)).to.deep.equal({
topicsWithChats: [],
@@ -321,10 +321,10 @@ describe('group digest v2', () => {
})
describe('personalizeData', () => {
- var user
+ let user
before(() => {
- user = factories.user({avatar_url: 'http://google.com/logo.png'})
+ user = factories.user({ avatar_url: 'http://google.com/logo.png' })
return user.save()
})
@@ -355,8 +355,8 @@ describe('group digest v2', () => {
details: 'foo@bar.com and ' +
`Person
`,
comments: [
- {id: 3, user: user.pick('id', 'avatar_url'), text: 'Na'},
- {id: 4, user: u2.attributes, text: `Woa Bob`}
+ { id: 3, user: user.pick('id', 'avatar_url'), text: 'Na' },
+ { id: 4, user: u2.attributes, text: `Woa Bob` }
],
url: 'https://www.hylo.com/all/post/2'
}
@@ -381,12 +381,12 @@ describe('group digest v2', () => {
title: 'Ya',
user: u3.attributes,
details: 'foo@bar.com and ' +
- `Person
`,
+ `Person`,
reply_url: Email.postReplyAddress(2, user.id),
url: 'https://www.hylo.com/all/post/2?' + ctParams,
comments: [
- {id: 3, user: user.pick('id', 'avatar_url'), text: 'Na'},
- {id: 4, user: u2.attributes, text: `Woa Bob`}
+ { id: 3, user: user.pick('id', 'avatar_url'), text: 'Na' },
+ { id: 4, user: u2.attributes, text: `Woa Bob` }
]
}
],
@@ -398,7 +398,7 @@ describe('group digest v2', () => {
post_creation_action_url: Frontend.Route.emailPostForm(),
reply_action_url: Frontend.Route.emailBatchCommentForm(),
form_token: Email.formToken(77, user.id),
- tracking_pixel_url: Analytics.pixelUrl('Digest', {userId: user.id, group: 'foo'}),
+ tracking_pixel_url: Analytics.pixelUrl('Digest', { userId: user.id, group: 'foo' }),
subject: `Hi | ${u4.name}`,
group_url: 'https://www.hylo.com/groups/foo?' + ctParams
}))
@@ -408,35 +408,35 @@ describe('group digest v2', () => {
describe('shouldSendData', () => {
it('is false if the data is empty', () => {
- const data = {requests: [], offers: [], discussions: []}
+ const data = { requests: [], offers: [], discussions: [] }
return shouldSendData(data).then(val => expect(val).to.be.false)
})
it('is true if there is some data', () => {
- const data = {discussions: [{id: 'foo'}]}
+ const data = { discussions: [{ id: 'foo' }] }
return shouldSendData(data).then(val => expect(val).to.be.true)
})
})
describe('sendAllDigests', () => {
- var args, u1, u2, group, post
+ let args, u1, u2, group, post
before(async () => {
spyify(Email, 'sendSimpleEmail', function () { args = arguments })
- const six = moment.tz(defaultTimezone).startOf('day').add(6, 'hours')
+ const six = DateTime.now().setZone(defaultTimezone).startOf('day').plus({ hours: 6 })
u1 = await factories.user({
active: true,
- settings: {digest_frequency: 'daily'},
+ settings: { digest_frequency: 'daily' },
avatar_url: 'av1'
}).save()
- u2 = await factories.user({avatar_url: 'av2'}).save()
+ u2 = await factories.user({ avatar_url: 'av2' }).save()
group = await factories.group({ avatar_url: 'foo' }).save()
- post = await factories.post({created_at: six, user_id: u2.id, type: 'discussion'}).save()
+ post = await factories.post({ created_at: six, user_id: u2.id, type: 'discussion' }).save()
await post.groups().attach(group.id)
await group.addMembers([u1.id], {
- settings: {sendEmail: true}
+ settings: { sendEmail: true }
})
})
@@ -490,7 +490,7 @@ describe('group digest v2', () => {
})
describe('sendDigest', () => {
- var group
+ let group
beforeEach(() => {
group = factories.group()
@@ -500,24 +500,24 @@ describe('group digest v2', () => {
describe('when there is no data', () => {
it('does not send -- feature disabled', () => {
return sendDigest(group.id, 'daily')
- .then(result => expect(result).to.equal(false))
+ .then(result => expect(result).to.equal(false))
})
})
})
})
describe('getRecipients', () => {
- var g, uIn1, uOut1, uOut2, uOut3, uOut4, uOut5, uIn2
+ let g, uIn1, uOut1, uOut2, uOut3, uOut4, uOut5, uIn2
before(async () => {
- const settings = {digest_frequency: 'daily'}
- uIn1 = factories.user({settings})
- uOut1 = factories.user({active: false, settings}) // inactive user
- uOut2 = factories.user({settings}) // inactive membership
- uOut3 = factories.user({settings}) // send_email = false
- uOut4 = factories.user({settings: {digest_frequency: 'weekly'}}) // digest_frequency = 'weekly'
- uOut5 = factories.user({settings}) // not in the group
- uIn2 = factories.user({settings})
+ const settings = { digest_frequency: 'daily' }
+ uIn1 = factories.user({ settings })
+ uOut1 = factories.user({ active: false, settings }) // inactive user
+ uOut2 = factories.user({ settings }) // inactive membership
+ uOut3 = factories.user({ settings }) // send_email = false
+ uOut4 = factories.user({ settings: { digest_frequency: 'weekly' } }) // digest_frequency = 'weekly'
+ uOut5 = factories.user({ settings }) // not in the group
+ uIn2 = factories.user({ settings })
g = factories.group()
await Promise.join(
uIn1.save(),
@@ -531,19 +531,19 @@ describe('getRecipients', () => {
)
await g.addMembers([uIn1, uOut1, uOut2, uOut4, uIn2], {
- settings: {sendEmail: true}
+ settings: { sendEmail: true }
})
- await g.addMembers([uOut3], {settings: {sendEmail: false}})
+ await g.addMembers([uOut3], { settings: { sendEmail: false } })
await g.removeMembers([uOut2])
})
it('only returns active members with email turned on and the right digest type', () => {
return getRecipients(g.id, 'daily')
- .then(models => {
- expect(models.length).to.equal(2)
- expect(models.map(m => m.id).sort())
- .to.deep.equal([uIn1.id, uIn2.id].sort())
- })
+ .then(models => {
+ expect(models.length).to.equal(2)
+ expect(models.map(m => m.id).sort())
+ .to.deep.equal([uIn1.id, uIn2.id].sort())
+ })
})
})
diff --git a/yarn.lock b/yarn.lock
index 69af3bbbc..9236978ac 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8598,6 +8598,11 @@ lru-cache@~2.5.0:
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.5.2.tgz#1fddad938aae1263ce138680be1b3f591c0ab41c"
integrity sha512-wyqfj+623mgqv+bpjTdivSoC/LtY9oOrmKz2Cke0NZcgYW9Kce/qWjd9e5PDYf8wuiKfVeo8VnyOSSyeRiUsLw==
+luxon@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.2.1.tgz#14f1af209188ad61212578ea7e3d518d18cee45f"
+ integrity sha512-QrwPArQCNLAKGO/C+ZIilgIuDnEnKx5QYODdDtbFaxzsbZcc/a7WFq7MhsVYgRlwawLtvOUESTlfJ+hc/USqPg==
+
machine-as-action@^10.3.1:
version "10.3.1"
resolved "https://registry.yarnpkg.com/machine-as-action/-/machine-as-action-10.3.1.tgz#a699e7da529705675b6b892654b034378ea596dc"
@@ -9100,18 +9105,6 @@ mock-require@^2.0.2:
dependencies:
caller-id "^0.1.0"
-moment-timezone@^0.5.37:
- version "0.5.40"
- resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.40.tgz#c148f5149fd91dd3e29bf481abc8830ecba16b89"
- integrity sha512-tWfmNkRYmBkPJz5mr9GVDn9vRlVZOTe6yqY92rFxiOdWXbjaR0+9LwQnZGGuNR63X456NqmEkbskte8tWL5ePg==
- dependencies:
- moment ">= 2.9.0"
-
-"moment@>= 2.9.0":
- version "2.29.4"
- resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
- integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
-
moo@^0.5.0, moo@^0.5.1:
version "0.5.2"
resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.2.tgz#f9fe82473bc7c184b0d32e2215d3f6e67278733c"