Add the full application logic on top of the Epic 1 skeleton:
Epic 2 — Authentication
- LoginDialog (AdwDialog, Blueprint template) with server URL,
username, and password fields; emits logged-in signal on submit
- credentials.rs: store/load/clear via libsecret (password_store_sync /
password_search_sync / password_clear_sync, v0_19 feature)
- api.rs: Api::login() parses Auth= token from ClientLogin response;
fetch_write_token() fetches the write token
- Auto-login on startup from stored credentials; logout with
AdwAlertDialog confirmation; login errors shown in AdwAlertDialog
Epic 3 — Article fetching
- model.rs: Article struct and ArticleObject GObject wrapper with
unread property for list store binding
- Api::fetch_unread() deserializes Google Reader JSON, derives unread
from categories, generates plain-text excerpt
- Sidebar uses a GtkStack with placeholder / loading / empty / error /
list pages; AdwSpinnerPaintable while fetching; Try Again button
Epic 4 — Sidebar
- article_row.blp: composite template with feed title, date, title,
and excerpt labels
- ArticleRow GObject subclass; binds ArticleObject, watches unread
notify to apply .dim-label on the title; relative timestamp format
Epic 5 — Content pane
- content.html updated: setArticle(), checkKey(), feedthemonkey: URI
navigation scheme; dark mode via prefers-color-scheme
- content.css: proper article layout, dark mode, code blocks
- WebView loaded from GResource; decide-policy intercepts
feedthemonkey:{next,previous,open} and all external links
Epic 6 — Read state
- Api::mark_read() / mark_unread() via edit-tag endpoint
- Optimistic unread toggle on ArticleObject; background API calls;
mark_unread_guard prevents re-marking on navigation
- AdwToast shown on mark-unread
Epic 7 — Keyboard shortcuts
- GtkShortcutController on window for all shortcuts from the backlog
- shortcuts.blp: AdwShortcutsWindow documenting all shortcuts
- F1 opens shortcuts dialog; Ctrl+W closes window; Ctrl+Q quits
Epic 8 — Zoom
- zoom_in/zoom_out/zoom_reset wired to Ctrl+±/0; zoom level saved to
and restored from GSettings zoom-level key
Epic 9 — Window state persistence
- Window width/height/maximized saved on close, restored on open
- (Sidebar width deferred — AdwNavigationSplitView fraction binding)
Epic 10 — Polish
- AdwAboutDialog with app name, version, GPL-3.0, website
- Logout confirmation AdwAlertDialog with destructive button
- Win.toggle-fullscreen action (F11)
- Api dropped on window close to cancel in-flight requests
49 lines
1.4 KiB
HTML
49 lines
1.4 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>FeedTheMonkey</title>
|
|
<link rel="stylesheet" href="content.css">
|
|
</head>
|
|
<body>
|
|
<div id="header">
|
|
<div id="feed-title"></div>
|
|
<h1 id="title"></h1>
|
|
<div id="meta">
|
|
<span id="author"></span>
|
|
<span id="date"></span>
|
|
</div>
|
|
<a id="link" href="#" onclick="window.location='feedthemonkey:open'; return false;">Open in Browser</a>
|
|
</div>
|
|
<div id="content"></div>
|
|
|
|
<script>
|
|
function setArticle(article) {
|
|
document.getElementById('feed-title').textContent = article.feed_title || '';
|
|
document.getElementById('title').textContent = article.title || '';
|
|
document.getElementById('author').textContent = article.author || '';
|
|
document.getElementById('content').innerHTML = article.content || '';
|
|
|
|
var ts = article.updated;
|
|
if (ts) {
|
|
document.getElementById('date').textContent = new Date(ts * 1000).toLocaleDateString();
|
|
}
|
|
|
|
window.scrollTo(0, 0);
|
|
window._article = article;
|
|
}
|
|
|
|
function checkKey(e) {
|
|
if (e.key === 'ArrowRight' || e.key === 'j') {
|
|
window.location = 'feedthemonkey:next';
|
|
} else if (e.key === 'ArrowLeft' || e.key === 'k') {
|
|
window.location = 'feedthemonkey:previous';
|
|
} else if (e.key === 'Enter' || e.key === 'n') {
|
|
window.location = 'feedthemonkey:open';
|
|
}
|
|
}
|
|
|
|
document.addEventListener('keydown', checkKey);
|
|
</script>
|
|
</body>
|
|
</html>
|