diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 64c11ae7a..d13b6f4b6 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -602,6 +602,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_proxy_sponsor_warning" = "This proxy may display a sponsored channel in your chat list. This doesn't reveal any of your Telegram traffic.";
 "lng_badge_psa_default" = "PSA";
 "lng_about_psa_default" = "This message provides you with a public service announcement. To remove it from your chats list, right click it and select **Hide**.";
+"lng_tooltip_psa_default" = "This message provides you with a public service announcement.";
 
 "lng_settings_blocked_users" = "Blocked users";
 "lng_settings_no_blocked_users" = "None";
diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp
index bb4dd9320..062f97818 100644
--- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp
+++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp
@@ -582,6 +582,9 @@ void InnerWidget::elementShowPollResults(
 	FullMsgId context) {
 }
 
+void InnerWidget::elementShowTooltip(const TextWithEntities &text) {
+}
+
 void InnerWidget::saveState(not_null<SectionMemento*> memento) {
 	memento->setFilter(std::move(_filter));
 	memento->setAdmins(std::move(_admins));
diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h
index 01bacd75d..3a19f46bf 100644
--- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h
+++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h
@@ -104,6 +104,7 @@ public:
 	void elementShowPollResults(
 		not_null<PollData*> poll,
 		FullMsgId context) override;
+	void elementShowTooltip(const TextWithEntities &text) override;
 
 	~InnerWidget();
 
diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style
index 624dab4a6..6cdd10442 100644
--- a/Telegram/SourceFiles/history/history.style
+++ b/Telegram/SourceFiles/history/history.style
@@ -394,6 +394,15 @@ historyBubbleTailOutRightSelected: icon {{ "bubble_tail-flip_horizontal", msgOut
 
 historyPeerUserpicFont: semiboldFont;
 
+historyPsaIconIn: icon {{ "message_psa_tooltip", msgFileThumbLinkInFg }};
+historyPsaIconInSelected: icon {{ "message_psa_tooltip", msgFileThumbLinkInFgSelected }};
+historyPsaIconOut: icon {{ "message_psa_tooltip", msgFileThumbLinkOutFg }};
+historyPsaIconOutSelected: icon {{ "message_psa_tooltip", msgFileThumbLinkOutFgSelected }};
+historyPsaIconSkip1: 23px;
+historyPsaIconSkip2: 23px;
+historyPsaIconPosition1: point(-5px, 0px);
+historyPsaIconPosition2: point(-5px, 0px);
+
 historyStatusFg: windowSubTextFg;
 historyStatusFgActive: windowActiveTextFg;
 historyStatusFgTyping: historyStatusFgActive;
diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp
index 67ae34198..6bea30897 100644
--- a/Telegram/SourceFiles/history/history_inner_widget.cpp
+++ b/Telegram/SourceFiles/history/history_inner_widget.cpp
@@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "history/history_inner_widget.h"
 
 #include <rpl/merge.h>
-#include "styles/style_history.h"
 #include "core/file_utilities.h"
 #include "core/crash_reports.h"
 #include "history/history.h"
@@ -57,6 +56,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_histories.h"
 #include "facades.h"
 #include "app.h"
+#include "styles/style_history.h"
+#include "styles/style_window.h" // st::windowMinWidth
 
 #include <QtGui/QClipboard>
 #include <QtWidgets/QApplication>
@@ -87,6 +88,13 @@ int BinarySearchBlocksOrItems(const T &list, int edge) {
 	return start;
 }
 
+[[nodiscard]] crl::time CountToastDuration(const TextWithEntities &text) {
+	return std::clamp(
+		crl::time(1000) * text.text.size() / 14,
+		crl::time(1000) * 5,
+		crl::time(1000) * 8);
+}
+
 } // namespace
 
 // flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html
@@ -2481,6 +2489,16 @@ void HistoryInner::elementShowPollResults(
 	_controller->showPollResults(poll, context);
 }
 
+void HistoryInner::elementShowTooltip(const TextWithEntities &text) {
+	auto config = Ui::Toast::Config();
+	config.multiline = config.dark = true;
+	config.minWidth = st::msgMinWidth;
+	config.maxWidth = st::windowMinWidth;
+	config.text = text;
+	config.durationMs = CountToastDuration(config.text);
+	Ui::Toast::Show(_widget, config);
+}
+
 auto HistoryInner::getSelectionState() const
 -> HistoryView::TopBarWidget::SelectedState {
 	auto result = HistoryView::TopBarWidget::SelectedState {};
@@ -3348,6 +3366,11 @@ not_null<HistoryView::ElementDelegate*> HistoryInner::ElementDelegate() {
 				Instance->elementShowPollResults(poll, context);
 			}
 		}
+		void elementShowTooltip(const TextWithEntities &text) override {
+			if (Instance) {
+				Instance->elementShowTooltip(text);
+			}
+		}
 
 	};
 
diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h
index a0b551450..1a95b2d76 100644
--- a/Telegram/SourceFiles/history/history_inner_widget.h
+++ b/Telegram/SourceFiles/history/history_inner_widget.h
@@ -85,6 +85,7 @@ public:
 	void elementShowPollResults(
 		not_null<PollData*> poll,
 		FullMsgId context);
+	void elementShowTooltip(const TextWithEntities &text);
 
 	void updateBotInfo(bool recount = true);
 
diff --git a/Telegram/SourceFiles/history/history_item_components.h b/Telegram/SourceFiles/history/history_item_components.h
index 6582bca1f..729069e24 100644
--- a/Telegram/SourceFiles/history/history_item_components.h
+++ b/Telegram/SourceFiles/history/history_item_components.h
@@ -78,6 +78,7 @@ struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded
 	std::unique_ptr<HiddenSenderInfo> hiddenSenderInfo;
 	QString originalAuthor;
 	QString psaType;
+	mutable ClickHandlerPtr psaTooltipLink;
 	MsgId originalId = 0;
 	mutable Ui::Text::String text = { 1 };
 
diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index f3fc0a3bb..e122320cd 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -4202,6 +4202,7 @@ void HistoryWidget::moveFieldControls() {
 	}
 
 	if (_aboutTopPromotion) {
+		_aboutTopPromotion->resizeToWidth(width());
 		_aboutTopPromotion->moveToLeft(
 			0,
 			fullWidthButtonRect.y() - _aboutTopPromotion->height());
@@ -5224,7 +5225,6 @@ void HistoryWidget::updateHistoryGeometry(
 		}
 	}
 	if (_aboutTopPromotion) {
-		_aboutTopPromotion->resizeToWidth(width());
 		newScrollHeight -= _aboutTopPromotion->height();
 	}
 	if (newScrollHeight <= 0) {
diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp
index 6921d7aa8..0e5a75209 100644
--- a/Telegram/SourceFiles/history/view/history_view_element.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_element.cpp
@@ -99,6 +99,10 @@ void SimpleElementDelegate::elementShowPollResults(
 	FullMsgId context) {
 }
 
+void SimpleElementDelegate::elementShowTooltip(
+	const TextWithEntities &text) {
+}
+
 TextSelection UnshiftItemSelection(
 		TextSelection selection,
 		uint16 byLength) {
diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h
index d23b08f17..27a583188 100644
--- a/Telegram/SourceFiles/history/view/history_view_element.h
+++ b/Telegram/SourceFiles/history/view/history_view_element.h
@@ -54,6 +54,7 @@ public:
 	virtual void elementShowPollResults(
 		not_null<PollData*> poll,
 		FullMsgId context) = 0;
+	virtual void elementShowTooltip(const TextWithEntities &text) = 0;
 
 };
 
@@ -77,6 +78,7 @@ public:
 	void elementShowPollResults(
 		not_null<PollData*> poll,
 		FullMsgId context) override;
+	void elementShowTooltip(const TextWithEntities &text) override;
 
 };
 
diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
index cf0bb871a..7bdfff6e4 100644
--- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
@@ -1158,6 +1158,9 @@ void ListWidget::elementShowPollResults(
 	FullMsgId context) {
 }
 
+void ListWidget::elementShowTooltip(const TextWithEntities &text) {
+}
+
 void ListWidget::saveState(not_null<ListMemento*> memento) {
 	memento->setAroundPosition(_aroundPosition);
 	auto state = countScrollState();
diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h
index b821cb127..1bb94045d 100644
--- a/Telegram/SourceFiles/history/view/history_view_list_widget.h
+++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h
@@ -200,6 +200,7 @@ public:
 	void elementShowPollResults(
 		not_null<PollData*> poll,
 		FullMsgId context) override;
+	void elementShowTooltip(const TextWithEntities &text) override;
 
 	~ListWidget();
 
diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp
index e25fa0545..2100d0867 100644
--- a/Telegram/SourceFiles/history/view/history_view_message.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_message.cpp
@@ -14,6 +14,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "history/view/media/history_view_web_page.h"
 #include "history/history.h"
 #include "ui/toast/toast.h"
+#include "ui/text/text_utilities.h"
+#include "ui/text/text_entity.h"
 #include "data/data_session.h"
 #include "data/data_user.h"
 #include "data/data_channel.h"
@@ -32,6 +34,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 namespace HistoryView {
 namespace {
 
+const auto kPsaTooltipPrefix = "cloud_lng_tooltip_psa_";
+
 class KeyboardStyle : public ReplyKeyboard::Style {
 public:
 	using ReplyKeyboard::Style::Style;
@@ -311,7 +315,10 @@ QSize Message::performCountOptimalSize() {
 				accumulate_max(maxWidth, st::msgPadding.left() + via->maxWidth + st::msgPadding.right());
 			}
 			if (displayForwardedFrom()) {
-				auto namew = st::msgPadding.left() + forwarded->text.maxWidth() + st::msgPadding.right();
+				const auto skip1 = forwarded->psaType.isEmpty()
+					? 0
+					: st::historyPsaIconSkip1;
+				auto namew = st::msgPadding.left() + forwarded->text.maxWidth() + skip1 + st::msgPadding.right();
 				if (via) {
 					namew += st::msgServiceFont->spacew + via->maxWidth;
 				}
@@ -608,12 +615,21 @@ void Message::paintForwardedInfo(Painter &p, QRect &trect, bool selected) const
 	if (displayForwardedFrom()) {
 		const auto item = message();
 		const auto outbg = hasOutLayout();
-		auto forwarded = item->Get<HistoryMessageForwarded>();
+		const auto forwarded = item->Get<HistoryMessageForwarded>();
 
 		const auto &serviceFont = st::msgServiceFont;
 		const auto &serviceName = st::msgServiceNameFont;
-		const auto breakEverywhere = (forwarded->text.countHeight(trect.width()) > 2 * serviceFont->height);
-
+		const auto skip1 = forwarded->psaType.isEmpty()
+			? 0
+			: st::historyPsaIconSkip1;
+		const auto skip2 = forwarded->psaType.isEmpty()
+			? 0
+			: st::historyPsaIconSkip2;
+		const auto fits = (forwarded->text.maxWidth() + skip1 <= trect.width());
+		const auto skip = fits ? skip1 : skip2;
+		const auto useWidth = trect.width() - skip;
+		const auto countedHeight = forwarded->text.countHeight(useWidth);
+		const auto breakEverywhere = (countedHeight > 2 * serviceFont->height);
 		p.setPen(!forwarded->psaType.isEmpty()
 			? st::boxTextFgGood
 			: selected
@@ -633,10 +649,26 @@ void Message::paintForwardedInfo(Painter &p, QRect &trect, bool selected) const
 			: (outbg
 				? st::outFwdTextPalette
 				: st::inFwdTextPalette));
-		forwarded->text.drawElided(p, trect.x(), trect.y(), trect.width(), 2, style::al_left, 0, -1, 0, breakEverywhere);
+		forwarded->text.drawElided(p, trect.x(), trect.y(), useWidth, 2, style::al_left, 0, -1, 0, breakEverywhere);
 		p.setTextPalette(selected ? (outbg ? st::outTextPaletteSelected : st::inTextPaletteSelected) : (outbg ? st::outTextPalette : st::inTextPalette));
 
-		trect.setY(trect.y() + (((forwarded->text.maxWidth() > trect.width()) ? 2 : 1) * serviceFont->height));
+		if (!forwarded->psaType.isEmpty()) {
+			const auto &icon = selected
+				? (outbg
+					? st::historyPsaIconOutSelected
+					: st::historyPsaIconInSelected)
+				: (outbg ? st::historyPsaIconOut : st::historyPsaIconIn);
+			const auto position = fits
+				? st::historyPsaIconPosition1
+				: st::historyPsaIconPosition2;
+			icon.paint(
+				p,
+				trect.x() + trect.width() - position.x() - icon.width(),
+				trect.y() + position.y(),
+				trect.width());
+		}
+
+		trect.setY(trect.y() + ((fits ? 1 : 2) * serviceFont->height));
 	}
 }
 
@@ -959,17 +991,41 @@ bool Message::getStateForwardedInfo(
 		StateRequest request) const {
 	if (displayForwardedFrom()) {
 		const auto item = message();
-		auto forwarded = item->Get<HistoryMessageForwarded>();
-		auto fwdheight = ((forwarded->text.maxWidth() > trect.width()) ? 2 : 1) * st::semiboldFont->height;
+		const auto forwarded = item->Get<HistoryMessageForwarded>();
+		const auto skip1 = forwarded->psaType.isEmpty()
+			? 0
+			: st::historyPsaIconSkip1;
+		const auto skip2 = forwarded->psaType.isEmpty()
+			? 0
+			: st::historyPsaIconSkip2;
+		const auto fits = (forwarded->text.maxWidth() <= (trect.width() - skip1));
+		const auto fwdheight = (fits ? 1 : 2) * st::semiboldFont->height;
 		if (point.y() >= trect.top() && point.y() < trect.top() + fwdheight) {
-			auto breakEverywhere = (forwarded->text.countHeight(trect.width()) > 2 * st::semiboldFont->height);
+			if (skip1) {
+				const auto &icon = st::historyPsaIconIn;
+				const auto position = fits
+					? st::historyPsaIconPosition1
+					: st::historyPsaIconPosition2;
+				const auto iconRect = QRect(
+					trect.x() + trect.width() - position.x() - icon.width(),
+					trect.y() + position.y(),
+					icon.width(),
+					icon.height());
+				if (iconRect.contains(point)) {
+					ensurePsaTooltipLink(forwarded);
+					outResult->link = forwarded->psaTooltipLink;
+					return true;
+				}
+			}
+			const auto useWidth = trect.width() - (fits ? skip1 : skip2);
+			const auto breakEverywhere = (forwarded->text.countHeight(useWidth) > 2 * st::semiboldFont->height);
 			auto textRequest = request.forText();
 			if (breakEverywhere) {
 				textRequest.flags |= Ui::Text::StateRequest::Flag::BreakEverywhere;
 			}
 			*outResult = TextState(item, forwarded->text.getState(
 				point - trect.topLeft(),
-				trect.width(),
+				useWidth,
 				textRequest));
 			outResult->symbol = 0;
 			outResult->afterSymbol = false;
@@ -985,6 +1041,29 @@ bool Message::getStateForwardedInfo(
 	return false;
 }
 
+void Message::ensurePsaTooltipLink(
+		not_null<const HistoryMessageForwarded*> forwarded) const {
+	if (forwarded->psaTooltipLink) {
+		return;
+	}
+	const auto type = forwarded->psaType;
+	const auto handler = [=] {
+		const auto custom = type.isEmpty()
+			? QString()
+			: Lang::Current().getNonDefaultValue(
+				kPsaTooltipPrefix + type.toUtf8());
+		auto text = Ui::Text::RichLangValue(
+			(custom.isEmpty()
+				? tr::lng_tooltip_psa_default(tr::now)
+				: custom));
+		TextUtilities::ParseEntities(text, 0);
+		delegate()->elementShowTooltip(text);
+	};
+	forwarded->psaTooltipLink
+		= std::make_shared<LambdaClickHandler>(
+			crl::guard(this, handler));
+}
+
 bool Message::getStateReplyInfo(
 		QPoint point,
 		QRect &trect,
@@ -1780,8 +1859,14 @@ int Message::resizeContentGetHeight(int newWidth) {
 		}
 
 		if (displayForwardedFrom()) {
-			auto forwarded = item->Get<HistoryMessageForwarded>();
-			auto fwdheight = ((forwarded->text.maxWidth() > (contentWidth - st::msgPadding.left() - st::msgPadding.right())) ? 2 : 1) * st::semiboldFont->height;
+			const auto forwarded = item->Get<HistoryMessageForwarded>();
+			const auto skip1 = forwarded->psaType.isEmpty()
+				? 0
+				: st::historyPsaIconSkip1;
+			const auto skip2 = forwarded->psaType.isEmpty()
+				? 0
+				: st::historyPsaIconSkip2;
+			const auto fwdheight = ((forwarded->text.maxWidth() > (contentWidth - st::msgPadding.left() - st::msgPadding.right() - skip1)) ? 2 : 1) * st::semiboldFont->height;
 			newHeight += fwdheight;
 		}
 
diff --git a/Telegram/SourceFiles/history/view/history_view_message.h b/Telegram/SourceFiles/history/view/history_view_message.h
index 2e9212bd3..abee35780 100644
--- a/Telegram/SourceFiles/history/view/history_view_message.h
+++ b/Telegram/SourceFiles/history/view/history_view_message.h
@@ -8,9 +8,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #pragma once
 
 #include "history/view/history_view_element.h"
+#include "base/weak_ptr.h"
 
 class HistoryMessage;
 struct HistoryMessageEdited;
+struct HistoryMessageForwarded;
 
 namespace HistoryView {
 
@@ -28,7 +30,7 @@ struct LogEntryOriginal
 
 };
 
-class Message : public Element {
+class Message : public Element, public base::has_weak_ptr {
 public:
 	Message(
 		not_null<ElementDelegate*> delegate,
@@ -152,6 +154,9 @@ private:
 
 	WebPage *logEntryOriginal() const;
 
+	void ensurePsaTooltipLink(
+		not_null<const HistoryMessageForwarded*> forwarded) const;
+
 	mutable ClickHandlerPtr _rightActionLink;
 	mutable ClickHandlerPtr _fastReplyLink;
 
diff --git a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp
index 4a5879453..68c20c9d3 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp
+++ b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp
@@ -122,13 +122,6 @@ void CountNicePercent(
 	}
 }
 
-[[nodiscard]] crl::time CountToastDuration(const TextWithEntities &text) {
-	return std::clamp(
-		crl::time(1000) * text.text.size() / 14,
-		crl::time(1000) * 5,
-		crl::time(1000) * 8);
-}
-
 } // namespace
 
 struct Poll::AnswerAnimation {
@@ -443,16 +436,9 @@ void Poll::checkQuizAnswered() {
 }
 
 void Poll::showSolution() const {
-	if (_poll->solution.text.isEmpty()) {
-		return;
+	if (!_poll->solution.text.isEmpty()) {
+		_parent->delegate()->elementShowTooltip(_poll->solution);
 	}
-	auto config = Ui::Toast::Config();
-	config.multiline = config.dark = true;
-	config.minWidth = st::msgMinWidth;
-	config.maxWidth = st::windowMinWidth;
-	config.text = _poll->solution;
-	config.durationMs = CountToastDuration(config.text);
-	Ui::Toast::Show(config);
 }
 
 void Poll::updateRecentVoters() {