diff --git a/apps/meteor/app/notifications/client/lib/Presence.ts b/apps/meteor/app/notifications/client/lib/Presence.ts index 69c255aaf1f96..3cfc98c440c2f 100644 --- a/apps/meteor/app/notifications/client/lib/Presence.ts +++ b/apps/meteor/app/notifications/client/lib/Presence.ts @@ -1,17 +1,13 @@ -import type { StreamerEvents } from '@rocket.chat/ui-contexts'; +import type { UserStatus } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; -import { Presence, STATUS_MAP } from '../../../../client/lib/presence'; +import { Presence } from '../../../../client/lib/presence'; // TODO implement API on Streamer to be able to listen to all streamed data // this is a hacky way to listen to all streamed data from user-presence Streamer new Meteor.Streamer('user-presence'); -(Meteor as any).StreamerCentral.on('stream-user-presence', (uid: string, ...args: StreamerEvents['user-presence'][number]['args']) => { - if (!Array.isArray(args)) { - throw new Error('Presence event must be an array'); - } - const [[username, status, statusText]] = args; - Presence.notify({ _id: uid, username, status: STATUS_MAP[status ?? 0], statusText }); +Meteor.StreamerCentral.on('stream-user-presence', (uid: string, username: string, statusChanged?: UserStatus, statusText?: string) => { + Presence.notify({ _id: uid, username, status: statusChanged, statusText }); }); diff --git a/apps/meteor/app/user-status/client/index.ts b/apps/meteor/app/user-status/client/index.ts deleted file mode 100644 index 62fb2a7e9fd03..0000000000000 --- a/apps/meteor/app/user-status/client/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import './lib/customUserStatus'; - -export { userStatus } from './lib/userStatus'; diff --git a/apps/meteor/app/user-status/client/lib/customUserStatus.js b/apps/meteor/app/user-status/client/lib/customUserStatus.js deleted file mode 100644 index 8d429d22c3ec2..0000000000000 --- a/apps/meteor/app/user-status/client/lib/customUserStatus.js +++ /dev/null @@ -1,62 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; - -import { sdk } from '../../../utils/client/lib/SDKClient'; -import { userStatus } from './userStatus'; - -userStatus.packages.customUserStatus = { - list: [], -}; - -export const deleteCustomUserStatus = function (customUserStatusData) { - delete userStatus.list[customUserStatusData._id]; - - const arrayIndex = userStatus.packages.customUserStatus.list.indexOf(customUserStatusData._id); - if (arrayIndex !== -1) { - userStatus.packages.customUserStatusData.list.splice(arrayIndex, 1); - } -}; - -export const updateCustomUserStatus = function (customUserStatusData) { - const newUserStatus = { - name: customUserStatusData.name, - id: customUserStatusData._id, - statusType: customUserStatusData.statusType, - localizeName: false, - }; - - const arrayIndex = userStatus.packages.customUserStatus.list.indexOf(newUserStatus.id); - if (arrayIndex === -1) { - userStatus.packages.customUserStatus.list.push(newUserStatus); - } else { - userStatus.packages.customUserStatus.list[arrayIndex] = newUserStatus; - } - - userStatus.list[newUserStatus.id] = newUserStatus; -}; - -Meteor.startup(() => { - Tracker.autorun(() => { - if (!Meteor.userId()) { - return; - } - - void sdk.call('listCustomUserStatus').then((result) => { - if (!result) { - return; - } - - for (const customStatus of result) { - const newUserStatus = { - name: customStatus.name, - id: customStatus._id, - statusType: customStatus.statusType, - localizeName: false, - }; - - userStatus.packages.customUserStatus.list.push(newUserStatus); - userStatus.list[newUserStatus.id] = newUserStatus; - } - }); - }); -}); diff --git a/apps/meteor/app/user-status/client/lib/userStatus.ts b/apps/meteor/app/user-status/client/lib/userStatus.ts deleted file mode 100644 index eec57acd29f1d..0000000000000 --- a/apps/meteor/app/user-status/client/lib/userStatus.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { UserStatus } from '@rocket.chat/core-typings'; - -type Status = { - name: string; - localizeName: boolean; - id: string; - statusType: UserStatus; -}; - -type UserStatusTypes = { - packages: any; - list: { - [status: string]: Status; - }; -}; - -export const userStatus: UserStatusTypes = { - packages: { - base: { - render(html: string): string { - return html; - }, - }, - }, - - list: { - online: { - name: UserStatus.ONLINE, - localizeName: true, - id: UserStatus.ONLINE, - statusType: UserStatus.ONLINE, - }, - away: { - name: UserStatus.AWAY, - localizeName: true, - id: UserStatus.AWAY, - statusType: UserStatus.AWAY, - }, - busy: { - name: UserStatus.BUSY, - localizeName: true, - id: UserStatus.BUSY, - statusType: UserStatus.BUSY, - }, - offline: { - name: UserStatus.OFFLINE, - localizeName: true, - id: UserStatus.OFFLINE, - statusType: UserStatus.OFFLINE, - }, - }, -} as const; diff --git a/apps/meteor/app/user-status/server/methods/listCustomUserStatus.ts b/apps/meteor/app/user-status/server/methods/listCustomUserStatus.ts index bb11a1be73bdf..3a962121d65c7 100644 --- a/apps/meteor/app/user-status/server/methods/listCustomUserStatus.ts +++ b/apps/meteor/app/user-status/server/methods/listCustomUserStatus.ts @@ -6,7 +6,7 @@ import { Meteor } from 'meteor/meteor'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { - listCustomUserStatus(): Promise; + listCustomUserStatus(): ICustomUserStatus[]; } } diff --git a/apps/meteor/client/importPackages.ts b/apps/meteor/client/importPackages.ts index cbbcc03918841..ba1bf01862352 100644 --- a/apps/meteor/client/importPackages.ts +++ b/apps/meteor/client/importPackages.ts @@ -35,7 +35,6 @@ import '../app/wordpress/client'; import '../app/e2e/client'; import '../app/discussion/client'; import '../app/threads/client'; -import '../app/user-status/client'; import '../app/utils/client'; import '../app/settings/client'; import '../app/models/client'; diff --git a/apps/meteor/client/lib/presence.ts b/apps/meteor/client/lib/presence.ts index 8cb367bf1e526..dbaddcbe405b5 100644 --- a/apps/meteor/client/lib/presence.ts +++ b/apps/meteor/client/lib/presence.ts @@ -6,8 +6,6 @@ import { Meteor } from 'meteor/meteor'; import { sdk } from '../../app/utils/client/lib/SDKClient'; -export const STATUS_MAP = [UserStatus.OFFLINE, UserStatus.ONLINE, UserStatus.AWAY, UserStatus.BUSY, UserStatus.DISABLED]; - type InternalEvents = { remove: IUser['_id']; reset: undefined; diff --git a/apps/meteor/client/lib/userStatuses.ts b/apps/meteor/client/lib/userStatuses.ts new file mode 100644 index 0000000000000..631c1d1ea0442 --- /dev/null +++ b/apps/meteor/client/lib/userStatuses.ts @@ -0,0 +1,90 @@ +import { UserStatus } from '@rocket.chat/core-typings'; +import type { ICustomUserStatus } from '@rocket.chat/core-typings'; + +import { sdk } from '../../app/utils/client/lib/SDKClient'; + +export type UserStatusDescriptor = { + id: string; + name: string; + statusType: UserStatus; + localizeName: boolean; +}; + +export class UserStatuses implements Iterable { + public invisibleAllowed = true; + + private store: Map = new Map( + [UserStatus.ONLINE, UserStatus.AWAY, UserStatus.BUSY, UserStatus.OFFLINE].map((status) => [ + status, + { + id: status, + name: status, + statusType: status, + localizeName: true, + }, + ]), + ); + + public delete(id: string): void { + this.store.delete(id); + } + + public put(customUserStatus: UserStatusDescriptor): void { + this.store.set(customUserStatus.id, customUserStatus); + } + + public createFromCustom(customUserStatus: ICustomUserStatus): UserStatusDescriptor { + if (!this.isValidType(customUserStatus.statusType)) { + throw new Error('Invalid user status type'); + } + + return { + name: customUserStatus.name, + id: customUserStatus._id, + statusType: customUserStatus.statusType as UserStatus, + localizeName: false, + }; + } + + public isValidType(type: string): type is UserStatus { + return (Object.values(UserStatus) as string[]).includes(type); + } + + public *[Symbol.iterator]() { + for (const value of this.store.values()) { + if (this.invisibleAllowed || value.statusType !== UserStatus.OFFLINE) { + yield value; + } + } + } + + public async sync() { + const result = await sdk.call('listCustomUserStatus'); + if (!result) { + return; + } + + for (const customStatus of result) { + this.put(this.createFromCustom(customStatus)); + } + } + + public watch(cb?: () => void) { + const updateSubscription = sdk.stream('notify-logged', ['updateCustomUserStatus'], (data) => { + this.put(this.createFromCustom(data.userStatusData)); + cb?.(); + }); + + const deleteSubscription = sdk.stream('notify-logged', ['deleteCustomUserStatus'], (data) => { + this.delete(data.userStatusData._id); + cb?.(); + }); + + return () => { + updateSubscription.stop(); + deleteSubscription.stop(); + }; + } +} + +export const userStatuses = new UserStatuses(); diff --git a/apps/meteor/client/providers/UserProvider/UserProvider.tsx b/apps/meteor/client/providers/UserProvider/UserProvider.tsx index 8552de8b61300..be415954cb068 100644 --- a/apps/meteor/client/providers/UserProvider/UserProvider.tsx +++ b/apps/meteor/client/providers/UserProvider/UserProvider.tsx @@ -17,7 +17,6 @@ import { useDeleteUser } from './hooks/useDeleteUser'; import { useEmailVerificationWarning } from './hooks/useEmailVerificationWarning'; import { useLDAPAndCrowdCollisionWarning } from './hooks/useLDAPAndCrowdCollisionWarning'; import { useUpdateAvatar } from './hooks/useUpdateAvatar'; -import { useUpdateCustomUserStatus } from './hooks/useUpdateCustomUserStatus'; const getUserId = (): string | null => Meteor.userId(); @@ -81,7 +80,6 @@ const UserProvider = ({ children }: UserProviderProps): ReactElement => { useLDAPAndCrowdCollisionWarning(); useEmailVerificationWarning(user ?? undefined); - useUpdateCustomUserStatus(); useDeleteUser(); useUpdateAvatar(); diff --git a/apps/meteor/client/providers/UserProvider/hooks/useUpdateCustomUserStatus.ts b/apps/meteor/client/providers/UserProvider/hooks/useUpdateCustomUserStatus.ts deleted file mode 100644 index 0ba973134cb27..0000000000000 --- a/apps/meteor/client/providers/UserProvider/hooks/useUpdateCustomUserStatus.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { useStream } from '@rocket.chat/ui-contexts'; -import { useEffect } from 'react'; - -import { updateCustomUserStatus, deleteCustomUserStatus } from '../../../../app/user-status/client/lib/customUserStatus'; - -export const useUpdateCustomUserStatus = () => { - const notify = useStream('notify-logged'); - useEffect(() => { - const unsubUpdate = notify('updateCustomUserStatus', (data) => updateCustomUserStatus(data.userStatusData)); - const unsubDelete = notify('deleteCustomUserStatus', (data) => deleteCustomUserStatus(data.userStatusData)); - - return () => { - unsubUpdate(); - unsubDelete(); - }; - }, [notify]); -}; diff --git a/apps/meteor/client/sidebar/header/hooks/useStatusItems.tsx b/apps/meteor/client/sidebar/header/hooks/useStatusItems.tsx index 217c5c53f5732..026f7c80400ed 100644 --- a/apps/meteor/client/sidebar/header/hooks/useStatusItems.tsx +++ b/apps/meteor/client/sidebar/header/hooks/useStatusItems.tsx @@ -1,77 +1,87 @@ -import type { IUser, ValueOf } from '@rocket.chat/core-typings'; -import { UserStatus as UserStatusEnum } from '@rocket.chat/core-typings'; import { Box } from '@rocket.chat/fuselage'; -import type { TranslationKey } from '@rocket.chat/ui-contexts'; -import { useEndpoint, useSetting, useTranslation } from '@rocket.chat/ui-contexts'; -import React from 'react'; +import { useEndpoint, useSetting } from '@rocket.chat/ui-contexts'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import React, { useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; -import { userStatus } from '../../../../app/user-status/client'; import { callbacks } from '../../../../lib/callbacks'; import type { GenericMenuItemProps } from '../../../components/GenericMenu/GenericMenuItem'; import MarkdownText from '../../../components/MarkdownText'; import { UserStatus } from '../../../components/UserStatus'; +import { userStatuses } from '../../../lib/userStatuses'; +import type { UserStatusDescriptor } from '../../../lib/userStatuses'; import { useStatusDisabledModal } from '../../../views/admin/customUserStatus/hooks/useStatusDisabledModal'; import { useCustomStatusModalHandler } from './useCustomStatusModalHandler'; -const isDefaultStatus = (id: string): boolean => (Object.values(UserStatusEnum) as string[]).includes(id); -const isDefaultStatusName = (_name: string, id: string): _name is UserStatusEnum => isDefaultStatus(id); -const translateStatusName = (t: ReturnType, status: (typeof userStatus.list)['']): string => { - if (isDefaultStatusName(status.name, status.id)) { - return t(status.name as TranslationKey); - } +export const useStatusItems = (): GenericMenuItemProps[] => { + // We should lift this up to somewhere else if we want to use it in other places - return status.name; -}; + userStatuses.invisibleAllowed = useSetting('Accounts_AllowInvisibleStatusOption', true); -export const useStatusItems = (user: IUser): GenericMenuItemProps[] => { - const t = useTranslation(); - const presenceDisabled = useSetting('Presence_broadcast_disabled'); - const setStatus = useEndpoint('POST', '/v1/users.setStatus'); + const queryClient = useQueryClient(); - const setStatusAction = (status: (typeof userStatus.list)['']): void => { - setStatus({ status: status.statusType, message: !isDefaultStatus(status.id) ? status.name : '' }); - void callbacks.run('userStatusManuallySet', status); - }; + useEffect( + () => + userStatuses.watch(() => { + queryClient.setQueryData(['user-statuses'], Array.from(userStatuses)); + }), + [queryClient], + ); - const filterInvisibleStatus = !useSetting('Accounts_AllowInvisibleStatusOption') - ? (status: ValueOf<(typeof userStatus)['list']>): boolean => status.name !== 'invisible' - : (): boolean => true; + const { t } = useTranslation(); - const handleCustomStatus = useCustomStatusModalHandler(); + const setStatus = useEndpoint('POST', '/v1/users.setStatus'); + const setStatusMutation = useMutation({ + mutationFn: async (status: UserStatusDescriptor) => { + void setStatus({ status: status.statusType, message: userStatuses.isValidType(status.id) ? '' : status.name }); + void callbacks.run('userStatusManuallySet', status); + }, + }); - const handleStatusDisabledModal = useStatusDisabledModal(); + const presenceDisabled = useSetting('Presence_broadcast_disabled', false); - const presenceDisabledItem = { - id: 'presence-disabled', - content: ( - - - {t('User_status_disabled')} - - - {t('Learn_more')} - - - ), - }; + const { data: statuses } = useQuery({ + queryKey: ['user-statuses'], + queryFn: async () => { + await userStatuses.sync(); + return Array.from(userStatuses); + }, + staleTime: Infinity, + select: (statuses) => + statuses.map((status): GenericMenuItemProps => { + const content = status.localizeName ? t(status.name) : status.name; + return { + id: status.id, + status: , + content: , + disabled: presenceDisabled, + onClick: () => setStatusMutation.mutate(status), + }; + }), + }); - const statusItems = Object.values(userStatus.list) - .filter(filterInvisibleStatus) - .map((status) => { - const name = status.localizeName ? translateStatusName(t, status) : status.name; - const modifier = status.statusType || user?.status; - return { - id: status.id, - status: , - content: , - onClick: () => setStatusAction(status), - disabled: presenceDisabled, - }; - }); + const handleStatusDisabledModal = useStatusDisabledModal(); + const handleCustomStatus = useCustomStatusModalHandler(); return [ - ...(presenceDisabled ? [presenceDisabledItem] : []), - ...statusItems, + ...(presenceDisabled + ? [ + { + id: 'presence-disabled', + content: ( + + + {t('User_status_disabled')} + + + {t('Learn_more')} + + + ), + }, + ] + : []), + ...(statuses ?? []), { id: 'custom-status', icon: 'emoji', content: t('Custom_Status'), onClick: handleCustomStatus, disabled: presenceDisabled }, ]; }; diff --git a/apps/meteor/client/sidebar/header/hooks/useUserMenu.tsx b/apps/meteor/client/sidebar/header/hooks/useUserMenu.tsx index 111065b0ac46e..de43d30306e96 100644 --- a/apps/meteor/client/sidebar/header/hooks/useUserMenu.tsx +++ b/apps/meteor/client/sidebar/header/hooks/useUserMenu.tsx @@ -11,7 +11,7 @@ import { useStatusItems } from './useStatusItems'; export const useUserMenu = (user: IUser) => { const t = useTranslation(); - const statusItems = useStatusItems(user); + const statusItems = useStatusItems(); const accountItems = useAccountItems(); const logout = useLogout(); diff --git a/apps/meteor/definition/externals/meteor/meteor.d.ts b/apps/meteor/definition/externals/meteor/meteor.d.ts index 7d8722896f8e4..774c65f39f657 100644 --- a/apps/meteor/definition/externals/meteor/meteor.d.ts +++ b/apps/meteor/definition/externals/meteor/meteor.d.ts @@ -22,6 +22,8 @@ declare module 'meteor/meteor' { const instances: { [name: string]: IStreamer; }; + + function on(name: string, callback: (...args: any[]) => void): void; } interface ErrorStatic { diff --git a/apps/meteor/server/modules/listeners/listeners.module.ts b/apps/meteor/server/modules/listeners/listeners.module.ts index ae78f78b0e397..e3691530843e0 100644 --- a/apps/meteor/server/modules/listeners/listeners.module.ts +++ b/apps/meteor/server/modules/listeners/listeners.module.ts @@ -2,7 +2,7 @@ import type { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; import type { ISetting as AppsSetting } from '@rocket.chat/apps-engine/definition/settings'; import type { IServiceClass } from '@rocket.chat/core-services'; import { EnterpriseSettings, listenToMessageSentEvent } from '@rocket.chat/core-services'; -import { UserStatus, isSettingColor, isSettingEnterprise } from '@rocket.chat/core-typings'; +import { isSettingColor, isSettingEnterprise } from '@rocket.chat/core-typings'; import type { IUser, IRoom, VideoConference, ISetting, IOmnichannelRoom } from '@rocket.chat/core-typings'; import { Logger } from '@rocket.chat/logger'; import { parse } from '@rocket.chat/message-parser'; @@ -12,13 +12,6 @@ import type { NotificationsModule } from '../notifications/notifications.module' const isMessageParserDisabled = process.env.DISABLE_MESSAGE_PARSER === 'true'; -const STATUS_MAP = { - [UserStatus.OFFLINE]: 0, - [UserStatus.ONLINE]: 1, - [UserStatus.AWAY]: 2, - [UserStatus.BUSY]: 3, -} as const; - const minimongoChangeMap: Record = { inserted: 'added', updated: 'changed', @@ -152,12 +145,10 @@ export class ListenersModule { return; } - const statusChanged = (STATUS_MAP as any)[status] as 0 | 1 | 2 | 3; - - notifications.notifyLoggedInThisInstance('user-status', [_id, username, statusChanged, statusText, name, roles]); + notifications.notifyLoggedInThisInstance('user-status', [_id, username, status, statusText, name, roles]); if (_id) { - notifications.sendPresence(_id, username, statusChanged, statusText); + notifications.sendPresence(_id, username, status, statusText); } }); diff --git a/apps/meteor/server/modules/notifications/notifications.module.ts b/apps/meteor/server/modules/notifications/notifications.module.ts index 979603c926506..73cede2cd36cb 100644 --- a/apps/meteor/server/modules/notifications/notifications.module.ts +++ b/apps/meteor/server/modules/notifications/notifications.module.ts @@ -1,5 +1,5 @@ import { Authorization, VideoConf } from '@rocket.chat/core-services'; -import type { ISubscription, IOmnichannelRoom, IUser } from '@rocket.chat/core-typings'; +import type { ISubscription, IOmnichannelRoom, IUser, UserStatus } from '@rocket.chat/core-typings'; import { Rooms, Subscriptions, Users, Settings } from '@rocket.chat/models'; import type { StreamerCallbackArgs, StreamKeys, StreamNames } from '@rocket.chat/ui-contexts'; import type { IStreamer, IStreamerConstructor, IPublication } from 'meteor/rocketchat:streamer'; @@ -531,7 +531,7 @@ export class NotificationsModule { return this.streamUser.emitWithoutBroadcast(`${userId}/${eventName}`, ...args); } - sendPresence(uid: string, ...args: [username: string, statusChanged: 0 | 1 | 2 | 3, statusText: string | undefined]): void { + sendPresence(uid: string, ...args: [username: string, status?: UserStatus, statusText?: string]): void { emit(uid, [args]); return this.streamPresence.emitWithoutBroadcast(uid, args); } diff --git a/ee/packages/ddp-client/src/types/streams.ts b/ee/packages/ddp-client/src/types/streams.ts index 38b5992546f01..d1202a5b97258 100644 --- a/ee/packages/ddp-client/src/types/streams.ts +++ b/ee/packages/ddp-client/src/types/streams.ts @@ -18,12 +18,13 @@ import type { IIntegrationHistory, IUserDataEvent, ICalendarNotification, - IUserStatus, ILivechatInquiryRecord, ILivechatAgent, IImportProgress, IBanner, LicenseLimitKind, + ICustomUserStatus, + UserStatus, } from '@rocket.chat/core-typings'; import type * as UiKit from '@rocket.chat/ui-kit'; @@ -198,7 +199,7 @@ export interface StreamerEvents { key: 'updateCustomUserStatus'; args: [ { - userStatusData: IUserStatus; + userStatusData: Omit; }, ]; }, @@ -234,8 +235,20 @@ export interface StreamerEvents { }, { key: 'Users:NameChanged'; args: [Pick] }, { key: 'private-settings-changed'; args: ['inserted' | 'updated' | 'removed' | 'changed', ISetting] }, - { key: 'deleteCustomUserStatus'; args: [{ userStatusData: unknown }] }, - { key: 'user-status'; args: [[IUser['_id'], IUser['username'], 0 | 1 | 2 | 3, IUser['statusText'], IUser['name'], IUser['roles']]] }, + { key: 'deleteCustomUserStatus'; args: [{ userStatusData: Omit }] }, + { + key: 'user-status'; + args: [ + [ + uid: IUser['_id'], + username: IUser['username'], + status: UserStatus, + statusText: IUser['statusText'], + name: IUser['name'], + roles: IUser['roles'], + ], + ]; + }, { key: 'Users:Deleted'; args: [ @@ -311,7 +324,7 @@ export interface StreamerEvents { }, ]; - 'user-presence': [{ key: string; args: [[username: string, statusChanged?: 0 | 1 | 2 | 3, statusText?: string]] }]; + 'user-presence': [{ key: string; args: [[username: string, statusChanged?: UserStatus, statusText?: string]] }]; // TODO: rename to 'integration-history' 'integrationHistory': [ diff --git a/packages/core-services/src/events/Events.ts b/packages/core-services/src/events/Events.ts index 6315bf43fa13f..dfe9b31ff8cea 100644 --- a/packages/core-services/src/events/Events.ts +++ b/packages/core-services/src/events/Events.ts @@ -18,7 +18,6 @@ import type { ISocketConnection, ISubscription, IUser, - IUserStatus, IInvite, IWebdavAccount, ICustomSound, @@ -33,6 +32,7 @@ import type { IBanner, ILivechatVisitor, LicenseLimitKind, + ICustomUserStatus, } from '@rocket.chat/core-typings'; import type * as UiKit from '@rocket.chat/ui-kit'; @@ -128,7 +128,7 @@ export type EventSignatures = { replaceByUser: { _id: IUser['_id']; username: IUser['username']; alias: string }; }, ): void; - 'user.deleteCustomStatus'(userStatus: IUserStatus): void; + 'user.deleteCustomStatus'(userStatus: Omit): void; 'user.nameChanged'(user: Pick): void; 'user.realNameChanged'(user: Partial): void; 'user.roleUpdate'(update: { @@ -137,7 +137,7 @@ export type EventSignatures = { u?: { _id: IUser['_id']; username: IUser['username']; name?: IUser['name'] }; scope?: string; }): void; - 'user.updateCustomStatus'(userStatus: IUserStatus): void; + 'user.updateCustomStatus'(userStatus: Omit): void; 'user.typing'(data: { user: Partial; isTyping: boolean; roomId: string }): void; 'user.video-conference'(data: { userId: IUser['_id']; diff --git a/packages/core-typings/src/ICustomUserStatus.ts b/packages/core-typings/src/ICustomUserStatus.ts index d634d53247fe5..a1d9e40a22698 100644 --- a/packages/core-typings/src/ICustomUserStatus.ts +++ b/packages/core-typings/src/ICustomUserStatus.ts @@ -1,6 +1,5 @@ -import type { IRocketChatRecord } from './IRocketChatRecord'; +import type { IUserStatus } from './IUserStatus'; -export interface ICustomUserStatus extends IRocketChatRecord { - name: string; - statusType: string; +export interface ICustomUserStatus extends IUserStatus { + _updatedAt?: Date; }