fix(train): a recording's screen opens as it starts, and pages run on beneath the floating bar (ADR 0030, D21)
An indoor row started showed its notification but not its screen: the Record tab's card pushed the screen only after the service had started, and gave up silently where the card was gone by then. Train's frame now opens it as the recording's phase turns to recording; one found running as the app opens leaves the tabs as they were. The notification tapped opens the recording's screen, not merely the app (RecordingOpenings.kt). A workout's page kept a SafeArea at its foot, whose inset under the floating bar is the bar itself: its content stopped at the bar's top, a plain band behind it. It runs on beneath the bar as the tabs do, and the tabs' lists and a day's page end with the body's bottom padding rather than a fixed 88 px, which left their last item under the bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
e26ba47fb9
commit
1aa679fe3e
2 changed files with 258 additions and 20 deletions
|
|
@ -39,6 +39,7 @@ import 'package:halkyon_train_flutter/src/feature/recording/data/recorder_platfo
|
|||
import 'package:halkyon_train_flutter/src/feature/recording/model/recording.dart';
|
||||
import 'package:halkyon_train_flutter/src/feature/recording/widget/record_sheet.dart';
|
||||
import 'package:halkyon_train_flutter/src/feature/recording/widget/recording_bar.dart';
|
||||
import 'package:halkyon_train_flutter/src/feature/recording/widget/recording_screen.dart';
|
||||
import 'package:halkyon_train_flutter/src/feature/route/data/place_search_gateway.dart';
|
||||
import 'package:halkyon_train_flutter/src/feature/route/data/route_files.dart';
|
||||
import 'package:halkyon_train_flutter/src/feature/route/data/route_repository.dart';
|
||||
|
|
@ -1431,6 +1432,37 @@ void main() {
|
|||
});
|
||||
});
|
||||
|
||||
testWidgets('a recording started opens its screen once; its '
|
||||
'notification tapped opens it again from any tab', (tester) async {
|
||||
await withClock(Clock(() => at), () async {
|
||||
await startRecording(tester);
|
||||
await tester.tap(startFor('Walking'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(sheetStart);
|
||||
await pass(tester, 2);
|
||||
expect(find.byType(RecordingScreen), findsOneWidget);
|
||||
|
||||
await tester.pageBack();
|
||||
await tester.pumpAndSettle();
|
||||
await goTab(tester, 'Home');
|
||||
expect(find.byType(RecordingScreen), findsNothing);
|
||||
|
||||
platform.openNotification();
|
||||
await pass(tester);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(RecordingScreen), findsOneWidget);
|
||||
|
||||
platform.openNotification();
|
||||
await pass(tester);
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
find.byType(RecordingScreen),
|
||||
findsOneWidget,
|
||||
reason: 'not a second one over the first',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('a run plays the metronome chosen, Music not offered '
|
||||
'without kept tracks, a ride neither (D48)', (tester) async {
|
||||
await withClock(Clock(() => at), () async {
|
||||
|
|
@ -2198,6 +2230,11 @@ void main() {
|
|||
expect(platform.started.single.rower, rower.address);
|
||||
expect(platform.started.single.autoPause, AutoPauseBy.strokes);
|
||||
expect(platform.scanned, [DeviceKind.rower]);
|
||||
expect(
|
||||
find.byType(RecordingScreen),
|
||||
findsOneWidget,
|
||||
reason: 'the recording on screen, not in the notification alone',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -3232,6 +3269,41 @@ void main() {
|
|||
);
|
||||
});
|
||||
|
||||
testWidgets('home and a workout’s page run on beneath the floating bar, '
|
||||
'their lists ending clear of it', (tester) async {
|
||||
for (var day = 1; day <= 12; day++) {
|
||||
await saveRun(DateTime(2026, 10, day, 7, 30));
|
||||
}
|
||||
await startWith(tester);
|
||||
tester.view.padding = const FakeViewPadding(bottom: 48);
|
||||
await tester.pumpAndSettle();
|
||||
final bar = find.byType(FloatingNavigationBar);
|
||||
final screen = tester.view.physicalSize.height;
|
||||
|
||||
Future<void> endsClearOfTheBar(Finder list) async {
|
||||
expect(
|
||||
tester.getBottomLeft(list).dy,
|
||||
screen,
|
||||
reason: 'the list runs on beneath the bar',
|
||||
);
|
||||
await tester.drag(list, const Offset(0, -5000));
|
||||
await tester.pumpAndSettle();
|
||||
final items = find.descendant(of: list, matching: find.byType(Text));
|
||||
expect(
|
||||
tester.getBottomLeft(items.last).dy,
|
||||
lessThan(tester.getTopLeft(bar).dy),
|
||||
reason: 'its end scrolls out from under the bar',
|
||||
);
|
||||
}
|
||||
|
||||
await endsClearOfTheBar(find.byType(Scrollable).first);
|
||||
await tester.drag(find.byType(Scrollable).first, const Offset(0, 5000));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Morning run').first);
|
||||
await tester.pumpAndSettle();
|
||||
await endsClearOfTheBar(find.byType(Scrollable).last);
|
||||
});
|
||||
|
||||
testWidgets('above them, this week: its time, distance and workouts, and '
|
||||
'its days from Monday in British English (D60)', (tester) async {
|
||||
tester.platformDispatcher.localesTestValue = const [Locale('en', 'GB')];
|
||||
|
|
|
|||
|
|
@ -159,6 +159,14 @@ class FakeRecorderPlatform({
|
|||
Future<({double lat, double lon})?> position() async =>
|
||||
allows.contains(RecorderPermission.location) ? at : null;
|
||||
|
||||
final _openings = StreamController<void>.broadcast();
|
||||
|
||||
@override
|
||||
Stream<void> openings() => _openings.stream;
|
||||
|
||||
/// The recording's notification tapped.
|
||||
void openNotification() => _openings.add(null);
|
||||
|
||||
/// A recording that runs on, as when the app comes back to one.
|
||||
void runOn(String path) => _file = File(path);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue