Add support for imported messages.

This commit is contained in:
John Preston 2021-01-20 18:09:45 +04:00
parent 34f7391ec9
commit 19455d44db
16 changed files with 211 additions and 99 deletions

View file

@ -1223,11 +1223,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_forwarded_channel_via" = "Forwarded from {channel} via {inline_bot}"; "lng_forwarded_channel_via" = "Forwarded from {channel} via {inline_bot}";
"lng_forwarded_signed" = "{channel} ({user})"; "lng_forwarded_signed" = "{channel} ({user})";
"lng_forwarded_hidden" = "The account was hidden by the user."; "lng_forwarded_hidden" = "The account was hidden by the user.";
"lng_forwarded_imported" = "This message was imported from another app. It may not be real.";
"lng_signed_author" = "Author: {user}"; "lng_signed_author" = "Author: {user}";
"lng_in_reply_to" = "In reply to"; "lng_in_reply_to" = "In reply to";
"lng_edited" = "edited"; "lng_edited" = "edited";
"lng_edited_date" = "Edited: {date}"; "lng_edited_date" = "Edited: {date}";
"lng_sent_date" = "Sent: {date}"; "lng_sent_date" = "Sent: {date}";
"lng_imported" = "imported";
"lng_admin_badge" = "admin"; "lng_admin_badge" = "admin";
"lng_owner_badge" = "owner"; "lng_owner_badge" = "owner";
"lng_channel_badge" = "channel"; "lng_channel_badge" = "channel";

View file

@ -451,6 +451,12 @@ void paintRow(
sendStateIcon->paint(p, rectForName.topLeft() + QPoint(rectForName.width(), 0), fullWidth); sendStateIcon->paint(p, rectForName.topLeft() + QPoint(rectForName.width(), 0), fullWidth);
} }
p.setFont(st::msgNameFont);
p.setPen(active
? st::dialogsNameFgActive
: selected
? st::dialogsNameFgOver
: st::dialogsNameFg);
if (flags & (Flag::SavedMessages | Flag::RepliesMessages)) { if (flags & (Flag::SavedMessages | Flag::RepliesMessages)) {
auto text = (flags & Flag::SavedMessages) auto text = (flags & Flag::SavedMessages)
? tr::lng_saved_messages(tr::now) ? tr::lng_saved_messages(tr::now)
@ -459,12 +465,6 @@ void paintRow(
if (textWidth > rectForName.width()) { if (textWidth > rectForName.width()) {
text = st::msgNameFont->elided(text, rectForName.width()); text = st::msgNameFont->elided(text, rectForName.width());
} }
p.setFont(st::msgNameFont);
p.setPen(active
? st::dialogsNameFgActive
: selected
? st::dialogsNameFgOver
: st::dialogsNameFg);
p.drawTextLeft(rectForName.left(), rectForName.top(), fullWidth, text); p.drawTextLeft(rectForName.left(), rectForName.top(), fullWidth, text);
} else if (from) { } else if (from) {
if (!(flags & Flag::SearchResult)) { if (!(flags & Flag::SearchResult)) {
@ -488,22 +488,15 @@ void paintRow(
badgeStyle); badgeStyle);
rectForName.setWidth(rectForName.width() - badgeWidth); rectForName.setWidth(rectForName.width() - badgeWidth);
} }
p.setPen(active
? st::dialogsNameFgActive
: selected
? st::dialogsNameFgOver
: st::dialogsNameFg);
from->nameText().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width()); from->nameText().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width());
} else if (hiddenSenderInfo) { } else if (hiddenSenderInfo) {
hiddenSenderInfo->nameText.drawElided(p, rectForName.left(), rectForName.top(), rectForName.width()); hiddenSenderInfo->nameText.drawElided(p, rectForName.left(), rectForName.top(), rectForName.width());
} else { } else {
const auto nameFg = active if (!active) {
? st::dialogsNameFgActive p.setPen(selected
: (selected
? st::dialogsArchiveFgOver ? st::dialogsArchiveFgOver
: st::dialogsArchiveFg); : st::dialogsArchiveFg);
p.setPen(nameFg); }
p.setFont(st::msgNameFont);
auto text = entry->chatListName(); // TODO feed name with emoji auto text = entry->chatListName(); // TODO feed name with emoji
auto textWidth = st::msgNameFont->width(text); auto textWidth = st::msgNameFont->width(text);
if (textWidth > rectForName.width()) { if (textWidth > rectForName.width()) {
@ -825,8 +818,10 @@ void RowPainter::paint(
const auto hiddenSenderInfo = [&]() -> const HiddenSenderInfo* { const auto hiddenSenderInfo = [&]() -> const HiddenSenderInfo* {
if (const auto searchChat = row->searchInChat()) { if (const auto searchChat = row->searchInChat()) {
if (const auto peer = searchChat.peer()) { if (const auto peer = searchChat.peer()) {
if (peer->isSelf()) { if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
return item->hiddenForwardedInfo(); if (peer->isSelf() || forwarded->imported) {
return forwarded->hiddenSenderInfo.get();
}
} }
} }
} }

View file

@ -2779,10 +2779,7 @@ void HistoryInner::mouseActionUpdate() {
const auto message = view->data()->toHistoryMessage(); const auto message = view->data()->toHistoryMessage();
Assert(message != nullptr); Assert(message != nullptr);
const auto from = message->displayFrom(); dragState = TextState(nullptr, view->fromPhotoLink());
dragState = TextState(nullptr, from
? from->openLink()
: hiddenUserpicLink(message->fullId()));
_dragStateItem = session().data().message(dragState.itemId); _dragStateItem = session().data().message(dragState.itemId);
lnkhost = view; lnkhost = view;
return false; return false;
@ -2923,13 +2920,6 @@ void HistoryInner::mouseActionUpdate() {
} }
} }
ClickHandlerPtr HistoryInner::hiddenUserpicLink(FullMsgId id) {
static const auto result = std::make_shared<LambdaClickHandler>([] {
Ui::Toast::Show(tr::lng_forwarded_hidden(tr::now));
});
return result;
}
void HistoryInner::updateDragSelection(Element *dragSelFrom, Element *dragSelTo, bool dragSelecting) { void HistoryInner::updateDragSelection(Element *dragSelFrom, Element *dragSelTo, bool dragSelecting) {
if (_dragSelFrom == dragSelFrom && _dragSelTo == dragSelTo && _dragSelecting == dragSelecting) { if (_dragSelFrom == dragSelFrom && _dragSelTo == dragSelTo && _dragSelecting == dragSelecting) {
return; return;

View file

@ -214,8 +214,6 @@ private:
template <typename Method> template <typename Method>
void enumerateDates(Method method); void enumerateDates(Method method);
ClickHandlerPtr hiddenUserpicLink(FullMsgId id);
void scrollDateCheck(); void scrollDateCheck();
void scrollDateHideByTimer(); void scrollDateHideByTimer();
bool canHaveFromUserpics() const; bool canHaveFromUserpics() const;

View file

@ -273,9 +273,10 @@ HistoryItem *HistoryItem::lookupDiscussionPostOriginal() const {
PeerData *HistoryItem::displayFrom() const { PeerData *HistoryItem::displayFrom() const {
if (const auto sender = discussionPostOriginalSender()) { if (const auto sender = discussionPostOriginalSender()) {
return sender; return sender;
} else if (history()->peer->isSelf() } else if (const auto forwarded = Get<HistoryMessageForwarded>()) {
|| history()->peer->isRepliesChat()) { if (history()->peer->isSelf() || history()->peer->isRepliesChat() || forwarded->imported) {
return senderOriginal(); return forwarded->originalSender;
}
} }
return author().get(); return author().get();
} }

View file

@ -112,10 +112,14 @@ int HistoryMessageEdited::maxWidth() const {
return text.maxWidth(); return text.maxWidth();
} }
HiddenSenderInfo::HiddenSenderInfo(const QString &name) HiddenSenderInfo::HiddenSenderInfo(const QString &name, bool external)
: name(name) : name(name)
, colorPeerId(Data::FakePeerIdForJustName(name)) , colorPeerId(Data::FakePeerIdForJustName(name))
, userpic(Data::PeerUserpicColor(colorPeerId), name) { , userpic(
Data::PeerUserpicColor(colorPeerId),
(external
? Ui::EmptyUserpic::ExternalName()
: name)) {
nameText.setText(st::msgNameStyle, name, Ui::NameTextOptions()); nameText.setText(st::msgNameStyle, name, Ui::NameTextOptions());
const auto parts = name.trimmed().split(' ', base::QStringSkipEmptyParts); const auto parts = name.trimmed().split(' ', base::QStringSkipEmptyParts);
firstName = parts[0]; firstName = parts[0];

View file

@ -71,7 +71,7 @@ struct HistoryMessageEdited : public RuntimeComponent<HistoryMessageEdited, Hist
}; };
struct HiddenSenderInfo { struct HiddenSenderInfo {
explicit HiddenSenderInfo(const QString &name); HiddenSenderInfo(const QString &name, bool external);
QString name; QString name;
QString firstName; QString firstName;
@ -101,6 +101,7 @@ struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded
PeerData *savedFromPeer = nullptr; PeerData *savedFromPeer = nullptr;
MsgId savedFromMsgId = 0; MsgId savedFromMsgId = 0;
bool imported = false;
}; };
struct HistoryMessageReply : public RuntimeComponent<HistoryMessageReply, HistoryItem> { struct HistoryMessageReply : public RuntimeComponent<HistoryMessageReply, HistoryItem> {

View file

@ -440,6 +440,7 @@ struct HistoryMessage::CreateConfig {
QString authorOriginal; QString authorOriginal;
TimeId originalDate = 0; TimeId originalDate = 0;
TimeId editDate = 0; TimeId editDate = 0;
bool imported = false;
// For messages created from MTP structs. // For messages created from MTP structs.
const MTPMessageReplies *mtpReplies = nullptr; const MTPMessageReplies *mtpReplies = nullptr;
@ -466,6 +467,7 @@ void HistoryMessage::FillForwardedInfo(
config.savedFromPeer = peerFromMTP(*savedFromPeer); config.savedFromPeer = peerFromMTP(*savedFromPeer);
config.savedFromMsgId = savedFromMsgId->v; config.savedFromMsgId = savedFromMsgId->v;
} }
config.imported = data.is_imported();
} }
HistoryMessage::HistoryMessage( HistoryMessage::HistoryMessage(
@ -1118,7 +1120,8 @@ void HistoryMessage::setupForwardedComponent(const CreateConfig &config) {
: nullptr; : nullptr;
if (!forwarded->originalSender) { if (!forwarded->originalSender) {
forwarded->hiddenSenderInfo = std::make_unique<HiddenSenderInfo>( forwarded->hiddenSenderInfo = std::make_unique<HiddenSenderInfo>(
config.senderNameOriginal); config.senderNameOriginal,
config.imported);
} }
forwarded->originalId = config.originalId; forwarded->originalId = config.originalId;
forwarded->originalAuthor = config.authorOriginal; forwarded->originalAuthor = config.authorOriginal;
@ -1126,6 +1129,7 @@ void HistoryMessage::setupForwardedComponent(const CreateConfig &config) {
forwarded->savedFromPeer = history()->owner().peerLoaded( forwarded->savedFromPeer = history()->owner().peerLoaded(
config.savedFromPeer); config.savedFromPeer);
forwarded->savedFromMsgId = config.savedFromMsgId; forwarded->savedFromMsgId = config.savedFromMsgId;
forwarded->imported = config.imported;
} }
void HistoryMessage::refreshMedia(const MTPMessageMedia *media) { void HistoryMessage::refreshMedia(const MTPMessageMedia *media) {

View file

@ -22,6 +22,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h" #include "main/main_session.h"
#include "chat_helpers/stickers_emoji_pack.h" #include "chat_helpers/stickers_emoji_pack.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "ui/toast/toast.h"
#include "ui/toasts/common_toasts.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_groups.h" #include "data/data_groups.h"
#include "data/data_media_types.h" #include "data/data_media_types.h"
@ -38,18 +40,19 @@ constexpr int kAttachMessageToPreviousSecondsDelta = 900;
bool IsAttachedToPreviousInSavedMessages( bool IsAttachedToPreviousInSavedMessages(
not_null<HistoryItem*> previous, not_null<HistoryItem*> previous,
not_null<HistoryItem*> item) { HistoryMessageForwarded *prevForwarded,
const auto forwarded = previous->Has<HistoryMessageForwarded>(); not_null<HistoryItem*> item,
HistoryMessageForwarded *forwarded) {
const auto sender = previous->senderOriginal(); const auto sender = previous->senderOriginal();
if (forwarded != item->Has<HistoryMessageForwarded>()) { if ((prevForwarded != nullptr) != (forwarded != nullptr)) {
return false; return false;
} else if (sender != item->senderOriginal()) { } else if (sender != item->senderOriginal()) {
return false; return false;
} else if (!forwarded || sender) { } else if (!prevForwarded || sender) {
return true; return true;
} }
const auto previousInfo = previous->hiddenForwardedInfo(); const auto previousInfo = prevForwarded->hiddenSenderInfo.get();
const auto itemInfo = item->hiddenForwardedInfo(); const auto itemInfo = forwarded->hiddenSenderInfo.get();
Assert(previousInfo != nullptr); Assert(previousInfo != nullptr);
Assert(itemInfo != nullptr); Assert(itemInfo != nullptr);
return (*previousInfo == *itemInfo); return (*previousInfo == *itemInfo);
@ -174,15 +177,27 @@ QString DateTooltipText(not_null<Element*> view) {
base::unixtime::parse(forwarded->originalDate).toString(format)); base::unixtime::parse(forwarded->originalDate).toString(format));
if (const auto media = view->media()) { if (const auto media = view->media()) {
if (media->hidesForwardedInfo()) { if (media->hidesForwardedInfo()) {
const auto from = forwarded->originalSender
? forwarded->originalSender->shortName()
: forwarded->hiddenSenderInfo->firstName;
if (forwarded->imported) {
dateText += '\n' + tr::lng_signed_author(
tr::now,
lt_user,
from);
} else {
dateText += '\n' + tr::lng_forwarded( dateText += '\n' + tr::lng_forwarded(
tr::now, tr::now,
lt_user, lt_user,
(forwarded->originalSender from);
? forwarded->originalSender->shortName()
: forwarded->hiddenSenderInfo->firstName));
} }
} }
} }
if (forwarded->imported) {
dateText = tr::lng_forwarded_imported(tr::now)
+ "\n\n" + dateText;
}
}
if (const auto msgsigned = view->data()->Get<HistoryMessageSigned>()) { if (const auto msgsigned = view->data()->Get<HistoryMessageSigned>()) {
if (msgsigned->isElided && !msgsigned->isAnonymousRank) { if (msgsigned->isElided && !msgsigned->isAnonymousRank) {
dateText += '\n' dateText += '\n'
@ -492,9 +507,17 @@ bool Element::computeIsAttachToPrevious(not_null<Element*> previous) {
&& mayBeAttached(item) && mayBeAttached(item)
&& mayBeAttached(prev); && mayBeAttached(prev);
if (possible) { if (possible) {
const auto forwarded = item->Get<HistoryMessageForwarded>();
const auto prevForwarded = prev->Get<HistoryMessageForwarded>();
if (item->history()->peer->isSelf() if (item->history()->peer->isSelf()
|| item->history()->peer->isRepliesChat()) { || item->history()->peer->isRepliesChat()
return IsAttachedToPreviousInSavedMessages(prev, item); || (forwarded && forwarded->imported)
|| (prevForwarded && prevForwarded->imported)) {
return IsAttachedToPreviousInSavedMessages(
prev,
prevForwarded,
item,
forwarded);
} else { } else {
return prev->from() == item->from(); return prev->from() == item->from();
} }
@ -503,6 +526,28 @@ bool Element::computeIsAttachToPrevious(not_null<Element*> previous) {
return false; return false;
} }
ClickHandlerPtr Element::fromLink() const {
const auto item = data();
const auto from = item->displayFrom();
if (from) {
return from->openLink();
}
if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
if (forwarded->imported) {
static const auto imported = std::make_shared<LambdaClickHandler>([] {
Ui::ShowMultilineToast({
.text = { tr::lng_forwarded_imported(tr::now) },
});
});
return imported;
}
}
static const auto hidden = std::make_shared<LambdaClickHandler>([] {
Ui::Toast::Show(tr::lng_forwarded_hidden(tr::now));
});
return hidden;
}
void Element::createUnreadBar(rpl::producer<QString> text) { void Element::createUnreadBar(rpl::producer<QString> text) {
if (!AddComponents(UnreadBar::Bit())) { if (!AddComponents(UnreadBar::Bit())) {
return; return;

View file

@ -325,6 +325,10 @@ public:
void previousInBlocksChanged(); void previousInBlocksChanged();
void nextInBlocksRemoved(); void nextInBlocksRemoved();
[[nodiscard]] ClickHandlerPtr fromPhotoLink() const {
return fromLink();
}
virtual ~Element(); virtual ~Element();
protected: protected:
@ -332,6 +336,8 @@ protected:
Painter &p, Painter &p,
int geometryHeight) const; int geometryHeight) const;
[[nodiscard]] ClickHandlerPtr fromLink() const;
virtual void refreshDataIdHook(); virtual void refreshDataIdHook();
private: private:

View file

@ -2387,9 +2387,7 @@ void ListWidget::mouseActionUpdate() {
Assert(message != nullptr); Assert(message != nullptr);
const auto from = message->displayFrom(); const auto from = message->displayFrom();
dragState = TextState(nullptr, from dragState = TextState(nullptr, view->fromPhotoLink());
? from->openLink()
: hiddenUserpicLink(message->fullId()));
_overItemExact = session().data().message(dragState.itemId); _overItemExact = session().data().message(dragState.itemId);
lnkhost = view; lnkhost = view;
return false; return false;
@ -2461,13 +2459,6 @@ void ListWidget::mouseActionUpdate() {
//} // #TODO select scroll //} // #TODO select scroll
} }
ClickHandlerPtr ListWidget::hiddenUserpicLink(FullMsgId id) {
static const auto result = std::make_shared<LambdaClickHandler>([] {
Ui::Toast::Show(tr::lng_forwarded_hidden(tr::now));
});
return result;
}
style::cursor ListWidget::computeMouseCursor() const { style::cursor ListWidget::computeMouseCursor() const {
if (ClickHandler::getPressed() || ClickHandler::getActive()) { if (ClickHandler::getPressed() || ClickHandler::getActive()) {
return style::cur_pointer; return style::cur_pointer;

View file

@ -472,8 +472,6 @@ private:
template <typename Method> template <typename Method>
void enumerateDates(Method method); void enumerateDates(Method method);
ClickHandlerPtr hiddenUserpicLink(FullMsgId id);
static constexpr auto kMinimalIdsLimit = 24; static constexpr auto kMinimalIdsLimit = 24;
const not_null<ListDelegate*> _delegate; const not_null<ListDelegate*> _delegate;

View file

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_group_call_tracker.h" // UserpicInRow. #include "history/view/history_view_group_call_tracker.h" // UserpicInRow.
#include "history/history.h" #include "history/history.h"
#include "ui/effects/ripple_animation.h" #include "ui/effects/ripple_animation.h"
#include "base/unixtime.h"
#include "core/application.h" #include "core/application.h"
#include "core/core_settings.h" #include "core/core_settings.h"
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
@ -1181,9 +1182,10 @@ void Message::unloadHeavyPart() {
_comments = nullptr; _comments = nullptr;
} }
bool Message::showForwardsFromSender() const { bool Message::showForwardsFromSender(
not_null<HistoryMessageForwarded*> forwarded) const {
const auto peer = message()->history()->peer; const auto peer = message()->history()->peer;
return peer->isSelf() || peer->isRepliesChat(); return peer->isSelf() || peer->isRepliesChat() || forwarded->imported;
} }
bool Message::hasFromPhoto() const { bool Message::hasFromPhoto() const {
@ -1204,8 +1206,10 @@ bool Message::hasFromPhoto() const {
return false; return false;
} else if (Core::App().settings().chatWide()) { } else if (Core::App().settings().chatWide()) {
return true; return true;
} else if (showForwardsFromSender()) { } else if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
return item->Has<HistoryMessageForwarded>(); if (showForwardsFromSender(forwarded)) {
return true;
}
} }
return !item->out() && !item->history()->peer->isUser(); return !item->out() && !item->history()->peer->isUser();
} break; } break;
@ -1436,10 +1440,7 @@ bool Message::getStateFromName(
if (point.x() >= availableLeft if (point.x() >= availableLeft
&& point.x() < availableLeft + availableWidth && point.x() < availableLeft + availableWidth
&& point.x() < availableLeft + nameText->maxWidth()) { && point.x() < availableLeft + nameText->maxWidth()) {
static const auto hidden = std::make_shared<LambdaClickHandler>([] { outResult->link = fromLink();
Ui::Toast::Show(tr::lng_forwarded_hidden(tr::now));
});
outResult->link = from ? from->openLink() : hidden;
return true; return true;
} }
auto via = item->Get<HistoryMessageVia>(); auto via = item->Get<HistoryMessageVia>();
@ -2068,9 +2069,17 @@ bool Message::hasFromName() const {
case Context::Pinned: case Context::Pinned:
case Context::Replies: { case Context::Replies: {
const auto item = message(); const auto item = message();
return (!hasOutLayout() || item->from()->isMegagroup()) if (hasOutLayout() && !item->from()->isMegagroup()) {
&& (!item->history()->peer->isUser() return false;
|| showForwardsFromSender()); } else if (!item->history()->peer->isUser()) {
return true;
}
if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
if (showForwardsFromSender(forwarded)) {
return true;
}
}
return false;
} break; } break;
case Context::ContactPreview: case Context::ContactPreview:
return false; return false;
@ -2087,10 +2096,10 @@ bool Message::displayFromName() const {
bool Message::displayForwardedFrom() const { bool Message::displayForwardedFrom() const {
const auto item = message(); const auto item = message();
if (showForwardsFromSender()) { if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
if (showForwardsFromSender(forwarded)) {
return false; return false;
} }
if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
if (const auto sender = item->discussionPostOriginalSender()) { if (const auto sender = item->discussionPostOriginalSender()) {
if (sender == forwarded->originalSender) { if (sender == forwarded->originalSender) {
return false; return false;
@ -2111,9 +2120,11 @@ bool Message::hasOutLayout() const {
const auto item = message(); const auto item = message();
if (item->history()->peer->isSelf()) { if (item->history()->peer->isSelf()) {
return !item->Has<HistoryMessageForwarded>(); return !item->Has<HistoryMessageForwarded>();
} else if (showForwardsFromSender()) { } else if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
if (showForwardsFromSender(forwarded)) {
return false; return false;
} }
}
return item->out() && !item->isPost(); return item->out() && !item->isPost();
} }
@ -2217,7 +2228,7 @@ bool Message::displayFastShare() const {
return !peer->isMegagroup(); return !peer->isMegagroup();
} else if (const auto user = peer->asUser()) { } else if (const auto user = peer->asUser()) {
if (const auto forwarded = item->Get<HistoryMessageForwarded>()) { if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
return !showForwardsFromSender() return !showForwardsFromSender(forwarded)
&& !item->out() && !item->out()
&& forwarded->originalSender && forwarded->originalSender
&& forwarded->originalSender->isChannel() && forwarded->originalSender->isChannel()
@ -2700,8 +2711,16 @@ void Message::initTime() {
item->_timeWidth = msgsigned->maxWidth(); item->_timeWidth = msgsigned->maxWidth();
} else if (const auto edited = displayedEditBadge()) { } else if (const auto edited = displayedEditBadge()) {
item->_timeWidth = edited->maxWidth(); item->_timeWidth = edited->maxWidth();
} else {
const auto forwarded = item->Get<HistoryMessageForwarded>();
if (forwarded && forwarded->imported) {
const auto date = base::unixtime::parse(forwarded->originalDate);
item->_timeText = date.toString(
u"d.MM.yy "_q + cTimeFormat() + ' '
) + tr::lng_imported(tr::now);
} else { } else {
item->_timeText = dateTime().toString(cTimeFormat()); item->_timeText = dateTime().toString(cTimeFormat());
}
item->_timeWidth = st::msgDateFont->width(item->_timeText); item->_timeWidth = st::msgDateFont->width(item->_timeText);
} }
if (item->_text.hasSkipBlock()) { if (item->_text.hasSkipBlock()) {

View file

@ -125,7 +125,8 @@ private:
void refreshEditedBadge(); void refreshEditedBadge();
void fromNameUpdated(int width) const; void fromNameUpdated(int width) const;
[[nodiscard]] bool showForwardsFromSender() const; [[nodiscard]] bool showForwardsFromSender(
not_null<HistoryMessageForwarded*> forwarded) const;
[[nodiscard]] TextSelection skipTextSelection( [[nodiscard]] TextSelection skipTextSelection(
TextSelection selection) const; TextSelection selection) const;
[[nodiscard]] TextSelection unskipTextSelection( [[nodiscard]] TextSelection unskipTextSelection(

View file

@ -13,16 +13,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "app.h" #include "app.h"
#include "styles/style_chat.h" #include "styles/style_chat.h"
#include "styles/style_dialogs.h" #include "styles/style_dialogs.h"
#include "styles/style_widgets.h" // style::IconButton
#include "styles/style_info.h" // st::topBarCall
namespace Ui { namespace Ui {
namespace { namespace {
[[nodiscard]] bool IsExternal(const QString &name) {
return !name.isEmpty()
&& (name.front() == QChar(0))
&& QStringView(name).mid(1) == qstr("external");
}
void PaintSavedMessagesInner( void PaintSavedMessagesInner(
Painter &p, Painter &p,
int x, int x,
int y, int y,
int size, int size,
const style::color &bg,
const style::color &fg) { const style::color &fg) {
// |<----width----->| // |<----width----->|
// //
@ -92,27 +99,28 @@ void PaintSavedMessagesInner(
} }
} }
void PaintRepliesMessagesInner( void PaintIconInner(
Painter &p, Painter &p,
int x, int x,
int y, int y,
int size, int size,
const style::color &bg, int defaultSize,
const style::icon &icon,
const style::color &fg) { const style::color &fg) {
if (size == st::dialogsPhotoSize) { if (size == defaultSize) {
const auto rect = QRect{ x, y, size, size }; const auto rect = QRect{ x, y, size, size };
st::dialogsRepliesUserpic.paintInCenter( icon.paintInCenter(
p, p,
rect, rect,
fg->c); fg->c);
} else { } else {
p.save(); p.save();
const auto ratio = size / float64(st::dialogsPhotoSize); const auto ratio = size / float64(defaultSize);
p.translate(x + size / 2., y + size / 2.); p.translate(x + size / 2., y + size / 2.);
p.scale(ratio, ratio); p.scale(ratio, ratio);
const auto skip = st::dialogsPhotoSize; const auto skip = defaultSize;
const auto rect = QRect{ -skip, -skip, 2 * skip, 2 * skip }; const auto rect = QRect{ -skip, -skip, 2 * skip, 2 * skip };
st::dialogsRepliesUserpic.paintInCenter( icon.paintInCenter(
p, p,
rect, rect,
fg->c); fg->c);
@ -120,6 +128,38 @@ void PaintRepliesMessagesInner(
} }
} }
void PaintRepliesMessagesInner(
Painter &p,
int x,
int y,
int size,
const style::color &fg) {
PaintIconInner(
p,
x,
y,
size,
st::dialogsPhotoSize,
st::dialogsRepliesUserpic,
fg);
}
void PaintExternalMessagesInner(
Painter &p,
int x,
int y,
int size,
const style::color &fg) {
PaintIconInner(
p,
x,
y,
size,
st::msgPhotoSize,
st::topBarCall.icon,
fg);
}
template <typename Callback> template <typename Callback>
[[nodiscard]] QPixmap Generate(int size, Callback callback) { [[nodiscard]] QPixmap Generate(int size, Callback callback) {
auto result = QImage( auto result = QImage(
@ -141,6 +181,10 @@ EmptyUserpic::EmptyUserpic(const style::color &color, const QString &name)
fillString(name); fillString(name);
} }
QString EmptyUserpic::ExternalName() {
return QChar(0) + u"external"_q;
}
template <typename Callback> template <typename Callback>
void EmptyUserpic::paint( void EmptyUserpic::paint(
Painter &p, Painter &p,
@ -160,10 +204,17 @@ void EmptyUserpic::paint(
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
paintBackground(); paintBackground();
if (IsExternal(_string)) {
PaintExternalMessagesInner(p, x, y, size, st::historyPeerUserpicFg);
} else {
p.setFont(font); p.setFont(font);
p.setBrush(Qt::NoBrush); p.setBrush(Qt::NoBrush);
p.setPen(st::historyPeerUserpicFg); p.setPen(st::historyPeerUserpicFg);
p.drawText(QRect(x, y, size, size), _string, QTextOption(style::al_center)); p.drawText(
QRect(x, y, size, size),
_string,
QTextOption(style::al_center));
}
} }
void EmptyUserpic::paint( void EmptyUserpic::paint(
@ -226,7 +277,7 @@ void EmptyUserpic::PaintSavedMessages(
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
p.drawEllipse(x, y, size, size); p.drawEllipse(x, y, size, size);
PaintSavedMessagesInner(p, x, y, size, bg, fg); PaintSavedMessagesInner(p, x, y, size, fg);
} }
void EmptyUserpic::PaintSavedMessagesRounded( void EmptyUserpic::PaintSavedMessagesRounded(
@ -244,7 +295,7 @@ void EmptyUserpic::PaintSavedMessagesRounded(
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
p.drawRoundedRect(x, y, size, size, st::roundRadiusSmall, st::roundRadiusSmall); p.drawRoundedRect(x, y, size, size, st::roundRadiusSmall, st::roundRadiusSmall);
PaintSavedMessagesInner(p, x, y, size, bg, fg); PaintSavedMessagesInner(p, x, y, size, fg);
} }
QPixmap EmptyUserpic::GenerateSavedMessages(int size) { QPixmap EmptyUserpic::GenerateSavedMessages(int size) {
@ -296,7 +347,7 @@ void EmptyUserpic::PaintRepliesMessages(
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
p.drawEllipse(x, y, size, size); p.drawEllipse(x, y, size, size);
PaintRepliesMessagesInner(p, x, y, size, bg, fg); PaintRepliesMessagesInner(p, x, y, size, fg);
} }
void EmptyUserpic::PaintRepliesMessagesRounded( void EmptyUserpic::PaintRepliesMessagesRounded(
@ -314,7 +365,7 @@ void EmptyUserpic::PaintRepliesMessagesRounded(
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
p.drawRoundedRect(x, y, size, size, st::roundRadiusSmall, st::roundRadiusSmall); p.drawRoundedRect(x, y, size, size, st::roundRadiusSmall, st::roundRadiusSmall);
PaintRepliesMessagesInner(p, x, y, size, bg, fg); PaintRepliesMessagesInner(p, x, y, size, fg);
} }
QPixmap EmptyUserpic::GenerateRepliesMessages(int size) { QPixmap EmptyUserpic::GenerateRepliesMessages(int size) {
@ -349,6 +400,10 @@ QPixmap EmptyUserpic::generate(int size) {
} }
void EmptyUserpic::fillString(const QString &name) { void EmptyUserpic::fillString(const QString &name) {
if (IsExternal(name)) {
_string = name;
return;
}
QList<QString> letters; QList<QString> letters;
QList<int> levels; QList<int> levels;

View file

@ -11,6 +11,8 @@ namespace Ui {
class EmptyUserpic { class EmptyUserpic {
public: public:
[[nodiscard]] static QString ExternalName();
EmptyUserpic(const style::color &color, const QString &name); EmptyUserpic(const style::color &color, const QString &name);
void paint( void paint(