mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Show custom chat wallpapers in chats.
This commit is contained in:
parent
5cbf9a2dc4
commit
bf27185feb
20 changed files with 369 additions and 121 deletions
|
@ -764,7 +764,10 @@ bool ResolveTestChatTheme(
|
|||
}
|
||||
const auto recache = [&](Data::CloudThemeType type) {
|
||||
[[maybe_unused]] auto value = theme->settings.contains(type)
|
||||
? controller->cachedChatThemeValue(*theme, type)
|
||||
? controller->cachedChatThemeValue(
|
||||
*theme,
|
||||
Data::WallPaper(0),
|
||||
type)
|
||||
: nullptr;
|
||||
};
|
||||
recache(Data::CloudThemeType::Dark);
|
||||
|
|
|
@ -64,45 +64,46 @@ struct PeerUpdate {
|
|||
Migration = (1ULL << 5),
|
||||
UnavailableReason = (1ULL << 6),
|
||||
ChatThemeEmoji = (1ULL << 7),
|
||||
IsBlocked = (1ULL << 8),
|
||||
MessagesTTL = (1ULL << 9),
|
||||
FullInfo = (1ULL << 10),
|
||||
Usernames = (1ULL << 11),
|
||||
TranslationDisabled = (1ULL << 12),
|
||||
ChatWallPaper = (1ULL << 8),
|
||||
IsBlocked = (1ULL << 9),
|
||||
MessagesTTL = (1ULL << 10),
|
||||
FullInfo = (1ULL << 11),
|
||||
Usernames = (1ULL << 12),
|
||||
TranslationDisabled = (1ULL << 13),
|
||||
|
||||
// For users
|
||||
CanShareContact = (1ULL << 13),
|
||||
IsContact = (1ULL << 14),
|
||||
PhoneNumber = (1ULL << 15),
|
||||
OnlineStatus = (1ULL << 16),
|
||||
BotCommands = (1ULL << 17),
|
||||
BotCanBeInvited = (1ULL << 18),
|
||||
BotStartToken = (1ULL << 19),
|
||||
CommonChats = (1ULL << 20),
|
||||
HasCalls = (1ULL << 21),
|
||||
SupportInfo = (1ULL << 22),
|
||||
IsBot = (1ULL << 23),
|
||||
EmojiStatus = (1ULL << 24),
|
||||
CanShareContact = (1ULL << 14),
|
||||
IsContact = (1ULL << 15),
|
||||
PhoneNumber = (1ULL << 16),
|
||||
OnlineStatus = (1ULL << 17),
|
||||
BotCommands = (1ULL << 18),
|
||||
BotCanBeInvited = (1ULL << 19),
|
||||
BotStartToken = (1ULL << 20),
|
||||
CommonChats = (1ULL << 21),
|
||||
HasCalls = (1ULL << 22),
|
||||
SupportInfo = (1ULL << 23),
|
||||
IsBot = (1ULL << 24),
|
||||
EmojiStatus = (1ULL << 25),
|
||||
|
||||
// For chats and channels
|
||||
InviteLinks = (1ULL << 25),
|
||||
Members = (1ULL << 26),
|
||||
Admins = (1ULL << 27),
|
||||
BannedUsers = (1ULL << 28),
|
||||
Rights = (1ULL << 29),
|
||||
PendingRequests = (1ULL << 30),
|
||||
Reactions = (1ULL << 31),
|
||||
InviteLinks = (1ULL << 26),
|
||||
Members = (1ULL << 27),
|
||||
Admins = (1ULL << 28),
|
||||
BannedUsers = (1ULL << 29),
|
||||
Rights = (1ULL << 30),
|
||||
PendingRequests = (1ULL << 31),
|
||||
Reactions = (1ULL << 32),
|
||||
|
||||
// For channels
|
||||
ChannelAmIn = (1ULL << 32),
|
||||
StickersSet = (1ULL << 33),
|
||||
ChannelLinkedChat = (1ULL << 34),
|
||||
ChannelLocation = (1ULL << 35),
|
||||
Slowmode = (1ULL << 36),
|
||||
GroupCall = (1ULL << 37),
|
||||
ChannelAmIn = (1ULL << 33),
|
||||
StickersSet = (1ULL << 34),
|
||||
ChannelLinkedChat = (1ULL << 35),
|
||||
ChannelLocation = (1ULL << 36),
|
||||
Slowmode = (1ULL << 37),
|
||||
GroupCall = (1ULL << 38),
|
||||
|
||||
// For iteration
|
||||
LastUsedBit = (1ULL << 37),
|
||||
LastUsedBit = (1ULL << 38),
|
||||
};
|
||||
using Flags = base::flags<Flag>;
|
||||
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
||||
|
|
|
@ -794,7 +794,7 @@ QString DocumentData::loadingFilePath() const {
|
|||
|
||||
bool DocumentData::displayLoading() const {
|
||||
return loading()
|
||||
? (!_loader->loadingLocal() || !_loader->autoLoading())
|
||||
? !_loader->loadingLocal()
|
||||
: (uploading() && !waitingForAlbum());
|
||||
}
|
||||
|
||||
|
|
|
@ -1093,6 +1093,22 @@ const QString &PeerData::themeEmoji() const {
|
|||
return _themeEmoticon;
|
||||
}
|
||||
|
||||
void PeerData::setWallPaper(std::optional<Data::WallPaper> paper) {
|
||||
if (!paper && !_wallPaper) {
|
||||
return;
|
||||
} else if (paper && _wallPaper && _wallPaper->equals(*paper)) {
|
||||
return;
|
||||
}
|
||||
_wallPaper = paper
|
||||
? std::make_unique<Data::WallPaper>(std::move(*paper))
|
||||
: nullptr;
|
||||
session().changes().peerUpdated(this, UpdateFlag::ChatWallPaper);
|
||||
}
|
||||
|
||||
const Data::WallPaper *PeerData::wallPaper() const {
|
||||
return _wallPaper.get();
|
||||
}
|
||||
|
||||
void PeerData::setIsBlocked(bool is) {
|
||||
const auto status = is
|
||||
? BlockStatus::Blocked
|
||||
|
|
|
@ -37,6 +37,7 @@ class ForumTopic;
|
|||
class Session;
|
||||
class GroupCall;
|
||||
struct ReactionId;
|
||||
class WallPaper;
|
||||
|
||||
[[nodiscard]] int PeerColorIndex(PeerId peerId);
|
||||
|
||||
|
@ -403,6 +404,9 @@ public:
|
|||
void setThemeEmoji(const QString &emoticon);
|
||||
[[nodiscard]] const QString &themeEmoji() const;
|
||||
|
||||
void setWallPaper(std::optional<Data::WallPaper> paper);
|
||||
[[nodiscard]] const Data::WallPaper *wallPaper() const;
|
||||
|
||||
const PeerId id;
|
||||
MTPinputPeer input = MTP_inputPeerEmpty();
|
||||
|
||||
|
@ -457,6 +461,7 @@ private:
|
|||
|
||||
QString _about;
|
||||
QString _themeEmoticon;
|
||||
std::unique_ptr<Data::WallPaper> _wallPaper;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_photo.h"
|
||||
#include "data/data_emoji_statuses.h"
|
||||
#include "data/data_user_names.h"
|
||||
#include "data/data_wall_paper.h"
|
||||
#include "data/notify/data_notify_settings.h"
|
||||
#include "api/api_peer_photo.h"
|
||||
#include "apiwrap.h"
|
||||
|
@ -462,6 +463,13 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
|
|||
}
|
||||
}
|
||||
|
||||
if (const auto paper = update.vwallpaper()) {
|
||||
user->setWallPaper(
|
||||
Data::WallPaper::Create(&user->session(), *paper));
|
||||
} else {
|
||||
user->setWallPaper({});
|
||||
}
|
||||
|
||||
user->fullUpdated();
|
||||
}
|
||||
|
||||
|
|
|
@ -198,6 +198,16 @@ WallPaperId WallPaper::id() const {
|
|||
return _id;
|
||||
}
|
||||
|
||||
bool WallPaper::equals(const WallPaper &paper) const {
|
||||
return (_flags == paper._flags)
|
||||
&& (_slug == paper._slug)
|
||||
&& (_backgroundColors == paper._backgroundColors)
|
||||
&& (_rotation == paper._rotation)
|
||||
&& (_intensity == paper._intensity)
|
||||
&& (_blurred == paper._blurred)
|
||||
&& (_document == paper._document);
|
||||
}
|
||||
|
||||
const std::vector<QColor> WallPaper::backgroundColors() const {
|
||||
return _backgroundColors;
|
||||
}
|
||||
|
@ -251,34 +261,54 @@ bool WallPaper::hasShareUrl() const {
|
|||
return !_slug.isEmpty();
|
||||
}
|
||||
|
||||
QString WallPaper::shareUrl(not_null<Main::Session*> session) const {
|
||||
if (!hasShareUrl()) {
|
||||
return QString();
|
||||
}
|
||||
const auto base = session->createInternalLinkFull("bg/" + _slug);
|
||||
auto params = QStringList();
|
||||
QStringList WallPaper::collectShareParams() const {
|
||||
auto result = QStringList();
|
||||
if (isPattern()) {
|
||||
if (!backgroundColors().empty()) {
|
||||
params.push_back(
|
||||
result.push_back(
|
||||
"bg_color=" + StringFromColors(backgroundColors()));
|
||||
}
|
||||
if (_intensity) {
|
||||
params.push_back("intensity=" + QString::number(_intensity));
|
||||
result.push_back("intensity=" + QString::number(_intensity));
|
||||
}
|
||||
}
|
||||
if (_rotation && backgroundColors().size() == 2) {
|
||||
params.push_back("rotation=" + QString::number(_rotation));
|
||||
result.push_back("rotation=" + QString::number(_rotation));
|
||||
}
|
||||
auto mode = QStringList();
|
||||
if (_blurred) {
|
||||
mode.push_back("blur");
|
||||
}
|
||||
if (!mode.isEmpty()) {
|
||||
params.push_back("mode=" + mode.join('+'));
|
||||
result.push_back("mode=" + mode.join('+'));
|
||||
}
|
||||
return params.isEmpty()
|
||||
? base
|
||||
: base + '?' + params.join('&');
|
||||
return result;
|
||||
}
|
||||
|
||||
bool WallPaper::isNull() const {
|
||||
return !_id && _slug.isEmpty() && _backgroundColors.empty();
|
||||
}
|
||||
|
||||
QString WallPaper::key() const {
|
||||
if (isNull()) {
|
||||
return QString();
|
||||
}
|
||||
const auto base = _slug.isEmpty()
|
||||
? (_id
|
||||
? QString::number(_id)
|
||||
: StringFromColors(backgroundColors()))
|
||||
: ("bg/" + _slug);
|
||||
const auto params = collectShareParams();
|
||||
return params.isEmpty() ? base : (base + '?' + params.join('&'));
|
||||
}
|
||||
|
||||
QString WallPaper::shareUrl(not_null<Main::Session*> session) const {
|
||||
if (!hasShareUrl()) {
|
||||
return QString();
|
||||
}
|
||||
const auto base = session->createInternalLinkFull("bg/" + _slug);
|
||||
const auto params = collectShareParams();
|
||||
return params.isEmpty() ? base : (base + '?' + params.join('&'));
|
||||
}
|
||||
|
||||
void WallPaper::loadDocumentThumbnail() const {
|
||||
|
|
|
@ -42,7 +42,11 @@ public:
|
|||
|
||||
void setLocalImageAsThumbnail(std::shared_ptr<Image> image);
|
||||
|
||||
[[nodiscard]] bool equals(const WallPaper &paper) const;
|
||||
|
||||
[[nodiscard]] WallPaperId id() const;
|
||||
[[nodiscard]] bool isNull() const;
|
||||
[[nodiscard]] QString key() const;
|
||||
[[nodiscard]] const std::vector<QColor> backgroundColors() const;
|
||||
[[nodiscard]] DocumentData *document() const;
|
||||
[[nodiscard]] Image *localThumbnail() const;
|
||||
|
@ -103,6 +107,8 @@ public:
|
|||
private:
|
||||
static constexpr auto kDefaultIntensity = 50;
|
||||
|
||||
[[nodiscard]] QStringList collectShareParams() const;
|
||||
|
||||
WallPaperId _id = WallPaperId();
|
||||
uint64 _accessHash = 0;
|
||||
UserId _ownerId = 0;
|
||||
|
|
|
@ -1264,6 +1264,9 @@ void History::newItemAdded(not_null<HistoryItem*> item) {
|
|||
if (!item->unread(this)) {
|
||||
outboxRead(item);
|
||||
}
|
||||
if (item->changesWallPaper()) {
|
||||
peer->updateFullForced();
|
||||
}
|
||||
} else {
|
||||
if (item->unread(this)) {
|
||||
if (unreadCountKnown()) {
|
||||
|
|
|
@ -2213,6 +2213,13 @@ bool HistoryItem::hasDirectLink() const {
|
|||
return isRegular() && _history->peer->isChannel();
|
||||
}
|
||||
|
||||
bool HistoryItem::changesWallPaper() const {
|
||||
if (const auto media = _media.get()) {
|
||||
return media->paper() != nullptr;
|
||||
}
|
||||
return Has<HistoryServiceSameBackground>();
|
||||
}
|
||||
|
||||
FullMsgId HistoryItem::fullId() const {
|
||||
return FullMsgId(_history->peer->id, id);
|
||||
}
|
||||
|
|
|
@ -433,6 +433,7 @@ public:
|
|||
[[nodiscard]] crl::time lastReactionsRefreshTime() const;
|
||||
|
||||
[[nodiscard]] bool hasDirectLink() const;
|
||||
[[nodiscard]] bool changesWallPaper() const;
|
||||
|
||||
[[nodiscard]] FullMsgId fullId() const;
|
||||
[[nodiscard]] GlobalMsgId globalId() const;
|
||||
|
|
|
@ -27,6 +27,12 @@ ServiceBox::ServiceBox(
|
|||
, _content(std::move(content))
|
||||
, _button([&] {
|
||||
auto result = Button();
|
||||
result.link = _content->createViewLink();
|
||||
|
||||
const auto text = _content->button();
|
||||
if (text.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
result.repaint = [=] { repaint(); };
|
||||
result.text.setText(st::semiboldTextStyle, _content->button());
|
||||
|
||||
|
@ -39,8 +45,6 @@ ServiceBox::ServiceBox(
|
|||
+ padding.right(),
|
||||
height);
|
||||
|
||||
result.link = _content->createViewLink();
|
||||
|
||||
return result;
|
||||
}())
|
||||
, _maxWidth(st::msgServiceGiftBoxSize.width()
|
||||
|
@ -67,8 +71,10 @@ ServiceBox::ServiceBox(
|
|||
: (_title.countHeight(_maxWidth)
|
||||
+ st::msgServiceGiftBoxTitlePadding.bottom()))
|
||||
+ _subtitle.countHeight(_maxWidth)
|
||||
+ st::msgServiceGiftBoxButtonMargins.top()
|
||||
+ _button.size.height()
|
||||
+ (_button.empty()
|
||||
? 0
|
||||
: (st::msgServiceGiftBoxButtonMargins.top()
|
||||
+ _button.size.height()))
|
||||
+ st::msgServiceGiftBoxButtonMargins.bottom()))
|
||||
, _innerSize(_size - QSize(0, st::msgServiceGiftBoxTopSkip)) {
|
||||
}
|
||||
|
@ -106,7 +112,7 @@ void ServiceBox::draw(Painter &p, const PaintContext &context) const {
|
|||
top += _subtitle.countHeight(_maxWidth) + padding.bottom();
|
||||
}
|
||||
|
||||
{
|
||||
if (!_button.empty()) {
|
||||
const auto position = buttonRect().topLeft();
|
||||
p.translate(position);
|
||||
|
||||
|
@ -142,7 +148,11 @@ void ServiceBox::draw(Painter &p, const PaintContext &context) const {
|
|||
|
||||
TextState ServiceBox::textState(QPoint point, StateRequest request) const {
|
||||
auto result = TextState(_parent);
|
||||
{
|
||||
if (_button.empty()) {
|
||||
if (QRect(QPoint(), _innerSize).contains(point)) {
|
||||
result.link = _button.link;
|
||||
}
|
||||
} else {
|
||||
const auto rect = buttonRect();
|
||||
if (rect.contains(point)) {
|
||||
result.link = _button.link;
|
||||
|
@ -214,7 +224,9 @@ QRect ServiceBox::contentRect() const {
|
|||
}
|
||||
|
||||
void ServiceBox::Button::toggleRipple(bool pressed) {
|
||||
if (pressed) {
|
||||
if (empty()) {
|
||||
return;
|
||||
} else if (pressed) {
|
||||
const auto linkWidth = size.width();
|
||||
const auto linkHeight = size.height();
|
||||
if (!ripple) {
|
||||
|
@ -234,6 +246,10 @@ void ServiceBox::Button::toggleRipple(bool pressed) {
|
|||
}
|
||||
}
|
||||
|
||||
bool ServiceBox::Button::empty() const {
|
||||
return text.isEmpty();
|
||||
}
|
||||
|
||||
void ServiceBox::Button::drawBg(QPainter &p) const {
|
||||
const auto radius = size.height() / 2.;
|
||||
p.drawRoundedRect(0, 0, size.width(), size.height(), radius, radius);
|
||||
|
|
|
@ -88,6 +88,7 @@ private:
|
|||
struct Button {
|
||||
void drawBg(QPainter &p) const;
|
||||
void toggleRipple(bool pressed);
|
||||
[[nodiscard]] bool empty() const;
|
||||
|
||||
Fn<void()> repaint;
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_file_origin.h"
|
||||
#include "data/data_wall_paper.h"
|
||||
#include "base/qthelp_url.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "core/local_url_handlers.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/text/format_values.h"
|
||||
|
@ -27,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/cached_round_corners.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
namespace HistoryView {
|
||||
|
@ -433,15 +435,21 @@ QString ThemeDocumentBox::subtitle() {
|
|||
}
|
||||
|
||||
QString ThemeDocumentBox::button() {
|
||||
return tr::lng_sticker_premium_view(tr::now);
|
||||
return _parent->data()->out()
|
||||
? QString()
|
||||
: tr::lng_action_set_wallpaper_button(tr::now);
|
||||
}
|
||||
|
||||
ClickHandlerPtr ThemeDocumentBox::createViewLink() {
|
||||
const auto out = _parent->data()->out();
|
||||
const auto to = _parent->history()->peer;
|
||||
return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
|
||||
//const auto my = context.other.value<ClickHandlerContext>();
|
||||
//if (const auto controller = my.sessionWindow.get()) {
|
||||
//}
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
if (const auto controller = my.sessionWindow.get()) {
|
||||
if (out) {
|
||||
controller->toggleChooseChatTheme(to);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -163,7 +163,8 @@ constexpr auto kMinAcceptableContrast = 1.14;// 4.5;
|
|||
} // namespace
|
||||
|
||||
bool operator==(const ChatThemeBackground &a, const ChatThemeBackground &b) {
|
||||
return (a.prepared.cacheKey() == b.prepared.cacheKey())
|
||||
return (a.key == b.key)
|
||||
&& (a.prepared.cacheKey() == b.prepared.cacheKey())
|
||||
&& (a.gradientForFill.cacheKey() == b.gradientForFill.cacheKey())
|
||||
&& (a.tile == b.tile)
|
||||
&& (a.patternOpacity == b.patternOpacity);
|
||||
|
@ -222,9 +223,14 @@ void ChatTheme::adjustPalette(const ChatThemeDescriptor &descriptor) {
|
|||
if (overrideOutBg) {
|
||||
set(p.msgOutBg(), descriptor.bubblesData.colors.front());
|
||||
}
|
||||
const auto &background = descriptor.backgroundData.colors;
|
||||
if (!background.empty()) {
|
||||
const auto average = CountAverageColor(background);
|
||||
const auto &data = descriptor.backgroundData;
|
||||
const auto &background = data.colors;
|
||||
const auto useImage = !data.isPattern
|
||||
&& (!data.path.isEmpty() || !data.bytes.isEmpty());
|
||||
if (useImage || !background.empty()) {
|
||||
const auto average = useImage
|
||||
? Ui::CountAverageColor(_mutableBackground.prepared)
|
||||
: CountAverageColor(background);
|
||||
adjust(p.msgServiceBg(), average);
|
||||
adjust(p.msgServiceBgSelected(), average);
|
||||
adjust(p.historyScrollBg(), average);
|
||||
|
@ -407,6 +413,7 @@ void ChatTheme::setBackground(ChatThemeBackground &&background) {
|
|||
}
|
||||
|
||||
void ChatTheme::updateBackgroundImageFrom(ChatThemeBackground &&background) {
|
||||
_mutableBackground.key = background.key;
|
||||
_mutableBackground.prepared = std::move(background.prepared);
|
||||
_mutableBackground.preparedForTiled = std::move(
|
||||
background.preparedForTiled);
|
||||
|
@ -1028,6 +1035,16 @@ ChatThemeBackground PrepareBackgroundImage(
|
|||
} else if (data.colors.empty()) {
|
||||
prepared.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
}
|
||||
if (!prepared.isNull()
|
||||
&& !data.isPattern
|
||||
&& data.forDarkMode
|
||||
&& data.darkModeDimming > 0) {
|
||||
const auto ratio = int(prepared.devicePixelRatio());
|
||||
auto p = QPainter(&prepared);
|
||||
p.fillRect(
|
||||
QRect(0, 0, prepared.width() / ratio, prepared.height() / ratio),
|
||||
QColor(0, 0, 0, 255 * data.darkModeDimming / 100));
|
||||
}
|
||||
const auto imageMonoColor = (data.colors.size() < 2)
|
||||
? CalculateImageMonoColor(prepared)
|
||||
: std::nullopt;
|
||||
|
@ -1038,6 +1055,7 @@ ChatThemeBackground PrepareBackgroundImage(
|
|||
? Ui::GenerateDitheredGradient(data.colors, data.gradientRotation)
|
||||
: QImage();
|
||||
return ChatThemeBackground{
|
||||
.key = data.key,
|
||||
.prepared = prepared,
|
||||
.preparedForTiled = PrepareImageForTiled(prepared),
|
||||
.gradientForFill = std::move(gradientForFill),
|
||||
|
|
|
@ -23,6 +23,7 @@ struct ChatPaintContext;
|
|||
struct BubblePattern;
|
||||
|
||||
struct ChatThemeBackground {
|
||||
QString key;
|
||||
QImage prepared;
|
||||
QImage preparedForTiled;
|
||||
QImage gradientForFill;
|
||||
|
@ -42,13 +43,16 @@ bool operator==(const ChatThemeBackground &a, const ChatThemeBackground &b);
|
|||
bool operator!=(const ChatThemeBackground &a, const ChatThemeBackground &b);
|
||||
|
||||
struct ChatThemeBackgroundData {
|
||||
QString key;
|
||||
QString path;
|
||||
QByteArray bytes;
|
||||
bool gzipSvg = false;
|
||||
std::vector<QColor> colors;
|
||||
bool isPattern = false;
|
||||
float64 patternOpacity = 0.;
|
||||
int darkModeDimming = 0;
|
||||
bool isBlurred = false;
|
||||
bool forDarkMode = false;
|
||||
bool generateGradient = false;
|
||||
int gradientRotation = 0;
|
||||
};
|
||||
|
@ -113,26 +117,10 @@ struct ChatThemeKey {
|
|||
explicit operator bool() const {
|
||||
return (id != 0);
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator<(ChatThemeKey a, ChatThemeKey b) {
|
||||
return (a.id < b.id) || ((a.id == b.id) && (a.dark < b.dark));
|
||||
}
|
||||
inline bool operator>(ChatThemeKey a, ChatThemeKey b) {
|
||||
return (b < a);
|
||||
}
|
||||
inline bool operator<=(ChatThemeKey a, ChatThemeKey b) {
|
||||
return !(b < a);
|
||||
}
|
||||
inline bool operator>=(ChatThemeKey a, ChatThemeKey b) {
|
||||
return !(a < b);
|
||||
}
|
||||
inline bool operator==(ChatThemeKey a, ChatThemeKey b) {
|
||||
return (a.id == b.id) && (a.dark == b.dark);
|
||||
}
|
||||
inline bool operator!=(ChatThemeKey a, ChatThemeKey b) {
|
||||
return !(a == b);
|
||||
}
|
||||
friend inline auto operator<=>(ChatThemeKey, ChatThemeKey) = default;
|
||||
friend inline bool operator==(ChatThemeKey, ChatThemeKey) = default;
|
||||
};
|
||||
|
||||
struct ChatThemeDescriptor {
|
||||
ChatThemeKey key;
|
||||
|
|
|
@ -383,7 +383,10 @@ void ChooseThemeController::initList() {
|
|||
_chosen = chosen;
|
||||
entry->chosen = true;
|
||||
if (entry->theme || !entry->key) {
|
||||
_controller->overridePeerTheme(_peer, entry->theme);
|
||||
_controller->overridePeerTheme(
|
||||
_peer,
|
||||
entry->theme,
|
||||
entry->emoji);
|
||||
}
|
||||
_inner->update();
|
||||
}
|
||||
|
@ -534,6 +537,7 @@ void ChooseThemeController::fill(
|
|||
});
|
||||
_controller->cachedChatThemeValue(
|
||||
theme,
|
||||
Data::WallPaper(0),
|
||||
type
|
||||
) | rpl::filter([=](const std::shared_ptr<ChatTheme> &data) {
|
||||
return data && (data->key() == key);
|
||||
|
@ -549,7 +553,10 @@ void ChooseThemeController::fill(
|
|||
i->theme = std::move(data);
|
||||
i->preview = GeneratePreview(theme);
|
||||
if (_chosen == i->emoji->text()) {
|
||||
_controller->overridePeerTheme(_peer, i->theme);
|
||||
_controller->overridePeerTheme(
|
||||
_peer,
|
||||
i->theme,
|
||||
i->emoji);
|
||||
}
|
||||
_inner->update();
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_peer.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_document_media.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_cloud_themes.h"
|
||||
|
@ -45,6 +46,44 @@ namespace {
|
|||
});
|
||||
}
|
||||
|
||||
struct ResolvedPaper {
|
||||
Data::WallPaper paper;
|
||||
std::shared_ptr<Data::DocumentMedia> media;
|
||||
};
|
||||
|
||||
[[nodiscard]] rpl::producer<std::optional<ResolvedPaper>> PeerWallPaperValue(
|
||||
not_null<PeerData*> peer) {
|
||||
return peer->session().changes().peerFlagsValue(
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::ChatWallPaper
|
||||
) | rpl::map([=]() -> rpl::producer<std::optional<ResolvedPaper>> {
|
||||
const auto paper = peer->wallPaper();
|
||||
const auto single = [](std::optional<ResolvedPaper> value) {
|
||||
return rpl::single(std::move(value));
|
||||
};
|
||||
if (!paper) {
|
||||
return single({});
|
||||
}
|
||||
const auto document = paper->document();
|
||||
auto value = ResolvedPaper{
|
||||
*paper,
|
||||
document ? document->createMediaView() : nullptr,
|
||||
};
|
||||
if (!value.media || value.media->loaded(true)) {
|
||||
return single(std::move(value));
|
||||
}
|
||||
paper->loadDocument();
|
||||
return single(
|
||||
value
|
||||
) | rpl::then(document->session().downloaderTaskFinished(
|
||||
) | rpl::filter([=] {
|
||||
return value.media->loaded(true);
|
||||
}) | rpl::take(1) | rpl::map_to(
|
||||
std::optional<ResolvedPaper>(value)
|
||||
));
|
||||
}) | rpl::flatten_latest();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto MaybeChatThemeDataValueFromPeer(
|
||||
not_null<PeerData*> peer)
|
||||
-> rpl::producer<std::optional<Data::CloudTheme>> {
|
||||
|
@ -58,6 +97,7 @@ namespace {
|
|||
|
||||
struct ResolvedTheme {
|
||||
std::optional<Data::CloudTheme> theme;
|
||||
std::optional<ResolvedPaper> paper;
|
||||
bool dark = false;
|
||||
};
|
||||
|
||||
|
@ -66,9 +106,13 @@ struct ResolvedTheme {
|
|||
-> rpl::producer<ResolvedTheme> {
|
||||
return rpl::combine(
|
||||
MaybeChatThemeDataValueFromPeer(peer),
|
||||
PeerWallPaperValue(peer),
|
||||
Theme::IsThemeDarkValue() | rpl::distinct_until_changed()
|
||||
) | rpl::map([](std::optional<Data::CloudTheme> theme, bool night) {
|
||||
return ResolvedTheme{ std::move(theme), night };
|
||||
) | rpl::map([](
|
||||
std::optional<Data::CloudTheme> theme,
|
||||
std::optional<ResolvedPaper> paper,
|
||||
bool night) {
|
||||
return ResolvedTheme{ std::move(theme), std::move(paper), night };
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -338,13 +382,23 @@ auto ChatThemeValueFromPeer(
|
|||
peer
|
||||
) | rpl::map([=](ResolvedTheme resolved)
|
||||
-> rpl::producer<std::shared_ptr<Ui::ChatTheme>> {
|
||||
return resolved.theme
|
||||
? controller->cachedChatThemeValue(
|
||||
*resolved.theme,
|
||||
(resolved.dark
|
||||
? Data::CloudThemeType::Dark
|
||||
: Data::CloudThemeType::Light))
|
||||
: rpl::single(controller->defaultChatTheme());
|
||||
if (!resolved.theme && !resolved.paper) {
|
||||
return rpl::single(controller->defaultChatTheme());
|
||||
}
|
||||
const auto theme = resolved.theme.value_or(Data::CloudTheme());
|
||||
const auto paper = resolved.paper
|
||||
? resolved.paper->paper
|
||||
: Data::WallPaper(0);
|
||||
const auto type = resolved.dark
|
||||
? Data::CloudThemeType::Dark
|
||||
: Data::CloudThemeType::Light;
|
||||
if (paper.document()
|
||||
&& resolved.paper->media
|
||||
&& !resolved.paper->media->loaded()
|
||||
&& !controller->chatThemeAlreadyCached(theme, paper, type)) {
|
||||
return rpl::single(controller->defaultChatTheme());
|
||||
}
|
||||
return controller->cachedChatThemeValue(theme, paper, type);
|
||||
}) | rpl::flatten_latest(
|
||||
) | rpl::distinct_until_changed();
|
||||
|
||||
|
@ -354,7 +408,8 @@ auto ChatThemeValueFromPeer(
|
|||
) | rpl::map([=](
|
||||
std::shared_ptr<Ui::ChatTheme> &&cloud,
|
||||
PeerThemeOverride &&overriden) {
|
||||
return (overriden.peer == peer.get())
|
||||
return (overriden.peer == peer.get()
|
||||
&& Ui::Emoji::Find(peer->themeEmoji()) != overriden.emoji)
|
||||
? std::move(overriden.theme)
|
||||
: std::move(cloud);
|
||||
});
|
||||
|
|
|
@ -97,9 +97,15 @@ constexpr auto kMaxChatEntryHistorySize = 50;
|
|||
constexpr auto kDayBaseFile = ":/gui/day-custom-base.tdesktop-theme"_cs;
|
||||
constexpr auto kNightBaseFile = ":/gui/night-custom-base.tdesktop-theme"_cs;
|
||||
|
||||
[[nodiscard]] Fn<void(style::palette&)> PrepareDefaultPaletteCallback() {
|
||||
return [=](style::palette &palette) {
|
||||
palette.reset();
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] Fn<void(style::palette&)> PreparePaletteCallback(
|
||||
bool dark,
|
||||
std::optional<QColor> accent) {
|
||||
bool dark,
|
||||
std::optional<QColor> accent) {
|
||||
return [=](style::palette &palette) {
|
||||
using namespace Theme;
|
||||
const auto &embedded = EmbeddedThemes();
|
||||
|
@ -727,10 +733,23 @@ void SessionNavigation::showPollResults(
|
|||
showSection(std::make_shared<Info::Memento>(poll, contextId), params);
|
||||
}
|
||||
|
||||
struct SessionController::CachedThemeKey {
|
||||
Ui::ChatThemeKey theme;
|
||||
QString paper;
|
||||
|
||||
friend inline auto operator<=>(
|
||||
const CachedThemeKey&,
|
||||
const CachedThemeKey&) = default;
|
||||
[[nodiscard]] explicit operator bool() const {
|
||||
return theme || !paper.isEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
struct SessionController::CachedTheme {
|
||||
std::weak_ptr<Ui::ChatTheme> theme;
|
||||
std::shared_ptr<Data::DocumentMedia> media;
|
||||
Data::WallPaper paper;
|
||||
bool basedOnDark = false;
|
||||
bool caching = false;
|
||||
rpl::lifetime lifetime;
|
||||
};
|
||||
|
@ -2052,19 +2071,29 @@ void SessionController::openDocument(
|
|||
|
||||
auto SessionController::cachedChatThemeValue(
|
||||
const Data::CloudTheme &data,
|
||||
const Data::WallPaper &paper,
|
||||
Data::CloudThemeType type)
|
||||
-> rpl::producer<std::shared_ptr<Ui::ChatTheme>> {
|
||||
const auto key = Ui::ChatThemeKey{
|
||||
const auto themeKey = Ui::ChatThemeKey{
|
||||
data.id,
|
||||
(type == Data::CloudThemeType::Dark),
|
||||
};
|
||||
const auto settings = data.settings.find(type);
|
||||
if (!key
|
||||
|| (settings == end(data.settings))
|
||||
|| !settings->second.paper
|
||||
|| settings->second.paper->backgroundColors().empty()) {
|
||||
if (!themeKey && paper.isNull()) {
|
||||
return rpl::single(_defaultChatTheme);
|
||||
}
|
||||
const auto settings = data.settings.find(type);
|
||||
if (!data.id && settings == end(data.settings)) {
|
||||
return rpl::single(_defaultChatTheme);
|
||||
}
|
||||
if (paper.isNull()
|
||||
&& (!settings->second.paper
|
||||
|| settings->second.paper->backgroundColors().empty())) {
|
||||
return rpl::single(_defaultChatTheme);
|
||||
}
|
||||
const auto key = CachedThemeKey{
|
||||
themeKey,
|
||||
!paper.isNull() ? paper.key() : settings->second.paper->key(),
|
||||
};
|
||||
const auto i = _customChatThemes.find(key);
|
||||
if (i != end(_customChatThemes)) {
|
||||
if (auto strong = i->second.theme.lock()) {
|
||||
|
@ -2073,7 +2102,7 @@ auto SessionController::cachedChatThemeValue(
|
|||
}
|
||||
}
|
||||
if (i == end(_customChatThemes) || !i->second.caching) {
|
||||
cacheChatTheme(data, type);
|
||||
cacheChatTheme(key, data, paper, type);
|
||||
}
|
||||
const auto limit = Data::CloudThemes::TestingColors() ? (1 << 20) : 1;
|
||||
using namespace rpl::mappers;
|
||||
|
@ -2081,7 +2110,8 @@ auto SessionController::cachedChatThemeValue(
|
|||
_defaultChatTheme
|
||||
) | rpl::then(_cachedThemesStream.events(
|
||||
) | rpl::filter([=](const std::shared_ptr<Ui::ChatTheme> &theme) {
|
||||
if (theme->key() != key) {
|
||||
if (theme->key() != key.theme
|
||||
|| theme->background().key != key.paper) {
|
||||
return false;
|
||||
}
|
||||
pushLastUsedChatTheme(theme);
|
||||
|
@ -2089,6 +2119,24 @@ auto SessionController::cachedChatThemeValue(
|
|||
}) | rpl::take(limit));
|
||||
}
|
||||
|
||||
bool SessionController::chatThemeAlreadyCached(
|
||||
const Data::CloudTheme &data,
|
||||
const Data::WallPaper &paper,
|
||||
Data::CloudThemeType type) {
|
||||
Expects(paper.document() != nullptr);
|
||||
|
||||
const auto key = CachedThemeKey{
|
||||
Ui::ChatThemeKey{
|
||||
data.id,
|
||||
(type == Data::CloudThemeType::Dark),
|
||||
},
|
||||
paper.key(),
|
||||
};
|
||||
const auto i = _customChatThemes.find(key);
|
||||
return (i != end(_customChatThemes))
|
||||
&& (i->second.theme.lock() != nullptr);
|
||||
}
|
||||
|
||||
void SessionController::pushLastUsedChatTheme(
|
||||
const std::shared_ptr<Ui::ChatTheme> &theme) {
|
||||
const auto i = ranges::find(_lastUsedCustomChatThemes, theme);
|
||||
|
@ -2124,10 +2172,12 @@ void SessionController::clearCachedChatThemes() {
|
|||
|
||||
void SessionController::overridePeerTheme(
|
||||
not_null<PeerData*> peer,
|
||||
std::shared_ptr<Ui::ChatTheme> theme) {
|
||||
std::shared_ptr<Ui::ChatTheme> theme,
|
||||
EmojiPtr emoji) {
|
||||
_peerThemeOverride = PeerThemeOverride{
|
||||
peer,
|
||||
theme ? theme : _defaultChatTheme,
|
||||
emoji,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -2154,25 +2204,28 @@ void SessionController::pushDefaultChatBackground() {
|
|||
}
|
||||
|
||||
void SessionController::cacheChatTheme(
|
||||
CachedThemeKey key,
|
||||
const Data::CloudTheme &data,
|
||||
const Data::WallPaper &paper,
|
||||
Data::CloudThemeType type) {
|
||||
Expects(data.id != 0);
|
||||
Expects(data.id != 0 || !paper.isNull());
|
||||
|
||||
const auto dark = (type == Data::CloudThemeType::Dark);
|
||||
const auto key = Ui::ChatThemeKey{ data.id, dark };
|
||||
const auto i = data.settings.find(type);
|
||||
Assert(i != end(data.settings));
|
||||
const auto &paper = i->second.paper;
|
||||
Assert(paper.has_value());
|
||||
Assert(!paper->backgroundColors().empty());
|
||||
const auto document = paper->document();
|
||||
Assert((!data.id || (i != end(data.settings)))
|
||||
&& (!paper.isNull()
|
||||
|| (i->second.paper.has_value()
|
||||
&& !i->second.paper->backgroundColors().empty())));
|
||||
const auto &use = !paper.isNull() ? paper : *i->second.paper;
|
||||
const auto document = use.document();
|
||||
const auto media = document ? document->createMediaView() : nullptr;
|
||||
paper->loadDocument();
|
||||
use.loadDocument();
|
||||
auto &theme = [&]() -> CachedTheme& {
|
||||
const auto i = _customChatThemes.find(key);
|
||||
if (i != end(_customChatThemes)) {
|
||||
i->second.media = media;
|
||||
i->second.paper = *paper;
|
||||
i->second.paper = use;
|
||||
i->second.basedOnDark = dark;
|
||||
i->second.caching = true;
|
||||
return i->second;
|
||||
}
|
||||
|
@ -2180,15 +2233,16 @@ void SessionController::cacheChatTheme(
|
|||
key,
|
||||
CachedTheme{
|
||||
.media = media,
|
||||
.paper = *paper,
|
||||
.paper = use,
|
||||
.basedOnDark = dark,
|
||||
.caching = true,
|
||||
}).first->second;
|
||||
}();
|
||||
auto descriptor = Ui::ChatThemeDescriptor{
|
||||
.key = key,
|
||||
.preparePalette = PreparePaletteCallback(
|
||||
dark,
|
||||
i->second.accentColor),
|
||||
.key = key.theme,
|
||||
.preparePalette = (data.id
|
||||
? PreparePaletteCallback(dark, i->second.accentColor)
|
||||
: PrepareDefaultPaletteCallback()),
|
||||
.backgroundData = backgroundData(theme),
|
||||
.bubblesData = PrepareBubblesData(data, type),
|
||||
.basedOnDark = dark,
|
||||
|
@ -2215,7 +2269,10 @@ void SessionController::cacheChatThemeDone(
|
|||
std::shared_ptr<Ui::ChatTheme> result) {
|
||||
Expects(result != nullptr);
|
||||
|
||||
const auto key = result->key();
|
||||
const auto key = CachedThemeKey{
|
||||
result->key(),
|
||||
result->background().key,
|
||||
};
|
||||
const auto i = _customChatThemes.find(key);
|
||||
if (i == end(_customChatThemes)) {
|
||||
return;
|
||||
|
@ -2257,7 +2314,8 @@ void SessionController::updateCustomThemeBackground(CachedTheme &theme) {
|
|||
=,
|
||||
result = Ui::PrepareBackgroundImage(data)
|
||||
]() mutable {
|
||||
const auto i = _customChatThemes.find(key);
|
||||
const auto cacheKey = CachedThemeKey{ key, result.key };
|
||||
const auto i = _customChatThemes.find(cacheKey);
|
||||
if (i != end(_customChatThemes)) {
|
||||
if (const auto strong = i->second.theme.lock()) {
|
||||
strong->updateBackgroundImageFrom(std::move(result));
|
||||
|
@ -2280,14 +2338,20 @@ Ui::ChatThemeBackgroundData SessionController::backgroundData(
|
|||
const auto patternOpacity = paper.patternOpacity();
|
||||
const auto isBlurred = paper.isBlurred();
|
||||
const auto gradientRotation = paper.gradientRotation();
|
||||
const auto darkModeDimming = isPattern
|
||||
? 100
|
||||
: std::clamp(paper.patternIntensity(), 0, 100);
|
||||
return {
|
||||
.key = paper.key(),
|
||||
.path = paperPath,
|
||||
.bytes = paperBytes,
|
||||
.gzipSvg = gzipSvg,
|
||||
.colors = colors,
|
||||
.isPattern = isPattern,
|
||||
.patternOpacity = patternOpacity,
|
||||
.darkModeDimming = darkModeDimming,
|
||||
.isBlurred = isBlurred,
|
||||
.forDarkMode = theme.basedOnDark,
|
||||
.generateGradient = generateGradient,
|
||||
.gradientRotation = gradientRotation,
|
||||
};
|
||||
|
|
|
@ -71,6 +71,7 @@ enum class CloudThemeType;
|
|||
class Thread;
|
||||
class Forum;
|
||||
class ForumTopic;
|
||||
class WallPaper;
|
||||
} // namespace Data
|
||||
|
||||
namespace HistoryView::Reactions {
|
||||
|
@ -108,6 +109,7 @@ enum class ResolveType {
|
|||
struct PeerThemeOverride {
|
||||
PeerData *peer = nullptr;
|
||||
std::shared_ptr<Ui::ChatTheme> theme;
|
||||
EmojiPtr emoji = nullptr;
|
||||
};
|
||||
bool operator==(const PeerThemeOverride &a, const PeerThemeOverride &b);
|
||||
bool operator!=(const PeerThemeOverride &a, const PeerThemeOverride &b);
|
||||
|
@ -538,8 +540,13 @@ public:
|
|||
}
|
||||
[[nodiscard]] auto cachedChatThemeValue(
|
||||
const Data::CloudTheme &data,
|
||||
const Data::WallPaper &paper,
|
||||
Data::CloudThemeType type)
|
||||
-> rpl::producer<std::shared_ptr<Ui::ChatTheme>>;
|
||||
[[nodiscard]] bool chatThemeAlreadyCached(
|
||||
const Data::CloudTheme &data,
|
||||
const Data::WallPaper &paper,
|
||||
Data::CloudThemeType type);
|
||||
void setChatStyleTheme(const std::shared_ptr<Ui::ChatTheme> &theme);
|
||||
void clearCachedChatThemes();
|
||||
void pushLastUsedChatTheme(const std::shared_ptr<Ui::ChatTheme> &theme);
|
||||
|
@ -547,7 +554,8 @@ public:
|
|||
|
||||
void overridePeerTheme(
|
||||
not_null<PeerData*> peer,
|
||||
std::shared_ptr<Ui::ChatTheme> theme);
|
||||
std::shared_ptr<Ui::ChatTheme> theme,
|
||||
EmojiPtr emoji);
|
||||
void clearPeerThemeOverride(not_null<PeerData*> peer);
|
||||
[[nodiscard]] auto peerThemeOverrideValue() const
|
||||
-> rpl::producer<PeerThemeOverride> {
|
||||
|
@ -586,6 +594,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
struct CachedThemeKey;
|
||||
struct CachedTheme;
|
||||
|
||||
void init();
|
||||
|
@ -616,7 +625,9 @@ private:
|
|||
|
||||
void pushDefaultChatBackground();
|
||||
void cacheChatTheme(
|
||||
CachedThemeKey key,
|
||||
const Data::CloudTheme &data,
|
||||
const Data::WallPaper &paper,
|
||||
Data::CloudThemeType type);
|
||||
void cacheChatThemeDone(std::shared_ptr<Ui::ChatTheme> result);
|
||||
void updateCustomThemeBackground(CachedTheme &theme);
|
||||
|
@ -668,7 +679,7 @@ private:
|
|||
rpl::event_stream<> _filtersMenuChanged;
|
||||
|
||||
std::shared_ptr<Ui::ChatTheme> _defaultChatTheme;
|
||||
base::flat_map<Ui::ChatThemeKey, CachedTheme> _customChatThemes;
|
||||
base::flat_map<CachedThemeKey, CachedTheme> _customChatThemes;
|
||||
rpl::event_stream<std::shared_ptr<Ui::ChatTheme>> _cachedThemesStream;
|
||||
const std::unique_ptr<Ui::ChatStyle> _chatStyle;
|
||||
std::weak_ptr<Ui::ChatTheme> _chatStyleTheme;
|
||||
|
|
Loading…
Add table
Reference in a new issue