mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 14:17:12 +02:00
Implement suggested profile photo message.
This commit is contained in:
parent
5fe9c93cb6
commit
48cf0a4382
24 changed files with 768 additions and 356 deletions
|
@ -673,8 +673,10 @@ PRIVATE
|
|||
history/view/media/history_view_photo.h
|
||||
history/view/media/history_view_poll.cpp
|
||||
history/view/media/history_view_poll.h
|
||||
history/view/media/history_view_service_media_gift.cpp
|
||||
history/view/media/history_view_service_media_gift.h
|
||||
history/view/media/history_view_premium_gift.cpp
|
||||
history/view/media/history_view_premium_gift.h
|
||||
history/view/media/history_view_service_box.cpp
|
||||
history/view/media/history_view_service_box.h
|
||||
history/view/media/history_view_slot_machine.cpp
|
||||
history/view/media/history_view_slot_machine.h
|
||||
history/view/media/history_view_sticker.cpp
|
||||
|
@ -684,6 +686,8 @@ PRIVATE
|
|||
history/view/media/history_view_sticker_player_abstract.h
|
||||
history/view/media/history_view_theme_document.cpp
|
||||
history/view/media/history_view_theme_document.h
|
||||
history/view/media/history_view_userpic_suggestion.cpp
|
||||
history/view/media/history_view_userpic_suggestion.h
|
||||
history/view/media/history_view_web_page.cpp
|
||||
history/view/media/history_view_web_page.h
|
||||
history/view/reactions/history_view_reactions.cpp
|
||||
|
|
|
@ -1561,6 +1561,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_action_webview_data_done" = "You have just successfully transferred data from the «{text}» button to the bot.";
|
||||
"lng_action_gift_received" = "{user} sent you a gift for {cost}";
|
||||
"lng_action_gift_received_me" = "You sent to {user} a gift for {cost}";
|
||||
"lng_action_suggested_photo_title" = "Suggested Photo";
|
||||
"lng_action_suggested_photo_me" = "You suggested {user} to use this photo for their account.";
|
||||
"lng_action_suggested_photo" = "{user} suggests you to use this photo for your account.";
|
||||
"lng_action_topic_created_inside" = "Topic created";
|
||||
"lng_action_topic_closed_inside" = "Topic closed";
|
||||
"lng_action_topic_reopened_inside" = "Topic reopened";
|
||||
|
|
|
@ -116,6 +116,17 @@ void PeerPhoto::upload(not_null<PeerData*> peer, QImage &&image) {
|
|||
upload(peer, std::move(image), false);
|
||||
}
|
||||
|
||||
void PeerPhoto::updateSelf(not_null<PhotoData*> photo) {
|
||||
_api.request(MTPphotos_UpdateProfilePhoto(
|
||||
photo->mtpInput()
|
||||
)).done([=](const MTPphotos_Photo &result) {
|
||||
result.match([&](const MTPDphotos_photo &data) {
|
||||
_session->data().processPhoto(data.vphoto());
|
||||
_session->data().processUsers(data.vusers());
|
||||
});
|
||||
}).send();
|
||||
}
|
||||
|
||||
void PeerPhoto::upload(
|
||||
not_null<PeerData*> peer,
|
||||
QImage &&image,
|
||||
|
@ -195,8 +206,7 @@ void PeerPhoto::clearPersonal(not_null<UserData*> user) {
|
|||
_session->data().processUsers(data.vusers());
|
||||
});
|
||||
}).send();
|
||||
if (!user->userpicPhotoUnknown()
|
||||
&& (user->flags() & UserDataFlag::PersonalPhoto)) {
|
||||
if (!user->userpicPhotoUnknown() && user->hasPersonalPhoto()) {
|
||||
_session->storage().remove(Storage::UserPhotosRemoveOne(
|
||||
peerToUser(user->id),
|
||||
user->userpicPhotoId()));
|
||||
|
@ -331,8 +341,7 @@ void PeerPhoto::requestUserPhotos(
|
|||
}
|
||||
return photoIds;
|
||||
});
|
||||
if (!user->userpicPhotoUnknown()
|
||||
&& (user->flags() & UserDataFlag::PersonalPhoto)) {
|
||||
if (!user->userpicPhotoUnknown() && user->hasPersonalPhoto()) {
|
||||
const auto photo = owner.photo(user->userpicPhotoId());
|
||||
if (!photo->isNull()) {
|
||||
++fullCount;
|
||||
|
|
|
@ -25,6 +25,7 @@ public:
|
|||
explicit PeerPhoto(not_null<ApiWrap*> api);
|
||||
|
||||
void upload(not_null<PeerData*> peer, QImage &&image);
|
||||
void updateSelf(not_null<PhotoData*> photo);
|
||||
void suggest(not_null<PeerData*> peer, QImage &&image);
|
||||
void clear(not_null<PhotoData*> photo);
|
||||
void clearPersonal(not_null<UserData*> user);
|
||||
|
|
|
@ -27,7 +27,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/media/history_view_theme_document.h"
|
||||
#include "history/view/media/history_view_slot_machine.h"
|
||||
#include "history/view/media/history_view_dice.h"
|
||||
#include "history/view/media/history_view_service_media_gift.h"
|
||||
#include "history/view/media/history_view_service_box.h"
|
||||
#include "history/view/media/history_view_premium_gift.h"
|
||||
#include "history/view/media/history_view_userpic_suggestion.h"
|
||||
#include "dialogs/ui/dialogs_message_view.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/effects/spoiler_mess.h"
|
||||
|
@ -702,6 +704,15 @@ std::unique_ptr<HistoryView::Media> MediaPhoto::createView(
|
|||
not_null<HistoryItem*> realParent,
|
||||
HistoryView::Element *replacing) {
|
||||
if (_chat) {
|
||||
if (realParent->isUserpicSuggestion()) {
|
||||
return std::make_unique<HistoryView::ServiceBox>(
|
||||
message,
|
||||
std::make_unique<HistoryView::UserpicSuggestion>(
|
||||
message,
|
||||
_chat,
|
||||
_photo,
|
||||
st::msgServicePhotoWidth));
|
||||
}
|
||||
return std::make_unique<HistoryView::Photo>(
|
||||
message,
|
||||
_chat,
|
||||
|
@ -1907,7 +1918,9 @@ std::unique_ptr<HistoryView::Media> MediaGiftBox::createView(
|
|||
not_null<HistoryView::Element*> message,
|
||||
not_null<HistoryItem*> realParent,
|
||||
HistoryView::Element *replacing) {
|
||||
return std::make_unique<HistoryView::MediaGift>(message, this);
|
||||
return std::make_unique<HistoryView::ServiceBox>(
|
||||
message,
|
||||
std::make_unique<HistoryView::PremiumGift>(message, this));
|
||||
}
|
||||
|
||||
bool MediaGiftBox::activated() const {
|
||||
|
|
|
@ -285,6 +285,9 @@ enum class MessageFlag : uint64 {
|
|||
|
||||
// Optimization for item text custom emoji repainting.
|
||||
CustomEmojiRepainting = (1ULL << 32),
|
||||
|
||||
// Profile photo suggestion, views have special media type.
|
||||
IsUserpicSuggestion = (1ULL << 33),
|
||||
};
|
||||
inline constexpr bool is_flag_type(MessageFlag) { return true; }
|
||||
using MessageFlags = base::flags<MessageFlag>;
|
||||
|
|
|
@ -298,6 +298,10 @@ bool UserData::applyMinPhoto() const {
|
|||
return !(flags() & UserDataFlag::DiscardMinPhoto);
|
||||
}
|
||||
|
||||
bool UserData::hasPersonalPhoto() const {
|
||||
return (flags() & UserDataFlag::PersonalPhoto);
|
||||
}
|
||||
|
||||
bool UserData::canAddContact() const {
|
||||
return canShareThisContact() && !isContact();
|
||||
}
|
||||
|
|
|
@ -114,6 +114,7 @@ public:
|
|||
[[nodiscard]] bool isInaccessible() const;
|
||||
[[nodiscard]] bool canWrite() const;
|
||||
[[nodiscard]] bool applyMinPhoto() const;
|
||||
[[nodiscard]] bool hasPersonalPhoto() const;
|
||||
|
||||
[[nodiscard]] bool canShareThisContact() const;
|
||||
[[nodiscard]] bool canAddContact() const;
|
||||
|
|
|
@ -123,7 +123,8 @@ void PrepareProfilePhoto(
|
|||
.cropType = (radius == ImageRoundRadius::Ellipse
|
||||
? EditorData::CropType::Ellipse
|
||||
: EditorData::CropType::RoundedRect),
|
||||
.keepAspectRatio = true, }),
|
||||
.keepAspectRatio = true,
|
||||
}),
|
||||
Ui::LayerOption::KeepOther);
|
||||
}
|
||||
|
||||
|
|
|
@ -1318,6 +1318,10 @@ bool HistoryItem::skipNotification() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool HistoryItem::isUserpicSuggestion() const {
|
||||
return (_flags & MessageFlag::IsUserpicSuggestion);
|
||||
}
|
||||
|
||||
void HistoryItem::destroy() {
|
||||
_history->destroyMessage(this);
|
||||
}
|
||||
|
@ -3872,12 +3876,20 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
|
|||
|
||||
auto prepareSuggestProfilePhoto = [this](const MTPDmessageActionSuggestProfilePhoto &action) {
|
||||
auto result = PreparedServiceText{};
|
||||
result.links.push_back(fromLink());
|
||||
result.text = tr::lng_action_changed_photo(
|
||||
tr::now,
|
||||
lt_from,
|
||||
fromLinkText(), // Link 1.
|
||||
Ui::Text::WithEntities);
|
||||
const auto isSelf = (_from->id == _from->session().userPeerId());
|
||||
const auto peer = isSelf ? history()->peer : _from;
|
||||
const auto user = peer->asUser();
|
||||
const auto name = (user && !user->firstName.isEmpty())
|
||||
? user->firstName
|
||||
: peer->name();
|
||||
result.links.push_back(peer->createOpenLink());
|
||||
result.text = (isSelf
|
||||
? tr::lng_action_suggested_photo_me
|
||||
: tr::lng_action_suggested_photo)(
|
||||
tr::now,
|
||||
lt_user,
|
||||
Ui::Text::Link(name, 1), // Link 1.
|
||||
Ui::Text::WithEntities);
|
||||
return result;
|
||||
};
|
||||
|
||||
|
@ -4009,6 +4021,15 @@ void HistoryItem::applyAction(const MTPMessageAction &action) {
|
|||
this,
|
||||
_from,
|
||||
data.vmonths().v);
|
||||
}, [&](const MTPDmessageActionSuggestProfilePhoto &data) {
|
||||
data.vphoto().match([&](const MTPDphoto &photo) {
|
||||
_flags |= MessageFlag::IsUserpicSuggestion;
|
||||
_media = std::make_unique<Data::MediaPhoto>(
|
||||
this,
|
||||
history()->peer,
|
||||
history()->owner().processPhoto(photo));
|
||||
}, [](const MTPDphotoEmpty &) {
|
||||
});
|
||||
}, [](const auto &) {
|
||||
});
|
||||
}
|
||||
|
|
|
@ -199,6 +199,7 @@ public:
|
|||
[[nodiscard]] bool isScheduled() const;
|
||||
[[nodiscard]] bool isSponsored() const;
|
||||
[[nodiscard]] bool skipNotification() const;
|
||||
[[nodiscard]] bool isUserpicSuggestion() const;
|
||||
|
||||
void addLogEntryOriginal(
|
||||
WebPageId localId,
|
||||
|
|
|
@ -427,8 +427,10 @@ QSize Service::performCountCurrentSize(int newWidth) {
|
|||
if (isHidden()) {
|
||||
return { newWidth, newHeight };
|
||||
}
|
||||
|
||||
if (!text().isEmpty()) {
|
||||
const auto media = this->media();
|
||||
if (media && data()->isUserpicSuggestion()) {
|
||||
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());
|
||||
|
@ -443,7 +445,7 @@ QSize Service::performCountCurrentSize(int newWidth) {
|
|||
? minHeight()
|
||||
: textHeightFor(nwidth);
|
||||
newHeight += st::msgServicePadding.top() + st::msgServicePadding.bottom() + st::msgServiceMargin.top() + st::msgServiceMargin.bottom();
|
||||
if (const auto media = this->media()) {
|
||||
if (media) {
|
||||
newHeight += st::msgServiceMargin.top() + media->resizeGetHeight(media->maxWidth());
|
||||
}
|
||||
}
|
||||
|
@ -454,11 +456,14 @@ QSize Service::performCountCurrentSize(int newWidth) {
|
|||
QSize Service::performCountOptimalSize() {
|
||||
validateText();
|
||||
|
||||
auto maxWidth = text().maxWidth() + st::msgServicePadding.left() + st::msgServicePadding.right();
|
||||
auto minHeight = text().minHeight();
|
||||
if (const auto media = this->media()) {
|
||||
media->initDimensions();
|
||||
if (data()->isUserpicSuggestion()) {
|
||||
return { media->maxWidth(), media->minHeight() };
|
||||
}
|
||||
}
|
||||
auto maxWidth = text().maxWidth() + st::msgServicePadding.left() + st::msgServicePadding.right();
|
||||
auto minHeight = text().minHeight();
|
||||
return { maxWidth, minHeight };
|
||||
}
|
||||
|
||||
|
@ -519,40 +524,42 @@ void Service::draw(Painter &p, const PaintContext &context) const {
|
|||
p.setTextPalette(st->serviceTextPalette());
|
||||
|
||||
const auto media = this->media();
|
||||
if (media) {
|
||||
height -= margin.top() + media->height();
|
||||
const auto onlyMedia = (media && data()->isUserpicSuggestion());
|
||||
|
||||
if (!onlyMedia) {
|
||||
if (media) {
|
||||
height -= margin.top() + media->height();
|
||||
}
|
||||
const auto trect = QRect(g.left(), margin.top(), g.width(), height)
|
||||
- st::msgServicePadding;
|
||||
|
||||
ServiceMessagePainter::PaintComplexBubble(
|
||||
p,
|
||||
context.st,
|
||||
g.left(),
|
||||
g.width(),
|
||||
text(),
|
||||
trect);
|
||||
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.setPen(st->msgServiceFg());
|
||||
p.setFont(st::msgServiceFont);
|
||||
prepareCustomEmojiPaint(p, context, text());
|
||||
text().draw(p, {
|
||||
.position = trect.topLeft(),
|
||||
.availableWidth = trect.width(),
|
||||
.align = style::al_top,
|
||||
.palette = &st->serviceTextPalette(),
|
||||
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
||||
.now = context.now,
|
||||
.paused = context.paused,
|
||||
.selection = context.selection,
|
||||
.fullWidthSelection = false,
|
||||
});
|
||||
}
|
||||
|
||||
const auto trect = QRect(g.left(), margin.top(), g.width(), height)
|
||||
- st::msgServicePadding;
|
||||
|
||||
ServiceMessagePainter::PaintComplexBubble(
|
||||
p,
|
||||
context.st,
|
||||
g.left(),
|
||||
g.width(),
|
||||
text(),
|
||||
trect);
|
||||
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.setPen(st->msgServiceFg());
|
||||
p.setFont(st::msgServiceFont);
|
||||
prepareCustomEmojiPaint(p, context, text());
|
||||
text().draw(p, {
|
||||
.position = trect.topLeft(),
|
||||
.availableWidth = trect.width(),
|
||||
.align = style::al_top,
|
||||
.palette = &st->serviceTextPalette(),
|
||||
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
||||
.now = context.now,
|
||||
.paused = context.paused,
|
||||
.selection = context.selection,
|
||||
.fullWidthSelection = false,
|
||||
});
|
||||
|
||||
if (media) {
|
||||
const auto left = margin.left() + (g.width() - media->maxWidth()) / 2;
|
||||
const auto top = margin.top() + height + margin.top();
|
||||
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);
|
||||
|
@ -591,6 +598,7 @@ PointState Service::pointState(QPoint point) const {
|
|||
TextState Service::textState(QPoint point, StateRequest request) const {
|
||||
const auto item = data();
|
||||
const auto media = this->media();
|
||||
const auto onlyMedia = (media && data()->isUserpicSuggestion());
|
||||
|
||||
auto result = TextState(item);
|
||||
|
||||
|
@ -609,7 +617,9 @@ TextState Service::textState(QPoint point, StateRequest request) const {
|
|||
g.setHeight(g.height() - unreadbarh);
|
||||
}
|
||||
|
||||
if (media) {
|
||||
if (onlyMedia) {
|
||||
return media->textState(point - QPoint(st::msgServiceMargin.left() + (g.width() - media->maxWidth()) / 2, st::msgServiceMargin.top()), request);
|
||||
} else if (media) {
|
||||
g.setHeight(g.height() - (st::msgServiceMargin.top() + media->height()));
|
||||
}
|
||||
auto trect = g.marginsAdded(-st::msgServicePadding);
|
||||
|
|
|
@ -176,6 +176,10 @@ Storage::SharedMediaTypesMask Media::sharedMediaTypes() const {
|
|||
return {};
|
||||
}
|
||||
|
||||
not_null<Element*> Media::parent() const {
|
||||
return _parent;
|
||||
}
|
||||
|
||||
not_null<History*> Media::history() const {
|
||||
return _parent->history();
|
||||
}
|
||||
|
|
|
@ -79,6 +79,7 @@ public:
|
|||
explicit Media(not_null<Element*> parent) : _parent(parent) {
|
||||
}
|
||||
|
||||
[[nodiscard]] not_null<Element*> parent() const;
|
||||
[[nodiscard]] not_null<History*> history() const;
|
||||
|
||||
[[nodiscard]] virtual TextForMimeData selectedText(
|
||||
|
|
|
@ -59,6 +59,7 @@ public:
|
|||
PhotoData *getPhoto() const override {
|
||||
return _data;
|
||||
}
|
||||
void showPhoto(FullMsgId id);
|
||||
|
||||
QSize sizeForGroupingOptimal(int maxWidth) const override;
|
||||
QSize sizeForGrouping(int width) const override;
|
||||
|
@ -103,8 +104,6 @@ protected:
|
|||
private:
|
||||
struct Streamed;
|
||||
|
||||
void showPhoto(FullMsgId id);
|
||||
|
||||
void create(FullMsgId contextId, PeerData *chat = nullptr);
|
||||
|
||||
void playAnimation(bool autoplay) override;
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
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 "history/view/media/history_view_premium_gift.h"
|
||||
|
||||
#include "chat_helpers/stickers_gift_box_pack.h"
|
||||
#include "core/click_handler_types.h" // ClickHandlerContext
|
||||
#include "data/data_document.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "settings/settings_premium.h" // Settings::ShowGiftPremium
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] QString FormatGiftMonths(int months) {
|
||||
return (months < 12)
|
||||
? tr::lng_premium_gift_duration_months(tr::now, lt_count, months)
|
||||
: tr::lng_premium_gift_duration_years(
|
||||
tr::now,
|
||||
lt_count,
|
||||
std::round(months / 12.));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PremiumGift::PremiumGift(
|
||||
not_null<Element*> parent,
|
||||
not_null<Data::MediaGiftBox*> gift)
|
||||
: _parent(parent)
|
||||
, _gift(gift) {
|
||||
}
|
||||
|
||||
PremiumGift::~PremiumGift() = default;
|
||||
|
||||
int PremiumGift::top() {
|
||||
return st::msgServiceGiftBoxStickerTop;
|
||||
}
|
||||
|
||||
QSize PremiumGift::size() {
|
||||
return st::msgServiceGiftBoxStickerSize;
|
||||
}
|
||||
|
||||
QString PremiumGift::title() {
|
||||
return tr::lng_premium_summary_title(tr::now);
|
||||
}
|
||||
|
||||
QString PremiumGift::subtitle() {
|
||||
return FormatGiftMonths(_gift->months());
|
||||
}
|
||||
|
||||
ClickHandlerPtr PremiumGift::createViewLink() {
|
||||
const auto from = _gift->from();
|
||||
const auto to = _parent->history()->peer;
|
||||
const auto months = _gift->months();
|
||||
return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
if (const auto controller = my.sessionWindow.get()) {
|
||||
const auto me = (from->id == controller->session().userPeerId());
|
||||
const auto peer = me ? to : from;
|
||||
Settings::ShowGiftPremium(controller, peer, months, me);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void PremiumGift::draw(
|
||||
Painter &p,
|
||||
const PaintContext &context,
|
||||
const QRect &geometry) {
|
||||
if (_sticker) {
|
||||
_sticker->draw(p, context, geometry);
|
||||
} else {
|
||||
ensureStickerCreated();
|
||||
}
|
||||
}
|
||||
|
||||
void PremiumGift::stickerClearLoopPlayed() {
|
||||
if (_sticker) {
|
||||
_sticker->stickerClearLoopPlayed();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<StickerPlayer> PremiumGift::stickerTakePlayer(
|
||||
not_null<DocumentData*> data,
|
||||
const Lottie::ColorReplacements *replacements) {
|
||||
return _sticker
|
||||
? _sticker->stickerTakePlayer(data, replacements)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
bool PremiumGift::hasHeavyPart() {
|
||||
return (_sticker ? _sticker->hasHeavyPart() : false);
|
||||
}
|
||||
|
||||
void PremiumGift::unloadHeavyPart() {
|
||||
if (_sticker) {
|
||||
_sticker->unloadHeavyPart();
|
||||
}
|
||||
}
|
||||
|
||||
void PremiumGift::ensureStickerCreated() const {
|
||||
if (_sticker) {
|
||||
return;
|
||||
}
|
||||
const auto &session = _parent->history()->session();
|
||||
auto &packs = session.giftBoxStickersPacks();
|
||||
if (const auto document = packs.lookup(_gift->months())) {
|
||||
if (const auto sticker = document->sticker()) {
|
||||
const auto skipPremiumEffect = false;
|
||||
_sticker.emplace(_parent, document, skipPremiumEffect, _parent);
|
||||
_sticker->setDiceIndex(sticker->alt, 1);
|
||||
_sticker->setGiftBoxSticker(true);
|
||||
_sticker->initSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace HistoryView
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
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
|
||||
|
||||
#include "history/view/media/history_view_sticker.h"
|
||||
#include "history/view/media/history_view_service_box.h"
|
||||
|
||||
namespace Data {
|
||||
class MediaGiftBox;
|
||||
} // namespace Data
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
class PremiumGift final : public ServiceBoxContent {
|
||||
public:
|
||||
PremiumGift(
|
||||
not_null<Element*> parent,
|
||||
not_null<Data::MediaGiftBox*> gift);
|
||||
~PremiumGift();
|
||||
|
||||
int top() override;
|
||||
QSize size() override;
|
||||
QString title() override;
|
||||
QString subtitle() override;
|
||||
void draw(
|
||||
Painter &p,
|
||||
const PaintContext &context,
|
||||
const QRect &geometry) override;
|
||||
ClickHandlerPtr createViewLink() override;
|
||||
|
||||
void stickerClearLoopPlayed() override;
|
||||
std::unique_ptr<StickerPlayer> stickerTakePlayer(
|
||||
not_null<DocumentData*> data,
|
||||
const Lottie::ColorReplacements *replacements) override;
|
||||
|
||||
bool hasHeavyPart() override;
|
||||
void unloadHeavyPart() override;
|
||||
|
||||
private:
|
||||
void ensureStickerCreated() const;
|
||||
|
||||
const not_null<Element*> _parent;
|
||||
const not_null<Data::MediaGiftBox*> _gift;
|
||||
mutable std::optional<Sticker> _sticker;
|
||||
|
||||
};
|
||||
|
||||
} // namespace HistoryView
|
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
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 "history/view/media/history_view_service_box.h"
|
||||
//
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
#include "history/view/media/history_view_sticker_player_abstract.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/painter.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_premium.h"
|
||||
#include "styles/style_settings.h"
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
ServiceBox::ServiceBox(
|
||||
not_null<Element*> parent,
|
||||
std::unique_ptr<ServiceBoxContent> content)
|
||||
: Media(parent)
|
||||
, _parent(parent)
|
||||
, _content(std::move(content))
|
||||
, _button([&] {
|
||||
auto result = Button();
|
||||
result.repaint = [=] { repaint(); };
|
||||
result.text.setText(
|
||||
st::semiboldTextStyle,
|
||||
tr::lng_sticker_premium_view(tr::now));
|
||||
|
||||
const auto height = st::msgServiceGiftBoxButtonHeight;
|
||||
const auto &padding = st::msgServiceGiftBoxButtonPadding;
|
||||
result.size = QSize(
|
||||
result.text.maxWidth()
|
||||
+ height
|
||||
+ padding.left()
|
||||
+ padding.right(),
|
||||
height);
|
||||
|
||||
result.link = _content->createViewLink();
|
||||
|
||||
return result;
|
||||
}())
|
||||
, _maxWidth(st::msgServiceGiftBoxSize.width()
|
||||
- st::msgPadding.left()
|
||||
- st::msgPadding.right())
|
||||
, _title(
|
||||
st::settingsSubsectionTitle.style,
|
||||
_content->title(),
|
||||
kDefaultTextOptions,
|
||||
_maxWidth)
|
||||
, _subtitle(
|
||||
st::premiumPreviewAbout.style,
|
||||
_content->subtitle(),
|
||||
kDefaultTextOptions,
|
||||
_maxWidth)
|
||||
, _size(
|
||||
st::msgServiceGiftBoxSize.width(),
|
||||
(st::msgServiceGiftBoxTopSkip
|
||||
+ _content->top()
|
||||
+ _content->size().height()
|
||||
+ st::msgServiceGiftBoxTitlePadding.top()
|
||||
+ _title.countHeight(_maxWidth)
|
||||
+ st::msgServiceGiftBoxTitlePadding.bottom()
|
||||
+ _subtitle.countHeight(_maxWidth)
|
||||
+ st::msgServiceGiftBoxButtonMargins.top()
|
||||
+ _button.size.height()
|
||||
+ st::msgServiceGiftBoxButtonMargins.bottom()))
|
||||
, _innerSize(_size - QSize(0, st::msgServiceGiftBoxTopSkip)) {
|
||||
}
|
||||
|
||||
ServiceBox::~ServiceBox() = default;
|
||||
|
||||
QSize ServiceBox::countOptimalSize() {
|
||||
return _size;
|
||||
}
|
||||
|
||||
QSize ServiceBox::countCurrentSize(int newWidth) {
|
||||
return _size;
|
||||
}
|
||||
|
||||
void ServiceBox::draw(Painter &p, const PaintContext &context) const {
|
||||
p.translate(0, st::msgServiceGiftBoxTopSkip);
|
||||
|
||||
PainterHighQualityEnabler hq(p);
|
||||
const auto radius = st::msgServiceGiftBoxRadius;
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(context.st->msgServiceBg());
|
||||
p.drawRoundedRect(QRect(QPoint(), _innerSize), radius, radius);
|
||||
|
||||
const auto content = contentRect();
|
||||
auto top = content.top() + content.height();
|
||||
{
|
||||
p.setPen(context.st->msgServiceFg());
|
||||
const auto &padding = st::msgServiceGiftBoxTitlePadding;
|
||||
top += padding.top();
|
||||
_title.draw(p, st::msgPadding.left(), top, _maxWidth, style::al_top);
|
||||
top += _title.countHeight(_maxWidth) + padding.bottom();
|
||||
_subtitle.draw(p, st::msgPadding.left(), top, _maxWidth, style::al_top);
|
||||
top += _subtitle.countHeight(_maxWidth) + padding.bottom();
|
||||
}
|
||||
|
||||
{
|
||||
const auto position = buttonRect().topLeft();
|
||||
p.translate(position);
|
||||
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(context.st->msgServiceBg()); // ?
|
||||
_button.drawBg(p);
|
||||
p.setPen(context.st->msgServiceFg());
|
||||
if (_button.ripple) {
|
||||
const auto opacity = p.opacity();
|
||||
p.setOpacity(st::historyPollRippleOpacity);
|
||||
_button.ripple->paint(
|
||||
p,
|
||||
0,
|
||||
0,
|
||||
width(),
|
||||
&context.messageStyle()->msgWaveformInactive->c);
|
||||
p.setOpacity(opacity);
|
||||
}
|
||||
_button.text.draw(
|
||||
p,
|
||||
0,
|
||||
(_button.size.height() - _button.text.minHeight()) / 2,
|
||||
_button.size.width(),
|
||||
style::al_top);
|
||||
|
||||
p.translate(-position);
|
||||
}
|
||||
|
||||
_content->draw(p, context, content);
|
||||
|
||||
p.translate(0, -st::msgServiceGiftBoxTopSkip);
|
||||
}
|
||||
|
||||
TextState ServiceBox::textState(QPoint point, StateRequest request) const {
|
||||
auto result = TextState(_parent);
|
||||
{
|
||||
const auto rect = buttonRect();
|
||||
if (rect.contains(point)) {
|
||||
result.link = _button.link;
|
||||
_button.lastPoint = point - rect.topLeft();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ServiceBox::toggleSelectionByHandlerClick(
|
||||
const ClickHandlerPtr &p) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ServiceBox::dragItemByHandler(const ClickHandlerPtr &p) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ServiceBox::clickHandlerPressedChanged(
|
||||
const ClickHandlerPtr &handler,
|
||||
bool pressed) {
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (handler == _button.link) {
|
||||
_button.toggleRipple(pressed);
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceBox::stickerClearLoopPlayed() {
|
||||
_content->stickerClearLoopPlayed();
|
||||
}
|
||||
|
||||
std::unique_ptr<StickerPlayer> ServiceBox::stickerTakePlayer(
|
||||
not_null<DocumentData*> data,
|
||||
const Lottie::ColorReplacements *replacements) {
|
||||
return _content->stickerTakePlayer(data, replacements);
|
||||
}
|
||||
|
||||
bool ServiceBox::needsBubble() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ServiceBox::customInfoLayout() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ServiceBox::hasHeavyPart() const {
|
||||
return _content->hasHeavyPart();
|
||||
}
|
||||
|
||||
void ServiceBox::unloadHeavyPart() {
|
||||
_content->unloadHeavyPart();
|
||||
}
|
||||
|
||||
QRect ServiceBox::buttonRect() const {
|
||||
const auto &padding = st::msgServiceGiftBoxButtonMargins;
|
||||
const auto position = QPoint(
|
||||
(width() - _button.size.width()) / 2,
|
||||
height() - padding.bottom() - _button.size.height());
|
||||
return QRect(position, _button.size);
|
||||
}
|
||||
|
||||
QRect ServiceBox::contentRect() const {
|
||||
const auto size = _content->size();
|
||||
const auto top = _content->top();
|
||||
return QRect(QPoint((width() - size.width()) / 2, top), size);
|
||||
}
|
||||
|
||||
void ServiceBox::Button::toggleRipple(bool pressed) {
|
||||
if (pressed) {
|
||||
const auto linkWidth = size.width();
|
||||
const auto linkHeight = size.height();
|
||||
if (!ripple) {
|
||||
const auto drawMask = [&](QPainter &p) { drawBg(p); };
|
||||
auto mask = Ui::RippleAnimation::MaskByDrawer(
|
||||
QSize(linkWidth, linkHeight),
|
||||
false,
|
||||
drawMask);
|
||||
ripple = std::make_unique<Ui::RippleAnimation>(
|
||||
st::defaultRippleAnimation,
|
||||
std::move(mask),
|
||||
repaint);
|
||||
}
|
||||
ripple->add(lastPoint);
|
||||
} else if (ripple) {
|
||||
ripple->lastStop();
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceBox::Button::drawBg(QPainter &p) const {
|
||||
const auto radius = size.height() / 2.;
|
||||
p.drawRoundedRect(0, 0, size.width(), size.height(), radius, radius);
|
||||
}
|
||||
|
||||
} // namespace HistoryView
|
|
@ -8,12 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#pragma once
|
||||
|
||||
#include "history/view/media/history_view_media.h"
|
||||
#include "history/view/media/history_view_media_unwrapped.h"
|
||||
#include "history/view/media/history_view_sticker.h"
|
||||
|
||||
namespace Data {
|
||||
class MediaGiftBox;
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
class RippleAnimation;
|
||||
|
@ -21,10 +15,35 @@ class RippleAnimation;
|
|||
|
||||
namespace HistoryView {
|
||||
|
||||
class MediaGift final : public Media {
|
||||
class ServiceBoxContent {
|
||||
public:
|
||||
MediaGift(not_null<Element*> parent, not_null<Data::MediaGiftBox*> gift);
|
||||
~MediaGift();
|
||||
virtual ~ServiceBoxContent() = default;
|
||||
|
||||
[[nodiscard]] virtual int top() = 0;
|
||||
[[nodiscard]] virtual QSize size() = 0;
|
||||
[[nodiscard]] virtual QString title() = 0;
|
||||
[[nodiscard]] virtual QString subtitle() = 0;
|
||||
virtual void draw(
|
||||
Painter &p,
|
||||
const PaintContext &context,
|
||||
const QRect &geometry) = 0;
|
||||
[[nodiscard]] virtual ClickHandlerPtr createViewLink() = 0;
|
||||
|
||||
virtual void stickerClearLoopPlayed() = 0;
|
||||
[[nodiscard]] virtual std::unique_ptr<StickerPlayer> stickerTakePlayer(
|
||||
not_null<DocumentData*> data,
|
||||
const Lottie::ColorReplacements *replacements) = 0;
|
||||
|
||||
[[nodiscard]] virtual bool hasHeavyPart() = 0;
|
||||
virtual void unloadHeavyPart() = 0;
|
||||
};
|
||||
|
||||
class ServiceBox final : public Media {
|
||||
public:
|
||||
ServiceBox(
|
||||
not_null<Element*> parent,
|
||||
std::unique_ptr<ServiceBoxContent> content);
|
||||
~ServiceBox();
|
||||
|
||||
QSize countOptimalSize() override;
|
||||
QSize countCurrentSize(int newWidth) override;
|
||||
|
@ -53,14 +72,11 @@ public:
|
|||
void unloadHeavyPart() override;
|
||||
|
||||
private:
|
||||
void ensureStickerCreated() const;
|
||||
[[nodiscard]] QRect buttonRect() const;
|
||||
[[nodiscard]] QRect stickerRect() const;
|
||||
[[nodiscard]] QRect contentRect() const;
|
||||
|
||||
const not_null<Element*> _parent;
|
||||
const not_null<Data::MediaGiftBox*> _gift;
|
||||
const QSize &_size;
|
||||
const QSize _innerSize;
|
||||
const std::unique_ptr<ServiceBoxContent> _content;
|
||||
|
||||
struct Button {
|
||||
void drawBg(QPainter &p) const;
|
||||
|
@ -77,10 +93,11 @@ private:
|
|||
mutable QPoint lastPoint;
|
||||
} _button;
|
||||
|
||||
const int _maxWidth = 0;
|
||||
Ui::Text::String _title;
|
||||
Ui::Text::String _subtitle;
|
||||
|
||||
mutable std::optional<Sticker> _sticker;
|
||||
const QSize _size;
|
||||
const QSize _innerSize;
|
||||
|
||||
};
|
||||
|
|
@ -1,282 +0,0 @@
|
|||
/*
|
||||
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 "history/view/media/history_view_service_media_gift.h"
|
||||
|
||||
#include "chat_helpers/stickers_gift_box_pack.h"
|
||||
#include "core/click_handler_types.h" // ClickHandlerContext
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "lottie/lottie_common.h"
|
||||
#include "lottie/lottie_single_player.h"
|
||||
#include "main/main_session.h"
|
||||
#include "settings/settings_premium.h" // Settings::ShowGiftPremium
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/painter.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_premium.h"
|
||||
#include "styles/style_settings.h"
|
||||
|
||||
namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] QString FormatGiftMonths(int months) {
|
||||
return (months < 12)
|
||||
? tr::lng_premium_gift_duration_months(tr::now, lt_count, months)
|
||||
: tr::lng_premium_gift_duration_years(
|
||||
tr::now,
|
||||
lt_count,
|
||||
std::round(months / 12.));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MediaGift::MediaGift(
|
||||
not_null<Element*> parent,
|
||||
not_null<Data::MediaGiftBox*> gift)
|
||||
: Media(parent)
|
||||
, _parent(parent)
|
||||
, _gift(gift)
|
||||
, _size(st::msgServiceGiftBoxSize)
|
||||
, _innerSize(_size - QSize(0, st::msgServiceGiftBoxTopSkip))
|
||||
, _button([&] {
|
||||
auto result = Button();
|
||||
result.repaint = [=] { repaint(); };
|
||||
result.text.setText(
|
||||
st::semiboldTextStyle,
|
||||
tr::lng_sticker_premium_view(tr::now));
|
||||
|
||||
const auto height = st::msgServiceGiftBoxButtonHeight;
|
||||
const auto &margins = st::msgServiceGiftBoxButtonMargins;
|
||||
result.size = QSize(
|
||||
result.text.maxWidth()
|
||||
+ height
|
||||
+ margins.left()
|
||||
+ margins.right(),
|
||||
height);
|
||||
|
||||
const auto from = _gift->from();
|
||||
const auto to = _parent->history()->peer;
|
||||
const auto months = _gift->months();
|
||||
result.link = std::make_shared<LambdaClickHandler>([=](
|
||||
ClickContext context) {
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
if (const auto controller = my.sessionWindow.get()) {
|
||||
const auto me = (from->id == controller->session().userPeerId());
|
||||
Settings::ShowGiftPremium(controller, me ? to : from, months, me);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}())
|
||||
, _title(
|
||||
st::settingsSubsectionTitle.style,
|
||||
tr::lng_premium_summary_title(tr::now))
|
||||
, _subtitle(
|
||||
st::premiumPreviewAbout.style,
|
||||
FormatGiftMonths(gift->months())) {
|
||||
}
|
||||
|
||||
MediaGift::~MediaGift() = default;
|
||||
|
||||
QSize MediaGift::countOptimalSize() {
|
||||
return _size;
|
||||
}
|
||||
|
||||
QSize MediaGift::countCurrentSize(int newWidth) {
|
||||
return _size;
|
||||
}
|
||||
|
||||
void MediaGift::draw(Painter &p, const PaintContext &context) const {
|
||||
p.translate(0, st::msgServiceGiftBoxTopSkip);
|
||||
|
||||
PainterHighQualityEnabler hq(p);
|
||||
const auto radius = st::msgServiceGiftBoxRadius;
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(context.st->msgServiceBg());
|
||||
p.drawRoundedRect(QRect(QPoint(), _innerSize), radius, radius);
|
||||
|
||||
{
|
||||
p.setPen(context.st->msgServiceFg());
|
||||
const auto &padding = st::msgServiceGiftBoxTitlePadding;
|
||||
const auto titleTop = padding.top();
|
||||
_title.draw(p, 0, titleTop, _innerSize.width(), style::al_top);
|
||||
const auto subtitleTop = titleTop
|
||||
+ _title.minHeight()
|
||||
+ padding.bottom();
|
||||
_subtitle.draw(p, 0, subtitleTop, _innerSize.width(), style::al_top);
|
||||
}
|
||||
|
||||
{
|
||||
const auto position = buttonRect().topLeft();
|
||||
p.translate(position);
|
||||
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(context.st->msgServiceBg()); // ?
|
||||
_button.drawBg(p);
|
||||
p.setPen(context.st->msgServiceFg());
|
||||
if (_button.ripple) {
|
||||
const auto opacity = p.opacity();
|
||||
p.setOpacity(st::historyPollRippleOpacity);
|
||||
_button.ripple->paint(
|
||||
p,
|
||||
0,
|
||||
0,
|
||||
width(),
|
||||
&context.messageStyle()->msgWaveformInactive->c);
|
||||
p.setOpacity(opacity);
|
||||
}
|
||||
_button.text.draw(
|
||||
p,
|
||||
0,
|
||||
(_button.size.height() - _button.text.minHeight()) / 2,
|
||||
_button.size.width(),
|
||||
style::al_top);
|
||||
|
||||
p.translate(-position);
|
||||
}
|
||||
|
||||
if (_sticker) {
|
||||
_sticker->draw(p, context, stickerRect());
|
||||
} else {
|
||||
ensureStickerCreated();
|
||||
}
|
||||
|
||||
p.translate(0, -st::msgServiceGiftBoxTopSkip);
|
||||
}
|
||||
|
||||
TextState MediaGift::textState(QPoint point, StateRequest request) const {
|
||||
auto result = TextState(_parent);
|
||||
{
|
||||
const auto rect = buttonRect();
|
||||
if (rect.contains(point)) {
|
||||
result.link = _button.link;
|
||||
_button.lastPoint = point - rect.topLeft();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MediaGift::toggleSelectionByHandlerClick(
|
||||
const ClickHandlerPtr &p) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MediaGift::dragItemByHandler(const ClickHandlerPtr &p) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void MediaGift::clickHandlerPressedChanged(
|
||||
const ClickHandlerPtr &handler,
|
||||
bool pressed) {
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (handler == _button.link) {
|
||||
_button.toggleRipple(pressed);
|
||||
}
|
||||
}
|
||||
|
||||
void MediaGift::stickerClearLoopPlayed() {
|
||||
if (_sticker) {
|
||||
_sticker->stickerClearLoopPlayed();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<StickerPlayer> MediaGift::stickerTakePlayer(
|
||||
not_null<DocumentData*> data,
|
||||
const Lottie::ColorReplacements *replacements) {
|
||||
return _sticker
|
||||
? _sticker->stickerTakePlayer(data, replacements)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
bool MediaGift::needsBubble() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MediaGift::customInfoLayout() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MediaGift::hasHeavyPart() const {
|
||||
return (_sticker ? _sticker->hasHeavyPart() : false);
|
||||
}
|
||||
|
||||
void MediaGift::unloadHeavyPart() {
|
||||
if (_sticker) {
|
||||
_sticker->unloadHeavyPart();
|
||||
}
|
||||
}
|
||||
|
||||
void MediaGift::ensureStickerCreated() const {
|
||||
if (_sticker) {
|
||||
return;
|
||||
}
|
||||
const auto &session = _parent->history()->session();
|
||||
auto &packs = session.giftBoxStickersPacks();
|
||||
if (const auto document = packs.lookup(_gift->months())) {
|
||||
if (const auto sticker = document->sticker()) {
|
||||
const auto skipPremiumEffect = false;
|
||||
_sticker.emplace(_parent, document, skipPremiumEffect, _parent);
|
||||
_sticker->setDiceIndex(sticker->alt, 1);
|
||||
_sticker->setGiftBoxSticker(true);
|
||||
_sticker->initSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QRect MediaGift::buttonRect() const {
|
||||
const auto &padding = st::msgServiceGiftBoxButtonPadding;
|
||||
const auto position = QPoint(
|
||||
(width() - _button.size.width()) / 2,
|
||||
height() - padding.bottom() - _button.size.height());
|
||||
return QRect(position, _button.size);
|
||||
}
|
||||
|
||||
QRect MediaGift::stickerRect() const {
|
||||
const auto &size = st::msgServiceGiftBoxStickerSize;
|
||||
const auto top = st::msgServiceGiftBoxStickerTop;
|
||||
return QRect(QPoint((width() - size.width()) / 2, top), size);
|
||||
}
|
||||
|
||||
void MediaGift::Button::toggleRipple(bool pressed) {
|
||||
if (pressed) {
|
||||
const auto linkWidth = size.width();
|
||||
const auto linkHeight = size.height();
|
||||
if (!ripple) {
|
||||
const auto drawMask = [&](QPainter &p) { drawBg(p); };
|
||||
auto mask = Ui::RippleAnimation::MaskByDrawer(
|
||||
QSize(linkWidth, linkHeight),
|
||||
false,
|
||||
drawMask);
|
||||
ripple = std::make_unique<Ui::RippleAnimation>(
|
||||
st::defaultRippleAnimation,
|
||||
std::move(mask),
|
||||
repaint);
|
||||
}
|
||||
ripple->add(lastPoint);
|
||||
} else if (ripple) {
|
||||
ripple->lastStop();
|
||||
}
|
||||
}
|
||||
|
||||
void MediaGift::Button::drawBg(QPainter &p) const {
|
||||
const auto radius = size.height() / 2.;
|
||||
p.drawRoundedRect(0, 0, size.width(), size.height(), radius, radius);
|
||||
}
|
||||
|
||||
} // namespace HistoryView
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
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 "history/view/media/history_view_userpic_suggestion.h"
|
||||
|
||||
#include "core/click_handler_types.h" // ClickHandlerContext
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_photo_media.h"
|
||||
#include "data/data_file_click_handler.h"
|
||||
#include "data/data_session.h"
|
||||
#include "editor/photo_editor_layer_widget.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/view/media/history_view_sticker_player_abstract.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "ui/painter.h"
|
||||
#include "mainwidget.h"
|
||||
#include "apiwrap.h"
|
||||
#include "api/api_peer_photo.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
UserpicSuggestion::UserpicSuggestion(
|
||||
not_null<Element*> parent,
|
||||
not_null<PeerData*> chat,
|
||||
not_null<PhotoData*> photo,
|
||||
int width)
|
||||
: _photo(parent, chat, photo, width) {
|
||||
_photo.initDimensions();
|
||||
_photo.resizeGetHeight(_photo.maxWidth());
|
||||
}
|
||||
|
||||
UserpicSuggestion::~UserpicSuggestion() = default;
|
||||
|
||||
int UserpicSuggestion::top() {
|
||||
return st::msgServiceGiftBoxButtonMargins.top();
|
||||
}
|
||||
|
||||
QSize UserpicSuggestion::size() {
|
||||
return { _photo.maxWidth(), _photo.minHeight() };
|
||||
}
|
||||
|
||||
QString UserpicSuggestion::title() {
|
||||
return tr::lng_action_suggested_photo_title(tr::now);
|
||||
}
|
||||
|
||||
QString UserpicSuggestion::subtitle() {
|
||||
return _photo.parent()->data()->notificationText().text;
|
||||
}
|
||||
|
||||
ClickHandlerPtr UserpicSuggestion::createViewLink() {
|
||||
const auto out = _photo.parent()->data()->out();
|
||||
const auto photo = _photo.getPhoto();
|
||||
const auto itemId = _photo.parent()->data()->fullId();
|
||||
const auto show = crl::guard(&_photo, [=](FullMsgId id) {
|
||||
_photo.showPhoto(id);
|
||||
});
|
||||
return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
if (const auto controller = my.sessionWindow.get()) {
|
||||
const auto media = photo->activeMediaView();
|
||||
if (media->loaded()) {
|
||||
if (out) {
|
||||
PhotoOpenClickHandler(photo, show, itemId).onClick(context);
|
||||
} else {
|
||||
const auto original = std::make_shared<QImage>(
|
||||
media->image(Data::PhotoSize::Large)->original());
|
||||
const auto callback = [=](QImage &&image) {
|
||||
const auto session = &photo->session();
|
||||
const auto user = session->user();
|
||||
auto &peerPhotos = session->api().peerPhoto();
|
||||
if (original->size() == image.size()
|
||||
&& original->constBits() == image.constBits()) {
|
||||
peerPhotos.updateSelf(photo);
|
||||
} else {
|
||||
peerPhotos.upload(user, std::move(image));
|
||||
}
|
||||
};
|
||||
Editor::PrepareProfilePhoto(
|
||||
controller->content(),
|
||||
&controller->window(),
|
||||
ImageRoundRadius::Ellipse,
|
||||
callback,
|
||||
base::duplicate(*original));
|
||||
}
|
||||
} else if (!photo->loading()) {
|
||||
PhotoSaveClickHandler(photo, itemId).onClick(context);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void UserpicSuggestion::draw(
|
||||
Painter &p,
|
||||
const PaintContext &context,
|
||||
const QRect &geometry) {
|
||||
p.translate(geometry.topLeft());
|
||||
_photo.draw(p, context);
|
||||
p.translate(-geometry.topLeft());
|
||||
}
|
||||
|
||||
void UserpicSuggestion::stickerClearLoopPlayed() {
|
||||
}
|
||||
|
||||
std::unique_ptr<StickerPlayer> UserpicSuggestion::stickerTakePlayer(
|
||||
not_null<DocumentData*> data,
|
||||
const Lottie::ColorReplacements *replacements) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool UserpicSuggestion::hasHeavyPart() {
|
||||
return _photo.hasHeavyPart();
|
||||
}
|
||||
|
||||
void UserpicSuggestion::unloadHeavyPart() {
|
||||
_photo.unloadHeavyPart();
|
||||
}
|
||||
|
||||
} // namespace HistoryView
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
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
|
||||
|
||||
#include "history/view/media/history_view_media.h"
|
||||
#include "history/view/media/history_view_media_unwrapped.h"
|
||||
#include "history/view/media/history_view_photo.h"
|
||||
#include "history/view/media/history_view_service_box.h"
|
||||
|
||||
namespace Data {
|
||||
class MediaGiftBox;
|
||||
} // namespace Data
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
class UserpicSuggestion final : public ServiceBoxContent {
|
||||
public:
|
||||
UserpicSuggestion(
|
||||
not_null<Element*> parent,
|
||||
not_null<PeerData*> chat,
|
||||
not_null<PhotoData*> photo,
|
||||
int width);
|
||||
~UserpicSuggestion();
|
||||
|
||||
int top() override;
|
||||
QSize size() override;
|
||||
QString title() override;
|
||||
QString subtitle() override;
|
||||
void draw(
|
||||
Painter &p,
|
||||
const PaintContext &context,
|
||||
const QRect &geometry) override;
|
||||
ClickHandlerPtr createViewLink() override;
|
||||
|
||||
void stickerClearLoopPlayed() override;
|
||||
std::unique_ptr<StickerPlayer> stickerTakePlayer(
|
||||
not_null<DocumentData*> data,
|
||||
const Lottie::ColorReplacements *replacements) override;
|
||||
|
||||
bool hasHeavyPart() override;
|
||||
void unloadHeavyPart() override;
|
||||
|
||||
private:
|
||||
Photo _photo;
|
||||
|
||||
};
|
||||
|
||||
} // namespace HistoryView
|
|
@ -1242,9 +1242,9 @@ msgServiceGiftBoxSize: size(206px, 231px); // Plus msgServiceGiftBoxTopSkip.
|
|||
msgServiceGiftBoxRadius: 12px;
|
||||
msgServiceGiftBoxTopSkip: 4px;
|
||||
msgServiceGiftBoxButtonHeight: 32px;
|
||||
msgServiceGiftBoxButtonMargins: margins(2px, 0px, 2px, 0px);
|
||||
msgServiceGiftBoxButtonPadding: margins(0px, 17px, 0px, 17px);
|
||||
msgServiceGiftBoxTitlePadding: margins(0px, 126px, 0px, 2px);
|
||||
msgServiceGiftBoxButtonPadding: margins(2px, 0px, 2px, 0px);
|
||||
msgServiceGiftBoxButtonMargins: margins(0px, 13px, 0px, 17px);
|
||||
msgServiceGiftBoxTitlePadding: margins(0px, 5px, 0px, 2px);
|
||||
msgServiceGiftBoxStickerTop: -19px;
|
||||
msgServiceGiftBoxStickerSize: size(140px, 140px);
|
||||
|
||||
|
|
|
@ -317,7 +317,7 @@ void UserpicButton::choosePhotoLocally() {
|
|||
_menu->addAction(
|
||||
tr::lng_profile_suggest_photo(tr::now, lt_user, name),
|
||||
[=] { chooseFile(ChosenType::Suggest); });
|
||||
if (user->flags() & UserDataFlag::PersonalPhoto) {
|
||||
if (user->hasPersonalPhoto()) {
|
||||
_menu->addAction(
|
||||
tr::lng_profile_photo_reset(tr::now),
|
||||
[=] { user->session().api().peerPhoto().clearPersonal(
|
||||
|
|
Loading…
Add table
Reference in a new issue