From 48736451382ad38ab4dd9394127a87e7853a3a08 Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Sat, 30 Apr 2022 03:25:36 +0530 Subject: [PATCH 01/11] WIP: Convert admin -> rooms to TS --- .../client/lib/rooms/roomCoordinator.ts | 4 +- .../admin/rooms/{EditRoom.js => EditRoom.tsx} | 17 ++- ...omContextBar.js => EditRoomContextBar.tsx} | 6 +- ...itRoomWithData.js => EditRoomWithData.tsx} | 17 +-- ...TypeAndText.js => FilterByTypeAndText.tsx} | 22 ++-- .../client/views/admin/rooms/RoomsPage.js | 75 ------------ .../client/views/admin/rooms/RoomsPage.tsx | 46 ++++++++ .../rooms/{RoomsRoute.js => RoomsRoute.tsx} | 6 +- .../rooms/{RoomsTable.js => RoomsTable.tsx} | 109 ++++++++++++++---- apps/meteor/definition/IRoomTypeConfig.ts | 4 +- .../rocketchat-i18n/i18n/en.i18n.json | 3 + 11 files changed, 176 insertions(+), 133 deletions(-) rename apps/meteor/client/views/admin/rooms/{EditRoom.js => EditRoom.tsx} (95%) rename apps/meteor/client/views/admin/rooms/{EditRoomContextBar.js => EditRoomContextBar.tsx} (71%) rename apps/meteor/client/views/admin/rooms/{EditRoomWithData.js => EditRoomWithData.tsx} (62%) rename apps/meteor/client/views/admin/rooms/{FilterByTypeAndText.js => FilterByTypeAndText.tsx} (72%) delete mode 100644 apps/meteor/client/views/admin/rooms/RoomsPage.js create mode 100644 apps/meteor/client/views/admin/rooms/RoomsPage.tsx rename apps/meteor/client/views/admin/rooms/{RoomsRoute.js => RoomsRoute.tsx} (81%) rename apps/meteor/client/views/admin/rooms/{RoomsTable.js => RoomsTable.tsx} (60%) diff --git a/apps/meteor/client/lib/rooms/roomCoordinator.ts b/apps/meteor/client/lib/rooms/roomCoordinator.ts index 96297baf069f..874c94dc783f 100644 --- a/apps/meteor/client/lib/rooms/roomCoordinator.ts +++ b/apps/meteor/client/lib/rooms/roomCoordinator.ts @@ -38,7 +38,7 @@ class RoomCoordinatorClient extends RoomCoordinator { getAvatarPath(_room): string { return ''; }, - getIcon(_room: Partial): string | undefined { + getIcon(_room: Partial): IRoomTypeConfig['icon'] { return this.config.icon; }, getUserStatus(_roomId: string): string | undefined { @@ -92,7 +92,7 @@ class RoomCoordinatorClient extends RoomCoordinator { openRoom(type, name, render); } - getIcon(room: Partial): string | undefined { + getIcon(room: Partial): IRoomTypeConfig['icon'] | undefined { return room?.t && this.getRoomDirectives(room.t)?.getIcon(room); } diff --git a/apps/meteor/client/views/admin/rooms/EditRoom.js b/apps/meteor/client/views/admin/rooms/EditRoom.tsx similarity index 95% rename from apps/meteor/client/views/admin/rooms/EditRoom.js rename to apps/meteor/client/views/admin/rooms/EditRoom.tsx index f12441449902..31953683b944 100644 --- a/apps/meteor/client/views/admin/rooms/EditRoom.js +++ b/apps/meteor/client/views/admin/rooms/EditRoom.tsx @@ -1,6 +1,7 @@ +import { IRoom, Serialized } from '@rocket.chat/core-typings'; import { Box, Button, ButtonGroup, TextInput, Field, ToggleSwitch, Icon, TextAreaInput } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import React, { useState, useMemo } from 'react'; +import React, { useState, useMemo, ReactElement } from 'react'; import { RoomSettingsEnum } from '../../../../definition/IRoomTypeConfig'; import GenericModal from '../../../components/GenericModal'; @@ -17,8 +18,14 @@ import { useForm } from '../../../hooks/useForm'; import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; import DeleteTeamModal from '../../teams/contextualBar/info/Delete/DeleteTeamModal'; -const getInitialValues = (room) => ({ - roomName: room.t === 'd' ? room.usernames.join(' x ') : roomCoordinator.getRoomName(room.t, { type: room.t, ...room }), +type EditRoomProps = { + room: Serialized; + onChange: () => void; + onDelete: () => void; +}; + +const getInitialValues = (room: IRoom) => ({ + roomName: room.t === 'd' ? room.usernames?.join(' x ') : roomCoordinator.getRoomName(room.t, { type: room.t, ...room }), roomType: room.t, readOnly: !!room.ro, archived: !!room.archived, @@ -31,7 +38,7 @@ const getInitialValues = (room) => ({ roomAvatar: undefined, }); -function EditRoom({ room, onChange, onDelete }) { +const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => { const t = useTranslation(); const [deleting, setDeleting] = useState(false); @@ -301,6 +308,6 @@ function EditRoom({ room, onChange, onDelete }) { ); -} +}; export default EditRoom; diff --git a/apps/meteor/client/views/admin/rooms/EditRoomContextBar.js b/apps/meteor/client/views/admin/rooms/EditRoomContextBar.tsx similarity index 71% rename from apps/meteor/client/views/admin/rooms/EditRoomContextBar.js rename to apps/meteor/client/views/admin/rooms/EditRoomContextBar.tsx index 5bda6d978f76..3c5a9e5ea5a8 100644 --- a/apps/meteor/client/views/admin/rooms/EditRoomContextBar.js +++ b/apps/meteor/client/views/admin/rooms/EditRoomContextBar.tsx @@ -1,12 +1,12 @@ -import React from 'react'; +import React, { ReactElement } from 'react'; import { usePermission } from '../../../contexts/AuthorizationContext'; import NotAuthorizedPage from '../../notAuthorized/NotAuthorizedPage'; import EditRoomWithData from './EditRoomWithData'; -function EditRoomContextBar({ rid, onReload }) { +const EditRoomContextBar = ({ rid, onReload }: { rid: string | undefined; onReload: () => void }): ReactElement => { const canViewRoomAdministration = usePermission('view-room-administration'); return canViewRoomAdministration ? : ; -} +}; export default EditRoomContextBar; diff --git a/apps/meteor/client/views/admin/rooms/EditRoomWithData.js b/apps/meteor/client/views/admin/rooms/EditRoomWithData.tsx similarity index 62% rename from apps/meteor/client/views/admin/rooms/EditRoomWithData.js rename to apps/meteor/client/views/admin/rooms/EditRoomWithData.tsx index cedf9bbdbe43..5c95b314980a 100644 --- a/apps/meteor/client/views/admin/rooms/EditRoomWithData.js +++ b/apps/meteor/client/views/admin/rooms/EditRoomWithData.tsx @@ -1,13 +1,14 @@ +import { IRoom, Serialized } from '@rocket.chat/core-typings'; import { Box, Skeleton } from '@rocket.chat/fuselage'; -import React, { useMemo } from 'react'; +import React, { useMemo, FC } from 'react'; import { AsyncStatePhase } from '../../../hooks/useAsyncState'; import { useEndpointData } from '../../../hooks/useEndpointData'; import EditRoom from './EditRoom'; -function EditRoomWithData({ rid, onReload }) { +const EditRoomWithData: FC<{ rid?: string; onReload: () => void }> = ({ rid, onReload }) => { const { - value: data = {}, + value: data, phase: state, error, reload, @@ -30,19 +31,19 @@ function EditRoomWithData({ rid, onReload }) { } if (state === AsyncStatePhase.REJECTED) { - return error.message; + return <>{error?.message}; } - const handleChange = () => { + const handleChange = (): void => { reload(); onReload(); }; - const handleDelete = () => { + const handleDelete = (): void => { onReload(); }; - return ; -} + return data ? } onChange={handleChange} onDelete={handleDelete} /> : null; +}; export default EditRoomWithData; diff --git a/apps/meteor/client/views/admin/rooms/FilterByTypeAndText.js b/apps/meteor/client/views/admin/rooms/FilterByTypeAndText.tsx similarity index 72% rename from apps/meteor/client/views/admin/rooms/FilterByTypeAndText.js rename to apps/meteor/client/views/admin/rooms/FilterByTypeAndText.tsx index 66d397558dd6..f36a48b11cf2 100644 --- a/apps/meteor/client/views/admin/rooms/FilterByTypeAndText.js +++ b/apps/meteor/client/views/admin/rooms/FilterByTypeAndText.tsx @@ -1,6 +1,6 @@ import { Box, Icon, TextInput, Field, CheckBox, Margins } from '@rocket.chat/fuselage'; import { useUniqueId } from '@rocket.chat/fuselage-hooks'; -import React, { useCallback, useState, useEffect } from 'react'; +import React, { useCallback, useState, useEffect, ReactElement, Dispatch, SetStateAction } from 'react'; import { useTranslation } from '../../../contexts/TranslationContext'; @@ -15,7 +15,7 @@ export const roomTypeI18nMap = { team: 'Team', }; -const FilterByTypeAndText = ({ setFilter, ...props }) => { +const FilterByTypeAndText = ({ setFilter, ...props }: { setFilter?: Dispatch> }): ReactElement => { const [text, setText] = useState(''); const [types, setTypes] = useState({ d: false, @@ -29,16 +29,16 @@ const FilterByTypeAndText = ({ setFilter, ...props }) => { const t = useTranslation(); const handleChange = useCallback((event) => setText(event.currentTarget.value), []); - const handleCheckBox = useCallback((type) => setTypes({ ...types, [type]: !types[type] }), [types]); + const handleCheckBox = useCallback((type: keyof typeof types) => setTypes({ ...types, [type]: !types[type] }), [types]); useEffect(() => { if (Object.values(types).filter(Boolean).length === 0) { - return setFilter({ text, types: DEFAULT_TYPES }); + return setFilter?.({ text, types: DEFAULT_TYPES }); } const _types = Object.entries(types) .filter(([, value]) => Boolean(value)) .map(([key]) => key); - setFilter({ text, types: _types }); + setFilter?.({ text, types: _types }); }, [setFilter, text, types]); const idDirect = useUniqueId(); @@ -61,27 +61,27 @@ const FilterByTypeAndText = ({ setFilter, ...props }) => { - handleCheckBox('d')} /> + handleCheckBox('d')} /> {t('Direct')} - handleCheckBox('c')} /> + handleCheckBox('c')} /> {t('Public')} - handleCheckBox('p')} /> + handleCheckBox('p')} /> {t('Private')} - handleCheckBox('l')} /> + handleCheckBox('l')} /> {t('Omnichannel')} - handleCheckBox('discussions')} /> + handleCheckBox('discussions')} /> {t('Discussions')} - handleCheckBox('teams')} /> + handleCheckBox('teams')} /> {t('Teams')} diff --git a/apps/meteor/client/views/admin/rooms/RoomsPage.js b/apps/meteor/client/views/admin/rooms/RoomsPage.js deleted file mode 100644 index 179417e8c842..000000000000 --- a/apps/meteor/client/views/admin/rooms/RoomsPage.js +++ /dev/null @@ -1,75 +0,0 @@ -import { useDebouncedValue } from '@rocket.chat/fuselage-hooks'; -import React, { useState, useMemo } from 'react'; - -import Page from '../../../components/Page'; -import VerticalBar from '../../../components/VerticalBar'; -import { useRouteParameter, useRoute } from '../../../contexts/RouterContext'; -import { useTranslation } from '../../../contexts/TranslationContext'; -import { useEndpointData } from '../../../hooks/useEndpointData'; -import EditRoomContextBar from './EditRoomContextBar'; -import RoomsTable from './RoomsTable'; - -export const DEFAULT_TYPES = ['d', 'p', 'c', 'teams']; - -const useQuery = ({ text, types, itemsPerPage, current }, [column, direction]) => - useMemo( - () => ({ - filter: text || '', - types, - sort: JSON.stringify({ [column]: direction === 'asc' ? 1 : -1 }), - ...(itemsPerPage && { count: itemsPerPage }), - ...(current && { offset: current }), - }), - [text, types, itemsPerPage, current, column, direction], - ); - -export function RoomsPage() { - const t = useTranslation(); - - const context = useRouteParameter('context'); - const id = useRouteParameter('id'); - - const roomsRoute = useRoute('admin-rooms'); - - const handleVerticalBarCloseButtonClick = () => { - roomsRoute.push({}); - }; - - const [params, setParams] = useState({ - text: '', - types: DEFAULT_TYPES, - current: 0, - itemsPerPage: 25, - }); - const [sort, setSort] = useState(['name', 'asc']); - - const debouncedParams = useDebouncedValue(params, 500); - const debouncedSort = useDebouncedValue(sort, 500); - - const query = useQuery(debouncedParams, debouncedSort); - - const endpointData = useEndpointData('rooms.adminRooms', query); - - return ( - - - - - - - - {context && ( - - - {t('Room_Info')} - - - - - - )} - - ); -} - -export default RoomsPage; diff --git a/apps/meteor/client/views/admin/rooms/RoomsPage.tsx b/apps/meteor/client/views/admin/rooms/RoomsPage.tsx new file mode 100644 index 000000000000..07983fbed33f --- /dev/null +++ b/apps/meteor/client/views/admin/rooms/RoomsPage.tsx @@ -0,0 +1,46 @@ +import React, { useRef, ReactElement } from 'react'; + +import Page from '../../../components/Page'; +import VerticalBar from '../../../components/VerticalBar'; +import { useRouteParameter, useRoute } from '../../../contexts/RouterContext'; +import { useTranslation } from '../../../contexts/TranslationContext'; +import EditRoomContextBar from './EditRoomContextBar'; +import RoomsTable from './RoomsTable'; + +const RoomsPage = (): ReactElement => { + const t = useTranslation(); + + const context = useRouteParameter('context'); + const id = useRouteParameter('id'); + + const roomsRoute = useRoute('admin-rooms'); + + const handleVerticalBarCloseButtonClick = (): void => { + roomsRoute.push({}); + }; + + const reloadRef = useRef(() => null); + + return ( + + + + + + + + {context && ( + + + {t('Room_Info')} + + + + + + )} + + ); +}; + +export default RoomsPage; diff --git a/apps/meteor/client/views/admin/rooms/RoomsRoute.js b/apps/meteor/client/views/admin/rooms/RoomsRoute.tsx similarity index 81% rename from apps/meteor/client/views/admin/rooms/RoomsRoute.js rename to apps/meteor/client/views/admin/rooms/RoomsRoute.tsx index 953babf0be4b..830740421ddd 100644 --- a/apps/meteor/client/views/admin/rooms/RoomsRoute.js +++ b/apps/meteor/client/views/admin/rooms/RoomsRoute.tsx @@ -1,10 +1,10 @@ -import React from 'react'; +import React, { ReactElement } from 'react'; import { usePermission } from '../../../contexts/AuthorizationContext'; import NotAuthorizedPage from '../../notAuthorized/NotAuthorizedPage'; import RoomsPage from './RoomsPage'; -function RoomsRoute() { +const RoomsRoute = (): ReactElement => { const canViewRoomAdministration = usePermission('view-room-administration'); if (!canViewRoomAdministration) { @@ -12,6 +12,6 @@ function RoomsRoute() { } return ; -} +}; export default RoomsRoute; diff --git a/apps/meteor/client/views/admin/rooms/RoomsTable.js b/apps/meteor/client/views/admin/rooms/RoomsTable.tsx similarity index 60% rename from apps/meteor/client/views/admin/rooms/RoomsTable.js rename to apps/meteor/client/views/admin/rooms/RoomsTable.tsx index a75f0a2521db..0710c36b597f 100644 --- a/apps/meteor/client/views/admin/rooms/RoomsTable.js +++ b/apps/meteor/client/views/admin/rooms/RoomsTable.tsx @@ -1,16 +1,58 @@ +import { IRoom } from '@rocket.chat/core-typings'; import { Box, Table, Icon } from '@rocket.chat/fuselage'; -import { useMediaQuery } from '@rocket.chat/fuselage-hooks'; -import React, { useMemo, useCallback } from 'react'; +import { useMediaQuery, useDebouncedValue } from '@rocket.chat/fuselage-hooks'; +import React, { useState, useEffect, useMemo, useCallback, CSSProperties, ReactElement, MutableRefObject } from 'react'; import GenericTable from '../../../components/GenericTable'; import RoomAvatar from '../../../components/avatar/RoomAvatar'; import { useRoute } from '../../../contexts/RouterContext'; import { useTranslation } from '../../../contexts/TranslationContext'; +import { useEndpointData } from '../../../hooks/useEndpointData'; import { AsyncStatePhase } from '../../../lib/asyncState'; import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; import FilterByTypeAndText from './FilterByTypeAndText'; -const style = { whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }; +type RoomParamsType = { + text?: string; + types?: string[]; + current?: number; + itemsPerPage?: 25 | 50 | 100; +}; + +const style: CSSProperties = { whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }; + +export const DEFAULT_TYPES = ['d', 'p', 'c', 'teams']; + +const useQuery = ( + { + text, + types, + itemsPerPage, + current, + }: { + text?: string; + types?: string[]; + itemsPerPage?: 25 | 50 | 100; + current?: number; + }, + [column, direction]: [string, 'asc' | 'desc'], +): { + filter: string; + types: string[]; + sort: string; + count?: number; + offset?: number; +} => + useMemo( + () => ({ + filter: text || '', + types: types || [], + sort: JSON.stringify({ [column]: direction === 'asc' ? 1 : -1 }), + ...(itemsPerPage && { count: itemsPerPage }), + ...(current && { offset: current }), + }), + [text, types, itemsPerPage, current, column, direction], + ); export const roomTypeI18nMap = { l: 'Omnichannel', @@ -18,18 +60,19 @@ export const roomTypeI18nMap = { d: 'Direct', p: 'Group', discussion: 'Discussion', -}; +} as const; -const getRoomType = (room) => { +const getRoomType = (room: IRoom): typeof roomTypeI18nMap[keyof typeof roomTypeI18nMap] | 'Teams_Public_Team' | 'Teams_Private_Team' => { if (room.teamMain) { return room.t === 'c' ? 'Teams_Public_Team' : 'Teams_Private_Team'; } - return roomTypeI18nMap[room.t]; + return roomTypeI18nMap[room.t as keyof typeof roomTypeI18nMap]; }; -const getRoomDisplayName = (room) => (room.t === 'd' ? room.usernames.join(' x ') : roomCoordinator.getRoomName(room.t, room)); +const getRoomDisplayName = (room: IRoom): string | undefined => + room.t === 'd' ? room.usernames?.join(' x ') : roomCoordinator.getRoomName(room.t, room); -const useDisplayData = (asyncState, sort) => +const useDisplayData = (asyncState: any, sort: [string, 'asc' | 'desc']): IRoom[] => useMemo(() => { const { value = {}, phase } = asyncState; @@ -38,9 +81,9 @@ const useDisplayData = (asyncState, sort) => } if (sort[0] === 'name' && value.rooms) { - return value.rooms.sort((a, b) => { - const aName = getRoomDisplayName(a); - const bName = getRoomDisplayName(b); + return value.rooms.sort((a: IRoom, b: IRoom) => { + const aName = getRoomDisplayName(a) || ''; + const bName = getRoomDisplayName(b) || ''; if (aName === bName) { return 0; } @@ -51,19 +94,37 @@ const useDisplayData = (asyncState, sort) => return value.rooms; }, [asyncState, sort]); -function RoomsTable({ endpointData, params, onChangeParams, sort, onChangeSort }) { +const RoomsTable = ({ reload }: { reload: MutableRefObject<() => void> }): ReactElement => { const t = useTranslation(); - const mediaQuery = useMediaQuery('(min-width: 1024px)'); const routeName = 'admin-rooms'; - const { value: data = {} } = endpointData; + const [params, setParams] = useState({ + text: '', + types: DEFAULT_TYPES, + current: 0, + itemsPerPage: 25, + }); + const [sort, setSort] = useState<[string, 'asc' | 'desc']>(['name', 'asc']); + + const debouncedParams = useDebouncedValue(params, 500); + const debouncedSort = useDebouncedValue(sort, 500); + + const query = useQuery(debouncedParams, debouncedSort); + + const endpointData = useEndpointData('rooms.adminRooms', query); + + const { value: data, reload: reloadEndPoint } = endpointData; + + useEffect(() => { + reload.current = reloadEndPoint; + }, [reload, reloadEndPoint]); const router = useRoute(routeName); const onClick = useCallback( - (rid) => () => + (rid) => (): void => router.push({ context: 'edit', id: rid, @@ -76,12 +137,12 @@ function RoomsTable({ endpointData, params, onChangeParams, sort, onChangeSort } const [sortBy, sortDirection] = sort; if (sortBy === id) { - onChangeSort([id, sortDirection === 'asc' ? 'desc' : 'asc']); + setSort([id, sortDirection === 'asc' ? 'desc' : 'asc']); return; } - onChangeSort([id, 'asc']); + setSort([id, 'asc']); }, - [sort, onChangeSort], + [sort, setSort], ); const displayData = useDisplayData(endpointData, sort); @@ -147,7 +208,7 @@ function RoomsTable({ endpointData, params, onChangeParams, sort, onChangeSort } const renderRow = useCallback( (room) => { - const { _id, name, t: type, usersCount, msgs, default: isDefault, featured, usernames, ...args } = room; + const { _id, t: type, usersCount, msgs, default: isDefault, featured, ...args } = room; const icon = roomCoordinator.getIcon(room); const roomName = getRoomDisplayName(room); @@ -158,7 +219,7 @@ function RoomsTable({ endpointData, params, onChangeParams, sort, onChangeSort } - + {icon && } {roomName} @@ -187,12 +248,12 @@ function RoomsTable({ endpointData, params, onChangeParams, sort, onChangeSort } header={header} renderRow={renderRow} results={displayData} - total={data.total} - setParams={onChangeParams} + total={data?.total} params={params} - renderFilter={({ onChange, ...props }) => } + setParams={setParams} + renderFilter={({ onChange, ...props }): ReactElement => } /> ); -} +}; export default RoomsTable; diff --git a/apps/meteor/definition/IRoomTypeConfig.ts b/apps/meteor/definition/IRoomTypeConfig.ts index 4201c38c3627..12badf3852fc 100644 --- a/apps/meteor/definition/IRoomTypeConfig.ts +++ b/apps/meteor/definition/IRoomTypeConfig.ts @@ -48,7 +48,7 @@ export const UiTextContext = { export interface IRoomTypeConfig { identifier: string; order: number; - icon?: string; + icon: 'hash' | 'hashtag' | 'hashtag-lock' | 'at' | 'omnichannel'; header?: string; label?: string; route?: IRoomTypeRouteConfig; @@ -70,7 +70,7 @@ export interface IRoomTypeClientDirectives { getAvatarPath: ( room: AtLeast & { username?: IRoom['_id'] }, ) => string; - getIcon: (room: Partial) => string | undefined; + getIcon: (room: Partial) => IRoomTypeConfig['icon']; getUserStatus: (roomId: string) => string | undefined; findRoom: (identifier: string) => IRoom | undefined; showJoinLink: (roomId: string) => boolean; diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index d158d61f833a..c8b93b1c9c99 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -1441,6 +1441,7 @@ "Desktop_Notifications_Not_Enabled": "Desktop Notifications are Not Enabled", "Details": "Details", "Different_Style_For_User_Mentions": "Different style for user mentions", + "Direct": "Direct", "Direct_Message": "Direct Message", "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", @@ -1892,6 +1893,7 @@ "Favorite": "Favorite", "Favorite_Rooms": "Enable Favorite Rooms", "Favorites": "Favorites", + "Featured": "Featured", "Feature_depends_on_selected_call_provider_to_be_enabled_from_administration_settings": "This feature depends on the above selected call provider to be enabled from the administration settings.
For **Jitsi**, please make sure you have Jitsi Enabled under Admin -> Video Conference -> Jitsi -> Enabled.
For **WebRTC**, please make sure you have WebRTC enabled under Admin -> WebRTC -> Enabled.", "Feature_Depends_on_Livechat_Visitor_navigation_as_a_message_to_be_enabled": "This feature depends on \"Send Visitor Navigation History as a Message\" to be enabled.", "Feature_Limiting": "Feature Limiting", @@ -2132,6 +2134,7 @@ "Graphql_CORS": "GraphQL CORS", "Graphql_Enabled": "GraphQL Enabled", "Graphql_Subscription_Port": "GraphQL Subscription Port", + "Group": "Group", "Group_by": "Group by", "Group_by_Type": "Group by Type", "Group_discussions": "Group discussions", From e6749f0e272809d3b9da28be02227cc58bed2df3 Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Sat, 30 Apr 2022 03:27:06 +0530 Subject: [PATCH 02/11] rest typings --- packages/rest-typings/src/v1/rooms.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/rest-typings/src/v1/rooms.ts b/packages/rest-typings/src/v1/rooms.ts index 0352d63f9882..b88e43d846eb 100644 --- a/packages/rest-typings/src/v1/rooms.ts +++ b/packages/rest-typings/src/v1/rooms.ts @@ -1,5 +1,7 @@ import type { IMessage, IRoom, IUser } from '@rocket.chat/core-typings'; +import type { PaginatedResult } from '../helpers/PaginatedResult'; + export type RoomsEndpoints = { 'rooms.autocomplete.channelAndPrivate': { GET: (params: { selector: string }) => { @@ -53,4 +55,16 @@ export type RoomsEndpoints = { success: boolean; }; }; + 'rooms.adminRooms': { + GET: (params: { + filter?: string; + types?: string[]; + sort?: string; + count?: number; + offset?: number; + }) => PaginatedResult<{ rooms: IRoom[] }>; + }; + 'rooms.adminRooms.getRoom': { + GET: (params: { rid?: string }) => IRoom; + }; }; From e0c254e6b1e9c8d6dfb265bb3ede19fb46d611a0 Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Fri, 13 May 2022 20:33:40 +0530 Subject: [PATCH 03/11] add admin fields definition --- packages/core-typings/src/IRoom.ts | 31 +++++++++++++++++++++++++++ packages/rest-typings/src/v1/rooms.ts | 6 +++--- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/packages/core-typings/src/IRoom.ts b/packages/core-typings/src/IRoom.ts index 4a663543a2a7..2b1a43df0e99 100644 --- a/packages/core-typings/src/IRoom.ts +++ b/packages/core-typings/src/IRoom.ts @@ -234,3 +234,34 @@ export const isOmnichannelRoomFromAppSource = (room: IRoom): room is IOmnichanne return room.source?.type === OmnichannelSourceType.APP; }; + +export enum RoomAdminFields { + _id, + prid, + fname, + name, + t, + cl, + u, + usernames, + usersCount, + muted, + unmuted, + ro, + default, + favorite, + featured, + topic, + msgs, + archived, + tokenpass, + teamId, + teamMain, + announcement, + description, + broadcast, + uids, + avatarETag, +} + +export type RoomAdminFieldsType = keyof typeof RoomAdminFields; diff --git a/packages/rest-typings/src/v1/rooms.ts b/packages/rest-typings/src/v1/rooms.ts index b88e43d846eb..85e6e5bbcd32 100644 --- a/packages/rest-typings/src/v1/rooms.ts +++ b/packages/rest-typings/src/v1/rooms.ts @@ -1,4 +1,4 @@ -import type { IMessage, IRoom, IUser } from '@rocket.chat/core-typings'; +import type { IMessage, IRoom, IUser, RoomAdminFieldsType } from '@rocket.chat/core-typings'; import type { PaginatedResult } from '../helpers/PaginatedResult'; @@ -62,9 +62,9 @@ export type RoomsEndpoints = { sort?: string; count?: number; offset?: number; - }) => PaginatedResult<{ rooms: IRoom[] }>; + }) => PaginatedResult<{ rooms: Pick[] }>; }; 'rooms.adminRooms.getRoom': { - GET: (params: { rid?: string }) => IRoom; + GET: (params: { rid?: string }) => Pick; }; }; From 067b6133028216f56966d6fce35477214501ba75 Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Fri, 13 May 2022 21:38:00 +0530 Subject: [PATCH 04/11] some more API typings --- packages/rest-typings/src/v1/rooms.ts | 29 +++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/packages/rest-typings/src/v1/rooms.ts b/packages/rest-typings/src/v1/rooms.ts index 85e6e5bbcd32..c216253c3ddb 100644 --- a/packages/rest-typings/src/v1/rooms.ts +++ b/packages/rest-typings/src/v1/rooms.ts @@ -67,4 +67,33 @@ export type RoomsEndpoints = { 'rooms.adminRooms.getRoom': { GET: (params: { rid?: string }) => Pick; }; + 'rooms.saveRoomSettings': { + POST: (params: { + rid: string; + roomAvatar?: string; + featured?: boolean; + roomName?: string; + roomTopic?: string; + roomAnnouncement?: string; + roomDescription?: string; + roomType?: IRoom['t']; + readOnly?: boolean; + reactWhenReadOnly?: boolean; + default?: boolean; + tokenpass?: string; + encrypted?: boolean; + favorite?: { + defaultValue?: boolean; + favorite?: boolean; + }; + }) => { + success: boolean; + rid: string; + }; + }; + 'rooms.changeArchivationState': { + POST: (params: { rid: string; action?: string }) => { + success: boolean; + }; + }; }; From 19b2c02975e616927a8747297e14ed88367789b4 Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Fri, 13 May 2022 21:38:36 +0530 Subject: [PATCH 05/11] TS conversion --- .../client/components/avatar/RoomAvatar.tsx | 2 +- ...omAvatarEditor.js => RoomAvatarEditor.tsx} | 13 +++- .../client/views/admin/rooms/EditRoom.tsx | 62 ++++++++++++------- .../views/admin/rooms/EditRoomWithData.tsx | 3 +- apps/meteor/lib/rooms/adminFields.ts | 1 + .../rocketchat-i18n/i18n/en.i18n.json | 1 + 6 files changed, 53 insertions(+), 29 deletions(-) rename apps/meteor/client/components/avatar/{RoomAvatarEditor.js => RoomAvatarEditor.tsx} (80%) diff --git a/apps/meteor/client/components/avatar/RoomAvatar.tsx b/apps/meteor/client/components/avatar/RoomAvatar.tsx index d2c59721f88c..bf450b79b5a7 100644 --- a/apps/meteor/client/components/avatar/RoomAvatar.tsx +++ b/apps/meteor/client/components/avatar/RoomAvatar.tsx @@ -7,7 +7,7 @@ import BaseAvatar from './BaseAvatar'; type RoomAvatarProps = { /* @deprecated */ - size?: 'x16' | 'x20' | 'x28' | 'x36' | 'x40' | 'x124'; + size?: 'x16' | 'x20' | 'x28' | 'x36' | 'x40' | 'x124' | 'x332'; /* @deprecated */ url?: string; diff --git a/apps/meteor/client/components/avatar/RoomAvatarEditor.js b/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx similarity index 80% rename from apps/meteor/client/components/avatar/RoomAvatarEditor.js rename to apps/meteor/client/components/avatar/RoomAvatarEditor.tsx index b3105d2f4009..32ee73298940 100644 --- a/apps/meteor/client/components/avatar/RoomAvatarEditor.js +++ b/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx @@ -1,20 +1,27 @@ +import { IRoom, RoomAdminFieldsType } from '@rocket.chat/core-typings'; import { css } from '@rocket.chat/css-in-js'; import { Box, Button, ButtonGroup, Icon } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import React, { useEffect } from 'react'; +import React, { useEffect, ReactElement } from 'react'; import { getAvatarURL } from '../../../app/utils/lib/getAvatarURL'; import { useTranslation } from '../../contexts/TranslationContext'; import { useFileInput } from '../../hooks/useFileInput'; import RoomAvatar from './RoomAvatar'; -const RoomAvatarEditor = ({ room, roomAvatar, onChangeAvatar = () => {}, ...props }) => { +type RoomAvatarEditorProps = { + room: Pick; + roomAvatar: IRoom['avatarETag']; + onChangeAvatar: (e: FileReader['result']) => void; +}; + +const RoomAvatarEditor = ({ room, roomAvatar, onChangeAvatar, ...props }: RoomAvatarEditorProps): ReactElement => { const t = useTranslation(); const handleChangeAvatar = useMutableCallback((file) => { const reader = new FileReader(); reader.readAsDataURL(file); - reader.onloadend = () => { + reader.onloadend = (): void => { onChangeAvatar(reader.result); }; }); diff --git a/apps/meteor/client/views/admin/rooms/EditRoom.tsx b/apps/meteor/client/views/admin/rooms/EditRoom.tsx index 31953683b944..ba68455dbbd2 100644 --- a/apps/meteor/client/views/admin/rooms/EditRoom.tsx +++ b/apps/meteor/client/views/admin/rooms/EditRoom.tsx @@ -1,4 +1,4 @@ -import { IRoom, Serialized } from '@rocket.chat/core-typings'; +import { IRoom, RoomAdminFieldsType } from '@rocket.chat/core-typings'; import { Box, Button, ButtonGroup, TextInput, Field, ToggleSwitch, Icon, TextAreaInput } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import React, { useState, useMemo, ReactElement } from 'react'; @@ -19,13 +19,27 @@ import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; import DeleteTeamModal from '../../teams/contextualBar/info/Delete/DeleteTeamModal'; type EditRoomProps = { - room: Serialized; + room: Pick; onChange: () => void; onDelete: () => void; }; -const getInitialValues = (room: IRoom) => ({ - roomName: room.t === 'd' ? room.usernames?.join(' x ') : roomCoordinator.getRoomName(room.t, { type: room.t, ...room }), +type EditRoomFormValues = { + roomName: IRoom['name']; + roomTopic: string; + roomType: IRoom['t']; + readOnly: boolean; + isDefault: boolean; + favorite: boolean; + featured: boolean; + roomDescription: string; + roomAnnouncement: string; + roomAvatar: IRoom['avatarETag']; + archived: boolean; +}; + +const getInitialValues = (room: Pick): EditRoomFormValues => ({ + roomName: room.t === 'd' ? room.usernames?.join(' x ') : roomCoordinator.getRoomName(room.t, room), roomType: room.t, readOnly: !!room.ro, archived: !!room.archived, @@ -45,19 +59,20 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => const setModal = useSetModal(); const dispatchToastMessage = useToastMessageDispatch(); + const { values, handlers, hasUnsavedChanges, reset } = useForm(getInitialValues(room)); const [canViewName, canViewTopic, canViewAnnouncement, canViewArchived, canViewDescription, canViewType, canViewReadOnly] = useMemo(() => { const isAllowed = roomCoordinator.getRoomDirectives(room.t)?.allowRoomSettingChange; return [ - isAllowed(room, RoomSettingsEnum.NAME), - isAllowed(room, RoomSettingsEnum.TOPIC), - isAllowed(room, RoomSettingsEnum.ANNOUNCEMENT), - isAllowed(room, RoomSettingsEnum.ARCHIVE_OR_UNARCHIVE), - isAllowed(room, RoomSettingsEnum.DESCRIPTION), - isAllowed(room, RoomSettingsEnum.TYPE), - isAllowed(room, RoomSettingsEnum.READ_ONLY), + isAllowed?.(room, RoomSettingsEnum.NAME), + isAllowed?.(room, RoomSettingsEnum.TOPIC), + isAllowed?.(room, RoomSettingsEnum.ANNOUNCEMENT), + isAllowed?.(room, RoomSettingsEnum.ARCHIVE_OR_UNARCHIVE), + isAllowed?.(room, RoomSettingsEnum.DESCRIPTION), + isAllowed?.(room, RoomSettingsEnum.TYPE), + isAllowed?.(room, RoomSettingsEnum.READ_ONLY), ]; }, [room]); @@ -73,7 +88,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => roomAvatar, roomDescription, roomAnnouncement, - } = values; + } = values as EditRoomFormValues; const { handleIsDefault, @@ -102,7 +117,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => const archiveAction = useEndpointActionExperimental('POST', 'rooms.changeArchivationState', t(archiveMessage)); const handleSave = useMutableCallback(async () => { - const save = () => + const save = (): Promise<{ success: boolean; rid: string }> => saveAction({ rid: room._id, roomName: roomType === 'd' ? undefined : roomName, @@ -117,7 +132,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => roomAvatar, }); - const archive = () => archiveAction({ rid: room._id, action: archiveSelector }); + const archive = (): Promise<{ success: boolean }> => archiveAction({ rid: room._id, action: archiveSelector }); await Promise.all([hasUnsavedChanges && save(), changeArchivation && archive()].filter(Boolean)); onChange(); @@ -134,24 +149,24 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => if (room.teamMain) { setModal( { - const roomsToRemove = Array.isArray(deletedRooms) && deletedRooms.length > 0 ? deletedRooms : []; + onConfirm={async (deletedRooms: EditRoomProps['room'][]): Promise => { + const roomsToRemove = Array.isArray(deletedRooms) && deletedRooms.length > 0 ? deletedRooms.map((room) => room._id) : []; try { setDeleting(true); setModal(null); - await deleteTeam({ teamId: room.teamId, ...(roomsToRemove.length && { roomsToRemove }) }); + await deleteTeam({ teamId: room.teamId as string, ...(roomsToRemove.length && { roomsToRemove }) }); dispatchToastMessage({ type: 'success', message: t('Team_has_been_deleted') }); roomsRoute.push({}); } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); + dispatchToastMessage({ type: 'error', message: String(error) }); setDeleting(false); } finally { onDelete(); } }} - onCancel={() => setModal(null)} - teamId={room.teamId} + onCancel={(): void => setModal(null)} + rooms={undefined} // Endpoint broken, doesn't return the list of rooms under a team />, ); @@ -161,7 +176,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => setModal( { + onConfirm={async (): Promise => { try { setDeleting(true); setModal(null); @@ -169,13 +184,14 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => dispatchToastMessage({ type: 'success', message: t('Room_has_been_deleted') }); roomsRoute.push({}); } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); + dispatchToastMessage({ type: 'error', message: String(error) }); setDeleting(false); } finally { onDelete(); } }} - onCancel={() => setModal(null)} + onClose={(): void => setModal(null)} + onCancel={(): void => setModal(null)} confirmText={t('Yes_delete_it')} > {t('Delete_Room_Warning')} diff --git a/apps/meteor/client/views/admin/rooms/EditRoomWithData.tsx b/apps/meteor/client/views/admin/rooms/EditRoomWithData.tsx index 5c95b314980a..95f822faca9b 100644 --- a/apps/meteor/client/views/admin/rooms/EditRoomWithData.tsx +++ b/apps/meteor/client/views/admin/rooms/EditRoomWithData.tsx @@ -1,4 +1,3 @@ -import { IRoom, Serialized } from '@rocket.chat/core-typings'; import { Box, Skeleton } from '@rocket.chat/fuselage'; import React, { useMemo, FC } from 'react'; @@ -43,7 +42,7 @@ const EditRoomWithData: FC<{ rid?: string; onReload: () => void }> = ({ rid, onR onReload(); }; - return data ? } onChange={handleChange} onDelete={handleDelete} /> : null; + return data ? : null; }; export default EditRoomWithData; diff --git a/apps/meteor/lib/rooms/adminFields.ts b/apps/meteor/lib/rooms/adminFields.ts index dba428695fde..3375e44eaa1d 100644 --- a/apps/meteor/lib/rooms/adminFields.ts +++ b/apps/meteor/lib/rooms/adminFields.ts @@ -1,6 +1,7 @@ import type { IRoom } from '@rocket.chat/core-typings'; export const adminFields: Partial> = { + _id: 1, prid: 1, fname: 1, name: 1, diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index c8b93b1c9c99..58997ef640f2 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -3372,6 +3372,7 @@ "Outgoing_WebHook_Description": "Get data out of Rocket.Chat in real-time.", "Output_format": "Output format", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Override URL to which files are uploaded. This url also used for downloads unless a CDN is given", + "Owner": "Owner", "Play": "Play", "Page_title": "Page title", "Page_URL": "Page URL", From 7222a04e8b61e42c9d459502763720159f781ef0 Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Sat, 14 May 2022 01:15:43 +0530 Subject: [PATCH 06/11] import of deleteModal --- .../client/views/admin/rooms/EditRoom.tsx | 8 +- .../client/views/admin/rooms/RoomsPage.js | 74 ------------------- 2 files changed, 4 insertions(+), 78 deletions(-) delete mode 100644 apps/meteor/client/views/admin/rooms/RoomsPage.js diff --git a/apps/meteor/client/views/admin/rooms/EditRoom.tsx b/apps/meteor/client/views/admin/rooms/EditRoom.tsx index 65ac743ae401..b72fd87dae11 100644 --- a/apps/meteor/client/views/admin/rooms/EditRoom.tsx +++ b/apps/meteor/client/views/admin/rooms/EditRoom.tsx @@ -19,7 +19,7 @@ import RoomAvatarEditor from '../../../components/avatar/RoomAvatarEditor'; import { useEndpointActionExperimental } from '../../../hooks/useEndpointActionExperimental'; import { useForm } from '../../../hooks/useForm'; import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; -import DeleteTeamModal from '../../teams/contextualBar/info/Delete/DeleteTeamModal'; +import DeleteTeamModalWithRooms from '../../teams/contextualBar/info/Delete/'; type EditRoomProps = { room: Pick; @@ -57,7 +57,7 @@ const getInitialValues = (room: Pick): EditRoomFormV const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => { const t = useTranslation(); - + console.log("Room = ", room) const [deleting, setDeleting] = useState(false); const setModal = useSetModal(); @@ -151,7 +151,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => const handleDelete = useMutableCallback(() => { if (room.teamMain) { setModal( - => { const roomsToRemove = Array.isArray(deletedRooms) && deletedRooms.length > 0 ? deletedRooms.map((room) => room._id) : []; @@ -169,7 +169,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => } }} onCancel={(): void => setModal(null)} - rooms={undefined} // Endpoint broken, doesn't return the list of rooms under a team + teamId={room.teamId as string} />, ); diff --git a/apps/meteor/client/views/admin/rooms/RoomsPage.js b/apps/meteor/client/views/admin/rooms/RoomsPage.js deleted file mode 100644 index ed3b62c1a80a..000000000000 --- a/apps/meteor/client/views/admin/rooms/RoomsPage.js +++ /dev/null @@ -1,74 +0,0 @@ -import { useDebouncedValue } from '@rocket.chat/fuselage-hooks'; -import { useRouteParameter, useRoute, useTranslation } from '@rocket.chat/ui-contexts'; -import React, { useState, useMemo } from 'react'; - -import Page from '../../../components/Page'; -import VerticalBar from '../../../components/VerticalBar'; -import { useEndpointData } from '../../../hooks/useEndpointData'; -import EditRoomContextBar from './EditRoomContextBar'; -import RoomsTable from './RoomsTable'; - -export const DEFAULT_TYPES = ['d', 'p', 'c', 'teams']; - -const useQuery = ({ text, types, itemsPerPage, current }, [column, direction]) => - useMemo( - () => ({ - filter: text || '', - types, - sort: JSON.stringify({ [column]: direction === 'asc' ? 1 : -1 }), - ...(itemsPerPage && { count: itemsPerPage }), - ...(current && { offset: current }), - }), - [text, types, itemsPerPage, current, column, direction], - ); - -export function RoomsPage() { - const t = useTranslation(); - - const context = useRouteParameter('context'); - const id = useRouteParameter('id'); - - const roomsRoute = useRoute('admin-rooms'); - - const handleVerticalBarCloseButtonClick = () => { - roomsRoute.push({}); - }; - - const [params, setParams] = useState({ - text: '', - types: DEFAULT_TYPES, - current: 0, - itemsPerPage: 25, - }); - const [sort, setSort] = useState(['name', 'asc']); - - const debouncedParams = useDebouncedValue(params, 500); - const debouncedSort = useDebouncedValue(sort, 500); - - const query = useQuery(debouncedParams, debouncedSort); - - const endpointData = useEndpointData('rooms.adminRooms', query); - - return ( - - - - - - - - {context && ( - - - {t('Room_Info')} - - - - - - )} - - ); -} - -export default RoomsPage; From 7c6081e51b2481e841570ef666e855e827227935 Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Sat, 14 May 2022 01:50:24 +0530 Subject: [PATCH 07/11] teams delete modal improvements --- apps/meteor/client/views/admin/rooms/EditRoom.tsx | 6 +++--- .../client/views/admin/rooms/RoomsTable.tsx | 2 +- .../contextualBar/info/Delete/DeleteTeamModal.js | 3 ++- .../info/Delete/DeleteTeamModal.stories.tsx | 4 +++- .../{index.js => DeleteTeamModalWithRooms.tsx} | 15 +++++++++------ .../teams/contextualBar/info/Delete/index.ts | 1 + 6 files changed, 19 insertions(+), 12 deletions(-) rename apps/meteor/client/views/teams/contextualBar/info/Delete/{index.js => DeleteTeamModalWithRooms.tsx} (77%) create mode 100644 apps/meteor/client/views/teams/contextualBar/info/Delete/index.ts diff --git a/apps/meteor/client/views/admin/rooms/EditRoom.tsx b/apps/meteor/client/views/admin/rooms/EditRoom.tsx index b72fd87dae11..0f476f4029b0 100644 --- a/apps/meteor/client/views/admin/rooms/EditRoom.tsx +++ b/apps/meteor/client/views/admin/rooms/EditRoom.tsx @@ -19,7 +19,7 @@ import RoomAvatarEditor from '../../../components/avatar/RoomAvatarEditor'; import { useEndpointActionExperimental } from '../../../hooks/useEndpointActionExperimental'; import { useForm } from '../../../hooks/useForm'; import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; -import DeleteTeamModalWithRooms from '../../teams/contextualBar/info/Delete/'; +import DeleteTeamModalWithRooms from '../../teams/contextualBar/info/Delete'; type EditRoomProps = { room: Pick; @@ -57,7 +57,7 @@ const getInitialValues = (room: Pick): EditRoomFormV const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => { const t = useTranslation(); - console.log("Room = ", room) + const [deleting, setDeleting] = useState(false); const setModal = useSetModal(); @@ -152,7 +152,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => if (room.teamMain) { setModal( => { + onConfirm={async (deletedRooms: IRoom[]): Promise => { const roomsToRemove = Array.isArray(deletedRooms) && deletedRooms.length > 0 ? deletedRooms.map((room) => room._id) : []; try { diff --git a/apps/meteor/client/views/admin/rooms/RoomsTable.tsx b/apps/meteor/client/views/admin/rooms/RoomsTable.tsx index 2fc5974a150d..eebd3b2e1790 100644 --- a/apps/meteor/client/views/admin/rooms/RoomsTable.tsx +++ b/apps/meteor/client/views/admin/rooms/RoomsTable.tsx @@ -4,9 +4,9 @@ import { useMediaQuery, useDebouncedValue } from '@rocket.chat/fuselage-hooks'; import { useRoute, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useState, useEffect, useMemo, useCallback, CSSProperties, ReactElement, MutableRefObject } from 'react'; -import { useEndpointData } from '../../../../client/hooks/useEndpointData'; import GenericTable from '../../../components/GenericTable'; import RoomAvatar from '../../../components/avatar/RoomAvatar'; +import { useEndpointData } from '../../../hooks/useEndpointData'; import { AsyncStatePhase } from '../../../lib/asyncState'; import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; import FilterByTypeAndText from './FilterByTypeAndText'; diff --git a/apps/meteor/client/views/teams/contextualBar/info/Delete/DeleteTeamModal.js b/apps/meteor/client/views/teams/contextualBar/info/Delete/DeleteTeamModal.js index e5b356b0a3ef..28ddbb624fa5 100644 --- a/apps/meteor/client/views/teams/contextualBar/info/Delete/DeleteTeamModal.js +++ b/apps/meteor/client/views/teams/contextualBar/info/Delete/DeleteTeamModal.js @@ -1,7 +1,8 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import React, { useState, useCallback } from 'react'; -import { StepOne, StepTwo } from '.'; +import StepOne from './StepOne'; +import StepTwo from './StepTwo'; const STEPS = { LIST_ROOMS: 'LIST_ROOMS', CONFIRM_DELETE: 'CONFIRM_DELETE' }; diff --git a/apps/meteor/client/views/teams/contextualBar/info/Delete/DeleteTeamModal.stories.tsx b/apps/meteor/client/views/teams/contextualBar/info/Delete/DeleteTeamModal.stories.tsx index 3b079ef9801a..d0911836922c 100644 --- a/apps/meteor/client/views/teams/contextualBar/info/Delete/DeleteTeamModal.stories.tsx +++ b/apps/meteor/client/views/teams/contextualBar/info/Delete/DeleteTeamModal.stories.tsx @@ -2,7 +2,9 @@ import { action } from '@storybook/addon-actions'; import { ComponentMeta, ComponentStory } from '@storybook/react'; import React from 'react'; -import DeleteTeamModal, { StepOne, StepTwo } from '.'; +import DeleteTeamModal from '.'; +import StepOne from './StepOne'; +import StepTwo from './StepTwo'; export default { title: 'Teams/Contextual Bar/DeleteTeamModal', diff --git a/apps/meteor/client/views/teams/contextualBar/info/Delete/index.js b/apps/meteor/client/views/teams/contextualBar/info/Delete/DeleteTeamModalWithRooms.tsx similarity index 77% rename from apps/meteor/client/views/teams/contextualBar/info/Delete/index.js rename to apps/meteor/client/views/teams/contextualBar/info/Delete/DeleteTeamModalWithRooms.tsx index c935ab038f66..3625da876daa 100644 --- a/apps/meteor/client/views/teams/contextualBar/info/Delete/index.js +++ b/apps/meteor/client/views/teams/contextualBar/info/Delete/DeleteTeamModalWithRooms.tsx @@ -1,15 +1,20 @@ +import { IRoom } from '@rocket.chat/core-typings'; import { Skeleton } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import React, { useMemo } from 'react'; +import React, { useMemo, ReactElement } from 'react'; import GenericModal from '../../../../../components/GenericModal'; import { AsyncStatePhase } from '../../../../../hooks/useAsyncState'; import { useEndpointData } from '../../../../../hooks/useEndpointData'; import DeleteTeamModal from './DeleteTeamModal'; -import StepOne from './StepOne'; -import StepTwo from './StepTwo'; -const DeleteTeamModalWithRooms = ({ teamId, onConfirm, onCancel }) => { +type DeleteTeamModalWithRoomsProps = { + teamId: string; + onConfirm: (rooms: IRoom[]) => void; + onCancel: () => void; +}; + +const DeleteTeamModalWithRooms = ({ teamId, onConfirm, onCancel }: DeleteTeamModalWithRoomsProps): ReactElement => { const { value, phase } = useEndpointData( 'teams.listRooms', useMemo(() => ({ teamId }), [teamId]), @@ -27,6 +32,4 @@ const DeleteTeamModalWithRooms = ({ teamId, onConfirm, onCancel }) => { return ; }; -export { StepOne, StepTwo }; - export default DeleteTeamModalWithRooms; diff --git a/apps/meteor/client/views/teams/contextualBar/info/Delete/index.ts b/apps/meteor/client/views/teams/contextualBar/info/Delete/index.ts new file mode 100644 index 000000000000..256a9f44f9a6 --- /dev/null +++ b/apps/meteor/client/views/teams/contextualBar/info/Delete/index.ts @@ -0,0 +1 @@ +export { default } from './DeleteTeamModalWithRooms'; From ed6e2d713a008d36e8c595574b49fee43e6027ff Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Sat, 14 May 2022 02:28:09 +0530 Subject: [PATCH 08/11] some type problem :( --- apps/meteor/client/lib/rooms/roomCoordinator.ts | 2 +- apps/meteor/definition/IRoomTypeConfig.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/meteor/client/lib/rooms/roomCoordinator.ts b/apps/meteor/client/lib/rooms/roomCoordinator.ts index 874c94dc783f..0a861577f50a 100644 --- a/apps/meteor/client/lib/rooms/roomCoordinator.ts +++ b/apps/meteor/client/lib/rooms/roomCoordinator.ts @@ -92,7 +92,7 @@ class RoomCoordinatorClient extends RoomCoordinator { openRoom(type, name, render); } - getIcon(room: Partial): IRoomTypeConfig['icon'] | undefined { + getIcon(room: Partial): IRoomTypeConfig['icon'] { return room?.t && this.getRoomDirectives(room.t)?.getIcon(room); } diff --git a/apps/meteor/definition/IRoomTypeConfig.ts b/apps/meteor/definition/IRoomTypeConfig.ts index 12badf3852fc..4b2689996d13 100644 --- a/apps/meteor/definition/IRoomTypeConfig.ts +++ b/apps/meteor/definition/IRoomTypeConfig.ts @@ -48,7 +48,7 @@ export const UiTextContext = { export interface IRoomTypeConfig { identifier: string; order: number; - icon: 'hash' | 'hashtag' | 'hashtag-lock' | 'at' | 'omnichannel'; + icon?: 'hash' | 'hashtag' | 'hashtag-lock' | 'at' | 'omnichannel' | 'phone' | 'star'; header?: string; label?: string; route?: IRoomTypeRouteConfig; From 5b6410e5b3ac3416002e7a5d3b3a18bfa48d3ec7 Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Mon, 16 May 2022 19:21:35 +0530 Subject: [PATCH 09/11] some TS problems --- apps/meteor/client/views/admin/rooms/EditRoom.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/meteor/client/views/admin/rooms/EditRoom.tsx b/apps/meteor/client/views/admin/rooms/EditRoom.tsx index 0f476f4029b0..029c1fbb251b 100644 --- a/apps/meteor/client/views/admin/rooms/EditRoom.tsx +++ b/apps/meteor/client/views/admin/rooms/EditRoom.tsx @@ -137,7 +137,10 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => const archive = (): Promise<{ success: boolean }> => archiveAction({ rid: room._id, action: archiveSelector }); - await Promise.all([hasUnsavedChanges && save(), changeArchivation && archive()].filter(Boolean)); + const promises = []; + hasUnsavedChanges && promises.push(save()); + changeArchivation && promises.push(archive()); + await Promise.all(promises); onChange(); }); From 54d9bb670d609a7a8534269e0687b7756db600d0 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Mon, 16 May 2022 13:10:52 -0300 Subject: [PATCH 10/11] Apply suggestions from code review --- packages/core-typings/src/IRoom.ts | 56 +++++++++++++-------------- packages/rest-typings/src/v1/rooms.ts | 8 ++-- 2 files changed, 29 insertions(+), 35 deletions(-) diff --git a/packages/core-typings/src/IRoom.ts b/packages/core-typings/src/IRoom.ts index 2b1a43df0e99..339032a293d8 100644 --- a/packages/core-typings/src/IRoom.ts +++ b/packages/core-typings/src/IRoom.ts @@ -235,33 +235,29 @@ export const isOmnichannelRoomFromAppSource = (room: IRoom): room is IOmnichanne return room.source?.type === OmnichannelSourceType.APP; }; -export enum RoomAdminFields { - _id, - prid, - fname, - name, - t, - cl, - u, - usernames, - usersCount, - muted, - unmuted, - ro, - default, - favorite, - featured, - topic, - msgs, - archived, - tokenpass, - teamId, - teamMain, - announcement, - description, - broadcast, - uids, - avatarETag, -} - -export type RoomAdminFieldsType = keyof typeof RoomAdminFields; +export type RoomAdminFieldsType = '_id' | + 'prid' | + 'fname' | + 'name' | + 't' | + 'cl' | + 'u' | + 'usernames' | + 'usersCount' | + 'muted' | + 'unmuted' | + 'ro' | + 'default' | + 'favorite' | + 'featured' | + 'topic' | + 'msgs' | + 'archived' | + 'tokenpass' | + 'teamId' | + 'teamMain' | + 'announcement' | + 'description' | + 'broadcast' | + 'uids' | + 'avatarETag'; diff --git a/packages/rest-typings/src/v1/rooms.ts b/packages/rest-typings/src/v1/rooms.ts index c216253c3ddb..9d80ce3d4dcf 100644 --- a/packages/rest-typings/src/v1/rooms.ts +++ b/packages/rest-typings/src/v1/rooms.ts @@ -1,5 +1,6 @@ import type { IMessage, IRoom, IUser, RoomAdminFieldsType } from '@rocket.chat/core-typings'; +import type { PaginatedRequest } from '../helpers/PaginatedRequest'; import type { PaginatedResult } from '../helpers/PaginatedResult'; export type RoomsEndpoints = { @@ -56,13 +57,10 @@ export type RoomsEndpoints = { }; }; 'rooms.adminRooms': { - GET: (params: { + GET: (params: PaginatedRequest<{ filter?: string; types?: string[]; - sort?: string; - count?: number; - offset?: number; - }) => PaginatedResult<{ rooms: Pick[] }>; + }>) => PaginatedResult<{ rooms: Pick[] }>; }; 'rooms.adminRooms.getRoom': { GET: (params: { rid?: string }) => Pick; From 387d9a550d96e3161709608318ad84e618510626 Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Mon, 16 May 2022 22:53:46 +0530 Subject: [PATCH 11/11] fix lint --- packages/core-typings/src/IRoom.ts | 53 ++++++++++++++------------- packages/rest-typings/src/v1/rooms.ts | 10 +++-- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/packages/core-typings/src/IRoom.ts b/packages/core-typings/src/IRoom.ts index 339032a293d8..14b3f9a0ba1a 100644 --- a/packages/core-typings/src/IRoom.ts +++ b/packages/core-typings/src/IRoom.ts @@ -235,29 +235,30 @@ export const isOmnichannelRoomFromAppSource = (room: IRoom): room is IOmnichanne return room.source?.type === OmnichannelSourceType.APP; }; -export type RoomAdminFieldsType = '_id' | - 'prid' | - 'fname' | - 'name' | - 't' | - 'cl' | - 'u' | - 'usernames' | - 'usersCount' | - 'muted' | - 'unmuted' | - 'ro' | - 'default' | - 'favorite' | - 'featured' | - 'topic' | - 'msgs' | - 'archived' | - 'tokenpass' | - 'teamId' | - 'teamMain' | - 'announcement' | - 'description' | - 'broadcast' | - 'uids' | - 'avatarETag'; +export type RoomAdminFieldsType = + | '_id' + | 'prid' + | 'fname' + | 'name' + | 't' + | 'cl' + | 'u' + | 'usernames' + | 'usersCount' + | 'muted' + | 'unmuted' + | 'ro' + | 'default' + | 'favorite' + | 'featured' + | 'topic' + | 'msgs' + | 'archived' + | 'tokenpass' + | 'teamId' + | 'teamMain' + | 'announcement' + | 'description' + | 'broadcast' + | 'uids' + | 'avatarETag'; diff --git a/packages/rest-typings/src/v1/rooms.ts b/packages/rest-typings/src/v1/rooms.ts index 9d80ce3d4dcf..647127c4b469 100644 --- a/packages/rest-typings/src/v1/rooms.ts +++ b/packages/rest-typings/src/v1/rooms.ts @@ -57,10 +57,12 @@ export type RoomsEndpoints = { }; }; 'rooms.adminRooms': { - GET: (params: PaginatedRequest<{ - filter?: string; - types?: string[]; - }>) => PaginatedResult<{ rooms: Pick[] }>; + GET: ( + params: PaginatedRequest<{ + filter?: string; + types?: string[]; + }>, + ) => PaginatedResult<{ rooms: Pick[] }>; }; 'rooms.adminRooms.getRoom': { GET: (params: { rid?: string }) => Pick;