Support personal photo edit in EditContactBox.

This commit is contained in:
John Preston 2022-12-20 17:12:32 +04:00
parent c7c652a277
commit 1dd83f3d34
26 changed files with 484 additions and 182 deletions

View file

@ -208,6 +208,7 @@ void PeerPhoto::clearPersonal(not_null<UserData*> user) {
_session->data().processUsers(data.vusers()); _session->data().processUsers(data.vusers());
}); });
}).send(); }).send();
if (!user->userpicPhotoUnknown() && user->hasPersonalPhoto()) { if (!user->userpicPhotoUnknown() && user->hasPersonalPhoto()) {
_session->storage().remove(Storage::UserPhotosRemoveOne( _session->storage().remove(Storage::UserPhotosRemoveOne(
peerToUser(user->id), peerToUser(user->id),
@ -304,6 +305,9 @@ void PeerPhoto::ready(const FullMsgId &msgId, const MTPInputFile &file) {
_session->data().processPhoto(data.vphoto()); _session->data().processPhoto(data.vphoto());
_session->data().processUsers(data.vusers()); _session->data().processUsers(data.vusers());
}); });
if (!suggestion) {
user->updateFullForced();
}
}).send(); }).send();
} }
} }

View file

@ -2416,6 +2416,12 @@ void ApiWrap::refreshFileReference(
} else { } else {
fail(); fail();
} }
}, [&](Data::FileOriginFullUser data) {
if (const auto user = _session->data().user(data.userId)) {
request(MTPusers_GetFullUser(user->inputUser));
} else {
fail();
}
}, [&](Data::FileOriginPeerPhoto data) { }, [&](Data::FileOriginPeerPhoto data) {
fail(); fail();
}, [&](Data::FileOriginStickerSet data) { }, [&](Data::FileOriginStickerSet data) {

View file

@ -465,7 +465,7 @@ void GroupInfoBox::prepare() {
_photo.create( _photo.create(
this, this,
&_navigation->parentController()->window(), &_navigation->parentController()->window(),
Ui::UserpicButton::Role::ChangePhoto, Ui::UserpicButton::Role::ChoosePhoto,
st::defaultUserpicButton); st::defaultUserpicButton);
_title.create( _title.create(
this, this,

View file

@ -86,7 +86,6 @@ void GiftBox(
const auto userpic = Ui::CreateChild<Ui::UserpicButton>( const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
top, top,
user, user,
Ui::UserpicButton::Role::Custom,
st::defaultUserpicButton); st::defaultUserpicButton);
userpic->setAttribute(Qt::WA_TransparentForMouseEvents); userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
top->widthValue( top->widthValue(

View file

@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "api/api_peer_photo.h"
#include "styles/style_layers.h" #include "styles/style_layers.h"
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
#include "styles/style_info.h" #include "styles/style_info.h"
@ -41,7 +42,8 @@ void SendRequest(
bool sharePhone, bool sharePhone,
const QString &first, const QString &first,
const QString &last, const QString &last,
const QString &phone) { const QString &phone,
Fn<void()> done) {
const auto wasContact = user->isContact(); const auto wasContact = user->isContact();
using Flag = MTPcontacts_AddContact::Flag; using Flag = MTPcontacts_AddContact::Flag;
user->session().api().request(MTPcontacts_AddContact( user->session().api().request(MTPcontacts_AddContact(
@ -73,6 +75,7 @@ void SendRequest(
} }
box->closeBox(); box->closeBox();
} }
done();
}).send(); }).send();
} }
@ -103,6 +106,7 @@ private:
QString _phone; QString _phone;
Fn<void()> _focus; Fn<void()> _focus;
Fn<void()> _save; Fn<void()> _save;
Fn<std::optional<QImage>()> _updatedPersonalPhoto;
}; };
@ -146,6 +150,7 @@ void Controller::setupCover() {
? tr::lng_contact_mobile_hidden() ? tr::lng_contact_mobile_hidden()
: rpl::single(Ui::FormatPhone(_phone)))), : rpl::single(Ui::FormatPhone(_phone)))),
style::margins()); style::margins());
_updatedPersonalPhoto = [=] { return cover->updatedPersonalPhoto(); };
} }
void Controller::setupNameFields() { void Controller::setupNameFields() {
@ -199,13 +204,29 @@ void Controller::initNameFields(
(inverted ? last : first)->showError(); (inverted ? last : first)->showError();
return; return;
} }
const auto user = _user;
const auto personal = _updatedPersonalPhoto
? _updatedPersonalPhoto()
: std::nullopt;
const auto done = [=] {
if (personal) {
if (personal->isNull()) {
user->session().api().peerPhoto().clearPersonal(user);
} else {
user->session().api().peerPhoto().upload(
user,
base::duplicate(*personal));
}
}
};
SendRequest( SendRequest(
Ui::MakeWeak(_box), Ui::MakeWeak(_box),
_user, user,
_sharePhone && _sharePhone->checked(), _sharePhone && _sharePhone->checked(),
firstValue, firstValue,
lastValue, lastValue,
_phone); _phone,
done);
}; };
const auto submit = [=] { const auto submit = [=] {
const auto firstValue = first->getLastText().trimmed(); const auto firstValue = first->getLastText().trimmed();

View file

@ -85,11 +85,7 @@ EditParticipantBox::Inner::Inner(
: RpWidget(parent) : RpWidget(parent)
, _peer(peer) , _peer(peer)
, _user(user) , _user(user)
, _userPhoto( , _userPhoto(this, _user, st::rightsPhotoButton)
this,
_user,
Ui::UserpicButton::Role::Custom,
st::rightsPhotoButton)
, _hasAdminRights(hasAdminRights) , _hasAdminRights(hasAdminRights)
, _rows(this) { , _rows(this) {
_rows->heightValue( _rows->heightValue(
@ -97,7 +93,7 @@ EditParticipantBox::Inner::Inner(
resizeToWidth(width()); resizeToWidth(width());
}, lifetime()); }, lifetime());
_userPhoto->setPointerCursor(false); _userPhoto->setAttribute(Qt::WA_TransparentForMouseEvents);
_userName.setText( _userName.setText(
st::rightsNameStyle, st::rightsNameStyle,
_user->name(), _user->name(),

View file

@ -470,9 +470,10 @@ object_ptr<Ui::RpWidget> Controller::createPhotoEdit() {
_wrap, _wrap,
object_ptr<Ui::UserpicButton>( object_ptr<Ui::UserpicButton>(
_wrap, _wrap,
&_navigation->parentController()->window(), _navigation->parentController(),
_peer, _peer,
Ui::UserpicButton::Role::ChangePhoto, Ui::UserpicButton::Role::ChangePhoto,
Ui::UserpicButton::Source::PeerPhoto,
st::defaultUserpicButton), st::defaultUserpicButton),
st::editPeerPhotoMargins); st::editPeerPhotoMargins);
_controls.photo = photoWrap->entity(); _controls.photo = photoWrap->entity();

View file

@ -1244,7 +1244,6 @@ void Panel::refreshTopButton() {
auto joinAsToggle = object_ptr<Ui::UserpicButton>( auto joinAsToggle = object_ptr<Ui::UserpicButton>(
widget(), widget(),
joinAs, joinAs,
Ui::UserpicButton::Role::Custom,
st::groupCallJoinAsToggle); st::groupCallJoinAsToggle);
_joinAsToggle.destroy(); _joinAsToggle.destroy();
_joinAsToggle = std::move(joinAsToggle); _joinAsToggle = std::move(joinAsToggle);

View file

@ -17,6 +17,12 @@ struct FileReferenceAccumulator {
push(item); push(item);
} }
} }
template <typename Type>
void push(const tl::conditional<Type> &data) {
if (data) {
push(*data);
}
}
void push(const MTPPhoto &data) { void push(const MTPPhoto &data) {
data.match([&](const MTPDphoto &data) { data.match([&](const MTPDphoto &data) {
result.data.emplace( result.data.emplace(
@ -47,52 +53,34 @@ struct FileReferenceAccumulator {
} }
void push(const MTPTheme &data) { void push(const MTPTheme &data) {
data.match([&](const MTPDtheme &data) { data.match([&](const MTPDtheme &data) {
if (const auto document = data.vdocument()) { push(data.vdocument());
push(*document);
}
}); });
} }
void push(const MTPWebPageAttribute &data) { void push(const MTPWebPageAttribute &data) {
data.match([&](const MTPDwebPageAttributeTheme &data) { data.match([&](const MTPDwebPageAttributeTheme &data) {
if (const auto documents = data.vdocuments()) { push(data.vdocuments());
push(*documents);
}
}); });
} }
void push(const MTPWebPage &data) { void push(const MTPWebPage &data) {
data.match([&](const MTPDwebPage &data) { data.match([&](const MTPDwebPage &data) {
if (const auto document = data.vdocument()) { push(data.vdocument());
push(*document); push(data.vattributes());
} push(data.vphoto());
if (const auto attributes = data.vattributes()) { push(data.vcached_page());
push(*attributes);
}
if (const auto photo = data.vphoto()) {
push(*photo);
}
if (const auto page = data.vcached_page()) {
push(*page);
}
}, [](const auto &data) { }, [](const auto &data) {
}); });
} }
void push(const MTPGame &data) { void push(const MTPGame &data) {
data.match([&](const MTPDgame &data) { data.match([&](const MTPDgame &data) {
if (const auto document = data.vdocument()) { push(data.vdocument());
push(*document);
}
}, [](const auto &data) { }, [](const auto &data) {
}); });
} }
void push(const MTPMessageMedia &data) { void push(const MTPMessageMedia &data) {
data.match([&](const MTPDmessageMediaPhoto &data) { data.match([&](const MTPDmessageMediaPhoto &data) {
if (const auto photo = data.vphoto()) { push(data.vphoto());
push(*photo);
}
}, [&](const MTPDmessageMediaDocument &data) { }, [&](const MTPDmessageMediaDocument &data) {
if (const auto document = data.vdocument()) { push(data.vdocument());
push(*document);
}
}, [&](const MTPDmessageMediaWebPage &data) { }, [&](const MTPDmessageMediaWebPage &data) {
push(data.vwebpage()); push(data.vwebpage());
}, [&](const MTPDmessageMediaGame &data) { }, [&](const MTPDmessageMediaGame &data) {
@ -102,9 +90,7 @@ struct FileReferenceAccumulator {
} }
void push(const MTPMessage &data) { void push(const MTPMessage &data) {
data.match([&](const MTPDmessage &data) { data.match([&](const MTPDmessage &data) {
if (const auto media = data.vmedia()) { push(data.vmedia());
push(*media);
}
}, [&](const MTPDmessageService &data) { }, [&](const MTPDmessageService &data) {
data.vaction().match( data.vaction().match(
[&](const MTPDmessageActionChatEditPhoto &data) { [&](const MTPDmessageActionChatEditPhoto &data) {
@ -125,6 +111,11 @@ struct FileReferenceAccumulator {
push(data.vphotos()); push(data.vphotos());
}); });
} }
void push(const MTPusers_UserFull &data) {
data.match([&](const auto &data) {
push(data.vfull_user().data().vpersonal_photo());
});
}
void push(const MTPmessages_RecentStickers &data) { void push(const MTPmessages_RecentStickers &data) {
data.match([&](const MTPDmessages_recentStickers &data) { data.match([&](const MTPDmessages_recentStickers &data) {
push(data.vstickers()); push(data.vstickers());
@ -181,6 +172,10 @@ UpdatedFileReferences GetFileReferences(const MTPphotos_Photos &data) {
return GetFileReferencesHelper(data); return GetFileReferencesHelper(data);
} }
UpdatedFileReferences GetFileReferences(const MTPusers_UserFull &data) {
return GetFileReferencesHelper(data);
}
UpdatedFileReferences GetFileReferences( UpdatedFileReferences GetFileReferences(
const MTPmessages_RecentStickers &data) { const MTPmessages_RecentStickers &data) {
return GetFileReferencesHelper(data); return GetFileReferencesHelper(data);

View file

@ -29,6 +29,18 @@ struct FileOriginUserPhoto {
} }
}; };
struct FileOriginFullUser {
FileOriginFullUser(UserId userId)
: userId(userId) {
}
UserId userId = 0;
inline bool operator<(const FileOriginFullUser &other) const {
return userId < other.userId;
}
};
struct FileOriginPeerPhoto { struct FileOriginPeerPhoto {
explicit FileOriginPeerPhoto(PeerId peerId) : peerId(peerId) { explicit FileOriginPeerPhoto(PeerId peerId) : peerId(peerId) {
} }
@ -113,6 +125,7 @@ struct FileOrigin {
v::null_t, v::null_t,
FileOriginMessage, FileOriginMessage,
FileOriginUserPhoto, FileOriginUserPhoto,
FileOriginFullUser,
FileOriginPeerPhoto, FileOriginPeerPhoto,
FileOriginStickerSet, FileOriginStickerSet,
FileOriginSavedGifs, FileOriginSavedGifs,
@ -126,6 +139,8 @@ struct FileOrigin {
} }
FileOrigin(FileOriginUserPhoto data) : data(data) { FileOrigin(FileOriginUserPhoto data) : data(data) {
} }
FileOrigin(FileOriginFullUser data) : data(data) {
}
FileOrigin(FileOriginPeerPhoto data) : data(data) { FileOrigin(FileOriginPeerPhoto data) : data(data) {
} }
FileOrigin(FileOriginStickerSet data) : data(data) { FileOrigin(FileOriginStickerSet data) : data(data) {
@ -177,6 +192,7 @@ struct UpdatedFileReferences {
UpdatedFileReferences GetFileReferences(const MTPmessages_Messages &data); UpdatedFileReferences GetFileReferences(const MTPmessages_Messages &data);
UpdatedFileReferences GetFileReferences(const MTPphotos_Photos &data); UpdatedFileReferences GetFileReferences(const MTPphotos_Photos &data);
UpdatedFileReferences GetFileReferences(const MTPusers_UserFull &data);
UpdatedFileReferences GetFileReferences( UpdatedFileReferences GetFileReferences(
const MTPmessages_RecentStickers &data); const MTPmessages_RecentStickers &data);
UpdatedFileReferences GetFileReferences( UpdatedFileReferences GetFileReferences(

View file

@ -15,6 +15,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_emoji_statuses.h" #include "data/data_emoji_statuses.h"
#include "data/data_user_names.h" #include "data/data_user_names.h"
#include "data/notify/data_notify_settings.h" #include "data/notify/data_notify_settings.h"
#include "api/api_peer_photo.h"
#include "apiwrap.h"
#include "ui/text/text_options.h" #include "ui/text/text_options.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "styles/style_chat.h" #include "styles/style_chat.h"
@ -365,11 +367,18 @@ bool UserData::hasCalls() const {
namespace Data { namespace Data {
void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) { void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
if (const auto photo = update.vprofile_photo()) { const auto profilePhoto = update.vprofile_photo()
user->owner().processPhoto(*photo); ? user->owner().processPhoto(*update.vprofile_photo()).get()
} : nullptr;
if (const auto photo = update.vpersonal_photo()) { const auto personalPhoto = update.vpersonal_photo()
user->owner().processPhoto(*photo); ? user->owner().processPhoto(*update.vpersonal_photo()).get()
: nullptr;
if (personalPhoto && profilePhoto) {
user->session().api().peerPhoto().registerNonPersonalPhoto(
user,
profilePhoto);
} else {
user->session().api().peerPhoto().unregisterNonPersonalPhoto(user);
} }
user->setSettings(update.vsettings()); user->setSettings(update.vsettings());
user->owner().notifySettings().apply(user, update.vnotify_settings()); user->owner().notifySettings().apply(user, update.vnotify_settings());

View file

@ -429,7 +429,9 @@ QSize Service::performCountCurrentSize(int newWidth) {
} }
const auto media = this->media(); const auto media = this->media();
if (media && data()->isUserpicSuggestion()) { if (media && data()->isUserpicSuggestion()) {
newHeight = st::msgServiceMargin.top() + media->resizeGetHeight(newWidth) + st::msgServiceMargin.bottom(); newHeight += st::msgServiceMargin.top()
+ media->resizeGetHeight(newWidth)
+ st::msgServiceMargin.bottom();
} else if (!text().isEmpty()) { } else if (!text().isEmpty()) {
auto contentWidth = newWidth; auto contentWidth = newWidth;
if (delegate()->elementIsChatWide()) { if (delegate()->elementIsChatWide()) {

View file

@ -841,9 +841,7 @@ void TopBarWidget::refreshInfoButton() {
} else if (const auto peer = _activeChat.key.peer()) { } else if (const auto peer = _activeChat.key.peer()) {
auto info = object_ptr<Ui::UserpicButton>( auto info = object_ptr<Ui::UserpicButton>(
this, this,
_controller,
peer, peer,
Ui::UserpicButton::Role::Custom,
st::topBarInfoButton); st::topBarInfoButton);
info->showSavedMessagesOnSelf(true); info->showSavedMessagesOnSelf(true);
_info.destroy(); _info.destroy();

View file

@ -329,6 +329,7 @@ infoEditContactCover: InfoProfileCover(infoProfileCover) {
nameTop: 33px; nameTop: 33px;
statusTop: 57px; statusTop: 57px;
} }
infoEditContactPersonalLeft: 6px;
infoProfileInaccessibleUserpic: icon {{ "info/inaccessible_userpic", historyPeerUserpicFg }}; infoProfileInaccessibleUserpic: icon {{ "info/inaccessible_userpic", historyPeerUserpicFg }};

View file

@ -308,10 +308,16 @@ Cover::Cover(
this, this,
controller, controller,
_peer, _peer,
(role == Role::Info Ui::UserpicButton::Role::OpenPhoto,
? Ui::UserpicButton::Role::OpenPhoto Ui::UserpicButton::Source::PeerPhoto,
: Ui::UserpicButton::Role::Custom),
_st.photo)) _st.photo))
, _changePersonal((role == Role::Info
|| topic
|| !_peer->isUser()
|| _peer->isSelf()
|| _peer->asUser()->isBot())
? nullptr
: CreateUploadSubButton(this, _peer->asUser(), controller).get())
, _iconButton(topic , _iconButton(topic
? object_ptr<TopicIconButton>(this, controller, topic) ? object_ptr<TopicIconButton>(this, controller, topic)
: nullptr) : nullptr)
@ -363,6 +369,16 @@ void Cover::setupChildGeometry() {
} else { } else {
_iconButton->moveToLeft(_st.photoLeft, _st.photoTop, newWidth); _iconButton->moveToLeft(_st.photoLeft, _st.photoTop, newWidth);
} }
if (_changePersonal) {
_changePersonal->moveToLeft(
(_st.photoLeft
+ _st.photo.photoSize
- _changePersonal->width()
+ st::infoEditContactPersonalLeft),
(_userpic->y()
+ _userpic->height()
- _changePersonal->height()));
}
refreshNameGeometry(newWidth); refreshNameGeometry(newWidth);
refreshStatusGeometry(newWidth); refreshStatusGeometry(newWidth);
}, lifetime()); }, lifetime());
@ -373,6 +389,10 @@ Cover *Cover::setOnlineCount(rpl::producer<int> &&count) {
return this; return this;
} }
std::optional<QImage> Cover::updatedPersonalPhoto() const {
return _personalChosen;
}
void Cover::initViewers(rpl::producer<QString> title) { void Cover::initViewers(rpl::producer<QString> title) {
using Flag = Data::PeerUpdate::Flag; using Flag = Data::PeerUpdate::Flag;
std::move( std::move(
@ -397,10 +417,15 @@ void Cover::initViewers(rpl::producer<QString> title) {
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
refreshUploadPhotoOverlay(); refreshUploadPhotoOverlay();
}, lifetime()); }, lifetime());
setupChangePersonal();
} }
void Cover::refreshUploadPhotoOverlay() { void Cover::refreshUploadPhotoOverlay() {
if (!_userpic || _role == Role::EditContact) { if (!_userpic) {
return;
} else if (_role == Role::EditContact) {
_userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
return; return;
} }
@ -421,7 +446,7 @@ void Cover::refreshUploadPhotoOverlay() {
auto &image = chosen.image; auto &image = chosen.image;
switch (chosen.type) { switch (chosen.type) {
case ChosenType::Set: case ChosenType::Set:
_userpic->changeTo(base::duplicate(image)); _userpic->showCustom(base::duplicate(image));
_peer->session().api().peerPhoto().upload( _peer->session().api().peerPhoto().upload(
_peer, _peer,
std::move(image)); std::move(image));
@ -433,6 +458,44 @@ void Cover::refreshUploadPhotoOverlay() {
break; break;
} }
}); });
if (const auto user = _peer->asUser()) {
_userpic->resetPersonalRequests(
) | rpl::start_with_next([=] {
user->session().api().peerPhoto().clearPersonal(user);
_userpic->showSource(Ui::UserpicButton::Source::PeerPhoto);
}, lifetime());
}
}
void Cover::setupChangePersonal() {
if (!_changePersonal) {
return;
}
_changePersonal->chosenImages(
) | rpl::start_with_next([=](Ui::UserpicButton::ChosenImage &&chosen) {
if (chosen.type == Ui::UserpicButton::ChosenType::Suggest) {
_peer->session().api().peerPhoto().suggest(
_peer,
std::move(chosen.image));
} else {
_personalChosen = std::move(chosen.image);
_userpic->showCustom(base::duplicate(*_personalChosen));
_changePersonal->overrideHasPersonalPhoto(true);
_changePersonal->showSource(
Ui::UserpicButton::Source::NonPersonalIfHasPersonal);
}
}, _changePersonal->lifetime());
_changePersonal->resetPersonalRequests(
) | rpl::start_with_next([=] {
_personalChosen = QImage();
_userpic->showSource(
Ui::UserpicButton::Source::NonPersonalPhoto);
_changePersonal->overrideHasPersonalPhoto(false);
_changePersonal->showCustom(QImage());
}, _changePersonal->lifetime());
} }
void Cover::refreshStatusText() { void Cover::refreshStatusText() {

View file

@ -115,6 +115,7 @@ public:
[[nodiscard]] rpl::producer<Section> showSection() const { [[nodiscard]] rpl::producer<Section> showSection() const {
return _showSection.events(); return _showSection.events();
} }
[[nodiscard]] std::optional<QImage> updatedPersonalPhoto() const;
private: private:
Cover( Cover(
@ -131,6 +132,7 @@ private:
void refreshNameGeometry(int newWidth); void refreshNameGeometry(int newWidth);
void refreshStatusGeometry(int newWidth); void refreshStatusGeometry(int newWidth);
void refreshUploadPhotoOverlay(); void refreshUploadPhotoOverlay();
void setupChangePersonal();
const style::InfoProfileCover &_st; const style::InfoProfileCover &_st;
@ -142,6 +144,8 @@ private:
rpl::variable<int> _onlineCount; rpl::variable<int> _onlineCount;
object_ptr<Ui::UserpicButton> _userpic; object_ptr<Ui::UserpicButton> _userpic;
Ui::UserpicButton *_changePersonal = nullptr;
std::optional<QImage> _personalChosen;
object_ptr<TopicIconButton> _iconButton; object_ptr<TopicIconButton> _iconButton;
object_ptr<Ui::FlatLabel> _name = { nullptr }; object_ptr<Ui::FlatLabel> _name = { nullptr };
object_ptr<Ui::FlatLabel> _status = { nullptr }; object_ptr<Ui::FlatLabel> _status = { nullptr };

View file

@ -19,11 +19,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/media/info_media_buttons.h" #include "info/media/info_media_buttons.h"
#include "boxes/abstract_box.h" #include "boxes/abstract_box.h"
#include "boxes/add_contact_box.h" #include "boxes/add_contact_box.h"
#include "data/data_changes.h"
#include "data/data_forum_topic.h" #include "data/data_forum_topic.h"
#include "data/data_photo.h"
#include "data/data_file_origin.h"
#include "ui/boxes/confirm_box.h" #include "ui/boxes/confirm_box.h"
#include "mainwidget.h" #include "mainwidget.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "api/api_peer_photo.h"
#include "window/main_window.h" #include "window/main_window.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "storage/storage_shared_media.h" #include "storage/storage_shared_media.h"
@ -64,6 +68,20 @@ InnerWidget::InnerWidget(
object_ptr<Ui::RpWidget> InnerWidget::setupContent( object_ptr<Ui::RpWidget> InnerWidget::setupContent(
not_null<RpWidget*> parent) { not_null<RpWidget*> parent) {
auto result = object_ptr<Ui::VerticalLayout>(parent); auto result = object_ptr<Ui::VerticalLayout>(parent);
if (const auto user = _peer->asUser()) {
user->session().changes().peerFlagsValue(
user,
Data::PeerUpdate::Flag::FullInfo
) | rpl::start_with_next([=] {
auto &photos = user->session().api().peerPhoto();
if (const auto original = photos.nonPersonalPhoto(user)) {
// Preload it for the edit contact box.
_nonPersonalView = original->createMediaView();
const auto id = peerToUser(user->id);
original->load(Data::FileOriginFullUser{ id });
}
}, lifetime());
}
_cover = _topic _cover = _topic
? result->add(object_ptr<Cover>( ? result->add(object_ptr<Cover>(
result, result,

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Data { namespace Data {
class ForumTopic; class ForumTopic;
class PhotoMedia;
} // namespace Data } // namespace Data
namespace Window { namespace Window {
@ -70,6 +71,8 @@ private:
PeerData * const _migrated = nullptr; PeerData * const _migrated = nullptr;
Data::ForumTopic * const _topic = nullptr; Data::ForumTopic * const _topic = nullptr;
std::shared_ptr<Data::PhotoMedia> _nonPersonalView;
Members *_members = nullptr; Members *_members = nullptr;
Cover *_cover = nullptr; Cover *_cover = nullptr;
Ui::SlideWrap<RpWidget> *_sharedMediaWrap = nullptr; Ui::SlideWrap<RpWidget> *_sharedMediaWrap = nullptr;

View file

@ -29,7 +29,7 @@ SignupWidget::SignupWidget(
, _photo( , _photo(
this, this,
data->controller, data->controller,
Ui::UserpicButton::Role::ChangePhoto, Ui::UserpicButton::Role::ChoosePhoto,
st::defaultUserpicButton) st::defaultUserpicButton)
, _first(this, st::introName, tr::lng_signup_firstname()) , _first(this, st::introName, tr::lng_signup_firstname())
, _last(this, st::introName, tr::lng_signup_lastname()) , _last(this, st::introName, tr::lng_signup_lastname())

View file

@ -73,7 +73,6 @@ not_null<Ui::RpWidget*> PanelForm::setupContent() {
object_ptr<Ui::UserpicButton>( object_ptr<Ui::UserpicButton>(
userpicWrap, userpicWrap,
bot, bot,
Ui::UserpicButton::Role::Custom,
st::passportFormUserpic)); st::passportFormUserpic));
userpicWrap->widthValue( userpicWrap->widthValue(
) | rpl::start_with_next([=](int width) { ) | rpl::start_with_next([=](int width) {

View file

@ -31,7 +31,6 @@ PanelAskPassword::PanelAskPassword(
, _userpic( , _userpic(
this, this,
_controller->bot(), _controller->bot(),
Ui::UserpicButton::Role::Custom,
st::passportPasswordUserpic) st::passportPasswordUserpic)
, _about1( , _about1(
this, this,

View file

@ -245,6 +245,7 @@ void SetupPhoto(
controller, controller,
self, self,
Ui::UserpicButton::Role::OpenPhoto, Ui::UserpicButton::Role::OpenPhoto,
Ui::UserpicButton::Source::PeerPhoto,
st::settingsInfoPhoto); st::settingsInfoPhoto);
const auto upload = CreateUploadSubButton(wrap, controller); const auto upload = CreateUploadSubButton(wrap, controller);
@ -252,7 +253,7 @@ void SetupPhoto(
) | rpl::start_with_next([=](Ui::UserpicButton::ChosenImage &&chosen) { ) | rpl::start_with_next([=](Ui::UserpicButton::ChosenImage &&chosen) {
auto &image = chosen.image; auto &image = chosen.image;
UpdatePhotoLocally(self, image); UpdatePhotoLocally(self, image);
photo->changeTo(base::duplicate(image)); photo->showCustom(base::duplicate(image));
self->session().api().peerPhoto().upload(self, std::move(image)); self->session().api().peerPhoto().upload(self, std::move(image));
}, upload->lifetime()); }, upload->lifetime());

View file

@ -123,6 +123,7 @@ Cover::Cover(
controller, controller,
_user, _user,
Ui::UserpicButton::Role::OpenPhoto, Ui::UserpicButton::Role::OpenPhoto,
Ui::UserpicButton::Source::PeerPhoto,
st::infoProfileCover.photo) st::infoProfileCover.photo)
, _name(this, st::infoProfileCover.name) , _name(this, st::infoProfileCover.name)
, _phone(this, st::defaultFlatLabel) , _phone(this, st::defaultFlatLabel)
@ -141,7 +142,7 @@ Cover::Cover(
_userpic->switchChangePhotoOverlay(_user->isSelf(), [=]( _userpic->switchChangePhotoOverlay(_user->isSelf(), [=](
Ui::UserpicButton::ChosenImage chosen) { Ui::UserpicButton::ChosenImage chosen) {
auto &image = chosen.image; auto &image = chosen.image;
_userpic->changeTo(base::duplicate(image)); _userpic->showCustom(base::duplicate(image));
_user->session().api().peerPhoto().upload(_user, std::move(image)); _user->session().api().peerPhoto().upload(_user, std::move(image));
}); });

View file

@ -14,8 +14,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_histories.h"
#include "data/data_streaming.h" #include "data/data_streaming.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_photo_media.h"
#include "history/history.h"
#include "calls/calls_instance.h" #include "calls/calls_instance.h"
#include "core/application.h" #include "core/application.h"
#include "ui/layers/generic_box.h" #include "ui/layers/generic_box.h"
@ -99,26 +102,32 @@ QPixmap CreateSquarePixmap(int width, Callback &&paintCallback) {
return Ui::PixmapFromImage(std::move(image)); return Ui::PixmapFromImage(std::move(image));
}; };
} // namespace void SetupSubButtonBackground(
not_null<Ui::UserpicButton*> upload,
not_null<Ui::RpWidget*> background) {
const auto border = st::uploadUserpicButtonBorder;
const auto size = upload->rect().marginsAdded(
{ border, border, border, border }
).size();
UserpicButton::UserpicButton( background->resize(size);
QWidget *parent, background->paintRequest(
not_null<Window::Controller*> window, ) | rpl::start_with_next([=] {
not_null<PeerData*> peer, auto p = QPainter(background);
Role role, auto hq = PainterHighQualityEnabler(p);
const style::UserpicButton &st) p.setBrush(st::boxBg);
: RippleButton(parent, st.changeButton.ripple) p.setPen(Qt::NoPen);
, _st(st) p.drawEllipse(background->rect());
, _controller(window->sessionController()) }, background->lifetime());
, _window(window)
, _peer(peer)
, _role(role) {
Expects(_role == Role::ChangePhoto);
_waiting = false; upload->positionValue(
prepare(); ) | rpl::start_with_next([=](QPoint position) {
background->move(position - QPoint(border, border));
}, background->lifetime());
} }
} // namespace
UserpicButton::UserpicButton( UserpicButton::UserpicButton(
QWidget *parent, QWidget *parent,
not_null<Window::Controller*> window, not_null<Window::Controller*> window,
@ -131,7 +140,6 @@ UserpicButton::UserpicButton(
, _role(role) { , _role(role) {
Expects(_role == Role::ChangePhoto || _role == Role::ChoosePhoto); Expects(_role == Role::ChangePhoto || _role == Role::ChoosePhoto);
_waiting = false;
prepare(); prepare();
} }
@ -140,34 +148,40 @@ UserpicButton::UserpicButton(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
not_null<PeerData*> peer, not_null<PeerData*> peer,
Role role, Role role,
Source source,
const style::UserpicButton &st) const style::UserpicButton &st)
: RippleButton(parent, st.changeButton.ripple) : RippleButton(parent, st.changeButton.ripple)
, _st(st) , _st(st)
, _controller(controller) , _controller(controller)
, _window(&controller->window()) , _window(&controller->window())
, _peer(peer) , _peer(peer)
, _role(role) { , _role(role)
, _source(source) {
if (_source != Source::Custom) {
processPeerPhoto(); processPeerPhoto();
prepare();
setupPeerViewers(); setupPeerViewers();
} }
prepare();
}
UserpicButton::UserpicButton( UserpicButton::UserpicButton(
QWidget *parent, QWidget *parent,
not_null<PeerData*> peer, not_null<PeerData*> peer,
Role role,
const style::UserpicButton &st) const style::UserpicButton &st)
: RippleButton(parent, st.changeButton.ripple) : RippleButton(parent, st.changeButton.ripple)
, _st(st) , _st(st)
, _peer(peer) , _peer(peer)
, _role(role) { , _role(Role::Custom)
Expects(_role != Role::OpenProfile && _role != Role::OpenPhoto); , _source(Source::PeerPhoto) {
Expects(_role != Role::OpenPhoto);
_waiting = false; if (_source != Source::Custom) {
processPeerPhoto(); processPeerPhoto();
prepare();
setupPeerViewers(); setupPeerViewers();
} }
_waiting = false;
prepare();
}
UserpicButton::~UserpicButton() = default; UserpicButton::~UserpicButton() = default;
@ -182,12 +196,39 @@ void UserpicButton::prepare() {
if (_role == Role::ChangePhoto) { if (_role == Role::ChangePhoto) {
chosenImages( chosenImages(
) | rpl::start_with_next([=](ChosenImage &&chosen) { ) | rpl::start_with_next([=](ChosenImage &&chosen) {
setImage(std::move(chosen.image)); showCustom(std::move(chosen.image));
}, lifetime()); }, lifetime());
} }
} }
void UserpicButton::requestSuggestAvailability() {
if (const auto user = _peer ? _peer->asUser() : nullptr) {
if (!user->isSelf()) {
const auto history = user->owner().history(user);
if (!history->lastServerMessageKnown()) {
// Server allows suggesting photos only in non-empty chats.
user->owner().histories().requestDialogEntry(history);
}
}
}
}
bool UserpicButton::canSuggestPhoto(not_null<UserData*> user) const {
// Server allows suggesting photos only in non-empty chats.
return !user->isSelf()
&& (user->owner().history(user)->lastServerMessage() != nullptr);
}
bool UserpicButton::hasPersonalPhotoLocally() const {
if (const auto user = _peer->asUser()) {
return _overrideHasPersonalPhoto.value_or(user->hasPersonalPhoto());
}
return false;
}
void UserpicButton::setClickHandlerByRole() { void UserpicButton::setClickHandlerByRole() {
requestSuggestAvailability();
switch (_role) { switch (_role) {
case Role::ChoosePhoto: case Role::ChoosePhoto:
case Role::ChangePhoto: case Role::ChangePhoto:
@ -197,21 +238,9 @@ void UserpicButton::setClickHandlerByRole() {
case Role::OpenPhoto: case Role::OpenPhoto:
addClickHandler([=] { openPeerPhoto(); }); addClickHandler([=] { openPeerPhoto(); });
break; break;
case Role::OpenProfile:
addClickHandler([this] {
Expects(_controller != nullptr);
_controller->showPeerInfo(_peer);
});
break;
} }
} }
void UserpicButton::changeTo(QImage &&image) {
setImage(std::move(image));
}
void UserpicButton::choosePhotoLocally() { void UserpicButton::choosePhotoLocally() {
if (!_window) { if (!_window) {
return; return;
@ -246,15 +275,16 @@ void UserpicButton::choosePhotoLocally() {
tr::lng_profile_set_photo_for(tr::now), tr::lng_profile_set_photo_for(tr::now),
[=] { chooseFile(); }, [=] { chooseFile(); },
&st::menuIconPhotoSet); &st::menuIconPhotoSet);
if (canSuggestPhoto(user)) {
_menu->addAction( _menu->addAction(
tr::lng_profile_suggest_photo(tr::now), tr::lng_profile_suggest_photo(tr::now),
[=] { chooseFile(ChosenType::Suggest); }, [=] { chooseFile(ChosenType::Suggest); },
&st::menuIconPhotoSuggest); &st::menuIconPhotoSuggest);
if (user->hasPersonalPhoto()) { }
if (hasPersonalPhotoLocally()) {
_menu->addAction( _menu->addAction(
tr::lng_profile_photo_reset(tr::now), tr::lng_profile_photo_reset(tr::now),
[=] { user->session().api().peerPhoto().clearPersonal( [=] { _resetPersonalRequests.fire({}); },
user); _userpicCustom = false; },
&st::menuIconRemove); &st::menuIconRemove);
} }
} else { } else {
@ -293,23 +323,47 @@ void UserpicButton::openPeerPhoto() {
} }
void UserpicButton::setupPeerViewers() { void UserpicButton::setupPeerViewers() {
const auto user = _peer->asUser();
if (user
&& (_source == Source::NonPersonalPhoto
|| _source == Source::NonPersonalIfHasPersonal)) {
user->session().changes().peerFlagsValue(
user,
Data::PeerUpdate::Flag::FullInfo
) | rpl::map([=] {
return std::pair(
user->session().api().peerPhoto().nonPersonalPhoto(user),
user->hasPersonalPhoto());
}) | rpl::distinct_until_changed() | rpl::skip(
1
) | rpl::start_with_next([=] {
processNewPeerPhoto();
update();
}, _sourceLifetime);
}
if (!user
|| _source == Source::PeerPhoto
|| _source == Source::NonPersonalIfHasPersonal) {
_peer->session().changes().peerUpdates( _peer->session().changes().peerUpdates(
_peer, _peer,
Data::PeerUpdate::Flag::Photo Data::PeerUpdate::Flag::Photo
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
processNewPeerPhoto(); processNewPeerPhoto();
update(); update();
}, lifetime()); }, _sourceLifetime);
}
_peer->session().downloaderTaskFinished( _peer->session().downloaderTaskFinished(
) | rpl::filter([=] { ) | rpl::filter([=] {
return _waiting; return _waiting;
}) | rpl::start_with_next([=] { }) | rpl::start_with_next([=] {
if (!Ui::PeerUserpicLoading(_userpicView)) { const auto loading = _showPeerUserpic
? Ui::PeerUserpicLoading(_userpicView)
: (_nonPersonalView && !_nonPersonalView->loaded());
if (!loading) {
_waiting = false; _waiting = false;
startNewPhotoShowing(); startNewPhotoShowing();
} }
}, lifetime()); }, _sourceLifetime);
} }
void UserpicButton::paintEvent(QPaintEvent *e) { void UserpicButton::paintEvent(QPaintEvent *e) {
@ -363,9 +417,9 @@ void UserpicButton::paintEvent(QPaintEvent *e) {
p, p,
photoLeft, photoLeft,
photoTop, photoTop,
_userpicHasImage (_userpicHasImage
? &st::shadowFg->c ? &st::shadowFg->c
: &_st.changeButton.ripple.color->c); : &_st.changeButton.ripple.color->c));
if (over || !_userpicHasImage) { if (over || !_userpicHasImage) {
auto iconLeft = (_st.changeIconPosition.x() < 0) auto iconLeft = (_st.changeIconPosition.x() < 0)
? (_st.photoSize - _st.changeIcon.width()) / 2 ? (_st.photoSize - _st.changeIcon.width()) / 2
@ -474,10 +528,36 @@ QPoint UserpicButton::prepareRippleStartPosition() const {
void UserpicButton::processPeerPhoto() { void UserpicButton::processPeerPhoto() {
Expects(_peer != nullptr); Expects(_peer != nullptr);
_userpicView = _peer->createUserpicView(); const auto user = _peer->asUser();
_waiting = Ui::PeerUserpicLoading(_userpicView); const auto nonPersonal = (user && _source != Source::PeerPhoto)
? _peer->session().api().peerPhoto().nonPersonalPhoto(user)
: nullptr;
_showPeerUserpic = (_source == Source::PeerPhoto)
|| (user
&& !user->hasPersonalPhoto()
&& (_source == Source::NonPersonalPhoto
|| (_source == Source::NonPersonalIfHasPersonal
&& hasPersonalPhotoLocally())));
const auto showNonPersonal = _showPeerUserpic ? nullptr : nonPersonal;
_userpicView = _showPeerUserpic
? _peer->createUserpicView()
: PeerUserpicView();
_nonPersonalView = showNonPersonal
? showNonPersonal->createMediaView()
: nullptr;
_waiting = _showPeerUserpic
? Ui::PeerUserpicLoading(_userpicView)
: (_nonPersonalView && !_nonPersonalView->loaded());
if (_waiting) { if (_waiting) {
if (_showPeerUserpic) {
_peer->loadUserpic(); _peer->loadUserpic();
} else if (_nonPersonalView) {
AssertIsDebug();
showNonPersonal->load(Data::FileOriginFullUser{
peerToUser(user->id),
});
}
} }
if (_role == Role::OpenPhoto) { if (_role == Role::OpenPhoto) {
if (_peer->userpicPhotoUnknown()) { if (_peer->userpicPhotoUnknown()) {
@ -492,7 +572,7 @@ void UserpicButton::processPeerPhoto() {
void UserpicButton::updateCursor() { void UserpicButton::updateCursor() {
Expects(_role == Role::OpenPhoto); Expects(_role == Role::OpenPhoto);
auto pointer = _canOpenPhoto const auto pointer = _canOpenPhoto
|| (_changeOverlayEnabled && _cursorInChangeOverlay); || (_changeOverlayEnabled && _cursorInChangeOverlay);
setPointerCursor(pointer); setPointerCursor(pointer);
} }
@ -634,7 +714,7 @@ void UserpicButton::setCursorInChangeOverlay(bool inOverlay) {
} }
void UserpicButton::processNewPeerPhoto() { void UserpicButton::processNewPeerPhoto() {
if (_userpicCustom) { if (_source == Source::Custom) {
return; return;
} }
processPeerPhoto(); processPeerPhoto();
@ -653,14 +733,13 @@ void UserpicButton::grabOldUserpic() {
} }
void UserpicButton::startNewPhotoShowing() { void UserpicButton::startNewPhotoShowing() {
auto oldUniqueKey = _userpicUniqueKey; const auto oldUniqueKey = _userpicUniqueKey;
prepareUserpicPixmap(); prepareUserpicPixmap();
update(); update();
if (_notShownYet) { if (_notShownYet) {
return; return;
} } else if (oldUniqueKey != _userpicUniqueKey
if (oldUniqueKey != _userpicUniqueKey
|| _a_appearance.animating()) { || _a_appearance.animating()) {
startAnimation(); startAnimation();
} }
@ -733,9 +812,15 @@ void UserpicButton::onStateChanged(
} }
} }
void UserpicButton::setImage(QImage &&image) { void UserpicButton::showCustom(QImage &&image) {
grabOldUserpic(); grabOldUserpic();
clearStreaming();
_sourceLifetime.destroy();
_source = Source::Custom;
_userpicHasImage = !image.isNull();
if (_userpicHasImage) {
auto size = QSize(_st.photoSize, _st.photoSize); auto size = QSize(_st.photoSize, _st.photoSize);
auto small = image.scaled( auto small = image.scaled(
size * cIntRetinaFactor(), size * cIntRetinaFactor(),
@ -745,14 +830,49 @@ void UserpicButton::setImage(QImage &&image) {
_userpic = Ui::PixmapFromImage(forum _userpic = Ui::PixmapFromImage(forum
? Images::Round(std::move(small), Images::Option::RoundLarge) ? Images::Round(std::move(small), Images::Option::RoundLarge)
: Images::Circle(std::move(small))); : Images::Circle(std::move(small)));
} else {
_userpic = CreateSquarePixmap(_st.photoSize, [&](Painter &p) {
fillShape(p, _st.changeButton.textBg);
});
}
_userpic.setDevicePixelRatio(cRetinaFactor()); _userpic.setDevicePixelRatio(cRetinaFactor());
_userpicCustom = _userpicHasImage = true;
_userpicUniqueKey = {}; _userpicUniqueKey = {};
_result = std::move(image); _result = std::move(image);
startNewPhotoShowing(); startNewPhotoShowing();
} }
void UserpicButton::showSource(Source source) {
Expects(_peer != nullptr);
Expects(source != Source::Custom); // Show this using showCustom().
Expects(source == Source::PeerPhoto || _peer->isUser());
if (_source != source) {
clearStreaming();
}
_sourceLifetime.destroy();
_source = source;
_result = QImage();
processPeerPhoto();
setupPeerViewers();
prepareUserpicPixmap();
update();
}
void UserpicButton::overrideHasPersonalPhoto(bool has) {
Expects(_peer && _peer->isUser());
_overrideHasPersonalPhoto = has;
}
rpl::producer<> UserpicButton::resetPersonalRequests() const {
return _resetPersonalRequests.events();
}
void UserpicButton::fillShape(QPainter &p, const style::color &color) const { void UserpicButton::fillShape(QPainter &p, const style::color &color) const {
PainterHighQualityEnabler hq(p); PainterHighQualityEnabler hq(p);
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
@ -767,22 +887,69 @@ void UserpicButton::fillShape(QPainter &p, const style::color &color) const {
} }
void UserpicButton::prepareUserpicPixmap() { void UserpicButton::prepareUserpicPixmap() {
if (_userpicCustom) { if (_source == Source::Custom) {
return; return;
} }
auto size = _st.photoSize; const auto size = _st.photoSize;
_userpicHasImage = _peer _userpicHasImage = _showPeerUserpic
? (_peer
&& (_peer->userpicCloudImage(_userpicView) && (_peer->userpicCloudImage(_userpicView)
|| _role != Role::ChangePhoto); || _role != Role::ChangePhoto))
: (_source == Source::NonPersonalPhoto
|| (_source == Source::NonPersonalIfHasPersonal
&& hasPersonalPhotoLocally()));
_userpic = CreateSquarePixmap(size, [&](Painter &p) { _userpic = CreateSquarePixmap(size, [&](Painter &p) {
if (_userpicHasImage) { if (_userpicHasImage) {
_peer->paintUserpic(p, _userpicView, 0, 0, _st.photoSize); if (_showPeerUserpic) {
_peer->paintUserpic(p, _userpicView, 0, 0, size);
} else if (_nonPersonalView) {
using Size = Data::PhotoSize;
if (const auto full = _nonPersonalView->image(Size::Large)) {
const auto ratio = style::DevicePixelRatio();
auto image = full->original().scaled(
QSize(size, size) * ratio,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
if (_peer->isForum()) {
image = Images::Round(
std::move(image),
Images::CornersMask(size
* Ui::ForumUserpicRadiusMultiplier()));
} else {
image = Images::Circle(std::move(image));
}
p.drawImage(0, 0, image);
}
} else {
const auto user = _peer->asUser();
auto empty = Ui::EmptyUserpic(
Ui::EmptyUserpic::UserpicColor(
Data::PeerColorIndex(_peer->id)),
((user && user->isInaccessible())
? Ui::EmptyUserpic::InaccessibleName()
: _peer->name()));
if (_peer->isForum()) {
empty.paintRounded(
p,
0,
0,
size,
size,
size * Ui::ForumUserpicRadiusMultiplier());
} else {
empty.paintCircle(p, 0, 0, size, size);
}
}
} else { } else {
fillShape(p, _st.changeButton.textBg); fillShape(p, _st.changeButton.textBg);
} }
}); });
_userpicUniqueKey = _userpicHasImage _userpicUniqueKey = _userpicHasImage
? (_showPeerUserpic
? _peer->userpicUniqueKey(_userpicView) ? _peer->userpicUniqueKey(_userpicView)
: _nonPersonalView
? InMemoryKey{ _nonPersonalView->owner()->id, 0 }
: InMemoryKey{ _peer->id.value, _peer->id.value })
: InMemoryKey(); : InMemoryKey();
} }
@ -795,27 +962,7 @@ not_null<Ui::UserpicButton*> CreateUploadSubButton(
&controller->window(), &controller->window(),
Ui::UserpicButton::Role::ChoosePhoto, Ui::UserpicButton::Role::ChoosePhoto,
st::uploadUserpicButton); st::uploadUserpicButton);
SetupSubButtonBackground(upload, background);
const auto border = st::uploadUserpicButtonBorder;
const auto size = upload->rect().marginsAdded(
{ border, border, border, border }
).size();
background->resize(size);
background->paintRequest(
) | rpl::start_with_next([=] {
auto p = QPainter(background);
auto hq = PainterHighQualityEnabler(p);
p.setBrush(st::boxBg);
p.setPen(Qt::NoPen);
p.drawEllipse(background->rect());
}, background->lifetime());
upload->positionValue(
) | rpl::start_with_next([=](QPoint position) {
background->move(position - QPoint(border, border));
}, background->lifetime());
return upload; return upload;
} }
@ -823,8 +970,16 @@ not_null<Ui::UserpicButton*> CreateUploadSubButton(
not_null<Ui::RpWidget*> parent, not_null<Ui::RpWidget*> parent,
not_null<UserData*> contact, not_null<UserData*> contact,
not_null<Window::SessionController*> controller) { not_null<Window::SessionController*> controller) {
const auto result = CreateUploadSubButton(parent, controller); const auto background = Ui::CreateChild<Ui::RpWidget>(parent.get());
return result; const auto upload = Ui::CreateChild<Ui::UserpicButton>(
parent.get(),
controller,
contact,
Ui::UserpicButton::Role::ChoosePhoto,
Ui::UserpicButton::Source::NonPersonalIfHasPersonal,
st::uploadUserpicButton);
SetupSubButtonBackground(upload, background);
return upload;
} }
} // namespace Ui } // namespace Ui

View file

@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class PeerData; class PeerData;
namespace Data {
class PhotoMedia;
} // namespace Data
namespace Window { namespace Window {
class Controller; class Controller;
class SessionController; class SessionController;
@ -40,16 +44,15 @@ public:
ChoosePhoto, ChoosePhoto,
ChangePhoto, ChangePhoto,
OpenPhoto, OpenPhoto,
OpenProfile, Custom,
};
enum class Source {
PeerPhoto,
NonPersonalPhoto,
NonPersonalIfHasPersonal,
Custom, Custom,
}; };
UserpicButton(
QWidget *parent,
not_null<::Window::Controller*> window,
not_null<PeerData*> peer,
Role role,
const style::UserpicButton &st);
UserpicButton( UserpicButton(
QWidget *parent, QWidget *parent,
not_null<::Window::Controller*> window, not_null<::Window::Controller*> window,
@ -60,11 +63,11 @@ public:
not_null<::Window::SessionController*> controller, not_null<::Window::SessionController*> controller,
not_null<PeerData*> peer, not_null<PeerData*> peer,
Role role, Role role,
Source source,
const style::UserpicButton &st); const style::UserpicButton &st);
UserpicButton( UserpicButton(
QWidget *parent, QWidget *parent,
not_null<PeerData*> peer, not_null<PeerData*> peer, // Role::Custom, Source::PeerPhoto
Role role,
const style::UserpicButton &st); const style::UserpicButton &st);
~UserpicButton(); ~UserpicButton();
@ -91,8 +94,11 @@ public:
return std::move(_result); return std::move(_result);
} }
// For Role::OpenPhoto as if it is Role::ChangePhoto. void showCustom(QImage &&image);
void changeTo(QImage &&image); void showSource(Source source);
void overrideHasPersonalPhoto(bool has);
[[nodiscard]] rpl::producer<> resetPersonalRequests() const;
protected: protected:
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
@ -106,7 +112,6 @@ protected:
private: private:
void prepare(); void prepare();
void setImage(QImage &&image);
void setupPeerViewers(); void setupPeerViewers();
void startAnimation(); void startAnimation();
void processPeerPhoto(); void processPeerPhoto();
@ -132,20 +137,24 @@ private:
void grabOldUserpic(); void grabOldUserpic();
void setClickHandlerByRole(); void setClickHandlerByRole();
void requestSuggestAvailability();
void openPeerPhoto(); void openPeerPhoto();
void choosePhotoLocally(); void choosePhotoLocally();
[[nodiscard]] bool canSuggestPhoto(not_null<UserData*> user) const;
[[nodiscard]] bool hasPersonalPhotoLocally() const;
const style::UserpicButton &_st; const style::UserpicButton &_st;
::Window::SessionController *_controller = nullptr; ::Window::SessionController *_controller = nullptr;
::Window::Controller *_window = nullptr; ::Window::Controller *_window = nullptr;
PeerData *_peer = nullptr; PeerData *_peer = nullptr;
PeerUserpicView _userpicView; PeerUserpicView _userpicView;
std::shared_ptr<Data::PhotoMedia> _nonPersonalView;
Role _role = Role::ChangePhoto; Role _role = Role::ChangePhoto;
bool _notShownYet = true; bool _notShownYet = true;
bool _waiting = false; bool _waiting = false;
QPixmap _userpic, _oldUserpic; QPixmap _userpic, _oldUserpic;
bool _userpicHasImage = false; bool _userpicHasImage = false;
bool _userpicCustom = false; bool _showPeerUserpic = false;
InMemoryKey _userpicUniqueKey; InMemoryKey _userpicUniqueKey;
Animations::Simple _a_appearance; Animations::Simple _a_appearance;
QImage _result; QImage _result;
@ -164,6 +173,11 @@ private:
rpl::event_stream<ChosenImage> _chosenImages; rpl::event_stream<ChosenImage> _chosenImages;
Source _source = Source::Custom;
std::optional<bool> _overrideHasPersonalPhoto;
rpl::event_stream<> _resetPersonalRequests;
rpl::lifetime _sourceLifetime;
}; };
[[nodiscard]] not_null<Ui::UserpicButton*> CreateUploadSubButton( [[nodiscard]] not_null<Ui::UserpicButton*> CreateUploadSubButton(

View file

@ -354,9 +354,7 @@ MainMenu::MainMenu(
, _controller(controller) , _controller(controller)
, _userpicButton( , _userpicButton(
this, this,
_controller,
_controller->session().user(), _controller->session().user(),
Ui::UserpicButton::Role::Custom,
st::mainMenuUserpic) st::mainMenuUserpic)
, _toggleAccounts(this) , _toggleAccounts(this)
, _setEmojiStatus(this, SetStatusLabel(&controller->session())) , _setEmojiStatus(this, SetStatusLabel(&controller->session()))