diff --git a/Telegram/SourceFiles/data/data_chat_filters.cpp b/Telegram/SourceFiles/data/data_chat_filters.cpp index d11d434a44..f85bc2d413 100644 --- a/Telegram/SourceFiles/data/data_chat_filters.cpp +++ b/Telegram/SourceFiles/data/data_chat_filters.cpp @@ -362,6 +362,12 @@ bool ChatFilter::contains( if (_never.contains(history)) { return false; } + const auto channel = history->peer->asChannel(); + if (channel && channel->isCommunity()) { + // A community never matches a filter by chat type (it is neither a + // group nor a channel); it can only be included explicitly by id. + return _always.contains(history); + } const auto state = (_flags & (Flag::NoMuted | Flag::NoRead)) ? history->chatListBadgesState() : Dialogs::BadgesState(); diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index 0511998845..b33e40184e 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -34,7 +34,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/media/history_view_todo_list.h" #include "history/view/media/history_view_slot_machine.h" #include "history/view/media/history_view_dice.h" -#include "history/view/media/history_view_community_added.h" #include "history/view/media/history_view_service_box.h" #include "history/view/media/history_view_story_mention.h" #include "history/view/media/history_view_premium_gift.h" @@ -2777,51 +2776,6 @@ std::unique_ptr MediaGiftBox::createView( std::make_unique(message, this)); } -MediaCommunityAdded::MediaCommunityAdded( - not_null parent, - not_null community) -: Media(parent) -, _community(community) { -} - -std::unique_ptr MediaCommunityAdded::clone( - not_null parent) { - return std::make_unique(parent, _community); -} - -not_null MediaCommunityAdded::community() const { - return _community; -} - -TextWithEntities MediaCommunityAdded::notificationText() const { - return {}; -} - -QString MediaCommunityAdded::pinnedTextSubstring() const { - return {}; -} - -TextForMimeData MediaCommunityAdded::clipboardText() const { - return {}; -} - -bool MediaCommunityAdded::updateInlineResultMedia(const MTPMessageMedia &media) { - return false; -} - -bool MediaCommunityAdded::updateSentMedia(const MTPMessageMedia &media) { - return false; -} - -std::unique_ptr MediaCommunityAdded::createView( - not_null message, - not_null realParent, - HistoryView::Element *replacing) { - return std::make_unique( - message, - std::make_unique(message, this)); -} - MediaWallPaper::MediaWallPaper( not_null parent, const WallPaper &paper, diff --git a/Telegram/SourceFiles/data/data_media_types.h b/Telegram/SourceFiles/data/data_media_types.h index 9653fe98d4..ded7677a9c 100644 --- a/Telegram/SourceFiles/data/data_media_types.h +++ b/Telegram/SourceFiles/data/data_media_types.h @@ -756,32 +756,6 @@ private: }; -class MediaCommunityAdded final : public Media { -public: - MediaCommunityAdded( - not_null parent, - not_null community); - - std::unique_ptr clone(not_null parent) override; - - [[nodiscard]] not_null community() const; - - TextWithEntities notificationText() const override; - QString pinnedTextSubstring() const override; - TextForMimeData clipboardText() const override; - - bool updateInlineResultMedia(const MTPMessageMedia &media) override; - bool updateSentMedia(const MTPMessageMedia &media) override; - std::unique_ptr createView( - not_null message, - not_null realParent, - HistoryView::Element *replacing = nullptr) override; - -private: - const not_null _community; - -}; - class MediaWallPaper final : public Media { public: MediaWallPaper( diff --git a/Telegram/SourceFiles/data/data_thread.cpp b/Telegram/SourceFiles/data/data_thread.cpp index 5fd76d0162..c1367fdcdb 100644 --- a/Telegram/SourceFiles/data/data_thread.cpp +++ b/Telegram/SourceFiles/data/data_thread.cpp @@ -117,6 +117,10 @@ HistoryUnreadThings::ConstProxy Thread::unreadPollVotes() const { bool Thread::canToggleUnread(bool nowUnread) const { if ((asTopic() || asForum()) && !nowUnread) { return false; + } else if (const auto channel = asHistory() ? peer()->asChannel() : nullptr + ; channel && channel->isCommunity() && !nowUnread) { + // Communities support "mark as read" but not "mark as unread". + return false; } else if (asSublist() && owningHistory()->peer->isSelf()) { return false; } else if (asHistory() && peer()->amMonoforumAdmin()) { diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 3d6d3b0d66..6d33873d52 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -5499,6 +5499,17 @@ void HistoryItem::createServiceFromMtp(const MTPDmessageService &message) { const auto markup = Get(); markup->updateSuggestControls(actions); } + } else if (type == mtpc_messageActionChangeCommunity) { + const auto &data = action.c_messageActionChangeCommunity(); + const auto communityId = data.vcommunity_id().value_or_empty(); + if (communityId) { + const auto community = _history->owner().channelLoaded( + ChannelId(communityId)); + if (community) { + UpdateComponents(HistoryServiceCommunityAdded::Bit()); + Get()->community = community; + } + } } if (const auto replyTo = message.vreply_to()) { replyTo->match([&](const MTPDmessageReplyHeader &data) { @@ -7697,17 +7708,6 @@ void HistoryItem::processAction(const MTPMessageAction &action) { .type = Data::GiftType::GiftOffer, }); } - }, [&](const MTPDmessageActionChangeCommunity &data) { - const auto communityId = data.vcommunity_id().value_or_empty(); - if (!communityId) { - return; - } - if (const auto community = _history->owner().channelLoaded( - ChannelId(communityId))) { - _media = std::make_unique( - this, - community); - } }, [](const auto &) { }); } diff --git a/Telegram/SourceFiles/history/history_item_components.h b/Telegram/SourceFiles/history/history_item_components.h index 98f9b6f467..e8c3630f51 100644 --- a/Telegram/SourceFiles/history/history_item_components.h +++ b/Telegram/SourceFiles/history/history_item_components.h @@ -23,6 +23,7 @@ struct WebPageData; struct TodoListItem; class DocumentData; class PhotoData; +class ChannelData; class VoiceSeekClickHandler; class ReplyKeyboard; @@ -823,6 +824,11 @@ struct HistoryServiceNoForwardsToggle : RuntimeComponent { }; +struct HistoryServiceCommunityAdded +: RuntimeComponent { + ChannelData *community = nullptr; +}; + struct HistoryServiceGameScore : RuntimeComponent , HistoryServiceDependentData { diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index c384a06d3b..02cd836183 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_transcribes.h" #include "history/view/history_view_service_message.h" #include "history/view/history_view_message.h" +#include "history/view/media/history_view_community_added.h" #include "history/view/media/history_view_media_generic.h" #include "history/view/media/history_view_media_grouped.h" #include "history/view/media/history_view_similar_channels.h" @@ -1618,6 +1619,16 @@ void Element::refreshMedia(Element *replacing) { .service = true, .hideServiceText = true, }); + } else if (const auto added = item->Get() + ; added && added->community) { + _media = std::make_unique( + this, + GenerateCommunityAddedMedia(this, added->community), + MediaGenericDescriptor{ + .maxWidth = st::msgServiceGiftBoxSize.width(), + .service = true, + .hideServiceText = true, + }); } else { _media = nullptr; } diff --git a/Telegram/SourceFiles/history/view/media/history_view_community_added.cpp b/Telegram/SourceFiles/history/view/media/history_view_community_added.cpp index 76cfaba088..78824df2f4 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_community_added.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_community_added.cpp @@ -9,114 +9,79 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/click_handler_types.h" // ClickHandlerContext #include "data/data_channel.h" -#include "data/data_media_types.h" -#include "data/data_session.h" -#include "history/history.h" #include "history/history_item.h" -#include "history/view/media/history_view_sticker_player_abstract.h" #include "history/view/history_view_element.h" +#include "history/view/media/history_view_media_generic.h" +#include "history/view/media/history_view_unique_gift.h" // MakeGenericButtonPart #include "lang/lang_keys.h" -#include "ui/text/text_utilities.h" -#include "ui/dynamic_image.h" #include "ui/dynamic_thumbnails.h" -#include "ui/painter.h" +#include "ui/text/text_utilities.h" #include "window/window_session_controller.h" #include "styles/style_chat.h" #include "styles/style_menu_icons.h" +#include "styles/style_premium.h" namespace HistoryView { -CommunityAdded::CommunityAdded( +auto GenerateCommunityAddedMedia( not_null parent, - not_null media) -: _parent(parent) -, _community(media->community()) { -} + not_null community) +-> Fn, + Fn)>)> { + return [=]( + not_null media, + Fn)> push) { + const auto from = parent->data()->from(); -CommunityAdded::~CommunityAdded() { - if (_subscribed) { - unloadHeavyPart(); - _parent->checkHeavyPart(); - } -} - -int CommunityAdded::top() { - return st::msgServiceGiftBoxButtonMargins.top(); -} - -QSize CommunityAdded::size() { - return { st::msgServicePhotoWidth, st::msgServicePhotoWidth }; -} - -TextWithEntities CommunityAdded::title() { - return {}; -} - -TextWithEntities CommunityAdded::subtitle() { - const auto from = _parent->data()->from(); - return tr::lng_action_community_added( - tr::now, - lt_from, - tr::bold(from->shortName()), - lt_community, - tr::bold(_community->name()), - tr::rich); -} - -rpl::producer CommunityAdded::button() { - return tr::lng_community_view(); -} - -void CommunityAdded::draw( - Painter &p, - const PaintContext &context, - const QRect &geometry) { - if (!_thumbnail) { - _thumbnail = _community->hasUserpic() - ? Ui::MakeUserpicThumbnail(_community) - : Ui::MakeIconThumbnail(st::menuIconGroups); - } - if (!_subscribed) { - _subscribed = true; - _parent->history()->owner().registerHeavyViewPart(_parent); - const auto raw = _parent; - _thumbnail->subscribeToUpdates([raw] { - raw->history()->owner().requestViewRepaint(raw); + const auto open = std::make_shared([=]( + ClickContext context) { + const auto my = context.other.value(); + if (const auto controller = my.sessionWindow.get()) { + controller->showPeerInfo(community); + } }); - } - p.drawImage(geometry.topLeft(), _thumbnail->image(geometry.width())); -} -ClickHandlerPtr CommunityAdded::createViewLink() { - const auto community = _community; - return std::make_shared([=](ClickContext context) { - const auto my = context.other.value(); - if (const auto controller = my.sessionWindow.get()) { - controller->showPeerInfo(community); - } - }); -} + auto image = community->hasUserpic() + ? Ui::MakeUserpicThumbnail(community) + : Ui::MakeIconThumbnail(st::menuIconGroups); + push(std::make_unique( + parent, + std::move(image), + st::msgServiceCommunityAddedPhoto, + QMargins( + 0, + st::msgServiceGiftBoxButtonMargins.top(), + 0, + st::msgServiceGiftBoxTitlePadding.top()), + open, + true)); // Paint the community stacked-cards effect behind it. -void CommunityAdded::stickerClearLoopPlayed() { -} + push(std::make_unique( + tr::lng_action_community_added( + tr::now, + lt_from, + tr::bold(from->shortName()), + lt_community, + tr::bold(community->name()), + tr::rich), + QMargins( + st::msgPadding.left(), + 0, + st::msgPadding.right(), + st::msgServiceGiftBoxTitlePadding.bottom()), + st::premiumPreviewAbout.style)); -std::unique_ptr CommunityAdded::stickerTakePlayer( - not_null data, - const Lottie::ColorReplacements *replacements) { - return nullptr; -} - -bool CommunityAdded::hasHeavyPart() { - return _subscribed; -} - -void CommunityAdded::unloadHeavyPart() { - if (_subscribed) { - _subscribed = false; - if (_thumbnail) { - _thumbnail->subscribeToUpdates(nullptr); - } - } + push(MakeGenericButtonPart( + tr::lng_community_view(tr::now), + QMargins( + 0, + st::msgServiceGiftBoxButtonMargins.top(), + 0, + st::msgServiceGiftBoxButtonMargins.bottom()), + [=] { parent->repaint(); }, + open)); + }; } } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/media/history_view_community_added.h b/Telegram/SourceFiles/history/view/media/history_view_community_added.h index 4eec6455d0..1308d0fb73 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_community_added.h +++ b/Telegram/SourceFiles/history/view/media/history_view_community_added.h @@ -7,58 +7,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "history/view/media/history_view_service_box.h" - class ChannelData; -namespace Data { -class MediaCommunityAdded; -} // namespace Data - -namespace Ui { -class DynamicImage; -} // namespace Ui - namespace HistoryView { -class CommunityAdded final - : public ServiceBoxContent - , public base::has_weak_ptr { -public: - CommunityAdded( - not_null parent, - not_null media); - ~CommunityAdded(); +class Element; +class MediaGeneric; +class MediaGenericPart; - int top() override; - QSize size() override; - TextWithEntities title() override; - TextWithEntities subtitle() override; - rpl::producer button() override; - void draw( - Painter &p, - const PaintContext &context, - const QRect &geometry) override; - ClickHandlerPtr createViewLink() override; - - bool hideServiceText() override { - return true; - } - - void stickerClearLoopPlayed() override; - std::unique_ptr stickerTakePlayer( - not_null data, - const Lottie::ColorReplacements *replacements) override; - - bool hasHeavyPart() override; - void unloadHeavyPart() override; - -private: - const not_null _parent; - const not_null _community; - std::shared_ptr _thumbnail; - bool _subscribed = false; - -}; +[[nodiscard]] auto GenerateCommunityAddedMedia( + not_null parent, + not_null community +) -> Fn, + Fn)>)>; } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_generic.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_generic.cpp index e5608b3513..c16aaa89dd 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_generic.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media_generic.cpp @@ -9,6 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "data/data_peer.h" +#include "data/data_session.h" +#include "history/history.h" #include "history/history_item.h" #include "history/history_item_components.h" #include "history/view/history_view_element.h" @@ -20,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/power_saving.h" #include "ui/rect.h" #include "ui/round_rect.h" +#include "ui/userpic_view.h" #include "styles/style_chat.h" namespace HistoryView { @@ -657,6 +660,86 @@ void StickerInBubblePart::ensureCreated(Element *replacing) const { } } +DynamicImagePart::DynamicImagePart( + not_null parent, + std::shared_ptr image, + int size, + QMargins margins, + ClickHandlerPtr link, + bool communityEffect) +: _parent(parent) +, _image(std::move(image)) +, _link(std::move(link)) +, _margins(margins) +, _size(size) +, _communityEffect(communityEffect) { +} + +DynamicImagePart::~DynamicImagePart() = default; + +void DynamicImagePart::draw( + Painter &p, + not_null owner, + const PaintContext &context, + int outerWidth) const { + if (!_subscribed) { + _subscribed = true; + const auto raw = _parent; + _image->subscribeToUpdates([raw] { raw->repaint(); }); + raw->history()->owner().registerHeavyViewPart(raw); + } + const auto left = (outerWidth - _size) / 2; + const auto top = _margins.top(); + if (_communityEffect) { + if (!_communityCache) { + _communityCache = std::make_unique(); + } + Ui::PaintCommunityUserpicEffect( + p, + *_communityCache, + left, + top, + _size, + context.st->msgServiceBg()->c); + } + p.drawImage(QPoint(left, top), _image->image(_size)); +} + +TextState DynamicImagePart::textState( + QPoint point, + StateRequest request, + int outerWidth) const { + auto result = TextState(_parent); + const auto left = (outerWidth - _size) / 2; + if (_link && QRect(left, _margins.top(), _size, _size).contains(point)) { + result.link = _link; + } + return result; +} + +bool DynamicImagePart::hasHeavyPart() { + return _subscribed; +} + +void DynamicImagePart::unloadHeavyPart() { + if (_subscribed) { + _subscribed = false; + _image->subscribeToUpdates(nullptr); + } + _communityCache = nullptr; +} + +QSize DynamicImagePart::countOptimalSize() { + return { + _margins.left() + _size + _margins.right(), + _margins.top() + _size + _margins.bottom(), + }; +} + +QSize DynamicImagePart::countCurrentSize(int newWidth) { + return { newWidth, minHeight() }; +} + StickerWithBadgePart::StickerWithBadgePart( not_null parent, Element *replacing, diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_generic.h b/Telegram/SourceFiles/history/view/media/history_view_media_generic.h index 3f7aa7e7a1..67cd19a765 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_generic.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media_generic.h @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { class DynamicImage; class RippleAnimation; +struct CommunityUserpicEffect; } // namespace Ui namespace style { @@ -303,6 +304,44 @@ private: }; +class DynamicImagePart final : public MediaGenericPart { +public: + DynamicImagePart( + not_null parent, + std::shared_ptr image, + int size, + QMargins margins, + ClickHandlerPtr link = nullptr, + bool communityEffect = false); + ~DynamicImagePart(); + + void draw( + Painter &p, + not_null owner, + const PaintContext &context, + int outerWidth) const override; + TextState textState( + QPoint point, + StateRequest request, + int outerWidth) const override; + bool hasHeavyPart() override; + void unloadHeavyPart() override; + + QSize countOptimalSize() override; + QSize countCurrentSize(int newWidth) override; + +private: + const not_null _parent; + const std::shared_ptr _image; + const ClickHandlerPtr _link; + const QMargins _margins; + const int _size = 0; + const bool _communityEffect = false; + mutable std::unique_ptr _communityCache; + mutable bool _subscribed = false; + +}; + class StickerWithBadgePart final : public MediaGenericPart { public: using Data = StickerInBubblePart::Data; diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 09f6960d4c..6facc72cba 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -959,6 +959,7 @@ msgServiceStarGiftStickerTop: 24px; msgServiceStarGiftStickerSize: 100px; msgServiceGiftThemeStickerSize: 64px; msgServiceGiftThemeStickerPadding: margins(18px, 18px, 18px, 18px); +msgServiceCommunityAddedPhoto: 66px; // About 2/3 of msgServicePhotoWidth. historySponsorInfoItem: FlatLabel(defaultFlatLabel) { style: TextStyle(defaultTextStyle) { diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 38a7bd060e..5c08c5b0b5 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -105,6 +105,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_poll.h" #include "data/data_channel.h" #include "data/data_chat.h" +#include "data/data_community.h" #include "data/data_forum.h" #include "data/data_forum_topic.h" #include "data/data_user.h" @@ -689,7 +690,16 @@ void Filler::addToggleUnreadMark() { return; } if (unread) { - MarkAsReadThread(thread); + const auto channel = peer ? peer->asChannel() : nullptr; + const auto info = (channel && channel->isCommunity()) + ? channel->communityInfo() + : nullptr; + if (info) { + // Mark every chat inside the community as read. + MarkAsReadChatList(info->chatsList()); + } else { + MarkAsReadThread(thread); + } } else if (const auto sublist = thread->asSublist()) { peer->owner().histories().changeSublistUnreadMark(sublist, true); } else if (history) { @@ -791,7 +801,7 @@ void Filler::addUngroup() { .confirmStyle = &st::attentionBoxButton, .title = tr::lng_community_ungroup_title(), })); - }, &st::menuIconCollapse); + }, &st::menuIconExpand); } void Filler::addToggleArchive() { @@ -4308,6 +4318,9 @@ bool IsArchived(not_null history) { bool CanArchive(History *history, PeerData *peer) { if (history && history->useTopPromotion()) { return false; + } else if (const auto channel = peer ? peer->asChannel() : nullptr + ; channel && channel->isCommunity()) { + return false; } else if (peer && (peer->isNotificationsUser() || peer->isSelf())) { if (!history || !history->folder()) { return false;