Skip to content

Commit b413b74

Browse files
fix: race condition when room invite is accepted
1 parent 35a6fdf commit b413b74

File tree

2 files changed

+43
-17
lines changed

2 files changed

+43
-17
lines changed

apps/meteor/client/views/room/hooks/useRoomInvitation.tsx

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,61 @@
1-
import { useRouter, useUser } from '@rocket.chat/ui-contexts';
1+
import { useEffectEvent } from '@rocket.chat/fuselage-hooks';
2+
import { useStream, useUser } from '@rocket.chat/ui-contexts';
23
import { useQueryClient } from '@tanstack/react-query';
4+
import { useEffect } from 'react';
35

46
import { useRoomRejectInvitationModal } from './useRoomRejectInvitationModal';
57
import { useEndpointMutation } from '../../../hooks/useEndpointMutation';
68
import { roomsQueryKeys, subscriptionsQueryKeys } from '../../../lib/queryKeys';
79
import type { IRoomWithFederationOriginalName } from '../contexts/RoomContext';
810

911
export const useRoomInvitation = (room: IRoomWithFederationOriginalName) => {
10-
const queryClient = useQueryClient();
1112
const user = useUser();
12-
const router = useRouter();
1313

14+
if (!user) {
15+
throw new Error('error-user-not-found');
16+
}
17+
18+
const queryClient = useQueryClient();
19+
const subscribeToNotifyUser = useStream('notify-user');
1420
const { open: openConfirmationModal } = useRoomRejectInvitationModal(room);
21+
const replyInvite = useEndpointMutation('POST', '/v1/rooms.invite');
1522

16-
const replyInvite = useEndpointMutation('POST', '/v1/rooms.invite', {
17-
onSuccess: async (_, { action }) => {
18-
const reference = room.federationOriginalName ?? room.name;
23+
const invalidateQueries = useEffectEvent(() => {
24+
const reference = room.federationOriginalName ?? room.name ?? room._id;
25+
return Promise.all([
26+
queryClient.invalidateQueries({ queryKey: roomsQueryKeys.room(room._id) }),
27+
queryClient.invalidateQueries({ queryKey: subscriptionsQueryKeys.subscription(room._id) }),
28+
queryClient.refetchQueries({
29+
queryKey: roomsQueryKeys.roomReference(reference, room.t, user._id, user.username),
30+
}),
31+
]);
32+
});
1933

20-
if (reference) {
21-
await queryClient.refetchQueries({
22-
queryKey: roomsQueryKeys.roomReference(reference, room.t, user?._id, user?.username),
23-
});
24-
}
34+
useEffect(() => {
35+
// Only listen for subscription changes if the mutation has been initiated
36+
if (!replyInvite.isPending) {
37+
return;
38+
}
2539

26-
await queryClient.invalidateQueries({ queryKey: roomsQueryKeys.room(room._id) });
27-
await queryClient.invalidateQueries({ queryKey: subscriptionsQueryKeys.subscription(room._id) });
40+
/*
41+
* NOTE: We need to listen for subscription changes here because when accepting an invitation
42+
* to a federated room, the server processes the acceptance asynchronously. Therefore,
43+
* we cannot rely solely on the mutation's completion to know when the subscription status
44+
* has changed. By subscribing to the 'notify-user' stream, we can react to changes in the
45+
* subscription status and ensure that our UI reflects the most up-to-date information.
46+
*/
47+
return subscribeToNotifyUser(`${user._id}/subscriptions-changed`, async (event, data) => {
48+
if (data.rid !== room._id) {
49+
return;
50+
}
2851

29-
if (action === 'reject') {
30-
router.navigate('/home');
52+
if (event !== 'removed' && data.status !== undefined) {
53+
return;
3154
}
32-
},
33-
});
55+
56+
await invalidateQueries();
57+
});
58+
}, [room._id, user._id, invalidateQueries, replyInvite.isPending, subscribeToNotifyUser]);
3459

3560
return {
3661
...replyInvite,

packages/ddp-client/src/types/streams.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ export interface StreamerEvents {
154154
| 'tunread'
155155
| 'tunreadGroup'
156156
| 'tunreadUser'
157+
| 'status'
157158

158159
// Omnichannel fields
159160
| 'department'

0 commit comments

Comments
 (0)