feat(app): adding SimpleLogin to an account (ADR 0020, D21, D23)
All checks were successful
CI / check (push) Successful in 32m34s
CI / deploy (push) Has been skipped

halkyon_app reads where the device's person stands with SimpleLogin
(nothing to show, linked, or missing) and shows it in SimpleLoginSection,
which each app places above the password part: Add SimpleLogin, once
added one more way in. GitHub is added to nobody where SimpleLogin is
offered, and Learn's Profile offers an anonymous person SimpleLogin
alone there (D23).

No cut-off day, reminder or banner (D24): with the developer the only
person on learn.halkyon.eu, the cut-off is the operator taking GitHub's
OAuth app out of the deploy's settings once that account has SimpleLogin.
ADR 0020 records it and drops step 5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-24 11:56:26 +02:00
commit fad12b4411
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
3 changed files with 829 additions and 77 deletions

View file

@ -6,9 +6,9 @@ import 'package:lucide_icons_flutter/lucide_icons.dart';
import 'package:material_ui/material_ui.dart';
/// The account: halkyon_app's sections (whose it is, on which instance,
/// switching to the account on another, signing out and deleting it; its
/// password and authenticators) in Todo's words where only Todo can say it;
/// and About.
/// switching to the account on another, signing out and deleting it;
/// SimpleLogin; its password and authenticators) in Todo's words where only
/// Todo can say it; and About.
Future<void> showAccountSheet(BuildContext context) => showAdaptiveSheet(
context,
builder: (context) => const _AccountSheet(),
@ -31,11 +31,15 @@ class const _AccountSheet() extends StatelessWidget {
signOutAnonymousMessage: l10n.accountSignOutAnonymousMessage,
deleteMessage: l10n.accountDeleteMessage,
),
// A password and an authenticator keep the account, and sign in
// on other devices and in the instance's other apps (ADR 0017).
// SimpleLogin keeps the account where the instance offers it
// (ADR 0020, D21), and a password and an authenticator, signing
// in on other devices and in the instance's other apps (ADR 0017).
const Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: AccountSecuritySection(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [SimpleLoginSection(), AccountSecuritySection()],
),
),
ListTile(
leading: const Icon(LucideIcons.info),

View file

@ -134,6 +134,7 @@ class TodoDependencies({
},
onDispose: () async {
await passwords.dispose();
await simpleLogin.dispose();
await authGateway.dispose();
client.close();
},

View file

@ -29,6 +29,7 @@ void main() {
bool usedBefore = false,
bool wasSignedOut = false,
bool passwords = true,
FakeSimpleLoginGateway? simpleLogin,
}) async {
tester.view.physicalSize = const Size(412, 900);
tester.view.devicePixelRatio = 1;
@ -57,9 +58,10 @@ void main() {
gateway,
addresses,
),
// Offered by no instance here: halkyon_app's tests test its sheet.
// Offered by no instance here but where a test gives one:
// halkyon_app's tests test its sheet.
simpleLogin: SimpleLoginRepository(
FakeSimpleLoginGateway(gateway)..isOffered = false,
simpleLogin ?? (FakeSimpleLoginGateway(gateway)..isOffered = false),
FakeOidcBrowser(),
FakeAccountCrypto(),
MemoryAccountKeyStore(),
@ -184,6 +186,18 @@ void main() {
expect(find.text('Sign in with GitHub'), findsOneWidget);
});
testWidgets('where the instance offers SimpleLogin, the account sheet '
'adds it (ADR 0020, D21)', (tester) async {
await start(
tester,
usedBefore: true,
simpleLogin: FakeSimpleLoginGateway(gateway),
);
await openAccount(tester);
expect(find.widgetWithText(ListTile, 'Add SimpleLogin'), findsOneWidget);
});
testWidgets('deleting the account asks first; Cancel keeps it, Delete '
'deletes it and asks how to start', (tester) async {
await start(tester, usedBefore: true);