Fix finalizing media in non-active account.
This commit is contained in:
		
							parent
							
								
									000a7ae28b
								
							
						
					
					
						commit
						2f5cb33bf2
					
				
					 15 changed files with 396 additions and 304 deletions
				
			
		| 
						 | 
					@ -215,6 +215,8 @@ PRIVATE
 | 
				
			||||||
    api/api_hash.h
 | 
					    api/api_hash.h
 | 
				
			||||||
    api/api_self_destruct.cpp
 | 
					    api/api_self_destruct.cpp
 | 
				
			||||||
    api/api_self_destruct.h
 | 
					    api/api_self_destruct.h
 | 
				
			||||||
 | 
					    api/api_send_progress.cpp
 | 
				
			||||||
 | 
					    api/api_send_progress.h
 | 
				
			||||||
    api/api_sending.cpp
 | 
					    api/api_sending.cpp
 | 
				
			||||||
    api/api_sending.h
 | 
					    api/api_sending.h
 | 
				
			||||||
    api/api_sensitive_content.cpp
 | 
					    api/api_sensitive_content.cpp
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										105
									
								
								Telegram/SourceFiles/api/api_send_progress.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								Telegram/SourceFiles/api/api_send_progress.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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<Main::Session*> session)
 | 
				
			||||||
 | 
					: _session(session)
 | 
				
			||||||
 | 
					, _stopTypingTimer([=] { cancelTyping(base::take(_stopTypingHistory)); }) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SendProgressManager::cancel(
 | 
				
			||||||
 | 
							not_null<History*> 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*> history) {
 | 
				
			||||||
 | 
						_stopTypingTimer.cancel();
 | 
				
			||||||
 | 
						cancel(history, SendProgressType::Typing);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SendProgressManager::update(
 | 
				
			||||||
 | 
							not_null<History*> 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*> 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
 | 
				
			||||||
							
								
								
									
										88
									
								
								Telegram/SourceFiles/api/api_send_progress.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								Telegram/SourceFiles/api/api_send_progress.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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<Main::Session*> session);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void update(
 | 
				
			||||||
 | 
							not_null<History*> history,
 | 
				
			||||||
 | 
							SendProgressType type,
 | 
				
			||||||
 | 
							int32 progress = 0);
 | 
				
			||||||
 | 
						void cancel(
 | 
				
			||||||
 | 
							not_null<History*> history,
 | 
				
			||||||
 | 
							SendProgressType type);
 | 
				
			||||||
 | 
						void cancelTyping(not_null<History*> history);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						struct Key {
 | 
				
			||||||
 | 
							not_null<History*> 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*> history,
 | 
				
			||||||
 | 
							SendProgressType type,
 | 
				
			||||||
 | 
							int32 progress);
 | 
				
			||||||
 | 
						void done(const MTPBool &result, mtpRequestId requestId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const not_null<Main::Session*> _session;
 | 
				
			||||||
 | 
						base::flat_map<Key, mtpRequestId> _requests;
 | 
				
			||||||
 | 
						base::Timer _stopTypingTimer;
 | 
				
			||||||
 | 
						History *_stopTypingHistory = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace Api
 | 
				
			||||||
| 
						 | 
					@ -15,6 +15,7 @@ struct FileLoadResult;
 | 
				
			||||||
namespace Api {
 | 
					namespace Api {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct MessageToSend;
 | 
					struct MessageToSend;
 | 
				
			||||||
 | 
					struct SendAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SendExistingDocument(
 | 
					void SendExistingDocument(
 | 
				
			||||||
	Api::MessageToSend &&message,
 | 
						Api::MessageToSend &&message,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -422,35 +422,6 @@ inline bool operator==(
 | 
				
			||||||
		&& (a.scroll == b.scroll);
 | 
							&& (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 {
 | 
					class FileClickHandler : public LeftButtonClickHandler {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	FileClickHandler(
 | 
						FileClickHandler(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
#include "history/history.h"
 | 
					#include "history/history.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "api/api_send_progress.h"
 | 
				
			||||||
#include "history/view/history_view_element.h"
 | 
					#include "history/view/history_view_element.h"
 | 
				
			||||||
#include "history/history_message.h"
 | 
					#include "history/history_message.h"
 | 
				
			||||||
#include "history/history_service.h"
 | 
					#include "history/history_service.h"
 | 
				
			||||||
| 
						 | 
					@ -357,7 +358,7 @@ bool History::updateSendActionNeedsAnimating(
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	using Type = SendAction::Type;
 | 
						using Type = Api::SendProgressType;
 | 
				
			||||||
	if (action.type() == mtpc_sendMessageCancelAction) {
 | 
						if (action.type() == mtpc_sendMessageCancelAction) {
 | 
				
			||||||
		clearSendAction(user);
 | 
							clearSendAction(user);
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
| 
						 | 
					@ -420,7 +421,7 @@ bool History::updateSendActionNeedsAnimating(
 | 
				
			||||||
	return updateSendActionNeedsAnimating(now, true);
 | 
						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 now = crl::now();
 | 
				
			||||||
	const auto i = _mySendActions.find(type);
 | 
						const auto i = _mySendActions.find(type);
 | 
				
			||||||
	if (doing) {
 | 
						if (doing) {
 | 
				
			||||||
| 
						 | 
					@ -508,7 +509,7 @@ bool History::updateSendActionNeedsAnimating(crl::time now, bool force) {
 | 
				
			||||||
					begin(_typing)->first->firstName);
 | 
										begin(_typing)->first->firstName);
 | 
				
			||||||
		} else if (!_sendActions.empty()) {
 | 
							} else if (!_sendActions.empty()) {
 | 
				
			||||||
			// Handles all actions except game playing.
 | 
								// Handles all actions except game playing.
 | 
				
			||||||
			using Type = SendAction::Type;
 | 
								using Type = Api::SendProgressType;
 | 
				
			||||||
			auto sendActionString = [](Type type, const QString &name) -> QString {
 | 
								auto sendActionString = [](Type type, const QString &name) -> QString {
 | 
				
			||||||
				switch (type) {
 | 
									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);
 | 
									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) {
 | 
							if (typingCount > 0) {
 | 
				
			||||||
			_sendActionAnimation.start(SendAction::Type::Typing);
 | 
								_sendActionAnimation.start(Api::SendProgressType::Typing);
 | 
				
			||||||
		} else if (newTypingString.isEmpty()) {
 | 
							} else if (newTypingString.isEmpty()) {
 | 
				
			||||||
			_sendActionAnimation.stop();
 | 
								_sendActionAnimation.stop();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,6 +23,11 @@ class HistoryItem;
 | 
				
			||||||
class HistoryMessage;
 | 
					class HistoryMessage;
 | 
				
			||||||
class HistoryService;
 | 
					class HistoryService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Api {
 | 
				
			||||||
 | 
					enum class SendProgressType;
 | 
				
			||||||
 | 
					struct SendProgress;
 | 
				
			||||||
 | 
					} // namespace Api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Main {
 | 
					namespace Main {
 | 
				
			||||||
class Session;
 | 
					class Session;
 | 
				
			||||||
} // namespace Main
 | 
					} // namespace Main
 | 
				
			||||||
| 
						 | 
					@ -266,7 +271,7 @@ public:
 | 
				
			||||||
	bool hasPendingResizedItems() const;
 | 
						bool hasPendingResizedItems() const;
 | 
				
			||||||
	void setHasPendingResizedItems();
 | 
						void setHasPendingResizedItems();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool mySendActionUpdated(SendAction::Type type, bool doing);
 | 
						bool mySendActionUpdated(Api::SendProgressType type, bool doing);
 | 
				
			||||||
	bool paintSendAction(
 | 
						bool paintSendAction(
 | 
				
			||||||
		Painter &p,
 | 
							Painter &p,
 | 
				
			||||||
		int x,
 | 
							int x,
 | 
				
			||||||
| 
						 | 
					@ -573,11 +578,11 @@ private:
 | 
				
			||||||
	QString _topPromotedType;
 | 
						QString _topPromotedType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	base::flat_map<not_null<UserData*>, crl::time> _typing;
 | 
						base::flat_map<not_null<UserData*>, crl::time> _typing;
 | 
				
			||||||
	base::flat_map<not_null<UserData*>, SendAction> _sendActions;
 | 
						base::flat_map<not_null<UserData*>, Api::SendProgress> _sendActions;
 | 
				
			||||||
	QString _sendActionString;
 | 
						QString _sendActionString;
 | 
				
			||||||
	Ui::Text::String _sendActionText;
 | 
						Ui::Text::String _sendActionText;
 | 
				
			||||||
	Ui::SendActionAnimation _sendActionAnimation;
 | 
						Ui::SendActionAnimation _sendActionAnimation;
 | 
				
			||||||
	base::flat_map<SendAction::Type, crl::time> _mySendActions;
 | 
						base::flat_map<Api::SendProgressType, crl::time> _mySendActions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	std::deque<not_null<HistoryItem*>> _notifications;
 | 
						std::deque<not_null<HistoryItem*>> _notifications;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "api/api_sending.h"
 | 
					#include "api/api_sending.h"
 | 
				
			||||||
#include "api/api_text_entities.h"
 | 
					#include "api/api_text_entities.h"
 | 
				
			||||||
 | 
					#include "api/api_send_progress.h"
 | 
				
			||||||
#include "boxes/confirm_box.h"
 | 
					#include "boxes/confirm_box.h"
 | 
				
			||||||
#include "boxes/send_files_box.h"
 | 
					#include "boxes/send_files_box.h"
 | 
				
			||||||
#include "boxes/share_box.h"
 | 
					#include "boxes/share_box.h"
 | 
				
			||||||
| 
						 | 
					@ -123,7 +124,6 @@ constexpr auto kSkipRepaintWhileScrollMs = 100;
 | 
				
			||||||
constexpr auto kShowMembersDropdownTimeoutMs = 300;
 | 
					constexpr auto kShowMembersDropdownTimeoutMs = 300;
 | 
				
			||||||
constexpr auto kDisplayEditTimeWarningMs = 300 * 1000;
 | 
					constexpr auto kDisplayEditTimeWarningMs = 300 * 1000;
 | 
				
			||||||
constexpr auto kFullDayInMs = 86400 * 1000;
 | 
					constexpr auto kFullDayInMs = 86400 * 1000;
 | 
				
			||||||
constexpr auto kCancelTypingActionTimeout = crl::time(5000);
 | 
					 | 
				
			||||||
constexpr auto kSaveDraftTimeout = 1000;
 | 
					constexpr auto kSaveDraftTimeout = 1000;
 | 
				
			||||||
constexpr auto kSaveDraftAnywayTimeout = 5000;
 | 
					constexpr auto kSaveDraftAnywayTimeout = 5000;
 | 
				
			||||||
constexpr auto kSaveCloudDraftIdleTimeout = 14000;
 | 
					constexpr auto kSaveCloudDraftIdleTimeout = 14000;
 | 
				
			||||||
| 
						 | 
					@ -299,7 +299,6 @@ HistoryWidget::HistoryWidget(
 | 
				
			||||||
, _attachDragState(DragState::None)
 | 
					, _attachDragState(DragState::None)
 | 
				
			||||||
, _attachDragDocument(this)
 | 
					, _attachDragDocument(this)
 | 
				
			||||||
, _attachDragPhoto(this)
 | 
					, _attachDragPhoto(this)
 | 
				
			||||||
, _sendActionStopTimer([this] { cancelTypingAction(); })
 | 
					 | 
				
			||||||
, _topShadow(this) {
 | 
					, _topShadow(this) {
 | 
				
			||||||
	setAcceptDrops(true);
 | 
						setAcceptDrops(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -737,7 +736,6 @@ HistoryWidget::HistoryWidget(
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}, lifetime());
 | 
						}, lifetime());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	subscribeToUploader();
 | 
					 | 
				
			||||||
	setupScheduledToggle();
 | 
						setupScheduledToggle();
 | 
				
			||||||
	orderWidgets();
 | 
						orderWidgets();
 | 
				
			||||||
	setupShortcuts();
 | 
						setupShortcuts();
 | 
				
			||||||
| 
						 | 
					@ -1247,7 +1245,9 @@ void HistoryWidget::onTextChange() {
 | 
				
			||||||
		if (!_inlineBot
 | 
							if (!_inlineBot
 | 
				
			||||||
			&& !_editMsgId
 | 
								&& !_editMsgId
 | 
				
			||||||
			&& (_textUpdateEvents & TextUpdateEvent::SendTyping)) {
 | 
								&& (_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*> 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*> 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() {
 | 
					void HistoryWidget::activate() {
 | 
				
			||||||
	if (_history) {
 | 
						if (_history) {
 | 
				
			||||||
		if (!_historyInited) {
 | 
							if (!_historyInited) {
 | 
				
			||||||
| 
						 | 
					@ -1515,7 +1444,9 @@ void HistoryWidget::onRecordUpdate(quint16 level, qint32 samples) {
 | 
				
			||||||
	Core::App().updateNonIdle();
 | 
						Core::App().updateNonIdle();
 | 
				
			||||||
	updateField();
 | 
						updateField();
 | 
				
			||||||
	if (_history) {
 | 
						if (_history) {
 | 
				
			||||||
		updateSendAction(_history, SendAction::Type::RecordVoice);
 | 
							session().sendProgressManager().update(
 | 
				
			||||||
 | 
								_history,
 | 
				
			||||||
 | 
								Api::SendProgressType::RecordVoice);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1795,9 +1726,12 @@ void HistoryWidget::showHistory(
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		updateSendAction(_history, SendAction::Type::Typing, -1);
 | 
							session().sendProgressManager().update(
 | 
				
			||||||
 | 
								_history,
 | 
				
			||||||
 | 
								Api::SendProgressType::Typing,
 | 
				
			||||||
 | 
								-1);
 | 
				
			||||||
		session().data().histories().sendPendingReadInbox(_history);
 | 
							session().data().histories().sendPendingReadInbox(_history);
 | 
				
			||||||
		cancelTypingAction();
 | 
							session().sendProgressManager().cancelTyping(_history);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	clearReplyReturns();
 | 
						clearReplyReturns();
 | 
				
			||||||
| 
						 | 
					@ -3588,7 +3522,10 @@ void HistoryWidget::stopRecording(bool send) {
 | 
				
			||||||
	_recording = false;
 | 
						_recording = false;
 | 
				
			||||||
	_recordingSamples = 0;
 | 
						_recordingSamples = 0;
 | 
				
			||||||
	if (_history) {
 | 
						if (_history) {
 | 
				
			||||||
		updateSendAction(_history, SendAction::Type::RecordVoice, -1);
 | 
							session().sendProgressManager().update(
 | 
				
			||||||
 | 
								_history,
 | 
				
			||||||
 | 
								Api::SendProgressType::RecordVoice,
 | 
				
			||||||
 | 
								-1);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	updateControlsVisibility();
 | 
						updateControlsVisibility();
 | 
				
			||||||
| 
						 | 
					@ -3689,22 +3626,13 @@ void HistoryWidget::app_sendBotCallback(
 | 
				
			||||||
		flags |= MTPmessages_GetBotCallbackAnswer::Flag::f_data;
 | 
							flags |= MTPmessages_GetBotCallbackAnswer::Flag::f_data;
 | 
				
			||||||
		sendData = button->data;
 | 
							sendData = button->data;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	const auto weak = Ui::MakeWeak(this);
 | 
					 | 
				
			||||||
	button->requestId = session().api().request(MTPmessages_GetBotCallbackAnswer(
 | 
						button->requestId = session().api().request(MTPmessages_GetBotCallbackAnswer(
 | 
				
			||||||
		MTP_flags(flags),
 | 
							MTP_flags(flags),
 | 
				
			||||||
		_peer->input,
 | 
							_peer->input,
 | 
				
			||||||
		MTP_int(msg->id),
 | 
							MTP_int(msg->id),
 | 
				
			||||||
		MTP_bytes(sendData)
 | 
							MTP_bytes(sendData)
 | 
				
			||||||
	)).done([info, weak](const MTPmessages_BotCallbackAnswer &result, mtpRequestId requestId) {
 | 
						)).done([info](const MTPmessages_BotCallbackAnswer &result, mtpRequestId requestId) {
 | 
				
			||||||
		BotCallbackDone(info, result, 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) {
 | 
						}).fail([info](const RPCError &error, mtpRequestId requestId) {
 | 
				
			||||||
		BotCallbackFail(info, error, requestId);
 | 
							BotCallbackFail(info, error, requestId);
 | 
				
			||||||
	}).send();
 | 
						}).send();
 | 
				
			||||||
| 
						 | 
					@ -3750,6 +3678,13 @@ void HistoryWidget::BotCallbackDone(
 | 
				
			||||||
				UrlClickHandler::Open(link);
 | 
									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);
 | 
						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<const History*> history) {
 | 
					void HistoryWidget::handleHistoryChange(not_null<const History*> history) {
 | 
				
			||||||
	if (_list && (_history == history || _migrated == history)) {
 | 
						if (_list && (_history == history || _migrated == history)) {
 | 
				
			||||||
		handlePendingHistoryUpdate();
 | 
							handlePendingHistoryUpdate();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -423,15 +423,6 @@ private:
 | 
				
			||||||
	void stopMessageHighlight();
 | 
						void stopMessageHighlight();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto computeSendButtonType() const;
 | 
						auto computeSendButtonType() const;
 | 
				
			||||||
	void updateSendAction(
 | 
					 | 
				
			||||||
		not_null<History*> history,
 | 
					 | 
				
			||||||
		SendAction::Type type,
 | 
					 | 
				
			||||||
		int32 progress = 0);
 | 
					 | 
				
			||||||
	void cancelSendAction(
 | 
					 | 
				
			||||||
		not_null<History*> history,
 | 
					 | 
				
			||||||
		SendAction::Type type);
 | 
					 | 
				
			||||||
	void cancelTypingAction();
 | 
					 | 
				
			||||||
	void sendActionDone(const MTPBool &result, mtpRequestId requestId);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void animationCallback();
 | 
						void animationCallback();
 | 
				
			||||||
	void updateOverStates(QPoint pos);
 | 
						void updateOverStates(QPoint pos);
 | 
				
			||||||
| 
						 | 
					@ -474,28 +465,6 @@ private:
 | 
				
			||||||
		Api::SendOptions options,
 | 
							Api::SendOptions options,
 | 
				
			||||||
		std::shared_ptr<SendingAlbum> album = nullptr);
 | 
							std::shared_ptr<SendingAlbum> 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<const HistoryItem*> item);
 | 
						void itemRemoved(not_null<const HistoryItem*> item);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Updates position of controls around the message field,
 | 
						// Updates position of controls around the message field,
 | 
				
			||||||
| 
						 | 
					@ -800,11 +769,6 @@ private:
 | 
				
			||||||
	base::Timer _highlightTimer;
 | 
						base::Timer _highlightTimer;
 | 
				
			||||||
	crl::time _highlightStart = 0;
 | 
						crl::time _highlightStart = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	base::flat_map<
 | 
					 | 
				
			||||||
		std::pair<not_null<History*>, SendAction::Type>,
 | 
					 | 
				
			||||||
		mtpRequestId> _sendActionRequests;
 | 
					 | 
				
			||||||
	base::Timer _sendActionStopTimer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	crl::time _saveDraftStart = 0;
 | 
						crl::time _saveDraftStart = 0;
 | 
				
			||||||
	bool _saveDraftText = false;
 | 
						bool _saveDraftText = false;
 | 
				
			||||||
	QTimer _saveDraftTimer, _saveCloudDraftTimer;
 | 
						QTimer _saveDraftTimer, _saveCloudDraftTimer;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "apiwrap.h"
 | 
					#include "apiwrap.h"
 | 
				
			||||||
#include "api/api_updates.h"
 | 
					#include "api/api_updates.h"
 | 
				
			||||||
 | 
					#include "api/api_send_progress.h"
 | 
				
			||||||
#include "core/application.h"
 | 
					#include "core/application.h"
 | 
				
			||||||
#include "main/main_account.h"
 | 
					#include "main/main_account.h"
 | 
				
			||||||
#include "main/main_domain.h"
 | 
					#include "main/main_domain.h"
 | 
				
			||||||
| 
						 | 
					@ -72,6 +73,7 @@ Session::Session(
 | 
				
			||||||
, _settings(std::move(settings))
 | 
					, _settings(std::move(settings))
 | 
				
			||||||
, _api(std::make_unique<ApiWrap>(this))
 | 
					, _api(std::make_unique<ApiWrap>(this))
 | 
				
			||||||
, _updates(std::make_unique<Api::Updates>(this))
 | 
					, _updates(std::make_unique<Api::Updates>(this))
 | 
				
			||||||
 | 
					, _sendProgressManager(std::make_unique<Api::SendProgressManager>(this))
 | 
				
			||||||
, _downloader(std::make_unique<Storage::DownloadManagerMtproto>(_api.get()))
 | 
					, _downloader(std::make_unique<Storage::DownloadManagerMtproto>(_api.get()))
 | 
				
			||||||
, _uploader(std::make_unique<Storage::Uploader>(_api.get()))
 | 
					, _uploader(std::make_unique<Storage::Uploader>(_api.get()))
 | 
				
			||||||
, _storage(std::make_unique<Storage::Facade>())
 | 
					, _storage(std::make_unique<Storage::Facade>())
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,6 +16,7 @@ class ApiWrap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Api {
 | 
					namespace Api {
 | 
				
			||||||
class Updates;
 | 
					class Updates;
 | 
				
			||||||
 | 
					class SendProgressManager;
 | 
				
			||||||
} // namespace Api
 | 
					} // namespace Api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace MTP {
 | 
					namespace MTP {
 | 
				
			||||||
| 
						 | 
					@ -87,6 +88,9 @@ public:
 | 
				
			||||||
	[[nodiscard]] Api::Updates &updates() const {
 | 
						[[nodiscard]] Api::Updates &updates() const {
 | 
				
			||||||
		return *_updates;
 | 
							return *_updates;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						[[nodiscard]] Api::SendProgressManager &sendProgressManager() const {
 | 
				
			||||||
 | 
							return *_sendProgressManager;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	[[nodiscard]] Storage::DownloadManagerMtproto &downloader() const {
 | 
						[[nodiscard]] Storage::DownloadManagerMtproto &downloader() const {
 | 
				
			||||||
		return *_downloader;
 | 
							return *_downloader;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -162,6 +166,7 @@ private:
 | 
				
			||||||
	const std::unique_ptr<SessionSettings> _settings;
 | 
						const std::unique_ptr<SessionSettings> _settings;
 | 
				
			||||||
	const std::unique_ptr<ApiWrap> _api;
 | 
						const std::unique_ptr<ApiWrap> _api;
 | 
				
			||||||
	const std::unique_ptr<Api::Updates> _updates;
 | 
						const std::unique_ptr<Api::Updates> _updates;
 | 
				
			||||||
 | 
						const std::unique_ptr<Api::SendProgressManager> _sendProgressManager;
 | 
				
			||||||
	const std::unique_ptr<Storage::DownloadManagerMtproto> _downloader;
 | 
						const std::unique_ptr<Storage::DownloadManagerMtproto> _downloader;
 | 
				
			||||||
	const std::unique_ptr<Storage::Uploader> _uploader;
 | 
						const std::unique_ptr<Storage::Uploader> _uploader;
 | 
				
			||||||
	const std::unique_ptr<Storage::Facade> _storage;
 | 
						const std::unique_ptr<Storage::Facade> _storage;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
#include "storage/file_upload.h"
 | 
					#include "storage/file_upload.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "api/api_send_progress.h"
 | 
				
			||||||
#include "storage/localimageloader.h"
 | 
					#include "storage/localimageloader.h"
 | 
				
			||||||
#include "storage/file_download.h"
 | 
					#include "storage/file_download.h"
 | 
				
			||||||
#include "data/data_document.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_photo.h"
 | 
				
			||||||
#include "data/data_session.h"
 | 
					#include "data/data_session.h"
 | 
				
			||||||
#include "ui/image/image_location_factory.h"
 | 
					#include "ui/image/image_location_factory.h"
 | 
				
			||||||
 | 
					#include "history/history_item.h"
 | 
				
			||||||
#include "core/mime_type.h"
 | 
					#include "core/mime_type.h"
 | 
				
			||||||
#include "main/main_session.h"
 | 
					#include "main/main_session.h"
 | 
				
			||||||
#include "apiwrap.h"
 | 
					#include "apiwrap.h"
 | 
				
			||||||
| 
						 | 
					@ -153,6 +155,141 @@ Uploader::Uploader(not_null<ApiWrap*> api)
 | 
				
			||||||
	connect(&nextTimer, SIGNAL(timeout()), this, SLOT(sendNext()));
 | 
						connect(&nextTimer, SIGNAL(timeout()), this, SLOT(sendNext()));
 | 
				
			||||||
	stopSessionsTimer.setSingleShot(true);
 | 
						stopSessionsTimer.setSingleShot(true);
 | 
				
			||||||
	connect(&stopSessionsTimer, SIGNAL(timeout()), this, SLOT(stopSessions()));
 | 
						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() {
 | 
					Uploader::~Uploader() {
 | 
				
			||||||
| 
						 | 
					@ -282,7 +419,9 @@ void Uploader::stopSessions() {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Uploader::sendNext() {
 | 
					void Uploader::sendNext() {
 | 
				
			||||||
	if (sentSize >= kMaxUploadFileParallelSize || _pausedId.msg) return;
 | 
						if (sentSize >= kMaxUploadFileParallelSize || _pausedId.msg) {
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool stopping = stopSessionsTimer.isActive();
 | 
						bool stopping = stopSessionsTimer.isActive();
 | 
				
			||||||
	if (queue.empty()) {
 | 
						if (queue.empty()) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -121,6 +121,11 @@ private:
 | 
				
			||||||
	void partLoaded(const MTPBool &result, mtpRequestId requestId);
 | 
						void partLoaded(const MTPBool &result, mtpRequestId requestId);
 | 
				
			||||||
	void partFailed(const RPCError &error, 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();
 | 
						void currentFailed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	not_null<ApiWrap*> _api;
 | 
						not_null<ApiWrap*> _api;
 | 
				
			||||||
| 
						 | 
					@ -147,6 +152,8 @@ private:
 | 
				
			||||||
	rpl::event_stream<FullMsgId> _documentFailed;
 | 
						rpl::event_stream<FullMsgId> _documentFailed;
 | 
				
			||||||
	rpl::event_stream<FullMsgId> _secureFailed;
 | 
						rpl::event_stream<FullMsgId> _secureFailed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rpl::lifetime _lifetime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace Storage
 | 
					} // namespace Storage
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
#include "ui/effects/send_action_animations.h"
 | 
					#include "ui/effects/send_action_animations.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "api/api_send_progress.h"
 | 
				
			||||||
#include "ui/effects/animation_value.h"
 | 
					#include "ui/effects/animation_value.h"
 | 
				
			||||||
#include "styles/style_widgets.h"
 | 
					#include "styles/style_widgets.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +18,7 @@ constexpr int kTypingDotsCount = 3;
 | 
				
			||||||
constexpr int kRecordArcsCount = 4;
 | 
					constexpr int kRecordArcsCount = 4;
 | 
				
			||||||
constexpr int kUploadArrowsCount = 3;
 | 
					constexpr int kUploadArrowsCount = 3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using ImplementationsMap = QMap<SendAction::Type, const SendActionAnimation::Impl::MetaData*>;
 | 
					using ImplementationsMap = QMap<Api::SendProgressType, const SendActionAnimation::Impl::MetaData*>;
 | 
				
			||||||
NeverFreedPointer<ImplementationsMap> Implementations;
 | 
					NeverFreedPointer<ImplementationsMap> Implementations;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TypingAnimation : public SendActionAnimation::Impl {
 | 
					class TypingAnimation : public SendActionAnimation::Impl {
 | 
				
			||||||
| 
						 | 
					@ -162,7 +163,7 @@ void CreateImplementationsMap() {
 | 
				
			||||||
	if (Implementations) {
 | 
						if (Implementations) {
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	using Type = SendAction::Type;
 | 
						using Type = Api::SendProgressType;
 | 
				
			||||||
	Implementations.createIfNull();
 | 
						Implementations.createIfNull();
 | 
				
			||||||
	Type recordTypes[] = {
 | 
						Type recordTypes[] = {
 | 
				
			||||||
		Type::RecordVideo,
 | 
							Type::RecordVideo,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,11 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Api {
 | 
				
			||||||
 | 
					enum class SendProgressType;
 | 
				
			||||||
 | 
					} // namespace Api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ui {
 | 
					namespace Ui {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SendActionAnimation {
 | 
					class SendActionAnimation {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	using Type = SendAction::Type;
 | 
						using Type = Api::SendProgressType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void start(Type type);
 | 
						void start(Type type);
 | 
				
			||||||
	void stop();
 | 
						void stop();
 | 
				
			||||||
| 
						 | 
					@ -31,7 +35,7 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	class Impl {
 | 
						class Impl {
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
		using Type = SendAction::Type;
 | 
							using Type = Api::SendProgressType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Impl(int period) : _period(period), _started(crl::now()) {
 | 
							Impl(int period) : _period(period), _started(crl::now()) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue