diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 42a8644d1..8df77697a 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -522,6 +522,7 @@ not_null Session::processUser(const MTPUser &data) { ? Flag::Contact | Flag::MutualContact | Flag::DiscardMinPhoto + | Flag::StoriesHidden : Flag()); const auto flagsSet = (data.is_deleted() ? Flag::Deleted : Flag()) | (data.is_verified() ? Flag::Verified : Flag()) @@ -534,6 +535,7 @@ not_null Session::processUser(const MTPUser &data) { ? (data.is_contact() ? Flag::Contact : Flag()) | (data.is_mutual_contact() ? Flag::MutualContact : Flag()) | (data.is_apply_min_photo() ? Flag() : Flag::DiscardMinPhoto) + | (data.is_stories_hidden() ? Flag::StoriesHidden : Flag()) : Flag()); result->setFlags((result->flags() & ~flagsMask) | flagsSet); if (minimal) { diff --git a/Telegram/SourceFiles/data/data_stories.cpp b/Telegram/SourceFiles/data/data_stories.cpp index 90d697126..b9fe9d67e 100644 --- a/Telegram/SourceFiles/data/data_stories.cpp +++ b/Telegram/SourceFiles/data/data_stories.cpp @@ -71,10 +71,10 @@ Story::Story( not_null peer, StoryMedia media, TimeId date) - : _id(id) - , _peer(peer) - , _media(std::move(media)) - , _date(date) { +: _id(id) +, _peer(peer) +, _media(std::move(media)) +, _date(date) { } Session &Story::owner() const { diff --git a/Telegram/SourceFiles/data/data_stories.h b/Telegram/SourceFiles/data/data_stories.h index aa0fcbda5..e41312f5e 100644 --- a/Telegram/SourceFiles/data/data_stories.h +++ b/Telegram/SourceFiles/data/data_stories.h @@ -94,6 +94,7 @@ struct StoriesList { base::flat_set ids; StoryId readTill = 0; int total = 0; + bool hidden = false; [[nodiscard]] bool unread() const; diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index a5c6de084..c903008ae 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -321,6 +321,10 @@ bool UserData::hasPersonalPhoto() const { return (flags() & UserDataFlag::PersonalPhoto); } +bool UserData::hasStoriesHidden() const { + return (flags() & UserDataFlag::StoriesHidden); +} + bool UserData::canAddContact() const { return canShareThisContact() && !isContact(); } diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index cc32613f9..ad0dec4d4 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -62,6 +62,7 @@ enum class UserDataFlag { CanReceiveGifts = (1 << 15), VoiceMessagesForbidden = (1 << 16), PersonalPhoto = (1 << 17), + StoriesHidden = (1 << 18), }; inline constexpr bool is_flag_type(UserDataFlag) { return true; }; using UserDataFlags = base::flags; @@ -119,6 +120,7 @@ public: [[nodiscard]] bool isInaccessible() const; [[nodiscard]] bool applyMinPhoto() const; [[nodiscard]] bool hasPersonalPhoto() const; + [[nodiscard]] bool hasStoriesHidden() const; [[nodiscard]] bool canShareThisContact() const; [[nodiscard]] bool canAddContact() const; diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 1b354b377..bbc9a2c49 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -342,6 +342,27 @@ InnerWidget::InnerWidget( _controller->openPeerStories(PeerId(int64(id))); }, lifetime()); + _stories->showProfileRequests( + ) | rpl::start_with_next([=](uint64 id) { + _controller->showPeerInfo(PeerId(int64(id))); + }, lifetime()); + + _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(); + } + }, lifetime()); + _stories->loadMoreRequests( ) | rpl::start_with_next([=] { session().data().stories().loadMore(); diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp index 58625ddef..5b2e24b08 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "dialogs/ui/dialogs_stories_list.h" #include "lang/lang_keys.h" +#include "ui/widgets/popup_menu.h" #include "ui/painter.h" #include "styles/style_dialogs.h" @@ -219,6 +220,14 @@ rpl::producer List::clicks() const { return _clicks.events(); } +rpl::producer List::showProfileRequests() const { + return _showProfileRequests.events(); +} + +rpl::producer List::toggleShown() const { + return _toggleShown.events(); +} + rpl::producer<> List::expandRequests() const { return _expandRequests.events(); } @@ -685,6 +694,43 @@ void List::mouseReleaseEvent(QMouseEvent *e) { } } +void List::contextMenuEvent(QContextMenuEvent *e) { + _menu = nullptr; + + if (e->reason() == QContextMenuEvent::Mouse) { + _lastMousePosition = e->globalPos(); + updateSelected(); + } + if (_selected < 0 || _data.empty()) { + return; + } + + auto &item = _data.items[_selected]; + _menu = base::make_unique_q(this); + + const auto id = item.user.id; + const auto hidden = item.user.hidden; + _menu->addAction(u"View Profile"_q, [=] { + _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, [=] { + const auto globalPosition = QCursor::pos(); + if (rect().contains(mapFromGlobal(globalPosition))) { + _lastMousePosition = globalPosition; + updateSelected(); + } + }); + if (_menu->empty()) { + _menu = nullptr; + } else { + _menu->popup(e->globalPos()); + e->accept(); + } +} + bool List::finishDragging() { if (!_dragging) { return false; diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h index 06a0fc286..fd1dc3d5b 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h @@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class QPainter; +namespace Ui { +class PopupMenu; +} // namespace Ui + namespace Dialogs::Stories { class Userpic { @@ -25,6 +29,7 @@ struct User { QString name; std::shared_ptr userpic; bool unread = false; + bool hidden = false; friend inline bool operator==(const User &a, const User &b) = default; }; @@ -37,6 +42,11 @@ struct Content { const Content &b) = default; }; +struct ToggleShownRequest { + uint64 id = 0; + bool shown = false; +}; + class List final : public Ui::RpWidget { public: List( @@ -45,6 +55,8 @@ public: Fn shownHeight); [[nodiscard]] rpl::producer clicks() const; + [[nodiscard]] rpl::producer showProfileRequests() const; + [[nodiscard]] rpl::producer toggleShown() const; [[nodiscard]] rpl::producer<> expandRequests() const; [[nodiscard]] rpl::producer<> entered() const; [[nodiscard]] rpl::producer<> loadMoreRequests() const; @@ -103,6 +115,7 @@ private: void mousePressEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; + void contextMenuEvent(QContextMenuEvent *e) override; void validateUserpic(not_null item); void validateName(not_null item); @@ -128,6 +141,8 @@ private: Data _hidingData; Fn _shownHeight = 0; rpl::event_stream _clicks; + rpl::event_stream _showProfileRequests; + rpl::event_stream _toggleShown; rpl::event_stream<> _expandRequests; rpl::event_stream<> _entered; rpl::event_stream<> _loadMoreRequests; @@ -144,6 +159,8 @@ private: int _selected = -1; int _pressed = -1; + base::unique_qptr _menu; + }; } // namespace Dialogs::Stories