Allow sending scheduled messages.

This commit is contained in:
John Preston 2019-08-12 17:33:36 +01:00
parent caef7dde24
commit 03cdddfe18
18 changed files with 336 additions and 180 deletions

View File

@ -40,7 +40,9 @@ void SendExistingMedia(
message.action.generateLocal = true; message.action.generateLocal = true;
api->sendAction(message.action); api->sendAction(message.action);
const auto newId = FullMsgId(peerToChannel(peer->id), clientMsgId()); const auto newId = FullMsgId(
peerToChannel(peer->id),
session->data().nextLocalMessageId());
const auto randomId = rand_value<uint64>(); const auto randomId = rand_value<uint64>();
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media; auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
@ -84,6 +86,13 @@ void SendExistingMedia(
const auto replyTo = message.action.replyTo; const auto replyTo = message.action.replyTo;
const auto captionText = caption.text; const auto captionText = caption.text;
if (message.action.options.scheduled) {
flags |= MTPDmessage::Flag::f_from_scheduled;
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
} else {
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
}
session->data().registerMessageRandomId(randomId, newId); session->data().registerMessageRandomId(randomId, newId);
history->addNewLocalMessage( history->addNewLocalMessage(
@ -92,7 +101,7 @@ void SendExistingMedia(
clientFlags, clientFlags,
0, 0,
replyTo, replyTo,
base::unixtime::now(), HistoryItem::NewMessageDate(message.action.options.scheduled),
messageFromId, messageFromId,
messagePostAuthor, messagePostAuthor,
media, media,
@ -111,7 +120,7 @@ void SendExistingMedia(
MTP_long(randomId), MTP_long(randomId),
MTPReplyMarkup(), MTPReplyMarkup(),
sentEntities, sentEntities,
MTP_int(0) // schedule_date MTP_int(message.action.options.scheduled)
)).done([=](const MTPUpdates &result) { )).done([=](const MTPUpdates &result) {
api->applyUpdates(result, randomId); api->applyUpdates(result, randomId);
}).fail([=](const RPCError &error) { }).fail([=](const RPCError &error) {

View File

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_media_types.h" #include "data/data_media_types.h"
#include "data/data_sparse_ids.h" #include "data/data_sparse_ids.h"
#include "data/data_search_controller.h" #include "data/data_search_controller.h"
#include "data/data_scheduled_messages.h"
#include "data/data_channel_admins.h" #include "data/data_channel_admins.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_channel.h" #include "data/data_channel.h"
@ -4427,6 +4428,12 @@ void ApiWrap::forwardMessages(
if (silentPost) { if (silentPost) {
sendFlags |= MTPmessages_ForwardMessages::Flag::f_silent; sendFlags |= MTPmessages_ForwardMessages::Flag::f_silent;
} }
if (action.options.scheduled) {
flags |= MTPDmessage::Flag::f_from_scheduled;
sendFlags |= MTPmessages_ForwardMessages::Flag::f_schedule_date;
} else {
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
}
auto forwardFrom = items.front()->history()->peer; auto forwardFrom = items.front()->history()->peer;
auto currentGroupId = items.front()->groupId(); auto currentGroupId = items.front()->groupId();
@ -4448,7 +4455,7 @@ void ApiWrap::forwardMessages(
MTP_vector<MTPint>(ids), MTP_vector<MTPint>(ids),
MTP_vector<MTPlong>(randomIds), MTP_vector<MTPlong>(randomIds),
peer->input, peer->input,
MTP_int(0) // schedule_date MTP_int(action.options.scheduled)
)).done([=, callback = std::move(successCallback)]( )).done([=, callback = std::move(successCallback)](
const MTPUpdates &updates) { const MTPUpdates &updates) {
applyUpdates(updates); applyUpdates(updates);
@ -4480,7 +4487,7 @@ void ApiWrap::forwardMessages(
if (const auto message = item->toHistoryMessage()) { if (const auto message = item->toHistoryMessage()) {
const auto newId = FullMsgId( const auto newId = FullMsgId(
peerToChannel(peer->id), peerToChannel(peer->id),
clientMsgId()); session().data().nextLocalMessageId());
const auto self = _session->user(); const auto self = _session->user();
const auto messageFromId = channelPost const auto messageFromId = channelPost
? UserId(0) ? UserId(0)
@ -4492,7 +4499,7 @@ void ApiWrap::forwardMessages(
newId.msg, newId.msg,
flags, flags,
clientFlags, clientFlags,
base::unixtime::now(), HistoryItem::NewMessageDate(action.options.scheduled),
messageFromId, messageFromId,
messagePostAuthor, messagePostAuthor,
message); message);
@ -4554,7 +4561,9 @@ void ApiWrap::sendSharedContact(
const auto history = action.history; const auto history = action.history;
const auto peer = history->peer; const auto peer = history->peer;
const auto newId = FullMsgId(history->channelId(), clientMsgId()); const auto newId = FullMsgId(
history->channelId(),
session().data().nextLocalMessageId());
const auto channelPost = peer->isChannel() && !peer->isMegagroup(); const auto channelPost = peer->isChannel() && !peer->isMegagroup();
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media; auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
@ -4571,6 +4580,11 @@ void ApiWrap::sendSharedContact(
} else { } else {
flags |= MTPDmessage::Flag::f_from_id; flags |= MTPDmessage::Flag::f_from_id;
} }
if (action.options.scheduled) {
flags |= MTPDmessage::Flag::f_from_scheduled;
} else {
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
}
const auto messageFromId = channelPost ? 0 : _session->userId(); const auto messageFromId = channelPost ? 0 : _session->userId();
const auto messagePostAuthor = channelPost const auto messagePostAuthor = channelPost
? App::peerName(_session->user()) ? App::peerName(_session->user())
@ -4586,7 +4600,7 @@ void ApiWrap::sendSharedContact(
MTPMessageFwdHeader(), MTPMessageFwdHeader(),
MTPint(), MTPint(),
MTP_int(action.replyTo), MTP_int(action.replyTo),
MTP_int(base::unixtime::now()), MTP_int(HistoryItem::NewMessageDate(action.options.scheduled)),
MTP_string(), MTP_string(),
MTP_messageMediaContact( MTP_messageMediaContact(
MTP_string(phone), MTP_string(phone),
@ -4907,7 +4921,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
HistoryItem *lastMessage = nullptr; HistoryItem *lastMessage = nullptr;
while (TextUtilities::CutPart(sending, left, MaxMessageSize)) { while (TextUtilities::CutPart(sending, left, MaxMessageSize)) {
auto newId = FullMsgId(peerToChannel(peer->id), clientMsgId()); auto newId = FullMsgId(
peerToChannel(peer->id),
session().data().nextLocalMessageId());
auto randomId = rand_value<uint64>(); auto randomId = rand_value<uint64>();
TextUtilities::Trim(sending); TextUtilities::Trim(sending);
@ -4963,6 +4979,12 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
auto messagePostAuthor = channelPost auto messagePostAuthor = channelPost
? App::peerName(_session->user()) ? App::peerName(_session->user())
: QString(); : QString();
if (action.options.scheduled) {
flags |= MTPDmessage::Flag::f_from_scheduled;
sendFlags |= MTPmessages_SendMessage::Flag::f_schedule_date;
} else {
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
}
lastMessage = history->addNewMessage( lastMessage = history->addNewMessage(
MTP_message( MTP_message(
MTP_flags(flags), MTP_flags(flags),
@ -4972,7 +4994,8 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
MTPMessageFwdHeader(), MTPMessageFwdHeader(),
MTPint(), MTPint(),
MTP_int(action.replyTo), MTP_int(action.replyTo),
MTP_int(base::unixtime::now()), MTP_int(
HistoryItem::NewMessageDate(action.options.scheduled)),
msgText, msgText,
media, media,
MTPReplyMarkup(), MTPReplyMarkup(),
@ -4993,7 +5016,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
MTP_long(randomId), MTP_long(randomId),
MTPReplyMarkup(), MTPReplyMarkup(),
sentEntities, sentEntities,
MTP_int(0) // schedule_date MTP_int(action.options.scheduled)
)).done([=](const MTPUpdates &result) { )).done([=](const MTPUpdates &result) {
applyUpdates(result, randomId); applyUpdates(result, randomId);
history->clearSentDraftText(QString()); history->clearSentDraftText(QString());
@ -5053,7 +5076,9 @@ void ApiWrap::sendInlineResult(
const auto history = action.history; const auto history = action.history;
const auto peer = history->peer; const auto peer = history->peer;
const auto newId = FullMsgId(peerToChannel(peer->id), clientMsgId()); const auto newId = FullMsgId(
peerToChannel(peer->id),
session().data().nextLocalMessageId());
const auto randomId = rand_value<uint64>(); const auto randomId = rand_value<uint64>();
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media; auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
@ -5081,14 +5106,17 @@ void ApiWrap::sendInlineResult(
if (bot) { if (bot) {
flags |= MTPDmessage::Flag::f_via_bot_id; flags |= MTPDmessage::Flag::f_via_bot_id;
} }
if (action.options.scheduled) {
flags |= MTPDmessage::Flag::f_from_scheduled;
sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_schedule_date;
} else {
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
}
auto messageFromId = channelPost ? 0 : _session->userId(); const auto messageFromId = channelPost ? 0 : _session->userId();
auto messagePostAuthor = channelPost const auto messagePostAuthor = channelPost
? App::peerName(_session->user()) ? App::peerName(_session->user())
: QString(); : QString();
MTPint messageDate = MTP_int(base::unixtime::now());
UserId messageViaBotId = bot ? peerToUser(bot->id) : 0;
MsgId messageId = newId.msg;
_session->data().registerMessageRandomId(randomId, newId); _session->data().registerMessageRandomId(randomId, newId);
@ -5096,10 +5124,10 @@ void ApiWrap::sendInlineResult(
history, history,
flags, flags,
clientFlags, clientFlags,
messageId, newId.msg,
messageFromId, messageFromId,
messageDate, MTP_int(HistoryItem::NewMessageDate(action.options.scheduled)),
messageViaBotId, bot ? peerToUser(bot->id) : 0,
action.replyTo, action.replyTo,
messagePostAuthor); messagePostAuthor);
@ -5113,7 +5141,7 @@ void ApiWrap::sendInlineResult(
MTP_long(randomId), MTP_long(randomId),
MTP_long(data->getQueryId()), MTP_long(data->getQueryId()),
MTP_string(data->getId()), MTP_string(data->getId()),
MTP_int(0) // schedule_date MTP_int(action.options.scheduled)
)).done([=](const MTPUpdates &result) { )).done([=](const MTPUpdates &result) {
applyUpdates(result, randomId); applyUpdates(result, randomId);
history->clearSentDraftText(QString()); history->clearSentDraftText(QString());
@ -5236,6 +5264,9 @@ void ApiWrap::sendMediaWithRandomId(
: MTPmessages_SendMedia::Flag(0)) : MTPmessages_SendMedia::Flag(0))
| (!sentEntities.v.isEmpty() | (!sentEntities.v.isEmpty()
? MTPmessages_SendMedia::Flag::f_entities ? MTPmessages_SendMedia::Flag::f_entities
: MTPmessages_SendMedia::Flag(0))
| (options.scheduled
? MTPmessages_SendMedia::Flag::f_schedule_date
: MTPmessages_SendMedia::Flag(0)); : MTPmessages_SendMedia::Flag(0));
const auto peer = history->peer; const auto peer = history->peer;
@ -5249,7 +5280,7 @@ void ApiWrap::sendMediaWithRandomId(
MTP_long(randomId), MTP_long(randomId),
MTPReplyMarkup(), MTPReplyMarkup(),
sentEntities, sentEntities,
MTP_int(0) // schedule_date MTP_int(options.scheduled)
)).done([=](const MTPUpdates &result) { )).done([=](const MTPUpdates &result) {
applyUpdates(result); applyUpdates(result);
}).fail([=](const RPCError &error) { }).fail([=](const RPCError &error) {
@ -5330,6 +5361,9 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
: MTPmessages_SendMultiMedia::Flag(0)) : MTPmessages_SendMultiMedia::Flag(0))
| (album->options.silent | (album->options.silent
? MTPmessages_SendMultiMedia::Flag::f_silent ? MTPmessages_SendMultiMedia::Flag::f_silent
: MTPmessages_SendMultiMedia::Flag(0))
| (album->options.scheduled
? MTPmessages_SendMultiMedia::Flag::f_schedule_date
: MTPmessages_SendMultiMedia::Flag(0)); : MTPmessages_SendMultiMedia::Flag(0));
const auto peer = history->peer; const auto peer = history->peer;
history->sendRequestId = request(MTPmessages_SendMultiMedia( history->sendRequestId = request(MTPmessages_SendMultiMedia(
@ -5337,7 +5371,7 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
peer->input, peer->input,
MTP_int(replyTo), MTP_int(replyTo),
MTP_vector<MTPInputSingleMedia>(medias), MTP_vector<MTPInputSingleMedia>(medias),
MTP_int(0) // schedule_date MTP_int(album->options.scheduled)
)).done([=](const MTPUpdates &result) { )).done([=](const MTPUpdates &result) {
_sendingAlbums.remove(groupId); _sendingAlbums.remove(groupId);
applyUpdates(result); applyUpdates(result);
@ -5360,10 +5394,7 @@ FileLoadTo ApiWrap::fileLoadTaskOptions(const SendAction &action) const {
if (_session->data().notifySilentPosts(peer)) { if (_session->data().notifySilentPosts(peer)) {
options.silent = true; options.silent = true;
} }
return FileLoadTo( return FileLoadTo(peer->id, action.options, action.replyTo);
peer->id,
action.options,
action.replyTo);
} }
void ApiWrap::requestSupportContact(FnMut<void(const MTPUser &)> callback) { void ApiWrap::requestSupportContact(FnMut<void(const MTPUser &)> callback) {
@ -5387,7 +5418,9 @@ void ApiWrap::uploadPeerPhoto(not_null<PeerData*> peer, QImage &&image) {
peer = peer->migrateToOrMe(); peer = peer->migrateToOrMe();
const auto ready = PreparePeerPhoto(peer->id, std::move(image)); const auto ready = PreparePeerPhoto(peer->id, std::move(image));
const auto fakeId = FullMsgId(peerToChannel(peer->id), clientMsgId()); const auto fakeId = FullMsgId(
peerToChannel(peer->id),
session().data().nextLocalMessageId());
const auto already = ranges::find( const auto already = ranges::find(
_peerPhotoUploads, _peerPhotoUploads,
peer, peer,
@ -5817,6 +5850,9 @@ void ApiWrap::createPoll(
if (silentPost) { if (silentPost) {
sendFlags |= MTPmessages_SendMedia::Flag::f_silent; sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
} }
if (action.options.scheduled) {
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
}
const auto replyTo = action.replyTo; const auto replyTo = action.replyTo;
history->sendRequestId = request(MTPmessages_SendMedia( history->sendRequestId = request(MTPmessages_SendMedia(
@ -5828,7 +5864,7 @@ void ApiWrap::createPoll(
MTP_long(rand_value<uint64>()), MTP_long(rand_value<uint64>()),
MTPReplyMarkup(), MTPReplyMarkup(),
MTPVector<MTPMessageEntity>(), MTPVector<MTPMessageEntity>(),
MTP_int(0) // schedule_date MTP_int(action.options.scheduled)
)).done([=, done = std::move(done)](const MTPUpdates &result) mutable { )).done([=, done = std::move(done)](const MTPUpdates &result) mutable {
applyUpdates(result); applyUpdates(result);
done(); done();

View File

@ -771,8 +771,14 @@ void DeleteMessagesBox::deleteAndClear() {
if (const auto item = _session->data().message(itemId)) { if (const auto item = _session->data().message(itemId)) {
const auto history = item->history(); const auto history = item->history();
if (item->isScheduled()) { if (item->isScheduled()) {
scheduledIdsByPeer[history->peer].push_back(MTP_int( const auto wasOnServer = !item->isSending()
_session->data().scheduledMessages().lookupId(item))); && !item->hasFailed();
if (wasOnServer) {
scheduledIdsByPeer[history->peer].push_back(MTP_int(
_session->data().scheduledMessages().lookupId(item)));
} else {
_session->data().scheduledMessages().removeSending(item);
}
continue; continue;
} }
const auto wasOnServer = IsServerMsgId(item->id); const auto wasOnServer = IsServerMsgId(item->id);

View File

@ -137,6 +137,49 @@ void ScheduledMessages::apply(
_updates.fire_copy(history); _updates.fire_copy(history);
} }
void ScheduledMessages::apply(
const MTPDupdateMessageID &update,
not_null<HistoryItem*> local) {
const auto id = update.vid().v;
const auto i = _data.find(local->history());
Assert(i != end(_data));
auto &list = i->second;
const auto j = list.itemById.find(id);
if (j != end(list.itemById)) {
local->destroy();
} else {
Assert(!list.itemById.contains(local->id));
Assert(!list.idByItem.contains(local));
local->setRealId(local->history()->nextNonHistoryEntryId());
list.idByItem.emplace(local, id);
list.itemById.emplace(id, local);
}
}
void ScheduledMessages::appendSending(not_null<HistoryItem*> item) {
Expects(item->isSending());
Expects(item->isScheduled());
const auto history = item->history();
auto &list = _data[history];
list.items.emplace_back(item);
sort(list);
_updates.fire_copy(history);
}
void ScheduledMessages::removeSending(not_null<HistoryItem*> item) {
Expects(item->isSending() || item->hasFailed());
Expects(item->isScheduled());
const auto history = item->history();
auto &list = _data[history];
Assert(!list.itemById.contains(item->id));
Assert(!list.idByItem.contains(item));
list.items.erase(
ranges::remove(list.items, item.get(), &OwnedItem::get),
end(list.items));
}
rpl::producer<> ScheduledMessages::updates(not_null<History*> history) { rpl::producer<> ScheduledMessages::updates(not_null<History*> history) {
request(history); request(history);
@ -175,10 +218,12 @@ void ScheduledMessages::request(not_null<History*> history) {
if (request.requestId) { if (request.requestId) {
return; return;
} }
const auto i = _data.find(history);
const auto hash = (i != end(_data)) ? countListHash(i->second) : 0;
request.requestId = _session->api().request( request.requestId = _session->api().request(
MTPmessages_GetScheduledHistory( MTPmessages_GetScheduledHistory(
history->peer->input, history->peer->input,
MTP_int(request.hash)) MTP_int(hash))
).done([=](const MTPmessages_Messages &result) { ).done([=](const MTPmessages_Messages &result) {
parse(history, result); parse(history, result);
}).fail([=](const RPCError &error) { }).fail([=](const RPCError &error) {
@ -197,14 +242,33 @@ void ScheduledMessages::parse(
}, [&](const auto &data) { }, [&](const auto &data) {
_session->data().processUsers(data.vusers()); _session->data().processUsers(data.vusers());
_session->data().processChats(data.vchats()); _session->data().processChats(data.vchats());
const auto &messages = data.vmessages().v; const auto &messages = data.vmessages().v;
if (messages.isEmpty()) { if (messages.isEmpty()) {
if (element != end(_data)) {
_data.erase(element);
element = end(_data);
_updates.fire_copy(history);
}
return; return;
} }
element = _data.emplace(history, List()).first; element = _data.emplace(history, List()).first;
auto received = base::flat_set<not_null<HistoryItem*>>();
auto &list = element->second; auto &list = element->second;
for (const auto &message : messages) { for (const auto &message : messages) {
append(history, list, message); if (const auto item = append(history, list, message)) {
received.emplace(item);
}
}
auto clear = base::flat_set<not_null<HistoryItem*>>();
for (const auto &owned : list.items) {
const auto item = owned.get();
if (!item->isSending() && !received.contains(item)) {
clear.emplace(item);
}
}
for (const auto item : clear) {
item->destroy();
} }
if (!list.items.empty()) { if (!list.items.empty()) {
sort(list); sort(list);
@ -214,24 +278,21 @@ void ScheduledMessages::parse(
} }
_updates.fire_copy(history); _updates.fire_copy(history);
}); });
if (!request.requestId) {
request.hash = (element != end(_data))
? countListHash(element->second)
: 0;
if (!request.requestId && !request.hash) {
_requests.remove(history); _requests.remove(history);
} }
} }
void ScheduledMessages::append( HistoryItem *ScheduledMessages::append(
not_null<History*> history, not_null<History*> history,
List &list, List &list,
const MTPMessage &message) { const MTPMessage &message) {
const auto id = message.match([&](const auto &data) { const auto id = message.match([&](const auto &data) {
return data.vid().v; return data.vid().v;
}); });
if (list.itemById.find(id) != end(list.itemById)) { const auto i = list.itemById.find(id);
return; if (i != end(list.itemById)) {
return i->second;
} }
const auto item = _session->data().addNewMessage( const auto item = _session->data().addNewMessage(
@ -240,11 +301,12 @@ void ScheduledMessages::append(
NewMessageType::Existing); NewMessageType::Existing);
if (!item || item->history() != history) { if (!item || item->history() != history) {
LOG(("API Error: Bad data received in scheduled messages.")); LOG(("API Error: Bad data received in scheduled messages."));
return; return nullptr;
} }
list.items.emplace_back(item); list.items.emplace_back(item);
list.itemById.emplace(id, item); list.itemById.emplace(id, item);
list.idByItem.emplace(item, id); list.idByItem.emplace(item, id);
return item;
} }
void ScheduledMessages::sort(List &list) { void ScheduledMessages::sort(List &list) {
@ -277,7 +339,12 @@ int32 ScheduledMessages::countListHash(const List &list) const {
using namespace Api; using namespace Api;
auto hash = HashInit(); auto hash = HashInit();
for (const auto &item : list.items | ranges::view::reverse) { auto &&serverside = ranges::view::all(
list.items
) | ranges::view::filter([](const OwnedItem &item) {
return !item->isSending() && !item->hasFailed();
}) | ranges::view::reverse;
for (const auto &item : serverside) {
const auto j = list.idByItem.find(item.get()); const auto j = list.idByItem.find(item.get());
HashUpdate(hash, j->second); HashUpdate(hash, j->second);
if (const auto edited = item->Get<HistoryMessageEdited>()) { if (const auto edited = item->Get<HistoryMessageEdited>()) {

View File

@ -32,6 +32,12 @@ public:
void apply(const MTPDupdateNewScheduledMessage &update); void apply(const MTPDupdateNewScheduledMessage &update);
void apply(const MTPDupdateDeleteScheduledMessages &update); void apply(const MTPDupdateDeleteScheduledMessages &update);
void apply(
const MTPDupdateMessageID &update,
not_null<HistoryItem*> local);
void appendSending(not_null<HistoryItem*> item);
void removeSending(not_null<HistoryItem*> item);
[[nodiscard]] rpl::producer<> updates(not_null<History*> history); [[nodiscard]] rpl::producer<> updates(not_null<History*> history);
[[nodiscard]] Data::MessagesSlice list(not_null<History*> history); [[nodiscard]] Data::MessagesSlice list(not_null<History*> history);
@ -44,7 +50,6 @@ private:
base::flat_map<not_null<HistoryItem*>, MsgId> idByItem; base::flat_map<not_null<HistoryItem*>, MsgId> idByItem;
}; };
struct Request { struct Request {
int32 hash = 0;
mtpRequestId requestId = 0; mtpRequestId requestId = 0;
}; };
@ -52,7 +57,7 @@ private:
void parse( void parse(
not_null<History*> history, not_null<History*> history,
const MTPmessages_Messages &list); const MTPmessages_Messages &list);
void append( HistoryItem *append(
not_null<History*> history, not_null<History*> history,
List &list, List &list,
const MTPMessage &message); const MTPMessage &message);

View File

@ -1844,6 +1844,12 @@ void Session::destroyMessage(not_null<HistoryItem*> item) {
list->erase(item->id); list->erase(item->id);
} }
MsgId Session::nextLocalMessageId() {
Expects(_localMessageIdCounter < EndClientMsgId);
return _localMessageIdCounter++;
}
HistoryItem *Session::message(ChannelId channelId, MsgId itemId) const { HistoryItem *Session::message(ChannelId channelId, MsgId itemId) const {
if (!itemId) { if (!itemId) {
return nullptr; return nullptr;
@ -3627,13 +3633,14 @@ void Session::insertCheckedServiceNotification(
const auto flags = MTPDmessage::Flag::f_entities const auto flags = MTPDmessage::Flag::f_entities
| MTPDmessage::Flag::f_from_id | MTPDmessage::Flag::f_from_id
| MTPDmessage::Flag::f_media; | MTPDmessage::Flag::f_media;
const auto clientFlags = MTPDmessage_ClientFlag::f_clientside_unread; const auto clientFlags = MTPDmessage_ClientFlag::f_clientside_unread
| MTPDmessage_ClientFlag::f_local_history_entry;
auto sending = TextWithEntities(), left = message; auto sending = TextWithEntities(), left = message;
while (TextUtilities::CutPart(sending, left, MaxMessageSize)) { while (TextUtilities::CutPart(sending, left, MaxMessageSize)) {
addNewMessage( addNewMessage(
MTP_message( MTP_message(
MTP_flags(flags), MTP_flags(flags),
MTP_int(clientMsgId()), MTP_int(nextLocalMessageId()),
MTP_int(peerToUser(PeerData::kServiceNotificationsId)), MTP_int(peerToUser(PeerData::kServiceNotificationsId)),
MTP_peerUser(MTP_int(_session->userId())), MTP_peerUser(MTP_int(_session->userId())),
MTPMessageFwdHeader(), MTPMessageFwdHeader(),

View File

@ -387,6 +387,7 @@ public:
ChannelId channelId, ChannelId channelId,
const QVector<MTPint> &data); const QVector<MTPint> &data);
[[nodiscard]] MsgId nextLocalMessageId();
[[nodiscard]] HistoryItem *message( [[nodiscard]] HistoryItem *message(
ChannelId channelId, ChannelId channelId,
MsgId itemId) const; MsgId itemId) const;
@ -877,6 +878,7 @@ private:
Dialogs::IndexedList _contactsList; Dialogs::IndexedList _contactsList;
Dialogs::IndexedList _contactsNoChatsList; Dialogs::IndexedList _contactsNoChatsList;
MsgId _localMessageIdCounter = StartClientMsgId;
Messages _messages; Messages _messages;
std::map<ChannelId, Messages> _channelMessages; std::map<ChannelId, Messages> _channelMessages;
std::map< std::map<

View File

@ -415,12 +415,6 @@ inline bool operator!=(const AudioMsgId &a, const AudioMsgId &b) {
return !(a == b); return !(a == b);
} }
inline MsgId clientMsgId() {
static MsgId CurrentClientMsgId = StartClientMsgId;
Assert(CurrentClientMsgId < EndClientMsgId);
return CurrentClientMsgId++;
}
struct MessageCursor { struct MessageCursor {
MessageCursor() = default; MessageCursor() = default;
MessageCursor(int position, int anchor, int scroll) MessageCursor(int position, int anchor, int scroll)

View File

@ -16,6 +16,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_drafts.h" #include "data/data_drafts.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_media_types.h" #include "data/data_media_types.h"
#include "data/data_channel_admins.h"
#include "data/data_scheduled_messages.h"
#include "data/data_folder.h"
#include "data/data_photo.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "mainwidget.h" #include "mainwidget.h"
@ -29,12 +36,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_shared_media.h" #include "storage/storage_shared_media.h"
//#include "storage/storage_feed_messages.h" // #feed //#include "storage/storage_feed_messages.h" // #feed
#include "support/support_helper.h" #include "support/support_helper.h"
#include "data/data_channel_admins.h"
#include "data/data_folder.h"
#include "data/data_photo.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
#include "ui/image/image.h" #include "ui/image/image.h"
#include "ui/text_options.h" #include "ui/text_options.h"
#include "core/crash_reports.h" #include "core/crash_reports.h"
@ -622,51 +623,50 @@ HistoryItem *History::addNewMessage(
const MTPMessage &msg, const MTPMessage &msg,
MTPDmessage_ClientFlags clientFlags, MTPDmessage_ClientFlags clientFlags,
NewMessageType type) { NewMessageType type) {
if (type == NewMessageType::Existing) { const auto detachExistingItem = (type == NewMessageType::Unread);
return addToHistory(msg, clientFlags); const auto item = createItem(msg, clientFlags, detachExistingItem);
} if (!item) {
if (!loadedAtBottom() || peer->migrateTo()) {
if (const auto item = addToHistory(msg, clientFlags)) {
setLastMessage(item);
if (type == NewMessageType::Unread) {
newItemAdded(item);
}
return item;
}
return nullptr; return nullptr;
} }
if (type == NewMessageType::Existing || item->mainView()) {
return addNewToLastBlock(msg, clientFlags, type);
}
HistoryItem *History::addNewToLastBlock(
const MTPMessage &msg,
MTPDmessage_ClientFlags clientFlags,
NewMessageType type) {
Expects(type != NewMessageType::Existing);
const auto detachExistingItem = (type != NewMessageType::Last);
const auto item = createItem(msg, clientFlags, detachExistingItem);
if (!item || item->mainView()) {
return item; return item;
} }
const auto newUnreadMessage = (type == NewMessageType::Unread); const auto unread = (type == NewMessageType::Unread);
if (newUnreadMessage) { if (unread && item->isHistoryEntry()) {
applyMessageChanges(item, msg); applyMessageChanges(item, msg);
} }
const auto result = addNewItem(item, newUnreadMessage); return addNewItem(item, unread);
checkForLoadedAtTop(result); }
if (type == NewMessageType::Last) {
// When we add just one last item, like we do while loading dialogs, not_null<HistoryItem*> History::addNewItem(
// we want to remove a single added grouped media, otherwise it will not_null<HistoryItem*> item,
// jump once we open the message history (first we show only that bool unread) {
// media, then we load the rest of the group and show the group). if (item->isScheduled()) {
// session().data().scheduledMessages().appendSending(item);
// That way when we open the message history we show nothing until a return item;
// whole history part is loaded, it certainly will contain the group. } else if (!item->isHistoryEntry()) {
removeOrphanMediaGroupPart(); return item;
} }
return result; if (!loadedAtBottom() || peer->migrateTo()) {
setLastMessage(item);
if (unread) {
newItemAdded(item);
}
} else {
addNewToBack(item, unread);
checkForLoadedAtTop(item);
if (!unread) {
// When we add just one last item, like we do while loading dialogs,
// we want to remove a single added grouped media, otherwise it will
// jump once we open the message history (first we show only that
// media, then we load the rest of the group and show the group).
//
// That way when we open the message history we show nothing until a
// whole history part is loaded, it certainly will contain the group.
removeOrphanMediaGroupPart();
}
}
return item;
} }
void History::checkForLoadedAtTop(not_null<HistoryItem*> added) { void History::checkForLoadedAtTop(not_null<HistoryItem*> added) {
@ -896,7 +896,7 @@ void History::addUnreadMentionsSlice(const MTPmessages_Messages &result) {
Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::UnreadMentionsChanged); Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::UnreadMentionsChanged);
} }
not_null<HistoryItem*> History::addNewItem( not_null<HistoryItem*> History::addNewToBack(
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
bool unread) { bool unread) {
Expects(!isBuildingFrontBlock()); Expects(!isBuildingFrontBlock());
@ -2866,7 +2866,7 @@ void History::insertLocalMessage(not_null<HistoryItem*> item) {
Expects(item->mainView() == nullptr); Expects(item->mainView() == nullptr);
if (isEmpty()) { if (isEmpty()) {
addNewItem(item, false); addNewToBack(item, false);
return; return;
} }

View File

@ -387,11 +387,6 @@ private:
// helper method for countScrollState(int top) // helper method for countScrollState(int top)
void countScrollTopItem(int top); void countScrollTopItem(int top);
HistoryItem *addNewToLastBlock(
const MTPMessage &msg,
MTPDmessage_ClientFlags clientFlags,
NewMessageType type);
// this method just removes a block from the blocks list // this method just removes a block from the blocks list
// when the last item from this block was detached and // when the last item from this block was detached and
// calls the required previousItemChanged() // calls the required previousItemChanged()
@ -401,6 +396,9 @@ private:
not_null<HistoryItem*> addNewItem( not_null<HistoryItem*> addNewItem(
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
bool unread); bool unread);
not_null<HistoryItem*> addNewToBack(
not_null<HistoryItem*> item,
bool unread);
not_null<HistoryItem*> addNewInTheMiddle( not_null<HistoryItem*> addNewInTheMiddle(
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
int blockIndex, int blockIndex,

View File

@ -187,6 +187,11 @@ TimeId HistoryItem::date() const {
return _date; return _date;
} }
TimeId HistoryItem::NewMessageDate(TimeId scheduled) {
const auto now = base::unixtime::now();
return scheduled ? std::max(scheduled, now + 60) : now;
}
void HistoryItem::finishEdition(int oldKeyboardTop) { void HistoryItem::finishEdition(int oldKeyboardTop) {
_history->owner().requestItemViewRefresh(this); _history->owner().requestItemViewRefresh(this);
invalidateChatListEntry(); invalidateChatListEntry();

View File

@ -86,7 +86,8 @@ public:
UserData *getMessageBot() const; UserData *getMessageBot() const;
[[nodiscard]] bool isHistoryEntry() const { [[nodiscard]] bool isHistoryEntry() const {
return (id < ServerMaxMsgId); return IsServerMsgId(id)
|| (_clientFlags & MTPDmessage_ClientFlag::f_local_history_entry);
} }
[[nodiscard]] bool isFromScheduled() const { [[nodiscard]] bool isFromScheduled() const {
return isHistoryEntry() return isHistoryEntry()
@ -190,13 +191,13 @@ public:
return _clientFlags & MTPDmessage_ClientFlag::f_failed; return _clientFlags & MTPDmessage_ClientFlag::f_failed;
} }
void sendFailed(); void sendFailed();
virtual int viewsCount() const { [[nodiscard]] virtual int viewsCount() const {
return hasViews() ? 1 : -1; return hasViews() ? 1 : -1;
} }
virtual bool needCheck() const; [[nodiscard]] virtual bool needCheck() const;
virtual bool serviceMsg() const { [[nodiscard]] virtual bool serviceMsg() const {
return false; return false;
} }
virtual void applyEdition(const MTPDmessage &message) { virtual void applyEdition(const MTPDmessage &message) {
@ -218,14 +219,14 @@ public:
virtual void addToUnreadMentions(UnreadMentionType type); virtual void addToUnreadMentions(UnreadMentionType type);
virtual void eraseFromUnreadMentions() { virtual void eraseFromUnreadMentions() {
} }
virtual Storage::SharedMediaTypesMask sharedMediaTypes() const = 0; [[nodiscard]] virtual Storage::SharedMediaTypesMask sharedMediaTypes() const = 0;
void indexAsNewItem(); void indexAsNewItem();
virtual QString notificationHeader() const { [[nodiscard]] virtual QString notificationHeader() const {
return QString(); return QString();
} }
virtual QString notificationText() const; [[nodiscard]] virtual QString notificationText() const;
enum class DrawInDialog { enum class DrawInDialog {
Normal, Normal,
@ -234,15 +235,15 @@ public:
// Returns text with link-start and link-end commands for service-color highlighting. // Returns text with link-start and link-end commands for service-color highlighting.
// Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text" // Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text"
virtual QString inDialogsText(DrawInDialog way) const; [[nodiscard]] virtual QString inDialogsText(DrawInDialog way) const;
virtual QString inReplyText() const { [[nodiscard]] virtual QString inReplyText() const {
return inDialogsText(DrawInDialog::WithoutSender); return inDialogsText(DrawInDialog::WithoutSender);
} }
virtual Ui::Text::IsolatedEmoji isolatedEmoji() const; [[nodiscard]] virtual Ui::Text::IsolatedEmoji isolatedEmoji() const;
virtual TextWithEntities originalText() const { [[nodiscard]] virtual TextWithEntities originalText() const {
return TextWithEntities(); return TextWithEntities();
} }
virtual TextForMimeData clipboardText() const { [[nodiscard]] virtual TextForMimeData clipboardText() const {
return TextForMimeData(); return TextForMimeData();
} }
@ -259,81 +260,83 @@ public:
const HistoryItem *&cacheFor, const HistoryItem *&cacheFor,
Ui::Text::String &cache) const; Ui::Text::String &cache) const;
bool emptyText() const { [[nodiscard]] bool emptyText() const {
return _text.isEmpty(); return _text.isEmpty();
} }
bool isPinned() const; [[nodiscard]] bool isPinned() const;
bool canPin() const; [[nodiscard]] bool canPin() const;
bool canStopPoll() const; [[nodiscard]] bool canStopPoll() const;
virtual bool allowsSendNow() const; [[nodiscard]] virtual bool allowsSendNow() const;
virtual bool allowsForward() const; [[nodiscard]] virtual bool allowsForward() const;
virtual bool allowsEdit(TimeId now) const; [[nodiscard]] virtual bool allowsEdit(TimeId now) const;
bool canDelete() const; [[nodiscard]] bool canDelete() const;
bool canDeleteForEveryone(TimeId now) const; [[nodiscard]] bool canDeleteForEveryone(TimeId now) const;
bool suggestReport() const; [[nodiscard]] bool suggestReport() const;
bool suggestBanReport() const; [[nodiscard]] bool suggestBanReport() const;
bool suggestDeleteAllReport() const; [[nodiscard]] bool suggestDeleteAllReport() const;
bool hasDirectLink() const; [[nodiscard]] bool hasDirectLink() const;
MsgId id; [[nodiscard]] ChannelId channelId() const;
[[nodiscard]] FullMsgId fullId() const {
ChannelId channelId() const;
FullMsgId fullId() const {
return FullMsgId(channelId(), id); return FullMsgId(channelId(), id);
} }
Data::MessagePosition position() const; [[nodiscard]] Data::MessagePosition position() const;
TimeId date() const; [[nodiscard]] TimeId date() const;
Data::Media *media() const { [[nodiscard]] static TimeId NewMessageDate(TimeId scheduled);
[[nodiscard]] Data::Media *media() const {
return _media.get(); return _media.get();
} }
virtual void setText(const TextWithEntities &textWithEntities) { virtual void setText(const TextWithEntities &textWithEntities) {
} }
virtual bool textHasLinks() const { [[nodiscard]] virtual bool textHasLinks() const {
return false; return false;
} }
virtual HistoryMessage *toHistoryMessage() { // dynamic_cast optimize [[nodiscard]] virtual HistoryMessage *toHistoryMessage() { // dynamic_cast optimize
return nullptr; return nullptr;
} }
virtual const HistoryMessage *toHistoryMessage() const { // dynamic_cast optimize [[nodiscard]] virtual const HistoryMessage *toHistoryMessage() const { // dynamic_cast optimize
return nullptr; return nullptr;
} }
MsgId replyToId() const; [[nodiscard]] MsgId replyToId() const;
not_null<PeerData*> author() const; [[nodiscard]] not_null<PeerData*> author() const;
TimeId dateOriginal() const; [[nodiscard]] TimeId dateOriginal() const;
PeerData *senderOriginal() const; [[nodiscard]] PeerData *senderOriginal() const;
const HiddenSenderInfo *hiddenForwardedInfo() const; [[nodiscard]] const HiddenSenderInfo *hiddenForwardedInfo() const;
not_null<PeerData*> fromOriginal() const; [[nodiscard]] not_null<PeerData*> fromOriginal() const;
QString authorOriginal() const; [[nodiscard]] QString authorOriginal() const;
MsgId idOriginal() const; [[nodiscard]] MsgId idOriginal() const;
bool isEmpty() const; [[nodiscard]] bool isEmpty() const;
MessageGroupId groupId() const; [[nodiscard]] MessageGroupId groupId() const;
const HistoryMessageReplyMarkup *inlineReplyMarkup() const { [[nodiscard]] const HistoryMessageReplyMarkup *inlineReplyMarkup() const {
return const_cast<HistoryItem*>(this)->inlineReplyMarkup(); return const_cast<HistoryItem*>(this)->inlineReplyMarkup();
} }
const ReplyKeyboard *inlineReplyKeyboard() const { [[nodiscard]] const ReplyKeyboard *inlineReplyKeyboard() const {
return const_cast<HistoryItem*>(this)->inlineReplyKeyboard(); return const_cast<HistoryItem*>(this)->inlineReplyKeyboard();
} }
HistoryMessageReplyMarkup *inlineReplyMarkup(); [[nodiscard]] HistoryMessageReplyMarkup *inlineReplyMarkup();
ReplyKeyboard *inlineReplyKeyboard(); [[nodiscard]] ReplyKeyboard *inlineReplyKeyboard();
[[nodiscard]] ChannelData *discussionPostOriginalSender() const; [[nodiscard]] ChannelData *discussionPostOriginalSender() const;
[[nodiscard]] bool isDiscussionPost() const; [[nodiscard]] bool isDiscussionPost() const;
[[nodiscard]] PeerData *displayFrom() const; [[nodiscard]] PeerData *displayFrom() const;
virtual std::unique_ptr<HistoryView::Element> createView( [[nodiscard]] virtual std::unique_ptr<HistoryView::Element> createView(
not_null<HistoryView::ElementDelegate*> delegate) = 0; not_null<HistoryView::ElementDelegate*> delegate) = 0;
virtual ~HistoryItem(); virtual ~HistoryItem();
MsgId id;
protected: protected:
HistoryItem( HistoryItem(
not_null<History*> history, not_null<History*> history,

View File

@ -799,8 +799,8 @@ not_null<HistoryService*> GenerateJoinedMessage(
MTPDmessage::Flags flags) { MTPDmessage::Flags flags) {
return new HistoryService( return new HistoryService(
history, history,
MTPDmessage_ClientFlags(), MTPDmessage_ClientFlag::f_local_history_entry,
clientMsgId(), history->owner().nextLocalMessageId(),
inviteDate, inviteDate,
GenerateJoinedText(history, inviter), GenerateJoinedText(history, inviter),
flags); flags);

View File

@ -634,12 +634,19 @@ HistoryWidget::HistoryWidget(
) | rpl::filter([=](const Api::SendAction &action) { ) | rpl::filter([=](const Api::SendAction &action) {
return (action.history == _history); return (action.history == _history);
}) | rpl::start_with_next([=](const Api::SendAction &action) { }) | rpl::start_with_next([=](const Api::SendAction &action) {
fastShowAtEnd(action.history); if (action.options.scheduled) {
const auto lastKeyboardUsed = lastForceReplyReplied(FullMsgId( crl::on_main(this, [=, history = action.history]{
action.history->channelId(), controller->showSection(
action.replyTo)); HistoryView::ScheduledMemento(action.history));
if (cancelReply(lastKeyboardUsed) && !action.clearDraft) { });
onCloudDraftSave(); } else {
fastShowAtEnd(action.history);
const auto lastKeyboardUsed = lastForceReplyReplied(FullMsgId(
action.history->channelId(),
action.replyTo));
if (cancelReply(lastKeyboardUsed) && !action.clearDraft) {
onCloudDraftSave();
}
} }
if (action.options.handleSupportSwitch) { if (action.options.handleSupportSwitch) {
handleSupportSwitch(action.history); handleSupportSwitch(action.history);
@ -2953,7 +2960,7 @@ void HistoryWidget::sendSilent() {
void HistoryWidget::sendScheduled() { void HistoryWidget::sendScheduled() {
auto options = Api::SendOptions(); auto options = Api::SendOptions();
options.scheduled = INT_MAX; options.scheduled = base::unixtime::now() + 86400;
send(options); send(options);
} }
@ -4400,7 +4407,8 @@ void HistoryWidget::sendFileConfirmed(
channelId, channelId,
file->to.replyTo)); file->to.replyTo));
const auto newId = oldId.value_or(FullMsgId(channelId, clientMsgId())); const auto newId = oldId.value_or(
FullMsgId(channelId, session().data().nextLocalMessageId()));
auto groupId = file->album ? file->album->groupId : uint64(0); auto groupId = file->album ? file->album->groupId : uint64(0);
if (file->album) { if (file->album) {
const auto proj = [](const SendingAlbum::Item &item) { const auto proj = [](const SendingAlbum::Item &item) {
@ -4423,6 +4431,7 @@ void HistoryWidget::sendFileConfirmed(
const auto peer = history->peer; const auto peer = history->peer;
auto action = Api::SendAction(history); auto action = Api::SendAction(history);
action.options = file->to.options;
action.clearDraft = false; action.clearDraft = false;
action.replyTo = file->to.replyTo; action.replyTo = file->to.replyTo;
action.generateLocal = true; action.generateLocal = true;
@ -4469,6 +4478,12 @@ void HistoryWidget::sendFileConfirmed(
if (groupId) { if (groupId) {
flags |= MTPDmessage::Flag::f_grouped_id; flags |= MTPDmessage::Flag::f_grouped_id;
} }
if (file->to.options.scheduled) {
flags |= MTPDmessage::Flag::f_from_scheduled;
} else {
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
}
const auto messageFromId = channelPost ? 0 : session().userId(); const auto messageFromId = channelPost ? 0 : session().userId();
const auto messagePostAuthor = channelPost const auto messagePostAuthor = channelPost
? App::peerName(session().user()) ? App::peerName(session().user())
@ -4489,7 +4504,7 @@ void HistoryWidget::sendFileConfirmed(
MTPMessageFwdHeader(), MTPMessageFwdHeader(),
MTPint(), MTPint(),
MTP_int(file->to.replyTo), MTP_int(file->to.replyTo),
MTP_int(base::unixtime::now()), MTP_int(HistoryItem::NewMessageDate(file->to.options.scheduled)),
MTP_string(caption.text), MTP_string(caption.text),
photo, photo,
MTPReplyMarkup(), MTPReplyMarkup(),
@ -4525,7 +4540,7 @@ void HistoryWidget::sendFileConfirmed(
MTPMessageFwdHeader(), MTPMessageFwdHeader(),
MTPint(), MTPint(),
MTP_int(file->to.replyTo), MTP_int(file->to.replyTo),
MTP_int(base::unixtime::now()), MTP_int(HistoryItem::NewMessageDate(file->to.options.scheduled)),
MTP_string(caption.text), MTP_string(caption.text),
document, document,
MTPReplyMarkup(), MTPReplyMarkup(),
@ -4564,7 +4579,8 @@ void HistoryWidget::sendFileConfirmed(
MTPMessageFwdHeader(), MTPMessageFwdHeader(),
MTPint(), MTPint(),
MTP_int(file->to.replyTo), MTP_int(file->to.replyTo),
MTP_int(base::unixtime::now()), MTP_int(
HistoryItem::NewMessageDate(file->to.options.scheduled)),
MTP_string(caption.text), MTP_string(caption.text),
document, document,
MTPReplyMarkup(), MTPReplyMarkup(),

View File

@ -3899,19 +3899,25 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
const auto &d = update.c_updateMessageID(); const auto &d = update.c_updateMessageID();
const auto randomId = d.vrandom_id().v; const auto randomId = d.vrandom_id().v;
if (const auto id = session().data().messageIdByRandomId(randomId)) { if (const auto id = session().data().messageIdByRandomId(randomId)) {
const auto channel = id.channel;
const auto newId = d.vid().v; const auto newId = d.vid().v;
if (const auto local = session().data().message(id)) { if (const auto local = session().data().message(id)) {
const auto existing = session().data().message(channel, newId); if (local->isScheduled()) {
if (existing && !local->mainView()) { session().data().scheduledMessages().apply(d, local);
const auto history = local->history();
local->destroy();
history->requestChatListMessage();
} else { } else {
if (existing) { const auto channel = id.channel;
existing->destroy(); const auto existing = session().data().message(
channel,
newId);
if (existing && !local->mainView()) {
const auto history = local->history();
local->destroy();
history->requestChatListMessage();
} else {
if (existing) {
existing->destroy();
}
local->setRealId(d.vid().v);
} }
local->setRealId(d.vid().v);
} }
} }
session().data().unregisterMessageRandomId(randomId); session().data().unregisterMessageRandomId(randomId);

View File

@ -66,8 +66,8 @@ enum class MTPDmessage_ClientFlag : uint32 {
// message has no media and only a several emoji text // message has no media and only a several emoji text
f_isolated_emoji = (1U << 21), f_isolated_emoji = (1U << 21),
// update this when adding new client side flags // message is local message existing in the message history
MIN_FIELD = (1U << 21), f_local_history_entry = (1U << 20),
}; };
inline constexpr bool is_flag_type(MTPDmessage_ClientFlag) { return true; } inline constexpr bool is_flag_type(MTPDmessage_ClientFlag) { return true; }
using MTPDmessage_ClientFlags = base::flags<MTPDmessage_ClientFlag>; using MTPDmessage_ClientFlags = base::flags<MTPDmessage_ClientFlag>;

View File

@ -1520,7 +1520,9 @@ void FormController::uploadEncryptedFile(
prepared->setFileData(prepared->content); prepared->setFileData(prepared->content);
prepared->filemd5 = file.uploadData->md5checksum; prepared->filemd5 = file.uploadData->md5checksum;
file.uploadData->fullId = FullMsgId(0, clientMsgId()); file.uploadData->fullId = FullMsgId(
0,
Auth().data().nextLocalMessageId());
Auth().uploader().upload(file.uploadData->fullId, std::move(prepared)); Auth().uploader().upload(file.uploadData->fullId, std::move(prepared));
} }

View File

@ -429,7 +429,7 @@ void ChatBackground::checkUploadWallPaper() {
const auto ready = PrepareWallPaper(_original); const auto ready = PrepareWallPaper(_original);
const auto documentId = ready.id; const auto documentId = ready.id;
_wallPaperUploadId = FullMsgId(0, clientMsgId()); _wallPaperUploadId = FullMsgId(0, _session->data().nextLocalMessageId());
_session->uploader().uploadMedia(_wallPaperUploadId, ready); _session->uploader().uploadMedia(_wallPaperUploadId, ready);
if (_wallPaperUploadLifetime) { if (_wallPaperUploadLifetime) {
return; return;