diff --git a/apps/meteor/client/definitions/IRocketChatDesktop.ts b/apps/meteor/client/definitions/IRocketChatDesktop.ts index a0707e9ba6d..b91a23ac112 100644 --- a/apps/meteor/client/definitions/IRocketChatDesktop.ts +++ b/apps/meteor/client/definitions/IRocketChatDesktop.ts @@ -1,10 +1,49 @@ +type ServerInfo = { + version: string; +}; + +type Badge = '•' | number; + +type ThemeAppearance = 'dark' | 'light' | 'auto' | 'high-contrast' | undefined; + +type VideoCallWindowOptions = { + providerName?: string | undefined; +}; + type OutlookEventsResponse = { status: 'success' | 'canceled' }; export interface IRocketChatDesktop { - openInternalVideoChatWindow?: (url: string, options: { providerName: string | undefined }) => void; - getOutlookEvents?: (date: Date) => Promise; - setOutlookExchangeUrl?: (url: string, userId: string) => Promise; - hasOutlookCredentials?: () => Promise; - clearOutlookCredentials?: () => Promise | void; - openDocumentViewer?: (url: string, format: string, options: any) => void; + onReady: (cb: (serverInfo: ServerInfo) => void) => void; + setServerInfo: (serverInfo: ServerInfo) => void; + setUrlResolver: (getAbsoluteUrl: (relativePath?: string) => string) => void; + setBadge: (badge: Badge) => void; + setFavicon: (faviconUrl: string) => void; + setBackground: (imageUrl: string) => void; + setSidebarCustomTheme: (customTheme: string) => void; + setTitle: (title: string) => void; + setUserLoggedIn: (userLoggedIn: boolean) => void; + setUserPresenceDetection: (options: { + isAutoAwayEnabled: boolean; + idleThreshold: number | null; + setUserOnline: (online: boolean) => void; + }) => void; + setUserThemeAppearance: (themeAppearance: ThemeAppearance) => void; + createNotification: ( + options: NotificationOptions & { + canReply?: boolean; + title: string; + onEvent: (eventDescriptor: { type: string; detail: unknown }) => void; + }, + ) => Promise; + destroyNotification: (id: unknown) => void; + getInternalVideoChatWindowEnabled: () => boolean; + openInternalVideoChatWindow: (url: string, options: VideoCallWindowOptions) => void; + setGitCommitHash: (gitCommitHash: string) => void; + writeTextToClipboard: (text: string) => void; + getOutlookEvents: (date: Date) => Promise; + setOutlookExchangeUrl: (url: string, userId: string) => void; + hasOutlookCredentials: () => Promise; + clearOutlookCredentials: () => void; + setUserToken: (token: string, userId: string) => void; + openDocumentViewer: (url: string, format: string, options: any) => void; } diff --git a/apps/meteor/client/lib/userPresence.ts b/apps/meteor/client/lib/userPresence.ts index 31ed7570f32..835c3230781 100644 --- a/apps/meteor/client/lib/userPresence.ts +++ b/apps/meteor/client/lib/userPresence.ts @@ -25,7 +25,7 @@ export class UserPresence { private storeUser: (doc: IUser) => void = () => undefined; - private startTimer() { + startTimer() { this.stopTimer(); if (!this.awayTime) return; @@ -70,15 +70,42 @@ export class UserPresence { const isLoggingIn = useIsLoggingIn(); const enableAutoAway = useUserPreference('enableAutoAway'); const idleTimeLimit = useUserPreference('idleTimeLimit') ?? 300; + const { RocketChatDesktop } = window; this.user = user; this.connected = connected; - this.awayTime = enableAutoAway ? idleTimeLimit * 1000 : undefined; + this.awayTime = enableAutoAway && !RocketChatDesktop ? idleTimeLimit * 1000 : undefined; this.goOnline = useMethod('UserPresence:online'); this.goAway = useMethod('UserPresence:away'); this.storeUser = Users.use((state) => state.store); useEffect(() => { + if (!RocketChatDesktop) return; + + RocketChatDesktop.setUserPresenceDetection({ + isAutoAwayEnabled: enableAutoAway ?? false, + idleThreshold: idleTimeLimit, + setUserOnline: (online) => { + if (!online) { + this.goAway(); + return; + } + this.goOnline(); + }, + }); + + return () => { + RocketChatDesktop.setUserPresenceDetection({ + isAutoAwayEnabled: false, + idleThreshold: null, + setUserOnline: () => undefined, + }); + }; + }, [RocketChatDesktop, enableAutoAway, idleTimeLimit]); + + useEffect(() => { + if (RocketChatDesktop) return; + const documentEvents = ['mousemove', 'mousedown', 'touchend', 'keydown'] as const; documentEvents.forEach((key) => document.addEventListener(key, this.setOnline)); window.addEventListener('focus', this.setOnline); @@ -87,7 +114,7 @@ export class UserPresence { documentEvents.forEach((key) => document.removeEventListener(key, this.setOnline)); window.removeEventListener('focus', this.setOnline); }; - }, []); + }, [RocketChatDesktop]); useEffect(() => { if (!user || !connected || isLoggingIn) return; diff --git a/apps/meteor/client/main.ts b/apps/meteor/client/main.ts index 087ae21dedf..2d1b6f396dd 100644 --- a/apps/meteor/client/main.ts +++ b/apps/meteor/client/main.ts @@ -1,5 +1,6 @@ import './serviceWorker'; import './startup/accounts'; +import './startup/fakeUserPresence'; import('@rocket.chat/fuselage-polyfills') .then(() => import('./meteorOverrides')) diff --git a/apps/meteor/client/providers/UserPresenceProvider.tsx b/apps/meteor/client/providers/UserPresenceProvider.tsx index 8cf1322c9ae..8c2ca06c991 100644 --- a/apps/meteor/client/providers/UserPresenceProvider.tsx +++ b/apps/meteor/client/providers/UserPresenceProvider.tsx @@ -6,7 +6,7 @@ import { useMemo, useEffect } from 'react'; import { Presence } from '../lib/presence'; import { UserPresence } from '../lib/userPresence'; -const userPresence = new UserPresence(); +export const userPresence = new UserPresence(); type UserPresenceProviderProps = { children?: ReactNode; diff --git a/apps/meteor/client/startup/fakeUserPresence.ts b/apps/meteor/client/startup/fakeUserPresence.ts new file mode 100644 index 00000000000..3bda53c5055 --- /dev/null +++ b/apps/meteor/client/startup/fakeUserPresence.ts @@ -0,0 +1,18 @@ +// backport of rocketchat:user-presence for the desktop app + +if (window.RocketChatDesktop) { + const fakeUserPresenceModule = { + UserPresence: { + awayTime: undefined, + start: () => undefined, + }, + }; + + window.require = ((fn) => + Object.assign((id: string) => { + if (id === 'meteor/rocketchat:user-presence') { + return fakeUserPresenceModule; + } + return fn(id); + }, fn))(window.require); +} diff --git a/apps/meteor/client/views/room/contextualBar/VideoConference/hooks/useVideoConfOpenCall.spec.tsx b/apps/meteor/client/views/room/contextualBar/VideoConference/hooks/useVideoConfOpenCall.spec.tsx index bf51e9bab4c..74f3a6f3f56 100644 --- a/apps/meteor/client/views/room/contextualBar/VideoConference/hooks/useVideoConfOpenCall.spec.tsx +++ b/apps/meteor/client/views/room/contextualBar/VideoConference/hooks/useVideoConfOpenCall.spec.tsx @@ -8,7 +8,7 @@ describe('with window.RocketChatDesktop set', () => { beforeEach(() => { window.RocketChatDesktop = { openInternalVideoChatWindow: jest.fn(), - }; + } as any; }); afterAll(() => {