feat(train): the instance names its map source (ADR 0030, D11, D22)

Train's first endpoint of its own, map.source, names the map workouts
are drawn on, for people signed in only, as a provider's key may be in
its style's address: OpenFreeMap's Liberty style where the operator sets
nothing, else MAP_STYLE_URL (HTTPS only) with MAP_SOURCE_NAME,
MAP_ATTRIBUTION and MAP_ABOUT_URL. A model without a table: no
migration. D22 is recorded in ADR 0030.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-28 13:42:28 +01:00
commit dd778cb2cd
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
3 changed files with 574 additions and 4 deletions

View file

@ -13,6 +13,8 @@
import 'dart:async' as _ida;
import 'package:halkyon_base_client/halkyon_base_client.dart' as _ihk4p5t7;
import 'package:halkyon_oidc_client/halkyon_oidc_client.dart' as _ibkpwzgl;
import 'package:halkyon_train_client/src/protocol/map/map_source.dart'
as _iebj1x2g;
import 'package:http/http.dart' as _i85jenna;
import 'package:serverpod_auth_core_client/serverpod_auth_core_client.dart'
as _iacc;
@ -26,6 +28,24 @@ import 'protocol.dart' as _il2as5qe;
import 'sync_tables.dart' as _ii3f3u05;
import 'package:halkyon_train_client/migrations/migration_registry.dart';
/// The map workouts are drawn on (ADR 0030, D11, D22), for the people signed
/// in: an operator's provider may have its key in the style's address.
/// {@category Endpoint}
class EndpointMap extends _isc.EndpointRef {
EndpointMap(_isc.EndpointCaller caller) : super(caller);
@override
String get name => 'map';
/// The instance's map source, as its operator sets it.
_ida.Future<_iebj1x2g.MapSource> source() =>
caller.callServerEndpoint<_iebj1x2g.MapSource>(
'map',
'source',
{},
);
}
class Modules {
Modules(Client client) {
base = _ihk4p5t7.Caller(client);
@ -73,13 +93,16 @@ class Client extends _isc.ServerpodClientShared {
disconnectStreamsOnLostInternetConnection,
httpClientOverride: httpClientOverride,
) {
map = EndpointMap(this);
modules = Modules(this);
}
late final EndpointMap map;
late final Modules modules;
@override
Map<String, _isc.EndpointRef> get endpointRefLookup => {};
Map<String, _isc.EndpointRef> get endpointRefLookup => {'map': map};
@override
Map<String, _isc.ModuleEndpointCaller> get moduleLookup => {

View file

@ -0,0 +1,127 @@
/* 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 'package:serverpod_client/serverpod_client.dart' as _isc;
/// The map this instance's workouts are drawn on (ADR 0030, D11, D22), as
/// its operator sets it: a MapLibre style, its source's name and the credit
/// it asks for, and where the source says what it sees. OpenFreeMap where
/// nothing is set.
abstract class MapSource
implements _isc.SerializableModel, _isc.ProtocolSerialization {
MapSource._({
required this.name,
required this.styleUrl,
required this.attribution,
this.aboutUrl,
});
factory MapSource({
required String name,
required String styleUrl,
required String attribution,
String? aboutUrl,
}) = _MapSourceImpl;
factory MapSource.fromJson(Map<String, dynamic> jsonSerialization) {
return MapSource(
name: jsonSerialization['name'] as String,
styleUrl: jsonSerialization['styleUrl'] as String,
attribution: jsonSerialization['attribution'] as String,
aboutUrl: jsonSerialization['aboutUrl'] as String?,
);
}
/// What the app names as drawing the map, as `OpenFreeMap`.
String name;
/// The MapLibre (Mapbox GL) style the vector tiles are drawn by; a
/// provider's key may be in it, so only people signed in get it.
String styleUrl;
/// The credit the map shows, as the source's terms ask.
String attribution;
/// Where the source says what it keeps of those viewing its maps; null
/// where the operator names nothing.
String? aboutUrl;
/// Returns a shallow copy of this [MapSource]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
MapSource copyWith({
String? name,
String? styleUrl,
String? attribution,
String? aboutUrl,
});
@override
Map<String, dynamic> toJson() {
return {
'__className__': 'MapSource',
'name': name,
'styleUrl': styleUrl,
'attribution': attribution,
if (aboutUrl != null) 'aboutUrl': aboutUrl,
};
}
@override
Map<String, dynamic> toJsonForProtocol() {
return {
'__className__': 'MapSource',
'name': name,
'styleUrl': styleUrl,
'attribution': attribution,
if (aboutUrl != null) 'aboutUrl': aboutUrl,
};
}
@override
String toString() {
return _isc.SerializationManager.encode(this);
}
}
class _Undefined {}
class _MapSourceImpl extends MapSource {
_MapSourceImpl({
required String name,
required String styleUrl,
required String attribution,
String? aboutUrl,
}) : super._(
name: name,
styleUrl: styleUrl,
attribution: attribution,
aboutUrl: aboutUrl,
);
/// Returns a shallow copy of this [MapSource]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
@override
MapSource copyWith({
String? name,
String? styleUrl,
String? attribution,
Object? aboutUrl = _Undefined,
}) {
return MapSource(
name: name ?? this.name,
styleUrl: styleUrl ?? this.styleUrl,
attribution: attribution ?? this.attribution,
aboutUrl: aboutUrl is String? ? aboutUrl : this.aboutUrl,
);
}
}

View file

@ -21,9 +21,11 @@ import 'package:serverpod_client/serverpod_client.dart' as _isc;
import 'package:serverpod_database/serverpod_database.dart' as _isd;
import 'package:serverpod_offline_sync_client/serverpod_offline_sync_client.dart'
as _ipulbpi2;
import 'map/map_source.dart' as _ikw0qg8x;
import 'privacy/content_key.dart' as _imed3x5d;
import 'workout/workout.dart' as _i5b9b17z;
import 'workout/workout_series.dart' as _i92mefto;
export 'map/map_source.dart';
export 'privacy/content_key.dart';
export 'workout/workout.dart';
export 'workout/workout_series.dart';
@ -312,6 +314,9 @@ class Protocol extends _isd.DatabaseSerializationManager {
}
}
if (t == _ikw0qg8x.MapSource) {
return _ikw0qg8x.MapSource.fromJson(data) as T;
}
if (t == _imed3x5d.ContentKey) {
return _imed3x5d.ContentKey.fromJson(data) as T;
}
@ -321,6 +326,9 @@ class Protocol extends _isd.DatabaseSerializationManager {
if (t == _i92mefto.WorkoutSeries) {
return _i92mefto.WorkoutSeries.fromJson(data) as T;
}
if (t == _isc.getType<_ikw0qg8x.MapSource?>()) {
return (data != null ? _ikw0qg8x.MapSource.fromJson(data) : null) as T;
}
if (t == _isc.getType<_imed3x5d.ContentKey?>()) {
return (data != null ? _imed3x5d.ContentKey.fromJson(data) : null) as T;
}
@ -351,6 +359,7 @@ class Protocol extends _isd.DatabaseSerializationManager {
static String? getClassNameForType(Type type) {
return switch (type) {
_ikw0qg8x.MapSource => 'MapSource',
_imed3x5d.ContentKey => 'ContentKey',
_i5b9b17z.Workout => 'Workout',
_i92mefto.WorkoutSeries => 'WorkoutSeries',
@ -371,6 +380,8 @@ class Protocol extends _isd.DatabaseSerializationManager {
}
switch (data) {
case _ikw0qg8x.MapSource():
return 'MapSource';
case _imed3x5d.ContentKey():
return 'ContentKey';
case _i5b9b17z.Workout():
@ -413,6 +424,9 @@ class Protocol extends _isd.DatabaseSerializationManager {
if (dataClassName is! String) {
return super.deserializeByClassName(data);
}
if (dataClassName == 'MapSource') {
return deserialize<_ikw0qg8x.MapSource>(data['data']);
}
if (dataClassName == 'ContentKey') {
return deserialize<_imed3x5d.ContentKey>(data['data']);
}