feat: refactor rust commands
This commit is contained in:
@@ -7,12 +7,12 @@ function isImage(url: string) {
|
|||||||
|
|
||||||
export function LinkPreview({ url }: { url: string }) {
|
export function LinkPreview({ url }: { url: string }) {
|
||||||
const domain = new URL(url);
|
const domain = new URL(url);
|
||||||
const { status, data } = useOpenGraph(url);
|
const { isLoading, isError, data } = useOpenGraph(url);
|
||||||
|
|
||||||
if (status === "pending") {
|
if (isLoading) {
|
||||||
return (
|
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="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="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-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" />
|
<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 (
|
return (
|
||||||
<Link
|
<Link
|
||||||
to={url}
|
to={url}
|
||||||
@@ -50,7 +63,7 @@ export function LinkPreview({ url }: { url: string }) {
|
|||||||
alt={url}
|
alt={url}
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
decoding="async"
|
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}
|
) : null}
|
||||||
<div className="flex flex-col items-start p-3">
|
<div className="flex flex-col items-start p-3">
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import { useQuery } from "@tanstack/react-query";
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
|
|
||||||
export function useOpenGraph(url: string) {
|
export function useOpenGraph(url: string) {
|
||||||
const { status, data, error } = useQuery({
|
const { isLoading, isError, data } = useQuery({
|
||||||
queryKey: ["opg", url],
|
queryKey: ["opg", url],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const res: Opengraph = await invoke("opengraph", { url });
|
const res: Opengraph = await invoke("fetch_opg", { url });
|
||||||
if (!res) {
|
if (!res) {
|
||||||
throw new Error("fetch preview failed");
|
throw new Error("fetch preview failed");
|
||||||
}
|
}
|
||||||
@@ -19,8 +19,8 @@ export function useOpenGraph(url: string) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status,
|
isLoading,
|
||||||
|
isError,
|
||||||
data,
|
data,
|
||||||
error,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
5
src-tauri/Cargo.lock
generated
5
src-tauri/Cargo.lock
generated
@@ -6064,15 +6064,16 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpage"
|
name = "webpage"
|
||||||
version = "1.6.0"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8598785beeb5af95abe95e7bb20c7e747d1188347080d6811d5a56d2b9a5f368"
|
checksum = "3fb86b12e58d490a99867f561ce8466ffa7b73e24d015a8e7f5bc111d4424ba2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"curl",
|
"curl",
|
||||||
"html5ever",
|
"html5ever",
|
||||||
"markup5ever_rcdom",
|
"markup5ever_rcdom",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ authors = ["Ren Amamiya"]
|
|||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
repository = "https://github.com/luminous-devs/lume"
|
repository = "https://github.com/luminous-devs/lume"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.66"
|
rust-version = "1.70"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "2.0.0-alpha", features = [] }
|
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 = [
|
sqlx-cli = { version = "0.7.0", default-features = false, features = [
|
||||||
"sqlite",
|
"sqlite",
|
||||||
] }
|
] }
|
||||||
webpage = { version = "1.6.0", features = ["serde"] }
|
webpage = { version = "2.0", features = ["serde"] }
|
||||||
|
|
||||||
|
[target.'cfg(not(target_os = "linux"))'.dependencies]
|
||||||
keyring = "2"
|
keyring = "2"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
keyring = { version = "2", default_features = false, features = ["linux-secret-service"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# by default Tauri runs in production mode
|
# 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
|
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
|
||||||
|
|||||||
@@ -1,140 +1,3 @@
|
|||||||
use keyring::Entry;
|
pub mod folder;
|
||||||
use std::process::Command;
|
pub mod opg;
|
||||||
use std::time::Duration;
|
pub mod secret;
|
||||||
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(())
|
|
||||||
}
|
|
||||||
|
|||||||
48
src-tauri/src/commands/folder.rs
Normal file
48
src-tauri/src/commands/folder.rs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
50
src-tauri/src/commands/opg.rs
Normal file
50
src-tauri/src/commands/opg.rs
Normal 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())
|
||||||
|
}
|
||||||
25
src-tauri/src/commands/secret.rs
Normal file
25
src-tauri/src/commands/secret.rs
Normal 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(())
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
windows_subsystem = "windows"
|
windows_subsystem = "windows"
|
||||||
)]
|
)]
|
||||||
|
|
||||||
mod commands;
|
pub mod commands;
|
||||||
|
|
||||||
use tauri_plugin_autostart::MacosLauncher;
|
use tauri_plugin_autostart::MacosLauncher;
|
||||||
use tauri_plugin_sql::{Migration, MigrationKind};
|
use tauri_plugin_sql::{Migration, MigrationKind};
|
||||||
@@ -23,14 +23,12 @@ fn main() {
|
|||||||
tauri_plugin_sql::Builder::default()
|
tauri_plugin_sql::Builder::default()
|
||||||
.add_migrations(
|
.add_migrations(
|
||||||
"sqlite:lume_v3.db",
|
"sqlite:lume_v3.db",
|
||||||
vec![
|
vec![Migration {
|
||||||
Migration {
|
version: 20230418013219,
|
||||||
version: 20230418013219,
|
description: "initial data",
|
||||||
description: "initial data",
|
sql: include_str!("../migrations/20230418013219_initial_data.sql"),
|
||||||
sql: include_str!("../migrations/20230418013219_initial_data.sql"),
|
kind: MigrationKind::Up,
|
||||||
kind: MigrationKind::Up,
|
}],
|
||||||
},
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
@@ -50,11 +48,11 @@ fn main() {
|
|||||||
Some(vec![]),
|
Some(vec![]),
|
||||||
))
|
))
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
commands::opengraph,
|
commands::secret::secure_save,
|
||||||
commands::secure_save,
|
commands::secret::secure_load,
|
||||||
commands::secure_load,
|
commands::secret::secure_remove,
|
||||||
commands::secure_remove,
|
commands::folder::show_in_folder,
|
||||||
commands::show_in_folder,
|
commands::opg::fetch_opg,
|
||||||
])
|
])
|
||||||
.run(ctx)
|
.run(ctx)
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
|
|||||||
Reference in New Issue
Block a user