feat(todo): the app on halkyon_app, with its own icon

Todo's app is halkyon_app's instance and session gates with Todo's
frame ("Todo" under "Halkyon", its icon, its own sentences) and an
instance check accepting only `todo`; TodoDependencies on an instance,
an empty TodoPerson per person, nothing kept or synced yet. A person
sees an empty home and their account: whose it is on which instance,
switching account, signing out and deleting it (each asking first where
it deletes), and About with the source of what the instance runs and
the licence list. Texts in British English and German. App ID
app.halkyon.todo on every platform, GitHub's callback scheme registered
on Android, no web build.

Its icon is Lucide's list-checks (lucide-static 1.50.0) in the brand's
blues on white: tool/app_icons.py draws its strokes with Pillow and
writes every platform's launcher icon; Lucide's ISC notice joins the
licence list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-19 21:56:12 +02:00
commit 6871e3763b
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -0,0 +1,179 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:halkyon_app/halkyon_app.dart';
import 'package:halkyon_app/testing.dart';
import 'package:halkyon_todo_flutter/src/app/todo_app.dart';
import 'package:halkyon_todo_flutter/src/app/todo_dependencies.dart';
import 'package:halkyon_ui/halkyon_ui.dart';
import 'package:material_ui/material_ui.dart';
void main() {
final builtIn = Instance.parse('todo.example.org')!;
late MemoryInstanceStore store;
late FakeAuthGateway gateway;
late MemorySignedOutMark signedOut;
late List<SignedIn> opened;
setUp(() {
store = MemoryInstanceStore();
gateway = FakeAuthGateway();
signedOut = MemorySignedOutMark();
opened = [];
});
/// Starts Todo on a new install ([usedBefore] false) or on one that chose
/// its instance before, signed out as [wasSignedOut] says.
Future<void> start(
WidgetTester tester, {
bool usedBefore = false,
bool wasSignedOut = false,
}) async {
tester.view.physicalSize = const Size(412, 900);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.reset);
final choice = InstanceChoice(
store,
builtIn: builtIn,
forget: (instance) async {},
);
await choice.load(usedBefore: usedBefore);
await signedOut.write(signedOut: wasSignedOut);
await tester.pumpWidget(
TodoApp(
choice: choice,
checker: FakeInstanceChecker({builtIn}),
countryNames: MemoryCountryNames(const {}),
open: (instance) async => TodoDependencies(
serverName: instance.name,
auth: AuthRepository(gateway, MemorySignInAddresses()),
signedOut: signedOut,
sourceCode: () async =>
Uri.parse('https://code.halkyon.app/project-halkyon/halkyon'),
openPerson: (person) async {
opened.add(person);
return TodoPerson(person);
},
),
),
);
await tester.pumpAndSettle();
}
/// Opens the account sheet from home.
Future<void> openAccount(WidgetTester tester) async {
await tester.tap(find.byTooltip('Account'));
await tester.pumpAndSettle();
}
testWidgets("a new install chooses its instance first, in Todo's words and "
'look', (tester) async {
await start(tester);
expect(
find.byWidgetPredicate(
(widget) => widget is BrandWordmark && widget.product == 'Todo',
),
findsOneWidget,
);
expect(
find.text(
'Choose the instance to keep your tasks on. Someone runs each one; '
'you can add others later.',
),
findsOneWidget,
);
expect(find.text('todo.example.org'), findsOneWidget);
expect(find.text('Another instance'), findsOneWidget);
expect(opened, isEmpty, reason: 'nothing made anywhere yet');
});
testWidgets("the build's instance in one tap: an account without a name, "
'and an empty home', (tester) async {
await start(tester);
await tester.tap(find.text('todo.example.org'));
await tester.pumpAndSettle();
expect(opened.single.isIdentified, isFalse);
expect(find.text('Nothing here yet'), findsOneWidget);
expect(
find.text('Tasks come with a later version of Halkyon Todo.'),
findsOneWidget,
);
});
testWidgets('signed out, it asks how to start; a GitHub account nobody here '
"signs in with is said so, with Todo's ways in", (tester) async {
await start(tester, usedBefore: true, wasSignedOut: true);
expect(find.text('Sign in with GitHub'), findsOneWidget);
gateway.gitHubError = const GitHubSignInFailed(
GitHubSignInProblem.unknownAccount,
);
await tester.tap(find.text('Sign in with GitHub'));
await tester.pumpAndSettle();
expect(
find.text(
'No account here signs in with this GitHub account yet. Start '
'without an account, or continue with your email address.',
),
findsOneWidget,
);
});
testWidgets('the account says whose it is, on which instance, and signing '
'out of an anonymous one asks first, as it deletes it', (tester) async {
await start(tester, usedBefore: true);
await openAccount(tester);
expect(find.text('Anonymous on todo.example.org'), findsOneWidget);
expect(find.text('Switch account'), findsOneWidget);
await tester.tap(find.text('Sign out'));
await tester.pumpAndSettle();
expect(find.text('Sign out?'), findsOneWidget);
await tester.tap(find.text('Sign out').last);
await tester.pumpAndSettle();
expect(gateway.accountDeletions, 1);
expect(gateway.status, const SignedOut());
expect(find.text('Sign in with GitHub'), 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);
await openAccount(tester);
await tester.tap(find.text('Delete account'));
await tester.pumpAndSettle();
expect(find.text('Delete your account?'), findsOneWidget);
await tester.tap(find.text('Cancel'));
await tester.pumpAndSettle();
expect(gateway.accountDeletions, 0);
await tester.tap(find.text('Delete account'));
await tester.pumpAndSettle();
await tester.tap(find.text('Delete account').last);
await tester.pumpAndSettle();
expect(gateway.accountDeletions, 1);
expect(find.text('Sign in with GitHub'), findsOneWidget);
});
testWidgets('About names the source of what the instance runs, which both '
'licences ask to offer', (tester) async {
await start(tester, usedBefore: true);
await openAccount(tester);
await tester.tap(find.text('About'));
await tester.pumpAndSettle();
expect(find.text('The source of what this instance runs:'), findsOneWidget);
expect(
find.text('https://code.halkyon.app/project-halkyon/halkyon'),
findsOneWidget,
);
});
}