Skip to content

Commit

Permalink
fix: do not send two upload requests when offline support is enabled
Browse files Browse the repository at this point in the history
  • Loading branch information
santhoshvai committed Nov 23, 2023
1 parent 8cbc927 commit b722859
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 21 deletions.
20 changes: 20 additions & 0 deletions package/src/components/Channel/Channel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,13 @@ const ChannelWithContext = <

const { setTargetedMessage, targetedMessage } = useTargetedMessage();

/**
* This ref will hold the abort controllers for
* requests made for uploading images/files in the messageInputContext
* Its a map of filename to AbortController
*/
const uploadAbortControllerRef = useRef<Map<string, AbortController>>(new Map());

const channelId = channel?.id || '';
useEffect(() => {
const initChannel = () => {
Expand Down Expand Up @@ -1299,8 +1306,14 @@ const ChannelWithContext = <
isLocalUrl(attachment.image_url)
) {
const filename = file.name ?? file.uri.replace(/^(file:\/\/|content:\/\/)/, '');
const controller = uploadAbortControllerRef.current.get(filename);
if (controller) {
controller.abort();
uploadAbortControllerRef.current.delete(filename);
}
const contentType = lookup(filename) || 'multipart/form-data';

// if any upload is in progress, cancel it
const uploadResponse = doImageUploadRequest
? await doImageUploadRequest(file, channel)
: await channel.sendImage(file.uri, filename, contentType);
Expand All @@ -1321,6 +1334,12 @@ const ChannelWithContext = <
isLocalUrl(attachment.asset_url) &&
file?.uri
) {
// if any upload is in progress, cancel it
const controller = uploadAbortControllerRef.current.get(file.name);
if (controller) {
controller.abort();
uploadAbortControllerRef.current.delete(file.name);
}
const response = doDocUploadRequest
? await doDocUploadRequest(file, channel)
: await channel.sendFile(file.uri, file.name, file.mimeType);
Expand Down Expand Up @@ -1851,6 +1870,7 @@ const ChannelWithContext = <
StickyHeader,
targetedMessage,
threadList,
uploadAbortControllerRef,
watcherCount,
watchers,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const useCreateChannelContext = <
StickyHeader,
targetedMessage,
threadList,
uploadAbortControllerRef,
watcherCount,
watchers,
}: ChannelContextValue<StreamChatGenerics>) => {
Expand Down Expand Up @@ -79,6 +80,7 @@ export const useCreateChannelContext = <
StickyHeader,
targetedMessage,
threadList,
uploadAbortControllerRef,
watcherCount,
watchers,
}),
Expand Down
5 changes: 5 additions & 0 deletions package/src/contexts/channelContext/ChannelContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ export type ChannelContextValue<
scrollToFirstUnreadThreshold: number;
setLastRead: React.Dispatch<React.SetStateAction<Date | undefined>>;
setTargetedMessage: (messageId: string) => void;
/**
* Abort controller for cancelling async requests made for uploading images/files
* Its a map of filename and AbortController
*/
uploadAbortControllerRef: React.MutableRefObject<Map<string, AbortController>>;
/**
*
* ```json
Expand Down
79 changes: 58 additions & 21 deletions package/src/contexts/messageInputContext/MessageInputContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,8 @@ export const MessageInputProvider = <

const channelCapabities = useOwnCapabilitiesContext();

const { channel, giphyEnabled } = useChannelContext<StreamChatGenerics>();
const { channel, giphyEnabled, uploadAbortControllerRef } =
useChannelContext<StreamChatGenerics>();
const { thread } = useThreadContext<StreamChatGenerics>();
const { t } = useTranslationContext();
const inputBoxRef = useRef<TextInput | null>(null);
Expand Down Expand Up @@ -968,11 +969,21 @@ export const MessageInputProvider = <
if (value.doDocUploadRequest) {
response = await value.doDocUploadRequest(file, channel);
} else if (channel && file.uri) {
uploadAbortControllerRef.current.set(
file.name,
client.createAbortControllerForNextRequest(),
);
response = await channel.sendFile(file.uri, file.name, file.mimeType);
uploadAbortControllerRef.current.delete(file.name);
}
const extraData: Partial<FileUpload> = { thumb_url: response.thumb_url, url: response.file };
setFileUploads(getUploadSetStateAction(id, FileState.UPLOADED, extraData));
} catch (error: unknown) {
if (error instanceof Error && error.name === 'AbortError') {
// nothing to do
uploadAbortControllerRef.current.delete(file.name);
return;
}
handleFileOrImageUploadError(error, false, id);
}
};
Expand All @@ -986,8 +997,10 @@ export const MessageInputProvider = <

let response = {} as SendFileAPIResponse;

const uri = file.uri || '';
const filename = file.filename ?? uri.replace(/^(file:\/\/|content:\/\/)/, '');

try {
const uri = file.uri || '';
/**
* We skip compression if:
* - the file is from the camera as that should already be compressed
Expand All @@ -1006,32 +1019,51 @@ export const MessageInputProvider = <
uri,
width: file.width,
}));
const filename = file.filename ?? uri.replace(/^(file:\/\/|content:\/\/)/, '');

const contentType = lookup(filename) || 'multipart/form-data';
if (value.doImageUploadRequest) {
response = await value.doImageUploadRequest(file, channel);
} else if (compressedUri && channel) {
if (value.sendImageAsync) {
channel.sendImage(compressedUri, filename, contentType).then((res) => {
if (asyncIds.includes(id)) {
// Evaluates to true if user hit send before image successfully uploaded
setAsyncUploads((prevAsyncUploads) => {
prevAsyncUploads[id] = {
...prevAsyncUploads[id],
state: FileState.UPLOADED,
url: res.file,
};
return prevAsyncUploads;
});
} else {
const newImageUploads = getUploadSetStateAction<ImageUpload>(id, FileState.UPLOADED, {
url: res.file,
});
setImageUploads(newImageUploads);
}
});
uploadAbortControllerRef.current.set(
filename,
client.createAbortControllerForNextRequest(),
);
channel.sendImage(compressedUri, filename, contentType).then(
(res) => {
uploadAbortControllerRef.current.delete(filename);
if (asyncIds.includes(id)) {
// Evaluates to true if user hit send before image successfully uploaded
setAsyncUploads((prevAsyncUploads) => {
prevAsyncUploads[id] = {
...prevAsyncUploads[id],
state: FileState.UPLOADED,
url: res.file,
};
return prevAsyncUploads;
});
} else {
const newImageUploads = getUploadSetStateAction<ImageUpload>(
id,
FileState.UPLOADED,
{
url: res.file,
},
);
setImageUploads(newImageUploads);
}
},
() => {
uploadAbortControllerRef.current.delete(filename);
},
);
} else {
uploadAbortControllerRef.current.set(
filename,
client.createAbortControllerForNextRequest(),
);
response = await channel.sendImage(compressedUri, filename, contentType);
uploadAbortControllerRef.current.delete(filename);
}
}

Expand All @@ -1044,6 +1076,11 @@ export const MessageInputProvider = <
setImageUploads(newImageUploads);
}
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
// nothing to do
uploadAbortControllerRef.current.delete(filename);
return;
}
handleFileOrImageUploadError(error, true, id);
}
};
Expand Down

0 comments on commit b722859

Please sign in to comment.