committed by
GitHub
31 changed files with 377 additions and 169 deletions
@ -0,0 +1,161 @@ |
|||
import { getIntl } from '@umijs/max'; |
|||
import { Button, Card, Result } from 'antd'; |
|||
import React from 'react'; |
|||
|
|||
function isChunkLoadError(error: Error): boolean { |
|||
return ( |
|||
error.name === 'ChunkLoadError' || |
|||
/(?:loading|failed to load) (?:css )?chunk/i.test(error.message) || |
|||
/Failed to fetch dynamically imported module/i.test(error.message) |
|||
); |
|||
} |
|||
|
|||
function getSubTitleId(isChunkError: boolean, isOffline: boolean): string { |
|||
if (!isChunkError) return 'app.error.render.description'; |
|||
return isOffline |
|||
? 'app.error.chunk.description.offline' |
|||
: 'app.error.chunk.description.online'; |
|||
} |
|||
|
|||
function renderErrorFallback( |
|||
error: Error, |
|||
isOnline: boolean, |
|||
onRetry: () => void, |
|||
onReload: () => void, |
|||
) { |
|||
const intl = getIntl(); |
|||
const isOffline = !isOnline; |
|||
const isChunkError = isChunkLoadError(error); |
|||
|
|||
return ( |
|||
<Card variant="borderless" style={{ margin: 24 }}> |
|||
<Result |
|||
status="error" |
|||
title={intl.formatMessage({ |
|||
id: isChunkError ? 'app.error.chunk.title' : 'app.error.render.title', |
|||
defaultMessage: isChunkError |
|||
? 'Failed to load page' |
|||
: 'Something went wrong', |
|||
})} |
|||
subTitle={intl.formatMessage({ |
|||
id: getSubTitleId(isChunkError, isOffline), |
|||
defaultMessage: |
|||
isChunkError && isOffline |
|||
? 'Your network connection has been lost. Please check your connection and reload.' |
|||
: isChunkError |
|||
? 'Page resources failed to load. Please reload and try again.' |
|||
: 'Sorry, an error occurred on this page. Please reload or go back to the home page.', |
|||
})} |
|||
extra={[ |
|||
isChunkError && ( |
|||
<Button type="primary" key="retry" onClick={onRetry}> |
|||
{intl.formatMessage({ |
|||
id: 'app.error.retry', |
|||
defaultMessage: 'Retry', |
|||
})} |
|||
</Button> |
|||
), |
|||
<Button |
|||
type={isChunkError ? 'default' : 'primary'} |
|||
key="reload" |
|||
onClick={onReload} |
|||
> |
|||
{intl.formatMessage({ |
|||
id: 'app.error.reload', |
|||
defaultMessage: 'Reload Page', |
|||
})} |
|||
</Button>, |
|||
<Button href="/" key="home"> |
|||
{intl.formatMessage({ |
|||
id: 'app.error.home', |
|||
defaultMessage: 'Back Home', |
|||
})} |
|||
</Button>, |
|||
].filter(Boolean)} |
|||
/> |
|||
</Card> |
|||
); |
|||
} |
|||
|
|||
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<ErrorBoundaryState> { |
|||
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 ( |
|||
<React.Fragment key={this.state.retryCount}> |
|||
{this.props.children} |
|||
</React.Fragment> |
|||
); |
|||
} |
|||
return renderErrorFallback( |
|||
this.state.error, |
|||
this.state.isOnline, |
|||
this.handleRetry, |
|||
this.handleReload, |
|||
); |
|||
} |
|||
} |
|||
@ -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 ( |
|||
<Alert |
|||
type="warning" |
|||
showIcon |
|||
closable={false} |
|||
style={{ |
|||
position: 'fixed', |
|||
top: 8, |
|||
left: '50%', |
|||
transform: 'translateX(-50%)', |
|||
zIndex: 999, |
|||
maxWidth: 480, |
|||
}} |
|||
message={getIntl().formatMessage({ |
|||
id: 'app.network.offline', |
|||
defaultMessage: |
|||
'You are currently offline. Some features may be unavailable.', |
|||
})} |
|||
/> |
|||
); |
|||
}; |
|||
|
|||
export default OfflineBanner; |
|||
@ -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': |
|||
'নেটওয়ার্ক অনুপলব্ধ। আপনার সংযোগ পরীক্ষা করুন এবং আবার চেষ্টা করুন।', |
|||
}; |
|||
@ -1,7 +0,0 @@ |
|||
export default { |
|||
'app.pwa.offline': 'আপনি এখন অফলাইন', |
|||
'app.pwa.serviceworker.updated': 'নতুন সামগ্রী উপলব্ধ', |
|||
'app.pwa.serviceworker.updated.hint': |
|||
'বর্তমান পৃষ্ঠাটি পুনরায় লোড করতে দয়া করে "রিফ্রেশ" বোতাম টিপুন', |
|||
'app.pwa.serviceworker.updated.ok': 'রিফ্রেশ', |
|||
}; |
|||
@ -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.', |
|||
}; |
|||
@ -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', |
|||
}; |
|||
@ -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': |
|||
'شبکه در دسترس نیست. لطفاً اتصال خود را بررسی و دوباره تلاش کنید.', |
|||
}; |
|||
@ -1,7 +0,0 @@ |
|||
export default { |
|||
'app.pwa.offline': 'شما اکنون آفلاین هستید', |
|||
'app.pwa.serviceworker.updated': 'مطالب جدید در دسترس است', |
|||
'app.pwa.serviceworker.updated.hint': |
|||
'لطفاً برای بارگیری مجدد صفحه فعلی ، دکمه "تازه سازی" را فشار دهید', |
|||
'app.pwa.serviceworker.updated.ok': 'تازه سازی', |
|||
}; |
|||
@ -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.', |
|||
}; |
|||
@ -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', |
|||
}; |
|||
@ -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': |
|||
'ネットワークに接続できません。接続を確認して再試行してください。', |
|||
}; |
|||
@ -1,7 +0,0 @@ |
|||
export default { |
|||
'app.pwa.offline': 'あなたは今オフラインです', |
|||
'app.pwa.serviceworker.updated': '新しいコンテンツが利用可能です', |
|||
'app.pwa.serviceworker.updated.hint': |
|||
'現在のページをリロードするには、「更新」ボタンを押してください', |
|||
'app.pwa.serviceworker.updated.ok': 'リフレッシュ', |
|||
}; |
|||
@ -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.', |
|||
}; |
|||
@ -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', |
|||
}; |
|||
@ -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': '网络不可用,请检查网络连接后重试。', |
|||
}; |
|||
@ -1,6 +0,0 @@ |
|||
export default { |
|||
'app.pwa.offline': '当前处于离线状态', |
|||
'app.pwa.serviceworker.updated': '有新内容', |
|||
'app.pwa.serviceworker.updated.hint': '请点击“刷新”按钮或者手动刷新页面', |
|||
'app.pwa.serviceworker.updated.ok': '刷新', |
|||
}; |
|||
@ -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': '網路不可用,請檢查網路連線後重試。', |
|||
}; |
|||
@ -1,6 +0,0 @@ |
|||
export default { |
|||
'app.pwa.offline': '當前處於離線狀態', |
|||
'app.pwa.serviceworker.updated': '有新內容', |
|||
'app.pwa.serviceworker.updated.hint': '請點擊“刷新”按鈕或者手動刷新頁面', |
|||
'app.pwa.serviceworker.updated.ok': '刷新', |
|||
}; |
|||
Loading…
Reference in new issue