mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Top peer realtime badges.
This commit is contained in:
parent
b259c566b7
commit
56e28feb00
7 changed files with 205 additions and 20 deletions
|
@ -1108,7 +1108,7 @@ void Widget::updateSuggestions(anim::type animated) {
|
||||||
} else if (suggest && !_suggestions) {
|
} else if (suggest && !_suggestions) {
|
||||||
_suggestions = std::make_unique<Suggestions>(
|
_suggestions = std::make_unique<Suggestions>(
|
||||||
this,
|
this,
|
||||||
rpl::single(TopPeersContent(&session())));
|
TopPeersContent(&session()));
|
||||||
|
|
||||||
_suggestions->topPeerChosen(
|
_suggestions->topPeerChosen(
|
||||||
) | rpl::start_with_next([=](PeerId id) {
|
) | rpl::start_with_next([=](PeerId id) {
|
||||||
|
|
|
@ -7,8 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "dialogs/ui/dialogs_suggestions.h"
|
#include "dialogs/ui/dialogs_suggestions.h"
|
||||||
|
|
||||||
|
#include "base/unixtime.h"
|
||||||
#include "data/components/top_peers.h"
|
#include "data/components/top_peers.h"
|
||||||
|
#include "data/data_changes.h"
|
||||||
|
#include "data/data_peer_values.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
|
#include "history/history.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
|
@ -82,17 +87,146 @@ object_ptr<Ui::RpWidget> Suggestions::setupDivider() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TopPeersList TopPeersContent(not_null<Main::Session*> session) {
|
rpl::producer<TopPeersList> TopPeersContent(
|
||||||
auto base = TopPeersList();
|
not_null<Main::Session*> session) {
|
||||||
const auto top = session->topPeers().list();
|
return [=](auto consumer) {
|
||||||
for (const auto &peer : top) {
|
auto lifetime = rpl::lifetime();
|
||||||
base.entries.push_back(TopPeersEntry{
|
|
||||||
.id = peer->id.value,
|
struct Entry {
|
||||||
.name = peer->shortName(),
|
not_null<History*> history;
|
||||||
.userpic = Ui::MakeUserpicThumbnail(peer),
|
int index = 0;
|
||||||
});
|
};
|
||||||
}
|
struct State {
|
||||||
return base;
|
TopPeersList data;
|
||||||
|
base::flat_map<not_null<PeerData*>, Entry> indices;
|
||||||
|
base::has_weak_ptr guard;
|
||||||
|
bool scheduled = true;
|
||||||
|
};
|
||||||
|
auto state = lifetime.make_state<State>();
|
||||||
|
const auto top = session->topPeers().list();
|
||||||
|
auto &entries = state->data.entries;
|
||||||
|
auto &indices = state->indices;
|
||||||
|
entries.reserve(top.size());
|
||||||
|
indices.reserve(top.size());
|
||||||
|
const auto now = base::unixtime::now();
|
||||||
|
for (const auto &peer : top) {
|
||||||
|
const auto user = peer->asUser();
|
||||||
|
const auto self = user && user->isSelf();
|
||||||
|
const auto history = peer->owner().history(peer);
|
||||||
|
const auto badges = history->chatListBadgesState();
|
||||||
|
entries.push_back({
|
||||||
|
.id = peer->id.value,
|
||||||
|
.name = (self
|
||||||
|
? tr::lng_saved_messages(tr::now)
|
||||||
|
: peer->shortName()),
|
||||||
|
.userpic = (self
|
||||||
|
? Ui::MakeSavedMessagesThumbnail()
|
||||||
|
: Ui::MakeUserpicThumbnail(peer)),
|
||||||
|
.badge = uint32(badges.unreadCounter),
|
||||||
|
.unread = badges.unread,
|
||||||
|
.muted = !self && history->muted(),
|
||||||
|
.online = user && !self && Data::IsUserOnline(user, now),
|
||||||
|
});
|
||||||
|
if (entries.back().online) {
|
||||||
|
user->owner().watchForOffline(user, now);
|
||||||
|
}
|
||||||
|
indices.emplace(peer, Entry{
|
||||||
|
.history = peer->owner().history(peer),
|
||||||
|
.index = int(entries.size()) - 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto push = [=] {
|
||||||
|
if (!state->scheduled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state->scheduled = false;
|
||||||
|
consumer.put_next_copy(state->data);
|
||||||
|
};
|
||||||
|
const auto schedule = [=] {
|
||||||
|
if (state->scheduled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state->scheduled = true;
|
||||||
|
crl::on_main(&state->guard, push);
|
||||||
|
};
|
||||||
|
|
||||||
|
using Flag = Data::PeerUpdate::Flag;
|
||||||
|
session->changes().peerUpdates(
|
||||||
|
Flag::Name
|
||||||
|
| Flag::Photo
|
||||||
|
| Flag::Notifications
|
||||||
|
| Flag::OnlineStatus
|
||||||
|
) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
|
||||||
|
const auto peer = update.peer;
|
||||||
|
if (peer->isSelf()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto i = state->indices.find(peer);
|
||||||
|
if (i == end(state->indices)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto changed = false;
|
||||||
|
auto &entry = state->data.entries[i->second.index];
|
||||||
|
const auto flags = update.flags;
|
||||||
|
if (flags & Flag::Name) {
|
||||||
|
const auto now = peer->shortName();
|
||||||
|
if (entry.name != now) {
|
||||||
|
entry.name = now;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flags & Flag::Photo) {
|
||||||
|
entry.userpic = Ui::MakeUserpicThumbnail(peer);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (flags & Flag::Notifications) {
|
||||||
|
const auto now = i->second.history->muted();
|
||||||
|
if (entry.muted != now) {
|
||||||
|
entry.muted = now;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flags & Flag::OnlineStatus) {
|
||||||
|
if (const auto user = peer->asUser()) {
|
||||||
|
const auto now = base::unixtime::now();
|
||||||
|
const auto value = Data::IsUserOnline(user, now);
|
||||||
|
if (entry.online != value) {
|
||||||
|
entry.online = value;
|
||||||
|
changed = true;
|
||||||
|
if (value) {
|
||||||
|
user->owner().watchForOffline(user, now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
schedule();
|
||||||
|
}
|
||||||
|
}, lifetime);
|
||||||
|
|
||||||
|
session->data().unreadBadgeChanges(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
auto changed = false;
|
||||||
|
auto &entries = state->data.entries;
|
||||||
|
for (const auto &[peer, data] : state->indices) {
|
||||||
|
const auto badges = data.history->chatListBadgesState();
|
||||||
|
auto &entry = entries[data.index];
|
||||||
|
if (entry.badge != badges.unreadCounter
|
||||||
|
|| entry.unread != badges.unread) {
|
||||||
|
entry.badge = badges.unreadCounter;
|
||||||
|
entry.unread = badges.unread;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
schedule();
|
||||||
|
}
|
||||||
|
}, lifetime);
|
||||||
|
|
||||||
|
push();
|
||||||
|
return lifetime;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Dialogs
|
} // namespace Dialogs
|
||||||
|
|
|
@ -53,7 +53,7 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] TopPeersList TopPeersContent(
|
[[nodiscard]] rpl::producer<TopPeersList> TopPeersContent(
|
||||||
not_null<Main::Session*> session);
|
not_null<Main::Session*> session);
|
||||||
|
|
||||||
} // namespace Dialogs
|
} // namespace Dialogs
|
||||||
|
|
|
@ -445,7 +445,7 @@ void TopPeersStrip::updateSelected() {
|
||||||
const auto p = mapFromGlobal(_lastMousePosition);
|
const auto p = mapFromGlobal(_lastMousePosition);
|
||||||
const auto x = p.x();
|
const auto x = p.x();
|
||||||
const auto single = st.photoLeft * 2 + st.photo;
|
const auto single = st.photoLeft * 2 + st.photo;
|
||||||
const auto index = (x - _scrollLeft) / single;
|
const auto index = (_scrollLeft + x) / single;
|
||||||
const auto selected = (index < 0 || index >= _entries.size())
|
const auto selected = (index < 0 || index >= _entries.size())
|
||||||
? -1
|
? -1
|
||||||
: index;
|
: index;
|
||||||
|
|
|
@ -22,6 +22,7 @@ struct TopPeersEntry {
|
||||||
QString name;
|
QString name;
|
||||||
std::shared_ptr<Ui::DynamicImage> userpic;
|
std::shared_ptr<Ui::DynamicImage> userpic;
|
||||||
uint32 badge : 28 = 0;
|
uint32 badge : 28 = 0;
|
||||||
|
uint32 unread : 1 = 0;
|
||||||
uint32 muted : 1 = 0;
|
uint32 muted : 1 = 0;
|
||||||
uint32 online : 1 = 0;
|
uint32 online : 1 = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_photo_media.h"
|
#include "data/data_photo_media.h"
|
||||||
#include "data/data_story.h"
|
#include "data/data_story.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
#include "ui/empty_userpic.h"
|
||||||
#include "ui/dynamic_image.h"
|
#include "ui/dynamic_image.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/userpic_view.h"
|
#include "ui/userpic_view.h"
|
||||||
|
@ -39,6 +40,7 @@ private:
|
||||||
Ui::PeerUserpicView view;
|
Ui::PeerUserpicView view;
|
||||||
Fn<void()> callback;
|
Fn<void()> callback;
|
||||||
InMemoryKey key;
|
InMemoryKey key;
|
||||||
|
int paletteVersion = 0;
|
||||||
rpl::lifetime photoLifetime;
|
rpl::lifetime photoLifetime;
|
||||||
rpl::lifetime downloadLifetime;
|
rpl::lifetime downloadLifetime;
|
||||||
};
|
};
|
||||||
|
@ -117,6 +119,17 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SavedMessagesUserpic final : public DynamicImage {
|
||||||
|
public:
|
||||||
|
QImage image(int size) override;
|
||||||
|
void subscribeToUpdates(Fn<void()> callback) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QImage _frame;
|
||||||
|
int _paletteVersion = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
PeerUserpic::PeerUserpic(not_null<PeerData*> peer, bool forceRound)
|
PeerUserpic::PeerUserpic(not_null<PeerData*> peer, bool forceRound)
|
||||||
: _peer(peer)
|
: _peer(peer)
|
||||||
, _forceRound(forceRound) {
|
, _forceRound(forceRound) {
|
||||||
|
@ -127,13 +140,21 @@ QImage PeerUserpic::image(int size) {
|
||||||
|
|
||||||
const auto good = (_frame.width() == size * _frame.devicePixelRatio());
|
const auto good = (_frame.width() == size * _frame.devicePixelRatio());
|
||||||
const auto key = _peer->userpicUniqueKey(_subscribed->view);
|
const auto key = _peer->userpicUniqueKey(_subscribed->view);
|
||||||
if (!good || (_subscribed->key != key && !waitingUserpicLoad())) {
|
const auto paletteVersion = style::PaletteVersion();
|
||||||
const auto ratio = style::DevicePixelRatio();
|
if (!good
|
||||||
|
|| (_subscribed->paletteVersion != paletteVersion
|
||||||
|
&& _peer->useEmptyUserpic(_subscribed->view))
|
||||||
|
|| (_subscribed->key != key && !waitingUserpicLoad())) {
|
||||||
_subscribed->key = key;
|
_subscribed->key = key;
|
||||||
_frame = QImage(
|
_subscribed->paletteVersion = paletteVersion;
|
||||||
QSize(size, size) * ratio,
|
|
||||||
QImage::Format_ARGB32_Premultiplied);
|
const auto ratio = style::DevicePixelRatio();
|
||||||
_frame.setDevicePixelRatio(ratio);
|
if (!good) {
|
||||||
|
_frame = QImage(
|
||||||
|
QSize(size, size) * ratio,
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
|
_frame.setDevicePixelRatio(ratio);
|
||||||
|
}
|
||||||
_frame.fill(Qt::transparent);
|
_frame.fill(Qt::transparent);
|
||||||
|
|
||||||
auto p = Painter(&_frame);
|
auto p = Painter(&_frame);
|
||||||
|
@ -313,6 +334,30 @@ QImage EmptyThumbnail::image(int size) {
|
||||||
void EmptyThumbnail::subscribeToUpdates(Fn<void()> callback) {
|
void EmptyThumbnail::subscribeToUpdates(Fn<void()> callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QImage SavedMessagesUserpic::image(int size) {
|
||||||
|
const auto good = (_frame.width() == size * _frame.devicePixelRatio());
|
||||||
|
const auto paletteVersion = style::PaletteVersion();
|
||||||
|
if (!good || _paletteVersion != paletteVersion) {
|
||||||
|
_paletteVersion = paletteVersion;
|
||||||
|
|
||||||
|
const auto ratio = style::DevicePixelRatio();
|
||||||
|
if (!good) {
|
||||||
|
_frame = QImage(
|
||||||
|
QSize(size, size) * ratio,
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
|
_frame.setDevicePixelRatio(ratio);
|
||||||
|
}
|
||||||
|
_frame.fill(Qt::transparent);
|
||||||
|
|
||||||
|
auto p = Painter(&_frame);
|
||||||
|
Ui::EmptyUserpic::PaintSavedMessages(p, 0, 0, size, size);
|
||||||
|
}
|
||||||
|
return _frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SavedMessagesUserpic::subscribeToUpdates(Fn<void()> callback) {
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::shared_ptr<DynamicImage> MakeUserpicThumbnail(
|
std::shared_ptr<DynamicImage> MakeUserpicThumbnail(
|
||||||
|
@ -321,6 +366,10 @@ std::shared_ptr<DynamicImage> MakeUserpicThumbnail(
|
||||||
return std::make_shared<PeerUserpic>(peer, forceRound);
|
return std::make_shared<PeerUserpic>(peer, forceRound);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<DynamicImage> MakeSavedMessagesThumbnail() {
|
||||||
|
return std::make_shared<SavedMessagesUserpic>();
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<DynamicImage> MakeStoryThumbnail(
|
std::shared_ptr<DynamicImage> MakeStoryThumbnail(
|
||||||
not_null<Data::Story*> story) {
|
not_null<Data::Story*> story) {
|
||||||
using Result = std::shared_ptr<DynamicImage>;
|
using Result = std::shared_ptr<DynamicImage>;
|
||||||
|
|
|
@ -20,6 +20,7 @@ class DynamicImage;
|
||||||
[[nodiscard]] std::shared_ptr<DynamicImage> MakeUserpicThumbnail(
|
[[nodiscard]] std::shared_ptr<DynamicImage> MakeUserpicThumbnail(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
bool forceRound = false);
|
bool forceRound = false);
|
||||||
|
[[nodiscard]] std::shared_ptr<DynamicImage> MakeSavedMessagesThumbnail();
|
||||||
[[nodiscard]] std::shared_ptr<DynamicImage> MakeStoryThumbnail(
|
[[nodiscard]] std::shared_ptr<DynamicImage> MakeStoryThumbnail(
|
||||||
not_null<Data::Story*> story);
|
not_null<Data::Story*> story);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue