feat(web-media): improve upload/send reliability
Some checks failed
CI / test (push) Failing after 17s
Some checks failed
CI / test (push) Failing after 17s
- add retry logic for presigned media upload - add retry for text send with idempotent client message id - avoid dropping already sent media message when attachment metadata call fails
This commit is contained in:
@@ -104,8 +104,12 @@ export async function uploadToPresignedUrl(
|
|||||||
uploadUrl: string,
|
uploadUrl: string,
|
||||||
requiredHeaders: Record<string, string>,
|
requiredHeaders: Record<string, string>,
|
||||||
file: File,
|
file: File,
|
||||||
onProgress?: (percent: number) => void
|
onProgress?: (percent: number) => void,
|
||||||
|
retries = 2
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
let attempt = 0;
|
||||||
|
while (attempt <= retries) {
|
||||||
|
try {
|
||||||
await axios.put(uploadUrl, file, {
|
await axios.put(uploadUrl, file, {
|
||||||
headers: requiredHeaders,
|
headers: requiredHeaders,
|
||||||
onUploadProgress: (progressEvent) => {
|
onUploadProgress: (progressEvent) => {
|
||||||
@@ -115,6 +119,17 @@ export async function uploadToPresignedUrl(
|
|||||||
onProgress(Math.round((progressEvent.loaded * 100) / progressEvent.total));
|
onProgress(Math.round((progressEvent.loaded * 100) / progressEvent.total));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
const status = axios.isAxiosError(error) ? error.response?.status : undefined;
|
||||||
|
const retryable = status === undefined || status >= 500;
|
||||||
|
if (!retryable || attempt >= retries) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
attempt += 1;
|
||||||
|
await new Promise((resolve) => window.setTimeout(resolve, 400 * attempt));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function attachFile(messageId: number, fileUrl: string, fileType: string, fileSize: number): Promise<void> {
|
export async function attachFile(messageId: number, fileUrl: string, fileType: string, fileSize: number): Promise<void> {
|
||||||
|
|||||||
@@ -77,7 +77,21 @@ export function MessageComposer() {
|
|||||||
const replyToMessageId = activeChatId ? (replyToByChat[activeChatId]?.id ?? undefined) : undefined;
|
const replyToMessageId = activeChatId ? (replyToByChat[activeChatId]?.id ?? undefined) : undefined;
|
||||||
addOptimisticMessage({ chatId: activeChatId, senderId: me.id, type: "text", text: textValue, clientMessageId });
|
addOptimisticMessage({ chatId: activeChatId, senderId: me.id, type: "text", text: textValue, clientMessageId });
|
||||||
try {
|
try {
|
||||||
const message = await sendMessageWithClientId(activeChatId, textValue, "text", clientMessageId, replyToMessageId);
|
let message = null;
|
||||||
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
||||||
|
try {
|
||||||
|
message = await sendMessageWithClientId(activeChatId, textValue, "text", clientMessageId, replyToMessageId);
|
||||||
|
break;
|
||||||
|
} catch (error) {
|
||||||
|
if (attempt === 1) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
await new Promise((resolve) => window.setTimeout(resolve, 250));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!message) {
|
||||||
|
throw new Error("send failed");
|
||||||
|
}
|
||||||
confirmMessageByClientId(activeChatId, clientMessageId, message);
|
confirmMessageByClientId(activeChatId, clientMessageId, message);
|
||||||
setText("");
|
setText("");
|
||||||
clearDraft(activeChatId);
|
clearDraft(activeChatId);
|
||||||
@@ -110,8 +124,12 @@ export function MessageComposer() {
|
|||||||
clientMessageId
|
clientMessageId
|
||||||
});
|
});
|
||||||
const message = await sendMessageWithClientId(activeChatId, upload.file_url, messageType, clientMessageId, replyToMessageId);
|
const message = await sendMessageWithClientId(activeChatId, upload.file_url, messageType, clientMessageId, replyToMessageId);
|
||||||
await attachFile(message.id, upload.file_url, file.type || "application/octet-stream", file.size);
|
|
||||||
confirmMessageByClientId(activeChatId, clientMessageId, message);
|
confirmMessageByClientId(activeChatId, clientMessageId, message);
|
||||||
|
try {
|
||||||
|
await attachFile(message.id, upload.file_url, file.type || "application/octet-stream", file.size);
|
||||||
|
} catch {
|
||||||
|
setUploadError("File sent, but metadata save failed. Please refresh chat.");
|
||||||
|
}
|
||||||
setReplyToMessage(activeChatId, null);
|
setReplyToMessage(activeChatId, null);
|
||||||
} catch {
|
} catch {
|
||||||
removeOptimisticMessage(activeChatId, clientMessageId);
|
removeOptimisticMessage(activeChatId, clientMessageId);
|
||||||
|
|||||||
Reference in New Issue
Block a user