diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 4565022e3..8e3ea3e3c 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -192,6 +192,8 @@ nice_target_sources(Telegram ${src_loc} PRIVATE ${style_files} + api/api_authorizations.cpp + api/api_authorizations.h api/api_bot.cpp api/api_bot.h api/api_chat_filters.cpp diff --git a/Telegram/SourceFiles/api/api_authorizations.cpp b/Telegram/SourceFiles/api/api_authorizations.cpp new file mode 100644 index 000000000..404a717b3 --- /dev/null +++ b/Telegram/SourceFiles/api/api_authorizations.cpp @@ -0,0 +1,161 @@ +/* +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_authorizations.h" + +#include "apiwrap.h" +#include "base/unixtime.h" +#include "core/changelogs.h" +#include "lang/lang_keys.h" + +namespace Api { +namespace { + +constexpr auto TestApiId = 17349; +constexpr auto DesktopApiId = 2040; + +Authorizations::Entry ParseEntry(const MTPDauthorization &data) { + auto result = Authorizations::Entry(); + + result.hash = data.is_current() ? 0 : data.vhash().v; + result.incomplete = data.is_password_pending(); + + const auto apiId = data.vapi_id().v; + const auto isTest = (apiId == TestApiId); + const auto isDesktop = (apiId == DesktopApiId) || isTest; + + const auto appName = isDesktop + ? QString("Telegram Desktop%1").arg(isTest ? " (GitHub)" : QString()) + : qs(data.vapp_name());// +qsl(" for ") + qs(d.vplatform()); + const auto appVer = [&] { + const auto version = qs(data.vapp_version()); + if (isDesktop) { + const auto verInt = version.toInt(); + if (version == QString::number(verInt)) { + return Core::FormatVersionDisplay(verInt); + } + } else { + if (const auto index = version.indexOf('('); index >= 0) { + return version.mid(index); + } + } + return version; + }(); + + result.name = QString("%1%2") + .arg(appName) + .arg(appVer.isEmpty() ? QString() : (' ' + appVer)); + + 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 = QString("%1, %2%3") + .arg(qs(data.vdevice_model())) + .arg(platform.isEmpty() ? QString() : platform + ' ') + .arg(qs(data.vsystem_version())); + result.ip = qs(data.vip()) + + (country.isEmpty() + ? QString() + : QString::fromUtf8(" \xe2\x80\x93 ") + country); + if (!result.hash) { + result.active = tr::lng_status_online(tr::now); + } else { + const auto now = QDateTime::currentDateTime(); + const auto lastTime = base::unixtime::parse(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")); + } + } + + return result; +} + +} // namespace + +Authorizations::Authorizations(not_null api) +: _api(&api->instance()) { +} + +void Authorizations::reload() { + if (_requestId) { + return; + } + + _requestId = _api.request(MTPaccount_GetAuthorizations( + )).done([=](const MTPaccount_Authorizations &result) { + _requestId = 0; + result.match([&](const MTPDaccount_authorizations &auths) { + _list = ( + auths.vauthorizations().v + ) | ranges::view::transform([](const MTPAuthorization &d) { + return ParseEntry(d.c_authorization()); + }) | ranges::to; + _listChanges.fire({}); + }); + }).fail([=](const RPCError &error) { + _requestId = 0; + }).send(); +} + +void Authorizations::cancelCurrentRequest() { + _api.request(base::take(_requestId)).cancel(); +} + +void Authorizations::requestTerminate( + Fn &&done, + Fn &&fail, + std::optional hash) { + auto request = hash + ? MTPaccount_ResetAuthorization(MTP_long(*hash)) + : MTPaccount_ResetAuthorization(); + _api.request(std::move(request)) + .done(std::move(done)) + .fail(std::move(fail)) + .send(); +} + +Authorizations::List Authorizations::list() const { + return _list; +} + +auto Authorizations::listChanges() const +-> rpl::producer { + return rpl::single( + list() + ) | rpl::then( + _listChanges.events() | rpl::map([=] { return list(); })); +} + +rpl::producer Authorizations::totalChanges() const { + return rpl::single( + total() + ) | rpl::then( + _listChanges.events() | rpl::map([=] { return total(); })); +} + +int Authorizations::total() const { + return ranges::count_if( + _list, + ranges::not_fn(&Entry::incomplete)); +} + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_authorizations.h b/Telegram/SourceFiles/api/api_authorizations.h new file mode 100644 index 000000000..b01192c03 --- /dev/null +++ b/Telegram/SourceFiles/api/api_authorizations.h @@ -0,0 +1,50 @@ +/* +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 "mtproto/sender.h" + +class ApiWrap; + +namespace Api { + +class Authorizations final { +public: + explicit Authorizations(not_null api); + + struct Entry { + uint64 hash = 0; + + bool incomplete = false; + TimeId activeTime = 0; + QString name, active, info, ip; + }; + using List = std::vector; + + void reload(); + void cancelCurrentRequest(); + void requestTerminate( + Fn &&done, + Fn &&fail, + std::optional hash = std::nullopt); + + [[nodiscard]] List list() const; + [[nodiscard]] rpl::producer listChanges() const; + [[nodiscard]] int total() const; + [[nodiscard]] rpl::producer totalChanges() const; + +private: + MTP::Sender _api; + mtpRequestId _requestId = 0; + + List _list; + rpl::event_stream<> _listChanges; + +}; + +} // namespace Api diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index b3fd28e3f..92805db3d 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "apiwrap.h" +#include "api/api_authorizations.h" #include "api/api_hash.h" #include "api/api_media.h" #include "api/api_sending.h" @@ -186,6 +187,7 @@ ApiWrap::ApiWrap(not_null session) //, _feedReadTimer([=] { readFeeds(); }) // #feed , _topPromotionTimer([=] { refreshTopPromotion(); }) , _updateNotifySettingsTimer([=] { sendNotifySettingsUpdates(); }) +, _authorizations(std::make_unique(this)) , _selfDestruct(std::make_unique(this)) , _sensitiveContent(std::make_unique(this)) , _globalPrivacy(std::make_unique(this)) { @@ -5221,6 +5223,10 @@ auto ApiWrap::blockedPeersSlice() -> rpl::producer { : (_blockedPeersChanges.events() | rpl::type_erased()); } +Api::Authorizations &ApiWrap::authorizations() { + return *_authorizations; +} + Api::SelfDestruct &ApiWrap::selfDestruct() { return *_selfDestruct; } diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 49d85d5d7..3284a10cf 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -52,6 +52,7 @@ struct CloudPasswordState; namespace Api { class Updates; +class Authorizations; class SelfDestruct; class SensitiveContent; class GlobalPrivacy; @@ -453,6 +454,7 @@ public: void reloadBlockedPeers(); rpl::producer blockedPeersSlice(); + [[nodiscard]] Api::Authorizations &authorizations(); [[nodiscard]] Api::SelfDestruct &selfDestruct(); [[nodiscard]] Api::SensitiveContent &sensitiveContent(); [[nodiscard]] Api::GlobalPrivacy &globalPrivacy(); @@ -815,6 +817,7 @@ private: std::optional _blockedPeersSlice; rpl::event_stream _blockedPeersChanges; + const std::unique_ptr _authorizations; const std::unique_ptr _selfDestruct; const std::unique_ptr _sensitiveContent; const std::unique_ptr _globalPrivacy; diff --git a/Telegram/SourceFiles/boxes/sessions_box.cpp b/Telegram/SourceFiles/boxes/sessions_box.cpp index 84a283eef..c23798706 100644 --- a/Telegram/SourceFiles/boxes/sessions_box.cpp +++ b/Telegram/SourceFiles/boxes/sessions_box.cpp @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/sessions_box.h" +#include "apiwrap.h" +#include "api/api_authorizations.h" #include "base/timer.h" #include "base/unixtime.h" #include "boxes/confirm_box.h" @@ -42,6 +44,17 @@ protected: private: struct Entry { + Entry() = default; + Entry(const Api::Authorizations::Entry &entry) + : hash(entry.hash) + , incomplete(entry.incomplete) + , activeTime(entry.activeTime) + , name(entry.name) + , active(entry.active) + , info(entry.info) + , ip(entry.ip) { + }; + uint64 hash = 0; bool incomplete = false; @@ -57,18 +70,15 @@ private: class Inner; class List; - static Entry ParseEntry(const MTPDauthorization &data); static void ResizeEntry(Entry &entry); void shortPollSessions(); - void got(const MTPaccount_Authorizations &result); - void terminate(Fn terminateRequest, QString message); void terminateOne(uint64 hash); void terminateAll(); const not_null _session; - MTP::Sender _api; + const not_null _authorizations; rpl::variable _loading = false; Full _data; @@ -77,7 +87,6 @@ private: QPointer _terminateBox; base::Timer _shortPollTimer; - mtpRequestId _shortPollRequest = 0; }; @@ -126,7 +135,7 @@ private: SessionsContent::SessionsContent(QWidget*, not_null session) : _session(session) -, _api(&_session->mtp()) +, _authorizations(&_session->api().authorizations()) , _inner(this) , _shortPollTimer([=] { shortPollSessions(); }) { } @@ -160,6 +169,31 @@ void SessionsContent::setupContent() { _inner->setVisible(!value); }, lifetime()); + _authorizations->listChanges( + ) | rpl::start_with_next([=](const Api::Authorizations::List &list) { + _data = Full(); + for (const auto auth : list) { + auto entry = Entry(auth); + ResizeEntry(entry); + if (!entry.hash) { + _data.current = std::move(entry); + } else if (entry.incomplete) { + _data.incomplete.push_back(std::move(entry)); + } else { + _data.list.push_back(std::move(entry)); + } + } + + _loading = false; + + ranges::sort(_data.list, std::greater<>(), &Entry::activeTime); + ranges::sort(_data.incomplete, std::greater<>(), &Entry::activeTime); + + _inner->showData(_data); + + _shortPollTimer.callOnce(kSessionsShortPollTimeout); + }, lifetime()); + _loading = true; shortPollSessions(); } @@ -185,120 +219,9 @@ void SessionsContent::paintEvent(QPaintEvent *e) { } } -void SessionsContent::got(const MTPaccount_Authorizations &result) { - _shortPollRequest = 0; - _loading = false; - _data = Full(); - - result.match([&](const MTPDaccount_authorizations &data) { - const auto &list = data.vauthorizations().v; - for (const auto &auth : list) { - auth.match([&](const MTPDauthorization &data) { - auto entry = ParseEntry(data); - if (!entry.hash) { - _data.current = std::move(entry); - } else if (entry.incomplete) { - _data.incomplete.push_back(std::move(entry)); - } else { - _data.list.push_back(std::move(entry)); - } - }); - } - }); - - 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); -} - -SessionsContent::Entry SessionsContent::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('(')); - } - } - result.name = appName; - if (!appVer.isEmpty()) { - result.name += ' ' + appVer; - } - - 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 = tr::lng_status_online(tr::now); - result.activeWidth = st::sessionWhenFont->width(tr::lng_status_online(tr::now)); - } else { - const auto now = QDateTime::currentDateTime(); - const auto lastTime = base::unixtime::parse(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 SessionsContent::ResizeEntry(Entry &entry) { + entry.activeWidth = st::sessionWhenFont->width(entry.active); + const auto available = st::boxWideWidth - st::sessionPadding.left() - st::sessionTerminateSkip; @@ -325,13 +248,7 @@ void SessionsContent::ResizeEntry(Entry &entry) { } void SessionsContent::shortPollSessions() { - if (_shortPollRequest) { - return; - } - _shortPollRequest = _api.request(MTPaccount_GetAuthorizations( - )).done([=](const MTPaccount_Authorizations &result) { - got(result); - }).send(); + _authorizations->reload(); update(); } @@ -356,10 +273,9 @@ void SessionsContent::terminate(Fn terminateRequest, QString message) { } void SessionsContent::terminateOne(uint64 hash) { + const auto weak = Ui::MakeWeak(this); auto callback = [=] { - _api.request(MTPaccount_ResetAuthorization( - MTP_long(hash) - )).done([=](const MTPBool &result) { + auto done = crl::guard(weak, [=](const MTPBool &result) { _inner->terminatingOne(hash, false); const auto getHash = [](const Entry &entry) { return entry.hash; @@ -372,24 +288,29 @@ void SessionsContent::terminateOne(uint64 hash) { removeByHash(_data.incomplete); removeByHash(_data.list); _inner->showData(_data); - }).fail([=](const RPCError &error) { + }); + auto fail = crl::guard(weak, [=](const RPCError &error) { _inner->terminatingOne(hash, false); - }).send(); + }); + _authorizations->requestTerminate( + std::move(done), + std::move(fail), + hash); _inner->terminatingOne(hash, true); }; terminate(std::move(callback), tr::lng_settings_reset_one_sure(tr::now)); } void SessionsContent::terminateAll() { + const auto weak = Ui::MakeWeak(this); auto callback = [=] { - _api.request(MTPauth_ResetAuthorizations( - )).done([=](const MTPBool &result) { - _api.request(base::take(_shortPollRequest)).cancel(); + const auto reset = crl::guard(weak, [=] { + _authorizations->cancelCurrentRequest(); shortPollSessions(); - }).fail([=](const RPCError &result) { - _api.request(base::take(_shortPollRequest)).cancel(); - shortPollSessions(); - }).send(); + }); + _authorizations->requestTerminate( + [=](const MTPBool &result) { reset(); }, + [=](const RPCError &result) { reset(); }); _loading = true; }; terminate(std::move(callback), tr::lng_settings_reset_sure(tr::now)); diff --git a/Telegram/SourceFiles/core/changelogs.cpp b/Telegram/SourceFiles/core/changelogs.cpp index 159f8a0ee..6dc84e216 100644 --- a/Telegram/SourceFiles/core/changelogs.cpp +++ b/Telegram/SourceFiles/core/changelogs.cpp @@ -98,20 +98,6 @@ std::map BetaLogs() { }; }; -QString FormatVersionDisplay(int version) { - return QString::number(version / 1000000) - + '.' + QString::number((version % 1000000) / 1000) - + ((version % 1000) - ? ('.' + QString::number(version % 1000)) - : QString()); -} - -QString FormatVersionPrecise(int version) { - return QString::number(version / 1000000) - + '.' + QString::number((version % 1000000) / 1000) - + '.' + QString::number(version % 1000); -} - } // namespace Changelogs::Changelogs(not_null session, int oldVersion) @@ -216,4 +202,18 @@ void Changelogs::addBetaLog(int changeVersion, const char *changes) { addLocalLog(log); } +QString FormatVersionDisplay(int version) { + return QString::number(version / 1000000) + + '.' + QString::number((version % 1000000) / 1000) + + ((version % 1000) + ? ('.' + QString::number(version % 1000)) + : QString()); +} + +QString FormatVersionPrecise(int version) { + return QString::number(version / 1000000) + + '.' + QString::number((version % 1000000) / 1000) + + '.' + QString::number(version % 1000); +} + } // namespace Core diff --git a/Telegram/SourceFiles/core/changelogs.h b/Telegram/SourceFiles/core/changelogs.h index 598f4eb5f..f888fa468 100644 --- a/Telegram/SourceFiles/core/changelogs.h +++ b/Telegram/SourceFiles/core/changelogs.h @@ -15,6 +15,9 @@ class Session; namespace Core { +[[nodiscard]] QString FormatVersionDisplay(int version); +[[nodiscard]] QString FormatVersionPrecise(int version); + class Changelogs : public base::has_weak_ptr, private base::Subscriber { public: Changelogs(not_null session, int oldVersion); diff --git a/Telegram/SourceFiles/core/update_checker.cpp b/Telegram/SourceFiles/core/update_checker.cpp index c291bd209..2c4fb4794 100644 --- a/Telegram/SourceFiles/core/update_checker.cpp +++ b/Telegram/SourceFiles/core/update_checker.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unixtime.h" #include "storage/localstorage.h" #include "core/application.h" +#include "core/changelogs.h" #include "core/click_handler_types.h" #include "mainwindow.h" #include "main/main_account.h" @@ -448,7 +449,7 @@ bool UnpackUpdate(const QString &filepath) { // create tdata/version file tempDir.mkdir(QDir(tempDirPath + qsl("/tdata")).absolutePath()); - std::wstring versionString = ((version % 1000) ? QString("%1.%2.%3").arg(int(version / 1000000)).arg(int((version % 1000000) / 1000)).arg(int(version % 1000)) : QString("%1.%2").arg(int(version / 1000000)).arg(int((version % 1000000) / 1000))).toStdWString(); + std::wstring versionString = FormatVersionDisplay(version).toStdWString(); const auto versionNum = VersionInt(version); const auto versionLen = VersionInt(versionString.size() * sizeof(VersionChar));