Allow accepting video userpic suggestions.

This commit is contained in:
John Preston 2022-12-14 12:20:01 +04:00
parent 446f0f1653
commit 9513aaa768
16 changed files with 287 additions and 149 deletions

View File

@ -1211,6 +1211,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_profile_suggest_photo" = "Suggest Profile Photo";
"lng_profile_set_photo_for" = "Set Profile Photo";
"lng_profile_photo_reset" = "Reset to Original";
"lng_profile_set_for_done" = "You will now always see this photo for {user}'s account.";
"lng_profile_suggest_sure" = "You can suggest {user} to set this photo for their page.";
"lng_profile_suggest_button" = "Suggest";
"lng_profile_set_personal_sure" = "Only you will see this photo and it will replace any photo {user} sets for themselves.";
"lng_profile_accept_photo_sure" = "{user} suggests you to use this profile photo for your Telegram account.";
"lng_profile_set_photo_button" = "Set Photo";
"lng_profile_accept_video_sure" = "{user} suggests you to use this profile video for your Telegram account.";
"lng_profile_set_video_button" = "Set Video";
"lng_profile_changed_photo_title" = "Photo updated";
"lng_profile_changed_photo_about" = "You can change it in {link}.";
"lng_profile_changed_photo_link" = "Settings";
"lng_media_type_photos" = "Photos";
"lng_media_type_gifs" = "GIFs";
"lng_media_type_videos" = "Videos";
@ -1569,6 +1580,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_suggested_photo_me" = "You suggested {user} to use this profile photo.";
"lng_action_suggested_photo" = "{user} suggests you to use this profile photo.";
"lng_action_suggested_photo_button" = "View Photo";
"lng_action_suggested_video_me" = "You suggested {user} to use this profile video.";
"lng_action_suggested_video" = "{user} suggests you to use this profile video.";
"lng_action_suggested_video_button" = "View Video";
"lng_action_attach_menu_bot_allowed" = "You allowed this bot to message you when you added it in the attachment menu.";
"lng_action_topic_created_inside" = "Topic created";
"lng_action_topic_closed_inside" = "Topic closed";

View File

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unixtime.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_file_origin.h"
#include "data/data_peer.h"
#include "data/data_photo.h"
#include "data/data_session.h"
@ -121,16 +122,32 @@ void PeerPhoto::uploadFallback(not_null<PeerData*> peer, QImage &&image) {
upload(peer, std::move(image), UploadType::Fallback);
}
void PeerPhoto::updateSelf(not_null<PhotoData*> photo) {
_api.request(MTPphotos_UpdateProfilePhoto(
MTP_flags(0),
photo->mtpInput()
)).done([=](const MTPphotos_Photo &result) {
result.match([&](const MTPDphotos_photo &data) {
_session->data().processPhoto(data.vphoto());
_session->data().processUsers(data.vusers());
});
}).send();
void PeerPhoto::updateSelf(
not_null<PhotoData*> photo,
Data::FileOrigin origin) {
const auto send = [=](auto resend) -> void {
const auto usedFileReference = photo->fileReference();
_api.request(MTPphotos_UpdateProfilePhoto(
MTP_flags(0),
photo->mtpInput()
)).done([=](const MTPphotos_Photo &result) {
result.match([&](const MTPDphotos_photo &data) {
_session->data().processPhoto(data.vphoto());
_session->data().processUsers(data.vusers());
});
}).fail([=](const MTP::Error &error) {
if (error.code() == 400
&& error.type().startsWith(u"FILE_REFERENCE_"_q)) {
photo->session().api().refreshFileReference(origin, [=](
const auto &) {
if (photo->fileReference() != usedFileReference) {
resend(resend);
}
});
}
}).send();
};
send(send);
}
void PeerPhoto::upload(

View File

@ -13,6 +13,10 @@ class ApiWrap;
class PeerData;
class UserData;
namespace Data {
struct FileOrigin;
} // namespace Data
namespace Main {
class Session;
} // namespace Main
@ -26,7 +30,9 @@ public:
void upload(not_null<PeerData*> peer, QImage &&image);
void uploadFallback(not_null<PeerData*> peer, QImage &&image);
void updateSelf(not_null<PhotoData*> photo);
void updateSelf(
not_null<PhotoData*> photo,
Data::FileOrigin origin);
void suggest(not_null<PeerData*> peer, QImage &&image);
void clear(not_null<PhotoData*> photo);
void clearPersonal(not_null<UserData*> user);

View File

@ -28,6 +28,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_premium_limits.h"
#include "data/stickers/data_stickers.h"
#include "data/stickers/data_custom_emoji.h"
#include "editor/editor_layer_widget.h"
#include "editor/photo_editor.h"
#include "editor/photo_editor_layer_widget.h"
#include "history/history_drag_area.h"
#include "history/history_item.h"
@ -453,13 +455,18 @@ void EditCaptionBox::setupPhotoEditorEventHandler() {
rebuildPreview();
};
const auto fileImage = std::make_shared<Image>(*large);
auto editor = base::make_unique_q<Editor::PhotoEditor>(
this,
&controller->window(),
fileImage,
Editor::PhotoModifications());
const auto raw = editor.get();
auto layer = std::make_unique<Editor::LayerWidget>(
this,
std::move(editor));
Editor::InitEditorLayer(layer.get(), raw, std::move(callback));
controller->showLayer(
std::make_unique<Editor::LayerWidget>(
this,
&controller->window(),
fileImage,
Editor::PhotoModifications(),
std::move(callback)),
std::move(layer),
Ui::LayerOption::KeepOther);
}
}, lifetime());

View File

@ -95,6 +95,8 @@ struct FileReferenceAccumulator {
data.vaction().match(
[&](const MTPDmessageActionChatEditPhoto &data) {
push(data.vphoto());
}, [&](const MTPDmessageActionSuggestProfilePhoto &data) {
push(data.vphoto());
}, [](const auto &data) {
});
}, [](const MTPDmessageEmpty &data) {

View File

@ -0,0 +1,50 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "editor/editor_layer_widget.h"
#include <QtGui/QGuiApplication>
namespace Editor {
LayerWidget::LayerWidget(
not_null<QWidget*> parent,
base::unique_qptr<Ui::RpWidget> content)
: Ui::LayerWidget(parent)
, _content(std::move(content)) {
_content->setParent(this);
_content->show();
paintRequest(
) | rpl::start_with_next([=](const QRect &clip) {
auto p = QPainter(this);
p.fillRect(clip, st::photoEditorBg);
}, lifetime());
sizeValue(
) | rpl::start_with_next([=](const QSize &size) {
_content->resize(size);
}, lifetime());
}
void LayerWidget::parentResized() {
resizeToWidth(parentWidget()->width());
}
void LayerWidget::keyPressEvent(QKeyEvent *e) {
QGuiApplication::sendEvent(_content.get(), e);
}
int LayerWidget::resizeGetHeight(int newWidth) {
return parentWidget()->height();
}
bool LayerWidget::closeByOutsideClick() const {
return false;
}
} // namespace Editor

View File

@ -0,0 +1,42 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "ui/layers/layer_widget.h"
#include "ui/image/image.h"
#include "editor/photo_editor_common.h"
#include "base/unique_qptr.h"
enum class ImageRoundRadius;
namespace Window {
class Controller;
class SessionController;
} // namespace Window
namespace Editor {
class LayerWidget final : public Ui::LayerWidget {
public:
LayerWidget(
not_null<QWidget*> parent,
base::unique_qptr<Ui::RpWidget> content);
void parentResized() override;
bool closeByOutsideClick() const override;
protected:
void keyPressEvent(QKeyEvent *e) override;
int resizeGetHeight(int newWidth) override;
private:
const base::unique_qptr<Ui::RpWidget> _content;
};
} // namespace Editor

View File

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "editor/photo_editor_controls.h"
#include "window/window_controller.h"
#include "window/window_session_controller.h"
#include "ui/layers/layer_widget.h"
#include "styles/style_editor.h"
namespace Editor {
@ -46,7 +47,7 @@ constexpr auto kPrecision = 100000;
} // namespace
PhotoEditor::PhotoEditor(
not_null<Ui::RpWidget*> parent,
not_null<QWidget*> parent,
not_null<Window::Controller*> controller,
std::shared_ptr<Image> photo,
PhotoModifications modifications,
@ -174,7 +175,7 @@ PhotoEditor::PhotoEditor(
}, lifetime());
}
void PhotoEditor::handleKeyPress(not_null<QKeyEvent*> e) {
void PhotoEditor::keyPressEvent(QKeyEvent *e) {
if (!_colorPicker->preventHandleKeyPress()) {
_content->handleKeyPress(e) || _controls->handleKeyPress(e);
}
@ -193,4 +194,24 @@ rpl::producer<> PhotoEditor::cancelRequests() const {
return _cancel.events();
}
void InitEditorLayer(
not_null<Ui::LayerWidget*> layer,
not_null<PhotoEditor*> editor,
Fn<void(PhotoModifications)> doneCallback) {
editor->cancelRequests(
) | rpl::start_with_next([=] {
layer->closeLayer();
}, editor->lifetime());
const auto weak = Ui::MakeWeak(layer.get());
editor->doneRequests(
) | rpl::start_with_next([=, done = std::move(doneCallback)](
const PhotoModifications &mods) {
done(mods);
if (const auto strong = weak.data()) {
strong->closeLayer();
}
}, editor->lifetime());
}
} // namespace Editor

View File

@ -8,11 +8,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "ui/rp_widget.h"
#include "ui/image/image.h"
#include "base/unique_qptr.h"
#include "editor/photo_editor_common.h"
#include "editor/photo_editor_inner_common.h"
#include "ui/image/image.h"
namespace Ui {
class LayerWidget;
} // namespace Ui
namespace Window {
class Controller;
@ -28,19 +31,18 @@ struct Controllers;
class PhotoEditor final : public Ui::RpWidget {
public:
PhotoEditor(
not_null<Ui::RpWidget*> parent,
not_null<QWidget*> parent,
not_null<Window::Controller*> controller,
std::shared_ptr<Image> photo,
PhotoModifications modifications,
EditorData data = EditorData());
void save();
rpl::producer<PhotoModifications> doneRequests() const;
rpl::producer<> cancelRequests() const;
void handleKeyPress(not_null<QKeyEvent*> e);
[[nodiscard]] rpl::producer<PhotoModifications> doneRequests() const;
[[nodiscard]] rpl::producer<> cancelRequests() const;
private:
void keyPressEvent(QKeyEvent *e) override;
PhotoModifications _modifications;
@ -59,4 +61,9 @@ private:
};
void InitEditorLayer(
not_null<Ui::LayerWidget*> layer,
not_null<PhotoEditor*> editor,
Fn<void(PhotoModifications)> doneCallback);
} // namespace Editor

View File

@ -8,12 +8,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "editor/photo_editor_layer_widget.h"
#include "ui/boxes/confirm_box.h" // InformBox
#include "editor/editor_layer_widget.h"
#include "editor/photo_editor.h"
#include "storage/storage_media_prepare.h"
#include "ui/chat/attach/attach_prepare.h"
#include "window/window_controller.h"
#include "window/window_session_controller.h"
#include <QtGui/QGuiApplication>
namespace Editor {
namespace {
@ -22,7 +25,7 @@ constexpr auto kProfilePhotoSize = 640;
} // namespace
void OpenWithPreparedFile(
not_null<Ui::RpWidget*> parent,
not_null<QWidget*> parent,
not_null<Window::SessionController*> controller,
not_null<Ui::PreparedFile*> file,
int previewWidth,
@ -54,18 +57,19 @@ void OpenWithPreparedFile(
};
auto copy = image->data;
const auto fileImage = std::make_shared<Image>(std::move(copy));
controller->showLayer(
std::make_unique<LayerWidget>(
parent,
&controller->window(),
fileImage,
image->modifications,
std::move(callback)),
Ui::LayerOption::KeepOther);
auto editor = base::make_unique_q<PhotoEditor>(
parent,
&controller->window(),
fileImage,
image->modifications);
const auto raw = editor.get();
auto layer = std::make_unique<LayerWidget>(parent, std::move(editor));
InitEditorLayer(layer.get(), raw, std::move(callback));
controller->showLayer(std::move(layer), Ui::LayerOption::KeepOther);
}
void PrepareProfilePhoto(
not_null<Ui::RpWidget*> parent,
not_null<QWidget*> parent,
not_null<Window::Controller*> controller,
ImageRoundRadius radius,
Fn<void(QImage &&image)> &&doneCallback,
@ -112,24 +116,25 @@ void PrepareProfilePhoto(
minSide);
}();
controller->showLayer(
std::make_unique<LayerWidget>(
parent,
controller,
fileImage,
PhotoModifications{ .crop = std::move(crop) },
std::move(applyModifications),
EditorData{
.cropType = (radius == ImageRoundRadius::Ellipse
? EditorData::CropType::Ellipse
: EditorData::CropType::RoundedRect),
.keepAspectRatio = true,
}),
Ui::LayerOption::KeepOther);
auto editor = base::make_unique_q<PhotoEditor>(
parent,
controller,
fileImage,
PhotoModifications{ .crop = std::move(crop) },
EditorData{
.cropType = (radius == ImageRoundRadius::Ellipse
? EditorData::CropType::Ellipse
: EditorData::CropType::RoundedRect),
.keepAspectRatio = true,
});
const auto raw = editor.get();
auto layer = std::make_unique<LayerWidget>(parent, std::move(editor));
InitEditorLayer(layer.get(), raw, std::move(applyModifications));
controller->showLayer(std::move(layer), Ui::LayerOption::KeepOther);
}
void PrepareProfilePhotoFromFile(
not_null<Ui::RpWidget*> parent,
not_null<QWidget*> parent,
not_null<Window::Controller*> controller,
ImageRoundRadius radius,
Fn<void(QImage &&image)> &&doneCallback) {
@ -158,60 +163,4 @@ void PrepareProfilePhotoFromFile(
crl::guard(parent, callback));
}
LayerWidget::LayerWidget(
not_null<Ui::RpWidget*> parent,
not_null<Window::Controller*> window,
std::shared_ptr<Image> photo,
PhotoModifications modifications,
Fn<void(PhotoModifications)> &&doneCallback,
EditorData data)
: Ui::LayerWidget(parent)
, _content(base::make_unique_q<PhotoEditor>(
this,
window,
photo,
std::move(modifications),
std::move(data))) {
paintRequest(
) | rpl::start_with_next([=](const QRect &clip) {
auto p = QPainter(this);
p.fillRect(clip, st::photoEditorBg);
}, lifetime());
_content->cancelRequests(
) | rpl::start_with_next([=] {
closeLayer();
}, lifetime());
const auto weak = Ui::MakeWeak(_content.get());
_content->doneRequests(
) | rpl::start_with_next([=, done = std::move(doneCallback)](
const PhotoModifications &mods) {
done(mods);
if (weak) closeLayer();
}, lifetime());
sizeValue(
) | rpl::start_with_next([=](const QSize &size) {
_content->resize(size);
}, lifetime());
}
void LayerWidget::parentResized() {
resizeToWidth(parentWidget()->width());
}
void LayerWidget::keyPressEvent(QKeyEvent *e) {
_content->handleKeyPress(e);
}
int LayerWidget::resizeGetHeight(int newWidth) {
return parentWidget()->height();
}
bool LayerWidget::closeByOutsideClick() const {
return false;
}
} // namespace Editor

View File

@ -6,16 +6,15 @@ For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "ui/layers/layer_widget.h"
#include "base/unique_qptr.h"
#include "editor/photo_editor_common.h"
#include "ui/image/image.h"
//
//#include "ui/image/image.h"
//#include "editor/photo_editor_common.h"
//#include "base/unique_qptr.h"
enum class ImageRoundRadius;
namespace Ui {
class RpWidget;
struct PreparedFile;
} // namespace Ui
@ -27,47 +26,23 @@ class SessionController;
namespace Editor {
void OpenWithPreparedFile(
not_null<Ui::RpWidget*> parent,
not_null<QWidget*> parent,
not_null<Window::SessionController*> controller,
not_null<Ui::PreparedFile*> file,
int previewWidth,
Fn<void()> &&doneCallback);
void PrepareProfilePhoto(
not_null<Ui::RpWidget*> parent,
not_null<QWidget*> parent,
not_null<Window::Controller*> controller,
ImageRoundRadius radius,
Fn<void(QImage &&image)> &&doneCallback,
QImage &&image);
void PrepareProfilePhotoFromFile(
not_null<Ui::RpWidget*> parent,
not_null<QWidget*> parent,
not_null<Window::Controller*> controller,
ImageRoundRadius radius,
Fn<void(QImage &&image)> &&doneCallback);
class PhotoEditor;
class LayerWidget : public Ui::LayerWidget {
public:
LayerWidget(
not_null<Ui::RpWidget*> parent,
not_null<Window::Controller*> window,
std::shared_ptr<Image> photo,
PhotoModifications modifications,
Fn<void(PhotoModifications)> &&doneCallback,
EditorData data = EditorData());
void parentResized() override;
bool closeByOutsideClick() const override;
protected:
void keyPressEvent(QKeyEvent *e) override;
int resizeGetHeight(int newWidth) override;
private:
const base::unique_qptr<PhotoEditor> _content;
};
} // namespace Editor

View File

@ -3884,6 +3884,12 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
auto prepareSuggestProfilePhoto = [this](const MTPDmessageActionSuggestProfilePhoto &action) {
auto result = PreparedServiceText{};
const auto isSelf = (_from->id == _from->session().userPeerId());
const auto isVideo = action.vphoto().match([&](const MTPDphoto &data) {
return data.vvideo_sizes().has_value()
&& !data.vvideo_sizes()->v.isEmpty();
}, [](const MTPDphotoEmpty &) {
return false;
});
const auto peer = isSelf ? history()->peer : _from;
const auto user = peer->asUser();
const auto name = (user && !user->firstName.isEmpty())
@ -3891,8 +3897,12 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
: peer->name();
result.links.push_back(peer->createOpenLink());
result.text = (isSelf
? tr::lng_action_suggested_photo_me
: tr::lng_action_suggested_photo)(
? (isVideo
? tr::lng_action_suggested_video_me
: tr::lng_action_suggested_photo_me)
: (isVideo
? tr::lng_action_suggested_video
: tr::lng_action_suggested_photo))(
tr::now,
lt_user,
Ui::Text::Link(name, 1), // Link 1.

View File

@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "window/window_session_controller.h"
#include "ui/boxes/confirm_box.h"
#include "ui/painter.h"
#include "mainwidget.h"
#include "apiwrap.h"
@ -56,7 +57,11 @@ QString UserpicSuggestion::title() {
}
QString UserpicSuggestion::button() {
return tr::lng_action_suggested_photo_button(tr::now);
return _photo.getPhoto()->hasVideo()
? (_photo.parent()->data()->out()
? tr::lng_action_suggested_video_button(tr::now)
: tr::lng_profile_set_video_button(tr::now))
: tr::lng_action_suggested_photo_button(tr::now);
}
QString UserpicSuggestion::subtitle() {
@ -67,6 +72,7 @@ ClickHandlerPtr UserpicSuggestion::createViewLink() {
const auto out = _photo.parent()->data()->out();
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) {
_photo.showPhoto(id);
});
@ -76,7 +82,29 @@ ClickHandlerPtr UserpicSuggestion::createViewLink() {
const auto media = photo->activeMediaView();
if (media->loaded()) {
if (out) {
PhotoOpenClickHandler(photo, show, itemId).onClick(context);
PhotoOpenClickHandler(photo, show, itemId).onClick(
context);
} else if (photo->hasVideo()) {
const auto user = peer->asUser();
const auto name = (user && !user->firstName.isEmpty())
? user->firstName
: peer->name();
const auto done = [=] {
using namespace Settings;
const auto session = &photo->session();
auto &peerPhotos = session->api().peerPhoto();
peerPhotos.updateSelf(photo, itemId);
controller->showSettings(Information::Id());
};
controller->show(Ui::MakeConfirmBox({
.text = tr::lng_profile_accept_video_sure(
tr::now,
lt_user,
name),
.confirmed = done,
.confirmText = tr::lng_profile_set_video_button(
tr::now),
}));
} else {
const auto original = std::make_shared<QImage>(
media->image(Data::PhotoSize::Large)->original());
@ -88,7 +116,7 @@ ClickHandlerPtr UserpicSuggestion::createViewLink() {
auto &peerPhotos = session->api().peerPhoto();
if (original->size() == image.size()
&& original->constBits() == image.constBits()) {
peerPhotos.updateSelf(photo);
peerPhotos.updateSelf(photo, itemId);
} else {
peerPhotos.upload(user, std::move(image));
}

View File

@ -140,6 +140,8 @@ UserpicButton::UserpicButton(
, _role(role) {
Expects(_role == Role::ChangePhoto || _role == Role::ChoosePhoto);
showCustom({});
_waiting = false;
prepare();
}
@ -157,7 +159,9 @@ UserpicButton::UserpicButton(
, _peer(peer)
, _role(role)
, _source(source) {
if (_source != Source::Custom) {
if (_source == Source::Custom) {
showCustom({});
} else {
processPeerPhoto();
setupPeerViewers();
}
@ -175,7 +179,9 @@ UserpicButton::UserpicButton(
, _source(Source::PeerPhoto) {
Expects(_role != Role::OpenPhoto);
if (_source != Source::Custom) {
if (_source == Source::Custom) {
showCustom({});
} else {
processPeerPhoto();
setupPeerViewers();
}
@ -813,7 +819,9 @@ void UserpicButton::onStateChanged(
}
void UserpicButton::showCustom(QImage &&image) {
grabOldUserpic();
if (!_notShownYet) {
grabOldUserpic();
}
clearStreaming();
_sourceLifetime.destroy();

View File

@ -73,6 +73,8 @@ PRIVATE
editor/controllers/undo_controller.h
editor/editor_crop.cpp
editor/editor_crop.h
editor/editor_layer_widget.cpp
editor/editor_layer_widget.h
editor/photo_editor_common.cpp
editor/photo_editor_common.h
editor/photo_editor_inner_common.h

@ -1 +1 @@
Subproject commit b178a5d15f4e422f4497e9c0334fe340df9d6046
Subproject commit 07d9420c19c008f9b49faaabde3e7d2139255961