From 07a5638d8da6084368afe068f44038a1e98871f6 Mon Sep 17 00:00:00 2001 From: afc163 Date: Wed, 6 May 2026 22:45:49 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20add=20retry=20button=20for=20chunk=20er?= =?UTF-8?q?rors,=20simplify=20DOM,=20rename=20pwa=E2=86=92network?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add Retry button for chunk load errors that remounts children via key increment, forcing React.lazy to re-execute import() without a full page reload - Remove wrapper divs: Card padding applied directly via style, Alert positioned directly without outer container - Rename pwa.ts → network.ts across all 8 locales (keys no longer PWA-specific) and update imports in locale index files - Add app.error.retry locale key to all 8 locales Co-Authored-By: Claude Opus 4.7 --- src/components/ErrorBoundary/index.tsx | 110 ++++++++++++++--------- src/components/OfflineBanner/index.tsx | 37 +++----- src/locales/bn-BD.ts | 4 +- src/locales/bn-BD/{pwa.ts => network.ts} | 1 + src/locales/en-US.ts | 4 +- src/locales/en-US/{pwa.ts => network.ts} | 1 + src/locales/fa-IR.ts | 4 +- src/locales/fa-IR/{pwa.ts => network.ts} | 1 + src/locales/id-ID.ts | 4 +- src/locales/id-ID/{pwa.ts => network.ts} | 1 + src/locales/ja-JP.ts | 4 +- src/locales/ja-JP/{pwa.ts => network.ts} | 1 + src/locales/pt-BR.ts | 4 +- src/locales/pt-BR/{pwa.ts => network.ts} | 1 + src/locales/zh-CN.ts | 4 +- src/locales/zh-CN/{pwa.ts => network.ts} | 1 + src/locales/zh-TW.ts | 4 +- src/locales/zh-TW/{pwa.ts => network.ts} | 1 + 18 files changed, 106 insertions(+), 81 deletions(-) rename src/locales/bn-BD/{pwa.ts => network.ts} (95%) rename src/locales/en-US/{pwa.ts => network.ts} (96%) rename src/locales/fa-IR/{pwa.ts => network.ts} (96%) rename src/locales/id-ID/{pwa.ts => network.ts} (95%) rename src/locales/ja-JP/{pwa.ts => network.ts} (96%) rename src/locales/pt-BR/{pwa.ts => network.ts} (95%) rename src/locales/zh-CN/{pwa.ts => network.ts} (95%) rename src/locales/zh-TW/{pwa.ts => network.ts} (95%) diff --git a/src/components/ErrorBoundary/index.tsx b/src/components/ErrorBoundary/index.tsx index c7ac7aef..591dc0ff 100644 --- a/src/components/ErrorBoundary/index.tsx +++ b/src/components/ErrorBoundary/index.tsx @@ -20,6 +20,7 @@ function getSubTitleId(isChunkError: boolean, isOffline: boolean): string { function renderErrorFallback( error: Error, isOnline: boolean, + onRetry: () => void, onReload: () => void, ) { const intl = getIntl(); @@ -27,65 +28,71 @@ function renderErrorFallback( const isChunkError = isChunkLoadError(error); return ( -
- - + + {intl.formatMessage({ - id: 'app.error.reload', - defaultMessage: 'Reload Page', + id: 'app.error.retry', + defaultMessage: 'Retry', })} - , - , - ]} - /> - -
+ + ), + , + , + ].filter(Boolean)} + /> + ); } -interface ErrorBoundaryProps { - children: React.ReactNode; -} - interface ErrorBoundaryState { hasError: boolean; error: Error | null; isOnline: boolean; + retryCount: number; } export default class ErrorBoundary extends React.Component< - ErrorBoundaryProps, + { children: React.ReactNode }, ErrorBoundaryState > { state: ErrorBoundaryState = { hasError: false, error: null, isOnline: typeof navigator !== 'undefined' ? navigator.onLine : true, + retryCount: 0, }; static getDerivedStateFromError(error: Error): Partial { @@ -121,15 +128,34 @@ export default class ErrorBoundary extends React.Component< console.error('[ErrorBoundary]', error, info.componentStack); } + /** Retry loading the failed module without a full page reload. */ + handleRetry = () => { + // Incrementing retryCount changes the key on the children fragment, + // forcing React to unmount and remount all lazy components. + // This causes React.lazy to re-execute import() for the failed chunk. + this.setState((prev) => ({ + hasError: false, + error: null, + retryCount: prev.retryCount + 1, + })); + }; + handleReload = () => { window.location.reload(); }; render() { - if (!this.state.hasError || !this.state.error) return this.props.children; + if (!this.state.hasError || !this.state.error) { + return ( + + {this.props.children} + + ); + } return renderErrorFallback( this.state.error, this.state.isOnline, + this.handleRetry, this.handleReload, ); } diff --git a/src/components/OfflineBanner/index.tsx b/src/components/OfflineBanner/index.tsx index 7fbc9eca..391904bc 100644 --- a/src/components/OfflineBanner/index.tsx +++ b/src/components/OfflineBanner/index.tsx @@ -21,33 +21,24 @@ const OfflineBanner: React.FC = () => { if (isOnline) return null; return ( -
- -
+ message={getIntl().formatMessage({ + id: 'app.network.offline', + defaultMessage: + 'You are currently offline. Some features may be unavailable.', + })} + /> ); }; diff --git a/src/locales/bn-BD.ts b/src/locales/bn-BD.ts index 618cc783..c113f3cd 100644 --- a/src/locales/bn-BD.ts +++ b/src/locales/bn-BD.ts @@ -1,8 +1,8 @@ import component from './bn-BD/component'; import globalHeader from './bn-BD/globalHeader'; import menu from './bn-BD/menu'; +import network from './bn-BD/network'; import pages from './bn-BD/pages'; -import pwa from './bn-BD/pwa'; import settingDrawer from './bn-BD/settingDrawer'; import settings from './bn-BD/settings'; @@ -16,7 +16,7 @@ export default { ...menu, ...settingDrawer, ...settings, - ...pwa, + ...network, ...component, ...pages, }; diff --git a/src/locales/bn-BD/pwa.ts b/src/locales/bn-BD/network.ts similarity index 95% rename from src/locales/bn-BD/pwa.ts rename to src/locales/bn-BD/network.ts index a8f2c8d5..b98f7c37 100644 --- a/src/locales/bn-BD/pwa.ts +++ b/src/locales/bn-BD/network.ts @@ -8,6 +8,7 @@ export default { 'app.error.render.title': 'কিছু ভুল হয়েছে', 'app.error.render.description': 'দুঃখিত, এই পৃষ্ঠায় একটি ত্রুটি ঘটেছে। দয়া করে রিফ্রেশ করুন বা হোম পৃষ্ঠায় ফিরে যান।', + 'app.error.retry': 'আবার চেষ্টা করুন', 'app.error.reload': 'পৃষ্ঠা রিফ্রেশ করুন', 'app.error.home': 'হোমে ফিরে যান', 'app.request.offline': diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts index 09f0ef0e..6392dba6 100644 --- a/src/locales/en-US.ts +++ b/src/locales/en-US.ts @@ -1,8 +1,8 @@ import component from './en-US/component'; import globalHeader from './en-US/globalHeader'; import menu from './en-US/menu'; +import network from './en-US/network'; import pages from './en-US/pages'; -import pwa from './en-US/pwa'; import settingDrawer from './en-US/settingDrawer'; import settings from './en-US/settings'; @@ -16,7 +16,7 @@ export default { ...menu, ...settingDrawer, ...settings, - ...pwa, + ...network, ...component, ...pages, }; diff --git a/src/locales/en-US/pwa.ts b/src/locales/en-US/network.ts similarity index 96% rename from src/locales/en-US/pwa.ts rename to src/locales/en-US/network.ts index 68f36946..9a0fd432 100644 --- a/src/locales/en-US/pwa.ts +++ b/src/locales/en-US/network.ts @@ -9,6 +9,7 @@ export default { 'app.error.render.title': 'Something went wrong', 'app.error.render.description': 'Sorry, an error occurred on this page. Please reload or go back to the home page.', + 'app.error.retry': 'Retry', 'app.error.reload': 'Reload Page', 'app.error.home': 'Back Home', 'app.request.offline': diff --git a/src/locales/fa-IR.ts b/src/locales/fa-IR.ts index f743db30..a6f41b1c 100644 --- a/src/locales/fa-IR.ts +++ b/src/locales/fa-IR.ts @@ -1,8 +1,8 @@ import component from './fa-IR/component'; import globalHeader from './fa-IR/globalHeader'; import menu from './fa-IR/menu'; +import network from './fa-IR/network'; import pages from './fa-IR/pages'; -import pwa from './fa-IR/pwa'; import settingDrawer from './fa-IR/settingDrawer'; import settings from './fa-IR/settings'; @@ -16,7 +16,7 @@ export default { ...menu, ...settingDrawer, ...settings, - ...pwa, + ...network, ...component, ...pages, }; diff --git a/src/locales/fa-IR/pwa.ts b/src/locales/fa-IR/network.ts similarity index 96% rename from src/locales/fa-IR/pwa.ts rename to src/locales/fa-IR/network.ts index fd5c7d4b..4f9a8baa 100644 --- a/src/locales/fa-IR/pwa.ts +++ b/src/locales/fa-IR/network.ts @@ -9,6 +9,7 @@ export default { 'app.error.render.title': 'خطایی رخ داد', 'app.error.render.description': 'متأسفانه، در این صفحه خطایی رخ داد. لطفاً صفحه را بارگذاری مجدد کنید یا به صفحه اصلی بازگردید.', + 'app.error.retry': 'تلاش مجدد', 'app.error.reload': 'بارگذاری مجدد صفحه', 'app.error.home': 'بازگشت به صفحه اصلی', 'app.request.offline': diff --git a/src/locales/id-ID.ts b/src/locales/id-ID.ts index ae9a39f3..b457ed75 100644 --- a/src/locales/id-ID.ts +++ b/src/locales/id-ID.ts @@ -1,8 +1,8 @@ import component from './id-ID/component'; import globalHeader from './id-ID/globalHeader'; import menu from './id-ID/menu'; +import network from './id-ID/network'; import pages from './id-ID/pages'; -import pwa from './id-ID/pwa'; import settingDrawer from './id-ID/settingDrawer'; import settings from './id-ID/settings'; @@ -16,7 +16,7 @@ export default { ...menu, ...settingDrawer, ...settings, - ...pwa, + ...network, ...component, ...pages, }; diff --git a/src/locales/id-ID/pwa.ts b/src/locales/id-ID/network.ts similarity index 95% rename from src/locales/id-ID/pwa.ts rename to src/locales/id-ID/network.ts index 04bb4aa2..4dbf0bf3 100644 --- a/src/locales/id-ID/pwa.ts +++ b/src/locales/id-ID/network.ts @@ -9,6 +9,7 @@ export default { 'app.error.render.title': 'Terjadi kesalahan', 'app.error.render.description': 'Maaf, terjadi kesalahan pada halaman ini. Muat ulang halaman atau kembali ke beranda.', + 'app.error.retry': 'Coba Lagi', 'app.error.reload': 'Muat Ulang Halaman', 'app.error.home': 'Kembali ke Beranda', 'app.request.offline': diff --git a/src/locales/ja-JP.ts b/src/locales/ja-JP.ts index f0d7eb79..f654bb62 100644 --- a/src/locales/ja-JP.ts +++ b/src/locales/ja-JP.ts @@ -1,8 +1,8 @@ import component from './ja-JP/component'; import globalHeader from './ja-JP/globalHeader'; import menu from './ja-JP/menu'; +import network from './ja-JP/network'; import pages from './ja-JP/pages'; -import pwa from './ja-JP/pwa'; import settingDrawer from './ja-JP/settingDrawer'; import settings from './ja-JP/settings'; @@ -17,7 +17,7 @@ export default { ...menu, ...settingDrawer, ...settings, - ...pwa, + ...network, ...component, ...pages, }; diff --git a/src/locales/ja-JP/pwa.ts b/src/locales/ja-JP/network.ts similarity index 96% rename from src/locales/ja-JP/pwa.ts rename to src/locales/ja-JP/network.ts index c0afbff3..b683b0ac 100644 --- a/src/locales/ja-JP/pwa.ts +++ b/src/locales/ja-JP/network.ts @@ -9,6 +9,7 @@ export default { 'app.error.render.title': 'エラーが発生しました', 'app.error.render.description': '申し訳ありません、ページでエラーが発生しました。ページを再読み込みするか、ホームに戻ってください。', + 'app.error.retry': '再試行', 'app.error.reload': 'ページを再読み込み', 'app.error.home': 'ホームに戻る', 'app.request.offline': diff --git a/src/locales/pt-BR.ts b/src/locales/pt-BR.ts index d501a8a0..8cfec21b 100644 --- a/src/locales/pt-BR.ts +++ b/src/locales/pt-BR.ts @@ -1,8 +1,8 @@ import component from './pt-BR/component'; import globalHeader from './pt-BR/globalHeader'; import menu from './pt-BR/menu'; +import network from './pt-BR/network'; import pages from './pt-BR/pages'; -import pwa from './pt-BR/pwa'; import settingDrawer from './pt-BR/settingDrawer'; import settings from './pt-BR/settings'; @@ -16,7 +16,7 @@ export default { ...menu, ...settingDrawer, ...settings, - ...pwa, + ...network, ...component, ...pages, }; diff --git a/src/locales/pt-BR/pwa.ts b/src/locales/pt-BR/network.ts similarity index 95% rename from src/locales/pt-BR/pwa.ts rename to src/locales/pt-BR/network.ts index af2bb2ca..b93c100c 100644 --- a/src/locales/pt-BR/pwa.ts +++ b/src/locales/pt-BR/network.ts @@ -9,6 +9,7 @@ export default { 'app.error.render.title': 'Algo deu errado', 'app.error.render.description': 'Desculpe, ocorreu um erro nesta página. Atualize a página ou volte para a página inicial.', + 'app.error.retry': 'Tentar novamente', 'app.error.reload': 'Atualizar página', 'app.error.home': 'Voltar ao Início', 'app.request.offline': diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index ce2ccd18..fb50c713 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -1,8 +1,8 @@ import component from './zh-CN/component'; import globalHeader from './zh-CN/globalHeader'; import menu from './zh-CN/menu'; +import network from './zh-CN/network'; import pages from './zh-CN/pages'; -import pwa from './zh-CN/pwa'; import settingDrawer from './zh-CN/settingDrawer'; import settings from './zh-CN/settings'; @@ -17,6 +17,6 @@ export default { ...menu, ...settingDrawer, ...settings, - ...pwa, + ...network, ...component, }; diff --git a/src/locales/zh-CN/pwa.ts b/src/locales/zh-CN/network.ts similarity index 95% rename from src/locales/zh-CN/pwa.ts rename to src/locales/zh-CN/network.ts index 7da21ffb..5df92e31 100644 --- a/src/locales/zh-CN/pwa.ts +++ b/src/locales/zh-CN/network.ts @@ -7,6 +7,7 @@ export default { 'app.error.render.title': '页面出现错误', 'app.error.render.description': '抱歉,页面遇到了一些问题,请刷新页面或返回首页。', + 'app.error.retry': '重试', 'app.error.reload': '刷新页面', 'app.error.home': '返回首页', 'app.request.offline': '网络不可用,请检查网络连接后重试。', diff --git a/src/locales/zh-TW.ts b/src/locales/zh-TW.ts index 95a685af..e0be9a05 100644 --- a/src/locales/zh-TW.ts +++ b/src/locales/zh-TW.ts @@ -1,8 +1,8 @@ import component from './zh-TW/component'; import globalHeader from './zh-TW/globalHeader'; import menu from './zh-TW/menu'; +import network from './zh-TW/network'; import pages from './zh-TW/pages'; -import pwa from './zh-TW/pwa'; import settingDrawer from './zh-TW/settingDrawer'; import settings from './zh-TW/settings'; @@ -17,6 +17,6 @@ export default { ...menu, ...settingDrawer, ...settings, - ...pwa, + ...network, ...component, }; diff --git a/src/locales/zh-TW/pwa.ts b/src/locales/zh-TW/network.ts similarity index 95% rename from src/locales/zh-TW/pwa.ts rename to src/locales/zh-TW/network.ts index 6428ff38..ce1296d0 100644 --- a/src/locales/zh-TW/pwa.ts +++ b/src/locales/zh-TW/network.ts @@ -8,6 +8,7 @@ export default { 'app.error.render.title': '頁面出現錯誤', 'app.error.render.description': '抱歉,頁面遇到了一些問題,請重新整理頁面或返回首頁。', + 'app.error.retry': '重試', 'app.error.reload': '重新整理頁面', 'app.error.home': '返回首頁', 'app.request.offline': '網路不可用,請檢查網路連線後重試。',