tdesktop/Telegram/SourceFiles/media/stories/media_stories_recent_views.cpp
2023-07-20 07:20:10 +04:00

189 lines
5.2 KiB
C++

/*
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 "media/stories/media_stories_recent_views.h"
#include "data/data_peer.h"
#include "main/main_session.h"
#include "media/stories/media_stories_controller.h"
#include "lang/lang_keys.h"
#include "ui/chat/group_call_userpics.h"
#include "ui/painter.h"
#include "ui/rp_widget.h"
#include "ui/userpic_view.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_media_view.h"
namespace Media::Stories {
namespace {
[[nodiscard]] rpl::producer<std::vector<Ui::GroupCallUser>> ContentByUsers(
const std::vector<not_null<PeerData*>> &list) {
struct Userpic {
not_null<PeerData*> peer;
mutable Ui::PeerUserpicView view;
mutable InMemoryKey uniqueKey;
};
struct State {
std::vector<Userpic> userpics;
std::vector<Ui::GroupCallUser> current;
base::has_weak_ptr guard;
bool someUserpicsNotLoaded = false;
bool scheduled = false;
};
static const auto size = st::storiesRecentViewsUserpics.size;
static const auto GenerateUserpic = [](Userpic &userpic) {
auto result = userpic.peer->generateUserpicImage(
userpic.view,
size * style::DevicePixelRatio());
result.setDevicePixelRatio(style::DevicePixelRatio());
return result;
};
static const auto RegenerateUserpics = [](not_null<State*> state) {
Expects(state->userpics.size() == state->current.size());
state->someUserpicsNotLoaded = false;
const auto count = int(state->userpics.size());
for (auto i = 0; i != count; ++i) {
auto &userpic = state->userpics[i];
auto &participant = state->current[i];
const auto peer = userpic.peer;
const auto key = peer->userpicUniqueKey(userpic.view);
if (peer->hasUserpic() && peer->useEmptyUserpic(userpic.view)) {
state->someUserpicsNotLoaded = true;
}
if (userpic.uniqueKey == key) {
continue;
}
participant.userpicKey = userpic.uniqueKey = key;
participant.userpic = GenerateUserpic(userpic);
}
};
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
const auto state = lifetime.make_state<State>();
const auto pushNext = [=] {
RegenerateUserpics(state);
consumer.put_next_copy(state->current);
};
for (const auto &peer : list) {
state->userpics.push_back(Userpic{
.peer = peer,
});
state->current.push_back(Ui::GroupCallUser{
.id = uint64(peer->id.value),
});
peer->loadUserpic();
}
pushNext();
if (!list.empty()) {
list.front()->session().downloaderTaskFinished(
) | rpl::filter([=] {
return state->someUserpicsNotLoaded && !state->scheduled;
}) | rpl::start_with_next([=] {
for (const auto &userpic : state->userpics) {
if (userpic.peer->userpicUniqueKey(userpic.view)
!= userpic.uniqueKey) {
state->scheduled = true;
crl::on_main(&state->guard, [=] {
state->scheduled = false;
pushNext();
});
return;
}
}
}, lifetime);
}
return lifetime;
};
}
} // namespace
RecentViews::RecentViews(not_null<Controller*> controller)
: _controller(controller) {
}
RecentViews::~RecentViews() = default;
void RecentViews::show(RecentViewsData data) {
if (_data == data) {
return;
}
const auto totalChanged = _text.isEmpty() || (_data.total != data.total);
const auto usersChanged = !_userpics || (_data.list != data.list);
_data = data;
if (!_data.valid) {
_text = {};
_userpics = nullptr;
_widget = nullptr;
return;
}
if (!_widget) {
const auto parent = _controller->wrap();
auto widget = std::make_unique<Ui::RpWidget>(parent);
const auto raw = widget.get();
raw->show();
_controller->layoutValue(
) | rpl::start_with_next([=](const Layout &layout) {
raw->setGeometry(layout.views);
}, raw->lifetime());
raw->paintRequest(
) | rpl::start_with_next([=](QRect clip) {
auto p = Painter(raw);
const auto skip = st::storiesRecentViewsSkip;
const auto full = _userpicsWidth + skip + _text.maxWidth();
const auto use = std::min(full, raw->width());
const auto ux = (raw->width() - use) / 2;
const auto height = st::storiesRecentViewsUserpics.size;
const auto uy = (raw->height() - height) / 2;
const auto tx = ux + _userpicsWidth + skip;
const auto ty = (raw->height() - st::normalFont->height) / 2;
_userpics->paint(p, ux, uy, height);
p.setPen(st::storiesComposeWhiteText);
_text.drawElided(p, tx, ty, use - _userpicsWidth - skip);
}, raw->lifetime());
_widget = std::move(widget);
}
if (totalChanged) {
_text.setText(st::defaultTextStyle, data.total
? tr::lng_stories_views(tr::now, lt_count, data.total)
: tr::lng_stories_no_views(tr::now));
}
if (!_userpics) {
_userpics = std::make_unique<Ui::GroupCallUserpics>(
st::storiesRecentViewsUserpics,
rpl::single(true),
[=] { _widget->update(); });
_userpics->widthValue() | rpl::start_with_next([=](int width) {
_userpicsWidth = width;
}, _widget->lifetime());
}
if (usersChanged) {
_userpicsLifetime = ContentByUsers(
data.list
) | rpl::start_with_next([=](
const std::vector<Ui::GroupCallUser> &list) {
_userpics->update(list, true);
});
}
}
} // namespace Media::Stories