From 65df1376102c0cd8c4e5120e18d1c6741a7a0657 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 23 Jan 2018 19:51:12 +0300 Subject: [PATCH] Add group/ungroup action in channel peer menu. --- Telegram/Resources/langs/lang.strings | 3 + Telegram/SourceFiles/apiwrap.cpp | 26 ++++++ Telegram/SourceFiles/apiwrap.h | 15 +++- Telegram/SourceFiles/data/data_feed.cpp | 81 ++++++++++++++++++- Telegram/SourceFiles/data/data_feed.h | 52 +++++------- .../SourceFiles/data/data_feed_messages.cpp | 10 ++- Telegram/SourceFiles/data/data_messages.cpp | 34 +++++++- Telegram/SourceFiles/data/data_messages.h | 2 + Telegram/SourceFiles/data/data_peer.cpp | 7 +- Telegram/SourceFiles/data/data_session.cpp | 2 +- Telegram/SourceFiles/data/data_session.h | 4 + .../SourceFiles/dialogs/dialogs_entry.cpp | 56 ++++++++++--- Telegram/SourceFiles/dialogs/dialogs_entry.h | 34 +++----- Telegram/SourceFiles/history/history.cpp | 40 +++++---- Telegram/SourceFiles/history/history.h | 19 +++-- .../SourceFiles/history/history_widget.cpp | 2 +- .../history/view/history_view_list_widget.cpp | 7 ++ Telegram/SourceFiles/mainwidget.cpp | 15 ++-- .../SourceFiles/storage/storage_facade.cpp | 18 +++++ Telegram/SourceFiles/storage/storage_facade.h | 3 + .../storage/storage_feed_messages.cpp | 12 +++ .../storage/storage_feed_messages.h | 12 +++ .../SourceFiles/window/window_peer_menu.cpp | 10 +++ .../SourceFiles/window/window_peer_menu.h | 1 + 24 files changed, 347 insertions(+), 118 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 1fb865d08..7d15f61ad 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1418,6 +1418,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_feed_name" = "Feed"; "lng_feed_show_next" = "Show Next"; +"lng_feed_group" = "Group in feed"; +"lng_feed_ungroup" = "Ungroup from feed"; + "lng_info_feed_title" = "Feed Info"; // Wnd specific diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index d807da049..0410e5335 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -170,6 +170,32 @@ void ApiWrap::savePinnedOrder() { )).send(); } +void ApiWrap::toggleChannelGrouping( + not_null channel, + bool group) { + if (const auto already = _channelGroupingRequests.take(channel)) { + request(*already).cancel(); + } + const auto feedId = Data::Feed::kId; + const auto flags = group + ? MTPchannels_ChangeFeedBroadcast::Flag::f_feed_id + : MTPchannels_ChangeFeedBroadcast::Flag(0); + const auto requestId = request(MTPchannels_ChangeFeedBroadcast( + MTP_flags(flags), + channel->inputChannel, + MTP_int(feedId) + )).done([=](const MTPBool &result) { + _channelGroupingRequests.remove(channel); + if (group) { + channel->setFeed(Auth().data().feed(feedId)); + } else { + channel->clearFeed(); + } + }).fail([=](const RPCError &error) { + _channelGroupingRequests.remove(channel); + }).send(); +} + void ApiWrap::sendMessageFail(const RPCError &error) { if (error.type() == qstr("PEER_FLOOD")) { Ui::show(Box( diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 99b64fd9c..198ebbf5e 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -46,6 +46,7 @@ public: void applyUpdates(const MTPUpdates &updates, uint64 sentMessageRandomId = 0); void savePinnedOrder(); + void toggleChannelGrouping(not_null channel, bool group); using RequestMessageDataCallback = base::lambda; void requestMessageData( @@ -92,9 +93,13 @@ public: void scheduleStickerSetRequest(uint64 setId, uint64 access); void requestStickerSets(); - void saveStickerSets(const Stickers::Order &localOrder, const Stickers::Order &localRemoved); + void saveStickerSets( + const Stickers::Order &localOrder, + const Stickers::Order &localRemoved); void updateStickers(); - void setGroupStickerSet(not_null megagroup, const MTPInputStickerSet &set); + void setGroupStickerSet( + not_null megagroup, + const MTPInputStickerSet &set); void joinChannel(ChannelData *channel); void leaveChannel(ChannelData *channel); @@ -369,7 +374,11 @@ private: ChannelData *_channelMembersForAdd = nullptr; mtpRequestId _channelMembersForAddRequestId = 0; - base::lambda _channelMembersForAddCallback; + base::lambda _channelMembersForAddCallback; + base::flat_map< + not_null, + mtpRequestId> _channelGroupingRequests; using KickRequest = std::pair< not_null, diff --git a/Telegram/SourceFiles/data/data_feed.cpp b/Telegram/SourceFiles/data/data_feed.cpp index badfe2380..73fdd78bf 100644 --- a/Telegram/SourceFiles/data/data_feed.cpp +++ b/Telegram/SourceFiles/data/data_feed.cpp @@ -7,10 +7,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "data/data_feed.h" +#include "data/data_session.h" #include "dialogs/dialogs_key.h" #include "history/history.h" #include "history/history_item.h" #include "lang/lang_keys.h" +#include "storage/storage_facade.h" +#include "storage/storage_feed_messages.h" +#include "auth_session.h" namespace Data { @@ -23,13 +27,18 @@ MessagePosition FeedPositionFromMTP(const MTPFeedPosition &position) { data.vid.v)); } -Feed::Feed(FeedId id) +Feed::Feed(FeedId id, not_null parent) : Entry(this) , _id(id) +, _parent(parent) , _name(lang(lng_feed_name)) { indexNameParts(); } +FeedId Feed::id() const { + return _id; +} + void Feed::indexNameParts() { _nameWords.clear(); _nameFirstLetters.clear(); @@ -59,18 +68,37 @@ void Feed::indexNameParts() { void Feed::registerOne(not_null channel) { const auto history = App::history(channel); if (!base::contains(_channels, history)) { + const auto invisible = (_channels.size() < 2); _channels.push_back(history); if (history->lastMsg) { updateLastMessage(history->lastMsg); } + _parent->session().storage().remove( + Storage::FeedMessagesInvalidate(_id)); + + history->updateChatListExistence(); + if (invisible && _channels.size() > 1) { + updateChatListExistence(); + } } } void Feed::unregisterOne(not_null channel) { const auto history = App::history(channel); - _channels.erase(ranges::remove(_channels, history), end(_channels)); - if (_lastMessage->history() == history) { - messageRemoved(_lastMessage); + const auto i = ranges::remove(_channels, history); + if (i != end(_channels)) { + const auto visible = (_channels.size() > 1); + _channels.erase(i, end(_channels)); + if (_lastMessage && _lastMessage->history() == history) { + messageRemoved(_lastMessage); + } + _parent->session().storage().remove( + Storage::FeedMessagesRemoveAll(_id, channel->bareId())); + + history->updateChatListExistence(); + if (visible && _channels.size() < 2) { + updateChatListExistence(); + } } } @@ -105,6 +133,7 @@ void Feed::paintUserpic( case 1: case 3: x += delta; break; case 2: x -= delta; y += delta; break; + case 4: return; } } } @@ -146,4 +175,48 @@ void Feed::setUnreadCounts(int unreadCount, int unreadMutedCount) { _unreadMutedCount = unreadMutedCount; } +void Feed::setUnreadPosition(const MessagePosition &position) { + _unreadPosition = position; +} + +MessagePosition Feed::unreadPosition() const { + return _unreadPosition.current(); +} + +rpl::producer Feed::unreadPositionChanges() const { + return _unreadPosition.changes(); +} + +bool Feed::toImportant() const { + return false; // TODO feeds workmode +} + +bool Feed::shouldBeInChatList() const { + return _channels.size() > 1; +} + +int Feed::chatListUnreadCount() const { + return _unreadCount; +} + +bool Feed::chatListMutedBadge() const { + return _unreadCount <= _unreadMutedCount; +} + +HistoryItem *Feed::chatsListItem() const { + return _lastMessage; +} + +const QString &Feed::chatsListName() const { + return _name; +} + +const base::flat_set &Feed::chatsListNameWords() const { + return _nameWords; +} + +const base::flat_set &Feed::chatsListFirstLetters() const { + return _nameFirstLetters; +} + } // namespace Data diff --git a/Telegram/SourceFiles/data/data_feed.h b/Telegram/SourceFiles/data/data_feed.h index f9145bc8f..5d7c136a6 100644 --- a/Telegram/SourceFiles/data/data_feed.h +++ b/Telegram/SourceFiles/data/data_feed.h @@ -14,15 +14,17 @@ class ChannelData; namespace Data { +class Session; + MessagePosition FeedPositionFromMTP(const MTPFeedPosition &position); class Feed : public Dialogs::Entry { public: - Feed(FeedId id); + static constexpr auto kId = 1; - FeedId id() const { - return _id; - } + Feed(FeedId id, not_null parent); + + FeedId id() const; void registerOne(not_null channel); void unregisterOne(not_null channel); @@ -31,37 +33,18 @@ public: void historyCleared(not_null history); void setUnreadCounts(int unreadCount, int unreadMutedCount); - void setUnreadPosition(const MessagePosition &position) { - _unreadPosition = position; - } - MessagePosition unreadPosition() const { - return _unreadPosition.current(); - } - rpl::producer unreadPositionChanges() const { - return _unreadPosition.changes(); - } + void setUnreadPosition(const MessagePosition &position); + MessagePosition unreadPosition() const; + rpl::producer unreadPositionChanges() const; - bool toImportant() const override { - return false; // TODO feeds workmode - } - int chatListUnreadCount() const override { - return _unreadCount; - } - bool chatListMutedBadge() const override { - return _unreadCount <= _unreadMutedCount; - } - HistoryItem *chatsListItem() const override { - return _lastMessage; - } - const QString &chatsListName() const override { - return _name; - } - const base::flat_set &chatsListNameWords() const override { - return _nameWords; - } - const base::flat_set &chatsListFirstLetters() const override { - return _nameFirstLetters; - } + bool toImportant() const override; + bool shouldBeInChatList() const override; + int chatListUnreadCount() const override; + bool chatListMutedBadge() const override; + HistoryItem *chatsListItem() const override; + const QString &chatsListName() const override; + const base::flat_set &chatsListNameWords() const override; + const base::flat_set &chatsListFirstLetters() const override; void loadUserpic() override; void paintUserpic( @@ -76,6 +59,7 @@ private: bool justSetLastMessage(not_null item); FeedId _id = 0; + not_null _parent; std::vector> _channels; QString _name; diff --git a/Telegram/SourceFiles/data/data_feed_messages.cpp b/Telegram/SourceFiles/data/data_feed_messages.cpp index 4203f27ae..8d43b376d 100644 --- a/Telegram/SourceFiles/data/data_feed_messages.cpp +++ b/Telegram/SourceFiles/data/data_feed_messages.cpp @@ -64,8 +64,16 @@ rpl::producer FeedMessagesViewer( Auth().storage().feedMessagesAllRemoved( ) | rpl::filter([=](const AllRemoved &update) { return (update.feedId == key.feedId); + }) | rpl::filter([=](const AllRemoved &update) { + return builder->removeFromChannel(update.channelId); + }) | rpl::start_with_next(pushNextSnapshot, lifetime); + + using Invalidate = Storage::FeedMessagesInvalidate; + Auth().storage().feedMessagesInvalidated( + ) | rpl::filter([=](const Invalidate &update) { + return (update.feedId == key.feedId); }) | rpl::filter([=] { - return builder->removeAll(); + return builder->invalidated(); }) | rpl::start_with_next(pushNextSnapshot, lifetime); using Result = Storage::FeedMessagesResult; diff --git a/Telegram/SourceFiles/data/data_messages.cpp b/Telegram/SourceFiles/data/data_messages.cpp index 832244ac6..4204c3001 100644 --- a/Telegram/SourceFiles/data/data_messages.cpp +++ b/Telegram/SourceFiles/data/data_messages.cpp @@ -136,7 +136,7 @@ void MessagesList::removeOne(MessagePosition messageId) { std::less<>(), [](const Slice &slice) { return slice.range.till; }); if (slice != _slices.end() && slice->range.from <= messageId) { - _slices.modify(slice, [messageId](Slice &slice) { + _slices.modify(slice, [&](Slice &slice) { return slice.messages.remove(messageId); }); } @@ -146,9 +146,28 @@ void MessagesList::removeOne(MessagePosition messageId) { } void MessagesList::removeAll(ChannelId channelId) { - // #TODO feeds show - //_slices.clear(); - //_slices.emplace(base::flat_set{}, FullMessagesRange); + auto removed = 0; + for (auto i = begin(_slices); i != end(_slices); ++i) { + _slices.modify(i, [&](Slice &slice) { + auto &messages = slice.messages; + for (auto j = begin(messages); j != end(messages);) { + if (j->fullId.channel == channelId) { + j = messages.erase(j); + ++removed; + } else { + ++j; + } + } + }); + } + if (removed && _count) { + *_count -= removed; + } +} + +void MessagesList::invalidated() { + _slices.clear(); + _count = base::none; } rpl::producer MessagesList::query( @@ -302,6 +321,13 @@ bool MessagesSliceBuilder::removeFromChannel(ChannelId channelId) { return true; } +bool MessagesSliceBuilder::invalidated() { + _fullCount = _skippedBefore = _skippedAfter = base::none; + _ids.clear(); + checkInsufficient(); + return false; +} + void MessagesSliceBuilder::checkInsufficient() { sliceToLimits(); } diff --git a/Telegram/SourceFiles/data/data_messages.h b/Telegram/SourceFiles/data/data_messages.h index 5fa3f57bb..2f1647d7d 100644 --- a/Telegram/SourceFiles/data/data_messages.h +++ b/Telegram/SourceFiles/data/data_messages.h @@ -130,6 +130,7 @@ public: base::optional count); void removeOne(MessagePosition messageId); void removeAll(ChannelId channelId); + void invalidated(); rpl::producer query(MessagesQuery &&query) const; rpl::producer sliceUpdated() const; @@ -194,6 +195,7 @@ public: bool removeOne(MessagePosition messageId); bool removeFromChannel(ChannelId channelId); bool removeAll(); + bool invalidated(); void checkInsufficient(); struct AroundData { diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 0b43831a4..96a7c1f7f 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -862,10 +862,11 @@ void ChannelData::clearFeed() { void ChannelData::setFeedPointer(Data::Feed *feed) { if (_feed != feed) { - if (_feed) { - _feed->unregisterOne(this); - } + const auto was = _feed; _feed = feed; + if (was) { + was->unregisterOne(this); + } if (_feed) { _feed->registerOne(this); } diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 9fb4df499..731cc3216 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -1426,7 +1426,7 @@ not_null Session::feed(FeedId id) { } const auto [it, ok] = _feeds.emplace( id, - std::make_unique(id)); + std::make_unique(id, this)); return it->second.get(); } diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index e9caf0735..8fa55c9c9 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -37,6 +37,10 @@ public: explicit Session(not_null session); ~Session(); + AuthSession &session() const { + return *_session; + } + base::Variable &contactsLoaded() { return _contactsLoaded; } diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp index 4b99e9901..346454b60 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp @@ -65,26 +65,56 @@ void Entry::cachePinnedIndex(int index) { } } +bool Entry::needUpdateInChatList() const { + return inChatList(Dialogs::Mode::All) || shouldBeInChatList(); +} + void Entry::updateChatListSortPosition() { _sortKeyInChatList = isPinnedDialog() ? PinnedDialogPos(_pinnedIndex) : DialogPosFromDate(adjustChatListDate()); - if (auto m = App::main()) { - if (needUpdateInChatList()) { - if (_sortKeyInChatList) { - m->createDialog(_key); - updateChatListEntry(); - } else { - removeDialog(); - } + if (needUpdateInChatList()) { + setChatListExistence(true); + } +} + +void Entry::updateChatListExistence() { + setChatListExistence(shouldBeInChatList()); +} + +void Entry::setChatListExistence(bool exists) { + if (const auto main = App::main()) { + if (exists && _sortKeyInChatList) { + main->createDialog(_key); + updateChatListEntry(); + } else { + main->removeDialog(_key); } } } -void Entry::removeDialog() { - if (const auto main = App::main()) { - main->removeDialog(_key); - } +QDateTime Entry::adjustChatListDate() const { + return chatsListDate(); +} + +void Entry::changedInChatListHook(Dialogs::Mode list, bool added) { +} + +void Entry::changedChatListPinHook() { +} + +RowsByLetter &Entry::chatListLinks(Mode list) { + return _chatListLinks[static_cast(list)]; +} + +const RowsByLetter &Entry::chatListLinks(Mode list) const { + return _chatListLinks[static_cast(list)]; +} + +Row *Entry::mainChatListLink(Mode list) const { + auto it = chatListLinks(list).find(0); + Assert(it != chatListLinks(list).cend()); + return it->second; } PositionChange Entry::adjustByPosInChatList( @@ -99,7 +129,7 @@ PositionChange Entry::adjustByPosInChatList( void Entry::setChatsListDate(const QDateTime &date) { if (!_lastMessageDate.isNull() && _lastMessageDate >= date) { - if (!needUpdateInChatList() || !inChatList(Dialogs::Mode::All)) { + if (!inChatList(Dialogs::Mode::All)) { return; } } diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.h b/Telegram/SourceFiles/dialogs/dialogs_entry.h index 261661782..6e210a861 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.h +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.h @@ -63,13 +63,11 @@ public: } void updateChatListSortPosition(); void setChatsListDate(const QDateTime &date); + void updateChatListExistence(); + bool needUpdateInChatList() const; virtual bool toImportant() const = 0; - - virtual bool needUpdateInChatList() const { - return true; - } - + virtual bool shouldBeInChatList() const = 0; virtual int chatListUnreadCount() const = 0; virtual bool chatListMutedBadge() const = 0; virtual HistoryItem *chatsListItem() const = 0; @@ -102,26 +100,14 @@ public: mutable Text lastItemTextCache; private: - virtual QDateTime adjustChatListDate() const { - return chatsListDate(); - } - virtual void removeDialog(); - virtual void changedInChatListHook(Dialogs::Mode list, bool added) { - } - virtual void changedChatListPinHook() { - } + virtual QDateTime adjustChatListDate() const; + virtual void changedInChatListHook(Dialogs::Mode list, bool added); + virtual void changedChatListPinHook(); - RowsByLetter &chatListLinks(Mode list) { - return _chatListLinks[static_cast(list)]; - } - const RowsByLetter &chatListLinks(Mode list) const { - return _chatListLinks[static_cast(list)]; - } - Row *mainChatListLink(Mode list) const { - auto it = chatListLinks(list).find(0); - Assert(it != chatListLinks(list).cend()); - return it->second; - } + void setChatListExistence(bool exists); + RowsByLetter &chatListLinks(Mode list); + const RowsByLetter &chatListLinks(Mode list) const; + Row *mainChatListLink(Mode list) const; Dialogs::Key _key; RowsByLetter _chatListLinks[2]; diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index aad620e44..bd598d69e 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/calls_instance.h" #include "storage/storage_facade.h" #include "storage/storage_shared_media.h" +#include "storage/storage_feed_messages.h" #include "data/data_channel_admins.h" #include "data/data_feed.h" #include "ui/text_options.h" @@ -1478,7 +1479,13 @@ HistoryBlock *History::prepareBlockForAddingItem() { blocks.back()->messages.reserve(kNewBlockEachMessage); } return blocks.back().get(); -}; +} + +void History::viewReplaced(not_null was, Element *now) { + if (scrollTopItem == was) scrollTopItem= now; + if (_firstUnreadView == was) _firstUnreadView= now; + if (_unreadBarView == was) _unreadBarView = now; +} void History::addItemToBlock(not_null item) { Expects(!item->mainView()); @@ -1908,7 +1915,7 @@ void History::getNextFirstUnreadMessage() { const auto count = int(block->messages.size()); for (auto i = index + 1; i != count; ++i) { const auto &message = block->messages[i]; - if (setFromMessage(block->messages[i])) { + if (setFromMessage(message)) { return; } } @@ -2264,15 +2271,17 @@ void History::setLastMessage(HistoryItem *msg) { } } -bool History::needUpdateInChatList() const { - if (inChatList(Dialogs::Mode::All)) { - return true; - } else if (peer->migrateTo()) { +bool History::shouldBeInChatList() const { + if (peer->migrateTo()) { return false; } else if (isPinnedDialog()) { return true; } else if (const auto channel = peer->asChannel()) { - return !channel->feed() && channel->amIn(); + if (!channel->amIn()) { + return false; + } else if (const auto feed = channel->feed()) { + return !feed->needUpdateInChatList(); + } } return true; } @@ -2396,6 +2405,13 @@ void History::clear(bool leaveItems) { setLastMessage(nullptr); notifies.clear(); Auth().storage().remove(Storage::SharedMediaRemoveAll(peer->id)); + if (const auto channel = peer->asChannel()) { + if (const auto feed = channel->feed()) { + Auth().storage().remove(Storage::FeedMessagesRemoveAll( + feed->id(), + channel->bareId())); + } + } Auth().data().notifyHistoryCleared(this); } clearBlocks(leaveItems); @@ -2483,12 +2499,6 @@ void History::clearOnDestroy() { clearBlocks(false); } -void History::removeDialog() { - if (const auto main = App::main()) { - main->deleteConversation(peer, false); - } -} - void History::changedInChatListHook(Dialogs::Mode list, bool added) { if (list == Dialogs::Mode::All && unreadCount()) { const auto delta = added ? unreadCount() : -unreadCount(); @@ -2588,9 +2598,7 @@ void HistoryBlock::refreshView(not_null view) { auto blockIndex = indexInHistory(); auto itemIndex = view->indexInBlock(); - if (_history->scrollTopItem == view) { - _history->scrollTopItem = refreshed.get(); - } + _history->viewReplaced(view, refreshed.get()); messages[itemIndex] = std::move(refreshed); messages[itemIndex]->attachToBlock(this, itemIndex); diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 99b491a97..56f97b8d9 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -130,6 +130,8 @@ class IndexedList; class ChannelHistory; class History : public Dialogs::Entry { public: + using Element = HistoryView::Element; + History(const PeerId &peerId); History(const History &) = delete; History &operator=(const History &) = delete; @@ -198,10 +200,10 @@ public: void addUnreadBar(); void destroyUnreadBar(); bool hasNotFreezedUnreadBar() const; - HistoryView::Element *unreadBar() const; + Element *unreadBar() const; void calculateFirstUnreadMessage(); void unsetFirstUnreadMessage(); - HistoryView::Element *firstUnreadMessage() const; + Element *firstUnreadMessage() const; void clearNotifications(); bool loadedAtBottom() const; // last message is in the list @@ -310,7 +312,7 @@ public: HistoryItemsList validateForwardDraft(); void setForwardDraft(MessageIdsList &&items); - bool needUpdateInChatList() const override; + bool shouldBeInChatList() const override; bool toImportant() const override { return !mute(); } @@ -363,7 +365,7 @@ public: // we save a pointer of the history item at the top of the displayed window // together with an offset from the window top to the top of this message // resulting scrollTop = top(scrollTopItem) + scrollTopOffset - HistoryView::Element *scrollTopItem = nullptr; + Element *scrollTopItem = nullptr; int scrollTopOffset = 0; bool lastKeyboardInited = false; @@ -439,10 +441,9 @@ private: void mainViewRemoved( not_null block, - not_null view); + not_null view); QDateTime adjustChatListDate() const override; - void removeDialog() override; void changedInChatListHook(Dialogs::Mode list, bool added) override; void changedChatListPinHook() override; @@ -474,13 +475,15 @@ private: // Depending on isBuildingFrontBlock() gets front or back block. HistoryBlock *prepareBlockForAddingItem(); + void viewReplaced(not_null was, Element *now); + Flags _flags = 0; bool _mute = false; int _unreadCount = 0; int _width = 0; int _height = 0; - HistoryView::Element *_unreadBarView = nullptr; - HistoryView::Element *_firstUnreadView = nullptr; + Element *_unreadBarView = nullptr; + Element *_firstUnreadView = nullptr; base::optional _unreadMentionsCount; base::flat_set _unreadMentions; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 7cdb1a6e8..64d1673f8 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -4349,11 +4349,11 @@ void HistoryWidget::sendFileConfirmed( NewMessageUnread); } + Auth().data().sendHistoryChangeNotifications(); if (_peer && file->to.peer == _peer->id) { App::main()->historyToDown(_history); } App::main()->dialogsToUp(); - Auth().data().sendHistoryChangeNotifications(); } void HistoryWidget::onPhotoUploaded( diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 2947b767f..81e98d010 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "layout.h" #include "window/window_controller.h" +#include "window/window_peer_menu.h" #include "auth_session.h" #include "ui/widgets/popup_menu.h" #include "core/file_utilities.h" @@ -879,6 +880,12 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } else if (lnkPeer) { // suggest to block // #TODO suggest restrict peer + if (const auto channel = lnkPeer->peer()->asChannel()) { + const auto grouped = (channel->feed() != nullptr); + _menu->addAction( + lang(grouped ? lng_feed_ungroup : lng_feed_group), + [=] { Window::ToggleChannelGrouping(channel, !grouped); }); + } } else { // maybe cursor on some text history item? const auto item = view ? view->data().get() : nullptr; const auto itemId = item ? item->fullId() : FullMsgId(); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index b78aa9a25..4d33a20e5 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -699,6 +699,7 @@ void MainWidget::finishForwarding(not_null history) { cancelForwarding(history); } + Auth().data().sendHistoryChangeNotifications(); historyToDown(history); dialogsToUp(); } @@ -1059,8 +1060,8 @@ void MainWidget::deleteConversation( history->newLoaded = true; history->oldLoaded = deleteHistory; } - if (peer->isChannel()) { - peer->asChannel()->ptsWaitingForShortPoll(-1); + if (const auto channel = peer->asChannel()) { + channel->ptsWaitingForShortPoll(-1); } if (deleteHistory) { DeleteHistoryRequest request = { peer, false }; @@ -1268,8 +1269,8 @@ void MainWidget::checkedHistory(PeerData *peer, const MTPmessages_Messages &resu history->asChannelHistory()->insertJoinedMessage(true); } } - } else { - deleteConversation(peer, false); + } else if (const auto history = App::historyLoaded(peer->id)) { + deleteConversation(history->peer, false); } } else { const auto history = App::history(peer->id); @@ -5221,8 +5222,10 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { if (auto channel = App::channelLoaded(d.vchannel_id.v)) { channel->inviter = 0; if (!channel->amIn()) { - deleteConversation(channel, false); - } else if (!channel->amCreator() && App::history(channel->id)) { // create history + if (const auto history = App::historyLoaded(channel->id)) { + history->updateChatListExistence(); + } + } else if (!channel->amCreator() && App::history(channel->id)) { _updatedChannels.insert(channel, true); Auth().api().requestSelfParticipant(channel); } diff --git a/Telegram/SourceFiles/storage/storage_facade.cpp b/Telegram/SourceFiles/storage/storage_facade.cpp index 9e426b173..de670c0e3 100644 --- a/Telegram/SourceFiles/storage/storage_facade.cpp +++ b/Telegram/SourceFiles/storage/storage_facade.cpp @@ -36,11 +36,13 @@ public: void add(FeedMessagesAddSlice &&query); void remove(FeedMessagesRemoveOne &&query); void remove(FeedMessagesRemoveAll &&query); + void remove(FeedMessagesInvalidate &&query); rpl::producer query( FeedMessagesQuery &&query) const; rpl::producer feedMessagesSliceUpdated() const; rpl::producer feedMessagesOneRemoved() const; rpl::producer feedMessagesAllRemoved() const; + rpl::producer feedMessagesInvalidated() const; private: SharedMedia _sharedMedia; @@ -125,6 +127,10 @@ void Facade::Impl::remove(FeedMessagesRemoveAll &&query) { return _feedMessages.remove(std::move(query)); } +void Facade::Impl::remove(FeedMessagesInvalidate &&query) { + return _feedMessages.remove(std::move(query)); +} + rpl::producer Facade::Impl::query( FeedMessagesQuery &&query) const { return _feedMessages.query(std::move(query)); @@ -142,6 +148,10 @@ rpl::producer Facade::Impl::feedMessagesAllRemoved() cons return _feedMessages.allRemoved(); } +rpl::producer Facade::Impl::feedMessagesInvalidated() const { + return _feedMessages.invalidated(); +} + Facade::Facade() : _impl(std::make_unique()) { } @@ -221,6 +231,10 @@ void Facade::remove(FeedMessagesRemoveAll &&query) { return _impl->remove(std::move(query)); } +void Facade::remove(FeedMessagesInvalidate &&query) { + return _impl->remove(std::move(query)); +} + rpl::producer Facade::query( FeedMessagesQuery &&query) const { return _impl->query(std::move(query)); @@ -238,6 +252,10 @@ rpl::producer Facade::feedMessagesAllRemoved() const { return _impl->feedMessagesAllRemoved(); } +rpl::producer Facade::feedMessagesInvalidated() const { + return _impl->feedMessagesInvalidated(); +} + Facade::~Facade() = default; } // namespace Storage diff --git a/Telegram/SourceFiles/storage/storage_facade.h b/Telegram/SourceFiles/storage/storage_facade.h index 8a804acad..4d9f8eefc 100644 --- a/Telegram/SourceFiles/storage/storage_facade.h +++ b/Telegram/SourceFiles/storage/storage_facade.h @@ -39,6 +39,7 @@ struct FeedMessagesAddNew; struct FeedMessagesAddSlice; struct FeedMessagesRemoveOne; struct FeedMessagesRemoveAll; +struct FeedMessagesInvalidate; struct FeedMessagesQuery; using FeedMessagesResult = Data::MessagesResult; struct FeedMessagesSliceUpdate; @@ -70,12 +71,14 @@ public: void add(FeedMessagesAddSlice &&query); void remove(FeedMessagesRemoveOne &&query); void remove(FeedMessagesRemoveAll &&query); + void remove(FeedMessagesInvalidate &&query); rpl::producer query( FeedMessagesQuery &&query) const; rpl::producer feedMessagesSliceUpdated() const; rpl::producer feedMessagesOneRemoved() const; rpl::producer feedMessagesAllRemoved() const; + rpl::producer feedMessagesInvalidated() const; ~Facade(); diff --git a/Telegram/SourceFiles/storage/storage_feed_messages.cpp b/Telegram/SourceFiles/storage/storage_feed_messages.cpp index dd958b956..9bbe9e3c5 100644 --- a/Telegram/SourceFiles/storage/storage_feed_messages.cpp +++ b/Telegram/SourceFiles/storage/storage_feed_messages.cpp @@ -55,6 +55,14 @@ void FeedMessages::remove(FeedMessagesRemoveAll &&query) { } } +void FeedMessages::remove(FeedMessagesInvalidate &&query) { + auto feedIt = _lists.find(query.feedId); + if (feedIt != _lists.end()) { + feedIt->second.invalidated(); + _invalidated.fire(std::move(query)); + } +} + rpl::producer FeedMessages::query( FeedMessagesQuery &&query) const { auto feedIt = _lists.find(query.key.feedId); @@ -82,4 +90,8 @@ rpl::producer FeedMessages::allRemoved() const { return _allRemoved.events(); } +rpl::producer FeedMessages::invalidated() const { + return _invalidated.events(); +} + } // namespace Storage diff --git a/Telegram/SourceFiles/storage/storage_feed_messages.h b/Telegram/SourceFiles/storage/storage_feed_messages.h index 6a87a2953..efb60b56f 100644 --- a/Telegram/SourceFiles/storage/storage_feed_messages.h +++ b/Telegram/SourceFiles/storage/storage_feed_messages.h @@ -64,6 +64,15 @@ struct FeedMessagesRemoveAll { }; +struct FeedMessagesInvalidate { + explicit FeedMessagesInvalidate(FeedId feedId) + : feedId(feedId) { + } + + FeedId feedId = 0; + +}; + struct FeedMessagesKey { FeedMessagesKey( FeedId feedId, @@ -122,12 +131,14 @@ public: void add(FeedMessagesAddSlice &&query); void remove(FeedMessagesRemoveOne &&query); void remove(FeedMessagesRemoveAll &&query); + void remove(FeedMessagesInvalidate &&query); rpl::producer query( FeedMessagesQuery &&query) const; rpl::producer sliceUpdated() const; rpl::producer oneRemoved() const; rpl::producer allRemoved() const; + rpl::producer invalidated() const; private: using List = Data::MessagesList; @@ -139,6 +150,7 @@ private: rpl::event_stream _sliceUpdated; rpl::event_stream _oneRemoved; rpl::event_stream _allRemoved; + rpl::event_stream _invalidated; rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 1c24faaf2..a306209fe 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -349,6 +349,12 @@ void Filler::addChatActions(not_null chat) { void Filler::addChannelActions(not_null channel) { auto isGroup = channel->isMegagroup(); + if (!isGroup) { + const auto grouped = (channel->feed() != nullptr); + _addAction( + lang(grouped ? lng_feed_ungroup : lng_feed_group), + [=] { ToggleChannelGrouping(channel, !grouped); }); + } if (_source != PeerMenuSource::ChatsList) { if (ManagePeerBox::Available(channel)) { auto text = lang(isGroup @@ -583,6 +589,10 @@ void PeerMenuAddChannelMembers(not_null channel) { Auth().api().requestChannelMembersForAdd(channel, callback); } +void ToggleChannelGrouping(not_null channel, bool group) { + Auth().api().toggleChannelGrouping(channel, group); +} + base::lambda ClearHistoryHandler(not_null peer) { return [peer] { const auto weak = std::make_shared>(); diff --git a/Telegram/SourceFiles/window/window_peer_menu.h b/Telegram/SourceFiles/window/window_peer_menu.h index 2a9a979fa..589211822 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.h +++ b/Telegram/SourceFiles/window/window_peer_menu.h @@ -45,6 +45,7 @@ void PeerMenuShareContactBox(not_null user); void PeerMenuAddContact(not_null user); void PeerMenuAddChannelMembers(not_null channel); +void ToggleChannelGrouping(not_null channel, bool group); base::lambda ClearHistoryHandler(not_null peer); base::lambda DeleteAndLeaveHandler(not_null peer);