Show applied boosts in message bubbles.

This commit is contained in:
John Preston 2024-02-02 17:08:56 +04:00
parent e32cbf468b
commit 33207b78d5
11 changed files with 108 additions and 44 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -1118,6 +1118,8 @@ void ApplyChannelUpdate(
if (stickersChanged) { if (stickersChanged) {
session->changes().peerUpdated(channel, UpdateFlag::StickersSet); session->changes().peerUpdated(channel, UpdateFlag::StickersSet);
} }
channel->mgInfo->boostsApplied = update.vboosts_applied().value_or_empty();
} }
channel->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty())); channel->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty()));
channel->setTranslationDisabled(update.is_translations_disabled()); channel->setTranslationDisabled(update.is_translations_disabled());

View file

@ -132,6 +132,7 @@ public:
}; };
mutable int lastParticipantsStatus = LastParticipantsUpToDate; mutable int lastParticipantsStatus = LastParticipantsUpToDate;
int lastParticipantsCount = 0; int lastParticipantsCount = 0;
int boostsApplied = 0;
private: private:
ChatData *_migratedFrom = nullptr; ChatData *_migratedFrom = nullptr;

View file

@ -139,6 +139,7 @@ struct HistoryItem::CreateConfig {
UserId viaBotId = 0; UserId viaBotId = 0;
int viewsCount = -1; int viewsCount = -1;
int forwardsCount = -1; int forwardsCount = -1;
int boostsApplied = 0;
QString postAuthor; QString postAuthor;
MsgId originalId = 0; MsgId originalId = 0;
@ -352,6 +353,8 @@ HistoryItem::HistoryItem(
FlagsFromMTP(id, data.vflags().v, localFlags), FlagsFromMTP(id, data.vflags().v, localFlags),
data.vdate().v, data.vdate().v,
data.vfrom_id() ? peerFromMTP(*data.vfrom_id()) : PeerId(0)) { data.vfrom_id() ? peerFromMTP(*data.vfrom_id()) : PeerId(0)) {
_boostsApplied = data.vfrom_boosts_applied().value_or_empty();
const auto media = data.vmedia(); const auto media = data.vmedia();
const auto checked = media const auto checked = media
? CheckMessageMedia(*media) ? CheckMessageMedia(*media)
@ -1474,8 +1477,9 @@ void HistoryItem::returnSavedMedia() {
return; return;
} }
const auto wasGrouped = history()->owner().groups().isGrouped(this); const auto wasGrouped = history()->owner().groups().isGrouped(this);
_media = std::move(_savedLocalEditMediaData->media); const auto data = Get<HistoryMessageSavedMediaData>();
setText(_savedLocalEditMediaData->text); _media = std::move(data->media);
setText(data->text);
clearSavedMedia(); clearSavedMedia();
if (wasGrouped) { if (wasGrouped) {
history()->owner().groups().refreshMessage(this, true); history()->owner().groups().refreshMessage(this, true);
@ -1488,19 +1492,18 @@ void HistoryItem::returnSavedMedia() {
void HistoryItem::savePreviousMedia() { void HistoryItem::savePreviousMedia() {
Expects(_media != nullptr); Expects(_media != nullptr);
using Data = SavedMediaData; AddComponents(HistoryMessageSavedMediaData::Bit());
_savedLocalEditMediaData = std::make_unique<Data>(Data{ const auto data = Get<HistoryMessageSavedMediaData>();
.text = originalText(), data->text = originalText();
.media = _media->clone(this), data->media = _media->clone(this);
});
} }
bool HistoryItem::isEditingMedia() const { bool HistoryItem::isEditingMedia() const {
return _savedLocalEditMediaData != nullptr; return Has<HistoryMessageSavedMediaData>();
} }
void HistoryItem::clearSavedMedia() { void HistoryItem::clearSavedMedia() {
_savedLocalEditMediaData = nullptr; RemoveComponents(HistoryMessageSavedMediaData::Bit());
} }
bool HistoryItem::definesReplyKeyboard() const { bool HistoryItem::definesReplyKeyboard() const {
@ -1652,9 +1655,10 @@ void HistoryItem::applyEdition(HistoryMessageEdition &&edition) {
// } // }
//} //}
const auto editingMedia = isEditingMedia();
const auto updatingSavedLocalEdit = !edition.savePreviousMedia const auto updatingSavedLocalEdit = !edition.savePreviousMedia
&& (_savedLocalEditMediaData != nullptr); && editingMedia;
if (!_savedLocalEditMediaData && edition.savePreviousMedia) { if (!editingMedia && edition.savePreviousMedia) {
savePreviousMedia(); savePreviousMedia();
} }
Assert(!updatingSavedLocalEdit || !isLocalUpdateMedia()); Assert(!updatingSavedLocalEdit || !isLocalUpdateMedia());
@ -1683,7 +1687,7 @@ void HistoryItem::applyEdition(HistoryMessageEdition &&edition) {
setReplyMarkup(base::take(edition.replyMarkup)); setReplyMarkup(base::take(edition.replyMarkup));
} }
if (updatingSavedLocalEdit) { if (updatingSavedLocalEdit) {
_savedLocalEditMediaData->media = edition.mtpMedia Get<HistoryMessageSavedMediaData>()->media = edition.mtpMedia
? CreateMedia(this, *edition.mtpMedia) ? CreateMedia(this, *edition.mtpMedia)
: nullptr; : nullptr;
} else { } else {
@ -1700,13 +1704,13 @@ void HistoryItem::applyEdition(HistoryMessageEdition &&edition) {
setForwardsCount(edition.forwards); setForwardsCount(edition.forwards);
} }
const auto &checkedMedia = updatingSavedLocalEdit const auto &checkedMedia = updatingSavedLocalEdit
? _savedLocalEditMediaData->media ? Get<HistoryMessageSavedMediaData>()->media
: _media; : _media;
auto updatedText = checkedMedia auto updatedText = checkedMedia
? edition.textWithEntities ? edition.textWithEntities
: EnsureNonEmpty(edition.textWithEntities); : EnsureNonEmpty(edition.textWithEntities);
if (updatingSavedLocalEdit) { if (updatingSavedLocalEdit) {
_savedLocalEditMediaData->text = std::move(updatedText); Get<HistoryMessageSavedMediaData>()->text = std::move(updatedText);
} else { } else {
setText(std::move(updatedText)); setText(std::move(updatedText));
addToSharedMediaIndex(); addToSharedMediaIndex();
@ -1866,7 +1870,7 @@ void HistoryItem::applySentMessage(
void HistoryItem::updateSentContent( void HistoryItem::updateSentContent(
const TextWithEntities &textWithEntities, const TextWithEntities &textWithEntities,
const MTPMessageMedia *media) { const MTPMessageMedia *media) {
if (_savedLocalEditMediaData) { if (isEditingMedia()) {
return; return;
} }
setText(textWithEntities); setText(textWithEntities);
@ -1998,10 +2002,9 @@ void HistoryItem::destroyHistoryEntry() {
} }
Storage::SharedMediaTypesMask HistoryItem::sharedMediaTypes() const { Storage::SharedMediaTypesMask HistoryItem::sharedMediaTypes() const {
auto result = Storage::SharedMediaTypesMask {}; auto result = Storage::SharedMediaTypesMask{};
const auto media = _savedLocalEditMediaData const auto saved = Get<HistoryMessageSavedMediaData>();
? _savedLocalEditMediaData->media.get() const auto media = saved ? saved->media.get() : _media.get();
: _media.get();
if (media) { if (media) {
result.set(media->sharedMediaTypes()); result.set(media->sharedMediaTypes());
} }
@ -3403,6 +3406,12 @@ void HistoryItem::createComponents(CreateConfig &&config) {
} else { } else {
_flags &= ~MessageFlag::HasReplyMarkup; _flags &= ~MessageFlag::HasReplyMarkup;
} }
if (out() && isSending()) {
if (const auto channel = _history->peer->asMegagroup()) {
_boostsApplied = channel->mgInfo->boostsApplied;
}
}
} }
bool HistoryItem::checkRepliesPts( bool HistoryItem::checkRepliesPts(

View file

@ -21,6 +21,7 @@ struct HistoryMessageMarkupData;
struct HistoryMessageReplyMarkup; struct HistoryMessageReplyMarkup;
struct HistoryMessageTranslation; struct HistoryMessageTranslation;
struct HistoryMessageForwarded; struct HistoryMessageForwarded;
struct HistoryMessageSavedMediaData;
struct HistoryServiceDependentData; struct HistoryServiceDependentData;
enum class HistorySelfDestructType; enum class HistorySelfDestructType;
struct PreparedServiceText; struct PreparedServiceText;
@ -536,16 +537,15 @@ public:
return _ttlDestroyAt; return _ttlDestroyAt;
} }
[[nodiscard]] int boostsApplied() const {
return _boostsApplied;
}
MsgId id; MsgId id;
private: private:
struct CreateConfig; struct CreateConfig;
struct SavedMediaData {
TextWithEntities text;
std::unique_ptr<Data::Media> media;
};
HistoryItem( HistoryItem(
not_null<History*> history, not_null<History*> history,
MsgId id, MsgId id,
@ -655,13 +655,13 @@ private:
TextWithEntities _text; TextWithEntities _text;
std::unique_ptr<SavedMediaData> _savedLocalEditMediaData;
std::unique_ptr<Data::Media> _media; std::unique_ptr<Data::Media> _media;
std::unique_ptr<Data::MessageReactions> _reactions; std::unique_ptr<Data::MessageReactions> _reactions;
crl::time _reactionsLastRefreshed = 0; crl::time _reactionsLastRefreshed = 0;
TimeId _date = 0; TimeId _date = 0;
TimeId _ttlDestroyAt = 0; TimeId _ttlDestroyAt = 0;
int _boostsApplied = 0;
HistoryView::Element *_mainView = nullptr; HistoryView::Element *_mainView = nullptr;
MessageGroupId _groupId = MessageGroupId(); MessageGroupId _groupId = MessageGroupId();
@ -672,3 +672,5 @@ private:
friend class HistoryView::ServiceMessagePainter; friend class HistoryView::ServiceMessagePainter;
}; };
constexpr auto kSize = int(sizeof(HistoryItem));

View file

@ -149,6 +149,11 @@ struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded
bool story = false; bool story = false;
}; };
struct HistoryMessageSavedMediaData : public RuntimeComponent<HistoryMessageSavedMediaData, HistoryItem> {
TextWithEntities text;
std::unique_ptr<Data::Media> media;
};
struct HistoryMessageSaved : public RuntimeComponent<HistoryMessageSaved, HistoryItem> { struct HistoryMessageSaved : public RuntimeComponent<HistoryMessageSaved, HistoryItem> {
Data::SavedSublist *sublist = nullptr; Data::SavedSublist *sublist = nullptr;
}; };

View file

@ -454,19 +454,20 @@ void Message::setReactions(std::unique_ptr<Reactions::InlineList> list) {
} }
void Message::refreshRightBadge() { void Message::refreshRightBadge() {
const auto item = data();
const auto text = [&] { const auto text = [&] {
if (data()->isDiscussionPost()) { if (item->isDiscussionPost()) {
return (delegate()->elementContext() == Context::Replies) return (delegate()->elementContext() == Context::Replies)
? QString() ? QString()
: tr::lng_channel_badge(tr::now); : tr::lng_channel_badge(tr::now);
} else if (data()->author()->isMegagroup()) { } else if (item->author()->isMegagroup()) {
if (const auto msgsigned = data()->Get<HistoryMessageSigned>()) { if (const auto msgsigned = item->Get<HistoryMessageSigned>()) {
Assert(msgsigned->isAnonymousRank); Assert(msgsigned->isAnonymousRank);
return msgsigned->postAuthor; return msgsigned->postAuthor;
} }
} }
const auto channel = data()->history()->peer->asMegagroup(); const auto channel = item->history()->peer->asMegagroup();
const auto user = data()->author()->asUser(); const auto user = item->author()->asUser();
if (!channel || !user) { if (!channel || !user) {
return QString(); return QString();
} }
@ -485,13 +486,41 @@ void Message::refreshRightBadge() {
? tr::lng_admin_badge(tr::now) ? tr::lng_admin_badge(tr::now)
: QString(); : QString();
}(); }();
const auto badge = text.isEmpty() auto badge = TextWithEntities{
? delegate()->elementAuthorRank(this) (text.isEmpty()
: TextUtilities::RemoveEmoji(TextUtilities::SingleLine(text)); ? delegate()->elementAuthorRank(this)
if (badge.isEmpty()) { : TextUtilities::RemoveEmoji(TextUtilities::SingleLine(text)))
};
_rightBadgeHasBoosts = 0;
if (const auto boosts = item->boostsApplied()) {
_rightBadgeHasBoosts = 1;
const auto many = (boosts > 1);
const auto &icon = many
? st::boostsMessageIcon
: st::boostMessageIcon;
const auto padding = many
? st::boostsMessageIconPadding
: st::boostMessageIconPadding;
const auto owner = &item->history()->owner();
auto added = Ui::Text::SingleCustomEmoji(
owner->customEmojiManager().registerInternalEmoji(icon, padding)
).append(many ? QString::number(boosts) : QString());
badge.append(' ').append(Ui::Text::Colorized(added, 1));
}
if (badge.empty()) {
_rightBadge.clear(); _rightBadge.clear();
} else { } else {
_rightBadge.setText(st::defaultTextStyle, badge); const auto context = Core::MarkedTextContext{
.session = &item->history()->session(),
.customEmojiRepaint = [] {},
.customEmojiLoopLimit = 1,
};
_rightBadge.setMarkedText(
st::defaultTextStyle,
badge,
Ui::NameTextOptions(),
context);
} }
} }
@ -1482,20 +1511,30 @@ void Message::paintFromName(
} }
if (rightWidth) { if (rightWidth) {
p.setPen(stm->msgDateFg); p.setPen(stm->msgDateFg);
p.setFont(ClickHandler::showAsActive(_fastReplyLink)
? st::msgFont->underline()
: st::msgFont);
if (replyWidth) { if (replyWidth) {
p.setFont(ClickHandler::showAsActive(_fastReplyLink)
? st::msgFont->underline()
: st::msgFont);
p.drawText( p.drawText(
trect.left() + trect.width() - rightWidth, trect.left() + trect.width() - rightWidth,
trect.top() + st::msgFont->ascent, trect.top() + st::msgFont->ascent,
FastReplyText()); FastReplyText());
} else { } else {
_rightBadge.draw( const auto shift = QPoint(trect.width() - rightWidth, 0);
p, const auto pen = !_rightBadgeHasBoosts
trect.left() + trect.width() - rightWidth, ? QPen()
trect.top(), : !context.outbg
rightWidth); ? QPen(FromNameFg(context, colorIndex()))
: stm->msgServiceFg->p;
auto colored = std::array<Ui::Text::SpecialColor, 1>{
{ { &pen, &pen } },
};
_rightBadge.draw(p, {
.position = trect.topLeft() + shift,
.availableWidth = rightWidth,
.colors = colored,
.now = context.now,
});
} }
} }
trect.setY(trect.y() + st::msgNameFont->height); trect.setY(trect.y() + st::msgNameFont->height);

View file

@ -305,9 +305,10 @@ private:
mutable std::unique_ptr<FromNameStatus> _fromNameStatus; mutable std::unique_ptr<FromNameStatus> _fromNameStatus;
Ui::Text::String _rightBadge; Ui::Text::String _rightBadge;
mutable int _fromNameVersion = 0; mutable int _fromNameVersion = 0;
uint32 _bubbleWidthLimit : 30 = 0; uint32 _bubbleWidthLimit : 29 = 0;
uint32 _invertMedia : 1 = 0; uint32 _invertMedia : 1 = 0;
uint32 _hideReply : 1 = 0; uint32 _hideReply : 1 = 0;
uint32 _rightBadgeHasBoosts : 1 = 0;
BottomInfo _bottomInfo; BottomInfo _bottomInfo;

View file

@ -1042,3 +1042,8 @@ chatSimilarSkip: 12px;
premiumRequiredWidth: 186px; premiumRequiredWidth: 186px;
premiumRequiredIcon: icon{{ "chat/large_lockedchat", msgServiceFg }}; premiumRequiredIcon: icon{{ "chat/large_lockedchat", msgServiceFg }};
premiumRequiredCircle: 60px; premiumRequiredCircle: 60px;
boostMessageIcon: icon {{ "stories/boost_mini", windowFg }};
boostMessageIconPadding: margins(0px, 2px, 0px, 0px);
boostsMessageIcon: icon {{ "stories/boosts_mini", windowFg }};
boostsMessageIconPadding: margins(0px, 2px, 0px, 0px);