Files
TagesByte/lib/models/source.dart
CedricandClaude Opus 5.5 246564f1d9 Keep per-kind facts on SourceKind and the Briefing's input in one place (review items 4, 5, 6, 7)
- SourceKind carries whether it feeds the Briefing, its fixed time window
  (Learning, Fun) and whether reading is picture-led (Fun); windowOf and
  the reader read from it.
- AppState.briefingArticles is the one place that says only News sources
  feed the Briefing; generateBriefing and the Briefing tab use it.
- articlesOf, hiddenCountOf and briefingArticles share one filter.
- FeedService.fetchAll takes windowOf: like AppState.windowOf.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 16:07:37 +02:00

89 lines
2.5 KiB
Dart

enum SourceLanguage {
de('Deutsch', '🇩🇪'),
en('English', '🇬🇧');
const SourceLanguage(this.label, this.flag);
final String label;
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', feedsBriefing: true),
learning('Learning', fixedWindow: Duration(days: 14)),
fun('Fun', fixedWindow: Duration(days: 3), pictureLed: true);
const SourceKind(
this.label, {
this.feedsBriefing = false,
this.fixedWindow,
this.pictureLed = false,
});
final String label;
/// Only News sources feed the Briefing.
final bool feedsBriefing;
/// How far back articles reach; null where the user sets it (News).
final Duration? fixedWindow;
/// Comics and memes are the picture: the reader shows it whole in the body
/// rather than as a cropped banner.
final bool pictureLed;
}
/// A Source of any [kind]; see GLOSSARY.md.
class Source {
const Source({
required this.id,
required this.name,
required this.feedUrl,
required this.homepage,
required this.language,
this.kind = SourceKind.news,
this.description = '',
this.enabledByDefault = false,
this.isCustom = false,
this.checkPaywallOnPage = false,
});
final String id;
final String name;
final String feedUrl;
final String homepage;
final SourceLanguage language;
final SourceKind kind;
final String description;
final bool enabledByDefault;
final bool isCustom;
/// Paid articles are only marked inside the page (schema.org), not in the
/// feed, so each new article page is checked once.
final bool checkPaywallOnPage;
Map<String, dynamic> toJson() => {
'id': id,
'name': name,
'feedUrl': feedUrl,
'homepage': homepage,
'language': language.name,
'kind': kind.name,
'description': description,
};
factory Source.custom(Map<String, dynamic> json) => Source(
id: json['id'] as String,
name: json['name'] as String,
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,
);
}