Show slowmode error messages.

This commit is contained in:
John Preston 2019-07-17 12:37:42 +02:00
parent 01d0479335
commit 04bf24288a
16 changed files with 210 additions and 68 deletions

View File

@ -1655,6 +1655,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_rights_slowmode_interval_minutes#one" = "every {count} minute"; "lng_rights_slowmode_interval_minutes#one" = "every {count} minute";
"lng_rights_slowmode_interval_minutes#other" = "every {count} minutes"; "lng_rights_slowmode_interval_minutes#other" = "every {count} minutes";
"lng_slowmode_enabled"= "Slowmode is enabled. You can send your next message in {left}.";
"lng_slowmode_no_many" = "Slowmode is enabled. You can't send more than one message at once.";
"lng_rights_channel_info" = "Change channel info"; "lng_rights_channel_info" = "Change channel info";
"lng_rights_channel_post" = "Post messages"; "lng_rights_channel_post" = "Post messages";
"lng_rights_channel_edit" = "Edit messages of others"; "lng_rights_channel_edit" = "Edit messages of others";

View File

@ -548,7 +548,9 @@ void ApiWrap::toggleHistoryArchived(
// }).send(); // }).send();
//} //}
void ApiWrap::sendMessageFail(const RPCError &error) { void ApiWrap::sendMessageFail(
not_null<PeerData*> peer,
const RPCError &error) {
if (error.type() == qstr("PEER_FLOOD")) { if (error.type() == qstr("PEER_FLOOD")) {
Ui::show(Box<InformBox>( Ui::show(Box<InformBox>(
PeerFloodErrorText(PeerFloodType::Send))); PeerFloodErrorText(PeerFloodType::Send)));
@ -560,6 +562,14 @@ void ApiWrap::sendMessageFail(const RPCError &error) {
tr::now, tr::now,
lt_more_info, lt_more_info,
link))); link)));
} else if (error.type().startsWith(qstr("SLOWMODE_WAIT_"))) {
const auto chop = qstr("SLOWMODE_WAIT_").size();
const auto left = error.type().mid(chop).toInt();
if (const auto channel = peer->asChannel()) {
const auto seconds = channel->slowmodeSeconds();
channel->growSlowmodeLastMessage(
base::unixtime::now() - (left - seconds));
}
} }
} }
@ -4828,9 +4838,10 @@ void ApiWrap::editUploadedFile(
return; return;
} }
const auto peer = item->history()->peer;
request(MTPmessages_EditMessage( request(MTPmessages_EditMessage(
MTP_flags(flagsEditMsg), MTP_flags(flagsEditMsg),
item->history()->peer->input, peer->input,
MTP_int(item->id), MTP_int(item->id),
MTP_string(item->originalText().text), MTP_string(item->originalText().text),
*media, *media,
@ -4853,7 +4864,7 @@ void ApiWrap::editUploadedFile(
Box<InformBox>(tr::lng_edit_media_invalid_file(tr::now)), Box<InformBox>(tr::lng_edit_media_invalid_file(tr::now)),
LayerOption::KeepOther); LayerOption::KeepOther);
} else { } else {
sendMessageFail(error); sendMessageFail(peer, error);
} }
}).send(); }).send();
} }
@ -4986,7 +4997,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
if (error.type() == qstr("MESSAGE_EMPTY")) { if (error.type() == qstr("MESSAGE_EMPTY")) {
lastMessage->destroy(); lastMessage->destroy();
} else { } else {
sendMessageFail(error); sendMessageFail(peer, error);
} }
history->clearSentDraftText(QString()); history->clearSentDraftText(QString());
}).afterRequest(history->sendRequestId }).afterRequest(history->sendRequestId
@ -5099,7 +5110,7 @@ void ApiWrap::sendInlineResult(
applyUpdates(result, randomId); applyUpdates(result, randomId);
history->clearSentDraftText(QString()); history->clearSentDraftText(QString());
}).fail([=](const RPCError &error) { }).fail([=](const RPCError &error) {
sendMessageFail(error); sendMessageFail(peer, error);
history->clearSentDraftText(QString()); history->clearSentDraftText(QString());
}).afterRequest(history->sendRequestId }).afterRequest(history->sendRequestId
).send(); ).send();
@ -5200,12 +5211,12 @@ void ApiWrap::sendExistingDocument(
if (document->fileReference() != usedFileReference) { if (document->fileReference() != usedFileReference) {
performRequest(); performRequest();
} else { } else {
sendMessageFail(error); sendMessageFail(peer, error);
} }
}; };
refreshFileReference(origin, std::move(refreshed)); refreshFileReference(origin, std::move(refreshed));
} else { } else {
sendMessageFail(error); sendMessageFail(peer, error);
} }
}; };
performRequest(); performRequest();
@ -5329,9 +5340,10 @@ void ApiWrap::sendMediaWithRandomId(
? MTPmessages_SendMedia::Flag::f_entities ? MTPmessages_SendMedia::Flag::f_entities
: MTPmessages_SendMedia::Flag(0)); : MTPmessages_SendMedia::Flag(0));
const auto peer = history->peer;
history->sendRequestId = request(MTPmessages_SendMedia( history->sendRequestId = request(MTPmessages_SendMedia(
MTP_flags(flags), MTP_flags(flags),
history->peer->input, peer->input,
MTP_int(replyTo), MTP_int(replyTo),
media, media,
MTP_string(caption.text), MTP_string(caption.text),
@ -5339,7 +5351,7 @@ void ApiWrap::sendMediaWithRandomId(
MTPReplyMarkup(), MTPReplyMarkup(),
sentEntities sentEntities
)).done([=](const MTPUpdates &result) { applyUpdates(result); )).done([=](const MTPUpdates &result) { applyUpdates(result);
}).fail([=](const RPCError &error) { sendMessageFail(error); }).fail([=](const RPCError &error) { sendMessageFail(peer, error);
}).afterRequest(history->sendRequestId }).afterRequest(history->sendRequestId
).send(); ).send();
} }
@ -5416,9 +5428,10 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
| (IsSilentPost(sample, album->silent) | (IsSilentPost(sample, album->silent)
? MTPmessages_SendMultiMedia::Flag::f_silent ? MTPmessages_SendMultiMedia::Flag::f_silent
: MTPmessages_SendMultiMedia::Flag(0)); : MTPmessages_SendMultiMedia::Flag(0));
const auto peer = history->peer;
history->sendRequestId = request(MTPmessages_SendMultiMedia( history->sendRequestId = request(MTPmessages_SendMultiMedia(
MTP_flags(flags), MTP_flags(flags),
history->peer->input, peer->input,
MTP_int(replyTo), MTP_int(replyTo),
MTP_vector<MTPInputSingleMedia>(medias) MTP_vector<MTPInputSingleMedia>(medias)
)).done([=](const MTPUpdates &result) { )).done([=](const MTPUpdates &result) {
@ -5426,7 +5439,7 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
applyUpdates(result); applyUpdates(result);
}).fail([=](const RPCError &error) { }).fail([=](const RPCError &error) {
_sendingAlbums.remove(groupId); _sendingAlbums.remove(groupId);
sendMessageFail(error); sendMessageFail(peer, error);
}).afterRequest(history->sendRequestId }).afterRequest(history->sendRequestId
).send(); ).send();
} }

View File

@ -662,7 +662,9 @@ private:
not_null<ChannelData*> channel, not_null<ChannelData*> channel,
not_null<UserData*> from); not_null<UserData*> from);
void sendMessageFail(const RPCError &error); void sendMessageFail(
not_null<PeerData*> peer,
const RPCError &error);
void uploadAlbumMedia( void uploadAlbumMedia(
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
const MessageGroupId &groupId, const MessageGroupId &groupId,

View File

@ -1365,11 +1365,13 @@ SendFilesBox::SendFilesBox(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
Storage::PreparedList &&list, Storage::PreparedList &&list,
const TextWithTags &caption, const TextWithTags &caption,
CompressConfirm compressed) CompressConfirm compressed,
SendLimit limit)
: _controller(controller) : _controller(controller)
, _list(std::move(list)) , _list(std::move(list))
, _compressConfirmInitial(compressed) , _compressConfirmInitial(compressed)
, _compressConfirm(compressed) , _compressConfirm(compressed)
, _sendLimit(limit)
, _caption( , _caption(
this, this,
st::confirmCaptionArea, st::confirmCaptionArea,
@ -1514,9 +1516,24 @@ void SendFilesBox::initSendWay() {
} }
void SendFilesBox::updateCaptionPlaceholder() { void SendFilesBox::updateCaptionPlaceholder() {
if (_caption) { if (!_caption) {
const auto sendWay = _sendWay->value(); return;
}
const auto sendWay = _sendWay->value();
const auto isAlbum = (sendWay == SendFilesWay::Album);
const auto compressImages = (sendWay != SendFilesWay::Files);
if (!_list.canAddCaption(isAlbum, compressImages)
&& _sendLimit == SendLimit::One) {
_caption->hide();
if (_emojiToggle) {
_emojiToggle->hide();
}
} else {
_caption->setPlaceholder(FieldPlaceholder(_list, sendWay)); _caption->setPlaceholder(FieldPlaceholder(_list, sendWay));
_caption->show();
if (_emojiToggle) {
_emojiToggle->show();
}
} }
} }
@ -1640,6 +1657,8 @@ void SendFilesBox::setupCaption() {
} }
void SendFilesBox::setupEmojiPanel() { void SendFilesBox::setupEmojiPanel() {
Expects(_caption != nullptr);
const auto container = getDelegate()->outerContainer(); const auto container = getDelegate()->outerContainer();
_emojiPanel = base::make_unique_q<ChatHelpers::TabbedPanel>( _emojiPanel = base::make_unique_q<ChatHelpers::TabbedPanel>(
container, container,
@ -1663,6 +1682,7 @@ void SendFilesBox::setupEmojiPanel() {
[=](not_null<QEvent*> event) { return emojiFilter(event); })); [=](not_null<QEvent*> event) { return emojiFilter(event); }));
_emojiToggle.create(this, st::boxAttachEmoji); _emojiToggle.create(this, st::boxAttachEmoji);
_emojiToggle->setVisible(!_caption->isHidden());
_emojiToggle->installEventFilter(_emojiPanel); _emojiToggle->installEventFilter(_emojiPanel);
_emojiToggle->addClickHandler([=] { _emojiToggle->addClickHandler([=] {
_emojiPanel->toggleAnimated(); _emojiPanel->toggleAnimated();
@ -1905,7 +1925,7 @@ void SendFilesBox::send(bool ctrlShiftEnter) {
applyAlbumOrder(); applyAlbumOrder();
_confirmed = true; _confirmed = true;
if (_confirmedCallback) { if (_confirmedCallback) {
auto caption = _caption auto caption = (_caption && !_caption->isHidden())
? _caption->getTextWithAppliedMarkdown() ? _caption->getTextWithAppliedMarkdown()
: TextWithTags(); : TextWithTags();
_confirmedCallback( _confirmedCallback(

View File

@ -43,12 +43,17 @@ enum class SendFilesWay {
class SendFilesBox : public BoxContent { class SendFilesBox : public BoxContent {
public: public:
enum class SendLimit {
One,
Many
};
SendFilesBox( SendFilesBox(
QWidget*, QWidget*,
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
Storage::PreparedList &&list, Storage::PreparedList &&list,
const TextWithTags &caption, const TextWithTags &caption,
CompressConfirm compressed); CompressConfirm compressed,
SendLimit limit);
void setConfirmedCallback( void setConfirmedCallback(
Fn<void( Fn<void(
@ -116,6 +121,7 @@ private:
CompressConfirm _compressConfirmInitial = CompressConfirm::None; CompressConfirm _compressConfirmInitial = CompressConfirm::None;
CompressConfirm _compressConfirm = CompressConfirm::None; CompressConfirm _compressConfirm = CompressConfirm::None;
SendLimit _sendLimit = SendLimit::Many;
Fn<void( Fn<void(
Storage::PreparedList &&list, Storage::PreparedList &&list,

View File

@ -74,9 +74,10 @@ void ApplyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d) {
Local::writeInstalledStickers(); Local::writeInstalledStickers();
Local::writeArchivedStickers(); Local::writeArchivedStickers();
Ui::Toast::Config toast; auto toast = Ui::Toast::Config();
toast.text = tr::lng_stickers_packs_archived(tr::now); toast.text = tr::lng_stickers_packs_archived(tr::now);
toast.maxWidth = st::stickersToastMaxWidth; toast.maxWidth = toast.minWidth = st::stickersToastMaxWidth;
toast.multiline = true;
toast.padding = st::stickersToastPadding; toast.padding = st::stickersToastPadding;
Ui::Toast::Show(toast); Ui::Toast::Show(toast);
// Ui::show(Box<StickersBox>(archived), LayerOption::KeepOther); // Ui::show(Box<StickersBox>(archived), LayerOption::KeepOther);

View File

@ -95,7 +95,8 @@ public:
| MTPDchannel::Flag::f_megagroup | MTPDchannel::Flag::f_megagroup
| MTPDchannel::Flag::f_restricted | MTPDchannel::Flag::f_restricted
| MTPDchannel::Flag::f_signatures | MTPDchannel::Flag::f_signatures
| MTPDchannel::Flag::f_username; | MTPDchannel::Flag::f_username
| MTPDchannel::Flag::f_slowmode_enabled;
using Flags = Data::Flags< using Flags = Data::Flags<
MTPDchannel::Flags, MTPDchannel::Flags,
kEssentialFlags>; kEssentialFlags>;

View File

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_photo.h" #include "data/data_photo.h"
#include "data/data_folder.h" #include "data/data_folder.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "base/unixtime.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "observer_peer.h" #include "observer_peer.h"
#include "apiwrap.h" #include "apiwrap.h"
@ -701,6 +702,26 @@ bool PeerData::canRevokeFullHistory() const {
&& (Global::RevokePrivateTimeLimit() == 0x7FFFFFFF); && (Global::RevokePrivateTimeLimit() == 0x7FFFFFFF);
} }
bool PeerData::slowmodeApplied() const {
if (const auto channel = asChannel()) {
return !channel->amCreator()
&& !channel->hasAdminRights()
&& (channel->flags() & MTPDchannel::Flag::f_slowmode_enabled);
}
return false;
}
int PeerData::slowmodeSecondsLeft() const {
if (const auto channel = asChannel()) {
if (const auto last = channel->slowmodeLastMessage()) {
const auto seconds = channel->slowmodeSeconds();
const auto now = base::unixtime::now();
return std::max(seconds - (now - last), 0);
}
}
return 0;
}
namespace Data { namespace Data {
std::vector<ChatRestrictions> ListOfRestrictions() { std::vector<ChatRestrictions> ListOfRestrictions() {

View File

@ -177,6 +177,8 @@ public:
[[nodiscard]] Data::RestrictionCheckResult amRestricted( [[nodiscard]] Data::RestrictionCheckResult amRestricted(
ChatRestriction right) const; ChatRestriction right) const;
[[nodiscard]] bool canRevokeFullHistory() const; [[nodiscard]] bool canRevokeFullHistory() const;
[[nodiscard]] bool slowmodeApplied() const;
[[nodiscard]] int slowmodeSecondsLeft() const;
[[nodiscard]] UserData *asUser(); [[nodiscard]] UserData *asUser();
[[nodiscard]] const UserData *asUser() const; [[nodiscard]] const UserData *asUser() const;

View File

@ -580,3 +580,5 @@ historyAudioInDownload: icon {{ "history_audio_download", historyFileInIconFg }}
historyAudioInDownloadSelected: icon {{ "history_audio_download", historyFileInIconFgSelected }}; historyAudioInDownloadSelected: icon {{ "history_audio_download", historyFileInIconFgSelected }};
historyAudioOutDownload: icon {{ "history_audio_download", historyFileOutIconFg }}; historyAudioOutDownload: icon {{ "history_audio_download", historyFileOutIconFg }};
historyAudioOutDownloadSelected: icon {{ "history_audio_download", historyFileOutIconFgSelected }}; historyAudioOutDownloadSelected: icon {{ "history_audio_download", historyFileOutIconFgSelected }};
historySlowmodeCounterMargins: margins(0px, 0px, 10px, 0px);

View File

@ -236,6 +236,21 @@ object_ptr<Ui::FlatButton> SetupDiscussButton(
return result; return result;
} }
void ShowSlowmodeToast(const QString &text) {
auto config = Ui::Toast::Config();
config.multiline = true;
config.minWidth = st::msgMinWidth;
config.text = text;
Ui::Toast::Show(config);
}
void ShowSlowmodeToast(int seconds) {
ShowSlowmodeToast(tr::lng_slowmode_enabled(
tr::now,
lt_left,
formatDurationWords(seconds)));
}
} // namespace } // namespace
HistoryWidget::HistoryWidget( HistoryWidget::HistoryWidget(
@ -2800,11 +2815,14 @@ void HistoryWidget::hideSelectorControlsAnimated() {
} }
void HistoryWidget::send(Qt::KeyboardModifiers modifiers) { void HistoryWidget::send(Qt::KeyboardModifiers modifiers) {
if (!_history) return; if (!_history) {
return;
if (_editMsgId) { } else if (_editMsgId) {
saveEditMsg(); saveEditMsg();
return; return;
} else if (const auto left = _peer->slowmodeSecondsLeft()) {
ShowSlowmodeToast(left);
return;
} }
const auto webPageId = _previewCancelled const auto webPageId = _previewCancelled
@ -2818,6 +2836,17 @@ void HistoryWidget::send(Qt::KeyboardModifiers modifiers) {
message.replyTo = replyToId(); message.replyTo = replyToId();
message.webPageId = webPageId; message.webPageId = webPageId;
message.handleSupportSwitch = Support::HandleSwitch(modifiers); message.handleSupportSwitch = Support::HandleSwitch(modifiers);
if (_peer->slowmodeApplied()) {
if (_toForward.size() > 1
|| (!_toForward.empty()
&& !message.textWithTags.text.isEmpty())
|| (message.textWithTags.text.size() > MaxMessageSize)) {
ShowSlowmodeToast(tr::lng_slowmode_no_many(tr::now));
return;
}
}
session().api().sendMessage(std::move(message)); session().api().sendMessage(std::move(message));
clearFieldText(); clearFieldText();
@ -3016,13 +3045,23 @@ void HistoryWidget::chooseAttach() {
} else if (const auto error = Data::RestrictionError( } else if (const auto error = Data::RestrictionError(
_peer, _peer,
ChatRestriction::f_send_media)) { ChatRestriction::f_send_media)) {
Ui::show(Box<InformBox>(*error)); Ui::Toast::Show(*error);
return;
} else if (const auto left = _peer->slowmodeSecondsLeft()) {
ShowSlowmodeToast(left);
return; return;
} }
auto filter = FileDialog::AllFilesFilter() + qsl(";;Image files (*") + cImgExtensions().join(qsl(" *")) + qsl(")"); const auto filter = FileDialog::AllFilesFilter()
+ qsl(";;Image files (*")
+ cImgExtensions().join(qsl(" *"))
+ qsl(")");
FileDialog::GetOpenPaths(this, tr::lng_choose_files(tr::now), filter, crl::guard(this, [this](FileDialog::OpenResult &&result) { const auto method = _peer->slowmodeApplied()
? &FileDialog::GetOpenPath
: &FileDialog::GetOpenPaths;
method(this, tr::lng_choose_files(tr::now), filter, crl::guard(this, [=](
FileDialog::OpenResult &&result) {
if (result.paths.isEmpty() && result.remoteContent.isEmpty()) { if (result.paths.isEmpty() && result.remoteContent.isEmpty()) {
return; return;
} }
@ -3052,14 +3091,12 @@ void HistoryWidget::chooseAttach() {
confirmSendingFiles(std::move(list), CompressConfirm::No); confirmSendingFiles(std::move(list), CompressConfirm::No);
} }
} }
})); }), nullptr);
} }
void HistoryWidget::sendButtonClicked() { void HistoryWidget::sendButtonClicked() {
const auto type = _send->type(); const auto type = _send->type();
if (type == Ui::SendButton::Type::Slowmode) { if (type == Ui::SendButton::Type::Cancel) {
return;
} else if (type == Ui::SendButton::Type::Cancel) {
onInlineBotCancel(); onInlineBotCancel();
} else if (type != Ui::SendButton::Type::Record) { } else if (type != Ui::SendButton::Type::Record) {
send(); send();
@ -3539,17 +3576,9 @@ void HistoryWidget::updateSendButtonType() {
_send->setType(type); _send->setType(type);
const auto delay = [&] { const auto delay = [&] {
if (type == Type::Cancel || type == Type::Save) { return (type != Type::Cancel && type != Type::Save && _peer)
return 0; ? _peer->slowmodeSecondsLeft()
} : 0;
const auto channel = _peer ? _peer->asChannel() : nullptr;
const auto last = channel ? channel->slowmodeLastMessage() : 0;
if (!last) {
return 0;
}
const auto seconds = channel->slowmodeSeconds();
const auto now = base::unixtime::now();
return std::max(seconds - (now - last), 0);
}(); }();
_send->setSlowmodeDelay(delay); _send->setSlowmodeDelay(delay);
@ -3963,6 +3992,14 @@ bool HistoryWidget::showSendingFilesError(
} else if (!canWriteMessage()) { } else if (!canWriteMessage()) {
return tr::lng_forward_send_files_cant(tr::now); return tr::lng_forward_send_files_cant(tr::now);
} }
if (list.files.size() > 1 && _peer->slowmodeApplied()) {
return tr::lng_slowmode_no_many(tr::now);
} else if (const auto left = _peer->slowmodeSecondsLeft()) {
return tr::lng_slowmode_enabled(
tr::now,
lt_left,
formatDurationWords(left));
}
using Error = Storage::PreparedList::Error; using Error = Storage::PreparedList::Error;
switch (list.error) { switch (list.error) {
case Error::None: return QString(); case Error::None: return QString();
@ -3983,7 +4020,7 @@ bool HistoryWidget::showSendingFilesError(
return false; return false;
} }
Ui::show(Box<InformBox>(text)); ShowSlowmodeToast(text);
return true; return true;
} }
@ -4024,11 +4061,13 @@ bool HistoryWidget::confirmSendingFiles(
const auto position = cursor.position(); const auto position = cursor.position();
const auto anchor = cursor.anchor(); const auto anchor = cursor.anchor();
const auto text = _field->getTextWithTags(); const auto text = _field->getTextWithTags();
using SendLimit = SendFilesBox::SendLimit;
auto box = Box<SendFilesBox>( auto box = Box<SendFilesBox>(
controller(), controller(),
std::move(list), std::move(list),
text, text,
boxCompressConfirm); boxCompressConfirm,
_peer->slowmodeApplied() ? SendLimit::One : SendLimit::Many);
_field->setTextWithTags({}); _field->setTextWithTags({});
box->setConfirmedCallback(crl::guard(this, [=]( box->setConfirmedCallback(crl::guard(this, [=](
Storage::PreparedList &&list, Storage::PreparedList &&list,
@ -4172,6 +4211,17 @@ void HistoryWidget::uploadFilesAfterConfirmation(
std::shared_ptr<SendingAlbum> album) { std::shared_ptr<SendingAlbum> album) {
Assert(canWriteMessage()); Assert(canWriteMessage());
const auto isAlbum = (album != nullptr);
const auto compressImages = (type == SendMediaType::Photo);
if (_peer->slowmodeApplied()
&& (list.files.size() > 1
|| (!list.files.empty()
&& !caption.text.isEmpty()
&& !list.canAddCaption(isAlbum, compressImages)))) {
ShowSlowmodeToast(tr::lng_slowmode_no_many(tr::now));
return;
}
auto options = ApiWrap::SendOptions(_history); auto options = ApiWrap::SendOptions(_history);
options.replyTo = replyTo; options.replyTo = replyTo;
session().api().sendFiles( session().api().sendFiles(
@ -5292,6 +5342,9 @@ void HistoryWidget::sendInlineResult(
not_null<UserData*> bot) { not_null<UserData*> bot) {
if (!_peer || !_peer->canWrite()) { if (!_peer || !_peer->canWrite()) {
return; return;
} else if (const auto left = _peer->slowmodeSecondsLeft()) {
ShowSlowmodeToast(left);
return;
} }
auto errorText = result->getErrorOnSend(_history); auto errorText = result->getErrorOnSend(_history);
@ -5445,6 +5498,9 @@ bool HistoryWidget::sendExistingDocument(
return false; return false;
} else if (!_peer || !_peer->canWrite()) { } else if (!_peer || !_peer->canWrite()) {
return false; return false;
} else if (const auto left = _peer->slowmodeSecondsLeft()) {
ShowSlowmodeToast(left);
return false;
} }
const auto origin = document->stickerOrGifOrigin(); const auto origin = document->stickerOrGifOrigin();
@ -5480,6 +5536,9 @@ bool HistoryWidget::sendExistingPhoto(
return false; return false;
} else if (!_peer || !_peer->canWrite()) { } else if (!_peer || !_peer->canWrite()) {
return false; return false;
} else if (const auto left = _peer->slowmodeSecondsLeft()) {
ShowSlowmodeToast(left);
return false;
} }
auto options = ApiWrap::SendOptions(_history); auto options = ApiWrap::SendOptions(_history);

View File

@ -420,7 +420,10 @@ void SendButton::paintSend(Painter &p, bool over) {
void SendButton::paintSlowmode(Painter &p) { void SendButton::paintSlowmode(Painter &p) {
p.setFont(st::normalFont); p.setFont(st::normalFont);
p.setPen(st::windowSubTextFg); p.setPen(st::windowSubTextFg);
p.drawText(rect(), _slowmodeDelayText, style::al_center); p.drawText(
rect().marginsRemoved(st::historySlowmodeCounterMargins),
_slowmodeDelayText,
style::al_center);
} }
void SendButton::onStateChanged(State was, StateChangeSource source) { void SendButton::onStateChanged(State was, StateChangeSource source) {

View File

@ -13,16 +13,19 @@ namespace Ui {
namespace Toast { namespace Toast {
namespace internal { namespace internal {
class Manager; class Manager;
class Widget; class Widget;
} // namespace internal } // namespace internal
static constexpr const int DefaultDuration = 1500; static constexpr const int DefaultDuration = 1500;
struct Config { struct Config {
QString text; QString text;
int durationMs = DefaultDuration;
int maxWidth = 0;
QMargins padding; QMargins padding;
int durationMs = DefaultDuration;
int minWidth = 0;
int maxWidth = 0;
int maxLines = 16;
bool multiline = false;
}; };
void Show(QWidget *parent, const Config &config); void Show(QWidget *parent, const Config &config);
void Show(const Config &config); void Show(const Config &config);

View File

@ -12,23 +12,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui { namespace Ui {
namespace Toast { namespace Toast {
namespace internal { namespace internal {
namespace {
constexpr auto kToastMaxLines = 16;
} // namespace
Widget::Widget(QWidget *parent, const Config &config) : TWidget(parent) Widget::Widget(QWidget *parent, const Config &config) : TWidget(parent)
, _multiline(config.maxWidth > 0) , _multiline(config.multiline)
, _maxWidth((config.maxWidth > 0) ? config.maxWidth : st::toastMaxWidth) , _maxWidth((config.maxWidth > 0) ? config.maxWidth : st::toastMaxWidth)
, _padding((config.padding.left() > 0) ? config.padding : st::toastPadding) , _padding((config.padding.left() > 0) ? config.padding : st::toastPadding)
, _maxTextWidth(_maxWidth - _padding.left() - _padding.right()) , _maxTextWidth(_maxWidth - _padding.left() - _padding.right())
, _text(_multiline ? _maxTextWidth : QFIXED_MAX) { , _maxTextHeight(
TextParseOptions toastOptions = { 0, _maxTextWidth, st::toastTextStyle.font->height, Qt::LayoutDirectionAuto }; st::toastTextStyle.font->height * (_multiline ? config.maxLines : 1))
if (_multiline) { , _text(_multiline ? config.minWidth : QFIXED_MAX) {
toastOptions.maxh *= kToastMaxLines; const auto toastOptions = TextParseOptions{
} 0,
_text.setText(st::toastTextStyle, _multiline ? config.text : TextUtilities::SingleLine(config.text), toastOptions); _maxTextWidth,
_maxTextHeight,
Qt::LayoutDirectionAuto
};
_text.setText(
st::toastTextStyle,
_multiline ? config.text : TextUtilities::SingleLine(config.text),
toastOptions);
setAttribute(Qt::WA_TransparentForMouseEvents); setAttribute(Qt::WA_TransparentForMouseEvents);
@ -41,9 +43,10 @@ void Widget::onParentResized() {
accumulate_min(newWidth, _padding.left() + _text.maxWidth() + _padding.right()); accumulate_min(newWidth, _padding.left() + _text.maxWidth() + _padding.right());
accumulate_min(newWidth, parentWidget()->width() - 2 * st::toastMinMargin); accumulate_min(newWidth, parentWidget()->width() - 2 * st::toastMinMargin);
_textWidth = newWidth - _padding.left() - _padding.right(); _textWidth = newWidth - _padding.left() - _padding.right();
auto maxHeight = kToastMaxLines * st::toastTextStyle.font->height; const auto textHeight = _multiline
auto textHeight = _multiline ? qMin(_text.countHeight(_textWidth), maxHeight) : _text.minHeight(); ? qMin(_text.countHeight(_textWidth), _maxTextHeight)
auto newHeight = _padding.top() + textHeight + _padding.bottom(); : _text.minHeight();
const auto newHeight = _padding.top() + textHeight + _padding.bottom();
setGeometry((parentWidget()->width() - newWidth) / 2, (parentWidget()->height() - newHeight) / 2, newWidth, newHeight); setGeometry((parentWidget()->width() - newWidth) / 2, (parentWidget()->height() - newHeight) / 2, newWidth, newHeight);
} }
@ -58,7 +61,7 @@ void Widget::paintEvent(QPaintEvent *e) {
p.setOpacity(_shownLevel); p.setOpacity(_shownLevel);
App::roundRect(p, rect(), st::toastBg, ImageRoundRadius::Large); App::roundRect(p, rect(), st::toastBg, ImageRoundRadius::Large);
auto lines = _multiline ? kToastMaxLines : 1; const auto lines = _maxTextHeight / st::toastTextStyle.font->height;
p.setPen(st::toastFg); p.setPen(st::toastFg);
_text.drawElided(p, _padding.left(), _padding.top(), _textWidth + 1, lines); _text.drawElided(p, _padding.left(), _padding.top(), _textWidth + 1, lines);
} }

View File

@ -34,6 +34,7 @@ private:
QMargins _padding; QMargins _padding;
int _maxTextWidth = 0; int _maxTextWidth = 0;
int _maxTextHeight = 0;
int _textWidth = 0; int _textWidth = 0;
Text::String _text; Text::String _text;

View File

@ -575,9 +575,10 @@ void FolderFiller::addTogglesForArchive() {
}); });
_addAction(tr::lng_context_archive_to_menu(tr::now), [=] { _addAction(tr::lng_context_archive_to_menu(tr::now), [=] {
Ui::Toast::Config toast; auto toast = Ui::Toast::Config();
toast.text = tr::lng_context_archive_to_menu_info(tr::now); toast.text = tr::lng_context_archive_to_menu_info(tr::now);
toast.maxWidth = st::boxWideWidth; toast.minWidth = toast.maxWidth = st::boxWideWidth;
toast.multiline = true;
toast.durationMs = kArchivedToastDuration; toast.durationMs = kArchivedToastDuration;
Ui::Toast::Show(toast); Ui::Toast::Show(toast);
@ -896,11 +897,12 @@ void PeerMenuAddMuteAction(
// //
void ToggleHistoryArchived(not_null<History*> history, bool archived) { void ToggleHistoryArchived(not_null<History*> history, bool archived) {
const auto callback = [=] { const auto callback = [=] {
Ui::Toast::Config toast; auto toast = Ui::Toast::Config();
toast.text = archived toast.text = archived
? tr::lng_archived_added(tr::now) ? tr::lng_archived_added(tr::now)
: tr::lng_archived_removed(tr::now); : tr::lng_archived_removed(tr::now);
toast.maxWidth = st::boxWideWidth; toast.minWidth = toast.maxWidth = st::boxWideWidth;
toast.multiline = true;
if (archived) { if (archived) {
toast.durationMs = kArchivedToastDuration; toast.durationMs = kArchivedToastDuration;
} }