diff --git a/src/components/proxy/proxy-empty-state-model.ts b/src/components/proxy/proxy-empty-state-model.ts
new file mode 100644
index 000000000..16c64708c
--- /dev/null
+++ b/src/components/proxy/proxy-empty-state-model.ts
@@ -0,0 +1,88 @@
+import type { ProxyViewV1 } from '@/types/proxy-view'
+
+export type ProxyEmptyStateReason =
+ | 'no-subscriptions'
+ | 'inactive-subscription'
+ | 'core-unavailable'
+ | 'no-proxy-info'
+
+type ProfileSummary = {
+ current?: string
+ items?: Array<{
+ uid?: string
+ type?: string
+ }>
+}
+
+interface ProxyEmptyStateInput {
+ mode: string
+ isChainMode: boolean
+ profiles?: ProfileSummary
+ isProfilesPending: boolean
+ proxyView?: ProxyViewV1
+ isProxyViewPending: boolean
+ isProxyViewError: boolean
+ runningMode?: string
+ isRunningModePending: boolean
+}
+
+const isSubscriptionProfile = (item: { type?: string }): boolean =>
+ item.type === 'remote' || item.type === 'local'
+
+export const hasRenderableProxyContent = (
+ proxyView: ProxyViewV1 | undefined,
+ mode: string,
+ isChainMode: boolean,
+) => {
+ if (!proxyView) return false
+
+ if (isChainMode) {
+ return proxyView.groups.some(
+ (group) => group.type === 'Selector' || group.type === 'URLTest',
+ )
+ }
+
+ if (mode === 'rule' || mode === 'script') {
+ return proxyView.groups.some((group) => !group.hidden)
+ }
+
+ return proxyView.global !== null
+}
+
+export const resolveProxyEmptyState = ({
+ mode,
+ isChainMode,
+ profiles,
+ isProfilesPending,
+ proxyView,
+ isProxyViewPending,
+ isProxyViewError,
+ runningMode,
+ isRunningModePending,
+}: ProxyEmptyStateInput): ProxyEmptyStateReason | 'loading' | null => {
+ if (mode === 'direct') return null
+
+ if (isProfilesPending || isProxyViewPending || isRunningModePending) {
+ return 'loading'
+ }
+
+ if (profiles) {
+ const subscriptions = (profiles.items ?? []).filter(isSubscriptionProfile)
+ if (subscriptions.length === 0) return 'no-subscriptions'
+
+ const hasActiveSubscription = subscriptions.some(
+ (item) => item.uid === profiles.current,
+ )
+ if (!hasActiveSubscription) return 'inactive-subscription'
+ }
+
+ if (hasRenderableProxyContent(proxyView, mode, isChainMode)) {
+ return null
+ }
+
+ if (runningMode === 'NotRunning' || isProxyViewError) {
+ return 'core-unavailable'
+ }
+
+ return 'no-proxy-info'
+}
diff --git a/src/components/proxy/proxy-empty-state.tsx b/src/components/proxy/proxy-empty-state.tsx
new file mode 100644
index 000000000..52bf1f41b
--- /dev/null
+++ b/src/components/proxy/proxy-empty-state.tsx
@@ -0,0 +1,118 @@
+import { Alert, AlertTitle, Box, Button, Stack } from '@mui/material'
+import { useLockFn } from 'ahooks'
+import { useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import { useNavigate } from 'react-router'
+
+import { useAppRefreshers } from '@/providers/app-data-context'
+import { openLogsDir, restartCore } from '@/services/cmds'
+import { showNotice } from '@/services/notice-service'
+import { revalidateQuery } from '@/services/query-client'
+
+import type { ProxyEmptyStateReason } from './proxy-empty-state-model'
+
+interface Props {
+ reason: ProxyEmptyStateReason
+}
+
+export const ProxyEmptyState = ({ reason }: Props) => {
+ const { t } = useTranslation()
+ const navigate = useNavigate()
+ const { refreshProxy } = useAppRefreshers()
+ const [isRestarting, setIsRestarting] = useState(false)
+
+ const handleRestart = useLockFn(async () => {
+ setIsRestarting(true)
+ try {
+ await restartCore()
+ await Promise.all([refreshProxy(), revalidateQuery(['getRunningMode'])])
+ } catch (error) {
+ showNotice.error(error)
+ } finally {
+ setIsRestarting(false)
+ }
+ })
+
+ const title =
+ reason === 'no-subscriptions'
+ ? t('proxies.page.empty.noSubscriptions.title')
+ : reason === 'inactive-subscription'
+ ? t('proxies.page.empty.inactiveSubscription.title')
+ : reason === 'core-unavailable'
+ ? t('proxies.page.empty.coreUnavailable.title')
+ : t('proxies.page.empty.noProxyInfo.title')
+ const description =
+ reason === 'no-subscriptions'
+ ? t('proxies.page.empty.noSubscriptions.description')
+ : reason === 'inactive-subscription'
+ ? t('proxies.page.empty.inactiveSubscription.description')
+ : reason === 'core-unavailable'
+ ? t('proxies.page.empty.coreUnavailable.description')
+ : t('proxies.page.empty.noProxyInfo.description')
+ const showProfiles =
+ reason === 'no-subscriptions' ||
+ reason === 'inactive-subscription' ||
+ reason === 'no-proxy-info'
+ const showCoreActions =
+ reason === 'core-unavailable' || reason === 'no-proxy-info'
+
+ return (
+
+
+ {title}
+ {description}
+
+
+ {showProfiles && (
+
+ )}
+
+ {showCoreActions && (
+ <>
+
+
+ >
+ )}
+
+
+
+ )
+}
diff --git a/src/components/proxy/proxy-groups.tsx b/src/components/proxy/proxy-groups.tsx
index 4b51a1b8b..b1d2bcfd3 100644
--- a/src/components/proxy/proxy-groups.tsx
+++ b/src/components/proxy/proxy-groups.tsx
@@ -18,9 +18,10 @@ import {
StickyVirtualList,
type StickyVirtualListHandle,
} from '@/components/base'
+import { useProfiles } from '@/hooks/use-profiles'
import { useProxySelection } from '@/hooks/use-proxy-selection'
import { useVerge } from '@/hooks/use-verge'
-import { useProxiesData } from '@/providers/app-data-context'
+import { useProxiesData, useSystemData } from '@/providers/app-data-context'
import delayManager from '@/services/delay'
import {
isInteractableMember,
@@ -30,6 +31,8 @@ import {
} from '@/types/proxy-view'
import { debugLog } from '@/utils/debug'
+import { ProxyEmptyState } from './proxy-empty-state'
+import { resolveProxyEmptyState } from './proxy-empty-state-model'
import {
DEFAULT_HOVER_DELAY,
ProxyGroupNavigator,
@@ -598,11 +601,34 @@ function NormalProxyGroups(props: { mode: string }) {
export const ProxyGroups = (props: Props) => {
const { mode, isChainMode = false, chainConfigData } = props
+ const { profiles, isLoading: isProfilesLoading } = useProfiles()
+ const { proxyView, isProxyViewPending, isProxyViewError } = useProxiesData()
+ const { runningMode, isRunningModePending } = useSystemData()
+
+ const emptyState = resolveProxyEmptyState({
+ mode,
+ isChainMode,
+ profiles,
+ isProfilesPending: !profiles && isProfilesLoading,
+ proxyView,
+ isProxyViewPending,
+ isProxyViewError,
+ runningMode,
+ isRunningModePending,
+ })
if (mode === 'direct') {
return
}
+ if (emptyState === 'loading') {
+ return
+ }
+
+ if (emptyState) {
+ return
+ }
+
if (isChainMode) {
return
}
diff --git a/src/locales/ar/proxies.json b/src/locales/ar/proxies.json
index ae7eb24c4..cebbe12a8 100644
--- a/src/locales/ar/proxies.json
+++ b/src/locales/ar/proxies.json
@@ -44,7 +44,28 @@
},
"empty": {
"noAvailableGroups": "لا توجد مجموعات وكيل متاحة",
- "noProxies": "لا توجد وكلاء"
+ "noProxies": "لا توجد وكلاء",
+ "noSubscriptions": {
+ "title": "لم يتم استيراد أي اشتراك",
+ "description": "لا يوجد محتوى لعرضه في صفحة الوكلاء. انتقل إلى صفحة «الملفات الشخصية» لاستيراد اشتراك وتفعيله."
+ },
+ "inactiveSubscription": {
+ "title": "لا يوجد اشتراك مفعّل",
+ "description": "تم العثور على اشتراكات، لكن لا يوجد أي اشتراك مفعّل حاليًا. انتقل إلى صفحة «الملفات الشخصية» واختر اشتراكًا لتفعيله."
+ },
+ "coreUnavailable": {
+ "title": "النواة غير متاحة مؤقتًا",
+ "description": "تعذر الحصول على معلومات الوكلاء من النواة. حاول إعادة تشغيل النواة. إذا استمرت المشكلة، افتح مجلد السجلات وتحقق من سجلات النواة."
+ },
+ "noProxyInfo": {
+ "title": "معلومات الوكلاء غير متاحة",
+ "description": "لا توجد مجموعات أو عقد وكلاء لعرضها. تحقق من أن الاشتراك المفعّل يحتوي على مجموعات وعقد صالحة، أو أعد تشغيل النواة وراجع السجلات."
+ },
+ "actions": {
+ "openProfiles": "الانتقال إلى الملفات الشخصية",
+ "restartCore": "إعادة تشغيل النواة",
+ "openLogs": "فتح مجلد السجلات"
+ }
},
"chain": {
"header": "Chain Proxy Config",
diff --git a/src/locales/de/proxies.json b/src/locales/de/proxies.json
index f71289018..cca58c8a9 100644
--- a/src/locales/de/proxies.json
+++ b/src/locales/de/proxies.json
@@ -44,7 +44,28 @@
},
"empty": {
"noAvailableGroups": "Keine Proxy-Gruppen verfügbar",
- "noProxies": "Keine Proxys"
+ "noProxies": "Keine Proxys",
+ "noSubscriptions": {
+ "title": "Kein Abonnement importiert",
+ "description": "Auf der Proxy-Seite gibt es nichts anzuzeigen. Öffnen Sie „Abonnement“, um ein Abonnement zu importieren und zu aktivieren."
+ },
+ "inactiveSubscription": {
+ "title": "Kein aktives Abonnement",
+ "description": "Es wurden Abonnements gefunden, aber keines ist derzeit aktiv. Öffnen Sie „Abonnement“, wählen Sie eines aus und aktivieren Sie es."
+ },
+ "coreUnavailable": {
+ "title": "Kern vorübergehend nicht verfügbar",
+ "description": "Die Proxy-Informationen konnten nicht vom Kern abgerufen werden. Starten Sie den Kern neu. Falls das Problem weiterhin besteht, öffnen Sie den Protokollordner und prüfen Sie die Kernprotokolle."
+ },
+ "noProxyInfo": {
+ "title": "Keine Proxy-Informationen verfügbar",
+ "description": "Es gibt keine Proxy-Gruppen oder -Knoten zum Anzeigen. Prüfen Sie, ob das aktive Abonnement gültige Proxy-Gruppen und -Knoten enthält, oder starten Sie den Kern neu und prüfen Sie die Protokolle."
+ },
+ "actions": {
+ "openProfiles": "Zum Abonnement",
+ "restartCore": "Kern neu starten",
+ "openLogs": "Protokollordner öffnen"
+ }
},
"chain": {
"header": "Chain Proxy Config",
diff --git a/src/locales/en/proxies.json b/src/locales/en/proxies.json
index 0d6a8f36f..f54974713 100644
--- a/src/locales/en/proxies.json
+++ b/src/locales/en/proxies.json
@@ -44,7 +44,28 @@
},
"empty": {
"noAvailableGroups": "No proxy groups are available",
- "noProxies": "No proxies"
+ "noProxies": "No proxies",
+ "noSubscriptions": {
+ "title": "No subscription imported",
+ "description": "There is nothing to display on the Proxies page. Go to Profiles to import and activate a subscription."
+ },
+ "inactiveSubscription": {
+ "title": "No active subscription",
+ "description": "Subscriptions were found, but none is currently active. Go to Profiles and select one to activate."
+ },
+ "coreUnavailable": {
+ "title": "Core temporarily unavailable",
+ "description": "Proxy information could not be retrieved from the core. Try restarting the core. If the problem persists, open the logs folder and check the core logs."
+ },
+ "noProxyInfo": {
+ "title": "No proxy information available",
+ "description": "There are no proxy groups or nodes to display. Check that the active subscription contains valid proxy groups and nodes, or restart the core and review the logs."
+ },
+ "actions": {
+ "openProfiles": "Go to Profiles",
+ "restartCore": "Restart Core",
+ "openLogs": "Open Logs Folder"
+ }
},
"chain": {
"header": "Chain Proxy Config",
diff --git a/src/locales/es/proxies.json b/src/locales/es/proxies.json
index e7eb9bc4b..ea02c26b1 100644
--- a/src/locales/es/proxies.json
+++ b/src/locales/es/proxies.json
@@ -44,7 +44,28 @@
},
"empty": {
"noAvailableGroups": "No hay grupos de proxy disponibles",
- "noProxies": "Sin proxies"
+ "noProxies": "Sin proxies",
+ "noSubscriptions": {
+ "title": "No se ha importado ninguna suscripción",
+ "description": "No hay contenido para mostrar en la página de proxies. Ve a Perfiles para importar y activar una suscripción."
+ },
+ "inactiveSubscription": {
+ "title": "No hay ninguna suscripción activa",
+ "description": "Se encontraron suscripciones, pero ninguna está activa. Ve a Perfiles, selecciona una suscripción y actívala."
+ },
+ "coreUnavailable": {
+ "title": "El núcleo no está disponible temporalmente",
+ "description": "No se pudo obtener la información de proxies del núcleo. Intenta reiniciar el núcleo. Si el problema continúa, abre la carpeta de registros y revisa los registros del núcleo."
+ },
+ "noProxyInfo": {
+ "title": "No hay información de proxies disponible",
+ "description": "No hay grupos ni nodos de proxy para mostrar. Comprueba que la suscripción activa contenga grupos y nodos válidos, o reinicia el núcleo y revisa los registros."
+ },
+ "actions": {
+ "openProfiles": "Ir a Perfiles",
+ "restartCore": "Reiniciar núcleo",
+ "openLogs": "Abrir carpeta de registros"
+ }
},
"chain": {
"header": "Chain Proxy Config",
diff --git a/src/locales/fa/proxies.json b/src/locales/fa/proxies.json
index ee5e4c2f6..3b9185f0f 100644
--- a/src/locales/fa/proxies.json
+++ b/src/locales/fa/proxies.json
@@ -44,7 +44,28 @@
},
"empty": {
"noAvailableGroups": "هیچ گروه پروکسی در دسترس نیست",
- "noProxies": "پروکسیای وجود ندارد"
+ "noProxies": "پروکسیای وجود ندارد",
+ "noSubscriptions": {
+ "title": "هیچ اشتراکی وارد نشده است",
+ "description": "چیزی برای نمایش در صفحه پروکسی وجود ندارد. برای وارد کردن و فعالسازی اشتراک به صفحه «پروفایلها» بروید."
+ },
+ "inactiveSubscription": {
+ "title": "هیچ اشتراک فعالی وجود ندارد",
+ "description": "اشتراکهایی پیدا شدند، اما در حال حاضر هیچکدام فعال نیستند. به صفحه «پروفایلها» بروید، یک اشتراک را انتخاب و فعال کنید."
+ },
+ "coreUnavailable": {
+ "title": "هسته موقتاً در دسترس نیست",
+ "description": "دریافت اطلاعات پروکسی از هسته ممکن نشد. هسته را دوباره راهاندازی کنید. اگر مشکل ادامه داشت، پوشه گزارشها را باز کرده و گزارشهای هسته را بررسی کنید."
+ },
+ "noProxyInfo": {
+ "title": "اطلاعات پروکسی در دسترس نیست",
+ "description": "هیچ گروه یا گره پروکسی برای نمایش وجود ندارد. بررسی کنید که اشتراک فعال دارای گروهها و گرههای معتبر باشد، یا هسته را دوباره راهاندازی کرده و گزارشها را ببینید."
+ },
+ "actions": {
+ "openProfiles": "رفتن به پروفایلها",
+ "restartCore": "راهاندازی مجدد هسته",
+ "openLogs": "باز کردن پوشه گزارشها"
+ }
},
"chain": {
"header": "Chain Proxy Config",
diff --git a/src/locales/id/proxies.json b/src/locales/id/proxies.json
index 473984537..869c0768c 100644
--- a/src/locales/id/proxies.json
+++ b/src/locales/id/proxies.json
@@ -44,7 +44,28 @@
},
"empty": {
"noAvailableGroups": "Tidak ada grup proxy yang tersedia",
- "noProxies": "Tidak ada proxy"
+ "noProxies": "Tidak ada proxy",
+ "noSubscriptions": {
+ "title": "Belum ada langganan yang diimpor",
+ "description": "Tidak ada yang dapat ditampilkan di halaman Proksi. Buka halaman Profil untuk mengimpor dan mengaktifkan langganan."
+ },
+ "inactiveSubscription": {
+ "title": "Tidak ada langganan aktif",
+ "description": "Langganan ditemukan, tetapi tidak ada yang sedang aktif. Buka halaman Profil, lalu pilih dan aktifkan langganan."
+ },
+ "coreUnavailable": {
+ "title": "Inti sementara tidak tersedia",
+ "description": "Informasi proksi tidak dapat diambil dari inti. Coba mulai ulang inti. Jika masalah berlanjut, buka folder log dan periksa log inti."
+ },
+ "noProxyInfo": {
+ "title": "Informasi proksi tidak tersedia",
+ "description": "Tidak ada grup atau node proksi yang dapat ditampilkan. Pastikan langganan aktif berisi grup dan node yang valid, atau mulai ulang inti dan periksa log."
+ },
+ "actions": {
+ "openProfiles": "Buka Profil",
+ "restartCore": "Mulai Ulang Inti",
+ "openLogs": "Buka Folder Log"
+ }
},
"chain": {
"header": "Chain Proxy Config",
diff --git a/src/locales/jp/proxies.json b/src/locales/jp/proxies.json
index 1e345dd49..b3f0b17d5 100644
--- a/src/locales/jp/proxies.json
+++ b/src/locales/jp/proxies.json
@@ -44,7 +44,28 @@
},
"empty": {
"noAvailableGroups": "利用可能なプロキシグループがありません",
- "noProxies": "プロキシがありません"
+ "noProxies": "プロキシがありません",
+ "noSubscriptions": {
+ "title": "サブスクリプションがインポートされていません",
+ "description": "プロキシページに表示する内容がありません。プロファイルページでサブスクリプションをインポートして有効化してください。"
+ },
+ "inactiveSubscription": {
+ "title": "有効なサブスクリプションがありません",
+ "description": "サブスクリプションは見つかりましたが、現在有効なものがありません。プロファイルページで選択して有効化してください。"
+ },
+ "coreUnavailable": {
+ "title": "コアを一時的に利用できません",
+ "description": "コアからプロキシ情報を取得できませんでした。コアを再起動してください。問題が解決しない場合は、ログフォルダーを開いてコアのログを確認してください。"
+ },
+ "noProxyInfo": {
+ "title": "プロキシ情報を取得できませんでした",
+ "description": "表示できるプロキシグループまたはノードがありません。有効なサブスクリプションに正しいプロキシグループとノードが含まれているか確認するか、コアを再起動してログを確認してください。"
+ },
+ "actions": {
+ "openProfiles": "プロファイルへ移動",
+ "restartCore": "コアを再起動",
+ "openLogs": "ログフォルダーを開く"
+ }
},
"chain": {
"header": "Chain Proxy Config",
diff --git a/src/locales/ko/proxies.json b/src/locales/ko/proxies.json
index 43b5b18c2..b534797c5 100644
--- a/src/locales/ko/proxies.json
+++ b/src/locales/ko/proxies.json
@@ -44,7 +44,28 @@
},
"empty": {
"noAvailableGroups": "사용 가능한 프록시 그룹이 없습니다",
- "noProxies": "프록시 없음"
+ "noProxies": "프록시 없음",
+ "noSubscriptions": {
+ "title": "가져온 구독이 없습니다",
+ "description": "프록시 페이지에 표시할 내용이 없습니다. 프로필 페이지에서 구독을 가져와 활성화하세요."
+ },
+ "inactiveSubscription": {
+ "title": "활성화된 구독이 없습니다",
+ "description": "구독이 있지만 현재 활성화된 구독이 없습니다. 프로필 페이지에서 구독을 선택하고 활성화하세요."
+ },
+ "coreUnavailable": {
+ "title": "코어를 일시적으로 사용할 수 없습니다",
+ "description": "코어에서 프록시 정보를 가져오지 못했습니다. 코어를 재시작해 보세요. 문제가 계속되면 로그 폴더를 열어 코어 로그를 확인하세요."
+ },
+ "noProxyInfo": {
+ "title": "프록시 정보를 가져오지 못했습니다",
+ "description": "표시할 프록시 그룹이나 노드가 없습니다. 활성 구독에 유효한 프록시 그룹과 노드가 포함되어 있는지 확인하거나 코어를 재시작하고 로그를 확인하세요."
+ },
+ "actions": {
+ "openProfiles": "프로필로 이동",
+ "restartCore": "코어 재시작",
+ "openLogs": "로그 폴더 열기"
+ }
},
"chain": {
"header": "체인 프록시 구성",
diff --git a/src/locales/ru/proxies.json b/src/locales/ru/proxies.json
index a9e5a1538..fdce686d0 100644
--- a/src/locales/ru/proxies.json
+++ b/src/locales/ru/proxies.json
@@ -44,7 +44,28 @@
},
"empty": {
"noAvailableGroups": "Нет доступных групп прокси",
- "noProxies": "Нет прокси"
+ "noProxies": "Нет прокси",
+ "noSubscriptions": {
+ "title": "Нет импортированных подписок",
+ "description": "На странице прокси нечего отображать. Перейдите в раздел «Профили», чтобы импортировать и активировать подписку."
+ },
+ "inactiveSubscription": {
+ "title": "Нет активной подписки",
+ "description": "Подписки найдены, но ни одна из них сейчас не активна. Перейдите в раздел «Профили», выберите подписку и активируйте её."
+ },
+ "coreUnavailable": {
+ "title": "Ядро временно недоступно",
+ "description": "Не удалось получить информацию о прокси от ядра. Попробуйте перезапустить ядро. Если проблема сохраняется, откройте папку журналов и проверьте журналы ядра."
+ },
+ "noProxyInfo": {
+ "title": "Нет информации о прокси",
+ "description": "Нет групп или узлов прокси для отображения. Убедитесь, что активная подписка содержит допустимые группы и узлы, либо перезапустите ядро и проверьте журналы."
+ },
+ "actions": {
+ "openProfiles": "Перейти к профилям",
+ "restartCore": "Перезапустить ядро",
+ "openLogs": "Открыть папку журналов"
+ }
},
"chain": {
"header": "Цепочка прокси",
diff --git a/src/locales/tr/proxies.json b/src/locales/tr/proxies.json
index 2849f21b6..00ee3b212 100644
--- a/src/locales/tr/proxies.json
+++ b/src/locales/tr/proxies.json
@@ -44,7 +44,28 @@
},
"empty": {
"noAvailableGroups": "Kullanılabilir proxy grubu yok",
- "noProxies": "Proxy yok"
+ "noProxies": "Proxy yok",
+ "noSubscriptions": {
+ "title": "İçe aktarılmış abonelik yok",
+ "description": "Proxy sayfasında görüntülenecek içerik yok. Bir aboneliği içe aktarmak ve etkinleştirmek için Profiller sayfasına gidin."
+ },
+ "inactiveSubscription": {
+ "title": "Etkin abonelik yok",
+ "description": "Abonelikler bulundu ancak hiçbiri şu anda etkin değil. Profiller sayfasına gidip bir abonelik seçin ve etkinleştirin."
+ },
+ "coreUnavailable": {
+ "title": "Çekirdek geçici olarak kullanılamıyor",
+ "description": "Proxy bilgileri çekirdekten alınamadı. Çekirdeği yeniden başlatmayı deneyin. Sorun devam ederse günlükler klasörünü açıp çekirdek günlüklerini kontrol edin."
+ },
+ "noProxyInfo": {
+ "title": "Proxy bilgisi yok",
+ "description": "Görüntülenecek proxy grubu veya düğümü yok. Etkin aboneliğin geçerli proxy grupları ve düğümleri içerdiğini doğrulayın ya da çekirdeği yeniden başlatıp günlükleri inceleyin."
+ },
+ "actions": {
+ "openProfiles": "Profillere Git",
+ "restartCore": "Çekirdeği Yeniden Başlat",
+ "openLogs": "Günlükler Klasörünü Aç"
+ }
},
"chain": {
"header": "Chain Proxy Config",
diff --git a/src/locales/tt/proxies.json b/src/locales/tt/proxies.json
index e20331ffd..53b3aa9ac 100644
--- a/src/locales/tt/proxies.json
+++ b/src/locales/tt/proxies.json
@@ -44,7 +44,28 @@
},
"empty": {
"noAvailableGroups": "Кулланырлык прокси төркемнәре юк",
- "noProxies": "Проксилар юк"
+ "noProxies": "Проксилар юк",
+ "noSubscriptions": {
+ "title": "Язылу импортланмаган",
+ "description": "Прокси битендә күрсәтерлек мәгълүмат юк. Язылуны импортлау һәм активлаштыру өчен «Профильләр» битенә күчегез."
+ },
+ "inactiveSubscription": {
+ "title": "Актив язылу юк",
+ "description": "Язылулар табылды, ләкин хәзер берсе дә актив түгел. «Профильләр» битенә күчеп, язылуны сайлагыз һәм активлаштырыгыз."
+ },
+ "coreUnavailable": {
+ "title": "Ядро вакытлыча кулланылмый",
+ "description": "Ядродан прокси мәгълүматын алып булмады. Ядроны яңадан эшләтеп карагыз. Мәсьәлә чишелмәсә, журналлар папкасын ачып, ядро журналларын тикшерегез."
+ },
+ "noProxyInfo": {
+ "title": "Прокси мәгълүматы юк",
+ "description": "Күрсәтү өчен прокси төркемнәре яки төеннәре юк. Актив язылуда дөрес төркемнәр һәм төеннәр булуын тикшерегез, яки ядроны яңадан эшләтеп журналларны карагыз."
+ },
+ "actions": {
+ "openProfiles": "Профильләргә күчү",
+ "restartCore": "Ядроны яңадан эшләтү",
+ "openLogs": "Журналлар папкасын ачу"
+ }
},
"chain": {
"header": "Chain Proxy Config",
diff --git a/src/locales/zh/proxies.json b/src/locales/zh/proxies.json
index 54c2dd3c1..2713cc1af 100644
--- a/src/locales/zh/proxies.json
+++ b/src/locales/zh/proxies.json
@@ -44,7 +44,28 @@
},
"empty": {
"noAvailableGroups": "暂无可用代理组",
- "noProxies": "暂无代理"
+ "noProxies": "暂无代理",
+ "noSubscriptions": {
+ "title": "尚未导入订阅",
+ "description": "代理页没有可展示的内容。请先前往“订阅”页面导入并启用订阅。"
+ },
+ "inactiveSubscription": {
+ "title": "尚未激活订阅",
+ "description": "检测到已有订阅,但当前没有有效订阅被激活。请前往“订阅”页面选择并激活一个订阅。"
+ },
+ "coreUnavailable": {
+ "title": "内核暂不可用",
+ "description": "无法从内核获取代理信息。请尝试重启内核;若问题仍然存在,请打开日志目录检查内核日志。"
+ },
+ "noProxyInfo": {
+ "title": "未获取到代理信息",
+ "description": "当前没有可展示的代理组或节点。请检查当前订阅是否包含有效的代理组和节点,或尝试重启内核并查看日志。"
+ },
+ "actions": {
+ "openProfiles": "前往订阅",
+ "restartCore": "重启内核",
+ "openLogs": "打开日志目录"
+ }
},
"chain": {
"header": "代理链配置",
diff --git a/src/locales/zhtw/proxies.json b/src/locales/zhtw/proxies.json
index 74b9bdced..c2e805457 100644
--- a/src/locales/zhtw/proxies.json
+++ b/src/locales/zhtw/proxies.json
@@ -44,7 +44,28 @@
},
"empty": {
"noAvailableGroups": "目前沒有可用的代理群組",
- "noProxies": "目前沒有代理"
+ "noProxies": "目前沒有代理",
+ "noSubscriptions": {
+ "title": "尚未匯入訂閱",
+ "description": "代理頁沒有可顯示的內容。請先前往「訂閱」頁面匯入並啟用訂閱。"
+ },
+ "inactiveSubscription": {
+ "title": "尚未啟用訂閱",
+ "description": "偵測到已有訂閱,但目前沒有有效訂閱被啟用。請前往「訂閱」頁面選擇並啟用一個訂閱。"
+ },
+ "coreUnavailable": {
+ "title": "核心暫時無法使用",
+ "description": "無法從核心取得代理資訊。請嘗試重新啟動核心;若問題仍然存在,請開啟日誌目錄檢查核心日誌。"
+ },
+ "noProxyInfo": {
+ "title": "未取得代理資訊",
+ "description": "目前沒有可顯示的代理群組或節點。請檢查目前訂閱是否包含有效的代理群組和節點,或嘗試重新啟動核心並查看日誌。"
+ },
+ "actions": {
+ "openProfiles": "前往訂閱",
+ "restartCore": "重新啟動核心",
+ "openLogs": "開啟日誌目錄"
+ }
},
"chain": {
"header": "鏈式代理設定",
diff --git a/src/providers/app-data-context.ts b/src/providers/app-data-context.ts
index e21882252..001337572 100644
--- a/src/providers/app-data-context.ts
+++ b/src/providers/app-data-context.ts
@@ -6,6 +6,7 @@ import type { ProxyViewV1 } from '@/types/proxy-view'
export interface ProxiesContextType {
proxyView: ProxyViewV1 | undefined
isProxyViewPending: boolean
+ isProxyViewError: boolean
}
export interface RulesContextType {
@@ -21,6 +22,7 @@ export interface ClashConfigContextType {
export interface SystemContextType {
sysproxy: any
runningMode?: string
+ isRunningModePending: boolean
systemProxyAddress: string
}
diff --git a/src/providers/app-data-provider.tsx b/src/providers/app-data-provider.tsx
index 8e7dc5afb..f1d7d3a0e 100644
--- a/src/providers/app-data-provider.tsx
+++ b/src/providers/app-data-provider.tsx
@@ -60,6 +60,7 @@ export const AppDataProvider = ({
const {
data: proxyView,
+ error: proxyViewError,
isPending: isProxyViewPending,
refetch: _refetchProxyView,
} = useQuery({
@@ -99,7 +100,7 @@ export const AppDataProvider = ({
...TQ_DEFAULTS,
})
- const { data: runningMode } = useQuery({
+ const { data: runningMode, isPending: isRunningModePending } = useQuery({
queryKey: ['getRunningMode'],
queryFn: getRunningMode,
...TQ_DEFAULTS,
@@ -216,8 +217,9 @@ export const AppDataProvider = ({
() => ({
proxyView,
isProxyViewPending,
+ isProxyViewError: Boolean(proxyViewError),
}),
- [proxyView, isProxyViewPending],
+ [proxyView, isProxyViewPending, proxyViewError],
)
const rulesValue = useMemo(
@@ -273,9 +275,18 @@ export const AppDataProvider = ({
return {
sysproxy,
runningMode,
+ isRunningModePending,
systemProxyAddress: calculateSystemProxyAddress(),
}
- }, [sysproxy, runningMode, verge, clashConfig, runtimeConfig, clashInfo])
+ }, [
+ sysproxy,
+ runningMode,
+ isRunningModePending,
+ verge,
+ clashConfig,
+ runtimeConfig,
+ clashInfo,
+ ])
const uptimeValue = useMemo(() => ({ uptime: uptimeData || 0 }), [uptimeData])
diff --git a/src/types/generated/i18n-keys.ts b/src/types/generated/i18n-keys.ts
index b555a6afe..222a02d89 100644
--- a/src/types/generated/i18n-keys.ts
+++ b/src/types/generated/i18n-keys.ts
@@ -288,6 +288,17 @@ export const translationKeys = [
'proxies.page.placeholders.delayCheckUrl',
'proxies.page.empty.noAvailableGroups',
'proxies.page.empty.noProxies',
+ 'proxies.page.empty.noSubscriptions.title',
+ 'proxies.page.empty.noSubscriptions.description',
+ 'proxies.page.empty.inactiveSubscription.title',
+ 'proxies.page.empty.inactiveSubscription.description',
+ 'proxies.page.empty.coreUnavailable.title',
+ 'proxies.page.empty.coreUnavailable.description',
+ 'proxies.page.empty.noProxyInfo.title',
+ 'proxies.page.empty.noProxyInfo.description',
+ 'proxies.page.empty.actions.openProfiles',
+ 'proxies.page.empty.actions.restartCore',
+ 'proxies.page.empty.actions.openLogs',
'proxies.page.chain.header',
'proxies.page.chain.warning',
'proxies.page.chain.empty',
diff --git a/src/types/generated/i18n-resources.ts b/src/types/generated/i18n-resources.ts
index c7ed9187b..d505d7cae 100644
--- a/src/types/generated/i18n-resources.ts
+++ b/src/types/generated/i18n-resources.ts
@@ -508,8 +508,29 @@ export interface TranslationResources {
warning: string
}
empty: {
+ actions: {
+ openLogs: string
+ openProfiles: string
+ restartCore: string
+ }
+ coreUnavailable: {
+ description: string
+ title: string
+ }
+ inactiveSubscription: {
+ description: string
+ title: string
+ }
noAvailableGroups: string
noProxies: string
+ noProxyInfo: {
+ description: string
+ title: string
+ }
+ noSubscriptions: {
+ description: string
+ title: string
+ }
}
labels: {
delayCheckReset: string