| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- /*
- 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_editing.h"
- #include "apiwrap.h"
- #include "api/api_media.h"
- #include "api/api_text_entities.h"
- #include "ui/boxes/confirm_box.h"
- #include "data/business/data_shortcut_messages.h"
- #include "data/components/scheduled_messages.h"
- #include "data/data_file_origin.h"
- #include "data/data_histories.h"
- #include "data/data_session.h"
- #include "data/data_web_page.h"
- #include "history/view/controls/history_view_compose_media_edit_manager.h"
- #include "history/history.h"
- #include "lang/lang_keys.h"
- #include "main/main_session.h"
- #include "mtproto/mtproto_response.h"
- #include "boxes/abstract_box.h" // Ui::show().
- namespace Api {
- namespace {
- using namespace rpl::details;
- template <typename T>
- constexpr auto WithId
- = is_callable_plain_v<T, Fn<void()>, mtpRequestId>;
- template <typename T>
- constexpr auto WithoutId
- = is_callable_plain_v<T, Fn<void()>>;
- template <typename T>
- constexpr auto WithoutCallback
- = is_callable_plain_v<T>;
- template <typename T>
- constexpr auto ErrorWithId
- = is_callable_plain_v<T, QString, mtpRequestId>;
- template <typename T>
- constexpr auto ErrorWithoutId
- = is_callable_plain_v<T, QString>;
- template <typename DoneCallback, typename FailCallback>
- mtpRequestId EditMessage(
- not_null<HistoryItem*> item,
- const TextWithEntities &textWithEntities,
- Data::WebPageDraft webpage,
- SendOptions options,
- DoneCallback &&done,
- FailCallback &&fail,
- std::optional<MTPInputMedia> inputMedia = std::nullopt) {
- const auto session = &item->history()->session();
- const auto api = &session->api();
- const auto text = textWithEntities.text;
- const auto sentEntities = EntitiesToMTP(
- session,
- textWithEntities.entities,
- ConvertOption::SkipLocal);
- const auto media = item->media();
- const auto updateRecentStickers = inputMedia.has_value()
- ? Api::HasAttachedStickers(*inputMedia)
- : false;
- const auto emptyFlag = MTPmessages_EditMessage::Flag(0);
- const auto flags = emptyFlag
- | ((!text.isEmpty() || media)
- ? MTPmessages_EditMessage::Flag::f_message
- : emptyFlag)
- | ((media && inputMedia.has_value())
- ? MTPmessages_EditMessage::Flag::f_media
- : emptyFlag)
- | (webpage.removed
- ? MTPmessages_EditMessage::Flag::f_no_webpage
- : emptyFlag)
- | ((!webpage.removed && !webpage.url.isEmpty())
- ? MTPmessages_EditMessage::Flag::f_media
- : emptyFlag)
- | (((!webpage.removed && !webpage.url.isEmpty() && webpage.invert)
- || options.invertCaption)
- ? MTPmessages_EditMessage::Flag::f_invert_media
- : emptyFlag)
- | (!sentEntities.v.isEmpty()
- ? MTPmessages_EditMessage::Flag::f_entities
- : emptyFlag)
- | (options.scheduled
- ? MTPmessages_EditMessage::Flag::f_schedule_date
- : emptyFlag)
- | (item->isBusinessShortcut()
- ? MTPmessages_EditMessage::Flag::f_quick_reply_shortcut_id
- : emptyFlag);
- const auto id = item->isScheduled()
- ? session->scheduledMessages().lookupId(item)
- : item->isBusinessShortcut()
- ? session->data().shortcutMessages().lookupId(item)
- : item->id;
- return api->request(MTPmessages_EditMessage(
- MTP_flags(flags),
- item->history()->peer->input,
- MTP_int(id),
- MTP_string(text),
- inputMedia.value_or(Data::WebPageForMTP(webpage, text.isEmpty())),
- MTPReplyMarkup(),
- sentEntities,
- MTP_int(options.scheduled),
- MTP_int(item->shortcutId())
- )).done([=](
- const MTPUpdates &result,
- [[maybe_unused]] mtpRequestId requestId) {
- const auto apply = [=] { api->applyUpdates(result); };
- if constexpr (WithId<DoneCallback>) {
- done(apply, requestId);
- } else if constexpr (WithoutId<DoneCallback>) {
- done(apply);
- } else if constexpr (WithoutCallback<DoneCallback>) {
- done();
- apply();
- } else {
- t_bad_callback(done);
- }
- if (updateRecentStickers) {
- api->requestSpecialStickersForce(false, false, true);
- }
- }).fail([=](const MTP::Error &error, mtpRequestId requestId) {
- if constexpr (ErrorWithId<FailCallback>) {
- fail(error.type(), requestId);
- } else if constexpr (ErrorWithoutId<FailCallback>) {
- fail(error.type());
- } else if constexpr (WithoutCallback<FailCallback>) {
- fail();
- } else {
- t_bad_callback(fail);
- }
- }).send();
- }
- template <typename DoneCallback, typename FailCallback>
- mtpRequestId EditMessage(
- not_null<HistoryItem*> item,
- SendOptions options,
- DoneCallback &&done,
- FailCallback &&fail,
- std::optional<MTPInputMedia> inputMedia = std::nullopt) {
- const auto &text = item->originalText();
- const auto webpage = (!item->media() || !item->media()->webpage())
- ? Data::WebPageDraft{ .removed = true }
- : Data::WebPageDraft::FromItem(item);
- return EditMessage(
- item,
- text,
- webpage,
- options,
- std::forward<DoneCallback>(done),
- std::forward<FailCallback>(fail),
- inputMedia);
- }
- void EditMessageWithUploadedMedia(
- not_null<HistoryItem*> item,
- SendOptions options,
- MTPInputMedia media) {
- const auto done = [=](Fn<void()> applyUpdates) {
- if (item) {
- item->removeFromSharedMediaIndex();
- item->clearSavedMedia();
- item->setIsLocalUpdateMedia(true);
- applyUpdates();
- item->setIsLocalUpdateMedia(false);
- }
- };
- const auto fail = [=](const QString &error) {
- const auto session = &item->history()->session();
- const auto notModified = (error == u"MESSAGE_NOT_MODIFIED"_q);
- const auto mediaInvalid = (error == u"MEDIA_NEW_INVALID"_q);
- if (notModified || mediaInvalid) {
- item->returnSavedMedia();
- session->data().sendHistoryChangeNotifications();
- if (mediaInvalid) {
- Ui::show(
- Ui::MakeInformBox(tr::lng_edit_media_invalid_file()),
- Ui::LayerOption::KeepOther);
- }
- } else {
- session->api().sendMessageFail(error, item->history()->peer);
- }
- };
- EditMessage(item, options, done, fail, media);
- }
- } // namespace
- void RescheduleMessage(
- not_null<HistoryItem*> item,
- SendOptions options) {
- const auto empty = [] {};
- options.invertCaption = item->invertMedia();
- EditMessage(item, options, empty, empty);
- }
- void EditMessageWithUploadedDocument(
- HistoryItem *item,
- RemoteFileInfo info,
- SendOptions options) {
- if (!item || !item->media() || !item->media()->document()) {
- return;
- }
- EditMessageWithUploadedMedia(
- item,
- options,
- PrepareUploadedDocument(item, std::move(info)));
- }
- void EditMessageWithUploadedPhoto(
- HistoryItem *item,
- RemoteFileInfo info,
- SendOptions options) {
- if (!item || !item->media() || !item->media()->photo()) {
- return;
- }
- EditMessageWithUploadedMedia(
- item,
- options,
- PrepareUploadedPhoto(item, std::move(info)));
- }
- mtpRequestId EditCaption(
- not_null<HistoryItem*> item,
- const TextWithEntities &caption,
- SendOptions options,
- Fn<void()> done,
- Fn<void(const QString &)> fail) {
- return EditMessage(
- item,
- caption,
- Data::WebPageDraft(),
- options,
- done,
- fail);
- }
- mtpRequestId EditTextMessage(
- not_null<HistoryItem*> item,
- const TextWithEntities &caption,
- Data::WebPageDraft webpage,
- SendOptions options,
- Fn<void(mtpRequestId requestId)> done,
- Fn<void(const QString &error, mtpRequestId requestId)> fail,
- bool spoilered) {
- const auto media = item->media();
- if (media
- && HistoryView::MediaEditManager::CanBeSpoilered(item)
- && spoilered != media->hasSpoiler()) {
- auto takeInputMedia = Fn<std::optional<MTPInputMedia>()>(nullptr);
- auto takeFileReference = Fn<QByteArray()>(nullptr);
- if (const auto photo = media->photo()) {
- using Flag = MTPDinputMediaPhoto::Flag;
- const auto flags = Flag()
- | (media->ttlSeconds() ? Flag::f_ttl_seconds : Flag())
- | (spoilered ? Flag::f_spoiler : Flag());
- takeInputMedia = [=] {
- return MTP_inputMediaPhoto(
- MTP_flags(flags),
- photo->mtpInput(),
- MTP_int(media->ttlSeconds()));
- };
- takeFileReference = [=] { return photo->fileReference(); };
- } else if (const auto document = media->document()) {
- using Flag = MTPDinputMediaDocument::Flag;
- const auto videoCover = media->videoCover();
- const auto videoTimestamp = media->videoTimestamp();
- const auto flags = Flag()
- | (media->ttlSeconds() ? Flag::f_ttl_seconds : Flag())
- | (spoilered ? Flag::f_spoiler : Flag())
- | (videoTimestamp ? Flag::f_video_timestamp : Flag())
- | (videoCover ? Flag::f_video_cover : Flag());
- takeInputMedia = [=] {
- return MTP_inputMediaDocument(
- MTP_flags(flags),
- document->mtpInput(),
- (videoCover
- ? videoCover->mtpInput()
- : MTPInputPhoto()),
- MTP_int(media->ttlSeconds()),
- MTP_int(videoTimestamp),
- MTPstring()); // query
- };
- takeFileReference = [=] { return document->fileReference(); };
- }
- const auto usedFileReference = takeFileReference
- ? takeFileReference()
- : QByteArray();
- const auto origin = item->fullId();
- const auto api = &item->history()->session().api();
- const auto performRequest = [=](
- const auto &repeatRequest,
- mtpRequestId originalRequestId) -> mtpRequestId {
- const auto handleReference = [=](
- const QString &error,
- mtpRequestId requestId) {
- if (error.startsWith(u"FILE_REFERENCE_"_q)) {
- api->refreshFileReference(origin, [=](const auto &) {
- if (takeFileReference &&
- (takeFileReference() != usedFileReference)) {
- repeatRequest(
- repeatRequest,
- originalRequestId
- ? originalRequestId
- : requestId);
- } else {
- fail(error, requestId);
- }
- });
- } else {
- fail(error, requestId);
- }
- };
- const auto callback = [=](
- Fn<void()> applyUpdates,
- mtpRequestId requestId) {
- applyUpdates();
- done(originalRequestId ? originalRequestId : requestId);
- };
- const auto requestId = EditMessage(
- item,
- caption,
- webpage,
- options,
- callback,
- handleReference,
- takeInputMedia ? takeInputMedia() : std::nullopt);
- return originalRequestId ? originalRequestId : requestId;
- };
- return performRequest(performRequest, 0);
- }
- const auto callback = [=](Fn<void()> applyUpdates, mtpRequestId id) {
- applyUpdates();
- done(id);
- };
- return EditMessage(
- item,
- caption,
- webpage,
- options,
- callback,
- fail,
- std::nullopt);
- }
- } // namespace Api
|