From 3a8eb20990f590b711a04ee7e15fd07ae665b9dd Mon Sep 17 00:00:00 2001 From: Chris Bobbe Date: Thu, 26 Jun 2025 20:13:37 -0700 Subject: [PATCH] msglist: Friendlier placeholder text when narrow has no messages Fixes #1653. --- assets/l10n/app_en.arb | 69 ++++++ lib/generated/l10n/zulip_localizations.dart | 90 ++++++++ .../l10n/zulip_localizations_ar.dart | 62 +++++ .../l10n/zulip_localizations_de.dart | 62 +++++ .../l10n/zulip_localizations_el.dart | 62 +++++ .../l10n/zulip_localizations_en.dart | 62 +++++ .../l10n/zulip_localizations_es.dart | 62 +++++ .../l10n/zulip_localizations_fr.dart | 62 +++++ .../l10n/zulip_localizations_he.dart | 62 +++++ .../l10n/zulip_localizations_hu.dart | 62 +++++ .../l10n/zulip_localizations_it.dart | 62 +++++ .../l10n/zulip_localizations_ja.dart | 62 +++++ .../l10n/zulip_localizations_nb.dart | 62 +++++ .../l10n/zulip_localizations_pl.dart | 62 +++++ .../l10n/zulip_localizations_ru.dart | 62 +++++ .../l10n/zulip_localizations_sk.dart | 62 +++++ .../l10n/zulip_localizations_sl.dart | 62 +++++ .../l10n/zulip_localizations_uk.dart | 62 +++++ .../l10n/zulip_localizations_zh.dart | 62 +++++ lib/widgets/message_list.dart | 97 +++++++- test/widgets/message_list_test.dart | 212 +++++++++++++++++- 21 files changed, 1511 insertions(+), 11 deletions(-) diff --git a/assets/l10n/app_en.arb b/assets/l10n/app_en.arb index b0b3523746..7165a6a76e 100644 --- a/assets/l10n/app_en.arb +++ b/assets/l10n/app_en.arb @@ -687,6 +687,75 @@ "@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." + }, + "emptyMessageListChannelWithoutContentAccess": "You don’t have content access to this channel.", + "@emptyMessageListChannelWithoutContentAccess": { + "description": "Placeholder for a channel or topic page when there are no messages and you don’t have content access." + }, + "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 user.", + "@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 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"} + } + }, "emptyMessageListSearch": "No search results.", "@emptyMessageListSearch": { "description": "Placeholder for the 'Search' page when there are no messages." diff --git a/lib/generated/l10n/zulip_localizations.dart b/lib/generated/l10n/zulip_localizations.dart index e76f70ca2b..26f83cc1b6 100644 --- a/lib/generated/l10n/zulip_localizations.dart +++ b/lib/generated/l10n/zulip_localizations.dart @@ -1065,6 +1065,96 @@ abstract class ZulipLocalizations { /// **'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 or topic page when there are no messages and you don’t have content access. + /// + /// In en, this message translates to: + /// **'You don’t have content access to this channel.'** + String get emptyMessageListChannelWithoutContentAccess; + + /// 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 user.'** + 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 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); + /// Placeholder for the 'Search' page when there are no messages. /// /// In en, this message translates to: diff --git a/lib/generated/l10n/zulip_localizations_ar.dart b/lib/generated/l10n/zulip_localizations_ar.dart index 8bbaefedb2..83e585eee7 100644 --- a/lib/generated/l10n/zulip_localizations_ar.dart +++ b/lib/generated/l10n/zulip_localizations_ar.dart @@ -568,6 +568,68 @@ class ZulipLocalizationsAr extends ZulipLocalizations { @override String get emptyMessageList => 'There are no messages here.'; + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListChannelWithoutContentAccess => + 'You don’t have content access to this channel.'; + + @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 user.'; + + @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 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 emptyMessageListSearch => 'No search results.'; diff --git a/lib/generated/l10n/zulip_localizations_de.dart b/lib/generated/l10n/zulip_localizations_de.dart index 2e5d76a19d..669931292d 100644 --- a/lib/generated/l10n/zulip_localizations_de.dart +++ b/lib/generated/l10n/zulip_localizations_de.dart @@ -587,6 +587,68 @@ class ZulipLocalizationsDe extends ZulipLocalizations { @override String get emptyMessageList => 'Hier gibt es keine Nachrichten.'; + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListChannelWithoutContentAccess => + 'You don’t have content access to this channel.'; + + @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 user.'; + + @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 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 emptyMessageListSearch => 'Keine Suchergebnisse.'; diff --git a/lib/generated/l10n/zulip_localizations_el.dart b/lib/generated/l10n/zulip_localizations_el.dart index 546d67c58a..030966263c 100644 --- a/lib/generated/l10n/zulip_localizations_el.dart +++ b/lib/generated/l10n/zulip_localizations_el.dart @@ -568,6 +568,68 @@ class ZulipLocalizationsEl extends ZulipLocalizations { @override String get emptyMessageList => 'There are no messages here.'; + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListChannelWithoutContentAccess => + 'You don’t have content access to this channel.'; + + @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 user.'; + + @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 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 emptyMessageListSearch => 'No search results.'; diff --git a/lib/generated/l10n/zulip_localizations_en.dart b/lib/generated/l10n/zulip_localizations_en.dart index f7797e261b..03ae1f5571 100644 --- a/lib/generated/l10n/zulip_localizations_en.dart +++ b/lib/generated/l10n/zulip_localizations_en.dart @@ -568,6 +568,68 @@ class ZulipLocalizationsEn extends ZulipLocalizations { @override String get emptyMessageList => 'There are no messages here.'; + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListChannelWithoutContentAccess => + 'You don’t have content access to this channel.'; + + @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 user.'; + + @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 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 emptyMessageListSearch => 'No search results.'; diff --git a/lib/generated/l10n/zulip_localizations_es.dart b/lib/generated/l10n/zulip_localizations_es.dart index c9bb85e3f4..0756069a36 100644 --- a/lib/generated/l10n/zulip_localizations_es.dart +++ b/lib/generated/l10n/zulip_localizations_es.dart @@ -568,6 +568,68 @@ class ZulipLocalizationsEs extends ZulipLocalizations { @override String get emptyMessageList => 'There are no messages here.'; + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListChannelWithoutContentAccess => + 'You don’t have content access to this channel.'; + + @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 user.'; + + @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 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 emptyMessageListSearch => 'No search results.'; diff --git a/lib/generated/l10n/zulip_localizations_fr.dart b/lib/generated/l10n/zulip_localizations_fr.dart index a56381bc5f..ee92f30af5 100644 --- a/lib/generated/l10n/zulip_localizations_fr.dart +++ b/lib/generated/l10n/zulip_localizations_fr.dart @@ -584,6 +584,68 @@ class ZulipLocalizationsFr extends ZulipLocalizations { @override String get emptyMessageList => 'There are no messages here.'; + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListChannelWithoutContentAccess => + 'You don’t have content access to this channel.'; + + @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 user.'; + + @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 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 emptyMessageListSearch => 'No search results.'; diff --git a/lib/generated/l10n/zulip_localizations_he.dart b/lib/generated/l10n/zulip_localizations_he.dart index 6d58801b0e..c5d09ec112 100644 --- a/lib/generated/l10n/zulip_localizations_he.dart +++ b/lib/generated/l10n/zulip_localizations_he.dart @@ -568,6 +568,68 @@ class ZulipLocalizationsHe extends ZulipLocalizations { @override String get emptyMessageList => 'There are no messages here.'; + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListChannelWithoutContentAccess => + 'You don’t have content access to this channel.'; + + @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 user.'; + + @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 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 emptyMessageListSearch => 'No search results.'; diff --git a/lib/generated/l10n/zulip_localizations_hu.dart b/lib/generated/l10n/zulip_localizations_hu.dart index 942d73b6de..1b8d14b673 100644 --- a/lib/generated/l10n/zulip_localizations_hu.dart +++ b/lib/generated/l10n/zulip_localizations_hu.dart @@ -568,6 +568,68 @@ class ZulipLocalizationsHu extends ZulipLocalizations { @override String get emptyMessageList => 'There are no messages here.'; + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListChannelWithoutContentAccess => + 'You don’t have content access to this channel.'; + + @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 user.'; + + @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 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 emptyMessageListSearch => 'No search results.'; diff --git a/lib/generated/l10n/zulip_localizations_it.dart b/lib/generated/l10n/zulip_localizations_it.dart index eb0bea54f2..bb32dfb720 100644 --- a/lib/generated/l10n/zulip_localizations_it.dart +++ b/lib/generated/l10n/zulip_localizations_it.dart @@ -581,6 +581,68 @@ class ZulipLocalizationsIt extends ZulipLocalizations { @override String get emptyMessageList => 'There are no messages here.'; + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListChannelWithoutContentAccess => + 'You don’t have content access to this channel.'; + + @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 user.'; + + @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 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 emptyMessageListSearch => 'No search results.'; diff --git a/lib/generated/l10n/zulip_localizations_ja.dart b/lib/generated/l10n/zulip_localizations_ja.dart index db0e7a0780..c8b31c5e52 100644 --- a/lib/generated/l10n/zulip_localizations_ja.dart +++ b/lib/generated/l10n/zulip_localizations_ja.dart @@ -556,6 +556,68 @@ class ZulipLocalizationsJa extends ZulipLocalizations { @override String get emptyMessageList => 'ここにはメッセージがありません。'; + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListChannelWithoutContentAccess => + 'You don’t have content access to this channel.'; + + @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 user.'; + + @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 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 emptyMessageListSearch => '検索結果はありません。'; diff --git a/lib/generated/l10n/zulip_localizations_nb.dart b/lib/generated/l10n/zulip_localizations_nb.dart index 60b9739d88..ae3e3c1fef 100644 --- a/lib/generated/l10n/zulip_localizations_nb.dart +++ b/lib/generated/l10n/zulip_localizations_nb.dart @@ -568,6 +568,68 @@ class ZulipLocalizationsNb extends ZulipLocalizations { @override String get emptyMessageList => 'There are no messages here.'; + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListChannelWithoutContentAccess => + 'You don’t have content access to this channel.'; + + @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 user.'; + + @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 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 emptyMessageListSearch => 'No search results.'; diff --git a/lib/generated/l10n/zulip_localizations_pl.dart b/lib/generated/l10n/zulip_localizations_pl.dart index bf31ba7d86..fdacfce947 100644 --- a/lib/generated/l10n/zulip_localizations_pl.dart +++ b/lib/generated/l10n/zulip_localizations_pl.dart @@ -581,6 +581,68 @@ class ZulipLocalizationsPl extends ZulipLocalizations { @override String get emptyMessageList => 'Póki co brak wiadomości.'; + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListChannelWithoutContentAccess => + 'You don’t have content access to this channel.'; + + @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 user.'; + + @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 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 emptyMessageListSearch => 'Brak wyników wyszukiwania.'; diff --git a/lib/generated/l10n/zulip_localizations_ru.dart b/lib/generated/l10n/zulip_localizations_ru.dart index 3673e97924..275ba49c42 100644 --- a/lib/generated/l10n/zulip_localizations_ru.dart +++ b/lib/generated/l10n/zulip_localizations_ru.dart @@ -582,6 +582,68 @@ class ZulipLocalizationsRu extends ZulipLocalizations { @override String get emptyMessageList => 'Здесь нет сообщений.'; + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListChannelWithoutContentAccess => + 'You don’t have content access to this channel.'; + + @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 user.'; + + @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 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 emptyMessageListSearch => 'Ничего не найдено.'; diff --git a/lib/generated/l10n/zulip_localizations_sk.dart b/lib/generated/l10n/zulip_localizations_sk.dart index a927a4d27d..c01152d607 100644 --- a/lib/generated/l10n/zulip_localizations_sk.dart +++ b/lib/generated/l10n/zulip_localizations_sk.dart @@ -568,6 +568,68 @@ class ZulipLocalizationsSk extends ZulipLocalizations { @override String get emptyMessageList => 'There are no messages here.'; + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListChannelWithoutContentAccess => + 'You don’t have content access to this channel.'; + + @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 user.'; + + @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 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 emptyMessageListSearch => 'No search results.'; diff --git a/lib/generated/l10n/zulip_localizations_sl.dart b/lib/generated/l10n/zulip_localizations_sl.dart index d58235e92e..77ca38bbe6 100644 --- a/lib/generated/l10n/zulip_localizations_sl.dart +++ b/lib/generated/l10n/zulip_localizations_sl.dart @@ -593,6 +593,68 @@ class ZulipLocalizationsSl extends ZulipLocalizations { @override String get emptyMessageList => 'Tukaj ni sporočil.'; + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListChannelWithoutContentAccess => + 'You don’t have content access to this channel.'; + + @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 user.'; + + @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 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 emptyMessageListSearch => 'Ni zadetkov iskanja.'; diff --git a/lib/generated/l10n/zulip_localizations_uk.dart b/lib/generated/l10n/zulip_localizations_uk.dart index e83f72029d..fb0cd0c57a 100644 --- a/lib/generated/l10n/zulip_localizations_uk.dart +++ b/lib/generated/l10n/zulip_localizations_uk.dart @@ -582,6 +582,68 @@ class ZulipLocalizationsUk extends ZulipLocalizations { @override String get emptyMessageList => 'Тут немає повідомлень.'; + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListChannelWithoutContentAccess => + 'You don’t have content access to this channel.'; + + @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 user.'; + + @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 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 emptyMessageListSearch => 'Немає результатів пошуку.'; diff --git a/lib/generated/l10n/zulip_localizations_zh.dart b/lib/generated/l10n/zulip_localizations_zh.dart index 267df9335e..0130d421a5 100644 --- a/lib/generated/l10n/zulip_localizations_zh.dart +++ b/lib/generated/l10n/zulip_localizations_zh.dart @@ -568,6 +568,68 @@ class ZulipLocalizationsZh extends ZulipLocalizations { @override String get emptyMessageList => 'There are no messages here.'; + @override + String get emptyMessageListCombinedFeed => + 'There are no messages in your combined feed.'; + + @override + String get emptyMessageListChannelWithoutContentAccess => + 'You don’t have content access to this channel.'; + + @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 user.'; + + @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 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 emptyMessageListSearch => 'No search results.'; diff --git a/lib/widgets/message_list.dart b/lib/widgets/message_list.dart index 0d3038b1da..6b4a6f325c 100644 --- a/lib/widgets/message_list.dart +++ b/lib/widgets/message_list.dart @@ -1027,19 +1027,10 @@ class _MessageListState extends State with PerAccountStoreAwareStat @override Widget build(BuildContext context) { - final zulipLocalizations = ZulipLocalizations.of(context); - if (!model.fetched) return const Center(child: CircularProgressIndicator()); if (model.items.isEmpty && model.haveNewest && model.haveOldest) { - final String header; - if (widget.narrow is KeywordSearchNarrow) { - header = zulipLocalizations.emptyMessageListSearch; - } else { - header = zulipLocalizations.emptyMessageList; - } - - return PageBodyEmptyContentPlaceholder(header: header); + return _EmptyContentPlaceholder(narrow: widget.narrow); } // Pad the left and right insets, for small devices in landscape. @@ -1265,6 +1256,92 @@ class _MessageListState extends State with PerAccountStoreAwareStat } } +class _EmptyContentPlaceholder extends StatelessWidget { + const _EmptyContentPlaceholder({required this.narrow}); + + final Narrow narrow; + + @override + Widget build(BuildContext context) { + final store = PerAccountStoreWidget.of(context); + final zulipLocalizations = ZulipLocalizations.of(context); + + switch (narrow) { + case CombinedFeedNarrow(): + return PageBodyEmptyContentPlaceholder( + header: zulipLocalizations.emptyMessageListCombinedFeed); + case ChannelNarrow(:final streamId) || TopicNarrow(:final streamId): + final channel = store.streams[streamId]; + if (channel == null) { + return PageBodyEmptyContentPlaceholder( + header: zulipLocalizations.emptyMessageListChannelUnavailable); + } else if (!store.selfHasContentAccess(channel)) { + return PageBodyEmptyContentPlaceholder( + headerWithLinkMarkup: zulipLocalizations.emptyMessageListChannelWithoutContentAccess, + onTapHeaderLink: () => PlatformActions.launchUrl(context, + store.tryResolveUrl('/help/channel-permissions')!)); + } else { + return PageBodyEmptyContentPlaceholder( + header: zulipLocalizations.emptyMessageList); + } + case DmNarrow(:final otherRecipientIds) when otherRecipientIds.isEmpty: + return PageBodyEmptyContentPlaceholder( + header: zulipLocalizations.emptyMessageListSelfDmHeader, + message: zulipLocalizations.emptyMessageListSelfDmMessage); + case DmNarrow(:final otherRecipientIds) when otherRecipientIds.length == 1: + final user = store.getUser(otherRecipientIds.single); + switch (user) { + case null: + return PageBodyEmptyContentPlaceholder( + header: zulipLocalizations.emptyMessageListDmUnknownUser); + case User(isActive: false): + return PageBodyEmptyContentPlaceholder( + header: zulipLocalizations.emptyMessageListDmDeactivatedUser( + store.userDisplayName(user.userId, replaceIfMuted: false))); + case User(): + final displayName = store.userDisplayName(user.userId, replaceIfMuted: false); + return PageBodyEmptyContentPlaceholder( + header: zulipLocalizations.emptyMessageListDm(displayName), + message: store.isUserMuted(user.userId) + ? null + : zulipLocalizations.emptyMessageListDmStartConversation, + ); + } + case DmNarrow(:final otherRecipientIds) + when otherRecipientIds.any((userId) { + final user = store.getUser(userId); + return user != null && !user.isActive; + }): + return PageBodyEmptyContentPlaceholder( + header: zulipLocalizations.emptyMessageListGroupDmDeactivatedUser); + case DmNarrow(): + return PageBodyEmptyContentPlaceholder( + header: zulipLocalizations.emptyMessageListGroupDm, + message: zulipLocalizations.emptyMessageListDmStartConversation); + case MentionsNarrow(): + return PageBodyEmptyContentPlaceholder( + headerWithLinkMarkup: zulipLocalizations.emptyMessageListMentionsHeader, + onTapHeaderLink: () => PlatformActions.launchUrl(context, + store.tryResolveUrl('/help/mention-a-user-or-group')!), + message: store.zulipFeatureLevel >= 224 + // This string mentions @topic, which is new in Server 8. + ? zulipLocalizations.emptyMessageListMentionsMessage + : null, // TODO(server-8) + ); + case StarredMessagesNarrow(): + return PageBodyEmptyContentPlaceholder( + header: zulipLocalizations.emptyMessageListStarredHeader, + messageWithLinkMarkup: zulipLocalizations.emptyMessageListStarredMessage( + zulipLocalizations.actionSheetOptionStarMessage), + onTapMessageLink: () => PlatformActions.launchUrl(context, + store.tryResolveUrl('/help/star-a-message')!)); + case KeywordSearchNarrow(): + return PageBodyEmptyContentPlaceholder( + header: zulipLocalizations.emptyMessageListSearch); + } + } +} + class _MessageListHistoryStart extends StatelessWidget { const _MessageListHistoryStart(); diff --git a/test/widgets/message_list_test.dart b/test/widgets/message_list_test.dart index 240285a222..23ca9ed55e 100644 --- a/test/widgets/message_list_test.dart +++ b/test/widgets/message_list_test.dart @@ -371,9 +371,219 @@ void main() { Finder findTextInPlaceholder(String text) => find.descendant(of: findPlaceholder, matching: find.textContaining(text)); + Future checkLink(WidgetTester tester, { + required String linkText, required Uri expectedUrl}) async { + await tester.tapOnText(find.textRange.ofSubstring(linkText)); + final (url: url, mode: _) = testBinding.takeLaunchUrlCalls().single; + check(url).equals(expectedUrl); + } + testWidgets('Combined feed', (tester) async { await setupMessageListPage(tester, narrow: CombinedFeedNarrow(), messages: []); - check(findTextInPlaceholder('There are no messages here.')).findsOne(); + check(findPlaceholder).findsOne(); + check(findTextInPlaceholder('combined feed')).findsOne(); + }); + + testWidgets('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('Channel without content access', (tester) async { + final channel = eg.stream(inviteOnly: true); + 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(store.selfHasContentAccess(channel)).isFalse(); + check(findPlaceholder).findsOne(); + check(findTextInPlaceholder('content access')).findsOne(); + await checkLink(tester, + linkText: 'content access', + expectedUrl: store.tryResolveUrl('/help/channel-permissions')!); + }); + + 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 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 channel without content access', (tester) async { + final channel = eg.stream(inviteOnly: true); + await setupMessageListPage(tester, + narrow: TopicNarrow(channel.streamId, eg.t('topic')), messages: [], streams: [channel], + skipPumpAndSettle: true); + + // The topic input is autofocused, triggering topic autocomplete. + connection.prepare(json: GetStreamTopicsResult(topics: []).toJson()); + await tester.pumpAndSettle(); + + check(store.selfHasContentAccess(channel)).isFalse(); + check(findPlaceholder).findsOne(); + check(findTextInPlaceholder('content access')).findsOne(); + await checkLink(tester, + linkText: 'content access', + expectedUrl: store.tryResolveUrl('/help/channel-permissions')!); + }); + + 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 user')).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('1:1 DM, muted and 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]); + 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(); + + // 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 checkLink(tester, linkText: 'Starring', + expectedUrl: store.tryResolveUrl('/help/star-a-message')!); }); testWidgets('Search, empty keyword', (tester) async {