The flow ran on a Pixel 9a and its emulator (2026-10-02); what it found: - The composer stayed on the card just written after "New flashcard", and on the original after "Duplicate": leaving a card changed the state before the new one was in it, so the screen took the new card for gone. It now waits for the card it asked for. - Bottom sheets reached under the status bar with the keyboard open (useSafeArea). - The field being written stayed behind the keyboard: the editor scrolls only within itself, so the card face now keeps it in view when it opens and when the keyboard rises, as a text field does. - A tap on read-only rich text put a caret in it and opened the keyboard on phones, and in the composer never opened the field. RichContentView is display only now: no selection, taps go to what surrounds it, a sound's button still plays (decided 2026-10-02). - The card types broke their names on a phone; they are choice chips that wrap (decided 2026-10-02). - The week overview marked days before the person's first activity as missed; they show nothing now. An import starts the activity too, without counting as studying (decided 2026-10-02). - A composer preview cut short gave no sign of it; it fades out over its last 32 dp, only when cut short (decided 2026-10-02). - "Isn't on this device yet" showed for any image or sound that failed; a file that is here but can't be decoded now says so, and offers no retry. - The signed-in account card was narrower than the anonymous one. - The composer's title said "Create flashcards" also when opened on a stored card; it says "Edit flashcards in …" then (decided 2026-10-02). - The Anki fixtures carry a valid PNG and a silent MP3, so they also serve to try the import by hand (decided 2026-10-02); regenerated with Anki 26.9.3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
45 lines
1.1 KiB
Dart
45 lines
1.1 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:halkyon_learn_flutter/src/core/asset/data/sound_player.dart';
|
|
|
|
/// A [SoundPlayer] that plays nothing and notes what it was asked to; a sound
|
|
/// plays until stopped or [finish]ed.
|
|
class FakeSoundPlayer() implements SoundPlayer {
|
|
final _playing = StreamController<String?>.broadcast();
|
|
|
|
/// The paths it was asked to play, in order.
|
|
final played = <String>[];
|
|
|
|
/// The file "playing" now.
|
|
String? current;
|
|
|
|
/// What playing throws, as for a file the platform can't decode; plays
|
|
/// while null.
|
|
Exception? failPlay;
|
|
|
|
@override
|
|
Stream<String?> get playing => _playing.stream;
|
|
|
|
@override
|
|
Future<void> play(String path, {required String mimeType}) async {
|
|
if (failPlay case final error?) {
|
|
throw error;
|
|
}
|
|
played.add(path);
|
|
_set(path);
|
|
}
|
|
|
|
@override
|
|
Future<void> stop() async => _set(null);
|
|
|
|
/// Ends the sound playing, as when it reached its end.
|
|
void finish() => _set(null);
|
|
|
|
void _set(String? path) {
|
|
current = path;
|
|
_playing.add(path);
|
|
}
|
|
|
|
@override
|
|
Future<void> dispose() => _playing.close();
|
|
}
|