mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 17:15:38 +08:00
Merge remote-tracking branch 'nyanpasu/main'
This commit is contained in:
@@ -4,14 +4,15 @@ import relativeTime from "dayjs/plugin/relativeTime";
|
||||
import { SWRConfig, mutate } from "swr";
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Route, Routes } from "react-router-dom";
|
||||
import { Route, Routes, useLocation } from "react-router-dom";
|
||||
import { CSSTransition, TransitionGroup } from "react-transition-group";
|
||||
import { alpha, List, Paper, ThemeProvider } from "@mui/material";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import { appWindow } from "@tauri-apps/api/window";
|
||||
import { routers } from "./_routers";
|
||||
import { getAxios } from "@/services/api";
|
||||
import { useVerge } from "@/hooks/use-verge";
|
||||
import { ReactComponent as LogoSvg } from "@/assets/image/logo.svg";
|
||||
import LogoSvg from "@/assets/image/logo.svg?react";
|
||||
import { BaseErrorBoundary, Notice } from "@/components/base";
|
||||
import { LayoutItem } from "@/components/layout/layout-item";
|
||||
import { LayoutControl } from "@/components/layout/layout-control";
|
||||
@@ -34,6 +35,8 @@ const Layout = () => {
|
||||
const { verge } = useVerge();
|
||||
const { theme_blur, language } = verge || {};
|
||||
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("keydown", (e) => {
|
||||
// macOS有cmd+w
|
||||
@@ -136,21 +139,27 @@ const Layout = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="the-content">
|
||||
<Routes>
|
||||
{routers.map(({ label, link, ele: Ele }) => (
|
||||
<Route
|
||||
key={label}
|
||||
path={link}
|
||||
element={
|
||||
<BaseErrorBoundary key={label}>
|
||||
<Ele />
|
||||
</BaseErrorBoundary>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Routes>
|
||||
</div>
|
||||
<TransitionGroup className="the-content">
|
||||
<CSSTransition
|
||||
key={location.pathname}
|
||||
timeout={300}
|
||||
classNames="page"
|
||||
>
|
||||
<Routes>
|
||||
{routers.map(({ label, link, ele: Ele }) => (
|
||||
<Route
|
||||
key={label}
|
||||
path={link}
|
||||
element={
|
||||
<BaseErrorBoundary key={label}>
|
||||
<Ele />
|
||||
</BaseErrorBoundary>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Routes>
|
||||
</CSSTransition>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
</Paper>
|
||||
</ThemeProvider>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// default theme setting
|
||||
export const defaultTheme = {
|
||||
primary_color: "#5b5c9d",
|
||||
secondary_color: "#9c27b0",
|
||||
primary_text: "#637381",
|
||||
secondary_text: "#909399",
|
||||
primary_color: "#1867c0",
|
||||
secondary_color: "#3a88bb",
|
||||
primary_text: "#1d1d1f",
|
||||
secondary_text: "#424245",
|
||||
info_color: "#0288d1",
|
||||
error_color: "#d32f2f",
|
||||
warning_color: "#ed6c02",
|
||||
@@ -14,6 +14,6 @@ export const defaultTheme = {
|
||||
// dark mode
|
||||
export const defaultDarkTheme = {
|
||||
...defaultTheme,
|
||||
primary_text: "#757575",
|
||||
secondary_text: "#637381",
|
||||
primary_text: "#E8E8ED",
|
||||
secondary_text: "#bbbbbb",
|
||||
};
|
||||
|
||||
@@ -3,16 +3,24 @@ import { useLockFn } from "ahooks";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Grid,
|
||||
IconButton,
|
||||
MenuItem,
|
||||
Paper,
|
||||
Select,
|
||||
TextField,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import { useRecoilState } from "recoil";
|
||||
import { Virtuoso } from "react-virtuoso";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TableChartRounded, TableRowsRounded } from "@mui/icons-material";
|
||||
import {
|
||||
ArrowDownward,
|
||||
ArrowUpward,
|
||||
Link,
|
||||
TableChartRounded,
|
||||
TableRowsRounded,
|
||||
} from "@mui/icons-material";
|
||||
import { closeAllConnections } from "@/services/api";
|
||||
import { atomConnectionSetting } from "@/services/states";
|
||||
import { useClashInfo } from "@/hooks/use-clash";
|
||||
@@ -24,6 +32,7 @@ import {
|
||||
ConnectionDetail,
|
||||
ConnectionDetailRef,
|
||||
} from "@/components/connection/connection-detail";
|
||||
import parseTraffic from "@/utils/parse-traffic";
|
||||
|
||||
const initConn = { uploadTotal: 0, downloadTotal: 0, connections: [] };
|
||||
|
||||
@@ -48,6 +57,10 @@ const ConnectionsPage = () => {
|
||||
list.sort((a, b) => b.curDownload! - a.curDownload!),
|
||||
};
|
||||
|
||||
const uploadTotal = connData.uploadTotal;
|
||||
|
||||
const downloadTotal = connData.downloadTotal;
|
||||
|
||||
const filterConn = useMemo(() => {
|
||||
const orderFunc = orderOpts[curOrderOpt];
|
||||
const connections = connData.connections.filter((conn) =>
|
||||
@@ -112,6 +125,24 @@ const ConnectionsPage = () => {
|
||||
|
||||
const detailRef = useRef<ConnectionDetailRef>(null!);
|
||||
|
||||
const connectionItems = [
|
||||
{
|
||||
icon: <ArrowUpward />,
|
||||
label: t("Upload Total"),
|
||||
value: parseTraffic(uploadTotal).join(" "),
|
||||
},
|
||||
{
|
||||
icon: <ArrowDownward />,
|
||||
label: t("Download Total"),
|
||||
value: parseTraffic(downloadTotal).join(" "),
|
||||
},
|
||||
{
|
||||
icon: <Link />,
|
||||
label: t("Active Connections"),
|
||||
value: filterConn.length,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<BasePage
|
||||
title={t("Connections")}
|
||||
@@ -142,7 +173,21 @@ const ConnectionsPage = () => {
|
||||
</Box>
|
||||
}
|
||||
>
|
||||
<Paper sx={{ boxShadow: 2, height: "100%" }}>
|
||||
<Paper sx={{ padding: 2, mb: 2 }}>
|
||||
<Grid container>
|
||||
{connectionItems.map((item, index) => (
|
||||
<Grid item xs={4} key={index}>
|
||||
<Box display="flex" alignItems="center" whiteSpace="nowrap">
|
||||
{item.icon}
|
||||
<Typography sx={{ ml: 1, mr: 1 }}>{item.label}</Typography>
|
||||
<Typography>{item.value}</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Paper>
|
||||
|
||||
<Paper sx={{ boxShadow: 2, height: "calc(100% - 56px - 16px)" }}>
|
||||
<Box
|
||||
sx={{
|
||||
pt: 1,
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useMemo, useRef, useState } from "react";
|
||||
import { useLockFn } from "ahooks";
|
||||
import { useSetRecoilState } from "recoil";
|
||||
import { Box, Button, Grid, IconButton, Stack, TextField } from "@mui/material";
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
import {
|
||||
ClearRounded,
|
||||
ContentCopyRounded,
|
||||
@@ -38,6 +39,7 @@ const ProfilePage = () => {
|
||||
const [url, setUrl] = useState("");
|
||||
const [disabled, setDisabled] = useState(false);
|
||||
const [activating, setActivating] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const {
|
||||
profiles = {},
|
||||
@@ -76,12 +78,13 @@ const ProfilePage = () => {
|
||||
|
||||
const onImport = async () => {
|
||||
if (!url) return;
|
||||
setUrl("");
|
||||
setDisabled(true);
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
await importProfile(url);
|
||||
Notice.success("Successfully import profile.");
|
||||
setUrl("");
|
||||
setLoading(false);
|
||||
|
||||
getProfiles().then((newProfiles) => {
|
||||
mutate("getProfiles", newProfiles);
|
||||
@@ -96,8 +99,10 @@ const ProfilePage = () => {
|
||||
});
|
||||
} catch (err: any) {
|
||||
Notice.error(err.message || err.toString());
|
||||
setLoading(false);
|
||||
} finally {
|
||||
setDisabled(false);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -271,14 +276,15 @@ const ProfilePage = () => {
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
<LoadingButton
|
||||
disabled={!url || disabled}
|
||||
loading={loading}
|
||||
variant="contained"
|
||||
size="small"
|
||||
onClick={onImport}
|
||||
>
|
||||
{t("Import")}
|
||||
</Button>
|
||||
</LoadingButton>
|
||||
<Button
|
||||
variant="contained"
|
||||
size="small"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IconButton, Paper } from "@mui/material";
|
||||
import { Grid, IconButton, Paper } from "@mui/material";
|
||||
import { useLockFn } from "ahooks";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { BasePage, Notice } from "@/components/base";
|
||||
@@ -33,17 +33,25 @@ const SettingPage = () => {
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
<Paper sx={{ borderRadius: 1, boxShadow: 2, mb: 3 }}>
|
||||
<SettingClash onError={onError} />
|
||||
</Paper>
|
||||
<Grid container spacing={{ xs: 2, lg: 3 }}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Paper sx={{ borderRadius: 1, boxShadow: 2 }}>
|
||||
<SettingClash onError={onError} />
|
||||
</Paper>
|
||||
</Grid>
|
||||
|
||||
<Paper sx={{ borderRadius: 1, boxShadow: 2, mb: 3 }}>
|
||||
<SettingSystem onError={onError} />
|
||||
</Paper>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Paper sx={{ borderRadius: 1, boxShadow: 2 }}>
|
||||
<SettingSystem onError={onError} />
|
||||
</Paper>
|
||||
</Grid>
|
||||
|
||||
<Paper sx={{ borderRadius: 1, boxShadow: 2 }}>
|
||||
<SettingVerge onError={onError} />
|
||||
</Paper>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Paper sx={{ borderRadius: 1, boxShadow: 2 }}>
|
||||
<SettingVerge onError={onError} />
|
||||
</Paper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</BasePage>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user