diff --git a/Telegram/SourceFiles/info/stories/info_stories_inner_widget.cpp b/Telegram/SourceFiles/info/stories/info_stories_inner_widget.cpp index d086c9372c..762f408e3f 100644 --- a/Telegram/SourceFiles/info/stories/info_stories_inner_widget.cpp +++ b/Telegram/SourceFiles/info/stories/info_stories_inner_widget.cpp @@ -567,6 +567,8 @@ void InnerWidget::setupEmpty() { _emptyLoading = false; if (listHeight <= padding.bottom() + padding.top()) { refreshEmpty(); + } else { + _albumEmpty = false; } refreshHeight(); }, _list->lifetime()); @@ -580,6 +582,7 @@ void InnerWidget::refreshEmpty() { && albumId && (albumId != Data::kStoriesAlbumIdArchive) && _peer->canEditStories(); + _albumEmpty = albumCanAdd; if (albumCanAdd) { auto empty = object_ptr(this); empty->add( diff --git a/Telegram/SourceFiles/info/stories/info_stories_inner_widget.h b/Telegram/SourceFiles/info/stories/info_stories_inner_widget.h index ae273cc9de..809148653d 100644 --- a/Telegram/SourceFiles/info/stories/info_stories_inner_widget.h +++ b/Telegram/SourceFiles/info/stories/info_stories_inner_widget.h @@ -74,6 +74,10 @@ public: void confirmDeleteAlbum(int id); void albumAdded(Data::StoryAlbum result); + [[nodiscard]] rpl::producer albumEmptyValue() const { + return _albumEmpty.value(); + } + [[nodiscard]] rpl::producer albumIdChanges() const; [[nodiscard]] rpl::producer changes() const; @@ -135,6 +139,7 @@ private: rpl::event_stream> _selectedLists; rpl::event_stream> _listTops; rpl::variable _topHeight; + rpl::variable _albumEmpty; }; diff --git a/Telegram/SourceFiles/info/stories/info_stories_widget.cpp b/Telegram/SourceFiles/info/stories/info_stories_widget.cpp index 68b751a2e5..d093987a47 100644 --- a/Telegram/SourceFiles/info/stories/info_stories_widget.cpp +++ b/Telegram/SourceFiles/info/stories/info_stories_widget.cpp @@ -12,9 +12,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/stories/info_stories_inner_widget.h" #include "info/info_controller.h" #include "info/info_memento.h" +#include "ui/text/text_utilities.h" +#include "ui/widgets/buttons.h" #include "ui/widgets/scroll_area.h" +#include "ui/wrap/slide_wrap.h" #include "lang/lang_keys.h" #include "ui/ui_utility.h" +#include "styles/style_info.h" +#include "styles/style_layers.h" namespace Info::Stories { @@ -70,6 +75,10 @@ Widget::Widget( ) | rpl::start_with_next([this](Ui::ScrollToRequest request) { scrollTo(request); }, _inner->lifetime()); + + _albumId.value() | rpl::start_with_next([=] { + refreshBottom(); + }, _inner->lifetime()); } void Widget::setIsStackBottom(bool isStackBottom) { @@ -120,6 +129,97 @@ void Widget::restoreState(not_null memento) { scrollTopRestore(memento->scrollTop()); } +void Widget::refreshBottom() { + const auto albumId = _albumId.current(); + const auto withButton = albumId + && controller()->storiesPeer()->canEditStories(); + const auto wasBottom = _pinnedToBottom ? _pinnedToBottom->height() : 0; + delete _pinnedToBottom.data(); + if (!withButton) { + setScrollBottomSkip(0); + _hasPinnedToBottom = false; + } else { + setupBottomButton(wasBottom, _inner->albumEmptyValue()); + } +} + +void Widget::setupBottomButton( + int wasBottomHeight, + rpl::producer hidden) { + _pinnedToBottom = Ui::CreateChild>( + this, + object_ptr(this)); + const auto wrap = _pinnedToBottom.data(); + wrap->toggle(false, anim::type::instant); + + const auto bottom = wrap->entity(); + bottom->show(); + + const auto button = Ui::CreateChild( + bottom, + rpl::single(QString()), + st::collectionEditBox.button); + button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform); + button->setText(tr::lng_stories_album_add_button( + ) | rpl::map([](const QString &text) { + return Ui::Text::IconEmoji(&st::collectionAddIcon).append(text); + })); + button->show(); + std::move(hidden) | rpl::start_with_next([=](bool hidden) { + button->setVisible(!hidden); + _hasPinnedToBottom = !hidden; + }, button->lifetime()); + + button->setClickedCallback([=] { + if (const auto id = _albumId.current()) { + _inner->editAlbumStories(id); + } else { + refreshBottom(); + } + }); + + const auto buttonTop = st::boxRadius; + bottom->widthValue() | rpl::start_with_next([=](int width) { + const auto normal = width - 2 * buttonTop; + button->resizeToWidth(normal); + const auto buttonLeft = (width - normal) / 2; + button->moveToLeft(buttonLeft, buttonTop); + }, button->lifetime()); + + button->heightValue() | rpl::start_with_next([=](int height) { + bottom->resize(bottom->width(), st::boxRadius + height); + }, button->lifetime()); + + const auto processHeight = [=] { + setScrollBottomSkip(wrap->height()); + wrap->moveToLeft(wrap->x(), height() - wrap->height()); + }; + + _inner->sizeValue( + ) | rpl::start_with_next([=](const QSize &s) { + wrap->resizeToWidth(s.width()); + crl::on_main(wrap, processHeight); + }, wrap->lifetime()); + + rpl::combine( + wrap->heightValue(), + heightValue() + ) | rpl::start_with_next(processHeight, wrap->lifetime()); + + if (_shown) { + wrap->toggle( + true, + wasBottomHeight ? anim::type::instant : anim::type::normal); + } +} + +void Widget::showFinished() { + _shown = true; + if (const auto bottom = _pinnedToBottom.data()) { + bottom->toggle(true, anim::type::normal); + } +} + rpl::producer Widget::selectedListValue() const { return _inner->selectedListValue(); } @@ -137,6 +237,10 @@ rpl::producer Widget::title() { : tr::lng_stories_my_title(); } +rpl::producer Widget::desiredBottomShadowVisibility() { + return _hasPinnedToBottom.value(); +} + std::shared_ptr Make(not_null peer, int albumId) { return std::make_shared( std::vector>( diff --git a/Telegram/SourceFiles/info/stories/info_stories_widget.h b/Telegram/SourceFiles/info/stories/info_stories_widget.h index 1985724531..9fcbcd7978 100644 --- a/Telegram/SourceFiles/info/stories/info_stories_widget.h +++ b/Telegram/SourceFiles/info/stories/info_stories_widget.h @@ -11,6 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/media/info_media_widget.h" #include "info/stories/info_stories_common.h" +namespace Ui { +template +class SlideWrap; +} // namespace Ui + namespace Info::Stories { class InnerWidget; @@ -59,14 +64,24 @@ public: rpl::producer title() override; + rpl::producer desiredBottomShadowVisibility() override; + + void showFinished() override; + private: void saveState(not_null memento); void restoreState(not_null memento); + void setupBottomButton(int wasBottomHeight, rpl::producer hidden); + void refreshBottom(); + std::shared_ptr doCreateMemento() override; rpl::variable _albumId; InnerWidget *_inner = nullptr; + QPointer> _pinnedToBottom; + rpl::variable _hasPinnedToBottom; + bool _shown = false; };