feat: create local profile with selected file
This commit is contained in:
parent
7fe94076c7
commit
98fa4d5e65
@ -43,9 +43,10 @@ pub async fn import_profile(
|
||||
#[tauri::command]
|
||||
pub async fn create_profile(
|
||||
item: PrfItem, // partial
|
||||
file_data: Option<String>,
|
||||
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))
|
||||
|
@ -119,7 +119,7 @@ impl Default for PrfItem {
|
||||
impl PrfItem {
|
||||
/// From partial item
|
||||
/// must contain `itype`
|
||||
pub async fn from(item: PrfItem) -> Result<PrfItem> {
|
||||
pub async fn from(item: PrfItem, file_data: Option<String>) -> Result<PrfItem> {
|
||||
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<PrfItem> {
|
||||
pub fn from_local(name: String, desc: String, file_data: Option<String>) -> Result<PrfItem> {
|
||||
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())),
|
||||
})
|
||||
}
|
||||
|
||||
|
61
src/components/profile/file-input.tsx
Normal file
61
src/components/profile/file-input.tsx
Normal file
@ -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<any>();
|
||||
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 (
|
||||
<Box sx={{ mt: 2, mb: 1, display: "flex", alignItems: "center" }}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
sx={{ flex: "none" }}
|
||||
onClick={() => inputRef.current?.click()}
|
||||
>
|
||||
Choose File
|
||||
</Button>
|
||||
|
||||
<input
|
||||
type="file"
|
||||
accept=".yaml,.yml"
|
||||
ref={inputRef}
|
||||
style={{ display: "none" }}
|
||||
onChange={onFileInput}
|
||||
/>
|
||||
|
||||
<Typography noWrap sx={{ ml: 1 }}>
|
||||
{loading ? "Loading..." : fileName}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default FileInput;
|
@ -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<string | null>(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) => {
|
||||
<TextField
|
||||
{...textFieldProps}
|
||||
label="Name"
|
||||
autoComplete="off"
|
||||
value={form.name}
|
||||
onChange={(e) => setForm({ name: e.target.value })}
|
||||
/>
|
||||
@ -104,6 +112,7 @@ const ProfileNew = (props: Props) => {
|
||||
<TextField
|
||||
{...textFieldProps}
|
||||
label="Descriptions"
|
||||
autoComplete="off"
|
||||
value={form.desc}
|
||||
onChange={(e) => setForm({ desc: e.target.value })}
|
||||
/>
|
||||
@ -112,15 +121,21 @@ const ProfileNew = (props: Props) => {
|
||||
<TextField
|
||||
{...textFieldProps}
|
||||
label="Subscription Url"
|
||||
autoComplete="off"
|
||||
value={form.url}
|
||||
onChange={(e) => setForm({ url: e.target.value })}
|
||||
/>
|
||||
)}
|
||||
|
||||
{form.type === "local" && (
|
||||
<FileInput onChange={(val) => (fileDataRef.current = val)} />
|
||||
)}
|
||||
|
||||
{showOpt && (
|
||||
<TextField
|
||||
{...textFieldProps}
|
||||
label="User Agent"
|
||||
autoComplete="off"
|
||||
value={option.user_agent}
|
||||
onChange={(e) => setOption({ user_agent: e.target.value })}
|
||||
/>
|
||||
|
@ -14,8 +14,11 @@ export async function enhanceProfiles() {
|
||||
return invoke<void>("enhance_profiles");
|
||||
}
|
||||
|
||||
export async function createProfile(item: Partial<CmdType.ProfileItem>) {
|
||||
return invoke<void>("create_profile", { item });
|
||||
export async function createProfile(
|
||||
item: Partial<CmdType.ProfileItem>,
|
||||
fileData?: string | null
|
||||
) {
|
||||
return invoke<void>("create_profile", { item, fileData });
|
||||
}
|
||||
|
||||
export async function viewProfile(index: string) {
|
||||
|
Loading…
Reference in New Issue
Block a user