From 51cead144559233aed032970a1f06950871a794d Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 9 Oct 2022 14:25:41 +0300 Subject: [PATCH] Added initial implementation of api for usernames. --- Telegram/CMakeLists.txt | 3 + Telegram/SourceFiles/api/api_user_names.cpp | 203 ++++++++++++++++++++ Telegram/SourceFiles/api/api_user_names.h | 53 +++++ Telegram/SourceFiles/apiwrap.cpp | 8 +- Telegram/SourceFiles/apiwrap.h | 3 + Telegram/SourceFiles/data/data_user_names.h | 20 ++ 6 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 Telegram/SourceFiles/api/api_user_names.cpp create mode 100644 Telegram/SourceFiles/api/api_user_names.h create mode 100644 Telegram/SourceFiles/data/data_user_names.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index cc240bddba..db0984c76d 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -162,6 +162,8 @@ PRIVATE api/api_unread_things.h api/api_updates.cpp api/api_updates.h + api/api_user_names.cpp + api/api_user_names.h api/api_user_privacy.cpp api/api_user_privacy.h api/api_views.cpp @@ -545,6 +547,7 @@ PRIVATE data/data_user.h data/data_user_photos.cpp data/data_user_photos.h + data/data_user_names.h data/data_wall_paper.cpp data/data_wall_paper.h data/data_web_page.cpp diff --git a/Telegram/SourceFiles/api/api_user_names.cpp b/Telegram/SourceFiles/api/api_user_names.cpp new file mode 100644 index 0000000000..6edefbad72 --- /dev/null +++ b/Telegram/SourceFiles/api/api_user_names.cpp @@ -0,0 +1,203 @@ +/* +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_user_names.h" + +#include "apiwrap.h" +#include "data/data_channel.h" +#include "data/data_peer.h" +#include "data/data_user.h" +#include "main/main_session.h" + +namespace Api { +namespace { + +[[nodiscard]] Data::Username UsernameFromTL(const MTPUsername &username) { + return { + .username = qs(username.data().vusername()), + .active = username.data().is_active(), + .editable = username.data().is_editable(), + }; +} + +} // namespace + +Usernames::Usernames(not_null api) +: _session(&api->session()) +, _api(&api->instance()) { +} + +rpl::producer Usernames::loadUsernames( + not_null peer) const { + return [=](auto consumer) { + auto lifetime = rpl::lifetime(); + + const auto push = [consumer]( + const auto &usernames, + const auto &username) { + if (usernames) { + if (usernames->v.empty()) { + // Probably will never happen. + consumer.put_next({}); + } else { + auto parsed = FromTL(*usernames); + if ((parsed.size() == 1) + && username + && (parsed.front().username == qs(*username))) { + // Probably will never happen. + consumer.put_next({}); + } else { + consumer.put_next(std::move(parsed)); + } + } + } else { + consumer.put_next({}); + } + }; + + const auto requestUser = [&](const MTPInputUser &data) { + _session->api().request(MTPusers_GetUsers( + MTP_vector(1, data) + )).done([=](const MTPVector &result) { + result.v.front().match([&](const MTPDuser &data) { + push(data.vusernames(), data.vusername()); + consumer.put_done(); + }, [&](const MTPDuserEmpty&) { + consumer.put_next({}); + consumer.put_done(); + }); + }).send(); + }; + const auto requestChannel = [&](const MTPInputChannel &data) { + _session->api().request(MTPchannels_GetChannels( + MTP_vector(1, data) + )).done([=](const MTPmessages_Chats &result) { + result.match([&](const auto &data) { + data.vchats().v.front().match([&](const MTPDchannel &c) { + push(c.vusernames(), c.vusername()); + consumer.put_done(); + }, [&](auto &&) { + consumer.put_next({}); + consumer.put_done(); + }); + }); + }).send(); + }; + if (peer->isSelf()) { + requestUser(MTP_inputUserSelf()); + } else if (const auto user = peer->asUser()) { + requestUser(user->inputUser); + } else if (const auto channel = peer->asChannel()) { + requestChannel(channel->inputChannel); + } + return lifetime; + }; +} + +rpl::producer<> Usernames::toggle( + not_null peer, + const QString &username, + bool active) { + const auto peerId = peer->id; + const auto it = _toggleRequests.find(peerId); + const auto found = (it != end(_toggleRequests)); + auto &entry = (!found + ? _toggleRequests.emplace( + peerId, + Entry{ .usernames = { username } }).first + : it)->second; + if (ranges::contains(entry.usernames, username)) { + if (found) { + return entry.done.events(); + } + } else { + entry.usernames.push_back(username); + } + + const auto finish = [=] { + const auto it = _toggleRequests.find(peerId); + if (it != end(_toggleRequests)) { + auto &list = it->second.usernames; + list.erase(ranges::remove(list, username), end(list)); + if (list.empty()) { + it->second.done.fire_done(); + _toggleRequests.remove(peerId); + } + } + }; + + if (peer->isSelf()) { + _api.request(MTPaccount_ToggleUsername( + MTP_string(username), + MTP_bool(active) + )).done(finish).fail(finish).send(); + } else if (const auto channel = peer->asChannel()) { + _api.request(MTPchannels_ToggleUsername( + channel->inputChannel, + MTP_string(username), + MTP_bool(active) + )).done(finish).fail(finish).send(); + } else { + return rpl::never<>(); + } + return entry.done.events(); +} + +rpl::producer<> Usernames::reorder( + not_null peer, + const std::vector &usernames) { + const auto peerId = peer->id; + const auto it = _reorderRequests.find(peerId); + if (it != end(_reorderRequests)) { + _api.request(it->second).cancel(); + _reorderRequests.erase(peerId); + } + + return [=](auto consumer) { + auto lifetime = rpl::lifetime(); + + auto tlUsernames = ranges::views::all( + usernames + ) | ranges::views::transform([](const QString &username) { + return MTP_string(username); + }) | ranges::to>; + + const auto finish = [=] { + if (_reorderRequests.contains(peerId)) { + _reorderRequests.erase(peerId); + } + consumer.put_done(); + }; + if (usernames.empty()) { + crl::on_main([=] { consumer.put_done(); }); + return lifetime; + } + + if (peer->isSelf()) { + const auto requestId = _api.request(MTPaccount_ReorderUsernames( + MTP_vector(std::move(tlUsernames)) + )).done(finish).fail(finish).send(); + _reorderRequests.emplace(peerId, requestId); + } else if (const auto channel = peer->asChannel()) { + const auto requestId = _api.request(MTPchannels_ReorderUsernames( + channel->inputChannel, + MTP_vector(std::move(tlUsernames)) + )).done(finish).fail(finish).send(); + _reorderRequests.emplace(peerId, requestId); + } + return lifetime; + }; +} + +Data::Usernames Usernames::FromTL(const MTPVector &usernames) { + return ranges::views::all( + usernames.v + ) | ranges::views::transform(UsernameFromTL) | ranges::to_vector; +} + + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_user_names.h b/Telegram/SourceFiles/api/api_user_names.h new file mode 100644 index 0000000000..dc3edc346d --- /dev/null +++ b/Telegram/SourceFiles/api/api_user_names.h @@ -0,0 +1,53 @@ +/* +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 "data/data_user_names.h" +#include "mtproto/sender.h" + +class ApiWrap; +class PeerData; + +namespace Main { +class Session; +} // namespace Main + +namespace Api { + +class Usernames final { +public: + explicit Usernames(not_null api); + + [[nodiscard]] rpl::producer loadUsernames( + not_null peer) const; + [[nodiscard]] rpl::producer<> toggle( + not_null peer, + const QString &username, + bool active); + [[nodiscard]] rpl::producer<> reorder( + not_null peer, + const std::vector &usernames); + + static Data::Usernames FromTL(const MTPVector &usernames); + +private: + + const not_null _session; + MTP::Sender _api; + + using Key = PeerId; + struct Entry final { + rpl::event_stream<> done; + std::vector usernames; + }; + base::flat_map _toggleRequests; + base::flat_map _reorderRequests; + +}; + +} // namespace Api diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 82e7752d77..5a3711ca48 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_ringtones.h" #include "api/api_transcribes.h" #include "api/api_premium.h" +#include "api/api_user_names.h" #include "data/notify/data_notify_settings.h" #include "data/stickers/data_stickers.h" #include "data/data_drafts.h" @@ -169,7 +170,8 @@ ApiWrap::ApiWrap(not_null session) , _unreadThings(std::make_unique(this)) , _ringtones(std::make_unique(this)) , _transcribes(std::make_unique(this)) -, _premium(std::make_unique(this)) { +, _premium(std::make_unique(this)) +, _usernames(std::make_unique(this)) { crl::on_main(session, [=] { // You can't use _session->lifetime() in the constructor, // only queued, because it is not constructed yet. @@ -4073,3 +4075,7 @@ Api::Transcribes &ApiWrap::transcribes() { Api::Premium &ApiWrap::premium() { return *_premium; } + +Api::Usernames &ApiWrap::usernames() { + return *_usernames; +} diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index ddbf755687..e56a4ebc25 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -77,6 +77,7 @@ class UnreadThings; class Ringtones; class Transcribes; class Premium; +class Usernames; namespace details { @@ -369,6 +370,7 @@ public: [[nodiscard]] Api::Ringtones &ringtones(); [[nodiscard]] Api::Transcribes &transcribes(); [[nodiscard]] Api::Premium &premium(); + [[nodiscard]] Api::Usernames &usernames(); void updatePrivacyLastSeens(); @@ -675,6 +677,7 @@ private: const std::unique_ptr _ringtones; const std::unique_ptr _transcribes; const std::unique_ptr _premium; + const std::unique_ptr _usernames; mtpRequestId _wallPaperRequestId = 0; QString _wallPaperSlug; diff --git a/Telegram/SourceFiles/data/data_user_names.h b/Telegram/SourceFiles/data/data_user_names.h new file mode 100644 index 0000000000..917f7948d6 --- /dev/null +++ b/Telegram/SourceFiles/data/data_user_names.h @@ -0,0 +1,20 @@ +/* +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 + +namespace Data { + +struct Username final { + QString username; + bool active = false; + bool editable = false; +}; + +using Usernames = std::vector; + +} // namespace Data