mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 15:04:57 +00:00
402 lines
11 KiB
C++
402 lines
11 KiB
C++
/*
|
|
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/select_future_owner_box.h"
|
|
|
|
#include "api/api_chat_participants.h"
|
|
#include "api/api_cloud_password.h"
|
|
#include "apiwrap.h"
|
|
#include "base/unixtime.h"
|
|
#include "boxes/filters/edit_filter_chats_list.h"
|
|
#include "boxes/passcode_box.h"
|
|
#include "boxes/peer_list_controllers.h"
|
|
#include "boxes/peer_lists_box.h"
|
|
#include "boxes/peers/replace_boost_box.h" // CreateUserpicsTransfer.
|
|
#include "core/application.h"
|
|
#include "core/core_cloud_password.h"
|
|
#include "data/data_channel.h"
|
|
#include "boxes/peers/channel_ownership_transfer.h"
|
|
#include "data/data_session.h"
|
|
#include "data/data_user.h"
|
|
#include "info/profile/info_profile_values.h"
|
|
#include "lang/lang_keys.h"
|
|
#include "main/main_session.h"
|
|
#include "ui/boxes/confirm_box.h"
|
|
#include "ui/layers/generic_box.h"
|
|
#include "ui/text/format_values.h"
|
|
#include "ui/vertical_list.h"
|
|
#include "ui/widgets/buttons.h"
|
|
#include "ui/widgets/labels.h"
|
|
#include "window/window_controller.h"
|
|
#include "window/window_session_controller.h"
|
|
#include "styles/style_boxes.h"
|
|
#include "styles/style_layers.h"
|
|
|
|
namespace {
|
|
|
|
enum class ParticipantType {
|
|
Admins,
|
|
Members
|
|
};
|
|
|
|
class ParticipantsController : public PeerListController {
|
|
public:
|
|
ParticipantsController(
|
|
not_null<Window::SessionController*> window,
|
|
not_null<ChannelData*> channel,
|
|
ParticipantType type);
|
|
|
|
Main::Session &session() const override;
|
|
void prepare() override;
|
|
void rowClicked(not_null<PeerListRow*> row) override;
|
|
void loadMoreRows() override;
|
|
void itemDeselectedHook(not_null<PeerData*> peer) override;
|
|
|
|
void setOnRowClicked(Fn<void()> callback);
|
|
rpl::producer<> itemDeselected() const;
|
|
|
|
private:
|
|
const not_null<Window::SessionController*> _window;
|
|
const not_null<ChannelData*> _channel;
|
|
const ParticipantType _type;
|
|
MTP::Sender _api;
|
|
Fn<void()> _onRowClicked;
|
|
rpl::event_stream<> _itemDeselected;
|
|
|
|
mtpRequestId _loadRequestId = 0;
|
|
int _offset = 0;
|
|
bool _allLoaded = false;
|
|
};
|
|
|
|
ParticipantsController::ParticipantsController(
|
|
not_null<Window::SessionController*> window,
|
|
not_null<ChannelData*> channel,
|
|
ParticipantType type)
|
|
: _window(window)
|
|
, _channel(channel)
|
|
, _type(type)
|
|
, _api(&channel->session().mtp()) {
|
|
}
|
|
|
|
Main::Session &ParticipantsController::session() const {
|
|
return _channel->session();
|
|
}
|
|
|
|
void ParticipantsController::setOnRowClicked(Fn<void()> callback) {
|
|
_onRowClicked = callback;
|
|
}
|
|
|
|
void ParticipantsController::prepare() {
|
|
loadMoreRows();
|
|
}
|
|
|
|
void ParticipantsController::rowClicked(not_null<PeerListRow*> row) {
|
|
delegate()->peerListSetRowChecked(
|
|
row,
|
|
!delegate()->peerListIsRowChecked(row));
|
|
for (auto i = 0; i < delegate()->peerListFullRowsCount(); ++i) {
|
|
auto r = delegate()->peerListRowAt(i);
|
|
if (r != row) {
|
|
delegate()->peerListSetRowChecked(r, false);
|
|
}
|
|
}
|
|
if (_onRowClicked) {
|
|
_onRowClicked();
|
|
}
|
|
}
|
|
|
|
void ParticipantsController::itemDeselectedHook(not_null<PeerData*> peer) {
|
|
_itemDeselected.fire({});
|
|
}
|
|
|
|
rpl::producer<> ParticipantsController::itemDeselected() const {
|
|
return _itemDeselected.events();
|
|
}
|
|
|
|
void ParticipantsController::loadMoreRows() {
|
|
if (_loadRequestId || _allLoaded) {
|
|
return;
|
|
}
|
|
|
|
const auto perPage = (_offset > 0) ? 200 : 50;
|
|
const auto participantsHash = uint64(0);
|
|
const auto filter = (_type == ParticipantType::Admins)
|
|
? MTP_channelParticipantsAdmins()
|
|
: MTP_channelParticipantsRecent();
|
|
|
|
_loadRequestId = _api.request(MTPchannels_GetParticipants(
|
|
_channel->inputChannel(),
|
|
filter,
|
|
MTP_int(_offset),
|
|
MTP_int(perPage),
|
|
MTP_long(participantsHash)
|
|
)).done([=](const MTPchannels_ChannelParticipants &result) {
|
|
_loadRequestId = 0;
|
|
auto added = false;
|
|
|
|
result.match([&](const MTPDchannels_channelParticipants &data) {
|
|
const auto &[availableCount, list] = Api::ChatParticipants::Parse(
|
|
_channel,
|
|
data);
|
|
for (const auto &participant : list) {
|
|
if (const auto user = _channel->owner().userLoaded(
|
|
participant.userId())) {
|
|
if (delegate()->peerListFindRow(user->id.value)) {
|
|
continue;
|
|
}
|
|
if (user->isBot()) {
|
|
continue;
|
|
}
|
|
using Type = Api::ChatParticipant::Type;
|
|
if ((participant.type() == Type::Creator)
|
|
|| (_type == ParticipantType::Members
|
|
&& (participant.type() == Type::Admin))) {
|
|
continue;
|
|
}
|
|
auto row = std::make_unique<PeerListRow>(user);
|
|
const auto promotedSince = participant.promotedSince();
|
|
row->setCustomStatus(
|
|
(promotedSince
|
|
? tr::lng_select_next_owner_box_status_promoted
|
|
: tr::lng_select_next_owner_box_status_joined)(
|
|
tr::now,
|
|
lt_date,
|
|
Ui::FormatDateTime(
|
|
base::unixtime::parse(promotedSince
|
|
? promotedSince
|
|
: participant.memberSince()))));
|
|
delegate()->peerListAppendRow(std::move(row));
|
|
added = true;
|
|
}
|
|
}
|
|
if (const auto size = list.size()) {
|
|
_offset += size;
|
|
} else {
|
|
_allLoaded = true;
|
|
}
|
|
}, [](const MTPDchannels_channelParticipantsNotModified &) {
|
|
});
|
|
|
|
if (!added && _offset > 0) {
|
|
_allLoaded = true;
|
|
}
|
|
delegate()->peerListRefreshRows();
|
|
}).fail([=] {
|
|
_loadRequestId = 0;
|
|
}).send();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void SelectFutureOwnerbox(
|
|
not_null<Ui::GenericBox*> box,
|
|
not_null<ChannelData*> channel,
|
|
not_null<UserData*> user) {
|
|
const auto content = box->verticalLayout();
|
|
Ui::AddSkip(content);
|
|
Ui::AddSkip(content);
|
|
content->add(
|
|
CreateUserpicsTransfer(
|
|
content,
|
|
rpl::single(std::vector<not_null<PeerData*>>{
|
|
user->session().user(),
|
|
channel,
|
|
}),
|
|
user,
|
|
UserpicsTransferType::ChannelFutureOwner),
|
|
st::boxRowPadding);
|
|
Ui::AddSkip(content);
|
|
Ui::AddSkip(content);
|
|
content->add(
|
|
object_ptr<Ui::FlatLabel>(
|
|
content,
|
|
channel->isMegagroup()
|
|
? tr::lng_leave_next_owner_box_title_group()
|
|
: tr::lng_leave_next_owner_box_title(),
|
|
box->getDelegate()->style().title),
|
|
st::boxRowPadding);
|
|
Ui::AddSkip(content);
|
|
Ui::AddSkip(content);
|
|
const auto adminsAreEqual = false;
|
|
content->add(
|
|
object_ptr<Ui::FlatLabel>(
|
|
content,
|
|
(adminsAreEqual
|
|
? tr::lng_leave_next_owner_box_about
|
|
: tr::lng_leave_next_owner_box_about_admin)(
|
|
lt_user,
|
|
Info::Profile::NameValue(user) | rpl::map(tr::marked),
|
|
lt_chat,
|
|
Info::Profile::NameValue(channel) | rpl::map(tr::marked),
|
|
tr::rich),
|
|
st::boxLabel),
|
|
st::boxRowPadding);
|
|
Ui::AddSkip(content);
|
|
Ui::AddSkip(content);
|
|
Ui::AddSkip(content);
|
|
|
|
const auto select = content->add(
|
|
object_ptr<Ui::RoundButton>(
|
|
content,
|
|
!adminsAreEqual
|
|
? tr::lng_select_next_owner_box()
|
|
: tr::lng_select_next_owner_box_admin(),
|
|
st::defaultLightButton),
|
|
st::boxRowPadding,
|
|
style::al_justify);
|
|
Ui::AddSkip(content);
|
|
const auto cancel = content->add(
|
|
object_ptr<Ui::RoundButton>(
|
|
content,
|
|
tr::lng_cancel(),
|
|
st::defaultLightButton),
|
|
st::boxRowPadding,
|
|
style::al_justify);
|
|
cancel->setClickedCallback([=] {
|
|
box->closeBox();
|
|
});
|
|
Ui::AddSkip(content);
|
|
const auto leave = content->add(
|
|
object_ptr<Ui::RoundButton>(
|
|
content,
|
|
channel->isMegagroup()
|
|
? tr::lng_profile_leave_group()
|
|
: tr::lng_profile_leave_channel(),
|
|
st::attentionBoxButton),
|
|
st::boxRowPadding,
|
|
style::al_justify);
|
|
leave->setClickedCallback([=, revoke = false] {
|
|
channel->session().api().deleteConversation(channel, revoke);
|
|
box->closeBox();
|
|
});
|
|
select->setClickedCallback([=] {
|
|
const auto window = Core::App().findWindow(box);
|
|
const auto sessionController = window
|
|
? window->sessionController()
|
|
: nullptr;
|
|
if (!sessionController) {
|
|
return;
|
|
}
|
|
|
|
auto adminsOwned = std::make_unique<ParticipantsController>(
|
|
sessionController,
|
|
channel,
|
|
ParticipantType::Admins);
|
|
|
|
auto membersOwned = std::make_unique<ParticipantsController>(
|
|
sessionController,
|
|
channel,
|
|
ParticipantType::Members);
|
|
|
|
const auto admins = adminsOwned.get();
|
|
const auto members = membersOwned.get();
|
|
|
|
auto initBox = [=](not_null<PeerListsBox*> selectBox) {
|
|
const auto selectionChanges
|
|
= selectBox->lifetime().make_state<rpl::event_stream<>>();
|
|
const auto uncheckOtherList = [=](
|
|
not_null<PeerListController*> otherController) {
|
|
auto delegate = otherController->delegate();
|
|
const auto full = delegate->peerListFullRowsCount();
|
|
for (auto i = 0; i < full; ++i) {
|
|
delegate->peerListSetRowChecked(
|
|
delegate->peerListRowAt(i),
|
|
false);
|
|
}
|
|
selectionChanges->fire({});
|
|
};
|
|
admins->setOnRowClicked([=] { uncheckOtherList(members); });
|
|
members->setOnRowClicked([=] { uncheckOtherList(admins); });
|
|
selectBox->setTitle(!adminsAreEqual
|
|
? tr::lng_select_next_owner_box_title()
|
|
: tr::lng_select_next_owner_box_title_admin());
|
|
const auto searchEnabled = PeerListSearchMode::Enabled;
|
|
admins->delegate()->peerListSetSearchMode(searchEnabled);
|
|
members->delegate()->peerListSetSearchMode(searchEnabled);
|
|
rpl::merge(
|
|
admins->itemDeselected(),
|
|
members->itemDeselected()
|
|
) | rpl::on_next([=] {
|
|
selectionChanges->fire({});
|
|
}, selectBox->lifetime());
|
|
selectBox->addSeparatorBefore(
|
|
0,
|
|
CreatePeerListSectionSubtitle(
|
|
selectBox,
|
|
!channel->isMegagroup()
|
|
? tr::lng_select_next_owner_box_sub_admins()
|
|
: tr::lng_select_next_owner_box_sub_admins_group()));
|
|
selectBox->addSeparatorBefore(
|
|
1,
|
|
CreatePeerListSectionSubtitle(
|
|
selectBox,
|
|
!channel->isMegagroup()
|
|
? tr::lng_select_next_owner_box_sub_members()
|
|
: tr::lng_select_next_owner_box_sub_members_group()));
|
|
{
|
|
const auto &st = st::futureOwnerBoxSelect;
|
|
selectBox->setStyle(st);
|
|
auto button = object_ptr<Ui::RoundButton>(
|
|
selectBox,
|
|
rpl::conditional(
|
|
selectionChanges->events(
|
|
) | rpl::map([=] {
|
|
return !selectBox->collectSelectedRows().empty();
|
|
}),
|
|
tr::lng_select_next_owner_box_confirm(),
|
|
tr::lng_close()),
|
|
st::defaultActiveButton);
|
|
button->setTextTransform(
|
|
Ui::RoundButton::TextTransform::NoTransform);
|
|
const auto raw = button.data();
|
|
rpl::combine(
|
|
selectionChanges->events() | rpl::map_to(0),
|
|
selectBox->widthValue()
|
|
) | rpl::on_next([=](int, int width) {
|
|
raw->resizeToWidth(width
|
|
- st.buttonPadding.left()
|
|
- st.buttonPadding.right());
|
|
}, selectBox->lifetime());
|
|
button->setFullRadius(true);
|
|
button->setClickedCallback([=] {
|
|
const auto selected = selectBox->collectSelectedRows();
|
|
if (selected.empty()) {
|
|
return selectBox->closeBox();
|
|
}
|
|
if (const auto user = selected.front()->asUser()) {
|
|
auto &lifetime = selectBox->lifetime();
|
|
lifetime.make_state<ChannelOwnershipTransfer>(
|
|
channel,
|
|
user,
|
|
selectBox->uiShow(),
|
|
[=](std::shared_ptr<Ui::Show> show) {
|
|
const auto revoke = false;
|
|
channel->session().api().deleteConversation(
|
|
channel,
|
|
revoke);
|
|
show->hideLayer();
|
|
})->start();
|
|
}
|
|
});
|
|
selectBox->addButton(std::move(button));
|
|
selectionChanges->fire({});
|
|
}
|
|
};
|
|
|
|
auto controllers = std::vector<std::unique_ptr<PeerListController>>();
|
|
controllers.reserve(2);
|
|
controllers.push_back(std::move(adminsOwned));
|
|
controllers.push_back(std::move(membersOwned));
|
|
box->uiShow()->showBox(
|
|
Box<PeerListsBox>(std::move(controllers), initBox));
|
|
});
|
|
for (const auto &b : { select, cancel, leave }) {
|
|
b->setFullRadius(true);
|
|
b->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
|
|
}
|
|
box->setStyle(st::futureOwnerBox);
|
|
} |