Add source kinds and the Learn tab with Learning sources (#2)

Every Source now has a kind: News, Learning or Fun. Curated Sources default
to News, and custom Sources persist their kind (ones saved before default
to News). Each kind has its own time window: News follows the 12/24/48 h
setting, Learning reaches back 14 days, Fun 3 days.

A new Learn tab between Briefing and Sources lists Learning articles with
the same paid/sponsored filters and reader view as Today. It is built from
a list of kinds so a Fun section can follow. Today and the Briefing only
use News. ByteByteGo is the first Learning source, on by default.

The home shell names its tabs instead of using hard-coded indexes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
CedricandClaude Opus 5.5 committed 2026-10-16 15:44:52 +02:00
1 parent 8c8ffca5dc
commit 64c4a11105
12 files changed
+498 -32

No files matched your search

+18
View File
@@ -8,6 +8,19 @@ enum SourceLanguage {
final String flag;
}
/// What a Source's articles are for; see GLOSSARY.md. Each kind has its own
/// section in the app and its own time window.
enum SourceKind {
news('News'),
learning('Learning'),
fun('Fun');
const SourceKind(this.label);
final String label;
}
/// A Source of any [kind]; the class name predates the kinds.
class NewsSource {
const NewsSource({
required this.id,
@@ -15,6 +28,7 @@ class NewsSource {
required this.feedUrl,
required this.homepage,
required this.language,
this.kind = SourceKind.news,
this.description = '',
this.enabledByDefault = false,
this.isCustom = false,
@@ -26,6 +40,7 @@ class NewsSource {
final String feedUrl;
final String homepage;
final SourceLanguage language;
final SourceKind kind;
final String description;
final bool enabledByDefault;
final bool isCustom;
@@ -40,6 +55,7 @@ class NewsSource {
'feedUrl': feedUrl,
'homepage': homepage,
'language': language.name,
'kind': kind.name,
'description': description,
};
@@ -49,6 +65,8 @@ class NewsSource {
feedUrl: json['feedUrl'] as String,
homepage: json['homepage'] as String? ?? '',
language: SourceLanguage.values.byName(json['language'] as String? ?? 'en'),
// Custom sources saved before kinds existed are News sources.
kind: SourceKind.values.asNameMap()[json['kind']] ?? SourceKind.news,
description: json['description'] as String? ?? '',
isCustom: true,
);