diff --git a/Telegram/Resources/animations/hello_status.tgs b/Telegram/Resources/animations/hello_status.tgs new file mode 100644 index 000000000..b48182c2c Binary files /dev/null and b/Telegram/Resources/animations/hello_status.tgs differ diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 23b7ae0c9..9c0aa15d6 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1461,6 +1461,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_profile_open_app" = "Open App"; "lng_profile_open_app_about" = "By launching this mini app, you agree to the {terms}."; "lng_profile_open_app_terms" = "Terms of Service for Mini Apps"; +"lng_profile_bot_permissions_title" = "Allow access to"; +"lng_profile_bot_emoji_status_access" = "Emoji Status"; "lng_info_add_as_contact" = "Add to contacts"; "lng_profile_shared_media" = "Shared media"; "lng_profile_suggest_photo" = "Suggest Profile Photo"; @@ -3421,6 +3423,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_bot_emoji_status_confirm" = "Confirm"; "lng_bot_emoji_status_title" = "Set Emoji Status"; "lng_bot_emoji_status_text" = "Do you want to set this emoji status suggested by {bot}?"; +"lng_bot_emoji_status_access_text" = "{bot} requests access to set your **emoji status**. You will be able to revoke this access in the profile page of {name}."; +"lng_bot_emoji_status_access_allow" = "Allow"; "lng_bot_status_users#one" = "{count} monthly user"; "lng_bot_status_users#other" = "{count} monthly users"; diff --git a/Telegram/Resources/qrc/telegram/animations.qrc b/Telegram/Resources/qrc/telegram/animations.qrc index ac6ff1dbf..f60061f99 100644 --- a/Telegram/Resources/qrc/telegram/animations.qrc +++ b/Telegram/Resources/qrc/telegram/animations.qrc @@ -28,6 +28,7 @@ ../../animations/collectible_phone.tgs ../../animations/search.tgs ../../animations/noresults.tgs + ../../animations/hello_status.tgs ../../animations/dice/dice_idle.tgs ../../animations/dice/dart_idle.tgs diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index af5f95077..c14d29bfc 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -745,6 +745,14 @@ bool DocumentData::emojiUsesTextColor() const { return (_flags & Flag::UseTextColor); } +void DocumentData::overrideEmojiUsesTextColor(bool value) { + if (value) { + _flags |= Flag::UseTextColor; + } else { + _flags &= ~Flag::UseTextColor; + } +} + bool DocumentData::hasThumbnail() const { return _thumbnail.location.valid() && !thumbnailFailed() diff --git a/Telegram/SourceFiles/data/data_document.h b/Telegram/SourceFiles/data/data_document.h index df1f9eab3..6aa21caa9 100644 --- a/Telegram/SourceFiles/data/data_document.h +++ b/Telegram/SourceFiles/data/data_document.h @@ -206,6 +206,7 @@ public: [[nodiscard]] bool isPremiumSticker() const; [[nodiscard]] bool isPremiumEmoji() const; [[nodiscard]] bool emojiUsesTextColor() const; + void overrideEmojiUsesTextColor(bool value); [[nodiscard]] bool hasThumbnail() const; [[nodiscard]] bool thumbnailLoading() const; diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 2456567d3..7433dbd9a 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -580,6 +580,9 @@ void ApplyUserUpdate(not_null user, const MTPDuserFull &update) { } else { user->setBotInfoVersion(-1); } + if (const auto info = user->botInfo.get()) { + info->canManageEmojiStatus = update.is_bot_can_manage_emoji_status(); + } if (const auto pinned = update.vpinned_msg_id()) { SetTopPinnedMessageId(user, pinned->v); } diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 41e33a6b0..2753b0dc0 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -2435,6 +2435,14 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (const auto sticker = emojiStickers->stickerForEmoji( isolated)) { addDocumentActions(sticker.document, item); + } else if (v::is(isolated.items.front()) + && v::is_null(isolated.items[1])) { + const auto id = v::get(isolated.items.front()); + const auto docId = id.toULongLong(); + const auto document = session->data().document(docId); + if (document->sticker()) { + addDocumentActions(document, item); + } } } } diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index ae7037a2c..05796c024 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -470,6 +470,7 @@ infoIconMediaSaved: icon {{ "info/info_media_saved", infoIconFg }}; infoIconMediaStoriesArchive: icon {{ "info/info_stories_archive", infoIconFg }}; infoIconMediaStoriesRecent: icon {{ "info/info_stories_recent", infoIconFg }}; infoIconMediaGifts: icon {{ "menu/gift_premium", infoIconFg, point(4px, 4px) }}; +infoIconEmojiStatusAccess: icon {{ "menu/read_reactions", infoIconFg, point(4px, 4px) }}; infoIconShare: icon {{ "info/info_share", infoIconFg }}; infoIconEdit: icon {{ "info/info_edit", infoIconFg }}; diff --git a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp index 40611897a..7ade4a32e 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp @@ -958,6 +958,7 @@ private: object_ptr setupInfo(); object_ptr setupMuteToggle(); void setupMainApp(); + void setupBotPermissions(); void setupMainButtons(); Ui::MultiSlideTracker fillTopicButtons(); Ui::MultiSlideTracker fillUserButtons( @@ -1865,6 +1866,37 @@ void DetailsFiller::setupMainApp() { Ui::AddSkip(_wrap); } +void DetailsFiller::setupBotPermissions() { + AddSkip(_wrap); + AddSubsectionTitle(_wrap, tr::lng_profile_bot_permissions_title()); + const auto emoji = _wrap->add( + object_ptr( + _wrap, + tr::lng_profile_bot_emoji_status_access(), + st::infoSharedMediaButton)); + object_ptr( + emoji, + st::infoIconEmojiStatusAccess, + st::infoSharedMediaButtonIconPosition); + + const auto user = _peer->asUser(); + emoji->toggleOn( + rpl::single(bool(user->botInfo->canManageEmojiStatus)) + )->toggledValue() | rpl::filter([=](bool allowed) { + return allowed != user->botInfo->canManageEmojiStatus; + }) | rpl::start_with_next([=](bool allowed) { + user->botInfo->canManageEmojiStatus = allowed; + const auto session = &user->session(); + session->api().request(MTPbots_ToggleUserEmojiStatusPermission( + user->inputUser, + MTP_bool(allowed) + )).send(); + }, emoji->lifetime()); + AddSkip(_wrap); + AddDivider(_wrap); + AddSkip(_wrap); +} + void DetailsFiller::setupMainButtons() { auto wrapButtons = [=](auto &&callback) { auto topSkip = _wrap->add(CreateSlideSkipWidget(_wrap)); @@ -2059,6 +2091,9 @@ object_ptr DetailsFiller::fill() { if (info->hasMainApp) { setupMainApp(); } + if (info->canManageEmojiStatus) { + setupBotPermissions(); + } } } if (!_peer->isSelf()) { diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp index f7ff549c2..e1858aa95 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/timer_rpl.h" #include "boxes/peer_list_controllers.h" #include "boxes/share_box.h" +#include "chat_helpers/stickers_lottie.h" #include "chat_helpers/tabbed_panel.h" #include "core/application.h" #include "core/click_handler_types.h" @@ -447,11 +448,21 @@ std::unique_ptr MakeEmojiSetStatusPreview( raw, rpl::single(peer->name()), st::botEmojiStatusName); - auto emojiText = TextWithEntities(); + const auto makeContext = [=](Fn update) { + return Core::MarkedTextContext{ + .session = &peer->session(), + .customEmojiRepaint = update, + }; + }; const auto emoji = raw->lifetime().make_state( raw, - rpl::single(emojiText), - st::botEmojiStatusName); + rpl::single( + Ui::Text::SingleCustomEmoji( + Data::SerializeCustomEmojiId(document->id), + document->sticker() ? document->sticker()->alt : QString())), + st::botEmojiStatusEmoji, + st::defaultPopupMenu, + makeContext); const auto userpic = raw->lifetime().make_state( raw, peer, @@ -496,6 +507,59 @@ std::unique_ptr MakeEmojiSetStatusPreview( return result; } +void ConfirmEmojiStatusAccessBox( + not_null box, + not_null bot, + Fn done) { + box->setNoContentMargin(true); + + const auto set = box->lifetime().make_state(); + + box->addTopButton(st::boxTitleClose, [=] { + box->closeBox(); + }); + + AddSkip(box->verticalLayout(), 4 * st::defaultVerticalListSkip); + + const auto statusIcon = ChatHelpers::GenerateLocalTgsSticker( + &bot->session(), + u"hello_status"_q); + statusIcon->overrideEmojiUsesTextColor(true); + + auto ownedSet = MakeEmojiSetStatusPreview( + box, + bot->session().user(), + statusIcon); + box->addRow( + object_ptr::fromRaw(ownedSet.release())); + + AddSkip(box->verticalLayout(), 2 * st::defaultVerticalListSkip); + + auto name = Ui::Text::Bold(bot->name()); + box->addRow(object_ptr( + box, + tr::lng_bot_emoji_status_access_text( + lt_bot, + rpl::single(name), + lt_name, + rpl::single(name), + Ui::Text::RichLangValue), + st::botEmojiStatusText)); + + box->addButton(tr::lng_bot_emoji_status_access_allow(), [=] { + *set = true; + box->closeBox(); + done(true); + }); + box->addButton(tr::lng_cancel(), [=] { + const auto was = *set; + box->closeBox(); + if (!was) { + done(false); + } + }); +} + void ConfirmEmojiStatusBox( not_null box, not_null bot, @@ -511,6 +575,10 @@ void ConfirmEmojiStatusBox( const auto set = box->lifetime().make_state(); + box->addTopButton(st::boxTitleClose, [=] { + box->closeBox(); + }); + box->addRow(object_ptr( box, tr::lng_bot_emoji_status_title(), @@ -525,7 +593,7 @@ void ConfirmEmojiStatusBox( Ui::Text::RichLangValue), st::botEmojiStatusText)); - AddSkip(box->verticalLayout()); + AddSkip(box->verticalLayout(), 2 * st::defaultVerticalListSkip); auto ownedSet = MakeEmojiSetStatusPreview( box, @@ -1600,6 +1668,33 @@ void WebViewInstance::botAllowWriteAccess(Fn callback) { }).send(); } +void WebViewInstance::botRequestEmojiStatusAccess( + Fn callback) { + if (_bot->botInfo->canManageEmojiStatus) { + callback(true); + } else if (const auto panel = _panel.get()) { + const auto bot = _bot; + panel->showBox(Box(ConfirmEmojiStatusAccessBox, bot, [=](bool ok) { + if (!ok) { + callback(false); + return; + } + const auto session = &bot->session(); + bot->botInfo->canManageEmojiStatus = true; + session->api().request(MTPbots_ToggleUserEmojiStatusPermission( + bot->inputUser, + MTP_bool(true) + )).done([=] { + callback(true); + }).fail([=] { + callback(false); + }).send(); + })); + } else { + callback(false); + } +} + void WebViewInstance::botSharePhone(Fn callback) { const auto history = _bot->owner().history(_bot); if (_bot->isBlocked()) { diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h index b3ce5cbf5..0d7ae73d4 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h @@ -260,6 +260,8 @@ private: QString query) override; void botCheckWriteAccess(Fn callback) override; void botAllowWriteAccess(Fn callback) override; + void botRequestEmojiStatusAccess( + Fn callback) override; void botSharePhone(Fn callback) override; void botInvokeCustomMethod( Ui::BotWebView::CustomMethodRequest request) override; diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp index 172420fd8..b14e9aee1 100644 --- a/Telegram/SourceFiles/settings/settings_premium.cpp +++ b/Telegram/SourceFiles/settings/settings_premium.cpp @@ -774,7 +774,7 @@ void TopBarUser::updateTitle( { EntityType::CustomEmoji, 0, 1, entityEmojiData }, Ui::Text::Link(text, linkIndex).entities.front(), }; - auto title = (setId == coloredId) + auto title = (setId != coloredId) ? tr::lng_premium_emoji_status_title_colored( tr::now, lt_user, diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp index 62e9191e1..da2a8a5b8 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp @@ -826,6 +826,8 @@ bool Panel::createWebview(const Webview::ThemeParams ¶ms) { processBottomBarColor(arguments); } else if (command == "web_app_set_emoji_status") { processEmojiStatusRequest(arguments); + } else if (command == "web_app_request_emoji_status_access") { + processEmojiStatusAccessRequest(); } else if (command == "share_score") { _delegate->botHandleMenuButton(MenuButton::ShareGame); } @@ -1000,6 +1002,15 @@ void Panel::processEmojiStatusRequest(const QJsonObject &args) { }); } +void Panel::processEmojiStatusAccessRequest() { + auto callback = crl::guard(this, [=](bool allowed) { + postEvent("emoji_status_access_requested", allowed + ? "{ status: \"allowed\" }" + : "{ status: \"cancelled\" }"); + }); + _delegate->botRequestEmojiStatusAccess(std::move(callback)); +} + void Panel::openTgLink(const QJsonObject &args) { if (args.isEmpty()) { LOG(("BotWebView Error: Bad arguments in 'web_app_open_tg_link'.")); diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h index 7ea2be451..97da49a60 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h @@ -71,6 +71,8 @@ public: QString query) = 0; virtual void botCheckWriteAccess(Fn callback) = 0; virtual void botAllowWriteAccess(Fn callback) = 0; + virtual void botRequestEmojiStatusAccess( + Fn callback) = 0; virtual void botSharePhone(Fn callback) = 0; virtual void botInvokeCustomMethod(CustomMethodRequest request) = 0; virtual void botSetEmojiStatus(SetEmojiStatusRequest request) = 0; diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index eb47373c5..d68c1c933 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -1178,3 +1178,6 @@ botEmojiStatusUserpic: UserpicButton(defaultUserpicButton) { } botEmojiStatusName: FlatLabel(defaultFlatLabel) { } +botEmojiStatusEmoji: FlatLabel(botEmojiStatusName) { + textFg: profileVerifiedCheckBg; +}