diff --git a/.changeset/old-zoos-hang.md b/.changeset/old-zoos-hang.md new file mode 100644 index 000000000000..eb39a6c9d83c --- /dev/null +++ b/.changeset/old-zoos-hang.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +fix: mobile ringing notification missing call id diff --git a/.changeset/tough-apples-turn.md b/.changeset/tough-apples-turn.md new file mode 100644 index 000000000000..056a0645186e --- /dev/null +++ b/.changeset/tough-apples-turn.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Forward headers when using proxy for file uploads diff --git a/apps/meteor/app/file-upload/server/lib/FileUpload.ts b/apps/meteor/app/file-upload/server/lib/FileUpload.ts index 8f929a17fe34..e512e5d09bfe 100644 --- a/apps/meteor/app/file-upload/server/lib/FileUpload.ts +++ b/apps/meteor/app/file-upload/server/lib/FileUpload.ts @@ -562,7 +562,32 @@ export const FileUpload = { ) { res.setHeader('Content-Disposition', `${forceDownload ? 'attachment' : 'inline'}; filename="${encodeURI(fileName)}"`); - request.get(fileUrl, (fileRes) => fileRes.pipe(res)); + request.get(fileUrl, (fileRes) => { + if (fileRes.statusCode !== 200) { + res.setHeader('x-rc-proxyfile-status', String(fileRes.statusCode)); + res.setHeader('content-length', 0); + res.writeHead(500); + res.end(); + return; + } + + // eslint-disable-next-line prettier/prettier + const headersToProxy = [ + 'age', + 'cache-control', + 'content-length', + 'content-type', + 'date', + 'expired', + 'last-modified', + ]; + + headersToProxy.forEach((header) => { + fileRes.headers[header] && res.setHeader(header, String(fileRes.headers[header])); + }); + + fileRes.pipe(res); + }); }, generateJWTToFileUrls({ rid, userId, fileId }: { rid: string; userId: string; fileId: string }) { diff --git a/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx b/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx index 32948eb42243..04b07e9cd627 100644 --- a/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx +++ b/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx @@ -71,7 +71,7 @@ const RoomAvatarEditor = ({ disabled = false, room, roomAvatar, onChangeAvatar } danger icon='trash' title={t('Accounts_SetDefaultAvatar')} - disabled={roomAvatar === null || isRoomFederated(room) || disabled} + disabled={!roomAvatar || isRoomFederated(room) || disabled} onClick={clickReset} /> diff --git a/apps/meteor/client/views/admin/rooms/EditRoom.tsx b/apps/meteor/client/views/admin/rooms/EditRoom.tsx index b12b5e3e1ab9..8130f2e9ec8b 100644 --- a/apps/meteor/client/views/admin/rooms/EditRoom.tsx +++ b/apps/meteor/client/views/admin/rooms/EditRoom.tsx @@ -13,18 +13,17 @@ import { TextAreaInput, } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useSetModal, useToastMessageDispatch, useRoute, usePermission, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; +import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; -import React, { useState, useMemo } from 'react'; +import React, { useMemo } from 'react'; import { RoomSettingsEnum } from '../../../../definition/IRoomTypeConfig'; import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../components/Contextualbar'; -import GenericModal from '../../../components/GenericModal'; import RoomAvatarEditor from '../../../components/avatar/RoomAvatarEditor'; import { useEndpointAction } from '../../../hooks/useEndpointAction'; import { useForm } from '../../../hooks/useForm'; import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; -import DeleteTeamModalWithRooms from '../../teams/contextualBar/info/DeleteTeam'; +import { useDeleteRoom } from '../../hooks/roomActions/useDeleteRoom'; type EditRoomProps = { room: Pick; @@ -65,11 +64,6 @@ const getInitialValues = (room: Pick): EditRoomFormV const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => { const t = useTranslation(); - const [deleting, setDeleting] = useState(false); - - const setModal = useSetModal(); - const dispatchToastMessage = useToastMessageDispatch(); - const { values, handlers, hasUnsavedChanges, reset } = useForm(getInitialValues(room)); const [canViewName, canViewTopic, canViewAnnouncement, canViewArchived, canViewDescription, canViewType, canViewReadOnly] = @@ -119,9 +113,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => const changeArchivation = archived !== !!room.archived; - const roomsRoute = useRoute('admin-rooms'); - - const canDelete = usePermission(`delete-${room.t}`); + const { handleDelete, canDeleteRoom, isDeleting } = useDeleteRoom(room, { reload: onDelete }); const archiveSelector = room.archived ? 'unarchive' : 'archive'; const archiveMessage = room.archived ? 'Room_has_been_unarchived' : 'Room_has_been_archived'; @@ -161,61 +153,6 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => handleRoomType(roomType === 'p' ? 'c' : 'p'); }); - const deleteRoom = useEndpoint('POST', '/v1/rooms.delete'); - const deleteTeam = useEndpoint('POST', '/v1/teams.delete'); - - const handleDelete = useMutableCallback(() => { - const handleDeleteTeam = async (roomsToRemove: IRoom['_id'][]) => { - try { - setDeleting(true); - setModal(null); - 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 }); - setDeleting(false); - } finally { - onDelete(); - } - }; - - if (room.teamMain) { - setModal( - setModal(null)} teamId={room.teamId as string} />, - ); - - return; - } - - const handleDeleteRoom = async (): Promise => { - try { - setDeleting(true); - setModal(null); - await deleteRoom({ roomId: room._id }); - dispatchToastMessage({ type: 'success', message: t('Room_has_been_deleted') }); - roomsRoute.push({}); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); - setDeleting(false); - } finally { - onDelete(); - } - }; - - setModal( - setModal(null)} - onCancel={(): void => setModal(null)} - confirmText={t('Yes_delete_it')} - > - {t('Delete_Room_Warning')} - , - ); - }); - return ( <> e.preventDefault())}> @@ -227,7 +164,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => {t('Name')} - + {room.t !== 'd' && ( @@ -246,7 +183,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => {t('Topic')} - + )} @@ -281,7 +218,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => {t('Private')} - + {t('Just_invited_people_can_access_this_channel')} @@ -292,7 +229,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => {t('Read_only')} - + {t('Only_authorized_users_can_write_new_messages')} @@ -314,7 +251,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => {t('Room_archivation_state_true')} - + @@ -325,7 +262,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => {t('Default')} - + @@ -333,7 +270,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => {t('Favorite')} - + @@ -341,22 +278,22 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => {t('Featured')} - + - - - diff --git a/apps/meteor/client/views/hooks/roomActions/useDeleteRoom.tsx b/apps/meteor/client/views/hooks/roomActions/useDeleteRoom.tsx new file mode 100644 index 000000000000..be4728732284 --- /dev/null +++ b/apps/meteor/client/views/hooks/roomActions/useDeleteRoom.tsx @@ -0,0 +1,89 @@ +import type { IRoom, RoomAdminFieldsType } from '@rocket.chat/core-typings'; +import { isRoomFederated } from '@rocket.chat/core-typings'; +import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { useSetModal, useToastMessageDispatch, useRouter, usePermission, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; +import { useMutation } from '@tanstack/react-query'; +import React from 'react'; + +import GenericModal from '../../../components/GenericModal'; +import DeleteTeamModal from '../../teams/contextualBar/info/DeleteTeam'; + +export const useDeleteRoom = (room: IRoom | Pick, { reload }: { reload?: () => void } = {}) => { + const t = useTranslation(); + const router = useRouter(); + const setModal = useSetModal(); + const dispatchToastMessage = useToastMessageDispatch(); + const hasPermissionToDelete = usePermission(`delete-${room.t}`, room._id); + const canDeleteRoom = isRoomFederated(room) ? false : hasPermissionToDelete; + + const isAdminRoute = router.getRouteName() === 'admin-rooms'; + + const deleteRoomEndpoint = useEndpoint('POST', '/v1/rooms.delete'); + const deleteTeamEndpoint = useEndpoint('POST', '/v1/teams.delete'); + + const deleteRoomMutation = useMutation({ + mutationFn: deleteRoomEndpoint, + onSuccess: () => { + dispatchToastMessage({ type: 'success', message: t('Room_has_been_deleted') }); + if (isAdminRoute) { + return router.navigate('/admin/rooms'); + } + + return router.navigate('/home'); + }, + onError: (error) => { + dispatchToastMessage({ type: 'error', message: error }); + }, + onSettled: () => { + setModal(null); + reload?.(); + }, + }); + + const deleteTeamMutation = useMutation({ + mutationFn: deleteTeamEndpoint, + onSuccess: () => { + dispatchToastMessage({ type: 'success', message: t('Team_has_been_deleted') }); + if (isAdminRoute) { + return router.navigate('/admin/rooms'); + } + + return router.navigate('/home'); + }, + onError: (error) => { + dispatchToastMessage({ type: 'error', message: error }); + }, + onSettled: () => { + setModal(null); + reload?.(); + }, + }); + + const isDeleting = deleteTeamMutation.isLoading || deleteRoomMutation.isLoading; + + const handleDelete = useMutableCallback(() => { + const handleDeleteTeam = async (roomsToRemove: IRoom['_id'][]) => { + if (!room.teamId) { + return; + } + + deleteTeamMutation.mutateAsync({ teamId: room.teamId, ...(roomsToRemove.length && { roomsToRemove }) }); + }; + + if (room.teamMain && room.teamId) { + return setModal( setModal(null)} teamId={room.teamId} />); + } + + const handleDeleteRoom = async () => { + deleteRoomMutation.mutateAsync({ roomId: room._id }); + }; + + setModal( + setModal(null)} confirmText={t('Yes_delete_it')}> + {t('Delete_Room_Warning')} + , + ); + }); + + return { handleDelete, canDeleteRoom, isDeleting }; +}; diff --git a/apps/meteor/client/views/marketplace/AppInstallPage.js b/apps/meteor/client/views/marketplace/AppInstallPage.js index 77afd5361c88..f3b4f02b0c16 100644 --- a/apps/meteor/client/views/marketplace/AppInstallPage.js +++ b/apps/meteor/client/views/marketplace/AppInstallPage.js @@ -1,4 +1,5 @@ -import { Button, ButtonGroup, Icon, Field, FieldGroup, TextInput, Throbber } from '@rocket.chat/fuselage'; +import { Button, ButtonGroup, Icon, Field, FieldGroup, FieldLabel, FieldRow, TextInput } from '@rocket.chat/fuselage'; +import { useUniqueId } from '@rocket.chat/fuselage-hooks'; import { useSetModal, useEndpoint, @@ -8,13 +9,13 @@ import { useRouter, useSearchParameter, } from '@rocket.chat/ui-contexts'; -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useState } from 'react'; +import { useForm, Controller } from 'react-hook-form'; import { AppClientOrchestratorInstance } from '../../../ee/client/apps/orchestrator'; import Page from '../../components/Page'; import { useAppsReload } from '../../contexts/hooks/useAppsReload'; import { useFileInput } from '../../hooks/useFileInput'; -import { useForm } from '../../hooks/useForm'; import AppPermissionsReviewModal from './AppPermissionsReviewModal'; import AppUpdateModal from './AppUpdateModal'; import AppInstallModal from './components/AppInstallModal/AppInstallModal'; @@ -48,22 +49,12 @@ function AppInstallPage() { const appCountQuery = useAppsCountQuery('private'); - const { values, handlers } = useForm({ - file: {}, - url: queryUrl, - }); + const { control, setValue, watch } = useForm({ defaultValues: { url: queryUrl || '' } }); + const { file, url } = watch(); - const { file, url } = values; + const canSave = !!url || !!file?.name; - const canSave = !!url || !!file.name; - - const { handleFile, handleUrl } = handlers; - - useEffect(() => { - queryUrl && handleUrl(queryUrl); - }, [queryUrl, handleUrl]); - - const [handleUploadButtonClick] = useFileInput(handleFile, 'app'); + const [handleUploadButtonClick] = useFileInput((value) => setValue('file', value), 'app'); const sendFile = async (permissionsGranted, appFile, appId) => { let app; @@ -200,35 +191,52 @@ function AppInstallPage() { }); }; + const urlField = useUniqueId(); + const fileField = useUniqueId(); + return ( - {t('App_Url_to_Install_From')} - - } /> - + {t('App_Url_to_Install_From')} + + ( + } {...field} /> + )} + /> + - {t('App_Url_to_Install_From_File')} - - - {t('Browse_Files')} - - } + {t('App_Url_to_Install_From_File')} + + ( + + {t('Browse_Files')} + + } + /> + )} /> - + diff --git a/apps/meteor/client/views/room/body/RoomForeword/RoomForewordUsernameList.tsx b/apps/meteor/client/views/room/body/RoomForeword/RoomForewordUsernameList.tsx index b84a867d11d7..4677bb5e1ad3 100644 --- a/apps/meteor/client/views/room/body/RoomForeword/RoomForewordUsernameList.tsx +++ b/apps/meteor/client/views/room/body/RoomForeword/RoomForewordUsernameList.tsx @@ -1,4 +1,5 @@ import type { IUser } from '@rocket.chat/core-typings'; +import { Margins } from '@rocket.chat/fuselage'; import { useSetting } from '@rocket.chat/ui-contexts'; import type { VFC } from 'react'; import React from 'react'; @@ -11,7 +12,7 @@ type RoomForewordUsernameListProps = { usernames: Array = ({ usernames }) => { const useRealName = Boolean(useSetting('UI_Use_Real_Name')); return ( - <> + {usernames.map((username) => ( = ({ username useRealName={useRealName} /> ))} - + ); }; diff --git a/apps/meteor/client/views/room/body/RoomForeword/RoomForewordUsernameListItem.tsx b/apps/meteor/client/views/room/body/RoomForeword/RoomForewordUsernameListItem.tsx index fe148aa79d78..a0732b35d29d 100644 --- a/apps/meteor/client/views/room/body/RoomForeword/RoomForewordUsernameListItem.tsx +++ b/apps/meteor/client/views/room/body/RoomForeword/RoomForewordUsernameListItem.tsx @@ -1,5 +1,5 @@ import type { IUser } from '@rocket.chat/core-typings'; -import { Box, Icon, Tag, Skeleton } from '@rocket.chat/fuselage'; +import { Icon, Tag, Skeleton } from '@rocket.chat/fuselage'; import type { VFC } from 'react'; import React from 'react'; @@ -12,18 +12,15 @@ type RoomForewordUsernameListItemProps = { useRealName: boolean; }; -// TODO: Improve `Tag` a11y to be used as a link const RoomForewordUsernameListItem: VFC = ({ username, href, useRealName }) => { const { data, isLoading, isError } = useUserInfoQuery({ username }); return ( - - } className='mention-link' data-username={username} large> - {isLoading && } - {!isLoading && isError && username} - {!isLoading && !isError && getUserDisplayName(data?.user?.name, username, useRealName)} - - + } data-username={username} large href={href}> + {isLoading && } + {!isLoading && isError && username} + {!isLoading && !isError && getUserDisplayName(data?.user?.name, username, useRealName)} + ); }; diff --git a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js deleted file mode 100644 index 22c84fbdebf1..000000000000 --- a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js +++ /dev/null @@ -1,514 +0,0 @@ -import { isRoomFederated } from '@rocket.chat/core-typings'; -import { - Field, - TextInput, - PasswordInput, - ToggleSwitch, - MultiSelect, - Accordion, - Callout, - NumberInput, - FieldGroup, - FieldLabel, - FieldRow, - FieldHint, - Button, - ButtonGroup, - Box, - TextAreaInput, -} from '@rocket.chat/fuselage'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { - useSetModal, - useSetting, - usePermission, - useAtLeastOnePermission, - useRole, - useMethod, - useTranslation, - useRouter, -} from '@rocket.chat/ui-contexts'; -import React, { useCallback, useMemo, useRef } from 'react'; - -import { e2e } from '../../../../../../app/e2e/client/rocketchat.e2e'; -import { MessageTypesValues } from '../../../../../../app/lib/lib/MessageTypes'; -import { RoomSettingsEnum } from '../../../../../../definition/IRoomTypeConfig'; -import { - ContextualbarHeader, - ContextualbarBack, - ContextualbarTitle, - ContextualbarClose, - ContextualbarScrollableContent, - ContextualbarFooter, -} from '../../../../../components/Contextualbar'; -import GenericModal from '../../../../../components/GenericModal'; -import RawText from '../../../../../components/RawText'; -import RoomAvatarEditor from '../../../../../components/avatar/RoomAvatarEditor'; -import { useEndpointAction } from '../../../../../hooks/useEndpointAction'; -import { useForm } from '../../../../../hooks/useForm'; -import { roomCoordinator } from '../../../../../lib/rooms/roomCoordinator'; - -const typeMap = { - c: 'Channels', - p: 'Groups', - d: 'DMs', -}; - -const useInitialValues = (room, settings) => { - const { - t, - ro, - archived, - topic, - description, - announcement, - joinCodeRequired, - sysMes, - encrypted, - retention = {}, - reactWhenReadOnly, - } = room; - - const { retentionPolicyEnabled, maxAgeDefault } = settings; - - const retentionEnabledDefault = useSetting(`RetentionPolicy_AppliesTo${typeMap[room.t]}`); - const excludePinnedDefault = useSetting('RetentionPolicy_DoNotPrunePinned'); - const filesOnlyDefault = useSetting('RetentionPolicy_FilesOnly'); - - return useMemo( - () => ({ - roomName: t === 'd' ? room.usernames.join(' x ') : roomCoordinator.getRoomName(t, { type: t, ...room }), - roomType: t, - readOnly: !!ro, - reactWhenReadOnly, - archived: !!archived, - roomTopic: topic ?? '', - roomDescription: description ?? '', - roomAnnouncement: announcement ?? '', - roomAvatar: undefined, - joinCode: '', - joinCodeRequired: !!joinCodeRequired, - systemMessages: Array.isArray(sysMes) ? sysMes : [], - hideSysMes: !!sysMes?.length, - encrypted, - ...(retentionPolicyEnabled && { - retentionEnabled: retention.enabled ?? retentionEnabledDefault, - retentionOverrideGlobal: !!retention.overrideGlobal, - retentionMaxAge: Math.min(retention.maxAge, maxAgeDefault) || maxAgeDefault, - retentionExcludePinned: retention.excludePinned ?? excludePinnedDefault, - retentionFilesOnly: retention.filesOnly ?? filesOnlyDefault, - }), - }), - [ - announcement, - archived, - description, - excludePinnedDefault, - filesOnlyDefault, - joinCodeRequired, - maxAgeDefault, - retention.enabled, - retention.excludePinned, - retention.filesOnly, - retention.maxAge, - retention.overrideGlobal, - retentionEnabledDefault, - retentionPolicyEnabled, - ro, - room, - sysMes, - t, - topic, - encrypted, - reactWhenReadOnly, - ], - ); -}; - -const getCanChangeType = (room, canCreateChannel, canCreateGroup, isAdmin) => - (!room.default || isAdmin) && ((room.t === 'p' && canCreateChannel) || (room.t === 'c' && canCreateGroup)); - -function EditChannel({ room, onClickClose, onClickBack }) { - const t = useTranslation(); - - const setModal = useSetModal(); - - const retentionPolicyEnabled = useSetting('RetentionPolicy_Enabled'); - const maxAgeDefault = useSetting(`RetentionPolicy_MaxAge_${typeMap[room.t]}`) || 30; - - const saveData = useRef({}); - const router = useRouter(); - - const onChange = useCallback(({ initialValue, value, key }) => { - const { current } = saveData; - if (JSON.stringify(initialValue) !== JSON.stringify(value)) { - if (key === 'systemMessages' && value?.length > 0) { - current.hideSysMes = true; - } - current[key] = value; - } else { - delete current[key]; - } - }, []); - - const { values, handlers, hasUnsavedChanges, reset, commit } = useForm( - useInitialValues(room, { retentionPolicyEnabled, maxAgeDefault }), - onChange, - ); - - const sysMesOptions = useMemo(() => MessageTypesValues.map(({ key, i18nLabel }) => [key, t(i18nLabel)]), [t]); - - const { - roomName, - roomType, - readOnly, - encrypted, - roomAvatar, - archived, - roomTopic, - roomDescription, - roomAnnouncement, - reactWhenReadOnly, - joinCode, - joinCodeRequired, - systemMessages, - hideSysMes, - retentionEnabled, - retentionOverrideGlobal, - retentionMaxAge, - retentionExcludePinned, - retentionFilesOnly, - } = values; - - const { - handleJoinCode, - handleJoinCodeRequired, - handleSystemMessages, - handleEncrypted, - handleHideSysMes, - handleRoomName, - handleReadOnly, - handleArchived, - handleRoomAvatar, - handleReactWhenReadOnly, - handleRoomType, - handleRoomTopic, - handleRoomDescription, - handleRoomAnnouncement, - handleRetentionEnabled, - handleRetentionOverrideGlobal, - handleRetentionMaxAge, - handleRetentionExcludePinned, - handleRetentionFilesOnly, - } = handlers; - - const [ - canViewName, - canViewTopic, - canViewAnnouncement, - canViewArchived, - canViewDescription, - canViewType, - canViewReadOnly, - canViewHideSysMes, - canViewJoinCode, - canViewEncrypted, - ] = 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.SYSTEM_MESSAGES), - isAllowed(room, RoomSettingsEnum.JOIN_CODE), - isAllowed(room, RoomSettingsEnum.REACT_WHEN_READ_ONLY), - isAllowed(room, RoomSettingsEnum.E2E), - ]; - }, [room]); - - const isAdmin = useRole('admin'); - - const canCreateChannel = usePermission('create-c'); - const canCreateGroup = usePermission('create-p'); - const canChangeType = getCanChangeType(room, canCreateChannel, canCreateGroup, isAdmin); - const canSetRo = usePermission('set-readonly', room._id); - const canSetReactWhenRo = usePermission('set-react-when-readonly', room._id); - const canEditRoomRetentionPolicy = usePermission('edit-room-retention-policy', room._id); - const canArchiveOrUnarchive = useAtLeastOnePermission( - useMemo(() => ['archive-room', 'unarchive-room'], []), - room._id, - ); - const canDelete = usePermission(`delete-${room.t}`); - const canToggleEncryption = usePermission('toggle-room-e2e-encryption', room._id) && (room.encrypted || e2e.isReady()); - - const changeArchivation = archived !== !!room.archived; - const archiveSelector = room.archived ? 'unarchive' : 'archive'; - const archiveMessage = room.archived ? 'Room_has_been_unarchived' : 'Room_has_been_archived'; - const saveAction = useEndpointAction('POST', '/v1/rooms.saveRoomSettings', { - successMessage: t('Room_updated_successfully'), - }); - const archiveAction = useEndpointAction('POST', '/v1/rooms.changeArchivationState', { successMessage: t(archiveMessage) }); - - const handleSave = useMutableCallback(async () => { - const { joinCodeRequired, hideSysMes, ...data } = saveData.current; - delete data.archived; - const save = () => - saveAction({ - rid: room._id, - ...data, - ...(joinCode && { joinCode: joinCodeRequired ? joinCode : '' }), - ...((data.systemMessages || !hideSysMes) && { - systemMessages: hideSysMes && systemMessages, - }), - }); - - const archive = () => archiveAction({ rid: room._id, action: archiveSelector }); - - await Promise.all([hasUnsavedChanges && save(), changeArchivation && archive()].filter(Boolean)); - saveData.current = {}; - commit(); - }); - - const deleteRoom = useMethod('eraseRoom'); - - const handleDelete = useMutableCallback(() => { - const onCancel = () => setModal(undefined); - const onConfirm = async () => { - await deleteRoom(room._id); - onCancel(); - router.navigate('/home'); - }; - - setModal( - - {t('Delete_Room_Warning')} - , - ); - }); - - const changeRoomType = useMutableCallback(() => { - handleRoomType(roomType === 'p' ? 'c' : 'p'); - }); - - const onChangeMaxAge = useMutableCallback((e) => { - handleRetentionMaxAge(Math.max(1, Number(e.currentTarget.value))); - }); - - const isFederated = useMemo(() => isRoomFederated(room), [room]); - - return ( - <> - - {onClickBack && } - {room.teamId ? t('edit-team') : t('edit-room')} - {onClickClose && } - - e.preventDefault())}> - - - - - {t('Name')} - - - - - {canViewDescription && ( - - {t('Description')} - - - - - )} - {canViewAnnouncement && ( - - {t('Announcement')} - - - - - )} - {canViewTopic && ( - - {t('Topic')} - - - - - )} - {canViewType && ( - - - {t('Private')} - - - - - {t('Teams_New_Private_Description_Enabled')} - - )} - {canViewReadOnly && ( - - - {t('Read_only')} - - - - - {t('Only_authorized_users_can_write_new_messages')} - - )} - {readOnly && ( - - - {t('React_when_read_only')} - - - - - {t('Only_authorized_users_can_react_to_messages')} - - )} - {canViewArchived && ( - - - {t('Room_archivation_state_true')} - - - - - - )} - {canViewJoinCode && ( - - - {t('Password_to_access')} - - - - - - - - - )} - {canViewHideSysMes && ( - - - {t('Hide_System_Messages')} - - - - - - - - - )} - {canViewEncrypted && ( - - - {t('Encrypted')} - - - - - - )} - {retentionPolicyEnabled && ( - - - - - - {t('RetentionPolicyRoom_Enabled')} - - - - - - - - {t('RetentionPolicyRoom_OverrideGlobal')} - - - - - - {retentionOverrideGlobal && ( - <> - - {t('RetentionPolicyRoom_ReadTheDocs')} - - - {t('RetentionPolicyRoom_MaxAge', { max: maxAgeDefault })} - - - - - - - {t('RetentionPolicyRoom_ExcludePinned')} - - - - - - - - {t('RetentionPolicyRoom_FilesOnly')} - - - - - - - )} - - - - )} - - - - - - - - - - - - ); -} - -export default EditChannel; diff --git a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannelWithData.js b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannelWithData.js deleted file mode 100644 index e64dcc17562a..000000000000 --- a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannelWithData.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; - -import { useRoom } from '../../../contexts/RoomContext'; -import { useRoomToolbox } from '../../../contexts/RoomToolboxContext'; -import EditChannel from './EditChannel'; - -function EditChannelWithData({ onClickBack }) { - const room = useRoom(); - const { closeTab } = useRoomToolbox(); - - return ; -} - -export default EditChannelWithData; diff --git a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfo.tsx b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfo.tsx new file mode 100644 index 000000000000..b2c552fd3d87 --- /dev/null +++ b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfo.tsx @@ -0,0 +1,487 @@ +import type { IRoomWithRetentionPolicy } from '@rocket.chat/core-typings'; +import { isRoomFederated } from '@rocket.chat/core-typings'; +import type { SelectOption } from '@rocket.chat/fuselage'; +import { + Field, + FieldRow, + FieldLabel, + FieldHint, + TextInput, + PasswordInput, + ToggleSwitch, + MultiSelect, + Accordion, + Callout, + NumberInput, + FieldGroup, + Button, + ButtonGroup, + Box, + TextAreaInput, +} from '@rocket.chat/fuselage'; +import { useMutableCallback, useUniqueId } from '@rocket.chat/fuselage-hooks'; +import type { TranslationKey } from '@rocket.chat/ui-contexts'; +import { useSetting, useTranslation, useToastMessageDispatch, useEndpoint } from '@rocket.chat/ui-contexts'; +import React, { useMemo } from 'react'; +import { useForm, Controller } from 'react-hook-form'; + +import { MessageTypesValues } from '../../../../../../app/lib/lib/MessageTypes'; +import { + ContextualbarHeader, + ContextualbarBack, + ContextualbarTitle, + ContextualbarClose, + ContextualbarScrollableContent, + ContextualbarFooter, +} from '../../../../../components/Contextualbar'; +import RawText from '../../../../../components/RawText'; +import RoomAvatarEditor from '../../../../../components/avatar/RoomAvatarEditor'; +import { getDirtyFields } from '../../../../../lib/getDirtyFields'; +import { useDeleteRoom } from '../../../../hooks/roomActions/useDeleteRoom'; +import { useEditRoomInitialValues } from './useEditRoomInitialValues'; +import { useEditRoomPermissions } from './useEditRoomPermissions'; + +type EditRoomInfoProps = { + room: IRoomWithRetentionPolicy; + onClickClose: () => void; + onClickBack: () => void; +}; + +const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) => { + const t = useTranslation(); + const dispatchToastMessage = useToastMessageDispatch(); + const isFederated = useMemo(() => isRoomFederated(room), [room]); + + const retentionPolicy = useSetting('RetentionPolicy_Enabled'); + const { handleDelete, canDeleteRoom } = useDeleteRoom(room); + const defaultValues = useEditRoomInitialValues(room); + + const { + watch, + reset, + control, + handleSubmit, + formState: { isDirty, dirtyFields, errors }, + } = useForm({ mode: 'onBlur', defaultValues }); + + const sysMesOptions: SelectOption[] = useMemo( + () => MessageTypesValues.map(({ key, i18nLabel }) => [key, t(i18nLabel as TranslationKey)]), + [t], + ); + + const { readOnly, archived, joinCodeRequired, hideSysMes, retentionEnabled, retentionMaxAge, retentionOverrideGlobal } = watch(); + + const { + canChangeType, + canSetReadOnly, + canSetReactWhenReadOnly, + canEditRoomRetentionPolicy, + canArchiveOrUnarchive, + canToggleEncryption, + canViewName, + canViewTopic, + canViewAnnouncement, + canViewArchived, + canViewDescription, + canViewType, + canViewReadOnly, + canViewHideSysMes, + canViewJoinCode, + canViewEncrypted, + } = useEditRoomPermissions(room); + + const changeArchiving = archived !== !!room.archived; + + const saveAction = useEndpoint('POST', '/v1/rooms.saveRoomSettings'); + const archiveAction = useEndpoint('POST', '/v1/rooms.changeArchivationState'); + + const handleUpdateRoomData = useMutableCallback(async ({ hideSysMes, ...formData }) => { + const data = getDirtyFields(formData, dirtyFields); + + try { + await saveAction({ + rid: room._id, + ...data, + ...(data.joinCode && { joinCode: joinCodeRequired ? data.joinCode : '' }), + ...((data.systemMessages || !hideSysMes) && { + systemMessages: hideSysMes && data.systemMessages, + }), + }); + + dispatchToastMessage({ type: 'success', message: t('Room_updated_successfully') }); + onClickClose(); + } catch (error) { + dispatchToastMessage({ type: 'error', message: error }); + } + }); + + const handleArchive = useMutableCallback(async () => { + try { + await archiveAction({ rid: room._id, action: room.archived ? 'unarchive' : 'archive' }); + dispatchToastMessage({ type: 'success', message: room.archived ? t('Room_has_been_unarchived') : t('Room_has_been_archived') }); + } catch (error) { + dispatchToastMessage({ type: 'error', message: error }); + } + }); + + const handleSave = useMutableCallback(async (data) => { + await Promise.all([isDirty && handleUpdateRoomData(data), changeArchiving && handleArchive()].filter(Boolean)); + }); + + const formId = useUniqueId(); + const roomNameField = useUniqueId(); + const roomDescriptionField = useUniqueId(); + const roomAnnouncementField = useUniqueId(); + const roomTopicField = useUniqueId(); + const roomTypeField = useUniqueId(); + const readOnlyField = useUniqueId(); + const reactWhenReadOnlyField = useUniqueId(); + const archivedField = useUniqueId(); + const joinCodeRequiredField = useUniqueId(); + const hideSysMesField = useUniqueId(); + const encryptedField = useUniqueId(); + const retentionEnabledField = useUniqueId(); + const retentionOverrideGlobalField = useUniqueId(); + const retentionMaxAgeField = useUniqueId(); + const retentionExcludePinnedField = useUniqueId(); + const retentionFilesOnlyField = useUniqueId(); + + return ( + <> + + {onClickBack && } + {room.teamId ? t('edit-team') : t('edit-room')} + {onClickClose && } + + +
+ + } + /> + + + + + {t('Name')} + + + } + /> + + {errors.roomName && {errors.roomName.message}} + + {canViewDescription && ( + + {t('Description')} + + } + /> + + + )} + {canViewAnnouncement && ( + + {t('Announcement')} + + } + /> + + + )} + {canViewTopic && ( + + {t('Topic')} + + } + /> + + + )} + {canViewType && ( + + + {t('Private')} + + ( + onChange(value === 'p' ? 'c' : 'p')} + aria-describedby={`${roomTypeField}-hint`} + /> + )} + /> + + + {t('Teams_New_Private_Description_Enabled')} + + )} + {canViewReadOnly && ( + + + {t('Read_only')} + + ( + + )} + /> + + + {t('Only_authorized_users_can_write_new_messages')} + + )} + {readOnly && ( + + + {t('React_when_read_only')} + + ( + + )} + /> + + + {t('Only_authorized_users_can_react_to_messages')} + + )} + {canViewArchived && ( + + + {t('Room_archivation_state_true')} + + ( + + )} + /> + + + + )} + {canViewJoinCode && ( + + + {t('Password_to_access')} + + ( + + )} + /> + + + + } + /> + + + )} + {canViewHideSysMes && ( + + + {t('Hide_System_Messages')} + + ( + + )} + /> + + + + ( + + )} + /> + + + )} + {canViewEncrypted && ( + + + {t('Encrypted')} + + ( + + )} + /> + + + + )} + + {retentionPolicy && ( + + + + + + {t('RetentionPolicyRoom_Enabled')} + + ( + + )} + /> + + + + + + {t('RetentionPolicyRoom_OverrideGlobal')} + + ( + + )} + /> + + + + {retentionOverrideGlobal && ( + <> + + {t('RetentionPolicyRoom_ReadTheDocs')} + + + {t('RetentionPolicyRoom_MaxAge', { max: retentionMaxAge })} + + ( + onChange(Math.max(1, Number(currentValue)))} + /> + )} + /> + + + + + {t('RetentionPolicyRoom_ExcludePinned')} + + ( + + )} + /> + + + + + + {t('RetentionPolicyRoom_FilesOnly')} + + ( + + )} + /> + + + + + )} + + + + )} +
+
+ + + + + + + + + + + ); +}; + +export default EditRoomInfo; diff --git a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfoWithData.tsx b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfoWithData.tsx new file mode 100644 index 000000000000..ad758c1bc8a6 --- /dev/null +++ b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfoWithData.tsx @@ -0,0 +1,15 @@ +import type { IRoomWithRetentionPolicy } from '@rocket.chat/core-typings'; +import React from 'react'; + +import { useRoom } from '../../../contexts/RoomContext'; +import { useRoomToolbox } from '../../../contexts/RoomToolboxContext'; +import EditRoomInfo from './EditRoomInfo'; + +const EditRoomInfoWithData = ({ onClickBack }: { onClickBack: () => void }) => { + const room = useRoom() as IRoomWithRetentionPolicy; + const { closeTab } = useRoomToolbox(); + + return ; +}; + +export default EditRoomInfoWithData; diff --git a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/index.ts b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/index.ts index 4083ad9a958f..d8b31e17800c 100644 --- a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/index.ts +++ b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/index.ts @@ -1 +1 @@ -export { default } from './EditChannelWithData'; +export { default } from './EditRoomInfoWithData'; diff --git a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/useEditRoomInitialValues.ts b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/useEditRoomInitialValues.ts new file mode 100644 index 000000000000..f36802bb9f56 --- /dev/null +++ b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/useEditRoomInitialValues.ts @@ -0,0 +1,71 @@ +import type { IRoomWithRetentionPolicy } from '@rocket.chat/core-typings'; +import { useSetting } from '@rocket.chat/ui-contexts'; +import { useMemo } from 'react'; + +import { roomCoordinator } from '../../../../../lib/rooms/roomCoordinator'; + +const getPolicyRoomType = (roomType: IRoomWithRetentionPolicy['t']) => { + switch (roomType) { + case 'c': + return 'Channels'; + case 'p': + return 'Groups'; + case 'd': + return 'DMs'; + } +}; + +export const useEditRoomInitialValues = (room: IRoomWithRetentionPolicy) => { + const { t, ro, archived, topic, description, announcement, joinCodeRequired, sysMes, encrypted, retention, reactWhenReadOnly } = room; + + const retentionPolicyEnabled = useSetting('RetentionPolicy_Enabled'); + const maxAgeDefault = useSetting(`RetentionPolicy_MaxAge_${getPolicyRoomType(room.t)}`) || 30; + const retentionEnabledDefault = useSetting(`RetentionPolicy_AppliesTo${getPolicyRoomType(room.t)}`); + const excludePinnedDefault = useSetting('RetentionPolicy_DoNotPrunePinned'); + const filesOnlyDefault = useSetting('RetentionPolicy_FilesOnly'); + + return useMemo( + () => ({ + roomName: t === 'd' && room.usernames ? room.usernames.join(' x ') : roomCoordinator.getRoomName(t, room), + roomType: t, + readOnly: !!ro, + reactWhenReadOnly, + archived: !!archived, + roomTopic: topic ?? '', + roomDescription: description ?? '', + roomAnnouncement: announcement ?? '', + roomAvatar: undefined, + joinCode: '', + joinCodeRequired: !!joinCodeRequired, + systemMessages: Array.isArray(sysMes) ? sysMes : [], + hideSysMes: Array.isArray(sysMes) ? !!sysMes?.length : !!sysMes, + encrypted, + ...(retentionPolicyEnabled && { + retentionEnabled: retention?.enabled ?? retentionEnabledDefault, + retentionOverrideGlobal: !!retention?.overrideGlobal, + retentionMaxAge: Math.min(retention?.maxAge, maxAgeDefault) || maxAgeDefault, + retentionExcludePinned: retention?.excludePinned ?? excludePinnedDefault, + retentionFilesOnly: retention?.filesOnly ?? filesOnlyDefault, + }), + }), + [ + announcement, + archived, + description, + excludePinnedDefault, + filesOnlyDefault, + joinCodeRequired, + maxAgeDefault, + retention, + retentionEnabledDefault, + retentionPolicyEnabled, + ro, + room, + sysMes, + t, + topic, + encrypted, + reactWhenReadOnly, + ], + ); +}; diff --git a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/useEditRoomPermissions.ts b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/useEditRoomPermissions.ts new file mode 100644 index 000000000000..7b9e8c353941 --- /dev/null +++ b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/useEditRoomPermissions.ts @@ -0,0 +1,77 @@ +import type { IRoom, IRoomWithRetentionPolicy } from '@rocket.chat/core-typings'; +import { usePermission, useAtLeastOnePermission, useRole } from '@rocket.chat/ui-contexts'; +import { useMemo } from 'react'; + +import { e2e } from '../../../../../../app/e2e/client/rocketchat.e2e'; +import { RoomSettingsEnum } from '../../../../../../definition/IRoomTypeConfig'; +import { roomCoordinator } from '../../../../../lib/rooms/roomCoordinator'; + +const getCanChangeType = (room: IRoom | IRoomWithRetentionPolicy, canCreateChannel: boolean, canCreateGroup: boolean, isAdmin: boolean) => + (!room.default || isAdmin) && ((room.t === 'p' && canCreateChannel) || (room.t === 'c' && canCreateGroup)); + +export const useEditRoomPermissions = (room: IRoom | IRoomWithRetentionPolicy) => { + const isAdmin = useRole('admin'); + const canCreateChannel = usePermission('create-c'); + const canCreateGroup = usePermission('create-p'); + + const canChangeType = getCanChangeType(room, canCreateChannel, canCreateGroup, isAdmin); + const canSetReadOnly = usePermission('set-readonly', room._id); + const canSetReactWhenReadOnly = usePermission('set-react-when-readonly', room._id); + const canEditRoomRetentionPolicy = usePermission('edit-room-retention-policy', room._id); + const canArchiveOrUnarchive = useAtLeastOnePermission( + useMemo(() => ['archive-room', 'unarchive-room'], []), + room._id, + ); + const canToggleEncryption = usePermission('toggle-room-e2e-encryption', room._id) && (room.encrypted || e2e.isReady()); + + const [ + canViewName, + canViewTopic, + canViewAnnouncement, + canViewArchived, + canViewDescription, + canViewType, + canViewReadOnly, + canViewHideSysMes, + canViewJoinCode, + canViewEncrypted, + ] = useMemo(() => { + const isAllowed = + roomCoordinator.getRoomDirectives(room.t)?.allowRoomSettingChange || + (() => { + undefined; + }); + 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.SYSTEM_MESSAGES), + isAllowed(room, RoomSettingsEnum.JOIN_CODE), + isAllowed(room, RoomSettingsEnum.REACT_WHEN_READ_ONLY), + isAllowed(room, RoomSettingsEnum.E2E), + ]; + }, [room]); + + return { + canChangeType, + canSetReadOnly, + canSetReactWhenReadOnly, + canEditRoomRetentionPolicy, + canArchiveOrUnarchive, + canToggleEncryption, + canViewName, + canViewTopic, + canViewAnnouncement, + canViewArchived, + canViewDescription, + canViewType, + canViewReadOnly, + canViewHideSysMes, + canViewJoinCode, + canViewEncrypted, + }; +}; diff --git a/apps/meteor/client/views/room/contextualBar/Info/hooks/actions/useRoomDelete.tsx b/apps/meteor/client/views/room/contextualBar/Info/hooks/actions/useRoomDelete.tsx deleted file mode 100644 index b812e896bab9..000000000000 --- a/apps/meteor/client/views/room/contextualBar/Info/hooks/actions/useRoomDelete.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import type { IRoom } from '@rocket.chat/core-typings'; -import { isRoomFederated } from '@rocket.chat/core-typings'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useSetModal, useToastMessageDispatch, useTranslation, useEndpoint, usePermission, useRouter } from '@rocket.chat/ui-contexts'; -import React from 'react'; - -import GenericModal from '../../../../../../components/GenericModal'; - -// TODO: resetState for TeamsChannels -export const useRoomDelete = (room: IRoom, resetState?: () => void) => { - const t = useTranslation(); - const setModal = useSetModal(); - const dispatchToastMessage = useToastMessageDispatch(); - const router = useRouter(); - - const hasPermissionToDelete = usePermission(room.t === 'c' ? 'delete-c' : 'delete-p', room._id); - const canDelete = isRoomFederated(room) ? false : hasPermissionToDelete; - - const deleteRoom = useEndpoint('POST', room.t === 'c' ? '/v1/channels.delete' : '/v1/groups.delete'); - - const handleDelete = useMutableCallback(() => { - const onConfirm = async () => { - try { - await deleteRoom({ roomId: room._id }); - dispatchToastMessage({ type: 'success', message: t('Room_has_been_deleted') }); - if (resetState) { - return resetState(); - } - - router.navigate('/home'); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); - } - setModal(null); - }; - - setModal( - setModal(null)} confirmText={t('Yes_delete_it')}> - {t('Delete_Room_Warning')} - , - ); - }); - - return canDelete ? handleDelete : null; -}; diff --git a/apps/meteor/client/views/room/contextualBar/Info/hooks/useRoomActions.ts b/apps/meteor/client/views/room/contextualBar/Info/hooks/useRoomActions.ts index d0ab03bd9ee7..638cd23b66ed 100644 --- a/apps/meteor/client/views/room/contextualBar/Info/hooks/useRoomActions.ts +++ b/apps/meteor/client/views/room/contextualBar/Info/hooks/useRoomActions.ts @@ -1,10 +1,9 @@ -import { isRoomFederated } from '@rocket.chat/core-typings'; import type { IRoom } from '@rocket.chat/core-typings'; import { useTranslation } from '@rocket.chat/ui-contexts'; import { useMemo } from 'react'; +import { useDeleteRoom } from '../../../../hooks/roomActions/useDeleteRoom'; import { useRoomConvertToTeam } from './actions/useRoomConvertToTeam'; -import { useRoomDelete } from './actions/useRoomDelete'; import { useRoomHide } from './actions/useRoomHide'; import { useRoomLeave } from './actions/useRoomLeave'; import { useRoomMoveToTeam } from './actions/useRoomMoveToTeam'; @@ -16,11 +15,10 @@ type RoomActions = { export const useRoomActions = (room: IRoom, { onClickEnterRoom, onClickEdit }: RoomActions, resetState?: () => void) => { const t = useTranslation(); - const isFederated = isRoomFederated(room); const handleHide = useRoomHide(room); const handleLeave = useRoomLeave(room); - const handleDelete = useRoomDelete(room, resetState); + const { handleDelete, canDeleteRoom } = useDeleteRoom(room, { reload: resetState }); const handleMoveToTeam = useRoomMoveToTeam(room); const handleConvertToTeam = useRoomConvertToTeam(room); @@ -40,7 +38,7 @@ export const useRoomActions = (room: IRoom, { onClickEnterRoom, onClickEdit }: R action: onClickEdit, }, }), - ...(!isFederated && + ...(canDeleteRoom && handleDelete && { delete: { label: t('Delete'), @@ -77,7 +75,7 @@ export const useRoomActions = (room: IRoom, { onClickEnterRoom, onClickEdit }: R }, }), }), - [onClickEdit, t, handleDelete, handleMoveToTeam, handleConvertToTeam, handleHide, handleLeave, onClickEnterRoom, isFederated], + [onClickEdit, t, handleDelete, handleMoveToTeam, handleConvertToTeam, handleHide, handleLeave, onClickEnterRoom, canDeleteRoom], ); return memoizedActions; diff --git a/apps/meteor/client/views/teams/contextualBar/info/TeamsInfoWithData.js b/apps/meteor/client/views/teams/contextualBar/info/TeamsInfoWithData.js index 0b72b3f164a6..f5cb4a44c5d2 100644 --- a/apps/meteor/client/views/teams/contextualBar/info/TeamsInfoWithData.js +++ b/apps/meteor/client/views/teams/contextualBar/info/TeamsInfoWithData.js @@ -16,10 +16,10 @@ import { GenericModalDoNotAskAgain } from '../../../../components/GenericModal'; import { useDontAskAgain } from '../../../../hooks/useDontAskAgain'; import { useEndpointAction } from '../../../../hooks/useEndpointAction'; import { roomCoordinator } from '../../../../lib/rooms/roomCoordinator'; +import { useDeleteRoom } from '../../../hooks/roomActions/useDeleteRoom'; import { useRoom } from '../../../room/contexts/RoomContext'; import { useRoomToolbox } from '../../../room/contexts/RoomToolboxContext'; import ConvertToChannelModal from '../../ConvertToChannelModal'; -import DeleteTeamModal from './DeleteTeam'; import LeaveTeam from './LeaveTeam'; import TeamsInfo from './TeamsInfo'; @@ -56,7 +56,6 @@ const TeamsInfoWithLogic = ({ openEditing }) => { const setModal = useSetModal(); const closeModal = useMutableCallback(() => setModal()); - const deleteTeam = useEndpointAction('POST', '/v1/teams.delete'); const leaveTeam = useEndpointAction('POST', '/v1/teams.leave'); const convertTeamToChannel = useEndpointAction('POST', '/v1/teams.convertToChannel'); @@ -64,28 +63,11 @@ const TeamsInfoWithLogic = ({ openEditing }) => { const router = useRouter(); - const canDelete = usePermission('delete-team', room._id); const canEdit = usePermission('edit-team-channel', room._id); // const canLeave = usePermission('leave-team'); /* && room.cl !== false && joined */ - const onClickDelete = useMutableCallback(() => { - const onConfirm = async (deletedRooms) => { - const roomsToRemove = Array.isArray(deletedRooms) && deletedRooms.length > 0 ? deletedRooms : []; - - try { - await deleteTeam({ teamId: room.teamId, ...(roomsToRemove.length && { roomsToRemove }) }); - dispatchToastMessage({ type: 'success', message: t('Team_has_been_deleted') }); - router.navigate('/home'); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); - } finally { - closeModal(); - } - }; - - setModal(); - }); + const { handleDelete, canDeleteRoom } = useDeleteRoom(room); const onClickLeave = useMutableCallback(() => { const onConfirm = async (roomsLeft) => { @@ -174,7 +156,7 @@ const TeamsInfoWithLogic = ({ openEditing }) => { retentionPolicy={retentionPolicyEnabled && retentionPolicy} onClickEdit={canEdit && openEditing} onClickClose={closeTab} - onClickDelete={canDelete && onClickDelete} + onClickDelete={canDeleteRoom && handleDelete} onClickLeave={/* canLeave && */ onClickLeave} onClickHide={/* joined && */ handleHide} onClickViewChannels={onClickViewChannels} diff --git a/apps/meteor/package.json b/apps/meteor/package.json index c1014a749158..3ee3366f47dd 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -236,7 +236,7 @@ "@rocket.chat/favicon": "workspace:^", "@rocket.chat/forked-matrix-appservice-bridge": "^4.0.1", "@rocket.chat/forked-matrix-bot-sdk": "^0.6.0-beta.2", - "@rocket.chat/fuselage": "^0.32.2", + "@rocket.chat/fuselage": "^0.34.0", "@rocket.chat/fuselage-hooks": "^0.32.1", "@rocket.chat/fuselage-polyfills": "next", "@rocket.chat/fuselage-toastbar": "next", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 0a39692523eb..80ad18ad6743 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -2611,6 +2611,7 @@ "Install_FxOs_done": "Great! You can now use Rocket.Chat via the icon on your homescreen. Have fun with Rocket.Chat!", "Install_FxOs_error": "Sorry, that did not work as intended! The following error appeared:", "Install_FxOs_follow_instructions": "Please confirm the app installation on your device (press \"Install\" when prompted).", + "Installing": "Installing", "Install_package": "Install package", "Installation": "Installation", "Installed": "Installed", diff --git a/apps/meteor/server/services/video-conference/service.ts b/apps/meteor/server/services/video-conference/service.ts index 77cdc1cbd8e0..c9079b0a2bfb 100644 --- a/apps/meteor/server/services/video-conference/service.ts +++ b/apps/meteor/server/services/video-conference/service.ts @@ -618,6 +618,7 @@ export class VideoConfService extends ServiceClassInternal implements IVideoConf caller: call.createdBy, avatar: getUserAvatarURL(call.createdBy.username), status: call.status, + callId: call._id, }, userId: calleeId, notId: PushNotification.getNotificationId(`${call.rid}|${call._id}`), diff --git a/ee/packages/ui-theming/package.json b/ee/packages/ui-theming/package.json index 9508e2b8e41e..11aa5fd57ff8 100644 --- a/ee/packages/ui-theming/package.json +++ b/ee/packages/ui-theming/package.json @@ -4,7 +4,7 @@ "private": true, "devDependencies": { "@rocket.chat/css-in-js": "next", - "@rocket.chat/fuselage": "^0.32.2", + "@rocket.chat/fuselage": "^0.34.0", "@rocket.chat/fuselage-hooks": "^0.32.1", "@rocket.chat/icons": "^0.32.0", "@rocket.chat/ui-contexts": "workspace:~", diff --git a/ee/packages/ui-theming/src/palette.ts b/ee/packages/ui-theming/src/palette.ts index 39f8d3f2bfed..5d207d63bc21 100644 --- a/ee/packages/ui-theming/src/palette.ts +++ b/ee/packages/ui-theming/src/palette.ts @@ -46,8 +46,8 @@ export const palette = [ { name: 'font-white', token: 'white', color: '#FFFFFF' }, { name: 'font-disabled', token: 'N500', color: '#CBCED1' }, { name: 'font-annotation', token: 'N600', color: '#9EA2A8' }, - { name: 'font-hint', token: 'N700', color: '#6C727A' }, - { name: 'font-secondary-info', token: 'N700', color: '#6C727A' }, + { name: 'font-hint', token: 'N700', color: '#6C737A' }, + { name: 'font-secondary-info', token: 'N700', color: '#6C737A' }, { name: 'font-default', token: 'N800', color: '#2F343D' }, { name: 'font-titles-labels', token: 'N900', color: '#1F2329' }, { name: 'font-info', token: 'P600', color: '#095AD2' }, @@ -76,7 +76,7 @@ export const palette = [ { name: 'status-font-on-info', token: 'P600', color: '#095AD2' }, { name: 'status-font-on-success', token: 'S800', color: '#148660' }, { name: 'status-font-on-danger', token: 'D800', color: '#9B1325' }, - { name: 'status-font-on-warning', token: 'W900', color: '#B88D00' }, + { name: 'status-font-on-warning', token: 'W900', color: '#8E6300' }, { name: 'status-font-on-warning-2', token: 'N800', color: '#2F343D' }, { name: 'status-font-on-service-1', token: 'S1-800', color: '#974809' }, { name: 'status-font-on-service-2 ', token: 'S2-600', color: '#7F1B9F' }, @@ -88,22 +88,22 @@ export const palette = [ description: 'Badge Background', list: [ { name: 'badge-background-level-0', token: '', color: '#E4E7EA' }, - { name: 'badge-background-level-1', token: 'N700', color: '#6C727A' }, - { name: 'badge-background-level-2', token: '', color: '#1D74F5' }, + { name: 'badge-background-level-1', token: 'N700', color: '#6C737A' }, + { name: 'badge-background-level-2', token: '', color: '#156FF5' }, { name: 'badge-background-level-3', token: '', color: '#F38C39' }, - { name: 'badge-background-level-4', token: '', color: '#F5455C' }, + { name: 'badge-background-level-4', token: '', color: '#EC0D2A' }, ], }, { category: 'Status Bullet', description: 'Used to show user status', list: [ - { name: 'status-bullet-online', token: '', color: '#158D65' }, + { name: 'status-bullet-online', token: '', color: '#148660' }, { name: 'status-bullet-away', token: '', color: '#AC892F' }, - { name: 'status-bullet-busy', token: '', color: '#DA1F37' }, + { name: 'status-bullet-busy', token: '', color: '#D40C26' }, { name: 'status-bullet-disabled', token: '', color: '#F38C39' }, - { name: 'status-bullet-offline', token: '', color: '#AC892F' }, - { name: 'status-bullet-loading', token: '', color: '#9ea2a8' }, + { name: 'status-bullet-offline', token: '', color: '#6C737A' }, + { name: 'status-bullet-loading', token: '', color: '#6C737A' }, ], }, { @@ -122,7 +122,7 @@ export const palette = [ list: [ { name: 'button-background-primary-default', token: 'P500', color: '#156FF5' }, { name: 'button-background-primary-hover', token: 'P600', color: '#095AD2' }, - { name: 'button-background-primary-press', token: 'P700', color: '#095AD2' }, + { name: 'button-background-primary-press', token: 'P700', color: '#10529E' }, { name: 'button-background-primary-focus', token: 'P500', color: '#156FF5' }, { name: 'button-background-primary-keyfocus', token: 'P500', color: '#156FF5' }, { name: 'button-background-primary-disabled', token: 'P200', color: '#D1EBFE' }, @@ -133,7 +133,7 @@ export const palette = [ list: [ { name: 'button-background-secondary-default', token: 'N400', color: '#E4E7EA' }, { name: 'button-background-secondary-hover', token: 'N500', color: '#CBCED1' }, - { name: 'button-background-secondary-press', token: 'N600', color: '#CBCED1' }, + { name: 'button-background-secondary-press', token: 'N600', color: '#9EA2A8' }, { name: 'button-background-secondary-focus', token: 'N400', color: '#E4E7EA' }, { name: 'button-background-secondary-keyfocus', token: 'N400', color: '#E4E7EA' }, { name: 'button-background-secondary-disabled', token: 'N300', color: '#EEEFF1' }, @@ -144,7 +144,7 @@ export const palette = [ list: [ { name: 'button-background-secondary-danger-default', token: 'N400', color: '#E4E7EA' }, { name: 'button-background-secondary-danger-hover', token: 'N500', color: '#CBCED1' }, - { name: 'button-background-secondary-danger-press', token: 'N600', color: '#CBCED1' }, + { name: 'button-background-secondary-danger-press', token: 'N600', color: '#9EA2A8' }, { name: 'button-background-secondary-danger-focus', token: 'N400', color: '#E4E7EA' }, { name: 'button-background-secondary-danger-keyfocus', token: 'N400', color: '#E4E7EA' }, { name: 'button-background-secondary-danger-disabled', token: 'N300', color: '#EEEFF1' }, @@ -164,11 +164,11 @@ export const palette = [ { description: 'Success Background', list: [ - { name: 'button-background-success-default', token: '', color: '#158D65' }, + { name: 'button-background-success-default', token: '', color: '#148660' }, { name: 'button-background-success-hover', token: 'S900', color: '#106D4F' }, { name: 'button-background-success-press', token: 'S1000', color: '#0D5940' }, - { name: 'button-background-success-focus', token: '', color: '#158D65' }, - { name: 'button-background-success-keyfocus', token: '', color: '#158D65' }, + { name: 'button-background-success-focus', token: '', color: '#148660' }, + { name: 'button-background-success-keyfocus', token: '', color: '#148660' }, { name: 'button-background-success-disabled', token: 'S200', color: '#C0F6E4' }, ], }, @@ -179,7 +179,7 @@ export const palette = [ { name: 'button-font-on-primary-disabled', token: 'white', color: '#FFFFFF' }, { name: 'button-font-on-secondary', token: 'N900', color: '#1F2329' }, { name: 'button-font-on-secondary-disabled', token: 'N600', color: '#CBCED1' }, - { name: 'button-font-on-secondary-danger', token: 'D900', color: '#BB0B21' }, + { name: 'button-font-on-secondary-danger', token: '', color: '#BB0B21' }, { name: 'button-font-on-secondary-danger-disabled', token: 'D300', @@ -187,7 +187,7 @@ export const palette = [ }, { name: 'button-font-on-danger', token: 'white', color: '#FFFFFF' }, { name: 'button-font-on-danger-disabled', token: 'white', color: '#FFFFFF' }, - { name: 'button-font-on-success', token: '', color: '#EBECEF' }, + { name: 'button-font-on-success', token: '', color: '#FFFFFF' }, { name: 'button-font-on-success-disabled', token: 'white', color: '#FFFFFF' }, ], }, diff --git a/ee/packages/ui-theming/src/paletteDark.ts b/ee/packages/ui-theming/src/paletteDark.ts index cdb60efffeac..89ac7817be42 100644 --- a/ee/packages/ui-theming/src/paletteDark.ts +++ b/ee/packages/ui-theming/src/paletteDark.ts @@ -9,7 +9,7 @@ export const palette = [ { name: 'stroke-dark', token: 'N600', color: '#9EA2A8' }, { name: 'stroke-extra-dark', token: 'N400', color: '#CBCED1' }, { name: 'stroke-extra-light-highlight', token: '', color: '#87CBFC' }, - { name: 'stroke-highlight', token: '', color: '#3976D1' }, + { name: 'stroke-highlight', token: '', color: '#6292DA' }, { name: 'stroke-extra-light-error', token: '', color: '#F49AA6' }, { name: 'stroke-error', token: '', color: '#BB3E4E' }, ], diff --git a/packages/fuselage-ui-kit/package.json b/packages/fuselage-ui-kit/package.json index 83ab677e2a9f..4555216c2d44 100644 --- a/packages/fuselage-ui-kit/package.json +++ b/packages/fuselage-ui-kit/package.json @@ -56,7 +56,7 @@ "devDependencies": { "@rocket.chat/apps-engine": "1.41.0-alpha.290", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.32.2", + "@rocket.chat/fuselage": "^0.34.0", "@rocket.chat/fuselage-hooks": "^0.32.1", "@rocket.chat/fuselage-polyfills": "next", "@rocket.chat/icons": "^0.32.0", diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index 89136a296f9d..e891e5677c75 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.9", "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/css-in-js": "next", - "@rocket.chat/fuselage": "^0.32.2", + "@rocket.chat/fuselage": "^0.34.0", "@rocket.chat/fuselage-tokens": "next", "@rocket.chat/message-parser": "next", "@rocket.chat/styled": "next", diff --git a/packages/ui-client/package.json b/packages/ui-client/package.json index fa227575ccc2..f8ef2d3a1e93 100644 --- a/packages/ui-client/package.json +++ b/packages/ui-client/package.json @@ -5,7 +5,7 @@ "devDependencies": { "@babel/core": "~7.22.9", "@rocket.chat/css-in-js": "next", - "@rocket.chat/fuselage": "^0.32.2", + "@rocket.chat/fuselage": "^0.34.0", "@rocket.chat/fuselage-hooks": "^0.32.1", "@rocket.chat/icons": "^0.32.0", "@rocket.chat/mock-providers": "workspace:^", diff --git a/packages/ui-composer/package.json b/packages/ui-composer/package.json index 1d5d478e17cd..b5c804b4a2ad 100644 --- a/packages/ui-composer/package.json +++ b/packages/ui-composer/package.json @@ -5,7 +5,7 @@ "devDependencies": { "@babel/core": "~7.22.9", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.32.2", + "@rocket.chat/fuselage": "^0.34.0", "@rocket.chat/icons": "^0.32.0", "@storybook/addon-actions": "~6.5.16", "@storybook/addon-docs": "~6.5.16", diff --git a/packages/ui-video-conf/package.json b/packages/ui-video-conf/package.json index d253b61cc1b0..7ca7b1d86140 100644 --- a/packages/ui-video-conf/package.json +++ b/packages/ui-video-conf/package.json @@ -6,7 +6,7 @@ "@babel/core": "~7.22.9", "@rocket.chat/css-in-js": "next", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.32.2", + "@rocket.chat/fuselage": "^0.34.0", "@rocket.chat/fuselage-hooks": "^0.32.1", "@rocket.chat/icons": "^0.32.0", "@rocket.chat/styled": "next", diff --git a/packages/uikit-playground/package.json b/packages/uikit-playground/package.json index 5e8e3bf2cc5c..5a8b1276defb 100644 --- a/packages/uikit-playground/package.json +++ b/packages/uikit-playground/package.json @@ -15,7 +15,7 @@ "@codemirror/tooltip": "^0.19.16", "@lezer/highlight": "^1.1.6", "@rocket.chat/css-in-js": "next", - "@rocket.chat/fuselage": "^0.32.2", + "@rocket.chat/fuselage": "^0.34.0", "@rocket.chat/fuselage-hooks": "^0.32.1", "@rocket.chat/fuselage-polyfills": "next", "@rocket.chat/fuselage-tokens": "next", diff --git a/yarn.lock b/yarn.lock index 7d75c388dc29..b4e4af200f30 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8273,7 +8273,7 @@ __metadata: dependencies: "@rocket.chat/apps-engine": 1.41.0-alpha.290 "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.32.2 + "@rocket.chat/fuselage": ^0.34.0 "@rocket.chat/fuselage-hooks": ^0.32.1 "@rocket.chat/fuselage-polyfills": next "@rocket.chat/gazzodown": "workspace:^" @@ -8322,9 +8322,9 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/fuselage@npm:^0.32.2": - version: 0.32.2 - resolution: "@rocket.chat/fuselage@npm:0.32.2" +"@rocket.chat/fuselage@npm:^0.34.0": + version: 0.34.0 + resolution: "@rocket.chat/fuselage@npm:0.34.0" dependencies: "@rocket.chat/css-in-js": ^0.31.25 "@rocket.chat/css-supports": ^0.31.25 @@ -8342,7 +8342,7 @@ __metadata: react: ^17.0.2 react-dom: ^17.0.2 react-virtuoso: 1.2.4 - checksum: 28e80385961b090c71d0897c22c3c799ca05d30285456d96d3ca5ff2a1a4ba02362644084e611bd3f2a376acdf4c2e75180b8aee196a63969a7d6559abd73d79 + checksum: 72cd1dd7ef13cc3b69fadac5c064a45cd2b65b8a221cde2e8149fa873ac6de89648c677caedb10979e5cf08d39b79f1d7a30caa6378bdeeb873414c7fbac5e6e languageName: node linkType: hard @@ -8353,7 +8353,7 @@ __metadata: "@babel/core": ~7.22.9 "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/css-in-js": next - "@rocket.chat/fuselage": ^0.32.2 + "@rocket.chat/fuselage": ^0.34.0 "@rocket.chat/fuselage-tokens": next "@rocket.chat/message-parser": next "@rocket.chat/styled": next @@ -8706,7 +8706,7 @@ __metadata: "@rocket.chat/favicon": "workspace:^" "@rocket.chat/forked-matrix-appservice-bridge": ^4.0.1 "@rocket.chat/forked-matrix-bot-sdk": ^0.6.0-beta.2 - "@rocket.chat/fuselage": ^0.32.2 + "@rocket.chat/fuselage": ^0.34.0 "@rocket.chat/fuselage-hooks": ^0.32.1 "@rocket.chat/fuselage-polyfills": next "@rocket.chat/fuselage-toastbar": next @@ -9569,7 +9569,7 @@ __metadata: dependencies: "@babel/core": ~7.22.9 "@rocket.chat/css-in-js": next - "@rocket.chat/fuselage": ^0.32.2 + "@rocket.chat/fuselage": ^0.34.0 "@rocket.chat/fuselage-hooks": ^0.32.1 "@rocket.chat/icons": ^0.32.0 "@rocket.chat/mock-providers": "workspace:^" @@ -9620,7 +9620,7 @@ __metadata: dependencies: "@babel/core": ~7.22.9 "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.32.2 + "@rocket.chat/fuselage": ^0.34.0 "@rocket.chat/icons": ^0.32.0 "@storybook/addon-actions": ~6.5.16 "@storybook/addon-docs": ~6.5.16 @@ -9691,7 +9691,7 @@ __metadata: resolution: "@rocket.chat/ui-theming@workspace:ee/packages/ui-theming" dependencies: "@rocket.chat/css-in-js": next - "@rocket.chat/fuselage": ^0.32.2 + "@rocket.chat/fuselage": ^0.34.0 "@rocket.chat/fuselage-hooks": ^0.32.1 "@rocket.chat/icons": ^0.32.0 "@rocket.chat/ui-contexts": "workspace:~" @@ -9734,7 +9734,7 @@ __metadata: "@rocket.chat/css-in-js": next "@rocket.chat/emitter": next "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.32.2 + "@rocket.chat/fuselage": ^0.34.0 "@rocket.chat/fuselage-hooks": ^0.32.1 "@rocket.chat/icons": ^0.32.0 "@rocket.chat/styled": next @@ -9777,7 +9777,7 @@ __metadata: "@codemirror/tooltip": ^0.19.16 "@lezer/highlight": ^1.1.6 "@rocket.chat/css-in-js": next - "@rocket.chat/fuselage": ^0.32.2 + "@rocket.chat/fuselage": ^0.34.0 "@rocket.chat/fuselage-hooks": ^0.32.1 "@rocket.chat/fuselage-polyfills": next "@rocket.chat/fuselage-tokens": next