Skip to content

Commit d1ca964

Browse files
committed
Merge branch 'control-join-requests' into dev
2 parents 1fd560b + 20c0a4f commit d1ca964

File tree

13 files changed

+161
-18
lines changed

13 files changed

+161
-18
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { trpc } from 'src/trpc/server';
22

33
import { makePublicProcedure } from './make-public';
4+
import { setJoinRequestsAllowedProcedure } from './set-join-requests-allowed';
45

56
export const privacyRouter = trpc.router({
67
makePublic: makePublicProcedure(),
8+
setJoinRequestsAllowed: setJoinRequestsAllowedProcedure(),
79
});
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { isNanoID } from '@stdlib/misc';
2+
import { checkRedlockSignalAborted } from '@stdlib/redlock';
3+
import { once } from 'lodash';
4+
import type { InferProcedureOpts } from 'src/trpc/helpers';
5+
import { authProcedure } from 'src/trpc/helpers';
6+
import { z } from 'zod';
7+
8+
const baseProcedure = authProcedure.input(
9+
z.object({
10+
groupId: z.string().refine(isNanoID),
11+
12+
areJoinRequestsAllowed: z.boolean(),
13+
}),
14+
);
15+
16+
export const setJoinRequestsAllowedProcedure = once(() =>
17+
baseProcedure.mutation(setJoinRequestsAllowed),
18+
);
19+
20+
export async function setJoinRequestsAllowed({
21+
ctx,
22+
input,
23+
}: InferProcedureOpts<typeof baseProcedure>) {
24+
return await ctx.usingLocks(
25+
[[`user-lock:${ctx.userId}`], [`group-lock:${input.groupId}`]],
26+
async (signals) => {
27+
return await ctx.dataAbstraction.transaction(async (dtrx) => {
28+
// Assert agent is subscribed
29+
30+
await ctx.assertUserSubscribed({ userId: ctx.userId });
31+
32+
// Check if user has sufficient permissions
33+
34+
await ctx.assertSufficientGroupPermissions({
35+
userId: ctx.userId,
36+
groupId: input.groupId,
37+
permission: 'editGroupSettings',
38+
});
39+
40+
await ctx.dataAbstraction.hmset(
41+
'group',
42+
input.groupId,
43+
{ 'are-join-requests-allowed': input.areJoinRequestsAllowed },
44+
{ dtrx },
45+
);
46+
47+
checkRedlockSignalAborted(signals);
48+
});
49+
},
50+
);
51+
}

apps/app-server/src/websocket/groups/change-user-role.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ export function registerGroupsChangeUserRole(
3939
await ctx.usingLocks(
4040
[
4141
[`user-lock:${ctx.userId}`],
42-
[`user-lock:${input.patientId}`],
42+
...(input.patientId !== ctx.userId
43+
? [[`user-lock:${input.patientId}`]]
44+
: []),
4345
[`group-lock:${input.groupId}`],
4446
],
4547
performCommunication,

apps/app-server/src/websocket/groups/join-requests/send.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,17 @@ export async function sendStep1({
5555

5656
await ctx.assertUserSubscribed({ userId: ctx.userId });
5757

58-
const [groupJoinRequestRejected, groupMemberRole] = await Promise.all([
58+
const [
59+
groupAreJoinRequestsAllowed,
60+
groupJoinRequestRejected,
61+
groupMemberRole,
62+
] = await Promise.all([
63+
ctx.dataAbstraction.hget(
64+
'group',
65+
input.groupId,
66+
'are-join-requests-allowed',
67+
),
68+
5969
ctx.dataAbstraction.hget(
6070
'group-join-request',
6171
`${input.groupId}:${ctx.userId}`,
@@ -69,6 +79,15 @@ export async function sendStep1({
6979
),
7080
]);
7181

82+
// Check if group allows join requests
83+
84+
if (!groupAreJoinRequestsAllowed) {
85+
throw new TRPCError({
86+
code: 'FORBIDDEN',
87+
message: 'This group does not allow join requests.',
88+
});
89+
}
90+
7291
// Check if user has been rejected from the group
7392

7493
if (groupJoinRequestRejected) {

apps/app-server/src/websocket/groups/remove-user.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ export function registerGroupsRemoveUser(fastify: ReturnType<typeof Fastify>) {
3636
await ctx.usingLocks(
3737
[
3838
[`user-lock:${ctx.userId}`],
39-
[`user-lock:${input.patientId}`],
39+
...(input.patientId !== ctx.userId
40+
? [[`user-lock:${input.patientId}`]]
41+
: []),
4042
[`group-lock:${input.groupId}`],
4143
],
4244
performCommunication,

apps/client/src/layouts/PagesLayout/MainContent/DisplayPage/DisplayScreens/DisplayUnauthorizedScreen.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
v-if="
66
uiStore().loggedIn &&
77
!realtimeCtx.loading &&
8-
!realtimeCtx.hget('group', page.react.groupId, 'is-personal')
8+
!realtimeCtx.hget('group', page.react.groupId, 'is-personal') &&
9+
realtimeCtx.hget('group', page.react.groupId, 'are-join-requests-allowed')
910
"
1011
>
1112
<Gap style="height: 12px" />

apps/client/src/layouts/PagesLayout/MainToolbar/Notifications/Items/GroupRequestSent.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ const canCancelRequest = computed(
7878
const canAcceptRequest = computed(() => {
7979
const rejected = realtimeCtx.hget(
8080
'group-join-request',
81-
`${notificationContent.value.groupId}:${authStore().userId}`,
81+
`${notificationContent.value.groupId}:${notificationContent.value.agentId}`,
8282
'rejected',
8383
);
8484
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<template>
2+
<Checkbox
3+
@update:model-value="(value) => setJoinRequestsAllowed(value)"
4+
:model-value="
5+
internals.realtime.globalCtx.hget(
6+
'group',
7+
groupId,
8+
'are-join-requests-allowed',
9+
)
10+
"
11+
label="Allow join requests"
12+
/>
13+
</template>
14+
15+
<script setup lang="ts">
16+
import { handleError } from 'src/code/utils/misc';
17+
18+
const groupId = inject<string>('groupId')!;
19+
20+
async function setJoinRequestsAllowed(value: boolean) {
21+
try {
22+
await trpcClient.groups.privacy.setJoinRequestsAllowed.mutate({
23+
groupId,
24+
25+
areJoinRequestsAllowed: value,
26+
});
27+
28+
if (value) {
29+
$quasar().notify({
30+
message: 'Join requests are now enabled in this group.',
31+
type: 'positive',
32+
});
33+
} else {
34+
$quasar().notify({
35+
message: 'Join requests are now disabled in this group.',
36+
type: 'positive',
37+
});
38+
}
39+
} catch (error) {
40+
handleError(error);
41+
}
42+
}
43+
</script>

apps/client/src/layouts/PagesLayout/RightSidebar/PageProperties/GroupSettingsDialog/GeneralTab/GeneralTab.vue

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -97,16 +97,16 @@
9797
/>
9898
</template>
9999

100-
<Gap style="height: 24px" />
100+
<Gap style="height: 28px" />
101101

102102
<q-separator />
103103

104104
<Gap style="height: 20px" />
105105
</template>
106106

107-
<div>Group infos</div>
107+
<b>Group infos</b>
108108

109-
<Gap style="height: 8px" />
109+
<Gap style="height: 20px" />
110110

111111
<TextField
112112
label="Group ID"
@@ -166,15 +166,15 @@
166166
]?.permissions.editGroupSettings
167167
"
168168
>
169-
<Gap style="height: 24px" />
169+
<Gap style="height: 28px" />
170170

171171
<q-separator />
172172

173173
<Gap style="height: 20px" />
174174

175-
<div>Group security</div>
175+
<b>Group security</b>
176176

177-
<Gap style="height: 10px" />
177+
<Gap style="height: 20px" />
178178

179179
<div style="max-width: 300px; display: flex; flex-direction: column">
180180
<template v-if="groupId !== internals.personalGroupId">
@@ -217,17 +217,21 @@
217217
]?.permissions.editGroupSettings
218218
"
219219
>
220-
<Gap style="height: 24px" />
220+
<Gap style="height: 28px" />
221221

222222
<q-separator />
223223

224224
<Gap style="height: 20px" />
225225

226-
<div>Group privacy</div>
226+
<b>Group privacy</b>
227227

228-
<Gap style="height: 10px" />
228+
<Gap style="height: 20px" />
229229

230230
<div style="max-width: 300px; display: flex; flex-direction: column">
231+
<AllowJoinRequestsCheckbox />
232+
233+
<Gap style="height: 16px" />
234+
231235
<MakePrivateBtn
232236
v-if="
233237
internals.realtime.globalCtx.hget('group', groupId, 'is-public')
@@ -249,15 +253,15 @@
249253
]?.permissions.editGroupSettings
250254
"
251255
>
252-
<Gap style="height: 24px" />
256+
<Gap style="height: 28px" />
253257

254258
<q-separator />
255259

256260
<Gap style="height: 20px" />
257261

258-
<div>Danger zone</div>
262+
<b>Danger zone</b>
259263

260-
<Gap style="height: 10px" />
264+
<Gap style="height: 20px" />
261265

262266
<div style="max-width: 300px; display: flex; flex-direction: column">
263267
<DeleteGroupBtn />
@@ -276,6 +280,7 @@ import type { RealtimeContext } from 'src/code/realtime/context';
276280
import { isCtrlDown } from 'src/code/utils/misc';
277281
import type { Ref } from 'vue';
278282
283+
import AllowJoinRequestsCheckbox from './AllowJoinRequestsCheckbox.vue';
279284
import DeleteGroupBtn from './DeleteGroupBtn.vue';
280285
import MakePrivateBtn from './Privacy/MakePrivateBtn.vue';
281286
import MakePublicBtn from './Privacy/MakePublicBtn.vue';

apps/client/src/layouts/PagesLayout/RightSidebar/PageProperties/GroupSettingsDialog/MembersTab/MembersTab.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,9 @@ async function removeSelectedUsers() {
244244
});
245245
}
246246
247-
await rotateGroupKeys({ groupId });
247+
if (canManageSelected.value) {
248+
await rotateGroupKeys({ groupId });
249+
}
248250
249251
if (finalSelectedUserIds.value.includes(authStore().userId)) {
250252
await removeGroupUser({

0 commit comments

Comments
 (0)