Ask for boosts to unlock group restrictions.

This commit is contained in:
John Preston 2025-01-02 23:52:49 +04:00
parent a7ae7a8cda
commit 5f10c1875c
38 changed files with 587 additions and 389 deletions

View file

@ -3267,13 +3267,13 @@ void ApiWrap::finishForwarding(const SendAction &action) {
const auto topicRootId = action.replyTo.topicRootId;
auto toForward = history->resolveForwardDraft(topicRootId);
if (!toForward.items.empty()) {
const auto error = GetErrorTextForSending(
const auto error = GetErrorForSending(
history->peer,
{
.topicRootId = topicRootId,
.forward = &toForward.items,
});
if (!error.isEmpty()) {
if (error) {
return;
}

View file

@ -26,7 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h"
#include "data/stickers/data_custom_emoji.h"
#include "history/history.h"
#include "history/history_item_helpers.h" // GetErrorTextForSending.
#include "history/history_item_helpers.h" // GetErrorForSending.
#include "history/view/history_view_group_call_bar.h" // GenerateUserpics...
#include "lang/lang_keys.h"
#include "main/main_session.h"
@ -1492,27 +1492,14 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
return;
}
const auto error = [&] {
for (const auto thread : result) {
const auto error = GetErrorTextForSending(
thread,
{ .text = &comment });
if (!error.isEmpty()) {
return std::make_pair(error, thread);
}
}
return std::make_pair(QString(), result.front());
}();
if (!error.first.isEmpty()) {
auto text = TextWithEntities();
if (result.size() > 1) {
text.append(
Ui::Text::Bold(error.second->chatListName())
).append("\n\n");
}
text.append(error.first);
const auto errorWithThread = GetErrorForSending(
result,
{ .text = &comment });
if (errorWithThread.error) {
if (*box) {
(*box)->uiShow()->showBox(Ui::MakeInformBox(text));
(*box)->uiShow()->showBox(MakeSendErrorBox(
errorWithThread,
result.size() > 1));
}
return;
}

View file

@ -220,7 +220,7 @@ SendFilesCheck DefaultCheckForPeer(
}
SendFilesCheck DefaultCheckForPeer(
std::shared_ptr<Ui::Show> show,
std::shared_ptr<ChatHelpers::Show> show,
not_null<PeerData*> peer) {
return [=](
const Ui::PreparedFile &file,
@ -228,7 +228,7 @@ SendFilesCheck DefaultCheckForPeer(
bool silent) {
const auto error = Data::FileRestrictionError(peer, file, compress);
if (error && !silent) {
show->showToast(*error);
Data::ShowSendErrorToast(show, peer, error);
}
return !error.has_value();
};

View file

@ -80,7 +80,7 @@ using SendFilesCheck = Fn<bool(
not_null<Window::SessionController*> controller,
not_null<PeerData*> peer);
[[nodiscard]] SendFilesCheck DefaultCheckForPeer(
std::shared_ptr<Ui::Show> show,
std::shared_ptr<ChatHelpers::Show> show,
not_null<PeerData*> peer);
using SendFilesConfirmed = Fn<void(

View file

@ -1508,26 +1508,11 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
return;
}
const auto error = [&] {
for (const auto thread : result) {
const auto error = GetErrorTextForSending(
thread,
{ .forward = &items, .text = &comment });
if (!error.isEmpty()) {
return std::make_pair(error, thread);
}
}
return std::make_pair(QString(), result.front());
}();
if (!error.first.isEmpty()) {
auto text = TextWithEntities();
if (result.size() > 1) {
text.append(
Ui::Text::Bold(error.second->chatListName())
).append("\n\n");
}
text.append(error.first);
show->showBox(Ui::MakeInformBox(text));
const auto error = GetErrorForSending(
result,
{ .forward = &items, .text = &comment });
if (error.error) {
show->showBox(MakeSendErrorBox(error, result.size() > 1));
return;
}
@ -1737,30 +1722,13 @@ void FastShareLink(
return;
}
const auto error = [&] {
for (const auto thread : result) {
const auto error = GetErrorTextForSending(
thread,
{ .text = &comment });
if (!error.isEmpty()) {
return std::make_pair(error, thread);
}
}
return std::make_pair(QString(), result.front());
}();
if (!error.first.isEmpty()) {
auto text = TextWithEntities();
if (result.size() > 1) {
text.append(
Ui::Text::Bold(error.second->chatListName())
).append("\n\n");
}
text.append(error.first);
const auto error = GetErrorForSending(
result,
{ .text = &comment });
if (error.error) {
if (const auto weak = *box) {
weak->getDelegate()->show(Ui::MakeConfirmBox({
.text = text,
.inform = true,
}));
weak->getDelegate()->show(
MakeSendErrorBox(error, result.size() > 1));
}
return;
}

View file

@ -23,7 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "boxes/share_box.h"
#include "history/view/history_view_schedule_box.h"
#include "history/history_item_helpers.h" // GetErrorTextForSending.
#include "history/history_item_helpers.h" // GetErrorForSending.
#include "history/history.h"
#include "data/data_histories.h"
#include "data/data_session.h"
@ -139,30 +139,13 @@ object_ptr<ShareBox> ShareInviteLinkBox(
return;
}
const auto error = [&] {
for (const auto thread : result) {
const auto error = GetErrorTextForSending(
thread,
{ .text = &comment });
if (!error.isEmpty()) {
return std::make_pair(error, thread);
}
}
return std::make_pair(QString(), result.front());
}();
if (!error.first.isEmpty()) {
auto text = TextWithEntities();
if (result.size() > 1) {
text.append(
Ui::Text::Bold(error.second->chatListName())
).append("\n\n");
}
text.append(error.first);
const auto error = GetErrorForSending(
result,
{ .text = &comment });
if (error.error) {
if (const auto weak = *box) {
weak->getDelegate()->show(ConfirmBox({
.text = text,
.inform = true,
}));
weak->getDelegate()->show(
MakeSendErrorBox(error, result.size() > 1));
}
return;
}

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/gifs_list_widget.h"
#include "menu/menu_send.h"
#include "ui/controls/tabbed_search.h"
#include "ui/text/text_utilities.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/shadow.h"
@ -1042,23 +1043,39 @@ void TabbedSelector::checkRestrictedPeer() {
? Data::RestrictionError(
_currentPeer,
ChatRestriction::SendOther)
: std::nullopt)
: std::nullopt;
if (error) {
if (!_restrictedLabel) {
_restrictedLabel.create(
this,
*error,
st::stickersRestrictedLabel);
_restrictedLabel->show();
updateRestrictedLabelGeometry();
currentTab()->footer()->hide();
_scroll->hide();
_bottomShadow->hide();
update();
}
: Data::SendError())
: Data::SendError();
const auto changed = (_restrictedLabelKey != error.text);
if (!changed) {
return;
}
_restrictedLabelKey = error.text;
if (error) {
const auto show = _show;
const auto peer = _currentPeer;
_restrictedLabel.create(
this,
rpl::single(error.boostsToLift
? Ui::Text::Link(error.text)
: TextWithEntities{ error.text }),
st::stickersRestrictedLabel);
const auto lifting = error.boostsToLift;
_restrictedLabel->setClickHandlerFilter([=](auto...) {
const auto window = show->resolveWindow(
ChatHelpers::WindowUsage::PremiumPromo);
window->resolveBoostState(peer->asChannel(), lifting);
return false;
});
_restrictedLabel->show();
updateRestrictedLabelGeometry();
currentTab()->footer()->hide();
_scroll->hide();
_bottomShadow->hide();
update();
return;
}
} else {
_restrictedLabelKey = QString();
}
if (_restrictedLabel) {
_restrictedLabel.destroy();

View file

@ -309,6 +309,7 @@ private:
object_ptr<Ui::PlainShadow> _bottomShadow;
object_ptr<Ui::ScrollArea> _scroll;
object_ptr<Ui::FlatLabel> _restrictedLabel = { nullptr };
QString _restrictedLabelKey;
std::vector<Tab> _tabs;
SelectorTab _currentTabType = SelectorTab::Emoji;

View file

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unixtime.h"
#include "boxes/peers/edit_peer_permissions_box.h"
#include "chat_helpers/compose/compose_show.h"
#include "data/data_chat.h"
#include "data/data_channel.h"
#include "data/data_forum_topic.h"
@ -17,6 +18,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "ui/chat/attach/attach_prepare.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "window/window_session_controller.h"
namespace {
@ -167,7 +171,7 @@ bool CanSendAnyOf(
Unexpected("Peer type in CanSendAnyOf.");
}
std::optional<QString> RestrictionError(
SendError RestrictionError(
not_null<PeerData*> peer,
ChatRestriction restriction) {
using Flag = ChatRestriction;
@ -175,10 +179,13 @@ std::optional<QString> RestrictionError(
if (const auto user = peer->asUser()) {
if (user->meRequiresPremiumToWrite()
&& !user->session().premium()) {
return tr::lng_restricted_send_non_premium(
tr::now,
lt_user,
user->shortName());
return SendError({
.text = tr::lng_restricted_send_non_premium(
tr::now,
lt_user,
user->shortName()),
.premiumToLift = true,
});
}
const auto result = (restriction == Flag::SendVoiceMessages)
? tr::lng_restricted_send_voice_messages(
@ -194,7 +201,7 @@ std::optional<QString> RestrictionError(
? u"can't send polls :("_q
: (restriction == Flag::PinMessages)
? u"can't pin :("_q
: std::optional<QString>();
: SendError();
Ensures(result.has_value());
return result;
@ -253,6 +260,15 @@ std::optional<QString> RestrictionError(
Unexpected("Restriction in Data::RestrictionErrorKey.");
}
}
if (all
&& channel->boostsUnrestrict()
&& !channel->unrestrictedByBoosts()) {
return SendError({
.text = tr::lng_restricted_boost_group(tr::now),
.boostsToLift = (channel->boostsUnrestrict()
- channel->boostsApplied()),
});
}
switch (restriction) {
case Flag::SendPolls:
return all
@ -302,10 +318,10 @@ std::optional<QString> RestrictionError(
}
Unexpected("Restriction in Data::RestrictionErrorKey.");
}
return std::nullopt;
return SendError();
}
std::optional<QString> AnyFileRestrictionError(not_null<PeerData*> peer) {
SendError AnyFileRestrictionError(not_null<PeerData*> peer) {
using Restriction = ChatRestriction;
for (const auto right : FilesSendRestrictionsList()) {
if (!RestrictionError(peer, right)) {
@ -315,7 +331,7 @@ std::optional<QString> AnyFileRestrictionError(not_null<PeerData*> peer) {
return RestrictionError(peer, Restriction::SendFiles);
}
std::optional<QString> FileRestrictionError(
SendError FileRestrictionError(
not_null<PeerData*> peer,
const Ui::PreparedList &list,
std::optional<bool> compress) {
@ -339,7 +355,7 @@ std::optional<QString> FileRestrictionError(
return {};
}
std::optional<QString> FileRestrictionError(
SendError FileRestrictionError(
not_null<PeerData*> peer,
const Ui::PreparedFile &file,
std::optional<bool> compress) {
@ -383,4 +399,32 @@ std::optional<QString> FileRestrictionError(
return {};
}
void ShowSendErrorToast(
not_null<Window::SessionNavigation*> navigation,
not_null<PeerData*> peer,
Data::SendError error) {
return ShowSendErrorToast(navigation->uiShow(), peer, error);
}
void ShowSendErrorToast(
std::shared_ptr<ChatHelpers::Show> show,
not_null<PeerData*> peer,
Data::SendError error) {
Expects(peer->isChannel());
if (!error.boostsToLift) {
show->showToast(*error);
return;
}
const auto boost = [=] {
const auto window = show->resolveWindow(
ChatHelpers::WindowUsage::PremiumPromo);
window->resolveBoostState(peer->asChannel(), error.boostsToLift);
};
show->showToast({
.text = Ui::Text::Link(*error),
.filter = [=](const auto &...) { boost(); return false; },
});
}
} // namespace Data

View file

@ -7,11 +7,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace ChatHelpers {
class Show;
} // namespace ChatHelpers
namespace Ui {
struct PreparedList;
struct PreparedFile;
} // namespace Ui
namespace Window {
class SessionNavigation;
} // namespace Window
enum class ChatAdminRight {
ChangeInfo = (1 << 0),
PostMessages = (1 << 1),
@ -175,18 +183,65 @@ struct RestrictionsSetOptions {
return CanSendAnyOf(peer, AllSendRestrictions(), forbidInForums);
}
[[nodiscard]] std::optional<QString> RestrictionError(
struct SendError {
SendError(QString text = QString()) : text(std::move(text)) {
}
struct Args {
QString text;
int boostsToLift = 0;
bool premiumToLift = false;
};
SendError(Args &&args)
: text(std::move(args.text))
, boostsToLift(args.boostsToLift)
, premiumToLift(args.premiumToLift) {
}
QString text;
int boostsToLift = 0;
bool premiumToLift = false;
[[nodiscard]] SendError value_or(SendError other) const {
return *this ? *this : other;
}
explicit operator bool() const {
return !text.isEmpty();
}
[[nodiscard]] bool has_value() const {
return !text.isEmpty();
}
[[nodiscard]] const QString &operator*() const {
return text;
}
};
struct SendErrorWithThread {
SendError error;
Thread *thread = nullptr;
};
[[nodiscard]] SendError RestrictionError(
not_null<PeerData*> peer,
ChatRestriction restriction);
[[nodiscard]] std::optional<QString> AnyFileRestrictionError(
not_null<PeerData*> peer);
[[nodiscard]] std::optional<QString> FileRestrictionError(
[[nodiscard]] SendError AnyFileRestrictionError(not_null<PeerData*> peer);
[[nodiscard]] SendError FileRestrictionError(
not_null<PeerData*> peer,
const Ui::PreparedList &list,
std::optional<bool> compress);
[[nodiscard]] std::optional<QString> FileRestrictionError(
[[nodiscard]] SendError FileRestrictionError(
not_null<PeerData*> peer,
const Ui::PreparedFile &file,
std::optional<bool> compress);
void ShowSendErrorToast(
not_null<Window::SessionNavigation*> navigation,
not_null<PeerData*> peer,
SendError error);
void ShowSendErrorToast(
std::shared_ptr<ChatHelpers::Show> show,
not_null<PeerData*> peer,
SendError error);
} // namespace Data

View file

@ -423,7 +423,7 @@ bool Story::hasDirectLink() const {
return !_peer->username().isEmpty();
}
std::optional<QString> Story::errorTextForForward(
Data::SendError Story::errorTextForForward(
not_null<Thread*> to) const {
const auto peer = to->peer();
const auto holdsPhoto = v::is<not_null<PhotoData*>>(_media.data);
@ -433,10 +433,10 @@ std::optional<QString> Story::errorTextForForward(
const auto second = holdsPhoto
? ChatRestriction::SendVideos
: ChatRestriction::SendPhotos;
if (const auto error = Data::RestrictionError(peer, first)) {
return *error;
} else if (const auto error = Data::RestrictionError(peer, second)) {
return *error;
if (const auto one = Data::RestrictionError(peer, first)) {
return one;
} else if (const auto two = Data::RestrictionError(peer, second)) {
return two;
} else if (!Data::CanSend(to, first, false)
|| !Data::CanSend(to, second, false)) {
return tr::lng_forward_cant(tr::now);

View file

@ -24,6 +24,7 @@ namespace Data {
class Session;
class Thread;
class MediaPreload;
struct SendError;
enum class StoryPrivacy : uchar {
Public,
@ -191,7 +192,7 @@ public:
[[nodiscard]] bool canReport() const;
[[nodiscard]] bool hasDirectLink() const;
[[nodiscard]] std::optional<QString> errorTextForForward(
[[nodiscard]] Data::SendError errorTextForForward(
not_null<Thread*> to) const;
void setCaption(TextWithEntities &&caption);

View file

@ -2479,17 +2479,17 @@ bool HistoryItem::requiresSendInlineRight() const {
return Has<HistoryMessageVia>();
}
std::optional<QString> HistoryItem::errorTextForForward(
Data::SendError HistoryItem::errorTextForForward(
not_null<Data::Thread*> to) const {
const auto requiredRight = requiredSendRight();
const auto requiresInline = requiresSendInlineRight();
const auto peer = to->peer();
constexpr auto kInline = ChatRestriction::SendInline;
if (const auto error = Data::RestrictionError(peer, requiredRight)) {
return *error;
return error;
} else if (requiresInline && !Data::CanSend(to, kInline)) {
return Data::RestrictionError(peer, kInline).value_or(
tr::lng_forward_cant(tr::now));
const auto forInline = Data::RestrictionError(peer, kInline);
return forInline ? forInline : tr::lng_forward_cant(tr::now);
} else if (_media
&& _media->poll()
&& _media->poll()->publicVotes()

View file

@ -68,6 +68,7 @@ struct SponsoredFrom;
class Story;
class SavedSublist;
struct PaidReactionSend;
struct SendError;
} // namespace Data
namespace Main {
@ -442,7 +443,7 @@ public:
[[nodiscard]] bool suggestDeleteAllReport() const;
[[nodiscard]] ChatRestriction requiredSendRight() const;
[[nodiscard]] bool requiresSendInlineRight() const;
[[nodiscard]] std::optional<QString> errorTextForForward(
[[nodiscard]] Data::SendError errorTextForForward(
not_null<Data::Thread*> to) const;
[[nodiscard]] const HistoryMessageTranslation *translation() const;
[[nodiscard]] bool translationShowRequiresCheck(LanguageId to) const;

View file

@ -38,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unixtime.h"
#include "core/application.h"
#include "core/click_handler_types.h" // ClickHandlerContext.
#include "ui/boxes/confirm_box.h"
#include "ui/text/format_values.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
@ -59,7 +60,7 @@ bool PeerCallKnown(not_null<PeerData*> peer) {
} // namespace
QString GetErrorTextForSending(
Data::SendError GetErrorForSending(
not_null<PeerData*> peer,
SendingErrorRequest request) {
const auto forum = request.topicRootId ? peer->forum() : nullptr;
@ -71,13 +72,13 @@ QString GetErrorTextForSending(
: peer->owner().history(peer);
if (request.story) {
if (const auto error = request.story->errorTextForForward(thread)) {
return *error;
return error;
}
}
if (request.forward) {
for (const auto &item : *request.forward) {
if (const auto error = item->errorTextForForward(thread)) {
return *error;
return error;
}
}
}
@ -87,7 +88,7 @@ QString GetErrorTextForSending(
peer,
ChatRestriction::SendOther);
if (error) {
return *error;
return error;
} else if (!Data::CanSendTexts(thread)) {
return tr::lng_forward_cant(tr::now);
}
@ -134,14 +135,58 @@ QString GetErrorTextForSending(
}
}
return QString();
return {};
}
QString GetErrorTextForSending(
Data::SendError GetErrorForSending(
not_null<Data::Thread*> thread,
SendingErrorRequest request) {
request.topicRootId = thread->topicRootId();
return GetErrorTextForSending(thread->peer(), std::move(request));
return GetErrorForSending(thread->peer(), std::move(request));
}
Data::SendErrorWithThread GetErrorForSending(
const std::vector<not_null<Data::Thread*>> &threads,
SendingErrorRequest request) {
for (const auto thread : threads) {
const auto error = GetErrorForSending(thread, request);
if (error) {
return Data::SendErrorWithThread{ error, thread };
}
}
return {};
}
object_ptr<Ui::BoxContent> MakeSendErrorBox(
const Data::SendErrorWithThread &error,
bool withTitle) {
Expects(error.error.has_value() && error.thread != nullptr);
auto text = TextWithEntities();
if (withTitle) {
text.append(
Ui::Text::Bold(error.thread->chatListName())
).append("\n\n");
}
if (error.error.boostsToLift) {
text.append(Ui::Text::Link(error.error.text));
} else {
text.append(error.error.text);
}
const auto peer = error.thread->peer();
const auto lifting = error.error.boostsToLift;
const auto filter = [=](const auto &...) {
Expects(peer->isChannel());
const auto window = ChatHelpers::ResolveWindowDefault()(
&peer->session(),
ChatHelpers::WindowUsage::PremiumPromo);
window->resolveBoostState(peer->asChannel(), lifting);
return false;
};
return Ui::MakeInformBox({
.text = text,
.labelFilter = filter,
});
}
void RequestDependentMessageItem(

View file

@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/object_ptr.h"
class History;
namespace Api {
@ -17,12 +19,18 @@ struct SendAction;
namespace Data {
class Story;
class Thread;
struct SendError;
struct SendErrorWithThread;
} // namespace Data
namespace Main {
class Session;
} // namespace Main
namespace Ui {
class BoxContent;
} // namespace Ui
struct PreparedServiceText {
TextWithEntities text;
std::vector<ClickHandlerPtr> links;
@ -108,13 +116,20 @@ struct SendingErrorRequest {
const TextWithTags *text = nullptr;
bool ignoreSlowmodeCountdown = false;
};
[[nodiscard]] QString GetErrorTextForSending(
[[nodiscard]] Data::SendError GetErrorForSending(
not_null<PeerData*> peer,
SendingErrorRequest request);
[[nodiscard]] QString GetErrorTextForSending(
[[nodiscard]] Data::SendError GetErrorForSending(
not_null<Data::Thread*> thread,
SendingErrorRequest request);
[[nodiscard]] Data::SendErrorWithThread GetErrorForSending(
const std::vector<not_null<Data::Thread*>> &threads,
SendingErrorRequest request);
[[nodiscard]] object_ptr<Ui::BoxContent> MakeSendErrorBox(
const Data::SendErrorWithThread &error,
bool withTitle);
[[nodiscard]] TextWithEntities DropDisallowedCustomEmoji(
not_null<PeerData*> to,
TextWithEntities text);

View file

@ -85,7 +85,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/stickers/data_custom_emoji.h"
#include "history/history.h"
#include "history/history_item.h"
#include "history/history_item_helpers.h" // GetErrorTextForSending.
#include "history/history_item_helpers.h" // GetErrorForSending.
#include "history/history_drag_area.h"
#include "history/history_inner_widget.h"
#include "history/history_item_components.h"
@ -1028,7 +1028,7 @@ void HistoryWidget::refreshTabbedPanel() {
void HistoryWidget::initVoiceRecordBar() {
_voiceRecordBar->setStartRecordingFilter([=] {
const auto error = [&]() -> std::optional<QString> {
const auto error = [&]() -> Data::SendError {
if (_peer) {
if (const auto error = Data::RestrictionError(
_peer,
@ -1036,10 +1036,10 @@ void HistoryWidget::initVoiceRecordBar() {
return error;
}
}
return std::nullopt;
return {};
}();
if (error) {
controller()->showToast(*error);
Data::ShowSendErrorToast(controller(), _peer, error);
return true;
} else if (showSlowmodeError()) {
return true;
@ -4672,7 +4672,7 @@ void HistoryWidget::chooseAttach(
if (!_peer || !_canSendMessages) {
return;
} else if (const auto error = Data::AnyFileRestrictionError(_peer)) {
controller()->showToast(*error);
Data::ShowSendErrorToast(controller(), _peer, error);
return;
} else if (showSlowmodeError()) {
return;
@ -5702,12 +5702,12 @@ bool HistoryWidget::showSendingFilesError(
bool HistoryWidget::showSendingFilesError(
const Ui::PreparedList &list,
std::optional<bool> compress) const {
const auto text = [&] {
const auto error = [&]() -> Data::SendError {
const auto error = _peer
? Data::FileRestrictionError(_peer, list, compress)
: std::nullopt;
if (error) {
return *error;
: Data::SendError();
if (!_peer || error) {
return error;
} else if (const auto left = _peer->slowmodeSecondsLeft()) {
return tr::lng_slowmode_enabled(
tr::now,
@ -5727,15 +5727,15 @@ bool HistoryWidget::showSendingFilesError(
}
return tr::lng_forward_send_files_cant(tr::now);
}();
if (text.isEmpty()) {
if (!error) {
return false;
} else if (text == u"(toolarge)"_q) {
} else if (error.text == u"(toolarge)"_q) {
const auto fileSize = list.files.back().size;
controller()->show(
Box(FileSizeLimitBox, &session(), fileSize, nullptr));
return true;
}
controller()->showToast(text);
Data::ShowSendErrorToast(controller(), _peer, error);
return true;
}
@ -5775,7 +5775,7 @@ bool HistoryWidget::showSendMessageError(
return false;
}
const auto topicRootId = resolveReplyToTopicRootId();
const auto error = GetErrorTextForSending(
const auto error = GetErrorForSending(
_peer,
{
.topicRootId = topicRootId,
@ -5783,10 +5783,10 @@ bool HistoryWidget::showSendMessageError(
.text = &textWithTags,
.ignoreSlowmodeCountdown = ignoreSlowmodeCountdown,
});
if (error.isEmpty()) {
if (!error) {
return false;
}
controller()->showToast(error);
Data::ShowSendErrorToast(controller(), _peer, error);
return true;
}
@ -6286,39 +6286,39 @@ int HistoryWidget::countAutomaticScrollTop() {
return ScrollMax;
}
QString HistoryWidget::computeSendRestriction() const {
if (const auto user = _peer ? _peer->asUser() : nullptr) {
if (user->meRequiresPremiumToWrite()
&& !user->session().premium()) {
return u"premium_required"_q;
}
}
Data::SendError HistoryWidget::computeSendRestriction() const {
const auto allWithoutPolls = Data::AllSendRestrictions()
& ~ChatRestriction::SendPolls;
const auto error = (_peer && !Data::CanSendAnyOf(_peer, allWithoutPolls))
return (_peer && !Data::CanSendAnyOf(_peer, allWithoutPolls))
? Data::RestrictionError(_peer, ChatRestriction::SendOther)
: std::nullopt;
return error ? (u"restriction:"_q + *error) : QString();
: Data::SendError();
}
void HistoryWidget::updateSendRestriction() {
const auto restriction = computeSendRestriction();
if (_sendRestrictionKey == restriction) {
if (_sendRestrictionKey == restriction.text) {
return;
}
_sendRestrictionKey = restriction;
if (restriction.isEmpty()) {
_sendRestrictionKey = restriction.text;
if (!restriction) {
_sendRestriction = nullptr;
} else if (restriction == u"premium_required"_q) {
} else if (restriction.premiumToLift) {
_sendRestriction = PremiumRequiredSendRestriction(
this,
_peer->asUser(),
controller());
} else if (restriction.startsWith(u"restriction:"_q)) {
const auto error = restriction.mid(12);
_sendRestriction = TextErrorSendRestriction(this, error);
} else if (const auto lifting = restriction.boostsToLift) {
auto button = base::make_unique_q<Ui::FlatButton>(
this,
restriction.text,
st::historyComposeButton);
const auto channel = _peer->asChannel();
button->setClickedCallback([=] {
controller()->resolveBoostState(channel, lifting);
});
_sendRestriction = std::move(button);
} else {
Unexpected("Restriction type.");
_sendRestriction = TextErrorSendRestriction(this, restriction.text);
}
if (_sendRestriction) {
_sendRestriction->show();
@ -7130,9 +7130,8 @@ void HistoryWidget::sendInlineResult(InlineBots::ResultSelected result) {
return;
}
auto errorText = result.result->getErrorOnSend(_history);
if (!errorText.isEmpty()) {
controller()->showToast(errorText);
if (const auto error = result.result->getErrorOnSend(_history)) {
Data::ShowSendErrorToast(controller(), _peer, error);
return;
}
@ -7774,9 +7773,9 @@ bool HistoryWidget::sendExistingDocument(
std::optional<MsgId> localId) {
const auto error = _peer
? Data::RestrictionError(_peer, ChatRestriction::SendStickers)
: std::nullopt;
: Data::SendError();
if (error) {
controller()->showToast(*error);
Data::ShowSendErrorToast(controller(), _peer, error);
return false;
} else if (!_peer
|| !_canSendMessages
@ -7811,9 +7810,9 @@ bool HistoryWidget::sendExistingPhoto(
Api::SendOptions options) {
const auto error = _peer
? Data::RestrictionError(_peer, ChatRestriction::SendPhotos)
: std::nullopt;
: Data::SendError();
if (error) {
controller()->showToast(*error);
Data::ShowSendErrorToast(controller(), _peer, error);
return false;
} else if (!_peer || !_canSendMessages) {
return false;

View file

@ -29,6 +29,7 @@ class Error;
namespace Data {
class PhotoMedia;
struct SendError;
} // namespace Data
namespace SendMenu {
@ -579,7 +580,7 @@ private:
void addMessagesToBack(not_null<PeerData*> peer, const QVector<MTPMessage> &messages);
void updateSendRestriction();
[[nodiscard]] QString computeSendRestriction() const;
[[nodiscard]] Data::SendError computeSendRestriction() const;
void updateHistoryGeometry(bool initial = false, bool loadedDown = false, const ScrollChange &change = { ScrollChangeNone, 0 });
void updateListSize();
void startItemRevealAnimations();

View file

@ -48,6 +48,7 @@ struct WriteRestriction {
QString text;
QString button;
Type type = Type::None;
int boostsToLift = false;
[[nodiscard]] bool empty() const {
return (type == Type::None);

View file

@ -2222,7 +2222,7 @@ void SetupRestrictionView(
not_null<Ui::RpWidget*> widget,
not_null<const style::ComposeControls*> st,
std::shared_ptr<ChatHelpers::Show> show,
const QString &name,
not_null<PeerData*> peer,
rpl::producer<Controls::WriteRestriction> restriction,
Fn<void(QPainter &p, QRect clip)> paintBackground) {
struct State {
@ -2234,7 +2234,9 @@ void SetupRestrictionView(
};
const auto state = widget->lifetime().make_state<State>();
state->updateGeometries = [=] {
if (!state->label) {
if (!state->label && state->button) {
state->button->setGeometry(widget->rect());
} else if (!state->label) {
return;
} else if (state->button) {
const auto available = widget->width()
@ -2307,14 +2309,24 @@ void SetupRestrictionView(
) | rpl::distinct_until_changed(
) | rpl::start_with_next([=](Controls::WriteRestriction value) {
using Type = Controls::WriteRestriction::Type;
if (value.type == Type::Rights) {
if (const auto lifting = value.boostsToLift) {
state->button = std::make_unique<Ui::FlatButton>(
widget,
tr::lng_restricted_boost_group(tr::now),
st::historyComposeButton);
state->button->setClickedCallback([=] {
const auto window = show->resolveWindow(
ChatHelpers::WindowUsage::PremiumPromo);
window->resolveBoostState(peer->asChannel(), lifting);
});
} else if (value.type == Type::Rights) {
state->icon = nullptr;
state->unlock = nullptr;
state->button = nullptr;
state->label = makeLabel(value.text, st->restrictionLabel);
} else if (value.type == Type::PremiumRequired) {
state->icon = makeIcon();
state->unlock = makeUnlock(value.button, name);
state->unlock = makeUnlock(value.button, peer->shortName());
state->button = std::make_unique<Ui::AbstractButton>(widget);
state->button->setClickedCallback([=] {
::Settings::ShowPremiumPromoToast(
@ -2322,7 +2334,7 @@ void SetupRestrictionView(
tr::lng_send_non_premium_message_toast(
tr::now,
lt_user,
TextWithEntities{ name },
TextWithEntities{ peer->shortName() },
lt_link,
Ui::Text::Link(
Ui::Text::Bold(
@ -2373,7 +2385,7 @@ void ComposeControls::initWriteRestriction() {
_writeRestricted.get(),
&_st,
_show,
_history->peer->shortName(),
_history->peer,
_writeRestriction.value(),
background);
@ -2405,7 +2417,7 @@ void ComposeControls::initVoiceRecordBar() {
}, _wrap->lifetime());
_voiceRecordBar->setStartRecordingFilter([=] {
const auto error = [&]() -> std::optional<QString> {
const auto error = [&]() -> Data::SendError {
const auto peer = _history ? _history->peer.get() : nullptr;
if (peer) {
if (const auto error = Data::RestrictionError(
@ -2414,10 +2426,10 @@ void ComposeControls::initVoiceRecordBar() {
return error;
}
}
return std::nullopt;
return {};
}();
if (error) {
_show->showToast(*error);
Data::ShowSendErrorToast(_show, _history->peer, error);
return true;
} else if (_showSlowmodeError && _showSlowmodeError()) {
return true;

View file

@ -23,7 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h"
#include "history/history_drag_area.h"
#include "history/history_item_components.h"
#include "history/history_item_helpers.h" // GetErrorTextForSending.
#include "history/history_item_helpers.h" // GetErrorForSending.
#include "history/history_view_swipe.h"
#include "ui/chat/pinned_bar.h"
#include "ui/chat/chat_style.h"
@ -661,7 +661,7 @@ void RepliesWidget::setupComposeControls() {
? _topic
: _history->peer->forumTopicFor(_rootId);
return (!topic || topic->canToggleClosed() || !topic->closed())
? std::optional<QString>()
? Data::SendError()
: tr::lng_forum_topic_closed(tr::now);
});
auto writeRestriction = rpl::combine(
@ -670,7 +670,7 @@ void RepliesWidget::setupComposeControls() {
Data::PeerUpdate::Flag::Rights),
Data::CanSendAnythingValue(_history->peer),
std::move(topicWriteRestrictions)
) | rpl::map([=](auto, auto, std::optional<QString> topicRestriction) {
) | rpl::map([=](auto, auto, Data::SendError topicRestriction) {
const auto allWithoutPolls = Data::AllSendRestrictions()
& ~ChatRestriction::SendPolls;
const auto canSendAnything = _topic
@ -687,10 +687,11 @@ void RepliesWidget::setupComposeControls() {
: tr::lng_group_not_accessible(tr::now))
: topicRestriction
? std::move(topicRestriction)
: std::optional<QString>();
: Data::SendError();
return text ? Controls::WriteRestriction{
.text = std::move(*text),
.type = Controls::WriteRestrictionType::Rights,
.boostsToLift = text.boostsToLift,
} : Controls::WriteRestriction();
});
@ -936,7 +937,7 @@ void RepliesWidget::chooseAttach(
std::optional<bool> overrideSendImagesAsPhotos) {
_choosingAttach = false;
if (const auto error = Data::AnyFileRestrictionError(_history->peer)) {
controller()->showToast(*error);
Data::ShowSendErrorToast(controller(), _history->peer, error);
return;
} else if (showSlowmodeError()) {
return;
@ -1168,11 +1169,11 @@ bool RepliesWidget::showSendingFilesError(
bool RepliesWidget::showSendingFilesError(
const Ui::PreparedList &list,
std::optional<bool> compress) const {
const auto text = [&] {
const auto error = [&]() -> Data::SendError {
const auto peer = _history->peer;
const auto error = Data::FileRestrictionError(peer, list, compress);
if (error) {
return *error;
return error;
} else if (const auto left = _history->peer->slowmodeSecondsLeft()) {
return tr::lng_slowmode_enabled(
tr::now,
@ -1192,16 +1193,16 @@ bool RepliesWidget::showSendingFilesError(
}
return tr::lng_forward_send_files_cant(tr::now);
}();
if (text.isEmpty()) {
if (!error) {
return false;
} else if (text == u"(toolarge)"_q) {
} else if (error.text == u"(toolarge)"_q) {
const auto fileSize = list.files.back().size;
controller()->show(
Box(FileSizeLimitBox, &session(), fileSize, nullptr));
return true;
}
controller()->showToast(text);
Data::ShowSendErrorToast(controller(), _history->peer, error);
return true;
}
@ -1247,7 +1248,7 @@ void RepliesWidget::send(Api::SendOptions options) {
message.textWithTags = _composeControls->getTextWithAppliedMarkdown();
message.webPage = _composeControls->webPageDraft();
const auto error = GetErrorTextForSending(
const auto error = GetErrorForSending(
_history->peer,
{
.topicRootId = _topic ? _topic->rootId() : MsgId(0),
@ -1255,8 +1256,8 @@ void RepliesWidget::send(Api::SendOptions options) {
.text = &message.textWithTags,
.ignoreSlowmodeCountdown = (options.scheduled != 0),
});
if (!error.isEmpty()) {
controller()->showToast(error);
if (error) {
Data::ShowSendErrorToast(controller(), _history->peer, error);
return;
}
@ -1407,7 +1408,7 @@ bool RepliesWidget::sendExistingDocument(
_history->peer,
ChatRestriction::SendStickers);
if (error) {
controller()->showToast(*error);
Data::ShowSendErrorToast(controller(), _history->peer, error);
return false;
} else if (showSlowmodeError()
|| ShowSendPremiumError(controller(), document)) {
@ -1435,7 +1436,7 @@ bool RepliesWidget::sendExistingPhoto(
_history->peer,
ChatRestriction::SendPhotos);
if (error) {
controller()->showToast(*error);
Data::ShowSendErrorToast(controller(), _history->peer, error);
return false;
} else if (showSlowmodeError()) {
return false;
@ -1453,9 +1454,8 @@ bool RepliesWidget::sendExistingPhoto(
void RepliesWidget::sendInlineResult(
not_null<InlineBots::Result*> result,
not_null<UserData*> bot) {
const auto errorText = result->getErrorOnSend(_history);
if (!errorText.isEmpty()) {
controller()->showToast(errorText);
if (const auto error = result->getErrorOnSend(_history)) {
Data::ShowSendErrorToast(controller(), _history->peer, error);
return;
}
sendInlineResult(result, bot, {}, std::nullopt);

View file

@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_sticker_toast.h"
#include "history/history.h"
#include "history/history_drag_area.h"
#include "history/history_item_helpers.h" // GetErrorTextForSending.
#include "history/history_item_helpers.h" // GetErrorForSending.
#include "menu/menu_send.h" // SendMenu::Type.
#include "ui/widgets/buttons.h"
#include "ui/widgets/tooltip.h"
@ -252,83 +252,85 @@ ScheduledWidget::~ScheduledWidget() = default;
void ScheduledWidget::setupComposeControls() {
auto writeRestriction = _forumTopic
? [&] {
auto topicWriteRestrictions = rpl::single(
) | rpl::then(session().changes().topicUpdates(
Data::TopicUpdate::Flag::Closed
) | rpl::filter([=](const Data::TopicUpdate &update) {
return (update.topic->history() == _history)
&& (update.topic->rootId() == _forumTopic->rootId());
}) | rpl::to_empty) | rpl::map([=] {
return (!_forumTopic
|| _forumTopic->canToggleClosed()
|| !_forumTopic->closed())
? std::optional<QString>()
: tr::lng_forum_topic_closed(tr::now);
});
return rpl::combine(
session().changes().peerFlagsValue(
_history->peer,
Data::PeerUpdate::Flag::Rights),
Data::CanSendAnythingValue(_history->peer),
std::move(topicWriteRestrictions)
) | rpl::map([=](
auto,
auto,
std::optional<QString> topicRestriction) {
const auto allWithoutPolls = Data::AllSendRestrictions()
& ~ChatRestriction::SendPolls;
const auto canSendAnything = Data::CanSendAnyOf(
_forumTopic,
allWithoutPolls);
const auto restriction = Data::RestrictionError(
_history->peer,
ChatRestriction::SendOther);
auto text = !canSendAnything
? (restriction
? restriction
auto topicWriteRestrictions = rpl::single(
) | rpl::then(session().changes().topicUpdates(
Data::TopicUpdate::Flag::Closed
) | rpl::filter([=](const Data::TopicUpdate &update) {
return (update.topic->history() == _history)
&& (update.topic->rootId() == _forumTopic->rootId());
}) | rpl::to_empty) | rpl::map([=] {
return (!_forumTopic
|| _forumTopic->canToggleClosed()
|| !_forumTopic->closed())
? Data::SendError()
: tr::lng_forum_topic_closed(tr::now);
});
return rpl::combine(
session().changes().peerFlagsValue(
_history->peer,
Data::PeerUpdate::Flag::Rights),
Data::CanSendAnythingValue(_history->peer),
std::move(topicWriteRestrictions)
) | rpl::map([=](
auto,
auto,
Data::SendError topicRestriction) {
const auto allWithoutPolls = Data::AllSendRestrictions()
& ~ChatRestriction::SendPolls;
const auto canSendAnything = Data::CanSendAnyOf(
_forumTopic,
allWithoutPolls);
const auto restriction = Data::RestrictionError(
_history->peer,
ChatRestriction::SendOther);
auto text = !canSendAnything
? (restriction
? restriction
: topicRestriction
? std::move(topicRestriction)
: tr::lng_group_not_accessible(tr::now))
: topicRestriction
? std::move(topicRestriction)
: tr::lng_group_not_accessible(tr::now))
: topicRestriction
? std::move(topicRestriction)
: std::optional<QString>();
return text ? Controls::WriteRestriction{
.text = std::move(*text),
.type = Controls::WriteRestrictionType::Rights,
} : Controls::WriteRestriction();
}) | rpl::type_erased();
}()
: Data::SendError();
return text ? Controls::WriteRestriction{
.text = std::move(*text),
.type = Controls::WriteRestrictionType::Rights,
.boostsToLift = text.boostsToLift,
} : Controls::WriteRestriction();
}) | rpl::type_erased();
}()
: [&] {
return rpl::combine(
session().changes().peerFlagsValue(
_history->peer,
Data::PeerUpdate::Flag::Rights),
Data::CanSendAnythingValue(_history->peer)
) | rpl::map([=] {
const auto allWithoutPolls = Data::AllSendRestrictions()
& ~ChatRestriction::SendPolls;
const auto canSendAnything = Data::CanSendAnyOf(
_history->peer,
allWithoutPolls,
false);
const auto restriction = Data::RestrictionError(
_history->peer,
ChatRestriction::SendOther);
auto text = !canSendAnything
? (restriction
? restriction
: tr::lng_group_not_accessible(tr::now))
: std::optional<QString>();
return text ? Controls::WriteRestriction{
.text = std::move(*text),
.type = Controls::WriteRestrictionType::Rights,
} : Controls::WriteRestriction();
}) | rpl::type_erased();
}();
return rpl::combine(
session().changes().peerFlagsValue(
_history->peer,
Data::PeerUpdate::Flag::Rights),
Data::CanSendAnythingValue(_history->peer)
) | rpl::map([=] {
const auto allWithoutPolls = Data::AllSendRestrictions()
& ~ChatRestriction::SendPolls;
const auto canSendAnything = Data::CanSendAnyOf(
_history->peer,
allWithoutPolls,
false);
const auto restriction = Data::RestrictionError(
_history->peer,
ChatRestriction::SendOther);
auto text = !canSendAnything
? (restriction
? restriction
: tr::lng_group_not_accessible(tr::now))
: Data::SendError();
return text ? Controls::WriteRestriction{
.text = std::move(*text),
.type = Controls::WriteRestrictionType::Rights,
.boostsToLift = text.boostsToLift,
} : Controls::WriteRestriction();
}) | rpl::type_erased();
}();
_composeControls->setHistory({
.history = _history.get(),
.writeRestriction = std::move(writeRestriction),
});
});
_composeControls->height(
) | rpl::start_with_next([=] {
@ -463,7 +465,7 @@ void ScheduledWidget::setupComposeControls() {
void ScheduledWidget::chooseAttach() {
if (const auto error = Data::AnyFileRestrictionError(_history->peer)) {
controller()->showToast(*error);
Data::ShowSendErrorToast(controller(), _history->peer, error);
return;
}
@ -667,12 +669,12 @@ bool ScheduledWidget::showSendingFilesError(
bool ScheduledWidget::showSendingFilesError(
const Ui::PreparedList &list,
std::optional<bool> compress) const {
const auto text = [&] {
const auto error = [&]() -> Data::SendError {
using Error = Ui::PreparedList::Error;
const auto peer = _history->peer;
const auto error = Data::FileRestrictionError(peer, list, compress);
if (error) {
return *error;
return error;
} else switch (list.error) {
case Error::None: return QString();
case Error::EmptyFile:
@ -685,16 +687,16 @@ bool ScheduledWidget::showSendingFilesError(
}
return tr::lng_forward_send_files_cant(tr::now);
}();
if (text.isEmpty()) {
if (!error) {
return false;
} else if (text == u"(toolarge)"_q) {
} else if (error.text == u"(toolarge)"_q) {
const auto fileSize = list.files.back().size;
controller()->show(
Box(FileSizeLimitBox, &session(), fileSize, nullptr));
return true;
}
controller()->showToast(text);
Data::ShowSendErrorToast(controller(), _history->peer, error);
return true;
}
@ -717,7 +719,7 @@ void ScheduledWidget::send() {
return;
}
const auto error = GetErrorTextForSending(
const auto error = GetErrorForSending(
_history->peer,
{
.topicRootId = _forumTopic
@ -729,8 +731,8 @@ void ScheduledWidget::send() {
.text = &textWithTags,
.ignoreSlowmodeCountdown = true,
});
if (!error.isEmpty()) {
controller()->showToast(error);
if (error) {
Data::ShowSendErrorToast(controller(), _history->peer, error);
return;
}
const auto callback = [=](Api::SendOptions options) { send(options); };
@ -865,7 +867,7 @@ bool ScheduledWidget::sendExistingDocument(
_history->peer,
ChatRestriction::SendStickers);
if (error) {
controller()->showToast(*error);
Data::ShowSendErrorToast(controller(), _history->peer, error);
return false;
} else if (ShowSendPremiumError(controller(), document)) {
return false;
@ -893,7 +895,7 @@ bool ScheduledWidget::sendExistingPhoto(
_history->peer,
ChatRestriction::SendPhotos);
if (error) {
controller()->showToast(*error);
Data::ShowSendErrorToast(controller(), _history->peer, error);
return false;
}
@ -909,9 +911,8 @@ bool ScheduledWidget::sendExistingPhoto(
void ScheduledWidget::sendInlineResult(
not_null<InlineBots::Result*> result,
not_null<UserData*> bot) {
const auto errorText = result->getErrorOnSend(_history);
if (!errorText.isEmpty()) {
controller()->showToast(errorText);
if (const auto error = result->getErrorOnSend(_history)) {
Data::ShowSendErrorToast(controller(), _history->peer, error);
return;
}
const auto callback = [=](Api::SendOptions options) {

View file

@ -394,13 +394,9 @@ not_null<HistoryItem*> Result::makeMessage(
return sendData->makeMessage(this, history, std::move(fields));
}
QString Result::getErrorOnSend(not_null<History*> history) const {
const auto specific = sendData->getErrorOnSend(this, history);
return !specific.isEmpty()
? specific
: Data::RestrictionError(
history->peer,
ChatRestriction::SendInline).value_or(QString());
Data::SendError Result::getErrorOnSend(not_null<History*> history) const {
return sendData->getErrorOnSend(this, history).value_or(
Data::RestrictionError(history->peer, ChatRestriction::SendInline));
}
std::optional<Data::LocationPoint> Result::getLocationPoint() const {

View file

@ -20,6 +20,7 @@ struct HistoryItemCommonFields;
namespace Data {
class LocationPoint;
struct SendError;
} // namespace Data
namespace InlineBots {
@ -69,7 +70,8 @@ public:
[[nodiscard]] not_null<HistoryItem*> makeMessage(
not_null<History*> history,
HistoryItemCommonFields &&fields) const;
QString getErrorOnSend(not_null<History*> history) const;
[[nodiscard]] Data::SendError getErrorOnSend(
not_null<History*> history) const;
// interface for Layout:: usage
std::optional<Data::LocationPoint> getLocationPoint() const;

View file

@ -42,11 +42,11 @@ not_null<HistoryItem*> SendDataCommon::makeMessage(
std::move(distinct.media));
}
QString SendDataCommon::getErrorOnSend(
Data::SendError SendDataCommon::getErrorOnSend(
const Result *owner,
not_null<History*> history) const {
const auto type = ChatRestriction::SendOther;
return Data::RestrictionError(history->peer, type).value_or(QString());
return Data::RestrictionError(history->peer, type);
}
SendDataCommon::SentMessageFields SendText::getSentMessageFields() const {
@ -106,11 +106,11 @@ not_null<HistoryItem*> SendPhoto::makeMessage(
TextWithEntities{ _message, _entities });
}
QString SendPhoto::getErrorOnSend(
Data::SendError SendPhoto::getErrorOnSend(
const Result *owner,
not_null<History*> history) const {
const auto type = ChatRestriction::SendPhotos;
return Data::RestrictionError(history->peer, type).value_or(QString());
return Data::RestrictionError(history->peer, type);
}
not_null<HistoryItem*> SendFile::makeMessage(
@ -123,11 +123,11 @@ not_null<HistoryItem*> SendFile::makeMessage(
TextWithEntities{ _message, _entities });
}
QString SendFile::getErrorOnSend(
Data::SendError SendFile::getErrorOnSend(
const Result *owner,
not_null<History*> history) const {
const auto type = _document->requiredSendRight();
return Data::RestrictionError(history->peer, type).value_or(QString());
return Data::RestrictionError(history->peer, type);
}
not_null<HistoryItem*> SendGame::makeMessage(
@ -137,11 +137,11 @@ not_null<HistoryItem*> SendGame::makeMessage(
return history->makeMessage(std::move(fields), _game);
}
QString SendGame::getErrorOnSend(
Data::SendError SendGame::getErrorOnSend(
const Result *owner,
not_null<History*> history) const {
const auto type = ChatRestriction::SendGames;
return Data::RestrictionError(history->peer, type).value_or(QString());
return Data::RestrictionError(history->peer, type);
}
SendDataCommon::SentMessageFields SendInvoice::getSentMessageFields() const {

View file

@ -11,6 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
struct HistoryItemCommonFields;
namespace Data {
struct SendError;
} // namespace Data
namespace Main {
class Session;
} // namespace Main
@ -44,7 +48,7 @@ public:
const Result *owner,
not_null<History*> history,
HistoryItemCommonFields &&fields) const = 0;
virtual QString getErrorOnSend(
virtual Data::SendError getErrorOnSend(
const Result *owner,
not_null<History*> history) const = 0;
@ -80,7 +84,7 @@ public:
not_null<History*> history,
HistoryItemCommonFields &&fields) const override;
QString getErrorOnSend(
Data::SendError getErrorOnSend(
const Result *owner,
not_null<History*> history) const override;
@ -241,7 +245,7 @@ public:
not_null<History*> history,
HistoryItemCommonFields &&fields) const override;
QString getErrorOnSend(
Data::SendError getErrorOnSend(
const Result *owner,
not_null<History*> history) const override;
@ -275,7 +279,7 @@ public:
not_null<History*> history,
HistoryItemCommonFields &&fields) const override;
QString getErrorOnSend(
Data::SendError getErrorOnSend(
const Result *owner,
not_null<History*> history) const override;
@ -303,7 +307,7 @@ public:
not_null<History*> history,
HistoryItemCommonFields &&fields) const override;
QString getErrorOnSend(
Data::SendError getErrorOnSend(
const Result *owner,
not_null<History*> history) const override;

View file

@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwindow.h"
#include "main/main_session.h"
#include "window/window_session_controller.h"
#include "ui/text/text_utilities.h"
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
@ -113,19 +114,35 @@ void Inner::checkRestrictedPeer() {
const auto error = Data::RestrictionError(
_inlineQueryPeer,
ChatRestriction::SendInline);
if (error) {
if (!_restrictedLabel) {
_restrictedLabel.create(this, *error, st::stickersRestrictedLabel);
_restrictedLabel->show();
_restrictedLabel->move(st::inlineResultsLeft - st::roundRadiusSmall, st::stickerPanPadding);
_restrictedLabel->resizeToNaturalWidth(width() - (st::inlineResultsLeft - st::roundRadiusSmall) * 2);
if (_switchPmButton) {
_switchPmButton->hide();
}
repaintItems();
}
const auto changed = (_restrictedLabelKey != error.text);
if (!changed) {
return;
}
_restrictedLabelKey = error.text;
if (error) {
const auto window = _controller;
const auto peer = _inlineQueryPeer;
_restrictedLabel.create(
this,
rpl::single(error.boostsToLift
? Ui::Text::Link(error.text)
: TextWithEntities{ error.text }),
st::stickersRestrictedLabel);
const auto lifting = error.boostsToLift;
_restrictedLabel->setClickHandlerFilter([=](auto...) {
window->resolveBoostState(peer->asChannel(), lifting);
return false;
});
_restrictedLabel->show();
updateRestrictedLabelGeometry();
if (_switchPmButton) {
_switchPmButton->hide();
}
repaintItems();
return;
}
} else {
_restrictedLabelKey = QString();
}
if (_restrictedLabel) {
_restrictedLabel.destroy();
@ -136,6 +153,18 @@ void Inner::checkRestrictedPeer() {
}
}
void Inner::updateRestrictedLabelGeometry() {
if (!_restrictedLabel) {
return;
}
auto labelWidth = width() - st::stickerPanPadding * 2;
_restrictedLabel->resizeToWidth(labelWidth);
_restrictedLabel->moveToLeft(
(width() - _restrictedLabel->width()) / 2,
st::stickerPanPadding);
}
bool Inner::isRestrictedView() {
checkRestrictedPeer();
return (_restrictedLabel != nullptr);
@ -178,6 +207,10 @@ rpl::producer<> Inner::inlineRowsCleared() const {
Inner::~Inner() = default;
void Inner::resizeEvent(QResizeEvent *e) {
updateRestrictedLabelGeometry();
}
void Inner::paintEvent(QPaintEvent *e) {
Painter p(this);
QRect r = e ? e->rect() : rect();

View file

@ -108,6 +108,7 @@ protected:
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void leaveEventHook(QEvent *e) override;
void leaveToChildEvent(QEvent *e, QWidget *child) override;
@ -136,6 +137,7 @@ private:
void clearInlineRows(bool resultsDeleted);
ItemBase *layoutPrepareInlineResult(Result *result);
void updateRestrictedLabelGeometry();
void deleteUnusedInlineLayouts();
int validateExistingInlineRows(const Results &results);
@ -162,6 +164,7 @@ private:
QByteArray _switchPmUrl;
object_ptr<Ui::FlatLabel> _restrictedLabel = { nullptr };
QString _restrictedLabelKey;
base::unique_qptr<Ui::PopupMenu> _menu;

View file

@ -52,7 +52,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "dialogs/dialogs_widget.h"
#include "history/history_widget.h"
#include "history/history_item_helpers.h" // GetErrorTextForSending.
#include "history/history_item_helpers.h" // GetErrorForSending.
#include "history/view/media/history_view_media.h"
#include "history/view/history_view_service_message.h"
#include "history/view/history_view_sublist_section.h"
@ -556,15 +556,15 @@ bool MainWidget::setForwardDraft(
const auto history = thread->owningHistory();
const auto items = session().data().idsToItems(draft.ids);
const auto topicRootId = thread->topicRootId();
const auto error = GetErrorTextForSending(
const auto error = GetErrorForSending(
history->peer,
{
.topicRootId = topicRootId,
.forward = &items,
.ignoreSlowmodeCountdown = true,
});
if (!error.isEmpty()) {
_controller->show(Ui::MakeInformBox(error));
if (error) {
Data::ShowSendErrorToast(_controller, history->peer, error);
return false;
}
@ -611,12 +611,12 @@ bool MainWidget::sendPaths(
not_null<Data::Thread*> thread,
const QStringList &paths) {
if (!Data::CanSendAnyOf(thread, Data::FilesSendRestrictions())) {
_controller->show(Ui::MakeInformBox(
tr::lng_forward_send_files_cant()));
_controller->showToast(
tr::lng_forward_send_files_cant(tr::now));
return false;
} else if (const auto error = Data::AnyFileRestrictionError(
thread->peer())) {
_controller->show(Ui::MakeInformBox(*error));
Data::ShowSendErrorToast(controller(), thread->peer(), error);
return false;
} else {
_controller->showThread(
@ -659,12 +659,12 @@ bool MainWidget::filesOrForwardDrop(
}
return false;
} else if (!Data::CanSendAnyOf(thread, Data::FilesSendRestrictions())) {
_controller->show(Ui::MakeInformBox(
tr::lng_forward_send_files_cant()));
_controller->showToast(
tr::lng_forward_send_files_cant(tr::now));
return false;
} else if (const auto error = Data::AnyFileRestrictionError(
thread->peer())) {
_controller->show(Ui::MakeInformBox(*error));
Data::ShowSendErrorToast(_controller, thread->peer(), error);
return false;
} else {
_controller->showThread(

View file

@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/file_utilities.h"
#include "core/mime_type.h"
#include "data/stickers/data_custom_emoji.h"
#include "data/data_chat_participant_status.h"
#include "data/data_document.h"
#include "data/data_message_reaction_id.h"
#include "data/data_peer_values.h"
@ -219,15 +220,15 @@ bool ReplyArea::send(
return false;
}
const auto error = GetErrorTextForSending(
const auto error = GetErrorForSending(
_data.peer,
{
.topicRootId = MsgId(0),
.text = &message.textWithTags,
.ignoreSlowmodeCountdown = (options.scheduled != 0),
});
if (!error.isEmpty()) {
_controller->uiShow()->showToast(error);
if (error) {
Data::ShowSendErrorToast(_controller->uiShow(), _data.peer, error);
return false;
}
@ -262,7 +263,7 @@ bool ReplyArea::sendExistingDocument(
_data.peer,
ChatRestriction::SendStickers);
if (error) {
show->showToast(*error);
Data::ShowSendErrorToast(show, _data.peer, error);
return false;
} else if (showSlowmodeError()
|| Window::ShowSendPremiumError(show, document)) {
@ -290,7 +291,7 @@ bool ReplyArea::sendExistingPhoto(
_data.peer,
ChatRestriction::SendPhotos);
if (error) {
show->showToast(*error);
Data::ShowSendErrorToast(show, _data.peer, error);
return false;
} else if (showSlowmodeError()) {
return false;
@ -308,9 +309,9 @@ bool ReplyArea::sendExistingPhoto(
void ReplyArea::sendInlineResult(
not_null<InlineBots::Result*> result,
not_null<UserData*> bot) {
const auto errorText = result->getErrorOnSend(history());
if (!errorText.isEmpty()) {
_controller->uiShow()->showToast(errorText);
if (const auto error = result->getErrorOnSend(history())) {
const auto show = _controller->uiShow();
Data::ShowSendErrorToast(show, history()->peer, error);
return;
}
sendInlineResult(result, bot, {}, std::nullopt);
@ -363,11 +364,11 @@ bool ReplyArea::showSendingFilesError(
bool ReplyArea::showSendingFilesError(
const Ui::PreparedList &list,
std::optional<bool> compress) const {
const auto text = [&] {
const auto error = [&]() -> Data::SendError {
const auto peer = _data.peer;
const auto error = Data::FileRestrictionError(peer, list, compress);
if (error) {
return *error;
return error;
}
using Error = Ui::PreparedList::Error;
switch (list.error) {
@ -382,9 +383,9 @@ bool ReplyArea::showSendingFilesError(
}
return tr::lng_forward_send_files_cant(tr::now);
}();
if (text.isEmpty()) {
if (!error) {
return false;
} else if (text == u"(toolarge)"_q) {
} else if (error.text == u"(toolarge)"_q) {
const auto fileSize = list.files.back().size;
_controller->uiShow()->showBox(Box(
FileSizeLimitBox,
@ -394,7 +395,7 @@ bool ReplyArea::showSendingFilesError(
return true;
}
_controller->uiShow()->showToast(text);
Data::ShowSendErrorToast(_controller->uiShow(), _data.peer, error);
return true;
}
@ -422,7 +423,7 @@ void ReplyArea::chooseAttach(
}
const auto peer = not_null(_data.peer);
if (const auto error = Data::AnyFileRestrictionError(peer)) {
_controller->uiShow()->showToast(*error);
Data::ShowSendErrorToast(_controller->uiShow(), peer, error);
return;
} else if (showSlowmodeError()) {
return;

View file

@ -22,7 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_thread.h"
#include "data/data_user.h"
#include "history/history.h"
#include "history/history_item_helpers.h" // GetErrorTextForSending.
#include "history/history_item_helpers.h" // GetErrorForSending.
#include "history/view/history_view_context_menu.h" // CopyStoryLink.
#include "lang/lang_keys.h"
#include "main/main_session.h"
@ -87,26 +87,11 @@ namespace Media::Stories {
return;
}
const auto peer = story->peer();
const auto error = [&] {
for (const auto thread : result) {
const auto error = GetErrorTextForSending(
thread,
{ .story = story, .text = &comment });
if (!error.isEmpty()) {
return std::make_pair(error, thread);
}
}
return std::make_pair(QString(), result.front());
}();
if (!error.first.isEmpty()) {
auto text = TextWithEntities();
if (result.size() > 1) {
text.append(
Ui::Text::Bold(error.second->chatListName())
).append("\n\n");
}
text.append(error.first);
show->showBox(Ui::MakeInformBox(text));
const auto error = GetErrorForSending(
result,
{ .story = story, .text = &comment });
if (error.error) {
show->showBox(MakeSendErrorBox(error, result.size() > 1));
return;
}

View file

@ -185,7 +185,9 @@ std::optional<QString> RestrictionToSend(
not_null<Window::Controller*> controller,
ChatRestriction right) {
if (const auto peer = ActiveChat(controller).peer()) {
return Data::RestrictionError(peer, right);
if (const auto error = Data::RestrictionError(peer, right)) {
return *error;
}
}
return std::nullopt;
}

View file

@ -1538,10 +1538,8 @@ void ShortcutMessages::sendInlineResult(
not_null<UserData*> bot) {
if (showPremiumRequired()) {
return;
}
const auto errorText = result->getErrorOnSend(_history);
if (!errorText.isEmpty()) {
_controller->showToast(errorText);
} else if (const auto error = result->getErrorOnSend(_history)) {
Data::ShowSendErrorToast(_controller, _history->peer, error);
return;
}
sendInlineResult(result, bot, {}, std::nullopt);

View file

@ -325,8 +325,14 @@ void BoostBox(
return (counters.mine > 1) ? u"x%1"_q.arg(counters.mine) : u""_q;
});
const auto wasMine = state->data.current().mine;
const auto wasLifting = data.lifting;
auto text = state->data.value(
) | rpl::map([=](BoostCounters counters) {
const auto lifting = wasLifting
? (wasLifting
- std::clamp(counters.mine - wasMine, 0, wasLifting - 1))
: 0;
const auto bold = Ui::Text::Bold(name);
const auto now = counters.boosts;
const auto full = !counters.nextLevelBoosts;
@ -337,7 +343,14 @@ void BoostBox(
lt_count,
rpl::single(float64(counters.level + (left ? 1 : 0))),
Ui::Text::RichLangValue);
return (counters.mine || full)
return (lifting > 1)
? tr::lng_boost_group_lift_restrictions_many(
lt_count,
rpl::single(float64(lifting)),
Ui::Text::RichLangValue)
: lifting
? tr::lng_boost_group_lift_restrictions(Ui::Text::RichLangValue)
: (counters.mine || full)
? (left
? tr::lng_boost_channel_needs_unlock(
lt_count,
@ -365,6 +378,14 @@ void BoostBox(
rpl::single(bold),
Ui::Text::RichLangValue);
}) | rpl::flatten_latest();
if (wasLifting) {
state->data.value(
) | rpl::start_with_next([=](BoostCounters counters) {
if (counters.mine - wasMine >= wasLifting) {
box->closeBox();
}
}, box->lifetime());
}
auto faded = object_ptr<Ui::FadeWrap<>>(
close->parentWidget(),

View file

@ -48,6 +48,7 @@ struct BoostBoxData {
QString name;
BoostCounters boost;
BoostFeatures features;
int lifting = 0;
bool allowMulti = false;
bool group = false;
};

View file

@ -61,7 +61,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_updates.h"
#include "mtproto/mtproto_config.h"
#include "history/history.h"
#include "history/history_item_helpers.h" // GetErrorTextForSending.
#include "history/history_item_helpers.h" // GetErrorForSending.
#include "history/view/history_view_context_menu.h"
#include "window/window_separate_id.h"
#include "window/window_session_controller.h"
@ -2595,11 +2595,11 @@ QPointer<Ui::BoxContent> ShowSendNowMessagesBox(
: tr::lng_scheduled_send_now(tr::now);
const auto list = session->data().idsToItems(items);
const auto error = GetErrorTextForSending(
const auto error = GetErrorForSending(
history->peer,
{ .forward = &list });
if (!error.isEmpty()) {
navigation->showToast(error);
if (error) {
Data::ShowSendErrorToast(navigation, history->peer, error);
return { nullptr };
}
auto done = [

View file

@ -764,7 +764,10 @@ void SessionNavigation::showPeerByLinkResolved(
}
}
void SessionNavigation::resolveBoostState(not_null<ChannelData*> channel) {
void SessionNavigation::resolveBoostState(
not_null<ChannelData*> channel,
int boostsToLift) {
_boostsToLift = boostsToLift;
if (_boostStateResolving == channel) {
return;
}
@ -772,18 +775,33 @@ void SessionNavigation::resolveBoostState(not_null<ChannelData*> channel) {
_api.request(MTPpremium_GetBoostsStatus(
channel->input
)).done([=](const MTPpremium_BoostsStatus &result) {
_boostStateResolving = nullptr;
if (base::take(_boostStateResolving) != channel) {
return;
}
const auto boosted = std::make_shared<bool>();
channel->updateLevelHint(result.data().vlevel().v);
const auto submit = [=](Fn<void(Ui::BoostCounters)> done) {
applyBoost(channel, done);
applyBoost(channel, [=](Ui::BoostCounters counters) {
*boosted = true;
done(counters);
});
};
uiShow()->show(Box(Ui::BoostBox, Ui::BoostBoxData{
const auto lifting = base::take(_boostsToLift);
const auto box = uiShow()->show(Box(Ui::BoostBox, Ui::BoostBoxData{
.name = channel->name(),
.boost = ParseBoostCounters(result),
.features = LookupBoostFeatures(channel),
.lifting = lifting,
.allowMulti = (BoostsForGift(_session) > 0),
.group = channel->isMegagroup(),
}, submit));
if (lifting) {
box->boxClosing() | rpl::start_with_next([=] {
if (*boosted) {
channel->updateFullForced();
}
}, box->lifetime());
}
}).fail([=](const MTP::Error &error) {
_boostStateResolving = nullptr;
showToast(u"Error: "_q + error.type());

View file

@ -253,7 +253,9 @@ public:
Dialogs::Key inChat,
PeerData *searchFrom = nullptr);
void resolveBoostState(not_null<ChannelData*> channel);
void resolveBoostState(
not_null<ChannelData*> channel,
int boostsToLift = 0);
void resolveCollectible(
PeerId ownerId,
@ -319,6 +321,7 @@ private:
mtpRequestId _showingRepliesRequestId = 0;
ChannelData *_boostStateResolving = nullptr;
int _boostsToLift = 0;
QString _collectibleEntity;
mtpRequestId _collectibleRequestId = 0;