Move shareContact and readServerHistory to ApiWrap.

Also allow non-confirming contact info sharing to Saved Messages.
This commit is contained in:
John Preston 2017-12-07 17:02:24 +04:00
parent f0a03223e8
commit 5bc47e5203
15 changed files with 551 additions and 431 deletions

View File

@ -31,7 +31,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "mainwindow.h" #include "mainwindow.h"
#include "messenger.h" #include "messenger.h"
#include "mainwidget.h" #include "mainwidget.h"
#include "history/history_widget.h" #include "boxes/add_contact_box.h"
#include "history/history_message.h"
#include "storage/localstorage.h" #include "storage/localstorage.h"
#include "auth_session.h" #include "auth_session.h"
#include "boxes/confirm_box.h" #include "boxes/confirm_box.h"
@ -171,7 +172,7 @@ void ApiWrap::resolveMessageDatas() {
gotMessageDatas(nullptr, result, requestId); gotMessageDatas(nullptr, result, requestId);
}).fail([this](const RPCError &error, mtpRequestId requestId) { }).fail([this](const RPCError &error, mtpRequestId requestId) {
finalizeMessageDataRequest(nullptr, requestId); finalizeMessageDataRequest(nullptr, requestId);
}).after(kSmallDelayMs).send(); }).afterDelay(kSmallDelayMs).send();
for (auto &request : _messageDataRequests) { for (auto &request : _messageDataRequests) {
if (request.requestId > 0) continue; if (request.requestId > 0) continue;
request.requestId = requestId; request.requestId = requestId;
@ -192,7 +193,7 @@ void ApiWrap::resolveMessageDatas() {
gotMessageDatas(channel, result, requestId); gotMessageDatas(channel, result, requestId);
}).fail([=](const RPCError &error, mtpRequestId requestId) { }).fail([=](const RPCError &error, mtpRequestId requestId) {
finalizeMessageDataRequest(channel, requestId); finalizeMessageDataRequest(channel, requestId);
}).after(kSmallDelayMs).send(); }).afterDelay(kSmallDelayMs).send();
for (auto &request : *j) { for (auto &request : *j) {
if (request.requestId > 0) continue; if (request.requestId > 0) continue;
@ -839,7 +840,7 @@ void ApiWrap::requestSelfParticipant(ChannelData *channel) {
if (error.type() == qstr("USER_NOT_PARTICIPANT")) { if (error.type() == qstr("USER_NOT_PARTICIPANT")) {
channel->inviter = -1; channel->inviter = -1;
} }
}).after(kSmallDelayMs).send(); }).afterDelay(kSmallDelayMs).send();
_selfParticipantRequests.insert(channel, requestId); _selfParticipantRequests.insert(channel, requestId);
} }
@ -933,7 +934,7 @@ void ApiWrap::requestStickerSets() {
gotStickerSet(setId, result); gotStickerSet(setId, result);
}).fail([this, setId = i.key()](const RPCError &error) { }).fail([this, setId = i.key()](const RPCError &error) {
_stickerSetRequests.remove(setId); _stickerSetRequests.remove(setId);
}).after(waitMs).send(); }).afterDelay(waitMs).send();
} }
} }
@ -987,7 +988,7 @@ void ApiWrap::saveStickerSets(const Stickers::Order &localOrder, const Stickers:
stickerSetDisenabled(requestId); stickerSetDisenabled(requestId);
}).fail([this](const RPCError &error, mtpRequestId requestId) { }).fail([this](const RPCError &error, mtpRequestId requestId) {
stickerSetDisenabled(requestId); stickerSetDisenabled(requestId);
}).after(kSmallDelayMs).send(); }).afterDelay(kSmallDelayMs).send();
_stickerSetDisenableRequests.insert(requestId); _stickerSetDisenableRequests.insert(requestId);
@ -1024,7 +1025,7 @@ void ApiWrap::saveStickerSets(const Stickers::Order &localOrder, const Stickers:
stickerSetDisenabled(requestId); stickerSetDisenabled(requestId);
}).fail([this](const RPCError &error, mtpRequestId requestId) { }).fail([this](const RPCError &error, mtpRequestId requestId) {
stickerSetDisenabled(requestId); stickerSetDisenabled(requestId);
}).after(kSmallDelayMs).send(); }).afterDelay(kSmallDelayMs).send();
_stickerSetDisenableRequests.insert(requestId); _stickerSetDisenableRequests.insert(requestId);
@ -1336,13 +1337,30 @@ void ApiWrap::clearHistory(not_null<PeerData*> peer) {
int ApiWrap::applyAffectedHistory( int ApiWrap::applyAffectedHistory(
not_null<PeerData*> peer, not_null<PeerData*> peer,
const MTPmessages_AffectedHistory &result) { const MTPmessages_AffectedHistory &result) {
auto &d = result.c_messages_affectedHistory(); const auto &data = result.c_messages_affectedHistory();
if (peer && peer->isChannel()) { if (const auto channel = peer->asChannel()) {
peer->asChannel()->ptsUpdateAndApply(d.vpts.v, d.vpts_count.v); channel->ptsUpdateAndApply(data.vpts.v, data.vpts_count.v);
} else { } else {
App::main()->ptsUpdateAndApply(d.vpts.v, d.vpts_count.v); App::main()->ptsUpdateAndApply(data.vpts.v, data.vpts_count.v);
} }
return d.voffset.v; return data.voffset.v;
}
void ApiWrap::applyAffectedMessages(
not_null<PeerData*> peer,
const MTPmessages_AffectedMessages &result) {
const auto &data = result.c_messages_affectedMessages();
if (const auto channel = peer->asChannel()) {
channel->ptsUpdateAndApply(data.vpts.v, data.vpts_count.v);
} else {
applyAffectedMessages(result);
}
}
void ApiWrap::applyAffectedMessages(
const MTPmessages_AffectedMessages &result) {
const auto &data = result.c_messages_affectedMessages();
App::main()->ptsUpdateAndApply(data.vpts.v, data.vpts_count.v);
} }
void ApiWrap::saveDraftsToCloud() { void ApiWrap::saveDraftsToCloud() {
@ -1522,13 +1540,13 @@ void ApiWrap::resolveWebPages() {
if (!ids.isEmpty()) { if (!ids.isEmpty()) {
requestId = request(MTPmessages_GetMessages(MTP_vector<MTPint>(ids))).done([this](const MTPmessages_Messages &result, mtpRequestId requestId) { requestId = request(MTPmessages_GetMessages(MTP_vector<MTPint>(ids))).done([this](const MTPmessages_Messages &result, mtpRequestId requestId) {
gotWebPages(nullptr, result, requestId); gotWebPages(nullptr, result, requestId);
}).after(kSmallDelayMs).send(); }).afterDelay(kSmallDelayMs).send();
} }
QVector<mtpRequestId> reqsByIndex(idsByChannel.size(), 0); QVector<mtpRequestId> reqsByIndex(idsByChannel.size(), 0);
for (auto i = idsByChannel.cbegin(), e = idsByChannel.cend(); i != e; ++i) { for (auto i = idsByChannel.cbegin(), e = idsByChannel.cend(); i != e; ++i) {
reqsByIndex[i.value().first] = request(MTPchannels_GetMessages(i.key()->inputChannel, MTP_vector<MTPint>(i.value().second))).done([this, channel = i.key()](const MTPmessages_Messages &result, mtpRequestId requestId) { reqsByIndex[i.value().first] = request(MTPchannels_GetMessages(i.key()->inputChannel, MTP_vector<MTPint>(i.value().second))).done([this, channel = i.key()](const MTPmessages_Messages &result, mtpRequestId requestId) {
gotWebPages(channel, result, requestId); gotWebPages(channel, result, requestId);
}).after(kSmallDelayMs).send(); }).afterDelay(kSmallDelayMs).send();
} }
if (requestId || !reqsByIndex.isEmpty()) { if (requestId || !reqsByIndex.isEmpty()) {
for (auto &pendingRequestId : _webPagesPending) { for (auto &pendingRequestId : _webPagesPending) {
@ -2242,7 +2260,7 @@ void ApiWrap::sendSaveChatAdminsRequests(not_null<ChatData*> chat) {
if (error.type() == qstr("USER_RESTRICTED")) { if (error.type() == qstr("USER_RESTRICTED")) {
Ui::show(Box<InformBox>(lang(lng_cant_do_this))); Ui::show(Box<InformBox>(lang(lng_cant_do_this)));
} }
}).canWait(5).send(); }).afterDelay(kSmallDelayMs).send();
_chatAdminsSaveRequests[chat].insert(requestId); _chatAdminsSaveRequests[chat].insert(requestId);
}; };
@ -2388,6 +2406,12 @@ void ApiWrap::userPhotosDone(
)); ));
} }
void ApiWrap::sendAction(const SendOptions &options) {
readServerHistory(options.history);
options.history->getReadyFor(ShowAtTheEndMsgId);
_sendActions.fire_copy(options);
}
void ApiWrap::forwardMessages( void ApiWrap::forwardMessages(
HistoryItemsList &&items, HistoryItemsList &&items,
const SendOptions &options, const SendOptions &options,
@ -2409,7 +2433,7 @@ void ApiWrap::forwardMessages(
const auto genClientSideMessage = options.generateLocal && (count < 2); const auto genClientSideMessage = options.generateLocal && (count < 2);
const auto history = options.history; const auto history = options.history;
App::main()->readServerHistory(history); readServerHistory(history);
const auto channelPost = history->peer->isChannel() const auto channelPost = history->peer->isChannel()
&& !history->peer->isMegagroup(); && !history->peer->isMegagroup();
@ -2450,7 +2474,7 @@ void ApiWrap::forwardMessages(
if (shared && !--shared->requestsLeft) { if (shared && !--shared->requestsLeft) {
shared->callback(); shared->callback();
} }
}).after( }).afterRequest(
history->sendRequestId history->sendRequestId
).send(); ).send();
@ -2494,4 +2518,187 @@ void ApiWrap::forwardMessages(
sendAccumulated(); sendAccumulated();
} }
void ApiWrap::shareContact(
const QString &phone,
const QString &firstName,
const QString &lastName,
const SendOptions &options) {
const auto userId = UserId(0);
sendSharedContact(phone, firstName, lastName, userId, options);
}
void ApiWrap::shareContact(
not_null<UserData*> user,
const SendOptions &options) {
const auto userId = peerToUser(user->id);
const auto phone = user->phone().isEmpty()
? App::phoneFromSharedContact(userId)
: user->phone();
if (phone.isEmpty()) {
return;
}
sendSharedContact(
phone,
user->firstName,
user->lastName,
userId,
options);
}
void ApiWrap::sendSharedContact(
const QString &phone,
const QString &firstName,
const QString &lastName,
UserId userId,
const SendOptions &options) {
sendAction(options);
const auto history = options.history;
const auto peer = history->peer;
const auto randomId = rand_value<uint64>();
const auto newId = FullMsgId(history->channelId(), clientMsgId());
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
const auto silentPost = channelPost && options.silent;
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;
}
if (channelPost) {
flags |= MTPDmessage::Flag::f_views;
flags |= MTPDmessage::Flag::f_post;
if (peer->asChannel()->addsSignature()) {
flags |= MTPDmessage::Flag::f_post_author;
}
} else {
flags |= MTPDmessage::Flag::f_from_id;
}
if (silentPost) {
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
}
const auto messageFromId = channelPost ? 0 : Auth().userId();
const auto messagePostAuthor = channelPost
? (Auth().user()->firstName + ' ' + Auth().user()->lastName)
: QString();
history->addNewMessage(
MTP_message(
MTP_flags(flags),
MTP_int(newId.msg),
MTP_int(messageFromId),
peerToMTP(peer->id),
MTPnullFwdHeader,
MTPint(),
MTP_int(options.replyTo),
MTP_int(unixtime()),
MTP_string(""),
MTP_messageMediaContact(
MTP_string(phone),
MTP_string(firstName),
MTP_string(lastName),
MTP_int(userId)),
MTPnullMarkup,
MTPnullEntities,
MTP_int(1),
MTPint(),
MTP_string(messagePostAuthor),
MTPlong()),
NewMessageUnread);
history->sendRequestId = request(MTPmessages_SendMedia(
MTP_flags(sendFlags),
peer->input,
MTP_int(options.replyTo),
MTP_inputMediaContact(
MTP_string(phone),
MTP_string(firstName),
MTP_string(lastName)),
MTP_long(randomId),
MTPnullMarkup
)).done([=](const MTPUpdates &result) {
applyUpdates(result);
}).fail([](const RPCError &error) {
if (error.type() == qstr("PEER_FLOOD")) {
Ui::show(Box<InformBox>(
PeerFloodErrorText(PeerFloodType::Send)));
} else if (error.type() == qstr("USER_BANNED_IN_CHANNEL")) {
const auto link = textcmdLink(
Messenger::Instance().createInternalLinkFull(qsl("spambot")),
lang(lng_cant_more_info));
Ui::show(Box<InformBox>(lng_error_public_groups_denied(
lt_more_info,
link)));
}
}).afterRequest(
history->sendRequestId
).send();
App::historyRegRandom(randomId, newId);
}
void ApiWrap::readServerHistory(not_null<History*> history) {
if (history->unreadCount()) {
readServerHistoryForce(history);
}
}
void ApiWrap::readServerHistoryForce(not_null<History*> history) {
const auto peer = history->peer;
const auto upTo = history->inboxRead(0);
if (const auto channel = peer->asChannel()) {
if (!channel->amIn()) {
return; // no read request for channels that I didn't join
} else if (const auto migrateFrom = channel->migrateFrom()) {
if (const auto migrated = App::historyLoaded(migrateFrom)) {
readServerHistory(migrated);
}
}
}
if (_readRequests.contains(peer)) {
const auto i = _readRequestsPending.find(peer);
if (i == _readRequestsPending.cend()) {
_readRequestsPending.emplace(peer, upTo);
} else if (i->second < upTo) {
i->second = upTo;
}
} else {
sendReadRequest(peer, upTo);
}
}
void ApiWrap::sendReadRequest(not_null<PeerData*> peer, MsgId upTo) {
const auto requestId = [&] {
const auto finished = [=] {
_readRequests.remove(peer);
if (const auto next = _readRequestsPending.take(peer)) {
sendReadRequest(peer, *next);
}
};
if (const auto channel = peer->asChannel()) {
return request(MTPchannels_ReadHistory(
channel->inputChannel,
MTP_int(upTo)
)).done([=](const MTPBool &result) {
finished();
}).fail([=](const RPCError &error) {
finished();
}).send();
}
return request(MTPmessages_ReadHistory(
peer->input,
MTP_int(upTo)
)).done([=](const MTPmessages_AffectedMessages &result) {
applyAffectedMessages(peer, result);
finished();
}).fail([=](const RPCError &error) {
finished();
}).send();
}();
_readRequests.emplace(peer, requestId, upTo);
}
ApiWrap::~ApiWrap() = default; ApiWrap::~ApiWrap() = default;

View File

@ -167,13 +167,25 @@ public:
MsgId replyTo = 0; MsgId replyTo = 0;
bool silent = false; bool silent = false;
WebPageId webPageId = 0; WebPageId webPageId = 0;
bool clearDraft = true; bool clearDraft = false;
bool generateLocal = true; bool generateLocal = true;
}; };
rpl::producer<SendOptions> sendActions() const {
return _sendActions.events();
}
void sendAction(const SendOptions &options);
void forwardMessages( void forwardMessages(
HistoryItemsList &&items, HistoryItemsList &&items,
const SendOptions &options, const SendOptions &options,
base::lambda_once<void()> &&successCallback = nullptr); base::lambda_once<void()> &&successCallback = nullptr);
void shareContact(
const QString &phone,
const QString &firstName,
const QString &lastName,
const SendOptions &options);
void shareContact(not_null<UserData*> user, const SendOptions &options);
void readServerHistory(not_null<History*> history);
void readServerHistoryForce(not_null<History*> history);
~ApiWrap(); ~ApiWrap();
@ -245,10 +257,6 @@ private:
const QDate &date, const QDate &date,
Callback &&callback); Callback &&callback);
int applyAffectedHistory(
not_null<PeerData*> peer,
const MTPmessages_AffectedHistory &result);
void sharedMediaDone( void sharedMediaDone(
not_null<PeerData*> peer, not_null<PeerData*> peer,
SharedMediaType type, SharedMediaType type,
@ -261,6 +269,22 @@ private:
PhotoId photoId, PhotoId photoId,
const MTPphotos_Photos &result); const MTPphotos_Photos &result);
void sendSharedContact(
const QString &phone,
const QString &firstName,
const QString &lastName,
UserId userId,
const SendOptions &options);
void sendReadRequest(not_null<PeerData*> peer, MsgId upTo);
int applyAffectedHistory(
not_null<PeerData*> peer,
const MTPmessages_AffectedHistory &result);
void applyAffectedMessages(const MTPmessages_AffectedMessages &result);
void applyAffectedMessages(
not_null<PeerData*> peer,
const MTPmessages_AffectedMessages &result);
not_null<AuthSession*> _session; not_null<AuthSession*> _session;
mtpRequestId _changelogSubscription = 0; mtpRequestId _changelogSubscription = 0;
@ -339,6 +363,20 @@ private:
base::flat_map<not_null<UserData*>, mtpRequestId> _userPhotosRequests; base::flat_map<not_null<UserData*>, mtpRequestId> _userPhotosRequests;
rpl::event_stream<SendOptions> _sendActions;
struct ReadRequest {
ReadRequest(mtpRequestId requestId, MsgId upTo)
: requestId(requestId)
, upTo(upTo) {
}
mtpRequestId requestId = 0;
MsgId upTo = 0;
};
base::flat_map<not_null<PeerData*>, ReadRequest> _readRequests;
base::flat_map<not_null<PeerData*>, MsgId> _readRequestsPending;
base::Observable<PeerData*> _fullPeerUpdated; base::Observable<PeerData*> _fullPeerUpdated;
rpl::event_stream<uint64> _stickerSetInstalled; rpl::event_stream<uint64> _stickerSetInstalled;

View File

@ -103,24 +103,16 @@ public:
using value_type = pair_type; using value_type = pair_type;
using difference_type = typename iterator_impl::difference_type; using difference_type = typename iterator_impl::difference_type;
using pointer = pointer_impl; using pointer = pointer_impl;
using const_pointer = const pair_type*;
using reference = reference_impl; using reference = reference_impl;
using const_reference = const pair_type&;
flat_multi_map_iterator_base_impl(iterator_impl impl = iterator_impl()) flat_multi_map_iterator_base_impl(iterator_impl impl = iterator_impl())
: _impl(impl) { : _impl(impl) {
} }
reference operator*() { reference operator*() const {
return *_impl; return *_impl;
} }
const_reference operator*() const { pointer operator->() const {
return *_impl;
}
pointer operator->() {
return std::addressof(**this);
}
const_pointer operator->() const {
return std::addressof(**this); return std::addressof(**this);
} }
Me &operator++() { Me &operator++() {
@ -166,10 +158,7 @@ public:
other_reference_impl> &right) const { other_reference_impl> &right) const {
return _impl - right._impl; return _impl - right._impl;
} }
reference operator[](difference_type offset) { reference operator[](difference_type offset) const {
return _impl[offset];
}
const_reference operator[](difference_type offset) const {
return _impl[offset]; return _impl[offset];
} }

View File

@ -756,7 +756,7 @@ void DialogsWidget::dragEnterEvent(QDragEnterEvent *e) {
e->setDropAction(Qt::CopyAction); e->setDropAction(Qt::CopyAction);
e->accept(); e->accept();
updateDragInScroll(_scroll->geometry().contains(e->pos())); updateDragInScroll(_scroll->geometry().contains(e->pos()));
} else if (App::main() && App::main()->getDragState(e->mimeData()) != DragStateNone) { } else if (App::main() && App::main()->getDragState(e->mimeData()) != DragState::None) {
e->setDropAction(Qt::CopyAction); e->setDropAction(Qt::CopyAction);
e->accept(); e->accept();
} }

View File

@ -27,6 +27,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "observer_peer.h" #include "observer_peer.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "mainwidget.h" #include "mainwidget.h"
#include "apiwrap.h"
#include "messenger.h" #include "messenger.h"
#include "auth_session.h" #include "auth_session.h"
#include "boxes/confirm_box.h" #include "boxes/confirm_box.h"
@ -121,10 +122,13 @@ void activateBotCommand(const HistoryItem *msg, int row, int col) {
case ButtonType::RequestPhone: { case ButtonType::RequestPhone: {
hideSingleUseKeyboard(msg); hideSingleUseKeyboard(msg);
Ui::show(Box<ConfirmBox>(lang(lng_bot_share_phone), lang(lng_bot_share_phone_confirm), [peerId = msg->history()->peer->id] { const auto msgId = msg->id;
if (auto m = App::main()) { const auto history = msg->history();
m->onShareContact(peerId, App::self()); Ui::show(Box<ConfirmBox>(lang(lng_bot_share_phone), lang(lng_bot_share_phone_confirm), [=] {
} Ui::showPeerHistory(history, ShowAtTheEndMsgId);
auto options = ApiWrap::SendOptions(history);
options.replyTo = msgId;
Auth().api().shareContact(App::self(), options);
})); }));
} break; } break;

View File

@ -1,33 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
enum DragState {
DragStateNone = 0x00,
DragStateFiles = 0x01,
DragStatePhotoFiles = 0x02,
DragStateImage = 0x03,
};
enum class ReadServerHistoryChecks {
OnlyIfUnread,
ForceRequest,
};

View File

@ -395,7 +395,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
HistoryLayout::paintEmpty(p, width(), height()); HistoryLayout::paintEmpty(p, width(), height());
} }
if (!noHistoryDisplayed) { if (!noHistoryDisplayed) {
auto readMentions = HistoryItemsMap(); auto readMentions = base::flat_set<not_null<HistoryItem*>>();
adjustCurrent(clip.top()); adjustCurrent(clip.top());

View File

@ -185,13 +185,6 @@ HistoryHider::HistoryHider(
init(); init();
} }
HistoryHider::HistoryHider(MainWidget *parent, UserData *sharedContact) : RpWidget(parent)
, _sharedContact(sharedContact)
, _send(this, langFactory(lng_forward_send), st::defaultBoxButton)
, _cancel(this, langFactory(lng_cancel), st::defaultBoxButton) {
init();
}
HistoryHider::HistoryHider(MainWidget *parent) : RpWidget(parent) HistoryHider::HistoryHider(MainWidget *parent) : RpWidget(parent)
, _sendPath(true) , _sendPath(true)
, _send(this, langFactory(lng_forward_send), st::defaultBoxButton) , _send(this, langFactory(lng_forward_send), st::defaultBoxButton)
@ -231,7 +224,7 @@ void HistoryHider::refreshLang() {
} }
bool HistoryHider::withConfirm() const { bool HistoryHider::withConfirm() const {
return _sharedContact || _sendPath; return _sendPath;
} }
void HistoryHider::paintEvent(QPaintEvent *e) { void HistoryHider::paintEvent(QPaintEvent *e) {
@ -317,9 +310,7 @@ void HistoryHider::animationCallback() {
void HistoryHider::forward() { void HistoryHider::forward() {
if (!_hiding && _offered) { if (!_hiding && _offered) {
if (_sharedContact) { if (_sendPath) {
parent()->onShareContact(_offered->id, _sharedContact);
} else if (_sendPath) {
parent()->onSendPaths(_offered->id); parent()->onSendPaths(_offered->id);
} else if (!_shareUrl.isEmpty()) { } else if (!_shareUrl.isEmpty()) {
parent()->shareUrl(_offered, _shareUrl, _shareText); parent()->shareUrl(_offered, _shareUrl, _shareText);
@ -375,17 +366,7 @@ bool HistoryHider::offerPeer(PeerId peer) {
_offered = App::peer(peer); _offered = App::peer(peer);
auto phrase = QString(); auto phrase = QString();
auto recipient = _offered->isUser() ? _offered->name : '\xAB' + _offered->name + '\xBB'; auto recipient = _offered->isUser() ? _offered->name : '\xAB' + _offered->name + '\xBB';
if (_sharedContact) { if (_sendPath) {
if (!_offered->canWrite()) {
Ui::show(Box<InformBox>(lang(lng_forward_share_cant)));
_offered = nullptr;
_toText.setText(st::boxLabelStyle, QString());
_toTextWidth = 0;
resizeEvent(nullptr);
return false;
}
phrase = lng_forward_share_contact(lt_recipient, recipient);
} else if (_sendPath) {
auto toId = _offered->id; auto toId = _offered->id;
_offered = nullptr; _offered = nullptr;
if (parent()->onSendPaths(toId)) { if (parent()->onSendPaths(toId)) {
@ -766,6 +747,17 @@ HistoryWidget::HistoryWidget(QWidget *parent, not_null<Window::Controller*> cont
setMembersShowAreaActive(active); setMembersShowAreaActive(active);
}, _topBar->lifetime()); }, _topBar->lifetime());
Auth().api().sendActions(
) | rpl::start_with_next([this](const ApiWrap::SendOptions &options) {
fastShowAtEnd(options.history);
const auto lastKeyboardUsed = lastForceReplyReplied(FullMsgId(
options.history->channelId(),
options.replyTo));
if (cancelReply(lastKeyboardUsed) && !options.clearDraft) {
onCloudDraftSave();
}
}, lifetime());
orderWidgets(); orderWidgets();
} }
@ -1345,7 +1337,6 @@ void HistoryWidget::onRecordDone(QByteArray result, VoiceWaveform waveform, qint
auto to = FileLoadTo(_peer->id, _silent->checked(), replyToId()); auto to = FileLoadTo(_peer->id, _silent->checked(), replyToId());
auto caption = QString(); auto caption = QString();
_fileLoader.addTask(MakeShared<FileLoadTask>(result, duration, waveform, to, caption)); _fileLoader.addTask(MakeShared<FileLoadTask>(result, duration, waveform, to, caption));
cancelReplyAfterMediaSend(lastForceReplyReplied());
} }
void HistoryWidget::onRecordUpdate(quint16 level, qint32 samples) { void HistoryWidget::onRecordUpdate(quint16 level, qint32 samples) {
@ -1555,23 +1546,21 @@ void HistoryWidget::calcNextReplyReturn() {
if (!_replyReturn) updateControlsVisibility(); if (!_replyReturn) updateControlsVisibility();
} }
void HistoryWidget::fastShowAtEnd(History *h) { void HistoryWidget::fastShowAtEnd(not_null<History*> history) {
if (h == _history) { if (_history != history) {
h->getReadyFor(ShowAtTheEndMsgId); return;
}
clearAllLoadRequests(); clearAllLoadRequests();
setMsgId(ShowAtUnreadMsgId); setMsgId(ShowAtUnreadMsgId);
_historyInited = false; _historyInited = false;
if (h->isReadyFor(_showAtMsgId)) { if (_history->isReadyFor(_showAtMsgId)) {
historyLoaded(); historyLoaded();
} else { } else {
firstLoadMessages(); firstLoadMessages();
doneShow(); doneShow();
}
} else if (h) {
h->getReadyFor(ShowAtTheEndMsgId);
} }
} }
@ -2219,7 +2208,7 @@ void HistoryWidget::newUnreadMsg(History *history, HistoryItem *item) {
if (item->mentionsMe() && item->isMediaUnread()) { if (item->mentionsMe() && item->isMediaUnread()) {
App::main()->mediaMarkRead(item); App::main()->mediaMarkRead(item);
} }
historyWasRead(ReadServerHistoryChecks::ForceRequest); Auth().api().readServerHistoryForce(history);
return; return;
} }
} }
@ -2237,13 +2226,6 @@ void HistoryWidget::historyToDown(History *history) {
} }
} }
void HistoryWidget::historyWasRead(ReadServerHistoryChecks checks) {
App::main()->readServerHistory(_history, checks);
if (_migrated) {
App::main()->readServerHistory(_migrated, ReadServerHistoryChecks::OnlyIfUnread);
}
}
void HistoryWidget::unreadCountChanged(History *history) { void HistoryWidget::unreadCountChanged(History *history) {
if (history == _history || history == _migrated) { if (history == _history || history == _migrated) {
updateHistoryDownVisibility(); updateHistoryDownVisibility();
@ -2722,7 +2704,7 @@ void HistoryWidget::visibleAreaUpdated() {
auto showFromVisible = (showFrom && !showFrom->detached() && scrollBottom > _list->itemTop(showFrom)); auto showFromVisible = (showFrom && !showFrom->detached() && scrollBottom > _list->itemTop(showFrom));
auto atBottom = (scrollTop >= _scroll->scrollTopMax()); auto atBottom = (scrollTop >= _scroll->scrollTopMax());
if ((showFromVisible || atBottom) && App::wnd()->doWeReadServerHistory()) { if ((showFromVisible || atBottom) && App::wnd()->doWeReadServerHistory()) {
historyWasRead(ReadServerHistoryChecks::OnlyIfUnread); Auth().api().readServerHistory(_history);
} }
} }
controller()->floatPlayerAreaUpdated().notify(true); controller()->floatPlayerAreaUpdated().notify(true);
@ -2911,7 +2893,7 @@ void HistoryWidget::hideSelectorControlsAnimated() {
} }
} }
void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) { void HistoryWidget::onSend(bool ctrlShiftEnter) {
if (!_history) return; if (!_history) return;
if (_editMsgId) { if (_editMsgId) {
@ -2919,14 +2901,11 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) {
return; return;
} }
bool lastKeyboardUsed = lastForceReplyReplied(FullMsgId(_channel, replyTo));
WebPageId webPageId = _previewCancelled ? CancelledWebPageId : ((_previewData && _previewData->pendingTill >= 0) ? _previewData->id : 0); WebPageId webPageId = _previewCancelled ? CancelledWebPageId : ((_previewData && _previewData->pendingTill >= 0) ? _previewData->id : 0);
MainWidget::MessageToSend message; auto message = MainWidget::MessageToSend(_history);
message.history = _history;
message.textWithTags = _field->getTextWithTags(); message.textWithTags = _field->getTextWithTags();
message.replyTo = replyTo; message.replyTo = replyToId();
message.silent = _silent->checked(); message.silent = _silent->checked();
message.webPageId = webPageId; message.webPageId = webPageId;
App::main()->sendMessage(message); App::main()->sendMessage(message);
@ -2938,11 +2917,12 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) {
hideSelectorControlsAnimated(); hideSelectorControlsAnimated();
if (replyTo < 0) cancelReply(lastKeyboardUsed);
if (_previewData && _previewData->pendingTill) previewCancel(); if (_previewData && _previewData->pendingTill) previewCancel();
_field->setFocus(); _field->setFocus();
if (!_keyboard->hasMarkup() && _keyboard->forceReply() && !_kbReplyTo) onKbToggle(); if (!_keyboard->hasMarkup() && _keyboard->forceReply() && !_kbReplyTo) {
onKbToggle();
}
} }
void HistoryWidget::onUnblock() { void HistoryWidget::onUnblock() {
@ -3039,84 +3019,6 @@ void HistoryWidget::onBroadcastSilentChange() {
updateFieldPlaceholder(); updateFieldPlaceholder();
} }
void HistoryWidget::onShareContact(const PeerId &peer, UserData *contact) {
auto phone = contact->phone();
if (phone.isEmpty()) phone = App::phoneFromSharedContact(peerToUser(contact->id));
if (!contact || phone.isEmpty()) return;
Ui::showPeerHistory(peer, ShowAtTheEndMsgId);
if (!_history) return;
shareContact(peer, phone, contact->firstName, contact->lastName, replyToId(), peerToUser(contact->id));
}
void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, int32 userId) {
auto history = App::history(peer);
uint64 randomId = rand_value<uint64>();
FullMsgId newId(peerToChannel(peer), clientMsgId());
App::main()->readServerHistory(history);
fastShowAtEnd(history);
auto p = App::peer(peer);
auto flags = NewMessageFlags(p) | MTPDmessage::Flag::f_media; // unread, out
bool lastKeyboardUsed = lastForceReplyReplied(FullMsgId(peerToChannel(peer), replyTo));
auto sendFlags = MTPmessages_SendMedia::Flags(0);
if (replyTo) {
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
}
bool channelPost = p->isChannel() && !p->isMegagroup();
bool silentPost = channelPost && _silent->checked();
if (channelPost) {
flags |= MTPDmessage::Flag::f_views;
flags |= MTPDmessage::Flag::f_post;
}
if (!channelPost) {
flags |= MTPDmessage::Flag::f_from_id;
} else if (p->asChannel()->addsSignature()) {
flags |= MTPDmessage::Flag::f_post_author;
}
if (silentPost) {
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
}
auto messageFromId = channelPost ? 0 : Auth().userId();
auto messagePostAuthor = channelPost ? (Auth().user()->firstName + ' ' + Auth().user()->lastName) : QString();
history->addNewMessage(
MTP_message(
MTP_flags(flags),
MTP_int(newId.msg),
MTP_int(messageFromId),
peerToMTP(peer),
MTPnullFwdHeader,
MTPint(),
MTP_int(replyToId()),
MTP_int(unixtime()),
MTP_string(""),
MTP_messageMediaContact(
MTP_string(phone),
MTP_string(fname),
MTP_string(lname),
MTP_int(userId)),
MTPnullMarkup,
MTPnullEntities,
MTP_int(1),
MTPint(),
MTP_string(messagePostAuthor),
MTPlong()),
NewMessageUnread);
history->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), p->input, MTP_int(replyTo), MTP_inputMediaContact(MTP_string(phone), MTP_string(fname), MTP_string(lname)), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, history->sendRequestId);
App::historyRegRandom(randomId, newId);
App::main()->finishForwarding(history, _silent->checked());
cancelReplyAfterMediaSend(lastKeyboardUsed);
}
History *HistoryWidget::history() const { History *HistoryWidget::history() const {
return _history; return _history;
} }
@ -3278,22 +3180,22 @@ void HistoryWidget::dragEnterEvent(QDragEnterEvent *e) {
_attachDrag = getDragState(e->mimeData()); _attachDrag = getDragState(e->mimeData());
updateDragAreas(); updateDragAreas();
if (_attachDrag) { if (_attachDrag != DragState::None) {
e->setDropAction(Qt::IgnoreAction); e->setDropAction(Qt::IgnoreAction);
e->accept(); e->accept();
} }
} }
void HistoryWidget::dragLeaveEvent(QDragLeaveEvent *e) { void HistoryWidget::dragLeaveEvent(QDragLeaveEvent *e) {
if (_attachDrag != DragStateNone || !_attachDragPhoto->isHidden() || !_attachDragDocument->isHidden()) { if (_attachDrag != DragState::None || !_attachDragPhoto->isHidden() || !_attachDragDocument->isHidden()) {
_attachDrag = DragStateNone; _attachDrag = DragState::None;
updateDragAreas(); updateDragAreas();
} }
} }
void HistoryWidget::leaveEventHook(QEvent *e) { void HistoryWidget::leaveEventHook(QEvent *e) {
if (_attachDrag != DragStateNone || !_attachDragPhoto->isHidden() || !_attachDragDocument->isHidden()) { if (_attachDrag != DragState::None || !_attachDragPhoto->isHidden() || !_attachDragDocument->isHidden()) {
_attachDrag = DragStateNone; _attachDrag = DragState::None;
updateDragAreas(); updateDragAreas();
} }
if (hasMouseTracking()) mouseMoveEvent(0); if (hasMouseTracking()) mouseMoveEvent(0);
@ -3362,8 +3264,8 @@ void HistoryWidget::mouseReleaseEvent(QMouseEvent *e) {
_replyForwardPressed = false; _replyForwardPressed = false;
update(0, _field->y() - st::historySendPadding - st::historyReplyHeight, width(), st::historyReplyHeight); update(0, _field->y() - st::historySendPadding - st::historyReplyHeight, width(), st::historyReplyHeight);
} }
if (_attachDrag != DragStateNone || !_attachDragPhoto->isHidden() || !_attachDragDocument->isHidden()) { if (_attachDrag != DragState::None || !_attachDragPhoto->isHidden() || !_attachDragDocument->isHidden()) {
_attachDrag = DragStateNone; _attachDrag = DragState::None;
updateDragAreas(); updateDragAreas();
} }
if (_recording) { if (_recording) {
@ -3405,10 +3307,13 @@ void HistoryWidget::sendBotCommand(PeerData *peer, UserData *bot, const QString
toSend += '@' + username; toSend += '@' + username;
} }
MainWidget::MessageToSend message; auto message = MainWidget::MessageToSend(_history);
message.history = _history;
message.textWithTags = { toSend, TextWithTags::Tags() }; message.textWithTags = { toSend, TextWithTags::Tags() };
message.replyTo = replyTo ? ((!_peer->isUser()/* && (botStatus == 0 || botStatus == 2)*/) ? replyTo : -1) : 0; message.replyTo = replyTo
? ((!_peer->isUser()/* && (botStatus == 0 || botStatus == 2)*/)
? replyTo
: replyToId())
: 0;
message.silent = false; message.silent = false;
App::main()->sendMessage(message); App::main()->sendMessage(message);
if (replyTo) { if (replyTo) {
@ -3585,30 +3490,30 @@ DragState HistoryWidget::getDragState(const QMimeData *d) {
if (!d if (!d
|| d->hasFormat(qsl("application/x-td-forward-selected")) || d->hasFormat(qsl("application/x-td-forward-selected"))
|| d->hasFormat(qsl("application/x-td-forward-pressed")) || d->hasFormat(qsl("application/x-td-forward-pressed"))
|| d->hasFormat(qsl("application/x-td-forward-pressed-link"))) return DragStateNone; || d->hasFormat(qsl("application/x-td-forward-pressed-link"))) return DragState::None;
if (d->hasImage()) return DragStateImage; if (d->hasImage()) return DragState::Image;
QString uriListFormat(qsl("text/uri-list")); QString uriListFormat(qsl("text/uri-list"));
if (!d->hasFormat(uriListFormat)) return DragStateNone; if (!d->hasFormat(uriListFormat)) return DragState::None;
QStringList imgExtensions(cImgExtensions()), files; QStringList imgExtensions(cImgExtensions()), files;
const QList<QUrl> &urls(d->urls()); const QList<QUrl> &urls(d->urls());
if (urls.isEmpty()) return DragStateNone; if (urls.isEmpty()) return DragState::None;
bool allAreSmallImages = true; bool allAreSmallImages = true;
for (QList<QUrl>::const_iterator i = urls.cbegin(), en = urls.cend(); i != en; ++i) { for (QList<QUrl>::const_iterator i = urls.cbegin(), en = urls.cend(); i != en; ++i) {
if (!i->isLocalFile()) return DragStateNone; if (!i->isLocalFile()) return DragState::None;
auto file = Platform::File::UrlToLocal(*i); auto file = Platform::File::UrlToLocal(*i);
QFileInfo info(file); QFileInfo info(file);
if (info.isDir()) return DragStateNone; if (info.isDir()) return DragState::None;
quint64 s = info.size(); quint64 s = info.size();
if (s > App::kFileSizeLimit) { if (s > App::kFileSizeLimit) {
return DragStateNone; return DragState::None;
} }
if (allAreSmallImages) { if (allAreSmallImages) {
if (s > App::kImageSizeLimit) { if (s > App::kImageSizeLimit) {
@ -3627,30 +3532,30 @@ DragState HistoryWidget::getDragState(const QMimeData *d) {
} }
} }
} }
return allAreSmallImages ? DragStatePhotoFiles : DragStateFiles; return allAreSmallImages ? DragState::PhotoFiles : DragState::Files;
} }
void HistoryWidget::updateDragAreas() { void HistoryWidget::updateDragAreas() {
_field->setAcceptDrops(!_attachDrag); _field->setAcceptDrops(_attachDrag == DragState::None);
updateControlsGeometry(); updateControlsGeometry();
switch (_attachDrag) { switch (_attachDrag) {
case DragStateNone: case DragState::None:
_attachDragDocument->otherLeave(); _attachDragDocument->otherLeave();
_attachDragPhoto->otherLeave(); _attachDragPhoto->otherLeave();
break; break;
case DragStateFiles: case DragState::Files:
_attachDragDocument->setText(lang(lng_drag_files_here), lang(lng_drag_to_send_files)); _attachDragDocument->setText(lang(lng_drag_files_here), lang(lng_drag_to_send_files));
_attachDragDocument->otherEnter(); _attachDragDocument->otherEnter();
_attachDragPhoto->hideFast(); _attachDragPhoto->hideFast();
break; break;
case DragStatePhotoFiles: case DragState::PhotoFiles:
_attachDragDocument->setText(lang(lng_drag_images_here), lang(lng_drag_to_send_no_compression)); _attachDragDocument->setText(lang(lng_drag_images_here), lang(lng_drag_to_send_no_compression));
_attachDragPhoto->setText(lang(lng_drag_photos_here), lang(lng_drag_to_send_quick)); _attachDragPhoto->setText(lang(lng_drag_photos_here), lang(lng_drag_to_send_quick));
_attachDragDocument->otherEnter(); _attachDragDocument->otherEnter();
_attachDragPhoto->otherEnter(); _attachDragPhoto->otherEnter();
break; break;
case DragStateImage: case DragState::Image:
_attachDragPhoto->setText(lang(lng_drag_images_here), lang(lng_drag_to_send_quick)); _attachDragPhoto->setText(lang(lng_drag_images_here), lang(lng_drag_to_send_quick));
_attachDragDocument->hideFast(); _attachDragDocument->hideFast();
_attachDragPhoto->otherEnter(); _attachDragPhoto->otherEnter();
@ -3774,7 +3679,7 @@ bool HistoryWidget::kbWasHidden() const {
} }
void HistoryWidget::dropEvent(QDropEvent *e) { void HistoryWidget::dropEvent(QDropEvent *e) {
_attachDrag = DragStateNone; _attachDrag = DragState::None;
updateDragAreas(); updateDragAreas();
e->acceptProposedAction(); e->acceptProposedAction();
} }
@ -4123,11 +4028,18 @@ bool HistoryWidget::showSendFilesBox(object_ptr<SendFilesBox> box, const QString
box->setConfirmedCallback(base::lambda_guarded(this, [this, withComment, sendCallback = std::move(callback)](const QStringList &files, const QImage &image, std::unique_ptr<FileLoadTask::MediaInformation> information, bool compressed, const QString &caption, bool ctrlShiftEnter) { box->setConfirmedCallback(base::lambda_guarded(this, [this, withComment, sendCallback = std::move(callback)](const QStringList &files, const QImage &image, std::unique_ptr<FileLoadTask::MediaInformation> information, bool compressed, const QString &caption, bool ctrlShiftEnter) {
if (!canWriteMessage()) return; if (!canWriteMessage()) return;
auto replyTo = replyToId(); const auto replyTo = replyToId();
if (withComment) { if (withComment) {
onSend(ctrlShiftEnter, replyTo); // This call will clear replyToId().
onSend(ctrlShiftEnter);
} }
sendCallback(files, image, std::move(information), compressed, caption, replyTo); sendCallback(
files,
image,
std::move(information),
compressed,
caption,
replyTo);
})); }));
if (withComment) { if (withComment) {
@ -4244,7 +4156,9 @@ bool HistoryWidget::confirmShareContact(
bool compressed, bool compressed,
const QString &caption, const QString &caption,
MsgId replyTo) { MsgId replyTo) {
shareContact(_peer->id, phone, fname, lname, replyTo); auto options = ApiWrap::SendOptions(_history);
options.replyTo = replyTo;
Auth().api().shareContact(phone, fname, lname, options);
}; };
auto insertTextOnCancel = QString(); auto insertTextOnCancel = QString();
return showSendFilesBox( return showSendFilesBox(
@ -4311,13 +4225,18 @@ void HistoryWidget::uploadFiles(const QStringList &files, SendMediaType type) {
uploadFilesAfterConfirmation(files, QByteArray(), QImage(), nullptr, type, caption); uploadFilesAfterConfirmation(files, QByteArray(), QImage(), nullptr, type, caption);
} }
void HistoryWidget::uploadFilesAfterConfirmation(const QStringList &files, const QByteArray &content, const QImage &image, std::unique_ptr<FileLoadTask::MediaInformation> information, SendMediaType type, QString caption) { void HistoryWidget::uploadFilesAfterConfirmation(
const QStringList &files,
const QByteArray &content,
const QImage &image,
std::unique_ptr<FileLoadTask::MediaInformation> information,
SendMediaType type,
QString caption) {
Assert(canWriteMessage()); Assert(canWriteMessage());
auto to = FileLoadTo(_peer->id, _silent->checked(), replyToId()); auto to = FileLoadTo(_peer->id, _silent->checked(), replyToId());
if (files.size() > 1 && !caption.isEmpty()) { if (files.size() > 1 && !caption.isEmpty()) {
MainWidget::MessageToSend message; auto message = MainWidget::MessageToSend(_history);
message.history = _history;
message.textWithTags = { caption, TextWithTags::Tags() }; message.textWithTags = { caption, TextWithTags::Tags() };
message.replyTo = to.replyTo; message.replyTo = to.replyTo;
message.silent = to.silent; message.silent = to.silent;
@ -4335,8 +4254,6 @@ void HistoryWidget::uploadFilesAfterConfirmation(const QStringList &files, const
} }
} }
_fileLoader.addTasks(tasks); _fileLoader.addTasks(tasks);
cancelReplyAfterMediaSend(lastForceReplyReplied());
} }
void HistoryWidget::uploadFile(const QByteArray &fileContent, SendMediaType type) { void HistoryWidget::uploadFile(const QByteArray &fileContent, SendMediaType type) {
@ -4345,8 +4262,6 @@ void HistoryWidget::uploadFile(const QByteArray &fileContent, SendMediaType type
auto to = FileLoadTo(_peer->id, _silent->checked(), replyToId()); auto to = FileLoadTo(_peer->id, _silent->checked(), replyToId());
auto caption = QString(); auto caption = QString();
_fileLoader.addTask(MakeShared<FileLoadTask>(fileContent, QImage(), type, to, caption)); _fileLoader.addTask(MakeShared<FileLoadTask>(fileContent, QImage(), type, to, caption));
cancelReplyAfterMediaSend(lastForceReplyReplied());
} }
void HistoryWidget::sendFileConfirmed(const FileLoadResultPtr &file) { void HistoryWidget::sendFileConfirmed(const FileLoadResultPtr &file) {
@ -4364,13 +4279,19 @@ void HistoryWidget::sendFileConfirmed(const FileLoadResultPtr &file) {
Auth().uploader().upload(newId, file); Auth().uploader().upload(newId, file);
History *h = App::history(file->to.peer); const auto history = App::history(file->to.peer);
const auto peer = history->peer;
fastShowAtEnd(h); auto options = ApiWrap::SendOptions(history);
options.clearDraft = false;
options.replyTo = file->to.replyTo;
options.generateLocal = true;
options.silent = file->to.silent;
Auth().api().sendAction(options);
auto flags = NewMessageFlags(h->peer) | MTPDmessage::Flag::f_media; // unread, out auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
if (file->to.replyTo) flags |= MTPDmessage::Flag::f_reply_to_msg_id; if (file->to.replyTo) flags |= MTPDmessage::Flag::f_reply_to_msg_id;
bool channelPost = h->peer->isChannel() && !h->peer->isMegagroup(); bool channelPost = peer->isChannel() && !peer->isMegagroup();
bool silentPost = channelPost && file->to.silent; bool silentPost = channelPost && file->to.silent;
if (channelPost) { if (channelPost) {
flags |= MTPDmessage::Flag::f_views; flags |= MTPDmessage::Flag::f_views;
@ -4378,7 +4299,7 @@ void HistoryWidget::sendFileConfirmed(const FileLoadResultPtr &file) {
} }
if (!channelPost) { if (!channelPost) {
flags |= MTPDmessage::Flag::f_from_id; flags |= MTPDmessage::Flag::f_from_id;
} else if (h->peer->asChannel()->addsSignature()) { } else if (peer->asChannel()->addsSignature()) {
flags |= MTPDmessage::Flag::f_post_author; flags |= MTPDmessage::Flag::f_post_author;
} }
if (silentPost) { if (silentPost) {
@ -4396,7 +4317,7 @@ void HistoryWidget::sendFileConfirmed(const FileLoadResultPtr &file) {
file->photo, file->photo,
MTP_string(file->caption), MTP_string(file->caption),
MTPint()); MTPint());
h->addNewMessage( history->addNewMessage(
MTP_message( MTP_message(
MTP_flags(flags), MTP_flags(flags),
MTP_int(newId.msg), MTP_int(newId.msg),
@ -4425,7 +4346,7 @@ void HistoryWidget::sendFileConfirmed(const FileLoadResultPtr &file) {
file->document, file->document,
MTP_string(file->caption), MTP_string(file->caption),
MTPint()); MTPint());
h->addNewMessage( history->addNewMessage(
MTP_message( MTP_message(
MTP_flags(flags), MTP_flags(flags),
MTP_int(newId.msg), MTP_int(newId.msg),
@ -4445,7 +4366,7 @@ void HistoryWidget::sendFileConfirmed(const FileLoadResultPtr &file) {
MTPlong()), MTPlong()),
NewMessageUnread); NewMessageUnread);
} else if (file->type == SendMediaType::Audio) { } else if (file->type == SendMediaType::Audio) {
if (!h->peer->isChannel()) { if (!peer->isChannel()) {
flags |= MTPDmessage::Flag::f_media_unread; flags |= MTPDmessage::Flag::f_media_unread;
} }
auto documentFlags = MTPDmessageMediaDocument::Flag::f_document | 0; auto documentFlags = MTPDmessageMediaDocument::Flag::f_document | 0;
@ -4457,7 +4378,7 @@ void HistoryWidget::sendFileConfirmed(const FileLoadResultPtr &file) {
file->document, file->document,
MTP_string(file->caption), MTP_string(file->caption),
MTPint()); MTPint());
h->addNewMessage( history->addNewMessage(
MTP_message( MTP_message(
MTP_flags(flags), MTP_flags(flags),
MTP_int(newId.msg), MTP_int(newId.msg),
@ -4483,8 +4404,6 @@ void HistoryWidget::sendFileConfirmed(const FileLoadResultPtr &file) {
} }
App::main()->dialogsToUp(); App::main()->dialogsToUp();
peerMessagesUpdated(file->to.peer); peerMessagesUpdated(file->to.peer);
cancelReplyAfterMediaSend(lastKeyboardUsed);
} }
void HistoryWidget::onPhotoUploaded(const FullMsgId &newId, bool silent, const MTPInputFile &file) { void HistoryWidget::onPhotoUploaded(const FullMsgId &newId, bool silent, const MTPInputFile &file) {
@ -4833,17 +4752,17 @@ void HistoryWidget::updateControlsGeometry() {
} }
switch (_attachDrag) { switch (_attachDrag) {
case DragStateFiles: case DragState::Files:
_attachDragDocument->resize(width() - st::dragMargin.left() - st::dragMargin.right(), height() - st::dragMargin.top() - st::dragMargin.bottom()); _attachDragDocument->resize(width() - st::dragMargin.left() - st::dragMargin.right(), height() - st::dragMargin.top() - st::dragMargin.bottom());
_attachDragDocument->move(st::dragMargin.left(), st::dragMargin.top()); _attachDragDocument->move(st::dragMargin.left(), st::dragMargin.top());
break; break;
case DragStatePhotoFiles: case DragState::PhotoFiles:
_attachDragDocument->resize(width() - st::dragMargin.left() - st::dragMargin.right(), (height() - st::dragMargin.top() - st::dragMargin.bottom()) / 2); _attachDragDocument->resize(width() - st::dragMargin.left() - st::dragMargin.right(), (height() - st::dragMargin.top() - st::dragMargin.bottom()) / 2);
_attachDragDocument->move(st::dragMargin.left(), st::dragMargin.top()); _attachDragDocument->move(st::dragMargin.left(), st::dragMargin.top());
_attachDragPhoto->resize(_attachDragDocument->width(), _attachDragDocument->height()); _attachDragPhoto->resize(_attachDragDocument->width(), _attachDragDocument->height());
_attachDragPhoto->move(st::dragMargin.left(), height() - _attachDragPhoto->height() - st::dragMargin.bottom()); _attachDragPhoto->move(st::dragMargin.left(), height() - _attachDragPhoto->height() - st::dragMargin.bottom());
break; break;
case DragStateImage: case DragState::Image:
_attachDragPhoto->resize(width() - st::dragMargin.left() - st::dragMargin.right(), height() - st::dragMargin.top() - st::dragMargin.bottom()); _attachDragPhoto->resize(width() - st::dragMargin.left() - st::dragMargin.right(), height() - st::dragMargin.top() - st::dragMargin.bottom());
_attachDragPhoto->move(st::dragMargin.left(), st::dragMargin.top()); _attachDragPhoto->move(st::dragMargin.left(), st::dragMargin.top());
break; break;
@ -5365,23 +5284,24 @@ void HistoryWidget::onInlineResultSend(InlineBots::Result *result, UserData *bot
return; return;
} }
App::main()->readServerHistory(_history); auto options = ApiWrap::SendOptions(_history);
fastShowAtEnd(_history); options.clearDraft = true;
options.replyTo = replyToId();
options.generateLocal = true;
options.silent = _silent->checked();
Auth().api().sendAction(options);
uint64 randomId = rand_value<uint64>(); uint64 randomId = rand_value<uint64>();
FullMsgId newId(_channel, clientMsgId()); FullMsgId newId(_channel, clientMsgId());
bool lastKeyboardUsed = lastForceReplyReplied(); auto flags = NewMessageFlags(_peer) | MTPDmessage::Flag::f_media;
bool out = !_peer->isSelf(), unread = !_peer->isSelf();
auto flags = NewMessageFlags(_peer) | MTPDmessage::Flag::f_media; // unread, out
auto sendFlags = MTPmessages_SendInlineBotResult::Flag::f_clear_draft | 0; auto sendFlags = MTPmessages_SendInlineBotResult::Flag::f_clear_draft | 0;
if (replyToId()) { if (options.replyTo) {
flags |= MTPDmessage::Flag::f_reply_to_msg_id; flags |= MTPDmessage::Flag::f_reply_to_msg_id;
sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_reply_to_msg_id; sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_reply_to_msg_id;
} }
bool channelPost = _peer->isChannel() && !_peer->isMegagroup(); bool channelPost = _peer->isChannel() && !_peer->isMegagroup();
bool silentPost = channelPost && _silent->checked(); bool silentPost = channelPost && options.silent;
if (channelPost) { if (channelPost) {
flags |= MTPDmessage::Flag::f_views; flags |= MTPDmessage::Flag::f_views;
flags |= MTPDmessage::Flag::f_post; flags |= MTPDmessage::Flag::f_post;
@ -5408,7 +5328,6 @@ void HistoryWidget::onInlineResultSend(InlineBots::Result *result, UserData *bot
_history->sendRequestId = MTP::send(MTPmessages_SendInlineBotResult(MTP_flags(sendFlags), _peer->input, MTP_int(replyToId()), MTP_long(randomId), MTP_long(result->getQueryId()), MTP_string(result->getId())), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, _history->sendRequestId); _history->sendRequestId = MTP::send(MTPmessages_SendInlineBotResult(MTP_flags(sendFlags), _peer->input, MTP_int(replyToId()), MTP_long(randomId), MTP_long(result->getQueryId()), MTP_string(result->getId())), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, _history->sendRequestId);
App::main()->finishForwarding(_history, _silent->checked()); App::main()->finishForwarding(_history, _silent->checked());
cancelReply(lastKeyboardUsed);
App::historyRegRandom(randomId, newId); App::historyRegRandom(randomId, newId);
@ -5548,23 +5467,24 @@ bool HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &capti
return false; return false;
} }
App::main()->readServerHistory(_history); auto options = ApiWrap::SendOptions(_history);
fastShowAtEnd(_history); options.clearDraft = false;
options.replyTo = replyToId();
options.generateLocal = true;
options.silent = _silent->checked();
Auth().api().sendAction(options);
uint64 randomId = rand_value<uint64>(); uint64 randomId = rand_value<uint64>();
FullMsgId newId(_channel, clientMsgId()); FullMsgId newId(_channel, clientMsgId());
bool lastKeyboardUsed = lastForceReplyReplied(); auto flags = NewMessageFlags(_peer) | MTPDmessage::Flag::f_media;
bool out = !_peer->isSelf(), unread = !_peer->isSelf();
auto flags = NewMessageFlags(_peer) | MTPDmessage::Flag::f_media; // unread, out
auto sendFlags = MTPmessages_SendMedia::Flags(0); auto sendFlags = MTPmessages_SendMedia::Flags(0);
if (replyToId()) { if (replyToId()) {
flags |= MTPDmessage::Flag::f_reply_to_msg_id; flags |= MTPDmessage::Flag::f_reply_to_msg_id;
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id; sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
} }
bool channelPost = _peer->isChannel() && !_peer->isMegagroup(); bool channelPost = _peer->isChannel() && !_peer->isMegagroup();
bool silentPost = channelPost && _silent->checked(); bool silentPost = channelPost && options.silent;
if (channelPost) { if (channelPost) {
flags |= MTPDmessage::Flag::f_views; flags |= MTPDmessage::Flag::f_views;
flags |= MTPDmessage::Flag::f_post; flags |= MTPDmessage::Flag::f_post;
@ -5583,7 +5503,6 @@ bool HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &capti
_history->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), _peer->input, MTP_int(replyToId()), MTP_inputMediaDocument(MTP_flags(0), mtpInput, MTP_string(caption), MTPint()), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, _history->sendRequestId); _history->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), _peer->input, MTP_int(replyToId()), MTP_inputMediaDocument(MTP_flags(0), mtpInput, MTP_string(caption), MTPint()), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, _history->sendRequestId);
App::main()->finishForwarding(_history, _silent->checked()); App::main()->finishForwarding(_history, _silent->checked());
cancelReplyAfterMediaSend(lastKeyboardUsed);
if (doc->sticker()) App::main()->incrementSticker(doc); if (doc->sticker()) App::main()->incrementSticker(doc);
@ -5608,16 +5527,17 @@ void HistoryWidget::sendExistingPhoto(PhotoData *photo, const QString &caption)
return; return;
} }
App::main()->readServerHistory(_history); auto options = ApiWrap::SendOptions(_history);
fastShowAtEnd(_history); options.clearDraft = false;
options.replyTo = replyToId();
options.generateLocal = true;
options.silent = _silent->checked();
Auth().api().sendAction(options);
uint64 randomId = rand_value<uint64>(); uint64 randomId = rand_value<uint64>();
FullMsgId newId(_channel, clientMsgId()); FullMsgId newId(_channel, clientMsgId());
bool lastKeyboardUsed = lastForceReplyReplied(); auto flags = NewMessageFlags(_peer) | MTPDmessage::Flag::f_media;
bool out = !_peer->isSelf(), unread = !_peer->isSelf();
auto flags = NewMessageFlags(_peer) | MTPDmessage::Flag::f_media; // unread, out
auto sendFlags = MTPmessages_SendMedia::Flags(0); auto sendFlags = MTPmessages_SendMedia::Flags(0);
if (replyToId()) { if (replyToId()) {
flags |= MTPDmessage::Flag::f_reply_to_msg_id; flags |= MTPDmessage::Flag::f_reply_to_msg_id;
@ -5643,7 +5563,6 @@ void HistoryWidget::sendExistingPhoto(PhotoData *photo, const QString &caption)
_history->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), _peer->input, MTP_int(replyToId()), MTP_inputMediaPhoto(MTP_flags(0), MTP_inputPhoto(MTP_long(photo->id), MTP_long(photo->access)), MTP_string(caption), MTPint()), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, _history->sendRequestId); _history->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), _peer->input, MTP_int(replyToId()), MTP_inputMediaPhoto(MTP_flags(0), MTP_inputPhoto(MTP_long(photo->id), MTP_long(photo->access)), MTP_string(caption), MTPint()), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, _history->sendRequestId);
App::main()->finishForwarding(_history, _silent->checked()); App::main()->finishForwarding(_history, _silent->checked());
cancelReplyAfterMediaSend(lastKeyboardUsed);
App::historyRegRandom(randomId, newId); App::historyRegRandom(randomId, newId);
@ -5843,10 +5762,18 @@ void HistoryWidget::onCopyPostLink() {
} }
bool HistoryWidget::lastForceReplyReplied(const FullMsgId &replyTo) const { bool HistoryWidget::lastForceReplyReplied(const FullMsgId &replyTo) const {
if (replyTo.msg > 0 && replyTo.channel != _channel) return false; if (replyTo.channel != _channel) {
return false;
}
return _keyboard->forceReply() return _keyboard->forceReply()
&& _keyboard->forMsgId() == FullMsgId(_channel, _history->lastKeyboardId) && _keyboard->forMsgId() == FullMsgId(_channel, _history->lastKeyboardId)
&& _keyboard->forMsgId().msg == (replyTo.msg < 0 ? replyToId() : replyTo.msg); && _keyboard->forMsgId().msg == replyTo.msg;
}
bool HistoryWidget::lastForceReplyReplied() const {
return _keyboard->forceReply()
&& _keyboard->forMsgId() == FullMsgId(_channel, _history->lastKeyboardId)
&& _keyboard->forMsgId().msg == replyToId();
} }
bool HistoryWidget::cancelReply(bool lastKeyboardUsed) { bool HistoryWidget::cancelReply(bool lastKeyboardUsed) {
@ -5880,7 +5807,10 @@ bool HistoryWidget::cancelReply(bool lastKeyboardUsed) {
_saveDraftStart = getms(); _saveDraftStart = getms();
onDraftSave(); onDraftSave();
} }
if (!_editMsgId && _keyboard->singleUse() && _keyboard->forceReply() && lastKeyboardUsed) { if (!_editMsgId
&& _keyboard->singleUse()
&& _keyboard->forceReply()
&& lastKeyboardUsed) {
if (_kbReplyTo) { if (_kbReplyTo) {
onKbToggle(false); onKbToggle(false);
} }

View File

@ -22,7 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "storage/localimageloader.h" #include "storage/localimageloader.h"
#include "ui/widgets/tooltip.h" #include "ui/widgets/tooltip.h"
#include "history/history_common.h" #include "mainwidget.h"
#include "chat_helpers/field_autocomplete.h" #include "chat_helpers/field_autocomplete.h"
#include "window/section_widget.h" #include "window/section_widget.h"
#include "core/single_timer.h" #include "core/single_timer.h"
@ -103,7 +103,6 @@ class HistoryHider : public Ui::RpWidget, private base::Subscriber {
public: public:
HistoryHider(MainWidget *parent, MessageIdsList &&items); // forward messages HistoryHider(MainWidget *parent, MessageIdsList &&items); // forward messages
HistoryHider(MainWidget *parent, UserData *sharedContact); // share contact
HistoryHider(MainWidget *parent); // send path from command line argument HistoryHider(MainWidget *parent); // send path from command line argument
HistoryHider(MainWidget *parent, const QString &url, const QString &text); // share url HistoryHider(MainWidget *parent, const QString &url, const QString &text); // share url
HistoryHider(MainWidget *parent, const QString &botAndQuery); // inline switch button handler HistoryHider(MainWidget *parent, const QString &botAndQuery); // inline switch button handler
@ -142,7 +141,6 @@ private:
void init(); void init();
MainWidget *parent(); MainWidget *parent();
UserData *_sharedContact = nullptr;
MessageIdsList _forwardItems; MessageIdsList _forwardItems;
bool _sendPath = false; bool _sendPath = false;
@ -200,7 +198,6 @@ public:
void newUnreadMsg(History *history, HistoryItem *item); void newUnreadMsg(History *history, HistoryItem *item);
void historyToDown(History *history); void historyToDown(History *history);
void historyWasRead(ReadServerHistoryChecks checks);
void unreadCountChanged(History *history); void unreadCountChanged(History *history);
QRect historyRect() const; QRect historyRect() const;
@ -234,10 +231,6 @@ public:
void updateControlsVisibility(); void updateControlsVisibility();
void updateControlsGeometry(); void updateControlsGeometry();
void onShareContact(const PeerId &peer, UserData *contact);
void shareContact(const PeerId &peer, const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, int32 userId = 0);
History *history() const; History *history() const;
PeerData *peer() const; PeerData *peer() const;
void setMsgId(MsgId showAtMsgId); void setMsgId(MsgId showAtMsgId);
@ -268,7 +261,8 @@ public:
MsgId replyToId() const; MsgId replyToId() const;
void messageDataReceived(ChannelData *channel, MsgId msgId); void messageDataReceived(ChannelData *channel, MsgId msgId);
bool lastForceReplyReplied(const FullMsgId &replyTo = FullMsgId(NoChannel, -1)) const; bool lastForceReplyReplied(const FullMsgId &replyTo) const;
bool lastForceReplyReplied() const;
bool cancelReply(bool lastKeyboardUsed = false); bool cancelReply(bool lastKeyboardUsed = false);
void cancelEdit(); void cancelEdit();
void updateForwarding(); void updateForwarding();
@ -301,7 +295,7 @@ public:
DragState getDragState(const QMimeData *d); DragState getDragState(const QMimeData *d);
void fastShowAtEnd(History *h); void fastShowAtEnd(not_null<History*> history);
void applyDraft(bool parseLinks = true, Ui::FlatTextarea::UndoHistoryAction undoHistoryAction = Ui::FlatTextarea::ClearUndoHistory); void applyDraft(bool parseLinks = true, Ui::FlatTextarea::UndoHistoryAction undoHistoryAction = Ui::FlatTextarea::ClearUndoHistory);
void showHistory(const PeerId &peer, MsgId showAtMsgId, bool reload = false); void showHistory(const PeerId &peer, MsgId showAtMsgId, bool reload = false);
void clearDelayedShowAt(); void clearDelayedShowAt();
@ -405,7 +399,6 @@ public slots:
void onReportSpamClear(); void onReportSpamClear();
void onScroll(); void onScroll();
void onSend(bool ctrlShiftEnter = false, MsgId replyTo = -1);
void onUnblock(); void onUnblock();
void onBotStart(); void onBotStart();
@ -452,6 +445,8 @@ public slots:
void preloadHistoryIfNeeded(); void preloadHistoryIfNeeded();
private slots: private slots:
void onSend(bool ctrlShiftEnter = false);
void onHashtagOrBotCommandInsert(QString str, FieldAutocomplete::ChooseMethod method); void onHashtagOrBotCommandInsert(QString str, FieldAutocomplete::ChooseMethod method);
void onMentionInsert(UserData *user); void onMentionInsert(UserData *user);
void onInlineBotCancel(); void onInlineBotCancel();
@ -825,7 +820,7 @@ private:
object_ptr<InlineBots::Layout::Widget> _inlineResults = { nullptr }; object_ptr<InlineBots::Layout::Widget> _inlineResults = { nullptr };
object_ptr<TabbedPanel> _tabbedPanel; object_ptr<TabbedPanel> _tabbedPanel;
QPointer<TabbedSelector> _tabbedSelector; QPointer<TabbedSelector> _tabbedSelector;
DragState _attachDrag = DragStateNone; DragState _attachDrag = DragState::None;
object_ptr<DragArea> _attachDragDocument, _attachDragPhoto; object_ptr<DragArea> _attachDragDocument, _attachDragPhoto;
object_ptr<Ui::Emoji::SuggestionsController> _emojiSuggestions = { nullptr }; object_ptr<Ui::Emoji::SuggestionsController> _emojiSuggestions = { nullptr };

View File

@ -790,10 +790,6 @@ void MainWidget::onUpdateMuted() {
App::updateMuted(); App::updateMuted();
} }
void MainWidget::onShareContact(const PeerId &peerId, UserData *contact) {
_history->onShareContact(peerId, contact);
}
bool MainWidget::onSendPaths(const PeerId &peerId) { bool MainWidget::onSendPaths(const PeerId &peerId) {
Expects(peerId != 0); Expects(peerId != 0);
auto peer = App::peer(peerId); auto peer = App::peer(peerId);
@ -1016,10 +1012,6 @@ void MainWidget::deletePhotoLayer(PhotoData *photo) {
}))); })));
} }
void MainWidget::shareContactLayer(UserData *contact) {
hiderLayer(object_ptr<HistoryHider>(this, contact));
}
void MainWidget::shareUrlLayer(const QString &url, const QString &text) { void MainWidget::shareUrlLayer(const QString &url, const QString &text) {
// Don't allow to insert an inline bot query by share url link. // Don't allow to insert an inline bot query by share url link.
if (url.trimmed().startsWith('@')) { if (url.trimmed().startsWith('@')) {
@ -1100,15 +1092,26 @@ void MainWidget::deleteHistoryPart(DeleteHistoryRequest request, const MTPmessag
MTP::send(MTPmessages_DeleteHistory(MTP_flags(flags), peer->input, MTP_int(0)), rpcDone(&MainWidget::deleteHistoryPart, request)); MTP::send(MTPmessages_DeleteHistory(MTP_flags(flags), peer->input, MTP_int(0)), rpcDone(&MainWidget::deleteHistoryPart, request));
} }
void MainWidget::deleteMessages(PeerData *peer, const QVector<MTPint> &ids, bool forEveryone) { void MainWidget::deleteMessages(
if (peer->isChannel()) { not_null<PeerData*> peer,
MTP::send(MTPchannels_DeleteMessages(peer->asChannel()->inputChannel, MTP_vector<MTPint>(ids)), rpcDone(&MainWidget::messagesAffected, peer)); const QVector<MTPint> &ids,
bool forEveryone) {
if (const auto channel = peer->asChannel()) {
MTP::send(
MTPchannels_DeleteMessages(
channel->inputChannel,
MTP_vector<MTPint>(ids)),
rpcDone(&MainWidget::messagesAffected, peer));
} else { } else {
auto flags = MTPmessages_DeleteMessages::Flags(0); auto flags = MTPmessages_DeleteMessages::Flags(0);
if (forEveryone) { if (forEveryone) {
flags |= MTPmessages_DeleteMessages::Flag::f_revoke; flags |= MTPmessages_DeleteMessages::Flag::f_revoke;
} }
MTP::send(MTPmessages_DeleteMessages(MTP_flags(flags), MTP_vector<MTPint>(ids)), rpcDone(&MainWidget::messagesAffected, peer)); MTP::send(
MTPmessages_DeleteMessages(
MTP_flags(flags),
MTP_vector<MTPint>(ids)),
rpcDone(&MainWidget::messagesAffected, peer));
} }
} }
@ -1458,13 +1461,18 @@ Dialogs::IndexedList *MainWidget::contactsNoDialogsList() {
} }
void MainWidget::sendMessage(const MessageToSend &message) { void MainWidget::sendMessage(const MessageToSend &message) {
auto history = message.history; const auto history = message.history;
auto &textWithTags = message.textWithTags; auto &textWithTags = message.textWithTags;
readServerHistory(history); auto options = ApiWrap::SendOptions(message.history);
_history->fastShowAtEnd(history); options.clearDraft = message.clearDraft;
options.replyTo = message.replyTo;
options.generateLocal = true;
options.silent = message.silent;
options.webPageId = message.webPageId;
Auth().api().sendAction(options);
if (!history || !history->peer->canWrite()) { if (!history->peer->canWrite()) {
return; return;
} }
saveRecentHashtags(textWithTags.text); saveRecentHashtags(textWithTags.text);
@ -1476,7 +1484,6 @@ void MainWidget::sendMessage(const MessageToSend &message) {
HistoryItem *lastMessage = nullptr; HistoryItem *lastMessage = nullptr;
auto replyTo = (message.replyTo < 0) ? _history->replyToId() : message.replyTo;
while (TextUtilities::CutPart(sending, left, MaxMessageSize)) { while (TextUtilities::CutPart(sending, left, MaxMessageSize)) {
auto newId = FullMsgId(peerToChannel(history->peer->id), clientMsgId()); auto newId = FullMsgId(peerToChannel(history->peer->id), clientMsgId());
auto randomId = rand_value<uint64>(); auto randomId = rand_value<uint64>();
@ -1489,7 +1496,7 @@ void MainWidget::sendMessage(const MessageToSend &message) {
MTPstring msgText(MTP_string(sending.text)); MTPstring msgText(MTP_string(sending.text));
auto flags = NewMessageFlags(history->peer) | MTPDmessage::Flag::f_entities; // unread, out auto flags = NewMessageFlags(history->peer) | MTPDmessage::Flag::f_entities; // unread, out
auto sendFlags = MTPmessages_SendMessage::Flags(0); auto sendFlags = MTPmessages_SendMessage::Flags(0);
if (replyTo) { if (message.replyTo) {
flags |= MTPDmessage::Flag::f_reply_to_msg_id; flags |= MTPDmessage::Flag::f_reply_to_msg_id;
sendFlags |= MTPmessages_SendMessage::Flag::f_reply_to_msg_id; sendFlags |= MTPmessages_SendMessage::Flag::f_reply_to_msg_id;
} }
@ -1534,7 +1541,7 @@ void MainWidget::sendMessage(const MessageToSend &message) {
peerToMTP(history->peer->id), peerToMTP(history->peer->id),
MTPnullFwdHeader, MTPnullFwdHeader,
MTPint(), MTPint(),
MTP_int(replyTo), MTP_int(message.replyTo),
MTP_int(unixtime()), MTP_int(unixtime()),
msgText, msgText,
media, media,
@ -1545,7 +1552,20 @@ void MainWidget::sendMessage(const MessageToSend &message) {
MTP_string(messagePostAuthor), MTP_string(messagePostAuthor),
MTPlong()), MTPlong()),
NewMessageUnread); NewMessageUnread);
history->sendRequestId = MTP::send(MTPmessages_SendMessage(MTP_flags(sendFlags), history->peer->input, MTP_int(replyTo), msgText, MTP_long(randomId), MTPnullMarkup, sentEntities), rpcDone(&MainWidget::sentUpdatesReceived, randomId), rpcFail(&MainWidget::sendMessageFail), 0, 0, history->sendRequestId); history->sendRequestId = MTP::send(
MTPmessages_SendMessage(
MTP_flags(sendFlags),
history->peer->input,
MTP_int(message.replyTo),
msgText,
MTP_long(randomId),
MTPnullMarkup,
sentEntities),
rpcDone(&MainWidget::sentUpdatesReceived, randomId),
rpcFail(&MainWidget::sendMessageFail),
0,
0,
history->sendRequestId);
} }
history->lastSentMsg = lastMessage; history->lastSentMsg = lastMessage;
@ -1581,30 +1601,6 @@ void MainWidget::saveRecentHashtags(const QString &text) {
} }
} }
void MainWidget::readServerHistory(History *history, ReadServerHistoryChecks checks) {
if (!history) return;
if (checks == ReadServerHistoryChecks::OnlyIfUnread && !history->unreadCount()) return;
auto peer = history->peer;
MsgId upTo = history->inboxRead(0);
if (auto channel = peer->asChannel()) {
if (!channel->amIn()) {
return; // no read request for channels that I didn't koin
}
}
if (_readRequests.contains(peer)) {
auto i = _readRequestsPending.find(peer);
if (i == _readRequestsPending.cend()) {
_readRequestsPending.insert(peer, upTo);
} else if (i.value() < upTo) {
i.value() = upTo;
}
} else {
sendReadRequest(peer, upTo);
}
}
void MainWidget::unreadCountChanged(History *history) { void MainWidget::unreadCountChanged(History *history) {
_history->unreadCountChanged(history); _history->unreadCountChanged(history);
} }
@ -1761,45 +1757,14 @@ void MainWidget::overviewLoaded(
Notify::mediaOverviewUpdated(history->peer, type); Notify::mediaOverviewUpdated(history->peer, type);
} }
void MainWidget::sendReadRequest(PeerData *peer, MsgId upTo) { void MainWidget::messagesAffected(
if (peer->isChannel()) { not_null<PeerData*> peer,
_readRequests.insert(peer, qMakePair(MTP::send(MTPchannels_ReadHistory(peer->asChannel()->inputChannel, MTP_int(upTo)), rpcDone(&MainWidget::channelReadDone, peer), rpcFail(&MainWidget::readRequestFail, peer)), upTo)); const MTPmessages_AffectedMessages &result) {
const auto &data = result.c_messages_affectedMessages();
if (const auto channel = peer->asChannel()) {
channel->ptsUpdateAndApply(data.vpts.v, data.vpts_count.v);
} else { } else {
_readRequests.insert(peer, qMakePair(MTP::send(MTPmessages_ReadHistory(peer->input, MTP_int(upTo)), rpcDone(&MainWidget::historyReadDone, peer), rpcFail(&MainWidget::readRequestFail, peer)), upTo)); ptsUpdateAndApply(data.vpts.v, data.vpts_count.v);
}
}
void MainWidget::channelReadDone(PeerData *peer, const MTPBool &result) {
readRequestDone(peer);
}
void MainWidget::historyReadDone(PeerData *peer, const MTPmessages_AffectedMessages &result) {
messagesAffected(peer, result);
readRequestDone(peer);
}
bool MainWidget::readRequestFail(PeerData *peer, const RPCError &error) {
if (MTP::isDefaultHandledError(error)) return false;
readRequestDone(peer);
return false;
}
void MainWidget::readRequestDone(PeerData *peer) {
_readRequests.remove(peer);
ReadRequestsPending::iterator i = _readRequestsPending.find(peer);
if (i != _readRequestsPending.cend()) {
sendReadRequest(peer, i.value());
_readRequestsPending.erase(i);
}
}
void MainWidget::messagesAffected(PeerData *peer, const MTPmessages_AffectedMessages &result) {
auto &d = result.c_messages_affectedMessages();
if (peer && peer->isChannel()) {
peer->asChannel()->ptsUpdateAndApply(d.vpts.v, d.vpts_count.v);
} else {
ptsUpdateAndApply(d.vpts.v, d.vpts_count.v);
} }
if (auto h = App::historyLoaded(peer ? peer->id : 0)) { if (auto h = App::historyLoaded(peer ? peer->id : 0)) {
@ -1809,6 +1774,12 @@ void MainWidget::messagesAffected(PeerData *peer, const MTPmessages_AffectedMess
} }
} }
void MainWidget::messagesContentsRead(
const MTPmessages_AffectedMessages &result) {
const auto &data = result.c_messages_affectedMessages();
ptsUpdateAndApply(data.vpts.v, data.vpts_count.v);
}
void MainWidget::handleAudioUpdate(const AudioMsgId &audioId) { void MainWidget::handleAudioUpdate(const AudioMsgId &audioId) {
using State = Media::Player::State; using State = Media::Player::State;
auto state = Media::Player::mixer()->currentState(audioId.type()); auto state = Media::Player::mixer()->currentState(audioId.type());
@ -2063,31 +2034,37 @@ void MainWidget::mediaMarkRead(not_null<DocumentData*> data) {
auto &items = App::documentItems(); auto &items = App::documentItems();
auto i = items.constFind(data); auto i = items.constFind(data);
if (i != items.cend()) { if (i != items.cend()) {
mediaMarkRead(i.value()); mediaMarkRead({ i.value().begin(), i.value().end() });
} }
} }
void MainWidget::mediaMarkRead(const HistoryItemsMap &items) { void MainWidget::mediaMarkRead(
const base::flat_set<not_null<HistoryItem*>> &items) {
QVector<MTPint> markedIds; QVector<MTPint> markedIds;
QMap<ChannelData*, QVector<MTPint>> channelMarkedIds; base::flat_map<not_null<ChannelData*>, QVector<MTPint>> channelMarkedIds;
markedIds.reserve(items.size()); markedIds.reserve(items.size());
for_const (auto item, items) { for (const auto item : items) {
if ((!item->out() || item->mentionsMe()) && item->isMediaUnread()) { if (!item->isMediaUnread() || (item->out() && !item->mentionsMe())) {
item->markMediaRead(); continue;
if (item->id > 0) { }
if (auto channel = item->history()->peer->asChannel()) { item->markMediaRead();
channelMarkedIds[channel].push_back(MTP_int(item->id)); if (item->id > 0) {
} else { if (const auto channel = item->history()->peer->asChannel()) {
markedIds.push_back(MTP_int(item->id)); channelMarkedIds[channel].push_back(MTP_int(item->id));
} } else {
markedIds.push_back(MTP_int(item->id));
} }
} }
} }
if (!markedIds.isEmpty()) { if (!markedIds.isEmpty()) {
MTP::send(MTPmessages_ReadMessageContents(MTP_vector<MTPint>(markedIds)), rpcDone(&MainWidget::messagesAffected, (PeerData*)0)); MTP::send(
MTPmessages_ReadMessageContents(MTP_vector<MTPint>(markedIds)),
rpcDone(&MainWidget::messagesContentsRead));
} }
for (auto i = channelMarkedIds.cbegin(), e = channelMarkedIds.cend(); i != e; ++i) { for (const auto &channelIds : channelMarkedIds) {
MTP::send(MTPchannels_ReadMessageContents(i.key()->inputChannel, MTP_vector<MTPint>(i.value()))); MTP::send(MTPchannels_ReadMessageContents(
channelIds.first->inputChannel,
MTP_vector<MTPint>(channelIds.second)));
} }
} }
@ -2095,10 +2072,16 @@ void MainWidget::mediaMarkRead(not_null<HistoryItem*> item) {
if ((!item->out() || item->mentionsMe()) && item->isMediaUnread()) { if ((!item->out() || item->mentionsMe()) && item->isMediaUnread()) {
item->markMediaRead(); item->markMediaRead();
if (item->id > 0) { if (item->id > 0) {
if (auto channel = item->history()->peer->asChannel()) { const auto ids = MTP_vector<MTPint>(1, MTP_int(item->id));
MTP::send(MTPchannels_ReadMessageContents(channel->inputChannel, MTP_vector<MTPint>(1, MTP_int(item->id)))); if (const auto channel = item->history()->peer->asChannel()) {
MTP::send(
MTPchannels_ReadMessageContents(
channel->inputChannel,
ids));
} else { } else {
MTP::send(MTPmessages_ReadMessageContents(MTP_vector<MTPint>(1, MTP_int(item->id))), rpcDone(&MainWidget::messagesAffected, (PeerData*)0)); MTP::send(
MTPmessages_ReadMessageContents(ids),
rpcDone(&MainWidget::messagesContentsRead));
} }
} }
} }
@ -3115,7 +3098,9 @@ void MainWidget::newUnreadMsg(History *history, HistoryItem *item) {
} }
void MainWidget::markActiveHistoryAsRead() { void MainWidget::markActiveHistoryAsRead() {
_history->historyWasRead(ReadServerHistoryChecks::OnlyIfUnread); if (const auto activeHistory = _history->history()) {
Auth().api().readServerHistory(activeHistory);
}
} }
void MainWidget::showAnimated(const QPixmap &bgAnimCache, bool back) { void MainWidget::showAnimated(const QPixmap &bgAnimCache, bool back) {

View File

@ -21,7 +21,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#pragma once #pragma once
#include "storage/localimageloader.h" #include "storage/localimageloader.h"
#include "history/history_common.h"
#include "core/single_timer.h" #include "core/single_timer.h"
#include "base/weak_ptr.h" #include "base/weak_ptr.h"
#include "ui/rp_widget.h" #include "ui/rp_widget.h"
@ -81,6 +80,13 @@ class ItemBase;
} // namespace Layout } // namespace Layout
} // namespace InlineBots } // namespace InlineBots
enum class DragState {
None = 0x00,
Files = 0x01,
PhotoFiles = 0x02,
Image = 0x03,
};
class MainWidget : public Ui::RpWidget, public RPCSender, private base::Subscriber { class MainWidget : public Ui::RpWidget, public RPCSender, private base::Subscriber {
Q_OBJECT Q_OBJECT
@ -182,7 +188,6 @@ public:
void showSendPathsLayer(); void showSendPathsLayer();
void deleteLayer(int selectedCount = 0); // 0 - context item void deleteLayer(int selectedCount = 0); // 0 - context item
void cancelUploadLayer(); void cancelUploadLayer();
void shareContactLayer(UserData *contact);
void shareUrlLayer(const QString &url, const QString &text); void shareUrlLayer(const QString &url, const QString &text);
void inlineSwitchLayer(const QString &botAndQuery); void inlineSwitchLayer(const QString &botAndQuery);
void hiderLayer(object_ptr<HistoryHider> h); void hiderLayer(object_ptr<HistoryHider> h);
@ -194,7 +199,6 @@ public:
const QString &url, const QString &url,
const QString &text); const QString &text);
bool onInlineSwitchChosen(const PeerId &peer, const QString &botAndQuery); bool onInlineSwitchChosen(const PeerId &peer, const QString &botAndQuery);
void onShareContact(const PeerId &peer, UserData *contact);
bool onSendPaths(const PeerId &peer); bool onSendPaths(const PeerId &peer);
void onFilesOrForwardDrop(const PeerId &peer, const QMimeData *data); void onFilesOrForwardDrop(const PeerId &peer, const QMimeData *data);
bool selectingPeer(bool withConfirm = false) const; bool selectingPeer(bool withConfirm = false) const;
@ -208,7 +212,10 @@ public:
bool leaveChatFailed(PeerData *peer, const RPCError &e); bool leaveChatFailed(PeerData *peer, const RPCError &e);
void deleteHistoryAfterLeave(PeerData *peer, const MTPUpdates &updates); void deleteHistoryAfterLeave(PeerData *peer, const MTPUpdates &updates);
void deleteMessages(PeerData *peer, const QVector<MTPint> &ids, bool forEveryone); void deleteMessages(
not_null<PeerData*> peer,
const QVector<MTPint> &ids,
bool forEveryone);
void deletedContact(UserData *user, const MTPcontacts_Link &result); void deletedContact(UserData *user, const MTPcontacts_Link &result);
void deleteConversation(PeerData *peer, bool deleteHistory = true); void deleteConversation(PeerData *peer, bool deleteHistory = true);
void deleteAndExit(ChatData *chat); void deleteAndExit(ChatData *chat);
@ -243,7 +250,10 @@ public:
Dialogs::IndexedList *contactsNoDialogsList(); Dialogs::IndexedList *contactsNoDialogsList();
struct MessageToSend { struct MessageToSend {
History *history = nullptr; MessageToSend(not_null<History*> history) : history(history) {
}
not_null<History*> history;
TextWithTags textWithTags; TextWithTags textWithTags;
MsgId replyTo = 0; MsgId replyTo = 0;
bool silent = false; bool silent = false;
@ -253,7 +263,6 @@ public:
void sendMessage(const MessageToSend &message); void sendMessage(const MessageToSend &message);
void saveRecentHashtags(const QString &text); void saveRecentHashtags(const QString &text);
void readServerHistory(History *history, ReadServerHistoryChecks checks = ReadServerHistoryChecks::OnlyIfUnread);
void unreadCountChanged(History *history); void unreadCountChanged(History *history);
TimeMs highlightStartTime(not_null<const HistoryItem*> item) const; TimeMs highlightStartTime(not_null<const HistoryItem*> item) const;
@ -295,7 +304,7 @@ public:
void finishForwarding(History *history, bool silent); // send them void finishForwarding(History *history, bool silent); // send them
void mediaMarkRead(not_null<DocumentData*> data); void mediaMarkRead(not_null<DocumentData*> data);
void mediaMarkRead(const HistoryItemsMap &items); void mediaMarkRead(const base::flat_set<not_null<HistoryItem*>> &items);
void mediaMarkRead(not_null<HistoryItem*> item); void mediaMarkRead(not_null<HistoryItem*> item);
void webPageUpdated(WebPageData *page); void webPageUpdated(WebPageData *page);
@ -458,13 +467,10 @@ private:
void destroyCallTopBar(); void destroyCallTopBar();
void callTopBarHeightUpdated(int callTopBarHeight); void callTopBarHeightUpdated(int callTopBarHeight);
void sendReadRequest(PeerData *peer, MsgId upTo); void messagesAffected(
void channelReadDone(PeerData *peer, const MTPBool &result); not_null<PeerData*> peer,
void historyReadDone(PeerData *peer, const MTPmessages_AffectedMessages &result); const MTPmessages_AffectedMessages &result);
bool readRequestFail(PeerData *peer, const RPCError &error); void messagesContentsRead(const MTPmessages_AffectedMessages &result);
void readRequestDone(PeerData *peer);
void messagesAffected(PeerData *peer, const MTPmessages_AffectedMessages &result);
void overviewLoaded( void overviewLoaded(
std::pair<not_null<History*>, MsgId> historyAndStartMsgId, std::pair<not_null<History*>, MsgId> historyAndStartMsgId,
const MTPmessages_Messages &result, const MTPmessages_Messages &result,
@ -640,11 +646,6 @@ private:
base::flat_set<not_null<PeerData*>> updateNotifySettingPeers; base::flat_set<not_null<PeerData*>> updateNotifySettingPeers;
SingleTimer updateNotifySettingTimer; SingleTimer updateNotifySettingTimer;
typedef QMap<PeerData*, QPair<mtpRequestId, MsgId> > ReadRequests;
ReadRequests _readRequests;
typedef QMap<PeerData*, MsgId> ReadRequestsPending;
ReadRequestsPending _readRequestsPending;
typedef QMap<PeerData*, mtpRequestId> OverviewsPreload; typedef QMap<PeerData*, mtpRequestId> OverviewsPreload;
OverviewsPreload _overviewPreload[OverviewCount], _overviewLoad[OverviewCount]; OverviewsPreload _overviewPreload[OverviewCount], _overviewLoad[OverviewCount];

View File

@ -218,7 +218,7 @@ public:
setToDC(dcId); setToDC(dcId);
return *this; return *this;
} }
[[nodiscard]] SpecificRequestBuilder &canWait(TimeMs ms) noexcept { [[nodiscard]] SpecificRequestBuilder &afterDelay(TimeMs ms) noexcept {
setCanWait(ms); setCanWait(ms);
return *this; return *this;
} }
@ -246,7 +246,7 @@ public:
setFailSkipPolicy(FailSkipPolicy::HandleAll); setFailSkipPolicy(FailSkipPolicy::HandleAll);
return *this; return *this;
} }
[[nodiscard]] SpecificRequestBuilder &after(mtpRequestId requestId) noexcept { [[nodiscard]] SpecificRequestBuilder &afterRequest(mtpRequestId requestId) noexcept {
setAfter(requestId); setAfter(requestId);
return *this; return *this;
} }

View File

@ -417,13 +417,15 @@ void Manager::notificationActivated(PeerId peerId, MsgId msgId) {
onAfterNotificationActivated(peerId, msgId); onAfterNotificationActivated(peerId, msgId);
} }
void Manager::notificationReplied(PeerId peerId, MsgId msgId, const QString &reply) { void Manager::notificationReplied(
PeerId peerId,
MsgId msgId,
const QString &reply) {
if (!peerId) return; if (!peerId) return;
auto history = App::history(peerId); auto history = App::history(peerId);
MainWidget::MessageToSend message; auto message = MainWidget::MessageToSend(history);
message.history = history;
message.textWithTags = { reply, TextWithTags::Tags() }; message.textWithTags = { reply, TextWithTags::Tags() };
message.replyTo = (msgId > 0 && !history->peer->isUser()) ? msgId : 0; message.replyTo = (msgId > 0 && !history->peer->isUser()) ? msgId : 0;
message.silent = false; message.silent = false;

View File

@ -419,17 +419,20 @@ void PeerMenuAddContact(not_null<UserData*> user) {
} }
void PeerMenuShareContactBox(not_null<UserData*> user) { void PeerMenuShareContactBox(not_null<UserData*> user) {
auto callback = [user](not_null<PeerData*> peer) { const auto weak = std::make_shared<QPointer<PeerListBox>>();
auto callback = [=](not_null<PeerData*> peer) {
if (!peer->canWrite()) { if (!peer->canWrite()) {
Ui::show(Box<InformBox>( Ui::show(Box<InformBox>(
lang(lng_forward_share_cant)), lang(lng_forward_share_cant)),
LayerOption::KeepOther); LayerOption::KeepOther);
return; return;
} else if (peer->isSelf()) { } else if (peer->isSelf()) {
App::main()->onShareContact( auto options = ApiWrap::SendOptions(App::history(peer));
peer->id, Auth().api().shareContact(user, options);
user);
Ui::Toast::Show(lang(lng_share_done)); Ui::Toast::Show(lang(lng_share_done));
if (auto strong = *weak) {
strong->closeBox();
}
return; return;
} }
auto recipient = peer->isUser() auto recipient = peer->isUser()
@ -439,13 +442,13 @@ void PeerMenuShareContactBox(not_null<UserData*> user) {
lng_forward_share_contact(lt_recipient, recipient), lng_forward_share_contact(lt_recipient, recipient),
lang(lng_forward_send), lang(lng_forward_send),
[peer, user] { [peer, user] {
App::main()->onShareContact( const auto history = App::history(peer);
peer->id, Ui::showPeerHistory(history, ShowAtTheEndMsgId);
user); auto options = ApiWrap::SendOptions(history);
Ui::hideLayer(); Auth().api().shareContact(user, options);
}), LayerOption::KeepOther); }), LayerOption::KeepOther);
}; };
Ui::show(Box<PeerListBox>( *weak = Ui::show(Box<PeerListBox>(
std::make_unique<ChooseRecipientBoxController>(std::move(callback)), std::make_unique<ChooseRecipientBoxController>(std::move(callback)),
[](not_null<PeerListBox*> box) { [](not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_cancel), [box] { box->addButton(langFactory(lng_cancel), [box] {
@ -457,7 +460,7 @@ void PeerMenuShareContactBox(not_null<UserData*> user) {
void ShowForwardMessagesBox( void ShowForwardMessagesBox(
MessageIdsList &&items, MessageIdsList &&items,
base::lambda_once<void()> &&successCallback) { base::lambda_once<void()> &&successCallback) {
auto weak = std::make_shared<QPointer<PeerListBox>>(); const auto weak = std::make_shared<QPointer<PeerListBox>>();
auto callback = [ auto callback = [
ids = std::move(items), ids = std::move(items),
callback = std::move(successCallback), callback = std::move(successCallback),

View File

@ -203,7 +203,6 @@
<(src_loc)/history/history_admin_log_item.h <(src_loc)/history/history_admin_log_item.h
<(src_loc)/history/history_admin_log_section.cpp <(src_loc)/history/history_admin_log_section.cpp
<(src_loc)/history/history_admin_log_section.h <(src_loc)/history/history_admin_log_section.h
<(src_loc)/history/history_common.h
<(src_loc)/history/history_drag_area.cpp <(src_loc)/history/history_drag_area.cpp
<(src_loc)/history/history_drag_area.h <(src_loc)/history/history_drag_area.h
<(src_loc)/history/history_item.cpp <(src_loc)/history/history_item.cpp