diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 892983de5..220683f8e 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -3789,6 +3789,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_userpic_builder_color_subtitle" = "Choose background"; "lng_userpic_builder_emoji_subtitle" = "Choose sticker or emoji"; +"lng_stories_hide_to_contacts" = "Archive"; +"lng_stories_show_in_chats" = "Unarchive"; "lng_stories_row_count#one" = "{count} Story"; "lng_stories_row_count#other" = "{count} Stories"; "lng_stories_row_unread_and_one" = "{accumulated}, {user}"; diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index 0679d4dc9..dc757dc81 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -37,6 +37,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_profile.h" #include "styles/style_dialogs.h" +#include "data/data_stories.h" +#include "dialogs/ui/dialogs_stories_content.h" +#include "dialogs/ui/dialogs_stories_list.h" + + namespace { constexpr auto kSortByOnlineThrottle = 3 * crl::time(1000); @@ -51,10 +56,14 @@ object_ptr PrepareContactsBox( &sessionController->session()); const auto raw = controller.get(); auto init = [=](not_null box) { + using namespace Dialogs; + struct State { - QPointer toggleSort; + Stories::List *stories = nullptr; + QPointer<::Ui::IconButton> toggleSort; Mode mode = ContactsBoxController::SortMode::Online; }; + const auto state = box->lifetime().make_state(); box->addButton(tr::lng_close(), [=] { box->closeBox(); }); box->addLeftButton( @@ -69,6 +78,43 @@ object_ptr PrepareContactsBox( online ? &st::contactsSortOnlineIconOver : nullptr); }); raw->setSortMode(Mode::Online); + + auto stories = object_ptr( + box, + Stories::ContentForSession( + &sessionController->session(), + Data::StorySourcesList::All), + [=] { return state->stories->height() - box->scrollTop(); }); + const auto raw = state->stories = stories.data(); + box->peerListSetAboveWidget(std::move(stories)); + + raw->entered( + ) | rpl::start_with_next([=] { + //clearSelection(); + }, raw->lifetime()); + + raw->clicks( + ) | rpl::start_with_next([=](uint64 id) { + sessionController->openPeerStories(PeerId(int64(id)), {}); + }, raw->lifetime()); + + raw->showProfileRequests( + ) | rpl::start_with_next([=](uint64 id) { + sessionController->showPeerInfo(PeerId(int64(id))); + }, raw->lifetime()); + + raw->toggleShown( + ) | rpl::start_with_next([=](Stories::ToggleShownRequest request) { + sessionController->session().data().stories().toggleHidden( + PeerId(int64(request.id)), + !request.shown); + }, raw->lifetime()); + + raw->loadMoreRequests( + ) | rpl::start_with_next([=] { + sessionController->session().data().stories().loadMore( + Data::StorySourcesList::All); + }, raw->lifetime()); }; return Box(std::move(controller), std::move(init)); } diff --git a/Telegram/SourceFiles/data/data_stories.cpp b/Telegram/SourceFiles/data/data_stories.cpp index a3f17a0da..b801dad8d 100644 --- a/Telegram/SourceFiles/data/data_stories.cpp +++ b/Telegram/SourceFiles/data/data_stories.cpp @@ -350,9 +350,11 @@ void Stories::parseAndApply(const MTPUserStories &stories) { const auto peerId = peerFromUser(data.vuser_id()); const auto readTill = data.vmax_read_id().value_or_empty(); const auto count = int(data.vstories().v.size()); + const auto user = _owner->peer(peerId)->asUser(); auto result = StoriesSource{ - .user = _owner->peer(peerId)->asUser(), + .user = user, .readTill = readTill, + .hidden = user->hasStoriesHidden(), }; const auto &list = data.vstories().v; result.ids.reserve(list.size()); @@ -806,6 +808,58 @@ void Stories::markAsRead(FullStoryId id, bool viewed) { _markReadTimer.callOnce(kMarkAsReadDelay); } +void Stories::toggleHidden(PeerId peerId, bool hidden) { + const auto user = _owner->peer(peerId)->asUser(); + Assert(user != nullptr); + if (user->hasStoriesHidden() != hidden) { + user->setFlags(hidden + ? (user->flags() | UserDataFlag::StoriesHidden) + : (user->flags() & ~UserDataFlag::StoriesHidden)); + session().api().request(MTPcontacts_ToggleStoriesHidden( + user->inputUser, + MTP_bool(hidden) + )).send(); + } + + const auto i = _all.find(peerId); + if (i == end(_all)) { + return; + } + i->second.hidden = hidden; + const auto main = static_cast(StorySourcesList::NotHidden); + const auto all = static_cast(StorySourcesList::All); + if (hidden) { + const auto i = ranges::find( + _sources[main], + peerId, + &StoriesSourceInfo::id); + if (i != end(_sources[main])) { + _sources[main].erase(i); + _sourcesChanged[main].fire({}); + } + const auto j = ranges::find(_sources[all], peerId, &StoriesSourceInfo::id); + if (j != end(_sources[all])) { + j->hidden = hidden; + _sourcesChanged[all].fire({}); + } + } else { + const auto i = ranges::find( + _sources[all], + peerId, + &StoriesSourceInfo::id); + if (i != end(_sources[all])) { + i->hidden = hidden; + _sourcesChanged[all].fire({}); + + auto &sources = _sources[main]; + if (!ranges::contains(sources, peerId, &StoriesSourceInfo::id)) { + sources.push_back(*i); + sort(StorySourcesList::NotHidden); + } + } + } +} + void Stories::sendMarkAsReadRequest( not_null peer, StoryId tillId) { diff --git a/Telegram/SourceFiles/data/data_stories.h b/Telegram/SourceFiles/data/data_stories.h index dc7b99141..ded86dce3 100644 --- a/Telegram/SourceFiles/data/data_stories.h +++ b/Telegram/SourceFiles/data/data_stories.h @@ -176,6 +176,8 @@ public: [[nodiscard]] bool isQuitPrevent(); void markAsRead(FullStoryId id, bool viewed); + void toggleHidden(PeerId peerId, bool hidden); + static constexpr auto kViewsPerPage = 50; void loadViewsSlice( StoryId id, diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index fb15560a3..cb559842e 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -351,18 +351,9 @@ InnerWidget::InnerWidget( _stories->toggleShown( ) | rpl::start_with_next([=](Stories::ToggleShownRequest request) { - const auto peerId = PeerId(int64(request.id)); - const auto user = session().data().peer(peerId)->asUser(); - Assert(user != nullptr); - if (user->hasStoriesHidden() == request.shown) { - user->setFlags(request.shown - ? (user->flags() & ~UserDataFlag::StoriesHidden) - : (user->flags() | UserDataFlag::StoriesHidden)); - session().api().request(MTPcontacts_ToggleStoriesHidden( - user->inputUser, - MTP_bool(!request.shown) - )).send(); - } + session().data().stories().toggleHidden( + PeerId(int64(request.id)), + !request.shown); }, lifetime()); _stories->loadMoreRequests( diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_content.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_content.cpp index 37016cfa5..bb9fd7578 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_content.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_content.cpp @@ -149,6 +149,7 @@ Content State::next() { .name = user->shortName(), .userpic = std::move(userpic), .unread = info.unread, + .hidden = info.hidden, }); } return result; diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp index 5b2e24b08..78a3dc3a3 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp @@ -99,6 +99,7 @@ void List::showContent(Content &&content) { item.nameCache = QImage(); } item.user.unread = user.unread; + item.user.hidden = user.hidden; } else { _data.items.emplace_back(Item{ .user = user }); } @@ -429,8 +430,8 @@ void List::paintEvent(QPaintEvent *e) { }); p.setBrush(gradient); p.drawEllipse(outer); - p.setOpacity(1.); } + p.setOpacity(1.); }, [&](Single single) { Expects(single.itemSmall || single.itemFull); @@ -710,19 +711,24 @@ void List::contextMenuEvent(QContextMenuEvent *e) { const auto id = item.user.id; const auto hidden = item.user.hidden; - _menu->addAction(u"View Profile"_q, [=] { + _menu->addAction(tr::lng_context_view_profile(tr::now), [=] { _showProfileRequests.fire_copy(id); }); - _menu->addAction(hidden ? u"Show in Chats"_q : u"Hide"_q, [=] { - _toggleShown.fire({ .id = id, .shown = hidden }); - }); - QObject::connect(_menu.get(), &QObject::destroyed, [=] { + _menu->addAction(hidden + ? tr::lng_stories_show_in_chats(tr::now) + : tr::lng_stories_hide_to_contacts(tr::now), + [=] { _toggleShown.fire({ .id = id, .shown = hidden }); }); + const auto updateAfterMenuDestroyed = [=] { const auto globalPosition = QCursor::pos(); if (rect().contains(mapFromGlobal(globalPosition))) { _lastMousePosition = globalPosition; updateSelected(); } - }); + }; + QObject::connect( + _menu.get(), + &QObject::destroyed, + crl::guard(&_menuGuard, updateAfterMenuDestroyed)); if (_menu->empty()) { _menu = nullptr; } else { diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h index fd1dc3d5b..6789765db 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "base/qt/qt_compare.h" +#include "base/weak_ptr.h" #include "ui/rp_widget.h" class QPainter; @@ -160,6 +161,7 @@ private: int _pressed = -1; base::unique_qptr _menu; + base::has_weak_ptr _menuGuard; }; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 00bb0f645..75c7a43bb 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -3061,7 +3061,11 @@ void OverlayWidget::show(OpenRequest request) { setSession(&photo->session()); if (story) { - setContext(StoriesContext{ story->peer(), story->id() }); + setContext(StoriesContext{ + story->peer(), + story->id(), + request.storiesList(), + }); } else if (contextPeer) { setContext(contextPeer); } else if (contextItem) {