Skip to content

Commit f03ce8b

Browse files
committed
compose: Focus topic input first when opening compose box
Fixes: #1448
1 parent 89f6663 commit f03ce8b

File tree

2 files changed

+54
-0
lines changed

2 files changed

+54
-0
lines changed

lib/widgets/compose_box.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,13 @@ class _ContentInput extends StatelessWidget {
514514
final String? hintText;
515515
final bool enabled;
516516

517+
void _handleTapped(ComposeBoxController controller) {
518+
if (controller is StreamComposeBoxController
519+
&& controller.topicInteractionStatus.value == .notEditingNotChosen) {
520+
controller.topicFocusNode.requestFocus();
521+
}
522+
}
523+
517524
void _handleContentInserted(BuildContext context, KeyboardInsertedContent content) async {
518525
if (content.data == null || content.data!.isEmpty) {
519526
// As of writing, the engine implementation never leaves `content.data` as
@@ -590,6 +597,7 @@ class _ContentInput extends StatelessWidget {
590597
enabled: enabled,
591598
controller: controller.content,
592599
focusNode: controller.contentFocusNode,
600+
onTap: () => _handleTapped(controller),
593601
contentInsertionConfiguration: ContentInsertionConfiguration(
594602
onContentInserted: (content) => _handleContentInserted(context, content)),
595603
// Let the content show through the `contentPadding` so that

test/widgets/compose_box_test.dart

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,52 @@ void main() {
219219
});
220220
});
221221

222+
group('topic not yet chosen, tap content input -> topic input focused', () {
223+
void checkFocused({required bool topic, required bool content}) {
224+
assert(!(topic && content), "Topic and content inputs can't both have focus!");
225+
226+
check(controller).isA<StreamComposeBoxController>()
227+
..topicFocusNode.hasFocus.equals(topic)
228+
..contentFocusNode.hasFocus.equals(content);
229+
}
230+
231+
Future<void> prepareAndTapContentInput(WidgetTester tester) async {
232+
final channel = eg.stream();
233+
await prepareComposeBox(tester,
234+
narrow: ChannelNarrow(channel.streamId),
235+
subscriptions: [eg.subscription(channel)]);
236+
checkFocused(topic: false, content: false);
237+
238+
// Prepare a response for a getChannelTopics request triggered by the
239+
// the topic input being focused.
240+
connection.prepare(json: GetChannelTopicsResult(topics: []).toJson());
241+
await tester.tap(contentInputFinder);
242+
await tester.pump(Duration.zero);
243+
}
244+
245+
testWidgets('basic', (tester) async {
246+
await prepareAndTapContentInput(tester);
247+
checkFocused(topic: true, content: false);
248+
});
249+
250+
testWidgets('choose topic, tap content input -> content input focused', (tester) async {
251+
await prepareAndTapContentInput(tester);
252+
checkFocused(topic: true, content: false);
253+
254+
await tester.enterText(topicInputFinder, 'topic');
255+
await tester.tap(contentInputFinder);
256+
checkFocused(topic: false, content: true);
257+
});
258+
259+
testWidgets('skip topic, tap content input -> content input focused', (tester) async {
260+
await prepareAndTapContentInput(tester);
261+
checkFocused(topic: true, content: false);
262+
263+
await tester.tap(contentInputFinder);
264+
checkFocused(topic: false, content: true);
265+
});
266+
});
267+
222268
group('ComposeBoxTheme', () {
223269
test('lerp light to dark, no crash', () {
224270
final a = ComposeBoxTheme.light;

0 commit comments

Comments
 (0)