@@ -7,9 +7,11 @@ import '../api/exception.dart';
77import '../api/model/model.dart' ;
88import '../api/model/narrow.dart' ;
99import '../api/route/messages.dart' ;
10+ import '../api/route/channels.dart' as channels_api;
1011import '../generated/l10n/zulip_localizations.dart' ;
1112import '../model/binding.dart' ;
1213import '../model/narrow.dart' ;
14+ import '../model/realm.dart' ;
1315import 'dialog.dart' ;
1416import 'store.dart' ;
1517
@@ -239,6 +241,55 @@ abstract final class ZulipAction {
239241
240242 return fetchedMessage? .content;
241243 }
244+
245+ /// Unsubscribe from a channel, possibly after a confirmation dialog,
246+ /// showing an error dialog on failure.
247+ ///
248+ /// A confirmation dialog is shown if the user would not have permission
249+ /// to resubscribe.
250+ static Future <void > unsubscribeFromChannel (BuildContext context, {
251+ required int channelId,
252+ }) async {
253+ final store = PerAccountStoreWidget .of (context);
254+ final subscription = store.subscriptions[channelId];
255+ if (subscription == null ) return ; // TODO could give feedback
256+
257+ // TODO(future) check if the self-user is a guest and the channel is not web-public
258+ final couldResubscribe = ! subscription.inviteOnly
259+ || store.selfHasPermissionForGroupSetting (subscription.canSubscribeGroup,
260+ GroupSettingType .stream, 'can_subscribe_group' );
261+ if (! couldResubscribe) {
262+ // TODO(#1788) warn if org would lose content access (nobody can subscribe)
263+ final zulipLocalizations = ZulipLocalizations .of (context);
264+
265+ final dialog = showSuggestedActionDialog (context: context,
266+ title: zulipLocalizations.unsubscribeConfirmationDialogTitle ('#${subscription .name }' ),
267+ message: zulipLocalizations.unsubscribeConfirmationDialogMessageCannotResubscribe,
268+ destructiveActionButton: true ,
269+ actionButtonText: zulipLocalizations.unsubscribeConfirmationDialogConfirmButton);
270+ if (await dialog.result != true ) return ;
271+ if (! context.mounted) return ;
272+ }
273+
274+ try {
275+ await channels_api.unsubscribeFromChannel (PerAccountStoreWidget .of (context).connection,
276+ subscriptions: [subscription.name]);
277+ } catch (e) {
278+ if (! context.mounted) return ;
279+
280+ String ? errorMessage;
281+ switch (e) {
282+ case ZulipApiException ():
283+ errorMessage = e.message;
284+ // TODO(#741) specific messages for common errors, like network errors
285+ // (support with reusable code)
286+ default :
287+ }
288+
289+ final title = ZulipLocalizations .of (context).unsubscribeFailedTitle;
290+ showErrorDialog (context: context, title: title, message: errorMessage);
291+ }
292+ }
242293}
243294
244295/// Methods that act through platform APIs and show feedback in the UI.
0 commit comments