feat(meditate): the library says what a recording speaks, and shows the languages chosen (ADR 0038, D17)
An item's page names its spoken language. Where the library has several, its cards do too and Explore offers them, several at once, none meaning every one, kept in the person's sealed settings. A library with one language asks nothing. Whats-New: The library says each recording's spoken language, and Explore lets you choose among several. Whats-New-De: Die Bibliothek nennt die gesprochene Sprache jeder Aufnahme, und bei mehreren wählst du sie in „Entdecken“ aus. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
1fa5890f8e
commit
3e69e6b2f7
3 changed files with 287 additions and 4 deletions
|
|
@ -2,6 +2,8 @@ import 'package:clock/clock.dart';
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:halkyon_app/halkyon_app.dart';
|
||||
import 'package:halkyon_app/testing.dart';
|
||||
import 'package:halkyon_meditate_client/halkyon_meditate_client.dart'
|
||||
show LibraryAudio, LibraryItem;
|
||||
import 'package:halkyon_meditate_flutter/src/app/meditate_app.dart';
|
||||
import 'package:halkyon_meditate_flutter/src/app/meditate_dependencies.dart';
|
||||
import 'package:halkyon_meditate_flutter/src/core/privacy/meditate_keys.dart';
|
||||
|
|
@ -1280,6 +1282,48 @@ void main() {
|
|||
expect(find.text('Library credits'), findsNothing);
|
||||
});
|
||||
|
||||
group('spoken languages (5 Nov 2026)', () {
|
||||
final german = LibraryItem(
|
||||
id: 'abendruhe',
|
||||
title: 'Abendruhe',
|
||||
category: 'meditation',
|
||||
language: 'de-CH',
|
||||
audio: [
|
||||
LibraryAudio(file: 'b.mp3', durationSeconds: 300, size: 1),
|
||||
],
|
||||
licence: 'CC0',
|
||||
credit: 'Niemand',
|
||||
);
|
||||
|
||||
testWidgets('asked only where the library has several', (tester) async {
|
||||
await exploreLibrary(tester);
|
||||
await scrollTo(tester, find.text('From meditate.example.org'));
|
||||
expect(find.text('Spoken language'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('where it has several, the items of those chosen show, '
|
||||
'several at once, none meaning every one', (tester) async {
|
||||
final sample = sampleCatalogue();
|
||||
library.answer = sample.copyWith(items: [...sample.items, german]);
|
||||
await start(tester, usedBefore: true);
|
||||
await goTab(tester, 'Explore');
|
||||
await scrollTo(tester, find.text('Spoken language'));
|
||||
await scrollTo(tester, find.text('Abendruhe'));
|
||||
expect(find.textContaining('· German ·'), findsOneWidget);
|
||||
|
||||
await scrollTo(tester, find.widgetWithText(FilterChip, 'English'));
|
||||
await tester.tap(find.widgetWithText(FilterChip, 'English'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Abendruhe'), findsNothing);
|
||||
expect(find.text('Evening rest'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.widgetWithText(FilterChip, 'German'));
|
||||
await tester.pumpAndSettle();
|
||||
await scrollTo(tester, find.text('Abendruhe'));
|
||||
expect(find.text('Abendruhe'), findsOneWidget);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets("Explore shows the instance's courses and items below the "
|
||||
'practices, by category and by the search', (tester) async {
|
||||
await exploreLibrary(tester);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import 'package:clock/clock.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:halkyon_app/testing.dart';
|
||||
import 'package:halkyon_meditate_client/halkyon_meditate_client.dart'
|
||||
show LibraryCatalogue;
|
||||
import 'package:halkyon_meditate_flutter/src/core/privacy/meditate_keys.dart';
|
||||
import 'package:halkyon_meditate_flutter/src/core/sealed/json_payload.dart';
|
||||
import 'package:halkyon_meditate_flutter/src/core/sound/guide_player.dart';
|
||||
|
|
@ -293,4 +295,31 @@ void main() {
|
|||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('spoken languages', () {
|
||||
LibraryCatalogue speaking(List<String> languages) {
|
||||
final sample = sampleCatalogue();
|
||||
return sample.copyWith(
|
||||
courses: const [],
|
||||
items: [
|
||||
for (final language in languages)
|
||||
sample.items.first.copyWith(language: language),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
test('are told apart by primary language', () {
|
||||
expect(languageKey('de-CH'), 'de');
|
||||
expect(speaking(['en', 'de-CH', 'de']).languagesUsed, ['de', 'en']);
|
||||
});
|
||||
|
||||
test('are no choice with only one', () {
|
||||
expect(speaking(['de', 'de-AT']).languagesShown(['de']), isEmpty);
|
||||
});
|
||||
|
||||
test('forget what is no longer offered', () {
|
||||
expect(speaking(['en', 'de']).languagesShown(['en', 'fr']), ['en']);
|
||||
expect(speaking(['en', 'de']).languagesShown(['fr']), isEmpty);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,4 +64,17 @@ void main() {
|
|||
expect(once.isFavourite('body-scan'), isTrue);
|
||||
expect(once.toggleFavourite('body-scan').favourites, isEmpty);
|
||||
});
|
||||
|
||||
test("the library's spoken languages are kept; a list of something else "
|
||||
'reads as none', () {
|
||||
const settings = MeditateSettings(libraryLanguages: ['de', 'en']);
|
||||
expect(
|
||||
decodeSettings(encodeSettings(settings))?.libraryLanguages,
|
||||
settings.libraryLanguages,
|
||||
);
|
||||
expect(
|
||||
json(encodeSettings(const MeditateSettings())),
|
||||
isNot(contains('libraryLanguages')),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue