Implement simple bot verification management.

This commit is contained in:
John Preston 2024-12-20 18:08:00 +04:00
parent b61e3b580d
commit 0d821c3630
16 changed files with 565 additions and 41 deletions

View file

@ -246,6 +246,8 @@ PRIVATE
boxes/peers/prepare_short_info_box.h
boxes/peers/replace_boost_box.cpp
boxes/peers/replace_boost_box.h
boxes/peers/verify_peers_box.cpp
boxes/peers/verify_peers_box.h
boxes/about_box.cpp
boxes/about_box.h
boxes/about_sponsored_box.cpp

View file

@ -1628,11 +1628,50 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_manage_peer_bot_star_ref" = "Affiliate Program";
"lng_manage_peer_bot_star_ref_off" = "Off";
"lng_manage_peer_bot_star_ref_about" = "Share a link to {bot} with your friends and earn {amount} of their spending there.";
"lng_manage_peer_bot_verify" = "Verify Accounts";
"lng_manage_peer_bot_edit_intro" = "Edit Intro";
"lng_manage_peer_bot_edit_commands" = "Edit Commands";
"lng_manage_peer_bot_edit_settings" = "Change Bot Settings";
"lng_manage_peer_bot_about" = "Use {bot} to manage this bot.";
"lng_bot_verify_title" = "Choose Chat to Verify";
"lng_bot_verify_bot_title" = "Verify Bot";
"lng_bot_verify_bot_text" = "Do you want to verify {name} with your verification mark and description?";
"lng_bot_verify_bot_about" = "You can customize your description for each bot.";
"lng_bot_verify_bot_submit" = "Verify Bot";
"lng_bot_verify_bot_sent" = "{name} has been notified and will receive your verification mark and description upon accepting.";
"lng_bot_verify_bot_remove" = "This bot is already verified by you. Do you want to remove verification?";
"lng_bot_verify_bot_telegram" = "This bot is verified as official by the representatives of Telegram.";
"lng_bot_verify_bot_company" = "This bot was verified by {company}.";
"lng_bot_verify_user_title" = "Verify User";
"lng_bot_verify_user_text" = "Do you want to verify {name} with your verification mark and description?";
"lng_bot_verify_user_about" = "You can customize your description for each account.";
"lng_bot_verify_user_submit" = "Verify User";
"lng_bot_verify_user_sent" = "{name} has been notified and will receive your verification mark and description upon accepting.";
"lng_bot_verify_user_remove" = "This account is already verified by you. Do you want to remove verification?";
"lng_bot_verify_user_telegram" = "This account is verified as official by the representatives of Telegram.";
"lng_bot_verify_user_company" = "This account was verified by {company}.";
"lng_bot_verify_channel_title" = "Verify Channel";
"lng_bot_verify_channel_text" = "Do you want to verify {name} with your verification mark and description?";
"lng_bot_verify_channel_about" = "You can customize your description for each channel.";
"lng_bot_verify_channel_submit" = "Verify Channel";
"lng_bot_verify_channel_sent" = "{name} has been notified and will receive your verification mark and description upon accepting.";
"lng_bot_verify_channel_remove" = "This channel is already verified by you. Do you want to remove verification?";
"lng_bot_verify_channel_telegram" = "This channel is verified as official by the representatives of Telegram.";
"lng_bot_verify_channel_company" = "This channel was verified by {company}.";
"lng_bot_verify_group_title" = "Verify Group";
"lng_bot_verify_group_text" = "Do you want to verify {name} with your verification mark and description?";
"lng_bot_verify_group_about" = "You can customize your description for each group.";
"lng_bot_verify_group_submit" = "Verify Group";
"lng_bot_verify_group_sent" = "{name} has been notified and will receive your verification mark and description upon accepting.";
"lng_bot_verify_group_remove" = "This group is already verified by you. Do you want to remove verification?";
"lng_bot_verify_group_telegram" = "This community is verified as official by the representatives of Telegram.";
"lng_bot_verify_group_company" = "This community was verified by {company}.";
"lng_bot_verify_description_label" = "Description";
"lng_bot_verify_remove_title" = "Remove verification";
"lng_bot_verify_remove_submit" = "Remove";
"lng_bot_verify_remove_done" = "You've removed this verification.";
"lng_star_ref_title" = "Affiliate Program";
"lng_star_ref_about" = "Reward those who help grow your user base.";
"lng_star_ref_share_title" = "Share revenue with affiliates";
@ -3484,8 +3523,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_verification_codes" = "Verification Codes";
"lng_verification_codes_about" = "Third-party services, like websites and stores, can send verification codes to your phone number via Telegram instead of SMS. Such codes will appear in this chat.\n\nIf you didn't request any codes — don't worry! Most likely, someone made a mistake when entering their number.";
"lng_verified_by_telegram" = "This community is verified as official by the representatives of Telegram.";
"lng_archived_name" = "Archived chats";
"lng_archived_add" = "Archive";
"lng_archived_remove" = "Unarchive";

View file

@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peers/edit_peer_requests_box.h"
#include "boxes/peers/edit_peer_reactions.h"
#include "boxes/peers/replace_boost_box.h"
#include "boxes/peers/verify_peers_box.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/stickers_box.h"
#include "boxes/username_box.h"
@ -366,6 +367,7 @@ private:
void fillBotEditIntroButton();
void fillBotEditCommandsButton();
void fillBotEditSettingsButton();
void fillBotVerifyAccounts();
void submitTitle();
void submitDescription();
@ -1206,6 +1208,7 @@ void Controller::fillManageSection() {
Ui::Text::RichLangValue),
st::boxDividerLabel),
st::defaultBoxDividerLabelPadding));
fillBotVerifyAccounts();
return;
}
@ -1796,6 +1799,39 @@ void Controller::fillBotEditSettingsButton() {
{ &st::menuIconSettings });
}
void Controller::fillBotVerifyAccounts() {
Expects(_isBot);
const auto user = _peer->asUser();
const auto wrap = _controls.buttonsLayout->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
_controls.buttonsLayout,
object_ptr<Ui::VerticalLayout>(
_controls.buttonsLayout)));
wrap->toggleOn(rpl::single(
rpl::empty
) | rpl::then(user->owner().botCommandsChanges(
) | rpl::filter(
rpl::mappers::_1 == _peer
) | rpl::to_empty) | rpl::map([=] {
const auto info = user->botInfo.get();
return info && info->verifierSettings;
}));
const auto inner = wrap->entity();
Ui::AddSkip(inner);
AddButtonWithCount(
inner,
tr::lng_manage_peer_bot_verify(),
rpl::never<QString>(),
[controller = _navigation->parentController(), user] {
controller->show(MakeVerifyPeersBox(controller, user));
},
{ &st::menuIconFactcheck });
Ui::AddSkip(inner);
Ui::AddDivider(inner);
}
void Controller::submitTitle() {
Expects(_controls.title != nullptr);

View file

@ -0,0 +1,300 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/peers/verify_peers_box.h"
#include "apiwrap.h"
#include "boxes/peer_list_controllers.h"
#include "data/data_user.h"
#include "history/history.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
#include "ui/boxes/confirm_box.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/vertical_list.h"
#include "window/window_session_controller.h"
#include "styles/style_boxes.h"
#include "styles/style_layers.h"
namespace {
constexpr auto kSetupVerificationToastDuration = 4 * crl::time(1000);
class Controller final : public ChatsListBoxController {
public:
Controller(not_null<Main::Session*> session, not_null<UserData*> bot)
: ChatsListBoxController(session)
, _bot(bot) {
}
Main::Session &session() const override;
void rowClicked(gsl::not_null<PeerListRow*> row) override;
private:
std::unique_ptr<Row> createRow(not_null<History*> history) override;
void prepareViewHook() override;
void confirmAdd(not_null<PeerData*> peer);
void confirmRemove(not_null<PeerData*> peer);
const not_null<UserData*> _bot;
};
void Setup(
not_null<UserData*> bot,
not_null<PeerData*> peer,
QString description,
Fn<void(QString)> done) {
using Flag = MTPbots_SetCustomVerification::Flag;
bot->session().api().request(MTPbots_SetCustomVerification(
MTP_flags(Flag::f_bot
| Flag::f_enabled
| (description.isEmpty() ? Flag() : Flag::f_custom_description)),
bot->inputUser,
peer->input,
MTP_string(description)
)).done([=] {
done(QString());
}).fail([=](const MTP::Error &error) {
done(error.type());
}).send();
}
void Remove(
not_null<UserData*> bot,
not_null<PeerData*> peer,
Fn<void(QString)> done) {
bot->session().api().request(MTPbots_SetCustomVerification(
MTP_flags(MTPbots_SetCustomVerification::Flag::f_bot),
bot->inputUser,
peer->input,
MTPstring()
)).done([=] {
done(QString());
}).fail([=](const MTP::Error &error) {
done(error.type());
}).send();
}
Main::Session &Controller::session() const {
return _bot->session();
}
void Controller::rowClicked(gsl::not_null<PeerListRow*> row) {
const auto peer = row->peer();
const auto details = peer->verifyDetails();
const auto already = details && (details->botId == peerToUser(_bot->id));
if (already) {
confirmRemove(peer);
} else {
confirmAdd(peer);
}
}
void Controller::confirmAdd(not_null<PeerData*> peer) {
const auto bot = _bot;
const auto show = delegate()->peerListUiShow();
show->show(Box([=](not_null<Ui::GenericBox*> box) {
struct State {
Ui::InputField *field = nullptr;
QString description;
bool sent = false;
};
const auto settings = bot->botInfo
? bot->botInfo->verifierSettings.get()
: nullptr;
const auto state = std::make_shared<State>(State{
.description = settings ? settings->customDescription : QString()
});
const auto limit = session().appConfig().get<int>(
u"bot_verification_description_length_limit"_q,
70);
const auto send = [=] {
if (state->description.size() > limit) {
state->field->showError();
return;
} else if (state->sent) {
return;
}
state->sent = true;
const auto weak = Ui::MakeWeak(box);
Setup(bot, peer, state->description, [=](QString error) {
if (error.isEmpty()) {
if (const auto strong = weak.data()) {
strong->closeBox();
}
show->showToast({
.text = PeerVerifyPhrases(peer).sent(
tr::now,
lt_name,
Ui::Text::Bold(peer->shortName()),
Ui::Text::WithEntities),
.duration = kSetupVerificationToastDuration,
});
} else {
state->sent = false;
show->showToast(error);
}
});
};
const auto phrases = PeerVerifyPhrases(peer);
Ui::ConfirmBox(box, {
.text = phrases.text(
lt_name,
rpl::single(Ui::Text::Bold(peer->shortName())),
Ui::Text::WithEntities),
.confirmed = send,
.confirmText = phrases.submit(),
.title = phrases.title(),
});
Ui::AddSubsectionTitle(
box->verticalLayout(),
tr::lng_bot_verify_description_label(),
QMargins(0, 0, 0, -st::defaultSubsectionTitlePadding.bottom()));
const auto field = box->addRow(object_ptr<Ui::InputField>(
box,
st::createPollField,
Ui::InputField::Mode::NoNewlines,
(settings
? phrases.company(lt_company, rpl::single(settings->company))
: rpl::single(QString())),
state->description
), st::createPollFieldPadding);
state->field = field;
box->setFocusCallback([=] {
field->setFocusFast();
});
Ui::AddSkip(box->verticalLayout());
field->changes() | rpl::start_with_next([=] {
state->description = field->getLastText();
}, field->lifetime());
field->setMaxLength(limit * 2);
Ui::AddLengthLimitLabel(field, limit, std::nullopt);
Ui::AddDividerText(box->verticalLayout(), phrases.about());
}));
}
void Controller::confirmRemove(not_null<PeerData*> peer) {
const auto bot = _bot;
const auto show = delegate()->peerListUiShow();
show->show(Box([=](not_null<Ui::GenericBox*> box) {
const auto sent = std::make_shared<bool>();
const auto send = [=] {
if (*sent) {
return;
}
*sent = true;
const auto weak = Ui::MakeWeak(box);
Remove(bot, peer, [=](QString error) {
if (error.isEmpty()) {
if (const auto strong = weak.data()) {
strong->closeBox();
}
show->showToast(tr::lng_bot_verify_remove_done(tr::now));
} else {
*sent = false;
show->showToast(error);
}
});
};
Ui::ConfirmBox(box, {
.text = PeerVerifyPhrases(peer).remove(),
.confirmed = send,
.confirmText = tr::lng_bot_verify_remove_submit(),
.confirmStyle = &st::attentionBoxButton,
.title = tr::lng_bot_verify_remove_title(),
});
}));
}
auto Controller::createRow(not_null<History*> history)
-> std::unique_ptr<Row> {
const auto peer = history->peer;
const auto may = peer->isUser() || peer->isChannel();
return may ? std::make_unique<Row>(history) : nullptr;
}
void Controller::prepareViewHook() {
}
} // namespace
object_ptr<Ui::BoxContent> MakeVerifyPeersBox(
not_null<Window::SessionController*> window,
not_null<UserData*> bot) {
const auto session = &window->session();
auto controller = std::make_unique<Controller>(session, bot);
auto init = [=](not_null<PeerListBox*> box) {
box->setTitle(tr::lng_bot_verify_title());
box->addButton(tr::lng_box_done(), [=] {
box->closeBox();
});
};
return Box<PeerListBox>(std::move(controller), std::move(init));
}
VerifyPhrases PeerVerifyPhrases(not_null<PeerData*> peer) {
if (const auto user = peer->asUser()) {
if (user->isBot()) {
return {
.title = tr::lng_bot_verify_bot_title,
.text = tr::lng_bot_verify_bot_text,
.about = tr::lng_bot_verify_bot_about,
.submit = tr::lng_bot_verify_bot_submit,
.sent = tr::lng_bot_verify_bot_sent,
.remove = tr::lng_bot_verify_bot_remove,
.telegram = tr::lng_bot_verify_bot_telegram,
.company = tr::lng_bot_verify_bot_company,
};
} else {
return {
.title = tr::lng_bot_verify_user_title,
.text = tr::lng_bot_verify_user_text,
.about = tr::lng_bot_verify_user_about,
.submit = tr::lng_bot_verify_user_submit,
.sent = tr::lng_bot_verify_user_sent,
.remove = tr::lng_bot_verify_user_remove,
.telegram = tr::lng_bot_verify_user_telegram,
.company = tr::lng_bot_verify_user_company,
};
}
} else if (peer->isBroadcast()) {
return {
.title = tr::lng_bot_verify_channel_title,
.text = tr::lng_bot_verify_channel_text,
.about = tr::lng_bot_verify_channel_about,
.submit = tr::lng_bot_verify_channel_submit,
.sent = tr::lng_bot_verify_channel_sent,
.remove = tr::lng_bot_verify_channel_remove,
.telegram = tr::lng_bot_verify_channel_telegram,
.company = tr::lng_bot_verify_channel_company,
};
}
return {
.title = tr::lng_bot_verify_group_title,
.text = tr::lng_bot_verify_group_text,
.about = tr::lng_bot_verify_group_about,
.submit = tr::lng_bot_verify_group_submit,
.sent = tr::lng_bot_verify_group_sent,
.remove = tr::lng_bot_verify_group_remove,
.telegram = tr::lng_bot_verify_group_telegram,
.company = tr::lng_bot_verify_group_company,
};
}

View file

@ -0,0 +1,38 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/object_ptr.h"
#include "lang/lang_keys.h"
class PeerData;
class UserData;
namespace Ui {
class BoxContent;
} // namespace Ui
namespace Window {
class SessionController;
} // namespace Window
[[nodiscard]] object_ptr<Ui::BoxContent> MakeVerifyPeersBox(
not_null<Window::SessionController*> window,
not_null<UserData*> bot);
struct VerifyPhrases {
tr::phrase<> title;
tr::phrase<lngtag_name> text;
tr::phrase<> about;
tr::phrase<> submit;
tr::phrase<lngtag_name> sent;
tr::phrase<> remove;
tr::phrase<> telegram;
tr::phrase<lngtag_company> company;
};
[[nodiscard]] VerifyPhrases PeerVerifyPhrases(not_null<PeerData*> peer);

View file

@ -142,7 +142,7 @@ void CheckForSwitchInlineButton(not_null<HistoryItem*> item) {
const auto flags = TextParseLinks;
return {
.botId = UserId(data.vbot_id().v),
.iconBgId = SerializeCustomEmojiId(DocumentId(data.vicon().v)),
.iconBgId = DocumentId(data.vicon().v),
.company = qs(data.vcompany()),
.description = TextUtilities::ParseEntities(description, flags),
};
@ -353,7 +353,7 @@ Session::Session(not_null<Main::Session*> session)
Ui::VerifyDetails Session::verifiedByTelegram() {
if (_verifiedByTelegramIconBgId.isEmpty()) {
if (!_verifiedByTelegramIconBgId) {
const auto bg = ChatHelpers::GenerateLocalSticker(
_session,
u":/gui/art/verified_bg.webp"_q);
@ -362,14 +362,13 @@ Ui::VerifyDetails Session::verifiedByTelegram() {
_session,
u":/gui/art/verified_fg.webp"_q);
fg->overrideEmojiUsesTextColor(true);
_verifiedByTelegramIconBgId = Data::SerializeCustomEmojiId(bg);
_verifiedByTelegramIconFgId = Data::SerializeCustomEmojiId(fg);
_verifiedByTelegramIconBgId = bg->id;
_verifiedByTelegramIconFgId = fg->id;
}
return {
.iconBgId = _verifiedByTelegramIconBgId,
.iconFgId = _verifiedByTelegramIconFgId,
.company = u"Telegram"_q,
.description = { tr::lng_verified_by_telegram(tr::now) },
};
}

View file

@ -1142,8 +1142,8 @@ private:
const std::unique_ptr<BusinessInfo> _businessInfo;
std::unique_ptr<ShortcutMessages> _shortcutMessages;
QString _verifiedByTelegramIconBgId;
QString _verifiedByTelegramIconFgId;
DocumentId _verifiedByTelegramIconBgId = 0;
DocumentId _verifiedByTelegramIconFgId = 0;
MsgId _nonHistoryEntryId = ShortcutMaxMsgId;

View file

@ -36,6 +36,30 @@ constexpr auto kSetOnlineAfterActivity = TimeId(30);
using UpdateFlag = Data::PeerUpdate::Flag;
bool ApplyBotVerifierSettings(
not_null<BotInfo*> info,
const MTPBotVerifierSettings *settings) {
if (!settings) {
const auto taken = base::take(info->verifierSettings);
return taken != nullptr;
}
const auto &data = settings->data();
const auto parsed = BotVerifierSettings{
.iconId = DocumentId(data.vicon().v),
.company = qs(data.vcompany()),
.customDescription = qs(data.vcustom_description().value_or_empty()),
};
if (!info->verifierSettings) {
info->verifierSettings = std::make_unique<BotVerifierSettings>(
parsed);
return true;
} else if (*info->verifierSettings != parsed) {
*info->verifierSettings = parsed;
return true;
}
return false;
}
} // namespace
BotInfo::BotInfo() = default;
@ -232,7 +256,11 @@ void UserData::setPersonalChannel(ChannelId channelId, MsgId messageId) {
}
}
void UserData::setName(const QString &newFirstName, const QString &newLastName, const QString &newPhoneName, const QString &newUsername) {
void UserData::setName(
const QString &newFirstName,
const QString &newLastName,
const QString &newPhoneName,
const QString &newUsername) {
bool changeName = !newFirstName.isEmpty() || !newLastName.isEmpty();
QString newFullName;
@ -245,7 +273,14 @@ void UserData::setName(const QString &newFirstName, const QString &newLastName,
firstName = newFirstName;
lastName = newLastName;
}
newFullName = lastName.isEmpty() ? firstName : tr::lng_full_name(tr::now, lt_first_name, firstName, lt_last_name, lastName);
newFullName = lastName.isEmpty()
? firstName
: tr::lng_full_name(
tr::now,
lt_first_name,
firstName,
lt_last_name,
lastName);
}
updateNameDelayed(newFullName, newPhoneName, newUsername);
}
@ -372,8 +407,14 @@ void UserData::setBotInfo(const MTPBotInfo &info) {
= botInfo->botAppColorBodyNight
= QColor(0, 0, 0, 0);
}
const auto changedVerifierSettings = ApplyBotVerifierSettings(
botInfo.get(),
d.vverifier_settings());
if (changedCommands || changedButton || privacyChanged) {
if (changedCommands
|| changedButton
|| privacyChanged
|| changedVerifierSettings) {
owner().botCommandsChanged(this);
}
} break;

View file

@ -31,6 +31,20 @@ struct StarRefProgram {
StarRefProgram) = default;
};
struct BotVerifierSettings {
DocumentId iconId = 0;
QString company;
QString customDescription;
explicit operator bool() const {
return iconId != 0;
}
friend inline bool operator==(
const BotVerifierSettings &a,
const BotVerifierSettings &b) = default;
};
struct BotInfo {
BotInfo();
@ -57,6 +71,7 @@ struct BotInfo {
ChatAdminRights channelAdminRights;
StarRefProgram starRefProgram;
std::unique_ptr<BotVerifierSettings> verifierSettings;
int version = 0;
int descriptionVersion = 0;

View file

@ -508,7 +508,7 @@ infoOpenApp: RoundButton(defaultActiveButton) {
}
infoOpenAppMargin: margins(16px, 12px, 16px, 12px);
infoPersonalChannelIconPosition: point(25px, 20px);
infoPersonalChannelIconPosition: point(25px, 12px);
infoPersonalChannelNameLabel: FlatLabel(infoProfileStatus) {
textFg: windowBoldFg;
style: semiboldTextStyle;

View file

@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peers/edit_contact_box.h"
#include "boxes/peers/edit_participants_box.h"
#include "boxes/peers/edit_peer_info_box.h"
#include "boxes/peers/verify_peers_box.h"
#include "boxes/report_messages_box.h"
#include "boxes/share_box.h"
#include "boxes/star_gift_box.h"
@ -960,6 +961,7 @@ private:
object_ptr<Ui::RpWidget> setupPersonalChannel(not_null<UserData*> user);
object_ptr<Ui::RpWidget> setupInfo();
object_ptr<Ui::RpWidget> setupMuteToggle();
void setupAboutVerification();
void setupMainApp();
void setupBotPermissions();
void setupMainButtons();
@ -1560,8 +1562,6 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupPersonalChannel(
}));
onlyChannelWrap->finishAnimating();
Ui::AddDivider(onlyChannelWrap->entity());
auto text = rpl::duplicate(
channel
) | rpl::map([=](ChannelData *channel) {
@ -1591,6 +1591,8 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupPersonalChannel(
onlyChannelWrap,
st::infoIconMediaChannel,
st::infoPersonalChannelIconPosition);
Ui::AddDivider(onlyChannelWrap->entity());
}
{
@ -1620,7 +1622,7 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupPersonalChannel(
messageChannelWrap->toggle(false, anim::type::instant);
clear();
Ui::AddDivider(messageChannelWrap->entity());
Ui::AddSkip(messageChannelWrap->entity());
const auto inner = messageChannelWrap->entity()->add(
@ -1751,6 +1753,7 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupPersonalChannel(
}
inner->setAttribute(Qt::WA_TransparentForMouseEvents);
Ui::AddSkip(messageChannelWrap->entity());
Ui::AddDivider(messageChannelWrap->entity());
Ui::ToggleChildrenVisibility(messageChannelWrap->entity(), true);
Ui::ToggleChildrenVisibility(line, true);
@ -1835,6 +1838,35 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupMuteToggle() {
return result;
}
void DetailsFiller::setupAboutVerification() {
const auto peer = _peer;
const auto inner = _wrap->add(object_ptr<Ui::VerticalLayout>(_wrap));
peer->session().changes().peerFlagsValue(
peer,
Data::PeerUpdate::Flag::VerifyInfo
) | rpl::start_with_next([=] {
const auto info = peer->verifyDetails();
while (inner->count()) {
delete inner->widgetAt(0);
}
if (!info) {
Ui::AddDivider(inner);
} else if (!info->description.empty()) {
Ui::AddDividerText(inner, rpl::single(info->description));
} else {
const auto phrases = PeerVerifyPhrases(peer);
Ui::AddDividerText(
inner,
(_peer->verifiedByTelegram()
? phrases.telegram()
: phrases.company(
lt_company,
rpl::single(info->company))));
}
inner->resizeToWidth(inner->width());
}, inner->lifetime());
}
void DetailsFiller::setupMainApp() {
const auto button = _wrap->add(
object_ptr<Ui::RoundButton>(
@ -2103,10 +2135,14 @@ Ui::MultiSlideTracker DetailsFiller::fillDiscussionButtons(
object_ptr<Ui::RpWidget> DetailsFiller::fill() {
Expects(!_topic || !_topic->creating());
if (!_topic) {
setupAboutVerification();
} else {
add(object_ptr<Ui::BoxContentDivider>(_wrap));
}
if (const auto user = _peer->asUser()) {
add(setupPersonalChannel(user));
}
add(object_ptr<Ui::BoxContentDivider>(_wrap));
add(CreateSkipWidget(_wrap));
add(setupInfo());
if (const auto user = _peer->asUser()) {

View file

@ -37,9 +37,7 @@ namespace {
}
return Badge::Content{
badge,
(emojiStatusId
? Data::SerializeCustomEmojiId(emojiStatusId)
: QString()),
emojiStatusId ? emojiStatusId : DocumentId(),
};
});
}
@ -120,14 +118,14 @@ void Badge::setContent(Content content) {
case BadgeType::Premium: {
const auto id = _content.emojiStatusId;
const auto innerId = _content.emojiStatusInnerId;
if (!id.isEmpty() || !innerId.isEmpty()) {
_emojiStatus = !id.isEmpty()
if (id || innerId) {
_emojiStatus = id
? _session->data().customEmojiManager().create(
id,
[raw = _view.data()] { raw->update(); },
sizeTag())
: nullptr;
_statusInner = !innerId.isEmpty()
_statusInner = innerId
? _session->data().customEmojiManager().create(
innerId,
[raw = _view.data()] { raw->update(); },

View file

@ -58,8 +58,8 @@ public:
struct Content {
BadgeType badge = BadgeType::None;
QString emojiStatusId;
QString emojiStatusInnerId;
DocumentId emojiStatusId = 0;
DocumentId emojiStatusInnerId = 0;
friend inline constexpr bool operator==(Content, Content) = default;
};

View file

@ -94,6 +94,16 @@ auto ChatStatusText(int fullCount, int onlineCount, bool isGroup) {
: st::infoProfileCover;
}
[[nodiscard]] QMargins LargeCustomEmojiMargins() {
const auto ratio = style::DevicePixelRatio();
const auto emoji = Ui::Emoji::GetSizeLarge() / ratio;
const auto size = Data::FrameSizeFromTag(Data::CustomEmojiSizeTag::Large)
/ ratio;
const auto left = (size - emoji) / 2;
const auto right = size - emoji - left;
return { left, left, right, right };
}
} // namespace
TopicIconView::TopicIconView(
@ -302,8 +312,8 @@ Cover::Cover(
const auto details = peer->verifyDetails();
return Badge::Content{
.badge = details ? BadgeType::Verified : BadgeType::None,
.emojiStatusId = details ? details->iconBgId : QString(),
.emojiStatusInnerId = details ? details->iconFgId : QString(),
.emojiStatusId = details ? details->iconBgId : DocumentId(),
.emojiStatusInnerId = details ? details->iconFgId : DocumentId(),
};
});
}
@ -731,10 +741,16 @@ void Cover::refreshNameGeometry(int newWidth) {
auto nameLeft = _st.nameLeft;
const auto badgeTop = _st.nameTop;
const auto badgeBottom = _st.nameTop + _name->height();
_verify->move(nameLeft, badgeTop, badgeBottom);
const auto margins = LargeCustomEmojiMargins();
_verify->move(nameLeft - margins.left(), badgeTop, badgeBottom);
if (const auto widget = _verify->widget()) {
nameLeft += widget->width() + st::infoVerifiedCheckPosition.x();
nameWidth -= widget->width() + st::infoVerifiedCheckPosition.x();
const auto skip = widget->width()
+ st::infoVerifiedCheckPosition.x()
- margins.left()
- margins.right();
nameLeft += skip;
nameWidth -= skip;
}
_name->resizeToNaturalWidth(nameWidth);
_name->moveToLeft(nameLeft, _st.nameTop, newWidth);

View file

@ -225,16 +225,18 @@ bool PeerBadge::ready(const VerifyDetails *details) const {
} else if (!_verifiedData) {
return false;
}
if (details->iconBgId.isEmpty()) {
if (!details->iconBgId) {
_verifiedData->bg = nullptr;
} else if (!_verifiedData->bg
|| _verifiedData->bg->entityData() != details->iconBgId) {
|| (_verifiedData->bg->entityData()
!= Data::SerializeCustomEmojiId(details->iconBgId))) {
return false;
}
if (details->iconFgId.isEmpty()) {
if (!details->iconFgId) {
_verifiedData->fg = nullptr;
} else if (!_verifiedData->fg
|| _verifiedData->fg->entityData() != details->iconFgId) {
|| (_verifiedData->fg->entityData()
!= Data::SerializeCustomEmojiId(details->iconFgId))) {
return false;
}
return true;
@ -247,11 +249,15 @@ void PeerBadge::set(
if (!_verifiedData) {
_verifiedData = std::make_unique<VerifiedData>();
}
if (!details->iconBgId.isEmpty()) {
_verifiedData->bg = factory(details->iconBgId, repaint);
if (details->iconBgId) {
_verifiedData->bg = factory(
Data::SerializeCustomEmojiId(details->iconBgId),
repaint);
}
if (!details->iconFgId.isEmpty()) {
_verifiedData->fg = factory(details->iconFgId, repaint);
if (details->iconFgId) {
_verifiedData->fg = factory(
Data::SerializeCustomEmojiId(details->iconFgId),
repaint);
}
}

View file

@ -34,13 +34,13 @@ private:
struct VerifyDetails {
UserId botId = 0;
QString iconBgId;
QString iconFgId;
DocumentId iconBgId = 0;
DocumentId iconFgId = 0;
QString company;
TextWithEntities description;
explicit operator bool() const {
return !iconBgId.isEmpty() || !iconFgId.isEmpty();
return iconBgId || iconFgId;
}
friend inline bool operator==(
const VerifyDetails &,