From c4094c0185947340dffc64ae8b3aa8b833ee5b7d Mon Sep 17 00:00:00 2001 From: Alex Zhu Date: Wed, 6 May 2026 22:56:21 +0800 Subject: [PATCH] feat: offline-friendly error handling with chunk retry and network banner (#11756) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add offline-friendly error handling and network status awareness Add ErrorBoundary to catch render errors (including chunk load failures from React.lazy), replacing the previous white-screen crash with a friendly Result page. Add a network status model (useModel('network')) and OfflineBanner that shows when the user goes offline. Make request error messages offline-aware. Clean up dead PWA/ServiceWorker code in global.tsx that never fired. - ErrorBoundary: catches all render errors, distinguishes chunk load failures (offline vs online messages), uses antd Result with retry/home buttons - OfflineBanner: Alert banner in ProLayout content area when offline - network model: navigator.onLine + online/offline event listeners - requestErrorConfig: offline-aware error messages in errorHandler - global.tsx: remove dead sw.offline/sw.updated event listeners - defaultSettings.ts: remove unused pwa flag - locales: replace dead PWA i18n keys with network/error keys (8 locales) Co-Authored-By: Claude Opus 4.7 * fix: resolve intermittent "Can't resolve 'tailwindcss'" on Windows (#11747) Set globalThis.__tw_resolve in postcss.config.js to bypass enhanced-resolve's CachedInputFileSystem, which caches stale negative results on Windows (e.g. when Windows Defender scans node_modules). Also fix @source path from "./src" to "." since the directive is relative to the CSS file location. Co-Authored-By: Claude Opus 4.7 * fix: address PR review feedback for offline features - Use getIntl() with i18n key in requestErrorConfig instead of hardcoded English strings (matches CodeRabbit, Gemini, Copilot feedback) - Add SSR guard for navigator.onLine in requestErrorConfig (Gemini) - Replace wrapping + ), + , + , + ].filter(Boolean)} + /> + + ); +} + +interface ErrorBoundaryState { + hasError: boolean; + error: Error | null; + isOnline: boolean; + retryCount: number; +} + +export default class ErrorBoundary extends React.Component< + { children: React.ReactNode }, + ErrorBoundaryState +> { + state: ErrorBoundaryState = { + hasError: false, + error: null, + isOnline: typeof navigator !== 'undefined' ? navigator.onLine : true, + retryCount: 0, + }; + + static getDerivedStateFromError(error: Error): Partial { + return { hasError: true, error }; + } + + componentDidMount() { + window.addEventListener('online', this.handleOnline); + window.addEventListener('offline', this.handleOffline); + } + + componentWillUnmount() { + window.removeEventListener('online', this.handleOnline); + window.removeEventListener('offline', this.handleOffline); + } + + handleOnline = () => { + this.setState({ isOnline: true }); + if ( + this.state.hasError && + this.state.error && + isChunkLoadError(this.state.error) + ) { + window.location.reload(); + } + }; + + handleOffline = () => { + this.setState({ isOnline: false }); + }; + + componentDidCatch(error: Error, info: React.ErrorInfo) { + console.error('[ErrorBoundary]', error, info.componentStack); + } + + 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} + + ); + } + 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 new file mode 100644 index 00000000..391904bc --- /dev/null +++ b/src/components/OfflineBanner/index.tsx @@ -0,0 +1,45 @@ +import { getIntl } from '@umijs/max'; +import { Alert } from 'antd'; +import { useEffect, useState } from 'react'; + +const OfflineBanner: React.FC = () => { + const [isOnline, setIsOnline] = useState( + typeof navigator !== 'undefined' ? navigator.onLine : true, + ); + + useEffect(() => { + const handleOnline = () => setIsOnline(true); + const handleOffline = () => setIsOnline(false); + window.addEventListener('online', handleOnline); + window.addEventListener('offline', handleOffline); + return () => { + window.removeEventListener('online', handleOnline); + window.removeEventListener('offline', handleOffline); + }; + }, []); + + if (isOnline) return null; + + return ( + + ); +}; + +export default OfflineBanner; diff --git a/src/components/index.ts b/src/components/index.ts index 94092f55..b22709db 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -14,6 +14,8 @@ import { AvatarDropdown } from './RightContent/AvatarDropdown'; */ export { default as ArticleListContent } from './ArticleListContent'; export { default as AvatarList } from './AvatarList'; +export { default as ErrorBoundary } from './ErrorBoundary'; +export { default as OfflineBanner } from './OfflineBanner'; export { default as StandardFormRow } from './StandardFormRow'; export { default as TagSelect } from './TagSelect'; diff --git a/src/global.tsx b/src/global.tsx index 9c7ac379..195b0524 100644 --- a/src/global.tsx +++ b/src/global.tsx @@ -1,94 +1 @@ import './tailwind.css'; -import { useIntl } from '@umijs/max'; -import { Button, message, notification } from 'antd'; -import defaultSettings from '../config/defaultSettings'; - -const { pwa } = defaultSettings; -const isHttps = document.location.protocol === 'https:'; - -const clearCache = () => { - // remove all caches - if (window.caches) { - caches - .keys() - .then((keys) => { - keys.forEach((key) => { - caches.delete(key); - }); - }) - .catch((e) => console.log(e)); - } -}; - -// if pwa is true -if (pwa) { - // Notify user if offline now - window.addEventListener('sw.offline', () => { - message.warning(useIntl().formatMessage({ id: 'app.pwa.offline' })); - }); - - // Pop up a prompt on the page asking the user if they want to use the latest version - window.addEventListener('sw.updated', (event: Event) => { - const e = event as CustomEvent; - const reloadSW = async () => { - // Check if there is sw whose state is waiting in ServiceWorkerRegistration - // https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration - const worker = e.detail?.waiting; - if (!worker) { - return true; - } - // Send skip-waiting event to waiting SW with MessageChannel - await new Promise((resolve, reject) => { - const channel = new MessageChannel(); - channel.port1.onmessage = (msgEvent) => { - if (msgEvent.data.error) { - reject(msgEvent.data.error); - } else { - resolve(msgEvent.data); - } - }; - worker.postMessage({ type: 'skip-waiting' }, [channel.port2]); - }); - - clearCache(); - window.location.reload(); - return true; - }; - const key = `open${Date.now()}`; - const btn = ( - - ); - notification.open({ - title: useIntl().formatMessage({ id: 'app.pwa.serviceworker.updated' }), - description: useIntl().formatMessage({ - id: 'app.pwa.serviceworker.updated.hint', - }), - btn, - key, - onClose: async () => null, - }); - }); -} else if ('serviceWorker' in navigator && isHttps) { - // unregister service worker - const { serviceWorker } = navigator; - if (serviceWorker.getRegistrations) { - serviceWorker.getRegistrations().then((sws) => { - sws.forEach((sw) => { - sw.unregister(); - }); - }); - } - serviceWorker.getRegistration().then((sw) => { - if (sw) sw.unregister(); - }); - - clearCache(); -} 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/network.ts b/src/locales/bn-BD/network.ts new file mode 100644 index 00000000..b98f7c37 --- /dev/null +++ b/src/locales/bn-BD/network.ts @@ -0,0 +1,16 @@ +export default { + 'app.network.offline': 'আপনি এখন অফলাইন। কিছু বৈশিষ্ট্য অনুপলব্ধ হতে পারে।', + 'app.error.chunk.title': 'পৃষ্ঠা লোড করতে ব্যর্থ', + 'app.error.chunk.description.offline': + 'আপনার নেটওয়ার্ক সংযোগ বিচ্ছিন্ন হয়েছে। আপনার সংযোগ পরীক্ষা করুন এবং পৃষ্ঠাটি রিফ্রেশ করুন।', + 'app.error.chunk.description.online': + 'পৃষ্ঠা সম্পদ লোড করতে ব্যর্থ। দয়া করে রিফ্রেশ করুন এবং আবার চেষ্টা করুন।', + '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/bn-BD/pwa.ts b/src/locales/bn-BD/pwa.ts deleted file mode 100644 index 233fb305..00000000 --- a/src/locales/bn-BD/pwa.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default { - 'app.pwa.offline': 'আপনি এখন অফলাইন', - 'app.pwa.serviceworker.updated': 'নতুন সামগ্রী উপলব্ধ', - 'app.pwa.serviceworker.updated.hint': - 'বর্তমান পৃষ্ঠাটি পুনরায় লোড করতে দয়া করে "রিফ্রেশ" বোতাম টিপুন', - 'app.pwa.serviceworker.updated.ok': 'রিফ্রেশ', -}; 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/network.ts b/src/locales/en-US/network.ts new file mode 100644 index 00000000..9a0fd432 --- /dev/null +++ b/src/locales/en-US/network.ts @@ -0,0 +1,17 @@ +export default { + 'app.network.offline': + 'You are currently offline. Some features may be unavailable.', + 'app.error.chunk.title': 'Failed to load page', + 'app.error.chunk.description.offline': + 'Your network connection has been lost. Please check your connection and reload.', + 'app.error.chunk.description.online': + 'Page resources failed to load. Please reload and try again.', + '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': + 'Network unavailable. Please check your connection and try again.', +}; diff --git a/src/locales/en-US/pwa.ts b/src/locales/en-US/pwa.ts deleted file mode 100644 index 1256c026..00000000 --- a/src/locales/en-US/pwa.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default { - 'app.pwa.offline': 'You are offline now', - 'app.pwa.serviceworker.updated': 'New content is available', - 'app.pwa.serviceworker.updated.hint': - 'Please press the "Refresh" button to reload current page', - 'app.pwa.serviceworker.updated.ok': 'Refresh', -}; 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/network.ts b/src/locales/fa-IR/network.ts new file mode 100644 index 00000000..4f9a8baa --- /dev/null +++ b/src/locales/fa-IR/network.ts @@ -0,0 +1,17 @@ +export default { + 'app.network.offline': + 'شما اکنون آفلاین هستید. برخی از قابلیت‌ها ممکن است در دسترس نباشند.', + 'app.error.chunk.title': 'بارگذاری صفحه ناموفق بود', + 'app.error.chunk.description.offline': + 'اتصال شبکه شما قطع شده است. اتصال خود را بررسی کنید و صفحه را بارگذاری مجدد کنید.', + 'app.error.chunk.description.online': + 'بارگذاری منابع صفحه ناموفق بود. لطفاً صفحه را بارگذاری مجدد کنید.', + '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/fa-IR/pwa.ts b/src/locales/fa-IR/pwa.ts deleted file mode 100644 index 54831b4c..00000000 --- a/src/locales/fa-IR/pwa.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default { - 'app.pwa.offline': 'شما اکنون آفلاین هستید', - 'app.pwa.serviceworker.updated': 'مطالب جدید در دسترس است', - 'app.pwa.serviceworker.updated.hint': - 'لطفاً برای بارگیری مجدد صفحه فعلی ، دکمه "تازه سازی" را فشار دهید', - 'app.pwa.serviceworker.updated.ok': 'تازه سازی', -}; 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/network.ts b/src/locales/id-ID/network.ts new file mode 100644 index 00000000..4dbf0bf3 --- /dev/null +++ b/src/locales/id-ID/network.ts @@ -0,0 +1,17 @@ +export default { + 'app.network.offline': + 'Anda sedang offline. Beberapa fitur mungkin tidak tersedia.', + 'app.error.chunk.title': 'Gagal memuat halaman', + 'app.error.chunk.description.offline': + 'Koneksi jaringan terputus. Periksa koneksi Anda dan muat ulang halaman.', + 'app.error.chunk.description.online': + 'Gagal memuat sumber daya halaman. Muat ulang dan coba lagi.', + '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': + 'Jaringan tidak tersedia. Periksa koneksi Anda dan coba lagi.', +}; diff --git a/src/locales/id-ID/pwa.ts b/src/locales/id-ID/pwa.ts deleted file mode 100644 index b2cb8a12..00000000 --- a/src/locales/id-ID/pwa.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default { - 'app.pwa.offline': 'Koneksi anda terputus', - 'app.pwa.serviceworker.updated': 'Konten baru sudah tersedia', - 'app.pwa.serviceworker.updated.hint': - 'Silahkan klik tombol "Refresh" untuk memuat ulang halaman ini', - 'app.pwa.serviceworker.updated.ok': 'Memuat ulang', -}; 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/network.ts b/src/locales/ja-JP/network.ts new file mode 100644 index 00000000..b683b0ac --- /dev/null +++ b/src/locales/ja-JP/network.ts @@ -0,0 +1,17 @@ +export default { + 'app.network.offline': + '現在オフラインです。一部の機能が利用できない場合があります', + 'app.error.chunk.title': 'ページの読み込みに失敗しました', + 'app.error.chunk.description.offline': + 'ネットワーク接続が切断されています。接続を確認してページを再読み込みしてください。', + 'app.error.chunk.description.online': + 'ページリソースの読み込みに失敗しました。ページを再読み込みしてください。', + '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/ja-JP/pwa.ts b/src/locales/ja-JP/pwa.ts deleted file mode 100644 index ace23aef..00000000 --- a/src/locales/ja-JP/pwa.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default { - 'app.pwa.offline': 'あなたは今オフラインです', - 'app.pwa.serviceworker.updated': '新しいコンテンツが利用可能です', - 'app.pwa.serviceworker.updated.hint': - '現在のページをリロードするには、「更新」ボタンを押してください', - 'app.pwa.serviceworker.updated.ok': 'リフレッシュ', -}; 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/network.ts b/src/locales/pt-BR/network.ts new file mode 100644 index 00000000..b93c100c --- /dev/null +++ b/src/locales/pt-BR/network.ts @@ -0,0 +1,17 @@ +export default { + 'app.network.offline': + 'Você está offline no momento. Alguns recursos podem estar indisponíveis.', + 'app.error.chunk.title': 'Falha ao carregar a página', + 'app.error.chunk.description.offline': + 'Sua conexão de rede foi perdida. Verifique sua conexão e atualize a página.', + 'app.error.chunk.description.online': + 'Falha ao carregar os recursos da página. Atualize e tente novamente.', + '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': + 'Rede indisponível. Verifique sua conexão e tente novamente.', +}; diff --git a/src/locales/pt-BR/pwa.ts b/src/locales/pt-BR/pwa.ts deleted file mode 100644 index 05cc7978..00000000 --- a/src/locales/pt-BR/pwa.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default { - 'app.pwa.offline': 'Você está offline agora', - 'app.pwa.serviceworker.updated': 'Novo conteúdo está disponível', - 'app.pwa.serviceworker.updated.hint': - 'Por favor, pressione o botão "Atualizar" para recarregar a página atual', - 'app.pwa.serviceworker.updated.ok': 'Atualizar', -}; 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/network.ts b/src/locales/zh-CN/network.ts new file mode 100644 index 00000000..5df92e31 --- /dev/null +++ b/src/locales/zh-CN/network.ts @@ -0,0 +1,14 @@ +export default { + 'app.network.offline': '当前处于离线状态,部分功能可能不可用', + 'app.error.chunk.title': '页面加载失败', + 'app.error.chunk.description.offline': + '网络连接已断开,请检查网络后重新加载。', + 'app.error.chunk.description.online': '页面资源加载失败,请重新加载重试。', + '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-CN/pwa.ts b/src/locales/zh-CN/pwa.ts deleted file mode 100644 index e9504849..00000000 --- a/src/locales/zh-CN/pwa.ts +++ /dev/null @@ -1,6 +0,0 @@ -export default { - 'app.pwa.offline': '当前处于离线状态', - 'app.pwa.serviceworker.updated': '有新内容', - 'app.pwa.serviceworker.updated.hint': '请点击“刷新”按钮或者手动刷新页面', - 'app.pwa.serviceworker.updated.ok': '刷新', -}; 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/network.ts b/src/locales/zh-TW/network.ts new file mode 100644 index 00000000..ce1296d0 --- /dev/null +++ b/src/locales/zh-TW/network.ts @@ -0,0 +1,15 @@ +export default { + 'app.network.offline': '當前處於離線狀態,部分功能可能不可用', + 'app.error.chunk.title': '頁面載入失敗', + 'app.error.chunk.description.offline': + '網路連線已中斷,請檢查網路後重新整理頁面。', + 'app.error.chunk.description.online': + '頁面資源載入失敗,請重新整理頁面重試。', + '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/pwa.ts b/src/locales/zh-TW/pwa.ts deleted file mode 100644 index 108a6e48..00000000 --- a/src/locales/zh-TW/pwa.ts +++ /dev/null @@ -1,6 +0,0 @@ -export default { - 'app.pwa.offline': '當前處於離線狀態', - 'app.pwa.serviceworker.updated': '有新內容', - 'app.pwa.serviceworker.updated.hint': '請點擊“刷新”按鈕或者手動刷新頁面', - 'app.pwa.serviceworker.updated.ok': '刷新', -}; diff --git a/src/requestErrorConfig.ts b/src/requestErrorConfig.ts index 56071223..e1736b97 100644 --- a/src/requestErrorConfig.ts +++ b/src/requestErrorConfig.ts @@ -1,5 +1,6 @@ import type { RequestOptions } from '@@/plugin-request/request'; import type { RequestConfig } from '@umijs/max'; +import { getIntl } from '@umijs/max'; import { message, notification } from 'antd'; // 错误处理方案: 错误类型 @@ -73,13 +74,17 @@ export const errorConfig: RequestConfig = { // Axios 的错误 // 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围 message.error(`Response status:${error.response.status}`); + } else if (typeof navigator !== 'undefined' && !navigator.onLine) { + message.error( + getIntl().formatMessage({ + id: 'app.request.offline', + defaultMessage: + 'Network unavailable. Please check your connection and try again.', + }), + ); } else if (error.request) { - // 请求已经成功发起,但没有收到响应 - // \`error.request\` 在浏览器中是 XMLHttpRequest 的实例, - // 而在node.js中是 http.ClientRequest 的实例 message.error('None response! Please retry.'); } else { - // 发送请求时出了点问题 message.error('Request error, please retry.'); } },