From 5fa160f114dca72b9e9c8d4d004c0a953208aa41 Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Wed, 11 Nov 2026 01:27:59 +0100 Subject: [PATCH] Make history screen text-free Section headings and the all-workouts entry are glyphs, tiles drop their word prefixes, chart and calendar dates are numeric, weekday headers are dots. Co-Authored-By: Claude Opus 5.5 --- .../lib/screens/history_screen.dart | 21 +++--- .../lib/screens/history_screen_charts.dart | 66 +++++++++++++------ .../lib/screens/history_screen_painter.dart | 19 +----- .../lib/screens/history_screen_planned.dart | 6 +- .../lib/screens/history_screen_tiles.dart | 54 ++++++++++++--- .../lib/widgets/calendar_widget.dart | 60 +++++++++-------- .../test/screens/history_chart_test.dart | 28 ++++---- .../test/screens/history_planned_test.dart | 34 ++++++---- .../test/screens/history_runs_test.dart | 13 ++-- .../test/screens/history_screen_test.dart | 6 +- .../screens/history_streak_motion_test.dart | 10 +-- .../test/widgets/calendar_widget_test.dart | 12 ++-- 12 files changed, 200 insertions(+), 129 deletions(-) diff --git a/stronglift_replacement/workout_app/lib/screens/history_screen.dart b/stronglift_replacement/workout_app/lib/screens/history_screen.dart index f702d6f..9f17b47 100644 --- a/stronglift_replacement/workout_app/lib/screens/history_screen.dart +++ b/stronglift_replacement/workout_app/lib/screens/history_screen.dart @@ -173,12 +173,7 @@ class _HistoryScreenState extends State { _syncedRows.isEmpty && _plannedRows.isEmpty && _runs.isEmpty - ? Center( - child: Text( - 'No workouts yet.', - style: TextStyle(color: colorScheme.onSurfaceVariant), - ), - ) + ? const _EmptyHistory() : ListView( padding: const EdgeInsets.all(12), children: [ @@ -210,18 +205,21 @@ class _HistoryScreenState extends State { _RunsStrip(runs: _runs, onOpen: _openRun), const SizedBox(height: 16), ], - const _SectionLabel('TOTAL VOLUME (2-session rolling avg, kg)'), + const _SectionLabel( + Icons.stacked_line_chart, + 'TOTAL VOLUME (2-session rolling avg, kg)', + ), const SizedBox(height: 6), _WeightChart(points: _rollingAvg2(_totalVolumePoints(_rows))), const SizedBox(height: 16), _calendar(_allWorkoutDates(_rows, _syncedRows, _runs)), const SizedBox(height: 16), - const _SectionLabel('ALL SESSIONS'), + const _SectionLabel(Icons.list, 'ALL SESSIONS'), const SizedBox(height: 8), ..._rows.map((row) => _AllSessionTile(row: row)), if (_syncedRows.isNotEmpty) ...[ const SizedBox(height: 16), - const _SectionLabel('SYNCED FROM PC'), + const _SectionLabel(Icons.computer, 'SYNCED FROM PC'), const SizedBox(height: 8), ..._syncedRows.map((row) => _SyncedWorkoutTile(payload: row)), ], @@ -232,13 +230,14 @@ class _HistoryScreenState extends State { _ProgressStatsCard(state: _selectedState!), const SizedBox(height: 12), ], - const _SectionLabel('WEIGHT OVER TIME'), + const _SectionLabel(Icons.show_chart, 'WEIGHT OVER TIME'), const SizedBox(height: 6), _WeightChart(points: _exerciseWeightPoints(_rows, name)), const SizedBox(height: 16), _calendar(_exerciseDates(_rows, name)), const SizedBox(height: 16), - _SectionLabel(name.toUpperCase()), + // The picker above already names the exercise: a list glyph here. + _SectionLabel(Icons.list, name), const SizedBox(height: 8), ..._sessionsForExercise( _rows, diff --git a/stronglift_replacement/workout_app/lib/screens/history_screen_charts.dart b/stronglift_replacement/workout_app/lib/screens/history_screen_charts.dart index a02356f..c2a1760 100644 --- a/stronglift_replacement/workout_app/lib/screens/history_screen_charts.dart +++ b/stronglift_replacement/workout_app/lib/screens/history_screen_charts.dart @@ -7,24 +7,44 @@ part of 'history_screen.dart'; // ── Shared sub-widgets ────────────────────────────────────────────────────── +/// A section heading as a glyph (textgate: was an all-caps word heading; +/// the words survive as [spoken], the glyph's semantic label). class _SectionLabel extends StatelessWidget { - const _SectionLabel(this.text); + const _SectionLabel(this.icon, this.spoken); - final String text; + final IconData icon; + final String spoken; @override Widget build(BuildContext context) { - return Text( - text, - style: TextStyle( + return Align( + alignment: Alignment.centerLeft, + child: Icon( + icon, + size: 18, color: Theme.of(context).colorScheme.onSurfaceVariant, - fontSize: AppTextSize.caption, - letterSpacing: 1.3, + semanticLabel: spoken, ), ); } } +/// The empty history: a large muted history glyph (textgate: was "No +/// workouts yet."). +class _EmptyHistory extends StatelessWidget { + const _EmptyHistory(); + + @override + Widget build(BuildContext context) => Center( + child: Icon( + Icons.history, + size: 48, + color: Theme.of(context).colorScheme.outlineVariant, + semanticLabel: 'No workouts yet.', + ), + ); +} + class _ExercisePicker extends StatelessWidget { const _ExercisePicker({ required this.names, @@ -49,15 +69,18 @@ class _ExercisePicker extends StatelessWidget { .map( (n) => DropdownMenuItem( value: n, - child: Text( - n, - style: TextStyle( - color: n == _kTotal - ? colorScheme.onSurfaceVariant - : colorScheme.onSurface, - fontStyle: n == _kTotal ? FontStyle.italic : FontStyle.normal, - ), - ), + // textgate: the all-workouts entry is a sum glyph (was + // "Total (all workouts)"); exercise names are content. + child: n == _kTotal + ? Align( + alignment: Alignment.centerLeft, + child: Icon( + Icons.functions, + color: colorScheme.onSurfaceVariant, + semanticLabel: _kTotal, + ), + ) + : Text(n, style: TextStyle(color: colorScheme.onSurface)), ), ) .toList(), @@ -162,7 +185,8 @@ class _StreakRow extends StatelessWidget { const SizedBox(width: 8), Expanded( child: Text( - '${threshold - shown} more to $direction $actionLabel', + // textgate: was "2 more to ↑ 25 kg". + '${threshold - shown} $direction $actionLabel', style: TextStyle( color: colorScheme.onSurfaceVariant, fontSize: AppTextSize.caption, @@ -187,9 +211,11 @@ class _WeightChart extends StatelessWidget { return Container( height: 80, alignment: Alignment.center, - child: Text( - 'Not enough data for chart', - style: TextStyle(color: colorScheme.onSurfaceVariant), + // textgate: was "Not enough data for chart". + child: Icon( + Icons.show_chart, + color: colorScheme.outlineVariant, + semanticLabel: 'Not enough data for chart', ), ); } diff --git a/stronglift_replacement/workout_app/lib/screens/history_screen_painter.dart b/stronglift_replacement/workout_app/lib/screens/history_screen_painter.dart index 6ee5bed..e7cb6d6 100644 --- a/stronglift_replacement/workout_app/lib/screens/history_screen_painter.dart +++ b/stronglift_replacement/workout_app/lib/screens/history_screen_painter.dart @@ -5,7 +5,6 @@ // these are `part` files. part of 'history_screen.dart'; - class _ChartPainter extends CustomPainter { _ChartPainter( this.points, { @@ -25,22 +24,8 @@ class _ChartPainter extends CustomPainter { static const _bottomPad = 22.0; // room for X-axis dates static const _hPad = 8.0; - static const _months = [ - 'Jan', - 'Feb', - 'Mar', - 'Apr', - 'May', - 'Jun', - 'Jul', - 'Aug', - 'Sep', - 'Oct', - 'Nov', - 'Dec', - ]; - - static String _shortDate(DateTime d) => '${_months[d.month - 1]} ${d.day}'; + // textgate: numeric day.month on the axis (was "Aug 15"). + static String _shortDate(DateTime d) => formatShortDate(d); @override void paint(Canvas canvas, Size size) { diff --git a/stronglift_replacement/workout_app/lib/screens/history_screen_planned.dart b/stronglift_replacement/workout_app/lib/screens/history_screen_planned.dart index 7294f0c..e7bed0f 100644 --- a/stronglift_replacement/workout_app/lib/screens/history_screen_planned.dart +++ b/stronglift_replacement/workout_app/lib/screens/history_screen_planned.dart @@ -17,7 +17,7 @@ extension _HistoryPlanned on _HistoryScreenState { /// when there are none. List _plannedSection() => [ if (_plannedRows.isNotEmpty) ...[ - const _SectionLabel('PLANNED (not done yet)'), + const _SectionLabel(Icons.event, 'PLANNED (not done yet)'), const SizedBox(height: 8), ..._plannedRows.map( (row) => _PlannedWorkoutTile( @@ -153,7 +153,9 @@ class _PlannedWorkoutTile extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '${payload['date']} $time · Planned', + // textgate: was "