Show toast after photo suggestion is accepted.

This commit is contained in:
John Preston 2022-12-22 19:02:51 +04:00
parent 349fbeeb23
commit b9b6d4dba1
7 changed files with 173 additions and 41 deletions

View File

@ -114,17 +114,21 @@ PeerPhoto::PeerPhoto(not_null<ApiWrap*> api)
});
}
void PeerPhoto::upload(not_null<PeerData*> peer, QImage &&image) {
upload(peer, std::move(image), UploadType::Default);
void PeerPhoto::upload(
not_null<PeerData*> peer,
QImage &&image,
Fn<void()> done) {
upload(peer, std::move(image), UploadType::Default, std::move(done));
}
void PeerPhoto::uploadFallback(not_null<PeerData*> peer, QImage &&image) {
upload(peer, std::move(image), UploadType::Fallback);
upload(peer, std::move(image), UploadType::Fallback, nullptr);
}
void PeerPhoto::updateSelf(
not_null<PhotoData*> photo,
Data::FileOrigin origin) {
Data::FileOrigin origin,
Fn<void()> done) {
const auto send = [=](auto resend) -> void {
const auto usedFileReference = photo->fileReference();
_api.request(MTPphotos_UpdateProfilePhoto(
@ -135,6 +139,9 @@ void PeerPhoto::updateSelf(
_session->data().processPhoto(data.vphoto());
_session->data().processUsers(data.vusers());
});
if (done) {
done();
}
}).fail([=](const MTP::Error &error) {
if (error.code() == 400
&& error.type().startsWith(u"FILE_REFERENCE_"_q)) {
@ -153,7 +160,8 @@ void PeerPhoto::updateSelf(
void PeerPhoto::upload(
not_null<PeerData*> peer,
QImage &&image,
UploadType type) {
UploadType type,
Fn<void()> done) {
peer = peer->migrateToOrMe();
const auto ready = PreparePeerPhoto(
_api.instance().mainDcId(),
@ -169,20 +177,16 @@ void PeerPhoto::upload(
[](const auto &pair) { return pair.second.peer; });
if (already != end(_uploads)) {
_session->uploader().cancel(already->first);
_suggestions.remove(already->first);
_uploads.erase(already);
}
_uploads.emplace(
fakeId,
UploadValue{ peer, type == UploadType::Fallback });
if (type == UploadType::Suggestion) {
_suggestions.emplace(fakeId);
}
UploadValue{ peer, type, std::move(done) });
_session->uploader().uploadMedia(fakeId, ready);
}
void PeerPhoto::suggest(not_null<PeerData*> peer, QImage &&image) {
upload(peer, std::move(image), UploadType::Suggestion);
upload(peer, std::move(image), UploadType::Suggestion, nullptr);
}
void PeerPhoto::clear(not_null<PhotoData*> photo) {
@ -285,20 +289,22 @@ void PeerPhoto::set(not_null<PeerData*> peer, not_null<PhotoData*> photo) {
void PeerPhoto::ready(const FullMsgId &msgId, const MTPInputFile &file) {
const auto maybeUploadValue = _uploads.take(msgId);
const auto suggestion = _suggestions.contains(msgId);
_suggestions.remove(msgId);
if (!maybeUploadValue) {
return;
}
const auto peer = maybeUploadValue->peer;
const auto fallback = maybeUploadValue->fallback;
const auto type = maybeUploadValue->type;
const auto done = maybeUploadValue->done;
const auto applier = [=](const MTPUpdates &result) {
_session->updates().applyUpdates(result);
if (done) {
done();
}
};
if (peer->isSelf()) {
_api.request(MTPphotos_UploadProfilePhoto(
MTP_flags(MTPphotos_UploadProfilePhoto::Flag::f_file
| (fallback
| ((type == UploadType::Fallback)
? MTPphotos_UploadProfilePhoto::Flag::f_fallback
: MTPphotos_UploadProfilePhoto::Flags(0))),
file,
@ -308,11 +314,14 @@ void PeerPhoto::ready(const FullMsgId &msgId, const MTPInputFile &file) {
const auto photoId = _session->data().processPhoto(
result.data().vphoto())->id;
_session->data().processUsers(result.data().vusers());
if (fallback) {
if (type == UploadType::Fallback) {
_session->storage().add(Storage::UserPhotosSetBack(
peerToUser(peer->id),
photoId));
}
if (done) {
done();
}
}).send();
} else if (const auto chat = peer->asChat()) {
const auto history = _session->data().history(chat);
@ -338,7 +347,9 @@ void PeerPhoto::ready(const FullMsgId &msgId, const MTPInputFile &file) {
using Flag = MTPphotos_UploadContactProfilePhoto::Flag;
_api.request(MTPphotos_UploadContactProfilePhoto(
MTP_flags(Flag::f_file
| (suggestion ? Flag::f_suggest : Flag::f_save)),
| ((type == UploadType::Suggestion)
? Flag::f_suggest
: Flag::f_save)),
user->inputUser,
file,
MTPInputFile(), // video
@ -348,9 +359,12 @@ void PeerPhoto::ready(const FullMsgId &msgId, const MTPInputFile &file) {
_session->data().processPhoto(data.vphoto());
_session->data().processUsers(data.vusers());
});
if (!suggestion) {
if (type != UploadType::Suggestion) {
user->updateFullForced();
}
if (done) {
done();
}
}).send();
}
}

View File

@ -28,11 +28,15 @@ public:
using UserPhotoId = PhotoId;
explicit PeerPhoto(not_null<ApiWrap*> api);
void upload(not_null<PeerData*> peer, QImage &&image);
void upload(
not_null<PeerData*> peer,
QImage &&image,
Fn<void()> done = nullptr);
void uploadFallback(not_null<PeerData*> peer, QImage &&image);
void updateSelf(
not_null<PhotoData*> photo,
Data::FileOrigin origin);
Data::FileOrigin origin,
Fn<void()> done = nullptr);
void suggest(not_null<PeerData*> peer, QImage &&image);
void clear(not_null<PhotoData*> photo);
void clearPersonal(not_null<UserData*> user);
@ -59,18 +63,19 @@ private:
void upload(
not_null<PeerData*> peer,
QImage &&image,
UploadType type);
UploadType type,
Fn<void()> done);
const not_null<Main::Session*> _session;
MTP::Sender _api;
struct UploadValue {
not_null<PeerData*> peer;
bool fallback = false;
UploadType type = UploadType::Default;
Fn<void()> done;
};
base::flat_map<FullMsgId, UploadValue> _uploads;
base::flat_set<FullMsgId> _suggestions;
base::flat_map<not_null<UserData*>, mtpRequestId> _userPhotosRequests;

View File

@ -41,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/themes/window_theme_editor_box.h" // GenerateSlug.
#include "payments/payments_checkout_process.h"
#include "settings/settings_common.h"
#include "settings/settings_information.h"
#include "settings/settings_global_ttl.h"
#include "settings/settings_folders.h"
#include "settings/settings_main.h"
@ -488,6 +489,8 @@ bool ResolveSettings(
return ::Settings::ChangePhone::Id();
} else if (section == u"auto_delete"_q) {
return ::Settings::GlobalTTLId();
} else if (section == u"information"_q) {
return ::Settings::Information::Id();
}
return ::Settings::Main::Id();
}();
@ -846,7 +849,7 @@ const std::vector<LocalUrlHandler> &LocalUrlHandlers() {
ResolvePrivatePost
},
{
u"^settings(/language|/devices|/folders|/privacy|/themes|/change_number|/auto_delete)?$"_q,
u"^settings(/language|/devices|/folders|/privacy|/themes|/change_number|/auto_delete|/information)?$"_q,
ResolveSettings
},
{

View File

@ -481,9 +481,10 @@ QImage Photo::prepareImageCacheWithLarge(QSize outer, Image *large) const {
void Photo::paintUserpicFrame(
Painter &p,
const PaintContext &context,
QPoint photoPosition) const {
const auto autoplay = _data->videoCanBePlayed() && videoAutoplayEnabled();
QPoint photoPosition,
bool markFrameShown) const {
const auto autoplay = _data->videoCanBePlayed()
&& videoAutoplayEnabled();
const auto startPlay = autoplay && !_streamed;
if (startPlay) {
const_cast<Photo*>(this)->playAnimation(true);
@ -493,8 +494,6 @@ void Photo::paintUserpicFrame(
const auto size = QSize(width(), height());
const auto rect = QRect(photoPosition, size);
const auto st = context.st;
const auto sti = context.imageStyle();
const auto forum = _parent->data()->history()->isForum();
if (_streamed
@ -527,7 +526,7 @@ void Photo::paintUserpicFrame(
} else {
_streamed->frozenFrame = QImage();
p.drawImage(rect, _streamed->instance.frame(request));
if (!context.paused) {
if (markFrameShown) {
_streamed->instance.markFrameShown();
}
}
@ -535,10 +534,23 @@ void Photo::paintUserpicFrame(
}
validateUserpicImageCache(size, forum);
p.drawImage(rect, _imageCache);
}
void Photo::paintUserpicFrame(
Painter &p,
const PaintContext &context,
QPoint photoPosition) const {
paintUserpicFrame(p, photoPosition, !context.paused);
if (_data->videoCanBePlayed() && !_streamed) {
const auto st = context.st;
const auto sti = context.imageStyle();
const auto innerSize = st::msgFileLayout.thumbSize;
auto inner = QRect(rect.x() + (rect.width() - innerSize) / 2, rect.y() + (rect.height() - innerSize) / 2, innerSize, innerSize);
auto inner = QRect(
photoPosition.x() + (width() - innerSize) / 2,
photoPosition.y() + (height() - innerSize) / 2,
innerSize,
innerSize);
p.setPen(Qt::NoPen);
if (context.selected()) {
p.setBrush(st->msgDateImgBgSelected());

View File

@ -63,6 +63,11 @@ public:
}
void showPhoto(FullMsgId id);
void paintUserpicFrame(
Painter &p,
QPoint photoPosition,
bool markFrameShown) const;
QSize sizeForGroupingOptimal(int maxWidth) const override;
QSize sizeForGrouping(int width) const override;
void drawGrouped(

View File

@ -24,6 +24,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "window/window_session_controller.h"
#include "ui/boxes/confirm_box.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/painter.h"
#include "mainwidget.h"
#include "apiwrap.h"
@ -34,23 +36,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace HistoryView {
namespace {
constexpr auto kToastDuration = 5 * crl::time(1000);
void ShowUserpicSuggestion(
not_null<Window::SessionController*> controller,
const std::shared_ptr<Data::PhotoMedia> &media,
const FullMsgId itemId,
not_null<PeerData*> peer) {
not_null<PeerData*> peer,
Fn<void()> setDone) {
const auto photo = media->owner();
const auto from = peer->asUser();
const auto name = (from && !from->firstName.isEmpty())
? from->firstName
: peer->name();
if (photo->hasVideo()) {
const auto done = [=] {
const auto done = [=](Fn<void()> close) {
using namespace Settings;
const auto session = &photo->session();
auto &peerPhotos = session->api().peerPhoto();
peerPhotos.updateSelf(photo, itemId);
controller->showSettings(Information::Id());
peerPhotos.updateSelf(photo, itemId, setDone);
close();
};
controller->show(Ui::MakeConfirmBox({
.text = tr::lng_profile_accept_video_sure(
@ -72,11 +77,10 @@ void ShowUserpicSuggestion(
auto &peerPhotos = session->api().peerPhoto();
if (original->size() == image.size()
&& original->constBits() == image.constBits()) {
peerPhotos.updateSelf(photo, itemId);
peerPhotos.updateSelf(photo, itemId, setDone);
} else {
peerPhotos.upload(user, std::move(image));
peerPhotos.upload(user, std::move(image), setDone);
}
controller->showSettings(Information::Id());
};
using namespace Editor;
PrepareProfilePhoto(
@ -96,7 +100,86 @@ void ShowUserpicSuggestion(
}
}
[[nodiscard]] QImage GrabUserpicFrame(base::weak_ptr<Photo> photo) {
const auto strong = photo.get();
if (!strong || !strong->width() || !strong->height()) {
return {};
}
const auto ratio = style::DevicePixelRatio();
auto frame = QImage(
QSize(strong->width(), strong->height()) * ratio,
QImage::Format_ARGB32_Premultiplied);
frame.fill(Qt::transparent);
frame.setDevicePixelRatio(ratio);
auto p = Painter(&frame);
strong->paintUserpicFrame(p, QPoint(0, 0), false);
p.end();
return frame;
}
void ShowSetToast(
not_null<Window::SessionController*> controller,
const QImage &frame) {
const auto text = Ui::Text::Bold(
tr::lng_profile_changed_photo_title(tr::now)
).append('\n').append(
tr::lng_profile_changed_photo_about(
tr::now,
lt_link,
Ui::Text::Link(
tr::lng_profile_changed_photo_link(tr::now),
u"tg://settings/information"_q),
Ui::Text::WithEntities)
);
auto st = std::make_shared<style::Toast>(st::historyPremiumToast);
const auto skip = st->padding.top();
const auto size = st->style.font->height * 2;
const auto ratio = style::DevicePixelRatio();
auto copy = frame.scaled(
QSize(size, size) * ratio,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
copy.setDevicePixelRatio(ratio);
st->padding.setLeft(skip + size + skip);
st->palette.linkFg = st->palette.selectLinkFg = st::mediaviewTextLinkFg;
const auto parent = Window::Show(controller).toastParent();
const auto weak = Ui::Toast::Show(parent, {
.text = text,
.st = st.get(),
.durationMs = kToastDuration,
.multiline = true,
.dark = true,
.slideSide = RectPart::Bottom,
});
if (const auto strong = weak.get()) {
const auto widget = strong->widget();
widget->lifetime().add([st = std::move(st)] {});
const auto preview = Ui::CreateChild<Ui::RpWidget>(widget.get());
preview->moveToLeft(skip, skip);
preview->resize(size, size);
preview->show();
preview->setAttribute(Qt::WA_TransparentForMouseEvents);
preview->paintRequest(
) | rpl::start_with_next([=] {
QPainter(preview).drawImage(0, 0, copy);
}, preview->lifetime());
}
}
[[nodiscard]] Fn<void()> ShowSetToastCallback(
base::weak_ptr<Window::SessionController> weak,
QImage frame) {
return [weak = std::move(weak), frame = std::move(frame)] {
if (const auto strong = weak.get()) {
ShowSetToast(strong, frame);
}
};
}
} // namespace
UserpicSuggestion::UserpicSuggestion(
not_null<Element*> parent,
not_null<PeerData*> chat,
@ -138,10 +221,15 @@ ClickHandlerPtr UserpicSuggestion::createViewLink() {
const auto photo = _photo.getPhoto();
const auto itemId = _photo.parent()->data()->fullId();
const auto peer = _photo.parent()->data()->history()->peer;
const auto show = crl::guard(&_photo, [=](FullMsgId id) {
const auto weak = base::make_weak(&_photo);
const auto show = crl::guard(weak, [=](FullMsgId id) {
_photo.showPhoto(id);
});
return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
auto frame = GrabUserpicFrame(weak);
if (frame.isNull()) {
return;
}
const auto my = context.other.value<ClickHandlerContext>();
if (const auto controller = my.sessionWindow.get()) {
const auto media = photo->activeMediaView();
@ -150,7 +238,12 @@ ClickHandlerPtr UserpicSuggestion::createViewLink() {
PhotoOpenClickHandler(photo, show, itemId).onClick(
context);
} else {
ShowUserpicSuggestion(controller, media, itemId, peer);
ShowUserpicSuggestion(
controller,
media,
itemId,
peer,
ShowSetToastCallback(controller, std::move(frame)));
}
} else if (!photo->loading()) {
PhotoSaveClickHandler(photo, itemId).onClick(context);

@ -1 +1 @@
Subproject commit fbdc6ed5ac791890d87ca7f22b668ceca47d7684
Subproject commit d079108e2949db26cd2d83108559a71f70fe52e2