diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs index 26beabb..774ebd5 100644 --- a/src-tauri/src/cmds.rs +++ b/src-tauri/src/cmds.rs @@ -43,9 +43,10 @@ pub async fn import_profile( #[tauri::command] pub async fn create_profile( item: PrfItem, // partial + file_data: Option, profiles_state: State<'_, ProfilesState>, ) -> Result<(), String> { - let item = wrap_err!(PrfItem::from(item).await)?; + let item = wrap_err!(PrfItem::from(item, file_data).await)?; let mut profiles = profiles_state.0.lock().unwrap(); wrap_err!(profiles.append_item(item)) diff --git a/src-tauri/src/core/profiles.rs b/src-tauri/src/core/profiles.rs index a7211c1..0d86bac 100644 --- a/src-tauri/src/core/profiles.rs +++ b/src-tauri/src/core/profiles.rs @@ -119,7 +119,7 @@ impl Default for PrfItem { impl PrfItem { /// From partial item /// must contain `itype` - pub async fn from(item: PrfItem) -> Result { + pub async fn from(item: PrfItem, file_data: Option) -> Result { if item.itype.is_none() { bail!("type should not be null"); } @@ -137,7 +137,7 @@ impl PrfItem { "local" => { let name = item.name.unwrap_or("Local File".into()); let desc = item.desc.unwrap_or("".into()); - PrfItem::from_local(name, desc) + PrfItem::from_local(name, desc, file_data) } "merge" => { let name = item.name.unwrap_or("Merge".into()); @@ -155,7 +155,7 @@ impl PrfItem { /// ## Local type /// create a new item from name/desc - pub fn from_local(name: String, desc: String) -> Result { + pub fn from_local(name: String, desc: String, file_data: Option) -> Result { let uid = help::get_uid("l"); let file = format!("{uid}.yaml"); @@ -170,7 +170,7 @@ impl PrfItem { extra: None, option: None, updated: Some(help::get_now()), - file_data: Some(tmpl::ITEM_LOCAL.into()), + file_data: Some(file_data.unwrap_or(tmpl::ITEM_LOCAL.into())), }) } diff --git a/src/components/profile/file-input.tsx b/src/components/profile/file-input.tsx new file mode 100644 index 0000000..476fa2c --- /dev/null +++ b/src/components/profile/file-input.tsx @@ -0,0 +1,61 @@ +import { useRef, useState } from "react"; +import { useLockFn } from "ahooks"; +import { Box, Button, Typography } from "@mui/material"; + +interface Props { + onChange: (value: string) => void; +} + +const FileInput = (props: Props) => { + const { onChange } = props; + + // file input + const inputRef = useRef(); + const [loading, setLoading] = useState(false); + const [fileName, setFileName] = useState(""); + + const onFileInput = useLockFn(async (e: any) => { + const file = e.target.files?.[0] as File; + + if (!file) return; + + setFileName(file.name); + setLoading(true); + + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = (event) => { + resolve(null); + onChange(event.target?.result as string); + }; + reader.onerror = reject; + reader.readAsText(file); + }).finally(() => setLoading(false)); + }); + + return ( + + + + + + + {loading ? "Loading..." : fileName} + + + ); +}; + +export default FileInput; diff --git a/src/components/profile/profile-new.tsx b/src/components/profile/profile-new.tsx index a52913c..10a24ad 100644 --- a/src/components/profile/profile-new.tsx +++ b/src/components/profile/profile-new.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useRef, useState } from "react"; import { useSWRConfig } from "swr"; import { useLockFn, useSetState } from "ahooks"; import { @@ -17,6 +17,7 @@ import { import { Settings } from "@mui/icons-material"; import { createProfile } from "../../services/cmds"; import Notice from "../base/base-notice"; +import FileInput from "./file-input"; interface Props { open: boolean; @@ -37,9 +38,10 @@ const ProfileNew = (props: Props) => { }); const [showOpt, setShowOpt] = useState(false); - const [option, setOption] = useSetState({ - user_agent: "", - }); // able to add more option + // can add more option + const [option, setOption] = useSetState({ user_agent: "" }); + // file input + const fileDataRef = useRef(null); const onCreate = useLockFn(async () => { if (!form.type) { @@ -55,10 +57,15 @@ const ProfileNew = (props: Props) => { } const option_ = form.type === "remote" ? option : undefined; - await createProfile({ ...form, name, option: option_ }); + const item = { ...form, name, option: option_ }; + const fileData = form.type === "local" ? fileDataRef.current : null; + + await createProfile(item, fileData); + setForm({ type: "remote", name: "", desc: "", url: "" }); setOption({ user_agent: "" }); setShowOpt(false); + fileDataRef.current = null; mutate("getProfiles"); onClose(); @@ -97,6 +104,7 @@ const ProfileNew = (props: Props) => { setForm({ name: e.target.value })} /> @@ -104,6 +112,7 @@ const ProfileNew = (props: Props) => { setForm({ desc: e.target.value })} /> @@ -112,15 +121,21 @@ const ProfileNew = (props: Props) => { setForm({ url: e.target.value })} /> )} + {form.type === "local" && ( + (fileDataRef.current = val)} /> + )} + {showOpt && ( setOption({ user_agent: e.target.value })} /> diff --git a/src/services/cmds.ts b/src/services/cmds.ts index 6dc28d3..06a4ecd 100644 --- a/src/services/cmds.ts +++ b/src/services/cmds.ts @@ -14,8 +14,11 @@ export async function enhanceProfiles() { return invoke("enhance_profiles"); } -export async function createProfile(item: Partial) { - return invoke("create_profile", { item }); +export async function createProfile( + item: Partial, + fileData?: string | null +) { + return invoke("create_profile", { item, fileData }); } export async function viewProfile(index: string) {