mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-13 04:37:11 +02:00
Merge tag 'v5.8.3' into dev
This commit is contained in:
commit
9db8cc6707
54 changed files with 1553 additions and 521 deletions
|
@ -374,6 +374,8 @@ PRIVATE
|
|||
boxes/peer_list_box.h
|
||||
boxes/peer_list_controllers.cpp
|
||||
boxes/peer_list_controllers.h
|
||||
boxes/peer_list_widgets.cpp
|
||||
boxes/peer_list_widgets.h
|
||||
boxes/peer_lists_box.cpp
|
||||
boxes/peer_lists_box.h
|
||||
boxes/passcode_box.cpp
|
||||
|
@ -1631,6 +1633,8 @@ PRIVATE
|
|||
ui/widgets/label_with_custom_emoji.h
|
||||
ui/widgets/chat_filters_tabs_strip.cpp
|
||||
ui/widgets/chat_filters_tabs_strip.h
|
||||
ui/widgets/peer_bubble.cpp
|
||||
ui/widgets/peer_bubble.h
|
||||
ui/countryinput.cpp
|
||||
ui/countryinput.h
|
||||
ui/dynamic_thumbnails.cpp
|
||||
|
|
|
@ -290,6 +290,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_error_cant_add_admin_invite" = "You can't add this user as an admin because they are not a member of this group and you are not allowed to add them.";
|
||||
"lng_error_cant_add_admin_unban" = "Sorry, you can't add this user as an admin because they are in the Removed Users list and you can't unban them.";
|
||||
"lng_error_cant_ban_admin" = "You can't ban this user because they are an admin in this group and you are not allowed to demote them.";
|
||||
"lng_error_cant_reply_other" = "This message can't be replied in another chat.";
|
||||
"lng_error_admin_limit" = "Sorry, you've reached the maximum number of admins for this group.";
|
||||
"lng_error_admin_limit_channel" = "Sorry, you've reached the maximum number of admins for this channel.";
|
||||
"lng_error_post_link_invalid" = "Unfortunately, you can't access this message. You aren't a member of the chat where it was posted.";
|
||||
|
@ -298,6 +299,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_error_nocopy_group" = "Sorry, copying from this group is disabled by admins.";
|
||||
"lng_error_nocopy_channel" = "Sorry, copying from this channel is disabled by admins.";
|
||||
"lng_error_nocopy_story" = "Sorry, the creator of this story disabled copying.";
|
||||
"lng_error_schedule_limit" = "Sorry, you can't schedule more than 100 messages.";
|
||||
"lng_sure_add_admin_invite" = "This user is not a member of this group. Add them to the group and promote them to admin?";
|
||||
"lng_sure_add_admin_invite_channel" = "This user is not a subscriber of this channel. Add them to the channel and promote them to admin?";
|
||||
"lng_sure_add_admin_unremove" = "This user is currently restricted or removed. Are you sure you want to promote them?";
|
||||
|
@ -1861,6 +1863,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_action_payment_init_recurring_for" = "You successfully transferred {amount} to {user} for {invoice} and allowed future recurring payments";
|
||||
"lng_action_payment_init_recurring" = "You successfully transferred {amount} to {user} and allowed future recurring payments";
|
||||
"lng_action_payment_used_recurring" = "You were charged {amount} via recurring payment";
|
||||
"lng_action_payment_bot_done" = "Bot connected to this account received {amount}";
|
||||
"lng_action_payment_bot_recurring" = "Bot connected to this account received {amount} via recurring payment";
|
||||
"lng_action_took_screenshot" = "{from} took a screenshot!";
|
||||
"lng_action_you_took_screenshot" = "You took a screenshot!";
|
||||
"lng_action_bot_allowed_from_domain" = "You allowed this bot to message you when you logged in on {domain}.";
|
||||
|
@ -2444,6 +2448,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_credits_box_out_media#other" = "Do you want to unlock {media} in {chat} for **{count} Stars**?";
|
||||
"lng_credits_box_out_media_user#one" = "Do you want to unlock {media} from {user} for **{count} Star**?";
|
||||
"lng_credits_box_out_media_user#other" = "Do you want to unlock {media} from {user} for **{count} Stars**?";
|
||||
"lng_credits_box_out_subscription_bot#one" = "Do you want to subscribe to **{title}** in **{recipient}** for **{count}** star per month?";
|
||||
"lng_credits_box_out_subscription_bot#other" = "Do you want to subscribe to **{title}** in **{recipient}** for **{count}** stars per month?";
|
||||
"lng_credits_box_out_subscription_business#one" = "Do you want to subscribe to **{title}** from **{recipient}** for **{count}** star per month?";
|
||||
"lng_credits_box_out_subscription_business#other" = "Do you want to subscribe to **{title}** from **{recipient}** for **{count}** stars per month?";
|
||||
"lng_credits_box_out_subscription_confirm#one" = "Subscribe for {emoji} {count} / month";
|
||||
"lng_credits_box_out_subscription_confirm#other" = "Subscribe for {emoji} {count} / month";
|
||||
"lng_credits_box_out_photo" = "a photo";
|
||||
"lng_credits_box_out_photos#one" = "{count} photo";
|
||||
"lng_credits_box_out_photos#other" = "{count} photos";
|
||||
|
@ -2504,6 +2514,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_credits_subscriber_subtitle" = "appx. {total} per month";
|
||||
|
||||
"lng_credits_subscription_row_to" = "Subscription";
|
||||
"lng_credits_subscription_row_to_bot" = "Bot";
|
||||
"lng_credits_subscription_row_to_business" = "Business";
|
||||
"lng_credits_subscription_row_from" = "Subscribed";
|
||||
|
||||
"lng_credits_subscription_row_next_on" = "Renews";
|
||||
|
@ -2514,13 +2526,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_credits_subscription_on_about" = "If you cancel now, you will still be able to access your subscription until {date}.";
|
||||
|
||||
"lng_credits_subscription_off_button" = "Renew Subscription";
|
||||
"lng_credits_subscription_off_rejoin_button" = "Subscribe again";
|
||||
"lng_credits_subscription_off_about" = "You have canceled your subscription.";
|
||||
"lng_credits_subscription_off_by_bot_about" = "{bot} has canceled your subscription.";
|
||||
|
||||
"lng_credits_subscription_status_on" = "renews on {date}";
|
||||
"lng_credits_subscription_status_off" = "expires on {date}";
|
||||
"lng_credits_subscription_status_none" = "expired on {date}";
|
||||
"lng_credits_subscription_status_off_right" = "canceled";
|
||||
"lng_credits_subscription_status_none_right" = "expired";
|
||||
"lng_credits_subscription_status_off_by_bot_right" = "canceled\nby bot";
|
||||
|
||||
"lng_credits_small_balance_title#one" = "{count} Star Needed";
|
||||
"lng_credits_small_balance_title#other" = "{count} Stars Needed";
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="5.8.2.0" />
|
||||
Version="5.8.3.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
|
|
@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
|||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 5,8,2,0
|
||||
PRODUCTVERSION 5,8,2,0
|
||||
FILEVERSION 5,8,3,0
|
||||
PRODUCTVERSION 5,8,3,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
@ -62,10 +62,10 @@ BEGIN
|
|||
BEGIN
|
||||
VALUE "CompanyName", "Radolyn Labs"
|
||||
VALUE "FileDescription", "AyuGram Desktop"
|
||||
VALUE "FileVersion", "5.8.2.0"
|
||||
VALUE "FileVersion", "5.8.3.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
||||
VALUE "ProductName", "AyuGram Desktop"
|
||||
VALUE "ProductVersion", "5.8.2.0"
|
||||
VALUE "ProductVersion", "5.8.3.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
|
|
@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
|||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 5,8,2,0
|
||||
PRODUCTVERSION 5,8,2,0
|
||||
FILEVERSION 5,8,3,0
|
||||
PRODUCTVERSION 5,8,3,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
@ -53,10 +53,10 @@ BEGIN
|
|||
BEGIN
|
||||
VALUE "CompanyName", "Radolyn Labs"
|
||||
VALUE "FileDescription", "AyuGram Desktop Updater"
|
||||
VALUE "FileVersion", "5.8.2.0"
|
||||
VALUE "FileVersion", "5.8.3.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
||||
VALUE "ProductName", "AyuGram Desktop"
|
||||
VALUE "ProductVersion", "5.8.2.0"
|
||||
VALUE "ProductVersion", "5.8.3.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "api/api_chat_invite.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "api/api_credits.h"
|
||||
#include "boxes/premium_limits_box.h"
|
||||
#include "core/application.h"
|
||||
#include "data/components/credits.h"
|
||||
|
@ -295,20 +296,39 @@ void ConfirmSubscriptionBox(
|
|||
const auto buttonWidth = state->saveButton
|
||||
? state->saveButton->width()
|
||||
: 0;
|
||||
const auto finish = [=] {
|
||||
state->api = std::nullopt;
|
||||
state->loading.force_assign(false);
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
};
|
||||
state->api->request(
|
||||
MTPpayments_SendStarsForm(
|
||||
MTP_long(formId),
|
||||
MTP_inputInvoiceChatInviteSubscription(MTP_string(hash)))
|
||||
).done([=](const MTPpayments_PaymentResult &result) {
|
||||
state->api = std::nullopt;
|
||||
state->loading.force_assign(false);
|
||||
result.match([&](const MTPDpayments_paymentResult &data) {
|
||||
session->api().applyUpdates(data.vupdates());
|
||||
}, [](const MTPDpayments_paymentVerificationNeeded &data) {
|
||||
});
|
||||
if (weak) {
|
||||
box->closeBox();
|
||||
const auto refill = session->data().activeCreditsSubsRebuilder();
|
||||
const auto strong = weak.data();
|
||||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
if (!refill) {
|
||||
return finish();
|
||||
}
|
||||
const auto api
|
||||
= strong->lifetime().make_state<Api::CreditsHistory>(
|
||||
session->user(),
|
||||
true,
|
||||
true);
|
||||
api->requestSubscriptions({}, [=](Data::CreditsStatusSlice d) {
|
||||
refill->fire(std::move(d));
|
||||
finish();
|
||||
});
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
const auto id = error.type();
|
||||
if (weak) {
|
||||
|
|
|
@ -133,17 +133,26 @@ constexpr auto kTransactionsLimit = 100;
|
|||
}
|
||||
|
||||
[[nodiscard]] Data::SubscriptionEntry SubscriptionFromTL(
|
||||
const MTPStarsSubscription &tl) {
|
||||
const MTPStarsSubscription &tl,
|
||||
not_null<PeerData*> peer) {
|
||||
return Data::SubscriptionEntry{
|
||||
.id = qs(tl.data().vid()),
|
||||
.inviteHash = qs(tl.data().vchat_invite_hash().value_or_empty()),
|
||||
.title = qs(tl.data().vtitle().value_or_empty()),
|
||||
.slug = qs(tl.data().vinvoice_slug().value_or_empty()),
|
||||
.until = base::unixtime::parse(tl.data().vuntil_date().v),
|
||||
.subscription = Data::PeerSubscription{
|
||||
.credits = tl.data().vpricing().data().vamount().v,
|
||||
.period = tl.data().vpricing().data().vperiod().v,
|
||||
},
|
||||
.barePeerId = peerFromMTP(tl.data().vpeer()).value,
|
||||
.photoId = (tl.data().vphoto()
|
||||
? peer->owner().photoFromWeb(
|
||||
*tl.data().vphoto(),
|
||||
ImageLocation())->id
|
||||
: 0),
|
||||
.cancelled = tl.data().is_canceled(),
|
||||
.cancelledByBot = tl.data().is_bot_canceled(),
|
||||
.expired = (base::unixtime::now() > tl.data().vuntil_date().v),
|
||||
.canRefulfill = tl.data().is_can_refulfill(),
|
||||
};
|
||||
|
@ -166,7 +175,7 @@ constexpr auto kTransactionsLimit = 100;
|
|||
if (const auto history = data.vsubscriptions()) {
|
||||
subscriptions.reserve(history->v.size());
|
||||
for (const auto &tl : history->v) {
|
||||
subscriptions.push_back(SubscriptionFromTL(tl));
|
||||
subscriptions.push_back(SubscriptionFromTL(tl, peer));
|
||||
}
|
||||
}
|
||||
return Data::CreditsStatusSlice{
|
||||
|
@ -463,4 +472,20 @@ Data::CreditsGiveawayOptions CreditsGiveawayOptions::options() const {
|
|||
return _options;
|
||||
}
|
||||
|
||||
void EditCreditsSubscription(
|
||||
not_null<Main::Session*> session,
|
||||
const QString &id,
|
||||
bool cancel,
|
||||
Fn<void()> done,
|
||||
Fn<void(QString)> fail) {
|
||||
using Flag = MTPpayments_ChangeStarsSubscription::Flag;
|
||||
session->api().request(
|
||||
MTPpayments_ChangeStarsSubscription(
|
||||
MTP_flags(Flag::f_canceled),
|
||||
MTP_inputPeerSelf(),
|
||||
MTP_string(id),
|
||||
MTP_bool(cancel)
|
||||
)).done(done).fail([=](const MTP::Error &e) { fail(e.type()); }).send();
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
|
|
@ -109,4 +109,11 @@ private:
|
|||
[[nodiscard]] rpl::producer<not_null<PeerData*>> PremiumPeerBot(
|
||||
not_null<Main::Session*> session);
|
||||
|
||||
void EditCreditsSubscription(
|
||||
not_null<Main::Session*> session,
|
||||
const QString &id,
|
||||
bool cancel,
|
||||
Fn<void()> done,
|
||||
Fn<void(QString)> fail);
|
||||
|
||||
} // namespace Api
|
||||
|
|
|
@ -582,6 +582,14 @@ void ApiWrap::sendMessageFail(
|
|||
: tr::lng_error_noforwards_group(tr::now), kJoinErrorDuration);
|
||||
} else if (error == u"PREMIUM_ACCOUNT_REQUIRED"_q) {
|
||||
Settings::ShowPremium(&session(), "premium_stickers");
|
||||
} else if (error == u"SCHEDULE_TOO_MUCH"_q) {
|
||||
auto &scheduled = _session->scheduledMessages();
|
||||
if (const auto item = scheduled.lookupItem(peer->id, itemId.msg)) {
|
||||
scheduled.removeSending(item);
|
||||
}
|
||||
if (show) {
|
||||
show->showToast(tr::lng_error_schedule_limit(tr::now));
|
||||
}
|
||||
}
|
||||
if (const auto item = _session->data().message(itemId)) {
|
||||
Assert(randomId != 0);
|
||||
|
|
|
@ -7,7 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "boxes/filters/edit_filter_chats_list.h"
|
||||
|
||||
#include "data/data_chat_filters.h"
|
||||
#include "data/data_premium_limits.h"
|
||||
#include "data/data_session.h"
|
||||
#include "history/history.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "lang/lang_keys.h"
|
||||
|
@ -125,7 +127,15 @@ Flag TypeRow::flag() const {
|
|||
}
|
||||
|
||||
ExceptionRow::ExceptionRow(not_null<History*> history) : Row(history) {
|
||||
if (peer()->isSelf()) {
|
||||
auto filters = QStringList();
|
||||
for (const auto &filter : history->owner().chatsFilters().list()) {
|
||||
if (filter.contains(history) && filter.id()) {
|
||||
filters << filter.title();
|
||||
}
|
||||
}
|
||||
if (!filters.isEmpty()) {
|
||||
setCustomStatus(filters.join(", "));
|
||||
} else if (peer()->isSelf()) {
|
||||
setCustomStatus(tr::lng_saved_forward_here(tr::now));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1247,25 +1247,20 @@ void AddCreditsHistoryEntryTable(
|
|||
}));
|
||||
}
|
||||
}
|
||||
if (!entry.subscriptionUntil.isNull() && !entry.title.isEmpty()) {
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_gift_link_label_reason(),
|
||||
tr::lng_credits_box_history_entry_subscription(
|
||||
Ui::Text::WithEntities));
|
||||
}
|
||||
if (!entry.id.isEmpty()) {
|
||||
constexpr auto kOneLineCount = 22;
|
||||
const auto oneLine = entry.id.size() <= kOneLineCount;
|
||||
auto multiLine = QString();
|
||||
if (!oneLine) {
|
||||
for (auto i = 0; i < entry.id.size(); ++i) {
|
||||
multiLine.append(entry.id[i]);
|
||||
if ((i + 1) % kOneLineCount == 0) {
|
||||
multiLine.append('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
constexpr auto kOneLineCount = 24;
|
||||
const auto oneLine = entry.id.length() <= kOneLineCount;
|
||||
auto label = object_ptr<Ui::FlatLabel>(
|
||||
table,
|
||||
rpl::single(
|
||||
Ui::Text::Wrapped(
|
||||
{ oneLine ? entry.id : std::move(multiLine) },
|
||||
EntityType::Code,
|
||||
{})),
|
||||
Ui::Text::Wrapped({ entry.id }, EntityType::Code, {})),
|
||||
oneLine
|
||||
? st::giveawayGiftCodeValue
|
||||
: st::giveawayGiftCodeValueMultiline);
|
||||
|
@ -1324,11 +1319,24 @@ void AddSubscriptionEntryTable(
|
|||
st::giveawayGiftCodeTable),
|
||||
st::giveawayGiftCodeTableMargin);
|
||||
const auto peerId = PeerId(s.barePeerId);
|
||||
const auto user = peerIsUser(peerId)
|
||||
? controller->session().data().peer(peerId)->asUser()
|
||||
: nullptr;
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_credits_subscription_row_to(),
|
||||
(!s.title.isEmpty() && user && user->botInfo)
|
||||
? tr::lng_credits_subscription_row_to_bot()
|
||||
: (!s.title.isEmpty() && user && !user->botInfo)
|
||||
? tr::lng_credits_subscription_row_to_business()
|
||||
: tr::lng_credits_subscription_row_to(),
|
||||
controller,
|
||||
peerId);
|
||||
if (!s.title.isEmpty()) {
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_credits_subscription_row_to(),
|
||||
rpl::single(Ui::Text::WithEntities(s.title)));
|
||||
}
|
||||
if (!s.until.isNull()) {
|
||||
if (s.subscription.period > 0) {
|
||||
const auto subscribed = s.until.addSecs(-s.subscription.period);
|
||||
|
|
388
Telegram/SourceFiles/boxes/peer_list_widgets.cpp
Normal file
388
Telegram/SourceFiles/boxes/peer_list_widgets.cpp
Normal file
|
@ -0,0 +1,388 @@
|
|||
/*
|
||||
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/peer_list_widgets.h"
|
||||
|
||||
#include "ui/painter.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using State = std::unique_ptr<PeerListState>;
|
||||
|
||||
} // namespace
|
||||
|
||||
PeerListWidgets::PeerListWidgets(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<PeerListController*> controller)
|
||||
: Ui::RpWidget(parent)
|
||||
, _controller(controller)
|
||||
, _st(controller->computeListSt()) {
|
||||
_content = base::make_unique_q<Ui::VerticalLayout>(this);
|
||||
parent->sizeValue() | rpl::start_with_next([this](const QSize &size) {
|
||||
_content->resizeToWidth(size.width());
|
||||
resize(size.width(), _content->height());
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
crl::time PeerListWidgets::paintRow(
|
||||
Painter &p,
|
||||
crl::time now,
|
||||
bool selected,
|
||||
not_null<PeerListRow*> row) {
|
||||
const auto &st = row->computeSt(_st.item);
|
||||
|
||||
row->lazyInitialize(st);
|
||||
const auto outerWidth = _content->width();
|
||||
const auto w = outerWidth;
|
||||
|
||||
auto refreshStatusAt = row->refreshStatusTime();
|
||||
if (refreshStatusAt > 0 && now >= refreshStatusAt) {
|
||||
row->refreshStatus();
|
||||
refreshStatusAt = row->refreshStatusTime();
|
||||
}
|
||||
const auto refreshStatusIn = (refreshStatusAt > 0)
|
||||
? std::max(refreshStatusAt - now, crl::time(1))
|
||||
: 0;
|
||||
|
||||
row->paintUserpic(
|
||||
p,
|
||||
st,
|
||||
st.photoPosition.x(),
|
||||
st.photoPosition.y(),
|
||||
outerWidth);
|
||||
|
||||
p.setPen(st::contactsNameFg);
|
||||
|
||||
const auto skipRight = st.photoPosition.x();
|
||||
const auto rightActionSize = row->rightActionSize();
|
||||
const auto rightActionMargins = rightActionSize.isEmpty()
|
||||
? QMargins()
|
||||
: row->rightActionMargins();
|
||||
const auto &name = row->name();
|
||||
const auto namePosition = st.namePosition;
|
||||
const auto namex = namePosition.x();
|
||||
const auto namey = namePosition.y();
|
||||
auto namew = outerWidth - namex - skipRight;
|
||||
if (!rightActionSize.isEmpty()
|
||||
&& (namey < rightActionMargins.top() + rightActionSize.height())
|
||||
&& (namey + st.nameStyle.font->height
|
||||
> rightActionMargins.top())) {
|
||||
namew -= rightActionMargins.left()
|
||||
+ rightActionSize.width()
|
||||
+ rightActionMargins.right()
|
||||
- skipRight;
|
||||
}
|
||||
const auto statusx = st.statusPosition.x();
|
||||
const auto statusy = st.statusPosition.y();
|
||||
auto statusw = outerWidth - statusx - skipRight;
|
||||
if (!rightActionSize.isEmpty()
|
||||
&& (statusy < rightActionMargins.top() + rightActionSize.height())
|
||||
&& (statusy + st::contactsStatusFont->height
|
||||
> rightActionMargins.top())) {
|
||||
statusw -= rightActionMargins.left()
|
||||
+ rightActionSize.width()
|
||||
+ rightActionMargins.right()
|
||||
- skipRight;
|
||||
}
|
||||
namew -= row->paintNameIconGetWidth(
|
||||
p,
|
||||
[=] { updateRow(row); },
|
||||
now,
|
||||
namex,
|
||||
namey,
|
||||
name.maxWidth(),
|
||||
namew,
|
||||
w,
|
||||
selected);
|
||||
auto nameCheckedRatio = row->disabled() ? 0. : row->checkedRatio();
|
||||
p.setPen(anim::pen(st.nameFg, st.nameFgChecked, nameCheckedRatio));
|
||||
name.drawLeftElided(p, namex, namey, namew, w);
|
||||
|
||||
p.setFont(st::contactsStatusFont);
|
||||
row->paintStatusText(p, st, statusx, statusy, statusw, w, selected);
|
||||
|
||||
row->elementsPaint(p, outerWidth, selected, 0);
|
||||
|
||||
return refreshStatusIn;
|
||||
}
|
||||
|
||||
void PeerListWidgets::appendRow(std::unique_ptr<PeerListRow> row) {
|
||||
Expects(row != nullptr);
|
||||
|
||||
if (_rowsById.find(row->id()) == _rowsById.cend()) {
|
||||
const auto raw = row.get();
|
||||
const auto &st = raw->computeSt(_st.item);
|
||||
raw->setAbsoluteIndex(_rows.size());
|
||||
_rows.push_back(std::move(row));
|
||||
|
||||
const auto widget = _content->add(
|
||||
object_ptr<Ui::AbstractButton>::fromRaw(
|
||||
Ui::CreateSimpleSettingsButton(
|
||||
_content.get(),
|
||||
st.button.ripple,
|
||||
st.button.textBgOver)));
|
||||
widget->resize(widget->width(), st.height);
|
||||
widget->paintRequest() | rpl::start_with_next([=, this] {
|
||||
auto p = Painter(widget);
|
||||
const auto selected = widget->isOver() || widget->isDown();
|
||||
paintRow(p, crl::now(), selected, raw);
|
||||
}, widget->lifetime());
|
||||
|
||||
widget->setClickedCallback([this, raw] {
|
||||
_controller->rowClicked(raw);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
PeerListRow *PeerListWidgets::findRow(PeerListRowId id) {
|
||||
const auto it = _rowsById.find(id);
|
||||
return (it == _rowsById.cend()) ? nullptr : it->second.get();
|
||||
}
|
||||
|
||||
void PeerListWidgets::updateRow(not_null<PeerListRow*> row) {
|
||||
const auto it = ranges::find_if(
|
||||
_rows,
|
||||
[row](const auto &r) { return r.get() == row; });
|
||||
if (it != _rows.end()) {
|
||||
const auto index = std::distance(_rows.begin(), it);
|
||||
if (const auto widget = _content->widgetAt(index)) {
|
||||
widget->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int PeerListWidgets::fullRowsCount() {
|
||||
return _rows.size();
|
||||
}
|
||||
|
||||
[[nodiscard]] not_null<PeerListRow*> PeerListWidgets::rowAt(int index) {
|
||||
Expects(index >= 0 && index < _rows.size());
|
||||
|
||||
return _rows[index].get();
|
||||
}
|
||||
|
||||
void PeerListWidgets::refreshRows() {
|
||||
_content->resizeToWidth(width());
|
||||
resize(width(), _content->height());
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::setContent(PeerListWidgets *content) {
|
||||
_content = content;
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::setUiShow(
|
||||
std::shared_ptr<Main::SessionShow> uiShow) {
|
||||
_uiShow = std::move(uiShow);
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListSetHideEmpty(bool hide) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListSetHideEmpty");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListAppendRow(
|
||||
std::unique_ptr<PeerListRow> row) {
|
||||
_content->appendRow(std::move(row));
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListAppendSearchRow(
|
||||
std::unique_ptr<PeerListRow> row) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListAppendSearchRow");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListAppendFoundRow(
|
||||
not_null<PeerListRow*> row) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListAppendFoundRow");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListPrependRow(
|
||||
std::unique_ptr<PeerListRow> row) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListPrependRow");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListPrependRowFromSearchResult(
|
||||
not_null<PeerListRow*> row) {
|
||||
Unexpected(
|
||||
"...PeerListWidgetsDelegate::peerListPrependRowFromSearchResult");
|
||||
}
|
||||
|
||||
PeerListRow* PeerListWidgetsDelegate::peerListFindRow(PeerListRowId id) {
|
||||
return _content->findRow(id);
|
||||
}
|
||||
|
||||
auto PeerListWidgetsDelegate::peerListLastRowMousePosition()
|
||||
-> std::optional<QPoint> {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListLastRowMousePosition");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListUpdateRow(not_null<PeerListRow*> row) {
|
||||
_content->updateRow(row);
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListRemoveRow(not_null<PeerListRow*> row) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListRemoveRow");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListConvertRowToSearchResult(
|
||||
not_null<PeerListRow*> row) {
|
||||
Unexpected(
|
||||
"...PeerListWidgetsDelegate::peerListConvertRowToSearchResult");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListSetRowChecked(
|
||||
not_null<PeerListRow*> row,
|
||||
bool checked) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListSetRowChecked");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListSetRowHidden(
|
||||
not_null<PeerListRow*> row,
|
||||
bool hidden) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListSetRowHidden");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListSetForeignRowChecked(
|
||||
not_null<PeerListRow*> row,
|
||||
bool checked,
|
||||
anim::type animated) {
|
||||
}
|
||||
|
||||
int PeerListWidgetsDelegate::peerListFullRowsCount() {
|
||||
return _content->fullRowsCount();
|
||||
}
|
||||
|
||||
not_null<PeerListRow*> PeerListWidgetsDelegate::peerListRowAt(int index) {
|
||||
return _content->rowAt(index);
|
||||
}
|
||||
|
||||
int PeerListWidgetsDelegate::peerListSearchRowsCount() {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListSearchRowsCount");
|
||||
}
|
||||
|
||||
not_null<PeerListRow*> PeerListWidgetsDelegate::peerListSearchRowAt(int) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListSearchRowAt");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListRefreshRows() {
|
||||
_content->refreshRows();
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListSetDescription(
|
||||
object_ptr<Ui::FlatLabel>) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListSetDescription");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListSetSearchNoResults(
|
||||
object_ptr<Ui::FlatLabel>) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListSetSearchNoResults");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListSetAboveWidget(
|
||||
object_ptr<Ui::RpWidget>) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListSetAboveWidget");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListSetAboveSearchWidget(
|
||||
object_ptr<Ui::RpWidget>) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListSetAboveSearchWidget");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListSetBelowWidget(
|
||||
object_ptr<Ui::RpWidget>) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListSetBelowWidget");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListSetSearchMode(PeerListSearchMode mode) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListSetSearchMode");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListMouseLeftGeometry() {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListMouseLeftGeometry");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListSortRows(
|
||||
Fn<bool(const PeerListRow &, const PeerListRow &)>) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListSortRows");
|
||||
}
|
||||
|
||||
int PeerListWidgetsDelegate::peerListPartitionRows(
|
||||
Fn<bool(const PeerListRow &a)> border) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListPartitionRows");
|
||||
}
|
||||
|
||||
State PeerListWidgetsDelegate::peerListSaveState() const {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListSaveState");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListRestoreState(
|
||||
std::unique_ptr<PeerListState> state) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListRestoreState");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListShowRowMenu(
|
||||
not_null<PeerListRow*> row,
|
||||
bool highlightRow,
|
||||
Fn<void(not_null<Ui::PopupMenu*>)> destroyed) {
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListSelectSkip(int direction) {
|
||||
// _content->selectSkip(direction);
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListPressLeftToContextMenu(bool shown) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListPressLeftToContextMenu");
|
||||
}
|
||||
|
||||
bool PeerListWidgetsDelegate::peerListTrackRowPressFromGlobal(QPoint) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<Main::SessionShow> PeerListWidgetsDelegate::peerListUiShow() {
|
||||
Expects(_uiShow != nullptr);
|
||||
return _uiShow;
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListAddSelectedPeerInBunch(
|
||||
not_null<PeerData*> peer) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListAddSelectedPeerInBunch");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListAddSelectedRowInBunch(
|
||||
not_null<PeerListRow*> row) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListAddSelectedRowInBunch");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListFinishSelectedRowsBunch() {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListFinishSelectedRowsBunch");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListSetTitle(rpl::producer<QString> title) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListSetTitle");
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListSetAdditionalTitle(
|
||||
rpl::producer<QString> title) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListSetAdditionalTitle");
|
||||
}
|
||||
|
||||
bool PeerListWidgetsDelegate::peerListIsRowChecked(
|
||||
not_null<PeerListRow*> row) {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListIsRowChecked");
|
||||
return false;
|
||||
}
|
||||
|
||||
void PeerListWidgetsDelegate::peerListScrollToTop() {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListScrollToTop");
|
||||
}
|
||||
|
||||
int PeerListWidgetsDelegate::peerListSelectedRowsCount() {
|
||||
Unexpected("...PeerListWidgetsDelegate::peerListSelectedRowsCount");
|
||||
return 0;
|
||||
}
|
110
Telegram/SourceFiles/boxes/peer_list_widgets.h
Normal file
110
Telegram/SourceFiles/boxes/peer_list_widgets.h
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
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 "boxes/peer_list_box.h"
|
||||
|
||||
namespace Ui {
|
||||
class VerticalLayout;
|
||||
} // namespace Ui
|
||||
|
||||
class PeerListWidgets : public Ui::RpWidget {
|
||||
public:
|
||||
PeerListWidgets(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<PeerListController*> controller);
|
||||
|
||||
crl::time paintRow(
|
||||
Painter &p,
|
||||
crl::time now,
|
||||
bool selected,
|
||||
not_null<PeerListRow*> row);
|
||||
void appendRow(std::unique_ptr<PeerListRow> row);
|
||||
PeerListRow* findRow(PeerListRowId id);
|
||||
void updateRow(not_null<PeerListRow*> row);
|
||||
int fullRowsCount();
|
||||
[[nodiscard]] not_null<PeerListRow*> rowAt(int index);
|
||||
void refreshRows();
|
||||
|
||||
private:
|
||||
const not_null<PeerListController*> _controller;
|
||||
const style::PeerList &_st;
|
||||
base::unique_qptr<Ui::VerticalLayout> _content;
|
||||
|
||||
std::vector<std::unique_ptr<PeerListRow>> _rows;
|
||||
std::map<PeerListRowId, not_null<PeerListRow*>> _rowsById;
|
||||
std::map<PeerData*, std::vector<not_null<PeerListRow*>>> _rowsByPeer;
|
||||
};
|
||||
|
||||
class PeerListWidgetsDelegate : public PeerListDelegate {
|
||||
public:
|
||||
void setContent(PeerListWidgets *content);
|
||||
void setUiShow(std::shared_ptr<Main::SessionShow> uiShow);
|
||||
|
||||
void peerListSetHideEmpty(bool hide) override;
|
||||
void peerListAppendRow(std::unique_ptr<PeerListRow> row) override;
|
||||
void peerListAppendSearchRow(std::unique_ptr<PeerListRow> row) override;
|
||||
void peerListAppendFoundRow(not_null<PeerListRow*> row) override;
|
||||
void peerListPrependRow(std::unique_ptr<PeerListRow> row) override;
|
||||
void peerListPrependRowFromSearchResult(
|
||||
not_null<PeerListRow*> row) override;
|
||||
PeerListRow *peerListFindRow(PeerListRowId id) override;
|
||||
std::optional<QPoint> peerListLastRowMousePosition() override;
|
||||
void peerListUpdateRow(not_null<PeerListRow*> row) override;
|
||||
void peerListRemoveRow(not_null<PeerListRow*> row) override;
|
||||
void peerListConvertRowToSearchResult(
|
||||
not_null<PeerListRow*> row) override;
|
||||
void peerListSetRowChecked(
|
||||
not_null<PeerListRow*> row,
|
||||
bool checked) override;
|
||||
void peerListSetRowHidden(
|
||||
not_null<PeerListRow*> row,
|
||||
bool hidden) override;
|
||||
void peerListSetForeignRowChecked(
|
||||
not_null<PeerListRow*> row,
|
||||
bool checked,
|
||||
anim::type animated) override;
|
||||
int peerListFullRowsCount() override;
|
||||
not_null<PeerListRow*> peerListRowAt(int index) override;
|
||||
int peerListSearchRowsCount() override;
|
||||
not_null<PeerListRow*> peerListSearchRowAt(int index) override;
|
||||
void peerListRefreshRows() override;
|
||||
void peerListSetDescription(object_ptr<Ui::FlatLabel>) override;
|
||||
void peerListSetSearchNoResults(object_ptr<Ui::FlatLabel>) override;
|
||||
void peerListSetAboveWidget(object_ptr<Ui::RpWidget>) override;
|
||||
void peerListSetAboveSearchWidget(object_ptr<Ui::RpWidget>) override;
|
||||
void peerListSetBelowWidget(object_ptr<Ui::RpWidget>) override;
|
||||
void peerListSetSearchMode(PeerListSearchMode mode) override;
|
||||
void peerListMouseLeftGeometry() override;
|
||||
void peerListSortRows(
|
||||
Fn<bool(const PeerListRow &, const PeerListRow &)>) override;
|
||||
int peerListPartitionRows(Fn<bool(const PeerListRow &a)> border) override;
|
||||
std::unique_ptr<PeerListState> peerListSaveState() const override;
|
||||
void peerListRestoreState(std::unique_ptr<PeerListState> state) override;
|
||||
void peerListShowRowMenu(
|
||||
not_null<PeerListRow*> row,
|
||||
bool highlightRow,
|
||||
Fn<void(not_null<Ui::PopupMenu*>)> destroyed = nullptr) override;
|
||||
void peerListSelectSkip(int direction) override;
|
||||
void peerListPressLeftToContextMenu(bool shown) override;
|
||||
bool peerListTrackRowPressFromGlobal(QPoint globalPosition) override;
|
||||
std::shared_ptr<Main::SessionShow> peerListUiShow() override;
|
||||
void peerListAddSelectedPeerInBunch(not_null<PeerData*> peer) override;
|
||||
void peerListAddSelectedRowInBunch(not_null<PeerListRow*> row) override;
|
||||
void peerListFinishSelectedRowsBunch() override;
|
||||
void peerListSetTitle(rpl::producer<QString> title) override;
|
||||
void peerListSetAdditionalTitle(rpl::producer<QString> title) override;
|
||||
bool peerListIsRowChecked(not_null<PeerListRow*> row) override;
|
||||
void peerListScrollToTop() override;
|
||||
int peerListSelectedRowsCount() override;
|
||||
|
||||
private:
|
||||
PeerListWidgets *_content = nullptr;
|
||||
std::shared_ptr<Main::SessionShow> _uiShow;
|
||||
|
||||
};
|
|
@ -26,17 +26,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "settings/settings_credits_graphics.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/controls/userpic_button.h"
|
||||
#include "ui/effects/credits_graphics.h"
|
||||
#include "ui/effects/premium_graphics.h"
|
||||
#include "ui/effects/premium_top_bar.h" // Ui::Premium::ColorizedSvg.
|
||||
#include "ui/image/image_prepare.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/peer_bubble.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_credits.h"
|
||||
#include "styles/style_giveaway.h"
|
||||
#include "styles/style_info.h" // inviteLinkSubscribeBoxTerms
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_premium.h"
|
||||
#include "styles/style_settings.h"
|
||||
|
@ -92,6 +97,44 @@ struct PaidMediaData {
|
|||
};
|
||||
}
|
||||
|
||||
void AddTerms(
|
||||
not_null<Ui::BoxContent*> box,
|
||||
not_null<Ui::RpWidget*> button,
|
||||
const style::Box &stBox) {
|
||||
const auto terms = Ui::CreateChild<Ui::FlatLabel>(
|
||||
button->parentWidget(),
|
||||
tr::lng_channel_invite_subscription_terms(
|
||||
lt_link,
|
||||
rpl::combine(
|
||||
tr::lng_paid_react_agree_link(),
|
||||
tr::lng_group_invite_subscription_about_url()
|
||||
) | rpl::map([](const QString &text, const QString &url) {
|
||||
return Ui::Text::Link(text, url);
|
||||
}),
|
||||
Ui::Text::RichLangValue),
|
||||
st::inviteLinkSubscribeBoxTerms);
|
||||
const auto &buttonPadding = stBox.buttonPadding;
|
||||
const auto style = box->lifetime().make_state<style::Box>(style::Box{
|
||||
.buttonPadding = buttonPadding + QMargins(0, 0, 0, terms->height()),
|
||||
.buttonHeight = stBox.buttonHeight,
|
||||
.button = stBox.button,
|
||||
.margin = stBox.margin,
|
||||
.title = stBox.title,
|
||||
.bg = stBox.bg,
|
||||
.titleAdditionalFg = stBox.titleAdditionalFg,
|
||||
.shadowIgnoreTopSkip = stBox.shadowIgnoreTopSkip,
|
||||
.shadowIgnoreBottomSkip = stBox.shadowIgnoreBottomSkip,
|
||||
});
|
||||
button->geometryValue() | rpl::start_with_next([=](const QRect &rect) {
|
||||
terms->resizeToWidth(box->width()
|
||||
- rect::m::sum::h(st::boxRowPadding));
|
||||
terms->moveToLeft(
|
||||
rect.x() + (rect.width() - terms->width()) / 2,
|
||||
rect::bottom(rect) + buttonPadding.bottom() / 2);
|
||||
}, terms->lifetime());
|
||||
box->setStyle(*style);
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::producer<TextWithEntities> SendCreditsConfirmText(
|
||||
not_null<Main::Session*> session,
|
||||
not_null<Payments::CreditsFormData*> form) {
|
||||
|
@ -150,6 +193,18 @@ struct PaidMediaData {
|
|||
}
|
||||
|
||||
const auto bot = session->data().user(form->botId);
|
||||
if (form->invoice.subscriptionPeriod) {
|
||||
return (bot->botInfo
|
||||
? tr::lng_credits_box_out_subscription_bot
|
||||
: tr::lng_credits_box_out_subscription_business)(
|
||||
lt_count,
|
||||
rpl::single(form->invoice.amount) | tr::to_count(),
|
||||
lt_title,
|
||||
rpl::single(TextWithEntities{ form->title }),
|
||||
lt_recipient,
|
||||
rpl::single(TextWithEntities{ bot->name() }),
|
||||
Ui::Text::RichLangValue);
|
||||
}
|
||||
return tr::lng_credits_box_out_sure(
|
||||
lt_count,
|
||||
rpl::single(form->invoice.amount) | tr::to_count(),
|
||||
|
@ -190,6 +245,57 @@ struct PaidMediaData {
|
|||
st::defaultUserpicButton);
|
||||
}
|
||||
|
||||
[[nodiscard]] not_null<Ui::RpWidget*> SendCreditsBadge(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
int credits) {
|
||||
const auto widget = Ui::CreateChild<Ui::RpWidget>(parent);
|
||||
const auto &font = st::chatGiveawayBadgeFont;
|
||||
const auto text = QString::number(credits);
|
||||
const auto iconHeight = font->ascent - font->descent;
|
||||
const auto iconWidth = iconHeight + st::lineWidth;
|
||||
const auto width = font->width(text) + iconWidth + st::lineWidth;
|
||||
const auto inner = QRect(0, 0, width, font->height);
|
||||
const auto rect = inner + st::subscriptionCreditsBadgePadding;
|
||||
const auto size = rect.size();
|
||||
const auto svg = widget->lifetime().make_state<QSvgRenderer>(
|
||||
Ui::Premium::Svg());
|
||||
const auto half = st::chatGiveawayBadgeStroke / 2.;
|
||||
const auto left = st::subscriptionCreditsBadgePadding.left();
|
||||
const auto smaller = QRectF(rect.translated(-rect.topLeft()))
|
||||
- Margins(half);
|
||||
const auto radius = smaller.height() / 2.;
|
||||
widget->resize(size);
|
||||
|
||||
widget->paintRequest() | rpl::start_with_next([=] {
|
||||
auto p = QPainter(widget);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(QPen(st::premiumButtonFg, st::chatGiveawayBadgeStroke * 1.));
|
||||
p.setBrush(st::creditsBg3);
|
||||
p.drawRoundedRect(smaller, radius, radius);
|
||||
|
||||
p.translate(0, font->descent / 2);
|
||||
|
||||
p.setPen(st::premiumButtonFg);
|
||||
p.setBrush(st::premiumButtonFg);
|
||||
svg->render(
|
||||
&p,
|
||||
QRect(
|
||||
left,
|
||||
half + (inner.height() - iconHeight) / 2,
|
||||
iconHeight,
|
||||
iconHeight));
|
||||
|
||||
p.setFont(font);
|
||||
p.drawText(
|
||||
left + iconWidth,
|
||||
st::subscriptionCreditsBadgePadding.top() + font->ascent,
|
||||
text);
|
||||
|
||||
}, widget->lifetime());
|
||||
|
||||
return widget;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void SendCreditsBox(
|
||||
|
@ -203,7 +309,8 @@ void SendCreditsBox(
|
|||
rpl::variable<bool> confirmButtonBusy = false;
|
||||
};
|
||||
const auto state = box->lifetime().make_state<State>();
|
||||
box->setStyle(st::giveawayGiftCodeBox);
|
||||
const auto &stBox = st::giveawayGiftCodeBox;
|
||||
box->setStyle(stBox);
|
||||
box->setNoContentMargin(true);
|
||||
|
||||
const auto session = form->invoice.session;
|
||||
|
@ -245,14 +352,36 @@ void SendCreditsBox(
|
|||
content,
|
||||
SendCreditsThumbnail(content, session, form.get(), photoSize)));
|
||||
thumb->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
if (form->invoice.subscriptionPeriod) {
|
||||
const auto badge = SendCreditsBadge(content, form->invoice.amount);
|
||||
thumb->geometryValue() | rpl::start_with_next([=](const QRect &r) {
|
||||
badge->moveToLeft(
|
||||
r.x() + (r.width() - badge->width()) / 2,
|
||||
rect::bottom(r) - badge->height() / 2);
|
||||
}, badge->lifetime());
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
}
|
||||
|
||||
Ui::AddSkip(content);
|
||||
box->addRow(object_ptr<Ui::CenterWrap<>>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_credits_box_out_title(),
|
||||
form->invoice.subscriptionPeriod
|
||||
? rpl::single(form->title)
|
||||
: tr::lng_credits_box_out_title(),
|
||||
st::settingsPremiumUserTitle)));
|
||||
if (form->invoice.subscriptionPeriod && form->botId && form->photo) {
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
const auto bot = session->data().user(form->botId);
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
box,
|
||||
Ui::CreatePeerBubble(box, bot)));
|
||||
Ui::AddSkip(content);
|
||||
}
|
||||
Ui::AddSkip(content);
|
||||
box->addRow(object_ptr<Ui::CenterWrap<>>(
|
||||
box,
|
||||
|
@ -306,6 +435,9 @@ void SendCreditsBox(
|
|||
}
|
||||
}).send();
|
||||
});
|
||||
if (form->invoice.subscriptionPeriod) {
|
||||
AddTerms(box, button, stBox);
|
||||
}
|
||||
{
|
||||
using namespace Info::Statistics;
|
||||
const auto loadingAnimation = InfiniteRadialAnimationWidget(
|
||||
|
@ -317,12 +449,14 @@ void SendCreditsBox(
|
|||
SetButtonMarkedLabel(
|
||||
button,
|
||||
rpl::combine(
|
||||
tr::lng_credits_box_out_confirm(
|
||||
lt_count,
|
||||
rpl::single(form->invoice.amount) | tr::to_count(),
|
||||
lt_emoji,
|
||||
rpl::single(CreditsEmojiSmall(session)),
|
||||
Ui::Text::RichLangValue),
|
||||
(form->invoice.subscriptionPeriod
|
||||
? tr::lng_credits_box_out_subscription_confirm
|
||||
: tr::lng_credits_box_out_confirm)(
|
||||
lt_count,
|
||||
rpl::single(form->invoice.amount) | tr::to_count(),
|
||||
lt_emoji,
|
||||
rpl::single(CreditsEmojiSmall(session)),
|
||||
Ui::Text::RichLangValue),
|
||||
state->confirmButtonBusy.value()
|
||||
) | rpl::map([](TextWithEntities &&text, bool busy) {
|
||||
return busy ? TextWithEntities() : std::move(text);
|
||||
|
@ -332,7 +466,7 @@ void SendCreditsBox(
|
|||
box->getDelegate()->style().button.textFg->c);
|
||||
|
||||
const auto buttonWidth = st::boxWidth
|
||||
- rect::m::sum::h(st::giveawayGiftCodeBox.buttonPadding);
|
||||
- rect::m::sum::h(stBox.buttonPadding);
|
||||
button->widthValue() | rpl::filter([=] {
|
||||
return (button->widthNoMargins() != buttonWidth);
|
||||
}) | rpl::start_with_next([=] {
|
||||
|
@ -341,15 +475,11 @@ void SendCreditsBox(
|
|||
|
||||
{
|
||||
const auto close = Ui::CreateChild<Ui::IconButton>(
|
||||
box.get(),
|
||||
content,
|
||||
st::boxTitleClose);
|
||||
close->setClickedCallback([=] {
|
||||
box->closeBox();
|
||||
});
|
||||
box->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
close->setClickedCallback([=] { box->closeBox(); });
|
||||
content->widthValue() | rpl::start_with_next([=](int) {
|
||||
close->moveToRight(0, 0);
|
||||
close->raise();
|
||||
}, close->lifetime());
|
||||
}
|
||||
|
||||
|
|
|
@ -669,7 +669,8 @@ bool Instance::inCall() const {
|
|||
return false;
|
||||
}
|
||||
const auto state = _currentCall->state();
|
||||
return (state != Call::State::Busy);
|
||||
return (state != Call::State::Busy)
|
||||
&& (state != Call::State::WaitingUserConfirmation);
|
||||
}
|
||||
|
||||
bool Instance::inGroupCall() const {
|
||||
|
|
|
@ -993,7 +993,12 @@ void Panel::paint(QRect clip) {
|
|||
|
||||
bool Panel::handleClose() const {
|
||||
if (_call) {
|
||||
window()->hide();
|
||||
if (_call->state() == Call::State::WaitingUserConfirmation
|
||||
|| _call->state() == Call::State::Busy) {
|
||||
_call->hangup();
|
||||
} else {
|
||||
window()->hide();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -1028,6 +1033,7 @@ void Panel::stateChanged(State state) {
|
|||
_startVideo = base::make_unique_q<Ui::CallButton>(
|
||||
widget(),
|
||||
st::callStartVideo);
|
||||
_startVideo->show();
|
||||
_startVideo->setText(tr::lng_call_start_video());
|
||||
_startVideo->clicks() | rpl::map_to(true) | rpl::start_to_stream(
|
||||
_startOutgoingRequests,
|
||||
|
|
|
@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D666}"_cs;
|
|||
constexpr auto AppNameOld = "AyuGram for Windows"_cs;
|
||||
constexpr auto AppName = "AyuGram Desktop"_cs;
|
||||
constexpr auto AppFile = "AyuGram"_cs;
|
||||
constexpr auto AppVersion = 5008002;
|
||||
constexpr auto AppVersionStr = "5.8.2";
|
||||
constexpr auto AppVersion = 5008003;
|
||||
constexpr auto AppVersionStr = "5.8.3";
|
||||
constexpr auto AppBetaVersion = false;
|
||||
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
||||
|
|
|
@ -1980,6 +1980,19 @@ rpl::producer<> Session::pinnedDialogsOrderUpdated() const {
|
|||
return _pinnedDialogsOrderUpdated.events();
|
||||
}
|
||||
|
||||
Session::CreditsSubsRebuilderPtr Session::createCreditsSubsRebuilder() {
|
||||
if (auto result = activeCreditsSubsRebuilder()) {
|
||||
return result;
|
||||
}
|
||||
auto result = std::make_shared<CreditsSubsRebuilder>();
|
||||
_creditsSubsRebuilder = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
Session::CreditsSubsRebuilderPtr Session::activeCreditsSubsRebuilder() const {
|
||||
return _creditsSubsRebuilder.lock();
|
||||
}
|
||||
|
||||
void Session::registerHeavyViewPart(not_null<ViewElement*> view) {
|
||||
_heavyViewParts.emplace(view);
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@ class Chatbots;
|
|||
class BusinessInfo;
|
||||
struct ReactionId;
|
||||
struct UnavailableReason;
|
||||
struct CreditsStatusSlice;
|
||||
|
||||
struct RepliesReadTillUpdate {
|
||||
FullMsgId id;
|
||||
|
@ -337,6 +338,11 @@ public:
|
|||
void notifyPinnedDialogsOrderUpdated();
|
||||
[[nodiscard]] rpl::producer<> pinnedDialogsOrderUpdated() const;
|
||||
|
||||
using CreditsSubsRebuilder = rpl::event_stream<Data::CreditsStatusSlice>;
|
||||
using CreditsSubsRebuilderPtr = std::shared_ptr<CreditsSubsRebuilder>;
|
||||
[[nodiscard]] CreditsSubsRebuilderPtr createCreditsSubsRebuilder();
|
||||
[[nodiscard]] CreditsSubsRebuilderPtr activeCreditsSubsRebuilder() const;
|
||||
|
||||
void registerRestricted(
|
||||
not_null<const HistoryItem*> item,
|
||||
const QString &reason);
|
||||
|
@ -1095,6 +1101,8 @@ private:
|
|||
|
||||
MessageIdsList _mimeForwardIds;
|
||||
|
||||
std::weak_ptr<CreditsSubsRebuilder> _creditsSubsRebuilder;
|
||||
|
||||
using CredentialsWithGeneration = std::pair<
|
||||
const Passport::SavedCredentials,
|
||||
int>;
|
||||
|
|
|
@ -18,6 +18,7 @@ struct PeerSubscription final {
|
|||
}
|
||||
};
|
||||
|
||||
using PhotoId = uint64;
|
||||
struct SubscriptionEntry final {
|
||||
explicit operator bool() const {
|
||||
return !id.isEmpty();
|
||||
|
@ -25,10 +26,14 @@ struct SubscriptionEntry final {
|
|||
|
||||
QString id;
|
||||
QString inviteHash;
|
||||
QString title;
|
||||
QString slug;
|
||||
QDateTime until;
|
||||
PeerSubscription subscription;
|
||||
uint64 barePeerId = 0;
|
||||
PhotoId photoId = PhotoId(0);
|
||||
bool cancelled = false;
|
||||
bool cancelledByBot = false;
|
||||
bool expired = false;
|
||||
bool canRefulfill = false;
|
||||
};
|
||||
|
|
|
@ -295,7 +295,7 @@ auto Entry::unreadStateChangeNotifier(bool required) {
|
|||
_flags |= Flag::InUnreadChangeBlock;
|
||||
const auto notify = required && inChatList();
|
||||
const auto wasState = notify ? chatListUnreadState() : UnreadState();
|
||||
return gsl::finally([=] {
|
||||
return gsl::finally([=, this] {
|
||||
_flags &= ~Flag::InUnreadChangeBlock;
|
||||
if (notify) {
|
||||
Assert(inChatList());
|
||||
|
|
|
@ -130,19 +130,6 @@ int BinarySearchBlocksOrItems(const T &list, int edge) {
|
|||
return start;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool CanSendReply(not_null<const HistoryItem*> item) {
|
||||
if (item->isDeleted()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto peer = item->history()->peer;
|
||||
const auto topic = item->topic();
|
||||
return topic
|
||||
? Data::CanSendAnything(topic)
|
||||
: (Data::CanSendAnything(peer)
|
||||
&& (!peer->isChannel() || peer->asChannel()->amIn()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html
|
||||
|
@ -631,21 +618,13 @@ void HistoryInner::setupSwipeReply() {
|
|||
const auto replyToItemId = (selected.item
|
||||
? selected.item
|
||||
: still)->fullId();
|
||||
if (canSendReply) {
|
||||
_widget->replyToMessage({
|
||||
.messageId = replyToItemId,
|
||||
.quote = selected.text,
|
||||
.quoteOffset = selected.offset,
|
||||
});
|
||||
if (!selected.text.empty()) {
|
||||
_widget->clearSelected();
|
||||
}
|
||||
} else {
|
||||
HistoryView::Controls::ShowReplyToChatBox(show, {
|
||||
.messageId = replyToItemId,
|
||||
.quote = selected.text,
|
||||
.quoteOffset = selected.offset,
|
||||
});
|
||||
_widget->replyToMessage({
|
||||
.messageId = replyToItemId,
|
||||
.quote = selected.text,
|
||||
.quoteOffset = selected.offset,
|
||||
});
|
||||
if (!selected.text.empty()) {
|
||||
_widget->clearSelected();
|
||||
}
|
||||
};
|
||||
return false;
|
||||
|
@ -2644,26 +2623,13 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
|||
const auto quoteOffset = selected.offset;
|
||||
text.replace('&', u"&&"_q);
|
||||
_menu->addAction(text, [=] {
|
||||
const auto still = session->data().message(itemId);
|
||||
const auto forceAnotherChat = base::IsCtrlPressed()
|
||||
&& still
|
||||
&& still->allowsForward();
|
||||
if (canSendReply && !forceAnotherChat) {
|
||||
_widget->replyToMessage({
|
||||
.messageId = itemId,
|
||||
.quote = quote,
|
||||
.quoteOffset = quoteOffset,
|
||||
});
|
||||
if (!quote.empty()) {
|
||||
_widget->clearSelected();
|
||||
}
|
||||
} else {
|
||||
const auto show = controller->uiShow();
|
||||
HistoryView::Controls::ShowReplyToChatBox(show, {
|
||||
.messageId = itemId,
|
||||
.quote = quote,
|
||||
.quoteOffset = quoteOffset,
|
||||
});
|
||||
_widget->replyToMessage({
|
||||
.messageId = itemId,
|
||||
.quote = quote,
|
||||
.quoteOffset = quoteOffset,
|
||||
});
|
||||
if (!quote.empty()) {
|
||||
_widget->clearSelected();
|
||||
}
|
||||
}, &st::menuIconReply);
|
||||
}
|
||||
|
@ -4847,3 +4813,16 @@ auto HistoryInner::DelegateMixin()
|
|||
-> std::unique_ptr<HistoryMainElementDelegateMixin> {
|
||||
return std::make_unique<HistoryMainElementDelegate>();
|
||||
}
|
||||
|
||||
bool CanSendReply(not_null<const HistoryItem*> item) {
|
||||
if (item->isDeleted()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto peer = item->history()->peer;
|
||||
const auto topic = item->topic();
|
||||
return topic
|
||||
? Data::CanSendAnything(topic)
|
||||
: (Data::CanSendAnything(peer)
|
||||
&& (!peer->isChannel() || peer->asChannel()->amIn()));
|
||||
}
|
||||
|
|
|
@ -555,3 +555,5 @@ private:
|
|||
bool _wasForceClickPreview = false;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]] bool CanSendReply(not_null<const HistoryItem*> item);
|
||||
|
|
|
@ -4701,6 +4701,21 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
|
|||
return preparePaymentSentText();
|
||||
};
|
||||
|
||||
auto preparePaymentSentMe = [&](const MTPDmessageActionPaymentSentMe &data) {
|
||||
auto result = PreparedServiceText();
|
||||
result.text = (data.is_recurring_used()
|
||||
? tr::lng_action_payment_bot_recurring
|
||||
: tr::lng_action_payment_bot_done)(
|
||||
tr::now,
|
||||
lt_amount,
|
||||
AmountAndStarCurrency(
|
||||
&_history->session(),
|
||||
data.vtotal_amount().v,
|
||||
qs(data.vcurrency())),
|
||||
Ui::Text::WithEntities);
|
||||
return result;
|
||||
};
|
||||
|
||||
auto prepareScreenshotTaken = [this](const MTPDmessageActionScreenshotTaken &) {
|
||||
auto result = PreparedServiceText();
|
||||
if (out()) {
|
||||
|
@ -5504,7 +5519,7 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
|
|||
prepareSecureValuesSent,
|
||||
prepareContactSignUp,
|
||||
prepareProximityReached,
|
||||
PrepareErrorText<MTPDmessageActionPaymentSentMe>,
|
||||
preparePaymentSentMe,
|
||||
PrepareErrorText<MTPDmessageActionSecureValuesSentMe>,
|
||||
prepareGroupCall,
|
||||
prepareInviteToGroupCall,
|
||||
|
|
|
@ -2028,24 +2028,12 @@ bool HistoryWidget::notify_switchInlineBotButtonReceived(
|
|||
UserData *samePeerBot,
|
||||
MsgId samePeerReplyTo) {
|
||||
if (samePeerBot) {
|
||||
if (_history) {
|
||||
const auto textWithTags = TextWithTags{
|
||||
'@' + samePeerBot->username() + ' ' + query,
|
||||
TextWithTags::Tags(),
|
||||
};
|
||||
MessageCursor cursor = {
|
||||
int(textWithTags.text.size()),
|
||||
int(textWithTags.text.size()),
|
||||
Ui::kQFixedMax,
|
||||
};
|
||||
_history->setLocalDraft(std::make_unique<Data::Draft>(
|
||||
textWithTags,
|
||||
FullReplyTo(),
|
||||
cursor,
|
||||
Data::WebPageDraft()));
|
||||
applyDraft();
|
||||
return true;
|
||||
const auto to = controller()->currentDialogsEntryState();
|
||||
if (!to.key.owningHistory()) {
|
||||
return false;
|
||||
}
|
||||
controller()->switchInlineQuery(to, samePeerBot, query);
|
||||
return true;
|
||||
} else if (const auto bot = _peer ? _peer->asUser() : nullptr) {
|
||||
const auto to = bot->isBot()
|
||||
? bot->botInfo->inlineReturnTo
|
||||
|
@ -2273,7 +2261,7 @@ void HistoryWidget::showHistory(
|
|||
_showAtMsgHighlightPart = {};
|
||||
_showAtMsgHighlightPartOffsetHint = 0;
|
||||
|
||||
const auto wasDialogsEntryState = computeDialogsEntryState();
|
||||
const auto wasState = controller()->currentDialogsEntryState();
|
||||
const auto startBot = (showAtMsgId == ShowAndStartBotMsgId);
|
||||
if (startBot) {
|
||||
showAtMsgId = ShowAtTheEndMsgId;
|
||||
|
@ -2378,8 +2366,8 @@ void HistoryWidget::showHistory(
|
|||
if (const auto user = _peer->asUser()) {
|
||||
if (const auto &info = user->botInfo) {
|
||||
if (startBot) {
|
||||
if (wasDialogsEntryState.key) {
|
||||
info->inlineReturnTo = wasDialogsEntryState;
|
||||
if (wasState.key) {
|
||||
info->inlineReturnTo = wasState;
|
||||
}
|
||||
sendBotStartCommand();
|
||||
_history->clearLocalDraft({});
|
||||
|
@ -2614,8 +2602,8 @@ void HistoryWidget::showHistory(
|
|||
if (const auto user = _peer->asUser()) {
|
||||
if (const auto &info = user->botInfo) {
|
||||
if (startBot) {
|
||||
if (wasDialogsEntryState.key) {
|
||||
info->inlineReturnTo = wasDialogsEntryState;
|
||||
if (wasState.key) {
|
||||
info->inlineReturnTo = wasState;
|
||||
}
|
||||
sendBotStartCommand();
|
||||
}
|
||||
|
@ -8060,7 +8048,15 @@ void HistoryWidget::clearFieldText(
|
|||
|
||||
void HistoryWidget::replyToMessage(FullReplyTo id) {
|
||||
if (const auto item = session().data().message(id.messageId)) {
|
||||
replyToMessage(item, id.quote, id.quoteOffset);
|
||||
if (CanSendReply(item) && !base::IsCtrlPressed()) {
|
||||
replyToMessage(item, id.quote, id.quoteOffset);
|
||||
} else if (item->allowsForward()) {
|
||||
const auto show = controller()->uiShow();
|
||||
HistoryView::Controls::ShowReplyToChatBox(show, id);
|
||||
} else {
|
||||
controller()->showToast(
|
||||
tr::lng_error_cant_reply_other(tr::now));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1887,7 +1887,7 @@ not_null<Ui::PathShiftGradient*> ListWidget::elementPathShiftGradient() {
|
|||
}
|
||||
|
||||
void ListWidget::elementReplyTo(const FullReplyTo &to) {
|
||||
replyToMessageRequestNotify(to);
|
||||
replyToMessageRequestNotify(to, base::IsCtrlPressed());
|
||||
}
|
||||
|
||||
void ListWidget::elementStartInteraction(not_null<const Element*> view) {
|
||||
|
|
|
@ -1659,7 +1659,8 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
|||
(right
|
||||
- (st::msgSelectionOffset * progress - st.size) / 2
|
||||
- st::msgPadding.right() / 2
|
||||
- st.size),
|
||||
- st.size
|
||||
- st::historyScroll.deltax),
|
||||
rect::bottom(g) - st.size - st::msgSelectionBottomSkip);
|
||||
{
|
||||
p.setPen(QPen(st.border, st.width));
|
||||
|
|
|
@ -255,7 +255,6 @@ DocumentData *Sticker::document() {
|
|||
|
||||
void Sticker::stickerClearLoopPlayed() {
|
||||
_oncePlayed = false;
|
||||
_premiumEffectPlayed = false;
|
||||
_premiumEffectSkipped = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -634,10 +634,13 @@ QSize WebPage::countOptimalSize() {
|
|||
_durationWidth = st::msgDateFont->width(_duration);
|
||||
}
|
||||
if (!_openButton.isEmpty()) {
|
||||
accumulate_max(
|
||||
maxWidth,
|
||||
rect::m::sum::h(st::historyPageButtonPadding)
|
||||
+ _openButton.maxWidth());
|
||||
const auto w = rect::m::sum::h(st::historyPageButtonPadding)
|
||||
+ _openButton.maxWidth();
|
||||
if (sponsored) {
|
||||
accumulate_max(maxWidth, w);
|
||||
} else {
|
||||
maxWidth += w;
|
||||
}
|
||||
}
|
||||
maxWidth += rect::m::sum::h(padding);
|
||||
minHeight += rect::m::sum::v(padding);
|
||||
|
|
|
@ -45,6 +45,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/widgets/labels.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "styles/style_color_indices.h"
|
||||
#include "styles/style_credits.h"
|
||||
#include "styles/style_giveaway.h"
|
||||
#include "styles/style_info.h"
|
||||
|
@ -59,7 +60,6 @@ namespace {
|
|||
|
||||
constexpr auto kDoneTooltipDuration = 5 * crl::time(1000);
|
||||
constexpr auto kAdditionalPrizeLengthMax = 128;
|
||||
constexpr auto kColorIndexCredits = int(1);
|
||||
|
||||
[[nodiscard]] QDateTime ThreeDaysAfterToday() {
|
||||
auto dateNow = QDateTime::currentDateTime();
|
||||
|
@ -370,7 +370,7 @@ void CreateGiveawayBox(
|
|||
prepaid->credits
|
||||
? GiveawayType::PrepaidCredits
|
||||
: GiveawayType::Prepaid,
|
||||
prepaid->credits ? kColorIndexCredits : prepaid->id,
|
||||
prepaid->credits ? st::colorIndexOrange : prepaid->id,
|
||||
tr::lng_boosts_prepaid_giveaway_single(),
|
||||
prepaid->credits
|
||||
? tr::lng_boosts_prepaid_giveaway_credits_status(
|
||||
|
@ -508,7 +508,7 @@ void CreateGiveawayBox(
|
|||
object_ptr<Giveaway::GiveawayTypeRow>(
|
||||
box,
|
||||
GiveawayType::Credits,
|
||||
kColorIndexCredits,
|
||||
st::colorIndexOrange,
|
||||
tr::lng_credits_summary_title(),
|
||||
tr::lng_giveaway_create_subtitle(),
|
||||
std::move(badge)));
|
||||
|
|
|
@ -16,14 +16,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/widgets/checkbox.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_color_indices.h"
|
||||
#include "styles/style_giveaway.h"
|
||||
#include "styles/style_statistics.h"
|
||||
|
||||
namespace Giveaway {
|
||||
|
||||
constexpr auto kColorIndexSpecific = int(4);
|
||||
constexpr auto kColorIndexRandom = int(2);
|
||||
|
||||
GiveawayTypeRow::GiveawayTypeRow(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
Type type,
|
||||
|
@ -32,7 +30,7 @@ GiveawayTypeRow::GiveawayTypeRow(
|
|||
: GiveawayTypeRow(
|
||||
parent,
|
||||
type,
|
||||
(type == Type::SpecificUsers) ? kColorIndexSpecific : kColorIndexRandom,
|
||||
(type == Type::SpecificUsers) ? st::colorIndexBlue : st::colorIndexGreen,
|
||||
(type == Type::SpecificUsers)
|
||||
? tr::lng_giveaway_award_option()
|
||||
: (type == Type::Random)
|
||||
|
|
|
@ -39,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/widgets/slider_natural_width.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "styles/style_color_indices.h"
|
||||
#include "styles/style_dialogs.h" // dialogsSearchTabs
|
||||
#include "styles/style_giveaway.h"
|
||||
#include "styles/style_info.h"
|
||||
|
@ -339,7 +340,6 @@ void InnerWidget::fill() {
|
|||
Ui::AddSkip(inner);
|
||||
|
||||
if (!status.prepaidGiveaway.empty()) {
|
||||
constexpr auto kColorIndexCredits = int(1);
|
||||
const auto multiplier = Api::PremiumGiftCodeOptions(_peer)
|
||||
.giveawayBoostsPerPremium();
|
||||
Ui::AddSkip(inner);
|
||||
|
@ -352,7 +352,7 @@ void InnerWidget::fill() {
|
|||
g.credits
|
||||
? GiveawayTypeRow::Type::PrepaidCredits
|
||||
: GiveawayTypeRow::Type::Prepaid,
|
||||
g.credits ? kColorIndexCredits : g.id,
|
||||
g.credits ? st::colorIndexOrange : g.id,
|
||||
g.credits
|
||||
? tr::lng_boosts_prepaid_giveaway_single()
|
||||
: tr::lng_boosts_prepaid_giveaway_quantity(
|
||||
|
|
|
@ -52,6 +52,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/fields/input_field.h"
|
||||
#include "ui/widgets/label_with_custom_emoji.h"
|
||||
#include "ui/widgets/peer_bubble.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/widgets/slider_natural_width.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
|
@ -1199,58 +1200,10 @@ void InnerWidget::fill() {
|
|||
AddRecipient(box, recipient);
|
||||
}
|
||||
if (isIn) {
|
||||
const auto peerBubble = box->addRow(
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
box,
|
||||
object_ptr<Ui::RpWidget>(box)))->entity();
|
||||
peerBubble->setAttribute(
|
||||
Qt::WA_TransparentForMouseEvents);
|
||||
const auto left = Ui::CreateChild<Ui::UserpicButton>(
|
||||
peerBubble,
|
||||
peer,
|
||||
st::uploadUserpicButton);
|
||||
const auto right = Ui::CreateChild<Ui::FlatLabel>(
|
||||
peerBubble,
|
||||
Info::Profile::NameValue(peer),
|
||||
st::channelEarnSemiboldLabel);
|
||||
rpl::combine(
|
||||
left->sizeValue(),
|
||||
right->sizeValue()
|
||||
) | rpl::start_with_next([=](
|
||||
const QSize &leftSize,
|
||||
const QSize &rightSize) {
|
||||
const auto padding = QMargins(
|
||||
st::chatGiveawayPeerPadding.left() * 2,
|
||||
st::chatGiveawayPeerPadding.top(),
|
||||
st::chatGiveawayPeerPadding.right(),
|
||||
st::chatGiveawayPeerPadding.bottom());
|
||||
peerBubble->resize(
|
||||
leftSize.width()
|
||||
+ rightSize.width()
|
||||
+ rect::m::sum::h(padding),
|
||||
leftSize.height());
|
||||
left->moveToLeft(0, 0);
|
||||
right->moveToRight(
|
||||
padding.right(),
|
||||
padding.top());
|
||||
const auto maxRightSize = box->width()
|
||||
- rect::m::sum::h(st::boxRowPadding)
|
||||
- rect::m::sum::h(padding)
|
||||
- leftSize.width();
|
||||
if (rightSize.width() > maxRightSize) {
|
||||
right->resizeToWidth(maxRightSize);
|
||||
}
|
||||
}, peerBubble->lifetime());
|
||||
peerBubble->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
auto p = QPainter(peerBubble);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::windowBgOver);
|
||||
const auto rect = peerBubble->rect();
|
||||
const auto radius = rect.height() / 2;
|
||||
p.drawRoundedRect(rect, radius, radius);
|
||||
}, peerBubble->lifetime());
|
||||
Ui::CreatePeerBubble(box, peer)));
|
||||
}
|
||||
const auto closeBox = [=] { box->closeBox(); };
|
||||
{
|
||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "api/api_credits.h"
|
||||
#include "api/api_statistics.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "boxes/peer_list_widgets.h"
|
||||
#include "chat_helpers/stickers_gift_box_pack.h"
|
||||
#include "core/ui_integration.h" // Core::MarkedTextContext.
|
||||
#include "data/data_channel.h"
|
||||
|
@ -24,6 +25,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "main/main_session.h"
|
||||
#include "main/session/session_show.h"
|
||||
#include "settings/settings_credits_graphics.h" // PaintSubscriptionRightLabelCallback
|
||||
#include "ui/dynamic_image.h"
|
||||
#include "ui/dynamic_thumbnails.h"
|
||||
#include "ui/effects/credits_graphics.h"
|
||||
#include "ui/effects/outline_segments.h" // Ui::UnreadStoryOutlineGradient.
|
||||
#include "ui/effects/toggle_arrow.h"
|
||||
|
@ -35,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_color_indices.h"
|
||||
#include "styles/style_credits.h"
|
||||
#include "styles/style_dialogs.h" // dialogsStoriesFull.
|
||||
#include "styles/style_layers.h" // boxRowPadding.
|
||||
|
@ -47,9 +51,6 @@ namespace Info::Statistics {
|
|||
namespace {
|
||||
|
||||
using BoostCallback = Fn<void(const Data::Boost &)>;
|
||||
constexpr auto kColorIndexCredits = int(1);
|
||||
constexpr auto kColorIndexUnclaimed = int(3);
|
||||
constexpr auto kColorIndexPending = int(4);
|
||||
|
||||
[[nodiscard]] PeerListRowId UniqueRowIdFromEntry(
|
||||
const Data::CreditsHistoryEntry &entry) {
|
||||
|
@ -70,6 +71,17 @@ void AddSubtitle(
|
|||
{ 0, -subtitlePadding.top(), 0, -subtitlePadding.bottom() });
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::SettingsButton> CreateShowMoreButton(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
rpl::producer<QString> title) {
|
||||
auto owned = object_ptr<Ui::SettingsButton>(
|
||||
parent,
|
||||
std::move(title),
|
||||
st::statisticsShowMoreButton);
|
||||
Ui::AddToggleUpDownArrowToMoreButton(owned.data());
|
||||
return owned;
|
||||
}
|
||||
|
||||
[[nodiscard]] QString FormatText(
|
||||
int value1, tr::phrase<lngtag_count> phrase1,
|
||||
int value2, tr::phrase<lngtag_count> phrase2,
|
||||
|
@ -475,10 +487,10 @@ BoostRow::BoostRow(const Data::Boost &boost)
|
|||
, _boost(boost)
|
||||
, _userpic(
|
||||
Ui::EmptyUserpic::UserpicColor(boost.credits
|
||||
? kColorIndexCredits
|
||||
? st::colorIndexOrange
|
||||
: boost.isUnclaimed
|
||||
? kColorIndexUnclaimed
|
||||
: kColorIndexPending),
|
||||
? st::colorIndexSea
|
||||
: st::colorIndexBlue),
|
||||
QString()) {
|
||||
init();
|
||||
}
|
||||
|
@ -763,6 +775,18 @@ public:
|
|||
bool selected,
|
||||
bool actionSelected) override;
|
||||
|
||||
void paintStatusText(
|
||||
Painter &p,
|
||||
const style::PeerListItem &st,
|
||||
int x,
|
||||
int y,
|
||||
int available,
|
||||
int outer,
|
||||
bool selected) override;
|
||||
|
||||
const style::PeerListItem &computeSt(
|
||||
const style::PeerListItem &st) const override;
|
||||
|
||||
private:
|
||||
void init();
|
||||
|
||||
|
@ -776,8 +800,12 @@ private:
|
|||
QString _title;
|
||||
QString _name;
|
||||
|
||||
Ui::Text::String _description;
|
||||
Ui::Text::String _rightText;
|
||||
|
||||
std::shared_ptr<Ui::DynamicImage> _descriptionThumbnail;
|
||||
QImage _descriptionThumbnailCache;
|
||||
|
||||
base::has_weak_ptr _guard;
|
||||
};
|
||||
|
||||
|
@ -822,33 +850,37 @@ void CreditsRow::init() {
|
|||
const auto name = !isSpecial
|
||||
? PeerListRow::generateName()
|
||||
: Ui::GenerateEntryName(_entry).text;
|
||||
_name = (_entry.reaction || _entry.stargift || _entry.bareGiveawayMsgId)
|
||||
? Ui::GenerateEntryName(_entry).text
|
||||
: _entry.title.isEmpty()
|
||||
_name = _entry.title.isEmpty()
|
||||
? name
|
||||
: (!_entry.subscriptionUntil.isNull() && !isSpecial)
|
||||
? name
|
||||
: _entry.title;
|
||||
const auto joiner = QString(QChar(' ')) + QChar(8212) + QChar(' ');
|
||||
setSkipPeerBadge(true);
|
||||
PeerListRow::setCustomStatus(
|
||||
langDateTime(_entry.date)
|
||||
+ (_entry.floodSkip
|
||||
? (joiner + tr::lng_credits_box_history_entry_floodskip_about(
|
||||
tr::now,
|
||||
lt_count_decimal,
|
||||
_entry.floodSkip))
|
||||
: _entry.refunded
|
||||
? (joiner + tr::lng_channel_earn_history_return(tr::now))
|
||||
: _entry.pending
|
||||
? (joiner + tr::lng_channel_earn_history_pending(tr::now))
|
||||
: _entry.failed
|
||||
? (joiner + tr::lng_channel_earn_history_failed(tr::now))
|
||||
: !_entry.subscriptionUntil.isNull()
|
||||
? (joiner
|
||||
+ tr::lng_credits_box_history_entry_subscription(tr::now))
|
||||
: QString())
|
||||
+ ((_entry.gift && isSpecial)
|
||||
? (joiner + tr::lng_credits_box_history_entry_anonymous(tr::now))
|
||||
: ((_name == name) ? QString() : (joiner + name))));
|
||||
const auto description = _entry.floodSkip
|
||||
? tr::lng_credits_box_history_entry_floodskip_about(
|
||||
tr::now,
|
||||
lt_count_decimal,
|
||||
_entry.floodSkip)
|
||||
: (!_entry.subscriptionUntil.isNull() && !_entry.title.isEmpty())
|
||||
? _entry.title
|
||||
: _entry.refunded
|
||||
? tr::lng_channel_earn_history_return(tr::now)
|
||||
: _entry.pending
|
||||
? tr::lng_channel_earn_history_pending(tr::now)
|
||||
: _entry.failed
|
||||
? tr::lng_channel_earn_history_failed(tr::now)
|
||||
: !_entry.subscriptionUntil.isNull()
|
||||
? tr::lng_credits_box_history_entry_subscription(tr::now)
|
||||
: (_entry.peerType
|
||||
== Data::CreditsHistoryEntry::PeerType::PremiumBot)
|
||||
? tr::lng_credits_box_history_entry_via_premium_bot(tr::now)
|
||||
: (_entry.gift && isSpecial)
|
||||
? tr::lng_credits_box_history_entry_anonymous(tr::now)
|
||||
: (_name == name)
|
||||
? Ui::GenerateEntryName(_entry).text
|
||||
: name;
|
||||
_description.setText(st::defaultTextStyle, description);
|
||||
PeerListRow::setCustomStatus(langDateTime(_entry.date));
|
||||
if (_subscription) {
|
||||
PeerListRow::setCustomStatus((_subscription.expired
|
||||
? tr::lng_credits_subscription_status_none
|
||||
|
@ -858,6 +890,24 @@ void CreditsRow::init() {
|
|||
tr::now,
|
||||
lt_date,
|
||||
langDayOfMonthFull(_subscription.until.date())));
|
||||
_description.setText(st::defaultTextStyle, _subscription.title);
|
||||
}
|
||||
const auto descriptionPhotoId = (!_entry.subscriptionUntil.isNull())
|
||||
? _entry.photoId
|
||||
: _subscription.photoId;
|
||||
if (descriptionPhotoId) {
|
||||
_descriptionThumbnail = Ui::MakePhotoThumbnail(
|
||||
_context.session->data().photo(descriptionPhotoId),
|
||||
{});
|
||||
_descriptionThumbnail->subscribeToUpdates([this] {
|
||||
const auto thumbnailSide = st::defaultTextStyle.font->height;
|
||||
_descriptionThumbnailCache = Images::Round(
|
||||
_descriptionThumbnail->image(thumbnailSide),
|
||||
ImageRoundRadius::Large);
|
||||
if (_context.customEmojiRepaint) {
|
||||
_context.customEmojiRepaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
auto &manager = _context.session->data().customEmojiManager();
|
||||
if (_entry) {
|
||||
|
@ -894,23 +944,40 @@ const Data::SubscriptionEntry &CreditsRow::subscription() const {
|
|||
}
|
||||
|
||||
QString CreditsRow::generateName() {
|
||||
return _entry.title.isEmpty() ? _name : _entry.title;
|
||||
return (!_entry.title.isEmpty() && !_entry.subscriptionUntil.isNull())
|
||||
? _name
|
||||
: _entry.title.isEmpty()
|
||||
? _name
|
||||
: _entry.title;
|
||||
}
|
||||
|
||||
PaintRoundImageCallback CreditsRow::generatePaintUserpicCallback(bool force) {
|
||||
return _paintUserpicCallback;
|
||||
}
|
||||
|
||||
[[nodiscard]] QString RightActionText(const Data::SubscriptionEntry &s) {
|
||||
return s.cancelledByBot
|
||||
? tr::lng_credits_subscription_status_off_by_bot_right(tr::now)
|
||||
: s.cancelled
|
||||
? tr::lng_credits_subscription_status_off_right(tr::now)
|
||||
: s.expired
|
||||
? tr::lng_credits_subscription_status_none_right(tr::now)
|
||||
: QString();
|
||||
}
|
||||
|
||||
QSize CreditsRow::rightActionSize() const {
|
||||
if (_rightLabel) {
|
||||
return _rightLabel->size;
|
||||
} else if (_subscription.cancelled || _subscription.expired) {
|
||||
const auto text = _subscription.cancelled
|
||||
? tr::lng_credits_subscription_status_off_right(tr::now)
|
||||
: tr::lng_credits_subscription_status_none_right(tr::now);
|
||||
return QSize(
|
||||
st::contactsStatusFont->width(text) + st::boxRowPadding.right(),
|
||||
_rowHeight);
|
||||
} else if (const auto t = RightActionText(_subscription); !t.isEmpty()) {
|
||||
const auto lines = t.split('\n');
|
||||
auto maxWidth = 0;
|
||||
for (const auto &line : lines) {
|
||||
const auto width = st::contactsStatusFont->width(line);
|
||||
if (width > maxWidth) {
|
||||
maxWidth = width;
|
||||
}
|
||||
}
|
||||
return QSize(maxWidth + st::boxRowPadding.right(), _rowHeight);
|
||||
} else if (_subscription || _entry) {
|
||||
return QSize(
|
||||
_rightText.maxWidth() + st::boxRowPadding.right() / 2,
|
||||
|
@ -940,18 +1007,31 @@ void CreditsRow::rightActionPaint(
|
|||
const auto rightSkip = st::boxRowPadding.right();
|
||||
if (_rightLabel) {
|
||||
return _rightLabel->draw(p, x, y, _rowHeight);
|
||||
} else if (_subscription.cancelled || _subscription.expired) {
|
||||
} else if (const auto t = RightActionText(_subscription); !t.isEmpty()) {
|
||||
const auto &statusFont = st::contactsStatusFont;
|
||||
y += _rowHeight / 2;
|
||||
p.setFont(statusFont);
|
||||
p.setPen(st::attentionButtonFg);
|
||||
p.drawTextRight(
|
||||
rightSkip,
|
||||
y - statusFont->height / 2,
|
||||
outerWidth,
|
||||
_subscription.expired
|
||||
? tr::lng_credits_subscription_status_none_right(tr::now)
|
||||
: tr::lng_credits_subscription_status_off_right(tr::now));
|
||||
|
||||
const auto lines = t.split('\n');
|
||||
if (lines.size() > 1) {
|
||||
const auto rect = QRect(x, 0, outerWidth - x, _rowHeight);
|
||||
const auto lineHeight = statusFont->height;
|
||||
const auto totalHeight = lines.size() * lineHeight;
|
||||
auto startY = rect.top()
|
||||
+ (rect.height() - totalHeight) / 2
|
||||
+ statusFont->ascent;
|
||||
|
||||
for (const auto &line : lines) {
|
||||
const auto lineWidth = statusFont->width(line);
|
||||
const auto startX = rect.left()
|
||||
+ (rect.width() - lineWidth) / 2;
|
||||
p.drawText(startX, startY, line);
|
||||
startY += lineHeight;
|
||||
}
|
||||
return;
|
||||
}
|
||||
p.drawTextRight(rightSkip, y - statusFont->height / 2, outerWidth, t);
|
||||
return;
|
||||
}
|
||||
y += _rowHeight / 2;
|
||||
|
@ -969,6 +1049,43 @@ void CreditsRow::rightActionPaint(
|
|||
});
|
||||
}
|
||||
|
||||
void CreditsRow::paintStatusText(
|
||||
Painter &p,
|
||||
const style::PeerListItem &st,
|
||||
int x,
|
||||
int y,
|
||||
int available,
|
||||
int outer,
|
||||
bool selected) {
|
||||
PeerListRow::paintStatusText(p, st, x, y, available, outer, selected);
|
||||
p.setPen(st.nameFg);
|
||||
if (!_descriptionThumbnailCache.isNull()) {
|
||||
const auto thumbnailSide = _descriptionThumbnailCache.width()
|
||||
/ style::DevicePixelRatio();
|
||||
const auto thumbnailSpace = st::lineWidth * 4 + thumbnailSide;
|
||||
p.drawImage(
|
||||
x,
|
||||
y - thumbnailSide,
|
||||
_descriptionThumbnailCache);
|
||||
x += thumbnailSpace;
|
||||
outer -= thumbnailSpace;
|
||||
available -= thumbnailSpace;
|
||||
}
|
||||
_description.draw(p, {
|
||||
.position = QPoint(x, y - _description.minHeight()),
|
||||
.outerWidth = outer,
|
||||
.availableWidth = available,
|
||||
.elisionLines = 1,
|
||||
});
|
||||
}
|
||||
|
||||
const style::PeerListItem &CreditsRow::computeSt(
|
||||
const style::PeerListItem &st) const {
|
||||
return (!_subscription || !_subscription.title.isEmpty())
|
||||
? st
|
||||
: st::boostsListBox.item;
|
||||
}
|
||||
|
||||
class CreditsController final : public PeerListController {
|
||||
public:
|
||||
explicit CreditsController(CreditsDescriptor d);
|
||||
|
@ -1010,7 +1127,7 @@ CreditsController::CreditsController(CreditsDescriptor d)
|
|||
.session = _session,
|
||||
.customEmojiRepaint = [] {},
|
||||
}) {
|
||||
PeerListController::setStyleOverrides(&st::boostsListBox);
|
||||
PeerListController::setStyleOverrides(&st::creditsHistoryEntriesList);
|
||||
}
|
||||
|
||||
Main::Session &CreditsController::session() const {
|
||||
|
@ -1052,7 +1169,9 @@ void CreditsController::applySlice(const Data::CreditsStatusSlice &slice) {
|
|||
.entry = i,
|
||||
.subscription = s,
|
||||
.context = _context,
|
||||
.rowHeight = computeListSt().item.height,
|
||||
.rowHeight = ((!s || !s.title.isEmpty())
|
||||
? computeListSt().item
|
||||
: st::boostsListBox.item).height,
|
||||
.updateCallback = [=](not_null<PeerListRow*> row) {
|
||||
delegate()->peerListUpdateRow(row);
|
||||
},
|
||||
|
@ -1239,28 +1358,36 @@ void AddCreditsHistoryList(
|
|||
not_null<PeerData*> bot,
|
||||
bool in,
|
||||
bool out,
|
||||
bool subscription) {
|
||||
bool subs) {
|
||||
struct State final {
|
||||
State(
|
||||
CreditsDescriptor d,
|
||||
std::shared_ptr<Main::SessionShow> show)
|
||||
: delegate(std::move(show))
|
||||
, controller(std::move(d)) {
|
||||
State(CreditsDescriptor d) : controller(std::move(d)) {
|
||||
}
|
||||
PeerListContentDelegateShow delegate;
|
||||
std::optional<PeerListContentDelegateShow> creditsDelegate;
|
||||
std::optional<PeerListWidgetsDelegate> subscriptionDelegate;
|
||||
CreditsController controller;
|
||||
};
|
||||
const auto state = container->lifetime().make_state<State>(
|
||||
CreditsDescriptor{ firstSlice, callback, bot, in, out, subscription },
|
||||
show);
|
||||
CreditsDescriptor{ firstSlice, callback, bot, in, out, subs });
|
||||
if (subs) {
|
||||
state->subscriptionDelegate.emplace();
|
||||
state->subscriptionDelegate->setUiShow(show);
|
||||
state->subscriptionDelegate->setContent(container->add(
|
||||
object_ptr<PeerListWidgets>(container, &state->controller)));
|
||||
state->controller.setDelegate(&(*state->subscriptionDelegate));
|
||||
} else {
|
||||
state->creditsDelegate.emplace(show);
|
||||
state->creditsDelegate->setContent(container->add(
|
||||
object_ptr<PeerListContent>(container, &state->controller)));
|
||||
state->controller.setDelegate(&(*state->creditsDelegate));
|
||||
}
|
||||
|
||||
state->delegate.setContent(container->add(
|
||||
object_ptr<PeerListContent>(container, &state->controller)));
|
||||
state->controller.setDelegate(&state->delegate);
|
||||
|
||||
const auto wrap = AddShowMoreButton(
|
||||
container,
|
||||
tr::lng_stories_show_more());
|
||||
const auto wrap = container->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
|
||||
container,
|
||||
CreateShowMoreButton(container, tr::lng_stories_show_more())),
|
||||
subs
|
||||
? QMargins()
|
||||
: QMargins(0, -st::settingsButton.padding.top(), 0, 0));
|
||||
|
||||
const auto showMore = [=] {
|
||||
if (!state->controller.skipRequest()) {
|
||||
|
@ -1277,16 +1404,11 @@ void AddCreditsHistoryList(
|
|||
not_null<Ui::SlideWrap<Ui::SettingsButton>*> AddShowMoreButton(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
rpl::producer<QString> title) {
|
||||
const auto wrap = container->add(
|
||||
return container->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
|
||||
container,
|
||||
object_ptr<Ui::SettingsButton>(
|
||||
container,
|
||||
std::move(title),
|
||||
st::statisticsShowMoreButton)),
|
||||
CreateShowMoreButton(container, std::move(title))),
|
||||
{ 0, -st::settingsButton.padding.top(), 0, 0 });
|
||||
Ui::AddToggleUpDownArrowToMoreButton(wrap->entity());
|
||||
return wrap;
|
||||
}
|
||||
|
||||
} // namespace Info::Statistics
|
||||
|
|
|
@ -643,12 +643,13 @@ void ConfirmEmojiStatusBox(
|
|||
done(true);
|
||||
});
|
||||
box->addButton(tr::lng_cancel(), [=] {
|
||||
const auto was = *set;
|
||||
box->closeBox();
|
||||
if (!was) {
|
||||
});
|
||||
box->boxClosing() | rpl::start_with_next([=] {
|
||||
if (!*set) {
|
||||
done(false);
|
||||
}
|
||||
});
|
||||
}, box->lifetime());
|
||||
}
|
||||
|
||||
class BotAction final : public Ui::Menu::ItemBase {
|
||||
|
@ -954,7 +955,12 @@ void WebViewInstance::resolve() {
|
|||
requestSimple();
|
||||
});
|
||||
}, [&](WebViewSourceLinkApp data) {
|
||||
resolveApp(data.appname, data.token, !_context.maySkipConfirmation);
|
||||
resolveApp(
|
||||
data.appname,
|
||||
data.token,
|
||||
(_context.maySkipConfirmation
|
||||
? ConfirmType::None
|
||||
: ConfirmType::Always));
|
||||
}, [&](WebViewSourceLinkBotProfile) {
|
||||
confirmOpen([=] {
|
||||
requestMain();
|
||||
|
@ -1002,14 +1008,14 @@ bool WebViewInstance::openAppFromBotMenuLink() {
|
|||
if (appname.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
resolveApp(appname, params.value(u"startapp"_q), true);
|
||||
resolveApp(appname, params.value(u"startapp"_q), ConfirmType::Once);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebViewInstance::resolveApp(
|
||||
const QString &appname,
|
||||
const QString &startparam,
|
||||
bool forceConfirmation) {
|
||||
ConfirmType confirmType) {
|
||||
const auto already = _session->data().findBotApp(_bot->id, appname);
|
||||
_requestId = _session->api().request(MTPmessages_GetBotApp(
|
||||
MTP_inputBotAppShortName(
|
||||
|
@ -1029,8 +1035,11 @@ void WebViewInstance::resolveApp(
|
|||
close();
|
||||
return;
|
||||
}
|
||||
const auto confirm = data.is_inactive() || forceConfirmation;
|
||||
const auto confirm = data.is_inactive()
|
||||
|| (confirmType != ConfirmType::None);
|
||||
const auto writeAccess = result.data().is_request_write_access();
|
||||
const auto forceConfirmation = data.is_inactive()
|
||||
|| (confirmType == ConfirmType::Always);
|
||||
|
||||
// Check if this app can be added to main menu.
|
||||
// On fail it'll still be opened.
|
||||
|
@ -1043,7 +1052,7 @@ void WebViewInstance::resolveApp(
|
|||
} else if (confirm) {
|
||||
confirmAppOpen(writeAccess, [=](bool allowWrite) {
|
||||
requestApp(allowWrite);
|
||||
});
|
||||
}, forceConfirmation);
|
||||
} else {
|
||||
requestApp(false);
|
||||
}
|
||||
|
@ -1090,10 +1099,18 @@ void WebViewInstance::confirmOpen(Fn<void()> done) {
|
|||
|
||||
void WebViewInstance::confirmAppOpen(
|
||||
bool writeAccess,
|
||||
Fn<void(bool allowWrite)> done) {
|
||||
Fn<void(bool allowWrite)> done,
|
||||
bool forceConfirmation) {
|
||||
if (!forceConfirmation
|
||||
&& (_bot->isVerified()
|
||||
|| _session->local().isBotTrustedOpenWebView(_bot->id))) {
|
||||
done(writeAccess);
|
||||
return;
|
||||
}
|
||||
_parentShow->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
const auto allowed = std::make_shared<Ui::Checkbox*>();
|
||||
const auto callback = [=](Fn<void()> close) {
|
||||
_session->local().markBotTrustedOpenWebView(_bot->id);
|
||||
done((*allowed) && (*allowed)->checked());
|
||||
close();
|
||||
};
|
||||
|
|
|
@ -230,12 +230,20 @@ private:
|
|||
void requestWithMenuAdd();
|
||||
void maybeChooseAndRequestButton(PeerTypes supported);
|
||||
|
||||
enum class ConfirmType : uchar {
|
||||
Always,
|
||||
Once,
|
||||
None,
|
||||
};
|
||||
void resolveApp(
|
||||
const QString &appname,
|
||||
const QString &startparam,
|
||||
bool forceConfirmation);
|
||||
ConfirmType confirmType);
|
||||
void confirmOpen(Fn<void()> done);
|
||||
void confirmAppOpen(bool writeAccess, Fn<void(bool allowWrite)> done);
|
||||
void confirmAppOpen(
|
||||
bool writeAccess,
|
||||
Fn<void(bool allowWrite)> done,
|
||||
bool forceConfirmation);
|
||||
|
||||
struct ShowArgs {
|
||||
QString url;
|
||||
|
|
|
@ -456,6 +456,8 @@ void Form::requestForm() {
|
|||
const auto amount = tlPrices.empty()
|
||||
? 0
|
||||
: tlPrices.front().data().vamount().v;
|
||||
const auto subscriptionPeriod
|
||||
= data.vinvoice().data().vsubscription_period().value_or(0);
|
||||
if (currency != ::Ui::kCreditsCurrency || !amount) {
|
||||
using Type = Error::Type;
|
||||
_updates.fire(Error{ Type::Form, u"Bad Stars Form."_q });
|
||||
|
@ -467,6 +469,7 @@ void Form::requestForm() {
|
|||
.credits = amount,
|
||||
.currency = currency,
|
||||
.amount = amount,
|
||||
.subscriptionPeriod = subscriptionPeriod,
|
||||
};
|
||||
const auto formData = CreditsFormData{
|
||||
.id = _id,
|
||||
|
|
|
@ -170,6 +170,7 @@ struct InvoiceCredits {
|
|||
uint64 amount = 0;
|
||||
bool extended = false;
|
||||
PeerId giftPeerId = PeerId(0);
|
||||
int subscriptionPeriod = 0;
|
||||
};
|
||||
|
||||
struct InvoiceStarGift {
|
||||
|
|
|
@ -94,14 +94,14 @@ void ProcessCreditsPayment(
|
|||
Ui::SendCreditsBox,
|
||||
form,
|
||||
[=] {
|
||||
*unsuccessful = false;
|
||||
if (const auto widget = fireworks.data()) {
|
||||
Ui::StartFireworks(widget);
|
||||
}
|
||||
if (const auto onstack = maybeReturnToBot) {
|
||||
onstack(CheckoutResult::Paid);
|
||||
}
|
||||
}));
|
||||
*unsuccessful = false;
|
||||
if (const auto widget = fireworks.data()) {
|
||||
Ui::StartFireworks(widget);
|
||||
}
|
||||
if (const auto onstack = maybeReturnToBot) {
|
||||
onstack(CheckoutResult::Paid);
|
||||
}
|
||||
}));
|
||||
box->boxClosing() | rpl::start_with_next([=] {
|
||||
crl::on_main([=] {
|
||||
if (*unsuccessful) {
|
||||
|
|
|
@ -191,6 +191,19 @@ void Credits::setupSubscriptions(not_null<Ui::VerticalLayout*> container) {
|
|||
fill(std::move(d));
|
||||
});
|
||||
}
|
||||
{
|
||||
using Rebuilder = Data::Session::CreditsSubsRebuilder;
|
||||
using RebuilderPtr = std::shared_ptr<Rebuilder>;
|
||||
const auto rebuilder = content->lifetime().make_state<RebuilderPtr>(
|
||||
self->owner().createCreditsSubsRebuilder());
|
||||
rebuilder->get()->events(
|
||||
) | rpl::start_with_next([=](Data::CreditsStatusSlice slice) {
|
||||
while (content->count()) {
|
||||
delete content->widgetAt(0);
|
||||
}
|
||||
fill(std::move(slice));
|
||||
}, content->lifetime());
|
||||
}
|
||||
}
|
||||
|
||||
void Credits::setupHistory(not_null<Ui::VerticalLayout*> container) {
|
||||
|
|
|
@ -47,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "main/main_session.h"
|
||||
#include "payments/payments_checkout_process.h"
|
||||
#include "payments/payments_form.h"
|
||||
#include "payments/payments_non_panel_process.h"
|
||||
#include "settings/settings_common_session.h"
|
||||
#include "settings/settings_credits.h"
|
||||
#include "statistics/widgets/chart_header_widget.h"
|
||||
|
@ -346,6 +347,37 @@ void AddViewMediaHandler(
|
|||
}, thumb->lifetime());
|
||||
}
|
||||
|
||||
void AddMiniStars(
|
||||
not_null<Ui::VerticalLayout*> content,
|
||||
not_null<Ui::RpWidget*> widget,
|
||||
const style::UserpicButton &stUser,
|
||||
int boxWidth,
|
||||
float64 heightRatio) {
|
||||
using ColoredMiniStars = Ui::Premium::ColoredMiniStars;
|
||||
const auto stars = widget->lifetime().make_state<ColoredMiniStars>(
|
||||
widget,
|
||||
false,
|
||||
Ui::Premium::MiniStars::Type::BiStars);
|
||||
stars->setColorOverride(Ui::Premium::CreditsIconGradientStops());
|
||||
widget->resize(
|
||||
boxWidth - stUser.photoSize,
|
||||
stUser.photoSize * heightRatio);
|
||||
content->sizeValue(
|
||||
) | rpl::start_with_next([=](const QSize &size) {
|
||||
widget->moveToLeft(stUser.photoSize / 2, 0);
|
||||
const auto starsRect = Rect(widget->size());
|
||||
stars->setPosition(starsRect.topLeft());
|
||||
stars->setSize(starsRect.size());
|
||||
widget->lower();
|
||||
}, widget->lifetime());
|
||||
widget->paintRequest(
|
||||
) | rpl::start_with_next([=](const QRect &r) {
|
||||
auto p = QPainter(widget);
|
||||
p.fillRect(r, Qt::transparent);
|
||||
stars->paint(p);
|
||||
}, widget->lifetime());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SubscriptionRightLabel PaintSubscriptionRightLabelCallback(
|
||||
|
@ -664,31 +696,12 @@ void BoostCreditsBox(
|
|||
{
|
||||
const auto &stUser = st::premiumGiftsUserpicButton;
|
||||
const auto widget = content->add(object_ptr<Ui::RpWidget>(content));
|
||||
using ColoredMiniStars = Ui::Premium::ColoredMiniStars;
|
||||
const auto stars = widget->lifetime().make_state<ColoredMiniStars>(
|
||||
widget,
|
||||
false,
|
||||
Ui::Premium::MiniStars::Type::BiStars);
|
||||
stars->setColorOverride(Ui::Premium::CreditsIconGradientStops());
|
||||
widget->resize(
|
||||
st::boxWidth - stUser.photoSize,
|
||||
stUser.photoSize * 1.3);
|
||||
AddMiniStars(content, widget, stUser, st::boxWidth, 1.3);
|
||||
const auto svg = std::make_shared<QSvgRenderer>(
|
||||
Ui::Premium::ColorizedSvg(
|
||||
Ui::Premium::CreditsIconGradientStops()));
|
||||
content->sizeValue(
|
||||
) | rpl::start_with_next([=](const QSize &size) {
|
||||
widget->moveToLeft(stUser.photoSize / 2, 0);
|
||||
const auto starsRect = Rect(widget->size());
|
||||
stars->setPosition(starsRect.topLeft());
|
||||
stars->setSize(starsRect.size());
|
||||
widget->lower();
|
||||
}, widget->lifetime());
|
||||
widget->paintRequest(
|
||||
) | rpl::start_with_next([=](const QRect &r) {
|
||||
widget->paintRequest() | rpl::start_with_next([=](const QRect &r) {
|
||||
auto p = QPainter(widget);
|
||||
p.fillRect(r, Qt::transparent);
|
||||
stars->paint(p);
|
||||
svg->render(
|
||||
&p,
|
||||
QRectF(
|
||||
|
@ -790,6 +803,28 @@ void BoostCreditsBox(
|
|||
}, button->lifetime());
|
||||
}
|
||||
|
||||
void ProcessReceivedSubscriptions(
|
||||
QPointer<Ui::GenericBox> weak,
|
||||
not_null<Main::Session*> session) {
|
||||
const auto rebuilder = session->data().activeCreditsSubsRebuilder();
|
||||
if (const auto strong = weak.data()) {
|
||||
if (!rebuilder) {
|
||||
return strong->closeBox();
|
||||
}
|
||||
const auto api
|
||||
= strong->lifetime().make_state<Api::CreditsHistory>(
|
||||
session->user(),
|
||||
true,
|
||||
true);
|
||||
api->requestSubscriptions({}, [=](Data::CreditsStatusSlice first) {
|
||||
rebuilder->fire(std::move(first));
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ReceiptCreditsBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> controller,
|
||||
|
@ -820,6 +855,7 @@ void ReceiptCreditsBox(
|
|||
const auto nonConvertible = (gotStarGift && !e.starsConverted);
|
||||
|
||||
box->setStyle(st::giveawayGiftCodeBox);
|
||||
box->setWidth(st::boxWideWidth);
|
||||
box->setNoContentMargin(true);
|
||||
|
||||
const auto content = box->verticalLayout();
|
||||
|
@ -851,6 +887,20 @@ void ReceiptCreditsBox(
|
|||
content,
|
||||
GenericEntryPhoto(content, callback, stUser.photoSize)));
|
||||
AddViewMediaHandler(thumb->entity(), controller, e);
|
||||
} else if (s.photoId || (e.photoId && !e.subscriptionUntil.isNull())) {
|
||||
if (!(s.cancelled || s.expired || s.cancelledByBot)) {
|
||||
const auto widget = Ui::CreateChild<Ui::RpWidget>(content);
|
||||
AddMiniStars(content, widget, stUser, st::boxWideWidth, 1.5);
|
||||
}
|
||||
const auto photoId = s.photoId ? s.photoId : e.photoId;
|
||||
const auto callback = [=](Fn<void()> update) {
|
||||
return Ui::GenerateCreditsPaintEntryCallback(
|
||||
session->data().photo(photoId),
|
||||
std::move(update));
|
||||
};
|
||||
content->add(object_ptr<Ui::CenterWrap<>>(
|
||||
content,
|
||||
GenericEntryPhoto(content, callback, stUser.photoSize)));
|
||||
} else if (peer && !e.gift) {
|
||||
if (e.subscriptionUntil.isNull() && s.until.isNull()) {
|
||||
content->add(object_ptr<Ui::CenterWrap<>>(
|
||||
|
@ -950,11 +1000,13 @@ void ReceiptCreditsBox(
|
|||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
rpl::single(!s.until.isNull()
|
||||
rpl::single(!s.title.isEmpty()
|
||||
? s.title
|
||||
: !s.until.isNull()
|
||||
? tr::lng_credits_box_subscription_title(tr::now)
|
||||
: isPrize
|
||||
? tr::lng_credits_box_history_entry_giveaway_name(tr::now)
|
||||
: !e.subscriptionUntil.isNull()
|
||||
: (!e.subscriptionUntil.isNull() && e.title.isEmpty())
|
||||
? tr::lng_credits_box_history_entry_subscription(tr::now)
|
||||
: !e.title.isEmpty()
|
||||
? e.title
|
||||
|
@ -1270,16 +1322,48 @@ void ReceiptCreditsBox(
|
|||
st::creditsBoxAboutDivider)));
|
||||
}
|
||||
if (s) {
|
||||
const auto user = peer ? peer->asUser() : nullptr;
|
||||
const auto bot = (user && !user->isSelf()) ? user : nullptr;
|
||||
const auto toCancel = !s.expired && !s.cancelled && !s.cancelledByBot;
|
||||
if (toCancel) {
|
||||
Ui::AddSkip(content);
|
||||
}
|
||||
Ui::AddSkip(content);
|
||||
auto label = object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
s.cancelled
|
||||
(s.cancelledByBot && bot)
|
||||
? tr::lng_credits_subscription_off_by_bot_about(
|
||||
lt_bot,
|
||||
rpl::single(bot->name()))
|
||||
: toCancel
|
||||
? tr::lng_credits_subscription_on_button()
|
||||
: s.cancelled
|
||||
? tr::lng_credits_subscription_off_about()
|
||||
: tr::lng_credits_subscription_on_about(
|
||||
lt_date,
|
||||
rpl::single(langDayOfMonthFull(s.until.date()))),
|
||||
st::creditsBoxAboutDivider);
|
||||
if (s.cancelled) {
|
||||
if (toCancel) {
|
||||
label->setClickHandlerFilter([=](
|
||||
const auto &,
|
||||
Qt::MouseButton button) {
|
||||
if (button != Qt::LeftButton) {
|
||||
return false;
|
||||
}
|
||||
const auto done = [=, weak = Ui::MakeWeak(box)] {
|
||||
ProcessReceivedSubscriptions(weak, session);
|
||||
};
|
||||
const auto fail = [=, s = box->uiShow()](const QString &e) {
|
||||
s->showToast(e);
|
||||
};
|
||||
Api::EditCreditsSubscription(session, s.id, true, done, fail);
|
||||
return true;
|
||||
});
|
||||
label->setMarkedText(
|
||||
Ui::Text::Link(
|
||||
tr::lng_credits_subscription_on_button(tr::now),
|
||||
u"internal:"_q));
|
||||
} else if (s.cancelled || s.cancelledByBot) {
|
||||
label->setTextColorOverride(st::menuIconAttentionColor->c);
|
||||
}
|
||||
box->addRow(
|
||||
|
@ -1290,41 +1374,26 @@ void ReceiptCreditsBox(
|
|||
|
||||
if (e.peerType == Data::CreditsHistoryEntry::PeerType::PremiumBot) {
|
||||
const auto widget = Ui::CreateChild<Ui::RpWidget>(content);
|
||||
using ColoredMiniStars = Ui::Premium::ColoredMiniStars;
|
||||
const auto stars = widget->lifetime().make_state<ColoredMiniStars>(
|
||||
widget,
|
||||
false,
|
||||
Ui::Premium::MiniStars::Type::BiStars);
|
||||
stars->setColorOverride(Ui::Premium::CreditsIconGradientStops());
|
||||
widget->resize(
|
||||
st::boxWidth - stUser.photoSize,
|
||||
stUser.photoSize * 2);
|
||||
content->sizeValue(
|
||||
) | rpl::start_with_next([=](const QSize &size) {
|
||||
widget->moveToLeft(stUser.photoSize / 2, 0);
|
||||
const auto starsRect = Rect(widget->size());
|
||||
stars->setPosition(starsRect.topLeft());
|
||||
stars->setSize(starsRect.size());
|
||||
widget->lower();
|
||||
}, widget->lifetime());
|
||||
widget->paintRequest(
|
||||
) | rpl::start_with_next([=](const QRect &r) {
|
||||
auto p = QPainter(widget);
|
||||
p.fillRect(r, Qt::transparent);
|
||||
stars->paint(p);
|
||||
}, widget->lifetime());
|
||||
AddMiniStars(content, widget, stUser, st::boxWideWidth, 2);
|
||||
}
|
||||
|
||||
const auto rejoinByApi = base::unixtime::serialize(s.until)
|
||||
> base::unixtime::now();
|
||||
const auto rejoinByInvite = !s.inviteHash.isEmpty();
|
||||
const auto rejoinBySlug = !s.slug.isEmpty();
|
||||
const auto toRenew = (s.cancelled || s.expired)
|
||||
&& !s.inviteHash.isEmpty();
|
||||
const auto toCancel = !toRenew && s;
|
||||
&& (rejoinByApi || rejoinByInvite)
|
||||
&& !s.cancelledByBot;
|
||||
const auto toRejoin = (s.cancelled || s.expired)
|
||||
&& rejoinBySlug
|
||||
&& !s.cancelledByBot;
|
||||
auto confirmText = rpl::conditional(
|
||||
state->confirmButtonBusy.value(),
|
||||
rpl::single(QString()),
|
||||
(toRenew
|
||||
? tr::lng_credits_subscription_off_button()
|
||||
: toCancel
|
||||
? tr::lng_credits_subscription_on_button()
|
||||
: toRejoin
|
||||
? tr::lng_credits_subscription_off_rejoin_button()
|
||||
: (canConvert || couldConvert || nonConvertible)
|
||||
? (e.savedToProfile
|
||||
? tr::lng_gift_display_on_page_hide()
|
||||
|
@ -1372,45 +1441,57 @@ void ReceiptCreditsBox(
|
|||
save,
|
||||
done);
|
||||
}
|
||||
} else if (toRejoin) {
|
||||
if (const auto window = weakWindow.get()) {
|
||||
const auto finish = [=](Payments::CheckoutResult&&) {
|
||||
ProcessReceivedSubscriptions(weak, session);
|
||||
};
|
||||
Payments::CheckoutProcess::Start(
|
||||
&window->session(),
|
||||
s.slug,
|
||||
[](auto) {},
|
||||
Payments::ProcessNonPanelPaymentFormFactory(
|
||||
window,
|
||||
finish));
|
||||
}
|
||||
} else if (toRenew && s.expired) {
|
||||
Api::CheckChatInvite(controller, s.inviteHash, nullptr, [=] {
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
ProcessReceivedSubscriptions(weak, session);
|
||||
});
|
||||
} else {
|
||||
using Flag = MTPpayments_ChangeStarsSubscription::Flag;
|
||||
session->api().request(
|
||||
MTPpayments_ChangeStarsSubscription(
|
||||
MTP_flags(Flag::f_canceled),
|
||||
MTP_inputPeerSelf(),
|
||||
MTP_string(s.id),
|
||||
MTP_bool(toCancel)
|
||||
)).done([=] {
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
}).fail([=, show = box->uiShow()](const MTP::Error &error) {
|
||||
const auto done = [=] {
|
||||
ProcessReceivedSubscriptions(weak, session);
|
||||
};
|
||||
const auto fail = [=, show = box->uiShow()](const QString &e) {
|
||||
if (const auto strong = weak.data()) {
|
||||
state->confirmButtonBusy = false;
|
||||
}
|
||||
show->showToast(error.type());
|
||||
}).send();
|
||||
show->showToast(e);
|
||||
};
|
||||
Api::EditCreditsSubscription(session, s.id, false, done, fail);
|
||||
}
|
||||
};
|
||||
|
||||
const auto willBusy = toRejoin
|
||||
|| (peer
|
||||
&& (toRenew || canConvert || couldConvert || nonConvertible));
|
||||
if (willBusy) {
|
||||
const auto close = Ui::CreateChild<Ui::IconButton>(
|
||||
content,
|
||||
st::boxTitleClose);
|
||||
close->setClickedCallback([=] { box->closeBox(); });
|
||||
content->widthValue() | rpl::start_with_next([=](int) {
|
||||
close->moveToRight(0, 0);
|
||||
}, content->lifetime());
|
||||
}
|
||||
|
||||
const auto button = box->addButton(std::move(confirmText), [=] {
|
||||
if (state->confirmButtonBusy.current()
|
||||
|| state->convertButtonBusy.current()) {
|
||||
return;
|
||||
}
|
||||
state->confirmButtonBusy = true;
|
||||
if (peer
|
||||
&& (toRenew
|
||||
|| toCancel
|
||||
|| canConvert
|
||||
|| couldConvert
|
||||
|| nonConvertible)) {
|
||||
if (willBusy) {
|
||||
state->confirmButtonBusy = true;
|
||||
send();
|
||||
} else {
|
||||
box->closeBox();
|
||||
|
@ -1424,7 +1505,7 @@ void ReceiptCreditsBox(
|
|||
AddChildToWidgetCenter(button, loadingAnimation);
|
||||
loadingAnimation->showOn(state->confirmButtonBusy.value());
|
||||
}
|
||||
const auto buttonWidth = st::boxWidth
|
||||
const auto buttonWidth = st::boxWideWidth
|
||||
- rect::m::sum::h(st::giveawayGiftCodeBox.buttonPadding);
|
||||
|
||||
button->widthValue() | rpl::filter([=] {
|
||||
|
|
16
Telegram/SourceFiles/ui/color_indices.style
Normal file
16
Telegram/SourceFiles/ui/color_indices.style
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
colorIndexRed: 0;
|
||||
colorIndexOrange: 1;
|
||||
colorIndexGreen: 2;
|
||||
colorIndexSea: 3;
|
||||
colorIndexBlue: 4;
|
||||
colorIndexPurple: 5;
|
||||
colorIndexPink: 6;
|
||||
colorIndexYellow: 7;
|
|
@ -156,3 +156,20 @@ giftListAbout: FlatLabel(defaultFlatLabel) {
|
|||
giftListAboutMargin: margins(12px, 24px, 12px, 24px);
|
||||
giftBoxEmojiToggleTop: 7px;
|
||||
giftBoxLimitTop: 28px;
|
||||
|
||||
creditsHistoryEntriesList: PeerList(defaultPeerList) {
|
||||
padding: margins(
|
||||
0px,
|
||||
7px,
|
||||
0px,
|
||||
7px);
|
||||
item: PeerListItem(defaultPeerListItem) {
|
||||
height: 66px;
|
||||
photoPosition: point(18px, 6px);
|
||||
namePosition: point(70px, 6px);
|
||||
statusPosition: point(70px, 43px);
|
||||
photoSize: 42px;
|
||||
}
|
||||
}
|
||||
|
||||
subscriptionCreditsBadgePadding: margins(10px, 1px, 8px, 3px);
|
||||
|
|
|
@ -533,7 +533,7 @@ Fn<PaintRoundImageCallback(Fn<void()>)> PaintPreviewCallback(
|
|||
extended,
|
||||
std::move(update));
|
||||
};
|
||||
} else if (entry.photoId) {
|
||||
} else if (entry.photoId && entry.subscriptionUntil.isNull()) {
|
||||
const auto photo = session->data().photo(entry.photoId);
|
||||
return [=](Fn<void()> update) {
|
||||
return GenerateCreditsPaintEntryCallback(
|
||||
|
|
|
@ -57,6 +57,26 @@ ChatsFiltersTabs::ChatsFiltersTabs(
|
|||
Ui::DiscreteSlider::setSelectOnPress(false);
|
||||
}
|
||||
|
||||
bool ChatsFiltersTabs::setSectionsAndCheckChanged(
|
||||
std::vector<QString> &§ions) {
|
||||
const auto &was = sectionsRef();
|
||||
const auto changed = [&] {
|
||||
if (was.size() != sections.size()) {
|
||||
return true;
|
||||
}
|
||||
for (auto i = 0; i < sections.size(); i++) {
|
||||
if (was[i].label.toString() != sections[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
if (changed) {
|
||||
Ui::DiscreteSlider::setSections(std::move(sections));
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
int ChatsFiltersTabs::centerOfSection(int section) const {
|
||||
const auto widths = countSectionsWidths(0);
|
||||
auto result = 0;
|
||||
|
|
|
@ -27,6 +27,8 @@ public:
|
|||
not_null<Ui::RpWidget*> parent,
|
||||
const style::SettingsSlider &st);
|
||||
|
||||
bool setSectionsAndCheckChanged(std::vector<QString> &§ions);
|
||||
|
||||
[[nodiscard]] int centerOfSection(int section) const;
|
||||
void fitWidthToSections();
|
||||
void setUnreadCount(int index, int unreadCount, bool muted);
|
||||
|
|
|
@ -293,15 +293,18 @@ not_null<Ui::RpWidget*> AddChatFiltersTabsStrip(
|
|||
if ((list.size() <= 1 && !slider->width()) || state->ignoreRefresh) {
|
||||
return;
|
||||
}
|
||||
const auto sectionsChanged = slider->setSectionsAndCheckChanged(
|
||||
ranges::views::all(
|
||||
list
|
||||
) | ranges::views::transform([](const Data::ChatFilter &filter) {
|
||||
return filter.title().isEmpty()
|
||||
? tr::lng_filters_all_short(tr::now)
|
||||
: filter.title();
|
||||
}) | ranges::to_vector);
|
||||
if (!sectionsChanged) {
|
||||
return;
|
||||
}
|
||||
state->rebuildLifetime.destroy();
|
||||
auto sections = ranges::views::all(
|
||||
list
|
||||
) | ranges::views::transform([](const Data::ChatFilter &filter) {
|
||||
return filter.title().isEmpty()
|
||||
? tr::lng_filters_all_short(tr::now)
|
||||
: filter.title();
|
||||
}) | ranges::to_vector;
|
||||
slider->setSections(std::move(sections));
|
||||
slider->fitWidthToSections();
|
||||
{
|
||||
const auto reorderAll = session->user()->isPremium();
|
||||
|
|
72
Telegram/SourceFiles/ui/widgets/peer_bubble.cpp
Normal file
72
Telegram/SourceFiles/ui/widgets/peer_bubble.cpp
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
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 "ui/widgets/peer_bubble.h"
|
||||
|
||||
#include "data/data_peer.h"
|
||||
#include "info/profile/info_profile_values.h"
|
||||
#include "ui/controls/userpic_button.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_channel_earn.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_layers.h"
|
||||
|
||||
namespace Ui {
|
||||
|
||||
object_ptr<Ui::RpWidget> CreatePeerBubble(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<PeerData*> peer) {
|
||||
auto owned = object_ptr<Ui::RpWidget>(parent);
|
||||
const auto peerBubble = owned.data();
|
||||
peerBubble->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
const auto left = Ui::CreateChild<Ui::UserpicButton>(
|
||||
peerBubble,
|
||||
peer,
|
||||
st::uploadUserpicButton);
|
||||
const auto right = Ui::CreateChild<Ui::FlatLabel>(
|
||||
peerBubble,
|
||||
Info::Profile::NameValue(peer),
|
||||
st::channelEarnSemiboldLabel);
|
||||
const auto padding = st::chatGiveawayPeerPadding
|
||||
+ QMargins(st::chatGiveawayPeerPadding.left(), 0, 0, 0);
|
||||
rpl::combine(
|
||||
left->sizeValue(),
|
||||
right->sizeValue()
|
||||
) | rpl::start_with_next([=](
|
||||
const QSize &leftSize,
|
||||
const QSize &rightSize) {
|
||||
peerBubble->resize(
|
||||
leftSize.width() + rightSize.width() + rect::m::sum::h(padding),
|
||||
leftSize.height());
|
||||
left->moveToLeft(0, 0);
|
||||
right->moveToRight(padding.right() + st::lineWidth, padding.top());
|
||||
const auto maxRightSize = parent->width()
|
||||
- rect::m::sum::h(st::boxRowPadding)
|
||||
- rect::m::sum::h(padding)
|
||||
- leftSize.width();
|
||||
if ((rightSize.width() > maxRightSize) && (maxRightSize > 0)) {
|
||||
right->resizeToWidth(maxRightSize);
|
||||
}
|
||||
}, peerBubble->lifetime());
|
||||
peerBubble->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
auto p = QPainter(peerBubble);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::windowBgOver);
|
||||
const auto rect = peerBubble->rect();
|
||||
const auto radius = rect.height() / 2;
|
||||
p.drawRoundedRect(rect, radius, radius);
|
||||
}, peerBubble->lifetime());
|
||||
|
||||
return owned;
|
||||
}
|
||||
|
||||
} // namespace Ui
|
26
Telegram/SourceFiles/ui/widgets/peer_bubble.h
Normal file
26
Telegram/SourceFiles/ui/widgets/peer_bubble.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
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
|
||||
|
||||
template <typename Object>
|
||||
class object_ptr;
|
||||
|
||||
class PeerData;
|
||||
|
||||
namespace Ui {
|
||||
class RpWidget;
|
||||
class VerticalLayout;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Ui {
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> CreatePeerBubble(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<PeerData*> peer);
|
||||
|
||||
} // namespace Ui
|
|
@ -281,7 +281,7 @@ private:
|
|||
void addInfo();
|
||||
void addStoryArchive();
|
||||
void addNewWindow();
|
||||
void addToggleFolder();
|
||||
void addToggleFolder(bool onlyForChannels);
|
||||
void addToggleUnreadMark();
|
||||
void addToggleArchive();
|
||||
void addClearHistory();
|
||||
|
@ -620,12 +620,16 @@ void Filler::addStoryArchive() {
|
|||
}, &st::menuIconStoriesArchiveSection);
|
||||
}
|
||||
|
||||
void Filler::addToggleFolder() {
|
||||
void Filler::addToggleFolder(bool onlyForChannels) {
|
||||
const auto controller = _controller;
|
||||
const auto history = _request.key.history();
|
||||
if (_topic || !history || !history->owner().chatsFilters().has()) {
|
||||
return;
|
||||
}
|
||||
if (onlyForChannels
|
||||
&& (!history->peer->isChannel() || !history->inChatList())) {
|
||||
return;
|
||||
}
|
||||
_addAction(PeerMenuCallback::Args{
|
||||
.text = tr::lng_filters_menu_add(tr::now),
|
||||
.handler = nullptr,
|
||||
|
@ -1409,7 +1413,7 @@ void Filler::fillContextMenuActions() {
|
|||
addToggleMuteSubmenu(false);
|
||||
addToggleUnreadMark();
|
||||
addToggleTopicClosed();
|
||||
addToggleFolder();
|
||||
addToggleFolder(false);
|
||||
if (const auto user = _peer->asUser()) {
|
||||
if (!user->isContact()) {
|
||||
addBlockUser();
|
||||
|
@ -1459,6 +1463,7 @@ void Filler::fillProfileActions() {
|
|||
addToggleTopicClosed();
|
||||
addViewDiscussion();
|
||||
addExportChat();
|
||||
addToggleFolder(true);
|
||||
addBlockUser();
|
||||
addReport();
|
||||
addLeaveChat();
|
||||
|
@ -2070,7 +2075,17 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
|||
|
||||
class ListBox final : public PeerListBox {
|
||||
public:
|
||||
using PeerListBox::PeerListBox;
|
||||
ListBox(
|
||||
QWidget *parent,
|
||||
std::unique_ptr<PeerListController> controller,
|
||||
Fn<void(not_null<ListBox*>)> init)
|
||||
: PeerListBox(
|
||||
parent,
|
||||
std::move(controller),
|
||||
[=](not_null<PeerListBox*> box) {
|
||||
init(static_cast<ListBox*>(box.get()));
|
||||
}) {
|
||||
}
|
||||
|
||||
void setBottomSkip(int bottomSkip) {
|
||||
PeerListBox::setInnerBottomSkip(bottomSkip);
|
||||
|
@ -2095,9 +2110,21 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
|||
_forwardOptions = forwardOptions;
|
||||
}
|
||||
|
||||
not_null<PeerListContent*> peerListContent() const {
|
||||
return PeerListBox::content();
|
||||
}
|
||||
|
||||
void setFilterId(FilterId filterId) {
|
||||
_filterId = filterId;
|
||||
}
|
||||
[[nodiscard]] FilterId filterId() const {
|
||||
return _filterId;
|
||||
}
|
||||
|
||||
private:
|
||||
rpl::event_stream<> _focusRequests;
|
||||
Ui::ForwardOptions _forwardOptions;
|
||||
FilterId _filterId = 0;
|
||||
|
||||
};
|
||||
|
||||
|
@ -2115,6 +2142,12 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
|||
}) {
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> createRestoredRow(
|
||||
not_null<PeerData*> peer) override final {
|
||||
return ChooseRecipientBoxController::createRow(
|
||||
peer->owner().history(peer));
|
||||
}
|
||||
|
||||
using PeerListController::setSearchNoResultsText;
|
||||
|
||||
void rowClicked(not_null<PeerListRow*> row) override final {
|
||||
|
@ -2167,166 +2200,48 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
|||
base::unique_qptr<Ui::PopupMenu> menu;
|
||||
};
|
||||
|
||||
const auto applyFilter = [=](not_null<PeerListBox*> box, FilterId id) {
|
||||
const auto applyFilter = [=](not_null<ListBox*> box, FilterId id) {
|
||||
box->scrollToY(0);
|
||||
auto &filters = session->data().chatsFilters();
|
||||
const auto &list = filters.list();
|
||||
if (list.size() <= 1) {
|
||||
return;
|
||||
}
|
||||
const auto pinnedList = [&](
|
||||
not_null<Dialogs::MainList*> list,
|
||||
bool foundSelf) {
|
||||
const auto pinned = list->pinned()->order();
|
||||
auto peers = std::vector<not_null<PeerData*>>();
|
||||
peers.reserve(pinned.size());
|
||||
for (const auto &pin : pinned) {
|
||||
if (!foundSelf && pin.peer()->isSelf()) {
|
||||
peers.insert(peers.begin(), pin.peer());
|
||||
foundSelf = true;
|
||||
} else {
|
||||
peers.push_back(pin.peer());
|
||||
}
|
||||
}
|
||||
if (!foundSelf) {
|
||||
peers.insert(peers.begin(), session->user());
|
||||
}
|
||||
return peers;
|
||||
};
|
||||
const auto folder = session->data().folderLoaded(
|
||||
Data::Folder::kId);
|
||||
const auto pinned = pinnedList(
|
||||
id
|
||||
? filters.chatsList(id)
|
||||
: session->data().chatsList(nullptr),
|
||||
!!id);
|
||||
const auto pinnedInFolder = (!id && folder)
|
||||
? pinnedList(folder->chatsList(), true)
|
||||
: std::vector<not_null<PeerData*>>();
|
||||
box->peerListSortRows([&](
|
||||
const PeerListRow &r1,
|
||||
const PeerListRow &r2) {
|
||||
{ // Pinned to top.
|
||||
auto it1 = pinned.end();
|
||||
auto it2 = pinned.end();
|
||||
for (auto it = pinned.begin(); it != pinned.end(); ++it) {
|
||||
if ((*it) == r1.peer()) {
|
||||
it1 = it;
|
||||
}
|
||||
if ((*it) == r2.peer()) {
|
||||
it2 = it;
|
||||
}
|
||||
if (it1 != pinned.end() && it2 != pinned.end()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (it1 == pinned.end() && it2 != pinned.end()) {
|
||||
return false;
|
||||
} else if (it2 == pinned.end() && it1 != pinned.end()) {
|
||||
return true;
|
||||
} else if (it1 != pinned.end() && it2 != pinned.end()) {
|
||||
return it1 < it2;
|
||||
}
|
||||
}
|
||||
{ // Pinned to bottom.
|
||||
const auto &indexed = session->data().contactsNoChatsList();
|
||||
auto it1 = indexed->end();
|
||||
auto it2 = indexed->end();
|
||||
for (auto it = indexed->begin(); it != indexed->end(); ++it) {
|
||||
if (it->get()->key().peer() == r1.peer()) {
|
||||
it1 = it;
|
||||
}
|
||||
if (it->get()->key().peer() == r2.peer()) {
|
||||
it2 = it;
|
||||
}
|
||||
if (it1 != indexed->end() && it2 != indexed->end()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (it1 == indexed->end() && it2 != indexed->end()) {
|
||||
return true;
|
||||
} else if (it2 == indexed->end() && it1 != indexed->end()) {
|
||||
return false;
|
||||
} else if (it1 != indexed->end() && it2 != indexed->end()) {
|
||||
return it1 > it2;
|
||||
}
|
||||
}
|
||||
if (folder) {
|
||||
const auto pinned1 = ranges::find(pinnedInFolder, r1.peer());
|
||||
const auto pinned2 = ranges::find(pinnedInFolder, r2.peer());
|
||||
const auto isPinned1 = pinned1 != pinnedInFolder.end();
|
||||
const auto isPinned2 = pinned2 != pinnedInFolder.end();
|
||||
if (isPinned1 && isPinned2) {
|
||||
return pinned1 < pinned2;
|
||||
}
|
||||
|
||||
const auto &indexed = folder->chatsList()->indexed();
|
||||
auto it1 = indexed->end();
|
||||
auto it2 = indexed->end();
|
||||
for (auto it = indexed->begin(); it != indexed->end(); ++it) {
|
||||
if (it->get()->key().peer() == r1.peer()) {
|
||||
it1 = it;
|
||||
}
|
||||
if (it->get()->key().peer() == r2.peer()) {
|
||||
it2 = it;
|
||||
}
|
||||
if (it1 != indexed->end() && it2 != indexed->end()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
const auto isFoldered1 = it1 != indexed->end();
|
||||
const auto isFoldered2 = it2 != indexed->end();
|
||||
if (isPinned1 && !isPinned2) {
|
||||
return isFoldered2;
|
||||
}
|
||||
if (isPinned2 && !isPinned1) {
|
||||
return !isFoldered1;
|
||||
}
|
||||
if (!isPinned1 && !isPinned2) {
|
||||
if (!isFoldered1 && isFoldered2) {
|
||||
return true;
|
||||
} else if (!isFoldered2 && isFoldered1) {
|
||||
return false;
|
||||
} else if (isFoldered1 && isFoldered2) {
|
||||
return it1 < it2;
|
||||
}
|
||||
}
|
||||
}
|
||||
const auto history1 = session->data().history(r1.peer());
|
||||
const auto history2 = session->data().history(r2.peer());
|
||||
const auto date1 = history1->lastMessage()
|
||||
? history1->lastMessage()->date()
|
||||
: TimeId(0);
|
||||
const auto date2 = history2->lastMessage()
|
||||
? history2->lastMessage()->date()
|
||||
: TimeId(0);
|
||||
return date1 > date2;
|
||||
});
|
||||
const auto filter = ranges::find(
|
||||
list,
|
||||
id,
|
||||
&Data::ChatFilter::id);
|
||||
if (filter == list.end()) {
|
||||
if (box->filterId() == id) {
|
||||
return;
|
||||
}
|
||||
box->peerListPartitionRows([&](const PeerListRow &row) {
|
||||
const auto rowPtr = const_cast<PeerListRow*>(&row);
|
||||
if (!filter->id()) {
|
||||
box->peerListSetRowHidden(rowPtr, false);
|
||||
} else {
|
||||
const auto result = filter->contains(
|
||||
session->data().history(row.peer()));
|
||||
box->peerListSetRowHidden(rowPtr, !result);
|
||||
box->setFilterId(id);
|
||||
|
||||
using SavedState = PeerListController::SavedStateBase;
|
||||
auto state = std::make_unique<PeerListState>();
|
||||
state->controllerState = std::make_unique<SavedState>();
|
||||
|
||||
const auto addList = [&](auto chats) {
|
||||
for (const auto &row : chats->all()) {
|
||||
if (const auto history = row->history()) {
|
||||
state->list.push_back(history->peer);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
box->peerListRefreshRows();
|
||||
};
|
||||
|
||||
if (!id) {
|
||||
state->list.push_back(session->user());
|
||||
addList(session->data().chatsList()->indexed());
|
||||
const auto folderId = Data::Folder::kId;
|
||||
if (const auto folder = session->data().folderLoaded(folderId)) {
|
||||
addList(folder->chatsList()->indexed());
|
||||
}
|
||||
addList(session->data().contactsNoChatsList());
|
||||
} else {
|
||||
addList(session->data().chatsFilters().chatsList(id)->indexed());
|
||||
}
|
||||
box->peerListContent()->restoreState(std::move(state));
|
||||
};
|
||||
|
||||
const auto state = [&] {
|
||||
auto controller = std::make_unique<Controller>(session);
|
||||
const auto controllerRaw = controller.get();
|
||||
auto init = [=](not_null<PeerListBox*> box) {
|
||||
auto init = [=](not_null<ListBox*> box) {
|
||||
controllerRaw->setSearchNoResultsText(
|
||||
tr::lng_bot_chats_not_found(tr::now));
|
||||
box->setSpecialTabMode(true);
|
||||
|
|
|
@ -1874,6 +1874,10 @@ bool SessionController::switchInlineQuery(
|
|||
int(textWithTags.text.size()),
|
||||
Ui::kQFixedMax
|
||||
};
|
||||
if (to.currentReplyTo.messageId.msg == to.currentReplyTo.topicRootId
|
||||
&& to.currentReplyTo.quote.empty()) {
|
||||
to.currentReplyTo.messageId.msg = MsgId();
|
||||
}
|
||||
auto draft = std::make_unique<Data::Draft>(
|
||||
textWithTags,
|
||||
to.currentReplyTo,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
AppVersion 5008002
|
||||
AppVersion 5008003
|
||||
AppVersionStrMajor 5.8
|
||||
AppVersionStrSmall 5.8.2
|
||||
AppVersionStr 5.8.2
|
||||
AppVersionStrSmall 5.8.3
|
||||
AppVersionStr 5.8.3
|
||||
BetaChannel 0
|
||||
AlphaVersion 0
|
||||
AppVersionOriginal 5.8.2
|
||||
AppVersionOriginal 5.8.3
|
||||
|
|
|
@ -21,6 +21,7 @@ set(style_files
|
|||
ui/chat/chat.style
|
||||
ui/effects/credits.style
|
||||
ui/effects/premium.style
|
||||
ui/color_indices.style
|
||||
boxes/boxes.style
|
||||
dialogs/dialogs.style
|
||||
chat_helpers/chat_helpers.style
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
5.8.3 (23.11.24)
|
||||
|
||||
- Ctrl+Click on Reply in groups to reply in another chat.
|
||||
- Fix freeze on forward messages box opening.
|
||||
- Improve phrases in bot subscriptions.
|
||||
- Several bugfixes.
|
||||
|
||||
5.8.2 (19.11.24)
|
||||
|
||||
- Improve bottom label color in mini apps.
|
||||
|
|
Loading…
Add table
Reference in a new issue