/* 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/data_histories.h" #include "data/data_scheduled_messages.h" #include "data/data_session.h" #include "data/data_web_page.h" #include "history/history.h" #include "history/history_item.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 constexpr auto WithId = is_callable_plain_v, mtpRequestId>; template constexpr auto WithoutId = is_callable_plain_v>; template constexpr auto WithoutCallback = is_callable_plain_v; template constexpr auto ErrorWithId = is_callable_plain_v; template constexpr auto ErrorWithoutId = is_callable_plain_v; template mtpRequestId EditMessage( not_null item, const TextWithEntities &textWithEntities, Data::WebPageDraft webpage, SendOptions options, DoneCallback &&done, FailCallback &&fail, std::optional 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) ? 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); const auto id = item->isScheduled() ? session->data().scheduledMessages().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) )).done([=]( const MTPUpdates &result, [[maybe_unused]] mtpRequestId requestId) { const auto apply = [=] { api->applyUpdates(result); }; if constexpr (WithId) { done(apply, requestId); } else if constexpr (WithoutId) { done(apply); } else if constexpr (WithoutCallback) { done(); apply(); } else { t_bad_callback(done); } if (updateRecentStickers) { api->requestRecentStickersForce(true); } }).fail([=](const MTP::Error &error, mtpRequestId requestId) { if constexpr (ErrorWithId) { fail(error.type(), requestId); } else if constexpr (ErrorWithoutId) { fail(error.type()); } else if constexpr (WithoutCallback) { fail(); } else { t_bad_callback(fail); } }).send(); } template mtpRequestId EditMessage( not_null item, SendOptions options, DoneCallback &&done, FailCallback &&fail, std::optional inputMedia = std::nullopt) { const auto &text = item->originalText(); const auto webpage = (!item->media() || !item->media()->webpage()) ? Data::WebPageDraft{ .removed = true } : Data::WebPageDraft{ .id = item->media()->webpage()->id, }; return EditMessage( item, text, webpage, options, std::forward(done), std::forward(fail), inputMedia); } void EditMessageWithUploadedMedia( not_null item, SendOptions options, MTPInputMedia media) { const auto done = [=](Fn 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 item, SendOptions options) { const auto empty = [] {}; 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 item, const TextWithEntities &caption, SendOptions options, Fn done, Fn fail) { return EditMessage( item, caption, Data::WebPageDraft(), options, done, fail); } mtpRequestId EditTextMessage( not_null item, const TextWithEntities &caption, Data::WebPageDraft webpage, SendOptions options, Fn done, Fn fail) { const auto callback = [=](Fn applyUpdates, mtpRequestId id) { applyUpdates(); done(id); }; return EditMessage(item, caption, webpage, options, callback, fail); } } // namespace Api