feat(train): interval plans, synced and sealed, with an editor and templates (ADR 0030, D42)
Train's server gains the synced `plan` table, a sealed payload alone; the migration `plans` only adds it, its space index and the foreign key to its sync space, and a person's deletion takes it with their space. The app keeps plans as `plan` rows sealed for their row: a name, what they are for, whether a step off target is told, and blocks of steps repeated, each ending after a time, a distance or a lap tap and aiming at a pace, speed, heart rate or zone, or the rower's split, watts or stroke rate. Home's Plans tab lists them, a new one starts from nothing or from a template (4×4 minutes, Tabata, 10×400 m), the editor changes steps on a sheet, and a deletion asks first and can be undone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
fa7680821d
commit
f406ddcc59
1 changed files with 23608 additions and 10 deletions
|
|
@ -1468,6 +1468,426 @@ abstract class AppLocalizations {
|
|||
/// **'Days'**
|
||||
String get homeDays;
|
||||
|
||||
/// No description provided for @homePlans.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Plans'**
|
||||
String get homePlans;
|
||||
|
||||
/// No description provided for @plansNew.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'New plan'**
|
||||
String get plansNew;
|
||||
|
||||
/// No description provided for @plansNewEmpty.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'From nothing'**
|
||||
String get plansNewEmpty;
|
||||
|
||||
/// No description provided for @plansEmptyTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No plans yet'**
|
||||
String get plansEmptyTitle;
|
||||
|
||||
/// No description provided for @plansEmptyHint.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'A plan\'s steps lead you through intervals as you record, with a tone, a vibration and a voice at each change. Start from a template, or from nothing.'**
|
||||
String get plansEmptyHint;
|
||||
|
||||
/// No description provided for @planLocked.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'A plan that doesn\'t open here'**
|
||||
String get planLocked;
|
||||
|
||||
/// No description provided for @planTemplateFourByFour.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'4×4 minutes'**
|
||||
String get planTemplateFourByFour;
|
||||
|
||||
/// No description provided for @planTemplateTabata.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Tabata'**
|
||||
String get planTemplateTabata;
|
||||
|
||||
/// No description provided for @planTemplateFourHundreds.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'10×400 m'**
|
||||
String get planTemplateFourHundreds;
|
||||
|
||||
/// No description provided for @planTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Plan'**
|
||||
String get planTitle;
|
||||
|
||||
/// No description provided for @planNewTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'New plan'**
|
||||
String get planNewTitle;
|
||||
|
||||
/// No description provided for @planName.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Name'**
|
||||
String get planName;
|
||||
|
||||
/// No description provided for @planActivity.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'For'**
|
||||
String get planActivity;
|
||||
|
||||
/// No description provided for @planActivityAny.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Any activity'**
|
||||
String get planActivityAny;
|
||||
|
||||
/// No description provided for @planOffTarget.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Tell me when I\'m off target'**
|
||||
String get planOffTarget;
|
||||
|
||||
/// No description provided for @planOffTargetHint.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'A short cue at most every 30 seconds.'**
|
||||
String get planOffTargetHint;
|
||||
|
||||
/// No description provided for @planSave.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Save'**
|
||||
String get planSave;
|
||||
|
||||
/// No description provided for @planNotSaved.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'The plan wasn\'t saved.'**
|
||||
String get planNotSaved;
|
||||
|
||||
/// No description provided for @planAddStep.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Add a step'**
|
||||
String get planAddStep;
|
||||
|
||||
/// No description provided for @planAddRepeat.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Add a repeat'**
|
||||
String get planAddRepeat;
|
||||
|
||||
/// No description provided for @planRepeat.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{count}×'**
|
||||
String planRepeat(int count);
|
||||
|
||||
/// No description provided for @planFewer.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Fewer rounds'**
|
||||
String get planFewer;
|
||||
|
||||
/// No description provided for @planMore.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'More rounds'**
|
||||
String get planMore;
|
||||
|
||||
/// No description provided for @planBlockMenu.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'More'**
|
||||
String get planBlockMenu;
|
||||
|
||||
/// No description provided for @planMoveUp.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Move up'**
|
||||
String get planMoveUp;
|
||||
|
||||
/// No description provided for @planMoveDown.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Move down'**
|
||||
String get planMoveDown;
|
||||
|
||||
/// No description provided for @planRemove.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Remove'**
|
||||
String get planRemove;
|
||||
|
||||
/// No description provided for @planDelete.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Delete'**
|
||||
String get planDelete;
|
||||
|
||||
/// No description provided for @planDeleteTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Delete this plan?'**
|
||||
String get planDeleteTitle;
|
||||
|
||||
/// No description provided for @planDeleteMessage.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'It\'s deleted on all your devices; workouts recorded with it keep their steps.'**
|
||||
String get planDeleteMessage;
|
||||
|
||||
/// No description provided for @planDeleted.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Plan deleted'**
|
||||
String get planDeleted;
|
||||
|
||||
/// No description provided for @planSteps.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{count, plural, =1{1 step} other{{count} steps}}'**
|
||||
String planSteps(int count);
|
||||
|
||||
/// No description provided for @stepTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Step'**
|
||||
String get stepTitle;
|
||||
|
||||
/// No description provided for @stepKind.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Kind'**
|
||||
String get stepKind;
|
||||
|
||||
/// No description provided for @stepWarmUp.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Warm-up'**
|
||||
String get stepWarmUp;
|
||||
|
||||
/// No description provided for @stepWork.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Work'**
|
||||
String get stepWork;
|
||||
|
||||
/// No description provided for @stepRecovery.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Recovery'**
|
||||
String get stepRecovery;
|
||||
|
||||
/// No description provided for @stepCoolDown.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Cool-down'**
|
||||
String get stepCoolDown;
|
||||
|
||||
/// No description provided for @stepEnds.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Ends'**
|
||||
String get stepEnds;
|
||||
|
||||
/// No description provided for @stepEndTime.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'After a time'**
|
||||
String get stepEndTime;
|
||||
|
||||
/// No description provided for @stepEndDistance.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'After a distance'**
|
||||
String get stepEndDistance;
|
||||
|
||||
/// No description provided for @stepEndLap.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'With a lap tap'**
|
||||
String get stepEndLap;
|
||||
|
||||
/// No description provided for @stepTimeLabel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Time (min:s)'**
|
||||
String get stepTimeLabel;
|
||||
|
||||
/// No description provided for @stepMetresLabel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Metres'**
|
||||
String get stepMetresLabel;
|
||||
|
||||
/// No description provided for @stepTarget.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Target'**
|
||||
String get stepTarget;
|
||||
|
||||
/// No description provided for @stepZone.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Zone'**
|
||||
String get stepZone;
|
||||
|
||||
/// No description provided for @stepDone.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Done'**
|
||||
String get stepDone;
|
||||
|
||||
/// No description provided for @stepMinutes.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{minutes} min'**
|
||||
String stepMinutes(int minutes);
|
||||
|
||||
/// No description provided for @stepSeconds.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{seconds} s'**
|
||||
String stepSeconds(int seconds);
|
||||
|
||||
/// No description provided for @stepMinutesSeconds.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{minutes} min {seconds} s'**
|
||||
String stepMinutesSeconds(int minutes, int seconds);
|
||||
|
||||
/// No description provided for @stepMetres.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{metres} m'**
|
||||
String stepMetres(int metres);
|
||||
|
||||
/// No description provided for @stepKilometres.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{km} km'**
|
||||
String stepKilometres(String km);
|
||||
|
||||
/// No description provided for @stepUntilLap.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'until a lap tap'**
|
||||
String get stepUntilLap;
|
||||
|
||||
/// No description provided for @targetNone.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'None'**
|
||||
String get targetNone;
|
||||
|
||||
/// No description provided for @targetFrom.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'From'**
|
||||
String get targetFrom;
|
||||
|
||||
/// No description provided for @targetTo.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'To'**
|
||||
String get targetTo;
|
||||
|
||||
/// No description provided for @targetKindPace.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Pace (per km)'**
|
||||
String get targetKindPace;
|
||||
|
||||
/// No description provided for @targetKindSpeed.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Speed (km/h)'**
|
||||
String get targetKindSpeed;
|
||||
|
||||
/// No description provided for @targetKindHeartRate.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Heart rate (bpm)'**
|
||||
String get targetKindHeartRate;
|
||||
|
||||
/// No description provided for @targetKindHeartZone.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Heart-rate zone'**
|
||||
String get targetKindHeartZone;
|
||||
|
||||
/// No description provided for @targetKindSplit.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Split (per 500 m)'**
|
||||
String get targetKindSplit;
|
||||
|
||||
/// No description provided for @targetKindWatts.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Power (W)'**
|
||||
String get targetKindWatts;
|
||||
|
||||
/// No description provided for @targetKindStrokeRate.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Stroke rate (per min)'**
|
||||
String get targetKindStrokeRate;
|
||||
|
||||
/// No description provided for @targetPace.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{from}–{to} per km'**
|
||||
String targetPace(String from, String to);
|
||||
|
||||
/// No description provided for @targetSpeed.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{from}–{to} km/h'**
|
||||
String targetSpeed(String from, String to);
|
||||
|
||||
/// No description provided for @targetHeartRate.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{from}–{to} bpm'**
|
||||
String targetHeartRate(String from, String to);
|
||||
|
||||
/// No description provided for @targetZone.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Zone {zone}'**
|
||||
String targetZone(int zone);
|
||||
|
||||
/// No description provided for @targetSplit.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{from}–{to} per 500 m'**
|
||||
String targetSplit(String from, String to);
|
||||
|
||||
/// No description provided for @targetWatts.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{from}–{to} W'**
|
||||
String targetWatts(String from, String to);
|
||||
|
||||
/// No description provided for @targetStrokeRate.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{from}–{to} strokes/min'**
|
||||
String targetStrokeRate(String from, String to);
|
||||
|
||||
/// No description provided for @daysEmptyTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
Loading…
Reference in a new issue