Allow many lines in ConfirmInviteBox about.

This commit is contained in:
John Preston
2026-04-09 16:09:16 +07:00
parent 263ddc3bcf
commit 111b15a855
3 changed files with 269 additions and 337 deletions
+267 -254
View File
@@ -48,6 +48,69 @@ namespace Api {
namespace {
struct InviteParticipant {
not_null<UserData*> user;
Ui::PeerUserpicView userpic;
};
struct ChatInvite {
QString title;
QString about;
PhotoData *photo = nullptr;
int participantsCount = 0;
std::vector<InviteParticipant> participants;
bool isPublic = false;
bool isChannel = false;
bool isMegagroup = false;
bool isBroadcast = false;
bool isRequestNeeded = false;
bool isFake = false;
bool isScam = false;
bool isVerified = false;
};
[[nodiscard]] ChatInvite ParseInvite(
not_null<Main::Session*> session,
const MTPDchatInvite &data) {
auto participants = std::vector<InviteParticipant>();
if (const auto list = data.vparticipants()) {
participants.reserve(list->v.size());
for (const auto &participant : list->v) {
if (const auto user = session->data().processUser(participant)) {
participants.push_back(InviteParticipant{ user });
}
}
}
const auto photo = session->data().processPhoto(data.vphoto());
return {
.title = qs(data.vtitle()),
.about = data.vabout().value_or_empty(),
.photo = (photo->isNull() ? nullptr : photo.get()),
.participantsCount = data.vparticipants_count().v,
.participants = std::move(participants),
.isPublic = data.is_public(),
.isChannel = data.is_channel(),
.isMegagroup = data.is_megagroup(),
.isBroadcast = data.is_broadcast(),
.isRequestNeeded = data.is_request_needed(),
.isFake = data.is_fake(),
.isScam = data.is_scam(),
.isVerified = data.is_verified(),
};
}
[[nodiscard]] Info::Profile::BadgeType BadgeForInvite(
const ChatInvite &invite) {
using Type = Info::Profile::BadgeType;
return invite.isVerified
? Type::Verified
: invite.isScam
? Type::Scam
: invite.isFake
? Type::Fake
: Type::None;
}
void SubmitChatInvite(
base::weak_ptr<Window::SessionController> weak,
not_null<Main::Session*> session,
@@ -356,6 +419,207 @@ void ConfirmSubscriptionBox(
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
void ConfirmInviteBox(
not_null<Ui::GenericBox*> box,
not_null<Main::Session*> session,
const MTPDchatInvite *invitePtr,
ChannelData *invitePeekChannel,
Fn<void()> submit) {
auto invite = ParseInvite(session, *invitePtr);
const auto isChannel = invite.isChannel && !invite.isMegagroup;
const auto requestApprove = invite.isRequestNeeded;
const auto count = invite.participantsCount;
struct State {
std::shared_ptr<Data::PhotoMedia> photoMedia;
std::unique_ptr<Ui::EmptyUserpic> photoEmpty;
std::vector<InviteParticipant> participants;
};
const auto state = box->lifetime().make_state<State>();
state->participants = std::move(invite.participants);
const auto status = [&] {
return invitePeekChannel
? tr::lng_channel_invite_private(tr::now)
: (!state->participants.empty()
&& int(state->participants.size()) < count)
? tr::lng_group_invite_members(tr::now, lt_count, count)
: (count > 0 && isChannel)
? tr::lng_chat_status_subscribers(
tr::now,
lt_count_decimal,
count)
: (count > 0)
? tr::lng_chat_status_members(tr::now, lt_count_decimal, count)
: isChannel
? tr::lng_channel_status(tr::now)
: tr::lng_group_status(tr::now);
}();
box->setNoContentMargin(true);
box->setWidth(st::boxWideWidth);
const auto content = box->verticalLayout();
Ui::AddSkip(content, st::confirmInvitePhotoTop);
const auto userpic = content->add(
object_ptr<Ui::RpWidget>(content),
style::al_top);
const auto photoSize = st::confirmInvitePhotoSize;
userpic->resize(Size(photoSize));
userpic->setNaturalWidth(photoSize);
userpic->paintRequest(
) | rpl::on_next([=, small = Data::PhotoSize::Small] {
auto p = QPainter(userpic);
if (state->photoMedia) {
if (const auto image = state->photoMedia->image(small)) {
p.drawPixmap(
0,
0,
image->pix(
Size(photoSize),
{ .options = Images::Option::RoundCircle }));
}
} else if (state->photoEmpty) {
state->photoEmpty->paintCircle(
p,
0,
0,
userpic->width(),
photoSize);
}
}, userpic->lifetime());
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
if (const auto photo = invite.photo) {
state->photoMedia = photo->createMediaView();
state->photoMedia->wanted(
Data::PhotoSize::Small,
Data::FileOrigin());
if (!state->photoMedia->image(Data::PhotoSize::Small)) {
session->downloaderTaskFinished(
) | rpl::on_next([=] {
userpic->update();
}, userpic->lifetime());
}
} else {
state->photoEmpty = std::make_unique<Ui::EmptyUserpic>(
Ui::EmptyUserpic::UserpicColor(0),
invite.title);
}
Ui::AddSkip(content);
const auto title = box->addRow(
object_ptr<Ui::FlatLabel>(
box,
invite.title,
st::confirmInviteTitle),
style::al_top);
const auto badgeType = BadgeForInvite(invite);
if (badgeType != Info::Profile::BadgeType::None) {
const auto badgeParent = title->parentWidget();
const auto badge = box->lifetime().make_state<Info::Profile::Badge>(
badgeParent,
st::infoPeerBadge,
session,
rpl::single(Info::Profile::Badge::Content{ badgeType }),
nullptr,
[] { return false; });
title->geometryValue(
) | rpl::on_next([=](const QRect &r) {
badge->move(r.x() + r.width(), r.y(), r.y() + r.height());
}, title->lifetime());
}
box->addRow(
object_ptr<Ui::FlatLabel>(
box,
status,
st::confirmInviteStatus),
style::al_top);
if (!invite.about.isEmpty()) {
box->addRow(
object_ptr<Ui::FlatLabel>(
box,
invite.about,
st::confirmInviteAbout),
st::confirmInviteAboutPadding,
style::al_top);
}
if (requestApprove) {
box->addRow(
object_ptr<Ui::FlatLabel>(
box,
(isChannel
? tr::lng_group_request_about_channel(tr::now)
: tr::lng_group_request_about(tr::now)),
st::confirmInviteStatus),
st::confirmInviteAboutRequestsPadding,
style::al_top);
}
if (!state->participants.empty()) {
while (state->participants.size() > 4) {
state->participants.pop_back();
}
const auto padding = (st::confirmInviteUsersWidth
- 4 * st::confirmInviteUserPhotoSize) / 10;
const auto userWidth = st::confirmInviteUserPhotoSize + 2 * padding;
auto strip = object_ptr<Ui::RpWidget>(content);
const auto rawStrip = strip.data();
rawStrip->resize(st::boxWideWidth, st::confirmInviteUserHeight);
rawStrip->setNaturalWidth(st::boxWideWidth);
const auto shown = int(state->participants.size());
const auto sumWidth = shown * userWidth;
const auto baseLeft = (st::boxWideWidth - sumWidth) / 2;
for (auto i = 0; i != shown; ++i) {
const auto &participant = state->participants[i];
const auto name = Ui::CreateChild<Ui::FlatLabel>(
rawStrip,
st::confirmInviteUserName);
name->resizeToWidth(
st::confirmInviteUserPhotoSize + padding);
name->setText(participant.user->firstName.isEmpty()
? participant.user->name()
: participant.user->firstName);
name->moveToLeft(
baseLeft + i * userWidth + (padding / 2),
st::confirmInviteUserNameTop - st::confirmInviteUserPhotoTop);
}
rawStrip->paintRequest(
) | rpl::on_next([=] {
auto p = Painter(rawStrip);
const auto total = int(state->participants.size());
const auto totalWidth = total * userWidth;
auto left = (rawStrip->width() - totalWidth) / 2;
for (auto &participant : state->participants) {
participant.user->paintUserpicLeft(
p,
participant.userpic,
left + (userWidth - st::confirmInviteUserPhotoSize) / 2,
0,
rawStrip->width(),
st::confirmInviteUserPhotoSize);
left += userWidth;
}
}, rawStrip->lifetime());
Ui::AddSkip(content, st::boxPadding.bottom());
content->add(std::move(strip), style::margins());
}
box->addButton((requestApprove
? tr::lng_group_request_to_join()
: isChannel
? tr::lng_profile_join_channel()
: tr::lng_profile_join_group()), submit);
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
} // namespace
void CheckChatInvite(
@@ -399,9 +663,10 @@ void CheckChatInvite(
session,
hash,
&data))
: strong->show(Box<ConfirmInviteBox>(
: strong->show(Box(
ConfirmInviteBox,
session,
data,
&data,
invitePeekChannel,
[=] { SubmitChatInvite(weak, session, hash, isGroup); }));
if (invitePeekChannel) {
@@ -450,255 +715,3 @@ void CheckChatInvite(
}
} // namespace Api
struct ConfirmInviteBox::Participant {
not_null<UserData*> user;
Ui::PeerUserpicView userpic;
};
ConfirmInviteBox::ConfirmInviteBox(
QWidget*,
not_null<Main::Session*> session,
const MTPDchatInvite &data,
ChannelData *invitePeekChannel,
Fn<void()> submit)
: ConfirmInviteBox(
session,
Parse(session, data),
invitePeekChannel,
std::move(submit)) {
}
ConfirmInviteBox::ConfirmInviteBox(
not_null<Main::Session*> session,
ChatInvite &&invite,
ChannelData *invitePeekChannel,
Fn<void()> submit)
: _session(session)
, _submit(std::move(submit))
, _title(this, st::confirmInviteTitle)
, _badge(std::make_unique<Info::Profile::Badge>(
this,
st::infoPeerBadge,
_session,
rpl::single(Info::Profile::Badge::Content{ BadgeForInvite(invite) }),
nullptr,
[=] { return false; }))
, _status(this, st::confirmInviteStatus)
, _about(this, st::confirmInviteAbout)
, _aboutRequests(this, st::confirmInviteStatus)
, _participants(std::move(invite.participants))
, _isChannel(invite.isChannel && !invite.isMegagroup)
, _requestApprove(invite.isRequestNeeded) {
const auto count = invite.participantsCount;
const auto status = [&] {
return invitePeekChannel
? tr::lng_channel_invite_private(tr::now)
: (!_participants.empty() && _participants.size() < count)
? tr::lng_group_invite_members(tr::now, lt_count, count)
: (count > 0 && _isChannel)
? tr::lng_chat_status_subscribers(
tr::now,
lt_count_decimal,
count)
: (count > 0)
? tr::lng_chat_status_members(tr::now, lt_count_decimal, count)
: _isChannel
? tr::lng_channel_status(tr::now)
: tr::lng_group_status(tr::now);
}();
_title->setText(invite.title);
_status->setText(status);
if (!invite.about.isEmpty()) {
_about->setText(invite.about);
} else {
_about.destroy();
}
if (_requestApprove) {
_aboutRequests->setText(_isChannel
? tr::lng_group_request_about_channel(tr::now)
: tr::lng_group_request_about(tr::now));
} else {
_aboutRequests.destroy();
}
if (invite.photo) {
_photo = invite.photo->createMediaView();
_photo->wanted(Data::PhotoSize::Small, Data::FileOrigin());
if (!_photo->image(Data::PhotoSize::Small)) {
_session->downloaderTaskFinished(
) | rpl::on_next([=] {
update();
}, lifetime());
}
} else {
_photoEmpty = std::make_unique<Ui::EmptyUserpic>(
Ui::EmptyUserpic::UserpicColor(0),
invite.title);
}
}
ConfirmInviteBox::~ConfirmInviteBox() = default;
ConfirmInviteBox::ChatInvite ConfirmInviteBox::Parse(
not_null<Main::Session*> session,
const MTPDchatInvite &data) {
auto participants = std::vector<Participant>();
if (const auto list = data.vparticipants()) {
participants.reserve(list->v.size());
for (const auto &participant : list->v) {
if (const auto user = session->data().processUser(participant)) {
participants.push_back(Participant{ user });
}
}
}
const auto photo = session->data().processPhoto(data.vphoto());
return {
.title = qs(data.vtitle()),
.about = data.vabout().value_or_empty(),
.photo = (photo->isNull() ? nullptr : photo.get()),
.participantsCount = data.vparticipants_count().v,
.participants = std::move(participants),
.isPublic = data.is_public(),
.isChannel = data.is_channel(),
.isMegagroup = data.is_megagroup(),
.isBroadcast = data.is_broadcast(),
.isRequestNeeded = data.is_request_needed(),
.isFake = data.is_fake(),
.isScam = data.is_scam(),
.isVerified = data.is_verified(),
};
}
[[nodiscard]] Info::Profile::BadgeType ConfirmInviteBox::BadgeForInvite(
const ChatInvite &invite) {
using Type = Info::Profile::BadgeType;
return invite.isVerified
? Type::Verified
: invite.isScam
? Type::Scam
: invite.isFake
? Type::Fake
: Type::None;
}
void ConfirmInviteBox::prepare() {
addButton(
(_requestApprove
? tr::lng_group_request_to_join()
: _isChannel
? tr::lng_profile_join_channel()
: tr::lng_profile_join_group()),
_submit);
addButton(tr::lng_cancel(), [=] { closeBox(); });
while (_participants.size() > 4) {
_participants.pop_back();
}
auto newHeight = st::confirmInviteStatusTop + _status->height() + st::boxPadding.bottom();
if (!_participants.empty()) {
int skip = (st::confirmInviteUsersWidth - 4 * st::confirmInviteUserPhotoSize) / 5;
int padding = skip / 2;
_userWidth = (st::confirmInviteUserPhotoSize + 2 * padding);
int sumWidth = _participants.size() * _userWidth;
int left = (st::boxWideWidth - sumWidth) / 2;
for (const auto &participant : _participants) {
auto name = new Ui::FlatLabel(this, st::confirmInviteUserName);
name->resizeToWidth(st::confirmInviteUserPhotoSize + padding);
name->setText(participant.user->firstName.isEmpty()
? participant.user->name()
: participant.user->firstName);
name->moveToLeft(left + (padding / 2), st::confirmInviteUserNameTop);
left += _userWidth;
}
newHeight += st::confirmInviteUserHeight;
}
if (_about) {
const auto padding = st::confirmInviteAboutPadding;
_about->resizeToWidth(st::boxWideWidth - padding.left() - padding.right());
newHeight += padding.top() + _about->height() + padding.bottom();
}
if (_aboutRequests) {
const auto padding = st::confirmInviteAboutRequestsPadding;
_aboutRequests->resizeToWidth(st::boxWideWidth - padding.left() - padding.right());
newHeight += padding.top() + _aboutRequests->height() + padding.bottom();
}
setDimensions(st::boxWideWidth, newHeight);
}
void ConfirmInviteBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
const auto padding = st::boxRowPadding;
auto nameWidth = width() - padding.left() - padding.right();
auto badgeWidth = 0;
if (const auto widget = _badge->widget()) {
badgeWidth = st::infoVerifiedCheckPosition.x() + widget->width();
nameWidth -= badgeWidth;
}
_title->resizeToWidth(std::min(nameWidth, _title->textMaxWidth()));
_title->moveToLeft(
(width() - _title->width() - badgeWidth) / 2,
st::confirmInviteTitleTop);
const auto badgeLeft = _title->x() + _title->width();
const auto badgeTop = _title->y();
const auto badgeBottom = _title->y() + _title->height();
_badge->move(badgeLeft, badgeTop, badgeBottom);
_status->move(
(width() - _status->width()) / 2,
st::confirmInviteStatusTop);
auto bottom = _status->y()
+ _status->height()
+ st::boxPadding.bottom()
+ (_participants.empty() ? 0 : st::confirmInviteUserHeight);
if (_about) {
const auto padding = st::confirmInviteAboutPadding;
_about->move((width() - _about->width()) / 2, bottom + padding.top());
bottom += padding.top() + _about->height() + padding.bottom();
}
if (_aboutRequests) {
const auto padding = st::confirmInviteAboutRequestsPadding;
_aboutRequests->move((width() - _aboutRequests->width()) / 2, bottom + padding.top());
}
}
void ConfirmInviteBox::paintEvent(QPaintEvent *e) {
BoxContent::paintEvent(e);
Painter p(this);
if (_photo) {
if (const auto image = _photo->image(Data::PhotoSize::Small)) {
const auto size = st::confirmInvitePhotoSize;
p.drawPixmap(
(width() - size) / 2,
st::confirmInvitePhotoTop,
image->pix(
{ size, size },
{ .options = Images::Option::RoundCircle }));
}
} else if (_photoEmpty) {
_photoEmpty->paintCircle(
p,
(width() - st::confirmInvitePhotoSize) / 2,
st::confirmInvitePhotoTop,
width(),
st::confirmInvitePhotoSize);
}
int sumWidth = _participants.size() * _userWidth;
int left = (width() - sumWidth) / 2;
for (auto &participant : _participants) {
participant.user->paintUserpicLeft(
p,
participant.userpic,
left + (_userWidth - st::confirmInviteUserPhotoSize) / 2,
st::confirmInviteUserPhotoTop,
width(),
st::confirmInviteUserPhotoSize);
left += _userWidth;
}
}