From da4110a4952389ced67a55089fa9e0b356336e0c Mon Sep 17 00:00:00 2001
From: Shivam Mishra
Date: Thu, 20 Nov 2025 19:40:20 +0530
Subject: [PATCH] feat: add retry loadWithRetry composable (#12873)
---
.../components-next/message/bubbles/Image.vue | 26 +++++----
.../dashboard/composables/loadWithRetry.js | 57 +++++++++++++++++++
2 files changed, 72 insertions(+), 11 deletions(-)
create mode 100644 app/javascript/dashboard/composables/loadWithRetry.js
diff --git a/app/javascript/dashboard/components-next/message/bubbles/Image.vue b/app/javascript/dashboard/components-next/message/bubbles/Image.vue
index 2484bb06c..f41fdad9a 100644
--- a/app/javascript/dashboard/components-next/message/bubbles/Image.vue
+++ b/app/javascript/dashboard/components-next/message/bubbles/Image.vue
@@ -1,7 +1,8 @@
@@ -54,14 +60,12 @@ const downloadAttachment = async () => {
{{ $t('COMPONENTS.MEDIA.IMAGE_UNAVAILABLE') }}
-
+
{
v-model:show="showGallery"
:attachment="useSnakeCase(attachment)"
:all-attachments="filteredCurrentChatAttachments"
- @error="handleError"
+ @error="handleImageError"
@close="() => (showGallery = false)"
/>
diff --git a/app/javascript/dashboard/composables/loadWithRetry.js b/app/javascript/dashboard/composables/loadWithRetry.js
new file mode 100644
index 000000000..0246c1e44
--- /dev/null
+++ b/app/javascript/dashboard/composables/loadWithRetry.js
@@ -0,0 +1,57 @@
+import { ref } from 'vue';
+
+export const useLoadWithRetry = (config = {}) => {
+ const maxRetry = config.max_retry || 3;
+ const backoff = config.backoff || 1000;
+
+ const isLoaded = ref(false);
+ const hasError = ref(false);
+
+ const loadWithRetry = async url => {
+ const attemptLoad = () => {
+ return new Promise((resolve, reject) => {
+ const img = new Image();
+
+ img.onload = () => {
+ isLoaded.value = true;
+ hasError.value = false;
+ resolve();
+ };
+
+ img.onerror = () => {
+ reject(new Error('Failed to load image'));
+ };
+
+ img.src = url;
+ });
+ };
+
+ const sleep = ms => {
+ return new Promise(resolve => {
+ setTimeout(resolve, ms);
+ });
+ };
+
+ const retry = async (attempt = 0) => {
+ try {
+ await attemptLoad();
+ } catch (error) {
+ if (attempt + 1 >= maxRetry) {
+ hasError.value = true;
+ isLoaded.value = false;
+ return;
+ }
+ await sleep(backoff * (attempt + 1));
+ await retry(attempt + 1);
+ }
+ };
+
+ await retry();
+ };
+
+ return {
+ isLoaded,
+ hasError,
+ loadWithRetry,
+ };
+};