feat(train): a workout's page as Strava's, maps readable and in the app's language, routes opening near the person (ADR 0030, D63)
- A workout with a track: its map to the foot of the screen under a sheet with its name; once the sheet settles, the map fits the track anew, eased, into what is left free; pulled up, the round buttons over the map fold into an app bar whose chevron lowers the sheet again. From expanded windows on, the map beside the rest. The page stays in Home but opens over the shell, without the floating bar. - What the map's source sees moves behind a question mark. - The map's credit on the app's theme: flutter_map's, built on flutter/material, never saw it (material_ui's Theme is a class of its own) and drew the theme's text on white; map_tiles read that Theme too, and so never chose the instance's dark map (D50). - Places named in the app's language (name:<language>) where OpenFreeMap's styles ask for the English name. - The route planner's hint behind a question mark; a new route's map opens at zoom 14 where the phone was last known to be, else where the newest workout with a track started; its details beside the map from expanded windows on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
30dd8c96eb
commit
3e90753b08
2 changed files with 1518 additions and 358 deletions
|
|
@ -59,6 +59,87 @@ void main() {
|
|||
expect(tileHeadersOf(provider), isNull);
|
||||
},
|
||||
);
|
||||
|
||||
group("a provider's places in the app's language (D63)", () {
|
||||
// OpenFreeMap's Liberty, as it labels places: English, else the name
|
||||
// on the spot.
|
||||
final liberty = {
|
||||
'version': 8,
|
||||
'layers': [
|
||||
{
|
||||
'id': 'poi',
|
||||
'type': 'symbol',
|
||||
'paint': {'text-color': '#333333'},
|
||||
'layout': {
|
||||
'text-field': [
|
||||
'case',
|
||||
['has', 'name:nonlatin'],
|
||||
[
|
||||
'concat',
|
||||
['get', 'name:latin'],
|
||||
'\n',
|
||||
['get', 'name:nonlatin'],
|
||||
],
|
||||
[
|
||||
'coalesce',
|
||||
['get', 'name_en'],
|
||||
['get', 'name'],
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 'old',
|
||||
'type': 'symbol',
|
||||
'paint': {'text-color': '#333333'},
|
||||
'layout': {'text-field': '{name_en}'},
|
||||
},
|
||||
{
|
||||
'id': 'road-ref',
|
||||
'type': 'symbol',
|
||||
'paint': {'text-color': '#333333'},
|
||||
'layout': {
|
||||
'text-field': [
|
||||
'to-string',
|
||||
['get', 'ref'],
|
||||
],
|
||||
},
|
||||
},
|
||||
{'id': 'water', 'type': 'fill'},
|
||||
],
|
||||
};
|
||||
|
||||
test('in German, the German name before the name on the spot', () {
|
||||
final german = labelledIn(liberty, 'de');
|
||||
final layers = german['layers']! as List<Object?>;
|
||||
expect(
|
||||
jsonEncode((layers[0]! as Map)['layout']),
|
||||
contains('["coalesce",["get","name:de"],["get","name"]]'),
|
||||
);
|
||||
expect(
|
||||
((layers[1]! as Map)['layout'] as Map)['text-field'],
|
||||
'{name:de}',
|
||||
);
|
||||
expect(
|
||||
jsonEncode(layers[2]),
|
||||
jsonEncode((liberty['layers']! as List)[2]),
|
||||
);
|
||||
expect(
|
||||
jsonEncode(layers[3]),
|
||||
jsonEncode((liberty['layers']! as List)[3]),
|
||||
);
|
||||
expect(jsonEncode(liberty), contains('name_en'), reason: 'unchanged');
|
||||
});
|
||||
|
||||
test('in English, as the style has it; read without a warning', () {
|
||||
expect(jsonEncode(labelledIn(liberty, 'en')), jsonEncode(liberty));
|
||||
final warnings = <String>[];
|
||||
ThemeReader(
|
||||
logger: _Warnings(warnings),
|
||||
).read(labelledIn(liberty, 'de'));
|
||||
expect(warnings, isEmpty);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class _Warnings(final List<String> _warnings) implements Logger {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
import 'package:flutter/painting.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:halkyon_train_flutter/src/feature/map/data/track_camera.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
|
||||
/// Where a workout's track is fitted on its page (ADR 0030, D63): into the
|
||||
/// map left free above the sheet and below the buttons, and how the camera
|
||||
/// moves there.
|
||||
void main() {
|
||||
group('the room the track is fitted into', () {
|
||||
test('clear of the buttons on top and of the sheet below, a margin '
|
||||
'around', () {
|
||||
final padding = trackFitPadding(
|
||||
view: const Size(400, 800),
|
||||
top: 24,
|
||||
covered: 440,
|
||||
);
|
||||
expect(padding.top, 24 + 56 + 32);
|
||||
expect(padding.bottom, 440 + 32);
|
||||
expect(padding.left, 32);
|
||||
expect(padding.right, 32);
|
||||
});
|
||||
|
||||
test(
|
||||
'the sheet pulled down: the map is larger, the track fitted lower',
|
||||
() {
|
||||
final up = trackFitPadding(
|
||||
view: const Size(400, 800),
|
||||
top: 24,
|
||||
covered: 440,
|
||||
);
|
||||
final down = trackFitPadding(
|
||||
view: const Size(400, 800),
|
||||
top: 24,
|
||||
covered: 120,
|
||||
);
|
||||
expect(down.bottom, lessThan(up.bottom));
|
||||
expect(down.top, up.top);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'a sheet covering nearly all of it still leaves the track a strip',
|
||||
() {
|
||||
final padding = trackFitPadding(
|
||||
view: const Size(400, 800),
|
||||
top: 24,
|
||||
covered: 780,
|
||||
);
|
||||
expect(
|
||||
800 - padding.top - padding.bottom,
|
||||
greaterThanOrEqualTo(minimumTrackRoom),
|
||||
);
|
||||
expect(padding.bottom, greaterThanOrEqualTo(0));
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('the camera on its way', () {
|
||||
const from = (center: LatLng(47.5, 9.6), zoom: 12.0);
|
||||
const to = (center: LatLng(47.6, 9.8), zoom: 14.0);
|
||||
|
||||
test('starts where it was and ends where it goes', () {
|
||||
expect(cameraBetween(from, to, 0), from);
|
||||
expect(cameraBetween(from, to, 1), to);
|
||||
});
|
||||
|
||||
test('halfway, halfway in place and zoom', () {
|
||||
final half = cameraBetween(from, to, 0.5);
|
||||
expect(half.center.latitude, closeTo(47.55, 1e-9));
|
||||
expect(half.center.longitude, closeTo(9.7, 1e-9));
|
||||
expect(half.zoom, 13);
|
||||
});
|
||||
});
|
||||
|
||||
group('the sheet', () {
|
||||
test('folds the round button into the bar over its last stretch', () {
|
||||
expect(barShown(0.55), 0);
|
||||
expect(barShown(trackBarFrom), 0);
|
||||
expect(barShown(1), 1);
|
||||
expect(barShown((trackBarFrom + 1) / 2), closeTo(0.5, 1e-9));
|
||||
});
|
||||
});
|
||||
}
|
||||
Loading…
Reference in a new issue