feat(train): routes kept as sealed rows, planned on the map (ADR 0030, D44)
Routes are synced `route` rows (the model `SavedRoute`, as Flutter has a `Route`), sealed as plans are; the migration `routes` only creates the table, its space index and its cascade to the sync engine's spaces. A route's payload holds its name, activity, profile, waypoints and the way planned, points and elevations delta-coded and gzipped. Home gets a fourth tab, Routes, and the planner: waypoints tapped on the map, held and dragged, the last undone, a new route's first where the phone is (GPS through the recorder's channel), the way planned by the instance's BRouter after each change, an earlier answer dropped; its distance, climb, descent, elevation and surfaces under the map. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
a8f7c26e69
commit
aea99c68ca
4 changed files with 24035 additions and 12 deletions
|
|
@ -32,6 +32,7 @@ import 'map/map_source.dart' as _ikw0qg8x;
|
|||
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 'routing/planned_route.dart' as _ikjgxraq;
|
||||
import 'routing/route_hint.dart' as _if6974nb;
|
||||
import 'routing/route_point.dart' as _isqtfus6;
|
||||
|
|
@ -49,6 +50,7 @@ export 'map/map_source.dart';
|
|||
export 'plan/plan.dart';
|
||||
export 'privacy/content_key.dart';
|
||||
export 'profile/profile.dart';
|
||||
export 'route/route.dart';
|
||||
export 'routing/planned_route.dart';
|
||||
export 'routing/route_hint.dart';
|
||||
export 'routing/route_point.dart';
|
||||
|
|
@ -493,6 +495,73 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
],
|
||||
managed: true,
|
||||
),
|
||||
_isp.TableDefinition(
|
||||
name: 'route',
|
||||
dartName: 'SavedRoute',
|
||||
schema: 'public',
|
||||
module: 'halkyon_train',
|
||||
columns: [
|
||||
_isp.ColumnDefinition(
|
||||
name: 'id',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue?',
|
||||
columnDefault: 'random_v7',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'spaceId',
|
||||
columnType: _isp.ColumnType.bigint,
|
||||
isNullable: true,
|
||||
dartType: 'int?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'keyId',
|
||||
columnType: _isp.ColumnType.text,
|
||||
isNullable: true,
|
||||
dartType: 'String?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'payload',
|
||||
columnType: _isp.ColumnType.bytea,
|
||||
isNullable: false,
|
||||
dartType: 'dart:typed_data:ByteData',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'nonce',
|
||||
columnType: _isp.ColumnType.bytea,
|
||||
isNullable: true,
|
||||
dartType: 'dart:typed_data:ByteData?',
|
||||
),
|
||||
],
|
||||
foreignKeys: [
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'route_fk_0',
|
||||
columns: ['spaceId'],
|
||||
referenceTable: 'offline_sync_spaces',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isp.ForeignKeyAction.noAction,
|
||||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
),
|
||||
],
|
||||
indexes: [
|
||||
_isp.IndexDefinition(
|
||||
indexName: 'route_space_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'spaceId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
],
|
||||
managed: true,
|
||||
),
|
||||
_isp.TableDefinition(
|
||||
name: 'workout',
|
||||
dartName: 'Workout',
|
||||
|
|
@ -719,6 +788,9 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (t == _ia5j8fch.Profile) {
|
||||
return _ia5j8fch.Profile.fromJson(data) as T;
|
||||
}
|
||||
if (t == _i0mgeh8t.SavedRoute) {
|
||||
return _i0mgeh8t.SavedRoute.fromJson(data) as T;
|
||||
}
|
||||
if (t == _ikjgxraq.PlannedRoute) {
|
||||
return _ikjgxraq.PlannedRoute.fromJson(data) as T;
|
||||
}
|
||||
|
|
@ -773,6 +845,9 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (t == _is.getType<_ia5j8fch.Profile?>()) {
|
||||
return (data != null ? _ia5j8fch.Profile.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _is.getType<_i0mgeh8t.SavedRoute?>()) {
|
||||
return (data != null ? _i0mgeh8t.SavedRoute.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _is.getType<_ikjgxraq.PlannedRoute?>()) {
|
||||
return (data != null ? _ikjgxraq.PlannedRoute.fromJson(data) : null) as T;
|
||||
}
|
||||
|
|
@ -857,6 +932,7 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
_i45prw73.Plan => 'Plan',
|
||||
_imed3x5d.ContentKey => 'ContentKey',
|
||||
_ia5j8fch.Profile => 'Profile',
|
||||
_i0mgeh8t.SavedRoute => 'SavedRoute',
|
||||
_ikjgxraq.PlannedRoute => 'PlannedRoute',
|
||||
_if6974nb.RouteHint => 'RouteHint',
|
||||
_isqtfus6.RoutePoint => 'RoutePoint',
|
||||
|
|
@ -900,6 +976,8 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
return 'ContentKey';
|
||||
case _ia5j8fch.Profile():
|
||||
return 'Profile';
|
||||
case _i0mgeh8t.SavedRoute():
|
||||
return 'SavedRoute';
|
||||
case _ikjgxraq.PlannedRoute():
|
||||
return 'PlannedRoute';
|
||||
case _if6974nb.RouteHint():
|
||||
|
|
@ -983,6 +1061,9 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (dataClassName == 'Profile') {
|
||||
return deserialize<_ia5j8fch.Profile>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'SavedRoute') {
|
||||
return deserialize<_i0mgeh8t.SavedRoute>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'PlannedRoute') {
|
||||
return deserialize<_ikjgxraq.PlannedRoute>(data['data']);
|
||||
}
|
||||
|
|
@ -1093,6 +1174,8 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
return _imed3x5d.ContentKey.t;
|
||||
case _ia5j8fch.Profile:
|
||||
return _ia5j8fch.Profile.t;
|
||||
case _i0mgeh8t.SavedRoute:
|
||||
return _i0mgeh8t.SavedRoute.t;
|
||||
case _i5b9b17z.Workout:
|
||||
return _i5b9b17z.Workout.t;
|
||||
case _i92mefto.WorkoutSeries:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,667 @@
|
|||
/* 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/serverpod.dart' as _is;
|
||||
|
||||
/// A planned route (ADR 0030, D44): its name, activity, profile, waypoints
|
||||
/// and the route BRouter planned through them, in its sealed payload (D7);
|
||||
/// the instance sees that it exists, its size and when it changed. Changed
|
||||
/// when edited, the last writer winning. Synced.
|
||||
abstract class SavedRoute
|
||||
implements _is.TableRow<_is.UuidValue?>, _is.ProtocolSerialization {
|
||||
SavedRoute._({
|
||||
this.id,
|
||||
this.spaceId,
|
||||
this.keyId,
|
||||
required this.payload,
|
||||
this.nonce,
|
||||
});
|
||||
|
||||
factory SavedRoute({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
String? keyId,
|
||||
required _idt.ByteData payload,
|
||||
_idt.ByteData? nonce,
|
||||
}) = _SavedRouteImpl;
|
||||
|
||||
factory SavedRoute.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return SavedRoute(
|
||||
id: jsonSerialization['id'] == null
|
||||
? null
|
||||
: _is.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
|
||||
spaceId: jsonSerialization['spaceId'] as int?,
|
||||
keyId: jsonSerialization['keyId'] as String?,
|
||||
payload: _is.ByteDataJsonExtension.fromJson(jsonSerialization['payload']),
|
||||
nonce: jsonSerialization['nonce'] == null
|
||||
? null
|
||||
: _is.ByteDataJsonExtension.fromJson(jsonSerialization['nonce']),
|
||||
);
|
||||
}
|
||||
|
||||
static final t = SavedRouteTable();
|
||||
|
||||
static const db = SavedRouteRepository._();
|
||||
|
||||
@override
|
||||
_is.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
|
||||
_is.Table<_is.UuidValue?> get table => t;
|
||||
|
||||
/// Returns a shallow copy of this [SavedRoute]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
SavedRoute copyWith({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
String? keyId,
|
||||
_idt.ByteData? payload,
|
||||
_idt.ByteData? nonce,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'SavedRoute',
|
||||
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__': 'SavedRoute',
|
||||
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 SavedRouteInclude include() {
|
||||
return SavedRouteInclude._();
|
||||
}
|
||||
|
||||
static SavedRouteIncludeList includeList({
|
||||
_is.WhereExpressionBuilder<SavedRouteTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<SavedRouteTable>? orderBy,
|
||||
_is.OrderByListBuilder<SavedRouteTable>? orderByList,
|
||||
SavedRouteInclude? include,
|
||||
}) {
|
||||
return SavedRouteIncludeList._(
|
||||
where: where,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(SavedRoute.t),
|
||||
orderByList: orderByList?.call(SavedRoute.t),
|
||||
include: include,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _SavedRouteImpl extends SavedRoute {
|
||||
_SavedRouteImpl({
|
||||
_is.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 [SavedRoute]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
SavedRoute copyWith({
|
||||
Object? id = _Undefined,
|
||||
Object? spaceId = _Undefined,
|
||||
Object? keyId = _Undefined,
|
||||
_idt.ByteData? payload,
|
||||
Object? nonce = _Undefined,
|
||||
}) {
|
||||
return SavedRoute(
|
||||
id: id is _is.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 SavedRouteUpdateTable extends _is.UpdateTable<SavedRouteTable> {
|
||||
SavedRouteUpdateTable(super.table);
|
||||
|
||||
_is.ColumnValue<int, int> spaceId(int? value) => _is.ColumnValue(
|
||||
table.spaceId,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> keyId(String? value) => _is.ColumnValue(
|
||||
table.keyId,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<_idt.ByteData, _idt.ByteData> payload(_idt.ByteData value) =>
|
||||
_is.ColumnValue(
|
||||
table.payload,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<_idt.ByteData, _idt.ByteData> nonce(_idt.ByteData? value) =>
|
||||
_is.ColumnValue(
|
||||
table.nonce,
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
class SavedRouteTable extends _is.Table<_is.UuidValue?> {
|
||||
SavedRouteTable({super.tableRelation}) : super(tableName: 'route') {
|
||||
updateTable = SavedRouteUpdateTable(this);
|
||||
spaceId = _is.ColumnInt(
|
||||
'spaceId',
|
||||
this,
|
||||
);
|
||||
keyId = _is.ColumnString(
|
||||
'keyId',
|
||||
this,
|
||||
);
|
||||
payload = _is.ColumnByteData(
|
||||
'payload',
|
||||
this,
|
||||
);
|
||||
nonce = _is.ColumnByteData(
|
||||
'nonce',
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
late final SavedRouteUpdateTable updateTable;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
late final _is.ColumnInt spaceId;
|
||||
|
||||
/// The key it is sealed with (ADR 0002's envelope); never null in Train.
|
||||
late final _is.ColumnString keyId;
|
||||
|
||||
/// The content, sealed: UTF-8 JSON with a `schemaVersion` once opened.
|
||||
late final _is.ColumnByteData payload;
|
||||
|
||||
/// The sealing's nonce (ADR 0002's envelope).
|
||||
late final _is.ColumnByteData nonce;
|
||||
|
||||
@override
|
||||
List<_is.Column> get columns => [
|
||||
id,
|
||||
spaceId,
|
||||
keyId,
|
||||
payload,
|
||||
nonce,
|
||||
];
|
||||
}
|
||||
|
||||
class SavedRouteInclude extends _is.IncludeObject {
|
||||
SavedRouteInclude._();
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => {};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => SavedRoute.t;
|
||||
}
|
||||
|
||||
class SavedRouteIncludeList extends _is.IncludeList {
|
||||
SavedRouteIncludeList._({
|
||||
_is.WhereExpressionBuilder<SavedRouteTable>? where,
|
||||
super.limit,
|
||||
super.offset,
|
||||
super.orderBy,
|
||||
super.orderByList,
|
||||
super.include,
|
||||
}) {
|
||||
super.where = where?.call(SavedRoute.t);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => include?.includes ?? {};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => SavedRoute.t;
|
||||
}
|
||||
|
||||
class SavedRouteRepository {
|
||||
const SavedRouteRepository._();
|
||||
|
||||
/// Returns a list of [SavedRoute]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<SavedRoute>> find(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<SavedRouteTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<SavedRouteTable>? orderBy,
|
||||
_is.OrderByListBuilder<SavedRouteTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.find<SavedRoute>(
|
||||
where: where?.call(SavedRoute.t),
|
||||
orderBy: orderBy?.call(SavedRoute.t),
|
||||
orderByList: orderByList?.call(SavedRoute.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the first matching [SavedRoute] 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<SavedRoute?> findFirstRow(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<SavedRouteTable>? where,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<SavedRouteTable>? orderBy,
|
||||
_is.OrderByListBuilder<SavedRouteTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findFirstRow<SavedRoute>(
|
||||
where: where?.call(SavedRoute.t),
|
||||
orderBy: orderBy?.call(SavedRoute.t),
|
||||
orderByList: orderByList?.call(SavedRoute.t),
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Finds a single [SavedRoute] by its [id] or null if no such row exists.
|
||||
Future<SavedRoute?> findById(
|
||||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findById<SavedRoute>(
|
||||
id,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts all [SavedRoute]s in the list and returns the inserted rows.
|
||||
///
|
||||
/// The returned [SavedRoute]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<SavedRoute>> insert(
|
||||
_is.DatabaseSession session,
|
||||
List<SavedRoute> rows, {
|
||||
_is.Transaction? transaction,
|
||||
bool ignoreConflicts = false,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.insert<SavedRoute>(
|
||||
rows,
|
||||
transaction: transaction,
|
||||
ignoreConflicts: ignoreConflicts,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts a single [SavedRoute] and returns the inserted row.
|
||||
///
|
||||
/// The returned [SavedRoute] will have its `id` field set.
|
||||
Future<SavedRoute> insertRow(
|
||||
_is.DatabaseSession session,
|
||||
SavedRoute row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.insertRow<SavedRoute>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts all [SavedRoute]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 [SavedRoute]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<SavedRoute>> upsert(
|
||||
_is.DatabaseSession session,
|
||||
List<SavedRoute> rows, {
|
||||
required _is.ColumnSelections<SavedRouteTable> conflictColumns,
|
||||
_is.ColumnSelections<SavedRouteTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<SavedRouteTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.upsert<SavedRoute>(
|
||||
rows,
|
||||
conflictColumns: conflictColumns(SavedRoute.t),
|
||||
updateColumns: updateColumns?.call(SavedRoute.t),
|
||||
updateWhere: updateWhere?.call(SavedRoute.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts a single [SavedRoute] 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 [SavedRoute] will have its `id` field set.
|
||||
Future<SavedRoute?> upsertRow(
|
||||
_is.DatabaseSession session,
|
||||
SavedRoute row, {
|
||||
required _is.ColumnSelections<SavedRouteTable> conflictColumns,
|
||||
_is.ColumnSelections<SavedRouteTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<SavedRouteTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.upsertRow<SavedRoute>(
|
||||
row,
|
||||
conflictColumns: conflictColumns(SavedRoute.t),
|
||||
updateColumns: updateColumns?.call(SavedRoute.t),
|
||||
updateWhere: updateWhere?.call(SavedRoute.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [SavedRoute]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<SavedRoute>> update(
|
||||
_is.DatabaseSession session,
|
||||
List<SavedRoute> rows, {
|
||||
_is.ColumnSelections<SavedRouteTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.update<SavedRoute>(
|
||||
rows,
|
||||
columns: columns?.call(SavedRoute.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [SavedRoute]. 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<SavedRoute> updateRow(
|
||||
_is.DatabaseSession session,
|
||||
SavedRoute row, {
|
||||
_is.ColumnSelections<SavedRouteTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateRow<SavedRoute>(
|
||||
row,
|
||||
columns: columns?.call(SavedRoute.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [SavedRoute] by its [id] with the specified [columnValues].
|
||||
/// Returns the updated row or null if no row with the given id exists.
|
||||
Future<SavedRoute?> updateById(
|
||||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
required _is.ColumnValueListBuilder<SavedRouteUpdateTable> columnValues,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateById<SavedRoute>(
|
||||
id,
|
||||
columnValues: columnValues(SavedRoute.t.updateTable),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [SavedRoute]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<SavedRoute>> updateWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.ColumnValueListBuilder<SavedRouteUpdateTable> columnValues,
|
||||
required _is.WhereExpressionBuilder<SavedRouteTable> where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<SavedRouteTable>? orderBy,
|
||||
_is.OrderByListBuilder<SavedRouteTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.updateWhere<SavedRoute>(
|
||||
columnValues: columnValues(SavedRoute.t.updateTable),
|
||||
where: where(SavedRoute.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(SavedRoute.t),
|
||||
orderByList: orderByList?.call(SavedRoute.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all [SavedRoute]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<SavedRoute>> delete(
|
||||
_is.DatabaseSession session,
|
||||
List<SavedRoute> rows, {
|
||||
_is.OrderByBuilder<SavedRouteTable>? orderBy,
|
||||
_is.OrderByListBuilder<SavedRouteTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.delete<SavedRoute>(
|
||||
rows,
|
||||
orderBy: orderBy?.call(SavedRoute.t),
|
||||
orderByList: orderByList?.call(SavedRoute.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes a single [SavedRoute].
|
||||
Future<SavedRoute> deleteRow(
|
||||
_is.DatabaseSession session,
|
||||
SavedRoute row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.deleteRow<SavedRoute>(
|
||||
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<SavedRoute>> deleteWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<SavedRouteTable> where,
|
||||
_is.OrderByBuilder<SavedRouteTable>? orderBy,
|
||||
_is.OrderByListBuilder<SavedRouteTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.deleteWhere<SavedRoute>(
|
||||
where: where(SavedRoute.t),
|
||||
orderBy: orderBy?.call(SavedRoute.t),
|
||||
orderByList: orderByList?.call(SavedRoute.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(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<SavedRouteTable>? where,
|
||||
int? limit,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.count<SavedRoute>(
|
||||
where: where?.call(SavedRoute.t),
|
||||
limit: limit,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Acquires row-level locks on [SavedRoute] rows matching the [where] expression.
|
||||
Future<void> lockRows(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<SavedRouteTable> where,
|
||||
required _is.LockMode lockMode,
|
||||
required _is.Transaction transaction,
|
||||
_is.LockBehavior lockBehavior = _is.LockBehavior.wait,
|
||||
}) async {
|
||||
return session.db.lockRows<SavedRoute>(
|
||||
where: where(SavedRoute.t),
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ import 'day/night.dart' as _i17cgxdf;
|
|||
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 'workout/workout.dart' as _i5b9b17z;
|
||||
import 'workout/workout_series.dart' as _i92mefto;
|
||||
|
||||
|
|
@ -30,6 +31,7 @@ final List<_is.Table> syncTables = [
|
|||
_i17cgxdf.Night.t,
|
||||
_i45prw73.Plan.t,
|
||||
_ia5j8fch.Profile.t,
|
||||
_i0mgeh8t.SavedRoute.t,
|
||||
_i5b9b17z.Workout.t,
|
||||
_i92mefto.WorkoutSeries.t,
|
||||
];
|
||||
|
|
|
|||
18
apps/train/halkyon_train_server/lib/src/route/route.spy.yaml
Normal file
18
apps/train/halkyon_train_server/lib/src/route/route.spy.yaml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
### A planned route (ADR 0030, D44): its name, activity, profile, waypoints
|
||||
### and the route BRouter planned through them, in its sealed payload (D7);
|
||||
### the instance sees that it exists, its size and when it changed. Changed
|
||||
### when edited, the last writer winning. Synced.
|
||||
class: SavedRoute
|
||||
table: route
|
||||
database: sync
|
||||
fields:
|
||||
### The key it is sealed with (ADR 0002's envelope); never null in Train.
|
||||
keyId: String?
|
||||
### The content, sealed: UTF-8 JSON with a `schemaVersion` once opened.
|
||||
payload: ByteData
|
||||
### The sealing's nonce (ADR 0002's envelope).
|
||||
nonce: ByteData?
|
||||
indexes:
|
||||
# Deleting a space finds its rows through this index (see content_key).
|
||||
route_space_idx:
|
||||
fields: spaceId
|
||||
Loading…
Reference in a new issue