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

2.3.2 #3144

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions api/models/community.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @flow
const { db } = require('./db');
import intersection from 'lodash.intersection';
import { parseRange } from './utils';
import { uploadImage } from '../utils/file-storage';
import getRandomDefaultPhoto from '../utils/get-random-default-photo';
Expand Down Expand Up @@ -74,6 +75,76 @@ export const getCommunitiesByUser = (userId: string): Promise<Array<DBCommunity>
);
};

// prettier-ignore
export const getVisibleCommunitiesByUser = async (evaluatingUserId: string, currentUserId: string) => {
const evaluatingUserMemberships = await db
.table('usersCommunities')
// get all the user's communities
.getAll(evaluatingUserId, { index: 'userId' })
// only return communities the user is a member of
.filter({ isMember: true })
// get the community objects for each community
.eqJoin('communityId', db.table('communities'))
// get rid of unnecessary info from the usersCommunities object on the left
.without({ left: ['id', 'communityId', 'userId', 'createdAt'] })
// zip the tables
.zip()
// ensure we don't return any deleted communities
.filter(community => db.not(community.hasFields('deletedAt')))
.run()

const currentUserMemberships = await db
.table('usersCommunities')
// get all the user's communities
.getAll(currentUserId, { index: 'userId' })
// only return communities the user is a member of
.filter({ isMember: true })
// get the community objects for each community
.eqJoin('communityId', db.table('communities'))
// get rid of unnecessary info from the usersCommunities object on the left
.without({ left: ['id', 'communityId', 'userId', 'createdAt'] })
// zip the tables
.zip()
// ensure we don't return any deleted communities
.filter(community => db.not(community.hasFields('deletedAt')))
.run()

const evaluatingUserCommunityIds = evaluatingUserMemberships.map(community => community.id)
const currentUserCommunityIds = currentUserMemberships.map(community => community.id)
const publicCommunityIds = evaluatingUserMemberships
.filter(community => !community.isPrivate)
.map(community => community.id)

const overlappingMemberships = intersection(evaluatingUserCommunityIds, currentUserCommunityIds)
const allVisibleCommunityIds = [...publicCommunityIds, ...overlappingMemberships]
const distinctCommunityIds = allVisibleCommunityIds.filter((x, i, a) => a.indexOf(x) === i)

return await db
.table('communities')
.getAll(...distinctCommunityIds)
.run()
}

export const getPublicCommunitiesByUser = async (userId: string) => {
return await db
.table('usersCommunities')
// get all the user's communities
.getAll(userId, { index: 'userId' })
// only return communities the user is a member of
.filter({ isMember: true })
// get the community objects for each community
.eqJoin('communityId', db.table('communities'))
// only return public community ids
.filter(row => row('right')('isPrivate').eq(false))
// get rid of unnecessary info from the usersCommunities object on the left
.without({ left: ['id', 'communityId', 'userId', 'createdAt'] })
// zip the tables
.zip()
// ensure we don't return any deleted communities
.filter(community => db.not(community.hasFields('deletedAt')))
.run();
};

export const getCommunitiesChannelCounts = (communityIds: Array<string>) => {
return db
.table('channels')
Expand Down
109 changes: 101 additions & 8 deletions api/models/thread.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,16 +153,36 @@ export const getViewableThreadsByUser = async (
.map(userChannel => userChannel('channelId'))
.run();

const getCurrentUserCommunityIds = db
.table('usersCommunities')
.getAll(currentUser, { index: 'userId' })
.filter({ isMember: true })
.map(userCommunity => userCommunity('communityId'))
.run();

// get a list of the channels where the user posted a thread
const getPublishedChannelIds = db
.table('threads')
.getAll(evalUser, { index: 'creatorId' })
.map(thread => thread('channelId'))
.run();

const [currentUsersChannelIds, publishedChannelIds] = await Promise.all([
const getPublishedCommunityIds = db
.table('threads')
.getAll(evalUser, { index: 'creatorId' })
.map(thread => thread('communityId'))
.run();

const [
currentUsersChannelIds,
publishedChannelIds,
currentUsersCommunityIds,
publishedCommunityIds,
] = await Promise.all([
getCurrentUsersChannelIds,
getPublishedChannelIds,
getCurrentUserCommunityIds,
getPublishedCommunityIds,
]);

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

const allIds = [...currentUsersChannelIds, ...publicChannelIds];
const publicCommunityIds = await db
.table('communities')
.getAll(...publishedCommunityIds)
.filter({ isPrivate: false })
.map(community => community('id'))
.run();

const allIds = [
...currentUsersChannelIds,
...currentUsersCommunityIds,
...publicChannelIds,
...publicCommunityIds,
];
const distinctIds = allIds.filter((x, i, a) => a.indexOf(x) == i);
const validIds = intersection(distinctIds, publishedChannelIds);
let validChannelIds = intersection(distinctIds, publishedChannelIds);
let validCommunityIds = intersection(distinctIds, publishedCommunityIds);

// takes ~70ms for a heavy load
return await db
.table('threads')
.getAll(evalUser, { index: 'creatorId' })
.filter(thread => db.not(thread.hasFields('deletedAt')))
.filter(thread => db.expr(validIds).contains(thread('channelId')))
.filter(thread => db.expr(validChannelIds).contains(thread('channelId')))
.filter(thread =>
db.expr(validCommunityIds).contains(thread('communityId'))
)
.orderBy(db.desc('lastActive'), db.desc('createdAt'))
.skip(after || 0)
.limit(first)
Expand All @@ -203,6 +239,10 @@ export const getPublicThreadsByUser = (evalUser: string, options: PaginationOpti
.filter({ right: { isPrivate: false } })
.without('right')
.zip()
.eqJoin('communityId', db.table('communities'))
.filter({ right: { isPrivate: false } })
.without('right')
.zip()
.orderBy(db.desc('lastActive'), db.desc('createdAt'))
.skip(after || 0)
.limit(first || 10)
Expand All @@ -223,6 +263,13 @@ export const getViewableParticipantThreadsByUser = async (
.map(userChannel => userChannel('channelId'))
.run();

const getCurrentUserCommunityIds = db
.table('usersCommunities')
.getAll(currentUser, { index: 'userId' })
.filter({ isMember: true })
.map(userCommunity => userCommunity('communityId'))
.run();

// get a list of the channels where the user participated in a thread
const getParticipantChannelIds = db
.table('usersThreads')
Expand All @@ -233,16 +280,36 @@ export const getViewableParticipantThreadsByUser = async (
.pluck('channelId', 'threadId')
.run();

const [currentUsersChannelIds, participantChannelIds] = await Promise.all([
const getParticipantCommunityIds = db
.table('usersThreads')
.getAll(evalUser, { index: 'userId' })
.filter({ isParticipant: true })
.eqJoin('threadId', db.table('threads'))
.zip()
.pluck('communityId', 'threadId')
.run();

const [
currentUsersChannelIds,
participantChannelIds,
currentUsersCommunityIds,
participantCommunityIds,
] = await Promise.all([
getCurrentUsersChannelIds,
getParticipantChannelIds,
getCurrentUserCommunityIds,
getParticipantCommunityIds,
]);

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

const distinctParticipantCommunityIds = participantCommunityIds
.map(c => c.communityId)
.filter((x, i, a) => a.indexOf(x) == i);

// get a list of all the channels that are public
const publicChannelIds = await db
.table('channels')
Expand All @@ -251,15 +318,37 @@ export const getViewableParticipantThreadsByUser = async (
.map(channel => channel('id'))
.run();

const allIds = [...currentUsersChannelIds, ...publicChannelIds];
const publicCommunityIds = await db
.table('communities')
.getAll(...distinctParticipantCommunityIds)
.filter({ isPrivate: false })
.map(community => community('id'))
.run();

const allIds = [
...currentUsersChannelIds,
...publicChannelIds,
...currentUsersCommunityIds,
...publicCommunityIds,
];
const distinctIds = allIds.filter((x, i, a) => a.indexOf(x) == i);
const validIds = intersection(distinctIds, distinctParticipantChannelIds);
let validChannelIds = intersection(
distinctIds,
distinctParticipantChannelIds
);
let validCommunityIds = intersection(
distinctIds,
distinctParticipantCommunityIds
);

return await db
.table('threads')
.getAll(...participantThreadIds)
.filter(thread => db.not(thread.hasFields('deletedAt')))
.filter(thread => db.expr(validIds).contains(thread('channelId')))
.filter(thread => db.expr(validChannelIds).contains(thread('channelId')))
.filter(thread =>
db.expr(validCommunityIds).contains(thread('communityId'))
)
.orderBy(db.desc('lastActive'), db.desc('createdAt'))
.skip(after || 0)
.limit(first)
Expand Down Expand Up @@ -293,6 +382,10 @@ export const getPublicParticipantThreadsByUser = (evalUser: string, options: Pag
.filter({ right: { isPrivate: false } })
.without('right')
.zip()
.eqJoin('communityId', db.table('communities'))
.filter({ right: { isPrivate: false } })
.without('right')
.zip()
.orderBy(db.desc('lastActive'), db.desc('createdAt'))
.skip(after || 0)
.limit(first || 10)
Expand Down
12 changes: 10 additions & 2 deletions api/queries/thread/rootThread.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,16 @@ export default async (
]);

// if the thread is in a private channel where the user is not a member, don't return any thread data
if (channel.isPrivate && !channelPermissions.isMember) return null;
if (community.isPrivate && !communityPermissions.isMember) return null;
if (
channel.isPrivate &&
(!channelPermissions || !channelPermissions.isMember)
)
return null;
if (
community.isPrivate &&
(!communityPermissions || !communityPermissions.isMember)
)
return null;
return thread;
}
};
40 changes: 29 additions & 11 deletions api/queries/user/communityConnection.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,33 @@
// @flow
import type { DBUser } from 'shared/types';
import { getCommunitiesByUser } from '../../models/community';
import {
getVisibleCommunitiesByUser,
getPublicCommunitiesByUser,
getCommunitiesByUser,
} from '../../models/community';
import type { GraphQLContext } from '../../';

export default (user: DBUser, _: any, { loaders }: GraphQLContext) => ({
// Don't paginate communities and channels of a user
pageInfo: {
hasNextPage: false,
},
edges: getCommunitiesByUser(user.id).then(communities =>
communities.map(async community => {
export default async (user: DBUser, _: any, ctx: GraphQLContext) => {
const evaluatingUserId = user.id;
const { loaders, user: currentUser } = ctx;

let communities;
if (!currentUser || !currentUser.id) {
communities = await getPublicCommunitiesByUser(evaluatingUserId);
} else if (evaluatingUserId === currentUser.id) {
communities = await getCommunitiesByUser(currentUser.id);
} else {
communities = await getVisibleCommunitiesByUser(
evaluatingUserId,
currentUser.id
);
}

return {
pageInfo: {
hasNextPage: false,
},
edges: communities.map(async community => {
const permissions = await loaders.userPermissionsInCommunity.load([
user.id,
community.id,
Expand All @@ -26,6 +44,6 @@ export default (user: DBUser, _: any, { loaders }: GraphQLContext) => ({
},
},
};
})
),
});
}),
};
};
5 changes: 0 additions & 5 deletions api/test/__snapshots__/user.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ Object {
"user": Object {
"communityConnection": Object {
"edges": Array [
Object {
"node": Object {
"name": "Private community",
},
},
Object {
"node": Object {
"name": "Payments",
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Spectrum",
"version": "2.3.1",
"version": "2.3.2",
"license": "BSD-3-Clause",
"devDependencies": {
"babel-cli": "^6.24.1",
Expand Down Expand Up @@ -82,7 +82,7 @@
"draft-js-image-plugin": "2.0.0-rc8",
"draft-js-import-markdown": "^1.2.1",
"draft-js-linkify-plugin": "^2.0.0-beta1",
"draft-js-markdown-plugin": "1.4.4",
"draft-js-markdown-plugin": "^2.0.3-0",
"draft-js-plugins-editor": "^2.0.4",
"draft-js-prism-plugin": "0.1.3",
"draftjs-to-markdown": "^0.4.2",
Expand Down
6 changes: 3 additions & 3 deletions src/components/chatInput/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -703,10 +703,10 @@ class ChatInput extends React.Component<Props, State> {
</ChatInputWrapper>
</ChatInputContainer>
<MarkdownHint showHint={markdownHint} data-cy="markdownHint">
<b>**bold**</b>
<i>*italics*</i>
<b>*bold*</b>
<i>_italic_</i>
<Preformatted>`code`</Preformatted>
<Preformatted>```preformatted```</Preformatted>
<Preformatted>```codeblock```</Preformatted>
</MarkdownHint>
</React.Fragment>
);
Expand Down
Loading