diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index b6dddc1c2c..0c8b2d5fcd 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -549,6 +549,7 @@ PRIVATE data/data_user.h data/data_user_photos.cpp data/data_user_photos.h + data/data_user_names.cpp data/data_user_names.h data/data_wall_paper.cpp data/data_wall_paper.h diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index 5ef4ba3ea6..a2bf673949 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_updates.h" #include "api/api_authorizations.h" +#include "api/api_user_names.h" #include "api/api_chat_participants.h" #include "api/api_ringtones.h" #include "api/api_text_entities.h" @@ -1888,6 +1889,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { TextUtilities::SingleLine(last), user->nameOrPhone, TextUtilities::SingleLine(username)); + user->setUsernames(Api::Usernames::FromTL(d.vusernames())); } } break; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index ae8961968f..977afdaa0a 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -723,7 +723,7 @@ void Controller::fillPrivacyTypeButton() { ? Privacy::HasUsername : Privacy::NoUsername), .username = (_peer->isChannel() - ? _peer->asChannel()->username() + ? _peer->asChannel()->editableUsername() : QString()), .noForwards = !_peer->allowsForwarding(), .joinToWrite = (_peer->isMegagroup() @@ -1452,10 +1452,10 @@ void Controller::saveUsernamesOrder() { _api.request(MTPchannels_DeactivateAllUsernames( channel->inputChannel )).done([=] { - channel->setUsernames(channel->username().isEmpty() + channel->setUsernames(channel->editableUsername().isEmpty() ? Data::Usernames() : Data::Usernames{ - { channel->username(), true, true } + { channel->editableUsername(), true, true } }); continueSave(); }).send(); @@ -1470,7 +1470,7 @@ void Controller::saveUsernamesOrder() { newUsernames ) | ranges::views::transform([&](QString username) { const auto editable = - (channel->username() == username); + (channel->editableUsername() == username); return Data::Username{ .username = std::move(username), .active = true, @@ -1485,7 +1485,7 @@ void Controller::saveUsernamesOrder() { void Controller::saveUsername() { const auto channel = _peer->asChannel(); - const auto username = (channel ? channel->username() : QString()); + const auto username = (channel ? channel->editableUsername() : QString()); if (!_savingData.username || *_savingData.username == username) { return continueSave(); } else if (!channel) { @@ -1583,7 +1583,9 @@ void Controller::saveTitle() { if (type == qstr("CHAT_NOT_MODIFIED") || type == qstr("CHAT_TITLE_NOT_MODIFIED")) { if (const auto channel = _peer->asChannel()) { - channel->setName(*_savingData.title, channel->username()); + channel->setName( + *_savingData.title, + channel->editableUsername()); } else if (const auto chat = _peer->asChat()) { chat->setName(*_savingData.title); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index 821dbc31bf..f6ce4ceea3 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -176,7 +176,7 @@ Controller::Controller( , _isGroup(_peer->isChat() || _peer->isMegagroup()) , _goodUsername(_dataSavedValue ? !_dataSavedValue->username.isEmpty() - : (_peer->isChannel() && !_peer->asChannel()->username().isEmpty())) + : (_peer->isChannel() && !_peer->asChannel()->editableUsername().isEmpty())) , _wrap(container) , _checkUsernameTimer([=] { checkUsernameAvailability(); }) { _peer->updateFull(); @@ -400,7 +400,7 @@ object_ptr Controller::createUsernameEdit() { const auto channel = _peer->asChannel(); const auto username = (!_dataSavedValue || !channel) ? QString() - : channel->username(); + : channel->editableUsername(); auto result = object_ptr>( _wrap, @@ -530,7 +530,7 @@ void Controller::checkUsernameAvailability() { _api.request(_checkUsernameRequestId).cancel(); } const auto channel = _peer->migrateToOrMe()->asChannel(); - const auto username = channel ? channel->username() : QString(); + const auto username = channel ? channel->editableUsername() : QString(); _checkUsernameRequestId = _api.request(MTPchannels_CheckUsername( channel ? channel->inputChannel : MTP_inputChannelEmpty(), MTP_string(checking) diff --git a/Telegram/SourceFiles/boxes/username_box.cpp b/Telegram/SourceFiles/boxes/username_box.cpp index e908854416..332fb89b4c 100644 --- a/Telegram/SourceFiles/boxes/username_box.cpp +++ b/Telegram/SourceFiles/boxes/username_box.cpp @@ -79,10 +79,10 @@ UsernameEditor::UsernameEditor( this, st::defaultInputField, rpl::single(qsl("@username")), - session->user()->username(), + session->user()->editableUsername(), QString()) , _checkTimer([=] { check(); }) { - _goodText = _session->user()->username().isEmpty() + _goodText = _session->user()->editableUsername().isEmpty() ? QString() : tr::lng_username_available(tr::now); @@ -183,7 +183,7 @@ void UsernameEditor::check() { _checkRequestId = 0; _errorText = (mtpIsTrue(result) - || _checkUsername == _session->user()->username()) + || _checkUsername == _session->user()->editableUsername()) ? QString() : tr::lng_username_occupied(tr::now); _goodText = _errorText.isEmpty() @@ -241,7 +241,7 @@ void UsernameEditor::changed() { void UsernameEditor::updateFail(const QString &error) { const auto self = _session->user(); if ((error == qstr("USERNAME_NOT_MODIFIED")) - || (_sentUsername == self->username())) { + || (_sentUsername == self->editableUsername())) { self->setName( TextUtilities::SingleLine(self->firstName), TextUtilities::SingleLine(self->lastName), @@ -269,7 +269,7 @@ void UsernameEditor::checkFail(const QString &error) { _errorText = tr::lng_username_invalid(tr::now); update(); } else if ((error == qstr("USERNAME_OCCUPIED")) - && (_checkUsername != _session->user()->username())) { + && (_checkUsername != _session->user()->editableUsername())) { _errorText = tr::lng_username_occupied(tr::now); update(); } else { diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 8dc25bbbe4..5e5e23e1b9 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -103,32 +103,36 @@ void ChannelData::setName( } void ChannelData::setUsername(const QString &username) { - if (_username != username) { - _username = username; - } + _username.setUsername(username); } -void ChannelData::setUsernames(const Data::Usernames &usernames) { - auto newUsernames = ranges::views::all( - usernames - ) | ranges::views::filter([&](const Data::Username &username) { - return username.active; - }) | ranges::views::transform([&](const Data::Username &username) { - return username.username; - }) | ranges::to_vector; - - if (!ranges::equal(_usernames, newUsernames)) { - _usernames = std::move(newUsernames); - session().changes().peerUpdated(this, UpdateFlag::Usernames); - } +void ChannelData::setUsernames(const Data::Usernames &newUsernames) { + const auto wasUsername = username(); + const auto wasUsernames = usernames(); + _username.setUsernames(newUsernames); + const auto nowUsername = username(); + const auto nowUsernames = usernames(); + session().changes().peerUpdated( + this, + UpdateFlag() + | ((wasUsername != nowUsername) + ? UpdateFlag::Username + : UpdateFlag()) + | (!ranges::equal(wasUsernames, nowUsernames) + ? UpdateFlag::Usernames + : UpdateFlag())); } QString ChannelData::username() const { - return _username; + return _username.username(); +} + +QString ChannelData::editableUsername() const { + return _username.editableUsername(); } const std::vector &ChannelData::usernames() const { - return _usernames; + return _username.usernames(); } void ChannelData::setAccessHash(uint64 accessHash) { diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index 78007d5385..73a7cc2b48 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -12,10 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_location.h" #include "data/data_chat_participant_status.h" #include "data/data_peer_bot_commands.h" - -namespace Data { -struct Username; -} // namespace Data +#include "data/data_user_names.h" struct ChannelLocation { QString address; @@ -151,7 +148,7 @@ public: void setName(const QString &name, const QString &username); void setUsername(const QString &username); - void setUsernames(const std::vector &usernames); + void setUsernames(const Data::Usernames &newUsernames); void setPhoto(const MTPChatPhoto &photo); void setAccessHash(uint64 accessHash); @@ -166,6 +163,7 @@ public: } [[nodiscard]] QString username() const; + [[nodiscard]] QString editableUsername() const; [[nodiscard]] const std::vector &usernames() const; [[nodiscard]] int membersCount() const { @@ -466,8 +464,7 @@ private: PtsWaiter _ptsWaiter; - QString _username; - std::vector _usernames; + Data::UsernamesInfo _username; int _membersCount = -1; int _adminsCount = 1; diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index da2e9f5c23..e59f4aee1b 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -198,11 +198,11 @@ void PeerData::updateNameDelayed( if (_name == newName && _nameVersion > 1) { if (isUser()) { if (asUser()->nameOrPhone == newNameOrPhone - && asUser()->username() == newUsername) { + && asUser()->editableUsername() == newUsername) { return; } } else if (isChannel()) { - if (asChannel()->username() == newUsername) { + if (asChannel()->editableUsername() == newUsername) { return; } } else if (isChat()) { @@ -220,15 +220,15 @@ void PeerData::updateNameDelayed( flags |= UpdateFlag::Name; } if (isUser()) { - if (asUser()->username() != newUsername) { + if (asUser()->editableUsername() != newUsername) { asUser()->setUsername(newUsername); flags |= UpdateFlag::Username; } asUser()->setNameOrPhone(newNameOrPhone); } else if (isChannel()) { - if (asChannel()->username() != newUsername) { + if (asChannel()->editableUsername() != newUsername) { asChannel()->setUsername(newUsername); - if (newUsername.isEmpty()) { + if (asChannel()->username().isEmpty()) { asChannel()->removeFlags(ChannelDataFlag::Username); } else { asChannel()->addFlags(ChannelDataFlag::Username); diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 09c75553b5..33b84a85f0 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -120,25 +120,25 @@ void UserData::setName(const QString &newFirstName, const QString &newLastName, updateNameDelayed(newFullName, newPhoneName, newUsername); } -void UserData::setUsernames(const Data::Usernames &usernames) { - auto newUsernames = ranges::views::all( - usernames - ) | ranges::views::filter([&](const Data::Username &username) { - return username.active; - }) | ranges::views::transform([&](const Data::Username &username) { - return username.username; - }) | ranges::to_vector; - - if (!ranges::equal(_usernames, newUsernames)) { - _usernames = std::move(newUsernames); - session().changes().peerUpdated(this, UpdateFlag::Usernames); - } +void UserData::setUsernames(const Data::Usernames &newUsernames) { + const auto wasUsername = username(); + const auto wasUsernames = usernames(); + _username.setUsernames(newUsernames); + const auto nowUsername = username(); + const auto nowUsernames = usernames(); + session().changes().peerUpdated( + this, + UpdateFlag() + | ((wasUsername != nowUsername) + ? UpdateFlag::Username + : UpdateFlag()) + | (!ranges::equal(wasUsernames, nowUsernames) + ? UpdateFlag::Usernames + : UpdateFlag())); } void UserData::setUsername(const QString &username) { - if (_username != username) { - _username = username; - } + _username.setUsername(username); } void UserData::setPhone(const QString &newPhone) { @@ -303,12 +303,16 @@ bool UserData::canShareThisContactFast() const { return !_phone.isEmpty(); } -const QString &UserData::username() const { - return _username; +QString UserData::username() const { + return _username.username(); +} + +QString UserData::editableUsername() const { + return _username.editableUsername();; } const std::vector &UserData::usernames() const { - return _usernames; + return _username.usernames(); } const QString &UserData::phone() const { diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index 1e7afce001..94c58aaddc 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -9,11 +9,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_peer.h" #include "data/data_chat_participant_status.h" +#include "data/data_user_names.h" #include "dialogs/dialogs_key.h" namespace Data { struct BotCommand; -struct Username; } // namespace Data struct BotInfo { @@ -74,7 +74,7 @@ public: const QString &newLastName, const QString &newPhoneName, const QString &newUsername); - void setUsernames(const std::vector &usernames); + void setUsernames(const Data::Usernames &newUsernames); void setEmojiStatus(DocumentId emojiStatusId, TimeId until = 0); [[nodiscard]] DocumentId emojiStatusId() const; @@ -130,7 +130,8 @@ public: QString firstName; QString lastName; [[nodiscard]] const QString &phone() const; - [[nodiscard]] const QString &username() const; + [[nodiscard]] QString username() const; + [[nodiscard]] QString editableUsername() const; [[nodiscard]] const std::vector &usernames() const; QString nameOrPhone; TimeId onlineTill = 0; @@ -168,15 +169,14 @@ private: Flags _flags; + Data::UsernamesInfo _username; + std::vector _unavailableReasons; - QString _username; QString _phone; ContactStatus _contactStatus = ContactStatus::Unknown; CallsStatus _callsStatus = CallsStatus::Unknown; int _commonChatsCount = 0; - std::vector _usernames; - uint64 _accessHash = 0; static constexpr auto kInaccessibleAccessHashOld = 0xFFFFFFFFFFFFFFFFULL; diff --git a/Telegram/SourceFiles/data/data_user_names.cpp b/Telegram/SourceFiles/data/data_user_names.cpp new file mode 100644 index 0000000000..b69756b5e3 --- /dev/null +++ b/Telegram/SourceFiles/data/data_user_names.cpp @@ -0,0 +1,83 @@ +/* +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 "data/data_user_names.h" + +namespace Data { + +UsernamesInfo::UsernamesInfo() = default; + +void UsernamesInfo::setUsername(const QString &username) { + if (_usernames.empty()) { + if (username.isEmpty()) { + _indexEditableUsername = -1; + } else { + _usernames.push_back(username); + _indexEditableUsername = 0; + } + } else if ((_indexEditableUsername < 0) + || (_indexEditableUsername >= _usernames.size())) { + if (username.isEmpty()) { + _indexEditableUsername = -1; + } else { + _usernames.push_back(username); + _indexEditableUsername = 0; + } + } else if (_usernames[_indexEditableUsername] != username) { + if (username.isEmpty()) { + _usernames.erase(begin(_usernames) + _indexEditableUsername); + _indexEditableUsername = -1; + } else { + _usernames[_indexEditableUsername] = username; + } + } +} + +void UsernamesInfo::setUsernames(const Usernames &usernames) { + auto editableUsername = QString(); + auto newUsernames = ranges::views::all( + usernames + ) | ranges::views::filter([&](const Data::Username &username) { + if (username.editable) { + editableUsername = username.username; + return true; + } + return username.active; + }) | ranges::views::transform([](const Data::Username &username) { + return username.username; + }) | ranges::to_vector; + + if (!ranges::equal(_usernames, newUsernames)) { + _usernames = std::move(newUsernames); + } + if (!editableUsername.isEmpty()) { + for (auto i = 0; i < _usernames.size(); i++) { + if (_usernames[i] == editableUsername) { + _indexEditableUsername = i; + break; + } + } + } else { + _indexEditableUsername = -1; + } +} + +QString UsernamesInfo::username() const { + return _usernames.empty() ? QString() : _usernames.front(); +} + +QString UsernamesInfo::editableUsername() const { + return (_indexEditableUsername < 0) + ? QString() + : _usernames[_indexEditableUsername]; +} + +const std::vector &UsernamesInfo::usernames() const { + return _usernames; +} + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_user_names.h b/Telegram/SourceFiles/data/data_user_names.h index 917f7948d6..9e410f75b2 100644 --- a/Telegram/SourceFiles/data/data_user_names.h +++ b/Telegram/SourceFiles/data/data_user_names.h @@ -17,4 +17,21 @@ struct Username final { using Usernames = std::vector; +class UsernamesInfo final { +public: + UsernamesInfo(); + + void setUsername(const QString &username); + void setUsernames(const Usernames &usernames); + + [[nodiscard]] QString username() const; + [[nodiscard]] QString editableUsername() const; + [[nodiscard]] const std::vector &usernames() const; + +private: + std::vector _usernames; + short _indexEditableUsername = -1; + +}; + } // namespace Data