Skip to content

Offer subscribe/unsubscribe in channel action sheet #1790

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified assets/icons/ZulipIcons.ttf
Binary file not shown.
4 changes: 4 additions & 0 deletions assets/icons/circle_x.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions assets/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,14 @@
"@permissionsDeniedReadExternalStorage": {
"description": "Message for dialog asking the user to grant permissions for external storage read access."
},
"actionSheetOptionSubscribe": "Subscribe",
"@actionSheetOptionSubscribe": {
"description": "Label in the channel action sheet for subscribing to the channel."
},
"subscribeFailedTitle": "Failed to subscribe",
"@subscribeFailedTitle": {
"description": "Error title when subscribing to a channel failed."
},
"actionSheetOptionMarkChannelAsRead": "Mark channel as read",
"@actionSheetOptionMarkChannelAsRead": {
"description": "Label for marking a channel as read."
Expand All @@ -108,6 +116,29 @@
"@actionSheetOptionListOfTopics": {
"description": "Label for navigating to a channel's topic-list page."
},
"actionSheetOptionUnsubscribe": "Unsubscribe",
"@actionSheetOptionUnsubscribe": {
"description": "Label in the channel action sheet for unsubscribing from the channel."
},
"unsubscribeConfirmationDialogTitle": "Unsubscribe from {channelName}?",
"@unsubscribeConfirmationDialogTitle": {
"description": "Title for a confirmation dialog for unsubscribing from a channel.",
"placeholders": {
"channelName": {"type": "String", "example": "mobile"}
}
},
"unsubscribeConfirmationDialogMessageMaybeCannotResubscribe": "Once you leave this channel, you might not be able to rejoin.",
"@unsubscribeConfirmationDialogMessageMaybeCannotResubscribe": {
"description": "Message for a confirmation dialog for unsubscribing from a channel when you might not have permission to resubscribe."
},
"unsubscribeConfirmationDialogConfirmButton": "Unsubscribe",
"@unsubscribeConfirmationDialogConfirmButton": {
"description": "Label for the 'Unsubscribe' button on a confirmation dialog for unsubscribing from a channel."
},
"unsubscribeFailedTitle": "Failed to unsubscribe",
"@unsubscribeFailedTitle": {
"description": "Error title when unsubscribing from a channel failed."
},
"actionSheetOptionMuteTopic": "Mute topic",
"@actionSheetOptionMuteTopic": {
"description": "Label for muting a topic on action sheet."
Expand Down
36 changes: 36 additions & 0 deletions lib/api/route/channels.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,42 @@ import '../core.dart';
import '../model/model.dart';
part 'channels.g.dart';

/// https://zulip.com/api/subscribe
///
/// [subscriptions] is a list of channel names.
/// (This is one of the few remaining areas where the Zulip API hasn't migrated
/// to using IDs.)
Future<void> subscribeToChannel(ApiConnection connection, {
// TODO(server-future): This should use a stream ID, not stream name.
// (Keep dartdoc up to date.)
// Server issue: https://github.com/zulip/zulip/issues/10744
required List<String> subscriptions,
List<int>? principals,
}) {
return connection.post('subscribeToChannel', (_) {}, 'users/me/subscriptions', {
'subscriptions': subscriptions.map((name) => {'name': name}).toList(),
if (principals != null) 'principals': principals,
});
}

/// https://zulip.com/api/unsubscribe
///
/// [subscriptions] is a list of channel names.
/// (This is one of the few remaining areas where the Zulip API hasn't migrated
/// to using IDs.)
Future<void> unsubscribeFromChannel(ApiConnection connection, {
// TODO(server-future): This should use a stream ID, not stream name.
// (Keep dartdoc up to date.)
// Server issue: https://github.com/zulip/zulip/issues/10744
required List<String> subscriptions,
List<int>? principals,
}) {
return connection.delete('unsubscribeFromChannel', (_) {}, 'users/me/subscriptions', {
'subscriptions': subscriptions,
if (principals != null) 'principals': principals,
});
}

/// https://zulip.com/api/get-stream-topics
Future<GetStreamTopicsResult> getStreamTopics(ApiConnection connection, {
required int streamId,
Expand Down
42 changes: 42 additions & 0 deletions lib/generated/l10n/zulip_localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,18 @@ abstract class ZulipLocalizations {
/// **'To upload files, please grant Zulip additional permissions in Settings.'**
String get permissionsDeniedReadExternalStorage;

/// Label in the channel context menu for subscribing to the channel.
///
/// In en, this message translates to:
/// **'Subscribe'**
String get actionSheetOptionSubscribe;

/// Error title when subscribing to a channel failed.
///
/// In en, this message translates to:
/// **'Failed to subscribe'**
String get subscribeFailedTitle;

/// Label for marking a channel as read.
///
/// In en, this message translates to:
Expand All @@ -293,6 +305,36 @@ abstract class ZulipLocalizations {
/// **'List of topics'**
String get actionSheetOptionListOfTopics;

/// Label in the channel context menu for unsubscribing from the channel.
///
/// In en, this message translates to:
/// **'Unsubscribe'**
String get actionSheetOptionUnsubscribe;

/// Title for a confirmation dialog for unsubscribing from a channel.
///
/// In en, this message translates to:
/// **'Unsubscribe from {channelName}?'**
String unsubscribeConfirmationDialogTitle(String channelName);

/// Message for a confirmation dialog for unsubscribing from a channel when you might not have permission to resubscribe.
///
/// In en, this message translates to:
/// **'Once you leave this channel, you might not be able to rejoin.'**
String get unsubscribeConfirmationDialogMessageMaybeCannotResubscribe;

/// Label for the 'Unsubscribe' button on a confirmation dialog for unsubscribing from a channel.
///
/// In en, this message translates to:
/// **'Unsubscribe'**
String get unsubscribeConfirmationDialogConfirmButton;

/// Error title when unsubscribing from a channel failed.
///
/// In en, this message translates to:
/// **'Failed to unsubscribe'**
String get unsubscribeFailedTitle;

/// Label for muting a topic on action sheet.
///
/// In en, this message translates to:
Expand Down
24 changes: 24 additions & 0 deletions lib/generated/l10n/zulip_localizations_ar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ class ZulipLocalizationsAr extends ZulipLocalizations {
String get permissionsDeniedReadExternalStorage =>
'To upload files, please grant Zulip additional permissions in Settings.';

@override
String get actionSheetOptionSubscribe => 'Subscribe';

@override
String get subscribeFailedTitle => 'Failed to subscribe';

@override
String get actionSheetOptionMarkChannelAsRead => 'Mark channel as read';

Expand All @@ -96,6 +102,24 @@ class ZulipLocalizationsAr extends ZulipLocalizations {
@override
String get actionSheetOptionListOfTopics => 'List of topics';

@override
String get actionSheetOptionUnsubscribe => 'Unsubscribe';

@override
String unsubscribeConfirmationDialogTitle(String channelName) {
return 'Unsubscribe from $channelName?';
}

@override
String get unsubscribeConfirmationDialogMessageMaybeCannotResubscribe =>
'Once you leave this channel, you might not be able to rejoin.';

@override
String get unsubscribeConfirmationDialogConfirmButton => 'Unsubscribe';

@override
String get unsubscribeFailedTitle => 'Failed to unsubscribe';

@override
String get actionSheetOptionMuteTopic => 'Mute topic';

Expand Down
24 changes: 24 additions & 0 deletions lib/generated/l10n/zulip_localizations_de.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ class ZulipLocalizationsDe extends ZulipLocalizations {
String get permissionsDeniedReadExternalStorage =>
'Bitte gewähre Zulip zusätzliche Berechtigungen in den Einstellungen, um Dateien hochzuladen.';

@override
String get actionSheetOptionSubscribe => 'Subscribe';

@override
String get subscribeFailedTitle => 'Failed to subscribe';

@override
String get actionSheetOptionMarkChannelAsRead =>
'Kanal als gelesen markieren';
Expand All @@ -98,6 +104,24 @@ class ZulipLocalizationsDe extends ZulipLocalizations {
@override
String get actionSheetOptionListOfTopics => 'Themenliste';

@override
String get actionSheetOptionUnsubscribe => 'Unsubscribe';

@override
String unsubscribeConfirmationDialogTitle(String channelName) {
return 'Unsubscribe from $channelName?';
}

@override
String get unsubscribeConfirmationDialogMessageMaybeCannotResubscribe =>
'Once you leave this channel, you might not be able to rejoin.';

@override
String get unsubscribeConfirmationDialogConfirmButton => 'Unsubscribe';

@override
String get unsubscribeFailedTitle => 'Failed to unsubscribe';

@override
String get actionSheetOptionMuteTopic => 'Thema stummschalten';

Expand Down
24 changes: 24 additions & 0 deletions lib/generated/l10n/zulip_localizations_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ class ZulipLocalizationsEn extends ZulipLocalizations {
String get permissionsDeniedReadExternalStorage =>
'To upload files, please grant Zulip additional permissions in Settings.';

@override
String get actionSheetOptionSubscribe => 'Subscribe';

@override
String get subscribeFailedTitle => 'Failed to subscribe';

@override
String get actionSheetOptionMarkChannelAsRead => 'Mark channel as read';

Expand All @@ -96,6 +102,24 @@ class ZulipLocalizationsEn extends ZulipLocalizations {
@override
String get actionSheetOptionListOfTopics => 'List of topics';

@override
String get actionSheetOptionUnsubscribe => 'Unsubscribe';

@override
String unsubscribeConfirmationDialogTitle(String channelName) {
return 'Unsubscribe from $channelName?';
}

@override
String get unsubscribeConfirmationDialogMessageMaybeCannotResubscribe =>
'Once you leave this channel, you might not be able to rejoin.';

@override
String get unsubscribeConfirmationDialogConfirmButton => 'Unsubscribe';

@override
String get unsubscribeFailedTitle => 'Failed to unsubscribe';

@override
String get actionSheetOptionMuteTopic => 'Mute topic';

Expand Down
24 changes: 24 additions & 0 deletions lib/generated/l10n/zulip_localizations_fr.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ class ZulipLocalizationsFr extends ZulipLocalizations {
String get permissionsDeniedReadExternalStorage =>
'To upload files, please grant Zulip additional permissions in Settings.';

@override
String get actionSheetOptionSubscribe => 'Subscribe';

@override
String get subscribeFailedTitle => 'Failed to subscribe';

@override
String get actionSheetOptionMarkChannelAsRead => 'Mark channel as read';

Expand All @@ -96,6 +102,24 @@ class ZulipLocalizationsFr extends ZulipLocalizations {
@override
String get actionSheetOptionListOfTopics => 'List of topics';

@override
String get actionSheetOptionUnsubscribe => 'Unsubscribe';

@override
String unsubscribeConfirmationDialogTitle(String channelName) {
return 'Unsubscribe from $channelName?';
}

@override
String get unsubscribeConfirmationDialogMessageMaybeCannotResubscribe =>
'Once you leave this channel, you might not be able to rejoin.';

@override
String get unsubscribeConfirmationDialogConfirmButton => 'Unsubscribe';

@override
String get unsubscribeFailedTitle => 'Failed to unsubscribe';

@override
String get actionSheetOptionMuteTopic => 'Mute topic';

Expand Down
24 changes: 24 additions & 0 deletions lib/generated/l10n/zulip_localizations_it.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ class ZulipLocalizationsIt extends ZulipLocalizations {
String get permissionsDeniedReadExternalStorage =>
'Per caricare file, bisogna concedere a Zulip autorizzazioni aggiuntive nelle Impostazioni.';

@override
String get actionSheetOptionSubscribe => 'Subscribe';

@override
String get subscribeFailedTitle => 'Failed to subscribe';

@override
String get actionSheetOptionMarkChannelAsRead => 'Segna il canale come letto';

Expand All @@ -97,6 +103,24 @@ class ZulipLocalizationsIt extends ZulipLocalizations {
@override
String get actionSheetOptionListOfTopics => 'Elenco degli argomenti';

@override
String get actionSheetOptionUnsubscribe => 'Unsubscribe';

@override
String unsubscribeConfirmationDialogTitle(String channelName) {
return 'Unsubscribe from $channelName?';
}

@override
String get unsubscribeConfirmationDialogMessageMaybeCannotResubscribe =>
'Once you leave this channel, you might not be able to rejoin.';

@override
String get unsubscribeConfirmationDialogConfirmButton => 'Unsubscribe';

@override
String get unsubscribeFailedTitle => 'Failed to unsubscribe';

@override
String get actionSheetOptionMuteTopic => 'Silenzia argomento';

Expand Down
24 changes: 24 additions & 0 deletions lib/generated/l10n/zulip_localizations_ja.dart
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ class ZulipLocalizationsJa extends ZulipLocalizations {
String get permissionsDeniedReadExternalStorage =>
'To upload files, please grant Zulip additional permissions in Settings.';

@override
String get actionSheetOptionSubscribe => 'Subscribe';

@override
String get subscribeFailedTitle => 'Failed to subscribe';

@override
String get actionSheetOptionMarkChannelAsRead => 'チャンネルを既読にする';

Expand All @@ -95,6 +101,24 @@ class ZulipLocalizationsJa extends ZulipLocalizations {
@override
String get actionSheetOptionListOfTopics => 'トピック一覧';

@override
String get actionSheetOptionUnsubscribe => 'Unsubscribe';

@override
String unsubscribeConfirmationDialogTitle(String channelName) {
return 'Unsubscribe from $channelName?';
}

@override
String get unsubscribeConfirmationDialogMessageMaybeCannotResubscribe =>
'Once you leave this channel, you might not be able to rejoin.';

@override
String get unsubscribeConfirmationDialogConfirmButton => 'Unsubscribe';

@override
String get unsubscribeFailedTitle => 'Failed to unsubscribe';

@override
String get actionSheetOptionMuteTopic => 'トピックをミュート';

Expand Down
Loading
Loading