|
@@ -1,13 +1,11 @@
|
|
|
import dayjs from "dayjs";
|
|
|
+import { mutate } from "swr";
|
|
|
import { useEffect, useState } from "react";
|
|
|
import { useLockFn } from "ahooks";
|
|
|
-import { useSWRConfig } from "swr";
|
|
|
import { useRecoilState } from "recoil";
|
|
|
import { useTranslation } from "react-i18next";
|
|
|
import {
|
|
|
- alpha,
|
|
|
Box,
|
|
|
- styled,
|
|
|
Typography,
|
|
|
LinearProgress,
|
|
|
IconButton,
|
|
@@ -19,21 +17,11 @@ import { RefreshRounded } from "@mui/icons-material";
|
|
|
import { atomLoadingCache } from "@/services/states";
|
|
|
import { updateProfile, deleteProfile, viewProfile } from "@/services/cmds";
|
|
|
import parseTraffic from "@/utils/parse-traffic";
|
|
|
-import ProfileEdit from "./profile-edit";
|
|
|
+import ProfileBox from "./profile-box";
|
|
|
+import InfoEditor from "./info-editor";
|
|
|
import FileEditor from "./file-editor";
|
|
|
import Notice from "../base/base-notice";
|
|
|
|
|
|
-const Wrapper = styled(Box)(({ theme }) => ({
|
|
|
- width: "100%",
|
|
|
- display: "block",
|
|
|
- cursor: "pointer",
|
|
|
- textAlign: "left",
|
|
|
- borderRadius: theme.shape.borderRadius,
|
|
|
- boxShadow: theme.shadows[2],
|
|
|
- padding: "8px 16px",
|
|
|
- boxSizing: "border-box",
|
|
|
-}));
|
|
|
-
|
|
|
const round = keyframes`
|
|
|
from { transform: rotate(0deg); }
|
|
|
to { transform: rotate(360deg); }
|
|
@@ -49,7 +37,6 @@ const ProfileItem = (props: Props) => {
|
|
|
const { selected, itemData, onSelect } = props;
|
|
|
|
|
|
const { t } = useTranslation();
|
|
|
- const { mutate } = useSWRConfig();
|
|
|
const [anchorEl, setAnchorEl] = useState<any>(null);
|
|
|
const [position, setPosition] = useState({ left: 0, top: 0 });
|
|
|
const [loadingCache, setLoadingCache] = useRecoilState(atomLoadingCache);
|
|
@@ -58,7 +45,6 @@ const ProfileItem = (props: Props) => {
|
|
|
|
|
|
// local file mode
|
|
|
// remote file mode
|
|
|
- // subscription url mode
|
|
|
const hasUrl = !!itemData.url;
|
|
|
const hasExtra = !!extra; // only subscription url has extra info
|
|
|
|
|
@@ -79,7 +65,6 @@ const ProfileItem = (props: Props) => {
|
|
|
const handler = () => {
|
|
|
const now = Date.now();
|
|
|
const lastUpdate = updated * 1000;
|
|
|
-
|
|
|
// 大于一天的不管
|
|
|
if (now - lastUpdate >= 24 * 36e5) return;
|
|
|
|
|
@@ -152,13 +137,6 @@ const ProfileItem = (props: Props) => {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- const boxStyle = {
|
|
|
- height: 26,
|
|
|
- display: "flex",
|
|
|
- alignItems: "center",
|
|
|
- justifyContent: "space-between",
|
|
|
- };
|
|
|
-
|
|
|
const urlModeMenu = [
|
|
|
{ label: "Select", handler: onForceSelect },
|
|
|
{ label: "Edit Info", handler: onEditInfo },
|
|
@@ -176,36 +154,17 @@ const ProfileItem = (props: Props) => {
|
|
|
{ label: "Delete", handler: onDelete },
|
|
|
];
|
|
|
|
|
|
+ const boxStyle = {
|
|
|
+ height: 26,
|
|
|
+ display: "flex",
|
|
|
+ alignItems: "center",
|
|
|
+ justifyContent: "space-between",
|
|
|
+ };
|
|
|
+
|
|
|
return (
|
|
|
<>
|
|
|
- <Wrapper
|
|
|
- sx={({ palette }) => {
|
|
|
- const { mode, primary, text, grey } = palette;
|
|
|
- const key = `${mode}-${selected}`;
|
|
|
-
|
|
|
- const bgcolor = {
|
|
|
- "light-true": alpha(primary.main, 0.15),
|
|
|
- "light-false": palette.background.paper,
|
|
|
- "dark-true": alpha(primary.main, 0.35),
|
|
|
- "dark-false": alpha(grey[700], 0.35),
|
|
|
- }[key]!;
|
|
|
-
|
|
|
- const color = {
|
|
|
- "light-true": text.secondary,
|
|
|
- "light-false": text.secondary,
|
|
|
- "dark-true": alpha(text.secondary, 0.75),
|
|
|
- "dark-false": alpha(text.secondary, 0.75),
|
|
|
- }[key]!;
|
|
|
-
|
|
|
- const h2color = {
|
|
|
- "light-true": primary.main,
|
|
|
- "light-false": text.primary,
|
|
|
- "dark-true": primary.light,
|
|
|
- "dark-false": text.primary,
|
|
|
- }[key]!;
|
|
|
-
|
|
|
- return { bgcolor, color, "& h2": { color: h2color } };
|
|
|
- }}
|
|
|
+ <ProfileBox
|
|
|
+ aria-selected={selected}
|
|
|
onClick={() => onSelect(false)}
|
|
|
onContextMenu={(event) => {
|
|
|
const { clientX, clientY } = event;
|
|
@@ -214,9 +173,9 @@ const ProfileItem = (props: Props) => {
|
|
|
event.preventDefault();
|
|
|
}}
|
|
|
>
|
|
|
- <Box display="flex" justifyContent="space-between">
|
|
|
+ <Box position="relative">
|
|
|
<Typography
|
|
|
- width="calc(100% - 40px)"
|
|
|
+ width="calc(100% - 36px)"
|
|
|
variant="h6"
|
|
|
component="h2"
|
|
|
noWrap
|
|
@@ -229,10 +188,13 @@ const ProfileItem = (props: Props) => {
|
|
|
{hasUrl && (
|
|
|
<IconButton
|
|
|
sx={{
|
|
|
- width: 26,
|
|
|
- height: 26,
|
|
|
+ position: "absolute",
|
|
|
+ p: "3px",
|
|
|
+ top: -1,
|
|
|
+ right: -5,
|
|
|
animation: loading ? `1s linear infinite ${round}` : "none",
|
|
|
}}
|
|
|
+ size="small"
|
|
|
color="inherit"
|
|
|
disabled={loading}
|
|
|
onClick={(e) => {
|
|
@@ -240,47 +202,47 @@ const ProfileItem = (props: Props) => {
|
|
|
onUpdate(false);
|
|
|
}}
|
|
|
>
|
|
|
- <RefreshRounded />
|
|
|
+ <RefreshRounded color="inherit" />
|
|
|
</IconButton>
|
|
|
)}
|
|
|
</Box>
|
|
|
|
|
|
{/* the second line show url's info or description */}
|
|
|
- {hasUrl ? (
|
|
|
- <Box sx={boxStyle}>
|
|
|
- <Typography noWrap title={`From: ${from}`}>
|
|
|
- {from}
|
|
|
- </Typography>
|
|
|
-
|
|
|
- <Typography
|
|
|
- noWrap
|
|
|
- flex="1 0 auto"
|
|
|
- fontSize={14}
|
|
|
- textAlign="right"
|
|
|
- title="updated time"
|
|
|
- >
|
|
|
- {updated > 0 ? dayjs(updated * 1000).fromNow() : ""}
|
|
|
- </Typography>
|
|
|
- </Box>
|
|
|
- ) : (
|
|
|
- <Box sx={boxStyle}>
|
|
|
+ <Box sx={boxStyle}>
|
|
|
+ {hasUrl ? (
|
|
|
+ <>
|
|
|
+ <Typography noWrap title={`From: ${from}`}>
|
|
|
+ {from}
|
|
|
+ </Typography>
|
|
|
+
|
|
|
+ <Typography
|
|
|
+ noWrap
|
|
|
+ flex="1 0 auto"
|
|
|
+ fontSize={14}
|
|
|
+ textAlign="right"
|
|
|
+ title={`Updated Time: ${parseExpire(updated)}`}
|
|
|
+ >
|
|
|
+ {updated > 0 ? dayjs(updated * 1000).fromNow() : ""}
|
|
|
+ </Typography>
|
|
|
+ </>
|
|
|
+ ) : (
|
|
|
<Typography noWrap title={itemData.desc}>
|
|
|
{itemData.desc}
|
|
|
</Typography>
|
|
|
- </Box>
|
|
|
- )}
|
|
|
+ )}
|
|
|
+ </Box>
|
|
|
|
|
|
{/* the third line show extra info or last updated time */}
|
|
|
{hasExtra ? (
|
|
|
<Box sx={{ ...boxStyle, fontSize: 14 }}>
|
|
|
- <span title="used / total">
|
|
|
+ <span title="Used / Total">
|
|
|
{parseTraffic(upload + download)} / {parseTraffic(total)}
|
|
|
</span>
|
|
|
- <span title="expire time">{expire}</span>
|
|
|
+ <span title="Expire Time">{expire}</span>
|
|
|
</Box>
|
|
|
) : (
|
|
|
<Box sx={{ ...boxStyle, fontSize: 14, justifyContent: "flex-end" }}>
|
|
|
- <span title="updated time">{parseExpire(updated)}</span>
|
|
|
+ <span title="Updated Time">{parseExpire(updated)}</span>
|
|
|
</Box>
|
|
|
)}
|
|
|
|
|
@@ -289,7 +251,7 @@ const ProfileItem = (props: Props) => {
|
|
|
value={progress}
|
|
|
color="inherit"
|
|
|
/>
|
|
|
- </Wrapper>
|
|
|
+ </ProfileBox>
|
|
|
|
|
|
<Menu
|
|
|
open={!!anchorEl}
|
|
@@ -314,22 +276,18 @@ const ProfileItem = (props: Props) => {
|
|
|
))}
|
|
|
</Menu>
|
|
|
|
|
|
- {editOpen && (
|
|
|
- <ProfileEdit
|
|
|
- open={editOpen}
|
|
|
- itemData={itemData}
|
|
|
- onClose={() => setEditOpen(false)}
|
|
|
- />
|
|
|
- )}
|
|
|
-
|
|
|
- {fileOpen && (
|
|
|
- <FileEditor
|
|
|
- uid={uid}
|
|
|
- open={fileOpen}
|
|
|
- mode="yaml"
|
|
|
- onClose={() => setFileOpen(false)}
|
|
|
- />
|
|
|
- )}
|
|
|
+ <InfoEditor
|
|
|
+ open={editOpen}
|
|
|
+ itemData={itemData}
|
|
|
+ onClose={() => setEditOpen(false)}
|
|
|
+ />
|
|
|
+
|
|
|
+ <FileEditor
|
|
|
+ uid={uid}
|
|
|
+ open={fileOpen}
|
|
|
+ mode="yaml"
|
|
|
+ onClose={() => setFileOpen(false)}
|
|
|
+ />
|
|
|
</>
|
|
|
);
|
|
|
};
|