From 26ff3148d6d635cb941831e17a4fd8cbb6ea6b31 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Feb 2023 11:31:19 +0400 Subject: [PATCH 01/21] One more fonts fix and fix paste from clipboard. --- Telegram/build/prepare/prepare.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index a37243a5afbea6..af339d72f98abf 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -404,7 +404,7 @@ def runStages(): stage('patches', """ git clone https://github.com/desktop-app/patches.git cd patches - git checkout 65cd5219e4 + git checkout c00002819c """) stage('msys64', """ From 5f3e7235a5408e9ecd96a359c7421ff8f791d497 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Feb 2023 11:35:17 +0400 Subject: [PATCH 02/21] Update submodules. --- Telegram/lib_base | 2 +- Telegram/lib_ui | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/lib_base b/Telegram/lib_base index d043c38a6a5652..0698cd1af30b09 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit d043c38a6a5652b341976b6e089f5ab8fbee7331 +Subproject commit 0698cd1af30b09b66d5eee7dfa93c4d489086660 diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 98fa93787a13d8..e053e04607653c 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 98fa93787a13d8ad9427facb465500fe3179860e +Subproject commit e053e04607653c8a304c72ff901a8e628dd94dbf From e486cf1afaf26738542784478af533a07321a39a Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 7 Feb 2023 04:40:51 +0300 Subject: [PATCH 03/21] Fixed calculate of discount in premium subscription options. --- Telegram/SourceFiles/api/api_premium_option.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/api/api_premium_option.cpp b/Telegram/SourceFiles/api/api_premium_option.cpp index 257a4d12a17d88..135082e946707a 100644 --- a/Telegram/SourceFiles/api/api_premium_option.cpp +++ b/Telegram/SourceFiles/api/api_premium_option.cpp @@ -11,7 +11,7 @@ For license and copyright information please follow this link: namespace Api { -constexpr auto kDiscountDivider = 5.; +constexpr auto kDiscountDivider = 1.; Data::SubscriptionOption CreateSubscriptionOption( int months, @@ -20,7 +20,7 @@ Data::SubscriptionOption CreateSubscriptionOption( const QString ¤cy, const QString &botUrl) { const auto discount = [&] { - const auto percent = monthlyAmount * months / float64(amount) - 1.; + const auto percent = 1. - float64(amount) / (monthlyAmount * months); return std::round(percent * 100. / kDiscountDivider) * kDiscountDivider; }(); From d7aa18cb0aacd6acd772cce07029bc7bc5533d39 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 7 Feb 2023 05:03:18 +0300 Subject: [PATCH 04/21] Moved light part of palette gradients to top in userpic emoji builder. --- .../info/userpic/info_userpic_emoji_builder_widget.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_widget.cpp b/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_widget.cpp index d7155ba1e8b326..16682097609714 100644 --- a/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_widget.cpp +++ b/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_widget.cpp @@ -97,7 +97,7 @@ void AlignChildren(not_null widget, int fullWidth) { } [[nodiscard]] std::vector> PaletteGradients() { - return std::vector>{ + auto v = std::vector>{ { QColor(32, 226, 205), QColor(14, 225, 241), @@ -141,6 +141,12 @@ void AlignChildren(not_null widget, int fullWidth) { QColor(176, 99, 255), }, }; + for (auto &g : v) { + // Rotate 180 degrees. + std::swap(g[0], g[2]); + std::swap(g[1], g[3]); + } + return v; } void ShowGradientEditor( From d889cd0e72b2a589bf1e06c27f5b37c8bdb9ebc7 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Feb 2023 16:28:59 +0400 Subject: [PATCH 05/21] Fix polls forwarding to private chats. --- .../data/data_chat_participant_status.cpp | 4 ++-- .../data/data_chat_participant_status.h | 16 ++++++++-------- Telegram/SourceFiles/data/data_peer.cpp | 12 ++++++++---- Telegram/SourceFiles/data/data_peer.h | 1 + Telegram/SourceFiles/data/data_peer_values.cpp | 10 +--------- .../history/view/history_view_top_bar_widget.cpp | 2 +- Telegram/SourceFiles/window/window_peer_menu.cpp | 5 ++--- 7 files changed, 23 insertions(+), 27 deletions(-) diff --git a/Telegram/SourceFiles/data/data_chat_participant_status.cpp b/Telegram/SourceFiles/data/data_chat_participant_status.cpp index 0a4922989238ec..af45ba79cb547e 100644 --- a/Telegram/SourceFiles/data/data_chat_participant_status.cpp +++ b/Telegram/SourceFiles/data/data_chat_participant_status.cpp @@ -96,7 +96,7 @@ ChatRestrictions TabbedPanelSendRestrictions() { // Duplicated in CanSendAnyOfValue(). bool CanSendAnyOf( - not_null thread, + not_null thread, ChatRestrictions rights, bool forbidInForums) { const auto peer = thread->peer(); @@ -107,7 +107,7 @@ bool CanSendAnyOf( // Duplicated in CanSendAnyOfValue(). bool CanSendAnyOf( - not_null peer, + not_null peer, ChatRestrictions rights, bool forbidInForums) { if (const auto user = peer->asUser()) { diff --git a/Telegram/SourceFiles/data/data_chat_participant_status.h b/Telegram/SourceFiles/data/data_chat_participant_status.h index 30dba03bfa55a0..2227e80424a2c8 100644 --- a/Telegram/SourceFiles/data/data_chat_participant_status.h +++ b/Telegram/SourceFiles/data/data_chat_participant_status.h @@ -131,43 +131,43 @@ struct RestrictionsSetOptions { [[nodiscard]] ChatRestrictions TabbedPanelSendRestrictions(); [[nodiscard]] bool CanSendAnyOf( - not_null thread, + not_null thread, ChatRestrictions rights, bool forbidInForums = true); [[nodiscard]] bool CanSendAnyOf( - not_null peer, + not_null peer, ChatRestrictions rights, bool forbidInForums = true); [[nodiscard]] inline bool CanSend( - not_null thread, + not_null thread, ChatRestriction right, bool forbidInForums = true) { return CanSendAnyOf(thread, right, forbidInForums); } [[nodiscard]] inline bool CanSend( - not_null peer, + not_null peer, ChatRestriction right, bool forbidInForums = true) { return CanSendAnyOf(peer, right, forbidInForums); } [[nodiscard]] inline bool CanSendTexts( - not_null thread, + not_null thread, bool forbidInForums = true) { return CanSend(thread, ChatRestriction::SendOther, forbidInForums); } [[nodiscard]] inline bool CanSendTexts( - not_null peer, + not_null peer, bool forbidInForums = true) { return CanSend(peer, ChatRestriction::SendOther, forbidInForums); } [[nodiscard]] inline bool CanSendAnything( - not_null thread, + not_null thread, bool forbidInForums = true) { return CanSendAnyOf(thread, AllSendRestrictions(), forbidInForums); } [[nodiscard]] inline bool CanSendAnything( - not_null peer, + not_null peer, bool forbidInForums = true) { return CanSendAnyOf(peer, AllSendRestrictions(), forbidInForums); } diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index e5c36129e842de..560736b46e32b1 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -9,6 +9,7 @@ For license and copyright information please follow this link: #include "data/data_user.h" #include "data/data_chat.h" +#include "data/data_chat_participant_status.h" #include "data/data_channel.h" #include "data/data_changes.h" #include "data/data_photo.h" @@ -475,6 +476,13 @@ bool PeerData::canPinMessages() const { Unexpected("Peer type in PeerData::canPinMessages."); } +bool PeerData::canCreatePolls() const { + if (const auto user = asUser()) { + return user->isBot() && !user->isSupport(); + } + return Data::CanSend(this, ChatRestriction::SendPolls); +} + bool PeerData::canCreateTopics() const { if (const auto channel = asChannel()) { return channel->isForum() @@ -940,10 +948,6 @@ Data::RestrictionCheckResult PeerData::amRestricted( ? ((user->flags() & UserDataFlag::VoiceMessagesForbidden) ? Result::Explicit() : Result::Allowed()) - : (right == ChatRestriction::SendPolls) - ? ((!user->isBot() || user->isSupport()) - ? Result::Explicit() - : Result::Allowed()) : (right == ChatRestriction::PinMessages) ? ((user->flags() & UserDataFlag::CanPinMessages) ? Result::Allowed() diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 3d966d1a32e6e4..1940e573b15dcd 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -317,6 +317,7 @@ class PeerData { [[nodiscard]] bool canPinMessages() const; [[nodiscard]] bool canEditMessagesIndefinitely() const; + [[nodiscard]] bool canCreatePolls() const; [[nodiscard]] bool canCreateTopics() const; [[nodiscard]] bool canManageTopics() const; [[nodiscard]] bool canExportChatHistory() const; diff --git a/Telegram/SourceFiles/data/data_peer_values.cpp b/Telegram/SourceFiles/data/data_peer_values.cpp index c7622ea24fadb0..1670afa8f43e56 100644 --- a/Telegram/SourceFiles/data/data_peer_values.cpp +++ b/Telegram/SourceFiles/data/data_peer_values.cpp @@ -215,19 +215,11 @@ inline auto DefaultRestrictionValue( return rpl::single(false); } using namespace rpl::mappers; - const auto other = rights & ~(ChatRestriction::SendPolls - | ChatRestriction::SendVoiceMessages + const auto other = rights & ~(ChatRestriction::SendVoiceMessages | ChatRestriction::SendVideoMessages); if (other) { return PeerFlagValue(user, UserDataFlag::Deleted) | rpl::map(!_1); - } else if (rights & ChatRestriction::SendPolls) { - if (CanSend(user, ChatRestriction::SendPolls)) { - return PeerFlagValue(user, UserDataFlag::Deleted) - | rpl::map(!_1); - } else if (rights == ChatRestriction::SendPolls) { - return rpl::single(false); - } } const auto mask = UserDataFlag::Deleted | UserDataFlag::VoiceMessagesForbidden; diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp index edcf5d7858bb6e..917b49a3c2802e 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -1033,7 +1033,7 @@ void TopBarWidget::updateControlsVisibility() { const auto section = _activeChat.section; const auto historyMode = (section == Section::History); const auto hasPollsMenu = (_activeChat.key.peer() - && Data::CanSend(_activeChat.key.peer(), ChatRestriction::SendPolls)) + && _activeChat.key.peer()->canCreatePolls()) || (topic && Data::CanSend(topic, ChatRestriction::SendPolls)); const auto hasTopicMenu = [&] { if (!topic || section != Section::Replies) { diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 2e00071fa17bae..7dceba83771693 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -991,10 +991,9 @@ void Filler::addManageChat() { } void Filler::addCreatePoll() { - constexpr auto kRight = ChatRestriction::SendPolls; const auto can = _topic - ? Data::CanSend(_topic, kRight) - : Data::CanSend(_peer, kRight); + ? Data::CanSend(_topic, ChatRestriction::SendPolls) + : _peer->canCreatePolls(); if (!can) { return; } From 64f4e0dd52306d09d61454131ecff841d4d1b83c Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Feb 2023 16:29:34 +0400 Subject: [PATCH 06/21] Don't offer translate from / to the same language. --- Telegram/Resources/langs/cloud_lang.strings | 98 ++++++++++++++++++- Telegram/Resources/langs/lang.strings | 3 + Telegram/SourceFiles/boxes/translate_box.cpp | 69 ++++++++++--- Telegram/SourceFiles/boxes/translate_box.h | 16 ++- .../view/history_view_translate_bar.cpp | 59 +++++++---- .../history/view/history_view_translate_bar.h | 3 + .../view/history_view_translate_tracker.cpp | 2 +- .../ui/boxes/choose_language_box.cpp | 40 ++++++-- .../ui/boxes/choose_language_box.h | 8 ++ Telegram/lib_spellcheck | 2 +- 10 files changed, 259 insertions(+), 41 deletions(-) diff --git a/Telegram/Resources/langs/cloud_lang.strings b/Telegram/Resources/langs/cloud_lang.strings index 1abb893e7b6061..c9d4af958b8f3b 100644 --- a/Telegram/Resources/langs/cloud_lang.strings +++ b/Telegram/Resources/langs/cloud_lang.strings @@ -35,7 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "cloud_lng_passport_in_it" = "Italian"; "cloud_lng_passport_in_ja" = "Japanese"; "cloud_lng_passport_in_ka" = "Georgian"; -"cloud_lng_passport_in_km" = "Khmer"; +// "cloud_lng_passport_in_km" = "Khmer"; "cloud_lng_passport_in_ko" = "Korean"; "cloud_lng_passport_in_lo" = "Lao"; "cloud_lng_passport_in_lt" = "Lithuanian"; @@ -58,3 +58,99 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "cloud_lng_passport_in_uk" = "Ukrainian"; "cloud_lng_passport_in_uz" = "Uzbek"; "cloud_lng_passport_in_vi" = "Vietnamese"; + +"cloud_lng_translate_to_ar" = "Arabic"; +"cloud_lng_translate_to_az" = "Azerbaijani"; +"cloud_lng_translate_to_bg" = "Bulgarian"; +// "cloud_lng_translate_to_bn" = "Bangla"; +"cloud_lng_translate_to_cs" = "Czech"; +"cloud_lng_translate_to_da" = "Danish"; +"cloud_lng_translate_to_de" = "German"; +// "cloud_lng_translate_to_dv" = "Divehi"; +// "cloud_lng_translate_to_dz" = "Dzongkha"; +"cloud_lng_translate_to_el" = "Greek"; +"cloud_lng_translate_to_en" = "English"; +"cloud_lng_translate_to_es" = "Spanish"; +"cloud_lng_translate_to_et" = "Estonian"; +"cloud_lng_translate_to_fa" = "Persian"; +"cloud_lng_translate_to_fr" = "French"; +"cloud_lng_translate_to_he" = "Hebrew"; +"cloud_lng_translate_to_hr" = "Croatian"; +"cloud_lng_translate_to_hu" = "Hungarian"; +"cloud_lng_translate_to_hy" = "Armenian"; +"cloud_lng_translate_to_id" = "Indonesian"; +"cloud_lng_translate_to_is" = "Icelandic"; +"cloud_lng_translate_to_it" = "Italian"; +"cloud_lng_translate_to_ja" = "Japanese"; +"cloud_lng_translate_to_ka" = "Georgian"; +// "cloud_lng_translate_to_km" = "Khmer"; +"cloud_lng_translate_to_ko" = "Korean"; +"cloud_lng_translate_to_lo" = "Lao"; +"cloud_lng_translate_to_lt" = "Lithuanian"; +"cloud_lng_translate_to_lv" = "Latvian"; +"cloud_lng_translate_to_mk" = "Macedonian"; +"cloud_lng_translate_to_mn" = "Mongolian"; +"cloud_lng_translate_to_ms" = "Malay"; +"cloud_lng_translate_to_my" = "Burmese"; +"cloud_lng_translate_to_ne" = "Nepali"; +"cloud_lng_translate_to_nl" = "Dutch"; +"cloud_lng_translate_to_pl" = "Polish"; +"cloud_lng_translate_to_pt" = "Portuguese"; +"cloud_lng_translate_to_ro" = "Romanian"; +"cloud_lng_translate_to_ru" = "Russian"; +"cloud_lng_translate_to_sk" = "Slovak"; +"cloud_lng_translate_to_sl" = "Slovenian"; +"cloud_lng_translate_to_th" = "Thai"; +"cloud_lng_translate_to_tk" = "Turkmen"; +"cloud_lng_translate_to_tr" = "Turkish"; +"cloud_lng_translate_to_uk" = "Ukrainian"; +"cloud_lng_translate_to_uz" = "Uzbek"; +"cloud_lng_translate_to_vi" = "Vietnamese"; + +"cloud_lng_language_ar" = "Arabic"; +"cloud_lng_language_az" = "Azerbaijani"; +"cloud_lng_language_bg" = "Bulgarian"; +// "cloud_lng_language_bn" = "Bangla"; +"cloud_lng_language_cs" = "Czech"; +"cloud_lng_language_da" = "Danish"; +"cloud_lng_language_de" = "German"; +// "cloud_lng_language_dv" = "Divehi"; +// "cloud_lng_language_dz" = "Dzongkha"; +"cloud_lng_language_el" = "Greek"; +"cloud_lng_language_en" = "English"; +"cloud_lng_language_es" = "Spanish"; +"cloud_lng_language_et" = "Estonian"; +"cloud_lng_language_fa" = "Persian"; +"cloud_lng_language_fr" = "French"; +"cloud_lng_language_he" = "Hebrew"; +"cloud_lng_language_hr" = "Croatian"; +"cloud_lng_language_hu" = "Hungarian"; +"cloud_lng_language_hy" = "Armenian"; +"cloud_lng_language_id" = "Indonesian"; +"cloud_lng_language_is" = "Icelandic"; +"cloud_lng_language_it" = "Italian"; +"cloud_lng_language_ja" = "Japanese"; +"cloud_lng_language_ka" = "Georgian"; +// "cloud_lng_language_km" = "Khmer"; +"cloud_lng_language_ko" = "Korean"; +"cloud_lng_language_lo" = "Lao"; +"cloud_lng_language_lt" = "Lithuanian"; +"cloud_lng_language_lv" = "Latvian"; +"cloud_lng_language_mk" = "Macedonian"; +"cloud_lng_language_mn" = "Mongolian"; +"cloud_lng_language_ms" = "Malay"; +"cloud_lng_language_my" = "Burmese"; +"cloud_lng_language_ne" = "Nepali"; +"cloud_lng_language_nl" = "Dutch"; +"cloud_lng_language_pl" = "Polish"; +"cloud_lng_language_pt" = "Portuguese"; +"cloud_lng_language_ro" = "Romanian"; +"cloud_lng_language_ru" = "Russian"; +"cloud_lng_language_sk" = "Slovak"; +"cloud_lng_language_sl" = "Slovenian"; +"cloud_lng_language_th" = "Thai"; +"cloud_lng_language_tk" = "Turkmen"; +"cloud_lng_language_tr" = "Turkish"; +"cloud_lng_language_uk" = "Ukrainian"; +"cloud_lng_language_uz" = "Uzbek"; +"cloud_lng_language_vi" = "Vietnamese"; diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 0f08fdf67cbe81..2d940c84fc7597 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2233,8 +2233,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_translate_show_original" = "Show Original"; "lng_translate_bar_to" = "Translate to {name}"; +"lng_translate_bar_to_other" = "Translate to {name}"; "lng_translate_menu_to" = "Translate To"; "lng_translate_menu_dont" = "Don't translate {name}"; +"lng_translate_menu_dont_other" = "Don't translate {name}"; "lng_translate_menu_hide" = "Hide"; "lng_translate_hidden_user" = "Translation bar is now hidden for this chat."; "lng_translate_hidden_group" = "Translation bar is now hidden for this group."; @@ -3389,6 +3391,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_translate_settings_chat" = "Translate Entire Chat"; "lng_translate_settings_choose" = "Do Not Translate"; "lng_translate_settings_about" = "The 'Translate' button will appear when you open a context menu on a text message."; +"lng_translate_settings_one" = "Please choose at least one language so that it can be used as the \"Translate to\" language."; "lng_launch_exe_warning" = "This file has a {extension} extension.\nAre you sure you want to run it?"; "lng_launch_svg_warning" = "Opening this file can potentially expose your IP address to its sender. Continue?"; diff --git a/Telegram/SourceFiles/boxes/translate_box.cpp b/Telegram/SourceFiles/boxes/translate_box.cpp index 20557206e9095d..07af0a754646ec 100644 --- a/Telegram/SourceFiles/boxes/translate_box.cpp +++ b/Telegram/SourceFiles/boxes/translate_box.cpp @@ -11,6 +11,8 @@ For license and copyright information please follow this link: #include "core/core_settings.h" #include "core/ui_integration.h" #include "data/data_peer.h" +#include "data/data_session.h" +#include "history/history.h" #include "lang/lang_instance.h" #include "lang/lang_keys.h" #include "main/main_session.h" @@ -96,9 +98,15 @@ void TranslateBox( box->addButton(tr::lng_box_ok(), [=] { box->closeBox(); }); const auto container = box->verticalLayout(); - auto id = Core::App().settings().translateToValue(); - const auto api = box->lifetime().make_state( - &peer->session().mtp()); + struct State { + State(not_null session) : api(&session->mtp()) { + } + + MTP::Sender api; + rpl::variable to; + }; + const auto state = box->lifetime().make_state(&peer->session()); + state->to = ChooseTranslateTo(peer->owner().history(peer)); text.entities = ranges::views::all( text.entities @@ -174,10 +182,10 @@ void TranslateBox( const auto padding = st::settingsSubsectionTitlePadding; const auto subtitle = Settings::AddSubsectionTitle( container, - rpl::duplicate(id) | rpl::map(LanguageName)); + state->to.value() | rpl::map(LanguageName)); // Workaround. - rpl::duplicate(id) | rpl::start_with_next([=] { + state->to.value() | rpl::start_with_next([=] { subtitle->resizeToWidth(container->width() - padding.left() - padding.right()); @@ -197,7 +205,7 @@ void TranslateBox( box, st::aboutLabel, std::min(original->entity()->height() / lineHeight, kMaxLines), - rpl::duplicate(id) | rpl::map([=](LanguageId id) { + state->to.value() | rpl::map([=](LanguageId id) { return id.locale().textDirection() == Qt::RightToLeft; })))); @@ -210,7 +218,7 @@ void TranslateBox( const auto send = [=](LanguageId to) { loading->show(anim::type::instant); translated->hide(anim::type::instant); - api->request(MTPmessages_TranslateText( + state->api.request(MTPmessages_TranslateText( MTP_flags(flags), msgId ? peer->input : MTP_inputPeerEmpty(), (msgId @@ -221,7 +229,7 @@ void TranslateBox( : MTP_vector(1, MTP_textWithEntities( MTP_string(text.text), MTP_vector()))), - MTP_string(to.locale().name().mid(0, 2)) + MTP_string(to.twoLetterCode()) )).done([=](const MTPmessages_TranslatedText &result) { const auto &data = result.data(); const auto &list = data.vresult().v; @@ -232,13 +240,15 @@ void TranslateBox( showText(tr::lng_translate_box_error(tr::now)); }).send(); }; - std::move(id) | rpl::start_with_next(send, box->lifetime()); + state->to.value() | rpl::start_with_next(send, box->lifetime()); box->addLeftButton(tr::lng_settings_language(), [=] { if (loading->toggled()) { return; } - Ui::BoxShow(box).showBox(ChooseTranslateToBox()); + Ui::BoxShow(box).showBox(ChooseTranslateToBox( + state->to.current(), + crl::guard(box, [=](LanguageId id) { state->to = id; }))); }); } @@ -283,17 +293,50 @@ object_ptr EditSkipTranslationLanguages() { }, Core::App().settings().skipTranslationLanguages(), true); } -object_ptr ChooseTranslateToBox() { - const auto selected = std::vector{ +object_ptr ChooseTranslateToBox( + LanguageId bringUp, + Fn callback) { + auto selected = std::vector{ Core::App().settings().translateTo(), }; + if (bringUp && bringUp != selected.front()) { + selected.push_back(bringUp); + } return Box(ChooseLanguageBox, tr::lng_languages(), [=]( const std::vector &ids) { Expects(!ids.empty()); - Core::App().settings().setTranslateTo(ids.front()); + const auto id = ids.front(); + Core::App().settings().setTranslateTo(id); Core::App().saveSettingsDelayed(); + callback(id); }, selected, false); } +LanguageId ChooseTranslateTo(not_null history) { + return ChooseTranslateTo(history->translateOfferedFrom()); +} + +LanguageId ChooseTranslateTo(LanguageId offeredFrom) { + auto &settings = Core::App().settings(); + return ChooseTranslateTo( + offeredFrom, + settings.translateTo(), + settings.skipTranslationLanguages()); +} + +LanguageId ChooseTranslateTo( + not_null history, + LanguageId savedTo, + const std::vector &skip) { + return ChooseTranslateTo(history->translateOfferedFrom(), savedTo, skip); +} + +LanguageId ChooseTranslateTo( + LanguageId offeredFrom, + LanguageId savedTo, + const std::vector &skip) { + return (offeredFrom != savedTo) ? savedTo : skip.front(); +} + } // namespace Ui diff --git a/Telegram/SourceFiles/boxes/translate_box.h b/Telegram/SourceFiles/boxes/translate_box.h index 04df4b1f13ced4..1e3bade46aefc8 100644 --- a/Telegram/SourceFiles/boxes/translate_box.h +++ b/Telegram/SourceFiles/boxes/translate_box.h @@ -9,6 +9,7 @@ For license and copyright information please follow this link: #include "base/object_ptr.h" +class History; class PeerData; struct LanguageId; @@ -27,6 +28,19 @@ void TranslateBox( [[nodiscard]] bool SkipTranslate(TextWithEntities textWithEntities); [[nodiscard]] object_ptr EditSkipTranslationLanguages(); -[[nodiscard]] object_ptr ChooseTranslateToBox(); +[[nodiscard]] object_ptr ChooseTranslateToBox( + LanguageId bringUp, + Fn callback); + +[[nodiscard]] LanguageId ChooseTranslateTo(not_null history); +[[nodiscard]] LanguageId ChooseTranslateTo(LanguageId offeredFrom); +[[nodiscard]] LanguageId ChooseTranslateTo( + not_null history, + LanguageId savedTo, + const std::vector &skip); +[[nodiscard]] LanguageId ChooseTranslateTo( + LanguageId offeredFrom, + LanguageId savedTo, + const std::vector &skip); } // namespace Ui diff --git a/Telegram/SourceFiles/history/view/history_view_translate_bar.cpp b/Telegram/SourceFiles/history/view/history_view_translate_bar.cpp index 3fb8d8067968fc..a9e01fd5754920 100644 --- a/Telegram/SourceFiles/history/view/history_view_translate_bar.cpp +++ b/Telegram/SourceFiles/history/view/history_view_translate_bar.cpp @@ -14,7 +14,6 @@ For license and copyright information please follow this link: #include "history/history.h" #include "lang/lang_keys.h" #include "main/main_session.h" -#include "spellcheck/spellcheck_types.h" #include "ui/effects/ripple_animation.h" #include "ui/boxes/choose_language_box.h" // EditSkipTranslationLanguages. #include "ui/layers/box_content.h" @@ -294,14 +293,6 @@ void TranslateBar::setup(not_null history) { : Core::App().settings().translateTo()); }); - Core::App().settings().translateToValue( - ) | rpl::filter([=](LanguageId should) { - const auto now = history->translatedTo(); - return now && (now != should); - }) | rpl::start_with_next([=](LanguageId should) { - translateTo(should); - }, _wrap.lifetime()); - const auto label = Ui::CreateChild( button, st::historyTranslateLabel); @@ -343,8 +334,34 @@ void TranslateBar::setup(not_null history) { updateLabelGeometry(); }, lifetime()); - rpl::combine( + _overridenTo = history->translatedTo(); + _to = rpl::combine( Core::App().settings().translateToValue(), + Core::App().settings().skipTranslationLanguagesValue(), + history->session().changes().historyFlagsValue( + history, + Data::HistoryUpdate::Flag::TranslateFrom), + _overridenTo.value() + ) | rpl::map([=]( + LanguageId to, + const std::vector &skip, + const auto &, + LanguageId overridenTo) { + return overridenTo + ? overridenTo + : Ui::ChooseTranslateTo(history, to, skip); + }) | rpl::distinct_until_changed(); + + _to.value( + ) | rpl::filter([=](LanguageId should) { + const auto now = history->translatedTo(); + return now && (now != should); + }) | rpl::start_with_next([=](LanguageId should) { + translateTo(should); + }, _wrap.lifetime()); + + rpl::combine( + _to.value(), history->session().changes().historyFlagsValue( history, (Data::HistoryUpdate::Flag::TranslatedTo @@ -352,16 +369,17 @@ void TranslateBar::setup(not_null history) { history->session().changes().peerFlagsValue( history->peer, Data::PeerUpdate::Flag::TranslationDisabled) - ) | rpl::map([=](LanguageId to, const auto&, const auto&) { + ) | rpl::map([=]( + LanguageId to, + const auto&, + const auto&) { using Flag = PeerData::TranslationFlag; return (history->peer->translationFlag() != Flag::Enabled) ? rpl::single(QString()) : history->translatedTo() ? tr::lng_translate_show_original() : history->translateOfferedFrom() - ? tr::lng_translate_bar_to( - lt_name, - rpl::single(Ui::LanguageName(to))) + ? Ui::TranslateBarTo(to) : rpl::single(QString()); }) | rpl::flatten_latest( ) | rpl::distinct_until_changed( @@ -408,20 +426,25 @@ void TranslateBar::showMenu(base::unique_qptr menu) { _menu = std::move(menu); _menu->setForcedOrigin(Ui::PanelAnimation::Origin::TopRight); + const auto guard = Ui::MakeWeak(&_wrap); + const auto now = _history->translatedTo(); + const auto to = now ? now : Ui::ChooseTranslateTo(_history); const auto weak = base::make_weak(_controller); const auto chooseCallback = [=] { if (const auto strong = weak.get()) { - strong->show(Ui::ChooseTranslateToBox()); + strong->show(Ui::ChooseTranslateToBox( + to, + crl::guard(guard, [=](LanguageId id) { _overridenTo = id; }) + )); } }; _menu->addAction(MakeTranslateToItem( _menu->menu(), - Ui::LanguageName(Core::App().settings().translateTo()), + Ui::LanguageName(to ? to : Ui::ChooseTranslateTo(_history)), chooseCallback)); _menu->addSeparator(); const auto history = _history; if (const auto translateOfferedFrom = _history->translateOfferedFrom()) { - const auto name = Ui::LanguageName(translateOfferedFrom); const auto addToIgnoreList = [=] { showSettingsToast(history->peer, translateOfferedFrom); @@ -436,7 +459,7 @@ void TranslateBar::showMenu(base::unique_qptr menu) { Core::App().saveSettingsDelayed(); }; _menu->addAction( - tr::lng_translate_menu_dont(tr::now, lt_name, name), + Ui::TranslateMenuDont(tr::now, translateOfferedFrom), addToIgnoreList, &st::menuIconBlock); } diff --git a/Telegram/SourceFiles/history/view/history_view_translate_bar.h b/Telegram/SourceFiles/history/view/history_view_translate_bar.h index 62846100a38a33..a50c0fc7108e48 100644 --- a/Telegram/SourceFiles/history/view/history_view_translate_bar.h +++ b/Telegram/SourceFiles/history/view/history_view_translate_bar.h @@ -8,6 +8,7 @@ For license and copyright information please follow this link: #pragma once #include "ui/wrap/slide_wrap.h" +#include "spellcheck/spellcheck_types.h" class History; struct LanguageId; @@ -68,6 +69,8 @@ class TranslateBar final { std::unique_ptr _shadow; Fn _shadowGeometryPostprocess; base::unique_qptr _menu; + rpl::variable _overridenTo; + rpl::variable _to; bool _shouldBeShown = false; bool _forceHidden = false; diff --git a/Telegram/SourceFiles/history/view/history_view_translate_tracker.cpp b/Telegram/SourceFiles/history/view/history_view_translate_tracker.cpp index a75a95e094b09c..77fcbfa102e033 100644 --- a/Telegram/SourceFiles/history/view/history_view_translate_tracker.cpp +++ b/Telegram/SourceFiles/history/view/history_view_translate_tracker.cpp @@ -234,7 +234,7 @@ void TranslateTracker::requestSome() { peer->input, MTP_vector(list), MTPVector(), - MTP_string(to.locale().name().mid(0, 2)) + MTP_string(to.twoLetterCode()) )).done([=](const MTPmessages_TranslatedText &result) { requestDone(to, result.data().vresult().v); }).fail([=] { diff --git a/Telegram/SourceFiles/ui/boxes/choose_language_box.cpp b/Telegram/SourceFiles/ui/boxes/choose_language_box.cpp index 403b242318beec..6f90d8f737c224 100644 --- a/Telegram/SourceFiles/ui/boxes/choose_language_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/choose_language_box.cpp @@ -22,7 +22,8 @@ For license and copyright information please follow this link: namespace Ui { namespace { -const auto kLanguageNamePrefix = "cloud_lng_passport_in_"; +const auto kLanguageNamePrefix = "cloud_lng_language_"; +const auto kTranslateToPrefix = "cloud_lng_translate_to_"; [[nodiscard]] std::vector TranslationLanguagesList() { // If adding some languages here you need to check that it is @@ -214,12 +215,13 @@ QString LanguageNameTranslated(const QString &twoLetterCode) { kLanguageNamePrefix + twoLetterCode.toUtf8()); } +QString LanguageNameLocal(LanguageId id) { + return QLocale::languageToString(id.language()); +} + QString LanguageName(LanguageId id) { - const auto code = id.locale().name().toLower().mid(0, 2); - const auto translated = LanguageNameTranslated(code); - return translated.isEmpty() - ? QLocale::languageToString(id.locale().language()) - : translated; + const auto translated = LanguageNameTranslated(id.twoLetterCode()); + return translated.isEmpty() ? LanguageNameLocal(id) : translated; } QString LanguageNameNative(LanguageId id) { @@ -236,6 +238,29 @@ QString LanguageNameNative(LanguageId id) { } } +rpl::producer TranslateBarTo(LanguageId id) { + const auto translated = Lang::GetNonDefaultValue( + kTranslateToPrefix + id.twoLetterCode().toUtf8()); + return (translated.isEmpty() + ? tr::lng_translate_bar_to_other + : tr::lng_translate_bar_to)( + lt_name, + rpl::single(translated.isEmpty() + ? LanguageNameLocal(id) + : translated)); +} + +QString TranslateMenuDont(tr::now_t, LanguageId id) { + const auto translated = Lang::GetNonDefaultValue( + kTranslateToPrefix + id.twoLetterCode().toUtf8()); + return (translated.isEmpty() + ? tr::lng_translate_menu_dont_other + : tr::lng_translate_menu_dont)( + tr::now, + lt_name, + translated.isEmpty() ? LanguageNameLocal(id) : translated); +} + void ChooseLanguageBox( not_null box, rpl::producer title, @@ -256,6 +281,9 @@ void ChooseLanguageBox( const auto container = box->verticalLayout(); const auto langs = [&] { auto list = TranslationLanguagesList(); + for (const auto id : list) { + LOG(("cloud_lng_language_%1").arg(id.twoLetterCode())); + } const auto current = LanguageId{ QLocale( Lang::LanguageIdOrDefault(Lang::Id())).language() }; if (const auto i = ranges::find(list, current); i != end(list)) { diff --git a/Telegram/SourceFiles/ui/boxes/choose_language_box.h b/Telegram/SourceFiles/ui/boxes/choose_language_box.h index afbe8d5f9ed341..f33af2b2bda313 100644 --- a/Telegram/SourceFiles/ui/boxes/choose_language_box.h +++ b/Telegram/SourceFiles/ui/boxes/choose_language_box.h @@ -9,14 +9,22 @@ For license and copyright information please follow this link: struct LanguageId; +namespace tr { +struct now_t; +} // namespace tr + namespace Ui { class GenericBox; [[nodiscard]] QString LanguageNameTranslated(const QString &twoLetterCode); +[[nodiscard]] QString LanguageNameLocal(LanguageId id); [[nodiscard]] QString LanguageName(LanguageId id); [[nodiscard]] QString LanguageNameNative(LanguageId id); +[[nodiscard]] rpl::producer TranslateBarTo(LanguageId id); +[[nodiscard]] QString TranslateMenuDont(tr::now_t, LanguageId id); + void ChooseLanguageBox( not_null box, rpl::producer title, diff --git a/Telegram/lib_spellcheck b/Telegram/lib_spellcheck index 55ed1489ffac0b..ae89fefd239ecc 160000 --- a/Telegram/lib_spellcheck +++ b/Telegram/lib_spellcheck @@ -1 +1 @@ -Subproject commit 55ed1489ffac0b60adbd427c2b76be1ce55194e7 +Subproject commit ae89fefd239ecc47d4dab7ba29f9e230376a57d3 From 4a3784660522061a6ed81e2e1984efacc705c835 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Feb 2023 17:29:30 +0400 Subject: [PATCH 07/21] Feed initial messages slice to translation tracker. --- Telegram/SourceFiles/history/history.cpp | 1 + .../history/history_inner_widget.cpp | 3 ++ .../history/view/history_view_list_widget.cpp | 3 ++ .../view/history_view_translate_bar.cpp | 4 +- .../view/history_view_translate_tracker.cpp | 53 ++++++++++++++++--- .../view/history_view_translate_tracker.h | 9 ++-- Telegram/SourceFiles/ui/chat/chat.style | 9 ++-- 7 files changed, 66 insertions(+), 16 deletions(-) diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index e4aaa7a612859e..77251f700aa60f 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -9,6 +9,7 @@ For license and copyright information please follow this link: #include "history/view/history_view_element.h" #include "history/view/history_view_item_preview.h" +#include "history/view/history_view_translate_tracker.h" #include "dialogs/dialogs_indexed_list.h" #include "history/history_inner_widget.h" #include "history/history_item.h" diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index f297ad57f14a46..90dd68bc60e697 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -555,6 +555,9 @@ void HistoryInner::messagesReceived( const QVector &messages) { if (_history->peer == peer) { _history->addOlderSlice(messages); + if (!messages.isEmpty()) { + _translateTracker->addBunchFromBlocks(); + } } else if (_migrated && _migrated->peer == peer) { const auto newLoaded = _migrated && _migrated->isEmpty() diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 2145504dba76f7..85347cb766a465 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -523,6 +523,9 @@ void ListWidget::refreshRows(const Data::MessagesSlice &old) { } } } + if (_translateTracker) { + _translateTracker->addBunchFrom(_items); + } for (auto e = end(_items), i = e - addedToEndCount; i != e; ++i) { _itemRevealPending.emplace(*i); } diff --git a/Telegram/SourceFiles/history/view/history_view_translate_bar.cpp b/Telegram/SourceFiles/history/view/history_view_translate_bar.cpp index a9e01fd5754920..9797a69d4dd07d 100644 --- a/Telegram/SourceFiles/history/view/history_view_translate_bar.cpp +++ b/Telegram/SourceFiles/history/view/history_view_translate_bar.cpp @@ -279,7 +279,7 @@ void TranslateBar::setup(not_null history) { } }; const auto button = static_cast(_wrap.entity()); - button->resize(0, st::historyComposeButton.height); + button->resize(0, st::historyTranslateBarHeight); button->setAttribute(Qt::WA_OpaquePaintEvent); button->paintRequest( @@ -619,7 +619,7 @@ int TranslateBar::height() const { return !_forceHidden ? _wrap.height() : _shouldBeShown - ? st::historyComposeButton.height + ? st::historyTranslateBarHeight : 0; } diff --git a/Telegram/SourceFiles/history/view/history_view_translate_tracker.cpp b/Telegram/SourceFiles/history/view/history_view_translate_tracker.cpp index 77fcbfa102e033..5a3ca97bc2ffae 100644 --- a/Telegram/SourceFiles/history/view/history_view_translate_tracker.cpp +++ b/Telegram/SourceFiles/history/view/history_view_translate_tracker.cpp @@ -26,6 +26,7 @@ namespace { constexpr auto kEnoughForRecognition = 10; constexpr auto kEnoughForTranslation = 6; +constexpr auto kMaxCheckInBunch = 100; constexpr auto kRequestLengthLimit = 24 * 1024; constexpr auto kRequestCountLimit = 20; @@ -82,20 +83,20 @@ void TranslateTracker::startBunch() { ++_generation; } -void TranslateTracker::add(not_null view) { +bool TranslateTracker::add(not_null view) { const auto item = view->data(); const auto only = view->isOnlyEmojiAndSpaces(); if (only != OnlyEmojiAndSpaces::Unknown) { item->cacheOnlyEmojiAndSpaces(only == OnlyEmojiAndSpaces::Yes); } - add(item, false); + return add(item, false); } -void TranslateTracker::add(not_null item) { - add(item, false); +bool TranslateTracker::add(not_null item) { + return add(item, false); } -void TranslateTracker::add( +bool TranslateTracker::add( not_null item, bool skipDependencies) { Expects(_addedInBunch >= 0); @@ -104,7 +105,7 @@ void TranslateTracker::add( || item->isService() || !item->isRegular() || item->isOnlyEmojiAndSpaces()) { - return; + return false; } if (item->translationShowRequiresCheck(_bunchTranslatedTo)) { _switchTranslations[item] = _bunchTranslatedTo; @@ -131,7 +132,7 @@ void TranslateTracker::add( const auto i = _itemsForRecognize.find(id); if (i != end(_itemsForRecognize)) { i->second.generation = _generation; - return; + return true; } const auto &text = item->originalText().text; _itemsForRecognize.emplace(id, ItemForRecognize{ @@ -141,6 +142,7 @@ void TranslateTracker::add( : MaybeLanguageId{ text }), }); ++_addedInBunch; + return true; } void TranslateTracker::switchTranslation( @@ -173,6 +175,43 @@ void TranslateTracker::finishBunch() { requestSome(); } +void TranslateTracker::addBunchFromBlocks() { + if (enoughForRecognition()) { + return; + } + startBunch(); + const auto guard = gsl::finally([&] { + finishBunch(); + }); + + auto check = kMaxCheckInBunch; + for (const auto &block : _history->blocks) { + for (const auto &view : block->messages) { + if (!check-- || (add(view.get()) && enoughForRecognition())) { + return; + } + } + } +} + +void TranslateTracker::addBunchFrom( + const std::vector> &views) { + if (enoughForRecognition()) { + return; + } + startBunch(); + const auto guard = gsl::finally([&] { + finishBunch(); + }); + + auto check = kMaxCheckInBunch; + for (const auto &view : views) { + if (!check-- || (add(view.get()) && enoughForRecognition())) { + return; + } + } +} + void TranslateTracker::cancelToRequest() { if (!_itemsToRequest.empty()) { const auto owner = &_history->owner(); diff --git a/Telegram/SourceFiles/history/view/history_view_translate_tracker.h b/Telegram/SourceFiles/history/view/history_view_translate_tracker.h index 77f9c4e32083c6..9e2ca8b69dfa08 100644 --- a/Telegram/SourceFiles/history/view/history_view_translate_tracker.h +++ b/Telegram/SourceFiles/history/view/history_view_translate_tracker.h @@ -23,10 +23,13 @@ class TranslateTracker final { [[nodiscard]] bool enoughForRecognition() const; void startBunch(); - void add(not_null view); - void add(not_null item); + bool add(not_null view); + bool add(not_null item); void finishBunch(); + void addBunchFromBlocks(); + void addBunchFrom(const std::vector> &views); + [[nodiscard]] rpl::producer trackingLanguage() const; private: @@ -40,7 +43,7 @@ class TranslateTracker final { }; void setup(); - void add(not_null item, bool skipDependencies); + bool add(not_null item, bool skipDependencies); void recognizeCollected(); void trackSkipLanguages(); void checkRecognized(); diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 7e70cce7b44b8e..104c72181b702f 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -1237,18 +1237,19 @@ historyTranslateLabel: FlatLabel(defaultFlatLabel) { minWidth: 80px; } historyTranslateIcon: icon{{ "menu/translate", windowActiveTextFg }}; +historyTranslateBarHeight: 36px; historyTranslateSettings: IconButton(defaultIconButton) { width: 46px; - height: 46px; + height: 36px; icon: icon{{ "menu/customize", windowActiveTextFg }}; iconOver: icon{{ "menu/customize", windowActiveTextFg }}; - rippleAreaPosition: point(4px, 4px); - rippleAreaSize: 38px; + rippleAreaPosition: point(6px, 2px); + rippleAreaSize: 32px; ripple: RippleAnimation(defaultRippleAnimation) { color: lightButtonBgOver; } } -historyTranslateMenuPosition: point(-6px, 40px); +historyTranslateMenuPosition: point(-6px, 30px); historySendDisabled: FlatLabel(defaultFlatLabel) { minWidth: 10px; From ddfcf9f1df51abd96be8259d9e0852cd74add5f4 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Feb 2023 18:38:10 +0400 Subject: [PATCH 08/21] Don't allow empty "Do Not Translate" list. --- Telegram/SourceFiles/boxes/translate_box.cpp | 33 +++++++++++++++++-- .../ui/boxes/choose_language_box.cpp | 29 +++++++++++++--- .../ui/boxes/choose_language_box.h | 3 +- 3 files changed, 57 insertions(+), 8 deletions(-) diff --git a/Telegram/SourceFiles/boxes/translate_box.cpp b/Telegram/SourceFiles/boxes/translate_box.cpp index 07af0a754646ec..805632556cbac6 100644 --- a/Telegram/SourceFiles/boxes/translate_box.cpp +++ b/Telegram/SourceFiles/boxes/translate_box.cpp @@ -22,6 +22,7 @@ For license and copyright information please follow this link: #include "ui/boxes/choose_language_box.h" #include "ui/effects/loading_element.h" #include "ui/layers/generic_box.h" +#include "ui/toasts/common_toasts.h" #include "ui/painter.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" @@ -39,6 +40,8 @@ For license and copyright information please follow this link: namespace Ui { namespace { +constexpr auto kSkipAtLeastOneDuration = 3 * crl::time(1000); + class ShowButton final : public RpWidget { public: ShowButton(not_null parent); @@ -285,12 +288,36 @@ bool SkipTranslate(TextWithEntities textWithEntities) { object_ptr EditSkipTranslationLanguages() { auto title = tr::lng_translate_settings_choose(); - return Box(ChooseLanguageBox, std::move(title), [=]( + const auto selected = std::make_shared>( + Core::App().settings().skipTranslationLanguages()); + const auto weak = std::make_shared>(); + const auto check = [=](LanguageId id) { + const auto already = ranges::contains(*selected, id); + if (already) { + selected->erase(ranges::remove(*selected, id), selected->end()); + } else { + selected->push_back(id); + } + if (already && selected->empty()) { + if (const auto strong = weak->data()) { + Ui::ShowMultilineToast({ + .parentOverride = BoxShow(strong).toastParent(), + .text = { tr::lng_translate_settings_one(tr::now) }, + .duration = kSkipAtLeastOneDuration, + }); + } + return false; + } + return true; + }; + auto result = Box(ChooseLanguageBox, std::move(title), [=]( std::vector &&list) { Core::App().settings().setSkipTranslationLanguages( std::move(list)); Core::App().saveSettingsDelayed(); - }, Core::App().settings().skipTranslationLanguages(), true); + }, *selected, true, check); + *weak = result.data(); + return result; } object_ptr ChooseTranslateToBox( @@ -310,7 +337,7 @@ object_ptr ChooseTranslateToBox( Core::App().settings().setTranslateTo(id); Core::App().saveSettingsDelayed(); callback(id); - }, selected, false); + }, selected, false, nullptr); } LanguageId ChooseTranslateTo(not_null history) { diff --git a/Telegram/SourceFiles/ui/boxes/choose_language_box.cpp b/Telegram/SourceFiles/ui/boxes/choose_language_box.cpp index 6f90d8f737c224..e4bea1cc71a1cb 100644 --- a/Telegram/SourceFiles/ui/boxes/choose_language_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/choose_language_box.cpp @@ -266,7 +266,8 @@ void ChooseLanguageBox( rpl::producer title, Fn)> callback, std::vector selected, - bool multiselect) { + bool multiselect, + Fn toggleCheck) { box->setMinHeight(st::boxWidth); box->setMaxHeight(st::boxWidth); box->setTitle(std::move(title)); @@ -294,6 +295,14 @@ void ChooseLanguageBox( }); return list; }(); + struct ToggleOne { + LanguageId id; + bool selected = false; + }; + struct State { + rpl::event_stream toggles; + }; + const auto state = box->lifetime().make_state(); auto rows = std::vector*>>(); rows.reserve(langs.size()); for (const auto &id : langs) { @@ -302,9 +311,21 @@ void ChooseLanguageBox( container, object_ptr(container, id))); if (multiselect) { - button->entity()->toggleOn( - rpl::single(ranges::contains(selected, id)), - false); + button->entity()->toggleOn(rpl::single( + ranges::contains(selected, id) + ) | rpl::then(state->toggles.events( + ) | rpl::filter([=](ToggleOne one) { + return one.id == id; + }) | rpl::map([=](ToggleOne one) { + return one.selected; + }))); + + button->entity()->toggledChanges( + ) | rpl::start_with_next([=](bool value) { + if (toggleCheck && !toggleCheck(id)) { + state->toggles.fire({ .id = id, .selected = !value }); + } + }, button->lifetime()); } else { button->entity()->setClickedCallback([=] { callback({ id }); diff --git a/Telegram/SourceFiles/ui/boxes/choose_language_box.h b/Telegram/SourceFiles/ui/boxes/choose_language_box.h index f33af2b2bda313..dd96332aedd654 100644 --- a/Telegram/SourceFiles/ui/boxes/choose_language_box.h +++ b/Telegram/SourceFiles/ui/boxes/choose_language_box.h @@ -30,6 +30,7 @@ void ChooseLanguageBox( rpl::producer title, Fn)> callback, std::vector selected, - bool multiselect); + bool multiselect, + Fn toggleCheck); } // namespace Ui From afd717b36e3ef729ea7f6c688330558f62d672ab Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Feb 2023 19:13:07 +0400 Subject: [PATCH 09/21] Ease local card expiry date check. --- .../payments/payments_checkout_process.cpp | 4 +++ .../payments/payments_checkout_process.h | 2 ++ .../SourceFiles/payments/payments_form.cpp | 22 +++++++++++---- Telegram/SourceFiles/payments/payments_form.h | 8 ++++-- .../payments/stripe/stripe_card_validator.cpp | 28 +++++++++++-------- .../payments/stripe/stripe_card_validator.h | 8 ++++-- .../payments/ui/payments_edit_card.cpp | 12 ++++---- .../payments/ui/payments_panel_delegate.h | 2 ++ 8 files changed, 60 insertions(+), 26 deletions(-) diff --git a/Telegram/SourceFiles/payments/payments_checkout_process.cpp b/Telegram/SourceFiles/payments/payments_checkout_process.cpp index efd2ffd39bf9ef..ee6ef45e226d7e 100644 --- a/Telegram/SourceFiles/payments/payments_checkout_process.cpp +++ b/Telegram/SourceFiles/payments/payments_checkout_process.cpp @@ -611,6 +611,10 @@ void CheckoutProcess::panelWebviewMessage( }); } +std::optional CheckoutProcess::panelOverrideExpireDateThreshold() { + return _form->overrideExpireDateThreshold(); +} + bool CheckoutProcess::panelWebviewNavigationAttempt(const QString &uri) { if (Core::TryConvertUrlToLocal(uri) == uri) { return true; diff --git a/Telegram/SourceFiles/payments/payments_checkout_process.h b/Telegram/SourceFiles/payments/payments_checkout_process.h index 9ad90c4062ab9c..1e37c3d28eec8b 100644 --- a/Telegram/SourceFiles/payments/payments_checkout_process.h +++ b/Telegram/SourceFiles/payments/payments_checkout_process.h @@ -151,6 +151,8 @@ class CheckoutProcess final QString panelWebviewDataPath() override; + std::optional panelOverrideExpireDateThreshold() override; + const not_null _session; const std::unique_ptr
_form; const std::unique_ptr _panel; diff --git a/Telegram/SourceFiles/payments/payments_form.cpp b/Telegram/SourceFiles/payments/payments_form.cpp index 248f09caf706b4..b10ee9b4a88c91 100644 --- a/Telegram/SourceFiles/payments/payments_form.cpp +++ b/Telegram/SourceFiles/payments/payments_form.cpp @@ -682,6 +682,13 @@ void Form::submit(const Core::CloudPasswordResult &result) { }).send(); } +std::optional Form::overrideExpireDateThreshold() const { + const auto phone = _session->user()->phone(); + return phone.startsWith('7') + ? QDate(2022, 2, 1) + : std::optional(); +} + void Form::validateInformation(const Ui::RequestedInformation &information) { if (_validateRequestId) { if (_validatedInformation == information) { @@ -795,7 +802,7 @@ void Form::validateCard( bool saveInformation) { Expects(!v::is_null(_paymentMethod.native.data)); - if (!validateCardLocal(details)) { + if (!validateCardLocal(details, overrideExpireDateThreshold())) { return; } const auto &native = _paymentMethod.native.data; @@ -809,15 +816,19 @@ void Form::validateCard( } } -bool Form::validateCardLocal(const Ui::UncheckedCardDetails &details) const { - if (auto error = cardErrorLocal(details)) { +bool Form::validateCardLocal( + const Ui::UncheckedCardDetails &details, + const std::optional &overrideExpireDateThreshold) const { + if (auto error = cardErrorLocal(details, overrideExpireDateThreshold)) { _updates.fire(std::move(error)); return false; } return true; } -Error Form::cardErrorLocal(const Ui::UncheckedCardDetails &details) const { +Error Form::cardErrorLocal( + const Ui::UncheckedCardDetails &details, + const std::optional &overrideExpireDateThreshold) const { using namespace Stripe; auto errors = QStringList(); @@ -830,7 +841,8 @@ Error Form::cardErrorLocal(const Ui::UncheckedCardDetails &details) const { } if (ValidateParsedExpireDate( details.expireMonth, - details.expireYear + details.expireYear, + overrideExpireDateThreshold ) != kValid) { push(u"LOCAL_CARD_EXPIRE_DATE_INVALID"_q); } diff --git a/Telegram/SourceFiles/payments/payments_form.h b/Telegram/SourceFiles/payments/payments_form.h index 4290edee4cbe67..b1e1797c4e7fdd 100644 --- a/Telegram/SourceFiles/payments/payments_form.h +++ b/Telegram/SourceFiles/payments/payments_form.h @@ -218,6 +218,8 @@ class Form final : public base::has_weak_ptr { return _updates.events(); } + [[nodiscard]] std::optional overrideExpireDateThreshold() const; + void validateInformation(const Ui::RequestedInformation &information); void validateCard( const Ui::UncheckedCardDetails &details, @@ -284,9 +286,11 @@ class Form final : public base::has_weak_ptr { const Ui::RequestedInformation &information) const; bool validateCardLocal( - const Ui::UncheckedCardDetails &details) const; + const Ui::UncheckedCardDetails &details, + const std::optional &overrideExpireDateThreshold) const; [[nodiscard]] Error cardErrorLocal( - const Ui::UncheckedCardDetails &details) const; + const Ui::UncheckedCardDetails &details, + const std::optional &overrideExpireDateThreshold) const; const InvoiceId _id; const not_null _session; diff --git a/Telegram/SourceFiles/payments/stripe/stripe_card_validator.cpp b/Telegram/SourceFiles/payments/stripe/stripe_card_validator.cpp index ee09add8d04996..139ab6b62a10d2 100644 --- a/Telegram/SourceFiles/payments/stripe/stripe_card_validator.cpp +++ b/Telegram/SourceFiles/payments/stripe/stripe_card_validator.cpp @@ -207,7 +207,9 @@ CardValidationResult ValidateCard(const QString &number) { }; } -ExpireDateValidationResult ValidateExpireDate(const QString &date) { +ExpireDateValidationResult ValidateExpireDate( + const QString &date, + const std::optional &overrideExpireDateThreshold) { const auto sanitized = RemoveWhitespaces(date).replace('/', QString()); if (!IsNumeric(sanitized)) { return { ValidationState::Invalid }; @@ -225,12 +227,13 @@ ExpireDateValidationResult ValidateExpireDate(const QString &date) { } const auto year = 2000 + normalized.mid(2).toInt(); - const auto currentDate = QDate::currentDate(); - const auto currentMonth = currentDate.month(); - const auto currentYear = currentDate.year(); - if (year < currentYear) { + const auto thresholdDate = overrideExpireDateThreshold.value_or( + QDate::currentDate()); + const auto thresholdMonth = thresholdDate.month(); + const auto thresholdYear = thresholdDate.year(); + if (year < thresholdYear) { return { ValidationState::Invalid }; - } else if (year == currentYear && month < currentMonth) { + } else if (year == thresholdYear && month < thresholdMonth) { return { ValidationState::Invalid }; } return { ValidationState::Valid, true }; @@ -238,15 +241,16 @@ ExpireDateValidationResult ValidateExpireDate(const QString &date) { ValidationState ValidateParsedExpireDate( quint32 month, - quint32 year) { + quint32 year, + const std::optional &overrideExpireDateThreshold) { if ((year / 100) != 20) { return ValidationState::Invalid; } - return ValidateExpireDate( - QString("%1%2" - ).arg(month, 2, 10, QChar('0') - ).arg(year % 100, 2, 10, QChar('0')) - ).state; + const auto date = QString("%1%2" + ).arg(month, 2, 10, QChar('0') + ).arg(year % 100, 2, 10, QChar('0')); + + return ValidateExpireDate(date, overrideExpireDateThreshold).state; } CvcValidationResult ValidateCvc( diff --git a/Telegram/SourceFiles/payments/stripe/stripe_card_validator.h b/Telegram/SourceFiles/payments/stripe/stripe_card_validator.h index 744af0814abb03..0cc6e5013bd7af 100644 --- a/Telegram/SourceFiles/payments/stripe/stripe_card_validator.h +++ b/Telegram/SourceFiles/payments/stripe/stripe_card_validator.h @@ -10,6 +10,8 @@ For license and copyright information please follow this link: #include "stripe/stripe_card.h" #include +class QDate; + namespace Stripe { enum class ValidationState { @@ -32,11 +34,13 @@ struct ExpireDateValidationResult { }; [[nodiscard]] ExpireDateValidationResult ValidateExpireDate( - const QString &date); + const QString &date, + const std::optional &overrideExpireDateThreshold); [[nodiscard]] ValidationState ValidateParsedExpireDate( quint32 month, - quint32 year); + quint32 year, + const std::optional &overrideExpireDateThreshold); struct CvcValidationResult { ValidationState state = ValidationState::Invalid; diff --git a/Telegram/SourceFiles/payments/ui/payments_edit_card.cpp b/Telegram/SourceFiles/payments/ui/payments_edit_card.cpp index bfef08ae12beb2..34efdc5ccce3de 100644 --- a/Telegram/SourceFiles/payments/ui/payments_edit_card.cpp +++ b/Telegram/SourceFiles/payments/ui/payments_edit_card.cpp @@ -165,10 +165,11 @@ template < PostprocessCardValidateResult); } -[[nodiscard]] auto ExpireDateValidator() { - return ComplexNumberValidator( - Stripe::ValidateExpireDate, - PostprocessExpireDateValidateResult); +[[nodiscard]] auto ExpireDateValidator( + const std::optional &overrideExpireDateThreshold) { + return ComplexNumberValidator([=](const QString &date) { + return Stripe::ValidateExpireDate(date, overrideExpireDateThreshold); + }, PostprocessExpireDateValidateResult); } [[nodiscard]] auto CvcValidator(Fn number) { @@ -306,7 +307,8 @@ not_null EditCard::setupContent() { _expire = make(container, { .type = FieldType::CardExpireDate, .placeholder = tr::lng_payments_card_expire_date(), - .validator = ExpireDateValidator(), + .validator = ExpireDateValidator( + _delegate->panelOverrideExpireDateThreshold()), }); _cvc = make(container, { .type = FieldType::CardCVC, diff --git a/Telegram/SourceFiles/payments/ui/payments_panel_delegate.h b/Telegram/SourceFiles/payments/ui/payments_panel_delegate.h index d65bc81a74108e..ca0ed0caf9c629 100644 --- a/Telegram/SourceFiles/payments/ui/payments_panel_delegate.h +++ b/Telegram/SourceFiles/payments/ui/payments_panel_delegate.h @@ -56,6 +56,8 @@ class PanelDelegate { virtual QVariant panelClickHandlerContext() = 0; virtual QString panelWebviewDataPath() = 0; + + virtual std::optional panelOverrideExpireDateThreshold() = 0; }; } // namespace Payments::Ui From 663e89662bac38fc628396ed019d7752865e47ea Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Feb 2023 19:45:18 +0400 Subject: [PATCH 10/21] Bring "Do Not Translate" to "Translate To" list top. --- Telegram/SourceFiles/boxes/translate_box.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/boxes/translate_box.cpp b/Telegram/SourceFiles/boxes/translate_box.cpp index 805632556cbac6..edca9a8ffbb3fd 100644 --- a/Telegram/SourceFiles/boxes/translate_box.cpp +++ b/Telegram/SourceFiles/boxes/translate_box.cpp @@ -323,10 +323,16 @@ object_ptr EditSkipTranslationLanguages() { object_ptr ChooseTranslateToBox( LanguageId bringUp, Fn callback) { + auto &settings = Core::App().settings(); auto selected = std::vector{ - Core::App().settings().translateTo(), + settings.translateTo(), }; - if (bringUp && bringUp != selected.front()) { + for (const auto &id : settings.skipTranslationLanguages()) { + if (id != selected.front()) { + selected.push_back(id); + } + } + if (bringUp && ranges::contains(selected, bringUp)) { selected.push_back(bringUp); } return Box(ChooseLanguageBox, tr::lng_languages(), [=]( From 2f1c674401fb71b771a4afa4ce937460bdfef9b2 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Feb 2023 20:06:16 +0400 Subject: [PATCH 11/21] Support markup in TranslateBox. Server will translate with markup for premium users. --- Telegram/SourceFiles/boxes/translate_box.cpp | 40 +++++++++++++------- Telegram/lib_ui | 2 +- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/Telegram/SourceFiles/boxes/translate_box.cpp b/Telegram/SourceFiles/boxes/translate_box.cpp index edca9a8ffbb3fd..de64a704e8d49c 100644 --- a/Telegram/SourceFiles/boxes/translate_box.cpp +++ b/Telegram/SourceFiles/boxes/translate_box.cpp @@ -7,6 +7,7 @@ For license and copyright information please follow this link: */ #include "boxes/translate_box.h" +#include "api/api_text_entities.h" // Api::EntitiesToMTP / EntitiesFromMTP. #include "core/application.h" #include "core/core_settings.h" #include "core/ui_integration.h" @@ -22,6 +23,7 @@ For license and copyright information please follow this link: #include "ui/boxes/choose_language_box.h" #include "ui/effects/loading_element.h" #include "ui/layers/generic_box.h" +#include "ui/text/text_utilities.h" #include "ui/toasts/common_toasts.h" #include "ui/painter.h" #include "ui/widgets/buttons.h" @@ -111,12 +113,6 @@ void TranslateBox( const auto state = box->lifetime().make_state(&peer->session()); state->to = ChooseTranslateTo(peer->owner().history(peer)); - text.entities = ranges::views::all( - text.entities - ) | ranges::views::filter([](const EntityInText &e) { - return e.type() != EntityType::Spoiler; - }) | ranges::to(); - if (!IsServerMsgId(msgId)) { msgId = 0; } @@ -212,8 +208,14 @@ void TranslateBox( return id.locale().textDirection() == Qt::RightToLeft; })))); - const auto showText = [=](const QString &text) { - translated->entity()->setText(text); + const auto showText = [=](TextWithEntities text) { + const auto label = translated->entity(); + label->setMarkedText( + text, + Core::MarkedTextContext{ + .session = &peer->session(), + .customEmojiRepaint = [=] { label->update(); }, + }); translated->show(anim::type::instant); loading->hide(anim::type::instant); }; @@ -231,16 +233,28 @@ void TranslateBox( ? MTPVector() : MTP_vector(1, MTP_textWithEntities( MTP_string(text.text), - MTP_vector()))), + Api::EntitiesToMTP( + &peer->session(), + text.entities, + Api::ConvertOption::SkipLocal)))), MTP_string(to.twoLetterCode()) )).done([=](const MTPmessages_TranslatedText &result) { const auto &data = result.data(); const auto &list = data.vresult().v; - showText(list.isEmpty() - ? tr::lng_translate_box_error(tr::now) - : qs(list.front().data().vtext())); + if (list.isEmpty()) { + showText( + Ui::Text::Italic(tr::lng_translate_box_error(tr::now))); + } else { + showText(TextWithEntities{ + .text = qs(list.front().data().vtext()), + .entities = Api::EntitiesFromMTP( + &peer->session(), + list.front().data().ventities().v), + }); + } }).fail([=](const MTP::Error &error) { - showText(tr::lng_translate_box_error(tr::now)); + showText( + Ui::Text::Italic(tr::lng_translate_box_error(tr::now))); }).send(); }; state->to.value() | rpl::start_with_next(send, box->lifetime()); diff --git a/Telegram/lib_ui b/Telegram/lib_ui index e053e04607653c..1b9b1739649ddf 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit e053e04607653c8a304c72ff901a8e628dd94dbf +Subproject commit 1b9b1739649ddf942937a6b9a1a22933070e5ecc From ed9ba07a321f596996099c311a954dab728b18c0 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Feb 2023 20:18:17 +0400 Subject: [PATCH 12/21] Disable local card number validation. Some cards are reported invalid there that are valid. --- .../payments/stripe/stripe_card_validator.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Telegram/SourceFiles/payments/stripe/stripe_card_validator.cpp b/Telegram/SourceFiles/payments/stripe/stripe_card_validator.cpp index 139ab6b62a10d2..5b91b70622acd8 100644 --- a/Telegram/SourceFiles/payments/stripe/stripe_card_validator.cpp +++ b/Telegram/SourceFiles/payments/stripe/stripe_card_validator.cpp @@ -193,11 +193,12 @@ CardValidationResult ValidateCard(const QString &number) { } const auto range = MostSpecificBinRangeForNumber(sanitized); const auto brand = range.brand; - if (sanitized.size() > range.length) { - return { .state = ValidationState::Invalid, .brand = brand }; - } else if (sanitized.size() < range.length) { - return { .state = ValidationState::Incomplete, .brand = brand }; - } else if (!IsValidLuhn(sanitized)) { + //if (sanitized.size() > range.length) { + // return { .state = ValidationState::Invalid, .brand = brand }; + //} else if (sanitized.size() < range.length) { + // return { .state = ValidationState::Incomplete, .brand = brand }; + //} else + if (!IsValidLuhn(sanitized)) { return { .state = ValidationState::Invalid, .brand = brand }; } return { From fec80c0c642bf324bfda07a05e015702315184b7 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Feb 2023 20:43:50 +0400 Subject: [PATCH 13/21] Support text-color-ed emoji in userpic editor. --- Telegram/SourceFiles/api/api_peer_photo.cpp | 7 +++- .../data/stickers/data_custom_emoji.cpp | 32 ++----------------- .../data/stickers/data_custom_emoji.h | 4 --- .../info_userpic_emoji_builder_preview.cpp | 6 ++-- .../SourceFiles/settings/settings_premium.cpp | 22 +------------ 5 files changed, 11 insertions(+), 60 deletions(-) diff --git a/Telegram/SourceFiles/api/api_peer_photo.cpp b/Telegram/SourceFiles/api/api_peer_photo.cpp index 65d9207a583c4a..86b08e0584e888 100644 --- a/Telegram/SourceFiles/api/api_peer_photo.cpp +++ b/Telegram/SourceFiles/api/api_peer_photo.cpp @@ -11,6 +11,7 @@ For license and copyright information please follow this link: #include "apiwrap.h" #include "base/random.h" #include "base/unixtime.h" +#include "data/stickers/data_stickers.h" #include "data/data_channel.h" #include "data/data_chat.h" #include "data/data_document.h" @@ -125,7 +126,11 @@ constexpr auto kSharedMediaLimit = 100; colors, ranges::back_inserter(mtpColors), [&](const QColor &c) { return MTP_int(serializeColor(c)); }); - if (sticker->set.id && sticker->set.accessHash) { + if (sticker->setType == Data::StickersType::Emoji) { + return MTP_videoSizeEmojiMarkup( + MTP_long(document->id), + MTP_vector(mtpColors)); + } else if (sticker->set.id && sticker->set.accessHash) { return MTP_videoSizeStickerMarkup( MTP_inputStickerSetID( MTP_long(sticker->set.id), diff --git a/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp b/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp index 447e6f0578e285..04565a741a7b03 100644 --- a/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp +++ b/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp @@ -425,10 +425,6 @@ CustomEmojiManager::CustomEmojiManager(not_null owner) QString()).toULongLong(); if (setId) { _coloredSetId = setId; - auto pending = base::take(_coloredSetPending); - for (const auto &instance : pending[setId]) { - instance->setColored(); - } } }, _lifetime); } @@ -460,12 +456,6 @@ std::unique_ptr CustomEmojiManager::create( }, std::move(repaint))).first; if (colored) { i->second->setColored(); - } else if (_coloredSetId) { - if (_coloredSetId == setId) { - i->second->setColored(); - } - } else if (setId) { - _coloredSetPending[setId].emplace(i->second.get()); } } else if (!i->second->hasImagePreview()) { auto preview = prepareNonExactPreview(documentId, tag, sizeOverride); @@ -693,30 +683,12 @@ void CustomEmojiManager::request() { } void CustomEmojiManager::fillColoredFlags(not_null document) { - const auto id = document->id; - const auto setColored = [&] { - for (auto &instances : _instances) { - const auto i = instances.find(id); - if (i != end(instances)) { - i->second->setColored(); - } - } - }; if (document->emojiUsesTextColor()) { - setColored(); - return; - } - const auto sticker = document->sticker(); - const auto setId = sticker ? sticker->set.id : uint64(); - if (!setId || (_coloredSetId && setId != _coloredSetId)) { - return; - } else if (setId == _coloredSetId) { - setColored(); - } else { + const auto id = document->id; for (auto &instances : _instances) { const auto i = instances.find(id); if (i != end(instances)) { - _coloredSetPending[setId].emplace(i->second.get()); + i->second->setColored(); } } } diff --git a/Telegram/SourceFiles/data/stickers/data_custom_emoji.h b/Telegram/SourceFiles/data/stickers/data_custom_emoji.h index be63ce549292ba..4c79f8870b43c4 100644 --- a/Telegram/SourceFiles/data/stickers/data_custom_emoji.h +++ b/Telegram/SourceFiles/data/stickers/data_custom_emoji.h @@ -152,10 +152,6 @@ class CustomEmojiManager final : public base::has_weak_ptr { not_null, base::flat_set> _listeners; base::flat_set _pendingForRequest; - base::flat_map< - uint64, - base::flat_set< - not_null>> _coloredSetPending; mtpRequestId _requestId = 0; diff --git a/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_preview.cpp b/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_preview.cpp index 01f026ecfd53ca..c7997381cbe5d6 100644 --- a/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_preview.cpp +++ b/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_preview.cpp @@ -118,12 +118,10 @@ void PreviewPainter::paintBackground(QPainter &p, const QImage &image) { bool PreviewPainter::paintForeground(QPainter &p) { if (_player && _player->ready()) { - // resolveIsColored(); + const auto c = _media->owner()->emojiUsesTextColor() ? 255 : 0; auto frame = _player->frame( Size(_emojiSize), - (/*_isColored - ? st::profileVerifiedCheckBg->c - : */QColor(0, 0, 0, 0)), + QColor(c, c, c, c), false, crl::now(), _paused); diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp index 8f7eca2ec5987b..b9c7f78ad79a1f 100644 --- a/Telegram/SourceFiles/settings/settings_premium.cpp +++ b/Telegram/SourceFiles/settings/settings_premium.cpp @@ -489,14 +489,10 @@ class EmojiStatusTopBar final { void paint(QPainter &p); private: - void resolveIsColored(); - QRectF _rect; std::shared_ptr _media; std::unique_ptr _player; bool _paused = false; - bool _isColored = false; - bool _isColoredResolved = false; rpl::lifetime _lifetime; }; @@ -555,27 +551,11 @@ void EmojiStatusTopBar::setPaused(bool paused) { _paused = paused; } -void EmojiStatusTopBar::resolveIsColored() { - if (_isColoredResolved) { - return; - } - const auto document = _media->owner(); - const auto manager = &document->owner().customEmojiManager(); - const auto coloredSetId = manager->coloredSetId(); - if (!coloredSetId) { - return; - } - _isColoredResolved = true; - const auto sticker = document->sticker(); - _isColored = sticker && (sticker->set.id == coloredSetId); -} - void EmojiStatusTopBar::paint(QPainter &p) { if (_player && _player->ready()) { - resolveIsColored(); const auto frame = _player->frame( _rect.size().toSize(), - (_isColored + (_media->owner()->emojiUsesTextColor() ? st::profileVerifiedCheckBg->c : QColor(0, 0, 0, 0)), false, From 41d9a9fcbd0c809c60ddbd9350791b1436aff7d9 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Feb 2023 21:27:33 +0400 Subject: [PATCH 14/21] Version 4.6.2. - One more attempt to fix fonts on Windows. - Fix polls forwarding to private chats. - Improve translations bar appearance. - Improve userpic editor presets. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 4 ++-- Telegram/build/version | 8 ++++---- changelog.txt | 7 +++++++ 6 files changed, 22 insertions(+), 15 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 26d552e49524b8..7f435e28cc935e 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -10,7 +10,7 @@ + Version="4.6.2.0" /> Telegram Desktop Telegram Messenger LLP diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index c80dd9cb5290e1..eb1f214d5bfbaa 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,6,1,0 - PRODUCTVERSION 4,6,1,0 + FILEVERSION 4,6,2,0 + PRODUCTVERSION 4,6,2,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "4.6.1.0" + VALUE "FileVersion", "4.6.2.0" VALUE "LegalCopyright", "Copyright (C) 2014-2023" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "4.6.1.0" + VALUE "ProductVersion", "4.6.2.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index a1e23626fcfc2b..b9d21141b6ff5a 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,6,1,0 - PRODUCTVERSION 4,6,1,0 + FILEVERSION 4,6,2,0 + PRODUCTVERSION 4,6,2,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "4.6.1.0" + VALUE "FileVersion", "4.6.2.0" VALUE "LegalCopyright", "Copyright (C) 2014-2023" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "4.6.1.0" + VALUE "ProductVersion", "4.6.2.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index e2d6b52db33c0e..a5b440255d2da0 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs; constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs; constexpr auto AppName = "Telegram Desktop"_cs; constexpr auto AppFile = "Telegram"_cs; -constexpr auto AppVersion = 4006001; -constexpr auto AppVersionStr = "4.6.1"; +constexpr auto AppVersion = 4006002; +constexpr auto AppVersionStr = "4.6.2"; constexpr auto AppBetaVersion = false; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index dc61870ca65b37..8a347182ccdc9c 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 4006001 +AppVersion 4006002 AppVersionStrMajor 4.6 -AppVersionStrSmall 4.6.1 -AppVersionStr 4.6.1 +AppVersionStrSmall 4.6.2 +AppVersionStr 4.6.2 BetaChannel 0 AlphaVersion 0 -AppVersionOriginal 4.6.1 +AppVersionOriginal 4.6.2 diff --git a/changelog.txt b/changelog.txt index 836130d1657337..5ba4a4848558d0 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,10 @@ +4.6.2 (07.02.23) + +- One more attempt to fix fonts on Windows. +- Fix polls forwarding to private chats. +- Improve translations bar appearance. +- Improve userpic editor presets. + 4.6.1 (06.02.23) - Fix fonts fallback on Windows. From eee800b6d0c53b27ca17e97ea9d6ee0e838cd1bc Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Wed, 8 Feb 2023 01:04:13 +0400 Subject: [PATCH 15/21] Use window widget's devicePixelRatio when displaying interface scale value This is less confusing in multi-monitor environments --- Telegram/SourceFiles/settings/settings_main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/settings/settings_main.cpp b/Telegram/SourceFiles/settings/settings_main.cpp index b74536c4eda8b3..6b56648ee8b980 100644 --- a/Telegram/SourceFiles/settings/settings_main.cpp +++ b/Telegram/SourceFiles/settings/settings_main.cpp @@ -474,7 +474,8 @@ void SetupInterfaceScale( if constexpr (Platform::IsMac()) { return QString::number(scale) + '%'; } else { - return QString::number(scale * ratio) + '%'; + const auto ratio = window->widget()->devicePixelRatioF(); + return QString::number(int(scale * ratio)) + '%'; } }; label->setText(labelText(cEvalScale(scale))); From 9659cb5b6fc215aac7f01629812a8a61bea71bc3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 8 Feb 2023 12:55:57 +0400 Subject: [PATCH 16/21] Version 4.6.2: Fix build on macOS. --- Telegram/lib_base | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_base b/Telegram/lib_base index 0698cd1af30b09..a2eb8075c2218a 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit 0698cd1af30b09b66d5eee7dfa93c4d489086660 +Subproject commit a2eb8075c2218a3d28a249fc913269fc1d8db0f7 From 84b4ab1c3c5ea6582a4640bfee3ecccd8d68c98d Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 8 Feb 2023 13:19:52 +0400 Subject: [PATCH 17/21] Version 4.6.2: Re-enable global /LTCG on Windows. With /LTCGOUT: empty path it should not try generating .iobj file, and work on both 32 bit and 64 bit build with all static libs. --- Telegram/CMakeLists.txt | 2 +- Telegram/cmake/lib_ffmpeg.cmake | 2 +- Telegram/cmake/td_mtproto.cmake | 2 +- Telegram/cmake/td_scheme.cmake | 2 +- Telegram/cmake/td_ui.cmake | 4 ++-- Telegram/codegen | 2 +- Telegram/lib_base | 2 +- Telegram/lib_crl | 2 +- Telegram/lib_lottie | 2 +- Telegram/lib_ui | 2 +- Telegram/lib_webrtc | 2 +- cmake | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 923385559950a0..d647177bd1cf6d 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -5,7 +5,7 @@ # https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL add_executable(Telegram WIN32 MACOSX_BUNDLE) -init_non_host_target(Telegram ltcg) +init_non_host_target(Telegram) add_subdirectory(lib_rpl) add_subdirectory(lib_crl) diff --git a/Telegram/cmake/lib_ffmpeg.cmake b/Telegram/cmake/lib_ffmpeg.cmake index 7e982d9c00b7f7..7416fa873924e3 100644 --- a/Telegram/cmake/lib_ffmpeg.cmake +++ b/Telegram/cmake/lib_ffmpeg.cmake @@ -6,7 +6,7 @@ add_library(lib_ffmpeg OBJECT) add_library(desktop-app::lib_ffmpeg ALIAS lib_ffmpeg) -init_target(lib_ffmpeg ltcg) +init_target(lib_ffmpeg) nice_target_sources(lib_ffmpeg ${src_loc} PRIVATE diff --git a/Telegram/cmake/td_mtproto.cmake b/Telegram/cmake/td_mtproto.cmake index 65ad1b8a9a7790..79a0a3c76f5cd6 100644 --- a/Telegram/cmake/td_mtproto.cmake +++ b/Telegram/cmake/td_mtproto.cmake @@ -5,7 +5,7 @@ # https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL add_library(td_mtproto OBJECT) -init_non_host_target(td_mtproto ltcg) +init_non_host_target(td_mtproto) add_library(tdesktop::td_mtproto ALIAS td_mtproto) target_precompile_headers(td_mtproto PRIVATE ${src_loc}/mtproto/mtproto_pch.h) diff --git a/Telegram/cmake/td_scheme.cmake b/Telegram/cmake/td_scheme.cmake index 6818d24b0aaa35..20b956382bad72 100644 --- a/Telegram/cmake/td_scheme.cmake +++ b/Telegram/cmake/td_scheme.cmake @@ -5,7 +5,7 @@ # https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL add_library(td_scheme OBJECT) -init_non_host_target(td_scheme ltcg) +init_non_host_target(td_scheme) add_library(tdesktop::td_scheme ALIAS td_scheme) include(cmake/generate_scheme.cmake) diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index 4636aa044d66b6..2153d772195941 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -4,8 +4,8 @@ # For license and copyright information please follow this link: # https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -add_library(td_ui STATIC) -init_non_host_target(td_ui ltcg) +add_library(td_ui OBJECT) +init_non_host_target(td_ui) add_library(tdesktop::td_ui ALIAS td_ui) include(lib_ui/cmake/generate_styles.cmake) diff --git a/Telegram/codegen b/Telegram/codegen index 40d9bc8a644a5c..1a969faa0afb29 160000 --- a/Telegram/codegen +++ b/Telegram/codegen @@ -1 +1 @@ -Subproject commit 40d9bc8a644a5c51bb0790cf5f268e93a01fe59a +Subproject commit 1a969faa0afb29d53af03e530775eccdfb8433f1 diff --git a/Telegram/lib_base b/Telegram/lib_base index a2eb8075c2218a..33d846fc2d88f5 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit a2eb8075c2218a3d28a249fc913269fc1d8db0f7 +Subproject commit 33d846fc2d88f577d89e7d359cde3fa0a22899e4 diff --git a/Telegram/lib_crl b/Telegram/lib_crl index 53d1322171ddfc..3d7e1e1f1321c3 160000 --- a/Telegram/lib_crl +++ b/Telegram/lib_crl @@ -1 +1 @@ -Subproject commit 53d1322171ddfcfbce767cad1e2f0cb4efb91997 +Subproject commit 3d7e1e1f1321c3defd21c01882d674e485ecd8df diff --git a/Telegram/lib_lottie b/Telegram/lib_lottie index 99d1d946633819..3e9c2f1026e4b5 160000 --- a/Telegram/lib_lottie +++ b/Telegram/lib_lottie @@ -1 +1 @@ -Subproject commit 99d1d946633819e375f9ea5508a903d37fef7146 +Subproject commit 3e9c2f1026e4b5aa3202fca4cc67ece36c7cebb2 diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 1b9b1739649ddf..66e47a0240251b 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 1b9b1739649ddf942937a6b9a1a22933070e5ecc +Subproject commit 66e47a0240251b50242b2a68413645b61fa28a95 diff --git a/Telegram/lib_webrtc b/Telegram/lib_webrtc index 853b9d8cbe0246..b68a95ad4d1ae9 160000 --- a/Telegram/lib_webrtc +++ b/Telegram/lib_webrtc @@ -1 +1 @@ -Subproject commit 853b9d8cbe02460f405ce1d13beb3a5be46a007f +Subproject commit b68a95ad4d1ae9a1827671100a7fd76cbe448c3f diff --git a/cmake b/cmake index aa1e0d84b93e09..17951fb650e380 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit aa1e0d84b93e0901bb8db497e67276329ef9cc66 +Subproject commit 17951fb650e380364968ed4f208e589e2812be3d From ee1162faff799d79884a7097ab34061d40c941dc Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 8 Feb 2023 15:48:05 +0400 Subject: [PATCH 18/21] Version 4.6.2: Fix ghost drafts appearing in channels. --- Telegram/SourceFiles/history/history_widget.cpp | 11 +++++++---- Telegram/SourceFiles/history/history_widget.h | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 7b33b4538a27e4..975517ce29979e 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -1867,11 +1867,11 @@ void HistoryWidget::fastShowAtEnd(not_null history) { } } -void HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) { +bool HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) { InvokeQueued(this, [=] { updateStickersByEmoji(); }); if (_voiceRecordBar->isActive() || !_canSendTexts) { - return; + return false; } const auto editDraft = _history ? _history->localEditDraft({}) : nullptr; @@ -1893,7 +1893,7 @@ void HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) { updateControlsGeometry(); } refreshTopBarActiveChat(); - return; + return true; } _textUpdateEvents = 0; @@ -1930,6 +1930,7 @@ void HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) { _processingReplyId = draft ? draft->msgId : MsgId(); processReply(); } + return true; } void HistoryWidget::applyCloudDraft(History *history) { @@ -2261,7 +2262,9 @@ void HistoryWidget::showHistory( handlePeerUpdate(); session().local().readDraftsWithCursors(_history); - applyDraft(); + if (!applyDraft()) { + clearFieldText(); + } _send->finishAnimating(); updateControlsGeometry(); diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 5533ca0cc944c5..93683a8e2d9122 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -213,7 +213,7 @@ class HistoryWidget final void botCallbackSent(not_null item); void fastShowAtEnd(not_null history); - void applyDraft( + bool applyDraft( FieldHistoryAction fieldHistoryAction = FieldHistoryAction::Clear); void showHistory(const PeerId &peer, MsgId showAtMsgId, bool reload = false); void setChooseReportMessagesDetails( From c314e43a44bfcd960e3e30e603353aedce7d5345 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 8 Feb 2023 16:59:00 +0400 Subject: [PATCH 19/21] Fix sending / sent image icon in night-green theme. --- Telegram/Resources/night-green.tdesktop-theme | Bin 8595 -> 8826 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Telegram/Resources/night-green.tdesktop-theme b/Telegram/Resources/night-green.tdesktop-theme index 19daf6e2db8593e8438746570e628988bfccf67b..1647e4654e71b3cb14e81913f21b67eff2be0977 100644 GIT binary patch literal 8826 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 literal 8595 zcmZ{KgL5U|vt?`>H=fv<+?Y4EZF45MnV1vXwr$(CCYjjI#7<`C_jb4H)z-f5I^AER z`uqXkQI>;-L4x>?C0Y_GsTWo9N&b8P2WbCbYGP(*n8OdaoHl=1|MDA`j&vzlEwDWzjYgUoDHe#cLew3II$8yzluT@Wa{zE760; zPxzLDPf)WjpZ@buWnKb)i%aEgY~n=QA8>32Q(oEY@m1|ac&W->(OAr)616rHv)>Bj z!dFK-Wdc#mrLNA<%dRn9*q|z$heLf#IBST=-znJYpnu_&7B>se(37Vt&ZG59I?sOxN*XRK^+pgX@eaA^a|JB-ZNS#qyfG-JQ3l!l?M=go$or-)d+v@XEo?Atm@&;qq*z?>?dqahq zd{>Zh#A9qR%_PmIO)6C+PO>2_tpu;315y~vVwyn|r{w*N#5t|;@)-|=9A9&|J?g5A zwc-DYWNIona;kOF5VTA?iiH6Cwj)^G)bXDX&DOu$zezd6!hu!0Z`cRDg>G**j!6}l zPrW19f{Tmtz7dxRF@&kbiog5}XF^TMJoHUR4GbRei=QBB&D`gdHW1OZ!IsiYy0^4c zPEys0m<6h)^3Bcn`w3Op5oS34dcF_frt}zM>t;4msld}}fxkww3Poh{Cc>N5m zEjTI{%}1;920{lbpI+@I7MLJ{nUy1Og?=(va5P`0%uv5Y;=;dYV{s2UBaS=)0;N_o zn@^mW_6@Qg&T1DFRfs8{nrhHO^)$CL`fM*Ns1y8`msLD#*u$dhRD!B_uUjdu51)~e z0{UopX_mdp$DtvH|DXlwj1%PsetQ4hzH~M`qOw?k8QwvaV z;*4NCQ>91->JWK~d%`ci*w!B~$XqDK!bw=a(0CnN6wVWhm{Pj={+uHx3z?-M%N%i| z$CK85F{h3xtl5h{H_`N-Uy)m;_|^FY20%quj!HW+YC^55a;nU+>k>`8>wv6LFmHj3 zrJ?cj4>`6=NI86~T#Gj#)UMezLBi3iK`o655}JX+zj1YvlP z{b~TAyRU9*fCHpo^MyNQ1ND$G015 zeqizD4oDiE*nhI-CG{BlTzC{$p(%KP$RNQZyy%3ARwOhX1;g+|;qmU{$P=;dhhe{7 zDDL+T!|KHIm#0t?EolqSHW%-jR3w)}*aP+ytB+^T3k!9;MdNj0A#uE^!pLG}Udjo2 zwzBi$bVsQ|j;)7r_a}M6Qtjn3Fb@2-kUV!ePJ$9!F5-xM)F0B4-{>5^HK~{#XR_UT zOevK%D5aT$>g*D`Z$4o*3-a{O<<11Td%Ye#xblfRHClnkX@gu_`F{}Qvu4GidCj&hNFfj3X71XFiF zJxAP%%;KjPIG^?iwSK|!k+dnKEX53SK~&pVsluu3cZE)XzEbE7?kg&%MU(5L%deW+ zIBt_q)1ZMpT0ta2y-3w6n6eRtl$@3#YAh5OGGFz$m|o~a(>aQS1R1KLrOhV?^OeTx zS3!15UPPqrs__$tg;>@Z6<5i>^hYtxkDkSk2T-m(U~z{ANrDdwUb~>e4C&{-l|82S z^=o5)K`sI8@W^S&Q>Lh&v`EkvHn1TIRaT&Mj%PB-faC@jl5+dhT}>;c==gHU`BP*J z8mLCcUo}yGsM~l7;j-=JT~9Ze!3|b9=QSVo-r;cWh@r7`{16xDA1w=IZAy4b%J!}O zWe)y}03UrBsF$xIF?dTQ`0E_?ocO5UyDp%Lccz@O6uabCw|!hl;Ss7-ryxyN3W?jG zcBo`aHpRZ&w2g!)Eyj(*>)|u3CiC(qT>xQzEqN2}dBBP{C;q-~QnK?wITNHlR+uch zdcn4zh$RB544bhfNx#FiIBBgmCiElRm5S)c}Q?8`qQ4zuIaD&Pp_@ zoB(5j*ufMZ8rCB}tk*<}!Py{_Ml8}68i(jKCq+_+h99$hF$q-j6%dsi;%6yDJQ4eA zaALMHj=LIGd{O5P^tG8y;vHiGgM~G-yCD8H_WUf!u42yL-0n-)u~F49FTQ*9&e9g} zYS`5oWrgU2d1NblzNZ3@aWkm4&fQn5zb_Y;*ZxkirEaz0pqY@*p~fOh3}{w@oGR0y zP*y_D70NVukS0`ueHYYL7TpuEYl1Tx_|nmbm)qB1pFL!na*EKa7!37sKH&uM{k^*m z+A)60miojjLH9C9`W4YhHLZ7e0sm*}eLVt4VjpGUV|AwgK4Yy{ZxRe2m6U@osyUZ0 z9OQxNzgDRFVWT7(wi|pJawMSI%g$waAQdw-(5HW`*ng<}js8NDRb+XkTYj18zmb_`wJ?;kx>Zo>D@E&D9tu+Oa~n zs+N6BiT1{^1h-6wMPl#NvT^H`XvgmQ9#NPsBG=vE03+6POT|a8);;B=`*vFMcg?0z3DU38xsuM+_WZ1mTq8V0QNHDC-gWKn)V|I*Yv zJ|OSmg}b404FHijOdq`kM;3RCAx?H3_Twr{hhK3NKVkNc@IWAw+SYFwL9D~uaF=#l z=Ws{R?c@33%XJZMTN;*he*am(#n`q|M>D~A9FR9xzJWEkO*5yp=)=@|#MtFOjDwEh zPaI3GBH1m5U~<;gjbk?1AvlbKbIQ9Uo6IY;*d+yF2M9@-`KaD@dp6GQ31VY?pjC@Y zZpyA55mfA>ycY=(e?EdrU%m5a&8~OFAVCau!jb1l^6!_x+j0$l2 z5g*PFlMUy}A58o*^IbBf7UjpvH6R{`~~lpnanl@6{F2>b$h&Wv9)OV&A#VYjZU; z!{~ofMaSDs^CrlC-MsspCS!6m7l8%uKFp!7eMi1Yc?rn!i2BI6z;KzkvRjMTb<8Q} z-nQgUCqfJ*WJ!Cpc|V-xnZcT9&91+3+jjLY_v`=hd=HU`R2avm3uC*$JP_Xjh9oa zF8b0=pJHl>><&IW^R@$V|emBXF4PF97qL^aR`u$SMk^tMRy%KeixhbY2jat_Q0B9W)))wJ z25(|G`u-A>)?Xqpl^|5H`dhJeDGPt&{}H=|@n$`D5rM53(X)F-+4Xt6vtM1aC<5pTvupb} zL~F5+z(pd5WZENGF0)eS=j}ZnoykL&djy_}Zx+Gc_L|H#jjYIAL>Zryl9BEYc`o zMdVbJw4xhr-mE9Xt$6$jCQvcoEDVEelTB`}mTg!Yec2+9p>JfTX z(FO2Kes&ElyWDsZE*7|oD$r^u*kq9;xhK}SALh&35uwI7MhawAW>RYny~Gr^!wZ({ zu(6rWzEIwo&~%vd>_6E71;?Q?4zNhlmWfO;$t)w(oRx2<9qVtajWfjCtmf8UbtS8U zRt#VQ!bGen?^$R@;rI$l66e|Z@Jm)^RFxQsEBfd=Qegfz_GqH0*UCuuqi-THvD0LZ?@4%+%DzXzI}U(F=F9F#>#HQRa#wi1 ze&s~1<$rxmKDnrb3G61QeRy%Y`~Hcf5&goTukzazDkEd%8fR`!cZV3!mnQE{XAEl1>do<>42lL@yVWXTurV@nZf#7x{6?wiSz%(!qD4FGZi6vO zdeskg=-Nklw1cszjdg7cIE1`L55_)*k$eLn$OSyu!pCEZ1P=pIu{lPBolI+P%^Hj$ zu%uglH(auC8mC4+g343Zu2Slpzg}gq(jREo(X=g%aR;hPh=iGSIFBBq#-pS!1NCe& z1m|t3wro|+VoZ}2G~a{aD|Q^=gsZn0zEFL0Wq^mD1r=7H6GRapN^rLH7iXN-M0y-= zv9C=3@Gp*d(Z3Q@h>5-WN>eLUz&i2g<@6fn`^)HZK33>jiNKm$nsGGZ^0+SxbD8)- zBFcP!e9AIa5POx(l5CVs3gMZH_w_-Y!b%Pk$wXppWdoL=`3Tm^;af~xL4a8}39^-D zd8%{rmJ>6R!gI7L{ZrxmqD9X^xoRER76P}!>s&EuPER5S?9M4^pWA*zbYbCWfN8rI z!USa&C!g+1c`q@I<=)`V@6wv#&AbVCkcme^x6)q(U7%(gBpG%Gc^ zu?RlbIOh2&U5!_J{vl#Xt_C52CjmqK|79@=aKK#fpRvqfJ$aekiR*r@!xMvP| zv>5|mgpk}Qpvnbgvj}~{7QwPVk5LU5a19U_Nk`bG#DC@W0({7fxlaszL4+W~)XR52t z+MrJEonQ1O-yTSBQM8VQ@LOvEDI|y2_J+`~^&Pm0;hvGCeZ3;=Rbcj_y7J%hONOQX zlFPwyHO7?&?*dCbl-j(Yx>+9kR&Scp0H`n!IxxA2&)J zuaaeM&01r?n=IuISxTKgv4`iEDL$k@!}F32iIq+Tyn<&va(CODqo27EFkRUBc=%0}dCTxwUso~=HtYa%U z&Z$7HQ%{v6q)*nGXE0$Ietu-y@iY0Knk%ZSp;T27*i1$91WmSH&!Y!x7)n#b*W^~-cp0g103e-kAh z=MUFt)*N3sV10?P_Pk{)so1leFi8Kez2ABiX8B-~GnT zGg0~cx~KR2nu)2Tt%2AL$K29zr4&Bt-f#HXgSqJqx@E!m?lkm{K?95Toa~R|*WjWu z3@@6$J4!OcwmyRM(P@{y<_hMs3)(kVQ86A^^W4Uj4}$Ur3&lkVGhBcgQe&m)Wc~Rd z{->>cJ%s}kBg>lO!QP4##XkIkK0XL-?VyI8-)FQNB5`HxaEUT2HA=u{;dQIXmpZOp z1BE%tuz6j`{jF&SW`Y(9s}}9ll zL3(c*1-j|=%S1kwD6u1m3-fD5plskKkbZ;L1R3?6c6;IIy-7f^DGHdzZJiDpP;b|j zd8H-t_QL3;#a5p^eh$T0h8S5hZAqYZA+>N+#$b&V&2myJ11DC3%db+>cJq>#>|#i7~6*5ExF;?Q$ar9O~F-7=i%}Wipa$d zu2*bk@tkiv{h9MO!+dL1sF62Rvx4L<^;?#(rQ+=iA=8m^%#TA>GFf&0F=xSv-a8^a zan9QIP8P}n=ptR4iey@&*j49m2pNWc=iTy{#YqzSk(W5}_Xp;Bw+~*X6akHA6I+4i zy=ksvbMh%3_pSuK{V&KAPgjF*JKiHLH@q4j2@C|wqIpm^jk3{H@SU%{6Y`%tZ2_p^Eoy) ziXy>D%d9mG=r{JworGyUflA$GJjc0sI^iOE;JzPVDZIwhv476_DXhZo5_$F`o#}3p zw<9`$FZ#etK=<^q|6z3NV%p18b##LfpOn&E%=&tNuY#M8TGtzvDZB5`KG+)jxnNT$ zx3G*!>&DgHcH_^tp*bTP(pi&JT$EpS?#&`yDrS)p@!C(D0jy4Jk1;UYrO)J5jnJ~B z*ZN7fS~BwW$wJUw$0OG1nj}lb4wPV4qDR(rP4fh@iMKFcomjEm-@>rBDsVsX;t*Yl zJGy%8jirB*wCMu*k0pE8&⁡v_z=ysK#fIA*R27EQ#YLsYFcTL2ta_Sao~FI1Gq5 zv|4o{^Kyrs9@Hp+XJeGBe|FnM{E;x+vt5C!n=&MQQ=E${Zdcr$wvU|U&blCWm0UHc zdysduexuUA_Z|K~%C&1V{xh~qnF8$>YK63cKZ>LDD-dn$O(i#GNF8H55n+Ne<7)%f zr5lxTkWJ{dusW20x&sUVeh`Ik$a^oz={xTUqkso7`pF}D^;s8_WGS9hh46_CcWR*G ztSq|>tHWyJ<E7LW>%+#nbOkmgrKc=PN6d?kAYL#C7(><48Ufngk4 zI)3DK1P=kX^n{FWcV?^-F7gYmQd3SHlww-Lm*OQgh0P`;DVtr_uxE>VSU;P~cCkG7 z8DI>c5yw79<9b3@(j+^BISp!`ZehOR{!KeoUXL87xWy3c$X{piJR07PDbsqnqMz)3 ze@@mM(k{3rS(jGHIoUi5Oi++PSaPX)j=Rs3dvzm+Bxw@?- z{>?*Wg(snfx&HlzCglHcytx8qB@=fPHsf0-BV!RbiqSg12A|i`` zE?VW_Q{Wjg#@e5dOuz5j`8KqzVY%Q#Qof;rmEwTgn`w^@tx`Q_oDk(TvyEqcYU|=! zllWqfP>HOt(uGjV%tS*Q}=(? zk>-Hc)1vNCgFos8e!sL#@1_TW!}EMfS3~v3PRXkP=L|7zs{YBB=o+rpk%?r%u!!;^ z%b5`S!cm_q9usbqm|PglXop!#8PfBp6MI0#EVW^}^J_j7d4`&eWLQGU?HT&VE9UOi z_i-#gAQ=>Bo+^HVE--T4_@N From 7fa229537defb47df44193b56e38a551d6c09be9 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 8 Feb 2023 18:11:43 +0400 Subject: [PATCH 20/21] Version 4.6.2: Fix translating to fallback language. --- .../SourceFiles/history/view/history_view_translate_bar.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/history/view/history_view_translate_bar.cpp b/Telegram/SourceFiles/history/view/history_view_translate_bar.cpp index 9797a69d4dd07d..adf6b75e5776c5 100644 --- a/Telegram/SourceFiles/history/view/history_view_translate_bar.cpp +++ b/Telegram/SourceFiles/history/view/history_view_translate_bar.cpp @@ -288,9 +288,7 @@ void TranslateBar::setup(not_null history) { }, button->lifetime()); button->setClickedCallback([=] { - translateTo(history->translatedTo() - ? LanguageId() - : Core::App().settings().translateTo()); + translateTo(history->translatedTo() ? LanguageId() : _to.current()); }); const auto label = Ui::CreateChild( From 646bb2ff71169625f6dd4560299b6b1f3c8c82a6 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 9 Feb 2023 08:12:38 +0400 Subject: [PATCH 21/21] Upload sources-full first in release script. --- Telegram/build/release.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Telegram/build/release.py b/Telegram/build/release.py index 0cc07905e445e1..83429b191a6502 100644 --- a/Telegram/build/release.py +++ b/Telegram/build/release.py @@ -163,6 +163,12 @@ def prepareSources(): local_folder = local_folder + '/' files = [] +files.append({ + 'local': 'sources', + 'remote': 'tdesktop-' + version + '-full.tar.gz', + 'mime': 'application/x-gzip', + 'label': 'Source code (tar.gz, full)', +}) files.append({ 'local': 'tsetup.' + version_full + '.exe', 'remote': 'tsetup.' + version_full + '.exe', @@ -205,12 +211,6 @@ def prepareSources(): 'mime': 'application/octet-stream', 'label': 'Linux 64 bit: Binary', }) -files.append({ - 'local': 'sources', - 'remote': 'tdesktop-' + version + '-full.tar.gz', - 'mime': 'application/x-gzip', - 'label': 'Source code (tar.gz, full)', -}) r = requests.get(url + 'repos/telegramdesktop/tdesktop/releases/tags/v' + version) if r.status_code == 404: