From 70ca3d4f1ac78f25020dde44fd0425fd80575bdb Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 11 Jul 2023 19:22:18 +0400
Subject: [PATCH] Show hidden stories in archive.

---
 Telegram/Resources/langs/lang.strings         |   8 +-
 .../SourceFiles/dialogs/dialogs_widget.cpp    | 233 ++++++++++++------
 Telegram/SourceFiles/dialogs/dialogs_widget.h |  18 +-
 .../dialogs/ui/dialogs_stories_content.cpp    |   8 +-
 .../dialogs/ui/dialogs_stories_list.cpp       | 113 ++++-----
 .../dialogs/ui/dialogs_stories_list.h         |  13 +-
 Telegram/SourceFiles/ui/chat/more_chats_bar.h |   4 +
 Telegram/SourceFiles/ui/menu_icons.style      |   1 -
 Telegram/lib_ui                               |   2 +-
 9 files changed, 241 insertions(+), 159 deletions(-)

diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 3130497ee..d360b8957 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -3823,8 +3823,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_userpic_builder_emoji_subtitle" = "Choose sticker or emoji";
 
 "lng_stories_my_name" = "My Story";
-"lng_stories_hide_to_contacts" = "Hide";
-"lng_stories_show_in_chats" = "Show in Chats";
+"lng_stories_archive" = "Archive";
+"lng_stories_unarchive" = "Unarchive";
 "lng_stories_row_count#one" = "{count} Story";
 "lng_stories_row_count#other" = "{count} Stories";
 "lng_stories_row_unread_and_one" = "{accumulated}, {user}";
@@ -3841,8 +3841,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_stories_archive_title" = "Stories Archive";
 "lng_stories_archive_about" = "Only you can see archived stories unless you choose to save them to your profile.";
 "lng_stories_reply_sent" = "Message Sent";
-"lng_stories_hidden_to_contacts" = "Stories of {user} were moved to **Contacts**.";
-"lng_stories_shown_in_chats" = "Stories of {user} were moved above the **Chats List**.";
+"lng_stories_hidden_to_contacts" = "Stories of {user} were moved to **Archive**.";
+"lng_stories_shown_in_chats" = "Stories of {user} were moved to the **Chats List**.";
 "lng_stories_delete_one_sure" = "Are you sure you want to delete this story?";
 "lng_stories_delete_sure#one" = "Are you sure you want to delete {count} story?";
 "lng_stories_delete_sure#other" = "Are you sure you want to delete {count} stories?";
diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
index 74fbf9e6c..fb92cbf1e 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
+++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
@@ -215,12 +215,12 @@ Widget::Widget(
 , _lockUnlock(_searchControls, st::dialogsLock)
 , _scroll(this)
 , _scrollToTop(_scroll, st::dialogsToUp)
-, _stories(std::make_unique<Stories::List>(
-	this,
-	st::dialogsStoriesList,
-	Stories::ContentForSession(
-		&controller->session(),
-		Data::StorySourcesList::NotHidden)))
+, _stories((_layout != Layout::Child)
+	? std::make_unique<Stories::List>(
+		this,
+		st::dialogsStoriesList,
+		_storiesContents.events() | rpl::flatten_latest())
+	: nullptr)
 , _searchTimer([=] { searchMessages(); })
 , _singleMessageSearch(&controller->session()) {
 	const auto makeChildListShown = [](PeerId peerId, float64 shown) {
@@ -237,46 +237,6 @@ Widget::Widget(
 			_childListShown.value(),
 			makeChildListShown)));
 	_scrollToTop->raise();
-	rpl::combine(
-		_scroll->positionValue(),
-		_scroll->movementValue(),
-		_storiesExplicitExpandValue.value()
-	) | rpl::start_with_next([=](
-			Ui::ElasticScrollPosition position,
-			Ui::ElasticScrollMovement movement,
-			int explicitlyExpanded) {
-		const auto overscrollTop = std::max(-position.overscroll, 0);
-		if (overscrollTop > 0 && _storiesExplicitExpand) {
-			_scroll->setOverscrollDefaults(
-				-st::dialogsStoriesFull.height,
-				0,
-				true);
-		}
-		if (explicitlyExpanded > 0 && explicitlyExpanded < overscrollTop) {
-			_storiesExplicitExpandAnimation.stop();
-			_storiesExplicitExpand = false;
-			_storiesExplicitExpandValue = 0;
-			return;
-		}
-		const auto above = std::max(explicitlyExpanded, overscrollTop);
-		if (_aboveScrollAdded != above) {
-			_aboveScrollAdded = above;
-			if (_updateScrollGeometryCached) {
-				_updateScrollGeometryCached();
-			}
-		}
-		using Phase = Ui::ElasticScrollMovement;
-		_stories->setExpandedHeight(
-			_aboveScrollAdded,
-			(movement == Phase::Momentum || movement == Phase::Returning)
-				&& (explicitlyExpanded < above));
-		if (position.overscroll > 0
-			|| (position.value
-				> (_storiesExplicitExpandScrollTop
-					+ st::dialogsRowHeight))) {
-			storiesToggleExplicitExpand(false);
-		}
-	}, lifetime());
 
 	_inner->updated(
 	) | rpl::start_with_next([=] {
@@ -417,7 +377,9 @@ Widget::Widget(
 
 	setupMainMenuToggle();
 	setupShortcuts();
-	setupStories();
+	if (_stories) {
+		setupStories();
+	}
 
 	_searchForNarrowFilters->setClickedCallback([=] {
 		_filter->setFocusFast();
@@ -609,14 +571,8 @@ void Widget::scrollToDefaultChecked(bool verytop) {
 
 void Widget::setupScrollUpButton() {
 	_scrollToTop->setClickedCallback([=] { scrollToDefaultChecked(); });
-	base::install_event_filter(_scrollToTop, [=](not_null<QEvent*> event) {
-		if (event->type() != QEvent::Wheel) {
-			return base::EventFilterResult::Continue;
-		}
-		return _scroll->viewportEvent(event)
-			? base::EventFilterResult::Cancel
-			: base::EventFilterResult::Continue;
-	});
+	trackScroll(_scrollToTop);
+	trackScroll(this);
 	updateScrollUpVisibility();
 }
 
@@ -628,7 +584,7 @@ void Widget::setupMoreChatsBar() {
 	) | rpl::start_with_next([=](FilterId id) {
 		storiesToggleExplicitExpand(false);
 
-		if (!id && false) { // #TODO stories testing
+		if (!id) {
 			_moreChatsBar = nullptr;
 			updateControlsGeometry();
 			return;
@@ -638,6 +594,8 @@ void Widget::setupMoreChatsBar() {
 			this,
 			filters->moreChatsContent(id));
 
+		trackScroll(_moreChatsBar->wrap());
+
 		_moreChatsBar->barClicks(
 		) | rpl::start_with_next([=] {
 			if (const auto missing = filters->moreChats(id)
@@ -810,11 +768,64 @@ void Widget::setupMainMenuToggle() {
 }
 
 void Widget::setupStories() {
+	trackScroll(_stories.get());
+
+	_storiesContents.fire(Stories::ContentForSession(
+		&controller()->session(),
+		Data::StorySourcesList::NotHidden));
+
+	const auto currentSource = [=] {
+		using List = Data::StorySourcesList;
+		return _openedFolder ? List::Hidden : List::NotHidden;
+	};
+
+	rpl::combine(
+		_scroll->positionValue(),
+		_scroll->movementValue(),
+		_storiesExplicitExpandValue.value()
+	) | rpl::start_with_next([=](
+			Ui::ElasticScrollPosition position,
+			Ui::ElasticScrollMovement movement,
+			int explicitlyExpanded) {
+		if (_stories->isHidden()) {
+			return;
+		}
+		const auto overscrollTop = std::max(-position.overscroll, 0);
+		if (overscrollTop > 0 && _storiesExplicitExpand) {
+			_scroll->setOverscrollDefaults(
+				-st::dialogsStoriesFull.height,
+				0,
+				true);
+		}
+		if (explicitlyExpanded > 0 && explicitlyExpanded < overscrollTop) {
+			_storiesExplicitExpandAnimation.stop();
+			_storiesExplicitExpand = false;
+			_storiesExplicitExpandValue = 0;
+			return;
+		}
+		const auto above = std::max(explicitlyExpanded, overscrollTop);
+		if (_aboveScrollAdded != above) {
+			_aboveScrollAdded = above;
+			if (_updateScrollGeometryCached) {
+				_updateScrollGeometryCached();
+			}
+		}
+		using Phase = Ui::ElasticScrollMovement;
+		_stories->setExpandedHeight(
+			_aboveScrollAdded,
+			(movement == Phase::Momentum || movement == Phase::Returning)
+				&& (explicitlyExpanded < above));
+		if (position.overscroll > 0
+			|| (position.value
+				> (_storiesExplicitExpandScrollTop
+					+ st::dialogsRowHeight))) {
+			storiesToggleExplicitExpand(false);
+		}
+	}, lifetime());
+
 	_stories->clicks(
 	) | rpl::start_with_next([=](uint64 id) {
-		controller()->openPeerStories(
-			PeerId(int64(id)),
-			Data::StorySourcesList::NotHidden);
+		controller()->openPeerStories(PeerId(int64(id)), currentSource());
 	}, lifetime());
 
 	_stories->showMenuRequests(
@@ -824,8 +835,7 @@ void Widget::setupStories() {
 
 	_stories->loadMoreRequests(
 	) | rpl::start_with_next([=] {
-		session().data().stories().loadMore(
-			Data::StorySourcesList::NotHidden);
+		session().data().stories().loadMore(currentSource());
 	}, lifetime());
 
 	_stories->toggleExpandedRequests(
@@ -860,6 +870,20 @@ void Widget::storiesToggleExplicitExpand(bool expand) {
 	}, expand ? 0 : height, expand ? height : 0, duration, anim::sineInOut);
 }
 
+void Widget::trackScroll(not_null<Ui::RpWidget*> widget) {
+	widget->events(
+	) | rpl::start_with_next([=](not_null<QEvent*> e) {
+		const auto type = e->type();
+		if (type == QEvent::TouchBegin
+			|| type == QEvent::TouchUpdate
+			|| type == QEvent::TouchEnd
+			|| type == QEvent::TouchCancel
+			|| type == QEvent::Wheel) {
+			_scroll->viewportEvent(e);
+		}
+	}, widget->lifetime());
+}
+
 void Widget::setupShortcuts() {
 	Shortcuts::Requests(
 	) | rpl::filter([=] {
@@ -902,6 +926,7 @@ void Widget::fullSearchRefreshOn(rpl::producer<> events) {
 void Widget::updateControlsVisibility(bool fast) {
 	updateLoadMoreChatsVisibility();
 	_scroll->show();
+	updateStoriesVisibility();
 	if ((_openedFolder || _openedForum) && _filter->hasFocus()) {
 		setInnerFocus();
 	}
@@ -1002,9 +1027,16 @@ void Widget::changeOpenedFolder(Data::Folder *folder, anim::type animated) {
 		cancelSearch();
 		closeChildList(anim::type::instant);
 		controller()->closeForum();
+		const auto was = (_openedFolder != nullptr);
 		_openedFolder = folder;
 		_inner->changeOpenedFolder(folder);
 		storiesToggleExplicitExpand(false);
+		if (was != (_openedFolder != nullptr)) {
+			using List = Data::StorySourcesList;
+			_storiesContents.fire(Stories::ContentForSession(
+				&controller()->session(),
+				folder ? List::Hidden : List::NotHidden));
+		}
 	}, (folder != nullptr), animated);
 }
 
@@ -1019,6 +1051,7 @@ void Widget::changeOpenedForum(Data::Forum *forum, anim::type animated) {
 		_api.request(base::take(_topicSearchRequest)).cancel();
 		_inner->changeOpenedForum(forum);
 		storiesToggleExplicitExpand(false);
+		updateStoriesVisibility();
 	}, (forum != nullptr), animated);
 }
 
@@ -1032,6 +1065,9 @@ void Widget::refreshTopBars() {
 	if (_openedFolder || _openedForum) {
 		if (!_subsectionTopBar) {
 			_subsectionTopBar.create(this, controller());
+			if (_stories) {
+				_stories->raise();
+			}
 			_subsectionTopBar->searchCancelled(
 			) | rpl::start_with_next([=] {
 				escape();
@@ -1306,6 +1342,7 @@ void Widget::startWidthAnimation() {
 	_widthAnimationCache = Ui::PixmapFromImage(std::move(image));
 	_scroll->setGeometry(scrollGeometry);
 	_scroll->hide();
+	updateStoriesVisibility();
 }
 
 void Widget::stopWidthAnimation() {
@@ -1313,9 +1350,41 @@ void Widget::stopWidthAnimation() {
 	if (!_showAnimation) {
 		_scroll->show();
 	}
+	updateStoriesVisibility();
 	update();
 }
 
+void Widget::updateStoriesVisibility() {
+	if (!_stories) {
+		return;
+	}
+	const auto hidden = (_showAnimation != nullptr)
+		|| _openedForum
+		|| !_widthAnimationCache.isNull()
+		|| _childList
+		|| !_filter->getLastText().isEmpty()
+		|| _searchInChat;
+	if (_stories->isHidden() != hidden) {
+		_stories->setVisible(!hidden);
+		using Type = Ui::ElasticScroll::OverscrollType;
+		if (hidden) {
+			_scroll->setOverscrollDefaults(0, 0);
+			_scroll->setOverscrollTypes(Type::Real, Type::Real);
+			if (_scroll->position().overscroll < 0) {
+				_scroll->scrollToY(0);
+			}
+		} else {
+			_scroll->setOverscrollDefaults(0, 0);
+			_scroll->setOverscrollTypes(Type::Virtual, Type::Real);
+			_storiesExplicitExpandValue.force_assign(
+				_storiesExplicitExpandValue.current());
+		}
+		if (_aboveScrollAdded > 0 && _updateScrollGeometryCached) {
+			_updateScrollGeometryCached();
+		}
+	}
+}
+
 void Widget::showFast() {
 	if (isHidden()) {
 		_inner->clearSelection();
@@ -1358,6 +1427,9 @@ void Widget::startSlideAnimation(
 		QPixmap newContentCache,
 		Window::SlideDirection direction) {
 	_scroll->hide();
+	if (_stories) {
+		_stories->hide();
+	}
 	_searchControls->hide();
 	if (_subsectionTopBar) {
 		_subsectionTopBar->hide();
@@ -2166,6 +2238,7 @@ void Widget::applyFilterUpdate(bool force) {
 		return;
 	}
 
+	updateStoriesVisibility();
 	const auto filterText = currentSearchQuery();
 	_inner->applyFilterUpdate(filterText, force);
 	if (filterText.isEmpty() && !_searchFromAuthor) {
@@ -2319,6 +2392,7 @@ void Widget::closeChildList(anim::type animated) {
 	} else {
 		_childListShadow = nullptr;
 	}
+	updateStoriesVisibility();
 }
 
 void Widget::searchInChat(Key chat) {
@@ -2372,6 +2446,7 @@ bool Widget::setSearchInChat(Key chat, PeerData *from) {
 		_searchInChat = chat;
 		controller()->searchInChat = _searchInChat;
 		updateJumpToDateVisibility();
+		updateStoriesVisibility();
 	}
 	if (searchFromUpdated) {
 		updateSearchFromVisibility();
@@ -2614,9 +2689,11 @@ void Widget::updateControlsGeometry() {
 	const auto storiesHeight = 2 * st::dialogsStories.photoTop
 		+ st::dialogsStories.photo;
 	const auto added = (st::dialogsFilter.heightMin - storiesHeight) / 2;
-	_stories->setLayoutConstraints(
-		{ filterLeft + filterWidth, filterTop + added },
-		{ 0, expandedStoriesTop, barw, st::dialogsStoriesFull.height });
+	if (_stories) {
+		_stories->setLayoutConstraints(
+			{ filterLeft + filterWidth, filterTop + added },
+			{ 0, expandedStoriesTop, barw, st::dialogsStoriesFull.height });
+	}
 	if (_forumTopShadow) {
 		_forumTopShadow->setGeometry(
 			0,
@@ -2650,43 +2727,49 @@ void Widget::updateControlsGeometry() {
 		? wasScrollTop
 		: (wasScrollTop + _topDelta);
 
-	const auto scrollw = _childList ? _narrowWidth : barw;
+	const auto scrollWidth = _childList ? _narrowWidth : barw;
+	if (_moreChatsBar) {
+		_moreChatsBar->resizeToWidth(barw);
+	}
+	if (_forumGroupCallBar) {
+		_forumGroupCallBar->resizeToWidth(barw);
+	}
+	if (_forumRequestsBar) {
+		_forumRequestsBar->resizeToWidth(barw);
+	}
 	_updateScrollGeometryCached = [=] {
-		const auto moreChatsBarTop = expandedStoriesTop + _aboveScrollAdded;
+		const auto moreChatsBarTop = expandedStoriesTop
+			+ ((!_stories || _stories->isHidden()) ? 0 : _aboveScrollAdded);
 		if (_moreChatsBar) {
 			_moreChatsBar->move(0, moreChatsBarTop);
-			_moreChatsBar->resizeToWidth(barw);
 		}
 		const auto forumGroupCallTop = moreChatsBarTop
 			+ (_moreChatsBar ? _moreChatsBar->height() : 0);
 		if (_forumGroupCallBar) {
 			_forumGroupCallBar->move(0, forumGroupCallTop);
-			_forumGroupCallBar->resizeToWidth(barw);
 		}
 		const auto forumRequestsTop = forumGroupCallTop
 			+ (_forumGroupCallBar ? _forumGroupCallBar->height() : 0);
 		if (_forumRequestsBar) {
 			_forumRequestsBar->move(0, forumRequestsTop);
-			_forumRequestsBar->resizeToWidth(barw);
 		}
 		const auto forumReportTop = forumRequestsTop
 			+ (_forumRequestsBar ? _forumRequestsBar->height() : 0);
 		if (_forumReportBar) {
 			_forumReportBar->bar().move(0, forumReportTop);
 		}
-		auto scrollTop = forumReportTop
+		const auto scrollTop = forumReportTop
 			+ (_forumReportBar ? _forumReportBar->bar().height() : 0);
-		auto scrollHeight = height() - scrollTop;
-
+		const auto scrollHeight = height() - scrollTop;
 		const auto wasScrollHeight = _scroll->height();
-		_scroll->setGeometry(0, scrollTop, scrollw, scrollHeight);
+		_scroll->setGeometry(0, scrollTop, scrollWidth, scrollHeight);
 		if (scrollHeight != wasScrollHeight) {
 			controller()->floatPlayerAreaUpdated();
 		}
 	};
 	_updateScrollGeometryCached();
 
-	_inner->resize(scrollw, _inner->height());
+	_inner->resize(scrollWidth, _inner->height());
 	_inner->setNarrowRatio(narrowRatio);
 	if (newScrollTop != wasScrollTop) {
 		_scroll->scrollToY(newScrollTop);
@@ -2698,7 +2781,7 @@ void Widget::updateControlsGeometry() {
 	}
 
 	if (_childList) {
-		const auto childw = std::max(_narrowWidth, width() - scrollw);
+		const auto childw = std::max(_narrowWidth, width() - scrollWidth);
 		const auto childh = _scroll->y() + _scroll->height();
 		const auto childx = width() - childw;
 		_childList->setGeometryWithTopMoved(
diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.h b/Telegram/SourceFiles/dialogs/dialogs_widget.h
index ca83fefa1..245998392 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_widget.h
+++ b/Telegram/SourceFiles/dialogs/dialogs_widget.h
@@ -58,6 +58,7 @@ struct SectionShow;
 
 namespace Dialogs::Stories {
 class List;
+struct Content;
 } // namespace Dialogs::Stories
 
 namespace Dialogs {
@@ -168,6 +169,7 @@ private:
 	void setupShortcuts();
 	void setupStories();
 	void storiesToggleExplicitExpand(bool expand);
+	void trackScroll(not_null<Ui::RpWidget*> widget);
 	[[nodiscard]] bool searchForPeersRequired(const QString &query) const;
 	[[nodiscard]] bool searchForTopicsRequired(const QString &query) const;
 	bool setSearchInChat(Key chat, PeerData *from = nullptr);
@@ -179,6 +181,7 @@ private:
 	void updateControlsVisibility(bool fast = false);
 	void updateLockUnlockVisibility();
 	void updateLoadMoreChatsVisibility();
+	void updateStoriesVisibility();
 	void updateJumpToDateVisibility(bool fast = false);
 	void updateSearchFromVisibility(bool fast = false);
 	void updateControlsGeometry();
@@ -268,6 +271,14 @@ private:
 	bool _scrollToTopIsShown = false;
 	bool _forumSearchRequested = false;
 
+	Data::Folder *_openedFolder = nullptr;
+	Data::Forum *_openedForum = nullptr;
+	Dialogs::Key _searchInChat;
+	History *_searchInMigrated = nullptr;
+	PeerData *_searchFromAuthor = nullptr;
+	QString _lastFilterText;
+
+	rpl::event_stream<rpl::producer<Stories::Content>> _storiesContents;
 	Fn<void()> _updateScrollGeometryCached;
 	std::unique_ptr<Stories::List> _stories;
 	Ui::Animations::Simple _storiesExplicitExpandAnimation;
@@ -276,13 +287,6 @@ private:
 	int _aboveScrollAdded = 0;
 	bool _storiesExplicitExpand = false;
 
-	Data::Folder *_openedFolder = nullptr;
-	Data::Forum *_openedForum = nullptr;
-	Dialogs::Key _searchInChat;
-	History *_searchInMigrated = nullptr;
-	PeerData *_searchFromAuthor = nullptr;
-	QString _lastFilterText;
-
 	base::Timer _searchTimer;
 
 	QString _peerSearchQuery;
diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_content.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_content.cpp
index b882842ca..2f6c55dd1 100644
--- a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_content.cpp
+++ b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_content.cpp
@@ -503,14 +503,14 @@ void FillSourceMenu(
 				controller->uiShow());
 		};
 		if (in(Data::StorySourcesList::NotHidden)) {
-			add(tr::lng_stories_hide_to_contacts(tr::now), [=] {
+			add(tr::lng_stories_archive(tr::now), [=] {
 				toggle(false);
-			}, &st::menuIconCancel);
+			}, &st::menuIconArchive);
 		}
 		if (in(Data::StorySourcesList::Hidden)) {
-			add(tr::lng_stories_show_in_chats(tr::now), [=] {
+			add(tr::lng_stories_unarchive(tr::now), [=] {
 				toggle(true);
-			}, &st::menuIconStoriesToChats);
+			}, &st::menuIconUnarchive);
 		}
 	}
 }
diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp
index 2d3ca0d76..dc6384a8d 100644
--- a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp
+++ b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp
@@ -21,8 +21,8 @@ namespace {
 constexpr auto kSmallThumbsShown = 3;
 constexpr auto kSummaryExpandLeft = 1;
 constexpr auto kPreloadPages = 2;
-constexpr auto kExpandAfterRatio = 0.85;
-constexpr auto kCollapseAfterRatio = 0.72;
+constexpr auto kExpandAfterRatio = 0.72;
+constexpr auto kCollapseAfterRatio = 0.68;
 constexpr auto kFrictionRatio = 0.15;
 constexpr auto kExpandCatchUpDuration = crl::time(200);
 
@@ -66,7 +66,6 @@ List::List(
 		showContent(std::move(content));
 	}, lifetime());
 
-	_shownAnimation.stop();
 	setMouseTracking(true);
 	resize(0, _data.empty() ? 0 : st.full.height);
 }
@@ -76,18 +75,13 @@ void List::showContent(Content &&content) {
 		return;
 	}
 	if (content.elements.empty()) {
-		_hidingData = base::take(_data);
-		if (!_hidingData.empty()) {
-			toggleAnimated(false);
-		}
+		_data = {};
+		_empty = true;
 		return;
 	}
-	const auto hidden = _content.elements.empty();
-	const auto wasCount = int((hidden ? _hidingData : _data).items.size());
+	const auto wasCount = int(_data.items.size());
 	_content = std::move(content);
-	auto items = base::take(
-		_data.items.empty() ? _hidingData.items : _data.items);
-	_hidingData = {};
+	auto items = base::take(_data.items);
 	_data.items.reserve(_content.elements.size());
 	for (const auto &element : _content.elements) {
 		const auto id = element.id;
@@ -117,8 +111,8 @@ void List::showContent(Content &&content) {
 	updateScrollMax();
 	updateSummary(_data);
 	update();
-	if (hidden) {
-		toggleAnimated(true);
+	if (!wasCount) {
+		_empty = false;
 	}
 }
 
@@ -204,24 +198,6 @@ void List::updateSummary(Data &data) {
 	Populate(_st.small, data.summaries);
 }
 
-void List::toggleAnimated(bool shown) {
-	_shownAnimation.start(
-		[=] { updateHeight(); },
-		shown ? 0. : 1.,
-		shown ? 1. : 0.,
-		st::slideWrapDuration);
-}
-
-void List::updateHeight() {
-	const auto shown = _shownAnimation.value(_data.empty() ? 0. : 1.);
-	resize(
-		width(),
-		anim::interpolate(0, _st.full.height, shown));
-	if (_data.empty() && shown == 0.) {
-		_hidingData = {};
-	}
-}
-
 void List::updateScrollMax() {
 	const auto &full = _st.full;
 	const auto singleFull = full.photoLeft * 2 + full.photo;
@@ -256,7 +232,7 @@ void List::requestExpanded(bool expanded) {
 	if (_expanded != expanded) {
 		_expanded = expanded;
 		_expandedAnimation.start(
-			[=] { update(); },
+			[=] { checkForFullState(); update(); },
 			_expanded ? 0. : 1.,
 			_expanded ? 1. : 0.,
 			st::slideWrapDuration,
@@ -308,15 +284,14 @@ List::Layout List::computeLayout() {
 	const auto lerp = [&](float64 a, float64 b) {
 		return a + (b - a) * ratio;
 	};
-	auto &rendering = _data.empty() ? _hidingData : _data;
 	const auto singleFull = full.photoLeft * 2 + full.photo;
-	const auto itemsCount = int(rendering.items.size());
+	const auto itemsCount = int(_data.items.size());
 	const auto narrowWidth = st::defaultDialogRow.padding.left()
 		+ st::defaultDialogRow.photoSize
 		+ st::defaultDialogRow.padding.left();
 	const auto narrow = false;// (width() <= narrowWidth);
 	const auto smallSkip = (itemsCount > 1
-		&& rendering.items[0].element.skipSmall)
+		&& _data.items[0].element.skipSmall)
 		? 1
 		: 0;
 	const auto smallCount = std::min(
@@ -378,7 +353,6 @@ void List::paintEvent(QPaintEvent *e) {
 	const auto elerp = [&](float64 a, float64 b) {
 		return a + (b - a) * expandRatio;
 	};
-	auto &rendering = _data.empty() ? _hidingData : _data;
 	const auto line = elerp(st.lineTwice, full.lineTwice) / 2.;
 	const auto lineRead = elerp(st.lineReadTwice, full.lineReadTwice) / 2.;
 	const auto photoTopSmall = st.photoTop;
@@ -389,7 +363,8 @@ void List::paintEvent(QPaintEvent *e) {
 		- (st.photoTop + (st.photo / 2.))
 		+ (photoTop + (photo / 2.));
 	const auto nameScale = _lastRatio;
-	const auto nameTop = nameScale * full.nameTop;
+	const auto nameTop = full.nameTop
+		+ (photoTop + photo - full.photoTop - full.photo);
 	const auto nameWidth = nameScale * AvailableNameWidth(_st);
 	const auto nameHeight = nameScale * full.nameStyle.font->height;
 	const auto nameLeft = layout.photoLeft + (photo - nameWidth) / 2.;
@@ -402,11 +377,11 @@ void List::paintEvent(QPaintEvent *e) {
 		const auto left = anim::interpolate(
 			_changingGeometryFrom.x(),
 			_geometryFull.x(),
-			layout.expandedRatio);
+			layout.ratio);
 		const auto top = anim::interpolate(
 			_changingGeometryFrom.y(),
 			_geometryFull.y(),
-			layout.expandedRatio);
+			layout.ratio);
 		p.translate(QPoint(left, top) - pos());
 	}
 
@@ -433,18 +408,19 @@ void List::paintEvent(QPaintEvent *e) {
 	const auto lookup = [&](int index) {
 		const auto indexSmall = layout.startIndexSmall + index;
 		const auto indexFull = layout.startIndexFull + index;
+		const auto k = (photoTop - photoTopSmall);
 		const auto ySmall = photoTopSmall
-			+ ((indexSmall - layout.smallSkip + 1)
-				* (photoTop - photoTopSmall) / 3.);
+			+ ((photoTop - photoTopSmall)
+				* (kSmallThumbsShown - indexSmall + layout.smallSkip) / 0.5);
 		const auto y = elerp(ySmall, photoTop);
 
 		const auto small = (drawSmall
 			&& indexSmall < layout.endIndexSmall
 			&& indexSmall >= layout.smallSkip)
-			? &rendering.items[indexSmall]
+			? &_data.items[indexSmall]
 			: nullptr;
 		const auto full = (drawFull && indexFull < layout.endIndexFull)
-			? &rendering.items[indexFull]
+			? &_data.items[indexFull]
 			: nullptr;
 		const auto x = layout.left + layout.single * index;
 		return Single{ x, indexSmall, small, indexFull, full, y };
@@ -556,9 +532,16 @@ void List::paintEvent(QPaintEvent *e) {
 
 		// White circle with possible read gray line.
 		const auto hasReadLine = (itemFull && !fullUnread);
+		p.setOpacity((small && itemFull)
+			? 1.
+			: small
+			? (1. - expandRatio)
+			: expandRatio);
 		if (hasReadLine) {
 			auto color = st::dialogsUnreadBgMuted->c;
-			color.setAlphaF(color.alphaF() * expandRatio);
+			if (small) {
+				color.setAlphaF(color.alphaF() * expandRatio);
+			}
 			auto pen = QPen(color);
 			pen.setWidthF(lineRead);
 			p.setPen(pen);
@@ -601,7 +584,7 @@ void List::paintEvent(QPaintEvent *e) {
 		p.setOpacity(1.);
 	});
 
-	paintSummary(p, rendering, summaryTop, ratio);
+	paintSummary(p, _data, summaryTop, ratio);
 }
 
 void List::validateThumbnail(not_null<Item*> item) {
@@ -729,7 +712,7 @@ void List::paintSummary(
 
 void List::wheelEvent(QWheelEvent *e) {
 	const auto horizontal = (e->angleDelta().x() != 0);
-	if (!horizontal) {
+	if (!horizontal || _state == State::Small) {
 		e->ignore();
 		return;
 	}
@@ -757,6 +740,10 @@ void List::wheelEvent(QWheelEvent *e) {
 void List::mousePressEvent(QMouseEvent *e) {
 	if (e->button() != Qt::LeftButton) {
 		return;
+	} else if (_state == State::Small) {
+		requestExpanded(true);
+	} else if (_state != State::Full) {
+		return;
 	}
 	_lastMousePosition = e->globalPos();
 	updateSelected();
@@ -834,25 +821,28 @@ void List::setExpandedHeight(int height, bool momentum) {
 		_expandIgnored = false;
 		_expandCatchUpAnimation.start([=] {
 			update();
-			if (!_expandCatchUpAnimation.animating()
-				&& _lastExpandedHeight == _st.full.height) {
-				setState(State::Full);
-			}
+			checkForFullState();
 		}, 0., 1., kExpandCatchUpDuration);
 	} else if (!height && _expandCatchUpAnimation.animating()) {
 		_expandCatchUpAnimation.stop();
 	}
 	_lastExpandedHeight = height;
-	setState(!height
-		? State::Small
-		: (height < _st.full.height
-			|| _expandCatchUpAnimation.animating()
-			|| _expandedAnimation.animating())
-		? State::Changing
-		: State::Full);
+	if (!checkForFullState()) {
+		setState(!height ? State::Small : State::Changing);
+	}
 	update();
 }
 
+bool List::checkForFullState() {
+	if (_expandCatchUpAnimation.animating()
+		|| _expandedAnimation.animating()
+		|| _lastExpandedHeight < _st.full.height) {
+		return false;
+	}
+	setState(State::Full);
+	return true;
+}
+
 void List::setLayoutConstraints(QPoint topRightSmall, QRect geometryFull) {
 	_topRightSmall = topRightSmall;
 	_geometryFull = geometryFull;
@@ -875,7 +865,9 @@ void List::updateGeometry() {
 QRect List::countSmallGeometry() const {
 	const auto &st = _st.small;
 	const auto layout = const_cast<List*>(this)->computeLayout();
-	const auto count = layout.endIndexSmall - layout.startIndexSmall;
+	const auto count = layout.endIndexSmall
+		- layout.startIndexSmall
+		- layout.smallSkip;
 	const auto width = st.left
 		+ st.photoLeft
 		+ st.photo + (count - 1) * st.shift
@@ -893,9 +885,6 @@ void List::setState(State state) {
 		return;
 	}
 	_state = state;
-	setAttribute(
-		Qt::WA_TransparentForMouseEvents,
-		state == State::Changing);
 	updateGeometry();
 }
 
diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h
index 0bf355bdb..7a02db40f 100644
--- a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h
+++ b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h
@@ -68,6 +68,12 @@ public:
 	void setExpandedHeight(int height, bool momentum = false);
 	void setLayoutConstraints(QPoint topRightSmall, QRect geometryFull);
 
+	[[nodiscard]] bool empty() const {
+		return _empty.current();
+	}
+	[[nodiscard]] rpl::producer<bool> emptyValue() const {
+		return _empty.value();
+	}
 	[[nodiscard]] rpl::producer<uint64> clicks() const;
 	[[nodiscard]] rpl::producer<ShowMenuRequest> showMenuRequests() const;
 	[[nodiscard]] rpl::producer<bool> toggleExpandedRequests() const;
@@ -153,12 +159,11 @@ private:
 	void checkLoadMore();
 	void requestExpanded(bool expanded);
 
+	bool checkForFullState();
 	void setState(State state);
 	void updateGeometry();
 	[[nodiscard]] QRect countSmallGeometry() const;
 	void updateExpanding(int expandingHeight, int expandedHeight);
-	void updateHeight();
-	void toggleAnimated(bool shown);
 	void paintSummary(
 		QPainter &p,
 		Data &data,
@@ -170,7 +175,6 @@ private:
 	const style::DialogsStoriesList &_st;
 	Content _content;
 	Data _data;
-	Data _hidingData;
 	rpl::event_stream<uint64> _clicks;
 	rpl::event_stream<ShowMenuRequest> _showMenuRequests;
 	rpl::event_stream<bool> _toggleExpandedRequests;
@@ -181,8 +185,7 @@ private:
 	QRect _geometryFull;
 	QRect _changingGeometryFrom;
 	State _state = State::Small;
-
-	Ui::Animations::Simple _shownAnimation;
+	rpl::variable<bool> _empty = true;
 
 	QPoint _lastMousePosition;
 	std::optional<QPoint> _mouseDownPosition;
diff --git a/Telegram/SourceFiles/ui/chat/more_chats_bar.h b/Telegram/SourceFiles/ui/chat/more_chats_bar.h
index 5536349c8..f920e4832 100644
--- a/Telegram/SourceFiles/ui/chat/more_chats_bar.h
+++ b/Telegram/SourceFiles/ui/chat/more_chats_bar.h
@@ -31,6 +31,10 @@ public:
 		rpl::producer<MoreChatsBarContent> content);
 	~MoreChatsBar();
 
+	[[nodiscard]] not_null<RpWidget*> wrap() {
+		return &_wrap;
+	}
+
 	void show();
 	void hide();
 	void raise();
diff --git a/Telegram/SourceFiles/ui/menu_icons.style b/Telegram/SourceFiles/ui/menu_icons.style
index 183026881..f1b1a2029 100644
--- a/Telegram/SourceFiles/ui/menu_icons.style
+++ b/Telegram/SourceFiles/ui/menu_icons.style
@@ -112,7 +112,6 @@ menuIconArchiveOpen: icon {{ "menu/archive_open", menuIconColor }};
 menuIconGroups: icon {{ "menu/groups", menuIconColor }};
 menuIconSavedMessages: icon {{ "menu/saved_messages", menuIconColor }};
 menuIconNightMode: icon {{ "menu/night_mode", menuIconColor }};
-menuIconStoriesToChats: icon {{ "menu/stories_to_chats", menuIconColor }};
 
 menuIconTTLAny: icon {{ "menu/auto_delete_plain", menuIconColor }};
 menuIconTTLAnyTextPosition: point(11px, 22px);
diff --git a/Telegram/lib_ui b/Telegram/lib_ui
index 763b3a37c..427fc4c8f 160000
--- a/Telegram/lib_ui
+++ b/Telegram/lib_ui
@@ -1 +1 @@
-Subproject commit 763b3a37c34fd7819f9d553b94387c4fe16554ff
+Subproject commit 427fc4c8f7c8b92cca900e0270718f83fcb16e35