diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index e694cc160..b41c7d502 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -232,6 +232,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_file_size_limit1" = "The document can't be sent, because it is larger than {size}."; "lng_file_size_limit2" = "You can double this limit to {size} per document by subscribing to **Telegram Premium**."; +"lng_filter_links_limit_title" = "Limit Reached"; +"lng_filter_links_limit1#one" = "Sorry, you can't create more than **{count}** invite link."; +"lng_filter_links_limit1#other" = "Sorry, you can't create more than **{count}** invite links."; +"lng_filter_links_limit2#one" = "You can increase the limit to **{count}** link by subscribing to **Telegram Premium**."; +"lng_filter_links_limit2#other" = "You can increase the limit to **{count}** links by subscribing to **Telegram Premium**."; + +"lng_filter_shared_limit_title" = "Limit Reached"; +"lng_filter_shared_limit1#one" = "Sorry, you can't add more than **{count}** shareable folders."; +"lng_filter_shared_limit1#other" = "Sorry, you can't add more than **{count}** shareable folders."; +"lng_filter_shared_limit2#one" = "You can increase the limit up to **{count}** folder by subscribing to **Telegram Premium**."; +"lng_filter_shared_limit2#other" = "You can increase the limit up to **{count}** folders by subscribing to **Telegram Premium**."; + "lng_limits_increase" = "Increase Limit"; "lng_sticker_premium_text" = "This pack contains premium stickers like this one."; @@ -3541,22 +3553,46 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_filters_toast_add" = "{chat} added to {folder} folder"; "lng_filters_toast_remove" = "{chat} removed from {folder} folder"; -"lng_filters_link" = "Invite Link"; +"lng_filters_link" = "Invite links"; +"lng_filters_link_badge" = "New"; "lng_filters_link_create" = "Share Folder"; -"lng_filters_link_add" = "Add Link"; -"lng_filters_link_cant" = "You can only share folders that have several explicit chats selected, no general chat types and no exclusions."; +"lng_filters_link_cant" = "No way to share folders with chat types or excluded chats."; "lng_filters_link_about" = "Share access to some of this folder's groups and channels with others."; +"lng_filters_link_about_many" = "Create more links to set up different access levels for different people."; "lng_filters_link_title" = "Share Folder"; -"lng_filters_link_select" = "Select the chats you want to share with the link."; +"lng_filters_link_share_about" = "Anyone with this link can add the {folder} folder and the chats selected below."; +"lng_filters_link_subtitle" = "Invite link"; +"lng_filters_link_chats_none" = "No chats selected"; +"lng_filters_link_chats#one" = "{count} chat selected"; +"lng_filters_link_chats#other" = "{count} chats selected"; +"lng_filters_link_bot_status" = "you can't share chats with bots"; +"lng_filters_link_bot_error" = "Chat's with bots can't be shared You can't share chats with bots"; +"lng_filters_link_private_status" = "you can't share private chats"; +"lng_filters_link_private_error" = "You can't share private chats"; +"lng_filters_link_noadmin_status" = "you can't invite others here"; +"lng_filters_link_noadmin_group_error" = "You don't have the admin rights to share invite links to this group chat."; +"lng_filters_link_noadmin_channel_error" = "You don't have the admin rights to share invite links to this channel."; +"lng_filters_link_chats_about" = "Select groups and channels that you want everyone who adds the folder via invite link to join."; +"lng_filters_link_no_about" = "There are no chats in this folder that you can share with others."; +"lng_filters_link_chats_no" = "These chats cannot be shared"; +"lng_filters_link_chats_no_about" = "You can only share groups and channels in which you are allowed to create invite links."; +"lng_filters_link_name_it" = "Name Link"; +"lng_filters_link_delete_sure" = "Are you sure you want to delete this link?"; "lng_filters_by_link_title" = "Add Folder"; "lng_filters_by_link_sure" = "Do you want to add a new chat folder {folder} and join its groups and channels?"; "lng_filters_by_link_join#one" = "{count} chat to join"; "lng_filters_by_link_join#other" = "{count} chats to join"; -"lng_filters_by_link_add" = "Add {folder}"; +"lng_filters_by_link_add_button" = "Add {folder}"; +"lng_filters_by_link_add_no" = "Do not add this folder"; "lng_filters_by_link_more" = "Add Chats to Folder"; "lng_filters_by_link_more_sure" = "Do you want to join chats and add them to your folder {folder}?"; "lng_filters_by_link_about" = "You can deselect the chats you don't want to join."; "lng_filters_by_link_join_button" = "Join Chats"; +"lng_filters_by_link_join_no" = "Do not join any chats"; +"lng_filters_by_link_already" = "Folder already added"; +"lng_filters_by_link_already_about" = "You have already added the folder {folder} and all its chats."; +"lng_filters_by_link_in#one" = "{count} chat in this folder"; +"lng_filters_by_link_in#other" = "{count} chats in this folder"; "lng_filters_by_link_remove" = "Remove Folder"; "lng_filters_by_link_remove_sure" = "Do you want to quit the chats you joined when added the folder {folder}?"; "lng_filters_by_link_quit#one" = "{count} chat to quit"; @@ -3565,6 +3601,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_filters_by_link_deselect" = "Deselect All"; "lng_filters_by_link_remove_button" = "Remove Folder"; "lng_filters_by_link_quit_button" = "Remove Folder and Chats"; +"lng_filters_bar_you_can#one" = "You can join {count} new chat"; +"lng_filters_bar_you_can#other" = "You can join {count} new chats"; +"lng_filters_bar_view" = "Click here to view them"; "lng_chat_theme_change" = "Change colors"; "lng_chat_theme_none" = "No\nTheme"; diff --git a/Telegram/SourceFiles/api/api_chat_filters.cpp b/Telegram/SourceFiles/api/api_chat_filters.cpp index 837af84d9..5396f14f7 100644 --- a/Telegram/SourceFiles/api/api_chat_filters.cpp +++ b/Telegram/SourceFiles/api/api_chat_filters.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "main/main_session.h" #include "ui/boxes/confirm_box.h" +#include "ui/text/text_utilities.h" #include "ui/toasts/common_toasts.h" #include "ui/widgets/buttons.h" #include "window/window_session_controller.h" @@ -64,6 +65,7 @@ public: private: void setupAboveWidget(); + void setupBelowWidget(); const not_null _window; @@ -81,16 +83,15 @@ private: }; [[nodiscard]] rpl::producer TitleText(HeaderType type) { - // langs switch (type) { case HeaderType::AddingFilter: - return rpl::single(u"Add Folder"_q); + return tr::lng_filters_by_link_title(); case HeaderType::AddingChats: - return rpl::single(u"Add Chats to Folder"_q); + return tr::lng_filters_by_link_more(); case HeaderType::AllAdded: - return rpl::single(u"Folder Already Added"_q); + return tr::lng_filters_by_link_already(); case HeaderType::Removing: - return rpl::single(u"Remove Folder"_q); + return tr::lng_filters_by_link_remove(); } Unexpected("HeaderType in TitleText."); } @@ -98,27 +99,44 @@ private: void FillHeader( not_null container, HeaderDescriptor descriptor) { - // langs - const auto description = (descriptor.type == HeaderType::AddingFilter) - ? (u"Do you want to add a new chat folder "_q - + descriptor.title - + u" and join its groups and channels?"_q) + const auto phrase = (descriptor.type == HeaderType::AddingFilter) + ? tr::lng_filters_by_link_sure : (descriptor.type == HeaderType::AddingChats) - ? (u"Do you want to join "_q - + QString::number(descriptor.badge) - + u" chats and add them to your folder "_q - + descriptor.title + '?') + ? tr::lng_filters_by_link_more_sure : (descriptor.type == HeaderType::AllAdded) - ? (u"You have already added the folder "_q - + descriptor.title - + u" and all its chats."_q) - : (u"Do you want to quit the chats you joined " - "when adding the folder "_q - + descriptor.title + '?'); + ? tr::lng_filters_by_link_already_about + : tr::lng_filters_by_link_remove_sure; + auto boldTitle = Ui::Text::Bold(descriptor.title); + auto description = (descriptor.type == HeaderType::AddingFilter) + ? tr::lng_filters_by_link_sure( + tr::now, + lt_folder, + std::move(boldTitle), + Ui::Text::WithEntities) + : (descriptor.type == HeaderType::AddingChats) + ? tr::lng_filters_by_link_more_sure( + tr::now, + lt_folder, + std::move(boldTitle), + Ui::Text::WithEntities) + : (descriptor.type == HeaderType::AllAdded) + ? tr::lng_filters_by_link_already_about( + tr::now, + lt_folder, + std::move(boldTitle), + Ui::Text::WithEntities) + : tr::lng_filters_by_link_remove_sure( + tr::now, + lt_folder, + std::move(boldTitle), + Ui::Text::WithEntities); container->add( object_ptr( container, - description, + phrase( + lt_folder, + rpl::single(Ui::Text::Bold(descriptor.title)), + Ui::Text::WithEntities), st::boxDividerLabel), st::boxRowPadding); } @@ -152,12 +170,12 @@ void ImportInvite( } ToggleChatsController::ToggleChatsController( - not_null window, - ToggleAction action, - const QString &slug, - FilterId filterId, - const QString &title, - std::vector> chats) + not_null window, + ToggleAction action, + const QString &slug, + FilterId filterId, + const QString &title, + std::vector> chats) : _window(window) , _action(action) , _slug(slug) @@ -168,6 +186,7 @@ ToggleChatsController::ToggleChatsController( void ToggleChatsController::prepare() { setupAboveWidget(); + setupBelowWidget(); auto selected = base::flat_set>(); for (const auto &peer : _chats) { auto row = std::make_unique(peer); @@ -211,9 +230,22 @@ void ToggleChatsController::setupAboveWidget() { .badge = (type == HeaderType::AddingChats) ? int(_chats.size()) : 0, }); + // lng_filters_by_link_join; // langs + delegate()->peerListSetAboveWidget(std::move(wrap)); } +void ToggleChatsController::setupBelowWidget() { + delegate()->peerListSetBelowWidget( + object_ptr( + (QWidget*)nullptr, + object_ptr( + (QWidget*)nullptr, + tr::lng_filters_by_link_about(), + st::boxDividerLabel), + st::settingsDividerLabelPadding)); +} + Main::Session &ToggleChatsController::session() const { return _window->session(); } @@ -226,7 +258,6 @@ auto ToggleChatsController::selectedValue() const [[nodiscard]] void AlreadyFilterBox( not_null box, const QString &title) { - // langs box->setTitle(TitleText(HeaderType::AllAdded)); FillHeader(box->verticalLayout(), { diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp index c52d16202..003efe022 100644 --- a/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp @@ -744,11 +744,7 @@ void EditFilterBox( } const auto shared = CollectFilterLinkChats(*result); if (shared.empty()) { - // langs - Ui::ShowMultilineToast({ - .parentOverride = Window::Show(window).toastParent(), - .text = { tr::lng_filters_link_cant(tr::now) }, - }); + window->show(ShowLinkBox(window, *result, {})); return; } saveAnd(*result, crl::guard(box, [=](Data::ChatFilter updated) { @@ -767,7 +763,12 @@ void EditFilterBox( }, createLink->lifetime()); AddSkip(content); - AddDividerText(content, tr::lng_filters_link_about()); + AddDividerText( + content, + rpl::conditional( + state->hasLinks.value(), + tr::lng_filters_link_about_many(), + tr::lng_filters_link_about())); const auto show = std::make_shared(box); const auto refreshPreviews = [=] { diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp index 30298fb72..a181d3dac 100644 --- a/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peers/edit_peer_invite_link.h" // InviteLinkQrBox. #include "boxes/peer_list_box.h" #include "data/data_channel.h" +#include "data/data_chat.h" #include "data/data_chat_filters.h" #include "data/data_session.h" #include "data/data_user.h" @@ -22,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/boxes/confirm_box.h" #include "ui/controls/invite_link_buttons.h" #include "ui/controls/invite_link_label.h" +#include "ui/text/text_utilities.h" #include "ui/toasts/common_toasts.h" #include "ui/widgets/input_fields.h" #include "ui/widgets/popup_menu.h" @@ -60,28 +62,49 @@ struct InviteLinkAction { Type type = Type::Copy; }; -[[nodiscard]] std::optional ErrorForSharing( +struct Errors { + QString status; + QString toast; +}; + +[[nodiscard]] std::optional ErrorForSharing( not_null history) { + const auto result = [](const QString &status, const QString &toast) { + return Errors{ status, toast }; + }; const auto peer = history->peer; - if (const auto user = peer->asUser()) { // langs + if (const auto user = peer->asUser()) { return user->isBot() - ? u"you can't share chats with bots"_q - : u"you can't share private chats"_q; - } else if (const auto channel = history->peer->asChannel()) { - if (!channel->canHaveInviteLink()) { - return u"you can't invite others here"_q; + ? result( + tr::lng_filters_link_bot_status(tr::now), + tr::lng_filters_link_bot_error(tr::now)) + : result( + tr::lng_filters_link_private_status(tr::now), + tr::lng_filters_link_private_error(tr::now)); + } else if (const auto chat = history->peer->asChat()) { + if (!chat->canHaveInviteLink()) { + return result( + tr::lng_filters_link_noadmin_status(tr::now), + tr::lng_filters_link_noadmin_group_error(tr::now)); + } + return std::nullopt; + } else if (const auto channel = history->peer->asChannel()) { + if (!channel->canHaveInviteLink()) { + return result( + tr::lng_filters_link_noadmin_status(tr::now), + (channel->isMegagroup() + ? tr::lng_filters_link_noadmin_group_error(tr::now) + : tr::lng_filters_link_noadmin_channel_error(tr::now))); } return std::nullopt; - } else { - return u"you can't share this :("_q; } + Unexpected("Peer type in ErrorForSharing."); } void ShowEmptyLinkError(not_null window) { - // langs Ui::ShowMultilineToast({ .parentOverride = Window::Show(window).toastParent(), - .text = { u"Link should have at least one chat shared."_q }, + .text = { tr::lng_filters_empty(tr::now) }, }); } @@ -359,14 +382,16 @@ public: private: void setupAboveWidget(); + void setupBelowWidget(); void addHeader(not_null container); void addLinkBlock(not_null container); const not_null _window; InviteLinkData _data; + QString _filterTitle; base::flat_set> _filterChats; - base::flat_set> _allowed; + base::flat_map, QString> _denied; rpl::variable>> _selected; base::flat_set> _initial; @@ -387,6 +412,7 @@ LinkController::LinkController( const Data::ChatFilter &filter, InviteLinkData data) : _window(window) +, _filterTitle(filter.title()) , _filterChats(filter.always()) { _data = std::move(data); _link = _data.url; @@ -421,7 +447,12 @@ void LinkController::addHeader(not_null container) { verticalLayout, object_ptr( verticalLayout, - tr::lng_filters_about(), // langs + (_data.url.isEmpty() + ? tr::lng_filters_link_no_about(Ui::Text::WithEntities) + : tr::lng_filters_link_share_about( + lt_folder, + rpl::single(Ui::Text::Bold(_filterTitle)), + Ui::Text::WithEntities)), st::settingsFilterDividerLabel)), st::settingsFilterDividerLabelPadding); @@ -439,8 +470,9 @@ object_ptr DeleteLinkBox( close(); }; return Ui::MakeConfirmBox({ - u"Are you sure you want to delete this link?"_q, // langs - sure, + .text = tr::lng_filters_link_delete_sure(tr::now), + .confirmed = sure, + .confirmText = tr::lng_box_delete(tr::now), }); } @@ -490,7 +522,7 @@ void LinkController::addLinkBlock(not_null container) { getLinkQr, &st::menuIconQrCode); result->addAction( - u"Name Link"_q, // langs + tr::lng_filters_link_name_it(tr::now), editLink, &st::menuIconEdit); result->addAction( @@ -499,8 +531,7 @@ void LinkController::addLinkBlock(not_null container) { &st::menuIconDelete); return result; }; - - AddSubsectionTitle(container, tr::lng_manage_peer_link_invite()); + AddSubsectionTitle(container, tr::lng_filters_link_subtitle()); const auto prefix = u"https://"_q; const auto label = container->lifetime().make_state( @@ -529,9 +560,9 @@ void LinkController::prepare() { Expects(!_data.url.isEmpty() || _data.chats.empty()); setupAboveWidget(); + setupBelowWidget(); for (const auto &history : _data.chats) { const auto peer = history->peer; - _allowed.emplace(peer); auto row = std::make_unique(peer); const auto raw = row.get(); delegate()->peerListAppendRow(std::move(row)); @@ -547,9 +578,10 @@ void LinkController::prepare() { const auto raw = row.get(); delegate()->peerListAppendRow(std::move(row)); if (const auto error = ErrorForSharing(history)) { - raw->setCustomStatus(*error); - } else if (!_data.url.isEmpty()) { - _allowed.emplace(peer); + raw->setCustomStatus(error->status); + _denied.emplace(peer, error->toast); + } else if (_data.url.isEmpty()) { + _denied.emplace(peer); } } delegate()->peerListRefreshRows(); @@ -557,8 +589,15 @@ void LinkController::prepare() { } void LinkController::rowClicked(not_null row) { - if (_allowed.contains(row->peer())) { - const auto peer = row->peer(); + const auto peer = row->peer(); + if (const auto i = _denied.find(peer); i != end(_denied)) { + if (!i->second.isEmpty()) { + Ui::ShowMultilineToast({ + .parentOverride = delegate()->peerListToastParent(), + .text = { i->second }, + }); + } + } else { const auto checked = row->checked(); auto selected = _selected.current(); delegate()->peerListSetRowChecked(row, !checked); @@ -588,12 +627,16 @@ void LinkController::setupAboveWidget() { addLinkBlock(container); } - // langs auto subtitle = _selected.value( - ) | rpl::map([](const base::flat_set> &selected) { - return selected.empty() - ? u"No chats selected"_q - : (QString::number(selected.size()) + u" chats selected"_q); + ) | rpl::map([=](const base::flat_set> &selected) { + return _data.url.isEmpty() + ? tr::lng_filters_link_chats_no(tr::now) + : selected.empty() + ? tr::lng_filters_link_chats_none(tr::now) + : tr::lng_filters_link_chats( + tr::now, + lt_count, + float64(selected.size())); }); Settings::AddSubsectionTitle( container, @@ -602,6 +645,19 @@ void LinkController::setupAboveWidget() { delegate()->peerListSetAboveWidget(std::move(wrap)); } +void LinkController::setupBelowWidget() { + delegate()->peerListSetBelowWidget( + object_ptr( + (QWidget*)nullptr, + object_ptr( + (QWidget*)nullptr, + (_data.url.isEmpty() + ? tr::lng_filters_link_chats_no_about() + : tr::lng_filters_link_chats_about()), + st::boxDividerLabel), + st::settingsDividerLabelPadding)); +} + Main::Session &LinkController::session() const { return _window->session(); } @@ -728,7 +784,7 @@ base::unique_qptr LinksController::createRowContextMenu( getLinkQr, &st::menuIconQrCode); result->addAction( - u"Name Link"_q, // langs + tr::lng_filters_link_name_it(tr::now), editLink, &st::menuIconEdit); result->addAction( @@ -931,7 +987,7 @@ object_ptr ShowLinkBox( auto initBox = [=](not_null box) { box->setTitle(!link.title.isEmpty() ? rpl::single(link.title) - : tr::lng_manage_peer_link_invite()); + : tr::lng_filters_link_title()); raw->hasChangesValue( ) | rpl::start_with_next([=](bool has) {