Refine ProxyWarden routing workflow
This commit is contained in:
+37
-104
@@ -67,13 +67,11 @@ import {
|
||||
} from "../ui";
|
||||
import { ProxiFyreSetupStrip } from "./components/ProxiFyreSetupStrip";
|
||||
import { SummaryStatusControl } from "./components/SummaryStatusControl";
|
||||
import { ConnectionCheckPanel } from "./components/ConnectionCheckPanel";
|
||||
import { AppList } from "./components/AppList";
|
||||
import { useNoticeLog } from "./hooks/useNoticeLog";
|
||||
import { parseProxy, type ParsedProxy } from "./lib/parseProxy";
|
||||
import {
|
||||
itemTypeLabel,
|
||||
normalizeItemValue,
|
||||
type DraftItemType,
|
||||
} from "./lib/profileItems";
|
||||
import { normalizeItemValue, type DraftItemType } from "./lib/profileItems";
|
||||
import {
|
||||
configChangeRows,
|
||||
configSnapshotFromUi,
|
||||
@@ -115,7 +113,6 @@ import {
|
||||
serverTooltip,
|
||||
pingSummary,
|
||||
errorMessage,
|
||||
type ConnectionCheckView,
|
||||
type DraftItem,
|
||||
type RouteChainInput,
|
||||
} from "./viewModel";
|
||||
@@ -928,6 +925,7 @@ export function App() {
|
||||
return;
|
||||
}
|
||||
|
||||
setProxyCheck(null);
|
||||
setIsProxyChecking(true);
|
||||
try {
|
||||
const result = await pingProxyTarget(target.host, target.port);
|
||||
@@ -1228,40 +1226,7 @@ export function App() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="app-list">
|
||||
{isLoading ? (
|
||||
<div className="list-skeleton" aria-label="Загрузка приложений">
|
||||
<span />
|
||||
<span />
|
||||
</div>
|
||||
) : items.length ? (
|
||||
items.map((item) => (
|
||||
<div className="app-row" key={item.id}>
|
||||
<div className="app-row-main">
|
||||
<span className="item-icon" aria-hidden="true">
|
||||
{itemIcon(item.type)}
|
||||
</span>
|
||||
<div>
|
||||
<strong>{item.value}</strong>
|
||||
<span>{itemTypeLabel(item.type)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="danger"
|
||||
onClick={() => removeItem(item.id)}
|
||||
aria-label={`Удалить ${item.value}`}
|
||||
>
|
||||
Удалить
|
||||
</Button>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="empty-state">
|
||||
Список пуст. Добавь первое приложение сверху.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<AppList items={items} loading={isLoading} onRemove={removeItem} />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -1373,54 +1338,6 @@ export function App() {
|
||||
);
|
||||
}
|
||||
|
||||
function renderConnectionCheck(check: ConnectionCheckView) {
|
||||
const buttonDisabled = Boolean(check.disabledReason);
|
||||
|
||||
return (
|
||||
<div className="connection-check" aria-label="Проверка соединения">
|
||||
<div className="connection-check-status">
|
||||
<span>Проверка маршрута</span>
|
||||
<strong>{check.title}</strong>
|
||||
<p>{check.text}</p>
|
||||
</div>
|
||||
{check.probes.length ? (
|
||||
<div className="connection-probes" aria-label="Контрольные точки">
|
||||
{check.probes.map((probe) => (
|
||||
<span className={`connection-probe ${probe.tone}`} key={probe.id}>
|
||||
<strong>{probe.label}</strong>
|
||||
<span>{probe.value}</span>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
<DetailsPopover
|
||||
className="connection-endpoint"
|
||||
details={check.details}
|
||||
popoverLabel="Проверка маршрута"
|
||||
align="end"
|
||||
aria-label={`${check.endpoint}. ${check.details.join(". ")}`}
|
||||
>
|
||||
<strong>{check.endpoint}</strong>
|
||||
</DetailsPopover>
|
||||
<Button
|
||||
type="button"
|
||||
variant="neutral"
|
||||
size="md"
|
||||
onClick={() => void checkRouteProxy()}
|
||||
disabled={buttonDisabled}
|
||||
loading={check.loading}
|
||||
loadingLabel="Проверяю"
|
||||
>
|
||||
Проверить
|
||||
</Button>
|
||||
<p className="network-disclosure">
|
||||
Проверка отправит HTTPS-запросы через выбранный прокси в Cloudflare и
|
||||
ipify, чтобы увидеть внешний IP.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderSingBoxCard() {
|
||||
const state = serviceControlState(singbox, isDetectingComponents);
|
||||
const setupSummary = singBoxSetupStatus
|
||||
@@ -1637,7 +1554,10 @@ export function App() {
|
||||
aria-label="Состояние прокси"
|
||||
>
|
||||
{check.tone === "checking" ? <BusyRing /> : null}
|
||||
{renderConnectionCheck(check)}
|
||||
<ConnectionCheckPanel
|
||||
check={check}
|
||||
onCheck={() => void checkRouteProxy()}
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -1650,32 +1570,51 @@ export function App() {
|
||||
id="panel-proxy"
|
||||
aria-labelledby="tab-proxy"
|
||||
>
|
||||
{renderProxyOverview()}
|
||||
|
||||
<section className="route-panel" aria-label="Маршрут приложений">
|
||||
<section className="route-mode-selector" aria-label="Режим прокси">
|
||||
<span>Режим маршрута</span>
|
||||
<div className="route-switch">
|
||||
<Button
|
||||
type="button"
|
||||
variant={routeMode === "external" ? "primary" : "neutral"}
|
||||
className="route-mode-option external"
|
||||
onClick={() => changeRouteMode("external")}
|
||||
aria-pressed={routeMode === "external"}
|
||||
aria-controls="route-mode-content"
|
||||
>
|
||||
Внешний прокси
|
||||
<span className="route-mode-label">
|
||||
<strong>Внешний прокси</strong>
|
||||
<small>Готовый SOCKS5 endpoint</small>
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant={routeMode === "local-singbox" ? "primary" : "neutral"}
|
||||
className="route-mode-option local"
|
||||
onClick={() => changeRouteMode("local-singbox")}
|
||||
aria-pressed={routeMode === "local-singbox"}
|
||||
aria-controls="route-mode-content"
|
||||
>
|
||||
Локальный прокси
|
||||
<span className="route-mode-label">
|
||||
<strong>Локальный прокси</strong>
|
||||
<small>sing-box на этом компьютере</small>
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{routeMode === "external"
|
||||
? renderExternalProxyControls()
|
||||
: renderSingBoxCard()}
|
||||
</section>
|
||||
|
||||
<div
|
||||
className={`route-mode-content ${routeMode === "external" ? "external" : "local"}`}
|
||||
id="route-mode-content"
|
||||
key={routeMode}
|
||||
>
|
||||
<section className="route-panel" aria-label="Маршрут приложений">
|
||||
{routeMode === "external"
|
||||
? renderExternalProxyControls()
|
||||
: renderSingBoxCard()}
|
||||
</section>
|
||||
|
||||
{renderProxyOverview()}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -1803,9 +1742,3 @@ function nextFrame() {
|
||||
window.requestAnimationFrame(() => resolve());
|
||||
});
|
||||
}
|
||||
|
||||
function itemIcon(type: DraftItemType) {
|
||||
if (type === "process") return <Cpu size={18} strokeWidth={1.9} />;
|
||||
if (type === "folder") return <FolderOpen size={18} strokeWidth={1.9} />;
|
||||
return <FileCode2 size={18} strokeWidth={1.9} />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user