mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Allow changing the recipients.
This commit is contained in:
parent
82cec83d87
commit
b8bf3f6520
14 changed files with 386 additions and 56 deletions
|
@ -1404,10 +1404,6 @@ void Session::forgetPassportCredentials() {
|
||||||
_passportCredentials = nullptr;
|
_passportCredentials = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Session::nameSortKey(const QString &name) const {
|
|
||||||
return TextUtilities::RemoveAccents(name).toLower();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Session::setupMigrationViewer() {
|
void Session::setupMigrationViewer() {
|
||||||
session().changes().peerUpdates(
|
session().changes().peerUpdates(
|
||||||
PeerUpdate::Flag::Migration
|
PeerUpdate::Flag::Migration
|
||||||
|
|
|
@ -115,8 +115,6 @@ public:
|
||||||
return *_session;
|
return *_session;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] QString nameSortKey(const QString &name) const;
|
|
||||||
|
|
||||||
[[nodiscard]] Groups &groups() {
|
[[nodiscard]] Groups &groups() {
|
||||||
return _groups;
|
return _groups;
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,7 +98,7 @@ History::History(not_null<Data::Session*> owner, PeerId peerId)
|
||||||
: Thread(owner, Type::History)
|
: Thread(owner, Type::History)
|
||||||
, peer(owner->peer(peerId))
|
, peer(owner->peer(peerId))
|
||||||
, _delegateMixin(HistoryInner::DelegateMixin())
|
, _delegateMixin(HistoryInner::DelegateMixin())
|
||||||
, _chatListNameSortKey(owner->nameSortKey(peer->name()))
|
, _chatListNameSortKey(TextUtilities::NameSortKey(peer->name()))
|
||||||
, _sendActionPainter(this) {
|
, _sendActionPainter(this) {
|
||||||
Thread::setMuted(owner->notifySettings().isMuted(peer));
|
Thread::setMuted(owner->notifySettings().isMuted(peer));
|
||||||
|
|
||||||
|
@ -2314,7 +2314,7 @@ const QString &History::chatListNameSortKey() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void History::refreshChatListNameSortKey() {
|
void History::refreshChatListNameSortKey() {
|
||||||
_chatListNameSortKey = owner().nameSortKey(peer->name());
|
_chatListNameSortKey = TextUtilities::NameSortKey(peer->name());
|
||||||
}
|
}
|
||||||
|
|
||||||
const base::flat_set<QString> &History::chatListNameWords() const {
|
const base::flat_set<QString> &History::chatListNameWords() const {
|
||||||
|
|
|
@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "boxes/send_credits_box.h" // Ui::CreditsEmoji.
|
#include "boxes/send_credits_box.h" // Ui::CreditsEmoji.
|
||||||
#include "chat_helpers/stickers_lottie.h"
|
#include "chat_helpers/stickers_lottie.h"
|
||||||
#include "core/ui_integration.h"
|
#include "core/ui_integration.h"
|
||||||
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
|
#include "data/data_channel.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "history/view/media/history_view_sticker.h"
|
#include "history/view/media/history_view_sticker.h"
|
||||||
|
@ -21,9 +23,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "settings/settings_common.h"
|
#include "settings/settings_common.h"
|
||||||
#include "ui/boxes/confirm_box.h"
|
#include "ui/boxes/confirm_box.h"
|
||||||
#include "ui/controls/userpic_button.h"
|
#include "ui/controls/userpic_button.h"
|
||||||
|
#include "ui/controls/who_reacted_context_action.h"
|
||||||
|
#include "ui/dynamic_image.h"
|
||||||
|
#include "ui/dynamic_thumbnails.h"
|
||||||
#include "ui/layers/generic_box.h"
|
#include "ui/layers/generic_box.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "ui/wrap/padding_wrap.h"
|
#include "ui/wrap/padding_wrap.h"
|
||||||
#include "ui/wrap/table_layout.h"
|
#include "ui/wrap/table_layout.h"
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
|
@ -31,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/vertical_list.h"
|
#include "ui/vertical_list.h"
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
|
#include "styles/style_chat_helpers.h"
|
||||||
#include "styles/style_dialogs.h"
|
#include "styles/style_dialogs.h"
|
||||||
#include "styles/style_giveaway.h"
|
#include "styles/style_giveaway.h"
|
||||||
#include "styles/style_layers.h"
|
#include "styles/style_layers.h"
|
||||||
|
@ -156,6 +163,58 @@ void ConnectStarRef(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChooseRecipient(
|
||||||
|
not_null<Ui::RpWidget*> button,
|
||||||
|
const std::vector<not_null<PeerData*>> &list,
|
||||||
|
not_null<PeerData*> now,
|
||||||
|
Fn<void(not_null<PeerData*>)> done) {
|
||||||
|
const auto menu = Ui::CreateChild<Ui::PopupMenu>(
|
||||||
|
button,
|
||||||
|
st::starrefPopupMenu);
|
||||||
|
|
||||||
|
struct Entry {
|
||||||
|
not_null<Ui::WhoReactedEntryAction*> action;
|
||||||
|
std::shared_ptr<Ui::DynamicImage> userpic;
|
||||||
|
};
|
||||||
|
auto actions = std::make_shared<std::vector<Entry>>();
|
||||||
|
actions->reserve(list.size());
|
||||||
|
for (const auto &peer : list) {
|
||||||
|
auto view = peer->createUserpicView();
|
||||||
|
auto action = base::make_unique_q<Ui::WhoReactedEntryAction>(
|
||||||
|
menu->menu(),
|
||||||
|
Data::ReactedMenuFactory(&list.front()->session()),
|
||||||
|
menu->menu()->st(),
|
||||||
|
Ui::WhoReactedEntryData());
|
||||||
|
const auto index = int(actions->size());
|
||||||
|
actions->push_back({ action.get(), Ui::MakeUserpicThumbnail(peer) });
|
||||||
|
|
||||||
|
const auto updateUserpic = [=] {
|
||||||
|
const auto size = st::defaultWhoRead.photoSize;
|
||||||
|
actions->at(index).action->setData({
|
||||||
|
.text = peer->name(),
|
||||||
|
.date = (peer->isSelf()
|
||||||
|
? tr::lng_group_call_join_as_personal(tr::now)
|
||||||
|
: peer->isUser()
|
||||||
|
? tr::lng_status_bot(tr::now)
|
||||||
|
: peer->isBroadcast()
|
||||||
|
? tr::lng_channel_status(tr::now)
|
||||||
|
: tr::lng_group_status(tr::now)),
|
||||||
|
.type = (peer == now
|
||||||
|
? Ui::WhoReactedType::RefRecipientNow
|
||||||
|
: Ui::WhoReactedType::RefRecipient),
|
||||||
|
.userpic = actions->at(index).userpic->image(size),
|
||||||
|
.callback = [=] { done(peer); },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
actions->back().userpic->subscribeToUpdates(updateUserpic);
|
||||||
|
|
||||||
|
menu->addAction(std::move(action));
|
||||||
|
updateUserpic();
|
||||||
|
}
|
||||||
|
|
||||||
|
menu->popup(button->mapToGlobal(QPoint(button->width() / 2, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
QString FormatCommission(ushort commission) {
|
QString FormatCommission(ushort commission) {
|
||||||
|
@ -453,9 +512,10 @@ object_ptr<Ui::BoxContent> StarRefLinkBox(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] object_ptr<Ui::BoxContent> JoinStarRefBox(
|
object_ptr<Ui::BoxContent> JoinStarRefBox(
|
||||||
ConnectedBot row,
|
ConnectedBot row,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> initialRecipient,
|
||||||
|
std::vector<not_null<PeerData*>> recipients,
|
||||||
Fn<void(ConnectedBotState)> done) {
|
Fn<void(ConnectedBotState)> done) {
|
||||||
Expects(row.bot->isUser());
|
Expects(row.bot->isUser());
|
||||||
|
|
||||||
|
@ -464,6 +524,13 @@ object_ptr<Ui::BoxContent> StarRefLinkBox(
|
||||||
|
|
||||||
const auto bot = row.bot;
|
const auto bot = row.bot;
|
||||||
const auto program = row.state.program;
|
const auto program = row.state.program;
|
||||||
|
auto list = recipients;
|
||||||
|
if (!list.empty()) {
|
||||||
|
list.erase(ranges::remove(list, bot), end(list));
|
||||||
|
if (!ranges::contains(list, initialRecipient)) {
|
||||||
|
list.insert(begin(list), initialRecipient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
box->setStyle(st::starrefFooterBox);
|
box->setStyle(st::starrefFooterBox);
|
||||||
box->setNoContentMargin(true);
|
box->setNoContentMargin(true);
|
||||||
|
@ -471,13 +538,34 @@ object_ptr<Ui::BoxContent> StarRefLinkBox(
|
||||||
box->closeBox();
|
box->closeBox();
|
||||||
});
|
});
|
||||||
|
|
||||||
box->addRow(
|
struct State {
|
||||||
CreateUserpicsTransfer(
|
rpl::variable<not_null<PeerData*>> recipient;
|
||||||
box,
|
QPointer<Ui::GenericBox> weak;
|
||||||
rpl::single(std::vector{ not_null<PeerData*>(bot) }),
|
bool sent = false;
|
||||||
peer,
|
};
|
||||||
UserpicsTransferType::StarRefJoin),
|
const auto state = std::make_shared<State>(State{
|
||||||
st::boxRowPadding + st::starrefJoinUserpicsPadding);
|
.recipient = initialRecipient,
|
||||||
|
.weak = box.get(),
|
||||||
|
});
|
||||||
|
const auto userpicsWrap = box->addRow(
|
||||||
|
object_ptr<Ui::VerticalLayout>(box),
|
||||||
|
QMargins());
|
||||||
|
|
||||||
|
state->recipient.value(
|
||||||
|
) | rpl::start_with_next([=](not_null<PeerData*> recipient) {
|
||||||
|
while (userpicsWrap->count()) {
|
||||||
|
delete userpicsWrap->widgetAt(0);
|
||||||
|
}
|
||||||
|
userpicsWrap->add(
|
||||||
|
CreateUserpicsTransfer(
|
||||||
|
box,
|
||||||
|
rpl::single(std::vector{ not_null<PeerData*>(bot) }),
|
||||||
|
recipient,
|
||||||
|
UserpicsTransferType::StarRefJoin),
|
||||||
|
st::boxRowPadding + st::starrefJoinUserpicsPadding);
|
||||||
|
userpicsWrap->resizeToWidth(box->width());
|
||||||
|
}, box->lifetime());
|
||||||
|
|
||||||
box->addRow(
|
box->addRow(
|
||||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||||
box,
|
box,
|
||||||
|
@ -504,7 +592,7 @@ object_ptr<Ui::BoxContent> StarRefLinkBox(
|
||||||
Ui::AddSkip(box->verticalLayout(), st::defaultVerticalListSkip * 3);
|
Ui::AddSkip(box->verticalLayout(), st::defaultVerticalListSkip * 3);
|
||||||
if (const auto average = program.revenuePerUser) {
|
if (const auto average = program.revenuePerUser) {
|
||||||
const auto layout = box->verticalLayout();
|
const auto layout = box->verticalLayout();
|
||||||
const auto session = &peer->session();
|
const auto session = &initialRecipient->session();
|
||||||
const auto makeContext = [session](Fn<void()> update) {
|
const auto makeContext = [session](Fn<void()> update) {
|
||||||
return Core::MarkedTextContext{
|
return Core::MarkedTextContext{
|
||||||
.session = session,
|
.session = session,
|
||||||
|
@ -512,13 +600,14 @@ object_ptr<Ui::BoxContent> StarRefLinkBox(
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
auto text = Ui::Text::Colorized(Ui::CreditsEmoji(session));
|
auto text = Ui::Text::Colorized(Ui::CreditsEmoji(session));
|
||||||
text.append(Lang::FormatStarsAmountDecimal(average));
|
text.append(Lang::FormatStarsAmountRounded(average));
|
||||||
layout->add(
|
layout->add(
|
||||||
object_ptr<Ui::FlatLabel>(
|
object_ptr<Ui::FlatLabel>(
|
||||||
box,
|
box,
|
||||||
tr::lng_star_ref_one_daily_revenue(
|
tr::lng_star_ref_one_daily_revenue(
|
||||||
lt_amount,
|
lt_amount,
|
||||||
rpl::single(Ui::Text::Wrapped(text, EntityType::Bold)),
|
rpl::single(
|
||||||
|
Ui::Text::Wrapped(text, EntityType::Bold)),
|
||||||
Ui::Text::WithEntities),
|
Ui::Text::WithEntities),
|
||||||
st::starrefRevenueText,
|
st::starrefRevenueText,
|
||||||
st::defaultPopupMenu,
|
st::defaultPopupMenu,
|
||||||
|
@ -526,35 +615,89 @@ object_ptr<Ui::BoxContent> StarRefLinkBox(
|
||||||
st::boxRowPadding);
|
st::boxRowPadding);
|
||||||
Ui::AddSkip(layout, st::defaultVerticalListSkip);
|
Ui::AddSkip(layout, st::defaultVerticalListSkip);
|
||||||
}
|
}
|
||||||
#if 0
|
|
||||||
box->addRow(
|
|
||||||
object_ptr<Ui::FlatLabel>(
|
|
||||||
box,
|
|
||||||
tr::lng_star_ref_link_recipient(),
|
|
||||||
st::starrefCenteredText));
|
|
||||||
Ui::AddSkip(box->verticalLayout());
|
|
||||||
box->addRow(object_ptr<Ui::AbstractButton>::fromRaw(
|
|
||||||
MakePeerBubbleButton(box, peer).release()
|
|
||||||
))->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct State {
|
if (!list.empty()) {
|
||||||
QPointer<Ui::GenericBox> weak;
|
struct Name {
|
||||||
bool sent = false;
|
not_null<PeerData*> peer;
|
||||||
};
|
QString name;
|
||||||
const auto state = std::make_shared<State>();
|
};
|
||||||
state->weak = box;
|
auto names = ranges::views::transform(list, [](auto peer) {
|
||||||
|
const auto name = TextUtilities::NameSortKey(peer->name());
|
||||||
|
return Name{ peer, name };
|
||||||
|
}) | ranges::to_vector;
|
||||||
|
ranges::sort(names, ranges::less(), &Name::name);
|
||||||
|
|
||||||
|
list = ranges::views::transform(names, &Name::peer)
|
||||||
|
| ranges::to_vector;
|
||||||
|
|
||||||
|
box->addRow(
|
||||||
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
box,
|
||||||
|
tr::lng_star_ref_link_recipient(),
|
||||||
|
st::starrefCenteredText));
|
||||||
|
Ui::AddSkip(box->verticalLayout());
|
||||||
|
const auto recipientWrap = box->addRow(
|
||||||
|
object_ptr<Ui::VerticalLayout>(box),
|
||||||
|
QMargins());
|
||||||
|
state->recipient.value(
|
||||||
|
) | rpl::start_with_next([=](not_null<PeerData*> recipient) {
|
||||||
|
while (recipientWrap->count()) {
|
||||||
|
delete recipientWrap->widgetAt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto selectable = (list.size() > 1);
|
||||||
|
const auto bgOverride = selectable
|
||||||
|
? &st::lightButtonBgOver
|
||||||
|
: nullptr;
|
||||||
|
const auto right = selectable
|
||||||
|
? Ui::CreateChild<Ui::RpWidget>(recipientWrap)
|
||||||
|
: nullptr;
|
||||||
|
if (right) {
|
||||||
|
const auto skip = st::chatGiveawayPeerPadding.right();
|
||||||
|
const auto icon = &st::starrefRecipientArrow;
|
||||||
|
right->resize(skip + icon->width(), icon->height());
|
||||||
|
right->paintRequest() | rpl::start_with_next([=] {
|
||||||
|
auto p = QPainter(right);
|
||||||
|
icon->paint(p, skip, 0, right->width());
|
||||||
|
}, right->lifetime());
|
||||||
|
}
|
||||||
|
const auto button = recipientWrap->add(
|
||||||
|
object_ptr<Ui::AbstractButton>::fromRaw(
|
||||||
|
MakePeerBubbleButton(
|
||||||
|
box,
|
||||||
|
recipient,
|
||||||
|
right,
|
||||||
|
bgOverride).release()),
|
||||||
|
st::boxRowPadding);
|
||||||
|
recipientWrap->resizeToWidth(box->width());
|
||||||
|
if (!selectable) {
|
||||||
|
button->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
button->setClickedCallback([=] {
|
||||||
|
const auto callback = [=](not_null<PeerData*> peer) {
|
||||||
|
state->recipient = peer;
|
||||||
|
};
|
||||||
|
ChooseRecipient(
|
||||||
|
button,
|
||||||
|
list,
|
||||||
|
state->recipient.current(),
|
||||||
|
crl::guard(button, callback));
|
||||||
|
});
|
||||||
|
}, box->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
const auto send = [=] {
|
const auto send = [=] {
|
||||||
if (state->sent) {
|
if (state->sent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state->sent = true;
|
state->sent = true;
|
||||||
ConnectStarRef(bot->asUser(), peer, [=](ConnectedBot info) {
|
const auto recipient = state->recipient.current();
|
||||||
|
ConnectStarRef(bot->asUser(), recipient, [=](ConnectedBot info) {
|
||||||
if (const auto onstack = done) {
|
if (const auto onstack = done) {
|
||||||
onstack(info.state);
|
onstack(info.state);
|
||||||
}
|
}
|
||||||
show->show(StarRefLinkBox(info, peer));
|
show->show(StarRefLinkBox(info, recipient));
|
||||||
if (const auto strong = state->weak.data()) {
|
if (const auto strong = state->weak.data()) {
|
||||||
strong->closeBox();
|
strong->closeBox();
|
||||||
}
|
}
|
||||||
|
@ -624,18 +767,103 @@ object_ptr<Ui::BoxContent> ConfirmEndBox(Fn<void()> finish) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResolveRecipients(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
Fn<void(std::vector<not_null<PeerData*>>)> done) {
|
||||||
|
struct State {
|
||||||
|
not_null<Main::Session*> session;
|
||||||
|
std::vector<not_null<PeerData*>> list;
|
||||||
|
Fn<void(std::vector<not_null<PeerData*>>)> done;
|
||||||
|
};
|
||||||
|
const auto state = std::make_shared<State>(State{
|
||||||
|
.session = session,
|
||||||
|
.done = std::move(done),
|
||||||
|
});
|
||||||
|
const auto finish1 = [state](const MTPmessages_Chats &result) {
|
||||||
|
const auto already = int(state->list.size());
|
||||||
|
const auto session = state->session;
|
||||||
|
result.match([&](const auto &data) {
|
||||||
|
const auto &list = data.vchats().v;
|
||||||
|
state->list.reserve(list.size() + (already ? already : 1));
|
||||||
|
if (!already) {
|
||||||
|
state->list.push_back(session->user());
|
||||||
|
}
|
||||||
|
for (const auto &chat : list) {
|
||||||
|
const auto peer = session->data().processChat(chat);
|
||||||
|
if (const auto channel = peer->asBroadcast()) {
|
||||||
|
if (channel->canPostMessages()) {
|
||||||
|
state->list.push_back(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (already) {
|
||||||
|
base::take(state->done)(base::take(state->list));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const auto finish2 = [state](const MTPVector<MTPUser> &result) {
|
||||||
|
const auto already = int(state->list.size());
|
||||||
|
const auto session = state->session;
|
||||||
|
const auto &list = result.v;
|
||||||
|
state->list.reserve(list.size() + (already ? already : 1));
|
||||||
|
if (!already) {
|
||||||
|
state->list.push_back(session->user());
|
||||||
|
}
|
||||||
|
for (const auto &user : list) {
|
||||||
|
state->list.push_back(session->data().processUser(user));
|
||||||
|
}
|
||||||
|
if (already) {
|
||||||
|
base::take(state->done)(base::take(state->list));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
session->api().request(MTPchannels_GetAdminedPublicChannels(
|
||||||
|
MTP_flags(0)
|
||||||
|
)).done(finish1).fail([=] {
|
||||||
|
finish1(MTP_messages_chats(MTP_vector<MTPChat>(0)));
|
||||||
|
}).send();
|
||||||
|
|
||||||
|
state->session->api().request(MTPbots_GetAdminedBots(
|
||||||
|
)).done(finish2).fail([=] {
|
||||||
|
finish2(MTP_vector<MTPUser>(0));
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<Ui::AbstractButton> MakePeerBubbleButton(
|
std::unique_ptr<Ui::AbstractButton> MakePeerBubbleButton(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
Ui::RpWidget *right) {
|
Ui::RpWidget *right,
|
||||||
auto result = std::make_unique<Ui::AbstractButton>(parent);
|
const style::color *bgOverride) {
|
||||||
|
class Button final : public Ui::AbstractButton {
|
||||||
|
public:
|
||||||
|
Button(QWidget *parent, not_null<int*> innerWidth)
|
||||||
|
: AbstractButton(parent)
|
||||||
|
, _innerWidth(innerWidth) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void mouseMoveEvent(QMouseEvent *e) override {
|
||||||
|
const auto inner = *_innerWidth;
|
||||||
|
const auto skip = (width() - inner) / 2;
|
||||||
|
const auto p = e->pos();
|
||||||
|
const auto over = QRect(skip, 0, inner, height()).contains(p);
|
||||||
|
setOver(over, StateChangeSource::ByHover);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const not_null<int*> _innerWidth;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
auto ownedWidth = std::make_unique<int>();
|
||||||
|
const auto width = ownedWidth.get();
|
||||||
|
auto result = std::make_unique<Button>(parent, width);
|
||||||
|
result->lifetime().add([moved = std::move(ownedWidth)] {});
|
||||||
|
|
||||||
const auto size = st::chatGiveawayPeerSize;
|
const auto size = st::chatGiveawayPeerSize;
|
||||||
const auto padding = st::chatGiveawayPeerPadding;
|
const auto padding = st::chatGiveawayPeerPadding;
|
||||||
|
|
||||||
const auto raw = result.get();
|
const auto raw = result.get();
|
||||||
|
|
||||||
const auto width = raw->lifetime().make_state<int>();
|
|
||||||
const auto name = raw->lifetime().make_state<Ui::FlatLabel>(
|
const auto name = raw->lifetime().make_state<Ui::FlatLabel>(
|
||||||
raw,
|
raw,
|
||||||
rpl::single(peer->name()),
|
rpl::single(peer->name()),
|
||||||
|
@ -691,7 +919,7 @@ std::unique_ptr<Ui::AbstractButton> MakePeerBubbleButton(
|
||||||
p.setClipRect(left + skip, 0, *width - skip, size);
|
p.setClipRect(left + skip, 0, *width - skip, size);
|
||||||
auto hq = PainterHighQualityEnabler(p);
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
p.setPen(Qt::NoPen);
|
p.setPen(Qt::NoPen);
|
||||||
p.setBrush(st::windowBgOver);
|
p.setBrush(bgOverride ? *bgOverride : st::windowBgOver);
|
||||||
p.drawRoundedRect(left, 0, *width, size, skip, skip);
|
p.drawRoundedRect(left, 0, *width, size, skip, skip);
|
||||||
}, raw->lifetime());
|
}, raw->lifetime());
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,10 @@ namespace style {
|
||||||
struct RoundButton;
|
struct RoundButton;
|
||||||
} // namespace style
|
} // namespace style
|
||||||
|
|
||||||
|
namespace Main {
|
||||||
|
class Session;
|
||||||
|
} // namespace Main
|
||||||
|
|
||||||
namespace Info::BotStarRef {
|
namespace Info::BotStarRef {
|
||||||
|
|
||||||
struct ConnectedBotState {
|
struct ConnectedBotState {
|
||||||
|
@ -65,14 +69,20 @@ void AddFullWidthButtonFooter(
|
||||||
not_null<PeerData*> peer);
|
not_null<PeerData*> peer);
|
||||||
[[nodiscard]] object_ptr<Ui::BoxContent> JoinStarRefBox(
|
[[nodiscard]] object_ptr<Ui::BoxContent> JoinStarRefBox(
|
||||||
ConnectedBot row,
|
ConnectedBot row,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> initialRecipient,
|
||||||
|
std::vector<not_null<PeerData*>> recipients,
|
||||||
Fn<void(ConnectedBotState)> done = nullptr);
|
Fn<void(ConnectedBotState)> done = nullptr);
|
||||||
[[nodiscard]] object_ptr<Ui::BoxContent> ConfirmEndBox(Fn<void()> finish);
|
[[nodiscard]] object_ptr<Ui::BoxContent> ConfirmEndBox(Fn<void()> finish);
|
||||||
|
|
||||||
|
void ResolveRecipients(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
Fn<void(std::vector<not_null<PeerData*>>)> done);
|
||||||
|
|
||||||
std::unique_ptr<Ui::AbstractButton> MakePeerBubbleButton(
|
std::unique_ptr<Ui::AbstractButton> MakePeerBubbleButton(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
Ui::RpWidget *right = nullptr);
|
Ui::RpWidget *right = nullptr,
|
||||||
|
const style::color *bgOverride = nullptr);
|
||||||
|
|
||||||
void ConfirmUpdate(
|
void ConfirmUpdate(
|
||||||
std::shared_ptr<Ui::Show> show,
|
std::shared_ptr<Ui::Show> show,
|
||||||
|
|
|
@ -87,6 +87,7 @@ public:
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] std::unique_ptr<PeerListRow> createRow(ConnectedBot bot);
|
[[nodiscard]] std::unique_ptr<PeerListRow> createRow(ConnectedBot bot);
|
||||||
void open(not_null<UserData*> bot, ConnectedBotState state);
|
void open(not_null<UserData*> bot, ConnectedBotState state);
|
||||||
|
void requestRecipients();
|
||||||
void setupAddForBot();
|
void setupAddForBot();
|
||||||
|
|
||||||
const not_null<Window::SessionController*> _controller;
|
const not_null<Window::SessionController*> _controller;
|
||||||
|
@ -97,6 +98,8 @@ private:
|
||||||
base::flat_set<not_null<PeerData*>> _resolving;
|
base::flat_set<not_null<PeerData*>> _resolving;
|
||||||
UserData *_openOnResolve = nullptr;
|
UserData *_openOnResolve = nullptr;
|
||||||
|
|
||||||
|
Fn<void()> _recipientsReady;
|
||||||
|
std::vector<not_null<PeerData*>> _recipients;
|
||||||
rpl::event_stream<ConnectedBot> _connected;
|
rpl::event_stream<ConnectedBot> _connected;
|
||||||
rpl::event_stream<> _addForBot;
|
rpl::event_stream<> _addForBot;
|
||||||
|
|
||||||
|
@ -104,6 +107,7 @@ private:
|
||||||
TimeId _offsetDate = 0;
|
TimeId _offsetDate = 0;
|
||||||
QString _offsetThing;
|
QString _offsetThing;
|
||||||
bool _allLoaded = false;
|
bool _allLoaded = false;
|
||||||
|
bool _recipientsRequested = false;
|
||||||
|
|
||||||
rpl::variable<int> _rowCount = 0;
|
rpl::variable<int> _rowCount = 0;
|
||||||
|
|
||||||
|
@ -328,17 +332,50 @@ void ListController::rowClicked(not_null<PeerListRow*> row) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListController::open(not_null<UserData*> bot, ConnectedBotState state) {
|
void ListController::open(not_null<UserData*> bot, ConnectedBotState state) {
|
||||||
|
const auto show = _controller->uiShow();
|
||||||
if (_type == JoinType::Joined || !state.link.isEmpty()) {
|
if (_type == JoinType::Joined || !state.link.isEmpty()) {
|
||||||
_controller->show(StarRefLinkBox({ bot, state }, _peer));
|
_recipientsReady = nullptr;
|
||||||
|
show->show(StarRefLinkBox({ bot, state }, _peer));
|
||||||
} else {
|
} else {
|
||||||
|
const auto requireOthers = (_type == JoinType::Existing)
|
||||||
|
|| _peer->isSelf();
|
||||||
|
const auto requestOthers = requireOthers && _recipients.empty();
|
||||||
|
if (requestOthers) {
|
||||||
|
_recipientsReady = [=] {
|
||||||
|
Expects(!_recipients.empty());
|
||||||
|
|
||||||
|
open(bot, state);
|
||||||
|
};
|
||||||
|
requestRecipients();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const auto connected = crl::guard(this, [=](ConnectedBotState now) {
|
const auto connected = crl::guard(this, [=](ConnectedBotState now) {
|
||||||
_states[bot] = now;
|
_states[bot] = now;
|
||||||
_connected.fire({ bot, now });
|
_connected.fire({ bot, now });
|
||||||
});
|
});
|
||||||
_controller->show(JoinStarRefBox({ bot, state }, _peer, connected));
|
show->show(JoinStarRefBox(
|
||||||
|
{ bot, state },
|
||||||
|
_peer,
|
||||||
|
requireOthers ? _recipients : std::vector<not_null<PeerData*>>(),
|
||||||
|
connected));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ListController::requestRecipients() {
|
||||||
|
if (_recipientsRequested) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_recipientsRequested = true;
|
||||||
|
const auto session = &this->session();
|
||||||
|
ResolveRecipients(session, crl::guard(this, [=](
|
||||||
|
std::vector<not_null<PeerData*>> list) {
|
||||||
|
_recipients = std::move(list);
|
||||||
|
if (const auto callback = base::take(_recipientsReady)) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
void RevokeLink(
|
void RevokeLink(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
|
|
|
@ -2142,6 +2142,29 @@ void ActionsFiller::addAffiliateProgram(not_null<UserData*> user) {
|
||||||
});
|
});
|
||||||
const auto show = _controller->uiShow();
|
const auto show = _controller->uiShow();
|
||||||
|
|
||||||
|
struct StarRefRecipients {
|
||||||
|
std::vector<not_null<PeerData*>> list;
|
||||||
|
bool requested = false;
|
||||||
|
Fn<void()> open;
|
||||||
|
};
|
||||||
|
const auto recipients = std::make_shared<StarRefRecipients>();
|
||||||
|
recipients->open = [=] {
|
||||||
|
if (!recipients->list.empty()) {
|
||||||
|
const auto program = user->botInfo->starRefProgram;
|
||||||
|
show->show(Info::BotStarRef::JoinStarRefBox(
|
||||||
|
{ user, { program } },
|
||||||
|
user->session().user(),
|
||||||
|
recipients->list));
|
||||||
|
} else if (!recipients->requested) {
|
||||||
|
recipients->requested = true;
|
||||||
|
const auto done = [=](std::vector<not_null<PeerData*>> list) {
|
||||||
|
recipients->list = std::move(list);
|
||||||
|
recipients->open();
|
||||||
|
};
|
||||||
|
Info::BotStarRef::ResolveRecipients(&user->session(), done);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Ui::AddSkip(inner);
|
Ui::AddSkip(inner);
|
||||||
::Settings::AddButtonWithLabel(
|
::Settings::AddButtonWithLabel(
|
||||||
inner,
|
inner,
|
||||||
|
@ -2149,12 +2172,7 @@ void ActionsFiller::addAffiliateProgram(not_null<UserData*> user) {
|
||||||
rpl::duplicate(commission),
|
rpl::duplicate(commission),
|
||||||
st::infoSharedMediaButton,
|
st::infoSharedMediaButton,
|
||||||
{ &st::menuIconSharing }
|
{ &st::menuIconSharing }
|
||||||
)->setClickedCallback([=] {
|
)->setClickedCallback(recipients->open);
|
||||||
const auto program = user->botInfo->starRefProgram;
|
|
||||||
show->show(Info::BotStarRef::JoinStarRefBox(
|
|
||||||
{ user, { program } },
|
|
||||||
user->session().user()));
|
|
||||||
});
|
|
||||||
Ui::AddSkip(inner);
|
Ui::AddSkip(inner);
|
||||||
Ui::AddDividerText(
|
Ui::AddDividerText(
|
||||||
inner,
|
inner,
|
||||||
|
|
|
@ -963,6 +963,11 @@ QString FormatStarsAmountDecimal(StarsAmount amount) {
|
||||||
return FormatExactCountDecimal(amount.value());
|
return FormatExactCountDecimal(amount.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString FormatStarsAmountRounded(StarsAmount amount) {
|
||||||
|
const auto value = amount.value();
|
||||||
|
return FormatExactCountDecimal(base::SafeRound(value * 100.) / 100.);
|
||||||
|
}
|
||||||
|
|
||||||
PluralResult Plural(
|
PluralResult Plural(
|
||||||
ushort keyBase,
|
ushort keyBase,
|
||||||
float64 value,
|
float64 value,
|
||||||
|
|
|
@ -31,6 +31,7 @@ struct ShortenedCount {
|
||||||
[[nodiscard]] QString FormatExactCountDecimal(float64 number);
|
[[nodiscard]] QString FormatExactCountDecimal(float64 number);
|
||||||
[[nodiscard]] ShortenedCount FormatStarsAmountToShort(StarsAmount amount);
|
[[nodiscard]] ShortenedCount FormatStarsAmountToShort(StarsAmount amount);
|
||||||
[[nodiscard]] QString FormatStarsAmountDecimal(StarsAmount amount);
|
[[nodiscard]] QString FormatStarsAmountDecimal(StarsAmount amount);
|
||||||
|
[[nodiscard]] QString FormatStarsAmountRounded(StarsAmount amount);
|
||||||
|
|
||||||
struct PluralResult {
|
struct PluralResult {
|
||||||
int keyShift = 0;
|
int keyShift = 0;
|
||||||
|
|
|
@ -1177,6 +1177,8 @@ botEmojiStatusUserpic: UserpicButton(defaultUserpicButton) {
|
||||||
photoSize: chatGiveawayPeerSize;
|
photoSize: chatGiveawayPeerSize;
|
||||||
}
|
}
|
||||||
botEmojiStatusName: FlatLabel(defaultFlatLabel) {
|
botEmojiStatusName: FlatLabel(defaultFlatLabel) {
|
||||||
|
minWidth: 32px;
|
||||||
|
maxHeight: 20px;
|
||||||
}
|
}
|
||||||
botEmojiStatusEmoji: FlatLabel(botEmojiStatusName) {
|
botEmojiStatusEmoji: FlatLabel(botEmojiStatusName) {
|
||||||
textFg: profileVerifiedCheckBg;
|
textFg: profileVerifiedCheckBg;
|
||||||
|
|
|
@ -796,7 +796,8 @@ void WhoReactedEntryAction::paint(Painter &&p) {
|
||||||
if (selected && _st.itemBgOver->c.alpha() < 255) {
|
if (selected && _st.itemBgOver->c.alpha() < 255) {
|
||||||
p.fillRect(0, 0, width(), _height, _st.itemBg);
|
p.fillRect(0, 0, width(), _height, _st.itemBg);
|
||||||
}
|
}
|
||||||
p.fillRect(0, 0, width(), _height, selected ? _st.itemBgOver : _st.itemBg);
|
const auto bg = selected ? _st.itemBgOver : _st.itemBg;
|
||||||
|
p.fillRect(0, 0, width(), _height, bg);
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
paintRipple(p, 0, 0);
|
paintRipple(p, 0, 0);
|
||||||
}
|
}
|
||||||
|
@ -817,6 +818,18 @@ void WhoReactedEntryAction::paint(Painter &&p) {
|
||||||
p.drawEllipse(photoLeft, photoTop, photoSize, photoSize);
|
p.drawEllipse(photoLeft, photoTop, photoSize, photoSize);
|
||||||
} else if (!_userpic.isNull()) {
|
} else if (!_userpic.isNull()) {
|
||||||
p.drawImage(photoLeft, photoTop, _userpic);
|
p.drawImage(photoLeft, photoTop, _userpic);
|
||||||
|
if (_type == WhoReactedType::RefRecipientNow) {
|
||||||
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
p.setBrush(Qt::NoBrush);
|
||||||
|
auto bgPen = bg->p;
|
||||||
|
bgPen.setWidthF(st::lineWidth * 6.);
|
||||||
|
p.setPen(bgPen);
|
||||||
|
p.drawEllipse(photoLeft, photoTop, photoSize, photoSize);
|
||||||
|
auto fgPen = st::windowBgActive->p;
|
||||||
|
fgPen.setWidthF(st::lineWidth * 2.);
|
||||||
|
p.setPen(fgPen);
|
||||||
|
p.drawEllipse(photoLeft, photoTop, photoSize, photoSize);
|
||||||
|
}
|
||||||
} else if (!_custom) {
|
} else if (!_custom) {
|
||||||
st::menuIconReactions.paintInCenter(
|
st::menuIconReactions.paintInCenter(
|
||||||
p,
|
p,
|
||||||
|
@ -852,7 +865,16 @@ void WhoReactedEntryAction::paint(Painter &&p) {
|
||||||
_textWidth,
|
_textWidth,
|
||||||
width());
|
width());
|
||||||
}
|
}
|
||||||
if (withDate) {
|
if (_type == WhoReactedType::RefRecipient
|
||||||
|
|| _type == WhoReactedType::RefRecipientNow) {
|
||||||
|
p.setPen(selected ? _st.itemFgShortcutOver : _st.itemFgShortcut);
|
||||||
|
_date.drawLeftElided(
|
||||||
|
p,
|
||||||
|
st::defaultWhoRead.nameLeft,
|
||||||
|
st::whoReadDateTop,
|
||||||
|
_textWidth,
|
||||||
|
width());
|
||||||
|
} else if (withDate) {
|
||||||
const auto iconPosition = QPoint(
|
const auto iconPosition = QPoint(
|
||||||
st::defaultWhoRead.nameLeft,
|
st::defaultWhoRead.nameLeft,
|
||||||
st::whoReadDateTop) + st::whoReadDateChecksPosition;
|
st::whoReadDateTop) + st::whoReadDateChecksPosition;
|
||||||
|
|
|
@ -74,6 +74,8 @@ enum class WhoReactedType : uchar {
|
||||||
Reposted,
|
Reposted,
|
||||||
Forwarded,
|
Forwarded,
|
||||||
Preloader,
|
Preloader,
|
||||||
|
RefRecipient,
|
||||||
|
RefRecipientNow,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WhoReactedEntryData {
|
struct WhoReactedEntryData {
|
||||||
|
|
|
@ -475,6 +475,17 @@ starrefLinkCountIcon: icon{{ "chat/mini_subscribers", historyPeerUserpicFg }};
|
||||||
starrefLinkCountIconPosition: point(0px, 1px);
|
starrefLinkCountIconPosition: point(0px, 1px);
|
||||||
starrefLinkCountFont: font(10px bold);
|
starrefLinkCountFont: font(10px bold);
|
||||||
starrefLinkCountPadding: margins(2px, 0px, 3px, 1px);
|
starrefLinkCountPadding: margins(2px, 0px, 3px, 1px);
|
||||||
|
starrefRecipientBg: lightButtonBgOver;
|
||||||
|
starrefRecipientBgDisabled: windowBgOver;
|
||||||
|
starrefRecipientArrow: icon{{ "calendar_down", lightButtonFg }};
|
||||||
|
|
||||||
starrefAddForBotIcon: icon {{ "menu/bot_add", lightButtonFg }};
|
starrefAddForBotIcon: icon {{ "menu/bot_add", lightButtonFg }};
|
||||||
starrefAddForBotIconPosition: point(23px, 2px);
|
starrefAddForBotIconPosition: point(23px, 2px);
|
||||||
|
|
||||||
|
starrefPopupMenu: PopupMenu(defaultPopupMenu) {
|
||||||
|
maxHeight: 320px;
|
||||||
|
menu: Menu(defaultMenu) {
|
||||||
|
widthMin: 156px;
|
||||||
|
widthMax: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 3254aebf55133066e0a8775cabdfcef568b79625
|
Subproject commit 93c1299a39258199d0336b33dc49cab029371785
|
Loading…
Add table
Reference in a new issue