diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 66149efe6..30d8ff596 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2533,4 +2533,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "ktg_settings_monospace_large_bubbles" = "Expand bubbles with monospace"; +"ktg_bot_id_copied" = "Bot ID copied to clipboard."; +"ktg_user_id_copied" = "User ID copied to clipboard."; +"ktg_group_id_copied" = "Group ID copied to clipboard."; +"ktg_supergroup_id_copied" = "Supergroup ID copied to clipboard."; +"ktg_channel_id_copied" = "Channel ID copied to clipboard."; + +"ktg_phone_copied" = "Phone copied to clipboard."; +"ktg_mention_copied" = "Username copied to clipboard."; + // Keys finished diff --git a/Telegram/Resources/langs/rewrites/ru.json b/Telegram/Resources/langs/rewrites/ru.json index a15fd6e13..4d3a3231b 100644 --- a/Telegram/Resources/langs/rewrites/ru.json +++ b/Telegram/Resources/langs/rewrites/ru.json @@ -132,5 +132,12 @@ "many": "{count} дней", "other": "{count} дней" }, - "ktg_settings_monospace_large_bubbles": "Расширять моноширинные сообщения" + "ktg_settings_monospace_large_bubbles": "Расширять моноширинные сообщения", + "ktg_bot_id_copied": "ID бота скопирован.", + "ktg_user_id_copied": "ID пользователя скопирован.", + "ktg_group_id_copied": "ID группы скопирован.", + "ktg_supergroup_id_copied": "ID супергруппы скопирован.", + "ktg_channel_id_copied": "ID канала скопирован.", + "ktg_phone_copied": "Номер телефона скопирован.", + "ktg_mention_copied": "Имя пользователя скопировано." } diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 4fd8dfac2..7b6089319 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -376,6 +376,12 @@ infoLabeledOneLine: FlatLabel(defaultFlatLabel) { lineHeight: 19px; } } +infoLabeledOneLineInline: FlatLabel(infoLabeledOneLine) { + palette: TextPalette(defaultTextPalette) { + linkFg: windowFg; + } +} + infoLabelSkip: 2px; infoLabel: FlatLabel(infoLabeledOneLine) { textFg: windowSubTextFg; diff --git a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp index f55d40496..a0372a173 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp @@ -48,6 +48,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "facades.h" #include "styles/style_info.h" #include "styles/style_boxes.h" +#include "app.h" #include #include @@ -255,19 +256,41 @@ object_ptr DetailsFiller::setupInfo() { result->setContextCopyText(contextCopyText); return result; }; + auto addInfoOneLineInline = [&]( + rpl::producer &&label, + rpl::producer &&text, + const QString &contextCopyText) { + auto result = addInfoLine( + std::move(label), + std::move(text), + st::infoLabeledOneLineInline); + result->setContextCopyText(contextCopyText); + return result; + }; if (const auto user = _peer->asUser()) { if (cShowChatId() != 0) { - if (user->isBot()) { - addInfoOneLine( - tr::ktg_profile_bot_id(), - IDValue(user), - tr::ktg_profile_copy_id(tr::now)); - } else { - addInfoOneLine( - tr::ktg_profile_user_id(), - IDValue(user), - tr::ktg_profile_copy_id(tr::now)); - } + auto idDrawableText = IDValue( + user + ) | rpl::map([](TextWithEntities &&text) { + return Ui::Text::Link(text.text); + }); + auto idInfo = addInfoOneLineInline( + (user->isBot() + ? tr::ktg_profile_bot_id() + : tr::ktg_profile_user_id()), + std::move(idDrawableText), + tr::ktg_profile_copy_id(tr::now)); + + idInfo->setClickHandlerFilter([user](auto&&...) { + const auto idText = IDString(user); + if (!idText.isEmpty()) { + QGuiApplication::clipboard()->setText(idText); + Ui::Toast::Show(user->isBot() + ? tr::ktg_bot_id_copied(tr::now) + : tr::ktg_user_id_copied(tr::now)); + } + return false; + }); } if (user->session().supportMode()) { @@ -275,21 +298,64 @@ object_ptr DetailsFiller::setupInfo() { user->session().supportHelper().infoLabelValue(user), user->session().supportHelper().infoTextValue(user)); } + + auto phoneDrawableText = rpl::combine( + PhoneValue(user), + UsernameValue(user), + AboutValue(user), + tr::lng_info_mobile_hidden() + ) | rpl::map([]( + const TextWithEntities &phone, + const TextWithEntities &username, + const TextWithEntities &bio, + const QString &hidden) { + return (phone.text.isEmpty() && username.text.isEmpty() && bio.text.isEmpty()) + ? Ui::Text::WithEntities(hidden) + : Ui::Text::Link(phone.text); + }); - addInfoOneLine( + auto phoneInfo = addInfoOneLineInline( tr::lng_info_mobile_label(), - PhoneOrHiddenValue(user), + std::move(phoneDrawableText), tr::lng_profile_copy_phone(tr::now)); + + phoneInfo->setClickHandlerFilter([user](auto&&...) { + const auto phoneText = user->phone(); + if (!phoneText.isEmpty()) { + QGuiApplication::clipboard()->setText(App::formatPhone(phoneText)); + Ui::Toast::Show(tr::ktg_phone_copied(tr::now)); + } + return false; + }); + if (user->isBot()) { addInfoLine(tr::lng_info_about_label(), AboutValue(user)); } else { addInfoLine(tr::lng_info_bio_label(), AboutValue(user)); } - addInfoOneLine( + + auto usernameDrawableText = UsernameValue( + user + ) | rpl::map([](TextWithEntities &&username) { + return username.text.isEmpty() + ? TextWithEntities() + : Ui::Text::Link(username.text); + }); + + auto usernameInfo = addInfoOneLineInline( tr::lng_info_username_label(), - UsernameValue(user), + std::move(usernameDrawableText), tr::lng_context_copy_mention(tr::now)); + usernameInfo->setClickHandlerFilter([user](auto&&...) { + const auto usernameText = user->userName(); + if (!usernameText.isEmpty()) { + QGuiApplication::clipboard()->setText('@' + usernameText); + Ui::Toast::Show(tr::ktg_mention_copied(tr::now)); + } + return false; + }); + const auto window = &_controller->parentController()->window(); AddMainButton( result, @@ -299,22 +365,32 @@ object_ptr DetailsFiller::setupInfo() { tracker); } else { if (cShowChatId() != 0) { - if (_peer->isChat()) { - addInfoOneLine( - tr::ktg_profile_group_id(), - IDValue(_peer), - tr::ktg_profile_copy_id(tr::now)); - } else if (_peer->isMegagroup()) { - addInfoOneLine( - tr::ktg_profile_supergroup_id(), - IDValue(_peer), - tr::ktg_profile_copy_id(tr::now)); - } else { - addInfoOneLine( - tr::ktg_profile_channel_id(), - IDValue(_peer), - tr::ktg_profile_copy_id(tr::now)); - } + auto idDrawableText = IDValue( + _peer + ) | rpl::map([](TextWithEntities &&text) { + return Ui::Text::Link(text.text); + }); + auto idInfo = addInfoOneLineInline( + (_peer->isChat() + ? tr::ktg_profile_group_id() + : _peer->isMegagroup() + ? tr::ktg_profile_supergroup_id() + : tr::ktg_profile_channel_id()), + std::move(idDrawableText), + tr::ktg_profile_copy_id(tr::now)); + + idInfo->setClickHandlerFilter([peer = _peer](auto&&...) { + const auto idText = IDString(peer); + if (!idText.isEmpty()) { + QGuiApplication::clipboard()->setText(idText); + Ui::Toast::Show(peer->isChat() + ? tr::ktg_group_id_copied(tr::now) + : peer->isMegagroup() + ? tr::ktg_supergroup_id_copied(tr::now) + : tr::ktg_channel_id_copied(tr::now)); + } + return false; + }); } auto linkText = LinkValue( diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.cpp b/Telegram/SourceFiles/info/profile/info_profile_values.cpp index 0b91a1d4f..0dd98d344 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_values.cpp @@ -47,7 +47,7 @@ auto PlainUsernameValue(not_null peer) { } // namespace -rpl::producer IDValue(not_null peer) { +QString IDString(not_null peer) { auto resultId = QString::number(peer->bareId()); if (cShowChatId() == 2) { @@ -58,7 +58,11 @@ rpl::producer IDValue(not_null peer) { } } - return rpl::single(resultId) | Ui::Text::ToWithEntities(); + return resultId; +} + +rpl::producer IDValue(not_null peer) { + return rpl::single(IDString(peer)) | Ui::Text::ToWithEntities(); } rpl::producer NameValue(not_null peer) { @@ -79,39 +83,6 @@ rpl::producer PhoneValue(not_null user) { }) | Ui::Text::ToWithEntities(); } -rpl::producer PhoneOrHiddenValue(not_null user) { - return rpl::combine( - PhoneValue(user), - PlainUsernameValue(user), - PlainBioValue(user), - tr::lng_info_mobile_hidden() - ) | rpl::map([]( - const TextWithEntities &phone, - const QString &username, - const QString &bio, - const QString &hidden) { - return (phone.text.isEmpty() && username.isEmpty() && bio.isEmpty()) - ? Ui::Text::WithEntities(hidden) - : phone; - }); -} - -rpl::producer BioValue(not_null user) { - return PlainBioValue(user) - | ToSingleLine() - | Ui::Text::ToWithEntities(); -} - -rpl::producer UsernameValue(not_null user) { - return PlainUsernameValue( - user - ) | rpl::map([](QString &&username) { - return username.isEmpty() - ? QString() - : ('@' + username); - }) | Ui::Text::ToWithEntities(); -} - rpl::producer PlainAboutValue(not_null peer) { /* if (const auto user = peer->asUser()) { @@ -142,6 +113,39 @@ rpl::producer AboutValue(not_null peer) { }); } +rpl::producer PhoneOrHiddenValue(not_null user) { + return rpl::combine( + PhoneValue(user), + PlainUsernameValue(user), + PlainAboutValue(user), + tr::lng_info_mobile_hidden() + ) | rpl::map([]( + const TextWithEntities &phone, + const QString &username, + const QString &bio, + const QString &hidden) { + return (phone.text.isEmpty() && username.isEmpty() && bio.isEmpty()) + ? Ui::Text::WithEntities(hidden) + : phone; + }); +} + +rpl::producer BioValue(not_null user) { + return PlainBioValue(user) + | ToSingleLine() + | Ui::Text::ToWithEntities(); +} + +rpl::producer UsernameValue(not_null user) { + return PlainUsernameValue( + user + ) | rpl::map([](QString &&username) { + return username.isEmpty() + ? QString() + : ('@' + username); + }) | Ui::Text::ToWithEntities(); +} + rpl::producer LinkValue(not_null peer) { return PlainUsernameValue( peer diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.h b/Telegram/SourceFiles/info/profile/info_profile_values.h index 9f60604a5..5b1f30331 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.h +++ b/Telegram/SourceFiles/info/profile/info_profile_values.h @@ -35,6 +35,8 @@ inline auto ToSingleLine() { rpl::producer> MigratedOrMeValue( not_null peer); +QString IDString(not_null peer); + rpl::producer IDValue(not_null peer); rpl::producer NameValue(not_null peer); rpl::producer PhoneValue(not_null user);