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_signed" = "{channel} ({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_in_reply_to" = "In reply to";
"lng_edited" = "edited";
"lng_edited_date" = "Edited: {date}";
"lng_sent_date" = "Sent: {date}";
"lng_imported" = "imported";
"lng_admin_badge" = "admin";
"lng_owner_badge" = "owner";
"lng_channel_badge" = "channel";

View File

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

View File

@ -2779,10 +2779,7 @@ void HistoryInner::mouseActionUpdate() {
const auto message = view->data()->toHistoryMessage();
Assert(message != nullptr);
const auto from = message->displayFrom();
dragState = TextState(nullptr, from
? from->openLink()
: hiddenUserpicLink(message->fullId()));
dragState = TextState(nullptr, view->fromPhotoLink());
_dragStateItem = session().data().message(dragState.itemId);
lnkhost = view;
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) {
if (_dragSelFrom == dragSelFrom && _dragSelTo == dragSelTo && _dragSelecting == dragSelecting) {
return;

View File

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

View File

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

View File

@ -112,10 +112,14 @@ int HistoryMessageEdited::maxWidth() const {
return text.maxWidth();
}
HiddenSenderInfo::HiddenSenderInfo(const QString &name)
HiddenSenderInfo::HiddenSenderInfo(const QString &name, bool external)
: name(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());
const auto parts = name.trimmed().split(' ', base::QStringSkipEmptyParts);
firstName = parts[0];

View File

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

View File

@ -440,6 +440,7 @@ struct HistoryMessage::CreateConfig {
QString authorOriginal;
TimeId originalDate = 0;
TimeId editDate = 0;
bool imported = false;
// For messages created from MTP structs.
const MTPMessageReplies *mtpReplies = nullptr;
@ -466,6 +467,7 @@ void HistoryMessage::FillForwardedInfo(
config.savedFromPeer = peerFromMTP(*savedFromPeer);
config.savedFromMsgId = savedFromMsgId->v;
}
config.imported = data.is_imported();
}
HistoryMessage::HistoryMessage(
@ -1118,7 +1120,8 @@ void HistoryMessage::setupForwardedComponent(const CreateConfig &config) {
: nullptr;
if (!forwarded->originalSender) {
forwarded->hiddenSenderInfo = std::make_unique<HiddenSenderInfo>(
config.senderNameOriginal);
config.senderNameOriginal,
config.imported);
}
forwarded->originalId = config.originalId;
forwarded->originalAuthor = config.authorOriginal;
@ -1126,6 +1129,7 @@ void HistoryMessage::setupForwardedComponent(const CreateConfig &config) {
forwarded->savedFromPeer = history()->owner().peerLoaded(
config.savedFromPeer);
forwarded->savedFromMsgId = config.savedFromMsgId;
forwarded->imported = config.imported;
}
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 "chat_helpers/stickers_emoji_pack.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_groups.h"
#include "data/data_media_types.h"
@ -38,18 +40,19 @@ constexpr int kAttachMessageToPreviousSecondsDelta = 900;
bool IsAttachedToPreviousInSavedMessages(
not_null<HistoryItem*> previous,
not_null<HistoryItem*> item) {
const auto forwarded = previous->Has<HistoryMessageForwarded>();
HistoryMessageForwarded *prevForwarded,
not_null<HistoryItem*> item,
HistoryMessageForwarded *forwarded) {
const auto sender = previous->senderOriginal();
if (forwarded != item->Has<HistoryMessageForwarded>()) {
if ((prevForwarded != nullptr) != (forwarded != nullptr)) {
return false;
} else if (sender != item->senderOriginal()) {
return false;
} else if (!forwarded || sender) {
} else if (!prevForwarded || sender) {
return true;
}
const auto previousInfo = previous->hiddenForwardedInfo();
const auto itemInfo = item->hiddenForwardedInfo();
const auto previousInfo = prevForwarded->hiddenSenderInfo.get();
const auto itemInfo = forwarded->hiddenSenderInfo.get();
Assert(previousInfo != nullptr);
Assert(itemInfo != nullptr);
return (*previousInfo == *itemInfo);
@ -174,14 +177,26 @@ QString DateTooltipText(not_null<Element*> view) {
base::unixtime::parse(forwarded->originalDate).toString(format));
if (const auto media = view->media()) {
if (media->hidesForwardedInfo()) {
dateText += '\n' + tr::lng_forwarded(
tr::now,
lt_user,
(forwarded->originalSender
? forwarded->originalSender->shortName()
: forwarded->hiddenSenderInfo->firstName));
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(
tr::now,
lt_user,
from);
}
}
}
if (forwarded->imported) {
dateText = tr::lng_forwarded_imported(tr::now)
+ "\n\n" + dateText;
}
}
if (const auto msgsigned = view->data()->Get<HistoryMessageSigned>()) {
if (msgsigned->isElided && !msgsigned->isAnonymousRank) {
@ -492,9 +507,17 @@ bool Element::computeIsAttachToPrevious(not_null<Element*> previous) {
&& mayBeAttached(item)
&& mayBeAttached(prev);
if (possible) {
const auto forwarded = item->Get<HistoryMessageForwarded>();
const auto prevForwarded = prev->Get<HistoryMessageForwarded>();
if (item->history()->peer->isSelf()
|| item->history()->peer->isRepliesChat()) {
return IsAttachedToPreviousInSavedMessages(prev, item);
|| item->history()->peer->isRepliesChat()
|| (forwarded && forwarded->imported)
|| (prevForwarded && prevForwarded->imported)) {
return IsAttachedToPreviousInSavedMessages(
prev,
prevForwarded,
item,
forwarded);
} else {
return prev->from() == item->from();
}
@ -503,6 +526,28 @@ bool Element::computeIsAttachToPrevious(not_null<Element*> previous) {
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) {
if (!AddComponents(UnreadBar::Bit())) {
return;

View File

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

View File

@ -2387,9 +2387,7 @@ void ListWidget::mouseActionUpdate() {
Assert(message != nullptr);
const auto from = message->displayFrom();
dragState = TextState(nullptr, from
? from->openLink()
: hiddenUserpicLink(message->fullId()));
dragState = TextState(nullptr, view->fromPhotoLink());
_overItemExact = session().data().message(dragState.itemId);
lnkhost = view;
return false;
@ -2461,13 +2459,6 @@ void ListWidget::mouseActionUpdate() {
//} // #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 {
if (ClickHandler::getPressed() || ClickHandler::getActive()) {
return style::cur_pointer;

View File

@ -472,8 +472,6 @@ private:
template <typename Method>
void enumerateDates(Method method);
ClickHandlerPtr hiddenUserpicLink(FullMsgId id);
static constexpr auto kMinimalIdsLimit = 24;
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/history.h"
#include "ui/effects/ripple_animation.h"
#include "base/unixtime.h"
#include "core/application.h"
#include "core/core_settings.h"
#include "ui/toast/toast.h"
@ -1181,9 +1182,10 @@ void Message::unloadHeavyPart() {
_comments = nullptr;
}
bool Message::showForwardsFromSender() const {
bool Message::showForwardsFromSender(
not_null<HistoryMessageForwarded*> forwarded) const {
const auto peer = message()->history()->peer;
return peer->isSelf() || peer->isRepliesChat();
return peer->isSelf() || peer->isRepliesChat() || forwarded->imported;
}
bool Message::hasFromPhoto() const {
@ -1204,8 +1206,10 @@ bool Message::hasFromPhoto() const {
return false;
} else if (Core::App().settings().chatWide()) {
return true;
} else if (showForwardsFromSender()) {
return item->Has<HistoryMessageForwarded>();
} else if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
if (showForwardsFromSender(forwarded)) {
return true;
}
}
return !item->out() && !item->history()->peer->isUser();
} break;
@ -1436,10 +1440,7 @@ bool Message::getStateFromName(
if (point.x() >= availableLeft
&& point.x() < availableLeft + availableWidth
&& point.x() < availableLeft + nameText->maxWidth()) {
static const auto hidden = std::make_shared<LambdaClickHandler>([] {
Ui::Toast::Show(tr::lng_forwarded_hidden(tr::now));
});
outResult->link = from ? from->openLink() : hidden;
outResult->link = fromLink();
return true;
}
auto via = item->Get<HistoryMessageVia>();
@ -2068,9 +2069,17 @@ bool Message::hasFromName() const {
case Context::Pinned:
case Context::Replies: {
const auto item = message();
return (!hasOutLayout() || item->from()->isMegagroup())
&& (!item->history()->peer->isUser()
|| showForwardsFromSender());
if (hasOutLayout() && !item->from()->isMegagroup()) {
return false;
} else if (!item->history()->peer->isUser()) {
return true;
}
if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
if (showForwardsFromSender(forwarded)) {
return true;
}
}
return false;
} break;
case Context::ContactPreview:
return false;
@ -2087,10 +2096,10 @@ bool Message::displayFromName() const {
bool Message::displayForwardedFrom() const {
const auto item = message();
if (showForwardsFromSender()) {
return false;
}
if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
if (showForwardsFromSender(forwarded)) {
return false;
}
if (const auto sender = item->discussionPostOriginalSender()) {
if (sender == forwarded->originalSender) {
return false;
@ -2111,8 +2120,10 @@ bool Message::hasOutLayout() const {
const auto item = message();
if (item->history()->peer->isSelf()) {
return !item->Has<HistoryMessageForwarded>();
} else if (showForwardsFromSender()) {
return false;
} else if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
if (showForwardsFromSender(forwarded)) {
return false;
}
}
return item->out() && !item->isPost();
}
@ -2217,7 +2228,7 @@ bool Message::displayFastShare() const {
return !peer->isMegagroup();
} else if (const auto user = peer->asUser()) {
if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
return !showForwardsFromSender()
return !showForwardsFromSender(forwarded)
&& !item->out()
&& forwarded->originalSender
&& forwarded->originalSender->isChannel()
@ -2701,7 +2712,15 @@ void Message::initTime() {
} else if (const auto edited = displayedEditBadge()) {
item->_timeWidth = edited->maxWidth();
} else {
item->_timeText = dateTime().toString(cTimeFormat());
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 {
item->_timeText = dateTime().toString(cTimeFormat());
}
item->_timeWidth = st::msgDateFont->width(item->_timeText);
}
if (item->_text.hasSkipBlock()) {

View File

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

View File

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

View File

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