mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 13:17:08 +02:00
Track and render reactions as tags in Saved Messages.
This commit is contained in:
parent
0faf801de7
commit
9b43d204e2
9 changed files with 216 additions and 39 deletions
|
@ -20,6 +20,8 @@ namespace {
|
|||
|
||||
constexpr auto kPerPage = 50;
|
||||
constexpr auto kFirstPerPage = 10;
|
||||
constexpr auto kListPerPage = 100;
|
||||
constexpr auto kListFirstPerPage = 20;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -82,7 +84,7 @@ void SavedMessages::sendLoadMore() {
|
|||
MTP_int(_offsetDate),
|
||||
MTP_int(_offsetId),
|
||||
_offsetPeer ? _offsetPeer->input : MTP_inputPeerEmpty(),
|
||||
MTP_int(kPerPage),
|
||||
MTP_int(_offsetId ? kListPerPage : kListFirstPerPage),
|
||||
MTP_long(0)) // hash
|
||||
).done([=](const MTPmessages_SavedDialogs &result) {
|
||||
apply(result, false);
|
||||
|
|
|
@ -313,6 +313,8 @@ enum class MessageFlag : uint64 {
|
|||
ShowSimilarChannels = (1ULL << 41),
|
||||
|
||||
Sponsored = (1ULL << 42),
|
||||
|
||||
ReactionsAreTags = (1ULL << 43),
|
||||
};
|
||||
inline constexpr bool is_flag_type(MessageFlag) { return true; }
|
||||
using MessageFlags = base::flags<MessageFlag>;
|
||||
|
|
|
@ -2429,6 +2429,10 @@ const std::vector<Data::MessageReaction> &HistoryItem::reactions() const {
|
|||
return _reactions ? _reactions->list() : kEmpty;
|
||||
}
|
||||
|
||||
bool HistoryItem::reactionsAreTags() const {
|
||||
return _flags & MessageFlag::ReactionsAreTags;
|
||||
}
|
||||
|
||||
auto HistoryItem::recentReactions() const
|
||||
-> const base::flat_map<
|
||||
Data::ReactionId,
|
||||
|
@ -3556,31 +3560,40 @@ bool HistoryItem::changeReactions(const MTPMessageReactions *reactions) {
|
|||
}
|
||||
if (!reactions) {
|
||||
_flags &= ~MessageFlag::CanViewReactions;
|
||||
if (_history->peer->isSelf()) {
|
||||
_flags |= MessageFlag::ReactionsAreTags;
|
||||
}
|
||||
return (base::take(_reactions) != nullptr);
|
||||
}
|
||||
return reactions->match([&](const MTPDmessageReactions &data) {
|
||||
if (data.is_can_see_list()) {
|
||||
_flags |= MessageFlag::CanViewReactions;
|
||||
} else {
|
||||
_flags &= ~MessageFlag::CanViewReactions;
|
||||
const auto &data = reactions->data();
|
||||
const auto empty = data.vresults().v.isEmpty();
|
||||
if (data.is_reactions_as_tags()
|
||||
|| (empty && _history->peer->isSelf())) {
|
||||
_flags |= MessageFlag::ReactionsAreTags;
|
||||
} else {
|
||||
_flags &= ~MessageFlag::ReactionsAreTags;
|
||||
}
|
||||
if (data.is_can_see_list()) {
|
||||
_flags |= MessageFlag::CanViewReactions;
|
||||
} else {
|
||||
_flags &= ~MessageFlag::CanViewReactions;
|
||||
}
|
||||
if (empty) {
|
||||
return (base::take(_reactions) != nullptr);
|
||||
} else if (!_reactions) {
|
||||
_reactions = std::make_unique<Data::MessageReactions>(this);
|
||||
}
|
||||
const auto min = data.is_min();
|
||||
const auto &list = data.vresults().v;
|
||||
const auto &recent = data.vrecent_reactions().value_or_empty();
|
||||
if (min && hasUnreadReaction()) {
|
||||
// We can't update reactions from min if we have unread.
|
||||
if (_reactions->checkIfChanged(list, recent, min)) {
|
||||
updateReactionsUnknown();
|
||||
}
|
||||
if (data.vresults().v.isEmpty()) {
|
||||
return (base::take(_reactions) != nullptr);
|
||||
} else if (!_reactions) {
|
||||
_reactions = std::make_unique<Data::MessageReactions>(this);
|
||||
}
|
||||
const auto min = data.is_min();
|
||||
const auto &list = data.vresults().v;
|
||||
const auto &recent = data.vrecent_reactions().value_or_empty();
|
||||
if (min && hasUnreadReaction()) {
|
||||
// We can't update reactions from min if we have unread.
|
||||
if (_reactions->checkIfChanged(list, recent, min)) {
|
||||
updateReactionsUnknown();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return _reactions->change(list, recent, min);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return _reactions->change(list, recent, min);
|
||||
}
|
||||
|
||||
void HistoryItem::applyTTL(const MTPDmessage &data) {
|
||||
|
|
|
@ -454,6 +454,7 @@ public:
|
|||
not_null<UserData*> from) const;
|
||||
[[nodiscard]] crl::time lastReactionsRefreshTime() const;
|
||||
|
||||
[[nodiscard]] bool reactionsAreTags() const;
|
||||
[[nodiscard]] bool hasDirectLink() const;
|
||||
[[nodiscard]] bool changesWallPaper() const;
|
||||
|
||||
|
|
|
@ -2917,8 +2917,12 @@ bool Message::isSignedAuthorElided() const {
|
|||
bool Message::embedReactionsInBottomInfo() const {
|
||||
const auto item = data();
|
||||
const auto user = item->history()->peer->asUser();
|
||||
if (!user || user->isPremium() || user->session().premium()) {
|
||||
if (!user
|
||||
|| user->isPremium()
|
||||
|| user->isSelf()
|
||||
|| user->session().premium()) {
|
||||
// Only in messages of a non premium user with a non premium user.
|
||||
// In saved messages we use reactions for tags, we don't embed them.
|
||||
return false;
|
||||
}
|
||||
auto seenMy = false;
|
||||
|
|
|
@ -55,6 +55,7 @@ struct InlineList::Button {
|
|||
int count = 0;
|
||||
int countTextWidth = 0;
|
||||
bool chosen = false;
|
||||
bool tag = false;
|
||||
};
|
||||
|
||||
InlineList::InlineList(
|
||||
|
@ -118,6 +119,7 @@ void InlineList::layoutButtons() {
|
|||
) | ranges::views::transform([](const MessageReaction &reaction) {
|
||||
return not_null{ &reaction };
|
||||
}) | ranges::to_vector;
|
||||
const auto tags = _data.flags & Data::Flag::Tags;
|
||||
const auto &list = _owner->list(::Data::Reactions::Type::All);
|
||||
ranges::sort(sorted, [&](
|
||||
not_null<const MessageReaction*> a,
|
||||
|
@ -142,8 +144,10 @@ void InlineList::layoutButtons() {
|
|||
buttons.push_back((i != end(_buttons))
|
||||
? std::move(*i)
|
||||
: prepareButtonWithId(id));
|
||||
const auto j = _data.recent.find(id);
|
||||
if (j != end(_data.recent) && !j->second.empty()) {
|
||||
if (tags) {
|
||||
setButtonTag(buttons.back());
|
||||
} else if (const auto j = _data.recent.find(id)
|
||||
; j != end(_data.recent) && !j->second.empty()) {
|
||||
setButtonUserpics(buttons.back(), j->second);
|
||||
} else {
|
||||
setButtonCount(buttons.back(), reaction->count);
|
||||
|
@ -168,12 +172,22 @@ InlineList::Button InlineList::prepareButtonWithId(const ReactionId &id) {
|
|||
return result;
|
||||
}
|
||||
|
||||
void InlineList::setButtonTag(Button &button) {
|
||||
if (button.tag) {
|
||||
return;
|
||||
}
|
||||
button.userpics = nullptr;
|
||||
button.count = 0;
|
||||
button.tag = true;
|
||||
}
|
||||
|
||||
void InlineList::setButtonCount(Button &button, int count) {
|
||||
if (button.count == count && !button.userpics) {
|
||||
if (!button.tag && button.count == count && !button.userpics) {
|
||||
return;
|
||||
}
|
||||
button.userpics = nullptr;
|
||||
button.count = count;
|
||||
button.tag = false;
|
||||
button.countText = Lang::FormatCountToShort(count).string;
|
||||
button.countTextWidth = st::semiboldFont->width(button.countText);
|
||||
}
|
||||
|
@ -181,6 +195,7 @@ void InlineList::setButtonCount(Button &button, int count) {
|
|||
void InlineList::setButtonUserpics(
|
||||
Button &button,
|
||||
const std::vector<not_null<PeerData*>> &peers) {
|
||||
button.tag = false;
|
||||
if (!button.userpics) {
|
||||
button.userpics = std::make_unique<Userpics>();
|
||||
}
|
||||
|
@ -228,6 +243,10 @@ QSize InlineList::countOptimalSize() {
|
|||
const auto between = st::reactionInlineBetween;
|
||||
const auto padding = st::reactionInlinePadding;
|
||||
const auto size = st::reactionInlineSize;
|
||||
const auto widthBaseTag = padding.left()
|
||||
+ size
|
||||
+ st::reactionInlineTagSkip
|
||||
+ padding.right();
|
||||
const auto widthBaseCount = padding.left()
|
||||
+ size
|
||||
+ st::reactionInlineSkip
|
||||
|
@ -245,7 +264,9 @@ QSize InlineList::countOptimalSize() {
|
|||
};
|
||||
const auto height = padding.top() + size + padding.bottom();
|
||||
for (auto &button : _buttons) {
|
||||
const auto width = button.userpics
|
||||
const auto width = button.tag
|
||||
? widthBaseTag
|
||||
: button.userpics
|
||||
? (widthBaseUserpics + userpicsWidth(button))
|
||||
: (widthBaseCount + button.countTextWidth);
|
||||
button.geometry.setSize({ width, height });
|
||||
|
@ -336,7 +357,8 @@ void InlineList::paint(
|
|||
const auto padding = st::reactionInlinePadding;
|
||||
const auto size = st::reactionInlineSize;
|
||||
const auto skip = (size - st::reactionInlineImage) / 2;
|
||||
const auto inbubble = (_data.flags & InlineListData::Flag::InBubble);
|
||||
const auto tags = (_data.flags & Data::Flag::Tags);
|
||||
const auto inbubble = (_data.flags & Data::Flag::InBubble);
|
||||
const auto flipped = (_data.flags & Data::Flag::Flipped);
|
||||
p.setFont(st::semiboldFont);
|
||||
for (const auto &button : _buttons) {
|
||||
|
@ -366,21 +388,26 @@ void InlineList::paint(
|
|||
if (bubbleProgress > 0.) {
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
auto opacity = 1.;
|
||||
auto color = QColor();
|
||||
if (inbubble) {
|
||||
if (!chosen) {
|
||||
p.setOpacity(bubbleProgress * (context.outbg
|
||||
opacity = bubbleProgress * (context.outbg
|
||||
? kOutNonChosenOpacity
|
||||
: kInNonChosenOpacity));
|
||||
: kInNonChosenOpacity);
|
||||
} else if (!bubbleReady) {
|
||||
p.setOpacity(bubbleProgress);
|
||||
opacity = bubbleProgress;
|
||||
}
|
||||
p.setBrush(stm->msgFileBg);
|
||||
color = stm->msgFileBg->c;
|
||||
} else {
|
||||
if (!bubbleReady) {
|
||||
p.setOpacity(bubbleProgress);
|
||||
opacity = bubbleProgress;
|
||||
}
|
||||
p.setBrush(chosen ? st->msgServiceFg() : st->msgServiceBg());
|
||||
color = (chosen
|
||||
? st->msgServiceFg()
|
||||
: st->msgServiceBg())->c;
|
||||
}
|
||||
|
||||
const auto radius = geometry.height() / 2.;
|
||||
const auto fill = geometry.marginsAdded({
|
||||
flipped ? bubbleSkip : 0,
|
||||
|
@ -388,7 +415,7 @@ void InlineList::paint(
|
|||
flipped ? 0 : bubbleSkip,
|
||||
0,
|
||||
});
|
||||
p.drawRoundedRect(fill, radius, radius);
|
||||
paintSingleBg(p, fill, color, opacity);
|
||||
if (inbubble && !chosen) {
|
||||
p.setOpacity(bubbleProgress);
|
||||
}
|
||||
|
@ -434,7 +461,8 @@ void InlineList::paint(
|
|||
.target = image,
|
||||
});
|
||||
}
|
||||
if (bubbleProgress == 0.) {
|
||||
if (tags || bubbleProgress == 0.) {
|
||||
p.setOpacity(1.);
|
||||
continue;
|
||||
}
|
||||
resolveUserpicsImage(button);
|
||||
|
@ -479,6 +507,115 @@ void InlineList::paint(
|
|||
}
|
||||
}
|
||||
|
||||
void InlineList::validateTagBg(const QColor &color) const {
|
||||
if (!_tagBg.isNull() && _tagBgColor == color) {
|
||||
return;
|
||||
}
|
||||
_tagBgColor = color;
|
||||
|
||||
const auto padding = st::reactionInlinePadding;
|
||||
const auto size = st::reactionInlineSize;
|
||||
const auto width = padding.left()
|
||||
+ size
|
||||
+ st::reactionInlineTagSkip
|
||||
+ padding.right();
|
||||
const auto height = padding.top() + size + padding.bottom();
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
|
||||
auto mask = QImage(
|
||||
QSize(width, height) * ratio,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
mask.setDevicePixelRatio(ratio);
|
||||
|
||||
mask.fill(Qt::transparent);
|
||||
auto p = QPainter(&mask);
|
||||
|
||||
auto path = QPainterPath();
|
||||
const auto arrow = st::reactionInlineTagArrow;
|
||||
const auto rradius = st::reactionInlineTagRightRadius * 1.;
|
||||
const auto radius = st::reactionInlineTagLeftRadius - rradius;
|
||||
const auto fg = QColor(255, 255, 255);
|
||||
auto pen = QPen(fg);
|
||||
pen.setWidthF(rradius * 2.);
|
||||
pen.setJoinStyle(Qt::RoundJoin);
|
||||
const auto rect = QRectF(0, 0, width, height).marginsRemoved(
|
||||
{ rradius, rradius, rradius, rradius });
|
||||
|
||||
const auto right = rect.x() + rect.width();
|
||||
const auto bottom = rect.y() + rect.height();
|
||||
path.moveTo(rect.x() + radius, rect.y());
|
||||
path.lineTo(right - arrow, rect.y());
|
||||
path.lineTo(right, rect.y() + rect.height() / 2);
|
||||
path.lineTo(right - arrow, bottom);
|
||||
path.lineTo(rect.x() + radius, bottom);
|
||||
path.arcTo(QRectF(rect.x(), bottom - radius * 2, radius * 2, radius * 2), 270, -90);
|
||||
path.lineTo(rect.x(), rect.y() + radius);
|
||||
path.arcTo(QRectF(rect.x(), rect.y(), radius * 2, radius * 2), 180, -90);
|
||||
path.closeSubpath();
|
||||
|
||||
const auto dsize = st::reactionInlineTagDot;
|
||||
const auto dot = QRectF(
|
||||
right - st::reactionInlineTagDotSkip - dsize,
|
||||
rect.y() + (rect.height() - dsize) / 2.,
|
||||
dsize,
|
||||
dsize);
|
||||
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
p.setPen(pen);
|
||||
p.setBrush(fg);
|
||||
p.drawPath(path);
|
||||
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(QColor(255, 255, 255, 255 * 0.6));
|
||||
p.drawEllipse(dot);
|
||||
|
||||
p.end();
|
||||
|
||||
_tagBg = style::colorizeImage(mask, color);
|
||||
}
|
||||
|
||||
void InlineList::paintSingleBg(
|
||||
Painter &p,
|
||||
const QRect &fill,
|
||||
const QColor &color,
|
||||
float64 opacity) const {
|
||||
p.setOpacity(opacity);
|
||||
if (!(_data.flags & Data::Flag::Tags)) {
|
||||
const auto radius = fill.height() / 2.;
|
||||
p.setBrush(color);
|
||||
p.drawRoundedRect(fill, radius, radius);
|
||||
return;
|
||||
}
|
||||
validateTagBg(color);
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
const auto left = st::reactionInlineTagLeftRadius;
|
||||
const auto right = (_tagBg.width() / ratio) - left;
|
||||
Assert(right > 0);
|
||||
const auto useLeft = std::min(fill.width(), left);
|
||||
p.drawImage(
|
||||
QRect(fill.x(), fill.y(), useLeft, fill.height()),
|
||||
_tagBg,
|
||||
QRect(0, 0, useLeft * ratio, _tagBg.height()));
|
||||
const auto middle = fill.width() - left - right;
|
||||
if (middle > 0) {
|
||||
p.fillRect(fill.x() + left, fill.y(), middle, fill.height(), color);
|
||||
}
|
||||
if (const auto useRight = fill.width() - left; useRight > 0) {
|
||||
p.drawImage(
|
||||
QRect(
|
||||
fill.x() + fill.width() - useRight,
|
||||
fill.y(),
|
||||
useRight,
|
||||
fill.height()),
|
||||
_tagBg,
|
||||
QRect(_tagBg.width() - useRight * ratio,
|
||||
0,
|
||||
useRight * ratio,
|
||||
_tagBg.height()));
|
||||
}
|
||||
}
|
||||
|
||||
bool InlineList::getState(
|
||||
QPoint point,
|
||||
not_null<TextState*> outResult) const {
|
||||
|
@ -654,7 +791,8 @@ InlineListData InlineListDataFromMessage(not_null<Message*> message) {
|
|||
}
|
||||
}
|
||||
result.flags = (message->hasOutLayout() ? Flag::OutLayout : Flag())
|
||||
| (message->embedReactionsInBubble() ? Flag::InBubble : Flag());
|
||||
| (message->embedReactionsInBubble() ? Flag::InBubble : Flag())
|
||||
| (item->reactionsAreTags() ? Flag::Tags : Flag());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ struct InlineListData {
|
|||
InBubble = 0x01,
|
||||
OutLayout = 0x02,
|
||||
Flipped = 0x04,
|
||||
Tags = 0x08,
|
||||
};
|
||||
friend inline constexpr bool is_flag_type(Flag) { return true; };
|
||||
using Flags = base::flags<Flag>;
|
||||
|
@ -103,6 +104,7 @@ private:
|
|||
void layout();
|
||||
void layoutButtons();
|
||||
|
||||
void setButtonTag(Button &button);
|
||||
void setButtonCount(Button &button, int count);
|
||||
void setButtonUserpics(
|
||||
Button &button,
|
||||
|
@ -115,6 +117,13 @@ private:
|
|||
QPoint innerTopLeft,
|
||||
const PaintContext &context,
|
||||
const QColor &textColor) const;
|
||||
void paintSingleBg(
|
||||
Painter &p,
|
||||
const QRect &fill,
|
||||
const QColor &color,
|
||||
float64 opacity) const;
|
||||
|
||||
void validateTagBg(const QColor &color) const;
|
||||
|
||||
QSize countOptimalSize() override;
|
||||
|
||||
|
@ -124,6 +133,8 @@ private:
|
|||
Data _data;
|
||||
std::vector<Button> _buttons;
|
||||
QSize _skipBlock;
|
||||
mutable QImage _tagBg;
|
||||
mutable QColor _tagBgColor;
|
||||
mutable QImage _customCache;
|
||||
mutable int _customSkip = 0;
|
||||
bool _hasCustomEmoji = false;
|
||||
|
|
|
@ -851,6 +851,12 @@ reactionInlinePadding: margins(5px, 2px, 7px, 2px);
|
|||
reactionInlineSize: 18px;
|
||||
reactionInlineImage: 32px;
|
||||
reactionInlineSkip: 3px;
|
||||
reactionInlineTagSkip: 6px;
|
||||
reactionInlineTagLeftRadius: 6px;
|
||||
reactionInlineTagRightRadius: 3px;
|
||||
reactionInlineTagArrow: 5px;
|
||||
reactionInlineTagDot: 5px;
|
||||
reactionInlineTagDotSkip: 2px;
|
||||
reactionInlineBetween: 4px;
|
||||
reactionInlineInBubbleLeft: -3px;
|
||||
reactionInlineUserpicsPadding: margins(1px, 1px, 1px, 1px);
|
||||
|
|
|
@ -440,7 +440,7 @@ void EmptyUserpic::PaintHiddenAuthor(
|
|||
{ 0., st::premiumButtonBg2->c },
|
||||
{ 1., st::premiumButtonBg3->c },
|
||||
});
|
||||
const auto &fg = st::historyPeerUserpicFg;
|
||||
const auto &fg = st::premiumButtonFg;
|
||||
PaintHiddenAuthor(p, x, y, outerWidth, size, QBrush(bg), fg);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue