2016-09-27 14:20:49 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
2018-01-03 10:23:14 +00:00
|
|
|
the official desktop application for the Telegram messaging service.
|
2016-09-27 14:20:49 +00:00
|
|
|
|
2018-01-03 10:23:14 +00:00
|
|
|
For license and copyright information please follow this link:
|
|
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
2016-09-27 14:20:49 +00:00
|
|
|
*/
|
|
|
|
#include "history/history_message.h"
|
|
|
|
|
2017-04-13 08:27:10 +00:00
|
|
|
#include "lang/lang_keys.h"
|
2016-09-27 14:20:49 +00:00
|
|
|
#include "mainwidget.h"
|
|
|
|
#include "mainwindow.h"
|
|
|
|
#include "apiwrap.h"
|
2018-01-13 12:45:11 +00:00
|
|
|
#include "history/history.h"
|
2017-12-18 15:44:50 +00:00
|
|
|
#include "history/history_item_components.h"
|
2016-09-27 14:20:49 +00:00
|
|
|
#include "history/history_location_manager.h"
|
2016-09-28 10:15:03 +00:00
|
|
|
#include "history/history_media_types.h"
|
2017-06-22 15:11:41 +00:00
|
|
|
#include "history/history_service.h"
|
2018-01-09 17:08:31 +00:00
|
|
|
#include "history/view/history_view_service_message.h"
|
2017-02-23 09:32:28 +00:00
|
|
|
#include "auth_session.h"
|
2017-07-11 17:11:06 +00:00
|
|
|
#include "boxes/share_box.h"
|
|
|
|
#include "boxes/confirm_box.h"
|
|
|
|
#include "ui/toast/toast.h"
|
2017-12-28 13:06:06 +00:00
|
|
|
#include "ui/text_options.h"
|
2017-07-11 17:11:06 +00:00
|
|
|
#include "messenger.h"
|
2018-01-11 19:33:26 +00:00
|
|
|
#include "layout.h"
|
2017-03-04 19:36:59 +00:00
|
|
|
#include "window/notifications_manager.h"
|
2017-12-05 16:14:28 +00:00
|
|
|
#include "window/window_controller.h"
|
2017-08-11 07:16:07 +00:00
|
|
|
#include "observer_peer.h"
|
2017-09-04 11:40:02 +00:00
|
|
|
#include "storage/storage_shared_media.h"
|
2018-01-04 10:22:53 +00:00
|
|
|
#include "data/data_session.h"
|
2018-01-14 16:02:25 +00:00
|
|
|
#include "data/data_media_types.h"
|
|
|
|
#include "styles/style_dialogs.h"
|
|
|
|
#include "styles/style_widgets.h"
|
|
|
|
#include "styles/style_history.h"
|
|
|
|
#include "styles/style_window.h"
|
2016-09-27 14:20:49 +00:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2017-06-21 08:53:14 +00:00
|
|
|
constexpr auto kPinnedMessageTextLimit = 16;
|
|
|
|
|
2017-12-01 10:21:40 +00:00
|
|
|
MTPDmessage::Flags NewForwardedFlags(
|
|
|
|
not_null<PeerData*> peer,
|
|
|
|
UserId from,
|
|
|
|
not_null<HistoryMessage*> fwd) {
|
2017-06-22 15:11:41 +00:00
|
|
|
auto result = NewMessageFlags(peer) | MTPDmessage::Flag::f_fwd_from;
|
2017-06-16 16:04:30 +00:00
|
|
|
if (from) {
|
|
|
|
result |= MTPDmessage::Flag::f_from_id;
|
|
|
|
}
|
|
|
|
if (fwd->Has<HistoryMessageVia>()) {
|
|
|
|
result |= MTPDmessage::Flag::f_via_bot_id;
|
|
|
|
}
|
2018-01-14 16:02:25 +00:00
|
|
|
if (const auto channel = peer->asChannel()) {
|
|
|
|
if (dynamic_cast<Data::MediaWebPage*>(fwd->media())) {
|
|
|
|
// Drop web page if we're not allowed to send it.
|
|
|
|
if (channel->restricted(
|
|
|
|
ChannelRestriction::f_embed_links)) {
|
|
|
|
result &= ~MTPDmessage::Flag::f_media;
|
2017-06-16 16:04:30 +00:00
|
|
|
}
|
|
|
|
}
|
2018-01-14 16:02:25 +00:00
|
|
|
} else if (const auto media = fwd->media()) {
|
|
|
|
if (media->forwardedBecomesUnread()) {
|
|
|
|
result |= MTPDmessage::Flag::f_media_unread;
|
2017-06-16 16:04:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (fwd->hasViews()) {
|
|
|
|
result |= MTPDmessage::Flag::f_views;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-12-06 10:13:38 +00:00
|
|
|
bool HasInlineItems(const HistoryItemsList &items) {
|
|
|
|
for (const auto item : items) {
|
2017-07-11 17:11:06 +00:00
|
|
|
if (item->viaBot()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
} // namespace
|
|
|
|
|
2017-08-17 08:31:24 +00:00
|
|
|
void FastShareMessage(not_null<HistoryItem*> item) {
|
2017-07-11 17:11:06 +00:00
|
|
|
struct ShareData {
|
2017-12-15 21:23:20 +00:00
|
|
|
ShareData(not_null<PeerData*> peer, MessageIdsList &&ids)
|
|
|
|
: peer(peer)
|
|
|
|
, msgIds(std::move(ids)) {
|
2017-07-11 17:11:06 +00:00
|
|
|
}
|
2017-12-15 21:23:20 +00:00
|
|
|
not_null<PeerData*> peer;
|
|
|
|
MessageIdsList msgIds;
|
|
|
|
base::flat_set<mtpRequestId> requests;
|
2017-07-11 17:11:06 +00:00
|
|
|
};
|
2018-01-11 15:51:59 +00:00
|
|
|
const auto data = std::make_shared<ShareData>(
|
|
|
|
item->history()->peer,
|
|
|
|
Auth().data().itemOrItsGroup(item));
|
2018-01-14 16:02:25 +00:00
|
|
|
const auto isGroup = (Auth().data().groups().find(item) != nullptr);
|
2017-12-15 21:23:20 +00:00
|
|
|
const auto isGame = item->getMessageBot()
|
2018-01-14 16:02:25 +00:00
|
|
|
&& item->media()
|
|
|
|
&& (item->media()->game() != nullptr);
|
2017-12-15 21:23:20 +00:00
|
|
|
const auto canCopyLink = item->hasDirectLink() || isGame;
|
2017-07-11 17:11:06 +00:00
|
|
|
|
|
|
|
auto copyCallback = [data]() {
|
|
|
|
if (auto main = App::main()) {
|
2017-12-15 21:23:20 +00:00
|
|
|
if (auto item = App::histItemById(data->msgIds[0])) {
|
2017-07-11 17:11:06 +00:00
|
|
|
if (item->hasDirectLink()) {
|
|
|
|
QApplication::clipboard()->setText(item->directLink());
|
|
|
|
|
|
|
|
Ui::Toast::Show(lang(lng_channel_public_link_copied));
|
2018-01-14 16:02:25 +00:00
|
|
|
} else if (const auto bot = item->getMessageBot()) {
|
|
|
|
if (const auto media = item->media()) {
|
|
|
|
if (const auto game = media->game()) {
|
|
|
|
const auto link = Messenger::Instance().createInternalLinkFull(
|
|
|
|
bot->username
|
|
|
|
+ qsl("?game=")
|
|
|
|
+ game->shortName);
|
2017-07-11 17:11:06 +00:00
|
|
|
|
2018-01-14 16:02:25 +00:00
|
|
|
QApplication::clipboard()->setText(link);
|
2017-07-11 17:11:06 +00:00
|
|
|
|
|
|
|
Ui::Toast::Show(lang(lng_share_game_link_copied));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2017-12-18 13:29:48 +00:00
|
|
|
auto submitCallback = [data, isGroup](const QVector<PeerData*> &result) {
|
2017-07-11 17:11:06 +00:00
|
|
|
if (!data->requests.empty()) {
|
|
|
|
return; // Share clicked already.
|
|
|
|
}
|
2017-12-15 21:23:20 +00:00
|
|
|
auto items = Auth().data().idsToItems(data->msgIds);
|
|
|
|
if (items.empty() || result.empty()) {
|
2017-07-11 17:11:06 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto restrictedSomewhere = false;
|
|
|
|
auto restrictedEverywhere = true;
|
|
|
|
auto firstError = QString();
|
2017-12-06 10:13:38 +00:00
|
|
|
for (const auto peer : result) {
|
|
|
|
const auto error = GetErrorTextForForward(peer, items);
|
2017-07-11 17:11:06 +00:00
|
|
|
if (!error.isEmpty()) {
|
|
|
|
if (firstError.isEmpty()) {
|
|
|
|
firstError = error;
|
|
|
|
}
|
|
|
|
restrictedSomewhere = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
restrictedEverywhere = false;
|
|
|
|
}
|
|
|
|
if (restrictedEverywhere) {
|
2017-09-15 17:34:41 +00:00
|
|
|
Ui::show(
|
|
|
|
Box<InformBox>(firstError),
|
|
|
|
LayerOption::KeepOther);
|
2017-07-11 17:11:06 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto doneCallback = [data](const MTPUpdates &updates, mtpRequestId requestId) {
|
|
|
|
if (auto main = App::main()) {
|
|
|
|
main->sentUpdatesReceived(updates);
|
|
|
|
}
|
|
|
|
data->requests.remove(requestId);
|
|
|
|
if (data->requests.empty()) {
|
|
|
|
Ui::Toast::Show(lang(lng_share_done));
|
|
|
|
Ui::hideLayer();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-12-18 13:29:48 +00:00
|
|
|
const auto sendFlags = MTPmessages_ForwardMessages::Flag(0)
|
|
|
|
| MTPmessages_ForwardMessages::Flag::f_with_my_score
|
|
|
|
| (isGroup
|
|
|
|
? MTPmessages_ForwardMessages::Flag::f_grouped
|
|
|
|
: MTPmessages_ForwardMessages::Flag(0));
|
2017-12-15 21:23:20 +00:00
|
|
|
auto msgIds = QVector<MTPint>();
|
|
|
|
msgIds.reserve(data->msgIds.size());
|
|
|
|
for (const auto fullId : data->msgIds) {
|
|
|
|
msgIds.push_back(MTP_int(fullId.msg));
|
|
|
|
}
|
|
|
|
auto generateRandom = [&] {
|
|
|
|
auto result = QVector<MTPlong>(data->msgIds.size());
|
|
|
|
for (auto &value : result) {
|
|
|
|
value = rand_value<MTPlong>();
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
};
|
2017-07-11 17:11:06 +00:00
|
|
|
if (auto main = App::main()) {
|
2017-12-06 10:13:38 +00:00
|
|
|
for (const auto peer : result) {
|
2017-07-11 17:11:06 +00:00
|
|
|
if (!GetErrorTextForForward(peer, items).isEmpty()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-12-15 21:23:20 +00:00
|
|
|
auto request = MTPmessages_ForwardMessages(
|
|
|
|
MTP_flags(sendFlags),
|
|
|
|
data->peer->input,
|
|
|
|
MTP_vector<MTPint>(msgIds),
|
|
|
|
MTP_vector<MTPlong>(generateRandom()),
|
|
|
|
peer->input);
|
2017-07-11 17:11:06 +00:00
|
|
|
auto callback = doneCallback;
|
|
|
|
auto requestId = MTP::send(request, rpcDone(std::move(callback)));
|
|
|
|
data->requests.insert(requestId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2017-07-25 19:38:08 +00:00
|
|
|
auto filterCallback = [isGame](PeerData *peer) {
|
2017-07-11 17:11:06 +00:00
|
|
|
if (peer->canWrite()) {
|
|
|
|
if (auto channel = peer->asChannel()) {
|
2017-07-25 19:38:08 +00:00
|
|
|
return isGame ? (!channel->isBroadcast()) : true;
|
2017-07-11 17:11:06 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
2017-12-05 14:07:01 +00:00
|
|
|
auto copyLinkCallback = canCopyLink
|
|
|
|
? base::lambda<void()>(std::move(copyCallback))
|
|
|
|
: base::lambda<void()>();
|
|
|
|
Ui::show(Box<ShareBox>(
|
|
|
|
std::move(copyLinkCallback),
|
|
|
|
std::move(submitCallback),
|
|
|
|
std::move(filterCallback)));
|
2017-07-11 17:11:06 +00:00
|
|
|
}
|
|
|
|
|
2017-12-18 15:44:50 +00:00
|
|
|
base::lambda<void(ChannelData*, MsgId)> HistoryDependentItemCallback(
|
|
|
|
const FullMsgId &msgId) {
|
2017-06-22 15:11:41 +00:00
|
|
|
return [dependent = msgId](ChannelData *channel, MsgId msgId) {
|
|
|
|
if (auto item = App::histItemById(dependent)) {
|
|
|
|
item->updateDependencyItem();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-08-17 08:31:24 +00:00
|
|
|
MTPDmessage::Flags NewMessageFlags(not_null<PeerData*> peer) {
|
2017-06-22 15:11:41 +00:00
|
|
|
MTPDmessage::Flags result = 0;
|
|
|
|
if (!peer->isSelf()) {
|
|
|
|
result |= MTPDmessage::Flag::f_out;
|
|
|
|
//if (p->isChat() || (p->isUser() && !p->asUser()->botInfo)) {
|
|
|
|
// result |= MTPDmessage::Flag::f_unread;
|
|
|
|
//}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-12-06 10:13:38 +00:00
|
|
|
QString GetErrorTextForForward(
|
|
|
|
not_null<PeerData*> peer,
|
|
|
|
const HistoryItemsList &items) {
|
2017-07-11 17:11:06 +00:00
|
|
|
if (!peer->canWrite()) {
|
|
|
|
return lang(lng_forward_cant);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (auto megagroup = peer->asMegagroup()) {
|
2018-01-14 16:02:25 +00:00
|
|
|
for (const auto item : items) {
|
|
|
|
if (const auto media = item->media()) {
|
|
|
|
const auto error = media->errorTextForForward(megagroup);
|
|
|
|
if (!error.isEmpty() && error != qstr("skip")) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (megagroup->restricted(ChannelRestriction::f_send_inline)
|
|
|
|
&& HasInlineItems(items)) {
|
2017-07-11 17:11:06 +00:00
|
|
|
return lang(lng_restricted_send_inline);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return QString();
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
2017-12-18 15:44:50 +00:00
|
|
|
struct HistoryMessage::CreateConfig {
|
|
|
|
MsgId replyTo = 0;
|
|
|
|
UserId viaBotId = 0;
|
|
|
|
int viewsCount = -1;
|
|
|
|
QString author;
|
|
|
|
PeerId senderOriginal = 0;
|
|
|
|
MsgId originalId = 0;
|
|
|
|
PeerId savedFromPeer = 0;
|
|
|
|
MsgId savedFromMsgId = 0;
|
|
|
|
QString authorOriginal;
|
|
|
|
QDateTime originalDate;
|
|
|
|
QDateTime editDate;
|
2016-09-27 14:20:49 +00:00
|
|
|
|
2017-12-18 15:44:50 +00:00
|
|
|
// For messages created from MTP structs.
|
|
|
|
const MTPReplyMarkup *mtpMarkup = nullptr;
|
2016-09-27 14:20:49 +00:00
|
|
|
|
2017-12-18 15:44:50 +00:00
|
|
|
// For messages created from existing messages (forwarded).
|
|
|
|
const HistoryMessageReplyMarkup *inlineMarkup = nullptr;
|
|
|
|
};
|
2016-09-27 14:20:49 +00:00
|
|
|
|
2017-12-13 18:10:48 +00:00
|
|
|
HistoryMessage::HistoryMessage(
|
|
|
|
not_null<History*> history,
|
|
|
|
const MTPDmessage &msg)
|
2017-06-16 16:04:30 +00:00
|
|
|
: HistoryItem(history, msg.vid.v, msg.vflags.v, ::date(msg.vdate), msg.has_from_id() ? msg.vfrom_id.v : 0) {
|
2016-09-27 14:20:49 +00:00
|
|
|
CreateConfig config;
|
|
|
|
|
|
|
|
if (msg.has_fwd_from() && msg.vfwd_from.type() == mtpc_messageFwdHeader) {
|
2016-10-07 07:58:34 +00:00
|
|
|
auto &f = msg.vfwd_from.c_messageFwdHeader();
|
2017-06-20 22:05:38 +00:00
|
|
|
config.originalDate = ::date(f.vdate);
|
2016-09-27 14:20:49 +00:00
|
|
|
if (f.has_from_id() || f.has_channel_id()) {
|
2017-12-05 16:14:28 +00:00
|
|
|
config.senderOriginal = f.has_channel_id()
|
|
|
|
? peerFromChannel(f.vchannel_id)
|
|
|
|
: peerFromUser(f.vfrom_id);
|
2016-09-27 14:20:49 +00:00
|
|
|
if (f.has_channel_post()) config.originalId = f.vchannel_post.v;
|
2017-07-12 19:14:20 +00:00
|
|
|
if (f.has_post_author()) config.authorOriginal = qs(f.vpost_author);
|
2017-12-05 16:14:28 +00:00
|
|
|
if (f.has_saved_from_peer() && f.has_saved_from_msg_id()) {
|
|
|
|
config.savedFromPeer = peerFromMTP(f.vsaved_from_peer);
|
|
|
|
config.savedFromMsgId = f.vsaved_from_msg_id.v;
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (msg.has_reply_to_msg_id()) config.replyTo = msg.vreply_to_msg_id.v;
|
|
|
|
if (msg.has_via_bot_id()) config.viaBotId = msg.vvia_bot_id.v;
|
|
|
|
if (msg.has_views()) config.viewsCount = msg.vviews.v;
|
2016-10-07 07:58:34 +00:00
|
|
|
if (msg.has_reply_markup()) config.mtpMarkup = &msg.vreply_markup;
|
2016-09-27 14:20:49 +00:00
|
|
|
if (msg.has_edit_date()) config.editDate = ::date(msg.vedit_date);
|
2017-07-12 19:14:20 +00:00
|
|
|
if (msg.has_post_author()) config.author = qs(msg.vpost_author);
|
2017-12-13 18:10:48 +00:00
|
|
|
if (msg.has_grouped_id()) {
|
2018-01-14 16:02:25 +00:00
|
|
|
setGroupId(MessageGroupId::FromRaw(msg.vgrouped_id.v));
|
2017-12-13 18:10:48 +00:00
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
|
|
|
|
createComponents(config);
|
|
|
|
|
2016-09-28 11:38:35 +00:00
|
|
|
initMedia(msg.has_media() ? (&msg.vmedia) : nullptr);
|
2016-09-27 14:20:49 +00:00
|
|
|
|
2017-07-06 11:37:42 +00:00
|
|
|
auto text = TextUtilities::Clean(qs(msg.vmessage));
|
2018-01-18 11:04:25 +00:00
|
|
|
auto entities = msg.has_entities()
|
|
|
|
? TextUtilities::EntitiesFromMTP(msg.ventities.v)
|
|
|
|
: EntitiesInText();
|
2017-06-20 16:03:18 +00:00
|
|
|
setText({ text, entities });
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
2017-12-13 18:10:48 +00:00
|
|
|
HistoryMessage::HistoryMessage(
|
|
|
|
not_null<History*> history,
|
|
|
|
const MTPDmessageService &msg)
|
2017-06-16 16:04:30 +00:00
|
|
|
: HistoryItem(history, msg.vid.v, mtpCastFlags(msg.vflags.v), ::date(msg.vdate), msg.has_from_id() ? msg.vfrom_id.v : 0) {
|
2017-04-27 21:17:00 +00:00
|
|
|
CreateConfig config;
|
|
|
|
|
|
|
|
if (msg.has_reply_to_msg_id()) config.replyTo = msg.vreply_to_msg_id.v;
|
|
|
|
|
|
|
|
createComponents(config);
|
|
|
|
|
|
|
|
switch (msg.vaction.type()) {
|
|
|
|
case mtpc_messageActionPhoneCall: {
|
2018-01-14 16:02:25 +00:00
|
|
|
_media = std::make_unique<Data::MediaCall>(
|
|
|
|
this,
|
|
|
|
msg.vaction.c_messageActionPhoneCall());
|
2017-04-27 21:17:00 +00:00
|
|
|
} break;
|
|
|
|
|
|
|
|
default: Unexpected("Service message action type in HistoryMessage.");
|
|
|
|
}
|
|
|
|
|
|
|
|
setText(TextWithEntities {});
|
|
|
|
}
|
|
|
|
|
2017-12-05 16:14:28 +00:00
|
|
|
HistoryMessage::HistoryMessage(
|
|
|
|
not_null<History*> history,
|
|
|
|
MsgId id,
|
|
|
|
MTPDmessage::Flags flags,
|
|
|
|
QDateTime date,
|
|
|
|
UserId from,
|
|
|
|
const QString &postAuthor,
|
|
|
|
not_null<HistoryMessage*> fwd)
|
2017-06-22 15:11:41 +00:00
|
|
|
: HistoryItem(history, id, NewForwardedFlags(history->peer, from, fwd) | flags, date, from) {
|
2016-09-27 14:20:49 +00:00
|
|
|
CreateConfig config;
|
|
|
|
|
2017-04-30 12:42:04 +00:00
|
|
|
if (fwd->Has<HistoryMessageForwarded>() || !fwd->history()->peer->isSelf()) {
|
|
|
|
// Server doesn't add "fwd_from" to non-forwarded messages from chat with yourself.
|
2017-06-20 22:05:38 +00:00
|
|
|
config.originalDate = fwd->dateOriginal();
|
2017-07-26 06:05:06 +00:00
|
|
|
auto senderOriginal = fwd->senderOriginal();
|
|
|
|
config.senderOriginal = senderOriginal->id;
|
2017-07-12 19:14:20 +00:00
|
|
|
config.authorOriginal = fwd->authorOriginal();
|
2017-07-26 06:05:06 +00:00
|
|
|
if (senderOriginal->isChannel()) {
|
2017-04-30 12:42:04 +00:00
|
|
|
config.originalId = fwd->idOriginal();
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2017-12-05 16:14:28 +00:00
|
|
|
if (history->peer->isSelf()) {
|
|
|
|
//
|
|
|
|
// iOS app sends you to the original post if we forward a forward from channel.
|
|
|
|
// But server returns not the original post but the forward in saved_from_...
|
|
|
|
//
|
|
|
|
//if (config.originalId) {
|
|
|
|
// config.savedFromPeer = config.senderOriginal;
|
|
|
|
// config.savedFromMsgId = config.originalId;
|
|
|
|
//} else {
|
|
|
|
config.savedFromPeer = fwd->history()->peer->id;
|
|
|
|
config.savedFromMsgId = fwd->id;
|
|
|
|
//}
|
|
|
|
}
|
2017-07-12 19:14:20 +00:00
|
|
|
if (flags & MTPDmessage::Flag::f_post_author) {
|
|
|
|
config.author = postAuthor;
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
auto fwdViaBot = fwd->viaBot();
|
|
|
|
if (fwdViaBot) config.viaBotId = peerToUser(fwdViaBot->id);
|
|
|
|
int fwdViewsCount = fwd->viewsCount();
|
|
|
|
if (fwdViewsCount > 0) {
|
|
|
|
config.viewsCount = fwdViewsCount;
|
|
|
|
} else if (isPost()) {
|
|
|
|
config.viewsCount = 1;
|
|
|
|
}
|
|
|
|
|
2016-10-07 07:58:34 +00:00
|
|
|
// Copy inline keyboard when forwarding messages with a game.
|
2018-01-14 16:02:25 +00:00
|
|
|
auto mediaOriginal = fwd->media();
|
|
|
|
if (mediaOriginal && mediaOriginal->game()) {
|
2016-10-07 07:58:34 +00:00
|
|
|
config.inlineMarkup = fwd->inlineReplyMarkup();
|
|
|
|
}
|
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
createComponents(config);
|
|
|
|
|
2018-01-14 16:02:25 +00:00
|
|
|
auto ignoreMedia = [&] {
|
|
|
|
if (mediaOriginal && mediaOriginal->webpage()) {
|
|
|
|
if (const auto channel = history->peer->asChannel()) {
|
2017-09-26 13:53:51 +00:00
|
|
|
if (channel->restricted(ChannelRestriction::f_embed_links)) {
|
2018-01-14 16:02:25 +00:00
|
|
|
return true;
|
2017-06-16 16:04:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-01-14 16:02:25 +00:00
|
|
|
return false;
|
2017-06-16 16:04:30 +00:00
|
|
|
};
|
2018-01-14 16:02:25 +00:00
|
|
|
if (mediaOriginal && !ignoreMedia()) {
|
|
|
|
_media = mediaOriginal->clone(this);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
setText(fwd->originalText());
|
|
|
|
}
|
|
|
|
|
2017-12-13 18:10:48 +00:00
|
|
|
HistoryMessage::HistoryMessage(
|
|
|
|
not_null<History*> history,
|
|
|
|
MsgId id,
|
|
|
|
MTPDmessage::Flags flags,
|
|
|
|
MsgId replyTo,
|
|
|
|
UserId viaBotId,
|
|
|
|
QDateTime date,
|
|
|
|
UserId from,
|
|
|
|
const QString &postAuthor,
|
|
|
|
const TextWithEntities &textWithEntities)
|
2017-12-05 16:14:28 +00:00
|
|
|
: HistoryItem(history, id, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) {
|
2017-07-12 19:14:20 +00:00
|
|
|
createComponentsHelper(flags, replyTo, viaBotId, postAuthor, MTPnullMarkup);
|
2016-09-27 14:20:49 +00:00
|
|
|
|
|
|
|
setText(textWithEntities);
|
|
|
|
}
|
|
|
|
|
2017-12-13 18:10:48 +00:00
|
|
|
HistoryMessage::HistoryMessage(
|
|
|
|
not_null<History*> history,
|
|
|
|
MsgId msgId,
|
|
|
|
MTPDmessage::Flags flags,
|
|
|
|
MsgId replyTo,
|
|
|
|
UserId viaBotId,
|
|
|
|
QDateTime date,
|
|
|
|
UserId from,
|
|
|
|
const QString &postAuthor,
|
|
|
|
not_null<DocumentData*> document,
|
|
|
|
const QString &caption,
|
|
|
|
const MTPReplyMarkup &markup)
|
2017-12-05 16:14:28 +00:00
|
|
|
: HistoryItem(history, msgId, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) {
|
2017-07-12 19:14:20 +00:00
|
|
|
createComponentsHelper(flags, replyTo, viaBotId, postAuthor, markup);
|
2016-09-27 14:20:49 +00:00
|
|
|
|
2018-01-14 16:02:25 +00:00
|
|
|
_media = std::make_unique<Data::MediaFile>(this, document, caption);
|
2016-09-27 14:20:49 +00:00
|
|
|
setText(TextWithEntities());
|
|
|
|
}
|
|
|
|
|
2017-12-13 18:10:48 +00:00
|
|
|
HistoryMessage::HistoryMessage(
|
|
|
|
not_null<History*> history,
|
|
|
|
MsgId msgId,
|
|
|
|
MTPDmessage::Flags flags,
|
|
|
|
MsgId replyTo,
|
|
|
|
UserId viaBotId,
|
|
|
|
QDateTime date,
|
|
|
|
UserId from,
|
|
|
|
const QString &postAuthor,
|
|
|
|
not_null<PhotoData*> photo,
|
|
|
|
const QString &caption,
|
|
|
|
const MTPReplyMarkup &markup)
|
2017-12-05 16:14:28 +00:00
|
|
|
: HistoryItem(history, msgId, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) {
|
2017-07-12 19:14:20 +00:00
|
|
|
createComponentsHelper(flags, replyTo, viaBotId, postAuthor, markup);
|
2016-09-27 14:20:49 +00:00
|
|
|
|
2018-01-14 16:02:25 +00:00
|
|
|
_media = std::make_unique<Data::MediaPhoto>(this, photo, caption);
|
2016-09-27 14:20:49 +00:00
|
|
|
setText(TextWithEntities());
|
|
|
|
}
|
|
|
|
|
2017-12-13 18:10:48 +00:00
|
|
|
HistoryMessage::HistoryMessage(
|
|
|
|
not_null<History*> history,
|
|
|
|
MsgId msgId,
|
|
|
|
MTPDmessage::Flags flags,
|
|
|
|
MsgId replyTo,
|
|
|
|
UserId viaBotId,
|
|
|
|
QDateTime date,
|
|
|
|
UserId from,
|
|
|
|
const QString &postAuthor,
|
|
|
|
not_null<GameData*> game,
|
|
|
|
const MTPReplyMarkup &markup)
|
2017-12-05 16:14:28 +00:00
|
|
|
: HistoryItem(history, msgId, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) {
|
2017-07-12 19:14:20 +00:00
|
|
|
createComponentsHelper(flags, replyTo, viaBotId, postAuthor, markup);
|
2016-09-28 16:23:25 +00:00
|
|
|
|
2018-01-14 16:02:25 +00:00
|
|
|
_media = std::make_unique<Data::MediaGame>(this, game);
|
2016-09-28 16:23:25 +00:00
|
|
|
setText(TextWithEntities());
|
|
|
|
}
|
|
|
|
|
2017-12-13 18:10:48 +00:00
|
|
|
void HistoryMessage::createComponentsHelper(
|
|
|
|
MTPDmessage::Flags flags,
|
|
|
|
MsgId replyTo,
|
|
|
|
UserId viaBotId,
|
|
|
|
const QString &postAuthor,
|
|
|
|
const MTPReplyMarkup &markup) {
|
2016-09-27 14:20:49 +00:00
|
|
|
CreateConfig config;
|
|
|
|
|
|
|
|
if (flags & MTPDmessage::Flag::f_via_bot_id) config.viaBotId = viaBotId;
|
|
|
|
if (flags & MTPDmessage::Flag::f_reply_to_msg_id) config.replyTo = replyTo;
|
2016-10-07 07:58:34 +00:00
|
|
|
if (flags & MTPDmessage::Flag::f_reply_markup) config.mtpMarkup = &markup;
|
2017-07-12 19:14:20 +00:00
|
|
|
if (flags & MTPDmessage::Flag::f_post_author) config.author = postAuthor;
|
2016-09-27 14:20:49 +00:00
|
|
|
if (isPost()) config.viewsCount = 1;
|
|
|
|
|
|
|
|
createComponents(config);
|
|
|
|
}
|
|
|
|
|
2017-12-18 15:44:50 +00:00
|
|
|
int HistoryMessage::viewsCount() const {
|
|
|
|
if (const auto views = Get<HistoryMessageViews>()) {
|
|
|
|
return views->_views;
|
|
|
|
}
|
|
|
|
return HistoryItem::viewsCount();
|
|
|
|
}
|
|
|
|
|
2017-12-05 16:14:28 +00:00
|
|
|
not_null<PeerData*> HistoryMessage::displayFrom() const {
|
|
|
|
return history()->peer->isSelf()
|
|
|
|
? senderOriginal()
|
|
|
|
: author();
|
|
|
|
}
|
|
|
|
|
2017-12-18 15:44:50 +00:00
|
|
|
bool HistoryMessage::updateDependencyItem() {
|
|
|
|
if (const auto reply = Get<HistoryMessageReply>()) {
|
|
|
|
return reply->updateData(this, true);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-12-01 18:38:44 +00:00
|
|
|
void HistoryMessage::updateAdminBadgeState() {
|
|
|
|
auto hasAdminBadge = [&] {
|
|
|
|
if (auto channel = history()->peer->asChannel()) {
|
|
|
|
if (auto user = author()->asUser()) {
|
|
|
|
return channel->isGroupAdmin(user);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}();
|
|
|
|
if (hasAdminBadge) {
|
|
|
|
_flags |= MTPDmessage_ClientFlag::f_has_admin_badge;
|
|
|
|
} else {
|
|
|
|
_flags &= ~MTPDmessage_ClientFlag::f_has_admin_badge;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::applyGroupAdminChanges(
|
|
|
|
const base::flat_map<UserId, bool> &changes) {
|
|
|
|
auto i = changes.find(peerToUser(author()->id));
|
|
|
|
if (i != changes.end()) {
|
|
|
|
if (i->second) {
|
|
|
|
_flags |= MTPDmessage_ClientFlag::f_has_admin_badge;
|
|
|
|
} else {
|
|
|
|
_flags &= ~MTPDmessage_ClientFlag::f_has_admin_badge;
|
|
|
|
}
|
2018-01-13 12:45:11 +00:00
|
|
|
Auth().data().requestItemViewResize(this);
|
2017-12-01 18:38:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-14 16:02:25 +00:00
|
|
|
bool HistoryMessage::allowsForward() const {
|
|
|
|
if (id < 0 || isLogEntry()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return !_media || _media->allowsForward();
|
2017-12-16 16:32:10 +00:00
|
|
|
}
|
|
|
|
|
2018-01-14 16:02:25 +00:00
|
|
|
bool HistoryMessage::allowsEdit(const QDateTime &now) const {
|
|
|
|
const auto peer = _history->peer;
|
|
|
|
const auto messageToMyself = peer->isSelf();
|
|
|
|
const auto canPinInMegagroup = [&] {
|
|
|
|
if (const auto megagroup = peer->asMegagroup()) {
|
|
|
|
return megagroup->canPinMessages();
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2018-01-14 16:02:25 +00:00
|
|
|
return false;
|
|
|
|
}();
|
|
|
|
const auto messageTooOld = (messageToMyself || canPinInMegagroup)
|
|
|
|
? false
|
|
|
|
: (date.secsTo(now) >= Global::EditTimeLimit());
|
|
|
|
if (id < 0 || messageTooOld) {
|
|
|
|
return false;
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2017-12-16 16:32:10 +00:00
|
|
|
|
2018-01-14 16:02:25 +00:00
|
|
|
if (Has<HistoryMessageVia>() || Has<HistoryMessageForwarded>()) {
|
|
|
|
return false;
|
2017-12-16 16:32:10 +00:00
|
|
|
}
|
|
|
|
|
2018-01-14 16:02:25 +00:00
|
|
|
if (_media && !_media->allowsEdit()) {
|
|
|
|
return false;
|
2017-12-16 16:32:10 +00:00
|
|
|
}
|
2018-01-14 16:02:25 +00:00
|
|
|
if (messageToMyself) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (const auto channel = _history->peer->asChannel()) {
|
|
|
|
if (isPost() && channel->canEditMessages()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (out()) {
|
|
|
|
return !isPost() || channel->canPublish();
|
2017-07-11 17:11:06 +00:00
|
|
|
}
|
|
|
|
}
|
2018-01-14 16:02:25 +00:00
|
|
|
return out();
|
2017-07-11 17:11:06 +00:00
|
|
|
}
|
|
|
|
|
2018-01-14 16:02:25 +00:00
|
|
|
bool HistoryMessage::uploading() const {
|
|
|
|
return _media && _media->uploading();
|
2017-12-05 16:14:28 +00:00
|
|
|
}
|
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
void HistoryMessage::createComponents(const CreateConfig &config) {
|
|
|
|
uint64 mask = 0;
|
|
|
|
if (config.replyTo) {
|
|
|
|
mask |= HistoryMessageReply::Bit();
|
|
|
|
}
|
|
|
|
if (config.viaBotId) {
|
|
|
|
mask |= HistoryMessageVia::Bit();
|
|
|
|
}
|
|
|
|
if (config.viewsCount >= 0) {
|
|
|
|
mask |= HistoryMessageViews::Bit();
|
|
|
|
}
|
2017-07-12 19:14:20 +00:00
|
|
|
if (!config.author.isEmpty()) {
|
2016-09-27 14:20:49 +00:00
|
|
|
mask |= HistoryMessageSigned::Bit();
|
|
|
|
}
|
2016-10-07 07:58:34 +00:00
|
|
|
auto hasViaBot = (config.viaBotId != 0);
|
|
|
|
auto hasInlineMarkup = [&config] {
|
|
|
|
if (config.mtpMarkup) {
|
|
|
|
return (config.mtpMarkup->type() == mtpc_replyInlineMarkup);
|
|
|
|
}
|
|
|
|
return (config.inlineMarkup != nullptr);
|
|
|
|
};
|
2017-12-16 16:32:10 +00:00
|
|
|
if (!config.editDate.isNull()) {
|
2016-09-27 14:20:49 +00:00
|
|
|
mask |= HistoryMessageEdited::Bit();
|
|
|
|
}
|
2017-07-26 06:05:06 +00:00
|
|
|
if (config.senderOriginal) {
|
2016-09-27 14:20:49 +00:00
|
|
|
mask |= HistoryMessageForwarded::Bit();
|
|
|
|
}
|
2016-10-07 07:58:34 +00:00
|
|
|
if (config.mtpMarkup) {
|
2016-09-27 14:20:49 +00:00
|
|
|
// optimization: don't create markup component for the case
|
|
|
|
// MTPDreplyKeyboardHide with flags = 0, assume it has f_zero flag
|
2016-10-07 07:58:34 +00:00
|
|
|
if (config.mtpMarkup->type() != mtpc_replyKeyboardHide || config.mtpMarkup->c_replyKeyboardHide().vflags.v != 0) {
|
2016-09-27 14:20:49 +00:00
|
|
|
mask |= HistoryMessageReplyMarkup::Bit();
|
|
|
|
}
|
2016-10-07 07:58:34 +00:00
|
|
|
} else if (config.inlineMarkup) {
|
|
|
|
mask |= HistoryMessageReplyMarkup::Bit();
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2016-10-07 07:58:34 +00:00
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
UpdateComponents(mask);
|
2016-10-07 07:58:34 +00:00
|
|
|
|
2017-12-13 18:10:48 +00:00
|
|
|
if (const auto reply = Get<HistoryMessageReply>()) {
|
2016-09-27 14:20:49 +00:00
|
|
|
reply->replyToMsgId = config.replyTo;
|
2017-08-04 14:54:32 +00:00
|
|
|
if (!reply->updateData(this)) {
|
2017-11-23 15:41:13 +00:00
|
|
|
Auth().api().requestMessageData(
|
|
|
|
history()->peer->asChannel(),
|
|
|
|
reply->replyToMsgId,
|
|
|
|
HistoryDependentItemCallback(fullId()));
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
}
|
2017-12-13 18:10:48 +00:00
|
|
|
if (const auto via = Get<HistoryMessageVia>()) {
|
2016-09-27 14:20:49 +00:00
|
|
|
via->create(config.viaBotId);
|
|
|
|
}
|
2017-12-13 18:10:48 +00:00
|
|
|
if (const auto views = Get<HistoryMessageViews>()) {
|
2016-09-27 14:20:49 +00:00
|
|
|
views->_views = config.viewsCount;
|
|
|
|
}
|
2017-12-13 18:10:48 +00:00
|
|
|
if (const auto edited = Get<HistoryMessageEdited>()) {
|
2017-12-16 16:32:10 +00:00
|
|
|
edited->date = config.editDate;
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2017-12-18 15:44:50 +00:00
|
|
|
if (const auto msgsigned = Get<HistoryMessageSigned>()) {
|
|
|
|
msgsigned->author = config.author;
|
|
|
|
}
|
2017-12-13 18:10:48 +00:00
|
|
|
if (const auto forwarded = Get<HistoryMessageForwarded>()) {
|
2017-12-18 15:44:50 +00:00
|
|
|
forwarded->originalDate = config.originalDate;
|
|
|
|
forwarded->originalSender = App::peer(config.senderOriginal);
|
|
|
|
forwarded->originalId = config.originalId;
|
|
|
|
forwarded->originalAuthor = config.authorOriginal;
|
|
|
|
forwarded->savedFromPeer = App::peerLoaded(config.savedFromPeer);
|
|
|
|
forwarded->savedFromMsgId = config.savedFromMsgId;
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2017-12-13 18:10:48 +00:00
|
|
|
if (const auto markup = Get<HistoryMessageReplyMarkup>()) {
|
2016-10-07 07:58:34 +00:00
|
|
|
if (config.mtpMarkup) {
|
|
|
|
markup->create(*config.mtpMarkup);
|
|
|
|
} else if (config.inlineMarkup) {
|
|
|
|
markup->create(*config.inlineMarkup);
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
if (markup->flags & MTPDreplyKeyboardMarkup_ClientFlag::f_has_switch_inline_button) {
|
|
|
|
_flags |= MTPDmessage_ClientFlag::f_has_switch_inline_button;
|
|
|
|
}
|
|
|
|
}
|
2017-12-05 16:14:28 +00:00
|
|
|
_fromNameVersion = displayFrom()->nameVersion;
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
2018-01-14 16:02:25 +00:00
|
|
|
QString FormatViewsCount(int views) {
|
2016-09-27 14:20:49 +00:00
|
|
|
if (views > 999999) {
|
|
|
|
views /= 100000;
|
|
|
|
if (views % 10) {
|
|
|
|
return QString::number(views / 10) + '.' + QString::number(views % 10) + 'M';
|
|
|
|
}
|
|
|
|
return QString::number(views / 10) + 'M';
|
|
|
|
} else if (views > 9999) {
|
|
|
|
views /= 100;
|
|
|
|
if (views % 10) {
|
|
|
|
return QString::number(views / 10) + '.' + QString::number(views % 10) + 'K';
|
|
|
|
}
|
|
|
|
return QString::number(views / 10) + 'K';
|
|
|
|
} else if (views > 0) {
|
|
|
|
return QString::number(views);
|
|
|
|
}
|
|
|
|
return qsl("1");
|
|
|
|
}
|
|
|
|
|
2016-09-28 11:38:35 +00:00
|
|
|
void HistoryMessage::initMedia(const MTPMessageMedia *media) {
|
2018-01-14 16:02:25 +00:00
|
|
|
_media = nullptr;
|
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
switch (media ? media->type() : mtpc_messageMediaEmpty) {
|
|
|
|
case mtpc_messageMediaContact: {
|
|
|
|
auto &d = media->c_messageMediaContact();
|
2018-01-14 16:02:25 +00:00
|
|
|
_media = std::make_unique<Data::MediaContact>(this, d.vuser_id.v, qs(d.vfirst_name), qs(d.vlast_name), qs(d.vphone_number));
|
2016-09-27 14:20:49 +00:00
|
|
|
} break;
|
|
|
|
case mtpc_messageMediaGeo: {
|
|
|
|
auto &point = media->c_messageMediaGeo().vgeo;
|
|
|
|
if (point.type() == mtpc_geoPoint) {
|
2018-01-14 16:02:25 +00:00
|
|
|
_media = std::make_unique<Data::MediaLocation>(this, LocationCoords(point.c_geoPoint()));
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
} break;
|
2017-11-20 19:54:05 +00:00
|
|
|
case mtpc_messageMediaGeoLive: {
|
|
|
|
auto &point = media->c_messageMediaGeoLive().vgeo;
|
|
|
|
if (point.type() == mtpc_geoPoint) {
|
2018-01-14 16:02:25 +00:00
|
|
|
_media = std::make_unique<Data::MediaLocation>(this, LocationCoords(point.c_geoPoint()));
|
2017-11-20 19:54:05 +00:00
|
|
|
}
|
|
|
|
} break;
|
2016-09-27 14:20:49 +00:00
|
|
|
case mtpc_messageMediaVenue: {
|
|
|
|
auto &d = media->c_messageMediaVenue();
|
|
|
|
if (d.vgeo.type() == mtpc_geoPoint) {
|
2018-01-14 16:02:25 +00:00
|
|
|
_media = std::make_unique<Data::MediaLocation>(this, LocationCoords(d.vgeo.c_geoPoint()), qs(d.vtitle), qs(d.vaddress));
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case mtpc_messageMediaPhoto: {
|
|
|
|
auto &photo = media->c_messageMediaPhoto();
|
2017-07-12 19:14:20 +00:00
|
|
|
if (photo.has_ttl_seconds()) {
|
|
|
|
LOG(("App Error: Unexpected MTPMessageMediaPhoto with ttl_seconds in HistoryMessage."));
|
|
|
|
} else if (photo.has_photo() && photo.vphoto.type() == mtpc_photo) {
|
2018-01-14 16:02:25 +00:00
|
|
|
_media = std::make_unique<Data::MediaPhoto>(
|
|
|
|
this,
|
|
|
|
App::feedPhoto(photo.vphoto.c_photo()),
|
|
|
|
photo.has_caption() ? qs(photo.vcaption) : QString());
|
2017-07-12 19:14:20 +00:00
|
|
|
} else {
|
|
|
|
LOG(("API Error: Got MTPMessageMediaPhoto without photo and without ttl_seconds."));
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case mtpc_messageMediaDocument: {
|
2017-07-12 19:14:20 +00:00
|
|
|
auto &document = media->c_messageMediaDocument();
|
|
|
|
if (document.has_ttl_seconds()) {
|
|
|
|
LOG(("App Error: Unexpected MTPMessageMediaDocument with ttl_seconds in HistoryMessage."));
|
|
|
|
} else if (document.has_document() && document.vdocument.type() == mtpc_document) {
|
2018-01-14 16:02:25 +00:00
|
|
|
_media = std::make_unique<Data::MediaFile>(
|
|
|
|
this,
|
|
|
|
App::feedDocument(document.vdocument.c_document()),
|
|
|
|
document.has_caption() ? qs(document.vcaption) : QString());
|
2017-07-12 19:14:20 +00:00
|
|
|
} else {
|
|
|
|
LOG(("API Error: Got MTPMessageMediaDocument without document and without ttl_seconds."));
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case mtpc_messageMediaWebPage: {
|
|
|
|
auto &d = media->c_messageMediaWebPage().vwebpage;
|
|
|
|
switch (d.type()) {
|
|
|
|
case mtpc_webPageEmpty: break;
|
|
|
|
case mtpc_webPagePending: {
|
2018-01-14 16:02:25 +00:00
|
|
|
_media = std::make_unique<Data::MediaWebPage>(this, App::feedWebPage(d.c_webPagePending()));
|
2016-09-27 14:20:49 +00:00
|
|
|
} break;
|
|
|
|
case mtpc_webPage: {
|
2018-01-14 16:02:25 +00:00
|
|
|
_media = std::make_unique<Data::MediaWebPage>(this, App::feedWebPage(d.c_webPage()));
|
2016-09-27 14:20:49 +00:00
|
|
|
} break;
|
2016-12-13 07:59:57 +00:00
|
|
|
case mtpc_webPageNotModified: LOG(("API Error: webPageNotModified is unexpected in message media.")); break;
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case mtpc_messageMediaGame: {
|
|
|
|
auto &d = media->c_messageMediaGame().vgame;
|
|
|
|
if (d.type() == mtpc_game) {
|
2018-01-14 16:02:25 +00:00
|
|
|
_media = std::make_unique<Data::MediaGame>(this, App::feedGame(d.c_game()));
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
} break;
|
2017-03-05 13:39:10 +00:00
|
|
|
case mtpc_messageMediaInvoice: {
|
2018-01-14 16:02:25 +00:00
|
|
|
_media = std::make_unique<Data::MediaInvoice>(this, media->c_messageMediaInvoice());
|
|
|
|
if (_media->invoice()->receiptMsgId) {
|
2017-03-06 15:00:59 +00:00
|
|
|
replaceBuyWithReceiptInMarkup();
|
|
|
|
}
|
2017-03-05 13:39:10 +00:00
|
|
|
} break;
|
2016-09-27 14:20:49 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-03-06 15:00:59 +00:00
|
|
|
void HistoryMessage::replaceBuyWithReceiptInMarkup() {
|
|
|
|
if (auto markup = inlineReplyMarkup()) {
|
|
|
|
for (auto &row : markup->rows) {
|
|
|
|
for (auto &button : row) {
|
2017-12-18 15:44:50 +00:00
|
|
|
if (button.type == HistoryMessageMarkupButton::Type::Buy) {
|
2017-03-06 15:00:59 +00:00
|
|
|
button.text = lang(lng_payments_receipt_button);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
int32 HistoryMessage::plainMaxWidth() const {
|
|
|
|
return st::msgPadding.left() + _text.maxWidth() + st::msgPadding.right();
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::applyEdition(const MTPDmessage &message) {
|
|
|
|
int keyboardTop = -1;
|
2018-01-13 12:45:11 +00:00
|
|
|
//if (!pendingResize()) {// #TODO edit bot message
|
|
|
|
// if (auto keyboard = inlineReplyKeyboard()) {
|
|
|
|
// int h = st::msgBotKbButton.margin + keyboard->naturalHeight();
|
|
|
|
// keyboardTop = _height - h + st::msgBotKbButton.margin - marginBottom();
|
|
|
|
// }
|
|
|
|
//}
|
2016-09-27 14:20:49 +00:00
|
|
|
|
|
|
|
if (message.has_edit_date()) {
|
|
|
|
_flags |= MTPDmessage::Flag::f_edit_date;
|
2017-12-16 16:32:10 +00:00
|
|
|
if (!Has<HistoryMessageEdited>()) {
|
|
|
|
AddComponents(HistoryMessageEdited::Bit());
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2017-12-16 16:32:10 +00:00
|
|
|
auto edited = Get<HistoryMessageEdited>();
|
|
|
|
edited->date = ::date(message.vedit_date);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TextWithEntities textWithEntities = { qs(message.vmessage), EntitiesInText() };
|
|
|
|
if (message.has_entities()) {
|
2017-07-06 11:37:42 +00:00
|
|
|
textWithEntities.entities = TextUtilities::EntitiesFromMTP(message.ventities.v);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
setText(textWithEntities);
|
|
|
|
setReplyMarkup(message.has_reply_markup() ? (&message.vreply_markup) : nullptr);
|
2018-01-14 16:02:25 +00:00
|
|
|
initMedia(message.has_media() ? (&message.vmedia) : nullptr);
|
2016-09-27 14:20:49 +00:00
|
|
|
setViewsCount(message.has_views() ? message.vviews.v : -1);
|
|
|
|
|
|
|
|
finishEdition(keyboardTop);
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::applyEdition(const MTPDmessageService &message) {
|
|
|
|
if (message.vaction.type() == mtpc_messageActionHistoryClear) {
|
|
|
|
applyEditionToEmpty();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::applyEditionToEmpty() {
|
|
|
|
setEmptyText();
|
2018-01-14 16:02:25 +00:00
|
|
|
initMedia(nullptr);
|
2016-09-27 14:20:49 +00:00
|
|
|
setReplyMarkup(nullptr);
|
|
|
|
setViewsCount(-1);
|
|
|
|
|
|
|
|
finishEditionToEmpty();
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::updateMedia(const MTPMessageMedia *media) {
|
|
|
|
if (_flags & MTPDmessage_ClientFlag::f_from_inline_bot) {
|
2018-01-14 16:02:25 +00:00
|
|
|
if (!media || !_media || !_media->updateInlineResultMedia(*media)) {
|
|
|
|
initMedia(media);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
_flags &= ~MTPDmessage_ClientFlag::f_from_inline_bot;
|
|
|
|
} else {
|
2018-01-14 16:02:25 +00:00
|
|
|
if (!media || !_media || !_media->updateSentMedia(*media)) {
|
|
|
|
initMedia(media);
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
2018-01-13 12:45:11 +00:00
|
|
|
Auth().data().requestItemViewResize(this);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
2017-12-21 12:11:33 +00:00
|
|
|
void HistoryMessage::addToUnreadMentions(UnreadMentionType type) {
|
|
|
|
if (IsServerMsgId(id) && mentionsMe() && isMediaUnread()) {
|
|
|
|
if (history()->addToUnreadMentions(id, type)) {
|
2017-12-08 18:27:28 +00:00
|
|
|
Notify::peerUpdatedDelayed(
|
|
|
|
history()->peer,
|
|
|
|
Notify::PeerUpdate::Flag::UnreadMentionsChanged);
|
2017-08-11 07:16:07 +00:00
|
|
|
}
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
2017-12-08 18:27:28 +00:00
|
|
|
void HistoryMessage::eraseFromUnreadMentions() {
|
2017-08-11 07:16:07 +00:00
|
|
|
if (mentionsMe() && isMediaUnread()) {
|
|
|
|
history()->eraseFromUnreadMentions(id);
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
2017-09-04 11:40:02 +00:00
|
|
|
Storage::SharedMediaTypesMask HistoryMessage::sharedMediaTypes() const {
|
|
|
|
auto result = Storage::SharedMediaTypesMask {};
|
2018-01-14 16:02:25 +00:00
|
|
|
if (const auto media = this->media()) {
|
2017-09-04 11:40:02 +00:00
|
|
|
result.set(media->sharedMediaTypes());
|
|
|
|
}
|
|
|
|
if (hasTextLinks()) {
|
|
|
|
result.set(Storage::SharedMediaType::Link);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-01-14 16:02:25 +00:00
|
|
|
//void HistoryMessage::setMedia(const MTPMessageMedia *media) {
|
2016-09-27 14:20:49 +00:00
|
|
|
|
2018-01-14 16:02:25 +00:00
|
|
|
// #TODO grouping and games
|
|
|
|
//bool mediaRemovedSkipBlock = false;
|
|
|
|
//if (_media) {
|
|
|
|
// // Don't update Game media because we loose the consumed text of the message.
|
|
|
|
// if (_media->type() == MediaTypeGame) {
|
|
|
|
// return;
|
|
|
|
// }
|
2016-09-29 16:15:44 +00:00
|
|
|
|
2018-01-14 16:02:25 +00:00
|
|
|
// mediaRemovedSkipBlock = _media->isDisplayed() && _media->isBubbleBottom();
|
|
|
|
// _media.reset();
|
|
|
|
//}
|
|
|
|
//initMedia(media);
|
|
|
|
//auto mediaDisplayed = _media && _media->isDisplayed();
|
|
|
|
//if (mediaDisplayed && _media->isBubbleBottom() && !mediaRemovedSkipBlock) {
|
|
|
|
// _text.removeSkipBlock();
|
|
|
|
// _textWidth = -1;
|
|
|
|
// _textHeight = 0;
|
|
|
|
//} else if (mediaRemovedSkipBlock && (!mediaDisplayed || !_media->isBubbleBottom())) {
|
|
|
|
// _text.updateSkipBlock(skipBlockWidth(), skipBlockHeight());
|
|
|
|
// _textWidth = -1;
|
|
|
|
// _textHeight = 0;
|
|
|
|
//}
|
|
|
|
//_history->recountGroupingAround(this);
|
|
|
|
//}
|
2016-09-27 14:20:49 +00:00
|
|
|
|
|
|
|
void HistoryMessage::setText(const TextWithEntities &textWithEntities) {
|
|
|
|
for_const (auto &entity, textWithEntities.entities) {
|
|
|
|
auto type = entity.type();
|
|
|
|
if (type == EntityInTextUrl || type == EntityInTextCustomUrl || type == EntityInTextEmail) {
|
|
|
|
_flags |= MTPDmessage_ClientFlag::f_has_text_links;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-09-28 11:38:35 +00:00
|
|
|
|
2018-01-14 16:02:25 +00:00
|
|
|
if (_media && _media->consumeMessageText(textWithEntities)) {
|
2016-09-28 11:38:35 +00:00
|
|
|
setEmptyText();
|
|
|
|
} else {
|
2018-01-14 16:02:25 +00:00
|
|
|
_text.setMarkedText(
|
|
|
|
st::messageTextStyle,
|
|
|
|
textWithEntities,
|
|
|
|
Ui::ItemTextOptions(this));
|
2016-09-28 11:38:35 +00:00
|
|
|
_textWidth = -1;
|
|
|
|
_textHeight = 0;
|
|
|
|
}
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::setEmptyText() {
|
2017-12-28 13:06:06 +00:00
|
|
|
_text.setMarkedText(
|
|
|
|
st::messageTextStyle,
|
|
|
|
{ QString(), EntitiesInText() },
|
|
|
|
Ui::ItemTextOptions(this));
|
2016-09-27 14:20:49 +00:00
|
|
|
|
|
|
|
_textWidth = -1;
|
|
|
|
_textHeight = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::setReplyMarkup(const MTPReplyMarkup *markup) {
|
|
|
|
if (!markup) {
|
|
|
|
if (_flags & MTPDmessage::Flag::f_reply_markup) {
|
|
|
|
_flags &= ~MTPDmessage::Flag::f_reply_markup;
|
|
|
|
if (Has<HistoryMessageReplyMarkup>()) {
|
|
|
|
RemoveComponents(HistoryMessageReplyMarkup::Bit());
|
|
|
|
}
|
2018-01-13 12:45:11 +00:00
|
|
|
Auth().data().requestItemViewResize(this);
|
2016-09-27 14:20:49 +00:00
|
|
|
Notify::replyMarkupUpdated(this);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// optimization: don't create markup component for the case
|
|
|
|
// MTPDreplyKeyboardHide with flags = 0, assume it has f_zero flag
|
|
|
|
if (markup->type() == mtpc_replyKeyboardHide && markup->c_replyKeyboardHide().vflags.v == 0) {
|
|
|
|
bool changed = false;
|
|
|
|
if (Has<HistoryMessageReplyMarkup>()) {
|
|
|
|
RemoveComponents(HistoryMessageReplyMarkup::Bit());
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
if (!(_flags & MTPDmessage::Flag::f_reply_markup)) {
|
|
|
|
_flags |= MTPDmessage::Flag::f_reply_markup;
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
if (changed) {
|
2018-01-13 12:45:11 +00:00
|
|
|
Auth().data().requestItemViewResize(this);
|
2016-09-27 14:20:49 +00:00
|
|
|
Notify::replyMarkupUpdated(this);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!(_flags & MTPDmessage::Flag::f_reply_markup)) {
|
|
|
|
_flags |= MTPDmessage::Flag::f_reply_markup;
|
|
|
|
}
|
|
|
|
if (!Has<HistoryMessageReplyMarkup>()) {
|
|
|
|
AddComponents(HistoryMessageReplyMarkup::Bit());
|
|
|
|
}
|
|
|
|
Get<HistoryMessageReplyMarkup>()->create(*markup);
|
2018-01-13 12:45:11 +00:00
|
|
|
Auth().data().requestItemViewResize(this);
|
2016-09-27 14:20:49 +00:00
|
|
|
Notify::replyMarkupUpdated(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TextWithEntities HistoryMessage::originalText() const {
|
|
|
|
if (emptyText()) {
|
|
|
|
return { QString(), EntitiesInText() };
|
|
|
|
}
|
|
|
|
return _text.originalTextWithEntities();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HistoryMessage::textHasLinks() const {
|
|
|
|
return emptyText() ? false : _text.hasLinks();
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::setViewsCount(int32 count) {
|
|
|
|
auto views = Get<HistoryMessageViews>();
|
|
|
|
if (!views || views->_views == count || (count >= 0 && views->_views > count)) return;
|
|
|
|
|
|
|
|
int32 was = views->_viewsWidth;
|
|
|
|
views->_views = count;
|
2018-01-14 16:02:25 +00:00
|
|
|
views->_viewsText = (views->_views >= 0) ? FormatViewsCount(views->_views) : QString();
|
2016-09-27 14:20:49 +00:00
|
|
|
views->_viewsWidth = views->_viewsText.isEmpty() ? 0 : st::msgDateFont->width(views->_viewsText);
|
|
|
|
if (was == views->_viewsWidth) {
|
2017-10-05 15:35:52 +00:00
|
|
|
Auth().data().requestItemRepaint(this);
|
2016-09-27 14:20:49 +00:00
|
|
|
} else {
|
2018-01-13 12:45:11 +00:00
|
|
|
Auth().data().requestItemViewResize(this);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-13 12:45:11 +00:00
|
|
|
void HistoryMessage::setRealId(MsgId newId) {
|
|
|
|
HistoryItem::setRealId(newId);
|
|
|
|
Auth().data().requestItemViewResize(this);
|
2016-09-27 14:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryMessage::dependencyItemRemoved(HistoryItem *dependency) {
|
|
|
|
if (auto reply = Get<HistoryMessageReply>()) {
|
|
|
|
reply->itemRemoved(this, dependency);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QString HistoryMessage::notificationHeader() const {
|
|
|
|
return (!_history->peer->isUser() && !isPost()) ? from()->name : QString();
|
|
|
|
}
|
|
|
|
|
2018-01-11 19:33:26 +00:00
|
|
|
std::unique_ptr<HistoryView::Element> HistoryMessage::createView(
|
|
|
|
not_null<Window::Controller*> controller,
|
|
|
|
HistoryView::Context context) {
|
|
|
|
return controller->createMessageView(this, context);
|
|
|
|
}
|
|
|
|
|
2016-09-27 14:20:49 +00:00
|
|
|
HistoryMessage::~HistoryMessage() {
|
2017-03-05 13:39:10 +00:00
|
|
|
_media.reset();
|
2016-09-27 14:20:49 +00:00
|
|
|
if (auto reply = Get<HistoryMessageReply>()) {
|
|
|
|
reply->clearData(this);
|
|
|
|
}
|
|
|
|
}
|