mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Added initial implementation of top bar for sponsored messages.
This commit is contained in:
parent
c857c24a64
commit
8a1cf2bb3a
8 changed files with 322 additions and 2 deletions
|
@ -1503,6 +1503,8 @@ PRIVATE
|
|||
ui/chat/choose_send_as.h
|
||||
ui/chat/choose_theme_controller.cpp
|
||||
ui/chat/choose_theme_controller.h
|
||||
ui/chat/sponsored_message_bar.cpp
|
||||
ui/chat/sponsored_message_bar.h
|
||||
ui/controls/emoji_button_factory.cpp
|
||||
ui/controls/emoji_button_factory.h
|
||||
ui/controls/location_picker.cpp
|
||||
|
|
|
@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/history_view_element.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/chat/sponsored_message_bar.h"
|
||||
#include "ui/text/text_utilities.h" // Ui::Text::RichLangValue.
|
||||
|
||||
namespace Data {
|
||||
|
@ -260,12 +261,33 @@ void SponsoredMessages::parse(
|
|||
list.postsBetween = postsBetween->v;
|
||||
list.state = State::InjectToMiddle;
|
||||
} else {
|
||||
list.state = State::AppendToEnd;
|
||||
list.state = history->peer->isChannel()
|
||||
? State::AppendToEnd
|
||||
: State::AppendToTopBar;
|
||||
}
|
||||
}, [](const MTPDmessages_sponsoredMessagesEmpty &) {
|
||||
});
|
||||
}
|
||||
|
||||
void SponsoredMessages::fillTopBar(
|
||||
not_null<History*> history,
|
||||
not_null<Ui::RpWidget*> widget) {
|
||||
const auto it = _data.find(history);
|
||||
if (it == end(_data)) {
|
||||
return;
|
||||
}
|
||||
const auto &list = it->second;
|
||||
if (list.entries.empty()) {
|
||||
return;
|
||||
}
|
||||
Ui::FillSponsoredMessageBar(
|
||||
widget,
|
||||
_session,
|
||||
list.entries.front().itemFullId,
|
||||
list.entries.front().sponsored.from,
|
||||
list.entries.front().sponsored.textWithEntities);
|
||||
}
|
||||
|
||||
void SponsoredMessages::append(
|
||||
not_null<History*> history,
|
||||
List &list,
|
||||
|
|
|
@ -18,6 +18,10 @@ namespace Main {
|
|||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Ui {
|
||||
class RpWidget;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Data {
|
||||
|
||||
class MediaPreload;
|
||||
|
@ -76,6 +80,7 @@ public:
|
|||
None,
|
||||
AppendToEnd,
|
||||
InjectToMiddle,
|
||||
AppendToTopBar,
|
||||
};
|
||||
struct Details {
|
||||
std::vector<TextWithEntities> info;
|
||||
|
@ -98,6 +103,9 @@ public:
|
|||
void clearItems(not_null<History*> history);
|
||||
[[nodiscard]] Details lookupDetails(const FullMsgId &fullId) const;
|
||||
void clicked(const FullMsgId &fullId, bool isMedia, bool isFullscreen);
|
||||
void fillTopBar(
|
||||
not_null<History*> history,
|
||||
not_null<Ui::RpWidget*> widget);
|
||||
|
||||
[[nodiscard]] AppendResult append(not_null<History*> history);
|
||||
void inject(
|
||||
|
|
|
@ -1611,6 +1611,9 @@ void HistoryWidget::orderWidgets() {
|
|||
if (_translateBar) {
|
||||
_translateBar->raise();
|
||||
}
|
||||
if (_sponsoredMessageBar) {
|
||||
_sponsoredMessageBar->raise();
|
||||
}
|
||||
if (_pinnedBar) {
|
||||
_pinnedBar->raise();
|
||||
}
|
||||
|
@ -2284,6 +2287,7 @@ void HistoryWidget::showHistory(
|
|||
_history->showAtMsgId = _showAtMsgId;
|
||||
|
||||
destroyUnreadBarOnClose();
|
||||
_sponsoredMessageBar = nullptr;
|
||||
_pinnedBar = nullptr;
|
||||
_translateBar = nullptr;
|
||||
_pinnedTracker = nullptr;
|
||||
|
@ -2520,6 +2524,26 @@ void HistoryWidget::showHistory(
|
|||
session().sponsoredMessages().canHaveFor(_history));
|
||||
} else if (state == State::InjectToMiddle) {
|
||||
injectSponsoredMessages();
|
||||
} else if (state == State::AppendToTopBar) {
|
||||
_sponsoredMessageBar
|
||||
= base::make_unique_q<Ui::SlideWrap<>>(
|
||||
this,
|
||||
object_ptr<Ui::RpWidget>(this));
|
||||
session().sponsoredMessages().fillTopBar(
|
||||
_history,
|
||||
_sponsoredMessageBar->entity());
|
||||
_sponsoredMessageBarHeight = 0;
|
||||
_sponsoredMessageBar->heightValue(
|
||||
) | rpl::start_with_next([=](int height) {
|
||||
_topDelta = _preserveScrollTop
|
||||
? 0
|
||||
: (height - _sponsoredMessageBarHeight);
|
||||
_sponsoredMessageBarHeight = height;
|
||||
updateHistoryGeometry();
|
||||
updateControlsGeometry();
|
||||
_topDelta = 0;
|
||||
}, _sponsoredMessageBar->lifetime());
|
||||
_sponsoredMessageBar->show(anim::type::normal);
|
||||
}
|
||||
});
|
||||
session().sponsoredMessages().request(_history, checkState);
|
||||
|
@ -2931,6 +2955,9 @@ void HistoryWidget::updateControlsVisibility() {
|
|||
if (_pinnedBar) {
|
||||
_pinnedBar->show();
|
||||
}
|
||||
if (_sponsoredMessageBar) {
|
||||
_sponsoredMessageBar->show(anim::type::instant);
|
||||
}
|
||||
if (_translateBar) {
|
||||
_translateBar->show();
|
||||
}
|
||||
|
@ -4128,6 +4155,9 @@ void HistoryWidget::hideChildWidgets() {
|
|||
if (_pinnedBar) {
|
||||
_pinnedBar->hide();
|
||||
}
|
||||
if (_sponsoredMessageBar) {
|
||||
_sponsoredMessageBar->hide(anim::type::instant);
|
||||
}
|
||||
if (_translateBar) {
|
||||
_translateBar->hide();
|
||||
}
|
||||
|
@ -4414,6 +4444,9 @@ void HistoryWidget::showAnimated(
|
|||
if (_pinnedBar) {
|
||||
_pinnedBar->finishAnimating();
|
||||
}
|
||||
if (_sponsoredMessageBar) {
|
||||
_sponsoredMessageBar->finishAnimating();
|
||||
}
|
||||
if (_translateBar) {
|
||||
_translateBar->finishAnimating();
|
||||
}
|
||||
|
@ -4452,6 +4485,9 @@ void HistoryWidget::showFinished() {
|
|||
if (_pinnedBar) {
|
||||
_pinnedBar->finishAnimating();
|
||||
}
|
||||
if (_sponsoredMessageBar) {
|
||||
_sponsoredMessageBar->finishAnimating();
|
||||
}
|
||||
if (_translateBar) {
|
||||
_translateBar->finishAnimating();
|
||||
}
|
||||
|
@ -4484,6 +4520,9 @@ void HistoryWidget::doneShow() {
|
|||
if (_pinnedBar) {
|
||||
_pinnedBar->finishAnimating();
|
||||
}
|
||||
if (_sponsoredMessageBar) {
|
||||
_sponsoredMessageBar->finishAnimating();
|
||||
}
|
||||
if (_translateBar) {
|
||||
_translateBar->finishAnimating();
|
||||
}
|
||||
|
@ -5999,8 +6038,14 @@ void HistoryWidget::updateControlsGeometry() {
|
|||
_pinnedBar->move(0, pinnedBarTop);
|
||||
_pinnedBar->resizeToWidth(width());
|
||||
}
|
||||
const auto translateTop = pinnedBarTop
|
||||
const auto sponsoredMessageBarTop = pinnedBarTop
|
||||
+ (_pinnedBar ? _pinnedBar->height() : 0);
|
||||
if (_sponsoredMessageBar) {
|
||||
_sponsoredMessageBar->move(0, sponsoredMessageBarTop);
|
||||
_sponsoredMessageBar->resizeToWidth(width());
|
||||
}
|
||||
const auto translateTop = sponsoredMessageBarTop
|
||||
+ (_sponsoredMessageBar ? _sponsoredMessageBar->height() : 0);
|
||||
if (_translateBar) {
|
||||
_translateBar->move(0, translateTop);
|
||||
_translateBar->resizeToWidth(width());
|
||||
|
@ -6245,6 +6290,9 @@ void HistoryWidget::updateHistoryGeometry(
|
|||
if (_translateBar) {
|
||||
newScrollHeight -= _translateBar->height();
|
||||
}
|
||||
if (_sponsoredMessageBar) {
|
||||
newScrollHeight -= _sponsoredMessageBar->height();
|
||||
}
|
||||
if (_pinnedBar) {
|
||||
newScrollHeight -= _pinnedBar->height();
|
||||
}
|
||||
|
@ -6662,6 +6710,7 @@ int HistoryWidget::computeMaxFieldHeight() const {
|
|||
- _topBar->height()
|
||||
- (_contactStatus ? _contactStatus->bar().height() : 0)
|
||||
- (_businessBotStatus ? _businessBotStatus->bar().height() : 0)
|
||||
- (_sponsoredMessageBar ? _sponsoredMessageBar->height() : 0)
|
||||
- (_pinnedBar ? _pinnedBar->height() : 0)
|
||||
- (_groupCallBar ? _groupCallBar->height() : 0)
|
||||
- (_requestsBar ? _requestsBar->height() : 0)
|
||||
|
|
|
@ -73,6 +73,8 @@ class SpoilerAnimation;
|
|||
class ChooseThemeController;
|
||||
class ContinuousScroll;
|
||||
struct ChatPaintHighlight;
|
||||
template <typename Widget>
|
||||
class SlideWrap;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Ui::Emoji {
|
||||
|
@ -687,6 +689,9 @@ private:
|
|||
std::unique_ptr<Ui::RequestsBar> _requestsBar;
|
||||
int _requestsBarHeight = 0;
|
||||
|
||||
base::unique_qptr<Ui::SlideWrap<Ui::RpWidget>> _sponsoredMessageBar;
|
||||
int _sponsoredMessageBarHeight = 0;
|
||||
|
||||
bool _preserveScrollTop = false;
|
||||
bool _repaintFieldScheduled = false;
|
||||
|
||||
|
|
|
@ -1153,3 +1153,5 @@ purchasedTagPadding: margins(3px, 2px, 6px, 2px);
|
|||
msgSelectionCheck: RoundCheckbox(defaultPeerListCheck) {
|
||||
bgActive: boxTextFgGood;
|
||||
}
|
||||
|
||||
sponsoredMessageBarMaxHeight: 156px;
|
||||
|
|
203
Telegram/SourceFiles/ui/chat/sponsored_message_bar.cpp
Normal file
203
Telegram/SourceFiles/ui/chat/sponsored_message_bar.cpp
Normal file
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "ui/chat/sponsored_message_bar.h"
|
||||
|
||||
#include "core/ui_integration.h" // Core::MarkedTextContext.
|
||||
#include "data/components/sponsored_messages.h"
|
||||
#include "data/data_session.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/dynamic_image.h"
|
||||
#include "ui/dynamic_thumbnails.h"
|
||||
#include "ui/image/image_prepare.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/rp_widget.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
|
||||
namespace Ui {
|
||||
|
||||
void FillSponsoredMessageBar(
|
||||
not_null<RpWidget*> widget,
|
||||
not_null<Main::Session*> session,
|
||||
FullMsgId fullId,
|
||||
Data::SponsoredFrom from,
|
||||
const TextWithEntities &textWithEntities) {
|
||||
struct State final {
|
||||
Ui::Text::String title;
|
||||
Ui::Text::String contentTitle;
|
||||
Ui::Text::String contentText;
|
||||
rpl::variable<int> lastPaintedContentLineAmount = 0;
|
||||
rpl::variable<int> lastPaintedContentTop = 0;
|
||||
|
||||
std::shared_ptr<Ui::DynamicImage> rightPhoto;
|
||||
QImage rightPhotoImage;
|
||||
};
|
||||
const auto state = widget->lifetime().make_state<State>();
|
||||
state->title.setText(
|
||||
st::semiboldTextStyle,
|
||||
from.isRecommended
|
||||
? tr::lng_recommended_message_title(tr::now)
|
||||
: tr::lng_sponsored_message_title(tr::now));
|
||||
state->contentTitle.setText(st::semiboldTextStyle, from.title);
|
||||
state->contentText.setMarkedText(
|
||||
st::defaultTextStyle,
|
||||
textWithEntities,
|
||||
kMarkupTextOptions,
|
||||
Core::MarkedTextContext{
|
||||
.session = session,
|
||||
.customEmojiRepaint = [=] { widget->update(); },
|
||||
});
|
||||
const auto kLinesForPhoto = 3;
|
||||
const auto rightPhotoSize = state->title.style()->font->ascent
|
||||
* kLinesForPhoto;
|
||||
const auto rightPhotoPlaceholder = state->title.style()->font->height
|
||||
* kLinesForPhoto;
|
||||
const auto hasRightPhoto = from.photoId > 0;
|
||||
if (hasRightPhoto) {
|
||||
state->rightPhoto = Ui::MakePhotoThumbnail(
|
||||
session->data().photo(from.photoId),
|
||||
fullId);
|
||||
const auto callback = [=] {
|
||||
state->rightPhotoImage = Images::Round(
|
||||
state->rightPhoto->image(rightPhotoSize),
|
||||
ImageRoundRadius::Small);
|
||||
widget->update();
|
||||
};
|
||||
state->rightPhoto->subscribeToUpdates(callback);
|
||||
callback();
|
||||
}
|
||||
widget->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
auto p = QPainter(widget);
|
||||
const auto r = widget->rect();
|
||||
p.fillRect(r, st::historyPinnedBg);
|
||||
const auto leftPadding = st::msgReplyBarSkip + st::msgReplyBarSkip;
|
||||
const auto rightPadding = st::msgReplyBarSkip;
|
||||
const auto topPadding = st::msgReplyPadding.top();
|
||||
const auto availableWidthNoPhoto = r.width()
|
||||
- leftPadding
|
||||
- rightPadding;
|
||||
const auto availableWidth = availableWidthNoPhoto
|
||||
- (hasRightPhoto ? (rightPadding + rightPhotoSize) : 0);
|
||||
const auto titleRight = leftPadding
|
||||
+ state->title.maxWidth()
|
||||
+ state->title.style()->font->spacew * 2;
|
||||
const auto hasSecondLineTitle
|
||||
= (availableWidth - state->contentTitle.maxWidth() < titleRight);
|
||||
p.setPen(st::windowActiveTextFg);
|
||||
state->title.draw(p, {
|
||||
.position = QPoint(leftPadding, topPadding),
|
||||
.outerWidth = availableWidth,
|
||||
.availableWidth = availableWidth,
|
||||
});
|
||||
p.setPen(st::windowFg);
|
||||
{
|
||||
const auto left = hasSecondLineTitle ? leftPadding : titleRight;
|
||||
const auto top = hasSecondLineTitle
|
||||
? (topPadding + state->title.style()->font->height)
|
||||
: topPadding;
|
||||
state->contentTitle.draw(p, {
|
||||
.position = QPoint(left, top),
|
||||
.outerWidth = hasSecondLineTitle
|
||||
? availableWidth
|
||||
: (availableWidth - titleRight),
|
||||
.availableWidth = availableWidth,
|
||||
.elisionLines = 1,
|
||||
});
|
||||
}
|
||||
{
|
||||
const auto left = leftPadding;
|
||||
const auto top = hasSecondLineTitle
|
||||
? (topPadding
|
||||
+ state->title.style()->font->height
|
||||
+ state->contentTitle.style()->font->height)
|
||||
: topPadding + state->title.style()->font->height;
|
||||
auto lastContentLineAmount = 0;
|
||||
const auto lineHeight = state->contentText.style()->font->height;
|
||||
const auto lineLayout = [&](int line) -> Ui::Text::LineGeometry {
|
||||
line++;
|
||||
lastContentLineAmount = line;
|
||||
const auto diff = (st::sponsoredMessageBarMaxHeight)
|
||||
- line * lineHeight;
|
||||
if (diff < 3 * lineHeight) {
|
||||
return {
|
||||
.width = availableWidthNoPhoto,
|
||||
.elided = true,
|
||||
};
|
||||
} else if (diff < 2 * lineHeight) {
|
||||
return {};
|
||||
}
|
||||
if (hasRightPhoto) {
|
||||
line += (hasSecondLineTitle ? 2 : 1);
|
||||
return {
|
||||
.width = (line > kLinesForPhoto)
|
||||
? availableWidthNoPhoto
|
||||
: availableWidth,
|
||||
};
|
||||
} else {
|
||||
return { .width = availableWidth };
|
||||
}
|
||||
};
|
||||
state->contentText.draw(p, {
|
||||
.position = QPoint(left, top),
|
||||
.outerWidth = availableWidth,
|
||||
.availableWidth = availableWidth,
|
||||
.geometry = Ui::Text::GeometryDescriptor{
|
||||
.layout = std::move(lineLayout),
|
||||
},
|
||||
});
|
||||
state->lastPaintedContentTop = top;
|
||||
state->lastPaintedContentLineAmount = lastContentLineAmount;
|
||||
}
|
||||
if (hasRightPhoto) {
|
||||
p.drawImage(
|
||||
r.width() - rightPadding - rightPhotoSize,
|
||||
topPadding + (rightPhotoPlaceholder - rightPhotoSize) / 2,
|
||||
state->rightPhotoImage);
|
||||
}
|
||||
}, widget->lifetime());
|
||||
rpl::combine(
|
||||
state->lastPaintedContentTop.value(),
|
||||
state->lastPaintedContentLineAmount.value()
|
||||
) | rpl::distinct_until_changed() | rpl::start_with_next([=](
|
||||
int lastTop,
|
||||
int lastLines) {
|
||||
const auto bottomPadding = st::msgReplyPadding.top();
|
||||
const auto desiredHeight = lastTop
|
||||
+ (lastLines * state->contentText.style()->font->height)
|
||||
+ bottomPadding;
|
||||
const auto minHeight = hasRightPhoto
|
||||
? (rightPhotoPlaceholder + bottomPadding * 2)
|
||||
: desiredHeight;
|
||||
widget->resize(
|
||||
widget->width(),
|
||||
std::clamp(
|
||||
desiredHeight,
|
||||
minHeight,
|
||||
st::sponsoredMessageBarMaxHeight));
|
||||
}, widget->lifetime());
|
||||
widget->resize(widget->width(), 1);
|
||||
|
||||
{
|
||||
const auto top = Ui::CreateChild<PlainShadow>(widget);
|
||||
const auto bottom = Ui::CreateChild<PlainShadow>(widget);
|
||||
widget->sizeValue() | rpl::start_with_next([=] (const QSize &s) {
|
||||
top->show();
|
||||
top->raise();
|
||||
top->resizeToWidth(s.width());
|
||||
bottom->show();
|
||||
bottom->raise();
|
||||
bottom->resizeToWidth(s.width());
|
||||
bottom->moveToLeft(0, s.height() - bottom->height());
|
||||
}, top->lifetime());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Ui
|
29
Telegram/SourceFiles/ui/chat/sponsored_message_bar.h
Normal file
29
Telegram/SourceFiles/ui/chat/sponsored_message_bar.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Data {
|
||||
struct SponsoredFrom;
|
||||
} // namespace Data
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class RpWidget;
|
||||
|
||||
void FillSponsoredMessageBar(
|
||||
not_null<RpWidget*> widget,
|
||||
not_null<Main::Session*> session,
|
||||
FullMsgId fullId,
|
||||
Data::SponsoredFrom from,
|
||||
const TextWithEntities &textWithEntities);
|
||||
|
||||
} // namespace Ui
|
Loading…
Add table
Reference in a new issue