Skip to content

Commit 4f4f852

Browse files
author
SeRoNet Concourse
committed
Merge remote-tracking branch 'rocket.chat/develop' into HEAD
2 parents 854c685 + f7501de commit 4f4f852

20 files changed

Lines changed: 436 additions & 206 deletions

File tree

app/api/server/lib/users.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export async function findUsersToAutocomplete({ uid, selector }) {
1010
const exceptions = selector.exceptions || [];
1111
const conditions = selector.conditions || {};
1212
const options = {
13-
fields: {
13+
projection: {
1414
name: 1,
1515
username: 1,
1616
nickname: 1,

app/apps/server/bridges/scheduler.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ function _callProcessor(processor: Function): (job: { attrs?: { data: object } }
1717
// This field is for internal use, no need to leak to app processor
1818
delete (data as any).appId;
1919

20-
processor(data);
20+
return processor(data);
2121
};
2222
}
2323

2424
/**
25-
* Provides the Apps Engine with task scheduling capabilities
25+
* Provides the Apps Engine with task scheduling capabilities.
2626
* It uses {@link agenda:github.com/agenda/agenda} as backend
2727
*/
2828
export class AppSchedulerBridge extends SchedulerBridge {
@@ -43,7 +43,7 @@ export class AppSchedulerBridge extends SchedulerBridge {
4343
}
4444

4545
/**
46-
* Entity that will be run in a job
46+
* Entity that will be run in a job.
4747
* @typedef {Object} Processor
4848
* @property {string} id The processor's identifier
4949
* @property {function} processor The function that will be run on a given schedule
@@ -127,7 +127,7 @@ export class AppSchedulerBridge extends SchedulerBridge {
127127
}
128128

129129
/**
130-
* Schedules a registered processor to run recurrently according to a given interval
130+
* Schedules a registered processor to run recurrently according to a given interval.
131131
*
132132
* @param {Object} job
133133
* @param {string} job.id The processor's id

app/livechat/server/methods/saveBusinessHour.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import { businessHourManager } from '../business-hour';
55

66
Meteor.methods({
77
'livechat:saveBusinessHour'(businessHourData: ILivechatBusinessHour) {
8-
businessHourManager.saveBusinessHour(businessHourData);
8+
try {
9+
Promise.await(businessHourManager.saveBusinessHour(businessHourData));
10+
} catch (e) {
11+
throw new Meteor.Error(e.message);
12+
}
913
},
1014
});

app/ui/client/lib/fileUpload.js

Lines changed: 25 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,12 @@
11
import { Tracker } from 'meteor/tracker';
22
import { Session } from 'meteor/session';
3-
import s from 'underscore.string';
4-
import { Handlebars } from 'meteor/ui';
53
import { Random } from 'meteor/random';
64

75
import { settings } from '../../../settings/client';
8-
import { t, fileUploadIsValidContentType, APIClient } from '../../../utils';
9-
import { modal, prependReplies } from '../../../ui-utils';
10-
11-
12-
const readAsDataURL = (file, callback) => {
13-
const reader = new FileReader();
14-
reader.onload = (e) => callback(e.target.result, file);
15-
16-
return reader.readAsDataURL(file);
17-
};
6+
import { fileUploadIsValidContentType, APIClient } from '../../../utils';
7+
import { prependReplies } from '../../../ui-utils';
8+
import { imperativeModal } from '../../../../client/lib/imperativeModal';
9+
import FileUploadModal from '../../../../client/components/modals/FileUploadModal';
1810

1911
export const uploadFileWithMessage = async (rid, tmid, { description, fileName, msg, file }) => {
2012
const data = new FormData();
@@ -84,129 +76,6 @@ export const uploadFileWithMessage = async (rid, tmid, { description, fileName,
8476
}
8577
};
8678

87-
88-
const showUploadPreview = (file, callback) => {
89-
// If greater then 10MB don't try and show a preview
90-
if (file.file.size > (10 * 1000000)) {
91-
return callback(file, null);
92-
}
93-
94-
if (file.file.type == null) {
95-
return callback(file, null);
96-
}
97-
98-
if ((file.file.type.indexOf('audio') > -1) || (file.file.type.indexOf('video') > -1) || (file.file.type.indexOf('image') > -1)) {
99-
file.type = file.file.type.split('/')[0];
100-
101-
return readAsDataURL(file.file, (content) => callback(file, content));
102-
}
103-
104-
return callback(file, null);
105-
};
106-
107-
const getAudioUploadPreview = (file, preview) => `\
108-
<div class='upload-preview'>
109-
<audio style="width: 100%;" controls="controls">
110-
<source src="${ preview }" type="${ file.file.type }">
111-
Your browser does not support the audio element.
112-
</audio>
113-
</div>
114-
<div class='upload-preview-title'>
115-
<div class="rc-input__wrapper">
116-
<input class="rc-input__element" id='file-name' style='display: inherit;' value='${ Handlebars._escape(file.name) }' placeholder='${ t('Upload_file_name') }'>
117-
</div>
118-
<div class="rc-input__wrapper">
119-
<input class="rc-input__element" id='file-description' autofocus style='display: inherit;' value='' placeholder='${ t('Upload_file_description') }'>
120-
</div>
121-
</div>`;
122-
123-
const getVideoUploadPreview = (file, preview) => `\
124-
<div class='upload-preview'>
125-
<video style="width: 100%;" controls="controls">
126-
<source src="${ preview }" type="video/webm">
127-
Your browser does not support the video element.
128-
</video>
129-
</div>
130-
<div class='upload-preview-title'>
131-
<div class="rc-input__wrapper">
132-
<input class="rc-input__element" id='file-name' style='display: inherit;' value='${ Handlebars._escape(file.name) }' placeholder='${ t('Upload_file_name') }'>
133-
</div>
134-
<div class="rc-input__wrapper">
135-
<input class="rc-input__element" id='file-description' autofocus style='display: inherit;' value='' placeholder='${ t('Upload_file_description') }'>
136-
</div>
137-
</div>`;
138-
139-
const getImageUploadPreview = (file, preview) => `\
140-
<div class='upload-preview'>
141-
<div class='upload-preview-file' style='background-image: url(${ preview })'></div>
142-
</div>
143-
<div class='upload-preview-title'>
144-
<div class="rc-input__wrapper">
145-
<input class="rc-input__element" id='file-name' style='display: inherit;' value='${ Handlebars._escape(file.name) }' placeholder='${ t('Upload_file_name') }'>
146-
</div>
147-
<div class="rc-input__wrapper">
148-
<input class="rc-input__element" id='file-description' autofocus style='display: inherit;' value='' placeholder='${ t('Upload_file_description') }'>
149-
</div>
150-
</div>`;
151-
152-
const formatBytes = (bytes, decimals) => {
153-
if (bytes === 0) {
154-
return '0 Bytes';
155-
}
156-
157-
const k = 1000;
158-
const dm = (decimals + 1) || 3;
159-
160-
const sizes = [
161-
'Bytes',
162-
'KB',
163-
'MB',
164-
'GB',
165-
'TB',
166-
'PB',
167-
];
168-
169-
const i = Math.floor(Math.log(bytes) / Math.log(k));
170-
171-
return `${ parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) } ${ sizes[i] }`;
172-
};
173-
174-
const getGenericUploadPreview = (file) => `\
175-
<div class='upload-preview'>
176-
<div>${ Handlebars._escape(file.name) } - ${ formatBytes(file.file.size) }</div>
177-
</div>
178-
<div class='upload-preview-title'>
179-
<div class="rc-input__wrapper">
180-
<input class="rc-input__element" id='file-name' style='display: inherit;' value='${ Handlebars._escape(file.name) }' placeholder='${ t('Upload_file_name') }'>
181-
</div>
182-
<div class="rc-input__wrapper">
183-
<input class="rc-input__element" id='file-description' style='display: inherit;' value='' autoFocus placeholder='${ t('Upload_file_description') }'>
184-
</div>
185-
</div>`;
186-
187-
const getUploadPreview = async (file, preview) => {
188-
if (file.type === 'audio') {
189-
return getAudioUploadPreview(file, preview);
190-
}
191-
192-
if (file.type === 'video') {
193-
return getVideoUploadPreview(file, preview);
194-
}
195-
196-
const isImageFormatSupported = () => new Promise((resolve) => {
197-
const element = document.createElement('img');
198-
element.onload = () => resolve(true);
199-
element.onerror = () => resolve(false);
200-
element.src = preview;
201-
});
202-
203-
if (file.type === 'image' && await isImageFormatSupported()) {
204-
return getImageUploadPreview(file, preview);
205-
}
206-
207-
return getGenericUploadPreview(file, preview);
208-
};
209-
21079
export const fileUpload = async (files, input, { rid, tmid }) => {
21180
const threadsEnabled = settings.get('Threads_enabled');
21281

@@ -228,55 +97,30 @@ export const fileUpload = async (files, input, { rid, tmid }) => {
22897
const uploadNextFile = () => {
22998
const file = files.pop();
23099
if (!file) {
231-
modal.close();
232100
return;
233101
}
234102

235-
if (file.file.type && !fileUploadIsValidContentType(file.file.type)) {
236-
modal.open({
237-
title: t('FileUpload_MediaType_NotAccepted'),
238-
text: file.file.type || `*.${ s.strRightBack(file.file.name, '.') }`,
239-
type: 'error',
240-
timer: 3000,
241-
});
242-
return;
243-
}
244-
245-
if (file.file.size === 0) {
246-
modal.open({
247-
title: t('FileUpload_File_Empty'),
248-
type: 'error',
249-
timer: 1000,
250-
});
251-
return;
252-
}
253-
254-
showUploadPreview(file, async (file, preview) => modal.open({
255-
title: t('Upload_file_question'),
256-
text: await getUploadPreview(file, preview),
257-
showCancelButton: true,
258-
closeOnConfirm: false,
259-
closeOnCancel: false,
260-
confirmButtonText: t('Send'),
261-
cancelButtonText: t('Cancel'),
262-
html: true,
263-
onRendered: () => $('#file-description').focus(),
264-
}, async (isConfirm) => {
265-
if (!isConfirm) {
266-
return;
267-
}
268-
269-
const fileName = document.getElementById('file-name').value || file.name || file.file.name;
270-
271-
uploadFileWithMessage(rid, tmid, {
272-
description: document.getElementById('file-description').value || undefined,
273-
fileName,
274-
msg: msg || undefined,
275-
file,
276-
});
277-
278-
uploadNextFile();
279-
}));
103+
imperativeModal.open({
104+
component: FileUploadModal,
105+
props: {
106+
file: file.file,
107+
onClose: () => {
108+
imperativeModal.close();
109+
uploadNextFile();
110+
},
111+
onSubmit: (fileName, description) => {
112+
uploadFileWithMessage(rid, tmid, {
113+
description,
114+
fileName,
115+
msg: msg || undefined,
116+
file,
117+
});
118+
imperativeModal.close();
119+
uploadNextFile();
120+
},
121+
isValidContentType: file.file.type && fileUploadIsValidContentType(file.file.type),
122+
},
123+
});
280124
};
281125

282126
uploadNextFile();
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import React, { ReactElement } from 'react';
2+
3+
import { isIE11 } from '../../../../app/ui-utils/client/lib/isIE11';
4+
import GenericPreview from './GenericPreview';
5+
import MediaPreview from './MediaPreview';
6+
7+
export enum FilePreviewType {
8+
IMAGE = 'image',
9+
AUDIO = 'audio',
10+
VIDEO = 'video',
11+
}
12+
13+
const getFileType = (fileType: File['type']): FilePreviewType | undefined => {
14+
if (!fileType) {
15+
return;
16+
}
17+
for (const type of Object.values(FilePreviewType)) {
18+
if (fileType.indexOf(type) > -1) {
19+
return type;
20+
}
21+
}
22+
};
23+
24+
const shouldShowMediaPreview = (file: File, fileType: FilePreviewType | undefined): boolean => {
25+
if (!fileType) {
26+
return false;
27+
}
28+
if (isIE11()) {
29+
return false;
30+
}
31+
// Avoid preview if file size bigger than 10mb
32+
if (file.size > 10000000) {
33+
return false;
34+
}
35+
if (!Object.values(FilePreviewType).includes(fileType)) {
36+
return false;
37+
}
38+
return true;
39+
};
40+
41+
type FilePreviewProps = {
42+
file: File;
43+
};
44+
45+
const FilePreview = ({ file }: FilePreviewProps): ReactElement => {
46+
const fileType = getFileType(file.type);
47+
48+
if (shouldShowMediaPreview(file, fileType)) {
49+
return <MediaPreview file={file} fileType={fileType as FilePreviewType} />;
50+
}
51+
52+
return <GenericPreview file={file} />;
53+
};
54+
55+
export default FilePreview;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Box } from '@rocket.chat/fuselage';
2+
import React, { useState } from 'react';
3+
4+
import FileUploadModal from '.';
5+
6+
export default {
7+
title: 'components/modals/FileUploadModal',
8+
component: FileUploadModal,
9+
};
10+
11+
const onClose = () => console.log('close');
12+
13+
const _file = new File(['lol'], 'lol', { type: 'image' });
14+
15+
export const Default = () => {
16+
const [file, setFile] = useState(_file);
17+
18+
const handleFile = (e) => {
19+
console.log(e.target.files);
20+
setFile(e.target.files[0]);
21+
};
22+
return (
23+
<Box>
24+
<Box is='input' type='file' onChange={handleFile} />
25+
<FileUploadModal file={file} onClose={onClose} />
26+
</Box>
27+
);
28+
};

0 commit comments

Comments
 (0)