Switch between story albums.

This commit is contained in:
John Preston
2025-07-28 14:24:55 +04:00
parent c26e8a69ca
commit d4d4f3c082
5 changed files with 141 additions and 86 deletions
+2 -2
View File
@@ -3760,7 +3760,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_collection_new_create" = "Create";
"lng_gift_collection_empty_title" = "Organize Your Gifts";
"lng_gift_collection_empty_text" = "Add some of your gifts to this collection.";
"lng_gift_collection_empty_button" = "Add to Collection";
"lng_gift_collection_all" = "All Gifts";
"lng_gift_collection_add_title" = "Add Gifts";
"lng_gift_collection_edit" = "Edit Name";
@@ -6420,6 +6419,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_stories_archive_title" = "Story Archive";
"lng_stories_archive_about" = "Only you can see archived stories unless you choose to post them to your profile.";
"lng_stories_channel_archive_about" = "Only admins of the channel can see archived stories unless they are posted to the channel page.";
"lng_stories_empty" = "Your stories will be here.";
"lng_stories_empty_channel" = "Channel posts will be here.";
"lng_stories_reply_sent" = "Message sent.";
"lng_stories_hidden_to_contacts" = "Stories from {user} were moved to the **Archive**.";
"lng_stories_shown_in_chats" = "Stories from {user} were returned from the **Archive**.";
@@ -6463,7 +6464,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_stories_album_new_create" = "Create";
"lng_stories_album_empty_title" = "Organize Your Stories";
"lng_stories_album_empty_text" = "Add some of your stories to this album.";
"lng_stories_album_empty_button" = "Add to Album";
"lng_stories_album_all" = "All Stories";
"lng_stories_album_add_title" = "Add Stories";
"lng_stories_album_edit" = "Edit Name";
-1
View File
@@ -763,7 +763,6 @@ infoEmptyAudio: icon {{ "info/info_media_audio_empty", infoEmptyFg }};
infoEmptyFile: icon {{ "info/info_media_file_empty", infoEmptyFg }};
infoEmptyVoice: icon {{ "info/info_media_voice_empty", infoEmptyFg }};
infoEmptyLink: icon {{ "info/info_media_link_empty", infoEmptyFg }};
infoEmptyStories: icon {{ "info/info_media_story_empty", infoEmptyFg }};
infoEmptyIconTop: 120px;
infoEmptyLabelTop: 40px;
infoEmptyLabelSkip: 20px;
@@ -988,7 +988,7 @@ void InnerWidget::refreshAbout() {
about.get(),
object_ptr<Ui::RoundButton>(
about.get(),
tr::lng_gift_collection_empty_button(),
rpl::single(QString()),
st::collectionEmptyButton)),
st::collectionEmptyAddMargin)->entity();
button->setText(tr::lng_gift_collection_add_title(
@@ -48,61 +48,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Info::Stories {
class EmptyWidget : public Ui::RpWidget {
public:
EmptyWidget(QWidget *parent);
void setFullHeight(rpl::producer<int> fullHeightValue);
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
private:
object_ptr<Ui::FlatLabel> _text;
int _height = 0;
};
EmptyWidget::EmptyWidget(QWidget *parent)
: RpWidget(parent)
, _text(this, st::infoEmptyLabel) {
}
void EmptyWidget::setFullHeight(rpl::producer<int> fullHeightValue) {
std::move(
fullHeightValue
) | rpl::start_with_next([this](int fullHeight) {
// Make icon center be on 1/3 height.
auto iconCenter = fullHeight / 3;
auto iconHeight = st::infoEmptyStories.height();
auto iconTop = iconCenter - iconHeight / 2;
_height = iconTop + st::infoEmptyIconTop;
resizeToWidth(width());
}, lifetime());
}
int EmptyWidget::resizeGetHeight(int newWidth) {
auto labelTop = _height - st::infoEmptyLabelTop;
auto labelWidth = newWidth - 2 * st::infoEmptyLabelSkip;
_text->resizeToNaturalWidth(labelWidth);
auto labelLeft = (newWidth - _text->width()) / 2;
_text->moveToLeft(labelLeft, labelTop, newWidth);
update();
return _height;
}
void EmptyWidget::paintEvent(QPaintEvent *e) {
auto p = QPainter(this);
const auto iconLeft = (width() - st::infoEmptyStories.width()) / 2;
const auto iconTop = height() - st::infoEmptyIconTop;
st::infoEmptyStories.paint(p, iconLeft, iconTop, width());
}
InnerWidget::InnerWidget(
QWidget *parent,
not_null<Controller*> controller,
@@ -116,23 +61,37 @@ InnerWidget::InnerWidget(
, _albumChanges(Data::StoryAlbumUpdate{
.peer = _peer,
.albumId = _addingToAlbumId,
})
, _empty(this) {
_empty->heightValue(
) | rpl::start_with_next([=] {
refreshHeight();
}, _empty->lifetime());
setupList();
}) {
preloadArchiveCount();
_albumId.changes(
_albumId.value(
) | rpl::start_with_next([=](int albumId) {
_list.destroy();
_controller->replaceKey(Key(Tag(_peer, albumId)));
setupList();
setupEmpty();
resizeToWidth(width());
}, lifetime());
}
void InnerWidget::preloadArchiveCount() {
constexpr auto kArchive = Data::kStoriesAlbumIdArchive;
const auto stories = &_peer->owner().stories();
if (!_peer->canEditStories()
|| stories->albumIdsCountKnown(_peer->id, kArchive)) {
return;
}
const auto key = Data::StoryAlbumIdsKey{ _peer->id, kArchive };
stories->albumIdsLoadMore(_peer->id, kArchive);
stories->albumIdsChanged() | rpl::filter(
rpl::mappers::_1 == key
) | rpl::take_while([=] {
return !stories->albumIdsCountKnown(_peer->id, kArchive);
}) | rpl::start_with_next([=] {
refreshAlbumsTabs();
}, lifetime());
}
void InnerWidget::setupAlbums() {
Ui::AddSkip(_top);
_albumsWrap = _top->add(object_ptr<Ui::BoxContentDivider>(_top));
@@ -437,11 +396,99 @@ void InnerWidget::setupList() {
raw->show();
}
void InnerWidget::setupEmpty() {
const auto stories = &_controller->session().data().stories();
const auto key = Data::StoryAlbumIdsKey{ _peer->id, _albumId.current() };
rpl::combine(
rpl::single(
rpl::empty
) | rpl::then(
stories->albumIdsChanged() | rpl::filter(
rpl::mappers::_1 == key
) | rpl::to_empty
),
_list->heightValue()
) | rpl::start_with_next([=](auto, int listHeight) {
if (listHeight) {
_empty.destroy();
return;
}
refreshEmpty();
}, _list->lifetime());
}
void InnerWidget::refreshEmpty() {
_empty.destroy();
const auto albumId = _albumId.current();
const auto stories = &_controller->session().data().stories();
const auto knownEmpty = stories->albumIdsCountKnown(_peer->id, albumId);
const auto albumCanAdd = knownEmpty
&& albumId
&& (albumId != Data::kStoriesAlbumIdArchive)
&& _peer->canEditStories();
if (albumCanAdd) {
auto empty = object_ptr<Ui::VerticalLayout>(this);
empty->add(
object_ptr<Ui::CenterWrap<>>(
empty.get(),
object_ptr<Ui::FlatLabel>(
empty.get(),
tr::lng_stories_album_empty_title(),
st::collectionEmptyTitle)),
st::collectionEmptyTitleMargin);
empty->add(
object_ptr<Ui::CenterWrap<>>(
empty.get(),
object_ptr<Ui::FlatLabel>(
empty.get(),
tr::lng_stories_album_empty_text(),
st::collectionEmptyText)),
st::collectionEmptyTextMargin);
const auto button = empty->add(
object_ptr<Ui::CenterWrap<Ui::RoundButton>>(
empty.get(),
object_ptr<Ui::RoundButton>(
empty.get(),
rpl::single(QString()),
st::collectionEmptyButton)),
st::collectionEmptyAddMargin)->entity();
button->setText(tr::lng_stories_album_add_title(
) | rpl::map([](const QString &text) {
return Ui::Text::IconEmoji(&st::collectionAddIcon).append(text);
}));
button->setTextTransform(
Ui::RoundButton::TextTransform::NoTransform);
button->setClickedCallback([=] {
editAlbumStories(albumId);
});
empty->show();
_empty = std::move(empty);
} else {
_empty = object_ptr<Ui::FlatLabel>(
this,
(!knownEmpty
? tr::lng_contacts_loading(Ui::Text::WithEntities)
: _peer->isSelf()
? tr::lng_stories_empty(Ui::Text::RichLangValue)
: tr::lng_stories_empty_channel(Ui::Text::RichLangValue)),
st::giftListAbout);
_empty->show();
}
resizeToWidth(width());
}
void InnerWidget::refreshAlbumsTabs() {
Expects(!_addingToAlbumId);
Expects(_albumsWrap != nullptr);
if (_albums.empty() && !_peer->canEditStories()) {
const auto has = _peer->canEditStories()
&& _peer->owner().stories().albumIdsCount(
_peer->id,
Data::kStoriesAlbumIdArchive);
if (_albums.empty() && !has) {
if (base::take(_albumsTabs)) {
resizeToWidth(width());
}
@@ -471,7 +518,7 @@ void InnerWidget::refreshAlbumsTabs() {
selected = tabs.front().id;
}
}
if (_peer->canEditStories()) {
if (has) {
tabs.push_back({
.id = u"add"_q,
.text = { '+' + tr::lng_stories_album_add(tr::now) },
@@ -754,14 +801,22 @@ void InnerWidget::selectionAction(SelectionAction action) {
}
int InnerWidget::resizeGetHeight(int newWidth) {
if (!newWidth) {
return 0;
}
_inResize = true;
auto guard = gsl::finally([this] { _inResize = false; });
if (_top) {
_top->resizeToWidth(newWidth);
}
_list->resizeToWidth(newWidth);
_empty->resizeToWidth(newWidth);
if (_list) {
_list->resizeToWidth(newWidth);
}
if (const auto empty = _empty.get()) {
const auto margin = st::giftListAboutMargin;
empty->resizeToWidth(newWidth - margin.left() - margin.right());
}
return recountHeight();
}
@@ -784,25 +839,23 @@ int InnerWidget::recountHeight() {
listHeight = _list->heightNoMargins();
top += listHeight;
}
if (listHeight > 0) {
_empty->hide();
} else {
_empty->show();
_empty->moveToLeft(0, top);
top += _empty->heightNoMargins();
if (const auto empty = _empty.get()) {
const auto margin = st::giftListAboutMargin;
empty->moveToLeft(margin.left(), top + margin.top());
top += margin.top() + empty->height() + margin.bottom();
}
return top;
}
void InnerWidget::setScrollHeightValue(rpl::producer<int> value) {
using namespace rpl::mappers;
_empty->setFullHeight(rpl::combine(
std::move(value),
_listTops.events_starting_with(
_list->topValue()
) | rpl::flatten_latest(),
_topHeight.value(),
_1 - _2 + _3));
//using namespace rpl::mappers;
//_empty->setFullHeight(rpl::combine(
// std::move(value),
// _listTops.events_starting_with(
// _list->topValue()
// ) | rpl::flatten_latest(),
// _topHeight.value(),
// _1 - _2 + _3));
}
rpl::producer<Ui::ScrollToRequest> InnerWidget::scrollToRequests() const {
@@ -85,9 +85,12 @@ protected:
private:
int recountHeight();
void refreshHeight();
void refreshEmpty();
void preloadArchiveCount();
void setupTop();
void setupList();
void setupEmpty();
void setupAlbums();
void createButtons();
void createProfileTop();
@@ -120,7 +123,7 @@ private:
object_ptr<Ui::VerticalLayout> _top = { nullptr };
object_ptr<Media::ListWidget> _list = { nullptr };
object_ptr<EmptyWidget> _empty;
object_ptr<Ui::RpWidget> _empty = { nullptr };
bool _inResize = false;
bool _isStackBottom = false;