From 1a17c75c1be8ee05f7776f0d7b43a699068c8509 Mon Sep 17 00:00:00 2001 From: mrwnslz Date: Sun, 4 Oct 2026 20:22:56 +0200 Subject: [PATCH] =?UTF-8?q?Home:=20Karte=20=E2=80=9ENachrichten=E2=80=9C?= =?UTF-8?q?=20mit=20den=20Top-Themen=20seit=20dem=20letzten=20Mal,=20neue?= =?UTF-8?q?=20Meldungen=20und=20Blogartikel;=20sw=20v90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- static/js/home.js | 73 ++++++++++++++++++++++++++++++++++++++++++++--- static/js/sw.js | 2 +- 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/static/js/home.js b/static/js/home.js index a689d20..e50cf7f 100644 --- a/static/js/home.js +++ b/static/js/home.js @@ -1,11 +1,11 @@ /** * 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 * podcast host (window.DioraPodcastHost in the browser and diora desktop, * 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/). * Written without ?. and ?? so old WebViews can still parse it. */ @@ -125,6 +125,56 @@ button + ''; } + // ---- 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 = '
' + top.map(x => { + const lead = x.lead || {}; + return ''; + }).join('') + '
'; + } else if (hasNews) { + body = '
' + (s.news_new ? 'Keine Top-Themen seit dem letzten Mal.' : 'Nichts Neues seit dem letzten Mal.') + '
'; + } + const chips = (s.news_new ? '' : '') + + (s.blogs_unread ? '' : ''); + if (!body && !chips) return ''; + return section(hasNews ? 'Nachrichten' : 'Blogs', body + (chips ? '
' + chips + '
' : ''), 'feeds'); + } + + function openFeeds(view) { + showTab('feeds'); + const b = view && document.querySelector('#tab-feeds [data-view="' + view + '"]:not(.on)'); + if (b) b.click(); + } + // ---- Radio ------------------------------------------------------------------ function stationRows() { @@ -205,7 +255,8 @@ tab.appendChild(host); } let html = '
diora
'; - 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); } } if (html !== lastHtml) { @@ -230,6 +281,12 @@ const h = podHost(); if (act === 'tab') showTab(el.dataset.tab); 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 === 'pod-queue' && h) { 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). - window.__dioraHomeShow = render; + window.__dioraHomeShow = function () { + refreshFeeds(30000); + render(); + }; if (typeof window.renderBookList === 'function') { const renderBookList = window.renderBookList; 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-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-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-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; }', diff --git a/static/js/sw.js b/static/js/sw.js index a35056d..98827c7 100644 --- a/static/js/sw.js +++ b/static/js/sw.js @@ -2,7 +2,7 @@ * diora service worker — caches the app shell for offline use. */ -const CACHE = 'diora-v89'; +const CACHE = 'diora-v90'; const SHELL = [ '/static/css/app.css', '/static/js/app.js',