From 6562a1f6af9d1cdbb5609519e3962d7d2da9e022 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 4 Dec 2018 14:32:06 +0400
Subject: [PATCH] Add 'Count unread messages' option.

---
 Telegram/Resources/langs/lang.strings         |   1 +
 Telegram/SourceFiles/auth_session.cpp         |  10 ++
 Telegram/SourceFiles/auth_session.h           |  15 ++
 Telegram/SourceFiles/data/data_feed.cpp       | 113 ++++++++-----
 Telegram/SourceFiles/data/data_feed.h         |   3 +
 .../SourceFiles/dialogs/dialogs_layout.cpp    |  33 ++--
 Telegram/SourceFiles/facades.cpp              |   2 -
 Telegram/SourceFiles/facades.h                |   1 -
 Telegram/SourceFiles/history/history.cpp      | 152 ++++++++++++++++--
 Telegram/SourceFiles/history/history.h        |  23 ++-
 .../view/history_view_top_bar_widget.cpp      |  32 ++--
 Telegram/SourceFiles/messenger.cpp            |  17 +-
 Telegram/SourceFiles/messenger.h              |   2 +
 .../platform/linux/main_window_linux.cpp      |  18 ++-
 .../platform/mac/main_window_mac.mm           |  13 +-
 .../platform/win/main_window_win.cpp          |   4 +-
 .../settings/settings_notifications.cpp       |  18 ++-
 Telegram/SourceFiles/storage/localstorage.cpp |   7 +-
 Telegram/SourceFiles/window/main_window.cpp   |   4 +-
 .../window/notifications_manager.cpp          |   3 +-
 .../window/notifications_manager.h            |   1 +
 21 files changed, 353 insertions(+), 119 deletions(-)

diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index a5bbddb4e..61f791643 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -283,6 +283,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_settings_notifications_count" = "Notifications count";
 "lng_settings_sound_notify" = "Play sound";
 "lng_settings_include_muted" = "Include muted chats in unread count";
+"lng_settings_count_unread" = "Count unread messages";
 
 "lng_notification_preview" = "You have a new message";
 "lng_notification_reply" = "Reply";
diff --git a/Telegram/SourceFiles/auth_session.cpp b/Telegram/SourceFiles/auth_session.cpp
index afd4aaea8..d9042b239 100644
--- a/Telegram/SourceFiles/auth_session.cpp
+++ b/Telegram/SourceFiles/auth_session.cpp
@@ -85,6 +85,8 @@ QByteArray AuthSessionSettings::serialize() const {
 		stream << qint32(_variables.supportFixChatsOrder ? 1 : 0);
 		stream << qint32(_variables.supportTemplatesAutocomplete ? 1 : 0);
 		stream << qint32(_variables.supportChatsTimeSlice.current());
+		stream << qint32(_variables.includeMutedCounter ? 1 : 0);
+		stream << qint32(_variables.countUnreadMessages ? 1 : 0);
 	}
 	return result;
 }
@@ -116,6 +118,8 @@ void AuthSessionSettings::constructFromSerialized(const QByteArray &serialized)
 	qint32 supportFixChatsOrder = _variables.supportFixChatsOrder ? 1 : 0;
 	qint32 supportTemplatesAutocomplete = _variables.supportTemplatesAutocomplete ? 1 : 0;
 	qint32 supportChatsTimeSlice = _variables.supportChatsTimeSlice.current();
+	qint32 includeMutedCounter = _variables.includeMutedCounter ? 1 : 0;
+	qint32 countUnreadMessages = _variables.countUnreadMessages ? 1 : 0;
 
 	stream >> selectorTab;
 	stream >> lastSeenWarningSeen;
@@ -182,6 +186,10 @@ void AuthSessionSettings::constructFromSerialized(const QByteArray &serialized)
 	if (!stream.atEnd()) {
 		stream >> supportChatsTimeSlice;
 	}
+	if (!stream.atEnd()) {
+		stream >> includeMutedCounter;
+		stream >> countUnreadMessages;
+	}
 	if (stream.status() != QDataStream::Ok) {
 		LOG(("App Error: "
 			"Bad data for AuthSessionSettings::constructFromSerialized()"));
@@ -243,6 +251,8 @@ void AuthSessionSettings::constructFromSerialized(const QByteArray &serialized)
 	_variables.supportTemplatesAutocomplete = (supportTemplatesAutocomplete == 1);
 	_variables.supportChatsTimeSlice = supportChatsTimeSlice;
 	_variables.hadLegacyCallsPeerToPeerNobody = (legacyCallsPeerToPeer == kLegacyCallsPeerToPeerNobody);
+	_variables.includeMutedCounter = (includeMutedCounter == 1);
+	_variables.countUnreadMessages = (countUnreadMessages == 1);
 }
 
 void AuthSessionSettings::setSupportChatsTimeSlice(int slice) {
diff --git a/Telegram/SourceFiles/auth_session.h b/Telegram/SourceFiles/auth_session.h
index 2494d5203..16e79298c 100644
--- a/Telegram/SourceFiles/auth_session.h
+++ b/Telegram/SourceFiles/auth_session.h
@@ -187,6 +187,19 @@ public:
 		return _variables.hadLegacyCallsPeerToPeerNobody;
 	}
 
+	bool includeMutedCounter() const {
+		return _variables.includeMutedCounter;
+	}
+	void setIncludeMutedCounter(bool value) {
+		_variables.includeMutedCounter = value;
+	}
+	bool countUnreadMessages() const {
+		return _variables.countUnreadMessages;
+	}
+	void setCountUnreadMessages(bool value) {
+		_variables.countUnreadMessages = value;
+	}
+
 private:
 	struct Variables {
 		Variables();
@@ -212,6 +225,8 @@ private:
 			= kDefaultThirdColumnWidth; // per-window
 		Ui::InputSubmitSettings sendSubmitWay;
 		bool hadLegacyCallsPeerToPeerNobody = false;
+		bool includeMutedCounter = true;
+		bool countUnreadMessages = true;
 
 		static constexpr auto kDefaultSupportChatsLimitSlice
 			= 7 * 24 * 60 * 60;
diff --git a/Telegram/SourceFiles/data/data_feed.cpp b/Telegram/SourceFiles/data/data_feed.cpp
index 14df12f17..09cf66e9f 100644
--- a/Telegram/SourceFiles/data/data_feed.cpp
+++ b/Telegram/SourceFiles/data/data_feed.cpp
@@ -361,13 +361,68 @@ bool Feed::unreadCountKnown() const {
 //}
 
 void Feed::changedInChatListHook(Dialogs::Mode list, bool added) {
-	if (list == Dialogs::Mode::All && unreadCount()) {
+	if (list != Dialogs::Mode::All) {
+		return;
+	}
+	if (const auto count = unreadCount()) {
 		const auto mutedCount = _unreadMutedCount;
-		const auto nonMutedCount = unreadCount() - mutedCount;
+		const auto nonMutedCount = count - mutedCount;
 		const auto mutedDelta = added ? mutedCount : -mutedCount;
 		const auto nonMutedDelta = added ? nonMutedCount : -nonMutedCount;
 		App::histories().unreadIncrement(nonMutedDelta, false);
 		App::histories().unreadIncrement(mutedDelta, true);
+
+		const auto fullMuted = (nonMutedCount == 0);
+		const auto entriesWithUnreadDelta = added ? 1 : -1;
+		const auto mutedEntriesWithUnreadDelta = fullMuted
+			? entriesWithUnreadDelta
+			: 0;
+		App::histories().unreadEntriesChanged(
+			entriesWithUnreadDelta,
+			mutedEntriesWithUnreadDelta);
+	}
+}
+
+template <typename PerformUpdate>
+void Feed::updateUnreadCounts(PerformUpdate &&performUpdate) {
+	const auto wasUnreadCount = _unreadCount  ? *_unreadCount : 0;
+	const auto wasUnreadMutedCount = _unreadMutedCount;
+	const auto wasFullMuted = (wasUnreadMutedCount > 0)
+		&& (wasUnreadCount == wasUnreadMutedCount);
+
+	performUpdate();
+	Assert(_unreadCount.has_value());
+
+	_unreadCountChanges.fire(unreadCount());
+	updateChatListEntry();
+
+	if (inChatList(Dialogs::Mode::All)) {
+		const auto nowUnreadCount = *_unreadCount;
+		const auto nowUnreadMutedCount = _unreadMutedCount;
+		const auto nowFullMuted = (nowUnreadMutedCount > 0)
+			&& (nowUnreadCount == nowUnreadMutedCount);
+
+		App::histories().unreadIncrement(
+			(nowUnreadCount - nowUnreadMutedCount)
+			- (wasUnreadCount - wasUnreadMutedCount),
+			false);
+		App::histories().unreadIncrement(
+			nowUnreadMutedCount - wasUnreadMutedCount,
+			true);
+
+		const auto entriesDelta = (nowUnreadCount && !wasUnreadCount)
+			? 1
+			: (wasUnreadCount && !nowUnreadCount)
+			? -1
+			: 0;
+		const auto mutedEntriesDelta = (!wasFullMuted && nowFullMuted)
+			? 1
+			: (wasFullMuted && !nowFullMuted)
+			? -1
+			: 0;
+		App::histories().unreadEntriesChanged(
+			entriesDelta,
+			mutedEntriesDelta);
 	}
 }
 
@@ -377,26 +432,10 @@ void Feed::setUnreadCounts(int unreadNonMutedCount, int unreadMutedCount) {
 		&& (_unreadMutedCount == unreadMutedCount)) {
 		return;
 	}
-	const auto unreadNonMutedCountDelta = _unreadCount | [&](int count) {
-		return unreadNonMutedCount - (count - _unreadMutedCount);
-	};
-	const auto unreadMutedCountDelta = _unreadCount | [&](int count) {
-		return unreadMutedCount - _unreadMutedCount;
-	};
-	_unreadCount = unreadNonMutedCount + unreadMutedCount;
-	_unreadMutedCount = unreadMutedCount;
-
-	_unreadCountChanges.fire(unreadCount());
-	updateChatListEntry();
-
-	if (inChatList(Dialogs::Mode::All)) {
-		App::histories().unreadIncrement(
-			unreadNonMutedCountDelta ? *unreadNonMutedCountDelta : unreadNonMutedCount,
-			false);
-		App::histories().unreadIncrement(
-			unreadMutedCountDelta ? *unreadMutedCountDelta : unreadMutedCount,
-			true);
-	}
+	updateUnreadCounts([&] {
+		_unreadCount = unreadNonMutedCount + unreadMutedCount;
+		_unreadMutedCount = unreadMutedCount;
+	});
 }
 
 void Feed::setUnreadPosition(const MessagePosition &position) {
@@ -405,30 +444,20 @@ void Feed::setUnreadPosition(const MessagePosition &position) {
 	}
 }
 
-void Feed::unreadCountChanged(
-		int unreadCountDelta,
-		int mutedCountDelta) {
+void Feed::unreadCountChanged(int unreadCountDelta, int mutedCountDelta) {
 	if (!unreadCountKnown()) {
 		return;
 	}
-	accumulate_max(unreadCountDelta, -*_unreadCount);
-	*_unreadCount += unreadCountDelta;
+	updateUnreadCounts([&] {
+		accumulate_max(unreadCountDelta, -*_unreadCount);
+		*_unreadCount += unreadCountDelta;
 
-	mutedCountDelta = snap(
-		mutedCountDelta,
-		-_unreadMutedCount,
-		*_unreadCount - _unreadMutedCount);
-	_unreadMutedCount += mutedCountDelta;
-
-	_unreadCountChanges.fire(unreadCount());
-	updateChatListEntry();
-
-	if (inChatList(Dialogs::Mode::All)) {
-		App::histories().unreadIncrement(
-			unreadCountDelta,
-			false);
-		App::histories().unreadMuteChanged(mutedCountDelta, true);
-	}
+		mutedCountDelta = snap(
+			mutedCountDelta,
+			-_unreadMutedCount,
+			*_unreadCount - _unreadMutedCount);
+		_unreadMutedCount += mutedCountDelta;
+	});
 }
 
 MessagePosition Feed::unreadPosition() const {
diff --git a/Telegram/SourceFiles/data/data_feed.h b/Telegram/SourceFiles/data/data_feed.h
index 1cdf30e2f..8304dea62 100644
--- a/Telegram/SourceFiles/data/data_feed.h
+++ b/Telegram/SourceFiles/data/data_feed.h
@@ -94,6 +94,9 @@ private:
 		const std::vector<not_null<ChannelData*>> &add,
 		const std::vector<not_null<ChannelData*>> &remove);
 
+	template <typename PerformUpdate>
+	void updateUnreadCounts(PerformUpdate &&performUpdate);
+
 	FeedId _id = 0;
 	not_null<Data::Session*> _parent;
 	std::vector<not_null<History*>> _channels;
diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp
index 34d31ac78..2c8f323cb 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp
+++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp
@@ -784,19 +784,30 @@ void paintImportantSwitch(Painter &p, Mode current, int fullWidth, bool selected
 	p.setFont(st::semiboldFont);
 	p.setPen(st::dialogsNameFg);
 
-	int unreadTop = (st::dialogsImportantBarHeight - st::dialogsUnreadHeight) / 2;
-	bool mutedHidden = (current == Dialogs::Mode::Important);
-	QString text = lang(mutedHidden ? lng_dialogs_show_all_chats : lng_dialogs_hide_muted_chats);
-	int textBaseline = unreadTop + (st::dialogsUnreadHeight - st::dialogsUnreadFont->height) / 2 + st::dialogsUnreadFont->ascent;
+	const auto unreadTop = (st::dialogsImportantBarHeight - st::dialogsUnreadHeight) / 2;
+	const auto mutedHidden = (current == Dialogs::Mode::Important);
+	const auto text = lang(mutedHidden
+		? lng_dialogs_show_all_chats
+		: lng_dialogs_hide_muted_chats);
+	const auto textBaseline = unreadTop
+		+ (st::dialogsUnreadHeight - st::dialogsUnreadFont->height) / 2
+		+ st::dialogsUnreadFont->ascent;
 	p.drawText(st::dialogsPadding.x(), textBaseline, text);
 
-	if (mutedHidden) {
-		if (int32 unread = App::histories().unreadMutedCount()) {
-			int unreadRight = fullWidth - st::dialogsPadding.x();
-			UnreadBadgeStyle st;
-			st.muted = true;
-			paintUnreadCount(p, QString::number(unread), unreadRight, unreadTop, st, nullptr);
-		}
+	if (!mutedHidden) {
+		return;
+	}
+	if (const auto unread = App::histories().unreadOnlyMutedBadge()) {
+		const auto unreadRight = fullWidth - st::dialogsPadding.x();
+		UnreadBadgeStyle st;
+		st.muted = true;
+		paintUnreadCount(
+			p,
+			QString::number(unread),
+			unreadRight,
+			unreadTop,
+			st,
+			nullptr);
 	}
 }
 
diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp
index fc2eea6ed..499e2afff 100644
--- a/Telegram/SourceFiles/facades.cpp
+++ b/Telegram/SourceFiles/facades.cpp
@@ -639,7 +639,6 @@ struct Data {
 	bool SoundNotify = true;
 	bool DesktopNotify = true;
 	bool RestoreSoundNotifyFromTray = false;
-	bool IncludeMuted = true;
 	DBINotifyView NotifyView = dbinvShowPreview;
 	bool NativeNotifications = false;
 	int NotificationsCount = 3;
@@ -769,7 +768,6 @@ DefineVar(Global, bool, VoiceMsgPlaybackDoubled);
 DefineVar(Global, bool, SoundNotify);
 DefineVar(Global, bool, DesktopNotify);
 DefineVar(Global, bool, RestoreSoundNotifyFromTray);
-DefineVar(Global, bool, IncludeMuted);
 DefineVar(Global, DBINotifyView, NotifyView);
 DefineVar(Global, bool, NativeNotifications);
 DefineVar(Global, int, NotificationsCount);
diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h
index aa5bca13d..c7bcb61a5 100644
--- a/Telegram/SourceFiles/facades.h
+++ b/Telegram/SourceFiles/facades.h
@@ -302,7 +302,6 @@ DeclareVar(bool, VoiceMsgPlaybackDoubled);
 DeclareVar(bool, SoundNotify);
 DeclareVar(bool, DesktopNotify);
 DeclareVar(bool, RestoreSoundNotifyFromTray);
-DeclareVar(bool, IncludeMuted);
 DeclareVar(DBINotifyView, NotifyView);
 DeclareVar(bool, NativeNotifications);
 DeclareVar(int, NotificationsCount);
diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp
index f7f113819..4af5ea2cc 100644
--- a/Telegram/SourceFiles/history/history.cpp
+++ b/Telegram/SourceFiles/history/history.cpp
@@ -108,6 +108,7 @@ void Histories::clear() {
 	_map.clear();
 
 	_unreadFull = _unreadMuted = 0;
+	_unreadEntriesFull = _unreadEntriesMuted = 0;
 	Notify::unreadCounterUpdated();
 	App::historyClearItems();
 	typing.clear();
@@ -164,34 +165,135 @@ HistoryItem *Histories::addNewMessage(
 }
 
 int Histories::unreadBadge() const {
-	return _unreadFull - (Global::IncludeMuted() ? 0 : _unreadMuted);
+	return computeUnreadBadge(
+		_unreadFull,
+		_unreadMuted,
+		_unreadEntriesFull,
+		_unreadEntriesMuted);
 }
 
-int Histories::unreadMutedCount() const {
-	return _unreadMuted;
+bool Histories::unreadBadgeMuted() const {
+	return computeUnreadBadgeMuted(
+		_unreadFull,
+		_unreadMuted,
+		_unreadEntriesFull,
+		_unreadEntriesMuted);
+}
+
+int Histories::unreadBadgeIgnoreOne(History *history) const {
+	const auto removeCount = (history
+		&& history->inChatList(Dialogs::Mode::All))
+		? history->unreadCount()
+		: 0;
+	if (!removeCount) {
+		return unreadBadge();
+	}
+	const auto removeMuted = history->mute();
+	return computeUnreadBadge(
+		_unreadFull - removeCount,
+		_unreadMuted - (removeMuted ? removeCount : 0),
+		_unreadEntriesFull - 1,
+		_unreadEntriesMuted - (removeMuted ? 1 : 0));
+}
+
+bool Histories::unreadBadgeMutedIgnoreOne(History *history) const {
+	const auto removeCount = (history
+		&& history->inChatList(Dialogs::Mode::All))
+		? history->unreadCount()
+		: 0;
+	if (!removeCount) {
+		return unreadBadgeMuted();
+	}
+	const auto removeMuted = history->mute();
+	return computeUnreadBadgeMuted(
+		_unreadFull - removeCount,
+		_unreadMuted - (removeMuted ? removeCount : 0),
+		_unreadEntriesFull - 1,
+		_unreadEntriesMuted - (removeMuted ? 1 : 0));
+}
+
+int Histories::unreadOnlyMutedBadge() const {
+	return Auth().settings().countUnreadMessages()
+		? _unreadMuted
+		: _unreadEntriesMuted;
+}
+
+int Histories::computeUnreadBadge(
+		int full,
+		int muted,
+		int entriesFull,
+		int entriesMuted) const {
+	const auto withMuted = Auth().settings().includeMutedCounter();
+	return Auth().settings().countUnreadMessages()
+		? (full - (withMuted ? 0 : muted))
+		: (entriesFull - (withMuted ? 0 : entriesMuted));
+}
+
+bool Histories::computeUnreadBadgeMuted(
+		int full,
+		int muted,
+		int entriesFull,
+		int entriesMuted) const {
+	if (!Auth().settings().includeMutedCounter()) {
+		return false;
+	}
+	return Auth().settings().countUnreadMessages()
+		? (muted >= full)
+		: (entriesMuted >= entriesFull);
 }
 
 void Histories::unreadIncrement(int count, bool muted) {
+	if (!count) {
+		return;
+	}
 	_unreadFull += count;
 	if (muted) {
 		_unreadMuted += count;
 	}
-	if (!muted || Global::IncludeMuted()) {
-		Notify::unreadCounterUpdated();
+	if (Auth().settings().countUnreadMessages()) {
+		if (!muted || Auth().settings().includeMutedCounter()) {
+			Notify::unreadCounterUpdated();
+		}
 	}
 }
 
 void Histories::unreadMuteChanged(int count, bool muted) {
+	const auto wasAll = (_unreadMuted == _unreadFull);
 	if (muted) {
 		_unreadMuted += count;
 	} else {
 		_unreadMuted -= count;
 	}
-	Notify::unreadCounterUpdated();
+	if (Auth().settings().countUnreadMessages()) {
+		const auto nowAll = (_unreadMuted == _unreadFull);
+		const auto changed = !Auth().settings().includeMutedCounter()
+			|| (wasAll != nowAll);
+		if (changed) {
+			Notify::unreadCounterUpdated();
+		}
+	}
 }
 
-bool Histories::unreadOnlyMuted() const {
-	return Global::IncludeMuted() ? (_unreadMuted >= _unreadFull) : false;
+void Histories::unreadEntriesChanged(
+		int withUnreadDelta,
+		int mutedWithUnreadDelta) {
+	if (!withUnreadDelta && !mutedWithUnreadDelta) {
+		return;
+	}
+	const auto wasAll = (_unreadEntriesMuted == _unreadEntriesFull);
+	_unreadEntriesFull += withUnreadDelta;
+	_unreadEntriesMuted += mutedWithUnreadDelta;
+	if (!Auth().settings().countUnreadMessages()) {
+		const auto nowAll = (_unreadEntriesMuted == _unreadEntriesFull);
+		const auto withMuted = Auth().settings().includeMutedCounter();
+		const auto withMutedChanged = withMuted
+			&& (withUnreadDelta != 0 || wasAll != nowAll);
+		const auto withoutMutedChanged = !withMuted
+			&& (withUnreadDelta != mutedWithUnreadDelta);
+		if (withMutedChanged || withoutMutedChanged) {
+			Notify::unreadCounterUpdated();
+		}
+	}
 }
 
 void Histories::selfDestructIn(not_null<HistoryItem*> item, TimeMs delay) {
@@ -1655,6 +1757,7 @@ bool History::unreadCountKnown() const {
 
 void History::setUnreadCount(int newUnreadCount) {
 	if (!_unreadCount || *_unreadCount != newUnreadCount) {
+		const auto wasUnread = _unreadMark || unreadCount();
 		const auto unreadCountDelta = _unreadCount | [&](int count) {
 			return newUnreadCount - count;
 		};
@@ -1699,12 +1802,20 @@ void History::setUnreadCount(int newUnreadCount) {
 		}
 
 		if (inChatList(Dialogs::Mode::All)) {
-			const auto delta = unreadCountDelta
+			const auto delta = unreadMarkDelta + (unreadCountDelta
 				? *unreadCountDelta
-				: newUnreadCount;
-			App::histories().unreadIncrement(
-				delta + unreadMarkDelta,
-				mute());
+				: newUnreadCount);
+			App::histories().unreadIncrement(delta, mute());
+
+			const auto nowUnread = (*_unreadCount > 0) || _unreadMark;
+			const auto entriesDelta = (wasUnread && !nowUnread)
+				? -1
+				: (nowUnread && !wasUnread)
+				? 1
+				: 0;
+			App::histories().unreadEntriesChanged(
+				entriesDelta,
+				mute() ? entriesDelta : 0);
 		}
 		Notify::peerUpdatedDelayed(
 			peer,
@@ -1722,6 +1833,9 @@ void History::setUnreadMark(bool unread) {
 			if (inChatList(Dialogs::Mode::All)) {
 				const auto delta = _unreadMark ? 1 : -1;
 				App::histories().unreadIncrement(delta, mute());
+				App::histories().unreadEntriesChanged(
+					delta,
+					mute() ? delta : 0);
 
 				updateChatListEntry();
 			}
@@ -1776,6 +1890,13 @@ bool History::changeMute(bool newMute) {
 	if (inChatList(Dialogs::Mode::All)) {
 		if (const auto count = historiesUnreadCount()) {
 			App::histories().unreadMuteChanged(count, _mute);
+
+			const auto entriesWithUnreadDelta = 0;
+			const auto mutedEntriesWithUnreadDelta = _mute ? 1 : -1;
+			App::histories().unreadEntriesChanged(
+				entriesWithUnreadDelta,
+				mutedEntriesWithUnreadDelta);
+
 			Notify::unreadCounterUpdated();
 		}
 		Notify::historyMuteUpdated(this);
@@ -2755,6 +2876,11 @@ void History::changedInChatListHook(Dialogs::Mode list, bool added) {
 	if (list == Dialogs::Mode::All) {
 		if (const auto delta = historiesUnreadCount() * (added ? 1 : -1)) {
 			App::histories().unreadIncrement(delta, mute());
+
+			const auto entriesDelta = added ? 1 : -1;
+			App::histories().unreadEntriesChanged(
+				entriesDelta,
+				mute() ? entriesDelta : 0);
 		}
 	}
 }
diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h
index 44de34fbc..a4fc1e869 100644
--- a/Telegram/SourceFiles/history/history.h
+++ b/Telegram/SourceFiles/history/history.h
@@ -23,6 +23,7 @@ class HistoryItem;
 class HistoryMessage;
 class HistoryService;
 class HistoryMedia;
+class AuthSession;
 
 namespace Data {
 struct Draft;
@@ -72,10 +73,16 @@ public:
 	BasicAnimation _a_typings;
 
 	int unreadBadge() const;
-	int unreadMutedCount() const;
-	bool unreadOnlyMuted() const;
+	bool unreadBadgeMuted() const;
+	int unreadBadgeIgnoreOne(History *history) const;
+	bool unreadBadgeMutedIgnoreOne(History *history) const;
+	int unreadOnlyMutedBadge() const;
+
 	void unreadIncrement(int count, bool muted);
 	void unreadMuteChanged(int count, bool muted);
+	void unreadEntriesChanged(
+		int withUnreadDelta,
+		int mutedWithUnreadDelta);
 
 	struct SendActionAnimationUpdate {
 		History *history;
@@ -90,11 +97,23 @@ public:
 
 private:
 	void checkSelfDestructItems();
+	int computeUnreadBadge(
+		int full,
+		int muted,
+		int entriesFull,
+		int entriesMuted) const;
+	bool computeUnreadBadgeMuted(
+		int full,
+		int muted,
+		int entriesFull,
+		int entriesMuted) const;
 
 	std::unordered_map<PeerId, std::unique_ptr<History>> _map;
 
 	int _unreadFull = 0;
 	int _unreadMuted = 0;
+	int _unreadEntriesFull = 0;
+	int _unreadEntriesMuted = 0;
 	base::Observable<SendActionAnimationUpdate> _sendActionAnimationUpdated;
 
 	base::Timer _selfDestructTimer;
diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp
index 6bbebf432..2efb04789 100644
--- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp
@@ -695,28 +695,18 @@ void TopBarWidget::createUnreadBadge() {
 void TopBarWidget::updateUnreadBadge() {
 	if (!_unreadBadge) return;
 
-	auto mutedCount = App::histories().unreadMutedCount();
-	auto fullCounter = App::histories().unreadBadge()
-		+ (Global::IncludeMuted() ? 0 : mutedCount);
-
-	// Do not include currently shown chat in the top bar unread counter.
-	if (const auto history = _activeChat.history()) {
-		auto shownUnreadCount = history->unreadCount();
-		fullCounter -= shownUnreadCount;
-		if (history->mute()) {
-			mutedCount -= shownUnreadCount;
+	const auto history = _activeChat.history();
+	const auto active = !App::histories().unreadBadgeMutedIgnoreOne(history);
+	const auto counter = App::histories().unreadBadgeIgnoreOne(history);
+	const auto text = [&] {
+		if (!counter) {
+			return QString();
 		}
-	}
-
-	auto active = (mutedCount < fullCounter);
-	_unreadBadge->setText([&] {
-		if (auto counter = (fullCounter - (Global::IncludeMuted() ? 0 : mutedCount))) {
-			return (counter > 999)
-				? qsl("..%1").arg(counter % 100, 2, 10, QChar('0'))
-				: QString::number(counter);
-		}
-		return QString();
-	}(), active);
+		return (counter > 999)
+			? qsl("..%1").arg(counter % 100, 2, 10, QChar('0'))
+			: QString::number(counter);
+	}();
+	_unreadBadge->setText(text, active);
 }
 
 void TopBarWidget::updateInfoToggleActive() {
diff --git a/Telegram/SourceFiles/messenger.cpp b/Telegram/SourceFiles/messenger.cpp
index 80736d800..85cc09659 100644
--- a/Telegram/SourceFiles/messenger.cpp
+++ b/Telegram/SourceFiles/messenger.cpp
@@ -740,14 +740,23 @@ void Messenger::authSessionDestroy() {
 	authSessionChanged().notify(true);
 }
 
+int Messenger::unreadBadge() const {
+	return _authSession ? App::histories().unreadBadge() : 0;
+}
+
+bool Messenger::unreadBadgeMuted() const {
+	return _authSession ? App::histories().unreadBadgeMuted() : false;
+}
+
 void Messenger::setInternalLinkDomain(const QString &domain) const {
-	// This domain should start with 'http[s]://' and end with '/', like 'https://t.me/'.
-	auto validate = [](auto &domain) {
-		auto prefixes = {
+	// This domain should start with 'http[s]://' and end with '/'.
+	// Like 'https://telegram.me/' or 'https://t.me/'.
+	auto validate = [](const auto &domain) {
+		const auto prefixes = {
 			qstr("https://"),
 			qstr("http://"),
 		};
-		for (auto &prefix : prefixes) {
+		for (const auto &prefix : prefixes) {
 			if (domain.startsWith(prefix, Qt::CaseInsensitive)) {
 				return domain.endsWith('/');
 			}
diff --git a/Telegram/SourceFiles/messenger.h b/Telegram/SourceFiles/messenger.h
index e6e5cc537..c4185b82f 100644
--- a/Telegram/SourceFiles/messenger.h
+++ b/Telegram/SourceFiles/messenger.h
@@ -153,6 +153,8 @@ public:
 	base::Observable<void> &authSessionChanged() {
 		return _authSessionChanged;
 	}
+	int unreadBadge() const;
+	bool unreadBadgeMuted() const;
 	void logOut();
 
 	// Media component.
diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
index 3555bed6f..e4dbc47ef 100644
--- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
+++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
@@ -71,8 +71,11 @@ gboolean _trayIconResized(GtkStatusIcon *status_icon, gint size, gpointer popup_
 #endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
 
 QImage _trayIconImageGen() {
-	int32 counter = App::histories().unreadBadge(), counterSlice = (counter >= 1000) ? (1000 + (counter % 100)) : counter;
-	bool muted = App::histories().unreadOnlyMuted();
+	const auto counter = Messenger::Instance().unreadBadge();
+	const auto muted = Messenger::Instance().unreadBadgeMuted();
+	const auto counterSlice = (counter >= 1000)
+		? (1000 + (counter % 100))
+		: counter;
 	if (_trayIconImage.isNull() || _trayIconImage.width() != _trayIconSize || muted != _trayIconMuted || counterSlice != _trayIconCount) {
 		if (_trayIconImageBack.isNull() || _trayIconImageBack.width() != _trayIconSize) {
 			_trayIconImageBack = Messenger::Instance().logo().scaled(_trayIconSize, _trayIconSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
@@ -109,8 +112,9 @@ QImage _trayIconImageGen() {
 }
 
 QString _trayIconImageFile() {
-	int32 counter = App::histories().unreadBadge(), counterSlice = (counter >= 1000) ? (1000 + (counter % 100)) : counter;
-	bool muted = App::histories().unreadOnlyMuted();
+	const auto counter = Messenger::Instance().unreadBadge();
+	const auto muted = Messenger::Instance().unreadBadgeMuted();
+	const auto counterSlice = (counter >= 1000) ? (1000 + (counter % 100)) : counter;
 
 	QString name = cWorkingDir() + qsl("tdata/ticons/ico%1_%2_%3.png").arg(muted ? "mute" : "").arg(_trayIconSize).arg(counterSlice);
 	QFileInfo info(name);
@@ -329,7 +333,7 @@ void MainWindow::unreadCounterChangedHook() {
 void MainWindow::updateIconCounters() {
 	updateWindowIcon();
 
-	auto counter = App::histories().unreadBadge();
+	const auto counter = Messenger::Instance().unreadBadge();
 
 #if !defined(TDESKTOP_DISABLE_GTK_INTEGRATION) && !defined(TDESKTOP_DISABLE_UNITY_INTEGRATION)
 	if (_psUnityLauncherEntry) {
@@ -368,8 +372,8 @@ void MainWindow::updateIconCounters() {
 			QByteArray path = QFile::encodeName(iconFile.absoluteFilePath());
 			icon = QIcon(path.constData());
 		} else {
-			int32 counter = App::histories().unreadBadge();
-			bool muted = App::histories().unreadOnlyMuted();
+			const auto counter = Messenger::Instance().unreadBadge();
+			const auto muted = Messenger::Instance().unreadBadgeMuted();
 
 			auto &bg = (muted ? st::trayCounterBgMute : st::trayCounterBg);
 			auto &fg = st::trayCounterFg;
diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm
index 961279819..f1b34ee86 100644
--- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm
+++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm
@@ -522,15 +522,18 @@ void MainWindow::unreadCounterChangedHook() {
 }
 
 void MainWindow::updateIconCounters() {
-	auto counter = App::histories().unreadBadge();
+	const auto counter = Messenger::Instance().unreadBadge();
+	const auto muted = Messenger::Instance().unreadBadgeMuted();
 
-	QString cnt = (counter < 1000) ? QString("%1").arg(counter) : QString("..%1").arg(counter % 100, 2, 10, QChar('0'));
-	_private->setWindowBadge(counter ? cnt : QString());
+	const auto string = !counter
+		? QString()
+		: (counter < 1000)
+		? QString("%1").arg(counter)
+		: QString("..%1").arg(counter % 100, 2, 10, QChar('0'));
+	_private->setWindowBadge(string);
 
 	if (trayIcon) {
-		bool muted = App::histories().unreadOnlyMuted();
 		bool dm = objc_darkMode();
-
 		auto &bg = (muted ? st::trayCounterBgMute : st::trayCounterBg);
 		QIcon icon;
 		QImage img(psTrayIcon(dm)), imgsel(psTrayIcon(true));
diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp
index 9e5fc3bb7..18688de49 100644
--- a/Telegram/SourceFiles/platform/win/main_window_win.cpp
+++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp
@@ -727,8 +727,8 @@ void MainWindow::unreadCounterChangedHook() {
 }
 
 void MainWindow::updateIconCounters() {
-	auto counter = App::histories().unreadBadge();
-	auto muted = App::histories().unreadOnlyMuted();
+	const auto counter = Messenger::Instance().unreadBadge();
+	const auto muted = Messenger::Instance().unreadBadgeMuted();
 
 	auto iconSizeSmall = QSize(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
 	auto iconSizeBig = QSize(GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
diff --git a/Telegram/SourceFiles/settings/settings_notifications.cpp b/Telegram/SourceFiles/settings/settings_notifications.cpp
index 641e48542..fdef59180 100644
--- a/Telegram/SourceFiles/settings/settings_notifications.cpp
+++ b/Telegram/SourceFiles/settings/settings_notifications.cpp
@@ -545,7 +545,10 @@ void SetupNotificationsContent(not_null<Ui::VerticalLayout*> container) {
 		Global::SoundNotify());
 	const auto muted = addCheckbox(
 		lng_settings_include_muted,
-		Global::IncludeMuted());
+		Auth().settings().includeMutedCounter());
+	const auto count = addCheckbox(
+		lng_settings_count_unread,
+		Auth().settings().countUnreadMessages());
 
 	const auto nativeNotificationsKey = [&] {
 		if (!Platform::Notifications::Supported()) {
@@ -644,12 +647,21 @@ void SetupNotificationsContent(not_null<Ui::VerticalLayout*> container) {
 	base::ObservableViewer(
 		muted->checkedChanged
 	) | rpl::filter([](bool checked) {
-		return (checked != Global::IncludeMuted());
+		return (checked != Auth().settings().includeMutedCounter());
 	}) | rpl::start_with_next([=](bool checked) {
-		Global::SetIncludeMuted(checked);
+		Auth().settings().setIncludeMutedCounter(checked);
 		changed(Change::IncludeMuted);
 	}, muted->lifetime());
 
+	base::ObservableViewer(
+		count->checkedChanged
+	) | rpl::filter([](bool checked) {
+		return (checked != Auth().settings().countUnreadMessages());
+	}) | rpl::start_with_next([=](bool checked) {
+		Auth().settings().setCountUnreadMessages(checked);
+		changed(Change::CountMessages);
+	}, muted->lifetime());
+
 	base::ObservableViewer(
 		Auth().notifications().settingsChanged()
 	) | rpl::start_with_next([=](Change change) {
diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp
index 06d799a8f..0876bf105 100644
--- a/Telegram/SourceFiles/storage/localstorage.cpp
+++ b/Telegram/SourceFiles/storage/localstorage.cpp
@@ -563,7 +563,7 @@ enum {
 	dbiTryIPv6 = 0x28,
 	dbiSongVolume = 0x29,
 	dbiWindowsNotificationsOld = 0x30,
-	dbiIncludeMuted = 0x31,
+	dbiIncludeMutedOld = 0x31,
 	dbiMegagroupSizeMax = 0x32,
 	dbiDownloadPath = 0x33,
 	dbiAutoDownload = 0x34,
@@ -1099,12 +1099,12 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting
 		Global::SetModerateModeEnabled(enabled == 1);
 	} break;
 
-	case dbiIncludeMuted: {
+	case dbiIncludeMutedOld: {
 		qint32 v;
 		stream >> v;
 		if (!_checkStreamStatus(stream)) return false;
 
-		Global::SetIncludeMuted(v == 1);
+		GetStoredAuthSessionCache().setIncludeMutedCounter(v == 1);
 	} break;
 
 	case dbiShowingSavedGifsOld: {
@@ -2019,7 +2019,6 @@ void _writeUserSettings() {
 	data.stream << quint32(dbiSuggestEmoji) << qint32(Global::SuggestEmoji() ? 1 : 0);
 	data.stream << quint32(dbiSuggestStickersByEmoji) << qint32(Global::SuggestStickersByEmoji() ? 1 : 0);
 	data.stream << quint32(dbiSoundNotify) << qint32(Global::SoundNotify());
-	data.stream << quint32(dbiIncludeMuted) << qint32(Global::IncludeMuted());
 	data.stream << quint32(dbiDesktopNotify) << qint32(Global::DesktopNotify());
 	data.stream << quint32(dbiNotifyView) << qint32(Global::NotifyView());
 	data.stream << quint32(dbiNativeNotifications) << qint32(Global::NativeNotifications());
diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp
index 9ce639e82..e1bc675c2 100644
--- a/Telegram/SourceFiles/window/main_window.cpp
+++ b/Telegram/SourceFiles/window/main_window.cpp
@@ -427,7 +427,9 @@ void MainWindow::updateControlsGeometry() {
 void MainWindow::updateUnreadCounter() {
 	if (!Global::started() || App::quitting()) return;
 
-	auto counter = App::histories().unreadBadge();
+	const auto counter = AuthSession::Exists()
+		? App::histories().unreadBadge()
+		: 0;
 	_titleText = (counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram");
 
 	unreadCounterChangedHook();
diff --git a/Telegram/SourceFiles/window/notifications_manager.cpp b/Telegram/SourceFiles/window/notifications_manager.cpp
index 33526049c..2b2bd98cd 100644
--- a/Telegram/SourceFiles/window/notifications_manager.cpp
+++ b/Telegram/SourceFiles/window/notifications_manager.cpp
@@ -45,7 +45,8 @@ System::System(AuthSession *session) : _authSession(session) {
 			clearAll();
 		} else if (type == ChangeType::ViewParams) {
 			updateAll();
-		} else if (type == ChangeType::IncludeMuted) {
+		} else if (type == ChangeType::IncludeMuted
+			|| type == ChangeType::CountMessages) {
 			Notify::unreadCounterUpdated();
 		}
 	});
diff --git a/Telegram/SourceFiles/window/notifications_manager.h b/Telegram/SourceFiles/window/notifications_manager.h
index 83a8936be..044b71331 100644
--- a/Telegram/SourceFiles/window/notifications_manager.h
+++ b/Telegram/SourceFiles/window/notifications_manager.h
@@ -27,6 +27,7 @@ namespace Notifications {
 enum class ChangeType {
 	SoundEnabled,
 	IncludeMuted,
+	CountMessages,
 	DesktopEnabled,
 	ViewParams,
 	MaxCount,