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

Commit db71b87

Browse files
authored
Merge pull request #3144 from withspectrum/2.3.2
2.3.2
2 parents 37b36f8 + 78d1cae commit db71b87

File tree

8 files changed

+220
-42
lines changed

8 files changed

+220
-42
lines changed

api/models/community.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// @flow
22
const { db } = require('./db');
3+
import intersection from 'lodash.intersection';
34
import { parseRange } from './utils';
45
import { uploadImage } from '../utils/file-storage';
56
import getRandomDefaultPhoto from '../utils/get-random-default-photo';
@@ -74,6 +75,76 @@ export const getCommunitiesByUser = (userId: string): Promise<Array<DBCommunity>
7475
);
7576
};
7677

78+
// prettier-ignore
79+
export const getVisibleCommunitiesByUser = async (evaluatingUserId: string, currentUserId: string) => {
80+
const evaluatingUserMemberships = await db
81+
.table('usersCommunities')
82+
// get all the user's communities
83+
.getAll(evaluatingUserId, { index: 'userId' })
84+
// only return communities the user is a member of
85+
.filter({ isMember: true })
86+
// get the community objects for each community
87+
.eqJoin('communityId', db.table('communities'))
88+
// get rid of unnecessary info from the usersCommunities object on the left
89+
.without({ left: ['id', 'communityId', 'userId', 'createdAt'] })
90+
// zip the tables
91+
.zip()
92+
// ensure we don't return any deleted communities
93+
.filter(community => db.not(community.hasFields('deletedAt')))
94+
.run()
95+
96+
const currentUserMemberships = await db
97+
.table('usersCommunities')
98+
// get all the user's communities
99+
.getAll(currentUserId, { index: 'userId' })
100+
// only return communities the user is a member of
101+
.filter({ isMember: true })
102+
// get the community objects for each community
103+
.eqJoin('communityId', db.table('communities'))
104+
// get rid of unnecessary info from the usersCommunities object on the left
105+
.without({ left: ['id', 'communityId', 'userId', 'createdAt'] })
106+
// zip the tables
107+
.zip()
108+
// ensure we don't return any deleted communities
109+
.filter(community => db.not(community.hasFields('deletedAt')))
110+
.run()
111+
112+
const evaluatingUserCommunityIds = evaluatingUserMemberships.map(community => community.id)
113+
const currentUserCommunityIds = currentUserMemberships.map(community => community.id)
114+
const publicCommunityIds = evaluatingUserMemberships
115+
.filter(community => !community.isPrivate)
116+
.map(community => community.id)
117+
118+
const overlappingMemberships = intersection(evaluatingUserCommunityIds, currentUserCommunityIds)
119+
const allVisibleCommunityIds = [...publicCommunityIds, ...overlappingMemberships]
120+
const distinctCommunityIds = allVisibleCommunityIds.filter((x, i, a) => a.indexOf(x) === i)
121+
122+
return await db
123+
.table('communities')
124+
.getAll(...distinctCommunityIds)
125+
.run()
126+
}
127+
128+
export const getPublicCommunitiesByUser = async (userId: string) => {
129+
return await db
130+
.table('usersCommunities')
131+
// get all the user's communities
132+
.getAll(userId, { index: 'userId' })
133+
// only return communities the user is a member of
134+
.filter({ isMember: true })
135+
// get the community objects for each community
136+
.eqJoin('communityId', db.table('communities'))
137+
// only return public community ids
138+
.filter(row => row('right')('isPrivate').eq(false))
139+
// get rid of unnecessary info from the usersCommunities object on the left
140+
.without({ left: ['id', 'communityId', 'userId', 'createdAt'] })
141+
// zip the tables
142+
.zip()
143+
// ensure we don't return any deleted communities
144+
.filter(community => db.not(community.hasFields('deletedAt')))
145+
.run();
146+
};
147+
77148
export const getCommunitiesChannelCounts = (communityIds: Array<string>) => {
78149
return db
79150
.table('channels')

api/models/thread.js

Lines changed: 101 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -153,16 +153,36 @@ export const getViewableThreadsByUser = async (
153153
.map(userChannel => userChannel('channelId'))
154154
.run();
155155

156+
const getCurrentUserCommunityIds = db
157+
.table('usersCommunities')
158+
.getAll(currentUser, { index: 'userId' })
159+
.filter({ isMember: true })
160+
.map(userCommunity => userCommunity('communityId'))
161+
.run();
162+
156163
// get a list of the channels where the user posted a thread
157164
const getPublishedChannelIds = db
158165
.table('threads')
159166
.getAll(evalUser, { index: 'creatorId' })
160167
.map(thread => thread('channelId'))
161168
.run();
162169

163-
const [currentUsersChannelIds, publishedChannelIds] = await Promise.all([
170+
const getPublishedCommunityIds = db
171+
.table('threads')
172+
.getAll(evalUser, { index: 'creatorId' })
173+
.map(thread => thread('communityId'))
174+
.run();
175+
176+
const [
177+
currentUsersChannelIds,
178+
publishedChannelIds,
179+
currentUsersCommunityIds,
180+
publishedCommunityIds,
181+
] = await Promise.all([
164182
getCurrentUsersChannelIds,
165183
getPublishedChannelIds,
184+
getCurrentUserCommunityIds,
185+
getPublishedCommunityIds,
166186
]);
167187

168188
// get a list of all the channels that are public
@@ -173,16 +193,32 @@ export const getViewableThreadsByUser = async (
173193
.map(channel => channel('id'))
174194
.run();
175195

176-
const allIds = [...currentUsersChannelIds, ...publicChannelIds];
196+
const publicCommunityIds = await db
197+
.table('communities')
198+
.getAll(...publishedCommunityIds)
199+
.filter({ isPrivate: false })
200+
.map(community => community('id'))
201+
.run();
202+
203+
const allIds = [
204+
...currentUsersChannelIds,
205+
...currentUsersCommunityIds,
206+
...publicChannelIds,
207+
...publicCommunityIds,
208+
];
177209
const distinctIds = allIds.filter((x, i, a) => a.indexOf(x) == i);
178-
const validIds = intersection(distinctIds, publishedChannelIds);
210+
let validChannelIds = intersection(distinctIds, publishedChannelIds);
211+
let validCommunityIds = intersection(distinctIds, publishedCommunityIds);
179212

180213
// takes ~70ms for a heavy load
181214
return await db
182215
.table('threads')
183216
.getAll(evalUser, { index: 'creatorId' })
184217
.filter(thread => db.not(thread.hasFields('deletedAt')))
185-
.filter(thread => db.expr(validIds).contains(thread('channelId')))
218+
.filter(thread => db.expr(validChannelIds).contains(thread('channelId')))
219+
.filter(thread =>
220+
db.expr(validCommunityIds).contains(thread('communityId'))
221+
)
186222
.orderBy(db.desc('lastActive'), db.desc('createdAt'))
187223
.skip(after || 0)
188224
.limit(first)
@@ -203,6 +239,10 @@ export const getPublicThreadsByUser = (evalUser: string, options: PaginationOpti
203239
.filter({ right: { isPrivate: false } })
204240
.without('right')
205241
.zip()
242+
.eqJoin('communityId', db.table('communities'))
243+
.filter({ right: { isPrivate: false } })
244+
.without('right')
245+
.zip()
206246
.orderBy(db.desc('lastActive'), db.desc('createdAt'))
207247
.skip(after || 0)
208248
.limit(first || 10)
@@ -223,6 +263,13 @@ export const getViewableParticipantThreadsByUser = async (
223263
.map(userChannel => userChannel('channelId'))
224264
.run();
225265

266+
const getCurrentUserCommunityIds = db
267+
.table('usersCommunities')
268+
.getAll(currentUser, { index: 'userId' })
269+
.filter({ isMember: true })
270+
.map(userCommunity => userCommunity('communityId'))
271+
.run();
272+
226273
// get a list of the channels where the user participated in a thread
227274
const getParticipantChannelIds = db
228275
.table('usersThreads')
@@ -233,16 +280,36 @@ export const getViewableParticipantThreadsByUser = async (
233280
.pluck('channelId', 'threadId')
234281
.run();
235282

236-
const [currentUsersChannelIds, participantChannelIds] = await Promise.all([
283+
const getParticipantCommunityIds = db
284+
.table('usersThreads')
285+
.getAll(evalUser, { index: 'userId' })
286+
.filter({ isParticipant: true })
287+
.eqJoin('threadId', db.table('threads'))
288+
.zip()
289+
.pluck('communityId', 'threadId')
290+
.run();
291+
292+
const [
293+
currentUsersChannelIds,
294+
participantChannelIds,
295+
currentUsersCommunityIds,
296+
participantCommunityIds,
297+
] = await Promise.all([
237298
getCurrentUsersChannelIds,
238299
getParticipantChannelIds,
300+
getCurrentUserCommunityIds,
301+
getParticipantCommunityIds,
239302
]);
240303

241304
const participantThreadIds = participantChannelIds.map(c => c.threadId);
242305
const distinctParticipantChannelIds = participantChannelIds
243306
.map(c => c.channelId)
244307
.filter((x, i, a) => a.indexOf(x) == i);
245308

309+
const distinctParticipantCommunityIds = participantCommunityIds
310+
.map(c => c.communityId)
311+
.filter((x, i, a) => a.indexOf(x) == i);
312+
246313
// get a list of all the channels that are public
247314
const publicChannelIds = await db
248315
.table('channels')
@@ -251,15 +318,37 @@ export const getViewableParticipantThreadsByUser = async (
251318
.map(channel => channel('id'))
252319
.run();
253320

254-
const allIds = [...currentUsersChannelIds, ...publicChannelIds];
321+
const publicCommunityIds = await db
322+
.table('communities')
323+
.getAll(...distinctParticipantCommunityIds)
324+
.filter({ isPrivate: false })
325+
.map(community => community('id'))
326+
.run();
327+
328+
const allIds = [
329+
...currentUsersChannelIds,
330+
...publicChannelIds,
331+
...currentUsersCommunityIds,
332+
...publicCommunityIds,
333+
];
255334
const distinctIds = allIds.filter((x, i, a) => a.indexOf(x) == i);
256-
const validIds = intersection(distinctIds, distinctParticipantChannelIds);
335+
let validChannelIds = intersection(
336+
distinctIds,
337+
distinctParticipantChannelIds
338+
);
339+
let validCommunityIds = intersection(
340+
distinctIds,
341+
distinctParticipantCommunityIds
342+
);
257343

258344
return await db
259345
.table('threads')
260346
.getAll(...participantThreadIds)
261347
.filter(thread => db.not(thread.hasFields('deletedAt')))
262-
.filter(thread => db.expr(validIds).contains(thread('channelId')))
348+
.filter(thread => db.expr(validChannelIds).contains(thread('channelId')))
349+
.filter(thread =>
350+
db.expr(validCommunityIds).contains(thread('communityId'))
351+
)
263352
.orderBy(db.desc('lastActive'), db.desc('createdAt'))
264353
.skip(after || 0)
265354
.limit(first)
@@ -293,6 +382,10 @@ export const getPublicParticipantThreadsByUser = (evalUser: string, options: Pag
293382
.filter({ right: { isPrivate: false } })
294383
.without('right')
295384
.zip()
385+
.eqJoin('communityId', db.table('communities'))
386+
.filter({ right: { isPrivate: false } })
387+
.without('right')
388+
.zip()
296389
.orderBy(db.desc('lastActive'), db.desc('createdAt'))
297390
.skip(after || 0)
298391
.limit(first || 10)

api/queries/thread/rootThread.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,16 @@ export default async (
3838
]);
3939

4040
// if the thread is in a private channel where the user is not a member, don't return any thread data
41-
if (channel.isPrivate && !channelPermissions.isMember) return null;
42-
if (community.isPrivate && !communityPermissions.isMember) return null;
41+
if (
42+
channel.isPrivate &&
43+
(!channelPermissions || !channelPermissions.isMember)
44+
)
45+
return null;
46+
if (
47+
community.isPrivate &&
48+
(!communityPermissions || !communityPermissions.isMember)
49+
)
50+
return null;
4351
return thread;
4452
}
4553
};
Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,33 @@
11
// @flow
22
import type { DBUser } from 'shared/types';
3-
import { getCommunitiesByUser } from '../../models/community';
3+
import {
4+
getVisibleCommunitiesByUser,
5+
getPublicCommunitiesByUser,
6+
getCommunitiesByUser,
7+
} from '../../models/community';
48
import type { GraphQLContext } from '../../';
59

6-
export default (user: DBUser, _: any, { loaders }: GraphQLContext) => ({
7-
// Don't paginate communities and channels of a user
8-
pageInfo: {
9-
hasNextPage: false,
10-
},
11-
edges: getCommunitiesByUser(user.id).then(communities =>
12-
communities.map(async community => {
10+
export default async (user: DBUser, _: any, ctx: GraphQLContext) => {
11+
const evaluatingUserId = user.id;
12+
const { loaders, user: currentUser } = ctx;
13+
14+
let communities;
15+
if (!currentUser || !currentUser.id) {
16+
communities = await getPublicCommunitiesByUser(evaluatingUserId);
17+
} else if (evaluatingUserId === currentUser.id) {
18+
communities = await getCommunitiesByUser(currentUser.id);
19+
} else {
20+
communities = await getVisibleCommunitiesByUser(
21+
evaluatingUserId,
22+
currentUser.id
23+
);
24+
}
25+
26+
return {
27+
pageInfo: {
28+
hasNextPage: false,
29+
},
30+
edges: communities.map(async community => {
1331
const permissions = await loaders.userPermissionsInCommunity.load([
1432
user.id,
1533
community.id,
@@ -26,6 +44,6 @@ export default (user: DBUser, _: any, { loaders }: GraphQLContext) => ({
2644
},
2745
},
2846
};
29-
})
30-
),
31-
});
47+
}),
48+
};
49+
};

api/test/__snapshots__/user.test.js.snap

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@ Object {
2323
"user": Object {
2424
"communityConnection": Object {
2525
"edges": Array [
26-
Object {
27-
"node": Object {
28-
"name": "Private community",
29-
},
30-
},
3126
Object {
3227
"node": Object {
3328
"name": "Payments",

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "Spectrum",
3-
"version": "2.3.1",
3+
"version": "2.3.2",
44
"license": "BSD-3-Clause",
55
"devDependencies": {
66
"babel-cli": "^6.24.1",
@@ -82,7 +82,7 @@
8282
"draft-js-image-plugin": "2.0.0-rc8",
8383
"draft-js-import-markdown": "^1.2.1",
8484
"draft-js-linkify-plugin": "^2.0.0-beta1",
85-
"draft-js-markdown-plugin": "1.4.4",
85+
"draft-js-markdown-plugin": "^2.0.3-0",
8686
"draft-js-plugins-editor": "^2.0.4",
8787
"draft-js-prism-plugin": "0.1.3",
8888
"draftjs-to-markdown": "^0.4.2",

src/components/chatInput/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -703,10 +703,10 @@ class ChatInput extends React.Component<Props, State> {
703703
</ChatInputWrapper>
704704
</ChatInputContainer>
705705
<MarkdownHint showHint={markdownHint} data-cy="markdownHint">
706-
<b>**bold**</b>
707-
<i>*italics*</i>
706+
<b>*bold*</b>
707+
<i>_italic_</i>
708708
<Preformatted>`code`</Preformatted>
709-
<Preformatted>```preformatted```</Preformatted>
709+
<Preformatted>```codeblock```</Preformatted>
710710
</MarkdownHint>
711711
</React.Fragment>
712712
);

0 commit comments

Comments
 (0)