Home: Karte „Nachrichten“ mit den Top-Themen seit dem letzten Mal, neue Meldungen und Blogartikel; sw v90
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
35701260e0
commit
1a17c75c1b
2 changed files with 70 additions and 5 deletions
|
|
@ -1,11 +1,11 @@
|
||||||
/**
|
/**
|
||||||
* diora Home: what you were in the middle of, one click away — the book you're
|
* diora Home: what you were in the middle of, one click away — the book you're
|
||||||
* reading, the next episode, your stations, Ambient.
|
* reading, the next episode, the news, your stations, Ambient.
|
||||||
*
|
*
|
||||||
* Built only from what the page already has: the book list (app.js), the
|
* Built only from what the page already has: the book list (app.js), the
|
||||||
* podcast host (window.DioraPodcastHost in the browser and diora desktop,
|
* podcast host (window.DioraPodcastHost in the browser and diora desktop,
|
||||||
* window.DioraNative in the Android app), the saved stations in the radio tab
|
* window.DioraNative in the Android app), the saved stations in the radio tab
|
||||||
* and Drift (app.js __dioraAmbient*). Renders into #tab-home; the same file runs
|
* Drift (app.js __dioraAmbient*) and the feed reader (feeds.js __dioraFeeds*). Renders into #tab-home; the same file runs
|
||||||
* in the browser, diora desktop and the Android app (copied with static/js/).
|
* in the browser, diora desktop and the Android app (copied with static/js/).
|
||||||
* Written without ?. and ?? so old WebViews can still parse it.
|
* Written without ?. and ?? so old WebViews can still parse it.
|
||||||
*/
|
*/
|
||||||
|
|
@ -125,6 +125,56 @@
|
||||||
button + '</div>';
|
button + '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- Nachrichten --------------------------------------------------------------
|
||||||
|
|
||||||
|
// The briefing comes from the server, so it's fetched now and then, not every render.
|
||||||
|
let feeds = null;
|
||||||
|
let feedsAt = 0;
|
||||||
|
let feedsBusy = false;
|
||||||
|
|
||||||
|
function refreshFeeds(maxAgeMs) {
|
||||||
|
if (typeof window.__dioraFeedsSummary !== 'function' || feedsBusy || !online()) return;
|
||||||
|
if (Date.now() - feedsAt < maxAgeMs) return;
|
||||||
|
feedsBusy = true;
|
||||||
|
window.__dioraFeedsSummary().then(r => { feeds = r; }, () => {}).then(() => {
|
||||||
|
feedsAt = Date.now();
|
||||||
|
feedsBusy = false;
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function feedsSection() {
|
||||||
|
if (!document.getElementById('tab-feeds') || !feeds || !feeds.state) return '';
|
||||||
|
const s = feeds.state;
|
||||||
|
const sources = s.sources || [];
|
||||||
|
if (!sources.length) return '';
|
||||||
|
const hasNews = sources.some(x => x.kind === 'news');
|
||||||
|
const top = ((feeds.briefing && feeds.briefing.top) || []).slice(0, 4);
|
||||||
|
let body = '';
|
||||||
|
if (top.length) {
|
||||||
|
body = '<div class="home-card home-news">' + top.map(x => {
|
||||||
|
const lead = x.lead || {};
|
||||||
|
return '<button type="button" class="home-row home-story" data-act="story" data-id="' + esc(x.id) + '">' +
|
||||||
|
'<span class="home-story-title">' + esc(lead.title) + '</span>' +
|
||||||
|
'<span class="home-row-side">' + esc(lead.source_title) + (x.sources > 1 ? ' +' + (x.sources - 1) : '') + '</span></button>';
|
||||||
|
}).join('') + '</div>';
|
||||||
|
} else if (hasNews) {
|
||||||
|
body = '<div class="home-card home-empty">' + (s.news_new ? 'Keine Top-Themen seit dem letzten Mal.' : 'Nichts Neues seit dem letzten Mal.') + '</div>';
|
||||||
|
}
|
||||||
|
const chips = (s.news_new ? '<button type="button" class="home-chip" data-act="feeds" data-view="news">' +
|
||||||
|
s.news_new + (s.news_new === 1 ? ' neue Meldung' : ' neue Meldungen') + '</button>' : '') +
|
||||||
|
(s.blogs_unread ? '<button type="button" class="home-chip" data-act="feeds" data-view="blogs">' +
|
||||||
|
s.blogs_unread + (s.blogs_unread === 1 ? ' neuer Blogartikel' : ' neue Blogartikel') + '</button>' : '');
|
||||||
|
if (!body && !chips) return '';
|
||||||
|
return section(hasNews ? 'Nachrichten' : 'Blogs', body + (chips ? '<div class="home-chips">' + chips + '</div>' : ''), 'feeds');
|
||||||
|
}
|
||||||
|
|
||||||
|
function openFeeds(view) {
|
||||||
|
showTab('feeds');
|
||||||
|
const b = view && document.querySelector('#tab-feeds [data-view="' + view + '"]:not(.on)');
|
||||||
|
if (b) b.click();
|
||||||
|
}
|
||||||
|
|
||||||
// ---- Radio ------------------------------------------------------------------
|
// ---- Radio ------------------------------------------------------------------
|
||||||
|
|
||||||
function stationRows() {
|
function stationRows() {
|
||||||
|
|
@ -205,7 +255,8 @@
|
||||||
tab.appendChild(host);
|
tab.appendChild(host);
|
||||||
}
|
}
|
||||||
let html = '<div class="home-brand">diora</div>';
|
let html = '<div class="home-brand">diora</div>';
|
||||||
for (const part of [booksSection, podcastSection, radioSection, ambientSection]) {
|
refreshFeeds(3 * 60000);
|
||||||
|
for (const part of [booksSection, podcastSection, feedsSection, radioSection, ambientSection]) {
|
||||||
try { html += part(); } catch (e) { console.warn('home', e); }
|
try { html += part(); } catch (e) { console.warn('home', e); }
|
||||||
}
|
}
|
||||||
if (html !== lastHtml) {
|
if (html !== lastHtml) {
|
||||||
|
|
@ -230,6 +281,12 @@
|
||||||
const h = podHost();
|
const h = podHost();
|
||||||
if (act === 'tab') showTab(el.dataset.tab);
|
if (act === 'tab') showTab(el.dataset.tab);
|
||||||
else if (act === 'book') openBook(Number(el.dataset.id));
|
else if (act === 'book') openBook(Number(el.dataset.id));
|
||||||
|
else if (act === 'story') {
|
||||||
|
const id = Number(el.dataset.id);
|
||||||
|
const story = feeds && feeds.briefing && (feeds.briefing.top || []).find(x => x.id === id);
|
||||||
|
if (story && window.__dioraFeedsOpenStory) { window.__dioraFeedsOpenStory(story); feedsAt = 0; }
|
||||||
|
else openFeeds('news');
|
||||||
|
} else if (act === 'feeds') { openFeeds(el.dataset.view); feedsAt = 0; }
|
||||||
else if (act === 'station') playStationFromHome(el.dataset.url, el.dataset.name);
|
else if (act === 'station') playStationFromHome(el.dataset.url, el.dataset.name);
|
||||||
else if (act === 'pod-queue' && h) {
|
else if (act === 'pod-queue' && h) {
|
||||||
const q = (parse(h.podcastState(), {}).queue || [])[0];
|
const q = (parse(h.podcastState(), {}).queue || [])[0];
|
||||||
|
|
@ -250,7 +307,10 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fresh whenever it shows, and while it shows (players move on, the book list arrives).
|
// Fresh whenever it shows, and while it shows (players move on, the book list arrives).
|
||||||
window.__dioraHomeShow = render;
|
window.__dioraHomeShow = function () {
|
||||||
|
refreshFeeds(30000);
|
||||||
|
render();
|
||||||
|
};
|
||||||
if (typeof window.renderBookList === 'function') {
|
if (typeof window.renderBookList === 'function') {
|
||||||
const renderBookList = window.renderBookList;
|
const renderBookList = window.renderBookList;
|
||||||
window.renderBookList = function () {
|
window.renderBookList = function () {
|
||||||
|
|
@ -292,6 +352,11 @@
|
||||||
'#tab-home .home-row { display: flex; justify-content: space-between; gap: 12px; width: 100%; padding: 10px 14px; background: none; border: none; border-bottom: 1px solid var(--border, #2a2a2a); color: inherit; font: inherit; font-size: 14px; text-align: left; }',
|
'#tab-home .home-row { display: flex; justify-content: space-between; gap: 12px; width: 100%; padding: 10px 14px; background: none; border: none; border-bottom: 1px solid var(--border, #2a2a2a); color: inherit; font: inherit; font-size: 14px; text-align: left; }',
|
||||||
'#tab-home .home-row-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }',
|
'#tab-home .home-row-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }',
|
||||||
'#tab-home .home-row-side { flex: none; color: var(--nui-muted, #9a9a9a); font-size: 12px; }',
|
'#tab-home .home-row-side { flex: none; color: var(--nui-muted, #9a9a9a); font-size: 12px; }',
|
||||||
|
'#tab-home .home-news { flex-direction: column; align-items: stretch; gap: 0; padding: 4px 0; }',
|
||||||
|
'#tab-home .home-story { align-items: baseline; }',
|
||||||
|
'#tab-home .home-news .home-story:last-child { border-bottom: none; }',
|
||||||
|
'#tab-home .home-story-title { overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; line-height: 1.35; }',
|
||||||
|
'#tab-home .home-story .home-row-side { max-width: 35%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }',
|
||||||
'#tab-home .home-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }',
|
'#tab-home .home-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }',
|
||||||
'#tab-home .home-chip { padding: 6px 12px; border: 1px solid var(--border, #2a2a2a); border-radius: 16px; background: none; color: inherit; font: inherit; font-size: 13px; cursor: pointer; }',
|
'#tab-home .home-chip { padding: 6px 12px; border: 1px solid var(--border, #2a2a2a); border-radius: 16px; background: none; color: inherit; font: inherit; font-size: 13px; cursor: pointer; }',
|
||||||
'#tab-home .home-stations { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 8px; }',
|
'#tab-home .home-stations { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 8px; }',
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
* diora service worker — caches the app shell for offline use.
|
* diora service worker — caches the app shell for offline use.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const CACHE = 'diora-v89';
|
const CACHE = 'diora-v90';
|
||||||
const SHELL = [
|
const SHELL = [
|
||||||
'/static/css/app.css',
|
'/static/css/app.css',
|
||||||
'/static/js/app.js',
|
'/static/js/app.js',
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue