diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index b69bbcbac..68ef86684 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -82,6 +82,50 @@ UnavailableReason UnavailableReason::Sensitive() { return { u"sensitive"_q }; } +QString UnavailableReason::Compute( + not_null session, + const std::vector &list) { + const auto &config = session->appConfig(); + const auto skip = config.get>( + "ignore_restriction_reasons", + std::vector()); + auto &&filtered = ranges::views::all( + list + ) | ranges::views::filter([&](const Data::UnavailableReason &reason) { + return !reason.sensitive() + && !ranges::contains(skip, reason.reason); + }); + const auto first = filtered.begin(); + return (first != filtered.end()) ? first->text : QString(); +} + +// We should get a full restriction in "{full}: {reason}" format and we +// need to find an "-all" tag in {full}, otherwise ignore this restriction. +std::vector UnavailableReason::Extract( + const MTPvector *list) { + if (!list) { + return {}; + } + return ranges::views::all( + list->v + ) | ranges::views::filter([](const MTPRestrictionReason &restriction) { + return restriction.match([&](const MTPDrestrictionReason &data) { + const auto platform = data.vplatform().v; + return false +#ifdef OS_MAC_STORE + || (platform == "ios"_q) +#elif defined OS_WIN_STORE // OS_MAC_STORE + || (platform == "ms"_q) +#endif // OS_MAC_STORE || OS_WIN_STORE + || (platform == "all"_q); + }); + }) | ranges::views::transform([](const MTPRestrictionReason &restriction) { + return restriction.match([&](const MTPDrestrictionReason &data) { + return UnavailableReason{ qs(data.vreason()), qs(data.vtext()) }; + }); + }) | ranges::to_vector; +} + bool ApplyBotMenuButton( not_null info, const MTPBotMenuButton *button) { @@ -505,19 +549,9 @@ auto PeerData::unavailableReasons() const } QString PeerData::computeUnavailableReason() const { - const auto &list = unavailableReasons(); - const auto &config = session().appConfig(); - const auto skip = config.get>( - "ignore_restriction_reasons", - std::vector()); - auto &&filtered = ranges::views::all( - list - ) | ranges::views::filter([&](const Data::UnavailableReason &reason) { - return !reason.sensitive() - && !ranges::contains(skip, reason.reason); - }); - const auto first = filtered.begin(); - return (first != filtered.end()) ? first->text : QString(); + return Data::UnavailableReason::Compute( + &session(), + unavailableReasons()); } bool PeerData::isUnavailableSensitive() const { diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index e457b8add..7c393718b 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -95,6 +95,13 @@ struct UnavailableReason { [[nodiscard]] bool sensitive() const; [[nodiscard]] static UnavailableReason Sensitive(); + + [[nodiscard]] static QString Compute( + not_null session, + const std::vector &list); + + [[nodiscard]] static std::vector Extract( + const MTPvector *list); }; bool ApplyBotMenuButton( diff --git a/Telegram/SourceFiles/data/data_photo.cpp b/Telegram/SourceFiles/data/data_photo.cpp index 869bbf733..4556c2195 100644 --- a/Telegram/SourceFiles/data/data_photo.cpp +++ b/Telegram/SourceFiles/data/data_photo.cpp @@ -252,7 +252,8 @@ Image *PhotoData::getReplyPreview( Image *PhotoData::getReplyPreview(not_null item) { const auto media = item->media(); - const auto spoiler = media && media->hasSpoiler(); + const auto spoiler = (media && media->hasSpoiler()) + || item->hasSensitiveSpoiler(); return getReplyPreview(item->fullId(), item->history()->peer, spoiler); } diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 86e284ae7..4c2ad3422 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -126,30 +126,6 @@ void CheckForSwitchInlineButton(not_null item) { } } -// We should get a full restriction in "{full}: {reason}" format and we -// need to find an "-all" tag in {full}, otherwise ignore this restriction. -std::vector ExtractUnavailableReasons( - const QVector &restrictions) { - return ranges::views::all( - restrictions - ) | ranges::views::filter([](const MTPRestrictionReason &restriction) { - return restriction.match([&](const auto &data) { - const auto platform = qs(data.vplatform()); - return false -#ifdef OS_MAC_STORE - || (platform == u"ios"_q) -#elif defined OS_WIN_STORE // OS_MAC_STORE - || (platform == u"ms"_q) -#endif // OS_MAC_STORE || OS_WIN_STORE - || (platform == u"all"_q); - }); - }) | ranges::views::transform([](const MTPRestrictionReason &restriction) { - return restriction.match([&](const MTPDrestrictionReason &data) { - return UnavailableReason{ qs(data.vreason()), qs(data.vtext()) }; - }); - }) | ranges::to_vector; -} - [[nodiscard]] InlineImageLocation FindInlineThumbnail( const QVector &sizes) { const auto i = ranges::find( @@ -606,12 +582,8 @@ not_null Session::processUser(const MTPUser &data) { result->input = MTP_inputPeerUser(data.vid(), MTP_long(result->accessHash())); result->inputUser = MTP_inputUser(data.vid(), MTP_long(result->accessHash())); } - if (const auto restriction = data.vrestriction_reason()) { - result->setUnavailableReasons( - ExtractUnavailableReasons(restriction->v)); - } else { - result->setUnavailableReasons({}); - } + result->setUnavailableReasons(Data::UnavailableReason::Extract( + data.vrestriction_reason())); } if (data.is_deleted()) { if (!result->phone().isEmpty()) { @@ -915,12 +887,8 @@ not_null Session::processChat(const MTPChat &data) { channel->setAccessHash( data.vaccess_hash().value_or(channel->access)); channel->date = data.vdate().v; - if (const auto restriction = data.vrestriction_reason()) { - channel->setUnavailableReasons( - ExtractUnavailableReasons(restriction->v)); - } else { - channel->setUnavailableReasons({}); - } + channel->setUnavailableReasons(Data::UnavailableReason::Extract( + data.vrestriction_reason())); } { diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h index 66e041c0f..515a6556f 100644 --- a/Telegram/SourceFiles/data/data_types.h +++ b/Telegram/SourceFiles/data/data_types.h @@ -323,6 +323,9 @@ enum class MessageFlag : uint64 { ShortcutMessage = (1ULL << 44), EffectWatched = (1ULL << 45), + + SensitiveContent = (1ULL << 46), + AllowSensitive = (1ULL << 47), }; inline constexpr bool is_flag_type(MessageFlag) { return true; } using MessageFlags = base::flags; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index a8e74a4f5..02bbf365e 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -190,6 +190,8 @@ struct HistoryItem::CreateConfig { // For messages created from existing messages (forwarded). const HistoryMessageReplyMarkup *inlineMarkup = nullptr; + + std::vector restrictions; }; void HistoryItem::FillForwardedInfo( @@ -3279,6 +3281,24 @@ EffectId HistoryItem::effectId() const { return _effectId; } +QString HistoryItem::computeUnavailableReason() const { + if (const auto restrictions = Get()) { + return Data::UnavailableReason::Compute( + &history()->session(), + restrictions->reasons); + } + return QString(); +} + +bool HistoryItem::hasSensitiveSpoiler() const { + return (_flags & MessageFlag::SensitiveContent) + && !(_flags & MessageFlag::AllowSensitive); +} + +void HistoryItem::allowSensitive() { + _flags |= MessageFlag::AllowSensitive; +} + bool HistoryItem::isEmpty() const { return _text.empty() && !_media @@ -3487,6 +3507,12 @@ void HistoryItem::createComponents(CreateConfig &&config) { if (_history->peer->isSelf()) { mask |= HistoryMessageSaved::Bit(); } + if (!config.restrictions.empty()) { + if (config.restrictions.size() > 1 + || !config.restrictions.front().sensitive()) { + mask |= HistoryMessageRestrictions::Bit(); + } + } UpdateComponents(mask); @@ -3559,6 +3585,19 @@ void HistoryItem::createComponents(CreateConfig &&config) { } else { _flags &= ~MessageFlag::HasReplyMarkup; } + if (const auto restrictions = Get()) { + restrictions->reasons = std::move(config.restrictions); + const auto i = ranges::find( + restrictions->reasons, + true, + &Data::UnavailableReason::sensitive); + if (i != end(restrictions->reasons)) { + restrictions->reasons.erase(i); + _flags |= MessageFlag::SensitiveContent; + } + } else if (!config.restrictions.empty()) { + _flags |= MessageFlag::SensitiveContent; + } if (out() && isSending()) { if (const auto channel = _history->peer->asMegagroup()) { @@ -3874,6 +3913,8 @@ void HistoryItem::createComponents(const MTPDmessage &data) { config.markup = HistoryMessageMarkupData(data.vreply_markup()); config.editDate = data.vedit_date().value_or_empty(); config.postAuthor = qs(data.vpost_author().value_or_empty()); + config.restrictions = Data::UnavailableReason::Extract( + data.vrestriction_reason()); createComponents(std::move(config)); } diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index be5c7f7a9..e43481635 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -516,6 +516,9 @@ public: [[nodiscard]] bool isEmpty() const; [[nodiscard]] MessageGroupId groupId() const; [[nodiscard]] EffectId effectId() const; + [[nodiscard]] QString computeUnavailableReason() const; + [[nodiscard]] bool hasSensitiveSpoiler() const; + void allowSensitive(); [[nodiscard]] const HistoryMessageReplyMarkup *inlineReplyMarkup() const { return const_cast(this)->inlineReplyMarkup(); diff --git a/Telegram/SourceFiles/history/history_item_components.h b/Telegram/SourceFiles/history/history_item_components.h index 1a5e14014..d4bc894b1 100644 --- a/Telegram/SourceFiles/history/history_item_components.h +++ b/Telegram/SourceFiles/history/history_item_components.h @@ -33,6 +33,7 @@ namespace Data { class Session; class Story; class SavedSublist; +struct UnavailableReason; } // namespace Data namespace Media::Player { @@ -596,6 +597,11 @@ struct HistoryMessageFactcheck bool requested = false; }; +struct HistoryMessageRestrictions +: public RuntimeComponent { + std::vector reasons; +}; + struct HistoryServiceData : public RuntimeComponent { std::vector textLinks;