Support invite link label editing.

This commit is contained in:
John Preston 2021-10-28 16:18:49 +04:00
parent eb82664452
commit aaae5b0553
8 changed files with 63 additions and 4 deletions

View File

@ -1307,6 +1307,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_invite_link_expired" = "Expired"; "lng_group_invite_link_expired" = "Expired";
"lng_group_invite_edit_title" = "Edit Link"; "lng_group_invite_edit_title" = "Edit Link";
"lng_group_invite_new_title" = "New Link"; "lng_group_invite_new_title" = "New Link";
"lng_group_invite_label_header" = "Label (optional)";
"lng_group_invite_label_about" = "Add an optional link label.";
"lng_group_invite_expire_title" = "Limit by time period"; "lng_group_invite_expire_title" = "Limit by time period";
"lng_group_invite_expire_about" = "You can make the link expire after a certain time."; "lng_group_invite_expire_about" = "You can make the link expire after a certain time.";
"lng_group_invite_expire_never" = "No limit"; "lng_group_invite_expire_never" = "No limit";
@ -2526,6 +2528,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_admin_log_edited_invite_link" = "edited invite link {link}"; "lng_admin_log_edited_invite_link" = "edited invite link {link}";
"lng_admin_log_invite_link_expire_date" = "Expire date: {previous} -> {limit}"; "lng_admin_log_invite_link_expire_date" = "Expire date: {previous} -> {limit}";
"lng_admin_log_invite_link_usage_limit" = "Usage limit: {previous} -> {limit}"; "lng_admin_log_invite_link_usage_limit" = "Usage limit: {previous} -> {limit}";
"lng_admin_log_invite_link_label" = "Label: {previous} -> {limit}";
"lng_admin_log_invite_link_request_needed" = "Now admin approval is required to join."; "lng_admin_log_invite_link_request_needed" = "Now admin approval is required to join.";
"lng_admin_log_invite_link_request_not_needed" = "Now admin approval is not required to join."; "lng_admin_log_invite_link_request_not_needed" = "Now admin approval is not required to join.";
"lng_admin_log_restricted_forever" = "indefinitely"; "lng_admin_log_restricted_forever" = "indefinitely";

View File

@ -70,6 +70,7 @@ InviteLinks::InviteLinks(not_null<ApiWrap*> api) : _api(api) {
void InviteLinks::create( void InviteLinks::create(
not_null<PeerData*> peer, not_null<PeerData*> peer,
Fn<void(Link)> done, Fn<void(Link)> done,
const QString &label,
TimeId expireDate, TimeId expireDate,
int usageLimit, int usageLimit,
bool requestApproval) { bool requestApproval) {
@ -77,6 +78,7 @@ void InviteLinks::create(
peer, peer,
std::move(done), std::move(done),
false, false,
label,
expireDate, expireDate,
usageLimit, usageLimit,
requestApproval); requestApproval);
@ -86,6 +88,7 @@ void InviteLinks::performCreate(
not_null<PeerData*> peer, not_null<PeerData*> peer,
Fn<void(Link)> done, Fn<void(Link)> done,
bool revokeLegacyPermanent, bool revokeLegacyPermanent,
const QString &label,
TimeId expireDate, TimeId expireDate,
int usageLimit, int usageLimit,
bool requestApproval) { bool requestApproval) {
@ -106,6 +109,7 @@ void InviteLinks::performCreate(
MTP_flags((revokeLegacyPermanent MTP_flags((revokeLegacyPermanent
? Flag::f_legacy_revoke_permanent ? Flag::f_legacy_revoke_permanent
: Flag(0)) : Flag(0))
| (!label.isEmpty() ? Flag::f_title : Flag(0))
| (expireDate ? Flag::f_expire_date : Flag(0)) | (expireDate ? Flag::f_expire_date : Flag(0))
| ((!requestApproval && usageLimit) | ((!requestApproval && usageLimit)
? Flag::f_usage_limit ? Flag::f_usage_limit
@ -114,7 +118,7 @@ void InviteLinks::performCreate(
peer->input, peer->input,
MTP_int(expireDate), MTP_int(expireDate),
MTP_int(usageLimit), MTP_int(usageLimit),
MTPstring() // title MTP_string(label)
)).done([=](const MTPExportedChatInvite &result) { )).done([=](const MTPExportedChatInvite &result) {
const auto callbacks = _createCallbacks.take(peer); const auto callbacks = _createCallbacks.take(peer);
const auto link = prepend(peer, peer->session().user(), result); const auto link = prepend(peer, peer->session().user(), result);
@ -212,6 +216,7 @@ void InviteLinks::edit(
not_null<PeerData*> peer, not_null<PeerData*> peer,
not_null<UserData*> admin, not_null<UserData*> admin,
const QString &link, const QString &link,
const QString &label,
TimeId expireDate, TimeId expireDate,
int usageLimit, int usageLimit,
bool requestApproval, bool requestApproval,
@ -222,6 +227,7 @@ void InviteLinks::edit(
link, link,
std::move(done), std::move(done),
false, false,
label,
expireDate, expireDate,
usageLimit, usageLimit,
requestApproval); requestApproval);
@ -233,6 +239,7 @@ void InviteLinks::performEdit(
const QString &link, const QString &link,
Fn<void(Link)> done, Fn<void(Link)> done,
bool revoke, bool revoke,
const QString &label,
TimeId expireDate, TimeId expireDate,
int usageLimit, int usageLimit,
bool requestApproval) { bool requestApproval) {
@ -253,6 +260,7 @@ void InviteLinks::performEdit(
} }
using Flag = MTPmessages_EditExportedChatInvite::Flag; using Flag = MTPmessages_EditExportedChatInvite::Flag;
const auto flags = (revoke ? Flag::f_revoked : Flag(0)) const auto flags = (revoke ? Flag::f_revoked : Flag(0))
| (!revoke ? Flag::f_title : Flag(0))
| (!revoke ? Flag::f_expire_date : Flag(0)) | (!revoke ? Flag::f_expire_date : Flag(0))
| ((!revoke && !requestApproval) ? Flag::f_usage_limit : Flag(0)) | ((!revoke && !requestApproval) ? Flag::f_usage_limit : Flag(0))
| ((!revoke && (requestApproval || !usageLimit)) | ((!revoke && (requestApproval || !usageLimit))
@ -265,7 +273,7 @@ void InviteLinks::performEdit(
MTP_int(expireDate), MTP_int(expireDate),
MTP_int(usageLimit), MTP_int(usageLimit),
MTP_bool(requestApproval), MTP_bool(requestApproval),
MTPstring() // title MTP_string(label)
)).done([=](const MTPmessages_ExportedChatInvite &result) { )).done([=](const MTPmessages_ExportedChatInvite &result) {
const auto callbacks = _editCallbacks.take(key); const auto callbacks = _editCallbacks.take(key);
const auto peer = key.peer; const auto peer = key.peer;
@ -729,6 +737,7 @@ auto InviteLinks::parse(
return invite.match([&](const MTPDchatInviteExported &data) { return invite.match([&](const MTPDchatInviteExported &data) {
return Link{ return Link{
.link = qs(data.vlink()), .link = qs(data.vlink()),
.label = qs(data.vtitle().value_or_empty()),
.admin = peer->session().data().user(data.vadmin_id()), .admin = peer->session().data().user(data.vadmin_id()),
.date = data.vdate().v, .date = data.vdate().v,
.startDate = data.vstart_date().value_or_empty(), .startDate = data.vstart_date().value_or_empty(),

View File

@ -13,6 +13,7 @@ namespace Api {
struct InviteLink { struct InviteLink {
QString link; QString link;
QString label;
not_null<UserData*> admin; not_null<UserData*> admin;
TimeId date = 0; TimeId date = 0;
TimeId startDate = 0; TimeId startDate = 0;
@ -62,6 +63,7 @@ public:
void create( void create(
not_null<PeerData*> peer, not_null<PeerData*> peer,
Fn<void(Link)> done = nullptr, Fn<void(Link)> done = nullptr,
const QString &label = QString(),
TimeId expireDate = 0, TimeId expireDate = 0,
int usageLimit = 0, int usageLimit = 0,
bool requestApproval = false); bool requestApproval = false);
@ -69,6 +71,7 @@ public:
not_null<PeerData*> peer, not_null<PeerData*> peer,
not_null<UserData*> admin, not_null<UserData*> admin,
const QString &link, const QString &link,
const QString &label,
TimeId expireDate, TimeId expireDate,
int usageLimit, int usageLimit,
bool requestApproval, bool requestApproval,
@ -180,6 +183,7 @@ private:
const QString &link, const QString &link,
Fn<void(Link)> done, Fn<void(Link)> done,
bool revoke, bool revoke,
const QString &label = QString(),
TimeId expireDate = 0, TimeId expireDate = 0,
int usageLimit = 0, int usageLimit = 0,
bool requestApproval = false); bool requestApproval = false);
@ -187,6 +191,7 @@ private:
not_null<PeerData*> peer, not_null<PeerData*> peer,
Fn<void(Link)> done, Fn<void(Link)> done,
bool revokeLegacyPermanent, bool revokeLegacyPermanent,
const QString &label = QString(),
TimeId expireDate = 0, TimeId expireDate = 0,
int usageLimit = 0, int usageLimit = 0,
bool requestApproval = false); bool requestApproval = false);

View File

@ -1180,6 +1180,7 @@ void EditLink(
peer->session().api().inviteLinks().create( peer->session().api().inviteLinks().create(
peer, peer,
finish, finish,
result.label,
result.expireDate, result.expireDate,
result.usageLimit, result.usageLimit,
result.requestApproval); result.requestApproval);
@ -1188,6 +1189,7 @@ void EditLink(
peer, peer,
data.admin, data.admin,
result.link, result.link,
result.label,
result.expireDate, result.expireDate,
result.usageLimit, result.usageLimit,
result.requestApproval, result.requestApproval,
@ -1202,6 +1204,7 @@ void EditLink(
Ui::EditInviteLinkBox, Ui::EditInviteLinkBox,
Fields{ Fields{
.link = data.link, .link = data.link,
.label = data.label,
.expireDate = data.expireDate, .expireDate = data.expireDate,
.usageLimit = data.usageLimit, .usageLimit = data.usageLimit,
.requestApproval = data.requestApproval, .requestApproval = data.requestApproval,

View File

@ -275,6 +275,7 @@ void Row::update(const InviteLinkData &data, TimeId now) {
_progressTillExpire = ComputeProgress(data, now); _progressTillExpire = ComputeProgress(data, now);
_color = ComputeColor(data, _progressTillExpire); _color = ComputeColor(data, _progressTillExpire);
setCustomStatus(ComputeStatus(data, now)); setCustomStatus(ComputeStatus(data, now));
refreshName(st::inviteLinkList.item);
_delegate->rowUpdateRow(this); _delegate->rowUpdateRow(this);
} }
@ -309,6 +310,9 @@ crl::time Row::updateExpireIn() const {
} }
QString Row::generateName() { QString Row::generateName() {
if (!_data.label.isEmpty()) {
return _data.label;
}
auto result = _data.link; auto result = _data.link;
return result.replace( return result.replace(
qstr("https://"), qstr("https://"),

View File

@ -263,13 +263,20 @@ QString ExtractInviteLink(const MTPExportedChatInvite &data) {
}); });
} }
QString ExtractInviteLinkLabel(const MTPExportedChatInvite &data) {
return data.match([&](const MTPDchatInviteExported &data) {
return qs(data.vtitle().value_or_empty());
});
}
QString InternalInviteLinkUrl(const MTPExportedChatInvite &data) { QString InternalInviteLinkUrl(const MTPExportedChatInvite &data) {
const auto base64 = ExtractInviteLink(data).toUtf8().toBase64(); const auto base64 = ExtractInviteLink(data).toUtf8().toBase64();
return "internal:show_invite_link/?link=" + QString::fromLatin1(base64); return "internal:show_invite_link/?link=" + QString::fromLatin1(base64);
} }
QString GenerateInviteLinkText(const MTPExportedChatInvite &data) { QString GenerateInviteLinkText(const MTPExportedChatInvite &data) {
return ExtractInviteLink(data).replace( const auto label = ExtractInviteLinkLabel(data);
return label.isEmpty() ? ExtractInviteLink(data).replace(
qstr("https://"), qstr("https://"),
QString() QString()
).replace( ).replace(
@ -278,7 +285,7 @@ QString GenerateInviteLinkText(const MTPExportedChatInvite &data) {
).replace( ).replace(
qstr("t.me/joinchat/"), qstr("t.me/joinchat/"),
QString() QString()
); ) : label;
} }
QString GenerateInviteLinkLink(const MTPExportedChatInvite &data) { QString GenerateInviteLinkLink(const MTPExportedChatInvite &data) {
@ -302,6 +309,11 @@ TextWithEntities GenerateInviteLinkChangeText(
auto result = tr::lng_admin_log_edited_invite_link(tr::now, lt_link, link, Ui::Text::WithEntities); auto result = tr::lng_admin_log_edited_invite_link(tr::now, lt_link, link, Ui::Text::WithEntities);
result.text.append('\n'); result.text.append('\n');
const auto label = [](const MTPExportedChatInvite &link) {
return link.match([](const MTPDchatInviteExported &data) {
return qs(data.vtitle().value_or_empty());
});
};
const auto expireDate = [](const MTPExportedChatInvite &link) { const auto expireDate = [](const MTPExportedChatInvite &link) {
return link.match([](const MTPDchatInviteExported &data) { return link.match([](const MTPDchatInviteExported &data) {
return data.vexpire_date().value_or_empty(); return data.vexpire_date().value_or_empty();
@ -327,12 +339,17 @@ TextWithEntities GenerateInviteLinkChangeText(
? QString::number(count) ? QString::number(count)
: tr::lng_group_invite_usage_any(tr::now); : tr::lng_group_invite_usage_any(tr::now);
}; };
const auto wasLabel = label(prevLink);
const auto nowLabel = label(newLink);
const auto wasExpireDate = expireDate(prevLink); const auto wasExpireDate = expireDate(prevLink);
const auto nowExpireDate = expireDate(newLink); const auto nowExpireDate = expireDate(newLink);
const auto wasUsageLimit = usageLimit(prevLink); const auto wasUsageLimit = usageLimit(prevLink);
const auto nowUsageLimit = usageLimit(newLink); const auto nowUsageLimit = usageLimit(newLink);
const auto wasRequestApproval = requestApproval(prevLink); const auto wasRequestApproval = requestApproval(prevLink);
const auto nowRequestApproval = requestApproval(newLink); const auto nowRequestApproval = requestApproval(newLink);
if (wasLabel != nowLabel) {
result.text.append('\n').append(tr::lng_admin_log_invite_link_label(tr::now, lt_previous, wasLabel, lt_limit, nowLabel));
}
if (wasExpireDate != nowExpireDate) { if (wasExpireDate != nowExpireDate) {
result.text.append('\n').append(tr::lng_admin_log_invite_link_expire_date(tr::now, lt_previous, wrapDate(wasExpireDate), lt_limit, wrapDate(nowExpireDate))); result.text.append('\n').append(tr::lng_admin_log_invite_link_expire_date(tr::now, lt_previous, wrapDate(wasExpireDate), lt_limit, wrapDate(nowExpireDate)));
} }

View File

@ -24,6 +24,7 @@ namespace {
constexpr auto kMaxLimit = std::numeric_limits<int>::max(); constexpr auto kMaxLimit = std::numeric_limits<int>::max();
constexpr auto kHour = 3600; constexpr auto kHour = 3600;
constexpr auto kDay = 86400; constexpr auto kDay = 86400;
constexpr auto kMaxLabelLength = 32;
[[nodiscard]] QString FormatExpireDate(TimeId date) { [[nodiscard]] QString FormatExpireDate(TimeId date) {
if (date > 0) { if (date > 0) {
@ -278,6 +279,20 @@ void EditInviteLinkBox(
regenerate(); regenerate();
const auto labelField = container->add(
object_ptr<Ui::InputField>(
container,
st::defaultInputField,
tr::lng_group_invite_label_header(),
data.label),
style::margins(
st::settingsSubsectionTitlePadding.left(),
st::settingsSectionSkip,
st::settingsSubsectionTitlePadding.right(),
st::settingsSectionSkip * 2));
labelField->setMaxLength(kMaxLabelLength);
addDivider(container, tr::lng_group_invite_label_about());
const auto buttonSkip = st::settingsSectionSkip; const auto buttonSkip = st::settingsSectionSkip;
const auto requestApproval = container->add( const auto requestApproval = container->add(
object_ptr<SettingsButton>( object_ptr<SettingsButton>(
@ -303,6 +318,7 @@ void EditInviteLinkBox(
? tr::lng_formatting_link_create ? tr::lng_formatting_link_create
: tr::lng_settings_save; : tr::lng_settings_save;
box->addButton(saveLabel(), [=] { box->addButton(saveLabel(), [=] {
const auto label = labelField->getLastText();
const auto expireDate = (state->expireValue == kMaxLimit) const auto expireDate = (state->expireValue == kMaxLimit)
? 0 ? 0
: (state->expireValue < 0) : (state->expireValue < 0)
@ -313,6 +329,7 @@ void EditInviteLinkBox(
: state->usageValue; : state->usageValue;
done(InviteLinkFields{ done(InviteLinkFields{
.link = link, .link = link,
.label = label,
.expireDate = expireDate, .expireDate = expireDate,
.usageLimit = usageLimit, .usageLimit = usageLimit,
.requestApproval = state->requestApproval.current(), .requestApproval = state->requestApproval.current(),

View File

@ -13,6 +13,7 @@ namespace Ui {
struct InviteLinkFields { struct InviteLinkFields {
QString link; QString link;
QString label;
TimeId expireDate = 0; TimeId expireDate = 0;
int usageLimit = 0; int usageLimit = 0;
bool requestApproval = false; bool requestApproval = false;