feat: refactor rust commands

This commit is contained in:
reya 2024-01-31 08:20:39 +07:00
parent b7f4af7883
commit 02e0309a41
9 changed files with 169 additions and 166 deletions

View File

@ -7,12 +7,12 @@ function isImage(url: string) {
export function LinkPreview({ url }: { url: string }) {
const domain = new URL(url);
const { status, data } = useOpenGraph(url);
const { isLoading, isError, data } = useOpenGraph(url);
if (status === "pending") {
if (isLoading) {
return (
<div className="flex flex-col w-full mt-1 mb-2.5 rounded-xl overflow-hidden bg-neutral-100 dark:bg-neutral-900 border border-black/5 dark:border-white/5">
<div className="w-full h-48 animate-pulse bg-neutral-300 dark:bg-neutral-700" />
<div className="w-full h-48 shrink-0 animate-pulse bg-neutral-300 dark:bg-neutral-700" />
<div className="flex flex-col gap-2 px-3 py-3">
<div className="w-2/3 h-3 rounded animate-pulse bg-neutral-300 dark:bg-neutral-700" />
<div className="w-3/4 h-3 rounded animate-pulse bg-neutral-300 dark:bg-neutral-700" />
@ -37,6 +37,19 @@ export function LinkPreview({ url }: { url: string }) {
);
}
if (isError) {
return (
<Link
to={url}
target="_blank"
rel="noreferrer"
className="text-blue-500 hover:text-blue-600"
>
{url}
</Link>
);
}
return (
<Link
to={url}
@ -50,7 +63,7 @@ export function LinkPreview({ url }: { url: string }) {
alt={url}
loading="lazy"
decoding="async"
className="object-cover w-full h-48 bg-white rounded-t-lg"
className="object-cover w-full h-48 shrink-0 bg-white rounded-t-lg"
/>
) : null}
<div className="flex flex-col items-start p-3">

View File

@ -3,10 +3,10 @@ import { useQuery } from "@tanstack/react-query";
import { invoke } from "@tauri-apps/api/core";
export function useOpenGraph(url: string) {
const { status, data, error } = useQuery({
const { isLoading, isError, data } = useQuery({
queryKey: ["opg", url],
queryFn: async () => {
const res: Opengraph = await invoke("opengraph", { url });
const res: Opengraph = await invoke("fetch_opg", { url });
if (!res) {
throw new Error("fetch preview failed");
}
@ -19,8 +19,8 @@ export function useOpenGraph(url: string) {
});
return {
status,
isLoading,
isError,
data,
error,
};
}

5
src-tauri/Cargo.lock generated
View File

@ -6064,15 +6064,16 @@ dependencies = [
[[package]]
name = "webpage"
version = "1.6.0"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8598785beeb5af95abe95e7bb20c7e747d1188347080d6811d5a56d2b9a5f368"
checksum = "3fb86b12e58d490a99867f561ce8466ffa7b73e24d015a8e7f5bc111d4424ba2"
dependencies = [
"curl",
"html5ever",
"markup5ever_rcdom",
"serde",
"serde_json",
"url",
]
[[package]]

View File

@ -6,7 +6,7 @@ authors = ["Ren Amamiya"]
license = "GPL-3.0"
repository = "https://github.com/luminous-devs/lume"
edition = "2021"
rust-version = "1.66"
rust-version = "1.70"
[build-dependencies]
tauri-build = { version = "2.0.0-alpha", features = [] }
@ -40,9 +40,14 @@ tauri-plugin-sql = {version="2.0.0-alpha", features = [
sqlx-cli = { version = "0.7.0", default-features = false, features = [
"sqlite",
] }
webpage = { version = "1.6.0", features = ["serde"] }
webpage = { version = "2.0", features = ["serde"] }
[target.'cfg(not(target_os = "linux"))'.dependencies]
keyring = "2"
[target.'cfg(target_os = "linux")'.dependencies]
keyring = { version = "2", default_features = false, features = ["linux-secret-service"] }
[features]
# by default Tauri runs in production mode
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL

View File

@ -1,140 +1,3 @@
use keyring::Entry;
use std::process::Command;
use std::time::Duration;
use webpage::{Webpage, WebpageOptions};
#[tauri::command]
pub async fn show_in_folder(path: String) {
#[cfg(target_os = "windows")]
{
Command::new("explorer")
.args(["/select,", &path]) // The comma after select is not a typo
.spawn()
.unwrap();
}
#[cfg(target_os = "linux")]
{
use std::fs::metadata;
use std::path::PathBuf;
if path.contains(",") {
// see https://gitlab.freedesktop.org/dbus/dbus/-/issues/76
let new_path = match metadata(&path).unwrap().is_dir() {
true => path,
false => {
let mut path2 = PathBuf::from(path);
path2.pop();
path2.into_os_string().into_string().unwrap()
}
};
Command::new("xdg-open").arg(&new_path).spawn().unwrap();
} else {
Command::new("dbus-send")
.args([
"--session",
"--dest=org.freedesktop.FileManager1",
"--type=method_call",
"/org/freedesktop/FileManager1",
"org.freedesktop.FileManager1.ShowItems",
format!("array:string:file://{path}").as_str(),
"string:\"\"",
])
.spawn()
.unwrap();
}
}
#[cfg(target_os = "macos")]
{
Command::new("open").args(["-R", &path]).spawn().unwrap();
}
}
#[derive(serde::Serialize)]
pub struct OpenGraphResponse {
title: String,
description: String,
url: String,
image: String,
}
pub async fn fetch_opengraph(url: String) -> OpenGraphResponse {
let options = WebpageOptions {
allow_insecure: false,
max_redirections: 3,
timeout: Duration::from_secs(15),
useragent: "lume - desktop app".to_string(),
..Default::default()
};
let result = match Webpage::from_url(&url, options) {
Ok(webpage) => webpage,
Err(_) => {
return OpenGraphResponse {
title: "".to_string(),
description: "".to_string(),
url: "".to_string(),
image: "".to_string(),
}
}
};
let html = result.html;
return OpenGraphResponse {
title: html
.opengraph
.properties
.get("title")
.cloned()
.unwrap_or_default(),
description: html
.opengraph
.properties
.get("description")
.cloned()
.unwrap_or_default(),
url: html
.opengraph
.properties
.get("url")
.cloned()
.unwrap_or_default(),
image: html
.opengraph
.images
.get(0)
.and_then(|i| Some(i.url.clone()))
.unwrap_or_default(),
};
}
#[tauri::command]
pub async fn opengraph(url: String) -> OpenGraphResponse {
let result = fetch_opengraph(url).await;
return result;
}
#[tauri::command]
pub fn secure_save(key: String, value: String) -> Result<(), ()> {
let entry = Entry::new("Lume", &key).expect("Failed to create entry");
let _ = entry.set_password(&value);
Ok(())
}
#[tauri::command]
pub fn secure_load(key: String) -> Result<String, String> {
let entry = Entry::new("Lume", &key).expect("Failed to create entry");
if let Ok(password) = entry.get_password() {
Ok(password.into())
} else {
Err("Not found".into())
}
}
#[tauri::command]
pub fn secure_remove(key: String) -> Result<(), ()> {
let entry = Entry::new("Lume", &key).expect("Failed to create entry");
let _ = entry.delete_password();
Ok(())
}
pub mod folder;
pub mod opg;
pub mod secret;

View File

@ -0,0 +1,48 @@
use std::process::Command;
#[tauri::command]
pub async fn show_in_folder(path: String) {
#[cfg(target_os = "windows")]
{
Command::new("explorer")
.args(["/select,", &path]) // The comma after select is not a typo
.spawn()
.unwrap();
}
#[cfg(target_os = "linux")]
{
use std::fs::metadata;
use std::path::PathBuf;
if path.contains(",") {
// see https://gitlab.freedesktop.org/dbus/dbus/-/issues/76
let new_path = match metadata(&path).unwrap().is_dir() {
true => path,
false => {
let mut path2 = PathBuf::from(path);
path2.pop();
path2.into_os_string().into_string().unwrap()
}
};
Command::new("xdg-open").arg(&new_path).spawn().unwrap();
} else {
Command::new("dbus-send")
.args([
"--session",
"--dest=org.freedesktop.FileManager1",
"--type=method_call",
"/org/freedesktop/FileManager1",
"org.freedesktop.FileManager1.ShowItems",
format!("array:string:file://{path}").as_str(),
"string:\"\"",
])
.spawn()
.unwrap();
}
}
#[cfg(target_os = "macos")]
{
Command::new("open").args(["-R", &path]).spawn().unwrap();
}
}

View File

@ -0,0 +1,50 @@
use std::time::Duration;
use webpage::{Webpage, WebpageOptions};
#[derive(serde::Serialize)]
pub struct OpenGraphResponse {
title: String,
description: String,
url: String,
image: String,
}
#[tauri::command]
pub fn fetch_opg(url: String) -> Result<OpenGraphResponse, ()> {
let mut options = WebpageOptions::default();
options.allow_insecure = true;
options.max_redirections = 3;
options.timeout = Duration::from_secs(15);
let info = Webpage::from_url(&url, options).expect("Failed");
let html = info.html;
let result = OpenGraphResponse {
title: html
.opengraph
.properties
.get("title")
.cloned()
.unwrap_or_default(),
description: html
.opengraph
.properties
.get("description")
.cloned()
.unwrap_or_default(),
url: html
.opengraph
.properties
.get("url")
.cloned()
.unwrap_or_default(),
image: html
.opengraph
.images
.get(0)
.and_then(|i| Some(i.url.clone()))
.unwrap_or_default(),
};
Ok(result.into())
}

View File

@ -0,0 +1,25 @@
use keyring::Entry;
#[tauri::command]
pub fn secure_save(key: String, value: String) -> Result<(), ()> {
let entry = Entry::new("Lume", &key).expect("Failed to create entry");
let _ = entry.set_password(&value);
Ok(())
}
#[tauri::command]
pub fn secure_load(key: String) -> Result<String, String> {
let entry = Entry::new("Lume", &key).expect("Failed to create entry");
if let Ok(password) = entry.get_password() {
Ok(password.into())
} else {
Err("Not found".into())
}
}
#[tauri::command]
pub fn secure_remove(key: String) -> Result<(), ()> {
let entry = Entry::new("Lume", &key).expect("Failed to remove entry");
let _ = entry.delete_password();
Ok(())
}

View File

@ -3,7 +3,7 @@
windows_subsystem = "windows"
)]
mod commands;
pub mod commands;
use tauri_plugin_autostart::MacosLauncher;
use tauri_plugin_sql::{Migration, MigrationKind};
@ -23,14 +23,12 @@ fn main() {
tauri_plugin_sql::Builder::default()
.add_migrations(
"sqlite:lume_v3.db",
vec![
Migration {
version: 20230418013219,
description: "initial data",
sql: include_str!("../migrations/20230418013219_initial_data.sql"),
kind: MigrationKind::Up,
},
],
vec![Migration {
version: 20230418013219,
description: "initial data",
sql: include_str!("../migrations/20230418013219_initial_data.sql"),
kind: MigrationKind::Up,
}],
)
.build(),
)
@ -50,11 +48,11 @@ fn main() {
Some(vec![]),
))
.invoke_handler(tauri::generate_handler![
commands::opengraph,
commands::secure_save,
commands::secure_load,
commands::secure_remove,
commands::show_in_folder,
commands::secret::secure_save,
commands::secret::secure_load,
commands::secret::secure_remove,
commands::folder::show_in_folder,
commands::opg::fetch_opg,
])
.run(ctx)
.expect("error while running tauri application");