The communications platform that puts data protection first.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
Rocket.Chat/apps/meteor/client/lib/chats/uploads.ts

151 lines
3.3 KiB

import type { IMessage, IRoom } from '@rocket.chat/core-typings';
import { Emitter } from '@rocket.chat/emitter';
import { Random } from '@rocket.chat/random';
import { UserAction, USER_ACTIVITIES } from '../../../app/ui/client/lib/UserAction';
import { sdk } from '../../../app/utils/client/lib/SDKClient';
import { getErrorMessage } from '../errorHandling';
import type { UploadsAPI } from './ChatAPI';
import type { Upload } from './Upload';
let uploads: readonly Upload[] = [];
const emitter = new Emitter<{ update: void; [x: `cancelling-${Upload['id']}`]: void }>();
const updateUploads = (update: (uploads: readonly Upload[]) => readonly Upload[]): void => {
uploads = update(uploads);
emitter.emit('update');
};
const get = (): readonly Upload[] => uploads;
const subscribe = (callback: () => void): (() => void) => emitter.on('update', callback);
const cancel = (id: Upload['id']): void => {
emitter.emit(`cancelling-${id}`);
};
const wipeFailedOnes = (): void => {
updateUploads((uploads) => uploads.filter((upload) => !upload.error));
};
const send = async (
file: File,
{
description,
msg,
rid,
tmid,
}: {
description?: string;
msg?: string;
rid: string;
tmid?: string;
},
): Promise<void> => {
const id = Random.id();
updateUploads((uploads) => [
...uploads,
{
id,
name: file.name,
percentage: 0,
},
]);
try {
await new Promise((resolve, reject) => {
const xhr = sdk.rest.upload(
`/v1/rooms.upload/${rid}`,
{
msg,
tmid,
file,
description,
},
{
load: (event) => {
resolve(event);
},
progress: (event) => {
if (!event.lengthComputable) {
return;
}
const progress = (event.loaded / event.total) * 100;
if (progress === 100) {
return;
}
updateUploads((uploads) =>
uploads.map((upload) => {
if (upload.id !== id) {
return upload;
}
return {
...upload,
percentage: Math.round(progress) || 0,
};
}),
);
},
error: (event) => {
updateUploads((uploads) =>
uploads.map((upload) => {
if (upload.id !== id) {
return upload;
}
return {
...upload,
percentage: 0,
error: new Error(xhr.responseText),
};
}),
);
reject(event);
},
},
);
if (uploads.length) {
UserAction.performContinuously(rid, USER_ACTIVITIES.USER_UPLOADING, { tmid });
}
emitter.once(`cancelling-${id}`, () => {
xhr.abort();
updateUploads((uploads) => uploads.filter((upload) => upload.id !== id));
});
});
updateUploads((uploads) => uploads.filter((upload) => upload.id !== id));
} catch (error: unknown) {
updateUploads((uploads) =>
uploads.map((upload) => {
if (upload.id !== id) {
return upload;
}
return {
...upload,
percentage: 0,
error: new Error(getErrorMessage(error)),
};
}),
);
} finally {
if (!uploads.length) {
UserAction.stop(rid, USER_ACTIVITIES.USER_UPLOADING, { tmid });
}
}
};
export const createUploadsAPI = ({ rid, tmid }: { rid: IRoom['_id']; tmid?: IMessage['_id'] }): UploadsAPI => ({
get,
subscribe,
wipeFailedOnes,
cancel,
send: (file: File, { description, msg }: { description?: string; msg?: string }): Promise<void> =>
send(file, { description, msg, rid, tmid }),
});