diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 3288d109e..efa1d4ede 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -1066,6 +1066,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_in_dlg_file" = "File";
 "lng_in_dlg_sticker" = "Sticker";
 "lng_in_dlg_sticker_emoji" = "{emoji} Sticker";
+"lng_in_dlg_poll" = "Poll";
 
 "lng_ban_user" = "Ban User";
 "lng_delete_all_from" = "Delete all from this user";
diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style
index 418465494..7ec24cee1 100644
--- a/Telegram/SourceFiles/boxes/boxes.style
+++ b/Telegram/SourceFiles/boxes/boxes.style
@@ -863,6 +863,7 @@ createPollLimitLabel: FlatLabel(defaultFlatLabel) {
 	minWidth: 274px;
 	align: align(topleft);
 }
+createPollLimitPadding: margins(22px, 10px, 22px, 5px);
 createPollOptionRemove: CrossButton {
 	width: 22px;
 	height: 22px;
@@ -884,3 +885,10 @@ createPollOptionRemove: CrossButton {
 	}
 }
 createPollOptionRemovePosition: point(10px, 7px);
+createPollWarning: FlatLabel(defaultFlatLabel) {
+	textFg: windowSubTextFg;
+	palette: TextPalette(defaultTextPalette) {
+		linkFg: boxTextFgError;
+	}
+}
+createPollWarningPosition: point(16px, 6px);
diff --git a/Telegram/SourceFiles/boxes/create_poll_box.cpp b/Telegram/SourceFiles/boxes/create_poll_box.cpp
index 64b3394aa..a16883ffb 100644
--- a/Telegram/SourceFiles/boxes/create_poll_box.cpp
+++ b/Telegram/SourceFiles/boxes/create_poll_box.cpp
@@ -15,10 +15,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/widgets/shadow.h"
 #include "ui/widgets/labels.h"
 #include "ui/widgets/buttons.h"
+#include "core/event_filter.h"
 #include "chat_helpers/emoji_suggestions_widget.h"
 #include "settings/settings_common.h"
 #include "base/unique_qptr.h"
 #include "styles/style_boxes.h"
+#include "styles/style_settings.h"
 
 namespace {
 
@@ -27,6 +29,7 @@ constexpr auto kMaxOptionsCount = 10;
 constexpr auto kOptionLimit = 100;
 constexpr auto kWarnQuestionLimit = 80;
 constexpr auto kWarnOptionLimit = 30;
+constexpr auto kErrorLimit = 99;
 
 class Options {
 public:
@@ -41,6 +44,7 @@ public:
 
 	[[nodiscard]] rpl::producer<int> usedCount() const;
 	[[nodiscard]] rpl::producer<not_null<QWidget*>> scrollToWidget() const;
+	[[nodiscard]] rpl::producer<> backspaceInFront() const;
 
 private:
 	class Option {
@@ -50,15 +54,14 @@ private:
 			not_null<Ui::VerticalLayout*> container,
 			int position);
 
-		[[nodisacrd]] bool hasShadow() const;
-		void createShadow();
-		//void destroyShadow();
-
-		void createRemove();
 		void toggleRemoveAlways(bool toggled);
 
+		//[[nodisacrd]] bool hasShadow() const;
+		//void destroyShadow();
+
 		[[nodiscard]] bool isEmpty() const;
 		[[nodiscard]] bool isGood() const;
+		[[nodiscard]] bool isTooLong() const;
 		[[nodiscard]] bool hasFocus() const;
 		void setFocus() const;
 		void clearValue();
@@ -75,6 +78,10 @@ private:
 	private:
 		Option() = default;
 
+		void createShadow();
+		void createRemove();
+		void createWarning();
+
 		base::unique_qptr<Ui::InputField> _field;
 		base::unique_qptr<Ui::PlainShadow> _shadow;
 		base::unique_qptr<Ui::CrossButton> _remove;
@@ -99,6 +106,7 @@ private:
 	rpl::variable<bool> _valid = false;
 	rpl::variable<int> _usedCount = 0;
 	rpl::event_stream<not_null<QWidget*>> _scrollToWidget;
+	rpl::event_stream<> _backspaceInFront;
 
 };
 
@@ -110,6 +118,38 @@ void InitField(
 	Ui::Emoji::SuggestionsController::Init(container, field);
 }
 
+not_null<Ui::FlatLabel*> CreateWarningLabel(
+		not_null<QWidget*> parent,
+		not_null<Ui::InputField*> field,
+		int valueLimit,
+		int warnLimit) {
+	const auto result = Ui::CreateChild<Ui::FlatLabel>(
+		parent.get(),
+		QString(),
+		Ui::FlatLabel::InitType::Simple,
+		st::createPollWarning);
+	result->setAttribute(Qt::WA_TransparentForMouseEvents);
+	QObject::connect(field, &Ui::InputField::changed, [=] {
+		Ui::PostponeCall(crl::guard(field, [=] {
+			const auto length = field->getLastText().size();
+			const auto value = valueLimit - length;
+			const auto shown = (value < warnLimit)
+				&& (field->height() > st::createPollOptionField.heightMin);
+			result->setRichText((value >= 0)
+				? QString::number(value)
+				: textcmdLink(1, QString::number(value)));
+			result->setVisible(shown);
+		}));
+	});
+	return result;
+}
+
+void FocusAtEnd(not_null<Ui::InputField*> field) {
+	field->setFocus();
+	field->setCursorPosition(field->getLastText().size());
+	field->ensureCursorVisible();
+}
+
 Options::Option Options::Option::Create(
 		not_null<QWidget*> outer,
 		not_null<Ui::VerticalLayout*> container,
@@ -123,16 +163,18 @@ Options::Option Options::Option::Create(
 			Ui::InputField::Mode::MultiLine,
 			langFactory(lng_polls_create_option_add)));
 	InitField(outer, field);
+	field->setMaxLength(kOptionLimit + kErrorLimit);
 	result._field.reset(field);
 
 	result.createShadow();
 	result.createRemove();
+	result.createWarning();
 	return result;
 }
 
-bool Options::Option::hasShadow() const {
-	return (_shadow != nullptr);
-}
+//bool Options::Option::hasShadow() const {
+//	return (_shadow != nullptr);
+//}
 
 void Options::Option::createShadow() {
 	Expects(_field != nullptr);
@@ -195,13 +237,40 @@ void Options::Option::createRemove() {
 	_remove.reset(remove);
 }
 
+void Options::Option::createWarning() {
+	using namespace rpl::mappers;
+
+	const auto field = _field.get();
+	const auto warning = CreateWarningLabel(
+		field,
+		field,
+		kOptionLimit,
+		kWarnOptionLimit);
+	rpl::combine(
+		field->sizeValue(),
+		warning->sizeValue()
+	) | rpl::start_with_next([=](QSize size, QSize label) {
+		warning->moveToLeft(
+			(size.width()
+				- label.width()
+				- st::createPollWarningPosition.x()),
+			(size.height()
+				- label.height()
+				- st::createPollWarningPosition.y()),
+			size.width());
+	}, warning->lifetime());
+}
+
 bool Options::Option::isEmpty() const {
 	return _field->getLastText().trimmed().isEmpty();
 }
 
 bool Options::Option::isGood() const {
-	const auto text = _field->getLastText().trimmed();
-	return !text.isEmpty() && (text.size() <= kOptionLimit);
+	return !_field->getLastText().trimmed().isEmpty() && !isTooLong();
+}
+
+bool Options::Option::isTooLong() const {
+	return (_field->getLastText().size() > kOptionLimit);
 }
 
 bool Options::Option::hasFocus() const {
@@ -209,7 +278,7 @@ bool Options::Option::hasFocus() const {
 }
 
 void Options::Option::setFocus() const {
-	_field->setFocus();
+	FocusAtEnd(_field);
 }
 
 void Options::Option::clearValue() {
@@ -272,6 +341,10 @@ rpl::producer<not_null<QWidget*>> Options::scrollToWidget() const {
 	return _scrollToWidget.events();
 }
 
+rpl::producer<> Options::backspaceInFront() const {
+	return _backspaceInFront.events();
+}
+
 std::vector<PollAnswer> Options::toPollAnswers() const {
 	auto result = std::vector<PollAnswer>();
 	result.reserve(_list.size());
@@ -378,6 +451,24 @@ void Options::addEmptyOption() {
 	QObject::connect(field, &Ui::InputField::focused, [=] {
 		_scrollToWidget.fire_copy(field);
 	});
+	Core::InstallEventFilter(field, [=](not_null<QEvent*> event) {
+		if (event->type() != QEvent::KeyPress
+			|| !field->getLastText().isEmpty()) {
+			return false;
+		}
+		const auto key = static_cast<QKeyEvent*>(event.get())->key();
+		if (key != Qt::Key_Backspace) {
+			return false;
+		}
+
+		const auto index = findField(field);
+		if (index > 0) {
+			_list[index - 1].setFocus();
+		} else {
+			_backspaceInFront.fire({});
+		}
+		return true;
+	});
 
 	_list.back().removeClicks(
 	) | rpl::start_with_next([=] {
@@ -403,7 +494,8 @@ void Options::addEmptyOption() {
 
 void Options::validateState() {
 	checkLastOption();
-	_valid = (ranges::count_if(_list, &Option::isGood) > 1);
+	_valid = (ranges::count_if(_list, &Option::isGood) > 1)
+		&& (ranges::find_if(_list, &Option::isTooLong) == end(_list));
 	const auto lastEmpty = !_list.empty() && _list.back().isEmpty();
 	_usedCount = _list.size() - (lastEmpty ? 1 : 0);
 }
@@ -454,6 +546,29 @@ not_null<Ui::InputField*> CreatePollBox::setupQuestion(
 			langFactory(lng_polls_create_question_placeholder)),
 		st::createPollFieldPadding);
 	InitField(getDelegate()->outerContainer(), question);
+	question->setMaxLength(kQuestionLimit + kErrorLimit);
+
+	const auto warning = CreateWarningLabel(
+		container,
+		question,
+		kQuestionLimit,
+		kWarnQuestionLimit);
+	rpl::combine(
+		question->geometryValue(),
+		warning->sizeValue()
+	) | rpl::start_with_next([=](QRect geometry, QSize label) {
+		warning->moveToLeft(
+			(container->width()
+				- label.width()
+				- st::createPollWarningPosition.x()),
+			(geometry.y()
+				- st::createPollFieldPadding.top()
+				- st::settingsSubsectionTitlePadding.bottom()
+				- st::settingsSubsectionTitle.style.font->height
+				+ st::settingsSubsectionTitle.style.font->ascent
+				- st::createPollWarning.style.font->ascent),
+			geometry.width());
+	}, warning->lifetime());
 
 	return question;
 }
@@ -486,7 +601,7 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
 			container,
 			std::move(limit),
 			st::createPollLimitLabel),
-		st::createPollFieldPadding);
+		st::createPollLimitPadding);
 
 	const auto isValidQuestion = [=] {
 		const auto text = question->getLastText().trimmed();
@@ -512,7 +627,11 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
 	const auto updateValid = [=] {
 		valid->fire(isValidQuestion() && options->isValid());
 	};
-	valid->events(
+	connect(question, &Ui::InputField::changed, [=] {
+		updateValid();
+	});
+	valid->events_starting_with(
+		false
 	) | rpl::distinct_until_changed(
 	) | rpl::start_with_next([=](bool valid) {
 		clearButtons();
@@ -534,6 +653,11 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
 		scrollToWidget(widget);
 	}, lifetime());
 
+	options->backspaceInFront(
+	) | rpl::start_with_next([=] {
+		FocusAtEnd(question);
+	}, lifetime());
+
 	return std::move(result);
 }
 
@@ -543,4 +667,7 @@ void CreatePollBox::prepare() {
 	const auto inner = setInnerWidget(setupContent());
 
 	setDimensionsToContent(st::boxWideWidth, inner);
+
+	setCloseByEscape(false);
+	setCloseByOutsideClick(false);
 }
diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp
index 5f7f5dfff..961ffd448 100644
--- a/Telegram/SourceFiles/data/data_media_types.cpp
+++ b/Telegram/SourceFiles/data/data_media_types.cpp
@@ -1216,12 +1216,8 @@ PollData *MediaPoll::poll() const {
 	return _poll;
 }
 
-QString MediaPoll::chatsListText() const {
-	return QString(); // #TODO polls
-}
-
 QString MediaPoll::notificationText() const {
-	return QString(); // #TODO polls
+	return lang(lng_in_dlg_poll);
 }
 
 QString MediaPoll::pinnedTextSubstring() const {
@@ -1229,7 +1225,19 @@ QString MediaPoll::pinnedTextSubstring() const {
 }
 
 TextWithEntities MediaPoll::clipboardText() const {
-	return TextWithEntities(); // #TODO polls
+	const auto text = qsl("[ ")
+		+ lang(lng_in_dlg_poll)
+		+ qsl(" : ")
+		+ _poll->question
+		+ qsl(" ]")
+		+ ranges::accumulate(
+			ranges::view::all(
+				_poll->answers
+			) | ranges::view::transform(
+				[](const PollAnswer &answer) { return "\n- " + answer.text; }
+			),
+			QString());
+	return { text, EntitiesInText() };
 }
 
 bool MediaPoll::updateInlineResultMedia(const MTPMessageMedia &media) {
diff --git a/Telegram/SourceFiles/data/data_media_types.h b/Telegram/SourceFiles/data/data_media_types.h
index c6b1e2928..1110578b0 100644
--- a/Telegram/SourceFiles/data/data_media_types.h
+++ b/Telegram/SourceFiles/data/data_media_types.h
@@ -390,7 +390,6 @@ public:
 
 	PollData *poll() const override;
 
-	QString chatsListText() const override;
 	QString notificationText() const override;
 	QString pinnedTextSubstring() const override;
 	TextWithEntities clipboardText() const override;