Nice join confcall confirm box.

This commit is contained in:
John Preston 2025-04-01 00:28:01 +05:00
parent 698d9c208f
commit 021115b463
17 changed files with 217 additions and 47 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -4916,6 +4916,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_confcall_join_title" = "Group Call";
"lng_confcall_join_text" = "You are invited to join a Telegram Call.";
"lng_confcall_join_text_inviter" = "{user} is inviting you to join a Telegram Call.";
"lng_confcall_already_joined_one" = "{user} already joined this call.";
"lng_confcall_already_joined_two" = "{user} and {other} already joined this call.";
"lng_confcall_already_joined_three" = "{user}, {other} and {third} already joined this call.";
"lng_confcall_already_joined_many#one" = "{user}, {other} and **{count}** other person already joined this call.";
"lng_confcall_already_joined_many#other" = "{user}, {other} and **{count}** other people already joined this call.";
"lng_confcall_join_button" = "Join Group Call";
"lng_confcall_create_link" = "Create Call Link";
"lng_confcall_create_link_description" = "You can create a link that will allow your friends on Telegram to join the call.";

View file

@ -24,6 +24,12 @@ UserpicButton {
uploadIcon: icon;
uploadIconPosition: point;
}
UserpicsRow {
button: UserpicButton;
shift: pixels;
stroke: pixels;
invert: bool;
}
ShortInfoBox {
label: FlatLabel;
labeled: FlatLabel;

View file

@ -179,6 +179,7 @@ void FillUpgradeToPremiumCover(
CreateUserpicsWithMoreBadge(
container,
rpl::single(std::move(userpicPeers)),
st::boostReplaceUserpicsRow,
kUserpicsLimit),
st::inviteForbiddenUserpicsPadding)
)->entity()->setAttribute(Qt::WA_TransparentForMouseEvents);

View file

@ -550,13 +550,13 @@ object_ptr<Ui::RpWidget> CreateUserpicsTransfer(
rpl::variable<int> count = 0;
bool painting = false;
};
const auto full = st::boostReplaceUserpic.size.height()
const auto st = &st::boostReplaceUserpicsRow;
const auto full = st->button.size.height()
+ st::boostReplaceIconAdd.y()
+ st::lineWidth;
auto result = object_ptr<Ui::FixedHeightWidget>(parent, full);
const auto raw = result.data();
const auto &st = st::boostReplaceUserpic;
const auto right = CreateChild<Ui::UserpicButton>(raw, to, st);
const auto right = CreateChild<Ui::UserpicButton>(raw, to, st->button);
const auto overlay = CreateChild<Ui::RpWidget>(raw);
const auto state = raw->lifetime().make_state<State>();
@ -564,7 +564,6 @@ object_ptr<Ui::RpWidget> CreateUserpicsTransfer(
from
) | rpl::start_with_next([=](
const std::vector<not_null<PeerData*>> &list) {
const auto &st = st::boostReplaceUserpic;
auto was = base::take(state->from);
auto buttons = base::take(state->buttons);
state->from.reserve(list.size());
@ -578,7 +577,7 @@ object_ptr<Ui::RpWidget> CreateUserpicsTransfer(
state->buttons.push_back(std::move(buttons[index]));
} else {
state->buttons.push_back(
std::make_unique<Ui::UserpicButton>(raw, peer, st));
std::make_unique<Ui::UserpicButton>(raw, peer, st->button));
const auto raw = state->buttons.back().get();
base::install_event_filter(raw, [=](not_null<QEvent*> e) {
return (e->type() == QEvent::Paint && !state->painting)
@ -598,7 +597,7 @@ object_ptr<Ui::RpWidget> CreateUserpicsTransfer(
const auto skip = st::boostReplaceUserpicsSkip;
const auto left = width - 2 * right->width() - skip;
const auto shift = std::min(
st::boostReplaceUserpicsShift,
st->shift,
(count > 1 ? (left / (count - 1)) : width));
const auto total = right->width()
+ (count ? (skip + right->width() + (count - 1) * shift) : 0);
@ -630,7 +629,7 @@ object_ptr<Ui::RpWidget> CreateUserpicsTransfer(
auto q = QPainter(&state->layer);
auto hq = PainterHighQualityEnabler(q);
const auto stroke = st::boostReplaceIconOutline;
const auto stroke = st->stroke;
const auto half = stroke / 2.;
auto pen = st::windowBg->p;
pen.setWidthF(stroke * 2.);
@ -684,6 +683,7 @@ object_ptr<Ui::RpWidget> CreateUserpicsTransfer(
object_ptr<Ui::RpWidget> CreateUserpicsWithMoreBadge(
not_null<Ui::RpWidget*> parent,
rpl::producer<std::vector<not_null<PeerData*>>> peers,
const style::UserpicsRow &st,
int limit) {
struct State {
std::vector<not_null<PeerData*>> from;
@ -693,7 +693,7 @@ object_ptr<Ui::RpWidget> CreateUserpicsWithMoreBadge(
rpl::variable<int> count = 0;
bool painting = false;
};
const auto full = st::boostReplaceUserpic.size.height()
const auto full = st.button.size.height()
+ st::boostReplaceIconAdd.y()
+ st::lineWidth;
auto result = object_ptr<Ui::FixedHeightWidget>(parent, full);
@ -703,9 +703,8 @@ object_ptr<Ui::RpWidget> CreateUserpicsWithMoreBadge(
const auto state = raw->lifetime().make_state<State>();
std::move(
peers
) | rpl::start_with_next([=](
) | rpl::start_with_next([=, &st](
const std::vector<not_null<PeerData*>> &list) {
const auto &st = st::boostReplaceUserpic;
auto was = base::take(state->from);
auto buttons = base::take(state->buttons);
state->from.reserve(list.size());
@ -719,7 +718,7 @@ object_ptr<Ui::RpWidget> CreateUserpicsWithMoreBadge(
state->buttons.push_back(std::move(buttons[index]));
} else {
state->buttons.push_back(
std::make_unique<Ui::UserpicButton>(raw, peer, st));
std::make_unique<Ui::UserpicButton>(raw, peer, st.button));
const auto raw = state->buttons.back().get();
base::install_event_filter(raw, [=](not_null<QEvent*> e) {
return (e->type() == QEvent::Paint && !state->painting)
@ -735,13 +734,12 @@ object_ptr<Ui::RpWidget> CreateUserpicsWithMoreBadge(
rpl::combine(
raw->widthValue(),
state->count.value()
) | rpl::start_with_next([=](int width, int count) {
const auto &st = st::boostReplaceUserpic;
const auto single = st.size.width();
) | rpl::start_with_next([=, &st](int width, int count) {
const auto single = st.button.size.width();
const auto left = width - single;
const auto used = std::min(count, int(state->buttons.size()));
const auto shift = std::min(
st::boostReplaceUserpicsShift,
st.shift,
(used > 1 ? (left / (used - 1)) : width));
const auto total = used ? (single + (used - 1) * shift) : 0;
auto x = (width - total) / 2;
@ -755,7 +753,7 @@ object_ptr<Ui::RpWidget> CreateUserpicsWithMoreBadge(
overlay->paintRequest(
) | rpl::filter([=] {
return !state->buttons.empty();
}) | rpl::start_with_next([=] {
}) | rpl::start_with_next([=, &st] {
const auto outerw = overlay->width();
const auto ratio = style::DevicePixelRatio();
if (state->layer.size() != QSize(outerw, full) * ratio) {
@ -768,17 +766,26 @@ object_ptr<Ui::RpWidget> CreateUserpicsWithMoreBadge(
auto q = QPainter(&state->layer);
auto hq = PainterHighQualityEnabler(q);
const auto stroke = st::boostReplaceIconOutline;
const auto stroke = st.stroke;
const auto half = stroke / 2.;
auto pen = st::windowBg->p;
pen.setWidthF(stroke * 2.);
state->painting = true;
for (const auto &button : state->buttons) {
const auto paintOne = [&](not_null<Ui::UserpicButton*> button) {
q.setPen(pen);
q.setBrush(Qt::NoBrush);
q.drawEllipse(button->geometry());
const auto position = button->pos();
button->render(&q, position, QRegion(), QWidget::DrawChildren);
};
if (st.invert) {
for (const auto &button : ranges::views::reverse(state->buttons)) {
paintOne(button.get());
}
} else {
for (const auto &button : state->buttons) {
paintOne(button.get());
}
}
state->painting = false;
const auto last = state->buttons.back().get();

View file

@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/object_ptr.h"
namespace style {
struct UserpicsRow;
} // namespace style
class ChannelData;
namespace Main {
@ -66,4 +70,5 @@ enum class UserpicsTransferType {
[[nodiscard]] object_ptr<Ui::RpWidget> CreateUserpicsWithMoreBadge(
not_null<Ui::RpWidget*> parent,
rpl::producer<std::vector<not_null<PeerData*>>> peers,
const style::UserpicsRow &st,
int limit);

View file

@ -1527,6 +1527,22 @@ confcallLinkFooterOr: FlatLabel(confcallLinkCenteredText) {
confcallLinkFooterOrTop: 12px;
confcallLinkFooterOrSkip: 8px;
confcallLinkFooterOrLineTop: 9px;
confcallJoinBox: Box(confcallLinkBox) {
buttonPadding: margins(24px, 24px, 24px, 24px);
}
confcallJoinLogo: icon {{ "calls/group_call_logo", windowFgActive }};
confcallJoinLogoPadding: margins(8px, 8px, 8px, 8px);
confcallJoinSepPadding: margins(0px, 8px, 0px, 8px);
confcallJoinUserpics: UserpicsRow {
button: UserpicButton(defaultUserpicButton) {
size: size(36px, 36px);
photoSize: 36px;
}
shift: 16px;
stroke: 2px;
invert: true;
}
confcallJoinUserpicsPadding: margins(0px, 0px, 0px, 16px);
groupCallLinkBox: Box(confcallLinkBox) {
bg: groupCallMembersBg;

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "base/platform/base_platform_info.h"
#include "base/random.h"
#include "boxes/peers/replace_boost_box.h" // CreateUserpicsWithMoreBadge
#include "boxes/share_box.h"
#include "calls/calls_instance.h"
#include "core/application.h"
@ -22,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/labels.h"
#include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h"
#include "ui/painter.h"
#include "ui/vertical_list.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
@ -90,25 +92,144 @@ object_ptr<Ui::GenericBox> ScreenSharingPrivacyRequestBox() {
void ConferenceCallJoinConfirm(
not_null<Ui::GenericBox*> box,
std::shared_ptr<Data::GroupCall> call,
UserData *maybeInviter,
Fn<void()> join) {
box->setTitle(tr::lng_confcall_join_title());
box->setStyle(st::confcallJoinBox);
box->setWidth(st::boxWideWidth);
box->setNoContentMargin(true);
box->addTopButton(st::boxTitleClose, [=] {
box->closeBox();
});
const auto logoSize = st::confcallJoinLogo.size();
const auto logoOuter = logoSize.grownBy(st::confcallJoinLogoPadding);
const auto logo = box->addRow(
object_ptr<Ui::RpWidget>(box),
st::boxRowPadding + st::confcallLinkHeaderIconPadding);
logo->resize(logo->width(), logoOuter.height());
logo->paintRequest() | rpl::start_with_next([=] {
if (logo->width() < logoOuter.width()) {
return;
}
auto p = QPainter(logo);
auto hq = PainterHighQualityEnabler(p);
const auto x = (logo->width() - logoOuter.width()) / 2;
const auto outer = QRect(QPoint(x, 0), logoOuter);
p.setBrush(st::windowBgActive);
p.setPen(Qt::NoPen);
p.drawEllipse(outer);
st::confcallJoinLogo.paintInCenter(p, outer);
}, logo->lifetime());
box->addRow(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
box,
object_ptr<Ui::FlatLabel>(
box,
tr::lng_confcall_join_title(),
st::boxTitle)),
st::boxRowPadding + st::confcallLinkTitlePadding);
const auto wrapName = [&](not_null<PeerData*> peer) {
return rpl::single(Ui::Text::Bold(peer->shortName()));
};
box->addRow(
object_ptr<Ui::FlatLabel>(
box,
tr::lng_confcall_join_text(),
st::boxLabel));
(maybeInviter
? tr::lng_confcall_join_text_inviter(
lt_user,
wrapName(maybeInviter),
Ui::Text::RichLangValue)
: tr::lng_confcall_join_text(Ui::Text::RichLangValue)),
st::confcallLinkCenteredText),
st::boxRowPadding
)->setTryMakeSimilarLines(true);
box->addButton(tr::lng_confcall_join_button(), [=] {
const auto &participants = call->participants();
const auto known = int(participants.size());
if (known) {
const auto sep = box->addRow(
object_ptr<Ui::RpWidget>(box),
st::boxRowPadding + st::confcallJoinSepPadding);
sep->resize(sep->width(), st::normalFont->height);
sep->paintRequest() | rpl::start_with_next([=] {
auto p = QPainter(sep);
const auto line = st::lineWidth;
const auto top = st::confcallLinkFooterOrLineTop;
const auto fg = st::windowSubTextFg->b;
p.setOpacity(0.2);
p.fillRect(0, top, sep->width(), line, fg);
}, sep->lifetime());
auto peers = std::vector<not_null<PeerData*>>();
for (const auto &participant : participants) {
peers.push_back(participant.peer);
if (peers.size() == 3) {
break;
}
}
box->addRow(
CreateUserpicsWithMoreBadge(
box,
rpl::single(peers),
st::confcallJoinUserpics,
known),
st::boxRowPadding + st::confcallJoinUserpicsPadding);
const auto wrapByIndex = [&](int index) {
Expects(index >= 0 && index < known);
return wrapName(participants[index].peer);
};
auto text = (known == 1)
? tr::lng_confcall_already_joined_one(
lt_user,
wrapByIndex(0),
Ui::Text::RichLangValue)
: (known == 2)
? tr::lng_confcall_already_joined_two(
lt_user,
wrapByIndex(0),
lt_other,
wrapByIndex(1),
Ui::Text::RichLangValue)
: (known == 3)
? tr::lng_confcall_already_joined_three(
lt_user,
wrapByIndex(0),
lt_other,
wrapByIndex(1),
lt_third,
wrapByIndex(2),
Ui::Text::RichLangValue)
: tr::lng_confcall_already_joined_many(
lt_count,
rpl::single(1. * (std::max(known, call->fullCount()) - 2)),
lt_user,
wrapByIndex(0),
lt_other,
wrapByIndex(1),
Ui::Text::RichLangValue);
box->addRow(
object_ptr<Ui::FlatLabel>(
box,
std::move(text),
st::confcallLinkCenteredText),
st::boxRowPadding
)->setTryMakeSimilarLines(true);
}
const auto joinAndClose = [=] {
const auto weak = Ui::MakeWeak(box);
join();
if (const auto strong = weak.data()) {
strong->closeBox();
}
});
box->addButton(tr::lng_cancel(), [=] {
box->closeBox();
});
};
Info::BotStarRef::AddFullWidthButton(
box,
tr::lng_confcall_join_button(),
joinAndClose,
&st::confcallLinkButton);
}
ConferenceCallLinkStyleOverrides DarkConferenceCallLinkStyle() {

View file

@ -129,6 +129,7 @@ using StickedTooltips = base::flags<StickedTooltip>;
void ConferenceCallJoinConfirm(
not_null<Ui::GenericBox*> box,
std::shared_ptr<Data::GroupCall> call,
UserData *maybeInviter,
Fn<void()> join);
struct ConferenceCallLinkStyleOverrides {

View file

@ -1459,8 +1459,9 @@ bool ResolveConferenceCall(
if (slug.isEmpty()) {
return false;
}
const auto myContext = context.value<ClickHandlerContext>();
controller->window().activate();
controller->resolveConferenceCall(match->captured(1));
controller->resolveConferenceCall(match->captured(1), myContext.itemId);
return true;
}
} // namespace

View file

@ -65,13 +65,15 @@ QSize Call::countOptimalSize() {
const auto user = _parent->history()->peer->asUser();
const auto conference = _conference;
const auto video = _video;
const auto contextId = _parent->data()->fullId();
const auto id = _parent->data()->id;
_link = std::make_shared<LambdaClickHandler>([=](ClickContext context) {
if (conference) {
const auto my = context.other.value<ClickHandlerContext>();
const auto weak = my.sessionWindow;
if (const auto strong = weak.get()) {
strong->resolveConferenceCall(id);
QSize();
strong->resolveConferenceCall(id, contextId);
}
} else if (user) {
Core::App().calls().startOutgoingCall(user, video);

View file

@ -55,7 +55,7 @@ using ConnectedBots = std::vector<ConnectedBot>;
rpl::producer<QString> subtitle,
bool newBadge = false);
[[nodiscard]] not_null<Ui::RoundButton*> AddFullWidthButton(
not_null<Ui::RoundButton*> AddFullWidthButton(
not_null<Ui::BoxContent*> box,
rpl::producer<QString> text,
Fn<void()> callback = nullptr,

View file

@ -311,6 +311,11 @@ boostReplaceIconSkip: 3px;
boostReplaceIconOutline: 2px;
boostReplaceIconAdd: point(4px, 2px);
boostReplaceArrow: icon{{ "mediaview/next", windowSubTextFg }};
boostReplaceUserpicsRow: UserpicsRow {
button: boostReplaceUserpic;
shift: boostReplaceUserpicsShift;
stroke: boostReplaceIconOutline;
}
showOrIconLastSeen: icon{{ "settings/premium/large_lastseen", windowFgActive }};
showOrIconReadTime: icon{{ "settings/premium/large_readtime", windowFgActive }};

View file

@ -842,21 +842,21 @@ void SessionNavigation::resolveCollectible(
void SessionNavigation::resolveConferenceCall(
QString slug,
Fn<void(bool)> finished) {
resolveConferenceCall(std::move(slug), 0, std::move(finished));
FullMsgId contextId) {
resolveConferenceCall(std::move(slug), 0, contextId);
}
void SessionNavigation::resolveConferenceCall(
MsgId inviteMsgId,
Fn<void(bool)> finished) {
resolveConferenceCall({}, inviteMsgId, std::move(finished));
FullMsgId contextId) {
resolveConferenceCall({}, inviteMsgId, contextId);
}
void SessionNavigation::resolveConferenceCall(
QString slug,
MsgId inviteMsgId,
Fn<void(bool)> finished) {
_conferenceCallResolveFinished = std::move(finished);
FullMsgId contextId) {
_conferenceCallResolveContextId = contextId;
if (_conferenceCallSlug == slug
&& _conferenceCallInviteMsgId == inviteMsgId) {
return;
@ -875,7 +875,7 @@ void SessionNavigation::resolveConferenceCall(
_conferenceCallRequestId = 0;
const auto slug = base::take(_conferenceCallSlug);
const auto inviteMsgId = base::take(_conferenceCallInviteMsgId);
const auto finished = base::take(_conferenceCallResolveFinished);
const auto contextId = base::take(_conferenceCallResolveContextId);
result.data().vcall().match([&](const auto &data) {
const auto call = session().data().sharedConferenceCall(
data.vid().v,
@ -888,22 +888,21 @@ void SessionNavigation::resolveConferenceCall(
.joinMessageId = inviteMsgId,
});
};
const auto context = session().data().message(contextId);
const auto inviter = context
? context->from()->asUser()
: nullptr;
uiShow()->show(Box(
Calls::Group::ConferenceCallJoinConfirm,
call,
(inviter && !inviter->isSelf()) ? inviter : nullptr,
join));
if (finished) {
finished(true);
}
});
}).fail([=] {
_conferenceCallRequestId = 0;
_conferenceCallSlug = QString();
const auto finished = base::take(_conferenceCallResolveFinished);
_conferenceCallResolveContextId = FullMsgId();
showToast(tr::lng_group_invite_bad_link(tr::now));
if (finished) {
finished(false);
}
}).send();
}

View file

@ -266,10 +266,10 @@ public:
Fn<void(QString)> fail = nullptr);
void resolveConferenceCall(
QString slug,
Fn<void(bool)> finished = nullptr);
FullMsgId contextId);
void resolveConferenceCall(
MsgId inviteMsgId,
Fn<void(bool)> finished = nullptr);
FullMsgId contextId);
base::weak_ptr<Ui::Toast::Instance> showToast(
Ui::Toast::Config &&config);
@ -299,7 +299,7 @@ private:
void resolveConferenceCall(
QString slug,
MsgId inviteMsgId,
Fn<void(bool)> finished);
FullMsgId contextId);
void resolveDone(
const MTPcontacts_ResolvedPeer &result,
@ -341,7 +341,7 @@ private:
QString _conferenceCallSlug;
MsgId _conferenceCallInviteMsgId;
Fn<void(bool)> _conferenceCallResolveFinished;
FullMsgId _conferenceCallResolveContextId;
mtpRequestId _conferenceCallRequestId = 0;
};