mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Edit price on send, send single paid media.
This commit is contained in:
parent
3ece9b1566
commit
a9bd7803e6
30 changed files with 382 additions and 37 deletions
|
@ -3311,6 +3311,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
"lng_context_spoiler_effect" = "Hide with Spoiler";
|
"lng_context_spoiler_effect" = "Hide with Spoiler";
|
||||||
"lng_context_disable_spoiler" = "Remove Spoiler";
|
"lng_context_disable_spoiler" = "Remove Spoiler";
|
||||||
|
"lng_context_make_paid" = "Make This Content Paid";
|
||||||
|
"lng_context_change_price" = "Change Price";
|
||||||
|
|
||||||
"lng_factcheck_title" = "Fact Check";
|
"lng_factcheck_title" = "Fact Check";
|
||||||
"lng_factcheck_placeholder" = "Add Facts or Context";
|
"lng_factcheck_placeholder" = "Add Facts or Context";
|
||||||
|
@ -3322,6 +3324,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_factcheck_bottom" = "This clarification was provided by a fact checking agency assigned by the department of the government of your country ({country}) responsible for combatting misinformation.";
|
"lng_factcheck_bottom" = "This clarification was provided by a fact checking agency assigned by the department of the government of your country ({country}) responsible for combatting misinformation.";
|
||||||
"lng_factcheck_links" = "Only **t.me/** links are allowed.";
|
"lng_factcheck_links" = "Only **t.me/** links are allowed.";
|
||||||
|
|
||||||
|
"lng_paid_title" = "Paid Content";
|
||||||
|
"lng_paid_enter_cost" = "Enter Unlock Cost";
|
||||||
|
"lng_paid_cost_placeholder" = "Stars to Unlock";
|
||||||
|
"lng_paid_about" = "Users will have to transfer this amount of Stars to your channel in order to view this media. {link}";
|
||||||
|
"lng_paid_about_link" = "More about stars >";
|
||||||
|
"lng_paid_price" = "Unlock for {price}";
|
||||||
|
|
||||||
"lng_translate_show_original" = "Show Original";
|
"lng_translate_show_original" = "Show Original";
|
||||||
"lng_translate_bar_to" = "Translate to {name}";
|
"lng_translate_bar_to" = "Translate to {name}";
|
||||||
"lng_translate_bar_to_other" = "Translate to {name}";
|
"lng_translate_bar_to_other" = "Translate to {name}";
|
||||||
|
|
|
@ -20,6 +20,7 @@ namespace Api {
|
||||||
inline constexpr auto kScheduledUntilOnlineTimestamp = TimeId(0x7FFFFFFE);
|
inline constexpr auto kScheduledUntilOnlineTimestamp = TimeId(0x7FFFFFFE);
|
||||||
|
|
||||||
struct SendOptions {
|
struct SendOptions {
|
||||||
|
uint64 price = 0;
|
||||||
PeerData *sendAs = nullptr;
|
PeerData *sendAs = nullptr;
|
||||||
TimeId scheduled = 0;
|
TimeId scheduled = 0;
|
||||||
BusinessShortcutId shortcutId = 0;
|
BusinessShortcutId shortcutId = 0;
|
||||||
|
|
|
@ -55,7 +55,9 @@ void ViewsManager::removeIncremented(not_null<PeerData*> peer) {
|
||||||
_incremented.remove(peer);
|
_incremented.remove(peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewsManager::pollExtendedMedia(not_null<HistoryItem*> item) {
|
void ViewsManager::pollExtendedMedia(
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
bool force) {
|
||||||
if (!item->isRegular()) {
|
if (!item->isRegular()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -63,14 +65,20 @@ void ViewsManager::pollExtendedMedia(not_null<HistoryItem*> item) {
|
||||||
const auto peer = item->history()->peer;
|
const auto peer = item->history()->peer;
|
||||||
auto &request = _pollRequests[peer];
|
auto &request = _pollRequests[peer];
|
||||||
if (request.ids.contains(id) || request.sent.contains(id)) {
|
if (request.ids.contains(id) || request.sent.contains(id)) {
|
||||||
return;
|
if (!force || request.forced) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
request.ids.emplace(id);
|
request.ids.emplace(id);
|
||||||
if (!request.id && !request.when) {
|
if (force) {
|
||||||
request.when = crl::now() + kPollExtendedMediaPeriod;
|
request.forced = true;
|
||||||
}
|
}
|
||||||
if (!_pollTimer.isActive()) {
|
const auto delay = force ? 1 : kPollExtendedMediaPeriod;
|
||||||
_pollTimer.callOnce(kPollExtendedMediaPeriod);
|
if (!request.id && (!request.when || force)) {
|
||||||
|
request.when = crl::now() + delay;
|
||||||
|
}
|
||||||
|
if (!_pollTimer.isActive() || force) {
|
||||||
|
_pollTimer.callOnce(delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,9 +168,12 @@ void ViewsManager::sendPollRequests(
|
||||||
if (i->second.ids.empty()) {
|
if (i->second.ids.empty()) {
|
||||||
i = _pollRequests.erase(i);
|
i = _pollRequests.erase(i);
|
||||||
} else {
|
} else {
|
||||||
i->second.when = now + kPollExtendedMediaPeriod;
|
const auto delay = i->second.forced
|
||||||
if (!_pollTimer.isActive()) {
|
? 1
|
||||||
_pollTimer.callOnce(kPollExtendedMediaPeriod);
|
: kPollExtendedMediaPeriod;
|
||||||
|
i->second.when = now + delay;
|
||||||
|
if (!_pollTimer.isActive() || i->second.forced) {
|
||||||
|
_pollTimer.callOnce(delay);
|
||||||
}
|
}
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ public:
|
||||||
void scheduleIncrement(not_null<HistoryItem*> item);
|
void scheduleIncrement(not_null<HistoryItem*> item);
|
||||||
void removeIncremented(not_null<PeerData*> peer);
|
void removeIncremented(not_null<PeerData*> peer);
|
||||||
|
|
||||||
void pollExtendedMedia(not_null<HistoryItem*> item);
|
void pollExtendedMedia(not_null<HistoryItem*> item, bool force = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct PollExtendedMediaRequest {
|
struct PollExtendedMediaRequest {
|
||||||
|
@ -34,6 +34,7 @@ private:
|
||||||
mtpRequestId id = 0;
|
mtpRequestId id = 0;
|
||||||
base::flat_set<MsgId> ids;
|
base::flat_set<MsgId> ids;
|
||||||
base::flat_set<MsgId> sent;
|
base::flat_set<MsgId> sent;
|
||||||
|
bool forced = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
void viewsIncrement();
|
void viewsIncrement();
|
||||||
|
|
|
@ -4188,7 +4188,11 @@ void ApiWrap::sendMediaWithRandomId(
|
||||||
MTP_flags(flags),
|
MTP_flags(flags),
|
||||||
peer->input,
|
peer->input,
|
||||||
Data::Histories::ReplyToPlaceholder(),
|
Data::Histories::ReplyToPlaceholder(),
|
||||||
media,
|
(options.price
|
||||||
|
? MTPInputMedia(MTP_inputMediaPaidMedia(
|
||||||
|
MTP_long(options.price),
|
||||||
|
MTP_vector<MTPInputMedia>(1, media)))
|
||||||
|
: media),
|
||||||
MTP_string(caption.text),
|
MTP_string(caption.text),
|
||||||
MTP_long(randomId),
|
MTP_long(randomId),
|
||||||
MTPReplyMarkup(),
|
MTPReplyMarkup(),
|
||||||
|
|
|
@ -463,6 +463,7 @@ void EditCaptionBox::rebuildPreview() {
|
||||||
st::defaultComposeControls,
|
st::defaultComposeControls,
|
||||||
gifPaused,
|
gifPaused,
|
||||||
file,
|
file,
|
||||||
|
[] { return true; },
|
||||||
Ui::AttachControls::Type::EditOnly);
|
Ui::AttachControls::Type::EditOnly);
|
||||||
_isPhoto = (media && media->isPhoto());
|
_isPhoto = (media && media->isPhoto());
|
||||||
const auto withCheckbox = _isPhoto && CanBeCompressed(_albumType);
|
const auto withCheckbox = _isPhoto && CanBeCompressed(_albumType);
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "storage/localstorage.h"
|
#include "storage/localstorage.h"
|
||||||
#include "storage/storage_media_prepare.h"
|
#include "storage/storage_media_prepare.h"
|
||||||
|
#include "iv/iv_instance.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "main/main_session_settings.h"
|
#include "main/main_session_settings.h"
|
||||||
|
@ -36,9 +37,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/chat/attach/attach_single_file_preview.h"
|
#include "ui/chat/attach/attach_single_file_preview.h"
|
||||||
#include "ui/chat/attach/attach_single_media_preview.h"
|
#include "ui/chat/attach/attach_single_media_preview.h"
|
||||||
#include "ui/grouped_layout.h"
|
#include "ui/grouped_layout.h"
|
||||||
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/toast/toast.h"
|
#include "ui/toast/toast.h"
|
||||||
#include "ui/controls/emoji_button.h"
|
#include "ui/controls/emoji_button.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
|
#include "ui/vertical_list.h"
|
||||||
#include "lottie/lottie_single_player.h"
|
#include "lottie/lottie_single_player.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
|
@ -58,6 +61,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kMaxMessageLength = 4096;
|
constexpr auto kMaxMessageLength = 4096;
|
||||||
|
constexpr auto kMaxPrice = 1000ULL;
|
||||||
|
|
||||||
using Ui::SendFilesWay;
|
using Ui::SendFilesWay;
|
||||||
|
|
||||||
|
@ -103,6 +107,74 @@ rpl::producer<QString> FieldPlaceholder(
|
||||||
: tr::lng_photos_comment();
|
: tr::lng_photos_comment();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EditPriceBox(
|
||||||
|
not_null<Ui::GenericBox*> box,
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
uint64 price,
|
||||||
|
Fn<void(uint64)> apply) {
|
||||||
|
const auto owner = &session->data();
|
||||||
|
box->setTitle(tr::lng_paid_title());
|
||||||
|
AddSubsectionTitle(
|
||||||
|
box->verticalLayout(),
|
||||||
|
tr::lng_paid_enter_cost(),
|
||||||
|
(st::boxRowPadding - QMargins(
|
||||||
|
st::defaultSubsectionTitlePadding.left(),
|
||||||
|
0,
|
||||||
|
st::defaultSubsectionTitlePadding.right(),
|
||||||
|
0)));
|
||||||
|
const auto field = box->addRow(object_ptr<Ui::InputField>(
|
||||||
|
box,
|
||||||
|
st::editTagField,
|
||||||
|
tr::lng_paid_cost_placeholder(),
|
||||||
|
price ? QString::number(price) : QString()));
|
||||||
|
field->selectAll();
|
||||||
|
field->setMaxLength(QString::number(kMaxPrice).size());
|
||||||
|
box->setFocusCallback([=] {
|
||||||
|
field->setFocusFast();
|
||||||
|
});
|
||||||
|
const auto about = box->addRow(
|
||||||
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
box,
|
||||||
|
tr::lng_paid_about(
|
||||||
|
lt_link,
|
||||||
|
tr::lng_paid_about_link() | Ui::Text::ToLink(),
|
||||||
|
Ui::Text::WithEntities),
|
||||||
|
st::paidAmountAbout),
|
||||||
|
st::boxRowPadding + QMargins(0, st::sendMediaRowSkip, 0, 0));
|
||||||
|
about->setClickHandlerFilter([=](const auto &...) {
|
||||||
|
Core::App().iv().openWithIvPreferred(
|
||||||
|
session,
|
||||||
|
u"https://telegram.org/blog/telegram-stars"_q);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
field->paintRequest() | rpl::start_with_next([=](QRect clip) {
|
||||||
|
auto p = QPainter(field);
|
||||||
|
st::paidStarIcon.paint(p, 0, st::paidStarIconTop, field->width());
|
||||||
|
}, field->lifetime());
|
||||||
|
|
||||||
|
const auto save = [=] {
|
||||||
|
const auto now = field->getLastText().toULongLong();
|
||||||
|
if (now > kMaxPrice) {
|
||||||
|
field->showError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto weak = Ui::MakeWeak(box);
|
||||||
|
apply(now);
|
||||||
|
if (const auto strong = weak.data()) {
|
||||||
|
strong->closeBox();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
field->submits(
|
||||||
|
) | rpl::start_with_next(save, field->lifetime());
|
||||||
|
|
||||||
|
box->addButton(tr::lng_settings_save(), save);
|
||||||
|
box->addButton(tr::lng_cancel(), [=] {
|
||||||
|
box->closeBox();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
SendFilesLimits DefaultLimitsForPeer(not_null<PeerData*> peer) {
|
SendFilesLimits DefaultLimitsForPeer(not_null<PeerData*> peer) {
|
||||||
|
@ -153,7 +225,8 @@ SendFilesBox::Block::Block(
|
||||||
int from,
|
int from,
|
||||||
int till,
|
int till,
|
||||||
Fn<bool()> gifPaused,
|
Fn<bool()> gifPaused,
|
||||||
SendFilesWay way)
|
SendFilesWay way,
|
||||||
|
Fn<bool()> canToggleSpoiler)
|
||||||
: _items(items)
|
: _items(items)
|
||||||
, _from(from)
|
, _from(from)
|
||||||
, _till(till) {
|
, _till(till) {
|
||||||
|
@ -170,14 +243,16 @@ SendFilesBox::Block::Block(
|
||||||
parent.get(),
|
parent.get(),
|
||||||
st,
|
st,
|
||||||
my,
|
my,
|
||||||
way);
|
way,
|
||||||
|
std::move(canToggleSpoiler));
|
||||||
_preview.reset(preview);
|
_preview.reset(preview);
|
||||||
} else {
|
} else {
|
||||||
const auto media = Ui::SingleMediaPreview::Create(
|
const auto media = Ui::SingleMediaPreview::Create(
|
||||||
parent,
|
parent,
|
||||||
st,
|
st,
|
||||||
gifPaused,
|
gifPaused,
|
||||||
first);
|
first,
|
||||||
|
std::move(canToggleSpoiler));
|
||||||
if (media) {
|
if (media) {
|
||||||
_isSingleMedia = true;
|
_isSingleMedia = true;
|
||||||
_preview.reset(media);
|
_preview.reset(media);
|
||||||
|
@ -385,6 +460,9 @@ Fn<SendMenu::Details()> SendFilesBox::prepareSendMenuDetails(
|
||||||
: _invertCaption
|
: _invertCaption
|
||||||
? SendMenu::CaptionState::Above
|
? SendMenu::CaptionState::Above
|
||||||
: SendMenu::CaptionState::Below;
|
: SendMenu::CaptionState::Below;
|
||||||
|
result.price = canChangePrice()
|
||||||
|
? _price.current()
|
||||||
|
: std::optional<uint64>();
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -398,6 +476,7 @@ auto SendFilesBox::prepareSendMenuCallback()
|
||||||
case Type::CaptionUp: _invertCaption = true; break;
|
case Type::CaptionUp: _invertCaption = true; break;
|
||||||
case Type::SpoilerOn: toggleSpoilers(true); break;
|
case Type::SpoilerOn: toggleSpoilers(true); break;
|
||||||
case Type::SpoilerOff: toggleSpoilers(false); break;
|
case Type::SpoilerOff: toggleSpoilers(false); break;
|
||||||
|
case Type::ChangePrice: changePrice(); break;
|
||||||
default:
|
default:
|
||||||
SendMenu::DefaultCallback(
|
SendMenu::DefaultCallback(
|
||||||
_show,
|
_show,
|
||||||
|
@ -588,14 +667,22 @@ void SendFilesBox::refreshButtons() {
|
||||||
addMenuButton();
|
addMenuButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SendFilesBox::hasSendMenu(const SendMenu::Details &details) const {
|
bool SendFilesBox::hasSendMenu(const MenuDetails &details) const {
|
||||||
return (details.type != SendMenu::Type::Disabled)
|
return (details.type != SendMenu::Type::Disabled)
|
||||||
|| (details.spoiler != SendMenu::SpoilerState::None)
|
|| (details.spoiler != SendMenu::SpoilerState::None)
|
||||||
|| (details.caption != SendMenu::CaptionState::None);
|
|| (details.caption != SendMenu::CaptionState::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SendFilesBox::hasSpoilerMenu() const {
|
bool SendFilesBox::hasSpoilerMenu() const {
|
||||||
return _list.hasSpoilerMenu(_sendWay.current().sendImagesAsPhotos());
|
return !hasPrice()
|
||||||
|
&& _list.hasSpoilerMenu(_sendWay.current().sendImagesAsPhotos());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SendFilesBox::canChangePrice() const {
|
||||||
|
const auto way = _sendWay.current();
|
||||||
|
return _list.canChangePrice(
|
||||||
|
way.groupFiles() && way.sendImagesAsPhotos(),
|
||||||
|
way.sendImagesAsPhotos());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendFilesBox::applyBlockChanges() {
|
void SendFilesBox::applyBlockChanges() {
|
||||||
|
@ -618,6 +705,71 @@ void SendFilesBox::toggleSpoilers(bool enabled) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SendFilesBox::changePrice() {
|
||||||
|
const auto weak = Ui::MakeWeak(this);
|
||||||
|
const auto session = &_show->session();
|
||||||
|
const auto now = _price.current();
|
||||||
|
_show->show(Box(EditPriceBox, session, now, [=](uint64 price) {
|
||||||
|
if (weak && price != now) {
|
||||||
|
_price = price;
|
||||||
|
refreshPriceTag();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SendFilesBox::hasPrice() const {
|
||||||
|
return canChangePrice() && _price.current() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendFilesBox::refreshPriceTag() {
|
||||||
|
const auto resetSpoilers = hasPrice() || _priceTag;
|
||||||
|
if (resetSpoilers) {
|
||||||
|
for (auto &file : _list.files) {
|
||||||
|
file.spoiler = false;
|
||||||
|
}
|
||||||
|
for (auto &block : _blocks) {
|
||||||
|
block.toggleSpoilers(hasPrice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasPrice()) {
|
||||||
|
_priceTag = nullptr;
|
||||||
|
} else if (!_priceTag) {
|
||||||
|
_priceTag = std::make_unique<Ui::RpWidget>(_inner.data());
|
||||||
|
const auto raw = _priceTag.get();
|
||||||
|
|
||||||
|
raw->show();
|
||||||
|
raw->paintRequest() | rpl::start_with_next([=] {
|
||||||
|
auto p = QPainter(raw);
|
||||||
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
p.setBrush(st::toastBg);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
const auto radius = std::min(raw->width(), raw->height()) / 2.;
|
||||||
|
p.drawRoundedRect(raw->rect(), radius, radius);
|
||||||
|
}, raw->lifetime());
|
||||||
|
|
||||||
|
auto price = _price.value() | rpl::map([=](uint64 amount) {
|
||||||
|
return QChar(0x2B50) + Lang::FormatCountDecimal(amount);
|
||||||
|
});
|
||||||
|
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||||
|
raw,
|
||||||
|
tr::lng_paid_price(lt_price, std::move(price)),
|
||||||
|
st::paidTagLabel);
|
||||||
|
label->sizeValue() | rpl::start_with_next([=](QSize size) {
|
||||||
|
const auto inner = QRect(QPoint(), size);
|
||||||
|
const auto rect = inner.marginsAdded(st::paidTagPadding);
|
||||||
|
raw->resize(rect.size());
|
||||||
|
label->move(-rect.topLeft());
|
||||||
|
}, label->lifetime());
|
||||||
|
_inner->sizeValue() | rpl::start_with_next([=](QSize size) {
|
||||||
|
raw->move(
|
||||||
|
(size.width() - raw->width()) / 2,
|
||||||
|
(size.height() - raw->height()) / 2);
|
||||||
|
}, raw->lifetime());
|
||||||
|
} else {
|
||||||
|
_priceTag->raise();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SendFilesBox::addMenuButton() {
|
void SendFilesBox::addMenuButton() {
|
||||||
const auto details = _sendMenuDetails();
|
const auto details = _sendMenuDetails();
|
||||||
if (!hasSendMenu(details)) {
|
if (!hasSendMenu(details)) {
|
||||||
|
@ -766,7 +918,8 @@ void SendFilesBox::pushBlock(int from, int till) {
|
||||||
from,
|
from,
|
||||||
till,
|
till,
|
||||||
gifPaused,
|
gifPaused,
|
||||||
_sendWay.current());
|
_sendWay.current(),
|
||||||
|
[=] { return !hasPrice(); });
|
||||||
auto &block = _blocks.back();
|
auto &block = _blocks.back();
|
||||||
const auto widget = _inner->add(
|
const auto widget = _inner->add(
|
||||||
block.takeWidget(),
|
block.takeWidget(),
|
||||||
|
@ -893,6 +1046,7 @@ void SendFilesBox::pushBlock(int from, int till) {
|
||||||
|
|
||||||
void SendFilesBox::refreshControls(bool initial) {
|
void SendFilesBox::refreshControls(bool initial) {
|
||||||
refreshButtons();
|
refreshButtons();
|
||||||
|
refreshPriceTag();
|
||||||
refreshTitleText();
|
refreshTitleText();
|
||||||
updateSendWayControls();
|
updateSendWayControls();
|
||||||
updateCaptionPlaceholder();
|
updateCaptionPlaceholder();
|
||||||
|
@ -1447,6 +1601,7 @@ void SendFilesBox::send(
|
||||||
auto child = _sendMenuDetails();
|
auto child = _sendMenuDetails();
|
||||||
child.spoiler = SendMenu::SpoilerState::None;
|
child.spoiler = SendMenu::SpoilerState::None;
|
||||||
child.caption = SendMenu::CaptionState::None;
|
child.caption = SendMenu::CaptionState::None;
|
||||||
|
child.price = std::nullopt;
|
||||||
return SendMenu::DefaultCallback(_show, sendCallback())(
|
return SendMenu::DefaultCallback(_show, sendCallback())(
|
||||||
{ .type = SendMenu::ActionType::Schedule },
|
{ .type = SendMenu::ActionType::Schedule },
|
||||||
child);
|
child);
|
||||||
|
@ -1475,6 +1630,7 @@ void SendFilesBox::send(
|
||||||
? _caption->getTextWithAppliedMarkdown()
|
? _caption->getTextWithAppliedMarkdown()
|
||||||
: TextWithTags();
|
: TextWithTags();
|
||||||
options.invertCaption = _invertCaption;
|
options.invertCaption = _invertCaption;
|
||||||
|
options.price = hasPrice() ? _price.current() : 0;
|
||||||
if (!validateLength(caption.text)) {
|
if (!validateLength(caption.text)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,7 +149,8 @@ private:
|
||||||
int from,
|
int from,
|
||||||
int till,
|
int till,
|
||||||
Fn<bool()> gifPaused,
|
Fn<bool()> gifPaused,
|
||||||
Ui::SendFilesWay way);
|
Ui::SendFilesWay way,
|
||||||
|
Fn<bool()> canToggleSpoiler);
|
||||||
Block(Block &&other) = default;
|
Block(Block &&other) = default;
|
||||||
Block &operator=(Block &&other) = default;
|
Block &operator=(Block &&other) = default;
|
||||||
|
|
||||||
|
@ -190,6 +191,11 @@ private:
|
||||||
void addMenuButton();
|
void addMenuButton();
|
||||||
void applyBlockChanges();
|
void applyBlockChanges();
|
||||||
void toggleSpoilers(bool enabled);
|
void toggleSpoilers(bool enabled);
|
||||||
|
void changePrice();
|
||||||
|
|
||||||
|
[[nodiscard]] bool canChangePrice() const;
|
||||||
|
[[nodiscard]] bool hasPrice() const;
|
||||||
|
void refreshPriceTag();
|
||||||
|
|
||||||
bool validateLength(const QString &text) const;
|
bool validateLength(const QString &text) const;
|
||||||
void refreshButtons();
|
void refreshButtons();
|
||||||
|
@ -251,6 +257,8 @@ private:
|
||||||
SendFilesCheck _check;
|
SendFilesCheck _check;
|
||||||
SendFilesConfirmed _confirmedCallback;
|
SendFilesConfirmed _confirmedCallback;
|
||||||
Fn<void()> _cancelledCallback;
|
Fn<void()> _cancelledCallback;
|
||||||
|
rpl::variable<uint64> _price = 0;
|
||||||
|
std::unique_ptr<Ui::RpWidget> _priceTag;
|
||||||
bool _confirmed = false;
|
bool _confirmed = false;
|
||||||
bool _invertCaption = false;
|
bool _invertCaption = false;
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,7 @@ ComposeIcons {
|
||||||
menuSpoilerOff: icon;
|
menuSpoilerOff: icon;
|
||||||
menuBelow: icon;
|
menuBelow: icon;
|
||||||
menuAbove: icon;
|
menuAbove: icon;
|
||||||
|
menuPrice: icon;
|
||||||
|
|
||||||
stripBubble: icon;
|
stripBubble: icon;
|
||||||
stripExpandPanel: icon;
|
stripExpandPanel: icon;
|
||||||
|
@ -610,6 +611,7 @@ defaultComposeIcons: ComposeIcons {
|
||||||
menuSpoilerOff: menuIconSpoilerOff;
|
menuSpoilerOff: menuIconSpoilerOff;
|
||||||
menuBelow: menuIconBelow;
|
menuBelow: menuIconBelow;
|
||||||
menuAbove: menuIconAbove;
|
menuAbove: menuIconAbove;
|
||||||
|
menuPrice: menuIconEarn;
|
||||||
|
|
||||||
stripBubble: icon{
|
stripBubble: icon{
|
||||||
{ "chat/reactions_bubble_shadow", windowShadowFg },
|
{ "chat/reactions_bubble_shadow", windowShadowFg },
|
||||||
|
@ -1406,3 +1408,15 @@ editTagField: InputField(defaultInputField) {
|
||||||
editTagLimit: FlatLabel(defaultFlatLabel) {
|
editTagLimit: FlatLabel(defaultFlatLabel) {
|
||||||
textFg: windowSubTextFg;
|
textFg: windowSubTextFg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
paidStarIcon: icon {{ "settings/premium/star", creditsBg1 }};
|
||||||
|
paidStarIconTop: 7px;
|
||||||
|
paidAmountAbout: FlatLabel(defaultFlatLabel) {
|
||||||
|
minWidth: 256px;
|
||||||
|
textFg: windowSubTextFg;
|
||||||
|
}
|
||||||
|
paidTagLabel: FlatLabel(defaultFlatLabel) {
|
||||||
|
textFg: radialFg;
|
||||||
|
style: semiboldTextStyle;
|
||||||
|
}
|
||||||
|
paidTagPadding: margins(16px, 6px, 16px, 6px);
|
||||||
|
|
|
@ -170,6 +170,12 @@ void UpdateCloudFile(
|
||||||
if (data.progressivePartSize && !file.location.valid()) {
|
if (data.progressivePartSize && !file.location.valid()) {
|
||||||
file.progressivePartSize = data.progressivePartSize;
|
file.progressivePartSize = data.progressivePartSize;
|
||||||
}
|
}
|
||||||
|
if (data.location.width()
|
||||||
|
&& data.location.height()
|
||||||
|
&& !file.location.valid()
|
||||||
|
&& !file.location.width()) {
|
||||||
|
file.location = data.location;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -323,7 +323,7 @@ bool UpdateExtendedMedia(
|
||||||
auto changed = false;
|
auto changed = false;
|
||||||
const auto count = int(media.size());
|
const auto count = int(media.size());
|
||||||
for (auto i = 0; i != count; ++i) {
|
for (auto i = 0; i != count; ++i) {
|
||||||
if (i < invoice.extendedMedia.size()) {
|
if (i <= invoice.extendedMedia.size()) {
|
||||||
invoice.extendedMedia.emplace_back();
|
invoice.extendedMedia.emplace_back();
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
@ -388,6 +388,7 @@ Invoice ComputeInvoiceData(
|
||||||
auto result = Invoice{
|
auto result = Invoice{
|
||||||
.amount = data.vstars_amount().v,
|
.amount = data.vstars_amount().v,
|
||||||
.currency = Ui::kCreditsCurrency,
|
.currency = Ui::kCreditsCurrency,
|
||||||
|
.isPaidMedia = true,
|
||||||
};
|
};
|
||||||
UpdateExtendedMedia(result, item, data.vextended_media().v);
|
UpdateExtendedMedia(result, item, data.vextended_media().v);
|
||||||
return result;
|
return result;
|
||||||
|
@ -1908,6 +1909,7 @@ MediaInvoice::MediaInvoice(
|
||||||
.title = data.title,
|
.title = data.title,
|
||||||
.description = data.description,
|
.description = data.description,
|
||||||
.photo = data.photo,
|
.photo = data.photo,
|
||||||
|
.isPaidMedia = data.isPaidMedia,
|
||||||
.isTest = data.isTest,
|
.isTest = data.isTest,
|
||||||
} {
|
} {
|
||||||
_invoice.extendedMedia.reserve(data.extendedMedia.size());
|
_invoice.extendedMedia.reserve(data.extendedMedia.size());
|
||||||
|
|
|
@ -94,6 +94,7 @@ struct Invoice {
|
||||||
TextWithEntities description;
|
TextWithEntities description;
|
||||||
std::vector<std::unique_ptr<Media>> extendedMedia;
|
std::vector<std::unique_ptr<Media>> extendedMedia;
|
||||||
PhotoData *photo = nullptr;
|
PhotoData *photo = nullptr;
|
||||||
|
bool isPaidMedia = false;
|
||||||
bool isTest = false;
|
bool isTest = false;
|
||||||
};
|
};
|
||||||
[[nodiscard]] bool HasExtendedMedia(const Invoice &invoice);
|
[[nodiscard]] bool HasExtendedMedia(const Invoice &invoice);
|
||||||
|
|
|
@ -7,9 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "history/view/media/history_view_media_common.h"
|
#include "history/view/media/history_view_media_common.h"
|
||||||
|
|
||||||
|
#include "api/api_views.h"
|
||||||
|
#include "apiwrap.h"
|
||||||
#include "ui/text/format_values.h"
|
#include "ui/text/format_values.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
|
#include "core/click_handler_types.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
#include "data/data_wall_paper.h"
|
#include "data/data_wall_paper.h"
|
||||||
#include "data/data_media_types.h"
|
#include "data/data_media_types.h"
|
||||||
#include "history/view/history_view_element.h"
|
#include "history/view/history_view_element.h"
|
||||||
|
@ -19,7 +23,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/media/history_view_document.h"
|
#include "history/view/media/history_view_document.h"
|
||||||
#include "history/view/media/history_view_sticker.h"
|
#include "history/view/media/history_view_sticker.h"
|
||||||
#include "history/view/media/history_view_theme_document.h"
|
#include "history/view/media/history_view_theme_document.h"
|
||||||
|
#include "history/history_item.h"
|
||||||
|
#include "history/history.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
#include "mainwindow.h"
|
||||||
#include "media/streaming/media_streaming_utility.h"
|
#include "media/streaming/media_streaming_utility.h"
|
||||||
|
#include "payments/payments_checkout_process.h"
|
||||||
|
#include "payments/payments_non_panel_process.h"
|
||||||
|
#include "window/window_session_controller.h"
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
|
@ -180,4 +191,34 @@ QSize CountPhotoMediaSize(
|
||||||
media.scaled(media.width(), newWidth, Qt::KeepAspectRatio));
|
media.scaled(media.width(), newWidth, Qt::KeepAspectRatio));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ClickHandlerPtr MakePaidMediaLink(not_null<HistoryItem*> item) {
|
||||||
|
return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
|
||||||
|
const auto my = context.other.value<ClickHandlerContext>();
|
||||||
|
const auto controller = my.sessionWindow.get();
|
||||||
|
const auto itemId = item->fullId();
|
||||||
|
const auto session = &item->history()->session();
|
||||||
|
using Result = Payments::CheckoutResult;
|
||||||
|
const auto done = crl::guard(session, [=](Result result) {
|
||||||
|
if (result != Result::Paid) {
|
||||||
|
return;
|
||||||
|
} else if (const auto item = session->data().message(itemId)) {
|
||||||
|
session->api().views().pollExtendedMedia(item, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Payments::CheckoutProcess::Start(
|
||||||
|
item,
|
||||||
|
Payments::Mode::Payment,
|
||||||
|
(controller
|
||||||
|
? crl::guard(
|
||||||
|
controller,
|
||||||
|
[=](auto) { controller->widget()->activate(); })
|
||||||
|
: Fn<void(Payments::CheckoutResult)>()),
|
||||||
|
((controller && Payments::IsCreditsInvoice(item))
|
||||||
|
? Payments::ProcessNonPanelPaymentFormFactory(
|
||||||
|
controller,
|
||||||
|
done)
|
||||||
|
: nullptr));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
|
@ -75,4 +75,6 @@ void PaintInterpolatedIcon(
|
||||||
int newWidth,
|
int newWidth,
|
||||||
int maxWidth);
|
int maxWidth);
|
||||||
|
|
||||||
|
[[nodiscard]] ClickHandlerPtr MakePaidMediaLink(not_null<HistoryItem*> item);
|
||||||
|
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
|
@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/history_view_cursor_state.h"
|
#include "history/view/history_view_cursor_state.h"
|
||||||
#include "history/view/media/history_view_media_common.h"
|
#include "history/view/media/history_view_media_common.h"
|
||||||
#include "history/view/media/history_view_media_spoiler.h"
|
#include "history/view/media/history_view_media_spoiler.h"
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
#include "media/streaming/media_streaming_instance.h"
|
#include "media/streaming/media_streaming_instance.h"
|
||||||
#include "media/streaming/media_streaming_player.h"
|
#include "media/streaming/media_streaming_player.h"
|
||||||
#include "media/streaming/media_streaming_document.h"
|
#include "media/streaming/media_streaming_document.h"
|
||||||
|
@ -38,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_web_page.h"
|
#include "data/data_web_page.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
|
#include "styles/style_chat_helpers.h"
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -140,7 +142,8 @@ void Photo::dataMediaCreated() const {
|
||||||
|
|
||||||
if (_data->inlineThumbnailBytes().isEmpty()
|
if (_data->inlineThumbnailBytes().isEmpty()
|
||||||
&& !_dataMedia->image(PhotoSize::Large)
|
&& !_dataMedia->image(PhotoSize::Large)
|
||||||
&& !_dataMedia->image(PhotoSize::Thumbnail)) {
|
&& !_dataMedia->image(PhotoSize::Thumbnail)
|
||||||
|
&& !_data->extendedMediaPreview()) {
|
||||||
_dataMedia->wanted(PhotoSize::Small, _realParent->fullId());
|
_dataMedia->wanted(PhotoSize::Small, _realParent->fullId());
|
||||||
}
|
}
|
||||||
history()->owner().registerHeavyViewPart(_parent);
|
history()->owner().registerHeavyViewPart(_parent);
|
||||||
|
@ -277,8 +280,9 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
|
||||||
_dataMedia->automaticLoad(_realParent->fullId(), _parent->data());
|
_dataMedia->automaticLoad(_realParent->fullId(), _parent->data());
|
||||||
const auto st = context.st;
|
const auto st = context.st;
|
||||||
const auto sti = context.imageStyle();
|
const auto sti = context.imageStyle();
|
||||||
auto loaded = _dataMedia->loaded();
|
const auto preview = _data->extendedMediaPreview();
|
||||||
auto displayLoading = _data->displayLoading();
|
auto loaded = preview || _dataMedia->loaded();
|
||||||
|
auto displayLoading = !preview && _data->displayLoading();
|
||||||
|
|
||||||
auto inWebPage = (_parent->media() != this);
|
auto inWebPage = (_parent->media() != this);
|
||||||
auto paintx = 0, painty = 0, paintw = width(), painth = height();
|
auto paintx = 0, painty = 0, paintw = width(), painth = height();
|
||||||
|
@ -365,6 +369,8 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
|
||||||
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
|
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
|
||||||
_animation->radial.draw(p, rinner, st::msgFileRadialLine, sti->historyFileThumbRadialFg);
|
_animation->radial.draw(p, rinner, st::msgFileRadialLine, sti->historyFileThumbRadialFg);
|
||||||
}
|
}
|
||||||
|
} else if (preview) {
|
||||||
|
paintPriceTag(p, rthumb);
|
||||||
}
|
}
|
||||||
if (showEnlarge) {
|
if (showEnlarge) {
|
||||||
auto hq = PainterHighQualityEnabler(p);
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
@ -397,6 +403,43 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Photo::paintPriceTag(Painter &p, QRect rthumb) const {
|
||||||
|
const auto media = parent()->data()->media();
|
||||||
|
const auto invoice = media ? media->invoice() : nullptr;
|
||||||
|
const auto price = invoice->isPaidMedia ? invoice->amount : 0;
|
||||||
|
if (!price) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto text = Ui::Text::String();
|
||||||
|
text.setText(
|
||||||
|
st::semiboldTextStyle,
|
||||||
|
tr::lng_paid_price(
|
||||||
|
tr::now,
|
||||||
|
lt_price,
|
||||||
|
QChar(0x2B50) + Lang::FormatCountDecimal(invoice->amount)));
|
||||||
|
const auto width = text.maxWidth();
|
||||||
|
const auto inner = QRect(0, 0, width, text.minHeight());
|
||||||
|
const auto outer = inner.marginsAdded(st::paidTagPadding);
|
||||||
|
const auto size = outer.size();
|
||||||
|
|
||||||
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
p.setBrush(st::toastBg);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
|
||||||
|
const auto radius = std::min(size.width(), size.height()) / 2.;
|
||||||
|
const auto rect = QRect(
|
||||||
|
rthumb.x() + (rthumb.width() - size.width()) / 2,
|
||||||
|
rthumb.y() + (rthumb.height() - size.height()) / 2,
|
||||||
|
size.width(),
|
||||||
|
size.height());
|
||||||
|
|
||||||
|
p.drawRoundedRect(rect, radius, radius);
|
||||||
|
p.setPen(st::toastFg);
|
||||||
|
|
||||||
|
text.draw(p, rect.x() - outer.x(), rect.y() - outer.y(), width);
|
||||||
|
}
|
||||||
|
|
||||||
void Photo::validateUserpicImageCache(QSize size, bool forum) const {
|
void Photo::validateUserpicImageCache(QSize size, bool forum) const {
|
||||||
const auto forumValue = forum ? 1 : 0;
|
const auto forumValue = forum ? 1 : 0;
|
||||||
const auto large = _dataMedia->image(PhotoSize::Large);
|
const auto large = _dataMedia->image(PhotoSize::Large);
|
||||||
|
@ -604,6 +647,14 @@ QRect Photo::enlargeRect() const {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ClickHandlerPtr Photo::ensureExtendedMediaLink() const {
|
||||||
|
const auto item = parent()->data();
|
||||||
|
if (!_extendedMediaLink && item->isRegular()) {
|
||||||
|
_extendedMediaLink = MakePaidMediaLink(item);
|
||||||
|
}
|
||||||
|
return _extendedMediaLink;
|
||||||
|
}
|
||||||
|
|
||||||
TextState Photo::textState(QPoint point, StateRequest request) const {
|
TextState Photo::textState(QPoint point, StateRequest request) const {
|
||||||
auto result = TextState(_parent);
|
auto result = TextState(_parent);
|
||||||
|
|
||||||
|
@ -617,7 +668,9 @@ TextState Photo::textState(QPoint point, StateRequest request) const {
|
||||||
|
|
||||||
if (QRect(paintx, painty, paintw, painth).contains(point)) {
|
if (QRect(paintx, painty, paintw, painth).contains(point)) {
|
||||||
ensureDataMediaCreated();
|
ensureDataMediaCreated();
|
||||||
result.link = (_spoiler && !_spoiler->revealed)
|
result.link = _data->extendedMediaPreview()
|
||||||
|
? ensureExtendedMediaLink()
|
||||||
|
: (_spoiler && !_spoiler->revealed)
|
||||||
? _spoiler->link
|
? _spoiler->link
|
||||||
: _data->uploading()
|
: _data->uploading()
|
||||||
? _cancell
|
? _cancell
|
||||||
|
|
|
@ -147,10 +147,13 @@ private:
|
||||||
[[nodiscard]] QSize photoSize() const;
|
[[nodiscard]] QSize photoSize() const;
|
||||||
[[nodiscard]] QRect enlargeRect() const;
|
[[nodiscard]] QRect enlargeRect() const;
|
||||||
|
|
||||||
|
void paintPriceTag(Painter &p, QRect rthumb) const;
|
||||||
|
[[nodiscard]] ClickHandlerPtr ensureExtendedMediaLink() const;
|
||||||
void togglePollingStory(bool enabled) const;
|
void togglePollingStory(bool enabled) const;
|
||||||
|
|
||||||
const not_null<PhotoData*> _data;
|
const not_null<PhotoData*> _data;
|
||||||
const FullStoryId _storyId;
|
const FullStoryId _storyId;
|
||||||
|
mutable ClickHandlerPtr _extendedMediaLink;
|
||||||
mutable std::shared_ptr<Data::PhotoMedia> _dataMedia;
|
mutable std::shared_ptr<Data::PhotoMedia> _dataMedia;
|
||||||
mutable std::unique_ptr<Streamed> _streamed;
|
mutable std::unique_ptr<Streamed> _streamed;
|
||||||
const std::unique_ptr<MediaSpoiler> _spoiler;
|
const std::unique_ptr<MediaSpoiler> _spoiler;
|
||||||
|
|
|
@ -638,6 +638,7 @@ storiesEmojiPan: EmojiPan(defaultEmojiPan) {
|
||||||
menuSpoilerOff: icon {{ "menu/spoiler_off", storiesComposeWhiteText }};
|
menuSpoilerOff: icon {{ "menu/spoiler_off", storiesComposeWhiteText }};
|
||||||
menuBelow: icon {{ "menu/link_below", storiesComposeWhiteText }};
|
menuBelow: icon {{ "menu/link_below", storiesComposeWhiteText }};
|
||||||
menuAbove: icon {{ "menu/link_above", storiesComposeWhiteText }};
|
menuAbove: icon {{ "menu/link_above", storiesComposeWhiteText }};
|
||||||
|
menuPrice: icon {{ "menu/earn", storiesComposeWhiteText }};
|
||||||
|
|
||||||
stripBubble: icon{
|
stripBubble: icon{
|
||||||
{ "chat/reactions_bubble_shadow", windowShadowFg },
|
{ "chat/reactions_bubble_shadow", windowShadowFg },
|
||||||
|
|
|
@ -678,6 +678,14 @@ FillMenuResult FillSendMenu(
|
||||||
}, details); },
|
}, details); },
|
||||||
above ? &icons.menuBelow : &icons.menuAbove);
|
above ? &icons.menuBelow : &icons.menuAbove);
|
||||||
}
|
}
|
||||||
|
if (details.price) {
|
||||||
|
menu->addAction(
|
||||||
|
((*details.price > 0)
|
||||||
|
? tr::lng_context_change_price(tr::now)
|
||||||
|
: tr::lng_context_make_paid(tr::now)),
|
||||||
|
[=] { action({ .type = ActionType::ChangePrice }, details); },
|
||||||
|
&icons.menuPrice);
|
||||||
|
}
|
||||||
|
|
||||||
using namespace HistoryView::Reactions;
|
using namespace HistoryView::Reactions;
|
||||||
const auto effect = std::make_shared<QPointer<EffectPreview>>();
|
const auto effect = std::make_shared<QPointer<EffectPreview>>();
|
||||||
|
|
|
@ -53,6 +53,7 @@ struct Details {
|
||||||
Type type = Type::Disabled;
|
Type type = Type::Disabled;
|
||||||
SpoilerState spoiler = SpoilerState::None;
|
SpoilerState spoiler = SpoilerState::None;
|
||||||
CaptionState caption = CaptionState::None;
|
CaptionState caption = CaptionState::None;
|
||||||
|
std::optional<uint64> price;
|
||||||
bool effectAllowed = false;
|
bool effectAllowed = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -69,6 +70,7 @@ enum class ActionType : uchar {
|
||||||
SpoilerOff,
|
SpoilerOff,
|
||||||
CaptionUp,
|
CaptionUp,
|
||||||
CaptionDown,
|
CaptionDown,
|
||||||
|
ChangePrice,
|
||||||
};
|
};
|
||||||
struct Action {
|
struct Action {
|
||||||
using Type = ActionType;
|
using Type = ActionType;
|
||||||
|
|
|
@ -27,7 +27,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
|
|
||||||
namespace Payments {
|
namespace Payments {
|
||||||
namespace {
|
|
||||||
|
|
||||||
bool IsCreditsInvoice(not_null<HistoryItem*> item) {
|
bool IsCreditsInvoice(not_null<HistoryItem*> item) {
|
||||||
if (const auto payment = item->Get<HistoryServicePayment>()) {
|
if (const auto payment = item->Get<HistoryServicePayment>()) {
|
||||||
|
@ -38,8 +37,6 @@ bool IsCreditsInvoice(not_null<HistoryItem*> item) {
|
||||||
return invoice && (invoice->currency == Ui::kCreditsCurrency);
|
return invoice && (invoice->currency == Ui::kCreditsCurrency);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
Fn<void(NonPanelPaymentForm)> ProcessNonPanelPaymentFormFactory(
|
Fn<void(NonPanelPaymentForm)> ProcessNonPanelPaymentFormFactory(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
Fn<void(CheckoutResult)> maybeReturnToBot) {
|
Fn<void(CheckoutResult)> maybeReturnToBot) {
|
||||||
|
|
|
@ -18,6 +18,8 @@ namespace Payments {
|
||||||
enum class CheckoutResult;
|
enum class CheckoutResult;
|
||||||
struct NonPanelPaymentForm;
|
struct NonPanelPaymentForm;
|
||||||
|
|
||||||
|
[[nodiscard]] bool IsCreditsInvoice(not_null<HistoryItem*> item);
|
||||||
|
|
||||||
Fn<void(NonPanelPaymentForm)> ProcessNonPanelPaymentFormFactory(
|
Fn<void(NonPanelPaymentForm)> ProcessNonPanelPaymentFormFactory(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
Fn<void(Payments::CheckoutResult)> maybeReturnToBot = nullptr);
|
Fn<void(Payments::CheckoutResult)> maybeReturnToBot = nullptr);
|
||||||
|
|
|
@ -32,9 +32,11 @@ constexpr auto kMinPreviewWidth = 20;
|
||||||
AbstractSingleMediaPreview::AbstractSingleMediaPreview(
|
AbstractSingleMediaPreview::AbstractSingleMediaPreview(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
const style::ComposeControls &st,
|
const style::ComposeControls &st,
|
||||||
AttachControls::Type type)
|
AttachControls::Type type,
|
||||||
|
Fn<bool()> canToggleSpoiler)
|
||||||
: AbstractSinglePreview(parent)
|
: AbstractSinglePreview(parent)
|
||||||
, _st(st)
|
, _st(st)
|
||||||
|
, _canToggleSpoiler(std::move(canToggleSpoiler))
|
||||||
, _minThumbH(st::sendBoxAlbumGroupSize.height()
|
, _minThumbH(st::sendBoxAlbumGroupSize.height()
|
||||||
+ st::sendBoxAlbumGroupSkipTop * 2)
|
+ st::sendBoxAlbumGroupSkipTop * 2)
|
||||||
, _controls(base::make_unique_q<AttachControlsWidget>(this, type)) {
|
, _controls(base::make_unique_q<AttachControlsWidget>(this, type)) {
|
||||||
|
@ -266,7 +268,9 @@ void AbstractSingleMediaPreview::applyCursor(style::cursor cursor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbstractSingleMediaPreview::showContextMenu(QPoint position) {
|
void AbstractSingleMediaPreview::showContextMenu(QPoint position) {
|
||||||
if (!_sendWay.sendImagesAsPhotos() || !supportsSpoilers()) {
|
if (!_canToggleSpoiler()
|
||||||
|
|| !_sendWay.sendImagesAsPhotos()
|
||||||
|
|| !supportsSpoilers()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_menu = base::make_unique_q<Ui::PopupMenu>(
|
_menu = base::make_unique_q<Ui::PopupMenu>(
|
||||||
|
|
|
@ -26,7 +26,8 @@ public:
|
||||||
AbstractSingleMediaPreview(
|
AbstractSingleMediaPreview(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
const style::ComposeControls &st,
|
const style::ComposeControls &st,
|
||||||
AttachControls::Type type);
|
AttachControls::Type type,
|
||||||
|
Fn<bool()> canToggleSpoiler);
|
||||||
~AbstractSingleMediaPreview();
|
~AbstractSingleMediaPreview();
|
||||||
|
|
||||||
void setSendWay(SendFilesWay way);
|
void setSendWay(SendFilesWay way);
|
||||||
|
@ -71,6 +72,7 @@ private:
|
||||||
|
|
||||||
const style::ComposeControls &_st;
|
const style::ComposeControls &_st;
|
||||||
SendFilesWay _sendWay;
|
SendFilesWay _sendWay;
|
||||||
|
Fn<bool()> _canToggleSpoiler;
|
||||||
bool _animated = false;
|
bool _animated = false;
|
||||||
QPixmap _preview;
|
QPixmap _preview;
|
||||||
QPixmap _previewBlurred;
|
QPixmap _previewBlurred;
|
||||||
|
|
|
@ -31,10 +31,12 @@ AlbumPreview::AlbumPreview(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
const style::ComposeControls &st,
|
const style::ComposeControls &st,
|
||||||
gsl::span<Ui::PreparedFile> items,
|
gsl::span<Ui::PreparedFile> items,
|
||||||
SendFilesWay way)
|
SendFilesWay way,
|
||||||
|
Fn<bool()> canToggleSpoiler)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
, _st(st)
|
, _st(st)
|
||||||
, _sendWay(way)
|
, _sendWay(way)
|
||||||
|
, _canToggleSpoiler(std::move(canToggleSpoiler))
|
||||||
, _dragTimer([=] { switchToDrag(); }) {
|
, _dragTimer([=] { switchToDrag(); }) {
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
prepareThumbs(items);
|
prepareThumbs(items);
|
||||||
|
@ -573,7 +575,7 @@ void AlbumPreview::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
void AlbumPreview::showContextMenu(
|
void AlbumPreview::showContextMenu(
|
||||||
not_null<AlbumThumbnail*> thumb,
|
not_null<AlbumThumbnail*> thumb,
|
||||||
QPoint position) {
|
QPoint position) {
|
||||||
if (!_sendWay.sendImagesAsPhotos()) {
|
if (!_canToggleSpoiler() || !_sendWay.sendImagesAsPhotos()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_menu = base::make_unique_q<Ui::PopupMenu>(
|
_menu = base::make_unique_q<Ui::PopupMenu>(
|
||||||
|
|
|
@ -28,7 +28,8 @@ public:
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
const style::ComposeControls &st,
|
const style::ComposeControls &st,
|
||||||
gsl::span<Ui::PreparedFile> items,
|
gsl::span<Ui::PreparedFile> items,
|
||||||
SendFilesWay way);
|
SendFilesWay way,
|
||||||
|
Fn<bool()> canToggleSpoiler);
|
||||||
~AlbumPreview();
|
~AlbumPreview();
|
||||||
|
|
||||||
void setSendWay(SendFilesWay way);
|
void setSendWay(SendFilesWay way);
|
||||||
|
@ -92,6 +93,7 @@ private:
|
||||||
|
|
||||||
const style::ComposeControls &_st;
|
const style::ComposeControls &_st;
|
||||||
SendFilesWay _sendWay;
|
SendFilesWay _sendWay;
|
||||||
|
Fn<bool()> _canToggleSpoiler;
|
||||||
style::cursor _cursor = style::cur_default;
|
style::cursor _cursor = style::cur_default;
|
||||||
std::vector<int> _order;
|
std::vector<int> _order;
|
||||||
std::vector<QSize> _itemsShownDimensions;
|
std::vector<QSize> _itemsShownDimensions;
|
||||||
|
|
|
@ -36,7 +36,7 @@ ItemSingleMediaPreview::ItemSingleMediaPreview(
|
||||||
Fn<bool()> gifPaused,
|
Fn<bool()> gifPaused,
|
||||||
not_null<HistoryItem*> item,
|
not_null<HistoryItem*> item,
|
||||||
AttachControls::Type type)
|
AttachControls::Type type)
|
||||||
: AbstractSingleMediaPreview(parent, st, type)
|
: AbstractSingleMediaPreview(parent, st, type, [] { return true; })
|
||||||
, _gifPaused(std::move(gifPaused))
|
, _gifPaused(std::move(gifPaused))
|
||||||
, _fullId(item->fullId()) {
|
, _fullId(item->fullId()) {
|
||||||
const auto media = item->media();
|
const auto media = item->media();
|
||||||
|
|
|
@ -195,6 +195,10 @@ bool PreparedList::canMoveCaption(bool sendingAlbum, bool compress) const {
|
||||||
|| (file.type == PreparedFile::Type::Photo && compress);
|
|| (file.type == PreparedFile::Type::Photo && compress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PreparedList::canChangePrice(bool sendingAlbum, bool compress) const {
|
||||||
|
return canMoveCaption(sendingAlbum, compress);
|
||||||
|
}
|
||||||
|
|
||||||
bool PreparedList::hasGroupOption(bool slowmode) const {
|
bool PreparedList::hasGroupOption(bool slowmode) const {
|
||||||
if (slowmode || files.size() < 2) {
|
if (slowmode || files.size() < 2) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -115,6 +115,9 @@ struct PreparedList {
|
||||||
[[nodiscard]] bool canMoveCaption(
|
[[nodiscard]] bool canMoveCaption(
|
||||||
bool sendingAlbum,
|
bool sendingAlbum,
|
||||||
bool compress) const;
|
bool compress) const;
|
||||||
|
[[nodiscard]] bool canChangePrice(
|
||||||
|
bool sendingAlbum,
|
||||||
|
bool compress) const;
|
||||||
[[nodiscard]] bool canBeSentInSlowmode() const;
|
[[nodiscard]] bool canBeSentInSlowmode() const;
|
||||||
[[nodiscard]] bool canBeSentInSlowmodeWith(
|
[[nodiscard]] bool canBeSentInSlowmodeWith(
|
||||||
const PreparedList &other) const;
|
const PreparedList &other) const;
|
||||||
|
|
|
@ -19,6 +19,7 @@ SingleMediaPreview *SingleMediaPreview::Create(
|
||||||
const style::ComposeControls &st,
|
const style::ComposeControls &st,
|
||||||
Fn<bool()> gifPaused,
|
Fn<bool()> gifPaused,
|
||||||
const PreparedFile &file,
|
const PreparedFile &file,
|
||||||
|
Fn<bool()> canToggleSpoiler,
|
||||||
AttachControls::Type type) {
|
AttachControls::Type type) {
|
||||||
auto preview = QImage();
|
auto preview = QImage();
|
||||||
auto animated = false;
|
auto animated = false;
|
||||||
|
@ -51,7 +52,8 @@ SingleMediaPreview *SingleMediaPreview::Create(
|
||||||
Core::IsMimeSticker(file.information->filemime),
|
Core::IsMimeSticker(file.information->filemime),
|
||||||
file.spoiler,
|
file.spoiler,
|
||||||
animationPreview ? file.path : QString(),
|
animationPreview ? file.path : QString(),
|
||||||
type);
|
type,
|
||||||
|
std::move(canToggleSpoiler));
|
||||||
}
|
}
|
||||||
|
|
||||||
SingleMediaPreview::SingleMediaPreview(
|
SingleMediaPreview::SingleMediaPreview(
|
||||||
|
@ -63,8 +65,9 @@ SingleMediaPreview::SingleMediaPreview(
|
||||||
bool sticker,
|
bool sticker,
|
||||||
bool spoiler,
|
bool spoiler,
|
||||||
const QString &animatedPreviewPath,
|
const QString &animatedPreviewPath,
|
||||||
AttachControls::Type type)
|
AttachControls::Type type,
|
||||||
: AbstractSingleMediaPreview(parent, st, type)
|
Fn<bool()> canToggleSpoiler)
|
||||||
|
: AbstractSingleMediaPreview(parent, st, type, std::move(canToggleSpoiler))
|
||||||
, _gifPaused(std::move(gifPaused))
|
, _gifPaused(std::move(gifPaused))
|
||||||
, _sticker(sticker) {
|
, _sticker(sticker) {
|
||||||
Expects(!preview.isNull());
|
Expects(!preview.isNull());
|
||||||
|
|
|
@ -25,6 +25,7 @@ public:
|
||||||
const style::ComposeControls &st,
|
const style::ComposeControls &st,
|
||||||
Fn<bool()> gifPaused,
|
Fn<bool()> gifPaused,
|
||||||
const PreparedFile &file,
|
const PreparedFile &file,
|
||||||
|
Fn<bool()> canToggleSpoiler,
|
||||||
AttachControls::Type type = AttachControls::Type::Full);
|
AttachControls::Type type = AttachControls::Type::Full);
|
||||||
|
|
||||||
SingleMediaPreview(
|
SingleMediaPreview(
|
||||||
|
@ -36,7 +37,8 @@ public:
|
||||||
bool sticker,
|
bool sticker,
|
||||||
bool spoiler,
|
bool spoiler,
|
||||||
const QString &animatedPreviewPath,
|
const QString &animatedPreviewPath,
|
||||||
AttachControls::Type type);
|
AttachControls::Type type,
|
||||||
|
Fn<bool()> canToggleSpoiler);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool supportsSpoilers() const override;
|
bool supportsSpoilers() const override;
|
||||||
|
|
Loading…
Add table
Reference in a new issue