From 50778790fe289a45fde1d85b84f2a6ee17c000f1 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 23 May 2019 23:38:49 +0200 Subject: [PATCH] Start supporting channel discussion groups. --- Telegram/Resources/langs/lang.strings | 16 ++ Telegram/SourceFiles/boxes/boxes.style | 3 + .../boxes/peers/edit_linked_chat_box.cpp | 152 +++++++++++++ .../boxes/peers/edit_linked_chat_box.h | 38 ++++ .../boxes/peers/edit_peer_info_box.cpp | 207 +++++++++++++++--- .../boxes/peers/edit_peer_info_box.h | 4 +- .../boxes/peers/edit_peer_permissions_box.cpp | 8 +- .../boxes/peers/edit_peer_type_box.cpp | 4 +- Telegram/SourceFiles/data/data_channel.cpp | 14 ++ Telegram/SourceFiles/data/data_channel.h | 5 +- Telegram/SourceFiles/observer_peer.h | 1 + Telegram/gyp/telegram_sources.txt | 2 + 12 files changed, 410 insertions(+), 44 deletions(-) create mode 100644 Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp create mode 100644 Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.h diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 0e003b8bf..bf98830a8 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -844,6 +844,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_manage_private_peer_title" = "Private"; "lng_manage_public_peer_title" = "Public"; +"lng_manage_discussion_group" = "Discussion"; +"lng_manage_discussion_group_add" = "Add a group"; +"lng_manage_linked_channel" = "Linked channel"; +"lng_manage_linked_channel_set" = "Set"; +"lng_manage_discussion_group_about" = "Select a group chat for discussion that will be displayed in your channel."; +"lng_manage_discussion_group_about_chosen" = "A link to {group} is shown to all subscribers in the bottom panel."; +"lng_manage_discussion_group_create" = "Create a new group"; +"lng_manage_discussion_group_unlink" = "Unlink group"; +"lng_manage_discussion_group_posted" = "Everything you post in the channel is forwarded to this group."; +"lng_manage_discussion_group_sure" = "Do you want to make {group} the discussion board for {channel}?"; +"lng_manage_linked_channel_private" = "Any member in this group will be able to see the messages in the channel."; +"lng_manage_discussion_group_private" = "private group"; +"lng_manage_linked_channel_about" = "{channel} is linking the group as its discussion board."; +"lng_manage_linked_channel_unlink" = "Unlink channel"; +"lng_manage_linked_channel_posted" = "All new messages posted in this channel are forwarded to the group."; + "lng_manage_history_visibility_title" = "Chat history for new members"; "lng_manage_history_visibility_shown" = "Visible"; "lng_manage_history_visibility_shown_about" = "New members will see messages that were sent before they joined."; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 7595d61c3..d5c6c8800 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -974,3 +974,6 @@ backgroundCheck: ServiceCheck { urlAuthCheckbox: Checkbox(defaultBoxCheckbox) { width: 240px; } + +linkedChatAbout: membersAbout; +linkedChatAboutPadding: margins(12px, 20px, 12px, 20px); \ No newline at end of file diff --git a/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp new file mode 100644 index 000000000..dec9f3049 --- /dev/null +++ b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp @@ -0,0 +1,152 @@ +/* +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 "boxes/peers/edit_linked_chat_box.h" + +#include "lang/lang_keys.h" +#include "data/data_channel.h" +#include "ui/widgets/labels.h" +#include "ui/wrap/vertical_layout.h" +#include "boxes/peer_list_box.h" +#include "styles/style_boxes.h" + +namespace { + +TextWithEntities BoldText(const QString &text) { + auto result = TextWithEntities{ text }; + result.entities.push_back( + EntityInText(EntityType::Bold, 0, text.size())); + return result; +} + +} // namespace + +EditLinkedChatBox::EditLinkedChatBox( + QWidget*, + not_null channel, + const std::vector> &chats, + Fn callback) +: _channel(channel) +, _content(setupContent(channel, nullptr, chats, callback)) { +} + +EditLinkedChatBox::EditLinkedChatBox( + QWidget*, + not_null channel, + not_null chat, + Fn callback) +: _channel(channel) +, _content(setupContent(channel, chat, {}, callback)) { +} + +object_ptr EditLinkedChatBox::setupContent( + not_null channel, + ChannelData *chat, + const std::vector> &chats, + Fn callback) { + Expects(channel->isBroadcast() || (chat != nullptr)); + + auto result = object_ptr(this); + const auto about = result->add( + object_ptr( + result, + QString(), + Ui::FlatLabel::InitType::Simple, + st::linkedChatAbout), + st::linkedChatAboutPadding); + about->setMarkedText([&]() -> TextWithEntities { + if (!channel->isBroadcast()) { + return lng_manage_linked_channel_about__generic< + TextWithEntities + >(lt_channel, BoldText(chat->name)); + } else if (chat != nullptr) { + return lng_manage_discussion_group_about_chosen__generic< + TextWithEntities + >(lt_group, BoldText(chat->name)); + } else { + return { lang(lng_manage_discussion_group_about) }; + } + }()); + + class ListController + : public PeerListController + , public PeerListContentDelegate { + public: + ListController(Fn)> callback) + : _callback(std::move(callback)) { + } + void prepare() override { + } + void rowClicked(not_null row) override { + _callback(row->peer()->asChannel()); + } + void peerListSetTitle(Fn title) override { + } + void peerListSetAdditionalTitle(Fn title) override { + } + bool peerListIsRowSelected(not_null peer) override { + return false; + } + int peerListSelectedRowsCount() override { + return 0; + } + auto peerListCollectSelectedRows() + -> std::vector> override { + return {}; + } + void peerListScrollToTop() override { + } + void peerListAddSelectedRowInBunch( + not_null peer) override { + } + void peerListFinishSelectedRowsBunch() override { + } + void peerListSetDescription( + object_ptr description) override { + } + + private: + Fn)> _callback; + + }; + const auto controller = Ui::CreateChild(this, [=](not_null chat) { + const auto onstack = callback; + onstack(chat); + }); + controller->setDelegate(controller); + const auto list = result->add(object_ptr( + this, + controller, + st::peerListBox)); + const auto createRow = [](not_null chat) { + auto result = std::make_unique(chat); + result->setCustomStatus(chat->isPublic() + ? ('@' + chat->username) + : lang(lng_manage_discussion_group_private)); + return result; + }; + if (chat) { + list->appendRow(createRow(chat)); + } else { + for (const auto chat : chats) { + list->appendRow(createRow(chat)); + } + } + return result; +} + +void EditLinkedChatBox::prepare() { + setTitle(langFactory(_channel->isBroadcast() + ? lng_manage_discussion_group + : lng_manage_linked_channel)); + + setDimensionsToContent( + st::boxWidth, + setInnerWidget(std::move(_content))); + + addButton(langFactory(lng_close), [=] { closeBox(); }); +} diff --git a/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.h b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.h new file mode 100644 index 000000000..f1f0b6a1a --- /dev/null +++ b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.h @@ -0,0 +1,38 @@ +/* +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 +*/ +#pragma once + +#include "boxes/abstract_box.h" + +class EditLinkedChatBox : public BoxContent { +public: + EditLinkedChatBox( + QWidget*, + not_null channel, + not_null chat, + Fn callback); + EditLinkedChatBox( + QWidget*, + not_null channel, + const std::vector> &chats, + Fn callback); + +protected: + void prepare() override; + +private: + object_ptr setupContent( + not_null channel, + ChannelData *chat, + const std::vector> &chats, + Fn callback); + + not_null _channel; + object_ptr _content; + +}; \ No newline at end of file diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index 32491c39a..ec1f026ad 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -16,11 +16,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peers/edit_peer_type_box.h" #include "boxes/peers/edit_peer_history_visibility_box.h" #include "boxes/peers/edit_peer_permissions_box.h" +#include "boxes/peers/edit_linked_chat_box.h" #include "boxes/stickers_box.h" #include "chat_helpers/emoji_suggestions_widget.h" #include "data/data_channel.h" #include "data/data_chat.h" #include "data/data_peer.h" +#include "data/data_session.h" #include "history/admin_log/history_admin_log_section.h" #include "info/profile/info_profile_button.h" #include "info/profile/info_profile_values.h" @@ -80,17 +82,17 @@ void AddButtonWithCount( rpl::producer &&count, Fn callback, const style::icon &icon) { - EditPeerInfoBox::CreateButton( + parent->add(EditPeerInfoBox::CreateButton( parent, std::move(text), std::move(count), std::move(callback), st::manageGroupButton, - &icon); + &icon)); } -Info::Profile::Button *AddButtonWithText( - not_null parent, +object_ptr CreateButtonWithText( + not_null parent, rpl::producer &&text, rpl::producer &&label, Fn callback) { @@ -103,17 +105,29 @@ Info::Profile::Button *AddButtonWithText( nullptr); } +Info::Profile::Button *AddButtonWithText( + not_null parent, + rpl::producer &&text, + rpl::producer &&label, + Fn callback) { + return parent->add(CreateButtonWithText( + parent, + std::move(text), + std::move(label), + std::move(callback))); +} + void AddButtonDelete( not_null parent, rpl::producer &&text, Fn callback) { - EditPeerInfoBox::CreateButton( + parent->add(EditPeerInfoBox::CreateButton( parent, std::move(text), rpl::single(QString()), std::move(callback), st::manageDeleteGroupButton, - nullptr); + nullptr)); } void ShowEditPermissions(not_null peer) { @@ -167,6 +181,7 @@ private: std::optional description; std::optional hiddenPreHistory; std::optional signatures; + std::optional linkedChat; }; object_ptr createPhotoAndTitleEdit(); @@ -179,7 +194,9 @@ private: bool canEditInformation() const; void refreshHistoryVisibility(bool instant); void showEditPeerTypeBox(std::optional error = std::nullopt); + void showEditLinkedChatBox(); void fillPrivacyTypeButton(); + void fillLinkedChatButton(); void fillInviteLinkButton(); void fillSignaturesButton(); void fillHistoryVisibilityButton(); @@ -192,6 +209,7 @@ private: std::optional validate() const; bool validateUsername(Saving &to) const; + bool validateLinkedChat(Saving &to) const; bool validateTitle(Saving &to) const; bool validateDescription(Saving &to) const; bool validateHistoryVisibility(Saving &to) const; @@ -199,6 +217,7 @@ private: void save(); void saveUsername(); + void saveLinkedChat(); void saveTitle(); void saveDescription(); void saveHistoryVisibility(); @@ -211,10 +230,11 @@ private: void subscribeToMigration(); void migrate(not_null channel); - std::optional _privacySavedValue = {}; - std::optional _historyVisibilitySavedValue = {}; - std::optional _usernameSavedValue = {}; - std::optional _signaturesSavedValue = {}; + std::optional _privacySavedValue; + std::optional _linkedChatSavedValue; + std::optional _historyVisibilitySavedValue; + std::optional _usernameSavedValue; + std::optional _signaturesSavedValue; not_null _box; not_null _peer; @@ -226,7 +246,10 @@ private: std::deque> _saveStagesQueue; Saving _savingData; - const rpl::event_stream _updadePrivacyType; + const rpl::event_stream _privacyTypeUpdates; + const rpl::event_stream _linkedChatUpdates; + MTP::Sender _linkedChatsRequester; + mtpRequestId _linkedChatsRequestId = 0; rpl::lifetime _lifetime; @@ -465,7 +488,7 @@ void Controller::refreshHistoryVisibility(bool instant = false) { void Controller::showEditPeerTypeBox(std::optional error) { const auto boxCallback = crl::guard(this, [=]( Privacy checked, QString publicLink) { - _updadePrivacyType.fire(std::move(checked)); + _privacyTypeUpdates.fire(std::move(checked)); _privacySavedValue = checked; _usernameSavedValue = publicLink; refreshHistoryVisibility(); @@ -480,6 +503,49 @@ void Controller::showEditPeerTypeBox(std::optional error) { LayerOption::KeepOther); } +void Controller::showEditLinkedChatBox() { + Expects(_peer->isChannel()); + + const auto box = std::make_shared>(); + const auto channel = _peer->asChannel(); + const auto callback = [=](ChannelData *result) { + if (*box) { + (*box)->closeBox(); + } + *_linkedChatSavedValue = result; + _linkedChatUpdates.fire_copy(result); + }; + if (const auto chat = *_linkedChatSavedValue) { + *box = Ui::show( + Box(channel, chat, callback), + LayerOption::KeepOther); + return; + } else if (_linkedChatsRequestId) { + return; + } + _linkedChatsRequestId = _linkedChatsRequester.request( + MTPchannels_GetGroupsForDiscussion() + ).done([=](const MTPmessages_Chats &result) { + _linkedChatsRequestId = 0; + const auto list = result.match([&](const auto &data) { + return data.vchats.v; + }); + auto chats = std::vector>(); + chats.reserve(list.size()); + for (const auto &item : list) { + const auto chat = _peer->owner().processChat(item); + if (chat->isChannel()) { + chats.emplace_back(chat->asChannel()); + } + } + *box = Ui::show( + Box(channel, chats, callback), + LayerOption::KeepOther); + }).fail([=](const RPCError &error) { + _linkedChatsRequestId = 0; + }).send(); +} + void Controller::fillPrivacyTypeButton() { Expects(_controls.buttonsLayout != nullptr); @@ -495,7 +561,7 @@ void Controller::fillPrivacyTypeButton() { Lang::Viewer(isGroup ? lng_manage_peer_group_type : lng_manage_peer_channel_type), - _updadePrivacyType.events( + _privacyTypeUpdates.events( ) | rpl::map([=](Privacy flag) { return lang(Privacy::Public == flag ? (isGroup @@ -507,7 +573,33 @@ void Controller::fillPrivacyTypeButton() { }), [=] { showEditPeerTypeBox(); }); - _updadePrivacyType.fire_copy(*_privacySavedValue); + _privacyTypeUpdates.fire_copy(*_privacySavedValue); +} + +void Controller::fillLinkedChatButton() { + Expects(_controls.buttonsLayout != nullptr); + + _linkedChatSavedValue = _peer->isChannel() + ? _peer->asChannel()->linkedChat() + : nullptr; + + const auto isGroup = (_peer->isChat() || _peer->isMegagroup()); + AddButtonWithText( + _controls.buttonsLayout, + Lang::Viewer(isGroup + ? lng_manage_linked_channel + : lng_manage_discussion_group), + _linkedChatUpdates.events( + ) | rpl::map([=](ChannelData *channel) { + return channel + ? channel->name + : isGroup + ? lang(lng_manage_linked_channel_set) + : lang(lng_manage_discussion_group_add); + }), + [=] { showEditLinkedChatBox(); }); + + _linkedChatUpdates.fire_copy(*_linkedChatSavedValue); } void Controller::fillInviteLinkButton() { @@ -602,75 +694,85 @@ void Controller::fillManageSection() { const auto isChannel = (!chat); if (!chat && !channel) return; - const auto canEditUsername = [=] { + const auto canEditUsername = [&] { return isChannel ? channel->canEditUsername() : chat->canEditUsername(); }(); - const auto canEditInviteLink = [=] { + const auto canEditInviteLink = [&] { return isChannel ? (channel->amCreator() || (channel->adminRights() & ChatAdminRight::f_invite_users)) : (chat->amCreator() || (chat->adminRights() & ChatAdminRight::f_invite_users)); }(); - const auto canEditSignatures = [=] { + const auto canEditSignatures = [&] { return isChannel ? (channel->canEditSignatures() && !channel->isMegagroup()) : false; }(); - const auto canEditPreHistoryHidden = [=] { + const auto canEditPreHistoryHidden = [&] { return isChannel ? channel->canEditPreHistoryHidden() : chat->canEditPreHistoryHidden(); }(); - const auto canEditPermissions = [=] { + const auto canEditPermissions = [&] { return isChannel ? channel->canEditPermissions() : chat->canEditPermissions(); }(); - const auto canViewAdmins = [=] { + const auto canViewAdmins = [&] { return isChannel ? channel->canViewAdmins() : chat->amIn(); }(); - const auto canViewMembers = [=] { + const auto canViewMembers = [&] { return isChannel ? channel->canViewMembers() : chat->amIn(); }(); - const auto canViewKicked = [=] { + const auto canViewKicked = [&] { return isChannel ? (!channel->isMegagroup()) : false; }(); - const auto hasRecentActions = [=] { + const auto hasRecentActions = [&] { return isChannel ? (channel->hasAdminRights() || channel->amCreator()) : false; }(); - const auto canEditStickers = [=] { + const auto canEditStickers = [&] { // return true; return isChannel ? channel->canEditStickers() : false; }(); - const auto canDeleteChannel = [=] { + const auto canDeleteChannel = [&] { return isChannel ? channel->canDelete() : false; }(); + const auto canEditLinkedChat = [&] { + return !isChannel + ? false + : channel->isBroadcast() + ? channel->canEditInformation() + : (channel->linkedChat() && channel->canPinMessages()); + }(); + AddSkip(_controls.buttonsLayout, 0); if (canEditUsername) { fillPrivacyTypeButton(); - } - else if (canEditInviteLink) { + } else if (canEditInviteLink) { fillInviteLinkButton(); } + if (canEditLinkedChat) { + fillLinkedChatButton(); + } if (canEditSignatures) { fillSignaturesButton(); } @@ -796,6 +898,7 @@ void Controller::submitDescription() { std::optional Controller::validate() const { auto result = Saving(); if (validateUsername(result) + && validateLinkedChat(result) && validateTitle(result) && validateDescription(result) && validateHistoryVisibility(result) @@ -824,6 +927,14 @@ bool Controller::validateUsername(Saving &to) const { return true; } +bool Controller::validateLinkedChat(Saving &to) const { + if (!_linkedChatSavedValue) { + return true; + } + to.linkedChat = *_linkedChatSavedValue; + return true; +} + bool Controller::validateTitle(Saving &to) const { if (!_controls.title) { return true; @@ -874,6 +985,7 @@ void Controller::save() { if (const auto saving = validate()) { _savingData = *saving; pushSaveStage([=] { saveUsername(); }); + pushSaveStage([=] { saveLinkedChat(); }); pushSaveStage([=] { saveTitle(); }); pushSaveStage([=] { saveDescription(); }); pushSaveStage([=] { saveHistoryVisibility(); }); @@ -950,6 +1062,31 @@ void Controller::saveUsername() { }).send(); } +void Controller::saveLinkedChat() { + const auto channel = _peer->asChannel(); + if (!channel) { + return continueSave(); + } + const auto linkedChat = channel->linkedChat(); + if (!_savingData.linkedChat || *_savingData.linkedChat == linkedChat) { + return continueSave(); + } + + const auto input = *_savingData.linkedChat + ? (*_savingData.linkedChat)->inputChannel + : MTP_inputChannelEmpty(); + request(MTPchannels_SetDiscussionGroup( + (channel->isBroadcast() ? channel->inputChannel : input), + (channel->isBroadcast() ? input : channel->inputChannel) + )).done([=](const MTPBool &result) { + channel->setLinkedChat(*_savingData.linkedChat); + continueSave(); + }).fail([=](const RPCError &error) { + const auto &type = error.type(); + cancelSave(); + }).send(); +} + void Controller::saveTitle() { if (!_savingData.title || *_savingData.title == _peer->name) { return continueSave(); @@ -1151,18 +1288,18 @@ void EditPeerInfoBox::prepare() { std::move(content))); } -Info::Profile::Button *EditPeerInfoBox::CreateButton( - not_null parent, +object_ptr EditPeerInfoBox::CreateButton( + not_null parent, rpl::producer &&text, rpl::producer &&count, Fn callback, const style::InfoProfileCountButton &st, const style::icon *icon) { - const auto button = parent->add( - object_ptr( - parent, - std::move(text), - st.button)); + auto result = object_ptr( + parent, + std::move(text), + st.button); + const auto button = result.data(); button->addClickHandler(callback); if (icon) { Ui::CreateChild( @@ -1186,7 +1323,7 @@ Info::Profile::Button *EditPeerInfoBox::CreateButton( outerWidth); }, label->lifetime()); - return button; + return result; } bool EditPeerInfoBox::Available(not_null peer) { diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h index 4cce8810a..c2a1e2ac1 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h @@ -34,8 +34,8 @@ public: static bool Available(not_null peer); - static Info::Profile::Button *CreateButton( - not_null parent, + [[nodiscard]] static object_ptr CreateButton( + not_null parent, rpl::producer &&text, rpl::producer &&count, Fn callback, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp index 0242b1b03..0d889dbf2 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp @@ -357,7 +357,7 @@ void EditPeerPermissionsBox::addBannedButtons( { 0, st::infoProfileSkip, 0, st::infoProfileSkip }); const auto navigation = App::wnd()->controller(); - EditPeerInfoBox::CreateButton( + container->add(EditPeerInfoBox::CreateButton( container, Lang::Viewer(lng_manage_peer_exceptions), (channel @@ -369,9 +369,9 @@ void EditPeerPermissionsBox::addBannedButtons( _peer, ParticipantsBoxController::Role::Restricted); }, - st::peerPermissionsButton); + st::peerPermissionsButton)); if (channel) { - EditPeerInfoBox::CreateButton( + container->add(EditPeerInfoBox::CreateButton( container, Lang::Viewer(lng_manage_peer_removed_users), Info::Profile::KickedCountValue(channel) @@ -382,7 +382,7 @@ void EditPeerPermissionsBox::addBannedButtons( _peer, ParticipantsBoxController::Role::Kicked); }, - st::peerPermissionsButton); + st::peerPermissionsButton)); } } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index 60c61bc14..cf6ac57c2 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -138,8 +138,8 @@ private: QString inviteLinkText(); not_null _peer; - std::optional _privacySavedValue = std::nullopt; - std::optional _usernameSavedValue = std::nullopt; + std::optional _privacySavedValue; + std::optional _usernameSavedValue; bool _isGroup = false; bool _isInviteLink = false; diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 20f7a471d..ca1ba4f83 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -92,6 +92,17 @@ bool ChannelData::canHaveInviteLink() const { || amCreator(); } +void ChannelData::setLinkedChat(ChannelData *linked) { + if (_linkedChat != linked) { + _linkedChat = linked; + Notify::peerUpdatedDelayed(this, UpdateFlag::ChannelLinkedChat); + } +} + +ChannelData *ChannelData::linkedChat() const { + return _linkedChat; +} + void ChannelData::setMembersCount(int newMembersCount) { if (_membersCount != newMembersCount) { if (isMegagroup() && !mgInfo->lastParticipants.empty()) { @@ -594,6 +605,9 @@ void ApplyChannelUpdate( }, [&](const MTPDchatInviteEmpty &) { return QString(); })); + channel->setLinkedChat(update.has_linked_chat_id() + ? channel->owner().channelLoaded(update.vlinked_chat_id.v) + : nullptr); if (const auto history = channel->owner().historyLoaded(channel)) { history->clearUpTill(update.vavailable_min_id.v); diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index 5ba5dcf7c..9d6d9d9ea 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -279,6 +279,9 @@ public: QString inviteLink() const; bool canHaveInviteLink() const; + void setLinkedChat(ChannelData *linked); + ChannelData *linkedChat() const; + void ptsInit(int32 pts) { _ptsWaiter.init(pts); } @@ -380,8 +383,8 @@ private: TimeId _restrictedUntil; QString _unavailableReason; - QString _inviteLink; + ChannelData *_linkedChat = nullptr; rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/observer_peer.h b/Telegram/SourceFiles/observer_peer.h index 181c27209..c47509de4 100644 --- a/Telegram/SourceFiles/observer_peer.h +++ b/Telegram/SourceFiles/observer_peer.h @@ -65,6 +65,7 @@ struct PeerUpdate { ChannelAmIn = (1 << 17), ChannelStickersChanged = (1 << 18), ChannelPromotedChanged = (1 << 19), + ChannelLinkedChat = (1 << 20), }; using Flags = base::flags; friend inline constexpr auto is_flag_type(Flag) { return true; } diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 5e9e0e79f..df4d7e910 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -1,5 +1,7 @@ <(src_loc)/boxes/peers/add_participants_box.cpp <(src_loc)/boxes/peers/add_participants_box.h +<(src_loc)/boxes/peers/edit_linked_chat_box.cpp +<(src_loc)/boxes/peers/edit_linked_chat_box.h <(src_loc)/boxes/peers/edit_participant_box.cpp <(src_loc)/boxes/peers/edit_participant_box.h <(src_loc)/boxes/peers/edit_participants_box.cpp