From 3399a05f1fd6a3794b910eab122c6a4b4396d1a4 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 8 Feb 2021 19:04:21 +0400 Subject: [PATCH] Improve phrases for invite links. --- Telegram/Resources/langs/lang.strings | 18 ++- Telegram/SourceFiles/boxes/boxes.style | 4 + Telegram/SourceFiles/boxes/peer_list_box.cpp | 10 +- Telegram/SourceFiles/boxes/peer_list_box.h | 1 + .../boxes/peers/edit_peer_invite_link.cpp | 139 +++++++++++++++--- .../boxes/peers/edit_peer_invite_link.h | 5 + .../boxes/peers/edit_peer_invite_links.cpp | 86 ++++------- Telegram/SourceFiles/info/info.style | 1 + .../ui/controls/invite_link_buttons.cpp | 14 ++ .../ui/controls/invite_link_buttons.h | 4 + 10 files changed, 199 insertions(+), 83 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 9c764943b..1b79c72d9 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -943,6 +943,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_manage_peer_link_type" = "Link type"; "lng_manage_peer_link_permanent" = "Permanent link"; "lng_manage_peer_link_invite" = "Invite link"; +"lng_manage_peer_link_expired" = "Expired link"; "lng_manage_private_group_title" = "Private"; "lng_manage_public_group_title" = "Public"; "lng_manage_private_peer_title" = "Private"; @@ -1186,9 +1187,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_copy" = "Copy Link"; "lng_group_invite_share" = "Share Link"; "lng_group_invite_revoke" = "Revoke Link"; +"lng_group_invite_reactivate" = "Reactivate Link"; "lng_group_invite_no_joined" = "No one joined yet"; -"lng_group_invite_joined#one" = "{count} person joined"; -"lng_group_invite_joined#other" = "{count} people joined"; +"lng_group_invite_joined#one" = "{count} joined"; +"lng_group_invite_joined#other" = "{count} joined"; +"lng_group_invite_remaining#one" = "{count} remaining"; +"lng_group_invite_remaining#other" = "{count} remaining"; +"lng_group_invite_can_join#one" = "{count} can join"; +"lng_group_invite_can_join#other" = "{count} can join"; +"lng_group_invite_days_left#one" = "{count} day left"; +"lng_group_invite_days_left#other" = "{count} days left"; "lng_group_invite_about_permanent_group" = "Anyone who has Telegram installed will be able to join your group by following this link."; "lng_group_invite_about_permanent_channel" = "Anyone who has Telegram installed will be able to join your channel by following this link."; "lng_group_invite_manage" = "Manage Invite Links"; @@ -1197,7 +1205,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_add" = "Create a New Link"; "lng_group_invite_add_about" = "You can generate invite links that will expire after they've been used."; "lng_group_invite_expires_at" = "This link expires {when}."; -"lng_group_invite_expired_already" = "This link has expired."; "lng_group_invite_created_by" = "Link created by"; "lng_group_invite_context_copy" = "Copy"; "lng_group_invite_context_share" = "Share"; @@ -1210,7 +1217,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_revoked_title" = "Revoked links"; "lng_group_invite_revoke_about" = "Are you sure you want to revoke that invite link?"; "lng_group_invite_link_expired" = "Expired"; -"lng_group_invite_link_revoked" = "Revoked"; "lng_group_invite_edit_title" = "Edit Link"; "lng_group_invite_new_title" = "New Link"; "lng_group_invite_expire_title" = "Limit by time period"; @@ -1228,6 +1234,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_invite_other_count#other" = "{count} invite links"; "lng_group_invite_permanent_other" = "Permanent link of this admin"; "lng_group_invite_other_list" = "Invite links created by this admin"; +"lng_group_invite_expired_about" = "The time limit for this link has expired."; +"lng_group_invite_used_about" = "This link reached its usage limit."; +"lng_group_invite_can_join_via_link#one" = "{count} person can join via this link."; +"lng_group_invite_can_join_via_link#other" = "{count} people can join via this link."; "lng_channel_public_link_copied" = "Link copied to clipboard."; "lng_context_about_private_link" = "This link will only work for members of this chat."; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 46807672e..5905da120 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -960,3 +960,7 @@ scheduleTimeSeparator: FlatLabel(defaultFlatLabel) { } } scheduleTimeSeparatorPadding: margins(2px, 0px, 2px, 0px); + +boxAttentionDividerLabel: FlatLabel(boxDividerLabel) { + textFg: boxTextFgError; +} diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index 08fd4104c..e8939f11a 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -125,7 +125,11 @@ void PeerListBox::prepare() { _controller->setDelegate(this); - setDimensions(_controller->contentWidth(), st::boxMaxListHeight); + _controller->boxHeightValue( + ) | rpl::start_with_next([=](int height) { + setDimensions(_controller->contentWidth(), height); + }, lifetime()); + if (_select) { _select->finishAnimating(); Ui::SendPendingMoveResizeEvents(_select); @@ -332,6 +336,10 @@ int PeerListController::contentWidth() const { return st::boxWideWidth; } +rpl::producer PeerListController::boxHeightValue() const { + return rpl::single(st::boxMaxListHeight); +} + void PeerListBox::addSelectItem( not_null peer, anim::type animated) { diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h index 81069bca3..aafbdd5df 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.h +++ b/Telegram/SourceFiles/boxes/peer_list_box.h @@ -430,6 +430,7 @@ public: std::unique_ptr state); virtual int contentWidth() const; + virtual rpl::producer boxHeightValue() const; bool isRowSelected(not_null row) { return delegate()->peerListIsRowChecked(row); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index 69dca4252..036f39ab0 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/abstract_button.h" #include "ui/toast/toast.h" #include "ui/text/text_utilities.h" +#include "ui/boxes/edit_invite_link.h" #include "boxes/share_box.h" #include "history/view/history_view_group_call_tracker.h" // GenerateUs... #include "history/history_message.h" // GetErrorTextForSending. @@ -38,7 +39,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_common.h" #include "mtproto/sender.h" #include "styles/style_boxes.h" +#include "styles/style_layers.h" // st::boxDividerLabel. #include "styles/style_info.h" +#include "styles/style_settings.h" // st::settingsDividerLabelPadding. #include @@ -58,6 +61,8 @@ public: void rowClicked(not_null row) override; Main::Session &session() const override; + //rpl::producer boxHeightValue() const override; + private: void appendSlice(const Api::JoinedByLinkSlice &slice); [[nodiscard]] object_ptr prepareHeader(); @@ -69,6 +74,8 @@ private: std::optional _lastUser; bool _allLoaded = false; + Ui::RpWidget *_headerWidget = nullptr; + MTP::Sender _api; rpl::lifetime _lifetime; @@ -108,6 +115,9 @@ void AddHeaderBlock( const auto revokeLink = crl::guard(weak, [=] { RevokeLink(peer, data.admin, data.link); }); + const auto editLink = crl::guard(weak, [=] { + EditLink(peer, data); + }); const auto createMenu = [=] { auto result = base::make_unique_q(container); @@ -117,6 +127,9 @@ void AddHeaderBlock( result->addAction( tr::lng_group_invite_context_share(tr::now), shareLink); + result->addAction( + tr::lng_group_invite_context_edit(tr::now), + editLink); result->addAction( tr::lng_group_invite_context_revoke(tr::now), revokeLink); @@ -137,34 +150,43 @@ void AddHeaderBlock( label->clicks( ) | rpl::start_with_next(copyLink, label->lifetime()); - if ((data.expireDate <= 0 || now < data.expireDate) - && (data.usageLimit <= 0 || data.usage < data.usageLimit)) { - AddCopyShareLinkButtons( - container, - copyLink, - shareLink); + if (IsExpiredLink(data, now)) { + AddReactivateLinkButton(container, editLink); + } else { + AddCopyShareLinkButtons(container, copyLink, shareLink); } } void AddHeader( not_null container, not_null peer, - const LinkData &data) { + const LinkData &data, + TimeId now) { using namespace Settings; - if (!data.revoked && !data.permanent) { - const auto now = base::unixtime::now(); AddHeaderBlock(container, peer, data, now); AddSkip(container, st::inviteLinkJoinedRowPadding.bottom() * 2); - if (data.expireDate > 0) { - AddDividerText( + const auto timeExpired = (data.expireDate > 0) + && (data.expireDate <= now); + const auto usageExpired = (data.usageLimit > 0) + && (data.usageLimit <= data.usage); + if (data.expireDate > 0 || usageExpired) { + container->add(object_ptr( container, - (data.expireDate > now - ? tr::lng_group_invite_expires_at( - lt_when, - rpl::single(langDateTime( - base::unixtime::parse(data.expireDate)))) - : tr::lng_group_invite_expired_already())); + object_ptr( + container, + (timeExpired + ? tr::lng_group_invite_expired_about() + : usageExpired + ? tr::lng_group_invite_used_about() + : tr::lng_group_invite_expires_at( + lt_when, + rpl::single(langDateTime( + base::unixtime::parse(data.expireDate))))), + (timeExpired + ? st::boxAttentionDividerLabel + : st::boxDividerLabel)), + st::settingsDividerLabelPadding)); } else { AddDivider(container); } @@ -177,6 +199,7 @@ void AddHeader( container, data.admin, rpl::single(langDateTime(base::unixtime::parse(data.date)))); + AddSkip(container, st::membersMarginBottom); } Controller::Controller(not_null peer, const LinkData &data) @@ -188,9 +211,11 @@ Controller::Controller(not_null peer, const LinkData &data) object_ptr Controller::prepareHeader() { using namespace Settings; + const auto now = base::unixtime::now(); + auto result = object_ptr((QWidget*)nullptr); const auto container = result.data(); - AddHeader(container, _peer, _data); + AddHeader(container, _peer, _data, now); AddDivider(container); AddSkip(container); AddSubsectionTitle( @@ -200,11 +225,20 @@ object_ptr Controller::prepareHeader() { lt_count, rpl::single(float64(_data.usage))) : tr::lng_group_invite_no_joined())); + + _headerWidget = result.data(); return result; } void Controller::prepare() { delegate()->peerListSetAboveWidget(prepareHeader()); + if (!_data.usage && _data.usageLimit > 0) { + setDescriptionText( + tr::lng_group_invite_can_join_via_link( + tr::now, + lt_count, + _data.usageLimit)); + } _allLoaded = (_data.usage == 0); const auto &inviteLinks = _peer->session().api().inviteLinks(); const auto slice = inviteLinks.joinedFirstSliceLoaded(_peer, _data.link); @@ -252,6 +286,12 @@ Main::Session &Controller::session() const { return _peer->session(); } +//rpl::producer Controller::boxHeightValue() const { +// Expects(_headerWidget != nullptr); +// +// return _headerWidget->heightValue(); +//} + SingleRowController::SingleRowController( not_null peer, rpl::producer status) @@ -284,6 +324,11 @@ Main::Session &SingleRowController::session() const { } // namespace +bool IsExpiredLink(const Api::InviteLink &data, TimeId now) { + return (data.expireDate > 0 && data.expireDate <= now) + || (data.usageLimit > 0 && data.usageLimit <= data.usage); +} + void AddSinglePeerRow( not_null container, not_null peer, @@ -394,10 +439,7 @@ void AddPermanentLinkBlock( label->clicks( ) | rpl::start_with_next(copyLink, label->lifetime()); - AddCopyShareLinkButtons( - container, - copyLink, - shareLink); + AddCopyShareLinkButtons(container, copyLink, shareLink); struct JoinedState { QImage cachedUserpics; @@ -574,6 +616,52 @@ void ShareInviteLinkBox(not_null peer, const QString &link) { Ui::LayerOption::KeepOther); } +void EditLink( + not_null peer, + const Api::InviteLink &data) { + const auto creating = data.link.isEmpty(); + const auto box = std::make_shared>(); + using Fields = Ui::InviteLinkFields; + const auto done = [=](Fields result) { + const auto finish = [=](Api::InviteLink finished) { + if (creating) { + ShowInviteLinkBox(peer, finished); + } + if (*box) { + (*box)->closeBox(); + } + }; + if (creating) { + Assert(data.admin->isSelf()); + peer->session().api().inviteLinks().create( + peer, + finish, + result.expireDate, + result.usageLimit); + } else { + peer->session().api().inviteLinks().edit( + peer, + data.admin, + result.link, + result.expireDate, + result.usageLimit, + finish); + } + }; + *box = Ui::show( + (creating + ? Box(Ui::CreateInviteLinkBox, done) + : Box( + Ui::EditInviteLinkBox, + Fields{ + .link = data.link, + .expireDate = data.expireDate, + .usageLimit = data.usageLimit + }, + done)), + Ui::LayerOption::KeepOther); +} + void RevokeLink( not_null peer, not_null admin, @@ -597,8 +685,11 @@ void RevokeLink( void ShowInviteLinkBox( not_null peer, const Api::InviteLink &link) { + const auto now = base::unixtime::now(); auto initBox = [=](not_null box) { - box->setTitle((link.permanent && !link.revoked) + box->setTitle(IsExpiredLink(link, now) + ? tr::lng_manage_peer_link_expired() + : (link.permanent && !link.revoked) ? tr::lng_manage_peer_link_permanent() : tr::lng_manage_peer_link_invite()); peer->session().api().inviteLinks().updates( @@ -622,7 +713,7 @@ void ShowInviteLinkBox( Ui::show(Box([=](not_null box) { initBox(box); const auto container = box->verticalLayout(); - AddHeader(container, peer, link); + AddHeader(container, peer, link, now); }), Ui::LayerOption::KeepOther); } } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h index 50e4da743..1543504f6 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.h @@ -19,6 +19,8 @@ namespace Ui { class VerticalLayout; } // namespace Ui +[[nodiscard]] bool IsExpiredLink(const Api::InviteLink &data, TimeId now); + void AddSinglePeerRow( not_null container, not_null peer, @@ -36,6 +38,9 @@ void RevokeLink( not_null peer, not_null admin, const QString &link); +void EditLink( + not_null peer, + const Api::InviteLink &data); void ShowInviteLinkBox( not_null peer, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 8cff8a49e..6c5f04362 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "main/main_session.h" #include "api/api_invite_links.h" -#include "ui/boxes/edit_invite_link.h" #include "ui/wrap/slide_wrap.h" #include "ui/widgets/buttons.h" #include "ui/widgets/popup_menu.h" @@ -156,71 +155,50 @@ private: } [[nodiscard]] QString ComputeStatus(const InviteLinkData &link, TimeId now) { + const auto expired = IsExpiredLink(link, now); + const auto revoked = link.revoked; auto result = link.usage ? tr::lng_group_invite_joined(tr::now, lt_count_decimal, link.usage) + : (!expired && !revoked && link.usageLimit > 0) + ? tr::lng_group_invite_can_join( + tr::now, + lt_count_decimal, + link.usageLimit) : tr::lng_group_invite_no_joined(tr::now); const auto add = [&](const QString &text) { result += QString::fromUtf8(" \xE2\xB8\xB1 ") + text; }; - if (link.revoked) { - add(tr::lng_group_invite_link_revoked(tr::now)); - } else if ((link.usageLimit > 0 && link.usage >= link.usageLimit) - || (link.expireDate > 0 && now >= link.expireDate)) { + if (revoked) { + return result; + } else if (expired) { add(tr::lng_group_invite_link_expired(tr::now)); + return result; + } + if (link.usage > 0 && link.usageLimit > link.usage) { + result += ", " + tr::lng_group_invite_remaining( + tr::now, + lt_count_decimal, + link.usageLimit - link.usage); + } + if (link.expireDate > now) { + const auto left = (link.expireDate - now); + if (left >= 86400) { + add(tr::lng_group_invite_days_left( + tr::now, + lt_count, + left / 86400)); + } else { + const auto time = base::unixtime::parse(link.expireDate).time(); + add(time.toString(Qt::SystemLocaleLongDate)); + } } return result; } -void EditLink( - not_null peer, - const InviteLinkData &data) { - const auto creating = data.link.isEmpty(); - const auto box = std::make_shared>(); - using Fields = Ui::InviteLinkFields; - const auto done = [=](Fields result) { - const auto finish = [=](Api::InviteLink finished) { - if (creating) { - ShowInviteLinkBox(peer, finished); - } - if (*box) { - (*box)->closeBox(); - } - }; - if (creating) { - Assert(data.admin->isSelf()); - peer->session().api().inviteLinks().create( - peer, - finish, - result.expireDate, - result.usageLimit); - } else { - peer->session().api().inviteLinks().edit( - peer, - data.admin, - result.link, - result.expireDate, - result.usageLimit, - finish); - } - }; - *box = Ui::show( - (creating - ? Box(Ui::CreateInviteLinkBox, done) - : Box( - Ui::EditInviteLinkBox, - Fields{ - .link = data.link, - .expireDate = data.expireDate, - .usageLimit = data.usageLimit - }, - done)), - Ui::LayerOption::KeepOther); -} - void DeleteLink( - not_null peer, - not_null admin, - const QString &link) { + not_null peer, + not_null admin, + const QString &link) { const auto box = std::make_shared>(); const auto sure = [=] { const auto finish = [=] { diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index f919fd15f..91a6d8ee7 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -868,6 +868,7 @@ inviteLinkShare: RoundButton(inviteLinkCopy) { icon: icon {{ "info/edit/links_share", activeButtonFg }}; iconOver: icon {{ "info/edit/links_share", activeButtonFgOver }}; } +inviteLinkReactivate: inviteLinkShare; inviteLinkUserpics: GroupCallUserpics { size: 28px; shift: 6px; diff --git a/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp b/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp index 11a904057..d050524ae 100644 --- a/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp +++ b/Telegram/SourceFiles/ui/controls/invite_link_buttons.cpp @@ -61,6 +61,20 @@ void AddCopyShareLinkButtons( }, wrap->lifetime()); } + +void AddReactivateLinkButton( + not_null container, + Fn editLink) { + const auto button = container->add( + object_ptr( + container, + tr::lng_group_invite_reactivate(), + st::inviteLinkReactivate), + st::inviteLinkButtonsPadding); + button->setTextTransform(RoundButton::TextTransform::NoTransform); + button->setClickedCallback(editLink); +} + not_null AddJoinedCountButton( not_null container, rpl::producer content, diff --git a/Telegram/SourceFiles/ui/controls/invite_link_buttons.h b/Telegram/SourceFiles/ui/controls/invite_link_buttons.h index c21195b76..5b4af472c 100644 --- a/Telegram/SourceFiles/ui/controls/invite_link_buttons.h +++ b/Telegram/SourceFiles/ui/controls/invite_link_buttons.h @@ -17,6 +17,10 @@ void AddCopyShareLinkButtons( Fn copyLink, Fn shareLink); +void AddReactivateLinkButton( + not_null container, + Fn editLink); + struct JoinedCountContent { int count = 0; QImage userpics;