Moved api peer photo processing to separated file.

Removed MainWidget::deletePhotoLayer.
This commit is contained in:
23rd 2021-10-20 19:54:22 +03:00
parent 36d6682122
commit 159beb138a
15 changed files with 294 additions and 213 deletions

View File

@ -126,6 +126,8 @@ PRIVATE
api/api_invite_links.h
api/api_media.cpp
api/api_media.h
api/api_peer_photo.cpp
api/api_peer_photo.h
api/api_self_destruct.cpp
api/api_self_destruct.h
api/api_send_progress.cpp

View File

@ -0,0 +1,212 @@
/*
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 "api/api_peer_photo.h"
#include "api/api_updates.h"
#include "apiwrap.h"
#include "base/random.h"
#include "base/unixtime.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_peer.h"
#include "data/data_photo.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "history/history.h"
#include "main/main_session.h"
#include "storage/file_upload.h"
#include "storage/localimageloader.h"
#include "storage/storage_user_photos.h"
#include <QtCore/QBuffer>
namespace Api {
namespace {
SendMediaReady PreparePeerPhoto(
MTP::DcId dcId,
PeerId peerId,
QImage &&image) {
PreparedPhotoThumbs photoThumbs;
QVector<MTPPhotoSize> photoSizes;
QByteArray jpeg;
QBuffer jpegBuffer(&jpeg);
image.save(&jpegBuffer, "JPG", 87);
const auto scaled = [&](int size) {
return image.scaled(
size,
size,
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
};
const auto push = [&](
const char *type,
QImage &&image,
QByteArray bytes = QByteArray()) {
photoSizes.push_back(MTP_photoSize(
MTP_string(type),
MTP_int(image.width()),
MTP_int(image.height()), MTP_int(0)));
photoThumbs.emplace(type[0], PreparedPhotoThumb{
.image = std::move(image),
.bytes = std::move(bytes)
});
};
push("a", scaled(160));
push("b", scaled(320));
push("c", std::move(image), jpeg);
const auto id = base::RandomValue<PhotoId>();
const auto photo = MTP_photo(
MTP_flags(0),
MTP_long(id),
MTP_long(0),
MTP_bytes(),
MTP_int(base::unixtime::now()),
MTP_vector<MTPPhotoSize>(photoSizes),
MTPVector<MTPVideoSize>(),
MTP_int(dcId));
QString file, filename;
int32 filesize = 0;
QByteArray data;
return SendMediaReady(
SendMediaType::Photo,
file,
filename,
filesize,
data,
id,
id,
u"jpg"_q,
peerId,
photo,
photoThumbs,
MTP_documentEmpty(MTP_long(0)),
jpeg,
0);
}
} // namespace
PeerPhoto::PeerPhoto(not_null<ApiWrap*> api)
: _session(&api->session())
, _api(&api->instance()) {
crl::on_main(_session, [=] {
// You can't use _session->lifetime() in the constructor,
// only queued, because it is not constructed yet.
_session->uploader().photoReady(
) | rpl::start_with_next([=](const Storage::UploadedPhoto &data) {
ready(data.fullId, data.file);
}, _session->lifetime());
});
}
void PeerPhoto::upload(not_null<PeerData*> peer, QImage &&image) {
peer = peer->migrateToOrMe();
const auto ready = PreparePeerPhoto(
_api.instance().mainDcId(),
peer->id,
std::move(image));
const auto fakeId = FullMsgId(
peerToChannel(peer->id),
_session->data().nextLocalMessageId());
const auto already = ranges::find(
_uploads,
peer,
[](const auto &pair) { return pair.second; });
if (already != end(_uploads)) {
_session->uploader().cancel(already->first);
_uploads.erase(already);
}
_uploads.emplace(fakeId, peer);
_session->uploader().uploadMedia(fakeId, ready);
}
void PeerPhoto::clear(not_null<PhotoData*> photo) {
const auto self = _session->user();
if (self->userpicPhotoId() == photo->id) {
_api.request(MTPphotos_UpdateProfilePhoto(
MTP_inputPhotoEmpty()
)).done([=](const MTPphotos_Photo &result) {
self->setPhoto(MTP_userProfilePhotoEmpty());
}).send();
} else if (photo->peer && photo->peer->userpicPhotoId() == photo->id) {
const auto applier = [=](const MTPUpdates &result) {
_session->updates().applyUpdates(result);
};
if (const auto chat = photo->peer->asChat()) {
_api.request(MTPmessages_EditChatPhoto(
chat->inputChat,
MTP_inputChatPhotoEmpty()
)).done(applier).send();
} else if (const auto channel = photo->peer->asChannel()) {
_api.request(MTPchannels_EditPhoto(
channel->inputChannel,
MTP_inputChatPhotoEmpty()
)).done(applier).send();
}
} else {
_api.request(MTPphotos_DeletePhotos(
MTP_vector<MTPInputPhoto>(1, photo->mtpInput())
)).send();
_session->storage().remove(Storage::UserPhotosRemoveOne(
peerToUser(self->id),
photo->id));
}
}
void PeerPhoto::ready(const FullMsgId &msgId, const MTPInputFile &file) {
const auto maybePeer = _uploads.take(msgId);
if (!maybePeer) {
return;
}
const auto peer = *maybePeer;
const auto applier = [=](const MTPUpdates &result) {
_session->updates().applyUpdates(result);
};
if (peer->isSelf()) {
_api.request(MTPphotos_UploadProfilePhoto(
MTP_flags(MTPphotos_UploadProfilePhoto::Flag::f_file),
file,
MTPInputFile(), // video
MTPdouble() // video_start_ts
)).done([=](const MTPphotos_Photo &result) {
result.match([&](const MTPDphotos_photo &data) {
_session->data().processPhoto(data.vphoto());
_session->data().processUsers(data.vusers());
});
}).send();
} else if (const auto chat = peer->asChat()) {
const auto history = _session->data().history(chat);
history->sendRequestId = _api.request(MTPmessages_EditChatPhoto(
chat->inputChat,
MTP_inputChatUploadedPhoto(
MTP_flags(MTPDinputChatUploadedPhoto::Flag::f_file),
file,
MTPInputFile(), // video
MTPdouble()) // video_start_ts
)).done(applier).afterRequest(history->sendRequestId).send();
} else if (const auto channel = peer->asChannel()) {
const auto history = _session->data().history(channel);
history->sendRequestId = _api.request(MTPchannels_EditPhoto(
channel->inputChannel,
MTP_inputChatUploadedPhoto(
MTP_flags(MTPDinputChatUploadedPhoto::Flag::f_file),
file,
MTPInputFile(), // video
MTPdouble()) // video_start_ts
)).done(applier).afterRequest(history->sendRequestId).send();
}
}
} // namespace Api

View File

@ -0,0 +1,38 @@
/*
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 "mtproto/sender.h"
class ApiWrap;
class PeerData;
namespace Main {
class Session;
} // namespace Main
namespace Api {
class PeerPhoto final {
public:
explicit PeerPhoto(not_null<ApiWrap*> api);
void upload(not_null<PeerData*> peer, QImage &&image);
void clear(not_null<PhotoData*> photo);
private:
void ready(const FullMsgId &msgId, const MTPInputFile &file);
const not_null<Main::Session*> _session;
MTP::Sender _api;
base::flat_map<FullMsgId, not_null<PeerData*>> _uploads;
};
} // namespace Api

View File

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_hash.h"
#include "api/api_invite_links.h"
#include "api/api_media.h"
#include "api/api_peer_photo.h"
#include "api/api_sending.h"
#include "api/api_text_entities.h"
#include "api/api_self_destruct.h"
@ -145,15 +146,11 @@ ApiWrap::ApiWrap(not_null<Main::Session*> session)
, _userPrivacy(std::make_unique<Api::UserPrivacy>(this))
, _inviteLinks(std::make_unique<Api::InviteLinks>(this))
, _views(std::make_unique<Api::ViewsManager>(this))
, _confirmPhone(std::make_unique<Api::ConfirmPhone>(this)) {
, _confirmPhone(std::make_unique<Api::ConfirmPhone>(this))
, _peerPhoto(std::make_unique<Api::PeerPhoto>(this)) {
crl::on_main(session, [=] {
// You can't use _session->lifetime() in the constructor,
// only queued, because it is not constructed yet.
_session->uploader().photoReady(
) | rpl::start_with_next([=](const Storage::UploadedPhoto &data) {
photoUploadReady(data.fullId, data.file);
}, _session->lifetime());
_session->data().chatsFilters().changed(
) | rpl::filter([=] {
return _session->data().chatsFilters().archiveNeeded();
@ -4501,105 +4498,6 @@ FileLoadTo ApiWrap::fileLoadTaskOptions(const SendAction &action) const {
action.replaceMediaOf);
}
void ApiWrap::uploadPeerPhoto(not_null<PeerData*> peer, QImage &&image) {
peer = peer->migrateToOrMe();
const auto ready = PreparePeerPhoto(
instance().mainDcId(),
peer->id,
std::move(image));
const auto fakeId = FullMsgId(
peerToChannel(peer->id),
_session->data().nextLocalMessageId());
const auto already = ranges::find(
_peerPhotoUploads,
peer,
[](const auto &pair) { return pair.second; });
if (already != end(_peerPhotoUploads)) {
_session->uploader().cancel(already->first);
_peerPhotoUploads.erase(already);
}
_peerPhotoUploads.emplace(fakeId, peer);
_session->uploader().uploadMedia(fakeId, ready);
}
void ApiWrap::photoUploadReady(
const FullMsgId &msgId,
const MTPInputFile &file) {
if (const auto maybePeer = _peerPhotoUploads.take(msgId)) {
const auto peer = *maybePeer;
const auto applier = [=](const MTPUpdates &result) {
applyUpdates(result);
};
if (peer->isSelf()) {
request(MTPphotos_UploadProfilePhoto(
MTP_flags(MTPphotos_UploadProfilePhoto::Flag::f_file),
file,
MTPInputFile(), // video
MTPdouble() // video_start_ts
)).done([=](const MTPphotos_Photo &result) {
result.match([&](const MTPDphotos_photo &data) {
_session->data().processPhoto(data.vphoto());
_session->data().processUsers(data.vusers());
});
}).send();
} else if (const auto chat = peer->asChat()) {
const auto history = _session->data().history(chat);
history->sendRequestId = request(MTPmessages_EditChatPhoto(
chat->inputChat,
MTP_inputChatUploadedPhoto(
MTP_flags(MTPDinputChatUploadedPhoto::Flag::f_file),
file,
MTPInputFile(), // video
MTPdouble()) // video_start_ts
)).done(applier).afterRequest(history->sendRequestId).send();
} else if (const auto channel = peer->asChannel()) {
const auto history = _session->data().history(channel);
history->sendRequestId = request(MTPchannels_EditPhoto(
channel->inputChannel,
MTP_inputChatUploadedPhoto(
MTP_flags(MTPDinputChatUploadedPhoto::Flag::f_file),
file,
MTPInputFile(), // video
MTPdouble()) // video_start_ts
)).done(applier).afterRequest(history->sendRequestId).send();
}
}
}
void ApiWrap::clearPeerPhoto(not_null<PhotoData*> photo) {
const auto self = _session->user();
if (self->userpicPhotoId() == photo->id) {
request(MTPphotos_UpdateProfilePhoto(
MTP_inputPhotoEmpty()
)).done([=](const MTPphotos_Photo &result) {
self->setPhoto(MTP_userProfilePhotoEmpty());
}).send();
} else if (photo->peer && photo->peer->userpicPhotoId() == photo->id) {
const auto applier = [=](const MTPUpdates &result) {
applyUpdates(result);
};
if (const auto chat = photo->peer->asChat()) {
request(MTPmessages_EditChatPhoto(
chat->inputChat,
MTP_inputChatPhotoEmpty()
)).done(applier).send();
} else if (const auto channel = photo->peer->asChannel()) {
request(MTPchannels_EditPhoto(
channel->inputChannel,
MTP_inputChatPhotoEmpty()
)).done(applier).send();
}
} else {
request(MTPphotos_DeletePhotos(
MTP_vector<MTPInputPhoto>(1, photo->mtpInput())
)).send();
_session->storage().remove(Storage::UserPhotosRemoveOne(
peerToUser(self->id),
photo->id));
}
}
void ApiWrap::reloadContactSignupSilent() {
if (_contactSignupSilentRequestId) {
return;
@ -4710,6 +4608,10 @@ Api::ConfirmPhone &ApiWrap::confirmPhone() {
return *_confirmPhone;
}
Api::PeerPhoto &ApiWrap::peerPhoto() {
return *_peerPhoto;
}
void ApiWrap::createPoll(
const PollData &data,
const SendAction &action,

View File

@ -64,6 +64,7 @@ class UserPrivacy;
class InviteLinks;
class ViewsManager;
class ConfirmPhone;
class PeerPhoto;
namespace details {
@ -383,9 +384,6 @@ public:
uint64 randomId = 0,
FullMsgId itemId = FullMsgId());
void uploadPeerPhoto(not_null<PeerData*> peer, QImage &&image);
void clearPeerPhoto(not_null<PhotoData*> photo);
void reloadContactSignupSilent();
rpl::producer<bool> contactSignupSilent() const;
std::optional<bool> contactSignupSilentCurrent() const;
@ -404,6 +402,7 @@ public:
[[nodiscard]] Api::InviteLinks &inviteLinks();
[[nodiscard]] Api::ViewsManager &views();
[[nodiscard]] Api::ConfirmPhone &confirmPhone();
[[nodiscard]] Api::PeerPhoto &peerPhoto();
void createPoll(
const PollData &data,
@ -576,8 +575,6 @@ private:
FileReferencesHandler &&handler,
Request &&data);
void photoUploadReady(const FullMsgId &msgId, const MTPInputFile &file);
void migrateDone(
not_null<PeerData*> peer,
not_null<ChannelData*> channel);
@ -708,8 +705,6 @@ private:
std::vector<FnMut<void(const MTPUser &)>> _supportContactCallbacks;
base::flat_map<FullMsgId, not_null<PeerData*>> _peerPhotoUploads;
struct {
mtpRequestId requestId = 0;
QString requestedText;
@ -726,6 +721,7 @@ private:
const std::unique_ptr<Api::InviteLinks> _inviteLinks;
const std::unique_ptr<Api::ViewsManager> _views;
const std::unique_ptr<Api::ConfirmPhone> _confirmPhone;
const std::unique_ptr<Api::PeerPhoto> _peerPhoto;
base::flat_map<FullMsgId, mtpRequestId> _pollVotesRequestIds;
base::flat_map<FullMsgId, mtpRequestId> _pollCloseRequestIds;

View File

@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_cloud_file.h"
#include "apiwrap.h"
#include "api/api_invite_links.h"
#include "api/api_peer_photo.h"
#include "main/main_session.h"
#include "facades.h"
#include "styles/style_layers.h"
@ -89,7 +90,7 @@ void ChatCreateDone(
}
| [&](not_null<ChatData*> chat) {
if (!image.isNull()) {
chat->session().api().uploadPeerPhoto(
chat->session().api().peerPhoto().upload(
chat,
std::move(image));
}
@ -741,7 +742,7 @@ void GroupInfoBox::createChannel(
| [&](not_null<ChannelData*> channel) {
auto image = _photo->takeResultImage();
if (!image.isNull()) {
channel->session().api().uploadPeerPhoto(
channel->session().api().peerPhoto().upload(
channel,
std::move(image));
}

View File

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peers/edit_peer_info_box.h"
#include "apiwrap.h"
#include "api/api_peer_photo.h"
#include "main/main_session.h"
#include "boxes/add_contact_box.h"
#include "ui/boxes/confirm_box.h"
@ -1469,7 +1470,7 @@ void Controller::savePhoto() {
? _controls.photo->takeResultImage()
: QImage();
if (!image.isNull()) {
_peer->session().api().uploadPeerPhoto(_peer, std::move(image));
_peer->session().api().peerPhoto().upload(_peer, std::move(image));
}
_box->closeBox();
}

View File

@ -7,8 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "info/profile/info_profile_cover.h"
#include <rpl/never.h>
#include <rpl/combine.h>
#include "data/data_photo.h"
#include "data/data_peer_values.h"
#include "data/data_channel.h"
@ -29,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h"
#include "main/main_session.h"
#include "apiwrap.h"
#include "api/api_peer_photo.h"
#include "styles/style_boxes.h"
#include "styles/style_info.h"
@ -273,7 +272,7 @@ Cover::Cover(
_userpic->uploadPhotoRequests(
) | rpl::start_with_next([=] {
_peer->session().api().uploadPeerPhoto(
_peer->session().api().peerPhoto().upload(
_peer,
_userpic->takeResultImage());
}, _userpic->lifetime());

View File

@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h"
#include "core/core_settings.h"
#include "apiwrap.h"
#include "api/api_peer_photo.h"
#include "mainwindow.h"
#include "ui/boxes/confirm_box.h"
#include "ui/text/text_utilities.h"
@ -197,7 +198,7 @@ void Step::createSession(
session.saveSettingsDelayed();
}
if (!photo.isNull()) {
session.api().uploadPeerPhoto(session.user(), std::move(photo));
session.api().peerPhoto().upload(session.user(), std::move(photo));
}
if (session.supportMode()) {
PrepareSupportMode(&session);

View File

@ -30,7 +30,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_file_origin.h"
#include "data/data_histories.h"
#include "data/stickers/data_stickers.h"
#include "api/api_text_entities.h"
#include "ui/chat/chat_theme.h"
#include "ui/special_buttons.h"
#include "ui/widgets/buttons.h"
@ -222,7 +221,6 @@ MainWidget::MainWidget(
not_null<Window::SessionController*> controller)
: RpWidget(parent)
, _controller(controller)
, _api(&controller->session().mtp())
, _dialogsWidth(st::columnMinimalWidthLeft)
, _thirdColumnWidth(st::columnMinimalWidthThird)
, _sideShadow(this)
@ -726,17 +724,6 @@ void MainWidget::showSendPathsLayer() {
}
}
void MainWidget::deletePhotoLayer(PhotoData *photo) {
if (!photo) return;
Ui::show(Box<Ui::ConfirmBox>(
tr::lng_delete_photo_sure(tr::now),
tr::lng_box_delete(tr::now),
crl::guard(this, [=] {
session().api().clearPeerPhoto(photo);
Ui::hideLayer();
})));
}
void MainWidget::shareUrlLayer(const QString &url, const QString &text) {
// Don't allow to insert an inline bot query by share url link.
if (url.trimmed().startsWith('@')) {

View File

@ -174,8 +174,6 @@ public:
void onFilesOrForwardDrop(const PeerId &peer, const QMimeData *data);
bool selectingPeer() const;
void deletePhotoLayer(PhotoData *photo);
void sendBotCommand(Bot::SendCommandRequest request);
void hideSingleUseKeyboard(PeerData *peer, MsgId replyTo);
bool insertBotCommand(const QString &cmd);
@ -328,7 +326,6 @@ private:
bool isThreeColumn() const;
const not_null<Window::SessionController*> _controller;
MTP::Sender _api;
Ui::Animations::Simple _a_show;
bool _showBack = false;

View File

@ -9,8 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "api/api_attached_stickers.h"
#include "api/api_peer_photo.h"
#include "lang/lang_keys.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "core/application.h"
#include "core/click_handler_types.h"
@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/ui_utility.h"
#include "ui/cached_round_corners.h"
#include "ui/gl/gl_surface.h"
#include "ui/boxes/confirm_box.h"
#include "boxes/delete_messages_box.h"
#include "media/audio/media_audio.h"
#include "media/view/media_view_playback_controls.h"
@ -1733,16 +1734,25 @@ void OverlayWidget::deleteMedia() {
}();
close();
Core::App().domain().activate(&session->account());
const auto &active = session->windows();
if (active.empty()) {
return;
}
if (deletingPeerPhoto) {
active.front()->content()->deletePhotoLayer(photo);
} else if (const auto item = session->data().message(msgid)) {
const auto suggestModerateActions = true;
Ui::show(Box<DeleteMessagesBox>(item, suggestModerateActions));
if (const auto window = findWindow()) {
if (deletingPeerPhoto) {
if (photo) {
window->show(
Box<Ui::ConfirmBox>(
tr::lng_delete_photo_sure(tr::now),
tr::lng_box_delete(tr::now),
crl::guard(_widget, [=] {
session->api().peerPhoto().clear(photo);
Ui::hideLayer();
})),
Ui::LayerOption::CloseOther);
}
} else if (const auto item = session->data().message(msgid)) {
const auto suggestModerateActions = true;
window->show(
Box<DeleteMessagesBox>(item, suggestModerateActions),
Ui::LayerOption::CloseOther);
}
}
}

View File

@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "window/window_session_controller.h"
#include "apiwrap.h"
#include "api/api_peer_photo.h"
#include "core/file_utilities.h"
#include "base/call_delayed.h"
#include "styles/style_layers.h"
@ -63,7 +64,7 @@ void SetupPhoto(
upload->setFullRadius(true);
upload->addClickHandler([=] {
auto callback = [=](QImage &&image) {
self->session().api().uploadPeerPhoto(self, std::move(image));
self->session().api().peerPhoto().upload(self, std::move(image));
};
Editor::PrepareProfilePhoto(
upload,

View File

@ -239,70 +239,6 @@ SendMediaReady::SendMediaReady(
}
}
SendMediaReady PreparePeerPhoto(MTP::DcId dcId, PeerId peerId, QImage &&image) {
PreparedPhotoThumbs photoThumbs;
QVector<MTPPhotoSize> photoSizes;
QByteArray jpeg;
QBuffer jpegBuffer(&jpeg);
image.save(&jpegBuffer, "JPG", 87);
const auto scaled = [&](int size) {
return image.scaled(
size,
size,
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
};
const auto push = [&](
const char *type,
QImage &&image,
QByteArray bytes = QByteArray()) {
photoSizes.push_back(MTP_photoSize(
MTP_string(type),
MTP_int(image.width()),
MTP_int(image.height()), MTP_int(0)));
photoThumbs.emplace(type[0], PreparedPhotoThumb{
.image = std::move(image),
.bytes = std::move(bytes)
});
};
push("a", scaled(160));
push("b", scaled(320));
push("c", std::move(image), jpeg);
const auto id = base::RandomValue<PhotoId>();
const auto photo = MTP_photo(
MTP_flags(0),
MTP_long(id),
MTP_long(0),
MTP_bytes(),
MTP_int(base::unixtime::now()),
MTP_vector<MTPPhotoSize>(photoSizes),
MTPVector<MTPVideoSize>(),
MTP_int(dcId));
QString file, filename;
int32 filesize = 0;
QByteArray data;
return SendMediaReady(
SendMediaType::Photo,
file,
filename,
filesize,
data,
id,
id,
qsl("jpg"),
peerId,
photo,
photoThumbs,
MTP_documentEmpty(MTP_long(0)),
jpeg,
0);
}
TaskQueue::TaskQueue(crl::time stopTimeoutMs) {
if (stopTimeoutMs > 0) {
_stopTimer = new QTimer(this);

View File

@ -98,8 +98,6 @@ struct SendMediaReady {
};
SendMediaReady PreparePeerPhoto(MTP::DcId dcId, PeerId peerId, QImage &&image);
using TaskId = void*; // no interface, just id
class Task {