diff --git a/assets/l10n/app_en.arb b/assets/l10n/app_en.arb index a5bb75779a..f0ad5a714d 100644 --- a/assets/l10n/app_en.arb +++ b/assets/l10n/app_en.arb @@ -31,6 +31,10 @@ "@upgradeWelcomeDialogDismiss": { "description": "Label for button dismissing dialog shown on first upgrade from the legacy Zulip app." }, + "learnMoreButtonLabel": "Learn more", + "@learnMoreButtonLabel": { + "description": "Label for a 'Learn more' button." + }, "chooseAccountPageTitle": "Choose account", "@chooseAccountPageTitle": { "description": "Title for the page to choose between Zulip accounts." @@ -530,6 +534,79 @@ "others": {"type": "String", "example": "Alice, Bob"} } }, + "emptyMessageList": "There are no messages here.", + "@emptyMessageList": { + "description": "Placeholder for some message-list pages when there are no messages." + }, + "emptyMessageListCombinedFeed": "There are no messages in your combined feed.", + "@emptyMessageListCombinedFeed": { + "description": "Placeholder for the 'Combined feed' page when there are no messages." + }, + "emptyMessageListPrivateChannelNotSubscribed": "This channel is private.", + "@emptyMessageListPrivateChannelNotSubscribed": { + "description": "Placeholder for a channel page when there are no messages, the channel is private, and you are not subscribed." + }, + "emptyMessageListChannelUnavailable": "This channel doesn’t exist, or you are not allowed to view it.", + "@emptyMessageListChannelUnavailable": { + "description": "Placeholder for a channel page when there are no messages and the channel does not exist or you don't have access to it." + }, + "emptyMessageListSelfDmHeader": "You have not sent any direct messages to yourself yet!", + "@emptyMessageListSelfDmHeader": { + "description": "Placeholder for the self-DM page when there are no messages." + }, + "emptyMessageListSelfDmMessage": "Use this space for personal notes, or to test out Zulip features.", + "@emptyMessageListSelfDmMessage": { + "description": "Extra detail in the placeholder for the self-DM page when there are no messages." + }, + "emptyMessageListDm": "You have no direct messages with {person} yet.", + "@emptyMessageListDm": { + "description": "Placeholder for a 1:1 DM page when there are no messages.", + "placeholders": { + "person": {"type": "String", "example": "Alice"} + } + }, + "emptyMessageListDmDeactivatedUser": "You have no direct messages with {person}.", + "@emptyMessageListDmDeactivatedUser": { + "description": "Placeholder for a 1:1 DM page when there are no messages and the other user is deactivated.", + "placeholders": { + "person": {"type": "String", "example": "Alice"} + } + }, + "emptyMessageListDmUnknownUser": "You have no direct messages with this person.", + "@emptyMessageListDmUnknownUser": { + "description": "Placeholder for a 1:1 DM page when there are no messages and the other user's name is unavailable." + }, + "emptyMessageListGroupDm": "You have no direct messages with these users yet.", + "@emptyMessageListGroupDm": { + "description": "Placeholder for a group DM page when there are no messages." + }, + "emptyMessageListGroupDmDeactivatedUser": "You have no direct messages with these users.", + "@emptyMessageListGroupDmDeactivatedUser": { + "description": "Placeholder for a group DM page when there are no messages and one or more participants is deactivated." + }, + "emptyMessageListDmStartConversation": "Why not start the conversation?", + "@emptyMessageListDmStartConversation": { + "description": "Extra detail in the placeholder for some DM pages when there are no messages." + }, + "emptyMessageListMentionsHeader": "This view will show messages where you are mentioned.", + "@emptyMessageListMentionsHeader": { + "description": "Placeholder for the 'Mentions' page when there are no messages." + }, + "emptyMessageListMentionsMessage": "To call attention to a message, you can mention a user, a group, topic participants, or all subscribers to a channel. Type @ in the compose box, and choose who you’d like to mention from the list of suggestions.", + "@emptyMessageListMentionsMessage": { + "description": "Extra detail in the placeholder for the 'Mentions' page when there are no messages." + }, + "emptyMessageListStarredHeader": "You have no starred messages.", + "@emptyMessageListStarredHeader": { + "description": "Placeholder for the 'Starred' page when there are no messages." + }, + "emptyMessageListStarredMessage": "Starring messages is a good way to keep track of important messages, such as tasks you need to go back to, or useful references. To star a message, long-press it and tap “{button}.”", + "@emptyMessageListStarredMessage": { + "description": "Extra detail in the placeholder for the 'Starred' page when there are no messages. The {button} placeholder will be the button's translated text.", + "placeholders": { + "button": {"type": "String", "example": "Star message"} + } + }, "messageListGroupYouWithYourself": "Messages with yourself", "@messageListGroupYouWithYourself": { "description": "Message list recipient header for a DM group that only includes yourself." @@ -793,10 +870,14 @@ "@inboxPageTitle": { "description": "Title for the page with unreads." }, - "inboxEmptyPlaceholder": "There are no unread messages in your inbox. Use the buttons below to view the combined feed or list of channels.", - "@inboxEmptyPlaceholder": { + "inboxEmptyPlaceholderHeader": "There are no unread messages in your inbox.", + "@inboxEmptyPlaceholderHeader": { "description": "Centered text on the 'Inbox' page saying that there is no content to show." }, + "inboxEmptyPlaceholderMessage": "Use the buttons below to view the combined feed or list of channels.", + "@inboxEmptyPlaceholderMessage": { + "description": "Additional centered text on the 'Inbox' page saying that there is no content to show." + }, "recentDmConversationsPageTitle": "Direct messages", "@recentDmConversationsPageTitle": { "description": "Title for the page with a list of DM conversations." @@ -805,10 +886,14 @@ "@recentDmConversationsSectionHeader": { "description": "Heading for direct messages section on the 'Inbox' message view." }, - "recentDmConversationsEmptyPlaceholder": "You have no direct messages yet! Why not start the conversation?", - "@recentDmConversationsEmptyPlaceholder": { + "recentDmConversationsEmptyPlaceholderHeader": "You have no direct messages yet!", + "@recentDmConversationsEmptyPlaceholderHeader": { "description": "Centered text on the 'Direct messages' page saying that there is no content to show." }, + "recentDmConversationsEmptyPlaceholderMessage": "Why not start the conversation?", + "@recentDmConversationsEmptyPlaceholderMessage": { + "description": "Additional centered text on the 'Direct messages' page saying that there is no content to show." + }, "combinedFeedPageTitle": "Combined feed", "@combinedFeedPageTitle": { "description": "Page title for the 'Combined feed' message view." @@ -825,8 +910,8 @@ "@channelsPageTitle": { "description": "Title for the page with a list of subscribed channels." }, - "channelsEmptyPlaceholder": "You are not subscribed to any channels yet.", - "@channelsEmptyPlaceholder": { + "channelsEmptyPlaceholderHeader": "You are not subscribed to any channels yet.", + "@channelsEmptyPlaceholderHeader": { "description": "Centered text on the 'Channels' page saying that there is no content to show." }, "mainMenuMyProfile": "My profile", diff --git a/lib/generated/l10n/zulip_localizations.dart b/lib/generated/l10n/zulip_localizations.dart index 241a3bbd16..90bf812864 100644 --- a/lib/generated/l10n/zulip_localizations.dart +++ b/lib/generated/l10n/zulip_localizations.dart @@ -177,6 +177,12 @@ abstract class ZulipLocalizations { /// **'Let\'s go'** String get upgradeWelcomeDialogDismiss; + /// Label for a 'Learn more' button. + /// + /// In en, this message translates to: + /// **'Learn more'** + String get learnMoreButtonLabel; + /// Title for the page to choose between Zulip accounts. /// /// In en, this message translates to: @@ -845,6 +851,102 @@ abstract class ZulipLocalizations { /// **'DMs with {others}'** String dmsWithOthersPageTitle(String others); + /// Placeholder for some message-list pages when there are no messages. + /// + /// In en, this message translates to: + /// **'There are no messages here.'** + String get emptyMessageList; + + /// Placeholder for the 'Combined feed' page when there are no messages. + /// + /// In en, this message translates to: + /// **'There are no messages in your combined feed.'** + String get emptyMessageListCombinedFeed; + + /// Placeholder for a channel page when there are no messages, the channel is private, and you are not subscribed. + /// + /// In en, this message translates to: + /// **'This channel is private.'** + String get emptyMessageListPrivateChannelNotSubscribed; + + /// Placeholder for a channel page when there are no messages and the channel does not exist or you don't have access to it. + /// + /// In en, this message translates to: + /// **'This channel doesn’t exist, or you are not allowed to view it.'** + String get emptyMessageListChannelUnavailable; + + /// Placeholder for the self-DM page when there are no messages. + /// + /// In en, this message translates to: + /// **'You have not sent any direct messages to yourself yet!'** + String get emptyMessageListSelfDmHeader; + + /// Extra detail in the placeholder for the self-DM page when there are no messages. + /// + /// In en, this message translates to: + /// **'Use this space for personal notes, or to test out Zulip features.'** + String get emptyMessageListSelfDmMessage; + + /// Placeholder for a 1:1 DM page when there are no messages. + /// + /// In en, this message translates to: + /// **'You have no direct messages with {person} yet.'** + String emptyMessageListDm(String person); + + /// Placeholder for a 1:1 DM page when there are no messages and the other user is deactivated. + /// + /// In en, this message translates to: + /// **'You have no direct messages with {person}.'** + String emptyMessageListDmDeactivatedUser(String person); + + /// Placeholder for a 1:1 DM page when there are no messages and the other user's name is unavailable. + /// + /// In en, this message translates to: + /// **'You have no direct messages with this person.'** + String get emptyMessageListDmUnknownUser; + + /// Placeholder for a group DM page when there are no messages. + /// + /// In en, this message translates to: + /// **'You have no direct messages with these users yet.'** + String get emptyMessageListGroupDm; + + /// Placeholder for a group DM page when there are no messages and one or more participants is deactivated. + /// + /// In en, this message translates to: + /// **'You have no direct messages with these users.'** + String get emptyMessageListGroupDmDeactivatedUser; + + /// Extra detail in the placeholder for some DM pages when there are no messages. + /// + /// In en, this message translates to: + /// **'Why not start the conversation?'** + String get emptyMessageListDmStartConversation; + + /// Placeholder for the 'Mentions' page when there are no messages. + /// + /// In en, this message translates to: + /// **'This view will show messages where you are mentioned.'** + String get emptyMessageListMentionsHeader; + + /// Extra detail in the placeholder for the 'Mentions' page when there are no messages. + /// + /// In en, this message translates to: + /// **'To call attention to a message, you can mention a user, a group, topic participants, or all subscribers to a channel. Type @ in the compose box, and choose who you’d like to mention from the list of suggestions.'** + String get emptyMessageListMentionsMessage; + + /// Placeholder for the 'Starred' page when there are no messages. + /// + /// In en, this message translates to: + /// **'You have no starred messages.'** + String get emptyMessageListStarredHeader; + + /// Extra detail in the placeholder for the 'Starred' page when there are no messages. The {button} placeholder will be the button's translated text. + /// + /// In en, this message translates to: + /// **'Starring messages is a good way to keep track of important messages, such as tasks you need to go back to, or useful references. To star a message, long-press it and tap “{button}.”'** + String emptyMessageListStarredMessage(String button); + /// Message list recipient header for a DM group that only includes yourself. /// /// In en, this message translates to: @@ -1206,8 +1308,14 @@ abstract class ZulipLocalizations { /// Centered text on the 'Inbox' page saying that there is no content to show. /// /// In en, this message translates to: - /// **'There are no unread messages in your inbox. Use the buttons below to view the combined feed or list of channels.'** - String get inboxEmptyPlaceholder; + /// **'There are no unread messages in your inbox.'** + String get inboxEmptyPlaceholderHeader; + + /// Additional centered text on the 'Inbox' page saying that there is no content to show. + /// + /// In en, this message translates to: + /// **'Use the buttons below to view the combined feed or list of channels.'** + String get inboxEmptyPlaceholderMessage; /// Title for the page with a list of DM conversations. /// @@ -1224,8 +1332,14 @@ abstract class ZulipLocalizations { /// Centered text on the 'Direct messages' page saying that there is no content to show. /// /// In en, this message translates to: - /// **'You have no direct messages yet! Why not start the conversation?'** - String get recentDmConversationsEmptyPlaceholder; + /// **'You have no direct messages yet!'** + String get recentDmConversationsEmptyPlaceholderHeader; + + /// Additional centered text on the 'Direct messages' page saying that there is no content to show. + /// + /// In en, this message translates to: + /// **'Why not start the conversation?'** + String get recentDmConversationsEmptyPlaceholderMessage; /// Page title for the 'Combined feed' message view. /// @@ -1255,7 +1369,7 @@ abstract class ZulipLocalizations { /// /// In en, this message translates to: /// **'You are not subscribed to any channels yet.'** - String get channelsEmptyPlaceholder; + String get channelsEmptyPlaceholderHeader; /// Label for main-menu button leading to the user's own profile. /// diff --git a/lib/generated/l10n/zulip_localizations_ar.dart b/lib/generated/l10n/zulip_localizations_ar.dart index e62354d420..c56e195c9b 100644 --- a/lib/generated/l10n/zulip_localizations_ar.dart +++ b/lib/generated/l10n/zulip_localizations_ar.dart @@ -34,6 +34,9 @@ class ZulipLocalizationsAr extends ZulipLocalizations { @override String get upgradeWelcomeDialogDismiss => 'Let\'s go'; + @override + String get learnMoreButtonLabel => 'Learn more'; + @override String get chooseAccountPageTitle => 'Choose account'; @@ -434,6 +437,71 @@ class ZulipLocalizationsAr extends ZulipLocalizations { return 'DMs with $others'; } + @override + String get emptyMessageList => 'There are no messages here.'; + + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListPrivateChannelNotSubscribed => + 'This channel is private.'; + + @override + String get emptyMessageListChannelUnavailable => + 'This channel doesn’t exist, or you are not allowed to view it.'; + + @override + String get emptyMessageListSelfDmHeader => + 'You have not sent any direct messages to yourself yet!'; + + @override + String get emptyMessageListSelfDmMessage => + 'Use this space for personal notes, or to test out Zulip features.'; + + @override + String emptyMessageListDm(String person) { + return 'You have no direct messages with $person yet.'; + } + + @override + String emptyMessageListDmDeactivatedUser(String person) { + return 'You have no direct messages with $person.'; + } + + @override + String get emptyMessageListDmUnknownUser => + 'You have no direct messages with this person.'; + + @override + String get emptyMessageListGroupDm => + 'You have no direct messages with these users yet.'; + + @override + String get emptyMessageListGroupDmDeactivatedUser => + 'You have no direct messages with these users.'; + + @override + String get emptyMessageListDmStartConversation => + 'Why not start the conversation?'; + + @override + String get emptyMessageListMentionsHeader => + 'This view will show messages where you are mentioned.'; + + @override + String get emptyMessageListMentionsMessage => + 'To call attention to a message, you can mention a user, a group, topic participants, or all subscribers to a channel. Type @ in the compose box, and choose who you’d like to mention from the list of suggestions.'; + + @override + String get emptyMessageListStarredHeader => 'You have no starred messages.'; + + @override + String emptyMessageListStarredMessage(String button) { + return 'Starring messages is a good way to keep track of important messages, such as tasks you need to go back to, or useful references. To star a message, long-press it and tap “$button.”'; + } + @override String get messageListGroupYouWithYourself => 'Messages with yourself'; @@ -651,8 +719,12 @@ class ZulipLocalizationsAr extends ZulipLocalizations { String get inboxPageTitle => 'Inbox'; @override - String get inboxEmptyPlaceholder => - 'There are no unread messages in your inbox. Use the buttons below to view the combined feed or list of channels.'; + String get inboxEmptyPlaceholderHeader => + 'There are no unread messages in your inbox.'; + + @override + String get inboxEmptyPlaceholderMessage => + 'Use the buttons below to view the combined feed or list of channels.'; @override String get recentDmConversationsPageTitle => 'Direct messages'; @@ -661,8 +733,12 @@ class ZulipLocalizationsAr extends ZulipLocalizations { String get recentDmConversationsSectionHeader => 'Direct messages'; @override - String get recentDmConversationsEmptyPlaceholder => - 'You have no direct messages yet! Why not start the conversation?'; + String get recentDmConversationsEmptyPlaceholderHeader => + 'You have no direct messages yet!'; + + @override + String get recentDmConversationsEmptyPlaceholderMessage => + 'Why not start the conversation?'; @override String get combinedFeedPageTitle => 'Combined feed'; @@ -677,7 +753,7 @@ class ZulipLocalizationsAr extends ZulipLocalizations { String get channelsPageTitle => 'Channels'; @override - String get channelsEmptyPlaceholder => + String get channelsEmptyPlaceholderHeader => 'You are not subscribed to any channels yet.'; @override diff --git a/lib/generated/l10n/zulip_localizations_de.dart b/lib/generated/l10n/zulip_localizations_de.dart index a7965d81ad..b1f410ceaa 100644 --- a/lib/generated/l10n/zulip_localizations_de.dart +++ b/lib/generated/l10n/zulip_localizations_de.dart @@ -34,6 +34,9 @@ class ZulipLocalizationsDe extends ZulipLocalizations { @override String get upgradeWelcomeDialogDismiss => 'Los gehts'; + @override + String get learnMoreButtonLabel => 'Learn more'; + @override String get chooseAccountPageTitle => 'Konto auswählen'; @@ -449,6 +452,71 @@ class ZulipLocalizationsDe extends ZulipLocalizations { return 'DNs mit $others'; } + @override + String get emptyMessageList => 'There are no messages here.'; + + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListPrivateChannelNotSubscribed => + 'This channel is private.'; + + @override + String get emptyMessageListChannelUnavailable => + 'This channel doesn’t exist, or you are not allowed to view it.'; + + @override + String get emptyMessageListSelfDmHeader => + 'You have not sent any direct messages to yourself yet!'; + + @override + String get emptyMessageListSelfDmMessage => + 'Use this space for personal notes, or to test out Zulip features.'; + + @override + String emptyMessageListDm(String person) { + return 'You have no direct messages with $person yet.'; + } + + @override + String emptyMessageListDmDeactivatedUser(String person) { + return 'You have no direct messages with $person.'; + } + + @override + String get emptyMessageListDmUnknownUser => + 'You have no direct messages with this person.'; + + @override + String get emptyMessageListGroupDm => + 'You have no direct messages with these users yet.'; + + @override + String get emptyMessageListGroupDmDeactivatedUser => + 'You have no direct messages with these users.'; + + @override + String get emptyMessageListDmStartConversation => + 'Why not start the conversation?'; + + @override + String get emptyMessageListMentionsHeader => + 'This view will show messages where you are mentioned.'; + + @override + String get emptyMessageListMentionsMessage => + 'To call attention to a message, you can mention a user, a group, topic participants, or all subscribers to a channel. Type @ in the compose box, and choose who you’d like to mention from the list of suggestions.'; + + @override + String get emptyMessageListStarredHeader => 'You have no starred messages.'; + + @override + String emptyMessageListStarredMessage(String button) { + return 'Starring messages is a good way to keep track of important messages, such as tasks you need to go back to, or useful references. To star a message, long-press it and tap “$button.”'; + } + @override String get messageListGroupYouWithYourself => 'Nachrichten mit dir selbst'; @@ -672,8 +740,12 @@ class ZulipLocalizationsDe extends ZulipLocalizations { String get inboxPageTitle => 'Eingang'; @override - String get inboxEmptyPlaceholder => - 'Es sind keine ungelesenen Nachrichten in deinem Eingang. Verwende die Buttons unten um den kombinierten Feed oder die Kanalliste anzusehen.'; + String get inboxEmptyPlaceholderHeader => + 'There are no unread messages in your inbox.'; + + @override + String get inboxEmptyPlaceholderMessage => + 'Use the buttons below to view the combined feed or list of channels.'; @override String get recentDmConversationsPageTitle => 'Direktnachrichten'; @@ -682,8 +754,12 @@ class ZulipLocalizationsDe extends ZulipLocalizations { String get recentDmConversationsSectionHeader => 'Direktnachrichten'; @override - String get recentDmConversationsEmptyPlaceholder => - 'Du hast noch keine Direktnachrichten! Warum nicht die Unterhaltung beginnen?'; + String get recentDmConversationsEmptyPlaceholderHeader => + 'You have no direct messages yet!'; + + @override + String get recentDmConversationsEmptyPlaceholderMessage => + 'Why not start the conversation?'; @override String get combinedFeedPageTitle => 'Kombinierter Feed'; @@ -698,7 +774,8 @@ class ZulipLocalizationsDe extends ZulipLocalizations { String get channelsPageTitle => 'Kanäle'; @override - String get channelsEmptyPlaceholder => 'Du hast noch keine Kanäle abonniert.'; + String get channelsEmptyPlaceholderHeader => + 'You are not subscribed to any channels yet.'; @override String get mainMenuMyProfile => 'Mein Profil'; diff --git a/lib/generated/l10n/zulip_localizations_en.dart b/lib/generated/l10n/zulip_localizations_en.dart index 0178fe9406..58d8b17391 100644 --- a/lib/generated/l10n/zulip_localizations_en.dart +++ b/lib/generated/l10n/zulip_localizations_en.dart @@ -34,6 +34,9 @@ class ZulipLocalizationsEn extends ZulipLocalizations { @override String get upgradeWelcomeDialogDismiss => 'Let\'s go'; + @override + String get learnMoreButtonLabel => 'Learn more'; + @override String get chooseAccountPageTitle => 'Choose account'; @@ -434,6 +437,71 @@ class ZulipLocalizationsEn extends ZulipLocalizations { return 'DMs with $others'; } + @override + String get emptyMessageList => 'There are no messages here.'; + + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListPrivateChannelNotSubscribed => + 'This channel is private.'; + + @override + String get emptyMessageListChannelUnavailable => + 'This channel doesn’t exist, or you are not allowed to view it.'; + + @override + String get emptyMessageListSelfDmHeader => + 'You have not sent any direct messages to yourself yet!'; + + @override + String get emptyMessageListSelfDmMessage => + 'Use this space for personal notes, or to test out Zulip features.'; + + @override + String emptyMessageListDm(String person) { + return 'You have no direct messages with $person yet.'; + } + + @override + String emptyMessageListDmDeactivatedUser(String person) { + return 'You have no direct messages with $person.'; + } + + @override + String get emptyMessageListDmUnknownUser => + 'You have no direct messages with this person.'; + + @override + String get emptyMessageListGroupDm => + 'You have no direct messages with these users yet.'; + + @override + String get emptyMessageListGroupDmDeactivatedUser => + 'You have no direct messages with these users.'; + + @override + String get emptyMessageListDmStartConversation => + 'Why not start the conversation?'; + + @override + String get emptyMessageListMentionsHeader => + 'This view will show messages where you are mentioned.'; + + @override + String get emptyMessageListMentionsMessage => + 'To call attention to a message, you can mention a user, a group, topic participants, or all subscribers to a channel. Type @ in the compose box, and choose who you’d like to mention from the list of suggestions.'; + + @override + String get emptyMessageListStarredHeader => 'You have no starred messages.'; + + @override + String emptyMessageListStarredMessage(String button) { + return 'Starring messages is a good way to keep track of important messages, such as tasks you need to go back to, or useful references. To star a message, long-press it and tap “$button.”'; + } + @override String get messageListGroupYouWithYourself => 'Messages with yourself'; @@ -651,8 +719,12 @@ class ZulipLocalizationsEn extends ZulipLocalizations { String get inboxPageTitle => 'Inbox'; @override - String get inboxEmptyPlaceholder => - 'There are no unread messages in your inbox. Use the buttons below to view the combined feed or list of channels.'; + String get inboxEmptyPlaceholderHeader => + 'There are no unread messages in your inbox.'; + + @override + String get inboxEmptyPlaceholderMessage => + 'Use the buttons below to view the combined feed or list of channels.'; @override String get recentDmConversationsPageTitle => 'Direct messages'; @@ -661,8 +733,12 @@ class ZulipLocalizationsEn extends ZulipLocalizations { String get recentDmConversationsSectionHeader => 'Direct messages'; @override - String get recentDmConversationsEmptyPlaceholder => - 'You have no direct messages yet! Why not start the conversation?'; + String get recentDmConversationsEmptyPlaceholderHeader => + 'You have no direct messages yet!'; + + @override + String get recentDmConversationsEmptyPlaceholderMessage => + 'Why not start the conversation?'; @override String get combinedFeedPageTitle => 'Combined feed'; @@ -677,7 +753,7 @@ class ZulipLocalizationsEn extends ZulipLocalizations { String get channelsPageTitle => 'Channels'; @override - String get channelsEmptyPlaceholder => + String get channelsEmptyPlaceholderHeader => 'You are not subscribed to any channels yet.'; @override diff --git a/lib/generated/l10n/zulip_localizations_it.dart b/lib/generated/l10n/zulip_localizations_it.dart index 847cf68981..8451dc6746 100644 --- a/lib/generated/l10n/zulip_localizations_it.dart +++ b/lib/generated/l10n/zulip_localizations_it.dart @@ -34,6 +34,9 @@ class ZulipLocalizationsIt extends ZulipLocalizations { @override String get upgradeWelcomeDialogDismiss => 'Andiamo'; + @override + String get learnMoreButtonLabel => 'Learn more'; + @override String get chooseAccountPageTitle => 'Scegli account'; @@ -445,6 +448,71 @@ class ZulipLocalizationsIt extends ZulipLocalizations { return 'MD con $others'; } + @override + String get emptyMessageList => 'There are no messages here.'; + + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListPrivateChannelNotSubscribed => + 'This channel is private.'; + + @override + String get emptyMessageListChannelUnavailable => + 'This channel doesn’t exist, or you are not allowed to view it.'; + + @override + String get emptyMessageListSelfDmHeader => + 'You have not sent any direct messages to yourself yet!'; + + @override + String get emptyMessageListSelfDmMessage => + 'Use this space for personal notes, or to test out Zulip features.'; + + @override + String emptyMessageListDm(String person) { + return 'You have no direct messages with $person yet.'; + } + + @override + String emptyMessageListDmDeactivatedUser(String person) { + return 'You have no direct messages with $person.'; + } + + @override + String get emptyMessageListDmUnknownUser => + 'You have no direct messages with this person.'; + + @override + String get emptyMessageListGroupDm => + 'You have no direct messages with these users yet.'; + + @override + String get emptyMessageListGroupDmDeactivatedUser => + 'You have no direct messages with these users.'; + + @override + String get emptyMessageListDmStartConversation => + 'Why not start the conversation?'; + + @override + String get emptyMessageListMentionsHeader => + 'This view will show messages where you are mentioned.'; + + @override + String get emptyMessageListMentionsMessage => + 'To call attention to a message, you can mention a user, a group, topic participants, or all subscribers to a channel. Type @ in the compose box, and choose who you’d like to mention from the list of suggestions.'; + + @override + String get emptyMessageListStarredHeader => 'You have no starred messages.'; + + @override + String emptyMessageListStarredMessage(String button) { + return 'Starring messages is a good way to keep track of important messages, such as tasks you need to go back to, or useful references. To star a message, long-press it and tap “$button.”'; + } + @override String get messageListGroupYouWithYourself => 'Messaggi con te stesso'; @@ -666,8 +734,12 @@ class ZulipLocalizationsIt extends ZulipLocalizations { String get inboxPageTitle => 'Inbox'; @override - String get inboxEmptyPlaceholder => - 'Non ci sono messaggi non letti nella posta in arrivo. Usare i pulsanti sotto per visualizzare il feed combinato o l\'elenco dei canali.'; + String get inboxEmptyPlaceholderHeader => + 'There are no unread messages in your inbox.'; + + @override + String get inboxEmptyPlaceholderMessage => + 'Use the buttons below to view the combined feed or list of channels.'; @override String get recentDmConversationsPageTitle => 'Messaggi diretti'; @@ -676,8 +748,12 @@ class ZulipLocalizationsIt extends ZulipLocalizations { String get recentDmConversationsSectionHeader => 'Messaggi diretti'; @override - String get recentDmConversationsEmptyPlaceholder => - 'Non ci sono ancora messaggi diretti! Perché non iniziare la conversazione?'; + String get recentDmConversationsEmptyPlaceholderHeader => + 'You have no direct messages yet!'; + + @override + String get recentDmConversationsEmptyPlaceholderMessage => + 'Why not start the conversation?'; @override String get combinedFeedPageTitle => 'Feed combinato'; @@ -692,8 +768,8 @@ class ZulipLocalizationsIt extends ZulipLocalizations { String get channelsPageTitle => 'Canali'; @override - String get channelsEmptyPlaceholder => - 'Non sei ancora iscritto ad alcun canale.'; + String get channelsEmptyPlaceholderHeader => + 'You are not subscribed to any channels yet.'; @override String get mainMenuMyProfile => 'Il mio profilo'; diff --git a/lib/generated/l10n/zulip_localizations_ja.dart b/lib/generated/l10n/zulip_localizations_ja.dart index d7c84a08cb..f13057bd85 100644 --- a/lib/generated/l10n/zulip_localizations_ja.dart +++ b/lib/generated/l10n/zulip_localizations_ja.dart @@ -34,6 +34,9 @@ class ZulipLocalizationsJa extends ZulipLocalizations { @override String get upgradeWelcomeDialogDismiss => 'Let\'s go'; + @override + String get learnMoreButtonLabel => 'Learn more'; + @override String get chooseAccountPageTitle => 'アカウントを選択'; @@ -434,6 +437,71 @@ class ZulipLocalizationsJa extends ZulipLocalizations { return 'DMs with $others'; } + @override + String get emptyMessageList => 'There are no messages here.'; + + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListPrivateChannelNotSubscribed => + 'This channel is private.'; + + @override + String get emptyMessageListChannelUnavailable => + 'This channel doesn’t exist, or you are not allowed to view it.'; + + @override + String get emptyMessageListSelfDmHeader => + 'You have not sent any direct messages to yourself yet!'; + + @override + String get emptyMessageListSelfDmMessage => + 'Use this space for personal notes, or to test out Zulip features.'; + + @override + String emptyMessageListDm(String person) { + return 'You have no direct messages with $person yet.'; + } + + @override + String emptyMessageListDmDeactivatedUser(String person) { + return 'You have no direct messages with $person.'; + } + + @override + String get emptyMessageListDmUnknownUser => + 'You have no direct messages with this person.'; + + @override + String get emptyMessageListGroupDm => + 'You have no direct messages with these users yet.'; + + @override + String get emptyMessageListGroupDmDeactivatedUser => + 'You have no direct messages with these users.'; + + @override + String get emptyMessageListDmStartConversation => + 'Why not start the conversation?'; + + @override + String get emptyMessageListMentionsHeader => + 'This view will show messages where you are mentioned.'; + + @override + String get emptyMessageListMentionsMessage => + 'To call attention to a message, you can mention a user, a group, topic participants, or all subscribers to a channel. Type @ in the compose box, and choose who you’d like to mention from the list of suggestions.'; + + @override + String get emptyMessageListStarredHeader => 'You have no starred messages.'; + + @override + String emptyMessageListStarredMessage(String button) { + return 'Starring messages is a good way to keep track of important messages, such as tasks you need to go back to, or useful references. To star a message, long-press it and tap “$button.”'; + } + @override String get messageListGroupYouWithYourself => 'Messages with yourself'; @@ -651,8 +719,12 @@ class ZulipLocalizationsJa extends ZulipLocalizations { String get inboxPageTitle => 'Inbox'; @override - String get inboxEmptyPlaceholder => - 'There are no unread messages in your inbox. Use the buttons below to view the combined feed or list of channels.'; + String get inboxEmptyPlaceholderHeader => + 'There are no unread messages in your inbox.'; + + @override + String get inboxEmptyPlaceholderMessage => + 'Use the buttons below to view the combined feed or list of channels.'; @override String get recentDmConversationsPageTitle => 'Direct messages'; @@ -661,8 +733,12 @@ class ZulipLocalizationsJa extends ZulipLocalizations { String get recentDmConversationsSectionHeader => 'Direct messages'; @override - String get recentDmConversationsEmptyPlaceholder => - 'You have no direct messages yet! Why not start the conversation?'; + String get recentDmConversationsEmptyPlaceholderHeader => + 'You have no direct messages yet!'; + + @override + String get recentDmConversationsEmptyPlaceholderMessage => + 'Why not start the conversation?'; @override String get combinedFeedPageTitle => 'Combined feed'; @@ -677,7 +753,7 @@ class ZulipLocalizationsJa extends ZulipLocalizations { String get channelsPageTitle => 'Channels'; @override - String get channelsEmptyPlaceholder => + String get channelsEmptyPlaceholderHeader => 'You are not subscribed to any channels yet.'; @override diff --git a/lib/generated/l10n/zulip_localizations_nb.dart b/lib/generated/l10n/zulip_localizations_nb.dart index 98bad7d7b8..f1b677134a 100644 --- a/lib/generated/l10n/zulip_localizations_nb.dart +++ b/lib/generated/l10n/zulip_localizations_nb.dart @@ -34,6 +34,9 @@ class ZulipLocalizationsNb extends ZulipLocalizations { @override String get upgradeWelcomeDialogDismiss => 'Let\'s go'; + @override + String get learnMoreButtonLabel => 'Learn more'; + @override String get chooseAccountPageTitle => 'Choose account'; @@ -434,6 +437,71 @@ class ZulipLocalizationsNb extends ZulipLocalizations { return 'DMs with $others'; } + @override + String get emptyMessageList => 'There are no messages here.'; + + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListPrivateChannelNotSubscribed => + 'This channel is private.'; + + @override + String get emptyMessageListChannelUnavailable => + 'This channel doesn’t exist, or you are not allowed to view it.'; + + @override + String get emptyMessageListSelfDmHeader => + 'You have not sent any direct messages to yourself yet!'; + + @override + String get emptyMessageListSelfDmMessage => + 'Use this space for personal notes, or to test out Zulip features.'; + + @override + String emptyMessageListDm(String person) { + return 'You have no direct messages with $person yet.'; + } + + @override + String emptyMessageListDmDeactivatedUser(String person) { + return 'You have no direct messages with $person.'; + } + + @override + String get emptyMessageListDmUnknownUser => + 'You have no direct messages with this person.'; + + @override + String get emptyMessageListGroupDm => + 'You have no direct messages with these users yet.'; + + @override + String get emptyMessageListGroupDmDeactivatedUser => + 'You have no direct messages with these users.'; + + @override + String get emptyMessageListDmStartConversation => + 'Why not start the conversation?'; + + @override + String get emptyMessageListMentionsHeader => + 'This view will show messages where you are mentioned.'; + + @override + String get emptyMessageListMentionsMessage => + 'To call attention to a message, you can mention a user, a group, topic participants, or all subscribers to a channel. Type @ in the compose box, and choose who you’d like to mention from the list of suggestions.'; + + @override + String get emptyMessageListStarredHeader => 'You have no starred messages.'; + + @override + String emptyMessageListStarredMessage(String button) { + return 'Starring messages is a good way to keep track of important messages, such as tasks you need to go back to, or useful references. To star a message, long-press it and tap “$button.”'; + } + @override String get messageListGroupYouWithYourself => 'Messages with yourself'; @@ -651,8 +719,12 @@ class ZulipLocalizationsNb extends ZulipLocalizations { String get inboxPageTitle => 'Inbox'; @override - String get inboxEmptyPlaceholder => - 'There are no unread messages in your inbox. Use the buttons below to view the combined feed or list of channels.'; + String get inboxEmptyPlaceholderHeader => + 'There are no unread messages in your inbox.'; + + @override + String get inboxEmptyPlaceholderMessage => + 'Use the buttons below to view the combined feed or list of channels.'; @override String get recentDmConversationsPageTitle => 'Direct messages'; @@ -661,8 +733,12 @@ class ZulipLocalizationsNb extends ZulipLocalizations { String get recentDmConversationsSectionHeader => 'Direct messages'; @override - String get recentDmConversationsEmptyPlaceholder => - 'You have no direct messages yet! Why not start the conversation?'; + String get recentDmConversationsEmptyPlaceholderHeader => + 'You have no direct messages yet!'; + + @override + String get recentDmConversationsEmptyPlaceholderMessage => + 'Why not start the conversation?'; @override String get combinedFeedPageTitle => 'Combined feed'; @@ -677,7 +753,7 @@ class ZulipLocalizationsNb extends ZulipLocalizations { String get channelsPageTitle => 'Channels'; @override - String get channelsEmptyPlaceholder => + String get channelsEmptyPlaceholderHeader => 'You are not subscribed to any channels yet.'; @override diff --git a/lib/generated/l10n/zulip_localizations_pl.dart b/lib/generated/l10n/zulip_localizations_pl.dart index 1a9bd161e0..ca6d8c8ccf 100644 --- a/lib/generated/l10n/zulip_localizations_pl.dart +++ b/lib/generated/l10n/zulip_localizations_pl.dart @@ -34,6 +34,9 @@ class ZulipLocalizationsPl extends ZulipLocalizations { @override String get upgradeWelcomeDialogDismiss => 'Zaczynajmy'; + @override + String get learnMoreButtonLabel => 'Learn more'; + @override String get chooseAccountPageTitle => 'Wybierz konto'; @@ -443,6 +446,71 @@ class ZulipLocalizationsPl extends ZulipLocalizations { return 'DM z $others'; } + @override + String get emptyMessageList => 'There are no messages here.'; + + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListPrivateChannelNotSubscribed => + 'This channel is private.'; + + @override + String get emptyMessageListChannelUnavailable => + 'This channel doesn’t exist, or you are not allowed to view it.'; + + @override + String get emptyMessageListSelfDmHeader => + 'You have not sent any direct messages to yourself yet!'; + + @override + String get emptyMessageListSelfDmMessage => + 'Use this space for personal notes, or to test out Zulip features.'; + + @override + String emptyMessageListDm(String person) { + return 'You have no direct messages with $person yet.'; + } + + @override + String emptyMessageListDmDeactivatedUser(String person) { + return 'You have no direct messages with $person.'; + } + + @override + String get emptyMessageListDmUnknownUser => + 'You have no direct messages with this person.'; + + @override + String get emptyMessageListGroupDm => + 'You have no direct messages with these users yet.'; + + @override + String get emptyMessageListGroupDmDeactivatedUser => + 'You have no direct messages with these users.'; + + @override + String get emptyMessageListDmStartConversation => + 'Why not start the conversation?'; + + @override + String get emptyMessageListMentionsHeader => + 'This view will show messages where you are mentioned.'; + + @override + String get emptyMessageListMentionsMessage => + 'To call attention to a message, you can mention a user, a group, topic participants, or all subscribers to a channel. Type @ in the compose box, and choose who you’d like to mention from the list of suggestions.'; + + @override + String get emptyMessageListStarredHeader => 'You have no starred messages.'; + + @override + String emptyMessageListStarredMessage(String button) { + return 'Starring messages is a good way to keep track of important messages, such as tasks you need to go back to, or useful references. To star a message, long-press it and tap “$button.”'; + } + @override String get messageListGroupYouWithYourself => 'Zapiski na własne konto'; @@ -662,8 +730,12 @@ class ZulipLocalizationsPl extends ZulipLocalizations { String get inboxPageTitle => 'Odebrane'; @override - String get inboxEmptyPlaceholder => - 'Obecnie brak nowych wiadomości. Skorzystaj z przycisków u dołu ekranu aby przejść do widoku mieszanego lub listy kanałów.'; + String get inboxEmptyPlaceholderHeader => + 'There are no unread messages in your inbox.'; + + @override + String get inboxEmptyPlaceholderMessage => + 'Use the buttons below to view the combined feed or list of channels.'; @override String get recentDmConversationsPageTitle => 'Wiadomości bezpośrednie'; @@ -672,8 +744,12 @@ class ZulipLocalizationsPl extends ZulipLocalizations { String get recentDmConversationsSectionHeader => 'Wiadomości bezpośrednie'; @override - String get recentDmConversationsEmptyPlaceholder => - 'Brak wiadomości w archiwum! Może warto rozpocząć dyskusję?'; + String get recentDmConversationsEmptyPlaceholderHeader => + 'You have no direct messages yet!'; + + @override + String get recentDmConversationsEmptyPlaceholderMessage => + 'Why not start the conversation?'; @override String get combinedFeedPageTitle => 'Mieszany widok'; @@ -688,7 +764,8 @@ class ZulipLocalizationsPl extends ZulipLocalizations { String get channelsPageTitle => 'Kanały'; @override - String get channelsEmptyPlaceholder => 'Nie śledzisz żadnego z kanałów.'; + String get channelsEmptyPlaceholderHeader => + 'You are not subscribed to any channels yet.'; @override String get mainMenuMyProfile => 'Mój profil'; diff --git a/lib/generated/l10n/zulip_localizations_ru.dart b/lib/generated/l10n/zulip_localizations_ru.dart index fced1a4980..993fa279d7 100644 --- a/lib/generated/l10n/zulip_localizations_ru.dart +++ b/lib/generated/l10n/zulip_localizations_ru.dart @@ -34,6 +34,9 @@ class ZulipLocalizationsRu extends ZulipLocalizations { @override String get upgradeWelcomeDialogDismiss => 'Приступим!'; + @override + String get learnMoreButtonLabel => 'Learn more'; + @override String get chooseAccountPageTitle => 'Выберите учетную запись'; @@ -443,6 +446,71 @@ class ZulipLocalizationsRu extends ZulipLocalizations { return 'ЛС с $others'; } + @override + String get emptyMessageList => 'There are no messages here.'; + + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListPrivateChannelNotSubscribed => + 'This channel is private.'; + + @override + String get emptyMessageListChannelUnavailable => + 'This channel doesn’t exist, or you are not allowed to view it.'; + + @override + String get emptyMessageListSelfDmHeader => + 'You have not sent any direct messages to yourself yet!'; + + @override + String get emptyMessageListSelfDmMessage => + 'Use this space for personal notes, or to test out Zulip features.'; + + @override + String emptyMessageListDm(String person) { + return 'You have no direct messages with $person yet.'; + } + + @override + String emptyMessageListDmDeactivatedUser(String person) { + return 'You have no direct messages with $person.'; + } + + @override + String get emptyMessageListDmUnknownUser => + 'You have no direct messages with this person.'; + + @override + String get emptyMessageListGroupDm => + 'You have no direct messages with these users yet.'; + + @override + String get emptyMessageListGroupDmDeactivatedUser => + 'You have no direct messages with these users.'; + + @override + String get emptyMessageListDmStartConversation => + 'Why not start the conversation?'; + + @override + String get emptyMessageListMentionsHeader => + 'This view will show messages where you are mentioned.'; + + @override + String get emptyMessageListMentionsMessage => + 'To call attention to a message, you can mention a user, a group, topic participants, or all subscribers to a channel. Type @ in the compose box, and choose who you’d like to mention from the list of suggestions.'; + + @override + String get emptyMessageListStarredHeader => 'You have no starred messages.'; + + @override + String emptyMessageListStarredMessage(String button) { + return 'Starring messages is a good way to keep track of important messages, such as tasks you need to go back to, or useful references. To star a message, long-press it and tap “$button.”'; + } + @override String get messageListGroupYouWithYourself => 'Сообщения с собой'; @@ -665,8 +733,12 @@ class ZulipLocalizationsRu extends ZulipLocalizations { String get inboxPageTitle => 'Входящие'; @override - String get inboxEmptyPlaceholder => - 'Нет непрочитанных входящих сообщений. Используйте кнопки ниже для просмотра объединенной ленты или списка каналов.'; + String get inboxEmptyPlaceholderHeader => + 'There are no unread messages in your inbox.'; + + @override + String get inboxEmptyPlaceholderMessage => + 'Use the buttons below to view the combined feed or list of channels.'; @override String get recentDmConversationsPageTitle => 'Личные сообщения'; @@ -675,8 +747,12 @@ class ZulipLocalizationsRu extends ZulipLocalizations { String get recentDmConversationsSectionHeader => 'Личные сообщения'; @override - String get recentDmConversationsEmptyPlaceholder => - 'У вас пока нет личных сообщений! Почему бы не начать беседу?'; + String get recentDmConversationsEmptyPlaceholderHeader => + 'You have no direct messages yet!'; + + @override + String get recentDmConversationsEmptyPlaceholderMessage => + 'Why not start the conversation?'; @override String get combinedFeedPageTitle => 'Объединенная лента'; @@ -691,8 +767,8 @@ class ZulipLocalizationsRu extends ZulipLocalizations { String get channelsPageTitle => 'Каналы'; @override - String get channelsEmptyPlaceholder => - 'Вы еще не подписаны ни на один канал.'; + String get channelsEmptyPlaceholderHeader => + 'You are not subscribed to any channels yet.'; @override String get mainMenuMyProfile => 'Мой профиль'; diff --git a/lib/generated/l10n/zulip_localizations_sk.dart b/lib/generated/l10n/zulip_localizations_sk.dart index 0742cfb143..e47ebb86a3 100644 --- a/lib/generated/l10n/zulip_localizations_sk.dart +++ b/lib/generated/l10n/zulip_localizations_sk.dart @@ -34,6 +34,9 @@ class ZulipLocalizationsSk extends ZulipLocalizations { @override String get upgradeWelcomeDialogDismiss => 'Let\'s go'; + @override + String get learnMoreButtonLabel => 'Learn more'; + @override String get chooseAccountPageTitle => 'Zvoliť účet'; @@ -434,6 +437,71 @@ class ZulipLocalizationsSk extends ZulipLocalizations { return 'DMs with $others'; } + @override + String get emptyMessageList => 'There are no messages here.'; + + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListPrivateChannelNotSubscribed => + 'This channel is private.'; + + @override + String get emptyMessageListChannelUnavailable => + 'This channel doesn’t exist, or you are not allowed to view it.'; + + @override + String get emptyMessageListSelfDmHeader => + 'You have not sent any direct messages to yourself yet!'; + + @override + String get emptyMessageListSelfDmMessage => + 'Use this space for personal notes, or to test out Zulip features.'; + + @override + String emptyMessageListDm(String person) { + return 'You have no direct messages with $person yet.'; + } + + @override + String emptyMessageListDmDeactivatedUser(String person) { + return 'You have no direct messages with $person.'; + } + + @override + String get emptyMessageListDmUnknownUser => + 'You have no direct messages with this person.'; + + @override + String get emptyMessageListGroupDm => + 'You have no direct messages with these users yet.'; + + @override + String get emptyMessageListGroupDmDeactivatedUser => + 'You have no direct messages with these users.'; + + @override + String get emptyMessageListDmStartConversation => + 'Why not start the conversation?'; + + @override + String get emptyMessageListMentionsHeader => + 'This view will show messages where you are mentioned.'; + + @override + String get emptyMessageListMentionsMessage => + 'To call attention to a message, you can mention a user, a group, topic participants, or all subscribers to a channel. Type @ in the compose box, and choose who you’d like to mention from the list of suggestions.'; + + @override + String get emptyMessageListStarredHeader => 'You have no starred messages.'; + + @override + String emptyMessageListStarredMessage(String button) { + return 'Starring messages is a good way to keep track of important messages, such as tasks you need to go back to, or useful references. To star a message, long-press it and tap “$button.”'; + } + @override String get messageListGroupYouWithYourself => 'Messages with yourself'; @@ -653,8 +721,12 @@ class ZulipLocalizationsSk extends ZulipLocalizations { String get inboxPageTitle => 'Inbox'; @override - String get inboxEmptyPlaceholder => - 'There are no unread messages in your inbox. Use the buttons below to view the combined feed or list of channels.'; + String get inboxEmptyPlaceholderHeader => + 'There are no unread messages in your inbox.'; + + @override + String get inboxEmptyPlaceholderMessage => + 'Use the buttons below to view the combined feed or list of channels.'; @override String get recentDmConversationsPageTitle => 'Priama správa'; @@ -663,8 +735,12 @@ class ZulipLocalizationsSk extends ZulipLocalizations { String get recentDmConversationsSectionHeader => 'Direct messages'; @override - String get recentDmConversationsEmptyPlaceholder => - 'You have no direct messages yet! Why not start the conversation?'; + String get recentDmConversationsEmptyPlaceholderHeader => + 'You have no direct messages yet!'; + + @override + String get recentDmConversationsEmptyPlaceholderMessage => + 'Why not start the conversation?'; @override String get combinedFeedPageTitle => 'Zlúčený kanál'; @@ -679,7 +755,7 @@ class ZulipLocalizationsSk extends ZulipLocalizations { String get channelsPageTitle => 'Kanály'; @override - String get channelsEmptyPlaceholder => + String get channelsEmptyPlaceholderHeader => 'You are not subscribed to any channels yet.'; @override diff --git a/lib/generated/l10n/zulip_localizations_sl.dart b/lib/generated/l10n/zulip_localizations_sl.dart index 885a18c31a..bdcef1b752 100644 --- a/lib/generated/l10n/zulip_localizations_sl.dart +++ b/lib/generated/l10n/zulip_localizations_sl.dart @@ -33,6 +33,9 @@ class ZulipLocalizationsSl extends ZulipLocalizations { @override String get upgradeWelcomeDialogDismiss => 'Začnimo'; + @override + String get learnMoreButtonLabel => 'Learn more'; + @override String get chooseAccountPageTitle => 'Izberite račun'; @@ -455,6 +458,71 @@ class ZulipLocalizationsSl extends ZulipLocalizations { return 'Neposredna sporočila z $others'; } + @override + String get emptyMessageList => 'There are no messages here.'; + + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListPrivateChannelNotSubscribed => + 'This channel is private.'; + + @override + String get emptyMessageListChannelUnavailable => + 'This channel doesn’t exist, or you are not allowed to view it.'; + + @override + String get emptyMessageListSelfDmHeader => + 'You have not sent any direct messages to yourself yet!'; + + @override + String get emptyMessageListSelfDmMessage => + 'Use this space for personal notes, or to test out Zulip features.'; + + @override + String emptyMessageListDm(String person) { + return 'You have no direct messages with $person yet.'; + } + + @override + String emptyMessageListDmDeactivatedUser(String person) { + return 'You have no direct messages with $person.'; + } + + @override + String get emptyMessageListDmUnknownUser => + 'You have no direct messages with this person.'; + + @override + String get emptyMessageListGroupDm => + 'You have no direct messages with these users yet.'; + + @override + String get emptyMessageListGroupDmDeactivatedUser => + 'You have no direct messages with these users.'; + + @override + String get emptyMessageListDmStartConversation => + 'Why not start the conversation?'; + + @override + String get emptyMessageListMentionsHeader => + 'This view will show messages where you are mentioned.'; + + @override + String get emptyMessageListMentionsMessage => + 'To call attention to a message, you can mention a user, a group, topic participants, or all subscribers to a channel. Type @ in the compose box, and choose who you’d like to mention from the list of suggestions.'; + + @override + String get emptyMessageListStarredHeader => 'You have no starred messages.'; + + @override + String emptyMessageListStarredMessage(String button) { + return 'Starring messages is a good way to keep track of important messages, such as tasks you need to go back to, or useful references. To star a message, long-press it and tap “$button.”'; + } + @override String get messageListGroupYouWithYourself => 'Sporočila sebi'; @@ -677,8 +745,12 @@ class ZulipLocalizationsSl extends ZulipLocalizations { String get inboxPageTitle => 'Nabiralnik'; @override - String get inboxEmptyPlaceholder => - 'V vašem nabiralniku ni neprebranih sporočil. Uporabite spodnje gumbe za ogled združenega prikaza ali seznama kanalov.'; + String get inboxEmptyPlaceholderHeader => + 'There are no unread messages in your inbox.'; + + @override + String get inboxEmptyPlaceholderMessage => + 'Use the buttons below to view the combined feed or list of channels.'; @override String get recentDmConversationsPageTitle => 'Neposredna sporočila'; @@ -687,8 +759,12 @@ class ZulipLocalizationsSl extends ZulipLocalizations { String get recentDmConversationsSectionHeader => 'Neposredna sporočila'; @override - String get recentDmConversationsEmptyPlaceholder => - 'Zaenkrat še nimate neposrednih sporočil! Zakaj ne bi začeli pogovora?'; + String get recentDmConversationsEmptyPlaceholderHeader => + 'You have no direct messages yet!'; + + @override + String get recentDmConversationsEmptyPlaceholderMessage => + 'Why not start the conversation?'; @override String get combinedFeedPageTitle => 'Združen prikaz'; @@ -703,7 +779,8 @@ class ZulipLocalizationsSl extends ZulipLocalizations { String get channelsPageTitle => 'Kanali'; @override - String get channelsEmptyPlaceholder => 'Niste še naročeni na noben kanal.'; + String get channelsEmptyPlaceholderHeader => + 'You are not subscribed to any channels yet.'; @override String get mainMenuMyProfile => 'Moj profil'; diff --git a/lib/generated/l10n/zulip_localizations_uk.dart b/lib/generated/l10n/zulip_localizations_uk.dart index 92bd6b9185..af7df4c7e0 100644 --- a/lib/generated/l10n/zulip_localizations_uk.dart +++ b/lib/generated/l10n/zulip_localizations_uk.dart @@ -34,6 +34,9 @@ class ZulipLocalizationsUk extends ZulipLocalizations { @override String get upgradeWelcomeDialogDismiss => 'Ходімо!'; + @override + String get learnMoreButtonLabel => 'Learn more'; + @override String get chooseAccountPageTitle => 'Обрати обліковий запис'; @@ -445,6 +448,71 @@ class ZulipLocalizationsUk extends ZulipLocalizations { return 'Особисті повідомлення з $others'; } + @override + String get emptyMessageList => 'There are no messages here.'; + + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListPrivateChannelNotSubscribed => + 'This channel is private.'; + + @override + String get emptyMessageListChannelUnavailable => + 'This channel doesn’t exist, or you are not allowed to view it.'; + + @override + String get emptyMessageListSelfDmHeader => + 'You have not sent any direct messages to yourself yet!'; + + @override + String get emptyMessageListSelfDmMessage => + 'Use this space for personal notes, or to test out Zulip features.'; + + @override + String emptyMessageListDm(String person) { + return 'You have no direct messages with $person yet.'; + } + + @override + String emptyMessageListDmDeactivatedUser(String person) { + return 'You have no direct messages with $person.'; + } + + @override + String get emptyMessageListDmUnknownUser => + 'You have no direct messages with this person.'; + + @override + String get emptyMessageListGroupDm => + 'You have no direct messages with these users yet.'; + + @override + String get emptyMessageListGroupDmDeactivatedUser => + 'You have no direct messages with these users.'; + + @override + String get emptyMessageListDmStartConversation => + 'Why not start the conversation?'; + + @override + String get emptyMessageListMentionsHeader => + 'This view will show messages where you are mentioned.'; + + @override + String get emptyMessageListMentionsMessage => + 'To call attention to a message, you can mention a user, a group, topic participants, or all subscribers to a channel. Type @ in the compose box, and choose who you’d like to mention from the list of suggestions.'; + + @override + String get emptyMessageListStarredHeader => 'You have no starred messages.'; + + @override + String emptyMessageListStarredMessage(String button) { + return 'Starring messages is a good way to keep track of important messages, such as tasks you need to go back to, or useful references. To star a message, long-press it and tap “$button.”'; + } + @override String get messageListGroupYouWithYourself => 'Повідомлення з собою'; @@ -665,8 +733,12 @@ class ZulipLocalizationsUk extends ZulipLocalizations { String get inboxPageTitle => 'Вхідні'; @override - String get inboxEmptyPlaceholder => - 'Немає непрочитаних вхідних повідомлень. Використовуйте кнопки знизу для перегляду обʼєднаної стрічки або списку каналів.'; + String get inboxEmptyPlaceholderHeader => + 'There are no unread messages in your inbox.'; + + @override + String get inboxEmptyPlaceholderMessage => + 'Use the buttons below to view the combined feed or list of channels.'; @override String get recentDmConversationsPageTitle => 'Особисті повідомлення'; @@ -675,8 +747,12 @@ class ZulipLocalizationsUk extends ZulipLocalizations { String get recentDmConversationsSectionHeader => 'Особисті повідомлення'; @override - String get recentDmConversationsEmptyPlaceholder => - 'У вас поки що немає особистих повідомлень! Чому б не розпочати бесіду?'; + String get recentDmConversationsEmptyPlaceholderHeader => + 'You have no direct messages yet!'; + + @override + String get recentDmConversationsEmptyPlaceholderMessage => + 'Why not start the conversation?'; @override String get combinedFeedPageTitle => 'Об\'єднана стрічка'; @@ -691,7 +767,8 @@ class ZulipLocalizationsUk extends ZulipLocalizations { String get channelsPageTitle => 'Канали'; @override - String get channelsEmptyPlaceholder => 'Ви ще не підписані на жодний канал.'; + String get channelsEmptyPlaceholderHeader => + 'You are not subscribed to any channels yet.'; @override String get mainMenuMyProfile => 'Мій профіль'; diff --git a/lib/generated/l10n/zulip_localizations_zh.dart b/lib/generated/l10n/zulip_localizations_zh.dart index 5befa99eea..74b65136d2 100644 --- a/lib/generated/l10n/zulip_localizations_zh.dart +++ b/lib/generated/l10n/zulip_localizations_zh.dart @@ -34,6 +34,9 @@ class ZulipLocalizationsZh extends ZulipLocalizations { @override String get upgradeWelcomeDialogDismiss => 'Let\'s go'; + @override + String get learnMoreButtonLabel => 'Learn more'; + @override String get chooseAccountPageTitle => 'Choose account'; @@ -434,6 +437,71 @@ class ZulipLocalizationsZh extends ZulipLocalizations { return 'DMs with $others'; } + @override + String get emptyMessageList => 'There are no messages here.'; + + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListPrivateChannelNotSubscribed => + 'This channel is private.'; + + @override + String get emptyMessageListChannelUnavailable => + 'This channel doesn’t exist, or you are not allowed to view it.'; + + @override + String get emptyMessageListSelfDmHeader => + 'You have not sent any direct messages to yourself yet!'; + + @override + String get emptyMessageListSelfDmMessage => + 'Use this space for personal notes, or to test out Zulip features.'; + + @override + String emptyMessageListDm(String person) { + return 'You have no direct messages with $person yet.'; + } + + @override + String emptyMessageListDmDeactivatedUser(String person) { + return 'You have no direct messages with $person.'; + } + + @override + String get emptyMessageListDmUnknownUser => + 'You have no direct messages with this person.'; + + @override + String get emptyMessageListGroupDm => + 'You have no direct messages with these users yet.'; + + @override + String get emptyMessageListGroupDmDeactivatedUser => + 'You have no direct messages with these users.'; + + @override + String get emptyMessageListDmStartConversation => + 'Why not start the conversation?'; + + @override + String get emptyMessageListMentionsHeader => + 'This view will show messages where you are mentioned.'; + + @override + String get emptyMessageListMentionsMessage => + 'To call attention to a message, you can mention a user, a group, topic participants, or all subscribers to a channel. Type @ in the compose box, and choose who you’d like to mention from the list of suggestions.'; + + @override + String get emptyMessageListStarredHeader => 'You have no starred messages.'; + + @override + String emptyMessageListStarredMessage(String button) { + return 'Starring messages is a good way to keep track of important messages, such as tasks you need to go back to, or useful references. To star a message, long-press it and tap “$button.”'; + } + @override String get messageListGroupYouWithYourself => 'Messages with yourself'; @@ -651,8 +719,12 @@ class ZulipLocalizationsZh extends ZulipLocalizations { String get inboxPageTitle => 'Inbox'; @override - String get inboxEmptyPlaceholder => - 'There are no unread messages in your inbox. Use the buttons below to view the combined feed or list of channels.'; + String get inboxEmptyPlaceholderHeader => + 'There are no unread messages in your inbox.'; + + @override + String get inboxEmptyPlaceholderMessage => + 'Use the buttons below to view the combined feed or list of channels.'; @override String get recentDmConversationsPageTitle => 'Direct messages'; @@ -661,8 +733,12 @@ class ZulipLocalizationsZh extends ZulipLocalizations { String get recentDmConversationsSectionHeader => 'Direct messages'; @override - String get recentDmConversationsEmptyPlaceholder => - 'You have no direct messages yet! Why not start the conversation?'; + String get recentDmConversationsEmptyPlaceholderHeader => + 'You have no direct messages yet!'; + + @override + String get recentDmConversationsEmptyPlaceholderMessage => + 'Why not start the conversation?'; @override String get combinedFeedPageTitle => 'Combined feed'; @@ -677,7 +753,7 @@ class ZulipLocalizationsZh extends ZulipLocalizations { String get channelsPageTitle => 'Channels'; @override - String get channelsEmptyPlaceholder => + String get channelsEmptyPlaceholderHeader => 'You are not subscribed to any channels yet.'; @override @@ -1491,18 +1567,12 @@ class ZulipLocalizationsZhHansCn extends ZulipLocalizationsZh { @override String get inboxPageTitle => '收件箱'; - @override - String get inboxEmptyPlaceholder => '您的收件箱中没有未读消息。您可以通过底部导航栏访问综合消息或者频道列表。'; - @override String get recentDmConversationsPageTitle => '私信'; @override String get recentDmConversationsSectionHeader => '私信'; - @override - String get recentDmConversationsEmptyPlaceholder => '您还没有任何私信消息!何不开启一个新对话?'; - @override String get combinedFeedPageTitle => '综合消息'; @@ -1515,9 +1585,6 @@ class ZulipLocalizationsZhHansCn extends ZulipLocalizationsZh { @override String get channelsPageTitle => '频道'; - @override - String get channelsEmptyPlaceholder => '您还没有订阅任何频道。'; - @override String get mainMenuMyProfile => '个人资料'; diff --git a/lib/widgets/compose_box.dart b/lib/widgets/compose_box.dart index a53d628a2c..cee8270c95 100644 --- a/lib/widgets/compose_box.dart +++ b/lib/widgets/compose_box.dart @@ -2064,8 +2064,12 @@ class _ComposeBoxState extends State with PerAccountStoreAwareStateM case ChannelNarrow(:final streamId): case TopicNarrow(:final streamId): final channel = store.streams[streamId]; - if (channel == null || !store.hasPostingPermission(inChannel: channel, - user: store.selfUser, byDate: DateTime.now())) { + if ( + channel == null + || (channel.inviteOnly && channel is! Subscription) + || !store.hasPostingPermission(inChannel: channel, + user: store.selfUser, byDate: DateTime.now()) + ) { return _ErrorBanner(getLabel: (zulipLocalizations) => zulipLocalizations.errorBannerCannotPostInChannelLabel); } diff --git a/lib/widgets/home.dart b/lib/widgets/home.dart index 9a0850e0b9..88d3bf5d2e 100644 --- a/lib/widgets/home.dart +++ b/lib/widgets/home.dart @@ -148,40 +148,6 @@ class _HomePageState extends State { } } -/// A "no content here" message, for the Inbox, Subscriptions, and DMs pages. -/// -/// This should go near the root of the "page body"'s widget subtree. -/// In particular, it handles the horizontal device insets. -/// (The vertical insets are handled externally, by the app bar and bottom nav.) -class PageBodyEmptyContentPlaceholder extends StatelessWidget { - const PageBodyEmptyContentPlaceholder({super.key, required this.message}); - - final String message; - - @override - Widget build(BuildContext context) { - final designVariables = DesignVariables.of(context); - - return SafeArea( - minimum: EdgeInsets.symmetric(horizontal: 24), - child: Padding( - padding: EdgeInsets.only(top: 48, bottom: 16), - child: Align( - alignment: Alignment.topCenter, - // TODO leading and trailing elements, like in Figma (given as SVGs): - // https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=5957-167736&m=dev - child: Text( - textAlign: TextAlign.center, - style: TextStyle( - color: designVariables.labelSearchPrompt, - fontSize: 17, - height: 23 / 17, - ).merge(weightVariableTextStyle(context, wght: 500)), - message)))); - } -} - - const kTryAnotherAccountWaitPeriod = Duration(seconds: 5); class _LoadingPlaceholderPage extends StatefulWidget { diff --git a/lib/widgets/inbox.dart b/lib/widgets/inbox.dart index cd1822bbac..9dde0de2ab 100644 --- a/lib/widgets/inbox.dart +++ b/lib/widgets/inbox.dart @@ -6,9 +6,9 @@ import '../model/narrow.dart'; import '../model/recent_dm_conversations.dart'; import '../model/unreads.dart'; import 'action_sheet.dart'; -import 'home.dart'; import 'icons.dart'; import 'message_list.dart'; +import 'page.dart'; import 'sticky_header.dart'; import 'store.dart'; import 'text.dart'; @@ -165,7 +165,8 @@ class _InboxPageState extends State with PerAccountStoreAwareStat if (sections.isEmpty) { return PageBodyEmptyContentPlaceholder( // TODO(#315) add e.g. "You might be interested in recent conversations." - message: zulipLocalizations.inboxEmptyPlaceholder); + header: zulipLocalizations.inboxEmptyPlaceholderHeader, + message: zulipLocalizations.inboxEmptyPlaceholderMessage); } return SafeArea( diff --git a/lib/widgets/message_list.dart b/lib/widgets/message_list.dart index 39ab1b4f04..0b94d6ea9c 100644 --- a/lib/widgets/message_list.dart +++ b/lib/widgets/message_list.dart @@ -790,6 +790,18 @@ class _MessageListState extends State with PerAccountStoreAwareStat Widget build(BuildContext context) { if (!model.fetched) return const Center(child: CircularProgressIndicator()); + if (model.haveNewest && model.haveOldest + && model.messages.isEmpty && model.outboxMessages.isEmpty) { + final (header, message, learnMoreButtonUrl) = + configureEmptyContentPlaceholder(context, narrow: widget.narrow); + + return PageBodyEmptyContentPlaceholder( + header: header, + message: message, + learnMoreButtonUrl: learnMoreButtonUrl, + ); + } + // Pad the left and right insets, for small devices in landscape. return SafeArea( // Don't let this be the place we pad the bottom inset. When there's @@ -998,6 +1010,75 @@ class _MessageListState extends State with PerAccountStoreAwareStat } } +(String header, String? message, Uri? learnMoreButton) configureEmptyContentPlaceholder( + BuildContext context, { + required Narrow narrow, +}) { + final store = PerAccountStoreWidget.of(context); + final zulipLocalizations = ZulipLocalizations.of(context); + + final String header; + String? message; + Uri? learnMoreButtonUrl; + switch (narrow) { + case CombinedFeedNarrow(): + header = zulipLocalizations.emptyMessageListCombinedFeed; + case ChannelNarrow(:final streamId) || TopicNarrow(:final streamId): + final channel = store.streams[streamId]; + if (channel == null) { + header = zulipLocalizations.emptyMessageListChannelUnavailable; + } else if (channel.inviteOnly && channel is! Subscription) { + header = zulipLocalizations.emptyMessageListPrivateChannelNotSubscribed; + learnMoreButtonUrl = store.tryResolveUrl('/help/channel-permissions#private-channels'); + } else { + header = zulipLocalizations.emptyMessageList; + } + case DmNarrow(:final otherRecipientIds) when otherRecipientIds.isEmpty: + header = zulipLocalizations.emptyMessageListSelfDmHeader; + message = zulipLocalizations.emptyMessageListSelfDmMessage; + case DmNarrow(:final otherRecipientIds) when otherRecipientIds.length == 1: + final user = store.getUser(otherRecipientIds.single); + switch (user) { + case null: + header = zulipLocalizations.emptyMessageListDmUnknownUser; + case User(isActive: false): + header = zulipLocalizations.emptyMessageListDmDeactivatedUser( + store.userDisplayName(user.userId)); + case User(): + header = zulipLocalizations.emptyMessageListDm(store.userDisplayName(user.userId)); + if (!store.isUserMuted(user.userId)) { + message = zulipLocalizations.emptyMessageListDmStartConversation; + } + } + case DmNarrow(:final otherRecipientIds) + when otherRecipientIds.any((userId) { + final user = store.getUser(userId); + return user != null && !user.isActive; + }): + header = zulipLocalizations.emptyMessageListGroupDmDeactivatedUser; + case DmNarrow(): + header = zulipLocalizations.emptyMessageListGroupDm; + message = zulipLocalizations.emptyMessageListDmStartConversation; + case MentionsNarrow(): + header = zulipLocalizations.emptyMessageListMentionsHeader; + if (store.zulipFeatureLevel >= 224) { // TODO(server-8) + // This string mentions @topic, which is new in Server 8. + + // TODO(#233) ...It also mentions user-group mentions, + // so we'll just comment it out until that's implemented. + // message = zulipLocalizations.emptyMessageListMentionsMessage; + } + // TODO(#233) as above, uncomment when user-group mentions are done + // learnMoreButtonUrl = store.tryResolveUrl('/help/mention-a-user-or-group'); + case StarredMessagesNarrow(): + header = zulipLocalizations.emptyMessageListStarredHeader; + message = zulipLocalizations.emptyMessageListStarredMessage(zulipLocalizations.actionSheetOptionStarMessage); + learnMoreButtonUrl = store.tryResolveUrl('/help/star-a-message'); + } + + return (header, message, learnMoreButtonUrl); +} + class _MessageListHistoryStart extends StatelessWidget { const _MessageListHistoryStart(); diff --git a/lib/widgets/page.dart b/lib/widgets/page.dart index a2c6fe52a1..08d27571b4 100644 --- a/lib/widgets/page.dart +++ b/lib/widgets/page.dart @@ -1,7 +1,11 @@ - import 'package:flutter/material.dart'; +import '../generated/l10n/zulip_localizations.dart'; +import '../model/binding.dart'; +import 'button.dart'; import 'store.dart'; +import 'text.dart'; +import 'theme.dart'; /// An [InheritedWidget] for near the root of a page's widget subtree, /// providing its [BuildContext]. @@ -210,3 +214,66 @@ class LoadingPlaceholderPage extends StatelessWidget { ); } } + +/// A "no content here" message for when a page has no content to show. +/// +/// Suitable for the inbox, the message-list page, etc. +/// +/// This handles the horizontal device insets +/// and the bottom inset when needed (in a message list with no compose box). +/// The top inset is handled externally by the app bar. +// TODO(#311) If the message list gets a bottom nav, the bottom inset will +// always be handled externally too; simplify implementation and dartdoc. +class PageBodyEmptyContentPlaceholder extends StatelessWidget { + const PageBodyEmptyContentPlaceholder({ + super.key, + required this.header, + this.message, + this.learnMoreButtonUrl, + }); + + final String header; + final String? message; + final Uri? learnMoreButtonUrl; + + @override + Widget build(BuildContext context) { + final zulipLocalizations = ZulipLocalizations.of(context); + final designVariables = DesignVariables.of(context); + + return SafeArea( + minimum: EdgeInsets.fromLTRB(24, 0, 24, 16), + child: Padding( + padding: EdgeInsets.only(top: 48), + child: Align( + alignment: Alignment.topCenter, + child: Column( + spacing: 16, + crossAxisAlignment: CrossAxisAlignment.center, + // TODO leading and trailing elements, like in Figma (given as SVGs): + // https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=5957-167736&m=dev + children: [ + Text( + textAlign: TextAlign.center, + style: TextStyle( + color: designVariables.labelSearchPrompt, + fontSize: 22, + height: 1.30, + ).merge(weightVariableTextStyle(context, wght: 600)), + header, + ), + if (message != null) Text( + textAlign: TextAlign.center, + style: TextStyle( + color: designVariables.labelSearchPrompt, + fontSize: 17, + height: 23 / 17, + ).merge(weightVariableTextStyle(context, wght: 500)), + message!), + if (learnMoreButtonUrl != null) + ZulipWebUiKitButton( + label: zulipLocalizations.learnMoreButtonLabel, + onPressed: () => ZulipBinding.instance.launchUrl(learnMoreButtonUrl!)), + ])))); + } +} diff --git a/lib/widgets/recent_dm_conversations.dart b/lib/widgets/recent_dm_conversations.dart index 28a0561f0d..3c427193bf 100644 --- a/lib/widgets/recent_dm_conversations.dart +++ b/lib/widgets/recent_dm_conversations.dart @@ -5,10 +5,10 @@ import '../model/narrow.dart'; import '../model/recent_dm_conversations.dart'; import '../model/unreads.dart'; import 'content.dart'; -import 'home.dart'; import 'icons.dart'; import 'message_list.dart'; import 'new_dm_sheet.dart'; +import 'page.dart'; import 'store.dart'; import 'text.dart'; import 'theme.dart'; @@ -61,7 +61,8 @@ class _RecentDmConversationsPageBodyState extends State wit if (pinned.isEmpty && unpinned.isEmpty) { return PageBodyEmptyContentPlaceholder( // TODO(#188) add e.g. "Go to 'All channels' and join some of them." - message: zulipLocalizations.channelsEmptyPlaceholder); + header: zulipLocalizations.channelsEmptyPlaceholderHeader); } return SafeArea( diff --git a/test/widgets/compose_box_test.dart b/test/widgets/compose_box_test.dart index 76305610c6..b777ffa6f1 100644 --- a/test/widgets/compose_box_test.dart +++ b/test/widgets/compose_box_test.dart @@ -58,6 +58,7 @@ void main() { User? selfUser, List otherUsers = const [], List streams = const [], + List subscriptions = const [], List? messages, bool? mandatoryTopics, int? zulipFeatureLevel, @@ -81,6 +82,7 @@ void main() { await testBinding.globalStore.add(selfAccount, eg.initialSnapshot( realmUsers: [selfUser, ...otherUsers], streams: streams, + subscriptions: subscriptions, zulipFeatureLevel: zulipFeatureLevel, realmMandatoryTopics: mandatoryTopics, realmAllowMessageEditing: true, @@ -1281,7 +1283,7 @@ void main() { }); }); - group('in channel/topic narrow according to channel post policy', () { + group('in channel/topic narrow according to channel post policy and privacy/subscribed', () { void checkComposeBox({required bool isShown}) => checkComposeBoxIsShown(isShown, bannerLabel: zulipLocalizations.errorBannerCannotPostInChannelLabel); @@ -1300,7 +1302,7 @@ void main() { checkComposeBox(isShown: true); }); - testWidgets('error banner is shown in $narrowType narrow', (tester) async { + testWidgets('error banner is shown in $narrowType narrow without posting permission', (tester) async { await prepareComposeBox(tester, narrow: narrow, selfUser: eg.user(role: UserRole.moderator), @@ -1308,6 +1310,17 @@ void main() { channelPostPolicy: ChannelPostPolicy.administrators)]); checkComposeBox(isShown: false); }); + + testWidgets('error banner is shown in $narrowType when private and unsubscribed', (tester) async { + await prepareComposeBox(tester, + narrow: narrow, + selfUser: eg.user(role: UserRole.moderator), + streams: [eg.stream(streamId: 1, + channelPostPolicy: ChannelPostPolicy.any, + inviteOnly: true)]); + check(store.subscriptions[1]).isNull(); + checkComposeBox(isShown: false); + }); } testWidgets('user loses privilege -> compose box is replaced with the banner', (tester) async { @@ -1375,6 +1388,80 @@ void main() { await tester.pump(); checkComposeBox(isShown: true); }); + + testWidgets('unsubscribed private channel becomes public -> banner is replaced with the compose box', (tester) async { + final selfUser = eg.user(role: UserRole.moderator); + final channel = eg.stream(streamId: 1, inviteOnly: true, + channelPostPolicy: ChannelPostPolicy.any); + + await prepareComposeBox(tester, + narrow: const ChannelNarrow(1), + selfUser: selfUser, + streams: [channel]); + check(store.subscriptions[1]).isNull(); + checkComposeBox(isShown: false); + + await store.handleEvent(eg.channelUpdateEvent(channel, + property: ChannelPropertyName.inviteOnly, + value: false)); + await tester.pump(); + checkComposeBox(isShown: true); + }); + + testWidgets('unsubscribed public channel becomes private -> compose box is replaced with the banner', (tester) async { + final selfUser = eg.user(role: UserRole.moderator); + final channel = eg.stream(streamId: 1, inviteOnly: false, + channelPostPolicy: ChannelPostPolicy.any); + + await prepareComposeBox(tester, + narrow: const ChannelNarrow(1), + selfUser: selfUser, + streams: [channel]); + check(store.subscriptions[1]).isNull(); + checkComposeBox(isShown: true); + + await store.handleEvent(eg.channelUpdateEvent(channel, + property: ChannelPropertyName.inviteOnly, + value: true)); + await tester.pump(); + checkComposeBox(isShown: false); + }); + + testWidgets('unsubscribed private channel becomes subscribed -> banner is replaced with the compose box', (tester) async { + final selfUser = eg.user(role: UserRole.moderator); + final channel = eg.stream(streamId: 1, inviteOnly: true, + channelPostPolicy: ChannelPostPolicy.any); + + await prepareComposeBox(tester, + narrow: const ChannelNarrow(1), + selfUser: selfUser, + streams: [channel]); + check(store.subscriptions[1]).isNull(); + checkComposeBox(isShown: false); + + await store.handleEvent(SubscriptionAddEvent(id: 1, + subscriptions: [eg.subscription(channel)])); + await tester.pump(); + checkComposeBox(isShown: true); + }); + + testWidgets('subscribed private channel becomes unsubscribed -> compose box is replaced with the banner', (tester) async { + final selfUser = eg.user(role: UserRole.moderator); + final channel = eg.stream(streamId: 1, inviteOnly: true, + channelPostPolicy: ChannelPostPolicy.any); + + await prepareComposeBox(tester, + narrow: const ChannelNarrow(1), + selfUser: selfUser, + streams: [channel], + subscriptions: [eg.subscription(channel)]); + checkComposeBox(isShown: true); + + await store.handleEvent(SubscriptionRemoveEvent(id: 1, + streamIds: [channel.streamId])); + await tester.pump(); + checkComposeBox(isShown: false); + }); }); }); diff --git a/test/widgets/message_list_test.dart b/test/widgets/message_list_test.dart index fd8dd6f10b..a8b8761251 100644 --- a/test/widgets/message_list_test.dart +++ b/test/widgets/message_list_test.dart @@ -23,6 +23,7 @@ import 'package:zulip/model/store.dart'; import 'package:zulip/model/typing_status.dart'; import 'package:zulip/widgets/app_bar.dart'; import 'package:zulip/widgets/autocomplete.dart'; +import 'package:zulip/widgets/button.dart'; import 'package:zulip/widgets/color.dart'; import 'package:zulip/widgets/compose_box.dart'; import 'package:zulip/widgets/content.dart'; @@ -125,6 +126,9 @@ void main() { return findScrollView(tester).controller; } + final contentInputFinder = find.byWidgetPredicate( + (widget) => widget is TextField && widget.controller is ComposeContentController); + group('MessageListPage', () { testWidgets('ancestorOf finds page state from message', (tester) async { await setupMessageListPage(tester, @@ -326,6 +330,208 @@ void main() { }); }); + group('no-messages placeholder', () { + final findPlaceholder = find.byType(PageBodyEmptyContentPlaceholder); + + Finder findTextInPlaceholder(String text) => + find.descendant(of: findPlaceholder, matching: find.textContaining(text)); + + Future checkLearnMoreButton(WidgetTester tester, Uri expectedUrl) async { + await tester.tap(find.widgetWithText(ZulipWebUiKitButton, 'Learn more')); + final (url: url, mode: _) = testBinding.takeLaunchUrlCalls().single; + check(url).equals(expectedUrl); + } + + testWidgets('Combined feed', (tester) async { + await setupMessageListPage(tester, narrow: CombinedFeedNarrow(), messages: []); + check(findPlaceholder).findsOne(); + check(findTextInPlaceholder('combined feed')).findsOne(); + }); + + testWidgets('Known, subscribed channel', (tester) async { + final channel = eg.stream(); + await setupMessageListPage(tester, + narrow: ChannelNarrow(channel.streamId), messages: [], streams: [channel], + skipPumpAndSettle: true); + + // The topic input is autofocused, triggering topic autocomplete. + connection.prepare(json: GetStreamTopicsResult(topics: []).toJson()); + await tester.pumpAndSettle(); + + check(findPlaceholder).findsOne(); + check(findTextInPlaceholder('no messages')).findsOne(); + }); + + testWidgets('Private, unsubscribed channel', (tester) async { + final channel = eg.stream(inviteOnly: true); + await setupMessageListPage(tester, + narrow: ChannelNarrow(channel.streamId), messages: [], streams: [channel]); + check(findPlaceholder).findsOne(); + check(findTextInPlaceholder('This channel is private.')).findsOne(); + await checkLearnMoreButton(tester, + store.tryResolveUrl('/help/channel-permissions#private-channels')!); + }); + + testWidgets('Unknown channel', (tester) async { + final channel = eg.stream(); + await setupMessageListPage(tester, + narrow: ChannelNarrow(channel.streamId), messages: [], streams: []); + check(findPlaceholder).findsOne(); + check(findTextInPlaceholder('This channel doesn’t exist, or you are not allowed to view it.')).findsOne(); + }); + + testWidgets('Topic in unknown channel', (tester) async { + final channel = eg.stream(); + await setupMessageListPage(tester, + narrow: TopicNarrow(channel.streamId, eg.t('topic')), messages: [], streams: []); + check(findPlaceholder).findsOne(); + check(findTextInPlaceholder('This channel doesn’t exist, or you are not allowed to view it.')).findsOne(); + }); + + testWidgets('Topic in known, subscribed channel', (tester) async { + final channel = eg.stream(); + await setupMessageListPage(tester, + narrow: TopicNarrow(channel.streamId, eg.t('topic')), messages: [], streams: [channel]); + check(findPlaceholder).findsOne(); + check(findTextInPlaceholder('no messages')).findsOne(); + }); + + testWidgets('Topic in private, unsubscribed channel', (tester) async { + final channel = eg.stream(inviteOnly: true); + await setupMessageListPage(tester, + narrow: TopicNarrow(channel.streamId, eg.t('topic')), messages: [], streams: [channel]); + check(findPlaceholder).findsOne(); + check(findTextInPlaceholder('This channel is private.')).findsOne(); + await checkLearnMoreButton(tester, + store.tryResolveUrl('/help/channel-permissions#private-channels')!); + }); + + testWidgets('Self-DM', (tester) async { + final selfUserId = eg.selfUser.userId; + await setupMessageListPage(tester, + narrow: DmNarrow.withUser(selfUserId, selfUserId: selfUserId), messages: [], users: [eg.selfUser]); + check(findPlaceholder).findsOne(); + check(findTextInPlaceholder('yourself')).findsOne(); + check(findTextInPlaceholder('Use this space')).findsOne(); + }); + + testWidgets('1:1 DM', (tester) async { + final selfUserId = eg.selfUser.userId; + final user = eg.user(); + await setupMessageListPage(tester, + narrow: DmNarrow.withUser(user.userId, selfUserId: selfUserId), messages: [], users: [eg.selfUser, user]); + check(findPlaceholder).findsOne(); + check(findTextInPlaceholder(user.fullName)).findsOne(); + check(findTextInPlaceholder('yet.')).findsOne(); + check(findTextInPlaceholder('Why not start the conversation?')).findsOne(); + }); + + testWidgets('1:1 DM, muted user', (tester) async { + final selfUserId = eg.selfUser.userId; + final user = eg.user(); + await setupMessageListPage(tester, + narrow: DmNarrow.withUser(user.userId, selfUserId: selfUserId), messages: [], users: [eg.selfUser, user]); + await store.handleEvent(MutedUsersEvent(id: 1, mutedUsers: [MutedUserItem(id: user.userId)])); + await tester.pump(); + check(store.isUserMuted(user.userId)).isTrue(); + check(findPlaceholder).findsOne(); + + // Probably want to show their name, not "Muted user"; + // this UI context is very much focused on the one user. + check(findTextInPlaceholder(user.fullName)).findsOne(); + check(findTextInPlaceholder('Muted user')).findsNothing(); + + // No need to encourage starting a conversation though. + check(findTextInPlaceholder('Why not start the conversation?')).findsNothing(); + }); + + testWidgets('1:1 DM, unknown user', (tester) async { + final selfUserId = eg.selfUser.userId; + await setupMessageListPage(tester, + narrow: DmNarrow.withUser(eg.user().userId, selfUserId: selfUserId), messages: [], users: [eg.selfUser]); + check(findPlaceholder).findsOne(); + check(findTextInPlaceholder('this person')).findsOne(); + check(findTextInPlaceholder('(unknown user)')).findsNothing(); + + // No need to encourage starting a conversation...right? + check(findTextInPlaceholder('yet.')).findsNothing(); + check(findTextInPlaceholder('Why not start the conversation?')).findsNothing(); + }); + + testWidgets('1:1 DM, deactivated user', (tester) async { + final selfUserId = eg.selfUser.userId; + final user = eg.user(isActive: false); + await setupMessageListPage(tester, + narrow: DmNarrow.withUser(user.userId, selfUserId: selfUserId), messages: [], users: [eg.selfUser, user]); + check(findPlaceholder).findsOne(); + check(findTextInPlaceholder(user.fullName)).findsOne(); + + // Sending messages isn't allowed; don't suggest that + check(findTextInPlaceholder('yet.')).findsNothing(); + check(findTextInPlaceholder('Why not start the conversation?')).findsNothing(); + }); + + testWidgets('Group DM', (tester) async { + final selfUserId = eg.selfUser.userId; + final user1 = eg.user(); + final user2 = eg.user(); + await setupMessageListPage(tester, + narrow: DmNarrow.withUsers([user1.userId, user2.userId], selfUserId: selfUserId), + messages: [], users: [eg.selfUser, user1, user2]); + check(findPlaceholder).findsOne(); + check(findTextInPlaceholder('these users')).findsOne(); + check(findTextInPlaceholder('yet.')).findsOne(); + check(findTextInPlaceholder('Why not start the conversation?')).findsOne(); + }); + + testWidgets('Group DM with a deactivated user', (tester) async { + final selfUserId = eg.selfUser.userId; + final user1 = eg.user(isActive: false); + final user2 = eg.user(); + await setupMessageListPage(tester, + narrow: DmNarrow.withUsers([user1.userId, user2.userId], selfUserId: selfUserId), + messages: [], users: [eg.selfUser, user1, user2]); + check(findPlaceholder).findsOne(); + check(findTextInPlaceholder('these users')).findsOne(); + + // Sending messages isn't allowed; don't suggest that + check(findTextInPlaceholder('yet.')).findsNothing(); + check(findTextInPlaceholder('Why not start the conversation?')).findsNothing(); + }); + + testWidgets('Mentions', (tester) async { + await setupMessageListPage(tester, narrow: MentionsNarrow(), messages: []); + check(findPlaceholder).findsOne(); + check(findTextInPlaceholder('mentioned')).findsOne(); + }); + + testWidgets('Starred', (tester) async { + await setupMessageListPage(tester, narrow: StarredMessagesNarrow(), messages: []); + check(findPlaceholder).findsOne(); + check(findTextInPlaceholder('starred')).findsOne(); + check(findTextInPlaceholder('tap “Star message.”')).findsOne(); + await checkLearnMoreButton(tester, + store.tryResolveUrl('/help/star-a-message')!); + }); + + testWidgets('when `messages` empty but `outboxMessages` not empty, show outboxes, not placeholder', (tester) async { + final channel = eg.stream(); + await setupMessageListPage(tester, + narrow: TopicNarrow(channel.streamId, eg.t('topic')), + streams: [channel], + messages: []); + check(findPlaceholder).findsOne(); + + connection.prepare(json: SendMessageResult(id: 1).toJson()); + await tester.enterText(contentInputFinder, 'asdfjkl;'); + await tester.tap(find.byIcon(ZulipIcons.send)); + await tester.pump(kLocalEchoDebounceDuration); + + check(findPlaceholder).findsNothing(); + check(find.text('asdfjkl;')).findsOne(); + }); + }); + group('presents message content appropriately', () { testWidgets('content not asked to consume insets (including bottom), even without compose box', (tester) async { // Regression test for: https://github.com/zulip/zulip-flutter/issues/736 @@ -1634,9 +1840,6 @@ void main() { final topicNarrow = eg.topicNarrow(stream.streamId, topic); const content = 'outbox message content'; - final contentInputFinder = find.byWidgetPredicate( - (widget) => widget is TextField && widget.controller is ComposeContentController); - Finder outboxMessageFinder = find.widgetWithText( OutboxMessageWithPossibleSender, content, skipOffstage: true); diff --git a/test/widgets/recent_dm_conversations_test.dart b/test/widgets/recent_dm_conversations_test.dart index b7307ef6f2..9ec5dc134e 100644 --- a/test/widgets/recent_dm_conversations_test.dart +++ b/test/widgets/recent_dm_conversations_test.dart @@ -72,8 +72,8 @@ void main() { testWidgets('appearance when empty', (tester) async { await setupPage(tester, users: [], dmMessages: []); - check(find.text('You have no direct messages yet! Why not start the conversation?')) - .findsOne(); + check(find.text('You have no direct messages yet!')).findsOne(); + check(find.text('Why not start the conversation?')).findsOne(); }); testWidgets('page builds; conversations appear in order', (tester) async {