mirror of https://github.com/procxx/kepka.git
				
				
				
			Add support for incomplete login attempts.
This commit is contained in:
		
							parent
							
								
									770f4a78aa
								
							
						
					
					
						commit
						21fab77c4b
					
				| 
						 | 
					@ -549,6 +549,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
				
			||||||
"lng_sessions_other_header" = "Active sessions";
 | 
					"lng_sessions_other_header" = "Active sessions";
 | 
				
			||||||
"lng_sessions_other_desc" = "You can log in to Telegram from other mobile, tablet and desktop devices, using the same phone number. All your data will be instantly synchronized.";
 | 
					"lng_sessions_other_desc" = "You can log in to Telegram from other mobile, tablet and desktop devices, using the same phone number. All your data will be instantly synchronized.";
 | 
				
			||||||
"lng_sessions_terminate_all" = "Terminate all other sessions";
 | 
					"lng_sessions_terminate_all" = "Terminate all other sessions";
 | 
				
			||||||
 | 
					"lng_sessions_terminate_all_about" = "Logs out all devices except for this one.";
 | 
				
			||||||
 | 
					"lng_sessions_incomplete" = "Incomplete login attempts";
 | 
				
			||||||
 | 
					"lng_sessions_incomplete_about" = "The devices above have no access to your messages. The code was entered correctly, but no correct password was given.";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"lng_blocked_list_title" = "Blocked users";
 | 
					"lng_blocked_list_title" = "Blocked users";
 | 
				
			||||||
"lng_blocked_list_unknown_phone" = "unknown phone number";
 | 
					"lng_blocked_list_unknown_phone" = "unknown phone number";
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -77,7 +77,9 @@ void BoxContent::finishPrepare() {
 | 
				
			||||||
void BoxContent::finishScrollCreate() {
 | 
					void BoxContent::finishScrollCreate() {
 | 
				
			||||||
	Expects(_scroll != nullptr);
 | 
						Expects(_scroll != nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_scroll->show();
 | 
						if (!_scroll->isHidden()) {
 | 
				
			||||||
 | 
							_scroll->show();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	updateScrollAreaGeometry();
 | 
						updateScrollAreaGeometry();
 | 
				
			||||||
	connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
 | 
						connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
 | 
				
			||||||
	connect(_scroll, SIGNAL(innerResized()), this, SLOT(onInnerResize()));
 | 
						connect(_scroll, SIGNAL(innerResized()), this, SLOT(onInnerResize()));
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -443,7 +443,7 @@ sessionsHeight: 350px;
 | 
				
			||||||
sessionHeight: 70px;
 | 
					sessionHeight: 70px;
 | 
				
			||||||
sessionCurrentPadding: margins(0px, 7px, 0px, 4px);
 | 
					sessionCurrentPadding: margins(0px, 7px, 0px, 4px);
 | 
				
			||||||
sessionCurrentHeight: 118px;
 | 
					sessionCurrentHeight: 118px;
 | 
				
			||||||
sessionPadding: margins(21px, 10px, 21px, 0px);
 | 
					sessionPadding: margins(23px, 10px, 23px, 0px);
 | 
				
			||||||
sessionNameFont: msgNameFont;
 | 
					sessionNameFont: msgNameFont;
 | 
				
			||||||
sessionNameFg: boxTextFg;
 | 
					sessionNameFg: boxTextFg;
 | 
				
			||||||
sessionWhenFont: msgDateFont;
 | 
					sessionWhenFont: msgDateFont;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,34 +12,100 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
				
			||||||
#include "mainwidget.h"
 | 
					#include "mainwidget.h"
 | 
				
			||||||
#include "mainwindow.h"
 | 
					#include "mainwindow.h"
 | 
				
			||||||
#include "countries.h"
 | 
					#include "countries.h"
 | 
				
			||||||
 | 
					#include "auth_session.h"
 | 
				
			||||||
 | 
					#include "data/data_session.h"
 | 
				
			||||||
#include "boxes/confirm_box.h"
 | 
					#include "boxes/confirm_box.h"
 | 
				
			||||||
 | 
					#include "info/profile/info_profile_button.h"
 | 
				
			||||||
 | 
					#include "settings/settings_common.h"
 | 
				
			||||||
#include "ui/widgets/buttons.h"
 | 
					#include "ui/widgets/buttons.h"
 | 
				
			||||||
#include "ui/widgets/scroll_area.h"
 | 
					#include "ui/widgets/scroll_area.h"
 | 
				
			||||||
 | 
					#include "ui/widgets/labels.h"
 | 
				
			||||||
 | 
					#include "ui/wrap/slide_wrap.h"
 | 
				
			||||||
 | 
					#include "ui/wrap/vertical_layout.h"
 | 
				
			||||||
#include "styles/style_boxes.h"
 | 
					#include "styles/style_boxes.h"
 | 
				
			||||||
 | 
					#include "styles/style_info.h"
 | 
				
			||||||
 | 
					#include "styles/style_settings.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					constexpr auto kSessionsShortPollTimeout = 60 * TimeMs(1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SessionsBox::List : public Ui::RpWidget {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						List(QWidget *parent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void showData(gsl::span<const Entry> items);
 | 
				
			||||||
 | 
						rpl::producer<int> itemsCount() const;
 | 
				
			||||||
 | 
						rpl::producer<uint64> terminate() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void terminating(uint64 hash, bool terminating);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						void paintEvent(QPaintEvent *e) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int resizeGetHeight(int newWidth) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						std::vector<Entry> _items;
 | 
				
			||||||
 | 
						std::map<uint64, std::unique_ptr<Ui::IconButton>> _terminateButtons;
 | 
				
			||||||
 | 
						rpl::event_stream<uint64> _terminate;
 | 
				
			||||||
 | 
						rpl::event_stream<int> _itemsCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SessionsBox::Inner : public Ui::RpWidget {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						Inner(QWidget *parent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void showData(const Full &data);
 | 
				
			||||||
 | 
						rpl::producer<uint64> terminateOne() const;
 | 
				
			||||||
 | 
						rpl::producer<> terminateAll() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void terminatingOne(uint64 hash, bool terminating);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						void setupContent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QPointer<List> _current;
 | 
				
			||||||
 | 
						QPointer<Info::Profile::Button> _terminateAll;
 | 
				
			||||||
 | 
						QPointer<List> _incomplete;
 | 
				
			||||||
 | 
						QPointer<List> _list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SessionsBox::SessionsBox(QWidget*)
 | 
					SessionsBox::SessionsBox(QWidget*)
 | 
				
			||||||
: _shortPollTimer(this) {
 | 
					: _shortPollTimer([=] { shortPollSessions(); }) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SessionsBox::prepare() {
 | 
					void SessionsBox::prepare() {
 | 
				
			||||||
	setTitle(langFactory(lng_sessions_other_header));
 | 
						setTitle(langFactory(lng_sessions_other_header));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	addButton(langFactory(lng_close), [this] { closeBox(); });
 | 
						addButton(langFactory(lng_close), [=] { closeBox(); });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	setDimensions(st::boxWideWidth, st::sessionsHeight);
 | 
						setDimensions(st::boxWideWidth, st::sessionsHeight);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_inner = setInnerWidget(object_ptr<Inner>(this, &_list, &_current), st::sessionsScroll);
 | 
						_inner = setInnerWidget(object_ptr<Inner>(this), st::sessionsScroll);
 | 
				
			||||||
	_inner->resize(width(), st::noContactsHeight);
 | 
						_inner->resize(width(), st::noContactsHeight);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	connect(_inner, SIGNAL(oneTerminated()), this, SLOT(onOneTerminated()));
 | 
						_inner->terminateOne(
 | 
				
			||||||
	connect(_inner, SIGNAL(allTerminated()), this, SLOT(onAllTerminated()));
 | 
						) | rpl::start_with_next([=](uint64 hash) {
 | 
				
			||||||
	connect(_inner, SIGNAL(terminateAll()), this, SLOT(onTerminateAll()));
 | 
							terminateOne(hash);
 | 
				
			||||||
	connect(App::wnd(), SIGNAL(checkNewAuthorization()), this, SLOT(onCheckNewAuthorization()));
 | 
						}, lifetime());
 | 
				
			||||||
	connect(_shortPollTimer, SIGNAL(timeout()), this, SLOT(onShortPollAuthorizations()));
 | 
					
 | 
				
			||||||
 | 
						_inner->terminateAll(
 | 
				
			||||||
 | 
						) | rpl::start_with_next([=] {
 | 
				
			||||||
 | 
							terminateAll();
 | 
				
			||||||
 | 
						}, lifetime());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Auth().data().newAuthorizationChecks(
 | 
				
			||||||
 | 
						) | rpl::start_with_next([=] {
 | 
				
			||||||
 | 
							shortPollSessions();
 | 
				
			||||||
 | 
						}, lifetime());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	setLoading(true);
 | 
						setLoading(true);
 | 
				
			||||||
 | 
						shortPollSessions();
 | 
				
			||||||
	MTP::send(MTPaccount_GetAuthorizations(), rpcDone(&SessionsBox::gotAuthorizations));
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SessionsBox::setLoading(bool loading) {
 | 
					void SessionsBox::setLoading(bool loading) {
 | 
				
			||||||
| 
						 | 
					@ -63,331 +129,420 @@ void SessionsBox::paintEvent(QPaintEvent *e) {
 | 
				
			||||||
	if (_loading) {
 | 
						if (_loading) {
 | 
				
			||||||
		p.setFont(st::noContactsFont);
 | 
							p.setFont(st::noContactsFont);
 | 
				
			||||||
		p.setPen(st::noContactsColor);
 | 
							p.setPen(st::noContactsColor);
 | 
				
			||||||
		p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center);
 | 
							p.drawText(
 | 
				
			||||||
 | 
								QRect(0, 0, width(), st::noContactsHeight),
 | 
				
			||||||
 | 
								lang(lng_contacts_loading),
 | 
				
			||||||
 | 
								style::al_center);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) {
 | 
					void SessionsBox::got(const MTPaccount_Authorizations &result) {
 | 
				
			||||||
	_shortPollRequest = 0;
 | 
						_shortPollRequest = 0;
 | 
				
			||||||
	setLoading(false);
 | 
						setLoading(false);
 | 
				
			||||||
 | 
						_data = Full();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const auto availCurrent = st::boxWideWidth - st::sessionPadding.left() - st::sessionTerminateSkip;
 | 
						result.match([&](const MTPDaccount_authorizations &data) {
 | 
				
			||||||
	const auto availOther = availCurrent - st::sessionTerminate.iconPosition.x();
 | 
							const auto &list = data.vauthorizations.v;
 | 
				
			||||||
	const auto availInfo = availCurrent - st::sessionTerminate.width;
 | 
							for (const auto &auth : list) {
 | 
				
			||||||
 | 
								auth.match([&](const MTPDauthorization &data) {
 | 
				
			||||||
	_list.clear();
 | 
									auto entry = ParseEntry(data);
 | 
				
			||||||
	if (result.type() != mtpc_account_authorizations) {
 | 
									if (!entry.hash) {
 | 
				
			||||||
		return;
 | 
										_data.current = std::move(entry);
 | 
				
			||||||
	}
 | 
									} else if (entry.incomplete) {
 | 
				
			||||||
	auto &v = result.c_account_authorizations().vauthorizations.v;
 | 
										_data.incomplete.push_back(std::move(entry));
 | 
				
			||||||
	_list.reserve(v.size());
 | 
									} else {
 | 
				
			||||||
 | 
										_data.list.push_back(std::move(entry));
 | 
				
			||||||
	const CountriesByISO2 &countries(countriesByISO2());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for_const (auto &auth, v) {
 | 
					 | 
				
			||||||
		if (auth.type() != mtpc_authorization) {
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		auto &d = auth.c_authorization();
 | 
					 | 
				
			||||||
		Data data;
 | 
					 | 
				
			||||||
		data.hash = d.vhash.v;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		QString appName, appVer = qs(d.vapp_version), systemVer = qs(d.vsystem_version), deviceModel = qs(d.vdevice_model);
 | 
					 | 
				
			||||||
		if (d.vapi_id.v == 2040 || d.vapi_id.v == 17349) {
 | 
					 | 
				
			||||||
			appName = (d.vapi_id.v == 2040) ? qstr("Telegram Desktop") : qstr("Telegram Desktop (GitHub)");
 | 
					 | 
				
			||||||
		//	if (systemVer == qstr("windows")) {
 | 
					 | 
				
			||||||
		//		deviceModel = qsl("Windows");
 | 
					 | 
				
			||||||
		//	} else if (systemVer == qstr("os x")) {
 | 
					 | 
				
			||||||
		//		deviceModel = qsl("OS X");
 | 
					 | 
				
			||||||
		//	} else if (systemVer == qstr("linux")) {
 | 
					 | 
				
			||||||
		//		deviceModel = qsl("Linux");
 | 
					 | 
				
			||||||
		//	}
 | 
					 | 
				
			||||||
			if (appVer == QString::number(appVer.toInt())) {
 | 
					 | 
				
			||||||
				int32 ver = appVer.toInt();
 | 
					 | 
				
			||||||
				appVer = QString("%1.%2").arg(ver / 1000000).arg((ver % 1000000) / 1000) + ((ver % 1000) ? ('.' + QString::number(ver % 1000)) : QString());
 | 
					 | 
				
			||||||
			//} else {
 | 
					 | 
				
			||||||
			//	appVer = QString();
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			appName = qs(d.vapp_name);// +qsl(" for ") + qs(d.vplatform);
 | 
					 | 
				
			||||||
			if (appVer.indexOf('(') >= 0) appVer = appVer.mid(appVer.indexOf('('));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		data.name = appName;
 | 
					 | 
				
			||||||
		if (!appVer.isEmpty()) data.name += ' ' + appVer;
 | 
					 | 
				
			||||||
		data.nameWidth = st::sessionNameFont->width(data.name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		QString country = qs(d.vcountry), platform = qs(d.vplatform);
 | 
					 | 
				
			||||||
		//CountriesByISO2::const_iterator j = countries.constFind(country);
 | 
					 | 
				
			||||||
		//if (j != countries.cend()) country = QString::fromUtf8(j.value()->name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		const auto active = data.activeTime = d.vdate_active.v
 | 
					 | 
				
			||||||
			? d.vdate_active.v
 | 
					 | 
				
			||||||
			: d.vdate_created.v;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		data.info = qs(d.vdevice_model) + qstr(", ") + (platform.isEmpty() ? QString() : platform + ' ') + qs(d.vsystem_version);
 | 
					 | 
				
			||||||
		data.ip = qs(d.vip) + (country.isEmpty() ? QString() : QString::fromUtf8(" \xe2\x80\x93 ") + country);
 | 
					 | 
				
			||||||
		if (!data.hash || (d.vflags.v & 1)) {
 | 
					 | 
				
			||||||
			data.active = lang(lng_sessions_header);
 | 
					 | 
				
			||||||
			data.activeWidth = st::sessionWhenFont->width(lang(lng_sessions_header));
 | 
					 | 
				
			||||||
			int32 availForName = availCurrent - st::sessionPadding.right() - data.activeWidth;
 | 
					 | 
				
			||||||
			if (data.nameWidth > availForName) {
 | 
					 | 
				
			||||||
				data.name = st::sessionNameFont->elided(data.name, availForName);
 | 
					 | 
				
			||||||
				data.nameWidth = st::sessionNameFont->width(data.name);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			data.infoWidth = st::sessionInfoFont->width(data.info);
 | 
					 | 
				
			||||||
			if (data.infoWidth > availCurrent) {
 | 
					 | 
				
			||||||
				data.info = st::sessionInfoFont->elided(data.info, availCurrent);
 | 
					 | 
				
			||||||
				data.infoWidth = st::sessionInfoFont->width(data.info);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			data.ipWidth = st::sessionInfoFont->width(data.ip);
 | 
					 | 
				
			||||||
			if (data.ipWidth > availCurrent) {
 | 
					 | 
				
			||||||
				data.ip = st::sessionInfoFont->elided(data.ip, availCurrent);
 | 
					 | 
				
			||||||
				data.ipWidth = st::sessionInfoFont->width(data.ip);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			_current = data;
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			const auto now = QDateTime::currentDateTime();
 | 
					 | 
				
			||||||
			const auto lastTime = ParseDateTime(active);
 | 
					 | 
				
			||||||
			const auto nowDate = now.date();
 | 
					 | 
				
			||||||
			const auto lastDate = lastTime.date();
 | 
					 | 
				
			||||||
			QString dt;
 | 
					 | 
				
			||||||
			if (lastDate == nowDate) {
 | 
					 | 
				
			||||||
				data.active = lastTime.toString(cTimeFormat());
 | 
					 | 
				
			||||||
			} else if (lastDate.year() == nowDate.year()
 | 
					 | 
				
			||||||
				&& lastDate.weekNumber() == nowDate.weekNumber()) {
 | 
					 | 
				
			||||||
				data.active = langDayOfWeek(lastDate);
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				data.active = lastDate.toString(qsl("d.MM.yy"));
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			data.activeWidth = st::sessionWhenFont->width(data.active);
 | 
					 | 
				
			||||||
			int32 availForName = availOther - st::sessionPadding.right() - data.activeWidth;
 | 
					 | 
				
			||||||
			if (data.nameWidth > availForName) {
 | 
					 | 
				
			||||||
				data.name = st::sessionNameFont->elided(data.name, availForName);
 | 
					 | 
				
			||||||
				data.nameWidth = st::sessionNameFont->width(data.name);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			data.infoWidth = st::sessionInfoFont->width(data.info);
 | 
					 | 
				
			||||||
			if (data.infoWidth > availInfo) {
 | 
					 | 
				
			||||||
				data.info = st::sessionInfoFont->elided(data.info, availInfo);
 | 
					 | 
				
			||||||
				data.infoWidth = st::sessionInfoFont->width(data.info);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			data.ipWidth = st::sessionInfoFont->width(data.ip);
 | 
					 | 
				
			||||||
			if (data.ipWidth > availOther) {
 | 
					 | 
				
			||||||
				data.ip = st::sessionInfoFont->elided(data.ip, availOther);
 | 
					 | 
				
			||||||
				data.ipWidth = st::sessionInfoFont->width(data.ip);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			_list.push_back(data);
 | 
					 | 
				
			||||||
			for (int32 i = _list.size(); i > 1;) {
 | 
					 | 
				
			||||||
				--i;
 | 
					 | 
				
			||||||
				if (_list.at(i).activeTime > _list.at(i - 1).activeTime) {
 | 
					 | 
				
			||||||
					qSwap(_list[i], _list[i - 1]);
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const auto getActiveTime = [](const Entry &entry) {
 | 
				
			||||||
 | 
							return entry.activeTime;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						ranges::sort(_data.list, std::greater<>(), getActiveTime);
 | 
				
			||||||
 | 
						ranges::sort(_data.incomplete, std::greater<>(), getActiveTime);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_inner->showData(_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_shortPollTimer.callOnce(kSessionsShortPollTimeout);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SessionsBox::Entry SessionsBox::ParseEntry(const MTPDauthorization &data) {
 | 
				
			||||||
 | 
						auto result = Entry();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result.hash = data.is_current() ? 0 : data.vhash.v;
 | 
				
			||||||
 | 
						result.incomplete = data.is_password_pending();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto appName = QString();
 | 
				
			||||||
 | 
						auto appVer = qs(data.vapp_version);
 | 
				
			||||||
 | 
						const auto systemVer = qs(data.vsystem_version);
 | 
				
			||||||
 | 
						const auto deviceModel = qs(data.vdevice_model);
 | 
				
			||||||
 | 
						const auto apiId = data.vapi_id.v;
 | 
				
			||||||
 | 
						if (apiId == 2040 || apiId == 17349) {
 | 
				
			||||||
 | 
							appName = (apiId == 2040)
 | 
				
			||||||
 | 
								? qstr("Telegram Desktop")
 | 
				
			||||||
 | 
								: qstr("Telegram Desktop (GitHub)");
 | 
				
			||||||
 | 
							//if (systemVer == qstr("windows")) {
 | 
				
			||||||
 | 
							//	deviceModel = qsl("Windows");
 | 
				
			||||||
 | 
							//} else if (systemVer == qstr("os x")) {
 | 
				
			||||||
 | 
							//	deviceModel = qsl("OS X");
 | 
				
			||||||
 | 
							//} else if (systemVer == qstr("linux")) {
 | 
				
			||||||
 | 
							//	deviceModel = qsl("Linux");
 | 
				
			||||||
 | 
							//}
 | 
				
			||||||
 | 
							if (appVer == QString::number(appVer.toInt())) {
 | 
				
			||||||
 | 
								const auto ver = appVer.toInt();
 | 
				
			||||||
 | 
								appVer = QString("%1.%2"
 | 
				
			||||||
 | 
								).arg(ver / 1000000
 | 
				
			||||||
 | 
								).arg((ver % 1000000) / 1000)
 | 
				
			||||||
 | 
									+ ((ver % 1000)
 | 
				
			||||||
 | 
										? ('.' + QString::number(ver % 1000))
 | 
				
			||||||
 | 
										: QString());
 | 
				
			||||||
 | 
							//} else {
 | 
				
			||||||
 | 
							//	appVer = QString();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							appName = qs(data.vapp_name);// +qsl(" for ") + qs(d.vplatform);
 | 
				
			||||||
 | 
							if (appVer.indexOf('(') >= 0) {
 | 
				
			||||||
 | 
								appVer = appVer.mid(appVer.indexOf('('));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_inner->listUpdated();
 | 
						result.name = appName;
 | 
				
			||||||
 | 
						if (!appVer.isEmpty()) {
 | 
				
			||||||
	update();
 | 
							result.name += ' ' + appVer;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	_shortPollTimer->start(SessionsShortPollTimeout);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void SessionsBox::onOneTerminated() {
 | 
					 | 
				
			||||||
	update();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void SessionsBox::onShortPollAuthorizations() {
 | 
					 | 
				
			||||||
	if (!_shortPollRequest) {
 | 
					 | 
				
			||||||
		_shortPollRequest = MTP::send(MTPaccount_GetAuthorizations(), rpcDone(&SessionsBox::gotAuthorizations));
 | 
					 | 
				
			||||||
		update();
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const auto country = qs(data.vcountry);
 | 
				
			||||||
 | 
						const auto platform = qs(data.vplatform);
 | 
				
			||||||
 | 
						//const auto &countries = countriesByISO2();
 | 
				
			||||||
 | 
						//const auto j = countries.constFind(country);
 | 
				
			||||||
 | 
						//if (j != countries.cend()) {
 | 
				
			||||||
 | 
						//	country = QString::fromUtf8(j.value()->name);
 | 
				
			||||||
 | 
						//}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result.activeTime = data.vdate_active.v
 | 
				
			||||||
 | 
							? data.vdate_active.v
 | 
				
			||||||
 | 
							: data.vdate_created.v;
 | 
				
			||||||
 | 
						result.info = qs(data.vdevice_model) + qstr(", ") + (platform.isEmpty() ? QString() : platform + ' ') + qs(data.vsystem_version);
 | 
				
			||||||
 | 
						result.ip = qs(data.vip) + (country.isEmpty() ? QString() : QString::fromUtf8(" \xe2\x80\x93 ") + country);
 | 
				
			||||||
 | 
						if (!result.hash) {
 | 
				
			||||||
 | 
							result.active = lang(lng_status_online);
 | 
				
			||||||
 | 
							result.activeWidth = st::sessionWhenFont->width(lang(lng_status_online));
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							const auto now = QDateTime::currentDateTime();
 | 
				
			||||||
 | 
							const auto lastTime = ParseDateTime(result.activeTime);
 | 
				
			||||||
 | 
							const auto nowDate = now.date();
 | 
				
			||||||
 | 
							const auto lastDate = lastTime.date();
 | 
				
			||||||
 | 
							if (lastDate == nowDate) {
 | 
				
			||||||
 | 
								result.active = lastTime.toString(cTimeFormat());
 | 
				
			||||||
 | 
							} else if (lastDate.year() == nowDate.year()
 | 
				
			||||||
 | 
								&& lastDate.weekNumber() == nowDate.weekNumber()) {
 | 
				
			||||||
 | 
								result.active = langDayOfWeek(lastDate);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								result.active = lastDate.toString(qsl("d.MM.yy"));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							result.activeWidth = st::sessionWhenFont->width(result.active);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ResizeEntry(result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SessionsBox::onCheckNewAuthorization() {
 | 
					void SessionsBox::ResizeEntry(Entry &entry) {
 | 
				
			||||||
	onShortPollAuthorizations();
 | 
						const auto available = st::boxWideWidth
 | 
				
			||||||
//	_shortPollTimer.start(1000);
 | 
							- st::sessionPadding.left()
 | 
				
			||||||
 | 
							- st::sessionTerminateSkip;
 | 
				
			||||||
 | 
						const auto availableInList = available
 | 
				
			||||||
 | 
							- st::sessionTerminate.iconPosition.x();
 | 
				
			||||||
 | 
						const auto availableListInfo = available - st::sessionTerminate.width;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const auto resize = [](
 | 
				
			||||||
 | 
								const style::font &font,
 | 
				
			||||||
 | 
								QString &string,
 | 
				
			||||||
 | 
								int &stringWidth,
 | 
				
			||||||
 | 
								int available) {
 | 
				
			||||||
 | 
							stringWidth = font->width(string);
 | 
				
			||||||
 | 
							if (stringWidth > available) {
 | 
				
			||||||
 | 
								string = font->elided(string, available);
 | 
				
			||||||
 | 
								stringWidth = font->width(string);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						const auto forName = entry.hash ? availableInList : available;
 | 
				
			||||||
 | 
						const auto forInfo = entry.hash ? availableListInfo : available;
 | 
				
			||||||
 | 
						resize(st::sessionNameFont, entry.name, entry.nameWidth, forName);
 | 
				
			||||||
 | 
						resize(st::sessionInfoFont, entry.info, entry.infoWidth, forInfo);
 | 
				
			||||||
 | 
						resize(st::sessionInfoFont, entry.ip, entry.ipWidth, available);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SessionsBox::onAllTerminated() {
 | 
					void SessionsBox::shortPollSessions() {
 | 
				
			||||||
	MTP::send(MTPaccount_GetAuthorizations(), rpcDone(&SessionsBox::gotAuthorizations));
 | 
					 | 
				
			||||||
	if (_shortPollRequest) {
 | 
						if (_shortPollRequest) {
 | 
				
			||||||
		MTP::cancel(_shortPollRequest);
 | 
					 | 
				
			||||||
		_shortPollRequest = 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void SessionsBox::onTerminateAll() {
 | 
					 | 
				
			||||||
	setLoading(true);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SessionsBox::Inner::Inner(QWidget *parent, SessionsBox::List *list, SessionsBox::Data *current) : TWidget(parent)
 | 
					 | 
				
			||||||
, _list(list)
 | 
					 | 
				
			||||||
, _current(current)
 | 
					 | 
				
			||||||
, _terminateAll(this, lang(lng_sessions_terminate_all), st::sessionTerminateAllButton) {
 | 
					 | 
				
			||||||
	connect(_terminateAll, SIGNAL(clicked()), this, SLOT(onTerminateAll()));
 | 
					 | 
				
			||||||
	_terminateAll->hide();
 | 
					 | 
				
			||||||
	setAttribute(Qt::WA_OpaquePaintEvent);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void SessionsBox::Inner::paintEvent(QPaintEvent *e) {
 | 
					 | 
				
			||||||
	QRect r(e->rect());
 | 
					 | 
				
			||||||
	Painter p(this);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	p.fillRect(r, st::boxBg);
 | 
					 | 
				
			||||||
	int32 x = st::sessionPadding.left(), xact = st::sessionTerminateSkip + st::sessionTerminate.iconPosition.x();// st::sessionTerminateSkip + st::sessionTerminate.width + st::sessionTerminateSkip;
 | 
					 | 
				
			||||||
	int32 w = width();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (_current->active.isEmpty() && _list->isEmpty()) {
 | 
					 | 
				
			||||||
		p.setFont(st::noContactsFont->f);
 | 
					 | 
				
			||||||
		p.setPen(st::noContactsColor->p);
 | 
					 | 
				
			||||||
		p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center);
 | 
					 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						_shortPollRequest = request(MTPaccount_GetAuthorizations(
 | 
				
			||||||
	p.translate(0, st::sessionCurrentPadding.top());
 | 
						)).done([=](const MTPaccount_Authorizations &result) {
 | 
				
			||||||
	if (r.y() <= st::sessionCurrentHeight) {
 | 
							got(result);
 | 
				
			||||||
		p.setFont(st::sessionNameFont);
 | 
						}).send();
 | 
				
			||||||
		p.setPen(st::sessionNameFg);
 | 
						update();
 | 
				
			||||||
		p.drawTextLeft(x, st::sessionPadding.top(), w, _current->name, _current->nameWidth);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		p.setFont(st::sessionWhenFont);
 | 
					 | 
				
			||||||
		p.setPen(st::sessionWhenFg);
 | 
					 | 
				
			||||||
		p.drawTextRight(x, st::sessionPadding.top(), w, _current->active, _current->activeWidth);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		p.setFont(st::sessionInfoFont);
 | 
					 | 
				
			||||||
		p.setPen(st::boxTextFg);
 | 
					 | 
				
			||||||
		p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height, w, _current->info, _current->infoWidth);
 | 
					 | 
				
			||||||
		p.setPen(st::sessionInfoFg);
 | 
					 | 
				
			||||||
		p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height + st::sessionInfoFont->height, w, _current->ip, _current->ipWidth);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	p.translate(0, st::sessionCurrentHeight - st::sessionCurrentPadding.top());
 | 
					 | 
				
			||||||
	if (_list->isEmpty()) {
 | 
					 | 
				
			||||||
		p.setFont(st::sessionInfoFont);
 | 
					 | 
				
			||||||
		p.setPen(st::sessionInfoFg);
 | 
					 | 
				
			||||||
		p.drawText(QRect(st::sessionPadding.left(), 0, width() - st::sessionPadding.left() - st::sessionPadding.right(), st::noContactsHeight), lang(lng_sessions_other_desc), style::al_topleft);
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	p.setFont(st::linkFont->f);
 | 
					 | 
				
			||||||
	int32 count = _list->size();
 | 
					 | 
				
			||||||
	int32 from = floorclamp(r.y() - st::sessionCurrentHeight, st::sessionHeight, 0, count);
 | 
					 | 
				
			||||||
	int32 to = ceilclamp(r.y() + r.height() - st::sessionCurrentHeight, st::sessionHeight, 0, count);
 | 
					 | 
				
			||||||
	p.translate(0, from * st::sessionHeight);
 | 
					 | 
				
			||||||
	for (int32 i = from; i < to; ++i) {
 | 
					 | 
				
			||||||
		const SessionsBox::Data &auth(_list->at(i));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		p.setFont(st::sessionNameFont);
 | 
					 | 
				
			||||||
		p.setPen(st::sessionNameFg);
 | 
					 | 
				
			||||||
		p.drawTextLeft(x, st::sessionPadding.top(), w, auth.name, auth.nameWidth);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		p.setFont(st::sessionWhenFont);
 | 
					 | 
				
			||||||
		p.setPen(st::sessionWhenFg);
 | 
					 | 
				
			||||||
		p.drawTextRight(xact, st::sessionPadding.top(), w, auth.active, auth.activeWidth);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		p.setFont(st::sessionInfoFont);
 | 
					 | 
				
			||||||
		p.setPen(st::boxTextFg);
 | 
					 | 
				
			||||||
		p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height, w, auth.info, auth.infoWidth);
 | 
					 | 
				
			||||||
		p.setPen(st::sessionInfoFg);
 | 
					 | 
				
			||||||
		p.drawTextLeft(x, st::sessionPadding.top() + st::sessionNameFont->height + st::sessionInfoFont->height, w, auth.ip, auth.ipWidth);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		p.translate(0, st::sessionHeight);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SessionsBox::Inner::onTerminate() {
 | 
					void SessionsBox::terminateOne(uint64 hash) {
 | 
				
			||||||
	for (auto i = _terminateButtons.begin(), e = _terminateButtons.end(); i != e; ++i) {
 | 
					 | 
				
			||||||
		if (i.value()->isOver()) {
 | 
					 | 
				
			||||||
			if (_terminateBox) _terminateBox->deleteLater();
 | 
					 | 
				
			||||||
			_terminateBox = Ui::show(Box<ConfirmBox>(lang(lng_settings_reset_one_sure), lang(lng_settings_reset_button), st::attentionBoxButton, crl::guard(this, [this, terminating = i.key()] {
 | 
					 | 
				
			||||||
				if (_terminateBox) {
 | 
					 | 
				
			||||||
					_terminateBox->closeBox();
 | 
					 | 
				
			||||||
					_terminateBox = nullptr;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				MTP::send(MTPaccount_ResetAuthorization(MTP_long(terminating)), rpcDone(&Inner::terminateDone, terminating), rpcFail(&Inner::terminateFail, terminating));
 | 
					 | 
				
			||||||
				auto i = _terminateButtons.find(terminating);
 | 
					 | 
				
			||||||
				if (i != _terminateButtons.cend()) {
 | 
					 | 
				
			||||||
					i.value()->clearState();
 | 
					 | 
				
			||||||
					i.value()->hide();
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			})), LayerOption::KeepOther);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void SessionsBox::Inner::onTerminateAll() {
 | 
					 | 
				
			||||||
	if (_terminateBox) _terminateBox->deleteLater();
 | 
						if (_terminateBox) _terminateBox->deleteLater();
 | 
				
			||||||
	_terminateBox = Ui::show(Box<ConfirmBox>(lang(lng_settings_reset_sure), lang(lng_settings_reset_button), st::attentionBoxButton, crl::guard(this, [this] {
 | 
						const auto callback = crl::guard(this, [=] {
 | 
				
			||||||
		if (_terminateBox) {
 | 
							if (_terminateBox) {
 | 
				
			||||||
			_terminateBox->closeBox();
 | 
								_terminateBox->closeBox();
 | 
				
			||||||
			_terminateBox = nullptr;
 | 
								_terminateBox = nullptr;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		MTP::send(MTPauth_ResetAuthorizations(), rpcDone(&Inner::terminateAllDone), rpcFail(&Inner::terminateAllFail));
 | 
							request(MTPaccount_ResetAuthorization(
 | 
				
			||||||
		emit terminateAll();
 | 
								MTP_long(hash)
 | 
				
			||||||
	})), LayerOption::KeepOther);
 | 
							)).done([=](const MTPBool &result) {
 | 
				
			||||||
 | 
								_inner->terminatingOne(hash, false);
 | 
				
			||||||
 | 
								const auto getHash = [](const Entry &entry) {
 | 
				
			||||||
 | 
									return entry.hash;
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
								const auto removeByHash = [&](std::vector<Entry> &list) {
 | 
				
			||||||
 | 
									list.erase(
 | 
				
			||||||
 | 
										ranges::remove(list, hash, getHash),
 | 
				
			||||||
 | 
										end(list));
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
								removeByHash(_data.incomplete);
 | 
				
			||||||
 | 
								removeByHash(_data.list);
 | 
				
			||||||
 | 
								_inner->showData(_data);
 | 
				
			||||||
 | 
							}).fail([=](const RPCError &error) {
 | 
				
			||||||
 | 
								_inner->terminatingOne(hash, false);
 | 
				
			||||||
 | 
							}).send();
 | 
				
			||||||
 | 
							_inner->terminatingOne(hash, true);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						_terminateBox = Ui::show(
 | 
				
			||||||
 | 
							Box<ConfirmBox>(
 | 
				
			||||||
 | 
								lang(lng_settings_reset_one_sure),
 | 
				
			||||||
 | 
								lang(lng_settings_reset_button),
 | 
				
			||||||
 | 
								st::attentionBoxButton,
 | 
				
			||||||
 | 
								callback),
 | 
				
			||||||
 | 
							LayerOption::KeepOther);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SessionsBox::Inner::terminateDone(uint64 hash, const MTPBool &result) {
 | 
					void SessionsBox::terminateAll() {
 | 
				
			||||||
	for (int32 i = 0, l = _list->size(); i < l; ++i) {
 | 
						if (_terminateBox) _terminateBox->deleteLater();
 | 
				
			||||||
		if (_list->at(i).hash == hash) {
 | 
						const auto callback = crl::guard(this, [=] {
 | 
				
			||||||
			_list->removeAt(i);
 | 
							if (_terminateBox) {
 | 
				
			||||||
			break;
 | 
								_terminateBox->closeBox();
 | 
				
			||||||
 | 
								_terminateBox = nullptr;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
							request(MTPauth_ResetAuthorizations(
 | 
				
			||||||
	listUpdated();
 | 
							)).done([=](const MTPBool &result) {
 | 
				
			||||||
	emit oneTerminated();
 | 
								request(base::take(_shortPollRequest)).cancel();
 | 
				
			||||||
 | 
								shortPollSessions();
 | 
				
			||||||
 | 
							}).fail([=](const RPCError &result) {
 | 
				
			||||||
 | 
								request(base::take(_shortPollRequest)).cancel();
 | 
				
			||||||
 | 
								shortPollSessions();
 | 
				
			||||||
 | 
							}).send();
 | 
				
			||||||
 | 
							setLoading(true);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						_terminateBox = Ui::show(
 | 
				
			||||||
 | 
							Box<ConfirmBox>(
 | 
				
			||||||
 | 
								lang(lng_settings_reset_sure),
 | 
				
			||||||
 | 
								lang(lng_settings_reset_button),
 | 
				
			||||||
 | 
								st::attentionBoxButton,
 | 
				
			||||||
 | 
								callback),
 | 
				
			||||||
 | 
							LayerOption::KeepOther);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool SessionsBox::Inner::terminateFail(uint64 hash, const RPCError &error) {
 | 
					SessionsBox::Inner::Inner(QWidget *parent)
 | 
				
			||||||
	if (MTP::isDefaultHandledError(error)) return false;
 | 
					: RpWidget(parent) {
 | 
				
			||||||
 | 
						setupContent();
 | 
				
			||||||
	TerminateButtons::iterator i = _terminateButtons.find(hash);
 | 
					 | 
				
			||||||
	if (i != _terminateButtons.end()) {
 | 
					 | 
				
			||||||
		i.value()->show();
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return false;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SessionsBox::Inner::terminateAllDone(const MTPBool &result) {
 | 
					void SessionsBox::Inner::setupContent() {
 | 
				
			||||||
	emit allTerminated();
 | 
						using namespace Settings;
 | 
				
			||||||
 | 
						using namespace rpl::mappers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						AddSubsectionTitle(content, lng_sessions_header);
 | 
				
			||||||
 | 
						_current = content->add(object_ptr<List>(content));
 | 
				
			||||||
 | 
						const auto terminateWrap = content->add(
 | 
				
			||||||
 | 
							object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
 | 
				
			||||||
 | 
								content,
 | 
				
			||||||
 | 
								object_ptr<Ui::VerticalLayout>(content)))->setDuration(0);
 | 
				
			||||||
 | 
						const auto terminateInner = terminateWrap->entity();
 | 
				
			||||||
 | 
						_terminateAll = terminateInner->add(
 | 
				
			||||||
 | 
							object_ptr<Info::Profile::Button>(
 | 
				
			||||||
 | 
								terminateInner,
 | 
				
			||||||
 | 
								Lang::Viewer(lng_sessions_terminate_all),
 | 
				
			||||||
 | 
								st::terminateSessionsButton));
 | 
				
			||||||
 | 
						AddSkip(terminateInner);
 | 
				
			||||||
 | 
						AddDividerText(
 | 
				
			||||||
 | 
							terminateInner,
 | 
				
			||||||
 | 
							Lang::Viewer(lng_sessions_terminate_all_about));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const auto incompleteWrap = content->add(
 | 
				
			||||||
 | 
							object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
 | 
				
			||||||
 | 
								content,
 | 
				
			||||||
 | 
								object_ptr<Ui::VerticalLayout>(content)))->setDuration(0);
 | 
				
			||||||
 | 
						const auto incompleteInner = incompleteWrap->entity();
 | 
				
			||||||
 | 
						AddSkip(incompleteInner);
 | 
				
			||||||
 | 
						AddSubsectionTitle(incompleteInner, lng_sessions_incomplete);
 | 
				
			||||||
 | 
						_incomplete = incompleteInner->add(object_ptr<List>(incompleteInner));
 | 
				
			||||||
 | 
						AddSkip(incompleteInner);
 | 
				
			||||||
 | 
						AddDividerText(
 | 
				
			||||||
 | 
							incompleteInner,
 | 
				
			||||||
 | 
							Lang::Viewer(lng_sessions_incomplete_about));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const auto listWrap = content->add(
 | 
				
			||||||
 | 
							object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
 | 
				
			||||||
 | 
								content,
 | 
				
			||||||
 | 
								object_ptr<Ui::VerticalLayout>(content)))->setDuration(0);
 | 
				
			||||||
 | 
						const auto listInner = listWrap->entity();
 | 
				
			||||||
 | 
						AddSkip(listInner);
 | 
				
			||||||
 | 
						AddSubsectionTitle(listInner, lng_sessions_other_header);
 | 
				
			||||||
 | 
						_list = listInner->add(object_ptr<List>(listInner));
 | 
				
			||||||
 | 
						AddSkip(listInner);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const auto placeholder = content->add(
 | 
				
			||||||
 | 
							object_ptr<Ui::SlideWrap<Ui::FlatLabel>>(
 | 
				
			||||||
 | 
								content,
 | 
				
			||||||
 | 
								object_ptr<Ui::FlatLabel>(
 | 
				
			||||||
 | 
									content,
 | 
				
			||||||
 | 
									Lang::Viewer(lng_sessions_other_desc),
 | 
				
			||||||
 | 
									st::boxDividerLabel),
 | 
				
			||||||
 | 
								st::settingsDividerLabelPadding))->setDuration(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						terminateWrap->toggleOn(
 | 
				
			||||||
 | 
							rpl::combine(
 | 
				
			||||||
 | 
								_incomplete->itemsCount(),
 | 
				
			||||||
 | 
								_list->itemsCount(),
 | 
				
			||||||
 | 
								(_1 + _2) > 0));
 | 
				
			||||||
 | 
						incompleteWrap->toggleOn(_incomplete->itemsCount() | rpl::map(_1 > 0));
 | 
				
			||||||
 | 
						listWrap->toggleOn(_list->itemsCount() | rpl::map(_1 > 0));
 | 
				
			||||||
 | 
						placeholder->toggleOn(_list->itemsCount() | rpl::map(_1 == 0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Ui::ResizeFitChild(this, content);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool SessionsBox::Inner::terminateAllFail(const RPCError &error) {
 | 
					void SessionsBox::Inner::showData(const Full &data) {
 | 
				
			||||||
	if (MTP::isDefaultHandledError(error)) return false;
 | 
						_current->showData({ &data.current, &data.current + 1 });
 | 
				
			||||||
	emit allTerminated();
 | 
						_list->showData(data.list);
 | 
				
			||||||
	return true;
 | 
						_incomplete->showData(data.incomplete);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SessionsBox::Inner::resizeEvent(QResizeEvent *e) {
 | 
					rpl::producer<> SessionsBox::Inner::terminateAll() const {
 | 
				
			||||||
	_terminateAll->moveToLeft(st::sessionPadding.left(), st::sessionCurrentPadding.top() + st::sessionHeight + st::sessionCurrentPadding.bottom());
 | 
						return _terminateAll->clicks() | rpl::map([] {
 | 
				
			||||||
 | 
							return rpl::empty_value();
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SessionsBox::Inner::listUpdated() {
 | 
					rpl::producer<uint64> SessionsBox::Inner::terminateOne() const {
 | 
				
			||||||
	if (_list->isEmpty()) {
 | 
						return rpl::merge(
 | 
				
			||||||
		_terminateAll->hide();
 | 
							_incomplete->terminate(),
 | 
				
			||||||
	} else {
 | 
							_list->terminate());
 | 
				
			||||||
		_terminateAll->show();
 | 
					}
 | 
				
			||||||
	}
 | 
					
 | 
				
			||||||
	for (TerminateButtons::iterator i = _terminateButtons.begin(), e = _terminateButtons.end(); i != e; ++i) {
 | 
					void SessionsBox::Inner::terminatingOne(uint64 hash, bool terminating) {
 | 
				
			||||||
		i.value()->move(0, -1);
 | 
						_incomplete->terminating(hash, terminating);
 | 
				
			||||||
	}
 | 
						_list->terminating(hash, terminating);
 | 
				
			||||||
	for (int32 i = 0, l = _list->size(); i < l; ++i) {
 | 
					}
 | 
				
			||||||
		TerminateButtons::iterator j = _terminateButtons.find(_list->at(i).hash);
 | 
					
 | 
				
			||||||
		if (j == _terminateButtons.cend()) {
 | 
					SessionsBox::List::List(QWidget *parent) : RpWidget(parent) {
 | 
				
			||||||
			j = _terminateButtons.insert(_list->at(i).hash, new Ui::IconButton(this, st::sessionTerminate));
 | 
						setAttribute(Qt::WA_OpaquePaintEvent);
 | 
				
			||||||
			connect(j.value(), SIGNAL(clicked()), this, SLOT(onTerminate()));
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SessionsBox::List::showData(gsl::span<const Entry> items) {
 | 
				
			||||||
 | 
						auto buttons = base::take(_terminateButtons);
 | 
				
			||||||
 | 
						_items.clear();
 | 
				
			||||||
 | 
						_items.insert(begin(_items), items.begin(), items.end());
 | 
				
			||||||
 | 
						for (const auto &entry : _items) {
 | 
				
			||||||
 | 
							const auto hash = entry.hash;
 | 
				
			||||||
 | 
							if (!hash) {
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		j.value()->moveToRight(st::sessionTerminateSkip, st::sessionCurrentHeight + i * st::sessionHeight + st::sessionTerminateTop, width());
 | 
							const auto button = [&] {
 | 
				
			||||||
		j.value()->show();
 | 
								const auto i = buttons.find(hash);
 | 
				
			||||||
 | 
								return _terminateButtons.emplace(
 | 
				
			||||||
 | 
									hash,
 | 
				
			||||||
 | 
									(i != end(buttons)
 | 
				
			||||||
 | 
										? std::move(i->second)
 | 
				
			||||||
 | 
										: std::make_unique<Ui::IconButton>(
 | 
				
			||||||
 | 
											this,
 | 
				
			||||||
 | 
											st::sessionTerminate))).first->second.get();
 | 
				
			||||||
 | 
							}();
 | 
				
			||||||
 | 
							button->setClickedCallback([=] {
 | 
				
			||||||
 | 
								_terminate.fire_copy(hash);
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
							button->show();
 | 
				
			||||||
 | 
							button->moveToRight(
 | 
				
			||||||
 | 
								st::sessionTerminateSkip,
 | 
				
			||||||
 | 
								((_terminateButtons.size() - 1) * st::sessionHeight
 | 
				
			||||||
 | 
									+ st::sessionTerminateTop));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for (TerminateButtons::iterator i = _terminateButtons.begin(); i != _terminateButtons.cend();) {
 | 
						resizeToWidth(width());
 | 
				
			||||||
		if (i.value()->y() >= 0) {
 | 
						_itemsCount.fire(_items.size());
 | 
				
			||||||
			++i;
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					rpl::producer<int> SessionsBox::List::itemsCount() const {
 | 
				
			||||||
 | 
						return _itemsCount.events_starting_with(_items.size());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					rpl::producer<uint64> SessionsBox::List::terminate() const {
 | 
				
			||||||
 | 
						return _terminate.events();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SessionsBox::List::terminating(uint64 hash, bool terminating) {
 | 
				
			||||||
 | 
						const auto i = _terminateButtons.find(hash);
 | 
				
			||||||
 | 
						if (i != _terminateButtons.cend()) {
 | 
				
			||||||
 | 
							if (terminating) {
 | 
				
			||||||
 | 
								i->second->clearState();
 | 
				
			||||||
 | 
								i->second->hide();
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			delete i.value();
 | 
								i->second->show();
 | 
				
			||||||
			i = _terminateButtons.erase(i);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	resize(width(), _list->isEmpty() ? (st::sessionCurrentHeight + st::noContactsHeight) : (st::sessionCurrentHeight + _list->size() * st::sessionHeight));
 | 
					}
 | 
				
			||||||
	update();
 | 
					
 | 
				
			||||||
 | 
					int SessionsBox::List::resizeGetHeight(int newWidth) {
 | 
				
			||||||
 | 
						return _items.size() * st::sessionHeight;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SessionsBox::List::paintEvent(QPaintEvent *e) {
 | 
				
			||||||
 | 
						QRect r(e->rect());
 | 
				
			||||||
 | 
						Painter p(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.fillRect(r, st::boxBg);
 | 
				
			||||||
 | 
						p.setFont(st::linkFont);
 | 
				
			||||||
 | 
						const auto count = int(_items.size());
 | 
				
			||||||
 | 
						const auto from = floorclamp(r.y(), st::sessionHeight, 0, count);
 | 
				
			||||||
 | 
						const auto till = ceilclamp(
 | 
				
			||||||
 | 
							r.y() + r.height(),
 | 
				
			||||||
 | 
							st::sessionHeight,
 | 
				
			||||||
 | 
							0,
 | 
				
			||||||
 | 
							count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const auto x = st::sessionPadding.left();
 | 
				
			||||||
 | 
						const auto y = st::sessionPadding.top();
 | 
				
			||||||
 | 
						const auto w = width();
 | 
				
			||||||
 | 
						const auto xact = st::sessionTerminateSkip
 | 
				
			||||||
 | 
							+ st::sessionTerminate.iconPosition.x();
 | 
				
			||||||
 | 
						p.translate(0, from * st::sessionHeight);
 | 
				
			||||||
 | 
						for (auto i = from; i != till; ++i) {
 | 
				
			||||||
 | 
							const auto &entry = _items[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							p.setFont(st::sessionNameFont);
 | 
				
			||||||
 | 
							p.setPen(st::sessionNameFg);
 | 
				
			||||||
 | 
							p.drawTextLeft(x, y, w, entry.name, entry.nameWidth);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							p.setFont(st::sessionWhenFont);
 | 
				
			||||||
 | 
							p.setPen(st::sessionWhenFg);
 | 
				
			||||||
 | 
							p.drawTextRight(xact, y, w, entry.active, entry.activeWidth);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const auto name = st::sessionNameFont->height;
 | 
				
			||||||
 | 
							p.setFont(st::sessionInfoFont);
 | 
				
			||||||
 | 
							p.setPen(st::boxTextFg);
 | 
				
			||||||
 | 
							p.drawTextLeft(x, y + name, w, entry.info, entry.infoWidth);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const auto info = st::sessionInfoFont->height;
 | 
				
			||||||
 | 
							p.setPen(st::sessionInfoFg);
 | 
				
			||||||
 | 
							p.drawTextLeft(x, y + name + info, w, entry.ip, entry.ipWidth);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							p.translate(0, st::sessionHeight);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "boxes/abstract_box.h"
 | 
					#include "boxes/abstract_box.h"
 | 
				
			||||||
#include "core/single_timer.h"
 | 
					#include "mtproto/sender.h"
 | 
				
			||||||
 | 
					#include "base/timer.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ConfirmBox;
 | 
					class ConfirmBox;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,9 +18,7 @@ class IconButton;
 | 
				
			||||||
class LinkButton;
 | 
					class LinkButton;
 | 
				
			||||||
} // namespace Ui
 | 
					} // namespace Ui
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SessionsBox : public BoxContent, public RPCSender {
 | 
					class SessionsBox : public BoxContent, private MTP::Sender {
 | 
				
			||||||
	Q_OBJECT
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	SessionsBox(QWidget*);
 | 
						SessionsBox(QWidget*);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,75 +28,40 @@ protected:
 | 
				
			||||||
	void resizeEvent(QResizeEvent *e) override;
 | 
						void resizeEvent(QResizeEvent *e) override;
 | 
				
			||||||
	void paintEvent(QPaintEvent *e) override;
 | 
						void paintEvent(QPaintEvent *e) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private slots:
 | 
					 | 
				
			||||||
	void onOneTerminated();
 | 
					 | 
				
			||||||
	void onAllTerminated();
 | 
					 | 
				
			||||||
	void onTerminateAll();
 | 
					 | 
				
			||||||
	void onShortPollAuthorizations();
 | 
					 | 
				
			||||||
	void onCheckNewAuthorization();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	void setLoading(bool loading);
 | 
						struct Entry {
 | 
				
			||||||
	struct Data {
 | 
							uint64 hash = 0;
 | 
				
			||||||
		uint64 hash;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		int32 activeTime;
 | 
							bool incomplete = false;
 | 
				
			||||||
		int32 nameWidth, activeWidth, infoWidth, ipWidth;
 | 
							TimeId activeTime = 0;
 | 
				
			||||||
 | 
							int nameWidth, activeWidth, infoWidth, ipWidth;
 | 
				
			||||||
		QString name, active, info, ip;
 | 
							QString name, active, info, ip;
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	using List = QList<Data>;
 | 
						struct Full {
 | 
				
			||||||
 | 
							Entry current;
 | 
				
			||||||
 | 
							std::vector<Entry> incomplete;
 | 
				
			||||||
 | 
							std::vector<Entry> list;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						class Inner;
 | 
				
			||||||
 | 
						class List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void gotAuthorizations(const MTPaccount_Authorizations &result);
 | 
						static Entry ParseEntry(const MTPDauthorization &data);
 | 
				
			||||||
 | 
						static void ResizeEntry(Entry &entry);
 | 
				
			||||||
 | 
						void setLoading(bool loading);
 | 
				
			||||||
 | 
						void shortPollSessions();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void got(const MTPaccount_Authorizations &result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void terminateOne(uint64 hash);
 | 
				
			||||||
 | 
						void terminateAll();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool _loading = false;
 | 
						bool _loading = false;
 | 
				
			||||||
 | 
						Full _data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Data _current;
 | 
					 | 
				
			||||||
	List _list;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	class Inner;
 | 
					 | 
				
			||||||
	QPointer<Inner> _inner;
 | 
						QPointer<Inner> _inner;
 | 
				
			||||||
 | 
						QPointer<ConfirmBox> _terminateBox;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	object_ptr<SingleTimer> _shortPollTimer;
 | 
						base::Timer _shortPollTimer;
 | 
				
			||||||
	mtpRequestId _shortPollRequest = 0;
 | 
						mtpRequestId _shortPollRequest = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
// This class is hold in header because it requires Qt preprocessing.
 | 
					 | 
				
			||||||
class SessionsBox::Inner : public TWidget, public RPCSender {
 | 
					 | 
				
			||||||
	Q_OBJECT
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	Inner(QWidget *parent, SessionsBox::List *list, SessionsBox::Data *current);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void listUpdated();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
protected:
 | 
					 | 
				
			||||||
	void paintEvent(QPaintEvent *e) override;
 | 
					 | 
				
			||||||
	void resizeEvent(QResizeEvent *e) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
signals:
 | 
					 | 
				
			||||||
	void oneTerminated();
 | 
					 | 
				
			||||||
	void allTerminated();
 | 
					 | 
				
			||||||
	void terminateAll();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public slots:
 | 
					 | 
				
			||||||
	void onTerminate();
 | 
					 | 
				
			||||||
	void onTerminateAll();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	void terminateDone(uint64 hash, const MTPBool &result);
 | 
					 | 
				
			||||||
	bool terminateFail(uint64 hash, const RPCError &error);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void terminateAllDone(const MTPBool &res);
 | 
					 | 
				
			||||||
	bool terminateAllFail(const RPCError &error);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	SessionsBox::List *_list;
 | 
					 | 
				
			||||||
	SessionsBox::Data *_current;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	typedef QMap<uint64, Ui::IconButton*> TerminateButtons;
 | 
					 | 
				
			||||||
	TerminateButtons _terminateButtons;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	object_ptr<Ui::LinkButton> _terminateAll;
 | 
					 | 
				
			||||||
	QPointer<ConfirmBox> _terminateBox;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -89,7 +89,6 @@ enum {
 | 
				
			||||||
	UpdateDelayRandPart = 8 * 3600, // 8 hour max - min time between update check requests
 | 
						UpdateDelayRandPart = 8 * 3600, // 8 hour max - min time between update check requests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	WrongPasscodeTimeout = 1500,
 | 
						WrongPasscodeTimeout = 1500,
 | 
				
			||||||
	SessionsShortPollTimeout = 60000,
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ChoosePeerByDragTimeout = 1000, // 1 second mouse not moved to choose dialog when dragging a file
 | 
						ChoosePeerByDragTimeout = 1000, // 1 second mouse not moved to choose dialog when dragging a file
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2001,6 +2001,14 @@ void Session::serviceNotification(
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Session::checkNewAuthorization() {
 | 
				
			||||||
 | 
						_newAuthorizationChecks.fire({});
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					rpl::producer<> Session::newAuthorizationChecks() const {
 | 
				
			||||||
 | 
						return _newAuthorizationChecks.events();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Session::insertCheckedServiceNotification(
 | 
					void Session::insertCheckedServiceNotification(
 | 
				
			||||||
		const TextWithEntities &message,
 | 
							const TextWithEntities &message,
 | 
				
			||||||
		const MTPMessageMedia &media,
 | 
							const MTPMessageMedia &media,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -423,6 +423,8 @@ public:
 | 
				
			||||||
	void serviceNotification(
 | 
						void serviceNotification(
 | 
				
			||||||
		const TextWithEntities &message,
 | 
							const TextWithEntities &message,
 | 
				
			||||||
		const MTPMessageMedia &media = MTP_messageMediaEmpty());
 | 
							const MTPMessageMedia &media = MTP_messageMediaEmpty());
 | 
				
			||||||
 | 
						void checkNewAuthorization();
 | 
				
			||||||
 | 
						rpl::producer<> newAuthorizationChecks() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void setMimeForwardIds(MessageIdsList &&list);
 | 
						void setMimeForwardIds(MessageIdsList &&list);
 | 
				
			||||||
	MessageIdsList takeMimeForwardIds();
 | 
						MessageIdsList takeMimeForwardIds();
 | 
				
			||||||
| 
						 | 
					@ -656,6 +658,8 @@ private:
 | 
				
			||||||
		int>;
 | 
							int>;
 | 
				
			||||||
	std::unique_ptr<CredentialsWithGeneration> _passportCredentials;
 | 
						std::unique_ptr<CredentialsWithGeneration> _passportCredentials;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rpl::event_stream<> _newAuthorizationChecks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rpl::lifetime _lifetime;
 | 
						rpl::lifetime _lifetime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -588,6 +588,10 @@ managePeerButtonLabel: FlatLabel(defaultFlatLabel) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
managePeerButtonLabelPosition: point(25px, 10px);
 | 
					managePeerButtonLabelPosition: point(25px, 10px);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					terminateSessionsButton: InfoProfileButton(infoBlockButton) {
 | 
				
			||||||
 | 
						padding: margins(23px, 12px, 23px, 10px);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
infoEmptyFg: windowSubTextFg;
 | 
					infoEmptyFg: windowSubTextFg;
 | 
				
			||||||
infoEmptyPhoto: icon {{ "info_media_photo_empty", infoEmptyFg }};
 | 
					infoEmptyPhoto: icon {{ "info_media_photo_empty", infoEmptyFg }};
 | 
				
			||||||
infoEmptyVideo: icon {{ "info_media_video_empty", infoEmptyFg }};
 | 
					infoEmptyVideo: icon {{ "info_media_video_empty", infoEmptyFg }};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4578,7 +4578,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
 | 
				
			||||||
			Ui::show(Box<InformBox>(text));
 | 
								Ui::show(Box<InformBox>(text));
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			Auth().data().serviceNotification(text, d.vmedia);
 | 
								Auth().data().serviceNotification(text, d.vmedia);
 | 
				
			||||||
			emit App::wnd()->checkNewAuthorization();
 | 
								Auth().data().checkNewAuthorization();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} break;
 | 
						} break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -148,7 +148,6 @@ public slots:
 | 
				
			||||||
signals:
 | 
					signals:
 | 
				
			||||||
	void tempDirCleared(int task);
 | 
						void tempDirCleared(int task);
 | 
				
			||||||
	void tempDirClearFailed(int task);
 | 
						void tempDirClearFailed(int task);
 | 
				
			||||||
	void checkNewAuthorization();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	[[nodiscard]] bool skipTrayClick() const;
 | 
						[[nodiscard]] bool skipTrayClick() const;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue