Skip to content

Commit

Permalink
Merge branch 'develop' into chore/remove-broken-listener-call-on-startup
Browse files Browse the repository at this point in the history
  • Loading branch information
kodiakhq[bot] authored Aug 28, 2024
2 parents 2e84787 + 5cbbb45 commit 13fa9d0
Show file tree
Hide file tree
Showing 37 changed files with 584 additions and 417 deletions.
5 changes: 5 additions & 0 deletions .changeset/stupid-pigs-share.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rocket.chat/meteor': minor
---

Wraps some room settings in an accordion advanced settings section in room edit contextual bar to improve organization
5 changes: 5 additions & 0 deletions .changeset/wise-avocados-taste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rocket.chat/meteor': patch
---

Fixes an issue where multi-step modals were closing unexpectedly
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ const GenericModal = ({
{tagline && <Modal.Tagline>{tagline}</Modal.Tagline>}
<Modal.Title id={`${genericModalId}-title`}>{title ?? t('Are_you_sure')}</Modal.Title>
</Modal.HeaderText>
<Modal.Close aria-label={t('Close')} onClick={handleCloseButtonClick} />
{onClose && <Modal.Close aria-label={t('Close')} onClick={handleCloseButtonClick} />}
</Modal.Header>
<Modal.Content fontScale='p2'>{children}</Modal.Content>
<Modal.Footer justifyContent={dontAskAgain ? 'space-between' : 'end'}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,13 @@
import { Skeleton } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import type { ComponentProps } from 'react';
import React from 'react';

import GenericModal from './GenericModal';

const GenericModalSkeleton = ({ onClose, ...props }: ComponentProps<typeof GenericModal>) => {
const t = useTranslation();

return (
<GenericModal
{...props}
variant='warning'
onClose={onClose}
title={<Skeleton width='50%' />}
confirmText={t('Cancel')}
onConfirm={onClose}
>
<Skeleton width='full' />
</GenericModal>
);
};
const GenericModalSkeleton = (props: ComponentProps<typeof GenericModal>) => (
<GenericModal {...props} icon={null} title={<Skeleton width='50%' />}>
<Skeleton width='full' />
</GenericModal>
);

export default GenericModalSkeleton;
Original file line number Diff line number Diff line change
@@ -1,53 +1,29 @@
import { UserStatus, type IOmnichannelRoomFromAppSource } from '@rocket.chat/core-typings';
import { type IOmnichannelSourceFromApp } from '@rocket.chat/core-typings';
import { Icon, Box } from '@rocket.chat/fuselage';
import type { ComponentProps, ReactElement } from 'react';
import type { ComponentProps } from 'react';
import React from 'react';

import { AsyncStatePhase } from '../../../lib/asyncState/AsyncStatePhase';
import { useOmnichannelRoomIcon } from './context/OmnichannelRoomIconContext';

const colors = {
busy: 'status-font-on-danger',
away: 'status-font-on-warning',
online: 'status-font-on-success',
offline: 'annotation',
disabled: 'annotation',
type OmnichannelAppSourceRoomIconProps = {
source: IOmnichannelSourceFromApp;
color: ComponentProps<typeof Box>['color'];
size: ComponentProps<typeof Icon>['size'];
placement: 'sidebar' | 'default';
};

const convertBoxSizeToNumber = (boxSize: ComponentProps<typeof Icon>['size']): number => {
switch (boxSize) {
case 'x20': {
return 20;
}
case 'x24': {
return 24;
}
case 'x16':
default: {
return 16;
}
}
};
export const OmnichannelAppSourceRoomIcon = ({ source, color, size, placement }: OmnichannelAppSourceRoomIconProps) => {
const icon = (placement === 'sidebar' && source.sidebarIcon) || source.defaultIcon;
const { phase, value } = useOmnichannelRoomIcon(source.id, icon || '');

export const OmnichannelAppSourceRoomIcon = ({
room,
size = 16,
placement = 'default',
}: {
room: IOmnichannelRoomFromAppSource;
size: ComponentProps<typeof Icon>['size'];
placement: 'sidebar' | 'default';
}): ReactElement => {
const color = colors[room.v.status || UserStatus.OFFLINE];
const icon = (placement === 'sidebar' && room.source.sidebarIcon) || room.source.defaultIcon;
const { phase, value } = useOmnichannelRoomIcon(room.source.id, icon || '');
const fontSize = convertBoxSizeToNumber(size);
if ([AsyncStatePhase.REJECTED, AsyncStatePhase.LOADING].includes(phase)) {
return <Icon name='headset' size={size} color={color} />;
}

return (
<Box size={fontSize} color={color}>
<Box is='svg' size={fontSize} aria-hidden='true'>
<Box size={size} color={color}>
<Box is='svg' size={size} aria-hidden='true'>
<Box is='use' href={`#${value}`} />
</Box>
</Box>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
import type { IOmnichannelRoom } from '@rocket.chat/core-typings';
import type { IOmnichannelSource } from '@rocket.chat/core-typings';
import { Icon } from '@rocket.chat/fuselage';
import type { ComponentProps, ReactElement } from 'react';
import type { ComponentProps } from 'react';
import React from 'react';

const colors = {
busy: 'status-font-on-danger',
away: 'status-font-on-warning',
online: 'status-font-on-success',
offline: 'annotation',
disabled: 'annotation',
};

const iconMap = {
widget: 'livechat',
email: 'mail',
Expand All @@ -20,13 +12,13 @@ const iconMap = {
other: 'headset',
} as const;

export const OmnichannelCoreSourceRoomIcon = ({
room,
size = 'x16',
}: {
room: IOmnichannelRoom;
type OmnichannelCoreSourceRoomIconProps = {
source: IOmnichannelSource;
color: ComponentProps<typeof Icon>['color'];
size: ComponentProps<typeof Icon>['size'];
}): ReactElement => {
const icon = iconMap[room.source?.type || 'other'] || 'headset';
return <Icon name={icon} size={size} color={colors[room.v?.status || 'offline']} />;
};

export const OmnichannelCoreSourceRoomIcon = ({ source, color, size }: OmnichannelCoreSourceRoomIconProps) => {
const icon = iconMap[source?.type || 'other'] || 'headset';
return <Icon name={icon} size={size} color={color} />;
};
Original file line number Diff line number Diff line change
@@ -1,23 +1,34 @@
import type { IOmnichannelRoom } from '@rocket.chat/core-typings';
import { isOmnichannelRoomFromAppSource } from '@rocket.chat/core-typings';
import type { IOmnichannelSource } from '@rocket.chat/core-typings';
import { UserStatus, isOmnichannelSourceFromApp } from '@rocket.chat/core-typings';
import type { Icon } from '@rocket.chat/fuselage';
import type { ComponentProps, ReactElement } from 'react';
import type { ComponentProps } from 'react';
import React from 'react';

import { OmnichannelAppSourceRoomIcon } from './OmnichannelAppSourceRoomIcon';
import { OmnichannelCoreSourceRoomIcon } from './OmnichannelCoreSourceRoomIcon';

export const OmnichannelRoomIcon = ({
room,
size,
placement = 'default',
}: {
room: IOmnichannelRoom;
const colors = {
busy: 'status-font-on-danger',
away: 'status-font-on-warning',
online: 'status-font-on-success',
offline: 'annotation',
disabled: 'annotation',
} as const;

type OmnichannelRoomIconProps = {
source: IOmnichannelSource;
color?: ComponentProps<typeof Icon>['color'];
status?: UserStatus;
size: ComponentProps<typeof Icon>['size'];
placement: 'sidebar' | 'default';
}): ReactElement => {
if (isOmnichannelRoomFromAppSource(room)) {
return <OmnichannelAppSourceRoomIcon placement={placement} room={room} size={size} />;
placement?: 'sidebar' | 'default';
};

export const OmnichannelRoomIcon = ({ source, color, status, size = 'x16', placement = 'default' }: OmnichannelRoomIconProps) => {
const iconColor = color ?? colors[status || UserStatus.OFFLINE];

if (isOmnichannelSourceFromApp(source)) {
return <OmnichannelAppSourceRoomIcon source={source} placement={placement} color={iconColor} size={size} />;
}
return <OmnichannelCoreSourceRoomIcon room={room} size={size} />;

return <OmnichannelCoreSourceRoomIcon source={source} color={iconColor} size={size} />;
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import DOMPurify from 'dompurify';

import { sdk } from '../../../../../app/utils/client/lib/SDKClient';

const OmnichannelRoomIcon = new (class extends Emitter {
const OmnichannelRoomIconManager = new (class extends Emitter {
icons = new Map<string, string>();

constructor() {
Expand All @@ -23,7 +23,7 @@ const OmnichannelRoomIcon = new (class extends Emitter {
sdk.rest
.send(`/apps/public/${appId}/get-sidebar-icon?icon=${icon}`, 'GET')
.then((response: any) => {
response.text().then((text: any) => {
response.text().then((text: string) => {
this.icons.set(
`${appId}-${icon}`,
DOMPurify.sanitize(text, {
Expand All @@ -44,4 +44,4 @@ const OmnichannelRoomIcon = new (class extends Emitter {
}
})();

export default OmnichannelRoomIcon;
export default OmnichannelRoomIconManager;
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { useSyncExternalStore } from 'use-sync-external-store/shim';
import type { AsyncState } from '../../../../lib/asyncState/AsyncState';
import { AsyncStatePhase } from '../../../../lib/asyncState/AsyncStatePhase';
import { OmnichannelRoomIconContext } from '../context/OmnichannelRoomIconContext';
import OmnichannelRoomIcon from '../lib/OmnichannelRoomIcon';
import OmnichannelRoomIconManager from '../lib/OmnichannelRoomIconManager';

let icons = Array.from(OmnichannelRoomIcon.icons.values());
let icons = Array.from(OmnichannelRoomIconManager.icons.values());

type OmnichannelRoomIconProviderProps = {
children?: ReactNode;
Expand All @@ -18,8 +18,8 @@ export const OmnichannelRoomIconProvider = ({ children }: OmnichannelRoomIconPro
const svgIcons = useSyncExternalStore(
useCallback(
(callback): (() => void) =>
OmnichannelRoomIcon.on('change', () => {
icons = Array.from(OmnichannelRoomIcon.icons.values());
OmnichannelRoomIconManager.on('change', () => {
icons = Array.from(OmnichannelRoomIconManager.icons.values());
callback();
}),
[],
Expand All @@ -31,7 +31,7 @@ export const OmnichannelRoomIconProvider = ({ children }: OmnichannelRoomIconPro
<OmnichannelRoomIconContext.Provider
value={useMemo(() => {
const extractSnapshot = (app: string, iconName: string): AsyncState<string> => {
const icon = OmnichannelRoomIcon.get(app, iconName);
const icon = OmnichannelRoomIconManager.get(app, iconName);

if (icon) {
return {
Expand All @@ -57,7 +57,7 @@ export const OmnichannelRoomIconProvider = ({ children }: OmnichannelRoomIconPro
iconName: string,
): [subscribe: (onStoreChange: () => void) => () => void, getSnapshot: () => AsyncState<string>] => [
(callback): (() => void) =>
OmnichannelRoomIcon.on(`${app}-${iconName}`, () => {
OmnichannelRoomIconManager.on(`${app}-${iconName}`, () => {
snapshots.set(`${app}-${iconName}`, extractSnapshot(app, iconName));

// Then we call the callback (onStoreChange), signaling React to re-render
Expand Down
6 changes: 3 additions & 3 deletions apps/meteor/client/components/RoomIcon/RoomIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { IOmnichannelRoom, IRoom } from '@rocket.chat/core-typings';
import type { IRoom } from '@rocket.chat/core-typings';
import { isOmnichannelRoom } from '@rocket.chat/core-typings';
import { Icon } from '@rocket.chat/fuselage';
import type { ComponentProps, ReactElement } from 'react';
Expand All @@ -24,8 +24,8 @@ export const RoomIcon = ({
return <Icon name='phone' size={size} />;
}

if (isOmnichannelRoom(room as IRoom)) {
return <OmnichannelRoomIcon placement={placement} room={room as IOmnichannelRoom} size={size} />;
if (isOmnichannelRoom(room)) {
return <OmnichannelRoomIcon placement={placement} source={room.source} status={room.v?.status} size={size} />;
}

if (isValidElement<any>(iconPropsOrReactNode)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const SourceField = ({ room }: SourceFieldProps) => {
<Label>{t('Channel')}</Label>
<Info>
<Box display='flex' alignItems='center'>
<OmnichannelRoomIcon room={room} size='x24' placement='default' />
<OmnichannelRoomIcon source={room.source} status={room.v.status} size='x24' />
<Label mi={8} mbe='0'>
{defaultTypesLabels[room.source.type] || roomSource}
</Label>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { OperationParams } from '@rocket.chat/rest-typings';
import type { MutableRefObject } from 'react';
import React from 'react';

import { useEndpointData } from '../../../../hooks/useEndpointData';
Expand All @@ -8,9 +10,14 @@ const overviewInitalValue = {
value: '-',
};

type AgentsOverviewChartsProps = {
params: OperationParams<'GET', '/v1/livechat/analytics/dashboards/agents-productivity-totalizers'>;
reloadRef: MutableRefObject<{ [x: string]: () => void }>;
};

const initialData = [overviewInitalValue, overviewInitalValue, overviewInitalValue];

const AgentsOverview = ({ params, reloadRef, ...props }) => {
const AgentsOverview = ({ params, reloadRef, ...props }: AgentsOverviewChartsProps) => {
const {
value: data,
phase: state,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { OperationParams } from '@rocket.chat/rest-typings';
import type { MutableRefObject } from 'react';
import React from 'react';

import { useEndpointData } from '../../../../hooks/useEndpointData';
Expand All @@ -9,7 +11,12 @@ const initialData = [
{ title: '', value: '00:00:00' },
];

const ChatsOverview = ({ params, reloadRef, ...props }) => {
type ChatsOverviewProps = {
params: OperationParams<'GET', '/v1/livechat/analytics/dashboards/chats-totalizers'>;
reloadRef: MutableRefObject<{ [x: string]: () => void }>;
};

const ChatsOverview = ({ params, reloadRef, ...props }: ChatsOverviewProps) => {
const { value: data, phase: state, reload } = useEndpointData('/v1/livechat/analytics/dashboards/chats-totalizers', { params });

reloadRef.current.chatsOverview = reload;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { OperationParams } from '@rocket.chat/rest-typings';
import type { MutableRefObject } from 'react';
import React from 'react';

import { useEndpointData } from '../../../../hooks/useEndpointData';
Expand All @@ -10,7 +12,12 @@ const overviewInitalValue = {

const initialData = [overviewInitalValue, overviewInitalValue, overviewInitalValue, overviewInitalValue];

const ConversationOverview = ({ params, reloadRef, ...props }) => {
type ConversationOverviewProps = {
params: OperationParams<'GET', '/v1/livechat/analytics/dashboards/conversation-totalizers'>;
reloadRef: MutableRefObject<{ [x: string]: () => void }>;
};

const ConversationOverview = ({ params, reloadRef, ...props }: ConversationOverviewProps) => {
const { value: data, phase: state, reload } = useEndpointData('/v1/livechat/analytics/dashboards/conversation-totalizers', { params });

reloadRef.current.conversationOverview = reload;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { OperationParams } from '@rocket.chat/rest-typings';
import type { MutableRefObject } from 'react';
import React from 'react';

import { useEndpointData } from '../../../../hooks/useEndpointData';
Expand All @@ -7,7 +9,12 @@ const defaultValue = { title: '', value: '00:00:00' };

const initialData = [defaultValue, defaultValue, defaultValue, defaultValue];

const ProductivityOverview = ({ params, reloadRef, ...props }) => {
type ProductivityOverviewProps = {
params: OperationParams<'GET', '/v1/livechat/analytics/dashboards/productivity-totalizers'>;
reloadRef: MutableRefObject<{ [x: string]: () => void }>;
};

const ProductivityOverview = ({ params, reloadRef, ...props }: ProductivityOverviewProps) => {
const { value: data, phase: state, reload } = useEndpointData('/v1/livechat/analytics/dashboards/productivity-totalizers', { params });

reloadRef.current.productivityOverview = reload;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type HeaderIconWithRoomProps = {
const HeaderIconWithRoom = ({ room }: HeaderIconWithRoomProps): ReactElement => {
const icon = useRoomIcon(room);
if (isOmnichannelRoom(room)) {
return <OmnichannelRoomIcon room={room} size='x20' placement='default' />;
return <OmnichannelRoomIcon source={room.source} status={room.v?.status} size='x20' />;
}

return <HeaderIcon icon={icon} />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type HeaderIconWithRoomProps = {
const HeaderIconWithRoom = ({ room }: HeaderIconWithRoomProps): ReactElement => {
const icon = useRoomIcon(room);
if (isOmnichannelRoom(room)) {
return <OmnichannelRoomIcon room={room} size='x20' placement='default' />;
return <OmnichannelRoomIcon source={room.source} status={room.v?.status} size='x20' />;
}

return <HeaderIcon icon={icon} />;
Expand Down
Loading

0 comments on commit 13fa9d0

Please sign in to comment.