mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 22:54:01 +02:00
Load my gifts while scrolling in Send Gift.
This commit is contained in:
parent
e1f4fd0c7b
commit
696be858f6
1 changed files with 188 additions and 54 deletions
|
@ -369,6 +369,30 @@ auto GenerateGiftMedia(
|
||||||
return Images::Round(std::move(result), mask, RectPart::FullTop);
|
return Images::Round(std::move(result), mask, RectPart::FullTop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct VisibleRange {
|
||||||
|
int top = 0;
|
||||||
|
int bottom = 0;
|
||||||
|
|
||||||
|
friend inline bool operator==(VisibleRange, VisibleRange) = default;
|
||||||
|
};
|
||||||
|
class WidgetWithRange final : public RpWidget {
|
||||||
|
public:
|
||||||
|
using RpWidget::RpWidget;
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<VisibleRange> visibleRange() const {
|
||||||
|
return _visibleRange.value();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
void visibleTopBottomUpdated(
|
||||||
|
int visibleTop,
|
||||||
|
int visibleBottom) override {
|
||||||
|
_visibleRange = VisibleRange{ visibleTop, visibleBottom };
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::variable<VisibleRange> _visibleRange;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
void PrepareImage(
|
void PrepareImage(
|
||||||
QImage &image,
|
QImage &image,
|
||||||
not_null<Text::CustomEmoji*> emoji,
|
not_null<Text::CustomEmoji*> emoji,
|
||||||
|
@ -767,7 +791,8 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<MyGiftsDescriptor> UniqueGiftsSlice(
|
[[nodiscard]] rpl::producer<MyGiftsDescriptor> UniqueGiftsSlice(
|
||||||
not_null<Main::Session*> session) {
|
not_null<Main::Session*> session,
|
||||||
|
QString offset = QString()) {
|
||||||
return [=](auto consumer) {
|
return [=](auto consumer) {
|
||||||
using Flag = MTPpayments_GetSavedStarGifts::Flag;
|
using Flag = MTPpayments_GetSavedStarGifts::Flag;
|
||||||
const auto user = session->user();
|
const auto user = session->user();
|
||||||
|
@ -775,7 +800,7 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
|
||||||
MTPpayments_GetSavedStarGifts(
|
MTPpayments_GetSavedStarGifts(
|
||||||
MTP_flags(Flag::f_exclude_limited | Flag::f_exclude_unlimited),
|
MTP_flags(Flag::f_exclude_limited | Flag::f_exclude_unlimited),
|
||||||
user->input,
|
user->input,
|
||||||
MTP_string(QString()),
|
MTP_string(offset),
|
||||||
MTP_int(kMyGiftsPerPage)
|
MTP_int(kMyGiftsPerPage)
|
||||||
)).done([=](const MTPpayments_SavedStarGifts &result) {
|
)).done([=](const MTPpayments_SavedStarGifts &result) {
|
||||||
auto gifts = MyGiftsDescriptor();
|
auto gifts = MyGiftsDescriptor();
|
||||||
|
@ -1798,14 +1823,21 @@ void SendGiftBox(
|
||||||
[[nodiscard]] object_ptr<RpWidget> MakeGiftsList(
|
[[nodiscard]] object_ptr<RpWidget> MakeGiftsList(
|
||||||
not_null<Window::SessionController*> window,
|
not_null<Window::SessionController*> window,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
rpl::producer<GiftsDescriptor> gifts) {
|
rpl::producer<GiftsDescriptor> gifts,
|
||||||
auto result = object_ptr<RpWidget>((QWidget*)nullptr);
|
Fn<void()> loadMore) {
|
||||||
|
auto result = object_ptr<WidgetWithRange>((QWidget*)nullptr);
|
||||||
const auto raw = result.data();
|
const auto raw = result.data();
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
Delegate delegate;
|
Delegate delegate;
|
||||||
|
std::vector<int> order;
|
||||||
|
std::vector<bool> validated;
|
||||||
|
std::vector<GiftDescriptor> list;
|
||||||
std::vector<std::unique_ptr<GiftButton>> buttons;
|
std::vector<std::unique_ptr<GiftButton>> buttons;
|
||||||
|
std::shared_ptr<Api::PremiumGiftCodeOptions> api;
|
||||||
|
rpl::variable<VisibleRange> visibleRange;
|
||||||
bool sending = false;
|
bool sending = false;
|
||||||
|
int perRow = 1;
|
||||||
};
|
};
|
||||||
const auto state = raw->lifetime().make_state<State>(State{
|
const auto state = raw->lifetime().make_state<State>(State{
|
||||||
.delegate = Delegate(&window->session(), GiftButtonMode::Full),
|
.delegate = Delegate(&window->session(), GiftButtonMode::Full),
|
||||||
|
@ -1821,54 +1853,65 @@ void SendGiftBox(
|
||||||
}
|
}
|
||||||
}, raw->lifetime());
|
}, raw->lifetime());
|
||||||
|
|
||||||
std::move(
|
const auto rebuild = [=] {
|
||||||
gifts
|
|
||||||
) | rpl::start_with_next([=](const GiftsDescriptor &gifts) {
|
|
||||||
const auto width = st::boxWideWidth;
|
const auto width = st::boxWideWidth;
|
||||||
const auto padding = st::giftBoxPadding;
|
const auto padding = st::giftBoxPadding;
|
||||||
const auto available = width - padding.left() - padding.right();
|
const auto available = width - padding.left() - padding.right();
|
||||||
const auto perRow = available / single.width();
|
const auto range = state->visibleRange.current();
|
||||||
const auto count = int(gifts.list.size());
|
const auto count = int(state->list.size());
|
||||||
|
|
||||||
auto order = ranges::views::ints
|
auto &buttons = state->buttons;
|
||||||
| ranges::views::take(count)
|
if (buttons.size() < count) {
|
||||||
| ranges::to_vector;
|
buttons.resize(count);
|
||||||
|
|
||||||
if (SortForBirthday(peer)) {
|
|
||||||
ranges::stable_partition(order, [&](int i) {
|
|
||||||
const auto &gift = gifts.list[i];
|
|
||||||
const auto stars = std::get_if<GiftTypeStars>(&gift);
|
|
||||||
return stars && stars->info.birthday;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
auto &validated = state->validated;
|
||||||
|
validated.resize(count);
|
||||||
|
|
||||||
auto x = padding.left();
|
auto x = padding.left();
|
||||||
auto y = padding.top();
|
auto y = padding.top();
|
||||||
state->buttons.resize(count);
|
const auto perRow = state->perRow;
|
||||||
for (auto &button : state->buttons) {
|
const auto singlew = single.width() + st::giftBoxGiftSkip.x();
|
||||||
|
const auto singleh = single.height() + st::giftBoxGiftSkip.y();
|
||||||
|
const auto rowFrom = std::max(range.top - y, 0) / singleh;
|
||||||
|
const auto rowTill = (std::max(range.bottom - y + st::giftBoxGiftSkip.y(), 0) + singleh - 1)
|
||||||
|
/ singleh;
|
||||||
|
Assert(rowTill >= rowFrom);
|
||||||
|
const auto first = rowFrom * perRow;
|
||||||
|
const auto last = std::min(rowTill * perRow, count);
|
||||||
|
auto checkedFrom = 0;
|
||||||
|
auto checkedTill = int(buttons.size());
|
||||||
|
const auto ensureButton = [&](int index) {
|
||||||
|
auto &button = buttons[index];
|
||||||
if (!button) {
|
if (!button) {
|
||||||
button = std::make_unique<GiftButton>(raw, &state->delegate);
|
validated[index] = false;
|
||||||
button->show();
|
for (; checkedFrom != first; ++checkedFrom) {
|
||||||
|
if (buttons[checkedFrom]) {
|
||||||
|
button = std::move(buttons[checkedFrom]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (!button) {
|
||||||
const auto api = gifts.api;
|
for (; checkedTill != last; ) {
|
||||||
for (auto i = 0; i != count; ++i) {
|
--checkedTill;
|
||||||
const auto button = state->buttons[i].get();
|
if (buttons[checkedTill]) {
|
||||||
const auto &descriptor = gifts.list[order[i]];
|
button = std::move(buttons[checkedTill]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!button) {
|
||||||
|
button = std::make_unique<GiftButton>(
|
||||||
|
raw,
|
||||||
|
&state->delegate);
|
||||||
|
}
|
||||||
|
if (validated[index]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
button->show();
|
||||||
|
validated[index] = true;
|
||||||
|
const auto &descriptor = state->list[state->order[index]];
|
||||||
button->setDescriptor(descriptor, GiftButton::Mode::Full);
|
button->setDescriptor(descriptor, GiftButton::Mode::Full);
|
||||||
|
|
||||||
const auto last = !((i + 1) % perRow);
|
|
||||||
if (last) {
|
|
||||||
x = padding.left() + available - single.width();
|
|
||||||
}
|
|
||||||
button->setGeometry(QRect(QPoint(x, y), single), extend);
|
|
||||||
if (last) {
|
|
||||||
x = padding.left();
|
|
||||||
y += single.height() + st::giftBoxGiftSkip.y();
|
|
||||||
} else {
|
|
||||||
x += single.width() + st::giftBoxGiftSkip.x();
|
|
||||||
}
|
|
||||||
|
|
||||||
button->setClickedCallback([=] {
|
button->setClickedCallback([=] {
|
||||||
const auto star = std::get_if<GiftTypeStars>(&descriptor);
|
const auto star = std::get_if<GiftTypeStars>(&descriptor);
|
||||||
if (star && star->info.unique) {
|
if (star && star->info.unique) {
|
||||||
|
@ -1885,17 +1928,84 @@ void SendGiftBox(
|
||||||
} else if (star && IsSoldOut(star->info)) {
|
} else if (star && IsSoldOut(star->info)) {
|
||||||
window->show(Box(SoldOutBox, window, *star));
|
window->show(Box(SoldOutBox, window, *star));
|
||||||
} else {
|
} else {
|
||||||
window->show(
|
window->show(Box(
|
||||||
Box(SendGiftBox, window, peer, api, descriptor));
|
SendGiftBox,
|
||||||
|
window,
|
||||||
|
peer,
|
||||||
|
state->api,
|
||||||
|
descriptor));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
button->setGeometry(QRect(QPoint(x, y), single), extend);
|
||||||
|
};
|
||||||
|
y += rowFrom * singleh;
|
||||||
|
for (auto row = rowFrom; row != rowTill; ++row) {
|
||||||
|
for (auto col = 0; col != perRow; ++col) {
|
||||||
|
const auto index = row * perRow + col;
|
||||||
|
if (index >= count) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const auto last = !((col + 1) % perRow);
|
||||||
|
if (last) {
|
||||||
|
x = padding.left() + available - single.width();
|
||||||
|
}
|
||||||
|
ensureButton(index);
|
||||||
|
if (last) {
|
||||||
|
x = padding.left();
|
||||||
|
y += singleh;
|
||||||
|
} else {
|
||||||
|
x += singlew;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (count % perRow) {
|
const auto till = std::min(int(buttons.size()), rowTill * perRow);
|
||||||
y += padding.bottom() + single.height();
|
for (auto i = count; i < till; ++i) {
|
||||||
} else {
|
if (const auto button = buttons[i].get()) {
|
||||||
y += padding.bottom() - st::giftBoxGiftSkip.y();
|
button->hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
raw->resize(raw->width(), count ? y : 0);
|
|
||||||
|
const auto page = range.bottom - range.top;
|
||||||
|
if (loadMore && page > 0 && range.bottom + page > raw->height()) {
|
||||||
|
loadMore();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
state->visibleRange = raw->visibleRange();
|
||||||
|
state->visibleRange.value(
|
||||||
|
) | rpl::start_with_next(rebuild, raw->lifetime());
|
||||||
|
|
||||||
|
std::move(
|
||||||
|
gifts
|
||||||
|
) | rpl::start_with_next([=](const GiftsDescriptor &gifts) {
|
||||||
|
const auto width = st::boxWideWidth;
|
||||||
|
const auto padding = st::giftBoxPadding;
|
||||||
|
const auto available = width - padding.left() - padding.right();
|
||||||
|
state->perRow = available / single.width();
|
||||||
|
state->list = std::move(gifts.list);
|
||||||
|
state->api = gifts.api;
|
||||||
|
|
||||||
|
const auto count = int(state->list.size());
|
||||||
|
state->order = ranges::views::ints
|
||||||
|
| ranges::views::take(count)
|
||||||
|
| ranges::to_vector;
|
||||||
|
state->validated.clear();
|
||||||
|
|
||||||
|
if (SortForBirthday(peer)) {
|
||||||
|
ranges::stable_partition(state->order, [&](int i) {
|
||||||
|
const auto &gift = state->list[i];
|
||||||
|
const auto stars = std::get_if<GiftTypeStars>(&gift);
|
||||||
|
return stars && stars->info.birthday && !stars->info.unique;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto rows = (count + state->perRow - 1) / state->perRow;
|
||||||
|
const auto height = padding.top()
|
||||||
|
+ (rows * single.height())
|
||||||
|
+ ((rows - 1) * st::giftBoxGiftSkip.y())
|
||||||
|
+ padding.bottom();
|
||||||
|
raw->resize(raw->width(), height);
|
||||||
|
rebuild();
|
||||||
}, raw->lifetime());
|
}, raw->lifetime());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -1959,7 +2069,7 @@ void AddBlock(
|
||||||
gifts.list | ranges::to<std::vector<GiftDescriptor>>,
|
gifts.list | ranges::to<std::vector<GiftDescriptor>>,
|
||||||
gifts.api,
|
gifts.api,
|
||||||
};
|
};
|
||||||
}));
|
}), nullptr);
|
||||||
result->lifetime().add([state = std::move(state)] {});
|
result->lifetime().add([state = std::move(state)] {});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1973,8 +2083,12 @@ void AddBlock(
|
||||||
struct State {
|
struct State {
|
||||||
rpl::variable<std::vector<GiftTypeStars>> gifts;
|
rpl::variable<std::vector<GiftTypeStars>> gifts;
|
||||||
rpl::variable<int> priceTab = kPriceTabAll;
|
rpl::variable<int> priceTab = kPriceTabAll;
|
||||||
|
rpl::event_stream<> myUpdated;
|
||||||
|
MyGiftsDescriptor my;
|
||||||
|
rpl::lifetime myLoading;
|
||||||
};
|
};
|
||||||
const auto state = result->lifetime().make_state<State>();
|
const auto state = result->lifetime().make_state<State>();
|
||||||
|
state->my = std::move(my);
|
||||||
|
|
||||||
state->gifts = GiftsStars(&window->session(), peer);
|
state->gifts = GiftsStars(&window->session(), peer);
|
||||||
|
|
||||||
|
@ -1982,16 +2096,17 @@ void AddBlock(
|
||||||
window,
|
window,
|
||||||
peer,
|
peer,
|
||||||
state->gifts.value(),
|
state->gifts.value(),
|
||||||
!my.list.empty());
|
!state->my.list.empty());
|
||||||
state->priceTab = std::move(tabs.priceTab);
|
state->priceTab = std::move(tabs.priceTab);
|
||||||
result->add(std::move(tabs.widget));
|
result->add(std::move(tabs.widget));
|
||||||
result->add(MakeGiftsList(window, peer, rpl::combine(
|
result->add(MakeGiftsList(window, peer, rpl::combine(
|
||||||
state->gifts.value(),
|
state->gifts.value(),
|
||||||
state->priceTab.value()
|
state->priceTab.value(),
|
||||||
) | rpl::map([=](std::vector<GiftTypeStars> &&gifts, int price) {
|
rpl::single(rpl::empty) | rpl::then(state->myUpdated.events())
|
||||||
|
) | rpl::map([=](std::vector<GiftTypeStars> &&gifts, int price, auto) {
|
||||||
if (price == kPriceTabMy) {
|
if (price == kPriceTabMy) {
|
||||||
gifts.clear();
|
gifts.clear();
|
||||||
for (const auto &gift : my.list) {
|
for (const auto &gift : state->my.list) {
|
||||||
gifts.push_back({
|
gifts.push_back({
|
||||||
.transferId = gift.manageId,
|
.transferId = gift.manageId,
|
||||||
.info = gift.info,
|
.info = gift.info,
|
||||||
|
@ -2011,7 +2126,26 @@ void AddBlock(
|
||||||
return GiftsDescriptor{
|
return GiftsDescriptor{
|
||||||
gifts | ranges::to<std::vector<GiftDescriptor>>(),
|
gifts | ranges::to<std::vector<GiftDescriptor>>(),
|
||||||
};
|
};
|
||||||
})));
|
}), [=] {
|
||||||
|
if (state->priceTab.current() == kPriceTabMy
|
||||||
|
&& !state->my.offset.isEmpty()
|
||||||
|
&& !state->myLoading) {
|
||||||
|
state->myLoading = UniqueGiftsSlice(
|
||||||
|
&peer->session(),
|
||||||
|
state->my.offset
|
||||||
|
) | rpl::start_with_next([=](MyGiftsDescriptor &&descriptor) {
|
||||||
|
state->myLoading.destroy();
|
||||||
|
state->my.offset = descriptor.list.empty()
|
||||||
|
? QString()
|
||||||
|
: descriptor.offset;
|
||||||
|
state->my.list.insert(
|
||||||
|
end(state->my.list),
|
||||||
|
std::make_move_iterator(begin(descriptor.list)),
|
||||||
|
std::make_move_iterator(end(descriptor.list)));
|
||||||
|
state->myUpdated.fire({});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue