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:18:49 +02:00
import ' dart:async ' ;
import ' package:flutter/foundation.dart ' ;
import ' package:flutter/widgets.dart ' ;
import ' package:flutter_localizations/flutter_localizations.dart ' ;
import ' package:intl/intl.dart ' as intl ;
import ' halkyon_app_localizations_de.dart ' ;
import ' halkyon_app_localizations_en.dart ' ;
// ignore_for_file: type=lint
/// Callers can lookup localized strings with an instance of HalkyonAppLocalizations
/// returned by `HalkyonAppLocalizations.of(context)`.
///
/// Applications need to include `HalkyonAppLocalizations.delegate()` in their app's
/// `localizationDelegates` list, and the locales they support in the app's
/// `supportedLocales` list. For example:
///
/// ```dart
/// import 'l10n/halkyon_app_localizations.dart';
///
/// return MaterialApp(
/// localizationsDelegates: HalkyonAppLocalizations.localizationsDelegates,
/// supportedLocales: HalkyonAppLocalizations.supportedLocales,
/// home: MyApplicationHome(),
/// );
/// ```
///
/// ## Update pubspec.yaml
///
/// Please make sure to update your pubspec.yaml to include the following
/// packages:
///
/// ```yaml
/// dependencies:
/// # Internationalization support.
/// flutter_localizations:
/// sdk: flutter
/// intl: any # Use the pinned version from flutter_localizations
///
/// # Rest of dependencies
/// ```
///
/// ## iOS Applications
///
/// iOS applications define key application metadata, including supported
/// locales, in an Info.plist file that is built into the application bundle.
/// To configure the locales supported by your app, you’ ll need to edit this
/// file.
///
/// First, open your project’ s ios/Runner.xcworkspace Xcode workspace file.
/// Then, in the Project Navigator, open the Info.plist file under the Runner
/// project’ s Runner folder.
///
/// Next, select the Information Property List item, select Add Item from the
/// Editor menu, then select Localizations from the pop-up menu.
///
/// Select and expand the newly-created Localizations item then, for each
/// locale your application supports, add a new item and select the locale
/// you wish to add from the pop-up menu in the Value field. This list should
/// be consistent with the languages listed in the HalkyonAppLocalizations.supportedLocales
/// property.
abstract class HalkyonAppLocalizations {
HalkyonAppLocalizations ( String locale )
: localeName = intl . Intl . canonicalizedLocale ( locale . toString ( ) ) ;
final String localeName ;
static HalkyonAppLocalizations of ( BuildContext context ) {
return Localizations . of < HalkyonAppLocalizations > (
context ,
HalkyonAppLocalizations ,
) ! ;
}
static const LocalizationsDelegate < HalkyonAppLocalizations > delegate =
_HalkyonAppLocalizationsDelegate ( ) ;
/// A list of this localizations delegate along with the default localizations
/// delegates.
///
/// Returns a list of localizations delegates containing this delegate along with
/// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
/// and GlobalWidgetsLocalizations.delegate.
///
/// Additional delegates can be added by appending to this list in
/// MaterialApp. This list does not have to be used at all if a custom list
/// of delegates is preferred or required.
static const List < LocalizationsDelegate < dynamic > > localizationsDelegates =
< LocalizationsDelegate < dynamic > > [
delegate ,
GlobalMaterialLocalizations . delegate ,
GlobalCupertinoLocalizations . delegate ,
GlobalWidgetsLocalizations . delegate ,
] ;
/// A list of this localizations delegate's supported locales.
static const List < Locale > supportedLocales = < Locale > [
Locale ( ' de ' ) ,
Locale ( ' en ' ) ,
Locale ( ' en ' , ' GB ' ) ,
Locale ( ' en ' , ' US ' ) ,
] ;
/// No description provided for @tryAgain.
///
/// In en, this message translates to:
/// **'Try again'**
String get tryAgain ;
/// Screen readers hear it for the wordmark on the first start.
///
/// In en, this message translates to:
/// **'Welcome to {app}'**
String instanceWelcome ( String app ) ;
/// No description provided for @instanceBuiltInTagline.
///
/// In en, this message translates to:
/// **'Comes with the app'**
String get instanceBuiltInTagline ;
/// No description provided for @instanceAnother.
///
/// In en, this message translates to:
/// **'Another instance'**
String get instanceAnother ;
/// No description provided for @instanceAnotherTagline.
///
/// In en, this message translates to:
/// **'Enter its address'**
String get instanceAnotherTagline ;
/// No description provided for @instanceAddress.
///
/// In en, this message translates to:
/// **'Instance address'**
String get instanceAddress ;
/// No description provided for @instanceAddressInvalid.
///
/// In en, this message translates to:
/// **'This isn\'t an address.'**
String get instanceAddressInvalid ;
fix(app)!: instances over plain HTTP are refused, but on this machine
What the app sends an instance (the proofs that sign in, the keys'
wrappings, every request a session carries) must not be read or changed on
the way, so an instance is reached over HTTPS, or over plain HTTP only on
this machine (localhost, 127.0.0.0/8, ::1), as Jellyfin servers are. An
address over plain HTTP elsewhere is refused before anything is sent there.
One kept from before is never opened: the app says why, and offers another
instance or removing it from the device, never dropping it silently.
Development builds still use their own instance over HTTP (an emulator
reaches the developer's machine as 10.0.2.2); releases never.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-21 14:35:21 +02:00
/// No description provided for @instanceAddressInsecure.
///
/// In en, this message translates to:
/// **'{app} reaches instances over HTTPS only, so that nobody on the way reads or changes what it sends. Ask for the instance\'s HTTPS address.'**
String instanceAddressInsecure ( String app ) ;
/// No description provided for @instanceInsecure.
///
/// In en, this message translates to:
/// **'{name} is reached over plain HTTP, where anyone on the way could read and change what {app} sends, so {app} no longer opens it. Its account stays on {name} and on this device. Use another instance, or remove it from this device.'**
String instanceInsecure ( String name , String app ) ;
/// No description provided for @instanceInsecureUseAnother.
///
/// In en, this message translates to:
/// **'Use another instance'**
String get instanceInsecureUseAnother ;
/// No description provided for @instanceInsecureRemove.
///
/// In en, this message translates to:
/// **'Remove from this device'**
String get instanceInsecureRemove ;
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:18:49 +02:00
/// No description provided for @instanceAddressRequired.
///
/// In en, this message translates to:
/// **'Enter the address of an instance.'**
String get instanceAddressRequired ;
/// No description provided for @instanceContinue.
///
/// In en, this message translates to:
/// **'Continue'**
String get instanceContinue ;
/// No description provided for @instanceUnreachable.
///
/// In en, this message translates to:
/// **'No instance of {app} answers at {name}.'**
String instanceUnreachable ( String app , String name ) ;
/// No description provided for @instanceUseBuiltIn.
///
/// In en, this message translates to:
/// **'Use {name}'**
String instanceUseBuiltIn ( String name ) ;
/// No description provided for @instanceOpenFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t open the app on {name}.'**
String instanceOpenFailed ( String name ) ;
/// No description provided for @instanceDataAndBackupsIn.
///
/// In en, this message translates to:
/// **'Data and backups: {place}'**
String instanceDataAndBackupsIn ( String place ) ;
/// No description provided for @instanceDataIn.
///
/// In en, this message translates to:
/// **'Data: {place}'**
String instanceDataIn ( String place ) ;
/// No description provided for @instanceBackupsIn.
///
/// In en, this message translates to:
/// **'Backups: {place}'**
String instanceBackupsIn ( String place ) ;
/// No description provided for @instanceNoBackups.
///
/// In en, this message translates to:
/// **'No backups'**
String get instanceNoBackups ;
/// No description provided for @instanceEuropeanUnion.
///
/// In en, this message translates to:
/// **'European Union'**
String get instanceEuropeanUnion ;
halkyon_app: data locations as precise as a country or a state
An operator's DATA_LOCATION and BACKUP_LOCATION may name a subdivision
(ISO 3166-2, US-CA, DE-BY) besides the EU or a country. The app adds
"(EU)" to a member country and the country to a subdivision ("Germany
(EU)", "California, United States", "Bavaria, Germany (EU)"), in
English and German, and shows the most specific flag there is: a
subdivision's own where the flag package has one, else its country's.
Subdivision names come from Unicode CLDR 48.2 (tool/subdivision_names.py),
bundled with their licence, which the app registers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 18:47:25 +02:00
/// A country of the European Union, where an instance keeps data: Germany (EU).
///
/// In en, this message translates to:
/// **'{country} (EU)'**
String instanceCountryInEu ( String country ) ;
/// A state or region with its country, where an instance keeps data: California, United States.
///
/// In en, this message translates to:
/// **'{subdivision}, {country}'**
String instanceSubdivisionOf ( String subdivision , String country ) ;
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:18:49 +02:00
/// No description provided for @accountsTitle.
///
/// In en, this message translates to:
/// **'Accounts'**
String get accountsTitle ;
/// No description provided for @accountAdd.
///
/// In en, this message translates to:
/// **'Add account'**
String get accountAdd ;
/// No description provided for @accountAddSubtitle.
///
/// In en, this message translates to:
/// **'Choose the instance for the account to add.'**
String get accountAddSubtitle ;
/// No description provided for @accountSignedIn.
///
/// In en, this message translates to:
/// **'Signed in'**
String get accountSignedIn ;
/// No description provided for @accountRemoveTooltip.
///
/// In en, this message translates to:
/// **'Remove account'**
String get accountRemoveTooltip ;
/// No description provided for @accountRemoveTitle.
///
/// In en, this message translates to:
/// **'Remove account?'**
String get accountRemoveTitle ;
/// No description provided for @accountRemoveMessage.
///
/// In en, this message translates to:
feat(app)!: no anonymous accounts on the device (ADR 0020, D4, D25)
Every account has a way in, so the apps make no anonymous account any
more. The start screen shows whenever nobody is signed in, a first
start included, and offers SimpleLogin, a password and GitHub for those
who have it, with no start without an account. An instance out of reach
is said so, with Try again, and one offering no way to create an account
too. An anonymous account's session left from before is deleted as the
app starts (D25), falling back to signing out on the device.
What only anonymous accounts needed goes: signing in anonymously, the
signed-out mark, adding GitHub to an account and switching to a GitHub
account from the Profile tab (with its Cancel, the only one), an
anonymous device's own keys, the warning that signing out deletes the
account, and the texts about keeping progress. Settings says on which
instance the account is signed in. Learn's publishing, reports and
suggestions and its learner count no longer ask whether the person has
an identity: everyone signed in has one.
The server still offers anonymous sign-in until the next commit, so
older apps keep working until v0.20.0 raises the minimum build.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 14:50:55 +02:00
/// **'Removes the account on {name} from this device, with all the device holds of it. What it synced stays on {name}: sign in there again to have it.'**
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:18:49 +02:00
String accountRemoveMessage ( String name ) ;
/// No description provided for @accountRemoveConfirm.
///
/// In en, this message translates to:
/// **'Remove'**
String get accountRemoveConfirm ;
refactor(halkyon_app): the account section, About, the language and countries' names, shared
What Learn and Todo each held now comes from halkyon_app:
- AccountTiles: whose account on which instance, switching, signing
out (asking first for an anonymous account, as it deletes it) and
deleting it (asking first), the app's own words passed in.
- showAppAbout: the app's name, version, icon and legalese as it gives
them, the source of what the instance runs (AppDependencies gains
sourceCodeUrl), the app's own parts, the licence list.
- resolveAppLocale: the first preferred language the app speaks, never
by country alone, British English otherwise.
- AssetCountryNames, the default of InstanceGate and InstanceScope:
Unicode CLDR 48.2's territory names in English and German
(tool/country_names.py, 7.6 KB), Hong Kong, Macao, Myanmar and
Palestine by their short names as Learn's world map has them, so
Todo says "Germany (EU)" where it said "DE (EU)".
Learn's texts read as before in every language; its About bloc and
gateway, its country names from the world map and the keys only the
moved code used are gone, as are Todo's copies.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 23:51:52 +02:00
/// No description provided for @accountSignedInOn.
///
/// In en, this message translates to:
/// **'Signed in on {instance}'**
String accountSignedInOn ( String instance ) ;
/// No description provided for @accountSwitch.
///
/// In en, this message translates to:
/// **'Switch account'**
String get accountSwitch ;
/// No description provided for @accountSignOut.
///
/// In en, this message translates to:
/// **'Sign out'**
String get accountSignOut ;
/// No description provided for @accountDelete.
///
/// In en, this message translates to:
/// **'Delete account'**
String get accountDelete ;
/// No description provided for @accountDeleteTitle.
///
/// In en, this message translates to:
/// **'Delete your account?'**
String get accountDeleteTitle ;
2026-10-21 19:49:18 +02:00
/// No description provided for @accountSignOutFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t sign out on this device. Try again.'**
String get accountSignOutFailed ;
/// No description provided for @accountDeleteFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t delete your account: the instance wasn\'t reached. Try again.'**
String get accountDeleteFailed ;
refactor(halkyon_app): the account section, About, the language and countries' names, shared
What Learn and Todo each held now comes from halkyon_app:
- AccountTiles: whose account on which instance, switching, signing
out (asking first for an anonymous account, as it deletes it) and
deleting it (asking first), the app's own words passed in.
- showAppAbout: the app's name, version, icon and legalese as it gives
them, the source of what the instance runs (AppDependencies gains
sourceCodeUrl), the app's own parts, the licence list.
- resolveAppLocale: the first preferred language the app speaks, never
by country alone, British English otherwise.
- AssetCountryNames, the default of InstanceGate and InstanceScope:
Unicode CLDR 48.2's territory names in English and German
(tool/country_names.py, 7.6 KB), Hong Kong, Macao, Myanmar and
Palestine by their short names as Learn's world map has them, so
Todo says "Germany (EU)" where it said "DE (EU)".
Learn's texts read as before in every language; its About bloc and
gateway, its country names from the world map and the keys only the
moved code used are gone, as are Todo's copies.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-19 23:51:52 +02:00
/// No description provided for @aboutSourceCode.
///
/// In en, this message translates to:
/// **'The source of what this instance runs:'**
String get aboutSourceCode ;
/// No description provided for @aboutSourceUnreachable.
///
/// In en, this message translates to:
/// **'Couldn\'t reach this instance to ask where its source is.'**
String get aboutSourceUnreachable ;
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:18:49 +02:00
/// No description provided for @sessionUnreachable.
///
/// In en, this message translates to:
/// **'Couldn\'t reach the server.'**
String get sessionUnreachable ;
/// No description provided for @personDataFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t open this device\'s data.'**
String get personDataFailed ;
/// No description provided for @startSignedOut.
///
/// In en, this message translates to:
/// **'Signed out'**
String get startSignedOut ;
/// No description provided for @startOn.
///
/// In en, this message translates to:
/// **'Start on {instance}:'**
String startOn ( String instance ) ;
/// No description provided for @startOtherInstance.
///
/// In en, this message translates to:
/// **'Use another instance'**
String get startOtherInstance ;
feat(app)!: no anonymous accounts on the device (ADR 0020, D4, D25)
Every account has a way in, so the apps make no anonymous account any
more. The start screen shows whenever nobody is signed in, a first
start included, and offers SimpleLogin, a password and GitHub for those
who have it, with no start without an account. An instance out of reach
is said so, with Try again, and one offering no way to create an account
too. An anonymous account's session left from before is deleted as the
app starts (D25), falling back to signing out on the device.
What only anonymous accounts needed goes: signing in anonymously, the
signed-out mark, adding GitHub to an account and switching to a GitHub
account from the Profile tab (with its Cancel, the only one), an
anonymous device's own keys, the warning that signing out deletes the
account, and the texts about keeping progress. Settings says on which
instance the account is signed in. Learn's publishing, reports and
suggestions and its learner count no longer ask whether the person has
an identity: everyone signed in has one.
The server still offers anonymous sign-in until the next commit, so
older apps keep working until v0.20.0 raises the minimum build.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 14:50:55 +02:00
/// On the start screen, where the instance offers neither SimpleLogin nor a password (ADR 0020, D4): nobody can make an account there.
///
/// In en, this message translates to:
/// **'{instance} offers no way to create an account.'**
String startNoWay ( String instance ) ;
feat(app): a wait screen for accounts that wait for the operator (ADR 0021, step 1)
An account signed in or up to on an instance whose operator approves
new accounts, or one suspended, gets no session but a wait: the device
keeps its pass, words and handle in the keyring per instance
(SecureAccountWaitStore, InstanceStorage.waitOf), the auth status is
Waiting, and SessionGate shows WaitScreen in its own app, so the start
screen's sheets go with it. Every sign-in flow hands over to it: the
password's sign-in, sign-up, reset and account reset, SimpleLogin's
sign-in, sign-up and mending; their cubits show no problem then.
The wait screen shows the handle, the four words the operator reads out
for the person to match, and that Halkyon never asks anyone to read them
out (D33); it asks where the wait stands as it opens, every 30 seconds,
as the app comes back and when asked. A new account can be deleted with
its pass, a suspended one can't (ADR 0017). Approved, it leads to the
usual sign-in, once more as before (D31); a pass the instance knows no
more ends the wait. The start screen says where new accounts wait for
approval, and offers no Create an account where the operator takes none;
both refusals (closed, too many waiting) have their words, en and de.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 15:29:22 +01:00
/// On the start screen, where new accounts wait for the operator's approval (Halkyon Learn's ADR 0021, D19).
///
/// In en, this message translates to:
/// **'New accounts on {instance} wait until its operator approves them.'**
String startSignUpsApproved ( String instance ) ;
/// On the start screen, where the operator takes no new accounts (D19); Create an account is gone.
///
/// In en, this message translates to:
/// **'{instance} takes no new accounts: sign in with yours.'**
String startSignUpsClosed ( String instance ) ;
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:18:49 +02:00
/// No description provided for @syncTitle.
///
/// In en, this message translates to:
/// **'Sync'**
String get syncTitle ;
/// No description provided for @syncNow.
///
/// In en, this message translates to:
/// **'Sync now'**
String get syncNow ;
/// No description provided for @syncStateSyncing.
///
/// In en, this message translates to:
/// **'Syncing…'**
String get syncStateSyncing ;
/// No description provided for @syncStateSynced.
///
/// In en, this message translates to:
/// **'Everything synced'**
String get syncStateSynced ;
/// No description provided for @syncStateFailing.
///
/// In en, this message translates to:
/// **'Sync is failing'**
String get syncStateFailing ;
/// No description provided for @syncStateNotYet.
///
/// In en, this message translates to:
/// **'Not synced yet'**
String get syncStateNotYet ;
/// No description provided for @syncStateMismatch.
///
/// In en, this message translates to:
/// **'This app can\'t sync with {server}'**
String syncStateMismatch ( String server ) ;
/// No description provided for @syncMismatchDetail.
///
/// In en, this message translates to:
/// **'Update the app. If it is up to date, the server needs an update. What you do meanwhile stays on this device and syncs afterwards.'**
String get syncMismatchDetail ;
/// No description provided for @syncLastCompleted.
///
/// In en, this message translates to:
/// **'Last completed at {time}'**
String syncLastCompleted ( String time ) ;
/// No description provided for @syncLastFailed.
///
/// In en, this message translates to:
/// **'Last attempt failed at {time}'**
String syncLastFailed ( String time ) ;
/// No description provided for @syncNoRunYet.
///
/// In en, this message translates to:
/// **'No sync has completed since the app started'**
String get syncNoRunYet ;
/// No description provided for @syncPending.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 change waits to sync} other{{count} changes wait to sync}}'**
String syncPending ( int count ) ;
/// No description provided for @syncTroubleLabel.
///
/// In en, this message translates to:
/// **'Sync needs attention'**
String get syncTroubleLabel ;
/// No description provided for @syncMismatchBanner.
///
/// In en, this message translates to:
/// **'This app can\'t sync with {server} any more. Update it to keep syncing.'**
String syncMismatchBanner ( String server ) ;
/// No description provided for @syncMismatchMore.
///
/// In en, this message translates to:
/// **'More'**
String get syncMismatchMore ;
feat(app): sign in with a password and TOTP on the device
ADR 0017, step 3: halkyon_app's side, which Learn and Todo take.
- The account's cryptography on the device (`SodiumAccountCrypto`):
halkyon_keys and SRP's client half, each step on an isolate of its
own. sodium hands out results as views of native memory its
finalizers free once the isolate that made them ends, so everything
crossing back is copied first: a view read freed memory and crashed
the process.
- `PasswordGateway` on the module's endpoints (steps without a session
through a client holding none; an authenticator set up with a session
granted but not yet the device's), and `PasswordRepository` running
the flows: signing in (M2 checked, the keys opened before anything is
trusted), signing up, a password for someone signed in, an account
never finished, the authenticators, recovery codes, changing the
password, a new recovery phrase, and getting back in.
- The unlocked keys in the system's keyring per instance and person
(`SecureAccountKeyStore`), gone once another person signs in or
nobody, and with the instance's removal.
- The screens: the start screen's Sign in and Create an account (and
when GitHub sign-in ends), the sign-in sheet (codes, recovery codes,
an account never finished, a lost authenticator), the account's setup
(the recovery phrase shown once, three random words typed back before
anything reaches the instance; the authenticator's QR code, drawn
with pub.dev's qr; the recovery codes), getting back in, and the
account section (`AccountSecuritySection`).
- `FileSaver` moves from Learn into halkyon_app, for the phrase and the
codes in Todo too; `AppDependencies` names it and `passwords`.
- halkyon_keys rebuilds account keys from the secret keys a device kept;
the key proof's message is in halkyon_base_client, checked against
the register's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-20 15:42:45 +02:00
/// No description provided for @startSignIn.
///
/// In en, this message translates to:
/// **'Sign in'**
String get startSignIn ;
/// No description provided for @startCreateAccount.
///
/// In en, this message translates to:
/// **'Create an account'**
String get startCreateAccount ;
feat(app): a wait screen for accounts that wait for the operator (ADR 0021, step 1)
An account signed in or up to on an instance whose operator approves
new accounts, or one suspended, gets no session but a wait: the device
keeps its pass, words and handle in the keyring per instance
(SecureAccountWaitStore, InstanceStorage.waitOf), the auth status is
Waiting, and SessionGate shows WaitScreen in its own app, so the start
screen's sheets go with it. Every sign-in flow hands over to it: the
password's sign-in, sign-up, reset and account reset, SimpleLogin's
sign-in, sign-up and mending; their cubits show no problem then.
The wait screen shows the handle, the four words the operator reads out
for the person to match, and that Halkyon never asks anyone to read them
out (D33); it asks where the wait stands as it opens, every 30 seconds,
as the app comes back and when asked. A new account can be deleted with
its pass, a suspended one can't (ADR 0017). Approved, it leads to the
usual sign-in, once more as before (D31); a pass the instance knows no
more ends the wait. The start screen says where new accounts wait for
approval, and offers no Create an account where the operator takes none;
both refusals (closed, too many waiting) have their words, en and de.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 15:29:22 +01:00
/// On the wait screen of a new account (D31), under its handle.
///
/// In en, this message translates to:
/// **'Your account waits until the operator of {instance} approves it.'**
String waitWaiting ( String instance ) ;
/// On the wait screen of a suspended account (D31).
///
/// In en, this message translates to:
/// **'The operator of {instance} has suspended your account. Only they can lift it.'**
String waitSuspended ( String instance ) ;
/// On the wait screen once the account is approved (D31), above Sign in.
///
/// In en, this message translates to:
/// **'The operator of {instance} has approved your account. Sign in once more, as you did before.'**
String waitApproved ( String instance ) ;
/// On the wait screen once the instance knows the wait no more: refused, past its 14 days, or its pass past its 30 (D31).
///
/// In en, this message translates to:
/// **'This sign-up has ended: the operator of {instance} refused it, or it waited too long.'**
String waitEnded ( String instance ) ;
/// The button of an ended wait: the start screen.
///
/// In en, this message translates to:
/// **'Back to the start'**
String get waitBack ;
/// Above the four words the operator reads out for the person to match (D33).
///
/// In en, this message translates to:
/// **'Matching code'**
String get waitMatchingCode ;
/// Under the matching code (D33): the operator reads, the person confirms, so nobody posing as the operator learns the words.
///
/// In en, this message translates to:
/// **'When the operator of {instance} reads you four words, check that they are these. Never read them out yourself: Halkyon never asks anyone to.'**
String waitMatchingNote ( String instance ) ;
/// Asks the instance at once where the wait stands; it asks every 30 seconds by itself.
///
/// In en, this message translates to:
/// **'Check now'**
String get waitCheckNow ;
/// On the wait screen, when asking where the wait stands failed.
///
/// In en, this message translates to:
/// **'{instance} couldn\'t be reached. It\'s asked again every 30 seconds.'**
String waitUnreachable ( String instance ) ;
/// On the wait screen of a new account: deletes it with its pass (D31).
///
/// In en, this message translates to:
/// **'Delete this account'**
String get waitDelete ;
/// The question before a waiting account is deleted.
///
/// In en, this message translates to:
/// **'Delete this account?'**
String get waitDeleteTitle ;
/// Under that question.
///
/// In en, this message translates to:
/// **'It goes with everything it has, and nobody can sign in to it again.'**
String get waitDeleteMessage ;
/// On the wait screen, when deleting the account failed.
///
/// In en, this message translates to:
/// **'Couldn\'t delete the account. Try again.'**
String get waitDeleteFailed ;
feat(app): sign in with a password and TOTP on the device
ADR 0017, step 3: halkyon_app's side, which Learn and Todo take.
- The account's cryptography on the device (`SodiumAccountCrypto`):
halkyon_keys and SRP's client half, each step on an isolate of its
own. sodium hands out results as views of native memory its
finalizers free once the isolate that made them ends, so everything
crossing back is copied first: a view read freed memory and crashed
the process.
- `PasswordGateway` on the module's endpoints (steps without a session
through a client holding none; an authenticator set up with a session
granted but not yet the device's), and `PasswordRepository` running
the flows: signing in (M2 checked, the keys opened before anything is
trusted), signing up, a password for someone signed in, an account
never finished, the authenticators, recovery codes, changing the
password, a new recovery phrase, and getting back in.
- The unlocked keys in the system's keyring per instance and person
(`SecureAccountKeyStore`), gone once another person signs in or
nobody, and with the instance's removal.
- The screens: the start screen's Sign in and Create an account (and
when GitHub sign-in ends), the sign-in sheet (codes, recovery codes,
an account never finished, a lost authenticator), the account's setup
(the recovery phrase shown once, three random words typed back before
anything reaches the instance; the authenticator's QR code, drawn
with pub.dev's qr; the recovery codes), getting back in, and the
account section (`AccountSecuritySection`).
- `FileSaver` moves from Learn into halkyon_app, for the phrase and the
codes in Todo too; `AppDependencies` names it and `passwords`.
- halkyon_keys rebuilds account keys from the secret keys a device kept;
the key proof's message is in halkyon_base_client, checked against
the register's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-20 15:42:45 +02:00
/// No description provided for @passwordAddress.
///
/// In en, this message translates to:
/// **'Email address'**
String get passwordAddress ;
/// No description provided for @passwordPassword.
///
/// In en, this message translates to:
/// **'Password'**
String get passwordPassword ;
/// No description provided for @passwordNewPassword.
///
/// In en, this message translates to:
/// **'New password'**
String get passwordNewPassword ;
/// No description provided for @passwordCurrent.
///
/// In en, this message translates to:
/// **'Current password'**
String get passwordCurrent ;
/// No description provided for @passwordRepeat.
///
/// In en, this message translates to:
/// **'New password again'**
String get passwordRepeat ;
/// No description provided for @passwordsDiffer.
///
/// In en, this message translates to:
/// **'The two passwords differ.'**
String get passwordsDiffer ;
/// No description provided for @passwordShow.
///
/// In en, this message translates to:
/// **'Show password'**
String get passwordShow ;
/// No description provided for @passwordHide.
///
/// In en, this message translates to:
/// **'Hide password'**
String get passwordHide ;
/// No description provided for @passwordContinue.
///
/// In en, this message translates to:
/// **'Continue'**
String get passwordContinue ;
/// No description provided for @passwordBack.
///
/// In en, this message translates to:
/// **'Back'**
String get passwordBack ;
/// No description provided for @passwordDone.
///
/// In en, this message translates to:
/// **'Done'**
String get passwordDone ;
/// No description provided for @passwordSave.
///
/// In en, this message translates to:
/// **'Save as a file'**
String get passwordSave ;
/// No description provided for @passwordQrCode.
///
/// In en, this message translates to:
/// **'QR code for your authenticator app'**
String get passwordQrCode ;
/// No description provided for @passwordMailCode.
///
/// In en, this message translates to:
/// **'Code from the email'**
String get passwordMailCode ;
/// No description provided for @passwordMailCodeSent.
///
/// In en, this message translates to:
/// **'We emailed a code to {address}. It\'s valid for 15 minutes.'**
String passwordMailCodeSent ( String address ) ;
feat(base)!: a handle at password sign-up (ADR 0020, D26, D28–D30)
Every new account gets a handle, and an account without one gets its
first as it sets a password. The handle is private until its person
goes public (D5).
- `passwordSignIn.signUp` takes a handle. `passwordSignIn.checkHandle`
says whether a sign-up could take one, only while that sign-up's
mailed code waits (`codeExpired` otherwise), counted per code request
(30 an hour) and per network (60) (D29). Members ask the register.
- `password.set` takes the first handle of an account without one
(D28); `SignInWays.handle` tells the app whether there is one.
- Each checks the handle before the code is used, so a refusal leaves
the code for another try. An existing account at the address keeps
its own handle where it has one, and the typed handle is still
checked, so the answer says nothing about the address.
- `first_handle.dart` holds what SimpleLogin, the keys and passwords
share: `refusalOfNewHandle`, `insertFirstProfile`, `hasHandle`.
- halkyon_app: one `HandleField` with its `HandleCheckCubit` (asked
300 ms after typing pauses, only about the handle typed last; the
instance's suggestion is free without asking) (D30). The password
setup asks for the handle between the phrase check and the code, and
a refusal at the code sends back to it. SimpleLogin's sign-up and
keys steps use the same field and now check as it is typed.
- The module's README and ADR 0020 (D29, D30, the built state) amended.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 18:09:04 +02:00
/// No description provided for @passwordHandleIntro.
///
/// In en, this message translates to:
/// **'A code is on its way to {address}. Meanwhile, choose your handle: others find you by it. It stays private until you publish something or fill in your profile, and can change until your first publish.'**
String passwordHandleIntro ( String address ) ;
feat(app): sign in with a password and TOTP on the device
ADR 0017, step 3: halkyon_app's side, which Learn and Todo take.
- The account's cryptography on the device (`SodiumAccountCrypto`):
halkyon_keys and SRP's client half, each step on an isolate of its
own. sodium hands out results as views of native memory its
finalizers free once the isolate that made them ends, so everything
crossing back is copied first: a view read freed memory and crashed
the process.
- `PasswordGateway` on the module's endpoints (steps without a session
through a client holding none; an authenticator set up with a session
granted but not yet the device's), and `PasswordRepository` running
the flows: signing in (M2 checked, the keys opened before anything is
trusted), signing up, a password for someone signed in, an account
never finished, the authenticators, recovery codes, changing the
password, a new recovery phrase, and getting back in.
- The unlocked keys in the system's keyring per instance and person
(`SecureAccountKeyStore`), gone once another person signs in or
nobody, and with the instance's removal.
- The screens: the start screen's Sign in and Create an account (and
when GitHub sign-in ends), the sign-in sheet (codes, recovery codes,
an account never finished, a lost authenticator), the account's setup
(the recovery phrase shown once, three random words typed back before
anything reaches the instance; the authenticator's QR code, drawn
with pub.dev's qr; the recovery codes), getting back in, and the
account section (`AccountSecuritySection`).
- `FileSaver` moves from Learn into halkyon_app, for the phrase and the
codes in Todo too; `AppDependencies` names it and `passwords`.
- halkyon_keys rebuilds account keys from the secret keys a device kept;
the key proof's message is in halkyon_base_client, checked against
the register's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-20 15:42:45 +02:00
/// No description provided for @passwordResendCode.
///
/// In en, this message translates to:
/// **'Send a new code'**
String get passwordResendCode ;
/// No description provided for @passwordTotpCode.
///
/// In en, this message translates to:
/// **'Code from your authenticator app'**
String get passwordTotpCode ;
/// No description provided for @passwordRecoveryCode.
///
/// In en, this message translates to:
/// **'Recovery code'**
String get passwordRecoveryCode ;
/// No description provided for @passwordUseRecoveryCode.
///
/// In en, this message translates to:
/// **'Use a recovery code instead'**
String get passwordUseRecoveryCode ;
/// No description provided for @passwordUseTotpCode.
///
/// In en, this message translates to:
/// **'Use your authenticator app instead'**
String get passwordUseTotpCode ;
/// No description provided for @passwordUnlocking.
///
/// In en, this message translates to:
/// **'Unlocking your password on this device…'**
String get passwordUnlocking ;
/// No description provided for @passwordMakingKeys.
///
/// In en, this message translates to:
/// **'Making your keys on this device…'**
String get passwordMakingKeys ;
/// No description provided for @passwordSignInTitle.
///
/// In en, this message translates to:
/// **'Sign in'**
String get passwordSignInTitle ;
/// No description provided for @passwordSignInAction.
///
/// In en, this message translates to:
/// **'Sign in'**
String get passwordSignInAction ;
/// No description provided for @passwordSignInIntro.
///
/// In en, this message translates to:
/// **'With the email address and password of your account on {instance}.'**
String passwordSignInIntro ( String instance ) ;
/// No description provided for @passwordSecondFactorIntro.
///
/// In en, this message translates to:
/// **'Enter the code your authenticator app shows for {instance}.'**
String passwordSecondFactorIntro ( String instance ) ;
/// No description provided for @passwordRecoveryCodeIntro.
///
/// In en, this message translates to:
/// **'Enter one of the recovery codes you kept. Each signs in once.'**
String get passwordRecoveryCodeIntro ;
/// No description provided for @passwordForgot.
///
/// In en, this message translates to:
/// **'Forgot your password?'**
String get passwordForgot ;
/// No description provided for @passwordOtherPassword.
///
/// In en, this message translates to:
/// **'Change the address or password'**
String get passwordOtherPassword ;
/// No description provided for @passwordLostAuthenticator.
///
/// In en, this message translates to:
/// **'Lost your authenticator?'**
String get passwordLostAuthenticator ;
/// No description provided for @passwordLostAuthenticatorTitle.
///
/// In en, this message translates to:
/// **'Lost authenticator'**
String get passwordLostAuthenticatorTitle ;
/// No description provided for @passwordLostAuthenticatorIntro.
///
/// In en, this message translates to:
/// **'With a recovery code you sign in as usual. Without one, ask to reset your authenticators: it takes effect 72 hours later, and signing in with a code before then stops it.'**
String get passwordLostAuthenticatorIntro ;
/// No description provided for @passwordResetWithMail.
///
/// In en, this message translates to:
/// **'Reset with a code by email'**
String get passwordResetWithMail ;
/// No description provided for @passwordResetWithMailHint.
///
/// In en, this message translates to:
/// **'We email a code to {address}.'**
String passwordResetWithMailHint ( String address ) ;
/// No description provided for @passwordResetWithPhrase.
///
/// In en, this message translates to:
/// **'Reset with your recovery phrase'**
String get passwordResetWithPhrase ;
/// No description provided for @passwordResetWithPhraseHint.
///
/// In en, this message translates to:
/// **'The 24 words you wrote down.'**
String get passwordResetWithPhraseHint ;
/// No description provided for @passwordSignInWithoutCode.
///
/// In en, this message translates to:
/// **'Sign in without a code'**
String get passwordSignInWithoutCode ;
/// No description provided for @passwordSignInWithoutCodeHint.
///
/// In en, this message translates to:
/// **'Once a reset you asked for has taken effect.'**
String get passwordSignInWithoutCodeHint ;
/// No description provided for @passwordResetAction.
///
/// In en, this message translates to:
/// **'Reset'**
String get passwordResetAction ;
/// No description provided for @passwordAuthenticatorResetAt.
///
/// In en, this message translates to:
/// **'Your authenticators are reset on {date}. We emailed you: if it wasn\'t you, sign in with a code before then to stop it. After that, sign in with your password alone and set up a new authenticator.'**
String passwordAuthenticatorResetAt ( String date ) ;
/// No description provided for @passwordUnfinishedIntro.
///
/// In en, this message translates to:
/// **'Your account was never finished. We emailed a code to {address}: with it, you sign in and set up an authenticator.'**
String passwordUnfinishedIntro ( String address ) ;
/// No description provided for @passwordPhraseField.
///
/// In en, this message translates to:
/// **'Recovery phrase'**
String get passwordPhraseField ;
/// No description provided for @passwordPhraseFieldHint.
///
/// In en, this message translates to:
/// **'The 24 words, with spaces between them'**
String get passwordPhraseFieldHint ;
/// No description provided for @passwordPhraseFieldIntro.
///
/// In en, this message translates to:
/// **'Enter your recovery phrase: it proves the account is yours.'**
String get passwordPhraseFieldIntro ;
/// No description provided for @passwordAuthenticatorTitle.
///
/// In en, this message translates to:
/// **'Set up an authenticator'**
String get passwordAuthenticatorTitle ;
/// No description provided for @passwordAuthenticatorIntro.
///
/// In en, this message translates to:
/// **'Every sign-in takes a code from an authenticator app, such as Aegis, 2FAS or Proton Authenticator. Scan this code with it, or type in the key.'**
String get passwordAuthenticatorIntro ;
/// No description provided for @passwordAuthenticatorKey.
///
/// In en, this message translates to:
/// **'Key'**
String get passwordAuthenticatorKey ;
/// No description provided for @passwordAuthenticatorOpen.
///
/// In en, this message translates to:
/// **'Open in your authenticator app'**
String get passwordAuthenticatorOpen ;
/// No description provided for @passwordAuthenticatorNoApp.
///
/// In en, this message translates to:
/// **'No app on this device opens it. Scan the code or type in the key.'**
String get passwordAuthenticatorNoApp ;
/// No description provided for @passwordAuthenticatorCodeIntro.
///
/// In en, this message translates to:
/// **'Then enter the code it shows.'**
String get passwordAuthenticatorCodeIntro ;
2026-10-21 10:42:55 +02:00
/// No description provided for @passwordAuthenticatorOwnerIntro.
///
/// In en, this message translates to:
/// **'To add it, confirm it is you: your password and a code from an authenticator you added before.'**
String get passwordAuthenticatorOwnerIntro ;
feat(app): sign in with a password and TOTP on the device
ADR 0017, step 3: halkyon_app's side, which Learn and Todo take.
- The account's cryptography on the device (`SodiumAccountCrypto`):
halkyon_keys and SRP's client half, each step on an isolate of its
own. sodium hands out results as views of native memory its
finalizers free once the isolate that made them ends, so everything
crossing back is copied first: a view read freed memory and crashed
the process.
- `PasswordGateway` on the module's endpoints (steps without a session
through a client holding none; an authenticator set up with a session
granted but not yet the device's), and `PasswordRepository` running
the flows: signing in (M2 checked, the keys opened before anything is
trusted), signing up, a password for someone signed in, an account
never finished, the authenticators, recovery codes, changing the
password, a new recovery phrase, and getting back in.
- The unlocked keys in the system's keyring per instance and person
(`SecureAccountKeyStore`), gone once another person signs in or
nobody, and with the instance's removal.
- The screens: the start screen's Sign in and Create an account (and
when GitHub sign-in ends), the sign-in sheet (codes, recovery codes,
an account never finished, a lost authenticator), the account's setup
(the recovery phrase shown once, three random words typed back before
anything reaches the instance; the authenticator's QR code, drawn
with pub.dev's qr; the recovery codes), getting back in, and the
account section (`AccountSecuritySection`).
- `FileSaver` moves from Learn into halkyon_app, for the phrase and the
codes in Todo too; `AppDependencies` names it and `passwords`.
- halkyon_keys rebuilds account keys from the secret keys a device kept;
the key proof's message is in halkyon_base_client, checked against
the register's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-20 15:42:45 +02:00
/// No description provided for @passwordExistingCode.
///
/// In en, this message translates to:
/// **'Code from an authenticator you added before'**
String get passwordExistingCode ;
/// No description provided for @passwordRecoveryCodesTitle.
///
/// In en, this message translates to:
/// **'Recovery codes'**
String get passwordRecoveryCodesTitle ;
/// No description provided for @passwordRecoveryCodesIntro.
///
/// In en, this message translates to:
/// **'Each signs in once in place of a code from your authenticator, should you lose it. Keep them where only you find them: they\'re shown only now.'**
String get passwordRecoveryCodesIntro ;
/// No description provided for @passwordCodesKept.
///
/// In en, this message translates to:
/// **'I\'ve kept them'**
String get passwordCodesKept ;
/// No description provided for @passwordPhraseTitle.
///
/// In en, this message translates to:
/// **'Your recovery phrase'**
String get passwordPhraseTitle ;
/// No description provided for @passwordPhraseIntro.
///
/// In en, this message translates to:
/// **'These 24 words open your account\'s keys should you forget your password: without them, what\'s encrypted is lost. Write them down in order, and keep them where only you find them. They\'re shown only now.'**
String get passwordPhraseIntro ;
/// No description provided for @passwordPhraseWrittenDown.
///
/// In en, this message translates to:
/// **'I\'ve written it down'**
String get passwordPhraseWrittenDown ;
/// No description provided for @passwordPhraseCheckTitle.
///
/// In en, this message translates to:
/// **'Check your recovery phrase'**
String get passwordPhraseCheckTitle ;
/// No description provided for @passwordPhraseCheckIntro.
///
/// In en, this message translates to:
/// **'Type the words asked for, from what you wrote down.'**
String get passwordPhraseCheckIntro ;
/// No description provided for @passwordPhraseWord.
///
/// In en, this message translates to:
/// **'Word {number}'**
String passwordPhraseWord ( int number ) ;
/// No description provided for @passwordPhraseWrong.
///
/// In en, this message translates to:
/// **'These aren\'t the words. Look again at what you wrote down.'**
String get passwordPhraseWrong ;
/// No description provided for @passwordPhraseShowAgain.
///
/// In en, this message translates to:
/// **'Show the phrase again'**
String get passwordPhraseShowAgain ;
/// No description provided for @passwordCreateTitle.
///
/// In en, this message translates to:
/// **'Create an account'**
String get passwordCreateTitle ;
/// No description provided for @passwordCreateIntro.
///
/// In en, this message translates to:
/// **'On {instance}, with your email address, a password, and an authenticator app for the code every sign-in takes.'**
String passwordCreateIntro ( String instance ) ;
/// No description provided for @passwordSetUpTitle.
///
/// In en, this message translates to:
/// **'Set up a password'**
String get passwordSetUpTitle ;
/// No description provided for @passwordSetUpIntro.
///
/// In en, this message translates to:
/// **'Sign in with your email address, a password and an authenticator app, on this device and others. Everything you have stays.'**
String get passwordSetUpIntro ;
/// No description provided for @passwordChooseIntro.
///
/// In en, this message translates to:
/// **'Choose a password. It never leaves this device: the instance can\'t read it, and can\'t reset it without what it protects being lost.'**
String get passwordChooseIntro ;
/// No description provided for @passwordFinishResetTitle.
///
/// In en, this message translates to:
/// **'Finish the reset'**
String get passwordFinishResetTitle ;
/// No description provided for @passwordFinishResetIntro.
///
/// In en, this message translates to:
/// **'72 hours after you asked to reset your account, finish it here with a new code by email.'**
String get passwordFinishResetIntro ;
/// No description provided for @passwordFinishResetHavePhrase.
///
/// In en, this message translates to:
/// **'I have my recovery phrase'**
String get passwordFinishResetHavePhrase ;
/// No description provided for @passwordFinishResetKeepsKeys.
///
/// In en, this message translates to:
/// **'Your account keeps its keys: nothing encrypted is lost.'**
String get passwordFinishResetKeepsKeys ;
/// No description provided for @passwordFinishResetNewKeys.
///
/// In en, this message translates to:
/// **'Without your recovery phrase, your account gets new keys: what was encrypted with the old ones is lost.'**
String get passwordFinishResetNewKeys ;
/// No description provided for @passwordRecoveryTitle.
///
/// In en, this message translates to:
/// **'Get back into your account'**
String get passwordRecoveryTitle ;
/// No description provided for @passwordRecoveryWithPhrase.
///
/// In en, this message translates to:
/// **'I have my recovery phrase'**
String get passwordRecoveryWithPhrase ;
/// No description provided for @passwordRecoveryWithPhraseHint.
///
/// In en, this message translates to:
/// **'Set a new password at once. Everything stays.'**
String get passwordRecoveryWithPhraseHint ;
/// No description provided for @passwordRecoveryWithPhraseIntro.
///
/// In en, this message translates to:
/// **'Your recovery phrase opens your account\'s keys, which your new password then protects. A code from your authenticator, or a recovery code, confirms it.'**
String get passwordRecoveryWithPhraseIntro ;
/// No description provided for @passwordRecoveryNoCodeHint.
///
/// In en, this message translates to:
/// **'Lost your authenticator as well? Reset your account 72 hours later with your recovery phrase: it keeps everything.'**
String get passwordRecoveryNoCodeHint ;
/// No description provided for @passwordRecoveryWithoutPhrase.
///
/// In en, this message translates to:
/// **'Reset my account'**
String get passwordRecoveryWithoutPhrase ;
/// No description provided for @passwordRecoveryWithoutPhraseHint.
///
/// In en, this message translates to:
/// **'Takes effect 72 hours later. Without your recovery phrase, what\'s encrypted is lost.'**
String get passwordRecoveryWithoutPhraseHint ;
/// No description provided for @passwordRecoveryStartIntro.
///
/// In en, this message translates to:
/// **'We email a code to your address. Your account can then be reset 72 hours later; signing in with your password before then stops it.'**
String get passwordRecoveryStartIntro ;
/// No description provided for @passwordRecoveryFinish.
///
/// In en, this message translates to:
/// **'Finish a reset'**
String get passwordRecoveryFinish ;
/// No description provided for @passwordRecoveryFinishHint.
///
/// In en, this message translates to:
/// **'72 hours after you asked for it.'**
String get passwordRecoveryFinishHint ;
/// No description provided for @passwordResetStarted.
///
/// In en, this message translates to:
/// **'Your account can be reset from {date}. We emailed you: if it wasn\'t you, sign in with your password before then to stop it.'**
String passwordResetStarted ( String date ) ;
/// No description provided for @passwordSectionTitle.
///
/// In en, this message translates to:
/// **'Password and authenticator'**
String get passwordSectionTitle ;
/// No description provided for @passwordSectionFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t read how your account signs in.'**
String get passwordSectionFailed ;
/// No description provided for @passwordSectionSetUp.
///
/// In en, this message translates to:
/// **'Set up a password'**
String get passwordSectionSetUp ;
/// No description provided for @passwordSectionSetUpHint.
///
/// In en, this message translates to:
/// **'Sign in with your email address, a password and an authenticator app.'**
String get passwordSectionSetUpHint ;
/// No description provided for @passwordSectionUnfinished.
///
/// In en, this message translates to:
/// **'Finish setting up: add an authenticator'**
String get passwordSectionUnfinished ;
/// No description provided for @passwordSectionUnfinishedHint.
///
/// In en, this message translates to:
/// **'Your password signs in once an authenticator is set up.'**
String get passwordSectionUnfinishedHint ;
/// No description provided for @passwordSectionChange.
///
/// In en, this message translates to:
/// **'Change password'**
String get passwordSectionChange ;
/// No description provided for @passwordSectionKeysMissing.
///
/// In en, this message translates to:
/// **'Sign in on this device with your password to change it.'**
String get passwordSectionKeysMissing ;
/// No description provided for @passwordSectionNotConfirmed.
///
/// In en, this message translates to:
/// **'Not set up yet'**
String get passwordSectionNotConfirmed ;
/// No description provided for @passwordSectionRemove.
///
/// In en, this message translates to:
/// **'Remove {name}?'**
String passwordSectionRemove ( String name ) ;
/// No description provided for @passwordSectionRemoveIntro.
///
/// In en, this message translates to:
2026-10-21 10:42:55 +02:00
/// **'Enter your password and a code from one of your authenticators to confirm.'**
feat(app): sign in with a password and TOTP on the device
ADR 0017, step 3: halkyon_app's side, which Learn and Todo take.
- The account's cryptography on the device (`SodiumAccountCrypto`):
halkyon_keys and SRP's client half, each step on an isolate of its
own. sodium hands out results as views of native memory its
finalizers free once the isolate that made them ends, so everything
crossing back is copied first: a view read freed memory and crashed
the process.
- `PasswordGateway` on the module's endpoints (steps without a session
through a client holding none; an authenticator set up with a session
granted but not yet the device's), and `PasswordRepository` running
the flows: signing in (M2 checked, the keys opened before anything is
trusted), signing up, a password for someone signed in, an account
never finished, the authenticators, recovery codes, changing the
password, a new recovery phrase, and getting back in.
- The unlocked keys in the system's keyring per instance and person
(`SecureAccountKeyStore`), gone once another person signs in or
nobody, and with the instance's removal.
- The screens: the start screen's Sign in and Create an account (and
when GitHub sign-in ends), the sign-in sheet (codes, recovery codes,
an account never finished, a lost authenticator), the account's setup
(the recovery phrase shown once, three random words typed back before
anything reaches the instance; the authenticator's QR code, drawn
with pub.dev's qr; the recovery codes), getting back in, and the
account section (`AccountSecuritySection`).
- `FileSaver` moves from Learn into halkyon_app, for the phrase and the
codes in Todo too; `AppDependencies` names it and `passwords`.
- halkyon_keys rebuilds account keys from the secret keys a device kept;
the key proof's message is in halkyon_base_client, checked against
the register's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-20 15:42:45 +02:00
String get passwordSectionRemoveIntro ;
/// No description provided for @passwordSectionRemoveAction.
///
/// In en, this message translates to:
/// **'Remove'**
String get passwordSectionRemoveAction ;
/// No description provided for @passwordSectionAddAuthenticator.
///
/// In en, this message translates to:
/// **'Add an authenticator'**
String get passwordSectionAddAuthenticator ;
/// No description provided for @passwordSectionRecoveryCodes.
///
/// In en, this message translates to:
/// **'New recovery codes'**
String get passwordSectionRecoveryCodes ;
/// No description provided for @passwordSectionRecoveryCodesHint.
///
/// In en, this message translates to:
/// **'The old ones stop working.'**
String get passwordSectionRecoveryCodesHint ;
/// No description provided for @passwordSectionNewPhrase.
///
/// In en, this message translates to:
/// **'New recovery phrase'**
String get passwordSectionNewPhrase ;
/// No description provided for @passwordSectionPhraseHint.
///
/// In en, this message translates to:
/// **'Your old phrase stops working.'**
String get passwordSectionPhraseHint ;
/// No description provided for @passwordChangeTitle.
///
/// In en, this message translates to:
/// **'Change password'**
String get passwordChangeTitle ;
/// No description provided for @passwordChangeIntro.
///
/// In en, this message translates to:
/// **'Your other devices are signed out. Your keys stay: nothing is lost.'**
String get passwordChangeIntro ;
/// No description provided for @passwordChangeAction.
///
/// In en, this message translates to:
/// **'Change password'**
String get passwordChangeAction ;
/// No description provided for @passwordChanged.
///
/// In en, this message translates to:
/// **'Password changed. Your other devices are signed out.'**
String get passwordChanged ;
/// No description provided for @passwordNewCodesIntro.
///
/// In en, this message translates to:
2026-10-21 10:42:55 +02:00
/// **'Enter your password and a code from your authenticator app. Your old recovery codes stop working.'**
feat(app): sign in with a password and TOTP on the device
ADR 0017, step 3: halkyon_app's side, which Learn and Todo take.
- The account's cryptography on the device (`SodiumAccountCrypto`):
halkyon_keys and SRP's client half, each step on an isolate of its
own. sodium hands out results as views of native memory its
finalizers free once the isolate that made them ends, so everything
crossing back is copied first: a view read freed memory and crashed
the process.
- `PasswordGateway` on the module's endpoints (steps without a session
through a client holding none; an authenticator set up with a session
granted but not yet the device's), and `PasswordRepository` running
the flows: signing in (M2 checked, the keys opened before anything is
trusted), signing up, a password for someone signed in, an account
never finished, the authenticators, recovery codes, changing the
password, a new recovery phrase, and getting back in.
- The unlocked keys in the system's keyring per instance and person
(`SecureAccountKeyStore`), gone once another person signs in or
nobody, and with the instance's removal.
- The screens: the start screen's Sign in and Create an account (and
when GitHub sign-in ends), the sign-in sheet (codes, recovery codes,
an account never finished, a lost authenticator), the account's setup
(the recovery phrase shown once, three random words typed back before
anything reaches the instance; the authenticator's QR code, drawn
with pub.dev's qr; the recovery codes), getting back in, and the
account section (`AccountSecuritySection`).
- `FileSaver` moves from Learn into halkyon_app, for the phrase and the
codes in Todo too; `AppDependencies` names it and `passwords`.
- halkyon_keys rebuilds account keys from the secret keys a device kept;
the key proof's message is in halkyon_base_client, checked against
the register's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-20 15:42:45 +02:00
String get passwordNewCodesIntro ;
/// No description provided for @passwordNewPhraseIntro.
///
/// In en, this message translates to:
/// **'Your new recovery phrase. Write the 24 words down in order: once you confirm it, your old phrase opens nothing any more.'**
String get passwordNewPhraseIntro ;
/// No description provided for @passwordPhraseKept.
///
/// In en, this message translates to:
/// **'New recovery phrase set. The old one no longer works.'**
String get passwordPhraseKept ;
/// No description provided for @passwordWrongCredentials.
///
/// In en, this message translates to:
/// **'The address, the password or the code isn\'t right.'**
String get passwordWrongCredentials ;
/// No description provided for @passwordTooManyAttempts.
///
/// In en, this message translates to:
/// **'Too many attempts. Try again in an hour.'**
String get passwordTooManyAttempts ;
/// No description provided for @passwordSetupIncomplete.
///
/// In en, this message translates to:
/// **'Your account was never finished: set up an authenticator.'**
String get passwordSetupIncomplete ;
/// No description provided for @passwordNotDue.
///
/// In en, this message translates to:
/// **'This takes effect on {date}.'**
String passwordNotDue ( String date ) ;
/// No description provided for @passwordNotDueYet.
///
/// In en, this message translates to:
/// **'This hasn\'t taken effect yet.'**
String get passwordNotDueYet ;
/// No description provided for @passwordNoReset.
///
/// In en, this message translates to:
/// **'No reset waits for this address.'**
String get passwordNoReset ;
/// No description provided for @passwordExists.
///
/// In en, this message translates to:
/// **'This address\'s account has a password already. Sign in with it, or get back in if you\'ve forgotten it.'**
String get passwordExists ;
/// No description provided for @passwordAddressTaken.
///
/// In en, this message translates to:
/// **'This address belongs to another account.'**
String get passwordAddressTaken ;
/// No description provided for @passwordNoPassword.
///
/// In en, this message translates to:
/// **'This account has no password.'**
String get passwordNoPassword ;
/// No description provided for @passwordWrongCode.
///
/// In en, this message translates to:
/// **'This code isn\'t right. Enter the one your app shows now.'**
String get passwordWrongCode ;
/// No description provided for @passwordLastAuthenticator.
///
/// In en, this message translates to:
/// **'Your last authenticator can\'t go: every sign-in needs one.'**
String get passwordLastAuthenticator ;
/// No description provided for @passwordTooManyAuthenticators.
///
/// In en, this message translates to:
/// **'Your account has as many authenticators as it may. Remove one first.'**
String get passwordTooManyAuthenticators ;
/// No description provided for @passwordUnknownAuthenticator.
///
/// In en, this message translates to:
/// **'This authenticator is gone.'**
String get passwordUnknownAuthenticator ;
/// No description provided for @passwordInvalidSetup.
///
/// In en, this message translates to:
/// **'The instance didn\'t accept what this device made. Update the app, or try again.'**
String get passwordInvalidSetup ;
/// No description provided for @passwordChallengeExpired.
///
/// In en, this message translates to:
/// **'That took too long. Try again.'**
String get passwordChallengeExpired ;
/// No description provided for @passwordUnavailable.
///
/// In en, this message translates to:
/// **'This instance offers no password sign-in.'**
String get passwordUnavailable ;
/// No description provided for @passwordNotAnAddress.
///
/// In en, this message translates to:
/// **'This isn\'t an email address.'**
String get passwordNotAnAddress ;
/// No description provided for @passwordTooManyCodes.
///
/// In en, this message translates to:
/// **'Too many codes asked for. Try again in an hour.'**
String get passwordTooManyCodes ;
/// No description provided for @passwordWrongMailCode.
///
/// In en, this message translates to:
/// **'This code isn\'t right.'**
String get passwordWrongMailCode ;
/// No description provided for @passwordTooManyMailTries.
///
/// In en, this message translates to:
/// **'Too many wrong codes. Ask for a new one.'**
String get passwordTooManyMailTries ;
/// No description provided for @passwordMailCodeExpired.
///
/// In en, this message translates to:
/// **'This code is no longer valid. Ask for a new one.'**
String get passwordMailCodeExpired ;
/// No description provided for @passwordMailFailed.
///
/// In en, this message translates to:
/// **'The instance couldn\'t send the email. Try again later.'**
String get passwordMailFailed ;
/// No description provided for @passwordOtherAddressLinked.
///
/// In en, this message translates to:
/// **'Your account has another email address.'**
String get passwordOtherAddressLinked ;
/// No description provided for @passwordNotAPhrase.
///
/// In en, this message translates to:
/// **'This isn\'t a recovery phrase: check every word and their number.'**
String get passwordNotAPhrase ;
/// No description provided for @passwordPhraseDoesNotFit.
///
/// In en, this message translates to:
/// **'The address or the recovery phrase isn\'t right.'**
String get passwordPhraseDoesNotFit ;
/// No description provided for @passwordUnusable.
///
/// In en, this message translates to:
/// **'This password can\'t be used: it\'s empty, or holds characters that aren\'t text.'**
String get passwordUnusable ;
/// No description provided for @passwordInstanceUnproven.
///
/// In en, this message translates to:
/// **'The instance couldn\'t prove it knows your password, so this device trusts nothing it says. Try again; if it happens again, the instance isn\'t the one it claims to be.'**
String get passwordInstanceUnproven ;
/// No description provided for @passwordKeysMissing.
///
/// In en, this message translates to:
/// **'Your account\'s keys aren\'t on this device. Sign out and in again with your password here.'**
String get passwordKeysMissing ;
feat(app): a wait screen for accounts that wait for the operator (ADR 0021, step 1)
An account signed in or up to on an instance whose operator approves
new accounts, or one suspended, gets no session but a wait: the device
keeps its pass, words and handle in the keyring per instance
(SecureAccountWaitStore, InstanceStorage.waitOf), the auth status is
Waiting, and SessionGate shows WaitScreen in its own app, so the start
screen's sheets go with it. Every sign-in flow hands over to it: the
password's sign-in, sign-up, reset and account reset, SimpleLogin's
sign-in, sign-up and mending; their cubits show no problem then.
The wait screen shows the handle, the four words the operator reads out
for the person to match, and that Halkyon never asks anyone to read them
out (D33); it asks where the wait stands as it opens, every 30 seconds,
as the app comes back and when asked. A new account can be deleted with
its pass, a suspended one can't (ADR 0017). Approved, it leads to the
usual sign-in, once more as before (D31); a pass the instance knows no
more ends the wait. The start screen says where new accounts wait for
approval, and offers no Create an account where the operator takes none;
both refusals (closed, too many waiting) have their words, en and de.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 15:29:22 +01:00
/// A sign-up refused: the operator takes no new accounts (D19).
///
/// In en, this message translates to:
/// **'This instance takes no new accounts.'**
String get signUpsClosed ;
/// A sign-up refused: ten new accounts wait for the operator's approval already (D19).
///
/// In en, this message translates to:
feat(app,learn,todo): the operator's "This instance" card, screen and badge (ADR 0021, step 1)
Whoever operates the instance approves, refuses and suspends accounts
and names other operators in the app (D21 to D24, D34):
- halkyon_app: InstanceOperatorRepository on
ServerpodInstanceOperatorGateway (the module's instanceOperator
endpoint; refusals as OperatorFailed: notFound, isOperator,
lastOperator, notAllowed for 401 and 403, failed), in AppDependencies.
OperatingCubit counts the accounts that wait: asked as the person's
app opens, after every change, and every 5 minutes only while they
operate the instance, so nobody else's app keeps asking.
InstanceOperatorCard (or its tiles) and WaitingAccountsBadge show
nothing at all for anyone else. InstanceOperatorScreen, laid out as
Settings: the accounts that wait, each showing its four words to read
out before Approve (D33), Refuse for new ones after asking; who
operates, Take back after asking, the last always kept; Name an
operator by handle; Find an account by the start of its handle, with
Suspend after asking and Name as operator. English and German texts
("Admin" as the role word in German).
- Learn: the card right after the Sync card in Settings, which the
cogs' count leads to as their dot leads to Sync; the count on the
Settings cogs of Home and Profile.
- Todo: the tiles in the account sheet and the count on its button.
Todo's server is a member, whose `operating()` is always null, so
nothing shows there until it holds the register.
Tests: the gateway's mapping, the repository, both cubits with their
denials, the card, the badge and the screen; Learn's Settings shows the
card and the count to an operator and neither to anyone else.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 15:55:28 +01:00
/// **'Too many new accounts already wait for approval. Try again later.'**
feat(app): a wait screen for accounts that wait for the operator (ADR 0021, step 1)
An account signed in or up to on an instance whose operator approves
new accounts, or one suspended, gets no session but a wait: the device
keeps its pass, words and handle in the keyring per instance
(SecureAccountWaitStore, InstanceStorage.waitOf), the auth status is
Waiting, and SessionGate shows WaitScreen in its own app, so the start
screen's sheets go with it. Every sign-in flow hands over to it: the
password's sign-in, sign-up, reset and account reset, SimpleLogin's
sign-in, sign-up and mending; their cubits show no problem then.
The wait screen shows the handle, the four words the operator reads out
for the person to match, and that Halkyon never asks anyone to read them
out (D33); it asks where the wait stands as it opens, every 30 seconds,
as the app comes back and when asked. A new account can be deleted with
its pass, a suspended one can't (ADR 0017). Approved, it leads to the
usual sign-in, once more as before (D31); a pass the instance knows no
more ends the wait. The start screen says where new accounts wait for
approval, and offers no Create an account where the operator takes none;
both refusals (closed, too many waiting) have their words, en and de.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 15:29:22 +01:00
String get signUpsTooManyWaiting ;
2026-10-20 20:04:37 +02:00
/// No description provided for @passwordUnsafeCosts.
///
/// In en, this message translates to:
/// **'The instance asked for your password to be checked in a way no account uses, so nothing was sent. If it happens again, the instance isn\'t the one it claims to be.'**
String get passwordUnsafeCosts ;
feat(app): sign in with a password and TOTP on the device
ADR 0017, step 3: halkyon_app's side, which Learn and Todo take.
- The account's cryptography on the device (`SodiumAccountCrypto`):
halkyon_keys and SRP's client half, each step on an isolate of its
own. sodium hands out results as views of native memory its
finalizers free once the isolate that made them ends, so everything
crossing back is copied first: a view read freed memory and crashed
the process.
- `PasswordGateway` on the module's endpoints (steps without a session
through a client holding none; an authenticator set up with a session
granted but not yet the device's), and `PasswordRepository` running
the flows: signing in (M2 checked, the keys opened before anything is
trusted), signing up, a password for someone signed in, an account
never finished, the authenticators, recovery codes, changing the
password, a new recovery phrase, and getting back in.
- The unlocked keys in the system's keyring per instance and person
(`SecureAccountKeyStore`), gone once another person signs in or
nobody, and with the instance's removal.
- The screens: the start screen's Sign in and Create an account (and
when GitHub sign-in ends), the sign-in sheet (codes, recovery codes,
an account never finished, a lost authenticator), the account's setup
(the recovery phrase shown once, three random words typed back before
anything reaches the instance; the authenticator's QR code, drawn
with pub.dev's qr; the recovery codes), getting back in, and the
account section (`AccountSecuritySection`).
- `FileSaver` moves from Learn into halkyon_app, for the phrase and the
codes in Todo too; `AppDependencies` names it and `passwords`.
- halkyon_keys rebuilds account keys from the secret keys a device kept;
the key proof's message is in halkyon_base_client, checked against
the register's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-20 15:42:45 +02:00
/// No description provided for @passwordFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t reach the instance. Check the connection and try again.'**
String get passwordFailed ;
2026-10-20 21:25:47 +02:00
/// No description provided for @passwordMinLength.
///
/// In en, this message translates to:
/// **'At least {count} characters'**
String passwordMinLength ( int count ) ;
/// No description provided for @passwordTooShort.
///
/// In en, this message translates to:
/// **'A new password needs at least {count} characters: should the instance\'s database ever leak, a short one is guessed first.'**
String passwordTooShort ( int count ) ;
fix(auth): codes wait after ten wrong in a row, and the owner is told
Whoever knew the password could guess codes at 10 an hour forever,
unnoticed, each guess checked against every authenticator (the second
reading's M4). Every code an account's second factor refuses after a
right password (sign-in, a phrase's reset, a change) or from a signed-in
device (removing an authenticator, new recovery codes, the code from one
confirmed before) now counts, per account, from every network; after
every tenth in a row no code is taken (codesWait, saying until when), an
hour, then twice as long each time, a day at the most (longestCodeWait,
second_factor_waits.dart): at a day, ten guesses a day still have about a
1 % chance a year against one authenticator, so the first wait mails the
owner that someone knows their password. The instance keeps no address,
so sign-in and the phrase's reset now send the address the device typed
(and its language), used only for that mail after its hash is checked
against the account's. A code accepted clears it all.
Migration (module, Learn's and Todo's servers, Learn's device schema):
CREATE TABLE "second_factor_failures" ("id" bigserial PRIMARY KEY,
"authUserId" uuid NOT NULL, "failures" bigint NOT NULL, "waits"
bigint NOT NULL, "waitUntil" timestamp, "updatedAt" timestamp NOT
NULL) with a unique index on "authUserId": a new table, nothing
touched.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-20 21:47:38 +02:00
/// No description provided for @passwordCodesWait.
///
/// In en, this message translates to:
/// **'Too many wrong codes in a row: no code is taken until {date}. If they weren\'t yours, someone knows your password: change it then.'**
String passwordCodesWait ( String date ) ;
2026-10-20 21:59:42 +02:00
/// No description provided for @passwordDeleteIntro.
///
/// In en, this message translates to:
/// **'Your account goes with everything in it, in every service of this instance. To confirm, type your password and a code from your authenticator app.'**
String get passwordDeleteIntro ;
fix(auth): a session's wrong codes end the session, not the account's codes
Wrong codes for removing an authenticator, new recovery codes or confirming
one counted toward the account's waits (the re-review's third finding): a
stolen session alone, without the password, could keep the owner's codes
waiting for up to a day at a time, renewed without end, holding them out
of signing in and of changing the password, the way to end that session,
and nobody was mailed. Those codes now count per session, where the session
is (the register's own, or a member's, which forwards the code): each try
counted before its code is checked (one statement, so tries sent at once
count each), a code accepted clearing the count, and the tenth wrong in a
row ending the session (sessionEnded; the device says it was signed out).
A try that checked no code (a refusal of something else) is taken back.
The account's waits now count only codes after a right password, and the
per-account ten tries an hour, which a session could fill as well, go.
Migration (module, Learn's and Todo's servers, Learn's device schema):
CREATE TABLE "session_code_failures" ("id" bigserial PRIMARY KEY,
"sessionId" uuid NOT NULL, "authUserId" uuid NOT NULL, "failures"
bigint NOT NULL, "updatedAt" timestamp NOT NULL) with a unique index on
"sessionId": a new table, nothing touched. Its rows go with a code
accepted, with the person, and hourly once their session is gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-20 22:53:20 +02:00
/// No description provided for @passwordSessionEnded.
///
/// In en, this message translates to:
/// **'Too many wrong codes in a row from this device: it was signed out. Sign in again with your password and a code.'**
String get passwordSessionEnded ;
fix(auth): every call with a code after the password takes the address
Whoever guessed codes decided whether the owner was mailed (the re-review's
fourth finding): the address a sign-in sent was optional, so a guesser left
it out and no mail went, and a wait begun without a mail used up the one
mail there was. Every call carrying a code after the password or the phrase
proved (signing in, the phrase's reset, a change of password, a new phrase,
deletion) now takes the account's address and the language of its mails,
and is refused (wrongCredentials, as anything wrong after the password)
before any code is checked when the address isn't the account's; every
wait mails the owner, not only the first. The app sends the address the
person typed, or for changes while signed in the one this device signed in
with, and says so where it doesn't know it (addressUnknown).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-20 23:08:25 +02:00
/// No description provided for @passwordAddressUnknown.
///
/// In en, this message translates to:
2026-10-21 10:42:55 +02:00
/// **'Enter your account\'s email address: the code goes there.'**
fix(auth): every call with a code after the password takes the address
Whoever guessed codes decided whether the owner was mailed (the re-review's
fourth finding): the address a sign-in sent was optional, so a guesser left
it out and no mail went, and a wait begun without a mail used up the one
mail there was. Every call carrying a code after the password or the phrase
proved (signing in, the phrase's reset, a change of password, a new phrase,
deletion) now takes the account's address and the language of its mails,
and is refused (wrongCredentials, as anything wrong after the password)
before any code is checked when the address isn't the account's; every
wait mails the owner, not only the first. The app sends the address the
person typed, or for changes while signed in the one this device signed in
with, and says so where it doesn't know it (addressUnknown).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-20 23:08:25 +02:00
String get passwordAddressUnknown ;
2026-10-20 23:30:22 +02:00
/// No description provided for @passwordDeleteIntroByMail.
///
/// In en, this message translates to:
2026-10-21 10:42:55 +02:00
/// **'Your account goes with everything in it, in every service of this instance. None of its authenticators works now, so a code by mail confirms it: type your password, then the code we email to your address.'**
2026-10-20 23:30:22 +02:00
String get passwordDeleteIntroByMail ;
/// No description provided for @passwordDeleteSendCode.
///
/// In en, this message translates to:
/// **'Email me a code'**
String get passwordDeleteSendCode ;
2026-10-20 23:32:49 +02:00
fix(auth)!: the first authenticator takes the password, and a reset ends every session
The fourth reading's second finding: a stolen session outlived the reset
of the authenticators (carrying it out ended no session) and confirmed
the account's first authenticator on its own code alone, ahead of the
owner, so the owner's confirmation and sign-in were refused after.
- Carrying out a reset of the authenticators ends every session of the
account, on the register at once and on every member by notice, as the
other resets do; the sign-in that carries it out gets its session after
that moment (PasswordProof.sessionsEndedBefore, issueSessionAfter).
- Confirming the first authenticator takes the password's proof for a
change's challenge, without a code (proveOwnersPassword), counted per
session as the owner's proofs are: a session of a lost phone, or of a
setup never finished, claims no second factor.
The app sends the password with every confirmation: the sign-up's, a
reset's and the account section's (which asks for it), and a sign-in's
own unlock where the session is still held back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-21 11:43:22 +02:00
/// No description provided for @passwordAuthenticatorPasswordIntro.
///
/// In en, this message translates to:
/// **'To add it, confirm it is you with your password.'**
String get passwordAuthenticatorPasswordIntro ;
2026-10-21 12:10:38 +02:00
/// No description provided for @passwordWarningTitle.
///
/// In en, this message translates to:
/// **'Someone tried your password'**
String get passwordWarningTitle ;
/// No description provided for @passwordWarningBody.
///
/// In en, this message translates to:
/// **'On {date}, a device signed in to your account sent a wrong password or code ten times in a row, and was signed out. If that wasn\'t you, someone else may have that device or its session: change your password.'**
String passwordWarningBody ( String date ) ;
/// No description provided for @passwordWarningDismiss.
///
/// In en, this message translates to:
/// **'Dismiss'**
String get passwordWarningDismiss ;
/// No description provided for @passwordWarningLater.
///
/// In en, this message translates to:
/// **'Remind me later'**
String get passwordWarningLater ;
fix(auth): a reset waits while its owner is held out, and devices show it
The full reading's H1: an owner could be kept from calling a reset off, and
the reset then went through. Whoever knew the password and had the mailbox
asked an authenticators' reset and kept the codes waiting, so the owner's
sign-ins were refused until it took effect; whoever had the mailbox filled
the address's proofs ceiling from three networks and completed an account
reset. Now every hold-out of the owner after a reset was asked (a wait
that begins, an attempt a wait refuses, a sign-in or proof refused by the
address's ceiling) moves the account's resets not carried out to take
effect no sooner than 72 hours after it ends (pushOutResets). A reset
carried out (the password alone signing in until an authenticator is
confirmed) is never pushed, nor a lapsed one brought back.
Signed-in devices are told too: SignInWays carries when a waiting reset
of each kind takes effect, the account section shows them with Call it
off, which takes the owner's proof (password and code; password.
callOffResets, forwarded by members) and calls every reset off, as no wait
holds a signed-in owner's proof back. Texts in English and German.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-21 13:17:30 +02:00
/// No description provided for @passwordResetsTitle.
///
/// In en, this message translates to:
/// **'A reset is waiting'**
String get passwordResetsTitle ;
/// No description provided for @passwordResetsAuthenticator.
///
/// In en, this message translates to:
/// **'Your authenticators will be removed on {date}: someone asked with your password. If that wasn\'t you, call it off and change your password.'**
String passwordResetsAuthenticator ( String date ) ;
/// No description provided for @passwordResetsAccount.
///
/// In en, this message translates to:
/// **'Your account will get a new password and new keys on {date}, and what is encrypted under the old ones will be lost: someone asked with a code by mail. If that wasn\'t you, call it off.'**
String passwordResetsAccount ( String date ) ;
/// No description provided for @passwordResetsCallOff.
///
/// In en, this message translates to:
/// **'Call it off'**
String get passwordResetsCallOff ;
/// No description provided for @passwordCallOffTitle.
///
/// In en, this message translates to:
/// **'Call off the reset'**
String get passwordCallOffTitle ;
/// No description provided for @passwordCallOffIntro.
///
/// In en, this message translates to:
/// **'Your password and a code call off every reset waiting.'**
String get passwordCallOffIntro ;
fix(auth): GitHub-only accounts go at GitHub's end; only the anonymous add an address
The full reading's M1 and L6: once GitHub IDs were forgotten at
GITHUB_SIGN_IN_ENDS, an account that had signed in with GitHub alone
counted as anonymous, so a stolen session and any mailbox gave it a first
password and authenticator; and such accounts, with no way in left, were
never pruned. Now:
- at the end, the daily run that forgets the GitHub IDs first deletes the
accounts that sign in with GitHub alone (no address, so never a
password), with everything they have, members told (deletePerson); the
app tells them for the three months before that their account goes then
unless they set up a password (passwordSectionGitHubEndsDeletes, en/de);
- password.set adds an address without GitHub only for someone anonymous
where the session lives (anonymousHere, which a member tells the
register, as it tells freshGitHubSignIn); anyone else is refused before
any code is used.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-21 13:23:40 +02:00
2026-10-21 13:32:33 +02:00
/// No description provided for @passwordSignedInBefore.
///
/// In en, this message translates to:
/// **'Only a device signed in since then can dismiss this warning: sign in again on this one, or change your password.'**
String get passwordSignedInBefore ;
feat(app): signing in with SimpleLogin, its sheet and the start screen's offer (ADR 0020)
Where the instance offers SimpleLogin, the start screen offers it first,
saying that the instance keeps only the SimpleLogin ID, and opens a sheet
that waits for SimpleLogin's page in the browser (Cancel ends the wait),
then: for someone new, the choice between creating an account (the
suggested handle or another, checked with the instance, the password
twice, the recovery phrase shown once and three of its words typed back,
nothing reaching the instance before) and linking theirs again with its
handle and recovery phrase; for an account, its password opening its
keys; for an account of before, the keys made with the handle it lacks.
SimpleLoginCubit holds the steps, each refusal shown where it is mended
(a handle's on the handle, a sign-in to begin again on the browser's),
and closing the sheet stops waiting on the loopback.
The suggested handle is never checked: the instance suggests none that
anybody has, and every answer about a handle counts against its limit
(D5). The fake's handle rule is now the module's.
Learn and Todo build it with SimpleLogin's browser (halkyon_oidc_flutter,
now a direct dependency of both, with their names on the loopback's
page), their schemes and their loopback ports (43823, 43824), as the
instance's forwarding page has them. Their tests offer no SimpleLogin;
halkyon_app's test the sheet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 01:21:29 +02:00
/// No description provided for @simpleLoginContinue.
///
/// In en, this message translates to:
/// **'Continue with SimpleLogin'**
String get simpleLoginContinue ;
/// No description provided for @simpleLoginNote.
///
/// In en, this message translates to:
/// **'SimpleLogin is Proton\'s sign-in, and a Proton account works there too. It learns that you use {instance}, which keeps only your SimpleLogin ID: your name suggests a handle once, and nothing else is kept.'**
String simpleLoginNote ( String instance ) ;
/// No description provided for @simpleLoginTitle.
///
/// In en, this message translates to:
/// **'Sign in with SimpleLogin'**
String get simpleLoginTitle ;
/// No description provided for @simpleLoginAddTitle.
///
/// In en, this message translates to:
/// **'Add SimpleLogin'**
String get simpleLoginAddTitle ;
/// No description provided for @simpleLoginInBrowser.
///
/// In en, this message translates to:
/// **'Continue in your browser.'**
String get simpleLoginInBrowser ;
/// No description provided for @simpleLoginNewcomerTitle.
///
/// In en, this message translates to:
/// **'New here?'**
String get simpleLoginNewcomerTitle ;
/// No description provided for @simpleLoginNewcomerIntro.
///
/// In en, this message translates to:
/// **'Your SimpleLogin account has no account on {instance} yet.'**
String simpleLoginNewcomerIntro ( String instance ) ;
/// No description provided for @simpleLoginCreateHint.
///
/// In en, this message translates to:
/// **'Choose a handle and a password; you get a recovery phrase.'**
String get simpleLoginCreateHint ;
/// No description provided for @simpleLoginHaveOne.
///
/// In en, this message translates to:
/// **'I already have one'**
String get simpleLoginHaveOne ;
/// No description provided for @simpleLoginHaveOneHint.
///
/// In en, this message translates to:
/// **'Link it again with your handle and your recovery phrase.'**
String get simpleLoginHaveOneHint ;
feat(base)!: a handle at password sign-up (ADR 0020, D26, D28–D30)
Every new account gets a handle, and an account without one gets its
first as it sets a password. The handle is private until its person
goes public (D5).
- `passwordSignIn.signUp` takes a handle. `passwordSignIn.checkHandle`
says whether a sign-up could take one, only while that sign-up's
mailed code waits (`codeExpired` otherwise), counted per code request
(30 an hour) and per network (60) (D29). Members ask the register.
- `password.set` takes the first handle of an account without one
(D28); `SignInWays.handle` tells the app whether there is one.
- Each checks the handle before the code is used, so a refusal leaves
the code for another try. An existing account at the address keeps
its own handle where it has one, and the typed handle is still
checked, so the answer says nothing about the address.
- `first_handle.dart` holds what SimpleLogin, the keys and passwords
share: `refusalOfNewHandle`, `insertFirstProfile`, `hasHandle`.
- halkyon_app: one `HandleField` with its `HandleCheckCubit` (asked
300 ms after typing pauses, only about the handle typed last; the
instance's suggestion is free without asking) (D30). The password
setup asks for the handle between the phrase check and the code, and
a refusal at the code sends back to it. SimpleLogin's sign-up and
keys steps use the same field and now check as it is typed.
- The module's README and ADR 0020 (D29, D30, the built state) amended.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 18:09:04 +02:00
/// No description provided for @handleIntro.
feat(app): signing in with SimpleLogin, its sheet and the start screen's offer (ADR 0020)
Where the instance offers SimpleLogin, the start screen offers it first,
saying that the instance keeps only the SimpleLogin ID, and opens a sheet
that waits for SimpleLogin's page in the browser (Cancel ends the wait),
then: for someone new, the choice between creating an account (the
suggested handle or another, checked with the instance, the password
twice, the recovery phrase shown once and three of its words typed back,
nothing reaching the instance before) and linking theirs again with its
handle and recovery phrase; for an account, its password opening its
keys; for an account of before, the keys made with the handle it lacks.
SimpleLoginCubit holds the steps, each refusal shown where it is mended
(a handle's on the handle, a sign-in to begin again on the browser's),
and closing the sheet stops waiting on the loopback.
The suggested handle is never checked: the instance suggests none that
anybody has, and every answer about a handle counts against its limit
(D5). The fake's handle rule is now the module's.
Learn and Todo build it with SimpleLogin's browser (halkyon_oidc_flutter,
now a direct dependency of both, with their names on the loopback's
page), their schemes and their loopback ports (43823, 43824), as the
instance's forwarding page has them. Their tests offer no SimpleLogin;
halkyon_app's test the sheet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 01:21:29 +02:00
///
/// In en, this message translates to:
/// **'Others find you by your handle. It stays private until you publish something or fill in your profile, and can change until your first publish.'**
feat(base)!: a handle at password sign-up (ADR 0020, D26, D28–D30)
Every new account gets a handle, and an account without one gets its
first as it sets a password. The handle is private until its person
goes public (D5).
- `passwordSignIn.signUp` takes a handle. `passwordSignIn.checkHandle`
says whether a sign-up could take one, only while that sign-up's
mailed code waits (`codeExpired` otherwise), counted per code request
(30 an hour) and per network (60) (D29). Members ask the register.
- `password.set` takes the first handle of an account without one
(D28); `SignInWays.handle` tells the app whether there is one.
- Each checks the handle before the code is used, so a refusal leaves
the code for another try. An existing account at the address keeps
its own handle where it has one, and the typed handle is still
checked, so the answer says nothing about the address.
- `first_handle.dart` holds what SimpleLogin, the keys and passwords
share: `refusalOfNewHandle`, `insertFirstProfile`, `hasHandle`.
- halkyon_app: one `HandleField` with its `HandleCheckCubit` (asked
300 ms after typing pauses, only about the handle typed last; the
instance's suggestion is free without asking) (D30). The password
setup asks for the handle between the phrase check and the code, and
a refusal at the code sends back to it. SimpleLogin's sign-up and
keys steps use the same field and now check as it is typed.
- The module's README and ADR 0020 (D29, D30, the built state) amended.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 18:09:04 +02:00
String get handleIntro ;
feat(app): signing in with SimpleLogin, its sheet and the start screen's offer (ADR 0020)
Where the instance offers SimpleLogin, the start screen offers it first,
saying that the instance keeps only the SimpleLogin ID, and opens a sheet
that waits for SimpleLogin's page in the browser (Cancel ends the wait),
then: for someone new, the choice between creating an account (the
suggested handle or another, checked with the instance, the password
twice, the recovery phrase shown once and three of its words typed back,
nothing reaching the instance before) and linking theirs again with its
handle and recovery phrase; for an account, its password opening its
keys; for an account of before, the keys made with the handle it lacks.
SimpleLoginCubit holds the steps, each refusal shown where it is mended
(a handle's on the handle, a sign-in to begin again on the browser's),
and closing the sheet stops waiting on the loopback.
The suggested handle is never checked: the instance suggests none that
anybody has, and every answer about a handle counts against its limit
(D5). The fake's handle rule is now the module's.
Learn and Todo build it with SimpleLogin's browser (halkyon_oidc_flutter,
now a direct dependency of both, with their names on the loopback's
page), their schemes and their loopback ports (43823, 43824), as the
instance's forwarding page has them. Their tests offer no SimpleLogin;
halkyon_app's test the sheet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 01:21:29 +02:00
feat(base)!: a handle at password sign-up (ADR 0020, D26, D28–D30)
Every new account gets a handle, and an account without one gets its
first as it sets a password. The handle is private until its person
goes public (D5).
- `passwordSignIn.signUp` takes a handle. `passwordSignIn.checkHandle`
says whether a sign-up could take one, only while that sign-up's
mailed code waits (`codeExpired` otherwise), counted per code request
(30 an hour) and per network (60) (D29). Members ask the register.
- `password.set` takes the first handle of an account without one
(D28); `SignInWays.handle` tells the app whether there is one.
- Each checks the handle before the code is used, so a refusal leaves
the code for another try. An existing account at the address keeps
its own handle where it has one, and the typed handle is still
checked, so the answer says nothing about the address.
- `first_handle.dart` holds what SimpleLogin, the keys and passwords
share: `refusalOfNewHandle`, `insertFirstProfile`, `hasHandle`.
- halkyon_app: one `HandleField` with its `HandleCheckCubit` (asked
300 ms after typing pauses, only about the handle typed last; the
instance's suggestion is free without asking) (D30). The password
setup asks for the handle between the phrase check and the code, and
a refusal at the code sends back to it. SimpleLogin's sign-up and
keys steps use the same field and now check as it is typed.
- The module's README and ADR 0020 (D29, D30, the built state) amended.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 18:09:04 +02:00
/// No description provided for @handleLabel.
feat(app): signing in with SimpleLogin, its sheet and the start screen's offer (ADR 0020)
Where the instance offers SimpleLogin, the start screen offers it first,
saying that the instance keeps only the SimpleLogin ID, and opens a sheet
that waits for SimpleLogin's page in the browser (Cancel ends the wait),
then: for someone new, the choice between creating an account (the
suggested handle or another, checked with the instance, the password
twice, the recovery phrase shown once and three of its words typed back,
nothing reaching the instance before) and linking theirs again with its
handle and recovery phrase; for an account, its password opening its
keys; for an account of before, the keys made with the handle it lacks.
SimpleLoginCubit holds the steps, each refusal shown where it is mended
(a handle's on the handle, a sign-in to begin again on the browser's),
and closing the sheet stops waiting on the loopback.
The suggested handle is never checked: the instance suggests none that
anybody has, and every answer about a handle counts against its limit
(D5). The fake's handle rule is now the module's.
Learn and Todo build it with SimpleLogin's browser (halkyon_oidc_flutter,
now a direct dependency of both, with their names on the loopback's
page), their schemes and their loopback ports (43823, 43824), as the
instance's forwarding page has them. Their tests offer no SimpleLogin;
halkyon_app's test the sheet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 01:21:29 +02:00
///
/// In en, this message translates to:
/// **'Handle'**
feat(base)!: a handle at password sign-up (ADR 0020, D26, D28–D30)
Every new account gets a handle, and an account without one gets its
first as it sets a password. The handle is private until its person
goes public (D5).
- `passwordSignIn.signUp` takes a handle. `passwordSignIn.checkHandle`
says whether a sign-up could take one, only while that sign-up's
mailed code waits (`codeExpired` otherwise), counted per code request
(30 an hour) and per network (60) (D29). Members ask the register.
- `password.set` takes the first handle of an account without one
(D28); `SignInWays.handle` tells the app whether there is one.
- Each checks the handle before the code is used, so a refusal leaves
the code for another try. An existing account at the address keeps
its own handle where it has one, and the typed handle is still
checked, so the answer says nothing about the address.
- `first_handle.dart` holds what SimpleLogin, the keys and passwords
share: `refusalOfNewHandle`, `insertFirstProfile`, `hasHandle`.
- halkyon_app: one `HandleField` with its `HandleCheckCubit` (asked
300 ms after typing pauses, only about the handle typed last; the
instance's suggestion is free without asking) (D30). The password
setup asks for the handle between the phrase check and the code, and
a refusal at the code sends back to it. SimpleLogin's sign-up and
keys steps use the same field and now check as it is typed.
- The module's README and ADR 0020 (D29, D30, the built state) amended.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 18:09:04 +02:00
String get handleLabel ;
feat(app): signing in with SimpleLogin, its sheet and the start screen's offer (ADR 0020)
Where the instance offers SimpleLogin, the start screen offers it first,
saying that the instance keeps only the SimpleLogin ID, and opens a sheet
that waits for SimpleLogin's page in the browser (Cancel ends the wait),
then: for someone new, the choice between creating an account (the
suggested handle or another, checked with the instance, the password
twice, the recovery phrase shown once and three of its words typed back,
nothing reaching the instance before) and linking theirs again with its
handle and recovery phrase; for an account, its password opening its
keys; for an account of before, the keys made with the handle it lacks.
SimpleLoginCubit holds the steps, each refusal shown where it is mended
(a handle's on the handle, a sign-in to begin again on the browser's),
and closing the sheet stops waiting on the loopback.
The suggested handle is never checked: the instance suggests none that
anybody has, and every answer about a handle counts against its limit
(D5). The fake's handle rule is now the module's.
Learn and Todo build it with SimpleLogin's browser (halkyon_oidc_flutter,
now a direct dependency of both, with their names on the loopback's
page), their schemes and their loopback ports (43823, 43824), as the
instance's forwarding page has them. Their tests offer no SimpleLogin;
halkyon_app's test the sheet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 01:21:29 +02:00
feat(base)!: a handle at password sign-up (ADR 0020, D26, D28–D30)
Every new account gets a handle, and an account without one gets its
first as it sets a password. The handle is private until its person
goes public (D5).
- `passwordSignIn.signUp` takes a handle. `passwordSignIn.checkHandle`
says whether a sign-up could take one, only while that sign-up's
mailed code waits (`codeExpired` otherwise), counted per code request
(30 an hour) and per network (60) (D29). Members ask the register.
- `password.set` takes the first handle of an account without one
(D28); `SignInWays.handle` tells the app whether there is one.
- Each checks the handle before the code is used, so a refusal leaves
the code for another try. An existing account at the address keeps
its own handle where it has one, and the typed handle is still
checked, so the answer says nothing about the address.
- `first_handle.dart` holds what SimpleLogin, the keys and passwords
share: `refusalOfNewHandle`, `insertFirstProfile`, `hasHandle`.
- halkyon_app: one `HandleField` with its `HandleCheckCubit` (asked
300 ms after typing pauses, only about the handle typed last; the
instance's suggestion is free without asking) (D30). The password
setup asks for the handle between the phrase check and the code, and
a refusal at the code sends back to it. SimpleLogin's sign-up and
keys steps use the same field and now check as it is typed.
- The module's README and ADR 0020 (D29, D30, the built state) amended.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 18:09:04 +02:00
/// No description provided for @handleFree.
feat(app): signing in with SimpleLogin, its sheet and the start screen's offer (ADR 0020)
Where the instance offers SimpleLogin, the start screen offers it first,
saying that the instance keeps only the SimpleLogin ID, and opens a sheet
that waits for SimpleLogin's page in the browser (Cancel ends the wait),
then: for someone new, the choice between creating an account (the
suggested handle or another, checked with the instance, the password
twice, the recovery phrase shown once and three of its words typed back,
nothing reaching the instance before) and linking theirs again with its
handle and recovery phrase; for an account, its password opening its
keys; for an account of before, the keys made with the handle it lacks.
SimpleLoginCubit holds the steps, each refusal shown where it is mended
(a handle's on the handle, a sign-in to begin again on the browser's),
and closing the sheet stops waiting on the loopback.
The suggested handle is never checked: the instance suggests none that
anybody has, and every answer about a handle counts against its limit
(D5). The fake's handle rule is now the module's.
Learn and Todo build it with SimpleLogin's browser (halkyon_oidc_flutter,
now a direct dependency of both, with their names on the loopback's
page), their schemes and their loopback ports (43823, 43824), as the
instance's forwarding page has them. Their tests offer no SimpleLogin;
halkyon_app's test the sheet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 01:21:29 +02:00
///
/// In en, this message translates to:
/// **'@{handle} is free'**
feat(base)!: a handle at password sign-up (ADR 0020, D26, D28–D30)
Every new account gets a handle, and an account without one gets its
first as it sets a password. The handle is private until its person
goes public (D5).
- `passwordSignIn.signUp` takes a handle. `passwordSignIn.checkHandle`
says whether a sign-up could take one, only while that sign-up's
mailed code waits (`codeExpired` otherwise), counted per code request
(30 an hour) and per network (60) (D29). Members ask the register.
- `password.set` takes the first handle of an account without one
(D28); `SignInWays.handle` tells the app whether there is one.
- Each checks the handle before the code is used, so a refusal leaves
the code for another try. An existing account at the address keeps
its own handle where it has one, and the typed handle is still
checked, so the answer says nothing about the address.
- `first_handle.dart` holds what SimpleLogin, the keys and passwords
share: `refusalOfNewHandle`, `insertFirstProfile`, `hasHandle`.
- halkyon_app: one `HandleField` with its `HandleCheckCubit` (asked
300 ms after typing pauses, only about the handle typed last; the
instance's suggestion is free without asking) (D30). The password
setup asks for the handle between the phrase check and the code, and
a refusal at the code sends back to it. SimpleLogin's sign-up and
keys steps use the same field and now check as it is typed.
- The module's README and ADR 0020 (D29, D30, the built state) amended.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 18:09:04 +02:00
String handleFree ( String handle ) ;
/// No description provided for @handleChecking.
///
/// In en, this message translates to:
/// **'Checking…'**
String get handleChecking ;
feat(app): signing in with SimpleLogin, its sheet and the start screen's offer (ADR 0020)
Where the instance offers SimpleLogin, the start screen offers it first,
saying that the instance keeps only the SimpleLogin ID, and opens a sheet
that waits for SimpleLogin's page in the browser (Cancel ends the wait),
then: for someone new, the choice between creating an account (the
suggested handle or another, checked with the instance, the password
twice, the recovery phrase shown once and three of its words typed back,
nothing reaching the instance before) and linking theirs again with its
handle and recovery phrase; for an account, its password opening its
keys; for an account of before, the keys made with the handle it lacks.
SimpleLoginCubit holds the steps, each refusal shown where it is mended
(a handle's on the handle, a sign-in to begin again on the browser's),
and closing the sheet stops waiting on the loopback.
The suggested handle is never checked: the instance suggests none that
anybody has, and every answer about a handle counts against its limit
(D5). The fake's handle rule is now the module's.
Learn and Todo build it with SimpleLogin's browser (halkyon_oidc_flutter,
now a direct dependency of both, with their names on the loopback's
page), their schemes and their loopback ports (43823, 43824), as the
instance's forwarding page has them. Their tests offer no SimpleLogin;
halkyon_app's test the sheet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 01:21:29 +02:00
/// No description provided for @simpleLoginPasswordIntro.
///
/// In en, this message translates to:
/// **'Choose a password: you type it when you sign in on another device. It never leaves this device: the instance can\'t read it, and can\'t reset it without what it protects being lost.'**
String get simpleLoginPasswordIntro ;
/// No description provided for @simpleLoginCreate.
///
/// In en, this message translates to:
/// **'Create the account'**
String get simpleLoginCreate ;
/// No description provided for @simpleLoginUnlockTitle.
///
/// In en, this message translates to:
/// **'Your password'**
String get simpleLoginUnlockTitle ;
/// No description provided for @simpleLoginUnlockIntroAs.
///
/// In en, this message translates to:
/// **'Signed in with SimpleLogin as @{handle}. Your password opens your keys on this device.'**
String simpleLoginUnlockIntroAs ( String handle ) ;
/// No description provided for @simpleLoginUnlockIntro.
///
/// In en, this message translates to:
/// **'Signed in with SimpleLogin. Your password opens your keys on this device.'**
String get simpleLoginUnlockIntro ;
/// No description provided for @simpleLoginOpen.
///
/// In en, this message translates to:
/// **'Open'**
String get simpleLoginOpen ;
/// No description provided for @simpleLoginMendTitle.
///
/// In en, this message translates to:
/// **'Link your account'**
String get simpleLoginMendTitle ;
/// No description provided for @simpleLoginMendIntro.
///
/// In en, this message translates to:
/// **'Your SimpleLogin account isn\'t the one your account knows any more. Link them again with your handle and your recovery phrase: your other devices are then signed out.'**
String get simpleLoginMendIntro ;
/// No description provided for @simpleLoginMend.
///
/// In en, this message translates to:
/// **'Link'**
String get simpleLoginMend ;
/// No description provided for @simpleLoginWrongPassword.
///
/// In en, this message translates to:
/// **'The password isn\'t right.'**
String get simpleLoginWrongPassword ;
/// No description provided for @simpleLoginMendDoesNotFit.
///
/// In en, this message translates to:
/// **'The handle or the recovery phrase isn\'t right.'**
String get simpleLoginMendDoesNotFit ;
feat(app): a lost password with the recovery phrase in SimpleLogin's sheet (ADR 0020, 7)
An account that signs in with SimpleLogin opens its keys with its
password. Where that is lost, the unlock step's "Forgot your password?"
opens one step: the recovery phrase, and a new password twice. There is
no address and no code: the SimpleLogin sign-in of a moment ago stands
for them (D20).
- The device opens the keys with the phrase (`keys.beginPhrase`) and
wraps them under the new password (`AccountCrypto.newKeysPassword`).
It then signs the phrase's proof bound to that password
(`signKeysResetProof`), which `keys.resetPassword` takes with the
sign-in. The keys stay, and every other session ends.
- A phrase that doesn't open the keys is refused on the device, and the
instance gets no proof to count. Two passwords that differ are
refused before anything is sent.
- The gateway's `beginPhrase` and `resetPassword` run on the account's
own session, not yet the device's. The repository's `resetPassword`
ties them together, and the cubit and sheet have their `forgot` step.
Three texts are new, in English and German.
- halkyon_base_server exports `isKeysPassword` for the interop test,
which checks against the register's checks, with real libsodium, what
the device sends.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 21:25:47 +02:00
/// No description provided for @simpleLoginForgotTitle.
///
/// In en, this message translates to:
/// **'A new password'**
String get simpleLoginForgotTitle ;
/// No description provided for @simpleLoginForgotIntro.
///
/// In en, this message translates to:
/// **'Your recovery phrase opens your keys, which your new password then protects. Everything stays; your other devices are signed out.'**
String get simpleLoginForgotIntro ;
/// No description provided for @simpleLoginForgotDoesNotFit.
///
/// In en, this message translates to:
/// **'The recovery phrase isn\'t right.'**
String get simpleLoginForgotDoesNotFit ;
feat(app): a reset of the keys without the phrase, its banner, and the keys' proof key for a password that signs in (ADR 0020, D31–D35)
SimpleLogin's sheet, its password and its recovery phrase both lost:
under the lost-password step's phrase field, Lost your recovery phrase
too? says what stays and what is lost and asks for new keys
(`keys.startReset`) with the sign-in of a moment ago, which the device
then drops. The next sign-in says when while it waits, and once due
offers new keys, the sign-up's own password and phrase steps
(`keys.completeReset`), or the password, which calls it off (D31).
Every app's home shows `KeysResetBanner` where "too old" shows, and the
account section's SimpleLogin row the same words (`KeysResetNotice`),
each with Call it off, the password's proof alone
(`keys.callOffResets`, D32), and, once due on a device that holds the
keys, Finish here, which wraps the same keys anew (D33, D35). Both show
only a reset of an account that signs in with SimpleLogin alone; ADR
0017's resets show in its own section, which now shares `AlertBox` with
them.
An account whose password signs in has no Forgot your password? there,
and is asked its password and a code once while its keys lack the proof
key, at a sign-in or on adding SimpleLogin (`password.setProofKey`,
D34). `PasswordUnlock` carries the proof key, which its forgetting
leaves, and the keys' password proves for a purpose given.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 22:37:17 +02:00
/// No description provided for @simpleLoginResetWaiting.
///
/// In en, this message translates to:
/// **'New keys were asked for: from {date}, a SimpleLogin sign-in can set them up. Your password opening your keys calls it off.'**
String simpleLoginResetWaiting ( String date ) ;
/// No description provided for @simpleLoginPhraseLost.
///
/// In en, this message translates to:
/// **'Lost your recovery phrase too?'**
String get simpleLoginPhraseLost ;
/// No description provided for @simpleLoginResetTitle.
///
/// In en, this message translates to:
/// **'New keys'**
String get simpleLoginResetTitle ;
/// No description provided for @simpleLoginResetIntro.
///
/// In en, this message translates to:
/// **'Without your password or your recovery phrase, nobody can open your keys, not even {instance}. New ones can be set up 72 hours from now, when you sign in with SimpleLogin again. Your handle, your profile and what you published stay; what was encrypted under your old keys is lost, unless you finish on a device still signed in, which keeps them.'**
String simpleLoginResetIntro ( String instance ) ;
/// No description provided for @simpleLoginResetIntroDevices.
///
/// In en, this message translates to:
/// **'Meanwhile your signed-in devices show the reset, and your password calls it off there, should someone else have asked for it.'**
String get simpleLoginResetIntroDevices ;
/// No description provided for @simpleLoginResetAsk.
///
/// In en, this message translates to:
/// **'Ask for new keys'**
String get simpleLoginResetAsk ;
/// No description provided for @simpleLoginResetAskedTitle.
///
/// In en, this message translates to:
/// **'New keys asked for'**
String get simpleLoginResetAskedTitle ;
/// No description provided for @simpleLoginResetAskedIntro.
///
/// In en, this message translates to:
/// **'From {date}, sign in with SimpleLogin again to set them up. Should you find your password or your recovery phrase before then, opening your keys with it calls the reset off.'**
String simpleLoginResetAskedIntro ( String date ) ;
/// No description provided for @simpleLoginResetDueTitle.
///
/// In en, this message translates to:
/// **'Your new keys'**
String get simpleLoginResetDueTitle ;
/// No description provided for @simpleLoginResetDueIntro.
///
/// In en, this message translates to:
/// **'The new keys you asked for can be set up now. Or open your old keys with your password, which calls the reset off.'**
String get simpleLoginResetDueIntro ;
/// No description provided for @simpleLoginResetNewKeys.
///
/// In en, this message translates to:
/// **'Set up new keys'**
String get simpleLoginResetNewKeys ;
/// No description provided for @simpleLoginResetNewKeysHint.
///
/// In en, this message translates to:
/// **'A new password and recovery phrase. What was encrypted under your old keys is lost, unless you finish on a device still signed in.'**
String get simpleLoginResetNewKeysHint ;
/// No description provided for @simpleLoginResetHavePassword.
///
/// In en, this message translates to:
/// **'I have my password'**
String get simpleLoginResetHavePassword ;
/// No description provided for @simpleLoginResetHavePasswordHint.
///
/// In en, this message translates to:
/// **'Your keys open, and stay.'**
String get simpleLoginResetHavePasswordHint ;
/// No description provided for @simpleLoginResetKeepIntro.
///
/// In en, this message translates to:
/// **'Choose a new password: your keys stay, under it and a new recovery phrase, so nothing is lost. Your other devices are signed out.'**
String get simpleLoginResetKeepIntro ;
/// No description provided for @simpleLoginResetFinish.
///
/// In en, this message translates to:
/// **'Set up the keys'**
String get simpleLoginResetFinish ;
/// No description provided for @simpleLoginProofKeyTitle.
///
/// In en, this message translates to:
/// **'Your password and a code'**
String get simpleLoginProofKeyTitle ;
/// No description provided for @simpleLoginProofKeyIntroAs.
///
/// In en, this message translates to:
/// **'Signed in with SimpleLogin as @{handle}. Your account also signs in with a password: type it and a code from your authenticator once, and your password opens your keys after SimpleLogin from then on.'**
String simpleLoginProofKeyIntroAs ( String handle ) ;
/// No description provided for @simpleLoginProofKeyIntro.
///
/// In en, this message translates to:
/// **'Your account also signs in with a password: type it and a code from your authenticator once, and your password opens your keys after SimpleLogin from then on.'**
String get simpleLoginProofKeyIntro ;
/// No description provided for @simpleLoginResetBanner.
///
/// In en, this message translates to:
/// **'New keys were asked for with your SimpleLogin account: from {date}, whoever has it can set them up. Unless that was you, call it off with your password.'**
String simpleLoginResetBanner ( String date ) ;
/// No description provided for @simpleLoginResetBannerDue.
///
/// In en, this message translates to:
/// **'New keys were asked for with your SimpleLogin account, and whoever has it can set them up now. Unless that was you, call it off with your password.'**
String get simpleLoginResetBannerDue ;
/// No description provided for @simpleLoginResetBannerDueHere.
///
/// In en, this message translates to:
/// **'New keys were asked for with your SimpleLogin account and can be set up now. If that was you, finish here: your keys stay. If not, call it off with your password.'**
String get simpleLoginResetBannerDueHere ;
/// No description provided for @simpleLoginResetFinishHere.
///
/// In en, this message translates to:
/// **'Finish here'**
String get simpleLoginResetFinishHere ;
/// No description provided for @simpleLoginCallOffIntro.
///
/// In en, this message translates to:
/// **'Your password calls off the reset of your keys.'**
String get simpleLoginCallOffIntro ;
/// No description provided for @simpleLoginResetCalledOff.
///
/// In en, this message translates to:
/// **'Reset called off.'**
String get simpleLoginResetCalledOff ;
/// No description provided for @simpleLoginResetFinished.
///
/// In en, this message translates to:
/// **'New password and recovery phrase set.'**
String get simpleLoginResetFinished ;
feat(app): signing in with SimpleLogin, its sheet and the start screen's offer (ADR 0020)
Where the instance offers SimpleLogin, the start screen offers it first,
saying that the instance keeps only the SimpleLogin ID, and opens a sheet
that waits for SimpleLogin's page in the browser (Cancel ends the wait),
then: for someone new, the choice between creating an account (the
suggested handle or another, checked with the instance, the password
twice, the recovery phrase shown once and three of its words typed back,
nothing reaching the instance before) and linking theirs again with its
handle and recovery phrase; for an account, its password opening its
keys; for an account of before, the keys made with the handle it lacks.
SimpleLoginCubit holds the steps, each refusal shown where it is mended
(a handle's on the handle, a sign-in to begin again on the browser's),
and closing the sheet stops waiting on the loopback.
The suggested handle is never checked: the instance suggests none that
anybody has, and every answer about a handle counts against its limit
(D5). The fake's handle rule is now the module's.
Learn and Todo build it with SimpleLogin's browser (halkyon_oidc_flutter,
now a direct dependency of both, with their names on the loopback's
page), their schemes and their loopback ports (43823, 43824), as the
instance's forwarding page has them. Their tests offer no SimpleLogin;
halkyon_app's test the sheet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 01:21:29 +02:00
/// No description provided for @simpleLoginUnavailable.
///
/// In en, this message translates to:
/// **'This instance doesn\'t offer sign-in with SimpleLogin.'**
String get simpleLoginUnavailable ;
/// No description provided for @simpleLoginDenied.
///
/// In en, this message translates to:
/// **'SimpleLogin\'s sign-in was declined, so nothing was shared.'**
String get simpleLoginDenied ;
/// No description provided for @simpleLoginNoBrowser.
///
/// In en, this message translates to:
/// **'There\'s no browser to open SimpleLogin\'s page in.'**
String get simpleLoginNoBrowser ;
/// No description provided for @simpleLoginStartAgain.
///
/// In en, this message translates to:
/// **'The sign-in took too long, or another one took its place. Begin it again.'**
String get simpleLoginStartAgain ;
/// No description provided for @simpleLoginRejected.
///
/// In en, this message translates to:
/// **'SimpleLogin didn\'t confirm the sign-in. Begin it again.'**
String get simpleLoginRejected ;
/// No description provided for @simpleLoginProviderUnavailable.
///
/// In en, this message translates to:
/// **'The instance can\'t reach SimpleLogin right now. Try again later.'**
String get simpleLoginProviderUnavailable ;
/// No description provided for @simpleLoginTakenByAnotherPerson.
///
/// In en, this message translates to:
/// **'This SimpleLogin account belongs to another account here.'**
String get simpleLoginTakenByAnotherPerson ;
/// No description provided for @simpleLoginOtherIdentityLinked.
///
/// In en, this message translates to:
/// **'Your account has another SimpleLogin account already.'**
String get simpleLoginOtherIdentityLinked ;
/// No description provided for @simpleLoginTooManyAttempts.
///
/// In en, this message translates to:
/// **'Too many sign-ins began from this network. Try again in an hour.'**
String get simpleLoginTooManyAttempts ;
feat(base)!: a handle at password sign-up (ADR 0020, D26, D28–D30)
Every new account gets a handle, and an account without one gets its
first as it sets a password. The handle is private until its person
goes public (D5).
- `passwordSignIn.signUp` takes a handle. `passwordSignIn.checkHandle`
says whether a sign-up could take one, only while that sign-up's
mailed code waits (`codeExpired` otherwise), counted per code request
(30 an hour) and per network (60) (D29). Members ask the register.
- `password.set` takes the first handle of an account without one
(D28); `SignInWays.handle` tells the app whether there is one.
- Each checks the handle before the code is used, so a refusal leaves
the code for another try. An existing account at the address keeps
its own handle where it has one, and the typed handle is still
checked, so the answer says nothing about the address.
- `first_handle.dart` holds what SimpleLogin, the keys and passwords
share: `refusalOfNewHandle`, `insertFirstProfile`, `hasHandle`.
- halkyon_app: one `HandleField` with its `HandleCheckCubit` (asked
300 ms after typing pauses, only about the handle typed last; the
instance's suggestion is free without asking) (D30). The password
setup asks for the handle between the phrase check and the code, and
a refusal at the code sends back to it. SimpleLogin's sign-up and
keys steps use the same field and now check as it is typed.
- The module's README and ADR 0020 (D29, D30, the built state) amended.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 18:09:04 +02:00
/// No description provided for @handleInvalid.
feat(app): signing in with SimpleLogin, its sheet and the start screen's offer (ADR 0020)
Where the instance offers SimpleLogin, the start screen offers it first,
saying that the instance keeps only the SimpleLogin ID, and opens a sheet
that waits for SimpleLogin's page in the browser (Cancel ends the wait),
then: for someone new, the choice between creating an account (the
suggested handle or another, checked with the instance, the password
twice, the recovery phrase shown once and three of its words typed back,
nothing reaching the instance before) and linking theirs again with its
handle and recovery phrase; for an account, its password opening its
keys; for an account of before, the keys made with the handle it lacks.
SimpleLoginCubit holds the steps, each refusal shown where it is mended
(a handle's on the handle, a sign-in to begin again on the browser's),
and closing the sheet stops waiting on the loopback.
The suggested handle is never checked: the instance suggests none that
anybody has, and every answer about a handle counts against its limit
(D5). The fake's handle rule is now the module's.
Learn and Todo build it with SimpleLogin's browser (halkyon_oidc_flutter,
now a direct dependency of both, with their names on the loopback's
page), their schemes and their loopback ports (43823, 43824), as the
instance's forwarding page has them. Their tests offer no SimpleLogin;
halkyon_app's test the sheet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 01:21:29 +02:00
///
/// In en, this message translates to:
/// **'A handle is 1 to 30 letters (a to z), digits or underscores.'**
feat(base)!: a handle at password sign-up (ADR 0020, D26, D28–D30)
Every new account gets a handle, and an account without one gets its
first as it sets a password. The handle is private until its person
goes public (D5).
- `passwordSignIn.signUp` takes a handle. `passwordSignIn.checkHandle`
says whether a sign-up could take one, only while that sign-up's
mailed code waits (`codeExpired` otherwise), counted per code request
(30 an hour) and per network (60) (D29). Members ask the register.
- `password.set` takes the first handle of an account without one
(D28); `SignInWays.handle` tells the app whether there is one.
- Each checks the handle before the code is used, so a refusal leaves
the code for another try. An existing account at the address keeps
its own handle where it has one, and the typed handle is still
checked, so the answer says nothing about the address.
- `first_handle.dart` holds what SimpleLogin, the keys and passwords
share: `refusalOfNewHandle`, `insertFirstProfile`, `hasHandle`.
- halkyon_app: one `HandleField` with its `HandleCheckCubit` (asked
300 ms after typing pauses, only about the handle typed last; the
instance's suggestion is free without asking) (D30). The password
setup asks for the handle between the phrase check and the code, and
a refusal at the code sends back to it. SimpleLogin's sign-up and
keys steps use the same field and now check as it is typed.
- The module's README and ADR 0020 (D29, D30, the built state) amended.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 18:09:04 +02:00
String get handleInvalid ;
feat(app): signing in with SimpleLogin, its sheet and the start screen's offer (ADR 0020)
Where the instance offers SimpleLogin, the start screen offers it first,
saying that the instance keeps only the SimpleLogin ID, and opens a sheet
that waits for SimpleLogin's page in the browser (Cancel ends the wait),
then: for someone new, the choice between creating an account (the
suggested handle or another, checked with the instance, the password
twice, the recovery phrase shown once and three of its words typed back,
nothing reaching the instance before) and linking theirs again with its
handle and recovery phrase; for an account, its password opening its
keys; for an account of before, the keys made with the handle it lacks.
SimpleLoginCubit holds the steps, each refusal shown where it is mended
(a handle's on the handle, a sign-in to begin again on the browser's),
and closing the sheet stops waiting on the loopback.
The suggested handle is never checked: the instance suggests none that
anybody has, and every answer about a handle counts against its limit
(D5). The fake's handle rule is now the module's.
Learn and Todo build it with SimpleLogin's browser (halkyon_oidc_flutter,
now a direct dependency of both, with their names on the loopback's
page), their schemes and their loopback ports (43823, 43824), as the
instance's forwarding page has them. Their tests offer no SimpleLogin;
halkyon_app's test the sheet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 01:21:29 +02:00
feat(base)!: a handle at password sign-up (ADR 0020, D26, D28–D30)
Every new account gets a handle, and an account without one gets its
first as it sets a password. The handle is private until its person
goes public (D5).
- `passwordSignIn.signUp` takes a handle. `passwordSignIn.checkHandle`
says whether a sign-up could take one, only while that sign-up's
mailed code waits (`codeExpired` otherwise), counted per code request
(30 an hour) and per network (60) (D29). Members ask the register.
- `password.set` takes the first handle of an account without one
(D28); `SignInWays.handle` tells the app whether there is one.
- Each checks the handle before the code is used, so a refusal leaves
the code for another try. An existing account at the address keeps
its own handle where it has one, and the typed handle is still
checked, so the answer says nothing about the address.
- `first_handle.dart` holds what SimpleLogin, the keys and passwords
share: `refusalOfNewHandle`, `insertFirstProfile`, `hasHandle`.
- halkyon_app: one `HandleField` with its `HandleCheckCubit` (asked
300 ms after typing pauses, only about the handle typed last; the
instance's suggestion is free without asking) (D30). The password
setup asks for the handle between the phrase check and the code, and
a refusal at the code sends back to it. SimpleLogin's sign-up and
keys steps use the same field and now check as it is typed.
- The module's README and ADR 0020 (D29, D30, the built state) amended.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 18:09:04 +02:00
/// No description provided for @handleReserved.
feat(app): signing in with SimpleLogin, its sheet and the start screen's offer (ADR 0020)
Where the instance offers SimpleLogin, the start screen offers it first,
saying that the instance keeps only the SimpleLogin ID, and opens a sheet
that waits for SimpleLogin's page in the browser (Cancel ends the wait),
then: for someone new, the choice between creating an account (the
suggested handle or another, checked with the instance, the password
twice, the recovery phrase shown once and three of its words typed back,
nothing reaching the instance before) and linking theirs again with its
handle and recovery phrase; for an account, its password opening its
keys; for an account of before, the keys made with the handle it lacks.
SimpleLoginCubit holds the steps, each refusal shown where it is mended
(a handle's on the handle, a sign-in to begin again on the browser's),
and closing the sheet stops waiting on the loopback.
The suggested handle is never checked: the instance suggests none that
anybody has, and every answer about a handle counts against its limit
(D5). The fake's handle rule is now the module's.
Learn and Todo build it with SimpleLogin's browser (halkyon_oidc_flutter,
now a direct dependency of both, with their names on the loopback's
page), their schemes and their loopback ports (43823, 43824), as the
instance's forwarding page has them. Their tests offer no SimpleLogin;
halkyon_app's test the sheet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 01:21:29 +02:00
///
/// In en, this message translates to:
/// **'This handle is kept for the instance.'**
feat(base)!: a handle at password sign-up (ADR 0020, D26, D28–D30)
Every new account gets a handle, and an account without one gets its
first as it sets a password. The handle is private until its person
goes public (D5).
- `passwordSignIn.signUp` takes a handle. `passwordSignIn.checkHandle`
says whether a sign-up could take one, only while that sign-up's
mailed code waits (`codeExpired` otherwise), counted per code request
(30 an hour) and per network (60) (D29). Members ask the register.
- `password.set` takes the first handle of an account without one
(D28); `SignInWays.handle` tells the app whether there is one.
- Each checks the handle before the code is used, so a refusal leaves
the code for another try. An existing account at the address keeps
its own handle where it has one, and the typed handle is still
checked, so the answer says nothing about the address.
- `first_handle.dart` holds what SimpleLogin, the keys and passwords
share: `refusalOfNewHandle`, `insertFirstProfile`, `hasHandle`.
- halkyon_app: one `HandleField` with its `HandleCheckCubit` (asked
300 ms after typing pauses, only about the handle typed last; the
instance's suggestion is free without asking) (D30). The password
setup asks for the handle between the phrase check and the code, and
a refusal at the code sends back to it. SimpleLogin's sign-up and
keys steps use the same field and now check as it is typed.
- The module's README and ADR 0020 (D29, D30, the built state) amended.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 18:09:04 +02:00
String get handleReserved ;
feat(app): signing in with SimpleLogin, its sheet and the start screen's offer (ADR 0020)
Where the instance offers SimpleLogin, the start screen offers it first,
saying that the instance keeps only the SimpleLogin ID, and opens a sheet
that waits for SimpleLogin's page in the browser (Cancel ends the wait),
then: for someone new, the choice between creating an account (the
suggested handle or another, checked with the instance, the password
twice, the recovery phrase shown once and three of its words typed back,
nothing reaching the instance before) and linking theirs again with its
handle and recovery phrase; for an account, its password opening its
keys; for an account of before, the keys made with the handle it lacks.
SimpleLoginCubit holds the steps, each refusal shown where it is mended
(a handle's on the handle, a sign-in to begin again on the browser's),
and closing the sheet stops waiting on the loopback.
The suggested handle is never checked: the instance suggests none that
anybody has, and every answer about a handle counts against its limit
(D5). The fake's handle rule is now the module's.
Learn and Todo build it with SimpleLogin's browser (halkyon_oidc_flutter,
now a direct dependency of both, with their names on the loopback's
page), their schemes and their loopback ports (43823, 43824), as the
instance's forwarding page has them. Their tests offer no SimpleLogin;
halkyon_app's test the sheet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 01:21:29 +02:00
feat(base)!: a handle at password sign-up (ADR 0020, D26, D28–D30)
Every new account gets a handle, and an account without one gets its
first as it sets a password. The handle is private until its person
goes public (D5).
- `passwordSignIn.signUp` takes a handle. `passwordSignIn.checkHandle`
says whether a sign-up could take one, only while that sign-up's
mailed code waits (`codeExpired` otherwise), counted per code request
(30 an hour) and per network (60) (D29). Members ask the register.
- `password.set` takes the first handle of an account without one
(D28); `SignInWays.handle` tells the app whether there is one.
- Each checks the handle before the code is used, so a refusal leaves
the code for another try. An existing account at the address keeps
its own handle where it has one, and the typed handle is still
checked, so the answer says nothing about the address.
- `first_handle.dart` holds what SimpleLogin, the keys and passwords
share: `refusalOfNewHandle`, `insertFirstProfile`, `hasHandle`.
- halkyon_app: one `HandleField` with its `HandleCheckCubit` (asked
300 ms after typing pauses, only about the handle typed last; the
instance's suggestion is free without asking) (D30). The password
setup asks for the handle between the phrase check and the code, and
a refusal at the code sends back to it. SimpleLogin's sign-up and
keys steps use the same field and now check as it is typed.
- The module's README and ADR 0020 (D29, D30, the built state) amended.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 18:09:04 +02:00
/// No description provided for @handleTaken.
feat(app): signing in with SimpleLogin, its sheet and the start screen's offer (ADR 0020)
Where the instance offers SimpleLogin, the start screen offers it first,
saying that the instance keeps only the SimpleLogin ID, and opens a sheet
that waits for SimpleLogin's page in the browser (Cancel ends the wait),
then: for someone new, the choice between creating an account (the
suggested handle or another, checked with the instance, the password
twice, the recovery phrase shown once and three of its words typed back,
nothing reaching the instance before) and linking theirs again with its
handle and recovery phrase; for an account, its password opening its
keys; for an account of before, the keys made with the handle it lacks.
SimpleLoginCubit holds the steps, each refusal shown where it is mended
(a handle's on the handle, a sign-in to begin again on the browser's),
and closing the sheet stops waiting on the loopback.
The suggested handle is never checked: the instance suggests none that
anybody has, and every answer about a handle counts against its limit
(D5). The fake's handle rule is now the module's.
Learn and Todo build it with SimpleLogin's browser (halkyon_oidc_flutter,
now a direct dependency of both, with their names on the loopback's
page), their schemes and their loopback ports (43823, 43824), as the
instance's forwarding page has them. Their tests offer no SimpleLogin;
halkyon_app's test the sheet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 01:21:29 +02:00
///
/// In en, this message translates to:
/// **'Someone else has this handle.'**
feat(base)!: a handle at password sign-up (ADR 0020, D26, D28–D30)
Every new account gets a handle, and an account without one gets its
first as it sets a password. The handle is private until its person
goes public (D5).
- `passwordSignIn.signUp` takes a handle. `passwordSignIn.checkHandle`
says whether a sign-up could take one, only while that sign-up's
mailed code waits (`codeExpired` otherwise), counted per code request
(30 an hour) and per network (60) (D29). Members ask the register.
- `password.set` takes the first handle of an account without one
(D28); `SignInWays.handle` tells the app whether there is one.
- Each checks the handle before the code is used, so a refusal leaves
the code for another try. An existing account at the address keeps
its own handle where it has one, and the typed handle is still
checked, so the answer says nothing about the address.
- `first_handle.dart` holds what SimpleLogin, the keys and passwords
share: `refusalOfNewHandle`, `insertFirstProfile`, `hasHandle`.
- halkyon_app: one `HandleField` with its `HandleCheckCubit` (asked
300 ms after typing pauses, only about the handle typed last; the
instance's suggestion is free without asking) (D30). The password
setup asks for the handle between the phrase check and the code, and
a refusal at the code sends back to it. SimpleLogin's sign-up and
keys steps use the same field and now check as it is typed.
- The module's README and ADR 0020 (D29, D30, the built state) amended.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 18:09:04 +02:00
String get handleTaken ;
feat(app): signing in with SimpleLogin, its sheet and the start screen's offer (ADR 0020)
Where the instance offers SimpleLogin, the start screen offers it first,
saying that the instance keeps only the SimpleLogin ID, and opens a sheet
that waits for SimpleLogin's page in the browser (Cancel ends the wait),
then: for someone new, the choice between creating an account (the
suggested handle or another, checked with the instance, the password
twice, the recovery phrase shown once and three of its words typed back,
nothing reaching the instance before) and linking theirs again with its
handle and recovery phrase; for an account, its password opening its
keys; for an account of before, the keys made with the handle it lacks.
SimpleLoginCubit holds the steps, each refusal shown where it is mended
(a handle's on the handle, a sign-in to begin again on the browser's),
and closing the sheet stops waiting on the loopback.
The suggested handle is never checked: the instance suggests none that
anybody has, and every answer about a handle counts against its limit
(D5). The fake's handle rule is now the module's.
Learn and Todo build it with SimpleLogin's browser (halkyon_oidc_flutter,
now a direct dependency of both, with their names on the loopback's
page), their schemes and their loopback ports (43823, 43824), as the
instance's forwarding page has them. Their tests offer no SimpleLogin;
halkyon_app's test the sheet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 01:21:29 +02:00
feat(base)!: a handle at password sign-up (ADR 0020, D26, D28–D30)
Every new account gets a handle, and an account without one gets its
first as it sets a password. The handle is private until its person
goes public (D5).
- `passwordSignIn.signUp` takes a handle. `passwordSignIn.checkHandle`
says whether a sign-up could take one, only while that sign-up's
mailed code waits (`codeExpired` otherwise), counted per code request
(30 an hour) and per network (60) (D29). Members ask the register.
- `password.set` takes the first handle of an account without one
(D28); `SignInWays.handle` tells the app whether there is one.
- Each checks the handle before the code is used, so a refusal leaves
the code for another try. An existing account at the address keeps
its own handle where it has one, and the typed handle is still
checked, so the answer says nothing about the address.
- `first_handle.dart` holds what SimpleLogin, the keys and passwords
share: `refusalOfNewHandle`, `insertFirstProfile`, `hasHandle`.
- halkyon_app: one `HandleField` with its `HandleCheckCubit` (asked
300 ms after typing pauses, only about the handle typed last; the
instance's suggestion is free without asking) (D30). The password
setup asks for the handle between the phrase check and the code, and
a refusal at the code sends back to it. SimpleLogin's sign-up and
keys steps use the same field and now check as it is typed.
- The module's README and ADR 0020 (D29, D30, the built state) amended.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 18:09:04 +02:00
/// No description provided for @handleTooManyChecks.
feat(app): signing in with SimpleLogin, its sheet and the start screen's offer (ADR 0020)
Where the instance offers SimpleLogin, the start screen offers it first,
saying that the instance keeps only the SimpleLogin ID, and opens a sheet
that waits for SimpleLogin's page in the browser (Cancel ends the wait),
then: for someone new, the choice between creating an account (the
suggested handle or another, checked with the instance, the password
twice, the recovery phrase shown once and three of its words typed back,
nothing reaching the instance before) and linking theirs again with its
handle and recovery phrase; for an account, its password opening its
keys; for an account of before, the keys made with the handle it lacks.
SimpleLoginCubit holds the steps, each refusal shown where it is mended
(a handle's on the handle, a sign-in to begin again on the browser's),
and closing the sheet stops waiting on the loopback.
The suggested handle is never checked: the instance suggests none that
anybody has, and every answer about a handle counts against its limit
(D5). The fake's handle rule is now the module's.
Learn and Todo build it with SimpleLogin's browser (halkyon_oidc_flutter,
now a direct dependency of both, with their names on the loopback's
page), their schemes and their loopback ports (43823, 43824), as the
instance's forwarding page has them. Their tests offer no SimpleLogin;
halkyon_app's test the sheet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 01:21:29 +02:00
///
/// In en, this message translates to:
/// **'Too many handles were tried. Try again in an hour.'**
feat(base)!: a handle at password sign-up (ADR 0020, D26, D28–D30)
Every new account gets a handle, and an account without one gets its
first as it sets a password. The handle is private until its person
goes public (D5).
- `passwordSignIn.signUp` takes a handle. `passwordSignIn.checkHandle`
says whether a sign-up could take one, only while that sign-up's
mailed code waits (`codeExpired` otherwise), counted per code request
(30 an hour) and per network (60) (D29). Members ask the register.
- `password.set` takes the first handle of an account without one
(D28); `SignInWays.handle` tells the app whether there is one.
- Each checks the handle before the code is used, so a refusal leaves
the code for another try. An existing account at the address keeps
its own handle where it has one, and the typed handle is still
checked, so the answer says nothing about the address.
- `first_handle.dart` holds what SimpleLogin, the keys and passwords
share: `refusalOfNewHandle`, `insertFirstProfile`, `hasHandle`.
- halkyon_app: one `HandleField` with its `HandleCheckCubit` (asked
300 ms after typing pauses, only about the handle typed last; the
instance's suggestion is free without asking) (D30). The password
setup asks for the handle between the phrase check and the code, and
a refusal at the code sends back to it. SimpleLogin's sign-up and
keys steps use the same field and now check as it is typed.
- The module's README and ADR 0020 (D29, D30, the built state) amended.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-24 18:09:04 +02:00
String get handleTooManyChecks ;
2026-10-24 11:56:26 +02:00
/// No description provided for @simpleLoginSectionTitle.
///
/// In en, this message translates to:
/// **'SimpleLogin'**
String get simpleLoginSectionTitle ;
/// No description provided for @simpleLoginSignsYouIn.
///
/// In en, this message translates to:
/// **'Signs you in'**
String get simpleLoginSignsYouIn ;
/// No description provided for @simpleLoginAddHint.
///
/// In en, this message translates to:
/// **'Sign in with SimpleLogin, or with your Proton account.'**
String get simpleLoginAddHint ;
/// No description provided for @simpleLoginAdded.
///
/// In en, this message translates to:
/// **'SimpleLogin added.'**
String get simpleLoginAdded ;
feat(app,learn,todo): the operator's "This instance" card, screen and badge (ADR 0021, step 1)
Whoever operates the instance approves, refuses and suspends accounts
and names other operators in the app (D21 to D24, D34):
- halkyon_app: InstanceOperatorRepository on
ServerpodInstanceOperatorGateway (the module's instanceOperator
endpoint; refusals as OperatorFailed: notFound, isOperator,
lastOperator, notAllowed for 401 and 403, failed), in AppDependencies.
OperatingCubit counts the accounts that wait: asked as the person's
app opens, after every change, and every 5 minutes only while they
operate the instance, so nobody else's app keeps asking.
InstanceOperatorCard (or its tiles) and WaitingAccountsBadge show
nothing at all for anyone else. InstanceOperatorScreen, laid out as
Settings: the accounts that wait, each showing its four words to read
out before Approve (D33), Refuse for new ones after asking; who
operates, Take back after asking, the last always kept; Name an
operator by handle; Find an account by the start of its handle, with
Suspend after asking and Name as operator. English and German texts
("Admin" as the role word in German).
- Learn: the card right after the Sync card in Settings, which the
cogs' count leads to as their dot leads to Sync; the count on the
Settings cogs of Home and Profile.
- Todo: the tiles in the account sheet and the count on its button.
Todo's server is a member, whose `operating()` is always null, so
nothing shows there until it holds the register.
Tests: the gateway's mapping, the repository, both cubits with their
denials, the card, the badge and the screen; Learn's Settings shows the
card and the count to an operator and neither to anyone else.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 15:55:28 +01:00
/// The card and screen for whoever operates the instance (ADR 0021, D34).
///
/// In en, this message translates to:
/// **'This instance'**
String get operatorTitle ;
/// The tile leading to the operator's screen.
///
/// In en, this message translates to:
/// **'Accounts and operators'**
String get operatorAccounts ;
/// How many accounts wait for the operator; also the badge's label.
///
/// In en, this message translates to:
/// **'{count, plural, =0{Nobody waits for approval} =1{1 account waits for approval} other{{count} accounts wait for approval}}'**
String operatorWaitingCount ( int count ) ;
/// The card of accounts that wait for approval, new or suspended.
///
/// In en, this message translates to:
/// **'Waiting'**
String get operatorWaitingTitle ;
/// The waiting card, empty.
///
/// In en, this message translates to:
/// **'Nobody waits.'**
String get operatorNobodyWaits ;
/// An account without a handle, where its handle would be.
///
/// In en, this message translates to:
/// **'No handle yet'**
String get operatorNoHandle ;
/// A new account that waits, and since when.
///
/// In en, this message translates to:
/// **'New, waiting since {date}'**
String operatorNewSince ( String date ) ;
/// A suspended account, and since when.
///
/// In en, this message translates to:
/// **'Suspended since {date}'**
String operatorSuspendedSince ( String date ) ;
/// Before approving: the words prove it is their account that waits (D33).
///
/// In en, this message translates to:
/// **'Read them these four words, and approve only once they confirm these are on their screen.'**
String get operatorApproveNote ;
/// Approves a waiting account.
///
/// In en, this message translates to:
/// **'Approve'**
String get operatorApprove ;
/// Refuses a new account: deleted.
///
/// In en, this message translates to:
/// **'Refuse'**
String get operatorRefuse ;
/// The question before refusing.
///
/// In en, this message translates to:
/// **'Refuse this account?'**
String get operatorRefuseTitle ;
/// What refusing does.
///
/// In en, this message translates to:
/// **'It is deleted with everything it has. They can sign up again.'**
String get operatorRefuseMessage ;
/// The card of who operates the instance.
///
/// In en, this message translates to:
/// **'Operators'**
String get operatorOperatorsTitle ;
/// Takes back the operator's role.
///
/// In en, this message translates to:
/// **'Take back'**
String get operatorTakeBack ;
/// The question before taking the role back.
///
/// In en, this message translates to:
/// **'Take back the role?'**
String get operatorTakeBackTitle ;
/// What taking the role back does.
///
/// In en, this message translates to:
/// **'They keep their account, but no longer approve, refuse or suspend accounts here.'**
String get operatorTakeBackMessage ;
/// Names an account an operator, by its handle.
///
/// In en, this message translates to:
/// **'Name an operator'**
String get operatorAppoint ;
/// What an operator may do.
///
/// In en, this message translates to:
/// **'They approve, refuse and suspend accounts here, and name operators, as you do.'**
String get operatorAppointMessage ;
/// Confirms naming an operator.
///
/// In en, this message translates to:
/// **'Name'**
String get operatorAppointConfirm ;
/// The field for an account's handle.
///
/// In en, this message translates to:
/// **'Handle'**
String get operatorHandle ;
/// The card searching accounts by the start of their handle.
///
/// In en, this message translates to:
/// **'Find an account'**
String get operatorFindTitle ;
/// A search that found nothing.
///
/// In en, this message translates to:
/// **'No account\'s handle starts like this.'**
String get operatorFoundNone ;
/// When an account was made.
///
/// In en, this message translates to:
/// **'Since {date}'**
String operatorMadeOn ( String date ) ;
/// A tag: the account operates the instance.
///
/// In en, this message translates to:
/// **'Operator'**
String get operatorIsOperator ;
/// A tag: the account waits for approval.
///
/// In en, this message translates to:
/// **'Waiting'**
String get operatorIsWaiting ;
/// A tag: the account is suspended.
///
/// In en, this message translates to:
/// **'Suspended'**
String get operatorIsSuspended ;
/// Suspends an account.
///
/// In en, this message translates to:
/// **'Suspend'**
String get operatorSuspend ;
/// The question before suspending.
///
/// In en, this message translates to:
/// **'Suspend this account?'**
String get operatorSuspendTitle ;
/// What suspending does.
///
/// In en, this message translates to:
/// **'They are signed out everywhere and can\'t sign in until you approve them again. Nothing they have is deleted.'**
String get operatorSuspendMessage ;
/// Names a found account an operator.
///
/// In en, this message translates to:
/// **'Name as operator'**
String get operatorMakeOperator ;
/// Refused: no such account, or it doesn't wait.
///
/// In en, this message translates to:
/// **'That account isn\'t there any more, or doesn\'t wait.'**
String get operatorNotFound ;
/// Refused: suspending an operator.
///
/// In en, this message translates to:
/// **'An operator can\'t be suspended. Take back their role first.'**
String get operatorIsOperatorProblem ;
/// Refused: taking back the last operator's role.
///
/// In en, this message translates to:
/// **'The instance needs at least one operator.'**
String get operatorLastOperator ;
/// Refused: the person doesn't operate the instance.
///
/// In en, this message translates to:
/// **'Only the instance\'s operators can do this.'**
String get operatorNotAllowed ;
/// Any other failure.
///
/// In en, this message translates to:
/// **'That didn\'t work. Try again.'**
String get operatorFailed ;
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
/// The assistant asked for is no longer in deploy/assistants.env.
///
/// In en, this message translates to:
/// **'That assistant isn\'t set up here any more.'**
String get operatorAssistantNotSet ;
/// A limit below 0 was refused.
///
/// In en, this message translates to:
/// **'A limit can\'t be below zero.'**
String get operatorInvalidLimit ;
/// The operator's card of the assistants granted (Halkyon Learn's ADR 0021).
///
/// In en, this message translates to:
/// **'Assistants'**
String get assistantGrantsTitle ;
/// No assistant is granted to anyone.
///
/// In en, this message translates to:
/// **'Nobody has an assistant yet.'**
String get assistantGrantsNone ;
/// Opens the sheet granting an assistant to an account.
///
/// In en, this message translates to:
/// **'Grant an assistant'**
String get assistantGrant ;
/// The grant sheet's button.
///
/// In en, this message translates to:
/// **'Grant'**
String get assistantGrantSave ;
/// A grant's menu item, and the sheet's title, changing its limits.
///
/// In en, this message translates to:
/// **'Change limits'**
String get assistantGrantChange ;
/// Above the choice of assistant in the grant sheet.
///
/// In en, this message translates to:
/// **'Assistant'**
String get assistantGrantAssistant ;
/// The daily limit field.
///
/// In en, this message translates to:
/// **'Limit a day'**
String get assistantGrantDaily ;
/// The monthly limit field.
///
/// In en, this message translates to:
/// **'Limit a month'**
String get assistantGrantMonthly ;
/// Under the limit fields: the instance counts by UTC days and months (D42).
///
/// In en, this message translates to:
/// **'Empty for none. Days and months are UTC\'s.'**
String get assistantGrantLimitHelper ;
/// A limit typed that is no amount or number of tokens.
///
/// In en, this message translates to:
/// **'Not a number this takes'**
String get assistantGrantLimitInvalid ;
/// The grant sheet: the handle typed is nobody's.
///
/// In en, this message translates to:
/// **'No account has this handle.'**
String get assistantGrantNoHandle ;
/// After a limit in tokens, for an assistant without prices.
///
/// In en, this message translates to:
/// **'tokens'**
String get assistantGrantTokens ;
/// In the grant sheet: consent (D40) and what the operator sees (D3).
///
/// In en, this message translates to:
/// **'They agree once to what goes where before they ask it. You see how much they use, never what they ask.'**
String get assistantGrantNote ;
/// A daily limit.
///
/// In en, this message translates to:
/// **'{amount} a day'**
String assistantPerDay ( String amount ) ;
/// A monthly limit.
///
/// In en, this message translates to:
/// **'{amount} a month'**
String assistantPerMonth ( String amount ) ;
/// A grant without limits.
///
/// In en, this message translates to:
/// **'No limits'**
String get assistantNoLimits ;
/// A grant whose person agreed to what goes where.
///
/// In en, this message translates to:
/// **'Agreed'**
String get assistantGrantAgreed ;
/// A grant whose person hasn't agreed yet.
///
/// In en, this message translates to:
/// **'Not agreed yet'**
String get assistantGrantNotAgreed ;
/// A grant whose limits are in another unit than the pricing now; it answers nothing until set again.
///
/// In en, this message translates to:
/// **'Its prices changed: set its limits again'**
String get assistantGrantOutdated ;
/// A grant of an assistant taken out of deploy/assistants.env.
///
/// In en, this message translates to:
/// **'No longer set up'**
String get assistantGrantUnset ;
/// A grant's menu item.
///
/// In en, this message translates to:
/// **'Take back'**
String get assistantRevoke ;
/// Asks before taking a grant back.
///
/// In en, this message translates to:
/// **'Take back {assistant}?'**
String assistantRevokeTitle ( String assistant ) ;
/// Under the question taking a grant back.
///
/// In en, this message translates to:
/// **'They can no longer ask it. What they used stays counted.'**
String get assistantRevokeMessage ;
/// Opens everyone's figures (D3).
///
/// In en, this message translates to:
/// **'Use by month'**
String get assistantUseByMonth ;
/// The title of the figures' screen and sheet.
///
/// In en, this message translates to:
/// **'Use of assistants'**
String get assistantUseTitle ;
/// A month without use.
///
/// In en, this message translates to:
/// **'Nothing used in this month.'**
String get assistantUseNone ;
/// Under the figures: what is counted and for how long (D41, D42).
///
/// In en, this message translates to:
/// **'Counted per UTC day, kept for this month and the two before. Never what was asked.'**
String get assistantUseKept ;
/// How many requests were counted.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 request} other{{count} requests}}'**
String assistantRequests ( int count ) ;
/// How many tokens were counted, the number formatted.
///
/// In en, this message translates to:
/// **'{amount} tokens'**
String assistantTokens ( String amount ) ;
/// Settings' card of the assistants a person may ask (Halkyon Learn's ADR 0021, point 9).
///
/// In en, this message translates to:
/// **'Assistants'**
String get assistantsTitle ;
/// Which model answers, and the host that receives what is asked.
///
/// In en, this message translates to:
/// **'{model} at {host}'**
String assistantModelAt ( String model , String host ) ;
/// What is left of a day's limit.
///
/// In en, this message translates to:
/// **'{left} of {limit} left today'**
String assistantLeftToday ( String left , String limit ) ;
/// What is left of a month's limit.
///
/// In en, this message translates to:
/// **'{left} of {limit} left this month'**
String assistantLeftThisMonth ( String left , String limit ) ;
/// Opens the consent sheet of an assistant not agreed to yet.
///
/// In en, this message translates to:
/// **'Read and agree'**
String get assistantAgree ;
/// An assistant whose limits are outdated: it answers nothing until the operator sets them again.
///
/// In en, this message translates to:
/// **'Waits for its limits to be set again'**
String get assistantWaitsForLimits ;
/// Opens the person's own figures.
///
/// In en, this message translates to:
/// **'Your use'**
String get assistantYourUse ;
/// Opens the instance's privacy page (D39).
///
/// In en, this message translates to:
/// **'What goes where on {instance}'**
String assistantPrivacyPage ( String instance ) ;
/// Above a person's own assistants (point 10).
///
/// In en, this message translates to:
/// **'With your own key'**
String get assistantOwnTitle ;
/// Opens the sheet adding an assistant with a personal key.
///
/// In en, this message translates to:
/// **'Add an assistant with your own key'**
String get assistantOwnAdd ;
/// What a personal key means (D5, D6).
///
/// In en, this message translates to:
/// **'The app asks your provider directly: this instance sees neither your key nor your questions, and counts nothing. The key is kept in your settings, encrypted on your devices.'**
String get assistantOwnNote ;
/// The personal key sheet's title.
///
/// In en, this message translates to:
/// **'Your own key'**
String get assistantOwnEdit ;
/// The name the person gives their assistant.
///
/// In en, this message translates to:
/// **'Name'**
String get assistantOwnName ;
/// Above the choice of protocol.
///
/// In en, this message translates to:
/// **'API'**
String get assistantOwnProtocol ;
/// The provider's address field.
///
/// In en, this message translates to:
/// **'Address'**
String get assistantOwnBaseUrl ;
/// Under the address field: which services take which address.
///
/// In en, this message translates to:
/// **'Empty for the provider\'s own. Mistral, OpenRouter or a model of your own: its address with /v1.'**
String get assistantOwnBaseUrlHelper ;
/// An address the app refuses: keys and questions would travel in the clear.
///
/// In en, this message translates to:
/// **'Only https, or http on your own network'**
String get assistantOwnBaseUrlInvalid ;
/// The model's name at the provider.
///
/// In en, this message translates to:
/// **'Model'**
String get assistantOwnModel ;
/// The API key field.
///
/// In en, this message translates to:
/// **'Key'**
String get assistantOwnKey ;
/// Under the key field.
///
/// In en, this message translates to:
/// **'Empty for a model of your own that takes none.'**
String get assistantOwnKeyHelper ;
/// Whether the model reads images (D12).
///
/// In en, this message translates to:
/// **'Reads images'**
String get assistantOwnImages ;
/// The personal key sheet's button.
///
/// In en, this message translates to:
/// **'Save'**
String get assistantOwnSave ;
/// Removes an assistant with a personal key.
///
/// In en, this message translates to:
/// **'Remove'**
String get assistantOwnRemove ;
/// Asks before removing an assistant with a personal key.
///
/// In en, this message translates to:
/// **'Remove {name}?'**
String assistantOwnRemoveTitle ( String name ) ;
/// Under the question removing a personal key.
///
/// In en, this message translates to:
/// **'Its key goes from your settings on every device.'**
String get assistantOwnRemoveMessage ;
/// A personal key refused, as the settings wouldn't be sealed.
///
/// In en, this message translates to:
/// **'The key wasn\'t kept: this device can\'t encrypt your settings yet. Open your account\'s keys here first.'**
String get assistantOwnRefused ;
/// Below the person's own assistants, where the operator doesn't allow them (ADR 0021, D6).
///
/// In en, this message translates to:
/// **'Whoever runs {instance} doesn\'t allow own keys here, so the app doesn\'t ask these.'**
String assistantOwnNotAllowed ( String instance ) ;
/// The consent sheet's title (D40).
///
/// In en, this message translates to:
/// **'Before you ask {name}'**
String assistantConsentTitle ( String name ) ;
/// Who answers (D27).
///
/// In en, this message translates to:
/// **'It answers with the model {model} at {host}.'**
String assistantConsentModel ( String model , String host ) ;
/// What goes to the provider (D27).
///
/// In en, this message translates to:
feat(learn): a private studyset goes to an assistant only once agreed
ADR 0021, D9 and D17. Before a question sends a private studyset's
content to a remote model, through the parts chosen or a wider reach
with private studysets switched on, a warning names the studysets, the
provider's host and its terms (where checked: Anthropic, OpenAI,
Google, OpenRouter), and is asked once per studyset and assistant: the
agreement is kept in the person's sealed settings (schema 13,
privateAgreements, folded only where both rows hold it), shown in the
studyset's privacy dialog with Take back, and marked on the folded
button while it holds. Follow-ups and Try again ask again once taken
back, as the scope keeps where the parts came from (partsFrom). No
warning for an own assistant on this machine (loopback or localhost,
halkyon_llm's LlmEndpoint.onThisMachine); the local network may be
shared.
Texts that step 3 made untrue: the consent sheet now says what an
assistant looks up goes too and that conversations are kept encrypted
for the person's devices; the privacy dialogs no longer mention
showing a studyset to Claude; the privacy page says what is looked up
and when a private studyset goes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 21:00:39 +01:00
/// **'Only what you send goes there: your question, what you pick of what it is about, and what it looks up where you let it. The provider\'s own terms apply to it.'**
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
String get assistantConsentWhatGoes ;
/// For an assistant that reads images (D12).
///
/// In en, this message translates to:
/// **'It reads the images you add.'**
String get assistantConsentImages ;
/// The instance sees it in passing and keeps nothing (D27).
///
/// In en, this message translates to:
/// **'It passes through {instance} on its way, which keeps none of it.'**
String assistantConsentInstance ( String instance ) ;
/// The operator's figures (D3).
///
/// In en, this message translates to:
/// **'Whoever runs {instance} sees how much you use it (requests, tokens, cost), never what you ask.'**
String assistantConsentOperator ( String instance ) ;
/// Conversations are sealed on the devices (D13).
///
/// In en, this message translates to:
feat(learn): a private studyset goes to an assistant only once agreed
ADR 0021, D9 and D17. Before a question sends a private studyset's
content to a remote model, through the parts chosen or a wider reach
with private studysets switched on, a warning names the studysets, the
provider's host and its terms (where checked: Anthropic, OpenAI,
Google, OpenRouter), and is asked once per studyset and assistant: the
agreement is kept in the person's sealed settings (schema 13,
privateAgreements, folded only where both rows hold it), shown in the
studyset's privacy dialog with Take back, and marked on the folded
button while it holds. Follow-ups and Try again ask again once taken
back, as the scope keeps where the parts came from (partsFrom). No
warning for an own assistant on this machine (loopback or localhost,
halkyon_llm's LlmEndpoint.onThisMachine); the local network may be
shared.
Texts that step 3 made untrue: the consent sheet now says what an
assistant looks up goes too and that conversations are kept encrypted
for the person's devices; the privacy dialogs no longer mention
showing a studyset to Claude; the privacy page says what is looked up
and when a private studyset goes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 21:00:39 +01:00
/// **'Your conversations are kept encrypted: only your devices can read them.'**
feat(app,learn,todo): the assistants in Settings and on the operator's screen
ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.
AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.
Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 18:59:41 +01:00
String get assistantConsentConversations ;
/// The consent sheet's button.
///
/// In en, this message translates to:
/// **'Agree'**
String get assistantConsentAgree ;
/// An assistant agreed to, and when.
///
/// In en, this message translates to:
/// **'You agreed on {date}.'**
String assistantConsentAgreedOn ( String date ) ;
/// notGranted.
///
/// In en, this message translates to:
/// **'This assistant isn\'t granted to you any more.'**
String get assistantProblemNotGranted ;
/// notAgreed.
///
/// In en, this message translates to:
/// **'Agree to what goes where first.'**
String get assistantProblemNotAgreed ;
/// limitReached, with when it resets.
///
/// In en, this message translates to:
/// **'Its limit is reached until {time}.'**
String assistantProblemLimitReached ( String time ) ;
/// Refused: a limit is reached, the instance not saying until when.
///
/// In en, this message translates to:
/// **'Its limit is reached for now.'**
String get assistantProblemLimitReachedNow ;
/// limitOutdated.
///
/// In en, this message translates to:
/// **'It waits for its limits to be set again.'**
String get assistantProblemLimitOutdated ;
/// imagesNotRead.
///
/// In en, this message translates to:
/// **'It reads no images.'**
String get assistantProblemImagesNotRead ;
/// malformed.
///
/// In en, this message translates to:
/// **'This question can\'t be sent.'**
String get assistantProblemMalformed ;
/// tooLarge.
///
/// In en, this message translates to:
/// **'This question is too long, or holds too many or too large images.'**
String get assistantProblemTooLarge ;
/// tooManyAtOnce.
///
/// In en, this message translates to:
/// **'Wait for the answers on their way first.'**
String get assistantProblemTooManyAtOnce ;
/// providerKey.
///
/// In en, this message translates to:
/// **'The provider refused the key.'**
String get assistantProblemProviderKey ;
/// providerBusy.
///
/// In en, this message translates to:
/// **'The provider is busy. Try again in a moment.'**
String get assistantProblemProviderBusy ;
/// providerRefused.
///
/// In en, this message translates to:
/// **'The provider refused this question.'**
String get assistantProblemProviderRefused ;
/// providerFailed.
///
/// In en, this message translates to:
/// **'The provider failed. Try again.'**
String get assistantProblemProviderFailed ;
/// notAllowed.
///
/// In en, this message translates to:
/// **'Only people it is granted to can do this.'**
String get assistantProblemNotAllowed ;
/// failed.
///
/// In en, this message translates to:
/// **'That didn\'t work. Try again.'**
String get assistantProblemFailed ;
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:18:49 +02:00
}
class _HalkyonAppLocalizationsDelegate
extends LocalizationsDelegate < HalkyonAppLocalizations > {
const _HalkyonAppLocalizationsDelegate ( ) ;
@ override
Future < HalkyonAppLocalizations > load ( Locale locale ) {
return SynchronousFuture < HalkyonAppLocalizations > (
lookupHalkyonAppLocalizations ( locale ) ,
) ;
}
@ override
bool isSupported ( Locale locale ) = >
< String > [ ' de ' , ' en ' ] . contains ( locale . languageCode ) ;
@ override
bool shouldReload ( _HalkyonAppLocalizationsDelegate old ) = > false ;
}
HalkyonAppLocalizations lookupHalkyonAppLocalizations ( Locale locale ) {
// Lookup logic when language+country codes are specified.
switch ( locale . languageCode ) {
case ' en ' :
{
switch ( locale . countryCode ) {
case ' GB ' :
return HalkyonAppLocalizationsEnGb ( ) ;
case ' US ' :
return HalkyonAppLocalizationsEnUs ( ) ;
}
break ;
}
}
// Lookup logic when only language code is specified.
switch ( locale . languageCode ) {
case ' de ' :
return HalkyonAppLocalizationsDe ( ) ;
case ' en ' :
return HalkyonAppLocalizationsEn ( ) ;
}
throw FlutterError (
' HalkyonAppLocalizations.delegate failed to load unsupported locale " $ locale ". This is likely '
' an issue with the localizations generation tool. Please file an issue '
' on GitHub with a reproducible sample app and the gen-l10n configuration '
' that was used. ' ,
) ;
}