feat(train): strength plans and sessions (ADR 0035)

Strength plans of exercises from a catalogue or the person's own, in
blocks of one exercise or several done in turn (supersets, circuits),
each with its sets, reps or seconds, weights and rests; each a sealed
`strength_plan` row. Plans are marked with what the place chosen lacks,
and a session offers swaps moving the same way that the place allows.

A session runs on every device, set by set with the rest counted down,
each weight made up from the place's plates, bars and fixed weights with
the plate changes between sets, kept in strength_session.json after each
change so one cut off is carried on, and saved as a workout of the new
activity Strength with its sets, listed on its page; strength training
in Health Connect and FIT, no GPX.

Migration 20261102113104496-strength-plans is purely additive: it
creates the table strength_plan (id uuid v7, spaceId, keyId, the sealed
payload and its nonce), its index on spaceId, and a foreign key to
offline_sync_spaces with ON DELETE CASCADE, so deleting a person's sync
spaces deletes their plans; then the modules' version rows. The
gen_random_uuid_v7 function is created or replaced as in every
migration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-11-02 13:18:12 +01:00
commit 508994be3a
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
69 changed files with 27612 additions and 25 deletions

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,94 @@
{
"__className__": "serverpod.DatabaseMigration",
"actions": [
{
"__className__": "serverpod.DatabaseMigrationAction",
"type": "createTable",
"createTable": {
"__className__": "serverpod.TableDefinition",
"name": "strength_plan",
"dartName": "StrengthPlan",
"module": "halkyon_train",
"schema": "public",
"columns": [
{
"__className__": "serverpod.ColumnDefinition",
"name": "id",
"fieldName": "id",
"columnType": 7,
"isNullable": false,
"columnDefault": "random_v7",
"dartType": "UuidValue?"
},
{
"__className__": "serverpod.ColumnDefinition",
"name": "spaceId",
"fieldName": "spaceId",
"columnType": 6,
"isNullable": true,
"dartType": "int?"
},
{
"__className__": "serverpod.ColumnDefinition",
"name": "keyId",
"fieldName": "keyId",
"columnType": 0,
"isNullable": true,
"dartType": "String?"
},
{
"__className__": "serverpod.ColumnDefinition",
"name": "payload",
"fieldName": "payload",
"columnType": 5,
"isNullable": false,
"dartType": "dart:typed_data:ByteData"
},
{
"__className__": "serverpod.ColumnDefinition",
"name": "nonce",
"fieldName": "nonce",
"columnType": 5,
"isNullable": true,
"dartType": "dart:typed_data:ByteData?"
}
],
"foreignKeys": [
{
"__className__": "serverpod.ForeignKeyDefinition",
"constraintName": "strength_plan_fk_0",
"columns": [
"spaceId"
],
"referenceTable": "offline_sync_spaces",
"referenceTableSchema": "public",
"referenceColumns": [
"id"
],
"onUpdate": 3,
"onDelete": 4
}
],
"indexes": [
{
"__className__": "serverpod.IndexDefinition",
"indexName": "strength_plan_space_idx",
"elements": [
{
"__className__": "serverpod.IndexElementDefinition",
"type": 0,
"definition": "spaceId"
}
],
"type": "btree",
"isUnique": false,
"isPrimary": false
}
],
"managed": true
}
}
],
"warnings": [],
"migrationApiVersion": 1
}

View file

@ -10,6 +10,7 @@ part '20261028181405893-days/migration.dart';
part '20261028211508606-plans/migration.dart';
part '20261028231042668-routes/migration.dart';
part '20261102103211357-training-places/migration.dart';
part '20261102113104496-strength-plans/migration.dart';
/// Migration registry for the client-side database.
class MigrationRegistry {
@ -22,6 +23,7 @@ class MigrationRegistry {
_Migration20261028211508606_plans(),
_Migration20261028231042668_routes(),
_Migration20261102103211357_training_places(),
_Migration20261102113104496_strength_plans(),
];
/// List of all client-side database migration versions.

View file

@ -44,6 +44,7 @@ import 'routing/route_refused.dart' as _ix4aepwk;
import 'routing/route_stretch.dart' as _ic7f85bc;
import 'search/place.dart' as _iadh29yw;
import 'search/search_refused.dart' as _ihbxkwke;
import 'strength/strength_plan.dart' as _imwvu2mn;
import 'workout/workout.dart' as _i5b9b17z;
import 'workout/workout_series.dart' as _i92mefto;
export 'day/day.dart';
@ -65,6 +66,7 @@ export 'routing/route_refused.dart';
export 'routing/route_stretch.dart';
export 'search/place.dart';
export 'search/search_refused.dart';
export 'strength/strength_plan.dart';
export 'workout/workout.dart';
export 'workout/workout_series.dart';
export 'client.dart';
@ -571,6 +573,73 @@ class Protocol extends _isd.DatabaseSerializationManager {
],
managed: true,
),
_isd.TableDefinition(
name: 'strength_plan',
dartName: 'StrengthPlan',
schema: 'public',
module: 'halkyon_train',
columns: [
_isd.ColumnDefinition(
name: 'id',
columnType: _isd.ColumnType.uuid,
isNullable: false,
dartType: 'UuidValue?',
columnDefault: 'random_v7',
),
_isd.ColumnDefinition(
name: 'spaceId',
columnType: _isd.ColumnType.bigint,
isNullable: true,
dartType: 'int?',
),
_isd.ColumnDefinition(
name: 'keyId',
columnType: _isd.ColumnType.text,
isNullable: true,
dartType: 'String?',
),
_isd.ColumnDefinition(
name: 'payload',
columnType: _isd.ColumnType.bytea,
isNullable: false,
dartType: 'dart:typed_data:ByteData',
),
_isd.ColumnDefinition(
name: 'nonce',
columnType: _isd.ColumnType.bytea,
isNullable: true,
dartType: 'dart:typed_data:ByteData?',
),
],
foreignKeys: [
_isd.ForeignKeyDefinition(
constraintName: 'strength_plan_fk_0',
columns: ['spaceId'],
referenceTable: 'offline_sync_spaces',
referenceTableSchema: 'public',
referenceColumns: ['id'],
onUpdate: _isd.ForeignKeyAction.noAction,
onDelete: _isd.ForeignKeyAction.cascade,
matchType: null,
),
],
indexes: [
_isd.IndexDefinition(
indexName: 'strength_plan_space_idx',
tableSpace: null,
elements: [
_isd.IndexElementDefinition(
type: _isd.IndexElementDefinitionType.column,
definition: 'spaceId',
),
],
type: 'btree',
isUnique: false,
isPrimary: false,
),
],
managed: true,
),
_isd.TableDefinition(
name: 'training_place',
dartName: 'TrainingPlace',
@ -908,6 +977,9 @@ class Protocol extends _isd.DatabaseSerializationManager {
if (t == _ihbxkwke.SearchRefused) {
return _ihbxkwke.SearchRefused.fromJson(data) as T;
}
if (t == _imwvu2mn.StrengthPlan) {
return _imwvu2mn.StrengthPlan.fromJson(data) as T;
}
if (t == _i5b9b17z.Workout) {
return _i5b9b17z.Workout.fromJson(data) as T;
}
@ -976,6 +1048,9 @@ class Protocol extends _isd.DatabaseSerializationManager {
return (data != null ? _ihbxkwke.SearchRefused.fromJson(data) : null)
as T;
}
if (t == _isc.getType<_imwvu2mn.StrengthPlan?>()) {
return (data != null ? _imwvu2mn.StrengthPlan.fromJson(data) : null) as T;
}
if (t == _isc.getType<_i5b9b17z.Workout?>()) {
return (data != null ? _i5b9b17z.Workout.fromJson(data) : null) as T;
}
@ -1053,6 +1128,7 @@ class Protocol extends _isd.DatabaseSerializationManager {
_ic7f85bc.RouteStretch => 'RouteStretch',
_iadh29yw.Place => 'Place',
_ihbxkwke.SearchRefused => 'SearchRefused',
_imwvu2mn.StrengthPlan => 'StrengthPlan',
_i5b9b17z.Workout => 'Workout',
_i92mefto.WorkoutSeries => 'WorkoutSeries',
_ => null,
@ -1110,6 +1186,8 @@ class Protocol extends _isd.DatabaseSerializationManager {
return 'Place';
case _ihbxkwke.SearchRefused():
return 'SearchRefused';
case _imwvu2mn.StrengthPlan():
return 'StrengthPlan';
case _i5b9b17z.Workout():
return 'Workout';
case _i92mefto.WorkoutSeries():
@ -1207,6 +1285,9 @@ class Protocol extends _isd.DatabaseSerializationManager {
if (dataClassName == 'SearchRefused') {
return deserialize<_ihbxkwke.SearchRefused>(data['data']);
}
if (dataClassName == 'StrengthPlan') {
return deserialize<_imwvu2mn.StrengthPlan>(data['data']);
}
if (dataClassName == 'Workout') {
return deserialize<_i5b9b17z.Workout>(data['data']);
}
@ -1308,6 +1389,8 @@ class Protocol extends _isd.DatabaseSerializationManager {
return _ia5j8fch.Profile.t;
case _i0mgeh8t.SavedRoute:
return _i0mgeh8t.SavedRoute.t;
case _imwvu2mn.StrengthPlan:
return _imwvu2mn.StrengthPlan.t;
case _i5b9b17z.Workout:
return _i5b9b17z.Workout.t;
case _i92mefto.WorkoutSeries:

View file

@ -0,0 +1,670 @@
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
/* To generate run: "serverpod generate" */
// ignore_for_file: implementation_imports
// ignore_for_file: library_private_types_in_public_api
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: public_member_api_docs
// ignore_for_file: type_literal_in_constant_pattern
// ignore_for_file: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:typed_data' as _idt;
import 'package:serverpod_client/serverpod_client.dart' as _isc;
import 'package:serverpod_database/serverpod_database.dart' as _isd;
/// A strength plan (ADR 0035): its name and blocks of exercises with their
/// sets, repetitions, weights and rests, in its sealed payload (ADR 0030,
/// D7); the instance sees that it exists, its size and when it changed.
/// Changed when edited, the last writer winning. Synced.
abstract class StrengthPlan
implements _isd.TableRow<_isc.UuidValue?>, _isc.ProtocolSerialization {
StrengthPlan._({
this.id,
this.spaceId,
this.keyId,
required this.payload,
this.nonce,
});
factory StrengthPlan({
_isc.UuidValue? id,
int? spaceId,
String? keyId,
required _idt.ByteData payload,
_idt.ByteData? nonce,
}) = _StrengthPlanImpl;
factory StrengthPlan.fromJson(Map<String, dynamic> jsonSerialization) {
return StrengthPlan(
id: jsonSerialization['id'] == null
? null
: _isc.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
spaceId: jsonSerialization['spaceId'] as int?,
keyId: jsonSerialization['keyId'] as String?,
payload: _isc.ByteDataJsonExtension.fromJson(
jsonSerialization['payload'],
),
nonce: jsonSerialization['nonce'] == null
? null
: _isc.ByteDataJsonExtension.fromJson(jsonSerialization['nonce']),
);
}
static final t = StrengthPlanTable();
static const db = StrengthPlanRepository._();
@override
_isc.UuidValue? id;
/// The space owning this row. Maintained by the sync engine.
int? spaceId;
/// The key it is sealed with (ADR 0002's envelope); never null in Train.
String? keyId;
/// The content, sealed: UTF-8 JSON with a `schemaVersion` once opened.
_idt.ByteData payload;
/// The sealing's nonce (ADR 0002's envelope).
_idt.ByteData? nonce;
@override
_isd.Table<_isc.UuidValue?> get table => t;
/// Returns a shallow copy of this [StrengthPlan]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
StrengthPlan copyWith({
_isc.UuidValue? id,
int? spaceId,
String? keyId,
_idt.ByteData? payload,
_idt.ByteData? nonce,
});
@override
Map<String, dynamic> toJson() {
return {
'__className__': 'StrengthPlan',
if (id != null) 'id': id?.toJson(),
if (spaceId != null) 'spaceId': spaceId,
if (keyId != null) 'keyId': keyId,
'payload': payload.toJson(),
if (nonce != null) 'nonce': nonce?.toJson(),
};
}
@override
Map<String, dynamic> toJsonForProtocol() {
return {
'__className__': 'StrengthPlan',
if (id != null) 'id': id?.toJson(),
if (spaceId != null) 'spaceId': spaceId,
if (keyId != null) 'keyId': keyId,
'payload': payload.toJson(),
if (nonce != null) 'nonce': nonce?.toJson(),
};
}
static StrengthPlanInclude include() {
return StrengthPlanInclude._();
}
static StrengthPlanIncludeList includeList({
_isd.WhereExpressionBuilder<StrengthPlanTable>? where,
int? limit,
int? offset,
_isd.OrderByBuilder<StrengthPlanTable>? orderBy,
_isd.OrderByListBuilder<StrengthPlanTable>? orderByList,
StrengthPlanInclude? include,
}) {
return StrengthPlanIncludeList._(
where: where,
limit: limit,
offset: offset,
orderBy: orderBy?.call(StrengthPlan.t),
orderByList: orderByList?.call(StrengthPlan.t),
include: include,
);
}
@override
String toString() {
return _isc.SerializationManager.encode(this);
}
}
class _Undefined {}
class _StrengthPlanImpl extends StrengthPlan {
_StrengthPlanImpl({
_isc.UuidValue? id,
int? spaceId,
String? keyId,
required _idt.ByteData payload,
_idt.ByteData? nonce,
}) : super._(
id: id,
spaceId: spaceId,
keyId: keyId,
payload: payload,
nonce: nonce,
);
/// Returns a shallow copy of this [StrengthPlan]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
@override
StrengthPlan copyWith({
Object? id = _Undefined,
Object? spaceId = _Undefined,
Object? keyId = _Undefined,
_idt.ByteData? payload,
Object? nonce = _Undefined,
}) {
return StrengthPlan(
id: id is _isc.UuidValue? ? id : this.id,
spaceId: spaceId is int? ? spaceId : this.spaceId,
keyId: keyId is String? ? keyId : this.keyId,
payload: payload ?? this.payload.clone(),
nonce: nonce is _idt.ByteData? ? nonce : this.nonce?.clone(),
);
}
}
class StrengthPlanUpdateTable extends _isd.UpdateTable<StrengthPlanTable> {
StrengthPlanUpdateTable(super.table);
_isd.ColumnValue<int, int> spaceId(int? value) => _isd.ColumnValue(
table.spaceId,
value,
);
_isd.ColumnValue<String, String> keyId(String? value) => _isd.ColumnValue(
table.keyId,
value,
);
_isd.ColumnValue<_idt.ByteData, _idt.ByteData> payload(_idt.ByteData value) =>
_isd.ColumnValue(
table.payload,
value,
);
_isd.ColumnValue<_idt.ByteData, _idt.ByteData> nonce(_idt.ByteData? value) =>
_isd.ColumnValue(
table.nonce,
value,
);
}
class StrengthPlanTable extends _isd.Table<_isc.UuidValue?> {
StrengthPlanTable({super.tableRelation}) : super(tableName: 'strength_plan') {
updateTable = StrengthPlanUpdateTable(this);
spaceId = _isd.ColumnInt(
'spaceId',
this,
);
keyId = _isd.ColumnString(
'keyId',
this,
);
payload = _isd.ColumnByteData(
'payload',
this,
);
nonce = _isd.ColumnByteData(
'nonce',
this,
);
}
late final StrengthPlanUpdateTable updateTable;
/// The space owning this row. Maintained by the sync engine.
late final _isd.ColumnInt spaceId;
/// The key it is sealed with (ADR 0002's envelope); never null in Train.
late final _isd.ColumnString keyId;
/// The content, sealed: UTF-8 JSON with a `schemaVersion` once opened.
late final _isd.ColumnByteData payload;
/// The sealing's nonce (ADR 0002's envelope).
late final _isd.ColumnByteData nonce;
@override
List<_isd.Column> get columns => [
id,
spaceId,
keyId,
payload,
nonce,
];
}
class StrengthPlanInclude extends _isd.IncludeObject {
StrengthPlanInclude._();
@override
Map<String, _isd.Include?> get includes => {};
@override
_isd.Table<_isc.UuidValue?> get table => StrengthPlan.t;
}
class StrengthPlanIncludeList extends _isd.IncludeList {
StrengthPlanIncludeList._({
_isd.WhereExpressionBuilder<StrengthPlanTable>? where,
super.limit,
super.offset,
super.orderBy,
super.orderByList,
super.include,
}) {
super.where = where?.call(StrengthPlan.t);
}
@override
Map<String, _isd.Include?> get includes => include?.includes ?? {};
@override
_isd.Table<_isc.UuidValue?> get table => StrengthPlan.t;
}
class StrengthPlanRepository {
const StrengthPlanRepository._();
/// Returns a list of [StrengthPlan]s matching the given query parameters.
///
/// Use [where] to specify which items to include in the return value.
/// If none is specified, all items will be returned.
///
/// To specify the order of the items use [orderBy] or [orderByList]
/// when sorting by multiple columns.
///
/// The maximum number of items can be set by [limit]. If no limit is set,
/// all items matching the query will be returned.
///
/// [offset] defines how many items to skip, after which [limit] (or all)
/// items are read from the database.
///
/// ```dart
/// var persons = await Persons.db.find(
/// session,
/// where: (t) => t.lastName.equals('Jones'),
/// orderBy: (t) => t.firstName,
/// limit: 100,
/// );
/// ```
Future<List<StrengthPlan>> find(
_isd.DatabaseSession session, {
_isd.WhereExpressionBuilder<StrengthPlanTable>? where,
int? limit,
int? offset,
_isd.OrderByBuilder<StrengthPlanTable>? orderBy,
_isd.OrderByListBuilder<StrengthPlanTable>? orderByList,
_isd.Transaction? transaction,
_isd.LockMode? lockMode,
_isd.LockBehavior? lockBehavior,
}) async {
return session.db.find<StrengthPlan>(
where: where?.call(StrengthPlan.t),
orderBy: orderBy?.call(StrengthPlan.t),
orderByList: orderByList?.call(StrengthPlan.t),
limit: limit,
offset: offset,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Returns the first matching [StrengthPlan] matching the given query parameters.
///
/// Use [where] to specify which items to include in the return value.
/// If none is specified, all items will be returned.
///
/// To specify the order use [orderBy] or [orderByList]
/// when sorting by multiple columns.
///
/// [offset] defines how many items to skip, after which the next one will be picked.
///
/// ```dart
/// var youngestPerson = await Persons.db.findFirstRow(
/// session,
/// where: (t) => t.lastName.equals('Jones'),
/// orderBy: (t) => t.age,
/// );
/// ```
Future<StrengthPlan?> findFirstRow(
_isd.DatabaseSession session, {
_isd.WhereExpressionBuilder<StrengthPlanTable>? where,
int? offset,
_isd.OrderByBuilder<StrengthPlanTable>? orderBy,
_isd.OrderByListBuilder<StrengthPlanTable>? orderByList,
_isd.Transaction? transaction,
_isd.LockMode? lockMode,
_isd.LockBehavior? lockBehavior,
}) async {
return session.db.findFirstRow<StrengthPlan>(
where: where?.call(StrengthPlan.t),
orderBy: orderBy?.call(StrengthPlan.t),
orderByList: orderByList?.call(StrengthPlan.t),
offset: offset,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Finds a single [StrengthPlan] by its [id] or null if no such row exists.
Future<StrengthPlan?> findById(
_isd.DatabaseSession session,
_isc.UuidValue id, {
_isd.Transaction? transaction,
_isd.LockMode? lockMode,
_isd.LockBehavior? lockBehavior,
}) async {
return session.db.findById<StrengthPlan>(
id,
transaction: transaction,
lockMode: lockMode,
lockBehavior: lockBehavior,
);
}
/// Inserts all [StrengthPlan]s in the list and returns the inserted rows.
///
/// The returned [StrengthPlan]s will have their `id` fields set.
///
/// This is an atomic operation, meaning that if one of the rows fails to
/// insert, none of the rows will be inserted.
///
/// If [ignoreConflicts] is set to `true`, rows that conflict with existing
/// rows are silently skipped, and only the successfully inserted rows are
/// returned.
///
/// If [noReturn] is set to `true`, the inserted rows are not read back from
/// the database and an empty list is returned. This avoids the overhead of
/// transferring and deserializing the rows when the result is not needed.
Future<List<StrengthPlan>> insert(
_isd.DatabaseSession session,
List<StrengthPlan> rows, {
_isd.Transaction? transaction,
bool ignoreConflicts = false,
bool noReturn = false,
}) async {
return session.db.insert<StrengthPlan>(
rows,
transaction: transaction,
ignoreConflicts: ignoreConflicts,
noReturn: noReturn,
);
}
/// Inserts a single [StrengthPlan] and returns the inserted row.
///
/// The returned [StrengthPlan] will have its `id` field set.
Future<StrengthPlan> insertRow(
_isd.DatabaseSession session,
StrengthPlan row, {
_isd.Transaction? transaction,
}) async {
return session.db.insertRow<StrengthPlan>(
row,
transaction: transaction,
);
}
/// Upserts all [StrengthPlan]s in the list and returns the resulting rows.
///
/// If a row conflicts on the given [conflictColumns], the existing row is
/// updated with the new values. Otherwise, a new row is inserted.
///
/// If [updateColumns] is provided, only those columns will be updated on
/// conflict. If null, all non-conflict, non-id columns are updated.
///
/// If [updateWhere] is provided, the update only applies to rows matching the
/// given expression. Conflicting rows that don't match are skipped and not
/// returned, so the resulting list may be shorter than [rows].
///
/// The returned [StrengthPlan]s will have their `id` fields set.
///
/// This is an atomic operation, meaning that if one of the rows fails,
/// none of the rows will be affected.
///
/// If [noReturn] is set to `true`, the resulting rows are not read back from
/// the database and an empty list is returned. This avoids the overhead of
/// transferring and deserializing the rows when the result is not needed.
Future<List<StrengthPlan>> upsert(
_isd.DatabaseSession session,
List<StrengthPlan> rows, {
required _isd.ColumnSelections<StrengthPlanTable> conflictColumns,
_isd.ColumnSelections<StrengthPlanTable>? updateColumns,
_isd.WhereExpressionBuilder<StrengthPlanTable>? updateWhere,
_isd.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.upsert<StrengthPlan>(
rows,
conflictColumns: conflictColumns(StrengthPlan.t),
updateColumns: updateColumns?.call(StrengthPlan.t),
updateWhere: updateWhere?.call(StrengthPlan.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Upserts a single [StrengthPlan] and returns the resulting row.
///
/// If the row conflicts on the given [conflictColumns], the existing row is
/// updated. Otherwise, a new row is inserted.
///
/// If [updateColumns] is provided, only those columns will be updated on
/// conflict. If null, all non-conflict, non-id columns are updated.
///
/// If [updateWhere] is provided, the update only applies when the existing
/// row matches the expression. Returns `null` if no row was affected — for
/// example when [updateWhere] does not match the conflicting row.
///
/// The returned [StrengthPlan] will have its `id` field set.
Future<StrengthPlan?> upsertRow(
_isd.DatabaseSession session,
StrengthPlan row, {
required _isd.ColumnSelections<StrengthPlanTable> conflictColumns,
_isd.ColumnSelections<StrengthPlanTable>? updateColumns,
_isd.WhereExpressionBuilder<StrengthPlanTable>? updateWhere,
_isd.Transaction? transaction,
}) async {
return session.db.upsertRow<StrengthPlan>(
row,
conflictColumns: conflictColumns(StrengthPlan.t),
updateColumns: updateColumns?.call(StrengthPlan.t),
updateWhere: updateWhere?.call(StrengthPlan.t),
transaction: transaction,
);
}
/// Updates all [StrengthPlan]s in the list and returns the updated rows. If
/// [columns] is provided, only those columns will be updated. Defaults to
/// all columns.
/// This is an atomic operation, meaning that if one of the rows fails to
/// update, none of the rows will be updated.
///
/// If [noReturn] is set to `true`, the updated rows are not read back from
/// the database and an empty list is returned. This avoids the overhead of
/// transferring and deserializing the rows when the result is not needed.
Future<List<StrengthPlan>> update(
_isd.DatabaseSession session,
List<StrengthPlan> rows, {
_isd.ColumnSelections<StrengthPlanTable>? columns,
_isd.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.update<StrengthPlan>(
rows,
columns: columns?.call(StrengthPlan.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Updates a single [StrengthPlan]. The row needs to have its id set.
/// Optionally, a list of [columns] can be provided to only update those
/// columns. Defaults to all columns.
Future<StrengthPlan> updateRow(
_isd.DatabaseSession session,
StrengthPlan row, {
_isd.ColumnSelections<StrengthPlanTable>? columns,
_isd.Transaction? transaction,
}) async {
return session.db.updateRow<StrengthPlan>(
row,
columns: columns?.call(StrengthPlan.t),
transaction: transaction,
);
}
/// Updates a single [StrengthPlan] by its [id] with the specified [columnValues].
/// Returns the updated row or null if no row with the given id exists.
Future<StrengthPlan?> updateById(
_isd.DatabaseSession session,
_isc.UuidValue id, {
required _isd.ColumnValueListBuilder<StrengthPlanUpdateTable> columnValues,
_isd.Transaction? transaction,
}) async {
return session.db.updateById<StrengthPlan>(
id,
columnValues: columnValues(StrengthPlan.t.updateTable),
transaction: transaction,
);
}
/// Updates all [StrengthPlan]s matching the [where] expression with the specified [columnValues].
/// Returns the list of updated rows.
///
/// If [noReturn] is set to `true`, the updated rows are not read back from
/// the database and an empty list is returned. This avoids the overhead of
/// transferring and deserializing the rows when the result is not needed.
Future<List<StrengthPlan>> updateWhere(
_isd.DatabaseSession session, {
required _isd.ColumnValueListBuilder<StrengthPlanUpdateTable> columnValues,
required _isd.WhereExpressionBuilder<StrengthPlanTable> where,
int? limit,
int? offset,
_isd.OrderByBuilder<StrengthPlanTable>? orderBy,
_isd.OrderByListBuilder<StrengthPlanTable>? orderByList,
_isd.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.updateWhere<StrengthPlan>(
columnValues: columnValues(StrengthPlan.t.updateTable),
where: where(StrengthPlan.t),
limit: limit,
offset: offset,
orderBy: orderBy?.call(StrengthPlan.t),
orderByList: orderByList?.call(StrengthPlan.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Deletes all [StrengthPlan]s in the list and returns the deleted rows.
///
/// To specify the order of the returned rows use [orderBy] or [orderByList]
/// when sorting by multiple columns.
///
/// This is an atomic operation, meaning that if one of the rows fail to
/// be deleted, none of the rows will be deleted.
///
/// If [noReturn] is set to `true`, the deleted rows are not read back from
/// the database and an empty list is returned. This avoids the overhead of
/// transferring and deserializing the rows when the result is not needed.
Future<List<StrengthPlan>> delete(
_isd.DatabaseSession session,
List<StrengthPlan> rows, {
_isd.OrderByBuilder<StrengthPlanTable>? orderBy,
_isd.OrderByListBuilder<StrengthPlanTable>? orderByList,
_isd.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.delete<StrengthPlan>(
rows,
orderBy: orderBy?.call(StrengthPlan.t),
orderByList: orderByList?.call(StrengthPlan.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Deletes a single [StrengthPlan].
Future<StrengthPlan> deleteRow(
_isd.DatabaseSession session,
StrengthPlan row, {
_isd.Transaction? transaction,
}) async {
return session.db.deleteRow<StrengthPlan>(
row,
transaction: transaction,
);
}
/// Deletes all rows matching the [where] expression.
///
/// To specify the order of the returned rows use [orderBy] or [orderByList]
/// when sorting by multiple columns.
///
/// If [noReturn] is set to `true`, the deleted rows are not read back from
/// the database and an empty list is returned. This avoids the overhead of
/// transferring and deserializing the rows when the result is not needed.
Future<List<StrengthPlan>> deleteWhere(
_isd.DatabaseSession session, {
required _isd.WhereExpressionBuilder<StrengthPlanTable> where,
_isd.OrderByBuilder<StrengthPlanTable>? orderBy,
_isd.OrderByListBuilder<StrengthPlanTable>? orderByList,
_isd.Transaction? transaction,
bool noReturn = false,
}) async {
return session.db.deleteWhere<StrengthPlan>(
where: where(StrengthPlan.t),
orderBy: orderBy?.call(StrengthPlan.t),
orderByList: orderByList?.call(StrengthPlan.t),
transaction: transaction,
noReturn: noReturn,
);
}
/// Counts the number of rows matching the [where] expression. If omitted,
/// will return the count of all rows in the table.
Future<int> count(
_isd.DatabaseSession session, {
_isd.WhereExpressionBuilder<StrengthPlanTable>? where,
int? limit,
_isd.Transaction? transaction,
}) async {
return session.db.count<StrengthPlan>(
where: where?.call(StrengthPlan.t),
limit: limit,
transaction: transaction,
);
}
/// Acquires row-level locks on [StrengthPlan] rows matching the [where] expression.
Future<void> lockRows(
_isd.DatabaseSession session, {
required _isd.WhereExpressionBuilder<StrengthPlanTable> where,
required _isd.LockMode lockMode,
required _isd.Transaction transaction,
_isd.LockBehavior lockBehavior = _isd.LockBehavior.wait,
}) async {
return session.db.lockRows<StrengthPlan>(
where: where(StrengthPlan.t),
lockMode: lockMode,
lockBehavior: lockBehavior,
transaction: transaction,
);
}
}

View file

@ -19,6 +19,7 @@ import 'plan/plan.dart' as _i45prw73;
import 'privacy/content_key.dart' as _imed3x5d;
import 'profile/profile.dart' as _ia5j8fch;
import 'route/route.dart' as _i0mgeh8t;
import 'strength/strength_plan.dart' as _imwvu2mn;
import 'workout/workout.dart' as _i5b9b17z;
import 'workout/workout_series.dart' as _i92mefto;
@ -33,6 +34,7 @@ final List<_isd.Table> syncTables = [
_i45prw73.Plan.t,
_ia5j8fch.Profile.t,
_i0mgeh8t.SavedRoute.t,
_imwvu2mn.StrengthPlan.t,
_icbb9j80.TrainingPlace.t,
_i5b9b17z.Workout.t,
_i92mefto.WorkoutSeries.t,

View file

@ -50,6 +50,7 @@ object HealthRecords {
"hiking" -> ExerciseSessionRecord.EXERCISE_TYPE_HIKING
"cycling" -> ExerciseSessionRecord.EXERCISE_TYPE_BIKING
"indoorRowing" -> ExerciseSessionRecord.EXERCISE_TYPE_ROWING_MACHINE
"strength" -> ExerciseSessionRecord.EXERCISE_TYPE_STRENGTH_TRAINING
else -> ExerciseSessionRecord.EXERCISE_TYPE_OTHER_WORKOUT
}

View file

@ -20,6 +20,7 @@
"activityWalking": "Gehen",
"activityHiking": "Wandern",
"activityCycling": "Radfahren",
"activityStrength": "Krafttraining",
"activityIndoorRowing": "Rudern drinnen",
"activityOther": "Anderes Training",
"recordAutoPause": "Von selbst pausieren",
@ -60,7 +61,7 @@
"recordingNotificationChannel": "Aufzeichnung",
"recordingNotificationPaused": "{activity} · pausiert",
"workoutName": "{when, select, morning_running{Morgenlauf} morning_walking{Morgenspaziergang} morning_hiking{Morgenwanderung} morning_cycling{Morgenradfahrt} morning_indoorRowing{Rudern am Morgen} afternoon_running{Nachmittagslauf} afternoon_walking{Nachmittagsspaziergang} afternoon_hiking{Nachmittagswanderung} afternoon_cycling{Nachmittagsradfahrt} afternoon_indoorRowing{Rudern am Nachmittag} evening_running{Abendlauf} evening_walking{Abendspaziergang} evening_hiking{Abendwanderung} evening_cycling{Abendradfahrt} evening_indoorRowing{Rudern am Abend} night_running{Nachtlauf} night_walking{Nachtspaziergang} night_hiking{Nachtwanderung} night_cycling{Nachtradfahrt} night_indoorRowing{Rudern in der Nacht} other{Training}}",
"workoutName": "{when, select, morning_strength{Krafttraining am Morgen} afternoon_strength{Krafttraining am Nachmittag} evening_strength{Krafttraining am Abend} night_strength{Krafttraining in der Nacht} morning_running{Morgenlauf} morning_walking{Morgenspaziergang} morning_hiking{Morgenwanderung} morning_cycling{Morgenradfahrt} morning_indoorRowing{Rudern am Morgen} afternoon_running{Nachmittagslauf} afternoon_walking{Nachmittagsspaziergang} afternoon_hiking{Nachmittagswanderung} afternoon_cycling{Nachmittagsradfahrt} afternoon_indoorRowing{Rudern am Nachmittag} evening_running{Abendlauf} evening_walking{Abendspaziergang} evening_hiking{Abendwanderung} evening_cycling{Abendradfahrt} evening_indoorRowing{Rudern am Abend} night_running{Nachtlauf} night_walking{Nachtspaziergang} night_hiking{Nachtwanderung} night_cycling{Nachtradfahrt} night_indoorRowing{Rudern in der Nacht} other{Training}}",
"workoutLocked": "Gesperrtes Training",
"workoutLockedHint": "Dieses Gerät hat den Schlüssel nicht, mit dem es versiegelt ist.",
"workoutEdit": "Bearbeiten",
@ -634,5 +635,77 @@
"plateOn": "Leg auf: {plates}",
"plateNoChange": "Nichts ändert sich",
"plateMoves": "{count, plural, =0{Keine Scheibe bewegt sich} =1{1 Scheibe je Seite bewegt sich} other{{count} Scheiben je Seite bewegen sich}}",
"plateSet": "Satz {number}"
"plateSet": "Satz {number}",
"exerciseName": "{id, select, backSquat{Kniebeuge} frontSquat{Frontkniebeuge} gobletSquat{Goblet Squat} kettlebellGobletSquat{Goblet Squat mit Kettlebell} bodyweightSquat{Kniebeuge ohne Gewicht} lunge{Ausfallschritt} bulgarianSplitSquat{Bulgarische Kniebeuge} bodyweightLunge{Ausfallschritt ohne Gewicht} deadlift{Kreuzheben} romanianDeadlift{Rumänisches Kreuzheben} trapBarDeadlift{Kreuzheben mit Trap Bar} dumbbellRomanianDeadlift{Rumänisches Kreuzheben mit Kurzhanteln} kettlebellSwing{Kettlebell Swing} hipThrust{Hip Thrust} gluteBridge{Glute Bridge} benchPress{Bankdrücken} inclineBenchPress{Schrägbankdrücken} dumbbellBenchPress{Kurzhantel-Bankdrücken} pushUp{Liegestütz} dip{Dip} overheadPress{Schulterdrücken} dumbbellShoulderPress{Kurzhantel-Schulterdrücken} pikePushUp{Pike-Liegestütz} barbellRow{Langhantelrudern} dumbbellRow{Einarmiges Kurzhantelrudern} cableRow{Rudern am Kabelzug} invertedRow{Umgekehrtes Rudern} bandPullApart{Band Pull-Apart} facePull{Face Pull} pullUp{Klimmzug} chinUp{Klimmzug im Untergriff} latPulldown{Latzug} bandPulldown{Latzug mit Band} barbellCurl{Langhantel-Curl} ezBarCurl{SZ-Curl} dumbbellCurl{Kurzhantel-Curl} hammerCurl{Hammer-Curl} tricepsPushdown{Trizepsdrücken am Kabel} skullCrusher{French Press} overheadTricepsExtension{Trizepsstrecken über Kopf} benchDip{Dip an der Bank} lateralRaise{Seitheben} cableLateralRaise{Seitheben am Kabel} calfRaise{Wadenheben} plank{Unterarmstütz} sidePlank{Seitstütz} hangingLegRaise{Beinheben im Hang} crunch{Crunch} farmersCarry{Farmer’s Walk} other{Übung}}",
"exerciseNeed": "{need, select, barbell{eine Langhantel} ezBar{eine SZ-Stange} trapBar{eine Trap Bar} dumbbells{Kurzhanteln} kettlebell{eine Kettlebell} bench{eine Bank} rack{ein Rack} pullUpBar{eine Klimmzugstange} dipBars{eine Dipstation} cableMachine{einen Kabelzug} bands{Widerstandsbänder} other{etwas}}",
"exercisePattern": "{pattern, select, squat{Kniebeugen} lunge{Ausfallschritte} hinge{Hüftstreckung} horizontalPush{Drücken} verticalPush{Drücken über Kopf} horizontalPull{Rudern} verticalPull{Klimmzüge und Latzug} curl{Curls} extension{Trizeps} raise{Heben} calf{Waden} core{Rumpf} carry{Tragen} other{Andere}}",
"exerciseLoad": "{load, select, barbell{Langhantel mit Scheiben} ezBar{SZ-Stange mit Scheiben} trapBar{Trap Bar mit Scheiben} dumbbellPair{Eine Kurzhantel in jeder Hand} dumbbell{Eine Kurzhantel} kettlebell{Kettlebell} cable{Kabelzug} band{Band} body{Körpergewicht} other{Anderes}}",
"activityStrengthCard": "Kraft",
"strengthPlansHeading": "Kraftpläne",
"strengthPlansNew": "Neuer Kraftplan",
"strengthPlansEmpty": "Kraftpläne listen Übungen mit Sätzen, Wiederholungen, Gewichten und Pausen auf, einzeln oder als Supersätze und Zirkel.",
"strengthPlanLocked": "Lässt sich auf diesem Gerät nicht öffnen",
"strengthPlanDeleted": "Kraftplan gelöscht",
"strengthStart": "Start",
"strengthChoosePlan": "Welcher Plan?",
"strengthNoPlans": "Leg zuerst einen Kraftplan an: Die Liste unten hat „Neuer Kraftplan“.",
"strengthPlanTitle": "Kraftplan",
"strengthPlanName": "Name",
"strengthPlanSave": "Speichern",
"strengthPlanDelete": "Löschen",
"strengthAddExercise": "Übung hinzufügen",
"strengthAddToBlock": "Übung im Wechsel hinzufügen",
"strengthOwnExercise": "Eigene Übung",
"strengthOwnExercises": "Eigene",
"strengthExerciseName": "Was es ist",
"strengthExerciseNeeds": "Was es braucht",
"strengthExerciseLoad": "Womit",
"strengthExercisePattern": "Bewegung",
"strengthExerciseTimed": "Wird gehalten",
"strengthExerciseDone": "Fertig",
"strengthAddSet": "Satz hinzufügen",
"strengthReps": "Wdh.",
"strengthSeconds": "Sekunden",
"strengthKg": "kg",
"strengthRemoveExercise": "Übung entfernen",
"strengthRemoveSet": "Satz entfernen",
"strengthMoveUp": "Nach oben",
"strengthMoveDown": "Nach unten",
"strengthRest": "Pause",
"strengthRestBetween": "Pause zwischen den Übungen",
"strengthDiscardTitle": "Änderungen verwerfen?",
"strengthDiscard": "Verwerfen",
"strengthDiscardMessage": "Was du an diesem Plan geändert hast, geht verloren.",
"strengthAbandon": "Ohne Speichern beenden",
"strengthAbandonMessage": "Die bisherigen Sätze werden von diesem Gerät gelöscht. Dies kann nicht rückgängig gemacht werden.",
"strengthFinish": "Beenden und speichern",
"strengthAllDone": "Alle Sätze sind durch.",
"strengthSetDone": "Geschafft",
"strengthSkip": "Satz überspringen",
"strengthSwap": "Übung tauschen",
"strengthSwapTitle": "Stattdessen",
"strengthNoSwaps": "Nichts anderes hier bewegt sich so.",
"strengthRestSkip": "Pause überspringen",
"strengthResting": "Pause",
"strengthCarryOn": "Ein Training läuft",
"strengthCarryOnAction": "Weiter",
"strengthWeightNotMadeUp": "Hier lässt sich dieses Gewicht nicht zusammenstellen.",
"strengthFixed": "{weight} kg, das nächste darunter",
"strengthNoPlace": "Kein Ort gewählt: Gewichte wie geplant.",
"strengthTrainsAt": "In {place}",
"strengthWorkoutVolume": "{weight} kg bewegt",
"strengthWorkoutSets": "{count, plural, =1{1 Satz} other{{count} Sätze}}",
"strengthMissing": "Fehlt hier: {needs}",
"strengthPlanSummary": "{exercises, plural, =1{1 Übung} other{{exercises} Übungen}} · {sets, plural, =1{1 Satz} other{{sets} Sätze}}",
"strengthBlockKind": "{kind, select, superset{Supersatz} circuit{Zirkel} other{Übung}}",
"strengthSetOf": "Satz {set} von {sets}",
"strengthRoundOf": "Runde {round} von {rounds}",
"strengthTarget": "{reps} × {weight} kg",
"strengthTargetReps": "{reps, plural, =1{1 Wdh.} other{{reps} Wdh.}}",
"strengthTargetSeconds": "{seconds} s",
"strengthNextUp": "Als Nächstes: {exercise}",
"strengthBarAndPlates": "{bar}-kg-Stange, jede Seite: {plates}",
"strengthHandlesAndPlates": "{bar}-kg-Griffe, jede Seite: {plates}",
"strengthWeightShort": "{weight} kg unter dem Plan"
}

View file

@ -22,6 +22,7 @@
"activityWalking": "Walking",
"activityHiking": "Hiking",
"activityCycling": "Cycling",
"activityStrength": "Strength training",
"activityIndoorRowing": "Rowing indoors",
"activityOther": "Other workout",
"recordAutoPause": "Pause by itself",
@ -75,7 +76,7 @@
"recordingNotificationPaused": "{activity} · paused",
"@recordingNotificationPaused": {"description": "The notification's title while paused.", "placeholders": {"activity": {"type": "String", "example": "Running"}}},
"workoutName": "{when, select, morning_running{Morning run} morning_walking{Morning walk} morning_hiking{Morning hike} morning_cycling{Morning ride} morning_indoorRowing{Morning row} afternoon_running{Afternoon run} afternoon_walking{Afternoon walk} afternoon_hiking{Afternoon hike} afternoon_cycling{Afternoon ride} afternoon_indoorRowing{Afternoon row} evening_running{Evening run} evening_walking{Evening walk} evening_hiking{Evening hike} evening_cycling{Evening ride} evening_indoorRowing{Evening row} night_running{Night run} night_walking{Night walk} night_hiking{Night hike} night_cycling{Night ride} night_indoorRowing{Night row} other{Workout}}",
"workoutName": "{when, select, morning_strength{Morning strength} afternoon_strength{Afternoon strength} evening_strength{Evening strength} night_strength{Night strength} morning_running{Morning run} morning_walking{Morning walk} morning_hiking{Morning hike} morning_cycling{Morning ride} morning_indoorRowing{Morning row} afternoon_running{Afternoon run} afternoon_walking{Afternoon walk} afternoon_hiking{Afternoon hike} afternoon_cycling{Afternoon ride} afternoon_indoorRowing{Afternoon row} evening_running{Evening run} evening_walking{Evening walk} evening_hiking{Evening hike} evening_cycling{Evening ride} evening_indoorRowing{Evening row} night_running{Night run} night_walking{Night walk} night_hiking{Night hike} night_cycling{Night ride} night_indoorRowing{Night row} other{Workout}}",
"@workoutName": {"description": "A workout's name where the person gave none: the time of day it started (morning 5–12, afternoon 12–17, evening 17–22, night) and its activity, as Strava names them.", "placeholders": {"when": {"type": "String"}}},
"workoutLocked": "Locked workout",
"workoutLockedHint": "This device doesn't hold the key it's sealed with.",
@ -801,5 +802,93 @@
"plateMoves": "{count, plural, =0{No plate moves} =1{1 plate moves on each side} other{{count} plates move on each side}}",
"@plateMoves": {"placeholders": {"count": {"type": "int"}}},
"plateSet": "Set {number}",
"@plateSet": {"placeholders": {"number": {"type": "int"}}}
"@plateSet": {"placeholders": {"number": {"type": "int"}}},
"exerciseName": "{id, select, backSquat{Back squat} frontSquat{Front squat} gobletSquat{Goblet squat} kettlebellGobletSquat{Kettlebell goblet squat} bodyweightSquat{Bodyweight squat} lunge{Lunge} bulgarianSplitSquat{Bulgarian split squat} bodyweightLunge{Bodyweight lunge} deadlift{Deadlift} romanianDeadlift{Romanian deadlift} trapBarDeadlift{Trap bar deadlift} dumbbellRomanianDeadlift{Dumbbell Romanian deadlift} kettlebellSwing{Kettlebell swing} hipThrust{Hip thrust} gluteBridge{Glute bridge} benchPress{Bench press} inclineBenchPress{Incline bench press} dumbbellBenchPress{Dumbbell bench press} pushUp{Push-up} dip{Dip} overheadPress{Overhead press} dumbbellShoulderPress{Dumbbell shoulder press} pikePushUp{Pike push-up} barbellRow{Barbell row} dumbbellRow{One-arm dumbbell row} cableRow{Seated cable row} invertedRow{Inverted row} bandPullApart{Band pull-apart} facePull{Face pull} pullUp{Pull-up} chinUp{Chin-up} latPulldown{Lat pulldown} bandPulldown{Band pulldown} barbellCurl{Barbell curl} ezBarCurl{EZ bar curl} dumbbellCurl{Dumbbell curl} hammerCurl{Hammer curl} tricepsPushdown{Triceps pushdown} skullCrusher{Skull crusher} overheadTricepsExtension{Overhead triceps extension} benchDip{Bench dip} lateralRaise{Lateral raise} cableLateralRaise{Cable lateral raise} calfRaise{Calf raise} plank{Plank} sidePlank{Side plank} hangingLegRaise{Hanging leg raise} crunch{Crunch} farmersCarry{Farmer's carry} other{Exercise}}",
"@exerciseName": {"placeholders": {"id": {"type": "String"}}, "description": "A catalogue exercise's name, by its id (ADR 0035, D1)."},
"exerciseNeed": "{need, select, barbell{a barbell} ezBar{an EZ bar} trapBar{a trap bar} dumbbells{dumbbells} kettlebell{a kettlebell} bench{a bench} rack{a rack} pullUpBar{a pull-up bar} dipBars{dip bars} cableMachine{a cable machine} bands{bands} other{something}}",
"@exerciseNeed": {"placeholders": {"need": {"type": "String"}}, "description": "What an exercise needs, as in \"Missing here: a rack, dip bars\"."},
"exercisePattern": "{pattern, select, squat{Squats} lunge{Lunges} hinge{Hinges} horizontalPush{Pushes} verticalPush{Pushes overhead} horizontalPull{Rows} verticalPull{Pull-ups and pulldowns} curl{Curls} extension{Triceps} raise{Raises} calf{Calves} core{Core} carry{Carries} other{Other}}",
"@exercisePattern": {"placeholders": {"pattern": {"type": "String"}}},
"exerciseLoad": "{load, select, barbell{Barbell with plates} ezBar{EZ bar with plates} trapBar{Trap bar with plates} dumbbellPair{A dumbbell in each hand} dumbbell{One dumbbell} kettlebell{Kettlebell} cable{Cable stack} band{Band} body{Body weight} other{Other}}",
"@exerciseLoad": {"placeholders": {"load": {"type": "String"}}},
"activityStrengthCard": "Strength",
"strengthPlansHeading": "Strength plans",
"strengthPlansNew": "New strength plan",
"strengthPlansEmpty": "Strength plans list exercises with their sets, repetitions, weights and rests, alone or as supersets and circuits.",
"strengthPlanLocked": "Can't be opened on this device",
"strengthPlanDeleted": "Strength plan deleted",
"strengthStart": "Start",
"strengthChoosePlan": "Which plan?",
"strengthNoPlans": "Make a strength plan first: the list below has New strength plan.",
"strengthPlanTitle": "Strength plan",
"strengthPlanName": "Name",
"strengthPlanSave": "Save",
"strengthPlanDelete": "Delete",
"strengthAddExercise": "Add exercise",
"strengthAddToBlock": "Add an exercise to do in turn",
"strengthOwnExercise": "Your own exercise",
"strengthOwnExercises": "Your own",
"strengthExerciseName": "What it is",
"strengthExerciseNeeds": "What it needs",
"strengthExerciseLoad": "What it is done with",
"strengthExercisePattern": "How it moves",
"strengthExerciseTimed": "Held for a time",
"strengthExerciseDone": "Done",
"strengthAddSet": "Add a set",
"strengthReps": "Reps",
"strengthSeconds": "Seconds",
"strengthKg": "kg",
"strengthRemoveExercise": "Remove exercise",
"strengthRemoveSet": "Remove set",
"strengthMoveUp": "Move up",
"strengthMoveDown": "Move down",
"strengthRest": "Rest",
"strengthRestBetween": "Rest between exercises",
"strengthDiscardTitle": "Discard changes?",
"strengthDiscard": "Discard",
"strengthDiscardMessage": "What you changed in this plan is not kept.",
"strengthAbandon": "End without saving",
"strengthAbandonMessage": "The sets done so far are deleted from this device. This can't be undone.",
"strengthFinish": "Finish and save",
"strengthAllDone": "Every set is through.",
"strengthSetDone": "Done",
"strengthSkip": "Skip set",
"strengthSwap": "Swap exercise",
"strengthSwapTitle": "Do instead",
"strengthNoSwaps": "Nothing else here moves this way.",
"strengthRestSkip": "Skip rest",
"strengthResting": "Rest",
"strengthCarryOn": "A session is under way",
"strengthCarryOnAction": "Carry on",
"strengthWeightNotMadeUp": "Nothing here makes this weight up.",
"strengthFixed": "{weight} kg, nearest at or below",
"strengthNoPlace": "No place chosen: weights as planned.",
"strengthTrainsAt": "At {place}",
"strengthWorkoutVolume": "{weight} kg moved",
"strengthWorkoutSets": "{count, plural, =1{1 set} other{{count} sets}}",
"strengthMissing": "Missing here: {needs}",
"@strengthMissing": {"placeholders": {"needs": {"type": "String"}}},
"strengthPlanSummary": "{exercises, plural, =1{1 exercise} other{{exercises} exercises}} · {sets, plural, =1{1 set} other{{sets} sets}}",
"@strengthPlanSummary": {"placeholders": {"exercises": {"type": "int"}, "sets": {"type": "int"}}},
"strengthBlockKind": "{kind, select, superset{Superset} circuit{Circuit} other{Exercise}}",
"@strengthBlockKind": {"placeholders": {"kind": {"type": "String"}}},
"strengthSetOf": "Set {set} of {sets}",
"@strengthSetOf": {"placeholders": {"set": {"type": "int"}, "sets": {"type": "int"}}},
"strengthRoundOf": "Round {round} of {rounds}",
"@strengthRoundOf": {"placeholders": {"round": {"type": "int"}, "rounds": {"type": "int"}}},
"strengthTarget": "{reps} × {weight} kg",
"@strengthTarget": {"placeholders": {"reps": {"type": "int"}, "weight": {"type": "String"}}},
"strengthTargetReps": "{reps, plural, =1{1 rep} other{{reps} reps}}",
"@strengthTargetReps": {"placeholders": {"reps": {"type": "int"}}},
"strengthTargetSeconds": "{seconds} s",
"@strengthTargetSeconds": {"placeholders": {"seconds": {"type": "int"}}},
"strengthNextUp": "Next: {exercise}",
"@strengthNextUp": {"placeholders": {"exercise": {"type": "String"}}},
"strengthBarAndPlates": "{bar} kg bar, each side: {plates}",
"@strengthBarAndPlates": {"placeholders": {"bar": {"type": "String"}, "plates": {"type": "String"}}},
"strengthHandlesAndPlates": "{bar} kg handles, each side: {plates}",
"@strengthHandlesAndPlates": {"placeholders": {"bar": {"type": "String"}, "plates": {"type": "String"}}},
"strengthWeightShort": "{weight} kg below what was planned",
"@strengthWeightShort": {"placeholders": {"weight": {"type": "String"}}}
}

View file

@ -184,6 +184,12 @@ abstract class AppLocalizations {
/// **'Cycling'**
String get activityCycling;
/// No description provided for @activityStrength.
///
/// In en, this message translates to:
/// **'Strength training'**
String get activityStrength;
/// No description provided for @activityIndoorRowing.
///
/// In en, this message translates to:
@ -421,7 +427,7 @@ abstract class AppLocalizations {
/// A workout's name where the person gave none: the time of day it started (morning 5–12, afternoon 12–17, evening 17–22, night) and its activity, as Strava names them.
///
/// In en, this message translates to:
/// **'{when, select, morning_running{Morning run} morning_walking{Morning walk} morning_hiking{Morning hike} morning_cycling{Morning ride} morning_indoorRowing{Morning row} afternoon_running{Afternoon run} afternoon_walking{Afternoon walk} afternoon_hiking{Afternoon hike} afternoon_cycling{Afternoon ride} afternoon_indoorRowing{Afternoon row} evening_running{Evening run} evening_walking{Evening walk} evening_hiking{Evening hike} evening_cycling{Evening ride} evening_indoorRowing{Evening row} night_running{Night run} night_walking{Night walk} night_hiking{Night hike} night_cycling{Night ride} night_indoorRowing{Night row} other{Workout}}'**
/// **'{when, select, morning_strength{Morning strength} afternoon_strength{Afternoon strength} evening_strength{Evening strength} night_strength{Night strength} morning_running{Morning run} morning_walking{Morning walk} morning_hiking{Morning hike} morning_cycling{Morning ride} morning_indoorRowing{Morning row} afternoon_running{Afternoon run} afternoon_walking{Afternoon walk} afternoon_hiking{Afternoon hike} afternoon_cycling{Afternoon ride} afternoon_indoorRowing{Afternoon row} evening_running{Evening run} evening_walking{Evening walk} evening_hiking{Evening hike} evening_cycling{Evening ride} evening_indoorRowing{Evening row} night_running{Night run} night_walking{Night walk} night_hiking{Night hike} night_cycling{Night ride} night_indoorRowing{Night row} other{Workout}}'**
String workoutName(String when);
/// No description provided for @workoutLocked.
@ -3825,6 +3831,432 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Set {number}'**
String plateSet(int number);
/// A catalogue exercise's name, by its id (ADR 0035, D1).
///
/// In en, this message translates to:
/// **'{id, select, backSquat{Back squat} frontSquat{Front squat} gobletSquat{Goblet squat} kettlebellGobletSquat{Kettlebell goblet squat} bodyweightSquat{Bodyweight squat} lunge{Lunge} bulgarianSplitSquat{Bulgarian split squat} bodyweightLunge{Bodyweight lunge} deadlift{Deadlift} romanianDeadlift{Romanian deadlift} trapBarDeadlift{Trap bar deadlift} dumbbellRomanianDeadlift{Dumbbell Romanian deadlift} kettlebellSwing{Kettlebell swing} hipThrust{Hip thrust} gluteBridge{Glute bridge} benchPress{Bench press} inclineBenchPress{Incline bench press} dumbbellBenchPress{Dumbbell bench press} pushUp{Push-up} dip{Dip} overheadPress{Overhead press} dumbbellShoulderPress{Dumbbell shoulder press} pikePushUp{Pike push-up} barbellRow{Barbell row} dumbbellRow{One-arm dumbbell row} cableRow{Seated cable row} invertedRow{Inverted row} bandPullApart{Band pull-apart} facePull{Face pull} pullUp{Pull-up} chinUp{Chin-up} latPulldown{Lat pulldown} bandPulldown{Band pulldown} barbellCurl{Barbell curl} ezBarCurl{EZ bar curl} dumbbellCurl{Dumbbell curl} hammerCurl{Hammer curl} tricepsPushdown{Triceps pushdown} skullCrusher{Skull crusher} overheadTricepsExtension{Overhead triceps extension} benchDip{Bench dip} lateralRaise{Lateral raise} cableLateralRaise{Cable lateral raise} calfRaise{Calf raise} plank{Plank} sidePlank{Side plank} hangingLegRaise{Hanging leg raise} crunch{Crunch} farmersCarry{Farmer\'s carry} other{Exercise}}'**
String exerciseName(String id);
/// What an exercise needs, as in "Missing here: a rack, dip bars".
///
/// In en, this message translates to:
/// **'{need, select, barbell{a barbell} ezBar{an EZ bar} trapBar{a trap bar} dumbbells{dumbbells} kettlebell{a kettlebell} bench{a bench} rack{a rack} pullUpBar{a pull-up bar} dipBars{dip bars} cableMachine{a cable machine} bands{bands} other{something}}'**
String exerciseNeed(String need);
/// No description provided for @exercisePattern.
///
/// In en, this message translates to:
/// **'{pattern, select, squat{Squats} lunge{Lunges} hinge{Hinges} horizontalPush{Pushes} verticalPush{Pushes overhead} horizontalPull{Rows} verticalPull{Pull-ups and pulldowns} curl{Curls} extension{Triceps} raise{Raises} calf{Calves} core{Core} carry{Carries} other{Other}}'**
String exercisePattern(String pattern);
/// No description provided for @exerciseLoad.
///
/// In en, this message translates to:
/// **'{load, select, barbell{Barbell with plates} ezBar{EZ bar with plates} trapBar{Trap bar with plates} dumbbellPair{A dumbbell in each hand} dumbbell{One dumbbell} kettlebell{Kettlebell} cable{Cable stack} band{Band} body{Body weight} other{Other}}'**
String exerciseLoad(String load);
/// No description provided for @activityStrengthCard.
///
/// In en, this message translates to:
/// **'Strength'**
String get activityStrengthCard;
/// No description provided for @strengthPlansHeading.
///
/// In en, this message translates to:
/// **'Strength plans'**
String get strengthPlansHeading;
/// No description provided for @strengthPlansNew.
///
/// In en, this message translates to:
/// **'New strength plan'**
String get strengthPlansNew;
/// No description provided for @strengthPlansEmpty.
///
/// In en, this message translates to:
/// **'Strength plans list exercises with their sets, repetitions, weights and rests, alone or as supersets and circuits.'**
String get strengthPlansEmpty;
/// No description provided for @strengthPlanLocked.
///
/// In en, this message translates to:
/// **'Can\'t be opened on this device'**
String get strengthPlanLocked;
/// No description provided for @strengthPlanDeleted.
///
/// In en, this message translates to:
/// **'Strength plan deleted'**
String get strengthPlanDeleted;
/// No description provided for @strengthStart.
///
/// In en, this message translates to:
/// **'Start'**
String get strengthStart;
/// No description provided for @strengthChoosePlan.
///
/// In en, this message translates to:
/// **'Which plan?'**
String get strengthChoosePlan;
/// No description provided for @strengthNoPlans.
///
/// In en, this message translates to:
/// **'Make a strength plan first: the list below has New strength plan.'**
String get strengthNoPlans;
/// No description provided for @strengthPlanTitle.
///
/// In en, this message translates to:
/// **'Strength plan'**
String get strengthPlanTitle;
/// No description provided for @strengthPlanName.
///
/// In en, this message translates to:
/// **'Name'**
String get strengthPlanName;
/// No description provided for @strengthPlanSave.
///
/// In en, this message translates to:
/// **'Save'**
String get strengthPlanSave;
/// No description provided for @strengthPlanDelete.
///
/// In en, this message translates to:
/// **'Delete'**
String get strengthPlanDelete;
/// No description provided for @strengthAddExercise.
///
/// In en, this message translates to:
/// **'Add exercise'**
String get strengthAddExercise;
/// No description provided for @strengthAddToBlock.
///
/// In en, this message translates to:
/// **'Add an exercise to do in turn'**
String get strengthAddToBlock;
/// No description provided for @strengthOwnExercise.
///
/// In en, this message translates to:
/// **'Your own exercise'**
String get strengthOwnExercise;
/// No description provided for @strengthOwnExercises.
///
/// In en, this message translates to:
/// **'Your own'**
String get strengthOwnExercises;
/// No description provided for @strengthExerciseName.
///
/// In en, this message translates to:
/// **'What it is'**
String get strengthExerciseName;
/// No description provided for @strengthExerciseNeeds.
///
/// In en, this message translates to:
/// **'What it needs'**
String get strengthExerciseNeeds;
/// No description provided for @strengthExerciseLoad.
///
/// In en, this message translates to:
/// **'What it is done with'**
String get strengthExerciseLoad;
/// No description provided for @strengthExercisePattern.
///
/// In en, this message translates to:
/// **'How it moves'**
String get strengthExercisePattern;
/// No description provided for @strengthExerciseTimed.
///
/// In en, this message translates to:
/// **'Held for a time'**
String get strengthExerciseTimed;
/// No description provided for @strengthExerciseDone.
///
/// In en, this message translates to:
/// **'Done'**
String get strengthExerciseDone;
/// No description provided for @strengthAddSet.
///
/// In en, this message translates to:
/// **'Add a set'**
String get strengthAddSet;
/// No description provided for @strengthReps.
///
/// In en, this message translates to:
/// **'Reps'**
String get strengthReps;
/// No description provided for @strengthSeconds.
///
/// In en, this message translates to:
/// **'Seconds'**
String get strengthSeconds;
/// No description provided for @strengthKg.
///
/// In en, this message translates to:
/// **'kg'**
String get strengthKg;
/// No description provided for @strengthRemoveExercise.
///
/// In en, this message translates to:
/// **'Remove exercise'**
String get strengthRemoveExercise;
/// No description provided for @strengthRemoveSet.
///
/// In en, this message translates to:
/// **'Remove set'**
String get strengthRemoveSet;
/// No description provided for @strengthMoveUp.
///
/// In en, this message translates to:
/// **'Move up'**
String get strengthMoveUp;
/// No description provided for @strengthMoveDown.
///
/// In en, this message translates to:
/// **'Move down'**
String get strengthMoveDown;
/// No description provided for @strengthRest.
///
/// In en, this message translates to:
/// **'Rest'**
String get strengthRest;
/// No description provided for @strengthRestBetween.
///
/// In en, this message translates to:
/// **'Rest between exercises'**
String get strengthRestBetween;
/// No description provided for @strengthDiscardTitle.
///
/// In en, this message translates to:
/// **'Discard changes?'**
String get strengthDiscardTitle;
/// No description provided for @strengthDiscard.
///
/// In en, this message translates to:
/// **'Discard'**
String get strengthDiscard;
/// No description provided for @strengthDiscardMessage.
///
/// In en, this message translates to:
/// **'What you changed in this plan is not kept.'**
String get strengthDiscardMessage;
/// No description provided for @strengthAbandon.
///
/// In en, this message translates to:
/// **'End without saving'**
String get strengthAbandon;
/// No description provided for @strengthAbandonMessage.
///
/// In en, this message translates to:
/// **'The sets done so far are deleted from this device. This can\'t be undone.'**
String get strengthAbandonMessage;
/// No description provided for @strengthFinish.
///
/// In en, this message translates to:
/// **'Finish and save'**
String get strengthFinish;
/// No description provided for @strengthAllDone.
///
/// In en, this message translates to:
/// **'Every set is through.'**
String get strengthAllDone;
/// No description provided for @strengthSetDone.
///
/// In en, this message translates to:
/// **'Done'**
String get strengthSetDone;
/// No description provided for @strengthSkip.
///
/// In en, this message translates to:
/// **'Skip set'**
String get strengthSkip;
/// No description provided for @strengthSwap.
///
/// In en, this message translates to:
/// **'Swap exercise'**
String get strengthSwap;
/// No description provided for @strengthSwapTitle.
///
/// In en, this message translates to:
/// **'Do instead'**
String get strengthSwapTitle;
/// No description provided for @strengthNoSwaps.
///
/// In en, this message translates to:
/// **'Nothing else here moves this way.'**
String get strengthNoSwaps;
/// No description provided for @strengthRestSkip.
///
/// In en, this message translates to:
/// **'Skip rest'**
String get strengthRestSkip;
/// No description provided for @strengthResting.
///
/// In en, this message translates to:
/// **'Rest'**
String get strengthResting;
/// No description provided for @strengthCarryOn.
///
/// In en, this message translates to:
/// **'A session is under way'**
String get strengthCarryOn;
/// No description provided for @strengthCarryOnAction.
///
/// In en, this message translates to:
/// **'Carry on'**
String get strengthCarryOnAction;
/// No description provided for @strengthWeightNotMadeUp.
///
/// In en, this message translates to:
/// **'Nothing here makes this weight up.'**
String get strengthWeightNotMadeUp;
/// No description provided for @strengthFixed.
///
/// In en, this message translates to:
/// **'{weight} kg, nearest at or below'**
String strengthFixed(Object weight);
/// No description provided for @strengthNoPlace.
///
/// In en, this message translates to:
/// **'No place chosen: weights as planned.'**
String get strengthNoPlace;
/// No description provided for @strengthTrainsAt.
///
/// In en, this message translates to:
/// **'At {place}'**
String strengthTrainsAt(Object place);
/// No description provided for @strengthWorkoutVolume.
///
/// In en, this message translates to:
/// **'{weight} kg moved'**
String strengthWorkoutVolume(Object weight);
/// No description provided for @strengthWorkoutSets.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 set} other{{count} sets}}'**
String strengthWorkoutSets(num count);
/// No description provided for @strengthMissing.
///
/// In en, this message translates to:
/// **'Missing here: {needs}'**
String strengthMissing(String needs);
/// No description provided for @strengthPlanSummary.
///
/// In en, this message translates to:
/// **'{exercises, plural, =1{1 exercise} other{{exercises} exercises}} · {sets, plural, =1{1 set} other{{sets} sets}}'**
String strengthPlanSummary(int exercises, int sets);
/// No description provided for @strengthBlockKind.
///
/// In en, this message translates to:
/// **'{kind, select, superset{Superset} circuit{Circuit} other{Exercise}}'**
String strengthBlockKind(String kind);
/// No description provided for @strengthSetOf.
///
/// In en, this message translates to:
/// **'Set {set} of {sets}'**
String strengthSetOf(int set, int sets);
/// No description provided for @strengthRoundOf.
///
/// In en, this message translates to:
/// **'Round {round} of {rounds}'**
String strengthRoundOf(int round, int rounds);
/// No description provided for @strengthTarget.
///
/// In en, this message translates to:
/// **'{reps} × {weight} kg'**
String strengthTarget(int reps, String weight);
/// No description provided for @strengthTargetReps.
///
/// In en, this message translates to:
/// **'{reps, plural, =1{1 rep} other{{reps} reps}}'**
String strengthTargetReps(int reps);
/// No description provided for @strengthTargetSeconds.
///
/// In en, this message translates to:
/// **'{seconds} s'**
String strengthTargetSeconds(int seconds);
/// No description provided for @strengthNextUp.
///
/// In en, this message translates to:
/// **'Next: {exercise}'**
String strengthNextUp(String exercise);
/// No description provided for @strengthBarAndPlates.
///
/// In en, this message translates to:
/// **'{bar} kg bar, each side: {plates}'**
String strengthBarAndPlates(String bar, String plates);
/// No description provided for @strengthHandlesAndPlates.
///
/// In en, this message translates to:
/// **'{bar} kg handles, each side: {plates}'**
String strengthHandlesAndPlates(String bar, String plates);
/// No description provided for @strengthWeightShort.
///
/// In en, this message translates to:
/// **'{weight} kg below what was planned'**
String strengthWeightShort(String weight);
}
class _AppLocalizationsDelegate

View file

@ -54,6 +54,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get activityCycling => 'Radfahren';
@override
String get activityStrength => 'Krafttraining';
@override
String get activityIndoorRowing => 'Rudern drinnen';
@ -199,6 +202,10 @@ class AppLocalizationsDe extends AppLocalizations {
String _temp0 = intl.Intl.selectLogic(
when,
{
'morning_strength': 'Krafttraining am Morgen',
'afternoon_strength': 'Krafttraining am Nachmittag',
'evening_strength': 'Krafttraining am Abend',
'night_strength': 'Krafttraining in der Nacht',
'morning_running': 'Morgenlauf',
'morning_walking': 'Morgenspaziergang',
'morning_hiking': 'Morgenwanderung',
@ -2332,4 +2339,400 @@ class AppLocalizationsDe extends AppLocalizations {
String plateSet(int number) {
return 'Satz $number';
}
@override
String exerciseName(String id) {
String _temp0 = intl.Intl.selectLogic(
id,
{
'backSquat': 'Kniebeuge',
'frontSquat': 'Frontkniebeuge',
'gobletSquat': 'Goblet Squat',
'kettlebellGobletSquat': 'Goblet Squat mit Kettlebell',
'bodyweightSquat': 'Kniebeuge ohne Gewicht',
'lunge': 'Ausfallschritt',
'bulgarianSplitSquat': 'Bulgarische Kniebeuge',
'bodyweightLunge': 'Ausfallschritt ohne Gewicht',
'deadlift': 'Kreuzheben',
'romanianDeadlift': 'Rumänisches Kreuzheben',
'trapBarDeadlift': 'Kreuzheben mit Trap Bar',
'dumbbellRomanianDeadlift': 'Rumänisches Kreuzheben mit Kurzhanteln',
'kettlebellSwing': 'Kettlebell Swing',
'hipThrust': 'Hip Thrust',
'gluteBridge': 'Glute Bridge',
'benchPress': 'Bankdrücken',
'inclineBenchPress': 'Schrägbankdrücken',
'dumbbellBenchPress': 'Kurzhantel-Bankdrücken',
'pushUp': 'Liegestütz',
'dip': 'Dip',
'overheadPress': 'Schulterdrücken',
'dumbbellShoulderPress': 'Kurzhantel-Schulterdrücken',
'pikePushUp': 'Pike-Liegestütz',
'barbellRow': 'Langhantelrudern',
'dumbbellRow': 'Einarmiges Kurzhantelrudern',
'cableRow': 'Rudern am Kabelzug',
'invertedRow': 'Umgekehrtes Rudern',
'bandPullApart': 'Band Pull-Apart',
'facePull': 'Face Pull',
'pullUp': 'Klimmzug',
'chinUp': 'Klimmzug im Untergriff',
'latPulldown': 'Latzug',
'bandPulldown': 'Latzug mit Band',
'barbellCurl': 'Langhantel-Curl',
'ezBarCurl': 'SZ-Curl',
'dumbbellCurl': 'Kurzhantel-Curl',
'hammerCurl': 'Hammer-Curl',
'tricepsPushdown': 'Trizepsdrücken am Kabel',
'skullCrusher': 'French Press',
'overheadTricepsExtension': 'Trizepsstrecken über Kopf',
'benchDip': 'Dip an der Bank',
'lateralRaise': 'Seitheben',
'cableLateralRaise': 'Seitheben am Kabel',
'calfRaise': 'Wadenheben',
'plank': 'Unterarmstütz',
'sidePlank': 'Seitstütz',
'hangingLegRaise': 'Beinheben im Hang',
'crunch': 'Crunch',
'farmersCarry': 'Farmer’s Walk',
'other': 'Übung',
},
);
return '$_temp0';
}
@override
String exerciseNeed(String need) {
String _temp0 = intl.Intl.selectLogic(
need,
{
'barbell': 'eine Langhantel',
'ezBar': 'eine SZ-Stange',
'trapBar': 'eine Trap Bar',
'dumbbells': 'Kurzhanteln',
'kettlebell': 'eine Kettlebell',
'bench': 'eine Bank',
'rack': 'ein Rack',
'pullUpBar': 'eine Klimmzugstange',
'dipBars': 'eine Dipstation',
'cableMachine': 'einen Kabelzug',
'bands': 'Widerstandsbänder',
'other': 'etwas',
},
);
return '$_temp0';
}
@override
String exercisePattern(String pattern) {
String _temp0 = intl.Intl.selectLogic(
pattern,
{
'squat': 'Kniebeugen',
'lunge': 'Ausfallschritte',
'hinge': 'Hüftstreckung',
'horizontalPush': 'Drücken',
'verticalPush': 'Drücken über Kopf',
'horizontalPull': 'Rudern',
'verticalPull': 'Klimmzüge und Latzug',
'curl': 'Curls',
'extension': 'Trizeps',
'raise': 'Heben',
'calf': 'Waden',
'core': 'Rumpf',
'carry': 'Tragen',
'other': 'Andere',
},
);
return '$_temp0';
}
@override
String exerciseLoad(String load) {
String _temp0 = intl.Intl.selectLogic(
load,
{
'barbell': 'Langhantel mit Scheiben',
'ezBar': 'SZ-Stange mit Scheiben',
'trapBar': 'Trap Bar mit Scheiben',
'dumbbellPair': 'Eine Kurzhantel in jeder Hand',
'dumbbell': 'Eine Kurzhantel',
'kettlebell': 'Kettlebell',
'cable': 'Kabelzug',
'band': 'Band',
'body': 'Körpergewicht',
'other': 'Anderes',
},
);
return '$_temp0';
}
@override
String get activityStrengthCard => 'Kraft';
@override
String get strengthPlansHeading => 'Kraftpläne';
@override
String get strengthPlansNew => 'Neuer Kraftplan';
@override
String get strengthPlansEmpty =>
'Kraftpläne listen Übungen mit Sätzen, Wiederholungen, Gewichten und Pausen auf, einzeln oder als Supersätze und Zirkel.';
@override
String get strengthPlanLocked => 'Lässt sich auf diesem Gerät nicht öffnen';
@override
String get strengthPlanDeleted => 'Kraftplan gelöscht';
@override
String get strengthStart => 'Start';
@override
String get strengthChoosePlan => 'Welcher Plan?';
@override
String get strengthNoPlans =>
'Leg zuerst einen Kraftplan an: Die Liste unten hat „Neuer Kraftplan“.';
@override
String get strengthPlanTitle => 'Kraftplan';
@override
String get strengthPlanName => 'Name';
@override
String get strengthPlanSave => 'Speichern';
@override
String get strengthPlanDelete => 'Löschen';
@override
String get strengthAddExercise => 'Übung hinzufügen';
@override
String get strengthAddToBlock => 'Übung im Wechsel hinzufügen';
@override
String get strengthOwnExercise => 'Eigene Übung';
@override
String get strengthOwnExercises => 'Eigene';
@override
String get strengthExerciseName => 'Was es ist';
@override
String get strengthExerciseNeeds => 'Was es braucht';
@override
String get strengthExerciseLoad => 'Womit';
@override
String get strengthExercisePattern => 'Bewegung';
@override
String get strengthExerciseTimed => 'Wird gehalten';
@override
String get strengthExerciseDone => 'Fertig';
@override
String get strengthAddSet => 'Satz hinzufügen';
@override
String get strengthReps => 'Wdh.';
@override
String get strengthSeconds => 'Sekunden';
@override
String get strengthKg => 'kg';
@override
String get strengthRemoveExercise => 'Übung entfernen';
@override
String get strengthRemoveSet => 'Satz entfernen';
@override
String get strengthMoveUp => 'Nach oben';
@override
String get strengthMoveDown => 'Nach unten';
@override
String get strengthRest => 'Pause';
@override
String get strengthRestBetween => 'Pause zwischen den Übungen';
@override
String get strengthDiscardTitle => 'Änderungen verwerfen?';
@override
String get strengthDiscard => 'Verwerfen';
@override
String get strengthDiscardMessage =>
'Was du an diesem Plan geändert hast, geht verloren.';
@override
String get strengthAbandon => 'Ohne Speichern beenden';
@override
String get strengthAbandonMessage =>
'Die bisherigen Sätze werden von diesem Gerät gelöscht. Dies kann nicht rückgängig gemacht werden.';
@override
String get strengthFinish => 'Beenden und speichern';
@override
String get strengthAllDone => 'Alle Sätze sind durch.';
@override
String get strengthSetDone => 'Geschafft';
@override
String get strengthSkip => 'Satz überspringen';
@override
String get strengthSwap => 'Übung tauschen';
@override
String get strengthSwapTitle => 'Stattdessen';
@override
String get strengthNoSwaps => 'Nichts anderes hier bewegt sich so.';
@override
String get strengthRestSkip => 'Pause überspringen';
@override
String get strengthResting => 'Pause';
@override
String get strengthCarryOn => 'Ein Training läuft';
@override
String get strengthCarryOnAction => 'Weiter';
@override
String get strengthWeightNotMadeUp =>
'Hier lässt sich dieses Gewicht nicht zusammenstellen.';
@override
String strengthFixed(Object weight) {
return '$weight kg, das nächste darunter';
}
@override
String get strengthNoPlace => 'Kein Ort gewählt: Gewichte wie geplant.';
@override
String strengthTrainsAt(Object place) {
return 'In $place';
}
@override
String strengthWorkoutVolume(Object weight) {
return '$weight kg bewegt';
}
@override
String strengthWorkoutSets(num count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count Sätze',
one: '1 Satz',
);
return '$_temp0';
}
@override
String strengthMissing(String needs) {
return 'Fehlt hier: $needs';
}
@override
String strengthPlanSummary(int exercises, int sets) {
String _temp0 = intl.Intl.pluralLogic(
exercises,
locale: localeName,
other: '$exercises Übungen',
one: '1 Übung',
);
String _temp1 = intl.Intl.pluralLogic(
sets,
locale: localeName,
other: '$sets Sätze',
one: '1 Satz',
);
return '$_temp0 · $_temp1';
}
@override
String strengthBlockKind(String kind) {
String _temp0 = intl.Intl.selectLogic(
kind,
{
'superset': 'Supersatz',
'circuit': 'Zirkel',
'other': 'Übung',
},
);
return '$_temp0';
}
@override
String strengthSetOf(int set, int sets) {
return 'Satz $set von $sets';
}
@override
String strengthRoundOf(int round, int rounds) {
return 'Runde $round von $rounds';
}
@override
String strengthTarget(int reps, String weight) {
return '$reps × $weight kg';
}
@override
String strengthTargetReps(int reps) {
String _temp0 = intl.Intl.pluralLogic(
reps,
locale: localeName,
other: '$reps Wdh.',
one: '1 Wdh.',
);
return '$_temp0';
}
@override
String strengthTargetSeconds(int seconds) {
return '$seconds s';
}
@override
String strengthNextUp(String exercise) {
return 'Als Nächstes: $exercise';
}
@override
String strengthBarAndPlates(String bar, String plates) {
return '$bar-kg-Stange, jede Seite: $plates';
}
@override
String strengthHandlesAndPlates(String bar, String plates) {
return '$bar-kg-Griffe, jede Seite: $plates';
}
@override
String strengthWeightShort(String weight) {
return '$weight kg unter dem Plan';
}
}

View file

@ -53,6 +53,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get activityCycling => 'Cycling';
@override
String get activityStrength => 'Strength training';
@override
String get activityIndoorRowing => 'Rowing indoors';
@ -197,6 +200,10 @@ class AppLocalizationsEn extends AppLocalizations {
String _temp0 = intl.Intl.selectLogic(
when,
{
'morning_strength': 'Morning strength',
'afternoon_strength': 'Afternoon strength',
'evening_strength': 'Evening strength',
'night_strength': 'Night strength',
'morning_running': 'Morning run',
'morning_walking': 'Morning walk',
'morning_hiking': 'Morning hike',
@ -2320,6 +2327,401 @@ class AppLocalizationsEn extends AppLocalizations {
String plateSet(int number) {
return 'Set $number';
}
@override
String exerciseName(String id) {
String _temp0 = intl.Intl.selectLogic(
id,
{
'backSquat': 'Back squat',
'frontSquat': 'Front squat',
'gobletSquat': 'Goblet squat',
'kettlebellGobletSquat': 'Kettlebell goblet squat',
'bodyweightSquat': 'Bodyweight squat',
'lunge': 'Lunge',
'bulgarianSplitSquat': 'Bulgarian split squat',
'bodyweightLunge': 'Bodyweight lunge',
'deadlift': 'Deadlift',
'romanianDeadlift': 'Romanian deadlift',
'trapBarDeadlift': 'Trap bar deadlift',
'dumbbellRomanianDeadlift': 'Dumbbell Romanian deadlift',
'kettlebellSwing': 'Kettlebell swing',
'hipThrust': 'Hip thrust',
'gluteBridge': 'Glute bridge',
'benchPress': 'Bench press',
'inclineBenchPress': 'Incline bench press',
'dumbbellBenchPress': 'Dumbbell bench press',
'pushUp': 'Push-up',
'dip': 'Dip',
'overheadPress': 'Overhead press',
'dumbbellShoulderPress': 'Dumbbell shoulder press',
'pikePushUp': 'Pike push-up',
'barbellRow': 'Barbell row',
'dumbbellRow': 'One-arm dumbbell row',
'cableRow': 'Seated cable row',
'invertedRow': 'Inverted row',
'bandPullApart': 'Band pull-apart',
'facePull': 'Face pull',
'pullUp': 'Pull-up',
'chinUp': 'Chin-up',
'latPulldown': 'Lat pulldown',
'bandPulldown': 'Band pulldown',
'barbellCurl': 'Barbell curl',
'ezBarCurl': 'EZ bar curl',
'dumbbellCurl': 'Dumbbell curl',
'hammerCurl': 'Hammer curl',
'tricepsPushdown': 'Triceps pushdown',
'skullCrusher': 'Skull crusher',
'overheadTricepsExtension': 'Overhead triceps extension',
'benchDip': 'Bench dip',
'lateralRaise': 'Lateral raise',
'cableLateralRaise': 'Cable lateral raise',
'calfRaise': 'Calf raise',
'plank': 'Plank',
'sidePlank': 'Side plank',
'hangingLegRaise': 'Hanging leg raise',
'crunch': 'Crunch',
'farmersCarry': 'Farmer\'s carry',
'other': 'Exercise',
},
);
return '$_temp0';
}
@override
String exerciseNeed(String need) {
String _temp0 = intl.Intl.selectLogic(
need,
{
'barbell': 'a barbell',
'ezBar': 'an EZ bar',
'trapBar': 'a trap bar',
'dumbbells': 'dumbbells',
'kettlebell': 'a kettlebell',
'bench': 'a bench',
'rack': 'a rack',
'pullUpBar': 'a pull-up bar',
'dipBars': 'dip bars',
'cableMachine': 'a cable machine',
'bands': 'bands',
'other': 'something',
},
);
return '$_temp0';
}
@override
String exercisePattern(String pattern) {
String _temp0 = intl.Intl.selectLogic(
pattern,
{
'squat': 'Squats',
'lunge': 'Lunges',
'hinge': 'Hinges',
'horizontalPush': 'Pushes',
'verticalPush': 'Pushes overhead',
'horizontalPull': 'Rows',
'verticalPull': 'Pull-ups and pulldowns',
'curl': 'Curls',
'extension': 'Triceps',
'raise': 'Raises',
'calf': 'Calves',
'core': 'Core',
'carry': 'Carries',
'other': 'Other',
},
);
return '$_temp0';
}
@override
String exerciseLoad(String load) {
String _temp0 = intl.Intl.selectLogic(
load,
{
'barbell': 'Barbell with plates',
'ezBar': 'EZ bar with plates',
'trapBar': 'Trap bar with plates',
'dumbbellPair': 'A dumbbell in each hand',
'dumbbell': 'One dumbbell',
'kettlebell': 'Kettlebell',
'cable': 'Cable stack',
'band': 'Band',
'body': 'Body weight',
'other': 'Other',
},
);
return '$_temp0';
}
@override
String get activityStrengthCard => 'Strength';
@override
String get strengthPlansHeading => 'Strength plans';
@override
String get strengthPlansNew => 'New strength plan';
@override
String get strengthPlansEmpty =>
'Strength plans list exercises with their sets, repetitions, weights and rests, alone or as supersets and circuits.';
@override
String get strengthPlanLocked => 'Can\'t be opened on this device';
@override
String get strengthPlanDeleted => 'Strength plan deleted';
@override
String get strengthStart => 'Start';
@override
String get strengthChoosePlan => 'Which plan?';
@override
String get strengthNoPlans =>
'Make a strength plan first: the list below has New strength plan.';
@override
String get strengthPlanTitle => 'Strength plan';
@override
String get strengthPlanName => 'Name';
@override
String get strengthPlanSave => 'Save';
@override
String get strengthPlanDelete => 'Delete';
@override
String get strengthAddExercise => 'Add exercise';
@override
String get strengthAddToBlock => 'Add an exercise to do in turn';
@override
String get strengthOwnExercise => 'Your own exercise';
@override
String get strengthOwnExercises => 'Your own';
@override
String get strengthExerciseName => 'What it is';
@override
String get strengthExerciseNeeds => 'What it needs';
@override
String get strengthExerciseLoad => 'What it is done with';
@override
String get strengthExercisePattern => 'How it moves';
@override
String get strengthExerciseTimed => 'Held for a time';
@override
String get strengthExerciseDone => 'Done';
@override
String get strengthAddSet => 'Add a set';
@override
String get strengthReps => 'Reps';
@override
String get strengthSeconds => 'Seconds';
@override
String get strengthKg => 'kg';
@override
String get strengthRemoveExercise => 'Remove exercise';
@override
String get strengthRemoveSet => 'Remove set';
@override
String get strengthMoveUp => 'Move up';
@override
String get strengthMoveDown => 'Move down';
@override
String get strengthRest => 'Rest';
@override
String get strengthRestBetween => 'Rest between exercises';
@override
String get strengthDiscardTitle => 'Discard changes?';
@override
String get strengthDiscard => 'Discard';
@override
String get strengthDiscardMessage =>
'What you changed in this plan is not kept.';
@override
String get strengthAbandon => 'End without saving';
@override
String get strengthAbandonMessage =>
'The sets done so far are deleted from this device. This can\'t be undone.';
@override
String get strengthFinish => 'Finish and save';
@override
String get strengthAllDone => 'Every set is through.';
@override
String get strengthSetDone => 'Done';
@override
String get strengthSkip => 'Skip set';
@override
String get strengthSwap => 'Swap exercise';
@override
String get strengthSwapTitle => 'Do instead';
@override
String get strengthNoSwaps => 'Nothing else here moves this way.';
@override
String get strengthRestSkip => 'Skip rest';
@override
String get strengthResting => 'Rest';
@override
String get strengthCarryOn => 'A session is under way';
@override
String get strengthCarryOnAction => 'Carry on';
@override
String get strengthWeightNotMadeUp => 'Nothing here makes this weight up.';
@override
String strengthFixed(Object weight) {
return '$weight kg, nearest at or below';
}
@override
String get strengthNoPlace => 'No place chosen: weights as planned.';
@override
String strengthTrainsAt(Object place) {
return 'At $place';
}
@override
String strengthWorkoutVolume(Object weight) {
return '$weight kg moved';
}
@override
String strengthWorkoutSets(num count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count sets',
one: '1 set',
);
return '$_temp0';
}
@override
String strengthMissing(String needs) {
return 'Missing here: $needs';
}
@override
String strengthPlanSummary(int exercises, int sets) {
String _temp0 = intl.Intl.pluralLogic(
exercises,
locale: localeName,
other: '$exercises exercises',
one: '1 exercise',
);
String _temp1 = intl.Intl.pluralLogic(
sets,
locale: localeName,
other: '$sets sets',
one: '1 set',
);
return '$_temp0 · $_temp1';
}
@override
String strengthBlockKind(String kind) {
String _temp0 = intl.Intl.selectLogic(
kind,
{
'superset': 'Superset',
'circuit': 'Circuit',
'other': 'Exercise',
},
);
return '$_temp0';
}
@override
String strengthSetOf(int set, int sets) {
return 'Set $set of $sets';
}
@override
String strengthRoundOf(int round, int rounds) {
return 'Round $round of $rounds';
}
@override
String strengthTarget(int reps, String weight) {
return '$reps × $weight kg';
}
@override
String strengthTargetReps(int reps) {
String _temp0 = intl.Intl.pluralLogic(
reps,
locale: localeName,
other: '$reps reps',
one: '1 rep',
);
return '$_temp0';
}
@override
String strengthTargetSeconds(int seconds) {
return '$seconds s';
}
@override
String strengthNextUp(String exercise) {
return 'Next: $exercise';
}
@override
String strengthBarAndPlates(String bar, String plates) {
return '$bar kg bar, each side: $plates';
}
@override
String strengthHandlesAndPlates(String bar, String plates) {
return '$bar kg handles, each side: $plates';
}
@override
String strengthWeightShort(String weight) {
return '$weight kg below what was planned';
}
}
/// The translations for English, as used in the United Kingdom (`en_GB`).

View file

@ -7,6 +7,7 @@ import 'package:halkyon_train_flutter/src/app/train_frame.dart';
import 'package:halkyon_train_flutter/src/app/train_languages.dart';
import 'package:halkyon_train_flutter/src/app/train_router.dart';
import 'package:halkyon_train_flutter/src/feature/day/bloc/days_cubit.dart';
import 'package:halkyon_train_flutter/src/feature/equipment/bloc/places_cubit.dart';
import 'package:halkyon_train_flutter/src/feature/music/data/cadence_model.dart';
import 'package:halkyon_train_flutter/src/feature/music/data/music_downloads.dart';
import 'package:halkyon_train_flutter/src/feature/music/data/recorder_music.dart';
@ -17,6 +18,7 @@ import 'package:halkyon_train_flutter/src/feature/recording/widget/announce_spee
import 'package:halkyon_train_flutter/src/feature/recording/widget/recording_texts.dart';
import 'package:halkyon_train_flutter/src/feature/route/bloc/routes_cubit.dart';
import 'package:halkyon_train_flutter/src/feature/route/widget/route_speech.dart';
import 'package:halkyon_train_flutter/src/feature/strength/bloc/strength_plans_cubit.dart';
import 'package:halkyon_train_flutter/src/feature/workout/bloc/workouts_cubit.dart';
import 'package:halkyon_train_flutter/src/feature/workout/model/workout.dart';
import 'package:halkyon_ui/halkyon_ui.dart';
@ -122,6 +124,23 @@ class _TrainPersonAppState() extends State<TrainPersonApp> {
return PlansCubit(train.plans, train.planDeletions)..open();
},
),
// The places and the strength plans, which the plans' lists, a
// session and Equipment share (ADR 0034, ADR 0035).
BlocProvider(
create: (context) {
final train = PersonScope.of<TrainPerson>(context);
return PlacesCubit(train.places, train.chosenPlace)..open();
},
),
BlocProvider(
create: (context) {
final train = PersonScope.of<TrainPerson>(context);
return StrengthPlansCubit(
train.strengthPlans,
train.strengthPlanDeletions,
)..open();
},
),
BlocProvider(
create: (context) {
final train = PersonScope.of<TrainPerson>(context);

View file

@ -40,6 +40,9 @@ import 'package:halkyon_train_flutter/src/feature/route/data/route_files.dart';
import 'package:halkyon_train_flutter/src/feature/route/data/route_repository.dart';
import 'package:halkyon_train_flutter/src/feature/route/data/routing_gateway.dart';
import 'package:halkyon_train_flutter/src/feature/route/data/sync_route_gateway.dart';
import 'package:halkyon_train_flutter/src/feature/strength/data/strength_plan_repository.dart';
import 'package:halkyon_train_flutter/src/feature/strength/data/strength_session_store.dart';
import 'package:halkyon_train_flutter/src/feature/strength/data/sync_strength_plan_gateway.dart';
import 'package:halkyon_train_flutter/src/feature/workout/data/sync_workout_gateway.dart';
import 'package:halkyon_train_flutter/src/feature/workout/data/workout_repository.dart';
import 'package:package_info_plus/package_info_plus.dart';
@ -92,6 +95,12 @@ class TrainPerson(
/// The place last chosen on this device (ADR 0034, D4).
required final ChosenPlaceStore chosenPlace,
/// Their strength plans (ADR 0035), sealed and synced.
required final StrengthPlanRepository strengthPlans,
/// The strength session under way on this device (ADR 0035, D3).
required final StrengthSessionStore strengthSession,
/// The instance's planning of routes (D44).
required final RoutingGateway routing,
@ -141,6 +150,12 @@ class TrainPerson(
/// Plans deleted, to be undone for a moment, as workouts are.
late final planDeletions = DelayedDeletions(plans.delete, logName: 'Plans');
/// Strength plans deleted, to be undone for a moment, as plans are.
late final strengthPlanDeletions = DelayedDeletions(
strengthPlans.delete,
logName: 'Strength plans',
);
/// Routes deleted, to be undone for a moment, as workouts are.
late final routeDeletions = DelayedDeletions(
routes.delete,
@ -414,6 +429,10 @@ Future<TrainPerson> _openPersonData(
final plans = PlanRepository(SyncPlanGateway(db, sync), keys);
final routes = RouteRepository(SyncRouteGateway(db, sync), keys);
final places = PlaceRepository(SyncPlaceGateway(db, sync), keys);
final strengthPlans = StrengthPlanRepository(
SyncStrengthPlanGateway(db, sync),
keys,
);
final maps = MapSourceRepository(
ServerpodMapSourceGateway(client),
kept: File(p.join(folder.path, 'map_source.json')),
@ -473,6 +492,10 @@ Future<TrainPerson> _openPersonData(
routes: routes,
places: places,
chosenPlace: ChosenPlaceStore(File(p.join(folder.path, 'place.json'))),
strengthPlans: strengthPlans,
strengthSession: StrengthSessionStore(
File(p.join(folder.path, 'strength_session.json')),
),
routing: ServerpodRoutingGateway(client),
placeSearch: ServerpodPlaceSearchGateway(client),
maps: maps,
@ -510,6 +533,7 @@ Future<TrainPerson> _openPersonData(
await plans.dispose();
await routes.dispose();
await places.dispose();
await strengthPlans.dispose();
await sync.dispose();
await db.close();
} finally {

View file

@ -1,9 +1,7 @@
import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:halkyon_app/halkyon_app.dart';
import 'package:halkyon_train_flutter/l10n/app_localizations.dart';
import 'package:halkyon_train_flutter/src/app/train_dependencies.dart';
import 'package:halkyon_train_flutter/src/feature/equipment/bloc/places_cubit.dart';
import 'package:halkyon_train_flutter/src/feature/equipment/model/equipment.dart';
import 'package:halkyon_train_flutter/src/feature/equipment/widget/place_screen.dart';
@ -14,14 +12,13 @@ import 'package:material_ui/material_ui.dart';
/// Opens *Equipment* over [context]'s, from Settings.
Future<void> openEquipmentScreen(BuildContext context) {
final train = PersonScope.of<TrainPerson>(context);
// The person's app's, which the plans' lists share, so that a place
// chosen here is chosen there.
final places = context.read<PlacesCubit>();
return Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (context) => BlocProvider(
create: (context) =>
PlacesCubit(train.places, train.chosenPlace)..open(),
child: const EquipmentScreen(),
),
builder: (context) =>
BlocProvider.value(value: places, child: const EquipmentScreen()),
),
);
}

View file

@ -194,8 +194,9 @@ class RecordingBloc(
Activity.walking ||
Activity.hiking => AutoPauseBy.steps,
Activity.indoorRowing => AutoPauseBy.strokes,
// Not recorded on the phone (D39): only the Loop brings it.
Activity.other => null,
// Not recorded on the phone (D39): only the Loop brings it; a
// strength session is the app's (ADR 0035).
Activity.other || Activity.strength => null,
};
final granted = await _platform.ask({
// The rower's connection, which also gives Android its service's type.

View file

@ -10,7 +10,7 @@ const _distancesSpelled = 100;
int? announceEvery(Activity activity) => switch (activity) {
Activity.running || Activity.walking || Activity.hiking => 1000,
Activity.cycling => 5000,
Activity.indoorRowing || Activity.other => null,
Activity.indoorRowing || Activity.other || Activity.strength => null,
};
/// The announcements' words for [activity] as the recorder takes them, in

Some files were not shown because too many files have changed in this diff Show more