feature: cache article images for offline reading

After fetching articles, all remote images referenced in article content
are downloaded to ~/.local/share/net.jeena.FeedTheMonkey/images/ and
their src attributes rewritten to file:// URIs. Subsequent loads of the
same article (including from the cache on the next startup) display
images without a network connection.

Metered-connection awareness: image caching is skipped automatically
when GIO reports the network connection as metered, regardless of the
preference setting.

A "Cache Images" toggle in Preferences lets the user disable caching
entirely (stored in the cache-images GSettings key).

After each refresh, images no longer referenced by any article in the
current unread list are deleted from the cache directory to prevent
unbounded disk growth.
This commit is contained in:
Jeena 2026-03-21 01:19:49 +00:00
parent 183191727b
commit fda441bebd
9 changed files with 186 additions and 12 deletions

View file

@ -584,9 +584,22 @@ pub mod imp {
}
let saved_id = self.current_article_id.borrow().clone();
let settings = gio::Settings::new("net.jeena.FeedTheMonkey");
let cache_images = settings.boolean("cache-images")
&& !gio::NetworkMonitor::default().is_network_metered();
let win_weak = self.obj().downgrade();
crate::runtime::spawn(
async move { api.fetch_unread().await },
async move {
let articles = api.fetch_unread().await?;
let articles = if cache_images {
crate::image_cache::process(articles).await
} else {
articles
};
Ok::<_, String>(articles)
},
move |result| {
let Some(win) = win_weak.upgrade() else { return };
let imp = win.imp();
@ -602,9 +615,10 @@ pub mod imp {
store.append(&ArticleObject::new(a.clone()));
}
// Save cache with updated article list.
// Save cache and clean up unreferenced images.
let sel_id = saved_id.as_deref().unwrap_or("");
crate::cache::save(&articles, sel_id);
crate::image_cache::cleanup(&articles);
if store.n_items() == 0 {
imp.sidebar_content.set_visible_child_name("empty");