This repository has been archived on 2026-10-23. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
halkyon_shared/packages/halkyon_app
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Tizian 「ティツィアーン」 e23cc77f7c
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>
2026-10-19 15:31:40 +02:00
..
assets/flags halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps 2026-10-19 15:31:40 +02:00
lib halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps 2026-10-19 15:31:40 +02:00
test halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps 2026-10-19 15:31:40 +02:00
.gitignore halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps 2026-10-19 15:31:40 +02:00
analysis_options.yaml halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps 2026-10-19 15:31:40 +02:00
CHANGELOG.md halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps 2026-10-19 15:31:40 +02:00
l10n.yaml halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps 2026-10-19 15:31:40 +02:00
LICENSE halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps 2026-10-19 15:31:40 +02:00
pubspec.yaml halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps 2026-10-19 15:31:40 +02:00
README.md halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps 2026-10-19 15:31:40 +02:00

halkyon_app

What every Halkyon app (Learn, Todo, Train) is built around, on an instance's halkyon_base module: the instances a device keeps an account on, signing in, each person's data opened and closed, and syncing it. Taken from Halkyon Learn, which it was written for, so it behaves as Learn's own did; the texts are in British English, American English and German, and where they name the app, they name yours.

Written as the apps are: Dart 3.13's primary constructors, Material from package:material_ui, Lucide icons, blocs (flutter_bloc), LeanCode's lints.

What it holds

  • Instances (lib/src/instance/): Instance (an address as people type it), InstanceChoice (those the device keeps an account on, one in use; FileInstanceStore), InstanceStorage (each instance's people's folders and session key), InstanceChecker (does an address answer, where does it keep people's data; ServerpodInstanceChecker asks its instance.basics()), InstanceScope. The screens: FirstStartScreen (the build's instance and another, as tiles, InstanceTile), InstanceStep (an address, checked), showAccountSwitcher and AddAccountScreen, and where an instance keeps people's data, as a flag and words (InstanceLocationBuilder, InstanceFlag, instanceLocationWords, countries named by the app's CountryNames).
  • Gates (lib/src/app/): InstanceGate opens the app's dependencies on the instance in use (AppDependencies, handed down by AppScope), anew on a switch, the previous ones closed first; SessionGate keeps a person signed in, signing in anonymously at a first start and asking how to start once signed out (StartScreen); PersonScope opens each person's own dependencies and closes them when the person goes.
  • Signing in (lib/src/auth/): AuthRepository on an AuthGateway; ServerpodAuthGateway signs in anonymously, with GitHub (the system browser and a loopback address on Linux and Windows, LoopbackWebAuth) and with a code by mail, through the instance's halkyon_base; SessionBloc, GitHubSignInBloc, EmailSignInBloc (with showEmailSignInSheet) and SignInWaysCubit; SignedOutMark, SignInAddresses, PersonFolders.
  • Sync (lib/src/sync/): SyncService (once at start, shortly after local changes, on resume and every few minutes, one sync at a time, counting the changes not on the server yet, PendingChangesStore), AppCompatibility and compatibleSync (the instance's minimum app build, the engine's table check), SyncStatusBloc, syncAndWait, and the widgets: SyncStatusCard, AppMismatchBanner, SyncTroubleBadge.
  • A client of halkyon_base alone (BaseClient): to ask an instance about itself, and to sign in as another person than the app's client holds.
  • Testing (package:halkyon_app/testing.dart): fakes of every boundary (FakeAuthGateway, FakeInstanceChecker, the stores in memory), a frame (testFrame), testApp, and TestAppDependencies.

Using it in an app

  1. The server includes halkyon_base with the nickname base (its README), so the app's generated client has client.modules.base.

  2. Depend on it by git, pinned to a signed commit of halkyon_shared's main, as halkyon_ui (whose README shows the pubspec_overrides.yaml for working on both at once):

    dependencies:
      halkyon_app:
        git:
          url: https://code.halkyon.app/project-halkyon/halkyon_shared.git
          path: packages/halkyon_app
          ref: <signed commit>
    
  3. Texts: add HalkyonAppLocalizations.delegate to every MaterialApp's delegates, beside HalkyonUiLocalizations.delegate and material_ui's GlobalMaterialLocalizations.delegates.

  4. The frame (AppFrame): the app's name and wordmark word, its icon, an example address, its theme, delegates, languages and launch screen, and the two sentences only it can say: what the first start's instance keeps of the person's, and what a switch to another account leaves behind.

  5. The dependencies: the app's composition root implements AppDependencies<P> (serverName, auth, signedOut, openPerson, dispose), its person's dependencies Disposable. Its auth is an AuthRepository on ServerpodAuthGateway(client, base: client.modules.base, callbackScheme: …, appName: …); the scheme is the app's own (app.halkyon.learn), registered with each platform and in the instance's GitHub OAuth app, whose loopback address on Linux and Windows is http://127.0.0.1/github-callback (port 43823 unless given).

  6. The tree:

    InstanceGate(
      frame: frame,
      choice: choice,
      checker: ServerpodInstanceChecker(app: 'todo'),
      countryNames: …,
      readLook: …,           // the device's theme mode and language
      open: Dependencies.create,
      app: SessionGate(
        look: (context) => …,
        app: (person) => PersonApp(person: person), // with PersonScope inside
      ),
    )
    

    A person's widgets find their dependencies with PersonScope.of<PersonDependencies>(context) and the app's with AppScope.of<Dependencies>(context).

  7. Licences: call registerFlagIconsLicense() once at startup, beside halkyon_ui's registerFontLicenses().

Licence

GNU Affero General Public License 3.0 or later (LICENSE). The flags are lipis' flag-icons, drawn by the flag package, under the MIT licence (assets/flags/LICENSE-flag-icons.txt).