Respected new error for occupied usernames in manage channel.

This commit is contained in:
23rd 2022-11-27 15:32:13 +03:00
parent 2acedca6b7
commit 3fdb807a1e
7 changed files with 126 additions and 114 deletions

View file

@ -1386,6 +1386,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_create_channel_link_too_short" = "Sorry, this link is too short";
"lng_create_channel_link_bad_symbols" = "Only 0-9, a-z, and underscores allowed.";
"lng_create_channel_link_available" = "This link is available";
"lng_create_channel_link_pending" = "Checking name...";
"lng_create_channel_link_copied" = "Link copied to clipboard";
"lng_create_group_crop" = "Select an area for group photo";

View file

@ -1225,6 +1225,8 @@ SetupChannelBox::UsernameResult SetupChannelBox::parseError(
return UsernameResult::Invalid;
} else if (error == u"USERNAME_OCCUPIED"_q) {
return UsernameResult::Occupied;
} else if (error == u"USERNAME_PURCHASE_AVAILABLE"_q) {
return UsernameResult::Occupied;
} else if (error == u"USERNAMES_UNAVAILABLE"_q) {
return UsernameResult::Occupied;
} else if (error == u"CHANNEL_PUBLIC_GROUP_NA"_q) {

View file

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peers/edit_peer_invite_link.h"
#include "boxes/peers/edit_peer_invite_links.h"
#include "boxes/peers/edit_peer_usernames_list.h"
#include "boxes/username_box.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
@ -103,8 +104,7 @@ private:
Ui::SlideWrap<Ui::RpWidget> *usernameWrap = nullptr;
Ui::UsernameInput *usernameInput = nullptr;
UsernamesList *usernamesList = nullptr;
base::unique_qptr<Ui::FlatLabel> usernameResult;
const style::FlatLabel *usernameResultStyle = nullptr;
base::unique_qptr<Ui::FlatLabel> usernameCheckResult;
Ui::SlideWrap<> *inviteLinkWrap = nullptr;
Ui::FlatLabel *inviteLink = nullptr;
@ -127,9 +127,8 @@ private:
void usernameChanged();
void showUsernameError(rpl::producer<QString> &&error);
void showUsernameGood();
void showUsernameResult(
rpl::producer<QString> &&text,
not_null<const style::FlatLabel*> st);
void showUsernamePending();
void showUsernameEmpty();
void fillPrivaciesButtons(
not_null<Ui::VerticalLayout*> parent,
@ -157,7 +156,9 @@ private:
base::Timer _checkUsernameTimer;
mtpRequestId _checkUsernameRequestId = 0;
UsernameState _usernameState = UsernameState::Normal;
rpl::event_stream<rpl::producer<QString>> _usernameResultTexts;
rpl::event_stream<UsernameCheckInfo> _usernameCheckInfo;
rpl::lifetime _usernameCheckInfoLifetime;
rpl::event_stream<int> _scrollToRequests;
@ -450,6 +451,8 @@ object_ptr<Ui::RpWidget> Controller::createUsernameEdit() {
}, placeholder->lifetime());
_controls.usernameInput->move(placeholder->pos());
AddUsernameCheckLabel(container, _usernameCheckInfo.events());
AddDividerText(
container,
tr::lng_create_channel_link_about());
@ -506,7 +509,7 @@ void Controller::privacyChanged(Privacy value) {
toggleEditUsername();
toggleWhoSendWrap();
_controls.usernameResult = nullptr;
showUsernameEmpty();
checkUsernameAvailability();
} else {
toggleWhoSendWrap();
@ -575,11 +578,16 @@ void Controller::checkUsernameAvailability() {
}
} else if (initial) {
if (_controls.privacy->value() == Privacy::HasUsername) {
_controls.usernameResult = nullptr;
showUsernameEmpty();
setFocusUsername();
}
} else if (type == u"USERNAME_INVALID"_q) {
showUsernameError(tr::lng_create_channel_link_invalid());
} else if (type == u"USERNAME_PURCHASE_AVAILABLE"_q) {
_goodUsername = false;
_usernameCheckInfo.fire({
.type = UsernameCheckInfo::Type::PurchaseAvailable,
});
} else if (type == u"USERNAME_OCCUPIED"_q && checking != username) {
showUsernameError(tr::lng_create_channel_link_occupied());
}
@ -602,7 +610,7 @@ void Controller::usernameChanged() {
_goodUsername = false;
const auto username = getUsernameInput();
if (username.isEmpty()) {
_controls.usernameResult = nullptr;
showUsernameEmpty();
_checkUsernameTimer.cancel();
return;
}
@ -617,43 +625,44 @@ void Controller::usernameChanged() {
} else if (username.size() < Ui::EditPeer::kMinUsernameLength) {
showUsernameError(tr::lng_create_channel_link_too_short());
} else {
_controls.usernameResult = nullptr;
showUsernamePending();
_checkUsernameTimer.callOnce(Ui::EditPeer::kUsernameCheckTimeout);
}
}
void Controller::showUsernameError(rpl::producer<QString> &&error) {
_goodUsername = false;
showUsernameResult(std::move(error), &st::editPeerUsernameError);
_usernameCheckInfoLifetime.destroy();
std::move(
error
) | rpl::map([](QString s) {
return UsernameCheckInfo{
.type = UsernameCheckInfo::Type::Error,
.text = { std::move(s) },
};
}) | rpl::start_to_stream(_usernameCheckInfo, _usernameCheckInfoLifetime);
}
void Controller::showUsernameGood() {
_goodUsername = true;
showUsernameResult(
tr::lng_create_channel_link_available(),
&st::editPeerUsernameGood);
_usernameCheckInfoLifetime.destroy();
_usernameCheckInfo.fire({
.type = UsernameCheckInfo::Type::Good,
.text = { tr::lng_create_channel_link_available(tr::now) },
});
}
void Controller::showUsernameResult(
rpl::producer<QString> &&text,
not_null<const style::FlatLabel*> st) {
if (!_controls.usernameResult
|| _controls.usernameResultStyle != st) {
_controls.usernameResultStyle = st;
_controls.usernameResult = base::make_unique_q<Ui::FlatLabel>(
_controls.usernameWrap,
_usernameResultTexts.events() | rpl::flatten_latest(),
*st);
const auto label = _controls.usernameResult.get();
label->show();
label->widthValue(
) | rpl::start_with_next([label] {
label->moveToRight(
st::editPeerUsernamePosition.x(),
st::editPeerUsernamePosition.y());
}, label->lifetime());
}
_usernameResultTexts.fire(std::move(text));
void Controller::showUsernamePending() {
_usernameCheckInfoLifetime.destroy();
_usernameCheckInfo.fire({
.type = UsernameCheckInfo::Type::Default,
.text = { .text = tr::lng_create_channel_link_pending(tr::now) },
});
}
void Controller::showUsernameEmpty() {
_usernameCheckInfoLifetime.destroy();
_usernameCheckInfo.fire({ .type = UsernameCheckInfo::Type::Default });
}
object_ptr<Ui::RpWidget> Controller::createInviteLinkBlock() {

View file

@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/buttons.h"
#include "ui/widgets/fields/special_fields.h"
#include "ui/widgets/labels.h"
#include "ui/wrap/follow_slide_wrap.h"
#include "ui/wrap/slide_wrap.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
@ -31,16 +32,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace {
struct CheckInfo final {
enum class Type {
Good,
Error,
Default,
PurchaseAvailable,
};
Type type;
v::text::data text;
};
[[nodiscard]] TextWithEntities PurchaseAvailableText() {
constexpr auto kUsernameAuction = "auction";
return tr::lng_username_purchase_available(
tr::now,
lt_link,
Ui::Text::Link(
'@' + QString(kUsernameAuction),
u"https://t.me/"_q + kUsernameAuction),
Ui::Text::RichLangValue);
}
class UsernameEditor final : public Ui::RpWidget {
public:
@ -49,7 +50,7 @@ public:
void setInnerFocus();
[[nodiscard]] rpl::producer<> submitted() const;
[[nodiscard]] rpl::producer<> save();
[[nodiscard]] rpl::producer<CheckInfo> checkInfoChanged() const;
[[nodiscard]] rpl::producer<UsernameCheckInfo> checkInfoChanged() const;
protected:
void resizeEvent(QResizeEvent *e) override;
@ -80,7 +81,7 @@ private:
base::Timer _checkTimer;
rpl::event_stream<> _saved;
rpl::event_stream<CheckInfo> _checkInfoChanged;
rpl::event_stream<UsernameCheckInfo> _checkInfoChanged;
};
@ -147,7 +148,7 @@ rpl::producer<> UsernameEditor::save() {
return _saved.events();
}
rpl::producer<CheckInfo> UsernameEditor::checkInfoChanged() const {
rpl::producer<UsernameCheckInfo> UsernameEditor::checkInfoChanged() const {
return _checkInfoChanged.events();
}
@ -184,7 +185,7 @@ void UsernameEditor::changed() {
if (name.isEmpty()) {
if (!_errorText.isEmpty() || !_goodText.isEmpty()) {
_errorText = _goodText = QString();
checkInfoChange();
_checkInfoChanged.fire({ UsernameCheckInfo::Type::Default });
}
_checkTimer.cancel();
} else {
@ -223,38 +224,29 @@ void UsernameEditor::changed() {
void UsernameEditor::checkInfoChange() {
if (!_errorText.isEmpty()) {
_checkInfoChanged.fire({
.type = CheckInfo::Type::Error,
.text = _errorText,
.type = UsernameCheckInfo::Type::Error,
.text = { _errorText },
});
} else if (!_goodText.isEmpty()) {
_checkInfoChanged.fire({
.type = CheckInfo::Type::Good,
.text = _goodText,
.type = UsernameCheckInfo::Type::Good,
.text = { _goodText },
});
} else {
_checkInfoChanged.fire({
.type = CheckInfo::Type::Default,
.text = tr::lng_username_choose(tr::now),
.type = UsernameCheckInfo::Type::Default,
.text = { tr::lng_username_choose(tr::now) },
});
}
}
void UsernameEditor::checkInfoPurchaseAvailable() {
constexpr auto kUsernameAuction = "auction";
const auto text = tr::lng_username_purchase_available(
tr::now,
lt_link,
Ui::Text::Link(
'@' + QString(kUsernameAuction),
u"https://t.me/"_q + kUsernameAuction),
Ui::Text::RichLangValue);
_username->setFocus();
_username->showError();
_errorText = text.text;
_errorText = u".bad."_q;
_checkInfoChanged.fire({
.type = CheckInfo::Type::PurchaseAvailable,
.text = text,
.type = UsernameCheckInfo::Type::PurchaseAvailable,
});
}
@ -320,52 +312,7 @@ void UsernamesBox(
{});
box->setFocusCallback([=] { editor->setInnerFocus(); });
{
const auto padding = st::boxRowPadding;
const auto &st = st::aboutRevokePublicLabel;
const auto skip = (st::usernameSkip - st.style.font->height) / 2;
box->addSkip(skip);
const auto wrap = box->addRow(
object_ptr<Ui::SlideWrap<Ui::RpWidget>>(
box,
object_ptr<Ui::RpWidget>(box)),
padding);
wrap->setMinimalHeight(st.style.font->height);
const auto maxHeightWrap = wrap->entity();
const auto label = Ui::CreateChild<Ui::FlatLabel>(
maxHeightWrap,
editor->checkInfoChanged(
) | rpl::map([](CheckInfo info) {
return v::text::take_marked(base::take(info.text));
}) | rpl::flatten_latest(),
st);
label->heightValue(
) | rpl::start_with_next([=](int height) {
if (height > maxHeightWrap->height()) {
maxHeightWrap->resize(maxHeightWrap->width(), height);
}
}, maxHeightWrap->lifetime());
editor->checkInfoChanged(
) | rpl::start_with_next([=](CheckInfo info) {
const auto &color = (info.type == CheckInfo::Type::Good)
? st::boxTextFgGood
: (info.type == CheckInfo::Type::Error)
? st::boxTextFgError
: st::usernameDefaultFg;
label->setTextColorOverride(color->c);
label->resizeToWidth(container->width()
- padding.left()
- padding.right());
wrap->toggle(
info.type == CheckInfo::Type::PurchaseAvailable,
anim::type::normal);
}, label->lifetime());
box->addSkip(skip);
}
AddUsernameCheckLabel(container, editor->checkInfoChanged());
container->add(object_ptr<Ui::DividerLabel>(
container,
@ -401,3 +348,41 @@ void UsernamesBox(
box->addButton(tr::lng_settings_save(), finish);
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
void AddUsernameCheckLabel(
not_null<Ui::VerticalLayout*> container,
rpl::producer<UsernameCheckInfo> checkInfo) {
const auto padding = st::boxRowPadding;
const auto &st = st::aboutRevokePublicLabel;
const auto skip = (st::usernameSkip - st.style.font->height) / 4;
auto wrapped = object_ptr<Ui::VerticalLayout>(container);
Settings::AddSkip(wrapped, skip);
const auto label = wrapped->add(object_ptr<Ui::FlatLabel>(wrapped, st));
Settings::AddSkip(wrapped, skip);
Settings::AddSkip(container, skip);
const auto wrap = container->add(
object_ptr<Ui::FollowSlideWrap<Ui::VerticalLayout>>(
container,
std::move(wrapped)),
padding);
rpl::combine(
std::move(checkInfo),
container->widthValue()
) | rpl::start_with_next([=](const UsernameCheckInfo &info, int w) {
using Type = UsernameCheckInfo::Type;
label->setMarkedText((info.type == Type::PurchaseAvailable)
? PurchaseAvailableText()
: info.text);
const auto &color = (info.type == Type::Good)
? st::boxTextFgGood
: (info.type == Type::Error)
? st::boxTextFgError
: st::usernameDefaultFg;
label->setTextColorOverride(color->c);
label->resizeToWidth(w - padding.left() - padding.right());
}, label->lifetime());
Settings::AddSkip(container, skip);
}

View file

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui {
class GenericBox;
class VerticalLayout;
} // namespace Ui
namespace Main {
@ -18,3 +19,18 @@ class Session;
void UsernamesBox(
not_null<Ui::GenericBox*> box,
not_null<Main::Session*> session);
struct UsernameCheckInfo final {
enum class Type {
Good,
Error,
Default,
PurchaseAvailable,
};
Type type;
TextWithEntities text;
};
void AddUsernameCheckLabel(
not_null<Ui::VerticalLayout*> container,
rpl::producer<UsernameCheckInfo> checkInfo);

View file

@ -655,7 +655,7 @@ editPeerHistoryVisibilityLabelMargins: margins(34px, 0px, 48px, 0px);
editPeerPrivacyLabelMargins: margins(42px, 0px, 34px, 0px);
editPeerPreHistoryLabelMargins: margins(34px, 0px, 34px, 0px);
editPeerUsernameTitleLabelMargins: margins(22px, 17px, 22px, 10px);
editPeerUsernameFieldMargins: margins(22px, 0px, 22px, 16px);
editPeerUsernameFieldMargins: margins(22px, 0px, 22px, 0px);
editPeerUsername: setupChannelLink;
editPeerUsernameSkip: 8px;
editPeerInviteLink: FlatLabel(defaultFlatLabel) {
@ -669,7 +669,6 @@ editPeerUsernameGood: FlatLabel(defaultFlatLabel) {
editPeerUsernameError: FlatLabel(editPeerUsernameGood) {
textFg: boxTextFgError;
}
editPeerUsernamePosition: point(22px, 18px);
editPeerReactionsButton: SettingsButton(infoProfileButton) {
padding: margins(59px, 13px, 8px, 11px);

@ -1 +1 @@
Subproject commit 0937ac0ad0feaf3cbccf009e0e1f2a04cc0ef9f8
Subproject commit c197c1831d1af48cf769f271a126af2c2eaf8185