diff --git a/.changeset/strong-laws-pump.md b/.changeset/strong-laws-pump.md new file mode 100644 index 000000000000..a4afefd65316 --- /dev/null +++ b/.changeset/strong-laws-pump.md @@ -0,0 +1,8 @@ +--- +'@rocket.chat/model-typings': patch +'@rocket.chat/meteor': patch +--- + +Change SAU aggregation to consider only sessions from few days ago instead of the whole past. + +This is particularly important for large workspaces in case the cron job did not run for some time, in that case the amount of sessions would accumulate and the aggregation would take a long time to run. diff --git a/.changeset/wise-walls-tan.md b/.changeset/wise-walls-tan.md new file mode 100644 index 000000000000..f558de82ec4c --- /dev/null +++ b/.changeset/wise-walls-tan.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/rest-typings': minor +'@rocket.chat/meteor': minor +--- + +fix: missing params on updateOwnBasicInfo endpoint diff --git a/.changeset/young-trains-glow.md b/.changeset/young-trains-glow.md new file mode 100644 index 000000000000..77f50812143f --- /dev/null +++ b/.changeset/young-trains-glow.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': minor +--- + +Fixed the issue of apps icon uneven alignment in case of missing icons inside message composer toolbar & message toolbar menu. diff --git a/apps/meteor/app/api/server/v1/users.ts b/apps/meteor/app/api/server/v1/users.ts index a4e3f974ac65..b23d41255c3b 100644 --- a/apps/meteor/app/api/server/v1/users.ts +++ b/apps/meteor/app/api/server/v1/users.ts @@ -126,7 +126,9 @@ API.v1.addRoute( realname: this.bodyParams.data.name, username: this.bodyParams.data.username, nickname: this.bodyParams.data.nickname, + bio: this.bodyParams.data.bio, statusText: this.bodyParams.data.statusText, + statusType: this.bodyParams.data.statusType, newPassword: this.bodyParams.data.newPassword, typedPassword: this.bodyParams.data.currentPassword, }; diff --git a/apps/meteor/app/mentions/server/getMentionedTeamMembers.ts b/apps/meteor/app/mentions/server/getMentionedTeamMembers.ts new file mode 100644 index 000000000000..1d36400296b4 --- /dev/null +++ b/apps/meteor/app/mentions/server/getMentionedTeamMembers.ts @@ -0,0 +1,25 @@ +import { Team } from '@rocket.chat/core-services'; +import type { IMessage } from '@rocket.chat/core-typings'; + +import { callbacks } from '../../../lib/callbacks'; + +interface IExtraDataForNotification { + userMentions: any[]; + otherMentions: any[]; + message: IMessage; +} + +callbacks.add('beforeGetMentions', async (mentionIds: string[], extra?: IExtraDataForNotification) => { + const { otherMentions } = extra ?? {}; + + const teamIds = otherMentions?.filter(({ type }) => type === 'team').map(({ _id }) => _id); + + if (!teamIds?.length) { + return mentionIds; + } + + const members = await Team.getMembersByTeamIds(teamIds, { projection: { userId: 1 } }); + mentionIds.push(...new Set(members.map(({ userId }) => userId).filter((userId) => !mentionIds.includes(userId)))); + + return mentionIds; +}); diff --git a/apps/meteor/app/mentions/server/index.ts b/apps/meteor/app/mentions/server/index.ts index 474d41a439e1..a04af05b9db1 100644 --- a/apps/meteor/app/mentions/server/index.ts +++ b/apps/meteor/app/mentions/server/index.ts @@ -1,2 +1,3 @@ -import './server'; +import './getMentionedTeamMembers'; import './methods/getUserMentionsByChannel'; +import './server'; diff --git a/apps/meteor/app/mentions/server/server.ts b/apps/meteor/app/mentions/server/server.ts index 5eb70aae4656..a5b0f89526a2 100644 --- a/apps/meteor/app/mentions/server/server.ts +++ b/apps/meteor/app/mentions/server/server.ts @@ -1,5 +1,5 @@ -import { api } from '@rocket.chat/core-services'; -import type { IUser, IRoom } from '@rocket.chat/core-typings'; +import { api, Team } from '@rocket.chat/core-services'; +import type { IUser, IRoom, ITeam } from '@rocket.chat/core-typings'; import { Subscriptions, Users, Rooms } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; @@ -9,16 +9,28 @@ import { settings } from '../../settings/server'; import MentionsServer from './Mentions'; export class MentionQueries { - async getUsers(usernames: string[]): Promise<(Pick & { type: 'user' })[]> { + async getUsers( + usernames: string[], + ): Promise<((Pick & { type: 'user' }) | (Pick & { type: 'team' }))[]> { + const uniqueUsernames = [...new Set(usernames)]; + const teams = await Team.listByNames(uniqueUsernames, { projection: { name: 1 } }); + const users = await Users.find( - { username: { $in: [...new Set(usernames)] } }, + { username: { $in: uniqueUsernames } }, { projection: { _id: true, username: true, name: 1 } }, ).toArray(); - return users.map((user) => ({ + const taggedUsers = users.map((user) => ({ ...user, - type: 'user', + type: 'user' as const, + })); + + const taggedTeams = teams.map((team) => ({ + ...team, + type: 'team' as const, })); + + return [...taggedUsers, ...taggedTeams]; } async getUser(userId: string): Promise { diff --git a/apps/meteor/app/statistics/server/lib/SAUMonitor.ts b/apps/meteor/app/statistics/server/lib/SAUMonitor.ts index 79ce688cffd3..b3aa68337106 100644 --- a/apps/meteor/app/statistics/server/lib/SAUMonitor.ts +++ b/apps/meteor/app/statistics/server/lib/SAUMonitor.ts @@ -318,33 +318,19 @@ export class SAUMonitorClass { return; } - logger.info('[aggregate] - Aggregating data.'); - - const date = new Date(); - date.setDate(date.getDate() - 0); // yesterday - const yesterday = getDateObj(date); - - for await (const record of aggregates.dailySessionsOfYesterday(Sessions.col, yesterday)) { - await Sessions.updateOne( - { _id: `${record.userId}-${record.year}-${record.month}-${record.day}` }, - { $set: record }, - { upsert: true }, - ); + const today = new Date(); + + // get sessions from 3 days ago to make sure even if a few cron jobs were skipped, we still have the data + const threeDaysAgo = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 3, 0, 0, 0, 0); + + const period = { start: getDateObj(threeDaysAgo), end: getDateObj(today) }; + + logger.info({ msg: '[aggregate] - Aggregating data.', period }); + + for await (const record of aggregates.dailySessions(Sessions.col, period)) { + await Sessions.updateDailySessionById(`${record.userId}-${record.year}-${record.month}-${record.day}`, record); } - await Sessions.updateMany( - { - type: 'session', - year: { $lte: yesterday.year }, - month: { $lte: yesterday.month }, - day: { $lte: yesterday.day }, - }, - { - $set: { - type: 'computed-session', - _computedAt: new Date(), - }, - }, - ); + await Sessions.updateAllSessionsByDateToComputed(period); } } diff --git a/apps/meteor/client/components/message/toolbox/MessageActionMenu.tsx b/apps/meteor/client/components/message/toolbox/MessageActionMenu.tsx index 4c5d442652f2..54a320ebf3d7 100644 --- a/apps/meteor/client/components/message/toolbox/MessageActionMenu.tsx +++ b/apps/meteor/client/components/message/toolbox/MessageActionMenu.tsx @@ -101,6 +101,7 @@ const MessageActionMenu = ({ options, onChangeMenuVisibility, ...props }: Messag data-qa-type='message-action' data-qa-id={option.id} role={option.role ? option.role : 'button'} + gap={!option.icon && option.type === 'apps'} /> ))} {index !== arr.length - 1 && } diff --git a/apps/meteor/client/lib/2fa/process2faReturn.ts b/apps/meteor/client/lib/2fa/process2faReturn.ts index e5ff73be77b1..95f7f1dcb361 100644 --- a/apps/meteor/client/lib/2fa/process2faReturn.ts +++ b/apps/meteor/client/lib/2fa/process2faReturn.ts @@ -1,10 +1,12 @@ import { SHA256 } from '@rocket.chat/sha256'; import { Meteor } from 'meteor/meteor'; +import { lazy } from 'react'; -import TwoFactorModal from '../../components/TwoFactorModal'; import { imperativeModal } from '../imperativeModal'; import { isTotpInvalidError, isTotpRequiredError } from './utils'; +const TwoFactorModal = lazy(() => import('../../components/TwoFactorModal')); + const twoFactorMethods = ['totp', 'email', 'password'] as const; type TwoFactorMethod = (typeof twoFactorMethods)[number]; diff --git a/apps/meteor/client/main.ts b/apps/meteor/client/main.ts index bb08f0242d4f..334515980787 100644 --- a/apps/meteor/client/main.ts +++ b/apps/meteor/client/main.ts @@ -1,13 +1,15 @@ -import '../ee/client/ecdh'; -import './polyfills'; +import { FlowRouter } from 'meteor/kadira:flow-router'; -import '../lib/oauthRedirectUriClient'; -import './lib/meteorCallWrapper'; -import './importPackages'; +FlowRouter.wait(); -import '../ee/client'; -import './methods'; -import './startup'; -import './views/admin'; -import './views/marketplace'; -import './views/account'; +FlowRouter.notFound = { + action: () => undefined, +}; + +import('./polyfills') + .then(() => Promise.all([import('./lib/meteorCallWrapper'), import('../lib/oauthRedirectUriClient')])) + .then(() => import('../ee/client/ecdh')) + .then(() => import('./importPackages')) + .then(() => Promise.all([import('./methods'), import('./startup')])) + .then(() => import('../ee/client')) + .then(() => Promise.all([import('./views/admin'), import('./views/marketplace'), import('./views/account')])); diff --git a/apps/meteor/client/polyfills/index.ts b/apps/meteor/client/polyfills/index.ts index 46f5bcb8d68d..f07d828a4602 100644 --- a/apps/meteor/client/polyfills/index.ts +++ b/apps/meteor/client/polyfills/index.ts @@ -4,4 +4,3 @@ import './childNodeRemove'; import './cssVars'; import './customEventPolyfill'; import './hoverTouchClick'; -import './objectFromEntries'; diff --git a/apps/meteor/client/polyfills/objectFromEntries.ts b/apps/meteor/client/polyfills/objectFromEntries.ts deleted file mode 100644 index d59198ebd1d3..000000000000 --- a/apps/meteor/client/polyfills/objectFromEntries.ts +++ /dev/null @@ -1,5 +0,0 @@ -Object.fromEntries = - Object.fromEntries || - function fromEntries(entries: Iterable): { [k: string]: T } { - return [...entries].reduce((obj, { 0: key, 1: val }) => Object.assign(obj, { [key]: val }), {}); - }; diff --git a/apps/meteor/client/providers/RouterProvider.tsx b/apps/meteor/client/providers/RouterProvider.tsx index 0dd7ee31deed..0f146ec83128 100644 --- a/apps/meteor/client/providers/RouterProvider.tsx +++ b/apps/meteor/client/providers/RouterProvider.tsx @@ -17,12 +17,6 @@ import React from 'react'; import { appLayout } from '../lib/appLayout'; import { queueMicrotask } from '../lib/utils/queueMicrotask'; -FlowRouter.wait(); - -FlowRouter.notFound = { - action: () => undefined, -}; - const subscribers = new Set<() => void>(); const listenToRouteChange = () => { diff --git a/apps/meteor/client/views/admin/rooms/RoomRow.tsx b/apps/meteor/client/views/admin/rooms/RoomRow.tsx new file mode 100644 index 000000000000..2c0dbb8c31a6 --- /dev/null +++ b/apps/meteor/client/views/admin/rooms/RoomRow.tsx @@ -0,0 +1,90 @@ +import { isDiscussion } from '@rocket.chat/core-typings'; +import type { IRoom, RoomAdminFieldsType } from '@rocket.chat/core-typings'; +import { Box, Icon } from '@rocket.chat/fuselage'; +import { useMediaQuery } from '@rocket.chat/fuselage-hooks'; +import { useRouter, useTranslation } from '@rocket.chat/ui-contexts'; +import React, { useCallback } from 'react'; + +import { GenericTableCell, GenericTableRow } from '../../../components/GenericTable'; +import RoomAvatar from '../../../components/avatar/RoomAvatar'; +import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; + +const roomTypeI18nMap = { + l: 'Omnichannel', + c: 'Channel', + d: 'Direct_Message', + p: 'Private_Channel', +} as const; + +const getRoomDisplayName = (room: Pick): string | undefined => + room.t === 'd' ? room.usernames?.join(' x ') : roomCoordinator.getRoomName(room.t, room); + +const RoomRow = ({ room }: { room: Pick }) => { + const t = useTranslation(); + const mediaQuery = useMediaQuery('(min-width: 1024px)'); + const router = useRouter(); + + const { _id, t: type, usersCount, msgs, default: isDefault, featured, ...args } = room; + const icon = roomCoordinator.getRoomDirectives(room.t).getIcon?.(room); + const roomName = getRoomDisplayName(room); + + const getRoomType = ( + room: Pick, + ): (typeof roomTypeI18nMap)[keyof typeof roomTypeI18nMap] | 'Teams_Public_Team' | 'Teams_Private_Team' | 'Discussion' => { + if (room.teamMain) { + return room.t === 'c' ? 'Teams_Public_Team' : 'Teams_Private_Team'; + } + if (isDiscussion(room)) { + return 'Discussion'; + } + return roomTypeI18nMap[(room as IRoom).t as keyof typeof roomTypeI18nMap]; + }; + + const onClick = useCallback( + (rid) => (): void => + router.navigate({ + name: 'admin-rooms', + params: { + context: 'edit', + id: rid, + }, + }), + [router], + ); + + return ( + + + + + + {icon && } + + {roomName} + + + + + + + {t(getRoomType(room))} + + + {usersCount} + {mediaQuery && {msgs}} + {mediaQuery && {isDefault ? t('True') : t('False')}} + {mediaQuery && {featured ? t('True') : t('False')}} + + ); +}; + +export default RoomRow; diff --git a/apps/meteor/client/views/admin/rooms/RoomsTable.tsx b/apps/meteor/client/views/admin/rooms/RoomsTable.tsx index 6b2c4435c8fa..c480fab9f657 100644 --- a/apps/meteor/client/views/admin/rooms/RoomsTable.tsx +++ b/apps/meteor/client/views/admin/rooms/RoomsTable.tsx @@ -1,31 +1,23 @@ -import { type IRoom, isDiscussion, isPublicRoom } from '@rocket.chat/core-typings'; -import { Box, Icon, Pagination, States, StatesIcon, StatesTitle, StatesActions, StatesAction } from '@rocket.chat/fuselage'; +import { Pagination, States, StatesIcon, StatesTitle, StatesActions, StatesAction } from '@rocket.chat/fuselage'; import { useMediaQuery, useDebouncedValue } from '@rocket.chat/fuselage-hooks'; import type { OptionProp } from '@rocket.chat/ui-client'; -import { useEndpoint, useRouter, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; +import { useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; -import type { CSSProperties, ReactElement, MutableRefObject } from 'react'; -import React, { useRef, useState, useEffect, useMemo, useCallback } from 'react'; +import type { ReactElement, MutableRefObject } from 'react'; +import React, { useRef, useState, useEffect, useMemo } from 'react'; import GenericNoResults from '../../../components/GenericNoResults'; import { GenericTable, GenericTableBody, - GenericTableCell, GenericTableHeader, GenericTableHeaderCell, GenericTableLoadingTable, - GenericTableRow, } from '../../../components/GenericTable'; import { usePagination } from '../../../components/GenericTable/hooks/usePagination'; import { useSort } from '../../../components/GenericTable/hooks/useSort'; -import RoomAvatar from '../../../components/avatar/RoomAvatar'; -import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; +import RoomRow from './RoomRow'; import RoomsTableFilters from './RoomsTableFilters'; -import { useFilteredTypeRooms } from './useFilteredTypeRooms'; -import { useFilteredVisibilityRooms } from './useFilteredVisibilityRooms'; - -const style: CSSProperties = { whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }; type RoomFilters = { searchText: string; @@ -33,34 +25,9 @@ type RoomFilters = { visibility: OptionProp[]; }; -const DEFAULT_TYPES = ['d', 'p', 'c', 'l', 'discussions', 'teams']; - -const roomTypeI18nMap = { - l: 'Omnichannel', - c: 'Channel', - d: 'Direct_Message', - p: 'Private_Channel', -} as const; - -const getRoomType = ( - room: IRoom, -): (typeof roomTypeI18nMap)[keyof typeof roomTypeI18nMap] | 'Teams_Public_Team' | 'Teams_Private_Team' | 'Discussion' => { - if (room.teamMain) { - return room.t === 'c' ? 'Teams_Public_Team' : 'Teams_Private_Team'; - } - if (isDiscussion(room)) { - return 'Discussion'; - } - return roomTypeI18nMap[(room as IRoom).t as keyof typeof roomTypeI18nMap]; -}; - -const getRoomDisplayName = (room: IRoom): string | undefined => - room.t === 'd' ? room.usernames?.join(' x ') : roomCoordinator.getRoomName(room.t, room); - const RoomsTable = ({ reload }: { reload: MutableRefObject<() => void> }): ReactElement => { - const mediaQuery = useMediaQuery('(min-width: 1024px)'); - const t = useTranslation(); + const mediaQuery = useMediaQuery('(min-width: 1024px)'); const [roomFilters, setRoomFilters] = useState({ searchText: '', types: [], visibility: [] }); @@ -80,29 +47,15 @@ const RoomsTable = ({ reload }: { reload: MutableRefObject<() => void> }): React sort: `{ "${sortBy}": ${sortDirection === 'asc' ? 1 : -1} }`, count: itemsPerPage, offset: searchText === prevRoomFilterText.current ? current : 0, - types: DEFAULT_TYPES, + types: [...roomFilters.types.map((roomType) => roomType.id)], }; - }, [searchText, sortBy, sortDirection, itemsPerPage, prevRoomFilterText, current, setCurrent]), + }, [searchText, sortBy, sortDirection, itemsPerPage, current, roomFilters.types, setCurrent]), 500, ); const getAdminRooms = useEndpoint('GET', '/v1/rooms.adminRooms'); - const dispatchToastMessage = useToastMessageDispatch(); - - const { data, refetch, isSuccess, isLoading, isError } = useQuery( - ['rooms', query, 'admin'], - async () => { - const adminRooms = await getAdminRooms(query); - - return { ...adminRooms, rooms: adminRooms.rooms as IRoom[] }; - }, - { - onError: (error) => { - dispatchToastMessage({ type: 'error', message: error }); - }, - }, - ); + const { data, refetch, isSuccess, isLoading, isError } = useQuery(['rooms', query, 'admin'], async () => getAdminRooms(query)); useEffect(() => { reload.current = refetch; @@ -112,48 +65,29 @@ const RoomsTable = ({ reload }: { reload: MutableRefObject<() => void> }): React prevRoomFilterText.current = searchText; }, [searchText]); - const router = useRouter(); - - const onClick = useCallback( - (rid) => (): void => - router.navigate({ - name: 'admin-rooms', - params: { - context: 'edit', - id: rid, - }, - }), - [router], - ); - - const headers = useMemo( - () => - [ - - {t('Name')} - , - - {t('Type')} - , - - {t('Visibility')} - , - - {t('Users')} - , - mediaQuery && ( + const headers = ( + <> + + {t('Name')} + + + {t('Type')} + + + {t('Users')} + + {mediaQuery && ( + <> {t('Msgs')} - ), - mediaQuery && ( void> }): React > {t('Default')} - ), - mediaQuery && ( void> }): React > {t('Featured')} - ), - ].filter(Boolean), - [sortDirection, sortBy, setSort, t, mediaQuery], - ); - - const renderRow = useCallback( - (room: IRoom) => { - const { _id, t: type, usersCount, msgs, default: isDefault, featured, ...args } = room; - const visibility = isPublicRoom(room) ? 'Public' : 'Private'; - const icon = roomCoordinator.getRoomDirectives(room.t).getIcon?.(room); - const roomName = getRoomDisplayName(room); - - return ( - - - - - - - {icon && } - - {roomName} - - - - - - - - {t(getRoomType(room))} - - - - - - {t(visibility)} - - - - {usersCount} - {mediaQuery && {msgs}} - {mediaQuery && {isDefault ? t('True') : t('False')}} - {mediaQuery && {featured ? t('True') : t('False')}} - - ); - }, - [mediaQuery, onClick, t], + + )} + ); - function intersectArraysWithoutDuplicates(array1: IRoom[], array2: IRoom[]) { - const set2 = new Set(array2); - - return [...new Set(array1)].filter((item) => set2.has(item)); - } - - const roomsTypeList = useFilteredTypeRooms(roomFilters.types, isLoading, data?.rooms); - const roomsVisibilityList = useFilteredVisibilityRooms(roomFilters.visibility, isLoading, data?.rooms); - - const roomsList = intersectArraysWithoutDuplicates(roomsTypeList, roomsVisibilityList); - return ( <> - {isLoading && ( {headers} - + )} - {isSuccess && data && data?.rooms.length > 0 && ( + {isSuccess && data.rooms.length === 0 && } + {isSuccess && data.rooms.length > 0 && ( <> {headers} - {isSuccess && roomsList?.map((room) => renderRow(room))} + + {data.rooms?.map((room) => ( + + ))} + void> }): React /> )} - {isSuccess && data && data.rooms.length === 0 && } {isError && ( diff --git a/apps/meteor/client/views/admin/rooms/RoomsTableFilters.tsx b/apps/meteor/client/views/admin/rooms/RoomsTableFilters.tsx index 0d8e5bd0c97e..dede0b34b918 100644 --- a/apps/meteor/client/views/admin/rooms/RoomsTableFilters.tsx +++ b/apps/meteor/client/views/admin/rooms/RoomsTableFilters.tsx @@ -12,12 +12,7 @@ const roomTypeFilterStructure = [ isGroupTitle: true, }, { - id: 'channels', - text: 'Channels', - checked: false, - }, - { - id: 'directMessages', + id: 'd', text: 'Direct_Message', checked: false, }, @@ -27,31 +22,23 @@ const roomTypeFilterStructure = [ checked: false, }, { - id: 'omnichannel', + id: 'l', text: 'Omnichannel', checked: false, }, { - id: 'teams', - text: 'Teams', + id: 'p', + text: 'Private_Channels', checked: false, }, -] as OptionProp[]; - -const roomVisibilityFilterStructure = [ { - id: 'filter_by_visibility', - text: 'Filter_by_visibility', - isGroupTitle: true, - }, - { - id: 'private', - text: 'Private', + id: 'c', + text: 'Public_Channels', checked: false, }, { - id: 'public', - text: 'Public', + id: 'teams', + text: 'Teams', checked: false, }, ] as OptionProp[]; @@ -60,13 +47,11 @@ const RoomsTableFilters = ({ setFilters }: { setFilters: Dispatch(roomTypeFilterStructure); - const [roomVisibilityOptions, setRoomVisibilityOptions] = useState(roomVisibilityFilterStructure); const [roomTypeSelectedOptions, setRoomTypeSelectedOptions] = useState([]); - const [roomVisibilitySelectedOptions, setRoomVisibilitySelectedOptions] = useState([]); useEffect(() => { - return setFilters({ searchText: text, types: roomTypeSelectedOptions, visibility: roomVisibilitySelectedOptions }); - }, [setFilters, roomTypeSelectedOptions, roomVisibilitySelectedOptions, text]); + return setFilters({ searchText: text, types: roomTypeSelectedOptions }); + }, [setFilters, roomTypeSelectedOptions, text]); const handleSearchTextChange = useCallback((event) => setText(event.currentTarget.value), []); @@ -100,17 +85,6 @@ const RoomsTableFilters = ({ setFilters }: { setFilters: Dispatch - - - - ); }; diff --git a/apps/meteor/client/views/admin/rooms/useFilteredTypeRooms.tsx b/apps/meteor/client/views/admin/rooms/useFilteredTypeRooms.tsx deleted file mode 100644 index 7114aa4f35c9..000000000000 --- a/apps/meteor/client/views/admin/rooms/useFilteredTypeRooms.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import type { IRoom } from '@rocket.chat/core-typings'; -import { isDiscussion, isTeamRoom, isDirectMessageRoom } from '@rocket.chat/core-typings'; -import type { OptionProp } from '@rocket.chat/ui-client'; - -const filterRoomsByChannels = (room: Partial): boolean => - (room.t === 'c' || room.t === 'p') && !isDiscussion(room) && !isTeamRoom(room); // can be a public channel or a private channel (group) -const filterRoomsByDirectMessages = (room: Partial): boolean => isDirectMessageRoom(room); -const filterRoomsByDiscussions = (room: Partial): boolean => isDiscussion(room); -const filterRoomsByOmnichannel = ({ t }: Partial): boolean => t === 'l'; // LiveChat -const filterRoomsByTeams = (room: Partial): boolean => isTeamRoom(room); - -const filters: Record) => boolean> = { - channels: filterRoomsByChannels, - directMessages: filterRoomsByDirectMessages, - discussions: filterRoomsByDiscussions, - omnichannel: filterRoomsByOmnichannel, - teams: filterRoomsByTeams, -}; - -export const useFilteredTypeRooms = (selectedOptions: OptionProp[], isLoading: boolean, rooms?: IRoom[]) => { - if (isLoading || !rooms) return []; - if (selectedOptions.length === 0) return rooms; - - let filtered: IRoom[] = []; - - selectedOptions.forEach((option) => { - filtered = [...new Set([...filtered, ...rooms.filter(filters[option.id])])]; - }); - - return filtered; -}; diff --git a/apps/meteor/client/views/admin/rooms/useFilteredVisibilityRooms.tsx b/apps/meteor/client/views/admin/rooms/useFilteredVisibilityRooms.tsx deleted file mode 100644 index be4064817001..000000000000 --- a/apps/meteor/client/views/admin/rooms/useFilteredVisibilityRooms.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import type { IRoom } from '@rocket.chat/core-typings'; -import { isPublicRoom } from '@rocket.chat/core-typings'; -import type { OptionProp } from '@rocket.chat/ui-client'; - -const filterRoomsByPrivate = (room: Partial): boolean => !isPublicRoom(room); -const filterRoomsByPublic = (room: Partial): boolean => isPublicRoom(room); - -const filters: Record) => boolean> = { - private: filterRoomsByPrivate, - public: filterRoomsByPublic, -}; - -export const useFilteredVisibilityRooms = (selectedOptions: OptionProp[], isLoading: boolean, rooms?: IRoom[]) => { - if (isLoading || !rooms) return []; - if (selectedOptions.length === 0) return rooms; - - let filtered: IRoom[] = []; - - selectedOptions.forEach((option) => { - filtered = [...new Set([...filtered, ...rooms.filter(filters[option.id])])]; - }); - - return filtered; -}; diff --git a/apps/meteor/client/views/room/composer/messageBox/MessageBoxActionsToolbar/ActionsToolbarDropdown.tsx b/apps/meteor/client/views/room/composer/messageBox/MessageBoxActionsToolbar/ActionsToolbarDropdown.tsx index 021f6518021a..5066ecb192e1 100644 --- a/apps/meteor/client/views/room/composer/messageBox/MessageBoxActionsToolbar/ActionsToolbarDropdown.tsx +++ b/apps/meteor/client/views/room/composer/messageBox/MessageBoxActionsToolbar/ActionsToolbarDropdown.tsx @@ -93,6 +93,7 @@ const ActionsToolbarDropdown = ({ isRecording, rid, tmid, actions, ...props }: A chat: chatContext, }) } + gap={!item.icon} > {item.icon && ['name']} />} {item.name} diff --git a/apps/meteor/ee/app/license/server/bundles.ts b/apps/meteor/ee/app/license/server/bundles.ts index 507283b3e60f..70f9d7b5a653 100644 --- a/apps/meteor/ee/app/license/server/bundles.ts +++ b/apps/meteor/ee/app/license/server/bundles.ts @@ -8,7 +8,6 @@ export type BundleFeature = | 'engagement-dashboard' | 'push-privacy' | 'scalability' - | 'teams-mention' | 'saml-enterprise' | 'device-management' | 'oauth-enterprise' @@ -32,7 +31,6 @@ const bundles: IBundle = { 'engagement-dashboard', 'push-privacy', 'scalability', - 'teams-mention', 'saml-enterprise', 'oauth-enterprise', 'device-management', diff --git a/apps/meteor/ee/app/teams-mention/server/EEMentionQueries.js b/apps/meteor/ee/app/teams-mention/server/EEMentionQueries.js deleted file mode 100644 index 020a90365d26..000000000000 --- a/apps/meteor/ee/app/teams-mention/server/EEMentionQueries.js +++ /dev/null @@ -1,19 +0,0 @@ -import { Team } from '@rocket.chat/core-services'; - -export const MentionQueriesEnterprise = { - async getUsers(sup, usernames) { - const uniqueUsernames = [...new Set(usernames)]; - const teams = await Team.listByNames(uniqueUsernames, { projection: { name: 1 } }); - - if (!teams?.length) { - return sup(usernames); - } - - return teams - .map((team) => ({ - ...team, - type: 'team', - })) - .concat(sup(usernames)); - }, -}; diff --git a/apps/meteor/ee/app/teams-mention/server/EESpotlight.js b/apps/meteor/ee/app/teams-mention/server/EESpotlight.js deleted file mode 100644 index 83829a759bba..000000000000 --- a/apps/meteor/ee/app/teams-mention/server/EESpotlight.js +++ /dev/null @@ -1,34 +0,0 @@ -import { Team } from '@rocket.chat/core-services'; - -export const SpotlightEnterprise = { - mapTeams(_, teams) { - return teams.map((t) => { - t.isTeam = true; - t.username = t.name; - t.status = 'online'; - return t; - }); - }, - - async _searchTeams(_, userId, { text, options, users, mentions }) { - if (!mentions) { - return users; - } - - options.limit -= users.length; - - if (options.limit <= 0) { - return users; - } - - const teamOptions = { ...options, projection: { name: 1, type: 1 } }; - const teams = await Team.search(userId, text, teamOptions); - users.push(...this.mapTeams(teams)); - - return users; - }, - - async _performExtraUserSearches(_, userId, searchParams) { - return this._searchTeams(userId, searchParams); - }, -}; diff --git a/apps/meteor/ee/app/teams-mention/server/index.ts b/apps/meteor/ee/app/teams-mention/server/index.ts deleted file mode 100644 index 631610e4c6d8..000000000000 --- a/apps/meteor/ee/app/teams-mention/server/index.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Team } from '@rocket.chat/core-services'; -import type { ITeamMember, IMessage } from '@rocket.chat/core-typings'; - -import { MentionQueries } from '../../../../app/mentions/server/server'; -import { callbacks } from '../../../../lib/callbacks'; -import { Spotlight } from '../../../../server/lib/spotlight'; -import { onLicense } from '../../license/server'; -import { overwriteClassOnLicense } from '../../license/server/license'; -import { MentionQueriesEnterprise } from './EEMentionQueries'; -import { SpotlightEnterprise } from './EESpotlight'; - -interface IExtraDataForNotification { - userMentions: any[]; - otherMentions: any[]; - message: IMessage; -} - -await onLicense('teams-mention', async () => { - // Override spotlight with EE version - await overwriteClassOnLicense('teams-mention', Spotlight, SpotlightEnterprise); - await overwriteClassOnLicense('teams-mention', MentionQueries, MentionQueriesEnterprise); - - callbacks.add('beforeGetMentions', async (mentionIds: string[], extra?: IExtraDataForNotification) => { - const { otherMentions } = extra ?? {}; - - const teamIds = otherMentions?.filter(({ type }) => type === 'team').map(({ _id }) => _id); - - if (!teamIds?.length) { - return mentionIds; - } - - const members: ITeamMember[] = await Team.getMembersByTeamIds(teamIds, { projection: { userId: 1 } }); - mentionIds.push( - ...new Set(members.map(({ userId }: { userId: string }) => userId).filter((userId: string) => !mentionIds.includes(userId))), - ); - - return mentionIds; - }); -}); diff --git a/apps/meteor/ee/server/index.ts b/apps/meteor/ee/server/index.ts index 2e526776c772..9b56239ad046 100644 --- a/apps/meteor/ee/server/index.ts +++ b/apps/meteor/ee/server/index.ts @@ -8,7 +8,6 @@ import '../app/livechat-enterprise/server/index'; import '../app/message-read-receipt/server/index'; import '../app/voip-enterprise/server/index'; import '../app/settings/server/index'; -import '../app/teams-mention/server/index'; import './api'; import './requestSeatsRoute'; import './configuration/index'; diff --git a/apps/meteor/ee/server/models/raw/LivechatRooms.ts b/apps/meteor/ee/server/models/raw/LivechatRooms.ts index 227da1c98e8c..70824c7d7130 100644 --- a/apps/meteor/ee/server/models/raw/LivechatRooms.ts +++ b/apps/meteor/ee/server/models/raw/LivechatRooms.ts @@ -540,6 +540,13 @@ export class LivechatRoomsRawEE extends LivechatRoomsRaw implements ILivechatRoo }, }, }, + { + $match: { + _id: { + $ne: null, + }, + }, + }, { $sort: sort || { total: 1 }, }, diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 34d6a6c4e8ba..f3e5e89c80d6 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -1622,7 +1622,7 @@ "Direct": "Direct", "Direction": "Direction", "Livechat_Facebook_API_Secret": "OmniChannel API Secret", - "Direct_Message": "Direct Message", + "Direct_Message": "Direct message", "Livechat_Facebook_Enabled": "Facebook integration enabled", "Direct_message_creation_description": "You are about to create a chat with multiple users. Add the ones you would like to talk, everyone in the same place, using direct messages.", "Direct_message_someone": "Direct message someone", @@ -4053,9 +4053,10 @@ "Privacy_summary": "Privacy summary", "Private": "Private", "private": "private", + "Private_channels": "Private channels", "Private_Apps": "Private Apps", "Private_Channel": "Private Channel", - "Private_Channels": "Private Channels", + "Private_Channels": "Private channels", "Private_Chats": "Private Chats", "Private_Group": "Private Group", "Private_Groups": "Private Groups", @@ -4085,7 +4086,7 @@ "Public": "Public", "public": "public", "Public_Channel": "Public Channel", - "Public_Channels": "Public Channels", + "Public_Channels": "Public channels", "Public_Community": "Public Community", "Public_URL": "Public URL", "Purchase_for_free": "Purchase for FREE", diff --git a/apps/meteor/server/lib/spotlight.js b/apps/meteor/server/lib/spotlight.js index 38dc1b873878..ae38c903cbe2 100644 --- a/apps/meteor/server/lib/spotlight.js +++ b/apps/meteor/server/lib/spotlight.js @@ -1,3 +1,4 @@ +import { Team } from '@rocket.chat/core-services'; import { Users, Subscriptions as SubscriptionsRaw, Rooms } from '@rocket.chat/models'; import { escapeRegExp } from '@rocket.chat/string-helpers'; @@ -133,8 +134,31 @@ export class Spotlight { } } - async _performExtraUserSearches(/* userId, searchParams */) { - // Overwrite this method to include extra searches + mapTeams(teams) { + return teams.map((t) => { + t.isTeam = true; + t.username = t.name; + t.status = 'online'; + return t; + }); + } + + async _searchTeams(userId, { text, options, users, mentions }) { + if (!mentions) { + return users; + } + + options.limit -= users.length; + + if (options.limit <= 0) { + return users; + } + + const teamOptions = { ...options, projection: { name: 1, type: 1 } }; + const teams = await Team.search(userId, text, teamOptions); + users.push(...this.mapTeams(teams)); + + return users; } async searchUsers({ userId, rid, text, usernames, mentions }) { @@ -245,7 +269,7 @@ export class Spotlight { return users; } - if (await this._performExtraUserSearches(userId, searchParams)) { + if (await this._searchTeams(userId, searchParams)) { return users; } diff --git a/apps/meteor/server/models/raw/Sessions.ts b/apps/meteor/server/models/raw/Sessions.ts index 68d5149232ed..c02fc8b5de99 100644 --- a/apps/meteor/server/models/raw/Sessions.ts +++ b/apps/meteor/server/models/raw/Sessions.ts @@ -167,9 +167,9 @@ const getProjectionByFullDate = (): { day: string; month: string; year: string } }); export const aggregates = { - dailySessionsOfYesterday( + dailySessions( collection: Collection, - { year, month, day }: DestructuredDate, + { start, end }: DestructuredRange, ): AggregationCursor< Pick & { time: number; @@ -178,115 +178,101 @@ export const aggregates = { _computedAt: string; } > { - return collection.aggregate< - Pick & { - time: number; - sessions: number; - devices: ISession['device'][]; - _computedAt: string; - } - >( - [ - { - $match: { - userId: { $exists: true }, - lastActivityAt: { $exists: true }, - device: { $exists: true }, - type: 'session', - $or: [ - { - year: { $lt: year }, - }, - { - year, - month: { $lt: month }, - }, - { - year, - month, - day: { $lte: day }, - }, - ], - }, - }, - { - $project: { - userId: 1, - device: 1, - day: 1, - month: 1, - year: 1, - mostImportantRole: 1, - time: { $trunc: { $divide: [{ $subtract: ['$lastActivityAt', '$loginAt'] }, 1000] } }, - }, - }, - { - $match: { - time: { $gt: 0 }, - }, + const pipeline = [ + { + $match: { + userId: { $exists: true }, + lastActivityAt: { $exists: true }, + device: { $exists: true }, + type: 'session', + ...matchBasedOnDate(start, end), }, - { - $group: { - _id: { - userId: '$userId', - device: '$device', - day: '$day', - month: '$month', - year: '$year', - }, - mostImportantRole: { $first: '$mostImportantRole' }, - time: { $sum: '$time' }, - sessions: { $sum: 1 }, - }, + }, + { + $project: { + userId: 1, + device: 1, + day: 1, + month: 1, + year: 1, + mostImportantRole: 1, + time: { $trunc: { $divide: [{ $subtract: ['$lastActivityAt', '$loginAt'] }, 1000] } }, }, - { - $sort: { - time: -1, - }, + }, + { + $match: { + time: { $gt: 0 }, }, - { - $group: { - _id: { - userId: '$_id.userId', - day: '$_id.day', - month: '$_id.month', - year: '$_id.year', - }, - mostImportantRole: { $first: '$mostImportantRole' }, - time: { $sum: '$time' }, - sessions: { $sum: '$sessions' }, - devices: { - $push: { - sessions: '$sessions', - time: '$time', - device: '$_id.device', - }, - }, + }, + { + $group: { + _id: { + userId: '$userId', + device: '$device', + day: '$day', + month: '$month', + year: '$year', }, + mostImportantRole: { $first: '$mostImportantRole' }, + time: { $sum: '$time' }, + sessions: { $sum: 1 }, }, - { - $sort: { - _id: 1, - }, + }, + { + $sort: { + time: -1, }, - { - $project: { - _id: 0, - type: { $literal: 'user_daily' }, - _computedAt: { $literal: new Date() }, + }, + { + $group: { + _id: { + userId: '$_id.userId', day: '$_id.day', month: '$_id.month', year: '$_id.year', - userId: '$_id.userId', - mostImportantRole: 1, - time: 1, - sessions: 1, - devices: 1, + }, + mostImportantRole: { $first: '$mostImportantRole' }, + time: { $sum: '$time' }, + sessions: { $sum: '$sessions' }, + devices: { + $push: { + sessions: '$sessions', + time: '$time', + device: '$_id.device', + }, }, }, - ], - { allowDiskUse: true }, - ); + }, + { + $sort: { + _id: 1, + }, + }, + { + $project: { + _id: 0, + type: { $literal: 'user_daily' }, + _computedAt: { $literal: new Date() }, + day: '$_id.day', + month: '$_id.month', + year: '$_id.year', + userId: '$_id.userId', + mostImportantRole: 1, + time: 1, + sessions: 1, + devices: 1, + }, + }, + ]; + + return collection.aggregate< + Pick & { + time: number; + sessions: number; + devices: ISession['device'][]; + _computedAt: string; + } + >(pipeline, { allowDiskUse: true }); }, async getUniqueUsersOfYesterday( @@ -1616,4 +1602,23 @@ export class SessionsRaw extends BaseRaw implements ISessionsModel { return this.col.bulkWrite(ops, { ordered: false }); } + + async updateDailySessionById(_id: ISession['_id'], record: Partial): Promise { + return this.updateOne({ _id }, { $set: record }, { upsert: true }); + } + + async updateAllSessionsByDateToComputed({ start, end }: DestructuredRange): Promise { + return this.updateMany( + { + type: 'session', + ...matchBasedOnDate(start, end), + }, + { + $set: { + type: 'computed-session', + _computedAt: new Date(), + }, + }, + ); + } } diff --git a/apps/meteor/tests/e2e/homepage.spec.ts b/apps/meteor/tests/e2e/homepage.spec.ts index 380fa54d2af3..4f8f9d09a2f3 100644 --- a/apps/meteor/tests/e2e/homepage.spec.ts +++ b/apps/meteor/tests/e2e/homepage.spec.ts @@ -32,7 +32,7 @@ test.describe.serial('homepage', () => { await adminPage.close(); }); - test('layout', async () => { + test('expect customize button and all cards to be visible', async () => { await test.step('expect show customize button', async () => { await expect(adminPage.locator('role=button[name="Customize"]')).toBeVisible(); }); @@ -47,7 +47,7 @@ test.describe.serial('homepage', () => { await expect((await api.post('/settings/Layout_Home_Body', { value: '' })).status()).toBe(200); }); - test('layout', async () => { + test('visibility and button functionality in custom body with empty custom content', async () => { await test.step('expect default value in custom body', async () => { await expect( adminPage.locator('role=status[name="Admins may insert content html to be rendered in this white space."]'), @@ -70,7 +70,7 @@ test.describe.serial('homepage', () => { await expect((await api.post('/settings/Layout_Home_Body', { value: 'Hello admin' })).status()).toBe(200); }); - test('layout', async () => { + test('visibility and button functionality in custom body with custom content', async () => { await test.step('expect custom body to be visible', async () => { await expect(adminPage.locator('role=status[name="Hello admin"]')).toBeVisible(); }); @@ -122,7 +122,7 @@ test.describe.serial('homepage', () => { await regularUserPage.close(); }); - test('layout', async () => { + test('the option customize is not be active', async () => { await test.step('expect to not show customize button', async () => { await expect(regularUserPage.locator('role=button[name="Customize"]')).not.toBeVisible(); }); @@ -162,7 +162,7 @@ test.describe.serial('homepage', () => { expect((await api.post('/settings/Layout_Home_Title', { value: 'Home' })).status()).toBe(200); }); - test('layout', async () => { + test('expect welcome text and header text to be correct', async () => { await test.step('expect welcome text to be NewSiteName', async () => { await expect(regularUserPage.locator('role=heading[name="Welcome to NewSiteName"]')).toBeVisible(); }); @@ -202,7 +202,7 @@ test.describe.serial('homepage', () => { expect((await api.post('/settings/Layout_Custom_Body_Only', { value: false })).status()).toBe(200); }); - test('layout', async () => { + test('expect default layout not be visible and custom body visible', async () => { await test.step('expect default layout to not be visible', async () => { await expect(regularUserPage.locator('[data-qa-id="homepage-welcome-text"]')).not.toBeVisible(); }); diff --git a/ee/apps/ddp-streamer/src/DDPStreamer.ts b/ee/apps/ddp-streamer/src/DDPStreamer.ts index 868ad8fec202..bccb35d2b326 100644 --- a/ee/apps/ddp-streamer/src/DDPStreamer.ts +++ b/ee/apps/ddp-streamer/src/DDPStreamer.ts @@ -72,6 +72,15 @@ export class DDPStreamer extends ServiceClass { return; } + metrics.register({ + name: 'rocketchat_subscription', + type: 'histogram', + labelNames: ['subscription'], + description: 'Client subscriptions to Rocket.Chat', + unit: 'millisecond', + quantiles: true, + }); + metrics.register({ name: 'users_connected', type: 'gauge', @@ -86,6 +95,8 @@ export class DDPStreamer extends ServiceClass { description: 'Users logged by streamer', }); + server.setMetrics(metrics); + server.on(DDP_EVENTS.CONNECTED, () => { metrics.increment('users_connected', { nodeID }, 1); }); diff --git a/ee/apps/ddp-streamer/src/Server.ts b/ee/apps/ddp-streamer/src/Server.ts index 01c7c63511ff..af083621230d 100644 --- a/ee/apps/ddp-streamer/src/Server.ts +++ b/ee/apps/ddp-streamer/src/Server.ts @@ -1,5 +1,6 @@ import { EventEmitter } from 'events'; +import type { IServiceMetrics } from '@rocket.chat/core-services'; import { MeteorService, isMeteorError, MeteorError } from '@rocket.chat/core-services'; import { Logger } from '@rocket.chat/logger'; import ejson from 'ejson'; @@ -38,6 +39,8 @@ export class Server extends EventEmitter { private _methods = new Map(); + private metrics?: IServiceMetrics; + public readonly id = uuidv1(); serialize = ejson.stringify; @@ -52,6 +55,10 @@ export class Server extends EventEmitter { return ejson.parse(payload); }; + setMetrics(metrics: IServiceMetrics): void { + this.metrics = metrics; + } + async call(client: Client, packet: IPacket): Promise { // if client is not connected we don't need to do anything if (client.ws.readyState !== WebSocket.OPEN) { @@ -103,9 +110,13 @@ export class Server extends EventEmitter { throw new MeteorError(404, `Subscription '${packet.name}' not found`); } + const end = this.metrics?.timer('rocketchat_subscription', { subscription: packet.name }); + const publication = new Publication(client, packet, this); const [eventName, options] = packet.params; await fn.call(publication, eventName, options); + + end?.(); } catch (err: unknown) { return this.nosub(client, packet, handleInternalException(err, 'Subscription error')); } diff --git a/packages/core-services/src/types/IBroker.ts b/packages/core-services/src/types/IBroker.ts index 8647d04a56dc..4bd48afef0ff 100644 --- a/packages/core-services/src/types/IBroker.ts +++ b/packages/core-services/src/types/IBroker.ts @@ -27,6 +27,7 @@ export type BaseMetricOptions = { labelNames?: Array; unit?: string; aggregator?: string; + [key: string]: unknown; }; export interface IServiceMetrics { diff --git a/packages/model-typings/src/models/ISessionsModel.ts b/packages/model-typings/src/models/ISessionsModel.ts index cebe0c861d3f..1e6a36fd6f78 100644 --- a/packages/model-typings/src/models/ISessionsModel.ts +++ b/packages/model-typings/src/models/ISessionsModel.ts @@ -145,4 +145,8 @@ export interface ISessionsModel extends IBaseModel { }): Promise; createBatch(sessions: OptionalId[]): Promise; + + updateDailySessionById(_id: ISession['_id'], record: Partial): Promise; + + updateAllSessionsByDateToComputed({ start, end }: DestructuredRange): Promise; } diff --git a/packages/rest-typings/src/v1/users.ts b/packages/rest-typings/src/v1/users.ts index 947228476bdd..c47f4be6404d 100644 --- a/packages/rest-typings/src/v1/users.ts +++ b/packages/rest-typings/src/v1/users.ts @@ -9,7 +9,6 @@ import type { } from '@rocket.chat/core-typings'; import Ajv from 'ajv'; -import type { UsersSendConfirmationEmailParamsPOST } from '..'; import type { PaginatedRequest } from '../helpers/PaginatedRequest'; import type { PaginatedResult } from '../helpers/PaginatedResult'; import type { UserCreateParamsPOST } from './users/UserCreateParamsPOST'; @@ -20,7 +19,9 @@ import type { UserSetActiveStatusParamsPOST } from './users/UserSetActiveStatusP import type { UsersAutocompleteParamsGET } from './users/UsersAutocompleteParamsGET'; import type { UsersInfoParamsGet } from './users/UsersInfoParamsGet'; import type { UsersListTeamsParamsGET } from './users/UsersListTeamsParamsGET'; +import type { UsersSendConfirmationEmailParamsPOST } from './users/UsersSendConfirmationEmailParamsPOST'; import type { UsersSetPreferencesParamsPOST } from './users/UsersSetPreferenceParamsPOST'; +import type { UsersUpdateOwnBasicInfoParamsPOST } from './users/UsersUpdateOwnBasicInfoParamsPOST'; import type { UsersUpdateParamsPOST } from './users/UsersUpdateParamsPOST'; const ajv = new Ajv({ @@ -358,18 +359,7 @@ export type UsersEndpoints = { }; '/v1/users.updateOwnBasicInfo': { - POST: (params: { - data: { - email?: string; - name?: string; - username?: string; - nickname?: string; - statusText?: string; - newPassword?: string; - currentPassword?: string; - }; - customFields?: Record; - }) => { + POST: (params: UsersUpdateOwnBasicInfoParamsPOST) => { user: IUser; }; }; diff --git a/packages/rest-typings/src/v1/users/UsersUpdateOwnBasicInfoParamsPOST.ts b/packages/rest-typings/src/v1/users/UsersUpdateOwnBasicInfoParamsPOST.ts index cff6fee56bfa..13c3066e4767 100644 --- a/packages/rest-typings/src/v1/users/UsersUpdateOwnBasicInfoParamsPOST.ts +++ b/packages/rest-typings/src/v1/users/UsersUpdateOwnBasicInfoParamsPOST.ts @@ -10,7 +10,9 @@ export type UsersUpdateOwnBasicInfoParamsPOST = { name?: string; username?: string; nickname?: string; + bio?: string; statusText?: string; + statusType?: string; currentPassword?: string; newPassword?: string; }; @@ -39,6 +41,14 @@ const UsersUpdateOwnBasicInfoParamsPostSchema = { type: 'string', nullable: true, }, + bio: { + type: 'string', + nullable: true, + }, + statusType: { + type: 'string', + nullable: true, + }, statusText: { type: 'string', nullable: true, diff --git a/yarn.lock b/yarn.lock index 36f1aba8d8ae..c598977509bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7928,6 +7928,19 @@ __metadata: languageName: node linkType: hard +"@rocket.chat/css-in-js@npm:~0.31.26-dev.23": + version: 0.31.26-dev.23 + resolution: "@rocket.chat/css-in-js@npm:0.31.26-dev.23" + dependencies: + "@emotion/hash": ^0.9.0 + "@rocket.chat/css-supports": ~0.31.26-dev.23 + "@rocket.chat/memo": ~0.31.26-dev.23 + "@rocket.chat/stylis-logical-props-middleware": ~0.31.26-dev.23 + stylis: ~4.1.3 + checksum: 6d71bd0f232c8ea3fc2711347064ddd14925b1c2b8713f6d7649b98679455029a53ee41d08b98d010da3ea4789afa21a15901a92efef61dee7b32d6965157445 + languageName: node + linkType: hard + "@rocket.chat/css-supports@npm:~0.31.26-dev.19": version: 0.31.26-dev.19 resolution: "@rocket.chat/css-supports@npm:0.31.26-dev.19" @@ -7937,6 +7950,15 @@ __metadata: languageName: node linkType: hard +"@rocket.chat/css-supports@npm:~0.31.26-dev.23": + version: 0.31.26-dev.23 + resolution: "@rocket.chat/css-supports@npm:0.31.26-dev.23" + dependencies: + "@rocket.chat/memo": ~0.31.26-dev.23 + checksum: a4f25562df67214b1c92c85a1cd16eb03fc2aea385f48cdde42ad0053b9e03a92ca9e3486d1387c7a31cf68f47fa888825f31acae8f4700ee2b9f03495286a12 + languageName: node + linkType: hard + "@rocket.chat/ddp-client@workspace:^, @rocket.chat/ddp-client@workspace:ee/packages/ddp-client": version: 0.0.0-use.local resolution: "@rocket.chat/ddp-client@workspace:ee/packages/ddp-client" @@ -8142,13 +8164,20 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/fuselage-tokens@npm:next, @rocket.chat/fuselage-tokens@npm:~0.32.0-dev.379": +"@rocket.chat/fuselage-tokens@npm:next": version: 0.32.0-dev.379 resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.379" checksum: c5cf40295c4ae1a5918651b9e156629d6400d5823b8cf5f81a14c66da986a9302d79392b45e991c2fc37aad9633f3d8e2f7f29c68969592340b05968265244e6 languageName: node linkType: hard +"@rocket.chat/fuselage-tokens@npm:~0.32.0-dev.383": + version: 0.32.0-dev.383 + resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.383" + checksum: bd3504fa6a7ce4ed6fc91246c4c8a4e3e3da8bc5e2c5590e7f913bc1fd6f08896aa4a6c4b1d01dccf78267ade9ad5a831c788cb17a4eb744deefb45032a34894 + languageName: node + linkType: hard + "@rocket.chat/fuselage-ui-kit@workspace:^, @rocket.chat/fuselage-ui-kit@workspace:packages/fuselage-ui-kit, @rocket.chat/fuselage-ui-kit@workspace:~": version: 0.0.0-use.local resolution: "@rocket.chat/fuselage-ui-kit@workspace:packages/fuselage-ui-kit" @@ -8195,9 +8224,9 @@ __metadata: "@rocket.chat/icons": "*" "@rocket.chat/prettier-config": "*" "@rocket.chat/styled": "*" - "@rocket.chat/ui-contexts": 1.0.3 + "@rocket.chat/ui-contexts": 1.0.4 "@rocket.chat/ui-kit": "*" - "@rocket.chat/ui-video-conf": 1.0.3 + "@rocket.chat/ui-video-conf": 1.0.4 "@tanstack/react-query": "*" react: "*" react-dom: "*" @@ -8205,14 +8234,14 @@ __metadata: linkType: soft "@rocket.chat/fuselage@npm:next": - version: 0.32.0-dev.429 - resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.429" - dependencies: - "@rocket.chat/css-in-js": ~0.31.26-dev.19 - "@rocket.chat/css-supports": ~0.31.26-dev.19 - "@rocket.chat/fuselage-tokens": ~0.32.0-dev.379 - "@rocket.chat/memo": ~0.31.26-dev.19 - "@rocket.chat/styled": ~0.31.26-dev.19 + version: 0.32.0-dev.433 + resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.433" + dependencies: + "@rocket.chat/css-in-js": ~0.31.26-dev.23 + "@rocket.chat/css-supports": ~0.31.26-dev.23 + "@rocket.chat/fuselage-tokens": ~0.32.0-dev.383 + "@rocket.chat/memo": ~0.31.26-dev.23 + "@rocket.chat/styled": ~0.31.26-dev.23 invariant: ^2.2.4 react-aria: ~3.23.1 react-keyed-flatten-children: ^1.3.0 @@ -8224,7 +8253,7 @@ __metadata: react: ^17.0.2 react-dom: ^17.0.2 react-virtuoso: 1.2.4 - checksum: 13ea95dea15c3677f82ffeb50780bc3479512cba6e226080bf464cf876794ed267db3419c45f63ddeaaff6a3401426ca4722e23e0f3586ca4f8eb2e6e25a7a70 + checksum: 2696da3e5cdf9d21c9c96ba069a3ef44b946ce832796ed10047666da780ee3ae88d679f3c3222fe6c6d88b1bdc0b7dab97c83d0acfbb67ca330a14b6e4739aa9 languageName: node linkType: hard @@ -8279,7 +8308,7 @@ __metadata: ts-jest: ~29.0.5 typescript: ~5.2.2 peerDependencies: - "@rocket.chat/core-typings": 6.3.3 + "@rocket.chat/core-typings": 6.3.4 "@rocket.chat/css-in-js": "*" "@rocket.chat/fuselage": "*" "@rocket.chat/fuselage-tokens": "*" @@ -8482,6 +8511,13 @@ __metadata: languageName: node linkType: hard +"@rocket.chat/memo@npm:~0.31.26-dev.23": + version: 0.31.26-dev.23 + resolution: "@rocket.chat/memo@npm:0.31.26-dev.23" + checksum: 68301161d87ba25347f1d2ab85c139ba86c5fdd1101f41678808c19ba461772814f4bff048a30e4aefd08978fe2feb952c541bddc0beb6bc3cd190bd7852393b + languageName: node + linkType: hard + "@rocket.chat/message-parser@npm:next": version: 0.32.0-dev.377 resolution: "@rocket.chat/message-parser@npm:0.32.0-dev.377" @@ -9312,6 +9348,15 @@ __metadata: languageName: node linkType: hard +"@rocket.chat/styled@npm:~0.31.26-dev.23": + version: 0.31.26-dev.23 + resolution: "@rocket.chat/styled@npm:0.31.26-dev.23" + dependencies: + "@rocket.chat/css-in-js": ~0.31.26-dev.23 + checksum: 0a1ff89b068f011097671c617844856b91f2477c16ff3771fcfc0bab62a905a9b21c7b79549ff028613700a72685fd591ba9cbeda6b5d3bd8becd3af7aef0498 + languageName: node + linkType: hard + "@rocket.chat/stylis-logical-props-middleware@npm:~0.31.26-dev.19": version: 0.31.26-dev.19 resolution: "@rocket.chat/stylis-logical-props-middleware@npm:0.31.26-dev.19" @@ -9323,6 +9368,17 @@ __metadata: languageName: node linkType: hard +"@rocket.chat/stylis-logical-props-middleware@npm:~0.31.26-dev.23": + version: 0.31.26-dev.23 + resolution: "@rocket.chat/stylis-logical-props-middleware@npm:0.31.26-dev.23" + dependencies: + "@rocket.chat/css-supports": ~0.31.26-dev.23 + peerDependencies: + stylis: 4.0.10 + checksum: b2fbfad3b2f4dedd9023b30d4cdc51e76ae76faeeca5819cf697e896c02fd4bb2dde5bbc428b377d77f32011fd8cc82c6d98a84d66b93056ef981c13aee1dc67 + languageName: node + linkType: hard + "@rocket.chat/tools@workspace:^, @rocket.chat/tools@workspace:packages/tools": version: 0.0.0-use.local resolution: "@rocket.chat/tools@workspace:packages/tools" @@ -9382,7 +9438,7 @@ __metadata: "@rocket.chat/fuselage": "*" "@rocket.chat/fuselage-hooks": "*" "@rocket.chat/icons": "*" - "@rocket.chat/ui-contexts": 1.0.3 + "@rocket.chat/ui-contexts": 1.0.4 react: ~17.0.2 languageName: unknown linkType: soft @@ -9534,7 +9590,7 @@ __metadata: "@rocket.chat/fuselage-hooks": "*" "@rocket.chat/icons": "*" "@rocket.chat/styled": "*" - "@rocket.chat/ui-contexts": 1.0.3 + "@rocket.chat/ui-contexts": 1.0.4 react: ^17.0.2 react-dom: ^17.0.2 languageName: unknown @@ -9618,7 +9674,7 @@ __metadata: typescript: ~5.2.2 peerDependencies: "@rocket.chat/layout": "*" - "@rocket.chat/ui-contexts": 1.0.3 + "@rocket.chat/ui-contexts": 1.0.4 "@tanstack/react-query": "*" react: "*" react-hook-form: "*"