halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps

What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.

An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.

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-19 15:18:49 +02:00
commit e23cc77f7c
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
2 changed files with 10761 additions and 10 deletions

View file

@ -197,7 +197,7 @@ class EndpointInstance extends _isc.EndpointRef {
@override
String get name => 'halkyon_base.instance';
/// This instance's basics: GitHub sign-in only with the operator's OAuth
/// This instance's basics: the app it serves, GitHub sign-in only with the operator's OAuth
/// app, email sign-in only where it can mail codes, where its source is,
/// the oldest app build it works with, and where it keeps people's data
/// and its backups.

View file

@ -13,12 +13,13 @@
import 'package:serverpod_client/serverpod_client.dart' as _isc;
/// What every Halkyon instance tells its apps, to anyone, before anyone signs
/// in: how people sign in here, where the source of what it runs is, the
/// in: which app it serves, how people sign in here, where the source of what it runs is, the
/// oldest app build it works with, and where it keeps people's data. Every
/// instance is run by someone else, with its own configuration.
abstract class InstanceBasics
implements _isc.SerializableModel, _isc.ProtocolSerialization {
InstanceBasics._({
required this.app,
this.gitHubClientId,
required this.emailSignIn,
required this.sourceCodeUrl,
@ -28,6 +29,7 @@ abstract class InstanceBasics
});
factory InstanceBasics({
required String app,
String? gitHubClientId,
required bool emailSignIn,
required String sourceCodeUrl,
@ -38,6 +40,7 @@ abstract class InstanceBasics
factory InstanceBasics.fromJson(Map<String, dynamic> jsonSerialization) {
return InstanceBasics(
app: jsonSerialization['app'] as String,
gitHubClientId: jsonSerialization['gitHubClientId'] as String?,
emailSignIn: _isc.BoolJsonExtension.fromJson(
jsonSerialization['emailSignIn'],
@ -49,6 +52,10 @@ abstract class InstanceBasics
);
}
/// Which app this instance serves (`learn` for Halkyon Learn), as its host
/// configures it: an app accepts only an instance of its own.
String app;
/// The client ID of this instance's GitHub OAuth app, or null when GitHub
/// sign-in is off here.
String? gitHubClientId;
@ -79,6 +86,7 @@ abstract class InstanceBasics
/// with some or all fields replaced by the given arguments.
@_isc.useResult
InstanceBasics copyWith({
String? app,
String? gitHubClientId,
bool? emailSignIn,
String? sourceCodeUrl,
@ -90,6 +98,7 @@ abstract class InstanceBasics
Map<String, dynamic> toJson() {
return {
'__className__': 'halkyon_base.InstanceBasics',
'app': app,
if (gitHubClientId != null) 'gitHubClientId': gitHubClientId,
'emailSignIn': emailSignIn,
'sourceCodeUrl': sourceCodeUrl,
@ -103,6 +112,7 @@ abstract class InstanceBasics
Map<String, dynamic> toJsonForProtocol() {
return {
'__className__': 'halkyon_base.InstanceBasics',
'app': app,
if (gitHubClientId != null) 'gitHubClientId': gitHubClientId,
'emailSignIn': emailSignIn,
'sourceCodeUrl': sourceCodeUrl,
@ -122,6 +132,7 @@ class _Undefined {}
class _InstanceBasicsImpl extends InstanceBasics {
_InstanceBasicsImpl({
required String app,
String? gitHubClientId,
required bool emailSignIn,
required String sourceCodeUrl,
@ -129,6 +140,7 @@ class _InstanceBasicsImpl extends InstanceBasics {
String? dataLocation,
String? backupLocation,
}) : super._(
app: app,
gitHubClientId: gitHubClientId,
emailSignIn: emailSignIn,
sourceCodeUrl: sourceCodeUrl,
@ -142,6 +154,7 @@ class _InstanceBasicsImpl extends InstanceBasics {
@_isc.useResult
@override
InstanceBasics copyWith({
String? app,
Object? gitHubClientId = _Undefined,
bool? emailSignIn,
String? sourceCodeUrl,
@ -150,6 +163,7 @@ class _InstanceBasicsImpl extends InstanceBasics {
Object? backupLocation = _Undefined,
}) {
return InstanceBasics(
app: app ?? this.app,
gitHubClientId: gitHubClientId is String?
? gitHubClientId
: this.gitHubClientId,