From 3af3f85f82be29cf8e9e4bfc06dbd64d679b4490 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 12 Oct 2021 12:39:07 +0400 Subject: [PATCH] Allow creating approve-only invite links. --- .../icons/info/edit/group_manage_requests.png | Bin 0 -> 620 bytes .../info/edit/group_manage_requests@2x.png | Bin 0 -> 1255 bytes .../info/edit/group_manage_requests@3x.png | Bin 0 -> 2238 bytes Telegram/Resources/langs/lang.strings | 33 ++++++++- Telegram/SourceFiles/api/api_invite_links.cpp | 30 ++++++-- Telegram/SourceFiles/api/api_invite_links.h | 11 ++- Telegram/SourceFiles/api/api_updates.cpp | 12 +++ .../boxes/peers/edit_peer_info_box.cpp | 42 +++++++++++ .../boxes/peers/edit_peer_invite_link.cpp | 11 ++- Telegram/SourceFiles/data/data_changes.h | 69 +++++++++--------- Telegram/SourceFiles/data/data_channel.cpp | 12 +++ Telegram/SourceFiles/data/data_channel.h | 6 ++ Telegram/SourceFiles/data/data_chat.cpp | 12 +++ Telegram/SourceFiles/data/data_chat.h | 6 ++ .../admin_log/history_admin_log_item.cpp | 10 +++ Telegram/SourceFiles/info/info.style | 1 + .../info/profile/info_profile_values.cpp | 19 +++++ .../info/profile/info_profile_values.h | 1 + .../SourceFiles/ui/boxes/edit_invite_link.cpp | 33 ++++++++- .../SourceFiles/ui/boxes/edit_invite_link.h | 3 + 20 files changed, 259 insertions(+), 52 deletions(-) create mode 100644 Telegram/Resources/icons/info/edit/group_manage_requests.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_requests@2x.png create mode 100644 Telegram/Resources/icons/info/edit/group_manage_requests@3x.png diff --git a/Telegram/Resources/icons/info/edit/group_manage_requests.png b/Telegram/Resources/icons/info/edit/group_manage_requests.png new file mode 100644 index 0000000000000000000000000000000000000000..8cf516c31fed3d16665331fa891c5b37715f4468 GIT binary patch literal 620 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz07Sip>6gA}f5OZp5{cFohpF(iWX zZRp0XLk72;5zPbwpDz8`uVQBE|uy(zU6M-%ewvEM`V8P zXQ*Re)9iYsYHygD0{^|O_nJ3%CmYTW*}~u6EXBjN>SBh+5gCT}ijP(mN1Tz+}sWyzxFpFOX?o*K4Vv~+ju^Ouvv zib6llH4mA7+I8Ke^Xyq0mtB9o^7h+r$tNFw4ES4Tf75y`bGloTzv^tA>E1VQgs}%+ zVTfqnrNEJ5GTYa5_Su%(@4vgru4c?ztJBip8}mGnIidI5OAh1bpCf-5@D$rzpK1M} zYOhFJ@A+r0jrN-=&y??;d*N-F_3`?`M#0N3uguu4x>#dVpvnC6nHDlZGRJq$I=`>Y rayP5Woc+wKFP?5&!xJK_v6uONL9UFz#bP0l+XkK2fG0n literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_requests@2x.png b/Telegram/Resources/icons/info/edit/group_manage_requests@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f9c210affcddd19a74097be280caa2a2fb54f61a GIT binary patch literal 1255 zcmVPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuAQAtEWRCodHSidhlK@i{fgv75z zbTldvQ4vu{G}IariT^`FtB~jvMB#tXXe1IMDiT2>lsrKpK|({qeD`y)Ywq6NyW6+x z>v^}6Y;JCMW_ISYv%7Ow5JHe_xus%mBmWBA1K=|t=Lq;Mkn;o(J_CFPVLJ@i8rSJq}suSUT@Q-yQ(0rb+M$1?bd3CfEZIm-Yt-2Q)c3NnKrCK?9GzC_(p^ zv%n4jd8kEWqNSxJDi(`nfuRp$QG$(M1i=mfd9sJ%!79fEuL8taG!tM$jq_)Fdpj*I zF4F1gDaqR;XyDO@F)nMt+W{c2w-5kG^)zQ7!wEK`I^*Nxbai#b#X>yBAQRQA;@u7a ztPCgY`7|dW!wELH4g&l9{Ny6BJY*bzP+I^XwJWWB6bZ=MYzOO7`1bZzF5Wi{4-bF8 zBydp~Zewn4PW1HjXwCQccd@XrAiBG|g9aXbiXaOe8w#bLG-UMd`Vc%jI}0uH{QP{G zKj`CxgVEG)CT|unQw%0`eGO(gQ_{|msE;Y{b^yS*%R=Z+0^qU~Mmlq<`7NN*%hksa ziX2XFZ*K$;R7njUeVm}9{HWq-zXjB!$b`7mcj{V`PdnovISN!F$CUx&>a zfPsMlv9huv&d<+-zrXU37bZ=ncAyIzuoXxB{r%$T=t#W2z6Rd`@+&ZSmP#eDxw$E3 zW@d!^&k(1YsjsR#w5h3y*4EbO>}*i3R+uQ(DU`U-g$+*F4w#GEYi@3)#>Pe}6bjVR z(n6!7qqMcP#nl3Sd3iac$8E&$W&p^;Dl{P+@9gX}nJ`usHliS4t{8{Q`qtOiLq0t` zJWxYJLz!HHEi(Y*X&;<CwKdxF5DSVXiE;TU%S{>FJ59lKktGNmtr80{|5nC#0~c zjU;)SA00b8JKQarnwrYwm+|G<*475lG~M6dYu=WL+KSFq$^PBlU1(UA(V_RG7h?cW zgec;?>iex{)R*Xcl3&IE93CEOTb$ROmdwS)g|@(X-I>4~A0O)x1b=dJqTkb)OnXHd z3FPGOXyw8AoTT#<^dP2=$yV2W71$>%-;{sFDJDgOd> RK*|6B002ovPDHLkV1j2=I*kAT literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/info/edit/group_manage_requests@3x.png b/Telegram/Resources/icons/info/edit/group_manage_requests@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..220e567f3b749fce0fbca9f6b1111f2e4e4c41b1 GIT binary patch literal 2238 zcma)8XHe729{q>3P!QUU?4BMTc)HUNN=3<5aW z3_4mPbNr)vkvacY9~oa>@U&w`oVP+DogzUi?l$FiS5Dmc)n+Y)6Br=aA1}#wkjj>m z_484HT!3(zDGfXDFZT~$=T<$z4(u$%pq&L{U_hX_`|;lBS$428AoJZTVJ z^karKyVeosfx5Y^;Xnq5^45zF9XvAOlDX%c3ZMWE3z^!TDGoUxUS4Dhj{%7isG?vD zfw6vde`B6fe+Dw? z#f_M0ZP(V2vqJ?2PZaG6ET3CEE^Wqs7|jsVU>^M53;i~(WEghv8PpHFcI#~ICF5Zia&Rcg8Syy`XT<22}x7PK5|BXN>ny&7De zs_WT5_ze@AveT9kHag@34}l2e))$AMr0TAwRO#5Xb|J&4t@FVuB0@1co5||#EhZ6b zj1oO8gPyPD8}M^^x-oRg0jhm7^?Z-lXj>SIxxKOQ=~pLlsYA(kUiI7eu9&^t&~tF{ z-si`{U=(`MmV`gc>M5YbnGty>P*v} z5^GYZN*MoxRZF?4z%uN&@s|(!1gzkByomMYv8DNTPvDpoJYPkkCID7ZH*NZ?^b$jH zYG8AEZ7@$mZ|y@@^Be22`ILgH!QdGUfPLPylbgM1kP1^I+or@Xn6S;7)Np&@01ZJP z*E_}2J0wY(6XRJ58q@JYtLDu0k3AQ?s03Fo?&BL}a#I0#~*eLCa-?^x;2kv~n$#)=}Q!KZ4=siiiF0yE-%r+Ve z(l?*$iZkAuwoc>`vd)C!qKkiALh|FwKWDLnF z)$WZzLj2y_y*tLJE16qTFTX3kDb-v`;sxTnsdM8Ufcv z2K53*65rw2m07DvBa_ErBkhqJKKEB=`*~`G;J=B+coo_*qoaL2NC|uWG>?s=VuXkN z2qtCC<*)8U-a7@A3IgJ_XGQT(INWux2m3n@YEae{LTwEdQCtW;U>)umdaS8FZN7{< zvFceaAnn*`IQ)Y(nc)WySxhyr5eIa!3{}IF>S8-@9qn2rQT#YFd3kB9?0$uo&S$sr zrcl^yI+cG?QLrUX!;{j`QRWSy+$G{0aWf^tl7>0H-6*g9?Tyxbmb1AG@1BJBNO%Fr0PxP{N=-LKqzF#E!iGg;sv$dBD5M>jN0p#H*`K zKMZ-vlykqG=+md^=!G)%a4r)`)4~KWK|S{N5}06^WXLTFc4L|M-dSH4O&eSWS?SRCc{C_C~`IAARjBX jLvopj88?OgKMf9nBApKMEWOj?= peer, Fn done, TimeId expireDate, - int usageLimit) { - performCreate(peer, std::move(done), false, expireDate, usageLimit); + int usageLimit, + bool requestApproval) { + performCreate( + peer, + std::move(done), + false, + expireDate, + usageLimit, + requestApproval); } void InviteLinks::performCreate( @@ -79,7 +86,8 @@ void InviteLinks::performCreate( Fn done, bool revokeLegacyPermanent, TimeId expireDate, - int usageLimit) { + int usageLimit, + bool requestApproval) { if (const auto i = _createCallbacks.find(peer) ; i != end(_createCallbacks)) { if (done) { @@ -98,7 +106,8 @@ void InviteLinks::performCreate( ? Flag::f_legacy_revoke_permanent : Flag(0)) | (expireDate ? Flag::f_expire_date : Flag(0)) - | (usageLimit ? Flag::f_usage_limit : Flag(0))), + | (usageLimit ? Flag::f_usage_limit : Flag(0)) + | (requestApproval ? Flag::f_request_needed : Flag(0))), peer->input, MTP_int(expireDate), MTP_int(usageLimit) @@ -201,6 +210,7 @@ void InviteLinks::edit( const QString &link, TimeId expireDate, int usageLimit, + bool requestApproval, Fn done) { performEdit( peer, @@ -209,7 +219,8 @@ void InviteLinks::edit( std::move(done), false, expireDate, - usageLimit); + usageLimit, + requestApproval); } void InviteLinks::performEdit( @@ -219,7 +230,8 @@ void InviteLinks::performEdit( Fn done, bool revoke, TimeId expireDate, - int usageLimit) { + int usageLimit, + bool requestApproval) { const auto key = LinkKey{ peer, link }; if (_deleteCallbacks.contains(key)) { return; @@ -239,12 +251,13 @@ void InviteLinks::performEdit( _api->request(MTPmessages_EditExportedChatInvite( MTP_flags((revoke ? Flag::f_revoked : Flag(0)) | (!revoke ? Flag::f_expire_date : Flag(0)) - | (!revoke ? Flag::f_usage_limit : Flag(0))), + | (!revoke ? Flag::f_usage_limit : Flag(0)) + | (!revoke ? Flag::f_request_needed : Flag(0))), peer->input, MTP_string(link), MTP_int(expireDate), MTP_int(usageLimit), - MTPbool() // request_needed // #TODO requests + MTP_bool(requestApproval) )).done([=](const MTPmessages_ExportedChatInvite &result) { const auto callbacks = _editCallbacks.take(key); const auto peer = key.peer; @@ -632,6 +645,7 @@ auto InviteLinks::parse( .expireDate = data.vexpire_date().value_or_empty(), .usageLimit = data.vusage_limit().value_or_empty(), .usage = data.vusage().value_or_empty(), + .requestApproval = data.is_request_needed(), .permanent = data.is_permanent(), .revoked = data.is_revoked(), }; diff --git a/Telegram/SourceFiles/api/api_invite_links.h b/Telegram/SourceFiles/api/api_invite_links.h index 38f673f8a..24c22889f 100644 --- a/Telegram/SourceFiles/api/api_invite_links.h +++ b/Telegram/SourceFiles/api/api_invite_links.h @@ -19,6 +19,7 @@ struct InviteLink { TimeId expireDate = 0; int usageLimit = 0; int usage = 0; + bool requestApproval = false; bool permanent = false; bool revoked = false; }; @@ -61,13 +62,15 @@ public: not_null peer, Fn done = nullptr, TimeId expireDate = 0, - int usageLimit = 0); + int usageLimit = 0, + bool requestApproval = false); void edit( not_null peer, not_null admin, const QString &link, TimeId expireDate, int usageLimit, + bool requestApproval, Fn done = nullptr); void revoke( not_null peer, @@ -164,13 +167,15 @@ private: Fn done, bool revoke, TimeId expireDate = 0, - int usageLimit = 0); + int usageLimit = 0, + bool requestApproval = false); void performCreate( not_null peer, Fn done, bool revokeLegacyPermanent, TimeId expireDate = 0, - int usageLimit = 0); + int usageLimit = 0, + bool requestApproval = false); void requestJoinedFirstSlice(LinkKey key); [[nodiscard]] std::optional lookupJoinedFirstSlice( diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index 2804319ed..eed3d963e 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -1965,6 +1965,18 @@ void Updates::feedUpdate(const MTPUpdate &update) { } } break; + case mtpc_updatePendingJoinRequests: { + const auto &d = update.c_updatePendingJoinRequests(); + if (const auto peer = session().data().peerLoaded(peerFromMTP(d.vpeer()))) { + const auto count = d.vrequests_pending().v; + if (const auto chat = peer->asChat()) { + chat->setPendingRequestsCount(count); + } else if (const auto channel = peer->asChannel()) { + channel->setPendingRequestsCount(count); + } + } + } break; + case mtpc_updateServiceNotification: { const auto &d = update.c_updateServiceNotification(); const auto text = TextWithEntities { diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index 71d7c400c..8060ef883 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -297,6 +297,7 @@ private: void fillSignaturesButton(); void fillHistoryVisibilityButton(); void fillManageSection(); + void fillPendingRequestsButton(); void submitTitle(); void submitDescription(); @@ -845,6 +846,8 @@ void Controller::fillHistoryVisibilityButton() { void Controller::fillManageSection() { Expects(_controls.buttonsLayout != nullptr); + using namespace rpl::mappers; + const auto chat = _peer->asChat(); const auto channel = _peer->asChannel(); const auto isChannel = (!chat); @@ -1042,6 +1045,9 @@ void Controller::fillManageSection() { }, st::infoIconMembers); } + + fillPendingRequestsButton(); + if (canViewKicked) { AddButtonWithCount( _controls.buttonsLayout, @@ -1089,6 +1095,42 @@ void Controller::fillManageSection() { } } +void Controller::fillPendingRequestsButton() { + auto pendingRequestsCount = Info::Profile::MigratedOrMeValue( + _peer + ) | rpl::map( + Info::Profile::PendingRequestsCountValue + ) | rpl::flatten_latest( + ) | rpl::start_spawning(_controls.buttonsLayout->lifetime()); + const auto wrap = _controls.buttonsLayout->add( + object_ptr>( + _controls.buttonsLayout, + object_ptr( + _controls.buttonsLayout))); + AddButtonWithCount( + wrap->entity(), + (_isGroup + ? tr::lng_manage_peer_requests() + : tr::lng_manage_peer_requests_channel()), + rpl::duplicate(pendingRequestsCount) | ToPositiveNumberString(), + [=] { + _navigation->parentController()->show( + Box( // #TODO requests + ManageInviteLinksBox, + _peer, + _peer->session().user(), + 0, + 0), + Ui::LayerOption::KeepOther); + }, + st::infoIconRequests); + std::move( + pendingRequestsCount + ) | rpl::start_with_next([=](int count) { + wrap->toggle(count > 0, anim::type::instant); + }, wrap->lifetime()); +} + void Controller::submitTitle() { Expects(_controls.title != nullptr); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index 56bef14ba..e72060af4 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -943,7 +943,8 @@ void EditLink( peer, finish, result.expireDate, - result.usageLimit); + result.usageLimit, + result.requestApproval); } else { peer->session().api().inviteLinks().edit( peer, @@ -951,18 +952,22 @@ void EditLink( result.link, result.expireDate, result.usageLimit, + result.requestApproval, finish); } }; + const auto isGroup = !peer->isBroadcast(); *box = Ui::show( (creating - ? Box(Ui::CreateInviteLinkBox, done) + ? Box(Ui::CreateInviteLinkBox, isGroup, done) : Box( Ui::EditInviteLinkBox, Fields{ .link = data.link, .expireDate = data.expireDate, - .usageLimit = data.usageLimit + .usageLimit = data.usageLimit, + .requestApproval = data.requestApproval, + .isGroup = isGroup, }, done)), Ui::LayerOption::KeepOther); diff --git a/Telegram/SourceFiles/data/data_changes.h b/Telegram/SourceFiles/data/data_changes.h index 0284dc8df..9882726a4 100644 --- a/Telegram/SourceFiles/data/data_changes.h +++ b/Telegram/SourceFiles/data/data_changes.h @@ -50,51 +50,52 @@ struct NameUpdate { }; struct PeerUpdate { - enum class Flag : uint32 { + enum class Flag : uint64 { None = 0, // Common flags - Name = (1U << 0), - Username = (1U << 1), - Photo = (1U << 2), - About = (1U << 3), - Notifications = (1U << 4), - Migration = (1U << 5), - UnavailableReason = (1U << 6), - ChatThemeEmoji = (1U << 7), - IsBlocked = (1U << 8), - MessagesTTL = (1U << 9), + Name = (1ULL << 0), + Username = (1ULL << 1), + Photo = (1ULL << 2), + About = (1ULL << 3), + Notifications = (1ULL << 4), + Migration = (1ULL << 5), + UnavailableReason = (1ULL << 6), + ChatThemeEmoji = (1ULL << 7), + IsBlocked = (1ULL << 8), + MessagesTTL = (1ULL << 9), // For users - CanShareContact = (1U << 10), - IsContact = (1U << 11), - PhoneNumber = (1U << 12), - OnlineStatus = (1U << 13), - BotCommands = (1U << 14), - BotCanBeInvited = (1U << 15), - BotStartToken = (1U << 16), - CommonChats = (1U << 17), - HasCalls = (1U << 18), - SupportInfo = (1U << 19), - IsBot = (1U << 20), + CanShareContact = (1ULL << 10), + IsContact = (1ULL << 11), + PhoneNumber = (1ULL << 12), + OnlineStatus = (1ULL << 13), + BotCommands = (1ULL << 14), + BotCanBeInvited = (1ULL << 15), + BotStartToken = (1ULL << 16), + CommonChats = (1ULL << 17), + HasCalls = (1ULL << 18), + SupportInfo = (1ULL << 19), + IsBot = (1ULL << 20), // For chats and channels - InviteLinks = (1U << 21), - Members = (1U << 22), - Admins = (1U << 23), - BannedUsers = (1U << 24), - Rights = (1U << 25), + InviteLinks = (1ULL << 21), + Members = (1ULL << 22), + Admins = (1ULL << 23), + BannedUsers = (1ULL << 24), + Rights = (1ULL << 25), + PendingRequests = (1ULL << 26), // For channels - ChannelAmIn = (1U << 26), - StickersSet = (1U << 27), - ChannelLinkedChat = (1U << 28), - ChannelLocation = (1U << 29), - Slowmode = (1U << 30), - GroupCall = (1U << 31), + ChannelAmIn = (1ULL << 27), + StickersSet = (1ULL << 28), + ChannelLinkedChat = (1ULL << 29), + ChannelLocation = (1ULL << 30), + Slowmode = (1ULL << 31), + GroupCall = (1ULL << 32), // For iteration - LastUsedBit = (1U << 31), + LastUsedBit = (1ULL << 32), }; using Flags = base::flags; friend inline constexpr auto is_flag_type(Flag) { return true; } diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 4dfc5c38b..03a7a3457 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -195,6 +195,13 @@ void ChannelData::setKickedCount(int newKickedCount) { } } +void ChannelData::setPendingRequestsCount(int count) { + if (_pendingRequestsCount != count) { + _pendingRequestsCount = count; + session().changes().peerUpdated(this, UpdateFlag::PendingRequests); + } +} + ChatRestrictionsInfo ChannelData::KickedRestrictedRights( not_null participant) { using Flag = ChatRestriction; @@ -542,6 +549,9 @@ void ChannelData::setAdminRights(ChatAdminRights rights) { return; } _adminRights.set(rights); + if (!canHaveInviteLink()) { + setPendingRequestsCount(0); + } if (isMegagroup()) { const auto self = session().user(); if (hasAdminRights()) { @@ -874,6 +884,8 @@ void ApplyChannelUpdate( } channel->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty())); channel->fullUpdated(); + channel->setPendingRequestsCount( + update.vrequests_pending().value_or_empty()); if (canViewAdmins != channel->canViewAdmins() || canViewMembers != channel->canViewMembers()) { diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index adbb3a419..426db0454 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -177,6 +177,11 @@ public: } void setKickedCount(int newKickedCount); + [[nodiscard]] int pendingRequestsCount() const { + return _pendingRequestsCount; + } + void setPendingRequestsCount(int count); + [[nodiscard]] bool haveLeft() const { return flags() & Flag::Left; } @@ -426,6 +431,7 @@ private: int _adminsCount = 1; int _restrictedCount = 0; int _kickedCount = 0; + int _pendingRequestsCount = 0; MsgId _availableMinId = 0; RestrictionFlags _defaultRestrictions; diff --git a/Telegram/SourceFiles/data/data_chat.cpp b/Telegram/SourceFiles/data/data_chat.cpp index 0e3604766..780e97770 100644 --- a/Telegram/SourceFiles/data/data_chat.cpp +++ b/Telegram/SourceFiles/data/data_chat.cpp @@ -146,6 +146,9 @@ void ChatData::setAdminRights(ChatAdminRights rights) { return; } _adminRights.set(rights); + if (!canHaveInviteLink()) { + setPendingRequestsCount(0); + } session().changes().peerUpdated( this, UpdateFlag::Rights | UpdateFlag::Admins | UpdateFlag::BannedUsers); @@ -258,6 +261,13 @@ void ChatData::setBotCommands( } } +void ChatData::setPendingRequestsCount(int count) { + if (_pendingRequestsCount != count) { + _pendingRequestsCount = count; + session().changes().peerUpdated(this, UpdateFlag::PendingRequests); + } +} + namespace Data { void ApplyChatUpdate( @@ -431,6 +441,8 @@ void ApplyChatUpdate(not_null chat, const MTPDchatFull &update) { chat->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty())); chat->fullUpdated(); chat->setAbout(qs(update.vabout())); + chat->setPendingRequestsCount( + update.vrequests_pending().value_or_empty()); chat->session().api().applyNotifySettings( MTP_inputNotifyPeer(chat->input), diff --git a/Telegram/SourceFiles/data/data_chat.h b/Telegram/SourceFiles/data/data_chat.h index d0e6572e2..fa98af044 100644 --- a/Telegram/SourceFiles/data/data_chat.h +++ b/Telegram/SourceFiles/data/data_chat.h @@ -164,6 +164,11 @@ public: return _botCommands; } + [[nodiscard]] int pendingRequestsCount() const { + return _pendingRequestsCount; + } + void setPendingRequestsCount(int count); + // Still public data members. const MTPlong inputChat; @@ -185,6 +190,7 @@ private: RestrictionFlags _defaultRestrictions; AdminRightFlags _adminRights; int _version = 0; + int _pendingRequestsCount = 0; std::unique_ptr _call; PeerId _callDefaultJoinAs = 0; diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp index 468fe9328..d83ce3cde 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -312,6 +312,11 @@ TextWithEntities GenerateInviteLinkChangeText( return data.vusage_limit().value_or_empty(); }); }; + const auto requestApproval = [](const MTPExportedChatInvite &link) { + return link.match([](const MTPDchatInviteExported &data) { + return data.is_request_needed(); + }); + }; const auto wrapDate = [](TimeId date) { return date ? langDateTime(base::unixtime::parse(date)) @@ -326,12 +331,17 @@ TextWithEntities GenerateInviteLinkChangeText( const auto nowExpireDate = expireDate(newLink); const auto wasUsageLimit = usageLimit(prevLink); const auto nowUsageLimit = usageLimit(newLink); + const auto wasRequestApproval = requestApproval(prevLink); + const auto nowRequestApproval = requestApproval(newLink); 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))); } if (wasUsageLimit != nowUsageLimit) { result.text.append('\n').append(tr::lng_admin_log_invite_link_usage_limit(tr::now, lt_previous, wrapUsage(wasUsageLimit), lt_limit, wrapUsage(nowUsageLimit))); } + if (wasRequestApproval != nowRequestApproval) { + result.text.append('\n').append(nowRequestApproval ? tr::lng_admin_log_invite_link_request_needed(tr::now) : tr::lng_admin_log_invite_link_request_not_needed(tr::now)); + } result.entities.push_front(EntityInText(EntityType::Italic, 0, result.text.size())); return result; diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index 70fe83e08..53f94b1d0 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -345,6 +345,7 @@ infoProfileSeparatorPadding: margins( infoIconFg: menuIconFg; infoIconInformation: icon {{ "info_information", infoIconFg }}; infoIconMembers: icon {{ "info/edit/group_manage_members", infoIconFg, point(-2px, 0px) }}; +infoIconRequests: icon {{ "info/edit/group_manage_requests", infoIconFg, point(-2px, 0px) }}; infoIconNotifications: icon {{ "info_notifications", infoIconFg }}; infoIconActions: icon {{ "info_actions", infoIconFg }}; infoIconMediaPhoto: icon {{ "info_media_photo", infoIconFg }}; diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.cpp b/Telegram/SourceFiles/info/profile/info_profile_values.cpp index 862e58782..a4b377122 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_values.cpp @@ -239,6 +239,25 @@ rpl::producer MembersCountValue(not_null peer) { Unexpected("User in MembersCountViewer()."); } +rpl::producer PendingRequestsCountValue(not_null peer) { + if (const auto chat = peer->asChat()) { + return peer->session().changes().peerFlagsValue( + peer, + UpdateFlag::PendingRequests + ) | rpl::map([=] { + return chat->pendingRequestsCount(); + }); + } else if (const auto channel = peer->asChannel()) { + return peer->session().changes().peerFlagsValue( + peer, + UpdateFlag::PendingRequests + ) | rpl::map([=] { + return channel->pendingRequestsCount(); + }); + } + Unexpected("User in MembersCountViewer()."); +} + rpl::producer AdminsCountValue(not_null peer) { if (const auto chat = peer->asChat()) { return peer->session().changes().peerFlagsValue( diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.h b/Telegram/SourceFiles/info/profile/info_profile_values.h index 130c3c474..ef09ec80a 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.h +++ b/Telegram/SourceFiles/info/profile/info_profile_values.h @@ -49,6 +49,7 @@ rpl::producer CanShareContactValue(not_null user); rpl::producer CanAddContactValue(not_null user); rpl::producer AmInChannelValue(not_null channel); rpl::producer MembersCountValue(not_null peer); +rpl::producer PendingRequestsCountValue(not_null peer); rpl::producer AdminsCountValue(not_null peer); rpl::producer RestrictionsCountValue(not_null peer); rpl::producer RestrictedCountValue(not_null channel); diff --git a/Telegram/SourceFiles/ui/boxes/edit_invite_link.cpp b/Telegram/SourceFiles/ui/boxes/edit_invite_link.cpp index a9f27fe53..f83eaf304 100644 --- a/Telegram/SourceFiles/ui/boxes/edit_invite_link.cpp +++ b/Telegram/SourceFiles/ui/boxes/edit_invite_link.cpp @@ -52,6 +52,7 @@ void EditInviteLinkBox( const InviteLinkFields &data, Fn done) { const auto link = data.link; + const auto isGroup = data.isGroup; box->setTitle(link.isEmpty() ? tr::lng_group_invite_new_title() : tr::lng_group_invite_edit_title()); @@ -119,10 +120,12 @@ void EditInviteLinkBox( Buttons usageButtons; int expireValue = 0; int usageValue = 0; + rpl::variable requestApproval = false; }; const auto state = box->lifetime().make_state(State{ .expireValue = expire, - .usageValue = usage + .usageValue = usage, + .requestApproval = data.requestApproval, }); const auto regenerate = [=] { expireGroup->setValue(state->expireValue); @@ -260,6 +263,24 @@ void EditInviteLinkBox( regenerate(); + const auto buttonSkip = st::settingsSectionSkip; + const auto requestApproval = container->add( + object_ptr( + container, + tr::lng_group_invite_request_approve(), + st::settingsButton), + style::margins{ 0, buttonSkip, 0, buttonSkip }); + requestApproval->toggleOn(state->requestApproval.value()); + state->requestApproval = requestApproval->toggledValue(); + addDivider(rpl::conditional( + state->requestApproval.value(), + (isGroup + ? tr::lng_group_invite_about_approve() + : tr::lng_group_invite_about_approve_channel()), + (isGroup + ? tr::lng_group_invite_about_no_approve() + : tr::lng_group_invite_about_no_approve_channel()))); + const auto &saveLabel = link.isEmpty() ? tr::lng_formatting_link_create : tr::lng_settings_save; @@ -275,7 +296,9 @@ void EditInviteLinkBox( done(InviteLinkFields{ .link = link, .expireDate = expireDate, - .usageLimit = usageLimit + .usageLimit = usageLimit, + .requestApproval = state->requestApproval.current(), + .isGroup = isGroup, }); }); box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); @@ -283,8 +306,12 @@ void EditInviteLinkBox( void CreateInviteLinkBox( not_null box, + bool isGroup, Fn done) { - EditInviteLinkBox(box, InviteLinkFields(), std::move(done)); + EditInviteLinkBox( + box, + InviteLinkFields{ .isGroup = isGroup }, + std::move(done)); } } // namespace Ui diff --git a/Telegram/SourceFiles/ui/boxes/edit_invite_link.h b/Telegram/SourceFiles/ui/boxes/edit_invite_link.h index 82b3f1754..2a275441f 100644 --- a/Telegram/SourceFiles/ui/boxes/edit_invite_link.h +++ b/Telegram/SourceFiles/ui/boxes/edit_invite_link.h @@ -15,6 +15,8 @@ struct InviteLinkFields { QString link; TimeId expireDate = 0; int usageLimit = 0; + bool requestApproval = false; + bool isGroup = false; }; void EditInviteLinkBox( @@ -24,6 +26,7 @@ void EditInviteLinkBox( void CreateInviteLinkBox( not_null box, + bool isGroup, Fn done); } // namespace Ui