Implement new permanent invite link management.
BIN
Telegram/Resources/icons/info/edit/group_manage_actions.png
Normal file
After Width: | Height: | Size: 430 B |
BIN
Telegram/Resources/icons/info/edit/group_manage_actions@2x.png
Normal file
After Width: | Height: | Size: 780 B |
BIN
Telegram/Resources/icons/info/edit/group_manage_actions@3x.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/Resources/icons/info/edit/group_manage_admins.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/info/edit/group_manage_admins@2x.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
Telegram/Resources/icons/info/edit/group_manage_admins@3x.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
Telegram/Resources/icons/info/edit/group_manage_links.png
Normal file
After Width: | Height: | Size: 933 B |
BIN
Telegram/Resources/icons/info/edit/group_manage_links@2x.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
Telegram/Resources/icons/info/edit/group_manage_links@3x.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
Telegram/Resources/icons/info/edit/group_manage_members.png
Normal file
After Width: | Height: | Size: 620 B |
BIN
Telegram/Resources/icons/info/edit/group_manage_members@2x.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/Resources/icons/info/edit/group_manage_members@3x.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
Telegram/Resources/icons/info/edit/group_manage_permissions.png
Normal file
After Width: | Height: | Size: 980 B |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.7 KiB |
|
@ -934,7 +934,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_manage_peer_permissions" = "Permissions";
|
||||
"lng_manage_peer_invite_links" = "Invite links";
|
||||
|
||||
|
||||
"lng_manage_peer_group_type" = "Group type";
|
||||
"lng_manage_peer_channel_type" = "Channel type";
|
||||
"lng_manage_peer_link_type" = "Link type";
|
||||
|
|
|
@ -21,6 +21,23 @@ namespace {
|
|||
constexpr auto kFirstPage = 10;
|
||||
constexpr auto kPerPage = 50;
|
||||
|
||||
void BringPermanentToFront(PeerInviteLinks &links) {
|
||||
auto &list = links.links;
|
||||
const auto i = ranges::find_if(list, [](const InviteLink &link) {
|
||||
return link.permanent && !link.revoked;
|
||||
});
|
||||
if (i != end(list) && i != begin(list)) {
|
||||
ranges::rotate(begin(list), i, i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void RemovePermanent(PeerInviteLinks &links) {
|
||||
auto &list = links.links;
|
||||
list.erase(ranges::remove_if(list, [](const InviteLink &link) {
|
||||
return link.permanent && !link.revoked;
|
||||
}), end(list));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
InviteLinks::InviteLinks(not_null<ApiWrap*> api) : _api(api) {
|
||||
|
@ -28,93 +45,167 @@ InviteLinks::InviteLinks(not_null<ApiWrap*> api) : _api(api) {
|
|||
|
||||
void InviteLinks::create(
|
||||
not_null<PeerData*> peer,
|
||||
Fn<void(Link)> done,
|
||||
TimeId expireDate,
|
||||
int usageLimit) {
|
||||
if (_createRequests.contains(peer)) {
|
||||
performCreate(peer, std::move(done), false, expireDate, usageLimit);
|
||||
}
|
||||
|
||||
void InviteLinks::performCreate(
|
||||
not_null<PeerData*> peer,
|
||||
Fn<void(Link)> done,
|
||||
bool revokeLegacyPermanent,
|
||||
TimeId expireDate,
|
||||
int usageLimit) {
|
||||
if (const auto i = _createCallbacks.find(peer)
|
||||
; i != end(_createCallbacks)) {
|
||||
if (done) {
|
||||
i->second.push_back(std::move(done));
|
||||
}
|
||||
return;
|
||||
}
|
||||
auto &callbacks = _createCallbacks[peer];
|
||||
if (done) {
|
||||
callbacks.push_back(std::move(done));
|
||||
}
|
||||
|
||||
using Flag = MTPmessages_ExportChatInvite::Flag;
|
||||
const auto requestId = _api->request(MTPmessages_ExportChatInvite(
|
||||
_api->request(MTPmessages_ExportChatInvite(
|
||||
MTP_flags((expireDate ? Flag::f_expire_date : Flag(0))
|
||||
| (usageLimit ? Flag::f_usage_limit : Flag(0))),
|
||||
peer->input,
|
||||
MTP_int(expireDate),
|
||||
MTP_int(usageLimit)
|
||||
)).done([=](const MTPExportedChatInvite &result) {
|
||||
_createRequests.erase(peer);
|
||||
const auto link = (result.type() == mtpc_chatInviteExported)
|
||||
? qs(result.c_chatInviteExported().vlink())
|
||||
: QString();
|
||||
if (!expireDate && !usageLimit) {
|
||||
editPermanentLink(peer, QString(), link);
|
||||
const auto callbacks = _createCallbacks.take(peer);
|
||||
const auto link = prepend(peer, result);
|
||||
if (callbacks) {
|
||||
for (const auto &callback : *callbacks) {
|
||||
callback(link);
|
||||
}
|
||||
}
|
||||
}).fail([=](const RPCError &error) {
|
||||
_createRequests.erase(peer);
|
||||
_createCallbacks.erase(peer);
|
||||
}).send();
|
||||
_createRequests.emplace(peer, requestId);
|
||||
}
|
||||
|
||||
auto InviteLinks::lookupPermanent(not_null<PeerData*> peer) -> Link* {
|
||||
auto i = _firstSlices.find(peer);
|
||||
return (i != end(_firstSlices)) ? lookupPermanent(i->second) : nullptr;
|
||||
}
|
||||
|
||||
auto InviteLinks::lookupPermanent(Links &links) -> Link* {
|
||||
const auto first = links.links.begin();
|
||||
return (first != end(links.links) && first->permanent && !first->revoked)
|
||||
? &*first
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
auto InviteLinks::lookupPermanent(const Links &links) const -> const Link* {
|
||||
const auto first = links.links.begin();
|
||||
return (first != end(links.links) && first->permanent && !first->revoked)
|
||||
? &*first
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
auto InviteLinks::prepend(
|
||||
not_null<PeerData*> peer,
|
||||
const MTPExportedChatInvite &invite) -> Link {
|
||||
const auto link = parse(peer, invite);
|
||||
auto i = _firstSlices.find(peer);
|
||||
if (i == end(_firstSlices)) {
|
||||
i = _firstSlices.emplace(peer).first;
|
||||
}
|
||||
auto &links = i->second;
|
||||
if (link.permanent) {
|
||||
if (const auto permanent = lookupPermanent(links)) {
|
||||
permanent->revoked = true;
|
||||
}
|
||||
editPermanentLink(peer, link.link);
|
||||
}
|
||||
++links.count;
|
||||
links.links.insert(begin(links.links), link);
|
||||
notify(peer);
|
||||
return link;
|
||||
}
|
||||
|
||||
void InviteLinks::edit(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &link,
|
||||
TimeId expireDate,
|
||||
int usageLimit,
|
||||
Fn<void(Link)> done) {
|
||||
performEdit(peer, link, std::move(done), false, expireDate, usageLimit);
|
||||
}
|
||||
|
||||
void InviteLinks::performEdit(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &link,
|
||||
Fn<void(Link)> done,
|
||||
bool revoke,
|
||||
TimeId expireDate,
|
||||
int usageLimit) {
|
||||
const auto key = EditKey{ peer, link };
|
||||
if (_editRequests.contains(key)) {
|
||||
if (const auto i = _editCallbacks.find(key); i != end(_editCallbacks)) {
|
||||
if (done) {
|
||||
i->second.push_back(std::move(done));
|
||||
}
|
||||
return;
|
||||
}
|
||||
auto &callbacks = _editCallbacks[key];
|
||||
if (done) {
|
||||
callbacks.push_back(std::move(done));
|
||||
}
|
||||
|
||||
if (const auto permanent = revoke ? lookupPermanent(peer) : nullptr) {
|
||||
if (permanent->link == link) {
|
||||
// In case of revoking a permanent link
|
||||
// we should just create a new one instead.
|
||||
performCreate(peer, std::move(done), true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
using Flag = MTPmessages_EditExportedChatInvite::Flag;
|
||||
const auto requestId = _api->request(MTPmessages_EditExportedChatInvite(
|
||||
MTP_flags((expireDate ? Flag::f_expire_date : Flag(0))
|
||||
| (usageLimit ? Flag::f_usage_limit : Flag(0))),
|
||||
MTP_flags((revoke ? Flag::f_revoked : Flag(0))
|
||||
| ((!revoke && expireDate) ? Flag::f_expire_date : Flag(0))
|
||||
| ((!revoke && usageLimit) ? Flag::f_usage_limit : Flag(0))),
|
||||
peer->input,
|
||||
MTP_string(link),
|
||||
MTP_int(expireDate),
|
||||
MTP_int(usageLimit)
|
||||
)).done([=](const MTPmessages_ExportedChatInvite &result) {
|
||||
_editRequests.erase(key);
|
||||
const auto callbacks = _editCallbacks.take(key);
|
||||
const auto peer = key.peer;
|
||||
result.match([&](const MTPDmessages_exportedChatInvite &data) {
|
||||
_api->session().data().processUsers(data.vusers());
|
||||
const auto &invite = data.vinvite();
|
||||
const auto link = (invite.type() == mtpc_chatInviteExported)
|
||||
? qs(invite.c_chatInviteExported().vlink())
|
||||
: QString();
|
||||
// #TODO links
|
||||
const auto link = parse(peer, data.vinvite());
|
||||
auto i = _firstSlices.find(peer);
|
||||
if (i != end(_firstSlices)) {
|
||||
const auto j = ranges::find(
|
||||
i->second.links,
|
||||
key.link,
|
||||
&Link::link);
|
||||
if (j != end(i->second.links)) {
|
||||
*j = link;
|
||||
notify(peer);
|
||||
}
|
||||
}
|
||||
for (const auto &callback : *callbacks) {
|
||||
callback(link);
|
||||
}
|
||||
});
|
||||
}).fail([=](const RPCError &error) {
|
||||
_editRequests.erase(key);
|
||||
_editCallbacks.erase(key);
|
||||
}).send();
|
||||
_editRequests.emplace(key, requestId);
|
||||
}
|
||||
|
||||
void InviteLinks::revoke(not_null<PeerData*> peer, const QString &link) {
|
||||
const auto key = EditKey{ peer, link };
|
||||
if (_editRequests.contains(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto requestId = _api->request(MTPmessages_EditExportedChatInvite(
|
||||
MTP_flags(MTPmessages_EditExportedChatInvite::Flag::f_revoked),
|
||||
peer->input,
|
||||
MTP_string(link),
|
||||
MTPint(), // expire_date
|
||||
MTPint() // usage_limit
|
||||
)).done([=](const MTPmessages_ExportedChatInvite &result) {
|
||||
_editRequests.erase(key);
|
||||
result.match([&](const MTPDmessages_exportedChatInvite &data) {
|
||||
_api->session().data().processUsers(data.vusers());
|
||||
const auto &invite = data.vinvite();
|
||||
const auto link = (invite.type() == mtpc_chatInviteExported)
|
||||
? qs(invite.c_chatInviteExported().vlink())
|
||||
: QString();
|
||||
editPermanentLink(peer, key.link, link);
|
||||
});
|
||||
}).fail([=](const RPCError &error) {
|
||||
_editRequests.erase(key);
|
||||
}).send();
|
||||
_editRequests.emplace(key, requestId);
|
||||
void InviteLinks::revoke(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &link,
|
||||
Fn<void(Link)> done) {
|
||||
performEdit(peer, link, std::move(done), true);
|
||||
}
|
||||
|
||||
void InviteLinks::requestLinks(not_null<PeerData*> peer) {
|
||||
|
@ -129,16 +220,80 @@ void InviteLinks::requestLinks(not_null<PeerData*> peer) {
|
|||
MTP_int(kFirstPage)
|
||||
)).done([=](const MTPmessages_ExportedChatInvites &result) {
|
||||
_firstSliceRequests.remove(peer);
|
||||
_firstSlices.emplace_or_assign(peer, parseSlice(peer, result));
|
||||
peer->session().changes().peerUpdated(
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::InviteLink);
|
||||
auto slice = parseSlice(peer, result);
|
||||
auto i = _firstSlices.find(peer);
|
||||
const auto permanent = (i != end(_firstSlices))
|
||||
? lookupPermanent(i->second)
|
||||
: nullptr;
|
||||
if (!permanent) {
|
||||
BringPermanentToFront(slice);
|
||||
const auto j = _firstSlices.emplace_or_assign(
|
||||
peer,
|
||||
std::move(slice)).first;
|
||||
if (const auto permanent = lookupPermanent(j->second)) {
|
||||
editPermanentLink(peer, permanent->link);
|
||||
}
|
||||
} else {
|
||||
RemovePermanent(slice);
|
||||
auto &existing = i->second.links;
|
||||
existing.erase(begin(existing) + 1, end(existing));
|
||||
existing.insert(
|
||||
end(existing),
|
||||
begin(slice.links),
|
||||
end(slice.links));
|
||||
i->second.count = std::max(slice.count, int(existing.size()));
|
||||
}
|
||||
notify(peer);
|
||||
}).fail([=](const RPCError &error) {
|
||||
_firstSliceRequests.remove(peer);
|
||||
}).send();
|
||||
_firstSliceRequests.emplace(peer, requestId);
|
||||
}
|
||||
|
||||
void InviteLinks::setPermanent(
|
||||
not_null<PeerData*> peer,
|
||||
const MTPExportedChatInvite &invite) {
|
||||
auto link = parse(peer, invite);
|
||||
link.permanent = true; // #TODO links remove hack
|
||||
//if (!link.permanent) {
|
||||
// LOG(("API Error: "
|
||||
// "InviteLinks::setPermanent called with non-permanent link."));
|
||||
// return;
|
||||
//}
|
||||
auto i = _firstSlices.find(peer);
|
||||
if (i == end(_firstSlices)) {
|
||||
i = _firstSlices.emplace(peer).first;
|
||||
}
|
||||
auto &links = i->second;
|
||||
if (const auto permanent = lookupPermanent(links)) {
|
||||
if (permanent->link == link.link) {
|
||||
if (permanent->usage != link.usage) {
|
||||
permanent->usage = link.usage;
|
||||
notify(peer);
|
||||
}
|
||||
return;
|
||||
}
|
||||
permanent->revoked = true;
|
||||
}
|
||||
links.links.insert(begin(links.links), link);
|
||||
editPermanentLink(peer, link.link);
|
||||
notify(peer);
|
||||
}
|
||||
|
||||
void InviteLinks::clearPermanent(not_null<PeerData*> peer) {
|
||||
if (const auto permanent = lookupPermanent(peer)) {
|
||||
permanent->revoked = true;
|
||||
editPermanentLink(peer, QString());
|
||||
notify(peer);
|
||||
}
|
||||
}
|
||||
|
||||
void InviteLinks::notify(not_null<PeerData*> peer) {
|
||||
peer->session().changes().peerUpdated(
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::InviteLinks);
|
||||
}
|
||||
|
||||
auto InviteLinks::links(not_null<PeerData*> peer) const -> Links {
|
||||
const auto i = _firstSlices.find(peer);
|
||||
return (i != end(_firstSlices)) ? i->second : Links();
|
||||
|
@ -147,28 +302,42 @@ auto InviteLinks::links(not_null<PeerData*> peer) const -> Links {
|
|||
auto InviteLinks::parseSlice(
|
||||
not_null<PeerData*> peer,
|
||||
const MTPmessages_ExportedChatInvites &slice) const -> Links {
|
||||
auto i = _firstSlices.find(peer);
|
||||
const auto permanent = (i != end(_firstSlices))
|
||||
? lookupPermanent(i->second)
|
||||
: nullptr;
|
||||
auto result = Links();
|
||||
slice.match([&](const MTPDmessages_exportedChatInvites &data) {
|
||||
auto &owner = peer->session().data();
|
||||
owner.processUsers(data.vusers());
|
||||
peer->session().data().processUsers(data.vusers());
|
||||
result.count = data.vcount().v;
|
||||
for (const auto &invite : data.vinvites().v) {
|
||||
invite.match([&](const MTPDchatInviteExported &data) {
|
||||
result.links.push_back({
|
||||
.link = qs(data.vlink()),
|
||||
.admin = owner.user(data.vadmin_id().v),
|
||||
.date = data.vdate().v,
|
||||
.expireDate = data.vexpire_date().value_or_empty(),
|
||||
.usageLimit = data.vusage_limit().value_or_empty(),
|
||||
.usage = data.vusage().value_or_empty(),
|
||||
.revoked = data.is_revoked(),
|
||||
});
|
||||
});
|
||||
const auto link = parse(peer, invite);
|
||||
if (!permanent || link.link != permanent->link) {
|
||||
result.links.push_back(link);
|
||||
}
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
auto InviteLinks::parse(
|
||||
not_null<PeerData*> peer,
|
||||
const MTPExportedChatInvite &invite) const -> Link {
|
||||
return invite.match([&](const MTPDchatInviteExported &data) {
|
||||
return Link{
|
||||
.link = qs(data.vlink()),
|
||||
.admin = peer->session().data().user(data.vadmin_id().v),
|
||||
.date = data.vdate().v,
|
||||
.expireDate = data.vexpire_date().value_or_empty(),
|
||||
.usageLimit = data.vusage_limit().value_or_empty(),
|
||||
.usage = data.vusage().value_or_empty(),
|
||||
.permanent = data.is_permanent(),
|
||||
.expired = data.is_expired(),
|
||||
.revoked = data.is_revoked(),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
void InviteLinks::requestMoreLinks(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &last,
|
||||
|
@ -180,7 +349,9 @@ void InviteLinks::requestMoreLinks(
|
|||
MTP_string(last),
|
||||
MTP_int(kPerPage)
|
||||
)).done([=](const MTPmessages_ExportedChatInvites &result) {
|
||||
done(parseSlice(peer, result));
|
||||
auto slice = parseSlice(peer, result);
|
||||
RemovePermanent(slice);
|
||||
done(std::move(slice));
|
||||
}).fail([=](const RPCError &error) {
|
||||
done(Links());
|
||||
}).send();
|
||||
|
@ -188,16 +359,11 @@ void InviteLinks::requestMoreLinks(
|
|||
|
||||
void InviteLinks::editPermanentLink(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &from,
|
||||
const QString &to) {
|
||||
const QString &link) {
|
||||
if (const auto chat = peer->asChat()) {
|
||||
if (chat->inviteLink() == from) {
|
||||
chat->setInviteLink(to);
|
||||
}
|
||||
chat->setInviteLink(link);
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
if (channel->inviteLink() == from) {
|
||||
channel->setInviteLink(to);
|
||||
}
|
||||
channel->setInviteLink(link);
|
||||
} else {
|
||||
Unexpected("Peer in InviteLinks::editMainLink.");
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ struct InviteLink {
|
|||
TimeId expireDate = 0;
|
||||
int usageLimit = 0;
|
||||
int usage = 0;
|
||||
bool permanent = false;
|
||||
bool expired = false;
|
||||
bool revoked = false;
|
||||
};
|
||||
|
||||
|
@ -35,14 +37,24 @@ public:
|
|||
|
||||
void create(
|
||||
not_null<PeerData*> peer,
|
||||
Fn<void(Link)> done = nullptr,
|
||||
TimeId expireDate = 0,
|
||||
int usageLimit = 0);
|
||||
void edit(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &link,
|
||||
TimeId expireDate,
|
||||
int usageLimit);
|
||||
void revoke(not_null<PeerData*> peer, const QString &link);
|
||||
int usageLimit,
|
||||
Fn<void(Link)> done = nullptr);
|
||||
void revoke(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &link,
|
||||
Fn<void(Link)> done = nullptr);
|
||||
|
||||
void setPermanent(
|
||||
not_null<PeerData*> peer,
|
||||
const MTPExportedChatInvite &invite);
|
||||
void clearPermanent(not_null<PeerData*> peer);
|
||||
|
||||
void requestLinks(not_null<PeerData*> peer);
|
||||
[[nodiscard]] Links links(not_null<PeerData*> peer) const;
|
||||
|
@ -64,21 +76,47 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
void editPermanentLink(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &from,
|
||||
const QString &to);
|
||||
[[nodiscard]] Links parseSlice(
|
||||
not_null<PeerData*> peer,
|
||||
const MTPmessages_ExportedChatInvites &slice) const;
|
||||
[[nodiscard]] Link parse(
|
||||
not_null<PeerData*> peer,
|
||||
const MTPExportedChatInvite &invite) const;
|
||||
[[nodiscard]] Link *lookupPermanent(not_null<PeerData*> peer);
|
||||
[[nodiscard]] Link *lookupPermanent(Links &links);
|
||||
[[nodiscard]] const Link *lookupPermanent(const Links &links) const;
|
||||
Link prepend(
|
||||
not_null<PeerData*> peer,
|
||||
const MTPExportedChatInvite &invite);
|
||||
void notify(not_null<PeerData*> peer);
|
||||
|
||||
void editPermanentLink(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &link);
|
||||
|
||||
void performEdit(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &link,
|
||||
Fn<void(Link)> done,
|
||||
bool revoke,
|
||||
TimeId expireDate = 0,
|
||||
int usageLimit = 0);
|
||||
void performCreate(
|
||||
not_null<PeerData*> peer,
|
||||
Fn<void(Link)> done,
|
||||
bool revokeLegacyPermanent,
|
||||
TimeId expireDate = 0,
|
||||
int usageLimit = 0);
|
||||
|
||||
const not_null<ApiWrap*> _api;
|
||||
|
||||
base::flat_map<not_null<PeerData*>, Links> _firstSlices;
|
||||
base::flat_map<not_null<PeerData*>, mtpRequestId> _firstSliceRequests;
|
||||
|
||||
base::flat_map<not_null<PeerData*>, mtpRequestId> _createRequests;
|
||||
base::flat_map<EditKey, mtpRequestId> _editRequests;
|
||||
base::flat_map<
|
||||
not_null<PeerData*>,
|
||||
std::vector<Fn<void(Link)>>> _createCallbacks;
|
||||
base::flat_map<EditKey, std::vector<Fn<void(Link)>>> _editCallbacks;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -614,7 +614,9 @@ void GroupInfoBox::createGroup(
|
|||
}
|
||||
|
||||
void GroupInfoBox::submit() {
|
||||
if (_creationRequestId) return;
|
||||
if (_creationRequestId || _creatingInviteLink) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto title = TextUtilities::PrepareForSending(_title->getLastText());
|
||||
auto description = _description
|
||||
|
@ -653,6 +655,8 @@ void GroupInfoBox::submit() {
|
|||
}
|
||||
|
||||
void GroupInfoBox::createChannel(const QString &title, const QString &description) {
|
||||
Expects(!_creationRequestId);
|
||||
|
||||
const auto flags = (_type == Type::Megagroup)
|
||||
? MTPchannels_CreateChannel::Flag::f_megagroup
|
||||
: MTPchannels_CreateChannel::Flag::f_broadcast;
|
||||
|
@ -692,29 +696,9 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio
|
|||
channel,
|
||||
std::move(image));
|
||||
}
|
||||
using Flag = MTPmessages_ExportChatInvite::Flag;
|
||||
_createdChannel = channel;
|
||||
_creationRequestId = _api.request(MTPmessages_ExportChatInvite(
|
||||
MTP_flags(0),
|
||||
_createdChannel->input,
|
||||
MTPint(), // expire_date
|
||||
MTPint() // usage_limit
|
||||
)).done([=](const MTPExportedChatInvite &result) {
|
||||
_creationRequestId = 0;
|
||||
if (result.type() == mtpc_chatInviteExported) {
|
||||
auto link = qs(result.c_chatInviteExported().vlink());
|
||||
_createdChannel->setInviteLink(link);
|
||||
}
|
||||
if (_channelDone) {
|
||||
const auto callback = _channelDone;
|
||||
const auto argument = _createdChannel;
|
||||
closeBox();
|
||||
callback(argument);
|
||||
} else {
|
||||
Ui::show(Box<SetupChannelBox>(
|
||||
_navigation,
|
||||
_createdChannel));
|
||||
}
|
||||
}).send();
|
||||
checkInviteLink();
|
||||
};
|
||||
if (!success) {
|
||||
LOG(("API Error: channel not found in updates (GroupInfoBox::creationDone)"));
|
||||
|
@ -733,6 +717,32 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio
|
|||
}).send();
|
||||
}
|
||||
|
||||
void GroupInfoBox::checkInviteLink() {
|
||||
Expects(_createdChannel != nullptr);
|
||||
|
||||
if (!_createdChannel->inviteLink().isEmpty()) {
|
||||
channelReady();
|
||||
return;
|
||||
}
|
||||
_creatingInviteLink = true;
|
||||
_createdChannel->session().api().inviteLinks().create(
|
||||
_createdChannel,
|
||||
crl::guard(this, [=](auto&&) { channelReady(); }));
|
||||
}
|
||||
|
||||
void GroupInfoBox::channelReady() {
|
||||
if (_channelDone) {
|
||||
const auto callback = _channelDone;
|
||||
const auto argument = _createdChannel;
|
||||
closeBox();
|
||||
callback(argument);
|
||||
} else {
|
||||
Ui::show(Box<SetupChannelBox>(
|
||||
_navigation,
|
||||
_createdChannel));
|
||||
}
|
||||
}
|
||||
|
||||
void GroupInfoBox::descriptionResized() {
|
||||
updateMaxHeight();
|
||||
update();
|
||||
|
@ -834,7 +844,7 @@ void SetupChannelBox::prepare() {
|
|||
|
||||
_channel->session().changes().peerUpdates(
|
||||
_channel,
|
||||
Data::PeerUpdate::Flag::InviteLink
|
||||
Data::PeerUpdate::Flag::InviteLinks
|
||||
) | rpl::start_with_next([=] {
|
||||
rtlupdate(_invitationLink);
|
||||
}, lifetime());
|
||||
|
|
|
@ -121,6 +121,8 @@ private:
|
|||
void createGroup(not_null<PeerListBox*> selectUsersBox, const QString &title, const std::vector<not_null<PeerData*>> &users);
|
||||
void submitName();
|
||||
void submit();
|
||||
void checkInviteLink();
|
||||
void channelReady();
|
||||
|
||||
void descriptionResized();
|
||||
void updateMaxHeight();
|
||||
|
@ -138,6 +140,7 @@ private:
|
|||
|
||||
// group / channel creation
|
||||
mtpRequestId _creationRequestId = 0;
|
||||
bool _creatingInviteLink = false;
|
||||
ChannelData *_createdChannel = nullptr;
|
||||
|
||||
};
|
||||
|
|
|
@ -394,7 +394,7 @@ void MaxInviteBox::prepare() {
|
|||
|
||||
_channel->session().changes().peerUpdates(
|
||||
_channel,
|
||||
Data::PeerUpdate::Flag::InviteLink
|
||||
Data::PeerUpdate::Flag::InviteLinks
|
||||
) | rpl::start_with_next([=] {
|
||||
rtlupdate(_invitationLink);
|
||||
}, lifetime());
|
||||
|
|
|
@ -331,7 +331,6 @@ private:
|
|||
void showEditLinkedChatBox();
|
||||
void fillPrivacyTypeButton();
|
||||
void fillLinkedChatButton();
|
||||
void fillInviteLinkButton();
|
||||
void fillSignaturesButton();
|
||||
void fillHistoryVisibilityButton();
|
||||
void fillManageSection();
|
||||
|
@ -640,6 +639,8 @@ void Controller::refreshHistoryVisibility(anim::type animated) {
|
|||
|
||||
void Controller::showEditPeerTypeBox(
|
||||
std::optional<rpl::producer<QString>> error) {
|
||||
Expects(_privacySavedValue.has_value());
|
||||
|
||||
const auto boxCallback = crl::guard(this, [=](
|
||||
Privacy checked, QString publicLink) {
|
||||
_privacyTypeUpdates.fire(std::move(checked));
|
||||
|
@ -652,7 +653,7 @@ void Controller::showEditPeerTypeBox(
|
|||
_peer,
|
||||
_channelHasLocationOriginalValue,
|
||||
boxCallback,
|
||||
_privacySavedValue,
|
||||
*_privacySavedValue,
|
||||
_usernameSavedValue,
|
||||
error),
|
||||
Ui::LayerOption::KeepOther);
|
||||
|
@ -798,22 +799,9 @@ void Controller::fillLinkedChatButton() {
|
|||
_linkedChatUpdates.fire_copy(*_linkedChatSavedValue);
|
||||
}
|
||||
|
||||
void Controller::fillInviteLinkButton() {
|
||||
Expects(_controls.buttonsLayout != nullptr);
|
||||
|
||||
const auto buttonCallback = [=] {
|
||||
Ui::show(Box<EditPeerTypeBox>(_peer), Ui::LayerOption::KeepOther);
|
||||
};
|
||||
|
||||
AddButtonWithText(
|
||||
_controls.buttonsLayout,
|
||||
tr::lng_profile_invite_link_section(),
|
||||
rpl::single(QString()), //Empty text.
|
||||
buttonCallback);
|
||||
}
|
||||
|
||||
void Controller::fillSignaturesButton() {
|
||||
Expects(_controls.buttonsLayout != nullptr);
|
||||
|
||||
const auto channel = _peer->asChannel();
|
||||
if (!channel) return;
|
||||
|
||||
|
@ -964,8 +952,6 @@ void Controller::fillManageSection() {
|
|||
|
||||
if (canEditUsername) {
|
||||
fillPrivacyTypeButton();
|
||||
} else if (canEditInviteLink) {
|
||||
fillInviteLinkButton();
|
||||
}
|
||||
if (canViewOrEditLinkedChat) {
|
||||
fillLinkedChatButton();
|
||||
|
@ -1010,7 +996,7 @@ void Controller::fillManageSection() {
|
|||
peer->session().api().inviteLinks().requestLinks(peer);
|
||||
return peer->session().changes().peerUpdates(
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::InviteLink
|
||||
Data::PeerUpdate::Flag::InviteLinks
|
||||
) | rpl::map([=] {
|
||||
return peer->session().api().inviteLinks().links(
|
||||
peer).count;
|
||||
|
@ -1018,7 +1004,7 @@ void Controller::fillManageSection() {
|
|||
}) | rpl::flatten_latest(
|
||||
) | ToPositiveNumberString(),
|
||||
[=] { ShowEditInviteLinks(_navigation, _peer); },
|
||||
st::infoIconPermissions);
|
||||
st::infoIconInviteLinks);
|
||||
}
|
||||
if (canViewAdmins) {
|
||||
AddButtonWithCount(
|
||||
|
|
|
@ -58,7 +58,7 @@ public:
|
|||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<PeerData*> peer,
|
||||
bool useLocationPhrases,
|
||||
std::optional<Privacy> privacySavedValue,
|
||||
Privacy privacySavedValue,
|
||||
std::optional<QString> usernameSavedValue);
|
||||
|
||||
void createContent();
|
||||
|
@ -66,17 +66,11 @@ public:
|
|||
void setFocusUsername();
|
||||
|
||||
rpl::producer<QString> getTitle() {
|
||||
return _isInviteLink
|
||||
? tr::lng_profile_invite_link_section()
|
||||
: _isGroup
|
||||
return _isGroup
|
||||
? tr::lng_manage_peer_group_type()
|
||||
: tr::lng_manage_peer_channel_type();
|
||||
}
|
||||
|
||||
bool isInviteLink() {
|
||||
return _isInviteLink;
|
||||
}
|
||||
|
||||
bool isAllowSave() {
|
||||
return _isAllowSave;
|
||||
}
|
||||
|
@ -98,17 +92,14 @@ private:
|
|||
base::unique_qptr<Ui::FlatLabel> usernameResult;
|
||||
const style::FlatLabel *usernameResultStyle = nullptr;
|
||||
|
||||
Ui::SlideWrap<Ui::RpWidget> *createInviteLinkWrap = nullptr;
|
||||
Ui::SlideWrap<Ui::RpWidget> *editInviteLinkWrap = nullptr;
|
||||
Ui::SlideWrap<Ui::RpWidget> *inviteLinkWrap = nullptr;
|
||||
Ui::FlatLabel *inviteLink = nullptr;
|
||||
};
|
||||
|
||||
Controls _controls;
|
||||
|
||||
object_ptr<Ui::RpWidget> createPrivaciesEdit();
|
||||
object_ptr<Ui::RpWidget> createUsernameEdit();
|
||||
object_ptr<Ui::RpWidget> createInviteLinkCreate();
|
||||
object_ptr<Ui::RpWidget> createInviteLinkEdit();
|
||||
object_ptr<Ui::RpWidget> createInviteLinkBlock();
|
||||
|
||||
void observeInviteLink();
|
||||
|
||||
|
@ -124,8 +115,7 @@ private:
|
|||
not_null<const style::FlatLabel*> st);
|
||||
|
||||
bool canEditInviteLink() const;
|
||||
void refreshEditInviteLink();
|
||||
void refreshCreateInviteLink();
|
||||
void refreshInviteLinkBlock();
|
||||
void createInviteLink();
|
||||
void revokeInviteLink(const QString &link);
|
||||
|
||||
|
@ -143,12 +133,11 @@ private:
|
|||
|
||||
not_null<PeerData*> _peer;
|
||||
MTP::Sender _api;
|
||||
std::optional<Privacy> _privacySavedValue;
|
||||
Privacy _privacySavedValue = Privacy();
|
||||
std::optional<QString> _usernameSavedValue;
|
||||
|
||||
bool _useLocationPhrases = false;
|
||||
bool _isGroup = false;
|
||||
bool _isInviteLink = false;
|
||||
bool _isAllowSave = false;
|
||||
|
||||
base::unique_qptr<Ui::VerticalLayout> _wrap;
|
||||
|
@ -165,7 +154,7 @@ Controller::Controller(
|
|||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<PeerData*> peer,
|
||||
bool useLocationPhrases,
|
||||
std::optional<Privacy> privacySavedValue,
|
||||
Privacy privacySavedValue,
|
||||
std::optional<QString> usernameSavedValue)
|
||||
: _peer(peer)
|
||||
, _api(&_peer->session().mtp())
|
||||
|
@ -173,8 +162,6 @@ Controller::Controller(
|
|||
, _usernameSavedValue(usernameSavedValue)
|
||||
, _useLocationPhrases(useLocationPhrases)
|
||||
, _isGroup(_peer->isChat() || _peer->isMegagroup())
|
||||
, _isInviteLink(!_privacySavedValue.has_value()
|
||||
&& !_usernameSavedValue.has_value())
|
||||
, _isAllowSave(!_usernameSavedValue.value_or(QString()).isEmpty())
|
||||
, _wrap(container)
|
||||
, _checkUsernameTimer([=] { checkUsernameAvailability(); }) {
|
||||
|
@ -184,18 +171,11 @@ Controller::Controller(
|
|||
void Controller::createContent() {
|
||||
_controls = Controls();
|
||||
|
||||
if (_isInviteLink) {
|
||||
_wrap->add(createInviteLinkCreate());
|
||||
_wrap->add(createInviteLinkEdit());
|
||||
return;
|
||||
}
|
||||
|
||||
fillPrivaciesButtons(_wrap, _privacySavedValue);
|
||||
// Skip.
|
||||
_wrap->add(object_ptr<Ui::BoxContentDivider>(_wrap));
|
||||
//
|
||||
_wrap->add(createInviteLinkCreate());
|
||||
_wrap->add(createInviteLinkEdit());
|
||||
_wrap->add(createInviteLinkBlock());
|
||||
_wrap->add(createUsernameEdit());
|
||||
|
||||
if (_controls.privacy->value() == Privacy::NoUsername) {
|
||||
|
@ -378,16 +358,14 @@ void Controller::privacyChanged(Privacy value) {
|
|||
// Otherwise box will change own Y position.
|
||||
|
||||
if (value == Privacy::HasUsername) {
|
||||
refreshCreateInviteLink();
|
||||
refreshEditInviteLink();
|
||||
refreshInviteLinkBlock();
|
||||
toggleEditUsername();
|
||||
|
||||
_controls.usernameResult = nullptr;
|
||||
checkUsernameAvailability();
|
||||
} else {
|
||||
toggleEditUsername();
|
||||
refreshCreateInviteLink();
|
||||
refreshEditInviteLink();
|
||||
refreshInviteLinkBlock();
|
||||
}
|
||||
};
|
||||
if (value == Privacy::HasUsername) {
|
||||
|
@ -562,29 +540,26 @@ void Controller::revokeInviteLink(const QString &link) {
|
|||
|
||||
bool Controller::canEditInviteLink() const {
|
||||
if (const auto channel = _peer->asChannel()) {
|
||||
return channel->amCreator()
|
||||
|| (channel->adminRights() & ChatAdminRight::f_invite_users);
|
||||
return channel->canHaveInviteLink();
|
||||
} else if (const auto chat = _peer->asChat()) {
|
||||
return chat->amCreator()
|
||||
|| (chat->adminRights() & ChatAdminRight::f_invite_users);
|
||||
return chat->canHaveInviteLink();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Controller::observeInviteLink() {
|
||||
if (!_controls.editInviteLinkWrap) {
|
||||
if (!_controls.inviteLinkWrap) {
|
||||
return;
|
||||
}
|
||||
_peer->session().changes().peerFlagsValue(
|
||||
_peer,
|
||||
Data::PeerUpdate::Flag::InviteLink
|
||||
Data::PeerUpdate::Flag::InviteLinks
|
||||
) | rpl::start_with_next([=] {
|
||||
refreshCreateInviteLink();
|
||||
refreshEditInviteLink();
|
||||
}, _controls.editInviteLinkWrap->lifetime());
|
||||
refreshInviteLinkBlock();
|
||||
}, _controls.inviteLinkWrap->lifetime());
|
||||
}
|
||||
|
||||
object_ptr<Ui::RpWidget> Controller::createInviteLinkEdit() {
|
||||
object_ptr<Ui::RpWidget> Controller::createInviteLinkBlock() {
|
||||
Expects(_wrap != nullptr);
|
||||
|
||||
if (!canEditInviteLink()) {
|
||||
|
@ -595,18 +570,16 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkEdit() {
|
|||
_wrap,
|
||||
object_ptr<Ui::VerticalLayout>(_wrap),
|
||||
st::editPeerInvitesMargins);
|
||||
_controls.editInviteLinkWrap = result.data();
|
||||
_controls.inviteLinkWrap = result.data();
|
||||
|
||||
const auto container = result->entity();
|
||||
if (!_isInviteLink) {
|
||||
container->add(object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
tr::lng_profile_invite_link_section(),
|
||||
st::editPeerSectionLabel));
|
||||
container->add(object_ptr<Ui::FixedHeightWidget>(
|
||||
container,
|
||||
st::editPeerInviteLinkBoxBottomSkip));
|
||||
}
|
||||
container->add(object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
tr::lng_profile_invite_link_section(),
|
||||
st::editPeerSectionLabel));
|
||||
container->add(object_ptr<Ui::FixedHeightWidget>(
|
||||
container,
|
||||
st::editPeerInviteLinkBoxBottomSkip));
|
||||
|
||||
_controls.inviteLink = container->add(object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
|
@ -634,7 +607,7 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkEdit() {
|
|||
return result;
|
||||
}
|
||||
|
||||
void Controller::refreshEditInviteLink() {
|
||||
void Controller::refreshInviteLinkBlock() {
|
||||
const auto link = inviteLinkText();
|
||||
auto text = TextWithEntities();
|
||||
if (!link.isEmpty()) {
|
||||
|
@ -652,74 +625,26 @@ void Controller::refreshEditInviteLink() {
|
|||
_controls.inviteLink->setMarkedText(text);
|
||||
|
||||
// Hack to expand FlatLabel width to naturalWidth again.
|
||||
_controls.editInviteLinkWrap->resizeToWidth(st::boxWideWidth);
|
||||
_controls.inviteLinkWrap->resizeToWidth(st::boxWideWidth);
|
||||
|
||||
_controls.editInviteLinkWrap->toggle(
|
||||
_controls.inviteLinkWrap->toggle(
|
||||
inviteLinkShown() && !link.isEmpty(),
|
||||
anim::type::instant);
|
||||
}
|
||||
|
||||
object_ptr<Ui::RpWidget> Controller::createInviteLinkCreate() {
|
||||
Expects(_wrap != nullptr);
|
||||
|
||||
if (!canEditInviteLink()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto result = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
_wrap,
|
||||
object_ptr<Ui::VerticalLayout>(_wrap),
|
||||
st::editPeerInvitesMargins);
|
||||
const auto container = result->entity();
|
||||
|
||||
if (!_isInviteLink) {
|
||||
container->add(object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
tr::lng_profile_invite_link_section(),
|
||||
st::editPeerSectionLabel));
|
||||
container->add(object_ptr<Ui::FixedHeightWidget>(
|
||||
container,
|
||||
st::editPeerInviteLinkSkip));
|
||||
}
|
||||
|
||||
container->add(object_ptr<Ui::LinkButton>(
|
||||
_wrap,
|
||||
tr::lng_group_invite_create(tr::now),
|
||||
st::editPeerInviteLinkButton)
|
||||
)->addClickHandler([this] {
|
||||
createInviteLink();
|
||||
});
|
||||
_controls.createInviteLinkWrap = result.data();
|
||||
|
||||
observeInviteLink();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Controller::refreshCreateInviteLink() {
|
||||
_controls.createInviteLinkWrap->toggle(
|
||||
inviteLinkShown() && inviteLinkText().isEmpty(),
|
||||
anim::type::instant);
|
||||
}
|
||||
|
||||
bool Controller::inviteLinkShown() {
|
||||
return !_controls.privacy
|
||||
|| (_controls.privacy->value() == Privacy::NoUsername)
|
||||
|| _isInviteLink;
|
||||
|| (_controls.privacy->value() == Privacy::NoUsername);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EditPeerTypeBox::EditPeerTypeBox(QWidget*, not_null<PeerData*> peer)
|
||||
: EditPeerTypeBox(nullptr, peer, false, {}, {}, {}, {}) {
|
||||
}
|
||||
|
||||
EditPeerTypeBox::EditPeerTypeBox(
|
||||
QWidget*,
|
||||
not_null<PeerData*> peer,
|
||||
bool useLocationPhrases,
|
||||
std::optional<FnMut<void(Privacy, QString)>> savedCallback,
|
||||
std::optional<Privacy> privacySaved,
|
||||
Privacy privacySaved,
|
||||
std::optional<QString> usernameSaved,
|
||||
std::optional<rpl::producer<QString>> usernameError)
|
||||
: _peer(peer)
|
||||
|
@ -760,7 +685,7 @@ void EditPeerTypeBox::prepare() {
|
|||
|
||||
setTitle(controller->getTitle());
|
||||
|
||||
if (!controller->isInviteLink() && _savedCallback.has_value()) {
|
||||
if (_savedCallback.has_value()) {
|
||||
addButton(tr::lng_settings_save(), [=] {
|
||||
const auto v = controller->getPrivacy();
|
||||
if (!controller->isAllowSave() && (v == Privacy::HasUsername)) {
|
||||
|
@ -772,13 +697,11 @@ void EditPeerTypeBox::prepare() {
|
|||
local(v,
|
||||
(v == Privacy::HasUsername)
|
||||
? controller->getUsernameInput()
|
||||
: QString()); // We dont need username with private type.
|
||||
: QString()); // We don't need username with private type.
|
||||
closeBox();
|
||||
});
|
||||
}
|
||||
addButton(
|
||||
controller->isInviteLink() ? tr::lng_close() : tr::lng_cancel(),
|
||||
[=] { closeBox(); });
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
|
||||
setDimensionsToContent(st::boxWideWidth, content);
|
||||
}
|
||||
|
|
|
@ -32,15 +32,12 @@ enum class UsernameState {
|
|||
|
||||
class EditPeerTypeBox : public Ui::BoxContent {
|
||||
public:
|
||||
// Edit just the invite link.
|
||||
EditPeerTypeBox(QWidget*, not_null<PeerData*> peer);
|
||||
|
||||
EditPeerTypeBox(
|
||||
QWidget*,
|
||||
not_null<PeerData*> peer,
|
||||
bool useLocationPhrases,
|
||||
std::optional<FnMut<void(Privacy, QString)>> savedCallback,
|
||||
std::optional<Privacy> privacySaved,
|
||||
Privacy privacySaved,
|
||||
std::optional<QString> usernameSaved,
|
||||
std::optional<rpl::producer<QString>> usernameError = {});
|
||||
|
||||
|
@ -53,7 +50,7 @@ private:
|
|||
bool _useLocationPhrases = false;
|
||||
std::optional<FnMut<void(Privacy, QString)>> _savedCallback;
|
||||
|
||||
std::optional<Privacy> _privacySavedValue;
|
||||
Privacy _privacySavedValue = Privacy();
|
||||
std::optional<QString> _usernameSavedValue;
|
||||
std::optional<rpl::producer<QString>> _usernameError;
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_group_call.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "core/application.h"
|
||||
#include "boxes/single_choice_box.h"
|
||||
#include "webrtc/webrtc_audio_input_tester.h"
|
||||
|
@ -32,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "settings/settings_calls.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "api/api_invite_links.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_calls.h"
|
||||
#include "styles/style_settings.h"
|
||||
|
@ -441,23 +443,9 @@ void GroupCallSettingsBox(
|
|||
)->addClickHandler([=] {
|
||||
if (!copyLink() && !state->generatingLink) {
|
||||
state->generatingLink = true;
|
||||
peer->session().api().request(MTPmessages_ExportChatInvite(
|
||||
MTP_flags(0),
|
||||
peer->input,
|
||||
MTPint(), // expire_date
|
||||
MTPint() // usage_limit
|
||||
)).done([=](const MTPExportedChatInvite &result) {
|
||||
if (result.type() == mtpc_chatInviteExported) {
|
||||
const auto link = qs(
|
||||
result.c_chatInviteExported().vlink());
|
||||
if (const auto chat = peer->asChat()) {
|
||||
chat->setInviteLink(link);
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
channel->setInviteLink(link);
|
||||
}
|
||||
copyLink();
|
||||
}
|
||||
}).send();
|
||||
peer->session().api().inviteLinks().create(
|
||||
peer,
|
||||
crl::guard(layout, [=](auto&&) { copyLink(); }));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ struct PeerUpdate {
|
|||
IsBot = (1 << 19),
|
||||
|
||||
// For chats and channels
|
||||
InviteLink = (1 << 20),
|
||||
InviteLinks = (1 << 20),
|
||||
Members = (1 << 21),
|
||||
Admins = (1 << 22),
|
||||
BannedUsers = (1 << 23),
|
||||
|
|
|
@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history.h"
|
||||
#include "main/main_session.h"
|
||||
#include "api/api_chat_invite.h"
|
||||
#include "api/api_invite_links.h"
|
||||
#include "apiwrap.h"
|
||||
|
||||
namespace {
|
||||
|
@ -98,10 +99,7 @@ void ChannelData::setAccessHash(uint64 accessHash) {
|
|||
}
|
||||
|
||||
void ChannelData::setInviteLink(const QString &newInviteLink) {
|
||||
if (newInviteLink != _inviteLink) {
|
||||
_inviteLink = newInviteLink;
|
||||
session().changes().peerUpdated(this, UpdateFlag::InviteLink);
|
||||
}
|
||||
_inviteLink = newInviteLink;
|
||||
}
|
||||
|
||||
bool ChannelData::canHaveInviteLink() const {
|
||||
|
@ -778,11 +776,11 @@ void ApplyChannelUpdate(
|
|||
next->v - channel->slowmodeSeconds());
|
||||
}
|
||||
if (const auto invite = update.vexported_invite()) {
|
||||
invite->match([&](const MTPDchatInviteExported &data) {
|
||||
channel->setInviteLink(qs(data.vlink()));
|
||||
});
|
||||
channel->session().api().inviteLinks().setPermanent(
|
||||
channel,
|
||||
*invite);
|
||||
} else {
|
||||
channel->setInviteLink(QString());
|
||||
channel->session().api().inviteLinks().clearPermanent(channel);
|
||||
}
|
||||
if (const auto location = update.vlocation()) {
|
||||
channel->setLocation(*location);
|
||||
|
|
|
@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "api/api_invite_links.h"
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -127,10 +128,7 @@ void ChatData::invalidateParticipants() {
|
|||
}
|
||||
|
||||
void ChatData::setInviteLink(const QString &newInviteLink) {
|
||||
if (newInviteLink != _inviteLink) {
|
||||
_inviteLink = newInviteLink;
|
||||
session().changes().peerUpdated(this, UpdateFlag::InviteLink);
|
||||
}
|
||||
_inviteLink = newInviteLink;
|
||||
}
|
||||
|
||||
bool ChatData::canHaveInviteLink() const {
|
||||
|
@ -389,11 +387,9 @@ void ApplyChatUpdate(not_null<ChatData*> chat, const MTPDchatFull &update) {
|
|||
chat->setUserpicPhoto(MTP_photoEmpty(MTP_long(0)));
|
||||
}
|
||||
if (const auto invite = update.vexported_invite()) {
|
||||
invite->match([&](const MTPDchatInviteExported &data) {
|
||||
chat->setInviteLink(qs(data.vlink()));
|
||||
});
|
||||
chat->session().api().inviteLinks().setPermanent(chat, *invite);
|
||||
} else {
|
||||
chat->setInviteLink(QString());
|
||||
chat->session().api().inviteLinks().clearPermanent(chat);
|
||||
}
|
||||
if (const auto pinned = update.vpinned_msg_id()) {
|
||||
SetTopPinnedMessageId(chat, pinned->v);
|
||||
|
|
|
@ -348,7 +348,7 @@ infoProfileSeparatorPadding: margins(
|
|||
|
||||
infoIconFg: menuIconFg;
|
||||
infoIconInformation: icon {{ "info_information", infoIconFg }};
|
||||
infoIconMembers: icon {{ "info_members", infoIconFg }};
|
||||
infoIconMembers: icon {{ "info/edit/group_manage_members", infoIconFg, point(-2px, 0px) }};
|
||||
infoIconNotifications: icon {{ "info_notifications", infoIconFg }};
|
||||
infoIconActions: icon {{ "info_actions", infoIconFg }};
|
||||
//infoIconFeed: icon {{ "info_feed", infoIconFg }};
|
||||
|
@ -360,10 +360,11 @@ infoIconMediaLink: icon {{ "info_media_link", infoIconFg }};
|
|||
infoIconMediaGroup: icon {{ "info_common_groups", infoIconFg }};
|
||||
infoIconMediaVoice: icon {{ "info_media_voice", infoIconFg }};
|
||||
infoIconMediaRound: icon {{ "info_media_round", infoIconFg }};
|
||||
infoIconRecentActions: icon {{ "info_recent_actions", infoIconFg, point(-2px, 0px) }};
|
||||
infoIconAdministrators: icon {{ "info_administrators", infoIconFg, point(-2px, -1px) }};
|
||||
infoIconRecentActions: icon {{ "info/edit/group_manage_actions", infoIconFg, point(-2px, -1px) }};
|
||||
infoIconAdministrators: icon {{ "info/edit/group_manage_admins", infoIconFg, point(-3px, 0px) }};
|
||||
infoIconBlacklist: icon {{ "info_blacklist", infoIconFg, point(-2px, -2px) }};
|
||||
infoIconPermissions: icon {{ "info_permissions", infoIconFg }};
|
||||
infoIconPermissions: icon {{ "info/edit/group_manage_permissions", infoIconFg, point(0px, -2px) }};
|
||||
infoIconInviteLinks: icon {{ "info/edit/group_manage_links", infoIconFg, point(-2px, 0px) }};
|
||||
infoInformationIconPosition: point(25px, 12px);
|
||||
infoNotificationsIconPosition: point(20px, 5px);
|
||||
infoSharedMediaIconPosition: point(20px, 24px);
|
||||
|
|