Skip to content

Commit a7f0e02

Browse files
authored
fix: upsert attachmemts only when attachment exist and handle FileList for react native (#1539)
The goal of the PR is to upsert attachments only when they exist. This is especially important because on React Native, we allow cancelling uploading by calling removeAttachments, but when the upload still happens, the upsert proceeds. We will only update you if the attachment is present. The other fix is that the `FileList` is not supported on RN. Thanks to @MartinCupela for a prompt suggestion on this issue.
1 parent e4db506 commit a7f0e02

File tree

3 files changed

+96
-25
lines changed

3 files changed

+96
-25
lines changed

src/messageComposer/attachmentManager.ts

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -207,35 +207,55 @@ export class AttachmentManager {
207207
return this.attachments.indexOf(attachmentsById[localId]);
208208
};
209209

210-
upsertAttachments = (attachmentsToUpsert: LocalAttachment[]) => {
211-
if (!attachmentsToUpsert.length) return;
210+
private prepareAttachmentUpdate = (attachmentToUpdate: LocalAttachment) => {
211+
const stateAttachments = this.attachments;
212+
const attachments = [...this.attachments];
213+
const attachmentIndex = this.getAttachmentIndex(attachmentToUpdate.localMetadata.id);
214+
if (attachmentIndex === -1) return null;
215+
// do not re-organize newAttachments array otherwise indexing would no longer work
216+
// replace in place only with the attachments with the same id's
217+
const merged = mergeWithDiff<LocalAttachment>(
218+
stateAttachments[attachmentIndex],
219+
attachmentToUpdate,
220+
);
221+
const updatesOnMerge = merged.diff && Object.keys(merged.diff.children).length;
222+
if (updatesOnMerge) {
223+
const localAttachment = ensureIsLocalAttachment(merged.result);
224+
if (localAttachment) {
225+
attachments.splice(attachmentIndex, 1, localAttachment);
226+
return attachments;
227+
}
228+
}
229+
return null;
230+
};
212231

213-
const currentAttachments = this.attachments;
214-
const newAttachments = [...currentAttachments];
232+
updateAttachment = (attachmentToUpdate: LocalAttachment) => {
233+
const updatedAttachments = this.prepareAttachmentUpdate(attachmentToUpdate);
234+
if (updatedAttachments) {
235+
this.state.partialNext({ attachments: updatedAttachments });
236+
}
237+
};
215238

239+
upsertAttachments = (attachmentsToUpsert: LocalAttachment[]) => {
240+
if (!attachmentsToUpsert.length) return;
241+
let attachments = [...this.attachments];
242+
let hasUpdates = false;
216243
attachmentsToUpsert.forEach((attachment) => {
217-
const targetAttachmentIndex = this.getAttachmentIndex(attachment.localMetadata?.id);
218-
219-
if (targetAttachmentIndex < 0) {
220-
const localAttachment = ensureIsLocalAttachment(attachment);
221-
if (localAttachment) newAttachments.push(localAttachment);
244+
const updatedAttachments = this.prepareAttachmentUpdate(attachment);
245+
if (updatedAttachments) {
246+
attachments = updatedAttachments;
247+
hasUpdates = true;
222248
} else {
223-
// do not re-organize newAttachments array otherwise indexing would no longer work
224-
// replace in place only with the attachments with the same id's
225-
const merged = mergeWithDiff<LocalAttachment>(
226-
currentAttachments[targetAttachmentIndex],
227-
attachment,
228-
);
229-
const updatesOnMerge = merged.diff && Object.keys(merged.diff.children).length;
230-
if (updatesOnMerge) {
231-
const localAttachment = ensureIsLocalAttachment(merged.result);
232-
if (localAttachment)
233-
newAttachments.splice(targetAttachmentIndex, 1, localAttachment);
249+
const localAttachment = ensureIsLocalAttachment(attachment);
250+
if (localAttachment) {
251+
attachments.push(localAttachment);
252+
hasUpdates = true;
234253
}
235254
}
236255
});
237-
238-
this.state.partialNext({ attachments: newAttachments });
256+
if (hasUpdates) {
257+
this.state.partialNext({ attachments });
258+
}
239259
};
240260

241261
removeAttachments = (localAttachmentIds: string[]) => {
@@ -476,7 +496,7 @@ export class AttachmentManager {
476496
},
477497
};
478498

479-
this.upsertAttachments([failedAttachment]);
499+
this.updateAttachment(failedAttachment);
480500
return failedAttachment;
481501
}
482502

@@ -510,7 +530,7 @@ export class AttachmentManager {
510530
(uploadedAttachment as LocalNotImageAttachment).thumb_url = response.thumb_url;
511531
}
512532

513-
this.upsertAttachments([uploadedAttachment]);
533+
this.updateAttachment(uploadedAttachment);
514534

515535
return uploadedAttachment;
516536
};

src/messageComposer/fileUtils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const isFileList = (obj: unknown): obj is FileList => {
1616
if (typeof obj !== 'object') return false;
1717

1818
return (
19-
(obj as object) instanceof FileList ||
19+
(typeof FileList !== 'undefined' && (obj as object) instanceof FileList) ||
2020
('item' in obj && 'length' in obj && !Array.isArray(obj))
2121
);
2222
};

test/unit/MessageComposer/attachmentManager.test.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,57 @@ describe('AttachmentManager', () => {
503503
});
504504
});
505505

506+
describe('updateAttachment', () => {
507+
it('should update an attachment by id', () => {
508+
const {
509+
messageComposer: { attachmentManager },
510+
} = setup();
511+
512+
const newAttachments = [
513+
{ localMetadata: { id: 'test-id-1' }, type: 'image' },
514+
{ localMetadata: { id: 'test-id-2' }, type: 'video' },
515+
];
516+
517+
attachmentManager.upsertAttachments(newAttachments);
518+
519+
const updatedAttachment = {
520+
id: 'test-id-1',
521+
localMetadata: { id: 'test-id-1' },
522+
type: 'audio',
523+
};
524+
525+
attachmentManager.updateAttachment(updatedAttachment);
526+
527+
expect(attachmentManager.attachments).toEqual([
528+
updatedAttachment,
529+
newAttachments[1],
530+
]);
531+
});
532+
533+
it('should not update an attachment if id is not found', () => {
534+
const {
535+
messageComposer: { attachmentManager },
536+
} = setup();
537+
538+
const newAttachments = [
539+
{ localMetadata: { id: 'test-id-1' }, type: 'image' },
540+
{ localMetadata: { id: 'test-id-2' }, type: 'video' },
541+
];
542+
543+
attachmentManager.upsertAttachments(newAttachments);
544+
545+
const updatedAttachment = {
546+
id: 'non-existent-id',
547+
localMetadata: { id: 'non-existent-id' },
548+
type: 'audio',
549+
};
550+
551+
attachmentManager.updateAttachment(updatedAttachment);
552+
553+
expect(attachmentManager.attachments).toEqual(newAttachments);
554+
});
555+
});
556+
506557
describe('removeAttachments', () => {
507558
it('should remove attachments by id', () => {
508559
const {

0 commit comments

Comments
 (0)