Skip to content

Commit

Permalink
fix: group zipped files
Browse files Browse the repository at this point in the history
  • Loading branch information
RomanenkoStud committed Nov 24, 2023
1 parent ff6b770 commit c90f1f3
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 48 deletions.
11 changes: 6 additions & 5 deletions packages/kite-chat-component/src/kite-chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,18 +271,19 @@ export class KiteChatElement extends LitElement {

private _sendFile(event: Event) {
const target = event.target as HTMLInputElement;
const numFiles = target.files?.length ?? 0;
for (let i = 0; i < numFiles; i++) {
const file = target.files?.item(i);
if (!file) continue;
const files = Array.from(target.files || []).filter(file => file);
const batchId = randomStringId();
files.forEach(file => {
const message: FileMsg = {
messageId: randomStringId(),
timestamp: new Date(),
status: MsgStatus.unknown,
file,
batchId,
totalFiles: files.length,
};
this._dispatchMsg(message) && this.appendMsg(message);
}
});
}

private _dispatchMsg(detail: KiteMsg): boolean {
Expand Down
2 changes: 2 additions & 0 deletions packages/kite-chat-component/src/kite-payload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export type PlaintextMsg = BaseMsg & {

export type FileMsg = BaseMsg & {
file: File;
batchId?: string;
totalFiles?: number;
};

export type KiteMsg = PlaintextMsg | FileMsg;
Expand Down
29 changes: 20 additions & 9 deletions packages/kite-chat/src/kite-chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ import {
KiteDB,
openDatabase,
getMessages,
addMessage,
messageById,
modifyMessage
addMessage,
modifyMessage,
deleteMessage
} from './kite-storage';

export type KiteChatOptions = {
Expand Down Expand Up @@ -148,11 +148,17 @@ export class KiteChat {
if(!this.db) {
return;
}
messageById(messageId, this.db).then((msg) => {
this.db && modifyMessage(messageId, {...msg, ...updatedMsg}, this.db);
});
modifyMessage(messageId, updatedMsg, this.db);
}

private delete(messageId: string) {
if(!this.db) {
return;
}
deleteMessage(messageId, this.db);
}


private restore() {
if(!this.db) {
return;
Expand Down Expand Up @@ -295,12 +301,17 @@ export class KiteChat {
const fileElement = document.querySelector(
`${KiteMsgElement.TAG}[messageId="${e.messageId}"] > ${KiteFileElement.TAG}`
) as KiteFileElement | undefined;
if (fileElement) {
fileElement.file = e.file;
}
fileElement && (fileElement.file = e.file);
this.update(e.messageId, {
file: e.file,
} as ContentMsg);
e.zippedIds.forEach(id => {
const msgElement = document.querySelector(
`${KiteMsgElement.TAG}[messageId="${id}"]`
) as KiteFileElement | undefined;
msgElement?.remove();
this.delete(id);
})
}

protected onFailedMessage(e: FailedMsg) {
Expand Down
18 changes: 17 additions & 1 deletion packages/kite-chat/src/kite-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,26 @@ export async function messageById(messageId: string, db: KiteDB) {
* Function to modify an existing message in the database.
*/
export async function modifyMessage(messageId: string, modifiedMessage: ContentMsg, db: KiteDB) {
const oldMessage = await messageById(messageId, db);

const tx = db.transaction(MESSAGES_STORE_NAME, 'readwrite');
const store = tx.objectStore(MESSAGES_STORE_NAME);

const primaryKey = await store.index(MESSAGES_KEY).getKey(messageId);
await store.put({...oldMessage, ...modifiedMessage}, primaryKey);
await tx.done;
}

/**
* Function to delete a message by its messageId.
*/
export async function deleteMessage(messageId: string, db: KiteDB) {
const tx = db.transaction(MESSAGES_STORE_NAME, 'readwrite');
const store = tx.objectStore(MESSAGES_STORE_NAME);

const primaryKey = await store.index(MESSAGES_KEY).getKey(messageId);
await store.put(modifiedMessage, primaryKey);
if (primaryKey) {
await store.delete(primaryKey);
}
await tx.done;
}
3 changes: 3 additions & 0 deletions packages/kite-chat/src/kite-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ export type FileMsg = {
file: File;
timestamp: Date;
status?: MsgStatus;
batchId?: string;
totalFiles?: number;
};

export type UploadRequest = {
Expand Down Expand Up @@ -139,6 +141,7 @@ export type ActiveTab = {
export type ZippedMsg = {
type: MsgType.ZIPPED;
messageId: string;
zippedIds: string[];
file: File;
};

Expand Down
94 changes: 61 additions & 33 deletions packages/kite-chat/src/kite-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ let joinChannel: JoinChannel | null = null;
*/
const messageHistory = new Array<ContentMsg>();

let batchAccumulator = new Array<FileMsg>();

let zipQueue = new Array<FileMsg>();

const outgoingQueue = new Array<KiteMsg>();

const tabPorts = new Set<KiteMessagePort>();
Expand Down Expand Up @@ -208,17 +212,21 @@ function verifyPlainText(text: string): PlainTextVerification {
return PlainTextVerification.SUCCEED;
}

async function zipFile(file: File, resultType = "application/zip"): Promise<File> {
async function zipFiles(files: File[], resultType = "application/zip", timestamp = new Date()): Promise<File> {
// Import JSZip module dynamically
await import(/* @vite-ignore */ JSZIP_CDN);

const extendedSelf = self as unknown as typeof self & {JSZip: JSZip};
const zip: JSZip = new extendedSelf.JSZip();

zip.file(file.name, file);
files.forEach((file) => {
zip.file(file.name, file);
});

const zipFileName = files.length === 1
? `${files[0].name.replace(/\.[^/.]+$/, '')}.zip`
: `${timestamp.toISOString()}.zip`;
const blob = await zip.generateAsync({ type: 'blob', compression: 'DEFLATE', compressionOptions: { level: 0 } });
const zipFileName = `${file.name.replace(/\.[^/.]+$/, '')}.zip`;
return new File([blob], zipFileName, {type: resultType});
}

Expand Down Expand Up @@ -255,9 +263,7 @@ function onPlaintextMessage(payload: PlaintextMsg, tabPort: KiteMessagePort) {
}

function onFileMessage(payload: FileMsg, tabPort: KiteMessagePort) {
messageHistory.push(payload);

const uploadFile = (file: File) => {
const uploadFile = (payload: FileMsg, file: File = payload.file) => {
const upload: UploadRequest = {
type: MsgType.UPLOAD,
messageId: payload.messageId,
Expand All @@ -279,46 +285,68 @@ function onFileMessage(payload: FileMsg, tabPort: KiteMessagePort) {
});
}

const zippedFile = (file: File) => {
tabPort.postMessage({
type: MsgType.ZIPPED,
messageId: payload.messageId,
file: file,
});
}

const maxSize = (type: string) => formatSize(SUPPORTED_FILE_FORMATS[type as keyof typeof SUPPORTED_FILE_FORMATS]);

const result = verifyFile(payload.file);
const {file} = payload;

switch (result) {
case FileVerification.UNSUPPORTED_TYPE:
if(payload.file.size > SUPPORTED_FILE_FORMATS[ZIP_FILE_FORMAT]) {
failedFile(
FileVerification.EXCEED_SIZE,
`${ZIP_FILE_FORMAT} size exceeds ${maxSize(ZIP_FILE_FORMAT)} limit.`
);
break;
if(file.size > SUPPORTED_FILE_FORMATS[ZIP_FILE_FORMAT]) {
failedFile(FileVerification.EXCEED_SIZE, `${ZIP_FILE_FORMAT} size exceeds ${maxSize(ZIP_FILE_FORMAT)} limit.`);
} else {
zipQueue.push(payload);
}
zipFile(payload.file, ZIP_FILE_FORMAT)
.then(file => {
zippedFile(file);
uploadFile(file);
})
.catch(error => {
console.error('Error zipping file:', error);
});
break;
case FileVerification.EXCEED_SIZE:
failedFile(
FileVerification.EXCEED_SIZE,
`${payload.file.type} size exceeds ${maxSize(payload.file.type)} limit.`
);
failedFile(FileVerification.EXCEED_SIZE, `${file.type} size exceeds ${maxSize(file.type)} limit.`);
break;
case FileVerification.SUCCEED:
uploadFile(payload.file);
uploadFile(payload);
break;
}

batchAccumulator.push(payload);

if(payload.totalFiles !== batchAccumulator.length) {
return;
}

const chunks = new Array<FileMsg[]>();

while (zipQueue.length > 0) {
const chunk = zipQueue.reduce((acc, msg) => {
const newSize = acc.size + msg.file.size;
if (newSize <= SUPPORTED_FILE_FORMATS[ZIP_FILE_FORMAT]) {
acc.messages.push(msg);
acc.size = newSize;
}
return acc;
}, { size: 0, messages: new Array<FileMsg>()});

chunks.push(chunk.messages);
zipQueue = zipQueue.filter(msg => !chunk.messages.includes(msg));
}

for(const chunk of chunks) {
zipFiles(chunk.map(msg => msg.file), ZIP_FILE_FORMAT, payload.timestamp)
.then(file => {
const {messageId} = chunk[0];
const messageIds = chunk.map(msg => msg.messageId);
tabPort.postMessage({
type: MsgType.ZIPPED,
messageId: messageId,
zippedIds: messageIds.filter(id => id !== messageId),
file: file,
});
uploadFile(chunk[0], file);
})
.catch(error => {
console.error('Error zipping file:', error);
});
}

batchAccumulator = [];
}

function onJoinChannel(payload: JoinChannel) {
Expand Down

0 comments on commit c90f1f3

Please sign in to comment.