mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Pass Ui::Show to all add participant handlers.
This commit is contained in:
parent
5741bd9cca
commit
87acc921c3
18 changed files with 82 additions and 41 deletions
|
@ -487,9 +487,9 @@ void ChatParticipants::requestCountDelayed(
|
|||
}
|
||||
|
||||
void ChatParticipants::add(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<PeerData*> peer,
|
||||
const std::vector<not_null<UserData*>> &users,
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
bool passGroupHistory,
|
||||
Fn<void(bool)> done) {
|
||||
if (const auto chat = peer->asChat()) {
|
||||
|
@ -504,14 +504,14 @@ void ChatParticipants::add(
|
|||
if (done) done(true);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
const auto type = error.type();
|
||||
ShowAddParticipantsError(type, peer, { 1, user }, show);
|
||||
ShowAddParticipantsError(show, type, peer, { 1, user });
|
||||
if (done) done(false);
|
||||
}).afterDelay(kSmallDelayMs).send();
|
||||
}
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
const auto hasBot = ranges::any_of(users, &UserData::isBot);
|
||||
if (!peer->isMegagroup() && hasBot) {
|
||||
ShowAddParticipantsError("USER_BOT", peer, users, show);
|
||||
ShowAddParticipantsError(show, "USER_BOT", peer, users);
|
||||
return;
|
||||
}
|
||||
auto list = QVector<MTPInputUser>();
|
||||
|
@ -531,7 +531,7 @@ void ChatParticipants::add(
|
|||
channel,
|
||||
CollectForbiddenUsers(&channel->session(), result));
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
ShowAddParticipantsError(error.type(), peer, users, show);
|
||||
ShowAddParticipantsError(show, error.type(), peer, users);
|
||||
if (callback) callback(false);
|
||||
}).afterDelay(kSmallDelayMs).send();
|
||||
};
|
||||
|
|
|
@ -97,9 +97,9 @@ public:
|
|||
not_null<ChannelData*> channel,
|
||||
const TLMembers &data);
|
||||
void add(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<PeerData*> peer,
|
||||
const std::vector<not_null<UserData*>> &users,
|
||||
std::shared_ptr<Ui::Show> show = nullptr,
|
||||
bool passGroupHistory = true,
|
||||
Fn<void(bool)> done = nullptr);
|
||||
|
||||
|
|
|
@ -3855,13 +3855,14 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
|||
}
|
||||
|
||||
void ApiWrap::sendBotStart(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<UserData*> bot,
|
||||
PeerData *chat,
|
||||
const QString &startTokenForChat) {
|
||||
Expects(bot->isBot());
|
||||
|
||||
if (chat && chat->isChannel() && !chat->isMegagroup()) {
|
||||
ShowAddParticipantsError("USER_BOT", chat, { 1, bot });
|
||||
ShowAddParticipantsError(show, "USER_BOT", chat, { 1, bot });
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3893,7 +3894,7 @@ void ApiWrap::sendBotStart(
|
|||
}).fail([=](const MTP::Error &error) {
|
||||
if (chat) {
|
||||
const auto type = error.type();
|
||||
ShowAddParticipantsError(type, chat, { 1, bot });
|
||||
ShowAddParticipantsError(show, type, chat, { 1, bot });
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ class Key;
|
|||
|
||||
namespace Ui {
|
||||
struct PreparedList;
|
||||
class Show;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Api {
|
||||
|
@ -343,6 +344,7 @@ public:
|
|||
BusinessShortcutId id);
|
||||
void sendMessage(MessageToSend &&message);
|
||||
void sendBotStart(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<UserData*> bot,
|
||||
PeerData *chat = nullptr,
|
||||
const QString &startTokenForChat = QString());
|
||||
|
|
|
@ -169,10 +169,10 @@ TextWithEntities PeerFloodErrorText(
|
|||
}
|
||||
|
||||
void ShowAddParticipantsError(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
const QString &error,
|
||||
not_null<PeerData*> chat,
|
||||
const std::vector<not_null<UserData*>> &users,
|
||||
std::shared_ptr<Ui::Show> show) {
|
||||
const std::vector<not_null<UserData*>> &users) {
|
||||
if (error == u"USER_BOT"_q) {
|
||||
const auto channel = chat->asChannel();
|
||||
if ((users.size() == 1)
|
||||
|
@ -189,6 +189,7 @@ void ShowAddParticipantsError(
|
|||
}
|
||||
};
|
||||
const auto saveCallback = SaveAdminCallback(
|
||||
show,
|
||||
channel,
|
||||
user,
|
||||
close,
|
||||
|
@ -199,9 +200,10 @@ void ShowAddParticipantsError(
|
|||
ChatAdminRightsInfo(),
|
||||
QString());
|
||||
box->setSaveCallback(saveCallback);
|
||||
*weak = Ui::show(std::move(box));
|
||||
*weak = box.data();
|
||||
show->showBox(std::move(box));
|
||||
};
|
||||
Ui::show(
|
||||
show->showBox(
|
||||
Ui::MakeConfirmBox({
|
||||
.text = tr::lng_cant_invite_offer_admin(),
|
||||
.confirmed = makeAdmin,
|
||||
|
@ -219,7 +221,7 @@ void ShowAddParticipantsError(
|
|||
const auto text = PeerFloodErrorText(&chat->session(), type);
|
||||
Ui::show(Ui::MakeInformBox(text), Ui::LayerOption::KeepOther);
|
||||
return;
|
||||
} else if (error == u"USER_PRIVACY_RESTRICTED"_q && show) {
|
||||
} else if (error == u"USER_PRIVACY_RESTRICTED"_q) {
|
||||
ChatInviteForbidden(show, chat, users);
|
||||
return;
|
||||
}
|
||||
|
@ -231,8 +233,6 @@ void ShowAddParticipantsError(
|
|||
} else if (error == u"USER_KICKED"_q) {
|
||||
// Trying to return a user who was kicked by admin.
|
||||
return tr::lng_cant_invite_banned(tr::now);
|
||||
} else if (error == u"USER_PRIVACY_RESTRICTED"_q) {
|
||||
return tr::lng_cant_invite_privacy(tr::now);
|
||||
} else if (error == u"USER_NOT_MUTUAL_CONTACT"_q) {
|
||||
// Trying to return user who does not have me in contacts.
|
||||
return tr::lng_failed_add_not_mutual(tr::now);
|
||||
|
@ -247,7 +247,7 @@ void ShowAddParticipantsError(
|
|||
}
|
||||
return tr::lng_failed_add_participant(tr::now);
|
||||
}();
|
||||
Ui::show(Ui::MakeInformBox(text), Ui::LayerOption::KeepOther);
|
||||
show->show(Ui::MakeInformBox(text), Ui::LayerOption::KeepOther);
|
||||
}
|
||||
|
||||
AddContactBox::AddContactBox(
|
||||
|
|
|
@ -34,6 +34,7 @@ template <typename Enum>
|
|||
class Radioenum;
|
||||
class LinkButton;
|
||||
class UserpicButton;
|
||||
class Show;
|
||||
} // namespace Ui
|
||||
|
||||
enum class PeerFloodType {
|
||||
|
@ -46,10 +47,10 @@ enum class PeerFloodType {
|
|||
not_null<Main::Session*> session,
|
||||
PeerFloodType type);
|
||||
void ShowAddParticipantsError(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
const QString &error,
|
||||
not_null<PeerData*> chat,
|
||||
const std::vector<not_null<UserData*>> &users,
|
||||
std::shared_ptr<Ui::Show> show = nullptr);
|
||||
const std::vector<not_null<UserData*>> &users);
|
||||
|
||||
class AddContactBox : public Ui::BoxContent {
|
||||
public:
|
||||
|
|
|
@ -217,6 +217,7 @@ void AddBotToGroupBoxController::addBotToGroup(not_null<PeerData*> chat) {
|
|||
? bot->botInfo->groupAdminRights
|
||||
: ChatAdminRights();
|
||||
const auto addingAdmin = requestedAddAdmin || (rights != 0);
|
||||
const auto show = controller->uiShow();
|
||||
if (addingAdmin) {
|
||||
const auto scope = _scope;
|
||||
const auto token = _token;
|
||||
|
@ -224,11 +225,12 @@ void AddBotToGroupBoxController::addBotToGroup(not_null<PeerData*> chat) {
|
|||
ChatAdminRightsInfo newRights,
|
||||
const QString &rank) {
|
||||
if (scope == Scope::GroupAdmin) {
|
||||
chat->session().api().sendBotStart(bot, chat, token);
|
||||
chat->session().api().sendBotStart(show, bot, chat, token);
|
||||
}
|
||||
close();
|
||||
};
|
||||
const auto saveCallback = SaveAdminCallback(
|
||||
show,
|
||||
chat,
|
||||
bot,
|
||||
done,
|
||||
|
@ -245,7 +247,7 @@ void AddBotToGroupBoxController::addBotToGroup(not_null<PeerData*> chat) {
|
|||
controller->show(std::move(box));
|
||||
} else {
|
||||
auto callback = crl::guard(this, [=] {
|
||||
AddBotToGroup(bot, chat, _token);
|
||||
AddBotToGroup(show, bot, chat, _token);
|
||||
controller->hideLayer();
|
||||
});
|
||||
controller->show(Ui::MakeConfirmBox({
|
||||
|
@ -394,13 +396,14 @@ void AddBotToGroupBoxController::prepareViewHook() {
|
|||
}
|
||||
|
||||
void AddBotToGroup(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<UserData*> bot,
|
||||
not_null<PeerData*> chat,
|
||||
const QString &startToken) {
|
||||
if (!startToken.isEmpty()) {
|
||||
chat->session().api().sendBotStart(bot, chat, startToken);
|
||||
chat->session().api().sendBotStart(show, bot, chat, startToken);
|
||||
} else {
|
||||
chat->session().api().chatParticipants().add(chat, { 1, bot });
|
||||
chat->session().api().chatParticipants().add(show, chat, { 1, bot });
|
||||
}
|
||||
if (const auto window = chat->session().tryResolveWindow()) {
|
||||
window->showPeerHistory(chat);
|
||||
|
|
|
@ -76,6 +76,7 @@ private:
|
|||
};
|
||||
|
||||
void AddBotToGroup(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<UserData*> bot,
|
||||
not_null<PeerData*> chat,
|
||||
const QString &startToken);
|
||||
|
|
|
@ -433,9 +433,9 @@ void AddParticipantsBoxController::inviteSelectedUsers(
|
|||
const auto show = box->uiShow();
|
||||
const auto request = [=](bool checked) {
|
||||
_peer->session().api().chatParticipants().add(
|
||||
show,
|
||||
_peer,
|
||||
users,
|
||||
show,
|
||||
checked);
|
||||
};
|
||||
if (_peer->isChannel()) {
|
||||
|
@ -945,6 +945,7 @@ void AddSpecialBoxController::showAdmin(
|
|||
user,
|
||||
currentRights,
|
||||
_additional.adminRank(user));
|
||||
const auto show = delegate()->peerListUiShow();
|
||||
if (_additional.canAddOrEditAdmin(user)) {
|
||||
const auto done = crl::guard(this, [=](
|
||||
ChatAdminRightsInfo newRights,
|
||||
|
@ -956,7 +957,8 @@ void AddSpecialBoxController::showAdmin(
|
|||
_editParticipantBox->closeBox();
|
||||
}
|
||||
});
|
||||
box->setSaveCallback(SaveAdminCallback(_peer, user, done, fail));
|
||||
box->setSaveCallback(
|
||||
SaveAdminCallback(show, _peer, user, done, fail));
|
||||
}
|
||||
_editParticipantBox = showBox(std::move(box));
|
||||
}
|
||||
|
|
|
@ -387,11 +387,12 @@ void EditAdminBox::prepare() {
|
|||
_rank ? _rank->getLastText().trimmed() : QString());
|
||||
};
|
||||
_save = [=] {
|
||||
const auto show = uiShow();
|
||||
if (!_saveCallback) {
|
||||
return;
|
||||
} else if (_addAsAdmin && !_addAsAdmin->checked()) {
|
||||
const auto weak = Ui::MakeWeak(this);
|
||||
AddBotToGroup(user(), peer(), _addingBot->token);
|
||||
AddBotToGroup(show, user(), peer(), _addingBot->token);
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
|
|
|
@ -72,6 +72,7 @@ void RemoveAdmin(
|
|||
}
|
||||
|
||||
void AddChatParticipant(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<ChatData*> chat,
|
||||
not_null<UserData*> user,
|
||||
Fn<void()> onDone,
|
||||
|
@ -87,7 +88,7 @@ void AddChatParticipant(
|
|||
onDone();
|
||||
}
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
ShowAddParticipantsError(error.type(), chat, { 1, user });
|
||||
ShowAddParticipantsError(show, error.type(), chat, { 1, user });
|
||||
if (onFail) {
|
||||
onFail();
|
||||
}
|
||||
|
@ -95,6 +96,7 @@ void AddChatParticipant(
|
|||
}
|
||||
|
||||
void SaveChatAdmin(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<ChatData*> chat,
|
||||
not_null<UserData*> user,
|
||||
bool isAdmin,
|
||||
|
@ -115,8 +117,15 @@ void SaveChatAdmin(
|
|||
if (retryOnNotParticipant
|
||||
&& isAdmin
|
||||
&& (type == u"USER_NOT_PARTICIPANT"_q)) {
|
||||
AddChatParticipant(chat, user, [=] {
|
||||
SaveChatAdmin(chat, user, isAdmin, onDone, onFail, false);
|
||||
AddChatParticipant(show, chat, user, [=] {
|
||||
SaveChatAdmin(
|
||||
show,
|
||||
chat,
|
||||
user,
|
||||
isAdmin,
|
||||
onDone,
|
||||
onFail,
|
||||
false);
|
||||
}, onFail);
|
||||
} else if (onFail) {
|
||||
onFail();
|
||||
|
@ -125,6 +134,7 @@ void SaveChatAdmin(
|
|||
}
|
||||
|
||||
void SaveChannelAdmin(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<UserData*> user,
|
||||
ChatAdminRightsInfo oldRights,
|
||||
|
@ -145,7 +155,7 @@ void SaveChannelAdmin(
|
|||
onDone();
|
||||
}
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
ShowAddParticipantsError(error.type(), channel, { 1, user });
|
||||
ShowAddParticipantsError(show, error.type(), channel, { 1, user });
|
||||
if (onFail) {
|
||||
onFail();
|
||||
}
|
||||
|
@ -206,6 +216,7 @@ Fn<void(
|
|||
ChatAdminRightsInfo oldRights,
|
||||
ChatAdminRightsInfo newRights,
|
||||
const QString &rank)> SaveAdminCallback(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<PeerData*> peer,
|
||||
not_null<UserData*> user,
|
||||
Fn<void(
|
||||
|
@ -219,6 +230,7 @@ Fn<void(
|
|||
const auto done = [=] { if (onDone) onDone(newRights, rank); };
|
||||
const auto saveForChannel = [=](not_null<ChannelData*> channel) {
|
||||
SaveChannelAdmin(
|
||||
show,
|
||||
channel,
|
||||
user,
|
||||
oldRights,
|
||||
|
@ -229,7 +241,7 @@ Fn<void(
|
|||
};
|
||||
if (const auto chat = peer->asChatNotMigrated()) {
|
||||
const auto saveChatAdmin = [&](bool isAdmin) {
|
||||
SaveChatAdmin(chat, user, isAdmin, done, onFail);
|
||||
SaveChatAdmin(show, chat, user, isAdmin, done, onFail);
|
||||
};
|
||||
if (newRights.flags == chat->defaultAdminRights(user).flags
|
||||
&& rank.isEmpty()) {
|
||||
|
@ -1743,7 +1755,9 @@ void ParticipantsBoxController::showAdmin(not_null<UserData*> user) {
|
|||
_editParticipantBox->closeBox();
|
||||
}
|
||||
});
|
||||
box->setSaveCallback(SaveAdminCallback(_peer, user, done, fail));
|
||||
const auto show = delegate()->peerListUiShow();
|
||||
box->setSaveCallback(
|
||||
SaveAdminCallback(show, _peer, user, done, fail));
|
||||
}
|
||||
_editParticipantBox = showBox(std::move(box));
|
||||
}
|
||||
|
@ -1863,7 +1877,8 @@ void ParticipantsBoxController::unkickParticipant(not_null<UserData*> user) {
|
|||
delegate()->peerListRemoveRow(row);
|
||||
refreshRows();
|
||||
}
|
||||
_peer->session().api().chatParticipants().add(_peer, { 1, user });
|
||||
const auto show = delegate()->peerListUiShow();
|
||||
_peer->session().api().chatParticipants().add(show, _peer, { 1, user });
|
||||
}
|
||||
|
||||
void ParticipantsBoxController::kickParticipantSure(
|
||||
|
@ -1906,7 +1921,8 @@ void ParticipantsBoxController::removeAdminSure(not_null<UserData*> user) {
|
|||
_editBox = nullptr;
|
||||
|
||||
if (const auto chat = _peer->asChat()) {
|
||||
SaveChatAdmin(chat, user, false, crl::guard(this, [=] {
|
||||
const auto show = delegate()->peerListUiShow();
|
||||
SaveChatAdmin(show, chat, user, false, crl::guard(this, [=] {
|
||||
editAdminDone(
|
||||
user,
|
||||
ChatAdminRightsInfo(),
|
||||
|
|
|
@ -17,6 +17,10 @@ class PeerListStories;
|
|||
struct ChatAdminRightsInfo;
|
||||
struct ChatRestrictionsInfo;
|
||||
|
||||
namespace Ui {
|
||||
class Show;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Window {
|
||||
class SessionNavigation;
|
||||
} // namespace Window
|
||||
|
@ -29,6 +33,7 @@ Fn<void(
|
|||
ChatAdminRightsInfo oldRights,
|
||||
ChatAdminRightsInfo newRights,
|
||||
const QString &rank)> SaveAdminCallback(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<PeerData*> peer,
|
||||
not_null<UserData*> user,
|
||||
Fn<void(
|
||||
|
|
|
@ -1963,7 +1963,8 @@ void Controller::toggleBotManager(const QString &command) {
|
|||
const auto botPeer = _peer->owner().peerLoaded(
|
||||
peerFromMTP(result.data().vpeer()));
|
||||
if (const auto bot = botPeer ? botPeer->asUser() : nullptr) {
|
||||
_peer->session().api().sendBotStart(bot, bot, command);
|
||||
const auto show = controller->uiShow();
|
||||
_peer->session().api().sendBotStart(show, bot, bot, command);
|
||||
controller->showPeerHistory(bot);
|
||||
}
|
||||
}).send();
|
||||
|
|
|
@ -225,9 +225,9 @@ object_ptr<Ui::BoxContent> PrepareInviteBox(
|
|||
const std::vector<not_null<UserData*>> &nonMembers,
|
||||
Fn<void()> finish) {
|
||||
peer->session().api().chatParticipants().add(
|
||||
show,
|
||||
peer,
|
||||
nonMembers,
|
||||
show,
|
||||
true,
|
||||
[=](bool) { invite(users); finish(); });
|
||||
};
|
||||
|
|
|
@ -4224,7 +4224,8 @@ SendMenu::Type HistoryWidget::sendButtonMenuType() const {
|
|||
|
||||
void HistoryWidget::unblockUser() {
|
||||
if (const auto user = _peer ? _peer->asUser() : nullptr) {
|
||||
Window::PeerMenuUnblockUserWithBotRestart(user);
|
||||
const auto show = controller()->uiShow();
|
||||
Window::PeerMenuUnblockUserWithBotRestart(show, user);
|
||||
} else {
|
||||
updateControlsVisibility();
|
||||
}
|
||||
|
@ -4238,7 +4239,7 @@ void HistoryWidget::sendBotStartCommand() {
|
|||
updateControlsVisibility();
|
||||
return;
|
||||
}
|
||||
session().api().sendBotStart(_peer->asUser());
|
||||
session().api().sendBotStart(controller()->uiShow(), _peer->asUser());
|
||||
updateControlsVisibility();
|
||||
updateControlsGeometry();
|
||||
}
|
||||
|
|
|
@ -1688,7 +1688,8 @@ void ActionsFiller::addBlockAction(not_null<UserData*> user) {
|
|||
});
|
||||
auto callback = [=] {
|
||||
if (user->isBlocked()) {
|
||||
Window::PeerMenuUnblockUserWithBotRestart(user);
|
||||
const auto show = controller->uiShow();
|
||||
Window::PeerMenuUnblockUserWithBotRestart(show, user);
|
||||
if (user->isBot()) {
|
||||
controller->showPeerHistory(user);
|
||||
}
|
||||
|
|
|
@ -781,8 +781,9 @@ void Filler::addBlockUser() {
|
|||
: tr::lng_profile_block_user(tr::now));
|
||||
};
|
||||
const auto blockAction = _addAction(blockText(user), [=] {
|
||||
const auto show = window->uiShow();
|
||||
if (user->isBlocked()) {
|
||||
PeerMenuUnblockUserWithBotRestart(user);
|
||||
PeerMenuUnblockUserWithBotRestart(show, user);
|
||||
} else if (user->isBot()) {
|
||||
user->session().api().blockedPeers().block(user);
|
||||
} else {
|
||||
|
@ -1700,10 +1701,12 @@ void PeerMenuBlockUserBox(
|
|||
});
|
||||
}
|
||||
|
||||
void PeerMenuUnblockUserWithBotRestart(not_null<UserData*> user) {
|
||||
void PeerMenuUnblockUserWithBotRestart(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<UserData*> user) {
|
||||
user->session().api().blockedPeers().unblock(user, [=](bool success) {
|
||||
if (success && user->isBot() && !user->isSupport()) {
|
||||
user->session().api().sendBotStart(user);
|
||||
user->session().api().sendBotStart(show, user);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace Ui {
|
|||
class RpWidget;
|
||||
class BoxContent;
|
||||
class GenericBox;
|
||||
class Show;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Data {
|
||||
|
@ -109,7 +110,9 @@ void PeerMenuBlockUserBox(
|
|||
not_null<PeerData*> peer,
|
||||
std::variant<v::null_t, bool> suggestReport,
|
||||
std::variant<v::null_t, ClearChat, ClearReply> suggestClear);
|
||||
void PeerMenuUnblockUserWithBotRestart(not_null<UserData*> user);
|
||||
void PeerMenuUnblockUserWithBotRestart(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<UserData*> user);
|
||||
|
||||
void BlockSenderFromRepliesBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
|
|
Loading…
Add table
Reference in a new issue