fixes some issue

This commit is contained in:
mindfreq 2026-05-06 18:44:08 +02:00
parent a5b8968993
commit c43a9ca800
7 changed files with 74 additions and 23 deletions

View file

@ -1,3 +1,7 @@
# Rufeed
Rufeed is a lightweight desktop feed reader built with Tauri and React.
## Screenshot
![Rufeed app screenshot](./public/screenshot.png)

BIN
public/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

View file

@ -1,7 +1,6 @@
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;
@ -65,11 +64,15 @@ pub async fn add_feed_direct(feed_url: &str) -> Result<Feed, Error> {
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();
let icon = feed
.icon
.map(|i| i.uri)
.unwrap_or(format!("{url}/favicon.ico"));
Feed::add(&title, &url, feed_url, &icon)
}

View file

@ -21,19 +21,19 @@ impl FeedItem {
title: entry
.title
.map(|t| t.content)
.ok_or(Error::MissingField("title".into()))?,
.unwrap_or("no_title".to_string()),
published: entry
.published
.or(entry.updated)
.map(|d| d.to_rfc3339())
.ok_or(Error::MissingField("published".into()))?,
.unwrap_or("no_published".to_string()),
url: entry
.links
.iter()
.find(|l| l.rel.as_deref() == Some("alternate"))
.or_else(|| entry.links.first())
.map(|l| l.href.clone())
.ok_or(Error::MissingField("url".into()))?,
.unwrap_or("no_url".to_string()),
};
items.push(item);
}
@ -79,18 +79,18 @@ impl FeedEntry {
title: entry
.title
.map(|t| t.content)
.ok_or(Error::MissingField("title".into()))?,
.unwrap_or("no_title".to_string()),
url,
published: entry
.published
.or(entry.updated)
.map(|d| d.to_rfc3339())
.ok_or(Error::MissingField("published".into()))?,
.unwrap_or("no_published".to_string()),
updated: entry
.updated
.or(entry.published)
.map(|d| d.to_rfc3339())
.ok_or(Error::MissingField("updated".into()))?,
.unwrap_or("no_updated".to_string()),
summary,
content,
authors: entry

View file

@ -59,8 +59,7 @@ export default function App() {
);
const handleRemoveFeed = useCallback(
async (feed: Feed, e: React.MouseEvent) => {
e.stopPropagation();
async (feed: Feed) => {
try {
await removeFeed(feed);
} catch (err) {

View file

@ -1,12 +1,12 @@
import { memo } from "react";
import { Rss, Trash2 } from "lucide-react";
import { memo, useCallback, useState, type MouseEvent } from "react";
import { Check, Rss, Trash2, X } from "lucide-react";
import { Feed } from "../../bindings";
interface FeedListItemProps {
feed: Feed;
isSelected: boolean;
onSelect: (feed: Feed) => void;
onRemove: (feed: Feed, e: React.MouseEvent) => void;
onRemove: (feed: Feed) => void;
}
export const FeedListItem = memo(function FeedListItem({
@ -15,6 +15,27 @@ export const FeedListItem = memo(function FeedListItem({
onSelect,
onRemove,
}: FeedListItemProps) {
const [isConfirmingDelete, setIsConfirmingDelete] = useState(false);
const handleToggleDeleteConfirm = useCallback((e: MouseEvent) => {
e.stopPropagation();
setIsConfirmingDelete((value) => !value);
}, []);
const handleCancelDelete = useCallback((e: MouseEvent) => {
e.stopPropagation();
setIsConfirmingDelete(false);
}, []);
const handleConfirmDelete = useCallback(
(e: MouseEvent) => {
e.stopPropagation();
onRemove(feed);
setIsConfirmingDelete(false);
},
[feed, onRemove]
);
return (
<button
onClick={() => onSelect(feed)}
@ -36,8 +57,31 @@ export const FeedListItem = memo(function FeedListItem({
<span dir="auto" className="min-w-0 flex-1 truncate font-medium">
{feed.title || feed.url}
</span>
{isConfirmingDelete ? (
<span className="inline-flex items-center gap-1">
<span
onClick={(e) => onRemove(feed, e)}
onClick={handleConfirmDelete}
role="button"
aria-label="Confirm removing feed"
title="Confirm remove"
className="inline-flex items-center gap-1 rounded-md border border-destructive/50 bg-destructive/10 px-2 py-1 text-[11px] font-medium text-destructive transition-all hover:bg-destructive/20"
>
<Check className="h-3 w-3" />
Confirm
</span>
<span
onClick={handleCancelDelete}
role="button"
aria-label="Cancel removing feed"
title="Cancel remove"
className="inline-flex items-center justify-center rounded-md p-1 text-muted-foreground transition-all hover:bg-muted hover:text-foreground"
>
<X className="h-3 w-3" />
</span>
</span>
) : (
<span
onClick={handleToggleDeleteConfirm}
role="button"
aria-label="Remove feed"
title="Remove feed"
@ -45,6 +89,7 @@ export const FeedListItem = memo(function FeedListItem({
>
<Trash2 className="h-3 w-3" />
</span>
)}
</button>
);
});

View file

@ -19,7 +19,7 @@ interface SidebarContentProps {
loading: boolean;
selectedFeedId?: string;
onSelectFeed: (feed: Feed) => void;
onRemoveFeed: (feed: Feed, e: React.MouseEvent) => void;
onRemoveFeed: (feed: Feed) => void;
onAddFeed: (url: string) => Promise<void>;
onRefresh: () => void;
onHideSidebar?: () => void;