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 ( + + Edit Profile + + setName(e.target.value)} + /> + + setDesc(e.target.value)} + /> + + setUrl(e.target.value)} + /> + + + + + + + + ); +}; + +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?: {