diff --git a/src/components/profile/profile-edit.tsx b/src/components/profile/profile-edit.tsx
new file mode 100644
index 0000000..0b191ad
--- /dev/null
+++ b/src/components/profile/profile-edit.tsx
@@ -0,0 +1,93 @@
+import { useEffect, useState } from "react";
+import { useLockFn } from "ahooks";
+import { mutate } from "swr";
+import {
+ Button,
+ Dialog,
+ DialogActions,
+ DialogContent,
+ DialogTitle,
+ TextField,
+} from "@mui/material";
+import { CmdType } from "../../services/types";
+import { patchProfile } from "../../services/cmds";
+import Notice from "../base/base-notice";
+
+interface Props {
+ open: boolean;
+ itemData: CmdType.ProfileItem;
+ onClose: () => void;
+}
+
+// edit the profile item
+const ProfileEdit = (props: Props) => {
+ const { open, itemData, onClose } = props;
+
+ // todo: more type
+ const [name, setName] = useState(itemData.name);
+ const [desc, setDesc] = useState(itemData.desc);
+ const [url, setUrl] = useState(itemData.url);
+
+ useEffect(() => {
+ if (itemData) {
+ setName(itemData.name);
+ setDesc(itemData.desc);
+ setUrl(itemData.url);
+ }
+ }, [itemData]);
+
+ const onUpdate = useLockFn(async () => {
+ try {
+ const { uid } = itemData;
+ await patchProfile(uid, { uid, name, desc, url });
+ mutate("getProfiles");
+ onClose();
+ } catch (err: any) {
+ Notice.error(err?.message || err?.toString());
+ }
+ });
+
+ return (
+
+ );
+};
+
+export default ProfileEdit;
diff --git a/src/components/profile/profile-item.tsx b/src/components/profile/profile-item.tsx
index b39459d..0484b46 100644
--- a/src/components/profile/profile-item.tsx
+++ b/src/components/profile/profile-item.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from "react";
+import React, { useEffect, useState } from "react";
import dayjs from "dayjs";
import {
alpha,
@@ -16,9 +16,10 @@ import { useSWRConfig } from "swr";
import { RefreshRounded } from "@mui/icons-material";
import { CmdType } from "../../services/types";
import { updateProfile, deleteProfile, viewProfile } from "../../services/cmds";
-import Notice from "../base/base-notice";
-import parseTraffic from "../../utils/parse-traffic";
import relativeTime from "dayjs/plugin/relativeTime";
+import parseTraffic from "../../utils/parse-traffic";
+import ProfileEdit from "./profile-edit";
+import Notice from "../base/base-notice";
dayjs.extend(relativeTime);
@@ -38,8 +39,10 @@ const round = keyframes`
to { transform: rotate(360deg); }
`;
+// save the state of each item loading
+const loadingCache: Record = {};
+
interface Props {
- // index: number;
selected: boolean;
itemData: CmdType.ProfileItem;
onSelect: (force: boolean) => void;
@@ -49,7 +52,7 @@ const ProfileItem: React.FC = (props) => {
const { selected, itemData, onSelect } = props;
const { mutate } = useSWRConfig();
- const [loading, setLoading] = useState(false);
+ const [loading, setLoading] = useState(loadingCache[itemData.uid] ?? false);
const [anchorEl, setAnchorEl] = useState(null);
const [position, setPosition] = useState({ left: 0, top: 0 });
@@ -66,6 +69,16 @@ const ProfileItem: React.FC = (props) => {
const hasUrl = !!itemData.url;
const hasExtra = !!extra; // only subscription url has extra info
+ useEffect(() => {
+ loadingCache[itemData.uid] = loading;
+ }, [itemData, loading]);
+
+ const [editOpen, setEditOpen] = useState(false);
+ const onEdit = () => {
+ setAnchorEl(null);
+ setEditOpen(true);
+ };
+
const onView = async () => {
setAnchorEl(null);
try {
@@ -86,11 +99,11 @@ const ProfileItem: React.FC = (props) => {
setLoading(true);
try {
await updateProfile(itemData.uid, withProxy);
+ setLoading(false);
mutate("getProfiles");
} catch (err: any) {
- Notice.error(err.toString());
- } finally {
setLoading(false);
+ Notice.error(err?.message || err.toString());
}
};
@@ -101,7 +114,7 @@ const ProfileItem: React.FC = (props) => {
await deleteProfile(itemData.uid);
mutate("getProfiles");
} catch (err: any) {
- Notice.error(err.toString());
+ Notice.error(err?.message || err.toString());
}
});
@@ -123,6 +136,7 @@ const ProfileItem: React.FC = (props) => {
const urlModeMenu = [
{ label: "Select", handler: onForceSelect },
+ { label: "Edit", handler: onEdit },
{ label: "View", handler: onView },
{ label: "Update", handler: onUpdateWrapper(false) },
{ label: "Update(Proxy)", handler: onUpdateWrapper(true) },
@@ -130,7 +144,8 @@ const ProfileItem: React.FC = (props) => {
];
const fileModeMenu = [
{ label: "Select", handler: onForceSelect },
- { label: "Edit", handler: onView },
+ { label: "Edit", handler: onEdit },
+ { label: "View", handler: onView },
{ label: "Delete", handler: onDelete },
];
@@ -261,6 +276,12 @@ const ProfileItem: React.FC = (props) => {
))}
+
+ setEditOpen(false)}
+ />
>
);
};
diff --git a/src/services/types.ts b/src/services/types.ts
index 07ace0e..b9e06f9 100644
--- a/src/services/types.ts
+++ b/src/services/types.ts
@@ -91,7 +91,6 @@ export namespace CmdType {
name?: string;
desc?: string;
file?: string;
- mode?: string;
url?: string;
updated?: number;
selected?: {