tdesktop/Telegram/SourceFiles/api/api_invite_links.cpp

629 lines
17 KiB
C++
Raw Normal View History

/*
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 "api/api_invite_links.h"
#include "data/data_peer.h"
#include "data/data_chat.h"
#include "data/data_channel.h"
#include "data/data_session.h"
#include "data/data_changes.h"
#include "main/main_session.h"
#include "apiwrap.h"
namespace Api {
namespace {
constexpr auto kFirstPage = 10;
constexpr auto kPerPage = 50;
constexpr auto kJoinedFirstPage = 10;
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
2021-01-25 13:42:02 +00:00
// #TODO links
//JoinedByLinkSlice ParseJoinedByLinkSlice(
// not_null<PeerData*> peer,
// const MTPmessages_ChatInviteImporters &slice) {
// auto result = JoinedByLinkSlice();
// slice.match([&](const MTPDmessages_chatInviteImporters &data) {
// auto &owner = peer->session().data();
// owner.processUsers(data.vusers());
// result.count = data.vcount().v;
// result.users.reserve(data.vimporters().v.size());
// for (const auto importer : data.vimporters().v) {
// importer.match([&](const MTPDchatInviteImporter &data) {
// result.users.push_back({
// .user = owner.user(data.vuser_id().v),
// .date = data.vdate().v,
// });
// });
// }
// });
// return result;
//}
2021-01-19 10:56:01 +00:00
InviteLinks::InviteLinks(not_null<ApiWrap*> api) : _api(api) {
}
void InviteLinks::create(
not_null<PeerData*> peer,
Fn<void(Link)> done,
TimeId expireDate,
int usageLimit) {
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));
}
2021-02-02 10:10:38 +00:00
using Flag = MTPmessages_ExportChatInvite::Flag;
_api->request(MTPmessages_ExportChatInvite(
2021-02-02 10:10:38 +00:00
MTP_flags((revokeLegacyPermanent
? Flag::f_legacy_revoke_permanent
: Flag(0))
| (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) {
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) {
_createCallbacks.erase(peer);
}).send();
}
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;
2021-01-15 16:13:45 +00:00
const auto permanent = lookupPermanent(links);
2021-01-19 10:26:00 +00:00
const auto hadPermanent = (permanent != nullptr);
auto updateOldPermanent = Update{ .peer = peer };
if (link.permanent && hadPermanent) {
updateOldPermanent.was = permanent->link;
updateOldPermanent.now = *permanent;
updateOldPermanent.now->revoked = true;
links.links.erase(begin(links.links));
if (links.count > 0) {
--links.count;
}
}
2021-01-19 10:26:00 +00:00
// Must not dereference 'permanent' pointer after that.
++links.count;
2021-01-19 10:26:00 +00:00
if (hadPermanent && !link.permanent) {
2021-01-15 16:13:45 +00:00
links.links.insert(begin(links.links) + 1, link);
} else {
links.links.insert(begin(links.links), link);
}
2021-01-19 10:26:00 +00:00
if (link.permanent) {
editPermanentLink(peer, link.link);
}
notify(peer);
2021-01-19 10:26:00 +00:00
if (updateOldPermanent.now) {
_updates.fire(std::move(updateOldPermanent));
}
_updates.fire(Update{ .peer = peer, .now = link });
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 = LinkKey{ peer, link };
2021-01-19 10:26:00 +00:00
if (_deleteCallbacks.contains(key)) {
return;
} else if (const auto i = _editCallbacks.find(key)
; i != end(_editCallbacks)) {
if (done) {
i->second.push_back(std::move(done));
}
return;
}
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;
}
}
2021-01-15 16:13:45 +00:00
auto &callbacks = _editCallbacks[key];
if (done) {
callbacks.push_back(std::move(done));
}
2021-01-25 13:42:02 +00:00
// #TODO links
//using Flag = MTPmessages_EditExportedChatInvite::Flag;
//_api->request(MTPmessages_EditExportedChatInvite(
// 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) {
// 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 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)) {
// if (link.revoked && !j->revoked) {
// i->second.links.erase(j);
// if (i->second.count > 0) {
// --i->second.count;
// }
// } else {
// *j = link;
// }
// }
// }
// for (const auto &callback : *callbacks) {
// callback(link);
// }
// _updates.fire(Update{
// .peer = peer,
// .was = key.link,
// .now = link,
// });
// });
//}).fail([=](const RPCError &error) {
// _editCallbacks.erase(key);
//}).send();
}
void InviteLinks::revoke(
not_null<PeerData*> peer,
const QString &link,
Fn<void(Link)> done) {
performEdit(peer, link, std::move(done), true);
}
void InviteLinks::revokePermanent(
not_null<PeerData*> peer,
Fn<void(Link)> done) {
performCreate(peer, std::move(done), true);
}
2021-01-19 10:26:00 +00:00
void InviteLinks::destroy(
not_null<PeerData*> peer,
const QString &link,
Fn<void()> done) {
const auto key = LinkKey{ peer, link };
if (const auto i = _deleteCallbacks.find(key)
; i != end(_deleteCallbacks)) {
if (done) {
i->second.push_back(std::move(done));
}
return;
}
auto &callbacks = _deleteCallbacks[key];
if (done) {
callbacks.push_back(std::move(done));
}
2021-01-25 13:42:02 +00:00
// #TODO links
//_api->request(MTPmessages_DeleteExportedChatInvite(
// peer->input,
// MTP_string(link)
//)).done([=](const MTPBool &result) {
// const auto callbacks = _deleteCallbacks.take(key);
// if (callbacks) {
// for (const auto &callback : *callbacks) {
// callback();
// }
// }
// _updates.fire(Update{
// .peer = peer,
// .was = key.link,
// });
//}).fail([=](const RPCError &error) {
// _deleteCallbacks.erase(key);
//}).send();
2021-01-19 10:26:00 +00:00
}
void InviteLinks::destroyAllRevoked(
not_null<PeerData*> peer,
Fn<void()> done) {
if (const auto i = _deleteRevokedCallbacks.find(peer)
; i != end(_deleteRevokedCallbacks)) {
if (done) {
i->second.push_back(std::move(done));
}
return;
}
auto &callbacks = _deleteRevokedCallbacks[peer];
if (done) {
callbacks.push_back(std::move(done));
}
2021-01-25 13:42:02 +00:00
// #TODO links
//_api->request(MTPmessages_DeleteRevokedExportedChatInvites(
// peer->input
//)).done([=](const MTPBool &result) {
// if (const auto callbacks = _deleteRevokedCallbacks.take(peer)) {
// for (const auto &callback : *callbacks) {
// callback();
// }
// }
// _allRevokedDestroyed.fire_copy(peer);
//}).fail([=](const RPCError &error) {
//}).send();
2021-01-19 10:26:00 +00:00
}
void InviteLinks::requestLinks(not_null<PeerData*> peer) {
if (_firstSliceRequests.contains(peer)) {
return;
}
2021-01-25 13:42:02 +00:00
// #TODO links
//const auto requestId = _api->request(MTPmessages_GetExportedChatInvites(
// MTP_flags(0),
// peer->input,
// MTPInputUser(), // admin_id
// MTPint(), // offset_date
// MTPstring(), // offset_link
// MTP_int(kFirstPage)
//)).done([=](const MTPmessages_ExportedChatInvites &result) {
// _firstSliceRequests.remove(peer);
// 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);
}
2021-01-19 10:56:01 +00:00
std::optional<JoinedByLinkSlice> InviteLinks::lookupJoinedFirstSlice(
LinkKey key) const {
const auto i = _firstJoined.find(key);
2021-01-19 10:56:01 +00:00
return (i != end(_firstJoined))
? std::make_optional(i->second)
: std::nullopt;
}
std::optional<JoinedByLinkSlice> InviteLinks::joinedFirstSliceLoaded(
not_null<PeerData*> peer,
const QString &link) const {
return lookupJoinedFirstSlice({ peer, link });
}
rpl::producer<JoinedByLinkSlice> InviteLinks::joinedFirstSliceValue(
not_null<PeerData*> peer,
const QString &link,
int fullCount) {
const auto key = LinkKey{ peer, link };
2021-01-19 10:56:01 +00:00
auto current = lookupJoinedFirstSlice(key).value_or(JoinedByLinkSlice());
if (current.count == fullCount
&& (!fullCount || !current.users.empty())) {
return rpl::single(current);
}
current.count = fullCount;
const auto remove = int(current.users.size()) - current.count;
if (remove > 0) {
current.users.erase(end(current.users) - remove, end(current.users));
}
requestJoinedFirstSlice(key);
using namespace rpl::mappers;
return rpl::single(
current
) | rpl::then(_joinedFirstSliceLoaded.events(
) | rpl::filter(
_1 == key
) | rpl::map([=] {
2021-01-19 10:56:01 +00:00
return lookupJoinedFirstSlice(key).value_or(JoinedByLinkSlice());
}));
}
auto InviteLinks::updates(
not_null<PeerData*> peer) const -> rpl::producer<Update> {
return _updates.events() | rpl::filter([=](const Update &update) {
return update.peer == peer;
});
}
2021-01-19 10:26:00 +00:00
rpl::producer<> InviteLinks::allRevokedDestroyed(
not_null<PeerData*> peer) const {
using namespace rpl::mappers;
return _allRevokedDestroyed.events(
) | rpl::filter(
_1 == peer
) | rpl::to_empty;
}
void InviteLinks::requestJoinedFirstSlice(LinkKey key) {
2021-01-25 13:42:02 +00:00
//if (_firstJoinedRequests.contains(key)) { // #TODO links
// return;
//}
//const auto requestId = _api->request(MTPmessages_GetChatInviteImporters(
// key.peer->input,
// MTP_string(key.link),
// MTP_int(0), // offset_date
// MTP_inputUserEmpty(), // offset_user
// MTP_int(kJoinedFirstPage)
//)).done([=](const MTPmessages_ChatInviteImporters &result) {
// _firstJoinedRequests.remove(key);
// _firstJoined[key] = ParseJoinedByLinkSlice(key.peer, result);
// _joinedFirstSliceLoaded.fire_copy(key);
//}).fail([=](const RPCError &error) {
// _firstJoinedRequests.remove(key);
//}).send();
//_firstJoinedRequests.emplace(key, requestId);
}
void InviteLinks::setPermanent(
not_null<PeerData*> peer,
const MTPExportedChatInvite &invite) {
auto link = parse(peer, invite);
2021-01-15 11:42:26 +00:00
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;
2021-01-19 10:26:00 +00:00
auto updateOldPermanent = Update{ .peer = peer };
if (const auto permanent = lookupPermanent(links)) {
if (permanent->link == link.link) {
if (permanent->usage != link.usage) {
permanent->usage = link.usage;
2021-01-19 10:26:00 +00:00
_updates.fire(Update{
.peer = peer,
.was = link.link,
.now = *permanent
});
}
return;
}
2021-01-19 10:26:00 +00:00
updateOldPermanent.was = permanent->link;
updateOldPermanent.now = *permanent;
updateOldPermanent.now->revoked = true;
links.links.erase(begin(links.links));
if (links.count > 0) {
--links.count;
}
}
links.links.insert(begin(links.links), link);
2021-01-19 10:26:00 +00:00
editPermanentLink(peer, link.link);
notify(peer);
2021-01-19 10:26:00 +00:00
if (updateOldPermanent.now) {
_updates.fire(std::move(updateOldPermanent));
}
_updates.fire(Update{ .peer = peer, .now = link });
}
void InviteLinks::clearPermanent(not_null<PeerData*> peer) {
2021-01-19 10:26:00 +00:00
auto i = _firstSlices.find(peer);
if (i == end(_firstSlices)) {
return;
}
auto &links = i->second;
const auto permanent = lookupPermanent(links);
if (!permanent) {
return;
}
auto updateOldPermanent = Update{ .peer = peer };
updateOldPermanent.was = permanent->link;
updateOldPermanent.now = *permanent;
updateOldPermanent.now->revoked = true;
links.links.erase(begin(links.links));
if (links.count > 0) {
--links.count;
}
editPermanentLink(peer, QString());
notify(peer);
if (updateOldPermanent.now) {
_updates.fire(std::move(updateOldPermanent));
}
}
void InviteLinks::notify(not_null<PeerData*> peer) {
peer->session().changes().peerUpdated(
peer,
Data::PeerUpdate::Flag::InviteLinks);
}
2021-01-15 11:42:26 +00:00
auto InviteLinks::links(not_null<PeerData*> peer) const -> const Links & {
static const auto kEmpty = Links();
const auto i = _firstSlices.find(peer);
2021-01-15 11:42:26 +00:00
return (i != end(_firstSlices)) ? i->second : kEmpty;
}
2021-01-25 13:42:02 +00:00
// #TODO 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) {
// peer->session().data().processUsers(data.vusers());
// result.count = data.vcount().v;
// for (const auto &invite : data.vinvites().v) {
// 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,
.startDate = data.vstart_date().value_or_empty(),
.expireDate = data.vexpire_date().value_or_empty(),
.usageLimit = data.vusage_limit().value_or_empty(),
.usage = data.vusage().value_or_empty(),
.permanent = true,//data.is_permanent(), // #TODO links
.revoked = data.is_revoked(),
};
});
}
void InviteLinks::requestMoreLinks(
not_null<PeerData*> peer,
2021-01-19 10:56:01 +00:00
TimeId lastDate,
const QString &lastLink,
bool revoked,
Fn<void(Links)> done) {
2021-01-25 13:42:02 +00:00
// #TODO links
//using Flag = MTPmessages_GetExportedChatInvites::Flag;
//_api->request(MTPmessages_GetExportedChatInvites(
// MTP_flags(Flag::f_offset_link
// | (revoked ? Flag::f_revoked : Flag(0))),
// peer->input,
// MTPInputUser(), // admin_id,
// MTP_int(lastDate),
// MTP_string(lastLink),
// MTP_int(kPerPage)
//)).done([=](const MTPmessages_ExportedChatInvites &result) {
// auto slice = parseSlice(peer, result);
// RemovePermanent(slice);
// done(std::move(slice));
//}).fail([=](const RPCError &error) {
// done(Links());
//}).send();
}
void InviteLinks::editPermanentLink(
not_null<PeerData*> peer,
const QString &link) {
if (const auto chat = peer->asChat()) {
chat->setInviteLink(link);
} else if (const auto channel = peer->asChannel()) {
channel->setInviteLink(link);
} else {
Unexpected("Peer in InviteLinks::editMainLink.");
}
}
} // namespace Api