Rename NewsSource to Source now that it models every kind (review item 3)
The class and its file (lib/models/source.dart) follow the glossary term. Persisted JSON keys are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1a3442ea02
commit
a47ff28ed1
33 files changed
+119
-119
No files matched your search
@@ -5,7 +5,7 @@ import 'package:http/http.dart' as http;
|
||||
|
||||
import '../models/article.dart';
|
||||
import '../models/briefing.dart';
|
||||
import '../models/news_source.dart';
|
||||
import '../models/source.dart';
|
||||
import 'summarizer.dart';
|
||||
|
||||
/// Calls the Claude Messages API directly over HTTP (there is no official
|
||||
|
||||
@@ -3,13 +3,13 @@ import 'package:html/parser.dart' as html_parser;
|
||||
import 'package:xml/xml.dart';
|
||||
|
||||
import '../models/article.dart';
|
||||
import '../models/news_source.dart';
|
||||
import '../models/source.dart';
|
||||
|
||||
/// Parses RSS 2.0, RSS 1.0 (RDF) and Atom feeds into [Article]s.
|
||||
class FeedParser {
|
||||
static const excerptLength = 400;
|
||||
|
||||
static List<Article> parse(String body, NewsSource source) {
|
||||
static List<Article> parse(String body, Source source) {
|
||||
final doc = XmlDocument.parse(body);
|
||||
final root = doc.rootElement;
|
||||
if (root.name.local == 'feed') {
|
||||
@@ -27,7 +27,7 @@ class FeedParser {
|
||||
.toList();
|
||||
}
|
||||
|
||||
static Article? _parseRssItem(XmlElement item, NewsSource source) {
|
||||
static Article? _parseRssItem(XmlElement item, Source source) {
|
||||
final title = htmlToText(_text(item, 'title'));
|
||||
final link = _text(item, 'link').trim().isNotEmpty
|
||||
? _text(item, 'link').trim()
|
||||
@@ -53,7 +53,7 @@ class FeedParser {
|
||||
);
|
||||
}
|
||||
|
||||
static Article? _parseAtomEntry(XmlElement entry, NewsSource source) {
|
||||
static Article? _parseAtomEntry(XmlElement entry, Source source) {
|
||||
final title = htmlToText(_text(entry, 'title'));
|
||||
final links = entry.childElements.where((e) => e.name.local == 'link');
|
||||
final link = links
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../models/article.dart';
|
||||
import '../models/news_source.dart';
|
||||
import '../models/source.dart';
|
||||
import 'feed_parser.dart';
|
||||
|
||||
class FeedResult {
|
||||
@@ -30,8 +30,8 @@ class FeedService {
|
||||
/// [corsProxy] is only used on web, where browsers block cross-origin feed
|
||||
/// requests. It is prepended to the feed URL, e.g. `https://proxy/?url=`.
|
||||
Future<FeedResult> fetchAll(
|
||||
List<NewsSource> sources, {
|
||||
required Duration Function(NewsSource source) maxAgeOf,
|
||||
List<Source> sources, {
|
||||
required Duration Function(Source source) maxAgeOf,
|
||||
String corsProxy = '',
|
||||
}) async {
|
||||
final errors = <String, String>{};
|
||||
@@ -64,7 +64,7 @@ class FeedService {
|
||||
return FeedResult(articles, errors);
|
||||
}
|
||||
|
||||
Future<List<Article>> fetchSource(NewsSource source, {String corsProxy = ''}) async {
|
||||
Future<List<Article>> fetchSource(Source source, {String corsProxy = ''}) async {
|
||||
final url = kIsWeb && corsProxy.isNotEmpty
|
||||
? '$corsProxy${Uri.encodeComponent(source.feedUrl)}'
|
||||
: source.feedUrl;
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'package:http/http.dart' as http;
|
||||
|
||||
import '../models/article.dart';
|
||||
import '../models/briefing.dart';
|
||||
import '../models/news_source.dart';
|
||||
import '../models/source.dart';
|
||||
import 'summarizer.dart';
|
||||
|
||||
/// Delegates summarizing to an existing n8n workflow via a Webhook trigger.
|
||||
|
||||
@@ -9,7 +9,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../models/app_settings.dart';
|
||||
import '../models/briefing.dart';
|
||||
import '../models/news_source.dart';
|
||||
import '../models/source.dart';
|
||||
|
||||
/// Persists settings, source selection and the last briefing.
|
||||
/// The Claude API key goes to the platform keystore, everything else to
|
||||
@@ -81,15 +81,15 @@ class Storage {
|
||||
Future<void> saveSeenSourceIds(Set<String> ids) =>
|
||||
_prefs.setStringList('seen_sources', ids.toList());
|
||||
|
||||
List<NewsSource> loadCustomSources() {
|
||||
List<Source> loadCustomSources() {
|
||||
final raw = _prefs.getString('custom_sources');
|
||||
if (raw == null) return [];
|
||||
return (jsonDecode(raw) as List)
|
||||
.map((j) => NewsSource.custom(j as Map<String, dynamic>))
|
||||
.map((j) => Source.custom(j as Map<String, dynamic>))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<void> saveCustomSources(List<NewsSource> sources) => _prefs.setString(
|
||||
Future<void> saveCustomSources(List<Source> sources) => _prefs.setString(
|
||||
'custom_sources', jsonEncode(sources.map((s) => s.toJson()).toList()));
|
||||
|
||||
/// Article URL -> whether its page said it's paid, and when that was checked.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import '../models/article.dart';
|
||||
import '../models/briefing.dart';
|
||||
import '../models/news_source.dart';
|
||||
import '../models/source.dart';
|
||||
|
||||
class SummarizerException implements Exception {
|
||||
SummarizerException(this.message);
|
||||
|
||||
Reference in new issue
Block a user