add_feed_direct

This commit is contained in:
mindfreq 2026-05-05 14:57:36 +02:00
parent 0ab56a7279
commit a5b8968993
7 changed files with 56 additions and 10 deletions

1
src-tauri/Cargo.lock generated
View file

@ -3362,6 +3362,7 @@ dependencies = [
"tauri-plugin-opener",
"thiserror 2.0.18",
"tokio",
"url",
"uuid",
]

View file

@ -30,3 +30,4 @@ thiserror = "2.0.18"
uuid = { version = "1", features = ["v4"] }
scraper = "0.22"
cached = { version = "0.54", features = ["async"] }
url = "2"

View file

@ -1,5 +1,7 @@
use cached::proc_macro::cached;
use feed_rs::parser;
use reqwest::header::{ACCEPT, ACCEPT_ENCODING};
use tauri::utils::config::parse;
use crate::client::CLIENT;
use crate::config::feed_config::Feed;
@ -44,14 +46,33 @@ pub async fn add_feed(url: &str) -> Result<Feed, Error> {
let icon = feed.favicon.as_deref().unwrap_or("favicon.ico");
Feed::add(title, &feed.url, &feed.feed_url, icon)
}
None => {
Err(Error::MissingField(
"No RSS/Atom/JSON feed found at this URL".into(),
))
}
None => Err(Error::MissingField(
"No RSS/Atom/JSON feed found at this URL".into(),
)),
}
}
#[tauri::command]
pub async fn add_feed_direct(feed_url: &str) -> Result<Feed, Error> {
// This function for full path, eg: 'https://blog.rust-lang.org/feed'
//
let xml = fetch_text(
feed_url,
"application/rss+xml,application/atom+xml,application/xml,text/xml,*/*;q=0.8",
)
.await?;
let feed = parser::parse(xml.as_bytes())?;
let title = feed.title.map(|t| t.content).unwrap_or("no title".into());
let icon = feed.icon.map(|i| i.uri).unwrap_or("defult.png".into());
let url = url::Url::parse(feed_url)
.unwrap()
.origin()
.ascii_serialization();
Feed::add(&title, &url, feed_url, &icon)
}
#[tauri::command]
pub async fn get_feeds() -> Result<Vec<Feed>, Error> {
let feeds = Feed::get_all()?;

View file

@ -3,7 +3,7 @@ pub mod commands;
pub mod config;
pub mod parser;
use commands::feed::{add_feed, get_entry, get_feed_item, get_feeds, remove_feed};
use commands::feed::{add_feed, add_feed_direct, get_entry, get_feed_item, get_feeds, remove_feed};
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
@ -15,10 +15,11 @@ pub fn run() {
.plugin(tauri_plugin_opener::init())
.invoke_handler(tauri::generate_handler![
add_feed,
add_feed_direct,
get_feeds,
remove_feed,
get_feed_item,
get_entry
get_entry,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");

View file

@ -179,7 +179,7 @@ fn extract_favicon(document: &Html, base_url: &str) -> Option<String> {
}
fn resolve_url(base: &str, href: &str) -> String {
if href.starts_with("http://") || href.starts_with("https://") {
if href.starts_with("http") {
href.to_string()
} else if href.starts_with("//") {
format!("https:{}", href)

View file

@ -44,6 +44,9 @@ export const commands = {
addFeed: (url: string): Promise<Feed> =>
tauriInvoke<Feed>("add_feed", { url }),
addFeedDirect: (feedUrl: string): Promise<Feed> =>
tauriInvoke<Feed>("add_feed_direct", { feedUrl }),
getFeeds: (): Promise<Feed[]> =>
tauriInvoke<Feed[]>("get_feeds"),

View file

@ -3,6 +3,22 @@ import { commands, Feed, FeedItem } from "../bindings";
import { UiError, View } from "../types";
import { extractErrorMessage, normalizeErrorMessage } from "../utils";
const DIRECT_FEED_PATH_PATTERN = /(feed|rss|atom)(\.[a-z0-9]+)?$/i;
const DIRECT_FEED_EXTENSION_PATTERN = /\.(rss|xml|atom|rdf|json)$/i;
const isLikelyDirectFeedUrl = (value: string): boolean => {
try {
const { pathname } = new URL(value);
const normalizedPath = pathname.toLowerCase();
return (
DIRECT_FEED_PATH_PATTERN.test(normalizedPath) ||
DIRECT_FEED_EXTENSION_PATTERN.test(normalizedPath)
);
} catch {
return false;
}
};
export function useFeeds() {
const [feeds, setFeeds] = useState<Feed[]>([]);
const [loadingFeeds, setLoadingFeeds] = useState(true);
@ -76,7 +92,10 @@ export function useFeeds() {
const addFeed = useCallback(
async (url: string) => {
const feed = await commands.addFeed(url.trim());
const normalizedUrl = url.trim();
const feed = isLikelyDirectFeedUrl(normalizedUrl)
? await commands.addFeedDirect(normalizedUrl)
: await commands.addFeed(normalizedUrl);
setFeeds((prev) => [...prev, feed]);
return feed;
},