Track sending and failed messages.

This commit is contained in:
John Preston 2019-07-17 16:34:39 +02:00
parent 0005e0a3ce
commit c50ade565a
19 changed files with 427 additions and 316 deletions

View file

@ -0,0 +1,193 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "api/api_sending.h"
#include "base/unixtime.h"
#include "data/data_document.h"
#include "data/data_photo.h"
#include "data/data_channel.h" // ChannelData::addsSignature.
#include "data/data_user.h" // App::peerName(UserData*).
#include "data/data_session.h"
#include "data/data_file_origin.h"
#include "history/history.h"
#include "history/history_message.h" // NewMessageFlags.
#include "ui/text/text_entity.h" // TextWithEntities.
#include "auth_session.h"
#include "mainwidget.h"
#include "apiwrap.h"
namespace Api {
namespace {
template <typename MediaData>
void SendExistingMedia(
not_null<History*> history,
not_null<MediaData*> media,
const MTPInputMedia &inputMedia,
Data::FileOrigin origin,
TextWithEntities caption,
MsgId replyToId) {
const auto peer = history->peer;
const auto session = &history->session();
const auto api = &session->api();
auto options = ApiWrap::SendOptions(history);
options.clearDraft = false;
options.replyTo = replyToId;
options.generateLocal = true;
api->sendAction(options);
const auto newId = FullMsgId(peerToChannel(peer->id), clientMsgId());
const auto randomId = rand_value<uint64>();
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
auto sendFlags = MTPmessages_SendMedia::Flags(0);
if (options.replyTo) {
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
}
bool channelPost = peer->isChannel() && !peer->isMegagroup();
bool silentPost = channelPost && session->data().notifySilentPosts(peer);
if (channelPost) {
flags |= MTPDmessage::Flag::f_views;
flags |= MTPDmessage::Flag::f_post;
}
if (!channelPost) {
flags |= MTPDmessage::Flag::f_from_id;
} else if (peer->asChannel()->addsSignature()) {
flags |= MTPDmessage::Flag::f_post_author;
}
if (silentPost) {
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
}
auto messageFromId = channelPost ? 0 : session->userId();
auto messagePostAuthor = channelPost
? App::peerName(session->user())
: QString();
TextUtilities::Trim(caption);
auto sentEntities = TextUtilities::EntitiesToMTP(
caption.entities,
TextUtilities::ConvertOption::SkipLocal);
if (!sentEntities.v.isEmpty()) {
sendFlags |= MTPmessages_SendMedia::Flag::f_entities;
}
const auto replyTo = options.replyTo;
const auto captionText = caption.text;
session->data().registerMessageRandomId(randomId, newId);
history->addNewLocalMessage(
newId.msg,
flags,
0,
replyTo,
base::unixtime::now(),
messageFromId,
messagePostAuthor,
media,
caption,
MTPReplyMarkup());
auto failHandler = std::make_shared<Fn<void(const RPCError&, QByteArray)>>();
auto performRequest = [=] {
const auto usedFileReference = media->fileReference();
history->sendRequestId = api->request(MTPmessages_SendMedia(
MTP_flags(sendFlags),
peer->input,
MTP_int(replyTo),
inputMedia,
MTP_string(captionText),
MTP_long(randomId),
MTPReplyMarkup(),
sentEntities
)).done([=](const MTPUpdates &result) {
api->applyUpdates(result, randomId);
}).fail([=](const RPCError &error) {
(*failHandler)(error, usedFileReference);
}).afterRequest(history->sendRequestId
).send();
};
*failHandler = [=](const RPCError &error, QByteArray usedFileReference) {
if (error.code() == 400
&& error.type().startsWith(qstr("FILE_REFERENCE_"))) {
api->refreshFileReference(origin, [=](const auto &result) {
if (media->fileReference() != usedFileReference) {
performRequest();
} else {
api->sendMessageFail(error, peer, newId);
}
});
} else {
api->sendMessageFail(error, peer, newId);
}
};
performRequest();
if (const auto main = App::main()) {
main->finishForwarding(history);
}
}
} // namespace
void SendExistingDocument(
not_null<History*> history,
not_null<DocumentData*> document) {
SendExistingDocument(history, document, {});
}
void SendExistingDocument(
not_null<History*> history,
not_null<DocumentData*> document,
TextWithEntities caption,
MsgId replyToId) {
SendExistingMedia(
history,
document,
MTP_inputMediaDocument(
MTP_flags(0),
document->mtpInput(),
MTPint()),
document->stickerOrGifOrigin(),
caption,
replyToId);
if (document->sticker()) {
if (const auto main = App::main()) {
main->incrementSticker(document);
document->session().data().notifyRecentStickersUpdated();
}
}
}
void SendExistingPhoto(
not_null<History*> history,
not_null<PhotoData*> photo) {
SendExistingPhoto(history, photo, {});
}
void SendExistingPhoto(
not_null<History*> history,
not_null<PhotoData*> photo,
TextWithEntities caption,
MsgId replyToId) {
SendExistingMedia(
history,
photo,
MTP_inputMediaPhoto(
MTP_flags(0),
photo->mtpInput(),
MTPint()),
Data::FileOrigin(),
caption,
replyToId);
}
} // namespace Api

View file

@ -0,0 +1,36 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class History;
class DocumentData;
struct TextWithEntities;
namespace Api {
void SendExistingDocument(
not_null<History*> history,
not_null<DocumentData*> document);
void SendExistingDocument(
not_null<History*> history,
not_null<DocumentData*> document,
TextWithEntities caption,
MsgId replyToId = 0);
void SendExistingPhoto(
not_null<History*> history,
not_null<PhotoData*> photo);
void SendExistingPhoto(
not_null<History*> history,
not_null<PhotoData*> photo,
TextWithEntities caption,
MsgId replyToId = 0);
} // namespace Api

View file

@ -549,8 +549,9 @@ void ApiWrap::toggleHistoryArchived(
//}
void ApiWrap::sendMessageFail(
const RPCError &error,
not_null<PeerData*> peer,
const RPCError &error) {
FullMsgId itemId) {
if (error.type() == qstr("PEER_FLOOD")) {
Ui::show(Box<InformBox>(
PeerFloodErrorText(PeerFloodType::Send)));
@ -571,6 +572,9 @@ void ApiWrap::sendMessageFail(
base::unixtime::now() - (left - seconds));
}
}
if (const auto item = _session->data().message(itemId)) {
item->sendFailed();
}
}
void ApiWrap::requestMessageData(ChannelData *channel, MsgId msgId, RequestMessageDataCallback callback) {
@ -4452,6 +4456,7 @@ void ApiWrap::forwardMessages(
auto currentGroupId = items.front()->groupId();
auto ids = QVector<MTPint>();
auto randomIds = QVector<MTPlong>();
auto localIds = std::unique_ptr<std::vector<FullMsgId>>();
const auto sendAccumulated = [&] {
if (shared) {
@ -4473,12 +4478,21 @@ void ApiWrap::forwardMessages(
if (shared && !--shared->requestsLeft) {
shared->callback();
}
}).fail([=, ids = std::move(localIds)](const RPCError &error) {
if (ids) {
for (const auto &itemId : *ids) {
sendMessageFail(error, peer, itemId);
}
} else {
sendMessageFail(error, peer);
}
}).afterRequest(
history->sendRequestId
).send();
ids.resize(0);
randomIds.resize(0);
localIds = nullptr;
};
ids.reserve(count);
@ -4486,7 +4500,7 @@ void ApiWrap::forwardMessages(
for (const auto item : items) {
auto randomId = rand_value<uint64>();
if (genClientSideMessage) {
if (auto message = item->toHistoryMessage()) {
if (const auto message = item->toHistoryMessage()) {
const auto newId = FullMsgId(
peerToChannel(peer->id),
clientMsgId());
@ -4497,7 +4511,7 @@ void ApiWrap::forwardMessages(
const auto messagePostAuthor = channelPost
? App::peerName(self)
: QString();
history->addNewForwarded(
history->addNewLocalMessage(
newId.msg,
flags,
base::unixtime::now(),
@ -4505,6 +4519,10 @@ void ApiWrap::forwardMessages(
messagePostAuthor,
message);
_session->data().registerMessageRandomId(randomId, newId);
if (!localIds) {
localIds = std::make_unique<std::vector<FullMsgId>>();
}
localIds->push_back(newId);
}
}
const auto newFrom = item->history()->peer;
@ -4864,7 +4882,7 @@ void ApiWrap::editUploadedFile(
Box<InformBox>(tr::lng_edit_media_invalid_file(tr::now)),
LayerOption::KeepOther);
} else {
sendMessageFail(peer, error);
sendMessageFail(error, peer);
}
}).send();
}
@ -4997,7 +5015,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
if (error.type() == qstr("MESSAGE_EMPTY")) {
lastMessage->destroy();
} else {
sendMessageFail(peer, error);
sendMessageFail(error, peer, newId);
}
history->clearSentDraftText(QString());
}).afterRequest(history->sendRequestId
@ -5110,7 +5128,7 @@ void ApiWrap::sendInlineResult(
applyUpdates(result, randomId);
history->clearSentDraftText(QString());
}).fail([=](const RPCError &error) {
sendMessageFail(peer, error);
sendMessageFail(error, peer, newId);
history->clearSentDraftText(QString());
}).afterRequest(history->sendRequestId
).send();
@ -5120,116 +5138,6 @@ void ApiWrap::sendInlineResult(
}
}
void ApiWrap::sendExistingDocument(
not_null<DocumentData*> document,
Data::FileOrigin origin,
TextWithEntities caption,
const SendOptions &options) {
sendAction(options);
const auto history = options.history;
const auto peer = history->peer;
const auto newId = FullMsgId(peerToChannel(peer->id), clientMsgId());
const auto randomId = rand_value<uint64>();
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
auto sendFlags = MTPmessages_SendMedia::Flags(0);
if (options.replyTo) {
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
}
bool channelPost = peer->isChannel() && !peer->isMegagroup();
bool silentPost = channelPost
&& _session->data().notifySilentPosts(peer);
if (channelPost) {
flags |= MTPDmessage::Flag::f_views;
flags |= MTPDmessage::Flag::f_post;
}
if (!channelPost) {
flags |= MTPDmessage::Flag::f_from_id;
} else if (peer->asChannel()->addsSignature()) {
flags |= MTPDmessage::Flag::f_post_author;
}
if (silentPost) {
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
}
auto messageFromId = channelPost ? 0 : _session->userId();
auto messagePostAuthor = channelPost
? App::peerName(_session->user())
: QString();
TextUtilities::Trim(caption);
auto sentEntities = TextUtilities::EntitiesToMTP(
caption.entities,
TextUtilities::ConvertOption::SkipLocal);
if (!sentEntities.v.isEmpty()) {
sendFlags |= MTPmessages_SendMedia::Flag::f_entities;
}
const auto replyTo = options.replyTo;
const auto captionText = caption.text;
_session->data().registerMessageRandomId(randomId, newId);
history->addNewDocument(
newId.msg,
flags,
0,
replyTo,
base::unixtime::now(),
messageFromId,
messagePostAuthor,
document,
caption,
MTPReplyMarkup());
auto failHandler = std::make_shared<Fn<void(const RPCError&, QByteArray)>>();
auto performRequest = [=] {
const auto usedFileReference = document->fileReference();
history->sendRequestId = request(MTPmessages_SendMedia(
MTP_flags(sendFlags),
peer->input,
MTP_int(replyTo),
MTP_inputMediaDocument(
MTP_flags(0),
document->mtpInput(),
MTPint()),
MTP_string(captionText),
MTP_long(randomId),
MTPReplyMarkup(),
sentEntities
)).done([=](const MTPUpdates &result) {
applyUpdates(result, randomId);
}).fail([=](const RPCError &error) {
(*failHandler)(error, usedFileReference);
}).afterRequest(history->sendRequestId
).send();
};
*failHandler = [=](const RPCError &error, QByteArray usedFileReference) {
if (error.code() == 400
&& error.type().startsWith(qstr("FILE_REFERENCE_"))) {
auto refreshed = [=](const UpdatedFileReferences &data) {
if (document->fileReference() != usedFileReference) {
performRequest();
} else {
sendMessageFail(peer, error);
}
};
refreshFileReference(origin, std::move(refreshed));
} else {
sendMessageFail(peer, error);
}
};
performRequest();
if (const auto main = App::main()) {
main->finishForwarding(history);
if (document->sticker()) {
main->incrementSticker(document);
_session->data().notifyRecentStickersUpdated();
}
}
}
void ApiWrap::uploadAlbumMedia(
not_null<HistoryItem*> item,
const MessageGroupId &groupId,
@ -5341,6 +5249,7 @@ void ApiWrap::sendMediaWithRandomId(
: MTPmessages_SendMedia::Flag(0));
const auto peer = history->peer;
const auto itemId = item->fullId();
history->sendRequestId = request(MTPmessages_SendMedia(
MTP_flags(flags),
peer->input,
@ -5350,9 +5259,12 @@ void ApiWrap::sendMediaWithRandomId(
MTP_long(randomId),
MTPReplyMarkup(),
sentEntities
)).done([=](const MTPUpdates &result) { applyUpdates(result);
}).fail([=](const RPCError &error) { sendMessageFail(peer, error);
}).afterRequest(history->sendRequestId
)).done([=](const MTPUpdates &result) {
applyUpdates(result);
}).fail([=](const RPCError &error) {
sendMessageFail(error, peer, itemId);
}).afterRequest(
history->sendRequestId
).send();
}
@ -5438,9 +5350,15 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
_sendingAlbums.remove(groupId);
applyUpdates(result);
}).fail([=](const RPCError &error) {
_sendingAlbums.remove(groupId);
sendMessageFail(peer, error);
}).afterRequest(history->sendRequestId
if (const auto album = _sendingAlbums.take(groupId)) {
for (const auto &item : (*album)->items) {
sendMessageFail(error, peer, item.msgId);
}
} else {
sendMessageFail(error, peer);
}
}).afterRequest(
history->sendRequestId
).send();
}

View file

@ -459,11 +459,10 @@ public:
not_null<UserData*> bot,
not_null<InlineBots::Result*> data,
const SendOptions &options);
void sendExistingDocument(
not_null<DocumentData*> document,
Data::FileOrigin origin,
TextWithEntities caption,
const SendOptions &options);
void sendMessageFail(
const RPCError &error,
not_null<PeerData*> peer,
FullMsgId itemId = FullMsgId());
void requestSupportContact(FnMut<void(const MTPUser&)> callback);
@ -662,9 +661,6 @@ private:
not_null<ChannelData*> channel,
not_null<UserData*> from);
void sendMessageFail(
not_null<PeerData*> peer,
const RPCError &error);
void uploadAlbumMedia(
not_null<HistoryItem*> item,
const MessageGroupId &groupId,

View file

@ -29,8 +29,8 @@ namespace {
void ShareBotGame(not_null<UserData*> bot, not_null<PeerData*> chat) {
const auto history = chat->owner().historyLoaded(chat);
const auto randomId = rand_value<uint64>();
const auto requestId = MTP::send(
MTPmessages_SendMedia(
const auto api = &chat->session().api();
const auto requestId = api->request(MTPmessages_SendMedia(
MTP_flags(0),
chat->input,
MTP_int(0),
@ -41,12 +41,15 @@ void ShareBotGame(not_null<UserData*> bot, not_null<PeerData*> chat) {
MTP_string(),
MTP_long(randomId),
MTPReplyMarkup(),
MTPVector<MTPMessageEntity>()),
App::main()->rpcDone(&MainWidget::sentUpdatesReceived),
App::main()->rpcFail(&MainWidget::sendMessageFail),
0,
0,
history ? history->sendRequestId : 0);
MTPVector<MTPMessageEntity>()
)).done([=](const MTPUpdates &result) {
api->applyUpdates(result, randomId);
}).fail([=](const RPCError &error) {
api->sendMessageFail(error, chat);
}).afterRequest(
history ? history->sendRequestId : 0
).send();
if (history) {
history->sendRequestId = requestId;
}

View file

@ -614,18 +614,6 @@ std::vector<not_null<HistoryItem*>> History::createItems(
return result;
}
not_null<HistoryItem*> History::addNewService(
MsgId msgId,
TimeId date,
const QString &text,
MTPDmessage::Flags flags,
bool unread) {
auto message = HistoryService::PreparedText { text };
return addNewItem(
new HistoryService(this, msgId, date, message, flags),
unread);
}
HistoryItem *History::addNewMessage(
const MTPMessage &msg,
NewMessageType type) {
@ -696,13 +684,13 @@ HistoryItem *History::addToHistory(const MTPMessage &msg) {
return createItem(msg, detachExistingItem);
}
not_null<HistoryItem*> History::addNewForwarded(
not_null<HistoryItem*> History::addNewLocalMessage(
MsgId id,
MTPDmessage::Flags flags,
TimeId date,
UserId from,
const QString &postAuthor,
not_null<HistoryMessage*> original) {
not_null<HistoryMessage*> forwardOriginal) {
return addNewItem(
owner().makeMessage(
this,
@ -711,11 +699,11 @@ not_null<HistoryItem*> History::addNewForwarded(
date,
from,
postAuthor,
original),
forwardOriginal),
true);
}
not_null<HistoryItem*> History::addNewDocument(
not_null<HistoryItem*> History::addNewLocalMessage(
MsgId id,
MTPDmessage::Flags flags,
UserId viaBotId,
@ -742,7 +730,7 @@ not_null<HistoryItem*> History::addNewDocument(
true);
}
not_null<HistoryItem*> History::addNewPhoto(
not_null<HistoryItem*> History::addNewLocalMessage(
MsgId id,
MTPDmessage::Flags flags,
UserId viaBotId,
@ -769,7 +757,7 @@ not_null<HistoryItem*> History::addNewPhoto(
true);
}
not_null<HistoryItem*> History::addNewGame(
not_null<HistoryItem*> History::addNewLocalMessage(
MsgId id,
MTPDmessage::Flags flags,
UserId viaBotId,
@ -1260,10 +1248,33 @@ void History::newItemAdded(not_null<HistoryItem*> item) {
void History::registerLocalMessage(not_null<HistoryItem*> item) {
_localMessages.emplace(item);
if (peer->isChannel()) {
Notify::peerUpdatedDelayed(
peer,
Notify::PeerUpdate::Flag::ChannelLocalMessages);
}
}
void History::unregisterLocalMessage(not_null<HistoryItem*> item) {
_localMessages.remove(item);
if (peer->isChannel()) {
Notify::peerUpdatedDelayed(
peer,
Notify::PeerUpdate::Flag::ChannelLocalMessages);
}
}
HistoryItem *History::latestSendingMessage() const {
auto sending = ranges::view::all(
_localMessages
) | ranges::view::filter([](not_null<HistoryItem*> item) {
return item->isSending();
});
const auto i = ranges::max_element(sending, ranges::less(), [](
not_null<HistoryItem*> item) {
return uint64(item->date()) << 32 | uint32(item->id);
});
return (i == sending.end()) ? nullptr : i->get();
}
HistoryBlock *History::prepareBlockForAddingItem() {

View file

@ -95,20 +95,14 @@ public:
HistoryItem *addNewMessage(const MTPMessage &msg, NewMessageType type);
HistoryItem *addToHistory(const MTPMessage &msg);
not_null<HistoryItem*> addNewService(
MsgId msgId,
TimeId date,
const QString &text,
MTPDmessage::Flags flags = 0,
bool newMsg = true);
not_null<HistoryItem*> addNewForwarded(
not_null<HistoryItem*> addNewLocalMessage(
MsgId id,
MTPDmessage::Flags flags,
TimeId date,
UserId from,
const QString &postAuthor,
not_null<HistoryMessage*> original);
not_null<HistoryItem*> addNewDocument(
not_null<HistoryMessage*> forwardOriginal);
not_null<HistoryItem*> addNewLocalMessage(
MsgId id,
MTPDmessage::Flags flags,
UserId viaBotId,
@ -119,7 +113,7 @@ public:
not_null<DocumentData*> document,
const TextWithEntities &caption,
const MTPReplyMarkup &markup);
not_null<HistoryItem*> addNewPhoto(
not_null<HistoryItem*> addNewLocalMessage(
MsgId id,
MTPDmessage::Flags flags,
UserId viaBotId,
@ -130,7 +124,7 @@ public:
not_null<PhotoData*> photo,
const TextWithEntities &caption,
const MTPReplyMarkup &markup);
not_null<HistoryItem*> addNewGame(
not_null<HistoryItem*> addNewLocalMessage(
MsgId id,
MTPDmessage::Flags flags,
UserId viaBotId,
@ -155,6 +149,7 @@ public:
void registerLocalMessage(not_null<HistoryItem*> item);
void unregisterLocalMessage(not_null<HistoryItem*> item);
[[nodiscard]] HistoryItem *latestSendingMessage() const;
MsgId readInbox();
void applyInboxReadUpdate(

View file

@ -39,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
#include "observer_peer.h"
#include "styles/style_dialogs.h"
#include "styles/style_history.h"
@ -664,6 +665,19 @@ MsgId HistoryItem::idOriginal() const {
return id;
}
void HistoryItem::sendFailed() {
Expects(_flags & MTPDmessage_ClientFlag::f_sending);
Expects(!(_flags & MTPDmessage_ClientFlag::f_failed));
_flags = (_flags | MTPDmessage_ClientFlag::f_failed)
& ~MTPDmessage_ClientFlag::f_sending;
if (history()->peer->isChannel()) {
Notify::peerUpdatedDelayed(
history()->peer,
Notify::PeerUpdate::Flag::ChannelLocalMessages);
}
}
bool HistoryItem::needCheck() const {
return out() || (id < 0 && history()->peer->isSelf());
}

View file

@ -122,7 +122,6 @@ public:
[[nodiscard]] bool hasUnreadMediaFlag() const;
void markMediaRead();
// For edit media in history_message.
virtual void returnSavedMedia() {};
void savePreviousMedia() {
@ -174,6 +173,13 @@ public:
bool isSilent() const {
return _flags & MTPDmessage::Flag::f_silent;
}
bool isSending() const {
return _flags & MTPDmessage_ClientFlag::f_sending;
}
bool hasFailed() const {
return _flags & MTPDmessage_ClientFlag::f_failed;
}
void sendFailed();
virtual int viewsCount() const {
return hasViews() ? 1 : -1;
}

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/history_widget.h"
#include "api/api_sending.h"
#include "boxes/confirm_box.h"
#include "boxes/send_files_box.h"
#include "boxes/share_box.h"
@ -236,7 +237,7 @@ object_ptr<Ui::FlatButton> SetupDiscussButton(
return result;
}
void ShowSlowmodeToast(const QString &text) {
void ShowErrorToast(const QString &text) {
auto config = Ui::Toast::Config();
config.multiline = true;
config.minWidth = st::msgMinWidth;
@ -244,13 +245,6 @@ void ShowSlowmodeToast(const QString &text) {
Ui::Toast::Show(config);
}
void ShowSlowmodeToast(int seconds) {
ShowSlowmodeToast(tr::lng_slowmode_enabled(
tr::now,
lt_left,
formatDurationWords(seconds)));
}
} // namespace
HistoryWidget::HistoryWidget(
@ -511,7 +505,9 @@ HistoryWidget::HistoryWidget(
| UpdateFlag::NotificationsEnabled
| UpdateFlag::ChannelAmIn
| UpdateFlag::ChannelPromotedChanged
| UpdateFlag::ChannelLinkedChat;
| UpdateFlag::ChannelLinkedChat
| UpdateFlag::ChannelSlowmode
| UpdateFlag::ChannelLocalMessages;
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(changes, [=](const Notify::PeerUpdate &update) {
if (update.peer == _peer) {
if (update.flags & UpdateFlag::RightsChanged) {
@ -552,6 +548,10 @@ HistoryWidget::HistoryWidget(
updateControlsGeometry();
this->update();
}
if (update.flags & (UpdateFlag::ChannelSlowmode
| UpdateFlag::ChannelLocalMessages)) {
updateSendButtonType();
}
if (update.flags & (UpdateFlag::UserIsBlocked
| UpdateFlag::AdminsChanged
| UpdateFlag::MembersChanged
@ -2820,8 +2820,7 @@ void HistoryWidget::send(Qt::KeyboardModifiers modifiers) {
} else if (_editMsgId) {
saveEditMsg();
return;
} else if (const auto left = _peer->slowmodeSecondsLeft()) {
ShowSlowmodeToast(left);
} else if (showSlowmodeError()) {
return;
}
@ -2842,7 +2841,7 @@ void HistoryWidget::send(Qt::KeyboardModifiers modifiers) {
|| (!_toForward.empty()
&& !message.textWithTags.text.isEmpty())
|| (message.textWithTags.text.size() > MaxMessageSize)) {
ShowSlowmodeToast(tr::lng_slowmode_no_many(tr::now));
ShowErrorToast(tr::lng_slowmode_no_many(tr::now));
return;
}
}
@ -3047,8 +3046,7 @@ void HistoryWidget::chooseAttach() {
ChatRestriction::f_send_media)) {
Ui::Toast::Show(*error);
return;
} else if (const auto left = _peer->slowmodeSecondsLeft()) {
ShowSlowmodeToast(left);
} else if (showSlowmodeError()) {
return;
}
@ -3167,6 +3165,8 @@ void HistoryWidget::recordStartCallback() {
if (error) {
Ui::show(Box<InformBox>(*error));
return;
} else if (showSlowmodeError()) {
return;
} else if (!Media::Capture::instance()->available()) {
return;
}
@ -3581,6 +3581,10 @@ void HistoryWidget::updateSendButtonType() {
: 0;
}();
_send->setSlowmodeDelay(delay);
_send->setDisabled(_peer
&& _peer->slowmodeApplied()
&& (_history->latestSendingMessage() != nullptr)
&& (type == Type::Send || type == Type::Record));
if (delay != 0) {
App::CallDelayed(
@ -4020,7 +4024,7 @@ bool HistoryWidget::showSendingFilesError(
return false;
}
ShowSlowmodeToast(text);
ShowErrorToast(text);
return true;
}
@ -4218,7 +4222,7 @@ void HistoryWidget::uploadFilesAfterConfirmation(
|| (!list.files.empty()
&& !caption.text.isEmpty()
&& !list.canAddCaption(isAlbum, compressImages)))) {
ShowSlowmodeToast(tr::lng_slowmode_no_many(tr::now));
ShowErrorToast(tr::lng_slowmode_no_many(tr::now));
return;
}
@ -5331,6 +5335,31 @@ void HistoryWidget::replyToNextMessage() {
}
}
bool HistoryWidget::showSlowmodeError() {
const auto text = [&] {
if (const auto left = _peer->slowmodeSecondsLeft()) {
return tr::lng_slowmode_enabled(
tr::now,
lt_left,
formatDurationWords(left));
} else if (_peer->slowmodeApplied()) {
if (const auto item = _history->latestSendingMessage()) {
if (const auto view = item->mainView()) {
animatedScrollToItem(item->id);
enqueueMessageHighlight(view);
}
return tr::lng_slowmode_no_many(tr::now);
}
}
return QString();
}();
if (text.isEmpty()) {
return false;
}
ShowErrorToast(text);
return true;
}
void HistoryWidget::onFieldTabbed() {
if (_supportAutocomplete) {
_supportAutocomplete->activate(_field.data());
@ -5344,8 +5373,7 @@ void HistoryWidget::sendInlineResult(
not_null<UserData*> bot) {
if (!_peer || !_peer->canWrite()) {
return;
} else if (const auto left = _peer->slowmodeSecondsLeft()) {
ShowSlowmodeToast(left);
} else if (showSlowmodeError()) {
return;
}
@ -5500,18 +5528,11 @@ bool HistoryWidget::sendExistingDocument(
return false;
} else if (!_peer || !_peer->canWrite()) {
return false;
} else if (const auto left = _peer->slowmodeSecondsLeft()) {
ShowSlowmodeToast(left);
} else if (showSlowmodeError()) {
return false;
}
const auto origin = document->stickerOrGifOrigin();
auto options = ApiWrap::SendOptions(_history);
options.clearDraft = false;
options.replyTo = replyToId();
options.generateLocal = true;
session().api().sendExistingDocument(document, origin, caption, options);
Api::SendExistingDocument(_history, document, caption, replyToId());
if (_fieldAutocomplete->stickersShown()) {
clearFieldText();
@ -5538,91 +5559,15 @@ bool HistoryWidget::sendExistingPhoto(
return false;
} else if (!_peer || !_peer->canWrite()) {
return false;
} else if (const auto left = _peer->slowmodeSecondsLeft()) {
ShowSlowmodeToast(left);
} else if (showSlowmodeError()) {
return false;
}
auto options = ApiWrap::SendOptions(_history);
options.clearDraft = false;
options.replyTo = replyToId();
options.generateLocal = true;
session().api().sendAction(options);
const auto randomId = rand_value<uint64>();
const auto newId = FullMsgId(_channel, clientMsgId());
auto flags = NewMessageFlags(_peer) | MTPDmessage::Flag::f_media;
auto sendFlags = MTPmessages_SendMedia::Flags(0);
if (options.replyTo) {
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
}
bool channelPost = _peer->isChannel() && !_peer->isMegagroup();
bool silentPost = channelPost && session().data().notifySilentPosts(_peer);
if (channelPost) {
flags |= MTPDmessage::Flag::f_views;
flags |= MTPDmessage::Flag::f_post;
}
if (!channelPost) {
flags |= MTPDmessage::Flag::f_from_id;
} else if (_peer->asChannel()->addsSignature()) {
flags |= MTPDmessage::Flag::f_post_author;
}
if (silentPost) {
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
}
auto messageFromId = channelPost ? 0 : session().userId();
auto messagePostAuthor = channelPost
? App::peerName(session().user())
: QString();
TextUtilities::Trim(caption);
auto sentEntities = TextUtilities::EntitiesToMTP(
caption.entities,
TextUtilities::ConvertOption::SkipLocal);
if (!sentEntities.v.isEmpty()) {
sendFlags |= MTPmessages_SendMedia::Flag::f_entities;
}
_history->addNewPhoto(
newId.msg,
flags,
0,
options.replyTo,
base::unixtime::now(),
messageFromId,
messagePostAuthor,
photo,
caption,
MTPReplyMarkup());
_history->sendRequestId = MTP::send(
MTPmessages_SendMedia(
MTP_flags(sendFlags),
_peer->input,
MTP_int(options.replyTo),
MTP_inputMediaPhoto(
MTP_flags(0),
photo->mtpInput(),
MTPint()),
MTP_string(caption.text),
MTP_long(randomId),
MTPReplyMarkup(),
sentEntities),
App::main()->rpcDone(&MainWidget::sentUpdatesReceived),
App::main()->rpcFail(&MainWidget::sendMessageFail),
0,
0,
_history->sendRequestId);
App::main()->finishForwarding(_history);
_history->owner().registerMessageRandomId(randomId, newId);
Api::SendExistingPhoto(_history, photo, caption, replyToId());
hideSelectorControlsAnimated();
_field->setFocus();
return true;
}

View file

@ -467,6 +467,7 @@ private:
void cancelReplyAfterMediaSend(bool lastKeyboardUsed);
void replyToPreviousMessage();
void replyToNextMessage();
[[nodiscard]] bool showSlowmodeError();
void hideSelectorControlsAnimated();
int countMembersDropdownHeightMax() const;

View file

@ -128,7 +128,7 @@ void SendPhoto::addToHistory(
MsgId replyToId,
const QString &postAuthor,
const MTPReplyMarkup &markup) const {
history->addNewPhoto(
history->addNewLocalMessage(
msgId,
flags,
viaBotId,
@ -161,7 +161,7 @@ void SendFile::addToHistory(
MsgId replyToId,
const QString &postAuthor,
const MTPReplyMarkup &markup) const {
history->addNewDocument(
history->addNewLocalMessage(
msgId,
flags,
viaBotId,
@ -208,7 +208,7 @@ void SendGame::addToHistory(
MsgId replyToId,
const QString &postAuthor,
const MTPReplyMarkup &markup) const {
history->addNewGame(
history->addNewLocalMessage(
msgId,
flags,
viaBotId,

View file

@ -918,23 +918,6 @@ void MainWidget::removeDialog(Dialogs::Key key) {
_dialogs->removeDialog(key);
}
bool MainWidget::sendMessageFail(const RPCError &error) {
if (MTP::isDefaultHandledError(error)) return false;
if (error.type() == qstr("PEER_FLOOD")) {
Ui::show(Box<InformBox>(PeerFloodErrorText(PeerFloodType::Send)));
return true;
} else if (error.type() == qstr("USER_BANNED_IN_CHANNEL")) {
const auto link = textcmdLink(
Core::App().createInternalLinkFull(qsl("spambot")),
tr::lng_cant_more_info(tr::now));
const auto text = tr::lng_error_public_groups_denied(tr::now, lt_more_info, link);
Ui::show(Box<InformBox>(text));
return true;
}
return false;
}
void MainWidget::cacheBackground() {
if (Window::Theme::Background()->colorForFill()) {
return;

View file

@ -195,8 +195,6 @@ public:
void deletePhotoLayer(PhotoData *photo);
bool sendMessageFail(const RPCError &error);
// While HistoryInner is not HistoryView::ListWidget.
crl::time highlightStartTime(not_null<const HistoryItem*> item) const;

View file

@ -60,8 +60,11 @@ enum class MTPDmessage_ClientFlag : uint32 {
// message is an outgoing message that is being sent
f_sending = (1U << 23),
// message was an outgoing message and failed to be sent
f_failed = (1U << 22),
// update this when adding new client side flags
MIN_FIELD = (1U << 23),
MIN_FIELD = (1U << 22),
};
DEFINE_MTP_CLIENT_FLAGS(MTPDmessage)

View file

@ -68,6 +68,7 @@ struct PeerUpdate {
ChannelLinkedChat = (1 << 20),
ChannelLocation = (1 << 21),
ChannelSlowmode = (1 << 22),
ChannelLocalMessages = (1 << 23),
};
using Flags = base::flags<Flag>;
friend inline constexpr auto is_flag_type(Flag) { return true; }

View file

@ -11,6 +11,7 @@
#include "apiwrap.h"
#include "auth_session.h"
#include "api/api_sending.h"
#include "boxes/confirm_box.h"
#include "chat_helpers/emoji_list_widget.h"
#include "core/application.h"
@ -809,11 +810,7 @@ void AppendEmojiPacks(std::vector<PickerScrubberItem> &to) {
if (const auto error = RestrictionToSendStickers()) {
Ui::show(Box<InformBox>(*error));
}
Auth().api().sendExistingDocument(
document,
document->stickerSetOrigin(),
{},
ApiWrap::SendOptions(chat.history()));
Api::SendExistingDocument(chat.history(), document);
return true;
} else if (const auto emoji = _stickers[index].emoji) {
if (const auto inputField = qobject_cast<QTextEdit*>(

View file

@ -375,11 +375,15 @@ void SendButton::paintEvent(QPaintEvent *e) {
void SendButton::paintRecord(Painter &p, bool over) {
auto recordActive = recordActiveRatio();
if (!isDisabled()) {
auto rippleColor = anim::color(st::historyAttachEmoji.ripple.color, st::historyRecordVoiceRippleBgActive, recordActive);
paintRipple(p, (width() - st::historyAttachEmoji.rippleAreaSize) / 2, st::historyAttachEmoji.rippleAreaPosition.y(), &rippleColor);
}
auto fastIcon = [&] {
if (recordActive == 1.) {
if (isDisabled()) {
return &st::historyRecordVoice;
} else if (recordActive == 1.) {
return &st::historyRecordVoiceActive;
} else if (over) {
return &st::historyRecordVoiceOver;
@ -387,7 +391,7 @@ void SendButton::paintRecord(Painter &p, bool over) {
return &st::historyRecordVoice;
};
fastIcon()->paintInCenter(p, rect());
if (recordActive > 0. && recordActive < 1.) {
if (!isDisabled() && recordActive > 0. && recordActive < 1.) {
p.setOpacity(recordActive);
st::historyRecordVoiceActive.paintInCenter(p, rect());
p.setOpacity(1.);
@ -414,8 +418,13 @@ void SendButton::paintSend(Painter &p, bool over) {
const auto &sendIcon = over
? st::historySendIconOver
: st::historySendIcon;
if (isDisabled()) {
const auto color = st::historyRecordVoiceFg->c;
sendIcon.paint(p, st::historySendIconPosition, width(), color);
} else {
sendIcon.paint(p, st::historySendIconPosition, width());
}
}
void SendButton::paintSlowmode(Painter &p) {
p.setFont(st::normalFont);

View file

@ -1,3 +1,5 @@
<(src_loc)/api/api_sending.cpp
<(src_loc)/api/api_sending.h
<(src_loc)/boxes/peers/add_participants_box.cpp
<(src_loc)/boxes/peers/add_participants_box.h
<(src_loc)/boxes/peers/edit_contact_box.cpp