From 41e13e39bc965ebec8a98063d0629a7ee33645f4 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Mon, 9 Dec 2019 16:57:33 +0300
Subject: [PATCH] Apply ignore_restriction_reasons from config.

---
 .../SourceFiles/api/api_sensitive_content.cpp | 14 +++++++-
 .../SourceFiles/api/api_sensitive_content.h   |  7 ++++
 Telegram/SourceFiles/data/data_channel.cpp    | 11 +++---
 Telegram/SourceFiles/data/data_channel.h      |  8 +++--
 Telegram/SourceFiles/data/data_peer.cpp       | 23 ++++++++++++
 Telegram/SourceFiles/data/data_peer.h         | 18 ++++++++--
 Telegram/SourceFiles/data/data_session.cpp    | 36 +++++++++----------
 Telegram/SourceFiles/data/data_user.cpp       | 12 ++++---
 Telegram/SourceFiles/data/data_user.h         |  9 +++--
 .../SourceFiles/history/history_widget.cpp    |  2 +-
 Telegram/SourceFiles/main/main_app_config.cpp | 20 +++++++++++
 Telegram/SourceFiles/main/main_app_config.h   |  8 ++++-
 Telegram/SourceFiles/mainwidget.cpp           |  2 +-
 13 files changed, 127 insertions(+), 43 deletions(-)

diff --git a/Telegram/SourceFiles/api/api_sensitive_content.cpp b/Telegram/SourceFiles/api/api_sensitive_content.cpp
index 0a5c4f739..7ff3dd610 100644
--- a/Telegram/SourceFiles/api/api_sensitive_content.cpp
+++ b/Telegram/SourceFiles/api/api_sensitive_content.cpp
@@ -8,11 +8,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "api/api_sensitive_content.h"
 
 #include "apiwrap.h"
+#include "main/main_session.h"
+#include "main/main_account.h"
+#include "main/main_app_config.h"
 
 namespace Api {
+namespace {
+
+constexpr auto kRefreshAppConfigTimeout = 3 * crl::time(1000);
+
+} // namespace
 
 SensitiveContent::SensitiveContent(not_null<ApiWrap*> api)
-: _api(api->instance()) {
+: _session(&api->session())
+, _api(api->instance())
+, _appConfigReloadTimer([=] { _session->account().appConfig().refresh(); }) {
 }
 
 void SensitiveContent::reload() {
@@ -57,6 +67,8 @@ void SensitiveContent::update(bool enabled) {
 		_requestId = 0;
 	}).send();
 	_enabled = enabled;
+
+	_appConfigReloadTimer.callOnce(kRefreshAppConfigTimeout);
 }
 
 } // namespace Api
diff --git a/Telegram/SourceFiles/api/api_sensitive_content.h b/Telegram/SourceFiles/api/api_sensitive_content.h
index 7c9e9d85c..0a61f9da7 100644
--- a/Telegram/SourceFiles/api/api_sensitive_content.h
+++ b/Telegram/SourceFiles/api/api_sensitive_content.h
@@ -8,9 +8,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #pragma once
 
 #include "mtproto/sender.h"
+#include "base/timer.h"
 
 class ApiWrap;
 
+namespace Main {
+class Session;
+} // namespace Main
+
 namespace Api {
 
 class SensitiveContent final {
@@ -25,10 +30,12 @@ public:
 	[[nodiscard]] rpl::producer<bool> canChange() const;
 
 private:
+	const not_null<Main::Session*> _session;
 	MTP::Sender _api;
 	mtpRequestId _requestId = 0;
 	rpl::variable<bool> _enabled = false;
 	rpl::variable<bool> _canChange = false;
+	base::Timer _appConfigReloadTimer;
 
 };
 
diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp
index 2435a1631..a7eefdbd3 100644
--- a/Telegram/SourceFiles/data/data_channel.cpp
+++ b/Telegram/SourceFiles/data/data_channel.cpp
@@ -339,13 +339,14 @@ bool ChannelData::isGroupAdmin(not_null<UserData*> user) const {
 	return false;
 }
 
-QString ChannelData::unavailableReason() const {
-	return _unavailableReason;
+auto ChannelData::unavailableReasons() const
+-> const std::vector<Data::UnavailableReason> & {
+	return _unavailableReasons;
 }
 
-void ChannelData::setUnavailableReason(const QString &text) {
-	if (_unavailableReason != text) {
-		_unavailableReason = text;
+void ChannelData::setUnavailableReasons(std::vector<Data::UnavailableReason> &&reasons) {
+	if (_unavailableReasons != reasons) {
+		_unavailableReasons = std::move(reasons);
 		Notify::peerUpdatedDelayed(
 			this,
 			Notify::PeerUpdate::Flag::UnavailableReasonChanged);
diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h
index df8adf4e7..df583c286 100644
--- a/Telegram/SourceFiles/data/data_channel.h
+++ b/Telegram/SourceFiles/data/data_channel.h
@@ -368,8 +368,8 @@ public:
 		return _ptsWaiter.waitingForShortPoll();
 	}
 
-	[[nodiscard]] QString unavailableReason() const override;
-	void setUnavailableReason(const QString &reason);
+	void setUnavailableReasons(
+		std::vector<Data::UnavailableReason> &&reason);
 
 	[[nodiscard]] MsgId availableMinId() const {
 		return _availableMinId;
@@ -412,6 +412,8 @@ public:
 	TimeId inviteDate = 0;
 
 private:
+	auto unavailableReasons() const
+		-> const std::vector<Data::UnavailableReason> & override;
 	bool canEditLastAdmin(not_null<UserData*> user) const;
 
 	Flags _flags = Flags(MTPDchannel_ClientFlag::f_forbidden | 0);
@@ -431,7 +433,7 @@ private:
 	RestrictionFlags _restrictions;
 	TimeId _restrictedUntil;
 
-	QString _unavailableReason;
+	std::vector<Data::UnavailableReason> _unavailableReasons;
 	QString _inviteLink;
 	ChannelData *_linkedChat = nullptr;
 
diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp
index cfb91084e..9431b625d 100644
--- a/Telegram/SourceFiles/data/data_peer.cpp
+++ b/Telegram/SourceFiles/data/data_peer.cpp
@@ -21,6 +21,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "apiwrap.h"
 #include "boxes/confirm_box.h"
 #include "main/main_session.h"
+#include "main/main_account.h"
+#include "main/main_app_config.h"
 #include "core/application.h"
 #include "mainwindow.h"
 #include "window/window_session_controller.h"
@@ -376,6 +378,27 @@ void PeerData::setUserpicChecked(
 	}
 }
 
+auto PeerData::unavailableReasons() const
+-> const std::vector<Data::UnavailableReason> & {
+	static const auto result = std::vector<Data::UnavailableReason>();
+	return result;
+}
+
+QString PeerData::computeUnavailableReason() const {
+	const auto &list = unavailableReasons();
+	const auto &config = session().account().appConfig();
+	const auto skip = config.get<std::vector<QString>>(
+		"ignore_restriction_reasons",
+		std::vector<QString>());
+	auto &&filtered = ranges::view::all(
+		list
+	) | ranges::view::filter([&](const Data::UnavailableReason &reason) {
+		return ranges::find(skip, reason.reason) == end(skip);
+	});
+	const auto first = filtered.begin();
+	return (first != filtered.end()) ? first->text : QString();
+}
+
 bool PeerData::canPinMessages() const {
 	if (const auto user = asUser()) {
 		return user->fullFlags() & MTPDuserFull::Flag::f_can_pin_message;
diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h
index 9d5716bb1..fcb82c2c4 100644
--- a/Telegram/SourceFiles/data/data_peer.h
+++ b/Telegram/SourceFiles/data/data_peer.h
@@ -84,6 +84,18 @@ private:
 
 };
 
+struct UnavailableReason {
+	QString reason;
+	QString text;
+
+	bool operator==(const UnavailableReason &other) const {
+		return (reason == other.reason) && (text == other.text);
+	}
+	bool operator!=(const UnavailableReason &other) const {
+		return !(*this == other);
+	}
+};
+
 } // namespace Data
 
 class PeerClickHandler : public ClickHandler {
@@ -271,9 +283,7 @@ public:
 
 	// If this string is not empty we must not allow to open the
 	// conversation and we must show this string instead.
-	[[nodiscard]] virtual QString unavailableReason() const {
-		return QString();
-	}
+	[[nodiscard]] QString computeUnavailableReason() const;
 
 	[[nodiscard]] ClickHandlerPtr createOpenLink();
 	[[nodiscard]] const ClickHandlerPtr &openLink() {
@@ -346,6 +356,8 @@ private:
 	void fillNames();
 	std::unique_ptr<Ui::EmptyUserpic> createEmptyUserpic() const;
 	void refreshEmptyUserpic() const;
+	[[nodiscard]] virtual auto unavailableReasons() const
+		-> const std::vector<Data::UnavailableReason> &;
 
 	void setUserpicChecked(
 		PhotoId photoId,
diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp
index 540979331..e794f48fa 100644
--- a/Telegram/SourceFiles/data/data_session.cpp
+++ b/Telegram/SourceFiles/data/data_session.cpp
@@ -101,30 +101,26 @@ void CheckForSwitchInlineButton(not_null<HistoryItem*> 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.
-QString ExtractUnavailableReason(
+std::vector<UnavailableReason> ExtractUnavailableReasons(
 		const QVector<MTPRestrictionReason> &restrictions) {
-	auto &&texts = ranges::view::all(
+	return ranges::view::all(
 		restrictions
-	) | ranges::view::transform([](const MTPRestrictionReason &restriction) {
+	) | ranges::view::filter([](const MTPRestrictionReason &restriction) {
 		return restriction.match([&](const MTPDrestrictionReason &data) {
 			const auto platform = qs(data.vplatform());
-			return (false
+			return false
 #ifdef OS_MAC_STORE
 				|| (platform == qstr("ios"))
 #elif defined OS_WIN_STORE // OS_MAC_STORE
 				|| (platform == qstr("ms"))
 #endif // OS_MAC_STORE || OS_WIN_STORE
-				|| (platform == qstr("all")))
-				? std::make_optional(qs(data.vtext()))
-				: std::nullopt;
+				|| (platform == qstr("all"));
 		});
-	}) | ranges::view::filter([](const std::optional<QString> &value) {
-		return value.has_value();
-	}) | ranges::view::transform([](const std::optional<QString> &value) {
-		return *value;
-	});
-	const auto begin = texts.begin();
-	return (begin != texts.end()) ? *begin : nullptr;
+	}) | ranges::view::transform([](const MTPRestrictionReason &restriction) {
+		return restriction.match([&](const MTPDrestrictionReason &data) {
+			return UnavailableReason{ qs(data.vreason()), qs(data.vtext()) };
+		});
+	}) | ranges::to_vector;
 }
 
 MTPPhotoSize FindDocumentInlineThumbnail(const MTPDdocument &data) {
@@ -365,10 +361,10 @@ not_null<UserData*> Session::processUser(const MTPUser &data) {
 				result->inputUser = MTP_inputUser(data.vid(), MTP_long(result->accessHash()));
 			}
 			if (const auto restriction = data.vrestriction_reason()) {
-				result->setUnavailableReason(
-					ExtractUnavailableReason(restriction->v));
+				result->setUnavailableReasons(
+					ExtractUnavailableReasons(restriction->v));
 			} else {
-				result->setUnavailableReason(QString());
+				result->setUnavailableReasons({});
 			}
 		}
 		if (data.is_deleted()) {
@@ -636,10 +632,10 @@ not_null<PeerData*> Session::processChat(const MTPChat &data) {
 				channel->setVersion(data.vversion().v);
 			}
 			if (const auto restriction = data.vrestriction_reason()) {
-				channel->setUnavailableReason(
-					ExtractUnavailableReason(restriction->v));
+				channel->setUnavailableReasons(
+					ExtractUnavailableReasons(restriction->v));
 			} else {
-				channel->setUnavailableReason(QString());
+				channel->setUnavailableReasons({});
 			}
 			channel->setFlags(data.vflags().v);
 			//if (const auto feedId = data.vfeed_id()) { // #feed
diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp
index d861551b5..fbb4e78f2 100644
--- a/Telegram/SourceFiles/data/data_user.cpp
+++ b/Telegram/SourceFiles/data/data_user.cpp
@@ -81,13 +81,15 @@ void UserData::setPhoto(const MTPUserProfilePhoto &photo) {
 	}
 }
 
-QString UserData::unavailableReason() const {
-	return _unavailableReason;
+auto UserData::unavailableReasons() const
+-> const std::vector<Data::UnavailableReason> & {
+	return _unavailableReasons;
 }
 
-void UserData::setUnavailableReason(const QString &text) {
-	if (_unavailableReason != text) {
-		_unavailableReason = text;
+void UserData::setUnavailableReasons(
+		std::vector<Data::UnavailableReason> &&reasons) {
+	if (_unavailableReasons != reasons) {
+		_unavailableReasons = std::move(reasons);
 		Notify::peerUpdatedDelayed(
 			this,
 			Notify::PeerUpdate::Flag::UnavailableReasonChanged);
diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h
index 5a6df93d1..d58365284 100644
--- a/Telegram/SourceFiles/data/data_user.h
+++ b/Telegram/SourceFiles/data/data_user.h
@@ -207,8 +207,8 @@ public:
 
 	std::unique_ptr<BotInfo> botInfo;
 
-	QString unavailableReason() const override;
-	void setUnavailableReason(const QString &reason);
+	void setUnavailableReasons(
+		std::vector<Data::UnavailableReason> &&reasons);
 
 	int commonChatsCount() const {
 		return _commonChatsCount;
@@ -216,10 +216,13 @@ public:
 	void setCommonChatsCount(int count);
 
 private:
+	auto unavailableReasons() const
+		-> const std::vector<Data::UnavailableReason> & override;
+
 	Flags _flags;
 	FullFlags _fullFlags;
 
-	QString _unavailableReason;
+	std::vector<Data::UnavailableReason> _unavailableReasons;
 	QString _phone;
 	ContactStatus _contactStatus = ContactStatus::Unknown;
 	BlockStatus _blockStatus = BlockStatus::Unknown;
diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index 4996804fa..12e7d6462 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -562,7 +562,7 @@ HistoryWidget::HistoryWidget(
 				updateNotifyControls();
 			}
 			if (update.flags & UpdateFlag::UnavailableReasonChanged) {
-				const auto unavailable = _peer->unavailableReason();
+				const auto unavailable = _peer->computeUnavailableReason();
 				if (!unavailable.isEmpty()) {
 					controller->showBackFromStack();
 					Ui::show(Box<InformBox>(unavailable));
diff --git a/Telegram/SourceFiles/main/main_app_config.cpp b/Telegram/SourceFiles/main/main_app_config.cpp
index bf8820725..0f09e2ea0 100644
--- a/Telegram/SourceFiles/main/main_app_config.cpp
+++ b/Telegram/SourceFiles/main/main_app_config.cpp
@@ -41,6 +41,7 @@ void AppConfig::refresh() {
 		_requestId = 0;
 		refreshDelayed();
 		if (result.type() == mtpc_jsonObject) {
+			_data.clear();
 			for (const auto &element : result.c_jsonObject().vvalue().v) {
 				element.match([&](const MTPDjsonObjectValue &data) {
 					_data.emplace_or_assign(qs(data.vkey()), data.vvalue());
@@ -94,4 +95,23 @@ QString AppConfig::getString(
 	});
 }
 
+std::vector<QString> AppConfig::getStringArray(
+		const QString &key,
+		std::vector<QString> &&fallback) const {
+	return getValue(key, [&](const MTPJSONValue &value) {
+		return value.match([&](const MTPDjsonArray &data) {
+			auto result = std::vector<QString>();
+			for (const auto &entry : data.vvalue().v) {
+				if (entry.type() != mtpc_jsonString) {
+					return std::move(fallback);
+				}
+				result.push_back(qs(entry.c_jsonString().vvalue()));
+			}
+			return result;
+		}, [&](const auto &data) {
+			return std::move(fallback);
+		});
+	});
+}
+
 } // namespace Main
diff --git a/Telegram/SourceFiles/main/main_app_config.h b/Telegram/SourceFiles/main/main_app_config.h
index 3eb8b4315..54afb170e 100644
--- a/Telegram/SourceFiles/main/main_app_config.h
+++ b/Telegram/SourceFiles/main/main_app_config.h
@@ -23,13 +23,16 @@ public:
 			return getDouble(key, fallback);
 		} else if constexpr (std::is_same_v<Type, QString>) {
 			return getString(key, fallback);
+		} else if constexpr (std::is_same_v<Type, std::vector<QString>>) {
+			return getStringArray(key, std::move(fallback));
 		}
 	}
 
 	[[nodiscard]] rpl::producer<> refreshed() const;
 
-private:
 	void refresh();
+
+private:
 	void refreshDelayed();
 
 	template <typename Extractor>
@@ -43,6 +46,9 @@ private:
 	[[nodiscard]] QString getString(
 		const QString &key,
 		const QString &fallback) const;
+	[[nodiscard]] std::vector<QString> getStringArray(
+		const QString &key,
+		std::vector<QString> &&fallback) const;
 
 	const not_null<Account*> _account;
 	std::optional<MTP::Sender> _api;
diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp
index 3209c76ee..7222f06c7 100644
--- a/Telegram/SourceFiles/mainwidget.cpp
+++ b/Telegram/SourceFiles/mainwidget.cpp
@@ -1593,7 +1593,7 @@ void MainWidget::ui_showPeerHistory(
 			peerId = peer->id;
 			if (showAtMsgId > 0) showAtMsgId = -showAtMsgId;
 		}
-		const auto unavailable = peer->unavailableReason();
+		const auto unavailable = peer->computeUnavailableReason();
 		if (!unavailable.isEmpty()) {
 			if (params.activation != anim::activation::background) {
 				Ui::show(Box<InformBox>(unavailable));