@@ -8,6 +8,7 @@ import 'package:share_plus/share_plus.dart';
8
8
9
9
import '../api/exception.dart' ;
10
10
import '../api/model/model.dart' ;
11
+ import '../api/route/channels.dart' ;
11
12
import '../api/route/messages.dart' ;
12
13
import '../model/internal_link.dart' ;
13
14
import '../model/narrow.dart' ;
@@ -140,6 +141,199 @@ class ActionSheetCancelButton extends StatelessWidget {
140
141
}
141
142
}
142
143
144
+ /// Show a sheet of actions you can take on a topic.
145
+ void showTopicActionSheet (BuildContext context, {
146
+ required int channelId,
147
+ required String topic,
148
+ }) {
149
+ final narrow = TopicNarrow (channelId, topic);
150
+ UserTopicUpdateButton button ({
151
+ UserTopicVisibilityPolicy ? from,
152
+ required UserTopicVisibilityPolicy to,
153
+ }) {
154
+ return UserTopicUpdateButton (
155
+ currentVisibilityPolicy: from,
156
+ newVisibilityPolicy: to,
157
+ narrow: narrow,
158
+ pageContext: context);
159
+ }
160
+
161
+ final mute = button (to: UserTopicVisibilityPolicy .muted);
162
+ final unmute = button (from: UserTopicVisibilityPolicy .muted,
163
+ to: UserTopicVisibilityPolicy .none);
164
+ final unmuteInMutedChannel = button (to: UserTopicVisibilityPolicy .unmuted);
165
+ final follow = button (to: UserTopicVisibilityPolicy .followed);
166
+ final unfollow = button (from: UserTopicVisibilityPolicy .followed,
167
+ to: UserTopicVisibilityPolicy .none);
168
+
169
+ final store = PerAccountStoreWidget .of (context);
170
+ final channelMuted = store.subscriptions[channelId]? .isMuted;
171
+ final visibilityPolicy = store.topicVisibilityPolicy (channelId, topic);
172
+
173
+ // TODO(server-7): simplify this condition away
174
+ final supportsUnmutingTopics = store.connection.zulipFeatureLevel! >= 170 ;
175
+ // TODO(server-8): simplify this condition away
176
+ final supportsFollowingTopics = store.connection.zulipFeatureLevel! >= 219 ;
177
+
178
+ final optionButtons = < Widget > [];
179
+ if (channelMuted != null && ! channelMuted) {
180
+ switch (visibilityPolicy) {
181
+ case UserTopicVisibilityPolicy .muted:
182
+ optionButtons.add (unmute);
183
+ if (supportsFollowingTopics) {
184
+ optionButtons.add (follow);
185
+ }
186
+ case UserTopicVisibilityPolicy .none:
187
+ case UserTopicVisibilityPolicy .unmuted:
188
+ optionButtons.add (mute);
189
+ if (supportsFollowingTopics) {
190
+ optionButtons.add (follow);
191
+ }
192
+ case UserTopicVisibilityPolicy .followed:
193
+ optionButtons.add (mute);
194
+ if (supportsFollowingTopics) {
195
+ optionButtons.add (unfollow);
196
+ }
197
+ case UserTopicVisibilityPolicy .unknown:
198
+ assert (false );
199
+ return ;
200
+ }
201
+ } else if (channelMuted != null && channelMuted) {
202
+ if (supportsUnmutingTopics) {
203
+ switch (visibilityPolicy) {
204
+ case UserTopicVisibilityPolicy .none:
205
+ case UserTopicVisibilityPolicy .muted:
206
+ optionButtons.add (unmuteInMutedChannel);
207
+ if (supportsFollowingTopics) {
208
+ optionButtons.add (follow);
209
+ }
210
+ case UserTopicVisibilityPolicy .unmuted:
211
+ optionButtons.add (mute);
212
+ if (supportsFollowingTopics) {
213
+ optionButtons.add (follow);
214
+ }
215
+ case UserTopicVisibilityPolicy .followed:
216
+ optionButtons.add (mute);
217
+ if (supportsFollowingTopics) {
218
+ optionButtons.add (unfollow);
219
+ }
220
+ case UserTopicVisibilityPolicy .unknown:
221
+ assert (false );
222
+ return ;
223
+ }
224
+ }
225
+ } else {
226
+ // Not subscribed to the channel; there is no user topic change to be made.
227
+ return ;
228
+ }
229
+
230
+ if (optionButtons.isEmpty) {
231
+ assert (! supportsUnmutingTopics);
232
+ return ;
233
+ }
234
+
235
+ _showActionSheet (context, optionButtons: optionButtons);
236
+ }
237
+
238
+ class UserTopicUpdateButton extends ActionSheetMenuItemButton {
239
+ const UserTopicUpdateButton ({
240
+ super .key,
241
+ this .currentVisibilityPolicy,
242
+ required this .newVisibilityPolicy,
243
+ required this .narrow,
244
+ required super .pageContext,
245
+ });
246
+
247
+ final UserTopicVisibilityPolicy ? currentVisibilityPolicy;
248
+ final UserTopicVisibilityPolicy newVisibilityPolicy;
249
+ final TopicNarrow narrow;
250
+
251
+ @override IconData get icon {
252
+ switch (newVisibilityPolicy) {
253
+ case UserTopicVisibilityPolicy .none:
254
+ return ZulipIcons .inherit;
255
+ case UserTopicVisibilityPolicy .muted:
256
+ return ZulipIcons .mute;
257
+ case UserTopicVisibilityPolicy .unmuted:
258
+ return ZulipIcons .unmute;
259
+ case UserTopicVisibilityPolicy .followed:
260
+ return ZulipIcons .follow;
261
+ case UserTopicVisibilityPolicy .unknown:
262
+ assert (false );
263
+ return ZulipIcons .inherit;
264
+ }
265
+ }
266
+
267
+ @override
268
+ String label (ZulipLocalizations zulipLocalizations) {
269
+ switch ((currentVisibilityPolicy, newVisibilityPolicy)) {
270
+ case (UserTopicVisibilityPolicy .muted, UserTopicVisibilityPolicy .none):
271
+ return zulipLocalizations.actionSheetOptionUnmuteTopic;
272
+ case (UserTopicVisibilityPolicy .followed, UserTopicVisibilityPolicy .none):
273
+ return zulipLocalizations.actionSheetOptionUnfollowTopic;
274
+
275
+ case (_, UserTopicVisibilityPolicy .muted):
276
+ return zulipLocalizations.actionSheetOptionMuteTopic;
277
+ case (_, UserTopicVisibilityPolicy .unmuted):
278
+ return zulipLocalizations.actionSheetOptionUnmuteTopic;
279
+ case (_, UserTopicVisibilityPolicy .followed):
280
+ return zulipLocalizations.actionSheetOptionFollowTopic;
281
+
282
+ case (_, UserTopicVisibilityPolicy .none):
283
+ case (_, UserTopicVisibilityPolicy .unknown):
284
+ assert (false );
285
+ return '' ;
286
+ }
287
+ }
288
+
289
+ String _errorTitle (ZulipLocalizations zulipLocalizations) {
290
+ switch ((currentVisibilityPolicy, newVisibilityPolicy)) {
291
+ case (UserTopicVisibilityPolicy .muted, UserTopicVisibilityPolicy .none):
292
+ return zulipLocalizations.errorUnmuteTopicFailed;
293
+ case (UserTopicVisibilityPolicy .followed, UserTopicVisibilityPolicy .none):
294
+ return zulipLocalizations.errorUnfollowTopicFailed;
295
+
296
+ case (_, UserTopicVisibilityPolicy .muted):
297
+ return zulipLocalizations.errorMuteTopicFailed;
298
+ case (_, UserTopicVisibilityPolicy .unmuted):
299
+ return zulipLocalizations.errorUnmuteTopicFailed;
300
+ case (_, UserTopicVisibilityPolicy .followed):
301
+ return zulipLocalizations.errorFollowTopicFailed;
302
+
303
+ case (_, UserTopicVisibilityPolicy .none):
304
+ case (_, UserTopicVisibilityPolicy .unknown):
305
+ assert (false );
306
+ return '' ;
307
+ }
308
+ }
309
+
310
+ @override void onPressed () async {
311
+ try {
312
+ await updateUserTopic (
313
+ PerAccountStoreWidget .of (pageContext).connection,
314
+ streamId: narrow.streamId,
315
+ topic: narrow.topic,
316
+ visibilityPolicy: newVisibilityPolicy);
317
+ } catch (e) {
318
+ if (! pageContext.mounted) return ;
319
+
320
+ String ? errorMessage;
321
+
322
+ switch (e) {
323
+ case ZulipApiException ():
324
+ errorMessage = e.message;
325
+ // TODO(#741) specific messages for common errors, like network errors
326
+ // (support with reusable code)
327
+ default :
328
+ }
329
+
330
+ final zulipLocalizations = ZulipLocalizations .of (pageContext);
331
+ showErrorDialog (context: pageContext,
332
+ title: _errorTitle (zulipLocalizations), message: errorMessage);
333
+ }
334
+ }
335
+ }
336
+
143
337
/// Show a sheet of actions you can take on a message in the message list.
144
338
///
145
339
/// Must have a [MessageListPage] ancestor.
0 commit comments