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

Commit 4bbd5de

Browse files
authored
Merge pull request #5087 from withspectrum/fix-private-channel-joins
Fix private channel joins
2 parents 0616877 + 30efaf1 commit 4bbd5de

File tree

3 files changed

+116
-18
lines changed

3 files changed

+116
-18
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
exports.up = async function(r, conn) {
2+
// get all private channels that haven't been deleted
3+
const privateChannels = await r
4+
.db('spectrum')
5+
.table('channels')
6+
.filter({ isPrivate: true })
7+
.filter(row => row.hasFields('deletedAt').not())
8+
.run(conn)
9+
.then(cursor => cursor.toArray());
10+
11+
// for each channel, remove all members except the community owner
12+
return Promise.all(
13+
privateChannels.map(async channel => {
14+
const community = await r
15+
.db('spectrum')
16+
.table('communities')
17+
.get(channel.communityId)
18+
.run(conn);
19+
20+
// ensure that the community owner also owns the channel
21+
// to account for situations where a moderator created the channel
22+
const communityOwnerChannelRecord = await r
23+
.db('spectrum')
24+
.table('usersChannels')
25+
.getAll([community.creatorId, channel.id], {
26+
index: 'userIdAndChannelId',
27+
})
28+
.run(conn)
29+
.then(cursor => cursor.toArray());
30+
31+
if (
32+
!communityOwnerChannelRecord ||
33+
communityOwnerChannelRecord.length === 0
34+
) {
35+
await r
36+
.db('spectrum')
37+
.table('usersChannels')
38+
.insert({
39+
channelId: channel.id,
40+
userId: community.creatorId,
41+
createdAt: new Date(),
42+
isOwner: true,
43+
isMember: true,
44+
isModerator: false,
45+
isBlocked: false,
46+
isPending: false,
47+
receiveNotifications: false,
48+
})
49+
.run(conn);
50+
} else {
51+
await r
52+
.db('spectrum')
53+
.table('usersChannels')
54+
.getAll([community.creatorId, channel.id], {
55+
index: 'userIdAndChannelId',
56+
})
57+
.update({ isOwner: true })
58+
.run(conn);
59+
}
60+
61+
return await r
62+
.db('spectrum')
63+
.table('usersChannels')
64+
.getAll(channel.id, { index: 'channelId' })
65+
.filter({ isMember: true })
66+
.filter(row => row('userId').ne(community.creatorId))
67+
.update({ isMember: false })
68+
.run(conn);
69+
})
70+
);
71+
};
72+
73+
exports.down = function(r, conn) {
74+
return Promise.resolve();
75+
};

api/models/usersChannels.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,11 @@ const toggleUserChannelNotifications = async (userId: string, channelId: string,
466466
.getAll([userId, channelId], { index: 'userIdAndChannelId' })
467467
.run();
468468

469+
const channel = await db
470+
.table('channels')
471+
.get(channelId)
472+
.run()
473+
469474
// permissions exist, this user is trying to toggle notifications for a channel where they
470475
// are already a member
471476
if (permissions && permissions.length > 0) {
@@ -476,8 +481,16 @@ const toggleUserChannelNotifications = async (userId: string, channelId: string,
476481
.run();
477482
}
478483

479-
// if permissions don't exist, create a usersChannel relationship with notifications on
480-
return createMemberInChannel(channelId, userId, false)
484+
// if the channel isn't private, it means the user is enabling notifications
485+
// in a public channel that they have not yet joined - for example, if a user
486+
// joins a community, then some time later the community creates a new channel,
487+
// then again some time later the user wants notifications about that channel
488+
if (!channel.isPrivate) {
489+
// if permissions don't exist, create a usersChannel relationship with notifications on
490+
return createMemberInChannel(channelId, userId, false)
491+
}
492+
493+
return
481494
};
482495

483496
const removeUsersChannelMemberships = async (userId: string) => {

api/routes/api/email.js

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
// @flow
22
require('now-env');
33
const IS_PROD = process.env.NODE_ENV === 'production';
4-
const IS_TESTING = process.env.TEST_DB;
5-
import { BRIAN_ID } from '../../migrations/seed/default/constants';
64
import { Router } from 'express';
75
const jwt = require('jsonwebtoken');
86
const emailRouter = Router();
97
import { updateUserEmail } from 'shared/db/queries/user';
108
import { unsubscribeUserFromEmailNotification } from '../../models/usersSettings';
119
import { updateThreadNotificationStatusForUser } from '../../models/usersThreads';
1210
import { updateDirectMessageThreadNotificationStatusForUser } from '../../models/usersDirectMessageThreads';
13-
import { toggleUserChannelNotifications } from '../../models/usersChannels';
11+
import {
12+
toggleUserChannelNotifications,
13+
getUsersPermissionsInChannels,
14+
} from '../../models/usersChannels';
1415
import {
1516
updateCommunityAdministratorEmail,
16-
resetCommunityAdministratorEmail,
1717
getCommunityById,
1818
} from '../../models/community';
1919
import { getChannelsByCommunity, getChannelById } from '../../models/channel';
@@ -89,18 +89,28 @@ emailRouter.get('/unsubscribe', async (req, res) => {
8989
}
9090
case 'muteCommunity': {
9191
const community = await getCommunityById(dataId);
92-
return getChannelsByCommunity(dataId)
93-
.then(channels => channels.map(c => c.id))
94-
.then(channels =>
95-
channels.map(c => toggleUserChannelNotifications(userId, c, false))
96-
)
97-
.then(() =>
98-
res.redirect(
99-
`${rootRedirect}/${
100-
community.slug
101-
}?toastType=success&toastMessage=You will no longer receive new thread emails from this community.`
102-
)
103-
);
92+
const channels = await getChannelsByCommunity(dataId);
93+
const channelIds = channels.map(channel => channel.id);
94+
const usersChannels = await getUsersPermissionsInChannels(
95+
channelIds.map(id => [userId, id])
96+
);
97+
const usersChannelsWithNotifications = usersChannels.filter(
98+
usersChannel => usersChannel && usersChannel.receiveNotifications
99+
);
100+
const channelIdsWithNotifications = usersChannelsWithNotifications.map(
101+
usersChannel => usersChannel.channelId
102+
);
103+
104+
await channelIdsWithNotifications.map(
105+
async channelId =>
106+
await toggleUserChannelNotifications(userId, channelId, false)
107+
);
108+
109+
return res.redirect(
110+
`${rootRedirect}/${
111+
community.slug
112+
}?toastType=success&toastMessage=You will no longer receive new thread emails from this community.`
113+
);
104114
}
105115
case 'muteThread':
106116
return updateThreadNotificationStatusForUser(

0 commit comments

Comments
 (0)