├── debian ├── rules ├── install ├── changelog ├── postinst ├── copyright └── control ├── images ├── screenshot.png ├── screenshot1.png ├── screenshot2.png ├── screenshot3.png ├── screenshot4.png ├── screenshot5.png ├── screenshot6.png └── screenshot7.png ├── data ├── icons │ ├── bbc-logo.png │ ├── no-image.png │ ├── npr-logo.png │ ├── paperboy.png │ ├── wsj-logo.png │ ├── reddit-logo.png │ ├── foxnews-logo.png │ ├── guardian-logo.png │ ├── nytimes-logo.png │ ├── reuters-logo.png │ ├── 128x128 │ │ └── paperboy.png │ ├── 256x256 │ │ └── paperboy.png │ ├── 512x512 │ │ └── paperboy.png │ ├── bloomberg-logo.png │ └── symbolic │ │ ├── markets-mono-white.svg │ │ ├── 24x24 │ │ ├── markets-mono-white.svg │ │ ├── markets-mono.svg │ │ ├── local-mono.svg │ │ ├── local-mono-white.svg │ │ ├── x-mono.svg │ │ ├── x-mono-white.svg │ │ ├── entertainment-mono-white.svg │ │ ├── entertainment-mono.svg │ │ ├── facebook-mono.svg │ │ ├── facebook-mono-white.svg │ │ ├── myfeed-mono.svg │ │ ├── myfeed-mono-white.svg │ │ ├── saved-mono.svg │ │ ├── saved-mono-white.svg │ │ ├── multiple-mono-white.svg │ │ ├── multiple-mono.svg │ │ ├── lifestyle-mono.svg │ │ ├── lifestyle-mono-white.svg │ │ ├── all-mono-white.svg │ │ ├── all-mono.svg │ │ └── topten-mono.svg │ │ ├── markets-mono.svg │ │ ├── 128x128 │ │ ├── markets-mono.svg │ │ ├── markets-mono-white.svg │ │ ├── local-mono.svg │ │ ├── local-mono-white.svg │ │ ├── entertainment-mono-white.svg │ │ ├── entertainment-mono.svg │ │ ├── myfeed-mono.svg │ │ ├── myfeed-mono-white.svg │ │ ├── saved-mono.svg │ │ ├── saved-mono-white.svg │ │ ├── lifestyle-mono.svg │ │ └── lifestyle-mono-white.svg │ │ ├── local-mono.svg │ │ ├── local-mono-white.svg │ │ ├── x-mono.svg │ │ ├── x-mono-white.svg │ │ ├── entertainment-mono-white.svg │ │ ├── entertainment-mono.svg │ │ ├── facebook-mono.svg │ │ ├── facebook-mono-white.svg │ │ ├── myfeed-mono.svg │ │ ├── myfeed-mono-white.svg │ │ ├── saved-mono.svg │ │ ├── saved-mono-white.svg │ │ ├── lifestyle-mono.svg │ │ ├── lifestyle-mono-white.svg │ │ ├── all-mono-white.svg │ │ └── all-mono.svg ├── resources │ ├── gschemas.compiled │ └── adblock.css └── io.github.thecalamityjoe87.Paperboy.desktop ├── packaging └── appimage │ └── AppDirTemplate │ ├── .DirIcon │ ├── paperboy.png │ ├── usr │ └── share │ │ ├── paperboy │ │ ├── icons │ │ │ ├── bbc-logo.png │ │ │ ├── no-image.png │ │ │ ├── npr-logo.png │ │ │ ├── paperboy.png │ │ │ ├── wsj-logo.png │ │ │ ├── foxnews-logo.png │ │ │ ├── guardian-logo.png │ │ │ ├── nytimes-logo.png │ │ │ ├── reddit-logo.png │ │ │ ├── reuters-logo.png │ │ │ ├── bloomberg-logo.png │ │ │ └── symbolic │ │ │ │ ├── markets-mono-white.svg │ │ │ │ ├── 24x24 │ │ │ │ ├── markets-mono-white.svg │ │ │ │ ├── markets-mono.svg │ │ │ │ ├── local-mono.svg │ │ │ │ ├── local-mono-white.svg │ │ │ │ ├── x-mono.svg │ │ │ │ ├── x-mono-white.svg │ │ │ │ ├── entertainment-mono-white.svg │ │ │ │ ├── entertainment-mono.svg │ │ │ │ ├── facebook-mono.svg │ │ │ │ ├── facebook-mono-white.svg │ │ │ │ ├── myfeed-mono.svg │ │ │ │ ├── myfeed-mono-white.svg │ │ │ │ ├── saved-mono.svg │ │ │ │ ├── saved-mono-white.svg │ │ │ │ ├── multiple-mono-white.svg │ │ │ │ ├── multiple-mono.svg │ │ │ │ ├── lifestyle-mono.svg │ │ │ │ ├── lifestyle-mono-white.svg │ │ │ │ ├── all-mono-white.svg │ │ │ │ └── all-mono.svg │ │ │ │ ├── markets-mono.svg │ │ │ │ ├── 128x128 │ │ │ │ ├── markets-mono.svg │ │ │ │ ├── markets-mono-white.svg │ │ │ │ ├── local-mono.svg │ │ │ │ ├── local-mono-white.svg │ │ │ │ ├── entertainment-mono-white.svg │ │ │ │ ├── entertainment-mono.svg │ │ │ │ ├── myfeed-mono.svg │ │ │ │ ├── myfeed-mono-white.svg │ │ │ │ ├── saved-mono.svg │ │ │ │ ├── saved-mono-white.svg │ │ │ │ ├── lifestyle-mono.svg │ │ │ │ └── lifestyle-mono-white.svg │ │ │ │ ├── local-mono.svg │ │ │ │ ├── local-mono-white.svg │ │ │ │ ├── x-mono.svg │ │ │ │ ├── x-mono-white.svg │ │ │ │ ├── entertainment-mono-white.svg │ │ │ │ ├── entertainment-mono.svg │ │ │ │ ├── facebook-mono.svg │ │ │ │ ├── facebook-mono-white.svg │ │ │ │ ├── myfeed-mono.svg │ │ │ │ ├── myfeed-mono-white.svg │ │ │ │ ├── saved-mono.svg │ │ │ │ ├── saved-mono-white.svg │ │ │ │ ├── lifestyle-mono.svg │ │ │ │ ├── lifestyle-mono-white.svg │ │ │ │ ├── all-mono-white.svg │ │ │ │ └── all-mono.svg │ │ └── resources │ │ │ └── adblock.css │ │ ├── glib-2.0 │ │ └── schemas │ │ │ └── gschemas.compiled │ │ ├── icons │ │ └── hicolor │ │ │ ├── 128x128 │ │ │ └── apps │ │ │ │ └── paperboy.png │ │ │ ├── 256x256 │ │ │ └── apps │ │ │ │ └── paperboy.png │ │ │ └── 512x512 │ │ │ └── apps │ │ │ └── paperboy.png │ │ ├── applications │ │ └── io.github.thecalamityjoe87.Paperboy.desktop │ │ └── metainfo │ │ └── io.github.thecalamityjoe87.Paperboy.appdata.xml │ ├── io.github.thecalamityjoe87.Paperboy.desktop │ └── AppRun ├── meson-templates └── build_constants.vala.in ├── CODE_OF_CONDUCT.md ├── tools └── html2rss │ └── Cargo.toml ├── CONTRIBUTING.md └── src ├── models ├── newsArticle.vala ├── articleItem.vala ├── imageModels.vala └── rssSource.vala ├── services ├── fetchers │ ├── baseFetcher.vala │ ├── fetcherUtils.vala │ └── reutersFetcher.vala ├── articleShareService.vala └── previewCache.vala ├── utils ├── browserUtils.vala └── pixbufUtils.vala ├── managers └── toastManager.vala ├── ui └── components │ └── toastWidget.vala └── main.vala /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | %: 3 | dh $@ --buildsystem=meson 4 | -------------------------------------------------------------------------------- /debian/install: -------------------------------------------------------------------------------- 1 | tools/html2rss/target/release/html2rss usr/libexec/paperboy/ 2 | -------------------------------------------------------------------------------- /images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/images/screenshot.png -------------------------------------------------------------------------------- /data/icons/bbc-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/data/icons/bbc-logo.png -------------------------------------------------------------------------------- /data/icons/no-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/data/icons/no-image.png -------------------------------------------------------------------------------- /data/icons/npr-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/data/icons/npr-logo.png -------------------------------------------------------------------------------- /data/icons/paperboy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/data/icons/paperboy.png -------------------------------------------------------------------------------- /data/icons/wsj-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/data/icons/wsj-logo.png -------------------------------------------------------------------------------- /images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/images/screenshot1.png -------------------------------------------------------------------------------- /images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/images/screenshot2.png -------------------------------------------------------------------------------- /images/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/images/screenshot3.png -------------------------------------------------------------------------------- /images/screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/images/screenshot4.png -------------------------------------------------------------------------------- /images/screenshot5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/images/screenshot5.png -------------------------------------------------------------------------------- /images/screenshot6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/images/screenshot6.png -------------------------------------------------------------------------------- /images/screenshot7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/images/screenshot7.png -------------------------------------------------------------------------------- /data/icons/reddit-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/data/icons/reddit-logo.png -------------------------------------------------------------------------------- /data/icons/foxnews-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/data/icons/foxnews-logo.png -------------------------------------------------------------------------------- /data/icons/guardian-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/data/icons/guardian-logo.png -------------------------------------------------------------------------------- /data/icons/nytimes-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/data/icons/nytimes-logo.png -------------------------------------------------------------------------------- /data/icons/reuters-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/data/icons/reuters-logo.png -------------------------------------------------------------------------------- /data/icons/128x128/paperboy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/data/icons/128x128/paperboy.png -------------------------------------------------------------------------------- /data/icons/256x256/paperboy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/data/icons/256x256/paperboy.png -------------------------------------------------------------------------------- /data/icons/512x512/paperboy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/data/icons/512x512/paperboy.png -------------------------------------------------------------------------------- /data/icons/bloomberg-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/data/icons/bloomberg-logo.png -------------------------------------------------------------------------------- /data/resources/gschemas.compiled: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/data/resources/gschemas.compiled -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/.DirIcon: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/packaging/appimage/AppDirTemplate/.DirIcon -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/paperboy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/packaging/appimage/AppDirTemplate/paperboy.png -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | paperboy (0.6.3a-1) unstable; urgency=low 2 | 3 | * Initial Debian packaging. 4 | 5 | -- Isaac Joseph Tue, 01 Dec 2025 00:00:00 +0000 6 | -------------------------------------------------------------------------------- /debian/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | # Update GTK icon cache 4 | if [ -x /usr/bin/gtk-update-icon-cache ]; then 5 | gtk-update-icon-cache -f /usr/share/icons/hicolor || true 6 | fi 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/bbc-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/bbc-logo.png -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/no-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/no-image.png -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/npr-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/npr-logo.png -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/paperboy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/paperboy.png -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/wsj-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/wsj-logo.png -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/foxnews-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/foxnews-logo.png -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/guardian-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/guardian-logo.png -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/nytimes-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/nytimes-logo.png -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/reddit-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/reddit-logo.png -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/reuters-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/reuters-logo.png -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/glib-2.0/schemas/gschemas.compiled: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/packaging/appimage/AppDirTemplate/usr/share/glib-2.0/schemas/gschemas.compiled -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/bloomberg-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/bloomberg-logo.png -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/icons/hicolor/128x128/apps/paperboy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/packaging/appimage/AppDirTemplate/usr/share/icons/hicolor/128x128/apps/paperboy.png -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/icons/hicolor/256x256/apps/paperboy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/packaging/appimage/AppDirTemplate/usr/share/icons/hicolor/256x256/apps/paperboy.png -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/icons/hicolor/512x512/apps/paperboy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecalamityjoe87/paperboy/HEAD/packaging/appimage/AppDirTemplate/usr/share/icons/hicolor/512x512/apps/paperboy.png -------------------------------------------------------------------------------- /data/io.github.thecalamityjoe87.Paperboy.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Paperboy 3 | Exec=paperboy 4 | Icon=paperboy 5 | Type=Application 6 | Categories=Network;News;GTK; 7 | Keywords=news;rss;reader;feed;aggregator; 8 | StartupNotify=true 9 | StartupWMClass=io.github.thecalamityjoe87.Paperboy 10 | X-GNOME-FullName=Paperboy 11 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: Paperboy 3 | Source: https://github.com/thecalamityjoe87/paperboy 4 | 5 | Files: * 6 | Copyright: 2021-2025 The upstream authors 7 | License: GPL-3.0-or-later 8 | 9 | Files: packaging/debian/* 10 | Copyright: Isaac Joseph 11 | License: GPL-3.0-or-later 12 | -------------------------------------------------------------------------------- /meson-templates/build_constants.vala.in: -------------------------------------------------------------------------------- 1 | // Generated by Meson - do not edit 2 | // Provides the install libexecdir so runtime code can resolve helper binaries 3 | 4 | using GLib; 5 | 6 | public class BuildConstants : GLib.Object { 7 | // Meson will substitute @libexecdir@ with the configured installation libexecdir 8 | public static const string LIBEXECDIR = "@libexecdir@"; 9 | } 10 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/io.github.thecalamityjoe87.Paperboy.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Paperboy 3 | Exec=paperboy 4 | Icon=paperboy 5 | Type=Application 6 | Categories=Network;News;GTK; 7 | Keywords=news;rss;reader;feed;aggregator; 8 | StartupNotify=true 9 | StartupWMClass=io.github.thecalamityjoe87.Paperboy 10 | X-GNOME-FullName=Paperboy 11 | 12 | X-AppImage-Version=0.7.3a 13 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/applications/io.github.thecalamityjoe87.Paperboy.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Paperboy 3 | Exec=paperboy 4 | Icon=paperboy 5 | Type=Application 6 | Categories=Network;News;GTK; 7 | Keywords=news;rss;reader;feed;aggregator; 8 | StartupNotify=true 9 | StartupWMClass=io.github.thecalamityjoe87.Paperboy 10 | X-GNOME-FullName=Paperboy 11 | 12 | X-AppImage-Version=0.7.3a 13 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | We are committed to creating a welcoming and respectful community. 5 | 6 | ## Our Standards 7 | - Be respectful and considerate in all interactions. 8 | - Refrain from harassment or discriminatory language. 9 | - Assume good intentions and give constructive feedback. 10 | 11 | ## Enforcement 12 | Project maintainers may remove content or ban contributors who violate these standards. If you experience or witness unacceptable behavior, please contact the maintainers. 13 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: paperboy 2 | Section: web 3 | Priority: optional 4 | Maintainer: Isaac Joseph 5 | Build-Depends: debhelper-compat (= 13), meson, ninja-build, pkg-config, valac, libgtk-4-dev, libadwaita-1-dev, libgdk-pixbuf-2.0-dev, libjson-glib-dev, libgee-0.8-dev, libglib2.0-dev, libsqlite3-dev, libxml2-dev, libwebkitgtk-6.0-dev 6 | Standards-Version: 4.6.0 7 | 8 | Package: paperboy 9 | Architecture: any 10 | Depends: ${shlibs:Depends}, ${misc:Depends} 11 | Description: RSS reader and article browser (Paperboy) 12 | A small Vala/GTK4 application for reading RSS/Atom feeds and 13 | viewing articles. 14 | -------------------------------------------------------------------------------- /data/icons/symbolic/markets-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /tools/html2rss/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "html2rss" 3 | version = "0.1.6" 4 | edition = "2021" 5 | authors = ["Paperboy "] 6 | license = "MIT" 7 | description = "Generate RSS feed from arbitrary HTML pages (standalone binary)." 8 | 9 | [dependencies] 10 | clap = { version = "4", features = ["derive"] } 11 | reqwest = { version = "0.12", features = ["blocking", "rustls-tls"] } 12 | scraper = "0.20" 13 | serde_json = "1.0" 14 | quick-xml = "0.36" 15 | url = "2" 16 | regex = "1" 17 | rand = "0.8" 18 | encoding_rs = "0.8" 19 | unicode-normalization = "0.1" 20 | once_cell = "1.19" 21 | html-escape = "0.2" 22 | chrono = "0.4" 23 | 24 | [profile.release] 25 | opt-level = "z" 26 | lto = true 27 | strip = true 28 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/markets-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /data/icons/symbolic/markets-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /data/icons/symbolic/128x128/markets-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/markets-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /data/icons/symbolic/128x128/markets-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /data/icons/symbolic/128x128/local-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/128x128/local-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/markets-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/24x24/markets-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/markets-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/128x128/markets-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/24x24/markets-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/128x128/markets-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/128x128/local-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/128x128/local-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to This Project 2 | 3 | Thank you for your interest in contributing to Paperboy! To keep things smooth, please follow these guidelines: 4 | 5 | ## How to Contribute 6 | - Fork the repository and create a new branch for your changes. 7 | - Ensure your code follows existing style and conventions. 8 | - Write tests for new features or bug fixes. 9 | - Submit a pull request with a clear description of your changes. 10 | 11 | ## Issues 12 | - Check existing issues before creating a new one. 13 | - Provide detailed information and steps to reproduce any bug. 14 | - Use clear and descriptive titles. 15 | 16 | ## Pull Requests 17 | - Keep PRs focused on a single change or feature. 18 | - Include relevant issue numbers (e.g., `Fixes #123`) when applicable. 19 | - Ensure all tests pass before submitting. 20 | -------------------------------------------------------------------------------- /data/icons/symbolic/local-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/local-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/local-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/local-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/local-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/24x24/local-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/metainfo/io.github.thecalamityjoe87.Paperboy.appdata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | io.github.thecalamityjoe87.Paperboy 4 | CC0-1.0 5 | Paperboy 6 | Simple news and RSS reader 7 | 8 |

Paperboy is a simple news app written in Vala built with GTK4 and Libadwaita.

9 |
10 | https://github.com/thecalamityjoe87/paperboy 11 | io.github.thecalamityjoe87.Paperboy.desktop 12 | GPL-3.0-or-later 13 | Isaac Joseph 14 | 15 | 16 | 17 | 18 |
19 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/local-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/24x24/local-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/x-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/x-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /data/icons/symbolic/x-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/x-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/x-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/24x24/x-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/x-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/24x24/x-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/AppRun: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | HERE="$(dirname "$(readlink -f "${0}")")" 4 | # Ensure application can find its data files inside the AppDir 5 | export XDG_DATA_DIRS="$HERE/usr/share:${XDG_DATA_DIRS:-}" 6 | # Point GSettings to the bundled schema directory so preferences can be saved/loaded 7 | export GSETTINGS_SCHEMA_DIR="$HERE/usr/share/glib-2.0/schemas:${GSETTINGS_SCHEMA_DIR:-}" 8 | # Let the runtime know where internal helper binaries live inside the AppDir 9 | export PAPERBOY_LIBEXECDIR="$HERE/usr/libexec" 10 | # Prepend bundled fallback libs so libxml2.so.16 from the AppDir is preferred. 11 | # Keep $HERE/usr/lib in the search path for other bundled libs if present. 12 | # Use a safe expansion under `set -u` so referencing an unset LD_LIBRARY_PATH 13 | # doesn't trigger an unbound variable error inside the AppImage runtime. 14 | if [ -n "${LD_LIBRARY_PATH-}" ]; then 15 | export LD_LIBRARY_PATH="$HERE/opt/fallback-libs:$LD_LIBRARY_PATH:$HERE/usr/lib" 16 | else 17 | export LD_LIBRARY_PATH="$HERE/opt/fallback-libs:$HERE/usr/lib" 18 | fi 19 | exec "$HERE/usr/bin/paperboy" "$@" 20 | -------------------------------------------------------------------------------- /data/resources/adblock.css: -------------------------------------------------------------------------------- 1 | /* =========================================== 2 | Gentle Cosmetic Ad Hiding for WebKitGTK 6 3 | =========================================== */ 4 | 5 | /* Hide specific ad iframes from major ad networks */ 6 | iframe[src*="doubleclick"], 7 | iframe[src*="googlesyndication"], 8 | iframe[src*="taboola"], 9 | iframe[src*="outbrain"], 10 | iframe[src*="doubleclick.net"], 11 | iframe[src*="googlesyndication.com"], 12 | iframe[src*="googleadservices.com"], 13 | iframe[src*="pagead2.googlesyndication.com"] { 14 | display: none !important; 15 | width: 0 !important; 16 | height: 0 !important; 17 | border: 0 !important; 18 | margin: 0 !important; 19 | padding: 0 !important; 20 | } 21 | 22 | /* AdChoices logos / overlays and descendants */ 23 | .adchoices, 24 | .adChoice, 25 | .adchoice-icon, 26 | .adchoices-logo, 27 | .adchoice-badge, 28 | .adchoices *, 29 | .ad-choices, 30 | .ad-Choice, 31 | .adchoices-badge, 32 | .adchoices-link { 33 | display: none !important; 34 | } 35 | 36 | /* Hide a few very common ad container classes */ 37 | .ad-banner, 38 | .ad-container { 39 | display: none !important; 40 | } 41 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/resources/adblock.css: -------------------------------------------------------------------------------- 1 | /* =========================================== 2 | Gentle Cosmetic Ad Hiding for WebKitGTK 6 3 | =========================================== */ 4 | 5 | /* Hide specific ad iframes from major ad networks */ 6 | iframe[src*="doubleclick"], 7 | iframe[src*="googlesyndication"], 8 | iframe[src*="taboola"], 9 | iframe[src*="outbrain"], 10 | iframe[src*="doubleclick.net"], 11 | iframe[src*="googlesyndication.com"], 12 | iframe[src*="googleadservices.com"], 13 | iframe[src*="pagead2.googlesyndication.com"] { 14 | display: none !important; 15 | width: 0 !important; 16 | height: 0 !important; 17 | border: 0 !important; 18 | margin: 0 !important; 19 | padding: 0 !important; 20 | } 21 | 22 | /* AdChoices logos / overlays and descendants */ 23 | .adchoices, 24 | .adChoice, 25 | .adchoice-icon, 26 | .adchoices-logo, 27 | .adchoice-badge, 28 | .adchoices *, 29 | .ad-choices, 30 | .ad-Choice, 31 | .adchoices-badge, 32 | .adchoices-link { 33 | display: none !important; 34 | } 35 | 36 | /* Hide a few very common ad container classes */ 37 | .ad-banner, 38 | .ad-container { 39 | display: none !important; 40 | } 41 | -------------------------------------------------------------------------------- /data/icons/symbolic/128x128/entertainment-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/entertainment-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/entertainment-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/128x128/entertainment-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/entertainment-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/entertainment-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/entertainment-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/128x128/entertainment-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/24x24/entertainment-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/128x128/entertainment-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/entertainment-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/24x24/entertainment-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/models/newsArticle.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2025 Isaac Joseph 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * as published by the Free Software Foundation; either version 2 7 | * of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | */ 18 | 19 | namespace Paperboy { 20 | public class NewsArticle : GLib.Object { 21 | public string title; 22 | public string url; 23 | public string? image_url; 24 | public string? snippet; 25 | // Optional published date/time extracted from feeds or page metadata 26 | // This is a raw string as found in the feed (ISO8601 or human-friendly) 27 | public string? published; 28 | public NewsArticle() {} 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/models/articleItem.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2025 Isaac Joseph 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | /** 19 | * Represents an article item with its metadata for display in the UI 20 | */ 21 | public class ArticleItem : GLib.Object { 22 | public string title { get; set; } 23 | public string url { get; set; } 24 | public string? thumbnail_url { get; set; } 25 | public string category_id { get; set; } 26 | public string? source_name { get; set; } 27 | public string? published { get; set; } 28 | 29 | public ArticleItem(string title, string url, string? thumbnail_url, string category_id, string? source_name = null, string? published = null) { 30 | this.title = title; 31 | this.url = url; 32 | this.thumbnail_url = thumbnail_url; 33 | this.category_id = category_id; 34 | this.source_name = source_name; 35 | this.published = published; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /data/icons/symbolic/facebook-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/facebook-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/facebook-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/facebook-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/facebook-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/24x24/facebook-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/facebook-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/24x24/facebook-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/128x128/myfeed-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/128x128/myfeed-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/myfeed-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/myfeed-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/myfeed-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/myfeed-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/saved-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/saved-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/128x128/myfeed-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/saved-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/128x128/myfeed-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/saved-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/myfeed-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/services/fetchers/baseFetcher.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2025 Isaac Joseph 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | using GLib; 19 | using Soup; 20 | 21 | public delegate void SetLabelFunc(string text); 22 | public delegate void ClearItemsFunc(); 23 | public delegate void AddItemFunc(string title, string url, string? thumbnail_url, string category_id, string? source_name); 24 | 25 | public abstract class BaseFetcher : GLib.Object { 26 | protected SetLabelFunc set_label; 27 | protected ClearItemsFunc clear_items; 28 | protected AddItemFunc add_item; 29 | 30 | protected BaseFetcher(SetLabelFunc set_label_func, ClearItemsFunc clear_items_func, AddItemFunc add_item_func) { 31 | this.set_label = set_label_func; 32 | this.clear_items = clear_items_func; 33 | this.add_item = add_item_func; 34 | } 35 | 36 | // Abstract method that each fetcher must implement 37 | public abstract void fetch(string category, string search_query, Soup.Session session); 38 | 39 | // Get the source name for this fetcher 40 | public abstract string get_source_name(); 41 | } 42 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/24x24/myfeed-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/myfeed-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/24x24/myfeed-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/saved-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/24x24/saved-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/saved-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/24x24/saved-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/models/imageModels.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2025 Isaac Joseph 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | /** 19 | * Small helper object to track hero image requests (size, multiplier, retries) 20 | */ 21 | public class HeroRequest : GLib.Object { 22 | public string url { get; set; } 23 | public int last_requested_w { get; set; } 24 | public int last_requested_h { get; set; } 25 | public int multiplier { get; set; } 26 | public int retries { get; set; } 27 | 28 | public HeroRequest(string url, int w, int h, int multiplier) { 29 | this.url = url; 30 | this.last_requested_w = w; 31 | this.last_requested_h = h; 32 | this.multiplier = multiplier; 33 | this.retries = 0; 34 | } 35 | } 36 | 37 | /** 38 | * Deferred request holder for widgets that postpone downloads until visible 39 | */ 40 | public class DeferredRequest : GLib.Object { 41 | public string url { get; set; } 42 | public int w { get; set; } 43 | public int h { get; set; } 44 | 45 | public DeferredRequest(string url, int w, int h) { 46 | this.url = url; 47 | this.w = w; 48 | this.h = h; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /data/icons/symbolic/128x128/saved-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/services/fetchers/fetcherUtils.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2025 Isaac Joseph 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | using GLib; 19 | 20 | namespace FetcherUtils { 21 | public string category_display_name(string cat) { 22 | switch (cat) { 23 | case "frontpage": return "The Frontpage"; 24 | case "myfeed": return "My Feed"; 25 | case "general": return "World News"; 26 | case "us": return "US News"; 27 | case "technology": return "Technology"; 28 | case "business": return "Business"; 29 | case "markets": return "Markets"; 30 | case "industries": return "Industries"; 31 | case "economics": return "Economics"; 32 | case "wealth": return "Wealth"; 33 | case "green": return "Green"; 34 | case "science": return "Science"; 35 | case "sports": return "Sports"; 36 | case "health": return "Health"; 37 | case "entertainment": return "Entertainment"; 38 | case "politics": return "Politics"; 39 | case "lifestyle": return "Lifestyle"; 40 | } 41 | return "News"; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /data/icons/symbolic/128x128/saved-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/multiple-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/multiple-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/128x128/saved-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/128x128/saved-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/24x24/multiple-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/24x24/multiple-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/models/rssSource.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2025 Isaac Joseph 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | namespace Paperboy { 19 | public class RssSource : GLib.Object { 20 | public int64 id { get; set; } 21 | public string name { get; set; } 22 | public string url { get; set; } 23 | public string? icon_filename { get; set; } 24 | public string? favicon_url { get; set; } 25 | public string? original_url { get; set; } 26 | public int64 created_at { get; set; } 27 | public int64 last_fetched_at { get; set; } 28 | 29 | public RssSource() { 30 | id = -1; 31 | name = ""; 32 | url = ""; 33 | icon_filename = null; 34 | favicon_url = null; 35 | created_at = 0; 36 | last_fetched_at = 0; 37 | } 38 | 39 | public RssSource.with_data(int64 id, string name, string url, string? icon_filename, int64 created_at, int64 last_fetched_at) { 40 | this.id = id; 41 | this.name = name; 42 | this.url = url; 43 | this.icon_filename = icon_filename; 44 | this.favicon_url = null; 45 | this.created_at = created_at; 46 | this.last_fetched_at = last_fetched_at; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/utils/browserUtils.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2025 Isaac Joseph 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | using GLib; 19 | using Gtk; 20 | 21 | /* 22 | * Robustly open a URL in the user's configured browser. Try the 23 | * platform Gio.AppInfo API first; on failure fall back to executing 24 | * xdg-open as a last resort. 25 | */ 26 | 27 | public class BrowserUtils : GLib.Object { 28 | // Open URL in default browser; returns true on success, false on error 29 | public static bool open_url_in_browser(string uri) { 30 | if (uri == null || uri.strip().length == 0) return false; 31 | 32 | string normalized = uri.strip(); 33 | if (!(normalized.has_prefix("http://") || normalized.has_prefix("https://") || normalized.has_prefix("mailto:") || normalized.has_prefix("file:") || normalized.has_prefix("ftp:"))) { 34 | normalized = "https://" + normalized; 35 | } 36 | 37 | try { 38 | AppInfo.launch_default_for_uri(normalized, null); 39 | return true; 40 | } catch (GLib.Error e) { 41 | try { 42 | string[] argv = { "xdg-open", normalized }; 43 | var proc = new GLib.Subprocess.newv(argv, GLib.SubprocessFlags.NONE); 44 | return true; 45 | } catch (GLib.Error e) { 46 | return false; 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /data/icons/symbolic/lifestyle-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/services/fetchers/reutersFetcher.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2025 Isaac Joseph 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | using GLib; 19 | using Soup; 20 | using Tools; 21 | 22 | public class ReutersFetcher : BaseFetcher { 23 | public ReutersFetcher(SetLabelFunc set_label_func, ClearItemsFunc clear_items_func, AddItemFunc add_item_func) { 24 | base(set_label_func, clear_items_func, add_item_func); 25 | } 26 | 27 | public override void fetch(string category, string search_query, Soup.Session session) { 28 | // Reuters RSS feeds require authentication, so use Google News site search 29 | fetch_google_domain(category, search_query, session, "reuters.com", "Reuters"); 30 | } 31 | 32 | public override string get_source_name() { 33 | return "Reuters"; 34 | } 35 | 36 | private void fetch_google_domain( 37 | string current_category, 38 | string current_search_query, 39 | Soup.Session session, 40 | string domain, 41 | string source_name 42 | ) { 43 | string base_url = "https://news.google.com/rss/search"; 44 | string ceid = "hl=en-US&gl=US&ceid=US:en"; 45 | string category_name = FetcherUtils.category_display_name(current_category); 46 | string query = @"site:$(domain)"; 47 | if (current_search_query.length > 0) { 48 | query = query + " " + current_search_query; 49 | } 50 | string url = @"$(base_url)?q=$(Uri.escape_string(query))&$(ceid)"; 51 | 52 | RssFeedProcessor.fetch_rss_url(url, source_name, category_name, current_category, current_search_query, session, set_label, clear_items, add_item); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/lifestyle-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/lifestyle-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/lifestyle-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/lifestyle-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/24x24/lifestyle-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/lifestyle-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/24x24/lifestyle-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/128x128/lifestyle-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/services/articleShareService.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2025 Isaac Joseph 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | using GLib; 19 | 20 | namespace ArticleShareService { 21 | public enum ShareTarget { 22 | EMAIL, 23 | REDDIT, 24 | TWITTER, 25 | FACEBOOK 26 | } 27 | 28 | // Build a platform-neutral share URI for the given target. 29 | // This function is UI-agnostic and does not launch anything. 30 | public string build_share_uri(ShareTarget target, string url, string? title) { 31 | string encoded_url = Uri.escape_string(url, null, false); 32 | switch (target) { 33 | case ShareTarget.EMAIL: 34 | string article_title = title ?? "Check out this article"; 35 | string subject = Uri.escape_string(article_title, null, false); 36 | string body = Uri.escape_string("I thought you might find this interesting:\n\n" + url, null, false); 37 | return "mailto:?subject=%s&body=%s".printf(subject, body); 38 | case ShareTarget.REDDIT: 39 | return "https://www.reddit.com/submit?url=%s".printf(encoded_url); 40 | case ShareTarget.TWITTER: 41 | string text = title != null ? Uri.escape_string(title, null, false) : ""; 42 | return "https://twitter.com/intent/tweet?text=%s&url=%s".printf(text, encoded_url); 43 | case ShareTarget.FACEBOOK: 44 | return "https://www.facebook.com/sharer/sharer.php?u=%s".printf(encoded_url); 45 | default: 46 | return ""; 47 | } 48 | } 49 | 50 | // Build text suitable for copying into the clipboard 51 | public string build_clipboard_text(string url) { 52 | return url; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /data/icons/symbolic/128x128/lifestyle-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/128x128/lifestyle-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/128x128/lifestyle-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/managers/toastManager.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2025 Isaac Joseph 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | public class ToastManager : GLib.Object { 19 | private uint? timeout_id; 20 | 21 | public signal void request_show_toast(string message, bool persistent); 22 | public signal void request_dismiss_toast(); 23 | 24 | public ToastManager() { 25 | this.timeout_id = null; 26 | } 27 | 28 | // Dismiss the current toast 29 | private void dismiss_toast() { 30 | if (timeout_id != null) { 31 | Source.remove(timeout_id); 32 | timeout_id = null; 33 | } 34 | request_dismiss_toast(); 35 | } 36 | 37 | // Show a transient toast with a 3-second timeout 38 | public void show_toast(string message) { 39 | // Cancel any existing timeout 40 | if (timeout_id != null) { 41 | Source.remove(timeout_id); 42 | timeout_id = null; 43 | } 44 | 45 | // Request UI to show the toast 46 | request_show_toast(message, false); 47 | 48 | // Auto-dismiss after 3 seconds 49 | timeout_id = Timeout.add_seconds(3, () => { 50 | dismiss_toast(); 51 | timeout_id = null; 52 | return false; 53 | }); 54 | } 55 | 56 | // Show a persistent toast (no timeout, stays until dismissed) 57 | public void show_persistent_toast(string message) { 58 | // Cancel any existing timeout 59 | if (timeout_id != null) { 60 | Source.remove(timeout_id); 61 | timeout_id = null; 62 | } 63 | 64 | // Request UI to show the persistent toast 65 | request_show_toast(message, true); 66 | } 67 | 68 | // Explicitly clear the current toast 69 | public void clear_persistent_toast() { 70 | dismiss_toast(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/ui/components/toastWidget.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2025 Isaac Joseph 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | using Gtk; 19 | 20 | public class ToastWidget : GLib.Object { 21 | // Create a custom toast widget that looks like Adwaita toast 22 | public static Gtk.Box create_toast_widget(string message, bool show_close_button, owned VoidFunc? dismiss_callback = null) { 23 | // Reduce inter-child spacing so text and close button sit closer 24 | var toast_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6); 25 | toast_box.add_css_class("custom-toast"); 26 | toast_box.set_halign(Gtk.Align.CENTER); 27 | toast_box.set_valign(Gtk.Align.END); // Bottom of content area 28 | toast_box.set_margin_bottom(24); // Space from bottom edge 29 | toast_box.set_can_focus(false); // Prevent toast from stealing focus 30 | 31 | // Message label 32 | var label = new Gtk.Label(message); 33 | //label.set_wrap(true); 34 | label.set_max_width_chars(50); 35 | label.add_css_class("custom-toast-label"); 36 | toast_box.append(label); 37 | 38 | // Close button (X) 39 | if (show_close_button && dismiss_callback != null) { 40 | var close_button = new Gtk.Button(); 41 | close_button.set_icon_name("window-close-symbolic"); 42 | close_button.add_css_class("flat"); 43 | close_button.add_css_class("circular"); 44 | close_button.add_css_class("custom-toast-close"); 45 | close_button.set_can_focus(false); // Prevent focus stealing 46 | close_button.clicked.connect(() => { 47 | dismiss_callback(); 48 | }); 49 | toast_box.append(close_button); 50 | } 51 | 52 | return toast_box; 53 | } 54 | 55 | public delegate void VoidFunc(); 56 | } 57 | -------------------------------------------------------------------------------- /src/services/previewCache.vala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2025 Isaac Joseph 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | using GLib; 19 | using Gee; 20 | using Gdk; 21 | 22 | /* 23 | * PreviewCache manager: provide a shared, bounded cache for article preview textures. 24 | * This centralizes the preview cache so the main window can clear it when 25 | * switching categories to free memory. 26 | */ 27 | 28 | public class PreviewCacheManager : GLib.Object { 29 | // Preview cache is now backed by ImageCache (pixbufs). We no longer 30 | // hold long-lived Gdk.Texture objects here; callers should request a 31 | // transient texture via `get_paintable` which will create a texture 32 | // from a cached pixbuf on-demand. 33 | 34 | public static Gdk.Texture? get_paintable(string key) { 35 | try { 36 | var ic = ImageCache.get_global(); 37 | var tex = ic.get_texture(key); 38 | return tex; 39 | } catch (GLib.Error e) { return null; } 40 | } 41 | 42 | public static void set_pixbuf(string key, Gdk.Pixbuf pb) { 43 | try { 44 | var ic = ImageCache.get_global(); 45 | ic.set(key, pb); 46 | } catch (GLib.Error e) { } 47 | } 48 | 49 | public static void clear_cache() { 50 | // Avoid clearing the global ImageCache from PreviewCacheManager as 51 | // this can cause excessive eviction when users switch categories. 52 | // The ImageCache LRU policy will manage memory; keep this as a 53 | // no-op to avoid thrashing. 54 | try { 55 | if (AppDebugger.debug_enabled()) { 56 | } 57 | } catch (GLib.Error e) { } 58 | } 59 | 60 | public static void set_capacity(int c) { 61 | try { ImageCache.get_global().set_capacity(c); } catch (GLib.Error e) { } 62 | } 63 | 64 | // Compatibility accessor used by older callsites. Returns the global 65 | // ImageCache so callers can still invoke `.get()`/`.set()` as before. 66 | public static ImageCache get_cache() { 67 | return ImageCache.get_global(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/utils/pixbufUtils.vala: -------------------------------------------------------------------------------- 1 | using Gdk; 2 | using GLib; 3 | using Cairo; 4 | 5 | public class PixbufUtils { 6 | // Scale to square (out_size x out_size) and mask to a circle (alpha outside = 0) 7 | public static Gdk.Pixbuf? scale_and_circularize (Gdk.Pixbuf? src, int out_size) { 8 | if (src == null) return null; 9 | 10 | int w = src.get_width(); 11 | int h = src.get_height(); 12 | int src_size = (w < h) ? w : h; 13 | 14 | // Create a square pixbuf scaled to out_size then paint it into a 15 | // Cairo ARGB surface clipped to a circle. Converting the surface 16 | // back to a pixbuf avoids accessing raw pixel memory directly. 17 | Gdk.Pixbuf scaled_pb; 18 | try { 19 | // Scale the source pixbuf to the desired output size 20 | scaled_pb = src.scale_simple (out_size, out_size, Gdk.InterpType.BILINEAR); 21 | } catch (GLib.Error e) { 22 | // If scaling fails, fall back to original (best-effort) 23 | scaled_pb = src; 24 | } 25 | 26 | // Create ARGB surface and draw a circular badge with the logo 27 | var surface = new ImageSurface(Format.ARGB32, out_size, out_size); 28 | var cr = new Context(surface); 29 | 30 | // Fill transparent first 31 | cr.set_source_rgba(0, 0, 0, 0); 32 | cr.paint(); 33 | 34 | // Use a slight inset (0.5) and best antialiasing to produce a 35 | // visually-crisp circle across different scale factors / DPI. 36 | cr.set_antialias(Antialias.BEST); 37 | double inset_f = 0.5; 38 | double radius = (out_size - (inset_f * 2.0)) / 2.0; 39 | double cx = out_size / 2.0; 40 | double cy = out_size / 2.0; 41 | 42 | // Draw solid circular background (white) so the badge is opaque. 43 | cr.arc(cx, cy, radius, 0, 2 * Math.PI); 44 | cr.set_source_rgba(1, 1, 1, 1); 45 | cr.fill(); 46 | 47 | // Clip to circle so the logo doesn't draw outside. 48 | cr.arc(cx, cy, radius, 0, 2 * Math.PI); 49 | cr.clip(); 50 | 51 | // Draw the scaled pixbuf inset slightly so it sits comfortably inside the badge 52 | int inset = 4; // matches previous code that centered a 16x16 inside 24x24 53 | int inner_size = out_size - (inset * 2); 54 | Gdk.Pixbuf inner_pb; 55 | try { 56 | inner_pb = scaled_pb.scale_simple(inner_size, inner_size, Gdk.InterpType.BILINEAR); 57 | } catch (GLib.Error e) { 58 | inner_pb = scaled_pb; 59 | } 60 | 61 | int ox = inset; 62 | int oy = inset; 63 | Gdk.cairo_set_source_pixbuf(cr, inner_pb, ox, oy); 64 | cr.paint(); 65 | 66 | // Convert surface back to pixbuf 67 | try { 68 | var result_pb = Gdk.pixbuf_get_from_surface(surface, 0, 0, out_size, out_size); 69 | return result_pb; 70 | } catch (GLib.Error e) { 71 | return null; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main.vala: -------------------------------------------------------------------------------- 1 | /* Paperboy - A simple news reader and RSS application 2 | * 3 | * Copyright (C) 2025 Isaac Joseph 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | public class PaperboyApp : Adw.Application { 20 | public PaperboyApp() { 21 | GLib.Object(application_id: "io.github.thecalamityjoe87.Paperboy", flags: ApplicationFlags.FLAGS_NONE); 22 | } 23 | 24 | protected override void activate() { 25 | // Ensure global HttpClient is constructed on the main thread 26 | // before any other subsystem can spawn worker threads. 27 | Paperboy.HttpClientUtils.ensure_initialized(); 28 | 29 | var win = new NewsWindow(this); 30 | win.present(); 31 | // Eagerly instantiate ZipLookup so it starts loading the CSV in 32 | // the background during app startup. This helps ensure the ZIP 33 | // database is ready by the time the user opens the Set Location 34 | // dialog. 35 | ZipLookup.get_instance(); 36 | // If this is the user's first time running the app, show the 37 | // preferences dialog so they can adjust sources immediately. 38 | var prefs = NewsPreferences.get_instance(); 39 | // On first run, show the preferences dialog so users can immediately 40 | // enable/disable individual providers and configure the app. 41 | if (prefs.first_run) PrefsDialog.show_preferences_dialog(win); 42 | 43 | var change_source_action = new SimpleAction("change-source", null); 44 | change_source_action.activate.connect(() => { 45 | PrefsDialog.show_source_dialog(win); 46 | }); 47 | this.add_action(change_source_action); 48 | 49 | var about_action = new SimpleAction("about", null); 50 | about_action.activate.connect(() => { 51 | PrefsDialog.show_about_dialog(win); 52 | }); 53 | this.add_action(about_action); 54 | 55 | var set_location_action = new SimpleAction("set-location", null); 56 | set_location_action.activate.connect(() => { 57 | LocationDialog.show(win); 58 | }); 59 | this.add_action(set_location_action); 60 | } 61 | 62 | } 63 | 64 | public static int main(string[] args) { 65 | 66 | var app = new PaperboyApp(); 67 | return app.run(args); 68 | } 69 | -------------------------------------------------------------------------------- /data/icons/symbolic/all-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/all-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/all-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/all-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/symbolic/24x24/topten-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/all-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/all-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/24x24/all-mono-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packaging/appimage/AppDirTemplate/usr/share/paperboy/icons/symbolic/24x24/all-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | --------------------------------------------------------------------------------