AyuGramDesktop/Telegram/SourceFiles/history/history_service.cpp
John Preston 8b69e6ab99 Rename some methods in DocumentData.
Also fix voice message mark as read when autoplaying after previous.
Also show play icon and don't show playlist for audio files that do
not have shared music files attributes but have audio file mime type.
2017-12-10 14:28:04 +04:00

802 lines
27 KiB
C++

/*
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
*/
#include "history/history_service.h"
#include "lang/lang_keys.h"
#include "mainwidget.h"
#include "apiwrap.h"
#include "history/history_service_layout.h"
#include "history/history_media_types.h"
#include "history/history_message.h"
#include "auth_session.h"
#include "window/notifications_manager.h"
#include "storage/storage_shared_media.h"
namespace {
constexpr auto kPinnedMessageTextLimit = 16;
} // namespace
TextParseOptions _historySrvOptions = {
TextParseLinks | TextParseMentions | TextParseHashtags/* | TextParseMultiline*/ | TextParseRichText, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // lang-dependent
};
void HistoryService::setMessageByAction(const MTPmessageAction &action) {
auto prepareChatAddUserText = [this](const MTPDmessageActionChatAddUser &action) {
auto result = PreparedText {};
auto &users = action.vusers.v;
if (users.size() == 1) {
auto u = App::user(peerFromUser(users[0]));
if (u == _from) {
result.links.push_back(fromLink());
result.text = lng_action_user_joined(lt_from, fromLinkText());
} else {
result.links.push_back(fromLink());
result.links.push_back(u->createOpenLink());
result.text = lng_action_add_user(lt_from, fromLinkText(), lt_user, textcmdLink(2, u->name));
}
} else if (users.isEmpty()) {
result.links.push_back(fromLink());
result.text = lng_action_add_user(lt_from, fromLinkText(), lt_user, "somebody");
} else {
result.links.push_back(fromLink());
for (auto i = 0, l = users.size(); i != l; ++i) {
auto user = App::user(peerFromUser(users[i]));
result.links.push_back(user->createOpenLink());
auto linkText = textcmdLink(i + 2, user->name);
if (i == 0) {
result.text = linkText;
} else if (i + 1 == l) {
result.text = lng_action_add_users_and_last(lt_accumulated, result.text, lt_user, linkText);
} else {
result.text = lng_action_add_users_and_one(lt_accumulated, result.text, lt_user, linkText);
}
}
result.text = lng_action_add_users_many(lt_from, fromLinkText(), lt_users, result.text);
}
return result;
};
auto prepareChatJoinedByLink = [this](const MTPDmessageActionChatJoinedByLink &action) {
auto result = PreparedText {};
result.links.push_back(fromLink());
result.text = lng_action_user_joined_by_link(lt_from, fromLinkText());
return result;
};
auto prepareChatCreate = [this](const MTPDmessageActionChatCreate &action) {
auto result = PreparedText {};
result.links.push_back(fromLink());
result.text = lng_action_created_chat(lt_from, fromLinkText(), lt_title, TextUtilities::Clean(qs(action.vtitle)));
return result;
};
auto prepareChannelCreate = [this](const MTPDmessageActionChannelCreate &action) {
auto result = PreparedText {};
if (isPost()) {
result.text = lang(lng_action_created_channel);
} else {
result.links.push_back(fromLink());
result.text = lng_action_created_chat(lt_from, fromLinkText(), lt_title, TextUtilities::Clean(qs(action.vtitle)));
}
return result;
};
auto prepareChatDeletePhoto = [this] {
auto result = PreparedText {};
if (isPost()) {
result.text = lang(lng_action_removed_photo_channel);
} else {
result.links.push_back(fromLink());
result.text = lng_action_removed_photo(lt_from, fromLinkText());
}
return result;
};
auto prepareChatDeleteUser = [this](const MTPDmessageActionChatDeleteUser &action) {
auto result = PreparedText {};
if (peerFromUser(action.vuser_id) == _from->id) {
result.links.push_back(fromLink());
result.text = lng_action_user_left(lt_from, fromLinkText());
} else {
auto user = App::user(peerFromUser(action.vuser_id));
result.links.push_back(fromLink());
result.links.push_back(user->createOpenLink());
result.text = lng_action_kick_user(lt_from, fromLinkText(), lt_user, textcmdLink(2, user->name));
}
return result;
};
auto prepareChatEditPhoto = [this](const MTPDmessageActionChatEditPhoto &action) {
auto result = PreparedText {};
if (isPost()) {
result.text = lang(lng_action_changed_photo_channel);
} else {
result.links.push_back(fromLink());
result.text = lng_action_changed_photo(lt_from, fromLinkText());
}
return result;
};
auto prepareChatEditTitle = [this](const MTPDmessageActionChatEditTitle &action) {
auto result = PreparedText {};
if (isPost()) {
result.text = lng_action_changed_title_channel(lt_title, TextUtilities::Clean(qs(action.vtitle)));
} else {
result.links.push_back(fromLink());
result.text = lng_action_changed_title(lt_from, fromLinkText(), lt_title, TextUtilities::Clean(qs(action.vtitle)));
}
return result;
};
auto prepareScreenshotTaken = [this] {
auto result = PreparedText {};
if (out()) {
result.text = lang(lng_action_you_took_screenshot);
} else {
result.links.push_back(fromLink());
result.text = lng_action_took_screenshot(lt_from, fromLinkText());
}
return result;
};
auto prepareCustomAction = [&](const MTPDmessageActionCustomAction &action) {
auto result = PreparedText {};
result.text = qs(action.vmessage);
return result;
};
auto messageText = PreparedText {};
switch (action.type()) {
case mtpc_messageActionChatAddUser: messageText = prepareChatAddUserText(action.c_messageActionChatAddUser()); break;
case mtpc_messageActionChatJoinedByLink: messageText = prepareChatJoinedByLink(action.c_messageActionChatJoinedByLink()); break;
case mtpc_messageActionChatCreate: messageText = prepareChatCreate(action.c_messageActionChatCreate()); break;
case mtpc_messageActionChannelCreate: messageText = prepareChannelCreate(action.c_messageActionChannelCreate()); break;
case mtpc_messageActionHistoryClear: break; // Leave empty text.
case mtpc_messageActionChatDeletePhoto: messageText = prepareChatDeletePhoto(); break;
case mtpc_messageActionChatDeleteUser: messageText = prepareChatDeleteUser(action.c_messageActionChatDeleteUser()); break;
case mtpc_messageActionChatEditPhoto: messageText = prepareChatEditPhoto(action.c_messageActionChatEditPhoto()); break;
case mtpc_messageActionChatEditTitle: messageText = prepareChatEditTitle(action.c_messageActionChatEditTitle()); break;
case mtpc_messageActionChatMigrateTo: messageText.text = lang(lng_action_group_migrate); break;
case mtpc_messageActionChannelMigrateFrom: messageText.text = lang(lng_action_group_migrate); break;
case mtpc_messageActionPinMessage: messageText = preparePinnedText(); break;
case mtpc_messageActionGameScore: messageText = prepareGameScoreText(); break;
case mtpc_messageActionPhoneCall: Unexpected("PhoneCall type in HistoryService.");
case mtpc_messageActionPaymentSent: messageText = preparePaymentSentText(); break;
case mtpc_messageActionScreenshotTaken: messageText = prepareScreenshotTaken(); break;
case mtpc_messageActionCustomAction: messageText = prepareCustomAction(action.c_messageActionCustomAction()); break;
default: messageText.text = lang(lng_message_empty); break;
}
setServiceText(messageText);
// Additional information.
switch (action.type()) {
case mtpc_messageActionChatAddUser: {
if (auto channel = history()->peer->asMegagroup()) {
auto &users = action.c_messageActionChatAddUser().vusers;
for_const (auto &item, users.v) {
if (item.v == Auth().userId()) {
channel->mgInfo->joinedMessageFound = true;
break;
}
}
}
} break;
case mtpc_messageActionChatJoinedByLink: {
if (_from->isSelf() && history()->peer->isMegagroup()) {
history()->peer->asChannel()->mgInfo->joinedMessageFound = true;
}
} break;
case mtpc_messageActionChatEditPhoto: {
auto &photo = action.c_messageActionChatEditPhoto().vphoto;
if (photo.type() == mtpc_photo) {
_media = std::make_unique<HistoryPhoto>(this, history()->peer, photo.c_photo(), st::msgServicePhotoWidth);
}
} break;
case mtpc_messageActionChatMigrateTo:
case mtpc_messageActionChannelMigrateFrom: {
_flags |= MTPDmessage_ClientFlag::f_is_group_migrate;
} break;
}
}
void HistoryService::setSelfDestruct(HistoryServiceSelfDestruct::Type type, int ttlSeconds) {
UpdateComponents(HistoryServiceSelfDestruct::Bit());
auto selfdestruct = Get<HistoryServiceSelfDestruct>();
selfdestruct->timeToLive = ttlSeconds * 1000LL;
selfdestruct->type = type;
}
bool HistoryService::updateDependent(bool force) {
auto dependent = GetDependentData();
Assert(dependent != nullptr);
if (!force) {
if (!dependent->msgId || dependent->msg) {
return true;
}
}
if (!dependent->lnk) {
dependent->lnk = goToMessageClickHandler(history()->peer, dependent->msgId);
}
auto gotDependencyItem = false;
if (!dependent->msg) {
dependent->msg = App::histItemById(channelId(), dependent->msgId);
if (dependent->msg) {
if (dependent->msg->isEmpty()) {
// Really it is deleted.
dependent->msg = nullptr;
force = true;
} else {
App::historyRegDependency(this, dependent->msg);
gotDependencyItem = true;
}
}
}
if (dependent->msg) {
updateDependentText();
} else if (force) {
if (dependent->msgId > 0) {
dependent->msgId = 0;
gotDependencyItem = true;
}
updateDependentText();
}
if (force && gotDependencyItem) {
Auth().notifications().checkDelayed();
}
return (dependent->msg || !dependent->msgId);
}
HistoryService::PreparedText HistoryService::preparePinnedText() {
auto result = PreparedText {};
auto pinned = Get<HistoryServicePinned>();
if (pinned && pinned->msg) {
auto mediaText = ([pinned]() -> QString {
auto media = pinned->msg->getMedia();
switch (media ? media->type() : MediaTypeCount) {
case MediaTypePhoto: return lang(lng_action_pinned_media_photo);
case MediaTypeVideo: return lang(lng_action_pinned_media_video);
case MediaTypeContact: return lang(lng_action_pinned_media_contact);
case MediaTypeFile: return lang(lng_action_pinned_media_file);
case MediaTypeGif: {
if (auto document = media->getDocument()) {
if (document->isVideoMessage()) {
return lang(lng_action_pinned_media_video_message);
}
}
return lang(lng_action_pinned_media_gif);
} break;
case MediaTypeSticker: {
auto emoji = static_cast<HistorySticker*>(media)->emoji();
if (emoji.isEmpty()) {
return lang(lng_action_pinned_media_sticker);
}
return lng_action_pinned_media_emoji_sticker(lt_emoji, emoji);
} break;
case MediaTypeLocation: return lang(lng_action_pinned_media_location);
case MediaTypeMusicFile: return lang(lng_action_pinned_media_audio);
case MediaTypeVoiceFile: return lang(lng_action_pinned_media_voice);
case MediaTypeGame: {
auto title = static_cast<HistoryGame*>(media)->game()->title;
return lng_action_pinned_media_game(lt_game, title);
} break;
}
return QString();
})();
result.links.push_back(fromLink());
result.links.push_back(pinned->lnk);
if (mediaText.isEmpty()) {
auto original = pinned->msg->originalText().text;
auto cutAt = 0;
auto limit = kPinnedMessageTextLimit;
auto size = original.size();
for (; limit != 0;) {
--limit;
if (cutAt >= size) break;
if (original.at(cutAt).isLowSurrogate() && cutAt + 1 < size && original.at(cutAt + 1).isHighSurrogate()) {
cutAt += 2;
} else {
++cutAt;
}
}
if (!limit && cutAt + 5 < size) {
original = original.mid(0, cutAt) + qstr("...");
}
result.text = lng_action_pinned_message(lt_from, fromLinkText(), lt_text, textcmdLink(2, original));
} else {
result.text = lng_action_pinned_media(lt_from, fromLinkText(), lt_media, textcmdLink(2, mediaText));
}
} else if (pinned && pinned->msgId) {
result.links.push_back(fromLink());
result.links.push_back(pinned->lnk);
result.text = lng_action_pinned_media(lt_from, fromLinkText(), lt_media, textcmdLink(2, lang(lng_contacts_loading)));
} else {
result.links.push_back(fromLink());
result.text = lng_action_pinned_media(lt_from, fromLinkText(), lt_media, lang(lng_deleted_message));
}
return result;
}
HistoryService::PreparedText HistoryService::prepareGameScoreText() {
auto result = PreparedText {};
auto gamescore = Get<HistoryServiceGameScore>();
auto computeGameTitle = [gamescore, &result]() -> QString {
if (gamescore && gamescore->msg) {
if (auto media = gamescore->msg->getMedia()) {
if (media->type() == MediaTypeGame) {
result.links.push_back(MakeShared<ReplyMarkupClickHandler>(gamescore->msg, 0, 0));
auto titleText = static_cast<HistoryGame*>(media)->game()->title;
return textcmdLink(result.links.size(), titleText);
}
}
return lang(lng_deleted_message);
} else if (gamescore && gamescore->msgId) {
return lang(lng_contacts_loading);
}
return QString();
};
auto scoreNumber = gamescore ? gamescore->score : 0;
if (_from->isSelf()) {
auto gameTitle = computeGameTitle();
if (gameTitle.isEmpty()) {
result.text = lng_action_game_you_scored_no_game(lt_count, scoreNumber);
} else {
result.text = lng_action_game_you_scored(lt_count, scoreNumber, lt_game, gameTitle);
}
} else {
result.links.push_back(fromLink());
auto gameTitle = computeGameTitle();
if (gameTitle.isEmpty()) {
result.text = lng_action_game_score_no_game(lt_count, scoreNumber, lt_from, fromLinkText());
} else {
result.text = lng_action_game_score(lt_count, scoreNumber, lt_from, fromLinkText(), lt_game, gameTitle);
}
}
return result;
}
HistoryService::PreparedText HistoryService::preparePaymentSentText() {
auto result = PreparedText {};
auto payment = Get<HistoryServicePayment>();
auto invoiceTitle = ([payment]() -> QString {
if (payment && payment->msg) {
if (auto media = payment->msg->getMedia()) {
if (media->type() == MediaTypeInvoice) {
return static_cast<HistoryInvoice*>(media)->getTitle();
}
}
return lang(lng_deleted_message);
} else if (payment && payment->msgId) {
return lang(lng_contacts_loading);
}
return QString();
})();
if (invoiceTitle.isEmpty()) {
result.text = lng_action_payment_done(lt_amount, payment->amount, lt_user, history()->peer->name);
} else {
result.text = lng_action_payment_done_for(lt_amount, payment->amount, lt_user, history()->peer->name, lt_invoice, invoiceTitle);
}
return result;
}
HistoryService::HistoryService(not_null<History*> history, const MTPDmessage &message) :
HistoryItem(history, message.vid.v, message.vflags.v, ::date(message.vdate), message.has_from_id() ? message.vfrom_id.v : 0) {
createFromMtp(message);
}
HistoryService::HistoryService(not_null<History*> history, const MTPDmessageService &message) :
HistoryItem(history, message.vid.v, mtpCastFlags(message.vflags.v), ::date(message.vdate), message.has_from_id() ? message.vfrom_id.v : 0) {
createFromMtp(message);
}
HistoryService::HistoryService(not_null<History*> history, MsgId msgId, QDateTime date, const PreparedText &message, MTPDmessage::Flags flags, int32 from, PhotoData *photo) :
HistoryItem(history, msgId, flags, date, from) {
setServiceText(message);
if (photo) {
_media = std::make_unique<HistoryPhoto>(this, history->peer, photo, st::msgServicePhotoWidth);
}
}
void HistoryService::initDimensions() {
_maxw = _text.maxWidth() + st::msgServicePadding.left() + st::msgServicePadding.right();
_minh = _text.minHeight();
if (_media) {
_media->initDimensions();
}
}
bool HistoryService::updateDependencyItem() {
if (GetDependentData()) {
return updateDependent(true);
}
return HistoryItem::updateDependencyItem();
}
QRect HistoryService::countGeometry() const {
auto result = QRect(0, 0, width(), _height);
if (Adaptive::ChatWide()) {
result.setWidth(qMin(result.width(), st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left()));
}
return result.marginsRemoved(st::msgServiceMargin);
}
TextWithEntities HistoryService::selectedText(TextSelection selection) const {
return _text.originalTextWithEntities((selection == FullSelection) ? AllTextSelection : selection);
}
QString HistoryService::inDialogsText(DrawInDialog way) const {
return textcmdLink(1, TextUtilities::Clean(notificationText()));
}
QString HistoryService::inReplyText() const {
QString result = HistoryService::notificationText();
return result.trimmed().startsWith(author()->name) ? result.trimmed().mid(author()->name.size()).trimmed() : result;
}
void HistoryService::setServiceText(const PreparedText &prepared) {
_text.setText(st::serviceTextStyle, prepared.text, _historySrvOptions);
auto linkIndex = 0;
for_const (auto &link, prepared.links) {
// Link indices start with 1.
_text.setLink(++linkIndex, link);
}
setPendingInitDimensions();
_textWidth = -1;
_textHeight = 0;
}
void HistoryService::draw(Painter &p, QRect clip, TextSelection selection, TimeMs ms) const {
auto height = _height - st::msgServiceMargin.top() - st::msgServiceMargin.bottom();
auto dateh = 0;
auto unreadbarh = 0;
if (auto date = Get<HistoryMessageDate>()) {
dateh = date->height();
p.translate(0, dateh);
clip.translate(0, -dateh);
height -= dateh;
}
if (auto unreadbar = Get<HistoryMessageUnreadBar>()) {
unreadbarh = unreadbar->height();
if (clip.intersects(QRect(0, 0, width(), unreadbarh))) {
unreadbar->paint(p, 0, width());
}
p.translate(0, unreadbarh);
clip.translate(0, -unreadbarh);
height -= unreadbarh;
}
HistoryLayout::PaintContext context(ms, clip, selection);
HistoryLayout::ServiceMessagePainter::paint(p, this, context, height);
if (auto skiph = dateh + unreadbarh) {
p.translate(0, -skiph);
}
}
int HistoryService::resizeContentGetHeight() {
_height = displayedDateHeight();
if (auto unreadbar = Get<HistoryMessageUnreadBar>()) {
_height += unreadbar->height();
}
if (_text.isEmpty()) {
_textHeight = 0;
} else {
auto contentWidth = width();
if (Adaptive::ChatWide()) {
accumulate_min(contentWidth, st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left());
}
contentWidth -= st::msgServiceMargin.left() + st::msgServiceMargin.left(); // two small margins
if (contentWidth < st::msgServicePadding.left() + st::msgServicePadding.right() + 1) {
contentWidth = st::msgServicePadding.left() + st::msgServicePadding.right() + 1;
}
auto nwidth = qMax(contentWidth - st::msgServicePadding.left() - st::msgServicePadding.right(), 0);
if (nwidth != _textWidth) {
_textWidth = nwidth;
_textHeight = _text.countHeight(nwidth);
}
if (contentWidth >= _maxw) {
_height += _minh;
} else {
_height += _textHeight;
}
_height += st::msgServicePadding.top() + st::msgServicePadding.bottom() + st::msgServiceMargin.top() + st::msgServiceMargin.bottom();
if (_media) {
_height += st::msgServiceMargin.top() + _media->resizeGetHeight(_media->currentWidth());
}
}
return _height;
}
void HistoryService::markMediaAsReadHook() {
if (auto selfdestruct = Get<HistoryServiceSelfDestruct>()) {
if (!selfdestruct->destructAt) {
selfdestruct->destructAt = getms(true) + selfdestruct->timeToLive;
App::histories().selfDestructIn(this, selfdestruct->timeToLive);
}
}
}
TimeMs HistoryService::getSelfDestructIn(TimeMs now) {
if (auto selfdestruct = Get<HistoryServiceSelfDestruct>()) {
if (selfdestruct->destructAt > 0) {
if (selfdestruct->destructAt <= now) {
auto text = [selfdestruct] {
switch (selfdestruct->type) {
case HistoryServiceSelfDestruct::Type::Photo: return lang(lng_ttl_photo_expired);
case HistoryServiceSelfDestruct::Type::Video: return lang(lng_ttl_video_expired);
}
Unexpected("Type in HistoryServiceSelfDestruct::Type");
};
setServiceText({ text() });
return 0;
}
return selfdestruct->destructAt - now;
}
}
return 0;
}
bool HistoryService::hasPoint(QPoint point) const {
auto g = countGeometry();
if (g.width() < 1) {
return false;
}
if (auto dateh = displayedDateHeight()) {
g.setTop(g.top() + dateh);
}
if (auto unreadbar = Get<HistoryMessageUnreadBar>()) {
g.setTop(g.top() + unreadbar->height());
}
if (_media) {
g.setHeight(g.height() - (st::msgServiceMargin.top() + _media->height()));
}
return g.contains(point);
}
HistoryTextState HistoryService::getState(QPoint point, HistoryStateRequest request) const {
HistoryTextState result;
auto g = countGeometry();
if (g.width() < 1) {
return result;
}
if (auto dateh = displayedDateHeight()) {
point.setY(point.y() - dateh);
g.setHeight(g.height() - dateh);
}
if (auto unreadbar = Get<HistoryMessageUnreadBar>()) {
auto unreadbarh = unreadbar->height();
point.setY(point.y() - unreadbarh);
g.setHeight(g.height() - unreadbarh);
}
if (_media) {
g.setHeight(g.height() - (st::msgServiceMargin.top() + _media->height()));
}
auto trect = g.marginsAdded(-st::msgServicePadding);
if (trect.contains(point)) {
auto textRequest = request.forText();
textRequest.align = style::al_center;
result = _text.getState(point - trect.topLeft(), trect.width(), textRequest);
if (auto gamescore = Get<HistoryServiceGameScore>()) {
if (!result.link && result.cursor == HistoryInTextCursorState && g.contains(point)) {
result.link = gamescore->lnk;
}
} else if (auto payment = Get<HistoryServicePayment>()) {
if (!result.link && result.cursor == HistoryInTextCursorState && g.contains(point)) {
result.link = payment->lnk;
}
}
} else if (_media) {
result = _media->getState(point - QPoint(st::msgServiceMargin.left() + (g.width() - _media->maxWidth()) / 2, st::msgServiceMargin.top() + g.height() + st::msgServiceMargin.top()), request);
}
return result;
}
void HistoryService::createFromMtp(const MTPDmessage &message) {
auto mediaType = message.vmedia.type();
switch (mediaType) {
case mtpc_messageMediaPhoto: {
if (message.is_media_unread()) {
auto &photo = message.vmedia.c_messageMediaPhoto();
Assert(photo.has_ttl_seconds());
setSelfDestruct(HistoryServiceSelfDestruct::Type::Photo, photo.vttl_seconds.v);
if (out()) {
setServiceText({ lang(lng_ttl_photo_sent) });
} else {
auto result = PreparedText();
result.links.push_back(fromLink());
result.text = lng_ttl_photo_received(lt_from, fromLinkText());
setServiceText(std::move(result));
}
} else {
setServiceText({ lang(lng_ttl_photo_expired) });
}
} break;
case mtpc_messageMediaDocument: {
if (message.is_media_unread()) {
auto &document = message.vmedia.c_messageMediaDocument();
Assert(document.has_ttl_seconds());
setSelfDestruct(HistoryServiceSelfDestruct::Type::Video, document.vttl_seconds.v);
if (out()) {
setServiceText({ lang(lng_ttl_video_sent) });
} else {
auto result = PreparedText();
result.links.push_back(fromLink());
result.text = lng_ttl_video_received(lt_from, fromLinkText());
setServiceText(std::move(result));
}
} else {
setServiceText({ lang(lng_ttl_video_expired) });
}
} break;
default: Unexpected("Media type in HistoryService::createFromMtp()");
}
}
void HistoryService::createFromMtp(const MTPDmessageService &message) {
if (message.vaction.type() == mtpc_messageActionGameScore) {
UpdateComponents(HistoryServiceGameScore::Bit());
Get<HistoryServiceGameScore>()->score = message.vaction.c_messageActionGameScore().vscore.v;
} else if (message.vaction.type() == mtpc_messageActionPaymentSent) {
UpdateComponents(HistoryServicePayment::Bit());
auto amount = message.vaction.c_messageActionPaymentSent().vtotal_amount.v;
auto currency = qs(message.vaction.c_messageActionPaymentSent().vcurrency);
Get<HistoryServicePayment>()->amount = HistoryInvoice::fillAmountAndCurrency(amount, currency);
}
if (message.has_reply_to_msg_id()) {
if (message.vaction.type() == mtpc_messageActionPinMessage) {
UpdateComponents(HistoryServicePinned::Bit());
}
if (auto dependent = GetDependentData()) {
dependent->msgId = message.vreply_to_msg_id.v;
if (!updateDependent()) {
Auth().api().requestMessageData(
history()->peer->asChannel(),
dependent->msgId,
HistoryDependentItemCallback(fullId()));
}
}
}
setMessageByAction(message.vaction);
}
void HistoryService::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
if (_media) _media->clickHandlerActiveChanged(p, active);
HistoryItem::clickHandlerActiveChanged(p, active);
}
void HistoryService::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) {
if (_media) _media->clickHandlerPressedChanged(p, pressed);
HistoryItem::clickHandlerPressedChanged(p, pressed);
}
void HistoryService::applyEdition(const MTPDmessageService &message) {
clearDependency();
UpdateComponents(0);
createFromMtp(message);
if (message.vaction.type() == mtpc_messageActionHistoryClear) {
removeMedia();
finishEditionToEmpty();
} else {
finishEdition(-1);
}
}
void HistoryService::removeMedia() {
if (!_media) return;
bool mediaWasDisplayed = _media->isDisplayed();
_media.reset();
if (mediaWasDisplayed) {
_textWidth = -1;
_textHeight = 0;
}
}
Storage::SharedMediaTypesMask HistoryService::sharedMediaTypes() const {
if (auto media = getMedia()) {
return media->sharedMediaTypes();
}
return {};
}
void HistoryService::updateDependentText() {
auto text = PreparedText {};
if (Has<HistoryServicePinned>()) {
text = preparePinnedText();
} else if (Has<HistoryServiceGameScore>()) {
text = prepareGameScoreText();
} else if (Has<HistoryServicePayment>()) {
text = preparePaymentSentText();
} else {
return;
}
setServiceText(text);
if (history()->textCachedFor == this) {
history()->textCachedFor = nullptr;
}
if (App::main()) {
App::main()->dlgUpdated(history()->peer, id);
}
App::historyUpdateDependent(this);
}
void HistoryService::clearDependency() {
if (auto dependent = GetDependentData()) {
if (dependent->msg) {
App::historyUnregDependency(this, dependent->msg);
}
}
}
HistoryService::~HistoryService() {
clearDependency();
_media.reset();
}
HistoryJoined::HistoryJoined(not_null<History*> history, const QDateTime &inviteDate, not_null<UserData*> inviter, MTPDmessage::Flags flags)
: HistoryService(history, clientMsgId(), inviteDate, GenerateText(history, inviter), flags) {
}
HistoryJoined::PreparedText HistoryJoined::GenerateText(not_null<History*> history, not_null<UserData*> inviter) {
if (inviter->id == Auth().userPeerId()) {
if (history->isMegagroup()) {
auto self = App::user(Auth().userPeerId());
auto result = PreparedText {};
result.links.push_back(self->createOpenLink());
result.text = lng_action_user_joined(lt_from, textcmdLink(1, self->name));
return result;
}
return { lang(lng_action_you_joined) };
}
auto result = PreparedText {};
result.links.push_back(inviter->createOpenLink());
result.text = (history->isMegagroup() ? lng_action_add_you_group : lng_action_add_you)(lt_from, textcmdLink(1, inviter->name));
return result;
}