diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 93b4830871..7bfd6ff7a7 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -215,6 +215,8 @@ PRIVATE api/api_hash.h api/api_self_destruct.cpp api/api_self_destruct.h + api/api_send_progress.cpp + api/api_send_progress.h api/api_sending.cpp api/api_sending.h api/api_sensitive_content.cpp diff --git a/Telegram/SourceFiles/api/api_send_progress.cpp b/Telegram/SourceFiles/api/api_send_progress.cpp new file mode 100644 index 0000000000..6666bde2e7 --- /dev/null +++ b/Telegram/SourceFiles/api/api_send_progress.cpp @@ -0,0 +1,105 @@ +/* +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_send_progress.h" + +#include "main/main_session.h" +#include "history/history.h" +#include "data/data_peer.h" +#include "apiwrap.h" + +namespace Api { +namespace { + +constexpr auto kCancelTypingActionTimeout = crl::time(5000); + +} // namespace + +SendProgressManager::SendProgressManager(not_null session) +: _session(session) +, _stopTypingTimer([=] { cancelTyping(base::take(_stopTypingHistory)); }) { +} + +void SendProgressManager::cancel( + not_null history, + SendProgressType type) { + const auto i = _requests.find({ history, type }); + if (i != _requests.end()) { + _session->api().request(i->second).cancel(); + _requests.erase(i); + } +} + +void SendProgressManager::cancelTyping(not_null history) { + _stopTypingTimer.cancel(); + cancel(history, SendProgressType::Typing); +} + +void SendProgressManager::update( + not_null history, + SendProgressType type, + int32 progress) { + const auto peer = history->peer; + if (peer->isSelf() || (peer->isChannel() && !peer->isMegagroup())) { + return; + } + + const auto doing = (progress >= 0); + if (history->mySendActionUpdated(type, doing)) { + cancel(history, type); + if (doing) { + send(history, type, progress); + } + } +} + +void SendProgressManager::send( + not_null history, + SendProgressType type, + int32 progress) { + using Type = SendProgressType; + MTPsendMessageAction action; + switch (type) { + case Type::Typing: action = MTP_sendMessageTypingAction(); break; + case Type::RecordVideo: action = MTP_sendMessageRecordVideoAction(); break; + case Type::UploadVideo: action = MTP_sendMessageUploadVideoAction(MTP_int(progress)); break; + case Type::RecordVoice: action = MTP_sendMessageRecordAudioAction(); break; + case Type::UploadVoice: action = MTP_sendMessageUploadAudioAction(MTP_int(progress)); break; + case Type::RecordRound: action = MTP_sendMessageRecordRoundAction(); break; + case Type::UploadRound: action = MTP_sendMessageUploadRoundAction(MTP_int(progress)); break; + case Type::UploadPhoto: action = MTP_sendMessageUploadPhotoAction(MTP_int(progress)); break; + case Type::UploadFile: action = MTP_sendMessageUploadDocumentAction(MTP_int(progress)); break; + case Type::ChooseLocation: action = MTP_sendMessageGeoLocationAction(); break; + case Type::ChooseContact: action = MTP_sendMessageChooseContactAction(); break; + case Type::PlayGame: action = MTP_sendMessageGamePlayAction(); break; + } + const auto requestId = _session->api().request(MTPmessages_SetTyping( + history->peer->input, + action + )).done([=](const MTPBool &result, mtpRequestId requestId) { + done(result, requestId); + }).send(); + _requests.emplace(Key{ history, type }, requestId); + + if (type == Type::Typing) { + _stopTypingHistory = history; + _stopTypingTimer.callOnce(kCancelTypingActionTimeout); + } +} + +void SendProgressManager::done( + const MTPBool &result, + mtpRequestId requestId) { + for (auto i = _requests.begin(), e = _requests.end(); i != e; ++i) { + if (i->second == requestId) { + _requests.erase(i); + break; + } + } +} + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_send_progress.h b/Telegram/SourceFiles/api/api_send_progress.h new file mode 100644 index 0000000000..14b409a43d --- /dev/null +++ b/Telegram/SourceFiles/api/api_send_progress.h @@ -0,0 +1,88 @@ +/* +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 "api/api_common.h" +#include "base/timer.h" + +class History; + +namespace Main { +class Session; +} // namespace Main + +namespace Api { + +enum class SendProgressType { + Typing, + RecordVideo, + UploadVideo, + RecordVoice, + UploadVoice, + RecordRound, + UploadRound, + UploadPhoto, + UploadFile, + ChooseLocation, + ChooseContact, + PlayGame, +}; + +struct SendProgress { + SendProgress( + SendProgressType type, + crl::time until, + int progress = 0) + : type(type) + , until(until) + , progress(progress) { + } + SendProgressType type = SendProgressType::Typing; + crl::time until = 0; + int progress = 0; + +}; + +class SendProgressManager final { +public: + SendProgressManager(not_null session); + + void update( + not_null history, + SendProgressType type, + int32 progress = 0); + void cancel( + not_null history, + SendProgressType type); + void cancelTyping(not_null history); + +private: + struct Key { + not_null history; + SendProgressType type = SendProgressType(); + + inline bool operator<(const Key &other) const { + return (history < other.history) + || (history == other.history && type < other.type); + } + }; + + void send( + not_null history, + SendProgressType type, + int32 progress); + void done(const MTPBool &result, mtpRequestId requestId); + + const not_null _session; + base::flat_map _requests; + base::Timer _stopTypingTimer; + History *_stopTypingHistory = nullptr; + +}; + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_sending.h b/Telegram/SourceFiles/api/api_sending.h index 96458281b4..29184faea6 100644 --- a/Telegram/SourceFiles/api/api_sending.h +++ b/Telegram/SourceFiles/api/api_sending.h @@ -15,6 +15,7 @@ struct FileLoadResult; namespace Api { struct MessageToSend; +struct SendAction; void SendExistingDocument( Api::MessageToSend &&message, diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h index 6ac30c5724..b66ea89184 100644 --- a/Telegram/SourceFiles/data/data_types.h +++ b/Telegram/SourceFiles/data/data_types.h @@ -422,35 +422,6 @@ inline bool operator==( && (a.scroll == b.scroll); } -struct SendAction { - enum class Type { - Typing, - RecordVideo, - UploadVideo, - RecordVoice, - UploadVoice, - RecordRound, - UploadRound, - UploadPhoto, - UploadFile, - ChooseLocation, - ChooseContact, - PlayGame, - }; - SendAction( - Type type, - crl::time until, - int progress = 0) - : type(type) - , until(until) - , progress(progress) { - } - Type type = Type::Typing; - crl::time until = 0; - int progress = 0; - -}; - class FileClickHandler : public LeftButtonClickHandler { public: FileClickHandler( diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 6dc719a429..ace095dba8 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "history/history.h" +#include "api/api_send_progress.h" #include "history/view/history_view_element.h" #include "history/history_message.h" #include "history/history_service.h" @@ -357,7 +358,7 @@ bool History::updateSendActionNeedsAnimating( return false; } - using Type = SendAction::Type; + using Type = Api::SendProgressType; if (action.type() == mtpc_sendMessageCancelAction) { clearSendAction(user); return false; @@ -420,7 +421,7 @@ bool History::updateSendActionNeedsAnimating( return updateSendActionNeedsAnimating(now, true); } -bool History::mySendActionUpdated(SendAction::Type type, bool doing) { +bool History::mySendActionUpdated(Api::SendProgressType type, bool doing) { const auto now = crl::now(); const auto i = _mySendActions.find(type); if (doing) { @@ -508,7 +509,7 @@ bool History::updateSendActionNeedsAnimating(crl::time now, bool force) { begin(_typing)->first->firstName); } else if (!_sendActions.empty()) { // Handles all actions except game playing. - using Type = SendAction::Type; + using Type = Api::SendProgressType; auto sendActionString = [](Type type, const QString &name) -> QString { switch (type) { case Type::RecordVideo: return name.isEmpty() ? tr::lng_send_action_record_video(tr::now) : tr::lng_user_action_record_video(tr::now, lt_user, name); @@ -562,7 +563,7 @@ bool History::updateSendActionNeedsAnimating(crl::time now, bool force) { } } if (typingCount > 0) { - _sendActionAnimation.start(SendAction::Type::Typing); + _sendActionAnimation.start(Api::SendProgressType::Typing); } else if (newTypingString.isEmpty()) { _sendActionAnimation.stop(); } diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 0687f305b7..71878c0281 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -23,6 +23,11 @@ class HistoryItem; class HistoryMessage; class HistoryService; +namespace Api { +enum class SendProgressType; +struct SendProgress; +} // namespace Api + namespace Main { class Session; } // namespace Main @@ -266,7 +271,7 @@ public: bool hasPendingResizedItems() const; void setHasPendingResizedItems(); - bool mySendActionUpdated(SendAction::Type type, bool doing); + bool mySendActionUpdated(Api::SendProgressType type, bool doing); bool paintSendAction( Painter &p, int x, @@ -573,11 +578,11 @@ private: QString _topPromotedType; base::flat_map, crl::time> _typing; - base::flat_map, SendAction> _sendActions; + base::flat_map, Api::SendProgress> _sendActions; QString _sendActionString; Ui::Text::String _sendActionText; Ui::SendActionAnimation _sendActionAnimation; - base::flat_map _mySendActions; + base::flat_map _mySendActions; std::deque> _notifications; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 22a9bca291..874d909ac8 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_sending.h" #include "api/api_text_entities.h" +#include "api/api_send_progress.h" #include "boxes/confirm_box.h" #include "boxes/send_files_box.h" #include "boxes/share_box.h" @@ -123,7 +124,6 @@ constexpr auto kSkipRepaintWhileScrollMs = 100; constexpr auto kShowMembersDropdownTimeoutMs = 300; constexpr auto kDisplayEditTimeWarningMs = 300 * 1000; constexpr auto kFullDayInMs = 86400 * 1000; -constexpr auto kCancelTypingActionTimeout = crl::time(5000); constexpr auto kSaveDraftTimeout = 1000; constexpr auto kSaveDraftAnywayTimeout = 5000; constexpr auto kSaveCloudDraftIdleTimeout = 14000; @@ -299,7 +299,6 @@ HistoryWidget::HistoryWidget( , _attachDragState(DragState::None) , _attachDragDocument(this) , _attachDragPhoto(this) -, _sendActionStopTimer([this] { cancelTypingAction(); }) , _topShadow(this) { setAcceptDrops(true); @@ -737,7 +736,6 @@ HistoryWidget::HistoryWidget( } }, lifetime()); - subscribeToUploader(); setupScheduledToggle(); orderWidgets(); setupShortcuts(); @@ -1247,7 +1245,9 @@ void HistoryWidget::onTextChange() { if (!_inlineBot && !_editMsgId && (_textUpdateEvents & TextUpdateEvent::SendTyping)) { - updateSendAction(_history, SendAction::Type::Typing); + session().sendProgressManager().update( + _history, + Api::SendProgressType::Typing); } } @@ -1390,77 +1390,6 @@ void HistoryWidget::writeDrafts(Data::Draft **localDraft, Data::Draft **editDraf } } -void HistoryWidget::cancelSendAction( - not_null history, - SendAction::Type type) { - const auto i = _sendActionRequests.find({ history, type }); - if (i != _sendActionRequests.end()) { - _api.request(i->second).cancel(); - _sendActionRequests.erase(i); - } -} - -void HistoryWidget::cancelTypingAction() { - if (_history) { - cancelSendAction(_history, SendAction::Type::Typing); - } - _sendActionStopTimer.cancel(); -} - -void HistoryWidget::updateSendAction( - not_null history, - SendAction::Type type, - int32 progress) { - const auto peer = history->peer; - if (peer->isSelf() || (peer->isChannel() && !peer->isMegagroup())) { - return; - } - - const auto doing = (progress >= 0); - if (history->mySendActionUpdated(type, doing)) { - cancelSendAction(history, type); - if (doing) { - using Type = SendAction::Type; - MTPsendMessageAction action; - switch (type) { - case Type::Typing: action = MTP_sendMessageTypingAction(); break; - case Type::RecordVideo: action = MTP_sendMessageRecordVideoAction(); break; - case Type::UploadVideo: action = MTP_sendMessageUploadVideoAction(MTP_int(progress)); break; - case Type::RecordVoice: action = MTP_sendMessageRecordAudioAction(); break; - case Type::UploadVoice: action = MTP_sendMessageUploadAudioAction(MTP_int(progress)); break; - case Type::RecordRound: action = MTP_sendMessageRecordRoundAction(); break; - case Type::UploadRound: action = MTP_sendMessageUploadRoundAction(MTP_int(progress)); break; - case Type::UploadPhoto: action = MTP_sendMessageUploadPhotoAction(MTP_int(progress)); break; - case Type::UploadFile: action = MTP_sendMessageUploadDocumentAction(MTP_int(progress)); break; - case Type::ChooseLocation: action = MTP_sendMessageGeoLocationAction(); break; - case Type::ChooseContact: action = MTP_sendMessageChooseContactAction(); break; - case Type::PlayGame: action = MTP_sendMessageGamePlayAction(); break; - } - const auto requestId = _api.request(MTPmessages_SetTyping( - peer->input, - action - )).done([=](const MTPBool &result, mtpRequestId requestId) { - sendActionDone(result, requestId); - }).send(); - _sendActionRequests.emplace(std::pair(history, type), requestId); - if (type == Type::Typing) { - _sendActionStopTimer.callOnce(kCancelTypingActionTimeout); - } - } - } -} - -void HistoryWidget::sendActionDone( - const MTPBool &result, - mtpRequestId requestId) { - for (auto i = _sendActionRequests.begin(), e = _sendActionRequests.end(); i != e; ++i) { - if (i->second == requestId) { - _sendActionRequests.erase(i); - break; - } - } -} - void HistoryWidget::activate() { if (_history) { if (!_historyInited) { @@ -1515,7 +1444,9 @@ void HistoryWidget::onRecordUpdate(quint16 level, qint32 samples) { Core::App().updateNonIdle(); updateField(); if (_history) { - updateSendAction(_history, SendAction::Type::RecordVoice); + session().sendProgressManager().update( + _history, + Api::SendProgressType::RecordVoice); } } @@ -1795,9 +1726,12 @@ void HistoryWidget::showHistory( } return; } - updateSendAction(_history, SendAction::Type::Typing, -1); + session().sendProgressManager().update( + _history, + Api::SendProgressType::Typing, + -1); session().data().histories().sendPendingReadInbox(_history); - cancelTypingAction(); + session().sendProgressManager().cancelTyping(_history); } clearReplyReturns(); @@ -3588,7 +3522,10 @@ void HistoryWidget::stopRecording(bool send) { _recording = false; _recordingSamples = 0; if (_history) { - updateSendAction(_history, SendAction::Type::RecordVoice, -1); + session().sendProgressManager().update( + _history, + Api::SendProgressType::RecordVoice, + -1); } updateControlsVisibility(); @@ -3689,22 +3626,13 @@ void HistoryWidget::app_sendBotCallback( flags |= MTPmessages_GetBotCallbackAnswer::Flag::f_data; sendData = button->data; } - const auto weak = Ui::MakeWeak(this); button->requestId = session().api().request(MTPmessages_GetBotCallbackAnswer( MTP_flags(flags), _peer->input, MTP_int(msg->id), MTP_bytes(sendData) - )).done([info, weak](const MTPmessages_BotCallbackAnswer &result, mtpRequestId requestId) { + )).done([info](const MTPmessages_BotCallbackAnswer &result, mtpRequestId requestId) { BotCallbackDone(info, result, requestId); - result.match([&](const MTPDmessages_botCallbackAnswer &data) { - const auto item = info.session->data().message(info.msgId); - if (!data.vmessage() && data.vurl() && info.game && item) { - if (const auto strong = weak.data()) { - strong->updateSendAction(item->history(), SendAction::Type::PlayGame); - } - } - }); }).fail([info](const RPCError &error, mtpRequestId requestId) { BotCallbackFail(info, error, requestId); }).send(); @@ -3750,6 +3678,13 @@ void HistoryWidget::BotCallbackDone( UrlClickHandler::Open(link); } } + if (const auto item = info.session->data().message(info.msgId)) { + if (!data.vmessage() && data.vurl() && info.game) { + info.session->sendProgressManager().update( + item->history(), + Api::SendProgressType::PlayGame); + } + } }); } @@ -4654,144 +4589,6 @@ void HistoryWidget::uploadFile( session().api().sendFile(fileContent, type, action); } -void HistoryWidget::subscribeToUploader() { - using namespace Storage; - - session().uploader().photoReady( - ) | rpl::start_with_next([=](const UploadedPhoto &data) { - if (data.edit) { - session().api().editUploadedFile( - data.fullId, - data.file, - std::nullopt, - data.options, - false); - } else { - session().api().sendUploadedPhoto( - data.fullId, - data.file, - data.options); - } - }, lifetime()); - - session().uploader().photoProgress( - ) | rpl::start_with_next([=](const FullMsgId &fullId) { - photoProgress(fullId); - }, lifetime()); - - session().uploader().photoFailed( - ) | rpl::start_with_next([=](const FullMsgId &fullId) { - photoFailed(fullId); - }, lifetime()); - - session().uploader().documentReady( - ) | rpl::start_with_next([=](const UploadedDocument &data) { - if (data.edit) { - documentEdited(data.fullId, data.options, data.file); - } else { - documentUploaded(data.fullId, data.options, data.file); - } - }, lifetime()); - - session().uploader().thumbDocumentReady( - ) | rpl::start_with_next([=](const UploadedThumbDocument &data) { - thumbDocumentUploaded( - data.fullId, - data.options, - data.file, - data.thumb, - data.edit); - }, lifetime()); - - session().uploader().documentProgress( - ) | rpl::start_with_next([=](const FullMsgId &fullId) { - documentProgress(fullId); - }, lifetime()); - - session().uploader().documentFailed( - ) | rpl::start_with_next([=](const FullMsgId &fullId) { - documentFailed(fullId); - }, lifetime()); -} - -void HistoryWidget::documentUploaded( - const FullMsgId &newId, - Api::SendOptions options, - const MTPInputFile &file) { - session().api().sendUploadedDocument(newId, file, std::nullopt, options); -} - -void HistoryWidget::documentEdited( - const FullMsgId &newId, - Api::SendOptions options, - const MTPInputFile &file) { - session().api().editUploadedFile(newId, file, std::nullopt, options, true); -} - -void HistoryWidget::thumbDocumentUploaded( - const FullMsgId &newId, - Api::SendOptions options, - const MTPInputFile &file, - const MTPInputFile &thumb, - bool edit) { - if (edit) { - session().api().editUploadedFile(newId, file, thumb, options, true); - } else { - session().api().sendUploadedDocument(newId, file, thumb, options); - } -} - -void HistoryWidget::photoProgress(const FullMsgId &newId) { - if (const auto item = session().data().message(newId)) { - const auto photo = item->media() - ? item->media()->photo() - : nullptr; - updateSendAction(item->history(), SendAction::Type::UploadPhoto, 0); - session().data().requestItemRepaint(item); - } -} - -void HistoryWidget::documentProgress(const FullMsgId &newId) { - if (const auto item = session().data().message(newId)) { - const auto media = item->media(); - const auto document = media ? media->document() : nullptr; - const auto sendAction = (document && document->isVoiceMessage()) - ? SendAction::Type::UploadVoice - : SendAction::Type::UploadFile; - const auto progress = (document && document->uploading()) - ? document->uploadingData->offset - : 0; - - updateSendAction( - item->history(), - sendAction, - progress); - session().data().requestItemRepaint(item); - } -} - -void HistoryWidget::photoFailed(const FullMsgId &newId) { - if (const auto item = session().data().message(newId)) { - updateSendAction( - item->history(), - SendAction::Type::UploadPhoto, - -1); - session().data().requestItemRepaint(item); - } -} - -void HistoryWidget::documentFailed(const FullMsgId &newId) { - if (const auto item = session().data().message(newId)) { - const auto media = item->media(); - const auto document = media ? media->document() : nullptr; - const auto sendAction = (document && document->isVoiceMessage()) - ? SendAction::Type::UploadVoice - : SendAction::Type::UploadFile; - updateSendAction(item->history(), sendAction, -1); - session().data().requestItemRepaint(item); - } -} - void HistoryWidget::handleHistoryChange(not_null history) { if (_list && (_history == history || _migrated == history)) { handlePendingHistoryUpdate(); diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index baa297a82a..e75111d007 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -423,15 +423,6 @@ private: void stopMessageHighlight(); auto computeSendButtonType() const; - void updateSendAction( - not_null history, - SendAction::Type type, - int32 progress = 0); - void cancelSendAction( - not_null history, - SendAction::Type type); - void cancelTypingAction(); - void sendActionDone(const MTPBool &result, mtpRequestId requestId); void animationCallback(); void updateOverStates(QPoint pos); @@ -474,28 +465,6 @@ private: Api::SendOptions options, std::shared_ptr album = nullptr); - void subscribeToUploader(); - - void photoProgress(const FullMsgId &msgId); - void photoFailed(const FullMsgId &msgId); - void documentUploaded( - const FullMsgId &msgId, - Api::SendOptions options, - const MTPInputFile &file); - void thumbDocumentUploaded( - const FullMsgId &msgId, - Api::SendOptions options, - const MTPInputFile &file, - const MTPInputFile &thumb, - bool edit = false); - void documentProgress(const FullMsgId &msgId); - void documentFailed(const FullMsgId &msgId); - - void documentEdited( - const FullMsgId &msgId, - Api::SendOptions options, - const MTPInputFile &file); - void itemRemoved(not_null item); // Updates position of controls around the message field, @@ -800,11 +769,6 @@ private: base::Timer _highlightTimer; crl::time _highlightStart = 0; - base::flat_map< - std::pair, SendAction::Type>, - mtpRequestId> _sendActionRequests; - base::Timer _sendActionStopTimer; - crl::time _saveDraftStart = 0; bool _saveDraftText = false; QTimer _saveDraftTimer, _saveCloudDraftTimer; diff --git a/Telegram/SourceFiles/main/main_session.cpp b/Telegram/SourceFiles/main/main_session.cpp index 95f637d3ec..f59c119a0d 100644 --- a/Telegram/SourceFiles/main/main_session.cpp +++ b/Telegram/SourceFiles/main/main_session.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "api/api_updates.h" +#include "api/api_send_progress.h" #include "core/application.h" #include "main/main_account.h" #include "main/main_domain.h" @@ -72,6 +73,7 @@ Session::Session( , _settings(std::move(settings)) , _api(std::make_unique(this)) , _updates(std::make_unique(this)) +, _sendProgressManager(std::make_unique(this)) , _downloader(std::make_unique(_api.get())) , _uploader(std::make_unique(_api.get())) , _storage(std::make_unique()) diff --git a/Telegram/SourceFiles/main/main_session.h b/Telegram/SourceFiles/main/main_session.h index 01ad24f0ab..712e863975 100644 --- a/Telegram/SourceFiles/main/main_session.h +++ b/Telegram/SourceFiles/main/main_session.h @@ -16,6 +16,7 @@ class ApiWrap; namespace Api { class Updates; +class SendProgressManager; } // namespace Api namespace MTP { @@ -87,6 +88,9 @@ public: [[nodiscard]] Api::Updates &updates() const { return *_updates; } + [[nodiscard]] Api::SendProgressManager &sendProgressManager() const { + return *_sendProgressManager; + } [[nodiscard]] Storage::DownloadManagerMtproto &downloader() const { return *_downloader; } @@ -162,6 +166,7 @@ private: const std::unique_ptr _settings; const std::unique_ptr _api; const std::unique_ptr _updates; + const std::unique_ptr _sendProgressManager; const std::unique_ptr _downloader; const std::unique_ptr _uploader; const std::unique_ptr _storage; diff --git a/Telegram/SourceFiles/storage/file_upload.cpp b/Telegram/SourceFiles/storage/file_upload.cpp index ccaefa2c4c..ffb9b1eeac 100644 --- a/Telegram/SourceFiles/storage/file_upload.cpp +++ b/Telegram/SourceFiles/storage/file_upload.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "storage/file_upload.h" +#include "api/api_send_progress.h" #include "storage/localimageloader.h" #include "storage/file_download.h" #include "data/data_document.h" @@ -14,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_photo.h" #include "data/data_session.h" #include "ui/image/image_location_factory.h" +#include "history/history_item.h" #include "core/mime_type.h" #include "main/main_session.h" #include "apiwrap.h" @@ -153,6 +155,141 @@ Uploader::Uploader(not_null api) connect(&nextTimer, SIGNAL(timeout()), this, SLOT(sendNext())); stopSessionsTimer.setSingleShot(true); connect(&stopSessionsTimer, SIGNAL(timeout()), this, SLOT(stopSessions())); + + photoReady( + ) | rpl::start_with_next([=](const UploadedPhoto &data) { + if (data.edit) { + _api->editUploadedFile( + data.fullId, + data.file, + std::nullopt, + data.options, + false); + } else { + _api->sendUploadedPhoto( + data.fullId, + data.file, + data.options); + } + }, _lifetime); + + documentReady( + ) | rpl::start_with_next([=](const UploadedDocument &data) { + if (data.edit) { + _api->editUploadedFile( + data.fullId, + data.file, + std::nullopt, + data.options, + true); + } else { + _api->sendUploadedDocument( + data.fullId, + data.file, + std::nullopt, + data.options); + } + }, _lifetime); + + thumbDocumentReady( + ) | rpl::start_with_next([=](const UploadedThumbDocument &data) { + if (data.edit) { + _api->editUploadedFile( + data.fullId, + data.file, + data.thumb, + data.options, + true); + } else { + _api->sendUploadedDocument( + data.fullId, + data.file, + data.thumb, + data.options); + } + }, _lifetime); + + + photoProgress( + ) | rpl::start_with_next([=](const FullMsgId &fullId) { + processPhotoProgress(fullId); + }, _lifetime); + + photoFailed( + ) | rpl::start_with_next([=](const FullMsgId &fullId) { + processPhotoFailed(fullId); + }, _lifetime); + + documentProgress( + ) | rpl::start_with_next([=](const FullMsgId &fullId) { + processDocumentProgress(fullId); + }, _lifetime); + + documentFailed( + ) | rpl::start_with_next([=](const FullMsgId &fullId) { + processDocumentFailed(fullId); + }, _lifetime); +} + +void Uploader::processPhotoProgress(const FullMsgId &newId) { + const auto session = &_api->session(); + if (const auto item = session->data().message(newId)) { + const auto photo = item->media() + ? item->media()->photo() + : nullptr; + session->sendProgressManager().update( + item->history(), + Api::SendProgressType::UploadPhoto, + 0); + session->data().requestItemRepaint(item); + } +} + +void Uploader::processDocumentProgress(const FullMsgId &newId) { + const auto session = &_api->session(); + if (const auto item = session->data().message(newId)) { + const auto media = item->media(); + const auto document = media ? media->document() : nullptr; + const auto sendAction = (document && document->isVoiceMessage()) + ? Api::SendProgressType::UploadVoice + : Api::SendProgressType::UploadFile; + const auto progress = (document && document->uploading()) + ? document->uploadingData->offset + : 0; + + session->sendProgressManager().update( + item->history(), + sendAction, + progress); + session->data().requestItemRepaint(item); + } +} + +void Uploader::processPhotoFailed(const FullMsgId &newId) { + const auto session = &_api->session(); + if (const auto item = session->data().message(newId)) { + session->sendProgressManager().update( + item->history(), + Api::SendProgressType::UploadPhoto, + -1); + session->data().requestItemRepaint(item); + } +} + +void Uploader::processDocumentFailed(const FullMsgId &newId) { + const auto session = &_api->session(); + if (const auto item = session->data().message(newId)) { + const auto media = item->media(); + const auto document = media ? media->document() : nullptr; + const auto sendAction = (document && document->isVoiceMessage()) + ? Api::SendProgressType::UploadVoice + : Api::SendProgressType::UploadFile; + session->sendProgressManager().update( + item->history(), + sendAction, + -1); + session->data().requestItemRepaint(item); + } } Uploader::~Uploader() { @@ -282,7 +419,9 @@ void Uploader::stopSessions() { } void Uploader::sendNext() { - if (sentSize >= kMaxUploadFileParallelSize || _pausedId.msg) return; + if (sentSize >= kMaxUploadFileParallelSize || _pausedId.msg) { + return; + } bool stopping = stopSessionsTimer.isActive(); if (queue.empty()) { diff --git a/Telegram/SourceFiles/storage/file_upload.h b/Telegram/SourceFiles/storage/file_upload.h index b42ecc4eb7..5e2d8c1b55 100644 --- a/Telegram/SourceFiles/storage/file_upload.h +++ b/Telegram/SourceFiles/storage/file_upload.h @@ -121,6 +121,11 @@ private: void partLoaded(const MTPBool &result, mtpRequestId requestId); void partFailed(const RPCError &error, mtpRequestId requestId); + void processPhotoProgress(const FullMsgId &msgId); + void processPhotoFailed(const FullMsgId &msgId); + void processDocumentProgress(const FullMsgId &msgId); + void processDocumentFailed(const FullMsgId &msgId); + void currentFailed(); not_null _api; @@ -147,6 +152,8 @@ private: rpl::event_stream _documentFailed; rpl::event_stream _secureFailed; + rpl::lifetime _lifetime; + }; } // namespace Storage diff --git a/Telegram/SourceFiles/ui/effects/send_action_animations.cpp b/Telegram/SourceFiles/ui/effects/send_action_animations.cpp index 64ed0e4362..46d9d0ffea 100644 --- a/Telegram/SourceFiles/ui/effects/send_action_animations.cpp +++ b/Telegram/SourceFiles/ui/effects/send_action_animations.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "ui/effects/send_action_animations.h" +#include "api/api_send_progress.h" #include "ui/effects/animation_value.h" #include "styles/style_widgets.h" @@ -17,7 +18,7 @@ constexpr int kTypingDotsCount = 3; constexpr int kRecordArcsCount = 4; constexpr int kUploadArrowsCount = 3; -using ImplementationsMap = QMap; +using ImplementationsMap = QMap; NeverFreedPointer Implementations; class TypingAnimation : public SendActionAnimation::Impl { @@ -162,7 +163,7 @@ void CreateImplementationsMap() { if (Implementations) { return; } - using Type = SendAction::Type; + using Type = Api::SendProgressType; Implementations.createIfNull(); Type recordTypes[] = { Type::RecordVideo, diff --git a/Telegram/SourceFiles/ui/effects/send_action_animations.h b/Telegram/SourceFiles/ui/effects/send_action_animations.h index f0514ea986..502f59415b 100644 --- a/Telegram/SourceFiles/ui/effects/send_action_animations.h +++ b/Telegram/SourceFiles/ui/effects/send_action_animations.h @@ -7,11 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +namespace Api { +enum class SendProgressType; +} // namespace Api + namespace Ui { class SendActionAnimation { public: - using Type = SendAction::Type; + using Type = Api::SendProgressType; void start(Type type); void stop(); @@ -31,7 +35,7 @@ public: class Impl { public: - using Type = SendAction::Type; + using Type = Api::SendProgressType; Impl(int period) : _period(period), _started(crl::now()) { }