mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-11 15:33:58 +01:00
Make exercise tile and settings sheet text-free
Mode chip shows the mode glyph, paused row a pause glyph + 11.10 date + cross. Target change reads x13 / = instead of 13 reps / same. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
1f8f602564
commit
661b293bc2
@ -200,23 +200,10 @@ DateTime? parsePauseDate(Object? raw) {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
const _months = [
|
||||
'Jan',
|
||||
'Feb',
|
||||
'Mar',
|
||||
'Apr',
|
||||
'May',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Aug',
|
||||
'Sep',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Dec',
|
||||
];
|
||||
|
||||
/// `11 Oct`: how a pause's end date is shown.
|
||||
String formatShortDate(DateTime d) => '${d.day} ${_months[d.month - 1]}';
|
||||
/// `11.10`: how a pause's end date is shown (textgate: numerals, no month
|
||||
/// word; was `11 Oct`).
|
||||
String formatShortDate(DateTime d) =>
|
||||
'${d.day}.${d.month.toString().padLeft(2, '0')}';
|
||||
|
||||
/// `3:00`: how a rest length is shown.
|
||||
String formatRest(int secs) =>
|
||||
|
||||
@ -125,15 +125,19 @@ ProgressionTarget targetAfterFailure(ProgressionInputs s) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Short label for moving from [from] to [to]: `25 kg`, `13 reps`,
|
||||
/// `25 kg × 6`, or `same` when the rule has nowhere left to go.
|
||||
/// What [describeTargetChange] returns when the rule has nowhere to go.
|
||||
const kNoTargetChange = '=';
|
||||
|
||||
/// Short label for moving from [from] to [to]: `25 kg`, `×13` (reps),
|
||||
/// `25 kg × 6`, or [kNoTargetChange]. textgate: numerals and symbols only
|
||||
/// (was `13 reps` / `same`).
|
||||
String describeTargetChange(ProgressionInputs from, ProgressionTarget to) {
|
||||
final weightChanged = to.weight != from.weight;
|
||||
final repsChanged = to.reps != from.reps;
|
||||
if (weightChanged && repsChanged) return '${_kg(to.weight)} kg × ${to.reps}';
|
||||
if (weightChanged) return '${_kg(to.weight)} kg';
|
||||
if (repsChanged) return '${to.reps} reps';
|
||||
return 'same';
|
||||
if (repsChanged) return '×${to.reps}';
|
||||
return kNoTargetChange;
|
||||
}
|
||||
|
||||
/// `25` for 25.0, `27.5` for 27.5 — no trailing `.0` noise in tight rows.
|
||||
|
||||
@ -18,7 +18,7 @@ class _DeloadRow extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final change = describeTargetChange(state, targetAfterFailure(state));
|
||||
final canDeload = change != 'same';
|
||||
final canDeload = change != kNoTargetChange;
|
||||
return SizedBox(
|
||||
height: 40,
|
||||
child: Row(
|
||||
|
||||
@ -207,16 +207,17 @@ class _PauseRow extends StatelessWidget {
|
||||
),
|
||||
]
|
||||
: [
|
||||
// textgate: was "Paused — back on 11 Oct".
|
||||
Icon(
|
||||
Icons.pause_circle_outline,
|
||||
size: 18,
|
||||
color: colorScheme.error,
|
||||
semanticLabel: 'Paused',
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Paused — back on ${formatShortDate(until)}',
|
||||
style: style,
|
||||
),
|
||||
Icon(Icons.arrow_forward, size: 16, color: colorScheme.error),
|
||||
const SizedBox(width: 4),
|
||||
Text(formatShortDate(until), style: style),
|
||||
const Spacer(),
|
||||
TextButton(
|
||||
onPressed: onResume,
|
||||
|
||||
@ -20,10 +20,12 @@ class _ModeChip extends StatelessWidget {
|
||||
|
||||
static const double width = 64;
|
||||
|
||||
String get _text => switch (state.mode) {
|
||||
ProgressionMode.weight => 'kg',
|
||||
ProgressionMode.reps => 'reps',
|
||||
ProgressionMode.doubleProgression => '${state.repsLow}→${state.repsHigh}',
|
||||
// textgate: was 'kg' / 'reps'. The same mode glyphs as the settings
|
||||
// sheet's segmented control; double progression keeps its rep range.
|
||||
IconData? get _modeIcon => switch (state.mode) {
|
||||
ProgressionMode.weight => Icons.fitness_center,
|
||||
ProgressionMode.reps => Icons.repeat,
|
||||
ProgressionMode.doubleProgression => null,
|
||||
};
|
||||
|
||||
@override
|
||||
@ -52,12 +54,17 @@ class _ModeChip extends StatelessWidget {
|
||||
Icon(Icons.tune, size: 13, color: color),
|
||||
const SizedBox(width: 3),
|
||||
Flexible(
|
||||
child: Text(
|
||||
_text,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(color: color, fontSize: AppTextSize.caption),
|
||||
),
|
||||
child: _modeIcon == null
|
||||
? Text(
|
||||
'${state.repsLow}→${state.repsHigh}',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: color,
|
||||
fontSize: AppTextSize.caption,
|
||||
),
|
||||
)
|
||||
: Icon(_modeIcon, size: 14, color: color),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -204,16 +211,24 @@ class _PausedRow extends StatelessWidget {
|
||||
height: ExerciseTile.setRowHeight,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.pause_circle_outline, size: 20, color: color),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Paused — back on ${formatShortDate(until)} · counts as failed',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(color: color, fontSize: AppTextSize.label),
|
||||
),
|
||||
// textgate: was "Paused — back on 11 Oct · counts as failed".
|
||||
// Now: pause glyph, the return date, and the failed-set cross.
|
||||
Icon(
|
||||
Icons.pause_circle_outline,
|
||||
size: 20,
|
||||
color: color,
|
||||
semanticLabel: 'Paused — counts as failed',
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Icon(Icons.arrow_forward, size: 16, color: color),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
formatShortDate(until),
|
||||
key: const Key('paused_until'),
|
||||
style: TextStyle(color: color, fontSize: AppTextSize.label),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Icon(Icons.close, size: 16, color: color),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@ -62,9 +62,9 @@ void main() {
|
||||
});
|
||||
|
||||
test('formatShortDate', () {
|
||||
expect(formatShortDate(DateTime(2026, 10, 11)), '11 Oct');
|
||||
expect(formatShortDate(DateTime(2026, 1, 1)), '1 Jan');
|
||||
expect(formatShortDate(DateTime(2026, 12, 31)), '31 Dec');
|
||||
expect(formatShortDate(DateTime(2026, 10, 11)), '11.10');
|
||||
expect(formatShortDate(DateTime(2026, 1, 1)), '1.01');
|
||||
expect(formatShortDate(DateTime(2026, 12, 31)), '31.12');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -153,16 +153,13 @@ void main() {
|
||||
});
|
||||
|
||||
test('reps only', () {
|
||||
expect(
|
||||
describeTargetChange(from, const ProgressionTarget(20, 9)),
|
||||
'9 reps',
|
||||
);
|
||||
expect(describeTargetChange(from, const ProgressionTarget(20, 9)), '×9');
|
||||
});
|
||||
|
||||
test('no change', () {
|
||||
expect(
|
||||
describeTargetChange(from, const ProgressionTarget(20, 8)),
|
||||
'same',
|
||||
kNoTargetChange,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@ import '../fake_audio_platform.dart';
|
||||
import '../fake_break_service.dart';
|
||||
import '../fake_secure_storage.dart';
|
||||
import '_workout_screen_test_fixtures.dart';
|
||||
import '../icon_finders.dart';
|
||||
|
||||
// Real plan names, so each has a progression-state row to pause.
|
||||
const _lunge = Exercise(name: 'Dumbbell Lunge', sets: 2, reps: 5, weight: 10);
|
||||
@ -63,7 +64,7 @@ void main() {
|
||||
testWidgets('the paused exercise shows no sets and says so', (tester) async {
|
||||
await pumpPausedSitup(tester);
|
||||
expect(find.byType(RepCircle), findsNWidgets(_lunge.sets));
|
||||
expect(find.textContaining('Paused — back on'), findsOneWidget);
|
||||
expect(iconLabeled('Paused — counts as failed'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Finish unlocks without it, and it is recorded as failed', (
|
||||
|
||||
@ -155,7 +155,7 @@ void main() {
|
||||
edits.last.pausedUntil,
|
||||
pauseEnd(DateTime.now(), kDefaultPauseDays + 1),
|
||||
);
|
||||
expect(find.textContaining('Paused — back on'), findsOneWidget);
|
||||
expect(iconLabeled('Paused'), findsOneWidget);
|
||||
|
||||
await tester.tap(iconLabeled('Resume'));
|
||||
await tester.pump();
|
||||
|
||||
@ -134,17 +134,17 @@ void main() {
|
||||
await tester.pumpWidget(
|
||||
_wrap(_tile(state: _state.copyWith(mode: ProgressionMode.reps))),
|
||||
);
|
||||
expect(find.text('0/3 → 6 reps'), findsOneWidget);
|
||||
expect(find.text('0/2 → 4 reps'), findsOneWidget);
|
||||
expect(find.text('0/3 → ×6'), findsOneWidget);
|
||||
expect(find.text('0/2 → ×4'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('mode chip names the mode', (tester) async {
|
||||
testWidgets('mode chip shows the mode glyph', (tester) async {
|
||||
await tester.pumpWidget(_wrap(_tile()));
|
||||
expect(find.text('kg'), findsOneWidget);
|
||||
expect(find.byIcon(Icons.fitness_center), findsOneWidget);
|
||||
await tester.pumpWidget(
|
||||
_wrap(_tile(state: _state.copyWith(mode: ProgressionMode.reps))),
|
||||
);
|
||||
expect(find.text('reps'), findsOneWidget);
|
||||
expect(find.byIcon(Icons.repeat), findsOneWidget);
|
||||
await tester.pumpWidget(
|
||||
_wrap(
|
||||
_tile(
|
||||
@ -184,10 +184,8 @@ void main() {
|
||||
);
|
||||
expect(find.byType(RepCircle), findsNothing);
|
||||
expect(find.bySemanticsLabel('Squat warmup'), findsNothing);
|
||||
expect(
|
||||
find.textContaining('back on ${formatShortDate(until)}'),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.text(formatShortDate(until)), findsOneWidget);
|
||||
expect(iconLabeled('Paused — counts as failed'), findsOneWidget);
|
||||
expect(tester.getSize(find.byType(ExerciseTile)), size);
|
||||
expect(_cardColor(tester), idle);
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user