fixes some issue
This commit is contained in:
parent
a5b8968993
commit
c43a9ca800
7 changed files with 74 additions and 23 deletions
|
|
@ -1,3 +1,7 @@
|
||||||
# Rufeed
|
# Rufeed
|
||||||
|
|
||||||
Rufeed is a lightweight desktop feed reader built with Tauri and React.
|
Rufeed is a lightweight desktop feed reader built with Tauri and React.
|
||||||
|
|
||||||
|
## Screenshot
|
||||||
|
|
||||||
|

|
||||||
|
|
|
||||||
BIN
public/screenshot.png
Normal file
BIN
public/screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 169 KiB |
|
|
@ -1,7 +1,6 @@
|
||||||
use cached::proc_macro::cached;
|
use cached::proc_macro::cached;
|
||||||
use feed_rs::parser;
|
use feed_rs::parser;
|
||||||
use reqwest::header::{ACCEPT, ACCEPT_ENCODING};
|
use reqwest::header::{ACCEPT, ACCEPT_ENCODING};
|
||||||
use tauri::utils::config::parse;
|
|
||||||
|
|
||||||
use crate::client::CLIENT;
|
use crate::client::CLIENT;
|
||||||
use crate::config::feed_config::Feed;
|
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 feed = parser::parse(xml.as_bytes())?;
|
||||||
|
|
||||||
let title = feed.title.map(|t| t.content).unwrap_or("no title".into());
|
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)
|
let url = url::Url::parse(feed_url)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.origin()
|
.origin()
|
||||||
.ascii_serialization();
|
.ascii_serialization();
|
||||||
|
|
||||||
|
let icon = feed
|
||||||
|
.icon
|
||||||
|
.map(|i| i.uri)
|
||||||
|
.unwrap_or(format!("{url}/favicon.ico"));
|
||||||
Feed::add(&title, &url, feed_url, &icon)
|
Feed::add(&title, &url, feed_url, &icon)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,19 +21,19 @@ impl FeedItem {
|
||||||
title: entry
|
title: entry
|
||||||
.title
|
.title
|
||||||
.map(|t| t.content)
|
.map(|t| t.content)
|
||||||
.ok_or(Error::MissingField("title".into()))?,
|
.unwrap_or("no_title".to_string()),
|
||||||
published: entry
|
published: entry
|
||||||
.published
|
.published
|
||||||
.or(entry.updated)
|
.or(entry.updated)
|
||||||
.map(|d| d.to_rfc3339())
|
.map(|d| d.to_rfc3339())
|
||||||
.ok_or(Error::MissingField("published".into()))?,
|
.unwrap_or("no_published".to_string()),
|
||||||
url: entry
|
url: entry
|
||||||
.links
|
.links
|
||||||
.iter()
|
.iter()
|
||||||
.find(|l| l.rel.as_deref() == Some("alternate"))
|
.find(|l| l.rel.as_deref() == Some("alternate"))
|
||||||
.or_else(|| entry.links.first())
|
.or_else(|| entry.links.first())
|
||||||
.map(|l| l.href.clone())
|
.map(|l| l.href.clone())
|
||||||
.ok_or(Error::MissingField("url".into()))?,
|
.unwrap_or("no_url".to_string()),
|
||||||
};
|
};
|
||||||
items.push(item);
|
items.push(item);
|
||||||
}
|
}
|
||||||
|
|
@ -79,18 +79,18 @@ impl FeedEntry {
|
||||||
title: entry
|
title: entry
|
||||||
.title
|
.title
|
||||||
.map(|t| t.content)
|
.map(|t| t.content)
|
||||||
.ok_or(Error::MissingField("title".into()))?,
|
.unwrap_or("no_title".to_string()),
|
||||||
url,
|
url,
|
||||||
published: entry
|
published: entry
|
||||||
.published
|
.published
|
||||||
.or(entry.updated)
|
.or(entry.updated)
|
||||||
.map(|d| d.to_rfc3339())
|
.map(|d| d.to_rfc3339())
|
||||||
.ok_or(Error::MissingField("published".into()))?,
|
.unwrap_or("no_published".to_string()),
|
||||||
updated: entry
|
updated: entry
|
||||||
.updated
|
.updated
|
||||||
.or(entry.published)
|
.or(entry.published)
|
||||||
.map(|d| d.to_rfc3339())
|
.map(|d| d.to_rfc3339())
|
||||||
.ok_or(Error::MissingField("updated".into()))?,
|
.unwrap_or("no_updated".to_string()),
|
||||||
summary,
|
summary,
|
||||||
content,
|
content,
|
||||||
authors: entry
|
authors: entry
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,7 @@ export default function App() {
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleRemoveFeed = useCallback(
|
const handleRemoveFeed = useCallback(
|
||||||
async (feed: Feed, e: React.MouseEvent) => {
|
async (feed: Feed) => {
|
||||||
e.stopPropagation();
|
|
||||||
try {
|
try {
|
||||||
await removeFeed(feed);
|
await removeFeed(feed);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { memo } from "react";
|
import { memo, useCallback, useState, type MouseEvent } from "react";
|
||||||
import { Rss, Trash2 } from "lucide-react";
|
import { Check, Rss, Trash2, X } from "lucide-react";
|
||||||
import { Feed } from "../../bindings";
|
import { Feed } from "../../bindings";
|
||||||
|
|
||||||
interface FeedListItemProps {
|
interface FeedListItemProps {
|
||||||
feed: Feed;
|
feed: Feed;
|
||||||
isSelected: boolean;
|
isSelected: boolean;
|
||||||
onSelect: (feed: Feed) => void;
|
onSelect: (feed: Feed) => void;
|
||||||
onRemove: (feed: Feed, e: React.MouseEvent) => void;
|
onRemove: (feed: Feed) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FeedListItem = memo(function FeedListItem({
|
export const FeedListItem = memo(function FeedListItem({
|
||||||
|
|
@ -15,6 +15,27 @@ export const FeedListItem = memo(function FeedListItem({
|
||||||
onSelect,
|
onSelect,
|
||||||
onRemove,
|
onRemove,
|
||||||
}: FeedListItemProps) {
|
}: 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 (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={() => onSelect(feed)}
|
onClick={() => onSelect(feed)}
|
||||||
|
|
@ -36,15 +57,39 @@ export const FeedListItem = memo(function FeedListItem({
|
||||||
<span dir="auto" className="min-w-0 flex-1 truncate font-medium">
|
<span dir="auto" className="min-w-0 flex-1 truncate font-medium">
|
||||||
{feed.title || feed.url}
|
{feed.title || feed.url}
|
||||||
</span>
|
</span>
|
||||||
<span
|
{isConfirmingDelete ? (
|
||||||
onClick={(e) => onRemove(feed, e)}
|
<span className="inline-flex items-center gap-1">
|
||||||
role="button"
|
<span
|
||||||
aria-label="Remove feed"
|
onClick={handleConfirmDelete}
|
||||||
title="Remove feed"
|
role="button"
|
||||||
className="inline-flex items-center justify-center rounded-md p-1 opacity-0 transition-all hover:bg-destructive/10 hover:text-destructive group-hover:opacity-100"
|
aria-label="Confirm removing feed"
|
||||||
>
|
title="Confirm remove"
|
||||||
<Trash2 className="h-3 w-3" />
|
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"
|
||||||
</span>
|
>
|
||||||
|
<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"
|
||||||
|
className="inline-flex items-center justify-center rounded-md p-1 opacity-0 transition-all hover:bg-destructive/10 hover:text-destructive group-hover:opacity-100"
|
||||||
|
>
|
||||||
|
<Trash2 className="h-3 w-3" />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ interface SidebarContentProps {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
selectedFeedId?: string;
|
selectedFeedId?: string;
|
||||||
onSelectFeed: (feed: Feed) => void;
|
onSelectFeed: (feed: Feed) => void;
|
||||||
onRemoveFeed: (feed: Feed, e: React.MouseEvent) => void;
|
onRemoveFeed: (feed: Feed) => void;
|
||||||
onAddFeed: (url: string) => Promise<void>;
|
onAddFeed: (url: string) => Promise<void>;
|
||||||
onRefresh: () => void;
|
onRefresh: () => void;
|
||||||
onHideSidebar?: () => void;
|
onHideSidebar?: () => void;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue