Skip to content

Commit 8524b1e

Browse files
committed
compose: Fix bug where compose progress can be lost on new event queue
And factor out a helper for the part that reads `widget.narrow` and sets a new controller accordingly; we'll use this helper for the edit-message UI, to reset the compose box at the end of an edit-message session. Fixes: zulip#1470
1 parent 1ca7f8a commit 8524b1e

File tree

2 files changed

+62
-7
lines changed

2 files changed

+62
-7
lines changed

lib/widgets/compose_box.dart

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1523,14 +1523,26 @@ class _ComposeBoxState extends State<ComposeBox> with PerAccountStoreAwareStateM
15231523

15241524
@override
15251525
void onNewStore() {
1526+
final newStore = PerAccountStoreWidget.of(context);
1527+
1528+
if (_controller == null) {
1529+
_setNewControllerForNarrow(newStore);
1530+
} else {
1531+
switch (_controller) {
1532+
case StreamComposeBoxController():
1533+
(_controller as StreamComposeBoxController).topic.store = newStore;
1534+
case FixedDestinationComposeBoxController():
1535+
break; // no reference to the store that needs updating
1536+
case null:
1537+
assert(false);
1538+
}
1539+
}
1540+
}
1541+
1542+
void _setNewControllerForNarrow(PerAccountStore store) {
15261543
switch (widget.narrow) {
15271544
case ChannelNarrow():
1528-
final store = PerAccountStoreWidget.of(context);
1529-
if (_controller == null) {
1530-
_controller = StreamComposeBoxController(store: store);
1531-
} else {
1532-
(controller as StreamComposeBoxController).topic.store = store;
1533-
}
1545+
_controller = StreamComposeBoxController(store: store);
15341546
case TopicNarrow():
15351547
case DmNarrow():
15361548
_controller = FixedDestinationComposeBoxController();

test/widgets/compose_box_test.dart

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ void main() {
5656
addTearDown(testBinding.reset);
5757
selfUser ??= eg.selfUser;
5858
zulipFeatureLevel ??= eg.futureZulipFeatureLevel;
59-
final selfAccount = eg.account(user: selfUser, zulipFeatureLevel: zulipFeatureLevel);
59+
final selfAccount = eg.account(
60+
id: eg.selfAccount.id, user: selfUser, zulipFeatureLevel: zulipFeatureLevel);
6061
await testBinding.globalStore.add(selfAccount, eg.initialSnapshot(
6162
zulipFeatureLevel: zulipFeatureLevel,
6263
realmMandatoryTopics: mandatoryTopics,
@@ -111,6 +112,11 @@ void main() {
111112
await tester.enterText(contentInputFinder, content);
112113
}
113114

115+
void checkContentInputValue(WidgetTester tester, String expected) {
116+
check(tester.widget<TextField>(contentInputFinder))
117+
.controller.isNotNull().value.text.equals(expected);
118+
}
119+
114120
Future<void> tapSendButton(WidgetTester tester) async {
115121
connection.prepare(json: SendMessageResult(id: 123).toJson());
116122
await tester.tap(find.byIcon(ZulipIcons.send));
@@ -1239,4 +1245,41 @@ void main() {
12391245
maxHeight: verticalPadding + 170 * 1.5, maxVisibleLines: 6);
12401246
});
12411247
});
1248+
1249+
group('ComposeBoxState new-event-queue transition', () {
1250+
testWidgets('content input not cleared when store changes', (tester) async {
1251+
// Regression test for: https://github.com/zulip/zulip-flutter/issues/1470
1252+
1253+
TypingNotifier.debugEnable = false;
1254+
addTearDown(TypingNotifier.debugReset);
1255+
1256+
final channel = eg.stream();
1257+
await prepareComposeBox(tester,
1258+
narrow: eg.topicNarrow(channel.streamId, 'topic'), streams: [channel]);
1259+
1260+
await enterContent(tester, 'some content');
1261+
checkContentInputValue(tester, 'some content');
1262+
1263+
store.updateMachine!
1264+
..debugPauseLoop()
1265+
..poll()
1266+
..debugPrepareLoopError(
1267+
eg.apiExceptionBadEventQueueId(queueId: store.queueId))
1268+
..debugAdvanceLoop();
1269+
await tester.pump();
1270+
1271+
// new store replaces the old one
1272+
final newStore = testBinding.globalStore.perAccountSync(eg.selfAccount.id)!;
1273+
check(store).not((it) => it.identicalTo(newStore));
1274+
1275+
// new store has the same basic boring data so the compose box
1276+
// has its content input instead of no-posting-permission error banner
1277+
// TODO instead, add data by preparing new store's initial snapshot
1278+
await newStore.addStream(channel);
1279+
await newStore.addUser(eg.selfUser);
1280+
await tester.pump();
1281+
1282+
checkContentInputValue(tester, 'some content');
1283+
});
1284+
});
12421285
}

0 commit comments

Comments
 (0)