Skip to content

Commit 2be25bb

Browse files
authored
Merge branch 'develop' into fix/NonMembers-InFollowers
2 parents fe2dfca + 7819573 commit 2be25bb

6 files changed

Lines changed: 133 additions & 66 deletions

File tree

apps/meteor/client/views/mediaCallHistory/useMediaCallInternalHistoryActions.ts

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { useEffectEvent } from '@rocket.chat/fuselage-hooks';
2+
import { useGoToDirectMessage } from '@rocket.chat/ui-client';
23
import { useRouter, useUserAvatarPath } from '@rocket.chat/ui-contexts';
34
import { useMediaCallContext } from '@rocket.chat/ui-voip';
45
import { useMemo } from 'react';
56

6-
import { useDirectMessageAction } from '../room/hooks/useUserInfoActions/actions/useDirectMessageAction';
7-
87
export type InternalCallHistoryContact = {
98
_id: string;
109
name?: string;
@@ -48,21 +47,7 @@ export const useMediaCallInternalHistoryActions = ({
4847
});
4948
});
5049

51-
const directMessage = useDirectMessageAction(contact, openRoomId ?? '');
52-
53-
const goToDirectMessage = useMemo(() => {
54-
if (directMessage?.onClick) {
55-
return directMessage.onClick;
56-
}
57-
if (!messageRoomId || openRoomId) {
58-
return;
59-
}
60-
return () =>
61-
router.navigate({
62-
name: 'direct',
63-
params: { rid: messageRoomId },
64-
});
65-
}, [directMessage?.onClick, messageRoomId, openRoomId, router]);
50+
const goToDirectMessage = useGoToDirectMessage({ username: contact.username }, openRoomId ?? '');
6651

6752
const jumpToMessage = useEffectEvent(() => {
6853
const rid = messageRoomId || openRoomId;
Lines changed: 17 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,27 @@
1-
import type { IRoom, IUser, ISubscription } from '@rocket.chat/core-typings';
2-
import { useEffectEvent } from '@rocket.chat/fuselage-hooks';
3-
import { useTranslation, usePermission, useRoute, useUserSubscription, useUserSubscriptionByName } from '@rocket.chat/ui-contexts';
1+
import type { IRoom, IUser } from '@rocket.chat/core-typings';
2+
import { useGoToDirectMessage } from '@rocket.chat/ui-client';
43
import { useMemo } from 'react';
4+
import { useTranslation } from 'react-i18next';
55

6-
import type { UserInfoAction, UserInfoActionType } from '../useUserInfoActions';
7-
8-
const getShouldOpenDirectMessage = (
9-
currentSubscription?: ISubscription,
10-
usernameSubscription?: ISubscription,
11-
canOpenDirectMessage?: boolean,
12-
username?: IUser['username'],
13-
): boolean => {
14-
const canOpenDm = canOpenDirectMessage || usernameSubscription;
15-
const directMessageIsNotAlreadyOpen = currentSubscription && currentSubscription.name !== username;
16-
return (canOpenDm && directMessageIsNotAlreadyOpen) ?? false;
17-
};
6+
import type { UserInfoAction } from '../useUserInfoActions';
187

198
export const useDirectMessageAction = (user: Pick<IUser, '_id' | 'username'>, rid: IRoom['_id']): UserInfoAction | undefined => {
20-
const t = useTranslation();
21-
const usernameSubscription = useUserSubscriptionByName(user.username ?? '');
22-
const currentSubscription = useUserSubscription(rid);
23-
const canOpenDirectMessage = usePermission('create-d');
24-
const directRoute = useRoute('direct');
9+
const { t } = useTranslation();
2510

26-
const shouldOpenDirectMessage = getShouldOpenDirectMessage(
27-
currentSubscription,
28-
usernameSubscription,
29-
canOpenDirectMessage,
30-
user.username,
31-
);
11+
const openDirectMessage = useGoToDirectMessage(user, rid);
3212

33-
const openDirectMessage = useEffectEvent(
34-
() =>
35-
user.username &&
36-
directRoute.push({
37-
rid: user.username,
38-
}),
39-
);
13+
const openDirectMessageOption = useMemo(() => {
14+
if (!openDirectMessage) {
15+
return undefined;
16+
}
4017

41-
const openDirectMessageOption = useMemo(
42-
() =>
43-
shouldOpenDirectMessage
44-
? {
45-
content: t('Direct_Message'),
46-
icon: 'balloon' as const,
47-
onClick: openDirectMessage,
48-
type: 'communication' as UserInfoActionType,
49-
}
50-
: undefined,
51-
[openDirectMessage, shouldOpenDirectMessage, t],
52-
);
18+
return {
19+
content: t('Direct_Message'),
20+
icon: 'balloon' as const,
21+
onClick: openDirectMessage,
22+
type: 'communication',
23+
} as const;
24+
}, [openDirectMessage, t]);
5325

5426
return openDirectMessageOption;
5527
};

packages/media-signaling/src/lib/Session.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,20 @@ export class MediaSignalingSession extends Emitter<MediaSignalingEvents> {
426426
return this.hangupCallsThatNeedInput();
427427
}
428428

429-
return this.setInputTrack(tracks[0]);
429+
const inputTrack = tracks[0];
430+
431+
// If we no longer have a call that can use this track, just release it
432+
if (inputTrack && !this.mayNeedInputTrack()) {
433+
try {
434+
// Stop the track so the browser doesn't have to wait for GC to detect that the stream is not in use
435+
inputTrack.stop();
436+
} catch {
437+
// we don't care if this failed
438+
}
439+
return;
440+
}
441+
442+
return this.setInputTrack(inputTrack);
430443
}
431444

432445
private hangupCallsThatNeedInput(): void {
@@ -445,14 +458,22 @@ export class MediaSignalingSession extends Emitter<MediaSignalingEvents> {
445458
}
446459
}
447460

448-
private async maybeStopInputTrack(): Promise<void> {
449-
this.config.logger?.debug('MediaSignalingSession.maybeStopInputTrack');
461+
private mayNeedInputTrack(): boolean {
450462
for (const call of this.knownCalls.values()) {
451463
if (call.mayNeedInputTrack()) {
452-
return;
464+
return true;
453465
}
454466
}
455467

468+
return false;
469+
}
470+
471+
private async maybeStopInputTrack(): Promise<void> {
472+
this.config.logger?.debug('MediaSignalingSession.maybeStopInputTrack');
473+
if (this.mayNeedInputTrack()) {
474+
return;
475+
}
476+
456477
await this.setInputTrack(null);
457478
}
458479

packages/ui-client/src/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ export * from './useLicense';
88
export * from './usePreferenceFeaturePreviewList';
99
export * from './useUserDisplayName';
1010
export * from './useValidatePassword';
11+
export * from './useGoToDirectMessage';
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { mockAppRoot } from '@rocket.chat/mock-providers';
2+
import type { SubscriptionWithRoom } from '@rocket.chat/ui-contexts';
3+
import { renderHook } from '@testing-library/react';
4+
5+
import { useGoToDirectMessage } from './useGoToDirectMessage';
6+
7+
it('should return undefined if username is not provided', () => {
8+
const { result } = renderHook(() => useGoToDirectMessage({}), {
9+
wrapper: mockAppRoot().build(),
10+
});
11+
12+
expect(result.current).toBe(undefined);
13+
});
14+
15+
it("should return undefined if the user doesn't have permission to create direct messages and doesn't have a subscription with target user", () => {
16+
const { result } = renderHook(() => useGoToDirectMessage({ username: 'test' }), {
17+
wrapper: mockAppRoot().build(),
18+
});
19+
20+
expect(result.current).toBe(undefined);
21+
});
22+
23+
it('should return undefined if the room is already open', () => {
24+
const { result } = renderHook(() => useGoToDirectMessage({ username: 'test' }, 'test-room'), {
25+
wrapper: mockAppRoot()
26+
.withSubscription({ _id: 'test-room', name: 'test', t: 'd', rid: 'test-room' } as SubscriptionWithRoom)
27+
.build(),
28+
});
29+
30+
expect(result.current).toBe(undefined);
31+
});
32+
33+
it('should return a function to navigate to the direct message room if the user has permission to create direct messages and no subscription with target user', () => {
34+
const { result } = renderHook(() => useGoToDirectMessage({ username: 'test' }), {
35+
wrapper: mockAppRoot().withPermission('create-d').build(),
36+
});
37+
38+
expect(typeof result.current).toBe('function');
39+
});
40+
41+
it("should return a function to navigate to the direct message room if the user has a subscription with target user and doesn't have permission to create direct messages", () => {
42+
const { result } = renderHook(() => useGoToDirectMessage({ username: 'test' }), {
43+
wrapper: mockAppRoot()
44+
.withSubscription({ _id: 'test-room', name: 'test', t: 'd', rid: 'test-room' } as SubscriptionWithRoom)
45+
.build(),
46+
});
47+
48+
expect(typeof result.current).toBe('function');
49+
});
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { useEffectEvent } from '@rocket.chat/fuselage-hooks';
2+
import { usePermission, useUserSubscriptionByName, useRouter } from '@rocket.chat/ui-contexts';
3+
4+
// TODO: Routes type definitions are declared in-file for most places, so this route doesn't exist in this package
5+
declare module '@rocket.chat/ui-contexts' {
6+
export interface IRouterPaths {
7+
direct: {
8+
pathname: `/direct/:rid${`/${string}` | ''}${`/${string}` | ''}`;
9+
pattern: '/direct/:rid/:tab?/:context?';
10+
};
11+
}
12+
}
13+
14+
/**
15+
* Hook to navigate to a direct message room
16+
* @param targetUser - Object containing the username of the user to navigate to
17+
* @param openRoomId - Optional ID of the room that is already open
18+
* @returns A function to navigate to the direct message room, or undefined if the room is already open or the user doesn't have permission to create direct messages and doesn't have a subscription to the target user
19+
*/
20+
export const useGoToDirectMessage = (targetUser: { username?: string }, openRoomId?: string): (() => void) | undefined => {
21+
const usernameSubscription = useUserSubscriptionByName(targetUser.username ?? '');
22+
const router = useRouter();
23+
const canOpenDirectMessage = usePermission('create-d');
24+
25+
const hasPermissionOrSubscription = usernameSubscription || canOpenDirectMessage;
26+
const alreadyOpen = openRoomId && usernameSubscription?.rid === openRoomId;
27+
const shouldOpen = targetUser.username && hasPermissionOrSubscription && !alreadyOpen;
28+
29+
const openDirectMessage = useEffectEvent(
30+
() =>
31+
targetUser.username &&
32+
router.navigate({
33+
name: 'direct' as const,
34+
params: { rid: targetUser.username },
35+
} as const),
36+
);
37+
38+
return shouldOpen ? openDirectMessage : undefined;
39+
};

0 commit comments

Comments
 (0)