Skip to content

Commit

Permalink
refactor: Pay the debt of some TODO comments (#30338)
Browse files Browse the repository at this point in the history
  • Loading branch information
tassoevan authored Jan 23, 2024
1 parent 9cb9796 commit 0312a6c
Show file tree
Hide file tree
Showing 21 changed files with 161 additions and 191 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {
MessageAttachment,
FileAttachmentProps,
ImageAttachmentProps,
AudioAttachmentProps,
VideoAttachmentProps,
Expand Down Expand Up @@ -56,7 +56,7 @@ export const sendFileLivechatMessage = async ({ roomId, visitorToken, file, msgD

const fileUrl = file.name && FileUpload.getPath(`${file._id}/${encodeURI(file.name)}`);

const attachment: MessageAttachment = {
const attachment: Partial<FileAttachmentProps> = {
title: file.name,
type: 'file',
description: file.description,
Expand Down
13 changes: 7 additions & 6 deletions apps/meteor/client/components/GenericTable/GenericTable.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { Box, Table } from '@rocket.chat/fuselage';
import type { ReactNode, TableHTMLAttributes } from 'react';
import React, { forwardRef } from 'react';
import React, { type ForwardedRef, type ReactNode, type TableHTMLAttributes, forwardRef } from 'react';

import ScrollableContentWrapper from '../ScrollableContentWrapper';

type GenericTableProps = {
fixed?: boolean;
children: ReactNode;
} & TableHTMLAttributes<HTMLTableElement>;
} & Omit<TableHTMLAttributes<HTMLTableElement>, 'is'>;

export const GenericTable = forwardRef<HTMLElement, GenericTableProps>(function GenericTable({ fixed = true, children, ...props }, ref) {
export const GenericTable = forwardRef(function GenericTable(
{ fixed = true, children, ...props }: GenericTableProps,
ref: ForwardedRef<HTMLElement>,
) {
return (
<Box mi='neg-x24' pi={24} flexShrink={1} flexGrow={1} ref={ref} overflow='hidden'>
<ScrollableContentWrapper overflowX>
{/* TODO: Fix fuselage */}
<Table fixed={fixed} sticky {...(props as any)}>
<Table fixed={fixed} sticky {...props}>
{children}
</Table>
</ScrollableContentWrapper>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ILivechatDepartment } from '@rocket.chat/core-typings';
import type { ILivechatDepartment, Serialized } from '@rocket.chat/core-typings';
import {
Field,
FieldGroup,
Expand Down Expand Up @@ -29,7 +29,7 @@ const CloseChatModal = ({
onCancel,
onConfirm,
}: {
department?: ILivechatDepartment | null;
department?: Serialized<ILivechatDepartment | null>;
visitorEmail?: string;
onCancel: () => void;
onConfirm: (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import type { ILivechatDepartment, ILivechatDepartmentAgents } from '@rocket.chat/core-typings';
import type { ReactElement } from 'react';
import type { ILivechatDepartment } from '@rocket.chat/core-typings';
import { useEndpoint } from '@rocket.chat/ui-contexts';
import { useQuery } from '@tanstack/react-query';
import React from 'react';

import { AsyncStatePhase } from '../../../hooks/useAsyncState';
import { useEndpointData } from '../../../hooks/useEndpointData';
import { FormSkeleton } from '../Skeleton';
import CloseChatModal from './CloseChatModal';

Expand All @@ -21,32 +20,14 @@ const CloseChatModalData = ({
tags?: string[],
preferences?: { omnichannelTranscriptPDF: boolean; omnichannelTranscriptEmail: boolean },
) => Promise<void>;
}): ReactElement => {
const { value: data, phase: state } = useEndpointData('/v1/livechat/department/:_id', { keys: { _id: departmentId } });
}) => {
const getDepartment = useEndpoint('GET', '/v1/livechat/department/:_id', { _id: departmentId });
const { data, isLoading } = useQuery(['/v1/livechat/department/:_id', departmentId], () => getDepartment({}));

if ([state].includes(AsyncStatePhase.LOADING)) {
if (isLoading) {
return <FormSkeleton />;
}

// TODO: chapter day: fix issue with rest typing
// TODO: This is necessary because of a weird problem
// There is an endpoint livechat/department/${departmentId}/agents
// that is causing the problem. type A | type B | undefined

return (
<CloseChatModal
onCancel={onCancel}
onConfirm={onConfirm}
visitorEmail={visitorEmail}
department={
(
data as {
department: ILivechatDepartment | null;
agents?: ILivechatDepartmentAgents[];
}
).department
}
/>
);
return <CloseChatModal onCancel={onCancel} onConfirm={onConfirm} visitorEmail={visitorEmail} department={data?.department} />;
};
export default CloseChatModalData;
23 changes: 10 additions & 13 deletions apps/meteor/client/components/TextCopy.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Box, Button, Scrollable } from '@rocket.chat/fuselage';
import { useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts';
import { useTranslation } from '@rocket.chat/ui-contexts';
import type { ComponentProps, ReactElement } from 'react';
import React, { useCallback } from 'react';
import React from 'react';

import useClipboardWithToast from '../hooks/useClipboardWithToast';

const defaultWrapperRenderer = (text: string): ReactElement => (
<Box fontFamily='mono' alignSelf='center' fontScale='p2' style={{ wordBreak: 'break-all' }} mie={4} flexGrow={1} maxHeight='x108'>
Expand All @@ -14,19 +16,14 @@ type TextCopyProps = {
wrapper?: (text: string) => ReactElement;
} & ComponentProps<typeof Box>;

// TODO: useClipboard instead of navigator API.
const TextCopy = ({ text, wrapper = defaultWrapperRenderer, ...props }: TextCopyProps): ReactElement => {
const t = useTranslation();
const dispatchToastMessage = useToastMessageDispatch();

const onClick = useCallback(() => {
try {
navigator.clipboard.writeText(text);
dispatchToastMessage({ type: 'success', message: t('Copied') });
} catch (e) {
dispatchToastMessage({ type: 'error', message: e });
}
}, [dispatchToastMessage, t, text]);
const { copy } = useClipboardWithToast(text);

const handleClick = () => {
copy();
};

return (
<Box
Expand All @@ -41,7 +38,7 @@ const TextCopy = ({ text, wrapper = defaultWrapperRenderer, ...props }: TextCopy
{...props}
>
<Scrollable vertical>{wrapper(text)}</Scrollable>
<Button icon='copy' secondary square small flexShrink={0} onClick={onClick} title={t('Copy')} />
<Button icon='copy' secondary square small flexShrink={0} onClick={handleClick} title={t('Copy')} />
</Box>
);
};
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,81 +1,52 @@
import { Icon } from '@rocket.chat/fuselage';
import { useConnectionStatus, useTranslation } from '@rocket.chat/ui-contexts';
import type { MouseEventHandler } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { Box, Icon } 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 './ConnectionStatusBar.styles.css';

// TODO: frontend chapter day - fix unknown translation keys

const getReconnectCountdown = (retryTime: number): number => {
const timeDiff = retryTime - Date.now();
return (timeDiff > 0 && Math.round(timeDiff / 1000)) || 0;
};

const useReconnectCountdown = (
retryTime: number | undefined,
status: 'connected' | 'connecting' | 'failed' | 'waiting' | 'offline',
): number => {
const reconnectionTimerRef = useRef<ReturnType<typeof setInterval>>();
const [reconnectCountdown, setReconnectCountdown] = useState(() => (retryTime ? getReconnectCountdown(retryTime) : 0));

useEffect(() => {
if (status === 'waiting') {
if (reconnectionTimerRef.current) {
return;
}

reconnectionTimerRef.current = setInterval(() => {
retryTime && setReconnectCountdown(getReconnectCountdown(retryTime));
}, 500);
return;
}

reconnectionTimerRef.current && clearInterval(reconnectionTimerRef.current);
reconnectionTimerRef.current = undefined;
}, [retryTime, status]);

useEffect(
() => (): void => {
reconnectionTimerRef.current && clearInterval(reconnectionTimerRef.current);
},
[],
);

return reconnectCountdown;
};
import { useReconnectCountdown } from './useReconnectCountdown';

function ConnectionStatusBar() {
const { connected, retryTime, status, reconnect } = useConnectionStatus();
const reconnectCountdown = useReconnectCountdown(retryTime, status);
const t = useTranslation();
const { t } = useTranslation();

if (connected) {
return null;
}

const handleRetryClick: MouseEventHandler<HTMLAnchorElement> = (event) => {
const handleRetryClick = (event: MouseEvent<HTMLAnchorElement>) => {
event.preventDefault();
reconnect?.();
};

return (
<div className='ConnectionStatusBar' role='alert'>
<strong>
<Icon name='warning' /> {t('meteor_status' as Parameters<typeof t>[0], { context: status })}
</strong>

{status === 'waiting' && <> {t('meteor_status_reconnect_in', { count: reconnectCountdown })}</>}

{['waiting', 'offline'].includes(status) && (
<>
{' '}
<a className='ConnectionStatusBar__retry-link' href='#' onClick={handleRetryClick}>
{t('meteor_status_try_now' as Parameters<typeof t>[0], { context: status })}
</a>
</>
)}
</div>
<Box
color='status-font-on-warning'
bg='status-background-warning'
position='fixed'
zIndex={1000000}
insetBlockStart={0}
p={2}
width='100%'
textAlign='center'
borderBlockEndWidth={1}
role='alert'
>
<Icon name='warning' />{' '}
<Box is='span' withRichContent>
<strong>{t('meteor_status', { context: status })}</strong>
{status === 'waiting' && <> {t('meteor_status_reconnect_in', { count: reconnectCountdown })}</>}
{['waiting', 'offline'].includes(status) && (
<>
{' '}
<a href='#' onClick={handleRetryClick} role='button'>
{t('meteor_status_try_now', { context: status })}
</a>
</>
)}
</Box>
</Box>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { useEffect, useRef, useState } from 'react';

const getReconnectCountdown = (retryTime: number): number => {
const timeDiff = retryTime - Date.now();
return (timeDiff > 0 && Math.round(timeDiff / 1000)) || 0;
};

export const useReconnectCountdown = (
retryTime: number | undefined,
status: 'connected' | 'connecting' | 'failed' | 'waiting' | 'offline',
): number => {
const reconnectionTimerRef = useRef<ReturnType<typeof setInterval>>();
const [reconnectCountdown, setReconnectCountdown] = useState(() => (retryTime ? getReconnectCountdown(retryTime) : 0));

useEffect(() => {
if (status === 'waiting') {
if (reconnectionTimerRef.current) {
return;
}

reconnectionTimerRef.current = setInterval(() => {
retryTime && setReconnectCountdown(getReconnectCountdown(retryTime));
}, 500);
return;
}

reconnectionTimerRef.current && clearInterval(reconnectionTimerRef.current);
reconnectionTimerRef.current = undefined;
}, [retryTime, status]);

useEffect(
() => (): void => {
reconnectionTimerRef.current && clearInterval(reconnectionTimerRef.current);
},
[],
);

return reconnectCountdown;
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { ReactElement } from 'react';
import React, { memo } from 'react';

import DefaultAttachment from './DefaultAttachment';
import { FileAttachment } from './FileAttachment';
import FileAttachment from './FileAttachment';
import { QuoteAttachment } from './QuoteAttachment';

type AttachmentsItemProps = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import type { FileAttachmentProps } from '@rocket.chat/core-typings';
import { isFileAudioAttachment, isFileImageAttachment, isFileVideoAttachment } from '@rocket.chat/core-typings';
import type { FC } from 'react';
import { type FileAttachmentProps, isFileAudioAttachment, isFileImageAttachment, isFileVideoAttachment } from '@rocket.chat/core-typings';
import React from 'react';

import { AudioAttachment } from './file/AudioAttachment';
import { GenericFileAttachment } from './file/GenericFileAttachment';
import { ImageAttachment } from './file/ImageAttachment';
import { VideoAttachment } from './file/VideoAttachment';
import AudioAttachment from './file/AudioAttachment';
import GenericFileAttachment from './file/GenericFileAttachment';
import ImageAttachment from './file/ImageAttachment';
import VideoAttachment from './file/VideoAttachment';

export const FileAttachment: FC<FileAttachmentProps & { id: string | undefined }> = (attachment) => {
const FileAttachment = (attachment: FileAttachmentProps) => {
if (isFileImageAttachment(attachment)) {
return <ImageAttachment {...attachment} />;
}

if (isFileAudioAttachment(attachment)) {
return <AudioAttachment {...attachment} />;
}

if (isFileVideoAttachment(attachment)) {
return <VideoAttachment {...attachment} />;
}

return <GenericFileAttachment {...(attachment as any)} />; // TODO: fix this
return <GenericFileAttachment {...attachment} />;
};

export default FileAttachment;
Loading

0 comments on commit 0312a6c

Please sign in to comment.