Use Data::CloudImage for userpics.

This commit is contained in:
John Preston 2020-05-28 18:32:10 +04:00
parent 249f7813c1
commit f066e0f05a
55 changed files with 748 additions and 284 deletions

View File

@ -34,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_chat.h" #include "data/data_chat.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_cloud_file.h"
#include "mainwidget.h" #include "mainwidget.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "apiwrap.h" #include "apiwrap.h"
@ -222,6 +223,7 @@ private:
} }
not_null<PeerData*> peer; not_null<PeerData*> peer;
mutable std::shared_ptr<Data::CloudImageView> userpic;
Ui::Text::String name, status; Ui::Text::String name, status;
}; };
void paintChat(Painter &p, const ChatRow &row, bool selected) const; void paintChat(Painter &p, const ChatRow &row, bool selected) const;
@ -1438,7 +1440,7 @@ void RevokePublicLinkBox::resizeEvent(QResizeEvent *e) {
void RevokePublicLinkBox::Inner::paintChat(Painter &p, const ChatRow &row, bool selected) const { void RevokePublicLinkBox::Inner::paintChat(Painter &p, const ChatRow &row, bool selected) const {
auto peer = row.peer; auto peer = row.peer;
peer->paintUserpicLeft(p, st::contactsPadding.left(), st::contactsPadding.top(), width(), st::contactsPhotoSize); peer->paintUserpicLeft(p, row.userpic, st::contactsPadding.left(), st::contactsPadding.top(), width(), st::contactsPhotoSize);
p.setPen(st::contactsNameFg); p.setPen(st::contactsNameFg);

View File

@ -909,19 +909,20 @@ ConfirmInviteBox::ConfirmInviteBox(
} }
} }
std::vector<not_null<UserData*>> ConfirmInviteBox::GetParticipants( auto ConfirmInviteBox::GetParticipants(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const MTPDchatInvite &data) { const MTPDchatInvite &data)
-> std::vector<Participant> {
const auto participants = data.vparticipants(); const auto participants = data.vparticipants();
if (!participants) { if (!participants) {
return {}; return {};
} }
const auto &v = participants->v; const auto &v = participants->v;
auto result = std::vector<not_null<UserData*>>(); auto result = std::vector<Participant>();
result.reserve(v.size()); result.reserve(v.size());
for (const auto &participant : v) { for (const auto &participant : v) {
if (const auto user = session->data().processUser(participant)) { if (const auto user = session->data().processUser(participant)) {
result.push_back(user); result.push_back(Participant{ user });
} }
} }
return result; return result;
@ -946,12 +947,12 @@ void ConfirmInviteBox::prepare() {
_userWidth = (st::confirmInviteUserPhotoSize + 2 * padding); _userWidth = (st::confirmInviteUserPhotoSize + 2 * padding);
int sumWidth = _participants.size() * _userWidth; int sumWidth = _participants.size() * _userWidth;
int left = (st::boxWideWidth - sumWidth) / 2; int left = (st::boxWideWidth - sumWidth) / 2;
for (const auto user : _participants) { for (const auto &participant : _participants) {
auto name = new Ui::FlatLabel(this, st::confirmInviteUserName); auto name = new Ui::FlatLabel(this, st::confirmInviteUserName);
name->resizeToWidth(st::confirmInviteUserPhotoSize + padding); name->resizeToWidth(st::confirmInviteUserPhotoSize + padding);
name->setText(user->firstName.isEmpty() name->setText(participant.user->firstName.isEmpty()
? user->name ? participant.user->name
: user->firstName); : participant.user->firstName);
name->moveToLeft(left + (padding / 2), st::confirmInviteUserNameTop); name->moveToLeft(left + (padding / 2), st::confirmInviteUserNameTop);
left += _userWidth; left += _userWidth;
} }
@ -993,9 +994,10 @@ void ConfirmInviteBox::paintEvent(QPaintEvent *e) {
int sumWidth = _participants.size() * _userWidth; int sumWidth = _participants.size() * _userWidth;
int left = (width() - sumWidth) / 2; int left = (width() - sumWidth) / 2;
for_const (auto user, _participants) { for (auto &participant : _participants) {
user->paintUserpicLeft( participant.user->paintUserpicLeft(
p, p,
participant.userpic,
left + (_userWidth - st::confirmInviteUserPhotoSize) / 2, left + (_userWidth - st::confirmInviteUserPhotoSize) / 2,
st::confirmInviteUserPhotoTop, st::confirmInviteUserPhotoTop,
width(), width(),

View File

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Data { namespace Data {
class PhotoMedia; class PhotoMedia;
class CloudImageView;
} // namespace Data } // namespace Data
namespace Main { namespace Main {
@ -225,7 +226,11 @@ protected:
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
private: private:
static std::vector<not_null<UserData*>> GetParticipants( struct Participant {
not_null<UserData*> user;
std::shared_ptr<Data::CloudImageView> userpic;
};
static std::vector<Participant> GetParticipants(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const MTPDchatInvite &data); const MTPDchatInvite &data);
@ -236,7 +241,7 @@ private:
object_ptr<Ui::FlatLabel> _status; object_ptr<Ui::FlatLabel> _status;
std::shared_ptr<Data::PhotoMedia> _photo; std::shared_ptr<Data::PhotoMedia> _photo;
std::unique_ptr<Ui::EmptyUserpic> _photoEmpty; std::unique_ptr<Ui::EmptyUserpic> _photoEmpty;
std::vector<not_null<UserData*>> _participants; std::vector<Participant> _participants;
bool _isChannel = false; bool _isChannel = false;
int _userWidth = 0; int _userWidth = 0;

View File

@ -78,6 +78,7 @@ private:
}; };
struct PeerButton { struct PeerButton {
not_null<History*> history; not_null<History*> history;
std::shared_ptr<Data::CloudImageView> userpic;
Button button; Button button;
}; };
@ -185,8 +186,9 @@ void FilterChatsPreview::updateData(
} }
for (const auto history : peers) { for (const auto history : peers) {
_removePeer.push_back({ _removePeer.push_back({
history, .history = history,
makeButton([=] { removePeer(history); }) }); .button = makeButton([=] { removePeer(history); })
});
} }
refresh(); refresh();
} }
@ -203,7 +205,7 @@ int FilterChatsPreview::resizeGetHeight(int newWidth) {
for (const auto &[flag, button] : _removeFlag) { for (const auto &[flag, button] : _removeFlag) {
moveNextButton(button.get()); moveNextButton(button.get());
} }
for (const auto &[history, button] : _removePeer) { for (const auto &[history, userpic, button] : _removePeer) {
moveNextButton(button.get()); moveNextButton(button.get());
} }
return top; return top;
@ -235,7 +237,7 @@ void FilterChatsPreview::paintEvent(QPaintEvent *e) {
FilterChatsTypeName(flag)); FilterChatsTypeName(flag));
top += st.height; top += st.height;
} }
for (const auto &[history, button] : _removePeer) { for (auto &[history, userpic, button] : _removePeer) {
const auto savedMessages = history->peer->isSelf(); const auto savedMessages = history->peer->isSelf();
if (savedMessages) { if (savedMessages) {
Ui::EmptyUserpic::PaintSavedMessages( Ui::EmptyUserpic::PaintSavedMessages(
@ -253,6 +255,7 @@ void FilterChatsPreview::paintEvent(QPaintEvent *e) {
} else { } else {
history->peer->paintUserpicLeft( history->peer->paintUserpicLeft(
p, p,
userpic,
iconLeft, iconLeft,
top + iconTop, top + iconTop,
width(), width(),

View File

@ -180,11 +180,12 @@ QString ExceptionRow::generateShortName() {
PaintRoundImageCallback ExceptionRow::generatePaintUserpicCallback() { PaintRoundImageCallback ExceptionRow::generatePaintUserpicCallback() {
const auto peer = this->peer(); const auto peer = this->peer();
const auto saved = peer->isSelf(); const auto saved = peer->isSelf();
return [=](Painter &p, int x, int y, int outerWidth, int size) { auto userpic = saved ? nullptr : ensureUserpicView();
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
if (saved) { if (saved) {
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size); Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
} else { } else {
peer->paintUserpicLeft(p, x, y, outerWidth, size); peer->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
} }
}; };
} }

View File

@ -41,8 +41,9 @@ PaintRoundImageCallback PaintUserpicCallback(
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size); Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
}; };
} }
return [=](Painter &p, int x, int y, int outerWidth, int size) { auto userpic = std::shared_ptr<Data::CloudImageView>();
peer->paintUserpicLeft(p, x, y, outerWidth, size); return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
peer->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
}; };
} }
@ -483,14 +484,22 @@ QString PeerListRow::generateShortName() {
: peer()->shortName(); : peer()->shortName();
} }
std::shared_ptr<Data::CloudImageView> PeerListRow::ensureUserpicView() {
if (!_userpic) {
_userpic = peer()->createUserpicView();
}
return _userpic;
}
PaintRoundImageCallback PeerListRow::generatePaintUserpicCallback() { PaintRoundImageCallback PeerListRow::generatePaintUserpicCallback() {
const auto saved = _isSavedMessagesChat; const auto saved = _isSavedMessagesChat;
const auto peer = this->peer(); const auto peer = this->peer();
return [=](Painter &p, int x, int y, int outerWidth, int size) { auto userpic = saved ? nullptr : ensureUserpicView();
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
if (saved) { if (saved) {
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size); Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
} else { } else {
peer->paintUserpicLeft(p, x, y, outerWidth, size); peer->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
} }
}; };
} }
@ -595,7 +604,7 @@ void PeerListRow::paintDisabledCheckUserpic(
if (_isSavedMessagesChat) { if (_isSavedMessagesChat) {
Ui::EmptyUserpic::PaintSavedMessages(p, userpicLeft, userpicTop, outerWidth, userpicRadius * 2); Ui::EmptyUserpic::PaintSavedMessages(p, userpicLeft, userpicTop, outerWidth, userpicRadius * 2);
} else { } else {
peer()->paintUserpicLeft(p, userpicLeft, userpicTop, outerWidth, userpicRadius * 2); peer()->paintUserpicLeft(p, _userpic, userpicLeft, userpicTop, outerWidth, userpicRadius * 2);
} }
{ {

View File

@ -7,11 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
#include <rpl/event_stream.h>
#include "ui/rp_widget.h" #include "ui/rp_widget.h"
#include "ui/empty_userpic.h" #include "ui/empty_userpic.h"
#include "boxes/abstract_box.h" #include "boxes/abstract_box.h"
#include "mtproto/sender.h" #include "mtproto/sender.h"
#include "data/data_cloud_file.h"
#include "base/timer.h" #include "base/timer.h"
namespace style { namespace style {
@ -85,6 +85,8 @@ public:
return _id; return _id;
} }
[[nodiscard]] std::shared_ptr<Data::CloudImageView> ensureUserpicView();
[[nodiscard]] virtual QString generateName(); [[nodiscard]] virtual QString generateName();
[[nodiscard]] virtual QString generateShortName(); [[nodiscard]] virtual QString generateShortName();
[[nodiscard]] virtual auto generatePaintUserpicCallback() [[nodiscard]] virtual auto generatePaintUserpicCallback()
@ -223,6 +225,7 @@ private:
PeerListRowId _id = 0; PeerListRowId _id = 0;
PeerData *_peer = nullptr; PeerData *_peer = nullptr;
mutable std::shared_ptr<Data::CloudImageView> _userpic;
std::unique_ptr<Ui::RippleAnimation> _ripple; std::unique_ptr<Ui::RippleAnimation> _ripple;
std::unique_ptr<Ui::RoundImageCheckbox> _checkbox; std::unique_ptr<Ui::RoundImageCheckbox> _checkbox;
Ui::Text::String _name; Ui::Text::String _name;

View File

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_photo_media.h" #include "data/data_photo_media.h"
#include "data/data_cloud_file.h"
#include "calls/calls_emoji_fingerprint.h" #include "calls/calls_emoji_fingerprint.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h" #include "ui/widgets/labels.h"
@ -490,6 +491,7 @@ void Panel::finishAnimating() {
void Panel::showControls() { void Panel::showControls() {
Expects(_call != nullptr); Expects(_call != nullptr);
showChildren(); showChildren();
_decline->setVisible(_decline->toggled()); _decline->setVisible(_decline->toggled());
_cancel->setVisible(_cancel->toggled()); _cancel->setVisible(_cancel->toggled());
@ -511,9 +513,8 @@ void Panel::hideAndDestroy() {
} }
void Panel::processUserPhoto() { void Panel::processUserPhoto() {
if (!_user->userpicLoaded()) { _userpic = _user->createUserpicView();
_user->loadUserpic(); _user->loadUserpic();
}
const auto photo = _user->userpicPhotoId() const auto photo = _user->userpicPhotoId()
? _user->owner().photo(_user->userpicPhotoId()).get() ? _user->owner().photo(_user->userpicPhotoId()).get()
: nullptr; : nullptr;
@ -542,9 +543,8 @@ void Panel::refreshUserPhoto() {
_photo->image(Data::PhotoSize::Large), _photo->image(Data::PhotoSize::Large),
_user->userpicPhotoOrigin()); _user->userpicPhotoOrigin());
} else if (_userPhoto.isNull()) { } else if (_userPhoto.isNull()) {
const auto userpic = _user->currentUserpic();
createUserpicCache( createUserpicCache(
userpic ? userpic.get() : nullptr, _userpic ? _userpic->image() : nullptr,
_user->userpicOrigin()); _user->userpicOrigin());
} }
} }

View File

@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Data { namespace Data {
class PhotoMedia; class PhotoMedia;
class CloudImageView;
} // namespace Data } // namespace Data
namespace Ui { namespace Ui {
@ -116,6 +117,7 @@ private:
Call *_call = nullptr; Call *_call = nullptr;
not_null<UserData*> _user; not_null<UserData*> _user;
std::shared_ptr<Data::CloudImageView> _userpic;
std::shared_ptr<Data::PhotoMedia> _photo; std::shared_ptr<Data::PhotoMedia> _photo;
bool _useTransparency = true; bool _useTransparency = true;

View File

@ -50,9 +50,9 @@ FieldAutocomplete::FieldAutocomplete(
&_srows)); &_srows));
_inner->setGeometry(rect()); _inner->setGeometry(rect());
connect(_inner, SIGNAL(mentionChosen(UserData*, FieldAutocomplete::ChooseMethod)), this, SIGNAL(mentionChosen(UserData*, FieldAutocomplete::ChooseMethod))); connect(_inner, SIGNAL(mentionChosen(not_null<UserData*>,FieldAutocomplete::ChooseMethod)), this, SIGNAL(mentionChosen(not_null<UserData*>,FieldAutocomplete::ChooseMethod)));
connect(_inner, SIGNAL(hashtagChosen(QString, FieldAutocomplete::ChooseMethod)), this, SIGNAL(hashtagChosen(QString, FieldAutocomplete::ChooseMethod))); connect(_inner, SIGNAL(hashtagChosen(QString,FieldAutocomplete::ChooseMethod)), this, SIGNAL(hashtagChosen(QString,FieldAutocomplete::ChooseMethod)));
connect(_inner, SIGNAL(botCommandChosen(QString, FieldAutocomplete::ChooseMethod)), this, SIGNAL(botCommandChosen(QString, FieldAutocomplete::ChooseMethod))); connect(_inner, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod)), this, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod)));
connect(_inner, SIGNAL(stickerChosen(not_null<DocumentData*>,FieldAutocomplete::ChooseMethod)), this, SIGNAL(stickerChosen(not_null<DocumentData*>,FieldAutocomplete::ChooseMethod))); connect(_inner, SIGNAL(stickerChosen(not_null<DocumentData*>,FieldAutocomplete::ChooseMethod)), this, SIGNAL(stickerChosen(not_null<DocumentData*>,FieldAutocomplete::ChooseMethod)));
connect(_inner, SIGNAL(mustScrollTo(int, int)), _scroll, SLOT(scrollToY(int, int))); connect(_inner, SIGNAL(mustScrollTo(int, int)), _scroll, SLOT(scrollToY(int, int)));
@ -152,7 +152,9 @@ void FieldAutocomplete::showStickers(EmojiPtr emoji) {
} }
bool FieldAutocomplete::clearFilteredBotCommands() { bool FieldAutocomplete::clearFilteredBotCommands() {
if (_brows.isEmpty()) return false; if (_brows.empty()) {
return false;
}
_brows.clear(); _brows.clear();
return true; return true;
} }
@ -160,8 +162,8 @@ bool FieldAutocomplete::clearFilteredBotCommands() {
namespace { namespace {
template <typename T, typename U> template <typename T, typename U>
inline int indexOfInFirstN(const T &v, const U &elem, int last) { inline int indexOfInFirstN(const T &v, const U &elem, int last) {
for (auto b = v.cbegin(), i = b, e = b + qMax(v.size(), last); i != e; ++i) { for (auto b = v.cbegin(), i = b, e = b + std::max(int(v.size()), last); i != e; ++i) {
if (*i == elem) { if (i->user == elem) {
return (i - b); return (i - b);
} }
} }
@ -241,12 +243,12 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
for_const (auto user, cRecentInlineBots()) { for_const (auto user, cRecentInlineBots()) {
if (user->isInaccessible()) continue; if (user->isInaccessible()) continue;
if (!listAllSuggestions && filterNotPassedByUsername(user)) continue; if (!listAllSuggestions && filterNotPassedByUsername(user)) continue;
mrows.push_back(user); mrows.push_back({ user });
++recentInlineBots; ++recentInlineBots;
} }
} }
if (_chat) { if (_chat) {
auto ordered = QMultiMap<TimeId, not_null<UserData*>>(); auto sorted = base::flat_multi_map<TimeId, not_null<UserData*>>();
const auto byOnline = [&](not_null<UserData*> user) { const auto byOnline = [&](not_null<UserData*> user) {
return Data::SortByOnlineValue(user, now); return Data::SortByOnlineValue(user, now);
}; };
@ -258,23 +260,19 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
if (user->isInaccessible()) continue; if (user->isInaccessible()) continue;
if (!listAllSuggestions && filterNotPassedByName(user)) continue; if (!listAllSuggestions && filterNotPassedByName(user)) continue;
if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue; if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue;
ordered.insertMulti(byOnline(user), user); sorted.emplace(byOnline(user), user);
} }
} }
for (const auto user : _chat->lastAuthors) { for (const auto user : _chat->lastAuthors) {
if (user->isInaccessible()) continue; if (user->isInaccessible()) continue;
if (!listAllSuggestions && filterNotPassedByName(user)) continue; if (!listAllSuggestions && filterNotPassedByName(user)) continue;
if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue; if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue;
mrows.push_back(user); mrows.push_back({ user });
if (!ordered.isEmpty()) { sorted.remove(byOnline(user), user);
ordered.remove(byOnline(user), user);
} }
} for (auto i = sorted.cend(), b = sorted.cbegin(); i != b;) {
if (!ordered.isEmpty()) {
for (auto i = ordered.cend(), b = ordered.cbegin(); i != b;) {
--i; --i;
mrows.push_back(i.value()); mrows.push_back({ i->second });
}
} }
} else if (_channel && _channel->isMegagroup()) { } else if (_channel && _channel->isMegagroup()) {
QMultiMap<int32, UserData*> ordered; QMultiMap<int32, UserData*> ordered;
@ -286,7 +284,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
if (user->isInaccessible()) continue; if (user->isInaccessible()) continue;
if (!listAllSuggestions && filterNotPassedByName(user)) continue; if (!listAllSuggestions && filterNotPassedByName(user)) continue;
if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue; if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue;
mrows.push_back(user); mrows.push_back({ user });
} }
} }
} }
@ -378,7 +376,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
continue; continue;
} }
} }
brows.push_back(qMakePair(user, &user->botInfo->commands.at(j))); brows.push_back({ user, &user->botInfo->commands.at(j) });
} }
} }
} }
@ -390,7 +388,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
QString toFilter = (hasUsername || botStatus == 0 || botStatus == 2) ? user->botInfo->commands.at(j).command + '@' + user->username : user->botInfo->commands.at(j).command; QString toFilter = (hasUsername || botStatus == 0 || botStatus == 2) ? user->botInfo->commands.at(j).command + '@' + user->username : user->botInfo->commands.at(j).command;
if (!toFilter.startsWith(_filter, Qt::CaseInsensitive)/* || toFilter.size() == _filter.size()*/) continue; if (!toFilter.startsWith(_filter, Qt::CaseInsensitive)/* || toFilter.size() == _filter.size()*/) continue;
} }
brows.push_back(qMakePair(user, &user->botInfo->commands.at(j))); brows.push_back({ user, &user->botInfo->commands.at(j) });
} }
} }
} }
@ -411,7 +409,7 @@ void FieldAutocomplete::rowsUpdated(
internal::BotCommandRows &&brows, internal::BotCommandRows &&brows,
internal::StickerRows &&srows, internal::StickerRows &&srows,
bool resetScroll) { bool resetScroll) {
if (mrows.isEmpty() && hrows.isEmpty() && brows.isEmpty() && srows.empty()) { if (mrows.empty() && hrows.empty() && brows.empty() && srows.empty()) {
if (!isHidden()) { if (!isHidden()) {
hideAnimated(); hideAnimated();
} }
@ -452,11 +450,11 @@ void FieldAutocomplete::recount(bool resetScroll) {
int32 stickersPerRow = qMax(1, int32(_boundings.width() - 2 * st::stickerPanPadding) / int32(st::stickerPanSize.width())); int32 stickersPerRow = qMax(1, int32(_boundings.width() - 2 * st::stickerPanPadding) / int32(st::stickerPanSize.width()));
int32 rows = rowscount(_srows.size(), stickersPerRow); int32 rows = rowscount(_srows.size(), stickersPerRow);
h = st::stickerPanPadding + rows * st::stickerPanSize.height(); h = st::stickerPanPadding + rows * st::stickerPanSize.height();
} else if (!_mrows.isEmpty()) { } else if (!_mrows.empty()) {
h = _mrows.size() * st::mentionHeight; h = _mrows.size() * st::mentionHeight;
} else if (!_hrows.isEmpty()) { } else if (!_hrows.empty()) {
h = _hrows.size() * st::mentionHeight; h = _hrows.size() * st::mentionHeight;
} else if (!_brows.isEmpty()) { } else if (!_brows.empty()) {
h = _brows.size() * st::mentionHeight; h = _brows.size() * st::mentionHeight;
} }
@ -693,7 +691,11 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) {
} }
} else { } else {
int32 from = qFloor(e->rect().top() / st::mentionHeight), to = qFloor(e->rect().bottom() / st::mentionHeight) + 1; int32 from = qFloor(e->rect().top() / st::mentionHeight), to = qFloor(e->rect().bottom() / st::mentionHeight) + 1;
int32 last = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size(); int32 last = !_mrows->empty()
? _mrows->size()
: !_hrows->empty()
? _hrows->size()
: _brows->size();
auto filter = _parent->filter(); auto filter = _parent->filter();
bool hasUsername = filter.indexOf('@') > 0; bool hasUsername = filter.indexOf('@') > 0;
int filterSize = filter.size(); int filterSize = filter.size();
@ -705,12 +707,13 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) {
if (selected) { if (selected) {
p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::mentionBgOver); p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::mentionBgOver);
int skip = (st::mentionHeight - st::smallCloseIconOver.height()) / 2; int skip = (st::mentionHeight - st::smallCloseIconOver.height()) / 2;
if (!_hrows->isEmpty() || (!_mrows->isEmpty() && i < _recentInlineBotsInRows)) { if (!_hrows->empty() || (!_mrows->empty() && i < _recentInlineBotsInRows)) {
st::smallCloseIconOver.paint(p, QPoint(width() - st::smallCloseIconOver.width() - skip, i * st::mentionHeight + skip), width()); st::smallCloseIconOver.paint(p, QPoint(width() - st::smallCloseIconOver.width() - skip, i * st::mentionHeight + skip), width());
} }
} }
if (!_mrows->isEmpty()) { if (!_mrows->empty()) {
const auto user = _mrows->at(i); auto &row = _mrows->at(i);
const auto user = row.user;
auto first = (!filterIsEmpty && user->username.startsWith(filter, Qt::CaseInsensitive)) ? ('@' + user->username.mid(0, filterSize)) : QString(); auto first = (!filterIsEmpty && user->username.startsWith(filter, Qt::CaseInsensitive)) ? ('@' + user->username.mid(0, filterSize)) : QString();
auto second = first.isEmpty() ? (user->username.isEmpty() ? QString() : ('@' + user->username)) : user->username.mid(filterSize); auto second = first.isEmpty() ? (user->username.isEmpty() ? QString() : ('@' + user->username)) : user->username.mid(filterSize);
auto firstwidth = st::mentionFont->width(first); auto firstwidth = st::mentionFont->width(first);
@ -732,7 +735,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) {
} }
} }
user->loadUserpic(); user->loadUserpic();
user->paintUserpicLeft(p, st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), width(), st::mentionPhotoSize); user->paintUserpicLeft(p, row.userpic, st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), width(), st::mentionPhotoSize);
p.setPen(selected ? st::mentionNameFgOver : st::mentionNameFg); p.setPen(selected ? st::mentionNameFgOver : st::mentionNameFg);
user->nameText().drawElided(p, 2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop, namewidth); user->nameText().drawElided(p, 2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop, namewidth);
@ -744,7 +747,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) {
p.setPen(selected ? st::mentionFgOver : st::mentionFg); p.setPen(selected ? st::mentionFgOver : st::mentionFg);
p.drawText(mentionleft + namewidth + st::mentionPadding.right() + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second); p.drawText(mentionleft + namewidth + st::mentionPadding.right() + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
} }
} else if (!_hrows->isEmpty()) { } else if (!_hrows->empty()) {
QString hrow = _hrows->at(i); QString hrow = _hrows->at(i);
QString first = filterIsEmpty ? QString() : ('#' + hrow.mid(0, filterSize)); QString first = filterIsEmpty ? QString() : ('#' + hrow.mid(0, filterSize));
QString second = filterIsEmpty ? ('#' + hrow) : hrow.mid(filterSize); QString second = filterIsEmpty ? ('#' + hrow) : hrow.mid(filterSize);
@ -768,16 +771,17 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) {
p.drawText(htagleft + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second); p.drawText(htagleft + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second);
} }
} else { } else {
UserData *user = _brows->at(i).first; auto &row = _brows->at(i);
const auto user = row.user;
const BotCommand *command = _brows->at(i).second; const auto command = row.command;
QString toHighlight = command->command; auto toHighlight = command->command;
int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1); int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1);
if (hasUsername || botStatus == 0 || botStatus == 2) { if (hasUsername || botStatus == 0 || botStatus == 2) {
toHighlight += '@' + user->username; toHighlight += '@' + user->username;
} }
user->loadUserpic(); user->loadUserpic();
user->paintUserpicLeft(p, st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), width(), st::mentionPhotoSize); user->paintUserpicLeft(p, row.userpic, st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), width(), st::mentionPhotoSize);
auto commandText = '/' + toHighlight; auto commandText = '/' + toHighlight;
@ -820,7 +824,7 @@ void FieldAutocompleteInner::clearSel(bool hidden) {
_overDelete = false; _overDelete = false;
_mouseSelection = false; _mouseSelection = false;
_lastMousePosition = std::nullopt; _lastMousePosition = std::nullopt;
setSel((_mrows->isEmpty() && _brows->isEmpty() && _hrows->isEmpty()) ? -1 : 0); setSel((_mrows->empty() && _brows->empty() && _hrows->empty()) ? -1 : 0);
if (hidden) { if (hidden) {
_down = -1; _down = -1;
_previewShown = false; _previewShown = false;
@ -831,7 +835,13 @@ bool FieldAutocompleteInner::moveSel(int key) {
_mouseSelection = false; _mouseSelection = false;
_lastMousePosition = std::nullopt; _lastMousePosition = std::nullopt;
int32 maxSel = (_mrows->isEmpty() ? (_hrows->isEmpty() ? (_brows->isEmpty() ? _srows->size() : _brows->size()) : _hrows->size()) : _mrows->size()); int32 maxSel = !_mrows->empty()
? _mrows->size()
: !_hrows->empty()
? _hrows->size()
: !_brows->empty()
? _brows->size()
: _srows->size();
int32 direction = (key == Qt::Key_Up) ? -1 : (key == Qt::Key_Down ? 1 : 0); int32 direction = (key == Qt::Key_Up) ? -1 : (key == Qt::Key_Down ? 1 : 0);
if (!_srows->empty()) { if (!_srows->empty()) {
if (key == Qt::Key_Left) { if (key == Qt::Key_Left) {
@ -862,20 +872,20 @@ bool FieldAutocompleteInner::chooseSelected(FieldAutocomplete::ChooseMethod meth
emit stickerChosen((*_srows)[_sel].document, method); emit stickerChosen((*_srows)[_sel].document, method);
return true; return true;
} }
} else if (!_mrows->isEmpty()) { } else if (!_mrows->empty()) {
if (_sel >= 0 && _sel < _mrows->size()) { if (_sel >= 0 && _sel < _mrows->size()) {
emit mentionChosen(_mrows->at(_sel), method); emit mentionChosen(_mrows->at(_sel).user, method);
return true; return true;
} }
} else if (!_hrows->isEmpty()) { } else if (!_hrows->empty()) {
if (_sel >= 0 && _sel < _hrows->size()) { if (_sel >= 0 && _sel < _hrows->size()) {
emit hashtagChosen('#' + _hrows->at(_sel), method); emit hashtagChosen('#' + _hrows->at(_sel), method);
return true; return true;
} }
} else if (!_brows->isEmpty()) { } else if (!_brows->empty()) {
if (_sel >= 0 && _sel < _brows->size()) { if (_sel >= 0 && _sel < _brows->size()) {
UserData *user = _brows->at(_sel).first; const auto user = _brows->at(_sel).user;
const BotCommand *command(_brows->at(_sel).second); const auto command = _brows->at(_sel).command;
int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1); int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1);
if (botStatus == 0 || botStatus == 2 || _parent->filter().indexOf('@') > 0) { if (botStatus == 0 || botStatus == 2 || _parent->filter().indexOf('@') > 0) {
emit botCommandChosen('/' + command->command + '@' + user->username, method); emit botCommandChosen('/' + command->command + '@' + user->username, method);
@ -895,9 +905,9 @@ void FieldAutocompleteInner::setRecentInlineBotsInRows(int32 bots) {
void FieldAutocompleteInner::mousePressEvent(QMouseEvent *e) { void FieldAutocompleteInner::mousePressEvent(QMouseEvent *e) {
selectByMouse(e->globalPos()); selectByMouse(e->globalPos());
if (e->button() == Qt::LeftButton) { if (e->button() == Qt::LeftButton) {
if (_overDelete && _sel >= 0 && _sel < (_mrows->isEmpty() ? _hrows->size() : _recentInlineBotsInRows)) { if (_overDelete && _sel >= 0 && _sel < (_mrows->empty() ? _hrows->size() : _recentInlineBotsInRows)) {
bool removed = false; bool removed = false;
if (_mrows->isEmpty()) { if (_mrows->empty()) {
QString toRemove = _hrows->at(_sel); QString toRemove = _hrows->at(_sel);
RecentHashtagPack &recent(cRefRecentWriteHashtags()); RecentHashtagPack &recent(cRefRecentWriteHashtags());
for (RecentHashtagPack::iterator i = recent.begin(); i != recent.cend();) { for (RecentHashtagPack::iterator i = recent.begin(); i != recent.cend();) {
@ -909,7 +919,7 @@ void FieldAutocompleteInner::mousePressEvent(QMouseEvent *e) {
} }
} }
} else { } else {
UserData *toRemove = _mrows->at(_sel); UserData *toRemove = _mrows->at(_sel).user;
RecentInlineBots &recent(cRefRecentInlineBots()); RecentInlineBots &recent(cRefRecentInlineBots());
int32 index = recent.indexOf(toRemove); int32 index = recent.indexOf(toRemove);
if (index >= 0) { if (index >= 0) {
@ -1066,8 +1076,12 @@ void FieldAutocompleteInner::selectByMouse(QPoint globalPosition) {
_overDelete = false; _overDelete = false;
} else { } else {
sel = mouse.y() / int32(st::mentionHeight); sel = mouse.y() / int32(st::mentionHeight);
maxSel = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size(); maxSel = !_mrows->empty()
_overDelete = (!_hrows->isEmpty() || (!_mrows->isEmpty() && sel < _recentInlineBotsInRows)) ? (mouse.x() >= width() - st::mentionHeight) : false; ? _mrows->size()
: !_hrows->empty()
? _hrows->size()
: _brows->size();
_overDelete = (!_hrows->empty() || (!_mrows->empty() && sel < _recentInlineBotsInRows)) ? (mouse.x() >= width() - st::mentionHeight) : false;
} }
if (sel < 0 || sel >= maxSel) { if (sel < 0 || sel >= maxSel) {
sel = -1; sel = -1;

View File

@ -28,6 +28,7 @@ class SessionController;
namespace Data { namespace Data {
class DocumentMedia; class DocumentMedia;
class CloudImageView;
} // namespace Data } // namespace Data
namespace internal { namespace internal {
@ -38,10 +39,21 @@ struct StickerSuggestion {
std::unique_ptr<Lottie::SinglePlayer> animated; std::unique_ptr<Lottie::SinglePlayer> animated;
}; };
using MentionRows = QList<UserData*>; struct MentionRow {
using HashtagRows = QList<QString>; not_null<UserData*> user;
using BotCommandRows = QList<QPair<UserData*, const BotCommand*>>; std::shared_ptr<Data::CloudImageView> userpic;
};
struct BotCommandRow {
not_null<UserData*> user;
not_null<const BotCommand*> command;
std::shared_ptr<Data::CloudImageView> userpic;
};
using HashtagRows = std::vector<QString>;
using BotCommandRows = std::vector<BotCommandRow>;
using StickerRows = std::vector<StickerSuggestion>; using StickerRows = std::vector<StickerSuggestion>;
using MentionRows = std::vector<MentionRow>;
class FieldAutocompleteInner; class FieldAutocompleteInner;
@ -94,7 +106,7 @@ public:
void hideFast(); void hideFast();
signals: signals:
void mentionChosen(UserData *user, FieldAutocomplete::ChooseMethod method) const; void mentionChosen(not_null<UserData*> user, FieldAutocomplete::ChooseMethod method) const;
void hashtagChosen(QString hashtag, FieldAutocomplete::ChooseMethod method) const; void hashtagChosen(QString hashtag, FieldAutocomplete::ChooseMethod method) const;
void botCommandChosen(QString command, FieldAutocomplete::ChooseMethod method) const; void botCommandChosen(QString command, FieldAutocomplete::ChooseMethod method) const;
void stickerChosen(not_null<DocumentData*> sticker, FieldAutocomplete::ChooseMethod method) const; void stickerChosen(not_null<DocumentData*> sticker, FieldAutocomplete::ChooseMethod method) const;
@ -182,7 +194,7 @@ public:
void rowsUpdated(); void rowsUpdated();
signals: signals:
void mentionChosen(UserData *user, FieldAutocomplete::ChooseMethod method) const; void mentionChosen(not_null<UserData*> user, FieldAutocomplete::ChooseMethod method) const;
void hashtagChosen(QString hashtag, FieldAutocomplete::ChooseMethod method) const; void hashtagChosen(QString hashtag, FieldAutocomplete::ChooseMethod method) const;
void botCommandChosen(QString command, FieldAutocomplete::ChooseMethod method) const; void botCommandChosen(QString command, FieldAutocomplete::ChooseMethod method) const;
void stickerChosen(not_null<DocumentData*> sticker, FieldAutocomplete::ChooseMethod method) const; void stickerChosen(not_null<DocumentData*> sticker, FieldAutocomplete::ChooseMethod method) const;

View File

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_cloud_file.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
#include "ui/effects/ripple_animation.h" #include "ui/effects/ripple_animation.h"
@ -89,9 +90,10 @@ struct StickerIcon {
mutable std::unique_ptr<Lottie::SinglePlayer> lottie; mutable std::unique_ptr<Lottie::SinglePlayer> lottie;
mutable QPixmap savedFrame; mutable QPixmap savedFrame;
DocumentData *sticker = nullptr; DocumentData *sticker = nullptr;
ChannelData *megagroup = nullptr;
mutable std::shared_ptr<Stickers::SetThumbnailView> thumbnailMedia; mutable std::shared_ptr<Stickers::SetThumbnailView> thumbnailMedia;
mutable std::shared_ptr<Data::DocumentMedia> stickerMedia; mutable std::shared_ptr<Data::DocumentMedia> stickerMedia;
ChannelData *megagroup = nullptr; mutable std::shared_ptr<Data::CloudImageView> megagroupUserpic;
int pixw = 0; int pixw = 0;
int pixh = 0; int pixh = 0;
mutable rpl::lifetime lifetime; mutable rpl::lifetime lifetime;
@ -820,7 +822,7 @@ void StickersListWidget::Footer::paintSetIcon(
} }
} }
} else if (icon.megagroup) { } else if (icon.megagroup) {
icon.megagroup->paintUserpicLeft(p, x + (st::stickerIconWidth - st::stickerGroupCategorySize) / 2, _iconsTop + (st::emojiFooterHeight - st::stickerGroupCategorySize) / 2, width(), st::stickerGroupCategorySize); icon.megagroup->paintUserpicLeft(p, icon.megagroupUserpic, x + (st::stickerIconWidth - st::stickerGroupCategorySize) / 2, _iconsTop + (st::emojiFooterHeight - st::stickerGroupCategorySize) / 2, width(), st::stickerGroupCategorySize);
} else { } else {
const auto paintedIcon = [&] { const auto paintedIcon = [&] {
if (icon.setId == Stickers::FeaturedSetId) { if (icon.setId == Stickers::FeaturedSetId) {

View File

@ -38,6 +38,36 @@ Image *CloudImageView::image() const {
return _image.get(); return _image.get();
} }
void CloudImage::set(
not_null<Main::Session*> session,
const ImageWithLocation &data) {
const auto &was = _file.location.file().data;
const auto &now = data.location.file().data;
if (!data.location.valid()) {
_file.flags |= CloudFile::Flag::Cancelled;
_file.loader = nullptr;
_file.location = ImageLocation();
_file.byteSize = 0;
_file.flags = CloudFile::Flag();
_view = std::weak_ptr<CloudImageView>();
} else if (was != now
&& (!was.is<InMemoryLocation>() || now.is<InMemoryLocation>())) {
_file.location = ImageLocation();
_view = std::weak_ptr<CloudImageView>();
}
UpdateCloudFile(
_file,
data,
session->data().cache(),
kImageCacheTag,
[=](FileOrigin origin) { load(session, origin); },
[=](QImage preloaded) {
if (const auto view = activeView()) {
view->set(session, data.preloaded);
}
});
}
void CloudImage::update( void CloudImage::update(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const ImageWithLocation &data) { const ImageWithLocation &data) {
@ -103,6 +133,14 @@ std::shared_ptr<CloudImageView> CloudImage::activeView() {
return _view.lock(); return _view.lock();
} }
bool CloudImage::isCurrentView(
const std::shared_ptr<CloudImageView> &view) const {
if (!view) {
return empty();
}
return !view.owner_before(_view) && !_view.owner_before(view);
}
void UpdateCloudFile( void UpdateCloudFile(
CloudFile &file, CloudFile &file,
const ImageWithLocation &data, const ImageWithLocation &data,

View File

@ -57,6 +57,11 @@ public:
not_null<Main::Session*> session, not_null<Main::Session*> session,
const ImageWithLocation &data); const ImageWithLocation &data);
// This method will replace the location and zero the _view pointer.
void set(
not_null<Main::Session*> session,
const ImageWithLocation &data);
void update( void update(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const ImageWithLocation &data); const ImageWithLocation &data);
@ -70,6 +75,8 @@ public:
[[nodiscard]] std::shared_ptr<CloudImageView> createView(); [[nodiscard]] std::shared_ptr<CloudImageView> createView();
[[nodiscard]] std::shared_ptr<CloudImageView> activeView(); [[nodiscard]] std::shared_ptr<CloudImageView> activeView();
[[nodiscard]] bool isCurrentView(
const std::shared_ptr<CloudImageView> &view) const;
private: private:
CloudFile _file; CloudFile _file;

View File

@ -248,6 +248,7 @@ void Folder::loadUserpic() {
void Folder::paintUserpic( void Folder::paintUserpic(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
int x, int x,
int y, int y,
int size) const { int size) const {

View File

@ -64,6 +64,7 @@ public:
void loadUserpic() override; void loadUserpic() override;
void paintUserpic( void paintUserpic(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
int x, int x,
int y, int y,
int size) const override; int size) const override;

View File

@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h" #include "history/history.h"
#include "history/view/history_view_element.h" #include "history/view/history_view_element.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "storage/file_download.h"
#include "facades.h" #include "facades.h"
#include "app.h" #include "app.h"
@ -108,8 +109,7 @@ void PeerClickHandler::onClick(ClickContext context) const {
PeerData::PeerData(not_null<Data::Session*> owner, PeerId id) PeerData::PeerData(not_null<Data::Session*> owner, PeerId id)
: id(id) : id(id)
, _owner(owner) , _owner(owner) {
, _userpicEmpty(createEmptyUserpic()) {
_nameText.setText(st::msgNameStyle, QString(), Ui::NameTextOptions()); _nameText.setText(st::msgNameStyle, QString(), Ui::NameTextOptions());
} }
@ -145,7 +145,8 @@ void PeerData::updateNameDelayed(
} }
name = newName; name = newName;
_nameText.setText(st::msgNameStyle, name, Ui::NameTextOptions()); _nameText.setText(st::msgNameStyle, name, Ui::NameTextOptions());
refreshEmptyUserpic(); _userpicEmpty = nullptr;
Notify::PeerUpdate update(this); Notify::PeerUpdate update(this);
if (nameVersion++ > 1) { if (nameVersion++ > 1) {
update.flags |= UpdateFlag::NameChanged; update.flags |= UpdateFlag::NameChanged;
@ -175,28 +176,22 @@ void PeerData::updateNameDelayed(
} }
} }
std::unique_ptr<Ui::EmptyUserpic> PeerData::createEmptyUserpic() const { not_null<Ui::EmptyUserpic*> PeerData::ensureEmptyUserpic() const {
return std::make_unique<Ui::EmptyUserpic>( if (!_userpicEmpty) {
_userpicEmpty = std::make_unique<Ui::EmptyUserpic>(
Data::PeerUserpicColor(id), Data::PeerUserpicColor(id),
name); name);
} }
return _userpicEmpty.get();
void PeerData::refreshEmptyUserpic() const {
_userpicEmpty = useEmptyUserpic() ? createEmptyUserpic() : nullptr;
} }
ClickHandlerPtr PeerData::createOpenLink() { ClickHandlerPtr PeerData::createOpenLink() {
return std::make_shared<PeerClickHandler>(this); return std::make_shared<PeerClickHandler>(this);
} }
void PeerData::setUserpic( void PeerData::setUserpic(PhotoId photoId, const ImageLocation &location) {
PhotoId photoId,
const StorageImageLocation &location,
ImagePtr userpic) {
_userpicPhotoId = photoId; _userpicPhotoId = photoId;
_userpic = userpic; _userpic.set(&session(), ImageWithLocation{ .location = location });
_userpicLocation = location;
refreshEmptyUserpic();
} }
void PeerData::setUserpicPhoto(const MTPPhoto &data) { void PeerData::setUserpicPhoto(const MTPPhoto &data) {
@ -213,80 +208,109 @@ void PeerData::setUserpicPhoto(const MTPPhoto &data) {
} }
} }
ImagePtr PeerData::currentUserpic() const { Image *PeerData::currentUserpic(
if (_userpic) { std::shared_ptr<Data::CloudImageView> &view) const {
_userpic->load(userpicOrigin()); if (!_userpic.isCurrentView(view)) {
if (_userpic->loaded()) { view = _userpic.createView();
if (!useEmptyUserpic()) { _userpic.load(&session(), userpicOrigin());
}
const auto image = view ? view->image() : nullptr;
if (image) {
_userpicEmpty = nullptr; _userpicEmpty = nullptr;
} }
return _userpic; return image;
}
}
if (!_userpicEmpty) {
refreshEmptyUserpic();
}
return ImagePtr();
} }
void PeerData::paintUserpic(Painter &p, int x, int y, int size) const { void PeerData::paintUserpic(
if (auto userpic = currentUserpic()) { Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
int x,
int y,
int size) const {
if (const auto userpic = currentUserpic(view)) {
p.drawPixmap(x, y, userpic->pixCircled(userpicOrigin(), size, size)); p.drawPixmap(x, y, userpic->pixCircled(userpicOrigin(), size, size));
} else { } else {
_userpicEmpty->paint(p, x, y, x + size + x, size); ensureEmptyUserpic()->paint(p, x, y, x + size + x, size);
} }
} }
void PeerData::paintUserpicRounded(Painter &p, int x, int y, int size) const { void PeerData::paintUserpicRounded(
if (auto userpic = currentUserpic()) { Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
int x,
int y,
int size) const {
if (const auto userpic = currentUserpic(view)) {
p.drawPixmap(x, y, userpic->pixRounded(userpicOrigin(), size, size, ImageRoundRadius::Small)); p.drawPixmap(x, y, userpic->pixRounded(userpicOrigin(), size, size, ImageRoundRadius::Small));
} else { } else {
_userpicEmpty->paintRounded(p, x, y, x + size + x, size); ensureEmptyUserpic()->paintRounded(p, x, y, x + size + x, size);
} }
} }
void PeerData::paintUserpicSquare(Painter &p, int x, int y, int size) const { void PeerData::paintUserpicSquare(
if (auto userpic = currentUserpic()) { Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
int x,
int y,
int size) const {
if (const auto userpic = currentUserpic(view)) {
p.drawPixmap(x, y, userpic->pix(userpicOrigin(), size, size)); p.drawPixmap(x, y, userpic->pix(userpicOrigin(), size, size));
} else { } else {
_userpicEmpty->paintSquare(p, x, y, x + size + x, size); ensureEmptyUserpic()->paintSquare(p, x, y, x + size + x, size);
} }
} }
void PeerData::loadUserpic() { void PeerData::loadUserpic() {
_userpic->load(userpicOrigin()); _userpic.load(&session(), userpicOrigin());
} }
bool PeerData::userpicLoaded() const { bool PeerData::hasUserpic() const {
return _userpic->loaded(); return !_userpic.empty();
} }
bool PeerData::useEmptyUserpic() const { std::shared_ptr<Data::CloudImageView> PeerData::activeUserpicView() {
return !_userpicLocation.valid() return _userpic.empty() ? nullptr : _userpic.activeView();
|| !_userpic
|| !_userpic->loaded();
} }
InMemoryKey PeerData::userpicUniqueKey() const { std::shared_ptr<Data::CloudImageView> PeerData::createUserpicView() {
if (useEmptyUserpic()) { if (_userpic.empty()) {
if (!_userpicEmpty) { return nullptr;
refreshEmptyUserpic();
} }
return _userpicEmpty->uniqueKey(); auto result = _userpic.createView();
} _userpic.load(&session(), userpicPhotoOrigin());
return inMemoryKey(_userpicLocation); return result;
} }
void PeerData::saveUserpic(const QString &path, int size) const { bool PeerData::useEmptyUserpic(
genUserpic(size).save(path, "PNG"); std::shared_ptr<Data::CloudImageView> &view) const {
return !currentUserpic(view);
} }
void PeerData::saveUserpicRounded(const QString &path, int size) const { InMemoryKey PeerData::userpicUniqueKey(
genUserpicRounded(size).save(path, "PNG"); std::shared_ptr<Data::CloudImageView> &view) const {
return useEmptyUserpic(view)
? ensureEmptyUserpic()->uniqueKey()
: inMemoryKey(_userpic.location());
} }
QPixmap PeerData::genUserpic(int size) const { void PeerData::saveUserpic(
if (auto userpic = currentUserpic()) { std::shared_ptr<Data::CloudImageView> &view,
const QString &path,
int size) const {
genUserpic(view, size).save(path, "PNG");
}
void PeerData::saveUserpicRounded(
std::shared_ptr<Data::CloudImageView> &view,
const QString &path,
int size) const {
genUserpicRounded(view, size).save(path, "PNG");
}
QPixmap PeerData::genUserpic(
std::shared_ptr<Data::CloudImageView> &view,
int size) const {
if (const auto userpic = currentUserpic(view)) {
return userpic->pixCircled(userpicOrigin(), size, size); return userpic->pixCircled(userpicOrigin(), size, size);
} }
auto result = QImage(QSize(size, size) * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); auto result = QImage(QSize(size, size) * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
@ -294,13 +318,15 @@ QPixmap PeerData::genUserpic(int size) const {
result.fill(Qt::transparent); result.fill(Qt::transparent);
{ {
Painter p(&result); Painter p(&result);
paintUserpic(p, 0, 0, size); paintUserpic(p, view, 0, 0, size);
} }
return App::pixmapFromImageInPlace(std::move(result)); return App::pixmapFromImageInPlace(std::move(result));
} }
QPixmap PeerData::genUserpicRounded(int size) const { QPixmap PeerData::genUserpicRounded(
if (auto userpic = currentUserpic()) { std::shared_ptr<Data::CloudImageView> &view,
int size) const {
if (auto userpic = currentUserpic(view)) {
return userpic->pixRounded(userpicOrigin(), size, size, ImageRoundRadius::Small); return userpic->pixRounded(userpicOrigin(), size, size, ImageRoundRadius::Small);
} }
auto result = QImage(QSize(size, size) * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); auto result = QImage(QSize(size, size) * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
@ -308,7 +334,7 @@ QPixmap PeerData::genUserpicRounded(int size) const {
result.fill(Qt::transparent); result.fill(Qt::transparent);
{ {
Painter p(&result); Painter p(&result);
paintUserpicRounded(p, 0, 0, size); paintUserpicRounded(p, view, 0, 0, size);
} }
return App::pixmapFromImageInPlace(std::move(result)); return App::pixmapFromImageInPlace(std::move(result));
} }
@ -327,49 +353,42 @@ void PeerData::updateUserpic(
PhotoId photoId, PhotoId photoId,
MTP::DcId dcId, MTP::DcId dcId,
const MTPFileLocation &location) { const MTPFileLocation &location) {
const auto size = kUserpicSize; setUserpicChecked(photoId, location.match([&](
const auto loc = location.match([&](
const MTPDfileLocationToBeDeprecated &deprecated) { const MTPDfileLocationToBeDeprecated &deprecated) {
return StorageImageLocation( return ImageLocation(
StorageFileLocation( { StorageFileLocation(
dcId, dcId,
isSelf() ? peerToUser(id) : 0, isSelf() ? peerToUser(id) : 0,
MTP_inputPeerPhotoFileLocation( MTP_inputPeerPhotoFileLocation(
MTP_flags(0), MTP_flags(0),
input, input,
deprecated.vvolume_id(), deprecated.vvolume_id(),
deprecated.vlocal_id())), deprecated.vlocal_id())) },
size, kUserpicSize,
size); kUserpicSize);
}); }));
setUserpicChecked(photoId, loc, Images::Create(loc));
} }
void PeerData::clearUserpic() { void PeerData::clearUserpic() {
const auto photoId = PhotoId(0); //const auto photo = [&] { // #TODO optimize
const auto loc = StorageImageLocation(); // if (isNotificationsUser()) {
const auto photo = [&] { // auto image = Core::App().logoNoMargin().scaledToWidth(
if (isNotificationsUser()) { // kUserpicSize,
auto image = Core::App().logoNoMargin().scaledToWidth( // Qt::SmoothTransformation);
kUserpicSize, // return _userpic
Qt::SmoothTransformation); // ? _userpic
return _userpic // : Images::Create(std::move(image), "PNG");
? _userpic // }
: Images::Create(std::move(image), "PNG"); // return ImagePtr();
} //}();
return ImagePtr(); setUserpicChecked(PhotoId(), ImageLocation());
}();
setUserpicChecked(photoId, loc, photo);
} }
void PeerData::setUserpicChecked( void PeerData::setUserpicChecked(
PhotoId photoId, PhotoId photoId,
const StorageImageLocation &location, const ImageLocation &location) {
ImagePtr userpic) { if (_userpicPhotoId != photoId || _userpic.location() != location) {
if (_userpicPhotoId != photoId setUserpic(photoId, location);
|| _userpic.get() != userpic.get()
|| _userpicLocation != location) {
setUserpic(photoId, location, userpic);
Notify::peerUpdatedDelayed(this, UpdateFlag::PhotoChanged); Notify::peerUpdatedDelayed(this, UpdateFlag::PhotoChanged);
//if (const auto channel = asChannel()) { // #feed //if (const auto channel = asChannel()) { // #feed
// if (const auto feed = channel->feed()) { // if (const auto feed = channel->feed()) {

View File

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_types.h" #include "data/data_types.h"
#include "data/data_flags.h" #include "data/data_flags.h"
#include "data/data_notify_settings.h" #include "data/data_notify_settings.h"
#include "data/data_cloud_file.h"
class PeerData; class PeerData;
class UserData; class UserData;
@ -43,6 +44,8 @@ using ChatRestrictions = MTPDchatBannedRights::Flags;
namespace Data { namespace Data {
class CloudImageView;
class RestrictionCheckResult { class RestrictionCheckResult {
public: public:
[[nodiscard]] static RestrictionCheckResult Allowed() { [[nodiscard]] static RestrictionCheckResult Allowed() {
@ -234,44 +237,59 @@ public:
return _nameFirstLetters; return _nameFirstLetters;
} }
void setUserpic( void setUserpic(PhotoId photoId, const ImageLocation &location);
PhotoId photoId,
const StorageImageLocation &location,
ImagePtr userpic);
void setUserpicPhoto(const MTPPhoto &data); void setUserpicPhoto(const MTPPhoto &data);
void paintUserpic( void paintUserpic(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
int x, int x,
int y, int y,
int size) const; int size) const;
void paintUserpicLeft( void paintUserpicLeft(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
int x, int x,
int y, int y,
int w, int w,
int size) const { int size) const {
paintUserpic(p, rtl() ? (w - x - size) : x, y, size); paintUserpic(p, view, rtl() ? (w - x - size) : x, y, size);
} }
void paintUserpicRounded( void paintUserpicRounded(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
int x, int x,
int y, int y,
int size) const; int size) const;
void paintUserpicSquare( void paintUserpicSquare(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
int x, int x,
int y, int y,
int size) const; int size) const;
void loadUserpic(); void loadUserpic();
[[nodiscard]] bool userpicLoaded() const; [[nodiscard]] bool hasUserpic() const;
[[nodiscard]] bool useEmptyUserpic() const; [[nodiscard]] std::shared_ptr<Data::CloudImageView> activeUserpicView();
[[nodiscard]] InMemoryKey userpicUniqueKey() const; [[nodiscard]] std::shared_ptr<Data::CloudImageView> createUserpicView();
void saveUserpic(const QString &path, int size) const; [[nodiscard]] bool useEmptyUserpic(
void saveUserpicRounded(const QString &path, int size) const; std::shared_ptr<Data::CloudImageView> &view) const;
[[nodiscard]] QPixmap genUserpic(int size) const; [[nodiscard]] InMemoryKey userpicUniqueKey(
[[nodiscard]] QPixmap genUserpicRounded(int size) const; std::shared_ptr<Data::CloudImageView> &view) const;
[[nodiscard]] StorageImageLocation userpicLocation() const { void saveUserpic(
return _userpicLocation; std::shared_ptr<Data::CloudImageView> &view,
const QString &path,
int size) const;
void saveUserpicRounded(
std::shared_ptr<Data::CloudImageView> &view,
const QString &path,
int size) const;
[[nodiscard]] QPixmap genUserpic(
std::shared_ptr<Data::CloudImageView> &view,
int size) const;
[[nodiscard]] QPixmap genUserpicRounded(
std::shared_ptr<Data::CloudImageView> &view,
int size) const;
[[nodiscard]] ImageLocation userpicLocation() const {
return _userpic.location();
} }
[[nodiscard]] bool userpicPhotoUnknown() const { [[nodiscard]] bool userpicPhotoUnknown() const {
return (_userpicPhotoId == kUnknownPhotoId); return (_userpicPhotoId == kUnknownPhotoId);
@ -294,7 +312,8 @@ public:
return _openLink; return _openLink;
} }
[[nodiscard]] ImagePtr currentUserpic() const; [[nodiscard]] Image *currentUserpic(
std::shared_ptr<Data::CloudImageView> &view) const;
[[nodiscard]] bool canPinMessages() const; [[nodiscard]] bool canPinMessages() const;
[[nodiscard]] bool canEditMessagesIndefinitely() const; [[nodiscard]] bool canEditMessagesIndefinitely() const;
@ -356,24 +375,19 @@ protected:
private: private:
void fillNames(); void fillNames();
std::unique_ptr<Ui::EmptyUserpic> createEmptyUserpic() const; [[nodiscard]] not_null<Ui::EmptyUserpic*> ensureEmptyUserpic() const;
void refreshEmptyUserpic() const;
[[nodiscard]] virtual auto unavailableReasons() const [[nodiscard]] virtual auto unavailableReasons() const
-> const std::vector<Data::UnavailableReason> &; -> const std::vector<Data::UnavailableReason> &;
void setUserpicChecked( void setUserpicChecked(PhotoId photoId, const ImageLocation &location);
PhotoId photoId,
const StorageImageLocation &location,
ImagePtr userpic);
static constexpr auto kUnknownPhotoId = PhotoId(0xFFFFFFFFFFFFFFFFULL); static constexpr auto kUnknownPhotoId = PhotoId(0xFFFFFFFFFFFFFFFFULL);
const not_null<Data::Session*> _owner; const not_null<Data::Session*> _owner;
ImagePtr _userpic; mutable Data::CloudImage _userpic;
PhotoId _userpicPhotoId = kUnknownPhotoId; PhotoId _userpicPhotoId = kUnknownPhotoId;
mutable std::unique_ptr<Ui::EmptyUserpic> _userpicEmpty; mutable std::unique_ptr<Ui::EmptyUserpic> _userpicEmpty;
StorageImageLocation _userpicLocation;
Ui::Text::String _nameText; Ui::Text::String _nameText;
Data::NotifySettings _notify; Data::NotifySettings _notify;

View File

@ -18,6 +18,7 @@ class Session;
namespace Data { namespace Data {
class Session; class Session;
class Folder; class Folder;
class CloudImageView;
} // namespace Data } // namespace Data
namespace Dialogs { namespace Dialogs {
@ -158,16 +159,18 @@ public:
virtual void loadUserpic() = 0; virtual void loadUserpic() = 0;
virtual void paintUserpic( virtual void paintUserpic(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
int x, int x,
int y, int y,
int size) const = 0; int size) const = 0;
void paintUserpicLeft( void paintUserpicLeft(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
int x, int x,
int y, int y,
int w, int w,
int size) const { int size) const {
paintUserpic(p, rtl() ? (w - x - size) : x, y, size); paintUserpic(p, view, rtl() ? (w - x - size) : x, y, size);
} }
TimeId chatListTimeId() const { TimeId chatListTimeId() const {

View File

@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_peer_values.h" #include "data/data_peer_values.h"
#include "data/data_histories.h" #include "data/data_histories.h"
#include "data/data_chat_filters.h" #include "data/data_chat_filters.h"
#include "data/data_cloud_file.h"
#include "base/unixtime.h" #include "base/unixtime.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "mainwindow.h" #include "mainwindow.h"
@ -734,7 +735,7 @@ void InnerWidget::paintPeerSearchResult(
auto peer = result->peer; auto peer = result->peer;
auto userpicPeer = (peer->migrateTo() ? peer->migrateTo() : peer); auto userpicPeer = (peer->migrateTo() ? peer->migrateTo() : peer);
userpicPeer->paintUserpicLeft(p, st::dialogsPadding.x(), st::dialogsPadding.y(), width(), st::dialogsPhotoSize); userpicPeer->paintUserpicLeft(p, result->row.userpicView(), st::dialogsPadding.x(), st::dialogsPadding.y(), width(), st::dialogsPhotoSize);
auto nameleft = st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPhotoPadding; auto nameleft = st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPhotoPadding;
auto namewidth = fullWidth - nameleft - st::dialogsPadding.x(); auto namewidth = fullWidth - nameleft - st::dialogsPadding.x();
@ -810,7 +811,7 @@ void InnerWidget::paintSearchInChat(Painter &p) const {
if (peer->isSelf()) { if (peer->isSelf()) {
paintSearchInSaved(p, top, _searchInChatText); paintSearchInSaved(p, top, _searchInChatText);
} else { } else {
paintSearchInPeer(p, peer, top, _searchInChatText); paintSearchInPeer(p, peer, _searchInChatUserpic, top, _searchInChatText);
} }
//} else if (const auto feed = _searchInChat.feed()) { // #feed //} else if (const auto feed = _searchInChat.feed()) { // #feed
// paintSearchInFeed(p, feed, top, fullWidth, _searchInChatText); // paintSearchInFeed(p, feed, top, fullWidth, _searchInChatText);
@ -821,7 +822,7 @@ void InnerWidget::paintSearchInChat(Painter &p) const {
top += st::dialogsSearchInHeight + st::lineWidth; top += st::dialogsSearchInHeight + st::lineWidth;
p.setPen(st::dialogsTextFg); p.setPen(st::dialogsTextFg);
p.setTextPalette(st::dialogsSearchFromPalette); p.setTextPalette(st::dialogsSearchFromPalette);
paintSearchInPeer(p, from, top, _searchFromUserText); paintSearchInPeer(p, from, _searchFromUserUserpic, top, _searchFromUserText);
p.restoreTextPalette(); p.restoreTextPalette();
} }
} }
@ -866,10 +867,11 @@ void InnerWidget::paintSearchInFilter(
void InnerWidget::paintSearchInPeer( void InnerWidget::paintSearchInPeer(
Painter &p, Painter &p,
not_null<PeerData*> peer, not_null<PeerData*> peer,
std::shared_ptr<Data::CloudImageView> &userpic,
int top, int top,
const Ui::Text::String &text) const { const Ui::Text::String &text) const {
const auto paintUserpic = [&](Painter &p, int x, int y, int size) { const auto paintUserpic = [&](Painter &p, int x, int y, int size) {
peer->paintUserpicLeft(p, x, y, width(), size); peer->paintUserpicLeft(p, userpic, x, y, width(), size);
}; };
const auto icon = Layout::ChatTypeIcon(peer, false, false); const auto icon = Layout::ChatTypeIcon(peer, false, false);
paintSearchInFilter(p, paintUserpic, top, icon, text); paintSearchInFilter(p, paintUserpic, top, icon, text);
@ -2334,9 +2336,18 @@ void InnerWidget::searchInChat(Key key, UserData *from) {
} }
if (_searchFromUser) { if (_searchFromUser) {
_cancelSearchFromUser->show(); _cancelSearchFromUser->show();
_searchFromUserUserpic = _searchFromUser->createUserpicView();
} else { } else {
_cancelSearchFromUser->hide(); _cancelSearchFromUser->hide();
_searchFromUserUserpic = nullptr;
} }
if (const auto peer = _searchInChat.peer()) {
_searchInChatUserpic = peer->createUserpicView();
} else {
_searchInChatUserpic = nullptr;
}
_controller->dialogsListDisplayForced().set( _controller->dialogsListDisplayForced().set(
_searchInChat || !_filter.isEmpty(), _searchInChat || !_filter.isEmpty(),
true); true);

View File

@ -33,6 +33,10 @@ namespace Notify {
struct PeerUpdate; struct PeerUpdate;
} // namespace Notify } // namespace Notify
namespace Data {
class CloudImageView;
} // namespace Data
namespace Dialogs { namespace Dialogs {
class Row; class Row;
@ -283,6 +287,7 @@ private:
void paintSearchInPeer( void paintSearchInPeer(
Painter &p, Painter &p,
not_null<PeerData*> peer, not_null<PeerData*> peer,
std::shared_ptr<Data::CloudImageView> &userpic,
int top, int top,
const Ui::Text::String &text) const; const Ui::Text::String &text) const;
void paintSearchInSaved( void paintSearchInSaved(
@ -394,6 +399,8 @@ private:
Key _searchInChat; Key _searchInChat;
History *_searchInMigrated = nullptr; History *_searchInMigrated = nullptr;
UserData *_searchFromUser = nullptr; UserData *_searchFromUser = nullptr;
mutable std::shared_ptr<Data::CloudImageView> _searchInChatUserpic;
mutable std::shared_ptr<Data::CloudImageView> _searchFromUserUserpic;
Ui::Text::String _searchInChatText; Ui::Text::String _searchInChatText;
Ui::Text::String _searchFromUserText; Ui::Text::String _searchFromUserText;
RowDescriptor _menuRow; RowDescriptor _menuRow;

View File

@ -273,6 +273,7 @@ void paintRow(
} else { } else {
entry->paintUserpicLeft( entry->paintUserpicLeft(
p, p,
row->userpicView(),
st::dialogsPadding.x(), st::dialogsPadding.x(),
st::dialogsPadding.y(), st::dialogsPadding.y(),
fullWidth, fullWidth,
@ -946,6 +947,7 @@ void PaintCollapsedRow(
} else { } else {
folder->paintUserpicLeft( folder->paintUserpicLeft(
p, p,
row.userpicView(),
(fullWidth - st::dialogsUnreadHeight) / 2, (fullWidth - st::dialogsUnreadHeight) / 2,
unreadTop, unreadTop,
fullWidth, fullWidth,

View File

@ -138,12 +138,14 @@ void BasicRow::ensureOnlineUserpic() const {
void BasicRow::PaintOnlineFrame( void BasicRow::PaintOnlineFrame(
not_null<OnlineUserpic*> data, not_null<OnlineUserpic*> data,
not_null<PeerData*> peer) { not_null<PeerData*> peer,
std::shared_ptr<Data::CloudImageView> &view) {
data->frame.fill(Qt::transparent); data->frame.fill(Qt::transparent);
Painter q(&data->frame); Painter q(&data->frame);
peer->paintUserpic( peer->paintUserpic(
q, q,
view,
0, 0,
0, 0,
st::dialogsPhotoSize); st::dialogsPhotoSize);
@ -185,6 +187,7 @@ void BasicRow::paintUserpic(
if (!allowOnline || online == 0.) { if (!allowOnline || online == 0.) {
peer->paintUserpicLeft( peer->paintUserpicLeft(
p, p,
_userpic,
st::dialogsPadding.x(), st::dialogsPadding.x(),
st::dialogsPadding.y(), st::dialogsPadding.y(),
fullWidth, fullWidth,
@ -202,14 +205,14 @@ void BasicRow::paintUserpic(
QImage::Format_ARGB32_Premultiplied); QImage::Format_ARGB32_Premultiplied);
_onlineUserpic->frame.setDevicePixelRatio(cRetinaFactor()); _onlineUserpic->frame.setDevicePixelRatio(cRetinaFactor());
} }
const auto key = peer->userpicUniqueKey(); const auto key = peer->userpicUniqueKey(_userpic);
if (_onlineUserpic->online != online if (_onlineUserpic->online != online
|| _onlineUserpic->key != key || _onlineUserpic->key != key
|| _onlineUserpic->active != active) { || _onlineUserpic->active != active) {
_onlineUserpic->online = online; _onlineUserpic->online = online;
_onlineUserpic->key = key; _onlineUserpic->key = key;
_onlineUserpic->active = active; _onlineUserpic->active = active;
PaintOnlineFrame(_onlineUserpic.get(), peer); PaintOnlineFrame(_onlineUserpic.get(), peer, _userpic);
} }
p.drawImage(st::dialogsPadding, _onlineUserpic->frame); p.drawImage(st::dialogsPadding, _onlineUserpic->frame);
} }

View File

@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class History; class History;
class HistoryItem; class HistoryItem;
namespace Data {
class CloudImageView;
} // namespace Data
namespace Ui { namespace Ui {
class RippleAnimation; class RippleAnimation;
} // namespace Ui } // namespace Ui
@ -48,6 +52,10 @@ public:
int outerWidth, int outerWidth,
const QColor *colorOverride = nullptr) const; const QColor *colorOverride = nullptr) const;
std::shared_ptr<Data::CloudImageView> &userpicView() const {
return _userpic;
}
private: private:
struct OnlineUserpic { struct OnlineUserpic {
InMemoryKey key; InMemoryKey key;
@ -60,8 +68,10 @@ private:
void ensureOnlineUserpic() const; void ensureOnlineUserpic() const;
static void PaintOnlineFrame( static void PaintOnlineFrame(
not_null<OnlineUserpic*> data, not_null<OnlineUserpic*> data,
not_null<PeerData*> peer); not_null<PeerData*> peer,
std::shared_ptr<Data::CloudImageView> &view);
mutable std::shared_ptr<Data::CloudImageView> _userpic;
mutable std::unique_ptr<Ui::RippleAnimation> _ripple; mutable std::unique_ptr<Ui::RippleAnimation> _ripple;
mutable std::unique_ptr<OnlineUserpic> _onlineUserpic; mutable std::unique_ptr<OnlineUserpic> _onlineUserpic;
mutable bool _online = false; mutable bool _online = false;

View File

@ -17,6 +17,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_layers.h" #include "styles/style_layers.h"
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
namespace Data {
class CloudImageView;
} // namespace Data
namespace AdminLog { namespace AdminLog {
namespace { namespace {
@ -59,7 +63,8 @@ private:
QRect _checkRect; QRect _checkRect;
not_null<UserData*> _user; const not_null<UserData*> _user;
std::shared_ptr<Data::CloudImageView> _userpic;
QString _statusText; QString _statusText;
bool _statusOnline = false; bool _statusOnline = false;
@ -113,7 +118,7 @@ void UserCheckbox::paintEvent(QPaintEvent *e) {
auto userpicLeft = _checkRect.x() + _checkRect.width() + st::adminLogFilterUserpicLeft; auto userpicLeft = _checkRect.x() + _checkRect.width() + st::adminLogFilterUserpicLeft;
auto userpicTop = 0; auto userpicTop = 0;
_user->paintUserpicLeft(p, userpicLeft, userpicTop, width(), st::contactsPhotoSize); _user->paintUserpicLeft(p, _userpic, userpicLeft, userpicTop, width(), st::contactsPhotoSize);
auto nameLeft = userpicLeft + st::contactsPhotoSize + st::contactsPadding.left(); auto nameLeft = userpicLeft + st::contactsPhotoSize + st::contactsPadding.left();
auto nameTop = userpicTop + st::contactsNameTop; auto nameTop = userpicTop + st::contactsNameTop;

View File

@ -43,6 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_media_types.h" #include "data/data_media_types.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_cloud_file.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "facades.h" #include "facades.h"
@ -59,6 +60,7 @@ constexpr auto kMaxChannelAdmins = 200;
constexpr auto kScrollDateHideTimeout = 1000; constexpr auto kScrollDateHideTimeout = 1000;
constexpr auto kEventsFirstPage = 20; constexpr auto kEventsFirstPage = 20;
constexpr auto kEventsPerPage = 50; constexpr auto kEventsPerPage = 50;
constexpr auto kClearUserpicsAfter = 50;
} // namespace } // namespace
@ -311,6 +313,11 @@ void InnerWidget::visibleTopBottomUpdated(
_visibleTop = visibleTop; _visibleTop = visibleTop;
_visibleBottom = visibleBottom; _visibleBottom = visibleBottom;
// Unload userpics.
if (_userpics.size() > kClearUserpicsAfter) {
_userpicsCache = std::move(_userpics);
}
updateVisibleTopItem(); updateVisibleTopItem();
checkPreloadMore(); checkPreloadMore();
if (scrolledUp) { if (scrolledUp) {
@ -813,6 +820,10 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
return; return;
} }
const auto guard = gsl::finally([&] {
_userpicsCache.clear();
});
Painter p(this); Painter p(this);
auto ms = crl::now(); auto ms = crl::now();
@ -855,7 +866,14 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
const auto message = view->data()->toHistoryMessage(); const auto message = view->data()->toHistoryMessage();
Assert(message != nullptr); Assert(message != nullptr);
message->from()->paintUserpicLeft(p, st::historyPhotoLeft, userpicTop, view->width(), st::msgPhotoSize); const auto from = message->from();
from->paintUserpicLeft(
p,
_userpics[from],
st::historyPhotoLeft,
userpicTop,
view->width(),
st::msgPhotoSize);
} }
return true; return true;
}); });

View File

@ -16,6 +16,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/sender.h" #include "mtproto/sender.h"
#include "base/timer.h" #include "base/timer.h"
namespace Data {
class CloudImageView;
} // namespace Data
namespace Main { namespace Main {
class Session; class Session;
} // namespace Main } // namespace Main
@ -230,6 +234,9 @@ private:
std::set<uint64> _eventIds; std::set<uint64> _eventIds;
std::map<not_null<const HistoryItem*>, not_null<Element*>> _itemsByData; std::map<not_null<const HistoryItem*>, not_null<Element*>> _itemsByData;
base::flat_set<FullMsgId> _animatedStickersPlayed; base::flat_set<FullMsgId> _animatedStickersPlayed;
base::flat_map<
not_null<PeerData*>,
std::shared_ptr<Data::CloudImageView>> _userpics, _userpicsCache;
int _itemsTop = 0; int _itemsTop = 0;
int _itemsWidth = 0; int _itemsWidth = 0;
int _itemsHeight = 0; int _itemsHeight = 0;

View File

@ -2218,10 +2218,11 @@ void History::loadUserpic() {
void History::paintUserpic( void History::paintUserpic(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
int x, int x,
int y, int y,
int size) const { int size) const {
peer->paintUserpic(p, x, y, size); peer->paintUserpic(p, view, x, y, size);
} }
void History::startBuildingFrontBlock(int expectedItemsCount) { void History::startBuildingFrontBlock(int expectedItemsCount) {

View File

@ -356,6 +356,7 @@ public:
void loadUserpic() override; void loadUserpic() override;
void paintUserpic( void paintUserpic(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
int x, int x,
int y, int y,
int size) const override; int size) const override;

View File

@ -68,6 +68,7 @@ namespace {
constexpr auto kScrollDateHideTimeout = 1000; constexpr auto kScrollDateHideTimeout = 1000;
constexpr auto kUnloadHeavyPartsPages = 2; constexpr auto kUnloadHeavyPartsPages = 2;
constexpr auto kClearUserpicsAfter = 50;
// Helper binary search for an item in a list that is not completely // Helper binary search for an item in a list that is not completely
// above the given top of the visible area or below the given bottom of the visible area // above the given top of the visible area or below the given bottom of the visible area
@ -566,6 +567,10 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
return; return;
} }
const auto guard = gsl::finally([&] {
_userpicsCache.clear();
});
Painter p(this); Painter p(this);
auto clip = e->rect(); auto clip = e->rect();
auto ms = crl::now(); auto ms = crl::now();
@ -731,6 +736,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
if (const auto from = message->displayFrom()) { if (const auto from = message->displayFrom()) {
from->paintUserpicLeft( from->paintUserpicLeft(
p, p,
_userpics[from],
st::historyPhotoLeft, st::historyPhotoLeft,
userpicTop, userpicTop,
width(), width(),
@ -2234,6 +2240,11 @@ void HistoryInner::visibleAreaUpdated(int top, int bottom) {
scrollDateHideByTimer(); scrollDateHideByTimer();
} }
// Unload userpics.
if (_userpics.size() > kClearUserpicsAfter) {
_userpicsCache = std::move(_userpics);
}
// Unload lottie animations. // Unload lottie animations.
const auto pages = kUnloadHeavyPartsPages; const auto pages = kUnloadHeavyPartsPages;
const auto from = _visibleAreaTop - pages * visibleAreaHeight; const auto from = _visibleAreaTop - pages * visibleAreaHeight;

View File

@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Data { namespace Data {
struct Group; struct Group;
class CloudImageView;
} // namespace Data } // namespace Data
namespace HistoryView { namespace HistoryView {
@ -345,6 +346,9 @@ private:
SelectedItems _selected; SelectedItems _selected;
base::flat_set<not_null<const HistoryItem*>> _animatedStickersPlayed; base::flat_set<not_null<const HistoryItem*>> _animatedStickersPlayed;
base::flat_map<
not_null<PeerData*>,
std::shared_ptr<Data::CloudImageView>> _userpics, _userpicsCache;
MouseAction _mouseAction = MouseAction::None; MouseAction _mouseAction = MouseAction::None;
TextSelectType _mouseSelectType = TextSelectType::Letters; TextSelectType _mouseSelectType = TextSelectType::Letters;

View File

@ -380,7 +380,9 @@ HistoryWidget::HistoryWidget(
InitMessageField(controller, _field); InitMessageField(controller, _field);
_fieldAutocomplete->hide(); _fieldAutocomplete->hide();
connect(_fieldAutocomplete, SIGNAL(mentionChosen(UserData*,FieldAutocomplete::ChooseMethod)), this, SLOT(onMentionInsert(UserData*))); connect(_fieldAutocomplete, &FieldAutocomplete::mentionChosen, this, [=](not_null<UserData*> user) {
onMentionInsert(user);
});
connect(_fieldAutocomplete, SIGNAL(hashtagChosen(QString,FieldAutocomplete::ChooseMethod)), this, SLOT(onHashtagOrBotCommandInsert(QString,FieldAutocomplete::ChooseMethod))); connect(_fieldAutocomplete, SIGNAL(hashtagChosen(QString,FieldAutocomplete::ChooseMethod)), this, SLOT(onHashtagOrBotCommandInsert(QString,FieldAutocomplete::ChooseMethod)));
connect(_fieldAutocomplete, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod)), this, SLOT(onHashtagOrBotCommandInsert(QString,FieldAutocomplete::ChooseMethod))); connect(_fieldAutocomplete, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod)), this, SLOT(onHashtagOrBotCommandInsert(QString,FieldAutocomplete::ChooseMethod)));
connect(_fieldAutocomplete, &FieldAutocomplete::stickerChosen, this, [=](not_null<DocumentData*> document) { connect(_fieldAutocomplete, &FieldAutocomplete::stickerChosen, this, [=](not_null<DocumentData*> document) {

View File

@ -50,6 +50,7 @@ constexpr auto kPreloadedScreensCount = 4;
constexpr auto kPreloadIfLessThanScreens = 2; constexpr auto kPreloadIfLessThanScreens = 2;
constexpr auto kPreloadedScreensCountFull constexpr auto kPreloadedScreensCountFull
= kPreloadedScreensCount + 1 + kPreloadedScreensCount; = kPreloadedScreensCount + 1 + kPreloadedScreensCount;
constexpr auto kClearUserpicsAfter = 50;
} // namespace } // namespace
@ -583,6 +584,11 @@ void ListWidget::visibleTopBottomUpdated(
_visibleTop = visibleTop; _visibleTop = visibleTop;
_visibleBottom = visibleBottom; _visibleBottom = visibleBottom;
// Unload userpics.
if (_userpics.size() > kClearUserpicsAfter) {
_userpicsCache = std::move(_userpics);
}
if (initializing) { if (initializing) {
checkUnreadBarCreation(); checkUnreadBarCreation();
} }
@ -1305,6 +1311,10 @@ void ListWidget::paintEvent(QPaintEvent *e) {
return; return;
} }
const auto guard = gsl::finally([&] {
_userpicsCache.clear();
});
Painter p(this); Painter p(this);
auto ms = crl::now(); auto ms = crl::now();
@ -1346,6 +1356,7 @@ void ListWidget::paintEvent(QPaintEvent *e) {
if (const auto from = message->displayFrom()) { if (const auto from = message->displayFrom()) {
from->paintUserpicLeft( from->paintUserpicLeft(
p, p,
_userpics[from],
st::historyPhotoLeft, st::historyPhotoLeft,
userpicTop, userpicTop,
view->width(), view->width(),

View File

@ -29,6 +29,7 @@ class SessionController;
namespace Data { namespace Data {
struct Group; struct Group;
class CloudImageView;
} // namespace Data } // namespace Data
namespace HistoryView { namespace HistoryView {
@ -454,6 +455,9 @@ private:
int _itemsHeight = 0; int _itemsHeight = 0;
int _itemAverageHeight = 0; int _itemAverageHeight = 0;
base::flat_set<FullMsgId> _animatedStickersPlayed; base::flat_set<FullMsgId> _animatedStickersPlayed;
base::flat_map<
not_null<PeerData*>,
std::shared_ptr<Data::CloudImageView>> _userpics, _userpicsCache;
int _minHeight = 0; int _minHeight = 0;
int _visibleTop = 0; int _visibleTop = 0;

View File

@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_media_types.h" #include "data/data_media_types.h"
#include "data/data_cloud_file.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "app.h" #include "app.h"
#include "styles/style_history.h" #include "styles/style_history.h"
@ -165,7 +166,11 @@ void Contact::draw(Painter &p, const QRect &r, TextSelection selection, crl::tim
QRect rthumb(style::rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top() - topMinus, st::msgFileThumbSize, st::msgFileThumbSize, paintw)); QRect rthumb(style::rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top() - topMinus, st::msgFileThumbSize, st::msgFileThumbSize, paintw));
if (_contact) { if (_contact) {
_contact->paintUserpic(p, rthumb.x(), rthumb.y(), st::msgFileThumbSize); const auto was = (_userpic != nullptr);
_contact->paintUserpic(p, _userpic, rthumb.x(), rthumb.y(), st::msgFileThumbSize);
if (!was && _userpic) {
history()->owner().registerHeavyViewPart(_parent);
}
} else { } else {
_photoEmpty->paint(p, st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top() - topMinus, paintw, st::msgFileThumbSize); _photoEmpty->paint(p, st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top() - topMinus, paintw, st::msgFileThumbSize);
} }

View File

@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/media/history_view_media.h" #include "history/view/media/history_view_media.h"
namespace Data {
class CloudImageView;
} // namespace Data
namespace Ui { namespace Ui {
class EmptyUserpic; class EmptyUserpic;
} // namespace Ui } // namespace Ui
@ -55,6 +59,14 @@ public:
// Should be called only by Data::Session. // Should be called only by Data::Session.
void updateSharedContactUserId(UserId userId) override; void updateSharedContactUserId(UserId userId) override;
void unloadHeavyPart() override {
_userpic = nullptr;
}
bool hasHeavyPart() const override {
return (_userpic != nullptr);
}
private: private:
QSize countOptimalSize() override; QSize countOptimalSize() override;
@ -65,6 +77,7 @@ private:
QString _fname, _lname, _phone; QString _fname, _lname, _phone;
Ui::Text::String _name; Ui::Text::String _name;
std::unique_ptr<Ui::EmptyUserpic> _photoEmpty; std::unique_ptr<Ui::EmptyUserpic> _photoEmpty;
mutable std::shared_ptr<Data::CloudImageView> _userpic;
ClickHandlerPtr _linkl; ClickHandlerPtr _linkl;
int _linkw = 0; int _linkw = 0;

View File

@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_cursor_state.h" #include "history/view/history_view_cursor_state.h"
#include "ui/image/image.h" #include "ui/image/image.h"
#include "ui/text_options.h" #include "ui/text_options.h"
#include "data/data_session.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_cloud_file.h" #include "data/data_cloud_file.h"
#include "app.h" #include "app.h"
@ -56,6 +57,7 @@ void Location::ensureMediaCreated() const {
} }
_media = _data->createView(); _media = _data->createView();
_data->load(&history()->session(), _parent->data()->fullId()); _data->load(&history()->session(), _parent->data()->fullId());
history()->owner().registerHeavyViewPart(_parent);
} }
QSize Location::countOptimalSize() { QSize Location::countOptimalSize() {

View File

@ -479,9 +479,17 @@ void Poll::updateRecentVoters() {
auto &&sliced = ranges::view::all( auto &&sliced = ranges::view::all(
_poll->recentVoters _poll->recentVoters
) | ranges::view::take(kShowRecentVotersCount); ) | ranges::view::take(kShowRecentVotersCount);
const auto changed = !ranges::equal(_recentVoters, sliced); const auto changed = !ranges::equal(
_recentVoters,
sliced,
ranges::equal_to(),
&RecentVoter::user);
if (changed) { if (changed) {
_recentVoters = sliced | ranges::to_vector; _recentVoters = ranges::view::all(
sliced
) | ranges::views::transform([](not_null<UserData*> user) {
return RecentVoter{ user };
}) | ranges::to_vector;
} }
} }
@ -858,14 +866,23 @@ void Poll::paintRecentVoters(
? (outbg ? st::msgOutBgSelected : st::msgInBgSelected) ? (outbg ? st::msgOutBgSelected : st::msgInBgSelected)
: (outbg ? st::msgOutBg : st::msgInBg))->p; : (outbg ? st::msgOutBg : st::msgInBg))->p;
pen.setWidth(st::lineWidth); pen.setWidth(st::lineWidth);
for (const auto &recent : _recentVoters) {
recent->paintUserpic(p, x, y, size); auto created = false;
for (auto &recent : _recentVoters) {
const auto was = (recent.userpic != nullptr);
recent.user->paintUserpic(p, recent.userpic, x, y, size);
if (!was && recent.userpic) {
created = true;
}
p.setPen(pen); p.setPen(pen);
p.setBrush(Qt::NoBrush); p.setBrush(Qt::NoBrush);
PainterHighQualityEnabler hq(p); PainterHighQualityEnabler hq(p);
p.drawEllipse(x, y, size, size); p.drawEllipse(x, y, size, size);
x -= st::historyPollRecentVoterSkip; x -= st::historyPollRecentVoterSkip;
} }
if (created) {
history()->owner().registerHeavyViewPart(_parent);
}
} }
void Poll::paintCloseByTimer( void Poll::paintCloseByTimer(
@ -1401,6 +1418,21 @@ void Poll::clickHandlerPressedChanged(
} }
} }
void Poll::unloadHeavyPart() {
for (auto &recent : _recentVoters) {
recent.userpic = nullptr;
}
}
bool Poll::hasHeavyPart() const {
for (auto &recent : _recentVoters) {
if (recent.userpic) {
return true;
}
}
return false;
}
void Poll::toggleRipple(Answer &answer, bool pressed) { void Poll::toggleRipple(Answer &answer, bool pressed) {
if (pressed) { if (pressed) {
const auto outerWidth = width(); const auto outerWidth = width();

View File

@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_poll.h" #include "data/data_poll.h"
#include "base/weak_ptr.h" #include "base/weak_ptr.h"
namespace Data {
class CloudImageView;
} // namespace Data
namespace Ui { namespace Ui {
class RippleAnimation; class RippleAnimation;
class FireworksAnimation; class FireworksAnimation;
@ -54,6 +58,9 @@ public:
const ClickHandlerPtr &handler, const ClickHandlerPtr &handler,
bool pressed) override; bool pressed) override;
void unloadHeavyPart() override;
bool hasHeavyPart() const override;
private: private:
struct AnswerAnimation; struct AnswerAnimation;
struct AnswersAnimation; struct AnswersAnimation;
@ -61,6 +68,11 @@ private:
struct Answer; struct Answer;
struct CloseInformation; struct CloseInformation;
struct RecentVoter {
not_null<UserData*> user;
mutable std::shared_ptr<Data::CloudImageView> userpic;
};
QSize countOptimalSize() override; QSize countOptimalSize() override;
QSize countCurrentSize(int newWidth) override; QSize countCurrentSize(int newWidth) override;
@ -186,7 +198,7 @@ private:
Ui::Text::String _question; Ui::Text::String _question;
Ui::Text::String _subtitle; Ui::Text::String _subtitle;
std::vector<not_null<UserData*>> _recentVoters; std::vector<RecentVoter> _recentVoters;
QImage _recentVotersImage; QImage _recentVotersImage;
std::vector<Answer> _answers; std::vector<Answer> _answers;

View File

@ -2743,13 +2743,12 @@ void OverlayWidget::validatePhotoCurrentImage() {
validatePhotoImage(_photoMedia->image(Data::PhotoSize::Small), true); validatePhotoImage(_photoMedia->image(Data::PhotoSize::Small), true);
validatePhotoImage(_photoMedia->thumbnailInline(), true); validatePhotoImage(_photoMedia->thumbnailInline(), true);
if (_staticContent.isNull() if (_staticContent.isNull()
&& _peer
&& !_msgid && !_msgid
&& _peer->userpicLoaded() && _peer
&& _peer->userpicLocation().file().valid()) { && _peer->hasUserpic()) {
validatePhotoImage( if (const auto view = _peer->activeUserpicView()) {
Images::Create(_peer->userpicLocation()).get(), validatePhotoImage(view->image(), true);
true); }
} }
if (_staticContent.isNull()) { if (_staticContent.isNull()) {
_photoMedia->wanted(Data::PhotoSize::Small, fileOrigin()); _photoMedia->wanted(Data::PhotoSize::Small, fileOrigin());

View File

@ -461,7 +461,9 @@ bool Manager::Private::showNotification(
const QString &msg, const QString &msg,
bool hideNameAndPhoto, bool hideNameAndPhoto,
bool hideReplyButton) { bool hideReplyButton) {
if (!_notificationManager || !_notifier || !_notificationFactory) return false; if (!_notificationManager || !_notifier || !_notificationFactory) {
return false;
}
ComPtr<IXmlDocument> toastXml; ComPtr<IXmlDocument> toastXml;
bool withSubtitle = !subtitle.isEmpty(); bool withSubtitle = !subtitle.isEmpty();
@ -476,9 +478,10 @@ bool Manager::Private::showNotification(
hr = SetAudioSilent(toastXml.Get()); hr = SetAudioSilent(toastXml.Get());
if (!SUCCEEDED(hr)) return false; if (!SUCCEEDED(hr)) return false;
auto view = std::shared_ptr<Data::CloudImageView>(); // #TODO optimize
const auto key = hideNameAndPhoto const auto key = hideNameAndPhoto
? InMemoryKey() ? InMemoryKey()
: peer->userpicUniqueKey(); : peer->userpicUniqueKey(view);
const auto userpicPath = _cachedUserpics.get(key, peer); const auto userpicPath = _cachedUserpics.get(key, peer);
const auto userpicPathWide = QDir::toNativeSeparators(userpicPath).toStdWString(); const auto userpicPathWide = QDir::toNativeSeparators(userpicPath).toStdWString();

View File

@ -32,11 +32,11 @@ using UpdateFlag = Notify::PeerUpdate::Flag;
} // namespace } // namespace
GroupMembersWidget::Member::Member(UserData *user) : Item(user) { GroupMembersWidget::Member::Member(not_null<UserData*> user) : Item(user) {
} }
UserData *GroupMembersWidget::Member::user() const { not_null<UserData*> GroupMembersWidget::Member::user() const {
return static_cast<UserData*>(peer); return static_cast<UserData*>(peer.get());
} }
GroupMembersWidget::GroupMembersWidget( GroupMembersWidget::GroupMembersWidget(

View File

@ -55,8 +55,8 @@ private:
void refreshUserOnline(UserData *user); void refreshUserOnline(UserData *user);
struct Member : public Item { struct Member : public Item {
explicit Member(UserData *user); explicit Member(not_null<UserData*> user);
UserData *user() const; not_null<UserData*> user() const;
TimeId onlineTextTill = 0; TimeId onlineTextTill = 0;
TimeId onlineTill = 0; TimeId onlineTill = 0;

View File

@ -10,13 +10,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/ripple_animation.h" #include "ui/effects/ripple_animation.h"
#include "ui/text_options.h" #include "ui/text_options.h"
#include "data/data_peer.h" #include "data/data_peer.h"
#include "data/data_cloud_file.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "styles/style_profile.h" #include "styles/style_profile.h"
#include "styles/style_widgets.h" #include "styles/style_widgets.h"
namespace Profile { namespace Profile {
PeerListWidget::Item::Item(PeerData *peer) : peer(peer) { PeerListWidget::Item::Item(not_null<PeerData*> peer) : peer(peer) {
} }
PeerListWidget::Item::~Item() = default; PeerListWidget::Item::~Item() = default;
@ -84,7 +85,7 @@ void PeerListWidget::paintItem(Painter &p, int x, int y, Item *item, bool select
} }
int skip = _st.photoPosition.x(); int skip = _st.photoPosition.x();
item->peer->paintUserpicLeft(p, x + _st.photoPosition.x(), y + _st.photoPosition.y(), width(), _st.photoSize); item->peer->paintUserpicLeft(p, item->userpic, x + _st.photoPosition.x(), y + _st.photoPosition.y(), width(), _st.photoSize);
if (item->name.isEmpty()) { if (item->name.isEmpty()) {
item->name.setText( item->name.setText(

View File

@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "profile/profile_block_widget.h" #include "profile/profile_block_widget.h"
namespace Data {
class CloudImageView;
} // namespace Data
namespace Ui { namespace Ui {
class RippleAnimation; class RippleAnimation;
class PopupMenu; class PopupMenu;
@ -29,10 +33,11 @@ public:
PeerListWidget(QWidget *parent, PeerData *peer, const QString &title, const style::PeerListItem &st, const QString &removeText); PeerListWidget(QWidget *parent, PeerData *peer, const QString &title, const style::PeerListItem &st, const QString &removeText);
struct Item { struct Item {
explicit Item(PeerData *peer); explicit Item(not_null<PeerData*> peer);
~Item(); ~Item();
PeerData * const peer; const not_null<PeerData*> peer;
std::shared_ptr<Data::CloudImageView> userpic;
Ui::Text::String name; Ui::Text::String name;
QString statusText; QString statusText;
bool statusHasOnlineColor = false; bool statusHasOnlineColor = false;

View File

@ -127,7 +127,7 @@ std::optional<ImageLocation> readImageLocation(
uint32 peerSize(not_null<PeerData*> peer) { uint32 peerSize(not_null<PeerData*> peer) {
uint32 result = sizeof(quint64) uint32 result = sizeof(quint64)
+ sizeof(quint64) + sizeof(quint64)
+ storageImageLocationSize(peer->userpicLocation()); + imageLocationSize(peer->userpicLocation());
if (peer->isUser()) { if (peer->isUser()) {
UserData *user = peer->asUser(); UserData *user = peer->asUser();
@ -157,7 +157,7 @@ uint32 peerSize(not_null<PeerData*> peer) {
void writePeer(QDataStream &stream, PeerData *peer) { void writePeer(QDataStream &stream, PeerData *peer) {
stream << quint64(peer->id) << quint64(peer->userpicPhotoId()); stream << quint64(peer->id) << quint64(peer->userpicPhotoId());
writeStorageImageLocation(stream, peer->userpicLocation()); writeImageLocation(stream, peer->userpicLocation());
if (const auto user = peer->asUser()) { if (const auto user = peer->asUser()) {
stream stream
<< user->firstName << user->firstName
@ -207,7 +207,7 @@ PeerData *readPeer(int streamAppVersion, QDataStream &stream) {
return nullptr; return nullptr;
} }
const auto userpic = readStorageImageLocation(streamAppVersion, stream); const auto userpic = readImageLocation(streamAppVersion, stream);
auto userpicAccessHash = uint64(0); auto userpicAccessHash = uint64(0);
if (!userpic) { if (!userpic) {
return nullptr; return nullptr;
@ -324,14 +324,13 @@ PeerData *readPeer(int streamAppVersion, QDataStream &stream) {
} }
if (!loaded) { if (!loaded) {
using LocationType = StorageFileLocation::Type; using LocationType = StorageFileLocation::Type;
const auto location = (userpic->valid() const auto location = (userpic->valid() && userpic->isLegacy())
&& userpic->type() == LocationType::Legacy)
? userpic->convertToModern( ? userpic->convertToModern(
LocationType::PeerPhoto, LocationType::PeerPhoto,
result->id, result->id,
userpicAccessHash) userpicAccessHash)
: *userpic; : *userpic;
result->setUserpic(photoId, location, Images::Create(location)); result->setUserpic(photoId, location);
} }
return result; return result;
} }

View File

@ -590,6 +590,49 @@ InMemoryKey inMemoryKey(const StorageFileLocation &location) {
return { key.high, key.low }; return { key.high, key.low };
} }
InMemoryKey inMemoryKey(const WebFileLocation &location) {
auto result = InMemoryKey();
const auto &url = location.url();
const auto sha = hashSha1(url.data(), url.size());
bytes::copy(
bytes::object_as_span(&result),
bytes::make_span(sha).subspan(0, sizeof(result)));
return result;
}
InMemoryKey inMemoryKey(const GeoPointLocation &location) {
return InMemoryKey(
(uint64(std::round(std::abs(location.lat + 360.) * 1000000)) << 32)
| uint64(std::round(std::abs(location.lon + 360.) * 1000000)),
(uint64(location.width) << 32) | uint64(location.height));
}
InMemoryKey inMemoryKey(const PlainUrlLocation &location) {
auto result = InMemoryKey();
const auto &url = location.url;
const auto sha = hashSha1(url.data(), url.size() * sizeof(QChar));
bytes::copy(
bytes::object_as_span(&result),
bytes::make_span(sha).subspan(0, sizeof(result)));
return result;
}
InMemoryKey inMemoryKey(const InMemoryLocation &location) {
auto result = InMemoryKey();
const auto &data = location.bytes;
const auto sha = hashSha1(data.data(), data.size());
bytes::copy(
bytes::object_as_span(&result),
bytes::make_span(sha).subspan(0, sizeof(result)));
return result;
}
InMemoryKey inMemoryKey(const DownloadLocation &location) {
return location.data.match([](const auto &data) {
return inMemoryKey(data);
});
}
StorageImageLocation::StorageImageLocation( StorageImageLocation::StorageImageLocation(
const StorageFileLocation &file, const StorageFileLocation &file,
int width, int width,

View File

@ -96,7 +96,6 @@ public:
[[nodiscard]] static const StorageFileLocation &Invalid(); [[nodiscard]] static const StorageFileLocation &Invalid();
private:
friend bool operator==( friend bool operator==(
const StorageFileLocation &a, const StorageFileLocation &a,
const StorageFileLocation &b); const StorageFileLocation &b);
@ -104,6 +103,7 @@ private:
const StorageFileLocation &a, const StorageFileLocation &a,
const StorageFileLocation &b); const StorageFileLocation &b);
private:
uint16 _dcId = 0; uint16 _dcId = 0;
Type _type = Type::Legacy; Type _type = Type::Legacy;
uint8 _sizeLetter = 0; uint8 _sizeLetter = 0;
@ -203,7 +203,6 @@ public:
return result; return result;
} }
private:
friend inline bool operator==( friend inline bool operator==(
const StorageImageLocation &a, const StorageImageLocation &a,
const StorageImageLocation &b) { const StorageImageLocation &b) {
@ -215,6 +214,7 @@ private:
return (a._file < b._file); return (a._file < b._file);
} }
private:
StorageFileLocation _file; StorageFileLocation _file;
int _width = 0; int _width = 0;
int _height = 0; int _height = 0;
@ -381,6 +381,30 @@ struct PlainUrlLocation {
} }
}; };
inline bool operator!=(
const PlainUrlLocation &a,
const PlainUrlLocation &b) {
return !(a == b);
}
inline bool operator>(
const PlainUrlLocation &a,
const PlainUrlLocation &b) {
return (b < a);
}
inline bool operator<=(
const PlainUrlLocation &a,
const PlainUrlLocation &b) {
return !(b < a);
}
inline bool operator>=(
const PlainUrlLocation &a,
const PlainUrlLocation &b) {
return !(a < b);
}
struct InMemoryLocation { struct InMemoryLocation {
QByteArray bytes; QByteArray bytes;
@ -396,6 +420,30 @@ struct InMemoryLocation {
} }
}; };
inline bool operator!=(
const InMemoryLocation &a,
const InMemoryLocation &b) {
return !(a == b);
}
inline bool operator>(
const InMemoryLocation &a,
const InMemoryLocation &b) {
return (b < a);
}
inline bool operator<=(
const InMemoryLocation &a,
const InMemoryLocation &b) {
return !(b < a);
}
inline bool operator>=(
const InMemoryLocation &a,
const InMemoryLocation &b) {
return !(a < b);
}
class DownloadLocation { class DownloadLocation {
public: public:
base::variant< base::variant<
@ -423,7 +471,6 @@ public:
bool refreshFileReference(const QByteArray &data); bool refreshFileReference(const QByteArray &data);
bool refreshFileReference(const Data::UpdatedFileReferences &updates); bool refreshFileReference(const Data::UpdatedFileReferences &updates);
private:
friend inline bool operator==( friend inline bool operator==(
const DownloadLocation &a, const DownloadLocation &a,
const DownloadLocation &b) { const DownloadLocation &b) {
@ -437,6 +484,30 @@ private:
}; };
inline bool operator!=(
const DownloadLocation &a,
const DownloadLocation &b) {
return !(a == b);
}
inline bool operator>(
const DownloadLocation &a,
const DownloadLocation &b) {
return (b < a);
}
inline bool operator<=(
const DownloadLocation &a,
const DownloadLocation &b) {
return !(b < a);
}
inline bool operator>=(
const DownloadLocation &a,
const DownloadLocation &b) {
return !(a < b);
}
class ImageLocation { class ImageLocation {
public: public:
ImageLocation() = default; ImageLocation() = default;
@ -496,7 +567,6 @@ public:
return result; return result;
} }
private:
friend inline bool operator==( friend inline bool operator==(
const ImageLocation &a, const ImageLocation &a,
const ImageLocation &b) { const ImageLocation &b) {
@ -508,12 +578,37 @@ private:
return (a._file < b._file); return (a._file < b._file);
} }
private:
DownloadLocation _file; DownloadLocation _file;
int _width = 0; int _width = 0;
int _height = 0; int _height = 0;
}; };
inline bool operator!=(
const ImageLocation &a,
const ImageLocation &b) {
return !(a == b);
}
inline bool operator>(
const ImageLocation &a,
const ImageLocation &b) {
return (b < a);
}
inline bool operator<=(
const ImageLocation &a,
const ImageLocation &b) {
return !(b < a);
}
inline bool operator>=(
const ImageLocation &a,
const ImageLocation &b) {
return !(a < b);
}
struct ImageWithLocation { struct ImageWithLocation {
ImageLocation location; ImageLocation location;
int bytesCount = 0; int bytesCount = 0;
@ -543,21 +638,14 @@ inline InMemoryKey inMemoryKey(const StorageImageLocation &location) {
return inMemoryKey(location.file()); return inMemoryKey(location.file());
} }
inline InMemoryKey inMemoryKey(const WebFileLocation &location) { InMemoryKey inMemoryKey(const WebFileLocation &location);
auto result = InMemoryKey(); InMemoryKey inMemoryKey(const GeoPointLocation &location);
const auto &url = location.url(); InMemoryKey inMemoryKey(const PlainUrlLocation &location);
const auto sha = hashSha1(url.data(), url.size()); InMemoryKey inMemoryKey(const InMemoryLocation &location);
bytes::copy( InMemoryKey inMemoryKey(const DownloadLocation &location);
bytes::object_as_span(&result),
bytes::make_span(sha).subspan(0, sizeof(result)));
return result;
}
inline InMemoryKey inMemoryKey(const GeoPointLocation &location) { inline InMemoryKey inMemoryKey(const ImageLocation &location) {
return InMemoryKey( return inMemoryKey(location.file());
(uint64(std::round(std::abs(location.lat + 360.) * 1000000)) << 32)
| uint64(std::round(std::abs(location.lon + 360.) * 1000000)),
(uint64(location.width) << 32) | uint64(location.height));
} }
inline QSize shrinkToKeepAspect(int32 width, int32 height, int32 towidth, int32 toheight) { inline QSize shrinkToKeepAspect(int32 width, int32 height, int32 towidth, int32 toheight) {

View File

@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_folder.h" #include "data/data_folder.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_cloud_file.h"
#include "history/history.h" #include "history/history.h"
#include "core/file_utilities.h" #include "core/file_utilities.h"
#include "core/application.h" #include "core/application.h"
@ -633,15 +634,17 @@ void UserpicButton::setupPeerViewers() {
Notify::PeerUpdateViewer( Notify::PeerUpdateViewer(
_peer, _peer,
Notify::PeerUpdate::Flag::PhotoChanged Notify::PeerUpdate::Flag::PhotoChanged
) | rpl::start_with_next([this] { ) | rpl::start_with_next([=] {
processNewPeerPhoto(); processNewPeerPhoto();
update(); update();
}, lifetime()); }, lifetime());
base::ObservableViewer( base::ObservableViewer(
_peer->session().downloaderTaskFinished() _peer->session().downloaderTaskFinished()
) | rpl::start_with_next([this] { ) | rpl::filter([=] {
if (_waiting && _peer->userpicLoaded()) { return _waiting;
}) | rpl::start_with_next([=] {
if (!_userpicView || _userpicView->image()) {
_waiting = false; _waiting = false;
startNewPhotoShowing(); startNewPhotoShowing();
} }
@ -776,7 +779,8 @@ QPoint UserpicButton::prepareRippleStartPosition() const {
void UserpicButton::processPeerPhoto() { void UserpicButton::processPeerPhoto() {
Expects(_peer != nullptr); Expects(_peer != nullptr);
_waiting = !_peer->userpicLoaded(); _userpicView = _peer->createUserpicView();
_waiting = _userpicView && !_userpicView->image();
if (_waiting) { if (_waiting) {
_peer->loadUserpic(); _peer->loadUserpic();
} }
@ -951,17 +955,17 @@ void UserpicButton::prepareUserpicPixmap() {
p.drawEllipse(0, 0, size, size); p.drawEllipse(0, 0, size, size);
}; };
_userpicHasImage = _peer _userpicHasImage = _peer
? (_peer->currentUserpic() || _role != Role::ChangePhoto) ? (_peer->currentUserpic(_userpicView) || _role != Role::ChangePhoto)
: false; : false;
_userpic = CreateSquarePixmap(size, [&](Painter &p) { _userpic = CreateSquarePixmap(size, [&](Painter &p) {
if (_userpicHasImage) { if (_userpicHasImage) {
_peer->paintUserpic(p, 0, 0, _st.photoSize); _peer->paintUserpic(p, _userpicView, 0, 0, _st.photoSize);
} else { } else {
paintButton(p, _st.changeButton.textBg); paintButton(p, _st.changeButton.textBg);
} }
}); });
_userpicUniqueKey = _userpicHasImage _userpicUniqueKey = _userpicHasImage
? _peer->userpicUniqueKey() ? _peer->userpicUniqueKey(_userpicView)
: InMemoryKey(); : InMemoryKey();
} }
// // #feed // // #feed

View File

@ -15,9 +15,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class PeerData; class PeerData;
namespace Ui { namespace Data {
class InfiniteRadialAnimation; class CloudImageView;
} // namespace Ui } // namespace Data
namespace Window { namespace Window {
class SessionController; class SessionController;
@ -25,6 +25,8 @@ class SessionController;
namespace Ui { namespace Ui {
class InfiniteRadialAnimation;
class HistoryDownButton : public RippleButton { class HistoryDownButton : public RippleButton {
public: public:
HistoryDownButton(QWidget *parent, const style::TwoIconButton &st); HistoryDownButton(QWidget *parent, const style::TwoIconButton &st);
@ -220,6 +222,7 @@ private:
const style::UserpicButton &_st; const style::UserpicButton &_st;
Window::SessionController *_controller = nullptr; Window::SessionController *_controller = nullptr;
PeerData *_peer = nullptr; PeerData *_peer = nullptr;
std::shared_ptr<Data::CloudImageView> _userpicView;
QString _cropTitle; QString _cropTitle;
Role _role = Role::ChangePhoto; Role _role = Role::ChangePhoto;
bool _notShownYet = true; bool _notShownYet = true;

View File

@ -536,9 +536,10 @@ Notification::Notification(
int shift, int shift,
Direction shiftDirection) Direction shiftDirection)
: Widget(manager, startPosition, shift, shiftDirection) : Widget(manager, startPosition, shift, shiftDirection)
, _peer(peer)
, _started(crl::now()) , _started(crl::now())
, _history(history) , _history(history)
, _peer(peer) , _userpicView(_peer->createUserpicView())
, _author(author) , _author(author)
, _item(item) , _item(item)
, _forwardedCount(forwardedCount) , _forwardedCount(forwardedCount)
@ -550,7 +551,7 @@ Notification::Notification(
auto position = computePosition(st::notifyMinHeight); auto position = computePosition(st::notifyMinHeight);
updateGeometry(position.x(), position.y(), st::notifyWidth, st::notifyMinHeight); updateGeometry(position.x(), position.y(), st::notifyWidth, st::notifyMinHeight);
_userpicLoaded = _peer ? _peer->userpicLoaded() : true; _userpicLoaded = (_userpicView->image() != nullptr);
updateNotifyDisplay(); updateNotifyDisplay();
_hideTimer.setSingleShot(true); _hideTimer.setSingleShot(true);
@ -674,7 +675,7 @@ void Notification::actionsOpacityCallback() {
} }
void Notification::updateNotifyDisplay() { void Notification::updateNotifyDisplay() {
if (!_history || !_peer || (!_item && _forwardedCount < 2)) return; if (!_history || (!_item && _forwardedCount < 2)) return;
const auto options = Manager::getNotificationOptions(_item); const auto options = Manager::getNotificationOptions(_item);
_hideReplyButton = options.hideReplyButton; _hideReplyButton = options.hideReplyButton;
@ -696,8 +697,9 @@ void Notification::updateNotifyDisplay() {
Ui::EmptyUserpic::PaintSavedMessages(p, st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), width(), st::notifyPhotoSize); Ui::EmptyUserpic::PaintSavedMessages(p, st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), width(), st::notifyPhotoSize);
_userpicLoaded = true; _userpicLoaded = true;
} else { } else {
_userpicView = _history->peer->createUserpicView();
_history->peer->loadUserpic(); _history->peer->loadUserpic();
_history->peer->paintUserpicLeft(p, st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), width(), st::notifyPhotoSize); _history->peer->paintUserpicLeft(p, _userpicView, st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), width(), st::notifyPhotoSize);
} }
} else { } else {
p.drawPixmap(st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), manager()->hiddenUserpicPlaceholder()); p.drawPixmap(st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), manager()->hiddenUserpicPlaceholder());
@ -792,7 +794,11 @@ void Notification::updateNotifyDisplay() {
} }
void Notification::updatePeerPhoto() { void Notification::updatePeerPhoto() {
if (_userpicLoaded || !_peer || !_peer->userpicLoaded()) { if (_userpicLoaded) {
return;
}
_userpicView = _peer->createUserpicView();
if (_userpicView && !_userpicView->image()) {
return; return;
} }
_userpicLoaded = true; _userpicLoaded = true;
@ -800,9 +806,16 @@ void Notification::updatePeerPhoto() {
auto img = _cache.toImage(); auto img = _cache.toImage();
{ {
Painter p(&img); Painter p(&img);
_peer->paintUserpicLeft(p, st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), width(), st::notifyPhotoSize); _peer->paintUserpicLeft(
p,
_userpicView,
st::notifyPhotoPos.x(),
st::notifyPhotoPos.y(),
width(),
st::notifyPhotoSize);
} }
_cache = App::pixmapFromImageInPlace(std::move(img)); _cache = App::pixmapFromImageInPlace(std::move(img));
_userpicView = nullptr;
update(); update();
} }

View File

@ -16,6 +16,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QTimer> #include <QtCore/QTimer>
namespace Data {
class CloudImageView;
} // namespace Data
namespace Ui { namespace Ui {
class IconButton; class IconButton;
class RoundButton; class RoundButton;
@ -232,6 +236,8 @@ private:
void updateGeometry(int x, int y, int width, int height) override; void updateGeometry(int x, int y, int width, int height) override;
void actionsOpacityCallback(); void actionsOpacityCallback();
const not_null<PeerData*> _peer;
QPixmap _cache; QPixmap _cache;
bool _hideReplyButton = false; bool _hideReplyButton = false;
@ -242,7 +248,7 @@ private:
crl::time _started; crl::time _started;
History *_history = nullptr; History *_history = nullptr;
PeerData *_peer = nullptr; std::shared_ptr<Data::CloudImageView> _userpicView;
QString _author; QString _author;
HistoryItem *_item = nullptr; HistoryItem *_item = nullptr;
int _forwardedCount = 0; int _forwardedCount = 0;

View File

@ -46,15 +46,16 @@ QString CachedUserpics::get(const InMemoryKey &key, PeerData *peer) {
} }
v.path = cWorkingDir() + qsl("tdata/temp/") + QString::number(rand_value<uint64>(), 16) + qsl(".png"); v.path = cWorkingDir() + qsl("tdata/temp/") + QString::number(rand_value<uint64>(), 16) + qsl(".png");
if (key.first || key.second) { if (key.first || key.second) {
auto userpic = std::shared_ptr<Data::CloudImageView>(); // #TODO optimize userpic
if (peer->isSelf()) { if (peer->isSelf()) {
const auto method = _type == Type::Rounded const auto method = (_type == Type::Rounded)
? Ui::EmptyUserpic::GenerateSavedMessagesRounded ? Ui::EmptyUserpic::GenerateSavedMessagesRounded
: Ui::EmptyUserpic::GenerateSavedMessages; : Ui::EmptyUserpic::GenerateSavedMessages;
method(st::notifyMacPhotoSize).save(v.path, "PNG"); method(st::notifyMacPhotoSize).save(v.path, "PNG");
} else if (_type == Type::Rounded) { } else if (_type == Type::Rounded) {
peer->saveUserpicRounded(v.path, st::notifyMacPhotoSize); peer->saveUserpicRounded(userpic, v.path, st::notifyMacPhotoSize);
} else { } else {
peer->saveUserpic(v.path, st::notifyMacPhotoSize); peer->saveUserpic(userpic, v.path, st::notifyMacPhotoSize);
} }
} else { } else {
Core::App().logoNoMargin().save(v.path, "PNG"); Core::App().logoNoMargin().save(v.path, "PNG");

View File

@ -36,7 +36,7 @@ private:
Type _type = Type::Rounded; Type _type = Type::Rounded;
struct Image { struct Image {
crl::time until; crl::time until = 0;
QString path; QString path;
}; };
using Images = QMap<InMemoryKey, Image>; using Images = QMap<InMemoryKey, Image>;

@ -1 +1 @@
Subproject commit dd96d4d482afae4dfd16d0d0f91ca814b9a652a5 Subproject commit 3fc4f2699e53ec225bb5052542255302e14b5ce4