Skip to content
This repository was archived by the owner on Oct 11, 2022. It is now read-only.

Commit f0655ae

Browse files
authored
Merge pull request #3479 from withspectrum/v2.4.17
v2.4.17
2 parents bfab3e1 + a1c5070 commit f0655ae

File tree

29 files changed

+651
-303
lines changed

29 files changed

+651
-303
lines changed

api/routes/api/email.js

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -193,16 +193,13 @@ emailRouter.get('/validate', (req, res) => {
193193

194194
// and send a database request to update the user record with this email
195195
try {
196-
return updateUserEmail(userId, email).then(
197-
user =>
198-
IS_PROD
199-
? res.redirect(
200-
`https://spectrum.chat/users/${user.username}/settings`
201-
)
202-
: res.redirect(
203-
`http://localhost:3000/users/${user.username}/settings`
204-
)
205-
);
196+
return updateUserEmail(userId, email).then(user => {
197+
const rootRedirect = IS_PROD
198+
? `https://spectrum.chat`
199+
: `http://localhost:3000`;
200+
if (!user.username) return res.redirect(rootRedirect);
201+
return res.redirect(`${rootRedirect}/users/${user.username}/settings`);
202+
});
206203
} catch (err) {
207204
console.error(err);
208205
return res

athena/queues/new-message-in-thread/buffer-email.js

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,26 @@ const bufferMessageNotificationEmail = async (
164164
thread.id
165165
}`
166166
);
167-
const job = await bufferNewMessageEmailQueue.getJob(recipient.email);
167+
let job = await bufferNewMessageEmailQueue.getJob(recipient.email);
168+
// Try to remove the job if it exists. This fails sometimes due to a
169+
// race condition where the getJob returns a failed or active job.
170+
// We circumvent that issue by simply pretending like no job exists
171+
// and adding a new one
172+
// Ref: withspectrum/spectrum#3189
173+
if (job) {
174+
debug(`timeout exists for ${recipient.email}, clearing`);
175+
try {
176+
await job.remove();
177+
} catch (err) {
178+
try {
179+
await job.finished();
180+
// Note(@mxstbr): This throws if the job fails to complete
181+
// but we don't care if that happens, so we ignore it
182+
} catch (err) {}
183+
184+
job = null;
185+
}
186+
}
168187
if (!job) {
169188
debug(
170189
`creating new timeout for ${
@@ -185,9 +204,6 @@ const bufferMessageNotificationEmail = async (
185204
);
186205
} else {
187206
const timeout = job.data;
188-
// If we already have a timeout going
189-
debug(`timeout exists for ${recipient.email}, clearing`);
190-
await job.remove();
191207
debug(`adding new thread to ${recipient.email}'s threads`);
192208
timeout.threads.push(thread);
193209
timeout.notifications.push(notification);

config-overrides.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,10 @@ module.exports = function override(config, env) {
9595
plugin => !isServiceWorkerPlugin(plugin)
9696
);
9797
// Get all public files so they're cached by the SW
98-
let externals = [];
98+
let externals = [
99+
'https://www.google-analytics.com/analytics.js',
100+
'https://cdn.amplitude.com/libs/amplitude-4.2.1-min.gz.js',
101+
];
99102
walkFolder('./public/', file => {
100103
// HOTFIX: Don't cache images
101104
if (file.indexOf('img') > -1 && file.indexOf('homescreen-icon') === -1)

cypress/integration/community_settings_billing_spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ describe('Community settings billing tab', () => {
141141
// click the moderator item in dropdown
142142
cy
143143
.get('[data-cy="community-settings-members-list"]')
144-
.contains('Can edit and delete conversations')
144+
.contains('can moderate conversations')
145145
.click();
146146

147147
// admin email verification modal should appear
@@ -252,7 +252,7 @@ describe('Community settings billing tab', () => {
252252
// click the moderator item in dropdown
253253
cy
254254
.get('[data-cy="community-settings-members-list"]')
255-
.contains('Can edit and delete conversations')
255+
.contains('can moderate conversations')
256256
.click();
257257

258258
// admin email verification modal should appear

package.json

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
22
"name": "Spectrum",
3-
"version": "2.4.16",
3+
"version": "2.4.17",
44
"license": "BSD-3-Clause",
55
"devDependencies": {
66
"babel-cli": "^6.24.1",
7-
"babel-eslint": "^8.2.2",
8-
"babel-jest": "^22.4.3",
7+
"babel-eslint": "^8.2.5",
8+
"babel-jest": "^22.4.4",
99
"babel-plugin-import-inspector": "^2.0.0",
1010
"babel-plugin-inline-import-graphql-ast": "^2.0.4",
1111
"babel-plugin-styled-components": "^1.1.7",
@@ -15,19 +15,19 @@
1515
"babel-plugin-transform-class-properties": "^6.24.1",
1616
"babel-plugin-transform-flow-strip-types": "^6.22.0",
1717
"babel-plugin-transform-object-rest-spread": "^6.23.0",
18-
"babel-preset-env": "^1.5.2",
18+
"babel-preset-env": "^1.7.0",
1919
"backpack-core": "^0.4.1",
2020
"bundle-buddy-webpack-plugin": "^0.3.0",
2121
"cheerio": "^1.0.0-rc.2",
22-
"cross-env": "^5.0.5",
22+
"cross-env": "^5.2.0",
2323
"eslint": "^4.1.1",
2424
"eslint-config-react-app": "^2.1.0",
25-
"eslint-plugin-flowtype": "^2.34.1",
26-
"eslint-plugin-import": "^2.6.0",
25+
"eslint-plugin-flowtype": "^2.49.3",
26+
"eslint-plugin-import": "^2.13.0",
2727
"eslint-plugin-jest": "^21.6.1",
2828
"eslint-plugin-jsx-a11y": "^5.1.1",
29-
"eslint-plugin-promise": "^3.6.0",
30-
"eslint-plugin-react": "^7.1.0",
29+
"eslint-plugin-promise": "^3.8.0",
30+
"eslint-plugin-react": "^7.10.0",
3131
"flow-bin": "0.66",
3232
"forever": "^0.15.3",
3333
"is-html": "^1.1.0",
@@ -36,13 +36,13 @@
3636
"prettier": "^1.0.0",
3737
"raw-loader": "^0.5.1",
3838
"react-app-rewire-hot-loader": "^1.0.1",
39-
"react-hot-loader": "^4.3.1",
39+
"react-hot-loader": "^4.3.3",
4040
"react-scripts": "^1.0.0",
4141
"rimraf": "^2.6.1",
4242
"sw-precache-webpack-plugin": "^0.11.4",
4343
"uuid": "^3.0.1",
4444
"wait-on": "^2.1.0",
45-
"webpack-bundle-analyzer": "^2.9.1",
45+
"webpack-bundle-analyzer": "^2.13.1",
4646
"write-file-webpack-plugin": "^4.3.2"
4747
},
4848
"dependencies": {
@@ -63,7 +63,7 @@
6363
"aws-sdk": "2.200.0",
6464
"axios": "^0.17.1",
6565
"bad-words": "^1.6.1",
66-
"body-parser": "^1.17.1",
66+
"body-parser": "^1.18.3",
6767
"bull": "3.3.10",
6868
"casual": "^1.5.19",
6969
"common-tags": "^1.7.2",
@@ -78,13 +78,13 @@
7878
"debug": "^2.6.8",
7979
"draft-js": "npm:draft-js-fork-mxstbr",
8080
"draft-js-code-editor-plugin": "0.2.1",
81-
"draft-js-drag-n-drop-plugin": "2.0.0-rc9",
81+
"draft-js-drag-n-drop-plugin": "^2.0.3",
8282
"draft-js-embed-plugin": "^1.2.0",
8383
"draft-js-focus-plugin": "2.0.0-rc2",
84-
"draft-js-image-plugin": "2.0.0-rc8",
84+
"draft-js-image-plugin": "^2.0.5",
8585
"draft-js-import-markdown": "^1.2.1",
8686
"draft-js-linkify-plugin": "^2.0.0-beta1",
87-
"draft-js-markdown-plugin": "^2.1.0",
87+
"draft-js-markdown-plugin": "^3.0.0",
8888
"draft-js-plugins-editor": "^2.0.4",
8989
"draft-js-prism-plugin": "0.1.3",
9090
"draftjs-to-markdown": "^0.4.2",
@@ -101,12 +101,12 @@
101101
"graphql-cost-analysis": "^0.1.1",
102102
"graphql-date": "^1.0.3",
103103
"graphql-depth-limit": "^1.1.0",
104-
"graphql-log": "0.1.2",
105-
"graphql-server-express": "1.3.0",
106-
"graphql-subscriptions": "0.5.6",
104+
"graphql-log": "0.1.3",
105+
"graphql-server-express": "1.3.6",
106+
"graphql-subscriptions": "0.5.8",
107107
"graphql-tag": "^2.9.2",
108-
"graphql-tools": "^3.0.2",
109-
"helmet": "^3.12.0",
108+
"graphql-tools": "^3.0.4",
109+
"helmet": "^3.12.1",
110110
"highlight.js": "^9.10.0",
111111
"history": "^4.6.1",
112112
"hoist-non-react-statics": "^2.3.1",
@@ -121,13 +121,13 @@
121121
"jsonwebtoken": "^8.0.1",
122122
"keygrip": "^1.0.2",
123123
"linkify-it": "^2.0.3",
124-
"lodash": "^4.17.4",
124+
"lodash": "^4.17.10",
125125
"lodash.intersection": "^4.4.0",
126126
"markdown-draft-js": "^0.6.3",
127-
"moment": "^2.18.1",
127+
"moment": "^2.22.2",
128128
"node-env-file": "^0.1.8",
129-
"now-env": "^3.0.1",
130-
"offline-plugin": "^5.0.3",
129+
"now-env": "^3.1.0",
130+
"offline-plugin": "^5.0.5",
131131
"optics-agent": "^1.1.2",
132132
"passport": "^0.3.2",
133133
"passport-facebook": "^2.1.1",
@@ -139,10 +139,10 @@
139139
"prismjs": "^1.8.1",
140140
"query-string": "^5.0.0",
141141
"raf": "^3.4.0",
142-
"raven": "^2.0.2",
142+
"raven": "^2.6.3",
143143
"raven-js": "^3.18.1",
144144
"react": "^16.4.0",
145-
"react-apollo": "^2.1.6",
145+
"react-apollo": "^2.1.8",
146146
"react-app-rewire-styled-components": "^3.0.0",
147147
"react-app-rewired": "^1.5.2",
148148
"react-clipboard.js": "^2.0.0",
@@ -159,36 +159,36 @@
159159
"react-stripe-checkout": "^2.2.5",
160160
"react-stripe-elements": "^1.4.1",
161161
"react-textarea-autosize": "^6.1.0",
162-
"react-transition-group": "^2.2.0",
162+
"react-transition-group": "^2.4.0",
163163
"react-trend": "^1.2.4",
164-
"recompose": "0.26.x",
165-
"redraft": "0.8.0",
164+
"recompose": "^0.27.1",
165+
"redraft": "0.10.1",
166166
"redux": "^3.6.0",
167-
"redux-thunk": "^2.2.0",
168-
"request": "^2.85.0",
167+
"redux-thunk": "^2.3.0",
168+
"request": "^2.87.0",
169169
"rethinkdb-changefeed-reconnect": "^0.3.2",
170170
"rethinkdb-inspector": "^0.3.3",
171171
"rethinkdb-migrate": "^1.1.0",
172172
"rethinkdbdash": "^2.3.29",
173173
"sanitize-filename": "^1.6.1",
174-
"serialize-javascript": "^1.4.0",
174+
"serialize-javascript": "^1.5.0",
175175
"session-rethinkdb": "^2.0.0",
176176
"sha1": "^1.1.1",
177177
"shortid": "^2.2.8",
178178
"slate": "^0.20.1",
179-
"slate-markdown": "0.1.0",
179+
"slate-markdown": "0.1.1",
180180
"slugg": "^1.1.0",
181181
"stopword": "^0.1.9",
182182
"string-replace-to-array": "^1.0.3",
183183
"string-similarity": "^1.2.0",
184184
"stripe": "^4.15.0",
185185
"striptags": "2.x",
186-
"styled-components": "3.1.x",
186+
"styled-components": "^3.3.3",
187187
"subscriptions-transport-ws": "0.9.x",
188188
"then-queue": "^1.3.0",
189189
"toobusy-js": "^0.5.1",
190190
"validator": "^9.0.0",
191-
"web-push": "^3.2.2"
191+
"web-push": "^3.3.2"
192192
},
193193
"resolutions": {
194194
"immutable": "3.7.4",

shared/bull/types.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export type Job<JobData> = {|
2424
id: string,
2525
data: JobData,
2626
remove: () => Promise<void>,
27+
finished: () => Promise<void>,
2728
|};
2829

2930
type JobOptions = {|

shared/graphql/queries/thread/getThreadMessageConnection.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export const getThreadMessageConnectionOptions = {
5353
};
5454

5555
// Any thread with less than 50 messages just load all of 'em
56-
if (props.threadMessageCount >= 50) {
56+
if (props.thread.messageCount >= 50) {
5757
// If the thread was active after the user last saw it, only load the new messages
5858
if (props.lastSeen) {
5959
if (

shared/notification-to-text.js

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import onlyContainsEmoji from 'shared/only-contains-emoji';
21
import sentencify from 'shared/sentencify';
3-
import { timeDifferenceShort } from 'shared/time-difference';
42
import sortByDate from 'shared/sort-by-date';
53
import { toState, toPlainText } from 'shared/draft-utils';
64

@@ -16,13 +14,6 @@ const sortThreads = (entities, currentUser) => {
1614
return threads;
1715
};
1816

19-
// parse date => modifiedAt to timeAgo
20-
const parseNotificationDate = date => {
21-
const now = new Date().getTime();
22-
const timestamp = new Date(date).getTime();
23-
return timeDifferenceShort(now, timestamp);
24-
};
25-
2617
export const parseActors = (actors, currentUser) => {
2718
const filteredActors = actors
2819
.filter(actor => actor.id !== currentUser.id)
@@ -31,12 +22,13 @@ export const parseActors = (actors, currentUser) => {
3122
};
3223

3324
const EVENT_VERB = {
34-
MESSAGE_CREATED: 'replied',
25+
MESSAGE_CREATED: 'replied in',
3526
REACTION_CREATED: 'liked',
3627
CHANNEL_CREATED: 'created in',
3728
USER_JOINED_COMMUNITY: 'joined',
3829
MENTION_MESSAGE: 'mentioned you in',
3930
MENTION_THREAD: 'mentioned you in',
31+
THREAD_REACTION_CREATED: 'liked',
4032
};
4133

4234
const contextToString = (context, currentUser) => {
@@ -45,18 +37,23 @@ const contextToString = (context, currentUser) => {
4537
case 'THREAD': {
4638
const payload = context.payload;
4739
const isCreator = payload.creatorId === currentUser.id;
48-
const str = isCreator ? 'in your thread' : 'in';
40+
const str = isCreator ? 'your thread' : '';
4941
return `${str} ${payload.content.title}`;
5042
}
5143
case 'DIRECT_MESSAGE_THREAD': {
5244
return 'in a direct message thread';
5345
}
46+
case 'THREAD_REACTION': {
47+
return 'your thread';
48+
}
5449
case 'MESSAGE':
5550
return 'your reply';
5651
case 'COMMUNITY':
5752
return context.payload.name;
5853
case 'CHANNEL':
5954
return context.payload.name;
55+
default:
56+
return;
6057
}
6158
};
6259

@@ -84,16 +81,13 @@ const parseNotification = notification => {
8481

8582
const formatNotification = (incomingNotification, currentUserId) => {
8683
const notification = parseNotification(incomingNotification);
87-
8884
const actors =
8985
notification.actors &&
9086
parseActors(notification.actors, { id: currentUserId });
9187
const event = notification.event && EVENT_VERB[notification.event];
9288
const context =
9389
notification.context &&
9490
contextToString(notification.context, { id: currentUserId });
95-
const date =
96-
notification.modifiedAt && parseNotificationDate(notification.modifiedAt);
9791

9892
let title = `${actors} ${event} ${context}`;
9993
let href, body;
@@ -171,6 +165,16 @@ const formatNotification = (incomingNotification, currentUserId) => {
171165
: message.content.body;
172166
break;
173167
}
168+
case 'THREAD_REACTION_CREATED': {
169+
const thread = notification.context.payload;
170+
171+
href = `/thread/${thread.id}`;
172+
body =
173+
thread.type === 'draftjs'
174+
? toPlainText(toState(thread.content.body))
175+
: thread.content.body;
176+
break;
177+
}
174178
case 'CHANNEL_CREATED': {
175179
const entities = notification.entities;
176180
const newChannelCount =
@@ -187,6 +191,7 @@ const formatNotification = (incomingNotification, currentUserId) => {
187191
title = `${actors} ${event} ${context}`;
188192
break;
189193
}
194+
190195
case 'MENTION_THREAD': {
191196
// sort and order the threads
192197
const threads = sortThreads(notification.entities, { id: currentUserId });
@@ -221,6 +226,8 @@ const formatNotification = (incomingNotification, currentUserId) => {
221226
title = `${actors} invited you to join their community, ${context}`;
222227
break;
223228
}
229+
default:
230+
return;
224231
}
225232

226233
const data = href && {

0 commit comments

Comments
 (0)