Update link rows in Manage Invite Links.

This commit is contained in:
John Preston 2021-01-19 12:41:22 +04:00
parent 97fb310f54
commit 144bad6c74
3 changed files with 159 additions and 12 deletions

View file

@ -123,10 +123,16 @@ auto InviteLinks::prepend(
auto &links = i->second;
const auto permanent = lookupPermanent(links);
if (link.permanent) {
auto update = Update{ .peer = peer };
if (permanent) {
update.was = permanent->link;
permanent->revoked = true;
}
editPermanentLink(peer, link.link);
if (permanent) {
update.now = *permanent;
_updates.fire(std::move(update));
}
}
++links.count;
if (permanent && !link.permanent) {
@ -135,6 +141,7 @@ auto InviteLinks::prepend(
links.links.insert(begin(links.links), link);
}
notify(peer);
_updates.fire(Update{ .peer = peer, .now = link });
return link;
}
@ -205,6 +212,11 @@ void InviteLinks::performEdit(
for (const auto &callback : *callbacks) {
callback(link);
}
_updates.fire(Update{
.peer = peer,
.was = key.link,
.now = link
});
});
}).fail([=](const RPCError &error) {
_editCallbacks.erase(key);
@ -298,6 +310,13 @@ rpl::producer<JoinedByLinkSlice> InviteLinks::joinedFirstSliceValue(
}));
}
auto InviteLinks::updates(
not_null<PeerData*> peer) const -> rpl::producer<Update> {
return _updates.events() | rpl::filter([=](const Update &update) {
return update.peer == peer;
});
}
void InviteLinks::requestJoinedFirstSlice(LinkKey key) {
if (_firstJoinedRequests.contains(key)) {
return;
@ -422,7 +441,6 @@ auto InviteLinks::parse(
.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(),
};
});

View file

@ -20,7 +20,6 @@ struct InviteLink {
int usageLimit = 0;
int usage = 0;
bool permanent = false;
bool expired = false;
bool revoked = false;
};
@ -39,12 +38,19 @@ struct JoinedByLinkSlice {
int count = 0;
};
struct InviteLinkUpdate {
not_null<PeerData*> peer;
QString was;
std::optional<InviteLink> now;
};
class InviteLinks final {
public:
explicit InviteLinks(not_null<ApiWrap*> api);
using Link = InviteLink;
using Links = PeerInviteLinks;
using Update = InviteLinkUpdate;
void create(
not_null<PeerData*> peer,
@ -77,6 +83,8 @@ public:
not_null<PeerData*> peer,
const QString &link,
int fullCount);
[[nodiscard]] rpl::producer<Update> updates(
not_null<PeerData*> peer) const;
void requestMoreLinks(
not_null<PeerData*> peer,
@ -152,6 +160,8 @@ private:
std::vector<Fn<void(Link)>>> _createCallbacks;
base::flat_map<LinkKey, std::vector<Fn<void(Link)>>> _editCallbacks;
rpl::event_stream<Update> _updates;
};
} // namespace Api

View file

@ -53,6 +53,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace {
constexpr auto kPreloadPages = 2;
constexpr auto kFullArcLength = 360 * 16;
enum class Color {
Permanent,
@ -100,9 +101,11 @@ public:
const InviteLinkData &data,
TimeId now);
void update(const InviteLinkData &data);
void update(const InviteLinkData &data, TimeId now);
void updateExpireProgress(TimeId now);
[[nodiscard]] InviteLinkData data() const;
[[nodiscard]] crl::time updateExpireIn() const;
QString generateName() override;
QString generateShortName() override;
@ -159,7 +162,6 @@ private:
[[nodiscard]] Color ComputeColor(
const InviteLinkData &link,
float64 progress) {
const auto startDate = link.startDate ? link.startDate : link.date;
return link.revoked
? Color::Revoked
: (progress >= 1.)
@ -171,8 +173,20 @@ private:
: Color::Permanent;
}
[[nodiscard]] QString ComputeStatus(const InviteLinkData &link) {
return "nothing";
[[nodiscard]] QString ComputeStatus(const InviteLinkData &link, TimeId now) {
auto result = link.usage
? tr::lng_group_invite_joined(tr::now, lt_count_decimal, link.usage)
: tr::lng_group_invite_no_joined(tr::now);
const auto add = [&](const QString &text) {
result += QString::fromUtf8(" \xE2\xB8\xB1 ") + text;
};
if (link.revoked) {
add(tr::lng_group_invite_link_revoked(tr::now));
} else if ((link.usageLimit > 0 && link.usage >= link.usageLimit)
|| (link.expireDate > 0 && now >= link.expireDate)) {
add(tr::lng_group_invite_link_expired(tr::now));
}
return result;
}
void CopyLink(const QString &link) {
@ -338,21 +352,45 @@ Row::Row(
, _data(data)
, _progressTillExpire(ComputeProgress(data, now))
, _color(ComputeColor(data, _progressTillExpire)) {
setCustomStatus(ComputeStatus(data));
setCustomStatus(ComputeStatus(data, now));
}
void Row::update(const InviteLinkData &data) {
void Row::update(const InviteLinkData &data, TimeId now) {
_data = data;
_progressTillExpire = ComputeProgress(data, base::unixtime::now());
_progressTillExpire = ComputeProgress(data, now);
_color = ComputeColor(data, _progressTillExpire);
setCustomStatus(ComputeStatus(data));
setCustomStatus(ComputeStatus(data, now));
_delegate->rowUpdateRow(this);
}
void Row::updateExpireProgress(TimeId now) {
const auto updated = ComputeProgress(_data, now);
if (std::round(_progressTillExpire * 360) != std::round(updated * 360)) {
_progressTillExpire = updated;
const auto color = ComputeColor(_data, _progressTillExpire);
if (_color != color) {
_color = color;
setCustomStatus(ComputeStatus(_data, now));
}
_delegate->rowUpdateRow(this);
}
}
InviteLinkData Row::data() const {
return _data;
}
crl::time Row::updateExpireIn() const {
if (_color != Color::Expiring && _color != Color::ExpireSoon) {
return 0;
}
const auto start = _data.startDate ? _data.startDate : _data.date;
if (_data.expireDate <= start) {
return 0;
}
return std::round((_data.expireDate - start) * crl::time(1000) / 720.);
}
QString Row::generateName() {
auto result = _data.link;
return result.replace(qstr("https://"), QString());
@ -425,6 +463,13 @@ public:
private:
void appendRow(const InviteLinkData &data, TimeId now);
void prependRow(const InviteLinkData &data, TimeId now);
void updateRow(const InviteLinkData &data, TimeId now);
bool removeRow(const QString &link);
void checkExpiringTimer(not_null<Row*> row);
void expiringProgressTimer();
[[nodiscard]] base::unique_qptr<Ui::PopupMenu> createRowContextMenu(
QWidget *parent,
not_null<PeerListRow*> row);
@ -433,6 +478,9 @@ private:
bool _revoked = false;
base::unique_qptr<Ui::PopupMenu> _menu;
base::flat_set<not_null<Row*>> _expiringRows;
base::Timer _updateExpiringTimer;
std::array<QImage, int(Color::Count)> _icons;
rpl::lifetime _lifetime;
@ -440,13 +488,31 @@ private:
Controller::Controller(not_null<PeerData*> peer, bool revoked)
: _peer(peer)
, _revoked(revoked) {
, _revoked(revoked)
, _updateExpiringTimer([=] { expiringProgressTimer(); }) {
style::PaletteChanged(
) | rpl::start_with_next([=] {
for (auto &image : _icons) {
image = QImage();
}
}, _lifetime);
peer->session().api().inviteLinks().updates(
peer
) | rpl::start_with_next([=](const Api::InviteLinkUpdate &update) {
const auto now = base::unixtime::now();
if (!update.now
|| (!update.was.isEmpty() && update.now->revoked != _revoked)) {
if (removeRow(update.was)) {
delegate()->peerListRefreshRows();
}
} else if (update.was.isEmpty()) {
prependRow(*update.now, now);
delegate()->peerListRefreshRows();
} else {
updateRow(*update.now, now);
}
}, _lifetime);
}
void Controller::prepare() {
@ -538,6 +604,60 @@ void Controller::appendRow(const InviteLinkData &data, TimeId now) {
delegate()->peerListAppendRow(std::make_unique<Row>(this, data, now));
}
void Controller::prependRow(const InviteLinkData &data, TimeId now) {
delegate()->peerListPrependRow(std::make_unique<Row>(this, data, now));
}
void Controller::updateRow(const InviteLinkData &data, TimeId now) {
if (const auto row = delegate()->peerListFindRow(ComputeRowId(data))) {
const auto real = static_cast<Row*>(row);
real->update(data, now);
checkExpiringTimer(real);
delegate()->peerListUpdateRow(row);
}
}
bool Controller::removeRow(const QString &link) {
if (const auto row = delegate()->peerListFindRow(ComputeRowId(link))) {
delegate()->peerListRemoveRow(row);
return true;
}
return false;
}
void Controller::checkExpiringTimer(not_null<Row*> row) {
const auto updateIn = row->updateExpireIn();
if (updateIn > 0) {
_expiringRows.emplace(row);
if (!_updateExpiringTimer.isActive()
|| updateIn < _updateExpiringTimer.remainingTime()) {
_updateExpiringTimer.callOnce(updateIn);
}
} else {
_expiringRows.remove(row);
}
}
void Controller::expiringProgressTimer() {
const auto now = base::unixtime::now();
auto minimalIn = 0;
for (auto i = begin(_expiringRows); i != end(_expiringRows);) {
(*i)->updateExpireProgress(now);
const auto updateIn = (*i)->updateExpireIn();
if (!updateIn) {
i = _expiringRows.erase(i);
} else {
++i;
if (!minimalIn || minimalIn > updateIn) {
minimalIn = updateIn;
}
}
}
if (minimalIn) {
_updateExpiringTimer.callOnce(minimalIn);
}
}
void Controller::rowUpdateRow(not_null<Row*> row) {
delegate()->peerListUpdateRow(row);
}
@ -578,7 +698,6 @@ void Controller::rowPaintIcon(
}
p.drawImage(x + skip, y + skip, icon);
if (progress >= 0. && progress < 1.) {
const auto kFullArcLength = 360 * 16;
const auto stroke = st::inviteLinkIconStroke;
auto hq = PainterHighQualityEnabler(p);
auto pen = QPen((*bg)->c);