Show applied boosts in message bubbles.
This commit is contained in:
parent
e32cbf468b
commit
33207b78d5
11 changed files with 108 additions and 44 deletions
BIN
Telegram/Resources/icons/stories/boosts_mini.png
Normal file
BIN
Telegram/Resources/icons/stories/boosts_mini.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 374 B |
BIN
Telegram/Resources/icons/stories/boosts_mini@2x.png
Normal file
BIN
Telegram/Resources/icons/stories/boosts_mini@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 690 B |
BIN
Telegram/Resources/icons/stories/boosts_mini@3x.png
Normal file
BIN
Telegram/Resources/icons/stories/boosts_mini@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1 KiB |
|
@ -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());
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue