-
+ {/* eslint-disable-next-line
+ jsx-a11y/no-noninteractive-element-interactions,
+ jsx-a11y/click-events-have-key-events
+ */}
+
diff --git a/apps/meteor/client/components/ImageGallery/ImageGalleryError.tsx b/apps/meteor/client/components/ImageGallery/ImageGalleryError.tsx
index b0c85ed13572..97d91de95f62 100644
--- a/apps/meteor/client/components/ImageGallery/ImageGalleryError.tsx
+++ b/apps/meteor/client/components/ImageGallery/ImageGalleryError.tsx
@@ -19,7 +19,7 @@ export const ImageGalleryError = ({ onClose }: { onClose: () => void }) => {
return createPortal(
-
+
,
document.body,
);
diff --git a/apps/meteor/client/components/ImageGallery/ImageGalleryLoading.tsx b/apps/meteor/client/components/ImageGallery/ImageGalleryLoading.tsx
index 6e5f2d0463b8..1c057584bd1f 100644
--- a/apps/meteor/client/components/ImageGallery/ImageGalleryLoading.tsx
+++ b/apps/meteor/client/components/ImageGallery/ImageGalleryLoading.tsx
@@ -1,5 +1,6 @@
import { css } from '@rocket.chat/css-in-js';
import { IconButton, ModalBackdrop, Throbber } from '@rocket.chat/fuselage';
+import { useTranslation } from '@rocket.chat/ui-contexts';
import React from 'react';
import { createPortal } from 'react-dom';
@@ -10,11 +11,14 @@ const closeButtonStyle = css`
right: 10px;
`;
-export const ImageGalleryLoading = ({ onClose }: { onClose: () => void }) =>
- createPortal(
+export const ImageGalleryLoading = ({ onClose }: { onClose: () => void }) => {
+ const t = useTranslation();
+
+ return createPortal(
-
+
,
document.body,
);
+};
diff --git a/apps/meteor/client/components/InfoPanel/InfoPanelActionGroup.tsx b/apps/meteor/client/components/InfoPanel/InfoPanelActionGroup.tsx
index 3bf9f7e33c70..00af64b0fa61 100644
--- a/apps/meteor/client/components/InfoPanel/InfoPanelActionGroup.tsx
+++ b/apps/meteor/client/components/InfoPanel/InfoPanelActionGroup.tsx
@@ -6,7 +6,7 @@ import Section from './InfoPanelSection';
const InfoPanelActionGroup: FC
> = (props) => (
);
diff --git a/apps/meteor/client/components/modal/ModalBackdrop.tsx b/apps/meteor/client/components/ModalBackdrop.tsx
similarity index 100%
rename from apps/meteor/client/components/modal/ModalBackdrop.tsx
rename to apps/meteor/client/components/ModalBackdrop.tsx
diff --git a/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx b/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx
index 5e65c43a957c..4a937b485f36 100644
--- a/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx
+++ b/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx
@@ -10,6 +10,7 @@ import {
Divider,
FieldLabel,
FieldRow,
+ Option,
} from '@rocket.chat/fuselage';
import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
import { useEndpoint, useSetting, useTranslation } from '@rocket.chat/ui-contexts';
@@ -36,7 +37,15 @@ const ForwardChatModal = ({
const getUserData = useEndpoint('GET', '/v1/users.info');
const idleAgentsAllowedForForwarding = useSetting('Livechat_enabled_when_agent_idle') as boolean;
- const { getValues, handleSubmit, register, setFocus, setValue, watch } = useForm();
+ const {
+ getValues,
+ handleSubmit,
+ register,
+ setFocus,
+ setValue,
+ watch,
+ formState: { isSubmitting },
+ } = useForm();
useEffect(() => {
setFocus('comment');
@@ -71,7 +80,7 @@ const ForwardChatModal = ({
uid = user?._id;
}
- onForward(departmentId, uid, comment);
+ await onForward(departmentId, uid, comment);
},
[getUserData, onForward],
);
@@ -98,7 +107,7 @@ const ForwardChatModal = ({
{t('Forward_to_department')}
@@ -146,7 +156,7 @@ const ForwardChatModal = ({
-
diff --git a/apps/meteor/client/components/connectionStatus/ConnectionStatusBar.tsx b/apps/meteor/client/components/connectionStatus/ConnectionStatusBar.tsx
index 6046aecc0bae..5a26392b60b9 100644
--- a/apps/meteor/client/components/connectionStatus/ConnectionStatusBar.tsx
+++ b/apps/meteor/client/components/connectionStatus/ConnectionStatusBar.tsx
@@ -1,11 +1,37 @@
-import { Box, Icon } from '@rocket.chat/fuselage';
+import { css } from '@rocket.chat/css-in-js';
+import { Box, Button, Icon, Palette } from '@rocket.chat/fuselage';
import { useConnectionStatus } from '@rocket.chat/ui-contexts';
-import type { MouseEvent } from 'react';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useReconnectCountdown } from './useReconnectCountdown';
+const connectionStatusBarStyle = css`
+ color: ${Palette.statusColor['status-font-on-warning']};
+ background-color: ${Palette.surface['surface-tint']};
+ border-color: ${Palette.statusColor['status-font-on-warning']};
+
+ position: fixed;
+ z-index: 1000000;
+
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ .rcx-connection-status-bar--wrapper {
+ display: flex;
+ align-items: center;
+ column-gap: 8px;
+ }
+ .rcx-connection-status-bar--content {
+ display: flex;
+ align-items: center;
+ column-gap: 8px;
+ }
+ .rcx-connection-status-bar--info {
+ color: ${Palette.text['font-default']};
+ }
+`;
function ConnectionStatusBar() {
const { connected, retryTime, status, reconnect } = useConnectionStatus();
const reconnectCountdown = useReconnectCountdown(retryTime, status);
@@ -15,37 +41,32 @@ function ConnectionStatusBar() {
return null;
}
- const handleRetryClick = (event: MouseEvent) => {
- event.preventDefault();
- reconnect?.();
- };
-
return (
- {' '}
-
- {t('meteor_status', { context: status })}
- {status === 'waiting' && <> {t('meteor_status_reconnect_in', { count: reconnectCountdown })}>}
- {['waiting', 'offline'].includes(status) && (
- <>
- {' '}
-
- {t('meteor_status_try_now', { context: status })}
-
- >
- )}
-
+
+
+
+ {t('meteor_status', { context: status })}
+ {['waiting', 'failed', 'offline'].includes(status) && (
+
+ {status === 'waiting' ? t('meteor_status_reconnect_in', { count: reconnectCountdown }) : t('meteor_status_try_again_later')}
+
+ )}
+
+
+
+ {t('Connect')}
+
);
}
diff --git a/apps/meteor/client/components/message/IgnoredContent.tsx b/apps/meteor/client/components/message/IgnoredContent.tsx
index b1973b13178a..b2dd7e726667 100644
--- a/apps/meteor/client/components/message/IgnoredContent.tsx
+++ b/apps/meteor/client/components/message/IgnoredContent.tsx
@@ -19,9 +19,15 @@ const IgnoredContent = ({ onShowMessageIgnored }: IgnoredContentProps): ReactEle
return (
-
+ e.code === 'Enter' && showMessageIgnored(e)}
+ style={{ cursor: 'pointer' }}
+ >
{t('Message_Ignored')}
-
+
);
diff --git a/apps/meteor/client/components/message/content/attachments/structure/AttachmentImage.tsx b/apps/meteor/client/components/message/content/attachments/structure/AttachmentImage.tsx
index a82e3a7daa5e..5b251ad6143f 100644
--- a/apps/meteor/client/components/message/content/attachments/structure/AttachmentImage.tsx
+++ b/apps/meteor/client/components/message/content/attachments/structure/AttachmentImage.tsx
@@ -83,6 +83,7 @@ const AttachmentImage: FC = ({ id, previewUrl, dataSrc, lo
className='gallery-item'
data-src={dataSrc || src}
src={src}
+ alt=''
width={dimensions.width}
height={dimensions.height}
/>
diff --git a/apps/meteor/client/components/message/content/urlPreviews/UrlVideoPreview.tsx b/apps/meteor/client/components/message/content/urlPreviews/UrlVideoPreview.tsx
index 87fd065a92f0..2827d50b9198 100644
--- a/apps/meteor/client/components/message/content/urlPreviews/UrlVideoPreview.tsx
+++ b/apps/meteor/client/components/message/content/urlPreviews/UrlVideoPreview.tsx
@@ -8,6 +8,7 @@ const UrlVideoPreview = ({ url, originalType }: Omit
);
diff --git a/apps/meteor/client/components/message/variants/SystemMessage.tsx b/apps/meteor/client/components/message/variants/SystemMessage.tsx
index f9d5747a0ff9..e2e1d9bf04bd 100644
--- a/apps/meteor/client/components/message/variants/SystemMessage.tsx
+++ b/apps/meteor/client/components/message/variants/SystemMessage.tsx
@@ -86,7 +86,12 @@ const SystemMessage = ({ message, showUserAvatar, ...props }: SystemMessageProps
{...triggerProps}
>
{getUserDisplayName(user.name, user.username, showRealName)}
- {showUsername && @{user.username}}
+ {showUsername && (
+ <>
+ {' '}
+ @{user.username}
+ >
+ )}
{messageType && (
import('../../views/teams/contextualBar/channels/TeamsChannels'));
+const TeamsChannels = lazy(() => import('../../views/teams/contextualBar/channels'));
export const useTeamChannelsRoomAction = () => {
return useMemo(
diff --git a/apps/meteor/client/hooks/useContinuousSoundNotification.ts b/apps/meteor/client/hooks/useContinuousSoundNotification.ts
new file mode 100644
index 000000000000..22fcabd4e6a9
--- /dev/null
+++ b/apps/meteor/client/hooks/useContinuousSoundNotification.ts
@@ -0,0 +1,53 @@
+import type { ICustomSound } from '@rocket.chat/core-typings';
+import { useSetting, useUserPreference, useUserSubscriptions } from '@rocket.chat/ui-contexts';
+import { useEffect } from 'react';
+
+import { CustomSounds } from '../../app/custom-sounds/client/lib/CustomSounds';
+
+const query = { t: 'l', ls: { $exists: false }, open: true };
+export const useContinuousSoundNotification = () => {
+ const userSubscriptions = useUserSubscriptions(query);
+
+ const playNewRoomSoundContinuously = useSetting('Livechat_continuous_sound_notification_new_livechat_room');
+
+ const newRoomNotification = useUserPreference('newRoomNotification');
+ const audioVolume = useUserPreference('notificationsSoundVolume');
+
+ const continuousCustomSoundId = newRoomNotification && `${newRoomNotification}-continuous`;
+
+ const volume = audioVolume !== undefined ? Number((audioVolume / 100).toPrecision(2)) : 1;
+
+ useEffect(() => {
+ let audio: ICustomSound;
+ if (playNewRoomSoundContinuously && continuousCustomSoundId) {
+ audio = { ...CustomSounds.getSound(newRoomNotification), _id: continuousCustomSoundId };
+ CustomSounds.add(audio);
+ }
+
+ return () => {
+ if (audio) {
+ CustomSounds.remove(audio);
+ }
+ };
+ }, [continuousCustomSoundId, newRoomNotification, playNewRoomSoundContinuously]);
+
+ useEffect(() => {
+ if (!continuousCustomSoundId) {
+ return;
+ }
+ if (!playNewRoomSoundContinuously) {
+ CustomSounds.pause(continuousCustomSoundId);
+ return;
+ }
+
+ if (userSubscriptions.length === 0) {
+ CustomSounds.pause(continuousCustomSoundId);
+ return;
+ }
+
+ CustomSounds.play(continuousCustomSoundId, {
+ volume,
+ loop: true,
+ });
+ }, [continuousCustomSoundId, playNewRoomSoundContinuously, userSubscriptions, volume]);
+};
diff --git a/apps/meteor/client/hooks/usePreventPropagation.ts b/apps/meteor/client/hooks/usePreventPropagation.ts
index a444fae2856f..6d9f38ad0dbf 100644
--- a/apps/meteor/client/hooks/usePreventPropagation.ts
+++ b/apps/meteor/client/hooks/usePreventPropagation.ts
@@ -1,8 +1,8 @@
-import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
-import type { MouseEvent } from 'react';
+import { useEffectEvent } from '@rocket.chat/fuselage-hooks';
+import type { UIEvent } from 'react';
-export const usePreventPropagation = (fn?: (e: MouseEvent) => void): ((e: MouseEvent) => void) => {
- const preventClickPropagation = useMutableCallback((e): void => {
+export const usePreventPropagation = (fn?: (e: UIEvent) => void): ((e: UIEvent) => void) => {
+ const preventClickPropagation = useEffectEvent((e): void => {
e.stopPropagation();
fn?.(e);
});
diff --git a/apps/meteor/client/lib/lists/ImagesList.ts b/apps/meteor/client/lib/lists/ImagesList.ts
index 540d5bb1c0f7..cefb1bec9fc5 100644
--- a/apps/meteor/client/lib/lists/ImagesList.ts
+++ b/apps/meteor/client/lib/lists/ImagesList.ts
@@ -6,7 +6,7 @@ type FilesMessage = Omit & Required>;
export type ImagesListOptions = {
roomId: Required['rid'];
- startingFromId: string;
+ startingFromId?: string;
count?: number;
offset?: number;
};
diff --git a/apps/meteor/client/components/modal/ModalPortal.tsx b/apps/meteor/client/portals/ModalPortal.tsx
similarity index 75%
rename from apps/meteor/client/components/modal/ModalPortal.tsx
rename to apps/meteor/client/portals/ModalPortal.tsx
index 577f89e72103..d7c9ae9caa2d 100644
--- a/apps/meteor/client/components/modal/ModalPortal.tsx
+++ b/apps/meteor/client/portals/ModalPortal.tsx
@@ -2,16 +2,13 @@ import type { ReactElement, ReactNode } from 'react';
import React, { memo, useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
-import { createAnchor } from '../../lib/utils/createAnchor';
-import { deleteAnchor } from '../../lib/utils/deleteAnchor';
+import { createAnchor } from '../lib/utils/createAnchor';
+import { deleteAnchor } from '../lib/utils/deleteAnchor';
type ModalPortalProps = {
children?: ReactNode;
};
-/**
- * @todo: move to portals folder
- */
const ModalPortal = ({ children }: ModalPortalProps): ReactElement => {
const [modalRoot] = useState(() => createAnchor('modal-root'));
useEffect(() => (): void => deleteAnchor(modalRoot), [modalRoot]);
diff --git a/apps/meteor/client/components/TooltipPortal.tsx b/apps/meteor/client/portals/TooltipPortal.tsx
similarity index 87%
rename from apps/meteor/client/components/TooltipPortal.tsx
rename to apps/meteor/client/portals/TooltipPortal.tsx
index 937f6ed879ca..2ee0830313c4 100644
--- a/apps/meteor/client/components/TooltipPortal.tsx
+++ b/apps/meteor/client/portals/TooltipPortal.tsx
@@ -6,7 +6,7 @@ import { createAnchor } from '../lib/utils/createAnchor';
import { deleteAnchor } from '../lib/utils/deleteAnchor';
const TooltipPortal: FC = ({ children }) => {
- const [tooltipRoot] = useState(() => createAnchor('react-tooltip'));
+ const [tooltipRoot] = useState(() => createAnchor('tooltip-root'));
useEffect(() => (): void => deleteAnchor(tooltipRoot), [tooltipRoot]);
return <>{createPortal(children, tooltipRoot)}>;
};
diff --git a/apps/meteor/client/providers/CallProvider/CallProvider.tsx b/apps/meteor/client/providers/CallProvider/CallProvider.tsx
index ce0d9385dc64..0ede8072d8bc 100644
--- a/apps/meteor/client/providers/CallProvider/CallProvider.tsx
+++ b/apps/meteor/client/providers/CallProvider/CallProvider.tsx
@@ -526,7 +526,13 @@ export const CallProvider: FC = ({ children }) => {
return (
{children}
- {contextValue.enabled && createPortal(, document.body)}
+ {contextValue.enabled &&
+ createPortal(
+ ,
+ document.body,
+ )}
);
};
diff --git a/apps/meteor/client/providers/CustomSoundProvider.tsx b/apps/meteor/client/providers/CustomSoundProvider.tsx
index cb2d2933117f..5e1b3944b6e8 100644
--- a/apps/meteor/client/providers/CustomSoundProvider.tsx
+++ b/apps/meteor/client/providers/CustomSoundProvider.tsx
@@ -3,6 +3,7 @@ import type { FC } from 'react';
import React, { useEffect } from 'react';
import { CustomSounds } from '../../app/custom-sounds/client/lib/CustomSounds';
+import { useContinuousSoundNotification } from '../hooks/useContinuousSoundNotification';
const CustomSoundProvider: FC = ({ children }) => {
const userId = useUserId();
@@ -13,6 +14,8 @@ const CustomSoundProvider: FC = ({ children }) => {
void CustomSounds.fetchCustomSoundList();
}, [userId]);
+ useContinuousSoundNotification();
+
const streamAll = useStream('notify-all');
useEffect(() => {
diff --git a/apps/meteor/client/providers/TooltipProvider.tsx b/apps/meteor/client/providers/TooltipProvider.tsx
index 0fc7c996b9f3..4cc9aa3a767c 100644
--- a/apps/meteor/client/providers/TooltipProvider.tsx
+++ b/apps/meteor/client/providers/TooltipProvider.tsx
@@ -4,7 +4,7 @@ import { TooltipContext } from '@rocket.chat/ui-contexts';
import type { FC, ReactNode } from 'react';
import React, { useEffect, useMemo, useRef, memo, useCallback, useState } from 'react';
-import TooltipPortal from '../components/TooltipPortal';
+import TooltipPortal from '../portals/TooltipPortal';
const TooltipProvider: FC = ({ children }) => {
const lastAnchor = useRef();
diff --git a/apps/meteor/client/providers/TranslationProvider.tsx b/apps/meteor/client/providers/TranslationProvider.tsx
index 7f98c374f949..2f31d282a63b 100644
--- a/apps/meteor/client/providers/TranslationProvider.tsx
+++ b/apps/meteor/client/providers/TranslationProvider.tsx
@@ -6,7 +6,6 @@ import type { TranslationContextValue } from '@rocket.chat/ui-contexts';
import { useMethod, useSetting, TranslationContext } from '@rocket.chat/ui-contexts';
import type i18next from 'i18next';
import I18NextHttpBackend from 'i18next-http-backend';
-import sprintf from 'i18next-sprintf-postprocessor';
import moment from 'moment';
import type { ReactElement, ReactNode } from 'react';
import React, { useEffect, useMemo } from 'react';
@@ -26,7 +25,7 @@ import {
import { AppClientOrchestratorInstance } from '../../ee/client/apps/orchestrator';
import { isRTLScriptLanguage } from '../lib/utils/isRTLScriptLanguage';
-i18n.use(I18NextHttpBackend).use(initReactI18next).use(sprintf);
+i18n.use(I18NextHttpBackend).use(initReactI18next);
const useCustomTranslations = (i18n: typeof i18next) => {
const customTranslations = useSetting('Custom_Translations');
@@ -64,9 +63,13 @@ const useCustomTranslations = (i18n: typeof i18next) => {
};
const localeCache = new Map>();
+let isI18nInitialized = false;
const useI18next = (lng: string): typeof i18next => {
- if (!i18n.isInitialized) {
+ // i18n.init is async, so there's a chance a race condition happens and it is initialized twice
+ // This breaks translations because it loads `lng` in the first init but not the second.
+ if (!isI18nInitialized) {
+ isI18nInitialized = true;
i18n.init({
lng,
fallbackLng: 'en',
diff --git a/apps/meteor/client/sidebar/RoomList/RoomList.tsx b/apps/meteor/client/sidebar/RoomList/RoomList.tsx
index 8dc44dcb73ad..4bd7338f9168 100644
--- a/apps/meteor/client/sidebar/RoomList/RoomList.tsx
+++ b/apps/meteor/client/sidebar/RoomList/RoomList.tsx
@@ -15,6 +15,8 @@ import { useRoomList } from '../hooks/useRoomList';
import { useShortcutOpenMenu } from '../hooks/useShortcutOpenMenu';
import { useTemplateByViewMode } from '../hooks/useTemplateByViewMode';
import RoomListRow from './RoomListRow';
+import RoomListRowWrapper from './RoomListRowWrapper';
+import RoomListWrapper from './RoomListWrapper';
const computeItemKey = (index: number, room: IRoom): IRoom['_id'] | number => room._id || index;
@@ -116,12 +118,12 @@ const RoomList = (): ReactElement => {
`;
return (
-
+
}
/>
diff --git a/apps/meteor/client/sidebar/RoomList/RoomListRowWrapper.tsx b/apps/meteor/client/sidebar/RoomList/RoomListRowWrapper.tsx
new file mode 100644
index 000000000000..e2c8f90b6953
--- /dev/null
+++ b/apps/meteor/client/sidebar/RoomList/RoomListRowWrapper.tsx
@@ -0,0 +1,8 @@
+import type { HTMLAttributes, Ref } from 'react';
+import React, { forwardRef } from 'react';
+
+const RoomListRoomWrapper = forwardRef(function RoomListRoomWrapper(props: HTMLAttributes, ref: Ref) {
+ return ;
+});
+
+export default RoomListRoomWrapper;
diff --git a/apps/meteor/client/sidebar/RoomList/RoomListWrapper.tsx b/apps/meteor/client/sidebar/RoomList/RoomListWrapper.tsx
new file mode 100644
index 000000000000..6779fb8d6c82
--- /dev/null
+++ b/apps/meteor/client/sidebar/RoomList/RoomListWrapper.tsx
@@ -0,0 +1,16 @@
+import { useMergedRefs } from '@rocket.chat/fuselage-hooks';
+import { useTranslation } from '@rocket.chat/ui-contexts';
+import type { HTMLAttributes, Ref } from 'react';
+import React, { forwardRef } from 'react';
+
+import { useSidebarListNavigation } from './useSidebarListNavigation';
+
+const RoomListWrapper = forwardRef(function RoomListWrapper(props: HTMLAttributes, ref: Ref) {
+ const t = useTranslation();
+ const { sidebarListRef } = useSidebarListNavigation();
+ const mergedRefs = useMergedRefs(ref, sidebarListRef);
+
+ return ;
+});
+
+export default RoomListWrapper;
diff --git a/apps/meteor/client/sidebar/RoomList/useSidebarListNavigation.ts b/apps/meteor/client/sidebar/RoomList/useSidebarListNavigation.ts
new file mode 100644
index 000000000000..f5c2d00d4b2c
--- /dev/null
+++ b/apps/meteor/client/sidebar/RoomList/useSidebarListNavigation.ts
@@ -0,0 +1,99 @@
+import { useFocusManager } from '@react-aria/focus';
+import { useCallback } from 'react';
+
+const isListItem = (node: EventTarget) => (node as HTMLElement).classList.contains('rcx-sidebar-item');
+const isListItemMenu = (node: EventTarget) => (node as HTMLElement).classList.contains('rcx-sidebar-item__menu');
+
+/**
+ * Custom hook to provide the sidebar navigation by keyboard.
+ * @param ref - A ref to the message list DOM element.
+ */
+export const useSidebarListNavigation = () => {
+ const sidebarListFocusManager = useFocusManager();
+
+ const sidebarListRef = useCallback(
+ (node: HTMLElement | null) => {
+ let lastItemFocused: HTMLElement | null = null;
+
+ if (!node) {
+ return;
+ }
+
+ node.addEventListener('keydown', (e) => {
+ if (!e.target) {
+ return;
+ }
+
+ if (!isListItem(e.target)) {
+ return;
+ }
+
+ if (e.key === 'Tab') {
+ e.preventDefault();
+ e.stopPropagation();
+
+ if (e.shiftKey) {
+ sidebarListFocusManager.focusPrevious({
+ accept: (node) => !isListItem(node) && !isListItemMenu(node),
+ });
+ } else if (isListItemMenu(e.target)) {
+ sidebarListFocusManager.focusNext({
+ accept: (node) => !isListItem(node) && !isListItemMenu(node),
+ });
+ } else {
+ sidebarListFocusManager.focusNext({
+ accept: (node) => !isListItem(node),
+ });
+ }
+ }
+
+ if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
+ if (e.key === 'ArrowUp') {
+ sidebarListFocusManager.focusPrevious({ accept: (node) => isListItem(node) });
+ }
+
+ if (e.key === 'ArrowDown') {
+ sidebarListFocusManager.focusNext({ accept: (node) => isListItem(node) });
+ }
+
+ lastItemFocused = document.activeElement as HTMLElement;
+ }
+ });
+
+ node.addEventListener(
+ 'blur',
+ (e) => {
+ if (
+ !(e.relatedTarget as HTMLElement)?.classList.contains('focus-visible') ||
+ !(e.currentTarget instanceof HTMLElement && e.relatedTarget instanceof HTMLElement)
+ ) {
+ return;
+ }
+
+ if (!e.currentTarget.contains(e.relatedTarget) && !lastItemFocused) {
+ lastItemFocused = e.target as HTMLElement;
+ }
+ },
+ { capture: true },
+ );
+
+ node.addEventListener(
+ 'focus',
+ (e) => {
+ const triggeredByKeyboard = (e.target as HTMLElement)?.classList.contains('focus-visible');
+ if (!triggeredByKeyboard || !(e.currentTarget instanceof HTMLElement && e.relatedTarget instanceof HTMLElement)) {
+ return;
+ }
+
+ if (lastItemFocused && !e.currentTarget.contains(e.relatedTarget) && node.contains(e.target as HTMLElement)) {
+ lastItemFocused?.focus();
+ }
+ },
+ { capture: true },
+ );
+ },
+ [sidebarListFocusManager],
+ );
+
+ return { sidebarListRef };
+};
diff --git a/apps/meteor/client/sidebar/RoomMenu.tsx b/apps/meteor/client/sidebar/RoomMenu.tsx
index be8d889f3065..a1fd7400e88f 100644
--- a/apps/meteor/client/sidebar/RoomMenu.tsx
+++ b/apps/meteor/client/sidebar/RoomMenu.tsx
@@ -251,7 +251,6 @@ const RoomMenu = ({
title={t('Options')}
mini
aria-keyshortcuts='alt'
- tabIndex={-1}
options={menuOptions}
maxHeight={300}
renderItem={({ label: { label, icon }, ...props }): JSX.Element => }
diff --git a/apps/meteor/client/sidebar/Sidebar.tsx b/apps/meteor/client/sidebar/Sidebar.tsx
index 19606854d0ed..b947554f4f3b 100644
--- a/apps/meteor/client/sidebar/Sidebar.tsx
+++ b/apps/meteor/client/sidebar/Sidebar.tsx
@@ -27,28 +27,25 @@ const Sidebar = () => {
`;
return (
- <>
-
-
- {presenceDisabled && !bannerDismissed && setBannerDismissed(true)} />}
- {showOmnichannel && }
-
-
-
- >
+
+
+ {presenceDisabled && !bannerDismissed && setBannerDismissed(true)} />}
+ {showOmnichannel && }
+
+
+
);
};
diff --git a/apps/meteor/client/sidebar/SidebarRegion.tsx b/apps/meteor/client/sidebar/SidebarRegion.tsx
index 1e68e610ba78..c3663bb7d359 100644
--- a/apps/meteor/client/sidebar/SidebarRegion.tsx
+++ b/apps/meteor/client/sidebar/SidebarRegion.tsx
@@ -3,6 +3,7 @@ import { Box } from '@rocket.chat/fuselage';
import { FeaturePreview, FeaturePreviewOff, FeaturePreviewOn } from '@rocket.chat/ui-client';
import { useLayout } from '@rocket.chat/ui-contexts';
import React, { lazy, memo } from 'react';
+import { FocusScope } from 'react-aria';
import Sidebar from './Sidebar';
@@ -101,17 +102,19 @@ const SidebarRegion = () => {
<>>
-
- {isMobile && (
- sidebar.toggle()}>
- )}
+
>
);
};
diff --git a/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.tsx b/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.tsx
index 2708e6e0a1e3..5738798f194e 100644
--- a/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.tsx
+++ b/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.tsx
@@ -30,10 +30,12 @@ import { useForm, Controller } from 'react-hook-form';
import { useHasLicenseModule } from '../../../../ee/client/hooks/useHasLicenseModule';
import UserAutoCompleteMultipleFederated from '../../../components/UserAutoCompleteMultiple/UserAutoCompleteMultipleFederated';
import { goToRoomById } from '../../../lib/utils/goToRoomById';
+import { useEncryptedRoomDescription } from '../hooks/useEncryptedRoomDescription';
type CreateChannelModalProps = {
teamId?: string;
onClose: () => void;
+ reload?: () => void;
};
type CreateChannelModalPayload = {
@@ -57,7 +59,7 @@ const getFederationHintKey = (licenseModule: ReturnType {
+const CreateChannelModal = ({ teamId = '', onClose, reload }: CreateChannelModalProps): ReactElement => {
const t = useTranslation();
const canSetReadOnly = usePermissionWithScopedRoles('set-readonly', ['owner']);
const e2eEnabled = useSetting('E2E_Enable');
@@ -68,6 +70,7 @@ const CreateChannelModal = ({ teamId = '', onClose }: CreateChannelModalProps):
const canCreateChannel = usePermission('create-c');
const canCreatePrivateChannel = usePermission('create-p');
+ const getEncryptedHint = useEncryptedRoomDescription('channel');
const channelNameRegex = useMemo(() => new RegExp(`^${namesValidation}$`), [namesValidation]);
const federatedModule = useHasLicenseModule('federation');
@@ -110,7 +113,7 @@ const CreateChannelModal = ({ teamId = '', onClose }: CreateChannelModalProps):
},
});
- const { isPrivate, broadcast, readOnly, federated } = watch();
+ const { isPrivate, broadcast, readOnly, federated, encrypted } = watch();
useEffect(() => {
if (!isPrivate) {
@@ -137,7 +140,7 @@ const CreateChannelModal = ({ teamId = '', onClose }: CreateChannelModalProps):
}
if (!allowSpecialNames && !channelNameRegex.test(name)) {
- return t('error-invalid-name');
+ return t('Name_cannot_have_special_characters');
}
const { exists } = await channelNameExists({ roomName: name });
@@ -171,6 +174,7 @@ const CreateChannelModal = ({ teamId = '', onClose }: CreateChannelModalProps):
}
dispatchToastMessage({ type: 'success', message: t('Room_has_been_created') });
+ reload?.();
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
} finally {
@@ -209,7 +213,7 @@ const CreateChannelModal = ({ teamId = '', onClose }: CreateChannelModalProps):
- {t('Channel_name')}
+ {t('Name')}
}
aria-invalid={errors.name ? 'true' : 'false'}
- aria-describedby={`${nameId}-error`}
+ aria-describedby={`${nameId}-error ${nameId}-hint`}
aria-required='true'
/>
@@ -231,13 +235,24 @@ const CreateChannelModal = ({ teamId = '', onClose }: CreateChannelModalProps):
{errors.name.message}
)}
+ {!allowSpecialNames && {t('No_spaces')}}
{t('Topic')}
- {t('Channel_what_is_this_channel_about')}
+ {t('Displayed_next_to_name')}
+
+
+ {t('Members')}
+ (
+
+ )}
+ />
@@ -258,7 +273,7 @@ const CreateChannelModal = ({ teamId = '', onClose }: CreateChannelModalProps):
/>
- {isPrivate ? t('Only_invited_users_can_acess_this_channel') : t('Everyone_can_access_this_channel')}
+ {isPrivate ? t('People_can_only_join_by_being_invited') : t('Anyone_can_access')}
@@ -283,48 +298,46 @@ const CreateChannelModal = ({ teamId = '', onClose }: CreateChannelModalProps):
- {t('Read_only')}
+ {t('Encrypted')}
(
)}
/>
-
- {readOnly ? t('Only_authorized_users_can_write_new_messages') : t('All_users_in_the_channel_can_write_new_messages')}
-
+ {getEncryptedHint({ isPrivate, broadcast, encrypted })}
- {t('Encrypted')}
+ {t('Read_only')}
(
)}
/>
-
- {isPrivate ? t('Encrypted_channel_Description') : t('Encrypted_not_available')}
-
+
+ {readOnly ? t('Read_only_field_hint_enabled', { roomType: 'channel' }) : t('Anyone_can_send_new_messages')}
+
@@ -344,17 +357,7 @@ const CreateChannelModal = ({ teamId = '', onClose }: CreateChannelModalProps):
)}
/>
- {t('Broadcast_channel_Description')}
-
-
- {t('Add_members')}
- (
-
- )}
- />
+ {broadcast && {t('Broadcast_hint_enabled', { roomType: 'channel' })}}
diff --git a/apps/meteor/client/sidebar/header/CreateDirectMessage.tsx b/apps/meteor/client/sidebar/header/CreateDirectMessage.tsx
index 626d1202a8e0..7c34f7ac01a3 100644
--- a/apps/meteor/client/sidebar/header/CreateDirectMessage.tsx
+++ b/apps/meteor/client/sidebar/header/CreateDirectMessage.tsx
@@ -1,5 +1,5 @@
import type { IUser } from '@rocket.chat/core-typings';
-import { Box, Modal, Button, FieldGroup, Field, FieldRow, FieldLabel, FieldError } from '@rocket.chat/fuselage';
+import { Box, Modal, Button, FieldGroup, Field, FieldRow, FieldError, FieldHint } from '@rocket.chat/fuselage';
import { useUniqueId } from '@rocket.chat/fuselage-hooks';
import { useTranslation, useEndpoint, useToastMessageDispatch, useSetting } from '@rocket.chat/ui-contexts';
import { useMutation } from '@tanstack/react-query';
@@ -20,7 +20,7 @@ const CreateDirectMessage = ({ onClose }: { onClose: () => void }) => {
const {
control,
handleSubmit,
- formState: { isDirty, isSubmitting, isValidating, errors },
+ formState: { isSubmitting, isValidating, errors },
} = useForm({ mode: 'onBlur', defaultValues: { users: [] } });
const mutateDirectMessage = useMutation({
@@ -47,17 +47,14 @@ const CreateDirectMessage = ({ onClose }: { onClose: () => void }) => {
- {t('Direct_message_creation_description')}
-
- {t('Members')}
-
+ {t('Direct_message_creation_description')}
users.length + 1 > directMaxUsers
? t('error-direct-message-max-user-exceeded', { maxUsers: directMaxUsers })
@@ -71,7 +68,7 @@ const CreateDirectMessage = ({ onClose }: { onClose: () => void }) => {
value={value}
onBlur={onBlur}
id={membersFieldId}
- aria-describedby={`${membersFieldId}-error`}
+ aria-describedby={`${membersFieldId}-hint ${membersFieldId}-error`}
aria-required='true'
aria-invalid={Boolean(errors.users)}
/>
@@ -83,13 +80,14 @@ const CreateDirectMessage = ({ onClose }: { onClose: () => void }) => {
{errors.users.message}
)}
+ {t('Direct_message_creation_description_hint')}
{t('Cancel')}
-
+
{t('Create')}
diff --git a/apps/meteor/client/sidebar/header/CreateTeam/CreateTeamModal.tsx b/apps/meteor/client/sidebar/header/CreateTeam/CreateTeamModal.tsx
index 115a8563f393..b56bb003aa9e 100644
--- a/apps/meteor/client/sidebar/header/CreateTeam/CreateTeamModal.tsx
+++ b/apps/meteor/client/sidebar/header/CreateTeam/CreateTeamModal.tsx
@@ -11,6 +11,7 @@ import {
FieldRow,
FieldError,
FieldDescription,
+ FieldHint,
} from '@rocket.chat/fuselage';
import { useUniqueId } from '@rocket.chat/fuselage-hooks';
import {
@@ -27,6 +28,7 @@ import { Controller, useForm } from 'react-hook-form';
import UserAutoCompleteMultiple from '../../../components/UserAutoCompleteMultiple';
import { goToRoomById } from '../../../lib/utils/goToRoomById';
+import { useEncryptedRoomDescription } from '../hooks/useEncryptedRoomDescription';
type CreateTeamModalInputs = {
name: string;
@@ -65,7 +67,7 @@ const CreateTeamModal = ({ onClose }: { onClose: () => void }): ReactElement =>
}
if (teamNameRegex && !teamNameRegex?.test(name)) {
- return t('Teams_Errors_team_name', { name });
+ return t('Name_cannot_have_special_characters');
}
const { exists } = await checkTeamNameExists({ roomName: name });
@@ -80,7 +82,7 @@ const CreateTeamModal = ({ onClose }: { onClose: () => void }): ReactElement =>
handleSubmit,
setValue,
watch,
- formState: { isDirty, errors, isSubmitting },
+ formState: { errors, isSubmitting },
} = useForm({
defaultValues: {
isPrivate: true,
@@ -91,7 +93,7 @@ const CreateTeamModal = ({ onClose }: { onClose: () => void }): ReactElement =>
},
});
- const { isPrivate, broadcast, readOnly } = watch();
+ const { isPrivate, broadcast, readOnly, encrypted } = watch();
useEffect(() => {
if (!isPrivate) {
@@ -107,7 +109,7 @@ const CreateTeamModal = ({ onClose }: { onClose: () => void }): ReactElement =>
const canChangeReadOnly = !broadcast;
const canChangeEncrypted = isPrivate && !broadcast && e2eEnabled && !e2eEnabledForPrivateByDefault;
- const isButtonEnabled = isDirty && canCreateTeam;
+ const getEncryptedHint = useEncryptedRoomDescription('team');
const handleCreateTeam = async ({
name,
@@ -164,6 +166,9 @@ const CreateTeamModal = ({ onClose }: { onClose: () => void }): ReactElement =>
+
+ {t('Teams_new_description')}
+
@@ -177,10 +182,9 @@ const CreateTeamModal = ({ onClose }: { onClose: () => void }): ReactElement =>
required: t('error-the-field-is-required', { field: t('Name') }),
validate: (value) => validateTeamName(value),
})}
- placeholder={t('Team_Name')}
addon={}
error={errors.name?.message}
- aria-describedby={`${nameId}-error`}
+ aria-describedby={`${nameId}-error ${nameId}-hint`}
aria-required='true'
/>
@@ -189,23 +193,27 @@ const CreateTeamModal = ({ onClose }: { onClose: () => void }): ReactElement =>
{errors.name.message}
)}
+ {!allowSpecialNames && {t('No_spaces')}}
-
- {t('Teams_New_Description_Label')}{' '}
-
- ({t('optional')})
-
-
+ {t('Topic')}
-
+
+
+
+ {t('Displayed_next_to_name')}
+
+ {t('Teams_New_Add_members_Label')}
+ (
+
+ )}
+ />
+
{t('Teams_New_Private_Label')}
@@ -218,7 +226,7 @@ const CreateTeamModal = ({ onClose }: { onClose: () => void }): ReactElement =>
/>
- {isPrivate ? t('Teams_New_Private_Description_Enabled') : t('Teams_New_Private_Description_Disabled')}
+ {isPrivate ? t('People_can_only_join_by_being_invited') : t('Anyone_can_access')}
@@ -240,7 +248,7 @@ const CreateTeamModal = ({ onClose }: { onClose: () => void }): ReactElement =>
/>
- {readOnly ? t('Only_authorized_users_can_write_new_messages') : t('Teams_New_Read_only_Description')}
+ {readOnly ? t('Read_only_field_hint_enabled', { roomType: 'team' }) : t('Anyone_can_send_new_messages')}
@@ -261,9 +269,7 @@ const CreateTeamModal = ({ onClose }: { onClose: () => void }): ReactElement =>
)}
/>
-
- {isPrivate ? t('Teams_New_Encrypted_Description_Enabled') : t('Teams_New_Encrypted_Description_Disabled')}
-
+ {getEncryptedHint({ isPrivate, broadcast, encrypted })}
@@ -276,27 +282,14 @@ const CreateTeamModal = ({ onClose }: { onClose: () => void }): ReactElement =>
)}
/>
- {t('Teams_New_Broadcast_Description')}
-
-
-
- {t('Teams_New_Add_members_Label')}{' '}
-
- ({t('optional')})
-
-
- }
- />
+ {broadcast && {t('Teams_New_Broadcast_Description')}}
{t('Cancel')}
-
+
{t('Create')}
diff --git a/apps/meteor/client/sidebar/header/UserAvatarWithStatus.tsx b/apps/meteor/client/sidebar/header/UserAvatarWithStatus.tsx
index 6fa08f5e0253..ce4878515781 100644
--- a/apps/meteor/client/sidebar/header/UserAvatarWithStatus.tsx
+++ b/apps/meteor/client/sidebar/header/UserAvatarWithStatus.tsx
@@ -1,7 +1,7 @@
import { css } from '@rocket.chat/css-in-js';
import { Box } from '@rocket.chat/fuselage';
import { UserAvatar } from '@rocket.chat/ui-avatar';
-import { useSetting, useUser, useTranslation } from '@rocket.chat/ui-contexts';
+import { useSetting, useUser } from '@rocket.chat/ui-contexts';
import React from 'react';
import { UserStatus } from '../../components/UserStatus';
@@ -20,7 +20,6 @@ const anon = {
*/
const UserAvatarWithStatus = () => {
- const t = useTranslation();
const user = useUser();
const presenceDisabled = useSetting('Presence_broadcast_disabled');
@@ -32,9 +31,6 @@ const UserAvatarWithStatus = () => {
className={css`
cursor: pointer;
`}
- aria-label={t('User_menu')}
- role='button'
- data-qa='sidebar-avatar-button'
>
{username && }
{
};
const createDirectMessageItem: GenericMenuItemProps = {
id: 'direct',
- content: t('Direct_Messages'),
+ content: t('Direct_message'),
icon: 'balloon',
onClick: () => {
createDirectMessage();
@@ -60,9 +60,9 @@ export const useCreateRoomItems = (): GenericMenuItemProps[] => {
};
return [
- ...(canCreateChannel ? [createChannelItem] : []),
- ...(canCreateTeam ? [createTeamItem] : []),
...(canCreateDirectMessages ? [createDirectMessageItem] : []),
...(canCreateDiscussion && discussionEnabled ? [createDiscussionItem] : []),
+ ...(canCreateChannel ? [createChannelItem] : []),
+ ...(canCreateTeam ? [createTeamItem] : []),
];
};
diff --git a/apps/meteor/client/sidebar/header/hooks/useEncryptedRoomDescription.tsx b/apps/meteor/client/sidebar/header/hooks/useEncryptedRoomDescription.tsx
new file mode 100644
index 000000000000..09796dd7a6b7
--- /dev/null
+++ b/apps/meteor/client/sidebar/header/hooks/useEncryptedRoomDescription.tsx
@@ -0,0 +1,23 @@
+import { useSetting, useTranslation } from '@rocket.chat/ui-contexts';
+
+export const useEncryptedRoomDescription = (roomType: 'channel' | 'team') => {
+ const t = useTranslation();
+ const e2eEnabled = useSetting('E2E_Enable');
+ const e2eEnabledForPrivateByDefault = useSetting('E2E_Enabled_Default_PrivateRooms');
+
+ return ({ isPrivate, broadcast, encrypted }: { isPrivate: boolean; broadcast: boolean; encrypted: boolean }) => {
+ if (!e2eEnabled) {
+ return t('Not_available_for_this_workspace');
+ }
+ if (!isPrivate) {
+ return t('Encrypted_not_available', { roomType });
+ }
+ if (broadcast) {
+ return t('Not_available_for_broadcast', { roomType });
+ }
+ if (e2eEnabledForPrivateByDefault || encrypted) {
+ return t('Encrypted_messages', { roomType });
+ }
+ return t('Encrypted_messages_false');
+ };
+};
diff --git a/apps/meteor/client/sidebar/search/SearchList.tsx b/apps/meteor/client/sidebar/search/SearchList.tsx
index ceb89d3d7092..d215a77ce4bd 100644
--- a/apps/meteor/client/sidebar/search/SearchList.tsx
+++ b/apps/meteor/client/sidebar/search/SearchList.tsx
@@ -101,7 +101,7 @@ const useSearchItems = (filterText: string): UseQueryResult<(ISubscription & IRo
const getSpotlight = useMethod('spotlight');
return useQuery(
- ['sidebar/search/spotlight', name, usernamesFromClient, type, localRooms.map(({ _id }) => _id)],
+ ['sidebar/search/spotlight', name, usernamesFromClient, type, localRooms.map(({ _id, name }) => _id + name)],
async () => {
if (localRooms.length === LIMIT) {
return localRooms;
diff --git a/apps/meteor/client/views/account/security/AccountSecurityPage.tsx b/apps/meteor/client/views/account/security/AccountSecurityPage.tsx
index 49ed9594c110..536ba8a04ef7 100644
--- a/apps/meteor/client/views/account/security/AccountSecurityPage.tsx
+++ b/apps/meteor/client/views/account/security/AccountSecurityPage.tsx
@@ -6,7 +6,6 @@ import React from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { Page, PageHeader, PageScrollableContentWithShadow, PageFooter } from '../../../components/Page';
-import NotAuthorizedPage from '../../notAuthorized/NotAuthorizedPage';
import ChangePassword from './ChangePassword';
import EndToEnd from './EndToEnd';
import TwoFactorEmail from './TwoFactorEmail';
@@ -30,25 +29,24 @@ const AccountSecurityPage = (): ReactElement => {
const twoFactorTOTP = useSetting('Accounts_TwoFactorAuthentication_By_TOTP_Enabled');
const twoFactorByEmailEnabled = useSetting('Accounts_TwoFactorAuthentication_By_Email_Enabled');
const e2eEnabled = useSetting('E2E_Enable');
+ const allowPasswordChange = useSetting('Accounts_AllowPasswordChange');
const passwordFormId = useUniqueId();
- if (!twoFactorEnabled && !e2eEnabled) {
- return ;
- }
-
return (
-
-
-
-
-
-
-
+ {allowPasswordChange && (
+
+
+
+
+
+
+
+ )}
{(twoFactorTOTP || twoFactorByEmailEnabled) && twoFactorEnabled && (
diff --git a/apps/meteor/client/views/account/security/AccountSecurityRoute.tsx b/apps/meteor/client/views/account/security/AccountSecurityRoute.tsx
index 10f9945cd7fe..11a0d8b63882 100644
--- a/apps/meteor/client/views/account/security/AccountSecurityRoute.tsx
+++ b/apps/meteor/client/views/account/security/AccountSecurityRoute.tsx
@@ -8,7 +8,9 @@ import AccountSecurityPage from './AccountSecurityPage';
const AccountSecurityRoute = (): ReactElement => {
const isTwoFactorEnabled = useSetting('Accounts_TwoFactorAuthentication_Enabled');
const isE2EEnabled = useSetting('E2E_Enable');
- const canViewSecurity = isTwoFactorEnabled || isE2EEnabled;
+ const allowPasswordChange = useSetting('Accounts_AllowPasswordChange');
+
+ const canViewSecurity = isTwoFactorEnabled || isE2EEnabled || allowPasswordChange;
if (!canViewSecurity) {
return ;
diff --git a/apps/meteor/client/views/account/sidebarItems.tsx b/apps/meteor/client/views/account/sidebarItems.tsx
index 5cc1fb0e1a65..ca0376be329d 100644
--- a/apps/meteor/client/views/account/sidebarItems.tsx
+++ b/apps/meteor/client/views/account/sidebarItems.tsx
@@ -27,7 +27,10 @@ export const {
href: '/account/security',
i18nLabel: 'Security',
icon: 'lock',
- permissionGranted: (): boolean => settings.get('Accounts_TwoFactorAuthentication_Enabled') || settings.get('E2E_Enable'),
+ permissionGranted: (): boolean =>
+ settings.get('Accounts_TwoFactorAuthentication_Enabled') ||
+ settings.get('E2E_Enable') ||
+ settings.get('Accounts_AllowPasswordChange'),
},
{
href: '/account/integrations',
diff --git a/apps/meteor/client/views/admin/moderation/helpers/ModerationFilter.tsx b/apps/meteor/client/views/admin/moderation/helpers/ModerationFilter.tsx
index 7d72d9d4c392..4fde3568b2aa 100644
--- a/apps/meteor/client/views/admin/moderation/helpers/ModerationFilter.tsx
+++ b/apps/meteor/client/views/admin/moderation/helpers/ModerationFilter.tsx
@@ -15,7 +15,7 @@ const ModerationFilter = ({ setText, setDateRange }: ModerationFilterProps) => {
const handleChange = useCallback(({ text }): void => setText(text), [setText]);
return (
-
+
);
diff --git a/apps/meteor/client/views/admin/rooms/EditRoom.tsx b/apps/meteor/client/views/admin/rooms/EditRoom.tsx
index 1522f4694160..cc165bca215b 100644
--- a/apps/meteor/client/views/admin/rooms/EditRoom.tsx
+++ b/apps/meteor/client/views/admin/rooms/EditRoom.tsx
@@ -86,7 +86,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps) => {
canViewReactWhenReadOnly,
} = useEditAdminRoomPermissions(room);
- const { roomType, readOnly, archived } = watch();
+ const { roomType, readOnly, archived, isDefault } = watch();
const changeArchiving = archived !== !!room.archived;
@@ -324,7 +324,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps) => {
name='favorite'
control={control}
render={({ field: { value, ...field } }) => (
-
+
)}
/>
diff --git a/apps/meteor/client/views/admin/settings/GroupPage.tsx b/apps/meteor/client/views/admin/settings/GroupPage.tsx
index 9e4ef1fa3ab8..18348e902500 100644
--- a/apps/meteor/client/views/admin/settings/GroupPage.tsx
+++ b/apps/meteor/client/views/admin/settings/GroupPage.tsx
@@ -2,15 +2,7 @@ import type { ISetting, ISettingColor } from '@rocket.chat/core-typings';
import { Accordion, Box, Button, ButtonGroup } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import type { TranslationKey } from '@rocket.chat/ui-contexts';
-import {
- useToastMessageDispatch,
- useUser,
- useSettingsDispatch,
- useSettings,
- useTranslation,
- useLoadLanguage,
- useRoute,
-} from '@rocket.chat/ui-contexts';
+import { useToastMessageDispatch, useSettingsDispatch, useSettings, useTranslation, useRoute } from '@rocket.chat/ui-contexts';
import type { FC, ReactNode, FormEvent, MouseEvent } from 'react';
import React, { useMemo, memo } from 'react';
@@ -39,11 +31,9 @@ const GroupPage: FC = ({
isCustom = false,
}) => {
const t = useTranslation();
- const user = useUser();
const router = useRoute('admin-settings');
const dispatch = useSettingsDispatch();
const dispatchToastMessage = useToastMessageDispatch();
- const loadLanguage = useLoadLanguage();
const changedEditableSettings = useEditableSettings(
useMemo(
@@ -88,17 +78,6 @@ const GroupPage: FC = ({
try {
await dispatch(changes);
-
- if (changes.some(({ _id }) => _id === 'Language')) {
- const lng = user?.language || changes.filter(({ _id }) => _id === 'Language').shift()?.value || 'en';
- if (typeof lng === 'string') {
- await loadLanguage(lng);
- dispatchToastMessage({ type: 'success', message: t('Settings_updated', { lng }) });
- return;
- }
- throw new Error('lng is not a string');
- }
-
dispatchToastMessage({ type: 'success', message: t('Settings_updated') });
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
diff --git a/apps/meteor/client/views/admin/users/UsersTable/UsersTable.tsx b/apps/meteor/client/views/admin/users/UsersTable/UsersTable.tsx
index 981e75483d03..1af9c67c9dcc 100644
--- a/apps/meteor/client/views/admin/users/UsersTable/UsersTable.tsx
+++ b/apps/meteor/client/views/admin/users/UsersTable/UsersTable.tsx
@@ -144,7 +144,7 @@ const UsersTable = ({ reload }: UsersTableProps): ReactElement | null => {
return (
<>
- setText(text)} />
+ setText(text)} />
{isLoading && (
{headers}
diff --git a/apps/meteor/client/views/admin/users/UsersTable/UsersTableRow.tsx b/apps/meteor/client/views/admin/users/UsersTable/UsersTableRow.tsx
index 4010b118e57d..e0d46c2ba6fb 100644
--- a/apps/meteor/client/views/admin/users/UsersTable/UsersTableRow.tsx
+++ b/apps/meteor/client/views/admin/users/UsersTable/UsersTableRow.tsx
@@ -1,5 +1,6 @@
-import type { IRole, IUser } from '@rocket.chat/core-typings';
+import type { IRole, IUser, Serialized } from '@rocket.chat/core-typings';
import { Box } from '@rocket.chat/fuselage';
+import type { DefaultUserInfo } from '@rocket.chat/rest-typings';
import { capitalize } from '@rocket.chat/string-helpers';
import { UserAvatar } from '@rocket.chat/ui-avatar';
import type { TranslationKey } from '@rocket.chat/ui-contexts';
@@ -11,7 +12,7 @@ import { Roles } from '../../../../../app/models/client';
import { GenericTableRow, GenericTableCell } from '../../../../components/GenericTable';
type UsersTableRowProps = {
- user: Pick;
+ user: Serialized;
onClick: (id: IUser['_id']) => void;
mediaQuery: boolean;
};
diff --git a/apps/meteor/client/views/admin/workspace/DeploymentCard/DeploymentCard.stories.tsx b/apps/meteor/client/views/admin/workspace/DeploymentCard/DeploymentCard.stories.tsx
index 5fb8b4d146f2..db88a34897a6 100644
--- a/apps/meteor/client/views/admin/workspace/DeploymentCard/DeploymentCard.stories.tsx
+++ b/apps/meteor/client/views/admin/workspace/DeploymentCard/DeploymentCard.stories.tsx
@@ -1,3 +1,4 @@
+import type { IStats } from '@rocket.chat/core-typings';
import type { ComponentMeta, ComponentStory } from '@storybook/react';
import React from 'react';
@@ -11,274 +12,29 @@ export default {
},
args: {
info: {
- build: {
- arch: 'x64',
- cpus: 1,
- platform: 'linux',
- osRelease: 'Ubuntu 18.04.1 LTS',
- date: '2020-01-01T00:00:00.000Z',
- freeMemory: 1.3 * 1024 * 1024 * 1024,
- nodeVersion: 'v12.0.0',
- totalMemory: 2.4 * 1024 * 1024 * 1024,
- },
- version: '1.0.0',
marketplaceApiVersion: '1.0.0',
commit: {
- author: 'John Doe',
- date: '2020-01-01T00:00:00.000Z',
branch: 'master',
hash: '1234567890',
subject: 'This is a commit',
- tag: 'v1.0.0',
},
},
statistics: {
- // Users
- totalUsers: 123,
- onlineUsers: 23,
- awayUsers: 32,
- busyUsers: 21,
- offlineUsers: 123 - 23 - 32 - 21,
- // Types and Distribution
- totalConnectedUsers: 32,
- activeUsers: 12,
- activeGuests: 32 - 12,
- nonActiveUsers: 0,
- appUsers: 23,
- // Uploads
- uploadsTotal: 321,
- uploadsTotalSize: 123 * 1024 * 1024,
- // Rooms
- totalRooms: 231,
- totalChannels: 12,
- totalPrivateGroups: 23,
- totalDirect: 21,
- totalDiscussions: 32,
- totalLivechat: 31,
- // Messages
- totalMessages: 321,
- totalThreads: 123,
- totalChannelMessages: 213,
- totalPrivateGroupMessages: 21,
- totalDirectMessages: 23,
- totalDiscussionsMessages: 32,
- totalLivechatMessages: 31,
- // -
- _id: '',
- wizard: {},
uniqueId: '',
- deploymentFingerprintHash: '',
- deploymentFingerprintVerified: true,
- installedAt: '',
version: '1.0.0',
- tag: '',
- branch: '',
- userLanguages: {},
- teams: {
- totalTeams: 0,
- totalRoomsInsideTeams: 0,
- totalDefaultRoomsInsideTeams: 0,
- },
- totalLivechatManagers: 10,
- totalCustomFields: 10,
- totalTriggers: 1,
- isDepartmentRemovalEnabled: false,
- archivedDepartments: 0,
- totalLivechatVisitors: 0,
- totalLivechatAgents: 0,
- livechatEnabled: false,
- federatedServers: 0,
- federatedUsers: 0,
- lastLogin: '',
- lastMessageSentAt: new Date(),
- lastSeenSubscription: '',
- os: {
- type: '',
- platform: 'linux',
- arch: '',
- release: '',
- uptime: 0,
- loadavg: [0, 0, 0],
- totalmem: 0,
- freemem: 0,
- cpus: [
- {
- model: '',
- speed: 0,
- times: {
- user: 0,
- nice: 0,
- sys: 0,
- idle: 0,
- irq: 0,
- },
- },
- ],
- },
process: {
nodeVersion: '',
pid: 0,
- uptime: 0,
- },
- deploy: {
- method: 'tar',
- platform: '',
},
- enterpriseReady: false,
migration: {
- _id: '',
- locked: false,
- version: 0,
- buildAt: '',
+ version: 272,
lockedAt: '',
},
- instanceCount: 0,
msEnabled: false,
oplogEnabled: false,
mongoVersion: '',
mongoStorageEngine: '',
- pushQueue: 0,
- omnichannelSources: [{}],
- departments: 0,
- routingAlgorithm: '',
- onHoldEnabled: false,
- emailInboxes: 0,
- BusinessHours: {},
- lastChattedAgentPreferred: false,
- assignNewConversationsToContactManager: false,
- visitorAbandonment: '',
- chatsOnHold: 0,
- voipEnabled: false,
- voipCalls: 0,
- voipExtensions: 0,
- voipSuccessfulCalls: 0,
- voipErrorCalls: 0,
- voipOnHoldCalls: 0,
- webRTCEnabled: false,
- webRTCEnabledForOmnichannel: false,
- omnichannelWebRTCCalls: 1,
- federationOverviewData: {
- numberOfEvents: 0,
- numberOfFederatedUsers: 0,
- numberOfServers: 0,
- },
- readReceiptsEnabled: false,
- readReceiptsDetailed: false,
- uniqueUsersOfLastWeek: { data: [], day: 0, month: 0, year: 0 },
- uniqueUsersOfLastMonth: { data: [], day: 0, month: 0, year: 0 },
- uniqueUsersOfYesterday: { data: [], day: 0, month: 0, year: 0 },
- uniqueDevicesOfYesterday: { data: [], day: 0, month: 0, year: 0 },
- uniqueDevicesOfLastWeek: { data: [], day: 0, month: 0, year: 0 },
- uniqueDevicesOfLastMonth: { data: [], day: 0, month: 0, year: 0 },
- uniqueOSOfYesterday: { data: [], day: 0, month: 0, year: 0 },
- uniqueOSOfLastWeek: { data: [], day: 0, month: 0, year: 0 },
- uniqueOSOfLastMonth: { data: [], day: 0, month: 0, year: 0 },
- omnichannelContactsBySource: { contactsCount: 0, conversationsCount: 0, sources: [] },
- uniqueContactsOfLastMonth: { contactsCount: 0, conversationsCount: 0, sources: [] },
- uniqueContactsOfLastWeek: { contactsCount: 0, conversationsCount: 0, sources: [] },
- uniqueContactsOfYesterday: { contactsCount: 0, conversationsCount: 0, sources: [] },
- apps: {
- engineVersion: 'x.y.z',
- enabled: false,
- totalInstalled: 0,
- totalActive: 0,
- totalFailed: 0,
- },
- services: {},
- importer: {},
- settings: {},
- integrations: {
- totalIntegrations: 0,
- totalIncoming: 0,
- totalIncomingActive: 0,
- totalOutgoing: 0,
- totalOutgoingActive: 0,
- totalWithScriptEnabled: 0,
- },
- enterprise: {
- modules: [],
- tags: [],
- seatRequests: 0,
- livechatTags: 0,
- cannedResponses: 0,
- priorities: 0,
- businessUnits: 0,
- },
- createdAt: new Date(),
- showHomeButton: false,
- homeTitleChanged: false,
- homeBodyChanged: false,
- customCSSChanged: false,
- onLogoutCustomScriptChanged: false,
- loggedOutCustomScriptChanged: false,
- loggedInCustomScriptChanged: false,
- logoChange: false,
- customCSS: 0,
- customScript: 0,
- tabInvites: 0,
- totalEmailInvitation: 0,
- totalRoomsWithStarred: 0,
- totalRoomsWithPinned: 0,
- totalStarred: 0,
- totalPinned: 0,
- totalE2ERooms: 0,
- totalE2EMessages: 0,
- totalUserTOTP: 0,
- totalUserEmail2fa: 0,
- usersCreatedADM: 0,
- usersCreatedSlackImport: 0,
- usersCreatedSlackUser: 0,
- usersCreatedCSVImport: 0,
- usersCreatedHiptext: 0,
- totalOTR: 0,
- totalOTRRooms: 0,
- slashCommandsJitsi: 0,
- messageAuditApply: 0,
- messageAuditLoad: 0,
- dashboardCount: 0,
- joinJitsiButton: 0,
- totalBroadcastRooms: 0,
- totalRoomsWithActiveLivestream: 0,
- totalTriggeredEmails: 0,
- totalLinkInvitation: 0,
- roomsInsideTeams: 0,
- totalEncryptedMessages: 0,
- totalLinkInvitationUses: 0,
- totalManuallyAddedUsers: 0,
- videoConf: {
- videoConference: {
- started: 0,
- ended: 0,
- },
- direct: {
- calling: 0,
- started: 0,
- ended: 0,
- },
- livechat: {
- started: 0,
- ended: 0,
- },
- settings: {
- provider: '',
- dms: false,
- channels: false,
- groups: false,
- teams: false,
- },
- },
- totalSubscriptionRoles: 0,
- totalUserRoles: 0,
- totalCustomRoles: 0,
- totalWebRTCCalls: 0,
- uncaughtExceptionsCount: 0,
- push: 0,
- dailyPeakConnections: 0,
- maxMonthlyPeakConnections: 0,
- matrixFederation: {
- enabled: false,
- },
- },
+ } as IStats,
instances: [],
},
} as ComponentMeta;
diff --git a/apps/meteor/client/views/admin/workspace/MessagesRoomsCard/MessagesRoomsCard.stories.tsx b/apps/meteor/client/views/admin/workspace/MessagesRoomsCard/MessagesRoomsCard.stories.tsx
index 53cd89838e2a..682d7936d63b 100644
--- a/apps/meteor/client/views/admin/workspace/MessagesRoomsCard/MessagesRoomsCard.stories.tsx
+++ b/apps/meteor/client/views/admin/workspace/MessagesRoomsCard/MessagesRoomsCard.stories.tsx
@@ -1,3 +1,4 @@
+import type { IStats } from '@rocket.chat/core-typings';
import type { ComponentMeta, ComponentStory } from '@storybook/react';
import React from 'react';
@@ -11,252 +12,16 @@ export default {
},
args: {
statistics: {
- // Users
- totalUsers: 123,
- onlineUsers: 23,
- awayUsers: 32,
- busyUsers: 21,
- offlineUsers: 123 - 23 - 32 - 21,
- // Types and Distribution
- totalConnectedUsers: 32,
- activeUsers: 12,
- activeGuests: 32 - 12,
- nonActiveUsers: 0,
- appUsers: 23,
- // Uploads
- uploadsTotal: 321,
- uploadsTotalSize: 123 * 1024 * 1024,
- // Rooms
- totalRooms: 231,
totalChannels: 12,
totalPrivateGroups: 23,
totalDirect: 21,
totalDiscussions: 32,
totalLivechat: 31,
- // Messages
totalMessages: 321,
- totalThreads: 123,
- totalChannelMessages: 213,
- totalPrivateGroupMessages: 21,
totalDirectMessages: 23,
totalDiscussionsMessages: 32,
totalLivechatMessages: 31,
- // -
- _id: '',
- wizard: {},
- uniqueId: '',
- deploymentFingerprintHash: '',
- deploymentFingerprintVerified: true,
- installedAt: '',
- version: '',
- tag: '',
- branch: '',
- userLanguages: {},
- teams: {
- totalTeams: 0,
- totalRoomsInsideTeams: 0,
- totalDefaultRoomsInsideTeams: 0,
- },
- totalLivechatManagers: 10,
- totalCustomFields: 10,
- totalTriggers: 1,
- isDepartmentRemovalEnabled: false,
- archivedDepartments: 0,
- totalLivechatVisitors: 0,
- totalLivechatAgents: 0,
- livechatEnabled: false,
- federatedServers: 0,
- federatedUsers: 0,
- lastLogin: '',
- lastMessageSentAt: new Date(),
- lastSeenSubscription: '',
- os: {
- type: '',
- platform: 'linux',
- arch: '',
- release: '',
- uptime: 0,
- loadavg: [0, 0, 0],
- totalmem: 0,
- freemem: 0,
- cpus: [
- {
- model: '',
- speed: 0,
- times: {
- user: 0,
- nice: 0,
- sys: 0,
- idle: 0,
- irq: 0,
- },
- },
- ],
- },
- process: {
- nodeVersion: '',
- pid: 0,
- uptime: 0,
- },
- deploy: {
- method: '',
- platform: '',
- },
- enterpriseReady: false,
- migration: {
- _id: '',
- locked: false,
- version: 0,
- buildAt: '',
- lockedAt: '',
- },
- instanceCount: 0,
- msEnabled: false,
- oplogEnabled: false,
- mongoVersion: '',
- mongoStorageEngine: '',
- pushQueue: 0,
- omnichannelSources: [{}],
- departments: 0,
- routingAlgorithm: '',
- onHoldEnabled: false,
- emailInboxes: 0,
- BusinessHours: {},
- lastChattedAgentPreferred: false,
- assignNewConversationsToContactManager: false,
- visitorAbandonment: '',
- chatsOnHold: 0,
- voipEnabled: false,
- voipCalls: 0,
- voipExtensions: 0,
- voipSuccessfulCalls: 0,
- voipErrorCalls: 0,
- voipOnHoldCalls: 0,
- webRTCEnabled: false,
- webRTCEnabledForOmnichannel: false,
- omnichannelWebRTCCalls: 1,
- federationOverviewData: {
- numberOfEvents: 0,
- numberOfFederatedUsers: 0,
- numberOfServers: 0,
- },
- readReceiptsEnabled: false,
- readReceiptsDetailed: false,
- uniqueUsersOfLastWeek: { data: [], day: 0, month: 0, year: 0 },
- uniqueUsersOfLastMonth: { data: [], day: 0, month: 0, year: 0 },
- uniqueUsersOfYesterday: { data: [], day: 0, month: 0, year: 0 },
- uniqueDevicesOfYesterday: { data: [], day: 0, month: 0, year: 0 },
- uniqueDevicesOfLastWeek: { data: [], day: 0, month: 0, year: 0 },
- uniqueDevicesOfLastMonth: { data: [], day: 0, month: 0, year: 0 },
- uniqueOSOfYesterday: { data: [], day: 0, month: 0, year: 0 },
- uniqueOSOfLastWeek: { data: [], day: 0, month: 0, year: 0 },
- uniqueOSOfLastMonth: { data: [], day: 0, month: 0, year: 0 },
- omnichannelContactsBySource: { contactsCount: 0, conversationsCount: 0, sources: [] },
- uniqueContactsOfLastMonth: { contactsCount: 0, conversationsCount: 0, sources: [] },
- uniqueContactsOfLastWeek: { contactsCount: 0, conversationsCount: 0, sources: [] },
- uniqueContactsOfYesterday: { contactsCount: 0, conversationsCount: 0, sources: [] },
- apps: {
- engineVersion: 'x.y.z',
- enabled: false,
- totalInstalled: 0,
- totalActive: 0,
- totalFailed: 0,
- },
- services: {},
- importer: {},
- settings: {},
- integrations: {
- totalIntegrations: 0,
- totalIncoming: 0,
- totalIncomingActive: 0,
- totalOutgoing: 0,
- totalOutgoingActive: 0,
- totalWithScriptEnabled: 0,
- },
- enterprise: {
- modules: [],
- tags: [],
- seatRequests: 0,
- livechatTags: 0,
- cannedResponses: 0,
- priorities: 0,
- businessUnits: 0,
- },
- createdAt: new Date(),
- showHomeButton: false,
- homeTitleChanged: false,
- homeBodyChanged: false,
- customCSSChanged: false,
- onLogoutCustomScriptChanged: false,
- loggedOutCustomScriptChanged: false,
- loggedInCustomScriptChanged: false,
- logoChange: false,
- customCSS: 0,
- customScript: 0,
- tabInvites: 0,
- totalEmailInvitation: 0,
- totalRoomsWithStarred: 0,
- totalRoomsWithPinned: 0,
- totalStarred: 0,
- totalPinned: 0,
- totalE2ERooms: 0,
- totalE2EMessages: 0,
- totalUserTOTP: 0,
- totalUserEmail2fa: 0,
- usersCreatedADM: 0,
- usersCreatedSlackImport: 0,
- usersCreatedSlackUser: 0,
- usersCreatedCSVImport: 0,
- usersCreatedHiptext: 0,
- totalOTR: 0,
- totalOTRRooms: 0,
- slashCommandsJitsi: 0,
- messageAuditApply: 0,
- messageAuditLoad: 0,
- dashboardCount: 0,
- joinJitsiButton: 0,
- totalBroadcastRooms: 0,
- totalRoomsWithActiveLivestream: 0,
- totalTriggeredEmails: 0,
- totalLinkInvitation: 0,
- roomsInsideTeams: 0,
- totalEncryptedMessages: 0,
- totalLinkInvitationUses: 0,
- totalManuallyAddedUsers: 0,
- videoConf: {
- videoConference: {
- started: 0,
- ended: 0,
- },
- direct: {
- calling: 0,
- started: 0,
- ended: 0,
- },
- livechat: {
- started: 0,
- ended: 0,
- },
- settings: {
- provider: '',
- dms: false,
- channels: false,
- groups: false,
- teams: false,
- },
- },
- totalSubscriptionRoles: 0,
- totalUserRoles: 0,
- totalCustomRoles: 0,
- totalWebRTCCalls: 0,
- uncaughtExceptionsCount: 0,
- push: 0,
- dailyPeakConnections: 0,
- maxMonthlyPeakConnections: 0,
- matrixFederation: {
- enabled: false,
- },
- },
+ } as IStats,
},
} as ComponentMeta;
diff --git a/apps/meteor/client/views/admin/workspace/VersionCard/VersionCard.tsx b/apps/meteor/client/views/admin/workspace/VersionCard/VersionCard.tsx
index 868b2ca24e6e..4291cd02d47a 100644
--- a/apps/meteor/client/views/admin/workspace/VersionCard/VersionCard.tsx
+++ b/apps/meteor/client/views/admin/workspace/VersionCard/VersionCard.tsx
@@ -170,7 +170,7 @@ const VersionCard = ({ serverInfo }: VersionCardProps): ReactElement => {
if (isLoading && !licenseData) {
return (
- ;
+
);
}
diff --git a/apps/meteor/client/views/banners/UiKitBanner.tsx b/apps/meteor/client/views/banners/UiKitBanner.tsx
index 27478422392b..72d2e5a96052 100644
--- a/apps/meteor/client/views/banners/UiKitBanner.tsx
+++ b/apps/meteor/client/views/banners/UiKitBanner.tsx
@@ -49,10 +49,6 @@ const UiKitBanner = ({ initialView }: UiKitBannerProps) => {
})
.catch((error) => {
dispatchToastMessage({ type: 'error', message: error });
- return Promise.reject(error);
- })
- .finally(() => {
- actionManager.disposeView(view.viewId);
});
});
diff --git a/apps/meteor/client/views/composer/EmojiPicker/EmojiPicker.tsx b/apps/meteor/client/views/composer/EmojiPicker/EmojiPicker.tsx
index 045952078304..f2e68a60d690 100644
--- a/apps/meteor/client/views/composer/EmojiPicker/EmojiPicker.tsx
+++ b/apps/meteor/client/views/composer/EmojiPicker/EmojiPicker.tsx
@@ -188,6 +188,7 @@ const EmojiPicker = ({ reference, onClose, onPickEmoji }: EmojiPickerProps) => {
-
+
{passwordError}
diff --git a/apps/meteor/client/views/e2e/SaveE2EPasswordModal.tsx b/apps/meteor/client/views/e2e/SaveE2EPasswordModal.tsx
index d335549ac505..f642e68e09af 100644
--- a/apps/meteor/client/views/e2e/SaveE2EPasswordModal.tsx
+++ b/apps/meteor/client/views/e2e/SaveE2EPasswordModal.tsx
@@ -1,5 +1,6 @@
-import { Box, Button } from '@rocket.chat/fuselage';
+import { Box, CodeSnippet } from '@rocket.chat/fuselage';
import { useClipboard } from '@rocket.chat/fuselage-hooks';
+import { ExternalLink } from '@rocket.chat/ui-client';
import { useTranslation } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import React from 'react';
@@ -13,6 +14,8 @@ type SaveE2EPasswordModalProps = {
onConfirm: () => void;
};
+const DOCS_URL = 'https://rocket.chat/docs/user-guides/end-to-end-encryption/';
+
const SaveE2EPasswordModal = ({ randomPassword, onClose, onCancel, onConfirm }: SaveE2EPasswordModalProps): ReactElement => {
const t = useTranslation();
const { copy, hasCopied } = useClipboard(randomPassword);
@@ -26,13 +29,21 @@ const SaveE2EPasswordModal = ({ randomPassword, onClose, onCancel, onConfirm }:
confirmText={t('I_Saved_My_Password')}
variant='warning'
title={t('Save_your_encryption_password')}
+ annotation={t('You_can_do_from_account_preferences')}
>
- <>
-
- copy()}>
- {hasCopied ? t('Copied') : t('Copy_password')}
-
- >
+
+
+
+ {t('Learn_more_about_E2EE')}
+
+
+
+ {t('E2E_password_save_text')}
+
+ {t('Your_E2EE_password_is')}
+ copy()} mbs={8}>
+ {randomPassword}
+
);
};
diff --git a/apps/meteor/client/views/hooks/roomActions/useDeleteRoom.tsx b/apps/meteor/client/views/hooks/roomActions/useDeleteRoom.tsx
index be4728732284..48b6507a331d 100644
--- a/apps/meteor/client/views/hooks/roomActions/useDeleteRoom.tsx
+++ b/apps/meteor/client/views/hooks/roomActions/useDeleteRoom.tsx
@@ -15,7 +15,8 @@ export const useDeleteRoom = (room: IRoom | Pick, {
const dispatchToastMessage = useToastMessageDispatch();
const hasPermissionToDelete = usePermission(`delete-${room.t}`, room._id);
const canDeleteRoom = isRoomFederated(room) ? false : hasPermissionToDelete;
-
+ // eslint-disable-next-line no-nested-ternary
+ const roomType = 'prid' in room ? 'discussion' : room.teamId && room.teamMain ? 'team' : 'channel';
const isAdminRoute = router.getRouteName() === 'admin-rooms';
const deleteRoomEndpoint = useEndpoint('POST', '/v1/rooms.delete');
@@ -24,7 +25,7 @@ export const useDeleteRoom = (room: IRoom | Pick, {
const deleteRoomMutation = useMutation({
mutationFn: deleteRoomEndpoint,
onSuccess: () => {
- dispatchToastMessage({ type: 'success', message: t('Room_has_been_deleted') });
+ dispatchToastMessage({ type: 'success', message: t('Deleted_roomType', { roomName: room.name, roomType }) });
if (isAdminRoute) {
return router.navigate('/admin/rooms');
}
@@ -79,8 +80,14 @@ export const useDeleteRoom = (room: IRoom | Pick, {
};
setModal(
- setModal(null)} confirmText={t('Yes_delete_it')}>
- {t('Delete_Room_Warning')}
+ setModal(null)}
+ confirmText={t('Yes_delete_it')}
+ >
+ {t('Delete_Room_Warning', { roomType })}
,
);
});
diff --git a/apps/meteor/client/views/marketplace/IframeModal.js b/apps/meteor/client/views/marketplace/IframeModal.js
index 259ac87fc89c..f69002e527c3 100644
--- a/apps/meteor/client/views/marketplace/IframeModal.js
+++ b/apps/meteor/client/views/marketplace/IframeModal.js
@@ -1,4 +1,5 @@
import { Box, Modal } from '@rocket.chat/fuselage';
+import { useTranslation } from '@rocket.chat/ui-contexts';
import React, { useEffect } from 'react';
const iframeMsgListener = (confirm, cancel) => (e) => {
@@ -13,6 +14,8 @@ const iframeMsgListener = (confirm, cancel) => (e) => {
};
const IframeModal = ({ url, confirm, cancel, wrapperHeight = 'x360', ...props }) => {
+ const t = useTranslation();
+
useEffect(() => {
const listener = iframeMsgListener(confirm, cancel);
@@ -26,7 +29,7 @@ const IframeModal = ({ url, confirm, cancel, wrapperHeight = 'x360', ...props })
return (
-
+
);
diff --git a/apps/meteor/client/views/meet/CallPage.tsx b/apps/meteor/client/views/meet/CallPage.tsx
index 87188221c091..b2d422b207f3 100644
--- a/apps/meteor/client/views/meet/CallPage.tsx
+++ b/apps/meteor/client/views/meet/CallPage.tsx
@@ -280,7 +280,9 @@ const CallPage: FC = ({
transform: 'scaleX(-1)',
display: isRemoteCameraOn ? 'block' : 'none',
}}
- >
+ >
+
+
import('react-aria').then((module) => ({ default: module.FocusScope })));
diff --git a/apps/meteor/client/views/modal/uikit/UiKitModal.tsx b/apps/meteor/client/views/modal/uikit/UiKitModal.tsx
index 1ce3efb28c5e..56242d399ac5 100644
--- a/apps/meteor/client/views/modal/uikit/UiKitModal.tsx
+++ b/apps/meteor/client/views/modal/uikit/UiKitModal.tsx
@@ -1,4 +1,4 @@
-import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
+import { useEffectEvent } from '@rocket.chat/fuselage-hooks';
import { UiKitContext } from '@rocket.chat/fuselage-ui-kit';
import { MarkupInteractionContext } from '@rocket.chat/gazzodown';
import type * as UiKit from '@rocket.chat/ui-kit';
@@ -22,59 +22,47 @@ const UiKitModal = ({ initialView }: UiKitModalProps) => {
const { view, errors, values, updateValues, state } = useUiKitView(initialView);
const contextValue = useModalContextValue({ view, values, updateValues });
- const handleSubmit = useMutableCallback((e: FormEvent) => {
+ const handleSubmit = useEffectEvent((e: FormEvent) => {
preventSyntheticEvent(e);
- void actionManager
- .emitInteraction(view.appId, {
- type: 'viewSubmit',
- payload: {
- view: {
- ...view,
- state,
- },
+ void actionManager.emitInteraction(view.appId, {
+ type: 'viewSubmit',
+ payload: {
+ view: {
+ ...view,
+ state,
},
- viewId: view.id,
- })
- .finally(() => {
- actionManager.disposeView(view.id);
- });
+ },
+ viewId: view.id,
+ });
});
- const handleCancel = useMutableCallback((e: FormEvent) => {
+ const handleCancel = useEffectEvent((e: FormEvent) => {
preventSyntheticEvent(e);
- void actionManager
- .emitInteraction(view.appId, {
- type: 'viewClosed',
- payload: {
- viewId: view.id,
- view: {
- ...view,
- state,
- },
- isCleared: false,
+ void actionManager.emitInteraction(view.appId, {
+ type: 'viewClosed',
+ payload: {
+ viewId: view.id,
+ view: {
+ ...view,
+ state,
},
- })
- .finally(() => {
- actionManager.disposeView(view.id);
- });
+ isCleared: false,
+ },
+ });
});
- const handleClose = useMutableCallback(() => {
- void actionManager
- .emitInteraction(view.appId, {
- type: 'viewClosed',
- payload: {
- viewId: view.id,
- view: {
- ...view,
- state,
- },
- isCleared: true,
+ const handleClose = useEffectEvent(() => {
+ void actionManager.emitInteraction(view.appId, {
+ type: 'viewClosed',
+ payload: {
+ viewId: view.id,
+ view: {
+ ...view,
+ state,
},
- })
- .finally(() => {
- actionManager.disposeView(view.id);
- });
+ isCleared: true,
+ },
+ });
});
return (
diff --git a/apps/meteor/client/views/omnichannel/ExternalFrameContainer.tsx b/apps/meteor/client/views/omnichannel/ExternalFrameContainer.tsx
index e339d48af103..3fe37eaced50 100644
--- a/apps/meteor/client/views/omnichannel/ExternalFrameContainer.tsx
+++ b/apps/meteor/client/views/omnichannel/ExternalFrameContainer.tsx
@@ -39,7 +39,7 @@ function ExternalFrameContainer() {
return (
-
+
);
}
diff --git a/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx b/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx
index afe7b162ea0b..2862b7e9dbb0 100644
--- a/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx
+++ b/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx
@@ -12,6 +12,7 @@ import {
Select,
ContextualbarFooter,
ButtonGroup,
+ CheckOption,
} from '@rocket.chat/fuselage';
import type { SelectOption } from '@rocket.chat/fuselage';
import { useMutableCallback, useUniqueId } from '@rocket.chat/fuselage-hooks';
@@ -185,6 +186,9 @@ const AgentEdit = ({ agentData, userDepartments, availableDepartments }: AgentEd
options={departmentsOptions}
{...field}
placeholder={t('Select_an_option')}
+ renderItem={({ label, ...props }) => (
+ {label}} />
+ )}
/>
)}
/>
diff --git a/apps/meteor/client/views/omnichannel/analytics/AnalyticsPage.tsx b/apps/meteor/client/views/omnichannel/analytics/AnalyticsPage.tsx
index 7ce0b699e28c..61f952e6f3c3 100644
--- a/apps/meteor/client/views/omnichannel/analytics/AnalyticsPage.tsx
+++ b/apps/meteor/client/views/omnichannel/analytics/AnalyticsPage.tsx
@@ -1,5 +1,5 @@
import type { SelectOption } from '@rocket.chat/fuselage';
-import { Box, Select, Margins, Field, FieldLabel, FieldRow, Label } from '@rocket.chat/fuselage';
+import { Box, Select, Margins, Field, FieldLabel, FieldRow, Label, Option } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import React, { useMemo, useState, useEffect } from 'react';
@@ -63,7 +63,13 @@ const AnalyticsPage = () => {
-
+
diff --git a/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursForm.tsx b/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursForm.tsx
index 71a3a5b27ed4..2156babf230d 100644
--- a/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursForm.tsx
+++ b/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursForm.tsx
@@ -1,5 +1,5 @@
import type { SelectOption } from '@rocket.chat/fuselage';
-import { InputBox, Field, MultiSelect, FieldGroup, Box, Select, FieldLabel, FieldRow } from '@rocket.chat/fuselage';
+import { InputBox, Field, MultiSelect, FieldGroup, Box, Select, FieldLabel, FieldRow, Callout } from '@rocket.chat/fuselage';
import { useUniqueId } from '@rocket.chat/fuselage-hooks';
import type { TranslationKey } from '@rocket.chat/ui-contexts';
import { useTranslation } from '@rocket.chat/ui-contexts';
@@ -70,6 +70,9 @@ const BusinessHoursForm = ({ type }: { type?: 'default' | 'custom' }) => {
render={({ field }) => }
/>
+
+ {t('Business_hours_will_update_automatically')}
+
{t('Open_days_of_the_week')}
diff --git a/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx b/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx
index 0f64e41d242f..74763121e000 100644
--- a/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx
+++ b/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx
@@ -15,6 +15,7 @@ import {
Button,
PaginatedSelectFiltered,
FieldHint,
+ Option,
} from '@rocket.chat/fuselage';
import { useDebouncedValue, useMutableCallback, useUniqueId } from '@rocket.chat/fuselage-hooks';
import { useToastMessageDispatch, useMethod, useEndpoint, useTranslation, useRouter } from '@rocket.chat/ui-contexts';
@@ -73,6 +74,7 @@ export type FormValues = {
fallbackForwardDepartment: string;
agentList: IDepartmentAgent[];
chatClosingTags: string[];
+ allowReceiveForwardOffline: boolean;
};
function withDefault(key: T | undefined | null, defaultValue: T) {
@@ -96,6 +98,7 @@ const getInitialValues = ({ department, agents, allowedToForwardData }: InitialV
fallbackForwardDepartment: withDefault(department?.fallbackForwardDepartment, ''),
chatClosingTags: department?.chatClosingTags ?? [],
agentList: agents || [],
+ allowReceiveForwardOffline: withDefault(department?.allowReceiveForwardOffline, false),
});
function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmentProps) {
@@ -151,6 +154,7 @@ function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmen
waitingQueueMessage,
departmentsAllowedToForward,
fallbackForwardDepartment,
+ allowReceiveForwardOffline,
} = data;
const payload = {
@@ -169,6 +173,7 @@ function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmen
waitingQueueMessage,
departmentsAllowedToForward: departmentsAllowedToForward?.map((dep) => dep.value),
fallbackForwardDepartment,
+ allowReceiveForwardOffline,
};
try {
@@ -214,6 +219,7 @@ function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmen
const fallbackForwardDepartmentField = useUniqueId();
const requestTagBeforeClosingChatField = useUniqueId();
const chatClosingTagsField = useUniqueId();
+ const allowReceiveForwardOffline = useUniqueId();
return (
@@ -412,6 +418,10 @@ function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmen
onChange={onChange}
onlyMyDepartments
showArchived
+ withTitle={false}
+ renderItem={({ label, ...props }) => (
+
)}
+
+
+ {t('Accept_receive_inquiry_no_online_agents')}
+
+
+
+ {t('Accept_receive_inquiry_no_online_agents_Hint')}
+
+
diff --git a/apps/meteor/client/views/omnichannel/directory/CallsContextualBarDirectory.tsx b/apps/meteor/client/views/omnichannel/directory/CallsContextualBarDirectory.tsx
index 010bb5f65517..bf35c50a6bf3 100644
--- a/apps/meteor/client/views/omnichannel/directory/CallsContextualBarDirectory.tsx
+++ b/apps/meteor/client/views/omnichannel/directory/CallsContextualBarDirectory.tsx
@@ -20,7 +20,7 @@ const CallsContextualBarDirectory: FC = () => {
const t = useTranslation();
- const handleCallsContextualbarCloseButtonClick = (): void => {
+ const handleClose = (): void => {
directoryRoute.push({ page: 'calls' });
};
@@ -52,9 +52,7 @@ const CallsContextualBarDirectory: FC = () => {
const room = data.room as unknown as IVoipRoom; // TODO Check why types are incompatible even though the endpoint returns an IVoipRooms
- return (
- {bar === 'info' && }
- );
+ return {bar === 'info' && };
};
export default CallsContextualBarDirectory;
diff --git a/apps/meteor/client/views/omnichannel/managers/ManagersTable.tsx b/apps/meteor/client/views/omnichannel/managers/ManagersTable.tsx
index 7662f740c7d3..afb2ae8f351b 100644
--- a/apps/meteor/client/views/omnichannel/managers/ManagersTable.tsx
+++ b/apps/meteor/client/views/omnichannel/managers/ManagersTable.tsx
@@ -100,9 +100,9 @@ const ManagersTable = () => {
)}
{isSuccess && data.users.length > 0 && (
<>
-
+
{headers}
-
+
{data.users.map((user) => (
diff --git a/apps/meteor/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.js b/apps/meteor/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.js
index e9e392ecc0f1..5b4d837d211c 100644
--- a/apps/meteor/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.js
+++ b/apps/meteor/client/views/omnichannel/realTimeMonitoring/RealTimeMonitoringPage.js
@@ -1,4 +1,4 @@
-import { Box, Select, Margins } from '@rocket.chat/fuselage';
+import { Box, Select, Margins, Option } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { useTranslation } from '@rocket.chat/ui-contexts';
import React, { useRef, useState, useMemo, useEffect, Fragment } from 'react';
@@ -80,6 +80,8 @@ const RealTimeMonitoringPage = () => {
placeholder={t('All')}
label={t('All')}
onlyMyDepartments
+ withTitle={false}
+ renderItem={({ label, ...props }) =>
diff --git a/apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts b/apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts
index 35f2a8bdceec..c4f7d0770815 100644
--- a/apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts
+++ b/apps/meteor/client/views/room/ImageGallery/hooks/useImagesList.ts
@@ -1,4 +1,4 @@
-import type { ChannelsImagesProps } from '@rocket.chat/rest-typings';
+import type { RoomsImagesProps } from '@rocket.chat/rest-typings';
import { useEndpoint } from '@rocket.chat/ui-contexts';
import { useCallback, useEffect, useState } from 'react';
@@ -7,7 +7,7 @@ import { useComponentDidUpdate } from '../../../../hooks/useComponentDidUpdate';
import { ImagesList } from '../../../../lib/lists/ImagesList';
export const useImagesList = (
- options: ChannelsImagesProps,
+ options: RoomsImagesProps,
): {
filesList: ImagesList;
initialItemCount: number;
@@ -27,7 +27,7 @@ export const useImagesList = (
}
}, [filesList, options]);
- const apiEndPoint = '/v1/channels.images';
+ const apiEndPoint = '/v1/rooms.images';
const getFiles = useEndpoint('GET', apiEndPoint);
diff --git a/apps/meteor/client/views/room/body/RoomBody.tsx b/apps/meteor/client/views/room/body/RoomBody.tsx
index c9e239fb723d..5f63f9f571ca 100644
--- a/apps/meteor/client/views/room/body/RoomBody.tsx
+++ b/apps/meteor/client/views/room/body/RoomBody.tsx
@@ -218,6 +218,7 @@ const RoomBody = (): ReactElement => {
{!isLayoutEmbedded && room.announcement && }
}
{item.type === 'audio' && (
)}
{item.type === 'video' && (
diff --git a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfo.tsx b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfo.tsx
index 2a9397364757..bc7714da06ae 100644
--- a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfo.tsx
+++ b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditRoomInfo.tsx
@@ -39,7 +39,6 @@ import RawText from '../../../../../components/RawText';
import RoomAvatarEditor from '../../../../../components/avatar/RoomAvatarEditor';
import { getDirtyFields } from '../../../../../lib/getDirtyFields';
import { useArchiveRoom } from '../../../../hooks/roomActions/useArchiveRoom';
-import { useDeleteRoom } from '../../../../hooks/roomActions/useDeleteRoom';
import { useEditRoomInitialValues } from './useEditRoomInitialValues';
import { useEditRoomPermissions } from './useEditRoomPermissions';
@@ -49,20 +48,39 @@ type EditRoomInfoProps = {
onClickBack: () => void;
};
+const title = {
+ team: 'Edit_team' as TranslationKey,
+ channel: 'Edit_channel' as TranslationKey,
+ discussion: 'Edit_discussion' as TranslationKey,
+};
+
const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) => {
const t = useTranslation();
const dispatchToastMessage = useToastMessageDispatch();
const isFederated = useMemo(() => isRoomFederated(room), [room]);
+ // eslint-disable-next-line no-nested-ternary
+ const roomType = 'prid' in room ? 'discussion' : room.teamId ? 'team' : 'channel';
const retentionPolicy = useSetting('RetentionPolicy_Enabled');
- const { handleDelete, canDeleteRoom } = useDeleteRoom(room);
const defaultValues = useEditRoomInitialValues(room);
+ const namesValidation = useSetting('UTF8_Channel_Names_Validation');
+ const allowSpecialNames = useSetting('UI_Allow_room_names_with_special_chars');
+ const checkTeamNameExists = useEndpoint('GET', '/v1/rooms.nameExists');
+
+ const teamNameRegex = useMemo(() => {
+ if (allowSpecialNames) {
+ return null;
+ }
+
+ return new RegExp(`^${namesValidation}$`);
+ }, [allowSpecialNames, namesValidation]);
const {
watch,
reset,
control,
handleSubmit,
+ getFieldState,
formState: { isDirty, dirtyFields, errors, isSubmitting },
} = useForm({ mode: 'onBlur', defaultValues });
@@ -71,7 +89,19 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) =>
[t],
);
- const { readOnly, archived, joinCodeRequired, hideSysMes, retentionEnabled, retentionMaxAge, retentionOverrideGlobal } = watch();
+ const { isDirty: isRoomNameDirty } = getFieldState('roomName');
+
+ const {
+ readOnly,
+ archived,
+ joinCodeRequired,
+ hideSysMes,
+ retentionEnabled,
+ retentionMaxAge,
+ retentionOverrideGlobal,
+ roomType: roomTypeP,
+ reactWhenReadOnly,
+ } = watch();
const {
canChangeType,
@@ -123,6 +153,20 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) =>
Promise.all([isDirty && handleUpdateRoomData(data), changeArchiving && handleArchive()].filter(Boolean)),
);
+ const validateName = async (name: string): Promise => {
+ if (!name || !isRoomNameDirty) return;
+ if (roomType === 'discussion') return;
+
+ if (teamNameRegex && !teamNameRegex?.test(name)) {
+ return t('Name_cannot_have_special_characters');
+ }
+
+ const { exists } = await checkTeamNameExists({ roomName: name });
+ if (exists) {
+ return t('Teams_Errors_Already_exists', { name });
+ }
+ };
+
const formId = useUniqueId();
const roomNameField = useUniqueId();
const roomDescriptionField = useUniqueId();
@@ -145,7 +189,7 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) =>
<>
{onClickBack && }
- {room.teamId ? t('edit-team') : t('edit-room')}
+ {t(`${title[roomType]}`)}
{onClickClose && }
@@ -166,22 +210,37 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) =>
}
+ rules={{
+ required: t('error-the-field-is-required', { field: t('Name') }),
+ validate: (value) => validateName(value),
+ }}
+ render={({ field }) => (
+
+ )}
/>
- {errors.roomName && {errors.roomName.message}}
+ {errors.roomName && {errors.roomName.message}}
- {canViewDescription && (
+ {canViewTopic && (
- {t('Description')}
+ {t('Topic')}
}
+ render={({ field }) => }
/>
+
+ {t('Displayed_next_to_name')}
+
)}
{canViewAnnouncement && (
@@ -191,23 +250,34 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) =>
}
+ render={({ field }) => (
+
+ )}
/>
+
+ {t('Information_to_keep_top_of_mind')}
+
)}
- {canViewTopic && (
+ {canViewDescription && (
- {t('Topic')}
+ {t('Description')}
}
+ render={({ field }) => }
/>
)}
+
{canViewType && (
@@ -229,7 +299,11 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) =>
)}
/>
- {t('Teams_New_Private_Description_Enabled')}
+
+
+ {roomTypeP === 'p' ? t('Only_invited_people') : t('Anyone_can_access')}
+
+
)}
{canViewReadOnly && (
@@ -250,7 +324,9 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) =>
)}
/>
- {t('Only_authorized_users_can_write_new_messages')}
+
+ {readOnly ? t('Read_only_field_hint_enabled', { roomType }) : t('Read_only_field_hint_disabled')}
+
)}
{readOnly && (
@@ -271,7 +347,11 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) =>
)}
/>
- {t('Only_authorized_users_can_react_to_messages')}
+
+
+ {reactWhenReadOnly ? t('Anyone_can_react_to_messages') : t('Only_authorized_users_can_react_to_messages')}
+
+
)}
{canViewArchived && (
@@ -282,10 +362,21 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) =>
control={control}
name='archived'
render={({ field: { value, ...field } }) => (
-
+
)}
/>
+ {archived && (
+
+ {t('New_messages_cannot_be_sent')}
+
+ )}
)}
{canViewJoinCode && (
@@ -300,13 +391,15 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) =>
)}
/>
-
- }
- />
-
+ {joinCodeRequired && (
+
+ }
+ />
+
+ )}
)}
{canViewHideSysMes && (
@@ -330,27 +423,13 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) =>
{...field}
options={sysMesOptions}
disabled={!hideSysMes || isFederated}
- placeholder={t('Select_an_option')}
+ placeholder={t('Select_messages_to_hide')}
/>
)}
/>
)}
- {canViewEncrypted && (
-
-
- {t('Encrypted')}
- (
-
- )}
- />
-
-
- )}
{retentionPolicy && (
@@ -428,6 +507,29 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) =>
/>
+ {canViewEncrypted && (
+
+
+ {t('Encrypted')}
+ (
+
+ )}
+ />
+
+
+ {t('Encrypted_field_hint')}
+
+
+ )}
>
)}
@@ -441,17 +543,10 @@ const EditRoomInfo = ({ room, onClickClose, onClickBack }: EditRoomInfoProps) =>
reset(defaultValues)}>
{t('Reset')}
-
+
{t('Save')}
-
-
-
- {t('Delete')}
-
-
-
>
);
diff --git a/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.tsx b/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.tsx
index 38a7593937ff..80abe104c88e 100644
--- a/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.tsx
+++ b/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.tsx
@@ -34,6 +34,7 @@ const RoomInfo = ({ room, icon, onClickBack, onClickClose, onClickEnterRoom, onC
const t = useTranslation();
const { name, fname, description, topic, archived, broadcast, announcement } = room;
const roomTitle = fname || name;
+ const isDiscussion = 'prid' in room;
const retentionPolicy = useRetentionPolicy(room);
const memoizedActions = useRoomActions(room, { onClickEnterRoom, onClickEdit }, resetState);
@@ -48,7 +49,7 @@ const RoomInfo = ({ room, icon, onClickBack, onClickClose, onClickEnterRoom, onC