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

Commit 8054f56

Browse files
authored
Merge pull request #3381 from withspectrum/2.4.10
2.4.10
2 parents 11c460a + 8c9ff2e commit 8054f56

File tree

160 files changed

+5632
-1358
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

160 files changed

+5632
-1358
lines changed

api/authentication.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ const init = () => {
7070
{
7171
consumerKey: TWITTER_OAUTH_CLIENT_ID,
7272
consumerSecret: TWITTER_OAUTH_CLIENT_SECRET,
73-
callbackURL: '/auth/twitter/callback',
73+
callbackURL: IS_PROD
74+
? 'https://spectrum.chat/auth/twitter/callback'
75+
: 'http://localhost:3001/auth/twitter/callback',
7476
includeEmail: true,
7577
},
7678
(token, tokenSecret, profile, done) => {
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// @flow
2+
import type { GraphQLContext } from '../../';
3+
import type { DBCommunity } from 'shared/types';
4+
import { getCommunityById } from '../../models/community';
5+
import {
6+
createMemberInCommunity,
7+
checkUserPermissionsInCommunity,
8+
} from '../../models/usersCommunities';
9+
import { createMemberInDefaultChannels } from '../../models/usersChannels';
10+
import { isAuthedResolver as requireAuth } from '../../utils/permissions';
11+
import UserError from '../../utils/UserError';
12+
13+
type Input = {
14+
input: {
15+
communityIds: Array<string>,
16+
},
17+
};
18+
19+
export default requireAuth(async (_: any, args: Input, ctx: GraphQLContext) => {
20+
const { user } = ctx;
21+
const { communityIds } = args.input;
22+
23+
if (communityIds.length > 20) {
24+
return new UserError('Try joining just a few communities at a time');
25+
}
26+
27+
const canJoinCommunity = async (
28+
communityId: string
29+
): Promise<?DBCommunity> => {
30+
const [permissions, community] = await Promise.all([
31+
checkUserPermissionsInCommunity(communityId, user.id),
32+
getCommunityById(communityId),
33+
]);
34+
35+
if (!community) {
36+
return null;
37+
}
38+
39+
// if no permissions exist, join them to the community!
40+
if (!permissions || permissions.length === 0) {
41+
return community;
42+
}
43+
44+
const permission = permissions[0];
45+
46+
if (!permission.isMember) {
47+
return community;
48+
}
49+
50+
return null;
51+
};
52+
53+
const handleJoin = async (communityId: string) => {
54+
return await Promise.all([
55+
createMemberInCommunity(communityId, user.id),
56+
createMemberInDefaultChannels(communityId, user.id),
57+
]);
58+
};
59+
60+
return communityIds.map(async id => {
61+
const community = await canJoinCommunity(id);
62+
if (!community) return null;
63+
return await handleJoin(id).then(() => community);
64+
});
65+
});

api/mutations/communityMember/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// @flow
22
import addCommunityMember from './addCommunityMember';
3+
import addCommunityMembers from './addCommunityMembers';
34
import addCommunityMemberWithToken from './addCommunityMemberWithToken';
45
import addPendingCommunityMember from './addPendingCommunityMember';
56
import removeCommunityMember from './removeCommunityMember';
@@ -14,6 +15,7 @@ import unblockCommunityMember from './unblockCommunityMember';
1415
module.exports = {
1516
Mutation: {
1617
addCommunityMember,
18+
addCommunityMembers,
1719
addCommunityMemberWithToken,
1820
addPendingCommunityMember,
1921
removeCommunityMember,

api/mutations/directMessageThread/createDirectMessageThread.js

Lines changed: 106 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { events } from 'shared/analytics';
1717
import { trackQueue } from 'shared/bull/queues';
1818
import { isAuthedResolver as requireAuth } from '../../utils/permissions';
1919

20-
type Input = {
20+
export type CreateDirectMessageThreadInput = {
2121
input: {
2222
participants: Array<string>,
2323
message: {
@@ -31,124 +31,126 @@ type Input = {
3131
},
3232
};
3333

34-
export default requireAuth(async (_: any, args: Input, ctx: GraphQLContext) => {
35-
const { user } = ctx;
36-
const { input } = args;
34+
export default requireAuth(
35+
async (_: any, args: CreateDirectMessageThreadInput, ctx: GraphQLContext) => {
36+
const { user } = ctx;
37+
const { input } = args;
3738

38-
if (!input.participants) {
39-
trackQueue.add({
40-
userId: user.id,
41-
event: events.DIRECT_MESSAGE_THREAD_CREATED_FAILED,
42-
properties: {
43-
reason: 'no users selected',
44-
},
45-
});
39+
if (!input.participants) {
40+
trackQueue.add({
41+
userId: user.id,
42+
event: events.DIRECT_MESSAGE_THREAD_CREATED_FAILED,
43+
properties: {
44+
reason: 'no users selected',
45+
},
46+
});
4647

47-
return new UserError('Nobody was selected to create a thread.');
48-
}
48+
return new UserError('Nobody was selected to create a thread.');
49+
}
4950

50-
// if users and messages exist, continue
51-
const { participants, message } = input;
52-
53-
// if the group being created has more than one participant, a group
54-
// thread is being created - this means that people can be added
55-
// and removed from the thread in the future. we *don't* want this
56-
// behavior for 1:1 threads to preserve privacy, so we store an `isGroup`
57-
// boolean on the dmThread object itself which will be used in other
58-
// mutations to add or remove members
59-
const isGroup = participants.length > 1;
60-
61-
// collect all participant ids and the current user id into an array - we
62-
// use this to determine if an existing DM thread with this exact
63-
// set of participants already exists or not
64-
const allMemberIds = [...participants, user.id];
65-
66-
// placeholder
67-
let threadId, threadToReturn;
68-
69-
// check to see if a dm thread with this exact set of participants exists
70-
const existingThread = await checkForExistingDMThread(allMemberIds);
71-
72-
if (existingThread) {
73-
threadId = existingThread;
74-
threadToReturn = await getDirectMessageThread(threadId);
75-
} else {
76-
threadToReturn = await createDirectMessageThread(isGroup, user.id);
77-
threadId = threadToReturn.id;
78-
}
51+
// if users and messages exist, continue
52+
const { participants, message } = input;
53+
54+
// if the group being created has more than one participant, a group
55+
// thread is being created - this means that people can be added
56+
// and removed from the thread in the future. we *don't* want this
57+
// behavior for 1:1 threads to preserve privacy, so we store an `isGroup`
58+
// boolean on the dmThread object itself which will be used in other
59+
// mutations to add or remove members
60+
const isGroup = participants.length > 1;
61+
62+
// collect all participant ids and the current user id into an array - we
63+
// use this to determine if an existing DM thread with this exact
64+
// set of participants already exists or not
65+
const allMemberIds = [...participants, user.id];
7966

80-
const handleStoreMessage = async message => {
81-
if (message.messageType === 'text' || message.messageType === 'draftjs') {
82-
// once we have an id we can generate a proper message object
83-
const messageWithThread = {
84-
...message,
85-
threadId,
86-
};
87-
88-
return await storeMessage(messageWithThread, user.id);
89-
} else if (message.messageType === 'media' && message.file) {
90-
let url;
91-
try {
92-
url = await uploadImage(message.file, 'threads', threadId);
93-
} catch (err) {
67+
// placeholder
68+
let threadId, threadToReturn;
69+
70+
// check to see if a dm thread with this exact set of participants exists
71+
const existingThread = await checkForExistingDMThread(allMemberIds);
72+
73+
if (existingThread) {
74+
threadId = existingThread;
75+
threadToReturn = await getDirectMessageThread(threadId);
76+
} else {
77+
threadToReturn = await createDirectMessageThread(isGroup, user.id);
78+
threadId = threadToReturn.id;
79+
}
80+
81+
const handleStoreMessage = async message => {
82+
if (message.messageType === 'text' || message.messageType === 'draftjs') {
83+
// once we have an id we can generate a proper message object
84+
const messageWithThread = {
85+
...message,
86+
threadId,
87+
};
88+
89+
return await storeMessage(messageWithThread, user.id);
90+
} else if (message.messageType === 'media' && message.file) {
91+
let url;
92+
try {
93+
url = await uploadImage(message.file, 'threads', threadId);
94+
} catch (err) {
95+
trackQueue.add({
96+
userId: user.id,
97+
event: events.DIRECT_MESSAGE_THREAD_CREATED_FAILED,
98+
properties: {
99+
reason: 'image upload failed',
100+
},
101+
});
102+
return new UserError(err.message);
103+
}
104+
105+
// build a new message object with a new file field with metadata
106+
const newMessage = Object.assign({}, message, {
107+
...message,
108+
threadId: threadId,
109+
content: {
110+
body: url,
111+
},
112+
file: {
113+
name: message.file && message.file.filename,
114+
size: null,
115+
type: message.file && message.file.mimetype,
116+
},
117+
});
118+
119+
return await storeMessage(newMessage, user.id);
120+
} else {
94121
trackQueue.add({
95122
userId: user.id,
96123
event: events.DIRECT_MESSAGE_THREAD_CREATED_FAILED,
97124
properties: {
98-
reason: 'image upload failed',
125+
reason: 'unknown message type',
99126
},
100127
});
101-
return new UserError(err.message);
128+
return new UserError('Unknown message type on this bad boy.');
102129
}
130+
};
103131

104-
// build a new message object with a new file field with metadata
105-
const newMessage = Object.assign({}, message, {
106-
...message,
107-
threadId: threadId,
108-
content: {
109-
body: url,
110-
},
111-
file: {
112-
name: message.file && message.file.filename,
113-
size: null,
114-
type: message.file && message.file.mimetype,
115-
},
116-
});
117-
118-
return await storeMessage(newMessage, user.id);
119-
} else {
120-
trackQueue.add({
121-
userId: user.id,
122-
event: events.DIRECT_MESSAGE_THREAD_CREATED_FAILED,
123-
properties: {
124-
reason: 'unknown message type',
125-
},
126-
});
127-
return new UserError('Unknown message type on this bad boy.');
132+
if (existingThread) {
133+
return await Promise.all([
134+
setUserLastSeenInDirectMessageThread(threadId, user.id),
135+
handleStoreMessage(message),
136+
]).then(() => threadToReturn);
128137
}
129-
};
130138

131-
if (existingThread) {
139+
trackQueue.add({
140+
userId: user.id,
141+
event: events.DIRECT_MESSAGE_THREAD_CREATED,
142+
});
143+
132144
return await Promise.all([
133-
setUserLastSeenInDirectMessageThread(threadId, user.id),
145+
createMemberInDirectMessageThread(threadId, user.id, true),
134146
handleStoreMessage(message),
147+
participants.map(participant => {
148+
trackQueue.add({
149+
userId: participant,
150+
event: events.DIRECT_MESSAGE_THREAD_RECEIVED,
151+
});
152+
return createMemberInDirectMessageThread(threadId, participant, false);
153+
}),
135154
]).then(() => threadToReturn);
136155
}
137-
138-
trackQueue.add({
139-
userId: user.id,
140-
event: events.DIRECT_MESSAGE_THREAD_CREATED,
141-
});
142-
143-
return await Promise.all([
144-
createMemberInDirectMessageThread(threadId, user.id, true),
145-
handleStoreMessage(message),
146-
participants.map(participant => {
147-
trackQueue.add({
148-
userId: participant,
149-
event: events.DIRECT_MESSAGE_THREAD_RECEIVED,
150-
});
151-
return createMemberInDirectMessageThread(threadId, participant, false);
152-
}),
153-
]).then(() => threadToReturn);
154-
});
156+
);

0 commit comments

Comments
 (0)