Skip to content

Commit c567e9e

Browse files
committed
channel_list: Ensure subscription toggle is disabled while loading
1 parent 4803049 commit c567e9e

File tree

2 files changed

+56
-6
lines changed

2 files changed

+56
-6
lines changed

lib/widgets/channel_list.dart

+23-6
Original file line numberDiff line numberDiff line change
@@ -95,27 +95,44 @@ class ChannelItem extends StatelessWidget {
9595
overflow: TextOverflow.ellipsis),
9696
])),
9797
const SizedBox(width: 8),
98-
_ChannelItemSubscriptionToggle(stream: stream),
98+
_ChannelItemSubscriptionToggle(stream: stream, channelItemContext: context),
9999
]))));
100100
}
101101
}
102102

103-
class _ChannelItemSubscriptionToggle extends StatelessWidget {
104-
const _ChannelItemSubscriptionToggle({required this.stream});
103+
class _ChannelItemSubscriptionToggle extends StatefulWidget {
104+
const _ChannelItemSubscriptionToggle({required this.stream, required this.channelItemContext});
105105

106106
final ZulipStream stream;
107+
final BuildContext channelItemContext;
108+
109+
@override
110+
State<_ChannelItemSubscriptionToggle> createState() => _ChannelItemSubscriptionToggleState();
111+
}
112+
113+
class _ChannelItemSubscriptionToggleState extends State<_ChannelItemSubscriptionToggle> {
114+
bool _isLoading = false;
115+
116+
void _setIsLoading(bool value) {
117+
if (!mounted) return;
118+
setState(() => _isLoading = value);
119+
}
107120

108121
@override
109122
Widget build(BuildContext context) {
110123
final colorScheme = Theme.of(context).colorScheme;
111-
final (icon, color, onPressed) = stream is Subscription
124+
final (icon, color, onPressed) = widget.stream is Subscription
112125
? (Icons.check, colorScheme.primary, _unsubscribeFromChannel)
113126
: (Icons.add, null, _subscribeToChannel);
114127

115128
return IconButton(
116129
color: color,
117130
icon: Icon(icon),
118-
onPressed: () => onPressed(context, stream));
131+
onPressed: _isLoading ? null : () async {
132+
_setIsLoading(true);
133+
await onPressed(context, widget.stream);
134+
_setIsLoading(false);
135+
});
119136
}
120137

121138
Future<void> _unsubscribeFromChannel(BuildContext context, ZulipStream stream) async {
@@ -165,7 +182,7 @@ class _ChannelItemSubscriptionToggle extends StatelessWidget {
165182
} catch (e) {
166183
if (!context.mounted) return;
167184
final zulipLocalizations = ZulipLocalizations.of(context);
168-
await showErrorDialog(context: context,
185+
showErrorDialog(context: context,
169186
title: zulipLocalizations.errorFailedToSubscribedToChannel(stream.name),
170187
message: e.toString()); // TODO(#741): extract user-facing message better
171188
}

test/widgets/channel_list_test.dart

+33
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,39 @@ void main() {
134134
check(find.byIcon(Icons.check).evaluate()).isEmpty();
135135
});
136136

137+
testWidgets('is disabled while loading', (WidgetTester tester) async {
138+
final stream = eg.stream();
139+
await setupChannelListPage(tester, streams: [stream], subscriptions: []);
140+
connection.prepare(json: SubscribeToChannelsResult(
141+
subscribed: {eg.selfUser.email: [stream.name]},
142+
alreadySubscribed: {}).toJson()); await tapSubscribeButton(tester);
143+
await tester.pump();
144+
145+
check(tester.widget<IconButton>(
146+
find.byType(IconButton)).onPressed).isNull();
147+
148+
await tester.pump(const Duration(seconds: 2));
149+
150+
check(tester.widget<IconButton>(
151+
find.byType(IconButton)).onPressed).isNotNull();
152+
});
153+
154+
testWidgets('is disabled while loading and enabled back when loading fails', (WidgetTester tester) async {
155+
final stream = eg.stream();
156+
await setupChannelListPage(tester, streams: [stream], subscriptions: []);
157+
connection.prepare(exception: http.ClientException('Oops'), delay: const Duration(seconds: 2));
158+
await tapSubscribeButton(tester);
159+
await tester.pump();
160+
161+
check(tester.widget<IconButton>(
162+
find.byType(IconButton)).onPressed).isNull();
163+
164+
await tester.pump(const Duration(seconds: 2));
165+
166+
check(tester.widget<IconButton>(
167+
find.byType(IconButton)).onPressed).isNotNull();
168+
});
169+
137170
group('subscribe', () {
138171
testWidgets('is shown only for streams that user is not subscribed to', (tester) async {
139172
final streams = [eg.stream(), eg.stream(), eg.subscription(eg.stream())];

0 commit comments

Comments
 (0)