mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Support media covers for bot descriptions.
For that replace custom handling of _botAbout by a fake message.
This commit is contained in:
parent
59c66d1f49
commit
221b0d19c7
10 changed files with 325 additions and 205 deletions
|
@ -293,6 +293,9 @@ enum class MessageFlag : uint64 {
|
|||
|
||||
OnlyEmojiAndSpaces = (1ULL << 34),
|
||||
OnlyEmojiAndSpacesSet = (1ULL << 35),
|
||||
|
||||
// Fake message with bot cover and information.
|
||||
FakeBotAbout = (1ULL << 36),
|
||||
};
|
||||
inline constexpr bool is_flag_type(MessageFlag) { return true; }
|
||||
using MessageFlags = base::flags<MessageFlag>;
|
||||
|
|
|
@ -32,8 +32,7 @@ using UpdateFlag = Data::PeerUpdate::Flag;
|
|||
|
||||
} // namespace
|
||||
|
||||
BotInfo::BotInfo() : text(st::msgMinWidth) {
|
||||
}
|
||||
BotInfo::BotInfo() = default;
|
||||
|
||||
UserData::UserData(not_null<Data::Session*> owner, PeerId id)
|
||||
: PeerData(owner, id)
|
||||
|
@ -188,10 +187,30 @@ void UserData::setBotInfo(const MTPBotInfo &info) {
|
|||
return;
|
||||
}
|
||||
|
||||
QString desc = qs(d.vdescription().value_or_empty());
|
||||
if (botInfo->description != desc) {
|
||||
botInfo->description = desc;
|
||||
botInfo->text = Ui::Text::String(st::msgMinWidth);
|
||||
const auto description = qs(d.vdescription().value_or_empty());
|
||||
if (botInfo->description != description) {
|
||||
botInfo->description = description;
|
||||
++botInfo->descriptionVersion;
|
||||
}
|
||||
if (const auto photo = d.vdescription_photo()) {
|
||||
const auto parsed = owner().processPhoto(*photo);
|
||||
if (botInfo->photo != parsed) {
|
||||
botInfo->photo = parsed;
|
||||
++botInfo->descriptionVersion;
|
||||
}
|
||||
} else if (botInfo->photo) {
|
||||
botInfo->photo = nullptr;
|
||||
++botInfo->descriptionVersion;
|
||||
}
|
||||
if (const auto document = d.vdescription_document()) {
|
||||
const auto parsed = owner().processDocument(*document);
|
||||
if (botInfo->document != parsed) {
|
||||
botInfo->document = parsed;
|
||||
++botInfo->descriptionVersion;
|
||||
}
|
||||
} else if (botInfo->document) {
|
||||
botInfo->document = nullptr;
|
||||
++botInfo->descriptionVersion;
|
||||
}
|
||||
|
||||
auto commands = d.vcommands()
|
||||
|
|
|
@ -24,9 +24,13 @@ struct BotInfo {
|
|||
bool cantJoinGroups = false;
|
||||
bool supportsAttachMenu = false;
|
||||
int version = 0;
|
||||
QString description, inlinePlaceholder;
|
||||
int descriptionVersion = 0;
|
||||
QString description;
|
||||
QString inlinePlaceholder;
|
||||
std::vector<Data::BotCommand> commands;
|
||||
Ui::Text::String text;
|
||||
|
||||
PhotoData *photo = nullptr;
|
||||
DocumentData *document = nullptr;
|
||||
|
||||
QString botMenuButtonText;
|
||||
QString botMenuButtonUrl;
|
||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "core/crash_reports.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "history/history.h"
|
||||
#include "history/admin_log/history_admin_log_item.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "history/view/media/history_view_media.h"
|
||||
|
@ -326,41 +327,100 @@ public:
|
|||
|
||||
};
|
||||
|
||||
class HistoryInner::BotAbout : public ClickHandlerHost {
|
||||
class HistoryInner::BotAbout final : public ClickHandlerHost {
|
||||
public:
|
||||
BotAbout(not_null<HistoryInner*> parent, not_null<BotInfo*> info);
|
||||
BotAbout(
|
||||
not_null<History*> history,
|
||||
not_null<HistoryView::ElementDelegate*> delegate);
|
||||
|
||||
// ClickHandlerHost interface
|
||||
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
|
||||
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
|
||||
[[nodiscard]] not_null<History*> history() const;
|
||||
[[nodiscard]] HistoryView::Element *view() const;
|
||||
[[nodiscard]] HistoryItem *item() const;
|
||||
|
||||
not_null<BotInfo*> info;
|
||||
int width = 0;
|
||||
bool refresh();
|
||||
|
||||
int top = 0;
|
||||
int height = 0;
|
||||
QRect rect;
|
||||
|
||||
private:
|
||||
not_null<HistoryInner*> _parent;
|
||||
const not_null<History*> _history;
|
||||
const not_null<HistoryView::ElementDelegate*> _delegate;
|
||||
AdminLog::OwnedItem _item;
|
||||
int _version = 0;
|
||||
|
||||
};
|
||||
|
||||
HistoryInner::BotAbout::BotAbout(
|
||||
not_null<HistoryInner*> parent,
|
||||
not_null<BotInfo*> info)
|
||||
: info(info)
|
||||
, _parent(parent) {
|
||||
not_null<History*> history,
|
||||
not_null<HistoryView::ElementDelegate*> delegate)
|
||||
: _history(history)
|
||||
, _delegate(delegate) {
|
||||
}
|
||||
|
||||
void HistoryInner::BotAbout::clickHandlerActiveChanged(
|
||||
const ClickHandlerPtr &p,
|
||||
bool active) {
|
||||
_parent->update(rect);
|
||||
not_null<History*> HistoryInner::BotAbout::history() const {
|
||||
return _history;
|
||||
}
|
||||
|
||||
void HistoryInner::BotAbout::clickHandlerPressedChanged(
|
||||
const ClickHandlerPtr &p,
|
||||
bool pressed) {
|
||||
_parent->update(rect);
|
||||
HistoryView::Element *HistoryInner::BotAbout::view() const {
|
||||
return _item.get();
|
||||
}
|
||||
|
||||
HistoryItem *HistoryInner::BotAbout::item() const {
|
||||
if (const auto element = view()) {
|
||||
return element->data();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool HistoryInner::BotAbout::refresh() {
|
||||
const auto bot = _history->peer->asUser();
|
||||
const auto info = bot ? bot->botInfo.get() : nullptr;
|
||||
if (!info) {
|
||||
if (_item) {
|
||||
_item = {};
|
||||
return true;
|
||||
}
|
||||
_version = 0;
|
||||
return false;
|
||||
}
|
||||
const auto version = info->descriptionVersion;
|
||||
if (_version == version) {
|
||||
return false;
|
||||
}
|
||||
_version = version;
|
||||
|
||||
const auto flags = MessageFlag::FakeBotAbout
|
||||
| MessageFlag::FakeHistoryItem
|
||||
| MessageFlag::Local;
|
||||
const auto postAuthor = QString();
|
||||
const auto date = TimeId(0);
|
||||
const auto replyTo = MsgId(0);
|
||||
const auto viaBotId = UserId(0);
|
||||
const auto groupedId = uint64(0);
|
||||
const auto textWithEntities = TextUtilities::ParseEntities(
|
||||
info->description,
|
||||
Ui::ItemTextBotNoMonoOptions().flags);
|
||||
const auto make = [&](auto &&a, auto &&b, auto &&...other) {
|
||||
return _history->makeMessage(
|
||||
_history->nextNonHistoryEntryId(),
|
||||
flags,
|
||||
replyTo,
|
||||
viaBotId,
|
||||
date,
|
||||
bot->id,
|
||||
postAuthor,
|
||||
std::forward<decltype(a)>(a),
|
||||
std::forward<decltype(b)>(b),
|
||||
HistoryMessageMarkupData(),
|
||||
std::forward<decltype(other)>(other)...);
|
||||
};
|
||||
const auto item = info->document
|
||||
? make(info->document, textWithEntities)
|
||||
: info->photo
|
||||
? make(info->photo, textWithEntities)
|
||||
: make(textWithEntities, MTP_messageMediaEmpty(), groupedId);
|
||||
_item = AdminLog::OwnedItem(_delegate, item);
|
||||
return true;
|
||||
}
|
||||
|
||||
HistoryInner::HistoryInner(
|
||||
|
@ -971,41 +1031,10 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
|||
|
||||
const auto historyDisplayedEmpty = _history->isDisplayedEmpty()
|
||||
&& (!_migrated || _migrated->isDisplayedEmpty());
|
||||
if (_botAbout && !_botAbout->info->text.isEmpty() && _botAbout->height > 0) {
|
||||
const auto st = context.st;
|
||||
const auto stm = &st->messageStyle(false, false);
|
||||
if (clip.y() < _botAbout->rect.y() + _botAbout->rect.height() && clip.y() + clip.height() > _botAbout->rect.y()) {
|
||||
p.setTextPalette(stm->textPalette);
|
||||
using Corner = Ui::BubbleCornerRounding;
|
||||
const auto rounding = Ui::BubbleRounding{
|
||||
Corner::Large,
|
||||
Corner::Large,
|
||||
Corner::Large,
|
||||
Corner::Large,
|
||||
};
|
||||
Ui::PaintBubble(p, Ui::SimpleBubble{
|
||||
.st = st,
|
||||
.geometry = _botAbout->rect,
|
||||
.pattern = context.bubblesPattern,
|
||||
.patternViewport = context.viewport,
|
||||
.outerWidth = width(),
|
||||
.selected = false,
|
||||
.outbg = false,
|
||||
.rounding = rounding,
|
||||
});
|
||||
|
||||
auto top = _botAbout->rect.top() + st::msgPadding.top();
|
||||
if (!_history->peer->isRepliesChat()) {
|
||||
p.setFont(st::msgNameFont);
|
||||
p.setPen(st->dialogsNameFg());
|
||||
p.drawText(_botAbout->rect.left() + st::msgPadding.left(), top + st::msgNameFont->ascent, tr::lng_bot_description(tr::now));
|
||||
top += +st::msgNameFont->height + st::botDescSkip;
|
||||
}
|
||||
|
||||
p.setPen(stm->historyTextFg);
|
||||
_botAbout->info->text.draw(p, _botAbout->rect.left() + st::msgPadding.left(), top, _botAbout->width);
|
||||
|
||||
p.restoreTextPalette();
|
||||
if (const auto view = _botAbout ? _botAbout->view() : nullptr) {
|
||||
if (clip.y() < _botAbout->top + _botAbout->height
|
||||
&& clip.y() + clip.height() > _botAbout->top) {
|
||||
view->draw(p, context);
|
||||
}
|
||||
} else if (historyDisplayedEmpty) {
|
||||
paintEmpty(p, context.st, width(), height());
|
||||
|
@ -2939,9 +2968,11 @@ void HistoryInner::recountHistoryGeometry() {
|
|||
}
|
||||
|
||||
const auto visibleHeight = _scroll->height();
|
||||
int oldHistoryPaddingTop = qMax(visibleHeight - historyHeight() - st::historyPaddingBottom, 0);
|
||||
if (_botAbout && !_botAbout->info->text.isEmpty()) {
|
||||
accumulate_max(oldHistoryPaddingTop, st::msgMargin.top() + st::msgMargin.bottom() + st::msgPadding.top() + st::msgPadding.bottom() + st::msgNameFont->height + st::botDescSkip + _botAbout->height);
|
||||
auto oldHistoryPaddingTop = qMax(
|
||||
visibleHeight - historyHeight() - st::historyPaddingBottom,
|
||||
0);
|
||||
if (_botAbout) {
|
||||
accumulate_max(oldHistoryPaddingTop, _botAbout->height);
|
||||
}
|
||||
|
||||
_history->resizeToWidth(_contentWidth);
|
||||
|
@ -2968,39 +2999,20 @@ void HistoryInner::recountHistoryGeometry() {
|
|||
}
|
||||
|
||||
updateBotInfo(false);
|
||||
if (_botAbout && !_botAbout->info->text.isEmpty()) {
|
||||
int32 tw = _scroll->width() - st::msgMargin.left() - st::msgMargin.right();
|
||||
if (tw > st::msgMaxWidth) tw = st::msgMaxWidth;
|
||||
tw -= st::msgPadding.left() + st::msgPadding.right();
|
||||
const auto descriptionWidth = _history->peer->isRepliesChat()
|
||||
? 0
|
||||
: st::msgNameFont->width(tr::lng_bot_description(tr::now));
|
||||
int32 mw = qMax(_botAbout->info->text.maxWidth(), descriptionWidth);
|
||||
if (tw > mw) tw = mw;
|
||||
|
||||
_botAbout->width = tw;
|
||||
_botAbout->height = _botAbout->info->text.countHeight(_botAbout->width);
|
||||
|
||||
const auto descriptionHeight = _history->peer->isRepliesChat()
|
||||
? 0
|
||||
: (st::msgNameFont->height + st::botDescSkip);
|
||||
int32 descH = st::msgMargin.top() + st::msgPadding.top() + descriptionHeight + _botAbout->height + st::msgPadding.bottom() + st::msgMargin.bottom();
|
||||
int32 descMaxWidth = _scroll->width();
|
||||
if (_isChatWide) {
|
||||
descMaxWidth = qMin(descMaxWidth, int32(st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left()));
|
||||
}
|
||||
int32 descAtX = (descMaxWidth - _botAbout->width) / 2 - st::msgPadding.left();
|
||||
int32 descAtY = qMin(_historyPaddingTop - descH, qMax(0, (_scroll->height() - descH) / 2)) + st::msgMargin.top();
|
||||
|
||||
_botAbout->rect = QRect(descAtX, descAtY, _botAbout->width + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom());
|
||||
if (const auto view = _botAbout ? _botAbout->view() : nullptr) {
|
||||
_botAbout->height = view->resizeGetHeight(_contentWidth);
|
||||
_botAbout->top = qMin(
|
||||
_historyPaddingTop - _botAbout->height,
|
||||
qMax(0, (_scroll->height() - _botAbout->height) / 2));
|
||||
} else if (_botAbout) {
|
||||
_botAbout->width = _botAbout->height = 0;
|
||||
_botAbout->rect = QRect();
|
||||
_botAbout->top = _botAbout->height = 0;
|
||||
}
|
||||
|
||||
int newHistoryPaddingTop = qMax(visibleHeight - historyHeight() - st::historyPaddingBottom, 0);
|
||||
if (_botAbout && !_botAbout->info->text.isEmpty()) {
|
||||
accumulate_max(newHistoryPaddingTop, st::msgMargin.top() + st::msgMargin.bottom() + st::msgPadding.top() + st::msgPadding.bottom() + st::msgNameFont->height + st::botDescSkip + _botAbout->height);
|
||||
auto newHistoryPaddingTop = qMax(
|
||||
visibleHeight - historyHeight() - st::historyPaddingBottom,
|
||||
0);
|
||||
if (_botAbout) {
|
||||
accumulate_max(newHistoryPaddingTop, _botAbout->height);
|
||||
}
|
||||
|
||||
auto historyPaddingTopDelta = (newHistoryPaddingTop - oldHistoryPaddingTop);
|
||||
|
@ -3014,48 +3026,15 @@ void HistoryInner::recountHistoryGeometry() {
|
|||
}
|
||||
|
||||
void HistoryInner::updateBotInfo(bool recount) {
|
||||
int newh = 0;
|
||||
if (_botAbout && !_botAbout->info->description.isEmpty()) {
|
||||
if (_botAbout->info->text.isEmpty()) {
|
||||
_botAbout->info->text.setText(
|
||||
st::messageTextStyle,
|
||||
_botAbout->info->description,
|
||||
Ui::ItemTextBotNoMonoOptions());
|
||||
if (recount) {
|
||||
int32 tw = _scroll->width() - st::msgMargin.left() - st::msgMargin.right();
|
||||
if (tw > st::msgMaxWidth) tw = st::msgMaxWidth;
|
||||
tw -= st::msgPadding.left() + st::msgPadding.right();
|
||||
const auto descriptionWidth = _history->peer->isRepliesChat()
|
||||
? 0
|
||||
: st::msgNameFont->width(tr::lng_bot_description(tr::now));
|
||||
int32 mw = qMax(_botAbout->info->text.maxWidth(), descriptionWidth);
|
||||
if (tw > mw) tw = mw;
|
||||
|
||||
_botAbout->width = tw;
|
||||
newh = _botAbout->info->text.countHeight(_botAbout->width);
|
||||
}
|
||||
} else if (recount) {
|
||||
newh = _botAbout->height;
|
||||
}
|
||||
}
|
||||
if (recount && _botAbout) {
|
||||
if (_botAbout->height != newh) {
|
||||
_botAbout->height = newh;
|
||||
if (!_botAbout) {
|
||||
return;
|
||||
} else if (_botAbout->refresh() && recount && _contentWidth > 0) {
|
||||
const auto view = _botAbout->view();
|
||||
const auto now = view ? view->resizeGetHeight(_contentWidth) : 0;
|
||||
if (_botAbout->height != now) {
|
||||
_botAbout->height = now;
|
||||
updateSize();
|
||||
}
|
||||
if (_botAbout->height > 0) {
|
||||
const auto descriptionHeight = _history->peer->isRepliesChat()
|
||||
? 0
|
||||
: (st::msgNameFont->height + st::botDescSkip);
|
||||
int32 descH = st::msgMargin.top() + st::msgPadding.top() + descriptionHeight + _botAbout->height + st::msgPadding.bottom() + st::msgMargin.bottom();
|
||||
int32 descAtX = (_scroll->width() - _botAbout->width) / 2 - st::msgPadding.left();
|
||||
int32 descAtY = qMin(_historyPaddingTop - descH, (_scroll->height() - descH) / 2) + st::msgMargin.top();
|
||||
|
||||
_botAbout->rect = QRect(descAtX, descAtY, _botAbout->width + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom());
|
||||
} else {
|
||||
_botAbout->width = 0;
|
||||
_botAbout->rect = QRect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3200,24 +3179,15 @@ void HistoryInner::changeItemsRevealHeight(int revealHeight) {
|
|||
void HistoryInner::updateSize() {
|
||||
const auto visibleHeight = _scroll->height();
|
||||
const auto itemsHeight = historyHeight() - _revealHeight;
|
||||
int newHistoryPaddingTop = qMax(visibleHeight - itemsHeight - st::historyPaddingBottom, 0);
|
||||
if (_botAbout && !_botAbout->info->text.isEmpty()) {
|
||||
accumulate_max(newHistoryPaddingTop, st::msgMargin.top() + st::msgMargin.bottom() + st::msgPadding.top() + st::msgPadding.bottom() + st::msgNameFont->height + st::botDescSkip + _botAbout->height);
|
||||
auto newHistoryPaddingTop = qMax(visibleHeight - itemsHeight - st::historyPaddingBottom, 0);
|
||||
if (_botAbout) {
|
||||
accumulate_max(newHistoryPaddingTop, _botAbout->height);
|
||||
}
|
||||
|
||||
if (_botAbout && _botAbout->height > 0) {
|
||||
const auto descriptionHeight = _history->peer->isRepliesChat()
|
||||
? 0
|
||||
: (st::msgNameFont->height + st::botDescSkip);
|
||||
int32 descH = st::msgMargin.top() + st::msgPadding.top() + descriptionHeight + _botAbout->height + st::msgPadding.bottom() + st::msgMargin.bottom();
|
||||
int32 descMaxWidth = _scroll->width();
|
||||
if (_isChatWide) {
|
||||
descMaxWidth = qMin(descMaxWidth, int32(st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left()));
|
||||
}
|
||||
int32 descAtX = (descMaxWidth - _botAbout->width) / 2 - st::msgPadding.left();
|
||||
int32 descAtY = qMin(newHistoryPaddingTop - descH, qMax(0, (_scroll->height() - descH) / 2)) + st::msgMargin.top();
|
||||
|
||||
_botAbout->rect = QRect(descAtX, descAtY, _botAbout->width + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom());
|
||||
_botAbout->top = qMin(
|
||||
newHistoryPaddingTop - _botAbout->height,
|
||||
qMax(0, (_scroll->height() - _botAbout->height) / 2));
|
||||
}
|
||||
|
||||
if (_historyPaddingTop != newHistoryPaddingTop) {
|
||||
|
@ -3261,6 +3231,7 @@ void HistoryInner::leaveEventHook(QEvent *e) {
|
|||
}
|
||||
|
||||
HistoryInner::~HistoryInner() {
|
||||
_botAbout = nullptr;
|
||||
for (const auto &item : _animatedStickersPlayed) {
|
||||
if (const auto view = item->mainView()) {
|
||||
if (const auto media = view->media()) {
|
||||
|
@ -3632,12 +3603,15 @@ void HistoryInner::mouseActionUpdate() {
|
|||
dragState = reactionState;
|
||||
lnkhost = reactionView;
|
||||
} else if (point.y() < _historyPaddingTop) {
|
||||
if (_botAbout && !_botAbout->info->text.isEmpty() && _botAbout->height > 0) {
|
||||
dragState = TextState(nullptr, _botAbout->info->text.getState(
|
||||
point - _botAbout->rect.topLeft() - QPoint(st::msgPadding.left(), st::msgPadding.top() + st::botDescSkip + st::msgNameFont->height),
|
||||
_botAbout->width));
|
||||
if (const auto view = _botAbout ? _botAbout->view() : nullptr) {
|
||||
StateRequest request;
|
||||
if (base::IsAltPressed()) {
|
||||
request.flags &= ~Ui::Text::StateRequest::Flag::LookupLink;
|
||||
}
|
||||
const auto relative = point - QPoint(0, _botAbout->top);
|
||||
dragState = view->textState(relative, request);
|
||||
_dragStateItem = session().data().message(dragState.itemId);
|
||||
lnkhost = _botAbout.get();
|
||||
lnkhost = view;
|
||||
}
|
||||
} else if (item) {
|
||||
if (item != _mouseActionItem || (m - _dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) {
|
||||
|
@ -3947,16 +3921,22 @@ void HistoryInner::setCanHaveFromUserpicsSponsored(bool value) {
|
|||
int HistoryInner::itemTop(const HistoryItem *item) const {
|
||||
if (!item) {
|
||||
return -2;
|
||||
} else if (_botAbout && item == _botAbout->item()) {
|
||||
return _botAbout->top;
|
||||
}
|
||||
return itemTop(item->mainView());
|
||||
}
|
||||
|
||||
int HistoryInner::itemTop(const Element *view) const {
|
||||
if (!view || view->data()->mainView() != view) {
|
||||
if (!view) {
|
||||
return -1;
|
||||
} else if (_botAbout && view == _botAbout->view()) {
|
||||
return _botAbout->top;
|
||||
} else if (view->data()->mainView() != view) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto top = (view->history() == _history)
|
||||
const auto top = (view->history() == _history)
|
||||
? historyTop()
|
||||
: (view->history() == _migrated
|
||||
? migratedTop()
|
||||
|
@ -3990,21 +3970,17 @@ auto HistoryInner::findViewForPinnedTracking(int top) const
|
|||
}
|
||||
|
||||
void HistoryInner::notifyIsBotChanged() {
|
||||
const auto newinfo = _peer->isUser()
|
||||
? _peer->asUser()->botInfo.get()
|
||||
: nullptr;
|
||||
if ((!newinfo && !_botAbout)
|
||||
|| (newinfo && _botAbout && _botAbout->info == newinfo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newinfo) {
|
||||
_botAbout = std::make_unique<BotAbout>(this, newinfo);
|
||||
if (newinfo && !newinfo->inited) {
|
||||
session().api().requestFullPeer(_peer);
|
||||
if (const auto user = _peer->asUser()) {
|
||||
if (const auto info = user->botInfo.get()) {
|
||||
if (!_botAbout) {
|
||||
_botAbout = std::make_unique<BotAbout>(
|
||||
_history,
|
||||
_history->delegateMixin()->delegate());
|
||||
}
|
||||
if (!info->inited) {
|
||||
session().api().requestFullPeer(_peer);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_botAbout = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -306,6 +306,9 @@ public:
|
|||
[[nodiscard]] bool isLocal() const {
|
||||
return _flags & MessageFlag::Local;
|
||||
}
|
||||
[[nodiscard]] bool isFakeBotAbout() const {
|
||||
return _flags & MessageFlag::FakeBotAbout;
|
||||
}
|
||||
[[nodiscard]] bool isRegular() const;
|
||||
[[nodiscard]] bool isUploading() const;
|
||||
void sendFailed();
|
||||
|
|
|
@ -354,6 +354,20 @@ void DateBadge::paint(
|
|||
ServiceMessagePainter::PaintDate(p, st, text, width, y, w, chatWide);
|
||||
}
|
||||
|
||||
void FakeBotAboutTop::init() {
|
||||
if (!text.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
text.setText(
|
||||
st::msgNameStyle,
|
||||
tr::lng_bot_description(tr::now),
|
||||
Ui::NameTextOptions());
|
||||
maxWidth = st::msgPadding.left()
|
||||
+ text.maxWidth()
|
||||
+ st::msgPadding.right();
|
||||
height = st::msgNameStyle.font->height + st::botDescSkip;
|
||||
}
|
||||
|
||||
Element::Element(
|
||||
not_null<ElementDelegate*> delegate,
|
||||
not_null<HistoryItem*> data,
|
||||
|
@ -376,6 +390,9 @@ Element::Element(
|
|||
if (_context == Context::History) {
|
||||
history()->setHasPendingResizedItems();
|
||||
}
|
||||
if (data->isFakeBotAbout() && !data->history()->peer->isRepliesChat()) {
|
||||
AddComponents(FakeBotAboutTop::Bit());
|
||||
}
|
||||
}
|
||||
|
||||
not_null<ElementDelegate*> Element::delegate() const {
|
||||
|
|
|
@ -230,6 +230,14 @@ struct DateBadge : public RuntimeComponent<DateBadge, Element> {
|
|||
|
||||
};
|
||||
|
||||
struct FakeBotAboutTop : public RuntimeComponent<FakeBotAboutTop, Element> {
|
||||
void init();
|
||||
|
||||
Ui::Text::String text;
|
||||
int maxWidth = 0;
|
||||
int height = 0;
|
||||
};
|
||||
|
||||
struct TopicButton {
|
||||
std::unique_ptr<Ui::RippleAnimation> ripple;
|
||||
ClickHandlerPtr link;
|
||||
|
|
|
@ -530,6 +530,12 @@ QSize Message::performCountOptimalSize() {
|
|||
refreshInfoSkipBlock();
|
||||
|
||||
const auto media = this->media();
|
||||
const auto botTop = item->isFakeBotAbout()
|
||||
? Get<FakeBotAboutTop>()
|
||||
: nullptr;
|
||||
if (botTop) {
|
||||
botTop->init();
|
||||
}
|
||||
|
||||
auto maxWidth = 0;
|
||||
auto minHeight = 0;
|
||||
|
@ -560,13 +566,14 @@ QSize Message::performCountOptimalSize() {
|
|||
}
|
||||
|
||||
// Entry page is always a bubble bottom.
|
||||
const auto withVisibleText = hasVisibleText();
|
||||
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/);
|
||||
auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop());
|
||||
maxWidth = plainMaxWidth();
|
||||
if (context() == Context::Replies && item->isDiscussionPost()) {
|
||||
maxWidth = std::max(maxWidth, st::msgMaxWidth);
|
||||
}
|
||||
minHeight = hasVisibleText() ? text().minHeight() : 0;
|
||||
minHeight = withVisibleText ? text().minHeight() : 0;
|
||||
if (reactionsInBubble) {
|
||||
const auto reactionsMaxWidth = st::msgPadding.left()
|
||||
+ _reactions->maxWidth()
|
||||
|
@ -604,9 +611,14 @@ QSize Message::performCountOptimalSize() {
|
|||
const auto innerWidth = maxWidth
|
||||
- st::msgPadding.left()
|
||||
- st::msgPadding.right();
|
||||
if (hasVisibleText() && maxWidth < plainMaxWidth()) {
|
||||
minHeight -= text().minHeight();
|
||||
minHeight += text().countHeight(innerWidth);
|
||||
if (withVisibleText) {
|
||||
if (botTop) {
|
||||
minHeight += botTop->height;
|
||||
}
|
||||
if (maxWidth < plainMaxWidth()) {
|
||||
minHeight -= text().minHeight();
|
||||
minHeight += text().countHeight(innerWidth);
|
||||
}
|
||||
}
|
||||
if (reactionsInBubble) {
|
||||
minHeight -= _reactions->minHeight();
|
||||
|
@ -678,6 +690,10 @@ QSize Message::performCountOptimalSize() {
|
|||
accumulate_max(maxWidth, entry->maxWidth());
|
||||
minHeight += entry->minHeight();
|
||||
}
|
||||
if (withVisibleText && botTop) {
|
||||
accumulate_max(maxWidth, botTop->maxWidth);
|
||||
minHeight += botTop->height;
|
||||
}
|
||||
}
|
||||
accumulate_max(maxWidth, minWidthForMedia());
|
||||
} else if (media) {
|
||||
|
@ -1467,6 +1483,15 @@ void Message::paintText(
|
|||
p.setPen(stm->historyTextFg);
|
||||
p.setFont(st::msgFont);
|
||||
prepareCustomEmojiPaint(p, context, text());
|
||||
if (const auto botTop = Get<FakeBotAboutTop>()) {
|
||||
botTop->text.drawLeftElided(
|
||||
p,
|
||||
trect.x(),
|
||||
trect.y(),
|
||||
trect.width(),
|
||||
width());
|
||||
trect.setY(trect.y() + botTop->height);
|
||||
}
|
||||
text().draw(p, {
|
||||
.position = trect.topLeft(),
|
||||
.availableWidth = trect.width(),
|
||||
|
@ -2281,6 +2306,8 @@ bool Message::getStateText(
|
|||
StateRequest request) const {
|
||||
if (!hasVisibleText()) {
|
||||
return false;
|
||||
} else if (const auto botTop = Get<FakeBotAboutTop>()) {
|
||||
trect.setY(trect.y() + botTop->height);
|
||||
}
|
||||
const auto item = data();
|
||||
if (base::in_range(point.y(), trect.y(), trect.y() + trect.height())) {
|
||||
|
@ -2886,7 +2913,7 @@ bool Message::drawBubble() const {
|
|||
const auto item = data();
|
||||
if (isHidden()) {
|
||||
return false;
|
||||
} else if (logEntryOriginal()) {
|
||||
} else if (logEntryOriginal() || item->isFakeBotAbout()) {
|
||||
return true;
|
||||
}
|
||||
const auto media = this->media();
|
||||
|
@ -3376,8 +3403,9 @@ QRect Message::innerGeometry() const {
|
|||
}
|
||||
|
||||
QRect Message::countGeometry() const {
|
||||
const auto commentsRoot = (context() == Context::Replies)
|
||||
&& data()->isDiscussionPost();
|
||||
const auto item = data();
|
||||
const auto centeredView = item->isFakeBotAbout()
|
||||
|| (context() == Context::Replies && item->isDiscussionPost());
|
||||
const auto media = this->media();
|
||||
const auto mediaWidth = (media && media->isDisplayed())
|
||||
? media->width()
|
||||
|
@ -3385,7 +3413,7 @@ QRect Message::countGeometry() const {
|
|||
const auto outbg = hasOutLayout();
|
||||
const auto availableWidth = width()
|
||||
- st::msgMargin.left()
|
||||
- (commentsRoot ? st::msgMargin.left() : st::msgMargin.right());
|
||||
- (centeredView ? st::msgMargin.left() : st::msgMargin.right());
|
||||
auto contentLeft = (outbg && !delegate()->elementIsChatWide())
|
||||
? st::msgMargin.right()
|
||||
: st::msgMargin.left();
|
||||
|
@ -3412,10 +3440,10 @@ QRect Message::countGeometry() const {
|
|||
if (contentWidth < availableWidth && !delegate()->elementIsChatWide()) {
|
||||
if (outbg) {
|
||||
contentLeft += availableWidth - contentWidth;
|
||||
} else if (commentsRoot) {
|
||||
} else if (centeredView) {
|
||||
contentLeft += (availableWidth - contentWidth) / 2;
|
||||
}
|
||||
} else if (contentWidth < availableWidth && commentsRoot) {
|
||||
} else if (contentWidth < availableWidth && centeredView) {
|
||||
contentLeft += std::max(
|
||||
((st::msgMaxWidth + 2 * st::msgPhotoSkip) - contentWidth) / 2,
|
||||
0);
|
||||
|
@ -3433,11 +3461,13 @@ Ui::BubbleRounding Message::countMessageRounding() const {
|
|||
const auto smallTop = isBubbleAttachedToPrevious();
|
||||
const auto smallBottom = isBubbleAttachedToNext();
|
||||
const auto media = smallBottom ? nullptr : this->media();
|
||||
const auto keyboard = data()->inlineReplyKeyboard();
|
||||
const auto item = data();
|
||||
const auto keyboard = item->inlineReplyKeyboard();
|
||||
const auto skipTail = smallBottom
|
||||
|| (media && media->skipBubbleTail())
|
||||
|| (keyboard != nullptr)
|
||||
|| (context() == Context::Replies && data()->isDiscussionPost());
|
||||
|| item->isFakeBotAbout()
|
||||
|| (context() == Context::Replies && item->isDiscussionPost());
|
||||
const auto right = !delegate()->elementIsChatWide() && hasOutLayout();
|
||||
using Corner = Ui::BubbleCornerRounding;
|
||||
return Ui::BubbleRounding{
|
||||
|
@ -3480,16 +3510,19 @@ int Message::resizeContentGetHeight(int newWidth) {
|
|||
auto newHeight = minHeight();
|
||||
|
||||
const auto item = data();
|
||||
const auto botTop = item->isFakeBotAbout()
|
||||
? Get<FakeBotAboutTop>()
|
||||
: nullptr;
|
||||
const auto media = this->media();
|
||||
const auto mediaDisplayed = media ? media->isDisplayed() : false;
|
||||
const auto bubble = drawBubble();
|
||||
|
||||
// This code duplicates countGeometry() but also resizes media.
|
||||
const auto commentsRoot = (context() == Context::Replies)
|
||||
&& data()->isDiscussionPost();
|
||||
const auto centeredView = item->isFakeBotAbout()
|
||||
|| (context() == Context::Replies && item->isDiscussionPost());
|
||||
auto contentWidth = newWidth
|
||||
- st::msgMargin.left()
|
||||
- (commentsRoot ? st::msgMargin.left() : st::msgMargin.right());
|
||||
- (centeredView ? st::msgMargin.left() : st::msgMargin.right());
|
||||
if (hasFromPhoto()) {
|
||||
if (const auto size = rightActionSize()) {
|
||||
contentWidth -= size->width() + (st::msgPhotoSkip - st::historyFastShareSize);
|
||||
|
@ -3540,7 +3573,14 @@ int Message::resizeContentGetHeight(int newWidth) {
|
|||
entry->resizeGetHeight(contentWidth);
|
||||
}
|
||||
} else {
|
||||
newHeight = hasVisibleText() ? textHeightFor(textWidth) : 0;
|
||||
const auto withVisibleText = hasVisibleText();
|
||||
newHeight = 0;
|
||||
if (withVisibleText) {
|
||||
if (botTop) {
|
||||
newHeight += botTop->height;
|
||||
}
|
||||
newHeight += textHeightFor(textWidth);
|
||||
}
|
||||
if (!mediaOnBottom && (!_viewButton || !reactionsInBubble)) {
|
||||
newHeight += st::msgPadding.bottom();
|
||||
if (mediaDisplayed) {
|
||||
|
|
|
@ -199,6 +199,10 @@ QSize Gif::countOptimalSize() {
|
|||
minHeight = adjustHeightForLessCrop(
|
||||
scaled,
|
||||
{ maxWidth, minHeight });
|
||||
if (const auto botTop = _parent->Get<FakeBotAboutTop>()) {
|
||||
accumulate_max(maxWidth, botTop->maxWidth);
|
||||
minHeight += botTop->height;
|
||||
}
|
||||
minHeight += st::mediaCaptionSkip + _caption.minHeight();
|
||||
if (isBubbleBottom()) {
|
||||
minHeight += st::msgPadding.bottom();
|
||||
|
@ -236,11 +240,14 @@ QSize Gif::countCurrentSize(int newWidth) {
|
|||
if (_parent->hasBubble()) {
|
||||
accumulate_max(newWidth, _parent->minWidthForMedia());
|
||||
if (!_caption.isEmpty()) {
|
||||
const auto maxWithCaption = qMin(
|
||||
st::msgMaxWidth,
|
||||
(st::msgPadding.left()
|
||||
+ _caption.maxWidth()
|
||||
+ st::msgPadding.right()));
|
||||
auto captionMaxWidth = st::msgPadding.left()
|
||||
+ _caption.maxWidth()
|
||||
+ st::msgPadding.right();
|
||||
const auto botTop = _parent->Get<FakeBotAboutTop>();
|
||||
if (botTop) {
|
||||
accumulate_max(captionMaxWidth, botTop->maxWidth);
|
||||
}
|
||||
const auto maxWithCaption = qMin(st::msgMaxWidth, captionMaxWidth);
|
||||
newWidth = qMin(qMax(newWidth, maxWithCaption), thumbMaxWidth);
|
||||
newHeight = adjustHeightForLessCrop(
|
||||
scaled,
|
||||
|
@ -248,6 +255,9 @@ QSize Gif::countCurrentSize(int newWidth) {
|
|||
const auto captionw = newWidth
|
||||
- st::msgPadding.left()
|
||||
- st::msgPadding.right();
|
||||
if (botTop) {
|
||||
newHeight += botTop->height;
|
||||
}
|
||||
newHeight += st::mediaCaptionSkip + _caption.countHeight(captionw);
|
||||
if (isBubbleBottom()) {
|
||||
newHeight += st::msgPadding.bottom();
|
||||
|
@ -349,12 +359,16 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
|
|||
const auto outbg = context.outbg;
|
||||
const auto inWebPage = (_parent->media() != this);
|
||||
const auto isRound = _data->isVideoMessage();
|
||||
const auto botTop = _parent->Get<FakeBotAboutTop>();
|
||||
|
||||
const auto rounding = inWebPage
|
||||
? std::optional<Ui::BubbleRounding>()
|
||||
: adjustedBubbleRoundingWithCaption(_caption);
|
||||
if (bubble) {
|
||||
if (!_caption.isEmpty()) {
|
||||
if (botTop) {
|
||||
painth -= botTop->height;
|
||||
}
|
||||
painth -= st::mediaCaptionSkip + _caption.countHeight(captionw);
|
||||
if (isBubbleBottom()) {
|
||||
painth -= st::msgPadding.bottom();
|
||||
|
@ -674,10 +688,18 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
|
|||
if (!unwrapped && !_caption.isEmpty()) {
|
||||
p.setPen(stm->historyTextFg);
|
||||
_parent->prepareCustomEmojiPaint(p, context, _caption);
|
||||
_caption.draw(p, {
|
||||
.position = QPoint(
|
||||
auto top = painty + painth + st::mediaCaptionSkip;
|
||||
if (botTop) {
|
||||
botTop->text.drawLeftElided(
|
||||
p,
|
||||
st::msgPadding.left(),
|
||||
painty + painth + st::mediaCaptionSkip),
|
||||
top,
|
||||
captionw,
|
||||
_parent->width());
|
||||
top += botTop->height;
|
||||
}
|
||||
_caption.draw(p, {
|
||||
.position = QPoint(st::msgPadding.left(), top),
|
||||
.availableWidth = captionw,
|
||||
.palette = &stm->textPalette,
|
||||
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
||||
|
@ -956,6 +978,9 @@ TextState Gif::textState(QPoint point, StateRequest request) const {
|
|||
request.forText()));
|
||||
return result;
|
||||
}
|
||||
if (const auto botTop = _parent->Get<FakeBotAboutTop>()) {
|
||||
painth -= botTop->height;
|
||||
}
|
||||
painth -= st::mediaCaptionSkip;
|
||||
}
|
||||
const auto outbg = _parent->hasOutLayout();
|
||||
|
|
|
@ -187,6 +187,10 @@ QSize Photo::countOptimalSize() {
|
|||
minHeight = adjustHeightForLessCrop(
|
||||
dimensions,
|
||||
{ maxWidth, minHeight });
|
||||
if (const auto botTop = _parent->Get<FakeBotAboutTop>()) {
|
||||
accumulate_max(maxWidth, botTop->maxWidth);
|
||||
minHeight += botTop->height;
|
||||
}
|
||||
minHeight += st::mediaCaptionSkip + _caption.minHeight();
|
||||
if (isBubbleBottom()) {
|
||||
minHeight += st::msgPadding.bottom();
|
||||
|
@ -214,11 +218,14 @@ QSize Photo::countCurrentSize(int newWidth) {
|
|||
newWidth = qMax(pix.width(), minWidth);
|
||||
auto newHeight = qMax(pix.height(), st::minPhotoSize);
|
||||
if (_parent->hasBubble() && !_caption.isEmpty()) {
|
||||
const auto maxWithCaption = qMin(
|
||||
st::msgMaxWidth,
|
||||
(st::msgPadding.left()
|
||||
+ _caption.maxWidth()
|
||||
+ st::msgPadding.right()));
|
||||
auto captionMaxWidth = st::msgPadding.left()
|
||||
+ _caption.maxWidth()
|
||||
+ st::msgPadding.right();
|
||||
const auto botTop = _parent->Get<FakeBotAboutTop>();
|
||||
if (botTop) {
|
||||
accumulate_max(captionMaxWidth, botTop->maxWidth);
|
||||
}
|
||||
const auto maxWithCaption = qMin(st::msgMaxWidth, captionMaxWidth);
|
||||
newWidth = qMin(qMax(newWidth, maxWithCaption), thumbMaxWidth);
|
||||
newHeight = adjustHeightForLessCrop(
|
||||
dimensions,
|
||||
|
@ -226,6 +233,9 @@ QSize Photo::countCurrentSize(int newWidth) {
|
|||
const auto captionw = newWidth
|
||||
- st::msgPadding.left()
|
||||
- st::msgPadding.right();
|
||||
if (botTop) {
|
||||
newHeight += botTop->height;
|
||||
}
|
||||
newHeight += st::mediaCaptionSkip + _caption.countHeight(captionw);
|
||||
if (isBubbleBottom()) {
|
||||
newHeight += st::msgPadding.bottom();
|
||||
|
@ -268,6 +278,7 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
|
|||
}
|
||||
}
|
||||
const auto radial = isRadialAnimation();
|
||||
const auto botTop = _parent->Get<FakeBotAboutTop>();
|
||||
|
||||
auto rthumb = style::rtlrect(paintx, painty, paintw, painth, width());
|
||||
if (_serviceWidth > 0) {
|
||||
|
@ -279,6 +290,9 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
|
|||
if (bubble) {
|
||||
if (!_caption.isEmpty()) {
|
||||
painth -= st::mediaCaptionSkip + _caption.countHeight(captionw);
|
||||
if (botTop) {
|
||||
painth -= botTop->height;
|
||||
}
|
||||
if (isBubbleBottom()) {
|
||||
painth -= st::msgPadding.bottom();
|
||||
}
|
||||
|
@ -348,10 +362,18 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
|
|||
if (!_caption.isEmpty()) {
|
||||
p.setPen(stm->historyTextFg);
|
||||
_parent->prepareCustomEmojiPaint(p, context, _caption);
|
||||
_caption.draw(p, {
|
||||
.position = QPoint(
|
||||
auto top = painty + painth + st::mediaCaptionSkip;
|
||||
if (botTop) {
|
||||
botTop->text.drawLeftElided(
|
||||
p,
|
||||
st::msgPadding.left(),
|
||||
painty + painth + st::mediaCaptionSkip),
|
||||
top,
|
||||
captionw,
|
||||
_parent->width());
|
||||
top += botTop->height;
|
||||
}
|
||||
_caption.draw(p, {
|
||||
.position = QPoint(st::msgPadding.left(), top),
|
||||
.availableWidth = captionw,
|
||||
.palette = &stm->textPalette,
|
||||
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
||||
|
@ -592,6 +614,9 @@ TextState Photo::textState(QPoint point, StateRequest request) const {
|
|||
request.forText()));
|
||||
return result;
|
||||
}
|
||||
if (const auto botTop = _parent->Get<FakeBotAboutTop>()) {
|
||||
painth -= botTop->height;
|
||||
}
|
||||
painth -= st::mediaCaptionSkip;
|
||||
}
|
||||
if (QRect(paintx, painty, paintw, painth).contains(point)) {
|
||||
|
|
Loading…
Add table
Reference in a new issue