Switch newly added default Sources on once (#1)

The app now remembers which curated Sources it has seen. A Source that is on
by default is enabled the first time it appears, also on installs with saved
toggles; Sources seen before keep the user's choice. Installs from before this
change count the current catalog as seen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
CedricandClaude Opus 5.5 committed 2026-10-16 15:35:36 +02:00
1 parent f35ac9b806
commit ea37a23c08
4 files changed
+135 -5

No files matched your search

+89
View File
@@ -0,0 +1,89 @@
import 'package:daily_tech_news/data/default_sources.dart';
import 'package:daily_tech_news/models/news_source.dart';
import 'package:daily_tech_news/services/feed_service.dart';
import 'package:daily_tech_news/services/storage.dart';
import 'package:daily_tech_news/state/app_state.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart';
/// Returns no articles, so starting the app doesn't hit the network.
class _NoFeeds extends FeedService {
@override
Future<FeedResult> fetchAll(List<NewsSource> sources,
{required Duration maxAge, String corsProxy = ''}) async =>
const FeedResult([], {});
}
NewsSource _source(String id, {bool onByDefault = false}) => NewsSource(
id: id,
name: id,
feedUrl: 'https://example.com/$id.xml',
homepage: 'https://example.com',
language: SourceLanguage.en,
enabledByDefault: onByDefault,
);
/// Starts the app the way a launch does, against whatever is in preferences.
Future<AppState> _launch(List<NewsSource> curatedSources) async {
final state =
AppState(await Storage.open(), feedService: _NoFeeds(), curatedSources: curatedSources);
await state.init();
return state;
}
void main() {
final heise = _source('heise', onByDefault: true);
final golem = _source('golem', onByDefault: true);
final wired = _source('wired');
setUp(() => SharedPreferences.setMockInitialValues({}));
test('a fresh install starts with exactly the on-by-default Sources', () async {
final state = await _launch([heise, golem, wired]);
expect(state.enabledSourceIds, {'heise', 'golem'});
});
test('a newly added on-by-default Source shows up enabled after the update', () async {
final before = await _launch([heise, golem, wired]);
await before.setSourceEnabled(wired, true);
final verge = _source('verge', onByDefault: true);
final after = await _launch([heise, golem, wired, verge]);
expect(after.enabledSourceIds, {'heise', 'golem', 'wired', 'verge'});
});
test('a Source the user switched off stays off across updates', () async {
final before = await _launch([heise, golem, wired]);
await before.setSourceEnabled(golem, false);
final verge = _source('verge', onByDefault: true);
await _launch([heise, golem, wired, verge]);
final later = await _launch([heise, golem, wired, verge]);
expect(later.enabledSourceIds, {'heise', 'verge'});
});
test('a newly added Source that is off by default stays off', () async {
final before = await _launch([heise, golem, wired]);
await before.setSourceEnabled(wired, true);
final engadget = _source('engadget');
final after = await _launch([heise, golem, wired, engadget]);
expect(after.enabledSourceIds, {'heise', 'golem', 'wired'});
});
test('an install from before the app remembered seen Sources keeps its toggles', () async {
// Toggles saved by an earlier version: golem, t3n and others switched off.
SharedPreferences.setMockInitialValues({
'enabled_sources': ['heise', 'verge', 'wired'],
});
final newSource = _source('new-source', onByDefault: true);
final state = await _launch([...defaultSources, newSource]);
expect(state.enabledSourceIds, {'heise', 'verge', 'wired', 'new-source'});
});
}