Implemented list of credit history entries.

This commit is contained in:
23rd 2024-05-20 19:13:22 +03:00 committed by John Preston
parent 65384d54f1
commit 154fe63b43
3 changed files with 319 additions and 0 deletions

View file

@ -7,9 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "info/statistics/info_statistics_list_controllers.h"
#include "api/api_credits.h"
#include "api/api_statistics.h"
#include "boxes/peer_list_controllers.h"
#include "data/data_channel.h"
#include "data/data_credits.h"
#include "data/data_session.h"
#include "data/data_stories.h"
#include "data/data_user.h"
@ -26,7 +28,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/popup_menu.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "styles/style_credits.h"
#include "styles/style_dialogs.h" // dialogsStoriesFull.
#include "styles/style_intro.h" // introFragmentIcon.
#include "styles/style_layers.h" // boxRowPadding.
#include "styles/style_menu_icons.h"
#include "styles/style_settings.h"
#include "styles/style_statistics.h"
@ -113,6 +118,15 @@ struct BoostsDescriptor final {
not_null<PeerData*> peer;
};
struct CreditsDescriptor final {
Data::CreditsStatusSlice firstSlice;
Fn<void(const Data::CreditsHistoryEntry &)> entryClickedCallback;
not_null<PeerData*> peer;
not_null<QImage*> creditIcon;
bool in = false;
bool out = false;
};
class PeerListRowWithFullId : public PeerListRow {
public:
PeerListRowWithFullId(
@ -692,6 +706,254 @@ rpl::producer<int> BoostsController::totalBoostsValue() const {
return _totalBoosts.value();
}
class CreditsRow final : public PeerListRow {
public:
struct Descriptor final {
Data::CreditsHistoryEntry entry;
not_null<QImage*> creditIcon;
int rowHeight = 0;
};
CreditsRow(not_null<PeerData*> peer, const Descriptor &descriptor);
CreditsRow(const Descriptor &descriptor);
[[nodiscard]] const Data::CreditsHistoryEntry &entry() const;
[[nodiscard]] QString generateName() override;
[[nodiscard]] PaintRoundImageCallback generatePaintUserpicCallback(
bool forceRound) override;
QSize rightActionSize() const override;
QMargins rightActionMargins() const override;
bool rightActionDisabled() const override;
void rightActionPaint(
Painter &p,
int x,
int y,
int outerWidth,
bool selected,
bool actionSelected) override;
private:
void init();
const Data::CreditsHistoryEntry _entry;
not_null<QImage*> const _creditIcon;
const int _rowHeight;
Ui::EmptyUserpic _userpic;
Ui::Text::String _rightText;
};
CreditsRow::CreditsRow(not_null<PeerData*> peer, const Descriptor &descriptor)
: PeerListRow(peer, UniqueRowIdFromString(descriptor.entry.id))
, _entry(descriptor.entry)
, _creditIcon(descriptor.creditIcon)
, _rowHeight(descriptor.rowHeight)
, _userpic(Ui::EmptyUserpic::UserpicColor(0), QString()) {
init();
}
CreditsRow::CreditsRow(const Descriptor &descriptor)
: PeerListRow(UniqueRowIdFromString(descriptor.entry.id))
, _entry(descriptor.entry)
, _creditIcon(descriptor.creditIcon)
, _rowHeight(descriptor.rowHeight)
, _userpic(
[&]() -> Ui::EmptyUserpic::BgColors {
switch (descriptor.entry.peerType) {
case Data::CreditsHistoryEntry::PeerType::Peer:
return Ui::EmptyUserpic::UserpicColor(0);
case Data::CreditsHistoryEntry::PeerType::AppStore:
return { st::historyPeer7UserpicBg, st::historyPeer7UserpicBg2 };
case Data::CreditsHistoryEntry::PeerType::PlayMarket:
return { st::historyPeer2UserpicBg, st::historyPeer2UserpicBg2 };
case Data::CreditsHistoryEntry::PeerType::Fragment:
return { st::historyPeer8UserpicBg, st::historyPeer8UserpicBg2 };
}
Unexpected("Unknown peer type.");
}(),
QString()) {
init();
}
void CreditsRow::init() {
PeerListRow::setCustomStatus(langDateTimeFull(_entry.date));
{
constexpr auto kMinus = QChar(0x2212);
_rightText.setText(
st::semiboldTextStyle,
(PeerListRow::special() ? QChar('+') : kMinus)
+ Lang::FormatCountDecimal(_entry.credits));
}
}
const Data::CreditsHistoryEntry &CreditsRow::entry() const {
return _entry;
}
QString CreditsRow::generateName() {
return !PeerListRow::special()
? PeerListRow::generateName()
: (_entry.peerType == Data::CreditsHistoryEntry::PeerType::Fragment)
? tr::lng_bot_username_description1_link(tr::now)
: tr::lng_credits_summary_history_entry_inner_in(tr::now);
}
PaintRoundImageCallback CreditsRow::generatePaintUserpicCallback(bool force) {
if (!PeerListRow::special()) {
return PeerListRow::generatePaintUserpicCallback(force);
}
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
_userpic.paintCircle(p, x, y, outerWidth, size);
using PeerType = Data::CreditsHistoryEntry::PeerType;
((_entry.peerType == PeerType::AppStore)
? st::sessionIconiPhone
: (_entry.peerType == PeerType::PlayMarket)
? st::sessionIconAndroid
: st::introFragmentIcon).paintInCenter(p, { x, y, size, size });
};
}
QSize CreditsRow::rightActionSize() const {
return QSize(
_rightText.maxWidth()
+ (_creditIcon->width() / style::DevicePixelRatio())
+ st::creditsHistoryRightSkip
+ _rightText.style()->font->spacew * 2,
_rowHeight);
}
QMargins CreditsRow::rightActionMargins() const {
return QMargins(0, 0, st::boxRowPadding.right(), 0);
}
bool CreditsRow::rightActionDisabled() const {
return true;
}
void CreditsRow::rightActionPaint(
Painter &p,
int x,
int y,
int outerWidth,
bool selected,
bool actionSelected) {
const auto &font = _rightText.style()->font;
y += _rowHeight / 2;
p.setPen(PeerListRow::special()
? st::boxTextFgGood
: st::menuIconAttentionColor);
x += st::creditsHistoryRightSkip;
_rightText.draw(p, Ui::Text::PaintContext{
.position = QPoint(x, y - font->height / 2),
.availableWidth = outerWidth,
.outerWidth = outerWidth,
});
x += _rightText.maxWidth() + font->spacew * 2;
p.drawImage(
x,
y -(_creditIcon->height() / style::DevicePixelRatio()) / 2,
*_creditIcon);
}
class CreditsController final : public PeerListController {
public:
explicit CreditsController(CreditsDescriptor d);
Main::Session &session() const override;
void prepare() override;
void rowClicked(not_null<PeerListRow*> row) override;
void loadMoreRows() override;
[[nodiscard]] bool skipRequest() const;
void requestNext();
[[nodiscard]] rpl::producer<bool> allLoadedValue() const;
private:
void applySlice(const Data::CreditsStatusSlice &slice);
const not_null<Main::Session*> _session;
Fn<void(const Data::CreditsHistoryEntry &)> _entryClickedCallback;
not_null<QImage*> const _creditIcon;
Api::CreditsHistory _api;
Data::CreditsStatusSlice _firstSlice;
Data::CreditsStatusSlice::OffsetToken _apiToken;
rpl::variable<bool> _allLoaded = false;
bool _requesting = false;
};
CreditsController::CreditsController(CreditsDescriptor d)
: _session(&d.peer->session())
, _entryClickedCallback(std::move(d.entryClickedCallback))
, _creditIcon(d.creditIcon)
, _api(d.peer, d.in, d.out)
, _firstSlice(std::move(d.firstSlice)) {
PeerListController::setStyleOverrides(&st::boostsListBox);
}
Main::Session &CreditsController::session() const {
return *_session;
}
bool CreditsController::skipRequest() const {
return _requesting || _allLoaded.current();
}
void CreditsController::requestNext() {
_requesting = true;
_api.request(_apiToken, [=](const Data::CreditsStatusSlice &s) {
_requesting = false;
applySlice(s);
});
}
void CreditsController::prepare() {
applySlice(base::take(_firstSlice));
delegate()->peerListRefreshRows();
}
void CreditsController::loadMoreRows() {
}
void CreditsController::applySlice(const Data::CreditsStatusSlice &slice) {
_allLoaded = slice.allLoaded;
_apiToken = slice.token;
for (const auto &item : slice.list) {
auto row = [&] {
const auto descriptor = CreditsRow::Descriptor{
.entry = item,
.creditIcon = _creditIcon,
.rowHeight = computeListSt().item.height,
};
if (item.peerId) {
const auto peer = session().data().peer(item.peerId);
return std::make_unique<CreditsRow>(peer, descriptor);
} else {
return std::make_unique<CreditsRow>(descriptor);
}
}();
delegate()->peerListAppendRow(std::move(row));
}
delegate()->peerListRefreshRows();
}
void CreditsController::rowClicked(not_null<PeerListRow*> row) {
if (_entryClickedCallback) {
_entryClickedCallback(
static_cast<const CreditsRow*>(row.get())->entry());
}
}
rpl::producer<bool> CreditsController::allLoadedValue() const {
return _allLoaded.value();
}
} // namespace
void AddPublicForwards(
@ -843,4 +1105,48 @@ void AddBoostsList(
button->setClickedCallback(showMore);
}
void AddCreditsHistoryList(
const Data::CreditsStatusSlice &firstSlice,
not_null<Ui::VerticalLayout*> container,
Fn<void(const Data::CreditsHistoryEntry &)> callback,
not_null<PeerData*> self,
not_null<QImage*> icon,
bool in,
bool out) {
struct State final {
State(CreditsDescriptor d) : controller(std::move(d)) {
}
PeerListContentDelegateSimple delegate;
CreditsController controller;
};
auto d = CreditsDescriptor{ firstSlice, callback, self, icon, in, out };
const auto state = container->lifetime().make_state<State>(std::move(d));
state->delegate.setContent(container->add(
object_ptr<PeerListContent>(container, &state->controller)));
state->controller.setDelegate(&state->delegate);
const auto wrap = container->add(
object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
container,
object_ptr<Ui::SettingsButton>(
container,
tr::lng_stories_show_more(),
st::statisticsShowMoreButton)),
{ 0, -st::settingsButton.padding.top(), 0, 0 });
const auto button = wrap->entity();
AddArrow(button);
const auto showMore = [=] {
if (!state->controller.skipRequest()) {
state->controller.requestNext();
container->resizeToWidth(container->width());
}
};
wrap->toggleOn(
state->controller.allLoadedValue() | rpl::map(!rpl::mappers::_1),
anim::type::instant);
button->setClickedCallback(showMore);
}
} // namespace Info::Statistics

View file

@ -16,6 +16,8 @@ class VerticalLayout;
namespace Data {
struct Boost;
struct BoostsListSlice;
struct CreditsHistoryEntry;
struct CreditsStatusSlice;
struct PublicForwardsSlice;
struct RecentPostId;
struct SupergroupStatistics;
@ -44,4 +46,13 @@ void AddBoostsList(
not_null<PeerData*> peer,
rpl::producer<QString> title);
void AddCreditsHistoryList(
const Data::CreditsStatusSlice &firstSlice,
not_null<Ui::VerticalLayout*> container,
Fn<void(const Data::CreditsHistoryEntry &)> entryClickedCallback,
not_null<PeerData*> self,
not_null<QImage*> creditIcon,
bool in,
bool out);
} // namespace Info::Statistics

View file

@ -22,3 +22,5 @@ creditsTopupButton: SettingsButton(settingsButton) {
creditsTopupPrice: FlatLabel(defaultFlatLabel) {
textFg: windowSubTextFg;
}
creditsHistoryRightSkip: 10px;