feat(learn): one assistant field across the app, kept from tab to tab (ADR 0021, D69)

Each screen's button kept its own open state and its own draft, so the
question half written on the Library was gone on Home. ComposerCubit,
provided beside ConversationDockCubit, now holds whether the field is
open and the draft (text, files attached, parts chosen) for every
button. Switching tabs keeps it as it is (AppShell says so before
goBranch); going to another screen within a tab, or over the tabs, keeps
it open only while something is written or attached. Only the button of
the screen at the router's location unfolds, so no tab kept alive
behind holds a second field.

Whats-New: The question you are writing stays open as you switch tabs.
Whats-New-De: Die Frage, die du schreibst, bleibt offen, wenn du den Tab wechselst.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-11-04 21:07:40 +01:00
commit f45ed98d52
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -683,4 +683,90 @@ void main() {
);
debugDefaultTargetPlatformOverride = null;
});
group('one field across the app (D69)', () {
/// The field's text, where one shows.
String? written(WidgetTester tester) =>
find
.descendant(
of: find.byType(AssistantField),
matching: find.byType(TextField),
)
.evaluate()
.isEmpty
? null
: tester
.widget<TextField>(
find.descendant(
of: find.byType(AssistantField),
matching: find.byType(TextField),
),
)
.controller!
.text;
Future<void> tab(WidgetTester tester, String label) async {
await tester.tap(find.text(label).last);
await tester.pumpAndSettle();
}
testWidgets('switching tabs keeps it as it is: open with its question '
'on the Library, open with it on Home; folded, folded', (
tester,
) async {
await showApp(tester, location: RoutePaths.library);
await unfold(tester);
await tester.enterText(
find.descendant(
of: find.byType(AssistantField),
matching: find.byType(TextField),
),
'kanji',
);
await tester.pump();
await tab(tester, 'Home');
expect(written(tester), 'kanji');
await tester.tap(find.byTooltip('Close'));
await tester.pumpAndSettle();
expect(find.byType(AssistantField), findsNothing);
await tab(tester, 'Library');
expect(find.byType(AssistantField), findsNothing, reason: 'folded');
await unfold(tester);
expect(written(tester), 'kanji', reason: 'folding keeps the question');
});
testWidgets('going to another screen in a tab keeps it open only while '
'something is written', (tester) async {
await showApp(tester, location: RoutePaths.library);
await unfold(tester);
await tester.tap(find.text('Japanese'));
await tester.pumpAndSettle();
expect(find.byType(AssistantField), findsNothing, reason: 'empty');
await unfold(tester);
await tester.enterText(
find.descendant(
of: find.byType(AssistantField),
matching: find.byType(TextField),
),
'kanji',
);
await tester.pump();
await tester.pageBack();
await tester.pumpAndSettle();
expect(written(tester), 'kanji', reason: 'on the Library again');
expect(find.byType(AssistantField), findsOneWidget);
});
testWidgets('once sent, it folds empty everywhere', (tester) async {
await showApp(tester, location: RoutePaths.library);
await send(tester, 'kanji');
await tab(tester, 'Home');
expect(find.byType(AssistantField), findsNothing);
await unfold(tester);
expect(written(tester), '');
});
});
}