Chore: Attachment Definitions and UiKitDefinitions (#22354)

Co-authored-by: Diego Sampaio <chinello@gmail.com>
pull/22430/head
Guilherme Gazzo 4 years ago committed by GitHub
parent 16520f1a98
commit 6924f93e2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      .eslintrc
  2. 3
      .storybook/main.js
  3. 4
      app/apps/lib/misc/determineFileType.js
  4. 19
      app/apps/server/bridges/uploads.ts
  5. 8
      app/integrations/server/lib/triggerHandler.js
  6. 4
      app/integrations/server/methods/outgoing/replayOutgoingIntegration.js
  7. 4
      app/integrations/server/triggers.js
  8. 11
      app/livechat/server/business-hour/Default.ts
  9. 24
      client/components/Message/Attachments/ActionAttachtment.tsx
  10. 2
      client/components/Message/Attachments/Attachment/Block.tsx
  11. 8
      client/components/Message/Attachments/Attachment/Download.tsx
  12. 2
      client/components/Message/Attachments/Attachment/TitleLink.tsx
  13. 10
      client/components/Message/Attachments/AttachmentProps.ts
  14. 39
      client/components/Message/Attachments/Attachments.stories.tsx
  15. 6
      client/components/Message/Attachments/Attachments.tsx
  16. 51
      client/components/Message/Attachments/DefaultAttachment.tsx
  17. 2
      client/components/Message/Attachments/FieldsAttachment/index.tsx
  18. 10
      client/components/Message/Attachments/Files/AudioAttachment.tsx
  19. 6
      client/components/Message/Attachments/Files/GenericFileAttachment.tsx
  20. 15
      client/components/Message/Attachments/Files/ImageAttachment.tsx
  21. 9
      client/components/Message/Attachments/Files/PDFAttachment.tsx
  22. 10
      client/components/Message/Attachments/Files/VideoAttachment.tsx
  23. 31
      client/components/Message/Attachments/Files/index.tsx
  24. 20
      client/components/Message/Attachments/Item.tsx
  25. 13
      client/components/Message/Attachments/QuoteAttachment.tsx
  26. 2
      client/components/Message/Attachments/components/Image.tsx
  27. 3
      client/components/Message/Attachments/index.tsx
  28. 2
      client/contexts/ServerContext/endpoints/v1/channels/files.ts
  29. 37
      definition/IMessage.ts
  30. 72
      definition/IMessage/IMessage.ts
  31. 5
      definition/IMessage/MessageAttachment/FieldProps.ts
  32. 15
      definition/IMessage/MessageAttachment/Files/AudioAttachmentProps.ts
  33. 0
      definition/IMessage/MessageAttachment/Files/Dimensions.ts
  34. 14
      definition/IMessage/MessageAttachment/Files/FileAttachmentProps.ts
  35. 0
      definition/IMessage/MessageAttachment/Files/FileProp.ts
  36. 18
      definition/IMessage/MessageAttachment/Files/ImageAttachmentProps.ts
  37. 7
      definition/IMessage/MessageAttachment/Files/PDFAttachmentProps.ts
  38. 15
      definition/IMessage/MessageAttachment/Files/VideoAttachmentProps.ts
  39. 6
      definition/IMessage/MessageAttachment/MessageAttachment.ts
  40. 24
      definition/IMessage/MessageAttachment/MessageAttachmentAction.ts
  41. 6
      definition/IMessage/MessageAttachment/MessageAttachmentBase.ts
  42. 28
      definition/IMessage/MessageAttachment/MessageAttachmentDefault.ts
  43. 15
      definition/IMessage/MessageAttachment/MessageQuoteAttachment.ts
  44. 1
      definition/IMessage/index.ts
  45. 15
      ee/app/livechat-enterprise/server/business-hour/Custom.ts
  46. 4
      ee/server/broker.ts
  47. 6
      ee/server/services/package-lock.json
  48. 24
      ee/server/services/package.json
  49. 6872
      package-lock.json
  50. 10
      package.json
  51. 8
      server/features/EmailInbox/EmailInbox_Outgoing.ts
  52. 10
      server/services/meteor/service.ts

@ -118,7 +118,8 @@
],
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": ["error", {
"argsIgnorePattern": "^_"
"argsIgnorePattern": "^_",
"ignoreRestSiblings": true
}]
},
"env": {

@ -3,6 +3,9 @@ const { resolve, relative, join } = require('path');
const webpack = require('webpack');
module.exports = {
typescript: {
reactDocgen: 'none',
},
stories: [
'../app/**/*.stories.{js,tsx}',
'../client/**/*.stories.{js,tsx}',

@ -2,8 +2,8 @@ import fileType from 'file-type';
import { mime as MIME } from '../../../utils/lib/mimeTypes';
export function determineFileType(buffer, details) {
const mime = MIME.lookup(details.name);
export function determineFileType(buffer, name) {
const mime = MIME.lookup(name);
if (mime) {
return Array.isArray(mime) ? mime[0] : mime;

@ -7,6 +7,14 @@ import { FileUpload } from '../../../file-upload/server';
import { determineFileType } from '../../lib/misc/determineFileType';
import { AppServerOrchestrator } from '../orchestrator';
const getUploadDetails = (details: IUploadDetails): Partial<IUploadDetails> => {
if (details.visitorToken) {
const { userId, ...result } = details;
return result;
}
return details;
};
export class AppUploadBridge extends UploadBridge {
// eslint-disable-next-line no-empty-function
constructor(private readonly orch: AppServerOrchestrator) {
@ -42,20 +50,13 @@ export class AppUploadBridge extends UploadBridge {
throw new Error('Missing user to perform the upload operation');
}
if (details.visitorToken) {
delete details.userId;
}
const fileStore = FileUpload.getStore('Uploads');
const insertSync = details.userId
? (...args: any[]): Function => Meteor.runAsUser(details.userId, () => fileStore.insertSync(...args))
: Meteor.wrapAsync(fileStore.insert.bind(fileStore));
details.type = determineFileType(buffer, details);
details.type = determineFileType(buffer, details.name);
return new Promise(Meteor.bindEnvironment((resolve, reject) => {
try {
const uploadedFile = insertSync(details, buffer);
const uploadedFile = fileStore.insertSync(getUploadDetails(details), buffer);
if (details.visitorToken) {
Meteor.call('sendFileLivechatMessage', details.rid, details.visitorToken, uploadedFile);

@ -15,7 +15,7 @@ import { getRoomByNameOrIdWithOptionToJoin, processWebhookMessage } from '../../
import { logger } from '../logger';
import { integrations } from '../../lib/rocketchat';
integrations.triggerHandler = new class RocketChatIntegrationHandler {
export class RocketChatIntegrationHandler {
constructor() {
this.vm = vm;
this.successResults = [200, 201, 202];
@ -805,6 +805,6 @@ integrations.triggerHandler = new class RocketChatIntegrationHandler {
this.executeTriggerUrl(history.url, integration, { event, message, room, owner, user });
}
}();
export { integrations };
}
const triggerHandler = new RocketChatIntegrationHandler();
export { integrations, triggerHandler };

@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor';
import { hasPermission } from '../../../../authorization';
import { Integrations, IntegrationHistory } from '../../../../models';
import { integrations } from '../../../lib/rocketchat';
import { triggerHandler } from '../../lib/triggerHandler';
Meteor.methods({
replayOutgoingIntegration({ integrationId, historyId }) {
@ -26,7 +26,7 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-integration-history', 'Invalid Integration History', { method: 'replayOutgoingIntegration' });
}
integrations.triggerHandler.replay(integration, history);
triggerHandler.replay(integration, history);
return true;
},

@ -1,9 +1,9 @@
import { callbacks } from '../../callbacks';
import { integrations } from '../lib/rocketchat';
import { triggerHandler } from './lib/triggerHandler';
const callbackHandler = function _callbackHandler(eventType) {
return function _wrapperFunction(...args) {
return integrations.triggerHandler.executeTriggers(eventType, ...args);
return triggerHandler.executeTriggers(eventType, ...args);
};
};

@ -3,7 +3,7 @@ import moment from 'moment';
import { AbstractBusinessHourType, IBusinessHourType } from './AbstractBusinessHour';
import { ILivechatBusinessHour, LivechatBusinessHourTypes } from '../../../../definition/ILivechatBusinessHour';
interface IExtraProperties extends ILivechatBusinessHour {
interface IExtraProperties {
timezoneName?: string;
}
@ -14,15 +14,16 @@ export class DefaultBusinessHour extends AbstractBusinessHourType implements IBu
return this.BusinessHourRepository.findOneDefaultBusinessHour();
}
async saveBusinessHour(businessHourData: IExtraProperties): Promise<ILivechatBusinessHour> {
async saveBusinessHour(businessHour: ILivechatBusinessHour & IExtraProperties): Promise<ILivechatBusinessHour> {
const { timezoneName, ...businessHourData } = businessHour;
if (!businessHourData._id) {
return businessHourData;
}
businessHourData.timezone = {
name: businessHourData.timezoneName || moment.tz.guess(),
utc: this.getUTCFromTimezone(businessHourData.timezoneName),
name: timezoneName || moment.tz.guess(),
utc: this.getUTCFromTimezone(timezoneName),
};
delete businessHourData.timezoneName;
await this.baseSaveBusinessHour(businessHourData);
return businessHourData;
}

@ -1,28 +1,10 @@
import { Box, Button, ButtonGroup } from '@rocket.chat/fuselage';
import React, { FC } from 'react';
import { AttachmentProps } from '.';
// DEPRECATED
import { MessageAttachmentAction } from '../../../../definition/IMessage/MessageAttachment/MessageAttachmentAction';
type Action = {
msgId?: string;
type: 'button';
text: string;
msg?: string;
url?: string;
image_url?: string;
is_webview?: true;
msg_in_chat_window?: true;
msg_processing_type?: 'sendMessage' | 'respondWithMessage' | 'respondWithQuotedMessage';
};
export type ActionAttachmentProps = {
button_alignment: 'horizontal' | 'vertical';
actions: Array<Action>;
} & AttachmentProps;
export const ActionAttachment: FC<ActionAttachmentProps> = ({ actions }) => (
<ButtonGroup mb='x4' {...{ small: true }}>
export const ActionAttachment: FC<MessageAttachmentAction> = ({ actions }) => (
<ButtonGroup mb='x4' {...({ small: true } as any)}>
{actions
.filter(
({ type, msg_in_chat_window: msgInChatWindow, url, image_url: image, text }) =>

@ -3,7 +3,7 @@ import React, { FC } from 'react';
import Attachment from './Attachment';
const Block: FC<{ pre?: JSX.Element | string; color?: string }> = ({
const Block: FC<{ pre?: JSX.Element | string | undefined; color?: string | undefined }> = ({
pre,
color = 'neutral-600',
children,

@ -3,11 +3,9 @@ import React, { ComponentProps, FC } from 'react';
import { useTranslation } from '../../../../contexts/TranslationContext';
import Action from './Action';
const Download: FC<Omit<ComponentProps<typeof Action>, 'icon'> & { href: string }> = ({
title,
href,
...props
}) => {
const Download: FC<
Omit<ComponentProps<typeof Action>, 'icon'> & { title?: string | undefined; href: string }
> = ({ title, href, ...props }) => {
const t = useTranslation();
return (
<Action

@ -2,7 +2,7 @@ import React, { FC } from 'react';
import Title from './Title';
const TitleLink: FC<{ link: string; title?: string }> = ({ link, title }) => (
const TitleLink: FC<{ link: string; title?: string | undefined }> = ({ link, title }) => (
<Title
is='a'
href={`${link}?download`}

@ -1,10 +0,0 @@
import { ComponentProps } from 'react';
import DefaultAttachment from './DefaultAttachment';
import { FileAttachmentProps } from './Files';
import { QuoteAttachmentProps } from './QuoteAttachment';
export type AttachmentProps =
| ComponentProps<typeof DefaultAttachment>
| FileAttachmentProps
| QuoteAttachmentProps;

@ -1,13 +1,19 @@
import React from 'react';
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable @typescript-eslint/camelcase */
import React, { ReactElement } from 'react';
import Attachments from '.';
import { FileAttachmentProps } from '../../../../definition/IMessage/MessageAttachment/Files/FileAttachmentProps';
import { FileProp } from '../../../../definition/IMessage/MessageAttachment/Files/FileProp';
import { MessageAttachmentDefault } from '../../../../definition/IMessage/MessageAttachment/MessageAttachmentDefault';
export default {
title: 'Message/Attachments',
component: Attachments,
decorators: [(storyFn: any): ReactElement => storyFn()],
};
const field = {
const field: MessageAttachmentDefault = {
color: '#ff0000',
text: 'Yay for gruggy!',
pretext: 'Pre Text',
@ -43,33 +49,38 @@ const field = {
],
};
const image = {
ts: '2016-12-09T16:53:06.761Z',
collapsed: false,
const image: FileAttachmentProps = {
ts: new Date('2016-12-09T16:53:06.761Z'),
// collapsed: false,
title: 'Attachment Image Example',
title_link: 'https://youtube.com',
title_link_download: true,
image_url: 'https://rocket.chat/wp-content/uploads/2020/07/devices-screens-768x433.png.webp',
type: 'file',
image_type: 'png',
};
const video = {
ts: '2016-12-09T16:53:06.761Z',
const video: FileAttachmentProps = {
ts: new Date('2016-12-09T16:53:06.761Z'),
collapsed: false,
title: 'Attachment Video Example',
title_link: 'https://youtube.com',
title_link_download: true,
video_url: 'http://www.w3schools.com/tags/movie.mp4',
video_size: 10000,
video_type: 'mp4',
type: 'file',
};
const audio = {
ts: '2016-12-09T16:53:06.761Z',
const audio: FileAttachmentProps = {
ts: new Date('2016-12-09T16:53:06.761Z'),
collapsed: false,
title: 'Attachment Audio Example',
title_link: 'https://youtube.com',
title_link_download: true,
audio_url: 'http://www.w3schools.com/tags/horse.mp3',
audio_type: 'mp3',
audio_size: 10000,
type: 'file',
};
@ -80,21 +91,21 @@ const message = {
alias: 'Gruggy',
emoji: ':smirk:',
avatar: 'https://avatars2.githubusercontent.com/u/5263975?s=60&v=3',
attachments: [field, image, video, audio],
attachments: [field, image],
};
window.__meteor_runtime_config__ = { ROOT_URL_PATH_PREFIX: '' };
// window.__meteor_runtime_config__ = { ROOT_URL_PATH_PREFIX: '' };
export const Default = () => <Attachments attachments={message.attachments} />;
export const Fields = () => <Attachments attachments={[field]} />;
export const FailingImage = () => (
<Attachments attachments={[{ ...image, image_url: 'invalid.url' }]} />
<Attachments attachments={[{ ...image, image_url: 'invalid.url' } as FileAttachmentProps]} />
);
export const Image = () => <Attachments attachments={[image]} />;
export const Video = () => <Attachments attachments={[video]} />;
export const Video = () => <Attachments attachments={[video]} file={{} as FileProp} />;
export const Audio = () => <Attachments attachments={[audio]} />;
export const Audio = () => <Attachments attachments={[audio]} file={{} as FileProp} />;

@ -1,11 +1,11 @@
import React, { FC } from 'react';
import { FileProp } from '../../../../definition/IMessage/MessageAttachment/Files/FileProp';
import { MessageAttachmentBase } from '../../../../definition/IMessage/MessageAttachment/MessageAttachmentBase';
import { useBlockRendered } from '../hooks/useBlockRendered';
import { AttachmentProps } from './AttachmentProps';
import { FileProp } from './FileProp';
import Item from './Item';
const Attachments: FC<{ attachments: Array<AttachmentProps>; file?: FileProp }> = ({
const Attachments: FC<{ attachments: Array<MessageAttachmentBase>; file?: FileProp }> = ({
attachments = null,
file,
}): any => {

@ -1,56 +1,23 @@
import React, { ComponentProps, FC, ReactNode } from 'react';
import React, { FC, ReactNode } from 'react';
import { AttachmentProps } from '.';
import { isActionAttachment } from '../../../../definition/IMessage/MessageAttachment/MessageAttachmentAction';
import {
MarkdownFields,
MessageAttachmentDefault,
} from '../../../../definition/IMessage/MessageAttachment/MessageAttachmentDefault';
import MarkdownText from '../../MarkdownText';
import { ActionAttachment, ActionAttachmentProps } from './ActionAttachtment';
import { ActionAttachment } from './ActionAttachtment';
import Attachment from './Attachment';
import FieldsAttachment from './FieldsAttachment';
import { Dimensions } from './components/Dimensions';
import { useCollapse } from './hooks/useCollapse';
type MarkdownFields = 'text' | 'pretext' | 'fields';
type DefaultAttachmentProps = {
collapsed?: true;
author_icon?: string;
author_link?: string;
author_name?: string;
// TODO: replace this component props type with a payload-based type because
// `value` comes as `string` and is passed as `ReactNode`
fields: ComponentProps<typeof FieldsAttachment>['fields'];
// footer
// footer_icon
image_url?: string;
image_dimensions?: Dimensions;
mrkdwn_in?: Array<MarkdownFields>;
pretext?: string;
text?: string;
thumb_url?: string;
title?: string;
title_link?: string;
ts?: Date;
color?: string;
};
const isActionAttachment = (attachment: AttachmentProps): attachment is ActionAttachmentProps =>
'actions' in attachment;
const applyMarkdownIfRequires = (
list: DefaultAttachmentProps['mrkdwn_in'] = ['text', 'pretext'],
list: MessageAttachmentDefault['mrkdwn_in'] = ['text', 'pretext'],
key: MarkdownFields,
text: string,
): ReactNode => (list?.includes(key) ? <MarkdownText variant='inline' content={text} /> : text);
const DefaultAttachment: FC<DefaultAttachmentProps> = (attachment) => {
const DefaultAttachment: FC<MessageAttachmentDefault> = (attachment) => {
const [collapsed, collapse] = useCollapse(!!attachment.collapsed);
return (

@ -14,7 +14,7 @@ const FieldsAttachment: FC<{ fields: FieldProp[] }> = ({ fields }): any => (
<Box flexWrap='wrap' display='flex' mb='x4' mi='neg-x4'>
{fields.map((field, index) =>
field.short ? <ShortField {...field} key={index} /> : <Field {...field} key={index} />,
)}{' '}
)}
</Box>
);

@ -1,19 +1,11 @@
import React, { FC } from 'react';
import { FileProp } from '..';
import { AudioAttachmentProps } from '../../../../../definition/IMessage/MessageAttachment/Files/AudioAttachmentProps';
import MarkdownText from '../../../MarkdownText';
import Attachment from '../Attachment';
import { AttachmentPropsBase } from '../Attachment/AttachmentPropsBase';
import { useMediaUrl } from '../context/AttachmentContext';
import { useCollapse } from '../hooks/useCollapse';
export type AudioAttachmentProps = {
audio_url: string;
audio_type: string;
audio_size?: number;
file?: FileProp;
} & AttachmentPropsBase;
export const AudioAttachment: FC<AudioAttachmentProps> = ({
title,
audio_url: url,

@ -1,14 +1,14 @@
import React, { FC } from 'react';
import { FileProp } from '..';
import { FileProp } from '../../../../../definition/IMessage/MessageAttachment/Files/FileProp';
import { MessageAttachmentBase } from '../../../../../definition/IMessage/MessageAttachment/MessageAttachmentBase';
import MarkdownText from '../../../MarkdownText';
import Attachment from '../Attachment';
import { AttachmentPropsBase } from '../Attachment/AttachmentPropsBase';
import { useMediaUrl } from '../context/AttachmentContext';
export type GenericFileAttachmentProps = {
file?: FileProp;
} & AttachmentPropsBase;
} & MessageAttachmentBase;
export const GenericFileAttachment: FC<GenericFileAttachmentProps> = ({
title,

@ -1,24 +1,13 @@
import React, { FC } from 'react';
import { FileProp } from '..';
import { ImageAttachmentProps } from '../../../../../definition/IMessage/MessageAttachment/Files/ImageAttachmentProps';
import MarkdownText from '../../../MarkdownText';
import Attachment from '../Attachment';
import { AttachmentPropsBase } from '../Attachment/AttachmentPropsBase';
import { Dimensions } from '../components/Dimensions';
import Image from '../components/Image';
import { useMediaUrl } from '../context/AttachmentContext';
import { useCollapse } from '../hooks/useCollapse';
import { useLoadImage } from '../hooks/useLoadImage';
export type ImageAttachmentProps = {
image_dimensions: Dimensions;
image_preview?: string;
image_url: string;
image_type: string;
image_size?: number;
file?: FileProp;
} & AttachmentPropsBase;
export const ImageAttachment: FC<ImageAttachmentProps> = ({
title,
image_url: url,
@ -38,7 +27,7 @@ export const ImageAttachment: FC<ImageAttachmentProps> = ({
const getURL = useMediaUrl();
return (
<Attachment>
<MarkdownText variant='inline' content={description} />
{description && <MarkdownText variant='inline' content={description} />}
<Attachment.Row>
<Attachment.Title>{title}</Attachment.Title>
{size && <Attachment.Size size={size} />}

@ -1,16 +1,11 @@
import React, { FC } from 'react';
import { FileProp } from '..';
import { PDFAttachmentProps } from '../../../../../definition/IMessage/MessageAttachment/Files/PDFAttachmentProps';
import { useTranslation } from '../../../../contexts/TranslationContext';
import MarkdownText from '../../../MarkdownText';
import Attachment from '../Attachment';
import { AttachmentPropsBase } from '../Attachment/AttachmentPropsBase';
import { useCollapse } from '../hooks/useCollapse';
export type PDFAttachmentProps = {
file: FileProp;
} & AttachmentPropsBase;
export const PDFAttachment: FC<PDFAttachmentProps> = ({
collapsed: collapsedDefault = false,
description,
@ -22,7 +17,7 @@ export const PDFAttachment: FC<PDFAttachmentProps> = ({
const [collapsed, collapse] = useCollapse(collapsedDefault);
return (
<Attachment>
<MarkdownText variant='inline' content={description} />
{description && <MarkdownText variant='inline' content={description} />}
<Attachment.Row>
<Attachment.Title>{t('PDF')}</Attachment.Title>
{collapse}

@ -1,19 +1,11 @@
import { Box } from '@rocket.chat/fuselage';
import React, { FC } from 'react';
import { FileProp } from '..';
import { VideoAttachmentProps } from '../../../../../definition/IMessage/MessageAttachment/Files/VideoAttachmentProps';
import Attachment from '../Attachment';
import { AttachmentPropsBase } from '../Attachment/AttachmentPropsBase';
import { useMediaUrl } from '../context/AttachmentContext';
import { useCollapse } from '../hooks/useCollapse';
export type VideoAttachmentProps = {
video_url: string;
video_type: string;
video_size: number;
file?: FileProp;
} & AttachmentPropsBase;
export const VideoAttachment: FC<VideoAttachmentProps> = ({
title,
video_url: url,

@ -1,27 +1,13 @@
import React, { FC } from 'react';
import { AttachmentProps, FileProp } from '..';
import { AudioAttachment, AudioAttachmentProps } from './AudioAttachment';
import { isFileAudioAttachment } from '../../../../../definition/IMessage/MessageAttachment/Files/AudioAttachmentProps';
import { FileAttachmentProps } from '../../../../../definition/IMessage/MessageAttachment/Files/FileAttachmentProps';
import { isFileImageAttachment } from '../../../../../definition/IMessage/MessageAttachment/Files/ImageAttachmentProps';
import { isFileVideoAttachment } from '../../../../../definition/IMessage/MessageAttachment/Files/VideoAttachmentProps';
import { AudioAttachment } from './AudioAttachment';
import { GenericFileAttachment } from './GenericFileAttachment';
import { ImageAttachment, ImageAttachmentProps } from './ImageAttachment';
// import { PDFAttachment } from './PDFAttachment';
import { VideoAttachment, VideoAttachmentProps } from './VideoAttachment';
export type FileAttachmentProps = {
type: 'file';
file?: FileProp;
} & (VideoAttachmentProps | ImageAttachmentProps | AudioAttachmentProps);
const isFileImageAttachment = (
attachment: FileAttachmentProps,
): attachment is ImageAttachmentProps & { type: 'file' } => 'image_url' in attachment;
const isFileAudioAttachment = (
attachment: FileAttachmentProps,
): attachment is AudioAttachmentProps & { type: 'file' } => 'audio_url' in attachment;
const isFileVideoAttachment = (
attachment: FileAttachmentProps,
): attachment is VideoAttachmentProps & { type: 'file' } => 'video_url' in attachment;
// const isFilePDFAttachment = (attachment: FileAttachmentProps): attachment is VideoAttachmentProps & { type: 'file' } => attachment?.file?.type.endsWith('pdf');
import { ImageAttachment } from './ImageAttachment';
import { VideoAttachment } from './VideoAttachment';
export const FileAttachment: FC<FileAttachmentProps> = (attachment) => {
if (isFileImageAttachment(attachment)) {
@ -38,5 +24,4 @@ export const FileAttachment: FC<FileAttachmentProps> = (attachment) => {
return <GenericFileAttachment {...attachment} />;
};
export const isFileAttachment = (attachment: AttachmentProps): attachment is FileAttachmentProps =>
'type' in attachment && attachment.type === 'file';
export { GenericFileAttachment, ImageAttachment, VideoAttachment };

@ -1,16 +1,18 @@
import React, { FC, memo } from 'react';
import { AttachmentProps } from './AttachmentProps';
import { isFileAttachment } from '../../../../definition/IMessage/MessageAttachment/Files/FileAttachmentProps';
import { FileProp } from '../../../../definition/IMessage/MessageAttachment/Files/FileProp';
import { MessageAttachmentBase } from '../../../../definition/IMessage/MessageAttachment/MessageAttachmentBase';
import { isQuoteAttachment } from '../../../../definition/IMessage/MessageAttachment/MessageQuoteAttachment';
import DefaultAttachment from './DefaultAttachment';
import { FileProp } from './FileProp';
import { isFileAttachment, FileAttachment } from './Files';
import { QuoteAttachment, QuoteAttachmentProps } from './QuoteAttachment';
import { FileAttachment } from './Files';
import { QuoteAttachment } from './QuoteAttachment';
const isQuoteAttachment = (attachment: AttachmentProps): attachment is QuoteAttachmentProps =>
'message_link' in attachment && attachment.message_link !== null;
const Item: FC<{ attachment: AttachmentProps; file?: FileProp }> = ({ attachment, file }) => {
if (isFileAttachment(attachment)) {
const Item: FC<{ attachment: MessageAttachmentBase; file?: FileProp | undefined }> = ({
attachment,
file,
}) => {
if (isFileAttachment(attachment) && file) {
return <FileAttachment {...attachment} file={file} />;
}

@ -4,19 +4,10 @@ import colors from '@rocket.chat/fuselage-tokens/colors';
import React, { FC } from 'react';
import Attachments from '.';
import { MessageQuoteAttachment } from '../../../../definition/IMessage/MessageAttachment/MessageQuoteAttachment';
import { useTimeAgo } from '../../../hooks/useTimeAgo';
import MarkdownText from '../../MarkdownText';
import Attachment from './Attachment';
import { AttachmentPropsBase } from './Attachment/AttachmentPropsBase';
export type QuoteAttachmentProps = {
author_name: string;
author_link: string;
author_icon: string;
message_link?: string;
text: string;
attachments?: Array<QuoteAttachmentProps>;
} & AttachmentPropsBase;
const hover = css`
&:hover,
@ -29,7 +20,7 @@ const hover = css`
}
`;
export const QuoteAttachment: FC<QuoteAttachmentProps> = ({
export const QuoteAttachment: FC<MessageQuoteAttachment> = ({
author_icon: url,
author_name: name,
author_link: authorLink,

@ -1,7 +1,7 @@
import React, { memo, FC, useState, useMemo } from 'react';
import { Dimensions } from '../../../../../definition/IMessage/MessageAttachment/Files/Dimensions';
import { useAttachmentDimensions } from '../context/AttachmentContext';
import { Dimensions } from './Dimensions';
import ImageBox from './ImageBox';
import Load from './Load';
import Retry from './Retry';

@ -1,4 +1 @@
export { default } from './Attachments';
export { AttachmentProps } from './AttachmentProps';
export { FileProp } from './FileProp';

@ -1,4 +1,4 @@
import { IMessage } from '../../../../../../definition/IMessage';
import { IMessage } from '../../../../../../definition/IMessage/IMessage';
import { IRoom } from '../../../../../../definition/IRoom';
import { ObjectFromApi } from '../../../../../../definition/ObjectFromApi';

@ -1,37 +0,0 @@
import { IRocketChatRecord } from './IRocketChatRecord';
import { IUser } from './IUser';
import { ChannelName, RoomID } from './IRoom';
type MentionType = 'user' | 'team';
export interface IMessage extends IRocketChatRecord {
rid: RoomID;
msg: string;
tmid?: string;
ts: Date;
mentions?: {
_id: string;
type: MentionType;
name?: string;
username?: string;
}[];
channels?: Array<ChannelName>;
u: Pick<IUser, '_id' | 'username' | 'name'>;
_hidden?: boolean;
imported?: boolean;
replies?: IUser['_id'][];
location?: {
type: 'Point';
coordinates: [string, string];
};
starred?: {_id: string}[];
pinned?: boolean;
drid?: RoomID;
tlm?: Date;
dcount?: number;
tcount?: number;
t?: string;
e2e?: 'pending';
}

@ -0,0 +1,72 @@
import { MessageSurfaceLayout } from '@rocket.chat/ui-kit';
import { parser } from '@rocket.chat/message-parser';
import { IRocketChatRecord } from '../IRocketChatRecord';
import { IUser } from '../IUser';
import { ChannelName, RoomID } from '../IRoom';
import { MessageAttachment } from './MessageAttachment/MessageAttachment';
type MentionType = 'user' | 'team';
type MessageTypesValues =
| 'e2e'
| 'uj'
| 'ul'
| 'ru'
| 'au'
| 'mute_unmute'
| 'r'
| 'ut'
| 'wm'
| 'rm'
| 'subscription-role-added'
| 'subscription-role-removed'
| 'room_archived'
| 'room_unarchived'
| 'room_changed_privacy'
| 'room_changed_avatar'
| 'room_changed_topic'
| 'room_e2e_enabled'
| 'room_e2e_disabled'
| 'livechat-close'
export interface IMessage extends IRocketChatRecord {
rid: RoomID;
msg: string;
tmid?: string;
ts: Date;
mentions?: {
_id: string;
type: MentionType;
name?: string;
username?: string;
}[];
groupable?: false;
channels?: Array<ChannelName>;
u: Pick<IUser, '_id' | 'username' | 'name'>;
blocks?: MessageSurfaceLayout;
md?: ReturnType<typeof parser>;
_hidden?: boolean;
imported?: boolean;
replies?: IUser['_id'][];
location?: {
type: 'Point';
coordinates: [string, string];
};
starred?: {_id: IUser['_id']}[];
pinned?: boolean;
drid?: RoomID;
tlm?: Date;
dcount?: number;
tcount?: number;
t?: MessageTypesValues;
e2e?: 'pending';
urls: any;
file: any;
attachments: MessageAttachment[];
}

@ -0,0 +1,5 @@
export type FieldProps = {
short?: boolean;
title: string;
value: string;
};

@ -0,0 +1,15 @@
import { MessageAttachmentBase } from '../MessageAttachmentBase';
import { FileAttachmentProps } from './FileAttachmentProps';
import { FileProp } from './FileProp';
export type AudioAttachmentProps = {
audio_url: string;
audio_type: string;
audio_size?: number;
file?: FileProp;
} & MessageAttachmentBase;
export const isFileAudioAttachment = (
attachment: FileAttachmentProps,
): attachment is AudioAttachmentProps & { type: 'file' } => 'audio_url' in attachment;

@ -0,0 +1,14 @@
import { MessageAttachmentBase } from '../MessageAttachmentBase';
import { AudioAttachmentProps } from './AudioAttachmentProps';
import { FileProp } from './FileProp';
import { ImageAttachmentProps } from './ImageAttachmentProps';
import { VideoAttachmentProps } from './VideoAttachmentProps';
export type FileAttachmentProps = {
type: 'file';
file?: FileProp;
} & (VideoAttachmentProps | ImageAttachmentProps | AudioAttachmentProps);
export const isFileAttachment = (attachment: MessageAttachmentBase): attachment is FileAttachmentProps =>
'type' in attachment && (attachment as any).type === 'file';

@ -0,0 +1,18 @@
import { MessageAttachmentBase } from '../MessageAttachmentBase';
import { Dimensions } from './Dimensions';
import { FileAttachmentProps } from './FileAttachmentProps';
import { FileProp } from './FileProp';
export type ImageAttachmentProps = {
image_dimensions?: Dimensions;
image_preview?: string;
image_url: string;
image_type: string;
image_size?: number;
file?: FileProp;
} & MessageAttachmentBase;
export const isFileImageAttachment = (
attachment: FileAttachmentProps,
): attachment is ImageAttachmentProps & { type: 'file' } => 'image_url' in attachment;

@ -0,0 +1,7 @@
import { MessageAttachmentBase } from '../MessageAttachmentBase';
import { FileProp } from './FileProp';
export type PDFAttachmentProps = {
file: FileProp;
} & MessageAttachmentBase;

@ -0,0 +1,15 @@
import { MessageAttachmentBase } from '../MessageAttachmentBase';
import { FileAttachmentProps } from './FileAttachmentProps';
import { FileProp } from './FileProp';
export type VideoAttachmentProps = {
video_url: string;
video_type: string;
video_size: number;
file?: FileProp;
} & MessageAttachmentBase;
export const isFileVideoAttachment = (
attachment: FileAttachmentProps,
): attachment is VideoAttachmentProps & { type: 'file' } => 'video_url' in attachment;

@ -0,0 +1,6 @@
import { MessageAttachmentDefault } from './MessageAttachmentDefault';
import { FileAttachmentProps } from './Files/FileAttachmentProps';
import { MessageQuoteAttachment } from './MessageQuoteAttachment';
import { MessageAttachmentAction } from './MessageAttachmentAction';
export type MessageAttachment = MessageAttachmentAction | MessageAttachmentDefault | FileAttachmentProps | MessageQuoteAttachment;

@ -0,0 +1,24 @@
// DEPRECATED
import { MessageAttachmentBase } from './MessageAttachmentBase';
type Action = {
msgId?: string;
type: 'button';
text: string;
msg?: string;
url?: string;
image_url?: string;
is_webview?: true;
msg_in_chat_window?: true;
msg_processing_type?: 'sendMessage' | 'respondWithMessage' | 'respondWithQuotedMessage';
};
export type MessageAttachmentAction = {
button_alignment?: 'horizontal' | 'vertical';
actions: Array<Action>;
} & MessageAttachmentBase;
export const isActionAttachment = (
attachment: MessageAttachmentBase,
): attachment is MessageAttachmentAction => 'actions' in attachment;

@ -1,10 +1,10 @@
export type AttachmentPropsBase = {
export type MessageAttachmentBase = {
title?: string;
ts: Date;
ts?: Date;
collapsed?: boolean;
description?: string;
title_link?: string;
title_link_download: boolean;
title_link_download?: boolean;
};

@ -0,0 +1,28 @@
import { FieldProps } from './FieldProps';
import { Dimensions } from './Files/Dimensions';
import { MessageAttachmentBase } from './MessageAttachmentBase';
export type MarkdownFields = 'text' | 'pretext' | 'fields';
export type MessageAttachmentDefault = {
author_icon?: string;
author_link?: string;
author_name?: string;
fields?: FieldProps[];
// footer
// footer_icon
image_url?: string;
image_dimensions?: Dimensions;
mrkdwn_in?: Array<MarkdownFields>;
pretext?: string;
text?: string;
thumb_url?: string;
color?: string;
} & MessageAttachmentBase;

@ -0,0 +1,15 @@
import { MessageAttachmentBase } from './MessageAttachmentBase';
export type MessageQuoteAttachment = {
author_name: string;
author_link: string;
author_icon: string;
message_link?: string;
text: string;
attachments?: Array<MessageQuoteAttachment>;
} & MessageAttachmentBase;
export const isQuoteAttachment = (
attachment: MessageAttachmentBase,
): attachment is MessageQuoteAttachment =>
'message_link' in attachment;

@ -0,0 +1 @@
export * from './IMessage';

@ -8,7 +8,7 @@ import { LivechatDepartmentAgentsRaw } from '../../../../../app/models/server/ra
import { LivechatDepartment, LivechatDepartmentAgents } from '../../../../../app/models/server/raw';
import { businessHourManager } from '../../../../../app/livechat/server/business-hour';
export interface IBusinessHoursExtraProperties extends ILivechatBusinessHour {
type IBusinessHoursExtraProperties = {
timezoneName: string;
departmentsToApplyBusinessHour: string;
}
@ -34,17 +34,16 @@ class CustomBusinessHour extends AbstractBusinessHourType implements IBusinessHo
return businessHour;
}
async saveBusinessHour(businessHourData: IBusinessHoursExtraProperties): Promise<ILivechatBusinessHour> {
async saveBusinessHour(businessHour: ILivechatBusinessHour & IBusinessHoursExtraProperties): Promise<ILivechatBusinessHour> {
const { timezoneName, departmentsToApplyBusinessHour, ...businessHourData } = businessHour;
businessHourData.timezone = {
name: businessHourData.timezoneName,
utc: this.getUTCFromTimezone(businessHourData.timezoneName),
name: timezoneName,
utc: this.getUTCFromTimezone(timezoneName),
};
const departments = businessHourData.departmentsToApplyBusinessHour?.split(',').filter(Boolean);
const departments = departmentsToApplyBusinessHour?.split(',').filter(Boolean) || [];
const businessHourToReturn = { ...businessHourData };
delete businessHourData.timezoneName;
delete businessHourData.departmentsToApplyBusinessHour;
delete businessHourData.departments;
const businessHourId = await this.baseSaveBusinessHour(businessHourData);
const businessHourId = await this.baseSaveBusinessHour(businessHour);
const currentDepartments = (await this.DepartmentsRepository.findByBusinessHourId(businessHourId, { fields: { _id: 1 } }).toArray()).map((dept: any) => dept._id);
const toRemove = [...currentDepartments.filter((dept: string) => !departments.includes(dept))];
const toAdd = [...departments.filter((dept: string) => !currentDepartments.includes(dept))];

@ -150,9 +150,7 @@ class NetworkBroker implements IBroker {
}
map[eventName] = (data: Parameters<EventSignatures[typeof eventName]>): any => {
if (this.allowed) {
return instance.emit(eventName, ...data);
}
this.allowed.then((allowed) => allowed && instance.emit(eventName, ...data));
};
return map;

@ -2820,9 +2820,9 @@
}
},
"typescript": {
"version": "3.9.7",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
"integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==",
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.2.tgz",
"integrity": "sha512-tbb+NVrLfnsJy3M59lsDgrzWIflR4d4TIUjz+heUnHZwdF7YsrMTKoRERiIvI2lvBG95dfpLxB21WZhys1bgaQ==",
"dev": true
},
"underscore.string": {

@ -19,32 +19,32 @@
"license": "MIT",
"dependencies": {
"bcrypt": "^5.0.0",
"body-parser": "^1.19.0",
"cookie": "^0.4.1",
"cookie-parser": "^1.4.5",
"ejson": "^2.2.0",
"eventemitter3": "^4.0.7",
"express": "^4.17.1",
"jaeger-client": "^3.18.1",
"mem": "^6.1.0",
"moleculer": "^0.14.10",
"mongodb": "^3.6.1",
"nats": "^1.4.8",
"sodium-native": "^3.2.1",
"sodium-plus": "^0.9.0",
"underscore.string": "^3.3.5",
"uuid": "^7.0.3",
"ws": "^7.2.3",
"eventemitter3": "^4.0.7",
"body-parser": "^1.19.0",
"cookie": "^0.4.1",
"cookie-parser": "^1.4.5",
"express": "^4.17.1",
"sodium-native": "^3.2.1",
"sodium-plus": "^0.9.0"
"ws": "^7.2.3"
},
"devDependencies": {
"@types/cookie": "^0.4.0",
"@types/cookie-parser": "^1.4.2",
"@types/ejson": "^2.1.2",
"@types/express": "^4.17.11",
"@types/node": "^14.6.4",
"@types/ws": "^7.2.6",
"pm2": "^4.4.1",
"ts-node": "^9.0.0",
"typescript": "^3.9.7",
"@types/cookie": "^0.4.0",
"@types/cookie-parser": "^1.4.2",
"@types/express": "^4.17.11"
"typescript": "^4.2.2"
}
}

6872
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -94,7 +94,7 @@
"@types/xml-crypto": "^1.4.1",
"@types/xmldom": "^0.1.30",
"@typescript-eslint/eslint-plugin": "^2.34.0",
"@typescript-eslint/parser": "^2.34.0",
"@typescript-eslint/parser": "^3.0.0",
"acorn": "^6.4.1",
"autoprefixer": "^9.8.6",
"babel-eslint": "^10.1.0",
@ -140,7 +140,7 @@
"supertest": "^3.4.2",
"ts-loader": "^7.0.5",
"ts-node": "^8.10.2",
"typescript": "^3.9.7",
"typescript": "^4.2.2",
"webpack": "^4.44.1"
},
"dependencies": {
@ -159,12 +159,12 @@
"@rocket.chat/fuselage-hooks": "^0.6.3-dev.261",
"@rocket.chat/fuselage-polyfills": "^0.6.3-dev.259",
"@rocket.chat/fuselage-tokens": "^0.6.3-dev.261",
"@rocket.chat/fuselage-ui-kit": "^0.26.0",
"@rocket.chat/fuselage-ui-kit": "^0.6.3-dev.267",
"@rocket.chat/memo": "^0.26.0",
"@rocket.chat/message-parser": "^0.26.0",
"@rocket.chat/mp3-encoder": "^0.24.0",
"@rocket.chat/string-helpers": "^0.26.0",
"@rocket.chat/ui-kit": "^0.26.0",
"@rocket.chat/string-helpers": "^0.6.3-dev.268",
"@rocket.chat/ui-kit": "^0.6.3-dev.267",
"@slack/client": "^4.12.0",
"adm-zip": "RocketChat/adm-zip",
"agenda": "^3.1.0",

@ -12,6 +12,8 @@ import { Messages, Rooms, Uploads, Users } from '../../../app/models/server';
import { Inbox, inboxes } from './EmailInbox';
import { sendMessage } from '../../../app/lib/server/functions/sendMessage';
import { settings } from '../../../app/settings/server';
import { IMessage } from '../../../definition/IMessage';
const livechatQuoteRegExp = /^\[\s\]\(https?:\/\/.+\/live\/.+\?msg=(?<id>.+?)\)\s(?<text>.+)/s;
@ -82,7 +84,7 @@ slashCommands.add('sendEmailAttachment', (command: any, params: string) => {
const file = Uploads.findOneById(message.file._id);
FileUpload.getBuffer(file, (_err?: Error, buffer?: Buffer) => {
sendEmail(inbox, {
!_err && buffer && sendEmail(inbox, {
to: room.email.replyTo,
subject: room.email.subject,
text: message.attachments[0].description || '',
@ -122,7 +124,7 @@ slashCommands.add('sendEmailAttachment', (command: any, params: string) => {
params: 'msg_id',
});
callbacks.add('beforeSaveMessage', function(message: any, room: any) {
callbacks.add('beforeSaveMessage', function(message: IMessage, room: any) {
if (!room?.email?.inbox) {
return message;
}
@ -145,7 +147,7 @@ callbacks.add('beforeSaveMessage', function(message: any, room: any) {
// Try to identify a quote in a livechat room
const match = msg.match(livechatQuoteRegExp);
if (!match) {
if (!match?.groups) {
return message;
}

@ -16,7 +16,7 @@ import { RoutingManager } from '../../../app/livechat/server/lib/RoutingManager'
import { onlineAgents, monitorAgents } from '../../../app/livechat/server/lib/stream/agentStatus';
import { IUser } from '../../../definition/IUser';
import { matrixBroadCastActions } from '../../stream/streamBroadcast';
import { integrations } from '../../../app/integrations/server/lib/triggerHandler';
import { triggerHandler } from '../../../app/integrations/server/lib/triggerHandler';
import { ListenersModule, minimongoChangeMap } from '../../modules/listeners/listeners.module';
import notifications from '../../../app/notifications/server/lib/Notifications';
import { configureEmailInboxes } from '../../features/EmailInbox/EmailInbox';
@ -219,17 +219,17 @@ export class MeteorService extends ServiceClass implements IMeteor {
switch (clientAction) {
case 'inserted':
if (data.type === 'webhook-outgoing') {
integrations.triggerHandler.addIntegration(data);
triggerHandler.addIntegration(data);
}
break;
case 'updated':
if (data.type === 'webhook-outgoing') {
integrations.triggerHandler.removeIntegration(data);
integrations.triggerHandler.addIntegration(data);
triggerHandler.removeIntegration(data);
triggerHandler.addIntegration(data);
}
break;
case 'removed':
integrations.triggerHandler.removeIntegration({ _id: id });
triggerHandler.removeIntegration({ _id: id });
break;
}
});

Loading…
Cancel
Save