diff --git a/.changeset/sixty-numbers-tan.md b/.changeset/sixty-numbers-tan.md
new file mode 100644
index 0000000000000..d0c6d9367cf85
--- /dev/null
+++ b/.changeset/sixty-numbers-tan.md
@@ -0,0 +1,6 @@
+---
+"@rocket.chat/i18n": minor
+"@rocket.chat/ui-voip": minor
+---
+
+Introduces native desktop notifications for voice calls when using the desktop app.
diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json
index 640ef7f49df6f..0984f1b3c9bd2 100644
--- a/packages/i18n/src/locales/en.i18n.json
+++ b/packages/i18n/src/locales/en.i18n.json
@@ -2560,6 +2560,7 @@
"Incoming_Livechats": "Queued chats",
"Incoming_WebHook": "Incoming WebHook",
"Incoming_call": "Incoming call",
+ "Incoming_call_ellipsis": "Incoming call...",
"Incoming_call_from": "Incoming call from",
"Incoming_call_from__roomName__": "Incoming call from {{roomName}}",
"Incoming_call_transfer": "Incoming call transfer",
diff --git a/packages/ui-voip/package.json b/packages/ui-voip/package.json
index df20769403453..7fdb5ec514721 100644
--- a/packages/ui-voip/package.json
+++ b/packages/ui-voip/package.json
@@ -19,6 +19,7 @@
"typecheck": "tsc --noEmit --skipLibCheck -p tsconfig.json"
},
"dependencies": {
+ "@rocket.chat/desktop-api": "workspace:^",
"@rocket.chat/emitter": "~0.31.25",
"@rocket.chat/media-signaling": "workspace:~",
"@tanstack/react-query": "~5.65.1",
diff --git a/packages/ui-voip/src/global.d.ts b/packages/ui-voip/src/global.d.ts
new file mode 100644
index 0000000000000..51c30e263ccad
--- /dev/null
+++ b/packages/ui-voip/src/global.d.ts
@@ -0,0 +1,7 @@
+import type { IRocketChatDesktop } from '@rocket.chat/desktop-api';
+
+declare global {
+ interface Window {
+ RocketChatDesktop?: IRocketChatDesktop;
+ }
+}
diff --git a/packages/ui-voip/src/v2/MediaCallProvider.tsx b/packages/ui-voip/src/v2/MediaCallProvider.tsx
index 069c13b307d02..e1a80109e2daa 100644
--- a/packages/ui-voip/src/v2/MediaCallProvider.tsx
+++ b/packages/ui-voip/src/v2/MediaCallProvider.tsx
@@ -11,7 +11,7 @@ import {
useToastMessageDispatch,
useSetting,
} from '@rocket.chat/ui-contexts';
-import { useCallback, useEffect } from 'react';
+import { ReactNode, useCallback, useEffect } from 'react';
import { createPortal } from 'react-dom';
import { useTranslation } from 'react-i18next';
@@ -19,13 +19,18 @@ import MediaCallContext, { PeerInfo } from './MediaCallContext';
import MediaCallWidget from './MediaCallWidget';
import TransferModal from './TransferModal';
import { useCallSounds } from './useCallSounds';
+import { useDesktopNotifications } from './useDesktopNotifications';
import { getExtensionFromPeerInfo, useMediaSession } from './useMediaSession';
import { useMediaSessionInstance } from './useMediaSessionInstance';
import useMediaStream from './useMediaStream';
import { isValidTone, useTonePlayer } from './useTonePlayer';
import { stopTracks, useDevicePermissionPrompt2, PermissionRequestCancelledCallRejectedError } from '../hooks/useDevicePermissionPrompt';
-const MediaCallProvider = ({ children }: { children: React.ReactNode }) => {
+type MediaCallProviderProps = {
+ children: ReactNode;
+};
+
+const MediaCallProvider = ({ children }: MediaCallProviderProps) => {
const user = useUser();
const { t } = useTranslation();
const dispatchToastMessage = useToastMessageDispatch();
@@ -37,6 +42,8 @@ const MediaCallProvider = ({ children }: { children: React.ReactNode }) => {
const instance = useMediaSessionInstance(userId ?? undefined);
const session = useMediaSession(instance);
+ useDesktopNotifications(session);
+
const [remoteStreamRefCallback, audioElement] = useMediaStream(instance);
const setOutputMediaDevice = useSetOutputMediaDevice();
diff --git a/packages/ui-voip/src/v2/MediaCallWidget.stories.tsx b/packages/ui-voip/src/v2/MediaCallWidget.stories.tsx
index 84c6d34bc46d1..4fe83d06c602e 100644
--- a/packages/ui-voip/src/v2/MediaCallWidget.stories.tsx
+++ b/packages/ui-voip/src/v2/MediaCallWidget.stories.tsx
@@ -4,7 +4,7 @@ import type { Meta, StoryFn, StoryObj } from '@storybook/react';
import { useMediaCallContext } from './MediaCallContext';
import MediaCallWidget from './MediaCallWidget';
-import MediaCallProviderMock from './MockedMediaCallProvider';
+import MockedMediaCallProvider from './MockedMediaCallProvider';
const mockedContexts = mockAppRoot()
.withTranslations('en', 'core', {
@@ -26,9 +26,9 @@ const meta = {
decorators: [
mockedContexts,
(Story, options) => (
-
+
-
+
),
],
} satisfies Meta;
diff --git a/packages/ui-voip/src/v2/MockedMediaCallProvider.tsx b/packages/ui-voip/src/v2/MockedMediaCallProvider.tsx
index 1da93f4a3206e..b49dc37bf74d1 100644
--- a/packages/ui-voip/src/v2/MockedMediaCallProvider.tsx
+++ b/packages/ui-voip/src/v2/MockedMediaCallProvider.tsx
@@ -1,5 +1,5 @@
import { UserStatus } from '@rocket.chat/core-typings';
-import { useState } from 'react';
+import { ReactNode, useState } from 'react';
import MediaCallContext from './MediaCallContext';
import type { State, PeerInfo } from './MediaCallContext';
@@ -7,7 +7,17 @@ import type { State, PeerInfo } from './MediaCallContext';
const avatarUrl = `data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAoACgDASIAAhEBAxEB/8QAGwAAAgIDAQAAAAAAAAAAAAAAAAcEBgIDBQj/xAAuEAACAQQAAwcEAQUAAAAAAAABAgMABAUREiExBhMUIkFRYQcWcYGhFTJSgpH/xAAYAQADAQEAAAAAAAAAAAAAAAACAwQBAP/EAB4RAAIBBQEBAQAAAAAAAAAAAAABAgMREiExE0HR/9oADAMBAAIRAxEAPwBuXuIkhBuMe5ib/AHQP49q4L3mLitryTLTSpOiHQI5k/HzXa/qbFOEudVTu1dumWvcTaNCZYZ7vU6g6LxqjOU/24dfs1Ouh9FnkMpd3Reeyx83hAxZZEhkdV9/MBrX71WGPvJcqrJBGveKATtuXXqNU0pu02bTHXD/AGvJAluyxxRd6F4x00o+NdKoVrjbzJdvVe1t5cVLc2ck8qjnohgpPtz2v7G6JtPQ2VJwjlcw+37mchpnK6GtIuv5NFWeTsLNPvxWTvpfjvOEfwKKzEVkSct2vscS/BIzSN0YRkeX81UpPqO8masJETu7OOccY4dswYFQeftv096XV5knuJGdm2T1+agvMXj8jEaHX905QihabvcbuS7X566mLWLwSY8PuRnk/u4eZ0deTl71Ef6hY+0yM88TzeNZY4luYwpVYyduOfrvhPTnr0pXSX9y5mCsyJMdyxxvwq599em+taItqCSNc90ChvZRUruUcT0JiO18Elpk7t8v41LWzacxkBSuvjQ/FFJayjDWrCTepAQ2vUH0oo/Jk3ovpwJJeVCP5CN+lFFaaMqy+nAyuChvrTI2kN9JAsi2ZOy4IBHMnkSCP+iqBexSWdxLazoUljJVlPUH2oorkV10pRc7b1zXb/hZOzuJvM86QWEXeELxOzHSIPcmiiiunVlF2RNTpRkrs//Z`;
const myData: any[] = Array.from({ length: 100 }, (_, i) => ({ value: `user-${i}`, label: `User ${i}`, identifier: `000${i}`, avatarUrl }));
-const MediaCallProviderMock = ({
+type MockedMediaCallProviderProps = {
+ children: ReactNode;
+ state?: State;
+ transferredBy?: string;
+ remoteMuted?: boolean;
+ remoteHeld?: boolean;
+ muted?: boolean;
+ held?: boolean;
+};
+
+const MockedMediaCallProvider = ({
children,
state = 'closed',
transferredBy = undefined,
@@ -15,15 +25,7 @@ const MediaCallProviderMock = ({
remoteHeld = false,
muted = false,
held = false,
-}: {
- children: React.ReactNode;
- state?: State;
- transferredBy?: string;
- remoteMuted?: boolean;
- remoteHeld?: boolean;
- muted?: boolean;
- held?: boolean;
-}) => {
+}: MockedMediaCallProviderProps) => {
const [peerInfo, setPeerInfo] = useState({
displayName: 'John Doe',
userId: '1234567890',
@@ -147,4 +149,4 @@ const MediaCallProviderMock = ({
return {children};
};
-export default MediaCallProviderMock;
+export default MockedMediaCallProvider;
diff --git a/packages/ui-voip/src/v2/components/Widget/Widget.tsx b/packages/ui-voip/src/v2/components/Widget/Widget.tsx
index 3b87d06a04026..5ba332a4be626 100644
--- a/packages/ui-voip/src/v2/components/Widget/Widget.tsx
+++ b/packages/ui-voip/src/v2/components/Widget/Widget.tsx
@@ -1,6 +1,6 @@
import { Palette } from '@rocket.chat/fuselage';
import styled from '@rocket.chat/styled';
-import { ComponentProps, useLayoutEffect } from 'react';
+import { ComponentProps, ReactNode, useLayoutEffect } from 'react';
import { FocusScope } from 'react-aria';
import { DragContext } from './WidgetDraggableContext';
@@ -28,7 +28,7 @@ const WidgetBase = styled('article')`
`;
type WidgetProps = {
- children: React.ReactNode;
+ children: ReactNode;
} & ComponentProps;
const Widget = ({ children, ...props }: WidgetProps) => {
diff --git a/packages/ui-voip/src/v2/useDesktopNotifications.ts b/packages/ui-voip/src/v2/useDesktopNotifications.ts
new file mode 100644
index 0000000000000..2038c54f5d715
--- /dev/null
+++ b/packages/ui-voip/src/v2/useDesktopNotifications.ts
@@ -0,0 +1,75 @@
+import { useEffect, useRef } from 'react';
+import { useTranslation } from 'react-i18next';
+
+import { PeerInfo } from './MediaCallContext';
+import { SessionInfo } from './useMediaSessionInstance';
+import { convertAvatarUrlToPng } from './utils/convertAvatarUrlToPng';
+
+const getDisplayInfo = (peerInfo?: PeerInfo) => {
+ if (!peerInfo) {
+ return undefined;
+ }
+
+ if ('number' in peerInfo) {
+ return { title: peerInfo.number };
+ }
+ if ('displayName' in peerInfo) {
+ return { title: peerInfo.displayName, avatar: peerInfo.avatarUrl };
+ }
+ return undefined;
+};
+
+export const useDesktopNotifications = (sessionInfo: SessionInfo) => {
+ const previousCallId = useRef(undefined);
+ const { t } = useTranslation();
+
+ const displayInfo = getDisplayInfo(sessionInfo.peerInfo);
+ useEffect(() => {
+ if (
+ typeof window.RocketChatDesktop?.dispatchCustomNotification !== 'function' ||
+ typeof window.RocketChatDesktop?.closeCustomNotification !== 'function'
+ ) {
+ return;
+ }
+
+ let isMounted = true;
+
+ if (sessionInfo.state !== 'ringing') {
+ if (previousCallId.current) {
+ window.RocketChatDesktop.closeCustomNotification(previousCallId.current);
+ previousCallId.current = undefined;
+ }
+ return;
+ }
+
+ if (!displayInfo?.title) {
+ return;
+ }
+
+ const notifyDesktop = async () => {
+ const avatarAsPng = await convertAvatarUrlToPng(displayInfo.avatar);
+
+ if (!isMounted) {
+ return;
+ }
+
+ window.RocketChatDesktop?.dispatchCustomNotification({
+ type: 'voice',
+ id: sessionInfo.callId,
+ payload: {
+ title: displayInfo.title,
+ body: t('Incoming_call_ellipsis'),
+ avatar: avatarAsPng || undefined,
+ requireInteraction: true,
+ },
+ });
+ };
+
+ notifyDesktop();
+ previousCallId.current = sessionInfo.callId;
+
+ return () => {
+ isMounted = false;
+ };
+ }, [displayInfo?.avatar, displayInfo?.title, sessionInfo.callId, sessionInfo.state, t]);
+};
diff --git a/packages/ui-voip/src/v2/useKeypad.tsx b/packages/ui-voip/src/v2/useKeypad.tsx
index 159cd6726496a..230776edacaaf 100644
--- a/packages/ui-voip/src/v2/useKeypad.tsx
+++ b/packages/ui-voip/src/v2/useKeypad.tsx
@@ -1,11 +1,11 @@
import { Divider, Box, TextInput, Field, FieldRow } from '@rocket.chat/fuselage';
-import { useState } from 'react';
+import { ReactNode, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Keypad } from './components';
type UseKeypad = {
- element: React.ReactNode;
+ element: ReactNode;
buttonProps: {
title: string;
onClick: () => void;
diff --git a/packages/ui-voip/src/v2/useMediaSession.ts b/packages/ui-voip/src/v2/useMediaSession.ts
index 062f30e07fec0..ab2bfe206b7ce 100644
--- a/packages/ui-voip/src/v2/useMediaSession.ts
+++ b/packages/ui-voip/src/v2/useMediaSession.ts
@@ -8,6 +8,7 @@ import type { SessionInfo } from './useMediaSessionInstance';
const defaultSessionInfo: SessionInfo = {
state: 'closed' as const,
+ callId: undefined,
connectionState: 'CONNECTING' as const,
peerInfo: undefined,
transferredBy: undefined,
@@ -48,7 +49,10 @@ export const getExtensionFromPeerInfo = (peerInfo: PeerInfo): string | undefined
return undefined;
};
-const deriveWidgetStateFromCallState = (callState: CallState, callRole: CallRole): State | undefined => {
+const deriveWidgetStateFromCallState = (
+ callState: CallState,
+ callRole: CallRole,
+): Extract | undefined => {
switch (callState) {
case 'active':
case 'accepted':
@@ -143,7 +147,18 @@ export const useMediaSession = (instance?: MediaSignalingSession): MediaSession
return;
}
- const { contact, transferredBy: callTransferredBy, state: callState, role, muted, held, hidden, remoteHeld, remoteMute } = mainCall;
+ const {
+ contact,
+ transferredBy: callTransferredBy,
+ state: callState,
+ role,
+ muted,
+ held,
+ hidden,
+ remoteHeld,
+ remoteMute,
+ callId,
+ } = mainCall;
const state = deriveWidgetStateFromCallState(callState, role);
const connectionState = deriveConnectionStateFromCallState(callState);
@@ -162,6 +177,7 @@ export const useMediaSession = (instance?: MediaSignalingSession): MediaSession
hidden,
remoteHeld,
remoteMuted: remoteMute,
+ callId,
},
});
return;
@@ -189,7 +205,7 @@ export const useMediaSession = (instance?: MediaSignalingSession): MediaSession
dispatch({
type: 'instance_updated',
- payload: { state, peerInfo, transferredBy, muted, held, connectionState, hidden, remoteHeld, remoteMuted: remoteMute },
+ payload: { state, peerInfo, transferredBy, muted, held, connectionState, hidden, remoteHeld, remoteMuted: remoteMute, callId },
});
};
diff --git a/packages/ui-voip/src/v2/useMediaSessionInstance.ts b/packages/ui-voip/src/v2/useMediaSessionInstance.ts
index d726191fa7cc4..c9c0da0db687f 100644
--- a/packages/ui-voip/src/v2/useMediaSessionInstance.ts
+++ b/packages/ui-voip/src/v2/useMediaSessionInstance.ts
@@ -23,10 +23,12 @@ interface BaseSession {
interface EmptySession extends BaseSession {
state: Extract;
+ callId: undefined;
}
interface CallSession extends BaseSession {
state: Extract;
+ callId: string;
peerInfo: PeerInfo;
}
diff --git a/packages/ui-voip/src/v2/utils/convertAvatarUrlToPng.ts b/packages/ui-voip/src/v2/utils/convertAvatarUrlToPng.ts
new file mode 100644
index 0000000000000..45da47cdcd55a
--- /dev/null
+++ b/packages/ui-voip/src/v2/utils/convertAvatarUrlToPng.ts
@@ -0,0 +1,36 @@
+export const convertAvatarUrlToPng = (avatarUrl: string | undefined): Promise => {
+ return new Promise((resolve) => {
+ if (!avatarUrl) {
+ resolve('');
+ return;
+ }
+
+ const image = new Image();
+
+ const onLoad = (): void => {
+ const canvas = document.createElement('canvas');
+ canvas.width = image.width;
+ canvas.height = image.height;
+ const context = canvas.getContext('2d');
+
+ if (!context) {
+ resolve('');
+ return;
+ }
+
+ context.drawImage(image, 0, 0);
+ try {
+ resolve(canvas.toDataURL('image/png'));
+ } catch (e) {
+ resolve('');
+ }
+ };
+
+ const onError = (): void => resolve('');
+
+ image.onload = onLoad;
+ image.onerror = onError;
+ image.crossOrigin = 'anonymous';
+ image.src = avatarUrl;
+ });
+};
diff --git a/packages/ui-voip/src/v2/views/IncomingCall.stories.tsx b/packages/ui-voip/src/v2/views/IncomingCall.stories.tsx
index 698e5936334dc..ffc34e625c3c6 100644
--- a/packages/ui-voip/src/v2/views/IncomingCall.stories.tsx
+++ b/packages/ui-voip/src/v2/views/IncomingCall.stories.tsx
@@ -2,7 +2,7 @@ import { mockAppRoot } from '@rocket.chat/mock-providers';
import type { Meta, StoryFn } from '@storybook/react';
import IncomingCall from './IncomingCall';
-import MediaCallProviderMock from '../MockedMediaCallProvider';
+import MockedMediaCallProvider from '../MockedMediaCallProvider';
const mockedContexts = mockAppRoot()
.withTranslations('en', 'core', {
@@ -18,9 +18,9 @@ export default {
decorators: [
mockedContexts,
(Story) => (
-
+
-
+
),
],
} satisfies Meta;
diff --git a/packages/ui-voip/src/v2/views/IncomingCallTransfer.stories.tsx b/packages/ui-voip/src/v2/views/IncomingCallTransfer.stories.tsx
index b639b1e708b40..a0a48f3de288c 100644
--- a/packages/ui-voip/src/v2/views/IncomingCallTransfer.stories.tsx
+++ b/packages/ui-voip/src/v2/views/IncomingCallTransfer.stories.tsx
@@ -2,7 +2,7 @@ import { mockAppRoot } from '@rocket.chat/mock-providers';
import type { Meta, StoryFn } from '@storybook/react';
import IncomingCallTransfer from './IncomingCallTransfer';
-import MediaCallProviderMock from '../MockedMediaCallProvider';
+import MockedMediaCallProvider from '../MockedMediaCallProvider';
const mockedContexts = mockAppRoot()
.withTranslations('en', 'core', {
@@ -19,9 +19,9 @@ export default {
decorators: [
mockedContexts,
(Story) => (
-
+
-
+
),
],
} satisfies Meta;
diff --git a/packages/ui-voip/src/v2/views/NewCall.stories.tsx b/packages/ui-voip/src/v2/views/NewCall.stories.tsx
index 736e729780a6f..953485f7c4921 100644
--- a/packages/ui-voip/src/v2/views/NewCall.stories.tsx
+++ b/packages/ui-voip/src/v2/views/NewCall.stories.tsx
@@ -2,7 +2,7 @@ import { mockAppRoot } from '@rocket.chat/mock-providers';
import type { Meta, StoryFn } from '@storybook/react';
import NewCall from './NewCall';
-import MediaCallProviderMock from '../MockedMediaCallProvider';
+import MockedMediaCallProvider from '../MockedMediaCallProvider';
const mockedContexts = mockAppRoot()
.withTranslations('en', 'core', {
@@ -18,9 +18,9 @@ export default {
decorators: [
mockedContexts,
(Story) => (
-
+
-
+
),
],
} satisfies Meta;
diff --git a/packages/ui-voip/src/v2/views/OngoingCall.stories.tsx b/packages/ui-voip/src/v2/views/OngoingCall.stories.tsx
index 68944f2128420..a8158cfb388a4 100644
--- a/packages/ui-voip/src/v2/views/OngoingCall.stories.tsx
+++ b/packages/ui-voip/src/v2/views/OngoingCall.stories.tsx
@@ -2,7 +2,7 @@ import { mockAppRoot } from '@rocket.chat/mock-providers';
import type { Meta, StoryFn } from '@storybook/react';
import OngoingCall from './OngoingCall';
-import MediaCallProviderMock from '../MockedMediaCallProvider';
+import MockedMediaCallProvider from '../MockedMediaCallProvider';
const mockedContexts = mockAppRoot().buildStoryDecorator();
@@ -12,9 +12,9 @@ export default {
decorators: [
mockedContexts,
(Story) => (
-
+
-
+
),
],
} satisfies Meta;
@@ -25,40 +25,40 @@ export const OngoingCallStory: StoryFn = () => {
export const OngoingCallWithSlots: StoryFn = () => {
return (
-
+
-
+
);
};
export const OngoingCallWithRemoteStatus: StoryFn = () => {
return (
-
+
-
+
);
};
export const OngoingCallWithRemoteStatusMuted: StoryFn = () => {
return (
-
+
-
+
);
};
export const OngoingCallWithRemoteStatusHeld: StoryFn = () => {
return (
-
+
-
+
);
};
export const OngoingCallWithSlotsAndRemoteStatus: StoryFn = () => {
return (
-
+
-
+
);
};
diff --git a/packages/ui-voip/src/v2/views/OutgoingCall.stories.tsx b/packages/ui-voip/src/v2/views/OutgoingCall.stories.tsx
index 74e7f0ecfaf51..fb02940f146c0 100644
--- a/packages/ui-voip/src/v2/views/OutgoingCall.stories.tsx
+++ b/packages/ui-voip/src/v2/views/OutgoingCall.stories.tsx
@@ -2,7 +2,7 @@ import { mockAppRoot } from '@rocket.chat/mock-providers';
import type { Meta, StoryFn } from '@storybook/react';
import OutgoingCall from './OutgoingCall';
-import MediaCallProviderMock from '../MockedMediaCallProvider';
+import MockedMediaCallProvider from '../MockedMediaCallProvider';
const mockedContexts = mockAppRoot()
.withTranslations('en', 'core', {
@@ -17,9 +17,9 @@ export default {
decorators: [
mockedContexts,
(Story) => (
-
+
-
+
),
],
} satisfies Meta;
diff --git a/packages/ui-voip/src/v2/views/OutgoingCallTransfer.stories.tsx b/packages/ui-voip/src/v2/views/OutgoingCallTransfer.stories.tsx
index c2a01b54cfd2e..f64fe038b7305 100644
--- a/packages/ui-voip/src/v2/views/OutgoingCallTransfer.stories.tsx
+++ b/packages/ui-voip/src/v2/views/OutgoingCallTransfer.stories.tsx
@@ -2,7 +2,7 @@ import { mockAppRoot } from '@rocket.chat/mock-providers';
import type { Meta, StoryFn } from '@storybook/react';
import OutgoingCallTransfer from './OutgoingCallTransfer';
-import MediaCallProviderMock from '../MockedMediaCallProvider';
+import MockedMediaCallProvider from '../MockedMediaCallProvider';
const mockedContexts = mockAppRoot()
.withTranslations('en', 'core', {
@@ -18,9 +18,9 @@ export default {
decorators: [
mockedContexts,
(Story) => (
-
+
-
+
),
],
} satisfies Meta;
diff --git a/yarn.lock b/yarn.lock
index a5a6d0b6dc198..ef60b63ad81ee 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8474,7 +8474,7 @@ __metadata:
languageName: unknown
linkType: soft
-"@rocket.chat/desktop-api@workspace:packages/desktop-api, @rocket.chat/desktop-api@workspace:~":
+"@rocket.chat/desktop-api@workspace:^, @rocket.chat/desktop-api@workspace:packages/desktop-api, @rocket.chat/desktop-api@workspace:~":
version: 0.0.0-use.local
resolution: "@rocket.chat/desktop-api@workspace:packages/desktop-api"
dependencies:
@@ -10601,6 +10601,7 @@ __metadata:
"@playwright/test": "npm:^1.52.0"
"@react-spectrum/test-utils": "npm:~1.0.0-alpha.8"
"@rocket.chat/css-in-js": "npm:~0.31.25"
+ "@rocket.chat/desktop-api": "workspace:^"
"@rocket.chat/emitter": "npm:~0.31.25"
"@rocket.chat/eslint-config": "workspace:^"
"@rocket.chat/fuselage": "npm:^0.68.1"