mirror of https://github.com/procxx/kepka.git
Start supporting channel discussion groups.
This commit is contained in:
parent
b05bf049bc
commit
50778790fe
|
@ -844,6 +844,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_manage_private_peer_title" = "Private";
|
"lng_manage_private_peer_title" = "Private";
|
||||||
"lng_manage_public_peer_title" = "Public";
|
"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_title" = "Chat history for new members";
|
||||||
"lng_manage_history_visibility_shown" = "Visible";
|
"lng_manage_history_visibility_shown" = "Visible";
|
||||||
"lng_manage_history_visibility_shown_about" = "New members will see messages that were sent before they joined.";
|
"lng_manage_history_visibility_shown_about" = "New members will see messages that were sent before they joined.";
|
||||||
|
|
|
@ -974,3 +974,6 @@ backgroundCheck: ServiceCheck {
|
||||||
urlAuthCheckbox: Checkbox(defaultBoxCheckbox) {
|
urlAuthCheckbox: Checkbox(defaultBoxCheckbox) {
|
||||||
width: 240px;
|
width: 240px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
linkedChatAbout: membersAbout;
|
||||||
|
linkedChatAboutPadding: margins(12px, 20px, 12px, 20px);
|
|
@ -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<ChannelData*> channel,
|
||||||
|
const std::vector<not_null<ChannelData*>> &chats,
|
||||||
|
Fn<void(ChannelData*)> callback)
|
||||||
|
: _channel(channel)
|
||||||
|
, _content(setupContent(channel, nullptr, chats, callback)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
EditLinkedChatBox::EditLinkedChatBox(
|
||||||
|
QWidget*,
|
||||||
|
not_null<ChannelData*> channel,
|
||||||
|
not_null<ChannelData*> chat,
|
||||||
|
Fn<void(ChannelData*)> callback)
|
||||||
|
: _channel(channel)
|
||||||
|
, _content(setupContent(channel, chat, {}, callback)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
object_ptr<Ui::RpWidget> EditLinkedChatBox::setupContent(
|
||||||
|
not_null<ChannelData*> channel,
|
||||||
|
ChannelData *chat,
|
||||||
|
const std::vector<not_null<ChannelData*>> &chats,
|
||||||
|
Fn<void(ChannelData*)> callback) {
|
||||||
|
Expects(channel->isBroadcast() || (chat != nullptr));
|
||||||
|
|
||||||
|
auto result = object_ptr<Ui::VerticalLayout>(this);
|
||||||
|
const auto about = result->add(
|
||||||
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
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<void(not_null<ChannelData*>)> callback)
|
||||||
|
: _callback(std::move(callback)) {
|
||||||
|
}
|
||||||
|
void prepare() override {
|
||||||
|
}
|
||||||
|
void rowClicked(not_null<PeerListRow*> row) override {
|
||||||
|
_callback(row->peer()->asChannel());
|
||||||
|
}
|
||||||
|
void peerListSetTitle(Fn<QString()> title) override {
|
||||||
|
}
|
||||||
|
void peerListSetAdditionalTitle(Fn<QString()> title) override {
|
||||||
|
}
|
||||||
|
bool peerListIsRowSelected(not_null<PeerData*> peer) override {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int peerListSelectedRowsCount() override {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
auto peerListCollectSelectedRows()
|
||||||
|
-> std::vector<not_null<PeerData*>> override {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
void peerListScrollToTop() override {
|
||||||
|
}
|
||||||
|
void peerListAddSelectedRowInBunch(
|
||||||
|
not_null<PeerData*> peer) override {
|
||||||
|
}
|
||||||
|
void peerListFinishSelectedRowsBunch() override {
|
||||||
|
}
|
||||||
|
void peerListSetDescription(
|
||||||
|
object_ptr<Ui::FlatLabel> description) override {
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Fn<void(not_null<ChannelData*>)> _callback;
|
||||||
|
|
||||||
|
};
|
||||||
|
const auto controller = Ui::CreateChild<ListController>(this, [=](not_null<ChannelData*> chat) {
|
||||||
|
const auto onstack = callback;
|
||||||
|
onstack(chat);
|
||||||
|
});
|
||||||
|
controller->setDelegate(controller);
|
||||||
|
const auto list = result->add(object_ptr<PeerListContent>(
|
||||||
|
this,
|
||||||
|
controller,
|
||||||
|
st::peerListBox));
|
||||||
|
const auto createRow = [](not_null<ChannelData*> chat) {
|
||||||
|
auto result = std::make_unique<PeerListRow>(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(); });
|
||||||
|
}
|
|
@ -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<ChannelData*> channel,
|
||||||
|
not_null<ChannelData*> chat,
|
||||||
|
Fn<void(ChannelData*)> callback);
|
||||||
|
EditLinkedChatBox(
|
||||||
|
QWidget*,
|
||||||
|
not_null<ChannelData*> channel,
|
||||||
|
const std::vector<not_null<ChannelData*>> &chats,
|
||||||
|
Fn<void(ChannelData*)> callback);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void prepare() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
object_ptr<Ui::RpWidget> setupContent(
|
||||||
|
not_null<ChannelData*> channel,
|
||||||
|
ChannelData *chat,
|
||||||
|
const std::vector<not_null<ChannelData*>> &chats,
|
||||||
|
Fn<void(ChannelData*)> callback);
|
||||||
|
|
||||||
|
not_null<ChannelData*> _channel;
|
||||||
|
object_ptr<Ui::RpWidget> _content;
|
||||||
|
|
||||||
|
};
|
|
@ -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_type_box.h"
|
||||||
#include "boxes/peers/edit_peer_history_visibility_box.h"
|
#include "boxes/peers/edit_peer_history_visibility_box.h"
|
||||||
#include "boxes/peers/edit_peer_permissions_box.h"
|
#include "boxes/peers/edit_peer_permissions_box.h"
|
||||||
|
#include "boxes/peers/edit_linked_chat_box.h"
|
||||||
#include "boxes/stickers_box.h"
|
#include "boxes/stickers_box.h"
|
||||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
#include "data/data_chat.h"
|
#include "data/data_chat.h"
|
||||||
#include "data/data_peer.h"
|
#include "data/data_peer.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
#include "history/admin_log/history_admin_log_section.h"
|
#include "history/admin_log/history_admin_log_section.h"
|
||||||
#include "info/profile/info_profile_button.h"
|
#include "info/profile/info_profile_button.h"
|
||||||
#include "info/profile/info_profile_values.h"
|
#include "info/profile/info_profile_values.h"
|
||||||
|
@ -80,17 +82,17 @@ void AddButtonWithCount(
|
||||||
rpl::producer<QString> &&count,
|
rpl::producer<QString> &&count,
|
||||||
Fn<void()> callback,
|
Fn<void()> callback,
|
||||||
const style::icon &icon) {
|
const style::icon &icon) {
|
||||||
EditPeerInfoBox::CreateButton(
|
parent->add(EditPeerInfoBox::CreateButton(
|
||||||
parent,
|
parent,
|
||||||
std::move(text),
|
std::move(text),
|
||||||
std::move(count),
|
std::move(count),
|
||||||
std::move(callback),
|
std::move(callback),
|
||||||
st::manageGroupButton,
|
st::manageGroupButton,
|
||||||
&icon);
|
&icon));
|
||||||
}
|
}
|
||||||
|
|
||||||
Info::Profile::Button *AddButtonWithText(
|
object_ptr<Info::Profile::Button> CreateButtonWithText(
|
||||||
not_null<Ui::VerticalLayout*> parent,
|
not_null<QWidget*> parent,
|
||||||
rpl::producer<QString> &&text,
|
rpl::producer<QString> &&text,
|
||||||
rpl::producer<QString> &&label,
|
rpl::producer<QString> &&label,
|
||||||
Fn<void()> callback) {
|
Fn<void()> callback) {
|
||||||
|
@ -103,17 +105,29 @@ Info::Profile::Button *AddButtonWithText(
|
||||||
nullptr);
|
nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Info::Profile::Button *AddButtonWithText(
|
||||||
|
not_null<Ui::VerticalLayout*> parent,
|
||||||
|
rpl::producer<QString> &&text,
|
||||||
|
rpl::producer<QString> &&label,
|
||||||
|
Fn<void()> callback) {
|
||||||
|
return parent->add(CreateButtonWithText(
|
||||||
|
parent,
|
||||||
|
std::move(text),
|
||||||
|
std::move(label),
|
||||||
|
std::move(callback)));
|
||||||
|
}
|
||||||
|
|
||||||
void AddButtonDelete(
|
void AddButtonDelete(
|
||||||
not_null<Ui::VerticalLayout*> parent,
|
not_null<Ui::VerticalLayout*> parent,
|
||||||
rpl::producer<QString> &&text,
|
rpl::producer<QString> &&text,
|
||||||
Fn<void()> callback) {
|
Fn<void()> callback) {
|
||||||
EditPeerInfoBox::CreateButton(
|
parent->add(EditPeerInfoBox::CreateButton(
|
||||||
parent,
|
parent,
|
||||||
std::move(text),
|
std::move(text),
|
||||||
rpl::single(QString()),
|
rpl::single(QString()),
|
||||||
std::move(callback),
|
std::move(callback),
|
||||||
st::manageDeleteGroupButton,
|
st::manageDeleteGroupButton,
|
||||||
nullptr);
|
nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShowEditPermissions(not_null<PeerData*> peer) {
|
void ShowEditPermissions(not_null<PeerData*> peer) {
|
||||||
|
@ -167,6 +181,7 @@ private:
|
||||||
std::optional<QString> description;
|
std::optional<QString> description;
|
||||||
std::optional<bool> hiddenPreHistory;
|
std::optional<bool> hiddenPreHistory;
|
||||||
std::optional<bool> signatures;
|
std::optional<bool> signatures;
|
||||||
|
std::optional<ChannelData*> linkedChat;
|
||||||
};
|
};
|
||||||
|
|
||||||
object_ptr<Ui::RpWidget> createPhotoAndTitleEdit();
|
object_ptr<Ui::RpWidget> createPhotoAndTitleEdit();
|
||||||
|
@ -179,7 +194,9 @@ private:
|
||||||
bool canEditInformation() const;
|
bool canEditInformation() const;
|
||||||
void refreshHistoryVisibility(bool instant);
|
void refreshHistoryVisibility(bool instant);
|
||||||
void showEditPeerTypeBox(std::optional<LangKey> error = std::nullopt);
|
void showEditPeerTypeBox(std::optional<LangKey> error = std::nullopt);
|
||||||
|
void showEditLinkedChatBox();
|
||||||
void fillPrivacyTypeButton();
|
void fillPrivacyTypeButton();
|
||||||
|
void fillLinkedChatButton();
|
||||||
void fillInviteLinkButton();
|
void fillInviteLinkButton();
|
||||||
void fillSignaturesButton();
|
void fillSignaturesButton();
|
||||||
void fillHistoryVisibilityButton();
|
void fillHistoryVisibilityButton();
|
||||||
|
@ -192,6 +209,7 @@ private:
|
||||||
|
|
||||||
std::optional<Saving> validate() const;
|
std::optional<Saving> validate() const;
|
||||||
bool validateUsername(Saving &to) const;
|
bool validateUsername(Saving &to) const;
|
||||||
|
bool validateLinkedChat(Saving &to) const;
|
||||||
bool validateTitle(Saving &to) const;
|
bool validateTitle(Saving &to) const;
|
||||||
bool validateDescription(Saving &to) const;
|
bool validateDescription(Saving &to) const;
|
||||||
bool validateHistoryVisibility(Saving &to) const;
|
bool validateHistoryVisibility(Saving &to) const;
|
||||||
|
@ -199,6 +217,7 @@ private:
|
||||||
|
|
||||||
void save();
|
void save();
|
||||||
void saveUsername();
|
void saveUsername();
|
||||||
|
void saveLinkedChat();
|
||||||
void saveTitle();
|
void saveTitle();
|
||||||
void saveDescription();
|
void saveDescription();
|
||||||
void saveHistoryVisibility();
|
void saveHistoryVisibility();
|
||||||
|
@ -211,10 +230,11 @@ private:
|
||||||
void subscribeToMigration();
|
void subscribeToMigration();
|
||||||
void migrate(not_null<ChannelData*> channel);
|
void migrate(not_null<ChannelData*> channel);
|
||||||
|
|
||||||
std::optional<Privacy> _privacySavedValue = {};
|
std::optional<Privacy> _privacySavedValue;
|
||||||
std::optional<HistoryVisibility> _historyVisibilitySavedValue = {};
|
std::optional<ChannelData*> _linkedChatSavedValue;
|
||||||
std::optional<QString> _usernameSavedValue = {};
|
std::optional<HistoryVisibility> _historyVisibilitySavedValue;
|
||||||
std::optional<bool> _signaturesSavedValue = {};
|
std::optional<QString> _usernameSavedValue;
|
||||||
|
std::optional<bool> _signaturesSavedValue;
|
||||||
|
|
||||||
not_null<BoxContent*> _box;
|
not_null<BoxContent*> _box;
|
||||||
not_null<PeerData*> _peer;
|
not_null<PeerData*> _peer;
|
||||||
|
@ -226,7 +246,10 @@ private:
|
||||||
std::deque<FnMut<void()>> _saveStagesQueue;
|
std::deque<FnMut<void()>> _saveStagesQueue;
|
||||||
Saving _savingData;
|
Saving _savingData;
|
||||||
|
|
||||||
const rpl::event_stream<Privacy> _updadePrivacyType;
|
const rpl::event_stream<Privacy> _privacyTypeUpdates;
|
||||||
|
const rpl::event_stream<ChannelData*> _linkedChatUpdates;
|
||||||
|
MTP::Sender _linkedChatsRequester;
|
||||||
|
mtpRequestId _linkedChatsRequestId = 0;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
@ -465,7 +488,7 @@ void Controller::refreshHistoryVisibility(bool instant = false) {
|
||||||
void Controller::showEditPeerTypeBox(std::optional<LangKey> error) {
|
void Controller::showEditPeerTypeBox(std::optional<LangKey> error) {
|
||||||
const auto boxCallback = crl::guard(this, [=](
|
const auto boxCallback = crl::guard(this, [=](
|
||||||
Privacy checked, QString publicLink) {
|
Privacy checked, QString publicLink) {
|
||||||
_updadePrivacyType.fire(std::move(checked));
|
_privacyTypeUpdates.fire(std::move(checked));
|
||||||
_privacySavedValue = checked;
|
_privacySavedValue = checked;
|
||||||
_usernameSavedValue = publicLink;
|
_usernameSavedValue = publicLink;
|
||||||
refreshHistoryVisibility();
|
refreshHistoryVisibility();
|
||||||
|
@ -480,6 +503,49 @@ void Controller::showEditPeerTypeBox(std::optional<LangKey> error) {
|
||||||
LayerOption::KeepOther);
|
LayerOption::KeepOther);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Controller::showEditLinkedChatBox() {
|
||||||
|
Expects(_peer->isChannel());
|
||||||
|
|
||||||
|
const auto box = std::make_shared<QPointer<BoxContent>>();
|
||||||
|
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<EditLinkedChatBox>(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<not_null<ChannelData*>>();
|
||||||
|
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<EditLinkedChatBox>(channel, chats, callback),
|
||||||
|
LayerOption::KeepOther);
|
||||||
|
}).fail([=](const RPCError &error) {
|
||||||
|
_linkedChatsRequestId = 0;
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
void Controller::fillPrivacyTypeButton() {
|
void Controller::fillPrivacyTypeButton() {
|
||||||
Expects(_controls.buttonsLayout != nullptr);
|
Expects(_controls.buttonsLayout != nullptr);
|
||||||
|
|
||||||
|
@ -495,7 +561,7 @@ void Controller::fillPrivacyTypeButton() {
|
||||||
Lang::Viewer(isGroup
|
Lang::Viewer(isGroup
|
||||||
? lng_manage_peer_group_type
|
? lng_manage_peer_group_type
|
||||||
: lng_manage_peer_channel_type),
|
: lng_manage_peer_channel_type),
|
||||||
_updadePrivacyType.events(
|
_privacyTypeUpdates.events(
|
||||||
) | rpl::map([=](Privacy flag) {
|
) | rpl::map([=](Privacy flag) {
|
||||||
return lang(Privacy::Public == flag
|
return lang(Privacy::Public == flag
|
||||||
? (isGroup
|
? (isGroup
|
||||||
|
@ -507,7 +573,33 @@ void Controller::fillPrivacyTypeButton() {
|
||||||
}),
|
}),
|
||||||
[=] { showEditPeerTypeBox(); });
|
[=] { 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() {
|
void Controller::fillInviteLinkButton() {
|
||||||
|
@ -602,75 +694,85 @@ void Controller::fillManageSection() {
|
||||||
const auto isChannel = (!chat);
|
const auto isChannel = (!chat);
|
||||||
if (!chat && !channel) return;
|
if (!chat && !channel) return;
|
||||||
|
|
||||||
const auto canEditUsername = [=] {
|
const auto canEditUsername = [&] {
|
||||||
return isChannel
|
return isChannel
|
||||||
? channel->canEditUsername()
|
? channel->canEditUsername()
|
||||||
: chat->canEditUsername();
|
: chat->canEditUsername();
|
||||||
}();
|
}();
|
||||||
const auto canEditInviteLink = [=] {
|
const auto canEditInviteLink = [&] {
|
||||||
return isChannel
|
return isChannel
|
||||||
? (channel->amCreator()
|
? (channel->amCreator()
|
||||||
|| (channel->adminRights() & ChatAdminRight::f_invite_users))
|
|| (channel->adminRights() & ChatAdminRight::f_invite_users))
|
||||||
: (chat->amCreator()
|
: (chat->amCreator()
|
||||||
|| (chat->adminRights() & ChatAdminRight::f_invite_users));
|
|| (chat->adminRights() & ChatAdminRight::f_invite_users));
|
||||||
}();
|
}();
|
||||||
const auto canEditSignatures = [=] {
|
const auto canEditSignatures = [&] {
|
||||||
return isChannel
|
return isChannel
|
||||||
? (channel->canEditSignatures() && !channel->isMegagroup())
|
? (channel->canEditSignatures() && !channel->isMegagroup())
|
||||||
: false;
|
: false;
|
||||||
}();
|
}();
|
||||||
const auto canEditPreHistoryHidden = [=] {
|
const auto canEditPreHistoryHidden = [&] {
|
||||||
return isChannel
|
return isChannel
|
||||||
? channel->canEditPreHistoryHidden()
|
? channel->canEditPreHistoryHidden()
|
||||||
: chat->canEditPreHistoryHidden();
|
: chat->canEditPreHistoryHidden();
|
||||||
}();
|
}();
|
||||||
|
|
||||||
const auto canEditPermissions = [=] {
|
const auto canEditPermissions = [&] {
|
||||||
return isChannel
|
return isChannel
|
||||||
? channel->canEditPermissions()
|
? channel->canEditPermissions()
|
||||||
: chat->canEditPermissions();
|
: chat->canEditPermissions();
|
||||||
}();
|
}();
|
||||||
const auto canViewAdmins = [=] {
|
const auto canViewAdmins = [&] {
|
||||||
return isChannel
|
return isChannel
|
||||||
? channel->canViewAdmins()
|
? channel->canViewAdmins()
|
||||||
: chat->amIn();
|
: chat->amIn();
|
||||||
}();
|
}();
|
||||||
const auto canViewMembers = [=] {
|
const auto canViewMembers = [&] {
|
||||||
return isChannel
|
return isChannel
|
||||||
? channel->canViewMembers()
|
? channel->canViewMembers()
|
||||||
: chat->amIn();
|
: chat->amIn();
|
||||||
}();
|
}();
|
||||||
const auto canViewKicked = [=] {
|
const auto canViewKicked = [&] {
|
||||||
return isChannel
|
return isChannel
|
||||||
? (!channel->isMegagroup())
|
? (!channel->isMegagroup())
|
||||||
: false;
|
: false;
|
||||||
}();
|
}();
|
||||||
const auto hasRecentActions = [=] {
|
const auto hasRecentActions = [&] {
|
||||||
return isChannel
|
return isChannel
|
||||||
? (channel->hasAdminRights() || channel->amCreator())
|
? (channel->hasAdminRights() || channel->amCreator())
|
||||||
: false;
|
: false;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
const auto canEditStickers = [=] {
|
const auto canEditStickers = [&] {
|
||||||
// return true;
|
// return true;
|
||||||
return isChannel
|
return isChannel
|
||||||
? channel->canEditStickers()
|
? channel->canEditStickers()
|
||||||
: false;
|
: false;
|
||||||
}();
|
}();
|
||||||
const auto canDeleteChannel = [=] {
|
const auto canDeleteChannel = [&] {
|
||||||
return isChannel
|
return isChannel
|
||||||
? channel->canDelete()
|
? channel->canDelete()
|
||||||
: false;
|
: false;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
const auto canEditLinkedChat = [&] {
|
||||||
|
return !isChannel
|
||||||
|
? false
|
||||||
|
: channel->isBroadcast()
|
||||||
|
? channel->canEditInformation()
|
||||||
|
: (channel->linkedChat() && channel->canPinMessages());
|
||||||
|
}();
|
||||||
|
|
||||||
AddSkip(_controls.buttonsLayout, 0);
|
AddSkip(_controls.buttonsLayout, 0);
|
||||||
|
|
||||||
if (canEditUsername) {
|
if (canEditUsername) {
|
||||||
fillPrivacyTypeButton();
|
fillPrivacyTypeButton();
|
||||||
}
|
} else if (canEditInviteLink) {
|
||||||
else if (canEditInviteLink) {
|
|
||||||
fillInviteLinkButton();
|
fillInviteLinkButton();
|
||||||
}
|
}
|
||||||
|
if (canEditLinkedChat) {
|
||||||
|
fillLinkedChatButton();
|
||||||
|
}
|
||||||
if (canEditSignatures) {
|
if (canEditSignatures) {
|
||||||
fillSignaturesButton();
|
fillSignaturesButton();
|
||||||
}
|
}
|
||||||
|
@ -796,6 +898,7 @@ void Controller::submitDescription() {
|
||||||
std::optional<Controller::Saving> Controller::validate() const {
|
std::optional<Controller::Saving> Controller::validate() const {
|
||||||
auto result = Saving();
|
auto result = Saving();
|
||||||
if (validateUsername(result)
|
if (validateUsername(result)
|
||||||
|
&& validateLinkedChat(result)
|
||||||
&& validateTitle(result)
|
&& validateTitle(result)
|
||||||
&& validateDescription(result)
|
&& validateDescription(result)
|
||||||
&& validateHistoryVisibility(result)
|
&& validateHistoryVisibility(result)
|
||||||
|
@ -824,6 +927,14 @@ bool Controller::validateUsername(Saving &to) const {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Controller::validateLinkedChat(Saving &to) const {
|
||||||
|
if (!_linkedChatSavedValue) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
to.linkedChat = *_linkedChatSavedValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Controller::validateTitle(Saving &to) const {
|
bool Controller::validateTitle(Saving &to) const {
|
||||||
if (!_controls.title) {
|
if (!_controls.title) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -874,6 +985,7 @@ void Controller::save() {
|
||||||
if (const auto saving = validate()) {
|
if (const auto saving = validate()) {
|
||||||
_savingData = *saving;
|
_savingData = *saving;
|
||||||
pushSaveStage([=] { saveUsername(); });
|
pushSaveStage([=] { saveUsername(); });
|
||||||
|
pushSaveStage([=] { saveLinkedChat(); });
|
||||||
pushSaveStage([=] { saveTitle(); });
|
pushSaveStage([=] { saveTitle(); });
|
||||||
pushSaveStage([=] { saveDescription(); });
|
pushSaveStage([=] { saveDescription(); });
|
||||||
pushSaveStage([=] { saveHistoryVisibility(); });
|
pushSaveStage([=] { saveHistoryVisibility(); });
|
||||||
|
@ -950,6 +1062,31 @@ void Controller::saveUsername() {
|
||||||
}).send();
|
}).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() {
|
void Controller::saveTitle() {
|
||||||
if (!_savingData.title || *_savingData.title == _peer->name) {
|
if (!_savingData.title || *_savingData.title == _peer->name) {
|
||||||
return continueSave();
|
return continueSave();
|
||||||
|
@ -1151,18 +1288,18 @@ void EditPeerInfoBox::prepare() {
|
||||||
std::move(content)));
|
std::move(content)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Info::Profile::Button *EditPeerInfoBox::CreateButton(
|
object_ptr<Info::Profile::Button> EditPeerInfoBox::CreateButton(
|
||||||
not_null<Ui::VerticalLayout*> parent,
|
not_null<QWidget*> parent,
|
||||||
rpl::producer<QString> &&text,
|
rpl::producer<QString> &&text,
|
||||||
rpl::producer<QString> &&count,
|
rpl::producer<QString> &&count,
|
||||||
Fn<void()> callback,
|
Fn<void()> callback,
|
||||||
const style::InfoProfileCountButton &st,
|
const style::InfoProfileCountButton &st,
|
||||||
const style::icon *icon) {
|
const style::icon *icon) {
|
||||||
const auto button = parent->add(
|
auto result = object_ptr<Info::Profile::Button>(
|
||||||
object_ptr<Info::Profile::Button>(
|
|
||||||
parent,
|
parent,
|
||||||
std::move(text),
|
std::move(text),
|
||||||
st.button));
|
st.button);
|
||||||
|
const auto button = result.data();
|
||||||
button->addClickHandler(callback);
|
button->addClickHandler(callback);
|
||||||
if (icon) {
|
if (icon) {
|
||||||
Ui::CreateChild<Info::Profile::FloatingIcon>(
|
Ui::CreateChild<Info::Profile::FloatingIcon>(
|
||||||
|
@ -1186,7 +1323,7 @@ Info::Profile::Button *EditPeerInfoBox::CreateButton(
|
||||||
outerWidth);
|
outerWidth);
|
||||||
}, label->lifetime());
|
}, label->lifetime());
|
||||||
|
|
||||||
return button;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EditPeerInfoBox::Available(not_null<PeerData*> peer) {
|
bool EditPeerInfoBox::Available(not_null<PeerData*> peer) {
|
||||||
|
|
|
@ -34,8 +34,8 @@ public:
|
||||||
|
|
||||||
static bool Available(not_null<PeerData*> peer);
|
static bool Available(not_null<PeerData*> peer);
|
||||||
|
|
||||||
static Info::Profile::Button *CreateButton(
|
[[nodiscard]] static object_ptr<Info::Profile::Button> CreateButton(
|
||||||
not_null<Ui::VerticalLayout*> parent,
|
not_null<QWidget*> parent,
|
||||||
rpl::producer<QString> &&text,
|
rpl::producer<QString> &&text,
|
||||||
rpl::producer<QString> &&count,
|
rpl::producer<QString> &&count,
|
||||||
Fn<void()> callback,
|
Fn<void()> callback,
|
||||||
|
|
|
@ -357,7 +357,7 @@ void EditPeerPermissionsBox::addBannedButtons(
|
||||||
{ 0, st::infoProfileSkip, 0, st::infoProfileSkip });
|
{ 0, st::infoProfileSkip, 0, st::infoProfileSkip });
|
||||||
|
|
||||||
const auto navigation = App::wnd()->controller();
|
const auto navigation = App::wnd()->controller();
|
||||||
EditPeerInfoBox::CreateButton(
|
container->add(EditPeerInfoBox::CreateButton(
|
||||||
container,
|
container,
|
||||||
Lang::Viewer(lng_manage_peer_exceptions),
|
Lang::Viewer(lng_manage_peer_exceptions),
|
||||||
(channel
|
(channel
|
||||||
|
@ -369,9 +369,9 @@ void EditPeerPermissionsBox::addBannedButtons(
|
||||||
_peer,
|
_peer,
|
||||||
ParticipantsBoxController::Role::Restricted);
|
ParticipantsBoxController::Role::Restricted);
|
||||||
},
|
},
|
||||||
st::peerPermissionsButton);
|
st::peerPermissionsButton));
|
||||||
if (channel) {
|
if (channel) {
|
||||||
EditPeerInfoBox::CreateButton(
|
container->add(EditPeerInfoBox::CreateButton(
|
||||||
container,
|
container,
|
||||||
Lang::Viewer(lng_manage_peer_removed_users),
|
Lang::Viewer(lng_manage_peer_removed_users),
|
||||||
Info::Profile::KickedCountValue(channel)
|
Info::Profile::KickedCountValue(channel)
|
||||||
|
@ -382,7 +382,7 @@ void EditPeerPermissionsBox::addBannedButtons(
|
||||||
_peer,
|
_peer,
|
||||||
ParticipantsBoxController::Role::Kicked);
|
ParticipantsBoxController::Role::Kicked);
|
||||||
},
|
},
|
||||||
st::peerPermissionsButton);
|
st::peerPermissionsButton));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -138,8 +138,8 @@ private:
|
||||||
QString inviteLinkText();
|
QString inviteLinkText();
|
||||||
|
|
||||||
not_null<PeerData*> _peer;
|
not_null<PeerData*> _peer;
|
||||||
std::optional<Privacy> _privacySavedValue = std::nullopt;
|
std::optional<Privacy> _privacySavedValue;
|
||||||
std::optional<QString> _usernameSavedValue = std::nullopt;
|
std::optional<QString> _usernameSavedValue;
|
||||||
|
|
||||||
bool _isGroup = false;
|
bool _isGroup = false;
|
||||||
bool _isInviteLink = false;
|
bool _isInviteLink = false;
|
||||||
|
|
|
@ -92,6 +92,17 @@ bool ChannelData::canHaveInviteLink() const {
|
||||||
|| amCreator();
|
|| 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) {
|
void ChannelData::setMembersCount(int newMembersCount) {
|
||||||
if (_membersCount != newMembersCount) {
|
if (_membersCount != newMembersCount) {
|
||||||
if (isMegagroup() && !mgInfo->lastParticipants.empty()) {
|
if (isMegagroup() && !mgInfo->lastParticipants.empty()) {
|
||||||
|
@ -594,6 +605,9 @@ void ApplyChannelUpdate(
|
||||||
}, [&](const MTPDchatInviteEmpty &) {
|
}, [&](const MTPDchatInviteEmpty &) {
|
||||||
return QString();
|
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)) {
|
if (const auto history = channel->owner().historyLoaded(channel)) {
|
||||||
history->clearUpTill(update.vavailable_min_id.v);
|
history->clearUpTill(update.vavailable_min_id.v);
|
||||||
|
|
||||||
|
|
|
@ -279,6 +279,9 @@ public:
|
||||||
QString inviteLink() const;
|
QString inviteLink() const;
|
||||||
bool canHaveInviteLink() const;
|
bool canHaveInviteLink() const;
|
||||||
|
|
||||||
|
void setLinkedChat(ChannelData *linked);
|
||||||
|
ChannelData *linkedChat() const;
|
||||||
|
|
||||||
void ptsInit(int32 pts) {
|
void ptsInit(int32 pts) {
|
||||||
_ptsWaiter.init(pts);
|
_ptsWaiter.init(pts);
|
||||||
}
|
}
|
||||||
|
@ -380,8 +383,8 @@ private:
|
||||||
TimeId _restrictedUntil;
|
TimeId _restrictedUntil;
|
||||||
|
|
||||||
QString _unavailableReason;
|
QString _unavailableReason;
|
||||||
|
|
||||||
QString _inviteLink;
|
QString _inviteLink;
|
||||||
|
ChannelData *_linkedChat = nullptr;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,7 @@ struct PeerUpdate {
|
||||||
ChannelAmIn = (1 << 17),
|
ChannelAmIn = (1 << 17),
|
||||||
ChannelStickersChanged = (1 << 18),
|
ChannelStickersChanged = (1 << 18),
|
||||||
ChannelPromotedChanged = (1 << 19),
|
ChannelPromotedChanged = (1 << 19),
|
||||||
|
ChannelLinkedChat = (1 << 20),
|
||||||
};
|
};
|
||||||
using Flags = base::flags<Flag>;
|
using Flags = base::flags<Flag>;
|
||||||
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<(src_loc)/boxes/peers/add_participants_box.cpp
|
<(src_loc)/boxes/peers/add_participants_box.cpp
|
||||||
<(src_loc)/boxes/peers/add_participants_box.h
|
<(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.cpp
|
||||||
<(src_loc)/boxes/peers/edit_participant_box.h
|
<(src_loc)/boxes/peers/edit_participant_box.h
|
||||||
<(src_loc)/boxes/peers/edit_participants_box.cpp
|
<(src_loc)/boxes/peers/edit_participants_box.cpp
|
||||||
|
|
Loading…
Reference in New Issue