From 0f0e14132a68ac1d4d30234163597218a7af2bb0 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 30 Jun 2025 14:53:54 +0300 Subject: [PATCH] Added ability to copy and paste proxy list with shortcuts. --- Telegram/SourceFiles/boxes/connection_box.cpp | 156 ++++++++++-------- 1 file changed, 88 insertions(+), 68 deletions(-) diff --git a/Telegram/SourceFiles/boxes/connection_box.cpp b/Telegram/SourceFiles/boxes/connection_box.cpp index cd4285b262..e5833d5f85 100644 --- a/Telegram/SourceFiles/boxes/connection_box.cpp +++ b/Telegram/SourceFiles/boxes/connection_box.cpp @@ -100,6 +100,80 @@ using ProxyData = MTP::ProxyData; return proxy; }; +void AddProxyFromClipboard( + not_null controller, + std::shared_ptr show) { + const auto proxyString = u"proxy"_q; + const auto socksString = u"socks"_q; + const auto protocol = u"tg://"_q; + + const auto maybeUrls = ExtractUrlsSimple( + QGuiApplication::clipboard()->text()); + const auto isSingle = maybeUrls.size() == 1; + + const auto proceedUrl = [=](const auto &local) { + const auto command = base::StringViewMid( + local, + protocol.size(), + 8192); + + if (local.startsWith(protocol + proxyString) + || local.startsWith(protocol + socksString)) { + + using namespace qthelp; + const auto options = RegExOption::CaseInsensitive; + for (const auto &[expression, _] : Core::LocalUrlHandlers()) { + const auto midExpression = base::StringViewMid( + expression, + 1); + const auto isSocks = midExpression.startsWith( + socksString); + if (!midExpression.startsWith(proxyString) + && !isSocks) { + continue; + } + const auto match = regex_match( + expression, + command, + options); + if (!match) { + continue; + } + const auto type = isSocks + ? ProxyData::Type::Socks5 + : ProxyData::Type::Mtproto; + const auto fields = url_parse_params( + match->captured(1), + qthelp::UrlParamNameTransform::ToLower); + const auto proxy = ProxyDataFromFields(type, fields); + const auto contains = controller->contains(proxy); + const auto toast = (contains + ? tr::lng_proxy_add_from_clipboard_existing_toast + : tr::lng_proxy_add_from_clipboard_good_toast)(tr::now); + if (isSingle) { + show->showToast(toast); + } + if (!contains) { + controller->addNewItem(proxy); + } + break; + } + return true; + } + return false; + }; + + auto success = false; + for (const auto &maybeUrl : maybeUrls) { + success |= proceedUrl(Core::TryConvertUrlToLocal(maybeUrl)); + } + + if (!success) { + show->showToast( + tr::lng_proxy_add_from_clipboard_failed_toast(tr::now)); + } +} + class HostInput : public Ui::MaskedInputField { public: HostInput( @@ -254,6 +328,7 @@ public: protected: void prepare() override; + void keyPressEvent(QKeyEvent *e) override; private: void setupContent(); @@ -656,6 +731,18 @@ ProxiesBox::ProxiesBox( }, lifetime()); } +void ProxiesBox::keyPressEvent(QKeyEvent *e) { + if (e->key() == Qt::Key_Copy + || (e->key() == Qt::Key_C && e->modifiers() == Qt::ControlModifier)) { + _controller->shareItems(); + } else if (e->key() == Qt::Key_Paste + || (e->key() == Qt::Key_V && e->modifiers() == Qt::ControlModifier)) { + AddProxyFromClipboard(_controller, uiShow()); + } else { + BoxContent::keyPressEvent(e); + } +} + void ProxiesBox::prepare() { setTitle(tr::lng_proxy_settings()); @@ -671,73 +758,6 @@ void ProxiesBox::setupTopButton() { const auto menu = top->lifetime().make_state>(); - const auto proxyString = u"proxy"_q; - const auto socksString = u"socks"_q; - const auto protocol = u"tg://"_q; - - const auto proceedUrl = [=](const auto &local) { - const auto command = base::StringViewMid( - local, - protocol.size(), - 8192); - - if (local.startsWith(protocol + proxyString) - || local.startsWith(protocol + socksString)) { - - using namespace qthelp; - const auto options = RegExOption::CaseInsensitive; - for (const auto &[expression, _] : Core::LocalUrlHandlers()) { - const auto midExpression = base::StringViewMid( - expression, - 1); - const auto isSocks = midExpression.startsWith( - socksString); - if (!midExpression.startsWith(proxyString) - && !isSocks) { - continue; - } - const auto match = regex_match( - expression, - command, - options); - if (!match) { - continue; - } - const auto type = isSocks - ? ProxyData::Type::Socks5 - : ProxyData::Type::Mtproto; - const auto fields = url_parse_params( - match->captured(1), - qthelp::UrlParamNameTransform::ToLower); - const auto proxy = ProxyDataFromFields(type, fields); - const auto contains = _controller->contains(proxy); - const auto toast = (contains - ? tr::lng_proxy_add_from_clipboard_existing_toast - : tr::lng_proxy_add_from_clipboard_good_toast)(tr::now); - uiShow()->showToast(toast); - if (!contains) { - _controller->addNewItem(proxy); - } - break; - } - return true; - } - return false; - }; - - const auto callback = [=] { - const auto maybeUrls = ExtractUrlsSimple( - QGuiApplication::clipboard()->text()); - auto success = false; - for (const auto &maybeUrl : maybeUrls) { - success |= proceedUrl(Core::TryConvertUrlToLocal(maybeUrl)); - } - - if (!success) { - uiShow()->showToast( - tr::lng_proxy_add_from_clipboard_failed_toast(tr::now)); - } - }; top->setClickedCallback([=] { *menu = base::make_unique_q( top, @@ -745,7 +765,7 @@ void ProxiesBox::setupTopButton() { const auto addAction = Ui::Menu::CreateAddActionCallback(*menu); addAction({ .text = tr::lng_proxy_add_from_clipboard(tr::now), - .handler = callback, + .handler = [=] { AddProxyFromClipboard(_controller, uiShow()); }, .icon = &st::menuIconImportTheme, }); addAction({