mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 13:47:05 +02:00
Support service messages reactions.
This commit is contained in:
parent
c1528f532e
commit
35e40be550
10 changed files with 319 additions and 222 deletions
|
@ -462,16 +462,16 @@ HistoryItem::HistoryItem(
|
|||
.from = data.vfrom_id() ? peerFromMTP(*data.vfrom_id()) : PeerId(0),
|
||||
.date = data.vdate().v,
|
||||
}) {
|
||||
if (data.vaction().type() != mtpc_messageActionPhoneCall) {
|
||||
createServiceFromMtp(data);
|
||||
} else {
|
||||
data.vaction().match([&](const MTPDmessageActionPhoneCall &data) {
|
||||
createComponents(CreateConfig());
|
||||
_media = std::make_unique<Data::MediaCall>(
|
||||
this,
|
||||
Data::ComputeCallData(
|
||||
data.vaction().c_messageActionPhoneCall()));
|
||||
Data::ComputeCallData(data));
|
||||
setTextValue({});
|
||||
}
|
||||
}, [&](const auto &) {
|
||||
createServiceFromMtp(data);
|
||||
});
|
||||
setReactions(data.vreactions());
|
||||
applyTTL(data);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history_item_components.h"
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "boxes/premium_preview_box.h"
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "core/click_handler_types.h"
|
||||
|
@ -30,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.h"
|
||||
#include "chat_helpers/stickers_emoji_pack.h"
|
||||
#include "payments/payments_reaction_process.h" // TryAddingPaidReaction.
|
||||
#include "window/window_session_controller.h"
|
||||
#include "ui/effects/path_shift_gradient.h"
|
||||
#include "ui/effects/reaction_fly_animation.h"
|
||||
|
@ -593,6 +595,10 @@ Element::Element(
|
|||
}
|
||||
}
|
||||
|
||||
bool Element::embedReactionsInBubble() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
not_null<ElementDelegate*> Element::delegate() const {
|
||||
return _delegate;
|
||||
}
|
||||
|
@ -1591,7 +1597,122 @@ bool Element::isSignedAuthorElided() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
void Element::setupReactions(Element *replacing) {
|
||||
refreshReactions();
|
||||
auto animations = replacing
|
||||
? replacing->takeReactionAnimations()
|
||||
: base::flat_map<
|
||||
Data::ReactionId,
|
||||
std::unique_ptr<Ui::ReactionFlyAnimation>>();
|
||||
if (!animations.empty()) {
|
||||
const auto repainter = [=] { repaint(); };
|
||||
for (const auto &[id, animation] : animations) {
|
||||
animation->setRepaintCallback(repainter);
|
||||
}
|
||||
if (_reactions) {
|
||||
_reactions->continueAnimations(std::move(animations));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Element::refreshReactions() {
|
||||
using namespace Reactions;
|
||||
auto reactionsData = InlineListDataFromMessage(this);
|
||||
if (reactionsData.reactions.empty()) {
|
||||
setReactions(nullptr);
|
||||
return;
|
||||
}
|
||||
if (!_reactions) {
|
||||
const auto handlerFactory = [=](ReactionId id) {
|
||||
const auto weak = base::make_weak(this);
|
||||
return std::make_shared<LambdaClickHandler>([=](
|
||||
ClickContext context) {
|
||||
const auto strong = weak.get();
|
||||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
const auto item = strong->data();
|
||||
const auto controller = ExtractController(context);
|
||||
if (item->reactionsAreTags()) {
|
||||
if (item->history()->session().premium()) {
|
||||
const auto tag = Data::SearchTagToQuery(id);
|
||||
HashtagClickHandler(tag).onClick(context);
|
||||
} else if (controller) {
|
||||
ShowPremiumPreviewBox(
|
||||
controller,
|
||||
PremiumFeature::TagsForMessages);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (id.paid()) {
|
||||
Payments::TryAddingPaidReaction(
|
||||
item,
|
||||
weak.get(),
|
||||
1,
|
||||
std::nullopt,
|
||||
controller->uiShow());
|
||||
return;
|
||||
} else {
|
||||
const auto source = HistoryReactionSource::Existing;
|
||||
item->toggleReaction(id, source);
|
||||
}
|
||||
if (const auto now = weak.get()) {
|
||||
const auto chosen = now->data()->chosenReactions();
|
||||
if (id.paid() || ranges::contains(chosen, id)) {
|
||||
now->animateReaction({
|
||||
.id = id,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
setReactions(std::make_unique<InlineList>(
|
||||
&history()->owner().reactions(),
|
||||
handlerFactory,
|
||||
[=] { customEmojiRepaint(); },
|
||||
std::move(reactionsData)));
|
||||
} else {
|
||||
auto was = _reactions->computeTagsList();
|
||||
_reactions->update(std::move(reactionsData), width());
|
||||
auto now = _reactions->computeTagsList();
|
||||
if (!was.empty() || !now.empty()) {
|
||||
auto &owner = history()->owner();
|
||||
owner.viewTagsChanged(this, std::move(was), std::move(now));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Element::setReactions(std::unique_ptr<Reactions::InlineList> list) {
|
||||
auto was = _reactions
|
||||
? _reactions->computeTagsList()
|
||||
: std::vector<Data::ReactionId>();
|
||||
_reactions = std::move(list);
|
||||
auto now = _reactions
|
||||
? _reactions->computeTagsList()
|
||||
: std::vector<Data::ReactionId>();
|
||||
if (!was.empty() || !now.empty()) {
|
||||
auto &owner = history()->owner();
|
||||
owner.viewTagsChanged(this, std::move(was), std::move(now));
|
||||
}
|
||||
}
|
||||
|
||||
bool Element::updateReactions() {
|
||||
const auto wasReactions = _reactions
|
||||
? _reactions->currentSize()
|
||||
: QSize();
|
||||
refreshReactions();
|
||||
const auto nowReactions = _reactions
|
||||
? _reactions->currentSize()
|
||||
: QSize();
|
||||
return (wasReactions != nowReactions);
|
||||
}
|
||||
|
||||
void Element::itemDataChanged() {
|
||||
if (updateReactions()) {
|
||||
history()->owner().requestViewResize(this);
|
||||
} else {
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void Element::itemTextUpdated() {
|
||||
|
@ -1615,6 +1736,9 @@ void Element::blockquoteExpandChanged() {
|
|||
|
||||
void Element::unloadHeavyPart() {
|
||||
history()->owner().unregisterHeavyViewPart(this);
|
||||
if (_reactions) {
|
||||
_reactions->unloadCustomEmoji();
|
||||
}
|
||||
if (_media) {
|
||||
_media->unloadHeavyPart();
|
||||
}
|
||||
|
@ -1915,9 +2039,6 @@ void Element::clickHandlerPressedChanged(
|
|||
}
|
||||
}
|
||||
|
||||
void Element::animateReaction(Ui::ReactionFlyAnimationArgs &&args) {
|
||||
}
|
||||
|
||||
void Element::animateUnreadReactions() {
|
||||
const auto &recent = data()->recentReactions();
|
||||
for (const auto &[id, list] : recent) {
|
||||
|
@ -1931,6 +2052,9 @@ auto Element::takeReactionAnimations()
|
|||
-> base::flat_map<
|
||||
Data::ReactionId,
|
||||
std::unique_ptr<Ui::ReactionFlyAnimation>> {
|
||||
if (_reactions) {
|
||||
return _reactions->takeAnimations();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -1950,6 +2074,8 @@ QRect Element::effectIconGeometry() const {
|
|||
}
|
||||
|
||||
Element::~Element() {
|
||||
setReactions(nullptr);
|
||||
|
||||
// Delete media while owner still exists.
|
||||
clearSpecialOnlyEmoji();
|
||||
base::take(_media);
|
||||
|
@ -2058,4 +2184,12 @@ int FindViewY(not_null<Element*> view, uint16 symbol, int yfrom) {
|
|||
return origin.y() + (yfrom + ytill) / 2;
|
||||
}
|
||||
|
||||
Window::SessionController *ExtractController(const ClickContext &context) {
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
if (const auto controller = my.sessionWindow.get()) {
|
||||
return controller;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -338,6 +338,8 @@ public:
|
|||
Element *replacing,
|
||||
Flag serviceFlag);
|
||||
|
||||
[[nodiscard]] virtual bool embedReactionsInBubble() const;
|
||||
|
||||
[[nodiscard]] not_null<ElementDelegate*> delegate() const;
|
||||
[[nodiscard]] not_null<HistoryItem*> data() const;
|
||||
[[nodiscard]] not_null<History*> history() const;
|
||||
|
@ -561,9 +563,9 @@ public:
|
|||
|
||||
[[nodiscard]] bool markSponsoredViewed(int shownFromTop) const;
|
||||
|
||||
virtual void animateReaction(Ui::ReactionFlyAnimationArgs &&args);
|
||||
virtual void animateReaction(Ui::ReactionFlyAnimationArgs &&args) = 0;
|
||||
void animateUnreadReactions();
|
||||
[[nodiscard]] virtual auto takeReactionAnimations()
|
||||
[[nodiscard]] auto takeReactionAnimations()
|
||||
-> base::flat_map<
|
||||
Data::ReactionId,
|
||||
std::unique_ptr<Ui::ReactionFlyAnimation>>;
|
||||
|
@ -617,6 +619,12 @@ protected:
|
|||
void clearSpecialOnlyEmoji();
|
||||
void checkSpecialOnlyEmoji();
|
||||
|
||||
void setupReactions(Element *replacing);
|
||||
void refreshReactions();
|
||||
bool updateReactions();
|
||||
|
||||
std::unique_ptr<Reactions::InlineList> _reactions;
|
||||
|
||||
private:
|
||||
// This should be called only from previousInBlocksChanged()
|
||||
// to add required bits to the Composer mask
|
||||
|
@ -641,6 +649,7 @@ private:
|
|||
void setTextWithLinks(
|
||||
const TextWithEntities &text,
|
||||
const std::vector<ClickHandlerPtr> &links = {});
|
||||
void setReactions(std::unique_ptr<Reactions::InlineList> list);
|
||||
|
||||
struct TextWithLinks {
|
||||
TextWithEntities text;
|
||||
|
@ -673,4 +682,7 @@ private:
|
|||
uint16 symbol,
|
||||
int yfrom = 0);
|
||||
|
||||
[[nodiscard]] Window::SessionController *ExtractController(
|
||||
const ClickContext &context);
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -38,7 +38,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "lang/lang_keys.h"
|
||||
#include "mainwidget.h"
|
||||
#include "main/main_session.h"
|
||||
#include "payments/payments_reaction_process.h" // TryAddingPaidReaction.
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/painter.h"
|
||||
#include "window/themes/window_theme.h" // IsNightMode.
|
||||
|
@ -54,15 +53,6 @@ namespace {
|
|||
constexpr auto kPlayStatusLimit = 2;
|
||||
const auto kPsaTooltipPrefix = "cloud_lng_tooltip_psa_";
|
||||
|
||||
[[nodiscard]] Window::SessionController *ExtractController(
|
||||
const ClickContext &context) {
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
if (const auto controller = my.sessionWindow.get()) {
|
||||
return controller;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
class KeyboardStyle : public ReplyKeyboard::Style {
|
||||
public:
|
||||
KeyboardStyle(const style::BotKeyboardButton &st);
|
||||
|
@ -423,22 +413,8 @@ Message::Message(
|
|||
}
|
||||
initLogEntryOriginal();
|
||||
initPsa();
|
||||
refreshReactions();
|
||||
auto animations = replacing
|
||||
? replacing->takeReactionAnimations()
|
||||
: base::flat_map<
|
||||
Data::ReactionId,
|
||||
std::unique_ptr<Ui::ReactionFlyAnimation>>();
|
||||
setupReactions(replacing);
|
||||
auto animation = replacing ? replacing->takeEffectAnimation() : nullptr;
|
||||
if (!animations.empty()) {
|
||||
const auto repainter = [=] { repaint(); };
|
||||
for (const auto &[id, animation] : animations) {
|
||||
animation->setRepaintCallback(repainter);
|
||||
}
|
||||
if (_reactions) {
|
||||
_reactions->continueAnimations(std::move(animations));
|
||||
}
|
||||
}
|
||||
if (animation) {
|
||||
_bottomInfo.continueEffectAnimation(std::move(animation));
|
||||
}
|
||||
|
@ -461,21 +437,6 @@ Message::~Message() {
|
|||
_fromNameStatus = nullptr;
|
||||
checkHeavyPart();
|
||||
}
|
||||
setReactions(nullptr);
|
||||
}
|
||||
|
||||
void Message::setReactions(std::unique_ptr<Reactions::InlineList> list) {
|
||||
auto was = _reactions
|
||||
? _reactions->computeTagsList()
|
||||
: std::vector<Data::ReactionId>();
|
||||
_reactions = std::move(list);
|
||||
auto now = _reactions
|
||||
? _reactions->computeTagsList()
|
||||
: std::vector<Data::ReactionId>();
|
||||
if (!was.empty() || !now.empty()) {
|
||||
auto &owner = history()->owner();
|
||||
owner.viewTagsChanged(this, std::move(was), std::move(now));
|
||||
}
|
||||
}
|
||||
|
||||
void Message::refreshRightBadge() {
|
||||
|
@ -694,16 +655,6 @@ void Message::animateEffect(Ui::ReactionFlyAnimationArgs &&args) {
|
|||
}
|
||||
}
|
||||
|
||||
auto Message::takeReactionAnimations()
|
||||
-> base::flat_map<
|
||||
Data::ReactionId,
|
||||
std::unique_ptr<Ui::ReactionFlyAnimation>> {
|
||||
if (_reactions) {
|
||||
return _reactions->takeAnimations();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Message::takeEffectAnimation()
|
||||
-> std::unique_ptr<Ui::ReactionFlyAnimation> {
|
||||
return _bottomInfo.takeEffectAnimation();
|
||||
|
@ -2425,9 +2376,6 @@ bool Message::hasHeavyPart() const {
|
|||
|
||||
void Message::unloadHeavyPart() {
|
||||
Element::unloadHeavyPart();
|
||||
if (_reactions) {
|
||||
_reactions->unloadCustomEmoji();
|
||||
}
|
||||
_comments = nullptr;
|
||||
if (_fromNameStatus) {
|
||||
_fromNameStatus->custom = nullptr;
|
||||
|
@ -3446,73 +3394,6 @@ bool Message::embedReactionsInBubble() const {
|
|||
return needInfoDisplay();
|
||||
}
|
||||
|
||||
void Message::refreshReactions() {
|
||||
using namespace Reactions;
|
||||
auto reactionsData = InlineListDataFromMessage(this);
|
||||
if (reactionsData.reactions.empty()) {
|
||||
setReactions(nullptr);
|
||||
return;
|
||||
}
|
||||
if (!_reactions) {
|
||||
const auto handlerFactory = [=](ReactionId id) {
|
||||
const auto weak = base::make_weak(this);
|
||||
return std::make_shared<LambdaClickHandler>([=](
|
||||
ClickContext context) {
|
||||
const auto strong = weak.get();
|
||||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
const auto item = strong->data();
|
||||
const auto controller = ExtractController(context);
|
||||
if (item->reactionsAreTags()) {
|
||||
if (item->history()->session().premium()) {
|
||||
const auto tag = Data::SearchTagToQuery(id);
|
||||
HashtagClickHandler(tag).onClick(context);
|
||||
} else if (controller) {
|
||||
ShowPremiumPreviewBox(
|
||||
controller,
|
||||
PremiumFeature::TagsForMessages);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (id.paid()) {
|
||||
Payments::TryAddingPaidReaction(
|
||||
item,
|
||||
weak.get(),
|
||||
1,
|
||||
std::nullopt,
|
||||
controller->uiShow());
|
||||
return;
|
||||
} else {
|
||||
const auto source = HistoryReactionSource::Existing;
|
||||
item->toggleReaction(id, source);
|
||||
}
|
||||
if (const auto now = weak.get()) {
|
||||
const auto chosen = now->data()->chosenReactions();
|
||||
if (id.paid() || ranges::contains(chosen, id)) {
|
||||
now->animateReaction({
|
||||
.id = id,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
setReactions(std::make_unique<InlineList>(
|
||||
&history()->owner().reactions(),
|
||||
handlerFactory,
|
||||
[=] { customEmojiRepaint(); },
|
||||
std::move(reactionsData)));
|
||||
} else {
|
||||
auto was = _reactions->computeTagsList();
|
||||
_reactions->update(std::move(reactionsData), width());
|
||||
auto now = _reactions->computeTagsList();
|
||||
if (!was.empty() || !now.empty()) {
|
||||
auto &owner = history()->owner();
|
||||
owner.viewTagsChanged(this, std::move(was), std::move(now));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Message::validateInlineKeyboard(HistoryMessageReplyMarkup *markup) {
|
||||
if (!markup
|
||||
|| markup->inlineKeyboard
|
||||
|
@ -3554,18 +3435,17 @@ void Message::validateFromNameText(PeerData *from) const {
|
|||
}
|
||||
}
|
||||
|
||||
void Message::itemDataChanged() {
|
||||
bool Message::updateBottomInfo() {
|
||||
const auto wasInfo = _bottomInfo.currentSize();
|
||||
const auto wasReactions = _reactions
|
||||
? _reactions->currentSize()
|
||||
: QSize();
|
||||
refreshReactions();
|
||||
_bottomInfo.update(BottomInfoDataFromMessage(this), width());
|
||||
const auto nowInfo = _bottomInfo.currentSize();
|
||||
const auto nowReactions = _reactions
|
||||
? _reactions->currentSize()
|
||||
: QSize();
|
||||
if (wasInfo != nowInfo || wasReactions != nowReactions) {
|
||||
return (_bottomInfo.currentSize() != wasInfo);
|
||||
}
|
||||
|
||||
void Message::itemDataChanged() {
|
||||
const auto infoChanged = updateBottomInfo();
|
||||
const auto reactionsChanged = updateReactions();
|
||||
|
||||
if (infoChanged || reactionsChanged) {
|
||||
history()->owner().requestViewResize(this);
|
||||
} else {
|
||||
repaint();
|
||||
|
|
|
@ -78,7 +78,7 @@ public:
|
|||
[[nodiscard]] const HistoryMessageEdited *displayedEditBadge() const;
|
||||
[[nodiscard]] HistoryMessageEdited *displayedEditBadge();
|
||||
|
||||
[[nodiscard]] bool embedReactionsInBubble() const;
|
||||
bool embedReactionsInBubble() const override;
|
||||
|
||||
int marginTop() const override;
|
||||
int marginBottom() const override;
|
||||
|
@ -159,10 +159,6 @@ public:
|
|||
const base::flat_set<UserId> &changes) override;
|
||||
|
||||
void animateReaction(Ui::ReactionFlyAnimationArgs &&args) override;
|
||||
auto takeReactionAnimations()
|
||||
-> base::flat_map<
|
||||
Data::ReactionId,
|
||||
std::unique_ptr<Ui::ReactionFlyAnimation>> override;
|
||||
|
||||
void animateEffect(Ui::ReactionFlyAnimationArgs &&args) override;
|
||||
auto takeEffectAnimation()
|
||||
|
@ -180,6 +176,8 @@ private:
|
|||
struct FromNameStatus;
|
||||
struct RightAction;
|
||||
|
||||
bool updateBottomInfo();
|
||||
|
||||
void initLogEntryOriginal();
|
||||
void initPsa();
|
||||
void fromNameUpdated(int width) const;
|
||||
|
@ -300,15 +298,12 @@ private:
|
|||
[[nodiscard]] ClickHandlerPtr psaTooltipLink() const;
|
||||
void psaTooltipToggled(bool shown) const;
|
||||
|
||||
void setReactions(std::unique_ptr<Reactions::InlineList> list);
|
||||
void refreshRightBadge();
|
||||
void refreshReactions();
|
||||
void validateFromNameText(PeerData *from) const;
|
||||
|
||||
mutable std::unique_ptr<RightAction> _rightAction;
|
||||
mutable ClickHandlerPtr _fastReplyLink;
|
||||
mutable std::unique_ptr<ViewButton> _viewButton;
|
||||
std::unique_ptr<Reactions::InlineList> _reactions;
|
||||
std::unique_ptr<TopicButton> _topicButton;
|
||||
mutable std::unique_ptr<CommentsButton> _comments;
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/history_view_service_message.h"
|
||||
|
||||
#include "history/view/media/history_view_media.h"
|
||||
#include "history/view/reactions/history_view_reactions.h"
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
|
@ -18,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_channel.h"
|
||||
#include "info/profile/info_profile_cover.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/effects/reaction_fly_animation.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/power_saving.h"
|
||||
|
@ -405,6 +407,7 @@ Service::Service(
|
|||
not_null<HistoryItem*> data,
|
||||
Element *replacing)
|
||||
: Element(delegate, data, replacing, Flag::ServiceMessage) {
|
||||
setupReactions(replacing);
|
||||
}
|
||||
|
||||
QRect Service::innerGeometry() const {
|
||||
|
@ -423,7 +426,27 @@ QRect Service::countGeometry() const {
|
|||
if (delegate()->elementIsChatWide()) {
|
||||
result.setWidth(qMin(result.width(), st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left()));
|
||||
}
|
||||
return result.marginsRemoved(st::msgServiceMargin);
|
||||
auto margins = st::msgServiceMargin;
|
||||
margins.setTop(marginTop());
|
||||
return result.marginsRemoved(margins);
|
||||
}
|
||||
|
||||
void Service::animateReaction(Ui::ReactionFlyAnimationArgs &&args) {
|
||||
const auto item = data();
|
||||
|
||||
auto g = countGeometry();
|
||||
if (g.width() < 1 || isHidden()) {
|
||||
return;
|
||||
}
|
||||
const auto repainter = [=] { repaint(); };
|
||||
|
||||
if (_reactions) {
|
||||
const auto reactionsHeight = st::mediaInBubbleSkip + _reactions->height();
|
||||
const auto reactionsLeft = 0;
|
||||
g.setHeight(g.height() - reactionsHeight);
|
||||
const auto reactionsPosition = QPoint(reactionsLeft + g.left(), g.top() + g.height() + st::mediaInBubbleSkip);
|
||||
_reactions->animate(args.translated(-reactionsPosition), repainter);
|
||||
}
|
||||
}
|
||||
|
||||
QSize Service::performCountCurrentSize(int newWidth) {
|
||||
|
@ -439,12 +462,12 @@ QSize Service::performCountCurrentSize(int newWidth) {
|
|||
}
|
||||
const auto media = this->media();
|
||||
const auto mediaDisplayed = media && media->isDisplayed();
|
||||
auto contentWidth = newWidth;
|
||||
if (mediaDisplayed && media->hideServiceText()) {
|
||||
newHeight += st::msgServiceMargin.top()
|
||||
+ media->resizeGetHeight(newWidth)
|
||||
+ st::msgServiceMargin.bottom();
|
||||
} else if (!text().isEmpty()) {
|
||||
auto contentWidth = newWidth;
|
||||
if (delegate()->elementIsChatWide()) {
|
||||
accumulate_min(contentWidth, st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left());
|
||||
}
|
||||
|
@ -465,12 +488,24 @@ QSize Service::performCountCurrentSize(int newWidth) {
|
|||
}
|
||||
}
|
||||
|
||||
if (_reactions) {
|
||||
newHeight += st::mediaInBubbleSkip
|
||||
+ _reactions->resizeGetHeight(contentWidth);
|
||||
if (hasRightLayout()) {
|
||||
_reactions->flipToRight();
|
||||
}
|
||||
}
|
||||
|
||||
return { newWidth, newHeight };
|
||||
}
|
||||
|
||||
QSize Service::performCountOptimalSize() {
|
||||
validateText();
|
||||
|
||||
if (_reactions) {
|
||||
_reactions->initDimensions();
|
||||
}
|
||||
|
||||
if (const auto media = this->media()) {
|
||||
media->initDimensions();
|
||||
if (media->hideServiceText()) {
|
||||
|
@ -487,7 +522,12 @@ bool Service::isHidden() const {
|
|||
}
|
||||
|
||||
int Service::marginTop() const {
|
||||
return st::msgServiceMargin.top();
|
||||
auto result = st::msgServiceMargin.top();
|
||||
result += displayedDateHeight();
|
||||
if (const auto bar = Get<UnreadBar>()) {
|
||||
result += bar->height();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int Service::marginBottom() const {
|
||||
|
@ -499,42 +539,32 @@ void Service::draw(Painter &p, const PaintContext &context) const {
|
|||
if (g.width() < 1) {
|
||||
return;
|
||||
}
|
||||
const auto &margin = st::msgServiceMargin;
|
||||
|
||||
const auto st = context.st;
|
||||
auto height = this->height() - margin.top() - margin.bottom();
|
||||
auto dateh = 0;
|
||||
auto unreadbarh = 0;
|
||||
auto clip = context.clip;
|
||||
if (auto date = Get<DateBadge>()) {
|
||||
dateh = date->height();
|
||||
p.translate(0, dateh);
|
||||
clip.translate(0, -dateh);
|
||||
height -= dateh;
|
||||
}
|
||||
if (const auto bar = Get<UnreadBar>()) {
|
||||
unreadbarh = bar->height();
|
||||
if (clip.intersects(QRect(0, 0, width(), unreadbarh))) {
|
||||
auto unreadbarh = bar->height();
|
||||
auto dateh = 0;
|
||||
if (const auto date = Get<DateBadge>()) {
|
||||
dateh = date->height();
|
||||
}
|
||||
if (context.clip.intersects(QRect(0, dateh, width(), unreadbarh))) {
|
||||
p.translate(0, dateh);
|
||||
bar->paint(
|
||||
p,
|
||||
context,
|
||||
0,
|
||||
width(),
|
||||
delegate()->elementIsChatWide());
|
||||
p.translate(0, -dateh);
|
||||
}
|
||||
p.translate(0, unreadbarh);
|
||||
clip.translate(0, -unreadbarh);
|
||||
height -= unreadbarh;
|
||||
}
|
||||
|
||||
if (isHidden()) {
|
||||
if (auto skiph = dateh + unreadbarh) {
|
||||
p.translate(0, -skiph);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
paintHighlight(p, context, height);
|
||||
paintHighlight(p, context, g.height());
|
||||
|
||||
p.setTextPalette(st->serviceTextPalette());
|
||||
|
||||
|
@ -542,13 +572,26 @@ void Service::draw(Painter &p, const PaintContext &context) const {
|
|||
const auto mediaDisplayed = media && media->isDisplayed();
|
||||
const auto onlyMedia = (mediaDisplayed && media->hideServiceText());
|
||||
|
||||
if (!onlyMedia) {
|
||||
if (mediaDisplayed) {
|
||||
height -= margin.top() + media->height();
|
||||
if (_reactions) {
|
||||
const auto reactionsHeight = st::mediaInBubbleSkip + _reactions->height();
|
||||
const auto reactionsLeft = 0;
|
||||
g.setHeight(g.height() - reactionsHeight);
|
||||
const auto reactionsPosition = QPoint(reactionsLeft + g.left(), g.top() + g.height() + st::mediaInBubbleSkip);
|
||||
p.translate(reactionsPosition);
|
||||
prepareCustomEmojiPaint(p, context, *_reactions);
|
||||
_reactions->paint(p, context, g.width(), context.clip.translated(-reactionsPosition));
|
||||
if (context.reactionInfo) {
|
||||
context.reactionInfo->position = reactionsPosition;
|
||||
}
|
||||
const auto trect = QRect(g.left(), margin.top(), g.width(), height)
|
||||
p.translate(-reactionsPosition);
|
||||
}
|
||||
|
||||
if (!onlyMedia) {
|
||||
const auto mediaSkip = mediaDisplayed ? (st::msgServiceMargin.top() + media->height()) : 0;
|
||||
const auto trect = QRect(g.left(), g.top(), g.width(), g.height() - mediaSkip)
|
||||
- st::msgServicePadding;
|
||||
|
||||
p.translate(0, g.top() - st::msgServiceMargin.top());
|
||||
ServiceMessagePainter::PaintComplexBubble(
|
||||
p,
|
||||
context.st,
|
||||
|
@ -556,6 +599,7 @@ void Service::draw(Painter &p, const PaintContext &context) const {
|
|||
g.width(),
|
||||
text(),
|
||||
trect);
|
||||
p.translate(0, -g.top() + st::msgServiceMargin.top());
|
||||
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.setPen(st->msgServiceFg());
|
||||
|
@ -575,15 +619,12 @@ void Service::draw(Painter &p, const PaintContext &context) const {
|
|||
});
|
||||
}
|
||||
if (mediaDisplayed) {
|
||||
const auto left = margin.left() + (g.width() - media->width()) / 2;
|
||||
const auto top = margin.top() + (onlyMedia ? 0 : (height + margin.top()));
|
||||
p.translate(left, top);
|
||||
media->draw(p, context.translated(-left, -top).withSelection({}));
|
||||
p.translate(-left, -top);
|
||||
}
|
||||
|
||||
if (auto skiph = dateh + unreadbarh) {
|
||||
p.translate(0, -skiph);
|
||||
const auto left = g.left() + (g.width() - media->width()) / 2;
|
||||
const auto top = g.top() + (onlyMedia ? 0 : (g.height() - media->height()));
|
||||
const auto position = QPoint(left, top);
|
||||
p.translate(position);
|
||||
media->draw(p, context.translated(-position).withSelection({}));
|
||||
p.translate(-position);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -596,12 +637,6 @@ PointState Service::pointState(QPoint point) const {
|
|||
return PointState::Outside;
|
||||
}
|
||||
|
||||
if (const auto dateh = displayedDateHeight()) {
|
||||
g.setTop(g.top() + dateh);
|
||||
}
|
||||
if (const auto bar = Get<UnreadBar>()) {
|
||||
g.setTop(g.top() + bar->height());
|
||||
}
|
||||
if (mediaDisplayed) {
|
||||
const auto centerPadding = (g.width() - media->width()) / 2;
|
||||
const auto r = g - QMargins(centerPadding, 0, centerPadding, 0);
|
||||
|
@ -626,24 +661,26 @@ TextState Service::textState(QPoint point, StateRequest request) const {
|
|||
return result;
|
||||
}
|
||||
|
||||
if (const auto dateh = displayedDateHeight()) {
|
||||
point.setY(point.y() - dateh);
|
||||
g.setHeight(g.height() - dateh);
|
||||
}
|
||||
if (const auto bar = Get<UnreadBar>()) {
|
||||
auto unreadbarh = bar->height();
|
||||
point.setY(point.y() - unreadbarh);
|
||||
g.setHeight(g.height() - unreadbarh);
|
||||
if (_reactions) {
|
||||
const auto reactionsHeight = st::mediaInBubbleSkip + _reactions->height();
|
||||
const auto reactionsLeft = 0;
|
||||
g.setHeight(g.height() - reactionsHeight);
|
||||
const auto reactionsPosition = QPoint(reactionsLeft + g.left(), g.top() + g.height() + st::mediaInBubbleSkip);
|
||||
if (_reactions->getState(point - reactionsPosition, &result)) {
|
||||
//result.symbol += visibleMediaTextLen + visibleTextLen;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (onlyMedia) {
|
||||
return media->textState(point - QPoint(st::msgServiceMargin.left() + (g.width() - media->width()) / 2, st::msgServiceMargin.top()), request);
|
||||
return media->textState(point - QPoint(st::msgServiceMargin.left() + (g.width() - media->width()) / 2, g.top()), request);
|
||||
} else if (mediaDisplayed) {
|
||||
g.setHeight(g.height() - (st::msgServiceMargin.top() + media->height()));
|
||||
}
|
||||
const auto mediaLeft = st::msgServiceMargin.left()
|
||||
+ (media ? ((g.width() - media->width()) / 2) : 0);
|
||||
const auto mediaTop = st::msgServiceMargin.top()
|
||||
const auto mediaTop = g.top()
|
||||
+ g.height()
|
||||
+ st::msgServiceMargin.top();
|
||||
const auto mediaPoint = point - QPoint(mediaLeft, mediaTop);
|
||||
|
|
|
@ -54,6 +54,8 @@ public:
|
|||
|
||||
bool consumeHorizontalScroll(QPoint position, int delta) override;
|
||||
|
||||
void animateReaction(Ui::ReactionFlyAnimationArgs &&args) override;
|
||||
|
||||
private:
|
||||
[[nodiscard]] QRect countGeometry() const;
|
||||
|
||||
|
|
|
@ -225,11 +225,10 @@ void ServiceBox::draw(Painter &p, const PaintContext &context) const {
|
|||
|
||||
TextState ServiceBox::textState(QPoint point, StateRequest request) const {
|
||||
auto result = TextState(_parent);
|
||||
point.setY(point.y() - st::msgServiceGiftBoxTopSkip);
|
||||
const auto content = contentRect();
|
||||
const auto lookupSubtitleLink = [&] {
|
||||
auto top = st::msgServiceGiftBoxTopSkip
|
||||
+ content.top()
|
||||
+ content.height();
|
||||
auto top = content.top() + content.height();
|
||||
const auto &padding = st::msgServiceGiftBoxTitlePadding;
|
||||
top += padding.top();
|
||||
if (!_title.isEmpty()) {
|
||||
|
|
|
@ -180,6 +180,21 @@ void InlineList::layoutButtons() {
|
|||
_buttons = std::move(buttons);
|
||||
}
|
||||
|
||||
InlineList::Dimension InlineList::countDimension(int width) const {
|
||||
using Flag = InlineListData::Flag;
|
||||
const auto inBubble = (_data.flags & Flag::InBubble);
|
||||
const auto centered = (_data.flags & Flag::Centered);
|
||||
const auto useWidth = centered
|
||||
? std::min(width, st::chatGiveawayWidth)
|
||||
: width;
|
||||
const auto left = inBubble
|
||||
? st::reactionInlineInBubbleLeft
|
||||
: centered
|
||||
? ((width - useWidth) / 2)
|
||||
: 0;
|
||||
return { .left = left, .width = useWidth };
|
||||
}
|
||||
|
||||
InlineList::Button InlineList::prepareButtonWithId(const ReactionId &id) {
|
||||
auto result = Button{ .id = id, .paid = id.paid()};
|
||||
if (const auto customId = id.custom()) {
|
||||
|
@ -258,9 +273,7 @@ QSize InlineList::countOptimalSize() {
|
|||
if (_buttons.empty()) {
|
||||
return _skipBlock;
|
||||
}
|
||||
const auto left = (_data.flags & InlineListData::Flag::InBubble)
|
||||
? st::reactionInlineInBubbleLeft
|
||||
: 0;
|
||||
const auto left = countDimension(width()).left;
|
||||
auto x = left;
|
||||
const auto between = st::reactionInlineBetween;
|
||||
const auto padding = st::reactionInlinePadding;
|
||||
|
@ -308,23 +321,42 @@ QSize InlineList::countCurrentSize(int newWidth) {
|
|||
}
|
||||
using Flag = InlineListData::Flag;
|
||||
const auto between = st::reactionInlineBetween;
|
||||
const auto inBubble = (_data.flags & Flag::InBubble);
|
||||
const auto left = inBubble ? st::reactionInlineInBubbleLeft : 0;
|
||||
const auto dimension = countDimension(newWidth);
|
||||
const auto left = dimension.left;
|
||||
const auto width = dimension.width;
|
||||
const auto centered = (_data.flags & Flag::Centered);
|
||||
auto x = left;
|
||||
auto y = 0;
|
||||
for (auto &button : _buttons) {
|
||||
const auto recenter = [&](int beforeIndex) {
|
||||
const auto added = centered ? (left + width + between - x) : 0;
|
||||
if (added <= 0) {
|
||||
return;
|
||||
}
|
||||
const auto shift = added / 2;
|
||||
for (auto j = beforeIndex; j != 0;) {
|
||||
auto &button = _buttons[--j];
|
||||
if (button.geometry.y() != y) {
|
||||
break;
|
||||
}
|
||||
button.geometry.translate(shift, 0);
|
||||
}
|
||||
};
|
||||
for (auto i = 0, count = int(_buttons.size()); i != count; ++i) {
|
||||
auto &button = _buttons[i];
|
||||
const auto size = button.geometry.size();
|
||||
if (x > left && x + size.width() > newWidth) {
|
||||
if (x > left && x + size.width() > left + width) {
|
||||
recenter(i);
|
||||
x = left;
|
||||
y += size.height() + between;
|
||||
}
|
||||
button.geometry = QRect(QPoint(x, y), size);
|
||||
x += size.width() + between;
|
||||
}
|
||||
recenter(_buttons.size());
|
||||
const auto &last = _buttons.back().geometry;
|
||||
const auto height = y + last.height();
|
||||
const auto right = last.x() + last.width() + _skipBlock.width();
|
||||
const auto add = (right > newWidth) ? _skipBlock.height() : 0;
|
||||
const auto add = (right > width) ? _skipBlock.height() : 0;
|
||||
return { newWidth, height + add };
|
||||
}
|
||||
|
||||
|
@ -667,10 +699,9 @@ void InlineList::paintSingleBg(
|
|||
bool InlineList::getState(
|
||||
QPoint point,
|
||||
not_null<TextState*> outResult) const {
|
||||
const auto left = (_data.flags & InlineListData::Flag::InBubble)
|
||||
? st::reactionInlineInBubbleLeft
|
||||
: 0;
|
||||
if (!QRect(left, 0, width() - left, height()).contains(point)) {
|
||||
const auto dimension = countDimension(width());
|
||||
const auto left = dimension.left;
|
||||
if (!QRect(left, 0, dimension.width, height()).contains(point)) {
|
||||
return false;
|
||||
}
|
||||
for (const auto &button : _buttons) {
|
||||
|
@ -792,9 +823,9 @@ void InlineList::continueAnimations(base::flat_map<
|
|||
}
|
||||
}
|
||||
|
||||
InlineListData InlineListDataFromMessage(not_null<Message*> message) {
|
||||
InlineListData InlineListDataFromMessage(not_null<Element*> view) {
|
||||
using Flag = InlineListData::Flag;
|
||||
const auto item = message->data();
|
||||
const auto item = view->data();
|
||||
auto result = InlineListData();
|
||||
result.reactions = item->reactionsWithLocal();
|
||||
if (const auto user = item->history()->peer->asUser()) {
|
||||
|
@ -838,9 +869,10 @@ InlineListData InlineListDataFromMessage(not_null<Message*> message) {
|
|||
}
|
||||
}
|
||||
}
|
||||
result.flags = (message->hasOutLayout() ? Flag::OutLayout : Flag())
|
||||
| (message->embedReactionsInBubble() ? Flag::InBubble : Flag())
|
||||
| (item->reactionsAreTags() ? Flag::Tags : Flag());
|
||||
result.flags = (view->hasOutLayout() ? Flag::OutLayout : Flag())
|
||||
| (view->embedReactionsInBubble() ? Flag::InBubble : Flag())
|
||||
| (item->reactionsAreTags() ? Flag::Tags : Flag())
|
||||
| (item->isService() ? Flag::Centered : Flag());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ class CustomEmoji;
|
|||
|
||||
namespace HistoryView {
|
||||
using PaintContext = Ui::ChatPaintContext;
|
||||
class Message;
|
||||
class Element;
|
||||
struct TextState;
|
||||
struct UserpicInRow;
|
||||
} // namespace HistoryView
|
||||
|
@ -42,6 +42,7 @@ struct InlineListData {
|
|||
OutLayout = 0x02,
|
||||
Flipped = 0x04,
|
||||
Tags = 0x08,
|
||||
Centered = 0x10,
|
||||
};
|
||||
friend inline constexpr bool is_flag_type(Flag) { return true; };
|
||||
using Flags = base::flags<Flag>;
|
||||
|
@ -99,6 +100,10 @@ public:
|
|||
[[nodiscard]] static QImage PrepareTagBg(QColor tagBg, QColor dotBg);
|
||||
|
||||
private:
|
||||
struct Dimension {
|
||||
int left = 0;
|
||||
int width = 0;
|
||||
};
|
||||
struct Userpics {
|
||||
QImage image;
|
||||
std::vector<UserpicInRow> list;
|
||||
|
@ -131,6 +136,7 @@ private:
|
|||
void validateTagBg(const QColor &color) const;
|
||||
|
||||
QSize countOptimalSize() override;
|
||||
[[nodiscard]] Dimension countDimension(int width) const;
|
||||
|
||||
const not_null<::Data::Reactions*> _owner;
|
||||
const Fn<ClickHandlerPtr(ReactionId)> _handlerFactory;
|
||||
|
@ -147,7 +153,7 @@ private:
|
|||
};
|
||||
|
||||
[[nodiscard]] InlineListData InlineListDataFromMessage(
|
||||
not_null<Message*> message);
|
||||
not_null<Element*> view);
|
||||
|
||||
[[nodiscard]] ReactionId ReactionIdOfLink(const ClickHandlerPtr &link);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue