mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-18 11:13:30 +00:00
re-add link preview
This commit is contained in:
parent
3ebcf4a981
commit
c74a81cfdb
129
src-tauri/Cargo.lock
generated
129
src-tauri/Cargo.lock
generated
@ -1082,6 +1082,36 @@ dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curl"
|
||||
version = "0.4.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22"
|
||||
dependencies = [
|
||||
"curl-sys",
|
||||
"libc",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"socket2 0.4.9",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curl-sys"
|
||||
version = "0.4.65+curl-8.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "961ba061c9ef2fe34bbd12b807152d96f0badd2bebe7b90ce6c8c8b7572a0986"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"libz-sys",
|
||||
"openssl-sys",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "3.2.0"
|
||||
@ -2098,7 +2128,21 @@ checksum = "e5c13fb08e5d4dfc151ee5e88bae63f7773d61852f3bdc73c9f4b9e1bde03148"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mac",
|
||||
"markup5ever",
|
||||
"markup5ever 0.10.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "html5ever"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mac",
|
||||
"markup5ever 0.11.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
@ -2462,7 +2506,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ea8e9c6e031377cff82ee3001dc8026cdf431ed4e2e6b51f98ab8c73484a358"
|
||||
dependencies = [
|
||||
"cssparser",
|
||||
"html5ever",
|
||||
"html5ever 0.25.2",
|
||||
"matches",
|
||||
"selectors",
|
||||
]
|
||||
@ -2511,6 +2555,18 @@ dependencies = [
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "line-wrap"
|
||||
version = "0.1.1"
|
||||
@ -2579,6 +2635,7 @@ dependencies = [
|
||||
"tauri-plugin-store",
|
||||
"tauri-plugin-stronghold",
|
||||
"tauri-plugin-upload",
|
||||
"webpage",
|
||||
"window-shadows",
|
||||
"window-vibrancy",
|
||||
]
|
||||
@ -2619,12 +2676,38 @@ checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd"
|
||||
dependencies = [
|
||||
"log",
|
||||
"phf 0.8.0",
|
||||
"phf_codegen",
|
||||
"phf_codegen 0.8.0",
|
||||
"string_cache",
|
||||
"string_cache_codegen",
|
||||
"tendril",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markup5ever"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
|
||||
dependencies = [
|
||||
"log",
|
||||
"phf 0.10.1",
|
||||
"phf_codegen 0.10.0",
|
||||
"string_cache",
|
||||
"string_cache_codegen",
|
||||
"tendril",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markup5ever_rcdom"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9521dd6750f8e80ee6c53d65e2e4656d7de37064f3a7a5d2d11d05df93839c2"
|
||||
dependencies = [
|
||||
"html5ever 0.26.0",
|
||||
"markup5ever 0.11.0",
|
||||
"tendril",
|
||||
"xml5ever",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
@ -3205,6 +3288,16 @@ dependencies = [
|
||||
"phf_shared 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
|
||||
dependencies = [
|
||||
"phf_generator 0.10.0",
|
||||
"phf_shared 0.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.8.0"
|
||||
@ -4022,7 +4115,7 @@ dependencies = [
|
||||
"log",
|
||||
"matches",
|
||||
"phf 0.8.0",
|
||||
"phf_codegen",
|
||||
"phf_codegen 0.8.0",
|
||||
"precomputed-hash",
|
||||
"servo_arc",
|
||||
"smallvec",
|
||||
@ -5096,7 +5189,7 @@ dependencies = [
|
||||
"dunce",
|
||||
"glob",
|
||||
"heck 0.4.1",
|
||||
"html5ever",
|
||||
"html5ever 0.25.2",
|
||||
"infer",
|
||||
"json-patch",
|
||||
"kuchiki",
|
||||
@ -5754,6 +5847,19 @@ dependencies = [
|
||||
"system-deps 6.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpage"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8598785beeb5af95abe95e7bb20c7e747d1188347080d6811d5a56d2b9a5f368"
|
||||
dependencies = [
|
||||
"curl",
|
||||
"html5ever 0.26.0",
|
||||
"markup5ever_rcdom",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.24.0"
|
||||
@ -6232,7 +6338,7 @@ dependencies = [
|
||||
"gio",
|
||||
"glib",
|
||||
"gtk",
|
||||
"html5ever",
|
||||
"html5ever 0.25.2",
|
||||
"http",
|
||||
"kuchiki",
|
||||
"libc",
|
||||
@ -6305,6 +6411,17 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xml5ever"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4034e1d05af98b51ad7214527730626f019682d797ba38b51689212118d8e650"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mac",
|
||||
"markup5ever 0.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus"
|
||||
version = "3.14.1"
|
||||
|
@ -50,6 +50,7 @@ sqlx-cli = { version = "0.7.0", default-features = false, features = [
|
||||
"sqlite",
|
||||
] }
|
||||
rust-argon2 = "1.0"
|
||||
webpage = { version = "1.6.0", features = ["serde"] }
|
||||
|
||||
[features]
|
||||
# this feature is used for production builds or when `devPath` points to the filesystem
|
||||
|
@ -7,6 +7,8 @@ use tauri::Manager;
|
||||
use tauri_plugin_autostart::MacosLauncher;
|
||||
use tauri_plugin_sql::{Migration, MigrationKind};
|
||||
use window_shadows::set_shadow;
|
||||
use webpage::{Webpage, WebpageOptions};
|
||||
use std::time::Duration;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use window_vibrancy::{apply_vibrancy, NSVisualEffectMaterial};
|
||||
@ -17,6 +19,71 @@ struct Payload {
|
||||
cwd: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
struct OpenGraphResponse {
|
||||
title: String,
|
||||
description: String,
|
||||
url: String,
|
||||
image: String,
|
||||
}
|
||||
|
||||
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]
|
||||
async fn opengraph(url: String) -> OpenGraphResponse {
|
||||
let result = fetch_opengraph(url).await;
|
||||
return result;
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn close_splashscreen(window: tauri::Window) {
|
||||
// Close splashscreen
|
||||
@ -186,7 +253,7 @@ fn main() {
|
||||
}))
|
||||
.plugin(tauri_plugin_upload::init())
|
||||
.plugin(tauri_plugin_store::Builder::default().build())
|
||||
.invoke_handler(tauri::generate_handler![close_splashscreen])
|
||||
.invoke_handler(tauri::generate_handler![close_splashscreen, opengraph])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
@ -15,27 +15,6 @@ import { useEvent } from '@utils/hooks/useEvent';
|
||||
export function ChildNote({ id, root }: { id: string; root?: string }) {
|
||||
const { status, data } = useEvent(id);
|
||||
|
||||
if (status === 'loading') {
|
||||
return (
|
||||
<div className="relative mb-5 overflow-hidden rounded-xl bg-white/10 px-3 py-3 backdrop-blur-xl">
|
||||
<NoteSkeleton />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (status === 'error') {
|
||||
return (
|
||||
<div className="mb-5 flex flex-col gap-1.5 overflow-hidden rounded-xl bg-white/10 px-3 py-3 backdrop-blur-xl">
|
||||
<p className="text-sm font-bold text-white">
|
||||
Lume cannot found the event with ID
|
||||
</p>
|
||||
<div className="inline-flex items-center justify-center rounded-xl border border-dashed border-red-400 bg-red-200/10 p-2">
|
||||
<p className="select-text break-all text-sm font-medium text-red-400">{id}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const renderKind = (event: NDKEvent) => {
|
||||
switch (event.kind) {
|
||||
case NDKKind.Text:
|
||||
@ -49,6 +28,40 @@ export function ChildNote({ id, root }: { id: string; root?: string }) {
|
||||
}
|
||||
};
|
||||
|
||||
if (status === 'loading') {
|
||||
return (
|
||||
<>
|
||||
<div className="absolute bottom-0 left-[18px] h-[calc(100%-3.4rem)] w-0.5 bg-gradient-to-t from-white/20 to-white/10" />
|
||||
<div className="relative mb-5 overflow-hidden">
|
||||
<NoteSkeleton />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (status === 'error') {
|
||||
return (
|
||||
<>
|
||||
<div className="absolute bottom-0 left-[18px] h-[calc(100%-3.4rem)] w-0.5 bg-gradient-to-t from-white/20 to-white/10" />
|
||||
<div className="relative mb-5 flex flex-col">
|
||||
<div className="relative z-10 flex items-start gap-3">
|
||||
<div className="h-11 w-11 rounded-lg bg-black" />
|
||||
<h5 className="truncate font-semibold leading-none text-white">
|
||||
Lume (System)
|
||||
</h5>
|
||||
</div>
|
||||
<div className="-mt-6 flex items-start gap-3">
|
||||
<div className="w-11 shrink-0" />
|
||||
<div className="markdown relative z-20 flex-1 select-text">
|
||||
<p>Event not found, click to open this note via nostr.com</p>
|
||||
<p>{id}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="absolute bottom-0 left-[18px] h-[calc(100%-3.4rem)] w-0.5 bg-gradient-to-t from-white/20 to-white/10" />
|
||||
|
@ -46,7 +46,7 @@ export const MentionNote = memo(function MentionNote({ id }: { id: string }) {
|
||||
|
||||
if (status === 'loading') {
|
||||
return (
|
||||
<div className="mb-2 mt-3 cursor-default rounded-lg bg-white/10 px-3 py-3 backdrop-blur-xl">
|
||||
<div className="mt-3 cursor-default rounded-lg bg-white/10 px-3 py-3 backdrop-blur-xl">
|
||||
<NoteSkeleton />
|
||||
</div>
|
||||
);
|
||||
@ -54,8 +54,11 @@ export const MentionNote = memo(function MentionNote({ id }: { id: string }) {
|
||||
|
||||
if (status === 'error') {
|
||||
return (
|
||||
<div className="mb-2 mt-3 cursor-default rounded-lg bg-white/10 px-3 py-3 backdrop-blur-xl">
|
||||
<p>Can't get event from relay, ID: {id}</p>
|
||||
<div className="mt-3 cursor-default rounded-lg bg-white/10 px-3 py-3 backdrop-blur-xl">
|
||||
<h5 className="mb-1 text-sm font-medium text-red-500">Event not found</h5>
|
||||
<div className="select-text break-all rounded border border-dashed border-red-500 p-2 text-red-500">
|
||||
{id}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { Image } from '@shared/image';
|
||||
|
||||
import { useOpenGraph } from '@utils/hooks/useOpenGraph';
|
||||
@ -20,11 +22,11 @@ export function LinkPreview({ urls }: { urls: string[] }) {
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<a
|
||||
className="flex flex-col rounded-lg"
|
||||
href={urls[0]}
|
||||
<Link
|
||||
to={urls[0]}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="flex flex-col rounded-lg"
|
||||
>
|
||||
{error ? (
|
||||
<div className="flex flex-col gap-2 px-3 py-3">
|
||||
@ -42,22 +44,22 @@ export function LinkPreview({ urls }: { urls: string[] }) {
|
||||
className="h-44 w-full rounded-t-lg object-cover"
|
||||
/>
|
||||
)}
|
||||
<div className="flex flex-col gap-2 px-3 py-3">
|
||||
<h5 className="line-clamp-1 font-medium leading-none text-white">
|
||||
<div className="flex flex-col gap-1 border-t border-white/5 px-3 py-3">
|
||||
<h5 className="line-clamp-1 font-semibold leading-none text-white">
|
||||
{data.title}
|
||||
</h5>
|
||||
{data.description && (
|
||||
<p className="line-clamp-3 break-all text-sm text-white/50">
|
||||
<p className="line-clamp-3 break-words text-sm text-white/50">
|
||||
{data.description}
|
||||
</p>
|
||||
)}
|
||||
<span className="mt-2.5 text-sm leading-none text-white/50">
|
||||
<span className="mt-2.5 text-sm leading-none text-white/80">
|
||||
{domain.hostname}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</a>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@ -5,7 +5,7 @@ import { Opengraph } from '@utils/types';
|
||||
|
||||
export function useOpenGraph(url: string) {
|
||||
const { status, data, error } = useQuery(
|
||||
['preview', url],
|
||||
['opg', url],
|
||||
async () => {
|
||||
const res: Opengraph = await invoke('opengraph', { url });
|
||||
if (!res) {
|
||||
@ -14,10 +14,10 @@ export function useOpenGraph(url: string) {
|
||||
return res;
|
||||
},
|
||||
{
|
||||
staleTime: Infinity,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
refetchOnReconnect: false,
|
||||
staleTime: Infinity,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -45,6 +45,8 @@ export function parser(eventContent: string) {
|
||||
// remove url from original content
|
||||
word = word.replace(word, '');
|
||||
}
|
||||
|
||||
content.links.push(url.toString());
|
||||
}
|
||||
|
||||
// hashtag
|
||||
|
Loading…
Reference in New Issue
Block a user