mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +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_box.h
|
||||||
boxes/peer_list_controllers.cpp
|
boxes/peer_list_controllers.cpp
|
||||||
boxes/peer_list_controllers.h
|
boxes/peer_list_controllers.h
|
||||||
|
boxes/peer_list_widgets.cpp
|
||||||
|
boxes/peer_list_widgets.h
|
||||||
boxes/peer_lists_box.cpp
|
boxes/peer_lists_box.cpp
|
||||||
boxes/peer_lists_box.h
|
boxes/peer_lists_box.h
|
||||||
boxes/passcode_box.cpp
|
boxes/passcode_box.cpp
|
||||||
|
@ -1631,6 +1633,8 @@ PRIVATE
|
||||||
ui/widgets/label_with_custom_emoji.h
|
ui/widgets/label_with_custom_emoji.h
|
||||||
ui/widgets/chat_filters_tabs_strip.cpp
|
ui/widgets/chat_filters_tabs_strip.cpp
|
||||||
ui/widgets/chat_filters_tabs_strip.h
|
ui/widgets/chat_filters_tabs_strip.h
|
||||||
|
ui/widgets/peer_bubble.cpp
|
||||||
|
ui/widgets/peer_bubble.h
|
||||||
ui/countryinput.cpp
|
ui/countryinput.cpp
|
||||||
ui/countryinput.h
|
ui/countryinput.h
|
||||||
ui/dynamic_thumbnails.cpp
|
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_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_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_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" = "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_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.";
|
"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_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_channel" = "Sorry, copying from this channel is disabled by admins.";
|
||||||
"lng_error_nocopy_story" = "Sorry, the creator of this story disabled copying.";
|
"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" = "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_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?";
|
"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_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_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_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_took_screenshot" = "{from} took a screenshot!";
|
||||||
"lng_action_you_took_screenshot" = "You 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}.";
|
"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#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#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_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_photo" = "a photo";
|
||||||
"lng_credits_box_out_photos#one" = "{count} photo";
|
"lng_credits_box_out_photos#one" = "{count} photo";
|
||||||
"lng_credits_box_out_photos#other" = "{count} photos";
|
"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_subscriber_subtitle" = "appx. {total} per month";
|
||||||
|
|
||||||
"lng_credits_subscription_row_to" = "Subscription";
|
"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_from" = "Subscribed";
|
||||||
|
|
||||||
"lng_credits_subscription_row_next_on" = "Renews";
|
"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_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_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_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_on" = "renews on {date}";
|
||||||
"lng_credits_subscription_status_off" = "expires on {date}";
|
"lng_credits_subscription_status_off" = "expires on {date}";
|
||||||
"lng_credits_subscription_status_none" = "expired on {date}";
|
"lng_credits_subscription_status_none" = "expired on {date}";
|
||||||
"lng_credits_subscription_status_off_right" = "canceled";
|
"lng_credits_subscription_status_off_right" = "canceled";
|
||||||
"lng_credits_subscription_status_none_right" = "expired";
|
"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#one" = "{count} Star Needed";
|
||||||
"lng_credits_small_balance_title#other" = "{count} Stars Needed";
|
"lng_credits_small_balance_title#other" = "{count} Stars Needed";
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||||
ProcessorArchitecture="ARCHITECTURE"
|
ProcessorArchitecture="ARCHITECTURE"
|
||||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||||
Version="5.8.2.0" />
|
Version="5.8.3.0" />
|
||||||
<Properties>
|
<Properties>
|
||||||
<DisplayName>Telegram Desktop</DisplayName>
|
<DisplayName>Telegram Desktop</DisplayName>
|
||||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||||
|
|
|
@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 5,8,2,0
|
FILEVERSION 5,8,3,0
|
||||||
PRODUCTVERSION 5,8,2,0
|
PRODUCTVERSION 5,8,3,0
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -62,10 +62,10 @@ BEGIN
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "Radolyn Labs"
|
VALUE "CompanyName", "Radolyn Labs"
|
||||||
VALUE "FileDescription", "AyuGram Desktop"
|
VALUE "FileDescription", "AyuGram Desktop"
|
||||||
VALUE "FileVersion", "5.8.2.0"
|
VALUE "FileVersion", "5.8.3.0"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
||||||
VALUE "ProductName", "AyuGram Desktop"
|
VALUE "ProductName", "AyuGram Desktop"
|
||||||
VALUE "ProductVersion", "5.8.2.0"
|
VALUE "ProductVersion", "5.8.3.0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|
|
@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 5,8,2,0
|
FILEVERSION 5,8,3,0
|
||||||
PRODUCTVERSION 5,8,2,0
|
PRODUCTVERSION 5,8,3,0
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -53,10 +53,10 @@ BEGIN
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "Radolyn Labs"
|
VALUE "CompanyName", "Radolyn Labs"
|
||||||
VALUE "FileDescription", "AyuGram Desktop Updater"
|
VALUE "FileDescription", "AyuGram Desktop Updater"
|
||||||
VALUE "FileVersion", "5.8.2.0"
|
VALUE "FileVersion", "5.8.3.0"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
||||||
VALUE "ProductName", "AyuGram Desktop"
|
VALUE "ProductName", "AyuGram Desktop"
|
||||||
VALUE "ProductVersion", "5.8.2.0"
|
VALUE "ProductVersion", "5.8.3.0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "api/api_chat_invite.h"
|
#include "api/api_chat_invite.h"
|
||||||
|
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
|
#include "api/api_credits.h"
|
||||||
#include "boxes/premium_limits_box.h"
|
#include "boxes/premium_limits_box.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "data/components/credits.h"
|
#include "data/components/credits.h"
|
||||||
|
@ -295,20 +296,39 @@ void ConfirmSubscriptionBox(
|
||||||
const auto buttonWidth = state->saveButton
|
const auto buttonWidth = state->saveButton
|
||||||
? state->saveButton->width()
|
? state->saveButton->width()
|
||||||
: 0;
|
: 0;
|
||||||
|
const auto finish = [=] {
|
||||||
|
state->api = std::nullopt;
|
||||||
|
state->loading.force_assign(false);
|
||||||
|
if (const auto strong = weak.data()) {
|
||||||
|
strong->closeBox();
|
||||||
|
}
|
||||||
|
};
|
||||||
state->api->request(
|
state->api->request(
|
||||||
MTPpayments_SendStarsForm(
|
MTPpayments_SendStarsForm(
|
||||||
MTP_long(formId),
|
MTP_long(formId),
|
||||||
MTP_inputInvoiceChatInviteSubscription(MTP_string(hash)))
|
MTP_inputInvoiceChatInviteSubscription(MTP_string(hash)))
|
||||||
).done([=](const MTPpayments_PaymentResult &result) {
|
).done([=](const MTPpayments_PaymentResult &result) {
|
||||||
state->api = std::nullopt;
|
|
||||||
state->loading.force_assign(false);
|
|
||||||
result.match([&](const MTPDpayments_paymentResult &data) {
|
result.match([&](const MTPDpayments_paymentResult &data) {
|
||||||
session->api().applyUpdates(data.vupdates());
|
session->api().applyUpdates(data.vupdates());
|
||||||
}, [](const MTPDpayments_paymentVerificationNeeded &data) {
|
}, [](const MTPDpayments_paymentVerificationNeeded &data) {
|
||||||
});
|
});
|
||||||
if (weak) {
|
const auto refill = session->data().activeCreditsSubsRebuilder();
|
||||||
box->closeBox();
|
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) {
|
}).fail([=](const MTP::Error &error) {
|
||||||
const auto id = error.type();
|
const auto id = error.type();
|
||||||
if (weak) {
|
if (weak) {
|
||||||
|
|
|
@ -133,17 +133,26 @@ constexpr auto kTransactionsLimit = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] Data::SubscriptionEntry SubscriptionFromTL(
|
[[nodiscard]] Data::SubscriptionEntry SubscriptionFromTL(
|
||||||
const MTPStarsSubscription &tl) {
|
const MTPStarsSubscription &tl,
|
||||||
|
not_null<PeerData*> peer) {
|
||||||
return Data::SubscriptionEntry{
|
return Data::SubscriptionEntry{
|
||||||
.id = qs(tl.data().vid()),
|
.id = qs(tl.data().vid()),
|
||||||
.inviteHash = qs(tl.data().vchat_invite_hash().value_or_empty()),
|
.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),
|
.until = base::unixtime::parse(tl.data().vuntil_date().v),
|
||||||
.subscription = Data::PeerSubscription{
|
.subscription = Data::PeerSubscription{
|
||||||
.credits = tl.data().vpricing().data().vamount().v,
|
.credits = tl.data().vpricing().data().vamount().v,
|
||||||
.period = tl.data().vpricing().data().vperiod().v,
|
.period = tl.data().vpricing().data().vperiod().v,
|
||||||
},
|
},
|
||||||
.barePeerId = peerFromMTP(tl.data().vpeer()).value,
|
.barePeerId = peerFromMTP(tl.data().vpeer()).value,
|
||||||
|
.photoId = (tl.data().vphoto()
|
||||||
|
? peer->owner().photoFromWeb(
|
||||||
|
*tl.data().vphoto(),
|
||||||
|
ImageLocation())->id
|
||||||
|
: 0),
|
||||||
.cancelled = tl.data().is_canceled(),
|
.cancelled = tl.data().is_canceled(),
|
||||||
|
.cancelledByBot = tl.data().is_bot_canceled(),
|
||||||
.expired = (base::unixtime::now() > tl.data().vuntil_date().v),
|
.expired = (base::unixtime::now() > tl.data().vuntil_date().v),
|
||||||
.canRefulfill = tl.data().is_can_refulfill(),
|
.canRefulfill = tl.data().is_can_refulfill(),
|
||||||
};
|
};
|
||||||
|
@ -166,7 +175,7 @@ constexpr auto kTransactionsLimit = 100;
|
||||||
if (const auto history = data.vsubscriptions()) {
|
if (const auto history = data.vsubscriptions()) {
|
||||||
subscriptions.reserve(history->v.size());
|
subscriptions.reserve(history->v.size());
|
||||||
for (const auto &tl : history->v) {
|
for (const auto &tl : history->v) {
|
||||||
subscriptions.push_back(SubscriptionFromTL(tl));
|
subscriptions.push_back(SubscriptionFromTL(tl, peer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Data::CreditsStatusSlice{
|
return Data::CreditsStatusSlice{
|
||||||
|
@ -463,4 +472,20 @@ Data::CreditsGiveawayOptions CreditsGiveawayOptions::options() const {
|
||||||
return _options;
|
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
|
} // namespace Api
|
||||||
|
|
|
@ -109,4 +109,11 @@ private:
|
||||||
[[nodiscard]] rpl::producer<not_null<PeerData*>> PremiumPeerBot(
|
[[nodiscard]] rpl::producer<not_null<PeerData*>> PremiumPeerBot(
|
||||||
not_null<Main::Session*> session);
|
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
|
} // namespace Api
|
||||||
|
|
|
@ -582,6 +582,14 @@ void ApiWrap::sendMessageFail(
|
||||||
: tr::lng_error_noforwards_group(tr::now), kJoinErrorDuration);
|
: tr::lng_error_noforwards_group(tr::now), kJoinErrorDuration);
|
||||||
} else if (error == u"PREMIUM_ACCOUNT_REQUIRED"_q) {
|
} else if (error == u"PREMIUM_ACCOUNT_REQUIRED"_q) {
|
||||||
Settings::ShowPremium(&session(), "premium_stickers");
|
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)) {
|
if (const auto item = _session->data().message(itemId)) {
|
||||||
Assert(randomId != 0);
|
Assert(randomId != 0);
|
||||||
|
|
|
@ -7,7 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "boxes/filters/edit_filter_chats_list.h"
|
#include "boxes/filters/edit_filter_chats_list.h"
|
||||||
|
|
||||||
|
#include "data/data_chat_filters.h"
|
||||||
#include "data/data_premium_limits.h"
|
#include "data/data_premium_limits.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
@ -125,7 +127,15 @@ Flag TypeRow::flag() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
ExceptionRow::ExceptionRow(not_null<History*> history) : Row(history) {
|
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));
|
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()) {
|
if (!entry.id.isEmpty()) {
|
||||||
constexpr auto kOneLineCount = 22;
|
constexpr auto kOneLineCount = 24;
|
||||||
const auto oneLine = entry.id.size() <= kOneLineCount;
|
const auto oneLine = entry.id.length() <= 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');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto label = object_ptr<Ui::FlatLabel>(
|
auto label = object_ptr<Ui::FlatLabel>(
|
||||||
table,
|
table,
|
||||||
rpl::single(
|
rpl::single(
|
||||||
Ui::Text::Wrapped(
|
Ui::Text::Wrapped({ entry.id }, EntityType::Code, {})),
|
||||||
{ oneLine ? entry.id : std::move(multiLine) },
|
|
||||||
EntityType::Code,
|
|
||||||
{})),
|
|
||||||
oneLine
|
oneLine
|
||||||
? st::giveawayGiftCodeValue
|
? st::giveawayGiftCodeValue
|
||||||
: st::giveawayGiftCodeValueMultiline);
|
: st::giveawayGiftCodeValueMultiline);
|
||||||
|
@ -1324,11 +1319,24 @@ void AddSubscriptionEntryTable(
|
||||||
st::giveawayGiftCodeTable),
|
st::giveawayGiftCodeTable),
|
||||||
st::giveawayGiftCodeTableMargin);
|
st::giveawayGiftCodeTableMargin);
|
||||||
const auto peerId = PeerId(s.barePeerId);
|
const auto peerId = PeerId(s.barePeerId);
|
||||||
|
const auto user = peerIsUser(peerId)
|
||||||
|
? controller->session().data().peer(peerId)->asUser()
|
||||||
|
: nullptr;
|
||||||
AddTableRow(
|
AddTableRow(
|
||||||
table,
|
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,
|
controller,
|
||||||
peerId);
|
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.until.isNull()) {
|
||||||
if (s.subscription.period > 0) {
|
if (s.subscription.period > 0) {
|
||||||
const auto subscribed = s.until.addSecs(-s.subscription.period);
|
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 "settings/settings_credits_graphics.h"
|
||||||
#include "ui/boxes/confirm_box.h"
|
#include "ui/boxes/confirm_box.h"
|
||||||
#include "ui/controls/userpic_button.h"
|
#include "ui/controls/userpic_button.h"
|
||||||
|
#include "ui/effects/credits_graphics.h"
|
||||||
#include "ui/effects/premium_graphics.h"
|
#include "ui/effects/premium_graphics.h"
|
||||||
#include "ui/effects/premium_top_bar.h" // Ui::Premium::ColorizedSvg.
|
#include "ui/effects/premium_top_bar.h" // Ui::Premium::ColorizedSvg.
|
||||||
#include "ui/image/image_prepare.h"
|
#include "ui/image/image_prepare.h"
|
||||||
#include "ui/layers/generic_box.h"
|
#include "ui/layers/generic_box.h"
|
||||||
|
#include "ui/painter.h"
|
||||||
#include "ui/rect.h"
|
#include "ui/rect.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/vertical_list.h"
|
#include "ui/vertical_list.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
|
#include "ui/widgets/peer_bubble.h"
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
|
#include "styles/style_chat.h"
|
||||||
#include "styles/style_credits.h"
|
#include "styles/style_credits.h"
|
||||||
#include "styles/style_giveaway.h"
|
#include "styles/style_giveaway.h"
|
||||||
|
#include "styles/style_info.h" // inviteLinkSubscribeBoxTerms
|
||||||
#include "styles/style_layers.h"
|
#include "styles/style_layers.h"
|
||||||
#include "styles/style_premium.h"
|
#include "styles/style_premium.h"
|
||||||
#include "styles/style_settings.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(
|
[[nodiscard]] rpl::producer<TextWithEntities> SendCreditsConfirmText(
|
||||||
not_null<Main::Session*> session,
|
not_null<Main::Session*> session,
|
||||||
not_null<Payments::CreditsFormData*> form) {
|
not_null<Payments::CreditsFormData*> form) {
|
||||||
|
@ -150,6 +193,18 @@ struct PaidMediaData {
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto bot = session->data().user(form->botId);
|
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(
|
return tr::lng_credits_box_out_sure(
|
||||||
lt_count,
|
lt_count,
|
||||||
rpl::single(form->invoice.amount) | tr::to_count(),
|
rpl::single(form->invoice.amount) | tr::to_count(),
|
||||||
|
@ -190,6 +245,57 @@ struct PaidMediaData {
|
||||||
st::defaultUserpicButton);
|
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
|
} // namespace
|
||||||
|
|
||||||
void SendCreditsBox(
|
void SendCreditsBox(
|
||||||
|
@ -203,7 +309,8 @@ void SendCreditsBox(
|
||||||
rpl::variable<bool> confirmButtonBusy = false;
|
rpl::variable<bool> confirmButtonBusy = false;
|
||||||
};
|
};
|
||||||
const auto state = box->lifetime().make_state<State>();
|
const auto state = box->lifetime().make_state<State>();
|
||||||
box->setStyle(st::giveawayGiftCodeBox);
|
const auto &stBox = st::giveawayGiftCodeBox;
|
||||||
|
box->setStyle(stBox);
|
||||||
box->setNoContentMargin(true);
|
box->setNoContentMargin(true);
|
||||||
|
|
||||||
const auto session = form->invoice.session;
|
const auto session = form->invoice.session;
|
||||||
|
@ -245,14 +352,36 @@ void SendCreditsBox(
|
||||||
content,
|
content,
|
||||||
SendCreditsThumbnail(content, session, form.get(), photoSize)));
|
SendCreditsThumbnail(content, session, form.get(), photoSize)));
|
||||||
thumb->setAttribute(Qt::WA_TransparentForMouseEvents);
|
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);
|
Ui::AddSkip(content);
|
||||||
box->addRow(object_ptr<Ui::CenterWrap<>>(
|
box->addRow(object_ptr<Ui::CenterWrap<>>(
|
||||||
box,
|
box,
|
||||||
object_ptr<Ui::FlatLabel>(
|
object_ptr<Ui::FlatLabel>(
|
||||||
box,
|
box,
|
||||||
tr::lng_credits_box_out_title(),
|
form->invoice.subscriptionPeriod
|
||||||
|
? rpl::single(form->title)
|
||||||
|
: tr::lng_credits_box_out_title(),
|
||||||
st::settingsPremiumUserTitle)));
|
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);
|
Ui::AddSkip(content);
|
||||||
box->addRow(object_ptr<Ui::CenterWrap<>>(
|
box->addRow(object_ptr<Ui::CenterWrap<>>(
|
||||||
box,
|
box,
|
||||||
|
@ -306,6 +435,9 @@ void SendCreditsBox(
|
||||||
}
|
}
|
||||||
}).send();
|
}).send();
|
||||||
});
|
});
|
||||||
|
if (form->invoice.subscriptionPeriod) {
|
||||||
|
AddTerms(box, button, stBox);
|
||||||
|
}
|
||||||
{
|
{
|
||||||
using namespace Info::Statistics;
|
using namespace Info::Statistics;
|
||||||
const auto loadingAnimation = InfiniteRadialAnimationWidget(
|
const auto loadingAnimation = InfiniteRadialAnimationWidget(
|
||||||
|
@ -317,12 +449,14 @@ void SendCreditsBox(
|
||||||
SetButtonMarkedLabel(
|
SetButtonMarkedLabel(
|
||||||
button,
|
button,
|
||||||
rpl::combine(
|
rpl::combine(
|
||||||
tr::lng_credits_box_out_confirm(
|
(form->invoice.subscriptionPeriod
|
||||||
lt_count,
|
? tr::lng_credits_box_out_subscription_confirm
|
||||||
rpl::single(form->invoice.amount) | tr::to_count(),
|
: tr::lng_credits_box_out_confirm)(
|
||||||
lt_emoji,
|
lt_count,
|
||||||
rpl::single(CreditsEmojiSmall(session)),
|
rpl::single(form->invoice.amount) | tr::to_count(),
|
||||||
Ui::Text::RichLangValue),
|
lt_emoji,
|
||||||
|
rpl::single(CreditsEmojiSmall(session)),
|
||||||
|
Ui::Text::RichLangValue),
|
||||||
state->confirmButtonBusy.value()
|
state->confirmButtonBusy.value()
|
||||||
) | rpl::map([](TextWithEntities &&text, bool busy) {
|
) | rpl::map([](TextWithEntities &&text, bool busy) {
|
||||||
return busy ? TextWithEntities() : std::move(text);
|
return busy ? TextWithEntities() : std::move(text);
|
||||||
|
@ -332,7 +466,7 @@ void SendCreditsBox(
|
||||||
box->getDelegate()->style().button.textFg->c);
|
box->getDelegate()->style().button.textFg->c);
|
||||||
|
|
||||||
const auto buttonWidth = st::boxWidth
|
const auto buttonWidth = st::boxWidth
|
||||||
- rect::m::sum::h(st::giveawayGiftCodeBox.buttonPadding);
|
- rect::m::sum::h(stBox.buttonPadding);
|
||||||
button->widthValue() | rpl::filter([=] {
|
button->widthValue() | rpl::filter([=] {
|
||||||
return (button->widthNoMargins() != buttonWidth);
|
return (button->widthNoMargins() != buttonWidth);
|
||||||
}) | rpl::start_with_next([=] {
|
}) | rpl::start_with_next([=] {
|
||||||
|
@ -341,15 +475,11 @@ void SendCreditsBox(
|
||||||
|
|
||||||
{
|
{
|
||||||
const auto close = Ui::CreateChild<Ui::IconButton>(
|
const auto close = Ui::CreateChild<Ui::IconButton>(
|
||||||
box.get(),
|
content,
|
||||||
st::boxTitleClose);
|
st::boxTitleClose);
|
||||||
close->setClickedCallback([=] {
|
close->setClickedCallback([=] { box->closeBox(); });
|
||||||
box->closeBox();
|
content->widthValue() | rpl::start_with_next([=](int) {
|
||||||
});
|
|
||||||
box->widthValue(
|
|
||||||
) | rpl::start_with_next([=](int width) {
|
|
||||||
close->moveToRight(0, 0);
|
close->moveToRight(0, 0);
|
||||||
close->raise();
|
|
||||||
}, close->lifetime());
|
}, close->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -669,7 +669,8 @@ bool Instance::inCall() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto state = _currentCall->state();
|
const auto state = _currentCall->state();
|
||||||
return (state != Call::State::Busy);
|
return (state != Call::State::Busy)
|
||||||
|
&& (state != Call::State::WaitingUserConfirmation);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Instance::inGroupCall() const {
|
bool Instance::inGroupCall() const {
|
||||||
|
|
|
@ -993,7 +993,12 @@ void Panel::paint(QRect clip) {
|
||||||
|
|
||||||
bool Panel::handleClose() const {
|
bool Panel::handleClose() const {
|
||||||
if (_call) {
|
if (_call) {
|
||||||
window()->hide();
|
if (_call->state() == Call::State::WaitingUserConfirmation
|
||||||
|
|| _call->state() == Call::State::Busy) {
|
||||||
|
_call->hangup();
|
||||||
|
} else {
|
||||||
|
window()->hide();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -1028,6 +1033,7 @@ void Panel::stateChanged(State state) {
|
||||||
_startVideo = base::make_unique_q<Ui::CallButton>(
|
_startVideo = base::make_unique_q<Ui::CallButton>(
|
||||||
widget(),
|
widget(),
|
||||||
st::callStartVideo);
|
st::callStartVideo);
|
||||||
|
_startVideo->show();
|
||||||
_startVideo->setText(tr::lng_call_start_video());
|
_startVideo->setText(tr::lng_call_start_video());
|
||||||
_startVideo->clicks() | rpl::map_to(true) | rpl::start_to_stream(
|
_startVideo->clicks() | rpl::map_to(true) | rpl::start_to_stream(
|
||||||
_startOutgoingRequests,
|
_startOutgoingRequests,
|
||||||
|
|
|
@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D666}"_cs;
|
||||||
constexpr auto AppNameOld = "AyuGram for Windows"_cs;
|
constexpr auto AppNameOld = "AyuGram for Windows"_cs;
|
||||||
constexpr auto AppName = "AyuGram Desktop"_cs;
|
constexpr auto AppName = "AyuGram Desktop"_cs;
|
||||||
constexpr auto AppFile = "AyuGram"_cs;
|
constexpr auto AppFile = "AyuGram"_cs;
|
||||||
constexpr auto AppVersion = 5008002;
|
constexpr auto AppVersion = 5008003;
|
||||||
constexpr auto AppVersionStr = "5.8.2";
|
constexpr auto AppVersionStr = "5.8.3";
|
||||||
constexpr auto AppBetaVersion = false;
|
constexpr auto AppBetaVersion = false;
|
||||||
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
||||||
|
|
|
@ -1980,6 +1980,19 @@ rpl::producer<> Session::pinnedDialogsOrderUpdated() const {
|
||||||
return _pinnedDialogsOrderUpdated.events();
|
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) {
|
void Session::registerHeavyViewPart(not_null<ViewElement*> view) {
|
||||||
_heavyViewParts.emplace(view);
|
_heavyViewParts.emplace(view);
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,7 @@ class Chatbots;
|
||||||
class BusinessInfo;
|
class BusinessInfo;
|
||||||
struct ReactionId;
|
struct ReactionId;
|
||||||
struct UnavailableReason;
|
struct UnavailableReason;
|
||||||
|
struct CreditsStatusSlice;
|
||||||
|
|
||||||
struct RepliesReadTillUpdate {
|
struct RepliesReadTillUpdate {
|
||||||
FullMsgId id;
|
FullMsgId id;
|
||||||
|
@ -337,6 +338,11 @@ public:
|
||||||
void notifyPinnedDialogsOrderUpdated();
|
void notifyPinnedDialogsOrderUpdated();
|
||||||
[[nodiscard]] rpl::producer<> pinnedDialogsOrderUpdated() const;
|
[[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(
|
void registerRestricted(
|
||||||
not_null<const HistoryItem*> item,
|
not_null<const HistoryItem*> item,
|
||||||
const QString &reason);
|
const QString &reason);
|
||||||
|
@ -1095,6 +1101,8 @@ private:
|
||||||
|
|
||||||
MessageIdsList _mimeForwardIds;
|
MessageIdsList _mimeForwardIds;
|
||||||
|
|
||||||
|
std::weak_ptr<CreditsSubsRebuilder> _creditsSubsRebuilder;
|
||||||
|
|
||||||
using CredentialsWithGeneration = std::pair<
|
using CredentialsWithGeneration = std::pair<
|
||||||
const Passport::SavedCredentials,
|
const Passport::SavedCredentials,
|
||||||
int>;
|
int>;
|
||||||
|
|
|
@ -18,6 +18,7 @@ struct PeerSubscription final {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using PhotoId = uint64;
|
||||||
struct SubscriptionEntry final {
|
struct SubscriptionEntry final {
|
||||||
explicit operator bool() const {
|
explicit operator bool() const {
|
||||||
return !id.isEmpty();
|
return !id.isEmpty();
|
||||||
|
@ -25,10 +26,14 @@ struct SubscriptionEntry final {
|
||||||
|
|
||||||
QString id;
|
QString id;
|
||||||
QString inviteHash;
|
QString inviteHash;
|
||||||
|
QString title;
|
||||||
|
QString slug;
|
||||||
QDateTime until;
|
QDateTime until;
|
||||||
PeerSubscription subscription;
|
PeerSubscription subscription;
|
||||||
uint64 barePeerId = 0;
|
uint64 barePeerId = 0;
|
||||||
|
PhotoId photoId = PhotoId(0);
|
||||||
bool cancelled = false;
|
bool cancelled = false;
|
||||||
|
bool cancelledByBot = false;
|
||||||
bool expired = false;
|
bool expired = false;
|
||||||
bool canRefulfill = false;
|
bool canRefulfill = false;
|
||||||
};
|
};
|
||||||
|
|
|
@ -295,7 +295,7 @@ auto Entry::unreadStateChangeNotifier(bool required) {
|
||||||
_flags |= Flag::InUnreadChangeBlock;
|
_flags |= Flag::InUnreadChangeBlock;
|
||||||
const auto notify = required && inChatList();
|
const auto notify = required && inChatList();
|
||||||
const auto wasState = notify ? chatListUnreadState() : UnreadState();
|
const auto wasState = notify ? chatListUnreadState() : UnreadState();
|
||||||
return gsl::finally([=] {
|
return gsl::finally([=, this] {
|
||||||
_flags &= ~Flag::InUnreadChangeBlock;
|
_flags &= ~Flag::InUnreadChangeBlock;
|
||||||
if (notify) {
|
if (notify) {
|
||||||
Assert(inChatList());
|
Assert(inChatList());
|
||||||
|
|
|
@ -130,19 +130,6 @@ int BinarySearchBlocksOrItems(const T &list, int edge) {
|
||||||
return start;
|
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
|
} // namespace
|
||||||
|
|
||||||
// flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html
|
// 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
|
const auto replyToItemId = (selected.item
|
||||||
? selected.item
|
? selected.item
|
||||||
: still)->fullId();
|
: still)->fullId();
|
||||||
if (canSendReply) {
|
_widget->replyToMessage({
|
||||||
_widget->replyToMessage({
|
.messageId = replyToItemId,
|
||||||
.messageId = replyToItemId,
|
.quote = selected.text,
|
||||||
.quote = selected.text,
|
.quoteOffset = selected.offset,
|
||||||
.quoteOffset = selected.offset,
|
});
|
||||||
});
|
if (!selected.text.empty()) {
|
||||||
if (!selected.text.empty()) {
|
_widget->clearSelected();
|
||||||
_widget->clearSelected();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
HistoryView::Controls::ShowReplyToChatBox(show, {
|
|
||||||
.messageId = replyToItemId,
|
|
||||||
.quote = selected.text,
|
|
||||||
.quoteOffset = selected.offset,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return false;
|
return false;
|
||||||
|
@ -2644,26 +2623,13 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
const auto quoteOffset = selected.offset;
|
const auto quoteOffset = selected.offset;
|
||||||
text.replace('&', u"&&"_q);
|
text.replace('&', u"&&"_q);
|
||||||
_menu->addAction(text, [=] {
|
_menu->addAction(text, [=] {
|
||||||
const auto still = session->data().message(itemId);
|
_widget->replyToMessage({
|
||||||
const auto forceAnotherChat = base::IsCtrlPressed()
|
.messageId = itemId,
|
||||||
&& still
|
.quote = quote,
|
||||||
&& still->allowsForward();
|
.quoteOffset = quoteOffset,
|
||||||
if (canSendReply && !forceAnotherChat) {
|
});
|
||||||
_widget->replyToMessage({
|
if (!quote.empty()) {
|
||||||
.messageId = itemId,
|
_widget->clearSelected();
|
||||||
.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,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, &st::menuIconReply);
|
}, &st::menuIconReply);
|
||||||
}
|
}
|
||||||
|
@ -4847,3 +4813,16 @@ auto HistoryInner::DelegateMixin()
|
||||||
-> std::unique_ptr<HistoryMainElementDelegateMixin> {
|
-> std::unique_ptr<HistoryMainElementDelegateMixin> {
|
||||||
return std::make_unique<HistoryMainElementDelegate>();
|
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;
|
bool _wasForceClickPreview = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] bool CanSendReply(not_null<const HistoryItem*> item);
|
||||||
|
|
|
@ -4701,6 +4701,21 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
|
||||||
return preparePaymentSentText();
|
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 prepareScreenshotTaken = [this](const MTPDmessageActionScreenshotTaken &) {
|
||||||
auto result = PreparedServiceText();
|
auto result = PreparedServiceText();
|
||||||
if (out()) {
|
if (out()) {
|
||||||
|
@ -5504,7 +5519,7 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
|
||||||
prepareSecureValuesSent,
|
prepareSecureValuesSent,
|
||||||
prepareContactSignUp,
|
prepareContactSignUp,
|
||||||
prepareProximityReached,
|
prepareProximityReached,
|
||||||
PrepareErrorText<MTPDmessageActionPaymentSentMe>,
|
preparePaymentSentMe,
|
||||||
PrepareErrorText<MTPDmessageActionSecureValuesSentMe>,
|
PrepareErrorText<MTPDmessageActionSecureValuesSentMe>,
|
||||||
prepareGroupCall,
|
prepareGroupCall,
|
||||||
prepareInviteToGroupCall,
|
prepareInviteToGroupCall,
|
||||||
|
|
|
@ -2028,24 +2028,12 @@ bool HistoryWidget::notify_switchInlineBotButtonReceived(
|
||||||
UserData *samePeerBot,
|
UserData *samePeerBot,
|
||||||
MsgId samePeerReplyTo) {
|
MsgId samePeerReplyTo) {
|
||||||
if (samePeerBot) {
|
if (samePeerBot) {
|
||||||
if (_history) {
|
const auto to = controller()->currentDialogsEntryState();
|
||||||
const auto textWithTags = TextWithTags{
|
if (!to.key.owningHistory()) {
|
||||||
'@' + samePeerBot->username() + ' ' + query,
|
return false;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
controller()->switchInlineQuery(to, samePeerBot, query);
|
||||||
|
return true;
|
||||||
} else if (const auto bot = _peer ? _peer->asUser() : nullptr) {
|
} else if (const auto bot = _peer ? _peer->asUser() : nullptr) {
|
||||||
const auto to = bot->isBot()
|
const auto to = bot->isBot()
|
||||||
? bot->botInfo->inlineReturnTo
|
? bot->botInfo->inlineReturnTo
|
||||||
|
@ -2273,7 +2261,7 @@ void HistoryWidget::showHistory(
|
||||||
_showAtMsgHighlightPart = {};
|
_showAtMsgHighlightPart = {};
|
||||||
_showAtMsgHighlightPartOffsetHint = 0;
|
_showAtMsgHighlightPartOffsetHint = 0;
|
||||||
|
|
||||||
const auto wasDialogsEntryState = computeDialogsEntryState();
|
const auto wasState = controller()->currentDialogsEntryState();
|
||||||
const auto startBot = (showAtMsgId == ShowAndStartBotMsgId);
|
const auto startBot = (showAtMsgId == ShowAndStartBotMsgId);
|
||||||
if (startBot) {
|
if (startBot) {
|
||||||
showAtMsgId = ShowAtTheEndMsgId;
|
showAtMsgId = ShowAtTheEndMsgId;
|
||||||
|
@ -2378,8 +2366,8 @@ void HistoryWidget::showHistory(
|
||||||
if (const auto user = _peer->asUser()) {
|
if (const auto user = _peer->asUser()) {
|
||||||
if (const auto &info = user->botInfo) {
|
if (const auto &info = user->botInfo) {
|
||||||
if (startBot) {
|
if (startBot) {
|
||||||
if (wasDialogsEntryState.key) {
|
if (wasState.key) {
|
||||||
info->inlineReturnTo = wasDialogsEntryState;
|
info->inlineReturnTo = wasState;
|
||||||
}
|
}
|
||||||
sendBotStartCommand();
|
sendBotStartCommand();
|
||||||
_history->clearLocalDraft({});
|
_history->clearLocalDraft({});
|
||||||
|
@ -2614,8 +2602,8 @@ void HistoryWidget::showHistory(
|
||||||
if (const auto user = _peer->asUser()) {
|
if (const auto user = _peer->asUser()) {
|
||||||
if (const auto &info = user->botInfo) {
|
if (const auto &info = user->botInfo) {
|
||||||
if (startBot) {
|
if (startBot) {
|
||||||
if (wasDialogsEntryState.key) {
|
if (wasState.key) {
|
||||||
info->inlineReturnTo = wasDialogsEntryState;
|
info->inlineReturnTo = wasState;
|
||||||
}
|
}
|
||||||
sendBotStartCommand();
|
sendBotStartCommand();
|
||||||
}
|
}
|
||||||
|
@ -8060,7 +8048,15 @@ void HistoryWidget::clearFieldText(
|
||||||
|
|
||||||
void HistoryWidget::replyToMessage(FullReplyTo id) {
|
void HistoryWidget::replyToMessage(FullReplyTo id) {
|
||||||
if (const auto item = session().data().message(id.messageId)) {
|
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) {
|
void ListWidget::elementReplyTo(const FullReplyTo &to) {
|
||||||
replyToMessageRequestNotify(to);
|
replyToMessageRequestNotify(to, base::IsCtrlPressed());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::elementStartInteraction(not_null<const Element*> view) {
|
void ListWidget::elementStartInteraction(not_null<const Element*> view) {
|
||||||
|
|
|
@ -1659,7 +1659,8 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
||||||
(right
|
(right
|
||||||
- (st::msgSelectionOffset * progress - st.size) / 2
|
- (st::msgSelectionOffset * progress - st.size) / 2
|
||||||
- st::msgPadding.right() / 2
|
- st::msgPadding.right() / 2
|
||||||
- st.size),
|
- st.size
|
||||||
|
- st::historyScroll.deltax),
|
||||||
rect::bottom(g) - st.size - st::msgSelectionBottomSkip);
|
rect::bottom(g) - st.size - st::msgSelectionBottomSkip);
|
||||||
{
|
{
|
||||||
p.setPen(QPen(st.border, st.width));
|
p.setPen(QPen(st.border, st.width));
|
||||||
|
|
|
@ -255,7 +255,6 @@ DocumentData *Sticker::document() {
|
||||||
|
|
||||||
void Sticker::stickerClearLoopPlayed() {
|
void Sticker::stickerClearLoopPlayed() {
|
||||||
_oncePlayed = false;
|
_oncePlayed = false;
|
||||||
_premiumEffectPlayed = false;
|
|
||||||
_premiumEffectSkipped = false;
|
_premiumEffectSkipped = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -634,10 +634,13 @@ QSize WebPage::countOptimalSize() {
|
||||||
_durationWidth = st::msgDateFont->width(_duration);
|
_durationWidth = st::msgDateFont->width(_duration);
|
||||||
}
|
}
|
||||||
if (!_openButton.isEmpty()) {
|
if (!_openButton.isEmpty()) {
|
||||||
accumulate_max(
|
const auto w = rect::m::sum::h(st::historyPageButtonPadding)
|
||||||
maxWidth,
|
+ _openButton.maxWidth();
|
||||||
rect::m::sum::h(st::historyPageButtonPadding)
|
if (sponsored) {
|
||||||
+ _openButton.maxWidth());
|
accumulate_max(maxWidth, w);
|
||||||
|
} else {
|
||||||
|
maxWidth += w;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
maxWidth += rect::m::sum::h(padding);
|
maxWidth += rect::m::sum::h(padding);
|
||||||
minHeight += rect::m::sum::v(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/widgets/labels.h"
|
||||||
#include "ui/wrap/slide_wrap.h"
|
#include "ui/wrap/slide_wrap.h"
|
||||||
#include "ui/ui_utility.h"
|
#include "ui/ui_utility.h"
|
||||||
|
#include "styles/style_color_indices.h"
|
||||||
#include "styles/style_credits.h"
|
#include "styles/style_credits.h"
|
||||||
#include "styles/style_giveaway.h"
|
#include "styles/style_giveaway.h"
|
||||||
#include "styles/style_info.h"
|
#include "styles/style_info.h"
|
||||||
|
@ -59,7 +60,6 @@ namespace {
|
||||||
|
|
||||||
constexpr auto kDoneTooltipDuration = 5 * crl::time(1000);
|
constexpr auto kDoneTooltipDuration = 5 * crl::time(1000);
|
||||||
constexpr auto kAdditionalPrizeLengthMax = 128;
|
constexpr auto kAdditionalPrizeLengthMax = 128;
|
||||||
constexpr auto kColorIndexCredits = int(1);
|
|
||||||
|
|
||||||
[[nodiscard]] QDateTime ThreeDaysAfterToday() {
|
[[nodiscard]] QDateTime ThreeDaysAfterToday() {
|
||||||
auto dateNow = QDateTime::currentDateTime();
|
auto dateNow = QDateTime::currentDateTime();
|
||||||
|
@ -370,7 +370,7 @@ void CreateGiveawayBox(
|
||||||
prepaid->credits
|
prepaid->credits
|
||||||
? GiveawayType::PrepaidCredits
|
? GiveawayType::PrepaidCredits
|
||||||
: GiveawayType::Prepaid,
|
: GiveawayType::Prepaid,
|
||||||
prepaid->credits ? kColorIndexCredits : prepaid->id,
|
prepaid->credits ? st::colorIndexOrange : prepaid->id,
|
||||||
tr::lng_boosts_prepaid_giveaway_single(),
|
tr::lng_boosts_prepaid_giveaway_single(),
|
||||||
prepaid->credits
|
prepaid->credits
|
||||||
? tr::lng_boosts_prepaid_giveaway_credits_status(
|
? tr::lng_boosts_prepaid_giveaway_credits_status(
|
||||||
|
@ -508,7 +508,7 @@ void CreateGiveawayBox(
|
||||||
object_ptr<Giveaway::GiveawayTypeRow>(
|
object_ptr<Giveaway::GiveawayTypeRow>(
|
||||||
box,
|
box,
|
||||||
GiveawayType::Credits,
|
GiveawayType::Credits,
|
||||||
kColorIndexCredits,
|
st::colorIndexOrange,
|
||||||
tr::lng_credits_summary_title(),
|
tr::lng_credits_summary_title(),
|
||||||
tr::lng_giveaway_create_subtitle(),
|
tr::lng_giveaway_create_subtitle(),
|
||||||
std::move(badge)));
|
std::move(badge)));
|
||||||
|
|
|
@ -16,14 +16,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/checkbox.h"
|
#include "ui/widgets/checkbox.h"
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
|
#include "styles/style_color_indices.h"
|
||||||
#include "styles/style_giveaway.h"
|
#include "styles/style_giveaway.h"
|
||||||
#include "styles/style_statistics.h"
|
#include "styles/style_statistics.h"
|
||||||
|
|
||||||
namespace Giveaway {
|
namespace Giveaway {
|
||||||
|
|
||||||
constexpr auto kColorIndexSpecific = int(4);
|
|
||||||
constexpr auto kColorIndexRandom = int(2);
|
|
||||||
|
|
||||||
GiveawayTypeRow::GiveawayTypeRow(
|
GiveawayTypeRow::GiveawayTypeRow(
|
||||||
not_null<Ui::RpWidget*> parent,
|
not_null<Ui::RpWidget*> parent,
|
||||||
Type type,
|
Type type,
|
||||||
|
@ -32,7 +30,7 @@ GiveawayTypeRow::GiveawayTypeRow(
|
||||||
: GiveawayTypeRow(
|
: GiveawayTypeRow(
|
||||||
parent,
|
parent,
|
||||||
type,
|
type,
|
||||||
(type == Type::SpecificUsers) ? kColorIndexSpecific : kColorIndexRandom,
|
(type == Type::SpecificUsers) ? st::colorIndexBlue : st::colorIndexGreen,
|
||||||
(type == Type::SpecificUsers)
|
(type == Type::SpecificUsers)
|
||||||
? tr::lng_giveaway_award_option()
|
? tr::lng_giveaway_award_option()
|
||||||
: (type == Type::Random)
|
: (type == Type::Random)
|
||||||
|
|
|
@ -39,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/slider_natural_width.h"
|
#include "ui/widgets/slider_natural_width.h"
|
||||||
#include "ui/wrap/slide_wrap.h"
|
#include "ui/wrap/slide_wrap.h"
|
||||||
#include "ui/ui_utility.h"
|
#include "ui/ui_utility.h"
|
||||||
|
#include "styles/style_color_indices.h"
|
||||||
#include "styles/style_dialogs.h" // dialogsSearchTabs
|
#include "styles/style_dialogs.h" // dialogsSearchTabs
|
||||||
#include "styles/style_giveaway.h"
|
#include "styles/style_giveaway.h"
|
||||||
#include "styles/style_info.h"
|
#include "styles/style_info.h"
|
||||||
|
@ -339,7 +340,6 @@ void InnerWidget::fill() {
|
||||||
Ui::AddSkip(inner);
|
Ui::AddSkip(inner);
|
||||||
|
|
||||||
if (!status.prepaidGiveaway.empty()) {
|
if (!status.prepaidGiveaway.empty()) {
|
||||||
constexpr auto kColorIndexCredits = int(1);
|
|
||||||
const auto multiplier = Api::PremiumGiftCodeOptions(_peer)
|
const auto multiplier = Api::PremiumGiftCodeOptions(_peer)
|
||||||
.giveawayBoostsPerPremium();
|
.giveawayBoostsPerPremium();
|
||||||
Ui::AddSkip(inner);
|
Ui::AddSkip(inner);
|
||||||
|
@ -352,7 +352,7 @@ void InnerWidget::fill() {
|
||||||
g.credits
|
g.credits
|
||||||
? GiveawayTypeRow::Type::PrepaidCredits
|
? GiveawayTypeRow::Type::PrepaidCredits
|
||||||
: GiveawayTypeRow::Type::Prepaid,
|
: GiveawayTypeRow::Type::Prepaid,
|
||||||
g.credits ? kColorIndexCredits : g.id,
|
g.credits ? st::colorIndexOrange : g.id,
|
||||||
g.credits
|
g.credits
|
||||||
? tr::lng_boosts_prepaid_giveaway_single()
|
? tr::lng_boosts_prepaid_giveaway_single()
|
||||||
: tr::lng_boosts_prepaid_giveaway_quantity(
|
: 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/vertical_list.h"
|
||||||
#include "ui/widgets/fields/input_field.h"
|
#include "ui/widgets/fields/input_field.h"
|
||||||
#include "ui/widgets/label_with_custom_emoji.h"
|
#include "ui/widgets/label_with_custom_emoji.h"
|
||||||
|
#include "ui/widgets/peer_bubble.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "ui/widgets/slider_natural_width.h"
|
#include "ui/widgets/slider_natural_width.h"
|
||||||
#include "ui/wrap/slide_wrap.h"
|
#include "ui/wrap/slide_wrap.h"
|
||||||
|
@ -1199,58 +1200,10 @@ void InnerWidget::fill() {
|
||||||
AddRecipient(box, recipient);
|
AddRecipient(box, recipient);
|
||||||
}
|
}
|
||||||
if (isIn) {
|
if (isIn) {
|
||||||
const auto peerBubble = box->addRow(
|
box->addRow(
|
||||||
object_ptr<Ui::CenterWrap<>>(
|
object_ptr<Ui::CenterWrap<>>(
|
||||||
box,
|
box,
|
||||||
object_ptr<Ui::RpWidget>(box)))->entity();
|
Ui::CreatePeerBubble(box, peer)));
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
const auto closeBox = [=] { box->closeBox(); };
|
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_credits.h"
|
||||||
#include "api/api_statistics.h"
|
#include "api/api_statistics.h"
|
||||||
#include "boxes/peer_list_controllers.h"
|
#include "boxes/peer_list_controllers.h"
|
||||||
|
#include "boxes/peer_list_widgets.h"
|
||||||
#include "chat_helpers/stickers_gift_box_pack.h"
|
#include "chat_helpers/stickers_gift_box_pack.h"
|
||||||
#include "core/ui_integration.h" // Core::MarkedTextContext.
|
#include "core/ui_integration.h" // Core::MarkedTextContext.
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
|
@ -24,6 +25,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "main/session/session_show.h"
|
#include "main/session/session_show.h"
|
||||||
#include "settings/settings_credits_graphics.h" // PaintSubscriptionRightLabelCallback
|
#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/credits_graphics.h"
|
||||||
#include "ui/effects/outline_segments.h" // Ui::UnreadStoryOutlineGradient.
|
#include "ui/effects/outline_segments.h" // Ui::UnreadStoryOutlineGradient.
|
||||||
#include "ui/effects/toggle_arrow.h"
|
#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/slide_wrap.h"
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
|
#include "styles/style_color_indices.h"
|
||||||
#include "styles/style_credits.h"
|
#include "styles/style_credits.h"
|
||||||
#include "styles/style_dialogs.h" // dialogsStoriesFull.
|
#include "styles/style_dialogs.h" // dialogsStoriesFull.
|
||||||
#include "styles/style_layers.h" // boxRowPadding.
|
#include "styles/style_layers.h" // boxRowPadding.
|
||||||
|
@ -47,9 +51,6 @@ namespace Info::Statistics {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using BoostCallback = Fn<void(const Data::Boost &)>;
|
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(
|
[[nodiscard]] PeerListRowId UniqueRowIdFromEntry(
|
||||||
const Data::CreditsHistoryEntry &entry) {
|
const Data::CreditsHistoryEntry &entry) {
|
||||||
|
@ -70,6 +71,17 @@ void AddSubtitle(
|
||||||
{ 0, -subtitlePadding.top(), 0, -subtitlePadding.bottom() });
|
{ 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(
|
[[nodiscard]] QString FormatText(
|
||||||
int value1, tr::phrase<lngtag_count> phrase1,
|
int value1, tr::phrase<lngtag_count> phrase1,
|
||||||
int value2, tr::phrase<lngtag_count> phrase2,
|
int value2, tr::phrase<lngtag_count> phrase2,
|
||||||
|
@ -475,10 +487,10 @@ BoostRow::BoostRow(const Data::Boost &boost)
|
||||||
, _boost(boost)
|
, _boost(boost)
|
||||||
, _userpic(
|
, _userpic(
|
||||||
Ui::EmptyUserpic::UserpicColor(boost.credits
|
Ui::EmptyUserpic::UserpicColor(boost.credits
|
||||||
? kColorIndexCredits
|
? st::colorIndexOrange
|
||||||
: boost.isUnclaimed
|
: boost.isUnclaimed
|
||||||
? kColorIndexUnclaimed
|
? st::colorIndexSea
|
||||||
: kColorIndexPending),
|
: st::colorIndexBlue),
|
||||||
QString()) {
|
QString()) {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
@ -763,6 +775,18 @@ public:
|
||||||
bool selected,
|
bool selected,
|
||||||
bool actionSelected) override;
|
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:
|
private:
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
|
@ -776,8 +800,12 @@ private:
|
||||||
QString _title;
|
QString _title;
|
||||||
QString _name;
|
QString _name;
|
||||||
|
|
||||||
|
Ui::Text::String _description;
|
||||||
Ui::Text::String _rightText;
|
Ui::Text::String _rightText;
|
||||||
|
|
||||||
|
std::shared_ptr<Ui::DynamicImage> _descriptionThumbnail;
|
||||||
|
QImage _descriptionThumbnailCache;
|
||||||
|
|
||||||
base::has_weak_ptr _guard;
|
base::has_weak_ptr _guard;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -822,33 +850,37 @@ void CreditsRow::init() {
|
||||||
const auto name = !isSpecial
|
const auto name = !isSpecial
|
||||||
? PeerListRow::generateName()
|
? PeerListRow::generateName()
|
||||||
: Ui::GenerateEntryName(_entry).text;
|
: Ui::GenerateEntryName(_entry).text;
|
||||||
_name = (_entry.reaction || _entry.stargift || _entry.bareGiveawayMsgId)
|
_name = _entry.title.isEmpty()
|
||||||
? Ui::GenerateEntryName(_entry).text
|
? name
|
||||||
: _entry.title.isEmpty()
|
: (!_entry.subscriptionUntil.isNull() && !isSpecial)
|
||||||
? name
|
? name
|
||||||
: _entry.title;
|
: _entry.title;
|
||||||
const auto joiner = QString(QChar(' ')) + QChar(8212) + QChar(' ');
|
|
||||||
setSkipPeerBadge(true);
|
setSkipPeerBadge(true);
|
||||||
PeerListRow::setCustomStatus(
|
const auto description = _entry.floodSkip
|
||||||
langDateTime(_entry.date)
|
? tr::lng_credits_box_history_entry_floodskip_about(
|
||||||
+ (_entry.floodSkip
|
tr::now,
|
||||||
? (joiner + tr::lng_credits_box_history_entry_floodskip_about(
|
lt_count_decimal,
|
||||||
tr::now,
|
_entry.floodSkip)
|
||||||
lt_count_decimal,
|
: (!_entry.subscriptionUntil.isNull() && !_entry.title.isEmpty())
|
||||||
_entry.floodSkip))
|
? _entry.title
|
||||||
: _entry.refunded
|
: _entry.refunded
|
||||||
? (joiner + tr::lng_channel_earn_history_return(tr::now))
|
? tr::lng_channel_earn_history_return(tr::now)
|
||||||
: _entry.pending
|
: _entry.pending
|
||||||
? (joiner + tr::lng_channel_earn_history_pending(tr::now))
|
? tr::lng_channel_earn_history_pending(tr::now)
|
||||||
: _entry.failed
|
: _entry.failed
|
||||||
? (joiner + tr::lng_channel_earn_history_failed(tr::now))
|
? tr::lng_channel_earn_history_failed(tr::now)
|
||||||
: !_entry.subscriptionUntil.isNull()
|
: !_entry.subscriptionUntil.isNull()
|
||||||
? (joiner
|
? tr::lng_credits_box_history_entry_subscription(tr::now)
|
||||||
+ tr::lng_credits_box_history_entry_subscription(tr::now))
|
: (_entry.peerType
|
||||||
: QString())
|
== Data::CreditsHistoryEntry::PeerType::PremiumBot)
|
||||||
+ ((_entry.gift && isSpecial)
|
? tr::lng_credits_box_history_entry_via_premium_bot(tr::now)
|
||||||
? (joiner + tr::lng_credits_box_history_entry_anonymous(tr::now))
|
: (_entry.gift && isSpecial)
|
||||||
: ((_name == name) ? QString() : (joiner + name))));
|
? 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) {
|
if (_subscription) {
|
||||||
PeerListRow::setCustomStatus((_subscription.expired
|
PeerListRow::setCustomStatus((_subscription.expired
|
||||||
? tr::lng_credits_subscription_status_none
|
? tr::lng_credits_subscription_status_none
|
||||||
|
@ -858,6 +890,24 @@ void CreditsRow::init() {
|
||||||
tr::now,
|
tr::now,
|
||||||
lt_date,
|
lt_date,
|
||||||
langDayOfMonthFull(_subscription.until.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();
|
auto &manager = _context.session->data().customEmojiManager();
|
||||||
if (_entry) {
|
if (_entry) {
|
||||||
|
@ -894,23 +944,40 @@ const Data::SubscriptionEntry &CreditsRow::subscription() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CreditsRow::generateName() {
|
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) {
|
PaintRoundImageCallback CreditsRow::generatePaintUserpicCallback(bool force) {
|
||||||
return _paintUserpicCallback;
|
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 {
|
QSize CreditsRow::rightActionSize() const {
|
||||||
if (_rightLabel) {
|
if (_rightLabel) {
|
||||||
return _rightLabel->size;
|
return _rightLabel->size;
|
||||||
} else if (_subscription.cancelled || _subscription.expired) {
|
} else if (const auto t = RightActionText(_subscription); !t.isEmpty()) {
|
||||||
const auto text = _subscription.cancelled
|
const auto lines = t.split('\n');
|
||||||
? tr::lng_credits_subscription_status_off_right(tr::now)
|
auto maxWidth = 0;
|
||||||
: tr::lng_credits_subscription_status_none_right(tr::now);
|
for (const auto &line : lines) {
|
||||||
return QSize(
|
const auto width = st::contactsStatusFont->width(line);
|
||||||
st::contactsStatusFont->width(text) + st::boxRowPadding.right(),
|
if (width > maxWidth) {
|
||||||
_rowHeight);
|
maxWidth = width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QSize(maxWidth + st::boxRowPadding.right(), _rowHeight);
|
||||||
} else if (_subscription || _entry) {
|
} else if (_subscription || _entry) {
|
||||||
return QSize(
|
return QSize(
|
||||||
_rightText.maxWidth() + st::boxRowPadding.right() / 2,
|
_rightText.maxWidth() + st::boxRowPadding.right() / 2,
|
||||||
|
@ -940,18 +1007,31 @@ void CreditsRow::rightActionPaint(
|
||||||
const auto rightSkip = st::boxRowPadding.right();
|
const auto rightSkip = st::boxRowPadding.right();
|
||||||
if (_rightLabel) {
|
if (_rightLabel) {
|
||||||
return _rightLabel->draw(p, x, y, _rowHeight);
|
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;
|
const auto &statusFont = st::contactsStatusFont;
|
||||||
y += _rowHeight / 2;
|
y += _rowHeight / 2;
|
||||||
p.setFont(statusFont);
|
p.setFont(statusFont);
|
||||||
p.setPen(st::attentionButtonFg);
|
p.setPen(st::attentionButtonFg);
|
||||||
p.drawTextRight(
|
|
||||||
rightSkip,
|
const auto lines = t.split('\n');
|
||||||
y - statusFont->height / 2,
|
if (lines.size() > 1) {
|
||||||
outerWidth,
|
const auto rect = QRect(x, 0, outerWidth - x, _rowHeight);
|
||||||
_subscription.expired
|
const auto lineHeight = statusFont->height;
|
||||||
? tr::lng_credits_subscription_status_none_right(tr::now)
|
const auto totalHeight = lines.size() * lineHeight;
|
||||||
: tr::lng_credits_subscription_status_off_right(tr::now));
|
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;
|
return;
|
||||||
}
|
}
|
||||||
y += _rowHeight / 2;
|
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 {
|
class CreditsController final : public PeerListController {
|
||||||
public:
|
public:
|
||||||
explicit CreditsController(CreditsDescriptor d);
|
explicit CreditsController(CreditsDescriptor d);
|
||||||
|
@ -1010,7 +1127,7 @@ CreditsController::CreditsController(CreditsDescriptor d)
|
||||||
.session = _session,
|
.session = _session,
|
||||||
.customEmojiRepaint = [] {},
|
.customEmojiRepaint = [] {},
|
||||||
}) {
|
}) {
|
||||||
PeerListController::setStyleOverrides(&st::boostsListBox);
|
PeerListController::setStyleOverrides(&st::creditsHistoryEntriesList);
|
||||||
}
|
}
|
||||||
|
|
||||||
Main::Session &CreditsController::session() const {
|
Main::Session &CreditsController::session() const {
|
||||||
|
@ -1052,7 +1169,9 @@ void CreditsController::applySlice(const Data::CreditsStatusSlice &slice) {
|
||||||
.entry = i,
|
.entry = i,
|
||||||
.subscription = s,
|
.subscription = s,
|
||||||
.context = _context,
|
.context = _context,
|
||||||
.rowHeight = computeListSt().item.height,
|
.rowHeight = ((!s || !s.title.isEmpty())
|
||||||
|
? computeListSt().item
|
||||||
|
: st::boostsListBox.item).height,
|
||||||
.updateCallback = [=](not_null<PeerListRow*> row) {
|
.updateCallback = [=](not_null<PeerListRow*> row) {
|
||||||
delegate()->peerListUpdateRow(row);
|
delegate()->peerListUpdateRow(row);
|
||||||
},
|
},
|
||||||
|
@ -1239,28 +1358,36 @@ void AddCreditsHistoryList(
|
||||||
not_null<PeerData*> bot,
|
not_null<PeerData*> bot,
|
||||||
bool in,
|
bool in,
|
||||||
bool out,
|
bool out,
|
||||||
bool subscription) {
|
bool subs) {
|
||||||
struct State final {
|
struct State final {
|
||||||
State(
|
State(CreditsDescriptor d) : controller(std::move(d)) {
|
||||||
CreditsDescriptor d,
|
|
||||||
std::shared_ptr<Main::SessionShow> show)
|
|
||||||
: delegate(std::move(show))
|
|
||||||
, controller(std::move(d)) {
|
|
||||||
}
|
}
|
||||||
PeerListContentDelegateShow delegate;
|
std::optional<PeerListContentDelegateShow> creditsDelegate;
|
||||||
|
std::optional<PeerListWidgetsDelegate> subscriptionDelegate;
|
||||||
CreditsController controller;
|
CreditsController controller;
|
||||||
};
|
};
|
||||||
const auto state = container->lifetime().make_state<State>(
|
const auto state = container->lifetime().make_state<State>(
|
||||||
CreditsDescriptor{ firstSlice, callback, bot, in, out, subscription },
|
CreditsDescriptor{ firstSlice, callback, bot, in, out, subs });
|
||||||
show);
|
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(
|
const auto wrap = container->add(
|
||||||
object_ptr<PeerListContent>(container, &state->controller)));
|
object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
|
||||||
state->controller.setDelegate(&state->delegate);
|
container,
|
||||||
|
CreateShowMoreButton(container, tr::lng_stories_show_more())),
|
||||||
const auto wrap = AddShowMoreButton(
|
subs
|
||||||
container,
|
? QMargins()
|
||||||
tr::lng_stories_show_more());
|
: QMargins(0, -st::settingsButton.padding.top(), 0, 0));
|
||||||
|
|
||||||
const auto showMore = [=] {
|
const auto showMore = [=] {
|
||||||
if (!state->controller.skipRequest()) {
|
if (!state->controller.skipRequest()) {
|
||||||
|
@ -1277,16 +1404,11 @@ void AddCreditsHistoryList(
|
||||||
not_null<Ui::SlideWrap<Ui::SettingsButton>*> AddShowMoreButton(
|
not_null<Ui::SlideWrap<Ui::SettingsButton>*> AddShowMoreButton(
|
||||||
not_null<Ui::VerticalLayout*> container,
|
not_null<Ui::VerticalLayout*> container,
|
||||||
rpl::producer<QString> title) {
|
rpl::producer<QString> title) {
|
||||||
const auto wrap = container->add(
|
return container->add(
|
||||||
object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
|
object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
|
||||||
container,
|
container,
|
||||||
object_ptr<Ui::SettingsButton>(
|
CreateShowMoreButton(container, std::move(title))),
|
||||||
container,
|
|
||||||
std::move(title),
|
|
||||||
st::statisticsShowMoreButton)),
|
|
||||||
{ 0, -st::settingsButton.padding.top(), 0, 0 });
|
{ 0, -st::settingsButton.padding.top(), 0, 0 });
|
||||||
Ui::AddToggleUpDownArrowToMoreButton(wrap->entity());
|
|
||||||
return wrap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Info::Statistics
|
} // namespace Info::Statistics
|
||||||
|
|
|
@ -643,12 +643,13 @@ void ConfirmEmojiStatusBox(
|
||||||
done(true);
|
done(true);
|
||||||
});
|
});
|
||||||
box->addButton(tr::lng_cancel(), [=] {
|
box->addButton(tr::lng_cancel(), [=] {
|
||||||
const auto was = *set;
|
|
||||||
box->closeBox();
|
box->closeBox();
|
||||||
if (!was) {
|
});
|
||||||
|
box->boxClosing() | rpl::start_with_next([=] {
|
||||||
|
if (!*set) {
|
||||||
done(false);
|
done(false);
|
||||||
}
|
}
|
||||||
});
|
}, box->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
class BotAction final : public Ui::Menu::ItemBase {
|
class BotAction final : public Ui::Menu::ItemBase {
|
||||||
|
@ -954,7 +955,12 @@ void WebViewInstance::resolve() {
|
||||||
requestSimple();
|
requestSimple();
|
||||||
});
|
});
|
||||||
}, [&](WebViewSourceLinkApp data) {
|
}, [&](WebViewSourceLinkApp data) {
|
||||||
resolveApp(data.appname, data.token, !_context.maySkipConfirmation);
|
resolveApp(
|
||||||
|
data.appname,
|
||||||
|
data.token,
|
||||||
|
(_context.maySkipConfirmation
|
||||||
|
? ConfirmType::None
|
||||||
|
: ConfirmType::Always));
|
||||||
}, [&](WebViewSourceLinkBotProfile) {
|
}, [&](WebViewSourceLinkBotProfile) {
|
||||||
confirmOpen([=] {
|
confirmOpen([=] {
|
||||||
requestMain();
|
requestMain();
|
||||||
|
@ -1002,14 +1008,14 @@ bool WebViewInstance::openAppFromBotMenuLink() {
|
||||||
if (appname.isEmpty()) {
|
if (appname.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
resolveApp(appname, params.value(u"startapp"_q), true);
|
resolveApp(appname, params.value(u"startapp"_q), ConfirmType::Once);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebViewInstance::resolveApp(
|
void WebViewInstance::resolveApp(
|
||||||
const QString &appname,
|
const QString &appname,
|
||||||
const QString &startparam,
|
const QString &startparam,
|
||||||
bool forceConfirmation) {
|
ConfirmType confirmType) {
|
||||||
const auto already = _session->data().findBotApp(_bot->id, appname);
|
const auto already = _session->data().findBotApp(_bot->id, appname);
|
||||||
_requestId = _session->api().request(MTPmessages_GetBotApp(
|
_requestId = _session->api().request(MTPmessages_GetBotApp(
|
||||||
MTP_inputBotAppShortName(
|
MTP_inputBotAppShortName(
|
||||||
|
@ -1029,8 +1035,11 @@ void WebViewInstance::resolveApp(
|
||||||
close();
|
close();
|
||||||
return;
|
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 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.
|
// Check if this app can be added to main menu.
|
||||||
// On fail it'll still be opened.
|
// On fail it'll still be opened.
|
||||||
|
@ -1043,7 +1052,7 @@ void WebViewInstance::resolveApp(
|
||||||
} else if (confirm) {
|
} else if (confirm) {
|
||||||
confirmAppOpen(writeAccess, [=](bool allowWrite) {
|
confirmAppOpen(writeAccess, [=](bool allowWrite) {
|
||||||
requestApp(allowWrite);
|
requestApp(allowWrite);
|
||||||
});
|
}, forceConfirmation);
|
||||||
} else {
|
} else {
|
||||||
requestApp(false);
|
requestApp(false);
|
||||||
}
|
}
|
||||||
|
@ -1090,10 +1099,18 @@ void WebViewInstance::confirmOpen(Fn<void()> done) {
|
||||||
|
|
||||||
void WebViewInstance::confirmAppOpen(
|
void WebViewInstance::confirmAppOpen(
|
||||||
bool writeAccess,
|
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) {
|
_parentShow->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||||
const auto allowed = std::make_shared<Ui::Checkbox*>();
|
const auto allowed = std::make_shared<Ui::Checkbox*>();
|
||||||
const auto callback = [=](Fn<void()> close) {
|
const auto callback = [=](Fn<void()> close) {
|
||||||
|
_session->local().markBotTrustedOpenWebView(_bot->id);
|
||||||
done((*allowed) && (*allowed)->checked());
|
done((*allowed) && (*allowed)->checked());
|
||||||
close();
|
close();
|
||||||
};
|
};
|
||||||
|
|
|
@ -230,12 +230,20 @@ private:
|
||||||
void requestWithMenuAdd();
|
void requestWithMenuAdd();
|
||||||
void maybeChooseAndRequestButton(PeerTypes supported);
|
void maybeChooseAndRequestButton(PeerTypes supported);
|
||||||
|
|
||||||
|
enum class ConfirmType : uchar {
|
||||||
|
Always,
|
||||||
|
Once,
|
||||||
|
None,
|
||||||
|
};
|
||||||
void resolveApp(
|
void resolveApp(
|
||||||
const QString &appname,
|
const QString &appname,
|
||||||
const QString &startparam,
|
const QString &startparam,
|
||||||
bool forceConfirmation);
|
ConfirmType confirmType);
|
||||||
void confirmOpen(Fn<void()> done);
|
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 {
|
struct ShowArgs {
|
||||||
QString url;
|
QString url;
|
||||||
|
|
|
@ -456,6 +456,8 @@ void Form::requestForm() {
|
||||||
const auto amount = tlPrices.empty()
|
const auto amount = tlPrices.empty()
|
||||||
? 0
|
? 0
|
||||||
: tlPrices.front().data().vamount().v;
|
: tlPrices.front().data().vamount().v;
|
||||||
|
const auto subscriptionPeriod
|
||||||
|
= data.vinvoice().data().vsubscription_period().value_or(0);
|
||||||
if (currency != ::Ui::kCreditsCurrency || !amount) {
|
if (currency != ::Ui::kCreditsCurrency || !amount) {
|
||||||
using Type = Error::Type;
|
using Type = Error::Type;
|
||||||
_updates.fire(Error{ Type::Form, u"Bad Stars Form."_q });
|
_updates.fire(Error{ Type::Form, u"Bad Stars Form."_q });
|
||||||
|
@ -467,6 +469,7 @@ void Form::requestForm() {
|
||||||
.credits = amount,
|
.credits = amount,
|
||||||
.currency = currency,
|
.currency = currency,
|
||||||
.amount = amount,
|
.amount = amount,
|
||||||
|
.subscriptionPeriod = subscriptionPeriod,
|
||||||
};
|
};
|
||||||
const auto formData = CreditsFormData{
|
const auto formData = CreditsFormData{
|
||||||
.id = _id,
|
.id = _id,
|
||||||
|
|
|
@ -170,6 +170,7 @@ struct InvoiceCredits {
|
||||||
uint64 amount = 0;
|
uint64 amount = 0;
|
||||||
bool extended = false;
|
bool extended = false;
|
||||||
PeerId giftPeerId = PeerId(0);
|
PeerId giftPeerId = PeerId(0);
|
||||||
|
int subscriptionPeriod = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InvoiceStarGift {
|
struct InvoiceStarGift {
|
||||||
|
|
|
@ -94,14 +94,14 @@ void ProcessCreditsPayment(
|
||||||
Ui::SendCreditsBox,
|
Ui::SendCreditsBox,
|
||||||
form,
|
form,
|
||||||
[=] {
|
[=] {
|
||||||
*unsuccessful = false;
|
*unsuccessful = false;
|
||||||
if (const auto widget = fireworks.data()) {
|
if (const auto widget = fireworks.data()) {
|
||||||
Ui::StartFireworks(widget);
|
Ui::StartFireworks(widget);
|
||||||
}
|
}
|
||||||
if (const auto onstack = maybeReturnToBot) {
|
if (const auto onstack = maybeReturnToBot) {
|
||||||
onstack(CheckoutResult::Paid);
|
onstack(CheckoutResult::Paid);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
box->boxClosing() | rpl::start_with_next([=] {
|
box->boxClosing() | rpl::start_with_next([=] {
|
||||||
crl::on_main([=] {
|
crl::on_main([=] {
|
||||||
if (*unsuccessful) {
|
if (*unsuccessful) {
|
||||||
|
|
|
@ -191,6 +191,19 @@ void Credits::setupSubscriptions(not_null<Ui::VerticalLayout*> container) {
|
||||||
fill(std::move(d));
|
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) {
|
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 "main/main_session.h"
|
||||||
#include "payments/payments_checkout_process.h"
|
#include "payments/payments_checkout_process.h"
|
||||||
#include "payments/payments_form.h"
|
#include "payments/payments_form.h"
|
||||||
|
#include "payments/payments_non_panel_process.h"
|
||||||
#include "settings/settings_common_session.h"
|
#include "settings/settings_common_session.h"
|
||||||
#include "settings/settings_credits.h"
|
#include "settings/settings_credits.h"
|
||||||
#include "statistics/widgets/chart_header_widget.h"
|
#include "statistics/widgets/chart_header_widget.h"
|
||||||
|
@ -346,6 +347,37 @@ void AddViewMediaHandler(
|
||||||
}, thumb->lifetime());
|
}, 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
|
} // namespace
|
||||||
|
|
||||||
SubscriptionRightLabel PaintSubscriptionRightLabelCallback(
|
SubscriptionRightLabel PaintSubscriptionRightLabelCallback(
|
||||||
|
@ -664,31 +696,12 @@ void BoostCreditsBox(
|
||||||
{
|
{
|
||||||
const auto &stUser = st::premiumGiftsUserpicButton;
|
const auto &stUser = st::premiumGiftsUserpicButton;
|
||||||
const auto widget = content->add(object_ptr<Ui::RpWidget>(content));
|
const auto widget = content->add(object_ptr<Ui::RpWidget>(content));
|
||||||
using ColoredMiniStars = Ui::Premium::ColoredMiniStars;
|
AddMiniStars(content, widget, stUser, st::boxWidth, 1.3);
|
||||||
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);
|
|
||||||
const auto svg = std::make_shared<QSvgRenderer>(
|
const auto svg = std::make_shared<QSvgRenderer>(
|
||||||
Ui::Premium::ColorizedSvg(
|
Ui::Premium::ColorizedSvg(
|
||||||
Ui::Premium::CreditsIconGradientStops()));
|
Ui::Premium::CreditsIconGradientStops()));
|
||||||
content->sizeValue(
|
widget->paintRequest() | rpl::start_with_next([=](const QRect &r) {
|
||||||
) | 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);
|
auto p = QPainter(widget);
|
||||||
p.fillRect(r, Qt::transparent);
|
|
||||||
stars->paint(p);
|
|
||||||
svg->render(
|
svg->render(
|
||||||
&p,
|
&p,
|
||||||
QRectF(
|
QRectF(
|
||||||
|
@ -790,6 +803,28 @@ void BoostCreditsBox(
|
||||||
}, button->lifetime());
|
}, 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(
|
void ReceiptCreditsBox(
|
||||||
not_null<Ui::GenericBox*> box,
|
not_null<Ui::GenericBox*> box,
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
|
@ -820,6 +855,7 @@ void ReceiptCreditsBox(
|
||||||
const auto nonConvertible = (gotStarGift && !e.starsConverted);
|
const auto nonConvertible = (gotStarGift && !e.starsConverted);
|
||||||
|
|
||||||
box->setStyle(st::giveawayGiftCodeBox);
|
box->setStyle(st::giveawayGiftCodeBox);
|
||||||
|
box->setWidth(st::boxWideWidth);
|
||||||
box->setNoContentMargin(true);
|
box->setNoContentMargin(true);
|
||||||
|
|
||||||
const auto content = box->verticalLayout();
|
const auto content = box->verticalLayout();
|
||||||
|
@ -851,6 +887,20 @@ void ReceiptCreditsBox(
|
||||||
content,
|
content,
|
||||||
GenericEntryPhoto(content, callback, stUser.photoSize)));
|
GenericEntryPhoto(content, callback, stUser.photoSize)));
|
||||||
AddViewMediaHandler(thumb->entity(), controller, e);
|
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) {
|
} else if (peer && !e.gift) {
|
||||||
if (e.subscriptionUntil.isNull() && s.until.isNull()) {
|
if (e.subscriptionUntil.isNull() && s.until.isNull()) {
|
||||||
content->add(object_ptr<Ui::CenterWrap<>>(
|
content->add(object_ptr<Ui::CenterWrap<>>(
|
||||||
|
@ -950,11 +1000,13 @@ void ReceiptCreditsBox(
|
||||||
box,
|
box,
|
||||||
object_ptr<Ui::FlatLabel>(
|
object_ptr<Ui::FlatLabel>(
|
||||||
box,
|
box,
|
||||||
rpl::single(!s.until.isNull()
|
rpl::single(!s.title.isEmpty()
|
||||||
|
? s.title
|
||||||
|
: !s.until.isNull()
|
||||||
? tr::lng_credits_box_subscription_title(tr::now)
|
? tr::lng_credits_box_subscription_title(tr::now)
|
||||||
: isPrize
|
: isPrize
|
||||||
? tr::lng_credits_box_history_entry_giveaway_name(tr::now)
|
? 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)
|
? tr::lng_credits_box_history_entry_subscription(tr::now)
|
||||||
: !e.title.isEmpty()
|
: !e.title.isEmpty()
|
||||||
? e.title
|
? e.title
|
||||||
|
@ -1270,16 +1322,48 @@ void ReceiptCreditsBox(
|
||||||
st::creditsBoxAboutDivider)));
|
st::creditsBoxAboutDivider)));
|
||||||
}
|
}
|
||||||
if (s) {
|
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);
|
Ui::AddSkip(content);
|
||||||
auto label = object_ptr<Ui::FlatLabel>(
|
auto label = object_ptr<Ui::FlatLabel>(
|
||||||
box,
|
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_off_about()
|
||||||
: tr::lng_credits_subscription_on_about(
|
: tr::lng_credits_subscription_on_about(
|
||||||
lt_date,
|
lt_date,
|
||||||
rpl::single(langDayOfMonthFull(s.until.date()))),
|
rpl::single(langDayOfMonthFull(s.until.date()))),
|
||||||
st::creditsBoxAboutDivider);
|
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);
|
label->setTextColorOverride(st::menuIconAttentionColor->c);
|
||||||
}
|
}
|
||||||
box->addRow(
|
box->addRow(
|
||||||
|
@ -1290,41 +1374,26 @@ void ReceiptCreditsBox(
|
||||||
|
|
||||||
if (e.peerType == Data::CreditsHistoryEntry::PeerType::PremiumBot) {
|
if (e.peerType == Data::CreditsHistoryEntry::PeerType::PremiumBot) {
|
||||||
const auto widget = Ui::CreateChild<Ui::RpWidget>(content);
|
const auto widget = Ui::CreateChild<Ui::RpWidget>(content);
|
||||||
using ColoredMiniStars = Ui::Premium::ColoredMiniStars;
|
AddMiniStars(content, widget, stUser, st::boxWideWidth, 2);
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
const auto toRenew = (s.cancelled || s.expired)
|
||||||
&& !s.inviteHash.isEmpty();
|
&& (rejoinByApi || rejoinByInvite)
|
||||||
const auto toCancel = !toRenew && s;
|
&& !s.cancelledByBot;
|
||||||
|
const auto toRejoin = (s.cancelled || s.expired)
|
||||||
|
&& rejoinBySlug
|
||||||
|
&& !s.cancelledByBot;
|
||||||
auto confirmText = rpl::conditional(
|
auto confirmText = rpl::conditional(
|
||||||
state->confirmButtonBusy.value(),
|
state->confirmButtonBusy.value(),
|
||||||
rpl::single(QString()),
|
rpl::single(QString()),
|
||||||
(toRenew
|
(toRenew
|
||||||
? tr::lng_credits_subscription_off_button()
|
? tr::lng_credits_subscription_off_button()
|
||||||
: toCancel
|
: toRejoin
|
||||||
? tr::lng_credits_subscription_on_button()
|
? tr::lng_credits_subscription_off_rejoin_button()
|
||||||
: (canConvert || couldConvert || nonConvertible)
|
: (canConvert || couldConvert || nonConvertible)
|
||||||
? (e.savedToProfile
|
? (e.savedToProfile
|
||||||
? tr::lng_gift_display_on_page_hide()
|
? tr::lng_gift_display_on_page_hide()
|
||||||
|
@ -1372,45 +1441,57 @@ void ReceiptCreditsBox(
|
||||||
save,
|
save,
|
||||||
done);
|
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) {
|
} else if (toRenew && s.expired) {
|
||||||
Api::CheckChatInvite(controller, s.inviteHash, nullptr, [=] {
|
Api::CheckChatInvite(controller, s.inviteHash, nullptr, [=] {
|
||||||
if (const auto strong = weak.data()) {
|
ProcessReceivedSubscriptions(weak, session);
|
||||||
strong->closeBox();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
using Flag = MTPpayments_ChangeStarsSubscription::Flag;
|
const auto done = [=] {
|
||||||
session->api().request(
|
ProcessReceivedSubscriptions(weak, session);
|
||||||
MTPpayments_ChangeStarsSubscription(
|
};
|
||||||
MTP_flags(Flag::f_canceled),
|
const auto fail = [=, show = box->uiShow()](const QString &e) {
|
||||||
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) {
|
|
||||||
if (const auto strong = weak.data()) {
|
if (const auto strong = weak.data()) {
|
||||||
state->confirmButtonBusy = false;
|
state->confirmButtonBusy = false;
|
||||||
}
|
}
|
||||||
show->showToast(error.type());
|
show->showToast(e);
|
||||||
}).send();
|
};
|
||||||
|
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), [=] {
|
const auto button = box->addButton(std::move(confirmText), [=] {
|
||||||
if (state->confirmButtonBusy.current()
|
if (state->confirmButtonBusy.current()
|
||||||
|| state->convertButtonBusy.current()) {
|
|| state->convertButtonBusy.current()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state->confirmButtonBusy = true;
|
if (willBusy) {
|
||||||
if (peer
|
state->confirmButtonBusy = true;
|
||||||
&& (toRenew
|
|
||||||
|| toCancel
|
|
||||||
|| canConvert
|
|
||||||
|| couldConvert
|
|
||||||
|| nonConvertible)) {
|
|
||||||
send();
|
send();
|
||||||
} else {
|
} else {
|
||||||
box->closeBox();
|
box->closeBox();
|
||||||
|
@ -1424,7 +1505,7 @@ void ReceiptCreditsBox(
|
||||||
AddChildToWidgetCenter(button, loadingAnimation);
|
AddChildToWidgetCenter(button, loadingAnimation);
|
||||||
loadingAnimation->showOn(state->confirmButtonBusy.value());
|
loadingAnimation->showOn(state->confirmButtonBusy.value());
|
||||||
}
|
}
|
||||||
const auto buttonWidth = st::boxWidth
|
const auto buttonWidth = st::boxWideWidth
|
||||||
- rect::m::sum::h(st::giveawayGiftCodeBox.buttonPadding);
|
- rect::m::sum::h(st::giveawayGiftCodeBox.buttonPadding);
|
||||||
|
|
||||||
button->widthValue() | rpl::filter([=] {
|
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);
|
giftListAboutMargin: margins(12px, 24px, 12px, 24px);
|
||||||
giftBoxEmojiToggleTop: 7px;
|
giftBoxEmojiToggleTop: 7px;
|
||||||
giftBoxLimitTop: 28px;
|
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,
|
extended,
|
||||||
std::move(update));
|
std::move(update));
|
||||||
};
|
};
|
||||||
} else if (entry.photoId) {
|
} else if (entry.photoId && entry.subscriptionUntil.isNull()) {
|
||||||
const auto photo = session->data().photo(entry.photoId);
|
const auto photo = session->data().photo(entry.photoId);
|
||||||
return [=](Fn<void()> update) {
|
return [=](Fn<void()> update) {
|
||||||
return GenerateCreditsPaintEntryCallback(
|
return GenerateCreditsPaintEntryCallback(
|
||||||
|
|
|
@ -57,6 +57,26 @@ ChatsFiltersTabs::ChatsFiltersTabs(
|
||||||
Ui::DiscreteSlider::setSelectOnPress(false);
|
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 {
|
int ChatsFiltersTabs::centerOfSection(int section) const {
|
||||||
const auto widths = countSectionsWidths(0);
|
const auto widths = countSectionsWidths(0);
|
||||||
auto result = 0;
|
auto result = 0;
|
||||||
|
|
|
@ -27,6 +27,8 @@ public:
|
||||||
not_null<Ui::RpWidget*> parent,
|
not_null<Ui::RpWidget*> parent,
|
||||||
const style::SettingsSlider &st);
|
const style::SettingsSlider &st);
|
||||||
|
|
||||||
|
bool setSectionsAndCheckChanged(std::vector<QString> &§ions);
|
||||||
|
|
||||||
[[nodiscard]] int centerOfSection(int section) const;
|
[[nodiscard]] int centerOfSection(int section) const;
|
||||||
void fitWidthToSections();
|
void fitWidthToSections();
|
||||||
void setUnreadCount(int index, int unreadCount, bool muted);
|
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) {
|
if ((list.size() <= 1 && !slider->width()) || state->ignoreRefresh) {
|
||||||
return;
|
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();
|
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();
|
slider->fitWidthToSections();
|
||||||
{
|
{
|
||||||
const auto reorderAll = session->user()->isPremium();
|
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 addInfo();
|
||||||
void addStoryArchive();
|
void addStoryArchive();
|
||||||
void addNewWindow();
|
void addNewWindow();
|
||||||
void addToggleFolder();
|
void addToggleFolder(bool onlyForChannels);
|
||||||
void addToggleUnreadMark();
|
void addToggleUnreadMark();
|
||||||
void addToggleArchive();
|
void addToggleArchive();
|
||||||
void addClearHistory();
|
void addClearHistory();
|
||||||
|
@ -620,12 +620,16 @@ void Filler::addStoryArchive() {
|
||||||
}, &st::menuIconStoriesArchiveSection);
|
}, &st::menuIconStoriesArchiveSection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Filler::addToggleFolder() {
|
void Filler::addToggleFolder(bool onlyForChannels) {
|
||||||
const auto controller = _controller;
|
const auto controller = _controller;
|
||||||
const auto history = _request.key.history();
|
const auto history = _request.key.history();
|
||||||
if (_topic || !history || !history->owner().chatsFilters().has()) {
|
if (_topic || !history || !history->owner().chatsFilters().has()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (onlyForChannels
|
||||||
|
&& (!history->peer->isChannel() || !history->inChatList())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_addAction(PeerMenuCallback::Args{
|
_addAction(PeerMenuCallback::Args{
|
||||||
.text = tr::lng_filters_menu_add(tr::now),
|
.text = tr::lng_filters_menu_add(tr::now),
|
||||||
.handler = nullptr,
|
.handler = nullptr,
|
||||||
|
@ -1409,7 +1413,7 @@ void Filler::fillContextMenuActions() {
|
||||||
addToggleMuteSubmenu(false);
|
addToggleMuteSubmenu(false);
|
||||||
addToggleUnreadMark();
|
addToggleUnreadMark();
|
||||||
addToggleTopicClosed();
|
addToggleTopicClosed();
|
||||||
addToggleFolder();
|
addToggleFolder(false);
|
||||||
if (const auto user = _peer->asUser()) {
|
if (const auto user = _peer->asUser()) {
|
||||||
if (!user->isContact()) {
|
if (!user->isContact()) {
|
||||||
addBlockUser();
|
addBlockUser();
|
||||||
|
@ -1459,6 +1463,7 @@ void Filler::fillProfileActions() {
|
||||||
addToggleTopicClosed();
|
addToggleTopicClosed();
|
||||||
addViewDiscussion();
|
addViewDiscussion();
|
||||||
addExportChat();
|
addExportChat();
|
||||||
|
addToggleFolder(true);
|
||||||
addBlockUser();
|
addBlockUser();
|
||||||
addReport();
|
addReport();
|
||||||
addLeaveChat();
|
addLeaveChat();
|
||||||
|
@ -2070,7 +2075,17 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||||
|
|
||||||
class ListBox final : public PeerListBox {
|
class ListBox final : public PeerListBox {
|
||||||
public:
|
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) {
|
void setBottomSkip(int bottomSkip) {
|
||||||
PeerListBox::setInnerBottomSkip(bottomSkip);
|
PeerListBox::setInnerBottomSkip(bottomSkip);
|
||||||
|
@ -2095,9 +2110,21 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||||
_forwardOptions = forwardOptions;
|
_forwardOptions = forwardOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
not_null<PeerListContent*> peerListContent() const {
|
||||||
|
return PeerListBox::content();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setFilterId(FilterId filterId) {
|
||||||
|
_filterId = filterId;
|
||||||
|
}
|
||||||
|
[[nodiscard]] FilterId filterId() const {
|
||||||
|
return _filterId;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
rpl::event_stream<> _focusRequests;
|
rpl::event_stream<> _focusRequests;
|
||||||
Ui::ForwardOptions _forwardOptions;
|
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;
|
using PeerListController::setSearchNoResultsText;
|
||||||
|
|
||||||
void rowClicked(not_null<PeerListRow*> row) override final {
|
void rowClicked(not_null<PeerListRow*> row) override final {
|
||||||
|
@ -2167,166 +2200,48 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||||
base::unique_qptr<Ui::PopupMenu> menu;
|
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);
|
box->scrollToY(0);
|
||||||
auto &filters = session->data().chatsFilters();
|
auto &filters = session->data().chatsFilters();
|
||||||
const auto &list = filters.list();
|
const auto &list = filters.list();
|
||||||
if (list.size() <= 1) {
|
if (list.size() <= 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto pinnedList = [&](
|
if (box->filterId() == id) {
|
||||||
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()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
box->peerListPartitionRows([&](const PeerListRow &row) {
|
box->setFilterId(id);
|
||||||
const auto rowPtr = const_cast<PeerListRow*>(&row);
|
|
||||||
if (!filter->id()) {
|
using SavedState = PeerListController::SavedStateBase;
|
||||||
box->peerListSetRowHidden(rowPtr, false);
|
auto state = std::make_unique<PeerListState>();
|
||||||
} else {
|
state->controllerState = std::make_unique<SavedState>();
|
||||||
const auto result = filter->contains(
|
|
||||||
session->data().history(row.peer()));
|
const auto addList = [&](auto chats) {
|
||||||
box->peerListSetRowHidden(rowPtr, !result);
|
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 = [&] {
|
const auto state = [&] {
|
||||||
auto controller = std::make_unique<Controller>(session);
|
auto controller = std::make_unique<Controller>(session);
|
||||||
const auto controllerRaw = controller.get();
|
const auto controllerRaw = controller.get();
|
||||||
auto init = [=](not_null<PeerListBox*> box) {
|
auto init = [=](not_null<ListBox*> box) {
|
||||||
controllerRaw->setSearchNoResultsText(
|
controllerRaw->setSearchNoResultsText(
|
||||||
tr::lng_bot_chats_not_found(tr::now));
|
tr::lng_bot_chats_not_found(tr::now));
|
||||||
box->setSpecialTabMode(true);
|
box->setSpecialTabMode(true);
|
||||||
|
|
|
@ -1874,6 +1874,10 @@ bool SessionController::switchInlineQuery(
|
||||||
int(textWithTags.text.size()),
|
int(textWithTags.text.size()),
|
||||||
Ui::kQFixedMax
|
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>(
|
auto draft = std::make_unique<Data::Draft>(
|
||||||
textWithTags,
|
textWithTags,
|
||||||
to.currentReplyTo,
|
to.currentReplyTo,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
AppVersion 5008002
|
AppVersion 5008003
|
||||||
AppVersionStrMajor 5.8
|
AppVersionStrMajor 5.8
|
||||||
AppVersionStrSmall 5.8.2
|
AppVersionStrSmall 5.8.3
|
||||||
AppVersionStr 5.8.2
|
AppVersionStr 5.8.3
|
||||||
BetaChannel 0
|
BetaChannel 0
|
||||||
AlphaVersion 0
|
AlphaVersion 0
|
||||||
AppVersionOriginal 5.8.2
|
AppVersionOriginal 5.8.3
|
||||||
|
|
|
@ -21,6 +21,7 @@ set(style_files
|
||||||
ui/chat/chat.style
|
ui/chat/chat.style
|
||||||
ui/effects/credits.style
|
ui/effects/credits.style
|
||||||
ui/effects/premium.style
|
ui/effects/premium.style
|
||||||
|
ui/color_indices.style
|
||||||
boxes/boxes.style
|
boxes/boxes.style
|
||||||
dialogs/dialogs.style
|
dialogs/dialogs.style
|
||||||
chat_helpers/chat_helpers.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)
|
5.8.2 (19.11.24)
|
||||||
|
|
||||||
- Improve bottom label color in mini apps.
|
- Improve bottom label color in mini apps.
|
||||||
|
|
Loading…
Add table
Reference in a new issue