From 5d1aa1076811bb973680125c4a6cb7346a57846c Mon Sep 17 00:00:00 2001
From: Ilya Fedin <fedin-ilja2010@ya.ru>
Date: Mon, 19 Feb 2024 05:27:09 +0400
Subject: [PATCH 001/108] Remove no longer needed boost-program-options from
 snap

Looks like cppgir has stopped to use it during some of the updates
---
 snap/snapcraft.yaml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index c36f7e512..0996a2797 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -96,7 +96,6 @@ parts:
       - python3
       - libasound2-dev
       - libavif-dev
-      - libboost-program-options1.74-dev
       - libboost-regex1.74-dev
       - libfmt-dev
       - libgirepository1.0-dev

From 778ab70b72ad7eca8a6701f1a8fa00eaab45d88e Mon Sep 17 00:00:00 2001
From: Kolya <142352140+agl-1984@users.noreply.github.com>
Date: Fri, 23 Feb 2024 09:07:08 +0100
Subject: [PATCH 002/108] Fix libvpx build on VS 17.8+

 use with https://github.com/desktop-app/patches/pull/182
---
 Telegram/build/prepare/prepare.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py
index f53417ea3..d60c818e3 100644
--- a/Telegram/build/prepare/prepare.py
+++ b/Telegram/build/prepare/prepare.py
@@ -418,7 +418,7 @@ if customRunCommand:
 stage('patches', """
     git clone https://github.com/desktop-app/patches.git
     cd patches
-    git checkout 94be868240
+    git checkout bed08b53a3
 """)
 
 stage('msys64', """

From ec427ad45df99365aa09f0a7b9412a4bb73ba42f Mon Sep 17 00:00:00 2001
From: Kolya <142352140+agl-1984@users.noreply.github.com>
Date: Fri, 23 Feb 2024 09:12:13 +0100
Subject: [PATCH 003/108] Use TOOLCHAIN variable name

---
 Telegram/build/prepare/prepare.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py
index d60c818e3..fce761f88 100644
--- a/Telegram/build/prepare/prepare.py
+++ b/Telegram/build/prepare/prepare.py
@@ -855,9 +855,9 @@ win:
     SET MSYS2_PATH_TYPE=inherit
 
     if "%X8664%" equ "x64" (
-        SET "TARGET=x86_64-win64-vs17"
+        SET "TOOLCHAIN=x86_64-win64-vs17"
     ) else (
-        SET "TARGET=x86-win32-vs17"
+        SET "TOOLCHAIN=x86-win32-vs17"
     )
 
 depends:patches/build_libvpx_win.sh

From a8b5061003b0c2231bfcebc2ef44d9fd5c3ff3ed Mon Sep 17 00:00:00 2001
From: Ilya Fedin <fedin-ilja2010@ya.ru>
Date: Fri, 1 Mar 2024 10:26:05 +0400
Subject: [PATCH 004/108] Fix a std::clamp assertion

---
 Telegram/SourceFiles/history/view/media/history_view_photo.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp
index e5996d1bf..2532509a7 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp
+++ b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp
@@ -232,7 +232,7 @@ QSize Photo::countCurrentSize(int newWidth) {
 	const auto thumbMaxWidth = qMin(newWidth, st::maxMediaSize);
 	const auto minWidth = std::clamp(
 		_parent->minWidthForMedia(),
-		(_parent->hasBubble()
+		qMin(thumbMaxWidth, _parent->hasBubble()
 			? st::historyPhotoBubbleMinWidth
 			: st::minPhotoSize),
 		thumbMaxWidth);

From da047edbc5ca747ebb9947d8943605f944a390e0 Mon Sep 17 00:00:00 2001
From: GitHub Action <action@github.com>
Date: Fri, 1 Mar 2024 00:25:21 +0000
Subject: [PATCH 005/108] Update User-Agent for DNS to Chrome 122.0.0.0.

---
 .../SourceFiles/mtproto/details/mtproto_domain_resolver.cpp     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp b/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp
index b058b9bb8..ea0a53f30 100644
--- a/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp
+++ b/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp
@@ -65,7 +65,7 @@ QByteArray DnsUserAgent() {
 	static const auto kResult = QByteArray(
 		"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
 		"AppleWebKit/537.36 (KHTML, like Gecko) "
-		"Chrome/121.0.6167.85 Safari/537.36");
+		"Chrome/122.0.0.0 Safari/537.36");
 	return kResult;
 }
 

From 95b4fc021622e75146ced592e87bbd3b2f3ba73e Mon Sep 17 00:00:00 2001
From: xmdn <72883689+xmdnx@users.noreply.github.com>
Date: Sun, 25 Feb 2024 15:00:35 +0300
Subject: [PATCH 006/108] use modern installer style

---
 Telegram/build/setup.iss | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Telegram/build/setup.iss b/Telegram/build/setup.iss
index f5975b097..8b0715581 100644
--- a/Telegram/build/setup.iss
+++ b/Telegram/build/setup.iss
@@ -33,6 +33,7 @@ VersionInfoVersion={#MyAppVersion}.0
 CloseApplications=force
 DisableDirPage=no
 DisableProgramGroupPage=no
+WizardStyle=modern
 
 #if MyBuildTarget == "win64"
   ArchitecturesAllowed="x64 arm64"

From 50f51d074721cda4e1cd8466a31685a6122be4fb Mon Sep 17 00:00:00 2001
From: Kolya <142352140+agl-1984@users.noreply.github.com>
Date: Fri, 1 Mar 2024 20:47:32 +0100
Subject: [PATCH 007/108] update new script location in qt repo

---
 Telegram/build/prepare/prepare.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py
index fce761f88..d0fefbea5 100644
--- a/Telegram/build/prepare/prepare.py
+++ b/Telegram/build/prepare/prepare.py
@@ -1302,7 +1302,7 @@ if buildQt5:
     stage('qt_5_15_12', """
     git clone https://github.com/qt/qt5.git qt_5_15_12
     cd qt_5_15_12
-    perl init-repository --module-subset=qtbase,qtimageformats,qtsvg
+    perl init-repository.pl --module-subset=qtbase,qtimageformats,qtsvg
     git checkout v5.15.12-lts-lgpl
     git submodule update qtbase qtimageformats qtsvg
 depends:patches/qtbase_5.15.12/*.patch

From b040b62b4eefcef173ac78d4ae9dddf3b27e51f9 Mon Sep 17 00:00:00 2001
From: Kolya <142352140+agl-1984@users.noreply.github.com>
Date: Sat, 2 Mar 2024 22:07:16 +0100
Subject: [PATCH 008/108] prepare.py: simplify qt5 clone

---
 Telegram/build/prepare/prepare.py | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py
index d0fefbea5..fceedf2b5 100644
--- a/Telegram/build/prepare/prepare.py
+++ b/Telegram/build/prepare/prepare.py
@@ -1300,11 +1300,9 @@ release:
 
 if buildQt5:
     stage('qt_5_15_12', """
-    git clone https://github.com/qt/qt5.git qt_5_15_12
+    git clone -b v5.15.12-lts-lgpl https://github.com/qt/qt5.git qt_5_15_12
     cd qt_5_15_12
-    perl init-repository.pl --module-subset=qtbase,qtimageformats,qtsvg
-    git checkout v5.15.12-lts-lgpl
-    git submodule update qtbase qtimageformats qtsvg
+    perl init-repository --module-subset=qtbase,qtimageformats,qtsvg
 depends:patches/qtbase_5.15.12/*.patch
     cd qtbase
 win:

From 5b62d97288b03750d5570f9c59ec7c3f88f762b4 Mon Sep 17 00:00:00 2001
From: Ilya Fedin <fedin-ilja2010@ya.ru>
Date: Wed, 6 Mar 2024 20:12:31 +0400
Subject: [PATCH 009/108] Update submodules

---
 .gitmodules                                   |   3 +
 Telegram/CMakeLists.txt                       |   3 -
 .../linux/notifications_manager_linux.cpp     |  72 ++++---
 .../linux/org.freedesktop.portal.Inhibit.xml  | 186 ------------------
 Telegram/ThirdParty/xdg-desktop-portal        |   1 +
 Telegram/lib_base                             |   2 +-
 Telegram/lib_webview                          |   2 +-
 cmake                                         |   2 +-
 8 files changed, 41 insertions(+), 230 deletions(-)
 delete mode 100644 Telegram/SourceFiles/platform/linux/org.freedesktop.portal.Inhibit.xml
 create mode 160000 Telegram/ThirdParty/xdg-desktop-portal

diff --git a/.gitmodules b/.gitmodules
index 101674a90..bf3c35f42 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -100,3 +100,6 @@
 [submodule "Telegram/ThirdParty/libprisma"]
 	path = Telegram/ThirdParty/libprisma
 	url = https://github.com/desktop-app/libprisma.git
+[submodule "Telegram/ThirdParty/xdg-desktop-portal"]
+	path = Telegram/ThirdParty/xdg-desktop-portal
+	url = https://github.com/flatpak/xdg-desktop-portal.git
diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index e1766f5e6..64409ea88 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -1651,9 +1651,6 @@ else()
         desktop-app::external_glibmm
     )
 
-    include(${cmake_helpers_loc}/external/glib/generate_dbus.cmake)
-    generate_dbus(Telegram org.freedesktop.portal. XdpInhibit ${src_loc}/platform/linux/org.freedesktop.portal.Inhibit.xml)
-
     if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION)
         target_link_libraries(Telegram
         PRIVATE
diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp
index c94400ff1..0473edf72 100644
--- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp
+++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp
@@ -78,24 +78,24 @@ std::unique_ptr<base::Platform::DBus::ServiceWatcher> CreateServiceWatcher() {
 			Gio::DBus::BusType::SESSION);
 
 		const auto activatable = [&] {
-			try {
-				return ranges::contains(
-					base::Platform::DBus::ListActivatableNames(connection),
-					kService,
-					&Glib::ustring::raw);
-			} catch (...) {
+			const auto names = base::Platform::DBus::ListActivatableNames(
+				connection->gobj());
+
+			if (!names) {
 				// avoid service restart loop in sandboxed environments
 				return true;
 			}
+
+			return ranges::contains(*names, kService);
 		}();
 
 		return std::make_unique<base::Platform::DBus::ServiceWatcher>(
-			connection,
+			connection->gobj(),
 			kService,
 			[=](
-				const Glib::ustring &service,
-				const Glib::ustring &oldOwner,
-				const Glib::ustring &newOwner) {
+				const std::string &service,
+				const std::string &oldOwner,
+				const std::string &newOwner) {
 				Core::Sandbox::Instance().customEnterFromEventLoop([&] {
 					if (activatable && newOwner.empty()) {
 						Core::App().notifications().clearAll();
@@ -115,27 +115,28 @@ void StartServiceAsync(Fn<void()> callback) {
 		const auto connection = Gio::DBus::Connection::get_sync(
 			Gio::DBus::BusType::SESSION);
 
-		base::Platform::DBus::StartServiceByNameAsync(
-			connection,
+		namespace DBus = base::Platform::DBus;
+		DBus::StartServiceByNameAsync(
+			connection->gobj(),
 			kService,
-			[=](Fn<base::Platform::DBus::StartReply()> result) {
+			[=](Fn<DBus::Result<DBus::StartReply>()> result) {
 				Core::Sandbox::Instance().customEnterFromEventLoop([&] {
 					Noexcept([&] {
-						try {
-							result(); // get the error if any
-						} catch (const Glib::Error &e) {
+						// get the error if any
+						if (const auto ret = result(); !ret) {
 							static const auto NotSupportedErrors = {
 								"org.freedesktop.DBus.Error.ServiceUnknown",
 							};
 
-							const auto errorName =
-								Gio::DBus::ErrorUtils::get_remote_error(e)
-									.raw();
-
-							if (!ranges::contains(
+							if (ranges::none_of(
 									NotSupportedErrors,
-									errorName)) {
-								throw;
+									[&](const auto &error) {
+										return strstr(
+											ret.error()->what(),
+											error);
+									})) {
+								throw std::runtime_error(
+									ret.error()->what());
 							}
 						}
 					});
@@ -156,25 +157,20 @@ bool GetServiceRegistered() {
 		const auto connection = Gio::DBus::Connection::get_sync(
 			Gio::DBus::BusType::SESSION);
 
-		const auto hasOwner = [&] {
-			try {
-				return base::Platform::DBus::NameHasOwner(
-					connection,
-					kService);
-			} catch (...) {
-				return false;
-			}
-		}();
+		const auto hasOwner = base::Platform::DBus::NameHasOwner(
+				connection->gobj(),
+				kService
+		).value_or(false);
 
 		static const auto activatable = [&] {
-			try {
-				return ranges::contains(
-					base::Platform::DBus::ListActivatableNames(connection),
-					kService,
-					&Glib::ustring::raw);
-			} catch (...) {
+			const auto names = base::Platform::DBus::ListActivatableNames(
+				connection->gobj());
+
+			if (!names) {
 				return false;
 			}
+
+			return ranges::contains(*names, kService);
 		}();
 
 		return hasOwner || activatable;
diff --git a/Telegram/SourceFiles/platform/linux/org.freedesktop.portal.Inhibit.xml b/Telegram/SourceFiles/platform/linux/org.freedesktop.portal.Inhibit.xml
deleted file mode 100644
index e91bd22d3..000000000
--- a/Telegram/SourceFiles/platform/linux/org.freedesktop.portal.Inhibit.xml
+++ /dev/null
@@ -1,186 +0,0 @@
-<?xml version="1.0"?>
-<!--
- Copyright (C) 2016 Red Hat, Inc.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library. If not, see <http://www.gnu.org/licenses/>.
-
- Author: Matthias Clasen <mclasen@redhat.com>
--->
-
-<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
-  <!--
-      org.freedesktop.portal.Inhibit:
-      @short_description: Portal for inhibiting session transitions
-
-      This simple interface lets sandboxed applications inhibit the user
-      session from ending, suspending, idling or getting switched away.
-
-      This documentation describes version 3 of this interface.
-  -->
-  <interface name="org.freedesktop.portal.Inhibit">
-    <!--
-        Inhibit:
-        @window: Identifier for the window
-        @flags: Flags identifying what is inhibited
-        @options: Vardict with optional further information
-        @handle: Object path for the #org.freedesktop.portal.Request object representing this call
-
-        Inhibits a session status changes. To remove the inhibition,
-        call org.freedesktop.portal.Request.Close() on the returned
-        handle.
-
-        The flags determine what changes are inhibited:
-        <simplelist>
-          <member>1: Logout</member>
-          <member>2: User Switch</member>
-          <member>4: Suspend</member>
-          <member>8: Idle</member>
-        </simplelist>
-
-        Supported keys in the @options vardict include:
-        <variablelist>
-          <varlistentry>
-            <term>handle_token s</term>
-            <listitem><para>
-              A string that will be used as the last element of the @handle. Must be a valid
-              object path element. See the #org.freedesktop.portal.Request documentation for
-              more information about the @handle.
-            </para></listitem>
-          </varlistentry>
-          <varlistentry>
-            <term>reason s</term>
-            <listitem><para>User-visible reason for the inhibition.</para></listitem>
-          </varlistentry>
-        </variablelist>
-    -->
-    <method name="Inhibit">
-      <arg type="s" name="window" direction="in"/>
-      <arg type="u" name="flags" direction="in"/>
-      <arg type="a{sv}" name="options" direction="in"/>
-      <arg type="o" name="handle" direction="out"/>
-    </method>
-
-    <!--
-        CreateMonitor:
-        @window: the parent window
-        @options: Vardict with optional further information
-        @handle: Object path for the #org.freedesktop.portal.Request object representing this call
-
-        Creates a monitoring session. While this session is
-        active, the caller will receive StateChanged signals
-        with updates on the session state.
-
-        A successfully created session can at any time be closed using
-        org.freedesktop.portal.Session::Close, or may at any time be closed
-        by the portal implementation, which will be signalled via
-        #org.freedesktop.portal.Session::Closed.
-
-        Supported keys in the @options vardict include:
-        <variablelist>
-          <varlistentry>
-            <term>handle_token s</term>
-            <listitem><para>
-              A string that will be used as the last element of the @handle. Must be a valid
-              object path element. See the #org.freedesktop.portal.Request documentation for
-              more information about the @handle.
-            </para></listitem>
-          </varlistentry>
-          <varlistentry>
-            <term>session_handle_token s</term>
-            <listitem><para>
-              A string that will be used as the last element of the session handle. Must be a valid
-              object path element. See the #org.freedesktop.portal.Session documentation for
-              more information about the session handle.
-            </para></listitem>
-          </varlistentry>
-        </variablelist>
-
-        The following results get returned via the #org.freedesktop.portal.Request::Response signal:
-        <variablelist>
-          <varlistentry>
-            <term>session_handle o</term>
-            <listitem><para>
-              The session handle. An object path for the
-              #org.freedesktop.portal.Session object representing the created
-              session.
-            </para></listitem>
-          </varlistentry>
-        </variablelist>
-
-        This method was added in version 2 of this interface.
-    -->
-    <method name="CreateMonitor">
-      <arg type="s" name="window" direction="in"/>
-      <arg type="a{sv}" name="options" direction="in"/>
-      <arg type="o" name="handle" direction="out"/>
-    </method>
-
-    <!--
-        StateChanged:
-        @session_handle: Object path for the #org.freedesktop.portal.Session object
-        @state: Vardict with information about the session state
-
-        The StateChanged signal is sent to active monitoring sessions when
-        the session state changes.
-
-        When the session state changes to 'Query End', clients with active monitoring
-        sessions are expected to respond by calling
-        org.freedesktop.portal.Inhibit.QueryEndResponse() within a second
-        of receiving the StateChanged signal. They may call org.freedesktop.portal.Inhibit.Inhibit()
-        first to inhibit logout, to prevent the session from proceeding to the Ending state.
-
-        The following information may get returned in the @state vardict:
-        <variablelist>
-          <varlistentry>
-            <term>screensaver-active b</term>
-            <listitem><para>
-              Whether the screensaver is active.
-            </para></listitem>
-          </varlistentry>
-           <varlistentry>
-            <term>session-state u</term>
-            <listitem><para>
-              The state of the session. This member is new in version 3.
-            </para>
-            <simplelist>
-              <member>1: Running</member>
-              <member>2: Query End</member>
-              <member>3: Ending</member>
-            </simplelist>
-            </listitem>
-          </varlistentry>
-        </variablelist>
-    -->
-    <signal name="StateChanged">
-      <arg type="o" name="session_handle" direction="out"/>
-      <arg type="a{sv}" name="state" direction="out"/>
-    </signal>
-
-    <!--
-      QueryEndResponse:
-      @session_handle: Object path for the #org.freedesktop.portal.Session object
-
-      Acknowledges that the caller received the #org.freedesktop.portal.Inhibit::StateChanged
-      signal. This method should be called within one second or receiving a StateChanged
-      signal with the 'Query End' state.
-
-      Since version 3.
-    -->
-    <method name="QueryEndResponse">
-      <arg type="o" name="session_handle" direction="in"/>
-    </method>
-
-    <property name="version" type="u" access="read"/>
-  </interface>
-</node>
diff --git a/Telegram/ThirdParty/xdg-desktop-portal b/Telegram/ThirdParty/xdg-desktop-portal
new file mode 160000
index 000000000..fa8d41a2f
--- /dev/null
+++ b/Telegram/ThirdParty/xdg-desktop-portal
@@ -0,0 +1 @@
+Subproject commit fa8d41a2f9a5d30a1e41568b6fb53b046dce14dc
diff --git a/Telegram/lib_base b/Telegram/lib_base
index 888a19075..cee9211bd 160000
--- a/Telegram/lib_base
+++ b/Telegram/lib_base
@@ -1 +1 @@
-Subproject commit 888a19075b569eda3d18a977543320823b984ae0
+Subproject commit cee9211bd58e054f24ad5e7f122037f71a44b237
diff --git a/Telegram/lib_webview b/Telegram/lib_webview
index 4fce8b197..27af88195 160000
--- a/Telegram/lib_webview
+++ b/Telegram/lib_webview
@@ -1 +1 @@
-Subproject commit 4fce8b1971721da739619acf36da0fe79d614a23
+Subproject commit 27af88195bca687e9d2a52b4fcd4e83ef5476be9
diff --git a/cmake b/cmake
index a46279fcf..b699c232d 160000
--- a/cmake
+++ b/cmake
@@ -1 +1 @@
-Subproject commit a46279fcfe69ebcc806bb31679ccece5f7c07508
+Subproject commit b699c232d57d50070a7b1b861809e206624f48d4

From a66b886c513d3554dc035a0aeff7b85da92576d3 Mon Sep 17 00:00:00 2001
From: Ilya Fedin <fedin-ilja2010@ya.ru>
Date: Wed, 6 Mar 2024 20:13:17 +0400
Subject: [PATCH 010/108] Initialize Linux lock screen monitor fully
 asynchronously

---
 .../platform/linux/integration_linux.cpp      | 22 +++++++++++--------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/Telegram/SourceFiles/platform/linux/integration_linux.cpp b/Telegram/SourceFiles/platform/linux/integration_linux.cpp
index 1087e09fb..6613f7382 100644
--- a/Telegram/SourceFiles/platform/linux/integration_linux.cpp
+++ b/Telegram/SourceFiles/platform/linux/integration_linux.cpp
@@ -180,7 +180,7 @@ gi::ref_ptr<Application> MakeApplication() {
 	return result;
 }
 
-class LinuxIntegration final : public Integration {
+class LinuxIntegration final : public Integration, public base::has_weak_ptr {
 public:
 	LinuxIntegration();
 
@@ -200,13 +200,6 @@ private:
 
 LinuxIntegration::LinuxIntegration()
 : _application(MakeApplication())
-, _inhibitProxy(
-	XdpInhibit::InhibitProxy::new_for_bus_sync(
-		Gio::BusType::SESSION_,
-		Gio::DBusProxyFlags::DO_NOT_AUTO_START_AT_CONSTRUCTION_,
-		base::Platform::XDP::kService,
-		base::Platform::XDP::kObjectPath,
-		nullptr))
 , _darkModeWatcher(
 	"org.freedesktop.appearance",
 	"color-scheme",
@@ -230,7 +223,18 @@ LinuxIntegration::LinuxIntegration()
 }
 
 void LinuxIntegration::init() {
-	initInhibit();
+	XdpInhibit::InhibitProxy::new_for_bus(
+		Gio::BusType::SESSION_,
+		Gio::DBusProxyFlags::NONE_,
+		base::Platform::XDP::kService,
+		base::Platform::XDP::kObjectPath,
+		crl::guard(this, [=](GObject::Object, Gio::AsyncResult res) {
+			_inhibitProxy = XdpInhibit::InhibitProxy::new_for_bus_finish(
+				res,
+				nullptr);
+
+			initInhibit();
+		}));
 }
 
 void LinuxIntegration::initInhibit() {

From 1e9b7e2726add6b87bd54d8532783b30cc22c553 Mon Sep 17 00:00:00 2001
From: Ilya Fedin <fedin-ilja2010@ya.ru>
Date: Thu, 7 Mar 2024 01:24:50 +0400
Subject: [PATCH 011/108] Use XDP::kObjectPath for session and request paths

---
 Telegram/SourceFiles/platform/linux/integration_linux.cpp     | 3 ++-
 .../SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp | 4 ++--
 Telegram/SourceFiles/platform/linux/specific_linux.cpp        | 4 ++--
 3 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/Telegram/SourceFiles/platform/linux/integration_linux.cpp b/Telegram/SourceFiles/platform/linux/integration_linux.cpp
index 6613f7382..fef646b20 100644
--- a/Telegram/SourceFiles/platform/linux/integration_linux.cpp
+++ b/Telegram/SourceFiles/platform/linux/integration_linux.cpp
@@ -252,7 +252,8 @@ void LinuxIntegration::initInhibit() {
 	const auto sessionHandleToken = "tdesktop"
 		+ std::to_string(base::RandomValue<uint>());
 
-	const auto sessionHandle = "/org/freedesktop/portal/desktop/session/"
+	const auto sessionHandle = base::Platform::XDP::kObjectPath
+		+ std::string("/session/")
 		+ uniqueName
 		+ '/'
 		+ sessionHandleToken;
diff --git a/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp
index e75bcb421..b01d5abbe 100644
--- a/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp
+++ b/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp
@@ -66,8 +66,8 @@ bool ShowXDPOpenWithDialog(const QString &filepath) {
 		uniqueName.erase(0, 1);
 		uniqueName.replace(uniqueName.find('.'), 1, 1, '_');
 
-		const auto requestPath = Glib::ustring(
-				"/org/freedesktop/portal/desktop/request/")
+		const auto requestPath = base::Platform::XDP::kObjectPath
+			+ Glib::ustring("/request/")
 			+ uniqueName
 			+ '/'
 			+ handleToken;
diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp
index a63c54bc7..c0cbed82e 100644
--- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp
+++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp
@@ -105,8 +105,8 @@ void PortalAutostart(bool enabled, Fn<void(bool)> done) {
 	uniqueName.erase(0, 1);
 	uniqueName.replace(uniqueName.find('.'), 1, 1, '_');
 
-	const auto requestPath = Glib::ustring(
-			"/org/freedesktop/portal/desktop/request/")
+	const auto requestPath = base::Platform::XDP::kObjectPath
+		+ Glib::ustring("/request/")
 		+ uniqueName
 		+ '/'
 		+ handleToken;

From 7b8cdb43c49d6d91e406c459f004fbf9a8658981 Mon Sep 17 00:00:00 2001
From: Ilya Fedin <fedin-ilja2010@ya.ru>
Date: Thu, 7 Mar 2024 02:21:09 +0400
Subject: [PATCH 012/108] Port linux_xdp_open_with_dialog to cppgir

---
 .../linux/linux_xdp_open_with_dialog.cpp      | 205 ++++++++----------
 1 file changed, 95 insertions(+), 110 deletions(-)

diff --git a/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp
index b01d5abbe..bd82cb17f 100644
--- a/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp
+++ b/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp
@@ -13,130 +13,115 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "base/random.h"
 
 #include <fcntl.h>
-#include <glibmm.h>
-#include <giomm.h>
+#include <xdpopenuri/xdpopenuri.hpp>
+#include <xdprequest/xdprequest.hpp>
 
 namespace Platform {
 namespace File {
 namespace internal {
 namespace {
 
-constexpr auto kXDPOpenURIInterface = "org.freedesktop.portal.OpenURI";
-constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties";
+using namespace gi::repository;
 using base::Platform::XdgActivationToken;
 
 } // namespace
 
 bool ShowXDPOpenWithDialog(const QString &filepath) {
-	try {
-		const auto connection = Gio::DBus::Connection::get_sync(
-			Gio::DBus::BusType::SESSION);
+	auto proxy = XdpOpenURI::OpenURIProxy::new_for_bus_sync(
+		Gio::BusType::SESSION_,
+		Gio::DBusProxyFlags::NONE_,
+		base::Platform::XDP::kService,
+		base::Platform::XDP::kObjectPath,
+		nullptr);
 
-		const auto version = connection->call_sync(
-			base::Platform::XDP::kObjectPath,
-			kPropertiesInterface,
-			"Get",
-			Glib::create_variant(std::tuple{
-				Glib::ustring(kXDPOpenURIInterface),
-				Glib::ustring("version"),
-			}),
-			base::Platform::XDP::kService
-		).get_child(0).get_dynamic<Glib::Variant<uint>>().get();
-
-		if (version < 3) {
-			return false;
-		}
-
-		const auto filepathUtf8 = filepath.toUtf8();
-
-		const auto fd = open(
-			filepathUtf8.constData(),
-			O_RDONLY);
-
-		if (fd == -1) {
-			return false;
-		}
-
-		const auto fdGuard = gsl::finally([&] { ::close(fd); });
-
-		const auto handleToken = Glib::ustring("tdesktop")
-			+ std::to_string(base::RandomValue<uint>());
-
-		auto uniqueName = connection->get_unique_name();
-		uniqueName.erase(0, 1);
-		uniqueName.replace(uniqueName.find('.'), 1, 1, '_');
-
-		const auto requestPath = base::Platform::XDP::kObjectPath
-			+ Glib::ustring("/request/")
-			+ uniqueName
-			+ '/'
-			+ handleToken;
-
-		const auto loop = Glib::MainLoop::create();
-
-		const auto signalId = connection->signal_subscribe(
-			[&](
-				const Glib::RefPtr<Gio::DBus::Connection> &connection,
-				const Glib::ustring &sender_name,
-				const Glib::ustring &object_path,
-				const Glib::ustring &interface_name,
-				const Glib::ustring &signal_name,
-				const Glib::VariantContainerBase &parameters) {
-				loop->quit();
-			},
-			base::Platform::XDP::kService,
-			base::Platform::XDP::kRequestInterface,
-			"Response",
-			requestPath);
-
-		const auto signalGuard = gsl::finally([&] {
-			if (signalId != 0) {
-				connection->signal_unsubscribe(signalId);
-			}
-		});
-
-		auto outFdList = Glib::RefPtr<Gio::UnixFDList>();
-
-		connection->call_sync(
-			base::Platform::XDP::kObjectPath,
-			kXDPOpenURIInterface,
-			"OpenFile",
-			Glib::create_variant(std::tuple{
-				base::Platform::XDP::ParentWindowID(),
-				Glib::DBusHandle(),
-				std::map<Glib::ustring, Glib::VariantBase>{
-					{
-						"handle_token",
-						Glib::create_variant(handleToken)
-					},
-					{
-						"activation_token",
-						Glib::create_variant(
-							Glib::ustring(XdgActivationToken().toStdString()))
-					},
-					{
-						"ask",
-						Glib::create_variant(true)
-					},
-				},
-			}),
-			Gio::UnixFDList::create(std::vector<int>{ fd }),
-			outFdList,
-			base::Platform::XDP::kService);
-
-		if (signalId != 0) {
-			QWidget window;
-			window.setAttribute(Qt::WA_DontShowOnScreen);
-			window.setWindowModality(Qt::ApplicationModal);
-			window.show();
-			loop->run();
-		}
-
-		return true;
-	} catch (...) {
+	if (!proxy) {
+		return false;
 	}
 
-	return false;
+	auto interface = XdpOpenURI::OpenURI(proxy);
+	if (interface.get_version() < 3) {
+		return false;
+	}
+
+	const auto fd = open(
+		QFile::encodeName(filepath).constData(),
+		O_RDONLY);
+
+	if (fd == -1) {
+		return false;
+	}
+
+	const auto fdGuard = gsl::finally([&] { close(fd); });
+
+	const auto handleToken = "tdesktop"
+		+ std::to_string(base::RandomValue<uint>());
+
+	std::string uniqueName = proxy.get_connection().get_unique_name();
+	uniqueName.erase(0, 1);
+	uniqueName.replace(uniqueName.find('.'), 1, 1, '_');
+
+	auto request = XdpRequest::Request(
+		XdpRequest::RequestProxy::new_sync(
+			proxy.get_connection(),
+			Gio::DBusProxyFlags::NONE_,
+			base::Platform::XDP::kService,
+			base::Platform::XDP::kObjectPath
+				+ std::string("/request/")
+				+ uniqueName
+				+ '/'
+				+ handleToken,
+			nullptr,
+			nullptr));
+
+	if (!request) {
+		return false;
+	}
+
+	auto loop = GLib::MainLoop::new_();
+
+	const auto signalId = request.signal_response().connect([=](
+			XdpRequest::Request,
+			guint,
+			GLib::Variant) mutable {
+		loop.quit();
+	});
+
+	const auto signalGuard = gsl::finally([&] {
+		request.disconnect(signalId);
+	});
+
+	auto result = interface.call_open_file_sync(
+		std::string(base::Platform::XDP::ParentWindowID()),
+		GLib::Variant::new_handle(0),
+		GLib::Variant::new_array({
+			GLib::Variant::new_dict_entry(
+				GLib::Variant::new_string("handle_token"),
+				GLib::Variant::new_variant(
+					GLib::Variant::new_string(handleToken))),
+			GLib::Variant::new_dict_entry(
+				GLib::Variant::new_string("activation_token"),
+				GLib::Variant::new_variant(
+					GLib::Variant::new_string(
+						XdgActivationToken().toStdString()))),
+			GLib::Variant::new_dict_entry(
+				GLib::Variant::new_string("ask"),
+				GLib::Variant::new_variant(
+					GLib::Variant::new_boolean(true))),
+		}),
+		Gio::UnixFDList::new_from_array((std::array{ fd }).data(), 1),
+		nullptr);
+
+	if (!result) {
+		return false;
+	}
+
+	QWidget window;
+	window.setAttribute(Qt::WA_DontShowOnScreen);
+	window.setWindowModality(Qt::ApplicationModal);
+	window.show();
+	loop.run();
+
+	return true;
 }
 
 } // namespace internal

From 41481129f7775faeb51f889937d4d6de0d18ddd1 Mon Sep 17 00:00:00 2001
From: Ilya Fedin <fedin-ilja2010@ya.ru>
Date: Thu, 7 Mar 2024 02:57:38 +0400
Subject: [PATCH 013/108] Port main_window_linux to cppgir

---
 .../platform/linux/main_window_linux.cpp      | 61 +++++++++----------
 1 file changed, 29 insertions(+), 32 deletions(-)

diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
index f4c400f32..e6a3b68a6 100644
--- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
+++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
@@ -43,8 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include <QtWidgets/QLineEdit>
 #include <QtWidgets/QTextEdit>
 
-#include <glibmm.h>
-#include <giomm.h>
+#include <gio/gio.hpp>
 
 namespace Platform {
 namespace {
@@ -236,6 +235,8 @@ void MainWindow::updateUnityCounter() {
 #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
 	qApp->setBadgeNumber(Core::App().unreadBadge());
 #else // Qt >= 6.6.0
+	using namespace gi::repository;
+
 	static const auto djbStringHash = [](const std::string &string) {
 		uint hash = 5381;
 		for (const auto &curChar : string) {
@@ -244,40 +245,36 @@ void MainWindow::updateUnityCounter() {
 		return hash;
 	};
 
-	const auto launcherUrl = Glib::ustring(
-		"application://"
-			+ QGuiApplication::desktopFileName().toStdString()
-			+ ".desktop");
+	const auto launcherUrl = "application://"
+		+ QGuiApplication::desktopFileName().toStdString()
+		+ ".desktop";
+
 	const auto counterSlice = std::min(Core::App().unreadBadge(), 9999);
-	std::map<Glib::ustring, Glib::VariantBase> dbusUnityProperties;
 
-	if (counterSlice > 0) {
-		// According to the spec, it should be of 'x' D-Bus signature,
-		// which corresponds to signed 64-bit integer
-		// https://wiki.ubuntu.com/Unity/LauncherAPI#Low_level_DBus_API:_com.canonical.Unity.LauncherEntry
-		dbusUnityProperties["count"] = Glib::create_variant(
-			int64(counterSlice));
-		dbusUnityProperties["count-visible"] = Glib::create_variant(true);
-	} else {
-		dbusUnityProperties["count-visible"] = Glib::create_variant(false);
+	auto connection = Gio::bus_get_sync(Gio::BusType::SESSION_, nullptr);
+	if (!connection) {
+		return;
 	}
 
-	try {
-		const auto connection = Gio::DBus::Connection::get_sync(
-			Gio::DBus::BusType::SESSION);
-
-		connection->emit_signal(
-			"/com/canonical/unity/launcherentry/"
-				+ std::to_string(djbStringHash(launcherUrl)),
-			"com.canonical.Unity.LauncherEntry",
-			"Update",
-			{},
-			Glib::create_variant(std::tuple{
-				launcherUrl,
-				dbusUnityProperties,
-			}));
-	} catch (...) {
-	}
+	connection.emit_signal(
+		{},
+		"/com/canonical/unity/launcherentry/"
+			+ std::to_string(djbStringHash(launcherUrl)),
+		"com.canonical.Unity.LauncherEntry",
+		"Update",
+		GLib::Variant::new_tuple({
+			GLib::Variant::new_string(launcherUrl),
+			GLib::Variant::new_array({
+				GLib::Variant::new_dict_entry(
+					GLib::Variant::new_string("count"),
+					GLib::Variant::new_variant(
+						GLib::Variant::new_int64(counterSlice))),
+				GLib::Variant::new_dict_entry(
+					GLib::Variant::new_string("count-visible"),
+					GLib::Variant::new_variant(
+						GLib::Variant::new_boolean(counterSlice))),
+			}),
+		}));
 #endif // Qt < 6.6.0
 }
 

From 5971aefb8384d8743d616699c4f6def1489f6949 Mon Sep 17 00:00:00 2001
From: mrbesen <y.g.2@gmx.de>
Date: Tue, 28 Nov 2023 19:42:17 +0100
Subject: [PATCH 014/108] add bash shebang

---
 Telegram/build/prepare/linux.sh | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Telegram/build/prepare/linux.sh b/Telegram/build/prepare/linux.sh
index d3ee5b5be..e4d2f920e 100755
--- a/Telegram/build/prepare/linux.sh
+++ b/Telegram/build/prepare/linux.sh
@@ -1,3 +1,5 @@
+#!/bin/bash
+
 set -e
 FullExecPath=$PWD
 pushd `dirname $0` > /dev/null

From c26982be3e31c99a6125b0f10e49e91038494322 Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Sun, 18 Feb 2024 20:48:05 +0300
Subject: [PATCH 015/108] Added support for AVIF, HEIF and JPEG XL on macOS.

---
 .github/workflows/mac.yml         |   2 +-
 Telegram/build/prepare/prepare.py | 234 +++++++++++++++++++++---------
 docs/building-mac.md              |   2 +-
 3 files changed, 169 insertions(+), 69 deletions(-)

diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml
index 8e99d7162..68bab389c 100644
--- a/.github/workflows/mac.yml
+++ b/.github/workflows/mac.yml
@@ -64,7 +64,7 @@ jobs:
       - name: First set up.
         run: |
           sudo chown -R `whoami`:admin /usr/local/share
-          brew install automake ninja pkg-config
+          brew install automake ninja pkg-config nasm meson
 
           # Disable spotlight.
           sudo mdutil -a -i off
diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py
index fceedf2b5..30d5084f4 100644
--- a/Telegram/build/prepare/prepare.py
+++ b/Telegram/build/prepare/prepare.py
@@ -202,6 +202,11 @@ def removeDir(folder):
         return 'if exist ' + folder + ' rmdir /Q /S ' + folder + '\nif exist ' + folder + ' exit /b 1'
     return 'rm -rf ' + folder
 
+def setVar(key, value):
+    if win:
+        return 'SET ' + key + '="' + value + '"';
+    return key + '="' + value + '"';
+
 def filterByPlatform(commands):
     commands = commands.split('\n')
     result = ''
@@ -690,10 +695,9 @@ mac:
 """)
 
 stage('dav1d', """
-win:
     git clone -b 1.2.1 --depth 1 https://code.videolan.org/videolan/dav1d.git
     cd dav1d
-
+win:
     if "%X8664%" equ "x64" (
         SET "TARGET=x86_64"
     ) else (
@@ -709,7 +713,7 @@ win:
     echo system = 'windows' >> %FILE%
     echo cpu_family = '%TARGET%' >> %FILE%
     echo cpu = '%TARGET%' >> %FILE%
-    echo endian = 'little'>> %FILE%
+    echo endian = 'little' >> %FILE%
 
 depends:python/Scripts/activate.bat
     %THIRDPARTY_DIR%\\python\\Scripts\\activate.bat
@@ -723,12 +727,55 @@ release:
 win:
     copy %LIBS_DIR%\\local\\lib\\libdav1d.a %LIBS_DIR%\\local\\lib\\dav1d.lib
     deactivate
+mac:
+    buildOneArch() {
+        arch=$1
+        folder=`pwd`/$2
+
+        TARGET="\'${arch}\'"
+        MIN="\'${MIN_VER}\'"
+        FILE=cross-file.txt
+        echo "[binaries]" > $FILE
+        echo "c = ['clang', '-arch', ${TARGET}]" >> $FILE
+        echo "cpp = ['clang++', '-arch', ${TARGET}]" >> $FILE
+        echo "ar = 'ar'" >> $FILE
+        echo "strip = 'strip'" >> $FILE
+        echo "[built-in options]" >> $FILE
+        echo "c_args = [${MIN}]" >> $FILE
+        echo "cpp_args = [${MIN}]" >> $FILE
+        echo "c_link_args = [${MIN}]" >> $FILE
+        echo "cpp_link_args = [${MIN}]" >> $FILE
+        echo "[host_machine]" >> $FILE
+        echo "system = 'darwin'" >> $FILE
+        echo "subsystem = 'macos'" >> $FILE
+        echo "cpu_family = ${TARGET}" >> $FILE
+        echo "cpu = ${TARGET}" >> $FILE
+        echo "endian = 'little'" >> $FILE
+
+        meson setup \\
+            --cross-file $FILE \\
+            --prefix ${USED_PREFIX} \\
+            --default-library=static \\
+            --buildtype=minsize \\
+            -Denable_tools=false \\
+            -Denable_tests=false \\
+            ${folder}
+        meson compile -C ${folder}
+        meson install -C ${folder}
+
+        mv ${USED_PREFIX}/lib/libdav1d.a ${folder}/libdav1d.a
+    }
+
+    buildOneArch arm64 build.arm64
+    buildOneArch x86_64 build
+
+    lipo -create build.arm64/libdav1d.a build/libdav1d.a -output ${USED_PREFIX}/lib/libdav1d.a
 """)
 
 stage('libavif', """
-win:
     git clone -b v0.11.1 --depth 1 https://github.com/AOMediaCodec/libavif.git
     cd libavif
+win:
     cmake . ^
         -A %WIN32X64% ^
         -DCMAKE_INSTALL_PREFIX=%LIBS_DIR%/local ^
@@ -743,12 +790,22 @@ win:
 release:
     cmake --build . --config Release
     cmake --install . --config Release
+mac:
+    cmake . \\
+        -D CMAKE_OSX_ARCHITECTURES="x86_64;arm64" \\
+        -D CMAKE_OSX_DEPLOYMENT_TARGET:STRING=$MACOSX_DEPLOYMENT_TARGET \\
+        -D CMAKE_INSTALL_PREFIX:STRING=$USED_PREFIX \\
+        -D BUILD_SHARED_LIBS=OFF \\
+        -D AVIF_ENABLE_WERROR=OFF \\
+        -D AVIF_CODEC_DAV1D=ON
+    cmake --build . --config MinSizeRel $MAKE_THREADS_CNT
+    cmake --install . --config MinSizeRel
 """)
 
 stage('libde265', """
-win:
     git clone --depth 1 -b v1.0.12 https://github.com/strukturag/libde265.git
     cd libde265
+win:
     cmake . ^
         -A %WIN32X64% ^
         -DCMAKE_INSTALL_PREFIX=%LIBS_DIR%/local ^
@@ -768,12 +825,63 @@ win:
 release:
     cmake --build . --config Release
     cmake --install . --config Release
+mac:
+    cmake . \\
+        -D CMAKE_OSX_ARCHITECTURES="x86_64;arm64" \\
+        -D CMAKE_OSX_DEPLOYMENT_TARGET:STRING=$MACOSX_DEPLOYMENT_TARGET \\
+        -D CMAKE_INSTALL_PREFIX:STRING=$USED_PREFIX \\
+        -D DISABLE_SSE=ON \\
+        -D BUILD_SHARED_LIBS=OFF \\
+        -D ENABLE_DECODER=ON \\
+        -D ENABLE_ENCODER=OFF
+    cmake --build . --config MinSizeRel $MAKE_THREADS_CNT
+    cmake --install . --config MinSizeRel
+""")
+
+stage('libwebp', """
+    git clone -b v1.3.2 https://github.com/webmproject/libwebp.git
+    cd libwebp
+win:
+    nmake /f Makefile.vc CFG=debug-static OBJDIR=out RTLIBCFG=static all
+    nmake /f Makefile.vc CFG=release-static OBJDIR=out RTLIBCFG=static all
+    copy out\\release-static\\$X8664\\lib\\libwebp.lib out\\release-static\\$X8664\\lib\\webp.lib
+    copy out\\release-static\\$X8664\\lib\\libwebpdemux.lib out\\release-static\\$X8664\\lib\\webpdemux.lib
+    copy out\\release-static\\$X8664\\lib\\libwebpmux.lib out\\release-static\\$X8664\\lib\\webpmux.lib
+mac:
+    buildOneArch() {
+        arch=$1
+        folder=$2
+
+        CFLAGS=$UNGUARDED cmake -B $folder -G Ninja . \\
+            -D CMAKE_BUILD_TYPE=Release \\
+            -D CMAKE_INSTALL_PREFIX=$USED_PREFIX \\
+            -D CMAKE_OSX_DEPLOYMENT_TARGET:STRING=$MACOSX_DEPLOYMENT_TARGET \\
+            -D CMAKE_OSX_ARCHITECTURES=$arch \\
+            -D WEBP_BUILD_ANIM_UTILS=OFF \\
+            -D WEBP_BUILD_CWEBP=OFF \\
+            -D WEBP_BUILD_DWEBP=OFF \\
+            -D WEBP_BUILD_GIF2WEBP=OFF \\
+            -D WEBP_BUILD_IMG2WEBP=OFF \\
+            -D WEBP_BUILD_VWEBP=OFF \\
+            -D WEBP_BUILD_WEBPMUX=OFF \\
+            -D WEBP_BUILD_WEBPINFO=OFF \\
+            -D WEBP_BUILD_EXTRAS=OFF
+        cmake --build $folder $MAKE_THREADS_CNT
+    }
+    buildOneArch arm64 build.arm64
+    buildOneArch x86_64 build
+
+    lipo -create build.arm64/libsharpyuv.a build/libsharpyuv.a -output build/libsharpyuv.a
+    lipo -create build.arm64/libwebp.a build/libwebp.a -output build/libwebp.a
+    lipo -create build.arm64/libwebpdemux.a build/libwebpdemux.a -output build/libwebpdemux.a
+    lipo -create build.arm64/libwebpmux.a build/libwebpmux.a -output build/libwebpmux.a
+    cmake --install build
 """)
 
 stage('libheif', """
-win:
     git clone --depth 1 -b v1.16.2 https://github.com/strukturag/libheif.git
     cd libheif
+win:
     %THIRDPARTY_DIR%\\msys64\\usr\\bin\\sed.exe -i 's/LIBHEIF_EXPORTS/LIBDE265_STATIC_BUILD/g' libheif/CMakeLists.txt
     %THIRDPARTY_DIR%\\msys64\\usr\\bin\\sed.exe -i 's/HAVE_VISIBILITY/LIBHEIF_STATIC_BUILD/g' libheif/CMakeLists.txt
     cmake . ^
@@ -797,12 +905,55 @@ win:
 release:
     cmake --build . --config Release
     cmake --install . --config Release
+mac:
+    cmake . \\
+        -D CMAKE_OSX_ARCHITECTURES="x86_64;arm64" \\
+        -D CMAKE_OSX_DEPLOYMENT_TARGET:STRING=$MACOSX_DEPLOYMENT_TARGET \\
+        -D CMAKE_INSTALL_PREFIX:STRING=$USED_PREFIX \\
+        -D BUILD_SHARED_LIBS=OFF \\
+        -D ENABLE_PLUGIN_LOADING=OFF \\
+        -D WITH_AOM_ENCODER=OFF \\
+        -D WITH_AOM_DECODER=OFF \\
+        -D WITH_X265=OFF \\
+        -D WITH_SvtEnc=OFF \\
+        -D WITH_RAV1E=OFF \\
+        -D WITH_DAV1D=ON \\
+        -D WITH_LIBDE265=ON \\
+        -D LIBDE265_INCLUDE_DIR=$USED_PREFIX/include/ \\
+        -D LIBDE265_LIBRARY=$USED_PREFIX/lib/libde265.a \\
+        -D LIBSHARPYUV_INCLUDE_DIR=$USED_PREFIX/include/webp/ \\
+        -D LIBSHARPYUV_LIBRARY=$USED_PREFIX/lib/libsharpyuv.a \\
+        -D WITH_EXAMPLES=OFF
+    cmake --build . --config MinSizeRel $MAKE_THREADS_CNT
+    cmake --install . --config MinSizeRel
 """)
 
 stage('libjxl', """
-win:
     git clone -b v0.8.2 --depth 1 --recursive --shallow-submodules https://github.com/libjxl/libjxl.git
     cd libjxl
+""" + setVar("cmake_defines", ' '.join("""
+    -DBUILD_SHARED_LIBS=OFF
+    -DBUILD_TESTING=OFF
+    -DJPEGXL_ENABLE_FUZZERS=OFF
+    -DJPEGXL_ENABLE_DEVTOOLS=OFF
+    -DJPEGXL_ENABLE_TOOLS=OFF
+    -DJPEGXL_ENABLE_DOXYGEN=OFF
+    -DJPEGXL_ENABLE_MANPAGES=OFF
+    -DJPEGXL_ENABLE_EXAMPLES=OFF
+    -DJPEGXL_ENABLE_JNI=OFF
+    -DJPEGXL_ENABLE_JPEGLI_LIBJPEG=OFF
+    -DJPEGXL_ENABLE_SJPEG=OFF
+    -DJPEGXL_ENABLE_OPENEXR=OFF
+    -DJPEGXL_ENABLE_SKCMS=ON
+    -DJPEGXL_BUNDLE_SKCMS=ON
+    -DJPEGXL_ENABLE_VIEWERS=OFF
+    -DJPEGXL_ENABLE_TCMALLOC=OFF
+    -DJPEGXL_ENABLE_PLUGINS=OFF
+    -DJPEGXL_ENABLE_COVERAGE=OFF
+    -DJPEGXL_ENABLE_PROFILER=OFF
+    -DJPEGXL_WARNINGS_AS_ERRORS=OFF
+""".replace('\n', '').split())) + """
+win:
     cmake . ^
         -A %WIN32X64% ^
         -DCMAKE_INSTALL_PREFIX=%LIBS_DIR%/local ^
@@ -813,31 +964,20 @@ win:
         -DCMAKE_CXX_FLAGS_DEBUG="/MTd /Zi /Ob0 /Od /RTC1" ^
         -DCMAKE_C_FLAGS_RELEASE="/MT /O2 /Ob2 /DNDEBUG" ^
         -DCMAKE_CXX_FLAGS_RELEASE="/MT /O2 /Ob2 /DNDEBUG" ^
-        -DBUILD_SHARED_LIBS=OFF ^
-        -DBUILD_TESTING=OFF ^
-        -DJPEGXL_ENABLE_FUZZERS=OFF ^
-        -DJPEGXL_ENABLE_DEVTOOLS=OFF ^
-        -DJPEGXL_ENABLE_TOOLS=OFF ^
-        -DJPEGXL_ENABLE_DOXYGEN=OFF ^
-        -DJPEGXL_ENABLE_MANPAGES=OFF ^
-        -DJPEGXL_ENABLE_EXAMPLES=OFF ^
-        -DJPEGXL_ENABLE_JNI=OFF ^
-        -DJPEGXL_ENABLE_JPEGLI_LIBJPEG=OFF ^
-        -DJPEGXL_ENABLE_SJPEG=OFF ^
-        -DJPEGXL_ENABLE_OPENEXR=OFF ^
-        -DJPEGXL_ENABLE_SKCMS=ON ^
-        -DJPEGXL_BUNDLE_SKCMS=ON ^
-        -DJPEGXL_ENABLE_VIEWERS=OFF ^
-        -DJPEGXL_ENABLE_TCMALLOC=OFF ^
-        -DJPEGXL_ENABLE_PLUGINS=OFF ^
-        -DJPEGXL_ENABLE_COVERAGE=OFF ^
-        -DJPEGXL_ENABLE_PROFILER=OFF ^
-        -DJPEGXL_WARNINGS_AS_ERRORS=OFF
+        %cmake_defines%
     cmake --build . --config Debug
     cmake --install . --config Debug
 release:
     cmake --build . --config Release
     cmake --install . --config Release
+mac:
+    cmake . \\
+        -D CMAKE_OSX_ARCHITECTURES="x86_64;arm64" \\
+        -D CMAKE_OSX_DEPLOYMENT_TARGET:STRING=$MACOSX_DEPLOYMENT_TARGET \\
+        -D CMAKE_INSTALL_PREFIX:STRING=$USED_PREFIX \\
+        ${cmake_defines}
+    cmake --build . --config MinSizeRel $MAKE_THREADS_CNT
+    cmake --install . --config MinSizeRel
 """)
 
 stage('libvpx', """
@@ -906,46 +1046,6 @@ depends:yasm/yasm
     make install
 """)
 
-stage('libwebp', """
-    git clone -b v1.3.2 https://github.com/webmproject/libwebp.git
-    cd libwebp
-win:
-    nmake /f Makefile.vc CFG=debug-static OBJDIR=out RTLIBCFG=static all
-    nmake /f Makefile.vc CFG=release-static OBJDIR=out RTLIBCFG=static all
-    copy out\\release-static\\$X8664\\lib\\libwebp.lib out\\release-static\\$X8664\\lib\\webp.lib
-    copy out\\release-static\\$X8664\\lib\\libwebpdemux.lib out\\release-static\\$X8664\\lib\\webpdemux.lib
-    copy out\\release-static\\$X8664\\lib\\libwebpmux.lib out\\release-static\\$X8664\\lib\\webpmux.lib
-mac:
-    buildOneArch() {
-        arch=$1
-        folder=$2
-
-        CFLAGS=$UNGUARDED cmake -B $folder -G Ninja . \\
-            -D CMAKE_BUILD_TYPE=Release \\
-            -D CMAKE_INSTALL_PREFIX=$USED_PREFIX \\
-            -D CMAKE_OSX_DEPLOYMENT_TARGET:STRING=$MACOSX_DEPLOYMENT_TARGET \\
-            -D CMAKE_OSX_ARCHITECTURES=$arch \\
-            -D WEBP_BUILD_ANIM_UTILS=OFF \\
-            -D WEBP_BUILD_CWEBP=OFF \\
-            -D WEBP_BUILD_DWEBP=OFF \\
-            -D WEBP_BUILD_GIF2WEBP=OFF \\
-            -D WEBP_BUILD_IMG2WEBP=OFF \\
-            -D WEBP_BUILD_VWEBP=OFF \\
-            -D WEBP_BUILD_WEBPMUX=OFF \\
-            -D WEBP_BUILD_WEBPINFO=OFF \\
-            -D WEBP_BUILD_EXTRAS=OFF
-        cmake --build $folder $MAKE_THREADS_CNT
-    }
-    buildOneArch arm64 build.arm64
-    buildOneArch x86_64 build
-
-    lipo -create build.arm64/libsharpyuv.a build/libsharpyuv.a -output build/libsharpyuv.a
-    lipo -create build.arm64/libwebp.a build/libwebp.a -output build/libwebp.a
-    lipo -create build.arm64/libwebpdemux.a build/libwebpdemux.a -output build/libwebpdemux.a
-    lipo -create build.arm64/libwebpmux.a build/libwebpmux.a -output build/libwebpmux.a
-    cmake --install build
-""")
-
 stage('nv-codec-headers', """
 win:
     git clone https://github.com/FFmpeg/nv-codec-headers.git
diff --git a/docs/building-mac.md b/docs/building-mac.md
index d357c66c1..11435a8dd 100644
--- a/docs/building-mac.md
+++ b/docs/building-mac.md
@@ -13,7 +13,7 @@ You will require **api_id** and **api_hash** to access the Telegram API servers.
 Go to ***BuildPath*** and run
 
     ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
-    brew install git automake cmake wget pkg-config gnu-tar ninja
+    brew install git automake cmake wget pkg-config gnu-tar ninja nasm meson
 
     sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
 

From de1bd6ef28ed625bcadbc4ef089dc3342a768fc0 Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Mon, 19 Feb 2024 18:55:56 +0300
Subject: [PATCH 016/108] Slightly simplified creation of universal variables
 in prepare.py.

---
 Telegram/build/prepare/prepare.py | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py
index 30d5084f4..c3a566796 100644
--- a/Telegram/build/prepare/prepare.py
+++ b/Telegram/build/prepare/prepare.py
@@ -202,10 +202,11 @@ def removeDir(folder):
         return 'if exist ' + folder + ' rmdir /Q /S ' + folder + '\nif exist ' + folder + ' exit /b 1'
     return 'rm -rf ' + folder
 
-def setVar(key, value):
+def setVar(key, multilineValue):
+    singlelineValue = ' '.join(multilineValue.replace('\n', '').split());
     if win:
-        return 'SET ' + key + '="' + value + '"';
-    return key + '="' + value + '"';
+        return 'SET ' + key + '="' + singlelineValue + '"';
+    return key + '="' + singlelineValue + '"';
 
 def filterByPlatform(commands):
     commands = commands.split('\n')
@@ -931,7 +932,7 @@ mac:
 stage('libjxl', """
     git clone -b v0.8.2 --depth 1 --recursive --shallow-submodules https://github.com/libjxl/libjxl.git
     cd libjxl
-""" + setVar("cmake_defines", ' '.join("""
+""" + setVar("cmake_defines", """
     -DBUILD_SHARED_LIBS=OFF
     -DBUILD_TESTING=OFF
     -DJPEGXL_ENABLE_FUZZERS=OFF
@@ -952,7 +953,7 @@ stage('libjxl', """
     -DJPEGXL_ENABLE_COVERAGE=OFF
     -DJPEGXL_ENABLE_PROFILER=OFF
     -DJPEGXL_WARNINGS_AS_ERRORS=OFF
-""".replace('\n', '').split())) + """
+""") + """
 win:
     cmake . ^
         -A %WIN32X64% ^

From 52c779bffa8dde3c5c09826add2607328fae0924 Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Wed, 21 Feb 2024 20:47:11 +0300
Subject: [PATCH 017/108] Added support of inline markup reply to HTML export.

---
 Telegram/Resources/export_html/css/style.css  | 23 +++++++++
 Telegram/Resources/export_html/js/script.js   |  6 +++
 .../export/data/export_data_types.cpp         | 24 +++++++++
 .../export/data/export_data_types.h           |  2 +
 .../export/output/export_output_html.cpp      | 49 +++++++++++++++++++
 .../export/output/export_output_json.cpp      | 26 +---------
 6 files changed, 106 insertions(+), 24 deletions(-)

diff --git a/Telegram/Resources/export_html/css/style.css b/Telegram/Resources/export_html/css/style.css
index 79b680cc2..102f5f3a5 100644
--- a/Telegram/Resources/export_html/css/style.css
+++ b/Telegram/Resources/export_html/css/style.css
@@ -559,3 +559,26 @@ div.toast_shown {
     opacity: 0;
     user-select: none;
 }
+
+.bot_buttons_table {
+    border-spacing: 0px 2px;
+    width: 100%;
+}
+.bot_button {
+    border-radius: 8px;
+    text-align: center;
+    vertical-align: middle;
+    background-color: #168acd40;
+}
+.bot_button_row {
+    display: table;
+    table-layout: fixed;
+    padding: 0px;
+    width:100%;
+}
+.bot_button_row div {
+    display: table-cell;
+}
+.bot_button_column_separator {
+    width: 2px
+}
diff --git a/Telegram/Resources/export_html/js/script.js b/Telegram/Resources/export_html/js/script.js
index 8d25f5302..284232202 100644
--- a/Telegram/Resources/export_html/js/script.js
+++ b/Telegram/Resources/export_html/js/script.js
@@ -62,6 +62,12 @@ function ShowNotAvailableEmoji() {
     return false;
 }
 
+function ShowTextCopied(content) {
+    navigator.clipboard.writeText(content);
+    ShowToast("Text copied to clipboard.");
+    return false;
+}
+
 function ShowSpoiler(target) {
     if (target.classList.contains("hidden")) {
         target.classList.toggle("hidden");
diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp
index f7928f006..1f56f9ec1 100644
--- a/Telegram/SourceFiles/export/data/export_data_types.cpp
+++ b/Telegram/SourceFiles/export/data/export_data_types.cpp
@@ -160,6 +160,30 @@ std::vector<std::vector<HistoryMessageMarkupButton>> ButtonRowsFromTL(
 
 } // namespace
 
+QByteArray HistoryMessageMarkupButton::TypeToString(
+		const HistoryMessageMarkupButton &button) {
+	using Type = HistoryMessageMarkupButton::Type;
+	switch (button.type) {
+	case Type::Default: return "default";
+	case Type::Url: return "url";
+	case Type::Callback: return "callback";
+	case Type::CallbackWithPassword: return "callback_with_password";
+	case Type::RequestPhone: return "request_phone";
+	case Type::RequestLocation: return "request_location";
+	case Type::RequestPoll: return "request_poll";
+	case Type::RequestPeer: return "request_peer";
+	case Type::SwitchInline: return "switch_inline";
+	case Type::SwitchInlineSame: return "switch_inline_same";
+	case Type::Game: return "game";
+	case Type::Buy: return "buy";
+	case Type::Auth: return "auth";
+	case Type::UserProfile: return "user_profile";
+	case Type::WebView: return "web_view";
+	case Type::SimpleWebView: return "simple_web_view";
+	}
+	Unexpected("Type in HistoryMessageMarkupButton::Type.");
+}
+
 uint8 PeerColorIndex(BareId bareId) {
 	const uint8 map[] = { 0, 7, 4, 1, 6, 3, 5 };
 	return map[bareId % base::array_size(map)];
diff --git a/Telegram/SourceFiles/export/data/export_data_types.h b/Telegram/SourceFiles/export/data/export_data_types.h
index 5f2c2da39..76585991c 100644
--- a/Telegram/SourceFiles/export/data/export_data_types.h
+++ b/Telegram/SourceFiles/export/data/export_data_types.h
@@ -690,6 +690,8 @@ struct HistoryMessageMarkupButton {
 		SimpleWebView,
 	};
 
+	static QByteArray TypeToString(const HistoryMessageMarkupButton &);
+
 	Type type;
 	QString text;
 	QByteArray data;
diff --git a/Telegram/SourceFiles/export/output/export_output_html.cpp b/Telegram/SourceFiles/export/output/export_output_html.cpp
index 395315227..9df6bcbb3 100644
--- a/Telegram/SourceFiles/export/output/export_output_html.cpp
+++ b/Telegram/SourceFiles/export/output/export_output_html.cpp
@@ -1443,6 +1443,55 @@ auto HtmlWriter::Wrap::pushMessage(
 		block.append(text);
 		block.append(popTag());
 	}
+	if (!message.inlineButtonRows.empty()) {
+		using Type = HistoryMessageMarkupButton::Type;
+		const auto endline = u" | "_q;
+		block.append(pushTag("table", { { "class", "bot_buttons_table" } }));
+		block.append(pushTag("tbody"));
+		for (const auto &row : message.inlineButtonRows) {
+			block.append(pushTag("tr"));
+			block.append(pushTag("td", { { "class", "bot_button_row" } }));
+			for (const auto &button : row) {
+				using Attribute = std::pair<QByteArray, QByteArray>;
+				const auto content = (!button.data.isEmpty()
+						? (u"Data: "_q + button.data + endline)
+						: QString())
+					+ (!button.forwardText.isEmpty()
+						? (u"Forward text: "_q + button.forwardText + endline)
+						: QString())
+					+ (u"Type: "_q
+						+ HistoryMessageMarkupButton::TypeToString(button));
+				const auto link = (button.type == Type::Url)
+					? button.data
+					: QByteArray();
+				const auto onclick = (button.type != Type::Url)
+					? ("return ShowTextCopied('" + content + "');").toUtf8()
+					: QByteArray();
+				block.append(pushTag("div", { { "class", "bot_button" } }));
+				block.append(pushTag("a", {
+					link.isEmpty() ? Attribute() : Attribute{ "href", link },
+					onclick.isEmpty()
+						? Attribute()
+						: Attribute{ "onclick", onclick },
+				}));
+				block.append(pushTag("div"));
+				block.append(button.text.toUtf8());
+				block.append(popTag());
+				block.append(popTag());
+				block.append(popTag());
+
+				if (&button != &row.back()) {
+					block.append(pushTag("div", {
+						{ "class", "bot_button_column_separator" }
+					}));
+					block.append(popTag());
+				}
+			}
+			block.append(popTag());
+			block.append(popTag());
+		}
+		block.append(popTag());
+	}
 	if (!message.signature.isEmpty()) {
 		block.append(pushDiv("signature details"));
 		block.append(SerializeString(message.signature));
diff --git a/Telegram/SourceFiles/export/output/export_output_json.cpp b/Telegram/SourceFiles/export/output/export_output_json.cpp
index dde36a5a3..5bd7f2c37 100644
--- a/Telegram/SourceFiles/export/output/export_output_json.cpp
+++ b/Telegram/SourceFiles/export/output/export_output_json.cpp
@@ -784,29 +784,6 @@ QByteArray SerializeMessage(
 	pushBare("text_entities", SerializeText(context, message.text, true));
 
 	if (!message.inlineButtonRows.empty()) {
-		const auto typeString = [](
-				const HistoryMessageMarkupButton &entry) -> QByteArray {
-			using Type = HistoryMessageMarkupButton::Type;
-			switch (entry.type) {
-			case Type::Default: return "default";
-			case Type::Url: return "url";
-			case Type::Callback: return "callback";
-			case Type::CallbackWithPassword: return "callback_with_password";
-			case Type::RequestPhone: return "request_phone";
-			case Type::RequestLocation: return "request_location";
-			case Type::RequestPoll: return "request_poll";
-			case Type::RequestPeer: return "request_peer";
-			case Type::SwitchInline: return "switch_inline";
-			case Type::SwitchInlineSame: return "switch_inline_same";
-			case Type::Game: return "game";
-			case Type::Buy: return "buy";
-			case Type::Auth: return "auth";
-			case Type::UserProfile: return "user_profile";
-			case Type::WebView: return "web_view";
-			case Type::SimpleWebView: return "simple_web_view";
-			}
-			Unexpected("Type in HistoryMessageMarkupButton::Type.");
-		};
 		const auto serializeRow = [&](
 				const std::vector<HistoryMessageMarkupButton> &row) {
 			context.nesting.push_back(Context::kArray);
@@ -817,7 +794,8 @@ QByteArray SerializeMessage(
 				auto pairs = std::vector<std::pair<QByteArray, QByteArray>>();
 				pairs.push_back({
 					"type",
-					SerializeString(typeString(entry)),
+					SerializeString(
+						HistoryMessageMarkupButton::TypeToString(entry)),
 				});
 				if (!entry.text.isEmpty()) {
 					pairs.push_back({

From 93d1a187ca4b10a6f26418664a046fadb84829cf Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Thu, 22 Feb 2024 05:04:35 +0300
Subject: [PATCH 018/108] Improved view style of contacts.

---
 Telegram/Resources/langs/lang.strings         |   3 +
 .../SourceFiles/data/data_media_types.cpp     |   2 +-
 .../view/media/history_view_contact.cpp       | 487 ++++++++++++++----
 .../history/view/media/history_view_contact.h |  52 +-
 4 files changed, 425 insertions(+), 119 deletions(-)

diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 636909863..ea6c5f2e8 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -4724,6 +4724,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_boosts_prepaid_giveaway_status#one" = "{count} subscription {duration}";
 "lng_boosts_prepaid_giveaway_status#other" = "{count} subscriptions {duration}";
 
+"lng_contact_add" = "Add";
+"lng_contact_send_message" = "message";
+
 // Wnd specific
 
 "lng_wnd_choose_program_menu" = "Choose Default Program...";
diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp
index 7361373df..3e43cd47a 100644
--- a/Telegram/SourceFiles/data/data_media_types.cpp
+++ b/Telegram/SourceFiles/data/data_media_types.cpp
@@ -1253,7 +1253,7 @@ const SharedContact *MediaContact::sharedContact() const {
 }
 
 TextWithEntities MediaContact::notificationText() const {
-	return tr::lng_in_dlg_contact(tr::now, Ui::Text::WithEntities);
+	return Ui::Text::Colorized(tr::lng_in_dlg_contact(tr::now));
 }
 
 QString MediaContact::pinnedTextSubstring() const {
diff --git a/Telegram/SourceFiles/history/view/media/history_view_contact.cpp b/Telegram/SourceFiles/history/view/media/history_view_contact.cpp
index 7b0ac35b9..908bf1c0c 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_contact.cpp
+++ b/Telegram/SourceFiles/history/view/media/history_view_contact.cpp
@@ -7,28 +7,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "history/view/media/history_view_contact.h"
 
-#include "core/click_handler_types.h" // ClickHandlerContext
-#include "lang/lang_keys.h"
-#include "layout/layout_selection.h"
-#include "mainwindow.h"
 #include "boxes/add_contact_box.h"
-#include "history/history_item_components.h"
-#include "history/history_item.h"
-#include "history/history.h"
-#include "history/view/history_view_element.h"
-#include "history/view/history_view_cursor_state.h"
-#include "window/window_session_controller.h"
-#include "ui/empty_userpic.h"
-#include "ui/chat/chat_style.h"
-#include "ui/text/format_values.h" // Ui::FormatPhone
-#include "ui/text/text_options.h"
-#include "ui/painter.h"
+#include "core/click_handler_types.h" // ClickHandlerContext
 #include "data/data_session.h"
 #include "data/data_user.h"
-#include "data/data_media_types.h"
-#include "data/data_cloud_file.h"
+#include "history/history.h"
+#include "history/history_item_components.h"
+#include "history/view/history_view_cursor_state.h"
+#include "history/view/history_view_reply.h"
+#include "history/view/media/history_view_media_common.h"
+#include "lang/lang_keys.h"
 #include "main/main_session.h"
+#include "styles/style_boxes.h"
 #include "styles/style_chat.h"
+#include "ui/chat/chat_style.h"
+#include "ui/empty_userpic.h"
+#include "ui/painter.h"
+#include "ui/power_saving.h"
+#include "ui/rect.h"
+#include "ui/text/format_values.h" // Ui::FormatPhone
+#include "ui/text/text_options.h"
+#include "window/window_session_controller.h"
 
 namespace HistoryView {
 namespace {
@@ -81,17 +80,32 @@ Contact::Contact(
 	const QString &last,
 	const QString &phone)
 : Media(parent)
-, _userId(userId)
-, _fname(first)
-, _lname(last)
-, _phone(Ui::FormatPhone(phone)) {
+, _st(st::historyPagePreview)
+, _pixh(st::contactsPhotoSize)
+, _userId(userId) {
 	history()->owner().registerContactView(userId, parent);
 
-	_name.setText(
-		st::semiboldTextStyle,
-		tr::lng_full_name(tr::now, lt_first_name, first, lt_last_name, last).trimmed(),
-		Ui::NameTextOptions());
-	_phonew = st::normalFont->width(_phone);
+	_nameLine.setText(
+		st::webPageTitleStyle,
+		tr::lng_full_name(
+			tr::now,
+			lt_first_name,
+			first,
+			lt_last_name,
+			last).trimmed(),
+		Ui::WebpageTextTitleOptions());
+
+	_phoneLine.setText(
+		st::webPageDescriptionStyle,
+		Ui::FormatPhone(QString(phone).replace(QChar('+'), QString())),
+		Ui::WebpageTextTitleOptions());
+
+#if 0 // No info.
+	_infoLine.setText(
+		st::webPageDescriptionStyle,
+		phone,
+		Ui::WebpageTextTitleOptions());
+#endif
 }
 
 Contact::~Contact() {
@@ -111,121 +125,300 @@ void Contact::updateSharedContactUserId(UserId userId) {
 }
 
 QSize Contact::countOptimalSize() {
-	const auto item = _parent->data();
-	auto maxWidth = st::msgFileMinWidth;
-
 	_contact = _userId
-		? item->history()->owner().userLoaded(_userId)
+		? _parent->data()->history()->owner().userLoaded(_userId)
 		: nullptr;
 	if (_contact) {
 		_contact->loadUserpic();
 	} else {
-		const auto full = _name.toString();
+		const auto full = _nameLine.toString();
 		_photoEmpty = std::make_unique<Ui::EmptyUserpic>(
 			Ui::EmptyUserpic::UserpicColor(Data::DecideColorIndex(_userId
 				? peerFromUser(_userId)
 				: Data::FakePeerIdForJustName(full))),
 			full);
 	}
-	if (_contact && _contact->isContact()) {
-		_linkl = SendMessageClickHandler(_contact);
-		_link = tr::lng_profile_send_message(tr::now).toUpper();
-	} else if (_userId) {
-		_linkl = AddContactClickHandler(_parent->data());
-		_link = tr::lng_profile_add_contact(tr::now).toUpper();
-	}
-	_linkw = _link.isEmpty() ? 0 : st::semiboldFont->width(_link);
 
-	const auto &st = _userId ? st::msgFileThumbLayout : st::msgFileLayout;
-
-	const auto tleft = st.padding.left() + st.thumbSize + st.thumbSkip;
-	const auto tright = st.padding.right();
-	if (_userId) {
-		accumulate_max(maxWidth, tleft + _phonew + tright);
+	_buttons.clear();
+	if (_contact) {
+		const auto message = tr::lng_contact_send_message(tr::now).toUpper();
+		_buttons.push_back({
+			message,
+			st::semiboldFont->width(message),
+			SendMessageClickHandler(_contact),
+		});
+		if (!_contact->isContact()) {
+			const auto add = tr::lng_contact_add(tr::now).toUpper();
+			_buttons.push_back({
+				add,
+				st::semiboldFont->width(add),
+				AddContactClickHandler(_parent->data()),
+			});
+		}
+		_mainButton.link = _buttons.front().link;
 	} else {
-		accumulate_max(maxWidth, tleft + _phonew + _parent->skipBlockWidth() + st::msgPadding.right());
+#if 0 // Can't view contact.
+		const auto view = tr::lng_profile_add_contact(tr::now).toUpper();
+		_buttons.push_back({
+			view,
+			st::semiboldFont->width(view),
+			AddContactClickHandler(_parent->data()),
+		});
+#endif
+		_mainButton.link = nullptr;
 	}
 
-	accumulate_max(maxWidth, tleft + _name.maxWidth() + tright);
-	accumulate_min(maxWidth, st::msgMaxWidth);
-	auto minHeight = st.padding.top() + st.thumbSize + st.padding.bottom();
-	if (_parent->bottomInfoIsWide()) {
-		minHeight += st::msgDateFont->height - st::msgDateDelta.y();
+	const auto padding = inBubblePadding() + innerMargin();
+	const auto full = Rect(currentSize());
+	const auto outer = full - inBubblePadding();
+	const auto inner = outer - innerMargin();
+	const auto lineLeft = inner.left() + _pixh + inner.left() - outer.left();
+	const auto lineHeight = UnitedLineHeight();
+
+	auto maxWidth = _parent->skipBlockWidth();
+	auto minHeight = 0;
+
+	auto textMinHeight = 0;
+	if (!_nameLine.isEmpty()) {
+		accumulate_max(maxWidth, lineLeft + _nameLine.maxWidth());
+		textMinHeight += 1 * lineHeight;
 	}
-	if (!isBubbleTop()) {
-		minHeight -= st::msgFileTopMinus;
+	if (!_phoneLine.isEmpty()) {
+		accumulate_max(maxWidth, lineLeft + _phoneLine.maxWidth());
+		textMinHeight += 1 * lineHeight;
 	}
+	if (!_infoLine.isEmpty()) {
+		accumulate_max(maxWidth, lineLeft + _infoLine.maxWidth());
+		textMinHeight += std::min(_infoLine.minHeight(), 1 * lineHeight);
+	}
+	minHeight = std::max(textMinHeight, st::contactsPhotoSize);
+
+	if (!_buttons.empty()) {
+		auto buttonsWidth = rect::m::sum::h(st::historyPageButtonPadding);
+		for (const auto &button : _buttons) {
+			buttonsWidth += button.width;
+		}
+		accumulate_max(maxWidth, buttonsWidth);
+	}
+	maxWidth += rect::m::sum::h(padding);
+	minHeight += rect::m::sum::v(padding);
+
 	return { maxWidth, minHeight };
 }
 
 void Contact::draw(Painter &p, const PaintContext &context) const {
-	if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
-	auto paintw = width();
+	if (width() < rect::m::sum::h(st::msgPadding) + 1) {
+		return;
+	}
 
+	const auto st = context.st;
+	const auto sti = context.imageStyle();
 	const auto stm = context.messageStyle();
 
-	accumulate_min(paintw, maxWidth());
+	const auto bubble = st::msgPadding;
+	const auto full = Rect(currentSize());
+	const auto outer = full - inBubblePadding();
+	const auto inner = outer - innerMargin();
+	auto tshift = inner.top();
 
-	const auto &st = _userId ? st::msgFileThumbLayout : st::msgFileLayout;
-	const auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus;
-	const auto nameleft = st.padding.left() + st.thumbSize + st.thumbSkip;
-	const auto nametop = st.nameTop - topMinus;
-	const auto nameright = st.padding.right();
-	const auto statustop = st.statusTop - topMinus;
-	const auto linkshift = st::msgDateFont->height / 2;
-	const auto linktop = st.linkTop - topMinus - linkshift;
-	if (_userId) {
-		QRect rthumb(style::rtlrect(st.padding.left(), st.padding.top() - topMinus, st.thumbSize, st.thumbSize, paintw));
-		if (_contact) {
-			const auto was = !_userpic.null();
-			_contact->paintUserpic(p, _userpic, rthumb.x(), rthumb.y(), st.thumbSize);
-			if (!was && !_userpic.null()) {
-				history()->owner().registerHeavyViewPart(_parent);
+	const auto selected = context.selected();
+	const auto view = parent();
+	const auto colorIndex = _contact
+		? _contact->colorIndex()
+		: Data::DecideColorIndex(
+			Data::FakePeerIdForJustName(_nameLine.toString()));
+	const auto cache = context.outbg
+		? stm->replyCache[st->colorPatternIndex(colorIndex)].get()
+		: st->coloredReplyCache(selected, colorIndex).get();
+	const auto backgroundEmojiId = _contact
+		? _contact->backgroundEmojiId()
+		: DocumentId();
+	const auto backgroundEmoji = backgroundEmojiId
+		? st->backgroundEmojiData(backgroundEmojiId).get()
+		: nullptr;
+	const auto backgroundEmojiCache = backgroundEmoji
+		? &backgroundEmoji->caches[Ui::BackgroundEmojiData::CacheIndex(
+			selected,
+			context.outbg,
+			true,
+			colorIndex + 1)]
+		: nullptr;
+	Ui::Text::ValidateQuotePaintCache(*cache, _st);
+	Ui::Text::FillQuotePaint(p, outer, *cache, _st);
+	if (backgroundEmoji) {
+		ValidateBackgroundEmoji(
+			backgroundEmojiId,
+			backgroundEmoji,
+			backgroundEmojiCache,
+			cache,
+			view);
+		if (!backgroundEmojiCache->frames[0].isNull()) {
+			const auto end = rect::bottom(inner) + _st.padding.bottom();
+			const auto r = outer
+				- QMargins(0, 0, 0, rect::bottom(outer) - end);
+			FillBackgroundEmoji(p, r, false, *backgroundEmojiCache);
+		}
+	}
+
+	if (_mainButton.ripple) {
+		_mainButton.ripple->paint(
+			p,
+			outer.x(),
+			outer.y(),
+			width(),
+			&cache->bg);
+		if (_mainButton.ripple->empty()) {
+			_mainButton.ripple = nullptr;
+		}
+	}
+
+	{
+		const auto left = inner.left();
+		const auto top = tshift;
+		if (_userId) {
+			if (_contact) {
+				const auto was = !_userpic.null();
+				_contact->paintUserpic(p, _userpic, left, top, _pixh);
+				if (!was && !_userpic.null()) {
+					history()->owner().registerHeavyViewPart(_parent);
+				}
+			} else {
+				_photoEmpty->paintCircle(p, left, top, _pixh, _pixh);
 			}
 		} else {
-			_photoEmpty->paintCircle(p, st.padding.left(), st.padding.top() - topMinus, paintw, st.thumbSize);
+			_photoEmpty->paintCircle(p, left, top, _pixh, _pixh);
 		}
 		if (context.selected()) {
-			PainterHighQualityEnabler hq(p);
+			auto hq = PainterHighQualityEnabler(p);
 			p.setBrush(p.textPalette().selectOverlay);
 			p.setPen(Qt::NoPen);
-			p.drawEllipse(rthumb);
+			p.drawEllipse(left, top, _pixh, _pixh);
 		}
-
-		bool over = ClickHandler::showAsActive(_linkl);
-		p.setFont(over ? st::semiboldFont->underline() : st::semiboldFont);
-		p.setPen(stm->msgFileThumbLinkFg);
-		p.drawTextLeft(nameleft, linktop, paintw, _link, _linkw);
-	} else {
-		_photoEmpty->paintCircle(p, st.padding.left(), st.padding.top() - topMinus, paintw, st.thumbSize);
 	}
-	const auto namewidth = paintw - nameleft - nameright;
 
-	p.setFont(st::semiboldFont);
-	p.setPen(stm->historyFileNameFg);
-	_name.drawLeftElided(p, nameleft, nametop, namewidth, paintw);
+	const auto lineHeight = UnitedLineHeight();
+	const auto lineLeft = inner.left() + _pixh + inner.left() - outer.left();
+	const auto lineWidth = rect::right(inner) - lineLeft;
 
-	p.setFont(st::normalFont);
-	p.setPen(stm->mediaFg);
-	p.drawTextLeft(nameleft, statustop, paintw, _phone);
+	{
+		p.setPen(cache->icon);
+		p.setTextPalette(context.outbg
+			? stm->semiboldPalette
+			: st->coloredTextPalette(selected, colorIndex));
+
+		const auto endskip = _nameLine.hasSkipBlock()
+			? _parent->skipBlockWidth()
+			: 0;
+		_nameLine.drawLeftElided(
+			p,
+			lineLeft,
+			tshift,
+			lineWidth,
+			width(),
+			1,
+			style::al_left,
+			0,
+			-1,
+			endskip,
+			false,
+			context.selection);
+		tshift += lineHeight;
+
+		p.setTextPalette(stm->textPalette);
+	}
+	p.setPen(stm->historyTextFg);
+	{
+		tshift += st::lineWidth * 3; // Additional skip.
+		const auto endskip = _phoneLine.hasSkipBlock()
+			? _parent->skipBlockWidth()
+			: 0;
+		_phoneLine.drawLeftElided(
+			p,
+			lineLeft,
+			tshift,
+			lineWidth,
+			width(),
+			1,
+			style::al_left,
+			0,
+			-1,
+			endskip,
+			false,
+			toTitleSelection(context.selection));
+		tshift += 1 * lineHeight;
+	}
+	if (!_infoLine.isEmpty()) {
+		tshift += st::lineWidth * 3; // Additional skip.
+		const auto endskip = _infoLine.hasSkipBlock()
+			? _parent->skipBlockWidth()
+			: 0;
+		_parent->prepareCustomEmojiPaint(p, context, _infoLine);
+		_infoLine.draw(p, {
+			.position = { lineLeft, tshift },
+			.outerWidth = width(),
+			.availableWidth = lineWidth,
+			.spoiler = Ui::Text::DefaultSpoilerCache(),
+			.now = context.now,
+			.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
+			.pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler),
+			.selection = toDescriptionSelection(context.selection),
+			.elisionHeight = (1 * lineHeight),
+			.elisionRemoveFromEnd = endskip,
+		});
+		tshift += (1 * lineHeight);
+	}
+
+	if (!_buttons.empty()) {
+		p.setFont(st::semiboldFont);
+		p.setPen(cache->icon);
+		const auto end = rect::bottom(inner) + _st.padding.bottom();
+		const auto line = st::historyPageButtonLine;
+		auto color = cache->icon;
+		color.setAlphaF(color.alphaF() * 0.3);
+		const auto top = end + st::historyPageButtonPadding.top();
+		const auto buttonWidth = inner.width() / float64(_buttons.size());
+		p.fillRect(inner.x(), end, inner.width(), line, color);
+		for (auto i = 0; i < _buttons.size(); i++) {
+			const auto &button = _buttons[i];
+			const auto left = inner.x() + i * buttonWidth;
+			if (button.ripple) {
+				button.ripple->paint(p, left, end, buttonWidth, &cache->bg);
+				if (button.ripple->empty()) {
+					_buttons[i].ripple = nullptr;
+				}
+			}
+			p.drawText(
+				left + (buttonWidth - button.width) / 2,
+				top + st::semiboldFont->ascent,
+				button.text);
+		}
+	}
 }
 
 TextState Contact::textState(QPoint point, StateRequest request) const {
 	auto result = TextState(_parent);
 
-	if (_userId) {
-		const auto &st = _userId ? st::msgFileThumbLayout : st::msgFileLayout;
-		const auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus;
-		const auto nameleft = st.padding.left() + st.thumbSize + st.thumbSkip;
-		const auto linkshift = st::msgDateFont->height / 2;
-		const auto linktop = st.linkTop - topMinus - linkshift;
-		if (style::rtlrect(nameleft, linktop, _linkw, st::semiboldFont->height, width()).contains(point)) {
-			result.link = _linkl;
-			return result;
+	const auto full = Rect(currentSize());
+	const auto outer = full - inBubblePadding();
+	const auto inner = outer - innerMargin();
+
+	_lastPoint = point;
+
+	if (_buttons.size() > 1) {
+		const auto end = rect::bottom(inner) + _st.padding.bottom();
+		const auto line = st::historyPageButtonLine;
+		const auto bWidth = inner.width() / float64(_buttons.size());
+		const auto bHeight = rect::bottom(outer) - end;
+		for (auto i = 0; i < _buttons.size(); i++) {
+			const auto left = inner.x() + i * bWidth;
+			if (QRectF(left, end, bWidth, bHeight).contains(point)) {
+				result.link = _buttons[i].link;
+				return result;
+			}
 		}
 	}
-	if (QRect(0, 0, width(), height()).contains(point) && _contact) {
-		result.link = _contact->openLink();
+	if (outer.contains(point)) {
+		result.link = _mainButton.link;
 		return result;
 	}
 	return result;
@@ -239,4 +432,100 @@ bool Contact::hasHeavyPart() const {
 	return !_userpic.null();
 }
 
+void Contact::clickHandlerPressedChanged(
+		const ClickHandlerPtr &p,
+		bool pressed) {
+	const auto full = Rect(currentSize());
+	const auto outer = full - inBubblePadding();
+	const auto inner = outer - innerMargin();
+	const auto end = rect::bottom(inner) + _st.padding.bottom();
+	if ((_lastPoint.y() < end) || (_buttons.size() <= 1)) {
+		if (p != _mainButton.link) {
+			return;
+		}
+		if (pressed) {
+			if (!_mainButton.ripple) {
+				const auto owner = &parent()->history()->owner();
+				_mainButton.ripple = std::make_unique<Ui::RippleAnimation>(
+					st::defaultRippleAnimation,
+					Ui::RippleAnimation::RoundRectMask(
+						outer.size(),
+						_st.radius),
+					[=] { owner->requestViewRepaint(parent()); });
+			}
+			_mainButton.ripple->add(_lastPoint - outer.topLeft());
+		} else if (_mainButton.ripple) {
+			_mainButton.ripple->lastStop();
+		}
+		return;
+	} else if (_buttons.empty()) {
+		return;
+	}
+	const auto bWidth = inner.width() / float64(_buttons.size());
+	const auto bHeight = rect::bottom(outer) - end;
+	for (auto i = 0; i < _buttons.size(); i++) {
+		const auto &button = _buttons[i];
+		if (p != button.link) {
+			continue;
+		}
+		if (pressed) {
+			if (!button.ripple) {
+				const auto owner = &parent()->history()->owner();
+
+				_buttons[i].ripple = std::make_unique<Ui::RippleAnimation>(
+					st::defaultRippleAnimation,
+					Ui::RippleAnimation::MaskByDrawer(
+						QSize(bWidth, bHeight),
+						false,
+						[=](QPainter &p) {
+							p.drawRect(0, 0, bWidth, bHeight);
+						}),
+					[=] { owner->requestViewRepaint(parent()); });
+			}
+			button.ripple->add(_lastPoint
+				- QPoint(inner.x() + i * bWidth, end));
+		} else if (button.ripple) {
+			button.ripple->lastStop();
+		}
+	}
+}
+
+QMargins Contact::inBubblePadding() const {
+	return {
+		st::msgPadding.left(),
+		isBubbleTop() ? st::msgPadding.left() : 0,
+		st::msgPadding.right(),
+		isBubbleBottom() ? (st::msgPadding.left() + bottomInfoPadding()) : 0
+	};
+}
+
+QMargins Contact::innerMargin() const {
+	const auto button = _buttons.empty() ? 0 : st::historyPageButtonHeight;
+	return _st.padding + QMargins(0, 0, 0, button);
+}
+
+int Contact::bottomInfoPadding() const {
+	if (!isBubbleBottom()) {
+		return 0;
+	}
+
+	auto result = st::msgDateFont->height;
+
+	// We use padding greater than st::msgPadding.bottom() in the
+	// bottom of the bubble so that the left line looks pretty.
+	// but if we have bottom skip because of the info display
+	// we don't need that additional padding so we replace it
+	// back with st::msgPadding.bottom() instead of left().
+	result += st::msgPadding.bottom() - st::msgPadding.left();
+	return result;
+}
+
+TextSelection Contact::toTitleSelection(TextSelection selection) const {
+	return UnshiftItemSelection(selection, _nameLine);
+}
+
+TextSelection Contact::toDescriptionSelection(TextSelection selection) const {
+	return UnshiftItemSelection(toTitleSelection(selection), _phoneLine);
+}
+
 } // namespace HistoryView
diff --git a/Telegram/SourceFiles/history/view/media/history_view_contact.h b/Telegram/SourceFiles/history/view/media/history_view_contact.h
index ecca595f2..2dd665b94 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_contact.h
+++ b/Telegram/SourceFiles/history/view/media/history_view_contact.h
@@ -12,11 +12,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 namespace Ui {
 class EmptyUserpic;
+class RippleAnimation;
 } // namespace Ui
 
 namespace HistoryView {
 
-class Contact : public Media {
+class Contact final : public Media {
 public:
 	Contact(
 		not_null<Element*> parent,
@@ -29,7 +30,8 @@ public:
 	void draw(Painter &p, const PaintContext &context) const override;
 	TextState textState(QPoint point, StateRequest request) const override;
 
-	bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
+	bool toggleSelectionByHandlerClick(
+			const ClickHandlerPtr &p) const override {
 		return true;
 	}
 	bool dragItemByHandler(const ClickHandlerPtr &p) const override {
@@ -43,16 +45,6 @@ public:
 		return false;
 	}
 
-	const QString &fname() const {
-		return _fname;
-	}
-	const QString &lname() const {
-		return _lname;
-	}
-	const QString &phone() const {
-		return _phone;
-	}
-
 	// Should be called only by Data::Session.
 	void updateSharedContactUserId(UserId userId) override;
 
@@ -62,18 +54,40 @@ public:
 private:
 	QSize countOptimalSize() override;
 
+	void clickHandlerPressedChanged(
+		const ClickHandlerPtr &p, bool pressed) override;
+
+	[[nodiscard]] QMargins inBubblePadding() const;
+	[[nodiscard]] QMargins innerMargin() const;
+	[[nodiscard]] int bottomInfoPadding() const;
+
+	[[nodiscard]] TextSelection toTitleSelection(
+		TextSelection selection) const;
+	[[nodiscard]] TextSelection toDescriptionSelection(
+		TextSelection selection) const;
+
+	const style::QuoteStyle &_st;
+	const int _pixh;
+
 	UserId _userId = 0;
 	UserData *_contact = nullptr;
 
-	int _phonew = 0;
-	QString _fname, _lname, _phone;
-	Ui::Text::String _name;
+	Ui::Text::String _nameLine;
+	Ui::Text::String _phoneLine;
+	Ui::Text::String _infoLine;
+
+	struct Button {
+		QString text;
+		int width = 0;
+		ClickHandlerPtr link;
+		mutable std::unique_ptr<Ui::RippleAnimation> ripple;
+	};
+	std::vector<Button> _buttons;
+	Button _mainButton;
+
 	std::unique_ptr<Ui::EmptyUserpic> _photoEmpty;
 	mutable Ui::PeerUserpicView _userpic;
-
-	ClickHandlerPtr _linkl;
-	int _linkw = 0;
-	QString _link;
+	mutable QPoint _lastPoint;
 
 };
 

From afdd22d15404bedc88ff88448d7e13afcef8e7dd Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Thu, 22 Feb 2024 15:17:59 +0300
Subject: [PATCH 019/108] Fixed phone formatting generally.

---
 .../SourceFiles/history/view/media/history_view_contact.cpp   | 2 +-
 Telegram/SourceFiles/ui/text/format_values.cpp                | 4 +++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/Telegram/SourceFiles/history/view/media/history_view_contact.cpp b/Telegram/SourceFiles/history/view/media/history_view_contact.cpp
index 908bf1c0c..a6cb9635c 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_contact.cpp
+++ b/Telegram/SourceFiles/history/view/media/history_view_contact.cpp
@@ -97,7 +97,7 @@ Contact::Contact(
 
 	_phoneLine.setText(
 		st::webPageDescriptionStyle,
-		Ui::FormatPhone(QString(phone).replace(QChar('+'), QString())),
+		Ui::FormatPhone(phone),
 		Ui::WebpageTextTitleOptions());
 
 #if 0 // No info.
diff --git a/Telegram/SourceFiles/ui/text/format_values.cpp b/Telegram/SourceFiles/ui/text/format_values.cpp
index c5dd7763b..e792e6560 100644
--- a/Telegram/SourceFiles/ui/text/format_values.cpp
+++ b/Telegram/SourceFiles/ui/text/format_values.cpp
@@ -388,7 +388,9 @@ QString FormatPhone(const QString &phone) {
 	if (phone.at(0) == '0') {
 		return phone;
 	}
-	return Countries::Instance().format({ .phone = phone }).formatted;
+	return Countries::Instance().format({
+		.phone = (phone.at(0) == '+') ? phone.mid(1) : phone,
+	}).formatted;
 }
 
 QString FormatTTL(float64 ttl) {

From ea20e41f1daf42c75455b429de212e902ecb9d82 Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Thu, 22 Feb 2024 15:18:35 +0300
Subject: [PATCH 020/108] Added drag text to contact view.

---
 .../view/media/history_view_contact.cpp       | 60 ++++++++++++++-----
 1 file changed, 46 insertions(+), 14 deletions(-)

diff --git a/Telegram/SourceFiles/history/view/media/history_view_contact.cpp b/Telegram/SourceFiles/history/view/media/history_view_contact.cpp
index a6cb9635c..1a3e35716 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_contact.cpp
+++ b/Telegram/SourceFiles/history/view/media/history_view_contact.cpp
@@ -32,8 +32,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 namespace HistoryView {
 namespace {
 
-ClickHandlerPtr SendMessageClickHandler(PeerData *peer) {
-	return std::make_shared<LambdaClickHandler>([peer](ClickContext context) {
+class ContactClickHandler : public LambdaClickHandler {
+public:
+	using LambdaClickHandler::LambdaClickHandler;
+
+	void setDragText(const QString &t) {
+		_dragText = t;
+	}
+
+	QString dragText() const override {
+		return _dragText;
+	}
+
+private:
+	QString _dragText;
+
+};
+
+ClickHandlerPtr SendMessageClickHandler(not_null<PeerData*> peer) {
+	const auto clickHandlerPtr = std::make_shared<ContactClickHandler>([peer](
+			ClickContext context) {
 		const auto my = context.other.value<ClickHandlerContext>();
 		if (const auto controller = my.sessionWindow.get()) {
 			if (controller->session().uniqueId()
@@ -45,30 +63,44 @@ ClickHandlerPtr SendMessageClickHandler(PeerData *peer) {
 				Window::SectionShow::Way::Forward);
 		}
 	});
+	if (const auto user = peer->asUser()) {
+		clickHandlerPtr->setDragText(user->phone().isEmpty()
+			? peer->name()
+			: Ui::FormatPhone(user->phone()));
+	}
+	return clickHandlerPtr;
 }
 
 ClickHandlerPtr AddContactClickHandler(not_null<HistoryItem*> item) {
 	const auto session = &item->history()->session();
-	const auto fullId = item->fullId();
-	return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
+	const auto sharedContact = [=, fullId = item->fullId()] {
+		if (const auto item = session->data().message(fullId)) {
+			if (const auto media = item->media()) {
+				return media->sharedContact();
+			}
+		}
+		return (const Data::SharedContact *)nullptr;
+	};
+	const auto clickHandlerPtr = std::make_shared<ContactClickHandler>([=](
+			ClickContext context) {
 		const auto my = context.other.value<ClickHandlerContext>();
 		if (const auto controller = my.sessionWindow.get()) {
 			if (controller->session().uniqueId() != session->uniqueId()) {
 				return;
 			}
-			if (const auto item = session->data().message(fullId)) {
-				if (const auto media = item->media()) {
-					if (const auto contact = media->sharedContact()) {
-						controller->show(Box<AddContactBox>(
-							session,
-							contact->firstName,
-							contact->lastName,
-							contact->phoneNumber));
-					}
-				}
+			if (const auto contact = sharedContact()) {
+				controller->show(Box<AddContactBox>(
+					session,
+					contact->firstName,
+					contact->lastName,
+					contact->phoneNumber));
 			}
 		}
 	});
+	if (const auto contact = sharedContact()) {
+		clickHandlerPtr->setDragText(Ui::FormatPhone(contact->phoneNumber));
+	}
+	return clickHandlerPtr;
 }
 
 } // namespace

From 58443bc197474a2830c63ae6e6efba68a9e2ef43 Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Thu, 22 Feb 2024 15:33:51 +0300
Subject: [PATCH 021/108] Fixed ability to schedule forwarded messages without
 comment.

---
 .../history/view/history_view_scheduled_section.cpp            | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp
index 6e019e6eb..af2008ed8 100644
--- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp
@@ -563,7 +563,7 @@ Api::SendAction ScheduledWidget::prepareSendAction(
 
 void ScheduledWidget::send() {
 	const auto textWithTags = _composeControls->getTextWithAppliedMarkdown();
-	if (textWithTags.text.isEmpty()) {
+	if (textWithTags.text.isEmpty() && !_composeControls->readyToForward()) {
 		return;
 	}
 
@@ -592,6 +592,7 @@ void ScheduledWidget::send(Api::SendOptions options) {
 
 	session().api().sendMessage(std::move(message));
 
+	_composeControls->cancelForward();
 	_composeControls->clear();
 	//_saveDraftText = true;
 	//_saveDraftStart = crl::now();

From d252427e34f95c5efabc0310946f6461b4d828df Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Tue, 27 Feb 2024 02:09:39 +0300
Subject: [PATCH 022/108] Added blockquote to white list for message links
 parser.

---
 Telegram/SourceFiles/chat_helpers/message_field.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Telegram/SourceFiles/chat_helpers/message_field.cpp b/Telegram/SourceFiles/chat_helpers/message_field.cpp
index 0cef46221..5715eea45 100644
--- a/Telegram/SourceFiles/chat_helpers/message_field.cpp
+++ b/Telegram/SourceFiles/chat_helpers/message_field.cpp
@@ -727,7 +727,8 @@ void MessageLinksParser::parse() {
 			|| (tag == Ui::InputField::kTagItalic)
 			|| (tag == Ui::InputField::kTagUnderline)
 			|| (tag == Ui::InputField::kTagStrikeOut)
-			|| (tag == Ui::InputField::kTagSpoiler);
+			|| (tag == Ui::InputField::kTagSpoiler)
+			|| (tag == Ui::InputField::kTagBlockquote);
 	};
 
 	_ranges.clear();

From a77c547a626251c9b43d1822dbb2e9f1679b07e1 Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Tue, 27 Feb 2024 04:19:15 +0300
Subject: [PATCH 023/108] Fixed edit of long media caption with emoji.

---
 .../SourceFiles/history/history_widget.cpp    | 30 +++++++++----------
 .../history_view_compose_controls.cpp         |  9 ++++--
 .../controls/history_view_compose_controls.h  |  1 +
 .../view/history_view_replies_section.cpp     | 26 ++++++++--------
 .../view/history_view_scheduled_section.cpp   | 26 ++++++++--------
 Telegram/lib_ui                               |  2 +-
 6 files changed, 49 insertions(+), 45 deletions(-)

diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index 47db26aac..31667caef 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -1953,6 +1953,7 @@ bool HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) {
 	updateControlsVisibility();
 	updateControlsGeometry();
 	refreshTopBarActiveChat();
+	checkCharsLimitation();
 	if (_editMsgId) {
 		updateReplyEditTexts();
 		if (!_replyEditMsg) {
@@ -3869,17 +3870,12 @@ void HistoryWidget::saveEditMsg() {
 		return;
 	}
 	const auto webPageDraft = _preview->draft();
-	auto left = prepareTextForEditMsg();
-	auto sending = TextWithEntities();
+	const auto sending = prepareTextForEditMsg();
 
-	const auto originalLeftSize = left.text.size();
 	const auto hasMediaWithCaption = item
 		&& item->media()
 		&& item->media()->allowsEditCaption();
-	const auto maxCaptionSize = !hasMediaWithCaption
-		? MaxMessageSize
-		: Data::PremiumLimits(&session()).captionLengthCurrent();
-	if (!TextUtilities::CutPart(sending, left, maxCaptionSize)
+	if (sending.text.isEmpty()
 		&& (webPageDraft.removed
 			|| webPageDraft.url.isEmpty()
 			|| !webPageDraft.manual)
@@ -3888,11 +3884,16 @@ void HistoryWidget::saveEditMsg() {
 		controller()->show(
 			Box<DeleteMessagesBox>(item, suggestModerateActions));
 		return;
-	} else if (!left.text.isEmpty()) {
-		const auto remove = originalLeftSize - maxCaptionSize;
-		controller()->showToast(
-			tr::lng_edit_limit_reached(tr::now, lt_count, remove));
-		return;
+	} else {
+		const auto maxCaptionSize = !hasMediaWithCaption
+			? MaxMessageSize
+			: Data::PremiumLimits(&session()).captionLengthCurrent();
+		const auto remove = Ui::FieldCharacterCount(_field) - maxCaptionSize;
+		if (remove > 0) {
+			controller()->showToast(
+				tr::lng_edit_limit_reached(tr::now, lt_count, remove));
+			return;
+		}
 	}
 
 	const auto weak = Ui::MakeWeak(this);
@@ -7319,9 +7320,8 @@ void HistoryWidget::checkCharsLimitation() {
 		_charsLimitation = nullptr;
 		return;
 	}
-	const auto limits = Data::PremiumLimits(&session());
-	const auto left = prepareTextForEditMsg();
-	const auto remove = left.text.size() - limits.captionLengthCurrent();
+	const auto remove = Ui::FieldCharacterCount(_field)
+		- Data::PremiumLimits(&session()).captionLengthCurrent();
 	if (remove > 0) {
 		if (!_charsLimitation) {
 			_charsLimitation = base::make_unique_q<CharactersLimitLabel>(
diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp
index c8d47451c..cabfedbab 100644
--- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp
+++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp
@@ -3150,6 +3150,10 @@ not_null<Ui::RpWidget*> ComposeControls::likeAnimationTarget() const {
 	return _like;
 }
 
+int ComposeControls::fieldCharacterCount() const {
+	return Ui::FieldCharacterCount(_field);
+}
+
 bool ComposeControls::preventsClose(Fn<void()> &&continueCallback) const {
 	if (_voiceRecordBar->isActive()) {
 		_voiceRecordBar->showDiscardBox(std::move(continueCallback));
@@ -3323,9 +3327,8 @@ void ComposeControls::checkCharsLimitation() {
 		_charsLimitation = nullptr;
 		return;
 	}
-	const auto limits = Data::PremiumLimits(&session());
-	const auto left = prepareTextForEditMsg();
-	const auto remove = left.text.size() - limits.captionLengthCurrent();
+	const auto remove = Ui::FieldCharacterCount(_field)
+		- Data::PremiumLimits(&session()).captionLengthCurrent();
 	if (remove > 0) {
 		if (!_charsLimitation) {
 			using namespace Controls;
diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h
index b1bc012ca..d5985d6cb 100644
--- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h
+++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h
@@ -228,6 +228,7 @@ public:
 	[[nodiscard]] rpl::producer<bool> hasSendTextValue() const;
 	[[nodiscard]] rpl::producer<bool> fieldMenuShownValue() const;
 	[[nodiscard]] not_null<Ui::RpWidget*> likeAnimationTarget() const;
+	[[nodiscard]] int fieldCharacterCount() const;
 
 	[[nodiscard]] TextWithEntities prepareTextForEditMsg() const;
 
diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp
index 8ad662925..d8482c9d6 100644
--- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp
@@ -1170,29 +1170,29 @@ void RepliesWidget::edit(
 		return;
 	}
 	const auto webpage = _composeControls->webPageDraft();
-	auto sending = TextWithEntities();
-	auto left = _composeControls->prepareTextForEditMsg();
+	const auto sending = _composeControls->prepareTextForEditMsg();
 
-	const auto originalLeftSize = left.text.size();
 	const auto hasMediaWithCaption = item
 		&& item->media()
 		&& item->media()->allowsEditCaption();
-	const auto maxCaptionSize = !hasMediaWithCaption
-		? MaxMessageSize
-		: Data::PremiumLimits(&session()).captionLengthCurrent();
-	if (!TextUtilities::CutPart(sending, left, maxCaptionSize)
-		&& !hasMediaWithCaption) {
+	if (sending.text.isEmpty() && !hasMediaWithCaption) {
 		if (item) {
 			controller()->show(Box<DeleteMessagesBox>(item, false));
 		} else {
 			doSetInnerFocus();
 		}
 		return;
-	} else if (!left.text.isEmpty()) {
-		const auto remove = originalLeftSize - maxCaptionSize;
-		controller()->showToast(
-			tr::lng_edit_limit_reached(tr::now, lt_count, remove));
-		return;
+	} else {
+		const auto maxCaptionSize = !hasMediaWithCaption
+			? MaxMessageSize
+			: Data::PremiumLimits(&session()).captionLengthCurrent();
+		const auto remove = _composeControls->fieldCharacterCount()
+			- maxCaptionSize;
+		if (remove > 0) {
+			controller()->showToast(
+				tr::lng_edit_limit_reached(tr::now, lt_count, remove));
+			return;
+		}
 	}
 
 	lifetime().add([=] {
diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp
index af2008ed8..ca1fff7c9 100644
--- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp
@@ -635,29 +635,29 @@ void ScheduledWidget::edit(
 		return;
 	}
 	const auto webpage = _composeControls->webPageDraft();
-	auto sending = TextWithEntities();
-	auto left = _composeControls->prepareTextForEditMsg();
+	const auto sending = _composeControls->prepareTextForEditMsg();
 
-	const auto originalLeftSize = left.text.size();
 	const auto hasMediaWithCaption = item
 		&& item->media()
 		&& item->media()->allowsEditCaption();
-	const auto maxCaptionSize = !hasMediaWithCaption
-		? MaxMessageSize
-		: Data::PremiumLimits(&session()).captionLengthCurrent();
-	if (!TextUtilities::CutPart(sending, left, maxCaptionSize)
-		&& !hasMediaWithCaption) {
+	if (sending.text.isEmpty() && !hasMediaWithCaption) {
 		if (item) {
 			controller()->show(Box<DeleteMessagesBox>(item, false));
 		} else {
 			_composeControls->focus();
 		}
 		return;
-	} else if (!left.text.isEmpty()) {
-		const auto remove = originalLeftSize - maxCaptionSize;
-		controller()->showToast(
-			tr::lng_edit_limit_reached(tr::now, lt_count, remove));
-		return;
+	} else {
+		const auto maxCaptionSize = !hasMediaWithCaption
+			? MaxMessageSize
+			: Data::PremiumLimits(&session()).captionLengthCurrent();
+		const auto remove = _composeControls->fieldCharacterCount()
+			- maxCaptionSize;
+		if (remove > 0) {
+			controller()->showToast(
+				tr::lng_edit_limit_reached(tr::now, lt_count, remove));
+			return;
+		}
 	}
 
 	lifetime().add([=] {
diff --git a/Telegram/lib_ui b/Telegram/lib_ui
index d42475113..333587d95 160000
--- a/Telegram/lib_ui
+++ b/Telegram/lib_ui
@@ -1 +1 @@
-Subproject commit d4247511355a666903e9a57d821b1eb58884aade
+Subproject commit 333587d95edefcae1ebaf8838d3f499639fc2de8

From 08717dcd78cc02d609224becdbf21992215c59a8 Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Tue, 27 Feb 2024 05:05:00 +0300
Subject: [PATCH 024/108] Added counter label of characters limit to edit mode
 without media.

---
 Telegram/SourceFiles/history/history_widget.cpp        | 10 +++++++---
 .../view/controls/history_view_compose_controls.cpp    | 10 +++++++---
 2 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index 31667caef..f288a1883 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -7316,12 +7316,16 @@ void HistoryWidget::checkCharsLimitation() {
 		return;
 	}
 	const auto item = session().data().message(_history->peer, _editMsgId);
-	if (!item || !item->media() || !item->media()->allowsEditCaption()) {
+	if (!item) {
 		_charsLimitation = nullptr;
 		return;
 	}
-	const auto remove = Ui::FieldCharacterCount(_field)
-		- Data::PremiumLimits(&session()).captionLengthCurrent();
+	const auto hasMediaWithCaption = item->media()
+		&& item->media()->allowsEditCaption();
+	const auto maxCaptionSize = !hasMediaWithCaption
+		? MaxMessageSize
+		: Data::PremiumLimits(&session()).captionLengthCurrent();
+	const auto remove = Ui::FieldCharacterCount(_field) - maxCaptionSize;
 	if (remove > 0) {
 		if (!_charsLimitation) {
 			_charsLimitation = base::make_unique_q<CharactersLimitLabel>(
diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp
index cabfedbab..69f0f2de8 100644
--- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp
+++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp
@@ -3323,12 +3323,16 @@ void ComposeControls::checkCharsLimitation() {
 		return;
 	}
 	const auto item = _history->owner().message(_header->editMsgId());
-	if (!item || !item->media() || !item->media()->allowsEditCaption()) {
+	if (!item) {
 		_charsLimitation = nullptr;
 		return;
 	}
-	const auto remove = Ui::FieldCharacterCount(_field)
-		- Data::PremiumLimits(&session()).captionLengthCurrent();
+	const auto hasMediaWithCaption = item->media()
+		&& item->media()->allowsEditCaption();
+	const auto maxCaptionSize = !hasMediaWithCaption
+		? MaxMessageSize
+		: Data::PremiumLimits(&session()).captionLengthCurrent();
+	const auto remove = Ui::FieldCharacterCount(_field) - maxCaptionSize;
 	if (remove > 0) {
 		if (!_charsLimitation) {
 			using namespace Controls;

From 5dc6bdcc42ffb3bf966f0e5267f31cf45fbf3977 Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Tue, 27 Feb 2024 05:53:48 +0300
Subject: [PATCH 025/108] Fixed reply bar stuck when sending file in replies
 section.

---
 .../SourceFiles/history/view/history_view_replies_section.cpp  | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp
index d8482c9d6..fc556245b 100644
--- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp
@@ -967,7 +967,8 @@ void RepliesWidget::sendingFilesConfirmed(
 			album,
 			action);
 	}
-	if (_composeControls->replyingToMessage() == action.replyTo) {
+	if (_composeControls->replyingToMessage().messageId
+			== action.replyTo.messageId) {
 		_composeControls->cancelReplyMessage();
 		refreshTopBarActiveChat();
 	}

From aec4857e7b6dc70765e91f74d3724bd77c2d7297 Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Fri, 1 Mar 2024 03:50:11 +0300
Subject: [PATCH 026/108] Fixed format of phone number in intro widget.

---
 Telegram/SourceFiles/intro/intro_phone.cpp | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/Telegram/SourceFiles/intro/intro_phone.cpp b/Telegram/SourceFiles/intro/intro_phone.cpp
index 7ac420956..5e8261674 100644
--- a/Telegram/SourceFiles/intro/intro_phone.cpp
+++ b/Telegram/SourceFiles/intro/intro_phone.cpp
@@ -32,13 +32,18 @@ namespace Intro {
 namespace details {
 namespace {
 
-bool AllowPhoneAttempt(const QString &phone) {
+[[nodiscard]] bool AllowPhoneAttempt(const QString &phone) {
 	const auto digits = ranges::count_if(
 		phone,
 		[](QChar ch) { return ch.isNumber(); });
 	return (digits > 1);
 }
 
+[[nodiscard]] QString DigitsOnly(QString value) {
+	static const auto RegExp = QRegularExpression("[^0-9]");
+	return value.replace(RegExp, QString());
+}
+
 } // namespace
 
 PhoneWidget::PhoneWidget(
@@ -168,16 +173,12 @@ void PhoneWidget::submit() {
 	cancelNearestDcRequest();
 
 	// Check if such account is authorized already.
-	const auto digitsOnly = [](QString value) {
-		static const auto RegExp = QRegularExpression("[^0-9]");
-		return value.replace(RegExp, QString());
-	};
-	const auto phoneDigits = digitsOnly(phone);
+	const auto phoneDigits = DigitsOnly(phone);
 	for (const auto &[index, existing] : Core::App().domain().accounts()) {
 		const auto raw = existing.get();
 		if (const auto session = raw->maybeSession()) {
 			if (raw->mtp().environment() == account().mtp().environment()
-				&& digitsOnly(session->user()->phone()) == phoneDigits) {
+				&& DigitsOnly(session->user()->phone()) == phoneDigits) {
 				crl::on_main(raw, [=] {
 					Core::App().domain().activate(raw);
 				});
@@ -231,7 +232,7 @@ void PhoneWidget::phoneSubmitDone(const MTPauth_SentCode &result) {
 
 	result.match([&](const MTPDauth_sentCode &data) {
 		fillSentCodeData(data);
-		getData()->phone = _sentPhone;
+		getData()->phone = DigitsOnly(_sentPhone);
 		getData()->phoneHash = qba(data.vphone_code_hash());
 		const auto next = data.vnext_type();
 		if (next && next->type() == mtpc_auth_codeTypeCall) {

From c0c330a1503221304f09ffbf6447eb372af163af Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Fri, 1 Mar 2024 04:55:20 +0300
Subject: [PATCH 027/108] Fixed bubble color under strip of emoji pad for
 reactions.

---
 Telegram/SourceFiles/chat_helpers/chat_helpers.style | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style
index a39019a93..a622c37ea 100644
--- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style
+++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style
@@ -608,7 +608,7 @@ defaultComposeIcons: ComposeIcons {
 
 	stripBubble: icon{
 		{ "chat/reactions_bubble_shadow", windowShadowFg },
-		{ "chat/reactions_bubble", windowBg },
+		{ "chat/reactions_bubble", emojiPanBg },
 	};
 	stripExpandPanel: icon{
 		{ "chat/reactions_round_big", windowBgRipple },

From b790847fded28fe8bb8a39f2306a286f8311c84e Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Fri, 1 Mar 2024 07:24:22 +0300
Subject: [PATCH 028/108] Added ability to close call panel without hanging up
 call.

---
 Telegram/SourceFiles/calls/calls_panel.cpp | 18 +++++++++++-------
 Telegram/SourceFiles/calls/calls_panel.h   |  2 +-
 2 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/Telegram/SourceFiles/calls/calls_panel.cpp b/Telegram/SourceFiles/calls/calls_panel.cpp
index 8ee43eeda..05fdaaff2 100644
--- a/Telegram/SourceFiles/calls/calls_panel.cpp
+++ b/Telegram/SourceFiles/calls/calls_panel.cpp
@@ -44,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "main/main_session.h"
 #include "apiwrap.h"
 #include "platform/platform_specific.h"
+#include "base/event_filter.h"
 #include "base/platform/base_platform_info.h"
 #include "base/power_save_blocker.h"
 #include "media/streaming/media_streaming_utility.h"
@@ -147,17 +148,18 @@ void Panel::initWindow() {
 	window()->setTitle(_user->name());
 	window()->setTitleStyle(st::callTitle);
 
-	window()->events(
-	) | rpl::start_with_next([=](not_null<QEvent*> e) {
-		if (e->type() == QEvent::Close) {
-			handleClose();
+	base::install_event_filter(window().get(), [=](not_null<QEvent*> e) {
+		if (e->type() == QEvent::Close && handleClose()) {
+			e->ignore();
+			return base::EventFilterResult::Cancel;
 		} else if (e->type() == QEvent::KeyPress) {
 			if ((static_cast<QKeyEvent*>(e.get())->key() == Qt::Key_Escape)
 				&& window()->isFullScreen()) {
 				window()->showNormal();
 			}
 		}
-	}, window()->lifetime());
+		return base::EventFilterResult::Continue;
+	});
 
 	window()->setBodyTitleArea([=](QPoint widgetPoint) {
 		using Flag = Ui::WindowTitleHitTestFlag;
@@ -828,10 +830,12 @@ void Panel::paint(QRect clip) {
 	}
 }
 
-void Panel::handleClose() {
+bool Panel::handleClose() const {
 	if (_call) {
-		_call->hangup();
+		window()->hide();
+		return true;
 	}
+	return false;
 }
 
 not_null<Ui::RpWindow*> Panel::window() const {
diff --git a/Telegram/SourceFiles/calls/calls_panel.h b/Telegram/SourceFiles/calls/calls_panel.h
index dc715584a..c98537eb9 100644
--- a/Telegram/SourceFiles/calls/calls_panel.h
+++ b/Telegram/SourceFiles/calls/calls_panel.h
@@ -106,7 +106,7 @@ private:
 	void initLayout();
 	void initGeometry();
 
-	void handleClose();
+	[[nodiscard]] bool handleClose() const;
 
 	void updateControlsGeometry();
 	void updateHangupGeometry();

From a704611705dfbf075c35a647135c507c2159b975 Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Fri, 1 Mar 2024 15:24:40 +0300
Subject: [PATCH 029/108] Fixed ability to create vertical drum picker with
 first chosen item.

---
 Telegram/SourceFiles/ui/widgets/vertical_drum_picker.cpp | 5 +++--
 Telegram/SourceFiles/ui/widgets/vertical_drum_picker.h   | 2 +-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.cpp b/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.cpp
index d0cc71ca3..58fd123ea 100644
--- a/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.cpp
+++ b/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.cpp
@@ -82,9 +82,10 @@ VerticalDrumPicker::VerticalDrumPicker(
 	) | rpl::start_with_next([=](const QSize &s) {
 		_itemsVisible.count = std::ceil(float64(s.height()) / _itemHeight);
 		_itemsVisible.centerOffset = _itemsVisible.count / 2;
-		if (_pendingStartIndex && _itemsVisible.count) {
-			_index = normalizedIndex(base::take(_pendingStartIndex)
+		if ((_pendingStartIndex >= 0) && _itemsVisible.count) {
+			_index = normalizedIndex(_pendingStartIndex
 				- _itemsVisible.centerOffset);
+			_pendingStartIndex = -1;
 		}
 
 		if (!_loopData.looped) {
diff --git a/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.h b/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.h
index 802f72fba..4140d3397 100644
--- a/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.h
+++ b/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.h
@@ -75,7 +75,7 @@ private:
 
 	PaintItemCallback _paintCallback;
 
-	int _pendingStartIndex = 0;
+	int _pendingStartIndex = -1;
 
 	struct {
 		int count = 0;

From ef474f0dc8520b092c9712c2059246f36ee002ac Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Tue, 5 Mar 2024 17:09:25 +0300
Subject: [PATCH 030/108] Fixed opening of local links from webview bots in
 appropriate window.

---
 Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp
index 9aba7d65b..39c48f7ad 100644
--- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp
+++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp
@@ -566,9 +566,16 @@ bool AttachWebView::botHandleLocalUri(QString uri, bool keepOpen) {
 	if (!keepOpen) {
 		botClose();
 	}
-	crl::on_main([=, shownUrl = _lastShownUrl] {
+	crl::on_main([=, shownUrl = _lastShownUrl, bot = _bot] {
+		if (bot->session().windows().empty()) {
+			Core::App().domain().activate(&bot->session().account());
+		}
+		const auto window = !bot->session().windows().empty()
+			? bot->session().windows().front()
+			: nullptr;
 		const auto variant = QVariant::fromValue(ClickHandlerContext{
 			.attachBotWebviewUrl = shownUrl,
+			.sessionWindow = window,
 		});
 		UrlClickHandler::Open(local, variant);
 	});

From 48eb408fb8f17780925dcafda23e8942a1ea5c29 Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Wed, 6 Mar 2024 02:32:18 +0300
Subject: [PATCH 031/108] Fixed text elision of vote amount in polls with
 reactions.

---
 .../SourceFiles/history/view/media/history_view_poll.cpp  | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp
index 7e96e068f..82ca69e8f 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp
+++ b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp
@@ -803,9 +803,11 @@ void Poll::paintInlineFooter(
 		p,
 		left,
 		top,
-		std::min(
-			_totalVotesLabel.maxWidth(),
-			paintw - _parent->bottomInfoFirstLineWidth()),
+		_parent->data()->reactions().empty()
+			? std::min(
+				_totalVotesLabel.maxWidth(),
+				paintw - _parent->bottomInfoFirstLineWidth())
+			: _totalVotesLabel.maxWidth(),
 		width());
 }
 

From eab249fc138a07aa2d65210e7b156cbb1d7d583d Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Wed, 6 Mar 2024 19:40:01 +0300
Subject: [PATCH 032/108] Fixed countdown label for input field of bio.

---
 Telegram/SourceFiles/history/history_widget.cpp        | 6 ++++++
 Telegram/SourceFiles/settings/settings_information.cpp | 3 ++-
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index f288a1883..b658b8555 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -3892,7 +3892,13 @@ void HistoryWidget::saveEditMsg() {
 		if (remove > 0) {
 			controller()->showToast(
 				tr::lng_edit_limit_reached(tr::now, lt_count, remove));
+#ifndef _DEBUG
 			return;
+#else
+			if (!base::IsCtrlPressed()) {
+				return;
+			}
+#endif
 		}
 	}
 
diff --git a/Telegram/SourceFiles/settings/settings_information.cpp b/Telegram/SourceFiles/settings/settings_information.cpp
index ef6068987..6a46992cc 100644
--- a/Telegram/SourceFiles/settings/settings_information.cpp
+++ b/Telegram/SourceFiles/settings/settings_information.cpp
@@ -483,7 +483,8 @@ void SetupBio(
 		}
 		changed->fire(*current != text);
 		const auto limit = self->isPremium() ? premiumLimit : defaultLimit;
-		const auto countLeft = limit - int(text.size());
+		const auto countLeft = limit
+			- bio->lastTextSizeWithoutSurrogatePairsCount();
 		countdown->setText(QString::number(countLeft));
 		countdown->setTextColorOverride(
 			countLeft < 0 ? st::boxTextFgError->c : std::optional<QColor>());

From 09285bc9cd7c3f8e797bd6af046490b49e650e3b Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Thu, 7 Mar 2024 02:27:15 +0300
Subject: [PATCH 033/108] Moved out minimal levels of boosts for channel
 settings to single place.

---
 .../boxes/background_preview_box.cpp          | 15 +---
 .../boxes/peers/edit_peer_color_box.cpp       | 12 +--
 .../boxes/peers/replace_boost_box.cpp         | 26 ++++---
 Telegram/SourceFiles/boxes/stickers_box.cpp   |  9 +--
 .../SourceFiles/data/data_premium_limits.cpp  | 76 +++++++++++++++++++
 .../SourceFiles/data/data_premium_limits.h    | 22 ++++++
 Telegram/SourceFiles/data/data_session.cpp    | 18 ++---
 7 files changed, 129 insertions(+), 49 deletions(-)

diff --git a/Telegram/SourceFiles/boxes/background_preview_box.cpp b/Telegram/SourceFiles/boxes/background_preview_box.cpp
index 6dadab562..8406657e6 100644
--- a/Telegram/SourceFiles/boxes/background_preview_box.cpp
+++ b/Telegram/SourceFiles/boxes/background_preview_box.cpp
@@ -31,8 +31,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "history/history_item.h"
 #include "history/history_item_helpers.h"
 #include "history/view/history_view_message.h"
-#include "main/main_account.h"
-#include "main/main_app_config.h"
 #include "main/main_session.h"
 #include "apiwrap.h"
 #include "data/data_session.h"
@@ -42,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_document_resolver.h"
 #include "data/data_file_origin.h"
 #include "data/data_peer_values.h"
+#include "data/data_premium_limits.h"
 #include "settings/settings_premium.h"
 #include "storage/file_upload.h"
 #include "storage/localimageloader.h"
@@ -699,16 +698,10 @@ void BackgroundPreviewBox::checkLevelForChannel() {
 		if (!weak) {
 			return std::optional<Ui::AskBoostReason>();
 		}
-		const auto appConfig = &_forPeer->session().account().appConfig();
-		const auto defaultRequired = appConfig->get<int>(
-			"channel_wallpaper_level_min",
-			9);
-		const auto customRequired = appConfig->get<int>(
-			"channel_custom_wallpaper_level_min",
-			10);
+		const auto limits = Data::LevelLimits(&_forPeer->session());
 		const auto required = _paperEmojiId.isEmpty()
-			? customRequired
-			: defaultRequired;
+			? limits.channelCustomWallpaperLevelMin()
+			: limits.channelWallpaperLevelMin();
 		if (level >= required) {
 			applyForPeer(false);
 			return std::optional<Ui::AskBoostReason>();
diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp
index 2627d2342..e62aaac54 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp
@@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_emoji_statuses.h"
 #include "data/data_file_origin.h"
 #include "data/data_peer.h"
+#include "data/data_premium_limits.h"
 #include "data/data_session.h"
 #include "data/data_web_page.h"
 #include "history/view/history_view_element.h"
@@ -34,8 +35,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "lang/lang_keys.h"
 #include "lottie/lottie_icon.h"
 #include "lottie/lottie_single_player.h"
-#include "main/main_account.h"
-#include "main/main_app_config.h"
 #include "main/main_session.h"
 #include "settings/settings_common.h"
 #include "settings/settings_premium.h"
@@ -541,16 +540,13 @@ void Apply(
 				: peerColors->requiredChannelLevelFor(
 					peer->id,
 					values.colorIndex);
+			const auto limits = Data::LevelLimits(&peer->session());
 			const auto iconRequired = values.backgroundEmojiId
-				? session->account().appConfig().get<int>(
-					"channel_bg_icon_level_min",
-					5)
+				? limits.channelBgIconLevelMin()
 				: 0;
 			const auto statusRequired = (values.statusChanged
 				&& values.statusId)
-				? session->account().appConfig().get<int>(
-					"channel_emoji_status_level_min",
-					8)
+				? limits.channelEmojiStatusLevelMin()
 				: 0;
 			const auto required = std::max({
 				colorRequired,
diff --git a/Telegram/SourceFiles/boxes/peers/replace_boost_box.cpp b/Telegram/SourceFiles/boxes/peers/replace_boost_box.cpp
index ef99b3a90..36f18ab23 100644
--- a/Telegram/SourceFiles/boxes/peers/replace_boost_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/replace_boost_box.cpp
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "apiwrap.h"
 #include "base/event_filter.h"
 #include "base/unixtime.h"
+#include "data/data_premium_limits.h"
 #include "boxes/peer_list_box.h"
 #include "data/data_channel.h"
 #include "data/data_cloud_themes.h"
@@ -447,22 +448,23 @@ Ui::BoostFeatures LookupBoostFeatures(not_null<ChannelData*> channel) {
 	if (themes.empty()) {
 		channel->owner().cloudThemes().refreshChatThemes();
 	}
+	const auto levelLimits = Data::LevelLimits(&channel->session());
 	return Ui::BoostFeatures{
 		.nameColorsByLevel = std::move(nameColorsByLevel),
 		.linkStylesByLevel = std::move(linkStylesByLevel),
-		.linkLogoLevel = get(u"channel_bg_icon_level_min"_q, 4, !group),
-		.transcribeLevel = get(u"group_transcribe_level_min"_q, 6, group),
-		.emojiPackLevel = get(u"group_emoji_stickers_level_min"_q, 4, group),
-		.emojiStatusLevel = get(group
-			? u"group_emoji_status_level_min"_q
-			: u"channel_emoji_status_level_min"_q, 8),
-		.wallpaperLevel = get(group
-			? u"group_wallpaper_level_min"_q
-			: u"channel_wallpaper_level_min"_q, 9),
+		.linkLogoLevel = group ? 0 : levelLimits.channelBgIconLevelMin(),
+		.transcribeLevel = group ? levelLimits.groupTranscribeLevelMin() : 0,
+		.emojiPackLevel = group ? levelLimits.groupEmojiStickersLevelMin() : 0,
+		.emojiStatusLevel = group
+			? levelLimits.groupEmojiStatusLevelMin()
+			: levelLimits.channelEmojiStatusLevelMin(),
+		.wallpaperLevel = group
+			? levelLimits.groupWallpaperLevelMin()
+			: levelLimits.channelWallpaperLevelMin(),
 		.wallpapersCount = themes.empty() ? 8 : int(themes.size()),
-		.customWallpaperLevel = get(group
-			? u"channel_custom_wallpaper_level_min"_q
-			: u"group_custom_wallpaper_level_min"_q, 10),
+		.customWallpaperLevel = group
+			? levelLimits.groupCustomWallpaperLevelMin()
+			: levelLimits.channelCustomWallpaperLevelMin(),
 	};
 }
 
diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp
index 8a88acfba..7d72ebf5a 100644
--- a/Telegram/SourceFiles/boxes/stickers_box.cpp
+++ b/Telegram/SourceFiles/boxes/stickers_box.cpp
@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_channel.h"
 #include "data/data_file_origin.h"
 #include "data/data_document_media.h"
+#include "data/data_premium_limits.h"
 #include "data/stickers/data_stickers.h"
 #include "core/application.h"
 #include "lang/lang_keys.h"
@@ -40,8 +41,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/painter.h"
 #include "ui/unread_badge_paint.h"
 #include "media/clip/media_clip_reader.h"
-#include "main/main_account.h"
-#include "main/main_app_config.h"
 #include "main/main_session.h"
 #include "styles/style_layers.h"
 #include "styles/style_boxes.h"
@@ -2050,10 +2049,8 @@ void StickersBox::Inner::checkGroupLevel(Fn<void()> done) {
 			return std::optional<Ui::AskBoostReason>();
 		}
 		_checkingGroupLevel = false;
-		const auto appConfig = &peer->session().account().appConfig();
-		const auto required = appConfig->get<int>(
-			"group_emoji_stickers_level_min",
-			4);
+		const auto required = Data::LevelLimits(
+			&peer->session()).groupEmojiStickersLevelMin();
 		if (level >= required) {
 			save();
 			return std::optional<Ui::AskBoostReason>();
diff --git a/Telegram/SourceFiles/data/data_premium_limits.cpp b/Telegram/SourceFiles/data/data_premium_limits.cpp
index 443474040..ecca86149 100644
--- a/Telegram/SourceFiles/data/data_premium_limits.cpp
+++ b/Telegram/SourceFiles/data/data_premium_limits.cpp
@@ -217,4 +217,80 @@ bool PremiumLimits::isPremium() const {
 	return _session->premium();
 }
 
+LevelLimits::LevelLimits(not_null<Main::Session*> session)
+: _session(session) {
+}
+
+int LevelLimits::channelColorLevelMin() const {
+	return _session->account().appConfig().get<int>(
+		u"channel_color_level_min"_q,
+		5);
+}
+
+int LevelLimits::channelBgIconLevelMin() const {
+	return _session->account().appConfig().get<int>(
+		u"channel_bg_icon_level_min"_q,
+		4);
+}
+
+int LevelLimits::channelProfileBgIconLevelMin() const {
+	return _session->account().appConfig().get<int>(
+		u"channel_profile_bg_icon_level_min"_q,
+		7);
+}
+
+int LevelLimits::channelEmojiStatusLevelMin() const {
+	return _session->account().appConfig().get<int>(
+		u"channel_emoji_status_level_min"_q,
+		8);
+}
+
+int LevelLimits::channelWallpaperLevelMin() const {
+	return _session->account().appConfig().get<int>(
+		u"channel_wallpaper_level_min"_q,
+		9);
+}
+
+int LevelLimits::channelCustomWallpaperLevelMin() const {
+	return _session->account().appConfig().get<int>(
+		u"channel_custom_wallpaper_level_min"_q,
+		10);
+}
+
+int LevelLimits::groupTranscribeLevelMin() const {
+	return _session->account().appConfig().get<int>(
+		u"group_transcribe_level_min"_q,
+		6);
+}
+
+int LevelLimits::groupEmojiStickersLevelMin() const {
+	return _session->account().appConfig().get<int>(
+		u"group_emoji_stickers_level_min"_q,
+		4);
+}
+
+int LevelLimits::groupProfileBgIconLevelMin() const {
+	return _session->account().appConfig().get<int>(
+		u"group_profile_bg_icon_level_min"_q,
+		5);
+}
+
+int LevelLimits::groupEmojiStatusLevelMin() const {
+	return _session->account().appConfig().get<int>(
+		u"group_emoji_status_level_min"_q,
+		8);
+}
+
+int LevelLimits::groupWallpaperLevelMin() const {
+	return _session->account().appConfig().get<int>(
+		u"group_wallpaper_level_min"_q,
+		9);
+}
+
+int LevelLimits::groupCustomWallpaperLevelMin() const {
+	return _session->account().appConfig().get<int>(
+		u"group_custom_wallpaper_level_min"_q,
+		10);
+}
+
 } // namespace Data
diff --git a/Telegram/SourceFiles/data/data_premium_limits.h b/Telegram/SourceFiles/data/data_premium_limits.h
index bc3c86d9f..5c50d7a12 100644
--- a/Telegram/SourceFiles/data/data_premium_limits.h
+++ b/Telegram/SourceFiles/data/data_premium_limits.h
@@ -91,4 +91,26 @@ private:
 
 };
 
+class LevelLimits final {
+public:
+	LevelLimits(not_null<Main::Session*> session);
+
+	[[nodiscard]] int channelColorLevelMin() const;
+	[[nodiscard]] int channelBgIconLevelMin() const;
+	[[nodiscard]] int channelProfileBgIconLevelMin() const;
+	[[nodiscard]] int channelEmojiStatusLevelMin() const;
+	[[nodiscard]] int channelWallpaperLevelMin() const;
+	[[nodiscard]] int channelCustomWallpaperLevelMin() const;
+	[[nodiscard]] int groupTranscribeLevelMin() const;
+	[[nodiscard]] int groupEmojiStickersLevelMin() const;
+	[[nodiscard]] int groupProfileBgIconLevelMin() const;
+	[[nodiscard]] int groupEmojiStatusLevelMin() const;
+	[[nodiscard]] int groupWallpaperLevelMin() const;
+	[[nodiscard]] int groupCustomWallpaperLevelMin() const;
+
+private:
+	const not_null<Main::Session*> _session;
+
+};
+
 } // namespace Data
diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp
index c8235eac4..e7ec13242 100644
--- a/Telegram/SourceFiles/data/data_session.cpp
+++ b/Telegram/SourceFiles/data/data_session.cpp
@@ -239,10 +239,8 @@ Session::Session(not_null<Main::Session*> session)
 	_session->local().cacheBigFilePath(),
 	_session->local().cacheBigFileSettings()))
 , _groupFreeTranscribeLevel(session->account().appConfig().value(
-) | rpl::map([=] {
-	return session->account().appConfig().get<int>(
-		u"group_transcribe_level_min"_q,
-		6);
+) | rpl::map([limits = Data::LevelLimits(session)] {
+	return limits.groupTranscribeLevelMin();
 }))
 , _chatsList(
 	session,
@@ -2185,8 +2183,7 @@ rpl::producer<int> Session::maxPinnedChatsLimitValue(
 	// because it slices the list to that limit. We don't want to slice
 	// premium-ly added chats from the pinned list because of sync issues.
 	return _session->account().appConfig().value(
-	) | rpl::map([=] {
-		const auto limits = Data::PremiumLimits(_session);
+	) | rpl::map([folder, limits = Data::PremiumLimits(_session)] {
 		return folder
 			? limits.dialogsFolderPinnedPremium()
 			: limits.dialogsPinnedPremium();
@@ -2200,8 +2197,7 @@ rpl::producer<int> Session::maxPinnedChatsLimitValue(
 	// because it slices the list to that limit. We don't want to slice
 	// premium-ly added chats from the pinned list because of sync issues.
 	return _session->account().appConfig().value(
-	) | rpl::map([=] {
-		const auto limits = Data::PremiumLimits(_session);
+	) | rpl::map([limits = Data::PremiumLimits(_session)] {
 		return limits.dialogFiltersChatsPremium();
 	});
 }
@@ -2209,8 +2205,7 @@ rpl::producer<int> Session::maxPinnedChatsLimitValue(
 rpl::producer<int> Session::maxPinnedChatsLimitValue(
 		not_null<Data::Forum*> forum) const {
 	return _session->account().appConfig().value(
-	) | rpl::map([=] {
-		const auto limits = Data::PremiumLimits(_session);
+	) | rpl::map([limits = Data::PremiumLimits(_session)] {
 		return limits.topicsPinnedCurrent();
 	});
 }
@@ -2222,8 +2217,7 @@ rpl::producer<int> Session::maxPinnedChatsLimitValue(
 	// because it slices the list to that limit. We don't want to slice
 	// premium-ly added chats from the pinned list because of sync issues.
 	return _session->account().appConfig().value(
-	) | rpl::map([=] {
-		const auto limits = Data::PremiumLimits(_session);
+	) | rpl::map([limits = Data::PremiumLimits(_session)] {
 		return limits.savedSublistsPinnedPremium();
 	});
 }

From f56b16c6efb4162c0e72a54c053b921843d1a8ad Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Thu, 7 Mar 2024 02:27:03 +0300
Subject: [PATCH 034/108] Added initial badges for minimal level of boosts to
 channel settings.

---
 .../boxes/peers/edit_peer_color_box.cpp       | 197 ++++++++++++++++--
 Telegram/SourceFiles/settings/settings.style  |   2 +
 2 files changed, 187 insertions(+), 12 deletions(-)

diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp
index e62aaac54..8fccba41a 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp
@@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "boxes/background_box.h"
 #include "boxes/stickers_box.h"
 #include "chat_helpers/compose/compose_show.h"
+#include "core/ui_integration.h" // Core::MarkedTextContext.
 #include "data/stickers/data_custom_emoji.h"
 #include "data/stickers/data_stickers.h"
 #include "data/data_changes.h"
@@ -42,10 +43,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/chat/chat_style.h"
 #include "ui/chat/chat_theme.h"
 #include "ui/effects/path_shift_gradient.h"
+#include "ui/effects/premium_graphics.h"
 #include "ui/layers/generic_box.h"
 #include "ui/text/text_utilities.h"
 #include "ui/widgets/buttons.h"
 #include "ui/painter.h"
+#include "ui/rect.h"
 #include "ui/vertical_list.h"
 #include "window/themes/window_theme.h"
 #include "window/section_widget.h"
@@ -145,6 +148,28 @@ private:
 
 };
 
+class LevelBadge final : public Ui::RpWidget {
+public:
+	LevelBadge(
+		not_null<QWidget*> parent,
+		uint32 level,
+		not_null<Main::Session*> session);
+
+	void setMinimal(bool value);
+
+private:
+	void paintEvent(QPaintEvent *e) override;
+
+	void updateText();
+
+	const uint32 _level;
+	const TextWithEntities _icon;
+	const Core::MarkedTextContext _context;
+	Ui::Text::String _text;
+	bool _minimal = false;
+
+};
+
 ColorSample::ColorSample(
 	not_null<QWidget*> parent,
 	std::shared_ptr<Ui::ChatStyle> style,
@@ -436,6 +461,108 @@ HistoryView::Context PreviewDelegate::elementContext() {
 	return HistoryView::Context::AdminLog;
 }
 
+LevelBadge::LevelBadge(
+	not_null<QWidget*> parent,
+	uint32 level,
+	not_null<Main::Session*> session)
+: Ui::RpWidget(parent)
+, _level(level)
+, _icon(Ui::Text::SingleCustomEmoji(
+	session->data().customEmojiManager().registerInternalEmoji(
+		st::settingsLevelBadgeLock,
+		QMargins(0, st::settingsLevelBadgeLockSkip, 0, 0),
+		false)))
+, _context({ .session = session }) {
+	updateText();
+}
+
+void LevelBadge::updateText() {
+	auto text = _icon;
+	text.append(' ');
+	if (!_minimal) {
+		text.append(tr::lng_boost_level(
+			tr::now,
+			lt_count,
+			_level,
+			Ui::Text::WithEntities));
+	} else {
+		text.append(QString::number(_level));
+	}
+	const auto &st = st::settingsPremiumNewBadge.style;
+	_text.setMarkedText(
+		st,
+		text,
+		kMarkupTextOptions,
+		_context);
+	const auto &padding = st::settingsColorSamplePadding;
+	QWidget::resize(
+		_text.maxWidth() + rect::m::sum::h(padding),
+		st.font->height + rect::m::sum::v(padding));
+}
+
+void LevelBadge::setMinimal(bool value) {
+	if ((value != _minimal) && value) {
+		_minimal = value;
+		updateText();
+		update();
+	}
+}
+
+void LevelBadge::paintEvent(QPaintEvent *e) {
+	auto p = QPainter(this);
+	auto hq = PainterHighQualityEnabler(p);
+
+	const auto radius = height() / 2;
+	p.setPen(Qt::NoPen);
+	auto gradient = QLinearGradient(QPointF(0, 0), QPointF(width(), 0));
+	gradient.setStops(Ui::Premium::ButtonGradientStops());
+	p.setBrush(gradient);
+	p.drawRoundedRect(rect(), radius, radius);
+
+	p.setPen(st::premiumButtonFg);
+	p.setBrush(Qt::NoBrush);
+
+	const auto context = Ui::Text::PaintContext{
+		.position = rect::m::pos::tl(st::settingsColorSamplePadding),
+		.outerWidth = width(),
+		.availableWidth = width(),
+	};
+	_text.draw(p, context);
+}
+
+void AddLevelBadge(
+		int level,
+		not_null<Ui::SettingsButton*> button,
+		Ui::RpWidget *right,
+		not_null<ChannelData*> channel,
+		const QMargins &padding,
+		rpl::producer<QString> text) {
+	if (channel->levelHint() >= level) {
+		return;
+	}
+	const auto badge = Ui::CreateChild<LevelBadge>(
+		button.get(),
+		level,
+		&channel->session());
+	badge->show();
+	const auto sampleLeft = st::settingsColorSamplePadding.left();
+	const auto badgeLeft = padding.left() + sampleLeft;
+	rpl::combine(
+		button->sizeValue(),
+		std::move(text)
+	) | rpl::start_with_next([=](const QSize &s, const QString &) {
+		if (s.isNull()) {
+			return;
+		}
+		badge->moveToLeft(
+			button->fullTextWidth() + badgeLeft,
+			(s.height() - badge->height()) / 2);
+		const auto rightEdge = right ? right->pos().x() : button->width();
+		badge->setMinimal((rect::right(badge) + sampleLeft) > rightEdge);
+		badge->setVisible((rect::right(badge) + sampleLeft) < rightEdge);
+	}, badge->lifetime());
+}
+
 struct SetValues {
 	uint8 colorIndex = 0;
 	DocumentId backgroundEmojiId = 0;
@@ -722,6 +849,7 @@ struct ButtonWithEmoji {
 		not_null<Ui::RpWidget*> parent,
 		std::shared_ptr<ChatHelpers::Show> show,
 		std::shared_ptr<Ui::ChatStyle> style,
+		not_null<PeerData*> peer,
 		rpl::producer<uint8> colorIndexValue,
 		rpl::producer<DocumentId> emojiIdValue,
 		Fn<void(DocumentId)> emojiIdChosen) {
@@ -823,21 +951,33 @@ struct ButtonWithEmoji {
 		}
 	});
 
+	if (const auto channel = peer->asChannel()) {
+		AddLevelBadge(
+			Data::LevelLimits(&channel->session()).channelBgIconLevelMin(),
+			raw,
+			right,
+			channel,
+			button.st->padding,
+			tr::lng_settings_color_emoji());
+	}
+
 	return result;
 }
 
 [[nodiscard]] object_ptr<Ui::SettingsButton> CreateEmojiStatusButton(
 		not_null<Ui::RpWidget*> parent,
 		std::shared_ptr<ChatHelpers::Show> show,
+		not_null<ChannelData*> channel,
 		rpl::producer<DocumentId> statusIdValue,
 		Fn<void(DocumentId,TimeId)> statusIdChosen,
 		bool group) {
 	const auto button = ButtonStyleWithRightEmoji(parent);
+	const auto &phrase = group
+		? tr::lng_edit_channel_status_group
+		: tr::lng_edit_channel_status;
 	auto result = Settings::CreateButtonWithIcon(
 		parent,
-		(group
-			? tr::lng_edit_channel_status_group()
-			: tr::lng_edit_channel_status()),
+		phrase(),
 		*button.st,
 		{ &st::menuBlueIconEmojiStatus });
 	const auto raw = result.data();
@@ -922,6 +1062,17 @@ struct ButtonWithEmoji {
 		}
 	});
 
+	const auto limits = Data::LevelLimits(&channel->session());
+	AddLevelBadge(
+		(group
+			? limits.groupEmojiStatusLevelMin()
+			: limits.channelEmojiStatusLevelMin()),
+		raw,
+		right,
+		channel,
+		button.st->padding,
+		phrase());
+
 	return result;
 }
 
@@ -1032,6 +1183,14 @@ struct ButtonWithEmoji {
 		}
 	}, right->lifetime());
 
+	AddLevelBadge(
+		Data::LevelLimits(&channel->session()).groupEmojiStickersLevelMin(),
+		raw,
+		right,
+		channel,
+		button.st->padding,
+		tr::lng_group_emoji());
+
 	return result;
 }
 
@@ -1075,10 +1234,7 @@ void EditPeerColorBox(
 			verticalLayout,
 			{
 				.name = u"palette"_q,
-				.sizeOverride = {
-					st::settingsCloudPasswordIconSize,
-					st::settingsCloudPasswordIconSize,
-				},
+				.sizeOverride = Size(st::settingsCloudPasswordIconSize),
 			},
 			st::peerAppearanceIconPadding);
 		box->setShowFinishedCallback([animate = std::move(icon.animate)] {
@@ -1131,6 +1287,7 @@ void EditPeerColorBox(
 			container,
 			show,
 			style,
+			peer,
 			state->index.value(),
 			state->emojiId.value(),
 			[=](DocumentId id) { state->emojiId = id; }));
@@ -1146,20 +1303,35 @@ void EditPeerColorBox(
 
 	if (const auto channel = peer->asChannel()) {
 		Ui::AddSkip(container, st::settingsColorSampleSkip);
-		Settings::AddButtonWithIcon(
+		const auto &phrase = group
+			? tr::lng_edit_channel_wallpaper_group
+			: tr::lng_edit_channel_wallpaper;
+		const auto button = Settings::AddButtonWithIcon(
 			container,
-			(group
-				? tr::lng_edit_channel_wallpaper_group()
-				: tr::lng_edit_channel_wallpaper()),
+			phrase(),
 			st::peerAppearanceButton,
 			{ &st::menuBlueIconWallpaper }
-		)->setClickedCallback([=] {
+		);
+		button->setClickedCallback([=] {
 			const auto usage = ChatHelpers::WindowUsage::PremiumPromo;
 			if (const auto strong = show->resolveWindow(usage)) {
 				show->show(Box<BackgroundBox>(strong, channel));
 			}
 		});
 
+		{
+			const auto limits = Data::LevelLimits(&channel->session());
+			AddLevelBadge(
+				group
+					? limits.groupCustomWallpaperLevelMin()
+					: limits.channelCustomWallpaperLevelMin(),
+				button,
+				nullptr,
+				channel,
+				st::peerAppearanceButton.padding,
+				phrase());
+		}
+
 		Ui::AddSkip(container, st::settingsColorSampleSkip);
 		Ui::AddDividerText(
 			container,
@@ -1197,6 +1369,7 @@ void EditPeerColorBox(
 		container->add(CreateEmojiStatusButton(
 			container,
 			show,
+			channel,
 			state->statusId.value(),
 			[=](DocumentId id, TimeId until) {
 				state->statusId = id;
diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style
index f45d68c0b..9716a383e 100644
--- a/Telegram/SourceFiles/settings/settings.style
+++ b/Telegram/SourceFiles/settings/settings.style
@@ -562,6 +562,8 @@ settingsColorButton: SettingsButton(settingsButton) {
 settingsColorRadioMargin: 17px;
 settingsColorRadioSkip: 13px;
 settingsColorRadioStroke: 2px;
+settingsLevelBadgeLock: icon {{ "chat/mini_lock", premiumButtonFg }};
+settingsLevelBadgeLockSkip: 4px;
 
 messagePrivacyTopSkip: 8px;
 messagePrivacyRadioSkip: 6px;

From 02e1c03ed911d5bb43d6ded06f16cbb3c22b91f2 Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Thu, 7 Mar 2024 16:36:07 +0300
Subject: [PATCH 035/108] Fixed position of connecting state widget when forum
 is opened.

Fixed #27548.
---
 Telegram/SourceFiles/dialogs/dialogs_widget.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
index 6ce80f8a9..6d85f7cef 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
+++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
@@ -2964,7 +2964,9 @@ void Widget::updateControlsGeometry() {
 	if (_connecting) {
 		_connecting->setBottomSkip(bottomSkip);
 	}
-	controller()->setConnectingBottomSkip(bottomSkip);
+	if (_layout != Layout::Child) {
+		controller()->setConnectingBottomSkip(bottomSkip);
+	}
 
 	const auto wasScrollTop = _scroll->scrollTop();
 	const auto newScrollTop = (_topDelta < 0 && wasScrollTop <= 0)

From 27bd9e3ee53a1f8688a8a5bf565beb849b42b3a8 Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Thu, 7 Mar 2024 17:41:30 +0300
Subject: [PATCH 036/108] Added icons to buttons for privacy settings that
 require premium.

---
 .../settings/settings_privacy_security.cpp    | 83 +++++++++++++++----
 1 file changed, 65 insertions(+), 18 deletions(-)

diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.cpp b/Telegram/SourceFiles/settings/settings_privacy_security.cpp
index feeb83a66..3f80fe54a 100644
--- a/Telegram/SourceFiles/settings/settings_privacy_security.cpp
+++ b/Telegram/SourceFiles/settings/settings_privacy_security.cpp
@@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "settings/settings_privacy_security.h"
 
 #include "api/api_authorizations.h"
-#include "api/api_blocked_peers.h"
 #include "api/api_cloud_password.h"
 #include "api/api_self_destruct.h"
 #include "api/api_sensitive_content.h"
@@ -24,31 +23,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "settings/settings_privacy_controllers.h"
 #include "settings/settings_websites.h"
 #include "base/timer_rpl.h"
-#include "boxes/edit_privacy_box.h"
 #include "boxes/passcode_box.h"
-#include "boxes/auto_lock_box.h"
 #include "boxes/sessions_box.h"
 #include "ui/boxes/confirm_box.h"
 #include "boxes/self_destruction_box.h"
 #include "core/application.h"
 #include "core/core_settings.h"
 #include "ui/chat/chat_style.h"
+#include "ui/effects/premium_top_bar.h"
 #include "ui/text/format_values.h"
 #include "ui/text/text_utilities.h"
 #include "ui/toast/toast.h"
-#include "ui/wrap/vertical_layout.h"
 #include "ui/wrap/slide_wrap.h"
 #include "ui/wrap/fade_wrap.h"
 #include "ui/widgets/shadow.h"
-#include "ui/widgets/labels.h"
-#include "ui/widgets/buttons.h"
 #include "ui/widgets/checkbox.h"
-#include "ui/layers/generic_box.h"
 #include "ui/vertical_list.h"
+#include "ui/rect.h"
 #include "calls/calls_instance.h"
-#include "core/core_cloud_password.h"
 #include "core/update_checker.h"
-#include "base/platform/base_platform_last_input.h"
 #include "lang/lang_keys.h"
 #include "data/data_session.h"
 #include "data/data_chat.h"
@@ -62,9 +55,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "styles/style_settings.h"
 #include "styles/style_menu_icons.h"
 #include "styles/style_layers.h"
-#include "styles/style_boxes.h"
 
 #include <QtGui/QGuiApplication>
+#include <QtSvg/QSvgRenderer>
 
 namespace Settings {
 namespace {
@@ -73,6 +66,53 @@ constexpr auto kUpdateTimeout = 60 * crl::time(1000);
 
 using Privacy = Api::UserPrivacy;
 
+[[nodiscard]] QImage PremiumStar() {
+	const auto factor = style::DevicePixelRatio();
+	const auto size = Size(st::settingsButtonNoIcon.style.font->ascent);
+	auto image = QImage(
+		size * factor,
+		QImage::Format_ARGB32_Premultiplied);
+	image.setDevicePixelRatio(factor);
+	image.fill(Qt::transparent);
+	{
+		auto p = QPainter(&image);
+		auto star = QSvgRenderer(Ui::Premium::ColorizedSvg());
+		star.render(&p, Rect(size));
+	}
+	return image;
+}
+
+void AddPremiumStar(
+		not_null<Ui::SettingsButton*> button,
+		not_null<Main::Session*> session,
+		rpl::producer<QString> label,
+		const QMargins &padding) {
+	const auto badge = Ui::CreateChild<Ui::RpWidget>(button.get());
+	badge->showOn(Data::AmPremiumValue(session));
+	const auto sampleLeft = st::settingsColorSamplePadding.left();
+	const auto badgeLeft = padding.left() + sampleLeft;
+
+	auto star = PremiumStar();
+	badge->resize(star.size() / style::DevicePixelRatio());
+	badge->paintRequest(
+	) | rpl::start_with_next([=] {
+		auto p = QPainter(badge);
+		p.drawImage(0, 0, star);
+	}, badge->lifetime());
+
+	rpl::combine(
+		button->sizeValue(),
+		std::move(label)
+	) | rpl::start_with_next([=](const QSize &s, const QString &) {
+		if (s.isNull()) {
+			return;
+		}
+		badge->moveToLeft(
+			button->fullTextWidth() + badgeLeft,
+			(s.height() - badge->height()) / 2);
+	}, badge->lifetime());
+}
+
 QString PrivacyBase(Privacy::Key key, Privacy::Option option) {
 	using Key = Privacy::Key;
 	using Option = Privacy::Option;
@@ -137,6 +177,9 @@ void AddPremiumPrivacyButton(
 		container,
 		rpl::duplicate(label),
 		st));
+
+	AddPremiumStar(button, session, rpl::duplicate(label), st.padding);
+
 	struct State {
 		State(QWidget *parent) : widget(parent) {
 			widget.setAttribute(Qt::WA_TransparentForMouseEvents);
@@ -246,18 +289,22 @@ void AddMessagesPrivacyButton(
 		not_null<Ui::VerticalLayout*> container) {
 	const auto session = &controller->session();
 	const auto privacy = &session->api().globalPrivacy();
-	AddButtonWithLabel(
+	auto label = rpl::conditional(
+		privacy->newRequirePremium(),
+		tr::lng_edit_privacy_premium(),
+		tr::lng_edit_privacy_everyone());
+	const auto &st = st::settingsButtonNoIcon;
+	const auto button = AddButtonWithLabel(
 		container,
 		tr::lng_settings_messages_privacy(),
-		rpl::conditional(
-			privacy->newRequirePremium(),
-			tr::lng_edit_privacy_premium(),
-			tr::lng_edit_privacy_everyone()),
-		st::settingsButtonNoIcon,
-		{}
-	)->addClickHandler([=] {
+		rpl::duplicate(label),
+		st,
+		{});
+	button->addClickHandler([=] {
 		controller->show(Box(EditMessagesPrivacyBox, controller));
 	});
+
+	AddPremiumStar(button, session, rpl::duplicate(label), st.padding);
 }
 
 rpl::producer<int> BlockedPeersCount(not_null<::Main::Session*> session) {

From 0fad42b5b4a7b45e9dc346df54ba7ed20c3aab98 Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Thu, 7 Mar 2024 19:07:05 +0300
Subject: [PATCH 037/108] Added ability to open box for voice restrictions
 without premium.

---
 .../SourceFiles/boxes/edit_privacy_box.cpp    | 59 +++++++++++--
 Telegram/SourceFiles/boxes/edit_privacy_box.h |  6 ++
 .../settings/settings_privacy_controllers.cpp | 82 +++++++++++++++++--
 .../settings/settings_privacy_controllers.h   | 11 +++
 .../settings/settings_privacy_security.cpp    | 27 +++---
 .../settings/settings_privacy_security.h      |  2 +-
 6 files changed, 158 insertions(+), 29 deletions(-)

diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp
index f09b10448..7ad5798ec 100644
--- a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp
+++ b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp
@@ -10,28 +10,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "api/api_global_privacy.h"
 #include "ui/layers/generic_box.h"
 #include "ui/widgets/checkbox.h"
-#include "ui/widgets/labels.h"
-#include "ui/widgets/buttons.h"
 #include "ui/widgets/shadow.h"
 #include "ui/text/text_utilities.h"
 #include "ui/toast/toast.h"
 #include "ui/wrap/slide_wrap.h"
-#include "ui/wrap/vertical_layout.h"
 #include "ui/painter.h"
 #include "ui/vertical_list.h"
 #include "history/history.h"
 #include "boxes/peer_list_controllers.h"
-#include "settings/settings_common.h"
 #include "settings/settings_premium.h"
 #include "settings/settings_privacy_security.h"
 #include "calls/calls_instance.h"
-#include "base/binary_guard.h"
 #include "lang/lang_keys.h"
 #include "apiwrap.h"
 #include "main/main_session.h"
 #include "data/data_user.h"
 #include "data/data_chat.h"
 #include "data/data_channel.h"
+#include "data/data_peer_values.h"
 #include "window/window_session_controller.h"
 #include "styles/style_settings.h"
 #include "styles/style_layers.h"
@@ -67,6 +63,32 @@ void CreateRadiobuttonLock(
 	}, lock->lifetime());
 }
 
+void AddPremiumRequiredRow(
+		not_null<Ui::RpWidget*> widget,
+		not_null<Main::Session*> session,
+		Fn<void()> clickedCallback,
+		Fn<void()> setDefaultOption,
+		const style::Checkbox &st) {
+	const auto row = Ui::CreateChild<Ui::AbstractButton>(widget.get());
+
+	widget->sizeValue(
+	) | rpl::start_with_next([=](const QSize &s) {
+		row->resize(s);
+	}, row->lifetime());
+	row->setClickedCallback(std::move(clickedCallback));
+
+	CreateRadiobuttonLock(row, st);
+
+	Data::AmPremiumValue(
+		session
+	) | rpl::start_with_next([=](bool premium) {
+		row->setVisible(!premium);
+		if (!premium) {
+			setDefaultOption();
+		}
+	}, row->lifetime());
+}
+
 } // namespace
 
 class PrivacyExceptionsBoxController : public ChatsListBoxController {
@@ -363,10 +385,29 @@ void EditPrivacyBox::setupContent() {
 		content,
 		_controller->optionsTitleKey(),
 		{ 0, st::settingsPrivacySkipTop, 0, 0 });
-	addOptionRow(Option::Everyone);
-	addOptionRow(Option::Contacts);
-	addOptionRow(Option::CloseFriends);
-	addOptionRow(Option::Nobody);
+
+	const auto options = {
+		Option::Everyone,
+		Option::Contacts,
+		Option::CloseFriends,
+		Option::Nobody,
+	};
+	for (const auto &option : options) {
+		if (const auto row = addOptionRow(option)) {
+			const auto premiumCallback = _controller->premiumClickedCallback(
+				option,
+				_window);
+			if (premiumCallback) {
+				AddPremiumRequiredRow(
+					row,
+					&_window->session(),
+					premiumCallback,
+					[=] { group->setValue(Option::Everyone); },
+					st::messagePrivacyCheck);
+			}
+		}
+	}
+
 	const auto warning = addLabelOrDivider(
 		content,
 		_controller->warning(),
diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.h b/Telegram/SourceFiles/boxes/edit_privacy_box.h
index c715ef473..cfdc14ad7 100644
--- a/Telegram/SourceFiles/boxes/edit_privacy_box.h
+++ b/Telegram/SourceFiles/boxes/edit_privacy_box.h
@@ -88,6 +88,12 @@ public:
 	virtual void saveAdditional() {
 	}
 
+	[[nodiscard]] virtual Fn<void()> premiumClickedCallback(
+			Option option,
+			not_null<Window::SessionController*> controller) {
+		return nullptr;
+	}
+
 	virtual ~EditPrivacyController() = default;
 
 protected:
diff --git a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp
index 6620c4bbb..503ea62e4 100644
--- a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp
+++ b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp
@@ -19,7 +19,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "boxes/peers/prepare_short_info_box.h"
 #include "calls/calls_instance.h"
 #include "core/application.h"
-#include "core/core_settings.h"
 #include "data/data_changes.h"
 #include "data/data_file_origin.h"
 #include "data/data_peer_values.h" // Data::AmPremiumValue.
@@ -31,34 +30,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "editor/photo_editor_layer_widget.h"
 #include "history/admin_log/history_admin_log_item.h"
 #include "history/history.h"
-#include "history/history_item.h"
 #include "history/history_item_components.h"
-#include "history/view/history_view_element.h"
 #include "history/view/history_view_message.h"
 #include "lang/lang_keys.h"
 #include "main/main_session.h"
 #include "settings/settings_premium.h"
 #include "settings/settings_privacy_security.h"
 #include "ui/boxes/confirm_box.h"
-#include "ui/cached_round_corners.h"
 #include "ui/chat/chat_style.h"
 #include "ui/chat/chat_theme.h"
-#include "ui/image/image_prepare.h"
-#include "ui/image/image_prepare.h"
 #include "ui/painter.h"
 #include "ui/vertical_list.h"
 #include "ui/text/format_values.h" // Ui::FormatPhone
 #include "ui/text/text_utilities.h"
+#include "ui/toast/toast.h"
 #include "ui/widgets/checkbox.h"
-#include "ui/wrap/padding_wrap.h"
 #include "ui/wrap/slide_wrap.h"
-#include "ui/wrap/vertical_layout.h"
 #include "window/section_widget.h"
 #include "window/window_controller.h"
 #include "window/window_session_controller.h"
 #include "styles/style_chat.h"
 #include "styles/style_chat_helpers.h"
-#include "styles/style_boxes.h"
 #include "styles/style_settings.h"
 #include "styles/style_info.h"
 #include "styles/style_menu_icons.h"
@@ -1377,6 +1369,78 @@ auto VoicesPrivacyController::exceptionsDescription() const
 	return tr::lng_edit_privacy_voices_exceptions();
 }
 
+object_ptr<Ui::RpWidget> VoicesPrivacyController::setupBelowWidget(
+		not_null<Window::SessionController*> controller,
+		not_null<QWidget*> parent,
+		rpl::producer<Option> option) {
+	using namespace rpl::mappers;
+
+	auto result = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
+		parent,
+		object_ptr<Ui::VerticalLayout>(parent));
+	result->toggleOn(
+		Data::AmPremiumValue(&controller->session()) | rpl::map(!_1),
+		anim::type::instant);
+
+	const auto content = result->entity();
+
+	Ui::AddSkip(content);
+	Settings::AddButtonWithIcon(
+		content,
+		tr::lng_messages_privacy_premium_button(),
+		st::messagePrivacySubscribe,
+		{ .icon = &st::menuBlueIconPremium }
+	)->setClickedCallback([=] {
+		Settings::ShowPremium(
+			controller,
+			u"voice_restrictions_require_premium"_q);
+	});
+	Ui::AddSkip(content);
+	Ui::AddDividerText(content, tr::lng_messages_privacy_premium_about());
+
+	return result;
+}
+
+Fn<void()> VoicesPrivacyController::premiumClickedCallback(
+		Option option,
+		not_null<Window::SessionController*> controller) {
+	if (option == Option::Everyone) {
+		return nullptr;
+	}
+	const auto showToast = [=] {
+		auto link = Ui::Text::Link(
+			Ui::Text::Semibold(
+				tr::lng_settings_privacy_premium_link(tr::now)));
+		_toastInstance = controller->showToast({
+			.text = tr::lng_settings_privacy_premium(
+				tr::now,
+				lt_link,
+				link,
+				Ui::Text::WithEntities),
+			.st = &st::defaultMultilineToast,
+			.duration = Ui::Toast::kDefaultDuration * 2,
+			.multiline = true,
+			.filter = crl::guard(&controller->session(), [=](
+					const ClickHandlerPtr &,
+					Qt::MouseButton button) {
+				if (button == Qt::LeftButton) {
+					if (const auto strong = _toastInstance.get()) {
+						strong->hideAnimated();
+						_toastInstance = nullptr;
+						Settings::ShowPremium(
+							controller,
+							u"voice_restrictions_require_premium"_q);
+						return true;
+					}
+				}
+				return false;
+			}),
+		});
+	};
+
+	return showToast;
+}
+
 UserPrivacy::Key AboutPrivacyController::key() const {
 	return Key::About;
 }
diff --git a/Telegram/SourceFiles/settings/settings_privacy_controllers.h b/Telegram/SourceFiles/settings/settings_privacy_controllers.h
index 2c214bfec..25920a1f0 100644
--- a/Telegram/SourceFiles/settings/settings_privacy_controllers.h
+++ b/Telegram/SourceFiles/settings/settings_privacy_controllers.h
@@ -18,6 +18,9 @@ class SessionController;
 
 namespace Ui {
 class ChatStyle;
+namespace Toast {
+class Instance;
+} // namespace Toast
 } // namespace Ui
 
 namespace Settings {
@@ -280,8 +283,16 @@ public:
 	rpl::producer<QString> exceptionBoxTitle(
 		Exception exception) const override;
 	rpl::producer<QString> exceptionsDescription() const override;
+	object_ptr<Ui::RpWidget> setupBelowWidget(
+		not_null<Window::SessionController*> controller,
+		not_null<QWidget*> parent,
+		rpl::producer<Option> option) override;
+	Fn<void()> premiumClickedCallback(
+		Option option,
+		not_null<Window::SessionController*> controller) override;
 
 private:
+	base::weak_ptr<Ui::Toast::Instance> _toastInstance;
 	rpl::lifetime _lifetime;
 
 };
diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.cpp b/Telegram/SourceFiles/settings/settings_privacy_security.cpp
index 3f80fe54a..ed4894275 100644
--- a/Telegram/SourceFiles/settings/settings_privacy_security.cpp
+++ b/Telegram/SourceFiles/settings/settings_privacy_security.cpp
@@ -164,6 +164,7 @@ rpl::producer<QString> PrivacyString(
 	});
 }
 
+#if 0 // Dead code.
 void AddPremiumPrivacyButton(
 		not_null<Window::SessionController*> controller,
 		not_null<Ui::VerticalLayout*> container,
@@ -283,6 +284,7 @@ void AddPremiumPrivacyButton(
 		});
 	});
 }
+#endif
 
 void AddMessagesPrivacyButton(
 		not_null<Window::SessionController*> controller,
@@ -328,7 +330,7 @@ void SetupPrivacy(
 			rpl::producer<QString> label,
 			Key key,
 			auto controllerFactory) {
-		AddPrivacyButton(
+		return AddPrivacyButton(
 			controller,
 			container,
 			std::move(label),
@@ -366,12 +368,15 @@ void SetupPrivacy(
 		tr::lng_settings_groups_invite(),
 		Key::Invites,
 		[] { return std::make_unique<GroupsInvitePrivacyController>(); });
-	AddPremiumPrivacyButton(
-		controller,
-		container,
-		tr::lng_settings_voices_privacy(),
-		Key::Voices,
-		[=] { return std::make_unique<VoicesPrivacyController>(session); });
+	{
+		const auto &phrase = tr::lng_settings_voices_privacy;
+		const auto &st = st::settingsButtonNoIcon;
+		auto callback = [=] {
+			return std::make_unique<VoicesPrivacyController>(session);
+		};
+		const auto voices = add(phrase(), Key::Voices, std::move(callback));
+		AddPremiumStar(voices, session, phrase(), st.padding);
+	}
 	AddMessagesPrivacyButton(controller, container);
 
 	session->api().userPrivacy().reload(Api::UserPrivacy::Key::AddedByPhone);
@@ -873,7 +878,7 @@ object_ptr<Ui::BoxContent> CloudPasswordAppOutdatedBox() {
 	});
 }
 
-void AddPrivacyButton(
+not_null<Ui::SettingsButton*> AddPrivacyButton(
 		not_null<Window::SessionController*> controller,
 		not_null<Ui::VerticalLayout*> container,
 		rpl::producer<QString> label,
@@ -883,13 +888,14 @@ void AddPrivacyButton(
 		const style::SettingsButton *stOverride) {
 	const auto shower = Ui::CreateChild<rpl::lifetime>(container.get());
 	const auto session = &controller->session();
-	AddButtonWithLabel(
+	const auto button = AddButtonWithLabel(
 		container,
 		std::move(label),
 		PrivacyString(session, key),
 		stOverride ? *stOverride : st::settingsButtonNoIcon,
 		std::move(descriptor)
-	)->addClickHandler([=] {
+	);
+	button->addClickHandler([=] {
 		*shower = session->api().userPrivacy().value(
 			key
 		) | rpl::take(
@@ -901,6 +907,7 @@ void AddPrivacyButton(
 				value));
 		});
 	});
+	return button;
 }
 
 void SetupArchiveAndMute(
diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.h b/Telegram/SourceFiles/settings/settings_privacy_security.h
index dfa7f1299..4fedfce81 100644
--- a/Telegram/SourceFiles/settings/settings_privacy_security.h
+++ b/Telegram/SourceFiles/settings/settings_privacy_security.h
@@ -26,7 +26,7 @@ object_ptr<Ui::BoxContent> EditCloudPasswordBox(
 void RemoveCloudPassword(not_null<Window::SessionController*> session);
 object_ptr<Ui::BoxContent> CloudPasswordAppOutdatedBox();
 
-void AddPrivacyButton(
+not_null<Ui::SettingsButton*> AddPrivacyButton(
 	not_null<Window::SessionController*> controller,
 	not_null<Ui::VerticalLayout*> container,
 	rpl::producer<QString> label,

From 0c991466f5b1918178d0d878af6b86050b997b7b Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Mon, 19 Feb 2024 11:35:53 +0400
Subject: [PATCH 038/108] Update API scheme to layer 175. Business promo.

---
 Telegram/CMakeLists.txt                       |   2 +
 Telegram/Resources/art/business_logo.png      | Bin 0 -> 48042 bytes
 Telegram/Resources/icons/menu/shop.png        | Bin 0 -> 687 bytes
 Telegram/Resources/icons/menu/shop@2x.png     | Bin 0 -> 1239 bytes
 Telegram/Resources/icons/menu/shop@3x.png     | Bin 0 -> 1770 bytes
 .../premium/business/business_away.png        | Bin 0 -> 662 bytes
 .../premium/business/business_away@2x.png     | Bin 0 -> 1190 bytes
 .../premium/business/business_away@3x.png     | Bin 0 -> 1806 bytes
 .../premium/business/business_chatbots.png    | Bin 0 -> 499 bytes
 .../premium/business/business_chatbots@2x.png | Bin 0 -> 928 bytes
 .../premium/business/business_chatbots@3x.png | Bin 0 -> 1405 bytes
 .../premium/business/business_hours.png       | Bin 0 -> 426 bytes
 .../premium/business/business_hours@2x.png    | Bin 0 -> 765 bytes
 .../premium/business/business_hours@3x.png    | Bin 0 -> 1130 bytes
 .../premium/business/business_location.png    | Bin 0 -> 454 bytes
 .../premium/business/business_location@2x.png | Bin 0 -> 845 bytes
 .../premium/business/business_location@3x.png | Bin 0 -> 1226 bytes
 .../premium/business/business_quick.png       | Bin 0 -> 615 bytes
 .../premium/business/business_quick@2x.png    | Bin 0 -> 1083 bytes
 .../premium/business/business_quick@3x.png    | Bin 0 -> 1520 bytes
 .../icons/settings/premium/status.png         | Bin 497 -> 568 bytes
 .../icons/settings/premium/status@2x.png      | Bin 787 -> 1093 bytes
 .../icons/settings/premium/status@3x.png      | Bin 1166 -> 1616 bytes
 Telegram/Resources/langs/lang.strings         |  18 +
 Telegram/Resources/qrc/telegram/telegram.qrc  |   1 +
 .../SourceFiles/boxes/premium_preview_box.cpp |  12 +
 .../SourceFiles/boxes/premium_preview_box.h   |   1 +
 Telegram/SourceFiles/mtproto/scheme/api.tl    |  17 +-
 Telegram/SourceFiles/settings/settings.style  |   8 +
 .../settings/settings_business.cpp            | 568 ++++++++++++++++++
 .../SourceFiles/settings/settings_business.h  |  37 ++
 .../SourceFiles/settings/settings_main.cpp    |  14 +
 .../SourceFiles/settings/settings_premium.cpp |  15 +-
 .../ui/effects/premium_top_bar.cpp            |  60 +-
 .../SourceFiles/ui/effects/premium_top_bar.h  |  15 +
 Telegram/SourceFiles/ui/menu_icons.style      |   1 +
 36 files changed, 756 insertions(+), 13 deletions(-)
 create mode 100644 Telegram/Resources/art/business_logo.png
 create mode 100644 Telegram/Resources/icons/menu/shop.png
 create mode 100644 Telegram/Resources/icons/menu/shop@2x.png
 create mode 100644 Telegram/Resources/icons/menu/shop@3x.png
 create mode 100644 Telegram/Resources/icons/settings/premium/business/business_away.png
 create mode 100644 Telegram/Resources/icons/settings/premium/business/business_away@2x.png
 create mode 100644 Telegram/Resources/icons/settings/premium/business/business_away@3x.png
 create mode 100644 Telegram/Resources/icons/settings/premium/business/business_chatbots.png
 create mode 100644 Telegram/Resources/icons/settings/premium/business/business_chatbots@2x.png
 create mode 100644 Telegram/Resources/icons/settings/premium/business/business_chatbots@3x.png
 create mode 100644 Telegram/Resources/icons/settings/premium/business/business_hours.png
 create mode 100644 Telegram/Resources/icons/settings/premium/business/business_hours@2x.png
 create mode 100644 Telegram/Resources/icons/settings/premium/business/business_hours@3x.png
 create mode 100644 Telegram/Resources/icons/settings/premium/business/business_location.png
 create mode 100644 Telegram/Resources/icons/settings/premium/business/business_location@2x.png
 create mode 100644 Telegram/Resources/icons/settings/premium/business/business_location@3x.png
 create mode 100644 Telegram/Resources/icons/settings/premium/business/business_quick.png
 create mode 100644 Telegram/Resources/icons/settings/premium/business/business_quick@2x.png
 create mode 100644 Telegram/Resources/icons/settings/premium/business/business_quick@3x.png
 create mode 100644 Telegram/SourceFiles/settings/settings_business.cpp
 create mode 100644 Telegram/SourceFiles/settings/settings_business.h

diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index 64409ea88..fbfbb55f2 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -1295,6 +1295,8 @@ PRIVATE
     settings/settings_advanced.h
     settings/settings_blocked_peers.cpp
     settings/settings_blocked_peers.h
+    settings/settings_business.cpp
+    settings/settings_business.h
     settings/settings_chat.cpp
     settings/settings_chat.h
     settings/settings_calls.cpp
diff --git a/Telegram/Resources/art/business_logo.png b/Telegram/Resources/art/business_logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..25c357e50b1bbc32bf61df5498738adeba3a4a12
GIT binary patch
literal 48042
zcmbq*RaYELur0wI5-hj`3BlbRLVy5C2yO}P?l!o)ySoiKID@;p5AMw1E;rv@>)!JN
z&O>#zt?pIbFS~YCci0alSuAuCbT~LTEV*yezy9gx{{$8JU#U_tZ2C{VbNnSM30FNq
zcJ!|yZLA?@qNoV><zF2Y?p=sE9O8dd{t3xHfrCTHeFul|Prv)GFBks*k3Q!j{7?PA
zi9v9{3^+J(I5}yF-)`?tJ3LQ~HC+~6eQr+6kD<p$+0g6|Q(@4-Uabhi?iTJ3E2{U>
zzPNGmsc`Vj47tyLslsuk-;LtZeU_v?M?r^=qoVpq_bU+h*AGOA_sC?Cy{Tcw!qFk_
zE_dI-<&-x*yH5|*H|wSl?K}0GierzPG21tvyUNBz&#I0)frD&MXv?U>5;l{ahF>o_
znc8Lzd<X%5W!GP%|6P|*b=L;DCD|8YP6D^9ani|+=31}Qp}0LEpuWCnVOO?59W#u;
z%fr^z{qZqs@5tCzpg=;@$52!0+ijpxjKVD<0@&54g?WV_FNF@Ec^>{~tFW)ytv(M>
ztz*OJq*+M`dW+u`ee1*Xgy5dlbYL*qHE&X0NVM7S3|u%KBr98Xzi_gECJ%3h_ks!U
zHZG}IM!0Z3dBN~+li4g8F6kcLh>iibhtqpGS)v`!ryCm-Lb1KKp=z6U-C~jm1^wQ;
z&kuX*I&GlmT*BKsrUuyjN!zvHD{7E?#Sea|`#1cj14MwlDI(zQba~IT0k7GIsYq%g
zprUKbA7IzmIgBR@Yl7(c{J_{*w4m}SA{+ru{k#gxK!o%o?%fm<zD<4x0t)LB!LIvb
zmv=am@Dv-q6s-3sI!_oxHeOFr6{EH-olVckEwA%hn2_HX&n|m&N@wGHkC!;nWZJK;
zTY#&%vJSQlrGr=i?8`sfVY)1Ljn@m>e4+&f_pm9%xA6~;eVzW0^hiKn>oZHyt2!mX
z3sw(Fr*pH@q-4E+`faHz@s=390ai(PXd3C?q7l=`YJs~4mze-$auA=_x_@IAK?~tS
zFkY3)ok^F~^*iiu`@;5IEoWY9q{X~RFQjf+zT}2I)IZX0v*~uWKPxMXb+|$a^Fu0y
zO8=b#IKbeHp}gP$Ot%4>jXP>GtIgR^hQ|6F5KJM805N<3D(P<l(T{J7S=#(_ssCNy
zfyLUpYNk^Lh+xz6dDN%T7>hi>CH8L|8CWO07+6R9^>oKH1zgDVk^<crYpUM=w?}U+
zU?+-n(#-G8r|d<qBfCxNM(V;-#Sb_-mb17#m*ajp+@TJQ8n(q^`e%smj@RpR6wXm_
zs#tmd>ne`&Dvn0O6LWDbKd|=RvUL=uoGIFIxA$}k&FFdlxB57H=gVyrj#=It$2#|A
z8+QvuThu-E6ZLO#jJJHz+vicOd%*KpS_e=h5O;%z6^H0qRH~myrEfd*^^?|N$?bd3
z!(B^tTW0ke&USh?Yt8BtZLY;Yef7=7>T<SH;0SXoioW7?=d`L&?cP9?NT)P{YaVQI
zTBO^9TnO3PR=~&0R=~x|R^ZD<!3zE|QqesT0VIe2*?$uH)5A&D*0udEt31MfaKp>-
zK}zcoAU#0=hGF9zZjKOmSDjtn?A2|~D=elc_%oj}>n$`GEWmFBrskMm$F16&Vz|Vz
z5K^3%x}Y;Z;;p<R6Uzf`T);1${4@{WRZQAf7QN`ZgdA^YySRt25V$#gWqdCpU0U!6
zoPKvcX}&KuR{>pgcItFG8gRJCC%oOW94(6AYd5Rl=4<$K$NR`UuXU=Kev*F>nqAc1
zA(g7oG44qDt}LJ=iRWRFa#urkKd{ru^Rl*J)Q}+tb%9v&ngYGO_PO2vS=qVaO^h=9
zO?K*tJ2B>u6V7Ma^~cBdXn%lK!fgm5rM%)q4!BdNxodmj=QHQ%X9a~)n(fczB&kG&
zW_%J{akejvq-M*SxlQXo(7Xr8;S{{Kfvq2V7EJ<rus3THWK&d1qM1vSPUbeZxFsLZ
z_HIy|PoN4o41uzPC((ZaPX29>IJ0-BN2?ZM9y^bhJ6gX@VNKv=QCJH^6kz=~Prubo
zF+xt(9BppqZ=0vtK2i%5PM(4yBHo3G?6o*Eu@E=rsz-^|PxBe%mjhC@Z&_^nLc4(c
zRv7(UqC}}XVRk_|f8&d?Ho+K7IooveTIu4UTZBT^{b>{Gr~dMOXqo!Z5CmI9sulxI
zm{R=yPWBh#bMkn(psnuQ+9`6dKqn*I=+q@2y{DmZZE<LkXU+BpsGrzaGs^0qxx2I`
zI%CW1P<C2p>Dhsn!&=pj549F*58J4V5pgkcf3|0N<&q5n%02ny3s>&k9UHdiwB{&L
zsIEdBFkaVMNR=($UQ2GHS-}bc4sVlOJUeWM+*vX{TW2S&2H9n}h3fh0v1YLWmT_F-
z3A<f}MN}}U1@fNolrr52fe7$okuEgf`f>SG;CCGS)`XiJL=ZugU^DS;Y-bQt;Xu^(
z0nSkKweIp|ax!~;=cgWo^Gw^K`ze2|?wKbF`UJZ}b(DD-=L53Xg1yHnljfgH+b1xD
zf6j5Hr34FH$**h3vp|Xs_V)XQ*ua`4F1Tn&-Y`Rh(k&k$U!KadP`)QVPqyBGY~NMN
zl7~#F*lXWU-5W>;TQmw43&_`XS}%6O_D!v|G^KT2rfNgvOS?MYBx-MvyZcR{BYHz_
z*Hy54!qN}?`lzaF46c@$+Z#tmqxIeN3qYpnNUJv2l|4(}AefccjyX(w<yCu?;hd+}
z25{;N<?Gyl)5Lnxoor-2IlaULg<3V<jJ$q{TUv3GMrzq~y)D}rJ$eGvD!Z~Wzs>K&
zu!+4^oo{5<J`~#smW!3pzO@!#0y}cTI&T&l_c93uWpC|$GCye$ioUCfHD7I*2Fj>Q
zTG=k@8xKgXYKX0H47uD=)<2~Y-mNxV<Hg(<Q|g<qa^@j4738=-Z6g}WdZgxvLQJ=E
z1nQqc!PWuICOO@HO;&u#;F3b0jOg37k~hWGefy_kg<q6SFM?68`tyIlY{Tm}fuVlH
zBkUz-2NSx}>o#N4%&gI!7TL|;yPy%fXl`xudjx&?$9H?{cbr_<C8s7w*0*QIr>-kZ
z@5`*O!qa)w?2>AcbsNte_Gj`MWHgi4tV#@4pZVE1jqNu~j6Q7HnNdS8g*<Ol*{)UX
zP1gf<F5nAQi?QyKiDVwhAMc^C^asUexedtgvIqQE7VkL_^IMRfs}nk)P12~R-5z@E
z0LAJMTO731JN#?{;`+qmHjDoGD*YzgY^cIm_UD*++uO0Y0&qMiXGsm?)XbyA&j}+p
ztg}(37i>)ooa$ez^{q0Sv=V%#R$p`0R$KJZjJ~_wxv!Hth2#4zh->Ch%5*xKpiD7)
zl8t?G8mIF7rd4t;c02ACt{em|OSjE$>3j~{pT^m^>f=`Ln3^=tI}x-c^iVwYhzXw*
z41&s4%4s?9q_MPh{Hm+zIJf$vYyA;3M^z13BSv!65l>N~ozUIw2k(V*UT*=T+F(nR
zXB5_*qg!&K<}W!nF0lqI0YM9%qjJ3@S+k6|<m>3FOb6$;N<xtcFcuWo3#E|MqZJyr
z7t*<Gvrx7I4rmpYFVF0;%zxpbyAf9LQeU<8UsrnF4mufSV$;{G)@c!C$ywIYK1c6z
ztO`EGx6vb>Y4jKtSjehC6xFe1cBwG_o4}&VZ*Lw}k$|*(Dv{y7*%0V3I}JXw8XXtp
zq7a(GUie}$Ix?(ylMxjP&^dWIGL2>{xo#1ys$=1gK#sXlx^lT>8}7G#GZE}p-Y6Sv
zBebY}lSO}JGxyJGPm!}5c^zR=f4<O)&h)#te>d1yd6X!pZXa3<P+l8CqMy1((llQ{
zp`3mHdu|_yi!f9c1dhrury12F#MAvVR{gRJtWwuFa}g!|yhETiJDOx1Vo8l=OA11|
zm<Dc;4O=1j)gq)4=jVtPK^A6uz>AQ5m2WA(L6i&mZ#kwZFtv7tXB#Z=_JYCNOZwnj
zmWM2-rZ0*)K9%4Q3QG3u)Agc$TkG29HMt2N;lvj-HJ#c1a=GP_N<FO4N`#UMH=0P?
zhYeX3lANJ31O6Sg!uRY;&4h8U^pk2ep6?M>?aYN<lT2ODRz@_UGw8`adkqUde@xr-
zaf+yvs3yrn;x$#)-|;>!6Y>Nof2n`l>?+eusd)Py@jCgv!IiF+#|hR+c0E+~_BG%l
z1c&570y)*Ttm6_(38Fc|bA3gF!y>BNm+i&#cfVh6rn7DxBaIy1I!Y#0h;Ov+-Wu=8
z{0iMNqn5{o@QT}pcut}Exdi3(obD$voNEUVqKv%?DpxduH^d-bi9VOn{iMJuu0>O>
zXDx+6rqsV*(FU8I&R%tGTJ-_dI<^%zzZFuH^)D7f8d-R}7wK|k9Hk-JZxQLg!KT=+
zS(u+8Yyef*EM(>d%3P0MLa)kV;1$$q(DAvyb|dwOAnHa&TUk~}es`wsa})J*+E>6r
z0gSiUhb?Q$PhbYfx#dE8L_+_ZrruB;_z{gU{0Qgs*E-S$7fKH&+u-^GyEf1fcC!=+
zZ!@~%uKggjvo{91-l(BGwg_Z-+rcwUp`4yL*M0DGAKrnMU&J;v*eP|s>|cTpF-)H0
zM_E>HeT_$KmEod~A#O*P4yq8DlKHap;yH^7ddMmNWt-6g&0XPq;MlgZF!T1*7dUhC
zqErZ;8(IHX{dhF{sRh(AW(1Ye)d(=n=c=CRj2Nh9Yn-Ng#j*qC%L+s7l)0nXzp)R@
zH~}HiJi72Fu1)<3hNWHA*5A$&Ct;AMjT^HZRH24%koSp4;3C`gm#6aHBB5Vih=D4L
zvQ&uyL0vH-WMZAq)^cy<x+H1na;Gi`k!vR&z}J-ub+5S|6D#~qt#oVaoNd{X!h>N3
zd9H){U@atj`q6Zm#!UsEfv^CRm3EKtcAI(w`6XV7)tB)K(|zjZ+RQK7Yb|9NS05i8
zKiZR`0{Y}jR>yFxgSh|$2@=O*Cz0a3p~KSBNb;4+S@W`Pt@fsEMZ9v38I(C)Q7(oL
z!%XTakb9e_xX72wwV|`0N9W>lih<yQ#XI_QZ<Jj)i7TTtAYkXxK>{LIviDinO_7AF
zSdr!W`)e4rlM0wn<O69!xjHU}0fVyBK_Y=MwWfHdbJf!0l!bkN`eyK{PF{GY@Z!RB
zqt|xjn95&I(u}|#&?YO_)Mz6V3f+5^`;0AK+vLV}UV`_lJOS5ZQjEbrS1e+VwB08~
zJ=Z1=R}I!x)uEy#i$DOM2>+~eYlRhM%`Y*LTL#t5n6YX(s#$E0^Yjne5!SzU9i0h<
zaNMOWlP0HIm~Ceb4Q?>h2+=cc)JLx-;2vtP6LH5Q2m^iCx~3x5oSZXvlGiF{Ka?i1
z{CVO4Vv3Us6&&I!E}i68yHyZ2R(Yw_Ynrz8)aCGad2U*FuN5bb0%UntWi~b%HRti{
zE1kvsFddM(23?pRb*6eZKtZK&y$Ca)Jpu@LZXEzVUavb$kTt#vwRz%Oef2AU=~zVq
zXT(0lM<{RM<YoqG1&tU$!x&ziCGl|*n5sOSzvTQ%=&q#9sDG7bGMpL4oE%A2@2N7o
z1zD=tTYdH9VrlvF*AuHsjSq4aljWC`c=Cb@a26$`rse!r#KMWP&fi4|Il>ovkYRW%
z3$*^8k##9JbHaD-bJMnUE$XhsD56yw?Zq&eqyJi+J#l<@DuhuT%O&2l4LQcot~}@`
z_gtM1ebMYZUoo)<9$=otr#tK1XZ!LDOV`@p@E)2e;%T-El7@OYPA^-KmOSb?qkgMa
z&s|p(Tzwg?DEyk(Lt?AKG5wd}IZx&JKSVa2;fk8~E)TDI=R-EW`on*itjZ>V%f_Gg
zZW~{W!np#boc+()Zk2tJ+!&|)T}%=KHy0?9Z#wY;^w?i(79Z@I_T{qKX5kfXd$H87
zxI4l8z!KrmM%i`>H{;)nA$s*C+l){;8myS5Lze>jxbIHSL^5t<X)~p*&$;#R#r>v%
zpi!OVu~EJzOQwjdPSVO3F<k9dQW{hEC0qq@_RB#yP-&3P^yhO!;th+Qpg|yCPLI&5
zOTZ-d;9qRKZK*--hM`UK{xaf33j)9nJb~kl=^HN<XAH6D7ShH-oaKXL>+C!7d!Og#
z1K-`2askVJS*NRSD1!&5BvX*Yd<r8tm-&Y}C;+fkk>W)u{JgwT?L#lmCWa&EMlX3z
zY{2kLAO$bNk90x4;wxggLu9`iUt=uXD}z@!$$W=sa~Q_DF;GZPMFN^GSyqy<<EnZf
zXL(O0huEDU8${aq=Y6mV5{|b6n=lmbS9g3s3i}yIDw??=4|1RDS6cO0R#>6>9kU3D
zsJ%R+LgRWYZiJh7G<(-{C9?Mf7bUR;(w7;PA^Tpm4x<y6`y}G{6NeM-D%aQ19%`wt
zDW)$rL}ip-Ia}ON(*z1<<~CA?FcG+)N@$){t69iT%Yt3e(*;0OL=(&2KM`#$XjEyp
zyA~@a;Tx<TtF#HVKHVBnaR&EE&Vrs6SP&{ElO=c``T>Mg-g6^fq&0eG^L{bWH#;Tk
zDK^7f5g>E(vq7<1bsaiz86Hyvpy;5V2zc)bXMo4QQ_DVUBJ9=w=9$`dBxYeZ{`fxp
zv>h6?$YApJQXR|bi<X%m0%8H^;Y2+TEV3!$M6V?;>GQ?cpJ~kz@5iT_XSgY*PJP)}
zG5H(#RqS4H+ymtYllTO!U+j7clGW8DVh9hmpW+>Qgs2@ivHezhauV;o1ao4v_fiLA
ze{T=IVi1GY^kVF(xkHHB#5W7SFQgX-Km1Xm#m^b<<je>7j0B^Tl1tC^I1FfSgz}m0
zCl8Z#vMW-A3#Rjc#kbvRZKITK_^=K!Gm57|SJ?3D!fMnWu+&cUX^GVhJ^kHsG}Wc;
z(H1@%*vlPr&BtN&I^ep(tdZ&yCHT622m?4w&~omld6+b7!S-06gY_gbYnfjy6s*P1
z9O(QZ1}zGkDPZBzaNekY?0G7d(ybh-8?^&8Rw1UH?b8<~?P3gFX`hPtpw+_mTK&T$
zon_d120f6%s}Kz<Sc`^Sl1q_aa8kHR2Ds_~E8Z&cKFkXqg?Y_&8m9=ZDcaoWguv>V
za=}&FE6VgWn7DT88R)`G!qS(j>jJVBG6a@VM*6h3xJ!MDNW^>R2nwc2+6<x~kH7Mt
zTPhpseze$ZJ6{{oghPqJE5r#d=cpv!JVrqWht3J7$vRAE4L-$HXX5>^vsIVRjd<}c
zJvrY42~jaJ7q*0aJe4)@CLLA+Rc`{cc!DW}=$w5q1wTT&4<>|HW3#L&Yn_-nzuTXj
z+0Nwyj40BQgz*=9t+h^{*Je!#Vk!0Fx;R5o7}<&2%VE|3{5Jx{OpT>6%QtdyVe64e
z5oY>mMGuyqG~S(QZKC_)EZu8W6a~%Qj2iwL8TE^rfW7hW(zD=)IE+3x7H<hz6NF8b
z(UE1}^Hs}3wH8Kr*~V=5LcKkNrZDhv+p!Ssa^wRKj!?P_ZiXrLG!vt#>U^B}6Q1EX
zyEDs?8{k*dPUnHhpaKvp4^OmkQ+x$m6leNAlJMoz1($kEpNtR|4Z$1fmj#Vee0CX3
zCz&Q*Vs0wFxpVT{7eb+O9+9TVW>STWc1=<HK*A_{{Or?^5UXpNlrm(TmNbghIL(qD
z!^_pYT_Y;Dnph1qB<vN6jgqw*JnJmFCY`2l^0vcuL~Wp`VrKab-YHAVXh`3W)=%yk
zO+4ETd4%!gVbq!z>VqaSEN1<+#E<zJP3vRV)XYZ3tI8)^jjAk4&z69_jy&14j=ZK|
z#}oNB5SQraSo5hU+ZK3BK$MH(!|ONZ`=$-8*XHm$1bjQp?>lMu#D7M=q4I-2epK4w
zdO#>UiJUE|il}^ivUVK~3V!1*mg`sf4N6kD={Bb$O4HCf`jJnxT$dE4fk8&%F0&Dl
z(`o8HY&F&HZ$$jDr3*g=ZrV1jtI;PZ=;?O$a-!V5+8;}`T4|m%G%3S5jAtiz?F;i@
z44s$NlysozY$aq%vP2>xlA6Hcj%7PN$i&@}FvVjfx+wxS<Rzs{53-%ML7D!F%-`YT
zE5JG<Wt+4og&A;nRz3##i7Et)A48%Y!{_qrJ+OFQ)Nh@W3Yvos{4rTRO>X9!W1d@H
zu_5`8G@WT`bU-E*@pU;GF@dRN_?OAEl{PmU4QZ^N+E-$&r&Xu>yr}uw$0)(~zgdUP
z_PVrp^qr%eKYyETN-8e0%{Jfrbo6ePS2FYP+>}BIw8X(7L3^%>CH4>L^oWJWF{|4Y
zYN=6iW@H9+3;+R3umjtiC$+h|x)aK<C2i4)cf^xX5Kztx!xTOj61Z@3S>G*>(36}X
zb%|t74VA^EAm<fxQHDzehONBT*04AXc4#tR^`<p+<WqJ(u^lRf+ocfsw5?U_F0~*@
zms9V5%B{mjRL_*MO(JiP|9hc_wC@t!(gz{}5SMBM*EZ*nW0Gt+StKQcWD=|ol6`8&
zrM?0K`K3ORwkOL7y}w9YGtCj&3Na4my~>-mDxrQEA~|lkyL?Iuc`zixIZM<_h_k;(
zo8R@u4~y3C_VbvAuV}Jy_dktb+jySNKxsA&WU#Any8s^Z{berXCLEAhDX(QDx4y5}
zEv#ejR@dVWCwx1<I*{YKR;YK=&?0TvJ$`0H<$lprXe?_jHQgw;{LT$yMpS>5|L6H+
zP;hImTOGH(t}d%1Cz&7hy{3!A<!}6omf+4{CSiVbuj(|czc%$*W+va=LgM{mFpIe2
zop4LIF4*Sva-!}fn{7>9pl-Jr*}J+Q(WUc-wikRTWsYOreIha*Q$__2F8}e-mY!^-
zng{{uB^^5EzGb=gyTE+x@DT8w;wHdQ&^jYoj^Lv}F44gEB@~9_2HgTwu?Ql8QyQX<
z^TR<?mhXiH7<44Ye~a)#&We3x5?1i_k*O;}r>e|D3#y4<&_bYu=|Y_F9qy`PH}&DU
zD3o!AB{Zi)bAv?49mIzF&18qi#k+-U#(LLTQYVAP({a>;@%1QMpJ;_`E@I1?`w)~-
zz77ude%GHNcURx{goUBJ=f&GtubU^VsaOR5g<^Pr9<LQOn|uzhAbX>{te`|$Tp)mc
z_D7fpwGtk1{AN3hV80QvVJUe(=5NBz^@oS}NWJ{LCf&yz^2DOjMh~sBUi_dzSI|T$
zvanF+thZ}q^5E<<!js%|Mn0=+e9EoznazQD>6}+LxJ;iwnRn5Hp5p44)1V}o1qEho
zcy#CrT%UvKn3^o4nAw7f0_ehaL?V`9Q(vE9-TG3wh2!0JHeRVhi&^18ut3%UfI(ki
zV)nTx=7~u>PcE{1Z&@K16^**p*gNmwd~$SgFz++bAAHuVi3(3n1ZpYYWI9a60G*<&
ze+@kANb=)`?apP$UuI0U*4pZ5!2(3*XS}21?anB0jYRb|TF8zs;p)Hofy^V9vy)`n
z*`C=4Acc~Tp&pV~iakX7bF%5e@!Ab5n%Cbnblno`INLuk>wMjW<!>38wh07$umlr?
zt=nW|jjOs4JMuGil3!LfS<h~zcs$^XCYh`8OxUi7y>;R>Gj$i%)3s1)zv?x+SU+r_
zygXgwJSXbmL6ytC!AiP_W4o%f&Q32ig(9+oVhc2ai>(&ejb%6A#TAhqaR}~QTcb!@
z>z&r?L?_ZUdbU~mk6ry4u)XAY$Uf_1Yu!9IC~-m7$e)icueX&y@_J5)VWGU=yx98)
z9^ocU!T0vbyOE+4?DV?ciCOMUvi_Of&S_C%p@UwnKp>QlWJ=QPEVNR|PP@9Vbs}*5
zBk$(@!U6`{aGg2P{uWb95~9&|pK4Iu3Od%~+F>v<lwJD|DL6Nx|3D8mc_<_!9RTmX
ztkk=NQr-cv%sWQ`SwnCJAP-l3o%%5kFCkw7DadrJS<w?oG8@kw=Rb4Gx)P~;ij~pD
zwdOxuC>)cVfa7|nwo)wWLF%<UG8fRKKg%r`tHUx8txR%QG~Pbty9cH<P1{^$Z=EAN
zITw2kOBfBaFL0o}4H53RBLetg<XKkLS?oWqNvl|oGDH(dghy9w`nf=6^4;5!u?jg$
zpU2D+vXaJ|^sfKXorKwzyFG<)KQu*=*KuxZoMJ@q_<Y|!$Puf}oV$XBEnyY*IPoze
zm}{S&$VRii?H|p0im9$WfxZ98x+x}$u-u9P69)_!I`GjC4cPg67=MH#?wa#Jrnn9r
z;;IQ7jq01HO@H$-8w9)~AXXaXq@oj=PPdgf|I3iLexB=a1)pPIX>(_Y*D6`%ftqnt
z9cNyopXQoR{-gpEaFC72(r{Z?#cM5^OBP0EB~&UmA^sSCyh2zwRL1Bch&bBCq%|5{
z8JpqiGeoCw*s!q;`KpSS&}U0x_<i*}rgA9xA48$ly=4Cxqyj>3jGRuF-3zPzyb77Z
zN#3Mv)xWpWb3zcpJXtARfVcE-oPK%2rdaX^+x>m`Y5YhWK5e+wGsS~tq<-?N{n}Z#
zaJCn{yhxPT>!-}f_5`9hZ$xH(%))V=ad?r~NdecLsn=2)T3Xh6-<sUFuk4nOvA=#A
zvRrf43`>!S&JBs&tzCTR=smldq)fu%A+8r8`ikG($N7B%aaW_OYfAGJN|XoED3U4l
zI4T*S+e5X9-)P8{5A!9}x+eF!Tf_x<YxR>FjYMBs)h>~1A(WZ5oDUB=n}=bT>12rG
zR`6M=<V+_x_;5eok&4D5O%fS8`E3vC{xHT*l|(f0MW*#s-OKF6>@6r{eAPhYgvTB?
zh_C3^<s@$2u67Vb|CTJr;pDkpl=DdjC-f{i>+&yP)~Lgs*y+d5R-{PUPJdalHm>mp
zUM|tRY58OseXPd|ym}WdY&fOtUHnF(>hND`4M;E=_n&?z5K)m*r&7z|Xw`tpg$SPu
zT3LmuGse1MjwHsTm0)#~`GhDJ>VUqZAL(k|W*1^HV(+BEo8_?kDXzN58lBnH4BPfR
zn4NSnHC#*AA);3H_`l$T<|jILKogEpH&c*Ul|jx8RVkhZUXa)Pl9N3$ei*~TJ%#&H
zIX+-6)DPhxXTP<jm8Nv(9l6vG$px5&HsrEsGMSY#2pGY_Jv-iyL{TyBX4Vui&1g#}
zvNN4yp5(pPdYlq?1vBV!v^Lmf{d3%&tbsp2(D>s__T@t^iu0vBdER&xx=vzp&N9VV
zHTdG^j>E47A^lbQ?0Ly89vj24F}9Xw*~aY{)`z+K5dTVDaWl&4kcKiKCD$|q8IjXV
zKrD;W@ol~2Q;p5Ali5(t{JwI8Gi23U|J-Z8n%QSa^@j<oK`%actW^L4aRcxqjK}h#
zA{^6e+QMKF5vaw1duwcH+?zDW3)n8OblF*8jDHyCp$f+G8SPLJ$8ICxD6Z1zrw~j(
z48e(PoB>i4HD~Z%mnt)33}!ar+szf7%*te02El625C5d9kCF_EHZ%~2m@NM;qCpk-
zoiBfN-};HQnhdZN*G&ry{D2B5c~T2(8i{x1ko1%RD-1Q#Cm2Jjn{u63?kd@eD32Rw
zHlR4oKn%TraoeY7fsWgFJ{dU5epKu=3$_!QET4(NgfWasFLZf1o<tGiHC4)1l-U7{
z5H$qJq#`=MYxN8!a-z6*lZdSP{Slb#EMAX>ae?kAb_>o~T0|a`z`xdeK(bx6N8Ap>
zKEkpj?uzO;HX*o8EsV$%WjTcsi=WLW>PWJcZbw`iS@Tnjc@lSj@=_`*4f7-%E63Sf
zWK{U4Q}ovi7%zMAjSdaOKP7K=Z6<#Zd(ouKd?MC6^CUh+^!&`jU9I?s+6C+9uhE<Y
zSOx$GgY>R+eGv)B;Py&wd?9B*N2+H9kAa3j-_JVql+X8M8K(G4k;D=~Z)_nf&?;k5
zG`7T+R|qcl!ylRjqrQW3{S5_^;!hE&&NUxot$m=2_)H2gC!tmO#5|;8R%voA<8!6<
z#e5}+T3Hb*b<#h3nXM}VebfnVviQ>>;xE5?(`@uCt)?yx-+g{sp<mnum(wVm1X;EZ
zC@Z~84p39=i7S*yhIbMS4mgajpaX{5iv&EwRGw^V5&y3te^X!O7QQ>dP}*%V^hy{e
zkD=pY+gcU~nHRVwB>eqwm%@U`a{&ThbOUrS-YdQ2_3ugn>5Y%l=yhK$>?~vB%G5_A
zj)s{K7SwT(EaQD=Ya|Lt)Az#H+T*LNInKfBF4G|6Ls&cp3$DPIfRrhl+t9ggQWvZG
z6LpHI68f_u_r#)O2El&wG_3Rp201E^AtcF+2qtg0sZ+EyMW@E8?>sTz81xiWpNoF#
z&@jz{BvBs=oHe4E+;Rss!mkTpB)PzpBlfw&MMvVpdw{qzicOQy{iE6jrO6qUhuDhu
zzX;83%<*jhpl%53Tx6TW6g4k*HI&rDVtjA(CiC3Dg9*D=YFWN4m{^BQwYA1%)-edB
zv4I6eaRoj*aUthAyn@9G!PX+4kj29<P9E^9SLI0d8+${TlY$e74<k4`3<IWQJ$cJ}
zQC4j5L+SYs_!<2cu9Y`0BCXkEjN=x)7U`&1Pmi(w>gWCZui@+a5Xh92^$QOuiLjSX
zo$*zO8BOW+E6{968e2q%0rSu%U$@h%SN)8`cs!WP2H7bTD+;2Kjk_0<T<Zz$V8h6!
z^qI>(mURiv=z(kV^}~;18#vc#B>|z%NuOM*z8AUq*u@#>wdIVM@3xhvE`(TKVM0|s
zj=&QZhZ24^y!x^|9hV+BVlvvm>|(aS<YZ+^0)l}WF)AiqByplV_i}_ijUF~poO14j
z%u1XPutDyBGurDD*|potv)or3^ME~x6zFrQk40z!$`@{pUO3Wdio%|WW9)D-Cj2|0
zbNtEd?e|sR*;Rg(rc^8PVVC+8#xjwlb3a}&UEWersGqqj15_~3N{M*kqbiuR#d=6u
z<?bkOp`%Ff>mt{K_}7es>O95!^WWIHrv|X*0sXGCXw;r=2AKA<b%(hF_)_h>Y(DD1
zl2WWn596=WgEWuDb<YwNXo8hAqSFpWsALXN(k}b|_$$wnm%yjp1lgB@&=Musk?4bn
zx95ox%Ng6-bW9F+0?hB~gqdgpqA_t)Z<2kbhA|>j^{&ttO<t?OM`Nf9-T5_={!(!;
z{3+Cbwy&*4EyH7)a>`?2!1rdW-y~=dG_cAsQxAOakA+L}8n@2lqjI0zR9d@|tGuo^
z>vy8L?xB5Ei8Nw0EiFmdNlkN}vWIW^?fkCA6^E~EP=u@OX-}*W{@GtIX8BOfUt1UF
zo0(4O(+UPro476-spbXdR%>7X6{?%tZ(#Yyj^DQZ#9i`@$oRqz{T1CO1wS(+O+Ddl
zMy#P^Amh=U@zvc9*4zwZlM%G>tb^;hB!+C^Z6whv3R)#-3hRYN?J3+y8&ODVlKMQ4
z+ur9EXSm=sWFWe>!>XH;y(^QRc6Di;BJF&M`juhM#bW#CwD?J0YRo&(3`j&-e#Y_Z
z;Zwt=@=*wjK_#uMYr1z4QRhdZMufL!X#JamJ#;+T3T8fw6auCy5Q{euuy_7d%0Vb4
z4h@=pxlCbQIIAx&zH67}{gUCw_`vyDAO+K#`S&DVDU;syHsT<2{K6r+TbzNy+m%03
zQ~N1F>+SgAcxAm}jL+fIaIlz^(ln$5_&3}8xD>o1ctwGfnSP#qL)zMLeY1Ff`_cJ~
zJlC!4p`D8gdOg@@XJ7Hk>ZJT}u^fLu=MQ2fjtJI^dAzY#Sgsb%wHlykrCbqHg=YLh
zNSgHIL7;_9aC?&9RFCo*l;CKVXvS6Pp!-)zyXcDOy6x(1SlVOI?9zKMm^~LV=kXQq
zTH&#AaaIZ4ApRZHB1>3|;v&3ytc}C2nxx}5L!J$3q}493bGVjZz4RA7yaE?8Lpv_>
zi>fzwxC9(-E4RTyrS3G|aj~H$vtiV@P3G$uuR%F6{tLqLr+TuSB{>qe98#VwgA)uk
z9qo;=s$$~N>Y|8c3DxQ=?EJ4?5$>#ykqjti4NEA$L`ZcLuuZ1p;m*|FSE`1PX@y~4
zIT?@TH6+UA@kom~c2mn8iYQZRPl&cmwjCg%7WQ}KUQg<`b$Z<oGu-Jh?h8RSdR@Ie
zyvC>Za1J{nh=hVJQVVfu8%}~UU-bZr`r^5gLsP!AIet8WR9|NugkzL$)bWg4oKJ&-
z6q(Xd23}U&@~QvkrH3}>kH!ty%$I#?I3%719iL|)i?dpbk#(*&ZgQ|(!=5J+MoT&m
z=B$Z?_OED)HeG~Bk@Tg^mG3BGx$d?AyPpQh-?(5RycU~9Yku=}N-rMYLzR*DYjnAs
zq$&B!xW})8xP#+y{q67)b-V~RDp?1PtDNMWi+*v3F!N}a{|Hvx?HzQu4k3D0ILHKg
zQq}D2wacJf=JO;%&8x~F`hA}DRo`KLELc5q-X{kNoHuL1EdtaZ-7v9!^Xm({X+nZY
zS&}Nzlc#d2=aX!-SagU<Tt?BPP|&_^(fwFUGVz8#B3x!Rqt`GJCO`GbXN>;EMk)o`
z2~p2W#<l^OlTC|V|LqV1_uHl?#Yk(`I4t+^nMF4~YnRK{nD2_-HoM(40`(Sv?~V-B
z=)|q?4GW4@GtKYSI;BnwWK8)K!&DzsPNND%imgkIXM?Edw;2mI<bTF$NW~1#9NF;=
z;Lj5HHh?U!V<^jZX)H5XLxbJo1|{usGoY+uSNE5H`-djgI(A$pqt89qAvhIZCtDn7
zbJXXnKV)8<(|JTg`~vvuzUzIEmtjA&_|tY#%<<kT_hwP+Z;4xD6`Tcd^3QE9sFR56
z3vX^z;s7n49$r9)i*o$b8k-N(sI<(_x{uc0@EjD)B^W}^(rdQ0c-d0SNm14rTv+X#
z%<ogA`oat6_a`dXPCc>icQ;o>oeUm!U317{+*}kx!&{GHM1TH*a2wKOq}U@Be~i*D
zxe*aFpaG>H{8lua8_n?!Q}g!tY`R;w6jr+y955^)1re2wQ2y>*Um5ryLpzAOD1N&1
zqnkLSq-o&!^1`w82^wMm%j)q2Z4wN{#3Ll3I;o~3FU`9F2UWxsezGSu)CeA}mh#C2
zP?B&qVA>$(mMX&hKuW>Yir*$|tk@AgORQFPHm<&Wg$oEKbwNaux>b_A#tx&xOA`C~
z6i6X<b9&VVf#lU6V>N2iXebNV8QUi5EOSaolKMAsyDO*av~L%5;&|oyRA3bl$fL|T
ze|lu5`e1*^82Cp{YJFO#azS~^LY#KZ2Qwei@VET2H=^Ij_S4LQIo@7yR+4Zil6xP-
z<>)F+xG5Y`{WqYVM$dSa1aR)+fwBilKmsydy(NEmrIl035MK;?90MljamKbtbdS$l
z&;<UTqB|9BK01~a4K^9P$L&By&pP%@bG6$_3lgxL!cfB-+#W}m)LV;Ky$CH++sakp
z#7>CTRz#gT%n^0$gE=an-cJZxNHt=vP4nvcO_ZVy+w^J#@`c_3d!HVaeU5AQhNcBu
zJ%#N{0xNi(rGubBpT4anBb3xcG9z&+;rx2EM$rq`K#nC5?6+Li4S-S`O_LZhwdh0D
zvIU?Yl+##M`UHY#J+E5==Dc_hHJi3&0hV#AB?Q+y1RnP%cSKuR*05oJ^))aMp@Mcl
zFka)2oBwmLkQn_IG5|AHu)9K<JtBFeI6nX)xKt7^1^%74Km&xcu#13?NzRt4)jbUi
zkwZ>Kqr+Lz=zJi+&Z3z!>i%F|Bw;!OSaemSoKK9E%I>wf6oMzuw)Pd`qYh9CcalW)
zlGU@V8k>G?6LXfI^p9}dwe(vRX)KzEm0`#FOu~lTg#q#VRH%mVqh-ogV2X#2l!NgR
zWQ}gX1Wx>DsAYQw+}3gxF3N1BX`2YzUatFo-dEfNnfbZVlfVD1)15nJ#zG_2>UA(9
zvhAd$*tJAU3)tzgb2^+bicv1Jt|@CRNUeYf**a3tZ*9<NX&}tqrAzfYl~~KQU9!~N
z71n?sIQkNM7%>ii=d09hJ#*gkr#hhhlB1ut5Bpb=QJSf5gsFGv(6{Ep3g+v(j<;r?
z600kG(W4%4PE~~wQ8&3wQ1g)ok+sTp*4^$-gnoNv%=yxhtE%qYagk4T=@A-wDWTR$
zt}#hF#ar!;xJkp3UriW<`_-D^hJ-%wUrdONy2@=iWa9mEY3I+|Je)<{$RW(T&}jqA
z=H-n}Cv*3=`blU<i(F^@hLC7a4u9)&jfKOAUx|yK<M~uEk^ZM^1J`yHm#b5p>@Otp
z)a+hXeGI5h8JI>Apf2B1@d&ev2;QCVeltKPi;_QQOLa*bn%^4fHDOfvmO%iiNe+Np
zL>4a7s<V>wl8ozGpUrXdk69?s4+-$gCuar{8_S`O?<~rS|6r<$$56(aVMWAzWN~t$
zvGfj~tiW4MMJZmEx~h$-4IkidjlCus3%t}QB~G^C<C>WKy*`x{)?UqK3$ALzdqA_0
zcFQBxz;4AgUJ;%m9ZegIW&>B1xngZx_(Oz1&rpFsgOylz11NdK2ls0ow29vdhBTf0
zdec!XRj_YdxUWMN>sacU7wSeA0`tE$xur?Jw|qEGei{%dn*yO?v@p-n#^Y9&l2*HS
z+%504vfOTwEek!r`#?aAtgJ=)mmww39S{q9uExV?!czac(?CRtRP}fE+;p0nQBb(U
zMDRtUnNjO{I<K+%f&;MD>((?+*%t|@RTm=$9By%_ySI(dnATp^j5^dGu!U%{`@mnM
zzk4^quj^fg9OpW-G3~$D->S?QrVK>-^@E&t>K-wn%a~v*U(fQZBo}V4`0NZ0rP)rY
zAz8~|6W8N8?MZh{)Nkh^cewH1z$E!jdo!L?%=Rq&K|xF^!Y{spFMZa-Bp^Vx_Qd>A
zOXF(Vb(cL&Ns-QGo^v?fJLro!=Fy;-y?jZV>Ldl)qF2E{Gc3IoItRI7Z54#d4meum
zx5PwE4XHIBAx;acBev3n<`^V@H&d;Mzq(gAET33ynNK}f)t$XDQ`%uP&~9SNewiGt
ziu|25`g`m1;a&|1`rD$@1=O&}&2`naVe@9K9CAb~nd|=E3?7!7Wo=gr@u~-oO>(q#
zDK33}Wu{$2;2yRf+76`W&y$%=ZeGeY6vZt>0o{y+-?RU2rLdY^LYm7F2xsyLDh*+J
z@xCsY_2fMj#BQMQ`LrL1_w9KiC`u29MlX1)fU*LntvX-M8;y|2k60(g(ptW{qkiRY
zp^lMhohP)LZ)(<?5uRtrF`$E*@2l45w(Cz8abIT>vxQGV3@XsfB>(lZ*#Jhk+C|I;
zja5Eu1#XF1E$I6FGa}{FsBPmSV!6`Pd+-PDDCrxT^CqvjeZmVGZuAX@vc$GCI@K=T
z%Q%kHPWNr8`tPUjWa_Oj8bu>okvY9+wZCfZk;}hy%ZPGjCpIrq=7>z{Yg-eM_w}L}
z@$fl(`g#~FnF%D8secA-TkW#i|0Ac6#(xg_ZSfh>>{PQT(K5E~@qqNEexS7<NG)^z
zEHr;l-_n&AbD_lAUM{|R>M_KG@l>a@%BHh9TO8j1Q*M%{;FAFNh!H{DRCC*0!`%Dk
z6V)$!zwe_gM*+U`ZPu<Gn>S=<lb^ek`Rhl9B?NrnRiwFW1g0NaN-G|}cC8GV$yT5b
z7vHz<BeOMhUG9GztB*_^vn9==UiEMeOpV&H%o0Agyoi9NrgEow;n{L-^|LKA+ytTd
zIN5O%8C_F+lq^T_V9XcjE0+#hF7y1T=;uyJLd4q=ughaX`vrY*&N)Q|NyX{X!V0mv
z(@gW30-{8b{zO~<GgjWH5dUR}c5-2dn#K9O5^UT%mm-}-^J11-aP0S|Zw9lISqhWj
zxTW?jdGArF7(J@5x5=<s?IRd+M+WcsfRNf7`9Ct1guYr_drTbe#)GSbvo|X=j+hv`
z7^R6&YxWm$`B%~({K7*NAOGA-O4j4-=3eOIjwN(^PwU)lSEvsZezn2Ozwsa~J2VYB
z-?F_+`fS-0<Oflvr9&64VY|%G+sz_!kNKr8F@|~8C2I326i~gkn)9NLxcqLe`S%NX
zFnp=q!o^h&C<uFneo4-$7tMpb{T=C{23~gU_nBv2VdWpU=4~4VPYt>bx7G+eY$FJB
zm&j_T|8i#cWFTxBwtcl^Ba&Sm95ejfJpUNy&1&Tn$guz0IO0?b2?@g=`0)zMZ?B05
zXIJG~AG+bHEIt_-G~e&r*sxQS*y(ih!g(Kt5t7w!b{%R@A|nHXaYWt<8jhV}hvuZ;
z18Msh*Px^aSGOqMUtoz}@CR>|_-a#T`YBjB5zoICbl&A=?fbp2wpeWR<nf~L3p+71
z8_#rq{;6uW@ORaZ)8!w)!t-E(B2s*LdHhzz%IWA?*$LP8<jBC*LTmd?$GqY!TO5S6
z_^KVBVP}P-5@+Ux%Z`p0YT3${3{c*zUfDm8M&Y*K>WQ*l{NyI;$1fIe;I?$Sm_BF<
zThlcPdNGokj3im^sVVt}AE~2pgu?l8K(%}`xyZ?)`yXrFWBJQ>-z?f}XQ=i13S?&N
zJn1^S{ED!|pXvP^D@ciRA>zDYs`q}k(?%&H<~W+q=gZ^na6$0Yt>p#B$QOwN`Ida{
zSbC}&#fVcDs?Lq4wV&Pr@j15gF9MQ8(ZkHZ%`J|F0W8aB;n!#xG6t$DT7MQJ`!5;>
zo*tD%wjz9ew2Z`Lt#WnNE6ErQGb_LPXGlsv=#rSCdC>!IE335wBf~y0*KTe}iPU-J
zWHaALh+rL5)H?rgQ^(MAeqh;6=iO07w3p0;r_iVHJwWV<EIJdt{<tzQ50mg$@hO@V
z|CgHu`Ln&g3wFO)%v(EGHv6M5^)c)ACyli}4bRavUeDwEK@?}FJK;}nWwk2air~S!
zAbk%@uV*g$#$6MyUtEuKDuXxLmnGb2e2$@O=dvv|^K7&8ay)CU9r^C2=N+vt_oQ0q
zwHhvbL@y-oK#MX8Y_;?7AxxrQ@l&s4dn8Xj1bVABh1`?i?X0_~WzA4JGC%PA<GF(E
zC)4Q1+_X&?9F;I$%SxJmEVDfN#WIl1+3V@P>gEC7-)1$mX`e5qK-g)%Ba-7IoJl%^
z7r%<2UYvDCxg36*=^?KRy2B8Y-(E#){R}&tbRt_q@oQANHstiiFi-{_{?Ydkohe|`
zJ15PmeEL<ReUal1?7Vx1BY_?Szp?pjJzvfPb$>8-z-du&ATVtxImR1?gR9Ar*jE0f
zp)blB$Jpw*S$T@&$d;As)g86Up?)K6tb|H5Wm=zy<Z-q@;wXs5ui(D3dV1BcU#8Uh
zX;1dH>FkX$h~d$RJbm|5DFCq{6md(@1i?7c?v<KfV!nR>QkWMA!K!scYROLOD_9b$
zBnZj{5TB+<yl^q}<j5BTSbc6vgW3g$5F3yf;krAM`l0gqbN*e{TJ^jVzrOnPykCKq
zSmc#ZqOSY+Auf5Ntb)jM>{{bF8jZaRQ9{rsACFIRGGwEVxCL0K3jx5~b>)HPrY%!3
z&*~kz%J9E>_g9k2ff)@yM$buYbu@kUb2>qp0*=2SrhpkRxmXKXQNaG=5A5^c`7|Ev
zXgnVlXRDR+ztauAr+69>A)@iq_UeAPDK}wdgW*{p8Z14OC(MOhyQf{pJ-SA+6SKbG
zk>L~0>*$l}8cULT;qH3AqN=HNB@L+eL9U$EQ;qKoTt9{qM5Ak6`;A`OT3W?0nkWTe
zuR-tgvk0GXMjr9__xkB;V%h%)viDwjDGZIzWSB8H*86bLCsEHXxcL7bMW711MySGH
z<){|*8WK;KC-z3A*YaEs4UYVm=M_M?);>(1>4}!0X4QGWO~-b-a)H}P?-7zNm;L%j
zTGK8N!o8Yye_Hu=qzndmy<XWu3YC}-2a#>MRTz8e`Gc_rc8yRx_MNgAYps4(SR<Oo
zOf3Y7?c(dS*kI@)R{ymtIiRZs#T0nVkGp)WU15FkQ58KCYN4e)2{Y9YqEjz;B3zv+
zo5-+Lq*P&G$<>4MA`brUt@mIb07jk!^a$ylb3R*gM5}-sh;b1;r0n2#o#Sl#r+a7@
zhy4Blvatr@7r6oS`WfEp&I&F@Qx}@_ktjEOM7M3u?C4z<hF4~wuSgLYfShhZxHB1_
z=&r4!u>Un8XOXCMw<7un64Ns}oJS8R(m$Q(CKAuq=!55%`<oP2*0*y8{fmRaEn*s6
z>O8lij1MK)VG@;C?su+MO2-3jT6J_A^B`J<W}OAlqw`##ztx_q7i-sf?MhL^+KwJ&
z#WKj>Aqn4TPj}~AcO{#%HkDfk^QYzQq_c8Pr3!uaSE(HL{Fcwr;*-z73_E6OLq9~d
zS^~sDncQ<q+@t7~_4bfEy?(#VkquIGeyZtsq3N8(_dR#`vSE>KGU}zPV>#|z6w809
z<k?Nyk+@dJY<&E=*tq8klvM+o$gM(XUq)0Alqqa@x^!+jb6E0r0z5&0i!0kCp%9&S
zoKdN<-zN%WV-DeIfZ3Ju{-RMF%d-z>JnN`3tzDLj)tWd8!$t$sPXM3&n?0e<ok&*b
zo|;A}&;F`$Md%+jx!SNuYuEvtp#@PRzshx;iuPr}JPl6Oqe-Z~@ZM)rz0F;X@AhR#
z_N8B`^Jlwp^hQ7Za?qfgjP44fCE?HlNBw&!q!o?2>TCTcY8|7YsqrfXK-iSK2xJ)n
zNZ*RuQ&)v;t_o?Le;In8HS>l(6Kc5atYG8xB_!W+lw77jhM0fny)Q2xnf2@#<@>Q2
zmbG@0mhH*=YQHq|VVec&&@t>`8*oesjj7EA+(;D_OzzI{?K$&PRRK1=B@N{~9!CtN
zSUwR9qrx}M^IOz|tn9c!kjG5@A=^<TW-7UCJ<5F#(Z+3m2kdnY&rq-T`!J^<vB48Q
zIA6jp-0<N3xcd9c%D6=dA&01>aoJ7$a;%hpOndcl=){vFN!_>E%1=)7z|Z8tcr~dB
z_ODI~eHe{|AXGUQR%m2FN?$jJ0Ylgd`E32>j;Ki}KR2^gqyBLE+`Tdk4MYoSJ$X1F
zG*rL)_l#HnXQ<DcQYrb9@0?>X(gBgNjwLE1T-v!jMpkFNRd5tHOFJ=W$-<H|e4KkV
zrj~x<S(DWwY?uI+i)n<ye^LRsJ$>mA%#jl>C1TT?jdtqL>%|u=Hr2@yeM!}OY&8cO
zP)4CP8CD_byhNS11qcg8a+xX}qqW8yNoGMkGROw8IofYm_V()EPH&<(#dG==|Gv+P
zFWO=);23-_3afu8pjKBX<PP4bX`Xrb7S*<One>}qbLo%9*bH07-k-kWKghcLb+o(f
zrlCcQ=jVA?WW+>Lyfl`nujj3mi_Q3xYc%kg7x4lW2<LYb+fBVNvu>sZ%2aO>2A6i!
zM*_0a0Y(Bo?`ABC8ycL<BRWm(*$iJYYf?hzq7uqxj;EDBW_}ciR-<la7lt8KL;_a5
zO_j05lXv5j`Js@L+j*c)qNlQKhN$~xGtp74p-5pgpx*a;pP7-YZym$oXJ&J{hfl4H
zC~Yw;3`)mFxPSn4Vtb><Bfw>PwcBK8Ik$GcTGwT)j)M9?@mUJkD{s$H=PO$}0cFcl
z_T}~+%nid%Mp@{<q&dDTkj~0-p3TDi&cMcYSEK3$)arLaRQ-AASk*a7OVgQcYlab_
zXh#%L&!Wy#&N2`qv{VD1Q`i~4yJ{L&r`**^3m}W3*YZM2j?gv!4i!Pr7wv-?`FhZ}
zKRbRI*SoKpyHSybxM?k#V6*@Nl=|FrYu%+hBuT^b!;s*02}>_2rK};LR}qoEzd0YL
z+6K>ph>q)BEL@N0<S4_KGPUNi1G6Io8Pj*$HIR<3@)zO)l({<onsp5-Wou9@{>&+O
z3(kzJk$M|^yH|Z%iA<X<ey&CwN&kwv>gCpc@VcVjxEVH!M?(@%L_&hAS>5dTIMLmI
z;?pDh|Na7apbD^lp~fRyPczBBFBV!pOrf~xp#T)2rJD8TFy}&EhODN}{|^9VK$^c|
z9~E5HS#}*9QRy>z&V-9?$cETxKwcF|L2#+x(Utl;TJXAq>wDy5qv4ch=Yn%k;Ppj+
zFq)(gbXObTC<6ScMJ2MZ;>>z28@a)RK8P)}A8n#j__PtzL<`RY)E(p|SuTt{h*qK2
zBsq2J#ByAx@|oz)-C<NLb3~rJN{NY_#c!yflYaVvl~3D}9;f;o^}{EMw4eRib9zSI
z`E)m6O0^9)gw)|eA|C<LSiouV6AF(uzsNT)wa^h8`8ynK3|qQGw4CP2DpEDXM=ih>
z6fh?BrWmm`85vrsV;PW4qMY?sntc$&(8E!AV5h=2N$Ei{Brf_5XFPndKCRb*+kqFn
zg5@hr-xcSRLkF~<$BX)|u$!wD?Usg?msu7n8X_A!^=I`KwG6Vf&{m}An@sgEloq9l
z)`JoWL(@WOS!jNV?m-Oy{z6=m?C}%F{Jx#Z!UC)#5h2kpn4=RqwMZ*pWS|vX<!u=T
z9-5CWnth1Kf<JYd`wj59Hf()Gr{e$kAHA~kXunGC#yDED*vEyoIF>kh;OmM$nYegC
zr|^P_4@Tu%cAbA>>j2SS%~e8Wv+S}gErTx%cfT+bMd^I0Ev_Wz`gH)MSM&rl)3TF9
zwS!-wY;$oNyYq`k6T^&0M(ppFkGg9|o!hkoU!~UtyYG|3<8cn8%|X!L=JQOiAr$T`
z6ay$DNS{X0*AZH6gue|-Yu(0&DF{PIoA5DHCK_TMbiomW_z@YgL6B|BhKch1ZG@y>
zgV)02g4)gQvoMhw;VUF2pC_$CT_+y~Hsg(c$%x<0`fZT2ietK{_x9VDbX2ZW(b&|*
z{pGWteO(t1c^vnd<?$zu>8-$OC-wrlqW)E#!hh+D^}fC6#NImo3|&Wa?GPw;rV(<r
z2y4-n9=TPn$POD)@-V^EA9^uV+G`PF6A3$P(b7XL`sK@phq{FIpIRpcERFgR$jpGL
zcBv1J?XHNo*-G|=e)WkjvVHD0qGlg>!Ats=C#S$&4hWV8<eORkcAP0qjew14<IlGm
z!i#Z_S}TSgUC2YOad_dw3odjO28R)4&<U@Zg3#J#KGY1<ptrt2SgZ2=!SHiJ-*DqI
z^c!72qF{0pRwf&?6}N=bcLh5BOm4=(ivxZAWuYL3zk+CPw$nP&pFe-mAA^NsPg3GZ
zFrWL}8z%FOJZ>L7ed?&5M_+G2F6onouYK*3uBh{A!=~-y$0eiv2v_U*T56gXs`D<2
zzubgFHRn(CiCHxAA44t|)`zY?{{2xaX!A@LW7Gvaq7tuSwEn6t`L3D#jaq@&hl~r4
z?-aCHj2S$=g;C8e>w%XiP9l#p0T`G8s&&_pK4M3%FW)C{j~D#)<<lN`S&mboiEBV;
zY23QFN_4~&!HvFNCTkN>`m@L`Isycf7M+j`n>gFDya>=DJ7?qATF=Qw8F|s+C;k>2
zafl9U4lCi^5BTA4KfS#4;+0N=>DK<`%uy@8OjLNaGbkD2sXhq*HR-w|10<K$b~`aI
zAhFP|*W#=A7cO4W5x71k+|ssvKG%OyKO;?ZJUAY0#>LnAKBC7V`$4A}0z_Q?CUoDL
z+IVQlv&6Jrmn?c7K6pKT?n~H<5PgKKU$s<cSb#IEMPC%;F<f<)@16kph8{fo6P;Rh
zkoM%-#a4K-$anAAIof=A7_qB+QzHt=@;?5r-^AQOIPiOTok4yQeLHA4<FP%{2);C$
zYQQE^LS1JH*A`r~&^JYgj0@B<sMUzHEeL8|OzXsUaaLa?7-~a|sSW*Bc3hL87OO$5
z`27@=(!M|2_>}c$o~e^FNb3RugHjXC7`37Z>Uh!#_vCecNRaBhLjW%tyjs+2-sz)m
z69}oD*2Tle^x*XEhOh0mi-rsfox2CGUq9-Lcb!V9JDIkG$qL=TaWpMBIa!5d^k3?Q
zr(ZTCC)rk?a5SL9p%qTAWXnD_z+6+LIsilQTrbpV9*d&Q#j|V`ecXax-s7uaVhoIo
z%J!*paQp3n7rgTLP#nxK&Ve(I44*BoO%#Z3jiBHr1d{uMD8i&eAtM8h*er^jnFfx0
z+D?3zfhaWe0Pesc^#GbH`~6n7h3`F>Q{d;Cp40d4lO()#LF2m$P!kZTp4cMSRMiw!
z{3AUXsL5G1@dOq#{7hghgOhsFFTZ-5aYCo;I>pbeyYU?zAg%lt?mi2hA8OEiscRF{
zJIy(1Cusv#w%>`LI4XmZ8!<=@zvi_L@<v<1i`#e+$UQp9Rotb6MEPrD*p|Jh11;YL
ztolh7pQcHI0#ZxKx~b0%L$Mh03{jy0C;THO9$<6e1+Uw$KQuYm*K~phGKkfHP`YnJ
z!rj#-N?n|lE=G89jzRD?<r#?#{k)I?k8W@vE%FG3X2wRxl(E_NKNv!qH^0}M+4l?Q
zfsc>zUcM8miIfS$M5;fM%wnL#yCy1`qrYnR1g6wXO63@y@gA%4Ss8-!IsN00Kc>^t
zW71itSLv5rFAE>h>xUY2(}^zJ9XrNDKc_qUGJdE9cl_<I2hZgfJ+Y7r&Ga5?=ZuW$
zBxMmp;-MRT6GcF$)m_QbsJdER8{n+JLGJ)8BRssSg?mLqj@s*Wsh?Mm-_>{dL4&dn
zSSFx-N5yvg?K&d2kkwN%pVjHs89Wh0;Ld=G?VxG_5S6tWs_#H28d<nC%|IK-yy(wx
z#$BwIX?68hmyr#Pe()I<wYRgOCXE=?555Jtz;wS`T;hk+KlTaTbmJ%EGVLnvL{Bs<
zQcp;AieD3JrZLgb@uFxODWG{cYCe5pDA|J0!f#Q3g7Cx>r+h;Y3z=X?Tz%@!V!v^H
z&kLP1gHv0#@p{Z{SsPrmfp#}&qu=~^a#W7B>i}PU$1B{m0gm|NOhjZY0ZM0G=%c2C
z*7Zw(LmkP|6L8JwQ$W|RVfmLA)rd7y-tbsq1VZ0_i3IHl{XoUj;J0hjxzqllitA&!
zj+0=3yIT0yHTii@=5Q#e6*!#{I+W*BknKJ~D8yL$kRt<1Gp*3Uiah$Ufe82z0^g@O
zNO-gIw(J&9NpX|WlTVf$IXaAOwR_Nac+N!;3Vuw_zJKNAYx-zQzk;BcGodR&jx1HA
znwA70j+r$b9g_O!F}bq<5Ssho)-F);9?T+GU;RK>Cv>mCaetV5!bkB-7q2dS+N`tx
z<;cnUCZA2%a&0OqDPwkfd`*~m;YU_5@jG_OXB;Am-Q=~->$Psy$6g1p_GLl;oAJhK
z)!AXRLl!U>@c6T}h`Lv)!H+Bau-rLt@VvoY4HNd!%;3lBDF5m%otbu^BfP$c@tqiU
z2-*aK1PAg;K$m^A>kM5XOL{L>0Ys8s%at|tOLiDb-%i4lj{B`2bB1ETi&;ckZH(5;
zoAFt8!@veZomR@|Yp`B;fd%(qn%r$P9;@{|-+Ov_>OEZHt-{p8iC8Vy+Oi00aZllU
zY|5;p#8@YK^{HXel|A}V?pmZDm>BWNXXw1e+uPIU_d4S9MIXMpiwXNln1U6fkgXhR
z40|BM>#k+NCwj%$hQ)JSEtA~h%e6{A$YQE%M29}Ij(6~|4F|VXPuxeg@+uGBO1XNo
zZFUqG=z0or7vX+wgh))~+ECXyKeC_BOh3?qzj9^yP?R!=K)=KoP&hFVE>Ha&#fXwQ
zNhXpK8x3Cg4Gkt7Td0o8xAHJI$wHeOtY(iuXFhoNfgAjZm}-yQbQ>{uN=+ggs`sI@
z?Tg#oKm6enTKK2*<Q;y{VND13Ov+45Cck?fughnHOgzaNPXQ&wzi3_rB3pkefbi{0
zJhWNp+(G7VzqJkc<sWRb5!1eHW=)|D`S9U29#Y~rz2i46f~b&>{;iIpeH4vu>g8qY
z`ia>3(AL-sxB8H%+Ler?yZ)_CL8`uzA73`Z{8mfJ9cqOpJzp^X;Fjpl?txC}rTI)H
zueivZf&q85W_zGvXM&XITmta)CkSfWLX5}ZM33*tdWMz<3b)Z*p{9kEAkz;-F?e8P
zE11=ED!)=Zh`cX-Og|p^pc-Lpr_4fsMo)d^#X{rp;IF)_pDOm(X?tSoTsD)mU`5PC
z%p^3LJgJ9udQ8!lK>1IxpdrUXuLo*yF%J7Q^BQ;GROr#C{3{Nd1+q=E8soB~VJacp
zRLfPY6iZ41wj0^WrR5M%!>@M&;u|LxbY?y@<R+ROw1v=7%;)p=O(-2K`ULGTMC0~h
z<F-*DH+mCj6Z&LCYxDr^Knq@HUhhp2;{Zsj0WU}&GUd9)cNEpeV76fG03_wUB`mkn
z>7t7a_%287C2znG+t8WxN1$zL1Z<^Yek3xPvP8S@V1}F4o{Ut!ujyy7-~ax4u3c)i
zDSPjGq(pP5Bi^F%@!eDJIi?r=6W#&vHBz33{@SZobl-Z(B{5HMlWH+zwen!H*8&Kf
z$H(I*S&ryD2j9^su8e=icN-4xN@80Uo)<%e1ie@))0L{6<3F`9v+II5e5*mDMuL&W
zNBD*)ql1v3#XCOxfrtKl)I&5n6OJwxtOwMe{b3uM#kM`?Dw@AbP>MyzuEbju&C-K_
zFW~j%y@1=b11)$NdAj{~U|RviDc&SV9sJ#15~2#G1GoFdDtd4WcL&4L6R_bxo6@FC
zb}-#XlpR5M^wXkGAAZ_Q4-E+2@mruc*4s8-e4ZTjl`owi3%(Q{+}2Tas44rlcXj}e
zKhCRu!kUT1SpeTWJ9mzq0GHHiA{tKhuf<S>^W^LrLg)MN>uI~V;$?CZgD1IsWj+ML
zv)w7+nFoJv_B#{6v6}(i8X9a>+$D#&V<AV!*#2RtnbtM5UG&RA$7v}EQloNK@fY0`
z?+6k|VCsPrs~4i}h|eZ?nyx%12J>O;(_;Q!9^n<>)8tIUJ}qzr-vk1sXnbw42CjlH
zJX$M_WH$#GN60m_-_Y=^mTLG2-@K>{J<&6w9xw^S3~+RWCajqpS>y(e0BxcTo`zp5
z{^I#7zFREcjUQo^;se{JL^z}j`!#qxhmU*LpLp`<@@=|W!wp1yJj5fWUVH7T&jxCd
zXe;?KK~?|jdcq8+@0`$;&l=B+>r6`+(a&O>$A8sZ=S6694lKfosfQ2aj0LZ>Io9we
z+GdSv$6)oAopUuVx>>C_?&CMTcMLLM*%6-zrnOO7Nd5^HP8ZfS<gCjy)3qU}5ZZq7
zBotU0vwL@hm$9cgdeQ7a9!9YUhSm`0@bVSM3a6Mga0u7LF-CRVkO5CWu(-9|mO)%{
zkjn*xGQrMZrrCf9<LKXVrnkOGqs@4T*b99d%GuE_5)YRA)b~75TF8w!V(7ubF;?w%
zlzg8sNaMtbBYNqvT~FzPfEqgiZhPed+^hNskVjW@VHP_1c>&6BeVGT1Dv*b#^RJ1{
z*r#}s3f{F%KhTz1W?uL#_SK89xyGsaOtbV3xu!d|)~g)}8L7n<hzdDq&0_vR<<FvQ
zhSE$+h($vxHFEW2yK>UmQokKWg0ZvR&Lz0rOcpd`_w+sdnM@ix>L46YJMDNzu1tg)
zV39mvN{Hf+n2f9tK9<6xgSEiov0Z852|6(PwAR~DpJ~XK<PcBSCdG7cBD&wqm=A3i
zl&GF2=#QFSedUtg$3E^OXUsoJFk?B#RAVqxHgR?Wk3F`h*Rkd4r;mA|^T_9O`ZeIM
zzjQ_SATShF>l)h{Ppo(WxwN6JFk|Mjdv3t1VL&&wY)3-nf!@un7M%tMq3t%)GJd;g
z(wpaI%s98!t9<ZAt8L~s^CmG{0=7e{h2GNA0;?WYvXjAu-x@(wtGdkfSZU^KV;6f%
zA7P;G-wyO1UIxeS9^1I=CDg6~IaLz_obO1_dugaC1lr-v6K&Yokd0wryo0y2D?)#(
z_2hHK!U}CR124%8o6tyPjmRcuy|4li5YFezJns3Ie(~+)6W{%~)iP-AJfa(yW-GSs
z^k+6ZfK#XU^g6!qOSzne^4ozgy>Qh#0gp8W>G}XN{#Dv$L|`&r^{P3Nc>+87ljsIo
zN5~?L#ZTAHC((>!53zHHmL~bsqiVp8Mgpx@airV4brN}QBjfZ(5oG}qw)I!BmO<N&
zZ(ykwB{-sKGalNq)Rp43zY{yqf>!{(R|*TM9nek4xJD5g(5KY(Eb#38*1;p|N^|8&
zr;D>Wuw-|WR~+aCn0Qr9h<ZU++bE7`O)h%m1jng<srATIXL8Zp)$SILn@MQj_{PQM
zJ3o469OWI_Ml8e9Ba39xU(@v%&e*s$p7f5?S^h@cd=&VUPXBpbyLNJULl*$Q^u^0M
z^6NSG`dcTt?5Jj55VA1FAE}+=Uv6r#Wbi_(ox<jPDc-#JH|9W0^jIWEw8j|C)55qm
zEpNM;@X^crcuyZ0AkT`YR4VUQx9^G!x_tuq?~8!Y{cJvS6_X&63EL$Sp-W|E)5bx$
zRd<2|EqK{KvBQB4dLV?Q0P#YQso)q837KxzIwWJzJFDSKa<;#*N0ALDoD$d(lKKx#
z5Y*1vBSt|(bHC*f4sYm5HaNWCLM=Q88J-|t9;tkm<)7&X8~)XQ{XJuF?9%?rgl*d1
zqq*UzoeOeKkt^|?_ui5I$!RV0zH~8?VDZ1_X&!iS$`}5=@|8<<_1`tF0;EPLjqx7^
z!CSR?#{i|~+2+w7{R20-XBLjx7ggwl=0pKnyv#dj6Fu?Favbr{ofmCeC^}{pBYyeg
z-*dr1%aeSXt}TAObM3flu8U6$Y6;0p!US#L=^3hYZ`bN@bQ>uJblgVEa2()-{{g}C
z@?(#j*eI=-g?oUML5jDrLvUuY(izrZ+Dk`Cf#?$qFIoZ%4yk#T#hz%xO!p&>Y<R(2
zebA9b#;?|o%w!w(@#E}A_01<e^kMumj3}c`@`LsWAxt>Frpc|a;M6i>#KZ*WOD}xp
z&S&};^xR562fm(vv5^U<{U7`IW6NiL`RV0pe}})uug1O#IBct1ctH#?)-~Q`mf=jl
zv{zXq%5KT-x2;*&T#UpJq{eMWxTjBx4xB-boa>=})2t^Ph=^l_r(XPGr`1hI5+1a|
zwN;0gWgEtSpsk{$CfNg>(jRESD~c1?wQX$zjdY?;IEgfk;A;Ss%#=YDWgSSw-IPfN
zlT1t3-Wo5l*sLl;8x|&f1W`%^Tc*@+WT**&<o+Ov);f@aV=c0iC}cO$_-d$|<X(F5
z;_~v#`rN9U2W!^D7OzL3ks^znheTe#)<FV#hQZIIt#HK@jd4bvtN0hBTMx0=Oz5N@
z+r*cAKK+@~st;Crtet!L!cFWm4;W{B(rU~%@zNr^V4L`c&aNV_YGpY6K>ID0^CT8{
z9h3bwj+Fc2TfOBH19?y<lcTTRs=n?J_@o0l+mS$^g>9gj!p$pq?~d@5jEu`L0R}o)
zZ301DdlSHGP@SmoCY!z%qLn<j3~)&SYamIf@bHSh$`M~RXhrWx!`KdNQ_fX`6kXJW
zlT5j9edubTful3@=_9IBB7Y?mfo1vmpF5``Acx9oHXEJ5FdqUW1$|#a3tgx1d<N{X
zV${^Msk&Fv#^Fl|{yIhoOGcCR+EqQsK}UXe9P6EY@asa~r+?Y$<-^}m^Rwm#KFUvf
zwS8<;E0?%^V#D!4RLh*$g;#7AQ)D{|jnB%mgvunuC^dr5M#T#rUH7r&BwF2a6+iYI
z3o<ej+!o>K5Bi#mi7=`KTBb-<uN5!s_S=IU;kEOwhzM3jiAOMr2ihHx(^`%sk|2=&
zN}Cq4c<_^3Y*rD9T6SETc0dg6XgD1fOzHABXj#W7e83JmPGf7+)yT<uaf?o?J9y}7
zC-w1_Tl4<OPrd5Tu!~sR4R`ScEB0pL`-F3Y6bqgEw^QrDbu`+!1MUP#w#O1KI#~b(
znZ!JlQK$P_{C;aNnR%Zc-tj4Y$%m)eqKnlU%gKJ*O`YJ|kLpvh1uNc_hMVwjSIV1m
zv@#pDC|9a7+GOb94HbU%n;)C5h<L;g?wHgse1bNBTp0jDYVv@1ZxO>87fZjnbNhCn
z1uw_zIVs7}fDV3zGt0p%eo`2dA&H4bvwW23!*{ZRO|RtX7ooOQm?30q!(!PPG)FLc
zyO9xSvD;S$*uHUW=M}+DJ7G!xh%RaSz5L|<XY|bVS6^96i46}LGQ83zozx+$dj&c2
zvd~$Osk?0}2ajFqEyx7+<kbx!{8daAtOSrDk!D4{`daKd>a*y1pFb~rZtwk!j`k<@
zy?F8`4x6!W8B?<@^(Ru*Ogz(C3gA2@2+gHj1&0j0{aWl;8KDYgg|lLKk?TC@df~gT
zj%zau!)TL2lc!L-1M*@MXao}wfHSTVi*nm`pan1cm+b^cTdz!5pm9Wma}&xukt%s@
zp-ZL)7p0C-gOZ7682xq`<L0$~l-KbrW09$fU_VY~oG8@H67J$fP7u_z7NYML8#SCZ
zYQZ5Z9tF@pKhNanyA8Qz`MLl1+`{j|&CTM-@WM`P&2UzD2RIKUxumJ#_vylGa?3Hk
zI)KnJejY3F*1C28j#V3v73$bwajrXwOk`~HKA(lpSw-4${X7?^`1}wj_N%NtPezUD
zV;>h4t9?LobMVDeKXIZ?OMk|_<1&}jTd@1Y?6yg^{}$<bVY?}kH69(pbRC+nK6WDy
z&-t(sN(|0l;(Y3X8JjY%I}q>}IODo>BF=5wffl^%k5l>)2~KytMi4MtrV)~?fg<tV
zA+#8kFyFE=O|iN<5s5w`L%-z<l>OjU9eTsUircp6VGXe+Fd#FHu3y@aBl<03Lh=p`
zF;$mea)spvPp>ch(p$?f{EN3Yj&|@d>9B@^wbspKdnFzalP;(27kHnptLvl5SJfRF
z^@BqoEis9DOvEVp=6BzTrDjo<?xc=&+vcJkv7|A<KmN(b`qy_w@0d1%5P6J0;i*vc
z=Qj8vg1eTDM=m;JQJaxuH{*@YfRnp1H$!vGEs43NhYhFVtE{XqX4Rx<j!iTUmY8r<
z+5{TGAOxWG3+m+S-W}oflWFw;bbD@x0&X)cfe4b22PK(z^+}}?%O0&4b`M?(8S}^v
z7j07p%C&8_ek<(K?~+LZOKr_2hUM0VJ<*IJC?sSMNKM{=SQ&D37`!8XuA4jXn*a5e
zE-e4>AH1yl(l<}>;S++xXj$vVADx+Q=B+o}I8#^8vq;K8)wYafidFrx<3z4(TUJ4N
z>;%U+j&RCmDYU-jB%s(U{MFahzOdIr>3l!-34WzkAL1jdMh)!GMKMJ*Vi2xZ@O8`;
z6-sUe6L%9QekdcwNo(b0L8cDjR2{m9s}XbjSNww>17Z_Db{(qo80wDCbj-wVVEc$o
zyeD1lH1FLJUVD$X?F5H$-T}Gz!kt9(!8b0(NGXMPqV#2|29vNW;Y=dS;z))(fXXVe
zhUv#S`upJP1i)kDjL`$>*0{lplc*Z@jZUz|T41eBWZ_GP-+(*e&*rgH(y8aq=?eZ&
z|KG3qd3enkfWh>D;+Ge?v;L6Wjrj9)y?bD>U)Sg6)IRFY2XjrZX?0RZj|I~8FUfTb
z97=MWZJ=)t9k}hSv0=OHW42<t<a_W-h9mv=e9vRUM!8@S6K5@u0g*zWTj(MWSy&nW
z$Zo?~e-}<2+=i=xxHi!VwEY$t;Z5-qU&eCqAvB%3%y%-z8_;-*+6JMup1u$NW@5M%
zJkTk<24a0U-c|(o#3|5t!|_{Up0TBrVk2mI@jS7TIdsdHk+@RN^5_pA$hGWDw`_!B
z7h+jh?Yln&EwOGUdQjvYPzZuSXS!)&tQH^B6IoC4^|P>mjq`(>Kdr0ypZ|roHcoY;
zom6>?5S;<%BDDf(bK2~y?>c>_rc9(7rv!j7co`T}WMzQAn(QmIdGn2#KgP7JMW@WR
zeG=$6$M}{B?TG4g`|kQ@mOlBZQ=OiaMu^$rRHU9cvTM`xIGbHE8?klV@gIm^)aU+;
zC$1IBc9~yc=VtmAZ?=^%T_emOt5@QzE?>ZcNMP-vA$(fkM(4=#iu<?O9O%|s9VuRQ
zaJMmLHi0T4?ztwhHJO@pGtHGQaS3n9syY;F=nGGxgC<?On}UNEy(+e*E(Np9urO$m
zckJGD0%f@z1c#3aMA-ON-j<)~_?peo5si=P*n3~Ra7F(4T)yLJz)d<Yy>L;Foj$6Y
zRGwI#Jj=7)@wK+?K5pj51u?F?*VXWe>k#I8q6NPkxTcQVf@0O8hmFLjq3O_KOPB;w
z$4!WjYT1F<4<sp>CU{++7_fuNGX%JR_uTuA>05YLZMVenxB9ZxuD@r7gCwKP@h5Jm
zj6ZR@H_;Mrai%;(Xz`I-WMr=V*nd;o|15)nHD7V?JJB2l3Zjt%u}h(zSc|<~t!>!W
z%)ha`Ja_lQ547Ord-DiSFeBP6)6F}ohbrt68Urr_8v~_8N79q(pkb{>&x2?Gl0(!D
z9)sH5THtib0$2@h(^_zqOC^T|1lI-`A7hY*0ny;;Wtr%&sv}Cta6`_XZa2MfQP)9r
zbQ>-DQs)aN|NLiPH;dnpd;bScjW6L0`9?B{>2`TEVbswg%H49Q8#3fu{erW83^)zy
z)~U?;b3L&~&hb0Tt#yUZ*mlF{P$+#k?5H@U=YA#Hw|(2m<?AnUIUmi`NwY_2MzrE9
z%l2X0)z3`WaA&{CtL4fFF5MD$aW`q1yHI1lB(zP4#jO5Jiy$<NHw^09wbSFrrW5a3
zezu(eSN^>_!pr8%J8ssm6jq1T@9i;esF)}z7BJ0tch`xiS};L05H5m*U6d`_tc$(K
z&yd;S@I0ui&FYGiYP)z@05dVMie|KV80g6}pjq}d2D5Z-rtuZpGpA1Kz4c{(mDcrW
z(;G2K^-KTqyuNj&o7L4m@NK7-=bk;OEB{Azrhvj`BP=~q+%J+NL_Tk4;d8{#BdChE
z)&nclErF-*PSGxEk)-B3YwW-=L}|#maB5Jti{!B_As9mz8SeH|X>8+|@_66-kLl6h
zT-5Bb0otHQ@)0*!9x+9B%Qmu+8TN)u=O-b{*dtae7ihGb7P+ZAHl&|&hTSEhJ6NKH
zjEm&sFI*0W6u>Kl72oC`xrax1xqIcd4z~izd-%CXwfu-#h1H*7irxv7nG7(AhC)F~
zkIG~aBmu)9n+z;4VNo&bx4qb%1P19Q*%(gc?XE3jkNzA2EXO1y{;&b9<&S7o&*&$a
zuIkWsUXR7vFkpR=fK&V0moF?|`-(nSQ#*eAh!(`D<q7@v`kBX%`Uy3&D`e>Vh&j&V
zYT>i^xpJ?s?bofZ9eagGX9}Rv)GlMV#uiyTO4qiE=IVo*Q)AcN5CvC#&Umiq>ODIx
z+6O*(QolH>KrPaXU7d<k#17S^E){Rb_@jufn#(Rh&l~9(n%)AIstuIn8#y?kmDFvv
z4r=v3&yTR6C03UWlTtaRdlM43y%5mt+r2x&>+6nR=6#kM&b!b}*%{n%?w+#fNX7ju
z!Au;A7`Y^mgX|C(-Qdo<H_<DeLO`xYwZdz>oNg`lc$Dk-wC<>K+iJN=u#-;y;r07S
z%h;zFV?@C%*c%7GniG>BWq3jh_VVSc`Y}Z<{K&UxCDV4AhIt+_^$S1$rs6MmTpM`i
znUl+Vbp~+u>~TGq;)ra=$QlEu&2{P=9{0Z2!oQ|70nP$qli!~o@u)Vbc_x1P$d@39
zbQ**Ycdm1G_{fs3$B?9Ch<gN%6Sw|3%0I7%YrN>Ad`n_mZP@Pm*Zce08F<dfmWM%`
z*|(Kt!#sJPc{K;hc5uu0W}B8gQjb~I2|yt1skyfFo4)4Z^c~j;?aafUR0}2lD#gmG
zntXL7?2nCo|NbJfe`Wclg;OnBoNXmi%yoO*r<{q7^fdtehgAANnt2RP%R%1LU`8hc
z(<3zo%5vyf7sFDl(KPOY!fSfMrVoi0-XIit+9qj{VjIVPAqFlPCZExUup)2&C1zRo
z5tfQ>bU1nPxIVr+s=MH>YSQasl{vkc++WdxU{N^sQa8>~`{i$3@~gzLsb83V{xN+m
z^@Ps^PU?C2$$2)<dw##~H<3lp>3{7AD*QQ`$}JPswM&>&okwkmQS?|yY#uKK(<w+u
znB{b8FaJ5pKmR-no*O-TF0`TQOUnjo{MU<m6%ah)k1ufb%&ajl@8FSvIY+gfXPKz^
z6uG$uHeN>JYLGxGhKW=mSk^rCx%-fExZx1I!Y<m5t#;!7atHY)(t#GdcKn|omk|<3
z;7ulrz(qSAlCX@=f2YMPkxYTW8o*lK#+5xTG8iNrJp8!MV9kRQng=6Qo+sEui>#w+
zKUHBz5H}6y#2E$pfuM~8Xr6U~5iwml;Um*L{nDC#P*IN_&_{SzbhN*sudea9EQ<(S
z?uSOkWb|-3(+aP9*T1aCX7fsW>OCixXP?tqz*EPUGiQ!!Va@rJ6mJ7^@yu8KbxQ}2
z9Ur+m_R8Bew?l%6m*%12oi?x*3G%iio$aw_T=~am7W_#)M~_zC(Ww?)cCz4O{y2<Y
zz)<niN`#r6a?x6E#D?v}sZa>VO68NSquHXxKm36wP>5;vpH9QbHBbBgIy2j%mt*Nb
z-(g2J`@)vxon2H9)Xu+s<1N+Y%y71!<`mEuf9ZlJ>Fgi_18v6V{x*G=zTH!J1ywGE
zLu4c;y6vj(MO~f3HwG<1&z>;F;9F@+XSe*Q%`h{|HJ%s|Hr~SaBRncVb*^f`pVu$O
zT)e1{^0eqXHR~QgeU+Qt%d*|c7^lzg|G;C*GkPn)^?-adhWEK~@sHbKIr2M52ZDqH
z^}_JtZ>3r&NhWgAwR$ZoQyDL{$X8EaqdlcxitnS$=RbdG+>yA>4PmlX>g6Z>z!96A
zj#*-zTIi|2wTWx1)%X`<KYUANqdOMAs53r<Fr9>j68CXEE%>7!JGIgyQ(+`zL+n)M
z%bSlsvDZIi3U&W>pd&o$3g*iKpPbfHS=|mu{S=T>NRCRh3{(=Y0f>njkeL=8pmh{L
zO{naF8MOFnUKeksTi*+}WZ>0af3icR@gjXNlAQ+_o+o6IUp;Ti!FL-GbYB1;@O|3f
zc<h*mDEf|oj=yEbcgv3*J+Alp$F<n5>q47eI`Uu57tM(ob12N}O&?@(Dk*s=oTKh#
zy*>ES7ccme0Mt*NKDK=OM@}!#zV}4`3^f+Gb5buYLfl@uD5wi;*r|BCcKGNT)mSC%
zt{)K)7yd!OqJxv-7;?=h3;r2BCHJ+Ld7D!iB!1UYwAipo%uwT|3s@^M@Pfxq^{vQe
z<x^O-N~vN-cIbEP^h+*z;<K_bV+&WBC4dmE`}pB)SSuGV{O)zx98(0O_J!cLg9l#l
z8ecDXf^(b%Nu$#N|Nrg1d8}pGb>4TYYo2;!lk6rbaganyvLq{t;|PKwI0Ili2oMK>
zh6HwiKu#h+MiRh@6+wo9*a@U0fMH93oLDyE43;Sbc7ni?{*Y}+7EMuuWs0&WlH%MP
z#AbK1y1J&W>aKjhZ>?{ieb0TbUsZQ?cQyO%s(bd@Ykh0jYwvx}dFP%X&D6&UzAyM=
zsFL6sHdJkC)RCi4i5xyX9P-dxwG?^)tXu|cBLk9wLS!EL)1cM3RZP*@TKEQ(ygJ=>
z+z*n#Cna<z$QAvN+$NgFrsA-!>C)#ylp7pI>wnnC^!x15o7(O5BeYoj9BN;`u0#Ku
zJS?(JV}gbXrX98nE%Ks~7q8mTS9LV;;SXKhd{~NS&K}!*-Mi2E96%Ny3;)d7y5p|i
z28$K^R;;$z{i(PKpfC`8)htC?ZsrR>2q}?sP9Sp9j&kn&J=V(jmjx^lcXXjT4ioUv
z7N!#z+ZZc4bz^J`or;ut9{)wB^o;9xk?~1csBosbI<PRNw%<!w9VvO}7+lwd`ov=F
zg{fO!@VZv_ca@KSyR%A0_AXO^C%F+~>sI=uXBoU0ncMS1hfbTp8>RgWSrYF-Yn(7X
z(Oe@xLawP%*5+<xa9!i52eJ5OcoHoGi=QnEZWEJzI7V*@=dfBQZ1dFUHZSnt?V;lY
zlr-@Vz)-ftrGH0tYMuqiefP{YjsUoVFGm75Ig=-TtILK;sfR>7N_mODjUU*-gE)`s
zi{|UU;r!-JZ$4>1DEKi|Quh7y>2F79P@LLd7Nr!WgU|96@3r9Qr}^qky~w2GC;B2T
zWDOWL%qz1JmDS1@X~OU1+k1YDEPm@~0ABUGbjl8>t+v%lR(RxvncFpA;$yK}n0&d=
z+AV%Iz%F~?#62|y|2H=us>Z?FTVL?^Y(CKTouhPz972y=2Bg}DCu3;9F=;E()Zx(%
zT_y?}UiqsnSSJ<R`tOON$&$e_%7fj>ZP^HoiKQ^WFC_z!dKsvK6*fBapq8y+-Tkm#
z1O(_$S4mW4h(+^INS}e%&tk@lAG=IiOaNsIA5?hGhZ-CK=++DG8Nt?~d0r>~^hKaU
zv>SeY6=)T0M!)PR_LsVHEIZQw?cchv`P8S5Z~o99x=)t~*B{s18UW_{vlUrsF2Ny#
z=UUD{6+QV-ARc2wg^&&;!8t6H^x;)lyy>RW*BEQME7EB@)?R$o{K15bU`qzDPaF%+
zZhQVLSYw<dA<<RYd;Dw8tTCmr=xpg@s{x6fCVO~?*|K$okE9Bjb9K+=x7N7*+T-N=
zet?PpFQ7e!U7ME2)a4x^$T|Vx8$PNjb_>*YvQ^K#(8xkBbzx~E_u>ZC0a+)(DJ$U%
zQpIsLUUKUPZ$#;YwG4Lm4~&Gd>wyorJOG%%YA-`-i18heX9xJY0`BTn_RPri@rXma
zRa-St=Y#5`E;+p~*{sghcsw@y<O%&QfBJ+DjZf<;y;GYrqEBeGa`BHnbeMSLL!oZ=
z;5~ie`sSDa>6bQ_p2<ZS^`(A~evPTTP+NW4EDU25ICXFpHyC~3TeCa|1MB1|5QVA!
zTC?#WmN~OX$_`R{uzeP%(8OVTNNMBVC`_5ItRk(bD(<3p=MOruvDDU{I#SwpfygC(
z__K*ud<7HvT*p-TlEgkYADl5SMBnm)KXYdDLFK{s#jCD}A=aPBs8yCj6l)bz+Zx(!
z9zr_54ErKaQotZ5`QdHpc;rCIRtD9Lgjy4h(aE4u%cP6xBE2m?%@D5S11v$=87kP?
zDqzE-Zdz<V`iaLu>u0YF<Pr<M1h=4R?1~4APsyvs3uSgcsh3b&UX*cEzqxTE&si=0
zv**sJKB<XzTwefs>P+IZ24j&@5`HOo|NEZUx}BGw&MGfeqf?8A2--#h9{uTYz#lMg
z@ZqT4K|>L#oYF#Y=}s^62X8h|V$q4YELE%W0;l{|IAAjmtls@78na73(Cq%TgN#-6
zU2@?U8egR!9_`{n?Uqxg^!Hf+)H5CU%o8o2&z?J{KeunO-134~(9h~m!jIw4$x?0K
z>U`>v>113OuK21lF`FYxrfQIbT5vU}*zbWUPi}*byfiBg1}OY2Rub-kqt-vS(HGXS
zw=EA0`tTXO)X{GpJlUS)-TP{{HOq$O#e={q6&;7!yrS8M-6Mf^X^fFhg@hFfgIj>c
z+dV)EjmFtOG<?;-TRdun$g#;$01N({7Cx8zp46kcj&X9Jb**34yyesYf7{1D(Cp=%
zJ1LHw6GH-tf(%xU5UN;Syd`(!xWsII0+@izq=s7n;tSu|ZA@jF*;C_XoOEcTyZEw!
z#$l>3n6WnQs!_6T+YPEJWowuAJhFUSHsg`dX023=HD;zA1AzV*%%+OiACeDUxo~SG
ztMQi5Iz0bv{1Vmh-?SqY?I62`KCN3l*QF8;*Jx5dAd9S6v?EU)um?kpg&z)<%eH59
zo1c?xB(r^5%(m-QRUVe8DpK?_fUX;Beu7fHnAox42T@$p;rsRZ`6TnqsUX@K{zQ)W
zVW(%Rk?B+F#V_BDksc72;F<vrl8^P9&*PPNV8<C2|5+A4?>8tRVVDx;nTvXd+?SVm
zc5Gb0Q;RFPN<BJ(g``DxkdGn}d9Cr(b`tse02p9Ua1GUXGig~cn;h=OJTaF1Rh~u+
zsJ0!<IMB55GbPHp$n+WdTVA*MvFmw*S{UWUZ7zgLpQ+<6_2`;>wrft<`P|xYeE@M=
z3to=jzqW^CN&;YvpsD$s)=3EJxs1e5lAfaCENFOh8yhnZS<vCbDgXJFE);Uds6;bB
z{zt7(_+;4a#xN+xiqA~QAPpW?(3cE3g*H*9CJV+}R@bca(1(&+!}#b|))v0a$;K#h
z6Q1j8SRK-K8$(FpIXZ2qi>JD2nC5pom|Fb0vrh}32Xb~7?E~G%`bHj+ePv#(P<Je*
z%4fWczB_hdWf-2vaMaN$1HL=ZW;?g^l^)mzmdkh9F531QQ*yQ$n~V!R5!W$bZi=Jh
zGLJBgD-1kbm#p-OG}!RVJwBEaJvhd-==5nV#|ph_`;@TjS%gJ@j%<FV)o-b~^|SQ&
zJ+k>lyr_0(@!+$j;?>#H@<|{m_vlKllluFMpPF8%c<>tFL6M_d;h|E<9w-7FS!;t-
zi8%u*9DV{_2B@MZxs)t=XfTv4{G!c5h`_lC=Dz!n>E;ivUaM=^$r}YIhsBpKJ->P4
zi5r{8zo^?}$Idb_C4$naIHfoeT{!0owmyY%ubc%~;n@l}SW*q#a`Doh&7<QebiL;Z
z?e&l8OgXpoKHnGpeCdlkH?iJQTz$jJRN7JURubYaZ;YGmx7rp?x@EJpeDjuZ+E`FF
zmiW^yo3!}22lfY@8D+0gHO}KNj&%@|h^2vYX^ZFaM`kl+tA{_#!k}bZ5YiV2-Rf8A
zWiD~9i<_NnRwBDb$C^uRJI$k#tzVv+2a>n7;C15eH}urgi;DaFR<@>2(V3<573i6>
z>jgh4>=9xO%QhxM1>BR|=}$Tq9`c$<ByW)~11esdw!9%EzA(gJ14B(Qe8n4BBZP^*
z>Qnk#?49pA)qA}LOcgoe@80wG-lNZn%~yT(3Eu|#=}&U&tAzH8R|+w}gN8T7rb$_S
ztVvYy(huTkGp)bPvzao9S(SlrJ+~1|?%CsWw*Oh(;Pb3*UiZ&^?F)RBiqZZi56KUh
z(9}4n*ovl)$vbokKglgYT1{;^Cq9Y;CKy{()wx05JQGqVPxI_RvgFcZD|>Ss)t_L>
z$JpE1ec|W6p#!q|DIaZfuX-HL6KltecEwt?t<l9`Hk%7)&u#uC{97%zbx5y3c}L_w
zCkQ7xfu$x8WL<~N6<ZX`R@BlJ3#Lub423-oStdD*$mZc&2Cw90VHzxDw0M$}Jxbgm
z$-szhx!Qx**`E0^ftiQjcwASf?Y-F9tMgeGevWzcEyp(B_OEeO8@VE;1SEQNA0876
z3&nICpeG)pOjhK?S78feS+$`ZyXq9k!WPH6@btk4i~gi;@ZqH1q-HSk@>@Pj%N7T}
ztnR&a7u@f%?ARL0H3|;EBGC~hz+rOsC#K~5b1Y6$&4ci&mg%7nvKFr?^remk=2*$p
zw%w{rx)?xKUmB5@taw;j_!+-*=XoxF;ZoB|T-2t8nd<jRe~%eecJ|ud)*ikMyiX3l
z(-{!MiO{BF-SI(iXE^(4AmTwZc6<3QU2L(s=MHvr8KC0MQV)jli^eR+gcB_B;MYoV
zI|GHo*kY1}9)0WqjXg{-jqZQo_~wzf^!1<}FF(2Q(g&`2@y##IKdQsG6UX%&H~pl=
z-k!ppYdoL*OOM~!{N6{VmsPksY!r!t=#bS`FMJHXk~tuNjU6ecCpI%|lsegH+sLJ7
zKSf%{9$J~cp3<KF+BMy_YF_yn`9se(h_7rByA4|=Z&MBtbp*mA6G$bO^Fnh2kxkU5
z93aSEp4y74^?%T9*-Y6j)ARrk+;(QF_*NGm4Dpv%ae<>zUu+O=sZIW*V|@CDZiO;0
zc6c+6=MJ3Vgq;P$B&{vi>!$wFdap6JvAL~1eB1rUNfB)a*)=pB8MO$oB!ZhVswCCj
zL_SC~tMt@NbJQM$;T2tbupC+Rs6!(RP2%^(Nk*(LszMvjU`k;9#wBhKv}AA7p?L~>
z@~5A^xq0SkUA({pZRYP`DRPm`6|LzDdUW08OO?0izTx3xe!VhJwdriCE^jr_k|%;h
zE3%N?TeF4L|8&da!=_YRE&Uv9j1~m-hO6rS*Pi~~`?mXAu@xpZQ`_L!urRPjTs2N?
z;T0a4I2|Lni~3cd2cUF^P#|cVD?I`))#lq*aXk7{8*XRICPJ5u(!qG~r7c~@X%7|S
zh*|C{tkNsG@oM09=nInEoYfI9*FZTI_{3y6JukYb?xq^_R99jDV)(aO-}Zt(dv@~+
z3g)7NYA?{Eq|pEZqX%d8cYI19Y424tFjZ#sOqaB9!IFrgsri=yoM|*^r$h!PgGtQ@
zWl(@a69HQfTpNIX^P{|0su-v~{bgNps&iWQgQ@7LM(&=#RD*Y{fB7kHf+>Br{3iR&
z->NI)^v&Ps7FGn+Xv0%qY+OQoBxbNQnM5uSFjNfF#xm884KcLj=C__JZKD3jBf9*p
z3r8;$=STqO$M%$gt|c|5a^JLsi`-(Y=s&ryHYe<SNZ$-sD7n};j+f5#_?Q8|?F^y}
zL|gsD79gmiGrpNWfNL(mQU8p?xBe>@WY#uaJY>-+eZ1%&*w%y3Bg#_5cA@4|QB|F8
zbbaCc`OW(Zee2t072cwbg1xT2*Dv2fKwB2i>Ti^kbgH@$E_J#z^A>caV3Co$xul&M
zSq)5~pl2EMBA*s_8+9KUz_!D{YP75auLiE3tA1`8v@4f&x7XOC3t0w+p3QsGRgY@$
zwHLdfz5ep73LoPC!EZjJzw7l-3_zn4U6TnW4jfw1)fBDXMNNch#}a)*w5d28Qgj;^
zeQLC#PF?34(AI+NUm3hmh9hE9mb<U95EqF%eC$M@hoGih%P&0Gm91eOyVVsmE$dX2
zY0Da5C1`YtS977|mXy!MGqa;~Oi2$}cpiVXT?DIsM!V(-uB;=%Zq=ZVgr2<IckeNK
zNhGPU8=9JrZK>A6{dwt+-*&{hrE=Q~9;-V1__<i$FCYTrL!svIG#BB7pVS$oO(im|
zb-ifCa`0KiX{Qb!OQDg4jtAOKs7EAR>1b3U%isWn2G6fkSA4Y`w~Q{S%-d8dH`+oF
zK4dXH=w{nT$(RLvSv+%5Q8tdFhOd41$<5o}sfQxikg7ITm<A$^l3CiwUP{F;p4kf@
zhGB@%um1xVS!#)mzgLsAZjLGsH+*!mZ5N46YU7;62l;lawpIqbjs9|81NMb4U)Rx7
zU(eCDk_s$1AP75yp<>h6(?_)XZ*ax3)z6Vn17cd-))%Mfn1wIwWRH0@`ZV#$o9A2-
zNJOGDR-E7RBdyTU2g|8VgNN6}rbX*;eyqXBZlzvk>8+p}qw^F0FF_=b)QA&M$8QWY
zn0xiR^t#NtGrh(wmXX9up-AXlmca?11v(E}@xzOL^oA`>pkQ#}ffDh&_*^N+^W*6m
zSAlsv)#C7zy*qtbCRtkqG<Q-q8F-@chY2e=Z+GSI|D4fB?U6_MyZ+p?$YO{ZZA$Z8
zX=8TDFr6Ta#&_{tn=Jjd(lM3VP!|FIU0=r)cvbjQWVx7#$s7rmszE=UkBE?9YmyhX
zbe3oAZ8l%j@BNJ6&_~g*=B1rM7(Q?S7Ht*eu-F*eGB6lyeN0o}vSnTr!cJu}ysle+
z%pV)jMoOdKqCJ0v1zz-KbN_=ndXpDicX*?QPe~1J)2ekl#Lv>c-SVOqydFRFDS7^|
z1JNe+h-{8<is-z5de}!e;TA-?w+I*6!dp|BL}LItu<*f079Q=;shxRYY5Si>ibcpZ
z@P0T2hQwQL5Yog~fMu&>j+btTVDPD3sHg5F9^_00_LxuW5TEC}rJU9aEWYJi&+7T~
zS!^*DL*2%YO3mMP4#Vu0(WbPDCmXcjj<d+6Em=WtZocN-r~M^obusxBVk}0XGMOW{
z5jA4B!!ypgl8r@2Z(h;i{Cqy-@h@I2`%<hJu-tu)u?l>N7YICeUs}d3+7`R1O`9r=
z=0V#WY<sOaV7;M`oU|MIu79N;yVb7wv#rhj2lw7vf9ev7!<lN(iGi9A7}Xy<bLPk`
zJ*8Igt@5H4JQg(Z|33$z&8Q(%YN8cAcY>$0o;zPZTZIp5rfIjCJtVUXBxONw)7b6w
z6i~<oo2e5X?a*bl25RBe(_DGKIrD)*wM?C5k*9d!bdTb_=g;JQiKC&auvfV#+jX?S
zqM!e+=W*0u_0_sB{oBs!qMbTo$^>2dU-sAh8kcQZ@v2Xz-4|i>pSTNMZRoH4x-*+M
zykR*XLq7bXO7bBC=s13i4W5lkdsuV2zzr<@Z~Z*!2GG+N^k^%Nn6Rf$StuVif(`_Z
z!E{wLlGZ>5?Rvr6Vi{-|1|-*}#-#PtF7LFFl<i2}N-T}7zF`Y%`X{jas5Y{D^*HgP
zS`*mFr?70RI5d&e<<a%uZ}DwcFM7et)Bl+u=9<70m^-1(s6}Lw!UMN1d-2woYGZ*i
zYs_>>XL$`0e5c=lg$J_qk%k7uT)KMg`FawKB-ZxeN+98U*>HYSZ{uyztwA%`Rn&(h
z0#h&}atzhJtiS7hh|gaTT~6uu`)~ZF(;H6e@eaBwD8~Txc8<zz$-RI;2R>*P-eM+V
z&cS@cH=fbME|<m5Q3!v2#K_Vw@nCxs3w#+A&7VY+B?e_%EX(CPPpk#=nNRELLj~l8
zB9Jw^jXb_9j~O)?X&=TEuz2Q|JuuO5u8@_3q3@<Qh#-3Wd*E7B9&8(Vu*6|eY2r^G
z@=Jd*ZnKR-FI#L;sQEPZJ@8&k$xOLz-E-%Td_WWN!;0YD1d+f(k4}W>no9_|=;S$G
zc+g%`)0}DAu?M|fVtAXom6j<*+l#n{ZktzjW*#($?q1Noz@jkLmiu6@>z;N_;AOG%
zo2oB2S7(f2piOW0CxdUR?l_?8S0;b@b3^A0w6oI_QMdz98phZ2-dOltm(RP{zVL<l
zHaskM2Ep5ODSB?Mc%W-xK|xDJz_)$%DV;;m{qoc9fxp`?J;US`sAC>M$&uq<jPyh=
zhp2!pOz~*L_X~C{ug6t?{_|J#2tR$vNXSDsT~ebU+V)!Hc&ek;!AF^6E0C)i4=Z9T
z1|+L7b$CTv-fDKf><kKveRx&&${&&n6=da<rxWo467)JUV1tH7S<A)x;Q8}MZuyZ`
zHZpBq^n$mI=QsaA6Zr>kD`0`y%Rlv`W{k?xu}U1gbaIwMQ)XFlYP;~EQWn~abfmN7
z#UqAg5W@q(&q00@OUr{xJ+SJ;<;&0MG0(O4JE1?ZuUrvRljY`E3~rWVL`9i!>}E7q
z@|rl?`kWZMte@?4ft#*g<YZxt)UO0@f0wSyf5!<O20yoX^2r;UCmz49FCH1AFmXr!
z;i=tUVl^2{ZkS~E>bdogYLVZoH@VIW9aB8Vj-!q$icw^(ndYfc9*`9;{Xr+Rb=nH?
z0G%I`&1<!=!~OcTaBG}8`sE8?jE}%Ff=4jWuB(F%(st4YX#m(#vi3}OkkNC0%mWFq
z3&D$Txmlq*7sS$XIQQSrmssgeR$^`6THm=py|3^V1nKTAKzAV*FW&qHO@^(D_sK=~
zy%aimb8a|1{`4ofQDlKYt%Heb!(LRC#W^d7mwJ)G;U^F6)fFQr=yov+uh5*8e_p>y
z>p_p-c+RHSyFc{=6F|*$f?EGtwhRCOI{ZmQK~%+q-pPnKd?(H*fFZVP^ub|{yQ@xV
zZ|_^yw_$EE4m?Z#s*XUeUgr7sx({F1q+i!j%X6xId9Q46FFQXC@e>gj{IKZOiyVXW
z>#DwRuKQ73-4~pwB(Cka)5Fw_Q}n2f1>nB&rW~CB?H5n*V31$<$B+A2f83kTlX~z}
zrJdzh8{gN(p;##tN@=m8#fk=ZDPFv|yB2qc;PS<ayE_zj3Bldn1BF76;4aC-?{9eK
z<-C|RYtCI~*131@&;DsY-ZLVV^)1xmYjvO`s@pq${XbE51rL!6r$WnK1#Lv-{3`XI
zT3a%3W0dw4Z=9FXBu^nXHS>D$$QWY1s)d|p&9mxHuV_@k%h}IQsab$uxU~||kXbB7
zufQ$d;oXs&#5{hjaA(b@P!Z-z8Y^h@kBABeTZN0IHftRVyBUMDb5-hTq<Ts(O+g#(
zm322KDDxvPZ{E|1#U_{QQgo!m)9%`MBfb9P%thR2bMat8bRN07@9g9rO5KUr(dCA`
z4>lkD4PWK9=f$(mjtC}DTu9{ZXsU})PaC@I^@$KztM=pYQ?#r2bLQw(?(dfi0c6+b
zutCb`KqEn@cgnDc>mvQJ)1c#d@(;5RiDw)sag057WSPt;jK1_zLeksc<tuf~(vY=4
zSTi#6coxfl*T|~Gtx*;t#_fc{K`T<;{a6P^N=I9FiP}OuWP?!KFQ-Jj;9FG!K_$ay
zX6Og^<OeXGmxyc6%#%O)+|$%I1p+w%qw4GFul6nMPM*&7Z1M$ag$W)<e_g*GX5NM~
zdkoXu67`gi4Q|Vl>HoEgziz`RP;-lu@J!XH8tw{?W07o#2>7ZzS<DslKF<WHgt0%S
z?)i4Q@ZeIP^>C48LQnKS>cGP$&Vi50Q)L<DTppPaALc=Mp|z(IDJ^jO-<)5Pr3Vr1
zG0xG5ILz|m-(>>sGS+*gbL@aCCS|X})t`%sv0-6#?VQSXGMLJYOMm49aR*mQG4dtN
z^w1Y1aar9D7rMT6w7_Zu?cXdYg3i@&@flz_9o0Mm#6ErV>Ccl?UI~(>z6nQPUaGyW
zy64My^5*+QL+~EYvRnSmOdX7k(~AyOm2>GW+HcdK{MV(~`DrcrOF~L<BFTd;!Gwdb
zUTq%u-gk33q}6(>Q!st>$6#bDBYgdFPD9ia79ViXZwBzyz=}r;fzS2725&L3&*!Yf
z{W;M~9_X|At%If`p3L07k2MVVZvig?xV}FveQ}9=5Kq8sa;_Tv<uNM{zml4;9s=*>
zuINC)bd8~iDIrSDk7d!hgbHuD=)m`R17PC<!&iUI1Y94B;clxU?Vmxn4rT>1!oyiV
zX^O$CR}lv2TxT>2Y0~s~rVAEC))J<1a7hB1D=$G@qQBPBc|4BI+ayeDoT9D|NBgr8
z6e4rA9_<=f>uqUW5y10a>PL89O_y5#1Jzhmcq?3}*mi(eXqQY&#m@FZxt-M&f9ahb
zY*2}|;)9(PrhRZ#->T&l0~?++o{%pS-Ed?b`-AD5_!Dx?RQMJBScK^H1lumS*7vL9
z`+(j9HaVGFEfeD<i&gnKW)w~Wa(iW{c-v&>_KMoEGQg-|oN#6kOTR{La?|e-YA5J<
z@-XsUhJQ+m)Dv|SW2<U;VI6NK`{{6E(dNvrbk~BE8QgyL_9%H82-jutSQV(0@Fwtm
zE^+jd^_qtcgjz%`NBa(JPq-(piH}=mG2bRQj`tpMzWM1iSqlK|bU5{;BL0iB_kNw>
zcYV%S6G|31H%YXmYeXrwu2cyHTMRFQher|)cz|ZtqqF^g;>$v<k8TdK14Kop#_^MX
zn3w<juZDojB9*Yrix!J9aZuCQ(tG`LbhLEM70u@XKcUD1z4>}sYkrU8JZhd!a|H#O
zc>n!Goa(gk-6=>FzNve4!U2|M25(J4H0doy(L@JkczMJYB>~EnI7%!I+d<2FY;L>(
zn;550PPaP*_ynbB`vY|El`a3hi*X)2q5Nfj@+k$$Set065hvW+S^8xy5dB|bfbL!x
zVc5a5#HPA9<x<`1O3nFKi98XNu?|`8lbKl6<y2=n))2KSlWv>QR~vgiZej;E(w`Q4
zN__b&-RIsnLmul|lg-%rhB!hLZ=r1BGP$}3Du$OkLJp^|{1Jcn2;lH68bV8d9N)<z
za`V_9cM-&E4Nxw;V@au_WJI<TMAT4ock&K0?M>81sGR?e@ZpSp&t)uTb*YWiX|RCm
zknOb{na5l7fdK}*$WlIRed?QOemXTG5WZ!T%KgT({(?Il;wZKX*lWnSS^ti?ZaJvZ
z5;a+>Yne}E=(p)aA&J7UuCV3cW|tb5?tAe@>{R&<6J&zvYpDR2JIE~(x)z)J_;fIB
zgjl4<vQHW#hP=I$H4NOz?Jn7@l+b+pT-Ef-?;%+Ce*Od4NC)zeRz<%T9LkOTg|AQi
z&8Q9wYCJA?%#~M0b<>+~Mt+1UEs>XD)v-b`Gum;7;#=`Aj@c{trwH6<ID5q)iTZwO
zk(}Gp;I;6^$rCfd(NoC<8f{K>va%pWLP9-P6RO8$WY~%fGD(?YJX-JfR=lUMQ=Ug|
z<aT(L%%?^19{3aF&U~}~y}lQ~hczh8e_!72dmquC`;dZo+8ULF7gN1WH=c#Ks^Bjt
zE%xj!QrL+1E)>OvXn^Y?XXqDthDSzf-hwY`sx9}uNQf$Fzy*b{hikv=B+mh0%g7hp
zP4lG8#GEap6CpK}Z1ShWjkoSZJT}H>1}F3P#ahYXY&dMCWKrR6a%iAG9EGjU+FSz1
zh?XyV{p<)!#V*&<4P0Z-hN=yu%<q3o*4{7B@KE*rRI=R+%MiEidHo$U8b^D8l+Eo@
z60V?j+z(*IJz5+{{@cj11{rb~&BfPwuUt6P!w{P|>1Ha{cXKLa9gOuWoZ(Hnn3>bK
zrh6fB<zHR_Gsfn5eA3S7aG+GY`D3$@_Z)psUHIWsbD8Mofo(Xe$4xJ4?uJ!ld(#KR
zZ;kM<cUJ6dH?^iwQ|S{tlr`}6#XVe#?Lvud`Aj6~#16X;@8|wATKEtR?J~{E+u9$T
z0$_tLs*IMrL%*QmXKNXg&98c21}n;0PtY03qhlwGhnZ#9TlDIE+VQSZ97bDGc(l>6
zZ*{ZQHsY|RT#DyjY?5!b*?ExJ@%!v5{9!q^nYgHBQSX^w*loW|_c^gjux0y3(MJ&(
z4!{&4b8(=_uc-lW&-QmJ1IMp6F1#Cj=SJV=uzuk#m<j4!dDm>)ioc&%?e!3XyMv}2
zW34Ty<RiY_j}|cQx3XL#-Weqva!+u;QZqnT3ctc>F<K@*@Uha*5b}ga)^OPs5m{4E
zi8VKw_N#{$ggMucM91}ht84>80}m+N2(8y60u2+XB(anEtKPOkBSm?!zmYxD;97|C
z%%mwO;Otz5MKF3l+!*=G-C;r}7<P>HG?nt^aC++9B^CRNjK_bnOY{Aur#Bu`J<Li~
zt?HaEEjj6M{Ry`(zuuWAPN7wvb3z)d{f+s<o`mPEJuMGBY)|avP#~JdDZ{&TJ^AJD
zQ)yiG%~Dx7c(1baDiO94=y2}!!xcXNEa}-9p6B<sgM3R{bWmNCX_<UxRQn@WJ!9*W
zTd!8?4P;))6dRSos!784I~pl0P(ucohm<?Nn!=QJC==Kfv%9iy^s-Db!+kz}#{_nD
z9rDwk_(+&E?YC(%db;1%gWD^ve59&6&e?#yKxhF2r9m|}v$0X*nf{~qN0+>D5<UFX
z(9%n>81#A)eA^u`BtBK}TnRt9n73Q|VCgmbPgZGZu+63;;UoNngDQxj`Ef3XUBYO<
zeCnj_oh6eaThj2t{x35r9ap4D(q2k+m77u-B?t0=w&@K5Pl&zgFPN2^@AW$QYiG;#
z^RgP_KsgHp`JM@1h6>yR?K9*&Dn{S;)pV-uUkqrmen#Wz{uLCg@iBz9-S}IG*5oX@
z;#T|baCvzQr2*^e*p+(hW-!MpCH7uOk6mC`rG4{*UUz`p8_h?pnvIpNyEJl$&;>1Q
zw1e&8p)(ivpp?X>g1*gOOSI1PPR~2tjb?)Utv-vog1cjmWb+TX7ti;sEweJqS=(Q-
z17e(^d?&8#KRY<^gn03C{zXdXdDn{;0PcXLtuu!6kduE+9YBSs^9=95O*w0PCj(??
zNIM#tEY5t@c|Ynj2~^}VZtpEwVC)E?4|B1?i_tlJ?`7<rYSgnP&ra{|owlN+!)t34
zt1>#I)PoRvIr=B{wiuo^MOlnnbb1sjfahbz92JCgnYdb~0ya)idoc&c$DAl4asj2E
z;~TlwYbV0|$jS86J+4sz?vH6nwc#-3OFTC(&K~5ENG}Bk<;a<ai8iBI>R;ZqK7&T@
zj4Axb4<|dpJK5k3#_}~6+AqAt3q^%vx4(-Vp2&FXY0$qnY0>^}=b38hV=2)QwiI4o
zKTG_xtwTDVAYO}wX(5|)V49~^*6MgFmIPiFeeRsLj&av9{&M3P?RGjF3-6j6ld>c&
zcuwm&`slH&haT@&WSsNWB))Dw7Oj57Y1{i1FD+K3?YJ-2UyTSibCsbWn*CN+8!-F}
zq8`RFCNRVu(%m%9^qA<E78fI8l<8^dD|2@qTHM(tQdG1C-i<5woKjgnjby9H2~{GO
z-CJ69Jy_XHs5<rL&eZZtOa;8b@(B@!vtH}zG(&q;w$<2(*PJtcsn~y&Veg6jH5~X<
z6nlX+!(J5yn=ey(+)KK{d>;fqF-zkGUt=jlr_+vk2(R@0E_*@*lAe$NKGvtEKDLH5
zF>(%J|GavY1++7=pc$=1P>-G9k|*<N7@NuDW0PP%4?VH66Ms?T+1*xa=Sn-z4hZ~A
zYg87P8IQxpAPnWSdcX{sGoooUa>cUkTFaHd4^K~EVW;beqBl{0mSUL|dK+W3w=mT|
zfMQGGa(WM|jpvg=BGQof9^fiEjc>m|V>lf1sov&kj_&3}|47uijL=s`+TCirM4p#I
zqbxv~Y<mg@E1c>uTlnPjCHDux3BB7WOInoxd&*8i@%QDUl^ucmFB|uT4e1u&gIl0s
zyv|5+T&LA!0Y|C0GEKdV*2-D$%&sO%m>N{rnddR<axco36<&fIg=!n`ZfLbO)?AYN
z3?aZ&r}_V$yMNo-BIh@q%~c5hyvEA6k5P1I3I0YHI-vCoMHTvUAG&aXFYr_&c(Q*m
zgVqZ<0$;5Y@+ylsPqt%2nkROWwA8I)g!bR&$X_Z8CB75<%9PqRC?8A0RYLVoZQ|T&
zHMfau@rO6Fu!lGgMUG0Jd@Pdgmbb+8a`#gVFz$=jIE&#Mk|3VSHz1nUvxPiyqm4Nb
zN-RIFXU~SIc(kDvAullQVr?vf@^Cje_nXvpGJNT!P-z?44L(D4VKZ^_)!caj_DB1j
zWrKhpM>2PPS~lmGKlAgZ^db!l6W7Bqz8@GNA-L7So8xJ<hfXcHXZA=zBC+1vK}ngU
z2iJE(Ru8jltngPF+_u=4o1Pyh@2Hv2(+f+J-^<>fTeS1c_6C};F9eR^wd>Z^Pxmby
z@A(_FO^fC?G73%J?NW4hJ!HpOR}So|x)M!S*Xm~~<VuUDLQ_TL^nXnWSvS6$VV47U
z+YRO^P!$!&7qPau*Z*`aO;7SES}T52?%nXOFDNYj&11Zfk1FpdbmBR(Q(^LfdCEo)
z84(%K@$9ITC^)AeavE8`8hM%m)K1zl>%c5B6p|(QhH;&asv>f8!6Z;|m5q_`s^pv&
zyYi$`j<2@Bb|J=Am|SsR-^M)i_#5rC8E(Ys$S!jC-kY?-@}mp*d9kXCO0#!tt0A2$
z2#FWIFk18)aXIOovnX8OY1QLE=%8h><C=s8ygK04Oo$h#SM%)U#eu07)6+DxE0f`l
zPz!urlnTxAZ5r{`6vE0rID%=k?d%^$Puq(;)XV$KF}E&{{)Raenbfa%8+`WdOF&9+
zXhyKOdl{I1WTtiPFYT~b=;RGoOxvS`rIX*%IFT-}(+HTbw11SFYQ67suI^oiq7x6}
z@0`#GnJi1~vs!m_sc9_<x2JAKc|3a2+rd_54B-(2&u2+ONCmrm{zeBO7(C}fjU*HQ
z*4H0}`-Emwl3dA?Zw(;}4?~t>6>q^|>@D428-|sQU4@9*uJ(Hd-_~q$aGngqKJ#={
z`KV9cSbgRiOU6tC1Q7-F_m}ZF`X3)oqI4LCR5jr+lA@je8;%;QT;znpe>2@-NT{9H
z`$<~4#vLCxvcBx~5^Xg0<@!NS@R1~(FAfiQ@(>#u2>&TE&yprSVt$7&T|Sx9c-bCo
z@{fhfW=i-m$Od6)sB)?5IM70lg>{f{%et&40UMcK#cYUDy)XCI)<1%QCk94Eyy!U<
zj)SY@k9q<&XCv`;aCz7!f@CGFXyT=9_qO>y&Y->NA-`q?fs$g;u6=ip))lbRxXeSC
zYZ+vIo|OBze(Md}QT-}QCgSepN6hAobK+*29UEzN^VyLr0^ix^Jc4?B3<*4M1oAMz
zQena?hcAlsO$vLHUW#5q8Nh!MqnaS(9`$vK7uf!%aqDq1L}f(+<0xE5!TI)^Al=Xa
zS>MZPe|RPC>TzEojWHlLF(GxnV>2a>)u^1tpR?2pRwV9`+1pdyeNB{LQ%}-D8$vAN
zJ{FVLEyB#Y%AwS_MRd&LhCms9{oGko9q3$N&>PdPmNagV4K(iT*Z2>sp8hdl9{;On
zt<qxMdN$*YcYr5I8!H<Dl5uNdp7!Wj<MS$s+-@oLagBJpnODa{jg_~KJLtuMI^R;!
zK$#yTq<_1s|2D=q`nczT9VW}NwM5t_lK$>LJ<aCUWrsVu<!4<W2W9FL@(K=CB241(
zjo0^99T$o|Vo>qbUWIQ{<k!t=(6+lgGxD0gX;Eq9C;-Km#p`ikP+seLLSzf6U!KaZ
zwgWZA)pYQ!cdb4^Y&`G!aNOCzW5N&=5TA$QBuVsB2fC8$y+<_iwK0)*Fxn~XFhE!M
zciWMVJY6)b(hlGt0Nm8NP$`>{fp6>>dgclnqr9~gZPK#%nACkX)69IPCw6L`6aYGr
z0s{~R{101tE?gE!Li9lLr(_(M)D<t2RW-l5MW9;vSh~~Ll_c&4dDlp}Ss7IKIrR=Y
zeaTL(%d_?^l3t5@KhD)6|2^Jr<FW&EIjoz~%45bYPNqwp-a8Ik_393>dNq7Kzke~Q
zLxz^UjM<a@V$}=Oc@=y9z28R~{JrVj`|401zVW;d!&-L71;y2MF!SfUiTb^k!wTqa
z;1w;Aw{SI$3#$iaz<5~rNSf?KLDrTmZliDBb(;m{ys_1!c(zpDnKp=Wvkj2sg@_DX
z;0TvZMO;Rq{e<%WF6feaW+58`LI@5ltVv0S$eb!Mn7d?+d?+-#&jd&GhXzoX*^phr
z)(Ee8zn<w%`<aNhD^NRpEH!VAuzSnwzdxlb*j9rDihd7qj~V%HS4tj&^&vaw&mn;V
z$JDlHRV(|}%qs7lEz!V?$Liyo$np!F=Q_xB>3o*sgVrhc*NE$4W_}m)#W*JNP;WzU
zo*Jv)!_dB#`!23`lm0fdG0Muxh;gcjc1`B)M;&sJcABC+O3$Rm)JfQ!@p0QMT^*kP
zO`ks}g>iIHm?l|O379EbXI)&&LY1PrIg1zkFtESFyQpZaIaVsJ*YO4J@luR)y;Ti*
zN^4p5>~<dpbgeQ;`n$e#Pu;>x5@6<JPS!U5Liq+(PVE=l3%|$!6>J%)qgwOeTM}XZ
zSjM6)^!S>aarA~0`+WdLmGYP4iPsoS<$8bnj|b_%1d@F8b1^ex8~L71lQN~;nXR4V
z+oop)_xQGIq*UeBLyKoZtq~lg)foxC&sjeOYdCk9Aa15_B4P}JU`4LK5Q)tz948l+
zR1TDX|7(A3=kaMnzq(BME_T_JDte2VTcW`uY|YnTXKE!KyQCKKt2uT^N6A^s9?CN7
zpSdquuPkEgqcPwyzdkFONQd`*tb4!m4I#yin6n!MeLdYhdg)3Kc%Gtt(&i^cAQabx
z@o;=}kCERxv=sA8LX#oI0J1bj^}N0nFcz=uQOT{#4MhvD!IX6=s2YlSAm1dRK}Ryo
z!cY}hU?L$lOLo>>!<UgG{~5{{65g|oO2#E*&WaV6SFQxT<|8b0ayc&UM)eCG=P4Z&
zTD?Y1u4i^zOEwW{BFaG@PX9n!%fq?#_=>zIInDHd&-9iO@ZaIojZ6zzf<&V5-@Vn?
zf6fvs#zFP&QjL$q{t~^m5@(EAtYFe>+4L*pMzX(k@%#b_hI@r!;a{#!-H5~0MW8!e
zuKC&i5BmmH`)b!smCG;l(ZG;;hxL~BTSD-*`WA5id7nPo@1eDJC3u+<qV59cywL#{
zRhT{e4jnru3ZyAkHW$R&(7w{>U|ps67t-Z+Ru~JMT%r5&*&?eKz#pO`VXam8p2a*~
zA!1ME=yxHrJ{=MxG4B_ZPT@?pP3&C~J2e~^j=)+Ig`+0DyK?fEL43VcwT5)<=iz%X
zzqyRm!d-bm?t0a=y%DLl{c&)A=3gg0%13=HKznkrnNR_fBWYHgTw6|xR6r;-{@$m>
z620(fRuwze1X}T<j8un;XmH`U6tSetUl-w<T=w{Y`4${y)cZ(wbAOaE=8pHxImSCE
zyI7CaK1}ft3*3<DpW3qtj?2@d19y!)p#f53OfB1ov4&1PW<KyK&D7=BeYzjz%7(Qc
zN1AUP{jOx+E1-T`CMV0v{qZg9y-^6{=W!FZ0RC@*rTirCtLkj_WF;MPuP5^m<dinn
z0OkCT8l>pI=I<Le>@%DdoW)~-FJKw4R^Gy!-8#f``+NsvyNQrEIxZ|c+tlCYW&CNK
znF!t!S>*A!W{_>#fZXx(VV*qO>htz-GJJ!t3M~M>LF|w0>}mFr@sDrR0C<jRuxoBr
ziVqPY*pZ5WR=MSGHpGvRx)Hd>gPrO%2-bjiAXA3p>MaMkysq;otRp3`&g(juiGUR2
z7iT8vmArVz6d;#|m%6bC>H`p2gMJn*!ltz`oL$hga?x7v%i)`x+ei={an3g!NKQPB
z73~2dS8m*84D0PV5`!)G46_6Dx`eQti1;KC#hO|D_zQ~g0#m*?p({>F|6rp391d<D
zWR*RfGQ8Z%Cv|5?z@&0k8~B-fJz`e>v9$1|o4p76G?_L1V4CH;oO4fV()j~uYc#hX
z)f&LcL?qh(;OG=^E+gYj>^y?`z-qYo3rj<+Ts-U}v71|VIXZI}3BHrYT8|SpA-R~E
zj1v41v_@uCV53h{H}V!UyXbsi0P&L$;x=AkT?VixZ-HRlNh{4Y+b*a6;rSe6R%hM|
z)CEt#&)>Z?@EXku3xReL#T080KlY|xgR-$dUW!Y(IPmYaT3K~okYmT=grKS=&3f2~
z+pV&Sz1K7=7vk^a(fbmgmMo*)Vla2Uv_A%9sv==jja1V4&R1SIUXbCJp^n6)FCS53
zOltVZ$Y1SX*gX<_FrL(Bx=;0K3OQyV2=G*O{1;D!)ySs?ca|*ZN*sL-p#BZV|38S!
z;7-RwbH0;y%$kFaHi3j^`Eyecsy6H0>R9g6tjHRnYhQ_=;fXM{_Cg8gaD2~^YGWx6
zz0$@mq1`F5f}iU2HP3yx1M@RE`9=3+z=K}xeQ=q4nKH!#{n)xu27Vvpn41#AO;>jB
z?7{QsQi}X3OH8WumH@iE6&%FGZ@-6SnGjTq1{;lP35;2cEL*39p=7{=3X)sQ7YBSV
zge3?>PK*iGq3b^|^)QSTpH0*U#sagDn8F9pWBgnUeE*wz^W?Jnnh+v<n7hbQo$bdV
z51aN+P2;=mqy5F$ltb8zS$|2d6J?_Apc-<lMDkIv=IWoy7&W2&pT{6<<z&8$!&Dub
zOv|UYTOKR`|L8BfD+8>U7fn9v$d-nk#qU!+&q2EeWquFrg?J?5B1>l^J}<~u&-)Qm
zT@!>WBjdSfceQdVeeuvi3{k)V0)06keqa)lx`98ciT+`c>2(-*#M_h`N83aBCRe1E
z6e~N{Bg;Vj_M<LZKwZU7#8z+0Tp_Mz9s|t>J)yM(+>VVjBs$vg+aj$?3o$t=oN|UE
z3f`k^zK7?1+~Zl@O#@|P@`^$Co9i5=J}(rQpWg9qhx~<6z&0_N*dlM>%SdnP6F)HV
zeq6b0jQ=!RIt}5P%Ov#2{wl|IaIeN#)J*e-{VXvVSL@nNne?a3%tTafmM7@}(v$MH
zdKZL_+mcS72Hh{quKOnO-gUS}k=dkZ;5P%y$E^?s59uJn`_C)gjJ{s<;$%w}{~$$!
z@Rbp{AzF9($xGox=OZr9H0OPh@3bpP)jiyfW7<?r_TH29be6~3QuxRvr4>R^SrG%b
zEQGP7TMIaw%%isTsOJ1~wo+@!t4MD5cwE!+)R(elf724Z^xr+r#zRv$44~**3ptDO
z4&Lc>xXIasf@kTkR-bF`wx#lJ4<u>~P+dyf2rNmmG8vc@*BdRYxVkc!N+$6MGIJ)f
znD&D=J7mT>H1{ZOo})1e3w--iz;v;V7k3p}`pDbZX{0ZD?EzZ_tt1pRE|+bj2tNnH
zbQjG8E+69;Wg787bN@8-&fU~`??r1GFCaysP~t(;^Mc}VC7bdGN_m#YllCHbdyR`W
z7GEGq70Jokc>Xx58VfadM!rU7eB49d%KM}RQ(anlL6cC&p3nk=H6BN2SpQfHS^B!Y
z^^+0%_>Kp}*^nygM_nD;&&Qd(=X=g9ApDrnjl~Zk1}Q!GuOBgeC|v@)9gjcOnDc>Y
z*2e8ch^WK0vHgM;3nNMO+OVgzn;FY0Edbifn>f>t<jkH;^)uy~IZ7-7UMF#xBsqrn
zmMH-bDb~<(`s%qzIqEI~^&dAv-L24YB32!bUm%t>pTEY~nLef5!zl-6vw51zKQ1d%
zj*hGLyGf7d*1?oVG8=LY`^8fild{?O`T<WXW!FMNqqeG_eq$yDP5$^Ky*mq<rWxCN
zLm?OvQ5#pi7=D*Y#NCb0e2zvx*6~@F+q`k|)GWU@P?hw4;4Sg)yGY=){sQE0pSao-
z#MiGs$5Ti;=8975njSAcGT=E{$mH!EPrfKx+>_P!>kZ&~wCfHd{|6kATbY{BR!B|t
zDi7npC4Thrvu}?j1#$<)dI#;AT+erW{XzD6$*I!MORDB1$NL%IPKq3b+r59bGIRzy
zW~>)fXlPF2)h`76)XIii7Z`%<BOZ>>0<GTP<ADyIDH!#DraAS5KaA0Y-}jsWcXW(g
z^O~&Yh*y#t(FKdTw5Msb=|+WgeEy~l!`~#tYK}?DOzbrq&DO{D5e#_@%!5NIoYrdA
zdlYJ&d1&=lKe}+7Pgrca4ZIwFvTKhVVRn2P3GZeryD@$jV3y}ypw?Qiog8EVAfLzv
z-hs`&VCk<}CJeJu1F5YZ1@Nq=D0FwwC|;^jW0YEV(P$XEDi1Dd%@@-_D$66_gmKHX
z53PSrxGWp-wl<zmR<E*vGXCEmD^s;@ev1gx<E`~R6Vu)_?kkQezWxgZI?O1G`yH>J
zd33MneQ!B5_2}MdTL03myp&Wmz{jWxBX!SA46KMkkNLIvICAaGaImhKNe>IQ)?4cz
zXEXL(YjY?nE!m~Z8o_I-dn{>@M~=HagVvn@(CI+T#duk!!fW7*2rV94EXOFa${2oN
zDU5kLC@jd~Upve1{=K1Hjt<UyESbk4^*4oSJ~V9XF&*N>$g!664)^ha>G|<?<a5-O
zTMN7cG0CH#!O$H)CHIPYmY)p%sqNK#A*>HoO&}%$A^(GYWHQZw)<mzxs0~1Y?7GOM
z%$Ca-78so+|HNNNT{xfnK`4qVA)SxAz{a$Ig7{tY%<LJ`K%Kh{UTS3e2ML_`J}yUC
zwa;W@UCP%GLO}~fz0e0c{_u)2$fY`Q(>28ZSB&>}^|^!d=UdXrcdb~eeEe(CG6eS#
zF`2=N2lLrevRBwh^L%wEfDRYo;4d6pHZf~&v`;)02c)PSUoH!I$jV|+BMtqqRj$}Z
zr$P?qh^63#vrUT4?dfQ!OwG`$%z4O-+$Pa(0BN1GABM4^N<De>%^8CUqSYnsxWDBk
z>4Ans7OJ6~J2Nex_o5)o&`bc>{s!=ouoR@Fh&C81P!lR(PF~NTaLr^;kdvdU{{mW#
zZl%?twrq@~8zOyKaQww4p_1J}n5pMbYxpl=Wo>%m(nAQaU?yMzJmPZXtJd-|b2Z%4
znJJeB7$q0kcQ)T|rZK3Tg1I(Zql_+=gZ&vEvIN^aYdrD46TzpPHn;;EGvJo-=<X7!
z`u*Ampr3Z_N^sGcl19XIpU(|L{Ad-$kO!*nHBj^F3drr5GNa)-u>(Ti>kIpmrF<O4
zKP?Kjw#a3ts<a#bPxwqRbo|fH_n99zbS<ks`cQpxo90*93Y!Pt>`4-c+M8B<{qU`A
zp&t+GCQpMq+f_PjbKzC3n{HZ)XelcSI+WaGi^<WDMc*H!_(32FyOc1;GN`FU8AW`u
z^w(izy(@Di_sOCD3#7^z9^8ukn6fsp6{^LZM(N6vTZR$OAz@6neKgd;zd&=Z+`NL=
z?`sn*$9Xz^YZ)gV4UgcK24;9?+*m#!V;aTdIyOGspZ%JBD&#C?Sai2jvw35aSt+bs
z!_H1y&aKL0<Dz9ZOW{k;zGk<MHk?}>jg2O#ofPLy^jqA`6qmSb7Ka_>{R(%_Zj<))
zM!F=pEg;DEtOE4ABrz;g09pMRCaJNu<f=d$o%<A#$7AeA`<pcxo{VvjZFq<Xc(6he
z2+nz?!k=$x<uLCS#v$hU=*gK9GPw^s69Tz%DojQUx*eus9G|jngn`So>|Rn<y0ebV
zp$`B38gM!MJ4rF`|50>XWeYIp-q#zpoZ9yFY?lJWv9)Yy_dQw1BrOdY!_wUDOD1*D
z!ipYr0$fh1xOz)N_QJF!zUx>;v%q5M*=ljiy(_M!_Hn`kNuH!fK|rTxkZ$N(pUxOh
z%?9P9_}!bNP3Du8C_^Vb0qUSjVu4rLrc69ZZS!dv4l3BnV3e=aXk+VPiu2J`8aVcr
z5C&5JT2%Vy;@$3WvlYUX7WC8-^>i8?W4_hpJzXLnz<cXS+k=81mn(9pZEdK3UmtI{
z#JRWqDUH7mRj0M}VT7=jR__>aRZnA4Z)NmrS%K6|@u%Ie@9b5o)J~Y`gn}pHqh|8l
zsX)EtiBF$-J*`O*S=ee%zD18eRX7X~*rYZ*p)Vs%#BAD)q>xBFT<U@QUDlewVJYJ(
zO=x|lse(GJ!LW&C#p>F7m3n!|RbnES_JzFn?dRMd5nT&svlfkr0=whT(M=sj6i+L=
zI5`w|@%RonvBf!rIlN_*BWC95h9;yabC>~ml%&Oz*4FG1lSwegb#YP@OF%IdGn<@J
ztB_y9O1i#M#G}=1mGhT{CpItem{rlh$=uFoQUjn%#V2T?cIq3fB<D*E4YS&8Imo}*
zzV~&O+m~W`-k3hA-)%9iKzbfr<maKku55Aa%e_BOB~6<Kd0&cWztwuoLBiHSN8REV
zzP_^e2^@4b2$k&e{d}TfBu9e3z>2kf(7f_cZJ=z}<r{tFdKa#hAPm2syed<6gh%N@
zTJ~DaiDNdO&sZX!p7Ow@@mo8mD(lVZm|FAn_?`Q5ha-`68Z4;$30+n8@=|f_DWkfx
zfl|weVaZH(792n0KJD?Hv-7!<ZzVY-0nqkvfY~XyTC-CVPdsQ-3%c2G0@C>|sbK+t
zXUY&=iMuQ90gMRjD_!iZO)CIjO+60le`xM+@bpOB4e{nI^sBpxO;#FPOj$v%Eh!!x
zp(JIZx99ybtThvx?vqn<287E>-+kN{zi^i#)Prl~=RCi&;Sd!-T{zCvD8MILJoZdz
zv-l8dkl=>lnGVQBpqoZ6|2tQSMdgCvx2)Vd)!beAk>K~8Ujx<Bo8HR#&!sUF?RIQ#
z$(0XTn=uLSglncXsYMGDh{z?sB5He6jTas0Z58S)Y;H>O;giuD>2lKle#Q)fP`>$H
zFS!#Nuk&%bq3u{+X*eEKh}~2<UM8@{p@!A-HCN|MPEN|8z7zq5qbWT7Hj&Xcy(d2r
zfRw%=Vm_V-3kPws5+{KKKI#Yv?D7vFuIX!zG-O}&?@fM*#fMFNB3!rKh#OPAu<Ur#
zU=$>8#6DC!kk20a=ZCYTo=nX5@k%PeES-JO_KdEW*{54OuckqTvfvzNd`DYkPo(_<
zzy>oeXraI0G2SJjDBuYz)?@x(clQVMTGz0x80B>VV^VpYbHd0Q#uZnej+4g^<mR4e
zY9TdmnTB*6W*Q#ezx=(RH)4v;sbk`l$2j%*um7pBj2B@H1|Mpfb#n$D<0J+G?8yFx
z@EMbgMBy2Ji;XpFl3FhQq+~kJ#Baz!mA1A~APS^fp)>s~q{@MJKoTI+6e6*2ih{$O
zErmH1fL^Mx9kzkxj~ZOVR)n}kaqZ;u^`Ho8`-6t#0uZ3mQDW6d?fMtpd@=~Ul=<oC
zBN=h-E5s|}G5*&>31*I%6rH;wg)i9dumzloKk{Fl`skwgi(8!`aM2zOj*;~+ra!$!
z*MYhT35Hr-yQM4ptJXHpF{EYktY>mZnS#vMqltBtg_5pl7Cxo+-;dpenk6uduU$vV
z!x9N8{jwZ%s_e8hZBn6sMrSXG$?R;*4HYgASzCfKaP5f)99DJyko8Q&YkF1e_$I8q
zWP|_u231kJOPZZ)Uk`90KzRTZoE>kPwTyrj^A0)1RT6w6rk={f8myan0kONX&&LM-
z!EvX5<R3x%kQ@8=P~kLyBRmuR1fwN*(!99zkIbJoUx(K<v0<jgt1R401=n@vAtkot
zGFUVZPQmHQAZYz}Q^Kxnj(LPgZ^O}RiOFFJ=UpU9?U1MAiv{6akR|Mve{21keP05;
zHg~4PoGOHVk3K+?N64_nYDkR+zfPQ9Bc1IUDWi)!Ag)U*<Zb@iKgJQ|8$$Vnf+Cj$
zhK3AGQTF4H4k+3kkz8EdQkKByiHu1gRh}nI+41w?*1DcT|JIl%3a{24ZE6m3BBc;p
z!7!4N&|b2Yn|QqMvG<vuqGsFd2<p|gFD|<|2nlpAMLp5Sa&zgBz)bgWW1->!ldV$)
zysY}VZI{qAjEu?OeYQkWc?-Cih}atMu?yL@{ApgdS!G#X3C9Q|mAsv+3R@>0B}_e5
zTJi}JykH{k{??WE!s5e^8vi?&T1uMcfAEP+r<U=Uv<Gl`w9V-l6SSph3*2lZdbiw$
zMcaJ~-`}VIL<0F|_7l^coNWzbw5SI}XY}~D5so?SSP2^qjcw3Xx91MCNYB|gnN0$v
zb6?N`vV;0OxJ>D4E@<m-Eb5t`y|n3`bxT{|C6F)xx!YJnIpcq*Y{&K&AB~TQ3JCzw
zUVJSD=*rS}LyC_{n?cRdATZ4u2zx=zw|yK&1=4w~X0+|mE3<%1;F7y&_=bN|`TE6k
zxB239!QPH2p*FIRcLC0@Nqy<LSfEBMDL^2AvZglU>J4}hAm+VpS^J+17@*B;OL;4|
z{<nhQ(XN8P;&!k3NLc2*i{0Aw-hW<RqdPuvq=qX`4sP=1og9MBNNQcOa|3IAQX&`}
z{Hv=~osPP7eh-rZ4G8gQ9{@2`n6kYuhws^o3~zk5hSmg`5peZ=q;DgR|GV|e8)+|L
z>0ehM-4oA61IJ@N$7YZmqPlW3r|s45_(0Zy`B(VAQ<kW64-7t^<Hx`Aygc##kD08K
zlhhs=jIJ5N)dXIa7R%$gq5_%8x-gxYaYOPa=0p;@qQ--}zj$TGEE!E39$xFdcizDm
z5{gk1u>_}sSutM(b|aV}hHrO~);490s$w^yy&lC!3Z-djyAqf%+qhEKb*3Rzjjq1Q
zr$cY4UJAOyE_HHe^Pg5BzE~^EsOIGVElc)bpjAjA(1lHsIq!j_Ct@gFxgP-LMj^Ea
zVBt>h=eybmm1$#n;B8_uLG5zo^1qL<*Qk2cI?W~2LH0^+rs9Ua)0cwxQAG=rI6Op0
zq(jqPr-3?$QYPy%9U&sOMMC&^C%=%$ZaY3sUAAzV*H^7|wdkyV+j;hUW`C9Y?C_F9
zy2K*Rx_;S)a;^r&$RB~;`SkwT6D>RNh97nw?tHGgH&k~83xU3wc}6GFJ>k9B4TrEJ
z8U(!T$Y*9+1(0w1u6SRPe5~oP5FNFv47Te@ylPQ@LJ0~vPX}S4_T%XX3cYL5#KvU1
zPvbq2EZHOvh1OJ$bj><K>D>1pXpf5m(M>y=-fwQL)O@%#Dh!g*B#Yd6(_|2%88=|K
z8*azZ1zWAg_jwrkavjqiK`yZGxixqfpqpfS40&S+UUYJCxGEak39!72JZbLzN?}wV
zCgCT%uzmK+0n~f|R9u*NbMs6<*VFU6oOqfXA^Q?~cYh`-NOnYMqf?i`nWRPuD*mYD
zkGw$m=32WB#I|(1L9VGSnl$wv_{Jje7*jqeF+f9p9mi8rG|bU%Z6O(%x#KUs>mYBc
z;<3Fya-<dv{-*EK3nAh)TUr-7%!@1H_1tm`eZY~+m5~u-WfVW8n~ptDnKNRKI6qp`
zJZy5j=q@DDy~aKKfB-HB!es?gdpB!$t1zA9y)4I*lno(%Dq1JbUrtX-o>LYTy5-n}
z<Yc(8FmeD$1p>y?z}W?b`Nyw6gs%w5+KwA8Prvo37t80I|A7?L*uzGGT_?!wpv=`F
zZ>;Idnr0{hIOCU>#D|mb)Bp47m;Riw$;~SCy?~nHzpTk==m+?;bv?LpIyUdGx6X+0
z<4eQ6w??cD;I)hAX;~pK6hCcZKm4Vi=%B7okrcT}Cg>(l^Z$2wY1nF!Spa$!9c|9@
z!4}4^rE5!lMkS*oX(i7I)$eM-WHEZPek!Eu`{0Lh-(T21s57aIZ6(ppY`fxBQ(Ik~
zElpB6OUKz>Lf0=HUY7U$Tg8r7c&ZTbRg@Y+NBe*u5$A|T7nf*Fhu&F(cU1nhS%rD0
z-3H)VuuRco%ZosF?gulR*1lVE4^Oy-xZ$jnpt7)Yf5bCyji~7DK<<9SKZ!+jD*y-^
zb*fatjh~DN&HQkHiFoa^|De$Exa|{aCnZx{*z#k4VnK6#z)c#`zR*gsdQ#_J&y5oS
zEfKywZF9-wI6L_a)ziyk_RfpF6K(CvrjV0bs({k<4JPitnFYqYSxoSm$g6Gcsq!HO
z3=07M^W+ltyBy8B%Mq&JUEbR81AEEfrhiabZ#6tVLqgl82bJY^?RbKOr&0dt?^h5q
zy=`d2;%;rU&S=p=4WaKNuox4n(DK!}Z`rEZ56S}#+a5LBZMG=d+fCziBC}b*umjAz
z-QXY}>ZuR`%wZIU?coVjrl)$|#VGpI{VpUuph=T`?zboW<r@2d`$aE{t50wyr8&g1
z#q6Rtq%sZ>y0lD@VYTA(raCEUG<~4@!uFw#1vxqg1Xp|X&C7Ccxf5-)RD{5Uc@vn^
zGgqST`u4{Q?{)5TZ=kyV4{x-atj3u?$`)g1H~3ovUZ@z_TQMi^8+f}@=(PQE7pp5z
zCfm1OA}I=W@<%Ed*gFRr&K;y2sOP0OeZZOe+;>^{Pwo<Q@wHhv(`5e~eK!NI{4*h<
zg;D2}4gR-p$3KZ$qfg<yy7$ySgmuGDuCf54?h?L;gIDxV`>D(?-yq9U6ms9EhTsi-
zvnIU$+qb$4GlZPae!nD_AMMRa$K*x13X>Z$mpp{cUG}o6TIPqsPBVocG8%`QcttC#
z{>B~B62D-#6VopW<{;w2k7Nh$LOL$~(JyNC0SyS|UOE8&^*DgWXJsz@E*=hQ%`0as
z<l3axp?Im8j4fMn(K2BKE$kw1$@Lmw*j3-|@%hY;<Mq_7l6mT73WYN7Zf+(dV$tx0
z6XGI(f0BB)R+V-aCs)u8D7Q0V3K~($>5$RtAzRc7c5kc695&G_b1!L8ny%x1TC0bM
z%o-7LwDcYK4rhU@InPD!Q#|k{JI-3?=tLt*ID2G;UvS11`5=kKbvzuhljC+{G1zs-
z&0fODxY;`mSJ|vD+i{@rw3R2wivChnZd8zR$|c7zfljT3$$`|CFOWen7srK6uMzh3
z`gw46>h6#BMA=Qc70zP2eC*S7{1MH@+Axk8-`e~Mj_dn8SL1V@ZIq4cOCJG-)}*@6
zAs2kiYUo_O@kb2pqWPPBK>FL)oiqhT<ASuX=5yv{I@dT4>m$&Z7}i&Kt%1K`t!F?$
zKj*FYvfzz-&dWiyz-Kj~9gD($JtC!mKnq8sK9=^yr*UyE4moBQF2BD6vy-wc4g~iV
z^D+jmih_eJ9i?Q#ua6@E^ZbUZsd=0alCR%-irpB^Dcg(IbWeffaW;-qM!dQWpTLe_
zfPUM<_C-+^LKi<G?Opc!I{IXKj;<WcHcQgty2RQ{XThTz!l|2MUiTEp)t4zup(?>A
zuSg70lp{3GLXBgs{evc5hswz<0W1vB4RPIwJKb%Y^ZZD&$BiOkPSJ$feY`1prSc`n
z2J?|w#}^g@n(~<iR@<Qt35=2D^K?bfDe&IFNEH#F;E%{V?K~SvVp=s(BPQB)OOFL{
z&M1$+U}aiA$9Vc!r8Q^vFqbQgftYKyF*4Q-RH{DL+nh4@khQ=05fkT#rp+y51f4g$
zZ|+|y-Ky$RcmaR)6s5`&ebn{I-!pvhZucf0N?V_{-Fj64`%2;;foBI0BJTV8bk{y_
zC{|`eJ}ogh*gI8&hbU-_*7ce_Cq~KrCxdbQ)2<Q3g9VMJSqK%;M*o1tl>U8kVq+iD
z)b)`bHRa7N#Qnu2ux7xy908R#ptOB)|Hj<J%wb+|r{`qrYx{CTHHbq_@wjm`@HREG
z;nuVBA~nULGafWOdK!PIGzh-vL6Pb1%Ezq;;c%+j_4%E%oTCrKz1rJ2(|ChmQoMCs
z<xP0oz&N4`trw@;Vj02J{f<FqTHCL)i@6gv76e}&vyA{$BPf0&z?DhPq9v}lwe9pn
zHELHjO?NqEwbE?08hMZWXYtlw;W~o|;l~(!ztd@(;Mz>VV;JRw*gL^Tk{#wb&|mhm
z61ycVAA8%+GnL*yJ%1Y|PC@3AV&J6#hP#aI{g)p(oZ7aH#4ndByJ#e|T>NVZh=2zO
zlf7e!Ih4C|rXrm`3&}Y*&6cE1g<}&dH85N;e2hV`hiB(wCm9JrfZ~Gkk=#h0Pc`Vo
z<a(Qx28Zx@oa>ub?bfsIi_rBeqZv{n`=V$F^?lG~S-E-BwTb5WA%H$K1+o4jF7N@k
z%^Upnon_-gzOHC>L~7T;LIpL&X4K8^i$u=<y_U;6T%P>A+it_O62DdgI9QYb$dNay
z1k%z0jBs1N*0gr7Y9H&wczibKJ_VcuV9tnoucyC5`<x$q|J2cUA^a!{lQ=#lYdNz+
z|A8>^M&rx}-$4qcF6j3XBkbUMg=gc28EmiGW%K5rRev%&u{@gdJqYsmc2WixZ;>!&
zOG^K2z&Jc$2EW&)kFQ*hcBhZdE~iZo>bf|HN1A*ry2yN1#{#FWDLQ@J`){5g485Mv
zE12%=U91MGkCjw!_Sk8#ZujRvkov=(jk1e9@V!7odp^-tSwNQ-{Z_!mLdE2hTX;^{
z1Zk=k6-opaT;s=M0P78!Q+&YDQ%HyYi(u2;lSg%l<C6G;4-1iSbbIp>mfcO*x$0dH
zMmhx3KMbh3<M*Qrw;UH-_=LArl{XWhhn0=-LYCS|iIO4hDfTUb>Ldl(ACnZgpE22a
zgLXbCfP1c}D+$R51hyWt)_Gbx=ZQMucJp-T76{6Gz!<equGddKQ;vKthC*iX#xNG)
z)vgCT6ia_^LpqloaMwL$&|ho3$&LeHeCKdJd;A+x@N%$jvCX1eg=>Gk;=$4#;dXzb
z`+W77eXSB~xM130laOWAyj?3RJsHM4LwI9x8A@=G)|BoYe4J*Eh?<<8w6eHM;&reS
zarQEMS0Tt8LCiPNV}g#u>69MSk4<X1&4%*-yLC*5f)FLW7^m=<iKan%y=0}7Bx}Tt
GgZ~fQj4B=g

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/menu/shop.png b/Telegram/Resources/icons/menu/shop.png
new file mode 100644
index 0000000000000000000000000000000000000000..80dfbc6f1cfa20f8e205ccdbac3ac965206afe51
GIT binary patch
literal 687
zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy
zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDq2jfl1QS#WBP}
z@NS6jYR5p4+V49(I?lMvTrh?4r^b_HFE*n<qtqkw6Eap@nf;sF$A?G5^&#&Rqkt0)
zQ#kbxMa#%u-@EtsvvUu&e7pC0UG=x)mh$rXyCve%?B<`(+G@me|NPRqe#`xq8_zzw
z<J@%qRadjjX3zCe6Y6wXfBpRP&#%9(y8QCP!Lr?<D?_rjR&9;aoa(jsq6A<2?z?*7
zuN%*%eSTW>vBGA<<daW|cB(Y4|IJjkGsc4ZNkjM1M-?_|ESu_sl;!&S-4@5KXYW7!
z5UA0_g~6u%@WJ=r53dQ)66-$twk-Huu!dOo)z_s$1y*yN7JmMDCvUs*L=KDBUw7Ti
zNi&k%CveH_T(4U*7cU#L*P;bivji0@vbIKPO<kloO(7&uYigU5;_kbAIR+9JcE{+o
zuQ;1_*l$DF>aSIMf7bk~u$kqf_Qg?cGGp_HH&F`%G^Tp3FyvwkyfXC<vn-24oVnTI
z4LaQuoE9E9f9a)(Z_@<%-*x->*_!p&+4Ed*c*Z8bvM@HLXXb?rlMipocE2n+#oL*9
zuk+MXBeqF;GrlB0?96z<R^c@JEZbyH2?hhrXpJv(l)V-?=uLl|CLqk9#>?3_W5NX8
zN0ow3mPg*(iZb!0WHIgGSg7CW@+e+cz(F`W$wbO^@5ic|`{Hc^!M}^+84Nn*S%5ar
zdtrI=NO!xM07pVW;-jL;8`Ts!zW%OF=&_UAb~MTHPyYV@OfR?pU*RsyxEGXIJYD@<
J);T3K0RTJ270Lhr

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/menu/shop@2x.png b/Telegram/Resources/icons/menu/shop@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..38625e754df56e214a38f600e25a66b63f7b8930
GIT binary patch
literal 1239
zcmV;|1StE7P)<h;3K|Lk000e1NJLTq001xm001xu0ssI2*kEqZ00001b5ch_0Itp)
z=>Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@
z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NGK}keGR9Fe^SXnD>VH7^!F-3+l
zXPzZ98A_tKFb_!*<-$L3L$2Jo^atdE3tUJp3~@n*BtxbQQOHz6gfer^Q>*iy)&BP0
z-#(jjI%V&RujhT%de{4WYrlK#wcl^wjLXP?k%2Fk0h9XwEh;K1DJcmJ4YjtmR`;}M
zJwHF6pPz4UZ@<00wdgG4U}tAHG&J<%voFX>N=lMh?-!+*p^lD@`ucjf?d|O?EiJvh
zy#a@WgrujZ1N8Ovy}Z0o5LZ{%yu3W%(b3VXt1BXNa&o-9yiQL~r>3Te@$vD=%E~gC
zOqZ9JVPRo6H#fv<`V9;W#Cph1XJ@CCm6e#&*475{@bFMD0LjV8lt%!;x3;!|ggz(`
zWK~radA78)2vQShXlTI9*Vk7M4-Y<Je0&`I;NT!9Wd-Hu=hND8adF&vU|<0J!NCC+
z${oJ*fxra?1;BH2a~BsEf&@rNNWkyv>MAE?@$Bp@-DTV+?$FiMg<()oP)0@u=LMD#
z<ml*_nVAWXk&zMM2jK7TkL?1WMGzh(a+3%`%<JoG1gWX1;lTx#5tN&oYinx@kKy5A
z;RleIm`Fn{g5XKpnKrVB^Zfjb2Li*&%1Rp>8^H@8BdD;j5N13e>+9>n4}b*WukY{g
zb6yEgPENw)?(QBP9nF2ZySp)Tb919-lbe8L1YtkIAh};)ND$J4T`7qoJDi@Let&<5
zDLXrxJIu_?9335jsjaQ$JYlII{2O0iU)Yoct*oq=N0DVvX~<b32>++2rw3Dti;H<p
zp&(L0etv#r*xA|P%OJx60Rdo^mzO!Oj_IyJkmMn!&0ueD@9F6&iSWB>k-^*B`}p`6
zZ!XkRUI3k(oFXD3;Ip;0_3-e(qc}J?L`6k`+1}p1zrW`u7Z(>4WiXqYn~#r=!W14J
z?(FP*c6NsHZFvJUHa5cPZ(n%a%F4=wL;<83A_MBs*w`3Sy12Ngebb1bDgaMLN=gcH
zjQWm@s(4CGxMfIEb91xQ@UbxhA}oR{g^_M*CBJ{h|965u)Cb~S8y_E!R!-#a>FH^0
zZB665Ma<$rry#tBdV6~-Dk^v^f&2UWtE;O;2Pb$fK<6!MYHCtagH>UIgO=sZeRK*c
zEiFZ?iHQl+H&kJ9VF(f?axh=dBBN6fDl9_dHF<Y;$Jf9RBp7nwCXHC9AdS&=xEX>h
zCC$i??hMgKjCn_ADWC;A1*G7zu8OyedV|>5SlMa1NeZaacqj_OmmXF{&Bf`5?&T#D
z5sK<N1(#T9DBqyLhVFA?V}n|39HZdKfzK~5FGu@^GKh=wz-V@Hp#{TNGdJN(CoL@v
z%*n|~Oiaw59X3!PG<CbXyPw<3A|OAL;ED+=W{Ava;Opw@Qd3idgM;zH67wS?Bh`<v
za8SX&Tkx-Nz_75efNy)rhk%G_03!oN2EJ$pegcb$u#M3xz@q>F002ovPDHLkV1n!K
BF#P}k

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/menu/shop@3x.png b/Telegram/Resources/icons/menu/shop@3x.png
new file mode 100644
index 0000000000000000000000000000000000000000..0da0b229c58d68318ac6e55a9db8291a10671d22
GIT binary patch
literal 1770
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
M07*qoM6N<$f^!%;>;M1&

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/settings/premium/business/business_away.png b/Telegram/Resources/icons/settings/premium/business/business_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..b6fe3bedefc44399c2ce6273a838cf37d10fb26e
GIT binary patch
literal 662
zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy
zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDq2jfr-J>#WBP}
z@NJ0giH(UO-#-8HRr`6?-zR0_rogbJI&7y>%x28gknVirl@!9peM(`84_C8OP>`A8
z$9>c1ZGXOZ|MS`v`G0@CdpYNI%yX;fme2Re^F5z$d*^%nvWpo$YLk7`tY!GRk3Oog
z+yC5nu~4UqT)+SF%l?lXmfp<SbvIA#_tf_#R?|<bE<9jz`st<+t=g&odUwaHyZrKq
z&)=uzyJHsy{0Q>aoBqDcI_2*Ba-S5l*?Ti}r=Q*{;<fy8$Xlm{4wJo>URvKa@4(wK
z<Jo6T-uACu8>YSZVu!@OJOha%NgKab*-G{H9e<o+#CYy$(cilLN0SzQ67F<So4j!A
zKHjNbu3>&lgE+2anKm>!G@O6_xxz+aVL-<+Qz_nMTs6Dz`Waiz<?D)GdkyIRyLp;6
zV848<@VWf!uU+?qu+>W?zgEdwzHm|y2+<OKa5G2DGQ#F;<H37^6|>K_J-Jx6`)-?t
zo15~Ki9TwPR!l7n&0ahC+L;6Q#`QlJ>UGl;h+e$BPh<WR8NT*|2>}ZOCMe|0^;;gc
zI@e5k*|U!o2EGkPlLS{M7}VeKP&sr<g!#?aDy4}p)O!zC=1s9#;kj!~+19A9KWnbP
zo@)AwWvZ9zJGs!B{rAs5FWxlq^wUdOTld82Xa8gGl;UB_`SP(M!S_e<gSuemW&hkO
isvJE2)&G94_JMWFwKw_GzMgpkN)w*0elF{r5}E);92(mI

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/settings/premium/business/business_away@2x.png b/Telegram/Resources/icons/settings/premium/business/business_away@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..be8035562fb145483be761bd94dda19ccac2fb6f
GIT binary patch
literal 1190
zcmV;X1X=ruP)<h;3K|Lk000e1NJLTq001xm001xu0ssI2*kEqZ00001b5ch_0Itp)
z=>Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@
z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NG5J^NqR9Fe^SW75vQ5bd(dFD}v
zJZ}g&3?vju3K<v~7<iQ^GdU(?U}T__GC&MaC`txOL>VZ`!26l!z$10ZJ3{2%&s|y8
zU3;(nGPr}g*TDJLzrO#m|NTGK=Hz59GXiD={<8>JzVp06K|!ghsrZZK@9%$iclYzp
zT3=s3I5_x@W__|TF)>X|O)D!a@9*!w1ncndu)V!KJw08YYQ1Q<GB7ak_Vy-lLxw#)
zJ)Mz}q1TW)-eR#dG&JaPh4rq#zu(i-Q{9Y41oC5Ya*{P*fZpETj*E-aXi*8~=jXSz
zwPnBqsmS&9b$oohk}=gYcX#*M*;%P-!<>_olhDvmRl7n!Pfw3w+akfGr6p%)XOT&z
zK9rW0Ha|a)bf&`3&(AY6GcPYMl;h>)m6@68=H^B@T8OHus?N?%Eyyp7)oR6~a8H?(
zl=Ox8q~H(-Q{7%&UD<55PX*e3v$C={y{P$+kPxY~i;D{)$AMM1wY9ZURZ5($t}dz4
zjg1X-qq@4fii!#<Tv%8rMXGWZ78a-mbplCuad9D1`uh5sot;gz#Ky+n-rfSaxVVV9
z_T%pr9v)s+RwjyHWMrheD+B>WB5%O2uC699Utizd-Ccq#FE1Bq@9yrF)mBhYAQIvE
zm_D*9!0heqC1_;*#KZ)Jq52{sB2aE*v4uT8K2BjQQBzaH8y4-0ii%iCN}~(7y1Ejn
z=H_O~=ZO3JdpafXazzucrKLsW0M9QeDdB8j3%z)i6dD#5hJ=ZZj&5sfW5KwS<mcxT
z7-DvLdC8u#v<`=)H4jA(4-f2%pq}~o_^=nF#>Pf+gz>(*y2^UM(K=q4-EQZE$+oq%
zH8?o<GfzH#;SD^X;IXkW+S!MOhA3Z_(2>+g*z@yqSuG08ot+){N5X@?r+_3!QBhGe
z%<4p6U!ST*7Jv&idJ@r_o0}_2WGb?}ye#X4RhA3p$jFH3NS0q;U%$7vM~*P6;1lW#
ztGKwB@>#**;bC^GiIxsm`g~GC;o@9cTFT*XZf*hs0yH_ng7Wh6I7JHE(a}-a0gfx)
z;NT!9=x{h#LoEGu*I~JoM%S;btfX6I0y7SuWgG@KU~g}4mWi*nl#~=C8Ozj4dw6(2
zo_oIUY|qZl*zPLzLzvVA`t<aK@hKHE#z7?_4UUhGjfsCqq01R)NU|Z^-``J2NHFdS
zkxWcXl+~`6i6ZAR$)IObQ&W0vbNIQrxtyFFgXTFg^fXR8wwR+RV+?A1iAql?=k)Xx
z6^bFv=~Ue+l|33`a&j_N@%Z?N_TACZLAyTc1=m)L+rYp;^6wZV94a4$xw*MX1$7Zh
z4&f6MIfRyqi^tK?(c0RY!RDdMBNJ>!z>I(yfqxu<-?g5BnfE563;+NC07*qoM6N<$
Eg1NaTLjV8(

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/settings/premium/business/business_away@3x.png b/Telegram/Resources/icons/settings/premium/business/business_away@3x.png
new file mode 100644
index 0000000000000000000000000000000000000000..59afa2748fc356339d4b1ae9a59cfd4f33ff00f7
GIT binary patch
literal 1806
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

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/settings/premium/business/business_chatbots.png b/Telegram/Resources/icons/settings/premium/business/business_chatbots.png
new file mode 100644
index 0000000000000000000000000000000000000000..aaa60e6a4eed7197178cb12965176fcb66249618
GIT binary patch
literal 499
zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy
zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlgfEY{P-F~maf
z?Zl0JO#uR~v0E0JO{;6rRTZsQsAs58NP09qC(+x9Q<Qbfgb6b;bhc~_5SD(;%iFmr
zz49b;_?6P_H;!q_mA~6-oPX~^*4{_@(i?KC&1Tn5VN|Hy6}|m-@1hkNT~CXC#<4Y+
z@Hj6~zg0PJdg{#7$gfgC9!sn$`DZUX`E>Ec7q?hCO=S4iFJ(LS{qNz27xK3M{=0A8
z>qCqRtuBWy1gNd_lU(xps}%=R!()r%j{_wbmLx2>n#DSyF7ZVN|MMEV=T}U8L$Cg=
z<4=>_eV5O{XZhu$NsJCsJZuav|4({-@G<9%vuVZY6)HS8mc*N^R9j`9djE}S-{a3e
zbt+v0c@8_=%G*9Ehv5^Op2&}DH!UV!y6`DH<m(m_U&%%_n}_Oam;B%Tb8S!D`f{te
zdA433JSPR{{MqDfI#D9sXr|4a6Q!}QTI0=jG4xD+{&Q~OkNC$Qe=K@7_0M58JH|tk
V1y6Cwzk3V{a8Fl1mvv4FO#sWG!?XYZ

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/settings/premium/business/business_chatbots@2x.png b/Telegram/Resources/icons/settings/premium/business/business_chatbots@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..e48940aaa12414726b4211902254063cc7f6285a
GIT binary patch
literal 928
zcmV;R17G}!P)<h;3K|Lk000e1NJLTq001xm001xu0ssI2*kEqZ00001b5ch_0Itp)
z=>Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@
z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NF3Q0skR9Fe^SFvg$Q54lEaRo_b
zVXPt~tq1`TK}<r*fQ4A3wG&bZisUPlRO#{ocA{9>qzxp6NQwk8U`!y23IU@oVfWzO
zTprIEoj1G7ZZWSI&%5`WbMIWonbBsmmNfxu0@ein?F8(m%WrRQClZO1lasx@J-^=%
zpO=@H$H&K7tyZa2USD5LJ1YylUhl=l#cVeFp|g+z0W@W66M|SQ_V)HBb@F|&q#$S@
zn<$|mNhXu?`J4;m+c`Zw#do){SOC}}$OMcE5`_Xv)cyVa$z(#sI-O2D9@mo}92`77
zJrNTU2=$oe<C~kC?RJ}LG@DJI&$rqO1ki{NA>^z1%Gqo-OYWo5C=du3(SrsusUSBZ
zG0L&q?a$B8WSz-mjLK{_kVyqOOpFWHFvH<6`SyA}rQw4N329-nrpzL}VT5#id`vXl
z@$f4nTq>3B?(U=?Hns>NMI4YJA<QPU{jKBW<t3eEDwQImqoX5=v|26E8e58kKygSY
z0uzy`hqWiPvskRb4Tf;H+a-X!1f(q!Si&Y?F6@6fkdTiW4V|B#<2Hi@Ks>%~hQlF6
zuCK3ga|UiS8tlbeq(GoJOW0)DUv$vG<MEh=3_$66y-tzc-CY($qtRS0x3#s!5ddrv
zM2drukQOEsT=r+OHXuD6iNOs;A`xwkeiRa^p=Ezjt?`wKX$c=59!in5TN4u5bNLK-
zLhtYI!C+7p=<h*>L}@}!eK(Da$K$!Vxw*Q!y1l*q`1sIZe1+U@H=e2KblT-|eZ>rY
zsx<^l6i|A480tbGsa->Y@NYq@p>>nhn9yJ_AQgjkCTe-MWgzM`8p`MMco)V3Aga@;
zeC>-hB)se|5j3{n7K;-P4-ewa$s8`1%WNq5a~weX)Th%alR}{o{tEKeG`PRNXRYYZ
zK|=uVop_#`8;Phw2Y=+OY-eX@qOWOOC=?70U^NM^QT#{DbSs~qpT%O);c%?3Whje>
z5kDGDG}Q0+tJSLX(ru`)MY1MfO~9IfHG%(m0zU!X#bt_=W%$ql0000<MNUMnLSTYL
C&x-;8

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/settings/premium/business/business_chatbots@3x.png b/Telegram/Resources/icons/settings/premium/business/business_chatbots@3x.png
new file mode 100644
index 0000000000000000000000000000000000000000..2db7e80ea5827de72e34dbb9cbb80e22dc7caf94
GIT binary patch
literal 1405
zcmZuxdo&Yz9RCf=Jj+{UB8G^wk#mVDv#srpv6+x!Ix&}yJn~qLrg8N^QbdK7xE^`l
zJVV2sggma?5S@%nZlf0CO6$%)_nvdlJ>T>Be!j2s{p0)np6^vJ@-a2#J<0$8H6j5^
zl`&x(Fgcl?x?#FYMvz$QF$|~~GWZ~KupxfLP!A7aDYIb!lKvA=*e;P_j|>3Fb0I)p
z#*pn8SMHxIfh+&7y<M1%!mj|JAS7a4X<<;&36e$JDSc_O>AaM(MYx%<Xm#5Q^P`zr
zMn2Nc4u!HqqL4@<EpH+|X78eBj1@)I{k>R7#>9O1S~OJI+}ybC&k+6kda|=?iZgbR
z`Bs?p`sw=AMN#AMjeU|j_<tp8g?D5*RO^q`)m4c^($v%>5C|^0Df)fi2{$)4ud1pd
zkw~7No@P`ie{_1fy`e!Yl}ZDc0O<2v$KRWHhJ?HqiyLAbY{;obBgdxn)74c~S;@(5
z+BLx#qi!|bu+S`onHiZ(maMHgT0MRCEGQ`Gh?CRPmKJt1m&+|LFR!mhIXS&iDk>^k
zURimLSUR7?LD<+t1_yUBkB2fC0RaJ8nwt0RyI^`?$igdSWVcu?Z*-JW(fRG$x6RE9
zg;XH#r>yvZeD*AAW@d)!9~&D>Boa6MA^0m;xf4k{z7U)%A|oRcJ-ofWDJKaqS0f?B
zy6>g$;};ILw%t{e)6?e*DTmL(unr9QRx+3A9vJv`U_d2T{OJ?hsi^o6Oi@YWvcZ9e
zW*JiH*!XyEP7dbk^WNUy)Cvy<LQ^@Z507SPKiXmbxVoB>@VU;`7I9S0@*(cpyIvF9
zkdTlh7Rw8oc$lO!KR<uS-oCW7lo(H)l>^8I7=(tsu1zq7oUUqm^2F-`3=W4o2f4Y)
z=ZWfyi;M5%=0@Mi4o+18Jg@+~q8GS#`p{_fZNq`~SFdP+)3dY2OrcQNO6o&u0=hcT
z35wdUvA#4wdF%Y?0?x6fwpP}J$me@gD6W=yX{NV$nc&NxYBVELQ%@`&-W6i}yr=G9
zd3AMK2!kOMi84=a;aN;3F&^fiqwLf2Dqptytu0o{{8hS=@6KV1Gvm9htgZQR;-Ez~
z$QAs>*^o%`KJGHyw{NGqdf(-+9O64XTLWVQ^NY{!*E+Cd+@8nwO}MMHi%Xr@*l=iV
zIR&C}_$9gl0D(Yg<K!};QXt4^T(*kb@OA9GAilz|J4}fc4B20<U1q3HFDNL8i;LS*
zBG+TsvJsd%(OiHF5{D*hjvL(S&!Gtf-o+U$ZL)(U#3|R-*3N>O6)rtnIh%GMEj)`j
z5@p$BC77G*VRlTp^3PQU_9&gB(-#ezCa;YC0xZ`mM&^G&BG25-&+qH&8*#5-rzp^r
z*_{J}JY^M?goK2M@bFbFr<=0fm*20kI9Pcv-MmdK7K=opl9G~+4)li?x;GU`vr9|i
z?$(^x@d=(6x_?vW@ct}Qz%C%6*qDy8I{7bC^((z^>?AV`UPSWv?w;T8jS(#qpe7_J
zp_0Z4r*F9MMsxa4b@fppIZF?AJZ{j_)8k*%Q|MgGi+=<)(|xwph@;#{r)aD)7L9yh
zEN_(VoH1F5Z;n7ynO-Kf&dbZokC3bdm5&3KoqW<{;Ti!vs9ea802v(}9YdKzQS@R-
zW~N%PYTN8YnhP3TN<x%%fBsw_=`W4ma5ot1iV_bEk-tMDP=v3;?XWrV;9$ma52<Tw
zYwJY+As66_*{Vl4I!<1Cc|w)L;nd*r?N6_VCniq3+sl}*_wn(WUD*?wZamT1*;(hA
t<=N=w=H_zbI^OVS+=Tl7$rc>GA%CT8aP-<4zm4tJN5qk_H5mHkzX7{|bCdu8

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/settings/premium/business/business_hours.png b/Telegram/Resources/icons/settings/premium/business/business_hours.png
new file mode 100644
index 0000000000000000000000000000000000000000..c6f0d03e3f6ad18cfe6f5563bcc97722fa6c70c9
GIT binary patch
literal 426
zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy
zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlgfOvKa0F~maf
zZHOVCi-U+L>)g1-6Eg}pdMEE_;NHR1x_d>5RiIUa-@@Aqgnn<DbaMX8)J@V$<M&qn
z|7-s2y!@<hNAAmhsI@=;dEfi^`3whyp76J|B~D4*nR{E0)nkc5z-`Bn>sy~35ENLk
zcY|X1miM{0%VO0hOo*)&iBgz<;`1H@9#-WOEp;!iSa=G>K9@+Do;vgS%u@}D%`+y&
zM9LgDDeq-9On+i=uGH`>SJJtTR}$NFvL)I~d=343LKuAJUI@{dy{s@+eCoE`@X(_y
z0`qT)x*jYMS@U{U>deepfg*--&2?EKER8jP*A&Sd&z#k_wC3-kl{OR3bTA!tIP`U!
vdQnB<;q~b(ol|}-yjXnigY?2OemC}eyzi&<1@t6!fx^kt)z4*}Q$iB}wRxH$

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/settings/premium/business/business_hours@2x.png b/Telegram/Resources/icons/settings/premium/business/business_hours@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..ce098492028837a3ab6cd4d9b7fb8cd214094184
GIT binary patch
literal 765
zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1SD@H<Xr$#jKx9jP7LeL$-D$|Tv8)E(|mmy
zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;Zuz(rC1}VI4p2Neyz?AOk;usRa
z`8HyA*C7WH->?9e4I3x2);ol35SHF3!0l-7uwm0zaUBtzw^uINZ}`Bjvvp&Lz!A5I
zkQ^3i)|<8#N`{|(9&L*BkV=0yr~2JoW8<B|re~{dSV8b#7~|cziv9QJpMQS(X_1ZG
z|2_5(e(tgdv;Ea3zy6xle`i*>iPYQgzn5Qr8U2HSLE^x*8@dab8y!|(wVHqakWU;N
zV;i@OE^81|^190}zo^bCV(-hJrZv?ocYE|x772$P>XSW}uJm@CG3RiON0UNjvtLBr
zZEaU>R;}f~Iin{%GmRAMb~T%Q_G!`UueVHG${jUgh4mP?R?k`KwbV&t)m5#zjVGC$
zJ|3zqxO{O-+Ga_vRu%@ME%)DhJ}$8EIH<s~R^%ANgIJx~-xF`98%aKj*AVfkGT6O<
z)l&J%-6NqKQx#?SKHr&h!;!m4j@exCisroj;@u5%W(%xZ_W6Jf8>6MU;*<ipxj?Z$
z|5cXgHJ^X3{NQHJwbx(sw$HvIzTCgdkU4C%=;sTUj=3#PEb&`@`BD~h(c<nP>2NzM
zv&+B`6Ygx87;^n<)m}bpVSeQky%kH%jy%73@!RRAm-4nZA6zieN~rV9^I|C;?i0Np
zOUpJ!q!e-X%s#s;L`(8!_Esrj{vu_cm22ayd=%Xm2kM9^8(Pin%M^(%d?}#X9&P2j
zJju~ho`>!5Ljm=^W6fbAp$5w;{w{Fl(&<|#zx(dG+i$sywi&WGUAR?Hvt!3QuI@b>
z3iNh85ng!btE5X&li#JL1Gi5Xh}>T+|NHPO{%@1J?Uv5JnY?cQuk^$J|64!PoO=C(
e2nbg6GudBYR_!`I^?L&->3O>PxvX<aXaWFn#Wscj

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/settings/premium/business/business_hours@3x.png b/Telegram/Resources/icons/settings/premium/business/business_hours@3x.png
new file mode 100644
index 0000000000000000000000000000000000000000..0aee12c236cf0f7d0e75b1f8c227a1b31a96be6c
GIT binary patch
literal 1130
zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1SD_us|Wxo#^NA%Cx&(BWL^R}E~ycoX}-P;
zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1|Sip>6gA`6MbbAj}e#O(pF(iZa
zZIofQlB3AExe6+ZtxG!=BqZifG*griP+?=aykvtweM4*SwxnGmV*j`{b}X3T(AD6o
zqbktRD^@1d(Q(A(o!tke$Gfa6H}BqDUNKp1bN;=W_rLGGJ9p+>ak~58l{_oZ;EVJJ
z?fY(SDlIQJm+F<1mv2A(FhJwU#|l0^z6PtAK5_B!zyJRG{Q2|WzklDpeVb#pS;p6n
zfn~|%msNZ1=K9qO{(Hcd&U4V==Nli_8@F$3i*@Jc=d)N^=}B}vWH1zsIj1oF^whIy
z?u#e##XAZ+Ft9pU`nU$v+Qo0bE&AktuQdas_Mt<0_YWU-j?TNXEZ_jsoxFV}drqJB
z_Q{w%=glSt8O4m4H=mt6cC&mal)2j0?(%AH(o&@f&gWI)vOV96u5Qm&p7UiVi}1#s
zmLDEJe*Ey^!pkpp#JH!KZ0uc7be%!R$VF?7wAg(Ql}$AV`}_Lt-M@b`E=hXf+u2Ij
zCT`F*_%1NbOI2)T)LNmV8}c}Gx9I;o+4f|6@y<EdUqAi*;@h_>+iUA~rtG%JZDC{V
zYhKo^#WVX#joo@lpFKyak5*;r-PEg-W_eMax@v=q(#AjU9vq)`i<@bIBafm&Plv|U
zvuTMxvTST^r=FU@!6&8QT&Q-}u!fuatjClC&uSc)Q#6u24}EC$KUj3E-J?x~=egLq
z3Cmm*Cn<6s&g4;DVr=JPq>}jM>S4xQ5Tm}9HTkovinGwMM^X9av(JXDPL*x%cMxg|
z_Os*KfBpUU&3uO+8i;kPPWCj3u-=$E@l4c|>C?r_7VF;1Gm*+Jh!>BTa_Z@)8ar_&
zmYMntr~iNaz;X0m$+;9GHywl8`*-i=@)>Sw=n<T<;qR|sw={Sz{`>i}vb1#T{{8tP
zzaKqn`mvGS$W7tUx6PZ47cuO+onT;LZ*R^e&%tu+v7tQUUWMcqg`DI2CP$?Fm?kEZ
zmzURcaDiT@*V04o47}Fn2Q8Shx8L3s;v@0+<B!XigBJ!A?7lne;e)jsGxQvpQ%u~v
zr?2?(p*VAqxay|KlP8Pn?rB^x`(vR)^3!~OW@*XGs!Dd(qt>>aKOUm>^~aAB-&q(R
z)p_|CzY70zU{kfDLyzG_>DQWtZqpV9eEHF<@v49JPUhg~5-%HrR*TE-cf_`O+COUB
z_EyJBXa0$|I?B3RbJHK@FZIp($^W2ln(_2iVds}DVP)9BCu$UFn<HJ^xid&1eICn#
zFG6ao53cAnW$5nF-Oi`bFe!SE$)v=T<NH!>*A*S{I;tir{h|J$d(x})+Bda<xl<WY
eVB>!!=KYMDRNrbZ(s;-U%IBW0elF{r5}E+<kmp$d

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/settings/premium/business/business_location.png b/Telegram/Resources/icons/settings/premium/business/business_location.png
new file mode 100644
index 0000000000000000000000000000000000000000..ae7020aad4fbc6b1b8c5f2e2952f4d8453132fff
GIT binary patch
literal 454
zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy
zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlgf%)rydF~maf
z?i9to76$=W_J~c5*$N!j9UdE0=&+p?JfO(InXY)SkoAZci<o2B#^28QT6bM9zg~Xj
z|Neig4Sdq#)^C?R{o!QI{r<-mJkBN4QYDXnwBU1&-|uy~<n}iC%ml$#xBWA3Z@a|m
zn)KD>qm8E!>t0C_i{kI=UaxztwK@L!2~MsRcOz8~-kv&h)v6;>PUixHH|*BzYHFyx
zpL<(OmLno+;jtV0<(AHSKJniUw}?pPiP?(|@IP$5pt$9uY;7E~;^Cs+UtJtc4fEnV
z-e*U2%xcO!YvRk3EXJyP;;_zXiIX~9b1qgLFML*+W)Qh1;6l?ZgD#~#l1-ipN4%IN
z7H!h$RByb{V6*Pku4TdkzL#yn{>PTYs^@*J&DWd#?}o#p=@XytOpQEjFz0#ZzOU<U
Z$9v80Y<+%9XDTQRJzf1=);T3K0RRcws$>8F

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/settings/premium/business/business_location@2x.png b/Telegram/Resources/icons/settings/premium/business/business_location@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..49b7ee3c819868aa70dcb36b4827bb6650a46f42
GIT binary patch
literal 845
zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1SD@H<Xr$#jKx9jP7LeL$-D$|Tv8)E(|mmy
zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;Zuz(rC1}VI4p2Neyz_i`d#W5s;
z^KJOvEG<WoZ_7JA>i2dnN)q+R+HK=tb&);b124;b$15gb`x+D!Z%O7}IrM-(tf_aw
zU0+p>sF)T9miK=zC`cclX=}XXbe?JPx$ni#)>N07&g?n;qTym={58hSpDN$KdzZBF
z#^cAyDngtrOcx^croVsxey-p0n>pv67pJCXCYea(ZomD%MVz6*)BCj0ny~DZA-CRt
zKa{j@QUfDT*BWlo_eDE*-PKzvqRS>Cuu`sM&e5dItx-|xzoc9)yo^&xSs1V&{D9Ku
zpEf7kICkybd-rYG8eUr_E0>P4UmK#<x_y79sJQ5TiqXsSiu2F6Gb;ZOOnB1cro8#)
zpAR1z9v&$^>fBzqHEL}ILzc~4zt4QV(i2an1e^-w=FVX?7Y_;vXY?!BU$j%k+hsyI
z!=u<_)BUb$lTW%fpFe;8_3PL97Y?&IF3}58IQr<k(yzw{-+!N9*mwJ8&bN;r4=xjM
z>FQNJH2G4~SME<$d!r6z>dx4|XUg%%mKU#z&p!Js>Ho(oEX|H#;s?7cSQL*wTB_WA
zv?={UhDn4aOJ#+}#)_97d*hx<D*XHPONy_(e^Eq&HiNU*t$C^`n<E>K8-9IW@bZwt
z(O2H?@7}*Jo!i0S;dwh=#b<f&qNNOM6T2U!JN`W`*W$Ib$SwHBzm5~r*I$15#P(0q
zw2a^d&MieeLG9aa=kAPIcH*M}kUu@NbNAi6nwAWH-}q*ZU0thZO!iTG>6$TdWr)|B
zHS^wnt%_1VB_&d7ADgBj61D#N6stao2LIxi@EAdX{^KuMRfRZRr8T2AMl8AV<;xcp
z)zy5*wtqf-x_gJ(9(Vuyw_f>`JiB@|bpQS5S50L2a?PY;-=<HH(EaiK@HG>;{`|bW
zx?|2^tohB`x0~nsHXm&G{#Q$TUB~v@Z?9&3egCkkuCB~Nrpt-#EI3u&Zv5}P>DU3z
SuX4{oN!ioY&t;ucLK6V5zj>+v

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/settings/premium/business/business_location@3x.png b/Telegram/Resources/icons/settings/premium/business/business_location@3x.png
new file mode 100644
index 0000000000000000000000000000000000000000..a442c069d4bf06da0b2e4019908ac8c19309962f
GIT binary patch
literal 1226
zcmV;*1U37KP)<h;3K|Lk000e1NJLTq002k;002k`0ssI2+K(g<00001b5ch_0Itp)
z=>Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@
z1ONa40RR91NT34%1ONa40RR91NB{r;0FW_8?*IS<G)Y83RA>e5TFWbKQ5bi)%4?z&
z*CVd-8X%^|%4=XMCWbOK6aES%k32f^2gp@sG7yR6Ro+T2MP^9i{yO`1TCpGB_pP<h
zack|-I&1x2>)VgB_u4xp#cs9&wgR>SwgR>SwgR>SiK;*(RHH~FQdwD9SXlTow!!O9
zY`eL+IX*uA`1lCb-Mn~KR#s<c=hW2H?d>hnyKWc)05E{fCory<k&!VlFmQi=?>ZUR
zPJlrH(RdhRg1x=H*Voss=>t1K1l?E^Q$ncs+}vDX`al3gzR|8LDk|FC+zbp&1V9Rc
zrYbJi$;->TxVRvOCc+>HS$QUXW_o)1>gp;nE(-=(i0da5DojjFu;Q}75EsVMq_wrR
zv4RS~u+R+2rl+Q+?(Xgiva%?!fDt>K4xy)~hZRu_?(Xi^$;sxPo}P-cvWPHZhl>#;
zH^B`J4cpt>bicj5y@iE^qoX5GYHDh_y1Hs>Yw7gH#>SnU9Xei)92y!TR{iz$b#QPn
zEiFwo!%uW#h$sR(l^$YyV`GDuskOD$D+h*%BCzukG1;G+oBRI$PRTSmIq7c<07VCO
zF!h%*FpATIk_JawU>X332PEs9bSBCEmnbMGpp<8Qef{zA(H{u_VE!0qlxV6-g*CCO
zt1H#Ut4)M`qlsl^W_nFV?I*$#F)^HAYBH~OU0t2mklJ5gU$6f3Y7<RWI{Q$>5{1h5
zYGZM#sj10p)aeJHqN2hd<BSqbRVntw9v&W4zE2xhbD!sY3<drl#Kq~RaSFSEdm_r^
z-oeby&bppO=)@3Rr@FeD9h6JJ8YnK)^h~%#{B%s>Cpzg0u!AW#rv$&UvO<sLAkWXw
zf5$d7xLnu?QvMKd*4EaVo10n0U$KoviGyJmZ-?8cs;c5vT?Y@laOb0{;{i!0E1fHT
z5@TQPo$2AhQJrUa?SvWqn?inm{`2!Qz3?(5EMO$7nsATZ-Q7)3Ps=RnteKe^7}4=&
zkhsphy}ikH1%(Ci^73*sS-C3FXp|CBhMAwA=jJhnFDom1dU}%O_GjVj!VzICs6c3V
zc-TLpY;<H~L=eU#1vlwuXJ@kXUMvXuJQkQt@2}O~-tHAq*5A?5;qREZ(ed$d*&M#I
zlJ@q<wOV+-IygA^nv&adczF2jzK+1spXR;D%gYOKrNp(kA$Iol^>KHRitq354>6pk
z^yuiQnw8!j8ynNK4wsHMDT|AX^!Yf*rKP1$Z)CzvDO1nR&fePEqR$Pl+H!JoWL}A8
zVd0ke@duW;@oik9^Xd5EHZpMC=JN6~Y4`6sb1Nw+IXOA;Iwo=7R9af9qn}JJKEHHb
zLc~{?#l^+ROs4ORXZufIgzWF{W5W8%{tAg$Va27qrKJU{Xjst2zFJ;h#?!HXEIVo|
oU@KrNU@KrNU@KrN@IO`HFH9dX^3*z>CIA2c07*qoM6N<$f?j<<wEzGB

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/settings/premium/business/business_quick.png b/Telegram/Resources/icons/settings/premium/business/business_quick.png
new file mode 100644
index 0000000000000000000000000000000000000000..a9e7e1d1f63ab711a6a5f4588aab8c8761605271
GIT binary patch
literal 615
zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy
zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlgf?4qZOV~B;|
z+bP!lj)4NlzZY@0xHNJ&DRnq456Ji-pfz{>f}oBS0#jY|g*x4p1p_rywY1i=bb4e2
zREf&%p1jwn@8PYr=GCkI|4Dyl{(N3>k-go&H;?Ys&pep0AwtJ%Y0>7JJ;xtE{`h0x
zyfjfRR|^?AzV`WNn7sDJeg9drB}(U8h0W)ma})z2HAE8nlir%&fBjXCkNws!k?m2s
zGkw%f1+BjN`s=KcQY+cXCqs0^I_IBFb9G!9(iP&oBSNRdYHo;@>YC3#bGF}(-(b-f
ztvCI=LIGQI;{mOyUOA3BV$K5nUhA(*_qr*zWcIoxZ@-;rCc>pU`DB8D#zYUM#)b#G
z@8;?3;GTZE_42H<&ul-g$T5?iw)J+d&%JV%<2~!{zh8ef>-S%|xpC{Kd#F^*Yp-3n
zGiq&seaFd^rppl*-+$j4r5m6%mFxB4#kDVvD+qONC~azsX6CvRwKFDd^M?x`eI^zN
z7wxREn!D-hg57u5-ON!F`LOt+#@qDd<(DgW#&Ep0WYLR}zwNR0@WYC{<hhq$iu50^
zU%+5G*U$O(*Q&G6KVQuf)#KCLd{bxg$%wUKR)4IQ{a+lT$G)6fzrsYyk<q5S*KKjo
m$_MpZ?&ifS{X2Vq-U0Cy=?r^UuU0V!MY5->pUXO@geCyA@A|<2

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/settings/premium/business/business_quick@2x.png b/Telegram/Resources/icons/settings/premium/business/business_quick@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..3f4c1852e08c328ed31c591c3e5e0e49d2d886d0
GIT binary patch
literal 1083
zcmV-B1jPG^P)<h;3K|Lk000e1NJLTq001xm001xu0ssI2*kEqZ00001b5ch_0Itp)
z=>Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@
z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NFrAb6VR9Fe^SW759Q5<%;ByYv_
z2n+Jwpj&KgT-{QV^3FzBTHY*#4P_$>3n?pEcx-Gf=qknaDEAf$kvu{qkCM0lS5v3c
zoHOUl%>8fv{byFc`Of$Ie&3v#^O}$l>#};l>Vf~P2W-JSZ(3Sfd3kwiYHCVKN>WnN
z)6>(<&CTuYZ6FXhIywqQ*;sBtLBYt#h~Mx3)xJ(oPbVfODl03ENgHJs6&20S&VGM?
zYdcZ5tgfzBRaF@+B<F{Pg$)f28L~oKo1dSLi;I(&(aFHK;`Msz0w&_)<KvQ&5}g%A
zwB+Pue4!>Buqn^a&!wfMqGHmvn3$OD?QK@tG=gE6lanK@t4Wxfo6}Q!YilbzJ3Bu=
ze`8}q%HQAL4-XI5EGcMkxm;3ph0xK_q3FcM#;&igOS5`=dj)m*O?JEe;NU<itq{h?
z$B7C?leC{+US87E(@Bc1pr)oqryJ0|zP{Sp+K4VZ`FuWcdVhaE;p+;fr>Dh=s#bVv
zZEYpeQcrt(dxR_x@X<X!KB`JeZ&jY+<Ks6sHw9URg@tk}3IiikAS7slr<Rr$QU;zd
z0JZqd&CP^K0y~N5ATu+Qz9sS9-QD2e;Q9GELD&GEY&IKApb6{0+wERiSt%<kW0@Hl
z87#<0+S}W?kJa7X%})@baKlv_8yjQuLRkpN$Hxa3$27ym#b9VCzsmdjJDbRVWmi{M
zEEUMWzyQ2nTwHKl74Y-(b7^Twd1XKn6BDPVrnvme%S$fK0kg8QxScEo;ijRXfhfYw
z&d!cjR$W~kVafw+`Lu+zaJXq~Y$VbN2?>IQ8Q;Fc;UHvrFgiM_RhWyzO;b}7(G|Oa
z!t(MmA?pg9PA9i87sTVzlZtryQ54J!*u}*~Zh01@7gs%QPEJlDBO`Tn3{A?-&Bd5v
z%d<#lXQz@VT`rW|+S*#Wkb&6a@v!AtWOsKrFE0;g62U@2vKAH=3^-t^m_>+I@WI;J
znwghH+lq*Yczt~h-U<|Ow#4Y8#m&UU#l?Tc&CJY9bab>C2dbQ&o}M5LJv=;gb#<v^
z&EMiItTOcW_NGF#Ztw5!`}+FKEy;Yx)z#H>F-*t9!^8M0D=I4RzX7L!pPwI^pu}fq
zXOokY_4V~8-jV2cOjJ}9W-$D1f!XWpYwWTy;;_ci-m%#Q0Y?ZdOxTX$Ukr|d2xyL>
zSY!zM7%WdX{~jJ5s(cu#VUevKuzJAifq$w8{s03|0&}BmY7GDY002ovPDHLkV1mSA
B?neLs

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/settings/premium/business/business_quick@3x.png b/Telegram/Resources/icons/settings/premium/business/business_quick@3x.png
new file mode 100644
index 0000000000000000000000000000000000000000..d927c704fc527ccf91ba70feaa026bb247ed6bac
GIT binary patch
literal 1520
zcmV<M1rPd(P)<h;3K|Lk000e1NJLTq002k;002k`0ssI2+K(g<00001b5ch_0Itp)
z=>Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@
z1ONa40RR91NT34%1ONa40RR91NB{r;0FW_8?*IS=T1iAfRA>e5T3IMIZ5aOWTl;?$
zMfP3xED5EQP$Fdsr5reRfCHq&ffFeX<U~##kYr!8l#(@(CHuY|*k$+sSKrh$&Ajh3
zGtUgZ`ezQ_p5<Py>wagR=YH;)U%#|Vi+~mZEdp8uv<PSs&?2BkKw$(76zUVyZEbB6
z5)y)ggT1`GTwGlK{(XIZem*=r+}_^q@9%GKZ_m%qkB^T(K0Xrc{1H=zhKAA6(bd(}
zv$L}wA0PipxSpS%M@B|UN=p3w{r?CKJP@Fio1443x+;N-UXih}vADQ6p5-6w8W|bo
z<>hT`Z0N-+b7FFGGCn^3$M`UgJv}`a78Yb8mn7cN(P3p}^)vJ`Gc&OaNP<Zvw7a_-
z5fSmN+)PYN8XFs_(8^PKd3h-;Ec_;JSSQ-s+vNdNCS6fc@g3Znnwpg1q|q)YDEL}#
zm6eq=xD@NWzrUxYrF|8*tgI|*gj-u%NJMOGY*JHG4-XG1>^wa^eaR+{l<4H-gaQzw
z5)u-^#@p4^b#HHv!W4FJ)`)72+S*zQD2&SW^)(L%xFKDnFqoa4%`>H_>*we9`ua+N
zgH=Hu%tPSj=0?Ho`1lz24$l;Qx3ja8g-F*x7H?)|#zO#Z2L}go4XU>KJ32aIQIhLk
zC_gkbB;@Aq?k<-b<gu6XVnov|FE1COq#GF=95gjG<$<8!h6R>qgr+++HAP25r`Ev0
zfRLMqhli{^prN6Gh6z{4($Z3u`;berzrSB0x3#r3E=ZboOiT<HlUi+^+;9jwIy#c#
z0>^W6b8`ZpzX)^8R6sz0s0<^3(#6}`n?zw$5L#MV^7HcnkyY8**}(~VVq(I{$%!4*
z*}&p~9cXcJQRl3r!K$h%(GV^zEum;5$(#fs6n`!+FGZcEq@)mxi7%RX#t8b%R8CIL
z^768%m_z|ouTa|&6(*mr#fb@@a-PeZn;R57c!LVNb8~Z|7RbQFiJ=Tg_F-*pjoY0p
zBX>3`DoRx4{{EgIBTfu|2tl8jYHMo?3kx$aP^bLr>+4%rS10OJ3NVrpRBS|_V@3e=
z2`r4#uh!O9l<s)GeSCaQPfw+IM4rjhmDk1DPKq13GF@F=#>U1xC}1V=$cG0+g&Ra}
zk}68FySp28E*>jyZ*PgLU>{X+o;pnziJ2%?=>k1HJqlUDS8;JM4HKSDY;3G_++@k#
z-d-Uq?8rDik`=kLcx8j6nFo@li~Qy6?2K!guHDzyr<@gB4-XI1(9o%aRDg~Qt2H`0
zD%?_~XT7W)G?J2%bb_QAM5K}FaIMR2J3Bk`9MiGFa>}!#w40HUA(sp*FWgM!o-7$t
zC@tc+t(1vS9|ai}tK3F-csOg2jukxOsKFXhvtegvN4=-voltUevc0{1dV0F_{fZmD
znVA`3El4$bK|w)xcXwQW-_ync0_AblXy`PAz^h&bAx8(h94f~;!KgWiZSCsniVQ`?
zS)8T=0|V70pwCulXsC*~$)@n4-NC^@9}<<vi;9ZKXjGX+-c(alBRtorOonR?xw$IP
z2}ah}*Ao*HxlX^ljS>pZ<OEC#r|=03N<-?D=4Az=tgK8QbW~ta<zHD@(I-F{M?n&g
z6W*AraD0@-hY`k_t8+a>ri1UN*b^~cUY3@Y;@yhqe~Ws3b8|DcY;rA)0;-Ji3vQ3;
z>FLPGNUj@f)+p%K*Vm&o%^hdj2tQsyp*Vxze|}%Quiq{%E`(aDjL0B{YzPUyg@wiW
z`T6GNCcenS&4Z&6YB;Ju`AH*4)xI!W`$=Y+Z!H2^1hfcf5zr!_ML>&y7J>g!1pWib
W|Bg(kR?{;80000<MNUMnLSTZm!>Ky}

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/settings/premium/status.png b/Telegram/Resources/icons/settings/premium/status.png
index fe154114576f3216e631ab8122122ec0f0bcce3f..712c91ba07f48f5492bc55bf502687f4cb8ae3d6 100644
GIT binary patch
delta 449
zcmV;y0Y3im1Gof`fq$Y&L_t(I5$%&N%fe6;#udhtfiN6m8Y~Dlqt)Sp#VCRXtwW2&
zZ(y{F#bOjh#{2@tB{5hAT0~(eDozwMI3M1<8#?CS`o1^Zd(OS*`R&|u&pe*LIw*<?
z1OngRJ|2$~1Oa`&-=9n-BuRe5Vi?9SjApY5F=p81a`_t&G=IHb5Bh928;wRUm&;<Y
z@caFrv1pp+I4&Fxuh;9{ZWoC}WLXBZQmNP=Q537ys_nWPtyb%JJSLOLd_E8Ta5zk*
zQZO`4L*bz@-PvsBuD5puK{%h!hr=O}NJx?dM5ECF4u+Y9X}8;LTk_Dz<#MRYe!tIT
zGMmi?2yij4R)4EPp#TG;SS&u&<DgcnO{Y`bt5hl>TG#c_X!Ij=PN?HEP&%DHolZ!n
zUate{R9pUXxqL=3&yWrt3lj99C3LQ;s=fvxA{L9y=X0m}#(x(C7R$2Z@%To2XDW*F
zRz_wJI&&}>+yN{H75o5%P@m7&>2xgl?}~<a@no(a@j80FXoc-|Yhuv!=veni*ImbE
rC=}v(J{SykyIuT)j?;hl-3nX*5h!a5mub8}00000NkvXXu0mjf#j?^M

delta 377
zcmV-<0fzp#1n~oqfqz>`L_t(I5o2H&1w$_c7>RVUxVX5Fj}J35GerG^2M>1b+C`*Z
z6lHyVeg9E`v9U3-3Un9Q+S($k`0?Y%h7B8Dym$e3QE+guo15E{Cr`AswLg9Oglraq
z&BVm?`SWK~i~nD{b`7q=#>VF7&!1nud@(REfD1wxEXe$S|9}3arKOphn?nTS;^G7a
z1%akRxJpV&>({SmVPOdk4ZU#T0<uPY?23vCpwqs6`(|Nb0aVA&&kuAOP^6@!1fMoE
zxrm4epzHqr{Tmb%1d*CLbt=%5-@kt!27`6$*75Q2q3J^B0>c{^#z1Wa1qE=qs;VlW
zxj;b(`0(L_f^vcaTowyMU0wa{+qXc~6DLkYlV)RM<Aectf`Y3-kN!i44gp;RjJpXF
zCIEd01VCS0xpD=rfmp`Hix*+ehtk`&Z6iiAx{DMQ6~n^9fIfg|Cf`NGSU##|phExv
Xyp6?Miua~900000NkvXXu0mjfbTP0F

diff --git a/Telegram/Resources/icons/settings/premium/status@2x.png b/Telegram/Resources/icons/settings/premium/status@2x.png
index d05da77b42d4912a4511193c40126b639acb3db2..f0e395b4ea57c6490940d6a765533e2853447305 100644
GIT binary patch
delta 979
zcmV;^11$WL2E_=FfPVw8Nkl<ZSPAV{ODMEq6y`_7!i;F#ZwtgwSjcLG6on0v8pM>c
zku+sD8!0j?vQcP?g$0o`laz%RrQA!C5VDZF1(VzV{N6f!nS7Ubn%Vr{ET;24=klEI
zyytz-`vnB3KeYmC1=I>Csz703p}}BK<l;l)va+(bx3{mauYca&ULVr@7P_IKp|7tm
zDk_Q$Y&ILR!SD6;H6bB^aDNDynVBCSAK-R6ogpD1jIyJn<2#>!f1{(L{>XzkGBPrV
z+iJBkJUl%7=H`asZnrxnC57z$AuKE`P);>9HH;FANZ{Dmm_PC$j<~qEySqC^4<R`@
z8LHUWSk#7rXn*kZ^mMZKhtS;IO!e5<*kH{xK0Z$1<mBXUc_bwzWo2dkrd5EKl$3OJ
zb!oL)=CioCNc2NPLjd)9Jt6P!@8~wq=mP@-pP!#FTV7uF)G9!Vii#MS%jJrXkB2Az
z@9*y!2#)INYJd)hgL0ahn)*S2h9m9O)zuHJvPYxQNPn@8j*fIX9sFBcTM4|qy+t!s
zR8$ax(;+)Mn`lBqLr+dlNIN|}O)4HCGBWb)?2N)49v;TT#30bp(h`B|>+5Kyy}dm`
zW@cuHth2L|a0n)oiBy~ddgA)}nu3BjIyyQrG4bKyfxyAR!S?ocdc;!Z<>i4dC@4ty
zJ}^j2OMm0cL+DmeQ1JNpNPOGd+ZZ5iZEb`ggWcU-dW0}HH-`wdwY5U<^z>91mPZZ_
z4rXo`=`SxY6n$%JOQX@)?RG*ug~rB4tfmm3n3&*6g84*5M69f=;LyX~Mq>iRW?{3P
zacpjG78e%_`6T6*mKOemDl03A0DY62n~PY8$bXI#QYjKrQ&ag9!ivzK04pvSJv}{v
zfq~uK-HMVR<BN+6{%n|MettgAF^UKQi-`Rt9H>IAxQtOFC!s$gM%Yd05}b!*Q(9WO
zzrT-aftx^?0*+9bmJ(*z;cIJa;%_fUJ3l`!!St;bs}S=?CzY3%&(6-u2`6HMLsCRb
zDu3yOV2U-Wudg2-9ws*q0W*%nOTxu$Hgj)#U)?90nwsc}D5rJE276QDb-C5pYr@G&
zl;T2NT^%>0v?C7g%gamg420U}=V#nxq~m&HfjjcS!GVxfMDFkJ_l8TZFUHI2>Z(W{
zOohC|!ouVR_0kR(fsv6Bk(}@)nUj;_B}|KA_o}L@<Ktt`w9e1Z+0{{TMDHDNmBSYS
z<^%4#Pft%fJ3AJO1(z!CBdZbA3aAxOEAXFJ;4hVi`X<?;k1qfK002ovPDHLkV1lZv
B<n;gm

delta 671
zcmV;Q0$}~c2$KeofPVsgNkl<ZSPAV`P0MLf6n?qBu0-!;AX4`Sn2|CtGBYz^qzvQ-
zFfbF!fHGqs`3H(fm>5b#3{1%2PNY!2@>Sxl`<~jJ&f)ktUW40SqqWyw>sf0*d#`oQ
z#|IY70A>I)fEmCHU<O{w0OzG>|G+#RkIiNi2n2NG{eE987JsAB==pr6?|zF#r_(_)
zW^uV(47s@sd*8IB*XtROe=zJlu{9o#SF4p&DxFLwpcC@*OAuNp6dDW$X!)38u~=HI
z)}QOr?RLZAut+4jh6|Ut*=*1y#$vH+p118np>VxkBP<3bolakbQ>)dZ(Fg?=3I#5g
z#Uqi)WT(>!!GAF*yWNh*<56l7i^c7B8$|(^5{ZQ3^YAhd2;5T>^fRR(4u_M?W>Gcp
zm^2#A!|5*uzzh9NlS-w6l7W&)Bq*_wNCY`-w_Ah3Kyi5GG8hb^WT4b)HOixQI-SS?
z2)5hpuS`$DA}52a(7+0Xg6v~5nGS~oD#-8mlXq|GkAHnS%w}`BT;}un1nz7$Ga8M<
z;Si-!DwVxnPcD}emc?R`OeUc?VhKv4(STk<{;U+ORtpKx(V?@T8VaJ%=R*$H6sy%r
zi6SrWoeC<Is#Ge$ARvO_WjGv;dc97@0)4q$QY`c(9E@&!%mVYH)9H{;?DcvtLcm8p
zm&-9sihq8;&$j<zz`(m9R}+uNFIIz$FcN=VlF!di<nz3eN~Orv<nwt}<Nx_Wz9QXj
zH@TW-vk9kXiODJ)TAExCT|XX=)oK;>Ijdy%1W%_^dJ%LB{DItqc8|}Ew$EfT_n^Gb
z2mbZoQP1adIxB?b-B<R$qvK4N0n7kq05gCY_!wUp_zQW<pU`aP7vul{002ovPDHLk
FV1lCuL9hS-

diff --git a/Telegram/Resources/icons/settings/premium/status@3x.png b/Telegram/Resources/icons/settings/premium/status@3x.png
index 01052baef0a0a36e8cfae110a990e23b1165aadf..9127319e96a61143c6a317b2d2e2702bcf2b0f31 100644
GIT binary patch
delta 1506
zcmV<81s(d13D69XfPV$LNkl<ZXa((AZ77~!7$2KqVG3*B`JyHiHA^dH-d6a+q)AL(
z+rF%3^MOR(it>TNB(#krg%4WV%-b|+nU^dSA}^!lb*(Y>|NZ}`PUnBl<8eRFbKmP(
z-9G5L&-FXk^}DX~oa;W<xi4K(|I`eq8BjBzW<bq=ngKNfYJUa{%)n&>=WCyCWo4C?
zmKG5avA({(ySu9${MTsB&CS1j`SSa}zoVlgA0MB;hPrm5$;rtiD)RsR`}f-M@@h>D
zH23V;v(3%T>FMd)w{LT%Zr;4fj)aDW1_uYTBgQM=y?d9g$@ccPlamw6H83!cYKbs3
zG{gdoSH^O&)PKSI^5y?kqp`6u762LRX1wuGX7}#h<9L^plu#{%`1p7ZFgiNQ0*qJg
z?d@f$pPrtgxKp}Vz~$v-76AGF{rePPyn?f{^WNSbOMPo=%hA!1GJg8>DXWD%K0Z#h
zMlGbKrgGE=2M6h9v$nS8A_q7oCdQ~kqU7GbeappGTz_0lwG|Z=oOWVj!o<Xc0*qI1
zadFw--)GrlvxSF;6UNY<oSd*)$g#1p+T+2R=jP@P3=E8njI^}0czSwj#~Z4>apOjF
zbMxoVp9>2M&CJZGx%BjOjyk*9T3cH=E#B$c7p=ComYwqT>sRVa5uvZIkNWHE?7VX2
z3U$M(2!96oHZ}eCa1#&^Kxa`QAt8az<pgX(3L^r}HylRJM;jX(&R=_bJFOFLZf*w$
z2OOZNsE9B?kemvOK*HuIT3TAJtgLWgB$0#3`3RTdZh7<O4ds`anaL5tW+SJK=;&xp
zg~z0%B#~c1L4k;T^5lt#lyl_e%a@{8Y;GFcyMK4@IKa1W-*6ztj+&dB<5UX^3v|H=
zOIur8t`3nYw{G2%GYrud7HxQVnB#}X#>Pfc4BXw_#UEsUe?KD6%E}Ta;x)*6$<NOh
zF&i5jEJi8$`t|FygJen6)zuM~+1c6bIFV6BkreS|y|lDMt1Eg_bP3|z*RNkI6%1z=
zMSmZ^B9TE&O%0G(IxG>V#b&FnuKw}k2RDw#jEoFoA6rh;V&u`IM;rkKA3Z!g*4EZU
z9#vIU@CxnW;i0J2ID#5@_3Bm3`uOpqMgZ!qf{|byu3fvvI`H=P-r3m^so)F+*yG2K
zMb&vD_4V~HUcAt##c!2$Dzkj&&K)>_V}CSMetv$`I>vEPBN8etEkz;#7FD{A;Gjd@
z$uBG}E>c22fBwwL$ssimp2EVyFhaFdXlQ7dnwp}IdJ76}FjO;<)`t%tTwPsB3LiXp
zfZ8BpWEoNR_^a_pg*(uP4<C})P}yV&gENA<BLB~iwTQZ;vA%;xTU#5B3jm|b;D2Zt
z{@Byg!_rVhwzs$c^y!mEuB)r7At52~D)y8b@zMv$i&2qySfki%-QC?9X`P*&RaRD_
zr~LKnmqvhogXGP@a!^XfnU!k+^<yk?R(W~3ev#;W#f|ERH*a2Eo__vxoy9h?I?dHN
zFk-!|tSluZ1-+S0&h!9tO^tL5dVdB62d`eeii(QDfl#_LJ&=ET#Rx_8l=y(6=xv1F
zKMMU<jKnrMIT;ibgw|M()sQmf`}glsU?RrQz+e%F1AI?H$01rbd<54Dh8r=+f`x?z
zE|Me`ap|zg$Vh`|$ub$Y8t9RwlfDp%SBssUoh;mBF!KEQbDTmhBrOGCbbsGvaOBt6
zp-Bmb#o$s%hD^(A0%v0JLW(7Qp^PgxTw2M?N*-&zzP=i7doB!#0OI1}<l&)&6&&_w
zW@aQt!or%Gnv~!pr%m*r=XE-4B&<9;JB#n2a*|Na7Cu(s48gKLPsR%4HdZ+%@>}-u
z^6Kj9IxikFj{9Q=2M76m|62iz{{H?Q9UbB&F|PL#6BA`5U?QU}>89`%0!L?DB%|Fr
zK0e0hoB8>9>C;uDngKNfY6jE{s2NZ*pk_eLfSLg{0~#{$556y(Rm*yKKmY&$07*qo
IM6N<$f|eEhF8}}l

delta 1053
zcmV+&1mgS942}tqfPVw{Nkl<ZXa((9ODJto7<P_V-iab&;4vWvQX(d#WTY^Zl#+5v
znPFl;C<6mAL5vKPGLsa^D`DbQgox;PB*~lmbx%3FwbtHy?Q_qu_r3Q&qqV>FKfd*?
zv;Ot(?dL~7Qh*d71xNu>fD|AFNC8rS6d(mi0aAbz_>UE^nSZ!;o6VM%mKGTqsm<Zz
z<Ky7q;PLTM+srDn>FMcvdwXAUzxVg|{{H@efB>to*WfWbJ1e&+w^dtPn?^vZOx@nz
zx+%+zu*34~?eeDHwcOa)Fd8sCJlv>@3HFB0m}^IehKA&c8m@|AVPUzsxzW+lGC4r#
z`1p8tcNeFhw|`fwiWwOhqobq#{{BjrlamwVy}7wjf?gXJ6%}1wUCEsYx7Fq4Wkp4W
zB#GGA*rTH(xAX7O{r!DrW~O8W!)A87{p{?FNh$gG`T1E~T&$ZiC@5%gagm9M-7YID
z)AjY-0H=aUY3OllYfCrx`1m*<)8TOFI{uLXiW0=*6Msu0@WuJmV9?sy%7+>l7|?L>
z+Eiy}C!bpq!NNj9Le!{KR#xKg%7mJknen*WFkBU@tE<b)%QdQEaB#5jNUW}|=I7@N
z7lv}Tu&@vs8mgSdbqsry4JByW7!`bCVuBGKk7#5I3JQd@5)%{8&(9ebr_-5|k|MM>
z2kh?dW`8KU9-Eq)goJSFHa9mJmzS3p+$%zRbHIj%28N>Rv9GUBNC?|IH8sV%)YjGt
z?acvk@PsvUa&i(Eppa8fPY=JUuC6Yju~E=)@en4+$;rX#=bO}(l@;8}&(F`iS4~X~
zZY>5sJUnb}ZpLaE{QCMD*WlaR8-x4yF*!N;;(y|T*B-CE=#PZCBO)SD9(WfGWM^k5
zAt6B+%_m?~RMhtNHcto|qf=2!Sy))eyXYdr!^3I;y^IG22F}gR@r0h9p45uFi$QjF
zHt(g2EH5v6c>!wS#>U2YHfSKKs;bmnzVW4{CEiyX>Fw?PcK6|+qoYH}qot)qCTo0r
z{D1WHw6Ku!VR65sx@xzYOi~Z4VqIO`$jAs23y<ho)$U^67znz@n3x#hloGtVyZedH
zZ>6QBudlDXuGj(QwO2{b>f@}eEHtM4s@B)nnJ(7qD^c>pSKHImQ+`$Wun`v*r?fPE
zZ0y@=dwY9QQW8%ZuOy9)jXVwnZ3kxO=YQvgvsMB3_xJhjl0gOs2d%OOArE~0l39k(
zCND2f7}H0fxbId=wzjsWHl>TB+t9T!%>b>s<dw>A+7IuTVU6nR>mME-c;4E`&z~_*
zIuCS1B_$=Psi|sjkV>`wj;g3+Ok+}j6d(mi0aAbzAO%PPQh*d71xNu>fE4%#6c+de
XLRu#M4xOR800000NkvXXu0mjfJ3JUC

diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index ea6c5f2e8..8bded7e02 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -2056,6 +2056,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_premium_summary_about_animated_userpics" = "Video avatars animated in chat lists and chats to allow for additional self-expression.";
 "lng_premium_summary_subtitle_translation" = "Real-Time Translation";
 "lng_premium_summary_about_translation" = "Real-time translation of channels and chats into other languages.";
+"lng_premium_summary_subtitle_business" = "Telegram Business";
+"lng_premium_summary_about_business" = "Upgrade your account with business features such as location, opening hours and quick replies.";
 "lng_premium_summary_bottom_subtitle" = "About Telegram Premium";
 "lng_premium_summary_bottom_about" = "While the free version of Telegram already gives its users more than any other messaging application, **Telegram Premium** pushes its capabilities even further.\n\n**Telegram Premium** is a paid option, because most Premium Features require additional expenses from Telegram to third parties such as data center providers and server manufacturers. Contributions from **Telegram Premium** users allow us to cover such costs and also help Telegram stay free for everyone.";
 "lng_premium_summary_button" = "Subscribe for {cost} per month";
@@ -2154,6 +2156,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_premium_gifts_terms" = "By gifting Telegram Premium, you agree to the Telegram {link} and {policy}.";
 "lng_premium_gifts_terms_policy" = "Privacy Policy";
 
+"lng_business_title" = "Telegram Business";
+"lng_business_about" = "Turn your account to a business page with these additional features.";
+"lng_business_unlocked" = "You have now unlocked these additional business features.";
+"lng_business_subtitle_location" = "Location";
+"lng_business_about_location" = "Display the location of your business on your account.";
+"lng_business_subtitle_opening_hours" = "Opening Hours";
+"lng_business_about_opening_hours" = "Show to your customers when you are open for business.";
+"lng_business_subtitle_quick_replies" = "Quick Replies";
+"lng_business_about_quick_replies" = "Set up shortcuts up to 20 messages each to respond to customers faster.";
+"lng_business_subtitle_greeting_messages" = "Greeting Messages";
+"lng_business_about_greeting_messages" = "Create greetings that will be automatically sent to new customers.";
+"lng_business_subtitle_away_messages" = "Away Messages";
+"lng_business_about_away_messages" = "Define messages that are automatically sent when you are off.";
+"lng_business_subtitle_chatbots" = "Chatbots";
+"lng_business_about_chatbots" = "Add any third party chatbots that will process customer interactions.";
+
 "lng_boost_channel_button" = "Boost Channel";
 "lng_boost_group_button" = "Boost Group";
 "lng_boost_again_button" = "Boost Again";
diff --git a/Telegram/Resources/qrc/telegram/telegram.qrc b/Telegram/Resources/qrc/telegram/telegram.qrc
index ccf1a1d7a..ae6edc957 100644
--- a/Telegram/Resources/qrc/telegram/telegram.qrc
+++ b/Telegram/Resources/qrc/telegram/telegram.qrc
@@ -3,6 +3,7 @@
     <file alias="art/background.tgv">../../art/background.tgv</file>
     <file alias="art/bg_thumbnail.png">../../art/bg_thumbnail.png</file>
     <file alias="art/bg_initial.jpg">../../art/bg_initial.jpg</file>
+    <file alias="art/business_logo.png">../../art/business_logo.png</file>
     <file alias="art/logo_256.png">../../art/logo_256.png</file>
     <file alias="art/logo_256_no_margin.png">../../art/logo_256_no_margin.png</file>
     <file alias="art/themeimage.jpg">../../art/themeimage.jpg</file>
diff --git a/Telegram/SourceFiles/boxes/premium_preview_box.cpp b/Telegram/SourceFiles/boxes/premium_preview_box.cpp
index 93df20cfa..c3ce95189 100644
--- a/Telegram/SourceFiles/boxes/premium_preview_box.cpp
+++ b/Telegram/SourceFiles/boxes/premium_preview_box.cpp
@@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/boxes/confirm_box.h"
 #include "ui/painter.h"
 #include "ui/vertical_list.h"
+#include "settings/settings_business.h"
 #include "settings/settings_premium.h"
 #include "lottie/lottie_single_player.h"
 #include "history/view/media/history_view_sticker.h"
@@ -128,6 +129,8 @@ void PreloadSticker(const std::shared_ptr<Data::DocumentMedia> &media) {
 		return tr::lng_premium_summary_subtitle_animated_userpics();
 	case PremiumPreview::RealTimeTranslation:
 		return tr::lng_premium_summary_subtitle_translation();
+	case PremiumPreview::Business:
+		return tr::lng_premium_summary_subtitle_business();
 	}
 	Unexpected("PremiumPreview in SectionTitle.");
 }
@@ -170,6 +173,8 @@ void PreloadSticker(const std::shared_ptr<Data::DocumentMedia> &media) {
 		return tr::lng_premium_summary_about_animated_userpics();
 	case PremiumPreview::RealTimeTranslation:
 		return tr::lng_premium_summary_about_translation();
+	case PremiumPreview::Business:
+		return tr::lng_premium_summary_about_business();
 	}
 	Unexpected("PremiumPreview in SectionTitle.");
 }
@@ -1219,6 +1224,13 @@ void Show(
 			DecorateListPromoBox(box, show, descriptor);
 		}));
 		return;
+	} else if (descriptor.section == PremiumPreview::Business) {
+		const auto window = show->resolveWindow(
+			ChatHelpers::WindowUsage::PremiumPromo);
+		if (window) {
+			Settings::ShowBusiness(window);
+		}
+		return;
 	}
 	auto &list = Preloads();
 	for (auto i = begin(list); i != end(list);) {
diff --git a/Telegram/SourceFiles/boxes/premium_preview_box.h b/Telegram/SourceFiles/boxes/premium_preview_box.h
index b7fb7a40e..9fc4da279 100644
--- a/Telegram/SourceFiles/boxes/premium_preview_box.h
+++ b/Telegram/SourceFiles/boxes/premium_preview_box.h
@@ -64,6 +64,7 @@ enum class PremiumPreview {
 	TagsForMessages,
 	LastSeen,
 	MessagePrivacy,
+	Business,
 
 	kCount,
 };
diff --git a/Telegram/SourceFiles/mtproto/scheme/api.tl b/Telegram/SourceFiles/mtproto/scheme/api.tl
index 1f17c1a7d..4b453c9b8 100644
--- a/Telegram/SourceFiles/mtproto/scheme/api.tl
+++ b/Telegram/SourceFiles/mtproto/scheme/api.tl
@@ -402,6 +402,7 @@ updateBotMessageReactions#9cb7759 peer:Peer msg_id:int date:int reactions:Vector
 updateSavedDialogPinned#aeaf9e74 flags:# pinned:flags.0?true peer:DialogPeer = Update;
 updatePinnedSavedDialogs#686c85a6 flags:# order:flags.0?Vector<DialogPeer> = Update;
 updateSavedReactionTags#39c67432 = Update;
+updateSmsJob#f16269d4 job_id:string = Update;
 
 updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
 
@@ -1653,6 +1654,12 @@ messages.savedReactionTags#3259950a tags:Vector<SavedReactionTag> hash:long = me
 
 outboxReadDate#3bb842ac date:int = OutboxReadDate;
 
+smsjobs.eligibleToJoin#dc8b44cf terms_url:string monthly_sent_sms:int = smsjobs.EligibilityToJoin;
+
+smsjobs.status#2aee9191 flags:# allow_international:flags.0?true recent_sent:int recent_since:int recent_remains:int total_sent:int total_since:int last_gift_slug:flags.1?string terms_url:string = smsjobs.Status;
+
+smsJob#e6a1eeb8 job_id:string phone_number:string text:string = SmsJob;
+
 ---functions---
 
 invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@@ -2252,4 +2259,12 @@ premium.applyBoost#6b7da746 flags:# slots:flags.0?Vector<int> peer:InputPeer = p
 premium.getBoostsStatus#42f1f61 peer:InputPeer = premium.BoostsStatus;
 premium.getUserBoosts#39854d1f peer:InputPeer user_id:InputUser = premium.BoostsList;
 
-// LAYER 174
+smsjobs.isEligibleToJoin#edc39d0 = smsjobs.EligibilityToJoin;
+smsjobs.join#a74ece2d = Bool;
+smsjobs.leave#9898ad73 = Bool;
+smsjobs.updateSettings#93fa0bf flags:# allow_international:flags.0?true = Bool;
+smsjobs.getStatus#10a698e8 = smsjobs.Status;
+smsjobs.getSmsJob#778d902f job_id:string = SmsJob;
+smsjobs.finishJob#4f1ebf24 flags:# job_id:string error:flags.0?string = Bool;
+
+// LAYER 175
diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style
index 9716a383e..eb6bcb360 100644
--- a/Telegram/SourceFiles/settings/settings.style
+++ b/Telegram/SourceFiles/settings/settings.style
@@ -94,6 +94,7 @@ settingsPremiumIconTranslations: icon {{ "settings/premium/translations", settin
 settingsPremiumIconTags: icon {{ "settings/premium/tags", settingsIconFg }};
 settingsPremiumIconLastSeen: icon {{ "settings/premium/lastseen", settingsIconFg }};
 settingsPremiumIconPrivacy: icon {{ "settings/premium/privacy", settingsIconFg }};
+settingsPremiumIconBusiness: icon {{ "settings/premium/privacy", settingsIconFg }};
 
 settingsStoriesIconOrder: icon {{ "settings/premium/stories_order", premiumButtonBg1 }};
 settingsStoriesIconStealth: icon {{ "menu/stealth", premiumButtonBg1 }};
@@ -103,6 +104,13 @@ settingsStoriesIconDownload: icon {{ "menu/download", premiumButtonBg1 }};
 settingsStoriesIconCaption: icon {{ "settings/premium/stories_caption", premiumButtonBg1 }};
 settingsStoriesIconLinks: icon {{ "menu/links_profile", premiumButtonBg1 }};
 
+settingsBusinessIconLocation: icon {{ "settings/premium/business/business_location", settingsIconFg }};
+settingsBusinessIconHours: icon {{ "settings/premium/business/business_hours", settingsIconFg }};
+settingsBusinessIconReplies: icon {{ "settings/premium/business/business_quick", settingsIconFg }};
+settingsBusinessIconGreeting: icon {{ "settings/premium/status", settingsIconFg }};
+settingsBusinessIconAway: icon {{ "settings/premium/business/business_away", settingsIconFg }};
+settingsBusinessIconChatbots: icon {{ "settings/premium/business/business_chatbots", settingsIconFg }};
+
 settingsPremiumNewBadge: FlatLabel(defaultFlatLabel) {
 	style: TextStyle(semiboldTextStyle) {
 		font: font(10px semibold);
diff --git a/Telegram/SourceFiles/settings/settings_business.cpp b/Telegram/SourceFiles/settings/settings_business.cpp
new file mode 100644
index 000000000..23a4d8914
--- /dev/null
+++ b/Telegram/SourceFiles/settings/settings_business.cpp
@@ -0,0 +1,568 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "settings/settings_business.h"
+
+#include "boxes/premium_preview_box.h"
+#include "core/click_handler_types.h"
+#include "data/data_peer_values.h" // AmPremiumValue.
+#include "info/info_wrap_widget.h" // Info::Wrap.
+#include "info/settings/info_settings_widget.h" // SectionCustomTopBarData.
+#include "lang/lang_keys.h"
+#include "main/main_session.h"
+#include "settings/settings_common_session.h"
+#include "settings/settings_premium.h"
+#include "ui/effects/gradient.h"
+#include "ui/effects/premium_graphics.h"
+#include "ui/effects/premium_top_bar.h"
+#include "ui/layers/generic_box.h"
+#include "ui/text/text_utilities.h"
+#include "ui/widgets/checkbox.h" // Ui::RadiobuttonGroup.
+#include "ui/widgets/gradient_round_button.h"
+#include "ui/wrap/fade_wrap.h"
+#include "ui/wrap/slide_wrap.h"
+#include "ui/wrap/vertical_layout.h"
+#include "ui/vertical_list.h"
+#include "window/window_session_controller.h"
+#include "apiwrap.h"
+#include "api/api_premium.h"
+#include "styles/style_premium.h"
+#include "styles/style_info.h"
+#include "styles/style_layers.h"
+#include "styles/style_settings.h"
+
+namespace Settings {
+namespace {
+
+struct Entry {
+	const style::icon *icon;
+	rpl::producer<QString> title;
+	rpl::producer<QString> description;
+	BusinessFeature feature = BusinessFeature::Location;
+};
+
+using Order = std::vector<QString>;
+
+[[nodiscard]] Order FallbackOrder() {
+	return Order{
+		u"location"_q,
+		u"opening_hours"_q,
+		u"quick_replies"_q,
+		u"greeting_messages"_q,
+		u"away_messages"_q,
+		u"chatbots"_q,
+	};
+}
+
+[[nodiscard]] base::flat_map<QString, Entry> EntryMap() {
+	return base::flat_map<QString, Entry>{
+		{
+			u"location"_q,
+			Entry{
+				&st::settingsBusinessIconLocation,
+				tr::lng_business_subtitle_location(),
+				tr::lng_business_about_location(),
+				BusinessFeature::Location,
+			},
+		},
+		{
+			u"opening_hours"_q,
+			Entry{
+				&st::settingsBusinessIconHours,
+				tr::lng_business_subtitle_opening_hours(),
+				tr::lng_business_about_opening_hours(),
+				BusinessFeature::OpeningHours,
+			},
+		},
+		{
+			u"quick_replies"_q,
+			Entry{
+				&st::settingsBusinessIconReplies,
+				tr::lng_business_subtitle_quick_replies(),
+				tr::lng_business_about_quick_replies(),
+				BusinessFeature::QuickReplies,
+			},
+		},
+		{
+			u"greeting_messages"_q,
+			Entry{
+				&st::settingsBusinessIconGreeting,
+				tr::lng_business_subtitle_greeting_messages(),
+				tr::lng_business_about_greeting_messages(),
+				BusinessFeature::GreetingMessages,
+			},
+		},
+		{
+			u"away_messages"_q,
+			Entry{
+				&st::settingsBusinessIconAway,
+				tr::lng_business_subtitle_away_messages(),
+				tr::lng_business_about_away_messages(),
+				BusinessFeature::AwayMessages,
+			},
+		},
+		{
+			u"chatbots"_q,
+			Entry{
+				&st::settingsBusinessIconChatbots,
+				tr::lng_business_subtitle_chatbots(),
+				tr::lng_business_about_chatbots(),
+				BusinessFeature::Chatbots,
+			},
+		},
+	};
+}
+
+void AddBusinessSummary(
+		not_null<Ui::VerticalLayout*> content,
+		not_null<Window::SessionController*> controller,
+		Fn<void(BusinessFeature)> buttonCallback) {
+	const auto &stDefault = st::settingsButton;
+	const auto &stLabel = st::defaultFlatLabel;
+	const auto iconSize = st::settingsPremiumIconDouble.size();
+	const auto &titlePadding = st::settingsPremiumRowTitlePadding;
+	const auto &descriptionPadding = st::settingsPremiumRowAboutPadding;
+
+	auto entryMap = EntryMap();
+	auto iconContainers = std::vector<Ui::AbstractButton*>();
+	iconContainers.reserve(int(entryMap.size()));
+
+	const auto addRow = [&](Entry &entry) {
+		const auto labelAscent = stLabel.style.font->ascent;
+		const auto button = Ui::CreateChild<Ui::SettingsButton>(
+			content.get(),
+			rpl::single(QString()));
+
+		const auto label = content->add(
+			object_ptr<Ui::FlatLabel>(
+				content,
+				std::move(entry.title) | rpl::map(Ui::Text::Bold),
+				stLabel),
+			titlePadding);
+		label->setAttribute(Qt::WA_TransparentForMouseEvents);
+		const auto description = content->add(
+			object_ptr<Ui::FlatLabel>(
+				content,
+				std::move(entry.description),
+				st::boxDividerLabel),
+			descriptionPadding);
+		description->setAttribute(Qt::WA_TransparentForMouseEvents);
+
+		const auto dummy = Ui::CreateChild<Ui::AbstractButton>(content.get());
+		dummy->setAttribute(Qt::WA_TransparentForMouseEvents);
+
+		content->sizeValue(
+		) | rpl::start_with_next([=](const QSize &s) {
+			dummy->resize(s.width(), iconSize.height());
+		}, dummy->lifetime());
+
+		label->geometryValue(
+		) | rpl::start_with_next([=](const QRect &r) {
+			dummy->moveToLeft(0, r.y() + (r.height() - labelAscent));
+		}, dummy->lifetime());
+
+		rpl::combine(
+			content->widthValue(),
+			label->heightValue(),
+			description->heightValue()
+		) | rpl::start_with_next([=,
+			topPadding = titlePadding,
+			bottomPadding = descriptionPadding](
+				int width,
+				int topHeight,
+				int bottomHeight) {
+			button->resize(
+				width,
+				topPadding.top()
+					+ topHeight
+					+ topPadding.bottom()
+					+ bottomPadding.top()
+					+ bottomHeight
+					+ bottomPadding.bottom());
+		}, button->lifetime());
+		label->topValue(
+		) | rpl::start_with_next([=, padding = titlePadding.top()](int top) {
+			button->moveToLeft(0, top - padding);
+		}, button->lifetime());
+		const auto arrow = Ui::CreateChild<Ui::IconButton>(
+			button,
+			st::backButton);
+		arrow->setIconOverride(
+			&st::settingsPremiumArrow,
+			&st::settingsPremiumArrowOver);
+		arrow->setAttribute(Qt::WA_TransparentForMouseEvents);
+		button->sizeValue(
+		) | rpl::start_with_next([=](const QSize &s) {
+			const auto &point = st::settingsPremiumArrowShift;
+			arrow->moveToRight(
+				-point.x(),
+				point.y() + (s.height() - arrow->height()) / 2);
+		}, arrow->lifetime());
+
+		const auto feature = entry.feature;
+		button->setClickedCallback([=] { buttonCallback(feature); });
+
+		iconContainers.push_back(dummy);
+	};
+
+	auto icons = std::vector<const style::icon *>();
+	icons.reserve(int(entryMap.size()));
+	{
+		const auto &account = controller->session().account();
+		const auto mtpOrder = FallbackOrder();/* session->account().appConfig().get<Order>(
+			"premium_promo_order",
+			FallbackOrder());*/ AssertIsDebug()
+		const auto processEntry = [&](Entry &entry) {
+			icons.push_back(entry.icon);
+			addRow(entry);
+		};
+
+		for (const auto &key : mtpOrder) {
+			auto it = entryMap.find(key);
+			if (it == end(entryMap)) {
+				continue;
+			}
+			processEntry(it->second);
+		}
+	}
+
+	content->resizeToWidth(content->height());
+
+	// Icons.
+	Assert(iconContainers.size() > 2);
+	const auto from = iconContainers.front()->y();
+	const auto to = iconContainers.back()->y() + iconSize.height();
+	auto gradient = QLinearGradient(0, 0, 0, to - from);
+	gradient.setStops(Ui::Premium::FullHeightGradientStops());
+	for (auto i = 0; i < int(icons.size()); i++) {
+		const auto &iconContainer = iconContainers[i];
+
+		const auto pointTop = iconContainer->y() - from;
+		const auto pointBottom = pointTop + iconContainer->height();
+		const auto ratioTop = pointTop / float64(to - from);
+		const auto ratioBottom = pointBottom / float64(to - from);
+
+		auto resultGradient = QLinearGradient(
+			QPointF(),
+			QPointF(0, pointBottom - pointTop));
+
+		resultGradient.setColorAt(
+			.0,
+			anim::gradient_color_at(gradient, ratioTop));
+		resultGradient.setColorAt(
+			.1,
+			anim::gradient_color_at(gradient, ratioBottom));
+
+		const auto brush = QBrush(resultGradient);
+		AddButtonIcon(
+			iconContainer,
+			stDefault,
+			{ .icon = icons[i], .backgroundBrush = brush });
+	}
+
+	Ui::AddSkip(content, descriptionPadding.bottom());
+}
+
+class Business : public Section<Business> {
+public:
+	Business(
+		QWidget *parent,
+		not_null<Window::SessionController*> controller);
+
+	[[nodiscard]] rpl::producer<QString> title() override;
+
+	[[nodiscard]] QPointer<Ui::RpWidget> createPinnedToTop(
+		not_null<QWidget*> parent) override;
+	[[nodiscard]] QPointer<Ui::RpWidget> createPinnedToBottom(
+		not_null<Ui::RpWidget*> parent) override;
+
+	void showFinished() override;
+
+	[[nodiscard]] bool hasFlexibleTopBar() const override;
+
+	void setStepDataReference(std::any &data) override;
+
+	[[nodiscard]] rpl::producer<> sectionShowBack() override final;
+
+private:
+	void setupContent();
+
+	const not_null<Window::SessionController*> _controller;
+
+	QPointer<Ui::GradientButton> _subscribe;
+	base::unique_qptr<Ui::FadeWrap<Ui::IconButton>> _back;
+	base::unique_qptr<Ui::IconButton> _close;
+	rpl::variable<bool> _backToggles;
+	rpl::variable<Info::Wrap> _wrap;
+	Fn<void(bool)> _setPaused;
+
+	std::shared_ptr<Ui::RadiobuttonGroup> _radioGroup;
+
+	rpl::event_stream<> _showBack;
+	rpl::event_stream<> _showFinished;
+	rpl::variable<QString> _buttonText;
+
+};
+
+Business::Business(
+	QWidget *parent,
+	not_null<Window::SessionController*> controller)
+: Section(parent)
+, _controller(controller)
+, _radioGroup(std::make_shared<Ui::RadiobuttonGroup>()) {
+	setupContent();
+	_controller->session().api().premium().reload();
+}
+
+rpl::producer<QString> Business::title() {
+	return tr::lng_premium_summary_title();
+}
+
+bool Business::hasFlexibleTopBar() const {
+	return true;
+}
+
+rpl::producer<> Business::sectionShowBack() {
+	return _showBack.events();
+}
+
+void Business::setStepDataReference(std::any &data) {
+	using namespace Info::Settings;
+	const auto my = std::any_cast<SectionCustomTopBarData>(&data);
+	if (my) {
+		_backToggles = std::move(
+			my->backButtonEnables
+		) | rpl::map_to(true);
+		_wrap = std::move(my->wrapValue);
+	}
+}
+
+void Business::setupContent() {
+	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
+
+	Ui::AddSkip(content, st::settingsFromFileTop);
+
+	AddBusinessSummary(content, _controller, [=](BusinessFeature feature) {
+	});
+
+	Ui::ResizeFitChild(this, content);
+}
+
+QPointer<Ui::RpWidget> Business::createPinnedToTop(
+		not_null<QWidget*> parent) {
+	auto title = tr::lng_business_title();
+	auto about = [&]() -> rpl::producer<TextWithEntities> {
+		return rpl::conditional(
+			Data::AmPremiumValue(&_controller->session()),
+			tr::lng_business_unlocked(),
+			tr::lng_business_about()
+		) | Ui::Text::ToWithEntities();
+	}();
+
+	const auto content = [&]() -> Ui::Premium::TopBarAbstract* {
+		const auto weak = base::make_weak(_controller);
+		const auto clickContextOther = [=] {
+			return QVariant::fromValue(ClickHandlerContext{
+				.sessionWindow = weak,
+				.botStartAutoSubmit = true,
+			});
+		};
+		return Ui::CreateChild<Ui::Premium::TopBar>(
+			parent.get(),
+			st::defaultPremiumCover,
+			Ui::Premium::TopBarDescriptor{
+				.clickContextOther = clickContextOther,
+				.logo = u"dollar"_q,
+				.title = std::move(title),
+				.about = std::move(about),
+			});
+	}();
+	_setPaused = [=](bool paused) {
+		content->setPaused(paused);
+		if (_subscribe) {
+			_subscribe->setGlarePaused(paused);
+		}
+	};
+
+	_wrap.value(
+	) | rpl::start_with_next([=](Info::Wrap wrap) {
+		content->setRoundEdges(wrap == Info::Wrap::Layer);
+	}, content->lifetime());
+
+	const auto calculateMaximumHeight = [=] {
+		return st::settingsPremiumTopHeight;
+	};
+
+	content->setMaximumHeight(calculateMaximumHeight());
+	content->setMinimumHeight(st::settingsPremiumTopHeight);// st::infoLayerTopBarHeight);
+
+	content->resize(content->width(), content->maximumHeight());
+	//content->additionalHeight(
+	//) | rpl::start_with_next([=](int additionalHeight) {
+	//	const auto wasMax = (content->height() == content->maximumHeight());
+	//	content->setMaximumHeight(calculateMaximumHeight()
+	//		+ additionalHeight);
+	//	if (wasMax) {
+	//		content->resize(content->width(), content->maximumHeight());
+	//	}
+	//}, content->lifetime());
+
+	_wrap.value(
+	) | rpl::start_with_next([=](Info::Wrap wrap) {
+		const auto isLayer = (wrap == Info::Wrap::Layer);
+		_back = base::make_unique_q<Ui::FadeWrap<Ui::IconButton>>(
+			content,
+			object_ptr<Ui::IconButton>(
+				content,
+				(isLayer
+					? st::settingsPremiumLayerTopBarBack
+					: st::settingsPremiumTopBarBack)),
+			st::infoTopBarScale);
+		_back->setDuration(0);
+		_back->toggleOn(isLayer
+			? _backToggles.value() | rpl::type_erased()
+			: rpl::single(true));
+		_back->entity()->addClickHandler([=] {
+			_showBack.fire({});
+		});
+		_back->toggledValue(
+		) | rpl::start_with_next([=](bool toggled) {
+			const auto &st = isLayer ? st::infoLayerTopBar : st::infoTopBar;
+			content->setTextPosition(
+				toggled ? st.back.width : st.titlePosition.x(),
+				st.titlePosition.y());
+		}, _back->lifetime());
+
+		if (!isLayer) {
+			_close = nullptr;
+		} else {
+			_close = base::make_unique_q<Ui::IconButton>(
+				content,
+				st::settingsPremiumTopBarClose);
+			_close->addClickHandler([=] {
+				_controller->parentController()->hideLayer();
+				_controller->parentController()->hideSpecialLayer();
+			});
+			content->widthValue(
+			) | rpl::start_with_next([=] {
+				_close->moveToRight(0, 0);
+			}, _close->lifetime());
+		}
+	}, content->lifetime());
+
+	return Ui::MakeWeak(not_null<Ui::RpWidget*>{ content });
+}
+
+void Business::showFinished() {
+	_showFinished.fire({});
+}
+
+QPointer<Ui::RpWidget> Business::createPinnedToBottom(
+		not_null<Ui::RpWidget*> parent) {
+	const auto content = Ui::CreateChild<Ui::RpWidget>(parent.get());
+
+	const auto session = &_controller->session();
+
+	auto buttonText = _buttonText.value();
+
+	_subscribe = CreateSubscribeButton({
+		_controller,
+		content,
+		[] { return u"business"_q; },
+		std::move(buttonText),
+		std::nullopt,
+		[=, options = session->api().premium().subscriptionOptions()] {
+			const auto value = _radioGroup->value();
+			return (value < options.size() && value >= 0)
+				? options[value].botUrl
+				: QString();
+		},
+	});
+	{
+		const auto callback = [=](int value) {
+			const auto options =
+				_controller->session().api().premium().subscriptionOptions();
+			if (options.empty()) {
+				return;
+			}
+			Assert(value < options.size() && value >= 0);
+			auto text = tr::lng_premium_subscribe_button(
+				tr::now,
+				lt_cost,
+				options[value].costPerMonth);
+			_buttonText = std::move(text);
+		};
+		_radioGroup->setChangedCallback(callback);
+		callback(0);
+	}
+
+	_showFinished.events(
+	) | rpl::take(1) | rpl::start_with_next([=] {
+		_subscribe->startGlareAnimation();
+	}, _subscribe->lifetime());
+
+	content->widthValue(
+	) | rpl::start_with_next([=](int width) {
+		const auto padding = st::settingsPremiumButtonPadding;
+		_subscribe->resizeToWidth(width - padding.left() - padding.right());
+	}, _subscribe->lifetime());
+
+	rpl::combine(
+		_subscribe->heightValue(),
+		Data::AmPremiumValue(session),
+		session->premiumPossibleValue()
+	) | rpl::start_with_next([=](
+			int buttonHeight,
+			bool premium,
+			bool premiumPossible) {
+		const auto padding = st::settingsPremiumButtonPadding;
+		const auto finalHeight = !premiumPossible
+			? 0
+			: !premium
+			? (padding.top() + buttonHeight + padding.bottom())
+			: 0;
+		content->resize(content->width(), finalHeight);
+		_subscribe->moveToLeft(padding.left(), padding.top());
+		_subscribe->setVisible(!premium && premiumPossible);
+	}, _subscribe->lifetime());
+
+	return Ui::MakeWeak(not_null<Ui::RpWidget*>{ content });
+}
+
+} // namespace
+
+template <>
+struct SectionFactory<Business> : AbstractSectionFactory {
+	object_ptr<AbstractSection> create(
+		not_null<QWidget*> parent,
+		not_null<Window::SessionController*> controller
+	) const final override {
+		return object_ptr<Business>(parent, controller);
+	}
+	bool hasCustomTopBar() const final override {
+		return true;
+	}
+
+	[[nodiscard]] static const std::shared_ptr<SectionFactory> &Instance() {
+		static const auto result = std::make_shared<SectionFactory>();
+		return result;
+	}
+};
+
+Type BusinessId() {
+	return Business::Id();
+}
+
+void ShowBusiness(not_null<Window::SessionController*> controller) {
+	if (!controller->session().premiumPossible()) {
+		controller->show(Box(PremiumUnavailableBox));
+		return;
+	}
+	controller->showSettings(Settings::BusinessId());
+}
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/settings_business.h b/Telegram/SourceFiles/settings/settings_business.h
new file mode 100644
index 000000000..e255fd715
--- /dev/null
+++ b/Telegram/SourceFiles/settings/settings_business.h
@@ -0,0 +1,37 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "settings/settings_type.h"
+
+namespace Main {
+class Session;
+} // namespace Main
+
+namespace Window {
+class SessionController;
+} // namespace Window
+
+namespace Settings {
+
+enum class BusinessFeature {
+	Location,
+	OpeningHours,
+	QuickReplies,
+	GreetingMessages,
+	AwayMessages,
+	Chatbots,
+
+	kCount,
+};
+
+[[nodiscard]] Type BusinessId();
+
+void ShowBusiness(not_null<Window::SessionController*> controller);
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/settings_main.cpp b/Telegram/SourceFiles/settings/settings_main.cpp
index 1b5f8304b..854c94b05 100644
--- a/Telegram/SourceFiles/settings/settings_main.cpp
+++ b/Telegram/SourceFiles/settings/settings_main.cpp
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "settings/settings_main.h"
 
+#include "settings/settings_business.h"
 #include "settings/settings_codes.h"
 #include "settings/settings_chat.h"
 #include "settings/settings_information.h"
@@ -419,6 +420,19 @@ void SetupPremium(
 		controller->setPremiumRef("settings");
 		showOther(PremiumId());
 	});
+	const auto button = AddButtonWithIcon(
+		container,
+		tr::lng_business_title(),
+		st::settingsButton,
+		{ .icon = &st::menuIconShop });
+	button->addClickHandler([=] {
+		showOther(BusinessId());
+	});
+	constexpr auto kNewExpiresAt = int(1711958400);
+	if (base::unixtime::now() < kNewExpiresAt) {
+		Ui::NewBadge::AddToRight(button);
+	}
+
 	if (controller->session().premiumCanBuy()) {
 		const auto button = AddButtonWithIcon(
 			container,
diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp
index cde74eb5a..f9d627d3b 100644
--- a/Telegram/SourceFiles/settings/settings_premium.cpp
+++ b/Telegram/SourceFiles/settings/settings_premium.cpp
@@ -180,6 +180,7 @@ using Order = std::vector<QString>;
 		u"stories"_q,
 		u"more_upload"_q,
 		u"double_limits"_q,
+		u"business"_q,
 		u"last_seen"_q,
 		u"voice_to_text"_q,
 		u"faster_download"_q,
@@ -367,6 +368,16 @@ using Order = std::vector<QString>;
 				PremiumPreview::RealTimeTranslation,
 			},
 		},
+		{
+			u"business"_q,
+			Entry{
+				&st::settingsPremiumIconPlay, AssertIsDebug()
+				tr::lng_premium_summary_subtitle_business(),
+				tr::lng_premium_summary_about_business(),
+				PremiumPreview::Business,
+				true,
+			},
+		},
 	};
 }
 
@@ -1671,9 +1682,9 @@ void AddSummaryPremium(
 	icons.reserve(int(entryMap.size()));
 	{
 		const auto &account = controller->session().account();
-		const auto mtpOrder = account.appConfig().get<Order>(
+		const auto mtpOrder = FallbackOrder();/* session->account().appConfig().get<Order>(
 			"premium_promo_order",
-			FallbackOrder());
+			FallbackOrder());*/ AssertIsDebug()
 		const auto processEntry = [&](Entry &entry) {
 			icons.push_back(entry.icon);
 			addRow(entry);
diff --git a/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp b/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp
index 4094b584d..5ef11ba50 100644
--- a/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp
+++ b/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp
@@ -25,6 +25,21 @@ constexpr auto kBodyAnimationPart = 0.90;
 constexpr auto kTitleAdditionalScale = 0.15;
 constexpr auto kMinAcceptableContrast = 4.5; // 1.14;
 
+[[nodiscard]] QImage ScaleTo(QImage image) {
+	using namespace style;
+	const auto size = image.size();
+	const auto scale = DevicePixelRatio() * Scale() / 300.;
+	const auto scaled = QSize(
+		int(base::SafeRound(size.width() * scale)),
+		int(base::SafeRound(size.height() * scale)));
+	image = image.scaled(
+		scaled,
+		Qt::IgnoreAspectRatio,
+		Qt::SmoothTransformation);
+	image.setDevicePixelRatio(DevicePixelRatio());
+	return image;
+}
+
 } // namespace
 
 QString Svg() {
@@ -157,27 +172,41 @@ TopBar::TopBar(
 	rpl::producer<TextWithEntities> about,
 	bool light,
 	bool optimizeMinistars)
+: TopBar(parent, st, {
+	.clickContextOther = std::move(clickContextOther),
+	.title = std::move(title),
+	.about = std::move(about),
+	.light = light,
+	.optimizeMinistars = optimizeMinistars,
+}) {
+}
+
+TopBar::TopBar(
+	not_null<QWidget*> parent,
+	const style::PremiumCover &st,
+	TopBarDescriptor &&descriptor)
 : TopBarAbstract(parent, st)
-, _light(light)
+, _light(descriptor.light)
+, _logo(descriptor.logo)
 , _titleFont(st.titleFont)
 , _titlePadding(st.titlePadding)
-, _about(this, std::move(about), st.about)
-, _ministars(this, optimizeMinistars) {
+, _about(this, std::move(descriptor.about), st.about)
+, _ministars(this, descriptor.optimizeMinistars) {
 	std::move(
-		title
+		descriptor.title
 	) | rpl::start_with_next([=](QString text) {
 		_titlePath = QPainterPath();
 		_titlePath.addText(0, _titleFont->ascent, _titleFont, text);
 		update();
 	}, lifetime());
 
-	if (clickContextOther) {
+	if (const auto other = descriptor.clickContextOther) {
 		_about->setClickHandlerFilter([=](
 				const ClickHandlerPtr &handler,
 				Qt::MouseButton button) {
 			ActivateClickHandler(_about, handler, {
 				button,
-				clickContextOther()
+				other()
 			});
 			return false;
 		});
@@ -188,7 +217,10 @@ TopBar::TopBar(
 	) | rpl::start_with_next([=] {
 		TopBarAbstract::computeIsDark();
 
-		if (!_light && !TopBarAbstract::isDark()) {
+		if (_logo == u"dollar"_q) {
+			_dollar = ScaleTo(QImage(u":/gui/art/business_logo.png"_q));
+			_ministars.setColorOverride(st::premiumButtonFg->c);
+		} else if (!_light && !TopBarAbstract::isDark()) {
 			_star.load(Svg());
 			_ministars.setColorOverride(st::premiumButtonFg->c);
 		} else {
@@ -232,8 +264,11 @@ rpl::producer<int> TopBar::additionalHeight() const {
 }
 
 void TopBar::resizeEvent(QResizeEvent *e) {
-	const auto progress = (e->size().height() - minimumHeight())
-		/ float64(maximumHeight() - minimumHeight());
+	const auto max = maximumHeight();
+	const auto min = minimumHeight();
+	const auto progress = (max > min)
+		? ((e->size().height() - min) / float64(max - min))
+		: 1.;
 	_progress.top = 1. -
 		std::clamp(
 			(1. - progress) / kBodyAnimationPart,
@@ -291,7 +326,12 @@ void TopBar::paintEvent(QPaintEvent *e) {
 	}
 	p.resetTransform();
 
-	_star.render(&p, _starRect);
+	if (!_dollar.isNull()) {
+		auto hq = PainterHighQualityEnabler(p);
+		p.drawImage(_starRect, _dollar);
+	} else {
+		_star.render(&p, _starRect);
+	}
 
 	const auto color = _light
 		? st::settingsPremiumUserTitle.textFg
diff --git a/Telegram/SourceFiles/ui/effects/premium_top_bar.h b/Telegram/SourceFiles/ui/effects/premium_top_bar.h
index 9ccc4fa30..4ac3cc8be 100644
--- a/Telegram/SourceFiles/ui/effects/premium_top_bar.h
+++ b/Telegram/SourceFiles/ui/effects/premium_top_bar.h
@@ -64,6 +64,15 @@ private:
 
 };
 
+struct TopBarDescriptor {
+	Fn<QVariant()> clickContextOther;
+	QString logo;
+	rpl::producer<QString> title;
+	rpl::producer<TextWithEntities> about;
+	bool light = false;
+	bool optimizeMinistars = true;
+};
+
 class TopBar final : public TopBarAbstract {
 public:
 	TopBar(
@@ -74,6 +83,10 @@ public:
 		rpl::producer<TextWithEntities> about,
 		bool light = false,
 		bool optimizeMinistars = true);
+	TopBar(
+		not_null<QWidget*> parent,
+		const style::PremiumCover &st,
+		TopBarDescriptor &&descriptor);
 	~TopBar();
 
 	void setPaused(bool paused) override;
@@ -87,11 +100,13 @@ protected:
 
 private:
 	const bool _light = false;
+	const QString _logo;
 	const style::font &_titleFont;
 	const style::margins &_titlePadding;
 	object_ptr<FlatLabel> _about;
 	ColoredMiniStars _ministars;
 	QSvgRenderer _star;
+	QImage _dollar;
 
 	struct {
 		float64 top = 0.;
diff --git a/Telegram/SourceFiles/ui/menu_icons.style b/Telegram/SourceFiles/ui/menu_icons.style
index b0e0dda13..60fa215bf 100644
--- a/Telegram/SourceFiles/ui/menu_icons.style
+++ b/Telegram/SourceFiles/ui/menu_icons.style
@@ -137,6 +137,7 @@ menuIconAntispam: icon {{ "menu/antispam", menuIconColor }};
 menuIconChatDiscuss: icon {{ "menu/chat_discuss", menuIconColor }};
 menuIconBotCommands: icon {{ "menu/bot_commands", menuIconColor }};
 menuIconPremium: icon {{ "menu/premium", menuIconColor }};
+menuIconShop: icon {{ "menu/shop", menuIconColor }};
 menuIconIpAddress: icon {{ "menu/ip_address", menuIconColor }};
 menuIconAddress: icon {{ "menu/payment_address", menuIconColor }};
 menuIconShowAll: icon {{ "menu/all_media", menuIconColor }};

From 205479fcccca7ef463d1da290d821b340544ec9f Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 20 Feb 2024 14:33:46 +0400
Subject: [PATCH 039/108] Layout chatbots editing section.

---
 Telegram/CMakeLists.txt                       |   2 +
 Telegram/Resources/animations/robot.tgs       | Bin 0 -> 30782 bytes
 Telegram/Resources/langs/lang.strings         |  18 ++
 .../Resources/qrc/telegram/animations.qrc     |   1 +
 .../boxes/peers/edit_linked_chat_box.cpp      |  10 +-
 .../boxes/peers/edit_peer_color_box.cpp       |  34 +--
 .../SourceFiles/core/local_url_handlers.cpp   |  17 +-
 .../history/history_inner_widget.cpp          |  13 ++
 .../settings/business/settings_chatbots.cpp   | 201 ++++++++++++++++++
 .../settings/business/settings_chatbots.h     |  16 ++
 .../settings_cloud_password_manage.cpp        |  10 +-
 Telegram/SourceFiles/settings/settings.style  |  16 +-
 .../settings/settings_business.cpp            |  14 +-
 .../SourceFiles/settings/settings_common.cpp  |  56 ++---
 .../SourceFiles/settings/settings_common.h    |  17 +-
 Telegram/SourceFiles/ui/vertical_list.cpp     |  12 +-
 Telegram/SourceFiles/ui/vertical_list.h       |   8 +-
 17 files changed, 367 insertions(+), 78 deletions(-)
 create mode 100644 Telegram/Resources/animations/robot.tgs
 create mode 100644 Telegram/SourceFiles/settings/business/settings_chatbots.cpp
 create mode 100644 Telegram/SourceFiles/settings/business/settings_chatbots.h

diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index fbfbb55f2..0afd120d0 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -1277,6 +1277,8 @@ PRIVATE
     profile/profile_block_widget.h
     profile/profile_cover_drop_area.cpp
     profile/profile_cover_drop_area.h
+    settings/business/settings_chatbots.cpp
+    settings/business/settings_chatbots.h
     settings/cloud_password/settings_cloud_password_common.cpp
     settings/cloud_password/settings_cloud_password_common.h
     settings/cloud_password/settings_cloud_password_email.cpp
diff --git a/Telegram/Resources/animations/robot.tgs b/Telegram/Resources/animations/robot.tgs
new file mode 100644
index 0000000000000000000000000000000000000000..0076344f4c799f30199f553d5402e5f8b8c1daf8
GIT binary patch
literal 30782
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

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 8bded7e02..19ff138a1 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -2172,6 +2172,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_business_subtitle_chatbots" = "Chatbots";
 "lng_business_about_chatbots" = "Add any third party chatbots that will process customer interactions.";
 
+"lng_chatbots_title" = "Chatbots";
+"lng_chatbots_about" = "Add a bot to your account to help you automatically process and respond to the messages you receive. {link}";
+"lng_chatbots_about_link" = "Learn more...";
+"lng_chatbots_placeholder" = "Enter bot URL or username";
+"lng_chatbots_add_about" = "Enter the link to the Telegram bot that you want to automatically process your chats.";
+"lng_chatbots_access_title" = "Chats accessible for the bot";
+"lng_chatbots_all_except" = "All 1-to-1 Chats Except...";
+"lng_chatbots_selected" = "Only Selected Chats";
+"lng_chatbots_excluded_title" = "Excluded chats";
+"lng_chatbots_exclude_button" = "Exclude Chats";
+"lng_chatbots_included_title" = "Included chats";
+"lng_chatbots_include_button" = "Select Chats";
+"lng_chatbots_exclude_about" = "Select chats or entire chat categories which the bot will not have access to.";
+"lng_chatbots_permissions_title" = "Bot permissions";
+"lng_chatbots_reply" = "Reply to Messages";
+"lng_chatbots_reply_about" = "The bot will be able to view all new incoming messages, but not the messages that had been sent before you added the bot.";
+"lng_chatbots_remove" = "Remove Bot";
+
 "lng_boost_channel_button" = "Boost Channel";
 "lng_boost_group_button" = "Boost Group";
 "lng_boost_again_button" = "Boost Again";
diff --git a/Telegram/Resources/qrc/telegram/animations.qrc b/Telegram/Resources/qrc/telegram/animations.qrc
index a129237ca..ede8feb2d 100644
--- a/Telegram/Resources/qrc/telegram/animations.qrc
+++ b/Telegram/Resources/qrc/telegram/animations.qrc
@@ -14,5 +14,6 @@
     <file alias="voice_ttl_idle.tgs">../../animations/voice_ttl_idle.tgs</file>
     <file alias="voice_ttl_start.tgs">../../animations/voice_ttl_start.tgs</file>
     <file alias="palette.tgs">../../animations/palette.tgs</file>
+    <file alias="robot.tgs">../../animations/robot.tgs</file>
   </qresource>
 </RCC>
diff --git a/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp
index 1f24ecd76..f8a7ea6e8 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp
@@ -260,11 +260,11 @@ void Controller::choose(not_null<ChatData*> chat) {
 
 	const auto init = [=](not_null<ListBox*> box) {
 		auto above = object_ptr<Ui::VerticalLayout>(box);
-		Settings::AddDividerTextWithLottie(
-			above,
-			box->showFinishes(),
-			About(channel, chat),
-			u"discussion"_q);
+		Settings::AddDividerTextWithLottie(above, {
+			.lottie = u"discussion"_q,
+			.showFinished = box->showFinishes(),
+			.about = About(channel, chat),
+		});
 		if (!chat) {
 			Assert(channel->isBroadcast());
 
diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp
index 8fccba41a..3f01fe269 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp
@@ -1223,35 +1223,15 @@ void EditPeerColorBox(
 	state->index = peer->colorIndex();
 	state->emojiId = peer->backgroundEmojiId();
 	state->statusId = peer->emojiStatusId();
-
 	if (group) {
-		const auto divider = Ui::CreateChild<Ui::BoxContentDivider>(
-			box.get());
-		const auto verticalLayout = box->verticalLayout()->add(
-			object_ptr<Ui::VerticalLayout>(box.get()));
-
-		auto icon = CreateLottieIcon(
-			verticalLayout,
-			{
-				.name = u"palette"_q,
-				.sizeOverride = Size(st::settingsCloudPasswordIconSize),
-			},
-			st::peerAppearanceIconPadding);
-		box->setShowFinishedCallback([animate = std::move(icon.animate)] {
-			animate(anim::repeat::once);
+		Settings::AddDividerTextWithLottie(box->verticalLayout(), {
+			.lottie = u"palette"_q,
+			.lottieSize = st::settingsCloudPasswordIconSize,
+			.lottieMargins = st::peerAppearanceIconPadding,
+			.showFinished = box->showFinishes(),
+			.about = tr::lng_boost_group_about(Ui::Text::WithEntities),
+			.aboutMargins = st::peerAppearanceCoverLabelMargin,
 		});
-		verticalLayout->add(std::move(icon.widget));
-		verticalLayout->add(
-			object_ptr<Ui::FlatLabel>(
-				verticalLayout,
-				tr::lng_boost_group_about(),
-				st::peerAppearanceCoverLabel),
-		st::peerAppearanceCoverLabelMargin);
-
-		verticalLayout->geometryValue(
-		) | rpl::start_with_next([=](const QRect &r) {
-			divider->setGeometry(r);
-		}, divider->lifetime());
 	} else {
 		box->addRow(object_ptr<PreviewWrap>(
 			box,
diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp
index fbb9bb533..82c115774 100644
--- a/Telegram/SourceFiles/core/local_url_handlers.cpp
+++ b/Telegram/SourceFiles/core/local_url_handlers.cpp
@@ -669,6 +669,17 @@ bool ShowSearchTagsPromo(
 	return true;
 }
 
+bool ShowAboutBusinessChatbots(
+		Window::SessionController *controller,
+		const Match &match,
+		const QVariant &context) {
+	if (!controller) {
+		return false;
+	}
+	controller->showToast(u"Cool feature, yeah.."_q); AssertIsDebug();
+	return true;
+}
+
 void ExportTestChatTheme(
 		not_null<Window::SessionController*> controller,
 		not_null<const Data::CloudTheme*> theme) {
@@ -1036,7 +1047,11 @@ const std::vector<LocalUrlHandler> &InternalUrlHandlers() {
 		{
 			u"about_tags"_q,
 			ShowSearchTagsPromo
-		}
+		},
+		{
+			u"about_business_chatbots"_q,
+			ShowAboutBusinessChatbots
+		},
 	};
 	return Result;
 }
diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp
index b85703cf0..0943e3ac1 100644
--- a/Telegram/SourceFiles/history/history_inner_widget.cpp
+++ b/Telegram/SourceFiles/history/history_inner_widget.cpp
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "history/history_inner_widget.h"
 
+#include "chat_helpers/stickers_emoji_pack.h"
 #include "core/file_utilities.h"
 #include "core/click_handler_types.h"
 #include "history/history_item_helpers.h"
@@ -32,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/effects/message_sending_animation_controller.h"
 #include "ui/effects/reaction_fly_animation.h"
 #include "ui/text/text_options.h"
+#include "ui/text/text_isolated_emoji.h"
 #include "ui/boxes/report_box.h"
 #include "ui/layers/generic_box.h"
 #include "ui/controls/delete_message_context_action.h"
@@ -2239,6 +2241,17 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
 		}
 	};
 
+	if (const auto item = _dragStateItem) {
+		const auto emojiStickers = &session->emojiStickersPack();
+		if (const auto view = item->mainView()) {
+			if (const auto isolated = view->isolatedEmoji()) {
+				if (const auto sticker = emojiStickers->stickerForEmoji(isolated)) {
+					addDocumentActions(sticker.document, item);
+				}
+			}
+		}
+	}
+
 	const auto asGroup = !Element::Moused()
 		|| (Element::Moused() != Element::Hovered())
 		|| (Element::Moused()->pointState(
diff --git a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
new file mode 100644
index 000000000..34969c7d9
--- /dev/null
+++ b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
@@ -0,0 +1,201 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "settings/business/settings_chatbots.h"
+
+#include "lang/lang_keys.h"
+#include "settings/settings_common_session.h"
+#include "ui/text/text_utilities.h"
+#include "ui/widgets/fields/input_field.h"
+#include "ui/widgets/checkbox.h"
+#include "ui/wrap/slide_wrap.h"
+#include "ui/wrap/vertical_layout.h"
+#include "ui/vertical_list.h"
+#include "styles/style_layers.h"
+#include "styles/style_settings.h"
+
+namespace Settings {
+namespace {
+
+constexpr auto kAllExcept = 0;
+constexpr auto kSelectedOnly = 1;
+
+class Chatbots : public Section<Chatbots> {
+public:
+	Chatbots(
+		QWidget *parent,
+		not_null<Window::SessionController*> controller);
+
+	[[nodiscard]] rpl::producer<QString> title() override;
+
+	rpl::producer<> showFinishes() const {
+		return _showFinished.events();
+	}
+
+	const Ui::RoundRect *bottomSkipRounding() const {
+		return &_bottomSkipRounding;
+	}
+
+private:
+	void setupContent(not_null<Window::SessionController*> controller);
+
+	void showFinished() override {
+		_showFinished.fire({});
+	}
+
+	rpl::event_stream<> _showFinished;
+	Ui::RoundRect _bottomSkipRounding;
+
+};
+
+Chatbots::Chatbots(
+	QWidget *parent,
+	not_null<Window::SessionController*> controller)
+: Section(parent)
+, _bottomSkipRounding(st::boxRadius, st::boxDividerBg) {
+	setupContent(controller);
+}
+
+rpl::producer<QString> Chatbots::title() {
+	return tr::lng_chatbots_title();
+}
+
+void Chatbots::setupContent(
+		not_null<Window::SessionController*> controller) {
+	using namespace rpl::mappers;
+
+	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
+
+	struct State {
+		rpl::variable<bool> onlySelected = false;
+		rpl::variable<bool> replyAllowed = true;
+	};
+	const auto state = content->lifetime().make_state<State>();
+
+	AddDividerTextWithLottie(content, {
+		.lottie = u"robot"_q,
+		.lottieSize = st::settingsCloudPasswordIconSize,
+		.lottieMargins = st::peerAppearanceIconPadding,
+		.showFinished = showFinishes(),
+		.about = tr::lng_chatbots_about(
+			lt_link,
+			tr::lng_chatbots_about_link(
+			) | Ui::Text::ToLink(u"internal:about_business_chatbots"_q),
+			Ui::Text::WithEntities),
+		.aboutMargins = st::peerAppearanceCoverLabelMargin,
+	});
+
+	const auto username = content->add(
+		object_ptr<Ui::InputField>(
+			content,
+			st::settingsChatbotsUsername,
+			tr::lng_chatbots_placeholder()),
+		st::settingsChatbotsUsernameMargins);
+
+	Ui::AddDividerText(
+		content,
+		tr::lng_chatbots_add_about(),
+		st::peerAppearanceDividerTextMargin);
+	Ui::AddSkip(content);
+	Ui::AddSubsectionTitle(content, tr::lng_chatbots_access_title());
+
+	const auto group = std::make_shared<Ui::RadiobuttonGroup>(
+		state->onlySelected.current() ? kSelectedOnly : kAllExcept);
+	const auto everyone = content->add(
+		object_ptr<Ui::Radiobutton>(
+			content,
+			group,
+			kAllExcept,
+			tr::lng_chatbots_all_except(tr::now),
+			st::settingsChatbotsAccess),
+		st::settingsChatbotsAccessMargins);
+	const auto selected = content->add(
+		object_ptr<Ui::Radiobutton>(
+			content,
+			group,
+			kSelectedOnly,
+			tr::lng_chatbots_selected(tr::now),
+			st::settingsChatbotsAccess),
+		st::settingsChatbotsAccessMargins);
+
+	Ui::AddSkip(content, st::settingsChatbotsAccessSkip);
+	Ui::AddDivider(content);
+
+	const auto excludeWrap = content->add(
+		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
+			content,
+			object_ptr<Ui::VerticalLayout>(content))
+	)->setDuration(0);
+	const auto excludeInner = excludeWrap->entity();
+
+	Ui::AddSkip(excludeInner);
+	Ui::AddSubsectionTitle(excludeInner, tr::lng_chatbots_excluded_title());
+	const auto excludeAdd = AddButtonWithIcon(
+		excludeInner,
+		tr::lng_chatbots_exclude_button(),
+		st::settingsChatbotsAdd,
+		{ &st::settingsIconRemove, IconType::Round, &st::windowBgActive });
+
+	excludeWrap->toggleOn(state->onlySelected.value() | rpl::map(!_1));
+	excludeWrap->finishAnimating();
+
+	const auto includeWrap = content->add(
+		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
+			content,
+			object_ptr<Ui::VerticalLayout>(content))
+	)->setDuration(0);
+	const auto includeInner = includeWrap->entity();
+
+	Ui::AddSkip(includeInner);
+	Ui::AddSubsectionTitle(includeInner, tr::lng_chatbots_included_title());
+	const auto includeAdd = AddButtonWithIcon(
+		includeInner,
+		tr::lng_chatbots_include_button(),
+		st::settingsChatbotsAdd,
+		{ &st::settingsIconAdd, IconType::Round, &st::windowBgActive });
+
+	includeWrap->toggleOn(state->onlySelected.value());
+	includeWrap->finishAnimating();
+
+	group->setChangedCallback([=](int value) {
+		state->onlySelected = (value == kSelectedOnly);
+	});
+
+	Ui::AddSkip(content, st::settingsChatbotsAccessSkip);
+	Ui::AddDividerText(
+		content,
+		tr::lng_chatbots_exclude_about(),
+		st::peerAppearanceDividerTextMargin);
+
+	Ui::AddSkip(content);
+	Ui::AddSubsectionTitle(content, tr::lng_chatbots_permissions_title());
+	content->add(object_ptr<Ui::SettingsButton>(
+		content,
+		tr::lng_chatbots_reply(),
+		st::settingsButtonNoIcon
+	))->toggleOn(state->replyAllowed.value())->toggledChanges(
+	) | rpl::start_with_next([=](bool value) {
+		state->replyAllowed = value;
+	}, content->lifetime());
+	Ui::AddSkip(content);
+
+	Ui::AddDividerText(
+		content,
+		tr::lng_chatbots_reply_about(),
+		st::settingsChatbotsBottomTextMargin,
+		RectPart::Top);
+
+	Ui::ResizeFitChild(this, content);
+}
+
+} // namespace
+
+Type ChatbotsId() {
+	return Chatbots::Id();
+}
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_chatbots.h b/Telegram/SourceFiles/settings/business/settings_chatbots.h
new file mode 100644
index 000000000..06fab806c
--- /dev/null
+++ b/Telegram/SourceFiles/settings/business/settings_chatbots.h
@@ -0,0 +1,16 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "settings/settings_type.h"
+
+namespace Settings {
+
+[[nodiscard]] Type ChatbotsId();
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_manage.cpp b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_manage.cpp
index d2f8f9569..2d273895e 100644
--- a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_manage.cpp
+++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_manage.cpp
@@ -121,12 +121,12 @@ void Manage::setupContent() {
 		showOther(type);
 	};
 
-	AddDividerTextWithLottie(
-		content,
-		showFinishes(),
-		tr::lng_settings_cloud_password_manage_about1(
+	AddDividerTextWithLottie(content, {
+		.lottie = u"cloud_password/intro"_q,
+		.showFinished = showFinishes(),
+		.about = tr::lng_settings_cloud_password_manage_about1(
 			TextWithEntities::Simple),
-		u"cloud_password/intro"_q);
+	});
 
 	Ui::AddSkip(content);
 	AddButtonWithIcon(
diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style
index eb6bcb360..f4eebf4ff 100644
--- a/Telegram/SourceFiles/settings/settings.style
+++ b/Telegram/SourceFiles/settings/settings.style
@@ -589,9 +589,19 @@ peerAppearanceButton: SettingsButton(settingsButtonLight) {
 	padding: margins(60px, 8px, 22px, 8px);
 	iconLeft: 20px;
 }
-peerAppearanceCoverLabel: FlatLabel(boxDividerLabel) {
-	align: align(top);
-}
 peerAppearanceCoverLabelMargin: margins(22px, 0px, 22px, 17px);
 peerAppearanceIconPadding: margins(0px, 15px, 0px, 5px);
 peerAppearanceDividerTextMargin: margins(22px, 8px, 22px, 11px);
+
+settingsChatbotsUsername: InputField(defaultMultiSelectSearchField) {
+}
+settingsChatbotsAccess: Checkbox(defaultCheckbox) {
+	textPosition: point(18px, 2px);
+}
+settingsChatbotsUsernameMargins: margins(20px, 8px, 20px, 8px);
+settingsChatbotsAccessMargins: margins(22px, 5px, 22px, 9px);
+settingsChatbotsAccessSkip: 4px;
+settingsChatbotsBottomTextMargin: margins(22px, 8px, 22px, 3px);
+settingsChatbotsAdd: SettingsButton(settingsButton) {
+	iconLeft: 22px;
+}
diff --git a/Telegram/SourceFiles/settings/settings_business.cpp b/Telegram/SourceFiles/settings/settings_business.cpp
index 23a4d8914..fd56adb41 100644
--- a/Telegram/SourceFiles/settings/settings_business.cpp
+++ b/Telegram/SourceFiles/settings/settings_business.cpp
@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "info/settings/info_settings_widget.h" // SectionCustomTopBarData.
 #include "lang/lang_keys.h"
 #include "main/main_session.h"
+#include "settings/business/settings_chatbots.h"
 #include "settings/settings_common_session.h"
 #include "settings/settings_premium.h"
 #include "ui/effects/gradient.h"
@@ -287,6 +288,7 @@ public:
 	void setStepDataReference(std::any &data) override;
 
 	[[nodiscard]] rpl::producer<> sectionShowBack() override final;
+	[[nodiscard]] rpl::producer<Type> sectionShowOther() override;
 
 private:
 	void setupContent();
@@ -299,9 +301,10 @@ private:
 	rpl::variable<bool> _backToggles;
 	rpl::variable<Info::Wrap> _wrap;
 	Fn<void(bool)> _setPaused;
-
 	std::shared_ptr<Ui::RadiobuttonGroup> _radioGroup;
 
+	rpl::event_stream<Type> _showOther;
+
 	rpl::event_stream<> _showBack;
 	rpl::event_stream<> _showFinished;
 	rpl::variable<QString> _buttonText;
@@ -330,6 +333,10 @@ rpl::producer<> Business::sectionShowBack() {
 	return _showBack.events();
 }
 
+rpl::producer<Type> Business::sectionShowOther() {
+	return _showOther.events();
+}
+
 void Business::setStepDataReference(std::any &data) {
 	using namespace Info::Settings;
 	const auto my = std::any_cast<SectionCustomTopBarData>(&data);
@@ -347,6 +354,11 @@ void Business::setupContent() {
 	Ui::AddSkip(content, st::settingsFromFileTop);
 
 	AddBusinessSummary(content, _controller, [=](BusinessFeature feature) {
+		switch (feature) {
+		case BusinessFeature::Chatbots:
+			_showOther.fire(Settings::ChatbotsId());
+			break;
+		}
 	});
 
 	Ui::ResizeFitChild(this, content);
diff --git a/Telegram/SourceFiles/settings/settings_common.cpp b/Telegram/SourceFiles/settings/settings_common.cpp
index f9de195fc..7e4ac4180 100644
--- a/Telegram/SourceFiles/settings/settings_common.cpp
+++ b/Telegram/SourceFiles/settings/settings_common.cpp
@@ -170,39 +170,43 @@ not_null<Button*> AddButtonWithLabel(
 }
 
 void AddDividerTextWithLottie(
-		not_null<Ui::VerticalLayout*> parent,
-		rpl::producer<> showFinished,
-		rpl::producer<TextWithEntities> text,
-		const QString &lottie) {
-	const auto divider = Ui::CreateChild<Ui::BoxContentDivider>(parent.get());
-	const auto verticalLayout = parent->add(
-		object_ptr<Ui::VerticalLayout>(parent.get()));
-
+		not_null<Ui::VerticalLayout*> container,
+		DividerWithLottieDescriptor &&descriptor) {
+	const auto divider = Ui::CreateChild<Ui::BoxContentDivider>(
+		container.get());
+	const auto verticalLayout = container->add(
+		object_ptr<Ui::VerticalLayout>(container.get()));
+	const auto size = descriptor.lottieSize.value_or(
+		st::settingsFilterIconSize);
 	auto icon = CreateLottieIcon(
 		verticalLayout,
 		{
-			.name = lottie,
-			.sizeOverride = {
-				st::settingsFilterIconSize,
-				st::settingsFilterIconSize,
-			},
+			.name = descriptor.lottie,
+			.sizeOverride = { size, size },
 		},
-		st::settingsFilterIconPadding);
-	std::move(
-		showFinished
-	) | rpl::start_with_next([animate = std::move(icon.animate)] {
-		animate(anim::repeat::once);
-	}, verticalLayout->lifetime());
+		descriptor.lottieMargins.value_or(st::settingsFilterIconPadding));
+	if (descriptor.showFinished) {
+		const auto repeat = descriptor.lottieRepeat.value_or(
+			anim::repeat::once);
+		std::move(
+			descriptor.showFinished
+		) | rpl::start_with_next([animate = std::move(icon.animate), repeat] {
+			animate(repeat);
+		}, verticalLayout->lifetime());
+	}
 	verticalLayout->add(std::move(icon.widget));
 
-	verticalLayout->add(
-		object_ptr<Ui::CenterWrap<>>(
-			verticalLayout,
-			object_ptr<Ui::FlatLabel>(
+	if (descriptor.about) {
+		verticalLayout->add(
+			object_ptr<Ui::CenterWrap<>>(
 				verticalLayout,
-				std::move(text),
-				st::settingsFilterDividerLabel)),
-		st::settingsFilterDividerLabelPadding);
+				object_ptr<Ui::FlatLabel>(
+					verticalLayout,
+					std::move(descriptor.about),
+					st::settingsFilterDividerLabel)),
+			descriptor.aboutMargins.value_or(
+				st::settingsFilterDividerLabelPadding));
+	}
 
 	verticalLayout->geometryValue(
 	) | rpl::start_with_next([=](const QRect &r) {
diff --git a/Telegram/SourceFiles/settings/settings_common.h b/Telegram/SourceFiles/settings/settings_common.h
index 838edcc64..00ecf2fe2 100644
--- a/Telegram/SourceFiles/settings/settings_common.h
+++ b/Telegram/SourceFiles/settings/settings_common.h
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #pragma once
 
+#include "ui/text/text_variant.h"
 #include "ui/rp_widget.h"
 #include "ui/round_rect.h"
 #include "base/object_ptr.h"
@@ -151,11 +152,19 @@ void CreateRightLabel(
 	rpl::producer<QString> label,
 	const style::SettingsButton &st,
 	rpl::producer<QString> buttonText);
+
+struct DividerWithLottieDescriptor {
+	QString lottie;
+	std::optional<anim::repeat> lottieRepeat;
+	std::optional<int> lottieSize;
+	std::optional<QMargins> lottieMargins;
+	rpl::producer<> showFinished;
+	rpl::producer<TextWithEntities> about;
+	std::optional<QMargins> aboutMargins;
+};
 void AddDividerTextWithLottie(
-	not_null<Ui::VerticalLayout*> parent,
-	rpl::producer<> showFinished,
-	rpl::producer<TextWithEntities> text,
-	const QString &lottie);
+	not_null<Ui::VerticalLayout*> container,
+	DividerWithLottieDescriptor &&descriptor);
 
 struct LottieIcon {
 	object_ptr<Ui::RpWidget> widget;
diff --git a/Telegram/SourceFiles/ui/vertical_list.cpp b/Telegram/SourceFiles/ui/vertical_list.cpp
index b1acce232..11347aa61 100644
--- a/Telegram/SourceFiles/ui/vertical_list.cpp
+++ b/Telegram/SourceFiles/ui/vertical_list.cpp
@@ -31,24 +31,28 @@ void AddDivider(not_null<Ui::VerticalLayout*> container) {
 void AddDividerText(
 		not_null<Ui::VerticalLayout*> container,
 		rpl::producer<QString> text,
-		const style::margins &margins) {
+		const style::margins &margins,
+		RectParts parts) {
 	AddDividerText(
 		container,
 		std::move(text) | Ui::Text::ToWithEntities(),
-		margins);
+		margins,
+		parts);
 }
 
 void AddDividerText(
 		not_null<Ui::VerticalLayout*> container,
 		rpl::producer<TextWithEntities> text,
-		const style::margins &margins) {
+		const style::margins &margins,
+		RectParts parts) {
 	container->add(object_ptr<Ui::DividerLabel>(
 		container,
 		object_ptr<Ui::FlatLabel>(
 			container,
 			std::move(text),
 			st::boxDividerLabel),
-		margins));
+		margins,
+		parts));
 }
 
 not_null<Ui::FlatLabel*> AddSubsectionTitle(
diff --git a/Telegram/SourceFiles/ui/vertical_list.h b/Telegram/SourceFiles/ui/vertical_list.h
index 87deef178..7ab743bd3 100644
--- a/Telegram/SourceFiles/ui/vertical_list.h
+++ b/Telegram/SourceFiles/ui/vertical_list.h
@@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #pragma once
 
+#include "ui/rect_part.h"
+
 namespace style {
 struct FlatLabel;
 } // namespace style
@@ -26,11 +28,13 @@ void AddDivider(not_null<Ui::VerticalLayout*> container);
 void AddDividerText(
 	not_null<Ui::VerticalLayout*> container,
 	rpl::producer<QString> text,
-	const style::margins &margins = st::defaultBoxDividerLabelPadding);
+	const style::margins &margins = st::defaultBoxDividerLabelPadding,
+	RectParts parts = RectPart::Top | RectPart::Bottom);
 void AddDividerText(
 	not_null<Ui::VerticalLayout*> container,
 	rpl::producer<TextWithEntities> text,
-	const style::margins &margins = st::defaultBoxDividerLabelPadding);
+	const style::margins &margins = st::defaultBoxDividerLabelPadding,
+	RectParts parts = RectPart::Top | RectPart::Bottom);
 not_null<Ui::FlatLabel*> AddSubsectionTitle(
 	not_null<Ui::VerticalLayout*> container,
 	rpl::producer<QString> text,

From ad9107ca909c5c52b60059b5733e15549b4ff6c4 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 20 Feb 2024 16:02:02 +0400
Subject: [PATCH 040/108] Implement chatbots section editing.

---
 Telegram/CMakeLists.txt                       |   7 +
 Telegram/Resources/langs/lang.strings         |   4 +
 .../boxes/filters/edit_filter_box.cpp         | 216 +-----------------
 .../boxes/filters/edit_filter_chats_list.cpp  |  16 +-
 .../filters/edit_filter_chats_preview.cpp     | 199 ++++++++++++++++
 .../boxes/filters/edit_filter_chats_preview.h |  64 ++++++
 .../boxes/filters/edit_filter_links.cpp       |   3 +-
 .../data/business/data_business_chatbots.cpp  |  34 +++
 .../data/business/data_business_chatbots.h    |  44 ++++
 .../data/business/data_business_common.h      |  31 +++
 .../SourceFiles/data/data_chat_filters.cpp    |   5 +-
 Telegram/SourceFiles/data/data_chat_filters.h |   4 +
 Telegram/SourceFiles/data/data_session.cpp    |   4 +-
 Telegram/SourceFiles/data/data_session.h      |   5 +
 .../business/settings_business_exceptions.cpp | 145 ++++++++++++
 .../business/settings_business_exceptions.h   |  37 +++
 .../settings/business/settings_chatbots.cpp   |  84 ++++++-
 Telegram/SourceFiles/window/window.style      |   2 +
 18 files changed, 670 insertions(+), 234 deletions(-)
 create mode 100644 Telegram/SourceFiles/boxes/filters/edit_filter_chats_preview.cpp
 create mode 100644 Telegram/SourceFiles/boxes/filters/edit_filter_chats_preview.h
 create mode 100644 Telegram/SourceFiles/data/business/data_business_chatbots.cpp
 create mode 100644 Telegram/SourceFiles/data/business/data_business_chatbots.h
 create mode 100644 Telegram/SourceFiles/data/business/data_business_common.h
 create mode 100644 Telegram/SourceFiles/settings/business/settings_business_exceptions.cpp
 create mode 100644 Telegram/SourceFiles/settings/business/settings_business_exceptions.h

diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index 0afd120d0..e4a5c5eff 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -180,6 +180,8 @@ PRIVATE
     boxes/filters/edit_filter_box.h
     boxes/filters/edit_filter_chats_list.cpp
     boxes/filters/edit_filter_chats_list.h
+    boxes/filters/edit_filter_chats_preview.cpp
+    boxes/filters/edit_filter_chats_preview.h
     boxes/filters/edit_filter_links.cpp
     boxes/filters/edit_filter_links.h
     boxes/peers/add_bot_to_chat_box.cpp
@@ -446,6 +448,9 @@ PRIVATE
     core/version.h
     countries/countries_manager.cpp
     countries/countries_manager.h
+    data/business/data_business_chatbots.cpp
+    data/business/data_business_chatbots.h
+    data/business/data_business_common.h
     data/notify/data_notify_settings.cpp
     data/notify/data_notify_settings.h
     data/notify/data_peer_notify_settings.cpp
@@ -1277,6 +1282,8 @@ PRIVATE
     profile/profile_block_widget.h
     profile/profile_cover_drop_area.cpp
     profile/profile_cover_drop_area.h
+    settings/business/settings_business_exceptions.cpp
+    settings/business/settings_business_exceptions.h
     settings/business/settings_chatbots.cpp
     settings/business/settings_chatbots.h
     settings/cloud_password/settings_cloud_password_common.cpp
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 19ff138a1..73b164dee 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -2189,6 +2189,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_chatbots_reply" = "Reply to Messages";
 "lng_chatbots_reply_about" = "The bot will be able to view all new incoming messages, but not the messages that had been sent before you added the bot.";
 "lng_chatbots_remove" = "Remove Bot";
+"lng_chatbots_not_found" = "Chatbot not found";
+"lng_chatbots_add" = "Add";
 
 "lng_boost_channel_button" = "Boost Channel";
 "lng_boost_group_button" = "Boost Group";
@@ -4341,6 +4343,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_filters_type_non_contacts" = "Non-Contacts";
 "lng_filters_type_groups" = "Groups";
 "lng_filters_type_channels" = "Channels";
+"lng_filters_type_new" = "New Chats";
+"lng_filters_type_existing" = "Existing Chats";
 "lng_filters_type_bots" = "Bots";
 "lng_filters_type_no_archived" = "Archived";
 "lng_filters_type_no_muted" = "Muted";
diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp
index 37b946d63..958d01436 100644
--- a/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp
+++ b/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "boxes/filters/edit_filter_box.h"
 
 #include "boxes/filters/edit_filter_chats_list.h"
+#include "boxes/filters/edit_filter_chats_preview.h"
 #include "boxes/filters/edit_filter_links.h"
 #include "boxes/premium_limits_box.h"
 #include "chat_helpers/emoji_suggestions_widget.h"
@@ -56,60 +57,6 @@ using Flags = Data::ChatFilter::Flags;
 using ExceptionPeersRef = const base::flat_set<not_null<History*>> &;
 using ExceptionPeersGetter = ExceptionPeersRef(Data::ChatFilter::*)() const;
 
-constexpr auto kAllTypes = {
-	Flag::Contacts,
-	Flag::NonContacts,
-	Flag::Groups,
-	Flag::Channels,
-	Flag::Bots,
-	Flag::NoMuted,
-	Flag::NoRead,
-	Flag::NoArchived,
-};
-
-class FilterChatsPreview final : public Ui::RpWidget {
-public:
-	FilterChatsPreview(
-		not_null<QWidget*> parent,
-		Flags flags,
-		const base::flat_set<not_null<History*>> &peers);
-
-	[[nodiscard]] rpl::producer<Flag> flagRemoved() const;
-	[[nodiscard]] rpl::producer<not_null<History*>> peerRemoved() const;
-
-	void updateData(
-		Flags flags,
-		const base::flat_set<not_null<History*>> &peers);
-
-	int resizeGetHeight(int newWidth) override;
-
-private:
-	using Button = base::unique_qptr<Ui::IconButton>;
-	struct FlagButton {
-		Flag flag = Flag();
-		Button button;
-	};
-	struct PeerButton {
-		not_null<History*> history;
-		Ui::PeerUserpicView userpic;
-		Ui::Text::String name;
-		Button button;
-	};
-
-	void paintEvent(QPaintEvent *e) override;
-
-	void refresh();
-	void removeFlag(Flag flag);
-	void removePeer(not_null<History*> history);
-
-	std::vector<FlagButton> _removeFlag;
-	std::vector<PeerButton> _removePeer;
-
-	rpl::event_stream<Flag> _flagRemoved;
-	rpl::event_stream<not_null<History*>> _peerRemoved;
-
-};
-
 struct NameEditing {
 	not_null<Ui::InputField*> field;
 	bool custom = false;
@@ -167,167 +114,6 @@ not_null<FilterChatsPreview*> SetupChatsPreview(
 	return preview;
 }
 
-FilterChatsPreview::FilterChatsPreview(
-	not_null<QWidget*> parent,
-	Flags flags,
-	const base::flat_set<not_null<History*>> &peers)
-: RpWidget(parent) {
-	updateData(flags, peers);
-}
-
-void FilterChatsPreview::refresh() {
-	resizeToWidth(width());
-}
-
-void FilterChatsPreview::updateData(
-		Flags flags,
-		const base::flat_set<not_null<History*>> &peers) {
-	_removeFlag.clear();
-	_removePeer.clear();
-	const auto makeButton = [&](Fn<void()> handler) {
-		auto result = base::make_unique_q<Ui::IconButton>(
-			this,
-			st::windowFilterSmallRemove);
-		result->setClickedCallback(std::move(handler));
-		return result;
-	};
-	for (const auto flag : kAllTypes) {
-		if (flags & flag) {
-			_removeFlag.push_back({
-				flag,
-				makeButton([=] { removeFlag(flag); }) });
-		}
-	}
-	for (const auto &history : peers) {
-		_removePeer.push_back(PeerButton{
-			.history = history,
-			.button = makeButton([=] { removePeer(history); })
-		});
-	}
-	refresh();
-}
-
-int FilterChatsPreview::resizeGetHeight(int newWidth) {
-	const auto right = st::windowFilterSmallRemoveRight;
-	const auto add = (st::windowFilterSmallItem.height
-		- st::windowFilterSmallRemove.height) / 2;
-	auto top = 0;
-	const auto moveNextButton = [&](not_null<Ui::IconButton*> button) {
-		button->moveToRight(right, top + add, newWidth);
-		top += st::windowFilterSmallItem.height;
-	};
-	for (const auto &[flag, button] : _removeFlag) {
-		moveNextButton(button.get());
-	}
-	for (const auto &[history, userpic, name, button] : _removePeer) {
-		moveNextButton(button.get());
-	}
-	return top;
-}
-
-void FilterChatsPreview::paintEvent(QPaintEvent *e) {
-	auto p = Painter(this);
-	auto top = 0;
-	const auto &st = st::windowFilterSmallItem;
-	const auto iconLeft = st.photoPosition.x();
-	const auto iconTop = st.photoPosition.y();
-	const auto nameLeft = st.namePosition.x();
-	p.setFont(st::windowFilterSmallItem.nameStyle.font);
-	const auto nameTop = st.namePosition.y();
-	for (const auto &[flag, button] : _removeFlag) {
-		PaintFilterChatsTypeIcon(
-			p,
-			flag,
-			iconLeft,
-			top + iconTop,
-			width(),
-			st.photoSize);
-
-		p.setPen(st::contactsNameFg);
-		p.drawTextLeft(
-			nameLeft,
-			top + nameTop,
-			width(),
-			FilterChatsTypeName(flag));
-		top += st.height;
-	}
-	for (auto &[history, userpic, name, button] : _removePeer) {
-		const auto savedMessages = history->peer->isSelf();
-		const auto repliesMessages = history->peer->isRepliesChat();
-		if (savedMessages || repliesMessages) {
-			if (savedMessages) {
-				Ui::EmptyUserpic::PaintSavedMessages(
-					p,
-					iconLeft,
-					top + iconTop,
-					width(),
-					st.photoSize);
-			} else {
-				Ui::EmptyUserpic::PaintRepliesMessages(
-					p,
-					iconLeft,
-					top + iconTop,
-					width(),
-					st.photoSize);
-			}
-			p.setPen(st::contactsNameFg);
-			p.drawTextLeft(
-				nameLeft,
-				top + nameTop,
-				width(),
-				(savedMessages
-					? tr::lng_saved_messages(tr::now)
-					: tr::lng_replies_messages(tr::now)));
-		} else {
-			history->peer->paintUserpicLeft(
-				p,
-				userpic,
-				iconLeft,
-				top + iconTop,
-				width(),
-				st.photoSize);
-			p.setPen(st::contactsNameFg);
-			if (name.isEmpty()) {
-				name.setText(
-					st::msgNameStyle,
-					history->peer->name(),
-					Ui::NameTextOptions());
-			}
-			name.drawLeftElided(
-				p,
-				nameLeft,
-				top + nameTop,
-				button->x() - nameLeft,
-				width());
-		}
-		top += st.height;
-	}
-}
-
-void FilterChatsPreview::removeFlag(Flag flag) {
-	const auto i = ranges::find(_removeFlag, flag, &FlagButton::flag);
-	Assert(i != end(_removeFlag));
-	_removeFlag.erase(i);
-	refresh();
-	_flagRemoved.fire_copy(flag);
-}
-
-void FilterChatsPreview::removePeer(not_null<History*> history) {
-	const auto i = ranges::find(_removePeer, history, &PeerButton::history);
-	Assert(i != end(_removePeer));
-	_removePeer.erase(i);
-	refresh();
-	_peerRemoved.fire_copy(history);
-}
-
-rpl::producer<Flag> FilterChatsPreview::flagRemoved() const {
-	return _flagRemoved.events();
-}
-
-rpl::producer<not_null<History*>> FilterChatsPreview::peerRemoved() const {
-	return _peerRemoved.events();
-}
-
 void EditExceptions(
 		not_null<Window::SessionController*> window,
 		not_null<QObject*> context,
diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp
index 989a9867b..25463f1e2 100644
--- a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp
+++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp
@@ -28,6 +28,8 @@ using Flag = Data::ChatFilter::Flag;
 using Flags = Data::ChatFilter::Flags;
 
 constexpr auto kAllTypes = {
+	Flag::NewChats,
+	Flag::ExistingChats,
 	Flag::Contacts,
 	Flag::NonContacts,
 	Flag::Groups,
@@ -119,7 +121,7 @@ PaintRoundImageCallback TypeRow::generatePaintUserpicCallback(
 }
 
 Flag TypeRow::flag() const {
-	return static_cast<Flag>(id() & 0xFF);
+	return static_cast<Flag>(id() & 0xFFFF);
 }
 
 ExceptionRow::ExceptionRow(not_null<History*> history) : Row(history) {
@@ -219,6 +221,8 @@ auto TypeController::rowSelectionChanges() const
 
 [[nodiscard]] QString FilterChatsTypeName(Flag flag) {
 	switch (flag) {
+	case Flag::NewChats: return tr::lng_filters_type_new(tr::now);
+	case Flag::ExistingChats: return tr::lng_filters_type_existing(tr::now);
 	case Flag::Contacts: return tr::lng_filters_type_contacts(tr::now);
 	case Flag::NonContacts:
 		return tr::lng_filters_type_non_contacts(tr::now);
@@ -241,6 +245,8 @@ void PaintFilterChatsTypeIcon(
 		int size) {
 	const auto &color1 = [&]() -> const style::color& {
 		switch (flag) {
+		case Flag::NewChats: return st::historyPeer5UserpicBg;
+		case Flag::ExistingChats: return st::historyPeer8UserpicBg;
 		case Flag::Contacts: return st::historyPeer4UserpicBg;
 		case Flag::NonContacts: return st::historyPeer7UserpicBg;
 		case Flag::Groups: return st::historyPeer2UserpicBg;
@@ -254,6 +260,8 @@ void PaintFilterChatsTypeIcon(
 	}();
 	const auto &color2 = [&]() -> const style::color& {
 		switch (flag) {
+		case Flag::NewChats: return st::historyPeer5UserpicBg2;
+		case Flag::ExistingChats: return st::historyPeer8UserpicBg2;
 		case Flag::Contacts: return st::historyPeer4UserpicBg2;
 		case Flag::NonContacts: return st::historyPeer7UserpicBg2;
 		case Flag::Groups: return st::historyPeer2UserpicBg2;
@@ -267,6 +275,8 @@ void PaintFilterChatsTypeIcon(
 	}();
 	const auto &icon = [&]() -> const style::icon& {
 		switch (flag) {
+		case Flag::NewChats: return st::windowFilterTypeNewChats;
+		case Flag::ExistingChats: return st::windowFilterTypeExistingChats;
 		case Flag::Contacts: return st::windowFilterTypeContacts;
 		case Flag::NonContacts: return st::windowFilterTypeNonContacts;
 		case Flag::Groups: return st::windowFilterTypeGroups;
@@ -469,6 +479,10 @@ object_ptr<Ui::RpWidget> EditFilterChatsListController::prepareTypesList() {
 
 auto EditFilterChatsListController::createRow(not_null<History*> history)
 -> std::unique_ptr<Row> {
+	const auto business = _options & (Flag::NewChats | Flag::ExistingChats);
+	if (business && (history->peer->isSelf() || !history->peer->isUser())) {
+		return nullptr;
+	}
 	return history->inChatList()
 		? std::make_unique<ExceptionRow>(history)
 		: nullptr;
diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_preview.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_preview.cpp
new file mode 100644
index 000000000..3e2efb87e
--- /dev/null
+++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_preview.cpp
@@ -0,0 +1,199 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "boxes/filters/edit_filter_chats_preview.h"
+
+#include "boxes/filters/edit_filter_chats_list.h"
+#include "data/data_peer.h"
+#include "history/history.h"
+#include "lang/lang_keys.h"
+#include "ui/text/text_options.h"
+#include "ui/widgets/buttons.h"
+#include "ui/painter.h"
+#include "styles/style_chat.h"
+#include "styles/style_window.h"
+
+namespace {
+
+using Flag = Data::ChatFilter::Flag;
+
+constexpr auto kAllTypes = {
+	Flag::NewChats,
+	Flag::ExistingChats,
+	Flag::Contacts,
+	Flag::NonContacts,
+	Flag::Groups,
+	Flag::Channels,
+	Flag::Bots,
+	Flag::NoMuted,
+	Flag::NoRead,
+	Flag::NoArchived,
+};
+
+} // namespace
+
+FilterChatsPreview::FilterChatsPreview(
+	not_null<QWidget*> parent,
+	Flags flags,
+	const base::flat_set<not_null<History*>> &peers)
+: RpWidget(parent) {
+	updateData(flags, peers);
+}
+
+void FilterChatsPreview::refresh() {
+	resizeToWidth(width());
+}
+
+void FilterChatsPreview::updateData(
+		Flags flags,
+		const base::flat_set<not_null<History*>> &peers) {
+	_removeFlag.clear();
+	_removePeer.clear();
+	const auto makeButton = [&](Fn<void()> handler) {
+		auto result = base::make_unique_q<Ui::IconButton>(
+			this,
+			st::windowFilterSmallRemove);
+		result->setClickedCallback(std::move(handler));
+		result->show();
+		return result;
+	};
+	for (const auto flag : kAllTypes) {
+		if (flags & flag) {
+			_removeFlag.push_back({
+				flag,
+				makeButton([=] { removeFlag(flag); }) });
+		}
+	}
+	for (const auto &history : peers) {
+		_removePeer.push_back(PeerButton{
+			.history = history,
+			.button = makeButton([=] { removePeer(history); })
+		});
+	}
+	refresh();
+}
+
+int FilterChatsPreview::resizeGetHeight(int newWidth) {
+	const auto right = st::windowFilterSmallRemoveRight;
+	const auto add = (st::windowFilterSmallItem.height
+		- st::windowFilterSmallRemove.height) / 2;
+	auto top = 0;
+	const auto moveNextButton = [&](not_null<Ui::IconButton*> button) {
+		button->moveToRight(right, top + add, newWidth);
+		top += st::windowFilterSmallItem.height;
+	};
+	for (const auto &[flag, button] : _removeFlag) {
+		moveNextButton(button.get());
+	}
+	for (const auto &[history, userpic, name, button] : _removePeer) {
+		moveNextButton(button.get());
+	}
+	return top;
+}
+
+void FilterChatsPreview::paintEvent(QPaintEvent *e) {
+	auto p = Painter(this);
+	auto top = 0;
+	const auto &st = st::windowFilterSmallItem;
+	const auto iconLeft = st.photoPosition.x();
+	const auto iconTop = st.photoPosition.y();
+	const auto nameLeft = st.namePosition.x();
+	p.setFont(st::windowFilterSmallItem.nameStyle.font);
+	const auto nameTop = st.namePosition.y();
+	for (const auto &[flag, button] : _removeFlag) {
+		PaintFilterChatsTypeIcon(
+			p,
+			flag,
+			iconLeft,
+			top + iconTop,
+			width(),
+			st.photoSize);
+
+		p.setPen(st::contactsNameFg);
+		p.drawTextLeft(
+			nameLeft,
+			top + nameTop,
+			width(),
+			FilterChatsTypeName(flag));
+		top += st.height;
+	}
+	for (auto &[history, userpic, name, button] : _removePeer) {
+		const auto savedMessages = history->peer->isSelf();
+		const auto repliesMessages = history->peer->isRepliesChat();
+		if (savedMessages || repliesMessages) {
+			if (savedMessages) {
+				Ui::EmptyUserpic::PaintSavedMessages(
+					p,
+					iconLeft,
+					top + iconTop,
+					width(),
+					st.photoSize);
+			} else {
+				Ui::EmptyUserpic::PaintRepliesMessages(
+					p,
+					iconLeft,
+					top + iconTop,
+					width(),
+					st.photoSize);
+			}
+			p.setPen(st::contactsNameFg);
+			p.drawTextLeft(
+				nameLeft,
+				top + nameTop,
+				width(),
+				(savedMessages
+					? tr::lng_saved_messages(tr::now)
+					: tr::lng_replies_messages(tr::now)));
+		} else {
+			history->peer->paintUserpicLeft(
+				p,
+				userpic,
+				iconLeft,
+				top + iconTop,
+				width(),
+				st.photoSize);
+			p.setPen(st::contactsNameFg);
+			if (name.isEmpty()) {
+				name.setText(
+					st::msgNameStyle,
+					history->peer->name(),
+					Ui::NameTextOptions());
+			}
+			name.drawLeftElided(
+				p,
+				nameLeft,
+				top + nameTop,
+				button->x() - nameLeft,
+				width());
+		}
+		top += st.height;
+	}
+}
+
+void FilterChatsPreview::removeFlag(Flag flag) {
+	const auto i = ranges::find(_removeFlag, flag, &FlagButton::flag);
+	Assert(i != end(_removeFlag));
+	_removeFlag.erase(i);
+	refresh();
+	_flagRemoved.fire_copy(flag);
+}
+
+void FilterChatsPreview::removePeer(not_null<History*> history) {
+	const auto i = ranges::find(_removePeer, history, &PeerButton::history);
+	Assert(i != end(_removePeer));
+	_removePeer.erase(i);
+	refresh();
+	_peerRemoved.fire_copy(history);
+}
+
+rpl::producer<Flag> FilterChatsPreview::flagRemoved() const {
+	return _flagRemoved.events();
+}
+
+rpl::producer<not_null<History*>> FilterChatsPreview::peerRemoved() const {
+	return _peerRemoved.events();
+}
diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_preview.h b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_preview.h
new file mode 100644
index 000000000..c795bc493
--- /dev/null
+++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_preview.h
@@ -0,0 +1,64 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "data/data_chat_filters.h"
+#include "ui/rp_widget.h"
+#include "ui/userpic_view.h"
+
+class History;
+
+namespace Ui {
+class IconButton;
+} // namespace Ui
+
+class FilterChatsPreview final : public Ui::RpWidget {
+public:
+	using Flag = Data::ChatFilter::Flag;
+	using Flags = Data::ChatFilter::Flags;
+
+	FilterChatsPreview(
+		not_null<QWidget*> parent,
+		Flags flags,
+		const base::flat_set<not_null<History*>> &peers);
+
+	[[nodiscard]] rpl::producer<Flag> flagRemoved() const;
+	[[nodiscard]] rpl::producer<not_null<History*>> peerRemoved() const;
+
+	void updateData(
+		Flags flags,
+		const base::flat_set<not_null<History*>> &peers);
+
+	int resizeGetHeight(int newWidth) override;
+
+private:
+	using Button = base::unique_qptr<Ui::IconButton>;
+	struct FlagButton {
+		Flag flag = Flag();
+		Button button;
+	};
+	struct PeerButton {
+		not_null<History*> history;
+		Ui::PeerUserpicView userpic;
+		Ui::Text::String name;
+		Button button;
+	};
+
+	void paintEvent(QPaintEvent *e) override;
+
+	void refresh();
+	void removeFlag(Flag flag);
+	void removePeer(not_null<History*> history);
+
+	std::vector<FlagButton> _removeFlag;
+	std::vector<PeerButton> _removePeer;
+
+	rpl::event_stream<Flag> _flagRemoved;
+	rpl::event_stream<not_null<History*>> _peerRemoved;
+
+};
diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp
index d60cc030d..5640c11b5 100644
--- a/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp
+++ b/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp
@@ -982,8 +982,7 @@ bool GoodForExportFilterLink(
 		not_null<Window::SessionController*> window,
 		const Data::ChatFilter &filter) {
 	using Flag = Data::ChatFilter::Flag;
-	const auto listflags = Flag::Chatlist | Flag::HasMyLinks;
-	if (!filter.never().empty() || (filter.flags() & ~listflags)) {
+	if (!filter.never().empty() || (filter.flags() & Flag::RulesMask)) {
 		window->showToast(tr::lng_filters_link_cant(tr::now));
 		return false;
 	}
diff --git a/Telegram/SourceFiles/data/business/data_business_chatbots.cpp b/Telegram/SourceFiles/data/business/data_business_chatbots.cpp
new file mode 100644
index 000000000..26dd21687
--- /dev/null
+++ b/Telegram/SourceFiles/data/business/data_business_chatbots.cpp
@@ -0,0 +1,34 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "data/business/data_business_chatbots.h"
+
+namespace Data {
+
+Chatbots::Chatbots(not_null<Session*> session)
+: _session(session) {
+}
+
+Chatbots::~Chatbots() = default;
+
+const ChatbotsSettings &Chatbots::current() const {
+	return _settings.current();
+}
+
+rpl::producer<ChatbotsSettings> Chatbots::changes() const {
+	return _settings.changes();
+}
+
+rpl::producer<ChatbotsSettings> Chatbots::value() const {
+	return _settings.value();
+}
+
+void Chatbots::save(ChatbotsSettings settings) {
+	_settings = settings;
+}
+
+} // namespace Data
diff --git a/Telegram/SourceFiles/data/business/data_business_chatbots.h b/Telegram/SourceFiles/data/business/data_business_chatbots.h
new file mode 100644
index 000000000..adfe998d2
--- /dev/null
+++ b/Telegram/SourceFiles/data/business/data_business_chatbots.h
@@ -0,0 +1,44 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "data/business/data_business_common.h"
+
+class UserData;
+
+namespace Data {
+
+class Session;
+
+struct ChatbotsSettings {
+	UserData *bot = nullptr;
+	BusinessExceptions allowed;
+	BusinessExceptions disallowed;
+	bool repliesAllowed = false;
+	bool onlySelected = false;
+};
+
+class Chatbots final {
+public:
+	explicit Chatbots(not_null<Session*> session);
+	~Chatbots();
+
+	[[nodiscard]] const ChatbotsSettings &current() const;
+	[[nodiscard]] rpl::producer<ChatbotsSettings> changes() const;
+	[[nodiscard]] rpl::producer<ChatbotsSettings> value() const;
+
+	void save(ChatbotsSettings settings);
+
+private:
+	const not_null<Session*> _session;
+
+	rpl::variable<ChatbotsSettings> _settings;
+
+};
+
+} // namespace Data
diff --git a/Telegram/SourceFiles/data/business/data_business_common.h b/Telegram/SourceFiles/data/business/data_business_common.h
new file mode 100644
index 000000000..aed51fdf9
--- /dev/null
+++ b/Telegram/SourceFiles/data/business/data_business_common.h
@@ -0,0 +1,31 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "base/flags.h"
+
+class UserData;
+
+namespace Data {
+
+enum class BusinessChatType {
+	NewChats = (1 << 0),
+	ExistingChats = (1 << 1),
+	Contacts = (1 << 2),
+	NonContacts = (1 << 3),
+};
+inline constexpr bool is_flag_type(BusinessChatType) { return true; }
+
+using BusinessChatTypes = base::flags<BusinessChatType>;
+
+struct BusinessExceptions {
+	BusinessChatTypes types;
+	std::vector<not_null<UserData*>> list;
+};
+
+} // namespace Data
diff --git a/Telegram/SourceFiles/data/data_chat_filters.cpp b/Telegram/SourceFiles/data/data_chat_filters.cpp
index 7e824cc33..237bf5c29 100644
--- a/Telegram/SourceFiles/data/data_chat_filters.cpp
+++ b/Telegram/SourceFiles/data/data_chat_filters.cpp
@@ -163,6 +163,7 @@ ChatFilter ChatFilter::withTitle(const QString &title) const {
 
 ChatFilter ChatFilter::withChatlist(bool chatlist, bool hasMyLinks) const {
 	auto result = *this;
+	result._flags &= Flag::RulesMask;
 	if (chatlist) {
 		result._flags |= Flag::Chatlist;
 		if (hasMyLinks) {
@@ -170,8 +171,6 @@ ChatFilter ChatFilter::withChatlist(bool chatlist, bool hasMyLinks) const {
 		} else {
 			result._flags &= ~Flag::HasMyLinks;
 		}
-	} else {
-		result._flags &= ~(Flag::Chatlist | Flag::HasMyLinks);
 	}
 	return result;
 }
@@ -593,7 +592,7 @@ bool ChatFilters::applyChange(ChatFilter &filter, ChatFilter &&updated) {
 
 	const auto id = filter.id();
 	const auto exceptionsChanged = filter.always() != updated.always();
-	const auto rulesMask = ~(Flag::Chatlist | Flag::HasMyLinks);
+	const auto rulesMask = Flag() | Flag::RulesMask;
 	const auto rulesChanged = exceptionsChanged
 		|| ((filter.flags() & rulesMask) != (updated.flags() & rulesMask))
 		|| (filter.never() != updated.never());
diff --git a/Telegram/SourceFiles/data/data_chat_filters.h b/Telegram/SourceFiles/data/data_chat_filters.h
index 987d55ebe..7b5a96476 100644
--- a/Telegram/SourceFiles/data/data_chat_filters.h
+++ b/Telegram/SourceFiles/data/data_chat_filters.h
@@ -36,9 +36,13 @@ public:
 		NoMuted     = (1 << 5),
 		NoRead      = (1 << 6),
 		NoArchived  = (1 << 7),
+		RulesMask   = ((1 << 8) - 1),
 
 		Chatlist    = (1 << 8),
 		HasMyLinks  = (1 << 9),
+
+		NewChats      = (1 << 10), // Telegram Business exceptions.
+		ExistingChats = (1 << 11),
 	};
 	friend constexpr inline bool is_flag_type(Flag) { return true; };
 	using Flags = base::flags<Flag>;
diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp
index e7ec13242..4efd8fa8b 100644
--- a/Telegram/SourceFiles/data/data_session.cpp
+++ b/Telegram/SourceFiles/data/data_session.cpp
@@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "boxes/abstract_box.h"
 #include "passport/passport_form_controller.h"
 #include "lang/lang_keys.h" // tr::lng_deleted(tr::now) in user name
+#include "data/business/data_business_chatbots.h"
 #include "data/stickers/data_stickers.h"
 #include "data/notify/data_notify_settings.h"
 #include "data/data_bot_app.h"
@@ -268,7 +269,8 @@ Session::Session(not_null<Main::Session*> session)
 , _notifySettings(std::make_unique<NotifySettings>(this))
 , _customEmojiManager(std::make_unique<CustomEmojiManager>(this))
 , _stories(std::make_unique<Stories>(this))
-, _savedMessages(std::make_unique<SavedMessages>(this)) {
+, _savedMessages(std::make_unique<SavedMessages>(this))
+, _chatbots(std::make_unique<Chatbots>(this)) {
 	_cache->open(_session->local().cacheKey());
 	_bigFileCache->open(_session->local().cacheBigFileKey());
 
diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h
index d391d1d31..4fc7b1db1 100644
--- a/Telegram/SourceFiles/data/data_session.h
+++ b/Telegram/SourceFiles/data/data_session.h
@@ -62,6 +62,7 @@ class NotifySettings;
 class CustomEmojiManager;
 class Stories;
 class SavedMessages;
+class Chatbots;
 struct ReactionId;
 
 struct RepliesReadTillUpdate {
@@ -142,6 +143,9 @@ public:
 	[[nodiscard]] SavedMessages &savedMessages() const {
 		return *_savedMessages;
 	}
+	[[nodiscard]] Chatbots &chatbots() const {
+		return *_chatbots;
+	}
 
 	[[nodiscard]] MsgId nextNonHistoryEntryId() {
 		return ++_nonHistoryEntryId;
@@ -1065,6 +1069,7 @@ private:
 	const std::unique_ptr<CustomEmojiManager> _customEmojiManager;
 	const std::unique_ptr<Stories> _stories;
 	const std::unique_ptr<SavedMessages> _savedMessages;
+	const std::unique_ptr<Chatbots> _chatbots;
 
 	MsgId _nonHistoryEntryId = ServerMaxMsgId.bare + ScheduledMsgIdsRange;
 
diff --git a/Telegram/SourceFiles/settings/business/settings_business_exceptions.cpp b/Telegram/SourceFiles/settings/business/settings_business_exceptions.cpp
new file mode 100644
index 000000000..568aca80f
--- /dev/null
+++ b/Telegram/SourceFiles/settings/business/settings_business_exceptions.cpp
@@ -0,0 +1,145 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "settings/business/settings_business_exceptions.h"
+
+#include "boxes/filters/edit_filter_chats_list.h"
+#include "boxes/filters/edit_filter_chats_preview.h"
+#include "data/data_session.h"
+#include "data/data_user.h"
+#include "history/history.h"
+#include "lang/lang_keys.h"
+#include "ui/wrap/vertical_layout.h"
+#include "window/window_session_controller.h"
+
+namespace Settings {
+namespace {
+
+using Flag = Data::ChatFilter::Flag;
+using Flags = Data::ChatFilter::Flags;
+
+[[nodiscard]] Flags TypesToFlags(Data::BusinessChatTypes types) {
+	using Type = Data::BusinessChatType;
+	return ((types & Type::Contacts) ? Flag::Contacts : Flag())
+		| ((types & Type::NonContacts) ? Flag::NonContacts : Flag())
+		| ((types & Type::NewChats) ? Flag::NewChats : Flag())
+		| ((types & Type::ExistingChats) ? Flag::ExistingChats : Flag());
+}
+
+[[nodiscard]] Data::BusinessChatTypes FlagsToTypes(Flags flags) {
+	using Type = Data::BusinessChatType;
+	return ((flags & Flag::Contacts) ? Type::Contacts : Type())
+		| ((flags & Flag::NonContacts) ? Type::NonContacts : Type())
+		| ((flags & Flag::NewChats) ? Type::NewChats : Type())
+		| ((flags & Flag::ExistingChats) ? Type::ExistingChats : Type());
+}
+
+} // namespace
+
+void EditBusinessExceptions(
+		not_null<Window::SessionController*> window,
+		BusinessExceptionsDescriptor &&descriptor) {
+	const auto session = &window->session();
+	const auto options = Flag::ExistingChats
+		| Flag::NewChats
+		| Flag::Contacts
+		| Flag::NonContacts;
+	auto &&peers = descriptor.current.list | ranges::views::transform([=](
+			not_null<UserData*> user) {
+		return user->owner().history(user);
+	});
+	auto controller = std::make_unique<EditFilterChatsListController>(
+		session,
+		(descriptor.allow
+			? tr::lng_filters_include_title()
+			: tr::lng_filters_exclude_title()),
+		options,
+		TypesToFlags(descriptor.current.types) & options,
+		base::flat_set<not_null<History*>>(begin(peers), end(peers)),
+		[=](int count) {
+			return nullptr; AssertIsDebug();
+		});
+	const auto rawController = controller.get();
+	const auto save = descriptor.save;
+	auto initBox = [=](not_null<PeerListBox*> box) {
+		box->setCloseByOutsideClick(false);
+		box->addButton(tr::lng_settings_save(), crl::guard(box, [=] {
+			const auto peers = box->collectSelectedRows();
+			auto &&users = ranges::views::all(
+				peers
+			) | ranges::views::transform([=](not_null<PeerData*> peer) {
+				return not_null(peer->asUser());
+			}) | ranges::to_vector;
+			save(Data::BusinessExceptions{
+				.types = FlagsToTypes(rawController->chosenOptions()),
+				.list = std::move(users),
+			});
+			box->closeBox();
+		}));
+		box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
+	};
+	window->show(
+		Box<PeerListBox>(std::move(controller), std::move(initBox)));
+}
+
+not_null<FilterChatsPreview*> SetupBusinessExceptionsPreview(
+		not_null<Ui::VerticalLayout*> content,
+		not_null<rpl::variable<Data::BusinessExceptions>*> data) {
+	const auto rules = data->current();
+
+	const auto locked = std::make_shared<bool>();
+	auto &&peers = data->current().list | ranges::views::transform([=](
+			not_null<UserData*> user) {
+		return user->owner().history(user);
+	});
+	const auto preview = content->add(object_ptr<FilterChatsPreview>(
+		content,
+		TypesToFlags(data->current().types),
+		base::flat_set<not_null<History*>>(begin(peers), end(peers))));
+
+	preview->flagRemoved(
+	) | rpl::start_with_next([=](Flag flag) {
+		*locked = true;
+		*data = Data::BusinessExceptions{
+			data->current().types & ~FlagsToTypes(flag),
+			data->current().list
+		};
+		*locked = false;
+	}, preview->lifetime());
+
+	preview->peerRemoved(
+	) | rpl::start_with_next([=](not_null<History*> history) {
+		auto list = data->current().list;
+		list.erase(
+			ranges::remove(list, not_null(history->peer->asUser())),
+			end(list));
+
+		*locked = true;
+		*data = Data::BusinessExceptions{
+			data->current().types,
+			std::move(list)
+		};
+		*locked = false;
+	}, preview->lifetime());
+
+	data->changes(
+	) | rpl::filter([=] {
+		return !*locked;
+	}) | rpl::start_with_next([=](const Data::BusinessExceptions &rules) {
+		auto &&peers = rules.list | ranges::views::transform([=](
+				not_null<UserData*> user) {
+			return user->owner().history(user);
+		});
+		preview->updateData(
+			TypesToFlags(rules.types),
+			base::flat_set<not_null<History*>>(begin(peers), end(peers)));
+	}, preview->lifetime());
+
+	return preview;
+}
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_business_exceptions.h b/Telegram/SourceFiles/settings/business/settings_business_exceptions.h
new file mode 100644
index 000000000..e60f1a01b
--- /dev/null
+++ b/Telegram/SourceFiles/settings/business/settings_business_exceptions.h
@@ -0,0 +1,37 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "data/business/data_business_common.h"
+
+class FilterChatsPreview;
+
+namespace Ui {
+class VerticalLayout;
+} // namespace Ui
+
+namespace Window {
+class SessionController;
+} // namespace Window
+
+namespace Settings {
+
+struct BusinessExceptionsDescriptor {
+	Data::BusinessExceptions current;
+	Fn<void(const Data::BusinessExceptions&)> save;
+	bool allow = false;
+};
+void EditBusinessExceptions(
+	not_null<Window::SessionController*> window,
+	BusinessExceptionsDescriptor &&descriptor);
+
+not_null<FilterChatsPreview*> SetupBusinessExceptionsPreview(
+	not_null<Ui::VerticalLayout*> content,
+	not_null<rpl::variable<Data::BusinessExceptions>*> data);
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
index 34969c7d9..d358b5112 100644
--- a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
@@ -7,7 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "settings/business/settings_chatbots.h"
 
+#include "core/application.h"
+#include "data/business/data_business_chatbots.h"
+#include "data/data_session.h"
+#include "data/data_user.h"
 #include "lang/lang_keys.h"
+#include "main/main_session.h"
+#include "settings/business/settings_business_exceptions.h"
 #include "settings/settings_common_session.h"
 #include "ui/text/text_utilities.h"
 #include "ui/widgets/fields/input_field.h"
@@ -15,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/wrap/slide_wrap.h"
 #include "ui/wrap/vertical_layout.h"
 #include "ui/vertical_list.h"
+#include "window/window_session_controller.h"
 #include "styles/style_layers.h"
 #include "styles/style_settings.h"
 
@@ -29,6 +36,7 @@ public:
 	Chatbots(
 		QWidget *parent,
 		not_null<Window::SessionController*> controller);
+	~Chatbots();
 
 	[[nodiscard]] rpl::producer<QString> title() override;
 
@@ -42,24 +50,41 @@ public:
 
 private:
 	void setupContent(not_null<Window::SessionController*> controller);
+	void save();
 
 	void showFinished() override {
 		_showFinished.fire({});
 	}
 
+	const not_null<Window::SessionController*> _controller;
+	const not_null<Main::Session*> _session;
+
 	rpl::event_stream<> _showFinished;
 	Ui::RoundRect _bottomSkipRounding;
 
+	rpl::variable<bool> _onlySelected = false;
+	rpl::variable<bool> _repliesAllowed = true;
+	rpl::variable<Data::BusinessExceptions> _allowed;
+	rpl::variable<Data::BusinessExceptions> _disallowed;
+
 };
 
 Chatbots::Chatbots(
 	QWidget *parent,
 	not_null<Window::SessionController*> controller)
 : Section(parent)
+, _controller(controller)
+, _session(&controller->session())
 , _bottomSkipRounding(st::boxRadius, st::boxDividerBg) {
 	setupContent(controller);
 }
 
+Chatbots::~Chatbots() {
+	if (!Core::Quitting()) {
+		save();
+	}
+}
+
 rpl::producer<QString> Chatbots::title() {
 	return tr::lng_chatbots_title();
 }
@@ -69,12 +94,12 @@ void Chatbots::setupContent(
 	using namespace rpl::mappers;
 
 	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
+	const auto current = controller->session().data().chatbots().current();
 
-	struct State {
-		rpl::variable<bool> onlySelected = false;
-		rpl::variable<bool> replyAllowed = true;
-	};
-	const auto state = content->lifetime().make_state<State>();
+	_onlySelected = current.onlySelected;
+	_repliesAllowed = current.repliesAllowed;
+	_allowed = current.allowed;
+	_disallowed = current.disallowed;
 
 	AddDividerTextWithLottie(content, {
 		.lottie = u"robot"_q,
@@ -93,7 +118,11 @@ void Chatbots::setupContent(
 		object_ptr<Ui::InputField>(
 			content,
 			st::settingsChatbotsUsername,
-			tr::lng_chatbots_placeholder()),
+			tr::lng_chatbots_placeholder(),
+			(current.bot
+				? current.bot->session().createInternalLink(
+					current.bot->username())
+				: QString())),
 		st::settingsChatbotsUsernameMargins);
 
 	Ui::AddDividerText(
@@ -104,7 +133,7 @@ void Chatbots::setupContent(
 	Ui::AddSubsectionTitle(content, tr::lng_chatbots_access_title());
 
 	const auto group = std::make_shared<Ui::RadiobuttonGroup>(
-		state->onlySelected.current() ? kSelectedOnly : kAllExcept);
+		_onlySelected.current() ? kSelectedOnly : kAllExcept);
 	const auto everyone = content->add(
 		object_ptr<Ui::Radiobutton>(
 			content,
@@ -139,8 +168,18 @@ void Chatbots::setupContent(
 		tr::lng_chatbots_exclude_button(),
 		st::settingsChatbotsAdd,
 		{ &st::settingsIconRemove, IconType::Round, &st::windowBgActive });
+	excludeAdd->setClickedCallback([=] {
+		EditBusinessExceptions(_controller, {
+			.current = _disallowed.current(),
+			.save = crl::guard(this, [=](Data::BusinessExceptions value) {
+				_disallowed = std::move(value);
+			}),
+			.allow = false,
+		});
+	});
+	SetupBusinessExceptionsPreview(excludeInner, &_disallowed);
 
-	excludeWrap->toggleOn(state->onlySelected.value() | rpl::map(!_1));
+	excludeWrap->toggleOn(_onlySelected.value() | rpl::map(!_1));
 	excludeWrap->finishAnimating();
 
 	const auto includeWrap = content->add(
@@ -157,12 +196,22 @@ void Chatbots::setupContent(
 		tr::lng_chatbots_include_button(),
 		st::settingsChatbotsAdd,
 		{ &st::settingsIconAdd, IconType::Round, &st::windowBgActive });
+	includeAdd->setClickedCallback([=] {
+		EditBusinessExceptions(_controller, {
+			.current = _allowed.current(),
+			.save = crl::guard(this, [=](Data::BusinessExceptions value) {
+				_allowed = std::move(value);
+			}),
+			.allow = true,
+		});
+	});
+	SetupBusinessExceptionsPreview(includeInner, &_allowed);
 
-	includeWrap->toggleOn(state->onlySelected.value());
+	includeWrap->toggleOn(_onlySelected.value());
 	includeWrap->finishAnimating();
 
 	group->setChangedCallback([=](int value) {
-		state->onlySelected = (value == kSelectedOnly);
+		_onlySelected = (value == kSelectedOnly);
 	});
 
 	Ui::AddSkip(content, st::settingsChatbotsAccessSkip);
@@ -177,9 +226,9 @@ void Chatbots::setupContent(
 		content,
 		tr::lng_chatbots_reply(),
 		st::settingsButtonNoIcon
-	))->toggleOn(state->replyAllowed.value())->toggledChanges(
+	))->toggleOn(_repliesAllowed.value())->toggledChanges(
 	) | rpl::start_with_next([=](bool value) {
-		state->replyAllowed = value;
+		_repliesAllowed = value;
 	}, content->lifetime());
 	Ui::AddSkip(content);
 
@@ -192,6 +241,17 @@ void Chatbots::setupContent(
 	Ui::ResizeFitChild(this, content);
 }
 
+void Chatbots::save() {
+	const auto settings = Data::ChatbotsSettings{
+		.bot = nullptr,
+		.allowed = _allowed.current(),
+		.disallowed = _disallowed.current(),
+		.repliesAllowed = _repliesAllowed.current(),
+		.onlySelected = _onlySelected.current(),
+	};
+	_session->data().chatbots().save(settings);
+}
+
 } // namespace
 
 Type ChatbotsId() {
diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style
index 38621c69b..645c78285 100644
--- a/Telegram/SourceFiles/window/window.style
+++ b/Telegram/SourceFiles/window/window.style
@@ -303,6 +303,8 @@ windowFilterTypeBots: icon {{ "folders/folders_type_bots", historyPeerUserpicFg
 windowFilterTypeNoMuted: icon {{ "folders/folders_type_muted", historyPeerUserpicFg }};
 windowFilterTypeNoArchived: icon {{ "folders/folders_type_archived", historyPeerUserpicFg }};
 windowFilterTypeNoRead: icon {{ "folders/folders_type_read", historyPeerUserpicFg }};
+windowFilterTypeNewChats: icon {{ "folders/folders_unread", historyPeerUserpicFg }};
+windowFilterTypeExistingChats: windowFilterTypeNoRead;
 windowFilterChatsSectionSubtitleHeight: 28px;
 windowFilterChatsSectionSubtitle: FlatLabel(defaultFlatLabel) {
 	style: TextStyle(defaultTextStyle) {

From 0af131f144adc4421e7eff7133b76e39b025cd69 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 20 Feb 2024 18:34:05 +0400
Subject: [PATCH 041/108] Invert group / channel features list.

---
 Telegram/SourceFiles/ui/boxes/boost_box.cpp | 110 ++++++++++----------
 1 file changed, 55 insertions(+), 55 deletions(-)

diff --git a/Telegram/SourceFiles/ui/boxes/boost_box.cpp b/Telegram/SourceFiles/ui/boxes/boost_box.cpp
index f4676dcc5..ff4c92c4e 100644
--- a/Telegram/SourceFiles/ui/boxes/boost_box.cpp
+++ b/Telegram/SourceFiles/ui/boxes/boost_box.cpp
@@ -201,57 +201,12 @@ void AddFeaturesList(
 						lt_count,
 						rpl::single(float64(i)))),
 			st::boostLevelBadgePadding);
-		add(
-			tr::lng_feature_stories(lt_count, rpl::single(float64(i)), proj),
-			st::boostFeatureStories);
-		if (!group) {
-			add(tr::lng_feature_reactions(
-				lt_count,
-				rpl::single(float64(i)),
-				proj
-			), st::boostFeatureCustomReactions);
-			if (const auto j = features.nameColorsByLevel.find(i)
-				; j != end(features.nameColorsByLevel)) {
-				nameColors += j->second;
-			}
-			if (nameColors > 0) {
-				add(tr::lng_feature_name_color_channel(
-					lt_count,
-					rpl::single(float64(nameColors)),
-					proj
-				), st::boostFeatureName);
-			}
-			if (const auto j = features.linkStylesByLevel.find(i)
-				; j != end(features.linkStylesByLevel)) {
-				linkStyles += j->second;
-			}
-			if (linkStyles > 0) {
-				add(tr::lng_feature_link_style_channel(
-					lt_count,
-					rpl::single(float64(linkStyles)),
-					proj
-				), st::boostFeatureLink);
-			}
-			if (i >= features.linkLogoLevel) {
-				add(
-					tr::lng_feature_link_emoji(proj),
-					st::boostFeatureCustomLink);
-			}
-		}
-		if (group && i >= features.emojiPackLevel) {
+		if (i >= features.customWallpaperLevel) {
 			add(
-				tr::lng_feature_custom_emoji_pack(proj),
-				st::boostFeatureCustomEmoji);
-		}
-		if (group && i >= features.transcribeLevel) {
-			add(
-				tr::lng_feature_transcribe(proj),
-				st::boostFeatureTranscribe);
-		}
-		if (i >= features.emojiStatusLevel) {
-			add(
-				tr::lng_feature_emoji_status(proj),
-				st::boostFeatureEmojiStatus);
+				(group
+					? tr::lng_feature_custom_background_group
+					: tr::lng_feature_custom_background_channel)(proj),
+				st::boostFeatureCustomBackground);
 		}
 		if (i >= features.wallpaperLevel) {
 			add(
@@ -263,13 +218,58 @@ void AddFeaturesList(
 						proj),
 				st::boostFeatureBackground);
 		}
-		if (i >= features.customWallpaperLevel) {
+		if (i >= features.emojiStatusLevel) {
 			add(
-				(group
-					? tr::lng_feature_custom_background_group
-					: tr::lng_feature_custom_background_channel)(proj),
-				st::boostFeatureCustomBackground);
+				tr::lng_feature_emoji_status(proj),
+				st::boostFeatureEmojiStatus);
 		}
+		if (group && i >= features.transcribeLevel) {
+			add(
+				tr::lng_feature_transcribe(proj),
+				st::boostFeatureTranscribe);
+		}
+		if (group && i >= features.emojiPackLevel) {
+			add(
+				tr::lng_feature_custom_emoji_pack(proj),
+				st::boostFeatureCustomEmoji);
+		}
+		if (!group) {
+			if (const auto j = features.linkStylesByLevel.find(i)
+				; j != end(features.linkStylesByLevel)) {
+				linkStyles += j->second;
+			}
+			if (i >= features.linkLogoLevel) {
+				add(
+					tr::lng_feature_link_emoji(proj),
+					st::boostFeatureCustomLink);
+			}
+			if (linkStyles > 0) {
+				add(tr::lng_feature_link_style_channel(
+					lt_count,
+					rpl::single(float64(linkStyles)),
+					proj
+				), st::boostFeatureLink);
+			}
+			if (const auto j = features.nameColorsByLevel.find(i)
+				; j != end(features.nameColorsByLevel)) {
+				nameColors += j->second;
+			}
+			if (nameColors > 0) {
+				add(tr::lng_feature_name_color_channel(
+					lt_count,
+					rpl::single(float64(nameColors)),
+					proj
+				), st::boostFeatureName);
+			}
+			add(tr::lng_feature_reactions(
+				lt_count,
+				rpl::single(float64(i)),
+				proj
+			), st::boostFeatureCustomReactions);
+		}
+		add(
+			tr::lng_feature_stories(lt_count, rpl::single(float64(i)), proj),
+			st::boostFeatureStories);
 	}
 }
 

From 1e5f821c6f7d35741c2882895b25343759996a2b Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 20 Feb 2024 19:06:01 +0400
Subject: [PATCH 042/108] Start all business sections implementation.

---
 Telegram/CMakeLists.txt                       |  14 +-
 Telegram/Resources/animations/greeting.tgs    | Bin 0 -> 11908 bytes
 Telegram/Resources/animations/hours.tgs       | Bin 0 -> 41195 bytes
 Telegram/Resources/animations/location.tgs    | Bin 0 -> 64974 bytes
 Telegram/Resources/animations/phone.tgs       | Bin 0 -> 7835 bytes
 Telegram/Resources/animations/sleep.tgs       | Bin 0 -> 38806 bytes
 Telegram/Resources/animations/writing.tgs     | Bin 0 -> 64148 bytes
 Telegram/Resources/langs/lang.strings         |  64 ++++
 .../Resources/qrc/telegram/animations.qrc     |   8 +-
 .../data/business/data_business_chatbots.h    |   4 +-
 .../data/business/data_business_common.h      |  16 +-
 .../business/settings_away_message.cpp        | 117 +++++++
 .../settings/business/settings_away_message.h |  16 +
 .../business/settings_business_exceptions.cpp | 145 ---------
 .../business/settings_business_exceptions.h   |  37 ---
 .../settings/business/settings_chatbots.cpp   | 175 ++++-------
 .../settings/business/settings_greeting.cpp   | 117 +++++++
 .../settings/business/settings_greeting.h     |  16 +
 .../settings/business/settings_location.cpp   | 121 +++++++
 .../settings/business/settings_location.h     |  16 +
 .../business/settings_quick_replies.cpp       | 107 +++++++
 .../business/settings_quick_replies.h         |  16 +
 .../business/settings_recipients_helper.cpp   | 294 ++++++++++++++++++
 .../business/settings_recipients_helper.h     |  74 +++++
 .../business/settings_working_hours.cpp       | 104 +++++++
 .../business/settings_working_hours.h         |  16 +
 Telegram/SourceFiles/settings/settings.style  |   2 +
 .../settings/settings_business.cpp            |  21 +-
 .../SourceFiles/settings/settings_common.cpp  |   5 +-
 .../SourceFiles/settings/settings_common.h    |   1 +
 30 files changed, 1196 insertions(+), 310 deletions(-)
 create mode 100644 Telegram/Resources/animations/greeting.tgs
 create mode 100644 Telegram/Resources/animations/hours.tgs
 create mode 100644 Telegram/Resources/animations/location.tgs
 create mode 100644 Telegram/Resources/animations/phone.tgs
 create mode 100644 Telegram/Resources/animations/sleep.tgs
 create mode 100644 Telegram/Resources/animations/writing.tgs
 create mode 100644 Telegram/SourceFiles/settings/business/settings_away_message.cpp
 create mode 100644 Telegram/SourceFiles/settings/business/settings_away_message.h
 delete mode 100644 Telegram/SourceFiles/settings/business/settings_business_exceptions.cpp
 delete mode 100644 Telegram/SourceFiles/settings/business/settings_business_exceptions.h
 create mode 100644 Telegram/SourceFiles/settings/business/settings_greeting.cpp
 create mode 100644 Telegram/SourceFiles/settings/business/settings_greeting.h
 create mode 100644 Telegram/SourceFiles/settings/business/settings_location.cpp
 create mode 100644 Telegram/SourceFiles/settings/business/settings_location.h
 create mode 100644 Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
 create mode 100644 Telegram/SourceFiles/settings/business/settings_quick_replies.h
 create mode 100644 Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
 create mode 100644 Telegram/SourceFiles/settings/business/settings_recipients_helper.h
 create mode 100644 Telegram/SourceFiles/settings/business/settings_working_hours.cpp
 create mode 100644 Telegram/SourceFiles/settings/business/settings_working_hours.h

diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index e4a5c5eff..9b3caa4ef 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -1282,10 +1282,20 @@ PRIVATE
     profile/profile_block_widget.h
     profile/profile_cover_drop_area.cpp
     profile/profile_cover_drop_area.h
-    settings/business/settings_business_exceptions.cpp
-    settings/business/settings_business_exceptions.h
+    settings/business/settings_away_message.cpp
+    settings/business/settings_away_message.h
     settings/business/settings_chatbots.cpp
     settings/business/settings_chatbots.h
+    settings/business/settings_greeting.cpp
+    settings/business/settings_greeting.h
+    settings/business/settings_location.cpp
+    settings/business/settings_location.h
+    settings/business/settings_quick_replies.cpp
+    settings/business/settings_quick_replies.h
+    settings/business/settings_recipients_helper.cpp
+    settings/business/settings_recipients_helper.h
+    settings/business/settings_working_hours.cpp
+    settings/business/settings_working_hours.h
     settings/cloud_password/settings_cloud_password_common.cpp
     settings/cloud_password/settings_cloud_password_common.h
     settings/cloud_password/settings_cloud_password_email.cpp
diff --git a/Telegram/Resources/animations/greeting.tgs b/Telegram/Resources/animations/greeting.tgs
new file mode 100644
index 0000000000000000000000000000000000000000..dd1ab78d28fda166f300238af72de46bf5dc1636
GIT binary patch
literal 11908
zcmY+qRZv~q)-{T|yDpprCj^4Kvv7BJ0t<I{*C4@NgS#%=T|;npcmK1`+2_`G=R>b1
zy^UU@=F6C~23ZsW#6J%d_L+|!h*-LA&%o-iRde_v&R5USjSTLA+4bC*jL7#g`mCvB
z?JTtNXR90h+r~p?IHOFVOp0kjofsV496NB}*RSz#>`z~Jb`0=S1^rqdXB6;1S{|<`
zr##T`=LXuR@CETW^}HY7wk5s2d&5AFLxL>6;{vky7efai@V4Z~!=0eO>-`P-$ITrG
zbUDWfS|0|j`?S3J;S0XsR!1p3+LjfulQ}HF`^IqGjb>1G75y=(K;6LV5<gJctzS3u
zcA_boo@sZ3<-cxh)c#15C}^Ee>1$HD&`Yt9Drh|*N|6wqtR1LfC-RBSvn*&eJE+hh
znmDBEVu=~tRjxE*KL(aE(bzV$CyJ8VF3z%f=E`lKIXe4TVWG}{1}D-&1J+H*<nqTR
zhw|m}lZiy;X`P0auq2ZxTH^c2s*9tR47xfx`+vLz`}ujl8Gr=+5{&c*-=NI{<~*JZ
z@CA#%h=RfIk0<D4g0HXdSlG8OCsQE5_T373d;@{8PH-;b(FvGYA4mn(ef`Z!jSs%i
zeYR5B&eTr-#yv?0+SPvx$k6voB<||)dwbD2L(cKJy0PFp?JLram*+mivFp$TwAvCi
zZw}^&{44;eWJmdNyv-lz&f*-bLv`7XX0qlHPYDdezE()+B05Q7;=i6A@6Kf&Gel2(
z`qpygVpk@g!Nr;SSn%zI=!u{EJv3%p5|}|To@vK^N_^pZ?Xt6$X>3&SVOa5HlDTmt
zEcTO3g>MzVECxoyoaBbGXkXq$t<;!LY^N+n4Y$O5amuor^U_2maL%vu@#^|!W5ZF(
zZ1H{RWb8rEJ$k_lWxaig4BSy9HiFUh;!g2kkV7JvSA^s9i=yNls%tQ@e5^7gZD-fu
z?aSFjty`vPasc?1USn_M<E?Kfu9@#CvA*pSUTh%iRqyBmi&v+!UGIm%kyG{Cz}jp(
z3wX8VZ0v+MwQ%L*>gf(-u<rBxdIEbV#{3pHmGl1I-ZAP9&H&vqIi~Mp6vO@8)D2^Q
z5yHH2x>lnl!_pdS&^pg$hv24_(oktVc8XFddDM@vPS3h|O~4*mjdpFGh*eLOpfkdJ
zfJZPXXmb~YCm(^!5wJ7kXyleGWyGNvwathWG(nqAyJgyMg>3a4sxAT@N?xi=y7R!|
zPqDOK!B<Hp&6$i^artL@o`DgL!OZX2%@al4*<12pXT9oC4p$^w2}1vzP<JnXYEzhQ
zBqwA{fAU2>rC%9B`2F}vVaQGxxy8e+B0`xe4@=#g<IK=I`{(uHe=-@mw#_9+tY;bh
zWR5~Q?id}9al6I6jZ_0x4}n1>$crNO;kk3gHF{5Utn5goqH0(=aD5OW1sqZibnk8R
zUEBWa9N)eSo)kbQnf5uoTn2aX@|jgB+H9f><79`bcUx60+oHrhxPRe@ZefA)Gi>(i
z3=I{lg`nM3Ru+}T=^Mz*h?O(SAV5JQ?M9SE`Wm=0edy{;Y5P6r8|fnh%lV6LIa(}o
zWd0$5POg8~G+-u}thSFtI|snaHS+V++swU=bGg;UUWRq~Y4md5vd-ybGuJjLYbC7Y
z;O0k`GkVj`s7D0a26txCPhT@&vnb2jHf9q+IpZ7<r=fe-g87^`K!&p}a0k{<(TZM&
zbQ_v0A`mftP5!Y=bLoH~A{YkSWps5<67rCm7<-N3x6nB>F{0V94~2yAr1&(HeS(+0
z);EeO6ZKu6<gEQky&O``b{=Z=UA_r4^0^t=)S;Tx_(_S580cBYa+PkD^s`V>HPyNF
z(yq4F<&Qn(uCYq*oh;J*n)W{Lr}qo+`(7AJYL4LB^}UVPxR=R*t?Br@@V7*bZvwM@
zP5i&nm40Q@37Ggr=Vutr>sYoSlajY=BHKa;PIxBiH3RYuJ@3U;sUsVUJq~DHAj+e*
z&DB;BP#>h#cwJfCsHeV}<P=mVT|ys|Q2$h%-cKJ>PiS4x6<@2r#U_n5M14%az&pQy
z6%M117H{udzV~wWS~-bv{kPbh-8N0JE0g*aLnaDC`SrR92-HlGjKY#$_ExE55IL9K
zXjz6_G>;sL;{AZ^+d{L3jAnl)39amvw;T3R#zr1`8<T$ymK@l1V{Q%PP6VRKNow;C
zeMx4p-QU7uYQD`>3|@DKXD$K7szcdhWtEDI8#XWqHOVz1)6)~6a<rl^Fd8iLvJrkI
z^h%2^f(Lcmnng=i)m)hT;>Bb2_!${qg3LXv_aZ8S40%MbO6yb>jE}W>#|{OIrI*be
zGrW?Gh90kH#f%k=NaMpZ(F?It%_DX}$+-8dKf6D?n>~uT_gt|$yJ;Hxvtt`Z#$=#7
zA>1Vb0IjHXl#@&q7LIYn-Rl`&@a(pGTHEfI<vSxdS6unzVap+Cv3QmTxwbQ7FWa!g
zgzi7Pbys)30sC0`s)Kfx)@f_DQn!|RE^R>QYnNk}C!3FI1^*}xmo^t9L;qD8-3ZJd
z%^vU0>eRYiQ!FpdE?vklb+{8bX1>g{syLrJ)xGUq?>kV?CL9FjnZ(-k>~42L0S-Nb
zdhDCY@ZX&{Eul07-qqxX1m5KYs>rC{wPJ_l*K6PNBqJOiK40yGjg7^D$b3@}9H>o3
zR0l9LB$<QI`yF#{2D9coVLyG&%x+maUNG7ahb3DQW1)&boTh2uE~DH5RJXAotQq=G
zkHP2;k7nmiOMAZs0q(z6g~ZZy6UO))MMG`YCqYcQ;F49#A$_t`$ukM&$_?e=r18vD
zWVynhvEMA+k$P@Lu#!RnNMYlE*}(;l#UK{>joI=p>FD2FgMJ^+6QVeA`!ptKq>4{c
zpPn|hixpOQ8DCgW_0K#t$J?~;SfhD-#Kdf`c#N%+$++rJ57dSt?V~E;@YK4E5_tEx
z(Dqi|-CV67-!4D4iKLUGm4vZ$7L&*z1-YrOu8#DeBQ9%%oFTYOi+CGvBldMuIZ4L>
zo%BJk)K#BrYT`F&=@(#1>3lSm?IEt_=?WO4jmF%~lYF;{x&7antaSB=e@GLmbo72A
zTs8Y{W*Or1D+#>_pg~1dx&(JPa)aep7r_I`;~@Xh^fl$#hEu~;t(anRlK@5&P2u{L
zh6w2KYl&W6($Jj$==RGdTHWzLec7igO~<lh2H<dt21phWC3=nkh=kl=7}SHjjW!HR
zWw(E0N)K5h;=*c7QMQ8Pb$Hyn_hS^57Qgt+u>3_;{#YGWpPF<+?}$t*flJb%_BR}Z
z@lAk$K@iT!Jk-4phD?Ju>a`1^M8D<M#cMc^_@R8@rFfcba!O=t3H*0$Q-44(O7&V{
zX#s<V@hn3gvxf2#1N~+fwc~6AUPXEKBW>pvmspl93e{(qvHJc-rPDl9fap9H8EXEr
zqq$gmhk!P<*8nhtF(3&g9Ei=+(Lb;Sl^a&@DYhwFKc3xvo0~4mumO_*Zq$98lK+nI
zTa_|Gs?u#yZ%3oB@y#Tz<8S9puAeb5=ZJ7-YMb~FN{`v4w6?AkaTfO9E^pXx-!L&k
zo&o{FpF7X#uUeB?FvQQVpaMiOUhUn#%Xim~<|EqtQa8n15dBS=jCSoBox`B&GQ;t6
zHb5=!QmuY`v7j4?MKQ6|GT|bYf$q+1vTx|?sEx(4u(1aDjn7YLw2E<IbPddDfrR*P
z4lDp7yu++XspH%lH|yNwfEjTiV6SO_HVhq0K*JM8DeT%cOreN{ZYwNlbaqoJ+J=@S
zhvOGHC*5hxyElt&$U6vg>M{AeDe7Z(skB(E>j0DA(VB#{Y{q8&$IBQM{2vrl>Jq>H
zkv!k~tv9$Q!jmx>{*khgYyUWXXaCzb`d6wrQ22G<X&&>xXzSl-oX!8D&uIPD6Ve;M
z6KiQqfP*QqeJNcWFPWw7)l^aVsQLT%*PUFLMEG7p7l+cXST|ms{nXnmYtX4rq)D4r
zD91>GJNmhi$HWU=S6(sD&niaSAGD5SX{gTmS8A7r)MTK0Xa=kbDcJxSolq5i7>;d=
zGgVZ?2;}0Zqqo-@>TY%vw^=L#G!`g^0%7^DJH$79iMVWQ6kRS$)M6Cv;k+iA?w`Y;
z`+<H);f&T2+FuwP!*Go%Bf52?XP;1Un2aK|NoRB^c9_Z+nG<;FrKmCIjZ$DIY%(jR
z*C+$^t>TVV39pLHVP^5Ke~e^Y6R<KvSW4u>mE-Y=;aT>U*FA0ceicg4Qo5C#K@Y{L
zHN}9gdl8BaBxZffe^pYgV$C?qt<$ZCmo?MgYa@{9Kuy83oo^gbr0Ed0d}~l&V~|q&
z%3Yo6VAic>LXo?Rb8N=lQ&S455krwMURj}3gvg%C+92VnB~}`lXl=<^pGzyvyk?qW
zyWm6iJ1~6PYp0i)?mI4ADBHCjlqjT50tne50kIOp*17V_x3sk(uUboN0TF{RCS44V
zL8)(YDp7aNRVE8(B5d%wJ8FX!(Hex+o>%A1|3y4*`ghx}pqRfRo4yNe$1#oP2GL(=
z)^WV*u@L11=J*Ro-4}jdmZu(i+x&&eSLI1NYAmxai^GWtG*m#AM8=;NXG%Vx8wYRD
zO0U6FHL3Xt{6?~0RxvvBcPsqJWKW{xMXJv2pA$SWZA*+}kYW=BRZ>WWZOYxAqFjs+
zbGc$Ep`W6->4GE<G^J7^D=duU{oma&JG9lJdi?oQ&X|p%q$;^?u~;C!0Y$TMHyOkj
zkuq$Jmmot17>M9wNTI2RS8#HsR<ni5HBI?z;-kOCEFY4;2&>CeVi1(@aNx<LdIWSB
z2d4?^uQwa9c?2xzh(Q^8YKr8ye|e35mP&J3B}muC`CJTC39XHXG}(jsgH4c6uA!+@
z96plji2-lmEr?JoKR~FFR;9w)8ljfp@h*O;qB}%yKvAfC`%bIr0oOiCxf-X)z}zmK
z5F~h|#-)hQ6_YTHFvdS`{cB5E6VV*C(NqsknK9B2x|!g3@S;1Sl$VOzhAD;C=9^B)
z20FM!As>RaFVw3@4Ao>*tbs_i@g^j%m75_@7&+WfD+;X0bZJPQmg2g_VEhFIqC$Hv
zup@Qg%QP7W6v6(Y=@<+HxyL|I4u}t-Uw(pRsU^jMX-E6hP^0{S@V~@jv{8OS<NrEV
z|JU*RzfQuxgeA)+E1#tr^AnZ4L}oUf1DnOVn#H;|xy8EaWCwinr*R>s@6B`@)aW4y
zSk3H!U<b718MHqIaQqi2gR?b_)U_D5p$ay&6pHCTtQ6+EGQ0R45-*6Gont=84-@rh
zX)a>7EFLl5k)!9P8>s_07--Et@xhp=tfUQl?&B3ng)h?@ey-3DOpE^_LRtQY0Pb#2
z40*;3OPH5RlXeEcKp&}VZW!KQhMO?CUKD{=T)qXE7H7mS#yhYT3ZG~6kGUcW*{6AH
zC&E>;sR?F}jvNd~m%J#@*iqV<2@K5$$o!UPR;~(zDxZOqqbd0dP0PrB%U>`Ip1q1h
zz1N!XX{6CT;vFU5x+j%&+!q4B0Xl2R$}HTK3U|^0w>6P6wYQ6mp>3EA-h-y<+X#Ik
z=pw&);SriV+Hlq=%8U*b0eJ*D*bFjva9ZbFOqh_wZ}y8h!LmG80>u?0Atfw*1NDzE
zmk|tPLrvTKTh>J_*60AIy2nBL2pADMXV3A1C4^K=W-48YmdXCgf3by3i~Pj@NQrcd
z{6O>nb^b^G<MjONuw>b1<p)*Qelk%Q%+CJm#m2J1!Ls3A`lEBE!#fUxLz&vLAJrwf
z2Hn}&sF_qK_yetcm#1K<gLuoTl1<Bdeki+BxW`>=aOrb>8J0p2CW=VOC6f3x*ajmI
z328?<e>n+$IcbzjU>MCZ_BeA$uoIZ%8!pxz3H{3Z({Hik6_!%sp0H>Yo(YvbqK8%(
z9~a_=hzrK*mlE}nS2FvxwtT-N+rqcnmBikhh6bYrVAA#i(iP3uPm$aQ6=rArJeo9%
zss7*0vmUr}ry|VgZ{M)`RvhQR4!8*SCoj)39qxLOsYwQr20@IKvcWW}=e>@k6j5do
z`Hq{P<M$kAz)<V{T1v$@AR?swSY^gQA1BgvKFOFd44p7Cj1H*fZL!gcUfJnm0<59w
zeYP58)_&Ob6ntr{LS9_P9Nbk(M)Z>O(^Up}NGyh({DM=hpH9_Rj5}I`_0bktVK)Eb
z$#{z_{c`<(Wxx~<V%+}-#S~8`d*?sxgi}2H*)CYI9sebsBYY?9Dz`*dY_q0?N3Dsp
zHye648xvvI8+SJE?JawK7mjCh7tYm18$(6jsX9-+p!HJT>XtimmlL^yy(u!^m#2Tf
z%Tl1PpKn$+EV+k`T=*7l+(3ojNI&NwLJ{s)RK*h4Pm9t*V*>mRP;++U4A#N#Z<Xmq
zVeSB%`Y1Z=%b;C6asaF*2Ngr?%#_a$e+VuY3Er(a8)}g-HJG#o=K%`HkK9ZZQcNY{
zL;XloQ`2OJfGZ^E8MriwT54>8KysLudLaUKF@3&hoYiUUZC)H+I%R(#O}Qc}3LPPB
zZi!6%+%TCC0&A|wlnNnzalH>ZY~N}1XH6T5{akB_3RyLo<vTc5$L5oHLANRn-KX$k
zXn}WHvZcneDN6Ava)u@^btDZ}-18-L(7Lzx3o5+^0T>1bR8x;})R+fdcz~Gi%N`=S
zf`%lCTw;dWfxsc10|d&Y3f;)P6Nvw5wcql@VCbyu?UASHLSi>P)S*R`xLJ!q7?S<d
zXC{qXA!Vl}{X6q;+Qp}@S{brANS*G68uko}@K0ug!e>hD1)lAMZZjFI<X?{PpZ!a}
z8dA`zw4_$ryJ&3LpPl)wi*5*P;B5ttF#fvg@yX7qqP2T9(@hN!so#m8I6EcxX$(QQ
zZ(kY2qDlhH4R3dKd8jf(5g!#0Y4LRcmXZhNwv}QS!*b(JMjZ*RN+nW!dNcH-A5wpq
zh2%8Zyl*yP4QHtTk}{%82{i)_SGZ4?9HSWvsT&WB{${!3Z7&snP;_Ir96C2V)9yGI
zSg7@uixD@}Op;vuuBixXS3g)Qf2vJbWLWEH=K8H5D@K)oq3@O}MXFbM>SUBzY}s$8
zDN@~9hLOFGe@t;(VBw_B5GwDajv$0PT9>O=)#hfvArAe`(6X6Ld7MR-IJ#j@j{3Z$
z`+Zk0zquK@Q^3w)IaNQdlJ|5vKxOR0Pz~t&Ivr4f^NWN$PJdL-XCrR~`JVf9+9+Yu
z6qWzM)jy%{newh*PrUjie#2$=9vW5l@SNY-D|?F8`2o=CQw_v!^Fr5)p>^K)=J>+=
z`5(2d^Q!|kPpncKe8Gxf53j|~_O3SWmxUgNl{8NWVij8#ljFA&&ZVZJW776+$Jx{p
z*3|d$#Y4XSQgU-=if}D-Wyp!H(WjDXDm7BE9Qb#ESZHE_7&6aQ1P28usl4XO>V)~r
zd#$PTuk0%~-s%cF3~o6)x@(SJvE$2XYYSJe5D_o~sC@1dwp<)At3-TJKhaVlybmt#
zYwP7MpnOBw>gZjv$HI6NX(qEnEV*$V!{-k0a9sj3qd*OA8MY6a^jtGjVTfCJ@tgMC
z>k>>Fl1Ko-FX9H}hdRWPPq;pyww_y+?F7qgdLNEwg27T+AkiUgQ<Q)RVGT_z<Twm2
z?PfGS3tX3|j0*j0yrlWKmIespu6Pqygsd%D`z_LGwHYM$OB!Wr^R~MK4I*0LvM~3k
zjKedUxZgrZq02&ezKJV<M9q*0ow(q6Oi`a`faZCRoZ9M-m<yb-!#R7uxHUPMiY^j^
zhN$i%-Zo{5&d<x;a)9A2XBkdvuCI(V$q6~L!;dysXOQLg9EJL6``FmA?>JAdYWBfw
zj+u0*RwlWuvS68*8x>><Yu#du;7Kl-l0KWPNBSX^S}0fz1d~S4te2Xqm6^q5upOv|
z56a#_l#Q1SH*IrphYmow<ofRLZXK_OAe*qmHbw26uujcC?BE{c9sKz`Kv@629eMot
z+YiWS$Z^s8breGsLqNlxC4>pYO}vjfGy!xPye$iHH$gX_slpn>J|pCQMjRH3!6dwa
z9lv00)=K?yYa>Edfy80qj_Gf~&4_V<7Ex!d{s8!V(Kv=vfFR5lEh-wUHA2Sw-NSRQ
zG=iaJyhc0GN>NYc#=#`-0__6i=fhrza6~eiC~dD>I(ZJPySs%z7L^f33m4;0Y@9Ua
zVQBv3clqlCU%Wv9Px{^vP&;CVpYz437P!`;Rg*>vyDv=znQ<K`QQ-Om%-OpxseZP@
z(rzvI5bI+E$YW^bcigz)5#+Vhts+ne?l}hGdUb39<E3=*P*0=}3!*UxXj~$JN6-eI
zX#J^5W5>a0rhj*bZE)^GY4T|NcpZL2(3g7CpGt&kfMO8qM`H5mnEmK@@sEl<lv<EI
zNYIgq(7|`eHP~dU;JC6FYoc%5vvc82f-T0xnY@fJrCyn*%`6i-oV%RQ^IO<-DJWVQ
zDG{~SoW44i=xZ|`N^D^>U%?y!?H^+?lixhjnN*}g3OX18GtRykX`A%ze~R*O1Ek%Z
zDLVP;=LlY*)kEszz4(_FB+{vd{IwK;N*>Y>7=zC;hBv4l5#yt>9ueo`u_h>n@;HXE
zE+i%pPU#R%zi$4%Mr_zLldK_v?o8Tlk`tjph<!20eNA{aI93)k!>)sf=&dne&)@wF
z|DHSp{%&oA{<*+s{Uij>wuNCe|LiFov&LubmNIvVSU%S48UN`J$g4gQEf<{T!ZN_5
zdq*{5fa3yG?nJcS<FBx_8YW19eMZ`IEJDT#X7sbu{w?bWE$>oxJYY>8LDSf=8k0j)
zN&uus<K@**gjrXA2nbrp*Tk+I{mH3<4%Gt{6ZHAyG;iKvL>9r;d<3N@Z!1L^mcyQ(
z0Rd*B>~J=bu68dUcdr3u=y1h+h5Kw}zuXClB!W7i8<LgOgP?5Tk!D|ZG_{CUyxTY<
zE0>PpxW->$q1;9}7{>5O0+Fq0mxgZ!H4Oq}WY(2lIILONB@rmGP;TpuW=6#*KBdXY
z0K3f@rFValdsT-C4JUW}>XHhGBZ<J(j12?qK@7E45RE#1Q?4^7c@YIb0skn8JLxGY
z5u8yD@<^L0BiP43bJYC0n&u&LV7Li_b1^jh&ARNSximA*!-*n+Ph&p8g;$r@2o$VK
zr2RFfX%nT!0!wLGXp&*C$E{=((l=bEuC;6q#T|%c%#YisZeXOYrZl%GE0?1#hraKU
z5LZ%MiOzqwVyyBjWNaYV5|5xn)hkGoU9BVSq(41N8`q&qx!<8GOk<8Zl-(+QBQ^x-
zv_+-2tW%qZrj2$|OVPYc`P0B&!*{*It>4<C={eF8N=pS1;u;J#p16?plp=CyRe)f_
z-vM+HT30NnOcRU<qXCh{g$m_`Loup;pRph8D${mFBi#33X4+<3_I9r9?FFp~_p}zs
zYPQlYcz-Cl#{S62z|owGi)^hl$3vCR->yW2FHPjI=woYvK?jEr;cX0@N>;+67Sk6^
zh+ZH8Fq@q+ayPos)alKcIugD>!DK8m;gEy_rc2HwAl}L8Xj}Yp@7%TcwUEcXn3B-&
z3Qj^`?OIkX5VzA(=*LFlbV=KK4VS*HbfF%SZym4^qLyz%`y&S+`#B^M<t#+EtM+`~
zPRZh(%fR3XGzAVRnmkMQtw6zc!JZeIQ)n-b!9c*z3nm>c*18_3OA$o(Vqb|S9tX%`
zhz(B3_{z)pW}`UsJ%qf>lr}FN-_^z)A8a49Z0kyTrdHF_bg8X*I`RYl9y-292BfzM
z7m`-6^+j8ImUvm3p$Ok=dsPpq0U@+!o)?JT;`2=}^WEw(2tUi&s?E=$N9ot3!(CLh
zQBBOQQ6$x>GHuy<K8f&U#tk+G;dGX0TbnnLyPV#MMtqZV)pc!lOU61eT&~ojOhL1z
z?@SvNz!Da|yfgVs<JstJElyYQYlVhqFm;EX#?~0CDIpbuhK~qOakNy_z!+mP)2twa
zSF0wvaFGYDrBmd<U6m>dL1hF#3;MWc{7>})($nMC!9(f;3tsQIcin3Y4QYj@l#aa8
zQ+3vjn&9N21{LLSCo=laRjwh(Pp4(9A`D}(#H`ewBP^U~jl*HQh2KfwmEYU(_FL6^
z!dwcl8=_|vcKlcCi8$hp>Ht(C)sX=61-3}oT8hu^fZKc+CYlDdi}n58o%wF5;QXO+
zC`oKJUNpVrHsJxLFnJDp6+I+*mJ+Xkp}nlorygI_m4sce`VOoe+NU@Tvw|8HFTxGh
z%+*u);TpJEf(a}RIzxm#9mzS~>d%0|0Nvdt$pd@14Rqe@X5hin+R>h>=p;hV`ZEIg
zsg;Pv0zjlO4F?+<M`xi1SPT>!8gtz5V=}XgD9*b2F641OyYGN=Qi#UAqgtQdrR;1&
zGd42t?!MjWl;Ms-dY$Yo0s#JW|3<X)Tf-$`VO&Q$T9lkHL|XISK9(uh>wTX#Hx6f-
z-|aj0^bpcvHn<M)kP5az0(&~>A6D@XbDkodhq;Y~?92!{_mE8wUf+_ni~=~-^Iqfi
z=WKv|o#H2+FIwj{4A+a@z_57!D_WE1H<%#+!4bZV>ni53gI^pIUmaaS-rgDB4tUc!
zh3_r<JX%5oR3M%r?m6aN@Pq`B8+hr>H)Nn4BUB_Rtfu|R0|=EJU+mc-I){Gs$1eRq
z{m!F^&kww4<&R75LOc}4f2Pe1(?81a2RP?}pga%%KM~&GD5o|UV4CNb4+Q8Okd3@Z
zE<QamE<VLKt$rGWuE0J@=wB~b%C4o?PK)U(Usa<TN4r>1(K;rx5CUzVUP;{ASNMdQ
zXq-Q}Nhh@{z;fji5Os8V{#cWMrDowM6_NqcjEXEVjH@!f*{gO-qlE+qEEaar0f|iZ
z6$$caOX~gzQW17K^?=x(`S4#Ys0-Yxp5!EQ4$?oOO>Za7z$1rW_3pu{6h6JlA3$B0
z<?RnC3Xi%+g|OIHy75^tLE@3B#(qfYMEpoS60K^}Cg=zGo5@Ml%k&muDu=Y(KnISh
zQnGlHCqQI%%0g~jV(@M2i7>lfy`^bn${?!I`H@p{8Iz0JE42oUiY{w}B%jcasIcHh
z93Ry@6FxVRd;&`U_dUc+uUFT(9Er`5ogM97JKeF1YQgvCzP%Ul$8#$0&Y;G;B?tn$
zK2uTmb+xY+N*|#68Uzo^;V$GIFElOmRGydwOHdr#T|}<Mm>6t;Rcj^8zK%OgT-@C+
zo*$UYB5r)pB*UZHIKhfg1C>rwE~GSKtKB*~+uzNre{0s?M1w)8@KsA9mb*CJc$oy1
zKQ%Vm_d6ekfOL&FMsR#=__Ll%$d<Jse$Ur*xtLg(9$MQVd2eb7ovf*7YDotE$+E*`
z<UTa3)zR?j>f2FdDl(M5<HQT>i={b?ahE>e-lY9n!kS#H5#J7|+TgeA@qRKjcE25M
z086aS0PKDi!&Y}Azbs%<7jVx>Lzq2^Er3@+bYv!dONamNf8E`xP#7;)l7U$n0$D8U
z(3p%^9AfqJY(a@@95^^oc_z@I$S!dYyw9Mmj|RwC6U!W^@kmQh^Ga_GmP%+*ZoPLi
za#N{4L<9#lh%dU0O9{!#29AE`*HgYCX_5~M82A}9@P1ws5E3w8%fX4lI7z~2&0fP=
z(_-+30v+`gKI-iT3p(lsA_`O*Vx;L{ty$~pY?kCLlk~j=G{l24!*1|SK1AUFP8n!-
zBimRXzHxCw(~Gc*Ph`Xh(MHb^)952kE5p9+ZB9t$xi7?%nrv(5QnYk^sAR)0v*~j>
zBS?kjt!`V~HXijNMZ-Qi=BjZ|iJU#{aG80b6nFcxddJ6BTMQS6(tcQ!_0I1Vpg?c!
z!2SmMI8g9=-U7e9fZv8b-X8?@yzjcF^t_gdha3eF4}54y_Dd?biTntc%sLvJR72%P
zdB+bcY{hu?P8+u2eavkW3HnUZk%DY(uM5&6?hN^5&dB*k-wgT7&&m0%-wpYXw-y0K
zFTjK-PTu~LF8!ogjit#p@R^inG(a6mmsGY42ak*rzpdq!IxgpC_m=m{_o=O`>W5z?
z3;AQU9<20orQn>NU#^bFYNtB=Rcy&==kNXJ3_Y^L4AGk*Bmh2VGQVsWY;>{BhhD$Y
z^&F~w`rrqC4?$M@H>>s9d07*B7uYwDH%{33A3qI1i!7nu!5<ZVXJPQ!++cm&^E50&
z&aE82s?RK^9_yp@Mnl`hWifJ-;86y%`sO63+}z{G4!d(t`(0`Wn&nH4gOB(n>tu?C
zchh>nn!Z6^A^-I~NBQD5)BVhnk4(M)%e&<|IAY)>l=;`j`sj9j&!Hyc6Sy^N&;7%p
zQwuGGY$apw_3|&2&jS2~{@Z_HQ{!Lt3}6Yz5`HvTwJg9w3`1e8b<)4YF<E)LAWrJj
zP<x-HoWitku~Rzi$>qE<z{^OnAA593yNr)K=Bl^&)>cX8nb29+I@U#IV|&ExiG%OB
z__9EsAP{4FvKg6TvS6)c8>=_AL|takCkvlFeOh!4=SHIy0*q{mC3)jRG<5t>Dy@??
zmjF=Q7RjTI5!Kt9|F#nov-?DCF6*p(a`2q8QQ9i5@NiZU!@1J+TVmt;i!rfJ{aMtJ
zm#qOX@Q&E4jKf(AJqi}5<tgEzNv!(4eeLy98OCyMM!L)<u^`qdk4nJLpw3;>3;cah
zgw+G8hqIS|<U-dm1n~QMQH`FX1_pGRZo)dOUX(dpa}W(@xMw_+HNutIO_<-Uy`dd9
z9!7md>7o-66Bj8@BYx1TffBw*I0yeFNN6h=uV{QZpp#zCUC!Mg`f@!S2&U*DXkR3#
zDkx&-6lM#k6N%gR-V#Rcn`E6)s_+%({*~ALwAWdXNy^S-9cgB3Q|HG!TI2m`c_5>9
z8yo1w%S6-1x$@mWZaw;L`gY*kv-t}C+{2$Pb+a$c5k(Y|*K<KKc7eOfTNJH^>jjrJ
zNL7mx7prz9pT80-Omi^+hDYUap&7%(TX?^XOq}fA{t=zkOi6gAoAG54`>qlAf$D_n
zWDt6}K3s2@W|Q{vI^at4GsLLM=zI)go#;O%$$k~J#g}o8aUa57FDmrde@r*r70?wI
z=P<|cn{S;L(EL-9RMPMTMMB)=9zJ+aDAkwef>Q(kVqcc9?feg@Y9h|rmbu-IjZ9Ei
z-#3ZHZ>pXu14#`DlVmm7k2oxex8Tis@rQ2XoE#ZG&7&>XhS&WbJ!aOss&A{7FHbr#
zxaGM=Q7sf5F2S1<FwdsubRMXHCI9!ls2W731S@{M_~mc_%d-=rwYtI)d_nG7@>_%D
zGy{*N&_tTQh<!x4B=$6lxryStHR;@{6=chgnE|x(D>YcQAy+0b7c>YDq0V$QONJmL
zD3)fUZS54+Ch7&^krTQjMvOL#X|_d%UTpJEGBc`A!|bZOMh$HW=|t78G)Qckkmjjw
z)agZRxKq@=@QOtBBnoB1wi-bKevfAdpju+c3WIAxT70HGgfE7A1(ifp+2Z{>vO;FV
zW(KDE=kb<f#Ygh|!?ZKL8<Fu0fhuepM^%Jn0Y>JVIaj06n{55W)(k*x=YGzstCF|N
z7BaCp4pbbAjuJchdAs`^SSde)gm-wIxZY%mWGNy1vD)#lUvc!<yXIOJ?&d6(iO!r>
zil(OIv=RI#yLfs}Qa^P!UAh3cghp-<f5wQI@R(oH{F+(5B#1A%==~v`u+!<Z<3~yl
zIKsre#G)B*SW=8GekzSpk2gM_pYNN-{8iuLIc9vPY<fDQii{s1YUa!~;mzj<i)1Gl
z{y32VqA-l_T5PLIHxW!nqFEk{wLO=4{K?xgj6}hR!pPd#sLEGil8Of&{!vy5r&LTe
zA$H^dv$|7(!L-<yGJ$ND!TfXMN$PVrwJErYZ~74DYf|uz$?39bF}0MjK&gPozV)xg
z1OmVsjhX2%_;lF6Ih0oC6BSJz-4sw60IuL`dAt{Va4GTCb0MJ)`r<8%=|Ej+WNd3E
z`o-r(>Ku&_qi=5B7^W08Xy<28?^tXWVCx4#)!xyfxv3~yGR+3xgt)dVPHWKb79)fV
za!J~04MWLjs;t4-y%{DYf;8p|jC}+{84ST|6?+wAF~mjmA2Su%BHVhJEyRR0W<o4h
z<<YNkP2W9?%CYU@_C6?91=IaHbZz1ze81)oesz&CV*dOo=iJ7qwVlF__+0MNtYyh_
zXsln9ns%CD^|k8>K!NKMcNq5su)?2uzrN~YG4Pc-CrWo%SKl}=vci46XL-KZBL|UW
z=psw4O610-9rJTrq~~SyHDXzHE)aBwJk-JG7%mm9l|<$KVF+yT+0yG^cIws6B=ojp
z^mdD7;~~eM-*Bqe_zG)82z_|3vWE*6{cMMFbpG`v^J-j-NTcgsRw86{l*5d%tFZ=J
zQ-0u2(_ZU*!^89oiJ*6@uILWCBdp6S==pg`XXY`-Ysvm-$H1)XpDQ-LZ^L8l{%;3k
zQ#rmKUNZ4nV5nuvAl^rE4LjlRj!=wRKL(M83L&$n2Oq`!yA$#&#TG3}{u2&ek0VVd
zNu}a7BQN?=4{{u4>}&SYTaM4vgrS+h*~a^Yuxo_$q2}CNs#M|!C9iYrHIO#vXKyV<
zDcogY5&afegfV)IrV^OmKX#D46@%w+z3Q$3mkB=FeQK%AZ+zUUm1kag$8SPlzAxnh
zHGUPPe(VtINkeO4i`*h*U%x&LRtw+&Sv4s9be*jA+Ph);ZbgUF+6_xT>J)S688h%G
z@kL8O!f$Pn&v$f6;Cbj=-wAl}Hy<jI3HcJ_=OO`{>FOtsp!aem9=5{=(=<>Tc;J=o
zUhIYZ`PCPBw^?nNMFH`tl?37%$n$ykLI>m6g->sIYEMhlS9*&0T0?F&fdVWZ<Q??N
zw5t+?_(X{(S^x@o?LvBbqWk+t4qC`J7Cz+F2U(;OtpxdrPS9p}>dHg(y<D1yZFk8q
z9uyA_@wR5AFz7e7yye-IY({!I`kO2Mn?w3HcmFr{h2LgaVe5Z6e9-?t#}t2|K_MtW
zvm53PF&y`v8xIBvmoA+<Qwa83pgzqAf`$z^q$NXNPf@N>ubbl)P-%A$wwxv}nuUBd
zLtZa`#9#D5k_)y3iM)5gZWZ~q{pIzcubbJNyVdE1RHDA8a8GbgI>Q^$J35$Ac{6`w
zx*^^l?!(}AfgK}ZPqG`9yYJ7YLY^#T8(-mj)Zzu$5JJt#Ey>+s19yZ}`nRv?Pj6}A
zszUF?-AMj;eL~Z>{!`)vRb}GEt^o}P9m-&+N$D%j%<QqX=-<^sG>z}dmtGL^sy$pC
z@|tZm4APBpM&8rG>=p54+FcUyMMW~u{VSrrBR$atO#dtXbre6!M0|IwUp1r@BA)TC
zZ79J1DWOjaSl7fFJ75uSnc?Pm1LUX1e+tL2Ut;`51JMU=uZl?5ztjGX6n;+%el1>j
ztlrGQy8h3+{|>#*q2A@J9Q1>I_z@5Kcj>?D$A3^$fUP**EBI-)b^5EtU$y@!MB9SW
z{UGfaS7dm9@2JPdaD%8v4h-*iOt2Myc{N$s{eQby`If>rH2hxuCKNG)I?|vZ{vS_K
B53T?J

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/animations/hours.tgs b/Telegram/Resources/animations/hours.tgs
new file mode 100644
index 0000000000000000000000000000000000000000..d49a48c326a452b128811d2f6ada933299029489
GIT binary patch
literal 41195
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%0A<qSaW!HTzxPjF}gtBua4j&5{BnCcb}
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

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/animations/location.tgs b/Telegram/Resources/animations/location.tgs
new file mode 100644
index 0000000000000000000000000000000000000000..32ba54f16cd28f4f5143ac316b1eb66d1afbbcef
GIT binary patch
literal 64974
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%0Aec=XXiR({K5Zt(KG|%<NG~0p
z9`3JOk8NkQH4uc$^jg{H@WH6p6xFJu9ADbL^Cv%!CZnsZ7IMhg+$`|{y$e+!@sQED
zkLX<!sUth{xT#Vy7&6S=$w-8}a7M{C?hmJh?;1Tick<K-0=@lAsZ5;oy3wN^x34%b
z(3~|M$03f;w?sy%em?UA0C#ZVfb22k7d}tsW9A8nfCN*edeb7@WK%#aF6BB%LN#3%
zSt!)t3dNc|E9DM!HJ+#dswDX3f%kGe&TZ}IMIQo;TO1NK1lJUTpXMP}sY1AVldc0`
z>uLt$)ZG&PI{gNa6~pjhJHL0@*g46gy$Bv+BZY%m&dJ1Lx2QLx*jlEumGwb|f)d;S
z<0Rx*6Q{-m-%~^eTWLxHY9|Pa9WgL7X=T7`bQg;(R8$NWNC7k@m4V&_!Ma8XKFlK9
z08GT4YJGdzkg50znflN;sy_pnnpd^a$3mv!9U)Vt-4!x5ZXl-8cm^>gnxJ^AO00e_
zU~dVAyg8vJJGZCg_#S|nQW|e*in@Gg^Oqjzq@@H-4i|ma4&-~<?eg|P0|~^)$?Hta
zYE5PA4ld9Q6YNy*&I^S019U&emUprRNQc!!cga|=TS3U#g-+MUG5AqYB18&I<uMCf
z_-c&L2Zr?u68U71$YLnwa(xygGM?TE>vb~e2SFl7QoG++|E+>y`;aO!y-%%wg!^3h
z>+i?FMCu$HdGUOXy!fgdQOSuWS}C}YWx>}5suEzeU%&`vLc`i-qLj+h82%{}qg}83
zLy<%Vaq0yx7zB0W1*f+pxB}j&<1H%Le`~#jinv5XjfX34WPaJf8HK*o0(oS@NETX!
zVo$NGi+BEG9hT^n)YfN7(3RbN21eU1=dCNM3*^z2L0g!bC<^B2ALEJ%2^klt8M)49
za$;{9z9M)|dHbiT=u&$<YLs;HOM2whOo^wVzeM|o4)?whrH(<wK$I`t$QO2iB71b+
z!FV9)*SS?f;@2*h4PLlJzOu^xAe1q{I|ivSzn(a}VW@7?VNDec9tUBrAy%u*Vu^FM
z>jg9dka5Efl6F(zoyDSEFQ3O?ife%($hCUOlC8(JK2PIvU`y5W*-%Vd8*3mxubzwd
z4D#5TIGQ)dHF1IpOUN>>0E1IE`-goT)>Q;){hiTLY9tCNZoOB*Arc#JwUAktWZ3@5
zSId^hVWF5vHx?X|q2}7MmW@U~FRXM>)vh*mC=PlMH8`n(?Y%Y^IFUe+yyl5){-Ag`
zN>Q39*<U*8;J|>*%#?)1Y!U2r2Pn%+owI8b!y8)C9b-|bK|c8S8#0Mm-{2+K8x>Cs
z-%vl<6oTB|Xd+qi7knxWzKz~c?_>jAw*S8QH<HdMwvsl)_gdHLvDGq@ej&Ef*3}4U
z*A%&HwJu&NdqcYh-axpC&MX2la178T1EEp<GFZb!U9J0R3ksEhi==g3;9_hbkBWL@
zf<~p`K{nkv$y*v0mcP;Am^IAm=);MX-_~}8N=MaM8o^(EwY%D|CjUtxTd};j?Fnr!
z8=v6xouC7il>I)Yo-t2JCd95c66tZLckS9wm=HikR32((EPsq_?3EMS)N?iaV%A89
zqB(|TMQ+At*nwA_$Nn`gD?oL9V*k1f4xTrT@zhBlI+q-h-4!`GRZO?t4gjFJGBaQV
zTy)IH-*z@it*+!DE}OQ%(V5bsLgRt5vP<P|5kz#sy=vXM)(*!7Hv)D5I5L#QJqOr`
zBdfEoRyB3W7Y7FfW}ll-csAEqhHx_5rEgZ8#GAwp!QFmDR7ur+--uf+(rKJdU(O6=
zjbbwgzU~+tqEvX|;Klz$P#9=|gh~L3C3t(4tHR<6zPiDL;g~wttYJ>nH?rK&&exLg
z@2GesCjdwUsz-M~kSik+&wEV3q(Kf~r|hbz{-NXx+a^+1z!loo76>4|un!+&J1gk&
zf)6tIGi*n~+jEKZurMWR`6DOOam^->7jtuN&TBATNl~n($h2Wo<qKVB7RNZ2@u7LW
zERkTWB0tR3Ss{)h4fhSo*4Ta&qCagQyOvmHb9P_CZ*abijSq}uZK)gq@Oi3E3W8^Y
z>vSWYD1s-#@xU60hk?j$@}X|vm_!a;H|U8~Qle%Z`%<L@o_%%9%IvfYjaPD^gthR1
z<Oa>U&co;C@LS|311T)hcEtkoPNyywb8&DLzD}Y<=oeK@Z-@Ran_A`Jc9&C)B&fo?
z6=(SUj4IUBAVtu`9g7_aFv*U%pA%M0vB#0rEcy#zJnZ`xxNt<Pnn;cePq<0gd6<!%
z!<+v*XCJ--B69LRce(HpDnG&qh3354A-Z1zp7jnJHo=h{9ZnL^?LELAGyEMgTq8>$
zgc1niv4wR~Bk&!hG(hy9m+%|dX=$eH^cu4)7aA~PaV-UGBOncFZXR)i0S7LiIVkHp
z@Fp@SA~3cSt<$30=9C#k-;fQLcH2fGkpUR~#BlE*iWWiWOc*{&OWVT=PQg7W`#kM|
zI0tuysfVTjFExt^g=7odanR)xd7Wuf{=kk{SvQuZMApq_Z5absP1<lLpn&_)p!DH{
z{v9T__oadkr5%E4V{`)HPc^Y;0{(E>qJT=bo7m<iHm39*HWmy+@cqDg+|ycuL>6z|
zP94ZQub}sU&1VF+ienOMw)o+C<KoTT>5x5sa{f3lYBc;jr~|4{HxM1$X-R5wuV|yL
z3at3!nw!zdkE5&F43aDrCrIM10|KR`>`gCi?Y<3&cmfZ!YY!T&G?uNWzM(kEV<oQ#
z`qrC32CS@9C&32EDARJ8v!(&9kGxV=%ni^&T!PD)<Acq$$yh_)B)Sa%x=l4{ty*$H
z1VY}pwb8ED>dGgDtUW=PzFK7DhxcNdPmTUGZ)pIB2)^9i@gdOJB>-#{v;d~8>59KV
z|C1E(W6f+cD^8rBZ-dxp5^qNE!>)$9x+DhFyEmNbwKO%eH|J@kk(pzOWzz<=ASvri
z55;x#0{u6nV};H~G%^+e7xohnXKyOr*x0}yziMNdR2ikMj=?rL9Ta{xE^`m^L8eYQ
zJXbV<q-;8L$4%m8;SIVMRWjfyTa68}d+@}Uf(4#GF)Ug=VAficqO_Y_EzZY(8l14+
zCInv+R5%`wcTPJjk?9gHE?XYb&V7Zqn-{;$kb9dJOFcN)ZSu|4zrd!|#cadW!2(5M
z&r<EMJ9d=J44z#P{e#w`F8zLH7l>4v$f&TH0v|Ri4D8(fN&JH2BnQ==jsQ?94k&^R
z+u&oxGIY)C3Bg4Tu8EEXQ}vD#YcRpA&&Je2inS8!gSa%vEoR62H!{6JXhGi^g<Sl+
z<S4<Wz_!feh+7-a)YE7YkQ2TG@q$cuVktyqdWgpc@gtdVP;E7uryzrk#Ri0OkpxnN
z%mkWU%K8E&0JvC{0fG0>W0KQP;?YJr84fGs%FKoiGp-8q!H?9%2uhT66>1L340OOU
zD3R#S6|Krnfb$WAU|B48eMqx2DD(l5SfiN^b4g(!3N7eRT5`{c{zP#5Scv*P=Hc=W
zINGGARaYfkCN-?Rzd*c%@;Jd=qK~q>H-s&ou%VrqBK*C?Pa1J_8b(}&tzDZVTC4W^
z9Mxs-yln#|f8wZhweS~MKN8Uic*pQEJKYK<ZWVZYh?r_riE2c5$TJ)fn4!4DSX8Xi
z^e$pA1f!HXi~8(RJay1Zg5+>4P6{`<!O6N5Z3WOGks7G&$60}bXWZxwu0f7eokGvE
zHiAt0Q2g9IT-N^?v<qH<8><jTlFkPN6qc}B#6ftOL!`NoK1Bi~`4$BsR3XT1duCLm
zvu19)ScdM#fxuKjaMJb-Lm@8#G2<zEhEA-!b%EQ>Y(sjrxe97RMFMTDvZIOHvAYx>
zV@U0?Qt+lMh(m#=FSuY%r=ScN$I?;}Dv1kscrY)?XAcQ5%y`+mb4}8*LP;>WgG1BO
zQaL}Aa9a97kp?a?^6T_0)8@<5>P{E!!3<*W*c5x&<fqVsU6HELQ+de^%?7MY79nwA
z!2~8~v8%CMw-U_I7Xt1iB-dL>_^yYI=$t@g8=L=DM8z4*8g7!BBxGV;2Cr)b=d#7v
z+CU=b`dUowcj=&Dx>I6C=W3Fj%hhAjIce=`c0<jWdjpjbr|%7zxXCWLw7C$foNtbw
z5gvx>&Kd#{hXzkd+|7<+x?o?P?x&Hc7jvMkjIHZb5!&OLMAYJg-Kli3?c|Z2mH}a@
z2epcCut#%ob!<gL_BYbmnv>k9u|y|ApqUbNX<mvem8nzwY`Q~3LrSM2Krd8{NH!Uf
z&K-aosTV_9IY$c^B|~XXTk@YRx@;SabW&#3<6L?KGCAJ;Y%yYG_75E>%e0mueU>SS
z9mK*Cw35sa3+r%wo2$S~s5DawhKGQQf`{9%!(e2X>8A!WzKx{yl=@s}J~kI7YiP`5
zmGt{m{e-|~QR{trf3O{j8;lxKgDf%hoR;DfVf6O(DlwnL4k5jx&>&(pR3Ff?{Km?}
zfzOjND4}vByrO-SCH>_`g1JCLe=l0^TVMr*61G9vJ~1Xa^=#(lR6|It)cklbhR?4j
z<sJGduqQo%AedJ@DgX5LAK8!c->&*ma{J(Z6f9~l)tAdA^ilVf`tGPC3ox12kMP5H
zEh%ts5(4+0cMU1dM~6M*5UzXQ(TaPcU3c}qo7wavO-4~<wZs%SE>L^zl!@72K%wLc
zPqm#tuztfV$&H2>>~oNbo8qW>q9NwpZ{4dQ29=22pBq9<!c0jz!BBWxEipa{)Vm_l
zM`KX>)Az+B7Qfj=@2Mqv`}P!9aIG4Q_~~Kj%At57N518f8Bq%;{KipTLZOIac9o$-
z=%FeI<1{MQ=2fDn=1CI~QDi|OMvsh{@{LQCCsDByuAS%p1I`+kv}MtE=?syc7d}hY
zadUPJ=&?~m)9k6jGdeQ^ii+*hSjj>HXUC(C#`z_rhxrL1z3L8R#C0e^f@)P&vR_T8
z(Bm`9yW+sw1b=g?fr$Kv3vAoOp>OWg2stwswBRgR3PdwV?}q{!D27PbkIynWrLZ`9
z(P`*zr?QH3>wp?y)J~q*K=vHNsD!U0|K)TeokNm^<S`f?7I7`jxw=z#5Y3tL2_-?4
zdOR|;uy~FpO;~pTopLyN(F}A9*q}TJLM+^9bmLf|C`byB)vYU7ERdyjvSx&m2PUGt
zLX;0|i&}V+N_RdeN~Fi!-2+OU0T3Mv-crW0ZsMe8oQll)6jgCyQ6HLji7m_`Y0i42
zP{M5<Q{|z!xlLaVSseCW4Qc|8fWKp*Br+8TQTDzIUi{3miQq%crZcpTXNA@avjr8s
z%nwA1!-YifhxpuP$IEWEc#t9v_XirYCFl^f?fA+I0;EI-a$v=3PZkU)sU>G=HAuq&
zyo712z>4L1pJqi^fpxG%#2`+#!-XZmg_2j^n4YB!cJy>Kq|1?k^O;>HHHKH0Jo7&-
zXg_M1eUhrtW6^>5$5RJtHVIp}V(vioxb&>^L#gBFi(F&1hDsx~21?_!?o6Y!28q3G
zd24P$j4Zkv*9JgZ0O^k|IZlqzHsTtiiy<1+Wu2$8v<YZorU&R#>3NOMF}otIkB|oF
zxJKxxAzCA(0XnV`Qd`U#AxB^lQ*qu&OHgNNTwn)8Mhsk{YeHk&302)FZIg}TMZuCs
z-yJK{n;|Y@<JO0u8lLX21SProW{}L+Q8gUWNdO!{Q<E0^kjyT)&V*vrUFS}C3+wWN
zK-9t{hm`0+KVP?(k$fB=i(nFP3i^kU=?>VSj<u_2du<b3NW(YM48t}~y0H?7cc`d*
z-J2|V=M_HLC*UB=E6<N}C}new3yOEozr0DAEK=kVGtsSNSRSL4$_!1Df{AVU^RXP^
zC15`~AQ^$Q&x_vBV|dVxj#Q?+Vuv4>=)eva)~!lmcgOoE69z%$&5;DIl9=GJsXqiK
zp<$Jf#ll*B^J+{*I@o!yA0>G;N|Q?xoykI=9I8@sxe$BfEL#NI$myiA)TCf^eqAPa
zcYHeKz7F7Vi7&lzJ;sN5K7*N8vqig3(84Ty^18@=I!B`T$XTaI2q*%*x{o-BQy7mp
z56{1Rpi7MOEet72o9U(Rpy`o{d*)H}B#&e5d&0kWH}$BKy6<|aw@1H&PCyOrbGbsD
zcHEhyz&l()J{{IU{-ky{l&zERw}MZi_~wBd(UksS;4X<0h{H@%^v^z@8CkpJ3o{<b
zOP`T<SKIA`d?YV^c7ZqnC2|^<R23QJef-qeU(#2vigvjc<*zc&y3`&|l}5I&)(9b<
zWAjh~{3%u7+Y4n8UDa`2)=}5BE{jx|aEhy|@?|!Il;+S;HfQvv@-1X5qu1+`7pd*5
za<y4DX$}P+tbk+6i*0SN2jSxCv`+(HG%20t;Hp#e^ogU8I{A8mm{Kt{vF3}0e+S_<
zw#iI~zaiI?R55K%#x}`_+`VrLMa}GDZ(SjS$W3)uDqt_fl%EYA&V5#C)_*;-F{`7r
z((Q@1sumjdWai!<yGjw~08<s9!T7QlX}Y^ZA^tsr1eBZ2l;<tGMF|NP;&sBJtwpUX
zJ0AlHak#E)-Wd}F?{yUwk%iqPF>R2M=@w~V5dTD+0wmzT$8}_`W&q>LUT(NX(Woff
z*rrtGrU0GH5;7QXVJV>@HFExj=R!RdEj4tdUS5)~ex(oFWUXvXqBZiSd!Ai*bEvn>
z`<YAXphJ7CM?NC?vEx8|_QH8zQhHeATDdQmRZW;G^Ubz87ML`b7z1SwSsGLc1DF;F
zH3+ub)!3M5D~9@h*c`MiFXv@}t#aINBFIT6_gvvRxz2suPA<_!2H7hr#`%xy0*<<Z
zbpfikdC%0D&M2`{3hQwZ4InOrnTf0SDU!s%(~nMl;JoFH?Ol|TDPBw>;$=Wf!;}1(
ztyFEUO{8NS0I+I5UG^#62&?a^*Cn;Okm?-o))F2XR09hQ6BufL;Y&C_uAGpxXoYJ5
zu$P3qDW8=)UDpkH;J~ioTCRnj0lJCw(O96HMA{QLd5+cdiSpvvtDQBYLO7z}UO4S>
zomZzluJh`&$IJ7gVTBN??tKA3)nzuLO>IEBi0v9Y{|!uMqH#>g)8A;3w`J%E@dPQ~
zhHt1@S!%z?stKmd23@S;)x9(cXjJmt4=+BbvrZH%$7Asr^bOf|4~O!H?dIcrNGfk9
zmaJd)`YDf0J#_U*hLbDoR>oS<dmD?AP}8g3BROZQbC<Qw96&9bGYI_r!UR{P5>~T;
z8vKLz*93J8I`238IO!#-(L=-mV}UEs7>!F?XHAFUd0qrqEfS4h(^YO{Z4+47Dg>Sj
zP3soPg2qNy=PVdg32fqlH(#Nt(L$GpdjkkSx5f^WT~$)Iw%K;}1|mLPMZ-)6h9rDp
zgv2Rrz3YP%5_pg^joU{ha0jWF9XVkhM0y<2%twSkZU7BshlOO({d9Ne(a%Q}385+g
z$P~f0D;!{V=tLLH;_W&E>~F!#^OYD;pa>UR99pHOcS_N9FX)DHdJyF3akR~!$F~81
z($BLR=%!<E@j=@iecf7j_#*oCP}M~nVV$3k<b6(*gFaZK#YVc9y#w7H5O9<`O0-kC
zFvmNWIg!>QwCm6RwCYBz1&M|R<#Oed7A!rhupdi*Nr7T<d1L!ya<(z<%M+d0PbeZs
zWm#H`Vmi=&bv6-=(C3`2+dnjDtfb+GA~HI{UN(N^?&Z#Hy5@~JXG$Qjrt5@dWUp;-
zCN_48;VI_p2^`hk*UZQ+Qe&zN7AF=;dNu#G&)*A0@8osIfR>;~=Q#>Rq(~^(UwERh
z-9J$ngv+yKdwYisoTten_8U9w);HB^-H?zOh@q|B7T+14Q%N$AYK3$QFWe}%2W7^P
zqlI^y%DL8vMjs1m(b9T7NeW4hu+5b-A&*0cKO->3r7nAu2C(hT)GYk*z!c!eLT(AY
z_vMA>ecIjgK1XL!TGaqNIFtef2TCLt^2`tsebFhxSV$Kh+|xIcFuKK&Q>$w2TXg8Z
zJVP@RDIL_debi}>>%7PFiFMv}TAp>C^vJUw*J*WDKdIBv`zaj&0(5fs_QT4yjksj&
z_!)CYn;JrvuPcE=w5SD_5G0L{$R(q<bJ})(Se#dgX(<VnUcHQuT9WWCKmi#C*>_&}
zl6+Ju3AG2bJuccQ@+xqjT<aj6dsg5&!*ky2n?XMolX6us8e5r*b0p(%>uA21x8t$Y
z&M91Wu$EqdH1-ag3KC{C60em=D8m8-N2ak*HBBmNve=LA{4ie^#m6lQ2n$A(n~+<6
z;h?`(@4hH(3box7MZI-;uhb6rI@u*v)XmSY9JlYR5E071>^)QBQSC*&wAY-*Jpx2n
z;X~?)t?n;+lJ*BkDuwCvm2t8Ev7yfUNTe-7-)5iNXzV=6>UH2x%GRDkCW!493ztnX
z^XgntXh|rpABN%skSfSHzs~1-bR?j=6AF`PGI{9}I^I7=*VM7Qk#rQ2F)L*!5Ct9X
zo!3EyB`@~q`{x{=!!FiOyqS{>vp@|x=W8@KL%KIvjjoL>NUziR4j_k0zS;!&*8IXf
z>wfoa3^-ayPPWLyQGT6t81fcs5=OAg=#>I5x~*m$?<ZWWA0kboY}0;!?j*%kCL3(w
z6yU6BQNoi!8MLpjbNVg`oCL=h<m)eehrT~<2Q_lh;dhrZSTcM&w>Za6%I>iwBQ`m|
zonM?(rmxPhlqsCoN`4L4KGJm{zT5&f%8b77iTR)`1`+~?;dJhf2o=g>o1KlyfbHZ+
zlsMTIP#3cTezN^yiP?7g$l1B)P{1I(G<uPb4btT`{r0gce3PVr2^@Rrg=fTjy}yVh
zDFe|GvZNCR<Fj!Qv?az|OQJG~QOLiu)%w(%D`DJ3wF|)P=W7+`{+sPUEg)v1s$ck&
zy+8D)odkYQOITpCza)4A_!M|9?W!+Ovh!_O-#)Id>oDw`OgpDm-CST^xB&IZFP`L$
z20VCtPQ8l&l%aD5tPlN#r(An~Kxr^a%|n1OJah}xr8<Acl%aSEUgM=Nrq04K+djf?
z!gKbjYFh7Vu*>=LnJuYjQs;y4dc1J_bHBSpI7+7lQEeS=HBnaX&VJ_Qt8FGF;`;%e
zSK~8^x|lQNT&iLR&Mkpv)MB~04d+#kUR+)&*ATZhZYnj`TF%E=haGI{?oLIwoK?_G
zhb`}VW&;v~hKgl$4A8g~<4&tu39bu}G#KxB;d7kV3%G&=2X_i-R^Qh+nJ-J-ttE>C
zjxyVM=aQmEA_4Far;}$%Z{5+QhBhF`d8aVFJppgNTBEe-X={`~V>`rA-{KnWdQN$4
zZ6a%=j+@l`Mlcm6Ea#;~4cBpv*I8ft-4O#)o9!jwtvzoZ1#Jb;NfAF0Xw!-rG^&nx
zgGK?CpqWu~)+&-p4cC^aqAvpFo?{JquHI0r#$@(>kpV7?YrT>I32f}%<1s0Zlib`T
zY6D_^FlFD^5dt!JK9|(Rp}ZXs97{y*cMAla(5RwmGn=DbU89_ja(EIHJ>kw!@5IcY
zvnPjrSxqU@SV_oPIo&%U3oWdHvZG7t%$W{LA@5wR-nxR0nQGw~e({Qk`~=D((oM}s
zHOZGPS^nM`)sK&!Y<HrV!l`VH9C@kGRZ1%Fl2w8kRA&#M4;h`?`m*1|dEsJDi%o2j
zc-XnZSyj`AJlysf>&1sfgSfS%Dzqc1nz+C`^?)qdy0#A6vGl+?i4=0*^<}Pa9`(a>
zkz@lhx7N;8KsLO1^GBn)Ym1x2$U#QJPpi%z-hEwqDa-1_CGpcm3!(ni)px|92Nm8(
zt09+N>-GF4cwOxv;_6+7ko%G9jH>g9;Vv$bQ;P-8rc@6OTHUM8N!geiPj2I@)5Fn&
zJFch02e*BQ9hqt#zD1mYV|||t+6Cq$7C9{l3!lR7sdU=4QMX}TPf!nVmolAwBlimq
zrO~s4@KV;!U-EE399s_^;d>I6Ko3vnJU_hSdU~1CzZ8ht8&TNEu4X{z`3Gg}isCL8
zzz}CB^gr6pIHpSaatLXtwFu2VP@xJeSE`?}w~B9!y39fV(>|0tr&E<f|9HBp3w66X
z$y2Ku&7fYwebseq3T7Y0N@Pa5WHV4w%HDt!B+<~Ev7_1il2+DqH?2$VC6i3tjZq{g
zZdzImxk)?^o?&%`Rw+M0Jg)ntvquHAty*k(P*g83F(i<x_AI%wHE+<L)@Y97)_+BL
zCD6)-CD;YG5-K*;S^Z}bE4<u393xY1g%3Lw>9m$L7eM+C^0czn5cSmhP9@EU8I1db
z-X3ozAIzjnNdUPFTUOAxk=HTVRn`ESbZJJjkh|BAz8QxCJnMZpt%_(}N0GwT4LkhB
ziNHHi&~tY5W-1{}D*VKW>G;A^2_t{#?U}a)EsjGD)z-<aBHWE{yoffu4`So5Pm%HG
z@EjTc&%XZ$d*ffe|G##u{Gji@pyU6;Grly1&YT4v|9;A!*1zRW=jBabFzkQi!OTo8
z`uKYsZsyCwQFO6w*Zb}i00&g*e||UTGNme*8dU~wE}@y(We2G<xC=Pt`_bNifXhp9
znoKPZhMj=jO5|4d%jrKVB61hrq5N?7uT{j4j;kdHdrV6$`5=h-pa#%UhobsXjfb+y
zDoT&d!c-C*?CKP{w(LLSwH<YF%*q|&I-;(Qf-JnYzP^NOb4fzGvM3*$Y$^(#ab13g
zx~ijNBPjPMmqJ%|%zND(C=0<SfQ~2~k(S(;n>CkCEzB<!0z!9@L3o-|Dz3dq%!O{>
z2>yj!Bw;3n8<&fvFFTz=jVDHIJ)N%|MFZkEAT^V_OWjDZC#|mXJ_s_fi!1AAkXoa%
zO;W${hgsZP$|_WY`0?~pZX5j?>PnbK>Po={=zWr4|AiR+K1n>MzkXc(mb-(JK4$xX
zKENQQL4E1Fi7wTz^vBs+22)<RZOJPYNI@lSBYx&gRj=g1$;{Bo_S-6G!^{+KUdj*=
z<4G|pC}q&8_v3a(x5=e12fIThgcaD{cid#811T7!fQ2v^=Z`LOhYOW7sJV%s%|=qf
zsM#VK(mo)A5VGB9pOy5M(>Ws$VtkpQ#1HF1!H1&<O6TZ1e(4DZG+sKOI`+T&>TkdP
z=4W{?234=z&dVE(5Q@9uW-&3H39&19n77=l`9Iiuw;k8A>`Lrc3VJ33>oV7^f1~@&
z&_lshEtf5|1gX&dB%sjnqtW`6M4@F1f+hdL$v^TMW6ZU=$za5ZI6-DIPlgP4R`8Jf
zx-RoFE^{)pga79?Vb&FED&!Edg6gTBnMBRu=KnIY<W*AcXD+1usj}H(O)a3PRZ~?N
ztX-wbv2&H9AVCIHp?j<h{<?QxbWzj~ZV1h)f*Fe>ezZV2Gk}z~1r#6!R!Ur=%FX6b
zdXZPIoV(Gb(b^dq%eatfm-$nJz>SJi)%}ErAFLC4nS<+J70G0!o#54gmJAFcv&#n6
z{4@@*9h7Zz0!z8Ei7DHkYH(WO7g$A{2LNvpc6GLF2`iR<h^VsF6iZ`aB5o}`S{Gs$
zKpidiSB%>6z_0j!)n(rv>sTi3us=V0yIIzu0OQK(QJWm9MJD4@3I>*CT7f8&qCH-=
zYFw3p%U#Zw>|8;8aCwI|oxt6)WpaUkPy1Jl!WJwNmm@7<KgdtqLo~1kjp~ZLDcOl+
zx5%AgW2D(d_l?gI9J*6q&OSq>*`eAinr5_Of?w9D=RN|<UNO*FgRZ+QKn(0=?y24(
z3Mx65xd$gXTjK|;gpNpW)EMiM_)kXEQw*qQM)#3~BkpLfD}}GjMQe_caxVP8rFk`-
zy)zgz(bTM}$VM*`cpB^3pajVRsYVuTI57Ai)N5WeWIZ-!XTRVvV(4Lq&0hX90E66s
z1#v`_mkE>%_mNGO5sn4dn38I>n?S;_1O<1jZi-lXX9;u!7ksKw_$PBS8bxl^{iq7v
zHT&jd+{lQ8{q<;Pb8|!#@G^55W+?5@s+=*|3q17Tp57=V&XgCHd+fa8*QwywC9QWc
z&)MLl%ct8bJ%?w|+<fHCFU;k%Wjfcq+k5f>0*}A?&wufTxZ$td?l61UeHI7Mizmn?
z7d1{QT{H2awJ*5mhvmHoM^8Bo|Kqjj0gYzV%^M{B@Cbf?(JL&Jqd8(q)s$D?b1O*w
z_IH1noAhrK;neRZ#CfgA#x0sL4p#<L+2)GyQk0Wkj&jo5qa5tn;~%dwY$Gg93S?l%
z&l%+aQ{_=k8)sYL+{@1#IK$a;igk}lBbDh2fD7{8?W$`G^&mjRmK5~d<y)2bb6LuC
zhvXX<44Qn4X+f5}b=f49mG~uDP{nhY3$iM>+5*U+P{#tT#vq2<)i5z48s-J4=mZ>3
zbZ84N<CwIvmO~NBq8pv9mq8yG3uvg!%_uEhJmE0f4u#ub`hGx6;;OtXCSmI<@R8Tf
z-ZG8PV-3Eu%?(0ZTP&pStRLd><D;Th?_5iR>64&CAvJ{B4Qvmx?z2H%q?Dx+4FBV%
zFVy%-C{(Y8La+t(P$)$o1A`0y**JiSGB8vYeIFfJu2<?}QaTlpq&pE0!S<rJ;*V4^
zgoLB(6`DXjkS$!HP>Ql<Tl-m&enwO~EVu&yOaRZuL`1`Pz9(A%l$Oy~&H~=R36j_#
zIVYl(i3YDMz`3UrPu*5zgm|sEYXO22(X5tr*9?(#jVpeuEI2m0yKm#84z2xWw3y_3
zmi4F%uYG70T?;nd^@wDb84D%z{dZ{S#*&x0Qo~ZC?=%5t=dfz9mMhhvXQ}huL>ue;
z_zD`d*#5`38bw7rd)@OfaC$R<DS8-NnPzAlyP+xdXT#p=yh&B<7&({PW}xmGnkq^G
zrLljJ^Jgjn`LTeR@~YKvpU^drhtOf>Ms5l9pXQ-(ax;4G)FLkP!yau{&^gZ4SWu6S
z?Q3{p20n0w0jl3YfI{5-Ao~Xm*Ak=OE)U{&p7_h;_lrq;Sk<B<Rm#h^xN(0c5fM7w
zSkB`X5fuvRlcuY1NSX<alHAXCNf@K|k>^fYS5N376gtkdlesOGZ_DeUQ2TXMd)n$Z
za(2`GyS^9+<%@>nzkd1Q$3Ok}-@p9un=gN!|M_=xAAkI(^QdUN-~hforgHW1&9{g*
z`O-s#g3@94-@8XdIlr_cR<HY{ake~ewoX=NW{ynGfmg^K(_9}H$}5PR9>U2lGq_u*
zsJ__^E;DuekS%7f#B?BeW{eY>U0`?o?48*gz)pRM7<YLb^i{`h^|17i!Jnn;u_J2c
zPLL0PkfcjR=H$?o06R4Ol0~23+7O|%(>-B_FVfkvRf8`{*w~d|&)b~Kzr(3%T0GB;
zaznb*cvp4=ln6I#`E%>`ba(6KbZ?v3Gi|I71ojH(GFK8{c4U9NH3)%i9Z<*%Mb!bg
zy3OBpppUuVHF&QB{R6Ke1-y~U%15a9%O$o&x-Q^@<ldvyJ)R#Sw$#r`rry~6sQJ%e
zL5kmv<e~CB2YH6osfmp^h65r$*?$MZy(!N_Ja&-?QiAu3c;F2{9@6UM9Tp*y-}yn7
z@lcMmp`F<#pnME+464nC&hGUq(c-)2M@}5C&*unM_IN-KHI^$=q|#O$0k>B<%%#~>
z41#F@Mf;<JoTE1K!1Be=Ah6&Rfh%(a9aSTwUBxnfS#IFjBLmuDg%hT~v6*0w@`L}z
zoF$+YTzI;nE{p2lj43@(2k=bYwZ3|4o$puW6~ZXlMe^hcAT!7~A^{m*Kdc7}{_Y^H
zHOIqzcB<W;p&+ySpYz5uB3hIPvrO3WKd8zqu=-FK=M}H0HWJY8=Ge<@nmj3;<jZ5D
zQBa^Nt44kFxiS=6H#h@CO$3=3AqB|<V8S3Ps%BJVN0exD#riOsY5W8=F82?TU3uF4
z#xt0ZsE6TR1ofToFi6Bwkucll2Xx7*drQyB3yr=(lWDFJ^N!|h7_J%?gE2iw$Q6gK
z7+AS9KLL1i-6$Q_5P<4e^GVG3rH4z+kPl55ELHEHFObP6XkZR~T|X=IsLXT95W&|@
zQwHEgktvcb#uY{bu#ur%oiocc4wMl!V;z(kROl?AKe>}f2Nhd`_gzy33szO>TuU+N
z2ULl4+vIt}AQH^Qvw3|}xa**WF34L9%XxRJ?SgY_;_UAuM`{^G$_|KUfkES&N&<iu
zE<7^6GD1H(s{Y?LQ}Q-bE+z_d7A(SgO*D7H9+I|R5em1M0>QkEa>UL<F_6YltE_Q=
zl#b8vHarPs0e-GCGYfXZuK7Ph;VRd?X;9ImNC#Z1dq2!i;0}q3doisyk#JGmz}Bzg
zgz7LnAgiRY>zt6rZF3`FPg1FJ%@!NCo-U1jtar~BtV4w<x>_OFQWP`|?gzjFRW{Y^
z!_%b)HQLHHf|Kg|!lHj*x@4lq+2RSpq9?SyFjP%rh5R4p)Vgj=mnIW9X`g@dgh?wf
zb*@^4RJEi1i7@VHM$J9WYiPHdGe+rZ^hso0f?5xdT61|1!7C4ei{TuWRm%v{apG8T
ze6K7GR0XQ$Om<^SY!#SCwQLosffX4BLnYPqJ8%{-o|~;t5d31wbYI9&GeogvPR!+C
z)QaX%;iJa%g0pf*VRV}^;Dx+@%G7OQ)Mj6uGJ`|^O4<U37sHB}u1pz(a<WzuX959i
zApg!}rE<e5!)~*?+F-_X*(*h*4zViNAoj;;A(fK`OoYp%B?X@|!rLcAPczhacea_2
zkkXShUyy#V;xIgmxyO8InlDC4v&tb0wnf=G!t%Vq^e#)homP!VLUt;{Xta9mx_Cpg
zC&&CF_vqwC%<)^<rbiIQjwH~B)gw{KNpO@3E;{FN_l=~L@{JRvePE)12?KI-3yuT8
zzs&t;9>zUy5HaE?W6h-q?n6`m^3~i0EA8`2x2-v1HEWL8)wDj)st<cgaC#@w5cGj}
zX-2L}@bKkR9?JzC6+-umNFr6v*S-tk>1P|sF)8CYGi|s#bQ(J=vMdnla(2e{F%AGh
z)NyI3#1)w%ye{{i%8sPGf0qBK`HXRf)@DlzsfPLE9u^S`_R`J0sy7Z6-qgB$i}0>S
zn<OzW%R{yM8O?FXkY>WXXY5u4_h14#Zg};iGSO~JmR_TOptQj5smj8pjR9Hs5V;m{
z4-Q|$G8mqLR>c|?%kboy7Drj1`0|1ZJPU?3Es|`+qRC^m?k#g27MEYK6X_FEy&+KY
z&I7IlP(`K%6#E%%t~fJxYKZyF!z=j0Q7A8JVO2i}5GY+Xxmaxen0VsAK@fc~zoUE#
z6Eercj0w?%cm?-z4fW#F!drdSz8+8o4&d0m*X2&=(R1eAY6wa);l1zO24GJ~X_wG<
zQv?@4Li5VggHl{9%wLU{u$Rr%ywZ*8bVxybPiws2@0(Y8^giRp;h%?tBn<|^@TI-#
z@FSktl2J<v3xN|xfjUi~RK>GFJ}3yFS`h|0VjT=}fxqwxhHopd#9k;VlEQ2DVF-K?
z>kBn{PxQ!{fUYS9dGMi$K?N0Ae6|HUK%*mxm^0t274vHlXaO(He}_s{!qD@C8|!?n
z7}HCso+m6-!@LMomS}VqnVxV^)7-4<6UINddzg>|HzL%}$TYHV_LY|AN+K@CdE5{4
z{<gYQiu7pQEpr)Y79n`z-Nt!mCwvn-2!s%F0OzldbHu%l*Xuw3K&N#=CLT}4?#h9l
z>?F;afV9e7&bd|aZwmd)gQ}EE)L+ynmY_D9#ns2Pb80gYZWRlWQ!tGg5I<|~bPc$m
zc828KjU!G7&LakTwz|Fsnrw1eEtPTY(27^;kEpnUj*PV0+IBifLezQ^fj})!%X{(5
z&iY1F`7#Pmi0z}Ws)0|?z0BBPUK!;rTpQ(J(0a_DZNXFX=UD$7>%Y9QzSrKkOjcpk
zIS;?uO&H_A4v)Hzx|wusl>0S$vm9kYciDuBy%*!4##e{$psCimqRi#+MeV&Dh-!`(
zqnA)n7D1YT)Tm}tc%<#(kuum=OUvEd^H^-^DG*Y^KdoZqrt$TWYe*)|Gdw_JM%_d#
zwx`)q9<k&jkm5NSy3p6&;*p+t9WI41g9*+2iu<rF=&md1t{!wlHHX1)?wS`i7=`x*
zaaUx{-xXKnW}q?R3g8{CF_h;b99aksgdLkW1HLiCPeceMY{7^jo(AMfV}<c_Je)4*
zC@3Z_XfWqo7k&E5x*DbqvU{C(U*_mGfVJZLF&uyVo4+`9xo<_rT@C4!Zb6^jx9?)P
z$r1hf=-bTsZQ+cL`};dRkU7I{{6I+Ky5S?~)sMG(C`36tNAw_q{hJet<v;!XAO7Yy
z7-|3T4!98Z4@fQq>nJkfPl_4EDm(8i&Q!@YzZFzuS3yNFyaQEq-H@hodL{@;rTz;P
zY;fES#^M>z|7DhyU7&7$`0{VS-2C{bFaJ9K`!8Sq=a(POAvbmY;CFoHuOV;F5H>HP
zZ|;Gsv7WA_WVrBCPFCvKUJrgom#MuL;f!)BnkN>$i(n+m4B*|>Y|n%BS_kSu6oozS
z;WGSgZWXmT%)O%vwbEU22@hbYwaks30R7-vP)J7c)8W=^Ce+R<rSc!kLbD)GrH$u;
zyHrdv&$TNQjC38V`5uTzb+nAYYWTiO(J@%$eaRHBf`+2M-N-w8xNGY^R6<vXlT|_w
zu7gP47sL9xH-+55vjx2y<jV`Lglelx;bhjLfa?|DCR4D-7|;~P7W_dGkAxk8Fyzf;
zw6=>uLUcLGLt4_>LDwgnbMIyoQ^bF5{!ZO@y*4%;k_pJr7)>j3?Gl&@uG$z0oy9B6
zGDtAV{te<pu(r{Qx(Pc}D;XFL2TzRk)JX#apD$Z3>yQ-<j!`zJ<25}>k*AxN#rJlb
zsM3S6RTwvj*4sM-293<F`9YH+B}DhwxM~TQa{S#qE=jyfn>kES;WKtBU*QvHPvyiK
z89p&$siJ`o6#Wx5n=+phK9Qg*s_78l;c?WH2!W;*ID6-J63^QCiO|>RJCvjk_;M2D
z3;4I3YiAlWf9$_t8z~e;O=+ok<?O8R6dkFY5d_-s`zCO!SHhxkbw%$0$Z>onyMmSh
z?ZQvy>tWs048<{<pHzoQ8`_5nhY9ZNXds%-{39c;s`)Jm$om&1%wF1^n+vl^8V#dz
z>5Gx<-hzuNch&+Cle?EqN)*(Ln)*XXJI7fk%9JLDxFuHJuvqEa^UbsA<HH5lLV>mD
z{T?W19Z<L4s1A|b2a6jx5c!jkOW-9I>Q5#ob!4`!G^l|1#)O*B!+D}vkiob{!_jSa
zKoxXslvXiml+J%{=Dm~n&SfdGv<se`>fu;zn&d{8G^e8#^=&fmhfj9Zh0TXw6iwev
z1f|xRxE5=f=)e$;4W3W$&qJy-0uYpd#u&EK+e|=siOHFD|1L|{y5!<kM+w_N0Y6d;
zOCU{Hg;ofI5hUjGo}jlPNCtp@l{(zgE*TWA?Y?@7Q<m&4>l1`*OLblZ0xq~BG)S6J
zR?oKi)3SpTo5e$BFU~W5=oun2RL6eK?)e1rXue!yF+yiJ_u)P@%me4W;2@(XZ}ZQ@
zex4ZPlb_5qZ_E$SP!UWP=rS;nfB;t+rLe4Gfwgq5PR6JMzAf6{(@@bY1Ea?GlL4@q
zkB;Brpw1a{CG)0L7|RfjiG!!oCX<nWLbIZ+eI^e~Jkzgu90Ss0?qYC4Qjy3Us(|CD
zYcq!^a6Sh(co^^80GvQ$zbd8v4_Mw*&@yDds8XSd{|#y8b;*Q8fwd4)Rs8|d`Zk0A
zHYgFv*niB#07REh$%Jdce6cqN^X2Qo{ADPQ%=IS><v*&G(66=?N7={r*Nf1RFom?R
zu%aGktb>f#VCTU7!BLKZ`lwBa8Ps+ODbICR$^zZ3(Ly=y$?^mV5n8P+m*|LfUY+*1
z&dar5!jtYMN8^I%CbXBO!L@)^U>QiZu3VB>isP0gR2)#pgGE+JOa)QB+qRQuF1Oq-
ztLtADDn^6vRh-i|3cOjJ*;b_+ibm{}hS-bcQ{S`DY1dOPcG3f7RZg{T;VlUNfW&Bd
z`KhC9*IX2Q_tYtWv&pyWw8wQ`o%ZA=f&1>%As4dQ&AE@up#G)j4%r=;QH6!>E0lLd
z$mqx=%I`vsLn*=_esV>HKtdSn#A~-XLO91H0O}dj<%9Qc08?Gc2Uq25wJ!4cuc}(J
z_^c|@UtY|t51XuXRfIxBI>ww6|JO`Fk?z{p^C8{cV`b(=w8V_<7c9{Ev#h8<v?%Dd
zlH5`rgrcEwJIAk`1ee+OVa(pvTu*!VZ>LK{1@gcJTz2Ft+YMl=#Cmc~IIi=t1k)0r
zPt7rQ6!AreYuDa@xEB2pH#o^=2cAWFLckjovXKISKv;VOWJ{oyBkgJN1J<9Uo))18
zmEx2{h+p;|B=`f7ot5nIZC0P9<hL)U-`gWcNX^AB4A%qhodPvN_ky~VD6$;d#vO_K
z4-(La5+=#oxR^Omh4Cq_=Z;5>1(eb8E06v!f8p*vpkJZn5zsDvaDTq^aO+u=Xy&F!
zPo-?MdR?qq&cOgeD$&mh8P%f&bF7}6g*su3MvB|1f!helkI0jELOoC#$eVaOAO~F(
z8$}UWrDM!$uhQT;)@2-Z9h)Dqu489#Btp816*EO{s!oi4nUv48ugwD$KwdrVCmI$h
zzH`HjNP#ui@nDeQ(_kffY{u$xnbB8r92|g`=B9G!IO}qxNHi19Hs^oJp6wR(`B6M<
z?`YBgD&AX<TzGp-m+#Hsi2`Xbl~)66;ZX`mZXE*}!&BIFnwAxeLQo3sQyO-)7yvry
zw(A-|E}!B-B02DsDLJW)e_;@e&*=2F_FIqyMsaEY`0?QWTls;O^Fpo`zIH;{Q0FXC
z+|WzNL^UB*TT&{O2=ZqI;WOGRrT`16g2yluJV;h?7g7QCA;+k=bA<=78VVGf6BUYb
z8JaRdRauC~RD=9pa6zF$1);`Eo~sZ<agG#f69GUK^ki>J$ba^y($JJ-lHO0qW8}LK
znk$PK0#I9})Jt4mSV@MtNu2Z&xL!8~!2W9)t+1AzC`gI*aNMkjIp7IiI?9yf?DV-V
zq`9nz%xBk)mZ8<sJ#}9}9!9KdP>V?q?#pDKehcp_-PSj$pFFPi$xkbWf*kv7aYIgu
z?y7lV)m-^DdeR4O56ZBx*!JG(T=EV)Qr^2p0sIQ;UTI4g0GyBJu8sq%Bbw924;od?
zL)myM63)!CLu(9T*epjH<V8XIuiZIA@K`f+<RR40rg8wWZbnrtQabd0vKXDv09;zX
z@|=(*CklW=pXKET1&s-*w~Q;#sXujzoR-&~Q;<{(C?zqgj9p2xfjDzw`8*Hwh~}J!
z=Tj253vBuMEK!Rc@qTg$N5NaM2@e%lEV}7JNN*oejK?eUsO&=uD7d`(nv4c0?*hQ-
ziw_IULgJZwaUGS^f#GO#F89?M48mjN1Ys+Fkf{X<JX>n@j$HU7(+~8fM{*BSdX+U-
znttQMLNC35Rp@2>%uYO?mE>0X5NlB_<?Bx>R9d?MM_U*(BoLHl`hw{vgRuE9fJ>Oe
zD!KVp<NKLk0qVTokpi75vjEh#sWTDTnW!#l`{s3uX6+E$ScSPta$0lvQ70CopC12}
zCzTq5F6sH0{I(znGv6a*=7j&Av1~5@z~`;mo#eYZpW!^q?Mbi{RYdQf3frPqBOA>y
zNLgjaK+rglD=G`t86t8K+pkfHvXo<Czk&asxqWeYXvl1G=9zO^qYTnI7#KSw`e0h0
z7~~2N(a35@00i)HLqm}huRzPExa<)`viY50*TVA)@+0{Vuz(#)%B!K$DfuLY=7g$2
z6-!z-jJb=%dv^n)0u@oc0t0j(zqHvJg85G`xy3BTN{xFir+cm_%O<MSG@<5DNuHw+
z5P0HADkQprk|>S^gXTI`%MK<)dVkz~Ch~W+5U@d-pA`a5P-rg%)b`AbsXy~#dXsb@
z$+#ft14IRt@<@&(`;Uc^%5@0{Mhex_m=NDF{kJEUP4K_ZWCdRhf$ZD{07;|%A+0Hs
zm)#VmCB_-z2y9D#@yEaQL}32UKd>ge6!$>|S*Y)YRYzUt04N>MZ?D7R&o+se&X>M?
zg52(|Zq5m+0b&0kK2%qS;g=J<NXaer>(pmQeRy%5Y4o{1MqVjY(f@H_?&nZ?7OuY#
zz+WgZ_U{KgV)rS%d^9Ad<pViz3$4gdGZG>!v>Q!k%aV2`LJ)yo-&I+Glz;}Ei#`GP
zfE4hl7P*Vd5TTt6RSQT+l+)Nn<eeVns5hGg1BUb=AeXAv8TIz_L4<eK5o9+Gf=+Dr
z49Fqp3(dTWh{8+3ENNtyO1gYd5OgUs%7f|mS)d6ISg1q<_j0gU@TjCUoZZv6B8u|U
zh%E)R;8XZiW(3q>*(s1H@T&B)NlRTLBS=644WTQspNQKN4e#NaQ0^C#M+z1~bwXl#
z*?rHmyYF5;DRe0Cp!!a(1&DUTF=RvDAsCH(NR}C3BT^?AcjWPUtFk`d)$*_tV&s7h
zPqN#nj0qsHY#t_6ni(XFRbq{UrU(?@VoCQ@*<t7az^X03dO2%Jo$K-;QQZ;qF0`@_
z#S^6-9raqMy@o~FjjYIyMW$OE=U*rS18ttu#7mG5MfMYDF3Whbp<7U4dm&aPrL$+6
zwNwhBRUeoZLR7HbKE4D-le|gq%RifLol6drm+CGnNSWoJyvu+abo;;q>CdMdwA~v;
zP=f2KZ@LRP?WA>FW*i12%RGgO6Ql@Mrgc!}$1ZJj+~QR37_DRow)m#laxklQLqPzp
zaB)Dihs8>4Vr8b__$jEpU@abJ!KF34Dpa?XRWz<c)dNz)5I_j)Xy#NTJu}j2=2S9E
zXv!k}RcMWnsM`durC|M>pl05AEK7<n5>x$fp=JBF&{87Wu0~y1NbXGngA$}Bqot|f
zOm+|!rpOrc8)C{<&O^44%0AlMMQup=0#F^fO8fVDOgSiep2;h}2-Q$J1QH#hftbGU
z2`bOmy40@4lm&ZC&@wt~7gLIuU6eDPVqZx}8rghj7n%+E-k`W3;4L?ZDaUtiaB_Hr
zGB2$6xVko%w=z{4eMzFh6%d!*W2rbOomKXxWu_f6ugU;K^HugG5Vc4$5I$N>bzhK?
zfJz#tr8^HvouWi3$VBdiwLD|vj-gVIwD<tDezfWZO|yI;iOB95RzjJT26rxmm=uqr
z-Jg~c(D}0h!UGd)&fAkD<lL_mf4eX$!U_~WqWdUhcIW7=!VnC#M=)L>|JBB>ep$)!
zN}y+u@{t2TyH1fH%sNM}<5cKYFlK_+)tsZbvHEexn29)SkUT&Dc3oS*`;Z_wkp~sG
zBNLJ2D3in}tF@B?8bEY|b@95K%$ngHB*|$>&5FKj$xkEoa+J4sl{u&s7{b!xgvU6z
zgp)qvl9he1sbF((i<_C~@_`}}%IJ5f%o$PRhLWVC)!R@RE`@$&L`Bj+5HJ8xdCF8M
zR*uD)H<G#gp=Y3Y;9~M=+PU`Vsc#JPYbe2jxX5Gxis>kQWq7ZM0#zweH=_~^D}I9H
z2;GGFUapw-J&&B`u{hQxMn}q~$4yJ1m(F)s30%FqfInV|0X-|q!F*+Pl}M3d1X?8)
zp9G@{;w^0*m>4f=aws}RXLwa{xKP6G!viPCE`lJGgMR=H6~7U`o08l&#7#qC69vxR
z8#ZyxxE40e1n;622MMz#s#d)gHeo*t)Pu2*FT)W?<c!FM;CNRpu9fpy3_k0%T3l;f
zYA`&gLAv55)_6BksRvH_P@i4a;_9HEW}d5Dze(Jb!uJz5<;RSh8hzb&jMfwcqppjH
zI%HQ7n{d}5sM982StYY2xisI>51Q*4`fT$c?k+OVQ7QmAI%eRX*^Ldd>Qm9{C|lJE
z5I5x9-u*<bSK!*~Jl--_;IcM{BfUi_9$Z`ppu2+a32DTt54uS5<~Yhfgia=&{t^L+
zd{|-(MuI0+%Tvh^iG7dkuSjBm0_l}DvFhZ4H3AC+2o1{g1H!cM2#0u(2tlY<XGAoh
zNi!gn#Jg3TJo}FzYKXwF3y~TyvDAw+X3|F0_{MW9JTZ7|RXyI=?ifX03>=uk4L;l8
za@)?-w&EIXnFfDyJdGX%WQtK?7v8B)Mee}|(s*UE1=j<hkj+hdYr#MeANYmggFY$>
zkHaP8BOn?XoxtkKj<g3XsC6wgS*GOMQ27ZR9LP+Wod$LpsFYD`swT(KLaOFiiaW!|
zT(z)0SL=9T3}GjLBL|F$74pslbiPIXC`YNbqJ`f?ISIJU#Uz%l0h+onLB;yb?|?N8
zE<Fa9ynC!tml}N>FWOeJrBO?BB5?=x%;b`+lcR(giMFogjx}3E%#9Rym)ok607E_!
zPukZUF)AFP0>(;Gar{W1@nlkvtPuqHYC^c%5)&9ed?Qv@*%wXFk{x~n;fk5Vpl|Iy
zdI(r6hhE$Fiw#h2GIWx|7#UzE&!A8-ooPeC{_bNRy3F8#YVdWIqs>v%cwW>;-~guc
z?YwQs@)n;<a36yXvU#@9XfOhZi5S_}236{v@K-2NcZnNE+9gNeD>*t-28um0HjdKu
zj>t?qhm~{K>5k1bRLNIDnGIccgY+_A10Em~v@U^UpaME@6b+zDre}Z&%rldA!qxz_
zN-)1#h<C)BK+_cnTk}{S9ydcM8gtRWK=3TRDK!d)KHwc|lr%I(*&Y~9=pr$o)Fogs
z;P72t2NB}4Sdb(^i0xp$K6p%9+_|OGR=J#m5YQM$QNC_t*>F<%BcXFeX}7Xr8vt)A
zo?}E)@pk=5u^0$ra6bf?0XsKVrX|CH(Q&L0m~$Cj=A@LluKA@=Rkbfc?^f93+9+yG
zA5azQ2Zh)ZM?+lN(13>2@#JFBkhw6{KSq*iBXULRqNrSh2m}4QjyY!DCdmX2L|P1E
zQA~8AhJ1rblO@V~lh%87vr+ZD2mml#r$CwOFsWhLUBa6(O_fiwEW1ZmRGM#Lm9Pn7
zHeB|TU6QagGPT74Q=L_2vMOxGGE`#UZRCv^4zxH7J9cdcz*&Tm5>uoR;bHC%iIs4!
z1!n%}q{70QeO#JGi7JM{8)no(jPO>KVkIPg!1r*SD!1Ehc`1}rfCsDfe5L87=t(#t
zJM`kRI?1G`4E6o64HifR0Ws1ftkNYJk^zDAzoBr+a+;b)copUYjHDYXjG}whqeE3-
z^MsPiaD^&#=8&~Rb40wH<jRfEO0VrrHKo|c*M^gw+tpSQW+HlrlCr`;KyxU1OO7ru
zC;}E0Jp%39)^6=~RLf(PI#==x<1nL(eR*;UB-SOivx?Z3VDlg%P>rI~v4H+8?aUp1
zZnmi9xHx=ZxC=a-iCBu<IkOm}lOzCu&<yphF&gnMz9H96ld};v9U`#W&HM%s1|x$&
z0`2mdP{C(+$b}NF3?nFuL!K6uE^b@&JT|q0<j@AnL~>}K<|JhqDzRAd8-PqzLClS3
zP{$IbqPk4<{w0nGXgN+OXTCzX6}GyR*QQE;dnn*U*UklGYoJY;K2mHOFSIEQZA$EO
zjE*A-U}KXGA|?)`<LILxXRp19-HSTqK}Of7&=#7@Y@R%&H8u+3Ajv7iG$&%hd1!%M
zRan)!;|7a;JXbD*#*!O9!s5@C$Z6yrhZJRv=E$)lRVt{V(PVeeL&12ZWdrh>zP9Vg
zps&`IYEW~yyAkBo5yMJ<IsneeQa(VSaK?sO4i1YfYp(GOj9G#0LuMPO*>b`RtPXvy
zsL5RqBR1>MxQn@~GdR=u5f|_3m_VevsUjzoF?pv_9*r3F2|W}H#)mn*rEPnjdjaU@
z#zxh#dr%MJ4C+3GYm?lBWU;Offxvx+*|BHC3UYFANCg&rO3h{s0R=VH&I+24e~w)p
zIKJ>Xo-5#PuI3flZ}tE`$j~mihVs<`pnZvy&>?s=;v(dgOAQ1spUL+N?|#10HluoA
z&Z_##5YNL*Rv{T5!0fOznsTaO3c(|QKe$(*^_->I#(=*T%7ozqFR>;seGOLyt_~)!
z;oC9Rjkz`(!nfjwn8zH6F~qO823xb^A%&fMOA!`6T3RA!0DNBJjGOR$yC`!;AiL0?
zLbivP=2;aMTN#DvTCt0jdZe)y<$Mmov<-qe!((5rlQ*OXWzS}kp~AdQn8;+F!_;f7
zI5Ql`k`RuY{_ZNbpc^t1nnW1n*SqF`{(7XF%6VuKuXoKmRYXBAp#krz{tn626=KQ!
zIveU92M|;2S%7V;>YUDBiKWwi7SA6LR3}k-NbI`A?cA{}D@nt$pmU?7LEFG&myamH
z_XDuxOoyRc6rR6#1+kyM@Vr!@fENUC<<dshrEcqzzrs|a(<g(N#`7PoyZ<C=hQW;v
zR9uW?)6l^zKXbCe9+J4|*rlA)xzz)0WKmhP=N{MC)=@^Rq9VX(3PSwc=wN$_NNBiS
z5<zvsr{?ByePP#WS6*Cqj?Fth)73u}pa;ZgYoOkiRQ~36s){ceV~F%;dIZq~QQoMb
zkFr<E`Wrd@+|Q6`sf1lw^ojTfl@Fk8h&o|y?rWXlc9n%{kf9uU?+`LDP-Em8*Aw$`
z<JQWeZ>Np8%Gz6TaRwv!z)_>UlJSP-z6^vc%7YRy_PAjM@+BweL3Od}1i>>W1AvPu
zcd+~|*ma=!hZ9V?lxQHNE2SC~{bc2^RD53<h_N!52c6*6kqGPs(JY~jXKsaT^P`X?
z>1N1(&M*ornc)HRT>HVCQ~iJz4yf0#jr`hBOzuk&7?)ljjKDy0@Fk5#Y9kXNs76O`
zltV&3!i*4Nl%|-^A;Q4@N$O*G5jzu9EHz`4!ht~~n=NE#vyD)Mcn}%{I>Gan49uf2
z)+eS<hQ8Ax-4m=0pC>@&G?Yl-s1}+}rHX(a8<SM|u;TFSl^%O{_E<(_4AO?zz*yp;
z4sY3eiVz(`4G#D9g!%-*2POGnkw9K|E*T_?(YHv$rIv<d?>Gb;;X}hNhpb}s0_ewt
zF0Ja0Y$-$h$~c7-HmLk{nV4tRJFM<nJb1gbT%>%3mhYnGUHAIgNCcEfc9D`Yfh`Gj
z)ZkZ+NKytRyn0-`G_?yI$0fFv8~ZWQ<Mitzm?=M8e6&Ih5_vGkd2|HzO=+t%ya7-G
zSC4Nod*O&h^R&EZ2df4EeP`#-b+!aiO-Kvg3<--&7gqlOZFm&khfq5ir5Yrhpto}>
zG^#jbr7R0x4>?N1VQm}1GsOa~Ot>u0(7F0!1otb@mRs8%<MJst>}D@06uE}X&Nec!
z#CHsCmNR!#RIXS<RC$pOMU9T5!Z}edn+-<oES;q~v}SAxIgcS}4XF^JRXmdL`O)1G
zpSYV3>#vSsE}ZzHS9hb4dkNsz-Xt;*tps$A(DPa>NvrZPok22K1WwEiE+xpogzjkr
znUA+}dHL(=WiJlRCC)bh5a${WiOm-Wai#)<Lxr%ZQb>8@W}vUr5EBSX4@Bbp0XoSN
z?S>uFTnt$N+v@z7R>+EL@<Kmaub{+sxkLqe?Go9NWVJK2AFvRsegR;)7?CI}X!<~u
z8?)P@7i=I|OgSiI>UH=yMm-mr=%T|WpOxqX$9TtDzYk*saB}C4l2mB|49Ai8mPqn)
za2qGYT|!Tcv_J{u*yZ3}Q>alTA?y{4HEB@MGI3Y*n2(RB!btR=m+PAp_CkGa^foW!
zo0|=bd6-<s<>Z0}IRmuLYhG3ZVD~P@6-(u)`AXLbfLJ3KL+%kozXfL^`?CmQMJ>9_
ze6Vvgd5z}Uh>d)oz`tshg^%xidabw<eS)9poQK_fhNMbYQ+%Mf9nFLWuW`uLdoQ8T
zzOl=T3F8y61dwuycYGy~t&TGr3v5G~ub*fn435?!97$$oQ8;|7w74rD=3JJu`?Vs*
z0XW5sC<sSt(C4i%=6q8tA%*(RR)XgXF3l4iT4JiAQNsT8dhkXiBR0J9MqS(-+?x4V
zSsf9@TF9Pg;_Z~^j?f5&A*a~dUM6T%_C{OqVtt{o;p<@nQVDF|Nlg#bQKLMrM?M3B
za4R3M8dKK{T_0eO)Kmi?0`H}`CO}OBBy0Tz)|93kr%UOKl;KG-yp1nM?2yv+Q3|dJ
z+;fZX^cbb{B*5I(;qm}B9{_wpT~V2p7KqWaGlfnCS~OInH}6zn8<4TQ+7Nu^Y*myb
zea<e0P=JgC{bYaCI4(tZY$la<bSiwlM3zmE%yq>+s5#Dy0}yybi6T`_WO!C65;R$Q
zAE;FSs-fwJoU0u_gC+yR+g%oez9pJ_$`4H=0g1Gu!_fWZg2@sQy(q-&Q_LQutX#ZF
z{E~CtOA1v-zoz5)MPZ!7VirPN!&&wK2|x)ysOrpaL2^Yd1Z0H1C--R2D0+rmzkW%s
zADuY30Th!{HQwDgz^+^iQzeE4AIRbsO@n!oIj({lRfuzHJP-}E8XRt2tJd0x<Wr%{
zEy0GN@+FX^ns=b+fd7D{O|T=-$V)36cSE-E(1r*YX6B|Fa~GhpAegdQ+A#%c=JPnr
zM9e>d#)1gnubD%a{*Cs~Sh<_*DV}*KvLlDZAPEOUm^E>JwzuY#G)=XyytJ!JS|J-0
z1=#w$+5~N0RBgI1euKu5A)gy}R<2&H$G4u}hfbhcPJ=b1GF^al6I%=1u`qVvHVCI?
zHPi{p%UHFfzgeWjJAa{B1d1W{dsKg9t2C%#3@nhKSYQ~U0|>wzLx`%19a<tqdXv^D
zj6(~ZvBt2u6_2(dE~iPWm@dsAE7CxN2_Agl{t%;a=ve+(<mbN2^~ELXY}6q5q~E=h
z7`PB65?N1!#-!5U>o<&=D-NHCr1PP>4l9J`D};NVQch=sB^_ATvG<LY+R4Qn<{l=Q
zWIW?o(e$qPAmbZZ4SiF=4r4_gBAE{wZX;T;-H;P>@GeUc269?wsteUa1O+Gu6(E6b
zbdxFhC~&Np#)?u4tbZlDZBz2XQOPoCiq+gX34kDw_LaNKONqHIsHg9B=e`nh4SUm2
z0RV_rY}JX>(jMsh#Q=a+WE3sLn+=uuJeqroCc(qZ?E<#%SWuE`>D@ym&*%t#=e>B6
z2G$4`iJ)agU=<7|Ii2Cn&#Y<wBzd@j@;rx5@u6exz%Ec4P-?2JF=+siIg3CbElQ3Y
zy>dzRVxZ);sMO*bPO)j|B(E@LQq;A~R5#tqGZ@4`kr*xq3NWr1q_2WBKI(9N!~ged
z1L8wR)=O_F11G$(Y+DG4oguq`6!8(6!@}Vh)+zNqvIB<jlIL<g%u8Pw1i*p<``-M6
z(Yzp4{4HmSB+YrF)xk`!SaCnfXlNGmrK=tSyfh-)6un}IAY>#TDq5$SS{TI0+wxum
z0*KJU*508boftU*r{cqO$kbW2J>qoH&`w`pjgPWoq2-A1IlL8^2}pzi6|V@+{iY@O
za>QZE^Sp+}mP0=*>oPRQvMQl<eD%7F${jeE>gUMg;S;0X9=(J}3+Mbo3?g3~Nbqs>
z!Rx?^IKxvZ31Hm7v3*oGl*CMK*hhlz0igTOP+&U*775EfZrq6wI|7#rX-yTWAft5a
zunnN<5p}9!fO8BVNzI21P{@c>hX)`^ST(eYN2-}3W40`^U^d^hPL1(Vtj3GVuUAkN
zue4n+JxtHy<JH^`sL?bGO`x-hzn~Q*s(69$I010vh8^6fv`|H$jb{vg9Bwd-9(C*v
zS153Ws11mWv>h%H`O$a*2xtEBYrqbG=p99n&$+tbc3-(ch7^<+1QBUm0d)Xqh_WGy
z>=z-xH8WW33A%!8zOHXZ$mmZ}vkG^%ffX3j(s6Kub5L?vuA}*da?h);Z#{2-FaYWX
zSh`2st7HM9$xv~_M??~o@g8#8R<r2Q%gl076(Sr7ZMCnVNKP+8kKwB?q6DJ^FB%-J
zj_Vvp3_CP8@;l$e80T*S&5b@(hu#T2NEVO^&?D{MMU)LLlte%9GO)^kdX-e-TdxA}
zY7BVWKr)cJWY&OE>g9`|elCP$3kk;?R3%=Ap`@P=hH?#4)<S*(Q^uWr`HHto+5H`^
z?4+fyd;b{1<;$PH{QG7^eMgW>d@Y;2T8&@@3*;L|wZDQf0dVAqN_6j6H&+l8`&?o>
zR^Kd*MJVO;=s`W&fpfDy5`Cdw7$#MlpP=@0zu@+!HyMe>hb$%=QXjp^7lXMsUvv>Y
zre|m8zu}@0a?LmiZCM^W3ypg0(fvA({*9ym<fdMlF%M;-?xh)9<rXh5&1Q{6i#vLz
zRRQeh1Fn>myz_GO9*FB`+p^<><7{#I%g6R&yI~S45)yYr%k`C<5)>0EmOy=a6Pqw<
zP;@w=C7*;y+54&-;sYzN_w5cYtH65bgF4<}5P5#lDB#j2cD!=QGV7u6nC<j<VDW5Z
z^|<s8Tzev}SxEq|8GzI6EB*~!a~$bbvoy*bqQ(%Z(>2qJR%a^XNXNmy>s)l=U1w>L
z&e}|;TC!i|EKLtTOZVFm*v=|9jH@lKlZ_19`5d*faz-jk+tEIc5cL5FD;-}Pn+0CN
z2R6QKYJTgL(p(G1)V(e1J<UKb)ez*i{(R*+Kd@<P&+G*fU(8djg&HCn9Y2|Oi=6oq
zr(?>$%GsEI#@M~YcMR{D+0nTkyG4C<cpD*w*kH_!B^x%$6~Oy6^vLxzyst)uho4}H
z`N*AdUakQ6y?6HRB?kDEK^#j<U%6167hvNWuUx1vFvsnKSFiNj7n#gerHoQoXFJ(_
z0&f2ktSi6fVBCID+x(Sq7HxSWx@w$HLEs#qxsf*?+rnKv5pz^B{op#8zZ&}$nA|p~
zFYw_xc<o9DrIZo@*=Lv9xfn<{1(CNkZsYpk%9S#AK#%EW`+QV39D408mOKAQX=Xp<
zAC52=lrU<aqcYynvN5p~4>%5}BfrM^SRQOX((2U8X0`hE@ty}L>*$A7=>#pq)fCFv
z=6JrtWbz1D4HCGG-+24hc!vwlXItdi<wD2<dhn;>)r*(=fgl`{ZwBHvjOo2fRb?m!
z*ThW`Sm=0PJJnzHPFa6}J7ty)+to6zwx60og65D;V(ZmJ5hUz4!W026#J^=Lp9fXq
z@C&nctDW-eR|*6sXj9vTVDdp@3YBDex?WtV|NHvIFFaa>{_cQf02I#RuXA^74>ku4
zP*jJ6<X6V{QQdC9euBN0k^!iNXtoE(i(xt<ltooXxM4D_Pk}55LeTo~Q1=bf(Qq+j
zGKOAXxoAq6gCZ-0?2Fe-aMNG~t@Q=Z@oKU{SpUZ9Zz>RNLGP`$NlnC<Gh55e7`3L<
zL*8u5IZ?-5XKAJ4<gYn~?eDfP{^~=!@{L<Pz<zp6{NLvfF>a4n|NFUC!}c&UJzH8W
zNY`%pRJRWPW5`ro?H9A5mFuE3OSuNB3*9x7DLsMzI3b0bXJjQUVcj@T1VJ<r5st(z
zkCBQ5QXsyL9RxGh2x^JTk)mo<mMjBw_!`lJUCfg}W?oQb05G>KH{Ns+lq71r(lxUB
zFGa{z0g!y`?ViAer5k}u3>;<9d(5to3B9m+n~>_4*d&D+VWaADlxL~}^NL9nBo{;X
zw{A~&w{A}Nc3d}?i10j;cI^b(CUI)(J73O{Snhy;jTGCUYaW@$NCH&cDX3V3U{+k^
ztx&-~!288NA>2Pi7vPLgte|fZEiL#nNO;jHl+K?-iedwrzZ=PE!N4=(L04xJH+qyq
zxLW^CYgQ0H%vHq-z=~1ctL(A%2JrLz00MIyHG#wme2`_ZBs0(h+#$>sYQF$JfmTqx
zyMhKbsCXb(yIh$fl@z$LAwMbTuy|{rt3fd%LDWo)@ko7EfC)tI#~`r9+PgAGta7x?
zpUClnU(Q*&h)ae*-bjm71}+uoPwov<46_8t`De%#Ds_Tiz4XO^F$F(N1V8w9b-rJ@
zl!3LF$WU{-IFfcqUM6@>xSI-@+In|zv6$mwKD*p5!z@aF&e!_X9g{0OZ<gc@{s-0>
zMrVbzRot+6nBW4sD4Z=BN7Lk)QNwAn091?am+O|2l&HBPT2E{)f`f@5=}#~ibHy72
z*OHY1*d+$=_8?Q0c(rv<1aW>6m(Sx8uY~ZkJdDxShnWiS=6N;xy1+RSp=;!k&)qLM
z@9hX$Ff^UV0N<0VM1l(%u>E4j1taYMF0}#*!xE~)lMVV2b=@eX=O}`!36J6>dNd*5
zUFYK~^99<a62Ov9BJ`vOX+}<smch_AWuQP=O%&kjx_VSJF0K*sA{rg?4C1vHX@-KS
z9?7SattMoHJNQT%yziPSSg@*$g|rkONmf1NWf_!~fg8jQmi~5@T%1mZl@ZIi{zNtp
zlzYFtC&1NFB64LSs^VjX(WV0Jk{RrSW=c3W1&zB4RD8os$=gi1m?&k?2&nsO>Nqqh
zW2oc1Q25{!2<A-jV-rvLh)qdaR!3;z(&>fA^aZRoWg@jMq5x<#p>UP!s?R~vu%PRe
z+_8|KK%AMBc<EnvBt3G?%S)4$6RMkQr?y63G;~bcHaC#eq<}X$)@-qH>*?~6sK{65
zOA<qnY7N7;6a`I#`vG*ADLk0%!_x&B7s$e88^MVh_N}E4P8az|x}Q;VnH+<@!Cc0M
zpB{x7b8S)S<BK<6(uXEYT7jwS*Fg0XTb37_I%Jh7c<$y7x0^Eri%I;3TAWpQZ7lC0
zxT;3rVhFi0EhC^d(-{fJcf+MosN%&rlbZ{i8wWoy`c)eaJ_1F7GQaD0pwYA?l)_JF
z0Sx6}U&x@3LA|0OA_?BNJ5NfFs7gdgWR-2ojP@;4=EE+p3kz4NsFY*6GG!3TK_@>e
z3xtY943y6?r+MFQ%GBXbEIwm;Cf{D9EU5A@*MKg6c7}iDq$xXd-jzC};B&_1O%OfJ
zP+yT&%5t`IA|S2iC|Wf_yW4ybYnDb52$h)hA@?|>rHZGJ3IcHQT$uM+I9Foc8NS*a
zTl3bqOH^)yEL@9ZR}mH!r7RNcKoAzI=71-rjYic+jZ*2#SBiIYQnaP||L%#>J}^-t
zb{PnBBcqIp3u_emP(qD}7;%)b<`Ogq;(SdFB;I;2MGESpP{YfDt0p52flKx-&4B4v
zO%hW%<*{7Q3oK`>cQ>1qWO>`mmynP{n}zXQ_z(I<<d*M5Co;W$+3#05fHrkW;%OJn
zJ+8<c;gZ^hk_)+6(l+-a0!6P4YwRR8;dG6XMt1u;sLn%&6Fwnz2LQ(PZ<lY;LHrm#
zu8>4w?)kU$^r_*5pf&7evS#FvWipYMre=~gLfa+nP?yBjcexb|a}+U7Dm@{@Js7r-
z;282ThaMKrScWIpP^)HUKR@y1r9z2^Zsut@6+|qWv<HXspDR1!u(*7fHo01u7^SS%
zq!n<%l?O24rtk`;Efr!eeaYn`=yQs*%rG!djh4*jmp$kx2?Gst#{&?VioT<K3KJ6C
zZbM%Gnq0;=ie+2`RC1q$>sLI{Gf;?N&b(U<L4`Ci|K7I^#7^Ov;NIG6z#~Ir9m(_b
zpcEGi^H<{~>}3!_MO9{ZNLZ7_-5S5h_itV4&zp6Sm=XlXhA-_^had4wy(7v=B`yb5
z_w?>Lo(*zoys1`%L5^4lgIu<l_yohZ6&F2-6b7SwfA(PrDNxoIppE9T`j`n|m0*wu
zADS3ckY>VXTd)HaWh4>V4cquN$dSN?-G5hUI|fZ<&mo{WL)w0E^<AE@IFnx;6yyr?
z-XhbJyzI&;#s$w&-dv_a2+G`TME?Yo6nM+j_-?g^9M3d8-d2}NkuqkxWeyx2uDQ0;
zWw`Y?Kc^fW<<Q9ioWDNK5%+e<5ci*dpci?#Nj{3?bIC5-38R*%-pJ*=RySjTLeLSL
zze>5#5u>*99L27aYt*FExNcsTqp(@ULgf0pk_6*t?wx}}b*|<Z{2mCRf&w`+*mhW5
zUxNrvawip)aSiYnRqBtZxWb<tX|-EL;dD0mk<6VaFKNEK7r*SRZ$y<Zaj&$kH`n~b
zCo=?svSKfH#P)uU_0O^X*kk==P9`{FOjcLB31b|RrF-4utL&1?j{ng$t1%90e0BI<
z1Jhj&-$bV=kk@m(g!McH+E>m<R!SawlG=4->`pjzP_er;H3_R#QtigqNA3>CceoCR
zM>vo22;_)`drI;rqYnHDpF8KCq?@Fc8KF^#nO~C&OI-xr)q`#e9I4e^eI)mAL_FJj
z+2RrAn6)f(M+6!}aZ7k|O?*^Kkmn*C@iJ#e*s-pN-+;_-gOvRij6f+0NAa=3csd?V
z7jz8DTneo2;_jkPUs+ef)Ikc1^X^L?GPwB$Q|y2E<5fVFl`v*r34kMuDa38ejG4J#
zcC;f{TQ9;k{@dAS*aH6ge`Txtum5-c=WqTR-#iC){Kr53{ono{zke6PqzFSYS0O57
zj+Aof^Fvhd?+N{)oD0^fQdqQpP%n*l(tCei;3W2}N=^UqoVk7`b&X8`Wv1Bc3p|>H
zO$A14ox5s-F<?YrS!QPymC(5yjOZ1TBi<*Zi${7Fr|`|!4Byns!$)f`UUY$&W)geQ
zuc(_GgpEd?yyW_|EB<E0)&c+D9a+4nQ$eB`UYH}@XV(0RIPuV-bF=VQSS}1dDf^#K
zAo><qNk;)o8xkc>G5FjGyF}qs;Do_luF>3Oeo)yc{6<Fn+b%h=X^>H1ImN85lrxfd
z%&%RtWz7hELtnUh#g98hDl5%3la6tR8Ir=UlaJnL4v9*8`U=UraeINYjuT9R29cbl
zDWsLPOmAe-wEq4$8@&ny1eC<r*yyy^?&gjkx{l*^Veh!mN1YAegHnc3-XV{?U*umr
zZL>eAXH-%>BSQw%W1-kBseBX&OtzFxe$1KD@p197=sq>~4a?~+v`v_5(mQCTSLc^H
zgkjW`z3Eaxv?<R!c8@wCK*$7cm#tNc54}G;8j%?gl}uoVy-E$aA~Py|*`s{?_QXl(
z1}|3y=p_J7f(2HH4;!*Sfrh;;x;}~~1CW3>F||H)(eUm&8ZU2u(UJBf&bofvMdLgE
zm0!p_i4^!`rcwGs?;MXtFfN6fmp~81_Bw^C&XEZ;`Rno<kTf3LLXzOJKnf!Hh%-IU
zLWM~A-`D9x;St|+-Y`kvu7vD5!<^?;{#0o5f`dC)zZ_ceda#8h)szh);+YprfXL+q
zgAtLpt>+gpLjb5@aq(^&$;~E6eJ^|ItM~T&3qQBm=yR#j8&Q>h{O@1>`OClk_<#N8
z%fEj4;m1GCKYw^w&XUOw6Ob)82LR?7Xw`V?WMMzlUua*2>ZVE^s4q)m+(f#QHt}W>
zT5B}t>9JpLs({Wi=u>9yGgV3D&vEgBAQlgdke^ycImsQI;6X*F>+%fw2MfL<t>g7W
zq7&)l?e~Lb;#l`{?5-OMf>b7*BsqALy*`jBa(7T*gZ*sq;>V@`E81vkK7f&9gq4>?
z{Hh{;377%YcGKGFbR|A5BwpQHNtu7Av12O%4G(viq#heg6$FtLYN9p4t^gvpR@O&s
z=Opzi^ic=ONgpLYkl@4y5K(j;eVWy#vYZjXm8gOKjM4328!V419hMS%Ot5-^F(uGp
z8X_H}6m*t(Jr(rD!@T^b!B+DKG#-L|rMx^M{WA3xN1FmqIWp-Ns^WsM1hY$!o!af}
z{UY4DuFMbg5D)W|=5CW9!I8eE?hs7R@t(OGlcfjRjT|UYJtp*%Hw)Qq1hfD?`#-0q
zuAt=?=t{6CAX5MoJh#^y6mAU;iACcMq`r}|_wzcyr_P!Y886c`?P8k1#C_1kt=ZV-
z2{3v>aTY0uqtR-9=9G0QeNE6VWNYNeqNgEO4#(8e6o=NVBR#hZh9}Unr9oqUj)Uw?
zNc@m}a@iUXs>jBtA`L_~QB+8__)&qZga#nQ*rrQ$qZs(VE=V~Lf6!J-YbsPg^2xdo
zHVN8#Mia5}RD2@Hb`eHM!lnn4KTCp%ACCl)LSiQ^N@tv=hDhOu13i%upch)7$}(+i
zfUUaUXpQJ+lc!fi|4g)5N^@{@*3RhB)eksB;c66t3ZC}|zgXcOVTlQ0bxeQvlLKZ7
z(BPPU3SuApkYV8wy<s$88c^4KM1Yl+jl79b2duB(T!e`jV@cWT0il}dsX0szt}wub
z_JxJ|LW>?IB3pez2ydCZMqAdbrJ+48{*c1b>{sXJH?Yp@<qGr!7X$a-wwo#NFCIP-
zs0j;BiTSK8ZVT(Z>Lf@#9UcX$h0_3lt?rB%D<uZ%vuy<qeRF=JBIL|I;MB`Pu@2fb
zV|R0JWFx@AxwV0{mm}BX?9dVO=6>37FW{F=e_1EJ!Y5K9++uOy6M_j+>h2v0D0d_o
zb6&Tg?LZ*HJ7;bhv~88oM&+V0<9~nBaqTRSM-Z~m#KJocyk{jVl~h*%qxGxK{p)~U
zyd!Mkv@#ds7~KaK_Na4y<biwUG)+{cz{HI*;wwoO5WpvE<!JQunZL@*=n;EbwFBV3
zA9WCW6S?oTITHcC#*h|i3$G2c1luWFEe42(iVd!)$P(R#ZwX}2a)WunZw9jj*DI$v
z{{T>?wp{CBWsNioYp`kTB7DHwn%+BcXu*u;5Qv^`T4n@&Cp3{9)x}xb-Cc&~r@c&D
z*n2E-<ta-bhjMmvt(J}DMJcAwpb8;acPeqt+$f#IV!h9Kq|QW<WIVu7*55UAE@n-8
zeb#iuyqXSK<1NwB=Lo*39N3SvwFUYGopeE4z#yhuaB)66KLZ#SGo@Gm2rWKN|LTki
zTnhFXIACfag7sgU3okTB^gf$tL9y56v5M6SVOj`egfq?q-V2+NT-h{l;<I^^Z#{2b
zVfno}al9T5s~4H9KVb|vx5hJ)9#0yk-#BrgWpiA)00wF#ePKb1NWsgDj^pOA3?&6v
zB#(ZJ$ukf+!U0f=B(7-&CIw-@x}++1osUc%P;r@=30MY4=l~#@yJ-Q>9)XA|x1Tt<
z`chZ2wj|L1h9_r*xX6K)oHO?$%ns+UZ=O7eVatsK&d-Vq00PURZ$u{-2aFF`|H#6d
zm`CjKbK+FR+4u_lP6>po3rq$(SdwAu3eIQdc)<AsCs!S8<N#!8?#0{4)BJ{sb0e-_
zCXoOLnrK&Mj%=`CVQX$e4*3IWh}1jI9MFsj|E<0WL=q0AB7k{g5KiDPJ^hCTZl*c(
zbK+lBHXwEuqOmyNN+&MU|4sv0nw-~@q9&Lww)@SREZ3vpp4o6!W;|T0^huCV%v_^$
zW+SQVVm=Mq6#xP^M{tTo{)c(xAS$c5@W^01GoMuOGAdcJoF_{H;5evkcf^XH#mR-&
z5Z?)>NgO}J)JbmrXP#1l&kpKS=kb{%K$ru{4BbmYVE>_5018abcVzz|&CTYH+k}48
zemCzmGm*Z<IZICPft=-x+nwB5X|;=$G45?QBO$3A^SxZj{}XvYwliplmkfL|w$NtI
z6B@rh%kcvzGn!IjZ)RZTCHk{x{ta5)k$>k)xCDMoTp)<%80<pBBHf~Hk=Y~LQ2>+N
zXJx547j6oFK;pzbLAhl^6ou7-9OPYy*7qKRL~(Jj$XB@~(5S1^)@(!4hb}rmaLy^l
z80ma~SZT|ji9wSsy@^kZ+EOF+Y12=LD1Icds3ai+L^I_{5IZ@S9%iM3^A062XS7z>
z8z;qI9yZ}1<W(w(ogY;6U}!@W7b_59CXS<WB=)>aJ{`ES9+-x?I)VCBRMwlfCb}uc
zzd1_)gj9Ue!@B*5s65kN>>eb*Aa$rdlmVx?(jjC``#HcIvhLP>3TPA=Po}qR53de<
zh;CwM7>l?;#xO>HyAnSIjF1c$rRJFX-dH}}lK?Ag4kCEC9FAAKRl@w{^&h1my0xUZ
zDjpQ!*+Zd@4yu6HUP_DRqMa`7U>114CRcK?#xdM6a8a%jzhVw<WYr>Li&cj(e)vfC
zsJJiApznPrIv{FM66^vo!}Ljsw(&+Fcn?pXIu=a?HlQ*Q^bZh-H0WNWwG+&2<$#<c
zJ2M<p&_7cDlEHB~;2K?nE79(hJ3$)T&4M~IDhR=AxX(-nB$br!!v+i_ND1)I3T`I5
zdN&$>iUH4+er#hZP!E9Py<Qpbc#*sDZmQ}B5`!OPz=PvoIN+V7!LW;7>7kzT1Jp1Z
z`$^(F;cx2SA;8%_Y0;N%NzY(>57S3gCzXJN7W{Hfm1?I*2c)vvHRMHW=VwuL6U8|R
zgAX^GJ-14f@j?w(jg|R{@a7oYUEC?04Yz^eoy-SBE`{Imj)D1%=pK<L6wuN5LRzG`
z&7ugIQu&^wz|SH7Iplu{L;f{xAhg>+Y<0aZHerVwlKU8*iJ)3)dm<c4NvzDdUL%rE
zir$Ph4nH)CNp;ML+MbuxKtn`hXfwV(gex}u8Tm<s0i}0>&6qA4QZP)!AtBL0j-bjw
zuZ`ctVsD{`#{6N2jwIv<`(7piO>D;Gr7Z~%r{d1MU?*7sX$nRJ{B;UhYD_hHFn=v1
zM+Z<LN_T<9KBQFRarVX!#PG>u3s9{4aaOi!WLz*ramlMOnED2q*RAh#=8KE$NT_pU
zwB1)TXHfA3m(&(VFjTqVDfq`VbtrdiSG}$X$4QD=64gFH5zOUQ6v4g60TvNjb(Y>E
z*5u&!2s50IZ>0$K`%nbqA>N$`h*tt4Al<?rnFx3lX*j>~hrbgd=WpkK=SVu>o&SgR
z<Z=7I`<;+LL<G(G&F5iD40!_<{pQP`=YRhF$3M*v{)veAzx(aq|Ng(Q2sjU_hb;`z
zGb8A7)e0IlO#J^ZJi@uz*<trzTDAZ7Z~2vf_}_l}cfbFKTksk`)tEokm_OB+Kh>B&
z)tEokm_OB+Kh>B&)tEokm~XAdoIcf<Kh>B&)tDbdjoCicn7;=#=Ju(^{Hex#sm5GB
z)tEokm_OB+Kh>B&)tEmOYCqMOKgas#SpQUG{#0ZBRAc^BV}3i-;``J@em@kVzYK-=
z|Gxb5kAM2|XFwsoe;@<^VY@&&oUPC3)u~P)8W(63VTtsiQ?1r)2)pGcBQarVQ9UK}
zciI6<RXSC{=X&vE@kFdg6^r0W-=QjKBS2k@T}n#MVx=5{dG{u>&rSNvPUFN-J*<b$
z?~9K%-*+#-n4$>94nAHA*ngrx&rQS7sY>cmL;C~-xA@f6azjNTLzX&*SBp}yU5VnZ
zRBmH>`9TlyPPwzQDh?X6>g}bUJ?Ksun&Kskw>)T2up#x)pnme=lSWF;Z8e~EI$n9y
z72V*x%G&A$L%J~r$g>c}0hLos0bo6fDhp_1h=|Uots68{AQ;tP9Rhc)qYC+^_EPQb
zd{oZ|vm4cMy!^ELs++oOs~*Vl*%JW)hd5U6zP#ya>)_k5UwGOOLE+{Y5G$5f9(TjD
zn>jN;1x{y!A_zbskx(0S?;tIMz=J~<+T=3=H}WHw@{RQ}A9tCdp84aR^Pw9Rc|O+7
z%hAiOiIu^ZA3M5Pg{V!0E0~0gH!;d34Rh1yZX6N0x$4xrpF23Xk=>iBVGdeKIiRA_
z9Pa?hMhtR=gN3C_K6Z5>@z^2e0wwj7_1G(ETL^lt;*-%=s9^w6^zxz%cof!v=s|mo
zf|QMX>xYO`uHhNlS)KbmV;4wTOlq~_J3G&)r?QJ@g90K63N+C_0C>u>3x}aK05bqf
z(VHvyE_<YvDnB;~+rEN3{cq30mFEGOy(1p`tN;8LZveRdl?NP|UwE!I7%&9R{(rv>
zC=u>HQ+RP1g(M>sIfXX=OM$!JUwoq{rnL}nC}k`Oye%&K?eG3DW2D~@ft&K1<IZq3
zVERC;c}>@ktd6U?eibXGbp8JFEcyv&3m@4Kc~bzy|Gqd5a-*~3to_EqOLO@q3W5I-
z@!`GH+`lno{7mV8?v%cNEu}BNqLjX0P3dnEv!5EhBMQOUUmWW5MP*_UssM;p`*d(Y
zqF6+Kn_+Jl4h?5+1<u)N1Vv?u=xPqLHxB>IJ|-|y+QiAG1&ypd)AFlKGtl&oFAjgG
zEOO}0OSdJ(6>)2>twfqH1uq?KOI`W{1m^l)u^<w<0zM2u6p(Wy6fsB<X2vRwAjf9j
zU#?R3*f|Fsqngfuvn);?6|kZAly*#iL^heyaAGq7u}!7qR)7U2reprpOR$|C#i3WG
zDy@)5RFtc${z{0&PzzJdVMj5}JEedc5}ml(%VcC%ph5I4^o#>E*z%eOnNNF;+9=Ep
zFt-L;bAeNl<{Tye#sP4VC2P`2+D8&4>5)0b(MyQX6;y5wq0se-SXI!!nOWc*M{Tq_
zBv{+)0X7#?!*BZP)R4kaT!NNE0GWC6!d$om+Xwo^a7`w1Cg!SAPEjb8Vh>QE5(xM$
z^CA&ijP`}*1xf<Ie^_}oFVMorz<0dF^?8vbn@0%}KP_&=?*r2WT46w4IBT9BIR|tj
z>V8OrqesIlr7GAYBbC$GX2CotoPL>A`7D$Os`dMc5lAT|zhjY8`P?uutnBEb^6LS1
z-ZTve&z+BQF8KU=Ubfm0K$SL88o=P-pbknWAj^@x*<8m4(zB|GVN0FGG&?b%gJX|o
z08daBb_{Pt%oKJmSWvy#CI(OzBP`i6@ZxI2#7K)n8bS4hy8<MLrKEv{4;^SIfOo&}
z#F=7CTqlvR#~-PClQPr1DkX*cWx}VoWTyEsR?|+hb-OGTFoI+pZLYtNg-NOc%K(Pe
z1)yuL8cY%@RhCYNLFH=x3<+w@SEfkeWDs2NRA2(<b`I<%5Hrwk8}T_DiSz^TzFoU=
zP(c>>j(CRm2y*{$gsb1nx)mVwpbUgjyEH8eOvkz->rN>r)J-bP4O}KBVyvP0X+k$<
z0z`oWUMex#ZFdfCM)6RFP>bh_;vy$lJ8STDDcTC`6^*C`>Wq0Cj2fQ6ZDbEdMh8?v
z^FcjD_6@sl9TqpeBx@d2WuaYJoQXqoWF_km$&AhCh%Mnhn=`=HwS&tvA2_Uzga(A}
z9%8>(09y8qDNz9n`%+M*j)E&F)3%B*fQ=;0tg4-I=7gbX6H<i<_d6qVhH$xl)8=uF
ze5Ve?R~y@jMU=DvjXzyu8}PU}SsR=a#FhqzuCrkM)xI_wrC5qwWAo?iYl9t8sqwO4
zn}F3uN2KoIN<oEOXR6_EN>9PHO34Hzy`0aW0EKZm0ZWun8rNIf_rfc+H{+Gq+%Eg(
zHcB4}P`>K2<jQKq>K@X521*fLl#K<eYX!Tvv3;P{LtykN98N==p7_-24MuQaJ?F@R
z+gU2WlR%oTdzGINNgno=F$_stP(Anf&P0We;APNvkKis%KHPR8nJW@VodlhjF*T9Y
z`%MIu%|S^835#X&d9YW3k$0kjRvLle8j8zUV~K#2eOtWHD1p?r35H|8A~rg@GzNvd
z1;5LyBw5W77|r##Vp8(Pq3h(FNV$Jz-g!8}0`n4OINSb?5cY~2d>VOg5uQS$(uQL&
z=0xNC0y3~*AcHfdM6puU75t$twSI`EArXfCs1)RpEX<2`b&t>9_V?)b`<`N7@FKFh
z2~T7etjH}=;23{r$8WVj=7%(L(ep)IH$3c?Hvt$utL)V&Q0}yFv=icDi5GeWKysqG
zd`^SSL0>qrd0y~QOp|&ZXcM+aHqXT|$wC8DH-bnga2rx+RLq1t<>rm=3ONp)eq-l@
z$IUs3*e(h=RTEKa5W^fMCQ!m|q5;x8EyL?mhJ0CPsYjCNAcB+VWKc$qE^)@jcXA!w
zxtq$0NskJm;q*E2dxwNLMNw3fgtM2q-fQ4~tmul_C!}z*+ZY6?+?uOb;>oZql6dp;
zN4)i}cV$6$SC<B<*4m+Lqtg8ZT`4aqifyb~hbS%x6r;Wysbk>wt$bN*6uGuzU~wyy
zQ_Y16!`bFb9Ao7Ks5wyezzY|sH^t_vu-svct?ArdY6WDGl$ZDcn%3CH$6Q`qntG<|
znHX>vL+DW7E@6oX<1Z;9GRWdy64Y#Lonrfgxhc#Jl9DK;v`87MtE7u$o1DI!J)2}j
zkNQiMqdn`Df`?pPsv)zl++127<UpQbxoT9YQs&jW!(K}p5Nl^m?{BrCg2xB7T@nSM
zI{s%EW9D9s%oCe|&b;=V?`Z=H`KJzr{R%SVW3RsT291=nwRJ0l70DYrxCO2>oR~iI
zsM5rShnLaONaf$2@{rCNT*;8qXyeeFxqc)O9t9#FW-z_o0dNtZan|o-oUnivMMueh
zQ`skQ<J`);BD+*lW;t_f$AI<(aA^%%kLa<1z`8C92gDwVNDg#yUaZ0>+r>;?{#sF!
z>RF4et!K3!m7dgkP<l@5&h(Vx30VQ*^wmlNz_oI6xfeu1+y?^yg>igF$5T%zfyT3c
zM4eIkcyd=8RFV+pC7o(eF>XmoWv}=86Vd}Zt|xTVBU(>L59qj_(2<X5Jt3*bpt!fU
zM@1fxy~LyZA2B;Nxy`XBQtyPAp(-(ZQlR)1=T*+NPV)5<qoF89<H?%VpLt1%SJqwe
z1Pkdwqg92b6i9;1&aziUUE40OQ>wA(uF*szE;{p>kk6U8_`9q%<m}d;Q8LZwEHss>
zs6y)8qeR0BTe1Nt0Sl(k%vtWz;DB_#%YQ&D9<}ZIk#+Q1Gx^jAetR={z7Yn;J%SF7
zZ?;hSjGGH}#VP~mFc<5Y$HWaOoz}1C&`Y_@pBk%xul*Sn(BH@29Aad~$`GoRj~zyb
zJFVoXqAMC!Ct9^S#CSz2e5ELybn&O8Wj?pP^=4p#WZff1?9F3Qp9f2j#8^n}DYO2%
z^}V+>p)0tf&z=%zu`vr*(N04j6o9oLjlnflJ3?mx(^%g6oF2CdbXp{|kA_<FsXzAo
zh<A&0dR5)YP7KbYu|t{MC~!ZFN%Y}<=Zxn;>;wL~NdU~ZKO^1FcHL`#-Z2EONW_eN
zr<5Lj<xKY(8O|w|S0N&dwcaJJpUt;+dJrg9y{JbDQ~**|@ED7ACNb7^oOKu-WgSG`
z*{FOW`QtiUyoC6aJyFA->mLJG$Hl=<R7TLheLo|zG<I+z07~uAM`XVBh-}jSLSDq3
z6EF-U3PH(EN=2E_zWForBl^<DWircQDkn*(R#hq*RtFMbb8QO>ONBu6T-^6;{xeDg
zmzLbRs^hw>qpoXR7RpcwL9mM-0BnS;p=#L#c_MgoDY2=<38IT5ti_Fahx>hbYXmCB
znW&^Jqg4s{?ypYkbP`v6I9dYPrk6|9b)k3!>RWD7MIpGkUx(FdNR>6{;T!<eb||Zi
zbfT^dCgwxidAUGa8VOueiXhJ#bMUySb|R-ooeh!AZDwiK|26or@U0^#X{8oCtU!U>
zm|MH=EJa>JZAchWJ@zC`HxDR|<w^6fCDmdx!BVzYvz~dNWbk@h^<`8S>e64AFLW(?
z;xUr-vV_l2bzNl@pwv*=ZYmE%2?cRO(SeWa$ehjaOWRw?Af1<1sZ$veRU$`T9&6OX
z1M9{G-B=1aeZy;?UV27_fqBN<2_7Ow&ncp#lRNee^w~KDEX@?$z3wl<@*4PmDXSk;
zwXe<D3!RtyM&cCS>vW2O!pdV$oOHKLwQxPQ)ioIM096Um<_l$U-1$~)yFzSUtqPdr
z!3njfQl6za%srwLj6|r#X<A{}Qf5f@Z#wsJo!o1J>*PB3aXa}@XFsmfA9eoYx`3mu
zU|j&NB%oQ++lgC(qeZ$+&W){lu^oPt(bZN|pgfRy@AW}8f1jFZ=SwBaRNK61)SR1C
zRxuypCz|$a@gRB#lI``5Ri<ln14_2}zv6#irqf%|KX7ZuA9KK_GWwOW|7A9<c|K4}
zg^E6Vx-%8;B#|&RA2Yf}pXJWUG&lfMoh|Lbr*OVs+rW)`h2QEFJW6hfiLEgzc5|p)
zpn|`|A<q=I<t)9R$74zVA(UJ*7>q3>-rm)Od~$M|51sb7&a2ZN*Lii?<N0|3QRx)$
zT2S&qrW++W-FFigv0a1LxhYr3O;He809FE3OdK^0JtVAD$=%VoUsCB+8jA#Hn48ao
zEj9=XRFT{dkG;1AZx|MJ<kIq5?OGLV+$P=o>MnT4*m2%E>>AOUe(d>U_bfTIxeugG
ze^ULZg=7XCk~zijt~PK==$J$|ouv{xO1@hVIG)n#+JvGKg0rgK8z{JW3T5xs4ha4H
zosvDc<OH}c5Ct?pqbrIBmMXf}bt#DxCH{P=iFN{vLtzpt<*A{xZV?AZXG>m7Jf{lQ
z=}hUSj(EDfNW$>CwM5;=Q}<D~w(aeOeKvC<J&uvn1_y&gA{j!-_gNZsYug-LJ^)UY
z-gG+&$7TwA61^^EG(hhw@E+OWybv2}9>~W`tI+7^2?~#8*dx2bWTZY$TFamDet=>1
zHhiJ>|C}$b*IuctIToV?A-hrhHf}X}9Bqr^@ogBT>gTw)f}fA2%Lqry?da>)U`$lq
zK^)y%+A`m$>+hj@im7gUc<2Tl{pWl5-TMBCK$V$cQD*e9SD>3W{*lhHNuD;SO<^ll
zzVWDAH1s?3!ls15WY?l3V?murM4YcF4=ALh5)~9D*NByZsE&7B2h{OShXX?d#_YR*
znF{X*S`djpV=#0NuBHgNaCW$>_>1h1qjkI~=~@$zzm?kzTDKjFmH5xXAaxGeL1VNK
zvPL!PQ+FJ+!>zwymip*DoP6^hCv+&Wu!~^|#2>c`U)d!=oE8MnR6_@AOMr?fSQ?}Q
zc3?TUI^(-lwa8pI+LTKguLJ6M(S<K{pu=`JjoCrx5XgxvgaZ*+?yz?xz?^7lm+`0)
zg&zXS*&NY@Q{ol_Hh5kI3^7(!CS9$f?sLz;E;W^glAMDde2CsWGpb~jZw~L2sR!++
zo##B);-tmi2F7F|R(Gpmw<w+DwOpfp&cBcsF(Ln7D3Hve!6Ve<o|(5@%jXm7)Tz`G
zGA%cS*^UXiL3d5zlg;$Kz~t}2A=kOwDSbyZj0=$R2LU$B!MBprK$B)ThcA)*d?Es!
z@Nr<DHm`$oh+-XU&i;K0GIT7<4h(G?L-u_+BUYhWoPBdDS(Ms3vxc<%&KkmJH~H*O
zEWG!zY$_orK3v{<`+#now+RJ&$d;h$#^s2;+rrv-CyAyjkG}WUd#20?Dp@<utFJ<;
zeDa+XVJeHGnUt&ZwYI9U?EFF_90WDktf2-faojqkdpuKOr4InjvIZ8GEG)J?E!0w^
znS%pM3oFjCNO;tL4aU(U^h<(gU7@%!#919Es16hpdd&~DYC}E@53(d8;~#q#w40}G
z>LQSgVpk)I2u~#1uGOpSD^fW^_M$B`v)cdBgX%2Ywa2wT|2U26ZC5ysp{b+)E7TPX
z8l`anL4IYJnuoyi{~@tWDO?_72KM*v@qsa&sJ9k-d~jO9%`>%am`c$?Wk>l^U4{=t
zqZ;L392!N<i<mD5E|e}wBd{g8Q!@%zRvjU)zs7@%+q3j5IYt!vTG!*?pL3|9I68+a
zL`K_#XKeNMIF2Bi6jHV8IU2`j^2X|w6*alt_uK}{Moi1DZi#q79EScqAx07ApnDCY
zu?HV9cLFd-4HR<nCnzXf$|9_NH?H}qbqMGIIZEc`BP@`%T!wyrjJqPb7T~HypFNDE
zxtdaF;+KMPuZ^M}u%&^L%3}}An-`y?Q^=IWtL%ezrebH%V{0|;NvxuVn{%jDD(&7~
zA7nF?m2MY7ptt6o|MFA;K<PiaEbdXKJ+AW}Pbb!S*J*jyb<$&$n}wfNXZ4fvoZBfK
zmk*L?gPo{Ycv*mI^RqOy_E(@Vl#0nKklx_fam}x;5HFNml?luEVM%jPycYGl^chvW
zx4ib$LGy&D`J>l?>%BVs)E?>cHTYDDu7sxI7sh`&Nd_Q9PV61*`V&y#C{N3(28TPY
z<LPkgXg-Oz;}QAHp=&#qK`Usxxxhi0gD}mb$29|qEMI&~`Md+fnuIYW_Bx_;GCn86
z`zAvP%(J0GG#<TgS8rZ#)?&VDz6;Gk{PS5FNcpj+VVWHH()OMI>c>$N*=>VB+kyy3
zws?oaZczm+WQm7nZveMa+Z{t|gAC~mlQM7``Oymp{q`;1veR3piZB`7t?x$)!vTFo
z-#Bhx*=hwQw7WT8sFVLvzMpXdbxvqALIW{8IEcKCoA0l4F{!)VE>iGRsy90(^Pt|1
zMECTS<My33QbH5MPu6EH6w~ZBM4k7QFYi_0!2*0pu>5}XW@ihx-+Y3i|6-nv6zR#j
zEQb~l84&N5m`WiJd4=jHwyB+P#o2zb=<dOY6KpP`0YC2Knw*V9AOQP5Yw8~nc))s&
z4ye4>AA2Cj+t<{qgwM9U(eg<&I=L`m8)xVy;&z3l)%Q;|d1tk(pSUI#gl(}^R{bPX
z=eW5~d?f}DK1!d%_ygP#fO%%BM{Ums&y3s6tEa{FS81rWQ0;@eblyrtWM}Lu@Q`o>
zklQs)c5+OttQ+%Ei}h?hZiHT`<23(-EG3Cwo*hTPU&B4y@!!CWT4TCF%6A*nxio5+
zKJcKf&_qv!{XKk)aeF=#xCaNVxALK~m7(l{QP_B-r3t0)9Jep6Xf%obP$Z?!-(V5U
zcff>RshHer<0fhT09pM0=(WIhr&=J&Y6rm9cXU@#ShgWjY0yxgoMsaTwGPUH;NvIT
zFBUAcF*22aW~Cb$xwFw*;zUK+XYu;IIQsz}>H%8%B|Uf_sg>IoVRBbXeEF!-W{>Qv
zU5b>L;l!-GSss<^J6mU50%I!J!I1|^y7lqd^Ss^S+R-_Ks&;yGF@bh_9#ef1lRp)w
zNDhi*1Xr&cVgjaJuM)EQz6|Tz$Mtm`hVoCT?pYnEAV4nijG?G#6g*+-)wKRS2{3^j
z2l)@91IUAqs&<D)I%|b#5jp7h;&iwZ(h^XE8TJ<ciYvwSQTO?D6*;xZ0pqDPsRZTx
z{fw5>GN}X+t~w7+=5BXtS>Oo>vWOar3)n_EZb1+>D+|qYjKDP3u?3M_Lg#v;tQfZa
zv{B$fQtjS2MsX%x^g*`sQgaz1R0Lr(;Cn%mNFoteGP?UFEyPnF*SWn2dR%8ex?ajU
zzjv;;^ZV`|q$3<%0ZY0Ck@5({%FQJcaYJ#FC3yOFP7w>v;cU_$w<x2GQKb;9f)gMa
z)GK#YBXF|8)`QJiyUpxzPlzz*iP9_t1g%P3Bt-X!u-xuS>YNV%`&1A`<)Nofzdio4
zQ7w7q&fSAeV3?Xa9{9*hJ4Rirkp7Y9A`wJba_xkVpc>D$K@Q*#V}maHgA^UfwE6-e
zZc>gM?Zy2ed7O1vDf&0JpIEt_L1t>3#U}-DspkrzA$DnWHS0Cs_nv<z3Si=ekAB*2
zpbsu-h~bjCfO@UxH0MNP<dR)|68i1KmmSK8C-D%=21_}vJ33#7AV-jQvvhj{F2uB+
z(zYh)DPflPE=QlZo_0~4Zf~l&J*nf?x!pwNX_dL*S@gJ$>v^5lsOL`%stxSL=Ct>B
z0Im066|ERIV@Q_h5AKdZ(LGw{vgk_n6q4`I*OQ`#Ofb4Ru&w3dqJk19qz)8aAM$dB
zY+SNhb-fpw8bV4+7`#9yo+xpKRH+VwWm&y!Ix*}H2wiomc(l3X<z+>gXfBkBuk(DB
z-7EG0M*z+Y`$_YA!XzlDq53`Z^};_*&Q{5UkE>xlsCjg7{l;=Pfgj;qnc+{PISR4#
zk`MUBC*1xUWXdTmKO&tjAA7k^-|YAE=J08*O2%1co0tpN7H9TJ7H$<*g=s6U9Lm%S
zO8xDb!I*nM$fTOi0eH`@S%b;3{UZ;z{hMSfmgUJNOQ%W*4HQI0^$Hy-y6f5+Bc!rn
z@gT|8cG_&~n@9cd)XCe}F}2oHi#jYiV=Pp73C>c~!L>Q~X{GMNyXU!1%jec6f!9_#
zY1l38AwGK8Ui7p?`KVY~;?6ld=gR(!?W@^bhms2?99@KGdvS2WsHZJdFYaikCVVHm
z`1zaLyeaf@^x}@|?eN8I|A>`1D^88>*Xi(~wy&A)>R94_E*Ki$!tSj!#n@$gtm_Tx
z1@6lGqiq1B;J8h_JKCyg#kbrYbxp9F(?_0!Hqpz|IZrR|xZd6jfla+$voqJc%Ize$
ztETLsR`KJ~Ba;3=X;4S#6trV7HCh}iGl1aFJQuAymMv|obQ<nuZ3iJ6T#dS$vGlp!
zDv#k8-~`%elvE@(RUr3*g;>c5HjqY%C}GGmG_OSEl809g^7z_366elXAdZTy*u%3%
zW*ELYlwHAPkp5SwGG=%FF3cd3PeY5;Yp0m0gkk2Tlkf<OCQY`Cnmir4hnqTO%n{nt
zu5p75r*a#Pn+DTBfdCd#?v9<{t%b{M0**ucwQ#3axQAn4r>;^2#;%Khw>1`ElMTZJ
zTXSgMpF#M!P}~NiP>=Zt18_!pyqSEMTe(pn3?;xhtqKgaqI)*>cBnw+7ex=q3QvD$
zs2{+8zUSNM*_$k!x-~_V&$iV&ZK}Ry{z858u4aV+O4Lxu!*G1?3S7^(2fMiNeprrF
zGg3ob(DHRh(ZMgElIv>Zb3MkYoX)KAMv(oi3;eJ;v*|1EccCmx^@66h_P};p#G^K)
zNJ7b4S%*Re01f2Sjr9QPYmBnzf)=TFh=tKtIR8NM$_WZ{2hUM_!)}sjPORAstx8z?
zZ=?|P5ISr#@Zf_;a0PvE;6S(A*A6+%fwLMLVe1rCx&7l3ZB$@$z|L_R)cB56=*NkC
zwT_?ztl#VB$MyR~q>^4rSah$P1(#fWuLQBcwhkx9$=YsIfa?-Y>(Y^Ej;o&k4|TF!
zC^*6Q@s0(!P$}h+2K*TxeGHa65TIEyKm_Cd*Q&%U*UA~pjks%u%!G|;hPjz3gU6BT
zsRY-b!n0+X{8vce@AcO({wbK|exNw7pVq}#s=HTi#XMD{=W(Dmh=TS?C;<E08M*M^
z=i=Go7HCtRZ3nhN{vrQYwf4sH`*cT6DGBH3B8d_NKj5FUSG;In3SwbAt?>vYO_|#6
zlWcAO$^7r=OP~{IK!lxT<nL!zZ3}zvSfL(#R^3Uj2-bxt9gm>oFGOkZ+Ab=QdQo!M
zLS(0fijtQKM3uQYt-M_?K8a;(DggVR@evDh=B<=b2w8sCS$QB)ILvO!w)6*A7wYX^
zKsE0yw7P6K3nS0!N}FfuEoG*V^cJ$^V$c)44_1$ZrM22F1~c$gUH%2D!)`&f^`f<N
zM&}TXhRvx|++m*DVUqe7*r5v(gL5d6$uY3bRX;thi$OAwdmCf#@$jYS{^k_=*-Pd-
z=8NS2+P!3sP5+nqzx0yh(0j_(f$lh@1Q_7FoIhgBGh9$flAmGfD26!bGk@C&(nRP$
z6hB%Sbo@yW0B?G&7mxemgu5sI!ni#=gPR{-0G%J6LC+6Y0O+STd$%*KhYMo+I@Oh@
z6+;7LA+pluli;TqY+wHQ%b$Pz)0ZE<{6Am*<;Q>e&6j`q&6gj3{6FU>|MKNuzx;6b
zZU|HaduMqzdttqYcxs4v42l?{tQtn6Zg)sKWK!ONH!>TuFw$Ff5_p)k^jx8a5%zX!
z?7IhoJSc0#o+e$qvzxTG&<_63w}@Jcm=C=b%7Imeu=5-Z-Htm3s&N=IjXduqJUZ6R
zZjW`7)%Y?$0$q~(c|H{kRv~<IT2kx4sI!um<0aZ7L?D^q7efN@?J2P&hq||+(2&CB
zxzt2Ivz^Vjj8&9IE^HHv5->Eg+|Jz9TJxE)tA<pguSHo#h>3kDTVCe4A;&Tx!6jF$
zE&*}QrJXBOte7};0sID79SNO@g$aauM#@`yx)0(t0lljhlQ`;86B2iGJindgF%?N5
z-PCXkU7^tH2stky-o)mFU@Sm<K`(c1;c5@|CloFd<1!E5B&a)<8$1pQS?b(ZE)gk!
zE+|%Y^TSG&&4q4EM3ewgp$;lqhET?dZbQ00$Z45|NFR&WE1*J^E00oR$nho>9ZI^9
zmoCefZT_dQd8!;^US4c>UO!NyutSC<ae4Ihqu-%YPq37WJyjKp!m<QMjr^nq1=%w@
zHx<q7*uhk0Eh7GNpM$W`+>XBcz1MIE;yb|nqzhjgiN@}nX=nt3cSOk=IoHsPDaCt|
z{)XJ=-<T)wy*i)S7zXdB&D+09`1_j3|NRDipZVe3HEJQI?4ieexxE4)Flq-u22Tak
zxWxPxM53`?)9R_VdECCU_<l`3OVxGPC=nQMQwG$yN^|g0@2u$W5)vcwpUAQV{mU7R
zW&^hF(G~e_9$Y1?XN+cTQ=_LVNuM))4^mx`Lt;Ay*1}~$$YX~ZUyODc^c*f@H-Re!
zm^|eIwKFQ)c&Az0a&d@bN%63`NIEhUftMF7Gl8Ny`+1Ba>#4j0p;BZYNdrxz&ZN^D
zJf-FsNL#QFwCo4zPS39tw6@Tqt-G%n;zNaxMoX>b(gh(i#2C?m4TUxIHrMtdWTxXL
z^GLsZv4X6hrh<k8Fk1A;9H~(waf!7X05W%7^bzUI;WD2sRk|K}GLQU`dQr*77a7lL
zMR`78){4%HCZm#x5wgSvR3<{CE39BWm5Jax#}&S8FL{+i255XkEP(loYvrMf6TRs{
zUmL&$t-GrOI76=H>HxmH-nkIXJnqG@_OUnk<Nx^be}DP6FF&9F^TU___sc*3Wc8QK
za8hM#@4zQ4snG-u&CVBWq5@!TK6<i@e6LWRE{w9%u0UY7da1r(4itGDkx1dv9#7PV
z8Oz$r+%j_{xg+kagfqQK)m4If)u1e7s&17QfZv;cIur&}=Ix*75?LZWF6=gWS67TW
z*X@S5qI>CkSQdc^f2TnCjr*-k0dH*XYfa``kDY^!+iP6)*iK?6n-r+yi#Od-SN@sX
zeT6Ps+N<hi`j0_WZl_JE11zCY|9M2fN~XrhXPH<_3>LBgOxUYw>gxyW6<R`aE=35j
zkS^cNARM6i!MU@N|Mu89QMi+-lnKjD-m8d6sNz$ilhuF-&GgV*-K2AAjHT>+o@0%)
z(37eF)~e-f3j7`UTy<&q6uH8-e5IH@WR+{~=^hz}dqGiKIRGGz_J;qop#|v@&<2Ow
zZsV6-nc_&yhs5&AgKGnn+cOIbTNXPEX=5>{>@cL6^(7D!)UT#VWO(vYc&0g2@e1#d
zkGR{8WnjTs#Z_w8HJT%w$ch)VBZk~wDIKYlbSq;^mk`#BQYg@U**Uq9IuvsJ)5RRu
z)i{|kx}Fu3H9(6@-8(*w)&vW!8p)eG(@!_lX%7UQ4`o#NJF?v*^wppkbY6)nfJ?>{
z%YrS*O+l=NxBek{$Y`al+_eJsA7RE;y?ZsBkr`cnp`*xLyqDEruHNYBdXr6oX;Lj<
zWITgGoh4*<oP)Vz?#(%v*e7}HId}&gcOflehS4UPYCuaR?v<a7tVcx1i-_AK@U{;x
z5aP&7Rd&LGFY$xDgGW)45#Ym2U|qrY(j1=j79OhGbry#1ncbK9jFIf14>}&aR9Ecw
zBNbk)o1kz}dvdb{nmJW<%HWPqz-Hs(h3O~j7f1c%xc%n8dgYk(uoJ1dGs4MTCdGpY
zAS}5z2|PHkdN*U>ae)gRy8`h&S3pZ3ip_CVn36vBxGHzhlA)T`Vq&gd0sh)a$pGE<
zRK51OdIkQSW!yK2G!sui&D7S@cQjD46EtUxksn<qu+%$w=52gbt`R%fz4UueRAi+_
zIi3-mAV@HS!KRVcsBsz{o<aHFy{MaKPv^3!dAMEglHcH{YvzhAoe}hKS&3MZEY{0+
zGz`;__i@hD&v@)%czX~qU}L9-l>Wy?C|bj2V|EkjyKS4IgRCK(Bxmvew|6DGt|Zs-
zU%VzCavuB<-e}zm!4lv>Mhk8kUTI-?<L@!N_BTj><cx@uRav~eGx?g=cv`#`>ylMj
zRhcKx5GP{iKLJ8SMbo7NBt_M?6vq^GAL(kk*u0i5wQ~Nqb^AT*atnV*h+C0%ZTe`D
zjVNlNM!&8mm!B#;+o!@1ufQKw&Z_W61-GxrFXXKen=l7Nck%y|K}ObY?A)n*d-TZV
z=C~H5Dw#74mPK#T=GGFpL>k8sWnf#JlYL>pY?(x_xmetS>TUiJwcHMf0@UWomq?u6
zV^7kXC;qFlK%Pa94WLQ>sJ);h_LB)qM3A?3;GrQ*U2M@P5_pn%4xM720aXIW*1%j2
z$t2XV>o@CCP1NEA^H4J+D72iSQ5K<ICAT4c4(Re@9jp_&V*5M!?8rp=*c^;EUk9^C
zbs-Ky|L5lH8rZl^enaCDV|8~aJ(LX4advg>Ak`@xeSu)WLj8XG`QA@D9F&eqq*AJ6
zU;$}Jv~^LC!v!SP9G0Xj^!#HT+-w~HUXhr^D&fHwLc4txZkFGS4;g@{&MppbWIjNa
zF#(m*&Vr;S8x<WYinh)5ow*7n-Lwm~#wKhS7@($ap1&oor^w&IEhbLFI-0q>P#H{U
z4OeID_^*Jz&M@&A$e3yuA~n+-l|{yLR7R&YN42Tf#*M>@6nPtcBjN<KZ5ss;cS{VU
zwgrh9p?MQn@aSSbXkgJn(}6RYl7Mnax58w1Ba}gvvy1E_fU5;(jw1JQsD*DQgGd|S
zLx3!am;qHinFo?F12^O5K_Q8hS^-t%`olils^`%)i4do;CzJ=za<_gbUE0aO2~Gy5
zaFzlysV9RuAq$->tTEF51yl+;9!5STuJudLz`Cj2s&k&_NzXj%d7k!Atp{OdSH`oL
z_@_g^kJAW-k!T&Huf=cr&5(Rf6D0+N)kef^UzZ}=mn5fr6u2kARO3B_)Zn;0INbAI
zfR;$!Icf0h>>{OD*B0n-=_*<;R-_1^J||=LFa_li1}PeIr7h9f*5#&z6{|=F^$v7t
zouLEa#CC*LMprUByTWXWpB?!mii$vz9|$pHVZK<KncaZV9&bYixeIo-vExpxS1Oc@
z_I=N}5hb|=hw}zVK3)WYXj;bLq<|mnjL{`DMyu(<07PWrBD%>$nh2u36R1+inQu^p
zF$*It3;L&>IBuN~4rB8qm!3CrWH1dP5Kbh#<6FcI-ATWfLo*UW5h^!ptpinJYHE3{
zMi7i&=|~T9b84rbQ`L)C`*>C;1LAXRY#_98-XaSqLy&vUy*+OTLbz`h(4#dQ{EwYF
zE{|;n{2f9|I4m2HDmpRqPBLEDpgFOTS`Zum=-4LX?x<3#q|BBDu8oMM*ttA~SF_&h
z)&PkJ7OjqHzz%N9v3s_FG^71zs^!ocYfe7TvM8SzpSD1qx{r+nT=%hKM9nzOTj8B-
z9`~?lk(9kcbHX={L_Q1ogC-Zm!F3kGn{a+XIsGUERCRFyr_K!1>Y(U0w!4@>kTi_Q
zSn6>S*3vI;#kyfhDHC@rG`eFWuvz%nOlvUfgNt3#ErO<Wap9J3y)Y$+aIb|8;H0J#
zqx>9q2#rBRnlhf=I%J}=dIl^A5HDo#Ha6d}4FDHgz>lstBaB!fs*&!8yYdNlC5lq9
zHPmz-+)jn>)}4|&zwcX;l-y)7VZ7;tJlR-GJd0uI*uU&gX@P9`{BQKv`%^NsIyx^^
zgmrs~zeL)G(NC@rf4VYBPlu^`a?kUXEQ4)08jhqP_iBV`cWZ+A4{(vN_vJIKFfZ(L
zpPX42tWm9^JnE{kH|4Z5QxCH%X$OUZG|z<`f`*96O7Fz71gB$zB9=d|<qGsomW4=M
zmp#wx>au(KiwJKjVF)9x$}&YK7FCM<-?_^S#}P;hxZIOD8ARj54W>I8gNWcKQy`Ky
zNeo#^KSxJ5n#NvxctVS!sHmk~0-|TsK>{LT!e8|bAa0M0Dwi|v;<*<P1`Utc%GrpV
zqhUTe*>~3G4j${}xsy@*$#X}U{mFCBeR=FGbhFT8z690(#_qR0_L{=-*rSv*-tyQz
z`%fduE5^Q_#6?!RrO`Z@^n)RtK|$`xuV~Fqs8}P>7B?RJ49QjO$!P+WafE(8<(Ku~
zeG@lDJwEuKesw|MChoiORw)SF3jSAP3Zx4V37IdsSqSjB8iiwmP{7^SFU-{aal?wu
z?x|tL2KS0#ElO`8YR<vZUulcL>5}htFZTX%0o7?}!-Bw>Jvv_O^@u_CM#YU)2P=x<
zz}{zYp4UT48_9UA@ZM*vAp^9l@`OrKkYKbs>k`kP<e(XJ-8kx-vUO@J7?D5GlFfQ7
z*=FF^10jJkP!(%=&^b|l4!*m%192dBno<T^_Blv~@2#d4&*1pv$I4rtd}`tF<QX08
zPrjrpPky|RRIfjI)Cms{9#=a&c!2NL2cJs3eDG*m%2ywJqJH{`@(vQu8dep(FVIg=
zbOf~oY8pr%X<PtN-S!zx`Xn$7+4PN|P2fp41Oh>CM{10Fu?ClQ+M&Z|jq8JHWQyu)
zST#|yKeSZ~4`$UM6^LBm%&Jf}1HC_{Tp=9b%q-@~6{h3_bj-?*jBQkH#d=)am=SOz
zGvL1kw;;A4QBrq+B9eb>EAi&Y!9fy>?!*vc99xD~<JfW}8pk3M3i^g@Ma$uD0QIiM
zl}jU-Ah3n?#*ziv^9VRakJ8j3J8`)wIRt?Y(-pIfP!?@F*}rD;vNTCJTIm+qR`R|S
zGl>zYN~NOtiNcI;cQh-E>Hfx0$W3}<VPiU8-dL9I>3n0w*Y%C%@Z^Ox!j&&y7$6(V
z`@$2*>-%ah9o`p4vAi#gVtHQ}#rnQ#+us-dPkrWnHR|bwWSqEQHB-{cITH?!h&vLJ
zCsQ(pa(K8alicjev_q6d*lGyPcyG*XZ3^qqPllYE2ws4l!YwFInDNwEzAs0`mLIO_
z&FaIR#Fnu)<}V-Gs>g@1>KUnEt~9@DvQ)M+^=Md>h@f!t=Q9*0FU6)vcJzqM?l|&F
zGS)O>85x=9zB*Q*G*u&SThQLa-zpx(;$mru$Z}SkB-7wy>dX(*!6TI=bJJY2_G`(E
zQGP5C=txJ&zrk`ME-sf(1*Av)@K`cj_KM<@S$hbwyyd}kmktl6l2iBM$#mWnx+<3>
zI!aYxX)aNI8qY6C77MZjT@Apgn;eyxsyqRPRG(@D*!SVOv&X(l0^76K_GZ4`Lm=Uu
zf{x_I(+m&JcbC39QT8P<)TBT<dD~HxUamYlc57}PAHFQPqW#6CDY&*>T9=BpWgRZ>
zDZV~wr|k{`);dlDxK(P;(l1@mU%ii>yBCr_J=s^^mF9j|0{xPXn}S-VtJNnVw{5}R
zf0FyVh)fq1_>0@v=83;Mz4$seKxp9o<*Nn<jDrN;8wvy;@rUNkK1*#L?pHDR>wo_B
zzwX15b<5z7Ua|9=O!grWyrx7<{j-6Rxn9i}BbOCiI5iLhH0?@pIMub-xuamG=jJEG
z)xL4IpS(5xfs$hvd)v!{22%&Xs{)u_9dwMU@1wd(TCl{>u>_;Bt#vpokT*h!I$Pk=
z=oHl9HuPKVt-;7=du5nZf$mHr3Mv5xsV{u8cidYO<hRO*+Q5|I`;YAKdzR9XKnC*%
zu~zL@J;n|836Mers$}osLAs))(^zUIgPN+SLBpi{PGU(qZ(p%*oNJ@(#akEBamXr3
zA<MfcHC`WdjhBP2jiBwZT*P$_vD;Yixf>QVvx|@4L*4;BdZ^n$c-`M>6?wU&#XC}a
zW|GdNUVGGj$j>FCLFJyS2>758n^ef*rXShJkFKKCqC@ANW!$Ot{7l^v$u_1WYc@4s
zRvffgR4o|6?+B3R$Fu!ipFgs44#O;z5JLeo`Im+n+1xaS35^eeo{&#VJkS~Ul}x-6
z7K{d(995RF#?#I6_d(lE%Qa((Q~`*b&$)lUm`?foz!ekXk3`Qc7L3UoUZXcVt`|-J
ziXIogFPGhm({ed)2hQ7qdY{+!>_Eo~2yYl@f;QzTV>xhncp{BA$IX_@bj#*?$#mU9
zy4e<U0R-L)T&M_shc)sge6#dL7ReRwCJ)Dj)D|YDw5<}z3^b%Ci}_@L`)&s^k`@`u
zCUW-K4{D{<%V*cfwQ-JVpte2l7lQYQ8QSP)bv5B7G{0tS8|q#tm1;Q^gp?NXkg=V<
z<INl~#V@WE>F9~|HKfRfvnTy&_bMPjUI>m@WJls-GZH+^l3{T(<*iuv_DLBbtH;ze
zlKh7f>$<-jc9R7DmbzvI_>DJjF2H<8qU1g>S`_rq4Yvq^6w+2N`%D|lqi(y1RW3R=
z8gHPN$O?Kv?Ks`cF(XlJ8@^v+9BDGGU4EHXSMVXFyDlK8qI`)Rr+ld$Csty+t|qX|
zj-%x&aiJK=D-}W;(`gK`vDmcZ=nMAu`HS0ZIgst}Buc2gTpuam4#+*UNqvF^3>kB#
z-MU6PB5$k@D#2yY^kgoelKNNo%b<?Q*1K_+{?V~V^**cQ`I6C#D+PVu;tO!Cyf76r
zg>n+ulV1$|)9_%bJejJ@*`7+1muBDCKYI|MZ+_k7nt9C*T&u%{!z~7=D-)ibN_^oK
zi7*P`l-Ygu!c79k4uE&{<4(L~+Qxp>k|_vg)S@fgy)LyLuimPPP5mvYK>`Q^;E!vq
z{MwLFgNvuJ`{meP7;;DwT^zx^_RG-5d&h|RGu(13i+_sjS=>C>TWzP-`xGN9d{v?Z
zAv9`6e<z$Gv@rmh`VtQ5+eMz=e0NvA@sT@yRrOB=z_+y6tsP5YD;ECrdF|r={>%UV
z<$vzg8uUSXdws*dh%2Fxnu!XpeiK)vo_5Gf4FGjO@{OkY+<D4a^qEqq&->s{|Ajkm
z>=cA=59`7FQzX!5^W6c4XG3pq{rUjL5HmY~L)yLj2<tMec7x6ui7_+e%=)Pp6UOi(
zvIl2cci^TG&@T<k+(98=pGzsHUI9Zv{Ol!WFis?4H(1>98j=U*DBCH~KARw|M!J1`
z8IL*nG^{h>lVf1c>pNl{NXWAd%Z~5x&J(IUuF)S)VyZ!k3)0V2mteLogV(TQwCInz
z{f2gcJ%QMuOI=qaqb;k8Ni<#74;K8pXIJ``l|}DY9fLPaud@wqj!pPi@~YGc{fY&O
z2XVRb@VeP7HqJ{VEtv4-poD#gi;0qUL<(OYTnu7ef+Fg|tl-p)olHa}RE%L9$fWQ)
zQ6!`V&P{8XZcv~kMj}(^*#~(4Bjj_Lya%<C1Tc&_saAwc=4yeXqNvX@(x%CQVTM;N
zikE?vml8lMW5$meo?K+78FIek9VX9TRi`7KRqu$pp8ESrr=Jk=)-_i%nWQ?KxL%lh
zyZI`u9?e%TtND5%V5wwrnhMwCdlQeLK+UbI3DDK@YfL_k?z{<bqp(6hqt51y;S@sj
zIiy?9z3XNVQ*Ag|Fbgt!6-_e3s*P15%2Y^$fHE&kS?CP^t7A51J_k_HqEXp9RIQU{
z-|(J7u7*_1;My7{%mF|-=UyI@rh{)K+2n(y#ISJYG{zAn5^^xUBLkcok{^XFYF=}1
z{HbH^p*2D9#>RSgAfi$~iiA^jh^D7E&AsQ*ZP7<VFrhm(9lc{AEgfFYLP8TsOcR4-
zzoWglB<kWE)w?9Qns-gcC}jLO94KU*@n#u;$~;K@0(5i5m4e0-;kq<?o;BR_94}>@
znq*mw?hM-ujL*UY0YpO<ER|T;fbEWRd|6_Mx6q0JuHyApgXGBSBKJcbbJEs%pO^i)
zQ8&8dX=%oQ{4H{#ayt%y{FQ)-TFh#qU0?)x-5!VcUf5uyf4OC2vbVyS>bcwI%*`qJ
zxyZq*3|T7F?lYfs$Rl)%kyf$0W_A*MhM3<?R<RJ0dqPuXuxgnnA6Jat#D@~he!|7E
zB=sHWy$v5kU-^igd<BVVbCu{{F$`XfDL>@-;X$FnqFUFWo9s(t&JP37%Kly8H^)QH
zB%j^;NQN<RXq)skz26%I&2ABDvaF^P{nME1ZM`4tVQg0n%Pv3;oqTE(HrVGKRF^t}
z(klI_Tp2fyc~|M?`7sgGxHLqR*$@K1aCj6Q1f)%$VeI^vFKR`}orM-UhINMGe(b7f
zeiT^}S}o=Cl_qaA6Ba;Ek<$kQ18OcsBaBME(v&BxPyQ$kD>$BKnyuRK6N8*Wr%9Pm
z%^Dllcp~m<3Lx~$iDSL2p{H9-97sx_b*CL-23#YaG7#+tH?hD)DS$a0b0i?Z5}C~h
zCQiPbIEfAz#Ig2^<097fx}=+)nKp2g#>@lrHQz-9<MLPu!+y!puhL@d^o?n)q_^0K
zqOt?nQbqvMXyAN?@^a++3+#XLC?<mGXe=EO1YrAyQNdJ_erX)zg*%qeSjHx*iZ0V(
zn<8j>IJS;L-1t#~)x+I8NnS@34e7x-fKXtoKxD=U1AiK~jk#3v`;#%h{OM{V0Z>_G
zL@d3*m+?IfC)~LJ8l)Fg`2PJ#`2B~!XMFsJf8u}s<qv=Jx9OOm`OTmH_2+;7{fCg4
zP$=7xn8+6S1&N6Ut2<_jED!XE*`eS3!*Boi`?sSnf6Y_}n~wMv`%}>J5#8Lu58Z>Q
z3tz*@py7z-p21F*#vb+yt`n!Ap?i7nXZ!nE-@lf14WAjXSB|za)$q)KVUxnCqSX=x
z0f4x%A68recQ7myISiXIeV#MT>lulSTeS?G++9^Qu@_Qb5gX~fbY34Du6(mpAb45`
zC&|6Ng8l{dLL?34#%SOv-q~kU^BH~&Kf7FQZjr%Y`-Z-7Zjf~GZ(bjl10K5wascli
zFwz-do-W@^V@`yb4KB=tC4k<hL6wu47Vu417A+AUe39h=ibD!=^mLa_x$xjH<eMhS
z7mRO~G?MeLL6I9ETBvm8Nq!P+PY6~Cuko~f7JbV6{E<bmjxzx@f~-V)v1kh3%E*Z=
z?;Rv{Ne2qUl$^<bLcB{f>>JX@4IXj*8cPA_$(kijb1y=|#|9YRWhq=Sw*^UuB(bk@
z08pZb86MV7r_j=AI{?%dlHnj8H=G!L&@#GGKD`#M13is^uDEni9{|%fJUJ0cpy~po
zXAylz(N5ODdY*+3V}`EvOOo@)T@3(6x^a^pS&!r&dS^%vwH$~xB+GDe@m=#RQM7?s
z(9&0IWbh4PM4;?!Mm*uQMW}CTtdjt0+c6(gn)rKs$G-CV%DDNM$4-$>6*Vd9?=dK5
z+)$-VHC9XqMx|HF00FFwY)oyewtbN`qAW6Fi`*W1%g7J4Mlv|bNI+D4eG%5>YC%%6
z$NHihRoOW>{ErF1MJ-b`0;xUo3;Rw2-#%`8&FlH~_twD4HlIbl)?P$)fio!+)fHaq
z1_N&gsQpSY%mI?*9h*+Ju45J>_DzUE{kTJvaOxW{UEbL8-#Fs<>WJeAyn%`?;CKDx
zh#%Dg^vr(tJ8qKxyP%&xbZ<|;a4V$NLXz=oqqH8;!`QMGZ9M*nRy))B1qm|J^ZW9R
zs5+*pYrk#U5#=U2;Nn_?Y69pmyHT?}TgNd9CZHD5fugj6=E;5y+DtTmJ6|uc;@^^&
z^oL*$F%c+*YS1!hn2aLAqbhwdN2E+qRhgmzK#UU35&$~~A%A^hjS?{4bsHA>pa7om
ztYic*5mI$<frpWtPVyz$u(RVFk-{UXr3GVKDHkKT1Jsft3wP(?`#06;yt)t15ato-
zsO#TEj*4Gp)#Fk2M-#ZIBMCS*4s{#lc;XRWot;OzF2N<SaHA6?i2(|FABFw7(x~8f
z8oM@&y1W7dTu*3a)5$R;&@p02l|>>{zls{ug~z~{z`D4PdX5O^36gvQO>8?67MDMX
ztk`$dtCJoWqsmF&%JZ|n6BOmFer82G$joFBUa>#Cej```U3qE2X^^~;^C>=={lklJ
zqNxne&VCPqEkYyQ<@65(QHjyl8<AI<wd!>eP6&^Q2}RxxzBOe5!R^>ss^!OqiQH{2
zZU7?}P)QyktTBS2kPHcMmHHs`<E4V#1>UHn1d1yKF3^aP;yuv>yoPa3Cu8p3y^~=V
zdZd&gA8jBpld2C=1!MgXlQ@6NQ3PnH>T!LH7tyBv1~2Dp0SKS0bWSpy{9!SzJupUK
zMU(&_j%e5t*fz(C$RJVXp(d0#D%7?_8q3Qgl5%2s3<MivMdWctQPiV=ii~LWR$3*8
z8kR(frh<Z@qw+V_8|2A(za4sM*b2q@8G`uYM^w}5-7uDTJ(a6L5T|EXgC`N{immZM
z{95D_HbOcI{enednE}=08`A-=GuH!vUzqu^w`bP_op<O%^+l6z*3n7gnSgRBMm0p1
zEWdE^VAYY=!5K0ZV3okSedtCUiZ$!AAtXRVH2hPv5G)NzXPB{uR)o%J1gas4BWo*z
za~hfJkwt}&RVH{z$)@dzd;AL2(55@n$$^iQ&$1j)6A<>8feWTTnYo9_6p=PcDe!hN
z^(rN8dL>4QlfT9?pRW#8P?+*AfMjYq)Vt}}REn7c<z(mzERjGNA7qR5&?+JbkSSJM
z4L~)szTr*s&t-}dSNy>i11MSpC%S0~1SEFH%SDchRSzzz%+(S2AOq?s4K;<ansxVB
z9b;Y=YI}ZYe|QV(6SE(V2QY8prPYwI<56#RTMbFj4=@Zffep|#)u4(bSL}P^w>XOQ
z#uH3AY+^wt+1NPC3#$P(DZmOoT@4g8!)lP68x}4=cd4!x8QhOVYzeh;;T=KN30qWV
z;+@+7Y~@&fVdd|o_-Q{CGzJCJ$RLG-Jr+KQy6dt=qcC>1Qa2DzHh-@A385yhdZ7H(
zwjLr;%NUz!!Zr5CjyXIL{GKgIviuxDPk^nM;n&=Yup!(r+NPk<!9Wb3%1;_$5205a
z$`eg^25OoGi;}lP<3hj|CUa`WdPrl;9T>r2w9QV^=vZo|Rta!*!qgsHi+Qja77IvN
znTc_hKCaFKL;(c6MbO~pZrM3JjXunFDJ*^758O9FpJ1Yg{GJmDbFPWzIE16abaDqz
zC?)OB&UE|EoQM?1O5r7R72ZCXSts4~4v%sbP<2xgK5J))tiE2N7cjFT8JVL@8XO2%
zb!Vc!d#K>F86?xQAH$VLH4QXSz}d8Z0m`k27FwvUL<^qmQ~0^n<uZI{fa!MxStiPs
znqn#J4Gm+gF71d_sMrD-k9(p$1UeMJ3KRr*F=P^{ABl~kXg`8$&Lz<xXCoEnCLbyp
zT2lc7V+yPri6_!M*O9D8ha>2=Ww=l*ssJf9;}_Fd_nxz==IK=;T<AbB0b3pFyj-<I
z-5V`;geQb}rOZ)7%%K53%ljA?1%(5K!w@vsn1PFF{*%*x$6;5p4_@6B`q+8t!8ZBe
zyzj6WRZV}`&{A$N|H(W~f8}6s@$z7$OidmN^>D&NeX<Wk60brUw9QVcF%l1S&f*s(
z0sKfJ-#$0>1C0WZLMGe;1imwk?(rb1mrmlcV-~UAj|mYaY{ZY9y-Hh@Je-S$%7izj
zbw6f2u-mNJsfU0mT8eFBZV{Ueh!uI&tM#;id%fFU&`c<(f3s<ywDZu1Z+1+i@N%x;
zKo#T}>I@XfmzXV{sZiKxnD^OjN3!O)!0osen4xh*YEU6cE44vyH5i_}AV%&?g$c4b
zUe*b>Cm?T&6f<a`oGWht*lw^hbdJIOwiQoD1^#99m-81L0R9H^7v>_F?$+88K#qKH
z<wui=8kYDpPUoJOZ;LH~aJS4x<US2%zoQr8Nd6)Z=Mxh+(=DGF3SUU;v}KG?#`Vbb
zEG}PE`~$16Iff1K#9$-rMQNW2jG<J58dgdWObPFvfdc*YPMxb_?NXlFVwl1m2=gN7
zH}rz(w<o%HK1=V5^HulD;4?>kE95?pWsoiopTpE6Jb=d(b$87ayebJ6&|M`k6^<lu
z=b{a!ne47v?l6<@F?A{gChl+^0~%)?lLq(0b{=;6oWl5RO3`{C-0(>&=2pBF)^}wp
z0hU<k*#@yNCPqkdGTd<gR@`s9iu*-E=~KX8fAV+bi69)m09&0&+WGa!h{jj`@sB+A
z^Y{<;?_ZR?<~MQkzx$mM<V=*04UtbH+L{0E-|Tt)qGzex`Oi;A_7VwnDbLjo;@<y#
zO(pW`IS}u>dKxdC6P^n~SAZ`*d&cKS6#}LHraxo+*JIJw=g$U2NqcpG!;iI-g-|~-
zG~fb)oh;{hGAKiQ<YacD7Uv_QXYq+M4gcp-@3Zzp5$FXD{msi9Cg<Q~hJLLE6QG|4
zoc|=RKZ8eBA4yza=)P&C9#ehgN~&)}zIORx<m}a>sj*BU*L1wves;T_dwQvBnxH+c
z_Y$pR5w)Si>D?=*hT_we)8}~?$0>rIzL4EJPG9P^dV6#SMbkD<U#j{$-*JK5uZ<rR
z<7;}!mHDzq*muIet-gMhb>8jM<j=234-}H(Z~yM{0H-T6|FrzOGxwjanIhX3u5tL^
z@{-fIjH%Sqy)QOx#^t@OM&R(bpP<bB+n=@P>|4`t-7og0pZ@%Rox*O{Cz1pJ-HLF=

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/animations/phone.tgs b/Telegram/Resources/animations/phone.tgs
new file mode 100644
index 0000000000000000000000000000000000000000..7541526afb0f9120bc9b77f1e4f2a6746104fb57
GIT binary patch
literal 7835
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

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/animations/sleep.tgs b/Telegram/Resources/animations/sleep.tgs
new file mode 100644
index 0000000000000000000000000000000000000000..b766d6e438e6d7158becd5d07b24b47225d47154
GIT binary patch
literal 38806
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
ZaaIFyRs(rf1OM{>{y%MOa-#vM1OV;Am4^TT

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/animations/writing.tgs b/Telegram/Resources/animations/writing.tgs
new file mode 100644
index 0000000000000000000000000000000000000000..47caac05a2fc28b951f68bb336c11a332dea94e2
GIT binary patch
literal 64148
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

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 73b164dee..89508d8c5 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -2172,6 +2172,70 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_business_subtitle_chatbots" = "Chatbots";
 "lng_business_about_chatbots" = "Add any third party chatbots that will process customer interactions.";
 
+"lng_location_title" = "Location";
+"lng_location_about" = "Display the location of your business on your account.";
+"lng_location_address" = "Enter Address";
+"lng_location_fallback" = "You can set your location on the map from your mobile device.";
+
+"lng_hours_title" = "Business Hours";
+"lng_hours_about" = "Turn this on to show your opening hours schedule to your customers.";
+"lng_hours_show" = "Show Business Hours";
+"lng_hours_time_zone" = "Time Zone";
+"lng_hours_monday" = "Monday";
+"lng_hours_tuesday" = "Tuesday";
+"lng_hours_wednesday" = "Wednesday";
+"lng_hours_thursday" = "Thursday";
+"lng_hours_friday" = "Friday";
+"lng_hours_saturday" = "Saturday";
+"lng_hours_sunday" = "Sunday";
+"lng_hours_closed" = "Closed";
+
+"lng_replies_title" = "Quick Replies";
+"lng_replies_about" = "Set up shortcuts with rich text and media to respond to messages faster.";
+"lng_replies_add" = "Add Quick Reply";
+"lng_replies_add_title" = "New Quick Reply";
+"lng_replies_add_shortcut" = "Add a shortcut for your reply.";
+"lng_replies_add_placeholder" = "Shortcut";
+"lng_replies_add_exists" = "This shortcut already exists.";
+"lng_replies_empty_title" = "New Quick Reply";
+"lng_replies_empty_about" = "Enter a message below that will be sent in chat when you type {shortcut}.\n\nYou can access Quick Replies in any chat by typing / or using Attachment menu.";
+"lng_replies_remove_title" = "Remove Shortcut";
+"lng_replies_remove_text" = "You didn't create a quick reply message. Do you want to remove the shortcut?";
+"lng_replies_edit_title" = "Edit Shortcut";
+"lng_replies_edit_about" = "Edit the name for this shortcut.";
+"lng_replies_message_placeholder" = "Add a Quick Reply";
+
+"lng_greeting_title" = "Greeting Message";
+"lng_greeting_about" = "Greet customers when they message you the first time or after a period of no activity.";
+"lng_greeting_enable" = "Send Greeting Message";
+"lng_greeting_create" = "Create a Greeting Message";
+"lng_greeting_recipients" = "Recipients";
+"lng_greeting_select" = "Select chats or entire chat categories for sending a greeting message.";
+"lng_greeting_period_title" = "Period of no activity";
+"lng_greeting_period_about" = "Choose how many days should pass after your last interaction with a recipient to send them a greeting in response to their message.";
+"lng_greeting_empty_title" = "New Greeting Message";
+"lng_greeting_empty_about" = "Create greetings that will be automatically sent to new customers.";
+"lng_greeting_message_placeholder" = "Add a Greeting";
+
+"lng_away_title" = "Away Message";
+"lng_away_about" = "Automatically reply with a message when you are away.";
+"lng_away_enable" = "Send Away Message";
+"lng_away_create" = "Create an Away Message";
+"lng_away_schedule" = "Schedule";
+"lng_away_schedule_always" = "Send Always";
+"lng_away_schedule_outside" = "Outside of Business Hours";
+"lng_away_schedule_custom" = "Custom Schedule";
+"lng_away_custom_start" = "Start Time";
+"lng_away_custom_end" = "End Time";
+"lng_away_recipients" = "Recipients";
+"lng_away_select" = "Select chats or entire chat categories for sending an away message.";
+"lng_away_empty_title" = "New Away Message";
+"lng_away_empty_about" = "Add messages that will be automatically sent when you are off.";
+"lng_away_message_placeholder" = "Add an Away Message";
+
+"lng_business_limit_reached#one" = "Limit of {count} message reached.";
+"lng_business_limit_reached#other" = "Limit of {count} messages reached.";
+
 "lng_chatbots_title" = "Chatbots";
 "lng_chatbots_about" = "Add a bot to your account to help you automatically process and respond to the messages you receive. {link}";
 "lng_chatbots_about_link" = "Learn more...";
diff --git a/Telegram/Resources/qrc/telegram/animations.qrc b/Telegram/Resources/qrc/telegram/animations.qrc
index ede8feb2d..12666b6fd 100644
--- a/Telegram/Resources/qrc/telegram/animations.qrc
+++ b/Telegram/Resources/qrc/telegram/animations.qrc
@@ -14,6 +14,12 @@
     <file alias="voice_ttl_idle.tgs">../../animations/voice_ttl_idle.tgs</file>
     <file alias="voice_ttl_start.tgs">../../animations/voice_ttl_start.tgs</file>
     <file alias="palette.tgs">../../animations/palette.tgs</file>
-    <file alias="robot.tgs">../../animations/robot.tgs</file>
+    <file alias="sleep.tgs">../../animations/sleep.tgs</file>
+	<file alias="greeting.tgs">../../animations/greeting.tgs</file>
+	<file alias="location.tgs">../../animations/location.tgs</file>
+	<file alias="robot.tgs">../../animations/robot.tgs</file>
+	<file alias="writing.tgs">../../animations/writing.tgs</file>
+	<file alias="hours.tgs">../../animations/hours.tgs</file>
+	<file alias="phone.tgs">../../animations/phone.tgs</file>
   </qresource>
 </RCC>
diff --git a/Telegram/SourceFiles/data/business/data_business_chatbots.h b/Telegram/SourceFiles/data/business/data_business_chatbots.h
index adfe998d2..13b8a894b 100644
--- a/Telegram/SourceFiles/data/business/data_business_chatbots.h
+++ b/Telegram/SourceFiles/data/business/data_business_chatbots.h
@@ -17,10 +17,8 @@ class Session;
 
 struct ChatbotsSettings {
 	UserData *bot = nullptr;
-	BusinessExceptions allowed;
-	BusinessExceptions disallowed;
+	BusinessRecipients recipients;
 	bool repliesAllowed = false;
-	bool onlySelected = false;
 };
 
 class Chatbots final {
diff --git a/Telegram/SourceFiles/data/business/data_business_common.h b/Telegram/SourceFiles/data/business/data_business_common.h
index aed51fdf9..743ddaa12 100644
--- a/Telegram/SourceFiles/data/business/data_business_common.h
+++ b/Telegram/SourceFiles/data/business/data_business_common.h
@@ -23,9 +23,23 @@ inline constexpr bool is_flag_type(BusinessChatType) { return true; }
 
 using BusinessChatTypes = base::flags<BusinessChatType>;
 
-struct BusinessExceptions {
+struct BusinessChats {
 	BusinessChatTypes types;
 	std::vector<not_null<UserData*>> list;
+
+	friend inline bool operator==(
+		const BusinessChats &a,
+		const BusinessChats &b) = default;
+};
+
+struct BusinessRecipients {
+	BusinessChats included;
+	BusinessChats excluded;
+	bool onlyIncluded = false;
+
+	friend inline bool operator==(
+		const BusinessRecipients &a,
+		const BusinessRecipients &b) = default;
 };
 
 } // namespace Data
diff --git a/Telegram/SourceFiles/settings/business/settings_away_message.cpp b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
new file mode 100644
index 000000000..de2f1c454
--- /dev/null
+++ b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
@@ -0,0 +1,117 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "settings/business/settings_away_message.h"
+
+#include "core/application.h"
+#include "data/data_session.h"
+#include "lang/lang_keys.h"
+#include "main/main_session.h"
+#include "settings/business/settings_recipients_helper.h"
+#include "ui/text/text_utilities.h"
+#include "ui/widgets/buttons.h"
+#include "ui/wrap/slide_wrap.h"
+#include "ui/wrap/vertical_layout.h"
+#include "ui/vertical_list.h"
+#include "window/window_session_controller.h"
+#include "styles/style_settings.h"
+
+namespace Settings {
+namespace {
+
+class AwayMessage : public BusinessSection<AwayMessage> {
+public:
+	AwayMessage(
+		QWidget *parent,
+		not_null<Window::SessionController*> controller);
+	~AwayMessage();
+
+	[[nodiscard]] rpl::producer<QString> title() override;
+
+private:
+	void setupContent(not_null<Window::SessionController*> controller);
+	void save();
+
+	rpl::variable<Data::BusinessRecipients> _recipients;
+
+};
+
+AwayMessage::AwayMessage(
+	QWidget *parent,
+	not_null<Window::SessionController*> controller)
+: BusinessSection(parent, controller) {
+	setupContent(controller);
+}
+
+AwayMessage::~AwayMessage() {
+	if (!Core::Quitting()) {
+		save();
+	}
+}
+
+rpl::producer<QString> AwayMessage::title() {
+	return tr::lng_away_title();
+}
+
+void AwayMessage::setupContent(
+		not_null<Window::SessionController*> controller) {
+	using namespace rpl::mappers;
+
+	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
+	//const auto current = controller->session().data().chatbots().current();
+
+	//_recipients = current.recipients;
+
+	AddDividerTextWithLottie(content, {
+		.lottie = u"sleep"_q,
+		.lottieSize = st::settingsCloudPasswordIconSize,
+		.lottieMargins = st::peerAppearanceIconPadding,
+		.showFinished = showFinishes(),
+		.about = tr::lng_away_about(Ui::Text::WithEntities),
+		.aboutMargins = st::peerAppearanceCoverLabelMargin,
+	});
+
+	Ui::AddSkip(content);
+	const auto enabled = content->add(object_ptr<Ui::SettingsButton>(
+		content,
+		tr::lng_away_enable(),
+		st::settingsButtonNoIcon
+	))->toggleOn(rpl::single(false));
+
+	const auto wrap = content->add(
+		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
+			content,
+			object_ptr<Ui::VerticalLayout>(content)));
+	const auto inner = wrap->entity();
+
+	Ui::AddSkip(inner);
+	Ui::AddDivider(inner);
+
+	wrap->toggleOn(enabled->toggledValue());
+	wrap->finishAnimating();
+
+	AddBusinessRecipientsSelector(inner, {
+		.controller = controller,
+		.title = tr::lng_away_recipients(),
+		.data = &_recipients,
+	});
+
+	Ui::AddSkip(inner, st::settingsChatbotsAccessSkip);
+
+	Ui::ResizeFitChild(this, content);
+}
+
+void AwayMessage::save() {
+}
+
+} // namespace
+
+Type AwayMessageId() {
+	return AwayMessage::Id();
+}
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_away_message.h b/Telegram/SourceFiles/settings/business/settings_away_message.h
new file mode 100644
index 000000000..e9037b4f6
--- /dev/null
+++ b/Telegram/SourceFiles/settings/business/settings_away_message.h
@@ -0,0 +1,16 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "settings/settings_type.h"
+
+namespace Settings {
+
+[[nodiscard]] Type AwayMessageId();
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_business_exceptions.cpp b/Telegram/SourceFiles/settings/business/settings_business_exceptions.cpp
deleted file mode 100644
index 568aca80f..000000000
--- a/Telegram/SourceFiles/settings/business/settings_business_exceptions.cpp
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
-This file is part of Telegram Desktop,
-the official desktop application for the Telegram messaging service.
-
-For license and copyright information please follow this link:
-https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
-*/
-#include "settings/business/settings_business_exceptions.h"
-
-#include "boxes/filters/edit_filter_chats_list.h"
-#include "boxes/filters/edit_filter_chats_preview.h"
-#include "data/data_session.h"
-#include "data/data_user.h"
-#include "history/history.h"
-#include "lang/lang_keys.h"
-#include "ui/wrap/vertical_layout.h"
-#include "window/window_session_controller.h"
-
-namespace Settings {
-namespace {
-
-using Flag = Data::ChatFilter::Flag;
-using Flags = Data::ChatFilter::Flags;
-
-[[nodiscard]] Flags TypesToFlags(Data::BusinessChatTypes types) {
-	using Type = Data::BusinessChatType;
-	return ((types & Type::Contacts) ? Flag::Contacts : Flag())
-		| ((types & Type::NonContacts) ? Flag::NonContacts : Flag())
-		| ((types & Type::NewChats) ? Flag::NewChats : Flag())
-		| ((types & Type::ExistingChats) ? Flag::ExistingChats : Flag());
-}
-
-[[nodiscard]] Data::BusinessChatTypes FlagsToTypes(Flags flags) {
-	using Type = Data::BusinessChatType;
-	return ((flags & Flag::Contacts) ? Type::Contacts : Type())
-		| ((flags & Flag::NonContacts) ? Type::NonContacts : Type())
-		| ((flags & Flag::NewChats) ? Type::NewChats : Type())
-		| ((flags & Flag::ExistingChats) ? Type::ExistingChats : Type());
-}
-
-} // namespace
-
-void EditBusinessExceptions(
-		not_null<Window::SessionController*> window,
-		BusinessExceptionsDescriptor &&descriptor) {
-	const auto session = &window->session();
-	const auto options = Flag::ExistingChats
-		| Flag::NewChats
-		| Flag::Contacts
-		| Flag::NonContacts;
-	auto &&peers = descriptor.current.list | ranges::views::transform([=](
-			not_null<UserData*> user) {
-		return user->owner().history(user);
-	});
-	auto controller = std::make_unique<EditFilterChatsListController>(
-		session,
-		(descriptor.allow
-			? tr::lng_filters_include_title()
-			: tr::lng_filters_exclude_title()),
-		options,
-		TypesToFlags(descriptor.current.types) & options,
-		base::flat_set<not_null<History*>>(begin(peers), end(peers)),
-		[=](int count) {
-			return nullptr; AssertIsDebug();
-		});
-	const auto rawController = controller.get();
-	const auto save = descriptor.save;
-	auto initBox = [=](not_null<PeerListBox*> box) {
-		box->setCloseByOutsideClick(false);
-		box->addButton(tr::lng_settings_save(), crl::guard(box, [=] {
-			const auto peers = box->collectSelectedRows();
-			auto &&users = ranges::views::all(
-				peers
-			) | ranges::views::transform([=](not_null<PeerData*> peer) {
-				return not_null(peer->asUser());
-			}) | ranges::to_vector;
-			save(Data::BusinessExceptions{
-				.types = FlagsToTypes(rawController->chosenOptions()),
-				.list = std::move(users),
-			});
-			box->closeBox();
-		}));
-		box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
-	};
-	window->show(
-		Box<PeerListBox>(std::move(controller), std::move(initBox)));
-}
-
-not_null<FilterChatsPreview*> SetupBusinessExceptionsPreview(
-		not_null<Ui::VerticalLayout*> content,
-		not_null<rpl::variable<Data::BusinessExceptions>*> data) {
-	const auto rules = data->current();
-
-	const auto locked = std::make_shared<bool>();
-	auto &&peers = data->current().list | ranges::views::transform([=](
-			not_null<UserData*> user) {
-		return user->owner().history(user);
-	});
-	const auto preview = content->add(object_ptr<FilterChatsPreview>(
-		content,
-		TypesToFlags(data->current().types),
-		base::flat_set<not_null<History*>>(begin(peers), end(peers))));
-
-	preview->flagRemoved(
-	) | rpl::start_with_next([=](Flag flag) {
-		*locked = true;
-		*data = Data::BusinessExceptions{
-			data->current().types & ~FlagsToTypes(flag),
-			data->current().list
-		};
-		*locked = false;
-	}, preview->lifetime());
-
-	preview->peerRemoved(
-	) | rpl::start_with_next([=](not_null<History*> history) {
-		auto list = data->current().list;
-		list.erase(
-			ranges::remove(list, not_null(history->peer->asUser())),
-			end(list));
-
-		*locked = true;
-		*data = Data::BusinessExceptions{
-			data->current().types,
-			std::move(list)
-		};
-		*locked = false;
-	}, preview->lifetime());
-
-	data->changes(
-	) | rpl::filter([=] {
-		return !*locked;
-	}) | rpl::start_with_next([=](const Data::BusinessExceptions &rules) {
-		auto &&peers = rules.list | ranges::views::transform([=](
-				not_null<UserData*> user) {
-			return user->owner().history(user);
-		});
-		preview->updateData(
-			TypesToFlags(rules.types),
-			base::flat_set<not_null<History*>>(begin(peers), end(peers)));
-	}, preview->lifetime());
-
-	return preview;
-}
-
-} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_business_exceptions.h b/Telegram/SourceFiles/settings/business/settings_business_exceptions.h
deleted file mode 100644
index e60f1a01b..000000000
--- a/Telegram/SourceFiles/settings/business/settings_business_exceptions.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
-This file is part of Telegram Desktop,
-the official desktop application for the Telegram messaging service.
-
-For license and copyright information please follow this link:
-https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
-*/
-#pragma once
-
-#include "data/business/data_business_common.h"
-
-class FilterChatsPreview;
-
-namespace Ui {
-class VerticalLayout;
-} // namespace Ui
-
-namespace Window {
-class SessionController;
-} // namespace Window
-
-namespace Settings {
-
-struct BusinessExceptionsDescriptor {
-	Data::BusinessExceptions current;
-	Fn<void(const Data::BusinessExceptions&)> save;
-	bool allow = false;
-};
-void EditBusinessExceptions(
-	not_null<Window::SessionController*> window,
-	BusinessExceptionsDescriptor &&descriptor);
-
-not_null<FilterChatsPreview*> SetupBusinessExceptionsPreview(
-	not_null<Ui::VerticalLayout*> content,
-	not_null<rpl::variable<Data::BusinessExceptions>*> data);
-
-} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
index d358b5112..5500ca539 100644
--- a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
@@ -6,18 +6,17 @@ For license and copyright information please follow this link:
 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "settings/business/settings_chatbots.h"
-
+//
 #include "core/application.h"
 #include "data/business/data_business_chatbots.h"
 #include "data/data_session.h"
 #include "data/data_user.h"
 #include "lang/lang_keys.h"
 #include "main/main_session.h"
-#include "settings/business/settings_business_exceptions.h"
-#include "settings/settings_common_session.h"
+#include "settings/business/settings_recipients_helper.h"
 #include "ui/text/text_utilities.h"
 #include "ui/widgets/fields/input_field.h"
-#include "ui/widgets/checkbox.h"
+#include "ui/widgets/buttons.h"
 #include "ui/wrap/slide_wrap.h"
 #include "ui/wrap/vertical_layout.h"
 #include "ui/vertical_list.h"
@@ -28,10 +27,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 namespace Settings {
 namespace {
 
-constexpr auto kAllExcept = 0;
-constexpr auto kSelectedOnly = 1;
+enum class LookupState {
+	Empty,
+	Loading,
+	Ready,
+};
 
-class Chatbots : public Section<Chatbots> {
+struct BotState {
+	UserData *bot = nullptr;
+	LookupState state = LookupState::Empty;
+};
+
+class Chatbots : public BusinessSection<Chatbots> {
 public:
 	Chatbots(
 		QWidget *parent,
@@ -40,10 +47,6 @@ public:
 
 	[[nodiscard]] rpl::producer<QString> title() override;
 
-	rpl::producer<> showFinishes() const {
-		return _showFinished.events();
-	}
-
 	const Ui::RoundRect *bottomSkipRounding() const {
 		return &_bottomSkipRounding;
 	}
@@ -52,29 +55,37 @@ private:
 	void setupContent(not_null<Window::SessionController*> controller);
 	void save();
 
-	void showFinished() override {
-		_showFinished.fire({});
-	}
-
-	const not_null<Window::SessionController*> _controller;
-	const not_null<Main::Session*> _session;
-
-	rpl::event_stream<> _showFinished;
 	Ui::RoundRect _bottomSkipRounding;
 
-	rpl::variable<bool> _onlySelected = false;
+	rpl::variable<Data::BusinessRecipients> _recipients;
+	rpl::variable<QString> _usernameValue;
+	rpl::variable<BotState> _botValue = nullptr;
 	rpl::variable<bool> _repliesAllowed = true;
-	rpl::variable<Data::BusinessExceptions> _allowed;
-	rpl::variable<Data::BusinessExceptions> _disallowed;
 
 };
 
+[[nodiscard]] rpl::producer<QString> DebouncedValue(
+		not_null<Ui::InputField*> field) {
+	return rpl::single(field->getLastText());
+}
+
+[[nodiscard]] rpl::producer<BotState> LookupBot(
+		not_null<Main::Session*> session,
+		rpl::producer<QString> usernameChanges) {
+	return rpl::never<BotState>();
+}
+
+[[nodiscard]] object_ptr<Ui::RpWidget> MakeBotPreview(
+		not_null<QWidget*> parent,
+		rpl::producer<BotState> state,
+		Fn<void()> resetBot) {
+	return object_ptr<Ui::RpWidget>(parent.get());
+}
+
 Chatbots::Chatbots(
 	QWidget *parent,
 	not_null<Window::SessionController*> controller)
-: Section(parent)
-, _controller(controller)
-, _session(&controller->session())
+: BusinessSection(parent, controller)
 , _bottomSkipRounding(st::boxRadius, st::boxDividerBg) {
 	setupContent(controller);
 }
@@ -96,10 +107,8 @@ void Chatbots::setupContent(
 	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
 	const auto current = controller->session().data().chatbots().current();
 
-	_onlySelected = current.onlySelected;
+	_recipients = current.recipients;
 	_repliesAllowed = current.repliesAllowed;
-	_allowed = current.allowed;
-	_disallowed = current.disallowed;
 
 	AddDividerTextWithLottie(content, {
 		.lottie = u"robot"_q,
@@ -125,93 +134,31 @@ void Chatbots::setupContent(
 				: QString())),
 		st::settingsChatbotsUsernameMargins);
 
+	_usernameValue = DebouncedValue(username);
+	_botValue = rpl::single(BotState{
+		current.bot,
+		current.bot ? LookupState::Ready : LookupState::Empty
+	}) | rpl::then(
+		LookupBot(&controller->session(), _usernameValue.changes())
+	);
+
+	const auto resetBot = [=] {
+		username->setText(QString());
+		username->setFocus();
+	};
+	content->add(object_ptr<Ui::SlideWrap<Ui::RpWidget>>(
+		content,
+		MakeBotPreview(content, _botValue.value(), resetBot)));
+
 	Ui::AddDividerText(
 		content,
 		tr::lng_chatbots_add_about(),
 		st::peerAppearanceDividerTextMargin);
-	Ui::AddSkip(content);
-	Ui::AddSubsectionTitle(content, tr::lng_chatbots_access_title());
 
-	const auto group = std::make_shared<Ui::RadiobuttonGroup>(
-		_onlySelected.current() ? kSelectedOnly : kAllExcept);
-	const auto everyone = content->add(
-		object_ptr<Ui::Radiobutton>(
-			content,
-			group,
-			kAllExcept,
-			tr::lng_chatbots_all_except(tr::now),
-			st::settingsChatbotsAccess),
-		st::settingsChatbotsAccessMargins);
-	const auto selected = content->add(
-		object_ptr<Ui::Radiobutton>(
-			content,
-			group,
-			kSelectedOnly,
-			tr::lng_chatbots_selected(tr::now),
-			st::settingsChatbotsAccess),
-		st::settingsChatbotsAccessMargins);
-
-	Ui::AddSkip(content, st::settingsChatbotsAccessSkip);
-	Ui::AddDivider(content);
-
-	const auto excludeWrap = content->add(
-		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
-			content,
-			object_ptr<Ui::VerticalLayout>(content))
-	)->setDuration(0);
-	const auto excludeInner = excludeWrap->entity();
-
-	Ui::AddSkip(excludeInner);
-	Ui::AddSubsectionTitle(excludeInner, tr::lng_chatbots_excluded_title());
-	const auto excludeAdd = AddButtonWithIcon(
-		excludeInner,
-		tr::lng_chatbots_exclude_button(),
-		st::settingsChatbotsAdd,
-		{ &st::settingsIconRemove, IconType::Round, &st::windowBgActive });
-	excludeAdd->setClickedCallback([=] {
-		EditBusinessExceptions(_controller, {
-			.current = _disallowed.current(),
-			.save = crl::guard(this, [=](Data::BusinessExceptions value) {
-				_disallowed = std::move(value);
-			}),
-			.allow = false,
-		});
-	});
-	SetupBusinessExceptionsPreview(excludeInner, &_disallowed);
-
-	excludeWrap->toggleOn(_onlySelected.value() | rpl::map(!_1));
-	excludeWrap->finishAnimating();
-
-	const auto includeWrap = content->add(
-		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
-			content,
-			object_ptr<Ui::VerticalLayout>(content))
-	)->setDuration(0);
-	const auto includeInner = includeWrap->entity();
-
-	Ui::AddSkip(includeInner);
-	Ui::AddSubsectionTitle(includeInner, tr::lng_chatbots_included_title());
-	const auto includeAdd = AddButtonWithIcon(
-		includeInner,
-		tr::lng_chatbots_include_button(),
-		st::settingsChatbotsAdd,
-		{ &st::settingsIconAdd, IconType::Round, &st::windowBgActive });
-	includeAdd->setClickedCallback([=] {
-		EditBusinessExceptions(_controller, {
-			.current = _allowed.current(),
-			.save = crl::guard(this, [=](Data::BusinessExceptions value) {
-				_allowed = std::move(value);
-			}),
-			.allow = true,
-		});
-	});
-	SetupBusinessExceptionsPreview(includeInner, &_allowed);
-
-	includeWrap->toggleOn(_onlySelected.value());
-	includeWrap->finishAnimating();
-
-	group->setChangedCallback([=](int value) {
-		_onlySelected = (value == kSelectedOnly);
+	AddBusinessRecipientsSelector(content, {
+		.controller = controller,
+		.title = tr::lng_chatbots_access_title(),
+		.data = &_recipients,
 	});
 
 	Ui::AddSkip(content, st::settingsChatbotsAccessSkip);
@@ -243,13 +190,11 @@ void Chatbots::setupContent(
 
 void Chatbots::save() {
 	const auto settings = Data::ChatbotsSettings{
-		.bot = nullptr,
-		.allowed = _allowed.current(),
-		.disallowed = _disallowed.current(),
+		.bot = _botValue.current().bot,
+		.recipients = _recipients.current(),
 		.repliesAllowed = _repliesAllowed.current(),
-		.onlySelected = _onlySelected.current(),
 	};
-	_session->data().chatbots().save(settings);
+	controller()->session().data().chatbots().save(settings);
 }
 
 } // namespace
diff --git a/Telegram/SourceFiles/settings/business/settings_greeting.cpp b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
new file mode 100644
index 000000000..599b25b2c
--- /dev/null
+++ b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
@@ -0,0 +1,117 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "settings/business/settings_greeting.h"
+
+#include "core/application.h"
+#include "data/data_session.h"
+#include "lang/lang_keys.h"
+#include "main/main_session.h"
+#include "settings/business/settings_recipients_helper.h"
+#include "ui/text/text_utilities.h"
+#include "ui/widgets/buttons.h"
+#include "ui/wrap/slide_wrap.h"
+#include "ui/wrap/vertical_layout.h"
+#include "ui/vertical_list.h"
+#include "window/window_session_controller.h"
+#include "styles/style_settings.h"
+
+namespace Settings {
+namespace {
+
+class Greeting : public BusinessSection<Greeting> {
+public:
+	Greeting(
+		QWidget *parent,
+		not_null<Window::SessionController*> controller);
+	~Greeting();
+
+	[[nodiscard]] rpl::producer<QString> title() override;
+
+private:
+	void setupContent(not_null<Window::SessionController*> controller);
+	void save();
+
+	rpl::variable<Data::BusinessRecipients> _recipients;
+
+};
+
+Greeting::Greeting(
+	QWidget *parent,
+	not_null<Window::SessionController*> controller)
+: BusinessSection(parent, controller) {
+	setupContent(controller);
+}
+
+Greeting::~Greeting() {
+	if (!Core::Quitting()) {
+		save();
+	}
+}
+
+rpl::producer<QString> Greeting::title() {
+	return tr::lng_greeting_title();
+}
+
+void Greeting::setupContent(
+		not_null<Window::SessionController*> controller) {
+	using namespace rpl::mappers;
+
+	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
+	//const auto current = controller->session().data().chatbots().current();
+
+	//_recipients = current.recipients;
+
+	AddDividerTextWithLottie(content, {
+		.lottie = u"greeting"_q,
+		.lottieSize = st::settingsCloudPasswordIconSize,
+		.lottieMargins = st::peerAppearanceIconPadding,
+		.showFinished = showFinishes(),
+		.about = tr::lng_greeting_about(Ui::Text::WithEntities),
+		.aboutMargins = st::peerAppearanceCoverLabelMargin,
+	});
+
+	Ui::AddSkip(content);
+	const auto enabled = content->add(object_ptr<Ui::SettingsButton>(
+		content,
+		tr::lng_greeting_enable(),
+		st::settingsButtonNoIcon
+	))->toggleOn(rpl::single(false));
+
+	const auto wrap = content->add(
+		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
+			content,
+			object_ptr<Ui::VerticalLayout>(content)));
+	const auto inner = wrap->entity();
+
+	Ui::AddSkip(inner);
+	Ui::AddDivider(inner);
+
+	wrap->toggleOn(enabled->toggledValue());
+	wrap->finishAnimating();
+
+	AddBusinessRecipientsSelector(inner, {
+		.controller = controller,
+		.title = tr::lng_greeting_recipients(),
+		.data = &_recipients,
+	});
+
+	Ui::AddSkip(inner, st::settingsChatbotsAccessSkip);
+
+	Ui::ResizeFitChild(this, content);
+}
+
+void Greeting::save() {
+}
+
+} // namespace
+
+Type GreetingId() {
+	return Greeting::Id();
+}
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_greeting.h b/Telegram/SourceFiles/settings/business/settings_greeting.h
new file mode 100644
index 000000000..2bb9afd59
--- /dev/null
+++ b/Telegram/SourceFiles/settings/business/settings_greeting.h
@@ -0,0 +1,16 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "settings/settings_type.h"
+
+namespace Settings {
+
+[[nodiscard]] Type GreetingId();
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_location.cpp b/Telegram/SourceFiles/settings/business/settings_location.cpp
new file mode 100644
index 000000000..84e8d4e4b
--- /dev/null
+++ b/Telegram/SourceFiles/settings/business/settings_location.cpp
@@ -0,0 +1,121 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "settings/business/settings_location.h"
+
+#include "core/application.h"
+#include "data/data_session.h"
+#include "lang/lang_keys.h"
+#include "main/main_session.h"
+#include "settings/business/settings_recipients_helper.h"
+#include "ui/text/text_utilities.h"
+#include "ui/widgets/fields/input_field.h"
+#include "ui/wrap/vertical_layout.h"
+#include "ui/vertical_list.h"
+#include "window/window_session_controller.h"
+#include "styles/style_layers.h"
+#include "styles/style_settings.h"
+
+namespace Settings {
+namespace {
+
+class Location : public BusinessSection<Location> {
+public:
+	Location(
+		QWidget *parent,
+		not_null<Window::SessionController*> controller);
+	~Location();
+
+	[[nodiscard]] rpl::producer<QString> title() override;
+
+	const Ui::RoundRect *bottomSkipRounding() const {
+		return mapSupported() ? nullptr : &_bottomSkipRounding;
+	}
+
+private:
+	void setupContent(not_null<Window::SessionController*> controller);
+	void save();
+
+	[[nodiscard]] bool mapSupported() const;
+
+	Ui::RoundRect _bottomSkipRounding;
+
+};
+
+Location::Location(
+	QWidget *parent,
+	not_null<Window::SessionController*> controller)
+: BusinessSection(parent, controller)
+, _bottomSkipRounding(st::boxRadius, st::boxDividerBg) {
+	setupContent(controller);
+}
+
+Location::~Location() {
+	if (!Core::Quitting()) {
+		save();
+	}
+}
+
+rpl::producer<QString> Location::title() {
+	return tr::lng_location_title();
+}
+
+void Location::setupContent(
+	not_null<Window::SessionController*> controller) {
+	using namespace rpl::mappers;
+
+	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
+
+	AddDividerTextWithLottie(content, {
+		.lottie = u"location"_q,
+		.lottieSize = st::settingsCloudPasswordIconSize,
+		.lottieMargins = st::peerAppearanceIconPadding,
+		.showFinished = showFinishes(),
+		.about = tr::lng_location_about(Ui::Text::WithEntities),
+		.aboutMargins = st::peerAppearanceCoverLabelMargin,
+	});
+
+	const auto address = content->add(
+		object_ptr<Ui::InputField>(
+			content,
+			st::settingsLocationAddress,
+			Ui::InputField::Mode::MultiLine,
+			tr::lng_location_address(),
+			QString()),
+		st::settingsChatbotsUsernameMargins);
+
+	if (!mapSupported()) {
+		AddDividerTextWithLottie(content, {
+			.lottie = u"phone"_q,
+			.lottieSize = st::settingsCloudPasswordIconSize,
+			.lottieMargins = st::peerAppearanceIconPadding,
+			.showFinished = showFinishes(),
+			.about = tr::lng_location_fallback(Ui::Text::WithEntities),
+			.aboutMargins = st::peerAppearanceCoverLabelMargin,
+			.parts = RectPart::Top,
+		});
+	} else {
+
+	}
+
+	Ui::ResizeFitChild(this, content);
+}
+
+void Location::save() {
+}
+
+bool Location::mapSupported() const {
+	return false;
+}
+
+} // namespace
+
+Type LocationId() {
+	return Location::Id();
+}
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_location.h b/Telegram/SourceFiles/settings/business/settings_location.h
new file mode 100644
index 000000000..31e033253
--- /dev/null
+++ b/Telegram/SourceFiles/settings/business/settings_location.h
@@ -0,0 +1,16 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "settings/settings_type.h"
+
+namespace Settings {
+
+[[nodiscard]] Type LocationId();
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
new file mode 100644
index 000000000..dc8927bc2
--- /dev/null
+++ b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
@@ -0,0 +1,107 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "settings/business/settings_quick_replies.h"
+
+#include "core/application.h"
+#include "data/data_session.h"
+#include "lang/lang_keys.h"
+#include "main/main_session.h"
+#include "settings/business/settings_recipients_helper.h"
+#include "ui/text/text_utilities.h"
+#include "ui/widgets/buttons.h"
+#include "ui/wrap/slide_wrap.h"
+#include "ui/wrap/vertical_layout.h"
+#include "ui/vertical_list.h"
+#include "window/window_session_controller.h"
+#include "styles/style_settings.h"
+
+namespace Settings {
+namespace {
+
+class QuickReplies : public BusinessSection<QuickReplies> {
+public:
+	QuickReplies(
+		QWidget *parent,
+		not_null<Window::SessionController*> controller);
+	~QuickReplies();
+
+	[[nodiscard]] rpl::producer<QString> title() override;
+
+private:
+	void setupContent(not_null<Window::SessionController*> controller);
+	void save();
+
+	rpl::variable<Data::BusinessRecipients> _recipients;
+
+};
+
+QuickReplies::QuickReplies(
+	QWidget *parent,
+	not_null<Window::SessionController*> controller)
+: BusinessSection(parent, controller) {
+	setupContent(controller);
+}
+
+QuickReplies::~QuickReplies() {
+	if (!Core::Quitting()) {
+		save();
+	}
+}
+
+rpl::producer<QString> QuickReplies::title() {
+	return tr::lng_replies_title();
+}
+
+void QuickReplies::setupContent(
+		not_null<Window::SessionController*> controller) {
+	using namespace rpl::mappers;
+
+	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
+
+	AddDividerTextWithLottie(content, {
+		.lottie = u"writing"_q,
+		.lottieSize = st::settingsCloudPasswordIconSize,
+		.lottieMargins = st::peerAppearanceIconPadding,
+		.showFinished = showFinishes(),
+		.about = tr::lng_replies_about(Ui::Text::WithEntities),
+		.aboutMargins = st::peerAppearanceCoverLabelMargin,
+	});
+
+	Ui::AddSkip(content);
+	const auto enabled = content->add(object_ptr<Ui::SettingsButton>(
+		content,
+		tr::lng_replies_add(),
+		st::settingsButtonNoIcon
+	));
+
+	enabled->setClickedCallback([=] {
+
+	});
+
+	const auto wrap = content->add(
+		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
+			content,
+			object_ptr<Ui::VerticalLayout>(content)));
+	const auto inner = wrap->entity();
+
+	Ui::AddSkip(inner);
+	Ui::AddDivider(inner);
+
+	Ui::ResizeFitChild(this, content);
+}
+
+void QuickReplies::save() {
+}
+
+} // namespace
+
+Type QuickRepliesId() {
+	return QuickReplies::Id();
+}
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_quick_replies.h b/Telegram/SourceFiles/settings/business/settings_quick_replies.h
new file mode 100644
index 000000000..80cc2f129
--- /dev/null
+++ b/Telegram/SourceFiles/settings/business/settings_quick_replies.h
@@ -0,0 +1,16 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "settings/settings_type.h"
+
+namespace Settings {
+
+[[nodiscard]] Type QuickRepliesId();
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp b/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
new file mode 100644
index 000000000..a6288dbee
--- /dev/null
+++ b/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
@@ -0,0 +1,294 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "settings/business/settings_recipients_helper.h"
+
+#include "boxes/filters/edit_filter_chats_list.h"
+#include "boxes/filters/edit_filter_chats_preview.h"
+#include "data/data_session.h"
+#include "data/data_user.h"
+#include "history/history.h"
+#include "lang/lang_keys.h"
+#include "settings/settings_common.h"
+#include "ui/widgets/checkbox.h"
+#include "ui/wrap/slide_wrap.h"
+#include "ui/wrap/vertical_layout.h"
+#include "ui/vertical_list.h"
+#include "window/window_session_controller.h"
+#include "styles/style_settings.h"
+
+namespace Settings {
+namespace {
+
+constexpr auto kAllExcept = 0;
+constexpr auto kSelectedOnly = 1;
+
+using Flag = Data::ChatFilter::Flag;
+using Flags = Data::ChatFilter::Flags;
+
+[[nodiscard]] Flags TypesToFlags(Data::BusinessChatTypes types) {
+	using Type = Data::BusinessChatType;
+	return ((types & Type::Contacts) ? Flag::Contacts : Flag())
+		| ((types & Type::NonContacts) ? Flag::NonContacts : Flag())
+		| ((types & Type::NewChats) ? Flag::NewChats : Flag())
+		| ((types & Type::ExistingChats) ? Flag::ExistingChats : Flag());
+}
+
+[[nodiscard]] Data::BusinessChatTypes FlagsToTypes(Flags flags) {
+	using Type = Data::BusinessChatType;
+	return ((flags & Flag::Contacts) ? Type::Contacts : Type())
+		| ((flags & Flag::NonContacts) ? Type::NonContacts : Type())
+		| ((flags & Flag::NewChats) ? Type::NewChats : Type())
+		| ((flags & Flag::ExistingChats) ? Type::ExistingChats : Type());
+}
+
+} // namespace
+
+void EditBusinessChats(
+		not_null<Window::SessionController*> window,
+		BusinessChatsDescriptor &&descriptor) {
+	const auto session = &window->session();
+	const auto options = Flag::ExistingChats
+		| Flag::NewChats
+		| Flag::Contacts
+		| Flag::NonContacts;
+	auto &&peers = descriptor.current.list | ranges::views::transform([=](
+			not_null<UserData*> user) {
+		return user->owner().history(user);
+	});
+	auto controller = std::make_unique<EditFilterChatsListController>(
+		session,
+		(descriptor.include
+			? tr::lng_filters_include_title()
+			: tr::lng_filters_exclude_title()),
+		options,
+		TypesToFlags(descriptor.current.types) & options,
+		base::flat_set<not_null<History*>>(begin(peers), end(peers)),
+		[=](int count) {
+			return nullptr; AssertIsDebug();
+		});
+	const auto rawController = controller.get();
+	const auto save = descriptor.save;
+	auto initBox = [=](not_null<PeerListBox*> box) {
+		box->setCloseByOutsideClick(false);
+		box->addButton(tr::lng_settings_save(), crl::guard(box, [=] {
+			const auto peers = box->collectSelectedRows();
+			auto &&users = ranges::views::all(
+				peers
+			) | ranges::views::transform([=](not_null<PeerData*> peer) {
+				return not_null(peer->asUser());
+			}) | ranges::to_vector;
+			save(Data::BusinessChats{
+				.types = FlagsToTypes(rawController->chosenOptions()),
+				.list = std::move(users),
+			});
+			box->closeBox();
+		}));
+		box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
+	};
+	window->show(
+		Box<PeerListBox>(std::move(controller), std::move(initBox)));
+}
+
+not_null<FilterChatsPreview*> SetupBusinessChatsPreview(
+		not_null<Ui::VerticalLayout*> container,
+		not_null<rpl::variable<Data::BusinessChats>*> data) {
+	const auto rules = data->current();
+
+	const auto locked = std::make_shared<bool>();
+	auto &&peers = data->current().list | ranges::views::transform([=](
+			not_null<UserData*> user) {
+		return user->owner().history(user);
+	});
+	const auto preview = container->add(object_ptr<FilterChatsPreview>(
+		container,
+		TypesToFlags(data->current().types),
+		base::flat_set<not_null<History*>>(begin(peers), end(peers))));
+
+	preview->flagRemoved(
+	) | rpl::start_with_next([=](Flag flag) {
+		*locked = true;
+		*data = Data::BusinessChats{
+			data->current().types & ~FlagsToTypes(flag),
+			data->current().list
+		};
+		*locked = false;
+	}, preview->lifetime());
+
+	preview->peerRemoved(
+	) | rpl::start_with_next([=](not_null<History*> history) {
+		auto list = data->current().list;
+		list.erase(
+			ranges::remove(list, not_null(history->peer->asUser())),
+			end(list));
+
+		*locked = true;
+		*data = Data::BusinessChats{
+			data->current().types,
+			std::move(list)
+		};
+		*locked = false;
+	}, preview->lifetime());
+
+	data->changes(
+	) | rpl::filter([=] {
+		return !*locked;
+	}) | rpl::start_with_next([=](const Data::BusinessChats &rules) {
+		auto &&peers = rules.list | ranges::views::transform([=](
+				not_null<UserData*> user) {
+			return user->owner().history(user);
+		});
+		preview->updateData(
+			TypesToFlags(rules.types),
+			base::flat_set<not_null<History*>>(begin(peers), end(peers)));
+	}, preview->lifetime());
+
+	return preview;
+}
+
+void AddBusinessRecipientsSelector(
+		not_null<Ui::VerticalLayout*> container,
+		BusinessRecipientsSelectorDescriptor &&descriptor) {
+	Ui::AddSkip(container);
+	Ui::AddSubsectionTitle(container, std::move(descriptor.title));
+
+	auto &lifetime = container->lifetime();
+	const auto controller = descriptor.controller;
+	const auto data = descriptor.data;
+	const auto change = [=](Fn<void(Data::BusinessRecipients&)> modify) {
+		auto now = data->current();
+		modify(now);
+		*data = std::move(now);
+	};
+	const auto group = std::make_shared<Ui::RadiobuttonGroup>(
+		data->current().onlyIncluded ? kSelectedOnly : kAllExcept);
+	const auto everyone = container->add(
+		object_ptr<Ui::Radiobutton>(
+			container,
+			group,
+			kAllExcept,
+			tr::lng_chatbots_all_except(tr::now),
+			st::settingsChatbotsAccess),
+		st::settingsChatbotsAccessMargins);
+	const auto selected = container->add(
+		object_ptr<Ui::Radiobutton>(
+			container,
+			group,
+			kSelectedOnly,
+			tr::lng_chatbots_selected(tr::now),
+			st::settingsChatbotsAccess),
+		st::settingsChatbotsAccessMargins);
+
+	Ui::AddSkip(container, st::settingsChatbotsAccessSkip);
+	Ui::AddDivider(container);
+
+	const auto excludeWrap = container->add(
+		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
+			container,
+			object_ptr<Ui::VerticalLayout>(container))
+	)->setDuration(0);
+	const auto excludeInner = excludeWrap->entity();
+
+	Ui::AddSkip(excludeInner);
+	Ui::AddSubsectionTitle(excludeInner, tr::lng_chatbots_excluded_title());
+	const auto excludeAdd = AddButtonWithIcon(
+		excludeInner,
+		tr::lng_chatbots_exclude_button(),
+		st::settingsChatbotsAdd,
+		{ &st::settingsIconRemove, IconType::Round, &st::windowBgActive });
+	excludeAdd->setClickedCallback([=] {
+		const auto save = [=](Data::BusinessChats value) {
+			change([&](Data::BusinessRecipients &data) {
+				data.excluded = std::move(value);
+			});
+		};
+		EditBusinessChats(controller, {
+			.current = data->current().excluded,
+			.save = crl::guard(excludeAdd, save),
+			.include = false,
+		});
+	});
+
+	const auto excluded = lifetime.make_state<
+		rpl::variable<Data::BusinessChats>
+	>(data->current().excluded);
+	data->changes(
+	) | rpl::start_with_next([=](const Data::BusinessRecipients &value) {
+		*excluded = value.excluded;
+	}, lifetime);
+	excluded->changes(
+	) | rpl::start_with_next([=](Data::BusinessChats &&value) {
+		auto now = data->current();
+		now.excluded = std::move(value);
+		*data = std::move(now);
+	}, lifetime);
+
+	SetupBusinessChatsPreview(excludeInner, excluded);
+
+	excludeWrap->toggleOn(data->value(
+	) | rpl::map([](const Data::BusinessRecipients &value) {
+		return !value.onlyIncluded;
+	}));
+	excludeWrap->finishAnimating();
+
+	const auto includeWrap = container->add(
+		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
+			container,
+			object_ptr<Ui::VerticalLayout>(container))
+	)->setDuration(0);
+	const auto includeInner = includeWrap->entity();
+
+	Ui::AddSkip(includeInner);
+	Ui::AddSubsectionTitle(includeInner, tr::lng_chatbots_included_title());
+	const auto includeAdd = AddButtonWithIcon(
+		includeInner,
+		tr::lng_chatbots_include_button(),
+		st::settingsChatbotsAdd,
+		{ &st::settingsIconAdd, IconType::Round, &st::windowBgActive });
+	includeAdd->setClickedCallback([=] {
+		const auto save = [=](Data::BusinessChats value) {
+			change([&](Data::BusinessRecipients &data) {
+				data.included = std::move(value);
+			});
+		};
+		EditBusinessChats(controller, {
+			.current = data->current().included ,
+			.save = crl::guard(includeAdd, save),
+			.include = true,
+		});
+	});
+
+	const auto included = lifetime.make_state<
+		rpl::variable<Data::BusinessChats>
+	>(data->current().included);
+	data->changes(
+	) | rpl::start_with_next([=](const Data::BusinessRecipients &value) {
+		*included = value.included;
+	}, lifetime);
+	included->changes(
+	) | rpl::start_with_next([=](Data::BusinessChats &&value) {
+		change([&](Data::BusinessRecipients &data) {
+			data.included = std::move(value);
+		});
+	}, lifetime);
+
+	SetupBusinessChatsPreview(includeInner, excluded);
+
+	includeWrap->toggleOn(data->value(
+	) | rpl::map([](const Data::BusinessRecipients &value) {
+		return value.onlyIncluded;
+	}));
+	includeWrap->finishAnimating();
+
+	group->setChangedCallback([=](int value) {
+		change([&](Data::BusinessRecipients &data) {
+			data.onlyIncluded = (value == kSelectedOnly);
+		});
+	});
+}
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_recipients_helper.h b/Telegram/SourceFiles/settings/business/settings_recipients_helper.h
new file mode 100644
index 000000000..60efd7425
--- /dev/null
+++ b/Telegram/SourceFiles/settings/business/settings_recipients_helper.h
@@ -0,0 +1,74 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "data/business/data_business_common.h"
+#include "settings/settings_common_session.h"
+
+class FilterChatsPreview;
+
+namespace Ui {
+class VerticalLayout;
+} // namespace Ui
+
+namespace Window {
+class SessionController;
+} // namespace Window
+
+namespace Settings {
+
+template <typename SectionType>
+class BusinessSection : public Section<SectionType> {
+public:
+	BusinessSection(
+		QWidget *parent,
+		not_null<Window::SessionController*> controller)
+	: Section<SectionType>(parent)
+	, _controller(controller) {
+	}
+
+	[[nodiscard]] not_null<Window::SessionController*> controller() const {
+		return _controller;
+	}
+	[[nodiscard]] rpl::producer<> showFinishes() const {
+		return _showFinished.events();
+	}
+
+private:
+	void showFinished() override {
+		_showFinished.fire({});
+	}
+
+	const not_null<Window::SessionController*> _controller;
+	rpl::event_stream<> _showFinished;
+
+};
+
+struct BusinessChatsDescriptor {
+	Data::BusinessChats current;
+	Fn<void(const Data::BusinessChats&)> save;
+	bool include = false;
+};
+void EditBusinessChats(
+	not_null<Window::SessionController*> window,
+	BusinessChatsDescriptor &&descriptor);
+
+not_null<FilterChatsPreview*> SetupBusinessChatsPreview(
+	not_null<Ui::VerticalLayout*> container,
+	not_null<rpl::variable<Data::BusinessChats>*> data);
+
+struct BusinessRecipientsSelectorDescriptor {
+	not_null<Window::SessionController*> controller;
+	rpl::producer<QString> title;
+	not_null<rpl::variable<Data::BusinessRecipients>*> data;
+};
+void AddBusinessRecipientsSelector(
+	not_null<Ui::VerticalLayout*> container,
+	BusinessRecipientsSelectorDescriptor &&descriptor);
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
new file mode 100644
index 000000000..7b2e87873
--- /dev/null
+++ b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
@@ -0,0 +1,104 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "settings/business/settings_working_hours.h"
+
+#include "core/application.h"
+#include "data/data_session.h"
+#include "lang/lang_keys.h"
+#include "main/main_session.h"
+#include "settings/business/settings_recipients_helper.h"
+#include "ui/text/text_utilities.h"
+#include "ui/widgets/buttons.h"
+#include "ui/wrap/vertical_layout.h"
+#include "ui/wrap/slide_wrap.h"
+#include "ui/vertical_list.h"
+#include "window/window_session_controller.h"
+#include "styles/style_settings.h"
+
+namespace Settings {
+namespace {
+
+class WorkingHours : public BusinessSection<WorkingHours> {
+public:
+	WorkingHours(
+		QWidget *parent,
+		not_null<Window::SessionController*> controller);
+	~WorkingHours();
+
+	[[nodiscard]] rpl::producer<QString> title() override;
+
+private:
+	void setupContent(not_null<Window::SessionController*> controller);
+	void save();
+
+};
+
+WorkingHours::WorkingHours(
+	QWidget *parent,
+	not_null<Window::SessionController*> controller)
+: BusinessSection(parent, controller) {
+	setupContent(controller);
+}
+
+WorkingHours::~WorkingHours() {
+	if (!Core::Quitting()) {
+		save();
+	}
+}
+
+rpl::producer<QString> WorkingHours::title() {
+	return tr::lng_hours_title();
+}
+
+void WorkingHours::setupContent(
+	not_null<Window::SessionController*> controller) {
+	using namespace rpl::mappers;
+
+	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
+
+	AddDividerTextWithLottie(content, {
+		.lottie = u"hours"_q,
+		.lottieSize = st::settingsCloudPasswordIconSize,
+		.lottieMargins = st::peerAppearanceIconPadding,
+		.showFinished = showFinishes(),
+		.about = tr::lng_hours_about(Ui::Text::WithEntities),
+		.aboutMargins = st::peerAppearanceCoverLabelMargin,
+	});
+
+	Ui::AddSkip(content);
+	const auto enabled = content->add(object_ptr<Ui::SettingsButton>(
+		content,
+		tr::lng_hours_show(),
+		st::settingsButtonNoIcon
+	))->toggleOn(rpl::single(false));
+
+	const auto wrap = content->add(
+		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
+			content,
+			object_ptr<Ui::VerticalLayout>(content)));
+	const auto inner = wrap->entity();
+
+	Ui::AddSkip(inner);
+	Ui::AddDivider(inner);
+
+	wrap->toggleOn(enabled->toggledValue());
+	wrap->finishAnimating();
+
+	Ui::ResizeFitChild(this, content);
+}
+
+void WorkingHours::save() {
+}
+
+} // namespace
+
+Type WorkingHoursId() {
+	return WorkingHours::Id();
+}
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_working_hours.h b/Telegram/SourceFiles/settings/business/settings_working_hours.h
new file mode 100644
index 000000000..213ef1488
--- /dev/null
+++ b/Telegram/SourceFiles/settings/business/settings_working_hours.h
@@ -0,0 +1,16 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "settings/settings_type.h"
+
+namespace Settings {
+
+[[nodiscard]] Type WorkingHoursId();
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style
index f4eebf4ff..ef519dced 100644
--- a/Telegram/SourceFiles/settings/settings.style
+++ b/Telegram/SourceFiles/settings/settings.style
@@ -598,6 +598,8 @@ settingsChatbotsUsername: InputField(defaultMultiSelectSearchField) {
 settingsChatbotsAccess: Checkbox(defaultCheckbox) {
 	textPosition: point(18px, 2px);
 }
+settingsLocationAddress: InputField(defaultMultiSelectSearchField) {
+}
 settingsChatbotsUsernameMargins: margins(20px, 8px, 20px, 8px);
 settingsChatbotsAccessMargins: margins(22px, 5px, 22px, 9px);
 settingsChatbotsAccessSkip: 4px;
diff --git a/Telegram/SourceFiles/settings/settings_business.cpp b/Telegram/SourceFiles/settings/settings_business.cpp
index fd56adb41..e72391272 100644
--- a/Telegram/SourceFiles/settings/settings_business.cpp
+++ b/Telegram/SourceFiles/settings/settings_business.cpp
@@ -14,7 +14,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "info/settings/info_settings_widget.h" // SectionCustomTopBarData.
 #include "lang/lang_keys.h"
 #include "main/main_session.h"
+#include "settings/business/settings_away_message.h"
 #include "settings/business/settings_chatbots.h"
+#include "settings/business/settings_greeting.h"
+#include "settings/business/settings_location.h"
+#include "settings/business/settings_quick_replies.h"
+#include "settings/business/settings_working_hours.h"
 #include "settings/settings_common_session.h"
 #include "settings/settings_premium.h"
 #include "ui/effects/gradient.h"
@@ -354,11 +359,17 @@ void Business::setupContent() {
 	Ui::AddSkip(content, st::settingsFromFileTop);
 
 	AddBusinessSummary(content, _controller, [=](BusinessFeature feature) {
-		switch (feature) {
-		case BusinessFeature::Chatbots:
-			_showOther.fire(Settings::ChatbotsId());
-			break;
-		}
+		_showOther.fire([&] {
+			switch (feature) {
+			case BusinessFeature::AwayMessages: return AwayMessageId();
+			case BusinessFeature::OpeningHours: return WorkingHoursId();
+			case BusinessFeature::Location: return LocationId();
+			case BusinessFeature::GreetingMessages: return GreetingId();
+			case BusinessFeature::QuickReplies: return QuickRepliesId();
+			case BusinessFeature::Chatbots: return ChatbotsId();
+			}
+			Unexpected("Feature in Business::setupContent.");
+		}());
 	});
 
 	Ui::ResizeFitChild(this, content);
diff --git a/Telegram/SourceFiles/settings/settings_common.cpp b/Telegram/SourceFiles/settings/settings_common.cpp
index 7e4ac4180..448061167 100644
--- a/Telegram/SourceFiles/settings/settings_common.cpp
+++ b/Telegram/SourceFiles/settings/settings_common.cpp
@@ -173,7 +173,10 @@ void AddDividerTextWithLottie(
 		not_null<Ui::VerticalLayout*> container,
 		DividerWithLottieDescriptor &&descriptor) {
 	const auto divider = Ui::CreateChild<Ui::BoxContentDivider>(
-		container.get());
+		container.get(),
+		0,
+		st::boxDividerBg,
+		descriptor.parts);
 	const auto verticalLayout = container->add(
 		object_ptr<Ui::VerticalLayout>(container.get()));
 	const auto size = descriptor.lottieSize.value_or(
diff --git a/Telegram/SourceFiles/settings/settings_common.h b/Telegram/SourceFiles/settings/settings_common.h
index 00ecf2fe2..ea80b4573 100644
--- a/Telegram/SourceFiles/settings/settings_common.h
+++ b/Telegram/SourceFiles/settings/settings_common.h
@@ -161,6 +161,7 @@ struct DividerWithLottieDescriptor {
 	rpl::producer<> showFinished;
 	rpl::producer<TextWithEntities> about;
 	std::optional<QMargins> aboutMargins;
+	RectParts parts = RectPart::Top | RectPart::Bottom;
 };
 void AddDividerTextWithLottie(
 	not_null<Ui::VerticalLayout*> container,

From 1fe641c4582fd7c1b4f89447d4d11419ea30ab40 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Wed, 21 Feb 2024 17:54:01 +0400
Subject: [PATCH 043/108] Update API scheme to layer 176.

---
 Telegram/SourceFiles/api/api_editing.cpp      |  3 +-
 Telegram/SourceFiles/api/api_polls.cpp        |  6 ++-
 Telegram/SourceFiles/api/api_sending.cpp      |  6 ++-
 Telegram/SourceFiles/api/api_updates.cpp      |  6 ++-
 Telegram/SourceFiles/apiwrap.cpp              | 18 ++++---
 Telegram/SourceFiles/boxes/share_box.cpp      |  3 +-
 .../data/data_scheduled_messages.cpp          |  6 ++-
 Telegram/SourceFiles/data/data_session.cpp    |  3 +-
 .../admin_log/history_admin_log_item.cpp      |  3 +-
 .../media/stories/media_stories_share.cpp     |  3 +-
 Telegram/SourceFiles/mtproto/scheme/api.tl    | 49 +++++++++++++++----
 .../business/settings_away_message.cpp        |  1 -
 .../settings/business/settings_location.cpp   |  4 ++
 .../settings/settings_privacy_controllers.cpp |  3 +-
 .../SourceFiles/window/window_peer_menu.cpp   |  3 +-
 15 files changed, 86 insertions(+), 31 deletions(-)

diff --git a/Telegram/SourceFiles/api/api_editing.cpp b/Telegram/SourceFiles/api/api_editing.cpp
index 005effad9..126223bee 100644
--- a/Telegram/SourceFiles/api/api_editing.cpp
+++ b/Telegram/SourceFiles/api/api_editing.cpp
@@ -101,7 +101,8 @@ mtpRequestId EditMessage(
 		inputMedia.value_or(Data::WebPageForMTP(webpage, text.isEmpty())),
 		MTPReplyMarkup(),
 		sentEntities,
-		MTP_int(options.scheduled)
+		MTP_int(options.scheduled),
+		MTPint() // quick_reply_shortcut_id
 	)).done([=](
 			const MTPUpdates &result,
 			[[maybe_unused]] mtpRequestId requestId) {
diff --git a/Telegram/SourceFiles/api/api_polls.cpp b/Telegram/SourceFiles/api/api_polls.cpp
index 7699894cf..58c959da5 100644
--- a/Telegram/SourceFiles/api/api_polls.cpp
+++ b/Telegram/SourceFiles/api/api_polls.cpp
@@ -84,7 +84,8 @@ void Polls::create(
 			MTPReplyMarkup(),
 			MTPVector<MTPMessageEntity>(),
 			MTP_int(action.options.scheduled),
-			(sendAs ? sendAs->input : MTP_inputPeerEmpty())
+			(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
+			MTPstring() // quick_reply_shortcut
 		), [=](const MTPUpdates &result, const MTP::Response &response) {
 		if (clearCloudDraft) {
 			history->finishSavingCloudDraft(
@@ -173,7 +174,8 @@ void Polls::close(not_null<HistoryItem*> item) {
 		PollDataToInputMedia(poll, true),
 		MTPReplyMarkup(),
 		MTPVector<MTPMessageEntity>(),
-		MTP_int(0) // schedule_date
+		MTP_int(0), // schedule_date
+		MTPint() // quick_reply_shortcut_id
 	)).done([=](const MTPUpdates &result) {
 		_pollCloseRequestIds.erase(itemId);
 		_session->updates().applyUpdates(result);
diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp
index 0e5d7c585..f06bba080 100644
--- a/Telegram/SourceFiles/api/api_sending.cpp
+++ b/Telegram/SourceFiles/api/api_sending.cpp
@@ -161,7 +161,8 @@ void SendExistingMedia(
 				MTPReplyMarkup(),
 				sentEntities,
 				MTP_int(message.action.options.scheduled),
-				(sendAs ? sendAs->input : MTP_inputPeerEmpty())
+				(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
+				MTPstring() // quick_reply_shortcut
 			), [=](const MTPUpdates &result, const MTP::Response &response) {
 		}, [=](const MTP::Error &error, const MTP::Response &response) {
 			if (error.code() == 400
@@ -325,7 +326,8 @@ bool SendDice(MessageToSend &message) {
 			MTPReplyMarkup(),
 			MTP_vector<MTPMessageEntity>(),
 			MTP_int(message.action.options.scheduled),
-			(sendAs ? sendAs->input : MTP_inputPeerEmpty())
+			(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
+			MTPstring() // quick_reply_shortcut
 		), [=](const MTPUpdates &result, const MTP::Response &response) {
 	}, [=](const MTP::Error &error, const MTP::Response &response) {
 		api->sendMessageFail(error, peer, randomId, newId);
diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp
index 2b0450374..679faa063 100644
--- a/Telegram/SourceFiles/api/api_updates.cpp
+++ b/Telegram/SourceFiles/api/api_updates.cpp
@@ -1133,7 +1133,8 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
 				MTPlong(),
 				MTPMessageReactions(),
 				MTPVector<MTPRestrictionReason>(),
-				MTP_int(d.vttl_period().value_or_empty())),
+				MTP_int(d.vttl_period().value_or_empty()),
+				MTPint()), // quick_reply_shortcut_id
 			MessageFlags(),
 			NewMessageType::Unread);
 	} break;
@@ -1166,7 +1167,8 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
 				MTPlong(),
 				MTPMessageReactions(),
 				MTPVector<MTPRestrictionReason>(),
-				MTP_int(d.vttl_period().value_or_empty())),
+				MTP_int(d.vttl_period().value_or_empty()),
+				MTPint()), // quick_reply_shortcut_id
 			MessageFlags(),
 			NewMessageType::Unread);
 	} break;
diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index 6766efe85..97400a701 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -3264,7 +3264,8 @@ void ApiWrap::forwardMessages(
 				peer->input,
 				MTP_int(topMsgId),
 				MTP_int(action.options.scheduled),
-				(sendAs ? sendAs->input : MTP_inputPeerEmpty())
+				(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
+				MTPstring() // quick_reply_shortcut
 			)).done([=](const MTPUpdates &result) {
 				applyUpdates(result);
 				if (shared && !--shared->requestsLeft) {
@@ -3779,7 +3780,8 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
 					MTPReplyMarkup(),
 					sentEntities,
 					MTP_int(message.action.options.scheduled),
-					(sendAs ? sendAs->input : MTP_inputPeerEmpty())
+					(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
+					MTPstring() // quick_reply_shortcut
 				), done, fail);
 		} else {
 			histories.sendPreparedMessage(
@@ -3795,7 +3797,8 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
 					MTPReplyMarkup(),
 					sentEntities,
 					MTP_int(action.options.scheduled),
-					(sendAs ? sendAs->input : MTP_inputPeerEmpty())
+					(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
+					MTPstring() // quick_reply_shortcut
 				), done, fail);
 		}
 		isFirst = false;
@@ -3928,7 +3931,8 @@ void ApiWrap::sendInlineResult(
 			MTP_long(data->getQueryId()),
 			MTP_string(data->getId()),
 			MTP_int(action.options.scheduled),
-			(sendAs ? sendAs->input : MTP_inputPeerEmpty())
+			(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
+			MTPstring() // quick_reply_shortcut
 		), [=](const MTPUpdates &result, const MTP::Response &response) {
 		history->finishSavingCloudDraft(
 			topicRootId,
@@ -4076,7 +4080,8 @@ void ApiWrap::sendMediaWithRandomId(
 			MTPReplyMarkup(),
 			sentEntities,
 			MTP_int(options.scheduled),
-			(options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty())
+			(options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()),
+			MTPstring() // quick_reply_shortcut
 		), [=](const MTPUpdates &result, const MTP::Response &response) {
 		if (done) done(true);
 		if (updateRecentStickers) {
@@ -4174,7 +4179,8 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
 			Data::Histories::ReplyToPlaceholder(),
 			MTP_vector<MTPInputSingleMedia>(medias),
 			MTP_int(album->options.scheduled),
-			(sendAs ? sendAs->input : MTP_inputPeerEmpty())
+			(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
+			MTPstring() // quick_reply_shortcut
 		), [=](const MTPUpdates &result, const MTP::Response &response) {
 		_sendingAlbums.remove(groupId);
 	}, [=](const MTP::Error &error, const MTP::Response &response) {
diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp
index dff268c26..37da38eee 100644
--- a/Telegram/SourceFiles/boxes/share_box.cpp
+++ b/Telegram/SourceFiles/boxes/share_box.cpp
@@ -1558,7 +1558,8 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
 						peer->input,
 						MTP_int(topMsgId),
 						MTP_int(options.scheduled),
-						MTP_inputPeerEmpty() // send_as
+						MTP_inputPeerEmpty(), // send_as
+						MTPstring() // quick_reply_shortcut
 				)).done([=](const MTPUpdates &updates, mtpRequestId reqId) {
 					threadHistory->session().api().applyUpdates(updates);
 					state->requests.remove(reqId);
diff --git a/Telegram/SourceFiles/data/data_scheduled_messages.cpp b/Telegram/SourceFiles/data/data_scheduled_messages.cpp
index 9ea40996e..41f3cd22f 100644
--- a/Telegram/SourceFiles/data/data_scheduled_messages.cpp
+++ b/Telegram/SourceFiles/data/data_scheduled_messages.cpp
@@ -88,7 +88,8 @@ constexpr auto kRequestTimeLimit = 60 * crl::time(1000);
 			MTP_long(data.vgrouped_id().value_or_empty()),
 			MTPMessageReactions(),
 			MTPVector<MTPRestrictionReason>(),
-			MTP_int(data.vttl_period().value_or_empty()));
+			MTP_int(data.vttl_period().value_or_empty()),
+			MTPint()); // quick_reply_shortcut_id
 	});
 }
 
@@ -238,7 +239,8 @@ void ScheduledMessages::sendNowSimpleMessage(
 			MTPlong(),
 			MTPMessageReactions(),
 			MTPVector<MTPRestrictionReason>(),
-			MTP_int(update.vttl_period().value_or_empty())),
+			MTP_int(update.vttl_period().value_or_empty()),
+			MTPint()), // quick_reply_shortcut_id
 		localFlags,
 		NewMessageType::Unread);
 
diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp
index 4efd8fa8b..1b07cf568 100644
--- a/Telegram/SourceFiles/data/data_session.cpp
+++ b/Telegram/SourceFiles/data/data_session.cpp
@@ -4470,7 +4470,8 @@ void Session::insertCheckedServiceNotification(
 				MTPlong(),
 				MTPMessageReactions(),
 				MTPVector<MTPRestrictionReason>(),
-				MTPint()), // ttl_period
+				MTPint(), // ttl_period
+				MTPint()), // quick_reply_shortcut_id
 			localFlags,
 			NewMessageType::Unread);
 	}
diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp
index 0642a1568..b71b0cd82 100644
--- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp
+++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp
@@ -137,7 +137,8 @@ MTPMessage PrepareLogMessage(const MTPMessage &message, TimeId newDate) {
 			MTP_long(0), // grouped_id
 			MTPMessageReactions(),
 			MTPVector<MTPRestrictionReason>(),
-			MTPint()); // ttl_period
+			MTPint(), // ttl_period
+			MTPint()); // quick_reply_shortcut_id
 	});
 }
 
diff --git a/Telegram/SourceFiles/media/stories/media_stories_share.cpp b/Telegram/SourceFiles/media/stories/media_stories_share.cpp
index deb291cc1..aa3ebb075 100644
--- a/Telegram/SourceFiles/media/stories/media_stories_share.cpp
+++ b/Telegram/SourceFiles/media/stories/media_stories_share.cpp
@@ -154,7 +154,8 @@ namespace Media::Stories {
 					MTPReplyMarkup(),
 					MTPVector<MTPMessageEntity>(),
 					MTP_int(action.options.scheduled),
-					MTP_inputPeerEmpty()
+					MTP_inputPeerEmpty(),
+					MTPstring() // quick_reply_shortcut
 				), [=](
 						const MTPUpdates &result,
 						const MTP::Response &response) {
diff --git a/Telegram/SourceFiles/mtproto/scheme/api.tl b/Telegram/SourceFiles/mtproto/scheme/api.tl
index 4b453c9b8..2c4cca0ba 100644
--- a/Telegram/SourceFiles/mtproto/scheme/api.tl
+++ b/Telegram/SourceFiles/mtproto/scheme/api.tl
@@ -114,7 +114,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
 chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto;
 
 messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message;
-message#1e4c8a69 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int = Message;
+message#a66c7efc flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int = Message;
 messageService#2b085862 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction ttl_period:flags.25?int = Message;
 
 messageMediaEmpty#3ded6320 = MessageMedia;
@@ -227,7 +227,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason;
 inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
 inputReportReasonPersonalDetails#9ec7863d = ReportReason;
 
-userFull#b9b12c6c flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> wallpaper:flags.24?WallPaper stories:flags.25?PeerStories = UserFull;
+userFull#e218d7f0 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation = UserFull;
 
 contact#145ade0b user_id:long mutual:Bool = Contact;
 
@@ -403,6 +403,11 @@ updateSavedDialogPinned#aeaf9e74 flags:# pinned:flags.0?true peer:DialogPeer = U
 updatePinnedSavedDialogs#686c85a6 flags:# order:flags.0?Vector<DialogPeer> = Update;
 updateSavedReactionTags#39c67432 = Update;
 updateSmsJob#f16269d4 job_id:string = Update;
+updateQuickReplies#dc3b36d quick_replies:Vector<messages.QuickReply> = Update;
+updateNewQuickReply#ad62c98d quick_reply:messages.QuickReply = Update;
+updateDeleteQuickReply#53e6f1ec shortcut_id:int = Update;
+updateQuickReplyMessage#3e050d0f message:Message = Update;
+updateDeleteQuickReplyMessages#566fe7cd shortcut_id:int messages:Vector<int> = Update;
 
 updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
 
@@ -1660,6 +1665,22 @@ smsjobs.status#2aee9191 flags:# allow_international:flags.0?true recent_sent:int
 
 smsJob#e6a1eeb8 job_id:string phone_number:string text:string = SmsJob;
 
+businessWeeklyOpen#120b1ab9 start_minute:int end_minute:int = BusinessWeeklyOpen;
+
+businessWorkHours#8c92b098 flags:# open_now:flags.0?true timezone_id:string weekly_open:Vector<BusinessWeeklyOpen> = BusinessWorkHours;
+
+businessLocation#be2bf843 geo_point:GeoPoint address:string = BusinessLocation;
+
+timezone#ff9289f5 id:string name:string utc_offset:int = Timezone;
+
+help.timezonesListNotModified#970708cc = help.TimezonesList;
+help.timezonesList#7b74ed71 timezones:Vector<Timezone> hash:int = help.TimezonesList;
+
+messages.quickReply#940ebc72 shortcut_id:int shortcut:string top_message:int count:int = messages.QuickReply;
+
+messages.quickReplies#7cd69880 quick_replies:Vector<messages.QuickReply> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.QuickReplies;
+messages.quickRepliesNotModified#5f91eb5b = messages.QuickReplies;
+
 ---functions---
 
 invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@@ -1785,6 +1806,8 @@ account.updateColor#7cefa15d flags:# for_profile:flags.1?true color:flags.2?int
 account.getDefaultBackgroundEmojis#a60ab9ce hash:long = EmojiList;
 account.getChannelDefaultEmojiStatuses#7727a7d5 hash:long = account.EmojiStatuses;
 account.getChannelRestrictedStatusEmojis#35a9e0d5 hash:long = EmojiList;
+account.updateBusinessWorkHours#4b00e066 flags:# business_work_hours:flags.0?BusinessWorkHours = Bool;
+account.updateBusinessLocation#3dfd3b56 flags:# geo_point:flags.0?InputGeoPoint address:flags.0?string = Bool;
 
 users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
 users.getFullUser#b60f5918 id:InputUser = users.UserFull;
@@ -1826,9 +1849,9 @@ messages.deleteHistory#b08f922a flags:# just_clear:flags.0?true revoke:flags.1?t
 messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
 messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
 messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
-messages.sendMessage#280d096f flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
-messages.sendMedia#72ccc23d flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
-messages.forwardMessages#c661bbc4 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer top_msg_id:flags.9?int schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
+messages.sendMessage#6854c960 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?string = Updates;
+messages.sendMedia#ff5ff75d flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?string = Updates;
+messages.forwardMessages#d5ae95ce flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer top_msg_id:flags.9?int schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?string = Updates;
 messages.reportSpam#cf1592db peer:InputPeer = Bool;
 messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings;
 messages.report#8953ab4e peer:InputPeer id:Vector<int> reason:ReportReason message:string = Bool;
@@ -1871,9 +1894,9 @@ messages.getSavedGifs#5cf09635 hash:long = messages.SavedGifs;
 messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool;
 messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
 messages.setInlineBotResults#bb12a419 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM switch_webview:flags.4?InlineBotWebView = Bool;
-messages.sendInlineBotResult#f7bc68ba flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to:flags.0?InputReplyTo random_id:long query_id:long id:string schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
+messages.sendInlineBotResult#e7bda5b7 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to:flags.0?InputReplyTo random_id:long query_id:long id:string schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?string = Updates;
 messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
-messages.editMessage#48f71778 flags:# no_webpage:flags.1?true invert_media:flags.16?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.15?int = Updates;
+messages.editMessage#dfd14005 flags:# no_webpage:flags.1?true invert_media:flags.16?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.15?int quick_reply_shortcut_id:flags.17?int = Updates;
 messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true invert_media:flags.16?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Bool;
 messages.getBotCallbackAnswer#9342ca07 flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes password:flags.2?InputCheckPasswordSRP = messages.BotCallbackAnswer;
 messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool;
@@ -1906,7 +1929,7 @@ messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool;
 messages.getUnreadMentions#f107e790 flags:# peer:InputPeer top_msg_id:flags.0?int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
 messages.readMentions#36e5bf4d flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory;
 messages.getRecentLocations#702a40e0 peer:InputPeer limit:int hash:long = messages.Messages;
-messages.sendMultiMedia#456e8987 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates;
+messages.sendMultiMedia#87262568 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?string = Updates;
 messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
 messages.searchStickerSets#35705b8a flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets;
 messages.getSplitRanges#1cff7e08 = Vector<MessageRange>;
@@ -2015,6 +2038,13 @@ messages.getSavedReactionTags#3637e05b flags:# peer:flags.0?InputPeer hash:long
 messages.updateSavedReactionTag#60297dec flags:# reaction:Reaction title:flags.0?string = Bool;
 messages.getDefaultTagReactions#bdf93428 hash:long = messages.Reactions;
 messages.getOutboxReadDate#8c4bfe5d peer:InputPeer msg_id:int = OutboxReadDate;
+messages.getQuickReplies#d483f2a8 hash:long = messages.QuickReplies;
+messages.reorderQuickReplies#60331907 order:Vector<int> = Bool;
+messages.checkQuickReplyShortcut#f1d0fbd3 shortcut:string = Bool;
+messages.editQuickReplyShortcut#5c003cef shortcut_id:int shortcut:string = Bool;
+messages.getQuickReplyMessages#94a495c3 flags:# shortcut_id:int id:flags.0?Vector<int> hash:long = messages.Messages;
+messages.sendQuickReplyMessages#33153ad4 peer:InputPeer shortcut_id:int = Updates;
+messages.deleteQuickReplyMessages#e105e910 shortcut_id:int id:Vector<int> = Updates;
 
 updates.getState#edd4882a = updates.State;
 updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference;
@@ -2059,6 +2089,7 @@ help.getCountriesList#735787a8 lang_code:string hash:int = help.CountriesList;
 help.getPremiumPromo#b81b93d4 = help.PremiumPromo;
 help.getPeerColors#da80f42f hash:int = help.PeerColors;
 help.getPeerProfileColors#abcfa9fd hash:int = help.PeerColors;
+help.getTimezonesList#49b30240 hash:int = help.TimezonesList;
 
 channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
 channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages;
@@ -2267,4 +2298,4 @@ smsjobs.getStatus#10a698e8 = smsjobs.Status;
 smsjobs.getSmsJob#778d902f job_id:string = SmsJob;
 smsjobs.finishJob#4f1ebf24 flags:# job_id:string error:flags.0?string = Bool;
 
-// LAYER 175
+// LAYER 176
diff --git a/Telegram/SourceFiles/settings/business/settings_away_message.cpp b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
index de2f1c454..0e22a4c2f 100644
--- a/Telegram/SourceFiles/settings/business/settings_away_message.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
@@ -65,7 +65,6 @@ void AwayMessage::setupContent(
 	//const auto current = controller->session().data().chatbots().current();
 
 	//_recipients = current.recipients;
-
 	AddDividerTextWithLottie(content, {
 		.lottie = u"sleep"_q,
 		.lottieSize = st::settingsCloudPasswordIconSize,
diff --git a/Telegram/SourceFiles/settings/business/settings_location.cpp b/Telegram/SourceFiles/settings/business/settings_location.cpp
index 84e8d4e4b..67a865696 100644
--- a/Telegram/SourceFiles/settings/business/settings_location.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_location.cpp
@@ -88,6 +88,10 @@ void Location::setupContent(
 			QString()),
 		st::settingsChatbotsUsernameMargins);
 
+	showFinishes() | rpl::start_with_next([=] {
+		address->setFocus();
+	}, address->lifetime());
+
 	if (!mapSupported()) {
 		AddDividerTextWithLottie(content, {
 			.lottie = u"phone"_q,
diff --git a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp
index 503ea62e4..5828b2e0e 100644
--- a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp
+++ b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp
@@ -195,7 +195,8 @@ AdminLog::OwnedItem GenerateForwardedItem(
 		MTPlong(), // grouped_id
 		MTPMessageReactions(),
 		MTPVector<MTPRestrictionReason>(),
-		MTPint() // ttl_period
+		MTPint(), // ttl_period
+		MTPint() // quick_reply_shortcut_id
 	).match([&](const MTPDmessage &data) {
 		return history->makeMessage(
 			history->nextNonHistoryEntryId(),
diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp
index bcba1a434..32f09ee80 100644
--- a/Telegram/SourceFiles/window/window_peer_menu.cpp
+++ b/Telegram/SourceFiles/window/window_peer_menu.cpp
@@ -132,7 +132,8 @@ void ShareBotGame(
 			MTPReplyMarkup(),
 			MTPVector<MTPMessageEntity>(),
 			MTP_int(0), // schedule_date
-			MTPInputPeer() // send_as
+			MTPInputPeer(), // send_as
+			MTPstring() // quick_reply_shortcut
 		), [=](const MTPUpdates &, const MTP::Response &) {
 	}, [=](const MTP::Error &error, const MTP::Response &) {
 		history->session().api().sendMessageFail(error, history->peer);

From 4d12f1c0ef2819a30380aab88bf4d24542ffbc39 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Wed, 21 Feb 2024 22:25:52 +0400
Subject: [PATCH 044/108] Initial working hours editing.

---
 Telegram/CMakeLists.txt                       |   3 +
 Telegram/Resources/langs/lang.strings         |   8 +
 .../data/business/data_business_common.cpp    | 158 +++++
 .../data/business/data_business_common.h      |  99 +++
 .../data/business/data_business_info.cpp      |  71 +++
 .../data/business/data_business_info.h        |  41 ++
 Telegram/SourceFiles/data/data_session.cpp    |   4 +-
 Telegram/SourceFiles/data/data_session.h      |   5 +
 .../business/settings_working_hours.cpp       | 580 +++++++++++++++++-
 Telegram/SourceFiles/settings/settings.style  |   7 +
 .../settings/settings_business.cpp            |   4 +
 11 files changed, 978 insertions(+), 2 deletions(-)
 create mode 100644 Telegram/SourceFiles/data/business/data_business_common.cpp
 create mode 100644 Telegram/SourceFiles/data/business/data_business_info.cpp
 create mode 100644 Telegram/SourceFiles/data/business/data_business_info.h

diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index 9b3caa4ef..6010bf833 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -450,7 +450,10 @@ PRIVATE
     countries/countries_manager.h
     data/business/data_business_chatbots.cpp
     data/business/data_business_chatbots.h
+    data/business/data_business_common.cpp
     data/business/data_business_common.h
+    data/business/data_business_info.cpp
+    data/business/data_business_info.h
     data/notify/data_notify_settings.cpp
     data/notify/data_notify_settings.h
     data/notify/data_peer_notify_settings.cpp
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 89508d8c5..dd5746e44 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -2189,6 +2189,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_hours_saturday" = "Saturday";
 "lng_hours_sunday" = "Sunday";
 "lng_hours_closed" = "Closed";
+"lng_hours_open_full" = "Open 24 hours";
+"lng_hours_next_day" = "Next day, {time}";
+"lng_hours_time_zone_title" = "Choose Time Zone";
+"lng_hours_add_button" = "Add a Set of Hours";
+"lng_hours_opening" = "Opening Time";
+"lng_hours_closing" = "Closing Time";
+"lng_hours_remove" = "Remove";
+"lng_hours_about_day" = "Specify your working hours during the day.";
 
 "lng_replies_title" = "Quick Replies";
 "lng_replies_about" = "Set up shortcuts with rich text and media to respond to messages faster.";
diff --git a/Telegram/SourceFiles/data/business/data_business_common.cpp b/Telegram/SourceFiles/data/business/data_business_common.cpp
new file mode 100644
index 000000000..1de65c952
--- /dev/null
+++ b/Telegram/SourceFiles/data/business/data_business_common.cpp
@@ -0,0 +1,158 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "data/business/data_business_common.h"
+
+namespace Data {
+namespace {
+
+constexpr auto kDay = WorkingInterval::kDay;
+constexpr auto kWeek = WorkingInterval::kWeek;
+constexpr auto kInNextDayMax = WorkingInterval::kInNextDayMax;
+
+[[nodiscard]] WorkingIntervals SortAndMerge(WorkingIntervals intervals) {
+	auto &list = intervals.list;
+	ranges::sort(list, ranges::less(), &WorkingInterval::start);
+	for (auto i = 0, count = int(list.size()); i != count; ++i) {
+		if (i && list[i].intersected(list[i - 1])) {
+			list[i - 1] = list[i - 1].united(list[i]);
+			list[i] = {};
+		}
+		if (!list[i]) {
+			list.erase(list.begin() + i);
+			--i;
+			--count;
+		}
+	}
+	return intervals;
+}
+
+[[nodiscard]] WorkingIntervals MoveTailToFront(WorkingIntervals intervals) {
+	auto &list = intervals.list;
+	auto after = WorkingInterval{ kWeek, kWeek + kDay };
+	while (!list.empty()) {
+		if (const auto tail = list.back().intersected(after)) {
+			list.back().end = tail.start;
+			if (!list.back()) {
+				list.pop_back();
+			}
+			list.insert(begin(list), tail.shifted(-kWeek));
+		} else {
+			break;
+		}
+	}
+	return intervals;
+}
+
+} // namespace
+
+WorkingIntervals WorkingIntervals::normalized() const {
+	return SortAndMerge(MoveTailToFront(SortAndMerge(*this)));
+}
+
+Data::WorkingIntervals ExtractDayIntervals(
+		const Data::WorkingIntervals &intervals,
+		int dayIndex) {
+	Expects(dayIndex >= 0 && dayIndex < 7);
+
+	auto result = Data::WorkingIntervals();
+	auto &list = result.list;
+	for (const auto &interval : intervals.list) {
+		const auto now = interval.intersected(
+			{ (dayIndex - 1) * kDay, (dayIndex + 2) * kDay });
+		const auto after = interval.intersected(
+			{ (dayIndex + 6) * kDay, (dayIndex + 9) * kDay });
+		const auto before = interval.intersected(
+			{ (dayIndex - 8) * kDay, (dayIndex - 5) * kDay });
+		if (now) {
+			list.push_back(now.shifted(-dayIndex * kDay));
+		}
+		if (after) {
+			list.push_back(after.shifted(-(dayIndex + 7) * kDay));
+		}
+		if (before) {
+			list.push_back(before.shifted(-(dayIndex - 7) * kDay));
+		}
+	}
+	result = result.normalized();
+
+	const auto outside = [&](Data::WorkingInterval interval) {
+		return (interval.end <= 0) || (interval.start >= kDay);
+	};
+	list.erase(ranges::remove_if(list, outside), end(list));
+
+	if (!list.empty() && list.back().start <= 0 && list.back().end >= kDay) {
+		list.back() = { 0, kDay };
+	} else if (!list.empty() && (list.back().end > kDay + kInNextDayMax)) {
+		list.back() = list.back().intersected({ 0, kDay });
+	}
+	if (!list.empty() && list.front().start <= 0) {
+		if (list.front().start < 0
+			&& list.front().end <= kInNextDayMax
+			&& list.front().start > -kDay) {
+			list.erase(begin(list));
+		} else {
+			list.front() = list.front().intersected({ 0, kDay });
+			if (!list.front()) {
+				list.erase(begin(list));
+			}
+		}
+	}
+
+	return result;
+}
+
+Data::WorkingIntervals RemoveDayIntervals(
+		const Data::WorkingIntervals &intervals,
+		int dayIndex) {
+	auto result = intervals.normalized();
+	auto &list = result.list;
+	const auto day = Data::WorkingInterval{ 0, kDay };
+	const auto shifted = day.shifted(dayIndex * kDay);
+	auto before = Data::WorkingInterval{ 0, shifted.start };
+	auto after = Data::WorkingInterval{ shifted.end, kWeek };
+	for (auto i = 0, count = int(list.size()); i != count; ++i) {
+		if (list[i].end <= shifted.start || list[i].start >= shifted.end) {
+			continue;
+		} else if (list[i].end <= shifted.start + kInNextDayMax
+			&& (list[i].start < shifted.start
+				|| (!dayIndex // This 'Sunday' finishing on next day <= 6:00.
+					&& list[i].start == shifted.start
+					&& list.back().end >= kWeek))) {
+			continue;
+		} else if (const auto first = list[i].intersected(before)) {
+			list[i] = first;
+			if (const auto second = list[i].intersected(after)) {
+				list.push_back(second);
+			}
+		} else if (const auto second = list[i].intersected(after)) {
+			list[i] = second;
+		} else {
+			list.erase(list.begin() + i);
+			--i;
+			--count;
+		}
+	}
+	return result.normalized();
+}
+
+Data::WorkingIntervals ReplaceDayIntervals(
+		const Data::WorkingIntervals &intervals,
+		int dayIndex,
+		Data::WorkingIntervals replacement) {
+	auto result = RemoveDayIntervals(intervals, dayIndex);
+	const auto first = result.list.insert(
+		end(result.list),
+		begin(replacement.list),
+		end(replacement.list));
+	for (auto &interval : ranges::subrange(first, end(result.list))) {
+		interval = interval.shifted(dayIndex * kDay);
+	}
+	return result.normalized();
+}
+
+} // namespace Data
diff --git a/Telegram/SourceFiles/data/business/data_business_common.h b/Telegram/SourceFiles/data/business/data_business_common.h
index 743ddaa12..41fcca431 100644
--- a/Telegram/SourceFiles/data/business/data_business_common.h
+++ b/Telegram/SourceFiles/data/business/data_business_common.h
@@ -42,4 +42,103 @@ struct BusinessRecipients {
 		const BusinessRecipients &b) = default;
 };
 
+struct Timezone {
+	QString id;
+	QString name;
+	TimeId utcOffset = 0;
+
+	friend inline bool operator==(
+		const Timezone &a,
+		const Timezone &b) = default;
+};
+
+struct Timezones {
+	std::vector<Timezone> list;
+
+	friend inline bool operator==(
+		const Timezones &a,
+		const Timezones &b) = default;
+};;
+
+struct WorkingInterval {
+	static constexpr auto kDay = 24 * 3600;
+	static constexpr auto kWeek = 7 * kDay;
+	static constexpr auto kInNextDayMax = 6 * 3600;
+
+	TimeId start = 0;
+	TimeId end = 0;
+
+	explicit operator bool() const {
+		return start < end;
+	}
+
+	[[nodiscard]] WorkingInterval shifted(TimeId offset) const {
+		return { start + offset, end + offset };
+	}
+	[[nodiscard]] WorkingInterval united(WorkingInterval other) const {
+		if (!*this) {
+			return other;
+		} else if (!other) {
+			return *this;
+		}
+		return {
+			std::min(start, other.start),
+			std::max(end, other.end),
+		};
+	}
+	[[nodiscard]] WorkingInterval intersected(WorkingInterval other) const {
+		const auto result = WorkingInterval{
+			std::max(start, other.start),
+			std::min(end, other.end),
+		};
+		return result ? result : WorkingInterval();
+	}
+
+	friend inline bool operator==(
+		const WorkingInterval &a,
+		const WorkingInterval &b) = default;
+};
+
+struct WorkingIntervals {
+	std::vector<WorkingInterval> list;
+
+	[[nodiscard]] WorkingIntervals normalized() const;
+
+	explicit operator bool() const {
+		for (const auto &interval : list) {
+			if (interval) {
+				return true;
+			}
+		}
+		return false;
+	}
+	friend inline bool operator==(
+		const WorkingIntervals &a,
+		const WorkingIntervals &b) = default;
+};
+
+struct WorkingHours {
+	WorkingIntervals intervals;
+	QString timezoneId;
+
+	[[nodiscard]] WorkingHours normalized() const {
+		return { intervals.normalized(), timezoneId };
+	}
+
+	friend inline bool operator==(
+		const WorkingHours &a,
+		const WorkingHours &b) = default;
+};
+
+[[nodiscard]] Data::WorkingIntervals ExtractDayIntervals(
+	const Data::WorkingIntervals &intervals,
+	int dayIndex);
+[[nodiscard]] Data::WorkingIntervals RemoveDayIntervals(
+	const Data::WorkingIntervals &intervals,
+	int dayIndex);
+[[nodiscard]] Data::WorkingIntervals ReplaceDayIntervals(
+	const Data::WorkingIntervals &intervals,
+	int dayIndex,
+	Data::WorkingIntervals replacement);
+
 } // namespace Data
diff --git a/Telegram/SourceFiles/data/business/data_business_info.cpp b/Telegram/SourceFiles/data/business/data_business_info.cpp
new file mode 100644
index 000000000..c4623bb1f
--- /dev/null
+++ b/Telegram/SourceFiles/data/business/data_business_info.cpp
@@ -0,0 +1,71 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "data/business/data_business_info.h"
+
+#include "apiwrap.h"
+#include "data/data_session.h"
+#include "main/main_session.h"
+
+namespace Data {
+
+BusinessInfo::BusinessInfo(not_null<Session*> owner)
+: _owner(owner) {
+}
+
+BusinessInfo::~BusinessInfo() = default;
+
+const WorkingHours &BusinessInfo::workingHours() const {
+	return _workingHours.current();
+}
+
+rpl::producer<WorkingHours> BusinessInfo::workingHoursValue() const {
+	return _workingHours.value();
+}
+
+void BusinessInfo::saveWorkingHours(WorkingHours data) {
+	_workingHours = std::move(data);
+}
+
+void BusinessInfo::preload() {
+	preloadTimezones();
+}
+
+void BusinessInfo::preloadTimezones() {
+	if (!_timezones.current().list.empty() || _timezonesRequestId) {
+		return;
+	}
+	_timezonesRequestId = _owner->session().api().request(
+		MTPhelp_GetTimezonesList(MTP_int(_timezonesHash))
+	).done([=](const MTPhelp_TimezonesList &result) {
+		result.match([&](const MTPDhelp_timezonesList &data) {
+			_timezonesHash = data.vhash().v;
+			const auto proj = [](const MTPtimezone &result) {
+				return Timezone{
+					.id = qs(result.data().vid()),
+					.name = qs(result.data().vname()),
+					.utcOffset = result.data().vutc_offset().v,
+				};
+			};
+			_timezones = Timezones{
+				.list = ranges::views::all(
+					data.vtimezones().v
+				) | ranges::views::transform(
+					proj
+				) | ranges::to_vector,
+			};
+		}, [](const MTPDhelp_timezonesListNotModified &) {
+		});
+	}).send();
+}
+
+rpl::producer<Timezones> BusinessInfo::timezonesValue() const {
+	const_cast<BusinessInfo*>(this)->preloadTimezones();
+	return _timezones.value();
+}
+
+} // namespace Data
diff --git a/Telegram/SourceFiles/data/business/data_business_info.h b/Telegram/SourceFiles/data/business/data_business_info.h
new file mode 100644
index 000000000..f109165d7
--- /dev/null
+++ b/Telegram/SourceFiles/data/business/data_business_info.h
@@ -0,0 +1,41 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "data/business/data_business_common.h"
+
+namespace Data {
+
+class Session;
+
+class BusinessInfo final {
+public:
+	explicit BusinessInfo(not_null<Session*> owner);
+	~BusinessInfo();
+
+	[[nodiscard]] const WorkingHours &workingHours() const;
+	[[nodiscard]] rpl::producer<WorkingHours> workingHoursValue() const;
+	void saveWorkingHours(WorkingHours data);
+
+	void preload();
+	void preloadTimezones();
+	[[nodiscard]] rpl::producer<Timezones> timezonesValue() const;
+
+private:
+	const not_null<Session*> _owner;
+
+	rpl::variable<WorkingHours> _workingHours;
+
+	rpl::variable<Timezones> _timezones;
+
+	mtpRequestId _timezonesRequestId = 0;
+	int32 _timezonesHash = 0;
+
+};
+
+} // namespace Data
diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp
index 1b07cf568..37fb68e86 100644
--- a/Telegram/SourceFiles/data/data_session.cpp
+++ b/Telegram/SourceFiles/data/data_session.cpp
@@ -38,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "passport/passport_form_controller.h"
 #include "lang/lang_keys.h" // tr::lng_deleted(tr::now) in user name
 #include "data/business/data_business_chatbots.h"
+#include "data/business/data_business_info.h"
 #include "data/stickers/data_stickers.h"
 #include "data/notify/data_notify_settings.h"
 #include "data/data_bot_app.h"
@@ -270,7 +271,8 @@ Session::Session(not_null<Main::Session*> session)
 , _customEmojiManager(std::make_unique<CustomEmojiManager>(this))
 , _stories(std::make_unique<Stories>(this))
 , _savedMessages(std::make_unique<SavedMessages>(this))
-, _chatbots(std::make_unique<Chatbots>(this)) {
+, _chatbots(std::make_unique<Chatbots>(this))
+, _businessInfo(std::make_unique<BusinessInfo>(this)) {
 	_cache->open(_session->local().cacheKey());
 	_bigFileCache->open(_session->local().cacheBigFileKey());
 
diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h
index 4fc7b1db1..aab939cb4 100644
--- a/Telegram/SourceFiles/data/data_session.h
+++ b/Telegram/SourceFiles/data/data_session.h
@@ -63,6 +63,7 @@ class CustomEmojiManager;
 class Stories;
 class SavedMessages;
 class Chatbots;
+class BusinessInfo;
 struct ReactionId;
 
 struct RepliesReadTillUpdate {
@@ -146,6 +147,9 @@ public:
 	[[nodiscard]] Chatbots &chatbots() const {
 		return *_chatbots;
 	}
+	[[nodiscard]] BusinessInfo &businessInfo() const {
+		return *_businessInfo;
+	}
 
 	[[nodiscard]] MsgId nextNonHistoryEntryId() {
 		return ++_nonHistoryEntryId;
@@ -1070,6 +1074,7 @@ private:
 	const std::unique_ptr<Stories> _stories;
 	const std::unique_ptr<SavedMessages> _savedMessages;
 	const std::unique_ptr<Chatbots> _chatbots;
+	const std::unique_ptr<BusinessInfo> _businessInfo;
 
 	MsgId _nonHistoryEntryId = ServerMaxMsgId.bare + ScheduledMsgIdsRange;
 
diff --git a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
index 7b2e87873..fbe4ccc60 100644
--- a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
@@ -7,22 +7,35 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "settings/business/settings_working_hours.h"
 
+#include "base/event_filter.h"
+#include "base/unixtime.h"
 #include "core/application.h"
+#include "data/business/data_business_info.h"
 #include "data/data_session.h"
 #include "lang/lang_keys.h"
 #include "main/main_session.h"
 #include "settings/business/settings_recipients_helper.h"
+#include "ui/layers/generic_box.h"
 #include "ui/text/text_utilities.h"
 #include "ui/widgets/buttons.h"
+#include "ui/widgets/checkbox.h"
+#include "ui/widgets/labels.h"
+#include "ui/widgets/vertical_drum_picker.h"
 #include "ui/wrap/vertical_layout.h"
 #include "ui/wrap/slide_wrap.h"
 #include "ui/vertical_list.h"
 #include "window/window_session_controller.h"
+#include "styles/style_boxes.h"
+#include "styles/style_layers.h"
 #include "styles/style_settings.h"
 
 namespace Settings {
 namespace {
 
+constexpr auto kDay = Data::WorkingInterval::kDay;
+constexpr auto kWeek = Data::WorkingInterval::kWeek;
+constexpr auto kInNextDayMax = Data::WorkingInterval::kInNextDayMax;
+
 class WorkingHours : public BusinessSection<WorkingHours> {
 public:
 	WorkingHours(
@@ -36,8 +49,492 @@ private:
 	void setupContent(not_null<Window::SessionController*> controller);
 	void save();
 
+	rpl::variable<Data::WorkingHours> _hours;
+
 };
 
+[[nodiscard]] QString TimezoneFullName(const Data::Timezone &data) {
+	const auto abs = std::abs(data.utcOffset);
+	const auto hours = abs / 3600;
+	const auto minutes = (abs % 3600) / 60;
+	const auto seconds = abs % 60;
+	const auto sign = (data.utcOffset < 0) ? '-' : '+';
+	const auto prefix = u"(UTC"_q
+		+ sign
+		+ QString::number(hours)
+		+ u":"_q
+		+ QString::number(minutes).rightJustified(2, u'0')
+		+ u")"_q;
+	return prefix + ' ' + data.name;
+}
+
+[[nodiscard]] QString FindClosestTimezoneId(
+		const std::vector<Data::Timezone> &list) {
+	const auto local = QDateTime::currentDateTime();
+	const auto utc = QDateTime(local.date(), local.time(), Qt::UTC);
+	const auto shift = base::unixtime::now() - (TimeId)::time(nullptr);
+	const auto delta = int(utc.toSecsSinceEpoch())
+		- int(local.toSecsSinceEpoch())
+		- shift;
+	const auto proj = [&](const Data::Timezone &value) {
+		auto distance = value.utcOffset - delta;
+		while (distance > 12 * 3600) {
+			distance -= 24 * 3600;
+		}
+		while (distance < -12 * 3600) {
+			distance += 24 * 3600;
+		}
+		return std::abs(distance);
+	};
+	return ranges::min_element(list, ranges::less(), proj)->id;
+}
+
+[[nodiscard]] QString FormatDayTime(
+		TimeId time,
+		bool showEndAsNextDay = false) {
+	const auto wrap = [](TimeId value) {
+		const auto hours = value / 3600;
+		const auto minutes = (value % 3600) / 60;
+		return QString::number(hours).rightJustified(2, u'0')
+			+ ':'
+			+ QString::number(minutes).rightJustified(2, u'0');
+	};
+	return (time > kDay || (showEndAsNextDay && time == kDay))
+		? tr::lng_hours_next_day(tr::now, lt_time, wrap(time - kDay))
+		: wrap(time == kDay ? 0 : time);
+}
+
+[[nodiscard]] QString JoinIntervals(const Data::WorkingIntervals &data) {
+	auto result = QStringList();
+	result.reserve(data.list.size());
+	for (const auto &interval : data.list) {
+		const auto start = FormatDayTime(interval.start);
+		const auto end = FormatDayTime(interval.end);
+		result.push_back(start + u" - "_q + end);
+	}
+	return result.join(u", "_q);
+}
+
+void EditTimeBox(
+		not_null<Ui::GenericBox*> box,
+		TimeId low,
+		TimeId high,
+		TimeId value,
+		Fn<void(TimeId)> save) {
+	Expects(low <= high);
+
+	const auto values = (high - low + 60) / 60;
+	const auto startIndex = (value - low) / 60;
+
+	const auto content = box->addRow(object_ptr<Ui::FixedHeightWidget>(
+		box,
+		st::settingsWorkingHoursPicker));
+
+	const auto font = st::boxTextFont;
+	const auto itemHeight = st::settingsWorkingHoursPickerItemHeight;
+	auto paintCallback = [=](
+			QPainter &p,
+			int index,
+			float64 y,
+			float64 distanceFromCenter,
+			int outerWidth) {
+		const auto r = QRectF(0, y, outerWidth, itemHeight);
+		const auto progress = std::abs(distanceFromCenter);
+		const auto revProgress = 1. - progress;
+		p.save();
+		p.translate(r.center());
+		constexpr auto kMinYScale = 0.2;
+		const auto yScale = kMinYScale
+			+ (1. - kMinYScale) * anim::easeOutCubic(1., revProgress);
+		p.scale(1., yScale);
+		p.translate(-r.center());
+		p.setOpacity(revProgress);
+		p.setFont(font);
+		p.setPen(st::defaultFlatLabel.textFg);
+		p.drawText(r, FormatDayTime(low + index * 60, true), style::al_center);
+		p.restore();
+	};
+
+	const auto picker = Ui::CreateChild<Ui::VerticalDrumPicker>(
+		content,
+		std::move(paintCallback),
+		values,
+		itemHeight,
+		startIndex);
+
+	content->sizeValue(
+	) | rpl::start_with_next([=](const QSize &s) {
+		picker->resize(s.width(), s.height());
+		picker->moveToLeft((s.width() - picker->width()) / 2, 0);
+	}, content->lifetime());
+
+	content->paintRequest(
+	) | rpl::start_with_next([=](const QRect &r) {
+		auto p = QPainter(content);
+
+		p.fillRect(r, Qt::transparent);
+
+		const auto lineRect = QRect(
+			0,
+			content->height() / 2,
+			content->width(),
+			st::defaultInputField.borderActive);
+		p.fillRect(lineRect.translated(0, itemHeight / 2), st::activeLineFg);
+		p.fillRect(lineRect.translated(0, -itemHeight / 2), st::activeLineFg);
+	}, content->lifetime());
+
+	base::install_event_filter(content, [=](not_null<QEvent*> e) {
+		if ((e->type() == QEvent::MouseButtonPress)
+			|| (e->type() == QEvent::MouseButtonRelease)
+			|| (e->type() == QEvent::MouseMove)) {
+			picker->handleMouseEvent(static_cast<QMouseEvent*>(e.get()));
+		} else if (e->type() == QEvent::Wheel) {
+			picker->handleWheelEvent(static_cast<QWheelEvent*>(e.get()));
+		}
+		return base::EventFilterResult::Continue;
+	});
+	base::install_event_filter(box, [=](not_null<QEvent*> e) {
+		if (e->type() == QEvent::KeyPress) {
+			picker->handleKeyEvent(static_cast<QKeyEvent*>(e.get()));
+		}
+		return base::EventFilterResult::Continue;
+	});
+
+	box->addButton(tr::lng_settings_save(), [=] {
+		const auto weak = Ui::MakeWeak(box);
+		save(std::clamp(low + picker->index() * 60, low, high));
+		if (const auto strong = weak.data()) {
+			strong->closeBox();
+		}
+	});
+	box->addButton(tr::lng_cancel(), [=] {
+		box->closeBox();
+	});
+}
+
+void EditDayBox(
+		not_null<Ui::GenericBox*> box,
+		rpl::producer<QString> title,
+		Data::WorkingIntervals intervals,
+		Fn<void(Data::WorkingIntervals)> save) {
+	box->setTitle(std::move(title));
+	box->setWidth(st::boxWideWidth);
+	struct State {
+		rpl::variable<Data::WorkingIntervals> data;
+	};
+	const auto state = box->lifetime().make_state<State>(State{
+		.data = std::move(intervals),
+	});
+
+	const auto container = box->verticalLayout();
+	const auto rows = container->add(
+		object_ptr<Ui::VerticalLayout>(container));
+	const auto makeRow = [=](
+			Data::WorkingInterval interval,
+			TimeId min,
+			TimeId max) {
+		auto result = object_ptr<Ui::VerticalLayout>(rows);
+		const auto raw = result.data();
+		AddDivider(raw);
+		AddSkip(raw);
+		AddButtonWithLabel(
+			raw,
+			tr::lng_hours_opening(),
+			rpl::single(FormatDayTime(interval.start, true)),
+			st::settingsButtonNoIcon
+		)->setClickedCallback([=] {
+			const auto max = std::max(min, interval.end - 60);
+			const auto now = std::clamp(interval.start, min, max);
+			const auto save = crl::guard(box, [=](TimeId value) {
+				auto now = state->data.current();
+				const auto i = ranges::find(now.list, interval);
+				if (i != end(now.list)) {
+					i->start = value;
+					state->data = now.normalized();
+				}
+			});
+			box->getDelegate()->show(Box(EditTimeBox, min, max, now, save));
+		});
+		AddButtonWithLabel(
+			raw,
+			tr::lng_hours_closing(),
+			rpl::single(FormatDayTime(interval.end, true)),
+			st::settingsButtonNoIcon
+		)->setClickedCallback([=] {
+			const auto min = std::min(max, interval.start + 60);
+			const auto now = std::clamp(interval.end, min, max);
+			const auto save = crl::guard(box, [=](TimeId value) {
+				auto now = state->data.current();
+				const auto i = ranges::find(now.list, interval);
+				if (i != end(now.list)) {
+					i->end = value;
+					state->data = now.normalized();
+				}
+			});
+			box->getDelegate()->show(Box(EditTimeBox, min, max, now, save));
+		});
+		raw->add(object_ptr<Ui::SettingsButton>(
+			raw,
+			tr::lng_hours_remove(),
+			st::settingsAttentionButton
+		))->setClickedCallback([=] {
+			auto now = state->data.current();
+			const auto i = ranges::find(now.list, interval);
+			if (i != end(now.list)) {
+				now.list.erase(i);
+				state->data = std::move(now);
+			}
+		});
+		AddSkip(raw);
+
+		return result;
+	};
+
+	const auto addWrap = container->add(
+		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
+			container,
+			object_ptr<Ui::VerticalLayout>(container)));
+	AddDivider(addWrap->entity());
+	AddSkip(addWrap->entity());
+	const auto add = addWrap->entity()->add(
+		object_ptr<Ui::SettingsButton>(
+			container,
+			tr::lng_hours_add_button(),
+			st::settingsButtonLightNoIcon));
+	add->setClickedCallback([=] {
+		auto now = state->data.current();
+		if (now.list.empty()) {
+			now.list.push_back({ 8 * 3600, 20 * 3600 });
+		} else if (const auto last = now.list.back().end; last + 60 < kDay) {
+			const auto from = std::max(
+				std::min(last + 30 * 60, kDay - 30 * 60),
+				last + 60);
+			const auto till = std::min(from + 4 * 3600, kDay + 30 * 60);
+			now.list.push_back({ from, from + 4 * 3600 });
+		}
+		state->data = std::move(now);
+	});
+
+	state->data.value(
+	) | rpl::start_with_next([=](const Data::WorkingIntervals &data) {
+		const auto count = int(data.list.size());
+		for (auto i = 0; i != count; ++i) {
+			const auto min = (i == 0) ? 0 : (data.list[i - 1].end + 60);
+			const auto max = (i == count - 1)
+				? (kDay + kInNextDayMax)
+				: (data.list[i + 1].start - 60);
+			rows->insert(i, makeRow(data.list[i], min, max));
+			if (rows->count() > i + 1) {
+				delete rows->widgetAt(i + 1);
+			}
+		}
+		while (rows->count() > count) {
+			delete rows->widgetAt(count);
+		}
+		rows->resizeToWidth(st::boxWideWidth);
+		addWrap->toggle(data.list.empty()
+			|| data.list.back().end + 60 < kDay, anim::type::instant);
+		add->clearState();
+	}, add->lifetime());
+	addWrap->finishAnimating();
+
+	AddSkip(container);
+	AddDividerText(container, tr::lng_hours_about_day());
+
+	box->addButton(tr::lng_settings_save(), [=] {
+		const auto weak = Ui::MakeWeak(box);
+		save(state->data.current());
+		if (const auto strong = weak.data()) {
+			strong->closeBox();
+		}
+	});
+	box->addButton(tr::lng_cancel(), [=] {
+		box->closeBox();
+	});
+}
+
+void ChooseTimezoneBox(
+		not_null<Ui::GenericBox*> box,
+		std::vector<Data::Timezone> list,
+		QString id,
+		Fn<void(QString)> save) {
+	Expects(!list.empty());
+	box->setWidth(st::boxWideWidth);
+	box->setTitle(tr::lng_hours_time_zone_title());
+
+	const auto height = st::boxWideWidth;
+	box->setMaxHeight(height);
+
+	ranges::sort(list, ranges::less(), [](const Data::Timezone &value) {
+		return std::pair(value.utcOffset, value.name);
+	});
+
+	if (!ranges::contains(list, id, &Data::Timezone::id)) {
+		id = FindClosestTimezoneId(list);
+	}
+	const auto i = ranges::find(list, id, &Data::Timezone::id);
+	const auto value = int(i - begin(list));
+	const auto group = std::make_shared<Ui::RadiobuttonGroup>(value);
+	const auto radioPadding = st::defaultCheckbox.margin;
+	const auto max = std::max(radioPadding.top(), radioPadding.bottom());
+	auto index = 0;
+	auto padding = st::boxRowPadding + QMargins(0, max, 0, max);
+	auto selected = (Ui::Radiobutton*)nullptr;
+	for (const auto &entry : list) {
+		const auto button = box->addRow(
+			object_ptr<Ui::Radiobutton>(
+				box,
+				group,
+				index++,
+				TimezoneFullName(entry)),
+			padding);
+		if (index == value + 1) {
+			selected = button;
+		}
+		padding = st::boxRowPadding + QMargins(0, 0, 0, max);
+	}
+	if (selected) {
+		box->verticalLayout()->resizeToWidth(st::boxWideWidth);
+		const auto y = selected->y() - (height - selected->height()) / 2;
+		box->setInitScrollCallback([=] {
+			box->scrollToY(y);
+		});
+	}
+	group->setChangedCallback([=](int index) {
+		const auto weak = Ui::MakeWeak(box);
+		save(list[index].id);
+		if (const auto strong = weak.data()) {
+			strong->closeBox();
+		}
+	});
+	box->addButton(tr::lng_close(), [=] {
+		box->closeBox();
+	});
+}
+
+void AddWeekButton(
+		not_null<Ui::VerticalLayout*> container,
+		not_null<Window::SessionController*> controller,
+		int index,
+		not_null<rpl::variable<Data::WorkingHours>*> data) {
+	auto label = [&] {
+		switch (index) {
+		case 0: return tr::lng_hours_monday();
+		case 1: return tr::lng_hours_tuesday();
+		case 2: return tr::lng_hours_wednesday();
+		case 3: return tr::lng_hours_thursday();
+		case 4: return tr::lng_hours_friday();
+		case 5: return tr::lng_hours_saturday();
+		case 6: return tr::lng_hours_sunday();
+		}
+		Unexpected("Index in AddWeekButton.");
+	}();
+	const auto &st = st::settingsWorkingHoursWeek;
+	const auto button = AddButtonWithIcon(
+		container,
+		rpl::duplicate(label),
+		st);
+	button->setClickedCallback([=] {
+		const auto done = [=](Data::WorkingIntervals intervals) {
+			auto now = data->current();
+			now.intervals = ReplaceDayIntervals(
+				now.intervals,
+				index,
+				std::move(intervals));
+			*data = now.normalized();
+		};
+		controller->show(Box(
+			EditDayBox,
+			rpl::duplicate(label),
+			ExtractDayIntervals(data->current().intervals, index),
+			crl::guard(button, done)));
+	});
+
+	const auto toggleButton = Ui::CreateChild<Ui::SettingsButton>(
+		container.get(),
+		nullptr,
+		st);
+	const auto checkView = button->lifetime().make_state<Ui::ToggleView>(
+		st.toggle,
+		false,
+		[=] { toggleButton->update(); });
+
+	auto status = data->value(
+	) | rpl::map([=](const Data::WorkingHours &data) {
+		using namespace Data;
+
+		const auto intervals = ExtractDayIntervals(data.intervals, index);
+		const auto empty = intervals.list.empty();
+		if (checkView->checked() == empty) {
+			checkView->setChecked(!empty, anim::type::instant);
+		}
+		if (!intervals) {
+			return tr::lng_hours_closed();
+		} else if (intervals.list.front() == WorkingInterval{ 0, kDay }) {
+			return tr::lng_hours_open_full();
+		}
+		return rpl::single(JoinIntervals(intervals));
+	}) | rpl::flatten_latest();
+	const auto details = Ui::CreateChild<Ui::FlatLabel>(
+		button.get(),
+		std::move(status),
+		st::settingsWorkingHoursDetails);
+	details->show();
+	details->moveToLeft(
+		st.padding.left(),
+		st.padding.top() + st.height - details->height());
+	details->setAttribute(Qt::WA_TransparentForMouseEvents);
+
+	const auto separator = Ui::CreateChild<Ui::RpWidget>(container.get());
+	separator->paintRequest(
+	) | rpl::start_with_next([=, bg = st.textBgOver] {
+		auto p = QPainter(separator);
+		p.fillRect(separator->rect(), bg);
+	}, separator->lifetime());
+	const auto separatorHeight = st.height - 2 * st.toggle.border;
+	button->geometryValue(
+	) | rpl::start_with_next([=](const QRect &r) {
+		const auto w = st::rightsButtonToggleWidth;
+		toggleButton->setGeometry(
+			r.x() + r.width() - w,
+			r.y(),
+			w,
+			r.height());
+		separator->setGeometry(
+			toggleButton->x() - st::lineWidth,
+			r.y() + (r.height() - separatorHeight) / 2,
+			st::lineWidth,
+			separatorHeight);
+	}, toggleButton->lifetime());
+
+	const auto checkWidget = Ui::CreateChild<Ui::RpWidget>(toggleButton);
+	checkWidget->resize(checkView->getSize());
+	checkWidget->paintRequest(
+	) | rpl::start_with_next([=] {
+		auto p = QPainter(checkWidget);
+		checkView->paint(p, 0, 0, checkWidget->width());
+	}, checkWidget->lifetime());
+	toggleButton->sizeValue(
+	) | rpl::start_with_next([=](const QSize &s) {
+		checkWidget->moveToRight(
+			st.toggleSkip,
+			(s.height() - checkWidget->height()) / 2);
+	}, toggleButton->lifetime());
+
+	toggleButton->setClickedCallback([=] {
+		const auto enabled = !checkView->checked();
+		checkView->setChecked(enabled, anim::type::normal);
+		auto now = data->current();
+		now.intervals = ReplaceDayIntervals(
+			now.intervals,
+			index,
+			(enabled
+				? Data::WorkingIntervals{ { { 0, kDay } } }
+				: Data::WorkingIntervals()));
+		*data = now.normalized();
+	});
+}
+
 WorkingHours::WorkingHours(
 	QWidget *parent,
 	not_null<Window::SessionController*> controller)
@@ -56,11 +553,21 @@ rpl::producer<QString> WorkingHours::title() {
 }
 
 void WorkingHours::setupContent(
-	not_null<Window::SessionController*> controller) {
+		not_null<Window::SessionController*> controller) {
 	using namespace rpl::mappers;
 
 	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
 
+	struct State {
+		rpl::variable<Data::Timezones> timezones;
+		bool timezoneEditPending = false;
+	};
+	const auto info = &controller->session().data().businessInfo();
+	const auto state = content->lifetime().make_state<State>(State{
+		.timezones = info->timezonesValue(),
+	});
+	_hours = info->workingHours();
+
 	AddDividerTextWithLottie(content, {
 		.lottie = u"hours"_q,
 		.lottieSize = st::settingsCloudPasswordIconSize,
@@ -85,6 +592,75 @@ void WorkingHours::setupContent(
 
 	Ui::AddSkip(inner);
 	Ui::AddDivider(inner);
+	Ui::AddSkip(inner);
+
+	for (auto i = 0; i != 7; ++i) {
+		AddWeekButton(inner, controller, i, &_hours);
+	}
+
+	Ui::AddSkip(inner);
+	Ui::AddDivider(inner);
+	Ui::AddSkip(inner);
+
+	state->timezones.value(
+	) | rpl::filter([=](const Data::Timezones &value) {
+		return !value.list.empty();
+	}) | rpl::start_with_next([=](const Data::Timezones &value) {
+		const auto now = _hours.current().timezoneId;
+		if (!ranges::contains(value.list, now, &Data::Timezone::id)) {
+			auto copy = _hours.current();
+			copy.timezoneId = FindClosestTimezoneId(value.list);
+			_hours = std::move(copy);
+		}
+	}, inner->lifetime());
+
+	auto timezoneLabel = rpl::combine(
+		_hours.value(),
+		state->timezones.value()
+	) | rpl::map([](
+			const Data::WorkingHours &hours,
+			const Data::Timezones &timezones) {
+		const auto i = ranges::find(
+			timezones.list,
+			hours.timezoneId,
+			&Data::Timezone::id);
+		return (i != end(timezones.list)) ? TimezoneFullName(*i) : QString();
+	});
+	const auto editTimezone = [=](const std::vector<Data::Timezone> &list) {
+		const auto was = _hours.current().timezoneId;
+		controller->show(Box(ChooseTimezoneBox, list, was, [=](QString id) {
+			if (id != was) {
+				auto copy = _hours.current();
+				copy.timezoneId = id;
+				_hours = std::move(copy);
+			}
+		}));
+	};
+	AddButtonWithLabel(
+		inner,
+		tr::lng_hours_time_zone(),
+		std::move(timezoneLabel),
+		st::settingsButtonNoIcon
+	)->setClickedCallback([=] {
+		const auto &list = state->timezones.current().list;
+		if (!list.empty()) {
+			editTimezone(list);
+		} else {
+			state->timezoneEditPending = true;
+		}
+	});
+
+	if (state->timezones.current().list.empty()) {
+		state->timezones.value(
+		) | rpl::filter([](const Data::Timezones &value) {
+			return !value.list.empty();
+		}) | rpl::start_with_next([=](const Data::Timezones &value) {
+			if (state->timezoneEditPending) {
+				state->timezoneEditPending = false;
+				editTimezone(value.list);
+			}
+		}, inner->lifetime());
+	}
 
 	wrap->toggleOn(enabled->toggledValue());
 	wrap->finishAnimating();
@@ -93,6 +669,8 @@ void WorkingHours::setupContent(
 }
 
 void WorkingHours::save() {
+	controller()->session().data().businessInfo().saveWorkingHours(
+		_hours.current());
 }
 
 } // namespace
diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style
index ef519dced..3e57d64cc 100644
--- a/Telegram/SourceFiles/settings/settings.style
+++ b/Telegram/SourceFiles/settings/settings.style
@@ -607,3 +607,10 @@ settingsChatbotsBottomTextMargin: margins(22px, 8px, 22px, 3px);
 settingsChatbotsAdd: SettingsButton(settingsButton) {
 	iconLeft: 22px;
 }
+settingsWorkingHoursWeek: SettingsButton(settingsButtonNoIcon) {
+	height: 40px;
+	padding: margins(22px, 4px, 22px, 4px);
+}
+settingsWorkingHoursDetails: settingsNotificationTypeDetails;
+settingsWorkingHoursPicker: 200px;
+settingsWorkingHoursPickerItemHeight: 40px;
diff --git a/Telegram/SourceFiles/settings/settings_business.cpp b/Telegram/SourceFiles/settings/settings_business.cpp
index e72391272..f93646606 100644
--- a/Telegram/SourceFiles/settings/settings_business.cpp
+++ b/Telegram/SourceFiles/settings/settings_business.cpp
@@ -10,6 +10,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "boxes/premium_preview_box.h"
 #include "core/click_handler_types.h"
 #include "data/data_peer_values.h" // AmPremiumValue.
+#include "data/data_session.h"
+#include "data/business/data_business_info.h"
 #include "info/info_wrap_widget.h" // Info::Wrap.
 #include "info/settings/info_settings_widget.h" // SectionCustomTopBarData.
 #include "lang/lang_keys.h"
@@ -356,6 +358,8 @@ void Business::setStepDataReference(std::any &data) {
 void Business::setupContent() {
 	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
 
+	_controller->session().data().businessInfo().preloadTimezones();
+
 	Ui::AddSkip(content, st::settingsFromFileTop);
 
 	AddBusinessSummary(content, _controller, [=](BusinessFeature feature) {

From dd0bdd62fb9208b1a553694f54916ebbcb5db794 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Thu, 22 Feb 2024 22:00:11 +0400
Subject: [PATCH 045/108] Support business working hours API.

---
 .../data/business/data_business_common.cpp    | 26 ++++-----
 .../data/business/data_business_common.h      | 45 ++++++++++++---
 .../data/business/data_business_info.cpp      | 40 ++++++++++---
 .../data/business/data_business_info.h        |  4 --
 Telegram/SourceFiles/data/data_changes.h      | 57 ++++++++++---------
 Telegram/SourceFiles/data/data_location.h     |  2 +-
 Telegram/SourceFiles/data/data_user.cpp       | 51 +++++++++++++++++
 Telegram/SourceFiles/data/data_user.h         |  7 +++
 .../business/settings_working_hours.cpp       | 10 +++-
 9 files changed, 177 insertions(+), 65 deletions(-)

diff --git a/Telegram/SourceFiles/data/business/data_business_common.cpp b/Telegram/SourceFiles/data/business/data_business_common.cpp
index 1de65c952..956807f5d 100644
--- a/Telegram/SourceFiles/data/business/data_business_common.cpp
+++ b/Telegram/SourceFiles/data/business/data_business_common.cpp
@@ -18,7 +18,7 @@ constexpr auto kInNextDayMax = WorkingInterval::kInNextDayMax;
 	auto &list = intervals.list;
 	ranges::sort(list, ranges::less(), &WorkingInterval::start);
 	for (auto i = 0, count = int(list.size()); i != count; ++i) {
-		if (i && list[i].intersected(list[i - 1])) {
+		if (i && list[i] && list[i -1] && list[i].start <= list[i - 1].end) {
 			list[i - 1] = list[i - 1].united(list[i]);
 			list[i] = {};
 		}
@@ -54,12 +54,12 @@ WorkingIntervals WorkingIntervals::normalized() const {
 	return SortAndMerge(MoveTailToFront(SortAndMerge(*this)));
 }
 
-Data::WorkingIntervals ExtractDayIntervals(
-		const Data::WorkingIntervals &intervals,
+WorkingIntervals ExtractDayIntervals(
+		const WorkingIntervals &intervals,
 		int dayIndex) {
 	Expects(dayIndex >= 0 && dayIndex < 7);
 
-	auto result = Data::WorkingIntervals();
+	auto result = WorkingIntervals();
 	auto &list = result.list;
 	for (const auto &interval : intervals.list) {
 		const auto now = interval.intersected(
@@ -80,7 +80,7 @@ Data::WorkingIntervals ExtractDayIntervals(
 	}
 	result = result.normalized();
 
-	const auto outside = [&](Data::WorkingInterval interval) {
+	const auto outside = [&](WorkingInterval interval) {
 		return (interval.end <= 0) || (interval.start >= kDay);
 	};
 	list.erase(ranges::remove_if(list, outside), end(list));
@@ -106,15 +106,15 @@ Data::WorkingIntervals ExtractDayIntervals(
 	return result;
 }
 
-Data::WorkingIntervals RemoveDayIntervals(
-		const Data::WorkingIntervals &intervals,
+WorkingIntervals RemoveDayIntervals(
+		const WorkingIntervals &intervals,
 		int dayIndex) {
 	auto result = intervals.normalized();
 	auto &list = result.list;
-	const auto day = Data::WorkingInterval{ 0, kDay };
+	const auto day = WorkingInterval{ 0, kDay };
 	const auto shifted = day.shifted(dayIndex * kDay);
-	auto before = Data::WorkingInterval{ 0, shifted.start };
-	auto after = Data::WorkingInterval{ shifted.end, kWeek };
+	auto before = WorkingInterval{ 0, shifted.start };
+	auto after = WorkingInterval{ shifted.end, kWeek };
 	for (auto i = 0, count = int(list.size()); i != count; ++i) {
 		if (list[i].end <= shifted.start || list[i].start >= shifted.end) {
 			continue;
@@ -140,10 +140,10 @@ Data::WorkingIntervals RemoveDayIntervals(
 	return result.normalized();
 }
 
-Data::WorkingIntervals ReplaceDayIntervals(
-		const Data::WorkingIntervals &intervals,
+WorkingIntervals ReplaceDayIntervals(
+		const WorkingIntervals &intervals,
 		int dayIndex,
-		Data::WorkingIntervals replacement) {
+		WorkingIntervals replacement) {
 	auto result = RemoveDayIntervals(intervals, dayIndex);
 	const auto first = result.list.insert(
 		end(result.list),
diff --git a/Telegram/SourceFiles/data/business/data_business_common.h b/Telegram/SourceFiles/data/business/data_business_common.h
index 41fcca431..be281d0ad 100644
--- a/Telegram/SourceFiles/data/business/data_business_common.h
+++ b/Telegram/SourceFiles/data/business/data_business_common.h
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #pragma once
 
 #include "base/flags.h"
+#include "data/data_location.h"
 
 class UserData;
 
@@ -125,20 +126,50 @@ struct WorkingHours {
 		return { intervals.normalized(), timezoneId };
 	}
 
+	explicit operator bool() const {
+		return !timezoneId.isEmpty();
+	}
+
 	friend inline bool operator==(
 		const WorkingHours &a,
 		const WorkingHours &b) = default;
 };
 
-[[nodiscard]] Data::WorkingIntervals ExtractDayIntervals(
-	const Data::WorkingIntervals &intervals,
+[[nodiscard]] WorkingIntervals ExtractDayIntervals(
+	const WorkingIntervals &intervals,
 	int dayIndex);
-[[nodiscard]] Data::WorkingIntervals RemoveDayIntervals(
-	const Data::WorkingIntervals &intervals,
+[[nodiscard]] WorkingIntervals RemoveDayIntervals(
+	const WorkingIntervals &intervals,
 	int dayIndex);
-[[nodiscard]] Data::WorkingIntervals ReplaceDayIntervals(
-	const Data::WorkingIntervals &intervals,
+[[nodiscard]] WorkingIntervals ReplaceDayIntervals(
+	const WorkingIntervals &intervals,
 	int dayIndex,
-	Data::WorkingIntervals replacement);
+	WorkingIntervals replacement);
+
+struct BusinessLocation {
+	QString address;
+	LocationPoint point;
+
+	explicit operator bool() const {
+		return !address.isEmpty();
+	}
+
+	friend inline bool operator==(
+		const BusinessLocation &a,
+		const BusinessLocation &b) = default;
+};
+
+struct BusinessDetails {
+	WorkingHours hours;
+	BusinessLocation location;
+
+	explicit operator bool() const {
+		return hours || location;
+	}
+
+	friend inline bool operator==(
+		const BusinessDetails &a,
+		const BusinessDetails &b) = default;
+};
 
 } // namespace Data
diff --git a/Telegram/SourceFiles/data/business/data_business_info.cpp b/Telegram/SourceFiles/data/business/data_business_info.cpp
index c4623bb1f..804b23deb 100644
--- a/Telegram/SourceFiles/data/business/data_business_info.cpp
+++ b/Telegram/SourceFiles/data/business/data_business_info.cpp
@@ -8,10 +8,28 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/business/data_business_info.h"
 
 #include "apiwrap.h"
+#include "data/business/data_business_common.h"
 #include "data/data_session.h"
+#include "data/data_user.h"
 #include "main/main_session.h"
 
 namespace Data {
+namespace {
+
+[[nodiscard]] MTPBusinessWorkHours ToMTP(const WorkingHours &data) {
+	const auto list = data.intervals.normalized().list;
+	const auto proj = [](const WorkingInterval &data) {
+		return MTPBusinessWeeklyOpen(MTP_businessWeeklyOpen(
+			MTP_int(data.start / 60),
+			MTP_int(data.end / 60)));
+	};
+	return MTP_businessWorkHours(
+		MTP_flags(0),
+		MTP_string(data.timezoneId),
+		MTP_vector_from_range(list | ranges::views::transform(proj)));
+}
+
+} // namespace
 
 BusinessInfo::BusinessInfo(not_null<Session*> owner)
 : _owner(owner) {
@@ -19,16 +37,20 @@ BusinessInfo::BusinessInfo(not_null<Session*> owner)
 
 BusinessInfo::~BusinessInfo() = default;
 
-const WorkingHours &BusinessInfo::workingHours() const {
-	return _workingHours.current();
-}
-
-rpl::producer<WorkingHours> BusinessInfo::workingHoursValue() const {
-	return _workingHours.value();
-}
-
 void BusinessInfo::saveWorkingHours(WorkingHours data) {
-	_workingHours = std::move(data);
+	auto details = _owner->session().user()->businessDetails();
+	if (details.hours == data) {
+		return;
+	}
+	details.hours = std::move(data);
+
+	using Flag = MTPaccount_UpdateBusinessWorkHours::Flag;
+	_owner->session().api().request(MTPaccount_UpdateBusinessWorkHours(
+		MTP_flags(details.hours ? Flag::f_business_work_hours : Flag()),
+		ToMTP(details.hours)
+	)).send();
+
+	_owner->session().user()->setBusinessDetails(std::move(details));
 }
 
 void BusinessInfo::preload() {
diff --git a/Telegram/SourceFiles/data/business/data_business_info.h b/Telegram/SourceFiles/data/business/data_business_info.h
index f109165d7..ee4a2e043 100644
--- a/Telegram/SourceFiles/data/business/data_business_info.h
+++ b/Telegram/SourceFiles/data/business/data_business_info.h
@@ -18,8 +18,6 @@ public:
 	explicit BusinessInfo(not_null<Session*> owner);
 	~BusinessInfo();
 
-	[[nodiscard]] const WorkingHours &workingHours() const;
-	[[nodiscard]] rpl::producer<WorkingHours> workingHoursValue() const;
 	void saveWorkingHours(WorkingHours data);
 
 	void preload();
@@ -29,8 +27,6 @@ public:
 private:
 	const not_null<Session*> _owner;
 
-	rpl::variable<WorkingHours> _workingHours;
-
 	rpl::variable<Timezones> _timezones;
 
 	mtpRequestId _timezonesRequestId = 0;
diff --git a/Telegram/SourceFiles/data/data_changes.h b/Telegram/SourceFiles/data/data_changes.h
index 2c29ef8fc..bd85b02fa 100644
--- a/Telegram/SourceFiles/data/data_changes.h
+++ b/Telegram/SourceFiles/data/data_changes.h
@@ -73,42 +73,43 @@ struct PeerUpdate {
 		TranslationDisabled = (1ULL << 13),
 		Color               = (1ULL << 14),
 		BackgroundEmoji     = (1ULL << 15),
+		StoriesState        = (1ULL << 16),
 
 		// For users
-		CanShareContact     = (1ULL << 16),
-		IsContact           = (1ULL << 17),
-		PhoneNumber         = (1ULL << 18),
-		OnlineStatus        = (1ULL << 19),
-		BotCommands         = (1ULL << 20),
-		BotCanBeInvited     = (1ULL << 21),
-		BotStartToken       = (1ULL << 22),
-		CommonChats         = (1ULL << 23),
-		HasCalls            = (1ULL << 24),
-		SupportInfo         = (1ULL << 25),
-		IsBot               = (1ULL << 26),
-		EmojiStatus         = (1ULL << 27),
-		StoriesState        = (1ULL << 28),
+		CanShareContact     = (1ULL << 17),
+		IsContact           = (1ULL << 18),
+		PhoneNumber         = (1ULL << 19),
+		OnlineStatus        = (1ULL << 20),
+		BotCommands         = (1ULL << 21),
+		BotCanBeInvited     = (1ULL << 22),
+		BotStartToken       = (1ULL << 23),
+		CommonChats         = (1ULL << 24),
+		HasCalls            = (1ULL << 25),
+		SupportInfo         = (1ULL << 26),
+		IsBot               = (1ULL << 27),
+		EmojiStatus         = (1ULL << 28),
+		BusinessDetails     = (1ULL << 29),
 
 		// For chats and channels
-		InviteLinks         = (1ULL << 29),
-		Members             = (1ULL << 30),
-		Admins              = (1ULL << 31),
-		BannedUsers         = (1ULL << 32),
-		Rights              = (1ULL << 33),
-		PendingRequests     = (1ULL << 34),
-		Reactions           = (1ULL << 35),
+		InviteLinks         = (1ULL << 30),
+		Members             = (1ULL << 31),
+		Admins              = (1ULL << 32),
+		BannedUsers         = (1ULL << 33),
+		Rights              = (1ULL << 34),
+		PendingRequests     = (1ULL << 35),
+		Reactions           = (1ULL << 36),
 
 		// For channels
-		ChannelAmIn         = (1ULL << 36),
-		StickersSet         = (1ULL << 37),
-		EmojiSet            = (1ULL << 38),
-		ChannelLinkedChat   = (1ULL << 39),
-		ChannelLocation     = (1ULL << 40),
-		Slowmode            = (1ULL << 41),
-		GroupCall           = (1ULL << 42),
+		ChannelAmIn         = (1ULL << 37),
+		StickersSet         = (1ULL << 38),
+		EmojiSet            = (1ULL << 39),
+		ChannelLinkedChat   = (1ULL << 40),
+		ChannelLocation     = (1ULL << 41),
+		Slowmode            = (1ULL << 42),
+		GroupCall           = (1ULL << 43),
 
 		// For iteration
-		LastUsedBit         = (1ULL << 42),
+		LastUsedBit         = (1ULL << 43),
 	};
 	using Flags = base::flags<Flag>;
 	friend inline constexpr auto is_flag_type(Flag) { return true; }
diff --git a/Telegram/SourceFiles/data/data_location.h b/Telegram/SourceFiles/data/data_location.h
index 7d9a59b4a..a5e0090db 100644
--- a/Telegram/SourceFiles/data/data_location.h
+++ b/Telegram/SourceFiles/data/data_location.h
@@ -26,7 +26,6 @@ public:
 
 	[[nodiscard]] size_t hash() const;
 
-private:
 	friend inline bool operator==(
 			const LocationPoint &a,
 			const LocationPoint &b) {
@@ -39,6 +38,7 @@ private:
 		return (a._lat < b._lat) || ((a._lat == b._lat) && (a._lon < b._lon));
 	}
 
+private:
 	float64 _lat = 0;
 	float64 _lon = 0;
 	uint64 _access = 0;
diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp
index 5014bdfb9..45f4f9e28 100644
--- a/Telegram/SourceFiles/data/data_user.cpp
+++ b/Telegram/SourceFiles/data/data_user.cpp
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "storage/localstorage.h"
 #include "storage/storage_user_photos.h"
 #include "main/main_session.h"
+#include "data/business/data_business_common.h"
 #include "data/data_session.h"
 #include "data/data_changes.h"
 #include "data/data_peer_bot_command.h"
@@ -30,6 +31,34 @@ constexpr auto kSetOnlineAfterActivity = TimeId(30);
 
 using UpdateFlag = Data::PeerUpdate::Flag;
 
+[[nodiscard]] Data::BusinessDetails FromMTP(
+		const tl::conditional<MTPBusinessWorkHours> &hours,
+		const tl::conditional<MTPBusinessLocation> &location) {
+	auto result = Data::BusinessDetails();
+	if (hours) {
+		const auto &data = hours->data();
+		result.hours.timezoneId = qs(data.vtimezone_id());
+		result.hours.intervals.list = ranges::views::all(
+			data.vweekly_open().v
+		) | ranges::views::transform([](const MTPBusinessWeeklyOpen &open) {
+			const auto &data = open.data();
+			return Data::WorkingInterval{
+				data.vstart_minute().v * 60,
+				data.vend_minute().v * 60,
+			};
+		}) | ranges::to_vector;
+	}
+	if (location) {
+		const auto &data = location->data();
+		result.location.address = qs(data.vaddress());
+		data.vgeo_point().match([&](const MTPDgeoPoint &data) {
+			result.location.point = Data::LocationPoint(data);
+		}, [&](const MTPDgeoPointEmpty &) {
+		});
+	}
+	return result;
+}
+
 } // namespace
 
 BotInfo::BotInfo() = default;
@@ -62,6 +91,8 @@ UserData::UserData(not_null<Data::Session*> owner, PeerId id)
 , _flags((id == owner->session().userPeerId()) ? Flag::Self : Flag(0)) {
 }
 
+UserData::~UserData() = default;
+
 bool UserData::canShareThisContact() const {
 	return canShareThisContactFast()
 		|| !owner().findContactPhone(peerToUser(id)).isEmpty();
@@ -174,6 +205,22 @@ void UserData::setStoriesState(StoriesState state) {
 	}
 }
 
+const Data::BusinessDetails &UserData::businessDetails() const {
+	static const auto empty = Data::BusinessDetails();
+	return _businessDetails ? *_businessDetails : empty;
+}
+
+void UserData::setBusinessDetails(Data::BusinessDetails details) {
+	if ((!details && !_businessDetails)
+		|| (details && _businessDetails && details == *_businessDetails)) {
+		return;
+	}
+	_businessDetails = details
+		? std::make_unique<Data::BusinessDetails>(std::move(details))
+		: nullptr;
+	session().changes().peerUpdated(this, UpdateFlag::BusinessDetails);
+}
+
 void UserData::setName(const QString &newFirstName, const QString &newLastName, const QString &newPhoneName, const QString &newUsername) {
 	bool changeName = !newFirstName.isEmpty() || !newLastName.isEmpty();
 
@@ -572,6 +619,10 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
 		user->setWallPaper({});
 	}
 
+	user->setBusinessDetails(FromMTP(
+		update.vbusiness_work_hours(),
+		update.vbusiness_location()));
+
 	user->owner().stories().apply(user, update.vstories());
 
 	user->fullUpdated();
diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h
index f6ba39749..cf9eafef7 100644
--- a/Telegram/SourceFiles/data/data_user.h
+++ b/Telegram/SourceFiles/data/data_user.h
@@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 namespace Data {
 struct BotCommand;
+struct BusinessDetails;
 } // namespace Data
 
 struct BotInfo {
@@ -84,6 +85,8 @@ public:
 	using Flags = Data::Flags<UserDataFlags>;
 
 	UserData(not_null<Data::Session*> owner, PeerId id);
+	~UserData();
+
 	void setPhoto(const MTPUserProfilePhoto &photo);
 
 	void setName(
@@ -192,6 +195,9 @@ public:
 	[[nodiscard]] bool hasUnreadStories() const;
 	void setStoriesState(StoriesState state);
 
+	[[nodiscard]] const Data::BusinessDetails &businessDetails() const;
+	void setBusinessDetails(Data::BusinessDetails details);
+
 private:
 	auto unavailableReasons() const
 		-> const std::vector<Data::UnavailableReason> & override;
@@ -201,6 +207,7 @@ private:
 
 	Data::UsernamesInfo _username;
 
+	std::unique_ptr<Data::BusinessDetails> _businessDetails;
 	std::vector<Data::UnavailableReason> _unavailableReasons;
 	QString _phone;
 	QString _privateForwardName;
diff --git a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
index fbe4ccc60..42865a2c0 100644
--- a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "core/application.h"
 #include "data/business/data_business_info.h"
 #include "data/data_session.h"
+#include "data/data_user.h"
 #include "lang/lang_keys.h"
 #include "main/main_session.h"
 #include "settings/business/settings_recipients_helper.h"
@@ -50,6 +51,7 @@ private:
 	void save();
 
 	rpl::variable<Data::WorkingHours> _hours;
+	rpl::variable<bool> _enabled;
 
 };
 
@@ -566,7 +568,7 @@ void WorkingHours::setupContent(
 	const auto state = content->lifetime().make_state<State>(State{
 		.timezones = info->timezonesValue(),
 	});
-	_hours = info->workingHours();
+	_hours = controller->session().user()->businessDetails().hours;
 
 	AddDividerTextWithLottie(content, {
 		.lottie = u"hours"_q,
@@ -582,7 +584,9 @@ void WorkingHours::setupContent(
 		content,
 		tr::lng_hours_show(),
 		st::settingsButtonNoIcon
-	))->toggleOn(rpl::single(false));
+	))->toggleOn(rpl::single(bool(_hours.current())));
+
+	_enabled = enabled->toggledValue();
 
 	const auto wrap = content->add(
 		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
@@ -670,7 +674,7 @@ void WorkingHours::setupContent(
 
 void WorkingHours::save() {
 	controller()->session().data().businessInfo().saveWorkingHours(
-		_hours.current());
+		_enabled.current() ? _hours.current() : Data::WorkingHours());
 }
 
 } // namespace

From c5139069969ae8f14b86a396b34b48924a758467 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 23 Feb 2024 09:17:13 +0400
Subject: [PATCH 046/108] Change default for business recipients.

---
 Telegram/SourceFiles/data/business/data_business_common.h | 2 +-
 .../settings/business/settings_recipients_helper.cpp      | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/Telegram/SourceFiles/data/business/data_business_common.h b/Telegram/SourceFiles/data/business/data_business_common.h
index be281d0ad..c480a8579 100644
--- a/Telegram/SourceFiles/data/business/data_business_common.h
+++ b/Telegram/SourceFiles/data/business/data_business_common.h
@@ -36,7 +36,7 @@ struct BusinessChats {
 struct BusinessRecipients {
 	BusinessChats included;
 	BusinessChats excluded;
-	bool onlyIncluded = false;
+	bool allButExcluded = false;
 
 	friend inline bool operator==(
 		const BusinessRecipients &a,
diff --git a/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp b/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
index a6288dbee..83c11a9cd 100644
--- a/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
@@ -165,7 +165,7 @@ void AddBusinessRecipientsSelector(
 		*data = std::move(now);
 	};
 	const auto group = std::make_shared<Ui::RadiobuttonGroup>(
-		data->current().onlyIncluded ? kSelectedOnly : kAllExcept);
+		data->current().allButExcluded ? kAllExcept : kSelectedOnly);
 	const auto everyone = container->add(
 		object_ptr<Ui::Radiobutton>(
 			container,
@@ -231,7 +231,7 @@ void AddBusinessRecipientsSelector(
 
 	excludeWrap->toggleOn(data->value(
 	) | rpl::map([](const Data::BusinessRecipients &value) {
-		return !value.onlyIncluded;
+		return value.allButExcluded;
 	}));
 	excludeWrap->finishAnimating();
 
@@ -280,13 +280,13 @@ void AddBusinessRecipientsSelector(
 
 	includeWrap->toggleOn(data->value(
 	) | rpl::map([](const Data::BusinessRecipients &value) {
-		return value.onlyIncluded;
+		return !value.allButExcluded;
 	}));
 	includeWrap->finishAnimating();
 
 	group->setChangedCallback([=](int value) {
 		change([&](Data::BusinessRecipients &data) {
-			data.onlyIncluded = (value == kSelectedOnly);
+			data.allButExcluded = (value == kAllExcept);
 		});
 	});
 }

From e05eb63476a6638423609d7f7c9aff8f23dbf3ce Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 23 Feb 2024 13:26:59 +0400
Subject: [PATCH 047/108] Update API scheme on layer 176.

---
 Telegram/SourceFiles/api/api_polls.cpp        |  2 +-
 Telegram/SourceFiles/api/api_sending.cpp      |  4 +-
 Telegram/SourceFiles/apiwrap.cpp              | 12 +++---
 Telegram/SourceFiles/boxes/share_box.cpp      |  2 +-
 .../media/stories/media_stories_share.cpp     |  2 +-
 Telegram/SourceFiles/mtproto/scheme/api.tl    | 42 +++++++++++++------
 .../SourceFiles/window/window_peer_menu.cpp   |  2 +-
 7 files changed, 42 insertions(+), 24 deletions(-)

diff --git a/Telegram/SourceFiles/api/api_polls.cpp b/Telegram/SourceFiles/api/api_polls.cpp
index 58c959da5..f465d7f37 100644
--- a/Telegram/SourceFiles/api/api_polls.cpp
+++ b/Telegram/SourceFiles/api/api_polls.cpp
@@ -85,7 +85,7 @@ void Polls::create(
 			MTPVector<MTPMessageEntity>(),
 			MTP_int(action.options.scheduled),
 			(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
-			MTPstring() // quick_reply_shortcut
+			MTPInputQuickReplyShortcut()
 		), [=](const MTPUpdates &result, const MTP::Response &response) {
 		if (clearCloudDraft) {
 			history->finishSavingCloudDraft(
diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp
index f06bba080..babce9329 100644
--- a/Telegram/SourceFiles/api/api_sending.cpp
+++ b/Telegram/SourceFiles/api/api_sending.cpp
@@ -162,7 +162,7 @@ void SendExistingMedia(
 				sentEntities,
 				MTP_int(message.action.options.scheduled),
 				(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
-				MTPstring() // quick_reply_shortcut
+				MTPInputQuickReplyShortcut()
 			), [=](const MTPUpdates &result, const MTP::Response &response) {
 		}, [=](const MTP::Error &error, const MTP::Response &response) {
 			if (error.code() == 400
@@ -327,7 +327,7 @@ bool SendDice(MessageToSend &message) {
 			MTP_vector<MTPMessageEntity>(),
 			MTP_int(message.action.options.scheduled),
 			(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
-			MTPstring() // quick_reply_shortcut
+			MTPInputQuickReplyShortcut()
 		), [=](const MTPUpdates &result, const MTP::Response &response) {
 	}, [=](const MTP::Error &error, const MTP::Response &response) {
 		api->sendMessageFail(error, peer, randomId, newId);
diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index 97400a701..0d603a044 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -3265,7 +3265,7 @@ void ApiWrap::forwardMessages(
 				MTP_int(topMsgId),
 				MTP_int(action.options.scheduled),
 				(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
-				MTPstring() // quick_reply_shortcut
+				MTPInputQuickReplyShortcut()
 			)).done([=](const MTPUpdates &result) {
 				applyUpdates(result);
 				if (shared && !--shared->requestsLeft) {
@@ -3781,7 +3781,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
 					sentEntities,
 					MTP_int(message.action.options.scheduled),
 					(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
-					MTPstring() // quick_reply_shortcut
+					MTPInputQuickReplyShortcut()
 				), done, fail);
 		} else {
 			histories.sendPreparedMessage(
@@ -3798,7 +3798,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
 					sentEntities,
 					MTP_int(action.options.scheduled),
 					(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
-					MTPstring() // quick_reply_shortcut
+					MTPInputQuickReplyShortcut()
 				), done, fail);
 		}
 		isFirst = false;
@@ -3932,7 +3932,7 @@ void ApiWrap::sendInlineResult(
 			MTP_string(data->getId()),
 			MTP_int(action.options.scheduled),
 			(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
-			MTPstring() // quick_reply_shortcut
+			MTPInputQuickReplyShortcut()
 		), [=](const MTPUpdates &result, const MTP::Response &response) {
 		history->finishSavingCloudDraft(
 			topicRootId,
@@ -4081,7 +4081,7 @@ void ApiWrap::sendMediaWithRandomId(
 			sentEntities,
 			MTP_int(options.scheduled),
 			(options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()),
-			MTPstring() // quick_reply_shortcut
+			MTPInputQuickReplyShortcut()
 		), [=](const MTPUpdates &result, const MTP::Response &response) {
 		if (done) done(true);
 		if (updateRecentStickers) {
@@ -4180,7 +4180,7 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
 			MTP_vector<MTPInputSingleMedia>(medias),
 			MTP_int(album->options.scheduled),
 			(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
-			MTPstring() // quick_reply_shortcut
+			MTPInputQuickReplyShortcut()
 		), [=](const MTPUpdates &result, const MTP::Response &response) {
 		_sendingAlbums.remove(groupId);
 	}, [=](const MTP::Error &error, const MTP::Response &response) {
diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp
index 37da38eee..8577aad62 100644
--- a/Telegram/SourceFiles/boxes/share_box.cpp
+++ b/Telegram/SourceFiles/boxes/share_box.cpp
@@ -1559,7 +1559,7 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
 						MTP_int(topMsgId),
 						MTP_int(options.scheduled),
 						MTP_inputPeerEmpty(), // send_as
-						MTPstring() // quick_reply_shortcut
+						MTPInputQuickReplyShortcut()
 				)).done([=](const MTPUpdates &updates, mtpRequestId reqId) {
 					threadHistory->session().api().applyUpdates(updates);
 					state->requests.remove(reqId);
diff --git a/Telegram/SourceFiles/media/stories/media_stories_share.cpp b/Telegram/SourceFiles/media/stories/media_stories_share.cpp
index aa3ebb075..05fceb77b 100644
--- a/Telegram/SourceFiles/media/stories/media_stories_share.cpp
+++ b/Telegram/SourceFiles/media/stories/media_stories_share.cpp
@@ -155,7 +155,7 @@ namespace Media::Stories {
 					MTPVector<MTPMessageEntity>(),
 					MTP_int(action.options.scheduled),
 					MTP_inputPeerEmpty(),
-					MTPstring() // quick_reply_shortcut
+					MTPInputQuickReplyShortcut()
 				), [=](
 						const MTPUpdates &result,
 						const MTP::Response &response) {
diff --git a/Telegram/SourceFiles/mtproto/scheme/api.tl b/Telegram/SourceFiles/mtproto/scheme/api.tl
index 2c4cca0ba..9fdf2c153 100644
--- a/Telegram/SourceFiles/mtproto/scheme/api.tl
+++ b/Telegram/SourceFiles/mtproto/scheme/api.tl
@@ -227,7 +227,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason;
 inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
 inputReportReasonPersonalDetails#9ec7863d = ReportReason;
 
-userFull#e218d7f0 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation = UserFull;
+userFull#22ff3e85 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage = UserFull;
 
 contact#145ade0b user_id:long mutual:Bool = Contact;
 
@@ -403,8 +403,8 @@ updateSavedDialogPinned#aeaf9e74 flags:# pinned:flags.0?true peer:DialogPeer = U
 updatePinnedSavedDialogs#686c85a6 flags:# order:flags.0?Vector<DialogPeer> = Update;
 updateSavedReactionTags#39c67432 = Update;
 updateSmsJob#f16269d4 job_id:string = Update;
-updateQuickReplies#dc3b36d quick_replies:Vector<messages.QuickReply> = Update;
-updateNewQuickReply#ad62c98d quick_reply:messages.QuickReply = Update;
+updateQuickReplies#f9470ab2 quick_replies:Vector<QuickReply> = Update;
+updateNewQuickReply#f53da717 quick_reply:QuickReply = Update;
 updateDeleteQuickReply#53e6f1ec shortcut_id:int = Update;
 updateQuickReplyMessage#3e050d0f message:Message = Update;
 updateDeleteQuickReplyMessages#566fe7cd shortcut_id:int messages:Vector<int> = Update;
@@ -1669,16 +1669,31 @@ businessWeeklyOpen#120b1ab9 start_minute:int end_minute:int = BusinessWeeklyOpen
 
 businessWorkHours#8c92b098 flags:# open_now:flags.0?true timezone_id:string weekly_open:Vector<BusinessWeeklyOpen> = BusinessWorkHours;
 
-businessLocation#be2bf843 geo_point:GeoPoint address:string = BusinessLocation;
+businessLocation#ac5c1af7 flags:# geo_point:flags.0?GeoPoint address:string = BusinessLocation;
+
+businessAwayMessageScheduleAlways#c9b9e2b9 = BusinessAwayMessageSchedule;
+businessAwayMessageScheduleOutsideWorkHours#c3f2f501 = BusinessAwayMessageSchedule;
+businessAwayMessageScheduleCustom#cc4d9ecc start_date:int end_date:int = BusinessAwayMessageSchedule;
+
+inputBusinessGreetingMessage#7d4a3609 flags:# existing_chats:flags.0?true new_chats:flags.1?true contacts:flags.2?true non_contacts:flags.3?true exclude_selected:flags.5?true shortcut_id:int users:flags.4?Vector<InputUser> no_activity_days:int = InputBusinessGreetingMessage;
+
+businessGreetingMessage#a098d54c flags:# existing_chats:flags.0?true new_chats:flags.1?true contacts:flags.2?true non_contacts:flags.3?true exclude_selected:flags.5?true shortcut_id:int users:flags.4?Vector<long> no_activity_days:int = BusinessGreetingMessage;
+
+inputBusinessAwayMessage#ce6fda48 flags:# existing_chats:flags.0?true new_chats:flags.1?true contacts:flags.2?true non_contacts:flags.3?true exclude_selected:flags.5?true shortcut_id:int schedule:BusinessAwayMessageSchedule users:flags.4?Vector<InputUser> = InputBusinessAwayMessage;
+
+businessAwayMessage#9acd7a15 flags:# existing_chats:flags.0?true new_chats:flags.1?true contacts:flags.2?true non_contacts:flags.3?true exclude_selected:flags.5?true shortcut_id:int schedule:BusinessAwayMessageSchedule users:flags.4?Vector<long> = BusinessAwayMessage;
 
 timezone#ff9289f5 id:string name:string utc_offset:int = Timezone;
 
 help.timezonesListNotModified#970708cc = help.TimezonesList;
 help.timezonesList#7b74ed71 timezones:Vector<Timezone> hash:int = help.TimezonesList;
 
-messages.quickReply#940ebc72 shortcut_id:int shortcut:string top_message:int count:int = messages.QuickReply;
+quickReply#697102b shortcut_id:int shortcut:string top_message:int count:int = QuickReply;
 
-messages.quickReplies#7cd69880 quick_replies:Vector<messages.QuickReply> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.QuickReplies;
+inputQuickReplyShortcut#24596d41 shortcut:string = InputQuickReplyShortcut;
+inputQuickReplyShortcutId#1190cf1 shortcut_id:int = InputQuickReplyShortcut;
+
+messages.quickReplies#c68d6695 quick_replies:Vector<QuickReply> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.QuickReplies;
 messages.quickRepliesNotModified#5f91eb5b = messages.QuickReplies;
 
 ---functions---
@@ -1807,7 +1822,9 @@ account.getDefaultBackgroundEmojis#a60ab9ce hash:long = EmojiList;
 account.getChannelDefaultEmojiStatuses#7727a7d5 hash:long = account.EmojiStatuses;
 account.getChannelRestrictedStatusEmojis#35a9e0d5 hash:long = EmojiList;
 account.updateBusinessWorkHours#4b00e066 flags:# business_work_hours:flags.0?BusinessWorkHours = Bool;
-account.updateBusinessLocation#3dfd3b56 flags:# geo_point:flags.0?InputGeoPoint address:flags.0?string = Bool;
+account.updateBusinessLocation#9e6b131a flags:# geo_point:flags.1?InputGeoPoint address:flags.0?string = Bool;
+account.updateBusinessGreetingMessage#66cdafc4 flags:# message:flags.0?InputBusinessGreetingMessage = Bool;
+account.updateBusinessAwayMessage#a26a7fa5 flags:# message:flags.0?InputBusinessAwayMessage = Bool;
 
 users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
 users.getFullUser#b60f5918 id:InputUser = users.UserFull;
@@ -1849,9 +1866,9 @@ messages.deleteHistory#b08f922a flags:# just_clear:flags.0?true revoke:flags.1?t
 messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
 messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
 messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
-messages.sendMessage#6854c960 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?string = Updates;
-messages.sendMedia#ff5ff75d flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?string = Updates;
-messages.forwardMessages#d5ae95ce flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer top_msg_id:flags.9?int schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?string = Updates;
+messages.sendMessage#dff8042c flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut = Updates;
+messages.sendMedia#7bd66041 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut = Updates;
+messages.forwardMessages#d5039208 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer top_msg_id:flags.9?int schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut = Updates;
 messages.reportSpam#cf1592db peer:InputPeer = Bool;
 messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings;
 messages.report#8953ab4e peer:InputPeer id:Vector<int> reason:ReportReason message:string = Bool;
@@ -1894,7 +1911,7 @@ messages.getSavedGifs#5cf09635 hash:long = messages.SavedGifs;
 messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool;
 messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
 messages.setInlineBotResults#bb12a419 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM switch_webview:flags.4?InlineBotWebView = Bool;
-messages.sendInlineBotResult#e7bda5b7 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to:flags.0?InputReplyTo random_id:long query_id:long id:string schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?string = Updates;
+messages.sendInlineBotResult#3ebee86a flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to:flags.0?InputReplyTo random_id:long query_id:long id:string schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut = Updates;
 messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
 messages.editMessage#dfd14005 flags:# no_webpage:flags.1?true invert_media:flags.16?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.15?int quick_reply_shortcut_id:flags.17?int = Updates;
 messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true invert_media:flags.16?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Bool;
@@ -1929,7 +1946,7 @@ messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool;
 messages.getUnreadMentions#f107e790 flags:# peer:InputPeer top_msg_id:flags.0?int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
 messages.readMentions#36e5bf4d flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory;
 messages.getRecentLocations#702a40e0 peer:InputPeer limit:int hash:long = messages.Messages;
-messages.sendMultiMedia#87262568 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?string = Updates;
+messages.sendMultiMedia#c964709 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut = Updates;
 messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
 messages.searchStickerSets#35705b8a flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets;
 messages.getSplitRanges#1cff7e08 = Vector<MessageRange>;
@@ -2042,6 +2059,7 @@ messages.getQuickReplies#d483f2a8 hash:long = messages.QuickReplies;
 messages.reorderQuickReplies#60331907 order:Vector<int> = Bool;
 messages.checkQuickReplyShortcut#f1d0fbd3 shortcut:string = Bool;
 messages.editQuickReplyShortcut#5c003cef shortcut_id:int shortcut:string = Bool;
+messages.deleteQuickReplyShortcut#3cc04740 shortcut_id:int = Bool;
 messages.getQuickReplyMessages#94a495c3 flags:# shortcut_id:int id:flags.0?Vector<int> hash:long = messages.Messages;
 messages.sendQuickReplyMessages#33153ad4 peer:InputPeer shortcut_id:int = Updates;
 messages.deleteQuickReplyMessages#e105e910 shortcut_id:int id:Vector<int> = Updates;
diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp
index 32f09ee80..44ddebc9d 100644
--- a/Telegram/SourceFiles/window/window_peer_menu.cpp
+++ b/Telegram/SourceFiles/window/window_peer_menu.cpp
@@ -133,7 +133,7 @@ void ShareBotGame(
 			MTPVector<MTPMessageEntity>(),
 			MTP_int(0), // schedule_date
 			MTPInputPeer(), // send_as
-			MTPstring() // quick_reply_shortcut
+			MTPInputQuickReplyShortcut()
 		), [=](const MTPUpdates &, const MTP::Response &) {
 	}, [=](const MTP::Error &error, const MTP::Response &) {
 		history->session().api().sendMessageFail(error, history->peer);

From f85c3c88f72f7835810ed20937495e278f6976cd Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 23 Feb 2024 13:29:17 +0400
Subject: [PATCH 048/108] Add rpl interface to RadiobuttonGroup.

---
 Telegram/SourceFiles/boxes/add_contact_box.cpp     |  6 +++---
 Telegram/SourceFiles/boxes/auto_lock_box.cpp       |  8 +++++---
 Telegram/SourceFiles/boxes/connection_box.cpp      |  8 ++++----
 Telegram/SourceFiles/boxes/download_path_box.cpp   |  8 +++++---
 Telegram/SourceFiles/boxes/edit_privacy_box.cpp    |  2 +-
 Telegram/SourceFiles/boxes/gift_premium_box.cpp    |  4 ++--
 .../boxes/peers/edit_participant_box.cpp           |  2 +-
 .../peers/edit_peer_history_visibility_box.cpp     |  2 +-
 .../SourceFiles/boxes/peers/edit_peer_type_box.cpp | 14 +++++++-------
 Telegram/SourceFiles/boxes/premium_limits_box.cpp  |  2 +-
 Telegram/SourceFiles/boxes/ringtones_box.cpp       |  2 +-
 .../SourceFiles/boxes/self_destruction_box.cpp     |  2 +-
 .../export/view/export_view_settings.cpp           |  2 +-
 .../info/boosts/create_giveaway_box.cpp            | 10 +++++-----
 .../passport/passport_panel_edit_document.cpp      |  2 +-
 .../SourceFiles/settings/settings_business.cpp     |  2 +-
 Telegram/SourceFiles/settings/settings_calls.cpp   |  2 +-
 .../SourceFiles/settings/settings_global_ttl.cpp   |  4 ++--
 Telegram/SourceFiles/settings/settings_premium.cpp |  2 +-
 .../settings/settings_privacy_controllers.cpp      |  2 +-
 .../SourceFiles/ui/effects/premium_graphics.cpp    |  8 ++++----
 .../window/themes/window_themes_cloud_list.cpp     |  2 +-
 22 files changed, 50 insertions(+), 46 deletions(-)

diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp
index 9819c855c..d3715e884 100644
--- a/Telegram/SourceFiles/boxes/add_contact_box.cpp
+++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp
@@ -1018,7 +1018,7 @@ void SetupChannelBox::prepare() {
 		cancel);
 
 	connect(_link, &Ui::MaskedInputField::changed, [=] { handleChange(); });
-	_link->setVisible(_privacyGroup->value() == Privacy::Public);
+	_link->setVisible(_privacyGroup->current() == Privacy::Public);
 
 	_privacyGroup->setChangedCallback([=](Privacy value) {
 		privacyChanged(value);
@@ -1063,7 +1063,7 @@ void SetupChannelBox::updateMaxHeight() {
 			: 0)
 		+ st::newGroupPadding.bottom();
 	if (!_channel->isMegagroup()
-		|| _privacyGroup->value() == Privacy::Public) {
+		|| _privacyGroup->current() == Privacy::Public) {
 		newHeight += st::newGroupLinkPadding.top()
 			+ _link->height()
 			+ st::newGroupLinkPadding.bottom();
@@ -1264,7 +1264,7 @@ void SetupChannelBox::save() {
 	};
 	if (_saveRequestId) {
 		return;
-	} else if (_privacyGroup->value() == Privacy::Private) {
+	} else if (_privacyGroup->current() == Privacy::Private) {
 		closeBox();
 	} else {
 		const auto link = _link->text().trimmed();
diff --git a/Telegram/SourceFiles/boxes/auto_lock_box.cpp b/Telegram/SourceFiles/boxes/auto_lock_box.cpp
index 70c09ec9e..cc7cdb427 100644
--- a/Telegram/SourceFiles/boxes/auto_lock_box.cpp
+++ b/Telegram/SourceFiles/boxes/auto_lock_box.cpp
@@ -81,9 +81,9 @@ void AutoLockBox::prepare() {
 
 		const auto timeInput = Ui::CreateChild<Ui::TimeInput>(
 			this,
-			(group->value() == kCustom)
+			(group->current() == kCustom
 				? TimeString(currentTime)
-				: kDefaultCustom.utf8(),
+				: kDefaultCustom.utf8()),
 			st::autolockTimeField,
 			st::autolockDateField,
 			st::scheduleTimeSeparator,
@@ -115,7 +115,9 @@ void AutoLockBox::prepare() {
 	});
 
 	rpl::merge(
-		boxClosing() | rpl::filter([=] { return group->value() == kCustom; }),
+		boxClosing() | rpl::filter(
+			[=] { return group->current() == kCustom; }
+		),
 		timeInput->submitRequests()
 	) | rpl::start_with_next([=] {
 		if (const auto result = collect()) {
diff --git a/Telegram/SourceFiles/boxes/connection_box.cpp b/Telegram/SourceFiles/boxes/connection_box.cpp
index 4403f56fa..cf9d91f8c 100644
--- a/Telegram/SourceFiles/boxes/connection_box.cpp
+++ b/Telegram/SourceFiles/boxes/connection_box.cpp
@@ -717,7 +717,7 @@ void ProxiesBox::refreshProxyForCalls() {
 		return;
 	}
 	_proxyForCalls->toggle(
-		(_proxySettings->value() == ProxyData::Settings::Enabled
+		(_proxySettings->current() == ProxyData::Settings::Enabled
 			&& _currentProxySupportsCallsId != 0),
 		anim::type::normal);
 }
@@ -864,7 +864,7 @@ void ProxyBox::refreshButtons() {
 	addButton(tr::lng_settings_save(), [=] { save(); });
 	addButton(tr::lng_cancel(), [=] { closeBox(); });
 
-	const auto type = _type->value();
+	const auto type = _type->current();
 	if (type == Type::Socks5 || type == Type::Mtproto) {
 		addLeftButton(tr::lng_proxy_share(), [=] { share(); });
 	}
@@ -885,7 +885,7 @@ void ProxyBox::share() {
 
 ProxyData ProxyBox::collectData() {
 	auto result = ProxyData();
-	result.type = _type->value();
+	result.type = _type->current();
 	result.host = _host->getLastText().trimmed();
 	result.port = _port->getLastText().trimmed().toInt();
 	result.user = (result.type == Type::Mtproto)
@@ -1053,7 +1053,7 @@ void ProxyBox::setupControls(const ProxyData &data) {
 		handleType(type);
 		refreshButtons();
 	});
-	handleType(_type->value());
+	handleType(_type->current());
 }
 
 void ProxyBox::addLabel(
diff --git a/Telegram/SourceFiles/boxes/download_path_box.cpp b/Telegram/SourceFiles/boxes/download_path_box.cpp
index 69179e9f4..f61c0e6e7 100644
--- a/Telegram/SourceFiles/boxes/download_path_box.cpp
+++ b/Telegram/SourceFiles/boxes/download_path_box.cpp
@@ -44,7 +44,9 @@ void DownloadPathBox::prepare() {
 
 	setTitle(tr::lng_download_path_header());
 
-	_group->setChangedCallback([this](Directory value) { radioChanged(value); });
+	_group->setChangedCallback([this](Directory value) {
+		radioChanged(value);
+	});
 
 	_pathLink->addClickHandler([=] { editPath(); });
 	if (!_path.isEmpty() && _path != FileDialog::Tmp()) {
@@ -54,7 +56,7 @@ void DownloadPathBox::prepare() {
 }
 
 void DownloadPathBox::updateControlsVisibility() {
-	auto custom = (_group->value() == Directory::Custom);
+	auto custom = (_group->current() == Directory::Custom);
 	_pathLink->setVisible(custom);
 
 	auto newHeight = st::boxOptionListPadding.top() + (_default ? _default->getMargins().top() + _default->heightNoMargins() : 0) + st::boxOptionListSkip + _temp->heightNoMargins() + st::boxOptionListSkip + _dir->heightNoMargins();
@@ -122,7 +124,7 @@ void DownloadPathBox::editPath() {
 
 void DownloadPathBox::save() {
 #ifndef OS_WIN_STORE
-	auto value = _group->value();
+	auto value = _group->current();
 	auto computePath = [this, value] {
 		if (value == Directory::Custom) {
 			return _path;
diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp
index 7ad5798ec..367742971 100644
--- a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp
+++ b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp
@@ -582,7 +582,7 @@ void EditMessagesPrivacyBox(
 		box->addButton(tr::lng_settings_save(), [=] {
 			if (controller->session().premium()) {
 				privacy->updateNewRequirePremium(
-					group->value() == kOptionPremium);
+					group->current() == kOptionPremium);
 				box->closeBox();
 			} else {
 				showToast();
diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.cpp b/Telegram/SourceFiles/boxes/gift_premium_box.cpp
index 7e0e6b5dc..f544b5647 100644
--- a/Telegram/SourceFiles/boxes/gift_premium_box.cpp
+++ b/Telegram/SourceFiles/boxes/gift_premium_box.cpp
@@ -386,7 +386,7 @@ void GiftBox(
 		state->buttonText.events(),
 		Ui::Premium::GiftGradientStops(),
 		[=] {
-			const auto value = group->value();
+			const auto value = group->current();
 			return (value < options.size() && value >= 0)
 				? options[value].botUrl
 				: QString();
@@ -665,7 +665,7 @@ void GiftsBox(
 		}
 		auto invoice = api->invoice(
 			users.size(),
-			api->monthsFromPreset(group->value()));
+			api->monthsFromPreset(group->current()));
 		invoice.purpose = Payments::InvoicePremiumGiftCodeUsers{ users };
 
 		state->confirmButtonBusy = true;
diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp
index 51d969d0e..ff9398bf0 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp
@@ -840,7 +840,7 @@ void EditRestrictedBox::createUntilGroup() {
 
 void EditRestrictedBox::createUntilVariants() {
 	auto addVariant = [&](int value, const QString &text) {
-		if (!canSave() && _untilGroup->value() != value) {
+		if (!canSave() && _untilGroup->current() != value) {
 			return;
 		}
 		_untilVariants.emplace_back(
diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_history_visibility_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_history_visibility_box.cpp
index 697066416..9086dfce8 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_history_visibility_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_history_visibility_box.cpp
@@ -25,7 +25,7 @@ void EditPeerHistoryVisibilityBox(
 
 	box->setTitle(tr::lng_manage_history_visibility_title());
 	box->addButton(tr::lng_settings_save(), [=] {
-		savedCallback(historyVisibility->value());
+		savedCallback(historyVisibility->current());
 		box->closeBox();
 	});
 	box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp
index 46fb2619c..6cafe4d2c 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp
@@ -76,7 +76,7 @@ public:
 	}
 
 	[[nodiscard]] Privacy getPrivacy() const {
-		return _controls.privacy->value();
+		return _controls.privacy->current();
 	}
 
 	[[nodiscard]] bool noForwards() const {
@@ -238,7 +238,7 @@ void Controller::createContent() {
 				}, wrap->lifetime());
 			} else {
 				_controls.whoSendWrap->toggle(
-					(_controls.privacy->value() == Privacy::HasUsername),
+					(_controls.privacy->current() == Privacy::HasUsername),
 					anim::type::instant);
 			}
 			auto joinToWrite = _controls.joinToWrite
@@ -299,7 +299,7 @@ void Controller::createContent() {
 	if (_linkOnly) {
 		_controls.inviteLinkWrap->show(anim::type::instant);
 	} else {
-		if (_controls.privacy->value() == Privacy::NoUsername) {
+		if (_controls.privacy->current() == Privacy::NoUsername) {
 			checkUsernameAvailability();
 		}
 		const auto forShowing = _dataSavedValue
@@ -474,7 +474,7 @@ object_ptr<Ui::RpWidget> Controller::createUsernameEdit() {
 		&Ui::UsernameInput::changed,
 		[this] { usernameChanged(); });
 
-	const auto shown = (_controls.privacy->value() == Privacy::HasUsername);
+	const auto shown = (_controls.privacy->current() == Privacy::HasUsername);
 	result->toggle(shown, anim::type::instant);
 
 	return result;
@@ -539,7 +539,7 @@ void Controller::checkUsernameAvailability() {
 	if (!_controls.usernameInput) {
 		return;
 	}
-	const auto initial = (_controls.privacy->value() != Privacy::HasUsername);
+	const auto initial = (_controls.privacy->current() != Privacy::HasUsername);
 	const auto checking = initial
 		? u".bad."_q
 		: getUsernameInput();
@@ -573,11 +573,11 @@ void Controller::checkUsernameAvailability() {
 			_controls.privacy->setValue(Privacy::NoUsername);
 		} else if (type == u"CHANNELS_ADMIN_PUBLIC_TOO_MUCH"_q) {
 			_usernameState = UsernameState::TooMany;
-			if (_controls.privacy->value() == Privacy::HasUsername) {
+			if (_controls.privacy->current() == Privacy::HasUsername) {
 				askUsernameRevoke();
 			}
 		} else if (initial) {
-			if (_controls.privacy->value() == Privacy::HasUsername) {
+			if (_controls.privacy->current() == Privacy::HasUsername) {
 				showUsernameEmpty();
 				setFocusUsername();
 			}
diff --git a/Telegram/SourceFiles/boxes/premium_limits_box.cpp b/Telegram/SourceFiles/boxes/premium_limits_box.cpp
index ad8d313df..2a9c0d4b1 100644
--- a/Telegram/SourceFiles/boxes/premium_limits_box.cpp
+++ b/Telegram/SourceFiles/boxes/premium_limits_box.cpp
@@ -1142,7 +1142,7 @@ void AccountsLimitBox(
 		const auto ref = QString();
 
 		const auto wasAccount = &session->account();
-		const auto nowAccount = accounts[group->value()];
+		const auto nowAccount = accounts[group->current()];
 		if (wasAccount == nowAccount) {
 			Settings::ShowPremium(session, ref);
 			return;
diff --git a/Telegram/SourceFiles/boxes/ringtones_box.cpp b/Telegram/SourceFiles/boxes/ringtones_box.cpp
index 74e8f510b..d8097222e 100644
--- a/Telegram/SourceFiles/boxes/ringtones_box.cpp
+++ b/Telegram/SourceFiles/boxes/ringtones_box.cpp
@@ -327,7 +327,7 @@ void RingtonesBox(
 
 	box->setWidth(st::boxWideWidth);
 	box->addButton(tr::lng_settings_save(), [=] {
-		const auto value = state->group->value();
+		const auto value = state->group->current();
 		auto sound = (value == kDefaultValue)
 			? Data::NotifySound()
 			: (value == kNoSoundValue)
diff --git a/Telegram/SourceFiles/boxes/self_destruction_box.cpp b/Telegram/SourceFiles/boxes/self_destruction_box.cpp
index 03d8a6155..0ab869ae5 100644
--- a/Telegram/SourceFiles/boxes/self_destruction_box.cpp
+++ b/Telegram/SourceFiles/boxes/self_destruction_box.cpp
@@ -95,7 +95,7 @@ void SelfDestructionBox::showContent() {
 
 	clearButtons();
 	addButton(tr::lng_settings_save(), [=] {
-		const auto value = _ttlGroup->value();
+		const auto value = _ttlGroup->current();
 		switch (_type) {
 		case Type::Account:
 			_session->api().selfDestruct().updateAccountTTL(value);
diff --git a/Telegram/SourceFiles/export/view/export_view_settings.cpp b/Telegram/SourceFiles/export/view/export_view_settings.cpp
index bbc91c2dc..8f546260a 100644
--- a/Telegram/SourceFiles/export/view/export_view_settings.cpp
+++ b/Telegram/SourceFiles/export/view/export_view_settings.cpp
@@ -78,7 +78,7 @@ void ChooseFormatBox(
 	addFormatOption(
 		tr::lng_export_option_html_and_json(tr::now),
 		Format::HtmlAndJson);
-	box->addButton(tr::lng_settings_save(), [=] { done(group->value()); });
+	box->addButton(tr::lng_settings_save(), [=] { done(group->current()); });
 	box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
 }
 
diff --git a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp
index 6c5583723..01462aa3b 100644
--- a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp
+++ b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp
@@ -630,9 +630,9 @@ void CreateGiveawayBox(
 
 		const auto createCallback = [=](GiveawayType type) {
 			return [=] {
-				const auto was = membersGroup->value();
+				const auto was = membersGroup->current();
 				membersGroup->setValue(type);
-				const auto now = membersGroup->value();
+				const auto now = membersGroup->current();
 				if (was == now) {
 					base::call_delayed(
 						st::defaultRippleAnimation.hideDuration,
@@ -990,7 +990,7 @@ void CreateGiveawayBox(
 			if (state->confirmButtonBusy.current()) {
 				return;
 			}
-			const auto type = typeGroup->value();
+			const auto type = typeGroup->current();
 			const auto isSpecific = (type == GiveawayType::SpecificUsers);
 			const auto isRandom = (type == GiveawayType::Random);
 			if (!isSpecific && !isRandom) {
@@ -1003,7 +1003,7 @@ void CreateGiveawayBox(
 				prepaid
 					? prepaid->months
 					: state->apiOptions.monthsFromPreset(
-						durationGroup->value()));
+						durationGroup->current()));
 			if (isSpecific) {
 				if (state->selectedToAward.empty()) {
 					return;
@@ -1029,7 +1029,7 @@ void CreateGiveawayBox(
 					.countries = state->countriesValue.current(),
 					.additionalPrize = state->additionalPrize.current(),
 					.untilDate = state->dateValue.current(),
-					.onlyNewSubscribers = (membersGroup->value()
+					.onlyNewSubscribers = (membersGroup->current()
 						== GiveawayType::OnlyNewMembers),
 					.showWinners = state->showWinners.current(),
 				};
diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp b/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp
index c3715e977..bac92c2c8 100644
--- a/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp
+++ b/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp
@@ -140,7 +140,7 @@ void RequestTypeBox::setupControls(
 	_height = y;
 
 	_submit = [=] {
-		const auto value = group->hasValue() ? group->value() : -1;
+		const auto value = group->hasValue() ? group->current() : -1;
 		if (value >= 0) {
 			submit(value);
 		}
diff --git a/Telegram/SourceFiles/settings/settings_business.cpp b/Telegram/SourceFiles/settings/settings_business.cpp
index f93646606..aae5868df 100644
--- a/Telegram/SourceFiles/settings/settings_business.cpp
+++ b/Telegram/SourceFiles/settings/settings_business.cpp
@@ -503,7 +503,7 @@ QPointer<Ui::RpWidget> Business::createPinnedToBottom(
 		std::move(buttonText),
 		std::nullopt,
 		[=, options = session->api().premium().subscriptionOptions()] {
-			const auto value = _radioGroup->value();
+			const auto value = _radioGroup->current();
 			return (value < options.size() && value >= 0)
 				? options[value].botUrl
 				: QString();
diff --git a/Telegram/SourceFiles/settings/settings_calls.cpp b/Telegram/SourceFiles/settings/settings_calls.cpp
index 0b6a70674..de7c34e3e 100644
--- a/Telegram/SourceFiles/settings/settings_calls.cpp
+++ b/Telegram/SourceFiles/settings/settings_calls.cpp
@@ -607,7 +607,7 @@ void ChooseMediaDeviceBox(
 			button->finishAnimating();
 			button->clicks(
 			) | rpl::filter([=] {
-				return (group->value() == index);
+				return (group->current() == index);
 			}) | rpl::start_with_next([=] {
 				choose(id);
 			}, button->lifetime());
diff --git a/Telegram/SourceFiles/settings/settings_global_ttl.cpp b/Telegram/SourceFiles/settings/settings_global_ttl.cpp
index 8e2024622..99dea5b2d 100644
--- a/Telegram/SourceFiles/settings/settings_global_ttl.cpp
+++ b/Telegram/SourceFiles/settings/settings_global_ttl.cpp
@@ -294,7 +294,7 @@ void GlobalTTL::rebuildButtons(TimeId currentTTL) const {
 					rpl::single(ttlText)),
 			st::settingsButtonNoIcon));
 		button->setClickedCallback([=] {
-			if (_group->value() == ttl) {
+			if (_group->current() == ttl) {
 				return;
 			}
 			if (!ttl) {
@@ -357,7 +357,7 @@ void GlobalTTL::setupContent() {
 
 		show->showBox(Box(TTLMenu::TTLBox, TTLMenu::Args{
 			.show = show,
-			.startTtl = _group->value(),
+			.startTtl = _group->current(),
 			.callback = [=](TimeId ttl, Fn<void()>) { showSure(ttl, true); },
 			.hideDisable = true,
 		}));
diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp
index f9d627d3b..e56122c94 100644
--- a/Telegram/SourceFiles/settings/settings_premium.cpp
+++ b/Telegram/SourceFiles/settings/settings_premium.cpp
@@ -1190,7 +1190,7 @@ QPointer<Ui::RpWidget> Premium::createPinnedToBottom(
 		std::move(buttonText),
 		std::nullopt,
 		[=, options = session->api().premium().subscriptionOptions()] {
-			const auto value = _radioGroup->value();
+			const auto value = _radioGroup->current();
 			return (value < options.size() && value >= 0)
 				? options[value].botUrl
 				: QString();
diff --git a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp
index 5828b2e0e..5f7cb56b1 100644
--- a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp
+++ b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp
@@ -598,7 +598,7 @@ object_ptr<Ui::RpWidget> PhoneNumberPrivacyController::setupMiddleWidget(
 	_saveAdditional = [=] {
 		controller->session().api().userPrivacy().save(
 			Api::UserPrivacy::Key::AddedByPhone,
-			Api::UserPrivacy::Rule{ .option = group->value() });
+			Api::UserPrivacy::Rule{ .option = group->current() });
 	};
 
 	return widget;
diff --git a/Telegram/SourceFiles/ui/effects/premium_graphics.cpp b/Telegram/SourceFiles/ui/effects/premium_graphics.cpp
index e46760ee1..3d3cb00ef 100644
--- a/Telegram/SourceFiles/ui/effects/premium_graphics.cpp
+++ b/Telegram/SourceFiles/ui/effects/premium_graphics.cpp
@@ -1063,7 +1063,7 @@ void AddAccountsRow(
 		});
 		const auto index = int(state->accounts.size()) - 1;
 		state->accounts[index].checkbox.setChecked(
-			index == group->value(),
+			index == group->current(),
 			anim::type::instant);
 
 		widget->paintRequest(
@@ -1303,7 +1303,7 @@ void AddGiftOptions(
 		int nowIndex = 0;
 		Ui::Animations::Simple animation;
 	};
-	const auto wasGroupValue = group->value();
+	const auto wasGroupValue = group->current();
 	const auto animation = parent->lifetime().make_state<Animation>();
 	animation->nowIndex = wasGroupValue;
 
@@ -1324,7 +1324,7 @@ void AddGiftOptions(
 		const auto &stCheckbox = st::defaultBoxCheckbox;
 		auto radioView = std::make_unique<GradientRadioView>(
 			st::defaultRadio,
-			(group->hasValue() && group->value() == index));
+			(group->hasValue() && group->current() == index));
 		const auto radioViewRaw = radioView.get();
 		const auto radio = Ui::CreateChild<Ui::Radiobutton>(
 			row,
@@ -1468,7 +1468,7 @@ void AddGiftOptions(
 
 		row->setClickedCallback([=, duration = st::defaultCheck.duration] {
 			group->setValue(index);
-			animation->nowIndex = group->value();
+			animation->nowIndex = group->current();
 			animation->animation.stop();
 			animation->animation.start(
 				[=] { parent->update(); },
diff --git a/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp b/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp
index 1e71863af..4001edae4 100644
--- a/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp
+++ b/Telegram/SourceFiles/window/themes/window_themes_cloud_list.cpp
@@ -498,7 +498,7 @@ bool CloudList::insertTillLimit(
 void CloudList::insert(int index, const Data::CloudTheme &theme) {
 	const auto id = theme.id;
 	const auto value = groupValueForId(id);
-	const auto checked = _group->hasValue() && (_group->value() == value);
+	const auto checked = _group->hasValue() && (_group->current() == value);
 	auto check = std::make_unique<CloudListCheck>(checked);
 	const auto raw = check.get();
 	auto button = std::make_unique<Ui::Radiobutton>(

From e6b9ac22675df39bf2e0139986ca5c9b6f5d23c3 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 23 Feb 2024 13:29:29 +0400
Subject: [PATCH 049/108] Support edit / save of away message settings.

---
 .../data/business/data_business_common.h      |  30 +++
 .../data/business/data_business_info.cpp      |  85 +++++++-
 .../data/business/data_business_info.h        |  11 +-
 Telegram/SourceFiles/data/data_user.cpp       |  65 +++++-
 .../business/settings_away_message.cpp        | 198 +++++++++++++++++-
 Telegram/SourceFiles/settings/settings.style  |   2 +
 6 files changed, 378 insertions(+), 13 deletions(-)

diff --git a/Telegram/SourceFiles/data/business/data_business_common.h b/Telegram/SourceFiles/data/business/data_business_common.h
index c480a8579..a74b5df48 100644
--- a/Telegram/SourceFiles/data/business/data_business_common.h
+++ b/Telegram/SourceFiles/data/business/data_business_common.h
@@ -172,4 +172,34 @@ struct BusinessDetails {
 		const BusinessDetails &b) = default;
 };
 
+enum class AwayScheduleType : uchar {
+	Never = 0,
+	Always = 1,
+	OutsideWorkingHours = 2,
+	Custom = 3,
+};
+
+struct AwaySchedule {
+	AwayScheduleType type = AwayScheduleType::Always;
+	WorkingInterval customInterval;
+
+	friend inline bool operator==(
+		const AwaySchedule &a,
+		const AwaySchedule &b) = default;
+};
+
+struct AwaySettings {
+	BusinessRecipients recipients;
+	AwaySchedule schedule;
+	int shortcutId = 0;
+
+	explicit operator bool() const {
+		return schedule.type != AwayScheduleType::Never;
+	}
+
+	friend inline bool operator==(
+		const AwaySettings &a,
+		const AwaySettings &b) = default;
+};
+
 } // namespace Data
diff --git a/Telegram/SourceFiles/data/business/data_business_info.cpp b/Telegram/SourceFiles/data/business/data_business_info.cpp
index 804b23deb..d054d3a56 100644
--- a/Telegram/SourceFiles/data/business/data_business_info.cpp
+++ b/Telegram/SourceFiles/data/business/data_business_info.cpp
@@ -29,6 +29,51 @@ namespace {
 		MTP_vector_from_range(list | ranges::views::transform(proj)));
 }
 
+template <typename Flag>
+[[nodiscard]] auto RecipientsFlags(
+		const BusinessRecipients &data,
+		Flag) {
+	using Type = BusinessChatType;
+	const auto &chats = data.allButExcluded
+		? data.excluded
+		: data.included;
+	return Flag()
+		| ((chats.types & Type::NewChats) ? Flag::f_new_chats : Flag())
+		| ((chats.types & Type::ExistingChats)
+			? Flag::f_existing_chats
+			: Flag())
+		| ((chats.types & Type::Contacts) ? Flag::f_contacts : Flag())
+		| ((chats.types & Type::NonContacts) ? Flag::f_non_contacts : Flag())
+		| (chats.list.empty() ? Flag() : Flag::f_users)
+		| (data.allButExcluded ? Flag::f_exclude_selected : Flag());
+}
+
+[[nodiscard]] MTPBusinessAwayMessageSchedule ToMTP(
+		const AwaySchedule &data) {
+	Expects(data.type != AwayScheduleType::Never);
+
+	return (data.type == AwayScheduleType::Always)
+		? MTP_businessAwayMessageScheduleAlways()
+		: (data.type == AwayScheduleType::OutsideWorkingHours)
+		? MTP_businessAwayMessageScheduleOutsideWorkHours()
+		: MTP_businessAwayMessageScheduleCustom(
+			MTP_int(data.customInterval.start),
+			MTP_int(data.customInterval.end));
+}
+
+[[nodiscard]] MTPInputBusinessAwayMessage ToMTP(const AwaySettings &data) {
+	using Flag = MTPDinputBusinessAwayMessage::Flag;
+	return MTP_inputBusinessAwayMessage(
+		MTP_flags(RecipientsFlags(data.recipients, Flag())),
+		MTP_int(data.shortcutId),
+		ToMTP(data.schedule),
+		MTP_vector_from_range(
+			(data.recipients.allButExcluded
+				? data.recipients.excluded
+				: data.recipients.included).list
+			| ranges::views::transform(&UserData::inputUser)));
+}
+
 } // namespace
 
 BusinessInfo::BusinessInfo(not_null<Session*> owner)
@@ -42,17 +87,51 @@ void BusinessInfo::saveWorkingHours(WorkingHours data) {
 	if (details.hours == data) {
 		return;
 	}
-	details.hours = std::move(data);
 
 	using Flag = MTPaccount_UpdateBusinessWorkHours::Flag;
 	_owner->session().api().request(MTPaccount_UpdateBusinessWorkHours(
-		MTP_flags(details.hours ? Flag::f_business_work_hours : Flag()),
-		ToMTP(details.hours)
+		MTP_flags(data ? Flag::f_business_work_hours : Flag()),
+		ToMTP(data)
 	)).send();
 
+	details.hours = std::move(data);
 	_owner->session().user()->setBusinessDetails(std::move(details));
 }
 
+void BusinessInfo::applyAwaySettings(AwaySettings data) {
+	if (_awaySettings == data) {
+		return;
+	}
+	_awaySettings = data;
+	_awaySettingsChanged.fire({});
+}
+
+void BusinessInfo::saveAwaySettings(AwaySettings data) {
+	if (_awaySettings == data) {
+		return;
+	}
+	using Flag = MTPaccount_UpdateBusinessAwayMessage::Flag;
+	_owner->session().api().request(MTPaccount_UpdateBusinessAwayMessage(
+		MTP_flags(data ? Flag::f_message : Flag()),
+		data ? ToMTP(data) : MTPInputBusinessAwayMessage()
+	)).send();
+
+	_awaySettings = std::move(data);
+	_awaySettingsChanged.fire({});
+}
+
+bool BusinessInfo::awaySettingsLoaded() const {
+	return _awaySettings.has_value();
+}
+
+AwaySettings BusinessInfo::awaySettings() const {
+	return _awaySettings.value_or(AwaySettings());
+}
+
+rpl::producer<> BusinessInfo::awaySettingsChanged() const {
+	return _awaySettingsChanged.events();
+}
+
 void BusinessInfo::preload() {
 	preloadTimezones();
 }
diff --git a/Telegram/SourceFiles/data/business/data_business_info.h b/Telegram/SourceFiles/data/business/data_business_info.h
index ee4a2e043..9cd7f9681 100644
--- a/Telegram/SourceFiles/data/business/data_business_info.h
+++ b/Telegram/SourceFiles/data/business/data_business_info.h
@@ -18,9 +18,16 @@ public:
 	explicit BusinessInfo(not_null<Session*> owner);
 	~BusinessInfo();
 
+	void preload();
+
 	void saveWorkingHours(WorkingHours data);
 
-	void preload();
+	void saveAwaySettings(AwaySettings data);
+	void applyAwaySettings(AwaySettings data);
+	[[nodiscard]] AwaySettings awaySettings() const;
+	[[nodiscard]] bool awaySettingsLoaded() const;
+	[[nodiscard]] rpl::producer<> awaySettingsChanged() const;
+
 	void preloadTimezones();
 	[[nodiscard]] rpl::producer<Timezones> timezonesValue() const;
 
@@ -28,6 +35,8 @@ private:
 	const not_null<Session*> _owner;
 
 	rpl::variable<Timezones> _timezones;
+	std::optional<AwaySettings> _awaySettings;
+	rpl::event_stream<> _awaySettingsChanged;
 
 	mtpRequestId _timezonesRequestId = 0;
 	int32 _timezonesHash = 0;
diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp
index 45f4f9e28..93346ec90 100644
--- a/Telegram/SourceFiles/data/data_user.cpp
+++ b/Telegram/SourceFiles/data/data_user.cpp
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "storage/storage_user_photos.h"
 #include "main/main_session.h"
 #include "data/business/data_business_common.h"
+#include "data/business/data_business_info.h"
 #include "data/data_session.h"
 #include "data/data_changes.h"
 #include "data/data_peer_bot_command.h"
@@ -51,14 +52,66 @@ using UpdateFlag = Data::PeerUpdate::Flag;
 	if (location) {
 		const auto &data = location->data();
 		result.location.address = qs(data.vaddress());
-		data.vgeo_point().match([&](const MTPDgeoPoint &data) {
-			result.location.point = Data::LocationPoint(data);
-		}, [&](const MTPDgeoPointEmpty &) {
-		});
+		if (const auto point = data.vgeo_point()) {
+			point->match([&](const MTPDgeoPoint &data) {
+				result.location.point = Data::LocationPoint(data);
+			}, [&](const MTPDgeoPointEmpty &) {
+			});
+		}
 	}
 	return result;
 }
 
+template <typename T>
+Data::BusinessRecipients RecipientsFromMTP(
+		not_null<Data::Session*> owner,
+		const T &data) {
+	using Type = Data::BusinessChatType;
+	auto result = Data::BusinessRecipients{
+		.allButExcluded = data.is_exclude_selected(),
+	};
+	auto &chats = result.allButExcluded
+		? result.excluded
+		: result.included;
+	chats.types = Type()
+		| (data.is_new_chats() ? Type::NewChats : Type())
+		| (data.is_existing_chats() ? Type::ExistingChats : Type())
+		| (data.is_contacts() ? Type::Contacts : Type())
+		| (data.is_non_contacts() ? Type::NonContacts : Type());
+	if (const auto users = data.vusers()) {
+		for (const auto &userId : users->v) {
+			chats.list.push_back(owner->user(UserId(userId.v)));
+		}
+	}
+	return result;
+}
+
+[[nodiscard]] Data::AwaySettings FromMTP(
+		not_null<Data::Session*> owner,
+		const tl::conditional<MTPBusinessAwayMessage> &message) {
+	if (!message) {
+		return Data::AwaySettings();
+	}
+	const auto &data = message->data();
+	auto result = Data::AwaySettings{
+		.recipients = RecipientsFromMTP(owner, data),
+		.shortcutId = data.vshortcut_id().v,
+	};
+	data.vschedule().match([&](
+			const MTPDbusinessAwayMessageScheduleAlways &) {
+		result.schedule.type = Data::AwayScheduleType::Always;
+	}, [&](const MTPDbusinessAwayMessageScheduleOutsideWorkHours &) {
+		result.schedule.type = Data::AwayScheduleType::OutsideWorkingHours;
+	}, [&](const MTPDbusinessAwayMessageScheduleCustom &data) {
+		result.schedule.type = Data::AwayScheduleType::Custom;
+		result.schedule.customInterval = Data::WorkingInterval{
+			data.vstart_date().v,
+			data.vend_date().v,
+		};
+	});
+	return result;
+}
+
 } // namespace
 
 BotInfo::BotInfo() = default;
@@ -622,6 +675,10 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
 	user->setBusinessDetails(FromMTP(
 		update.vbusiness_work_hours(),
 		update.vbusiness_location()));
+	if (user->isSelf()) {
+		user->owner().businessInfo().applyAwaySettings(
+			FromMTP(&user->owner(), update.vbusiness_away_message()));
+	}
 
 	user->owner().stories().apply(user, update.vstories());
 
diff --git a/Telegram/SourceFiles/settings/business/settings_away_message.cpp b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
index 0e22a4c2f..b38e4e184 100644
--- a/Telegram/SourceFiles/settings/business/settings_away_message.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
@@ -7,17 +7,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "settings/business/settings_away_message.h"
 
+#include "base/unixtime.h"
 #include "core/application.h"
+#include "data/business/data_business_info.h"
 #include "data/data_session.h"
 #include "lang/lang_keys.h"
 #include "main/main_session.h"
 #include "settings/business/settings_recipients_helper.h"
+#include "ui/boxes/choose_date_time.h"
 #include "ui/text/text_utilities.h"
 #include "ui/widgets/buttons.h"
+#include "ui/widgets/checkbox.h"
 #include "ui/wrap/slide_wrap.h"
 #include "ui/wrap/vertical_layout.h"
 #include "ui/vertical_list.h"
 #include "window/window_session_controller.h"
+#include "styles/style_layers.h"
 #include "styles/style_settings.h"
 
 namespace Settings {
@@ -37,9 +42,144 @@ private:
 	void save();
 
 	rpl::variable<Data::BusinessRecipients> _recipients;
+	rpl::variable<Data::AwaySchedule> _schedule;
+	rpl::variable<bool> _enabled;
 
 };
 
+[[nodiscard]] TimeId StartTimeMin() {
+	// Telegram was launched in August 2013 :)
+	return base::unixtime::serialize(QDateTime(QDate(2013, 8, 1)));
+}
+
+[[nodiscard]] TimeId EndTimeMin() {
+	return StartTimeMin() + 3600;
+}
+
+[[nodiscard]] bool BadCustomInterval(const Data::WorkingInterval &interval) {
+	return !interval
+		|| (interval.start < StartTimeMin())
+		|| (interval.end < EndTimeMin());
+}
+
+struct AwayScheduleSelectorDescriptor {
+	not_null<Window::SessionController*> controller;
+	not_null<rpl::variable<Data::AwaySchedule>*> data;
+};
+void AddAwayScheduleSelector(
+		not_null<Ui::VerticalLayout*> container,
+		AwayScheduleSelectorDescriptor &&descriptor) {
+	using Type = Data::AwayScheduleType;
+	using namespace rpl::mappers;
+
+	const auto controller = descriptor.controller;
+	const auto data = descriptor.data;
+
+	Ui::AddSubsectionTitle(container, tr::lng_away_schedule());
+	const auto group = std::make_shared<Ui::RadioenumGroup<Type>>(
+		data->current().type);
+
+	const auto add = [&](Type type, const QString &label) {
+		container->add(
+			object_ptr<Ui::Radioenum<Type>>(
+				container,
+				group,
+				type,
+				label),
+			st::boxRowPadding + st::settingsAwaySchedulePadding);
+	};
+	add(Type::Always, tr::lng_away_schedule_always(tr::now));
+	add(Type::OutsideWorkingHours, tr::lng_away_schedule_outside(tr::now));
+	add(Type::Custom, tr::lng_away_schedule_custom(tr::now));
+
+	const auto customWrap = container->add(
+		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
+			container,
+			object_ptr<Ui::VerticalLayout>(container)));
+	const auto customInner = customWrap->entity();
+	customWrap->toggleOn(group->value() | rpl::map(_1 == Type::Custom));
+
+	group->changes() | rpl::start_with_next([=](Type value) {
+		auto copy = data->current();
+		copy.type = value;
+		*data = copy;
+	}, customWrap->lifetime());
+
+	const auto chooseDate = [=](
+			rpl::producer<QString> title,
+			TimeId now,
+			Fn<TimeId()> min,
+			Fn<TimeId()> max,
+			Fn<void(TimeId)> done) {
+		using namespace Ui;
+		const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
+		const auto save = [=](TimeId time) {
+			done(time);
+			if (const auto strong = box->data()) {
+				strong->closeBox();
+			}
+		};
+		*box = controller->show(Box(ChooseDateTimeBox, ChooseDateTimeBoxArgs{
+			.title = std::move(title),
+			.submit = tr::lng_settings_save(),
+			.done = save,
+			.min = min,
+			.time = now,
+			.max = max,
+		}));
+	};
+
+	Ui::AddSkip(customInner);
+	Ui::AddDivider(customInner);
+	Ui::AddSkip(customInner);
+
+	auto startLabel = data->value(
+	) | rpl::map([=](const Data::AwaySchedule &value) {
+		return langDateTime(
+			base::unixtime::parse(value.customInterval.start));
+	});
+	AddButtonWithLabel(
+		customInner,
+		tr::lng_away_custom_start(),
+		std::move(startLabel),
+		st::settingsButtonNoIcon
+	)->setClickedCallback([=] {
+		chooseDate(
+			tr::lng_away_custom_start(),
+			data->current().customInterval.start,
+			StartTimeMin,
+			[=] { return data->current().customInterval.end - 1; },
+			[=](TimeId time) {
+				auto copy = data->current();
+				copy.customInterval.start = time;
+				*data = copy;
+			});
+	});
+
+	auto endLabel = data->value(
+	) | rpl::map([=](const Data::AwaySchedule &value) {
+		return langDateTime(
+			base::unixtime::parse(value.customInterval.end));
+	});
+	AddButtonWithLabel(
+		customInner,
+		tr::lng_away_custom_end(),
+		std::move(endLabel),
+		st::settingsButtonNoIcon
+	)->setClickedCallback([=] {
+		chooseDate(
+			tr::lng_away_custom_end(),
+			data->current().customInterval.end,
+			[=] { return data->current().customInterval.start + 1; },
+			nullptr,
+			[=](TimeId time) {
+				auto copy = data->current();
+				copy.customInterval.end = time;
+				*data = copy;
+			});
+	});
+}
+
 AwayMessage::AwayMessage(
 	QWidget *parent,
 	not_null<Window::SessionController*> controller)
@@ -59,12 +199,27 @@ rpl::producer<QString> AwayMessage::title() {
 
 void AwayMessage::setupContent(
 		not_null<Window::SessionController*> controller) {
+	using namespace Data;
 	using namespace rpl::mappers;
 
 	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
-	//const auto current = controller->session().data().chatbots().current();
+	const auto info = &controller->session().data().businessInfo();
+	const auto current = info->awaySettings();
+	const auto disabled = (current.schedule.type == AwayScheduleType::Never);
+
+	_recipients = current.recipients;
+	auto initialSchedule = disabled ? AwaySchedule{
+		.type = AwayScheduleType::Always,
+	} : current.schedule;
+	if (BadCustomInterval(initialSchedule.customInterval)) {
+		const auto now = base::unixtime::now();
+		initialSchedule.customInterval = WorkingInterval{
+			.start = now,
+			.end = now + 24 * 60 * 60,
+		};
+	}
+	_schedule = initialSchedule;
 
-	//_recipients = current.recipients;
 	AddDividerTextWithLottie(content, {
 		.lottie = u"sleep"_q,
 		.lottieSize = st::settingsCloudPasswordIconSize,
@@ -79,7 +234,8 @@ void AwayMessage::setupContent(
 		content,
 		tr::lng_away_enable(),
 		st::settingsButtonNoIcon
-	))->toggleOn(rpl::single(false));
+	))->toggleOn(rpl::single(!disabled));
+	_enabled = enabled->toggledValue();
 
 	const auto wrap = content->add(
 		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
@@ -90,8 +246,32 @@ void AwayMessage::setupContent(
 	Ui::AddSkip(inner);
 	Ui::AddDivider(inner);
 
-	wrap->toggleOn(enabled->toggledValue());
-	wrap->finishAnimating();
+	const auto createWrap = inner->add(
+		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
+			inner,
+			object_ptr<Ui::VerticalLayout>(inner)));
+	const auto createInner = createWrap->entity();
+	Ui::AddSkip(createInner);
+	const auto create = createInner->add(object_ptr<Ui::SettingsButton>(
+		createInner,
+		tr::lng_away_create(),
+		st::settingsButtonLightNoIcon
+	));
+	create->setClickedCallback([=] {
+
+	});
+	Ui::AddSkip(createInner);
+	Ui::AddDivider(createInner);
+
+	createWrap->toggleOn(rpl::single(true));
+
+	Ui::AddSkip(inner);
+	AddAwayScheduleSelector(inner, {
+		.controller = controller,
+		.data = &_schedule,
+	});
+	Ui::AddSkip(inner);
+	Ui::AddDivider(inner);
 
 	AddBusinessRecipientsSelector(inner, {
 		.controller = controller,
@@ -101,10 +281,18 @@ void AwayMessage::setupContent(
 
 	Ui::AddSkip(inner, st::settingsChatbotsAccessSkip);
 
+	wrap->toggleOn(enabled->toggledValue());
+	wrap->finishAnimating();
+
 	Ui::ResizeFitChild(this, content);
 }
 
 void AwayMessage::save() {
+	controller()->session().data().businessInfo().saveAwaySettings(
+		_enabled.current() ? Data::AwaySettings{
+			.recipients = _recipients.current(),
+			.schedule = _schedule.current(),
+		} : Data::AwaySettings());
 }
 
 } // namespace
diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style
index 3e57d64cc..41fb908e0 100644
--- a/Telegram/SourceFiles/settings/settings.style
+++ b/Telegram/SourceFiles/settings/settings.style
@@ -614,3 +614,5 @@ settingsWorkingHoursWeek: SettingsButton(settingsButtonNoIcon) {
 settingsWorkingHoursDetails: settingsNotificationTypeDetails;
 settingsWorkingHoursPicker: 200px;
 settingsWorkingHoursPickerItemHeight: 40px;
+
+settingsAwaySchedulePadding: margins(0px, 8px, 0px, 8px);

From dd7ccada2f2ff97582271a6c4668f5324c2394e5 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 23 Feb 2024 13:56:18 +0400
Subject: [PATCH 050/108] Support edit / save of greeting message settings.

---
 .../data/business/data_business_common.h      |  14 ++
 .../data/business/data_business_info.cpp      |  48 +++++
 .../data/business/data_business_info.h        |  10 +
 Telegram/SourceFiles/data/data_user.cpp       |  16 ++
 .../settings/business/settings_greeting.cpp   | 189 +++++++++++++++++-
 5 files changed, 266 insertions(+), 11 deletions(-)

diff --git a/Telegram/SourceFiles/data/business/data_business_common.h b/Telegram/SourceFiles/data/business/data_business_common.h
index a74b5df48..914d62503 100644
--- a/Telegram/SourceFiles/data/business/data_business_common.h
+++ b/Telegram/SourceFiles/data/business/data_business_common.h
@@ -202,4 +202,18 @@ struct AwaySettings {
 		const AwaySettings &b) = default;
 };
 
+struct GreetingSettings {
+	BusinessRecipients recipients;
+	int noActivityDays = 0;
+	int shortcutId = 0;
+
+	explicit operator bool() const {
+		return noActivityDays > 0;
+	}
+
+	friend inline bool operator==(
+		const GreetingSettings &a,
+		const GreetingSettings &b) = default;
+};
+
 } // namespace Data
diff --git a/Telegram/SourceFiles/data/business/data_business_info.cpp b/Telegram/SourceFiles/data/business/data_business_info.cpp
index d054d3a56..1a1d30555 100644
--- a/Telegram/SourceFiles/data/business/data_business_info.cpp
+++ b/Telegram/SourceFiles/data/business/data_business_info.cpp
@@ -74,6 +74,20 @@ template <typename Flag>
 			| ranges::views::transform(&UserData::inputUser)));
 }
 
+[[nodiscard]] MTPInputBusinessGreetingMessage ToMTP(
+		const GreetingSettings &data) {
+	using Flag = MTPDinputBusinessGreetingMessage::Flag;
+	return MTP_inputBusinessGreetingMessage(
+		MTP_flags(RecipientsFlags(data.recipients, Flag())),
+		MTP_int(data.shortcutId),
+		MTP_vector_from_range(
+			(data.recipients.allButExcluded
+				? data.recipients.excluded
+				: data.recipients.included).list
+			| ranges::views::transform(&UserData::inputUser)),
+		MTP_int(data.noActivityDays));
+}
+
 } // namespace
 
 BusinessInfo::BusinessInfo(not_null<Session*> owner)
@@ -132,6 +146,40 @@ rpl::producer<> BusinessInfo::awaySettingsChanged() const {
 	return _awaySettingsChanged.events();
 }
 
+void BusinessInfo::applyGreetingSettings(GreetingSettings data) {
+	if (_greetingSettings == data) {
+		return;
+	}
+	_greetingSettings = data;
+	_greetingSettingsChanged.fire({});
+}
+
+void BusinessInfo::saveGreetingSettings(GreetingSettings data) {
+	if (_greetingSettings == data) {
+		return;
+	}
+	using Flag = MTPaccount_UpdateBusinessGreetingMessage::Flag;
+	_owner->session().api().request(MTPaccount_UpdateBusinessGreetingMessage(
+		MTP_flags(data ? Flag::f_message : Flag()),
+		data ? ToMTP(data) : MTPInputBusinessGreetingMessage()
+	)).send();
+
+	_greetingSettings = std::move(data);
+	_greetingSettingsChanged.fire({});
+}
+
+bool BusinessInfo::greetingSettingsLoaded() const {
+	return _greetingSettings.has_value();
+}
+
+GreetingSettings BusinessInfo::greetingSettings() const {
+	return _greetingSettings.value_or(GreetingSettings());
+}
+
+rpl::producer<> BusinessInfo::greetingSettingsChanged() const {
+	return _greetingSettingsChanged.events();
+}
+
 void BusinessInfo::preload() {
 	preloadTimezones();
 }
diff --git a/Telegram/SourceFiles/data/business/data_business_info.h b/Telegram/SourceFiles/data/business/data_business_info.h
index 9cd7f9681..e572d2757 100644
--- a/Telegram/SourceFiles/data/business/data_business_info.h
+++ b/Telegram/SourceFiles/data/business/data_business_info.h
@@ -28,6 +28,12 @@ public:
 	[[nodiscard]] bool awaySettingsLoaded() const;
 	[[nodiscard]] rpl::producer<> awaySettingsChanged() const;
 
+	void saveGreetingSettings(GreetingSettings data);
+	void applyGreetingSettings(GreetingSettings data);
+	[[nodiscard]] GreetingSettings greetingSettings() const;
+	[[nodiscard]] bool greetingSettingsLoaded() const;
+	[[nodiscard]] rpl::producer<> greetingSettingsChanged() const;
+
 	void preloadTimezones();
 	[[nodiscard]] rpl::producer<Timezones> timezonesValue() const;
 
@@ -35,9 +41,13 @@ private:
 	const not_null<Session*> _owner;
 
 	rpl::variable<Timezones> _timezones;
+
 	std::optional<AwaySettings> _awaySettings;
 	rpl::event_stream<> _awaySettingsChanged;
 
+	std::optional<GreetingSettings> _greetingSettings;
+	rpl::event_stream<> _greetingSettingsChanged;
+
 	mtpRequestId _timezonesRequestId = 0;
 	int32 _timezonesHash = 0;
 
diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp
index 93346ec90..35ad558c7 100644
--- a/Telegram/SourceFiles/data/data_user.cpp
+++ b/Telegram/SourceFiles/data/data_user.cpp
@@ -112,6 +112,20 @@ Data::BusinessRecipients RecipientsFromMTP(
 	return result;
 }
 
+[[nodiscard]] Data::GreetingSettings FromMTP(
+		not_null<Data::Session*> owner,
+		const tl::conditional<MTPBusinessGreetingMessage> &message) {
+	if (!message) {
+		return Data::GreetingSettings();
+	}
+	const auto &data = message->data();
+	return Data::GreetingSettings{
+		.recipients = RecipientsFromMTP(owner, data),
+		.noActivityDays = data.vno_activity_days().v,
+		.shortcutId = data.vshortcut_id().v,
+	};
+}
+
 } // namespace
 
 BotInfo::BotInfo() = default;
@@ -678,6 +692,8 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
 	if (user->isSelf()) {
 		user->owner().businessInfo().applyAwaySettings(
 			FromMTP(&user->owner(), update.vbusiness_away_message()));
+		user->owner().businessInfo().applyGreetingSettings(
+			FromMTP(&user->owner(), update.vbusiness_greeting_message()));
 	}
 
 	user->owner().stories().apply(user, update.vstories());
diff --git a/Telegram/SourceFiles/settings/business/settings_greeting.cpp b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
index 599b25b2c..39520846f 100644
--- a/Telegram/SourceFiles/settings/business/settings_greeting.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
@@ -7,22 +7,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "settings/business/settings_greeting.h"
 
+#include "base/event_filter.h"
 #include "core/application.h"
+#include "data/business/data_business_info.h"
 #include "data/data_session.h"
 #include "lang/lang_keys.h"
 #include "main/main_session.h"
 #include "settings/business/settings_recipients_helper.h"
+#include "ui/layers/generic_box.h"
 #include "ui/text/text_utilities.h"
+#include "ui/widgets/box_content_divider.h"
 #include "ui/widgets/buttons.h"
+#include "ui/widgets/vertical_drum_picker.h"
 #include "ui/wrap/slide_wrap.h"
 #include "ui/wrap/vertical_layout.h"
 #include "ui/vertical_list.h"
 #include "window/window_session_controller.h"
+#include "styles/style_layers.h"
 #include "styles/style_settings.h"
 
 namespace Settings {
 namespace {
 
+constexpr auto kDefaultNoActivityDays = 7;
+
 class Greeting : public BusinessSection<Greeting> {
 public:
 	Greeting(
@@ -32,21 +40,129 @@ public:
 
 	[[nodiscard]] rpl::producer<QString> title() override;
 
+	const Ui::RoundRect *bottomSkipRounding() const {
+		return &_bottomSkipRounding;
+	}
+
 private:
 	void setupContent(not_null<Window::SessionController*> controller);
 	void save();
 
+	Ui::RoundRect _bottomSkipRounding;
+
 	rpl::variable<Data::BusinessRecipients> _recipients;
+	rpl::variable<int> _noActivityDays;
+	rpl::variable<bool> _enabled;
 
 };
 
 Greeting::Greeting(
 	QWidget *parent,
 	not_null<Window::SessionController*> controller)
-: BusinessSection(parent, controller) {
+: BusinessSection(parent, controller)
+, _bottomSkipRounding(st::boxRadius, st::boxDividerBg) {
 	setupContent(controller);
 }
 
+void EditPeriodBox(
+		not_null<Ui::GenericBox*> box,
+		int days,
+		Fn<void(int)> save) {
+	auto values = base::flat_set<int>{ 7, 14, 21, 28 };
+	if (!values.contains(days)) {
+		values.emplace(days);
+	}
+	const auto startIndex = int(values.find(days) - begin(values));
+
+	const auto content = box->addRow(object_ptr<Ui::FixedHeightWidget>(
+		box,
+		st::settingsWorkingHoursPicker));
+
+	const auto font = st::boxTextFont;
+	const auto itemHeight = st::settingsWorkingHoursPickerItemHeight;
+	auto paintCallback = [=](
+			QPainter &p,
+			int index,
+			float64 y,
+			float64 distanceFromCenter,
+			int outerWidth) {
+		const auto r = QRectF(0, y, outerWidth, itemHeight);
+		const auto progress = std::abs(distanceFromCenter);
+		const auto revProgress = 1. - progress;
+		p.save();
+		p.translate(r.center());
+		constexpr auto kMinYScale = 0.2;
+		const auto yScale = kMinYScale
+			+ (1. - kMinYScale) * anim::easeOutCubic(1., revProgress);
+		p.scale(1., yScale);
+		p.translate(-r.center());
+		p.setOpacity(revProgress);
+		p.setFont(font);
+		p.setPen(st::defaultFlatLabel.textFg);
+		p.drawText(
+			r,
+			tr::lng_days(tr::now, lt_count, *(values.begin() + index)),
+			style::al_center);
+		p.restore();
+	};
+
+	const auto picker = Ui::CreateChild<Ui::VerticalDrumPicker>(
+		content,
+		std::move(paintCallback),
+		int(values.size()),
+		itemHeight,
+		startIndex);
+
+	content->sizeValue(
+	) | rpl::start_with_next([=](const QSize &s) {
+		picker->resize(s.width(), s.height());
+		picker->moveToLeft((s.width() - picker->width()) / 2, 0);
+	}, content->lifetime());
+
+	content->paintRequest(
+	) | rpl::start_with_next([=](const QRect &r) {
+		auto p = QPainter(content);
+
+		p.fillRect(r, Qt::transparent);
+
+		const auto lineRect = QRect(
+			0,
+			content->height() / 2,
+			content->width(),
+			st::defaultInputField.borderActive);
+		p.fillRect(lineRect.translated(0, itemHeight / 2), st::activeLineFg);
+		p.fillRect(lineRect.translated(0, -itemHeight / 2), st::activeLineFg);
+	}, content->lifetime());
+
+	base::install_event_filter(content, [=](not_null<QEvent*> e) {
+		if ((e->type() == QEvent::MouseButtonPress)
+			|| (e->type() == QEvent::MouseButtonRelease)
+			|| (e->type() == QEvent::MouseMove)) {
+			picker->handleMouseEvent(static_cast<QMouseEvent*>(e.get()));
+		} else if (e->type() == QEvent::Wheel) {
+			picker->handleWheelEvent(static_cast<QWheelEvent*>(e.get()));
+		}
+		return base::EventFilterResult::Continue;
+	});
+	base::install_event_filter(box, [=](not_null<QEvent*> e) {
+		if (e->type() == QEvent::KeyPress) {
+			picker->handleKeyEvent(static_cast<QKeyEvent*>(e.get()));
+		}
+		return base::EventFilterResult::Continue;
+	});
+
+	box->addButton(tr::lng_settings_save(), [=] {
+		const auto weak = Ui::MakeWeak(box);
+		save(*(begin(values) + picker->index()));
+		if (const auto strong = weak.data()) {
+			strong->closeBox();
+		}
+	});
+	box->addButton(tr::lng_cancel(), [=] {
+		box->closeBox();
+	});
+}
+
 Greeting::~Greeting() {
 	if (!Core::Quitting()) {
 		save();
@@ -62,9 +178,14 @@ void Greeting::setupContent(
 	using namespace rpl::mappers;
 
 	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
-	//const auto current = controller->session().data().chatbots().current();
+	const auto info = &controller->session().data().businessInfo();
+	const auto current = info->greetingSettings();
+	const auto disabled = !current.noActivityDays;
 
-	//_recipients = current.recipients;
+	_recipients = current.recipients;
+	_noActivityDays = disabled
+		? kDefaultNoActivityDays
+		: current.noActivityDays;
 
 	AddDividerTextWithLottie(content, {
 		.lottie = u"greeting"_q,
@@ -80,7 +201,27 @@ void Greeting::setupContent(
 		content,
 		tr::lng_greeting_enable(),
 		st::settingsButtonNoIcon
-	))->toggleOn(rpl::single(false));
+	))->toggleOn(rpl::single(!disabled));
+
+	_enabled = enabled->toggledValue();
+
+	Ui::AddSkip(content);
+
+	content->add(
+		object_ptr<Ui::SlideWrap<Ui::BoxContentDivider>>(
+			content,
+			object_ptr<Ui::BoxContentDivider>(
+				content,
+				st::boxDividerHeight,
+				st::boxDividerBg,
+				RectPart::Top))
+	)->setDuration(0)->toggleOn(enabled->toggledValue() | rpl::map(!_1));
+	content->add(
+		object_ptr<Ui::SlideWrap<Ui::BoxContentDivider>>(
+			content,
+			object_ptr<Ui::BoxContentDivider>(
+				content))
+	)->setDuration(0)->toggleOn(enabled->toggledValue());
 
 	const auto wrap = content->add(
 		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
@@ -88,24 +229,50 @@ void Greeting::setupContent(
 			object_ptr<Ui::VerticalLayout>(content)));
 	const auto inner = wrap->entity();
 
-	Ui::AddSkip(inner);
-	Ui::AddDivider(inner);
-
-	wrap->toggleOn(enabled->toggledValue());
-	wrap->finishAnimating();
-
 	AddBusinessRecipientsSelector(inner, {
 		.controller = controller,
 		.title = tr::lng_greeting_recipients(),
 		.data = &_recipients,
 	});
 
-	Ui::AddSkip(inner, st::settingsChatbotsAccessSkip);
+	Ui::AddSkip(inner);
+	Ui::AddDivider(inner);
+	Ui::AddSkip(inner);
+
+	AddButtonWithLabel(
+		inner,
+		tr::lng_greeting_period_title(),
+		_noActivityDays.value(
+		) | rpl::map(
+			[](int days) { return tr::lng_days(tr::now, lt_count, days); }
+		),
+		st::settingsButtonNoIcon
+	)->setClickedCallback([=] {
+		controller->show(Box(
+			EditPeriodBox,
+			_noActivityDays.current(),
+			[=](int days) { _noActivityDays = days; }));
+	});
+
+	Ui::AddSkip(inner);
+	Ui::AddDividerText(
+		inner,
+		tr::lng_greeting_period_about(),
+		st::settingsChatbotsBottomTextMargin,
+		RectPart::Top);
+
+	wrap->toggleOn(enabled->toggledValue());
+	wrap->finishAnimating();
 
 	Ui::ResizeFitChild(this, content);
 }
 
 void Greeting::save() {
+	controller()->session().data().businessInfo().saveGreetingSettings(
+		_enabled.current() ? Data::GreetingSettings{
+			.recipients = _recipients.current(),
+			.noActivityDays = _noActivityDays.current(),
+		} : Data::GreetingSettings());
 }
 
 } // namespace

From d05c4e099085c29df9e8d66519e772236ae2b8e3 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 23 Feb 2024 21:23:15 +0400
Subject: [PATCH 051/108] Start shortcut messages sending.

---
 Telegram/CMakeLists.txt                       |    4 +
 Telegram/SourceFiles/api/api_common.h         |    1 +
 Telegram/SourceFiles/api/api_polls.cpp        |    6 +-
 Telegram/SourceFiles/api/api_sending.cpp      |   42 +-
 Telegram/SourceFiles/apiwrap.cpp              |   54 +-
 Telegram/SourceFiles/boxes/share_box.cpp      |    9 +-
 .../data/business/data_business_common.h      |    4 +-
 .../data/business/data_shortcut_messages.cpp  |  539 ++++++++
 .../data/business/data_shortcut_messages.h    |  125 ++
 Telegram/SourceFiles/data/data_msg_id.h       |    4 +-
 .../data/data_scheduled_messages.cpp          |    3 +-
 Telegram/SourceFiles/data/data_session.cpp    |    9 +-
 Telegram/SourceFiles/data/data_session.h      |   11 +-
 Telegram/SourceFiles/data/data_sparse_ids.h   |    3 +-
 Telegram/SourceFiles/data/data_types.cpp      |    9 +
 Telegram/SourceFiles/data/data_types.h        |   11 +-
 Telegram/SourceFiles/history/history_item.h   |    7 +
 .../SourceFiles/info/info_content_widget.cpp  |   11 +
 .../SourceFiles/info/info_content_widget.h    |    8 +-
 .../SourceFiles/info/info_layer_widget.cpp    |    7 +-
 .../SourceFiles/info/info_section_widget.cpp  |    6 +-
 .../SourceFiles/info/info_wrap_widget.cpp     |    6 +-
 Telegram/SourceFiles/info/info_wrap_widget.h  |    4 +-
 .../info/settings/info_settings_widget.cpp    |   11 +-
 .../info/settings/info_settings_widget.h      |    2 +
 Telegram/SourceFiles/main/main_session.cpp    |    4 +-
 Telegram/SourceFiles/main/main_session.h      |    2 +-
 .../media/stories/media_stories_share.cpp     |   10 +-
 .../business/settings_away_message.cpp        |   13 +-
 .../settings/business/settings_chatbots.cpp   |    2 +-
 .../settings/business/settings_greeting.cpp   |   25 +
 .../business/settings_shortcut_messages.cpp   | 1183 +++++++++++++++++
 .../business/settings_shortcut_messages.h     |   16 +
 .../settings/settings_business.cpp            |    5 +-
 .../SourceFiles/settings/settings_common.h    |    7 +
 .../settings/settings_common_session.h        |   10 +-
 .../settings/settings_notifications_type.cpp  |    3 +-
 .../SourceFiles/settings/settings_premium.cpp |    3 +-
 38 files changed, 2109 insertions(+), 70 deletions(-)
 create mode 100644 Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
 create mode 100644 Telegram/SourceFiles/data/business/data_shortcut_messages.h
 create mode 100644 Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
 create mode 100644 Telegram/SourceFiles/settings/business/settings_shortcut_messages.h

diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index 6010bf833..76e06c8e1 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -454,6 +454,8 @@ PRIVATE
     data/business/data_business_common.h
     data/business/data_business_info.cpp
     data/business/data_business_info.h
+    data/business/data_shortcut_messages.cpp
+    data/business/data_shortcut_messages.h
     data/notify/data_notify_settings.cpp
     data/notify/data_notify_settings.h
     data/notify/data_peer_notify_settings.cpp
@@ -1287,6 +1289,8 @@ PRIVATE
     profile/profile_cover_drop_area.h
     settings/business/settings_away_message.cpp
     settings/business/settings_away_message.h
+    settings/business/settings_shortcut_messages.cpp
+    settings/business/settings_shortcut_messages.h
     settings/business/settings_chatbots.cpp
     settings/business/settings_chatbots.h
     settings/business/settings_greeting.cpp
diff --git a/Telegram/SourceFiles/api/api_common.h b/Telegram/SourceFiles/api/api_common.h
index 155666a5d..fe0d489b0 100644
--- a/Telegram/SourceFiles/api/api_common.h
+++ b/Telegram/SourceFiles/api/api_common.h
@@ -22,6 +22,7 @@ inline constexpr auto kScheduledUntilOnlineTimestamp = TimeId(0x7FFFFFFE);
 struct SendOptions {
 	PeerData *sendAs = nullptr;
 	TimeId scheduled = 0;
+	BusinessShortcutId shortcutId = 0;
 	bool silent = false;
 	bool handleSupportSwitch = false;
 	bool hideViaBot = false;
diff --git a/Telegram/SourceFiles/api/api_polls.cpp b/Telegram/SourceFiles/api/api_polls.cpp
index f465d7f37..3532e2c79 100644
--- a/Telegram/SourceFiles/api/api_polls.cpp
+++ b/Telegram/SourceFiles/api/api_polls.cpp
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "api/api_updates.h"
 #include "apiwrap.h"
 #include "base/random.h"
+#include "data/business/data_shortcut_messages.h"
 #include "data/data_changes.h"
 #include "data/data_histories.h"
 #include "data/data_poll.h"
@@ -64,6 +65,9 @@ void Polls::create(
 	if (action.options.scheduled) {
 		sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
 	}
+	if (action.options.shortcutId) {
+		sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
+	}
 	const auto sendAs = action.options.sendAs;
 	if (sendAs) {
 		sendFlags |= MTPmessages_SendMedia::Flag::f_send_as;
@@ -85,7 +89,7 @@ void Polls::create(
 			MTPVector<MTPMessageEntity>(),
 			MTP_int(action.options.scheduled),
 			(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
-			MTPInputQuickReplyShortcut()
+			Data::ShortcutIdToMTP(_session, action.options.shortcutId)
 		), [=](const MTPUpdates &result, const MTP::Response &response) {
 		if (clearCloudDraft) {
 			history->finishSavingCloudDraft(
diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp
index babce9329..96bdf4a4a 100644
--- a/Telegram/SourceFiles/api/api_sending.cpp
+++ b/Telegram/SourceFiles/api/api_sending.cpp
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "api/api_text_entities.h"
 #include "base/random.h"
 #include "base/unixtime.h"
+#include "data/business/data_shortcut_messages.h"
 #include "data/data_document.h"
 #include "data/data_photo.h"
 #include "data/data_channel.h" // ChannelData::addsSignature.
@@ -128,6 +129,9 @@ void SendExistingMedia(
 		flags |= MessageFlag::IsOrWasScheduled;
 		sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
 	}
+	if (message.action.options.shortcutId) {
+		sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
+	}
 
 	session->data().registerMessageRandomId(randomId, newId);
 
@@ -146,10 +150,12 @@ void SendExistingMedia(
 
 	const auto performRequest = [=](const auto &repeatRequest) -> void {
 		auto &histories = history->owner().histories();
+		const auto session = &history->session();
+		const auto &action = message.action;
 		const auto usedFileReference = media->fileReference();
 		histories.sendPreparedMessage(
 			history,
-			message.action.replyTo,
+			action.replyTo,
 			randomId,
 			Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
 				MTP_flags(sendFlags),
@@ -160,9 +166,9 @@ void SendExistingMedia(
 				MTP_long(randomId),
 				MTPReplyMarkup(),
 				sentEntities,
-				MTP_int(message.action.options.scheduled),
+				MTP_int(action.options.scheduled),
 				(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
-				MTPInputQuickReplyShortcut()
+				Data::ShortcutIdToMTP(session, action.options.shortcutId)
 			), [=](const MTPUpdates &result, const MTP::Response &response) {
 		}, [=](const MTP::Error &error, const MTP::Response &response) {
 			if (error.code() == 400
@@ -260,7 +266,10 @@ bool SendDice(MessageToSend &message) {
 	message.textWithTags = TextWithTags();
 	message.action.clearDraft = false;
 	message.action.generateLocal = true;
-	api->sendAction(message.action);
+
+
+	const auto &action = message.action;
+	api->sendAction(action);
 
 	const auto newId = FullMsgId(
 		peer->id,
@@ -270,17 +279,17 @@ bool SendDice(MessageToSend &message) {
 	auto &histories = history->owner().histories();
 	auto flags = NewMessageFlags(peer);
 	auto sendFlags = MTPmessages_SendMedia::Flags(0);
-	if (message.action.replyTo) {
+	if (action.replyTo) {
 		flags |= MessageFlag::HasReplyInfo;
 		sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
 	}
 	const auto anonymousPost = peer->amAnonymous();
-	const auto silentPost = ShouldSendSilent(peer, message.action.options);
-	InnerFillMessagePostFlags(message.action.options, peer, flags);
+	const auto silentPost = ShouldSendSilent(peer, action.options);
+	InnerFillMessagePostFlags(action.options, peer, flags);
 	if (silentPost) {
 		sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
 	}
-	const auto sendAs = message.action.options.sendAs;
+	const auto sendAs = action.options.sendAs;
 	const auto messageFromId = sendAs
 		? sendAs->id
 		: anonymousPost
@@ -293,10 +302,13 @@ bool SendDice(MessageToSend &message) {
 		? session->user()->name()
 		: QString();
 
-	if (message.action.options.scheduled) {
+	if (action.options.scheduled) {
 		flags |= MessageFlag::IsOrWasScheduled;
 		sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
 	}
+	if (action.options.shortcutId) {
+		sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
+	}
 
 	session->data().registerMessageRandomId(randomId, newId);
 
@@ -305,8 +317,8 @@ bool SendDice(MessageToSend &message) {
 		newId.msg,
 		flags,
 		viaBotId,
-		message.action.replyTo,
-		HistoryItem::NewMessageDate(message.action.options.scheduled),
+		action.replyTo,
+		HistoryItem::NewMessageDate(action.options.scheduled),
 		messageFromId,
 		messagePostAuthor,
 		TextWithEntities(),
@@ -314,7 +326,7 @@ bool SendDice(MessageToSend &message) {
 		HistoryMessageMarkupData());
 	histories.sendPreparedMessage(
 		history,
-		message.action.replyTo,
+		action.replyTo,
 		randomId,
 		Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
 			MTP_flags(sendFlags),
@@ -325,14 +337,14 @@ bool SendDice(MessageToSend &message) {
 			MTP_long(randomId),
 			MTPReplyMarkup(),
 			MTP_vector<MTPMessageEntity>(),
-			MTP_int(message.action.options.scheduled),
+			MTP_int(action.options.scheduled),
 			(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
-			MTPInputQuickReplyShortcut()
+			Data::ShortcutIdToMTP(session, action.options.shortcutId)
 		), [=](const MTPUpdates &result, const MTP::Response &response) {
 	}, [=](const MTP::Error &error, const MTP::Response &response) {
 		api->sendMessageFail(error, peer, randomId, newId);
 	});
-	api->finishForwarding(message.action);
+	api->finishForwarding(action);
 	return true;
 }
 
diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index 0d603a044..bca32c3da 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "api/api_premium.h"
 #include "api/api_user_names.h"
 #include "api/api_websites.h"
+#include "data/business/data_shortcut_messages.h"
 #include "data/notify/data_notify_settings.h"
 #include "data/data_changes.h"
 #include "data/data_web_page.h"
@@ -3140,7 +3141,9 @@ void ApiWrap::sharedMediaDone(
 }
 
 void ApiWrap::sendAction(const SendAction &action) {
-	if (!action.options.scheduled && !action.replaceMediaOf) {
+	if (!action.options.scheduled
+		&& !action.options.shortcutId
+		&& !action.replaceMediaOf) {
 		const auto topicRootId = action.replyTo.topicRootId;
 		const auto topic = topicRootId
 			? action.history->peer->forumTopicFor(topicRootId)
@@ -3175,11 +3178,13 @@ void ApiWrap::finishForwarding(const SendAction &action) {
 	}
 
 	_session->data().sendHistoryChangeNotifications();
-	_session->changes().historyUpdated(
-		history,
-		(action.options.scheduled
-			? Data::HistoryUpdate::Flag::ScheduledSent
-			: Data::HistoryUpdate::Flag::MessageSent));
+	if (!action.options.shortcutId) {
+		_session->changes().historyUpdated(
+			history,
+			(action.options.scheduled
+				? Data::HistoryUpdate::Flag::ScheduledSent
+				: Data::HistoryUpdate::Flag::MessageSent));
+	}
 }
 
 void ApiWrap::forwardMessages(
@@ -3208,7 +3213,7 @@ void ApiWrap::forwardMessages(
 	const auto history = action.history;
 	const auto peer = history->peer;
 
-	if (!action.options.scheduled) {
+	if (!action.options.scheduled && !action.options.shortcutId) {
 		histories.readInbox(history);
 	}
 	const auto anonymousPost = peer->amAnonymous();
@@ -3226,6 +3231,9 @@ void ApiWrap::forwardMessages(
 		flags |= MessageFlag::IsOrWasScheduled;
 		sendFlags |= SendFlag::f_schedule_date;
 	}
+	if (action.options.shortcutId) {
+		sendFlags |= SendFlag::f_quick_reply_shortcut;
+	}
 	if (draft.options != Data::ForwardOptions::PreserveInfo) {
 		sendFlags |= SendFlag::f_drop_author;
 	}
@@ -3265,7 +3273,7 @@ void ApiWrap::forwardMessages(
 				MTP_int(topMsgId),
 				MTP_int(action.options.scheduled),
 				(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
-				MTPInputQuickReplyShortcut()
+				Data::ShortcutIdToMTP(_session, action.options.shortcutId)
 			)).done([=](const MTPUpdates &result) {
 				applyUpdates(result);
 				if (shared && !--shared->requestsLeft) {
@@ -3728,6 +3736,10 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
 			sendFlags |= MTPmessages_SendMessage::Flag::f_schedule_date;
 			mediaFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
 		}
+		if (action.options.shortcutId) {
+			sendFlags |= MTPmessages_SendMessage::Flag::f_quick_reply_shortcut;
+			mediaFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
+		}
 		const auto viaBotId = UserId();
 		lastMessage = history->addNewLocalMessage(
 			newId.msg,
@@ -3763,6 +3775,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
 					UnixtimeFromMsgId(response.outerMsgId));
 			}
 		};
+		const auto mtpShortcut = Data::ShortcutIdToMTP(
+			_session,
+			action.options.shortcutId);
 		if (exactWebPage
 			&& !ignoreWebPage
 			&& (manualWebPage || sending.empty())) {
@@ -3779,9 +3794,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
 					MTP_long(randomId),
 					MTPReplyMarkup(),
 					sentEntities,
-					MTP_int(message.action.options.scheduled),
+					MTP_int(action.options.scheduled),
 					(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
-					MTPInputQuickReplyShortcut()
+					mtpShortcut
 				), done, fail);
 		} else {
 			histories.sendPreparedMessage(
@@ -3798,7 +3813,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
 					sentEntities,
 					MTP_int(action.options.scheduled),
 					(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
-					MTPInputQuickReplyShortcut()
+					mtpShortcut
 				), done, fail);
 		}
 		isFirst = false;
@@ -3887,6 +3902,9 @@ void ApiWrap::sendInlineResult(
 		flags |= MessageFlag::IsOrWasScheduled;
 		sendFlags |= SendFlag::f_schedule_date;
 	}
+	if (action.options.shortcutId) {
+		sendFlags |= SendFlag::f_quick_reply_shortcut;
+	}
 	if (action.options.hideViaBot) {
 		sendFlags |= SendFlag::f_hide_via;
 	}
@@ -3932,7 +3950,7 @@ void ApiWrap::sendInlineResult(
 			MTP_string(data->getId()),
 			MTP_int(action.options.scheduled),
 			(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
-			MTPInputQuickReplyShortcut()
+			Data::ShortcutIdToMTP(_session, action.options.shortcutId)
 		), [=](const MTPUpdates &result, const MTP::Response &response) {
 		history->finishSavingCloudDraft(
 			topicRootId,
@@ -4061,7 +4079,8 @@ void ApiWrap::sendMediaWithRandomId(
 			: Flag(0))
 		| (!sentEntities.v.isEmpty() ? Flag::f_entities : Flag(0))
 		| (options.scheduled ? Flag::f_schedule_date : Flag(0))
-		| (options.sendAs ? Flag::f_send_as : Flag(0));
+		| (options.sendAs ? Flag::f_send_as : Flag(0))
+		| (options.shortcutId ? Flag::f_quick_reply_shortcut : Flag(0));
 
 	auto &histories = history->owner().histories();
 	const auto peer = history->peer;
@@ -4081,7 +4100,7 @@ void ApiWrap::sendMediaWithRandomId(
 			sentEntities,
 			MTP_int(options.scheduled),
 			(options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()),
-			MTPInputQuickReplyShortcut()
+			Data::ShortcutIdToMTP(_session, options.shortcutId)
 		), [=](const MTPUpdates &result, const MTP::Response &response) {
 		if (done) done(true);
 		if (updateRecentStickers) {
@@ -4166,7 +4185,10 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
 			? Flag::f_silent
 			: Flag(0))
 		| (album->options.scheduled ? Flag::f_schedule_date : Flag(0))
-		| (sendAs ? Flag::f_send_as : Flag(0));
+		| (sendAs ? Flag::f_send_as : Flag(0))
+		| (album->options.shortcutId
+			? Flag::f_quick_reply_shortcut
+			: Flag(0));
 	auto &histories = history->owner().histories();
 	const auto peer = history->peer;
 	histories.sendPreparedMessage(
@@ -4180,7 +4202,7 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
 			MTP_vector<MTPInputSingleMedia>(medias),
 			MTP_int(album->options.scheduled),
 			(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
-			MTPInputQuickReplyShortcut()
+			Data::ShortcutIdToMTP(_session, album->options.shortcutId)
 		), [=](const MTPUpdates &result, const MTP::Response &response) {
 		_sendingAlbums.remove(groupId);
 	}, [=](const MTP::Error &error, const MTP::Response &response) {
diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp
index 8577aad62..4581fac40 100644
--- a/Telegram/SourceFiles/boxes/share_box.cpp
+++ b/Telegram/SourceFiles/boxes/share_box.cpp
@@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "boxes/peer_list_controllers.h"
 #include "chat_helpers/emoji_suggestions_widget.h"
 #include "chat_helpers/share_message_phrase_factory.h"
+#include "data/business/data_shortcut_messages.h"
 #include "data/data_channel.h"
 #include "data/data_game.h"
 #include "data/data_histories.h"
@@ -1543,11 +1544,15 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
 			const auto threadHistory = thread->owningHistory();
 			histories.sendRequest(threadHistory, requestType, [=](
 					Fn<void()> finish) {
-				auto &api = threadHistory->session().api();
+				const auto session = &threadHistory->session();
+				auto &api = session->api();
 				const auto sendFlags = commonSendFlags
 					| (topMsgId ? Flag::f_top_msg_id : Flag(0))
 					| (ShouldSendSilent(peer, options)
 						? Flag::f_silent
+						: Flag(0))
+					| (options.shortcutId
+						? Flag::f_quick_reply_shortcut
 						: Flag(0));
 				threadHistory->sendRequestId = api.request(
 					MTPmessages_ForwardMessages(
@@ -1559,7 +1564,7 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
 						MTP_int(topMsgId),
 						MTP_int(options.scheduled),
 						MTP_inputPeerEmpty(), // send_as
-						MTPInputQuickReplyShortcut()
+						Data::ShortcutIdToMTP(session, options.shortcutId)
 				)).done([=](const MTPUpdates &updates, mtpRequestId reqId) {
 					threadHistory->session().api().applyUpdates(updates);
 					state->requests.remove(reqId);
diff --git a/Telegram/SourceFiles/data/business/data_business_common.h b/Telegram/SourceFiles/data/business/data_business_common.h
index 914d62503..6f3b716dc 100644
--- a/Telegram/SourceFiles/data/business/data_business_common.h
+++ b/Telegram/SourceFiles/data/business/data_business_common.h
@@ -191,7 +191,7 @@ struct AwaySchedule {
 struct AwaySettings {
 	BusinessRecipients recipients;
 	AwaySchedule schedule;
-	int shortcutId = 0;
+	BusinessShortcutId shortcutId = 0;
 
 	explicit operator bool() const {
 		return schedule.type != AwayScheduleType::Never;
@@ -205,7 +205,7 @@ struct AwaySettings {
 struct GreetingSettings {
 	BusinessRecipients recipients;
 	int noActivityDays = 0;
-	int shortcutId = 0;
+	BusinessShortcutId shortcutId = 0;
 
 	explicit operator bool() const {
 		return noActivityDays > 0;
diff --git a/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp b/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
new file mode 100644
index 000000000..1013c05bc
--- /dev/null
+++ b/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
@@ -0,0 +1,539 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "data/business/data_shortcut_messages.h"
+
+#include "api/api_hash.h"
+#include "apiwrap.h"
+#include "base/unixtime.h"
+#include "data/data_peer.h"
+#include "data/data_session.h"
+#include "api/api_text_entities.h"
+#include "main/main_session.h"
+#include "history/history.h"
+#include "history/history_item_components.h"
+#include "history/history_item_helpers.h"
+#include "apiwrap.h"
+
+namespace Data {
+namespace {
+
+constexpr auto kRequestTimeLimit = 60 * crl::time(1000);
+
+[[nodiscard]] MsgId RemoteToLocalMsgId(MsgId id) {
+	Expects(IsServerMsgId(id));
+
+	return ServerMaxMsgId + id + 1;
+}
+
+[[nodiscard]] MsgId LocalToRemoteMsgId(MsgId id) {
+	Expects(IsShortcutMsgId(id));
+
+	return (id - ServerMaxMsgId - 1);
+}
+
+[[nodiscard]] bool TooEarlyForRequest(crl::time received) {
+	return (received > 0) && (received + kRequestTimeLimit > crl::now());
+}
+
+[[nodiscard]] MTPMessage PrepareMessage(
+		BusinessShortcutId shortcutId,
+		const MTPMessage &message) {
+	return message.match([&](const MTPDmessageEmpty &data) {
+		return MTP_messageEmpty(
+			data.vflags(),
+			data.vid(),
+			data.vpeer_id() ? *data.vpeer_id() : MTPPeer());
+	}, [&](const MTPDmessageService &data) {
+		return MTP_messageService(
+			data.vflags(),
+			data.vid(),
+			data.vfrom_id() ? *data.vfrom_id() : MTPPeer(),
+			data.vpeer_id(),
+			data.vreply_to() ? *data.vreply_to() : MTPMessageReplyHeader(),
+			data.vdate(),
+			data.vaction(),
+			MTP_int(data.vttl_period().value_or_empty()));
+	}, [&](const MTPDmessage &data) {
+		return MTP_message(
+			MTP_flags(data.vflags().v | MTPDmessage::Flag::f_quick_reply_shortcut_id),
+			data.vid(),
+			data.vfrom_id() ? *data.vfrom_id() : MTPPeer(),
+			MTPint(), // from_boosts_applied
+			data.vpeer_id(),
+			data.vsaved_peer_id() ? *data.vsaved_peer_id() : MTPPeer(),
+			data.vfwd_from() ? *data.vfwd_from() : MTPMessageFwdHeader(),
+			MTP_long(data.vvia_bot_id().value_or_empty()),
+			data.vreply_to() ? *data.vreply_to() : MTPMessageReplyHeader(),
+			data.vdate(),
+			data.vmessage(),
+			data.vmedia() ? *data.vmedia() : MTPMessageMedia(),
+			data.vreply_markup() ? *data.vreply_markup() : MTPReplyMarkup(),
+			(data.ventities()
+				? *data.ventities()
+				: MTPVector<MTPMessageEntity>()),
+			MTP_int(data.vviews().value_or_empty()),
+			MTP_int(data.vforwards().value_or_empty()),
+			data.vreplies() ? *data.vreplies() : MTPMessageReplies(),
+			MTP_int(data.vedit_date().value_or_empty()),
+			MTP_bytes(data.vpost_author().value_or_empty()),
+			MTP_long(data.vgrouped_id().value_or_empty()),
+			MTPMessageReactions(),
+			MTPVector<MTPRestrictionReason>(),
+			MTP_int(data.vttl_period().value_or_empty()),
+			MTP_int(shortcutId));
+	});
+}
+
+} // namespace
+
+bool IsShortcutMsgId(MsgId id) {
+	return (id > ScheduledMaxMsgId) && (id < ShortcutMaxMsgId);
+}
+
+ShortcutMessages::ShortcutMessages(not_null<Session*> owner)
+: _session(&owner->session())
+, _history(owner->history(_session->userPeerId()))
+, _clearTimer([=] { clearOldRequests(); }) {
+	owner->itemRemoved(
+	) | rpl::filter([](not_null<const HistoryItem*> item) {
+		return item->isBusinessShortcut();
+	}) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
+		remove(item);
+	}, _lifetime);
+}
+
+ShortcutMessages::~ShortcutMessages() {
+	for (const auto &request : _requests) {
+		_session->api().request(request.second.requestId).cancel();
+	}
+}
+
+void ShortcutMessages::clearOldRequests() {
+	const auto now = crl::now();
+	while (true) {
+		const auto i = ranges::find_if(_requests, [&](const auto &value) {
+			const auto &request = value.second;
+			return !request.requestId
+				&& (request.lastReceived + kRequestTimeLimit <= now);
+		});
+		if (i == end(_requests)) {
+			break;
+		}
+		_requests.erase(i);
+	}
+}
+
+MsgId ShortcutMessages::localMessageId(MsgId remoteId) const {
+	return RemoteToLocalMsgId(remoteId);
+}
+
+MsgId ShortcutMessages::lookupId(not_null<const HistoryItem*> item) const {
+	Expects(item->isBusinessShortcut());
+	Expects(!item->isSending());
+	Expects(!item->hasFailed());
+
+	return LocalToRemoteMsgId(item->id);
+}
+
+int ShortcutMessages::count(BusinessShortcutId shortcutId) const {
+	const auto i = _data.find(shortcutId);
+	return (i != end(_data)) ? i->second.items.size() : 0;
+}
+
+void ShortcutMessages::apply(const MTPDupdateQuickReplyMessage &update) {
+	const auto &message = update.vmessage();
+	const auto shortcutId = BusinessShortcutIdFromMessage(message);
+	if (!shortcutId) {
+		return;
+	}
+	auto &list = _data[shortcutId];
+	append(shortcutId, list, message);
+	sort(list);
+	_updates.fire_copy(shortcutId);
+}
+
+void ShortcutMessages::apply(
+		const MTPDupdateDeleteQuickReplyMessages &update) {
+	const auto shortcutId = update.vshortcut_id().v;
+	if (!shortcutId) {
+		return;
+	}
+	auto i = _data.find(shortcutId);
+	if (i == end(_data)) {
+		return;
+	}
+	for (const auto &id : update.vmessages().v) {
+		const auto &list = i->second;
+		const auto j = list.itemById.find(id.v);
+		if (j != end(list.itemById)) {
+			j->second->destroy();
+			i = _data.find(shortcutId);
+			if (i == end(_data)) {
+				break;
+			}
+		}
+	}
+	_updates.fire_copy(shortcutId);
+}
+
+void ShortcutMessages::apply(const MTPDupdateDeleteQuickReply &update) {
+	const auto shortcutId = update.vshortcut_id().v;
+	if (!shortcutId) {
+		return;
+	}
+	auto i = _data.find(shortcutId);
+	while (i != end(_data)) {
+		Assert(!i->second.itemById.empty());
+		i->second.itemById.back().second->destroy();
+		i = _data.find(shortcutId);
+	}
+	_updates.fire_copy(shortcutId);
+}
+
+void ShortcutMessages::apply(
+		const MTPDupdateMessageID &update,
+		not_null<HistoryItem*> local) {
+	const auto id = update.vid().v;
+	const auto i = _data.find(local->shortcutId());
+	Assert(i != end(_data));
+	auto &list = i->second;
+	const auto j = list.itemById.find(id);
+	if (j != end(list.itemById) || !IsServerMsgId(id)) {
+		local->destroy();
+	} else {
+		Assert(!list.itemById.contains(local->id));
+		local->setRealId(localMessageId(id));
+		list.itemById.emplace(id, local);
+	}
+}
+
+void ShortcutMessages::appendSending(not_null<HistoryItem*> item) {
+	Expects(item->isSending());
+	Expects(item->isBusinessShortcut());
+
+	const auto shortcutId = item->shortcutId();
+	auto &list = _data[shortcutId];
+	list.items.emplace_back(item);
+	sort(list);
+	_updates.fire_copy(shortcutId);
+}
+
+void ShortcutMessages::removeSending(not_null<HistoryItem*> item) {
+	Expects(item->isSending() || item->hasFailed());
+	Expects(item->isBusinessShortcut());
+
+	item->destroy();
+}
+
+rpl::producer<> ShortcutMessages::updates(BusinessShortcutId shortcutId) {
+	request(shortcutId);
+
+	return _updates.events(
+	) | rpl::filter([=](BusinessShortcutId value) {
+		return (value == shortcutId);
+	}) | rpl::to_empty;
+}
+
+Data::MessagesSlice ShortcutMessages::list(BusinessShortcutId shortcutId) {
+	auto result = Data::MessagesSlice();
+	const auto i = _data.find(shortcutId);
+	if (i == end(_data)) {
+		const auto i = _requests.find(shortcutId);
+		if (i == end(_requests)) {
+			return result;
+		}
+		result.fullCount = result.skippedAfter = result.skippedBefore = 0;
+		return result;
+	}
+	const auto &list = i->second.items;
+	result.skippedAfter = result.skippedBefore = 0;
+	result.fullCount = int(list.size());
+	result.ids = ranges::views::all(
+		list
+	) | ranges::views::transform(
+		&HistoryItem::fullId
+	) | ranges::to_vector;
+	return result;
+}
+
+void ShortcutMessages::preloadShortcuts() {
+	if (_shortcutsLoaded || _shortcutsRequestId) {
+		return;
+	}
+	const auto owner = &_session->data();
+	_shortcutsRequestId = owner->session().api().request(
+		MTPmessages_GetQuickReplies(MTP_long(_shortcutsHash))
+	).done([=](const MTPmessages_QuickReplies &result) {
+		result.match([&](const MTPDmessages_quickReplies &data) {
+			owner->processUsers(data.vusers());
+			owner->processChats(data.vchats());
+			owner->processMessages(
+				data.vmessages(),
+				NewMessageType::Existing);
+			auto shortcuts = Shortcuts();
+			const auto messages = &owner->shortcutMessages();
+			for (const auto &reply : data.vquick_replies().v) {
+				const auto &data = reply.data();
+				const auto id = BusinessShortcutId(data.vshortcut_id().v);
+				shortcuts.list.emplace(id, Shortcut{
+					.name = qs(data.vshortcut()),
+					.topMessageId = messages->localMessageId(
+						data.vtop_message().v),
+					.count = data.vcount().v,
+				});
+			}
+			for (auto &[id, shortcut] : _shortcuts.list) {
+				if (id < 0) {
+					shortcuts.list.emplace(id, shortcut);
+				}
+			}
+			const auto changed = !_shortcutsLoaded
+				|| (shortcuts != _shortcuts);
+			if (changed) {
+				_shortcuts = std::move(shortcuts);
+				_shortcutsLoaded = true;
+				_shortcutsChanged.fire({});
+			}
+		}, [&](const MTPDmessages_quickRepliesNotModified &) {
+			if (!_shortcutsLoaded) {
+				_shortcutsLoaded = true;
+				_shortcutsChanged.fire({});
+			}
+		});
+	}).send();
+}
+
+const Shortcuts &ShortcutMessages::shortcuts() const {
+	return _shortcuts;
+}
+
+bool ShortcutMessages::shortcutsLoaded() const {
+	return _shortcutsLoaded;
+}
+
+rpl::producer<> ShortcutMessages::shortcutsChanged() const {
+	return _shortcutsChanged.events();
+}
+
+BusinessShortcutId ShortcutMessages::emplaceShortcut(QString name) {
+	Expects(_shortcutsLoaded);
+
+	for (auto &[id, shortcut] : _shortcuts.list) {
+		if (shortcut.name == name) {
+			return id;
+		}
+	}
+	const auto result = --_localShortcutId;
+	_shortcuts.list.emplace(result, Shortcut{ name });
+	return result;
+}
+
+Shortcut ShortcutMessages::lookupShortcut(BusinessShortcutId id) const {
+	const auto i = _shortcuts.list.find(id);
+
+	Ensures(i != end(_shortcuts.list));
+	return i->second;
+}
+
+void ShortcutMessages::request(BusinessShortcutId shortcutId) {
+	auto &request = _requests[shortcutId];
+	if (request.requestId || TooEarlyForRequest(request.lastReceived)) {
+		return;
+	}
+	const auto i = _data.find(shortcutId);
+	const auto hash = (i != end(_data))
+		? countListHash(i->second)
+		: uint64(0);
+	request.requestId = _session->api().request(
+		MTPmessages_GetQuickReplyMessages(
+			MTP_flags(0),
+			MTP_int(shortcutId),
+			MTPVector<MTPint>(),
+			MTP_long(hash))
+	).done([=](const MTPmessages_Messages &result) {
+		parse(shortcutId, result);
+	}).fail([=] {
+		_requests.remove(shortcutId);
+	}).send();
+}
+
+void ShortcutMessages::parse(
+		BusinessShortcutId shortcutId,
+		const MTPmessages_Messages &list) {
+	auto &request = _requests[shortcutId];
+	request.lastReceived = crl::now();
+	request.requestId = 0;
+	if (!_clearTimer.isActive()) {
+		_clearTimer.callOnce(kRequestTimeLimit * 2);
+	}
+
+	list.match([&](const MTPDmessages_messagesNotModified &data) {
+	}, [&](const auto &data) {
+		_session->data().processUsers(data.vusers());
+		_session->data().processChats(data.vchats());
+
+		const auto &messages = data.vmessages().v;
+		if (messages.isEmpty()) {
+			clearNotSending(shortcutId);
+			return;
+		}
+		auto received = base::flat_set<not_null<HistoryItem*>>();
+		auto clear = base::flat_set<not_null<HistoryItem*>>();
+		auto &list = _data.emplace(shortcutId, List()).first->second;
+		for (const auto &message : messages) {
+			if (const auto item = append(shortcutId, list, message)) {
+				received.emplace(item);
+			}
+		}
+		for (const auto &owned : list.items) {
+			const auto item = owned.get();
+			if (!item->isSending() && !received.contains(item)) {
+				clear.emplace(item);
+			}
+		}
+		updated(shortcutId, received, clear);
+	});
+}
+
+HistoryItem *ShortcutMessages::append(
+		BusinessShortcutId shortcutId,
+		List &list,
+		const MTPMessage &message) {
+	const auto id = message.match([&](const auto &data) {
+		return data.vid().v;
+	});
+	const auto i = list.itemById.find(id);
+	if (i != end(list.itemById)) {
+		const auto existing = i->second;
+		message.match([&](const MTPDmessage &data) {
+			if (data.is_edit_hide()) {
+				existing->applyEdition(HistoryMessageEdition(_session, data));
+			} else {
+				existing->updateSentContent({
+					qs(data.vmessage()),
+					Api::EntitiesFromMTP(
+						_session,
+						data.ventities().value_or_empty())
+				}, data.vmedia());
+				existing->updateReplyMarkup(
+					HistoryMessageMarkupData(data.vreply_markup()));
+				existing->updateForwardedInfo(data.vfwd_from());
+			}
+			existing->updateDate(data.vdate().v);
+			_history->owner().requestItemTextRefresh(existing);
+		}, [&](const auto &data) {});
+		return existing;
+	}
+
+	if (!IsServerMsgId(id)) {
+		LOG(("API Error: Bad id in quick reply messages: %1.").arg(id));
+		return nullptr;
+	}
+	const auto item = _session->data().addNewMessage(
+		localMessageId(id),
+		PrepareMessage(shortcutId, message),
+		MessageFlags(), // localFlags
+		NewMessageType::Existing);
+	if (!item
+		|| item->history() != _history
+		|| item->shortcutId() != shortcutId) {
+		LOG(("API Error: Bad data received in quick reply messages."));
+		return nullptr;
+	}
+	list.items.emplace_back(item);
+	list.itemById.emplace(id, item);
+	return item;
+}
+
+void ShortcutMessages::clearNotSending(BusinessShortcutId shortcutId) {
+	const auto i = _data.find(shortcutId);
+	if (i == end(_data)) {
+		return;
+	}
+	auto clear = base::flat_set<not_null<HistoryItem*>>();
+	for (const auto &owned : i->second.items) {
+		if (!owned->isSending() && !owned->hasFailed()) {
+			clear.emplace(owned.get());
+		}
+	}
+	updated(shortcutId, {}, clear);
+}
+
+void ShortcutMessages::updated(
+		BusinessShortcutId shortcutId,
+		const base::flat_set<not_null<HistoryItem*>> &added,
+		const base::flat_set<not_null<HistoryItem*>> &clear) {
+	if (!clear.empty()) {
+		for (const auto &item : clear) {
+			item->destroy();
+		}
+	}
+	const auto i = _data.find(shortcutId);
+	if (i != end(_data)) {
+		sort(i->second);
+	}
+	if (!added.empty() || !clear.empty()) {
+		_updates.fire_copy(shortcutId);
+	}
+}
+
+void ShortcutMessages::sort(List &list) {
+	ranges::sort(list.items, ranges::less(), &HistoryItem::position);
+}
+
+void ShortcutMessages::remove(not_null<const HistoryItem*> item) {
+	const auto shortcutId = item->shortcutId();
+	const auto i = _data.find(shortcutId);
+	Assert(i != end(_data));
+	auto &list = i->second;
+
+	if (!item->isSending() && !item->hasFailed()) {
+		list.itemById.remove(lookupId(item));
+	}
+	const auto k = ranges::find(list.items, item, &OwnedItem::get);
+	Assert(k != list.items.end());
+	k->release();
+	list.items.erase(k);
+
+	if (list.items.empty()) {
+		_data.erase(i);
+	}
+	_updates.fire_copy(shortcutId);
+}
+
+uint64 ShortcutMessages::countListHash(const List &list) const {
+	using namespace Api;
+
+	auto hash = HashInit();
+	auto &&serverside = ranges::views::all(
+		list.items
+	) | ranges::views::filter([](const OwnedItem &item) {
+		return !item->isSending() && !item->hasFailed();
+	}) | ranges::views::reverse;
+	for (const auto &item : serverside) {
+		HashUpdate(hash, lookupId(item.get()).bare);
+		if (const auto edited = item->Get<HistoryMessageEdited>()) {
+			HashUpdate(hash, edited->date);
+		} else {
+			HashUpdate(hash, TimeId(0));
+		}
+	}
+	return HashFinalize(hash);
+}
+
+MTPInputQuickReplyShortcut ShortcutIdToMTP(
+		not_null<Main::Session*> session,
+		BusinessShortcutId id) {
+	if (id >= 0) {
+		return MTP_inputQuickReplyShortcutId(MTP_int(id));
+	}
+	return MTP_inputQuickReplyShortcut(MTP_string(
+		session->data().shortcutMessages().lookupShortcut(id).name));
+}
+
+} // namespace Data
diff --git a/Telegram/SourceFiles/data/business/data_shortcut_messages.h b/Telegram/SourceFiles/data/business/data_shortcut_messages.h
new file mode 100644
index 000000000..2028f2ece
--- /dev/null
+++ b/Telegram/SourceFiles/data/business/data_shortcut_messages.h
@@ -0,0 +1,125 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "history/history_item.h"
+#include "base/timer.h"
+
+class History;
+
+namespace Main {
+class Session;
+} // namespace Main
+
+namespace Data {
+
+class Session;
+struct MessagesSlice;
+
+struct Shortcut {
+	QString name;
+	MsgId topMessageId = 0;
+	int count = 0;
+
+	friend inline bool operator==(
+		const Shortcut &a,
+		const Shortcut &b) = default;
+};
+
+struct Shortcuts {
+	base::flat_map<BusinessShortcutId, Shortcut> list;
+
+	friend inline bool operator==(
+		const Shortcuts &a,
+		const Shortcuts &b) = default;
+};
+
+[[nodiscard]] bool IsShortcutMsgId(MsgId id);
+
+class ShortcutMessages final {
+public:
+	explicit ShortcutMessages(not_null<Session*> owner);
+	~ShortcutMessages();
+
+	[[nodiscard]] MsgId lookupId(not_null<const HistoryItem*> item) const;
+	[[nodiscard]] int count(BusinessShortcutId shortcutId) const;
+	[[nodiscard]] MsgId localMessageId(MsgId remoteId) const;
+
+	void apply(const MTPDupdateQuickReplyMessage &update);
+	void apply(const MTPDupdateDeleteQuickReplyMessages &update);
+	void apply(const MTPDupdateDeleteQuickReply &update);
+	void apply(
+		const MTPDupdateMessageID &update,
+		not_null<HistoryItem*> local);
+
+	void appendSending(not_null<HistoryItem*> item);
+	void removeSending(not_null<HistoryItem*> item);
+
+	[[nodiscard]] rpl::producer<> updates(BusinessShortcutId shortcutId);
+	[[nodiscard]] Data::MessagesSlice list(BusinessShortcutId shortcutId);
+
+	void preloadShortcuts();
+	[[nodiscard]] const Shortcuts &shortcuts() const;
+	[[nodiscard]] bool shortcutsLoaded() const;
+	[[nodiscard]] rpl::producer<> shortcutsChanged() const;
+	[[nodiscard]] BusinessShortcutId emplaceShortcut(QString name);
+	[[nodiscard]] Shortcut lookupShortcut(BusinessShortcutId id) const;
+
+private:
+	using OwnedItem = std::unique_ptr<HistoryItem, HistoryItem::Destroyer>;
+	struct List {
+		std::vector<OwnedItem> items;
+		base::flat_map<MsgId, not_null<HistoryItem*>> itemById;
+	};
+	struct Request {
+		mtpRequestId requestId = 0;
+		crl::time lastReceived = 0;
+	};
+
+	void request(BusinessShortcutId shortcutId);
+	void parse(
+		BusinessShortcutId shortcutId,
+		const MTPmessages_Messages &list);
+	HistoryItem *append(
+		BusinessShortcutId shortcutId,
+		List &list,
+		const MTPMessage &message);
+	void clearNotSending(BusinessShortcutId shortcutId);
+	void updated(
+		BusinessShortcutId shortcutId,
+		const base::flat_set<not_null<HistoryItem*>> &added,
+		const base::flat_set<not_null<HistoryItem*>> &clear);
+	void sort(List &list);
+	void remove(not_null<const HistoryItem*> item);
+	[[nodiscard]] uint64 countListHash(const List &list) const;
+	void clearOldRequests();
+
+	const not_null<Main::Session*> _session;
+	const not_null<History*> _history;
+
+	base::Timer _clearTimer;
+	base::flat_map<BusinessShortcutId, List> _data;
+	base::flat_map<BusinessShortcutId, Request> _requests;
+	rpl::event_stream<BusinessShortcutId> _updates;
+
+	Shortcuts _shortcuts;
+	rpl::event_stream<> _shortcutsChanged;
+	BusinessShortcutId _localShortcutId = 0;
+	uint64 _shortcutsHash = 0;
+	mtpRequestId _shortcutsRequestId = 0;
+	bool _shortcutsLoaded = false;
+
+	rpl::lifetime _lifetime;
+
+};
+
+[[nodiscard]] MTPInputQuickReplyShortcut ShortcutIdToMTP(
+	not_null<Main::Session*> session,
+	BusinessShortcutId id);
+
+} // namespace Data
diff --git a/Telegram/SourceFiles/data/data_msg_id.h b/Telegram/SourceFiles/data/data_msg_id.h
index d2790a288..b3ecb27b0 100644
--- a/Telegram/SourceFiles/data/data_msg_id.h
+++ b/Telegram/SourceFiles/data/data_msg_id.h
@@ -54,6 +54,7 @@ Q_DECLARE_METATYPE(MsgId);
 }
 
 using StoryId = int32;
+using BusinessShortcutId = int32;
 
 struct FullStoryId {
 	PeerId peer = 0;
@@ -77,7 +78,8 @@ constexpr auto ServerMaxStoryId = StoryId(1 << 30);
 constexpr auto StoryMsgIds = int64(ServerMaxStoryId);
 constexpr auto EndStoryMsgId = MsgId(StartStoryMsgId.bare + StoryMsgIds);
 constexpr auto ServerMaxMsgId = MsgId(1LL << 56);
-constexpr auto ScheduledMsgIdsRange = (1LL << 32);
+constexpr auto ScheduledMaxMsgId = MsgId(ServerMaxMsgId + (1LL << 32));
+constexpr auto ShortcutMaxMsgId = MsgId(ScheduledMaxMsgId + (1LL << 32));
 constexpr auto ShowAtUnreadMsgId = MsgId(0);
 
 constexpr auto SpecialMsgIdShift = EndStoryMsgId.bare;
diff --git a/Telegram/SourceFiles/data/data_scheduled_messages.cpp b/Telegram/SourceFiles/data/data_scheduled_messages.cpp
index 41f3cd22f..3fe58f530 100644
--- a/Telegram/SourceFiles/data/data_scheduled_messages.cpp
+++ b/Telegram/SourceFiles/data/data_scheduled_messages.cpp
@@ -96,8 +96,7 @@ constexpr auto kRequestTimeLimit = 60 * crl::time(1000);
 } // namespace
 
 bool IsScheduledMsgId(MsgId id) {
-	return (id > ServerMaxMsgId)
-		&& (id < ServerMaxMsgId + ScheduledMsgIdsRange);
+	return (id > ServerMaxMsgId) && (id < ScheduledMaxMsgId);
 }
 
 ScheduledMessages::ScheduledMessages(not_null<Session*> owner)
diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp
index 37fb68e86..24ea2cd46 100644
--- a/Telegram/SourceFiles/data/data_session.cpp
+++ b/Telegram/SourceFiles/data/data_session.cpp
@@ -39,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "lang/lang_keys.h" // tr::lng_deleted(tr::now) in user name
 #include "data/business/data_business_chatbots.h"
 #include "data/business/data_business_info.h"
+#include "data/business/data_shortcut_messages.h"
 #include "data/stickers/data_stickers.h"
 #include "data/notify/data_notify_settings.h"
 #include "data/data_bot_app.h"
@@ -256,14 +257,12 @@ Session::Session(not_null<Main::Session*> session)
 , _watchForOfflineTimer([=] { checkLocalUsersWentOffline(); })
 , _groups(this)
 , _chatsFilters(std::make_unique<ChatFilters>(this))
-, _scheduledMessages(std::make_unique<ScheduledMessages>(this))
 , _cloudThemes(std::make_unique<CloudThemes>(session))
 , _sendActionManager(std::make_unique<SendActionManager>())
 , _streaming(std::make_unique<Streaming>(this))
 , _mediaRotation(std::make_unique<MediaRotation>())
 , _histories(std::make_unique<Histories>(this))
 , _stickers(std::make_unique<Stickers>(this))
-, _sponsoredMessages(std::make_unique<SponsoredMessages>(this))
 , _reactions(std::make_unique<Reactions>(this))
 , _emojiStatuses(std::make_unique<EmojiStatuses>(this))
 , _forumIcons(std::make_unique<ForumIcons>(this))
@@ -272,7 +271,10 @@ Session::Session(not_null<Main::Session*> session)
 , _stories(std::make_unique<Stories>(this))
 , _savedMessages(std::make_unique<SavedMessages>(this))
 , _chatbots(std::make_unique<Chatbots>(this))
-, _businessInfo(std::make_unique<BusinessInfo>(this)) {
+, _businessInfo(std::make_unique<BusinessInfo>(this))
+, _scheduledMessages(std::make_unique<ScheduledMessages>(this))
+, _shortcutMessages(std::make_unique<ShortcutMessages>(this))
+, _sponsoredMessages(std::make_unique<SponsoredMessages>(this)) {
 	_cache->open(_session->local().cacheKey());
 	_bigFileCache->open(_session->local().cacheBigFileKey());
 
@@ -396,6 +398,7 @@ void Session::clear() {
 
 	_histories->unloadAll();
 	_scheduledMessages = nullptr;
+	_shortcutMessages = nullptr;
 	_sponsoredMessages = nullptr;
 	_dependentMessages.clear();
 	base::take(_messages);
diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h
index aab939cb4..74204af26 100644
--- a/Telegram/SourceFiles/data/data_session.h
+++ b/Telegram/SourceFiles/data/data_session.h
@@ -44,6 +44,7 @@ class Folder;
 class LocationPoint;
 class WallPaper;
 class ScheduledMessages;
+class ShortcutMessages;
 class SendActionManager;
 class SponsoredMessages;
 class Reactions;
@@ -102,6 +103,9 @@ public:
 	[[nodiscard]] ScheduledMessages &scheduledMessages() const {
 		return *_scheduledMessages;
 	}
+	[[nodiscard]] ShortcutMessages &shortcutMessages() const {
+		return *_shortcutMessages;
+	}
 	[[nodiscard]] SendActionManager &sendActionManager() const {
 		return *_sendActionManager;
 	}
@@ -1058,14 +1062,12 @@ private:
 
 	Groups _groups;
 	const std::unique_ptr<ChatFilters> _chatsFilters;
-	std::unique_ptr<ScheduledMessages> _scheduledMessages;
 	const std::unique_ptr<CloudThemes> _cloudThemes;
 	const std::unique_ptr<SendActionManager> _sendActionManager;
 	const std::unique_ptr<Streaming> _streaming;
 	const std::unique_ptr<MediaRotation> _mediaRotation;
 	const std::unique_ptr<Histories> _histories;
 	const std::unique_ptr<Stickers> _stickers;
-	std::unique_ptr<SponsoredMessages> _sponsoredMessages;
 	const std::unique_ptr<Reactions> _reactions;
 	const std::unique_ptr<EmojiStatuses> _emojiStatuses;
 	const std::unique_ptr<ForumIcons> _forumIcons;
@@ -1075,8 +1077,11 @@ private:
 	const std::unique_ptr<SavedMessages> _savedMessages;
 	const std::unique_ptr<Chatbots> _chatbots;
 	const std::unique_ptr<BusinessInfo> _businessInfo;
+	std::unique_ptr<ScheduledMessages> _scheduledMessages;
+	std::unique_ptr<ShortcutMessages> _shortcutMessages;
+	std::unique_ptr<SponsoredMessages> _sponsoredMessages;
 
-	MsgId _nonHistoryEntryId = ServerMaxMsgId.bare + ScheduledMsgIdsRange;
+	MsgId _nonHistoryEntryId = ShortcutMaxMsgId;
 
 	rpl::lifetime _lifetime;
 
diff --git a/Telegram/SourceFiles/data/data_sparse_ids.h b/Telegram/SourceFiles/data/data_sparse_ids.h
index 8d5489d70..6b762e2db 100644
--- a/Telegram/SourceFiles/data/data_sparse_ids.h
+++ b/Telegram/SourceFiles/data/data_sparse_ids.h
@@ -27,8 +27,7 @@ using SparseUnsortedIdsSlice = AbstractSparseIds<std::vector<MsgId>>;
 class SparseIdsMergedSlice {
 public:
 	using UniversalMsgId = MsgId;
-	static constexpr MsgId kScheduledTopicId
-		= ServerMaxMsgId + ScheduledMsgIdsRange;
+	static constexpr MsgId kScheduledTopicId = ScheduledMaxMsgId;
 
 	struct Key {
 		Key(
diff --git a/Telegram/SourceFiles/data/data_types.cpp b/Telegram/SourceFiles/data/data_types.cpp
index 997d4282e..ab792cd30 100644
--- a/Telegram/SourceFiles/data/data_types.cpp
+++ b/Telegram/SourceFiles/data/data_types.cpp
@@ -145,6 +145,15 @@ TimeId DateFromMessage(const MTPmessage &message) {
 	});
 }
 
+BusinessShortcutId BusinessShortcutIdFromMessage(
+		const MTPmessage &message) {
+	return message.match([](const MTPDmessage &data) {
+		return data.vquick_reply_shortcut_id().value_or_empty();
+	}, [](const auto &) {
+		return BusinessShortcutId();
+	});
+}
+
 bool GoodStickerDimensions(int width, int height) {
 	// Show all .webp (except very large ones) as stickers,
 	// allow to open them in media viewer to see details.
diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h
index 3b511cba0..176a13f74 100644
--- a/Telegram/SourceFiles/data/data_types.h
+++ b/Telegram/SourceFiles/data/data_types.h
@@ -109,10 +109,13 @@ using FilterId = int32;
 
 using MessageIdsList = std::vector<FullMsgId>;
 
-PeerId PeerFromMessage(const MTPmessage &message);
-MTPDmessage::Flags FlagsFromMessage(const MTPmessage &message);
-MsgId IdFromMessage(const MTPmessage &message);
-TimeId DateFromMessage(const MTPmessage &message);
+[[nodiscard]] PeerId PeerFromMessage(const MTPmessage &message);
+[[nodiscard]] MTPDmessage::Flags FlagsFromMessage(
+	const MTPmessage &message);
+[[nodiscard]] MsgId IdFromMessage(const MTPmessage &message);
+[[nodiscard]] TimeId DateFromMessage(const MTPmessage &message);
+[[nodiscard]] BusinessShortcutId BusinessShortcutIdFromMessage(
+	const MTPmessage &message);
 
 [[nodiscard]] inline MTPint MTP_int(MsgId id) noexcept {
 	return MTP_int(id.bare);
diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h
index 0525e5c12..febc8d67c 100644
--- a/Telegram/SourceFiles/history/history_item.h
+++ b/Telegram/SourceFiles/history/history_item.h
@@ -210,6 +210,12 @@ public:
 	[[nodiscard]] bool isSponsored() const;
 	[[nodiscard]] bool skipNotification() const;
 	[[nodiscard]] bool isUserpicSuggestion() const;
+	[[nodiscard]] BusinessShortcutId shortcutId() const {
+		return _shortcutId;
+	}
+	[[nodiscard]] bool isBusinessShortcut() const {
+		return _shortcutId != 0;
+	}
 
 	void addLogEntryOriginal(
 		WebPageId localId,
@@ -662,6 +668,7 @@ private:
 	TimeId _date = 0;
 	TimeId _ttlDestroyAt = 0;
 	int _boostsApplied = 0;
+	BusinessShortcutId _shortcutId = 0;
 
 	HistoryView::Element *_mainView = nullptr;
 	MessageGroupId _groupId = MessageGroupId();
diff --git a/Telegram/SourceFiles/info/info_content_widget.cpp b/Telegram/SourceFiles/info/info_content_widget.cpp
index 8cf7a38d5..ab5a0ef5e 100644
--- a/Telegram/SourceFiles/info/info_content_widget.cpp
+++ b/Telegram/SourceFiles/info/info_content_widget.cpp
@@ -203,6 +203,13 @@ void ContentWidget::applyAdditionalScroll(int additionalScroll) {
 	}
 }
 
+void ContentWidget::applyMaxVisibleHeight(int maxVisibleHeight) {
+	if (_maxVisibleHeight != maxVisibleHeight) {
+		_maxVisibleHeight = maxVisibleHeight;
+		update();
+	}
+}
+
 rpl::producer<int> ContentWidget::desiredHeightValue() const {
 	using namespace rpl::mappers;
 	return rpl::combine(
@@ -328,6 +335,10 @@ rpl::producer<bool> ContentWidget::desiredBottomShadowVisibility() const {
 	});
 }
 
+not_null<Ui::ScrollArea*> ContentWidget::scroll() const {
+	return _scroll.data();
+}
+
 Key ContentMemento::key() const {
 	if (const auto topic = this->topic()) {
 		return Key(topic);
diff --git a/Telegram/SourceFiles/info/info_content_widget.h b/Telegram/SourceFiles/info/info_content_widget.h
index a7782d4f9..b8d1ebe4b 100644
--- a/Telegram/SourceFiles/info/info_content_widget.h
+++ b/Telegram/SourceFiles/info/info_content_widget.h
@@ -81,6 +81,7 @@ public:
 		const QRect &newGeometry,
 		int topDelta);
 	void applyAdditionalScroll(int additionalScroll);
+	void applyMaxVisibleHeight(int maxVisibleHeight);
 	int scrollTillBottom(int forHeight) const;
 	[[nodiscard]] rpl::producer<int> scrollTillBottomChanges() const;
 	[[nodiscard]] virtual const Ui::RoundRect *bottomSkipRounding() const {
@@ -115,9 +116,13 @@ protected:
 			doSetInnerWidget(std::move(inner)));
 	}
 
-	not_null<Controller*> controller() const {
+	[[nodiscard]] not_null<Controller*> controller() const {
 		return _controller;
 	}
+	[[nodiscard]] not_null<Ui::ScrollArea*> scroll() const;
+	[[nodiscard]] int maxVisibleHeight() const {
+		return _maxVisibleHeight;
+	}
 
 	void resizeEvent(QResizeEvent *e) override;
 	void paintEvent(QPaintEvent *e) override;
@@ -151,6 +156,7 @@ private:
 	base::unique_qptr<Ui::RpWidget> _searchWrap = nullptr;
 	QPointer<Ui::InputField> _searchField;
 	int _innerDesiredHeight = 0;
+	int _maxVisibleHeight = 0;
 	bool _isStackBottom = false;
 
 	// Saving here topDelta in setGeometryWithTopMoved() to get it passed to resizeEvent().
diff --git a/Telegram/SourceFiles/info/info_layer_widget.cpp b/Telegram/SourceFiles/info/info_layer_widget.cpp
index cbf5117c3..b4fdd2414 100644
--- a/Telegram/SourceFiles/info/info_layer_widget.cpp
+++ b/Telegram/SourceFiles/info/info_layer_widget.cpp
@@ -291,9 +291,10 @@ QRect LayerWidget::countGeometry(int newWidth) {
 	const auto newBottom = newTop;
 
 	const auto bottomRadius = st::boxRadius;
+	const auto maxVisibleHeight = windowHeight - newTop;
 	// Top rounding is included in _contentHeight.
 	auto desiredHeight = _contentHeight + bottomRadius;
-	accumulate_min(desiredHeight, windowHeight - newTop - newBottom);
+	accumulate_min(desiredHeight, maxVisibleHeight - newBottom);
 
 	// First resize content to new width and get the new desired height.
 	const auto contentLeft = 0;
@@ -308,7 +309,7 @@ QRect LayerWidget::countGeometry(int newWidth) {
 
 	desiredHeight += additionalScroll;
 	contentHeight += additionalScroll;
-	_tillBottom = (newTop + desiredHeight >= windowHeight);
+	_tillBottom = (desiredHeight >= maxVisibleHeight);
 	if (_tillBottom) {
 		additionalScroll += contentBottom;
 	}
@@ -321,7 +322,7 @@ QRect LayerWidget::countGeometry(int newWidth) {
 		contentTop,
 		contentWidth,
 		contentHeight,
-	}, expanding, additionalScroll);
+	}, expanding, additionalScroll, maxVisibleHeight);
 
 	return QRect(newLeft, newTop, newWidth, desiredHeight);
 }
diff --git a/Telegram/SourceFiles/info/info_section_widget.cpp b/Telegram/SourceFiles/info/info_section_widget.cpp
index 084b2f223..a70131428 100644
--- a/Telegram/SourceFiles/info/info_section_widget.cpp
+++ b/Telegram/SourceFiles/info/info_section_widget.cpp
@@ -54,7 +54,11 @@ void SectionWidget::init() {
 		const auto full = !_content->scrollBottomSkip();
 		const auto height = size.height() - (full ? 0 : st::boxRadius);
 		const auto wrapGeometry = QRect{ 0, 0, size.width(), height };
-		_content->updateGeometry(wrapGeometry, expanding, additionalScroll);
+		_content->updateGeometry(
+			wrapGeometry,
+			expanding,
+			additionalScroll,
+			size.height());
 	}, lifetime());
 
 	_connecting = std::make_unique<Window::ConnectionState>(
diff --git a/Telegram/SourceFiles/info/info_wrap_widget.cpp b/Telegram/SourceFiles/info/info_wrap_widget.cpp
index 32bd69c7a..bf03f7131 100644
--- a/Telegram/SourceFiles/info/info_wrap_widget.cpp
+++ b/Telegram/SourceFiles/info/info_wrap_widget.cpp
@@ -934,13 +934,17 @@ object_ptr<Ui::RpWidget> WrapWidget::createTopBarSurrogate(
 void WrapWidget::updateGeometry(
 		QRect newGeometry,
 		bool expanding,
-		int additionalScroll) {
+		int additionalScroll,
+		int maxVisibleHeight) {
 	auto scrollChanged = (_additionalScroll != additionalScroll);
 	auto geometryChanged = (geometry() != newGeometry);
 	auto shrinkingContent = (additionalScroll < _additionalScroll);
 	_additionalScroll = additionalScroll;
+	_maxVisibleHeight = maxVisibleHeight;
 	_expanding = expanding;
 
+	_content->applyMaxVisibleHeight(maxVisibleHeight);
+
 	if (geometryChanged) {
 		if (shrinkingContent) {
 			setGeometry(newGeometry);
diff --git a/Telegram/SourceFiles/info/info_wrap_widget.h b/Telegram/SourceFiles/info/info_wrap_widget.h
index d16108114..f102cc834 100644
--- a/Telegram/SourceFiles/info/info_wrap_widget.h
+++ b/Telegram/SourceFiles/info/info_wrap_widget.h
@@ -124,7 +124,8 @@ public:
 	void updateGeometry(
 		QRect newGeometry,
 		bool expanding,
-		int additionalScroll);
+		int additionalScroll,
+		int maxVisibleHeight);
 	[[nodiscard]] int scrollBottomSkip() const;
 	[[nodiscard]] int scrollTillBottom(int forHeight) const;
 	[[nodiscard]] rpl::producer<int> scrollTillBottomChanges() const;
@@ -207,6 +208,7 @@ private:
 	std::unique_ptr<Controller> _controller;
 	object_ptr<ContentWidget> _content = { nullptr };
 	int _additionalScroll = 0;
+	int _maxVisibleHeight = 0;
 	bool _expanding = false;
 	rpl::variable<bool> _grabbingForExpanding = false;
 	object_ptr<TopBar> _topBar = { nullptr };
diff --git a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp
index cd6d6bd03..2cddcf63e 100644
--- a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp
+++ b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp
@@ -44,7 +44,10 @@ Widget::Widget(
 , _self(controller->key().settingsSelf())
 , _type(controller->section().settingsType())
 , _inner([&] {
-	auto inner = _type->create(this, controller->parentController());
+	auto inner = _type->create(
+		this,
+		controller->parentController(),
+		scroll());
 	if (inner->hasFlexibleTopBar()) {
 		auto filler = setInnerWidget(object_ptr<Ui::RpWidget>(this));
 		filler->resize(1, 1);
@@ -229,6 +232,12 @@ rpl::producer<QString> Widget::title() {
 	return _inner->title();
 }
 
+void Widget::paintEvent(QPaintEvent *e) {
+	if (!_inner->paintOuter(this, maxVisibleHeight(), e->rect())) {
+		ContentWidget::paintEvent(e);
+	}
+}
+
 std::shared_ptr<ContentMemento> Widget::doCreateMemento() {
 	auto result = std::make_shared<Memento>(self(), _type);
 	saveState(result.get());
diff --git a/Telegram/SourceFiles/info/settings/info_settings_widget.h b/Telegram/SourceFiles/info/settings/info_settings_widget.h
index e6715d68c..d2eb63615 100644
--- a/Telegram/SourceFiles/info/settings/info_settings_widget.h
+++ b/Telegram/SourceFiles/info/settings/info_settings_widget.h
@@ -84,6 +84,8 @@ private:
 	void saveState(not_null<Memento*> memento);
 	void restoreState(not_null<Memento*> memento);
 
+	void paintEvent(QPaintEvent *e) override;
+
 	std::shared_ptr<ContentMemento> doCreateMemento() override;
 
 	not_null<UserData*> _self;
diff --git a/Telegram/SourceFiles/main/main_session.cpp b/Telegram/SourceFiles/main/main_session.cpp
index 9a3722eae..2ba11542e 100644
--- a/Telegram/SourceFiles/main/main_session.cpp
+++ b/Telegram/SourceFiles/main/main_session.cpp
@@ -79,7 +79,8 @@ Session::Session(
 	not_null<Account*> account,
 	const MTPUser &user,
 	std::unique_ptr<SessionSettings> settings)
-: _account(account)
+: _userId(user.c_user().vid())
+, _account(account)
 , _settings(std::move(settings))
 , _changes(std::make_unique<Data::Changes>(this))
 , _api(std::make_unique<ApiWrap>(this))
@@ -89,7 +90,6 @@ Session::Session(
 , _uploader(std::make_unique<Storage::Uploader>(_api.get()))
 , _storage(std::make_unique<Storage::Facade>())
 , _data(std::make_unique<Data::Session>(this))
-, _userId(user.c_user().vid())
 , _user(_data->processUser(user))
 , _emojiStickersPack(std::make_unique<Stickers::EmojiPack>(this))
 , _diceStickersPacks(std::make_unique<Stickers::DicePacks>(this))
diff --git a/Telegram/SourceFiles/main/main_session.h b/Telegram/SourceFiles/main/main_session.h
index cca061d08..635f453d5 100644
--- a/Telegram/SourceFiles/main/main_session.h
+++ b/Telegram/SourceFiles/main/main_session.h
@@ -199,6 +199,7 @@ private:
 
 	void parseColorIndices(const MTPDhelp_peerColors &data);
 
+	const UserId _userId;
 	const not_null<Account*> _account;
 
 	const std::unique_ptr<SessionSettings> _settings;
@@ -212,7 +213,6 @@ private:
 
 	// _data depends on _downloader / _uploader.
 	const std::unique_ptr<Data::Session> _data;
-	const UserId _userId;
 	const not_null<UserData*> _user;
 
 	// _emojiStickersPack depends on _data.
diff --git a/Telegram/SourceFiles/media/stories/media_stories_share.cpp b/Telegram/SourceFiles/media/stories/media_stories_share.cpp
index 05fceb77b..76e353c00 100644
--- a/Telegram/SourceFiles/media/stories/media_stories_share.cpp
+++ b/Telegram/SourceFiles/media/stories/media_stories_share.cpp
@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "base/random.h"
 #include "boxes/share_box.h"
 #include "chat_helpers/compose/compose_show.h"
+#include "data/business/data_shortcut_messages.h"
 #include "data/data_chat_participant_status.h"
 #include "data/data_forum_topic.h"
 #include "data/data_histories.h"
@@ -119,6 +120,7 @@ namespace Media::Stories {
 				message.action.clearDraft = false;
 				api->sendMessage(std::move(message));
 			}
+			const auto session = &thread->session();
 			const auto threadPeer = thread->peer();
 			const auto threadHistory = thread->owningHistory();
 			const auto randomId = base::RandomValue<uint64>();
@@ -132,6 +134,12 @@ namespace Media::Stories {
 			if (silentPost) {
 				sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
 			}
+			if (options.scheduled) {
+				sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
+			}
+			if (options.shortcutId) {
+				sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
+			}
 			const auto done = [=] {
 				if (!--state->requests) {
 					if (show->valid()) {
@@ -155,7 +163,7 @@ namespace Media::Stories {
 					MTPVector<MTPMessageEntity>(),
 					MTP_int(action.options.scheduled),
 					MTP_inputPeerEmpty(),
-					MTPInputQuickReplyShortcut()
+					Data::ShortcutIdToMTP(session, action.options.shortcutId)
 				), [=](
 						const MTPUpdates &result,
 						const MTP::Response &response) {
diff --git a/Telegram/SourceFiles/settings/business/settings_away_message.cpp b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
index b38e4e184..6c820afa9 100644
--- a/Telegram/SourceFiles/settings/business/settings_away_message.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
@@ -10,10 +10,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "base/unixtime.h"
 #include "core/application.h"
 #include "data/business/data_business_info.h"
+#include "data/business/data_shortcut_messages.h"
 #include "data/data_session.h"
 #include "lang/lang_keys.h"
 #include "main/main_session.h"
 #include "settings/business/settings_recipients_helper.h"
+#include "settings/business/settings_shortcut_messages.h"
 #include "ui/boxes/choose_date_time.h"
 #include "ui/text/text_utilities.h"
 #include "ui/widgets/buttons.h"
@@ -37,10 +39,13 @@ public:
 
 	[[nodiscard]] rpl::producer<QString> title() override;
 
+	[[nodiscard]] rpl::producer<Type> sectionShowOther() override;
+
 private:
 	void setupContent(not_null<Window::SessionController*> controller);
 	void save();
 
+	rpl::event_stream<Type> _showOther;
 	rpl::variable<Data::BusinessRecipients> _recipients;
 	rpl::variable<Data::AwaySchedule> _schedule;
 	rpl::variable<bool> _enabled;
@@ -197,6 +202,10 @@ rpl::producer<QString> AwayMessage::title() {
 	return tr::lng_away_title();
 }
 
+rpl::producer<Type> AwayMessage::sectionShowOther() {
+	return _showOther.events();
+}
+
 void AwayMessage::setupContent(
 		not_null<Window::SessionController*> controller) {
 	using namespace Data;
@@ -258,7 +267,9 @@ void AwayMessage::setupContent(
 		st::settingsButtonLightNoIcon
 	));
 	create->setClickedCallback([=] {
-
+		const auto owner = &controller->session().data();
+		const auto id = owner->shortcutMessages().emplaceShortcut("away");
+		_showOther.fire(ShortcutMessagesId(id));
 	});
 	Ui::AddSkip(createInner);
 	Ui::AddDivider(createInner);
diff --git a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
index 5500ca539..18d9c9545 100644
--- a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
@@ -6,7 +6,7 @@ For license and copyright information please follow this link:
 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "settings/business/settings_chatbots.h"
-//
+
 #include "core/application.h"
 #include "data/business/data_business_chatbots.h"
 #include "data/data_session.h"
diff --git a/Telegram/SourceFiles/settings/business/settings_greeting.cpp b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
index 39520846f..7e5ad6b1e 100644
--- a/Telegram/SourceFiles/settings/business/settings_greeting.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
@@ -10,9 +10,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "base/event_filter.h"
 #include "core/application.h"
 #include "data/business/data_business_info.h"
+#include "data/business/data_shortcut_messages.h"
 #include "data/data_session.h"
 #include "lang/lang_keys.h"
 #include "main/main_session.h"
+#include "settings/business/settings_shortcut_messages.h"
 #include "settings/business/settings_recipients_helper.h"
 #include "ui/layers/generic_box.h"
 #include "ui/text/text_utilities.h"
@@ -50,6 +52,7 @@ private:
 
 	Ui::RoundRect _bottomSkipRounding;
 
+	rpl::event_stream<Type> _showOther;
 	rpl::variable<Data::BusinessRecipients> _recipients;
 	rpl::variable<int> _noActivityDays;
 	rpl::variable<bool> _enabled;
@@ -229,6 +232,28 @@ void Greeting::setupContent(
 			object_ptr<Ui::VerticalLayout>(content)));
 	const auto inner = wrap->entity();
 
+	const auto createWrap = inner->add(
+		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
+			inner,
+			object_ptr<Ui::VerticalLayout>(inner)));
+	const auto createInner = createWrap->entity();
+	Ui::AddSkip(createInner);
+	const auto create = createInner->add(object_ptr<Ui::SettingsButton>(
+		createInner,
+		tr::lng_greeting_create(),
+		st::settingsButtonLightNoIcon
+	));
+	create->setClickedCallback([=] {
+		const auto owner = &controller->session().data();
+		const auto id = owner->shortcutMessages().emplaceShortcut("hello");
+		_showOther.fire(ShortcutMessagesId(id));
+	});
+	Ui::AddSkip(createInner);
+	Ui::AddDivider(createInner);
+
+	createWrap->toggleOn(rpl::single(true));
+
+	Ui::AddSkip(inner);
 	AddBusinessRecipientsSelector(inner, {
 		.controller = controller,
 		.title = tr::lng_greeting_recipients(),
diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
new file mode 100644
index 000000000..290aa222c
--- /dev/null
+++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
@@ -0,0 +1,1183 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "settings/business/settings_shortcut_messages.h"
+
+#include "api/api_editing.h"
+#include "api/api_sending.h"
+#include "apiwrap.h"
+#include "base/call_delayed.h"
+#include "boxes/delete_messages_box.h"
+#include "boxes/premium_limits_box.h"
+#include "boxes/send_files_box.h"
+#include "chat_helpers/tabbed_selector.h"
+#include "core/file_utilities.h"
+#include "core/mime_type.h"
+#include "data/data_message_reaction_id.h"
+#include "data/data_premium_limits.h"
+#include "data/data_session.h"
+#include "data/data_user.h"
+#include "history/view/controls/compose_controls_common.h"
+#include "history/view/controls/history_view_compose_controls.h"
+#include "history/view/history_view_corner_buttons.h"
+#include "history/view/history_view_empty_list_bubble.h"
+#include "history/view/history_view_list_widget.h"
+#include "history/view/history_view_sticker_toast.h"
+#include "history/history.h"
+#include "history/history_item.h"
+#include "inline_bots/inline_bot_result.h"
+#include "lang/lang_keys.h"
+#include "main/main_session.h"
+#include "menu/menu_send.h"
+#include "settings/business/settings_recipients_helper.h"
+#include "storage/localimageloader.h"
+#include "storage/storage_account.h"
+#include "storage/storage_media_prepare.h"
+#include "ui/chat/attach/attach_send_files_way.h"
+#include "ui/chat/chat_style.h"
+#include "ui/chat/chat_theme.h"
+#include "ui/controls/jump_down_button.h"
+#include "ui/text/format_values.h"
+#include "ui/text/text_utilities.h"
+#include "ui/widgets/scroll_area.h"
+#include "window/themes/window_theme.h"
+#include "window/section_widget.h"
+#include "window/window_session_controller.h"
+#include "styles/style_boxes.h"
+#include "styles/style_chat_helpers.h"
+#include "styles/style_chat.h"
+
+namespace Settings {
+namespace {
+
+using namespace HistoryView;
+
+class ShortcutMessages
+	: public AbstractSection
+	, private ListDelegate
+	, private CornerButtonsDelegate {
+public:
+	ShortcutMessages(
+		QWidget *parent,
+		not_null<Window::SessionController*> controller,
+		not_null<Ui::ScrollArea*> scroll,
+		BusinessShortcutId shortcutId);
+	~ShortcutMessages();
+
+	[[nodiscard]] static Type Id(BusinessShortcutId shortcutId);
+
+	[[nodiscard]] Type id() const final override {
+		return Id(_shortcutId);
+	}
+
+	[[nodiscard]] rpl::producer<QString> title() override;
+
+	bool paintOuter(
+		not_null<QWidget*> outer,
+		int maxVisibleHeight,
+		QRect clip) override;
+
+private:
+	// ListDelegate interface.
+	Context listContext() override;
+	bool listScrollTo(int top, bool syntetic = true) override;
+	void listCancelRequest() override;
+	void listDeleteRequest() override;
+	void listTryProcessKeyInput(not_null<QKeyEvent*> e) override;
+	rpl::producer<Data::MessagesSlice> listSource(
+		Data::MessagePosition aroundId,
+		int limitBefore,
+		int limitAfter) override;
+	bool listAllowsMultiSelect() override;
+	bool listIsItemGoodForSelection(not_null<HistoryItem*> item) override;
+	bool listIsLessInOrder(
+		not_null<HistoryItem*> first,
+		not_null<HistoryItem*> second) override;
+	void listSelectionChanged(SelectedItems &&items) override;
+	void listMarkReadTill(not_null<HistoryItem*> item) override;
+	void listMarkContentsRead(
+		const base::flat_set<not_null<HistoryItem*>> &items) override;
+	MessagesBarData listMessagesBar(
+		const std::vector<not_null<Element*>> &elements) override;
+	void listContentRefreshed() override;
+	void listUpdateDateLink(
+		ClickHandlerPtr &link,
+		not_null<Element*> view) override;
+	bool listElementHideReply(not_null<const Element*> view) override;
+	bool listElementShownUnread(not_null<const Element*> view) override;
+	bool listIsGoodForAroundPosition(
+		not_null<const Element *> view) override;
+	void listSendBotCommand(
+		const QString &command,
+		const FullMsgId &context) override;
+	void listSearch(
+		const QString &query,
+		const FullMsgId &context) override;
+	void listHandleViaClick(not_null<UserData*> bot) override;
+	not_null<Ui::ChatTheme*> listChatTheme() override;
+	CopyRestrictionType listCopyRestrictionType(HistoryItem *item) override;
+	CopyRestrictionType listCopyMediaRestrictionType(
+		not_null<HistoryItem*> item) override;
+	CopyRestrictionType listSelectRestrictionType() override;
+	auto listAllowedReactionsValue()
+		-> rpl::producer<Data::AllowedReactions> override;
+	void listShowPremiumToast(not_null<DocumentData*> document) override;
+	void listOpenPhoto(
+		not_null<PhotoData*> photo,
+		FullMsgId context) override;
+	void listOpenDocument(
+		not_null<DocumentData*> document,
+		FullMsgId context,
+		bool showInMediaView) override;
+	void listPaintEmpty(
+		Painter &p,
+		const Ui::ChatPaintContext &context) override;
+	QString listElementAuthorRank(not_null<const Element*> view) override;
+	History *listTranslateHistory() override;
+	void listAddTranslatedItems(
+		not_null<TranslateTracker*> tracker) override;
+
+	// CornerButtonsDelegate delegate.
+	void cornerButtonsShowAtPosition(
+		Data::MessagePosition position) override;
+	Data::Thread *cornerButtonsThread() override;
+	FullMsgId cornerButtonsCurrentId() override;
+	bool cornerButtonsIgnoreVisibility() override;
+	std::optional<bool> cornerButtonsDownShown() override;
+	bool cornerButtonsUnreadMayBeShown() override;
+	bool cornerButtonsHas(CornerButtonType type) override;
+
+	QPointer<Ui::RpWidget> createPinnedToBottom(
+		not_null<Ui::RpWidget*> parent) override;
+	void setupComposeControls();
+
+
+	void uploadFile(const QByteArray &fileContent, SendMediaType type);
+	bool confirmSendingFiles(
+		QImage &&image,
+		QByteArray &&content,
+		std::optional<bool> overrideSendImagesAsPhotos = std::nullopt,
+		const QString &insertTextOnCancel = QString());
+	bool confirmSendingFiles(
+		const QStringList &files,
+		const QString &insertTextOnCancel);
+	bool confirmSendingFiles(
+		Ui::PreparedList &&list,
+		const QString &insertTextOnCancel = QString());
+	bool confirmSendingFiles(
+		not_null<const QMimeData*> data,
+		std::optional<bool> overrideSendImagesAsPhotos,
+		const QString &insertTextOnCancel = QString());
+	bool showSendingFilesError(const Ui::PreparedList &list) const;
+	bool showSendingFilesError(
+		const Ui::PreparedList &list,
+		std::optional<bool> compress) const;
+	void sendingFilesConfirmed(
+		Ui::PreparedList &&list,
+		Ui::SendFilesWay way,
+		TextWithTags &&caption,
+		Api::SendOptions options,
+		bool ctrlShiftEnter);
+
+	void sendExistingDocument(not_null<DocumentData*> document);
+	bool sendExistingDocument(
+		not_null<DocumentData*> document,
+		Api::SendOptions options,
+		std::optional<MsgId> localId);
+	void sendExistingPhoto(not_null<PhotoData*> photo);
+	bool sendExistingPhoto(
+		not_null<PhotoData*> photo,
+		Api::SendOptions options);
+	void sendInlineResult(
+		not_null<InlineBots::Result*> result,
+		not_null<UserData*> bot);
+	void sendInlineResult(
+		not_null<InlineBots::Result*> result,
+		not_null<UserData*> bot,
+		Api::SendOptions options,
+		std::optional<MsgId> localMessageId);
+
+	[[nodiscard]] Api::SendAction prepareSendAction(
+		Api::SendOptions options) const;
+	void send();
+	void send(Api::SendOptions options);
+	void sendVoice(Controls::VoiceToSend &&data);
+	void edit(
+		not_null<HistoryItem*> item,
+		Api::SendOptions options,
+		mtpRequestId *const saveEditMsgRequestId);
+	void chooseAttach(std::optional<bool> overrideSendImagesAsPhotos);
+	[[nodiscard]] SendMenu::Type sendMenuType() const;
+	[[nodiscard]] FullReplyTo replyTo() const;
+	void doSetInnerFocus();
+	void showAtPosition(
+		Data::MessagePosition position,
+		FullMsgId originItemId = {});
+	void showAtPosition(
+		Data::MessagePosition position,
+		FullMsgId originItemId,
+		const Window::SectionShow &params);
+	void showAtEnd();
+	void finishSending();
+
+	const not_null<Window::SessionController*> _controller;
+	const not_null<Main::Session*> _session;
+	const not_null<Ui::ScrollArea*> _scroll;
+	const BusinessShortcutId _shortcutId;
+	const not_null<History*> _history;
+	std::shared_ptr<Ui::ChatStyle> _style;
+	std::shared_ptr<Ui::ChatTheme> _theme;
+	QPointer<ListWidget> _inner;
+	std::unique_ptr<Ui::RpWidget> _controlsWrap;
+	std::unique_ptr<ComposeControls> _composeControls;
+	bool _skipScrollEvent = false;
+
+	std::unique_ptr<StickerToast> _stickerToast;
+
+	FullMsgId _lastShownAt;
+	CornerButtons _cornerButtons;
+
+	Data::MessagesSlice _lastSlice;
+	bool _choosingAttach = false;
+
+};
+
+struct Factory : AbstractSectionFactory {
+	explicit Factory(BusinessShortcutId shortcutId)
+		: shortcutId(shortcutId) {
+	}
+
+	object_ptr<AbstractSection> create(
+		not_null<QWidget*> parent,
+		not_null<Window::SessionController*> controller,
+		not_null<Ui::ScrollArea*> scroll
+	) const final override {
+		return object_ptr<ShortcutMessages>(
+			parent,
+			controller,
+			scroll,
+			shortcutId);
+	}
+
+	const BusinessShortcutId shortcutId = {};
+};
+
+ShortcutMessages::ShortcutMessages(
+	QWidget *parent,
+	not_null<Window::SessionController*> controller,
+	not_null<Ui::ScrollArea*> scroll,
+	BusinessShortcutId shortcutId)
+: AbstractSection(parent)
+, _controller(controller)
+, _session(&controller->session())
+, _scroll(scroll)
+, _shortcutId(shortcutId)
+, _history(_session->data().history(_session->user()->id))
+, _cornerButtons(
+		_scroll,
+		controller->chatStyle(),
+		static_cast<HistoryView::CornerButtonsDelegate*>(this)) {
+	controller->chatStyle()->paletteChanged(
+	) | rpl::start_with_next([=] {
+		_scroll->updateBars();
+	}, _scroll->lifetime());
+
+	_style = std::make_shared<Ui::ChatStyle>(_session->colorIndicesValue());
+	_theme = std::shared_ptr<Ui::ChatTheme>(
+		Window::Theme::DefaultChatThemeOn(lifetime()));
+
+	_inner = Ui::CreateChild<ListWidget>(
+		this,
+		controller,
+		static_cast<ListDelegate*>(this));
+	//_scroll->scrolls(
+	//) | rpl::start_with_next([=] {
+	//	onScroll();
+	//}, lifetime());
+
+	_inner->editMessageRequested(
+	) | rpl::start_with_next([=](auto fullId) {
+		if (const auto item = _session->data().message(fullId)) {
+			const auto media = item->media();
+			if (!media || media->webpage() || media->allowsEditCaption()) {
+				_composeControls->editMessage(fullId);
+			}
+		}
+	}, _inner->lifetime());
+
+	{
+		auto emptyInfo = base::make_unique_q<EmptyListBubbleWidget>(
+			_inner,
+			controller->chatStyle(),
+			st::msgServicePadding);
+		const auto emptyText = Ui::Text::Semibold(
+			tr::lng_scheduled_messages_empty(tr::now));
+		emptyInfo->setText(emptyText);
+		_inner->setEmptyInfoWidget(std::move(emptyInfo));
+	}
+
+	widthValue() | rpl::start_with_next([=](int width) {
+		resize(width, width);
+	}, lifetime());
+}
+
+ShortcutMessages::~ShortcutMessages() = default;
+
+Type ShortcutMessages::Id(BusinessShortcutId shortcutId) {
+	return std::make_shared<Factory>(shortcutId);
+}
+
+rpl::producer<QString> ShortcutMessages::title() {
+	return rpl::single(u"Editing messages list"_q);
+}
+
+bool ShortcutMessages::paintOuter(
+		not_null<QWidget*> outer,
+		int maxVisibleHeight,
+		QRect clip) {
+	const auto window = outer->window()->height();
+	Window::SectionWidget::PaintBackground(
+		_theme.get(),
+		outer,
+		std::max(outer->height(), maxVisibleHeight),
+		0,
+		clip);
+	return true;
+}
+
+void ShortcutMessages::setupComposeControls() {
+	_composeControls->setHistory({
+		.history = _history.get(),
+		.writeRestriction = rpl::single(Controls::WriteRestriction()),
+	});
+
+	_composeControls->height(
+	) | rpl::start_with_next([=](int height) {
+		const auto wasMax = (_scroll->scrollTopMax() == _scroll->scrollTop());
+		_controlsWrap->resize(width(), height);
+		if (wasMax) {
+			listScrollTo(_scroll->scrollTopMax());
+		}
+	}, lifetime());
+
+	_composeControls->cancelRequests(
+	) | rpl::start_with_next([=] {
+		listCancelRequest();
+	}, lifetime());
+
+	_composeControls->sendRequests(
+	) | rpl::start_with_next([=] {
+		send();
+	}, lifetime());
+
+	_composeControls->sendVoiceRequests(
+	) | rpl::start_with_next([=](ComposeControls::VoiceToSend &&data) {
+		sendVoice(std::move(data));
+	}, lifetime());
+
+	_composeControls->sendCommandRequests(
+	) | rpl::start_with_next([=](const QString &command) {
+		listSendBotCommand(command, FullMsgId());
+	}, lifetime());
+
+	const auto saveEditMsgRequestId = lifetime().make_state<mtpRequestId>(0);
+	_composeControls->editRequests(
+	) | rpl::start_with_next([=](auto data) {
+		if (const auto item = _session->data().message(data.fullId)) {
+			if (item->isBusinessShortcut()) {
+				edit(item, data.options, saveEditMsgRequestId);
+			}
+		}
+	}, lifetime());
+
+	_composeControls->attachRequests(
+	) | rpl::filter([=] {
+		return !_choosingAttach;
+	}) | rpl::start_with_next([=](std::optional<bool> overrideCompress) {
+		_choosingAttach = true;
+		base::call_delayed(st::historyAttach.ripple.hideDuration, this, [=] {
+			_choosingAttach = false;
+			chooseAttach(overrideCompress);
+		});
+	}, lifetime());
+
+	_composeControls->fileChosen(
+	) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
+		_controller->hideLayer(anim::type::normal);
+		sendExistingDocument(data.document);
+	}, lifetime());
+
+	_composeControls->photoChosen(
+	) | rpl::start_with_next([=](ChatHelpers::PhotoChosen chosen) {
+		sendExistingPhoto(chosen.photo);
+	}, lifetime());
+
+	_composeControls->inlineResultChosen(
+	) | rpl::start_with_next([=](ChatHelpers::InlineChosen chosen) {
+		sendInlineResult(chosen.result, chosen.bot);
+	}, lifetime());
+
+	_composeControls->jumpToItemRequests(
+	) | rpl::start_with_next([=](FullReplyTo to) {
+		if (const auto item = _session->data().message(to.messageId)) {
+			showAtPosition(item->position());
+		}
+	}, lifetime());
+
+	_composeControls->scrollKeyEvents(
+	) | rpl::start_with_next([=](not_null<QKeyEvent*> e) {
+		_scroll->keyPressEvent(e);
+	}, lifetime());
+
+	_composeControls->editLastMessageRequests(
+	) | rpl::start_with_next([=](not_null<QKeyEvent*> e) {
+		if (!_inner->lastMessageEditRequestNotify()) {
+			_scroll->keyPressEvent(e);
+		}
+	}, lifetime());
+
+	_composeControls->setMimeDataHook([=](
+			not_null<const QMimeData*> data,
+			Ui::InputField::MimeAction action) {
+		if (action == Ui::InputField::MimeAction::Check) {
+			return Core::CanSendFiles(data);
+		} else if (action == Ui::InputField::MimeAction::Insert) {
+			//return confirmSendingFiles(
+			//	data,
+			//	std::nullopt,
+			//	Core::ReadMimeText(data));#TODO
+		}
+		Unexpected("action in MimeData hook.");
+	});
+
+	_composeControls->lockShowStarts(
+	) | rpl::start_with_next([=] {
+		_cornerButtons.updateJumpDownVisibility();
+		_cornerButtons.updateUnreadThingsVisibility();
+	}, lifetime());
+
+	_composeControls->viewportEvents(
+	) | rpl::start_with_next([=](not_null<QEvent*> e) {
+		_scroll->viewportEvent(e);
+	}, lifetime());
+
+	_controlsWrap->widthValue() | rpl::start_with_next([=](int width) {
+		_composeControls->resizeToWidth(width);
+	}, _controlsWrap->lifetime());
+	_composeControls->height() | rpl::start_with_next([=](int height) {
+		_controlsWrap->resize(_controlsWrap->width(), height);
+	}, _controlsWrap->lifetime());
+}
+
+QPointer<Ui::RpWidget> ShortcutMessages::createPinnedToBottom(
+		not_null<Ui::RpWidget*> parent) {
+	_controlsWrap = std::make_unique<Ui::RpWidget>(parent);
+	_composeControls = std::make_unique<ComposeControls>(
+		_controlsWrap.get(),
+		_controller,
+		[=](not_null<DocumentData*> emoji) { listShowPremiumToast(emoji); },
+		ComposeControls::Mode::Scheduled,
+		SendMenu::Type::Disabled);
+
+	setupComposeControls();
+
+	return _controlsWrap.get();
+}
+
+Context ShortcutMessages::listContext() {
+	return Context::History;
+}
+
+bool ShortcutMessages::listScrollTo(int top, bool syntetic) {
+	top = std::clamp(top, 0, _scroll->scrollTopMax());
+	if (_scroll->scrollTop() == top) {
+		//updateInnerVisibleArea();
+		return false;
+	}
+	_scroll->scrollToY(top);
+	return true;
+}
+
+void ShortcutMessages::listCancelRequest() {
+	if (_inner && !_inner->getSelectedItems().empty()) {
+		//clearSelected();
+		return;
+	} else if (_composeControls->handleCancelRequest()) {
+		return;
+	}
+	_controller->showBackFromStack();
+}
+
+void ShortcutMessages::listDeleteRequest() {
+	//confirmDeleteSelected();
+}
+
+void ShortcutMessages::listTryProcessKeyInput(not_null<QKeyEvent*> e) {
+	_composeControls->tryProcessKeyInput(e);
+}
+
+rpl::producer<Data::MessagesSlice> ShortcutMessages::listSource(
+		Data::MessagePosition aroundId,
+		int limitBefore,
+		int limitAfter) {
+	const auto data = &_controller->session().data();
+	//return rpl::single(rpl::empty) | rpl::then(
+	//	data->scheduledMessages().updates(_history)
+	//) | rpl::map([=] {
+	//	return data->scheduledMessages().list(_history);
+	//}) | rpl::after_next([=](const Data::MessagesSlice &slice) {
+	//	highlightSingleNewMessage(slice);
+	//});
+	return rpl::never<Data::MessagesSlice>();
+}
+
+bool ShortcutMessages::listAllowsMultiSelect() {
+	return true;
+}
+
+bool ShortcutMessages::listIsItemGoodForSelection(
+		not_null<HistoryItem*> item) {
+	return !item->isSending() && !item->hasFailed();
+}
+
+bool ShortcutMessages::listIsLessInOrder(
+		not_null<HistoryItem*> first,
+		not_null<HistoryItem*> second) {
+	return first->position() < second->position();
+}
+
+void ShortcutMessages::listSelectionChanged(SelectedItems &&items) {
+}
+
+void ShortcutMessages::listMarkReadTill(not_null<HistoryItem*> item) {
+}
+
+void ShortcutMessages::listMarkContentsRead(
+	const base::flat_set<not_null<HistoryItem*>> &items) {
+}
+
+MessagesBarData ShortcutMessages::listMessagesBar(
+		const std::vector<not_null<Element*>> &elements) {
+	return {};
+}
+
+void ShortcutMessages::listContentRefreshed() {
+}
+
+void ShortcutMessages::listUpdateDateLink(
+	ClickHandlerPtr &link,
+	not_null<Element*> view) {
+}
+
+bool ShortcutMessages::listElementHideReply(not_null<const Element*> view) {
+	return false;
+}
+
+bool ShortcutMessages::listElementShownUnread(not_null<const Element*> view) {
+	return true;
+}
+
+bool ShortcutMessages::listIsGoodForAroundPosition(
+		not_null<const Element*> view) {
+	return true;
+}
+
+void ShortcutMessages::listSendBotCommand(
+	const QString &command,
+	const FullMsgId &context) {
+}
+
+void ShortcutMessages::listSearch(
+		const QString &query,
+		const FullMsgId &context) {
+	const auto inChat = _history->peer->isUser()
+		? Dialogs::Key()
+		: Dialogs::Key(_history);
+	_controller->searchMessages(query, inChat);
+}
+
+void ShortcutMessages::listHandleViaClick(not_null<UserData*> bot) {
+	_composeControls->setText({ '@' + bot->username() + ' ' });
+}
+
+not_null<Ui::ChatTheme*> ShortcutMessages::listChatTheme() {
+	return _theme.get();
+}
+
+CopyRestrictionType ShortcutMessages::listCopyRestrictionType(
+		HistoryItem *item) {
+	return CopyRestrictionType::None;
+}
+
+CopyRestrictionType ShortcutMessages::listCopyMediaRestrictionType(
+		not_null<HistoryItem*> item) {
+	if (const auto media = item->media()) {
+		if (const auto invoice = media->invoice()) {
+			if (invoice->extendedMedia) {
+				return CopyMediaRestrictionTypeFor(_history->peer, item);
+			}
+		}
+	}
+	return CopyRestrictionType::None;
+}
+
+CopyRestrictionType ShortcutMessages::listSelectRestrictionType() {
+	return CopyRestrictionType::None;
+}
+
+auto ShortcutMessages::listAllowedReactionsValue()
+-> rpl::producer<Data::AllowedReactions> {
+	return rpl::single(Data::AllowedReactions());
+}
+
+void ShortcutMessages::listShowPremiumToast(
+		not_null<DocumentData*> document) {
+	if (!_stickerToast) {
+		_stickerToast = std::make_unique<HistoryView::StickerToast>(
+			_controller,
+			this,
+			[=] { _stickerToast = nullptr; });
+	}
+	_stickerToast->showFor(document);
+}
+
+void ShortcutMessages::listOpenPhoto(
+		not_null<PhotoData*> photo,
+		FullMsgId context) {
+	_controller->openPhoto(photo, { context });
+}
+
+void ShortcutMessages::listOpenDocument(
+		not_null<DocumentData*> document,
+		FullMsgId context,
+		bool showInMediaView) {
+	_controller->openDocument(document, showInMediaView, { context });
+}
+
+void ShortcutMessages::listPaintEmpty(
+	Painter &p,
+	const Ui::ChatPaintContext &context) {
+}
+
+QString ShortcutMessages::listElementAuthorRank(
+		not_null<const Element*> view) {
+	return {};
+}
+
+History *ShortcutMessages::listTranslateHistory() {
+	return nullptr;
+}
+
+void ShortcutMessages::listAddTranslatedItems(
+	not_null<TranslateTracker*> tracker) {
+}
+
+void ShortcutMessages::cornerButtonsShowAtPosition(
+		Data::MessagePosition position) {
+	//showAtPosition(position);
+}
+
+Data::Thread *ShortcutMessages::cornerButtonsThread() {
+	return _history;
+}
+
+FullMsgId ShortcutMessages::cornerButtonsCurrentId() {
+	return _lastShownAt;
+}
+
+bool ShortcutMessages::cornerButtonsIgnoreVisibility() {
+	return false;// animatingShow();
+}
+
+std::optional<bool> ShortcutMessages::cornerButtonsDownShown() {
+	if (_composeControls->isLockPresent()
+		|| _composeControls->isTTLButtonShown()) {
+		return false;
+	}
+	//const auto top = _scroll->scrollTop() + st::historyToDownShownAfter;
+	//if (top < _scroll->scrollTopMax() || _cornerButtons.replyReturn()) {
+	//	return true;
+	//} else if (_inner->loadedAtBottomKnown()) {
+	//	return !_inner->loadedAtBottom();
+	//}
+	return std::nullopt;
+}
+
+bool ShortcutMessages::cornerButtonsUnreadMayBeShown() {
+	return _inner->loadedAtBottomKnown()
+		&& !_composeControls->isLockPresent()
+		&& !_composeControls->isTTLButtonShown();
+}
+
+bool ShortcutMessages::cornerButtonsHas(CornerButtonType type) {
+	return (type == CornerButtonType::Down);
+}
+
+void ShortcutMessages::uploadFile(
+		const QByteArray &fileContent,
+		SendMediaType type) {
+	// #TODO replies schedule
+	_session->api().sendFile(fileContent, type, prepareSendAction({}));
+}
+
+bool ShortcutMessages::showSendingFilesError(
+		const Ui::PreparedList &list) const {
+	return showSendingFilesError(list, std::nullopt);
+}
+
+bool ShortcutMessages::showSendingFilesError(
+		const Ui::PreparedList &list,
+		std::optional<bool> compress) const {
+	const auto text = [&] {
+		using Error = Ui::PreparedList::Error;
+		switch (list.error) {
+		case Error::None: return QString();
+		case Error::EmptyFile:
+		case Error::Directory:
+		case Error::NonLocalUrl: return tr::lng_send_image_empty(
+			tr::now,
+			lt_name,
+			list.errorData);
+		case Error::TooLargeFile: return u"(toolarge)"_q;
+		}
+		return tr::lng_forward_send_files_cant(tr::now);
+	}();
+	if (text.isEmpty()) {
+		return false;
+	} else if (text == u"(toolarge)"_q) {
+		const auto fileSize = list.files.back().size;
+		_controller->show(
+			Box(FileSizeLimitBox, _session, fileSize, nullptr));
+		return true;
+	}
+
+	_controller->showToast(text);
+	return true;
+}
+
+Api::SendAction ShortcutMessages::prepareSendAction(
+		Api::SendOptions options) const {
+	auto result = Api::SendAction(_history, options);
+	result.replyTo = replyTo();
+	result.options.shortcutId = _shortcutId;
+	result.options.sendAs = _composeControls->sendAsPeer();
+	return result;
+}
+
+void ShortcutMessages::send() {
+	if (_composeControls->getTextWithAppliedMarkdown().text.isEmpty()) {
+		return;
+	}
+	send({});
+	// #TODO replies schedule
+	//const auto callback = [=](Api::SendOptions options) { send(options); };
+	//Ui::show(
+	//	PrepareScheduleBox(this, sendMenuType(), callback),
+	//	Ui::LayerOption::KeepOther);
+}
+
+void ShortcutMessages::sendVoice(ComposeControls::VoiceToSend &&data) {
+	auto action = prepareSendAction(data.options);
+	_session->api().sendVoiceMessage(
+		data.bytes,
+		data.waveform,
+		data.duration,
+		std::move(action));
+
+	_composeControls->cancelReplyMessage();
+	_composeControls->clearListenState();
+	finishSending();
+}
+
+void ShortcutMessages::send(Api::SendOptions options) {
+	_cornerButtons.clearReplyReturns();
+
+	auto message = Api::MessageToSend(prepareSendAction(options));
+	message.textWithTags = _composeControls->getTextWithAppliedMarkdown();
+	message.webPage = _composeControls->webPageDraft();
+
+	_session->api().sendMessage(std::move(message));
+
+	_composeControls->clear();
+
+	finishSending();
+}
+
+void ShortcutMessages::edit(
+		not_null<HistoryItem*> item,
+		Api::SendOptions options,
+		mtpRequestId *const saveEditMsgRequestId) {
+	if (*saveEditMsgRequestId) {
+		return;
+	}
+	const auto webpage = _composeControls->webPageDraft();
+	auto sending = TextWithEntities();
+	auto left = _composeControls->prepareTextForEditMsg();
+
+	const auto originalLeftSize = left.text.size();
+	const auto hasMediaWithCaption = item
+		&& item->media()
+		&& item->media()->allowsEditCaption();
+	const auto maxCaptionSize = !hasMediaWithCaption
+		? MaxMessageSize
+		: Data::PremiumLimits(_session).captionLengthCurrent();
+	if (!TextUtilities::CutPart(sending, left, maxCaptionSize)
+		&& !hasMediaWithCaption) {
+		if (item) {
+			_controller->show(Box<DeleteMessagesBox>(item, false));
+		} else {
+			doSetInnerFocus();
+		}
+		return;
+	} else if (!left.text.isEmpty()) {
+		const auto remove = originalLeftSize - maxCaptionSize;
+		_controller->showToast(
+			tr::lng_edit_limit_reached(tr::now, lt_count, remove));
+		return;
+	}
+
+	lifetime().add([=] {
+		if (!*saveEditMsgRequestId) {
+			return;
+		}
+		_session->api().request(base::take(*saveEditMsgRequestId)).cancel();
+	});
+
+	const auto done = [=](mtpRequestId requestId) {
+		if (requestId == *saveEditMsgRequestId) {
+			*saveEditMsgRequestId = 0;
+			_composeControls->cancelEditMessage();
+		}
+	};
+
+	const auto fail = [=](const QString &error, mtpRequestId requestId) {
+		if (requestId == *saveEditMsgRequestId) {
+			*saveEditMsgRequestId = 0;
+		}
+
+		if (ranges::contains(Api::kDefaultEditMessagesErrors, error)) {
+			_controller->showToast(tr::lng_edit_error(tr::now));
+		} else if (error == u"MESSAGE_NOT_MODIFIED"_q) {
+			_composeControls->cancelEditMessage();
+		} else if (error == u"MESSAGE_EMPTY"_q) {
+			doSetInnerFocus();
+		} else {
+			_controller->showToast(tr::lng_edit_error(tr::now));
+		}
+		update();
+		return true;
+	};
+
+	*saveEditMsgRequestId = Api::EditTextMessage(
+		item,
+		sending,
+		webpage,
+		options,
+		crl::guard(this, done),
+		crl::guard(this, fail));
+
+	_composeControls->hidePanelsAnimated();
+	doSetInnerFocus();
+}
+
+bool ShortcutMessages::confirmSendingFiles(
+		not_null<const QMimeData*> data,
+		std::optional<bool> overrideSendImagesAsPhotos,
+		const QString &insertTextOnCancel) {
+	const auto hasImage = data->hasImage();
+	const auto premium = _controller->session().user()->isPremium();
+
+	if (const auto urls = Core::ReadMimeUrls(data); !urls.empty()) {
+		auto list = Storage::PrepareMediaList(
+			urls,
+			st::sendMediaPreviewSize,
+			premium);
+		if (list.error != Ui::PreparedList::Error::NonLocalUrl) {
+			if (list.error == Ui::PreparedList::Error::None
+				|| !hasImage) {
+				const auto emptyTextOnCancel = QString();
+				list.overrideSendImagesAsPhotos = overrideSendImagesAsPhotos;
+				confirmSendingFiles(std::move(list), emptyTextOnCancel);
+				return true;
+			}
+		}
+	}
+
+	if (auto read = Core::ReadMimeImage(data)) {
+		confirmSendingFiles(
+			std::move(read.image),
+			std::move(read.content),
+			overrideSendImagesAsPhotos,
+			insertTextOnCancel);
+		return true;
+	}
+	return false;
+}
+
+bool ShortcutMessages::confirmSendingFiles(
+		Ui::PreparedList &&list,
+		const QString &insertTextOnCancel) {
+	if (_composeControls->confirmMediaEdit(list)) {
+		return true;
+	} else if (showSendingFilesError(list)) {
+		return false;
+	}
+
+	auto box = Box<SendFilesBox>(
+		_controller,
+		std::move(list),
+		_composeControls->getTextWithAppliedMarkdown(),
+		_history->peer,
+		Api::SendType::Normal,
+		SendMenu::Type::SilentOnly); // #TODO replies schedule
+
+	box->setConfirmedCallback(crl::guard(this, [=](
+			Ui::PreparedList &&list,
+			Ui::SendFilesWay way,
+			TextWithTags &&caption,
+			Api::SendOptions options,
+			bool ctrlShiftEnter) {
+		sendingFilesConfirmed(
+			std::move(list),
+			way,
+			std::move(caption),
+			options,
+			ctrlShiftEnter);
+	}));
+	box->setCancelledCallback(_composeControls->restoreTextCallback(
+		insertTextOnCancel));
+
+	//ActivateWindow(_controller);
+	_controller->show(std::move(box));
+
+	return true;
+}
+
+bool ShortcutMessages::confirmSendingFiles(
+		QImage &&image,
+		QByteArray &&content,
+		std::optional<bool> overrideSendImagesAsPhotos,
+		const QString &insertTextOnCancel) {
+	if (image.isNull()) {
+		return false;
+	}
+
+	auto list = Storage::PrepareMediaFromImage(
+		std::move(image),
+		std::move(content),
+		st::sendMediaPreviewSize);
+	list.overrideSendImagesAsPhotos = overrideSendImagesAsPhotos;
+	return confirmSendingFiles(std::move(list), insertTextOnCancel);
+}
+
+void ShortcutMessages::sendingFilesConfirmed(
+		Ui::PreparedList &&list,
+		Ui::SendFilesWay way,
+		TextWithTags &&caption,
+		Api::SendOptions options,
+		bool ctrlShiftEnter) {
+	Expects(list.filesToProcess.empty());
+
+	if (showSendingFilesError(list, way.sendImagesAsPhotos())) {
+		return;
+	}
+	auto groups = DivideByGroups(
+		std::move(list),
+		way,
+		_history->peer->slowmodeApplied());
+	const auto type = way.sendImagesAsPhotos()
+		? SendMediaType::Photo
+		: SendMediaType::File;
+	auto action = prepareSendAction(options);
+	action.clearDraft = false;
+	if ((groups.size() != 1 || !groups.front().sentWithCaption())
+		&& !caption.text.isEmpty()) {
+		auto message = Api::MessageToSend(action);
+		message.textWithTags = base::take(caption);
+		_session->api().sendMessage(std::move(message));
+	}
+	for (auto &group : groups) {
+		const auto album = (group.type != Ui::AlbumType::None)
+			? std::make_shared<SendingAlbum>()
+			: nullptr;
+		_session->api().sendFiles(
+			std::move(group.list),
+			type,
+			base::take(caption),
+			album,
+			action);
+	}
+	if (_composeControls->replyingToMessage() == action.replyTo) {
+		_composeControls->cancelReplyMessage();
+	}
+	finishSending();
+}
+
+void ShortcutMessages::chooseAttach(
+		std::optional<bool> overrideSendImagesAsPhotos) {
+	_choosingAttach = false;
+
+	const auto filter = (overrideSendImagesAsPhotos == true)
+		? FileDialog::ImagesOrAllFilter()
+		: FileDialog::AllOrImagesFilter();
+	FileDialog::GetOpenPaths(this, tr::lng_choose_files(tr::now), filter, crl::guard(this, [=](
+			FileDialog::OpenResult &&result) {
+		if (result.paths.isEmpty() && result.remoteContent.isEmpty()) {
+			return;
+		}
+
+		if (!result.remoteContent.isEmpty()) {
+			auto read = Images::Read({
+				.content = result.remoteContent,
+			});
+			if (!read.image.isNull() && !read.animated) {
+				confirmSendingFiles(
+					std::move(read.image),
+					std::move(result.remoteContent),
+					overrideSendImagesAsPhotos);
+			} else {
+				uploadFile(result.remoteContent, SendMediaType::File);
+			}
+		} else {
+			const auto premium = _controller->session().user()->isPremium();
+			auto list = Storage::PrepareMediaList(
+				result.paths,
+				st::sendMediaPreviewSize,
+				premium);
+			list.overrideSendImagesAsPhotos = overrideSendImagesAsPhotos;
+			confirmSendingFiles(std::move(list));
+		}
+	}), nullptr);
+}
+
+void ShortcutMessages::finishSending() {
+	_composeControls->hidePanelsAnimated();
+	//if (_previewData && _previewData->pendingTill) previewCancel();
+	doSetInnerFocus();
+	showAtEnd();
+}
+
+void ShortcutMessages::showAtEnd() {
+	showAtPosition(Data::MaxMessagePosition);
+}
+
+void ShortcutMessages::doSetInnerFocus() {
+	if (!_inner->getSelectedText().rich.text.isEmpty()
+		|| !_inner->getSelectedItems().empty()
+		|| !_composeControls->focus()) {
+		_inner->setFocus();
+	}
+}
+
+void ShortcutMessages::sendExistingDocument(
+		not_null<DocumentData*> document) {
+	sendExistingDocument(document, {}, std::nullopt);
+}
+
+bool ShortcutMessages::sendExistingDocument(
+		not_null<DocumentData*> document,
+		Api::SendOptions options,
+		std::optional<MsgId> localId) {
+	Api::SendExistingDocument(
+		Api::MessageToSend(prepareSendAction(options)),
+		document,
+		localId);
+
+	_composeControls->cancelReplyMessage();
+	finishSending();
+	return true;
+}
+
+void ShortcutMessages::sendExistingPhoto(not_null<PhotoData*> photo) {
+	sendExistingPhoto(photo, {});
+}
+
+bool ShortcutMessages::sendExistingPhoto(
+		not_null<PhotoData*> photo,
+		Api::SendOptions options) {
+	Api::SendExistingPhoto(
+		Api::MessageToSend(prepareSendAction(options)),
+		photo);
+
+	_composeControls->cancelReplyMessage();
+	finishSending();
+	return true;
+}
+
+void ShortcutMessages::sendInlineResult(
+		not_null<InlineBots::Result*> result,
+		not_null<UserData*> bot) {
+	const auto errorText = result->getErrorOnSend(_history);
+	if (!errorText.isEmpty()) {
+		_controller->showToast(errorText);
+		return;
+	}
+	sendInlineResult(result, bot, {}, std::nullopt);
+	//const auto callback = [=](Api::SendOptions options) {
+	//	sendInlineResult(result, bot, options);
+	//};
+	//Ui::show(
+	//	PrepareScheduleBox(this, sendMenuType(), callback),
+	//	Ui::LayerOption::KeepOther);
+}
+
+void ShortcutMessages::sendInlineResult(
+		not_null<InlineBots::Result*> result,
+		not_null<UserData*> bot,
+		Api::SendOptions options,
+		std::optional<MsgId> localMessageId) {
+	auto action = prepareSendAction(options);
+	action.generateLocal = true;
+	_session->api().sendInlineResult(bot, result, action, localMessageId);
+
+	_composeControls->clear();
+	//_saveDraftText = true;
+	//_saveDraftStart = crl::now();
+	//onDraftSave();
+
+	auto &bots = cRefRecentInlineBots();
+	const auto index = bots.indexOf(bot);
+	if (index) {
+		if (index > 0) {
+			bots.removeAt(index);
+		} else if (bots.size() >= RecentInlineBotsLimit) {
+			bots.resize(RecentInlineBotsLimit - 1);
+		}
+		bots.push_front(bot);
+		bot->session().local().writeRecentHashtagsAndBots();
+	}
+	finishSending();
+}
+
+void ShortcutMessages::showAtPosition(
+		Data::MessagePosition position,
+		FullMsgId originItemId) {
+	showAtPosition(position, originItemId, {});
+}
+
+void ShortcutMessages::showAtPosition(
+		Data::MessagePosition position,
+		FullMsgId originItemId,
+		const Window::SectionShow &params) {
+	_lastShownAt = position.fullId;
+	_inner->showAtPosition(
+		position,
+		params,
+		_cornerButtons.doneJumpFrom(position.fullId, originItemId, true));
+}
+
+FullReplyTo ShortcutMessages::replyTo() const {
+	return _composeControls->replyingToMessage();
+}
+
+} // namespace
+
+Type ShortcutMessagesId(int shortcutId) {
+	return ShortcutMessages::Id(shortcutId);
+}
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.h b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.h
new file mode 100644
index 000000000..325b12602
--- /dev/null
+++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.h
@@ -0,0 +1,16 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "settings/settings_type.h"
+
+namespace Settings {
+
+[[nodiscard]] Type ShortcutMessagesId(int shortcutId);
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/settings_business.cpp b/Telegram/SourceFiles/settings/settings_business.cpp
index aae5868df..e3aa43129 100644
--- a/Telegram/SourceFiles/settings/settings_business.cpp
+++ b/Telegram/SourceFiles/settings/settings_business.cpp
@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_peer_values.h" // AmPremiumValue.
 #include "data/data_session.h"
 #include "data/business/data_business_info.h"
+#include "data/business/data_shortcut_messages.h"
 #include "info/info_wrap_widget.h" // Info::Wrap.
 #include "info/settings/info_settings_widget.h" // SectionCustomTopBarData.
 #include "lang/lang_keys.h"
@@ -359,6 +360,7 @@ void Business::setupContent() {
 	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
 
 	_controller->session().data().businessInfo().preloadTimezones();
+	_controller->session().data().shortcutMessages().preloadShortcuts();
 
 	Ui::AddSkip(content, st::settingsFromFileTop);
 
@@ -566,7 +568,8 @@ template <>
 struct SectionFactory<Business> : AbstractSectionFactory {
 	object_ptr<AbstractSection> create(
 		not_null<QWidget*> parent,
-		not_null<Window::SessionController*> controller
+		not_null<Window::SessionController*> controller,
+		not_null<Ui::ScrollArea*> scroll
 	) const final override {
 		return object_ptr<Business>(parent, controller);
 	}
diff --git a/Telegram/SourceFiles/settings/settings_common.h b/Telegram/SourceFiles/settings/settings_common.h
index ea80b4573..02beb1003 100644
--- a/Telegram/SourceFiles/settings/settings_common.h
+++ b/Telegram/SourceFiles/settings/settings_common.h
@@ -90,6 +90,13 @@ public:
 	}
 	virtual void setStepDataReference(std::any &data) {
 	}
+
+	virtual bool paintOuter(
+			not_null<QWidget*> outer,
+			int maxVisibleHeight,
+			QRect clip) {
+		return false;
+	}
 };
 
 enum class IconType {
diff --git a/Telegram/SourceFiles/settings/settings_common_session.h b/Telegram/SourceFiles/settings/settings_common_session.h
index 911e22897..16a03b9e4 100644
--- a/Telegram/SourceFiles/settings/settings_common_session.h
+++ b/Telegram/SourceFiles/settings/settings_common_session.h
@@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "base/object_ptr.h"
 #include "settings/settings_type.h"
 
+namespace Ui {
+class ScrollArea;
+} // namespace Ui
+
 namespace Ui::Menu {
 struct MenuCallback;
 } // namespace Ui::Menu
@@ -27,7 +31,8 @@ class AbstractSection;
 struct AbstractSectionFactory {
 	[[nodiscard]] virtual object_ptr<AbstractSection> create(
 		not_null<QWidget*> parent,
-		not_null<Window::SessionController*> controller) const = 0;
+		not_null<Window::SessionController*> controller,
+		not_null<Ui::ScrollArea*> scroll) const = 0;
 	[[nodiscard]] virtual bool hasCustomTopBar() const {
 		return false;
 	}
@@ -39,7 +44,8 @@ template <typename SectionType>
 struct SectionFactory : AbstractSectionFactory {
 	object_ptr<AbstractSection> create(
 		not_null<QWidget*> parent,
-		not_null<Window::SessionController*> controller
+		not_null<Window::SessionController*> controller,
+		not_null<Ui::ScrollArea*> scroll
 	) const final override {
 		return object_ptr<SectionType>(parent, controller);
 	}
diff --git a/Telegram/SourceFiles/settings/settings_notifications_type.cpp b/Telegram/SourceFiles/settings/settings_notifications_type.cpp
index 52114830f..717627573 100644
--- a/Telegram/SourceFiles/settings/settings_notifications_type.cpp
+++ b/Telegram/SourceFiles/settings/settings_notifications_type.cpp
@@ -43,7 +43,8 @@ struct Factory : AbstractSectionFactory {
 
 	object_ptr<AbstractSection> create(
 		not_null<QWidget*> parent,
-		not_null<Window::SessionController*> controller
+		not_null<Window::SessionController*> controller,
+		not_null<Ui::ScrollArea*> scroll
 	) const final override {
 		return object_ptr<NotificationsType>(parent, controller, type);
 	}
diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp
index e56122c94..ec17c8aac 100644
--- a/Telegram/SourceFiles/settings/settings_premium.cpp
+++ b/Telegram/SourceFiles/settings/settings_premium.cpp
@@ -1267,7 +1267,8 @@ template <>
 struct SectionFactory<Premium> : AbstractSectionFactory {
 	object_ptr<AbstractSection> create(
 		not_null<QWidget*> parent,
-		not_null<Window::SessionController*> controller
+		not_null<Window::SessionController*> controller,
+		not_null<Ui::ScrollArea*> scroll
 	) const final override {
 		return object_ptr<Premium>(parent, controller);
 	}

From 5c11fa4f6321671545ec9553a255d8b4984d8ac6 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Mon, 26 Feb 2024 22:01:23 +0400
Subject: [PATCH 052/108] Update API scheme on layer 176.

---
 .../data/business/data_business_info.cpp      | 48 +++++++++++++------
 Telegram/SourceFiles/data/data_user.cpp       | 11 +++--
 Telegram/SourceFiles/mtproto/scheme/api.tl    | 12 +++--
 3 files changed, 48 insertions(+), 23 deletions(-)

diff --git a/Telegram/SourceFiles/data/business/data_business_info.cpp b/Telegram/SourceFiles/data/business/data_business_info.cpp
index 1a1d30555..5fa0844ee 100644
--- a/Telegram/SourceFiles/data/business/data_business_info.cpp
+++ b/Telegram/SourceFiles/data/business/data_business_info.cpp
@@ -29,6 +29,38 @@ namespace {
 		MTP_vector_from_range(list | ranges::views::transform(proj)));
 }
 
+[[nodiscard]] MTPInputBusinessRecipients ToMTP(
+		const BusinessRecipients &data) {
+	//MTP_flags(RecipientsFlags(data.recipients, Flag())),
+	//	MTP_vector_from_range(
+	//		(data.recipients.allButExcluded
+	//			? data.recipients.excluded
+	//			: data.recipients.included).list
+	//		| ranges::views::transform(&UserData::inputUser)),
+
+	using Flag = MTPDinputBusinessRecipients::Flag;
+	using Type = BusinessChatType;
+	const auto &chats = data.allButExcluded
+		? data.excluded
+		: data.included;
+	const auto flags = Flag()
+		| ((chats.types & Type::NewChats) ? Flag::f_new_chats : Flag())
+		| ((chats.types & Type::ExistingChats)
+			? Flag::f_existing_chats
+			: Flag())
+		| ((chats.types & Type::Contacts) ? Flag::f_contacts : Flag())
+		| ((chats.types & Type::NonContacts) ? Flag::f_non_contacts : Flag())
+		| (chats.list.empty() ? Flag() : Flag::f_users)
+		| (data.allButExcluded ? Flag::f_exclude_selected : Flag());
+	const auto &users = data.allButExcluded
+		? data.excluded
+		: data.included;
+	return MTP_inputBusinessRecipients(
+		MTP_flags(flags),
+		MTP_vector_from_range(users.list
+			| ranges::views::transform(&UserData::inputUser)));
+}
+
 template <typename Flag>
 [[nodiscard]] auto RecipientsFlags(
 		const BusinessRecipients &data,
@@ -62,29 +94,17 @@ template <typename Flag>
 }
 
 [[nodiscard]] MTPInputBusinessAwayMessage ToMTP(const AwaySettings &data) {
-	using Flag = MTPDinputBusinessAwayMessage::Flag;
 	return MTP_inputBusinessAwayMessage(
-		MTP_flags(RecipientsFlags(data.recipients, Flag())),
 		MTP_int(data.shortcutId),
 		ToMTP(data.schedule),
-		MTP_vector_from_range(
-			(data.recipients.allButExcluded
-				? data.recipients.excluded
-				: data.recipients.included).list
-			| ranges::views::transform(&UserData::inputUser)));
+		ToMTP(data.recipients));
 }
 
 [[nodiscard]] MTPInputBusinessGreetingMessage ToMTP(
 		const GreetingSettings &data) {
-	using Flag = MTPDinputBusinessGreetingMessage::Flag;
 	return MTP_inputBusinessGreetingMessage(
-		MTP_flags(RecipientsFlags(data.recipients, Flag())),
 		MTP_int(data.shortcutId),
-		MTP_vector_from_range(
-			(data.recipients.allButExcluded
-				? data.recipients.excluded
-				: data.recipients.included).list
-			| ranges::views::transform(&UserData::inputUser)),
+		ToMTP(data.recipients),
 		MTP_int(data.noActivityDays));
 }
 
diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp
index 35ad558c7..a351ce678 100644
--- a/Telegram/SourceFiles/data/data_user.cpp
+++ b/Telegram/SourceFiles/data/data_user.cpp
@@ -62,11 +62,12 @@ using UpdateFlag = Data::PeerUpdate::Flag;
 	return result;
 }
 
-template <typename T>
-Data::BusinessRecipients RecipientsFromMTP(
+Data::BusinessRecipients FromMTP(
 		not_null<Data::Session*> owner,
-		const T &data) {
+		const MTPBusinessRecipients &recipients) {
 	using Type = Data::BusinessChatType;
+
+	const auto &data = recipients.data();
 	auto result = Data::BusinessRecipients{
 		.allButExcluded = data.is_exclude_selected(),
 	};
@@ -94,7 +95,7 @@ Data::BusinessRecipients RecipientsFromMTP(
 	}
 	const auto &data = message->data();
 	auto result = Data::AwaySettings{
-		.recipients = RecipientsFromMTP(owner, data),
+		.recipients = FromMTP(owner, data.vrecipients()),
 		.shortcutId = data.vshortcut_id().v,
 	};
 	data.vschedule().match([&](
@@ -120,7 +121,7 @@ Data::BusinessRecipients RecipientsFromMTP(
 	}
 	const auto &data = message->data();
 	return Data::GreetingSettings{
-		.recipients = RecipientsFromMTP(owner, data),
+		.recipients = FromMTP(owner, data.vrecipients()),
 		.noActivityDays = data.vno_activity_days().v,
 		.shortcutId = data.vshortcut_id().v,
 	};
diff --git a/Telegram/SourceFiles/mtproto/scheme/api.tl b/Telegram/SourceFiles/mtproto/scheme/api.tl
index 9fdf2c153..cd07a4e19 100644
--- a/Telegram/SourceFiles/mtproto/scheme/api.tl
+++ b/Telegram/SourceFiles/mtproto/scheme/api.tl
@@ -1671,17 +1671,21 @@ businessWorkHours#8c92b098 flags:# open_now:flags.0?true timezone_id:string week
 
 businessLocation#ac5c1af7 flags:# geo_point:flags.0?GeoPoint address:string = BusinessLocation;
 
+inputBusinessRecipients#6f8b32aa flags:# existing_chats:flags.0?true new_chats:flags.1?true contacts:flags.2?true non_contacts:flags.3?true exclude_selected:flags.5?true users:flags.4?Vector<InputUser> = InputBusinessRecipients;
+
+businessRecipients#21108ff7 flags:# existing_chats:flags.0?true new_chats:flags.1?true contacts:flags.2?true non_contacts:flags.3?true exclude_selected:flags.5?true users:flags.4?Vector<long> = BusinessRecipients;
+
 businessAwayMessageScheduleAlways#c9b9e2b9 = BusinessAwayMessageSchedule;
 businessAwayMessageScheduleOutsideWorkHours#c3f2f501 = BusinessAwayMessageSchedule;
 businessAwayMessageScheduleCustom#cc4d9ecc start_date:int end_date:int = BusinessAwayMessageSchedule;
 
-inputBusinessGreetingMessage#7d4a3609 flags:# existing_chats:flags.0?true new_chats:flags.1?true contacts:flags.2?true non_contacts:flags.3?true exclude_selected:flags.5?true shortcut_id:int users:flags.4?Vector<InputUser> no_activity_days:int = InputBusinessGreetingMessage;
+inputBusinessGreetingMessage#194cb3b shortcut_id:int recipients:InputBusinessRecipients no_activity_days:int = InputBusinessGreetingMessage;
 
-businessGreetingMessage#a098d54c flags:# existing_chats:flags.0?true new_chats:flags.1?true contacts:flags.2?true non_contacts:flags.3?true exclude_selected:flags.5?true shortcut_id:int users:flags.4?Vector<long> no_activity_days:int = BusinessGreetingMessage;
+businessGreetingMessage#e519abab shortcut_id:int recipients:BusinessRecipients no_activity_days:int = BusinessGreetingMessage;
 
-inputBusinessAwayMessage#ce6fda48 flags:# existing_chats:flags.0?true new_chats:flags.1?true contacts:flags.2?true non_contacts:flags.3?true exclude_selected:flags.5?true shortcut_id:int schedule:BusinessAwayMessageSchedule users:flags.4?Vector<InputUser> = InputBusinessAwayMessage;
+inputBusinessAwayMessage#edac03f4 shortcut_id:int schedule:BusinessAwayMessageSchedule recipients:InputBusinessRecipients = InputBusinessAwayMessage;
 
-businessAwayMessage#9acd7a15 flags:# existing_chats:flags.0?true new_chats:flags.1?true contacts:flags.2?true non_contacts:flags.3?true exclude_selected:flags.5?true shortcut_id:int schedule:BusinessAwayMessageSchedule users:flags.4?Vector<long> = BusinessAwayMessage;
+businessAwayMessage#1bd9bebc shortcut_id:int schedule:BusinessAwayMessageSchedule recipients:BusinessRecipients = BusinessAwayMessage;
 
 timezone#ff9289f5 id:string name:string utc_offset:int = Timezone;
 

From 7f3ebde252efc86c71e6ad414962b9c563cf93e7 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Mon, 26 Feb 2024 22:24:00 +0400
Subject: [PATCH 053/108] Implement sending of shortcutted messages.

---
 Telegram/SourceFiles/api/api_editing.cpp      |   3 +
 Telegram/SourceFiles/api/api_sending.cpp      |  95 ++++---
 Telegram/SourceFiles/api/api_updates.cpp      |  26 ++
 Telegram/SourceFiles/apiwrap.cpp              | 104 ++++---
 .../boxes/background_preview_box.cpp          |  38 +--
 .../boxes/peers/edit_peer_color_box.cpp       |  59 ++--
 .../boxes/reactions_settings_box.cpp          |  19 +-
 .../data/business/data_shortcut_messages.cpp  |  12 +-
 .../data/business/data_shortcut_messages.h    |   2 +
 .../data/data_download_manager.cpp            |  27 +-
 Telegram/SourceFiles/data/data_histories.cpp  |  21 ++
 .../SourceFiles/data/data_replies_list.cpp    |  10 +-
 .../data/data_sponsored_messages.cpp          |   2 +-
 Telegram/SourceFiles/data/data_stories.cpp    |   2 +-
 Telegram/SourceFiles/data/data_types.h        |   2 +
 .../admin_log/history_admin_log_item.cpp      |  96 +++----
 Telegram/SourceFiles/history/history.cpp      | 137 +++------
 Telegram/SourceFiles/history/history.h        |  75 ++---
 Telegram/SourceFiles/history/history_item.cpp | 263 +++++++-----------
 Telegram/SourceFiles/history/history_item.h   | 104 +++----
 .../history/history_item_components.cpp       |   1 +
 .../history/history_item_helpers.cpp          |  14 +-
 .../controls/history_view_draft_options.cpp   |  52 ++--
 .../history/view/history_view_about_view.cpp  |  57 ++--
 .../history/view/history_view_element.h       |   1 +
 .../history/view/history_view_list_widget.cpp |   8 +-
 .../history/view/history_view_list_widget.h   |   2 +-
 .../history/view/history_view_message.cpp     |   5 +
 .../inline_bots/inline_bot_result.cpp         |  32 +--
 .../inline_bots/inline_bot_result.h           |   9 +-
 .../inline_bots/inline_bot_send_data.cpp      |  88 +-----
 .../inline_bots/inline_bot_send_data.h        |  47 +---
 .../media/stories/media_stories_reactions.cpp |  23 +-
 .../business/settings_shortcut_messages.cpp   | 149 +++++++---
 .../support/support_autocomplete.cpp          |  58 ++--
 35 files changed, 709 insertions(+), 934 deletions(-)

diff --git a/Telegram/SourceFiles/api/api_editing.cpp b/Telegram/SourceFiles/api/api_editing.cpp
index 126223bee..a1e168adf 100644
--- a/Telegram/SourceFiles/api/api_editing.cpp
+++ b/Telegram/SourceFiles/api/api_editing.cpp
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "api/api_media.h"
 #include "api/api_text_entities.h"
 #include "ui/boxes/confirm_box.h"
+#include "data/business/data_shortcut_messages.h"
 #include "data/data_histories.h"
 #include "data/data_scheduled_messages.h"
 #include "data/data_session.h"
@@ -92,6 +93,8 @@ mtpRequestId EditMessage(
 
 	const auto id = item->isScheduled()
 		? session->data().scheduledMessages().lookupId(item)
+		: item->isBusinessShortcut()
+		? session->data().shortcutMessages().lookupId(item)
 		: item->id;
 	return api->request(MTPmessages_EditMessage(
 		MTP_flags(flags),
diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp
index 96bdf4a4a..982992ce3 100644
--- a/Telegram/SourceFiles/api/api_sending.cpp
+++ b/Telegram/SourceFiles/api/api_sending.cpp
@@ -85,20 +85,21 @@ void SendExistingMedia(
 			? (*localMessageId)
 			: session->data().nextLocalMessageId());
 	const auto randomId = base::RandomValue<uint64>();
+	const auto &action = message.action;
 
 	auto flags = NewMessageFlags(peer);
 	auto sendFlags = MTPmessages_SendMedia::Flags(0);
-	if (message.action.replyTo) {
+	if (action.replyTo) {
 		flags |= MessageFlag::HasReplyInfo;
 		sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
 	}
 	const auto anonymousPost = peer->amAnonymous();
-	const auto silentPost = ShouldSendSilent(peer, message.action.options);
-	InnerFillMessagePostFlags(message.action.options, peer, flags);
+	const auto silentPost = ShouldSendSilent(peer, action.options);
+	InnerFillMessagePostFlags(action.options, peer, flags);
 	if (silentPost) {
 		sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
 	}
-	const auto sendAs = message.action.options.sendAs;
+	const auto sendAs = action.options.sendAs;
 	const auto messageFromId = sendAs
 		? sendAs->id
 		: anonymousPost
@@ -125,33 +126,30 @@ void SendExistingMedia(
 	}
 	const auto captionText = caption.text;
 
-	if (message.action.options.scheduled) {
+	if (action.options.scheduled) {
 		flags |= MessageFlag::IsOrWasScheduled;
 		sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
 	}
-	if (message.action.options.shortcutId) {
+	if (action.options.shortcutId) {
+		flags |= MessageFlag::ShortcutMessage;
 		sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
 	}
 
 	session->data().registerMessageRandomId(randomId, newId);
 
-	const auto viaBotId = UserId();
-	history->addNewLocalMessage(
-		newId.msg,
-		flags,
-		viaBotId,
-		message.action.replyTo,
-		HistoryItem::NewMessageDate(message.action.options.scheduled),
-		messageFromId,
-		messagePostAuthor,
-		media,
-		caption,
-		HistoryMessageMarkupData());
+	history->addNewLocalMessage({
+		.id = newId.msg,
+		.flags = flags,
+		.from = messageFromId,
+		.replyTo = action.replyTo,
+		.date = HistoryItem::NewMessageDate(action.options),
+		.shortcutId = action.options.shortcutId,
+		.postAuthor = messagePostAuthor,
+	}, media, caption);
 
 	const auto performRequest = [=](const auto &repeatRequest) -> void {
 		auto &histories = history->owner().histories();
 		const auto session = &history->session();
-		const auto &action = message.action;
 		const auto usedFileReference = media->fileReference();
 		histories.sendPreparedMessage(
 			history,
@@ -187,7 +185,7 @@ void SendExistingMedia(
 	};
 	performRequest(performRequest);
 
-	api->finishForwarding(message.action);
+	api->finishForwarding(action);
 }
 
 } // namespace
@@ -307,23 +305,23 @@ bool SendDice(MessageToSend &message) {
 		sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
 	}
 	if (action.options.shortcutId) {
+		flags |= MessageFlag::ShortcutMessage;
 		sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
 	}
 
 	session->data().registerMessageRandomId(randomId, newId);
 
-	const auto viaBotId = UserId();
-	history->addNewLocalMessage(
-		newId.msg,
-		flags,
-		viaBotId,
-		action.replyTo,
-		HistoryItem::NewMessageDate(action.options.scheduled),
-		messageFromId,
-		messagePostAuthor,
-		TextWithEntities(),
-		MTP_messageMediaDice(MTP_int(0), MTP_string(emoji)),
-		HistoryMessageMarkupData());
+	history->addNewLocalMessage({
+		.id = newId.msg,
+		.flags = flags,
+		.from = messageFromId,
+		.replyTo = action.replyTo,
+		.date = HistoryItem::NewMessageDate(action.options),
+		.shortcutId = action.options.shortcutId,
+		.postAuthor = messagePostAuthor,
+	}, TextWithEntities(), MTP_messageMediaDice(
+		MTP_int(0),
+		MTP_string(emoji)));
 	histories.sendPreparedMessage(
 		history,
 		action.replyTo,
@@ -420,7 +418,13 @@ void SendConfirmedFile(
 	if (file->to.options.scheduled) {
 		flags |= MessageFlag::IsOrWasScheduled;
 
-		// Scheduled messages have no the 'edited' badge.
+		// Scheduled messages have no 'edited' badge.
+		flags |= MessageFlag::HideEdited;
+	}
+	if (file->to.options.shortcutId) {
+		flags |= MessageFlag::ShortcutMessage;
+
+		// Shortcut messages have no 'edited' badge.
 		flags |= MessageFlag::HideEdited;
 	}
 	if (file->type == SendMediaType::Audio) {
@@ -429,8 +433,7 @@ void SendConfirmedFile(
 		}
 	}
 
-	const auto messageFromId =
-		file->to.options.sendAs
+	const auto messageFromId = file->to.options.sendAs
 		? file->to.options.sendAs->id
 		: anonymousPost
 		? PeerId()
@@ -500,19 +503,15 @@ void SendConfirmedFile(
 		edition.savePreviousMedia = true;
 		itemToEdit->applyEdition(std::move(edition));
 	} else {
-		const auto viaBotId = UserId();
-		history->addNewLocalMessage(
-			newId.msg,
-			flags,
-			viaBotId,
-			file->to.replyTo,
-			HistoryItem::NewMessageDate(file->to.options.scheduled),
-			messageFromId,
-			messagePostAuthor,
-			caption,
-			media,
-			HistoryMessageMarkupData(),
-			groupId);
+		history->addNewLocalMessage({
+			.id = newId.msg,
+			.flags = flags,
+			.from = messageFromId,
+			.replyTo = file->to.replyTo,
+			.date = HistoryItem::NewMessageDate(file->to.options),
+			.shortcutId = file->to.options.shortcutId,
+			.postAuthor = messagePostAuthor,
+		}, caption, media);
 	}
 
 	if (isEditing) {
diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp
index 679faa063..57db15d92 100644
--- a/Telegram/SourceFiles/api/api_updates.cpp
+++ b/Telegram/SourceFiles/api/api_updates.cpp
@@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "mtproto/mtp_instance.h"
 #include "mtproto/mtproto_config.h"
 #include "mtproto/mtproto_dc_options.h"
+#include "data/business/data_shortcut_messages.h"
 #include "data/notify/data_notify_settings.h"
 #include "data/stickers/data_stickers.h"
 #include "data/data_saved_messages.h"
@@ -1774,6 +1775,31 @@ void Updates::feedUpdate(const MTPUpdate &update) {
 		session().data().scheduledMessages().apply(d);
 	} break;
 
+	case mtpc_updateQuickReplies: {
+		const auto &d = update.c_updateQuickReplies();
+		session().data().shortcutMessages().apply(d);
+	} break;
+
+	case mtpc_updateNewQuickReply: {
+		const auto &d = update.c_updateNewQuickReply();
+		session().data().shortcutMessages().apply(d);
+	} break;
+
+	case mtpc_updateDeleteQuickReply: {
+		const auto &d = update.c_updateDeleteQuickReply();
+		session().data().shortcutMessages().apply(d);
+	} break;
+
+	case mtpc_updateQuickReplyMessage: {
+		const auto &d = update.c_updateQuickReplyMessage();
+		session().data().shortcutMessages().apply(d);
+	} break;
+
+	case mtpc_updateDeleteQuickReplyMessages: {
+		const auto &d = update.c_updateDeleteQuickReplyMessages();
+		session().data().shortcutMessages().apply(d);
+	} break;
+
 	case mtpc_updateWebPage: {
 		auto &d = update.c_updateWebPage();
 
diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index bca32c3da..4676c5a50 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -2449,6 +2449,14 @@ void ApiWrap::refreshFileReference(
 				request(MTPmessages_GetScheduledMessages(
 					item->history()->peer->input,
 					MTP_vector<MTPint>(1, MTP_int(realId))));
+			} else if (item->isBusinessShortcut()) {
+				const auto &shortcuts = _session->data().shortcutMessages();
+				const auto realId = shortcuts.lookupId(item);
+				request(MTPmessages_GetQuickReplyMessages(
+					MTP_flags(MTPmessages_GetQuickReplyMessages::Flag::f_id),
+					MTP_int(item->shortcutId()),
+					MTP_vector<MTPint>(1, MTP_int(realId)),
+					MTP_long(0)));
 			} else if (const auto channel = item->history()->peer->asChannel()) {
 				request(MTPchannels_GetMessages(
 					channel->inputChannel,
@@ -3232,6 +3240,7 @@ void ApiWrap::forwardMessages(
 		sendFlags |= SendFlag::f_schedule_date;
 	}
 	if (action.options.shortcutId) {
+		flags |= MessageFlag::ShortcutMessage;
 		sendFlags |= SendFlag::f_quick_reply_shortcut;
 	}
 	if (draft.options != Data::ForwardOptions::PreserveInfo) {
@@ -3317,14 +3326,15 @@ void ApiWrap::forwardMessages(
 			const auto messagePostAuthor = peer->isBroadcast()
 				? self->name()
 				: QString();
-			history->addNewLocalMessage(
-				newId.msg,
-				flags,
-				HistoryItem::NewMessageDate(action.options.scheduled),
-				messageFromId,
-				messagePostAuthor,
-				item,
-				topMsgId);
+			history->addNewLocalMessage({
+				.id = newId.msg,
+				.flags = flags,
+				.from = messageFromId,
+				.replyTo = { .topicRootId = topMsgId },
+				.date = HistoryItem::NewMessageDate(action.options),
+				.shortcutId = action.options.shortcutId,
+				.postAuthor = messagePostAuthor,
+			}, item);
 			_session->data().registerMessageRandomId(randomId, newId);
 			if (!localIds) {
 				localIds = std::make_shared<base::flat_map<uint64, FullMsgId>>();
@@ -3405,6 +3415,9 @@ void ApiWrap::sendSharedContact(
 	if (action.options.scheduled) {
 		flags |= MessageFlag::IsOrWasScheduled;
 	}
+	if (action.options.shortcutId) {
+		flags |= MessageFlag::ShortcutMessage;
+	}
 	const auto messageFromId = action.options.sendAs
 		? action.options.sendAs->id
 		: anonymousPost
@@ -3413,23 +3426,20 @@ void ApiWrap::sendSharedContact(
 	const auto messagePostAuthor = peer->isBroadcast()
 		? _session->user()->name()
 		: QString();
-	const auto viaBotId = UserId();
-	const auto item = history->addNewLocalMessage(
-		newId.msg,
-		flags,
-		viaBotId,
-		action.replyTo,
-		HistoryItem::NewMessageDate(action.options.scheduled),
-		messageFromId,
-		messagePostAuthor,
-		TextWithEntities(),
-		MTP_messageMediaContact(
-			MTP_string(phone),
-			MTP_string(firstName),
-			MTP_string(lastName),
-			MTP_string(), // vcard
-			MTP_long(userId.bare)),
-		HistoryMessageMarkupData());
+	const auto item = history->addNewLocalMessage({
+		.id = newId.msg,
+		.flags = flags,
+		.from = messageFromId,
+		.replyTo = action.replyTo,
+		.date = HistoryItem::NewMessageDate(action.options),
+		.shortcutId = action.options.shortcutId,
+		.postAuthor = messagePostAuthor,
+	}, TextWithEntities(), MTP_messageMediaContact(
+		MTP_string(phone),
+		MTP_string(firstName),
+		MTP_string(lastName),
+		MTP_string(), // vcard
+		MTP_long(userId.bare)));
 
 	const auto media = MTP_inputMediaContact(
 		MTP_string(phone),
@@ -3737,21 +3747,19 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
 			mediaFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
 		}
 		if (action.options.shortcutId) {
+			flags |= MessageFlag::ShortcutMessage;
 			sendFlags |= MTPmessages_SendMessage::Flag::f_quick_reply_shortcut;
 			mediaFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
 		}
-		const auto viaBotId = UserId();
-		lastMessage = history->addNewLocalMessage(
-			newId.msg,
-			flags,
-			viaBotId,
-			action.replyTo,
-			HistoryItem::NewMessageDate(action.options.scheduled),
-			messageFromId,
-			messagePostAuthor,
-			sending,
-			media,
-			HistoryMessageMarkupData());
+		lastMessage = history->addNewLocalMessage({
+			.id = newId.msg,
+			.flags = flags,
+			.from = messageFromId,
+			.replyTo = action.replyTo,
+			.date = HistoryItem::NewMessageDate(action.options),
+			.shortcutId = action.options.shortcutId,
+			.postAuthor = messagePostAuthor,
+		}, sending, media);
 		const auto done = [=](
 				const MTPUpdates &result,
 				const MTP::Response &response) {
@@ -3903,6 +3911,7 @@ void ApiWrap::sendInlineResult(
 		sendFlags |= SendFlag::f_schedule_date;
 	}
 	if (action.options.shortcutId) {
+		flags |= MessageFlag::ShortcutMessage;
 		sendFlags |= SendFlag::f_quick_reply_shortcut;
 	}
 	if (action.options.hideViaBot) {
@@ -3923,15 +3932,18 @@ void ApiWrap::sendInlineResult(
 
 	_session->data().registerMessageRandomId(randomId, newId);
 
-	data->addToHistory(
-		history,
-		flags,
-		newId.msg,
-		messageFromId,
-		HistoryItem::NewMessageDate(action.options.scheduled),
-		(bot && !action.options.hideViaBot) ? peerToUser(bot->id) : 0,
-		action.replyTo,
-		messagePostAuthor);
+	data->addToHistory(history, {
+		.id = newId.msg,
+		.flags = flags,
+		.from = messageFromId,
+		.replyTo = action.replyTo,
+		.date = HistoryItem::NewMessageDate(action.options),
+		.shortcutId = action.options.shortcutId,
+		.viaBotId = ((bot && !action.options.hideViaBot)
+			? peerToUser(bot->id)
+			: UserId()),
+		.postAuthor = messagePostAuthor,
+	});
 
 	history->clearCloudDraft(topicRootId);
 	history->startSavingCloudDraft(topicRootId);
diff --git a/Telegram/SourceFiles/boxes/background_preview_box.cpp b/Telegram/SourceFiles/boxes/background_preview_box.cpp
index 8406657e6..048362638 100644
--- a/Telegram/SourceFiles/boxes/background_preview_box.cpp
+++ b/Telegram/SourceFiles/boxes/background_preview_box.cpp
@@ -81,11 +81,11 @@ constexpr auto kMaxWallPaperSlugLength = 255;
 	const auto flags = MessageFlag::FakeHistoryItem
 		| MessageFlag::HasFromId
 		| (out ? MessageFlag::Outgoing : MessageFlag(0));
-	const auto item = history->makeMessage(
-		history->owner().nextLocalMessageId(),
-		flags,
-		base::unixtime::now(),
-		PreparedServiceText{ { text } });
+	const auto item = history->makeMessage({
+		.id = history->owner().nextLocalMessageId(),
+		.flags = flags,
+		.date = base::unixtime::now(),
+	}, PreparedServiceText{ { text } });
 	return AdminLog::OwnedItem(delegate, item);
 }
 
@@ -96,24 +96,16 @@ constexpr auto kMaxWallPaperSlugLength = 255;
 		bool out) {
 	Expects(history->peer->isUser());
 
-	const auto flags = MessageFlag::FakeHistoryItem
-		| MessageFlag::HasFromId
-		| (out ? MessageFlag::Outgoing : MessageFlag(0));
-	const auto replyTo = FullReplyTo();
-	const auto viaBotId = UserId();
-	const auto groupedId = uint64();
-	const auto item = history->makeMessage(
-		history->nextNonHistoryEntryId(),
-		flags,
-		replyTo,
-		viaBotId,
-		base::unixtime::now(),
-		out ? history->session().userId() : peerToUser(history->peer->id),
-		QString(),
-		TextWithEntities{ text },
-		MTP_messageMediaEmpty(),
-		HistoryMessageMarkupData(),
-		groupedId);
+	const auto item = history->makeMessage({
+		.id = history->nextNonHistoryEntryId(),
+		.flags = (MessageFlag::FakeHistoryItem
+			| MessageFlag::HasFromId
+			| (out ? MessageFlag::Outgoing : MessageFlag(0))),
+		.from = (out
+			? history->session().userId()
+			: peerToUser(history->peer->id)),
+		.date = base::unixtime::now(),
+	}, TextWithEntities{ text }, MTP_messageMediaEmpty());
 	return AdminLog::OwnedItem(delegate, item);
 }
 
diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp
index 3f01fe269..2e24d2804 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp
@@ -319,47 +319,36 @@ PreviewWrap::PreviewWrap(
 , _delegate(std::make_unique<PreviewDelegate>(box, _style.get(), [=] {
 	update();
 }))
-, _replyToItem(_history->addNewLocalMessage(
-	_history->nextNonHistoryEntryId(),
-	(MessageFlag::FakeHistoryItem
+, _replyToItem(_history->addNewLocalMessage({
+	.id = _history->nextNonHistoryEntryId(),
+	.flags = (MessageFlag::FakeHistoryItem
 		| MessageFlag::HasFromId
 		| MessageFlag::Post),
-	UserId(), // via
-	FullReplyTo(),
-	base::unixtime::now(), // date
-	_fake->id,
-	QString(), // postAuthor
-	TextWithEntities{ _peer->isSelf()
-		? tr::lng_settings_color_reply(tr::now)
-		: tr::lng_settings_color_reply_channel(tr::now),
-	},
-	MTP_messageMediaEmpty(),
-	HistoryMessageMarkupData(),
-	uint64(0)))
-, _replyItem(_history->addNewLocalMessage(
-	_history->nextNonHistoryEntryId(),
-	(MessageFlag::FakeHistoryItem
+	.from = _fake->id,
+	.date = base::unixtime::now(),
+}, TextWithEntities{ _peer->isSelf()
+	? tr::lng_settings_color_reply(tr::now)
+	: tr::lng_settings_color_reply_channel(tr::now),
+}, MTP_messageMediaEmpty()))
+, _replyItem(_history->addNewLocalMessage({
+	.id = _history->nextNonHistoryEntryId(),
+	.flags = (MessageFlag::FakeHistoryItem
 		| MessageFlag::HasFromId
 		| MessageFlag::HasReplyInfo
 		| MessageFlag::Post),
-	UserId(), // via
-	FullReplyTo{ .messageId = _replyToItem->fullId() },
-	base::unixtime::now(), // date
-	_fake->id,
-	QString(), // postAuthor
-	TextWithEntities{ _peer->isSelf()
-		? tr::lng_settings_color_text(tr::now)
-		: tr::lng_settings_color_text_channel(tr::now),
-	},
-	MTP_messageMediaWebPage(
+	.from = _fake->id,
+	.replyTo = FullReplyTo{.messageId = _replyToItem->fullId() },
+	.date = base::unixtime::now(),
+}, TextWithEntities{ _peer->isSelf()
+	? tr::lng_settings_color_text(tr::now)
+	: tr::lng_settings_color_text_channel(tr::now),
+}, MTP_messageMediaWebPage(
+	MTP_flags(0),
+	MTP_webPagePending(
 		MTP_flags(0),
-		MTP_webPagePending(
-			MTP_flags(0),
-			MTP_long(_webpage->id),
-			MTPstring(),
-			MTP_int(0))),
-	HistoryMessageMarkupData(),
-	uint64(0)))
+		MTP_long(_webpage->id),
+		MTPstring(),
+		MTP_int(0)))))
 , _element(_replyItem->createView(_delegate.get()))
 , _position(0, st::msgMargin.bottom()) {
 	_style->apply(_theme.get());
diff --git a/Telegram/SourceFiles/boxes/reactions_settings_box.cpp b/Telegram/SourceFiles/boxes/reactions_settings_box.cpp
index 4de09e882..e33624403 100644
--- a/Telegram/SourceFiles/boxes/reactions_settings_box.cpp
+++ b/Telegram/SourceFiles/boxes/reactions_settings_box.cpp
@@ -77,20 +77,15 @@ AdminLog::OwnedItem GenerateItem(
 		const QString &text) {
 	Expects(history->peer->isUser());
 
-	const auto item = history->addNewLocalMessage(
-		history->nextNonHistoryEntryId(),
-		(MessageFlag::FakeHistoryItem
+	const auto item = history->addNewLocalMessage({
+		.id = history->nextNonHistoryEntryId(),
+		.flags = (MessageFlag::FakeHistoryItem
 			| MessageFlag::HasFromId
 			| MessageFlag::HasReplyInfo),
-		UserId(), // via
-		FullReplyTo{ .messageId = replyTo },
-		base::unixtime::now(), // date
-		from,
-		QString(), // postAuthor
-		TextWithEntities{ .text = text },
-		MTP_messageMediaEmpty(),
-		HistoryMessageMarkupData(),
-		uint64(0)); // groupedId
+		.from = from,
+		.replyTo = FullReplyTo{ .messageId = replyTo },
+		.date = base::unixtime::now(),
+	}, TextWithEntities{ .text = text }, MTP_messageMediaEmpty());
 
 	return AdminLog::OwnedItem(delegate, item);
 }
diff --git a/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp b/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
index 1013c05bc..026e6a99e 100644
--- a/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
+++ b/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
@@ -27,13 +27,13 @@ constexpr auto kRequestTimeLimit = 60 * crl::time(1000);
 [[nodiscard]] MsgId RemoteToLocalMsgId(MsgId id) {
 	Expects(IsServerMsgId(id));
 
-	return ServerMaxMsgId + id + 1;
+	return ScheduledMaxMsgId + id + 1;
 }
 
 [[nodiscard]] MsgId LocalToRemoteMsgId(MsgId id) {
 	Expects(IsShortcutMsgId(id));
 
-	return (id - ServerMaxMsgId - 1);
+	return (id - ScheduledMaxMsgId - 1);
 }
 
 [[nodiscard]] bool TooEarlyForRequest(crl::time received) {
@@ -145,6 +145,14 @@ int ShortcutMessages::count(BusinessShortcutId shortcutId) const {
 	return (i != end(_data)) ? i->second.items.size() : 0;
 }
 
+void ShortcutMessages::apply(const MTPDupdateQuickReplies &update) {
+
+}
+
+void ShortcutMessages::apply(const MTPDupdateNewQuickReply &update) {
+
+}
+
 void ShortcutMessages::apply(const MTPDupdateQuickReplyMessage &update) {
 	const auto &message = update.vmessage();
 	const auto shortcutId = BusinessShortcutIdFromMessage(message);
diff --git a/Telegram/SourceFiles/data/business/data_shortcut_messages.h b/Telegram/SourceFiles/data/business/data_shortcut_messages.h
index 2028f2ece..57997e7e0 100644
--- a/Telegram/SourceFiles/data/business/data_shortcut_messages.h
+++ b/Telegram/SourceFiles/data/business/data_shortcut_messages.h
@@ -50,6 +50,8 @@ public:
 	[[nodiscard]] int count(BusinessShortcutId shortcutId) const;
 	[[nodiscard]] MsgId localMessageId(MsgId remoteId) const;
 
+	void apply(const MTPDupdateQuickReplies &update);
+	void apply(const MTPDupdateNewQuickReply &update);
 	void apply(const MTPDupdateQuickReplyMessage &update);
 	void apply(const MTPDupdateDeleteQuickReplyMessages &update);
 	void apply(const MTPDupdateDeleteQuickReply &update);
diff --git a/Telegram/SourceFiles/data/data_download_manager.cpp b/Telegram/SourceFiles/data/data_download_manager.cpp
index cd736e9e7..95c78092d 100644
--- a/Telegram/SourceFiles/data/data_download_manager.cpp
+++ b/Telegram/SourceFiles/data/data_download_manager.cpp
@@ -879,29 +879,20 @@ not_null<HistoryItem*> DownloadManager::generateItem(
 	const auto session = document
 		? &document->session()
 		: &photo->session();
-	const auto fromId = previousItem
-		? previousItem->from()->id
-		: session->userPeerId();
 	const auto history = previousItem
 		? previousItem->history()
 		: session->data().history(session->user());
-	const auto flags = MessageFlag::FakeHistoryItem;
-	const auto replyTo = FullReplyTo();
-	const auto viaBotId = UserId();
-	const auto date = base::unixtime::now();
+	;
 	const auto caption = TextWithEntities();
 	const auto make = [&](const auto media) {
-		return history->makeMessage(
-			history->nextNonHistoryEntryId(),
-			flags,
-			replyTo,
-			viaBotId,
-			date,
-			fromId,
-			QString(),
-			media,
-			caption,
-			HistoryMessageMarkupData());
+		return history->makeMessage({
+			.id = history->nextNonHistoryEntryId(),
+			.flags = MessageFlag::FakeHistoryItem,
+			.from = (previousItem
+				? previousItem->from()->id
+				: session->userPeerId()),
+			.date = base::unixtime::now(),
+		}, media, caption);
 	};
 	const auto result = document ? make(document) : make(photo);
 	_generated.emplace(result);
diff --git a/Telegram/SourceFiles/data/data_histories.cpp b/Telegram/SourceFiles/data/data_histories.cpp
index 0dde157ad..a43b2f680 100644
--- a/Telegram/SourceFiles/data/data_histories.cpp
+++ b/Telegram/SourceFiles/data/data_histories.cpp
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_histories.h"
 
 #include "api/api_text_entities.h"
+#include "data/business/data_shortcut_messages.h"
 #include "data/data_session.h"
 #include "data/data_channel.h"
 #include "data/data_chat.h"
@@ -821,6 +822,7 @@ void Histories::deleteMessages(const MessageIdsList &ids, bool revoke) {
 	remove.reserve(ids.size());
 	base::flat_map<not_null<History*>, QVector<MTPint>> idsByPeer;
 	base::flat_map<not_null<PeerData*>, QVector<MTPint>> scheduledIdsByPeer;
+	base::flat_map<BusinessShortcutId, QVector<MTPint>> quickIdsByShortcut;
 	for (const auto &itemId : ids) {
 		if (const auto item = _owner->message(itemId)) {
 			const auto history = item->history();
@@ -834,6 +836,16 @@ void Histories::deleteMessages(const MessageIdsList &ids, bool revoke) {
 					_owner->scheduledMessages().removeSending(item);
 				}
 				continue;
+			} else if (item->isBusinessShortcut()) {
+				const auto wasOnServer = !item->isSending()
+					&& !item->hasFailed();
+				if (wasOnServer) {
+					quickIdsByShortcut[item->shortcutId()].push_back(MTP_int(
+						_owner->shortcutMessages().lookupId(item)));
+				} else {
+					_owner->shortcutMessages().removeSending(item);
+				}
+				continue;
 			}
 			remove.push_back(item);
 			if (item->isRegular()) {
@@ -853,6 +865,15 @@ void Histories::deleteMessages(const MessageIdsList &ids, bool revoke) {
 			peer->session().api().applyUpdates(result);
 		}).send();
 	}
+	for (const auto &[shortcutId, ids] : quickIdsByShortcut) {
+		const auto api = &_owner->session().api();
+		api->request(MTPmessages_DeleteQuickReplyMessages(
+			MTP_int(shortcutId),
+			MTP_vector<MTPint>(ids)
+		)).done([=](const MTPUpdates &result) {
+			api->applyUpdates(result);
+		}).send();
+	}
 
 	for (const auto item : remove) {
 		const auto history = item->history();
diff --git a/Telegram/SourceFiles/data/data_replies_list.cpp b/Telegram/SourceFiles/data/data_replies_list.cpp
index 733a0ae05..5c1700093 100644
--- a/Telegram/SourceFiles/data/data_replies_list.cpp
+++ b/Telegram/SourceFiles/data/data_replies_list.cpp
@@ -34,11 +34,11 @@ constexpr auto kMaxMessagesToDeleteMyTopic = 10;
 		not_null<History*> history,
 		TimeId date,
 		const QString &text) {
-	return history->makeMessage(
-		history->nextNonHistoryEntryId(),
-		MessageFlag::FakeHistoryItem,
-		date,
-		PreparedServiceText{ { .text = text } });
+	return history->makeMessage({
+		.id = history->nextNonHistoryEntryId(),
+		.flags = MessageFlag::FakeHistoryItem,
+		.date = date,
+	}, PreparedServiceText{ { .text = text } });
 }
 
 [[nodiscard]] bool IsCreating(not_null<History*> history, MsgId rootId) {
diff --git a/Telegram/SourceFiles/data/data_sponsored_messages.cpp b/Telegram/SourceFiles/data/data_sponsored_messages.cpp
index 556bee6ce..fb6f03308 100644
--- a/Telegram/SourceFiles/data/data_sponsored_messages.cpp
+++ b/Telegram/SourceFiles/data/data_sponsored_messages.cpp
@@ -80,7 +80,7 @@ bool SponsoredMessages::append(not_null<History*> history) {
 	entryIt->itemFullId = FullMsgId(
 		history->peer->id,
 		_session->data().nextLocalMessageId());
-	entryIt->item.reset(history->addNewLocalMessage(
+	entryIt->item.reset(history->addSponsoredMessage(
 		entryIt->itemFullId.msg,
 		entryIt->sponsored.from,
 		entryIt->sponsored.textWithEntities));
diff --git a/Telegram/SourceFiles/data/data_stories.cpp b/Telegram/SourceFiles/data/data_stories.cpp
index a35279be4..0090ba369 100644
--- a/Telegram/SourceFiles/data/data_stories.cpp
+++ b/Telegram/SourceFiles/data/data_stories.cpp
@@ -970,7 +970,7 @@ std::shared_ptr<HistoryItem> Stories::resolveItem(not_null<Story*> story) {
 	}
 	const auto history = _owner->history(story->peer());
 	auto result = std::shared_ptr<HistoryItem>(
-		history->makeMessage(story).get(),
+		history->makeMessage(StoryIdToMsgId(story->id()), story).get(),
 		HistoryItem::Destroyer());
 	i->second = result;
 	return result;
diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h
index 176a13f74..ca11c8969 100644
--- a/Telegram/SourceFiles/data/data_types.h
+++ b/Telegram/SourceFiles/data/data_types.h
@@ -318,6 +318,8 @@ enum class MessageFlag : uint64 {
 	Sponsored             = (1ULL << 42),
 
 	ReactionsAreTags      = (1ULL << 43),
+
+	ShortcutMessage       = (1ULL << 44),
 };
 inline constexpr bool is_flag_type(MessageFlag) { return true; }
 using MessageFlags = base::flags<MessageFlag>;
diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp
index b71b0cd82..c54093dc3 100644
--- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp
+++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp
@@ -801,13 +801,12 @@ void GenerateItems(
 		auto message = PreparedServiceText{ text };
 		message.links.push_back(fromLink);
 		addPart(
-			history->makeMessage(
-				history->nextNonHistoryEntryId(),
-				MessageFlag::AdminLogEntry,
-				date,
-				std::move(message),
-				peerToUser(from->id),
-				photo),
+			history->makeMessage({
+				.id = history->nextNonHistoryEntryId(),
+				.flags = MessageFlag::AdminLogEntry,
+				.from = from->id,
+				.date = date,
+			}, std::move(message), photo),
 			0,
 			realId);
 	};
@@ -826,23 +825,11 @@ void GenerateItems(
 	};
 
 	const auto makeSimpleTextMessage = [&](TextWithEntities &&text) {
-		const auto bodyFlags = MessageFlag::HasFromId
-			| MessageFlag::AdminLogEntry;
-		const auto bodyReplyTo = FullReplyTo();
-		const auto bodyViaBotId = UserId();
-		const auto bodyGroupedId = uint64();
-		return history->makeMessage(
-			history->nextNonHistoryEntryId(),
-			bodyFlags,
-			bodyReplyTo,
-			bodyViaBotId,
-			date,
-			peerToUser(from->id),
-			QString(),
-			std::move(text),
-			MTP_messageMediaEmpty(),
-			HistoryMessageMarkupData(),
-			bodyGroupedId);
+		return history->makeMessage({
+			.id = history->nextNonHistoryEntryId(),
+			.flags = MessageFlag::HasFromId | MessageFlag::AdminLogEntry,
+			.from = from->id,
+		}, std::move(text), MTP_messageMediaEmpty());
 	};
 
 	const auto addSimpleTextMessage = [&](TextWithEntities &&text) {
@@ -1145,12 +1132,12 @@ void GenerateItems(
 			auto message = PreparedServiceText{ text };
 			message.links.push_back(fromLink);
 			message.links.push_back(setLink);
-			addPart(history->makeMessage(
-				history->nextNonHistoryEntryId(),
-				MessageFlag::AdminLogEntry,
-				date,
-				std::move(message),
-				peerToUser(from->id)));
+			addPart(history->makeMessage({
+				.id = history->nextNonHistoryEntryId(),
+				.flags = MessageFlag::AdminLogEntry,
+				.from = from->id,
+				.date = date,
+			}, std::move(message)));
 		}
 	};
 
@@ -1189,12 +1176,12 @@ void GenerateItems(
 			auto message = PreparedServiceText{ text };
 			message.links.push_back(fromLink);
 			message.links.push_back(setLink);
-			addPart(history->makeMessage(
-				history->nextNonHistoryEntryId(),
-				MessageFlag::AdminLogEntry,
-				date,
-				std::move(message),
-				peerToUser(from->id)));
+			addPart(history->makeMessage({
+				.id = history->nextNonHistoryEntryId(),
+				.flags = MessageFlag::AdminLogEntry,
+				.from = from->id,
+				.date = date,
+			}, std::move(message)));
 		}
 	};
 
@@ -1270,12 +1257,12 @@ void GenerateItems(
 			auto message = PreparedServiceText{ text };
 			message.links.push_back(fromLink);
 			message.links.push_back(chatLink);
-			addPart(history->makeMessage(
-				history->nextNonHistoryEntryId(),
-				MessageFlag::AdminLogEntry,
-				date,
-				std::move(message),
-				peerToUser(from->id)));
+			addPart(history->makeMessage({
+				.id = history->nextNonHistoryEntryId(),
+				.flags = MessageFlag::AdminLogEntry,
+				.from = from->id,
+				.date = date,
+			}, std::move(message)));
 		}
 	};
 
@@ -1366,12 +1353,12 @@ void GenerateItems(
 		auto message = PreparedServiceText{ text };
 		message.links.push_back(fromLink);
 		message.links.push_back(link);
-		addPart(history->makeMessage(
-			history->nextNonHistoryEntryId(),
-			MessageFlag::AdminLogEntry,
-			date,
-			std::move(message),
-			peerToUser(from->id)));
+		addPart(history->makeMessage({
+			.id = history->nextNonHistoryEntryId(),
+			.flags = MessageFlag::AdminLogEntry,
+			.from = from->id,
+			.date = date,
+		}, std::move(message)));
 	};
 
 	const auto createParticipantMute = [&](const LogMute &data) {
@@ -1441,13 +1428,12 @@ void GenerateItems(
 		if (additional) {
 			message.links.push_back(std::move(additional));
 		}
-		addPart(history->makeMessage(
-			history->nextNonHistoryEntryId(),
-			MessageFlag::AdminLogEntry,
-			date,
-			std::move(message),
-			peerToUser(from->id),
-			nullptr));
+		addPart(history->makeMessage({
+			.id = history->nextNonHistoryEntryId(),
+			.flags = MessageFlag::AdminLogEntry,
+			.from = from->id,
+			.date = date,
+		}, std::move(message)));
 	};
 
 	const auto createParticipantJoinByInvite = [&](
diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp
index f5a9765c0..499bf7cea 100644
--- a/Telegram/SourceFiles/history/history.cpp
+++ b/Telegram/SourceFiles/history/history.cpp
@@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "history/history_translation.h"
 #include "history/history_unread_things.h"
 #include "dialogs/ui/dialogs_layout.h"
+#include "data/business/data_shortcut_messages.h"
 #include "data/notify/data_notify_settings.h"
 #include "data/stickers/data_stickers.h"
 #include "data/data_drafts.h"
@@ -71,6 +72,12 @@ constexpr auto kSkipCloudDraftsFor = TimeId(2);
 
 using UpdateFlag = Data::HistoryUpdate::Flag;
 
+[[nodiscard]] HistoryItemCommonFields WithLocalFlag(
+		HistoryItemCommonFields fields) {
+	fields.flags |= MessageFlag::Local;
+	return fields;
+}
+
 } // namespace
 
 History::History(not_null<Data::Session*> owner, PeerId peerId)
@@ -446,17 +453,17 @@ std::vector<not_null<HistoryItem*>> History::createItems(
 
 not_null<HistoryItem*> History::addNewMessage(
 		MsgId id,
-		const MTPMessage &msg,
+		const MTPMessage &message,
 		MessageFlags localFlags,
 		NewMessageType type) {
-	const auto detachExistingItem = (type == NewMessageType::Unread);
-	const auto item = createItem(id, msg, localFlags, detachExistingItem);
+	const auto detachExisting = (type == NewMessageType::Unread);
+	const auto item = createItem(id, message, localFlags, detachExisting);
 	if (type == NewMessageType::Existing || item->mainView()) {
 		return item;
 	}
 	const auto unread = (type == NewMessageType::Unread);
 	if (unread && item->isHistoryEntry()) {
-		applyMessageChanges(item, msg);
+		applyMessageChanges(item, message);
 	}
 	return addNewItem(item, unread);
 }
@@ -585,6 +592,9 @@ not_null<HistoryItem*> History::addNewItem(
 	if (item->isScheduled()) {
 		owner().scheduledMessages().appendSending(item);
 		return item;
+	} else if (item->isBusinessShortcut()) {
+		owner().shortcutMessages().appendSending(item);
+		return item;
 	} else if (!item->isHistoryEntry()) {
 		return item;
 	}
@@ -635,139 +645,54 @@ void History::checkForLoadedAtTop(not_null<HistoryItem*> added) {
 }
 
 not_null<HistoryItem*> History::addNewLocalMessage(
-		MsgId id,
-		MessageFlags flags,
-		UserId viaBotId,
-		FullReplyTo replyTo,
-		TimeId date,
-		PeerId from,
-		const QString &postAuthor,
+		HistoryItemCommonFields &&fields,
 		const TextWithEntities &text,
-		const MTPMessageMedia &media,
-		HistoryMessageMarkupData &&markup,
-		uint64 groupedId) {
+		const MTPMessageMedia &media) {
 	return addNewItem(
-		makeMessage(
-			id,
-			flags | MessageFlag::Local,
-			replyTo,
-			viaBotId,
-			date,
-			from,
-			postAuthor,
-			text,
-			media,
-			std::move(markup),
-			groupedId),
+		makeMessage(WithLocalFlag(std::move(fields)), text, media),
 		true);
 }
 
 not_null<HistoryItem*> History::addNewLocalMessage(
-		MsgId id,
-		MessageFlags flags,
-		TimeId date,
-		PeerId from,
-		const QString &postAuthor,
-		not_null<HistoryItem*> forwardOriginal,
-		MsgId topicRootId) {
+		HistoryItemCommonFields &&fields,
+		not_null<HistoryItem*> forwardOriginal) {
 	return addNewItem(
-		makeMessage(
-			id,
-			flags | MessageFlag::Local,
-			date,
-			from,
-			postAuthor,
-			forwardOriginal,
-			topicRootId),
+		makeMessage(WithLocalFlag(std::move(fields)), forwardOriginal),
 		true);
 }
 
 not_null<HistoryItem*> History::addNewLocalMessage(
-		MsgId id,
-		MessageFlags flags,
-		UserId viaBotId,
-		FullReplyTo replyTo,
-		TimeId date,
-		PeerId from,
-		const QString &postAuthor,
+		HistoryItemCommonFields &&fields,
 		not_null<DocumentData*> document,
-		const TextWithEntities &caption,
-		HistoryMessageMarkupData &&markup) {
+		const TextWithEntities &caption) {
 	return addNewItem(
-		makeMessage(
-			id,
-			flags | MessageFlag::Local,
-			replyTo,
-			viaBotId,
-			date,
-			from,
-			postAuthor,
-			document,
-			caption,
-			std::move(markup)),
+		makeMessage(WithLocalFlag(std::move(fields)), document, caption),
 		true);
 }
 
 not_null<HistoryItem*> History::addNewLocalMessage(
-		MsgId id,
-		MessageFlags flags,
-		UserId viaBotId,
-		FullReplyTo replyTo,
-		TimeId date,
-		PeerId from,
-		const QString &postAuthor,
+		HistoryItemCommonFields &&fields,
 		not_null<PhotoData*> photo,
-		const TextWithEntities &caption,
-		HistoryMessageMarkupData &&markup) {
+		const TextWithEntities &caption) {
 	return addNewItem(
-		makeMessage(
-			id,
-			flags | MessageFlag::Local,
-			replyTo,
-			viaBotId,
-			date,
-			from,
-			postAuthor,
-			photo,
-			caption,
-			std::move(markup)),
+		makeMessage(WithLocalFlag(std::move(fields)), photo, caption),
 		true);
 }
 
 not_null<HistoryItem*> History::addNewLocalMessage(
-		MsgId id,
-		MessageFlags flags,
-		UserId viaBotId,
-		FullReplyTo replyTo,
-		TimeId date,
-		PeerId from,
-		const QString &postAuthor,
-		not_null<GameData*> game,
-		HistoryMessageMarkupData &&markup) {
+		HistoryItemCommonFields &&fields,
+		not_null<GameData*> game) {
 	return addNewItem(
-		makeMessage(
-			id,
-			flags | MessageFlag::Local,
-			replyTo,
-			viaBotId,
-			date,
-			from,
-			postAuthor,
-			game,
-			std::move(markup)),
+		makeMessage(WithLocalFlag(std::move(fields)), game),
 		true);
 }
 
-not_null<HistoryItem*> History::addNewLocalMessage(
+not_null<HistoryItem*> History::addSponsoredMessage(
 		MsgId id,
 		Data::SponsoredFrom from,
 		const TextWithEntities &textWithEntities) {
 	return addNewItem(
-		makeMessage(
-			id,
-			from,
-			textWithEntities,
-			nullptr),
+		makeMessage(id, from, textWithEntities, nullptr),
 		true);
 }
 
diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h
index adeb63fc2..29620dc2e 100644
--- a/Telegram/SourceFiles/history/history.h
+++ b/Telegram/SourceFiles/history/history.h
@@ -20,6 +20,7 @@ class History;
 class HistoryBlock;
 class HistoryTranslation;
 class HistoryItem;
+struct HistoryItemCommonFields;
 struct HistoryMessageMarkupData;
 class HistoryMainElementDelegateMixin;
 struct LanguageId;
@@ -127,11 +128,23 @@ public:
 	void applyGroupAdminChanges(const base::flat_set<UserId> &changes);
 
 	template <typename ...Args>
-	not_null<HistoryItem*> makeMessage(Args &&...args) {
+	not_null<HistoryItem*> makeMessage(MsgId id, Args &&...args) {
 		return static_cast<HistoryItem*>(
 			insertItem(
 				std::make_unique<HistoryItem>(
 					this,
+					id,
+					std::forward<Args>(args)...)).get());
+	}
+	template <typename ...Args>
+	not_null<HistoryItem*> makeMessage(
+			HistoryItemCommonFields &&fields,
+			Args &&...args) {
+		return static_cast<HistoryItem*>(
+			insertItem(
+				std::make_unique<HistoryItem>(
+					this,
+					std::move(fields),
 					std::forward<Args>(args)...)).get());
 	}
 
@@ -143,62 +156,30 @@ public:
 
 	not_null<HistoryItem*> addNewMessage(
 		MsgId id,
-		const MTPMessage &msg,
+		const MTPMessage &message,
 		MessageFlags localFlags,
 		NewMessageType type);
+
 	not_null<HistoryItem*> addNewLocalMessage(
-		MsgId id,
-		MessageFlags flags,
-		UserId viaBotId,
-		FullReplyTo replyTo,
-		TimeId date,
-		PeerId from,
-		const QString &postAuthor,
+		HistoryItemCommonFields &&fields,
 		const TextWithEntities &text,
-		const MTPMessageMedia &media,
-		HistoryMessageMarkupData &&markup,
-		uint64 groupedId = 0);
+		const MTPMessageMedia &media);
 	not_null<HistoryItem*> addNewLocalMessage(
-		MsgId id,
-		MessageFlags flags,
-		TimeId date,
-		PeerId from,
-		const QString &postAuthor,
-		not_null<HistoryItem*> forwardOriginal,
-		MsgId topicRootId);
+		HistoryItemCommonFields &&fields,
+		not_null<HistoryItem*> forwardOriginal);
 	not_null<HistoryItem*> addNewLocalMessage(
-		MsgId id,
-		MessageFlags flags,
-		UserId viaBotId,
-		FullReplyTo replyTo,
-		TimeId date,
-		PeerId from,
-		const QString &postAuthor,
+		HistoryItemCommonFields &&fields,
 		not_null<DocumentData*> document,
-		const TextWithEntities &caption,
-		HistoryMessageMarkupData &&markup);
+		const TextWithEntities &caption);
 	not_null<HistoryItem*> addNewLocalMessage(
-		MsgId id,
-		MessageFlags flags,
-		UserId viaBotId,
-		FullReplyTo replyTo,
-		TimeId date,
-		PeerId from,
-		const QString &postAuthor,
+		HistoryItemCommonFields &&fields,
 		not_null<PhotoData*> photo,
-		const TextWithEntities &caption,
-		HistoryMessageMarkupData &&markup);
-	not_null<HistoryItem*> addNewLocalMessage(
-		MsgId id,
-		MessageFlags flags,
-		UserId viaBotId,
-		FullReplyTo replyTo,
-		TimeId date,
-		PeerId from,
-		const QString &postAuthor,
-		not_null<GameData*> game,
-		HistoryMessageMarkupData &&markup);
+		const TextWithEntities &caption);
 	not_null<HistoryItem*> addNewLocalMessage(
+		HistoryItemCommonFields &&fields,
+		not_null<GameData*> game);
+
+	not_null<HistoryItem*> addSponsoredMessage(
 		MsgId id,
 		Data::SponsoredFrom from,
 		const TextWithEntities &textWithEntities); // sponsored
diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp
index aa815ccc9..1f75a47d8 100644
--- a/Telegram/SourceFiles/history/history_item.cpp
+++ b/Telegram/SourceFiles/history/history_item.cpp
@@ -125,6 +125,14 @@ template <typename T>
 	return false;
 }
 
+[[nodiscard]] HistoryItemCommonFields ForwardedFields(
+		HistoryItemCommonFields fields,
+		not_null<History*> history,
+		not_null<HistoryItem*> original) {
+	fields.flags |= NewForwardedFlags(history->peer, fields.from, original);
+	return fields;
+}
+
 } // namespace
 
 void HistoryItem::HistoryItem::Destroyer::operator()(HistoryItem *value) {
@@ -347,12 +355,13 @@ HistoryItem::HistoryItem(
 	MsgId id,
 	const MTPDmessage &data,
 	MessageFlags localFlags)
-: HistoryItem(
-		history,
-		id,
-		FlagsFromMTP(id, data.vflags().v, localFlags),
-		data.vdate().v,
-		data.vfrom_id() ? peerFromMTP(*data.vfrom_id()) : PeerId(0)) {
+: HistoryItem(history, {
+	.id = id,
+	.flags = FlagsFromMTP(id, data.vflags().v, localFlags),
+	.from = data.vfrom_id() ? peerFromMTP(*data.vfrom_id()) : PeerId(0),
+	.date = data.vdate().v,
+	.shortcutId = data.vquick_reply_shortcut_id().value_or_empty(),
+}) {
 	_boostsApplied = data.vfrom_boosts_applied().value_or_empty();
 
 	const auto media = data.vmedia();
@@ -406,12 +415,12 @@ HistoryItem::HistoryItem(
 	MsgId id,
 	const MTPDmessageService &data,
 	MessageFlags localFlags)
-: HistoryItem(
-		history,
-		id,
-		FlagsFromMTP(id, data.vflags().v, localFlags),
-		data.vdate().v,
-		data.vfrom_id() ? peerFromMTP(*data.vfrom_id()) : PeerId(0)) {
+: HistoryItem(history, {
+	.id = id,
+	.flags = FlagsFromMTP(id, data.vflags().v, localFlags),
+	.from = data.vfrom_id() ? peerFromMTP(*data.vfrom_id()) : PeerId(0),
+	.date = data.vdate().v,
+}) {
 	if (data.vaction().type() != mtpc_messageActionPhoneCall) {
 		createServiceFromMtp(data);
 	} else {
@@ -431,9 +440,7 @@ HistoryItem::HistoryItem(
 	MessageFlags localFlags)
 : HistoryItem(
 	history,
-	id,
-	localFlags,
-	TimeId(0),
+	{ .id = id, .flags = localFlags },
 	PreparedServiceText{ tr::lng_message_empty(
 		tr::now,
 		Ui::Text::WithEntities) }) {
@@ -441,13 +448,10 @@ HistoryItem::HistoryItem(
 
 HistoryItem::HistoryItem(
 	not_null<History*> history,
-	MsgId id,
-	MessageFlags flags,
-	TimeId date,
+	HistoryItemCommonFields &&fields,
 	PreparedServiceText &&message,
-	PeerId from,
 	PhotoData *photo)
-: HistoryItem(history, id, flags, date, from) {
+: HistoryItem(history, fields) {
 	setServiceText(std::move(message));
 	if (photo) {
 		_media = std::make_unique<Data::MediaPhoto>(
@@ -459,25 +463,16 @@ HistoryItem::HistoryItem(
 
 HistoryItem::HistoryItem(
 	not_null<History*> history,
-	MsgId id,
-	MessageFlags flags,
-	TimeId date,
-	PeerId from,
-	const QString &postAuthor,
-	not_null<HistoryItem*> original,
-	MsgId topicRootId)
-: HistoryItem(
-		history,
-		id,
-		(NewForwardedFlags(history->peer, from, original) | flags),
-		date,
-		from) {
+	HistoryItemCommonFields &&fields,
+	not_null<HistoryItem*> original)
+: HistoryItem(history, ForwardedFields(fields, history, original)) {
 	const auto peer = history->peer;
 
 	auto config = CreateConfig();
 
 	const auto originalMedia = original->media();
 	const auto dropForwardInfo = original->computeDropForwardedInfo();
+	const auto topicRootId = fields.replyTo.topicRootId;
 	config.reply.messageId = config.reply.topMessageId = topicRootId;
 	config.reply.topicPost = (topicRootId != 0) ? 1 : 0;
 	if (const auto originalReply = original->Get<HistoryMessageReply>()) {
@@ -520,8 +515,8 @@ HistoryItem::HistoryItem(
 			? original->author()->id
 			: PeerId();
 	}
-	if (flags & MessageFlag::HasPostAuthor) {
-		config.postAuthor = postAuthor;
+	if (_flags & MessageFlag::HasPostAuthor) {
+		config.postAuthor = fields.postAuthor;
 	}
 	if (const auto fwdViaBot = original->viaBot()) {
 		config.viaBotId = peerToUser(fwdViaBot->id);
@@ -571,63 +566,28 @@ HistoryItem::HistoryItem(
 
 HistoryItem::HistoryItem(
 	not_null<History*> history,
-	MsgId id,
-	MessageFlags flags,
-	FullReplyTo replyTo,
-	UserId viaBotId,
-	TimeId date,
-	PeerId from,
-	const QString &postAuthor,
+	HistoryItemCommonFields &&fields,
 	const TextWithEntities &textWithEntities,
-	const MTPMessageMedia &media,
-	HistoryMessageMarkupData &&markup,
-	uint64 groupedId)
-: HistoryItem(
-		history,
-		id,
-		flags,
-		date,
-		(flags & MessageFlag::HasFromId) ? from : 0) {
-	createComponentsHelper(
-		flags,
-		replyTo,
-		viaBotId,
-		postAuthor,
-		std::move(markup));
+	const MTPMessageMedia &media)
+: HistoryItem(history, fields) {
+	createComponentsHelper(std::move(fields));
 	setMedia(media);
 	setText(textWithEntities);
-	if (groupedId) {
+	if (fields.groupedId) {
 		setGroupId(MessageGroupId::FromRaw(
 			history->peer->id,
-			groupedId,
-			flags & MessageFlag::IsOrWasScheduled));
+			fields.groupedId,
+			_flags & MessageFlag::IsOrWasScheduled));
 	}
 }
 
 HistoryItem::HistoryItem(
 	not_null<History*> history,
-	MsgId id,
-	MessageFlags flags,
-	FullReplyTo replyTo,
-	UserId viaBotId,
-	TimeId date,
-	PeerId from,
-	const QString &postAuthor,
+	HistoryItemCommonFields &&fields,
 	not_null<DocumentData*> document,
-	const TextWithEntities &caption,
-	HistoryMessageMarkupData &&markup)
-: HistoryItem(
-		history,
-		id,
-		flags,
-		date,
-		(flags & MessageFlag::HasFromId) ? from : 0) {
-	createComponentsHelper(
-		flags,
-		replyTo,
-		viaBotId,
-		postAuthor,
-		std::move(markup));
+	const TextWithEntities &caption)
+: HistoryItem(history, fields) {
+	createComponentsHelper(std::move(fields));
 
 	const auto skipPremiumEffect = !history->session().premium();
 	const auto spoiler = false;
@@ -642,28 +602,11 @@ HistoryItem::HistoryItem(
 
 HistoryItem::HistoryItem(
 	not_null<History*> history,
-	MsgId id,
-	MessageFlags flags,
-	FullReplyTo replyTo,
-	UserId viaBotId,
-	TimeId date,
-	PeerId from,
-	const QString &postAuthor,
+	HistoryItemCommonFields &&fields,
 	not_null<PhotoData*> photo,
-	const TextWithEntities &caption,
-	HistoryMessageMarkupData &&markup)
-: HistoryItem(
-		history,
-		id,
-		flags,
-		date,
-		(flags & MessageFlag::HasFromId) ? from : 0) {
-	createComponentsHelper(
-		flags,
-		replyTo,
-		viaBotId,
-		postAuthor,
-		std::move(markup));
+	const TextWithEntities &caption)
+: HistoryItem(history, fields) {
+	createComponentsHelper(std::move(fields));
 
 	const auto spoiler = false;
 	_media = std::make_unique<Data::MediaPhoto>(this, photo, spoiler);
@@ -672,27 +615,10 @@ HistoryItem::HistoryItem(
 
 HistoryItem::HistoryItem(
 	not_null<History*> history,
-	MsgId id,
-	MessageFlags flags,
-	FullReplyTo replyTo,
-	UserId viaBotId,
-	TimeId date,
-	PeerId from,
-	const QString &postAuthor,
-	not_null<GameData*> game,
-	HistoryMessageMarkupData &&markup)
-: HistoryItem(
-		history,
-		id,
-		flags,
-		date,
-		(flags & MessageFlag::HasFromId) ? from : 0) {
-	createComponentsHelper(
-		flags,
-		replyTo,
-		viaBotId,
-		postAuthor,
-		std::move(markup));
+	HistoryItemCommonFields &&fields,
+	not_null<GameData*> game)
+: HistoryItem(history, fields) {
+	createComponentsHelper(std::move(fields));
 
 	_media = std::make_unique<Data::MediaGame>(this, game);
 	setTextValue({});
@@ -704,18 +630,15 @@ HistoryItem::HistoryItem(
 	Data::SponsoredFrom from,
 	const TextWithEntities &textWithEntities,
 	HistoryItem *injectedAfter)
-: HistoryItem(
-		history,
-		id,
-		((history->peer->isChannel() ? MessageFlag::Post : MessageFlag(0))
-			//| (from.peer ? MessageFlag::HasFromId : MessageFlag(0))
-			| MessageFlag::Local),
-		HistoryItem::NewMessageDate(injectedAfter
-			? injectedAfter->date()
-			: 0),
-		/*from.peer ? from.peer->id : */PeerId(0)) {
-	_flags |= MessageFlag::Sponsored;
-
+: HistoryItem(history, {
+	.id = id,
+	.flags = (MessageFlag::Local
+		| MessageFlag::Sponsored
+		| (history->peer->isChannel() ? MessageFlag::Post : MessageFlag(0))),
+	.date = HistoryItem::NewMessageDate(injectedAfter
+		? injectedAfter->date()
+		: 0),
+}) {
 	const auto webPageType = !from.externalLink.isEmpty()
 		? WebPageType::None
 		: from.isExactPost
@@ -758,15 +681,15 @@ HistoryItem::HistoryItem(
 
 HistoryItem::HistoryItem(
 	not_null<History*> history,
-	MsgId id,
-	MessageFlags flags,
-	TimeId date,
-	PeerId from)
-: id(id)
+	const HistoryItemCommonFields &fields)
+: id(fields.id)
 , _history(history)
-, _from(from ? history->owner().peer(from) : history->peer)
-, _flags(FinalizeMessageFlags(history, flags))
-, _date(date) {
+, _from((fields.flags & MessageFlag::HasFromId && fields.from)
+	? history->owner().peer(fields.from)
+	: history->peer)
+, _flags(FinalizeMessageFlags(history, fields.flags))
+, _date(fields.date)
+, _shortcutId(fields.shortcutId) {
 	if (isHistoryEntry() && IsClientMsgId(id)) {
 		_history->registerClientSideMessage(this);
 	}
@@ -774,15 +697,18 @@ HistoryItem::HistoryItem(
 
 HistoryItem::HistoryItem(
 	not_null<History*> history,
+	MsgId id,
 	not_null<Data::Story*> story)
-: id(StoryIdToMsgId(story->id()))
-, _history(history)
-, _from(history->peer)
-, _flags(MessageFlag::Local
-	| MessageFlag::Outgoing
-	| MessageFlag::FakeHistoryItem
-	| MessageFlag::StoryItem)
-, _date(story->date()) {
+: HistoryItem(history, {
+	.id = id,
+	.flags = (MessageFlag::Local
+		| MessageFlag::Outgoing
+		| MessageFlag::HasFromId
+		| MessageFlag::FakeHistoryItem
+		| MessageFlag::StoryItem),
+	.from = history->peer->id,
+	.date = story->date(),
+}) {
 	setStoryFields(story);
 }
 
@@ -807,6 +733,11 @@ TimeId HistoryItem::NewMessageDate(TimeId scheduled) {
 	return scheduled ? scheduled : base::unixtime::now();
 }
 
+TimeId HistoryItem::NewMessageDate(
+		const Api::SendOptions &options) {
+	return options.shortcutId ? TimeId() : NewMessageDate(options.scheduled);
+}
+
 HistoryServiceDependentData *HistoryItem::GetServiceDependentData() {
 	if (const auto pinned = Get<HistoryServicePinned>()) {
 		return pinned;
@@ -1603,6 +1534,14 @@ bool HistoryItem::isUserpicSuggestion() const {
 	return (_flags & MessageFlag::IsUserpicSuggestion);
 }
 
+BusinessShortcutId HistoryItem::shortcutId() const {
+	return _shortcutId;
+}
+
+bool HistoryItem::isBusinessShortcut() const {
+	return _shortcutId != 0;
+}
+
 void HistoryItem::destroy() {
 	_history->destroyMessage(this);
 }
@@ -3520,15 +3459,11 @@ TextWithEntities HistoryItem::withLocalEntities(
 	return textWithEntities;
 }
 
-void HistoryItem::createComponentsHelper(
-		MessageFlags flags,
-		FullReplyTo replyTo,
-		UserId viaBotId,
-		const QString &postAuthor,
-		HistoryMessageMarkupData &&markup) {
+void HistoryItem::createComponentsHelper(HistoryItemCommonFields &&fields) {
+	const auto &replyTo = fields.replyTo;
 	auto config = CreateConfig();
-	config.viaBotId = viaBotId;
-	if (flags & MessageFlag::HasReplyInfo) {
+	config.viaBotId = fields.viaBotId;
+	if (fields.flags & MessageFlag::HasReplyInfo) {
 		config.reply.messageId = replyTo.messageId.msg;
 		config.reply.storyId = replyTo.storyId.story;
 		config.reply.externalPeerId = replyTo.storyId
@@ -3567,9 +3502,13 @@ void HistoryItem::createComponentsHelper(
 		config.reply.quoteOffset = replyTo.quoteOffset;
 		config.reply.quote = std::move(replyTo.quote);
 	}
-	config.markup = std::move(markup);
-	if (flags & MessageFlag::HasPostAuthor) config.postAuthor = postAuthor;
-	if (flags & MessageFlag::HasViews) config.viewsCount = 1;
+	config.markup = std::move(fields.markup);
+	if (fields.flags & MessageFlag::HasPostAuthor) {
+		config.postAuthor = fields.postAuthor;
+	}
+	if (fields.flags & MessageFlag::HasViews) {
+		config.viewsCount = 1;
+	}
 
 	createComponents(std::move(config));
 }
diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h
index febc8d67c..f287bb039 100644
--- a/Telegram/SourceFiles/history/history_item.h
+++ b/Telegram/SourceFiles/history/history_item.h
@@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 class HiddenSenderInfo;
 class History;
+
 struct HistoryMessageReply;
 struct HistoryMessageViews;
 struct HistoryMessageMarkupData;
@@ -28,6 +29,10 @@ struct PreparedServiceText;
 class ReplyKeyboard;
 struct LanguageId;
 
+namespace Api {
+struct SendOptions;
+} // namespace Api
+
 namespace base {
 template <typename Enum>
 class enum_mask;
@@ -86,6 +91,19 @@ class Service;
 class ServiceMessagePainter;
 } // namespace HistoryView
 
+struct HistoryItemCommonFields {
+	MsgId id = 0;
+	MessageFlags flags = 0;
+	PeerId from = 0;
+	FullReplyTo replyTo;
+	TimeId date = 0;
+	BusinessShortcutId shortcutId = 0;
+	UserId viaBotId = 0;
+	QString postAuthor;
+	uint64 groupedId = 0;
+	HistoryMessageMarkupData markup;
+};
+
 class HistoryItem final : public RuntimeComposer<HistoryItem> {
 public:
 	[[nodiscard]] static std::unique_ptr<Data::Media> CreateMedia(
@@ -114,73 +132,39 @@ public:
 		Data::SponsoredFrom from,
 		const TextWithEntities &textWithEntities,
 		HistoryItem *injectedAfter);
+	HistoryItem( // Story wrap.
+		not_null<History*> history,
+		MsgId id,
+		not_null<Data::Story*> story);
 
 	HistoryItem( // Local message.
 		not_null<History*> history,
-		MsgId id,
-		MessageFlags flags,
-		FullReplyTo replyTo,
-		UserId viaBotId,
-		TimeId date,
-		PeerId from,
-		const QString &postAuthor,
+		HistoryItemCommonFields &&fields,
 		const TextWithEntities &textWithEntities,
-		const MTPMessageMedia &media,
-		HistoryMessageMarkupData &&markup,
-		uint64 groupedId);
+		const MTPMessageMedia &media);
 	HistoryItem( // Local service message.
 		not_null<History*> history,
-		MsgId id,
-		MessageFlags flags,
-		TimeId date,
+		HistoryItemCommonFields &&fields,
 		PreparedServiceText &&message,
-		PeerId from = 0,
 		PhotoData *photo = nullptr);
 	HistoryItem( // Local forwarded.
 		not_null<History*> history,
-		MsgId id,
-		MessageFlags flags,
-		TimeId date,
-		PeerId from,
-		const QString &postAuthor,
-		not_null<HistoryItem*> original,
-		MsgId topicRootId);
+		HistoryItemCommonFields &&fields,
+		not_null<HistoryItem*> original);
 	HistoryItem( // Local photo.
 		not_null<History*> history,
-		MsgId id,
-		MessageFlags flags,
-		FullReplyTo replyTo,
-		UserId viaBotId,
-		TimeId date,
-		PeerId from,
-		const QString &postAuthor,
+		HistoryItemCommonFields &&fields,
 		not_null<PhotoData*> photo,
-		const TextWithEntities &caption,
-		HistoryMessageMarkupData &&markup);
+		const TextWithEntities &caption);
 	HistoryItem( // Local document.
 		not_null<History*> history,
-		MsgId id,
-		MessageFlags flags,
-		FullReplyTo replyTo,
-		UserId viaBotId,
-		TimeId date,
-		PeerId from,
-		const QString &postAuthor,
+		HistoryItemCommonFields &&fields,
 		not_null<DocumentData*> document,
-		const TextWithEntities &caption,
-		HistoryMessageMarkupData &&markup);
+		const TextWithEntities &caption);
 	HistoryItem( // Local game.
 		not_null<History*> history,
-		MsgId id,
-		MessageFlags flags,
-		FullReplyTo replyTo,
-		UserId viaBotId,
-		TimeId date,
-		PeerId from,
-		const QString &postAuthor,
-		not_null<GameData*> game,
-		HistoryMessageMarkupData &&markup);
-	HistoryItem(not_null<History*> history, not_null<Data::Story*> story);
+		HistoryItemCommonFields &&fields,
+		not_null<GameData*> game);
 	~HistoryItem();
 
 	struct Destroyer {
@@ -210,12 +194,8 @@ public:
 	[[nodiscard]] bool isSponsored() const;
 	[[nodiscard]] bool skipNotification() const;
 	[[nodiscard]] bool isUserpicSuggestion() const;
-	[[nodiscard]] BusinessShortcutId shortcutId() const {
-		return _shortcutId;
-	}
-	[[nodiscard]] bool isBusinessShortcut() const {
-		return _shortcutId != 0;
-	}
+	[[nodiscard]] BusinessShortcutId shortcutId() const;
+	[[nodiscard]] bool isBusinessShortcut() const;
 
 	void addLogEntryOriginal(
 		WebPageId localId,
@@ -473,6 +453,8 @@ public:
 	[[nodiscard]] TimeId date() const;
 
 	[[nodiscard]] static TimeId NewMessageDate(TimeId scheduled);
+	[[nodiscard]] static TimeId NewMessageDate(
+		const Api::SendOptions &options);
 
 	[[nodiscard]] Data::Media *media() const {
 		return _media.get();
@@ -554,17 +536,9 @@ private:
 
 	HistoryItem(
 		not_null<History*> history,
-		MsgId id,
-		MessageFlags flags,
-		TimeId date,
-		PeerId from);
+		const HistoryItemCommonFields &fields);
 
-	void createComponentsHelper(
-		MessageFlags flags,
-		FullReplyTo replyTo,
-		UserId viaBotId,
-		const QString &postAuthor,
-		HistoryMessageMarkupData &&markup);
+	void createComponentsHelper(HistoryItemCommonFields &&fields);
 	void createComponents(CreateConfig &&config);
 	void setupForwardedComponent(const CreateConfig &config);
 
diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp
index ae6d3dc3a..ee5d1d21f 100644
--- a/Telegram/SourceFiles/history/history_item_components.cpp
+++ b/Telegram/SourceFiles/history/history_item_components.cpp
@@ -301,6 +301,7 @@ ReplyFields ReplyFieldsFromMTP(
 		if (const auto id = data.vreply_to_msg_id().value_or_empty()) {
 			result.messageId = data.is_reply_to_scheduled()
 				? owner->scheduledMessages().localMessageId(id)
+				AssertIsDebug()
 				: id;
 			result.topMessageId
 				= data.vreply_to_top_id().value_or(id);
diff --git a/Telegram/SourceFiles/history/history_item_helpers.cpp b/Telegram/SourceFiles/history/history_item_helpers.cpp
index 752560a7f..65153bf86 100644
--- a/Telegram/SourceFiles/history/history_item_helpers.cpp
+++ b/Telegram/SourceFiles/history/history_item_helpers.cpp
@@ -386,6 +386,9 @@ MessageFlags FlagsFromMTP(
 		| ((flags & MTP::f_from_id) ? Flag::HasFromId : Flag())
 		| ((flags & MTP::f_reply_to) ? Flag::HasReplyInfo : Flag())
 		| ((flags & MTP::f_reply_markup) ? Flag::HasReplyMarkup : Flag())
+		| ((flags & MTP::f_quick_reply_shortcut_id)
+			? Flag::ShortcutMessage
+			: Flag())
 		| ((flags & MTP::f_from_scheduled)
 			? Flag::IsOrWasScheduled
 			: Flag())
@@ -598,11 +601,11 @@ not_null<HistoryItem*> GenerateJoinedMessage(
 		TimeId inviteDate,
 		not_null<UserData*> inviter,
 		bool viaRequest) {
-	return history->makeMessage(
-		history->owner().nextLocalMessageId(),
-		MessageFlag::Local | MessageFlag::ShowSimilarChannels,
-		inviteDate,
-		GenerateJoinedText(history, inviter, viaRequest));
+	return history->makeMessage({
+		.id = history->owner().nextLocalMessageId(),
+		.flags = MessageFlag::Local | MessageFlag::ShowSimilarChannels,
+		.date = inviteDate,
+	}, GenerateJoinedText(history, inviter, viaRequest));
 }
 
 std::optional<bool> PeerHasThisCall(
@@ -657,6 +660,7 @@ std::optional<bool> PeerHasThisCall(
 		MessageFlags flags) {
 	if (!(flags & MessageFlag::FakeHistoryItem)
 		&& !(flags & MessageFlag::IsOrWasScheduled)
+		&& !(flags & MessageFlag::ShortcutMessage)
 		&& !(flags & MessageFlag::AdminLogEntry)) {
 		flags |= MessageFlag::HistoryEntry;
 		if (history->peer->isSelf()) {
diff --git a/Telegram/SourceFiles/history/view/controls/history_view_draft_options.cpp b/Telegram/SourceFiles/history/view/controls/history_view_draft_options.cpp
index 5ba81cd1e..a0d3d6066 100644
--- a/Telegram/SourceFiles/history/view/controls/history_view_draft_options.cpp
+++ b/Telegram/SourceFiles/history/view/controls/history_view_draft_options.cpp
@@ -257,38 +257,32 @@ rpl::producer<QString> PreviewWrap::showLinkSelector(
 		was->destroy();
 	}
 	using Flag = MTPDmessageMediaWebPage::Flag;
-	_draftItem = _history->addNewLocalMessage(
-		_history->nextNonHistoryEntryId(),
-		(MessageFlag::FakeHistoryItem
+	_draftItem = _history->addNewLocalMessage({
+		.id = _history->nextNonHistoryEntryId(),
+		.flags = (MessageFlag::FakeHistoryItem
 			| MessageFlag::Outgoing
 			| MessageFlag::HasFromId
 			| (webpage.invert ? MessageFlag::InvertMedia : MessageFlag())),
-		UserId(), // via
-		FullReplyTo(),
-		base::unixtime::now(), // date
-		_history->session().userPeerId(),
-		QString(), // postAuthor
-		HighlightParsedLinks({
-			message.text,
-			TextUtilities::ConvertTextTagsToEntities(message.tags),
-		}, links),
-		MTP_messageMediaWebPage(
-			MTP_flags(Flag()
-				| (webpage.forceLargeMedia
-					? Flag::f_force_large_media
-					: Flag())
-				| (webpage.forceSmallMedia
-					? Flag::f_force_small_media
-					: Flag())),
-			MTP_webPagePending(
-				MTP_flags(webpage.url.isEmpty()
-					? MTPDwebPagePending::Flag()
-					: MTPDwebPagePending::Flag::f_url),
-				MTP_long(webpage.id),
-				MTP_string(webpage.url),
-				MTP_int(0))),
-		HistoryMessageMarkupData(),
-		uint64(0)); // groupedId
+		.from = _history->session().userPeerId(),
+		.date = base::unixtime::now(),
+	}, HighlightParsedLinks({
+		message.text,
+		TextUtilities::ConvertTextTagsToEntities(message.tags),
+	}, links), MTP_messageMediaWebPage(
+		MTP_flags(Flag()
+			| (webpage.forceLargeMedia
+				? Flag::f_force_large_media
+				: Flag())
+			| (webpage.forceSmallMedia
+				? Flag::f_force_small_media
+				: Flag())),
+		MTP_webPagePending(
+			MTP_flags(webpage.url.isEmpty()
+				? MTPDwebPagePending::Flag()
+				: MTPDwebPagePending::Flag::f_url),
+			MTP_long(webpage.id),
+			MTP_string(webpage.url),
+			MTP_int(0))));
 	_element = _draftItem->createView(_delegate.get());
 	_selectType = TextSelectType::Letters;
 	_symbol = _selectionStartSymbol = 0;
diff --git a/Telegram/SourceFiles/history/view/history_view_about_view.cpp b/Telegram/SourceFiles/history/view/history_view_about_view.cpp
index 51ae055f3..c017d8842 100644
--- a/Telegram/SourceFiles/history/view/history_view_about_view.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_about_view.cpp
@@ -188,54 +188,39 @@ bool AboutView::refresh() {
 }
 
 AdminLog::OwnedItem AboutView::makeAboutBot(not_null<BotInfo*> info) {
-	const auto flags = MessageFlag::FakeAboutView
-		| MessageFlag::FakeHistoryItem
-		| MessageFlag::Local;
-	const auto postAuthor = QString();
-	const auto date = TimeId(0);
-	const auto replyTo = FullReplyTo();
-	const auto viaBotId = UserId(0);
-	const auto groupedId = uint64(0);
 	const auto textWithEntities = TextUtilities::ParseEntities(
 		info->description,
 		Ui::ItemTextBotNoMonoOptions().flags);
-	const auto make = [&](auto &&a, auto &&b, auto &&...other) {
-		return _history->makeMessage(
-			_history->nextNonHistoryEntryId(),
-			flags,
-			replyTo,
-			viaBotId,
-			date,
-			_history->peer->id,
-			postAuthor,
-			std::forward<decltype(a)>(a),
-			std::forward<decltype(b)>(b),
-			HistoryMessageMarkupData(),
-			std::forward<decltype(other)>(other)...);
+	const auto make = [&](auto &&...args) {
+		return _history->makeMessage({
+			.id = _history->nextNonHistoryEntryId(),
+			.flags = (MessageFlag::FakeAboutView
+				| MessageFlag::FakeHistoryItem
+				| MessageFlag::Local),
+			.from = _history->peer->id,
+		}, std::forward<decltype(args)>(args)...);
 	};
 	const auto item = info->document
 		? make(info->document, textWithEntities)
 		: info->photo
 		? make(info->photo, textWithEntities)
-		: make(textWithEntities, MTP_messageMediaEmpty(), groupedId);
+		: make(textWithEntities, MTP_messageMediaEmpty());
 	return AdminLog::OwnedItem(_delegate, item);
 }
 
 AdminLog::OwnedItem AboutView::makePremiumRequired() {
-	const auto flags = MessageFlag::FakeAboutView
-		| MessageFlag::FakeHistoryItem
-		| MessageFlag::Local;
-	const auto date = TimeId(0);
-	const auto item = _history->makeMessage(
-		_history->nextNonHistoryEntryId(),
-		flags,
-		date,
-		PreparedServiceText{ tr::lng_send_non_premium_text(
-			tr::now,
-			lt_user,
-			Ui::Text::Bold(_history->peer->shortName()),
-			Ui::Text::RichLangValue) },
-		peerToUser(_history->peer->id));
+	const auto item = _history->makeMessage({
+		.id = _history->nextNonHistoryEntryId(),
+		.flags = (MessageFlag::FakeAboutView
+			| MessageFlag::FakeHistoryItem
+			| MessageFlag::Local),
+		.from = _history->peer->id,
+	}, PreparedServiceText{ tr::lng_send_non_premium_text(
+		tr::now,
+		lt_user,
+		Ui::Text::Bold(_history->peer->shortName()),
+		Ui::Text::RichLangValue),
+	});
 	auto result = AdminLog::OwnedItem(_delegate, item);
 	result->overrideMedia(std::make_unique<ServiceBox>(
 		result.get(),
diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h
index 0f15a0fef..5a6ea07d6 100644
--- a/Telegram/SourceFiles/history/view/history_view_element.h
+++ b/Telegram/SourceFiles/history/view/history_view_element.h
@@ -59,6 +59,7 @@ enum class Context : char {
 	ContactPreview,
 	SavedSublist,
 	TTLViewer,
+	ShortcutMessages,
 };
 
 enum class OnlyEmojiAndSpaces : char {
diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
index 393284d32..3c09635a1 100644
--- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
@@ -1792,7 +1792,7 @@ void ListWidget::updateItemsGeometry() {
 			if (view->isHidden()) {
 				view->setDisplayDate(false);
 			} else {
-				view->setDisplayDate(true);
+				view->setDisplayDate(_context != Context::ShortcutMessages);
 				view->setAttachToPrevious(false);
 				return i;
 			}
@@ -2048,7 +2048,8 @@ void ListWidget::checkActivation() {
 }
 
 void ListWidget::paintEvent(QPaintEvent *e) {
-	if (_controller->contentOverlapped(this, e)) {
+	if ((_context != Context::ShortcutMessages)
+		&& _controller->contentOverlapped(this, e)) {
 		return;
 	}
 
@@ -3737,7 +3738,8 @@ void ListWidget::refreshAttachmentsFromTill(int from, int till) {
 		} else {
 			const auto viewDate = view->dateTime();
 			const auto nextDate = next->dateTime();
-			next->setDisplayDate(nextDate.date() != viewDate.date());
+			next->setDisplayDate(_context != Context::ShortcutMessages
+				&& nextDate.date() != viewDate.date());
 			auto attached = next->computeIsAttachToPrevious(view);
 			next->setAttachToPrevious(attached, view);
 			view->setAttachToNext(attached, next);
diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h
index ebba2f578..2e2fdbae7 100644
--- a/Telegram/SourceFiles/history/view/history_view_list_widget.h
+++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h
@@ -631,11 +631,11 @@ private:
 	const not_null<ListDelegate*> _delegate;
 	const not_null<Window::SessionController*> _controller;
 	const std::unique_ptr<EmojiInteractions> _emojiInteractions;
+	const Context _context;
 
 	Data::MessagePosition _aroundPosition;
 	Data::MessagePosition _shownAtPosition;
 	Data::MessagePosition _initialAroundPosition;
-	Context _context;
 	int _aroundIndex = -1;
 	int _idsLimit = kMinimalIdsLimit;
 	Data::MessagesSlice _slice;
diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp
index 4eafd53e5..f08ed8414 100644
--- a/Telegram/SourceFiles/history/view/history_view_message.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_message.cpp
@@ -2061,6 +2061,7 @@ bool Message::hasFromPhoto() const {
 		return !item->out() && !item->history()->peer->isUser();
 	} break;
 	case Context::ContactPreview:
+	case Context::ShortcutMessages:
 		return false;
 	}
 	Unexpected("Context in Message::hasFromPhoto.");
@@ -3268,6 +3269,7 @@ bool Message::hasFromName() const {
 		return false;
 	} break;
 	case Context::ContactPreview:
+	case Context::ShortcutMessages:
 		return false;
 	}
 	Unexpected("Context in Message::hasFromName.");
@@ -3306,6 +3308,9 @@ bool Message::hasOutLayout() const {
 	const auto item = data();
 	if (item->history()->peer->isSelf()) {
 		if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
+			if (context() == Context::ShortcutMessages) {
+				return true;
+			}
 			return (context() == Context::SavedSublist)
 				&& (!forwarded->forwardOfForward()
 					? (forwarded->originalSender
diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp
index 339b40eaf..5931c22c4 100644
--- a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp
+++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp
@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_photo_media.h"
 #include "data/data_document_media.h"
 #include "history/history.h"
+#include "history/history_item.h"
 #include "history/history_item_reply_markup.h"
 #include "inline_bots/inline_bot_layout_item.h"
 #include "inline_bots/inline_bot_send_data.h"
@@ -376,30 +377,15 @@ bool Result::hasThumbDisplay() const {
 
 void Result::addToHistory(
 		not_null<History*> history,
-		MessageFlags flags,
-		MsgId msgId,
-		PeerId fromId,
-		TimeId date,
-		UserId viaBotId,
-		FullReplyTo replyTo,
-		const QString &postAuthor) const {
-	flags |= MessageFlag::FromInlineBot;
-
-	auto markup = _replyMarkup ? *_replyMarkup : HistoryMessageMarkupData();
-	if (!markup.isNull()) {
-		flags |= MessageFlag::HasReplyMarkup;
+		HistoryItemCommonFields &&fields) const {
+	fields.flags |= MessageFlag::FromInlineBot;
+	if (_replyMarkup) {
+		fields.markup = *_replyMarkup;
+		if (!fields.markup.isNull()) {
+			fields.flags |= MessageFlag::HasReplyMarkup;
+		}
 	}
-	sendData->addToHistory(
-		this,
-		history,
-		flags,
-		msgId,
-		fromId,
-		date,
-		viaBotId,
-		replyTo,
-		postAuthor,
-		std::move(markup));
+	sendData->addToHistory(this, history, std::move(fields));
 }
 
 QString Result::getErrorOnSend(not_null<History*> history) const {
diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.h b/Telegram/SourceFiles/inline_bots/inline_bot_result.h
index 296deb613..cc7e090ee 100644
--- a/Telegram/SourceFiles/inline_bots/inline_bot_result.h
+++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.h
@@ -16,6 +16,7 @@ class FileLoader;
 class History;
 class UserData;
 struct HistoryMessageMarkupData;
+struct HistoryItemCommonFields;
 
 namespace Data {
 class LocationPoint;
@@ -64,13 +65,7 @@ public:
 
 	void addToHistory(
 		not_null<History*> history,
-		MessageFlags flags,
-		MsgId msgId,
-		PeerId fromId,
-		TimeId date,
-		UserId viaBotId,
-		FullReplyTo replyTo,
-		const QString &postAuthor) const;
+		HistoryItemCommonFields &&fields) const;
 	QString getErrorOnSend(not_null<History*> history) const;
 
 	// interface for Layout:: usage
diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp
index a8135979b..0b8e9d8b7 100644
--- a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp
+++ b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp
@@ -31,29 +31,15 @@ QString SendData::getLayoutDescription(const Result *owner) const {
 void SendDataCommon::addToHistory(
 		const Result *owner,
 		not_null<History*> history,
-		MessageFlags flags,
-		MsgId msgId,
-		PeerId fromId,
-		TimeId date,
-		UserId viaBotId,
-		FullReplyTo replyTo,
-		const QString &postAuthor,
-		HistoryMessageMarkupData &&markup) const {
-	auto fields = getSentMessageFields();
-	if (replyTo) {
-		flags |= MessageFlag::HasReplyInfo;
+		HistoryItemCommonFields &&fields) const {
+	auto distinct = getSentMessageFields();
+	if (fields.replyTo) {
+		fields.flags |= MessageFlag::HasReplyInfo;
 	}
 	history->addNewLocalMessage(
-		msgId,
-		flags,
-		viaBotId,
-		replyTo,
-		date,
-		fromId,
-		postAuthor,
-		std::move(fields.text),
-		std::move(fields.media),
-		std::move(markup));
+		std::move(fields),
+		std::move(distinct.text),
+		std::move(distinct.media));
 }
 
 QString SendDataCommon::getErrorOnSend(
@@ -113,25 +99,11 @@ QString SendContact::getLayoutDescription(const Result *owner) const {
 void SendPhoto::addToHistory(
 		const Result *owner,
 		not_null<History*> history,
-		MessageFlags flags,
-		MsgId msgId,
-		PeerId fromId,
-		TimeId date,
-		UserId viaBotId,
-		FullReplyTo replyTo,
-		const QString &postAuthor,
-		HistoryMessageMarkupData &&markup) const {
+		HistoryItemCommonFields &&fields) const {
 	history->addNewLocalMessage(
-		msgId,
-		flags,
-		viaBotId,
-		replyTo,
-		date,
-		fromId,
-		postAuthor,
+		std::move(fields),
 		_photo,
-		{ _message, _entities },
-		std::move(markup));
+		{ _message, _entities });
 }
 
 QString SendPhoto::getErrorOnSend(
@@ -144,25 +116,11 @@ QString SendPhoto::getErrorOnSend(
 void SendFile::addToHistory(
 		const Result *owner,
 		not_null<History*> history,
-		MessageFlags flags,
-		MsgId msgId,
-		PeerId fromId,
-		TimeId date,
-		UserId viaBotId,
-		FullReplyTo replyTo,
-		const QString &postAuthor,
-		HistoryMessageMarkupData &&markup) const {
+		HistoryItemCommonFields &&fields) const {
 	history->addNewLocalMessage(
-		msgId,
-		flags,
-		viaBotId,
-		replyTo,
-		date,
-		fromId,
-		postAuthor,
+		std::move(fields),
 		_document,
-		{ _message, _entities },
-		std::move(markup));
+		{ _message, _entities });
 }
 
 QString SendFile::getErrorOnSend(
@@ -175,24 +133,8 @@ QString SendFile::getErrorOnSend(
 void SendGame::addToHistory(
 		const Result *owner,
 		not_null<History*> history,
-		MessageFlags flags,
-		MsgId msgId,
-		PeerId fromId,
-		TimeId date,
-		UserId viaBotId,
-		FullReplyTo replyTo,
-		const QString &postAuthor,
-		HistoryMessageMarkupData &&markup) const {
-	history->addNewLocalMessage(
-		msgId,
-		flags,
-		viaBotId,
-		replyTo,
-		date,
-		fromId,
-		postAuthor,
-		_game,
-		std::move(markup));
+		HistoryItemCommonFields &&fields) const {
+	history->addNewLocalMessage(std::move(fields), _game);
 }
 
 QString SendGame::getErrorOnSend(
diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h
index 84c25624f..502cc006e 100644
--- a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h
+++ b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h
@@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "history/history_location_manager.h"
 
-struct HistoryMessageMarkupData;
+struct HistoryItemCommonFields;
 
 namespace Main {
 class Session;
@@ -43,14 +43,7 @@ public:
 	virtual void addToHistory(
 		const Result *owner,
 		not_null<History*> history,
-		MessageFlags flags,
-		MsgId msgId,
-		PeerId fromId,
-		TimeId date,
-		UserId viaBotId,
-		FullReplyTo replyTo,
-		const QString &postAuthor,
-		HistoryMessageMarkupData &&markup) const = 0;
+		HistoryItemCommonFields &&fields) const = 0;
 	virtual QString getErrorOnSend(
 		const Result *owner,
 		not_null<History*> history) const = 0;
@@ -85,14 +78,7 @@ public:
 	void addToHistory(
 		const Result *owner,
 		not_null<History*> history,
-		MessageFlags flags,
-		MsgId msgId,
-		PeerId fromId,
-		TimeId date,
-		UserId viaBotId,
-		FullReplyTo replyTo,
-		const QString &postAuthor,
-		HistoryMessageMarkupData &&markup) const override;
+		HistoryItemCommonFields &&fields) const override;
 
 	QString getErrorOnSend(
 		const Result *owner,
@@ -253,14 +239,7 @@ public:
 	void addToHistory(
 		const Result *owner,
 		not_null<History*> history,
-		MessageFlags flags,
-		MsgId msgId,
-		PeerId fromId,
-		TimeId date,
-		UserId viaBotId,
-		FullReplyTo replyTo,
-		const QString &postAuthor,
-		HistoryMessageMarkupData &&markup) const override;
+		HistoryItemCommonFields &&fields) const override;
 
 	QString getErrorOnSend(
 		const Result *owner,
@@ -294,14 +273,7 @@ public:
 	void addToHistory(
 		const Result *owner,
 		not_null<History*> history,
-		MessageFlags flags,
-		MsgId msgId,
-		PeerId fromId,
-		TimeId date,
-		UserId viaBotId,
-		FullReplyTo replyTo,
-		const QString &postAuthor,
-		HistoryMessageMarkupData &&markup) const override;
+		HistoryItemCommonFields &&fields) const override;
 
 	QString getErrorOnSend(
 		const Result *owner,
@@ -329,14 +301,7 @@ public:
 	void addToHistory(
 		const Result *owner,
 		not_null<History*> history,
-		MessageFlags flags,
-		MsgId msgId,
-		PeerId fromId,
-		TimeId date,
-		UserId viaBotId,
-		FullReplyTo replyTo,
-		const QString &postAuthor,
-		HistoryMessageMarkupData &&markup) const override;
+		HistoryItemCommonFields &&fields) const override;
 
 	QString getErrorOnSend(
 		const Result *owner,
diff --git a/Telegram/SourceFiles/media/stories/media_stories_reactions.cpp b/Telegram/SourceFiles/media/stories/media_stories_reactions.cpp
index f401f6b37..da07a1516 100644
--- a/Telegram/SourceFiles/media/stories/media_stories_reactions.cpp
+++ b/Telegram/SourceFiles/media/stories/media_stories_reactions.cpp
@@ -131,23 +131,12 @@ private:
 		not_null<History*> history) {
 	Expects(history->peer->isUser());
 
-	const auto flags = MessageFlag::FakeHistoryItem
-		| MessageFlag::HasFromId;
-	const auto replyTo = FullReplyTo();
-	const auto viaBotId = UserId();
-	const auto groupedId = uint64();
-	const auto item = history->makeMessage(
-		history->nextNonHistoryEntryId(),
-		flags,
-		replyTo,
-		viaBotId,
-		base::unixtime::now(),
-		peerToUser(history->peer->id),
-		QString(),
-		TextWithEntities(),
-		MTP_messageMediaEmpty(),
-		HistoryMessageMarkupData(),
-		groupedId);
+	const auto item = history->makeMessage({
+		.id = history->nextNonHistoryEntryId(),
+		.flags = MessageFlag::FakeHistoryItem | MessageFlag::HasFromId,
+		.from = history->peer->id,
+		.date = base::unixtime::now(),
+	}, TextWithEntities(), MTP_messageMediaEmpty());
 	return AdminLog::OwnedItem(delegate, item);
 }
 
diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
index 290aa222c..8a1053da5 100644
--- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "chat_helpers/tabbed_selector.h"
 #include "core/file_utilities.h"
 #include "core/mime_type.h"
+#include "data/business/data_shortcut_messages.h"
 #include "data/data_message_reaction_id.h"
 #include "data/data_premium_limits.h"
 #include "data/data_session.h"
@@ -75,6 +76,8 @@ public:
 	}
 
 	[[nodiscard]] rpl::producer<QString> title() override;
+	[[nodiscard]] rpl::producer<> sectionShowBack() override;
+	void setInnerFocus() override;
 
 	bool paintOuter(
 		not_null<QWidget*> outer,
@@ -82,6 +85,8 @@ public:
 		QRect clip) override;
 
 private:
+	void outerResized(QSize outer);
+
 	// ListDelegate interface.
 	Context listContext() override;
 	bool listScrollTo(int top, bool syntetic = true) override;
@@ -154,7 +159,11 @@ private:
 	QPointer<Ui::RpWidget> createPinnedToBottom(
 		not_null<Ui::RpWidget*> parent) override;
 	void setupComposeControls();
+	void processScroll();
+	void updateInnerVisibleArea();
 
+	void pushReplyReturn(not_null<HistoryItem*> item);
+	void checkReplyReturns();
 
 	void uploadFile(const QByteArray &fileContent, SendMediaType type);
 	bool confirmSendingFiles(
@@ -234,6 +243,7 @@ private:
 	QPointer<ListWidget> _inner;
 	std::unique_ptr<Ui::RpWidget> _controlsWrap;
 	std::unique_ptr<ComposeControls> _composeControls;
+	rpl::event_stream<> _showBackRequests;
 	bool _skipScrollEvent = false;
 
 	std::unique_ptr<StickerToast> _stickerToast;
@@ -294,10 +304,17 @@ ShortcutMessages::ShortcutMessages(
 		this,
 		controller,
 		static_cast<ListDelegate*>(this));
-	//_scroll->scrolls(
-	//) | rpl::start_with_next([=] {
-	//	onScroll();
-	//}, lifetime());
+
+	_scroll->sizeValue() | rpl::filter([](QSize size) {
+		return !size.isEmpty();
+	}) | rpl::start_with_next([=](QSize size) {
+		outerResized(size);
+	}, lifetime());
+
+	_scroll->scrolls(
+	) | rpl::start_with_next([=] {
+		processScroll();
+	}, lifetime());
 
 	_inner->editMessageRequested(
 	) | rpl::start_with_next([=](auto fullId) {
@@ -312,10 +329,10 @@ ShortcutMessages::ShortcutMessages(
 	{
 		auto emptyInfo = base::make_unique_q<EmptyListBubbleWidget>(
 			_inner,
-			controller->chatStyle(),
+			_style.get(),
 			st::msgServicePadding);
 		const auto emptyText = Ui::Text::Semibold(
-			tr::lng_scheduled_messages_empty(tr::now));
+			u"give me your money.."_q);
 		emptyInfo->setText(emptyText);
 		_inner->setEmptyInfoWidget(std::move(emptyInfo));
 	}
@@ -335,6 +352,31 @@ rpl::producer<QString> ShortcutMessages::title() {
 	return rpl::single(u"Editing messages list"_q);
 }
 
+void ShortcutMessages::processScroll() {
+	if (_skipScrollEvent) {
+		return;
+	}
+	updateInnerVisibleArea();
+}
+
+void ShortcutMessages::updateInnerVisibleArea() {
+	if (!_inner->animatedScrolling()) {
+		checkReplyReturns();
+	}
+	const auto scrollTop = _scroll->scrollTop();
+	_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
+	_cornerButtons.updateJumpDownVisibility();
+	_cornerButtons.updateUnreadThingsVisibility();
+}
+
+rpl::producer<> ShortcutMessages::sectionShowBack() {
+	return _showBackRequests.events();
+}
+
+void ShortcutMessages::setInnerFocus() {
+	_composeControls->focus();
+}
+
 bool ShortcutMessages::paintOuter(
 		not_null<QWidget*> outer,
 		int maxVisibleHeight,
@@ -349,6 +391,29 @@ bool ShortcutMessages::paintOuter(
 	return true;
 }
 
+void ShortcutMessages::outerResized(QSize outer) {
+	const auto contentWidth = outer.width();
+
+	const auto newScrollTop = _scroll->isHidden()
+		? std::nullopt
+		: _scroll->scrollTop()
+		? base::make_optional(_scroll->scrollTop())
+		: 0;
+	_skipScrollEvent = true;
+	_inner->resizeToWidth(contentWidth, _scroll->height());
+	resize(width(), _inner->height());
+	_skipScrollEvent = false;
+
+	if (!_scroll->isHidden()) {
+		if (newScrollTop) {
+			_scroll->scrollToY(*newScrollTop);
+		}
+		updateInnerVisibleArea();
+	}
+	_composeControls->setAutocompleteBoundingRect(_scroll->geometry());
+	_cornerButtons.updatePositions();
+}
+
 void ShortcutMessages::setupComposeControls() {
 	_composeControls->setHistory({
 		.history = _history.get(),
@@ -446,10 +511,10 @@ void ShortcutMessages::setupComposeControls() {
 		if (action == Ui::InputField::MimeAction::Check) {
 			return Core::CanSendFiles(data);
 		} else if (action == Ui::InputField::MimeAction::Insert) {
-			//return confirmSendingFiles(
-			//	data,
-			//	std::nullopt,
-			//	Core::ReadMimeText(data));#TODO
+			return confirmSendingFiles(
+				data,
+				std::nullopt,
+				Core::ReadMimeText(data));
 		}
 		Unexpected("action in MimeData hook.");
 	});
@@ -480,22 +545,24 @@ QPointer<Ui::RpWidget> ShortcutMessages::createPinnedToBottom(
 		_controlsWrap.get(),
 		_controller,
 		[=](not_null<DocumentData*> emoji) { listShowPremiumToast(emoji); },
-		ComposeControls::Mode::Scheduled,
+		ComposeControls::Mode::Normal,
 		SendMenu::Type::Disabled);
 
 	setupComposeControls();
 
+	showAtEnd();
+
 	return _controlsWrap.get();
 }
 
 Context ShortcutMessages::listContext() {
-	return Context::History;
+	return Context::ShortcutMessages;
 }
 
 bool ShortcutMessages::listScrollTo(int top, bool syntetic) {
 	top = std::clamp(top, 0, _scroll->scrollTopMax());
 	if (_scroll->scrollTop() == top) {
-		//updateInnerVisibleArea();
+		updateInnerVisibleArea();
 		return false;
 	}
 	_scroll->scrollToY(top);
@@ -509,7 +576,7 @@ void ShortcutMessages::listCancelRequest() {
 	} else if (_composeControls->handleCancelRequest()) {
 		return;
 	}
-	_controller->showBackFromStack();
+	_showBackRequests.fire({});
 }
 
 void ShortcutMessages::listDeleteRequest() {
@@ -525,13 +592,11 @@ rpl::producer<Data::MessagesSlice> ShortcutMessages::listSource(
 		int limitBefore,
 		int limitAfter) {
 	const auto data = &_controller->session().data();
-	//return rpl::single(rpl::empty) | rpl::then(
-	//	data->scheduledMessages().updates(_history)
-	//) | rpl::map([=] {
-	//	return data->scheduledMessages().list(_history);
-	//}) | rpl::after_next([=](const Data::MessagesSlice &slice) {
-	//	highlightSingleNewMessage(slice);
-	//});
+	return rpl::single(rpl::empty) | rpl::then(
+		data->shortcutMessages().updates(_shortcutId)
+	) | rpl::map([=] {
+		return data->shortcutMessages().list(_shortcutId);
+	});
 	return rpl::never<Data::MessagesSlice>();
 }
 
@@ -698,12 +763,12 @@ std::optional<bool> ShortcutMessages::cornerButtonsDownShown() {
 		|| _composeControls->isTTLButtonShown()) {
 		return false;
 	}
-	//const auto top = _scroll->scrollTop() + st::historyToDownShownAfter;
-	//if (top < _scroll->scrollTopMax() || _cornerButtons.replyReturn()) {
-	//	return true;
-	//} else if (_inner->loadedAtBottomKnown()) {
-	//	return !_inner->loadedAtBottom();
-	//}
+	const auto top = _scroll->scrollTop() + st::historyToDownShownAfter;
+	if (top < _scroll->scrollTopMax() || _cornerButtons.replyReturn()) {
+		return true;
+	} else if (_inner->loadedAtBottomKnown()) {
+		return !_inner->loadedAtBottom();
+	}
 	return std::nullopt;
 }
 
@@ -717,10 +782,31 @@ bool ShortcutMessages::cornerButtonsHas(CornerButtonType type) {
 	return (type == CornerButtonType::Down);
 }
 
+void ShortcutMessages::pushReplyReturn(not_null<HistoryItem*> item) {
+	if (item->shortcutId() == _shortcutId) {
+		_cornerButtons.pushReplyReturn(item);
+	}
+}
+
+void ShortcutMessages::checkReplyReturns() {
+	const auto currentTop = _scroll->scrollTop();
+	while (const auto replyReturn = _cornerButtons.replyReturn()) {
+		const auto position = replyReturn->position();
+		const auto scrollTop = _inner->scrollTopForPosition(position);
+		const auto below = scrollTop
+			? (currentTop >= std::min(*scrollTop, _scroll->scrollTopMax()))
+			: _inner->isBelowPosition(position);
+		if (below) {
+			_cornerButtons.calculateNextReplyReturn();
+		} else {
+			break;
+		}
+	}
+}
+
 void ShortcutMessages::uploadFile(
 		const QByteArray &fileContent,
 		SendMediaType type) {
-	// #TODO replies schedule
 	_session->api().sendFile(fileContent, type, prepareSendAction({}));
 }
 
@@ -773,11 +859,6 @@ void ShortcutMessages::send() {
 		return;
 	}
 	send({});
-	// #TODO replies schedule
-	//const auto callback = [=](Api::SendOptions options) { send(options); };
-	//Ui::show(
-	//	PrepareScheduleBox(this, sendMenuType(), callback),
-	//	Ui::LayerOption::KeepOther);
 }
 
 void ShortcutMessages::sendVoice(ComposeControls::VoiceToSend &&data) {
@@ -933,7 +1014,7 @@ bool ShortcutMessages::confirmSendingFiles(
 		_composeControls->getTextWithAppliedMarkdown(),
 		_history->peer,
 		Api::SendType::Normal,
-		SendMenu::Type::SilentOnly); // #TODO replies schedule
+		SendMenu::Type::Disabled);
 
 	box->setConfirmedCallback(crl::guard(this, [=](
 			Ui::PreparedList &&list,
diff --git a/Telegram/SourceFiles/support/support_autocomplete.cpp b/Telegram/SourceFiles/support/support_autocomplete.cpp
index 75d42760d..45ad83edd 100644
--- a/Telegram/SourceFiles/support/support_autocomplete.cpp
+++ b/Telegram/SourceFiles/support/support_autocomplete.cpp
@@ -273,24 +273,14 @@ AdminLog::OwnedItem GenerateCommentItem(
 	if (data.comment.isEmpty()) {
 		return nullptr;
 	}
-	const auto flags = MessageFlag::HasFromId
-		| MessageFlag::Outgoing
-		| MessageFlag::FakeHistoryItem;
-	const auto replyTo = FullReplyTo();
-	const auto viaBotId = UserId();
-	const auto groupedId = uint64();
-	const auto item = history->makeMessage(
-		history->nextNonHistoryEntryId(),
-		flags,
-		replyTo,
-		viaBotId,
-		base::unixtime::now(),
-		history->session().userId(),
-		QString(),
-		TextWithEntities{ data.comment },
-		MTP_messageMediaEmpty(),
-		HistoryMessageMarkupData(),
-		groupedId);
+	const auto item = history->makeMessage({
+		.id = history->nextNonHistoryEntryId(),
+		.flags = (MessageFlag::HasFromId
+			| MessageFlag::Outgoing
+			| MessageFlag::FakeHistoryItem),
+		.from = history->session().userPeerId(),
+		.date = base::unixtime::now(),
+	}, TextWithEntities{ data.comment }, MTP_messageMediaEmpty());
 	return AdminLog::OwnedItem(delegate, item);
 }
 
@@ -298,29 +288,19 @@ AdminLog::OwnedItem GenerateContactItem(
 		not_null<HistoryView::ElementDelegate*> delegate,
 		not_null<History*> history,
 		const Contact &data) {
-	const auto replyTo = FullReplyTo();
-	const auto viaBotId = UserId();
-	const auto postAuthor = QString();
-	const auto groupedId = uint64();
-	const auto item = history->makeMessage(
-		history->nextNonHistoryEntryId(),
-		(MessageFlag::HasFromId
+	const auto item = history->makeMessage({
+		.id = history->nextNonHistoryEntryId(),
+		.flags = (MessageFlag::HasFromId
 			| MessageFlag::Outgoing
 			| MessageFlag::FakeHistoryItem),
-		replyTo,
-		viaBotId,
-		base::unixtime::now(),
-		history->session().userPeerId(),
-		postAuthor,
-		TextWithEntities(),
-		MTP_messageMediaContact(
-			MTP_string(data.phone),
-			MTP_string(data.firstName),
-			MTP_string(data.lastName),
-			MTP_string(), // vcard
-			MTP_long(0)), // user_id
-		HistoryMessageMarkupData(),
-		groupedId);
+		.from = history->session().userPeerId(),
+		.date = base::unixtime::now(),
+	}, TextWithEntities(), MTP_messageMediaContact(
+		MTP_string(data.phone),
+		MTP_string(data.firstName),
+		MTP_string(data.lastName),
+		MTP_string(), // vcard
+		MTP_long(0))); // user_id
 	return AdminLog::OwnedItem(delegate, item);
 }
 

From 6e08b00dba33d55c8069177f18058f6b90e9eeb4 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 27 Feb 2024 13:11:16 +0400
Subject: [PATCH 054/108] Fix sending .tgs files as stickers.

Regression was introduced in 3467fe226f.
---
 Telegram/SourceFiles/storage/localimageloader.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp
index 8044e266c..532a1d35e 100644
--- a/Telegram/SourceFiles/storage/localimageloader.cpp
+++ b/Telegram/SourceFiles/storage/localimageloader.cpp
@@ -901,11 +901,11 @@ void FileLoadTask::process(Args &&args) {
 		attributes.push_back(MTP_documentAttributeImageSize(MTP_int(w), MTP_int(h)));
 
 		if (ValidateThumbDimensions(w, h)) {
-			isSticker = (_type == SendMediaType::File)
-				&& Core::IsMimeSticker(filemime)
+			isSticker = Core::IsMimeSticker(filemime)
 				&& (filesize < Storage::kMaxStickerBytesSize)
 				&& (Core::IsMimeStickerAnimated(filemime)
-					|| GoodStickerDimensions(w, h));
+					|| (_type == SendMediaType::File
+						&& GoodStickerDimensions(w, h)));
 			if (isSticker) {
 				attributes.push_back(MTP_documentAttributeSticker(
 					MTP_flags(0),

From fb539b0f70046e6fcf1f71212ef56cbfc8196348 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 27 Feb 2024 13:58:46 +0400
Subject: [PATCH 055/108] Improve shortcut messages geometry.

---
 Telegram/SourceFiles/history/history_item.cpp              | 2 +-
 Telegram/SourceFiles/history/history_item_components.cpp   | 6 ++++--
 Telegram/SourceFiles/history/view/history_view_element.cpp | 2 +-
 .../settings/business/settings_shortcut_messages.cpp       | 7 +++----
 4 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp
index 1f75a47d8..38f236c4b 100644
--- a/Telegram/SourceFiles/history/history_item.cpp
+++ b/Telegram/SourceFiles/history/history_item.cpp
@@ -735,7 +735,7 @@ TimeId HistoryItem::NewMessageDate(TimeId scheduled) {
 
 TimeId HistoryItem::NewMessageDate(
 		const Api::SendOptions &options) {
-	return options.shortcutId ? TimeId() : NewMessageDate(options.scheduled);
+	return options.shortcutId ? 1 : NewMessageDate(options.scheduled);
 }
 
 HistoryServiceDependentData *HistoryItem::GetServiceDependentData() {
diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp
index ee5d1d21f..f2ebc57c5 100644
--- a/Telegram/SourceFiles/history/history_item_components.cpp
+++ b/Telegram/SourceFiles/history/history_item_components.cpp
@@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "mainwindow.h"
 #include "media/audio/media_audio.h"
 #include "media/player/media_player_instance.h"
+#include "data/business/data_shortcut_messages.h"
 #include "data/stickers/data_custom_emoji.h"
 #include "data/data_channel.h"
 #include "data/data_media_types.h"
@@ -301,10 +302,11 @@ ReplyFields ReplyFieldsFromMTP(
 		if (const auto id = data.vreply_to_msg_id().value_or_empty()) {
 			result.messageId = data.is_reply_to_scheduled()
 				? owner->scheduledMessages().localMessageId(id)
-				AssertIsDebug()
+				: item->shortcutId()
+				? owner->shortcutMessages().localMessageId(id)
 				: id;
 			result.topMessageId
-				= data.vreply_to_top_id().value_or(id);
+				= data.vreply_to_top_id().value_or(result.messageId.bare);
 			result.topicPost = data.is_forum_topic() ? 1 : 0;
 		}
 		if (const auto header = data.vreply_from()) {
diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp
index bac85515a..3e142d0c9 100644
--- a/Telegram/SourceFiles/history/view/history_view_element.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_element.cpp
@@ -462,7 +462,7 @@ Element::Element(
 	Flag serviceFlag)
 : _delegate(delegate)
 , _data(data)
-, _dateTime(IsItemScheduledUntilOnline(data)
+, _dateTime((IsItemScheduledUntilOnline(data) || data->shortcutId())
 	? QDateTime()
 	: ItemDateTime(data))
 , _text(st::msgMinWidth)
diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
index 8a1053da5..06ea5ac7e 100644
--- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
@@ -337,8 +337,8 @@ ShortcutMessages::ShortcutMessages(
 		_inner->setEmptyInfoWidget(std::move(emptyInfo));
 	}
 
-	widthValue() | rpl::start_with_next([=](int width) {
-		resize(width, width);
+	_inner->heightValue() | rpl::start_with_next([=](int height) {
+		resize(width(), height);
 	}, lifetime());
 }
 
@@ -401,7 +401,6 @@ void ShortcutMessages::outerResized(QSize outer) {
 		: 0;
 	_skipScrollEvent = true;
 	_inner->resizeToWidth(contentWidth, _scroll->height());
-	resize(width(), _inner->height());
 	_skipScrollEvent = false;
 
 	if (!_scroll->isHidden()) {
@@ -743,7 +742,7 @@ void ShortcutMessages::listAddTranslatedItems(
 
 void ShortcutMessages::cornerButtonsShowAtPosition(
 		Data::MessagePosition position) {
-	//showAtPosition(position);
+	showAtPosition(position);
 }
 
 Data::Thread *ShortcutMessages::cornerButtonsThread() {

From 23e22de6ec7d3478d23d729f3c26210ef5d0821c Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 27 Feb 2024 14:20:30 +0400
Subject: [PATCH 056/108] Fix deleting shortcut items.

---
 Telegram/SourceFiles/history/history_item.cpp        |  7 ++++++-
 .../settings/business/settings_shortcut_messages.cpp | 12 +++++++++++-
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp
index 38f236c4b..84986c28a 100644
--- a/Telegram/SourceFiles/history/history_item.cpp
+++ b/Telegram/SourceFiles/history/history_item.cpp
@@ -2037,6 +2037,9 @@ void HistoryItem::setRealId(MsgId newId) {
 
 	const auto oldId = std::exchange(id, newId);
 	_flags &= ~(MessageFlag::BeingSent | MessageFlag::Local);
+	if (isBusinessShortcut()) {
+		_date = 0;
+	}
 	if (isRegular()) {
 		_history->unregisterClientSideMessage(this);
 	}
@@ -2154,7 +2157,9 @@ bool HistoryItem::canDelete() const {
 		return false;
 	} else if (topicRootId() == id) {
 		return false;
-	} else if (!isHistoryEntry() && !isScheduled()) {
+	} else if (!isHistoryEntry()
+		&& !isScheduled()
+		&& !isBusinessShortcut()) {
 		return false;
 	}
 	auto channel = _history->peer->asChannel();
diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
index 06ea5ac7e..df5f778ba 100644
--- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
@@ -164,6 +164,8 @@ private:
 
 	void pushReplyReturn(not_null<HistoryItem*> item);
 	void checkReplyReturns();
+	void confirmDeleteSelected();
+	void clearSelected();
 
 	void uploadFile(const QByteArray &fileContent, SendMediaType type);
 	bool confirmSendingFiles(
@@ -579,7 +581,7 @@ void ShortcutMessages::listCancelRequest() {
 }
 
 void ShortcutMessages::listDeleteRequest() {
-	//confirmDeleteSelected();
+	confirmDeleteSelected();
 }
 
 void ShortcutMessages::listTryProcessKeyInput(not_null<QKeyEvent*> e) {
@@ -803,6 +805,14 @@ void ShortcutMessages::checkReplyReturns() {
 	}
 }
 
+void ShortcutMessages::confirmDeleteSelected() {
+	ConfirmDeleteSelectedItems(_inner);
+}
+
+void ShortcutMessages::clearSelected() {
+	_inner->cancelSelection();
+}
+
 void ShortcutMessages::uploadFile(
 		const QByteArray &fileContent,
 		SendMediaType type) {

From f086203d258450592bdf713321b4e86a26df690e Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Wed, 28 Feb 2024 08:58:37 +0400
Subject: [PATCH 057/108] Implement proper shortcut management.

---
 Telegram/SourceFiles/api/api_updates.cpp      |   2 +
 .../data/business/data_shortcut_messages.cpp  | 222 +++++++++++++++---
 .../data/business/data_shortcut_messages.h    |  21 +-
 Telegram/SourceFiles/history/history_item.cpp |   4 +
 Telegram/SourceFiles/history/history_item.h   |   1 +
 .../history/view/history_view_bottom_info.cpp |  11 +-
 .../history/view/history_view_bottom_info.h   |   1 +
 .../history/view/history_view_list_widget.cpp |   6 +
 Telegram/SourceFiles/info/info_top_bar.cpp    |  21 +-
 .../SourceFiles/info/info_wrap_widget.cpp     |  32 ++-
 Telegram/SourceFiles/info/info_wrap_widget.h  |   8 +-
 .../info/settings/info_settings_widget.cpp    |   8 +
 .../info/settings/info_settings_widget.h      |   3 +
 .../business/settings_away_message.cpp        |   1 -
 .../settings/business/settings_greeting.cpp   |   5 +
 .../business/settings_quick_replies.cpp       |  89 ++++++-
 .../business/settings_shortcut_messages.cpp   |  98 ++++++--
 Telegram/SourceFiles/settings/settings.style  |  16 ++
 .../SourceFiles/settings/settings_common.h    |  12 +
 19 files changed, 474 insertions(+), 87 deletions(-)

diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp
index 57db15d92..ab4b5f30b 100644
--- a/Telegram/SourceFiles/api/api_updates.cpp
+++ b/Telegram/SourceFiles/api/api_updates.cpp
@@ -1560,6 +1560,8 @@ void Updates::feedUpdate(const MTPUpdate &update) {
 			if (const auto local = owner.message(id)) {
 				if (local->isScheduled()) {
 					session().data().scheduledMessages().apply(d, local);
+				} else if (local->isBusinessShortcut()) {
+					session().data().shortcutMessages().apply(d, local);
 				} else {
 					const auto existing = session().data().message(
 						id.peer,
diff --git a/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp b/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
index 026e6a99e..8b59309bf 100644
--- a/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
+++ b/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
@@ -128,6 +128,94 @@ void ShortcutMessages::clearOldRequests() {
 	}
 }
 
+void ShortcutMessages::updateShortcuts(const QVector<MTPQuickReply> &list) {
+	auto shortcuts = parseShortcuts(list);
+	auto changes = std::vector<ShortcutIdChange>();
+	for (auto &[id, shortcut] : _shortcuts.list) {
+		if (shortcuts.list.contains(id)) {
+			continue;
+		}
+		auto foundId = BusinessShortcutId();
+		for (auto &[realId, real] : shortcuts.list) {
+			if (real.name == shortcut.name) {
+				foundId = realId;
+				break;
+			}
+		}
+		if (foundId) {
+			mergeMessagesFromTo(id, foundId);
+			changes.push_back({ .oldId = id, .newId = foundId });
+		} else {
+			shortcuts.list.emplace(id, shortcut);
+		}
+	}
+	const auto changed = !_shortcutsLoaded
+		|| (shortcuts != _shortcuts);
+	if (changed) {
+		_shortcuts = std::move(shortcuts);
+		_shortcutsLoaded = true;
+		for (const auto &change : changes) {
+			_shortcutIdChanges.fire_copy(change);
+		}
+		_shortcutsChanged.fire({});
+	} else {
+		Assert(changes.empty());
+	}
+}
+
+void ShortcutMessages::mergeMessagesFromTo(
+		BusinessShortcutId fromId,
+		BusinessShortcutId toId) {
+	auto &to = _data[toId];
+	const auto i = _data.find(fromId);
+	if (i == end(_data)) {
+		return;
+	}
+
+	auto &from = i->second;
+	auto destroy = base::flat_set<not_null<HistoryItem*>>();
+	for (auto &item : from.items) {
+		if (item->isSending() || item->hasFailed()) {
+			item->setRealShortcutId(toId);
+			to.items.push_back(std::move(item));
+		} else {
+			destroy.emplace(item.get());
+		}
+	}
+	for (const auto &item : destroy) {
+		item->destroy();
+	}
+	_data.remove(fromId);
+
+	cancelRequest(fromId);
+
+	_updates.fire_copy(toId);
+	if (!destroy.empty()) {
+		cancelRequest(toId);
+		request(toId);
+	}
+}
+
+Shortcuts ShortcutMessages::parseShortcuts(
+		const QVector<MTPQuickReply> &list) const {
+	auto result = Shortcuts();
+	for (const auto &reply : list) {
+		const auto shortcut = parseShortcut(reply);
+		result.list.emplace(shortcut.id, shortcut);
+	}
+	return result;
+}
+
+Shortcut ShortcutMessages::parseShortcut(const MTPQuickReply &reply) const {
+	const auto &data = reply.data();
+	return Shortcut{
+		.id = BusinessShortcutId(data.vshortcut_id().v),
+		.count = data.vcount().v,
+		.name = qs(data.vshortcut()),
+		.topMessageId = localMessageId(data.vtop_message().v),
+	};
+}
+
 MsgId ShortcutMessages::localMessageId(MsgId remoteId) const {
 	return RemoteToLocalMsgId(remoteId);
 }
@@ -146,11 +234,60 @@ int ShortcutMessages::count(BusinessShortcutId shortcutId) const {
 }
 
 void ShortcutMessages::apply(const MTPDupdateQuickReplies &update) {
+	updateShortcuts(update.vquick_replies().v);
+	scheduleShortcutsReload();
+}
 
+void ShortcutMessages::scheduleShortcutsReload() {
+	const auto hasUnknownMessages = [&] {
+		const auto selfId = _session->userPeerId();
+		for (const auto &[id, shortcut] : _shortcuts.list) {
+			if (!_session->data().message({ selfId, shortcut.topMessageId })) {
+				return true;
+			}
+		}
+		return false;
+	};
+	if (hasUnknownMessages()) {
+		_shortcutsLoaded = false;
+		const auto cancelledId = base::take(_shortcutsRequestId);
+		_session->api().request(cancelledId).cancel();
+		crl::on_main(_session, [=] {
+			if (cancelledId || hasUnknownMessages()) {
+				preloadShortcuts();
+			}
+		});
+	}
 }
 
 void ShortcutMessages::apply(const MTPDupdateNewQuickReply &update) {
-
+	const auto selfId = _session->userPeerId();
+	const auto &reply = update.vquick_reply();
+	auto foundId = BusinessShortcutId();
+	const auto shortcut = parseShortcut(reply);
+	for (auto &[id, existing] : _shortcuts.list) {
+		if (id == shortcut.id) {
+			foundId = id;
+			break;
+		} else if (existing.name == shortcut.name) {
+			foundId = id;
+			break;
+		}
+	}
+	if (foundId == shortcut.id) {
+		auto &already = _shortcuts.list[shortcut.id];
+		if (already != shortcut) {
+			already = shortcut;
+			_shortcutsChanged.fire({});
+		}
+		return;
+	} else if (foundId) {
+		_shortcuts.list.emplace(shortcut.id, shortcut);
+		mergeMessagesFromTo(foundId, shortcut.id);
+		_shortcuts.list.remove(foundId);
+		_shortcutIdChanges.fire({ foundId, shortcut.id });
+		_shortcutsChanged.fire({});
+	}
 }
 
 void ShortcutMessages::apply(const MTPDupdateQuickReplyMessage &update) {
@@ -159,10 +296,30 @@ void ShortcutMessages::apply(const MTPDupdateQuickReplyMessage &update) {
 	if (!shortcutId) {
 		return;
 	}
+	const auto loaded = _data.contains(shortcutId);
 	auto &list = _data[shortcutId];
 	append(shortcutId, list, message);
 	sort(list);
 	_updates.fire_copy(shortcutId);
+	updateCount(shortcutId);
+	if (!loaded) {
+		request(shortcutId);
+	}
+}
+
+void ShortcutMessages::updateCount(BusinessShortcutId shortcutId) {
+	const auto i = _data.find(shortcutId);
+	const auto j = _shortcuts.list.find(shortcutId);
+	if (j == end(_shortcuts.list)) {
+		return;
+	}
+	const auto count = (i != end(_data))
+		? int(i->second.itemById.size())
+		: 0;
+	if (j->second.count != count) {
+		_shortcuts.list[shortcutId].count = count;
+		_shortcutsChanged.fire({});
+	}
 }
 
 void ShortcutMessages::apply(
@@ -187,6 +344,10 @@ void ShortcutMessages::apply(
 		}
 	}
 	_updates.fire_copy(shortcutId);
+	updateCount(shortcutId);
+
+	cancelRequest(shortcutId);
+	request(shortcutId);
 }
 
 void ShortcutMessages::apply(const MTPDupdateDeleteQuickReply &update) {
@@ -195,12 +356,17 @@ void ShortcutMessages::apply(const MTPDupdateDeleteQuickReply &update) {
 		return;
 	}
 	auto i = _data.find(shortcutId);
-	while (i != end(_data)) {
-		Assert(!i->second.itemById.empty());
+	while (i != end(_data) && !i->second.itemById.empty()) {
 		i->second.itemById.back().second->destroy();
 		i = _data.find(shortcutId);
 	}
 	_updates.fire_copy(shortcutId);
+	if (_data.contains(shortcutId)) {
+		updateCount(shortcutId);
+	} else {
+		_shortcuts.list.remove(shortcutId);
+		_shortcutIdChanges.fire({ shortcutId, 0 });
+	}
 }
 
 void ShortcutMessages::apply(
@@ -283,30 +449,7 @@ void ShortcutMessages::preloadShortcuts() {
 			owner->processMessages(
 				data.vmessages(),
 				NewMessageType::Existing);
-			auto shortcuts = Shortcuts();
-			const auto messages = &owner->shortcutMessages();
-			for (const auto &reply : data.vquick_replies().v) {
-				const auto &data = reply.data();
-				const auto id = BusinessShortcutId(data.vshortcut_id().v);
-				shortcuts.list.emplace(id, Shortcut{
-					.name = qs(data.vshortcut()),
-					.topMessageId = messages->localMessageId(
-						data.vtop_message().v),
-					.count = data.vcount().v,
-				});
-			}
-			for (auto &[id, shortcut] : _shortcuts.list) {
-				if (id < 0) {
-					shortcuts.list.emplace(id, shortcut);
-				}
-			}
-			const auto changed = !_shortcutsLoaded
-				|| (shortcuts != _shortcuts);
-			if (changed) {
-				_shortcuts = std::move(shortcuts);
-				_shortcutsLoaded = true;
-				_shortcutsChanged.fire({});
-			}
+			updateShortcuts(data.vquick_replies().v);
 		}, [&](const MTPDmessages_quickRepliesNotModified &) {
 			if (!_shortcutsLoaded) {
 				_shortcutsLoaded = true;
@@ -328,6 +471,11 @@ rpl::producer<> ShortcutMessages::shortcutsChanged() const {
 	return _shortcutsChanged.events();
 }
 
+auto ShortcutMessages::shortcutIdChanged() const
+-> rpl::producer<ShortcutIdChange> {
+	return _shortcutIdChanges.events();
+}
+
 BusinessShortcutId ShortcutMessages::emplaceShortcut(QString name) {
 	Expects(_shortcutsLoaded);
 
@@ -337,7 +485,7 @@ BusinessShortcutId ShortcutMessages::emplaceShortcut(QString name) {
 		}
 	}
 	const auto result = --_localShortcutId;
-	_shortcuts.list.emplace(result, Shortcut{ name });
+	_shortcuts.list.emplace(result, Shortcut{ .id = result, .name = name });
 	return result;
 }
 
@@ -348,6 +496,14 @@ Shortcut ShortcutMessages::lookupShortcut(BusinessShortcutId id) const {
 	return i->second;
 }
 
+void ShortcutMessages::cancelRequest(BusinessShortcutId shortcutId) {
+	const auto j = _requests.find(shortcutId);
+	if (j != end(_requests)) {
+		_session->api().request(j->second.requestId).cancel();
+		_requests.erase(j);
+	}
+}
+
 void ShortcutMessages::request(BusinessShortcutId shortcutId) {
 	auto &request = _requests[shortcutId];
 	if (request.requestId || TooEarlyForRequest(request.lastReceived)) {
@@ -512,6 +668,7 @@ void ShortcutMessages::remove(not_null<const HistoryItem*> item) {
 		_data.erase(i);
 	}
 	_updates.fire_copy(shortcutId);
+	updateCount(shortcutId);
 }
 
 uint64 ShortcutMessages::countListHash(const List &list) const {
@@ -537,11 +694,10 @@ uint64 ShortcutMessages::countListHash(const List &list) const {
 MTPInputQuickReplyShortcut ShortcutIdToMTP(
 		not_null<Main::Session*> session,
 		BusinessShortcutId id) {
-	if (id >= 0) {
-		return MTP_inputQuickReplyShortcutId(MTP_int(id));
-	}
-	return MTP_inputQuickReplyShortcut(MTP_string(
-		session->data().shortcutMessages().lookupShortcut(id).name));
+	return id
+		? MTP_inputQuickReplyShortcut(MTP_string(
+			session->data().shortcutMessages().lookupShortcut(id).name))
+		: MTPInputQuickReplyShortcut();
 }
 
 } // namespace Data
diff --git a/Telegram/SourceFiles/data/business/data_shortcut_messages.h b/Telegram/SourceFiles/data/business/data_shortcut_messages.h
index 57997e7e0..6b7343b57 100644
--- a/Telegram/SourceFiles/data/business/data_shortcut_messages.h
+++ b/Telegram/SourceFiles/data/business/data_shortcut_messages.h
@@ -22,15 +22,21 @@ class Session;
 struct MessagesSlice;
 
 struct Shortcut {
+	BusinessShortcutId id = 0;
+	int count = 0;
 	QString name;
 	MsgId topMessageId = 0;
-	int count = 0;
 
 	friend inline bool operator==(
 		const Shortcut &a,
 		const Shortcut &b) = default;
 };
 
+struct ShortcutIdChange {
+	BusinessShortcutId oldId = 0;
+	BusinessShortcutId newId = 0;
+};
+
 struct Shortcuts {
 	base::flat_map<BusinessShortcutId, Shortcut> list;
 
@@ -69,6 +75,7 @@ public:
 	[[nodiscard]] const Shortcuts &shortcuts() const;
 	[[nodiscard]] bool shortcutsLoaded() const;
 	[[nodiscard]] rpl::producer<> shortcutsChanged() const;
+	[[nodiscard]] rpl::producer<ShortcutIdChange> shortcutIdChanged() const;
 	[[nodiscard]] BusinessShortcutId emplaceShortcut(QString name);
 	[[nodiscard]] Shortcut lookupShortcut(BusinessShortcutId id) const;
 
@@ -100,6 +107,17 @@ private:
 	void remove(not_null<const HistoryItem*> item);
 	[[nodiscard]] uint64 countListHash(const List &list) const;
 	void clearOldRequests();
+	void cancelRequest(BusinessShortcutId shortcutId);
+	void updateCount(BusinessShortcutId shortcutId);
+
+	void scheduleShortcutsReload();
+	void mergeMessagesFromTo(
+		BusinessShortcutId fromId,
+		BusinessShortcutId toId);
+	void updateShortcuts(const QVector<MTPQuickReply> &list);
+	[[nodiscard]] Shortcut parseShortcut(const MTPQuickReply &reply) const;
+	[[nodiscard]] Shortcuts parseShortcuts(
+		const QVector<MTPQuickReply> &list) const;
 
 	const not_null<Main::Session*> _session;
 	const not_null<History*> _history;
@@ -111,6 +129,7 @@ private:
 
 	Shortcuts _shortcuts;
 	rpl::event_stream<> _shortcutsChanged;
+	rpl::event_stream<ShortcutIdChange> _shortcutIdChanges;
 	BusinessShortcutId _localShortcutId = 0;
 	uint64 _shortcutsHash = 0;
 	mtpRequestId _shortcutsRequestId = 0;
diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp
index 84986c28a..807563fc0 100644
--- a/Telegram/SourceFiles/history/history_item.cpp
+++ b/Telegram/SourceFiles/history/history_item.cpp
@@ -1542,6 +1542,10 @@ bool HistoryItem::isBusinessShortcut() const {
 	return _shortcutId != 0;
 }
 
+void HistoryItem::setRealShortcutId(BusinessShortcutId id) {
+	_shortcutId = id;
+}
+
 void HistoryItem::destroy() {
 	_history->destroyMessage(this);
 }
diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h
index f287bb039..d75182680 100644
--- a/Telegram/SourceFiles/history/history_item.h
+++ b/Telegram/SourceFiles/history/history_item.h
@@ -196,6 +196,7 @@ public:
 	[[nodiscard]] bool isUserpicSuggestion() const;
 	[[nodiscard]] BusinessShortcutId shortcutId() const;
 	[[nodiscard]] bool isBusinessShortcut() const;
+	void setRealShortcutId(BusinessShortcutId id);
 
 	void addLogEntryOriginal(
 		WebPageId localId,
diff --git a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp
index c008fac99..cfb31eabe 100644
--- a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp
@@ -442,7 +442,7 @@ void BottomInfo::paintReactions(
 }
 
 QSize BottomInfo::countCurrentSize(int newWidth) {
-	if (newWidth >= maxWidth()) {
+	if (newWidth >= maxWidth() || (_data.flags & Data::Flag::Shortcut)) {
 		return optimalSize();
 	}
 	const auto dateHeight = (_data.flags & Data::Flag::Sponsored)
@@ -509,7 +509,8 @@ void BottomInfo::layoutRepliesText() {
 	if (!_data.replies
 		|| !*_data.replies
 		|| (_data.flags & Data::Flag::RepliesContext)
-		|| (_data.flags & Data::Flag::Sending)) {
+		|| (_data.flags & Data::Flag::Sending)
+		|| (_data.flags & Data::Flag::Shortcut)) {
 		_replies.clear();
 		return;
 	}
@@ -549,6 +550,9 @@ void BottomInfo::layoutReactionsText() {
 }
 
 QSize BottomInfo::countOptimalSize() {
+	if (_data.flags & Data::Flag::Shortcut) {
+		return { st::historySendStateSpace / 2, st::msgDateFont->height };
+	}
 	auto width = 0;
 	if (_data.flags & (Data::Flag::OutLayout | Data::Flag::Sending)) {
 		width += st::historySendStateSpace;
@@ -654,6 +658,9 @@ BottomInfo::Data BottomInfoDataFromMessage(not_null<Message*> message) {
 	if (item->isPinned() && message->context() != Context::Pinned) {
 		result.flags |= Flag::Pinned;
 	}
+	if (message->context() == Context::ShortcutMessages) {
+		result.flags |= Flag::Shortcut;
+	}
 	if (const auto msgsigned = item->Get<HistoryMessageSigned>()) {
 		 if (!msgsigned->isAnonymousRank) {
 			result.author = msgsigned->postAuthor;
diff --git a/Telegram/SourceFiles/history/view/history_view_bottom_info.h b/Telegram/SourceFiles/history/view/history_view_bottom_info.h
index d593063ef..efdab3334 100644
--- a/Telegram/SourceFiles/history/view/history_view_bottom_info.h
+++ b/Telegram/SourceFiles/history/view/history_view_bottom_info.h
@@ -44,6 +44,7 @@ public:
 			Sponsored      = 0x10,
 			Pinned         = 0x20,
 			Imported       = 0x40,
+			Shortcut       = 0x80,
 			//Unread, // We don't want to pass and update it in Date for now.
 		};
 		friend inline constexpr bool is_flag_type(Flag) { return true; };
diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
index 3c09635a1..31e6b4e9e 100644
--- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
@@ -1937,6 +1937,9 @@ int ListWidget::resizeGetHeight(int newWidth) {
 	_itemsTop = (_minHeight > _itemsHeight + st::historyPaddingBottom)
 		? (_minHeight - _itemsHeight - st::historyPaddingBottom)
 		: 0;
+	if (_emptyInfo) {
+		_emptyInfo->setVisible(isEmpty());
+	}
 	return _itemsTop + _itemsHeight + st::historyPaddingBottom;
 }
 
@@ -3934,6 +3937,9 @@ void ListWidget::replyNextMessage(FullMsgId fullId, bool next) {
 
 void ListWidget::setEmptyInfoWidget(base::unique_qptr<Ui::RpWidget> &&w) {
 	_emptyInfo = std::move(w);
+	if (_emptyInfo) {
+		_emptyInfo->setVisible(isEmpty());
+	}
 }
 
 ListWidget::~ListWidget() {
diff --git a/Telegram/SourceFiles/info/info_top_bar.cpp b/Telegram/SourceFiles/info/info_top_bar.cpp
index d9a422adb..1e5f34c2f 100644
--- a/Telegram/SourceFiles/info/info_top_bar.cpp
+++ b/Telegram/SourceFiles/info/info_top_bar.cpp
@@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "dialogs/ui/dialogs_stories_list.h"
 #include "lang/lang_keys.h"
-#include "lang/lang_numbers_animation.h"
 #include "info/info_wrap_widget.h"
 #include "info/info_controller.h"
 #include "info/profile/info_profile_values.h"
@@ -721,25 +720,7 @@ bool TopBar::computeCanToggleStoryPin() const {
 }
 
 Ui::StringWithNumbers TopBar::generateSelectedText() const {
-	using Type = Storage::SharedMediaType;
-	const auto phrase = [&] {
-		switch (_selectedItems.type) {
-		case Type::Photo: return tr::lng_media_selected_photo;
-		case Type::GIF: return tr::lng_media_selected_gif;
-		case Type::Video: return tr::lng_media_selected_video;
-		case Type::File: return tr::lng_media_selected_file;
-		case Type::MusicFile: return tr::lng_media_selected_song;
-		case Type::Link: return tr::lng_media_selected_link;
-		case Type::RoundVoiceFile: return tr::lng_media_selected_audio;
-		case Type::PhotoVideo: return tr::lng_stories_row_count;
-		}
-		Unexpected("Type in TopBar::generateSelectedText()");
-	}();
-	return phrase(
-		tr::now,
-		lt_count,
-		_selectedItems.list.size(),
-		Ui::StringWithNumbers::FromString);
+	return _selectedItems.title(_selectedItems.list.size());
 }
 
 bool TopBar::selectionMode() const {
diff --git a/Telegram/SourceFiles/info/info_wrap_widget.cpp b/Telegram/SourceFiles/info/info_wrap_widget.cpp
index bf03f7131..ddab6ef51 100644
--- a/Telegram/SourceFiles/info/info_wrap_widget.cpp
+++ b/Telegram/SourceFiles/info/info_wrap_widget.cpp
@@ -43,6 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_forum_topic.h"
 #include "mainwidget.h"
 #include "lang/lang_keys.h"
+#include "lang/lang_numbers_animation.h"
 #include "styles/style_chat.h" // popupMenuExpandedSeparator
 #include "styles/style_info.h"
 #include "styles/style_profile.h"
@@ -64,6 +65,26 @@ const style::InfoTopBar &TopBarStyle(Wrap wrap) {
 		&& section.settingsType()->hasCustomTopBar();
 }
 
+[[nodiscard]] Fn<Ui::StringWithNumbers(int)> SelectedTitleForMedia(
+		Section::MediaType type) {
+	return [type](int count) {
+		using Type = Storage::SharedMediaType;
+		return [&] {
+			switch (type) {
+			case Type::Photo: return tr::lng_media_selected_photo;
+			case Type::GIF: return tr::lng_media_selected_gif;
+			case Type::Video: return tr::lng_media_selected_video;
+			case Type::File: return tr::lng_media_selected_file;
+			case Type::MusicFile: return tr::lng_media_selected_song;
+			case Type::Link: return tr::lng_media_selected_link;
+			case Type::RoundVoiceFile: return tr::lng_media_selected_audio;
+			case Type::PhotoVideo: return tr::lng_stories_row_count;
+			}
+			Unexpected("Type in TopBar::generateSelectedText()");
+		}()(tr::now, lt_count, count, Ui::StringWithNumbers::FromString);
+	};
+}
+
 } // namespace
 
 struct WrapWidget::StackItem {
@@ -71,6 +92,10 @@ struct WrapWidget::StackItem {
 //	std::shared_ptr<ContentMemento> anotherTab;
 };
 
+SelectedItems::SelectedItems(Section::MediaType mediaType)
+: title(SelectedTitleForMedia(mediaType)) {
+}
+
 WrapWidget::WrapWidget(
 	QWidget *parent,
 	not_null<Window::SessionController*> window,
@@ -609,7 +634,12 @@ void WrapWidget::finishShowContent() {
 	_desiredShadowVisibilities.fire(_content->desiredShadowVisibility());
 	_desiredBottomShadowVisibilities.fire(
 		_content->desiredBottomShadowVisibility());
-	_selectedLists.fire(_content->selectedListValue());
+	if (auto selection = _content->selectedListValue()) {
+		_selectedLists.fire(std::move(selection));
+	} else {
+		_selectedLists.fire(rpl::single(
+			SelectedItems(Storage::SharedMediaType::Photo)));
+	}
 	_scrollTillBottomChanges.fire(_content->scrollTillBottomChanges());
 	_topShadow->raise();
 	_topShadow->finishAnimating();
diff --git a/Telegram/SourceFiles/info/info_wrap_widget.h b/Telegram/SourceFiles/info/info_wrap_widget.h
index f102cc834..0759c0729 100644
--- a/Telegram/SourceFiles/info/info_wrap_widget.h
+++ b/Telegram/SourceFiles/info/info_wrap_widget.h
@@ -20,6 +20,7 @@ class PlainShadow;
 class PopupMenu;
 class IconButton;
 class RoundRect;
+struct StringWithNumbers;
 } // namespace Ui
 
 namespace Window {
@@ -61,11 +62,10 @@ struct SelectedItem {
 };
 
 struct SelectedItems {
-	explicit SelectedItems(Storage::SharedMediaType type)
-	: type(type) {
-	}
+	SelectedItems() = default;
+	explicit SelectedItems(Storage::SharedMediaType type);
 
-	Storage::SharedMediaType type;
+	Fn<Ui::StringWithNumbers(int)> title;
 	std::vector<SelectedItem> list;
 };
 
diff --git a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp
index 2cddcf63e..85878baf1 100644
--- a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp
+++ b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp
@@ -248,6 +248,14 @@ void Widget::enableBackButton() {
 	_flexibleScroll.backButtonEnables.fire({});
 }
 
+rpl::producer<SelectedItems> Widget::selectedListValue() const {
+	return _inner->selectedListValue();
+}
+
+void Widget::selectionAction(SelectionAction action) {
+	_inner->selectionAction(action);
+}
+
 void Widget::saveState(not_null<Memento*> memento) {
 	memento->setScrollTop(scrollTopSave());
 }
diff --git a/Telegram/SourceFiles/info/settings/info_settings_widget.h b/Telegram/SourceFiles/info/settings/info_settings_widget.h
index d2eb63615..7ab7968d5 100644
--- a/Telegram/SourceFiles/info/settings/info_settings_widget.h
+++ b/Telegram/SourceFiles/info/settings/info_settings_widget.h
@@ -80,6 +80,9 @@ public:
 
 	void enableBackButton() override;
 
+	rpl::producer<SelectedItems> selectedListValue() const override;
+	void selectionAction(SelectionAction action) override;
+
 private:
 	void saveState(not_null<Memento*> memento);
 	void restoreState(not_null<Memento*> memento);
diff --git a/Telegram/SourceFiles/settings/business/settings_away_message.cpp b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
index 6c820afa9..180145716 100644
--- a/Telegram/SourceFiles/settings/business/settings_away_message.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
@@ -38,7 +38,6 @@ public:
 	~AwayMessage();
 
 	[[nodiscard]] rpl::producer<QString> title() override;
-
 	[[nodiscard]] rpl::producer<Type> sectionShowOther() override;
 
 private:
diff --git a/Telegram/SourceFiles/settings/business/settings_greeting.cpp b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
index 7e5ad6b1e..81580801c 100644
--- a/Telegram/SourceFiles/settings/business/settings_greeting.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
@@ -41,6 +41,7 @@ public:
 	~Greeting();
 
 	[[nodiscard]] rpl::producer<QString> title() override;
+	[[nodiscard]] rpl::producer<Type> sectionShowOther() override;
 
 	const Ui::RoundRect *bottomSkipRounding() const {
 		return &_bottomSkipRounding;
@@ -176,6 +177,10 @@ rpl::producer<QString> Greeting::title() {
 	return tr::lng_greeting_title();
 }
 
+rpl::producer<Type> Greeting::sectionShowOther() {
+	return _showOther.events();
+}
+
 void Greeting::setupContent(
 		not_null<Window::SessionController*> controller) {
 	using namespace rpl::mappers;
diff --git a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
index dc8927bc2..e13f822a2 100644
--- a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
@@ -8,16 +8,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "settings/business/settings_quick_replies.h"
 
 #include "core/application.h"
+#include "data/business/data_shortcut_messages.h"
 #include "data/data_session.h"
 #include "lang/lang_keys.h"
 #include "main/main_session.h"
 #include "settings/business/settings_recipients_helper.h"
+#include "settings/business/settings_shortcut_messages.h"
+#include "ui/layers/generic_box.h"
 #include "ui/text/text_utilities.h"
 #include "ui/widgets/buttons.h"
+#include "ui/widgets/fields/input_field.h"
 #include "ui/wrap/slide_wrap.h"
 #include "ui/wrap/vertical_layout.h"
 #include "ui/vertical_list.h"
 #include "window/window_session_controller.h"
+#include "styles/style_chat_helpers.h"
+#include "styles/style_layers.h"
 #include "styles/style_settings.h"
 
 namespace Settings {
@@ -31,12 +37,13 @@ public:
 	~QuickReplies();
 
 	[[nodiscard]] rpl::producer<QString> title() override;
+	[[nodiscard]] rpl::producer<Type> sectionShowOther() override;
 
 private:
 	void setupContent(not_null<Window::SessionController*> controller);
 	void save();
 
-	rpl::variable<Data::BusinessRecipients> _recipients;
+	rpl::event_stream<Type> _showOther;
 
 };
 
@@ -57,6 +64,10 @@ rpl::producer<QString> QuickReplies::title() {
 	return tr::lng_replies_title();
 }
 
+rpl::producer<Type> QuickReplies::sectionShowOther() {
+	return _showOther.events();
+}
+
 void QuickReplies::setupContent(
 		not_null<Window::SessionController*> controller) {
 	using namespace rpl::mappers;
@@ -73,24 +84,82 @@ void QuickReplies::setupContent(
 	});
 
 	Ui::AddSkip(content);
-	const auto enabled = content->add(object_ptr<Ui::SettingsButton>(
+	const auto add = content->add(object_ptr<Ui::SettingsButton>(
 		content,
 		tr::lng_replies_add(),
 		st::settingsButtonNoIcon
 	));
 
-	enabled->setClickedCallback([=] {
+	const auto owner = &controller->session().data();
+	const auto messages = &owner->shortcutMessages();
 
+	add->setClickedCallback([=] {
+		controller->show(Box([=](not_null<Ui::GenericBox*> box) {
+			box->setTitle(tr::lng_replies_add_title());
+			box->addRow(object_ptr<Ui::FlatLabel>(
+				box,
+				tr::lng_replies_add_shortcut(),
+				st::settingsAddReplyLabel));
+			const auto field = box->addRow(object_ptr<Ui::InputField>(
+				box,
+				st::settingsAddReplyField,
+				tr::lng_replies_add_placeholder(),
+				QString()));
+			box->setFocusCallback([=] {
+				field->setFocusFast();
+			});
+
+			const auto submit = [=] {
+				const auto weak = Ui::MakeWeak(box);
+				const auto name = field->getLastText().trimmed();
+				if (name.isEmpty()) {
+					field->showError();
+				} else {
+					const auto id = messages->emplaceShortcut(name);
+					_showOther.fire(ShortcutMessagesId(id));
+				}
+				if (const auto strong = weak.data()) {
+					strong->closeBox();
+				}
+			};
+			field->submits(
+			) | rpl::start_with_next(submit, field->lifetime());
+			box->addButton(tr::lng_settings_save(), submit);
+			box->addButton(tr::lng_cancel(), [=] {
+				box->closeBox();
+			});
+		}));
 	});
 
-	const auto wrap = content->add(
-		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
-			content,
-			object_ptr<Ui::VerticalLayout>(content)));
-	const auto inner = wrap->entity();
+	Ui::AddSkip(content);
+	Ui::AddDivider(content);
+	Ui::AddSkip(content);
 
-	Ui::AddSkip(inner);
-	Ui::AddDivider(inner);
+	const auto inner = content->add(
+		object_ptr<Ui::VerticalLayout>(content));
+	rpl::single(rpl::empty) | rpl::then(
+		messages->shortcutsChanged()
+	) | rpl::start_with_next([=] {
+		while (inner->count()) {
+			delete inner->widgetAt(0);
+		}
+		const auto &shortcuts = messages->shortcuts();
+		auto i = 0;
+		for (const auto &shortcut : shortcuts.list) {
+			const auto name = shortcut.second.name;
+			AddButtonWithLabel(
+				inner,
+				rpl::single('/' + name),
+				tr::lng_forum_messages(
+					lt_count,
+					rpl::single(1. * shortcut.second.count)),
+				st::settingsButtonNoIcon
+			)->setClickedCallback([=] {
+				const auto id = messages->emplaceShortcut(name);
+				_showOther.fire(ShortcutMessagesId(id));
+			});
+		}
+	}, content->lifetime());
 
 	Ui::ResizeFitChild(this, content);
 }
diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
index df5f778ba..e2186a172 100644
--- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
@@ -30,14 +30,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "history/view/history_view_sticker_toast.h"
 #include "history/history.h"
 #include "history/history_item.h"
+#include "info/info_wrap_widget.h"
 #include "inline_bots/inline_bot_result.h"
 #include "lang/lang_keys.h"
+#include "lang/lang_numbers_animation.h"
 #include "main/main_session.h"
 #include "menu/menu_send.h"
 #include "settings/business/settings_recipients_helper.h"
 #include "storage/localimageloader.h"
 #include "storage/storage_account.h"
 #include "storage/storage_media_prepare.h"
+#include "storage/storage_shared_media.h"
 #include "ui/chat/attach/attach_send_files_way.h"
 #include "ui/chat/chat_style.h"
 #include "ui/chat/chat_theme.h"
@@ -72,13 +75,16 @@ public:
 	[[nodiscard]] static Type Id(BusinessShortcutId shortcutId);
 
 	[[nodiscard]] Type id() const final override {
-		return Id(_shortcutId);
+		return Id(_shortcutId.current());
 	}
 
 	[[nodiscard]] rpl::producer<QString> title() override;
 	[[nodiscard]] rpl::producer<> sectionShowBack() override;
 	void setInnerFocus() override;
 
+	rpl::producer<Info::SelectedItems> selectedListValue() override;
+	void selectionAction(Info::SelectionAction action) override;
+
 	bool paintOuter(
 		not_null<QWidget*> outer,
 		int maxVisibleHeight,
@@ -238,8 +244,9 @@ private:
 	const not_null<Window::SessionController*> _controller;
 	const not_null<Main::Session*> _session;
 	const not_null<Ui::ScrollArea*> _scroll;
-	const BusinessShortcutId _shortcutId;
 	const not_null<History*> _history;
+	rpl::variable<BusinessShortcutId> _shortcutId;
+	rpl::variable<QString> _shortcut;
 	std::shared_ptr<Ui::ChatStyle> _style;
 	std::shared_ptr<Ui::ChatTheme> _theme;
 	QPointer<ListWidget> _inner;
@@ -248,6 +255,9 @@ private:
 	rpl::event_stream<> _showBackRequests;
 	bool _skipScrollEvent = false;
 
+	rpl::variable<Info::SelectedItems> _selectedItems
+		= Info::SelectedItems(Storage::SharedMediaType::kCount);
+
 	std::unique_ptr<StickerToast> _stickerToast;
 
 	FullMsgId _lastShownAt;
@@ -287,12 +297,31 @@ ShortcutMessages::ShortcutMessages(
 , _controller(controller)
 , _session(&controller->session())
 , _scroll(scroll)
-, _shortcutId(shortcutId)
 , _history(_session->data().history(_session->user()->id))
+, _shortcutId(shortcutId)
+, _shortcut(
+	_session->data().shortcutMessages().lookupShortcut(shortcutId).name)
 , _cornerButtons(
 		_scroll,
 		controller->chatStyle(),
 		static_cast<HistoryView::CornerButtonsDelegate*>(this)) {
+	const auto messages = &_session->data().shortcutMessages();
+
+	messages->shortcutIdChanged(
+	) | rpl::start_with_next([=](Data::ShortcutIdChange change) {
+		if (change.oldId == _shortcutId.current()) {
+			if (change.newId) {
+				_shortcutId = change.newId;
+			} else {
+				_showBackRequests.fire({});
+			}
+		}
+	}, lifetime());
+	messages->shortcutsChanged(
+	) | rpl::start_with_next([=] {
+		_shortcut = messages->lookupShortcut(_shortcutId.current()).name;
+	}, lifetime());
+
 	controller->chatStyle()->paletteChanged(
 	) | rpl::start_with_next([=] {
 		_scroll->updateBars();
@@ -351,7 +380,13 @@ Type ShortcutMessages::Id(BusinessShortcutId shortcutId) {
 }
 
 rpl::producer<QString> ShortcutMessages::title() {
-	return rpl::single(u"Editing messages list"_q);
+	return _shortcut.value() | rpl::map([=](const QString &shortcut) {
+		return (shortcut == u"away"_q)
+			? tr::lng_away_title()
+			: (shortcut == u"hello"_q)
+			? tr::lng_greeting_title()
+			: rpl::single('/' + shortcut);
+	}) | rpl::flatten_latest();
 }
 
 void ShortcutMessages::processScroll() {
@@ -379,6 +414,18 @@ void ShortcutMessages::setInnerFocus() {
 	_composeControls->focus();
 }
 
+rpl::producer<Info::SelectedItems> ShortcutMessages::selectedListValue() {
+	return _selectedItems.value();
+}
+
+void ShortcutMessages::selectionAction(Info::SelectionAction action) {
+	switch (action) {
+	case Info::SelectionAction::Clear: clearSelected(); return;
+	case Info::SelectionAction::Delete: confirmDeleteSelected(); return;
+	}
+	Unexpected("Action in ShortcutMessages::selectionAction.");
+}
+
 bool ShortcutMessages::paintOuter(
 		not_null<QWidget*> outer,
 		int maxVisibleHeight,
@@ -572,7 +619,7 @@ bool ShortcutMessages::listScrollTo(int top, bool syntetic) {
 
 void ShortcutMessages::listCancelRequest() {
 	if (_inner && !_inner->getSelectedItems().empty()) {
-		//clearSelected();
+		clearSelected();
 		return;
 	} else if (_composeControls->handleCancelRequest()) {
 		return;
@@ -592,13 +639,15 @@ rpl::producer<Data::MessagesSlice> ShortcutMessages::listSource(
 		Data::MessagePosition aroundId,
 		int limitBefore,
 		int limitAfter) {
-	const auto data = &_controller->session().data();
-	return rpl::single(rpl::empty) | rpl::then(
-		data->shortcutMessages().updates(_shortcutId)
-	) | rpl::map([=] {
-		return data->shortcutMessages().list(_shortcutId);
-	});
-	return rpl::never<Data::MessagesSlice>();
+	const auto messages = &_session->data().shortcutMessages();
+	return _shortcutId.value(
+	) | rpl::map([=](BusinessShortcutId shortcutId) {
+		return rpl::single(rpl::empty) | rpl::then(
+			messages->updates(shortcutId)
+		) | rpl::map([=] {
+			return messages->list(shortcutId);
+		});
+	}) | rpl::flatten_latest();
 }
 
 bool ShortcutMessages::listAllowsMultiSelect() {
@@ -617,6 +666,24 @@ bool ShortcutMessages::listIsLessInOrder(
 }
 
 void ShortcutMessages::listSelectionChanged(SelectedItems &&items) {
+	auto value = Info::SelectedItems();
+	value.title = [](int count) {
+		return tr::lng_forum_messages(
+			tr::now,
+			lt_count,
+			count,
+			Ui::StringWithNumbers::FromString);
+	};
+	value.list = items | ranges::views::transform([](SelectedItem item) {
+		auto result = Info::SelectedItem(GlobalMsgId{ item.msgId });
+		result.canDelete = item.canDelete;
+		return result;
+	}) | ranges::to_vector;
+	_selectedItems = std::move(value);
+
+	if (items.empty()) {
+		doSetInnerFocus();
+	}
 }
 
 void ShortcutMessages::listMarkReadTill(not_null<HistoryItem*> item) {
@@ -784,20 +851,21 @@ bool ShortcutMessages::cornerButtonsHas(CornerButtonType type) {
 }
 
 void ShortcutMessages::pushReplyReturn(not_null<HistoryItem*> item) {
-	if (item->shortcutId() == _shortcutId) {
+	if (item->shortcutId() == _shortcutId.current()) {
 		_cornerButtons.pushReplyReturn(item);
 	}
 }
 
 void ShortcutMessages::checkReplyReturns() {
 	const auto currentTop = _scroll->scrollTop();
+	const auto shortcutId = _shortcutId.current();
 	while (const auto replyReturn = _cornerButtons.replyReturn()) {
 		const auto position = replyReturn->position();
 		const auto scrollTop = _inner->scrollTopForPosition(position);
 		const auto below = scrollTop
 			? (currentTop >= std::min(*scrollTop, _scroll->scrollTopMax()))
 			: _inner->isBelowPosition(position);
-		if (below) {
+		if (replyReturn->shortcutId() != shortcutId || below) {
 			_cornerButtons.calculateNextReplyReturn();
 		} else {
 			break;
@@ -858,7 +926,7 @@ Api::SendAction ShortcutMessages::prepareSendAction(
 		Api::SendOptions options) const {
 	auto result = Api::SendAction(_history, options);
 	result.replyTo = replyTo();
-	result.options.shortcutId = _shortcutId;
+	result.options.shortcutId = _shortcutId.current();
 	result.options.sendAs = _composeControls->sendAsPeer();
 	return result;
 }
diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style
index 41fb908e0..fcdd0a7cd 100644
--- a/Telegram/SourceFiles/settings/settings.style
+++ b/Telegram/SourceFiles/settings/settings.style
@@ -616,3 +616,19 @@ settingsWorkingHoursPicker: 200px;
 settingsWorkingHoursPickerItemHeight: 40px;
 
 settingsAwaySchedulePadding: margins(0px, 8px, 0px, 8px);
+
+settingsAddReplyLabel: FlatLabel(defaultFlatLabel) {
+	minWidth: 256px;
+}
+settingsAddReplyField: InputField(defaultInputField) {
+	textBg: transparent;
+	textMargins: margins(0px, 10px, 0px, 2px);
+
+	placeholderFg: placeholderFg;
+	placeholderFgActive: placeholderFgActive;
+	placeholderFgError: placeholderFgActive;
+	placeholderMargins: margins(2px, 0px, 2px, 0px);
+	placeholderScale: 0.;
+
+	heightMin: 36px;
+}
\ No newline at end of file
diff --git a/Telegram/SourceFiles/settings/settings_common.h b/Telegram/SourceFiles/settings/settings_common.h
index 02beb1003..e401ad419 100644
--- a/Telegram/SourceFiles/settings/settings_common.h
+++ b/Telegram/SourceFiles/settings/settings_common.h
@@ -17,6 +17,11 @@ namespace anim {
 enum class repeat : uchar;
 } // namespace anim
 
+namespace Info {
+struct SelectedItems;
+enum class SelectionAction;
+} // namespace Info
+
 namespace Main {
 class Session;
 } // namespace Main
@@ -91,6 +96,13 @@ public:
 	virtual void setStepDataReference(std::any &data) {
 	}
 
+	[[nodiscard]] virtual auto selectedListValue()
+	-> rpl::producer<Info::SelectedItems> {
+		return nullptr;
+	}
+	virtual void selectionAction(Info::SelectionAction action) {
+	}
+
 	virtual bool paintOuter(
 			not_null<QWidget*> outer,
 			int maxVisibleHeight,

From aad8e989d8e8e6050602d7d704dcacc71b3ba300 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Wed, 28 Feb 2024 14:55:28 +0400
Subject: [PATCH 058/108] Shortcuts edit / delete menu.

---
 Telegram/Resources/langs/lang.strings         |   4 +
 .../data/business/data_shortcut_messages.cpp  |  65 ++++++++++
 .../data/business/data_shortcut_messages.h    |   6 +
 .../info/downloads/info_downloads_widget.cpp  |  29 +++++
 .../info/downloads/info_downloads_widget.h    |   2 +
 .../SourceFiles/info/info_content_widget.cpp  |  19 +++
 .../SourceFiles/info/info_content_widget.h    |   5 +
 .../SourceFiles/info/info_wrap_widget.cpp     |  85 ++++---------
 Telegram/SourceFiles/info/info_wrap_widget.h  |   2 +-
 .../info/settings/info_settings_widget.cpp    |   4 +
 .../info/settings/info_settings_widget.h      |   1 +
 .../business/settings_away_message.cpp        |   8 +-
 .../settings/business/settings_greeting.cpp   |   8 +-
 .../business/settings_quick_replies.cpp       | 116 ++++++++++--------
 .../business/settings_quick_replies.h         |   9 ++
 .../business/settings_shortcut_messages.cpp   |  60 ++++++++-
 .../settings_cloud_password_email_confirm.cpp |  22 ++++
 .../settings/settings_advanced.cpp            |   8 +-
 .../SourceFiles/settings/settings_advanced.h  |   4 -
 .../settings/settings_business.cpp            |   9 +-
 .../SourceFiles/settings/settings_chat.cpp    |  12 +-
 Telegram/SourceFiles/settings/settings_chat.h |   5 +
 .../SourceFiles/settings/settings_common.h    |   3 +
 .../settings/settings_common_session.cpp      |  46 +------
 .../settings/settings_common_session.h        |  23 +++-
 .../settings/settings_local_passcode.cpp      |   8 +-
 .../SourceFiles/settings/settings_main.cpp    |  37 ++++--
 Telegram/SourceFiles/settings/settings_main.h |   4 +-
 .../settings/settings_notifications.cpp       |   8 +-
 .../settings/settings_notifications.h         |   4 -
 .../settings/settings_privacy_security.cpp    |   8 +-
 .../settings/settings_privacy_security.h      |   4 -
 32 files changed, 391 insertions(+), 237 deletions(-)

diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index dd5746e44..896fdc415 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -2212,6 +2212,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_replies_edit_title" = "Edit Shortcut";
 "lng_replies_edit_about" = "Edit the name for this shortcut.";
 "lng_replies_message_placeholder" = "Add a Quick Reply";
+"lng_replies_delete_sure" = "Are you sure you want to delete this quick reply with all its messages?";
+"lng_replies_error_occupied" = "This shortcut is already used.";
 
 "lng_greeting_title" = "Greeting Message";
 "lng_greeting_about" = "Greet customers when they message you the first time or after a period of no activity.";
@@ -3027,6 +3029,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_context_translate_selected" = "Translate Selected Text";
 "lng_context_read_hidden" = "read";
 "lng_context_read_show" = "show when";
+"lng_context_edit_shortcut" = "Edit Shortcut";
+"lng_context_delete_shortcut" = "Delete Quick Reply";
 
 "lng_add_tag_about" = "Tag this message with an emoji for quick search.";
 "lng_subscribe_tag_about" = "Organize your Saved Messages with tags. {link}";
diff --git a/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp b/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
index 8b59309bf..d9d75ec52 100644
--- a/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
+++ b/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
@@ -496,6 +496,71 @@ Shortcut ShortcutMessages::lookupShortcut(BusinessShortcutId id) const {
 	return i->second;
 }
 
+void ShortcutMessages::editShortcut(
+		BusinessShortcutId id,
+		QString name,
+		Fn<void()> done,
+		Fn<void(QString)> fail) {
+	name = name.trimmed();
+	if (name.isEmpty()) {
+		fail(QString());
+		return;
+	}
+	const auto finish = [=] {
+		const auto i = _shortcuts.list.find(id);
+		if (i != end(_shortcuts.list)) {
+			i->second.name = name;
+			_shortcutsChanged.fire({});
+		}
+		done();
+	};
+	for (const auto &[existingId, shortcut] : _shortcuts.list) {
+		if (shortcut.name == name) {
+			if (existingId == id) {
+				//done();
+				//return;
+				break;
+			} else if (_data[existingId].items.empty() && !shortcut.count) {
+				removeShortcut(existingId);
+				break;
+			} else {
+				fail(u"SHORTCUT_OCCUPIED"_q);
+				return;
+			}
+		}
+	}
+	_session->api().request(MTPmessages_EditQuickReplyShortcut(
+		MTP_int(id),
+		MTP_string(name)
+	)).done(finish).fail([=](const MTP::Error &error) {
+		const auto type = error.type();
+		if (type == u"SHORTCUT_ID_INVALID"_q) {
+			// Not on the server (yet).
+			finish();
+		} else {
+			fail(type);
+		}
+	}).send();
+}
+
+void ShortcutMessages::removeShortcut(BusinessShortcutId shortcutId) {
+	auto i = _data.find(shortcutId);
+	while (i != end(_data)) {
+		if (i->second.items.empty()) {
+			_data.erase(i);
+		} else {
+			i->second.items.front()->destroy();
+		}
+		i = _data.find(shortcutId);
+	}
+	_shortcuts.list.remove(shortcutId);
+	_shortcutIdChanges.fire({ shortcutId, 0 });
+
+	_session->api().request(MTPmessages_DeleteQuickReplyShortcut(
+		MTP_int(shortcutId)
+	)).send();
+}
+
 void ShortcutMessages::cancelRequest(BusinessShortcutId shortcutId) {
 	const auto j = _requests.find(shortcutId);
 	if (j != end(_requests)) {
diff --git a/Telegram/SourceFiles/data/business/data_shortcut_messages.h b/Telegram/SourceFiles/data/business/data_shortcut_messages.h
index 6b7343b57..57c1205f8 100644
--- a/Telegram/SourceFiles/data/business/data_shortcut_messages.h
+++ b/Telegram/SourceFiles/data/business/data_shortcut_messages.h
@@ -78,6 +78,12 @@ public:
 	[[nodiscard]] rpl::producer<ShortcutIdChange> shortcutIdChanged() const;
 	[[nodiscard]] BusinessShortcutId emplaceShortcut(QString name);
 	[[nodiscard]] Shortcut lookupShortcut(BusinessShortcutId id) const;
+	void editShortcut(
+		BusinessShortcutId id,
+		QString name,
+		Fn<void()> done,
+		Fn<void(QString)> fail);
+	void removeShortcut(BusinessShortcutId shortcutId);
 
 private:
 	using OwnedItem = std::unique_ptr<HistoryItem, HistoryItem::Destroyer>;
diff --git a/Telegram/SourceFiles/info/downloads/info_downloads_widget.cpp b/Telegram/SourceFiles/info/downloads/info_downloads_widget.cpp
index 6efd528f6..9203981dc 100644
--- a/Telegram/SourceFiles/info/downloads/info_downloads_widget.cpp
+++ b/Telegram/SourceFiles/info/downloads/info_downloads_widget.cpp
@@ -10,13 +10,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "info/downloads/info_downloads_inner_widget.h"
 #include "info/info_controller.h"
 #include "info/info_memento.h"
+#include "ui/boxes/confirm_box.h"
 #include "ui/search_field_controller.h"
+#include "ui/widgets/menu/menu_add_action_callback.h"
 #include "ui/widgets/scroll_area.h"
 #include "data/data_download_manager.h"
 #include "data/data_user.h"
 #include "core/application.h"
 #include "lang/lang_keys.h"
 #include "styles/style_info.h"
+#include "styles/style_layers.h"
+#include "styles/style_menu_icons.h"
 
 namespace Info::Downloads {
 
@@ -102,6 +106,31 @@ void Widget::selectionAction(SelectionAction action) {
 	_inner->selectionAction(action);
 }
 
+void Widget::fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) {
+	const auto window = controller()->parentController();
+	const auto deleteAll = [=] {
+		auto &manager = Core::App().downloadManager();
+		const auto phrase = tr::lng_downloads_delete_sure_all(tr::now);
+		const auto added = manager.loadedHasNonCloudFile()
+			? QString()
+			: tr::lng_downloads_delete_in_cloud(tr::now);
+		const auto deleteSure = [=, &manager](Fn<void()> close) {
+			Ui::PostponeCall(this, close);
+			manager.deleteAll();
+		};
+		window->show(Ui::MakeConfirmBox({
+			.text = phrase + (added.isEmpty() ? QString() : "\n\n" + added),
+			.confirmed = deleteSure,
+			.confirmText = tr::lng_box_delete(tr::now),
+			.confirmStyle = &st::attentionBoxButton,
+		}));
+	};
+	addAction(
+		tr::lng_context_delete_all_files(tr::now),
+		deleteAll,
+		&st::menuIconDelete);
+}
+
 rpl::producer<QString> Widget::title() {
 	return tr::lng_downloads_section();
 }
diff --git a/Telegram/SourceFiles/info/downloads/info_downloads_widget.h b/Telegram/SourceFiles/info/downloads/info_downloads_widget.h
index 3da1a4f79..f79a31466 100644
--- a/Telegram/SourceFiles/info/downloads/info_downloads_widget.h
+++ b/Telegram/SourceFiles/info/downloads/info_downloads_widget.h
@@ -57,6 +57,8 @@ public:
 	rpl::producer<SelectedItems> selectedListValue() const override;
 	void selectionAction(SelectionAction action) override;
 
+	void fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) override;
+
 	rpl::producer<QString> title() override;
 
 private:
diff --git a/Telegram/SourceFiles/info/info_content_widget.cpp b/Telegram/SourceFiles/info/info_content_widget.cpp
index ab5a0ef5e..adf4f22d2 100644
--- a/Telegram/SourceFiles/info/info_content_widget.cpp
+++ b/Telegram/SourceFiles/info/info_content_widget.cpp
@@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_forum_topic.h"
 #include "data/data_forum.h"
 #include "main/main_session.h"
+#include "window/window_peer_menu.h"
 #include "styles/style_info.h"
 #include "styles/style_profile.h"
 #include "styles/style_layers.h"
@@ -263,6 +264,24 @@ QRect ContentWidget::floatPlayerAvailableRect() const {
 	return mapToGlobal(_scroll->geometry());
 }
 
+void ContentWidget::fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) {
+	const auto peer = _controller->key().peer();
+	const auto topic = _controller->key().topic();
+	if (!peer && !topic) {
+		return;
+	}
+
+	Window::FillDialogsEntryMenu(
+		_controller->parentController(),
+		Dialogs::EntryState{
+			.key = (topic
+				? Dialogs::Key{ topic }
+				: Dialogs::Key{ peer->owner().history(peer) }),
+			.section = Dialogs::EntryState::Section::Profile,
+		},
+		addAction);
+}
+
 rpl::producer<SelectedItems> ContentWidget::selectedListValue() const {
 	return rpl::single(SelectedItems(Storage::SharedMediaType::Photo));
 }
diff --git a/Telegram/SourceFiles/info/info_content_widget.h b/Telegram/SourceFiles/info/info_content_widget.h
index b8d1ebe4b..29d947c66 100644
--- a/Telegram/SourceFiles/info/info_content_widget.h
+++ b/Telegram/SourceFiles/info/info_content_widget.h
@@ -28,6 +28,10 @@ template <typename Widget>
 class PaddingWrap;
 } // namespace Ui
 
+namespace Ui::Menu {
+struct MenuCallback;
+} // namespace Ui::Menu
+
 namespace Info::Settings {
 struct Tag;
 } // namespace Info::Settings
@@ -95,6 +99,7 @@ public:
 	virtual rpl::producer<SelectedItems> selectedListValue() const;
 	virtual void selectionAction(SelectionAction action) {
 	}
+	virtual void fillTopBarMenu(const Ui::Menu::MenuCallback &addAction);
 
 	[[nodiscard]] virtual rpl::producer<QString> title() = 0;
 	[[nodiscard]] virtual rpl::producer<QString> subtitle() {
diff --git a/Telegram/SourceFiles/info/info_wrap_widget.cpp b/Telegram/SourceFiles/info/info_wrap_widget.cpp
index ddab6ef51..a2c6a781c 100644
--- a/Telegram/SourceFiles/info/info_wrap_widget.cpp
+++ b/Telegram/SourceFiles/info/info_wrap_widget.cpp
@@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "settings/settings_main.h"
 #include "settings/settings_premium.h"
 #include "ui/effects/ripple_animation.h" // MaskByDrawer.
+#include "ui/widgets/menu/menu_add_action_callback.h"
 #include "ui/widgets/discrete_sliders.h"
 #include "ui/widgets/buttons.h"
 #include "ui/widgets/shadow.h"
@@ -31,7 +32,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "core/shortcuts.h"
 #include "window/window_session_controller.h"
 #include "window/window_slide_animation.h"
-#include "window/window_peer_menu.h"
 #include "boxes/peer_list_box.h"
 #include "ui/boxes/confirm_box.h"
 #include "main/main_session.h"
@@ -364,16 +364,24 @@ void WrapWidget::createTopBar() {
 			_controller->searchEnabledByContent(),
 			_controller->takeSearchStartsFocused());
 	}
+	_topBar->lower();
+	_topBar->resizeToWidth(width());
+	_topBar->finishAnimating();
+	_topBar->show();
+}
+
+void WrapWidget::setupTopBarMenuToggle() {
+	Expects(_content != nullptr);
+
+	if (!_topBar) {
+		return;
+	}
 	const auto section = _controller->section();
 	if (section.type() == Section::Type::Profile
-		&& (wrapValue != Wrap::Side || hasStackHistory())) {
+		&& (wrap() != Wrap::Side || hasStackHistory())) {
 		addTopBarMenuButton();
 		addProfileCallsButton();
-	} else if (section.type() == Section::Type::Settings
-		&& (section.settingsType()
-				== ::Settings::CloudPasswordEmailConfirmId()
-			|| section.settingsType() == ::Settings::Main::Id()
-			|| section.settingsType() == ::Settings::Chat::Id())) {
+	} else if (section.type() == Section::Type::Settings) {
 		addTopBarMenuButton();
 	} else if (section.type() == Section::Type::Downloads) {
 		auto &manager = Core::App().downloadManager();
@@ -399,11 +407,6 @@ void WrapWidget::createTopBar() {
 			}
 		}, _topBar->lifetime());
 	}
-
-	_topBar->lower();
-	_topBar->resizeToWidth(width());
-	_topBar->finishAnimating();
-	_topBar->show();
 }
 
 void WrapWidget::checkBeforeClose(Fn<void()> close) {
@@ -413,11 +416,12 @@ void WrapWidget::checkBeforeClose(Fn<void()> close) {
 
 void WrapWidget::addTopBarMenuButton() {
 	Expects(_topBar != nullptr);
+	Expects(_content != nullptr);
 
 	{
 		const auto guard = gsl::finally([&] { _topBarMenu = nullptr; });
 		showTopBarMenu(true);
-		if (_topBarMenu->empty()) {
+		if (!_topBarMenu) {
 			return;
 		}
 	}
@@ -486,65 +490,19 @@ void WrapWidget::showTopBarMenu(bool check) {
 		}
 	});
 
-	const auto addAction = Ui::Menu::CreateAddActionCallback(_topBarMenu);
-	if (key().isDownloads()) {
-		addAction(
-			tr::lng_context_delete_all_files(tr::now),
-			[=] { deleteAllDownloads(); },
-			&st::menuIconDelete);
-	} else if (const auto peer = key().peer()) {
-		const auto topic = key().topic();
-		Window::FillDialogsEntryMenu(
-			_controller->parentController(),
-			Dialogs::EntryState{
-				.key = (topic
-					? Dialogs::Key{ topic }
-					: Dialogs::Key{ peer->owner().history(peer) }),
-				.section = Dialogs::EntryState::Section::Profile,
-			},
-			addAction);
-	} else if (const auto self = key().settingsSelf()) {
-		const auto showOther = [=](::Settings::Type type) {
-			const auto controller = _controller.get();
-			_topBarMenu = nullptr;
-			controller->showSettings(type);
-		};
-		::Settings::FillMenu(
-			_controller->parentController(),
-			_controller->section().settingsType(),
-			showOther,
-			addAction);
-	} else {
+	_content->fillTopBarMenu(Ui::Menu::CreateAddActionCallback(_topBarMenu));
+	if (_topBarMenu->empty()) {
 		_topBarMenu = nullptr;
 		return;
+	} else if (check) {
+		return;
 	}
 	_topBarMenu->setForcedOrigin(Ui::PanelAnimation::Origin::TopRight);
-	if (check) {
-		return;
-	}
 	_topBarMenuToggle->setForceRippled(true);
 	_topBarMenu->popup(_topBarMenuToggle->mapToGlobal(
 		st::infoLayerTopBarMenuPosition));
 }
 
-void WrapWidget::deleteAllDownloads() {
-	auto &manager = Core::App().downloadManager();
-	const auto phrase = tr::lng_downloads_delete_sure_all(tr::now);
-	const auto added = manager.loadedHasNonCloudFile()
-		? QString()
-		: tr::lng_downloads_delete_in_cloud(tr::now);
-	const auto deleteSure = [=, &manager](Fn<void()> close) {
-		Ui::PostponeCall(this, close);
-		manager.deleteAll();
-	};
-	_controller->parentController()->show(Ui::MakeConfirmBox({
-		.text = phrase + (added.isEmpty() ? QString() : "\n\n" + added),
-		.confirmed = deleteSure,
-		.confirmText = tr::lng_box_delete(tr::now),
-		.confirmStyle = &st::attentionBoxButton,
-	}));
-}
-
 bool WrapWidget::requireTopBarSearch() const {
 	if (!_topBar || !_controller->searchFieldController()) {
 		return false;
@@ -619,6 +577,7 @@ void WrapWidget::showContent(object_ptr<ContentWidget> content) {
 }
 
 void WrapWidget::finishShowContent() {
+	setupTopBarMenuToggle();
 	updateContentGeometry();
 	_content->setIsStackBottom(!hasStackHistory());
 	if (_topBar) {
diff --git a/Telegram/SourceFiles/info/info_wrap_widget.h b/Telegram/SourceFiles/info/info_wrap_widget.h
index 0759c0729..db76d4b43 100644
--- a/Telegram/SourceFiles/info/info_wrap_widget.h
+++ b/Telegram/SourceFiles/info/info_wrap_widget.h
@@ -172,6 +172,7 @@ private:
 		not_null<ContentMemento*> memento,
 		const Window::SectionShow &params);
 	void setupTop();
+	void setupTopBarMenuToggle();
 	void createTopBar();
 	void highlightTopBar();
 	void setupShortcuts();
@@ -202,7 +203,6 @@ private:
 	void addTopBarMenuButton();
 	void addProfileCallsButton();
 	void showTopBarMenu(bool check);
-	void deleteAllDownloads();
 
 	rpl::variable<Wrap> _wrap;
 	std::unique_ptr<Controller> _controller;
diff --git a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp
index 85878baf1..3524be661 100644
--- a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp
+++ b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp
@@ -256,6 +256,10 @@ void Widget::selectionAction(SelectionAction action) {
 	_inner->selectionAction(action);
 }
 
+void Widget::fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) {
+	_inner->fillTopBarMenu(addAction);
+}
+
 void Widget::saveState(not_null<Memento*> memento) {
 	memento->setScrollTop(scrollTopSave());
 }
diff --git a/Telegram/SourceFiles/info/settings/info_settings_widget.h b/Telegram/SourceFiles/info/settings/info_settings_widget.h
index 7ab7968d5..09fca4419 100644
--- a/Telegram/SourceFiles/info/settings/info_settings_widget.h
+++ b/Telegram/SourceFiles/info/settings/info_settings_widget.h
@@ -82,6 +82,7 @@ public:
 
 	rpl::producer<SelectedItems> selectedListValue() const override;
 	void selectionAction(SelectionAction action) override;
+	void fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) override;
 
 private:
 	void saveState(not_null<Memento*> memento);
diff --git a/Telegram/SourceFiles/settings/business/settings_away_message.cpp b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
index 180145716..69ac74bee 100644
--- a/Telegram/SourceFiles/settings/business/settings_away_message.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
@@ -38,13 +38,11 @@ public:
 	~AwayMessage();
 
 	[[nodiscard]] rpl::producer<QString> title() override;
-	[[nodiscard]] rpl::producer<Type> sectionShowOther() override;
 
 private:
 	void setupContent(not_null<Window::SessionController*> controller);
 	void save();
 
-	rpl::event_stream<Type> _showOther;
 	rpl::variable<Data::BusinessRecipients> _recipients;
 	rpl::variable<Data::AwaySchedule> _schedule;
 	rpl::variable<bool> _enabled;
@@ -201,10 +199,6 @@ rpl::producer<QString> AwayMessage::title() {
 	return tr::lng_away_title();
 }
 
-rpl::producer<Type> AwayMessage::sectionShowOther() {
-	return _showOther.events();
-}
-
 void AwayMessage::setupContent(
 		not_null<Window::SessionController*> controller) {
 	using namespace Data;
@@ -268,7 +262,7 @@ void AwayMessage::setupContent(
 	create->setClickedCallback([=] {
 		const auto owner = &controller->session().data();
 		const auto id = owner->shortcutMessages().emplaceShortcut("away");
-		_showOther.fire(ShortcutMessagesId(id));
+		showOther(ShortcutMessagesId(id));
 	});
 	Ui::AddSkip(createInner);
 	Ui::AddDivider(createInner);
diff --git a/Telegram/SourceFiles/settings/business/settings_greeting.cpp b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
index 81580801c..96b2615c4 100644
--- a/Telegram/SourceFiles/settings/business/settings_greeting.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
@@ -41,7 +41,6 @@ public:
 	~Greeting();
 
 	[[nodiscard]] rpl::producer<QString> title() override;
-	[[nodiscard]] rpl::producer<Type> sectionShowOther() override;
 
 	const Ui::RoundRect *bottomSkipRounding() const {
 		return &_bottomSkipRounding;
@@ -53,7 +52,6 @@ private:
 
 	Ui::RoundRect _bottomSkipRounding;
 
-	rpl::event_stream<Type> _showOther;
 	rpl::variable<Data::BusinessRecipients> _recipients;
 	rpl::variable<int> _noActivityDays;
 	rpl::variable<bool> _enabled;
@@ -177,10 +175,6 @@ rpl::producer<QString> Greeting::title() {
 	return tr::lng_greeting_title();
 }
 
-rpl::producer<Type> Greeting::sectionShowOther() {
-	return _showOther.events();
-}
-
 void Greeting::setupContent(
 		not_null<Window::SessionController*> controller) {
 	using namespace rpl::mappers;
@@ -251,7 +245,7 @@ void Greeting::setupContent(
 	create->setClickedCallback([=] {
 		const auto owner = &controller->session().data();
 		const auto id = owner->shortcutMessages().emplaceShortcut("hello");
-		_showOther.fire(ShortcutMessagesId(id));
+		showOther(ShortcutMessagesId(id));
 	});
 	Ui::AddSkip(createInner);
 	Ui::AddDivider(createInner);
diff --git a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
index e13f822a2..431dcd4e1 100644
--- a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
@@ -37,14 +37,11 @@ public:
 	~QuickReplies();
 
 	[[nodiscard]] rpl::producer<QString> title() override;
-	[[nodiscard]] rpl::producer<Type> sectionShowOther() override;
 
 private:
 	void setupContent(not_null<Window::SessionController*> controller);
 	void save();
 
-	rpl::event_stream<Type> _showOther;
-
 };
 
 QuickReplies::QuickReplies(
@@ -64,10 +61,6 @@ rpl::producer<QString> QuickReplies::title() {
 	return tr::lng_replies_title();
 }
 
-rpl::producer<Type> QuickReplies::sectionShowOther() {
-	return _showOther.events();
-}
-
 void QuickReplies::setupContent(
 		not_null<Window::SessionController*> controller) {
 	using namespace rpl::mappers;
@@ -94,41 +87,13 @@ void QuickReplies::setupContent(
 	const auto messages = &owner->shortcutMessages();
 
 	add->setClickedCallback([=] {
-		controller->show(Box([=](not_null<Ui::GenericBox*> box) {
-			box->setTitle(tr::lng_replies_add_title());
-			box->addRow(object_ptr<Ui::FlatLabel>(
-				box,
-				tr::lng_replies_add_shortcut(),
-				st::settingsAddReplyLabel));
-			const auto field = box->addRow(object_ptr<Ui::InputField>(
-				box,
-				st::settingsAddReplyField,
-				tr::lng_replies_add_placeholder(),
-				QString()));
-			box->setFocusCallback([=] {
-				field->setFocusFast();
-			});
-
-			const auto submit = [=] {
-				const auto weak = Ui::MakeWeak(box);
-				const auto name = field->getLastText().trimmed();
-				if (name.isEmpty()) {
-					field->showError();
-				} else {
-					const auto id = messages->emplaceShortcut(name);
-					_showOther.fire(ShortcutMessagesId(id));
-				}
-				if (const auto strong = weak.data()) {
-					strong->closeBox();
-				}
-			};
-			field->submits(
-			) | rpl::start_with_next(submit, field->lifetime());
-			box->addButton(tr::lng_settings_save(), submit);
-			box->addButton(tr::lng_cancel(), [=] {
-				box->closeBox();
-			});
-		}));
+		const auto submit = [=](QString name, Fn<void()> close) {
+			const auto id = messages->emplaceShortcut(name);
+			showOther(ShortcutMessagesId(id));
+			close();
+		};
+		controller->show(
+			Box(EditShortcutNameBox, QString(), crl::guard(this, submit)));
 	});
 
 	Ui::AddSkip(content);
@@ -140,24 +105,33 @@ void QuickReplies::setupContent(
 	rpl::single(rpl::empty) | rpl::then(
 		messages->shortcutsChanged()
 	) | rpl::start_with_next([=] {
-		while (inner->count()) {
-			delete inner->widgetAt(0);
-		}
+		auto old = inner->count();
+
 		const auto &shortcuts = messages->shortcuts();
 		auto i = 0;
-		for (const auto &shortcut : shortcuts.list) {
-			const auto name = shortcut.second.name;
+		for (const auto &[_, shortcut] : shortcuts.list) {
+			if (!shortcut.count) {
+				continue;
+			}
+			const auto name = shortcut.name;
 			AddButtonWithLabel(
 				inner,
 				rpl::single('/' + name),
 				tr::lng_forum_messages(
 					lt_count,
-					rpl::single(1. * shortcut.second.count)),
+					rpl::single(1. * shortcut.count)),
 				st::settingsButtonNoIcon
 			)->setClickedCallback([=] {
 				const auto id = messages->emplaceShortcut(name);
-				_showOther.fire(ShortcutMessagesId(id));
+				showOther(ShortcutMessagesId(id));
 			});
+			if (old) {
+				delete inner->widgetAt(0);
+				--old;
+			}
+		}
+		while (old--) {
+			delete inner->widgetAt(0);
 		}
 	}, content->lifetime());
 
@@ -173,4 +147,48 @@ Type QuickRepliesId() {
 	return QuickReplies::Id();
 }
 
+void EditShortcutNameBox(
+		not_null<Ui::GenericBox*> box,
+		QString name,
+		Fn<void(QString, Fn<void()>)> submit) {
+	name = name.trimmed();
+	const auto editing = !name.isEmpty();
+	box->setTitle(editing
+		? tr::lng_replies_edit_title()
+		: tr::lng_replies_add_title());
+	box->addRow(object_ptr<Ui::FlatLabel>(
+		box,
+		(editing
+			? tr::lng_replies_edit_about()
+			: tr::lng_replies_add_shortcut()),
+		st::settingsAddReplyLabel));
+	const auto field = box->addRow(object_ptr<Ui::InputField>(
+		box,
+		st::settingsAddReplyField,
+		tr::lng_replies_add_placeholder(),
+		name));
+	box->setFocusCallback([=] {
+		field->setFocusFast();
+	});
+
+	const auto callback = [=] {
+		const auto name = field->getLastText().trimmed();
+		if (name.isEmpty()) {
+			field->showError();
+		} else {
+			submit(name, [weak = Ui::MakeWeak(box)] {
+				if (const auto strong = weak.data()) {
+					strong->closeBox();
+				}
+			});
+		}
+	};
+	field->submits(
+	) | rpl::start_with_next(callback, field->lifetime());
+	box->addButton(tr::lng_settings_save(), callback);
+	box->addButton(tr::lng_cancel(), [=] {
+		box->closeBox();
+	});
+}
+
 } // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_quick_replies.h b/Telegram/SourceFiles/settings/business/settings_quick_replies.h
index 80cc2f129..4765c4f59 100644
--- a/Telegram/SourceFiles/settings/business/settings_quick_replies.h
+++ b/Telegram/SourceFiles/settings/business/settings_quick_replies.h
@@ -9,8 +9,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "settings/settings_type.h"
 
+namespace Ui {
+class GenericBox;
+} // namespace Ui
+
 namespace Settings {
 
 [[nodiscard]] Type QuickRepliesId();
 
+void EditShortcutNameBox(
+	not_null<Ui::GenericBox*> box,
+	QString name,
+	Fn<void(QString, Fn<void()>)> submit);
+
 } // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
index e2186a172..0681b078a 100644
--- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
@@ -36,17 +36,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "lang/lang_numbers_animation.h"
 #include "main/main_session.h"
 #include "menu/menu_send.h"
+#include "settings/business/settings_quick_replies.h"
 #include "settings/business/settings_recipients_helper.h"
 #include "storage/localimageloader.h"
 #include "storage/storage_account.h"
 #include "storage/storage_media_prepare.h"
 #include "storage/storage_shared_media.h"
+#include "ui/boxes/confirm_box.h"
 #include "ui/chat/attach/attach_send_files_way.h"
 #include "ui/chat/chat_style.h"
 #include "ui/chat/chat_theme.h"
 #include "ui/controls/jump_down_button.h"
 #include "ui/text/format_values.h"
 #include "ui/text/text_utilities.h"
+#include "ui/widgets/menu/menu_add_action_callback.h"
 #include "ui/widgets/scroll_area.h"
 #include "window/themes/window_theme.h"
 #include "window/section_widget.h"
@@ -54,6 +57,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "styles/style_boxes.h"
 #include "styles/style_chat_helpers.h"
 #include "styles/style_chat.h"
+#include "styles/style_menu_icons.h"
+#include "styles/style_layers.h"
 
 namespace Settings {
 namespace {
@@ -84,6 +89,7 @@ public:
 
 	rpl::producer<Info::SelectedItems> selectedListValue() override;
 	void selectionAction(Info::SelectionAction action) override;
+	void fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) override;
 
 	bool paintOuter(
 		not_null<QWidget*> outer,
@@ -268,9 +274,9 @@ private:
 
 };
 
-struct Factory : AbstractSectionFactory {
+struct Factory final : AbstractSectionFactory {
 	explicit Factory(BusinessShortcutId shortcutId)
-		: shortcutId(shortcutId) {
+	: shortcutId(shortcutId) {
 	}
 
 	object_ptr<AbstractSection> create(
@@ -426,6 +432,56 @@ void ShortcutMessages::selectionAction(Info::SelectionAction action) {
 	Unexpected("Action in ShortcutMessages::selectionAction.");
 }
 
+void ShortcutMessages::fillTopBarMenu(
+		const Ui::Menu::MenuCallback &addAction) {
+	const auto owner = &_controller->session().data();
+	const auto messages = &owner->shortcutMessages();
+
+	addAction(tr::lng_context_edit_shortcut(tr::now), [=] {
+		const auto submit = [=](QString name, Fn<void()> close) {
+			const auto id = _shortcutId.current();
+			const auto error = [=](QString text) {
+				if (!text.isEmpty()) {
+					_controller->showToast((text == u"SHORTCUT_OCCUPIED"_q)
+						? tr::lng_replies_error_occupied(tr::now)
+						: text);
+				}
+			};
+			messages->editShortcut(id, name, close, crl::guard(this, error));
+		};
+		const auto name = _shortcut.current();
+		_controller->show(
+			Box(EditShortcutNameBox, name, crl::guard(this, submit)));
+	}, &st::menuIconEdit);
+
+	const auto justDelete = crl::guard(this, [=] {
+		messages->removeShortcut(_shortcutId.current());
+	});
+	const auto confirmDeleteShortcut = [=] {
+		const auto slice = messages->list(_shortcutId.current());
+		if (slice.fullCount == 0) {
+			justDelete();
+		} else {
+			const auto confirmed = [=](Fn<void()> close) {
+				justDelete();
+				close();
+			};
+			_controller->show(Ui::MakeConfirmBox({
+				.text = { tr::lng_replies_delete_sure() },
+				.confirmed = confirmed,
+				.confirmText = tr::lng_box_delete(),
+				.confirmStyle = &st::attentionBoxButton,
+			}));
+		}
+	};
+	addAction({
+		.text = tr::lng_context_delete_shortcut(tr::now),
+		.handler = crl::guard(this, confirmDeleteShortcut),
+		.icon = &st::menuIconDeleteAttention,
+		.isAttention = true,
+	});
+}
+
 bool ShortcutMessages::paintOuter(
 		not_null<QWidget*> outer,
 		int maxVisibleHeight,
diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.cpp b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.cpp
index 60596c5bf..19bb96969 100644
--- a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.cpp
+++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.cpp
@@ -7,10 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "settings/cloud_password/settings_cloud_password_email_confirm.h"
 
+#include "apiwrap.h"
 #include "api/api_cloud_password.h"
 #include "base/unixtime.h"
 #include "core/core_cloud_password.h"
 #include "lang/lang_keys.h"
+#include "main/main_session.h"
 #include "settings/cloud_password/settings_cloud_password_common.h"
 #include "settings/cloud_password/settings_cloud_password_email.h"
 #include "settings/cloud_password/settings_cloud_password_hint.h"
@@ -20,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/vertical_list.h"
 #include "ui/boxes/confirm_box.h"
 #include "ui/text/format_values.h"
+#include "ui/widgets/menu/menu_add_action_callback.h"
 #include "ui/widgets/buttons.h"
 #include "ui/widgets/sent_code_field.h"
 #include "ui/wrap/padding_wrap.h"
@@ -27,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "window/window_session_controller.h"
 #include "styles/style_boxes.h"
 #include "styles/style_layers.h"
+#include "styles/style_menu_icons.h"
 #include "styles/style_settings.h"
 
 /*
@@ -55,6 +59,10 @@ public:
 	using TypedAbstractStep::TypedAbstractStep;
 
 	[[nodiscard]] rpl::producer<QString> title() override;
+
+	[[nodiscard]] void fillTopBarMenu(
+		const Ui::Menu::MenuCallback &addAction) override;
+
 	void setupContent();
 
 protected:
@@ -69,6 +77,20 @@ rpl::producer<QString> EmailConfirm::title() {
 	return tr::lng_settings_cloud_password_email_title();
 }
 
+void EmailConfirm::fillTopBarMenu(
+		const Ui::Menu::MenuCallback &addAction) {
+	const auto api = &controller()->session().api();
+	if (const auto state = api->cloudPassword().stateCurrent()) {
+		if (state->unconfirmedPattern.isEmpty()) {
+			return;
+		}
+	}
+	addAction(
+		tr::lng_settings_password_abort(tr::now),
+		[=] { api->cloudPassword().clearUnconfirmedPassword(); },
+		&st::menuIconCancel);
+}
+
 rpl::producer<std::vector<Type>> EmailConfirm::removeTypes() {
 	return rpl::single(std::vector<Type>{
 		CloudPasswordStartId(),
diff --git a/Telegram/SourceFiles/settings/settings_advanced.cpp b/Telegram/SourceFiles/settings/settings_advanced.cpp
index d23b2a6d6..b833383f5 100644
--- a/Telegram/SourceFiles/settings/settings_advanced.cpp
+++ b/Telegram/SourceFiles/settings/settings_advanced.cpp
@@ -978,10 +978,6 @@ rpl::producer<QString> Advanced::title() {
 	return tr::lng_settings_advanced();
 }
 
-rpl::producer<Type> Advanced::sectionShowOther() {
-	return _showOther.events();
-}
-
 void Advanced::setupContent(not_null<Window::SessionController*> controller) {
 	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
 
@@ -1033,9 +1029,7 @@ void Advanced::setupContent(not_null<Window::SessionController*> controller) {
 	AddSkip(content);
 	AddDivider(content);
 	AddSkip(content);
-	SetupExport(controller, content, [=](Type type) {
-		_showOther.fire_copy(type);
-	});
+	SetupExport(controller, content, showOtherMethod());
 
 	Ui::ResizeFitChild(this, content);
 }
diff --git a/Telegram/SourceFiles/settings/settings_advanced.h b/Telegram/SourceFiles/settings/settings_advanced.h
index fce804253..1d46797b4 100644
--- a/Telegram/SourceFiles/settings/settings_advanced.h
+++ b/Telegram/SourceFiles/settings/settings_advanced.h
@@ -54,13 +54,9 @@ public:
 
 	[[nodiscard]] rpl::producer<QString> title() override;
 
-	rpl::producer<Type> sectionShowOther() override;
-
 private:
 	void setupContent(not_null<Window::SessionController*> controller);
 
-	rpl::event_stream<Type> _showOther;
-
 };
 
 } // namespace Settings
diff --git a/Telegram/SourceFiles/settings/settings_business.cpp b/Telegram/SourceFiles/settings/settings_business.cpp
index e3aa43129..0881925b5 100644
--- a/Telegram/SourceFiles/settings/settings_business.cpp
+++ b/Telegram/SourceFiles/settings/settings_business.cpp
@@ -296,7 +296,6 @@ public:
 	void setStepDataReference(std::any &data) override;
 
 	[[nodiscard]] rpl::producer<> sectionShowBack() override final;
-	[[nodiscard]] rpl::producer<Type> sectionShowOther() override;
 
 private:
 	void setupContent();
@@ -311,8 +310,6 @@ private:
 	Fn<void(bool)> _setPaused;
 	std::shared_ptr<Ui::RadiobuttonGroup> _radioGroup;
 
-	rpl::event_stream<Type> _showOther;
-
 	rpl::event_stream<> _showBack;
 	rpl::event_stream<> _showFinished;
 	rpl::variable<QString> _buttonText;
@@ -341,10 +338,6 @@ rpl::producer<> Business::sectionShowBack() {
 	return _showBack.events();
 }
 
-rpl::producer<Type> Business::sectionShowOther() {
-	return _showOther.events();
-}
-
 void Business::setStepDataReference(std::any &data) {
 	using namespace Info::Settings;
 	const auto my = std::any_cast<SectionCustomTopBarData>(&data);
@@ -365,7 +358,7 @@ void Business::setupContent() {
 	Ui::AddSkip(content, st::settingsFromFileTop);
 
 	AddBusinessSummary(content, _controller, [=](BusinessFeature feature) {
-		_showOther.fire([&] {
+		showOther([&] {
 			switch (feature) {
 			case BusinessFeature::AwayMessages: return AwayMessageId();
 			case BusinessFeature::OpeningHours: return WorkingHoursId();
diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp
index 3da8d85db..d4f1e8b7e 100644
--- a/Telegram/SourceFiles/settings/settings_chat.cpp
+++ b/Telegram/SourceFiles/settings/settings_chat.cpp
@@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/painter.h"
 #include "ui/vertical_list.h"
 #include "ui/ui_utility.h"
+#include "ui/widgets/menu/menu_add_action_callback.h"
 #include "history/view/history_view_quick_action.h"
 #include "lang/lang_keys.h"
 #include "export/export_manager.h"
@@ -1732,7 +1733,8 @@ void SetupSupport(
 }
 
 Chat::Chat(QWidget *parent, not_null<Window::SessionController*> controller)
-: Section(parent) {
+: Section(parent)
+, _controller(controller) {
 	setupContent(controller);
 }
 
@@ -1740,6 +1742,14 @@ rpl::producer<QString> Chat::title() {
 	return tr::lng_settings_section_chat_settings();
 }
 
+void Chat::fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) {
+	const auto window = &_controller->window();
+	addAction(
+		tr::lng_settings_bg_theme_create(tr::now),
+		[=] { window->show(Box(Window::Theme::CreateBox, window)); },
+		&st::menuIconChangeColors);
+}
+
 void Chat::setupContent(not_null<Window::SessionController*> controller) {
 	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
 
diff --git a/Telegram/SourceFiles/settings/settings_chat.h b/Telegram/SourceFiles/settings/settings_chat.h
index 91cd93101..d1256de96 100644
--- a/Telegram/SourceFiles/settings/settings_chat.h
+++ b/Telegram/SourceFiles/settings/settings_chat.h
@@ -44,9 +44,14 @@ public:
 
 	[[nodiscard]] rpl::producer<QString> title() override;
 
+	[[nodiscard]] void fillTopBarMenu(
+		const Ui::Menu::MenuCallback &addAction) override;
+
 private:
 	void setupContent(not_null<Window::SessionController*> controller);
 
+	const not_null<Window::SessionController*> _controller;
+
 };
 
 } // namespace Settings
diff --git a/Telegram/SourceFiles/settings/settings_common.h b/Telegram/SourceFiles/settings/settings_common.h
index e401ad419..47bf52774 100644
--- a/Telegram/SourceFiles/settings/settings_common.h
+++ b/Telegram/SourceFiles/settings/settings_common.h
@@ -102,6 +102,9 @@ public:
 	}
 	virtual void selectionAction(Info::SelectionAction action) {
 	}
+	[[nodiscard]] virtual void fillTopBarMenu(
+		const Ui::Menu::MenuCallback &addAction) {
+	}
 
 	virtual bool paintOuter(
 			not_null<QWidget*> outer,
diff --git a/Telegram/SourceFiles/settings/settings_common_session.cpp b/Telegram/SourceFiles/settings/settings_common_session.cpp
index 047c2c622..8fac1a399 100644
--- a/Telegram/SourceFiles/settings/settings_common_session.cpp
+++ b/Telegram/SourceFiles/settings/settings_common_session.cpp
@@ -34,48 +34,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 namespace Settings {
 
-void FillMenu(
-		not_null<Window::SessionController*> controller,
-		Type type,
-		Fn<void(Type)> showOther,
-		Ui::Menu::MenuCallback addAction) {
-	const auto window = &controller->window();
-	if (type == Chat::Id()) {
-		addAction(
-			tr::lng_settings_bg_theme_create(tr::now),
-			[=] { window->show(Box(Window::Theme::CreateBox, window)); },
-			&st::menuIconChangeColors);
-	} else if (type == CloudPasswordEmailConfirmId()) {
-		const auto api = &controller->session().api();
-		if (const auto state = api->cloudPassword().stateCurrent()) {
-			if (state->unconfirmedPattern.isEmpty()) {
-				return;
-			}
-		}
-		addAction(
-			tr::lng_settings_password_abort(tr::now),
-			[=] { api->cloudPassword().clearUnconfirmedPassword(); },
-			&st::menuIconCancel);
-	} else {
-		const auto &list = Core::App().domain().accounts();
-		if (list.size() < Core::App().domain().maxAccounts()) {
-			addAction(tr::lng_menu_add_account(tr::now), [=] {
-				Core::App().domain().addActivated(MTP::Environment{});
-			}, &st::menuIconAddAccount);
-		}
-		if (!controller->session().supportMode()) {
-			addAction(
-				tr::lng_settings_information(tr::now),
-				[=] { showOther(Information::Id()); },
-				&st::menuIconInfo);
-		}
-		addAction({
-			.text = tr::lng_settings_logout(tr::now),
-			.handler = [=] { window->showLogoutConfirmation(); },
-			.icon = &st::menuIconLeaveAttention,
-			.isAttention = true,
-		});
-	}
+bool HasMenu(Type type) {
+	return (type == ::Settings::CloudPasswordEmailConfirmId())
+		|| (type == Main::Id())
+		|| (type == Chat::Id());
 }
 
 } // namespace Settings
diff --git a/Telegram/SourceFiles/settings/settings_common_session.h b/Telegram/SourceFiles/settings/settings_common_session.h
index 16a03b9e4..2ebd1319c 100644
--- a/Telegram/SourceFiles/settings/settings_common_session.h
+++ b/Telegram/SourceFiles/settings/settings_common_session.h
@@ -54,6 +54,7 @@ struct SectionFactory : AbstractSectionFactory {
 		static const auto result = std::make_shared<SectionFactory>();
 		return result;
 	}
+
 };
 
 template <typename SectionType>
@@ -67,12 +68,24 @@ public:
 	[[nodiscard]] Type id() const final override {
 		return Id();
 	}
+
+	[[nodiscard]] rpl::producer<Type> sectionShowOther() final override {
+		return _showOtherRequests.events();
+	}
+	[[nodiscard]] void showOther(Type type) {
+		_showOtherRequests.fire_copy(type);
+	}
+	[[nodiscard]] Fn<void(Type)> showOtherMethod() {
+		return crl::guard(this, [=](Type type) {
+			showOther(type);
+		});
+	}
+
+private:
+	rpl::event_stream<Type> _showOtherRequests;
+
 };
 
-void FillMenu(
-	not_null<Window::SessionController*> controller,
-	Type type,
-	Fn<void(Type)> showOther,
-	Ui::Menu::MenuCallback addAction);
+bool HasMenu(Type type);
 
 } // namespace Settings
diff --git a/Telegram/SourceFiles/settings/settings_local_passcode.cpp b/Telegram/SourceFiles/settings/settings_local_passcode.cpp
index d06726cb4..9a766f16d 100644
--- a/Telegram/SourceFiles/settings/settings_local_passcode.cpp
+++ b/Telegram/SourceFiles/settings/settings_local_passcode.cpp
@@ -383,7 +383,6 @@ public:
 	[[nodiscard]] rpl::producer<QString> title() override;
 
 	void showFinished() override;
-	[[nodiscard]] rpl::producer<Type> sectionShowOther() override;
 	[[nodiscard]] rpl::producer<> sectionShowBack() override;
 
 	[[nodiscard]] rpl::producer<std::vector<Type>> removeFromStack() override;
@@ -399,7 +398,6 @@ private:
 	rpl::variable<bool> _isBottomFillerShown;
 
 	rpl::event_stream<> _showFinished;
-	rpl::event_stream<Type> _showOther;
 	rpl::event_stream<> _showBack;
 
 };
@@ -445,7 +443,7 @@ void LocalPasscodeManage::setupContent() {
 		st::settingsButton,
 		{ &st::menuIconLock }
 	)->addClickHandler([=] {
-		_showOther.fire(LocalPasscodeChange::Id());
+		showOther(LocalPasscodeChange::Id());
 	});
 
 	auto autolockLabel = state->autoLockBoxClosing.events_starting_with(
@@ -542,10 +540,6 @@ void LocalPasscodeManage::showFinished() {
 	_showFinished.fire({});
 }
 
-rpl::producer<Type> LocalPasscodeManage::sectionShowOther() {
-	return _showOther.events();
-}
-
 rpl::producer<> LocalPasscodeManage::sectionShowBack() {
 	return _showBack.events();
 }
diff --git a/Telegram/SourceFiles/settings/settings_main.cpp b/Telegram/SourceFiles/settings/settings_main.cpp
index 854c94b05..4dd57db8f 100644
--- a/Telegram/SourceFiles/settings/settings_main.cpp
+++ b/Telegram/SourceFiles/settings/settings_main.cpp
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "settings/settings_main.h"
 
+#include "core/application.h"
 #include "settings/settings_business.h"
 #include "settings/settings_codes.h"
 #include "settings/settings_chat.h"
@@ -28,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/wrap/vertical_layout.h"
 #include "ui/wrap/slide_wrap.h"
 #include "ui/wrap/padding_wrap.h"
+#include "ui/widgets/menu/menu_add_action_callback.h"
 #include "ui/widgets/labels.h"
 #include "ui/widgets/continuous_sliders.h"
 #include "ui/widgets/buttons.h"
@@ -49,6 +51,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "main/main_session.h"
 #include "main/main_session_settings.h"
 #include "main/main_account.h"
+#include "main/main_domain.h"
 #include "main/main_app_config.h"
 #include "apiwrap.h"
 #include "api/api_peer_photo.h"
@@ -691,6 +694,28 @@ rpl::producer<QString> Main::title() {
 	return tr::lng_menu_settings();
 }
 
+void Main::fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) {
+	const auto &list = Core::App().domain().accounts();
+	if (list.size() < Core::App().domain().maxAccounts()) {
+		addAction(tr::lng_menu_add_account(tr::now), [=] {
+			Core::App().domain().addActivated(MTP::Environment{});
+		}, &st::menuIconAddAccount);
+	}
+	if (!_controller->session().supportMode()) {
+		addAction(
+			tr::lng_settings_information(tr::now),
+			[=] { showOther(Information::Id()); },
+			&st::menuIconInfo);
+	}
+	const auto window = &_controller->window();
+	addAction({
+		.text = tr::lng_settings_logout(tr::now),
+		.handler = [=] { window->showLogoutConfirmation(); },
+		.icon = &st::menuIconLeaveAttention,
+		.isAttention = true,
+	});
+}
+
 void Main::keyPressEvent(QKeyEvent *e) {
 	crl::on_main(this, [=, text = e->text()]{
 		CodesFeedString(_controller, text);
@@ -706,18 +731,14 @@ void Main::setupContent(not_null<Window::SessionController*> controller) {
 		controller,
 		controller->session().user()));
 
-	SetupSections(controller, content, [=](Type type) {
-		_showOther.fire_copy(type);
-	});
+	SetupSections(controller, content, showOtherMethod());
 	if (HasInterfaceScale()) {
 		Ui::AddDivider(content);
 		Ui::AddSkip(content);
 		SetupInterfaceScale(&controller->window(), content);
 		Ui::AddSkip(content);
 	}
-	SetupPremium(controller, content, [=](Type type) {
-		_showOther.fire_copy(type);
-	});
+	SetupPremium(controller, content, showOtherMethod());
 	SetupHelp(controller, content);
 
 	Ui::ResizeFitChild(this, content);
@@ -730,8 +751,4 @@ void Main::setupContent(not_null<Window::SessionController*> controller) {
 	controller->session().data().cloudThemes().refresh();
 }
 
-rpl::producer<Type> Main::sectionShowOther() {
-	return _showOther.events();
-}
-
 } // namespace Settings
diff --git a/Telegram/SourceFiles/settings/settings_main.h b/Telegram/SourceFiles/settings/settings_main.h
index e3d26d02b..4356a49b3 100644
--- a/Telegram/SourceFiles/settings/settings_main.h
+++ b/Telegram/SourceFiles/settings/settings_main.h
@@ -38,7 +38,8 @@ public:
 
 	[[nodiscard]] rpl::producer<QString> title() override;
 
-	rpl::producer<Type> sectionShowOther() override;
+	[[nodiscard]] void fillTopBarMenu(
+		const Ui::Menu::MenuCallback &addAction) override;
 
 protected:
 	void keyPressEvent(QKeyEvent *e) override;
@@ -47,7 +48,6 @@ private:
 	void setupContent(not_null<Window::SessionController*> controller);
 
 	const not_null<Window::SessionController*> _controller;
-	rpl::event_stream<Type> _showOther;
 
 };
 
diff --git a/Telegram/SourceFiles/settings/settings_notifications.cpp b/Telegram/SourceFiles/settings/settings_notifications.cpp
index 4f54017c9..ddfb849f8 100644
--- a/Telegram/SourceFiles/settings/settings_notifications.cpp
+++ b/Telegram/SourceFiles/settings/settings_notifications.cpp
@@ -1281,17 +1281,11 @@ rpl::producer<QString> Notifications::title() {
 	return tr::lng_settings_section_notify();
 }
 
-rpl::producer<Type> Notifications::sectionShowOther() {
-	return _showOther.events();
-}
-
 void Notifications::setupContent(
 		not_null<Window::SessionController*> controller) {
 	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
 
-	SetupNotifications(controller, content, [=](Type type) {
-		_showOther.fire_copy(type);
-	});
+	SetupNotifications(controller, content, showOtherMethod());
 
 	Ui::ResizeFitChild(this, content);
 }
diff --git a/Telegram/SourceFiles/settings/settings_notifications.h b/Telegram/SourceFiles/settings/settings_notifications.h
index 1911a48af..f6df4d2e5 100644
--- a/Telegram/SourceFiles/settings/settings_notifications.h
+++ b/Telegram/SourceFiles/settings/settings_notifications.h
@@ -19,13 +19,9 @@ public:
 
 	[[nodiscard]] rpl::producer<QString> title() override;
 
-	rpl::producer<Type> sectionShowOther() override;
-
 private:
 	void setupContent(not_null<Window::SessionController*> controller);
 
-	rpl::event_stream<Type> _showOther;
-
 };
 
 } // namespace Settings
diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.cpp b/Telegram/SourceFiles/settings/settings_privacy_security.cpp
index ed4894275..6d16cac01 100644
--- a/Telegram/SourceFiles/settings/settings_privacy_security.cpp
+++ b/Telegram/SourceFiles/settings/settings_privacy_security.cpp
@@ -968,10 +968,6 @@ rpl::producer<QString> PrivacySecurity::title() {
 	return tr::lng_settings_section_privacy();
 }
 
-rpl::producer<Type> PrivacySecurity::sectionShowOther() {
-	return _showOther.events();
-}
-
 void PrivacySecurity::setupContent(
 		not_null<Window::SessionController*> controller) {
 	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
@@ -982,9 +978,7 @@ void PrivacySecurity::setupContent(
 		return rpl::duplicate(updateOnTick);
 	};
 
-	SetupSecurity(controller, content, trigger(), [=](Type type) {
-		_showOther.fire_copy(type);
-	});
+	SetupSecurity(controller, content, trigger(), showOtherMethod());
 	SetupPrivacy(controller, content, trigger());
 #if !defined OS_MAC_STORE && !defined OS_WIN_STORE
 	SetupSensitiveContent(controller, content, trigger());
diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.h b/Telegram/SourceFiles/settings/settings_privacy_security.h
index 4fedfce81..01922b01e 100644
--- a/Telegram/SourceFiles/settings/settings_privacy_security.h
+++ b/Telegram/SourceFiles/settings/settings_privacy_security.h
@@ -47,13 +47,9 @@ public:
 
 	[[nodiscard]] rpl::producer<QString> title() override;
 
-	rpl::producer<Type> sectionShowOther() override;
-
 private:
 	void setupContent(not_null<Window::SessionController*> controller);
 
-	rpl::event_stream<Type> _showOther;
-
 };
 
 } // namespace Settings

From 8545a14763040d183eb934b371dabf2101bbb88c Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Thu, 29 Feb 2024 14:41:17 +0400
Subject: [PATCH 059/108] Fix shortcut messages sizing / emoji panel.

---
 Telegram/SourceFiles/api/api_editing.cpp      |   5 +-
 .../SourceFiles/boxes/edit_caption_box.cpp    |   1 +
 .../chat_helpers/compose/compose_features.h   |   1 +
 .../data/business/data_shortcut_messages.cpp  |   3 +-
 Telegram/SourceFiles/data/data_drafts.h       |  13 +++
 Telegram/SourceFiles/dialogs/dialogs_key.h    |   1 +
 Telegram/SourceFiles/history/history_item.cpp |   2 +-
 .../history_view_compose_controls.cpp         |  45 +++++---
 .../controls/history_view_compose_controls.h  |   8 ++
 .../history/view/history_view_bottom_info.cpp |   2 +-
 .../view/history_view_context_menu.cpp        |   4 +-
 .../history/view/history_view_list_widget.cpp |  27 ++++-
 .../history/view/history_view_list_widget.h   |   9 ++
 .../SourceFiles/info/info_content_widget.cpp  |  12 ++-
 .../SourceFiles/info/info_layer_widget.cpp    | 100 ++++++++++++------
 Telegram/SourceFiles/info/info_layer_widget.h |   4 +-
 .../SourceFiles/info/info_section_widget.cpp  |   2 +-
 .../info/settings/info_settings_widget.cpp    |   8 +-
 .../business/settings_shortcut_messages.cpp   |  86 +++++++++++----
 .../SourceFiles/storage/localimageloader.cpp  |   1 +
 Telegram/SourceFiles/ui/chat/chat.style       |   1 +
 21 files changed, 253 insertions(+), 82 deletions(-)

diff --git a/Telegram/SourceFiles/api/api_editing.cpp b/Telegram/SourceFiles/api/api_editing.cpp
index a1e168adf..84f0cbfff 100644
--- a/Telegram/SourceFiles/api/api_editing.cpp
+++ b/Telegram/SourceFiles/api/api_editing.cpp
@@ -89,6 +89,9 @@ mtpRequestId EditMessage(
 		: emptyFlag)
 	| (options.scheduled
 		? MTPmessages_EditMessage::Flag::f_schedule_date
+		: emptyFlag)
+	| (item->isBusinessShortcut()
+		? MTPmessages_EditMessage::Flag::f_quick_reply_shortcut_id
 		: emptyFlag);
 
 	const auto id = item->isScheduled()
@@ -105,7 +108,7 @@ mtpRequestId EditMessage(
 		MTPReplyMarkup(),
 		sentEntities,
 		MTP_int(options.scheduled),
-		MTPint() // quick_reply_shortcut_id
+		MTP_int(item->shortcutId())
 	)).done([=](
 			const MTPUpdates &result,
 			[[maybe_unused]] mtpRequestId requestId) {
diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp
index 8e94beeb9..0c4b212c5 100644
--- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp
+++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp
@@ -895,6 +895,7 @@ void EditCaptionBox::save() {
 
 	auto options = Api::SendOptions();
 	options.scheduled = item->isScheduled() ? item->date() : 0;
+	options.shortcutId = item->shortcutId();
 
 	if (!_preparedList.files.empty()) {
 		if ((_albumType != Ui::AlbumType::None)
diff --git a/Telegram/SourceFiles/chat_helpers/compose/compose_features.h b/Telegram/SourceFiles/chat_helpers/compose/compose_features.h
index ba6f43b4e..5466b34e9 100644
--- a/Telegram/SourceFiles/chat_helpers/compose/compose_features.h
+++ b/Telegram/SourceFiles/chat_helpers/compose/compose_features.h
@@ -23,6 +23,7 @@ struct ComposeFeatures {
 	bool autocompleteHashtags = true;
 	bool autocompleteMentions = true;
 	bool autocompleteCommands = true;
+	bool commonTabbedPanel = true;
 };
 
 } // namespace ChatHelpers
diff --git a/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp b/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
index d9d75ec52..e7b4a65a7 100644
--- a/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
+++ b/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
@@ -60,7 +60,8 @@ constexpr auto kRequestTimeLimit = 60 * crl::time(1000);
 			MTP_int(data.vttl_period().value_or_empty()));
 	}, [&](const MTPDmessage &data) {
 		return MTP_message(
-			MTP_flags(data.vflags().v | MTPDmessage::Flag::f_quick_reply_shortcut_id),
+			MTP_flags(data.vflags().v
+				| MTPDmessage::Flag::f_quick_reply_shortcut_id),
 			data.vid(),
 			data.vfrom_id() ? *data.vfrom_id() : MTPPeer(),
 			MTPint(), // from_boosts_applied
diff --git a/Telegram/SourceFiles/data/data_drafts.h b/Telegram/SourceFiles/data/data_drafts.h
index 3f5e22a23..4fdd9159c 100644
--- a/Telegram/SourceFiles/data/data_drafts.h
+++ b/Telegram/SourceFiles/data/data_drafts.h
@@ -96,6 +96,18 @@ public:
 	[[nodiscard]] static constexpr DraftKey ScheduledEdit() {
 		return kScheduledDraftIndex + kEditDraftShift;
 	}
+	[[nodiscard]] static constexpr DraftKey Shortcut(
+			BusinessShortcutId shortcutId) {
+		return (shortcutId < 0 || shortcutId >= ServerMaxMsgId)
+			? None()
+			: (kShortcutDraftShift + shortcutId);
+	}
+	[[nodiscard]] static constexpr DraftKey ShortcutEdit(
+			BusinessShortcutId shortcutId) {
+		return (shortcutId < 0 || shortcutId >= ServerMaxMsgId)
+			? None()
+			: (kShortcutDraftShift + kEditDraftShift + shortcutId);
+	}
 
 	[[nodiscard]] static constexpr DraftKey FromSerialized(qint64 value) {
 		return value;
@@ -156,6 +168,7 @@ private:
 	static constexpr auto kScheduledDraftIndex = -3;
 	static constexpr auto kEditDraftShift = ServerMaxMsgId.bare;
 	static constexpr auto kCloudDraftShift = 2 * ServerMaxMsgId.bare;
+	static constexpr auto kShortcutDraftShift = 3 * ServerMaxMsgId.bare;
 	static constexpr auto kEditDraftShiftOld = 0x3FFF'FFFF;
 
 	int64 _value = 0;
diff --git a/Telegram/SourceFiles/dialogs/dialogs_key.h b/Telegram/SourceFiles/dialogs/dialogs_key.h
index 2396b216f..c43dc9f15 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_key.h
+++ b/Telegram/SourceFiles/dialogs/dialogs_key.h
@@ -108,6 +108,7 @@ struct EntryState {
 		Replies,
 		SavedSublist,
 		ContextMenu,
+		ShortcutMessages,
 	};
 
 	Key key;
diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp
index 807563fc0..b55baa6d2 100644
--- a/Telegram/SourceFiles/history/history_item.cpp
+++ b/Telegram/SourceFiles/history/history_item.cpp
@@ -2108,7 +2108,7 @@ bool HistoryItem::allowsEdit(TimeId now) const {
 }
 
 bool HistoryItem::canBeEdited() const {
-	if ((!isRegular() && !isScheduled())
+	if ((!isRegular() && !isScheduled() && !isBusinessShortcut())
 		|| Has<HistoryMessageVia>()
 		|| Has<HistoryMessageForwarded>()) {
 		return false;
diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp
index 69f0f2de8..9cfec19f5 100644
--- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp
+++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp
@@ -114,6 +114,9 @@ using ForwardPanel = Controls::ForwardPanel;
 
 } // namespace
 
+const ChatHelpers::PauseReason kDefaultPanelsLevel
+	= ChatHelpers::PauseReason::TabbedPanel;
+
 class FieldHeader final : public Ui::RpWidget {
 public:
 	FieldHeader(
@@ -760,7 +763,10 @@ MessageToEdit FieldHeader::queryToEdit() {
 	}
 	return {
 		.fullId = item->fullId(),
-		.options = { .scheduled = item->isScheduled() ? item->date() : 0 },
+		.options = {
+			.scheduled = item->isScheduled() ? item->date() : 0,
+			.shortcutId = item->shortcutId(),
+		},
 	};
 }
 
@@ -788,21 +794,24 @@ ComposeControls::ComposeControls(
 	: st::defaultComposeControls)
 , _features(descriptor.features)
 , _parent(parent)
+, _panelsParent(descriptor.panelsParent
+	? descriptor.panelsParent
+	: _parent.get())
 , _show(std::move(descriptor.show))
 , _session(&_show->session())
 , _regularWindow(descriptor.regularWindow)
-, _ownedSelector(_regularWindow
+, _ownedSelector((_regularWindow && _features.commonTabbedPanel)
 	? nullptr
 	: std::make_unique<ChatHelpers::TabbedSelector>(
-		_parent,
+		_panelsParent,
 		ChatHelpers::TabbedSelectorDescriptor{
 			.show = _show,
 			.st = _st.tabbed,
-			.level = Window::GifPauseReason::TabbedPanel,
+			.level = descriptor.panelsLevel,
 			.mode = ChatHelpers::TabbedSelector::Mode::Full,
 			.features = _features,
 		}))
-, _selector(_regularWindow
+, _selector((_regularWindow && _features.commonTabbedPanel)
 	? _regularWindow->tabbedSelector()
 	: not_null(_ownedSelector.get()))
 , _mode(descriptor.mode)
@@ -876,6 +885,12 @@ void ComposeControls::updateTopicRootId(MsgId topicRootId) {
 	_header->updateTopicRootId(_topicRootId);
 }
 
+void ComposeControls::updateShortcutId(BusinessShortcutId shortcutId) {
+	unregisterDraftSources();
+	_shortcutId = shortcutId;
+	registerDraftSource();
+}
+
 void ComposeControls::setHistory(SetHistoryArgs &&args) {
 	_showSlowmodeError = std::move(args.showSlowmodeError);
 	_sendActionFactory = std::move(args.sendActionFactory);
@@ -1592,7 +1607,7 @@ void ComposeControls::initField() {
 			&& Data::AllowEmojiWithoutPremium(_history->peer, emoji);
 	};
 	const auto suggestions = Ui::Emoji::SuggestionsController::Init(
-		_parent,
+		_panelsParent,
 		_field,
 		_session,
 		{
@@ -1828,6 +1843,10 @@ Data::DraftKey ComposeControls::draftKey(DraftType type) const {
 		return (type == DraftType::Edit)
 			? Key::ScheduledEdit()
 			: Key::Scheduled();
+	case Section::ShortcutMessages:
+		return (type == DraftType::Edit)
+			? Key::ShortcutEdit(_shortcutId)
+			: Key::Shortcut(_shortcutId);
 	}
 	return Key::None();
 }
@@ -2053,7 +2072,9 @@ rpl::producer<SendActionUpdate> ComposeControls::sendActionUpdates() const {
 }
 
 void ComposeControls::initTabbedSelector() {
-	if (!_regularWindow || _regularWindow->hasTabbedSelectorOwnership()) {
+	if (!_regularWindow
+		|| !_features.commonTabbedPanel
+		|| _regularWindow->hasTabbedSelectorOwnership()) {
 		createTabbedPanel();
 	} else {
 		setTabbedPanel(nullptr);
@@ -2686,7 +2707,7 @@ void ComposeControls::updateAttachBotsMenu() {
 		return;
 	}
 	_attachBotsMenu = InlineBots::MakeAttachBotsMenu(
-		_parent,
+		_panelsParent,
 		_regularWindow,
 		_history->peer,
 		_sendActionFactory,
@@ -2729,7 +2750,7 @@ void ComposeControls::escape() {
 bool ComposeControls::pushTabbedSelectorToThirdSection(
 		not_null<Data::Thread*> thread,
 		const Window::SectionShow &params) {
-	if (!_tabbedPanel || !_regularWindow) {
+	if (!_tabbedPanel || !_regularWindow || !_features.commonTabbedPanel) {
 		return true;
 	//} else if (!_canSendMessages) {
 	//	Core::App().settings().setTabbedReplacedWithInfo(true);
@@ -2764,7 +2785,7 @@ void ComposeControls::createTabbedPanel() {
 		.nonOwnedSelector = _ownedSelector ? nullptr : _selector.get(),
 	};
 	setTabbedPanel(std::make_unique<TabbedPanel>(
-		_parent,
+		_panelsParent,
 		std::move(descriptor)));
 	_tabbedPanel->setDesiredHeightValues(
 		st::emojiPanHeightRatio,
@@ -2787,7 +2808,7 @@ void ComposeControls::setTabbedPanel(
 }
 
 void ComposeControls::toggleTabbedSelectorMode() {
-	if (!_history || !_regularWindow) {
+	if (!_history || !_regularWindow || !_features.commonTabbedPanel) {
 		return;
 	}
 	if (_tabbedPanel) {
@@ -3248,7 +3269,7 @@ void ComposeControls::applyInlineBotQuery(
 		}
 		if (!_inlineResults) {
 			_inlineResults = std::make_unique<InlineBots::Layout::Widget>(
-				_parent,
+				_panelsParent,
 				_regularWindow);
 			_inlineResults->setResultSelectedCallback([=](
 					InlineBots::ResultSelected result) {
diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h
index d5985d6cb..d21ea9884 100644
--- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h
+++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h
@@ -37,6 +37,7 @@ class TabbedSelector;
 struct FileChosen;
 struct PhotoChosen;
 class Show;
+enum class PauseReason;
 } // namespace ChatHelpers
 
 namespace Data {
@@ -95,6 +96,8 @@ enum class ComposeControlsMode {
 	Scheduled,
 };
 
+extern const ChatHelpers::PauseReason kDefaultPanelsLevel;
+
 struct ComposeControlsDescriptor {
 	const style::ComposeControls *stOverride = nullptr;
 	std::shared_ptr<ChatHelpers::Show> show;
@@ -104,6 +107,8 @@ struct ComposeControlsDescriptor {
 	Window::SessionController *regularWindow = nullptr;
 	rpl::producer<ChatHelpers::FileChosen> stickerOrEmojiChosen;
 	rpl::producer<QString> customPlaceholder;
+	QWidget *panelsParent = nullptr;
+	ChatHelpers::PauseReason panelsLevel = kDefaultPanelsLevel;
 	QString voiceCustomCancelText;
 	bool voiceLockFromBottom = false;
 	ChatHelpers::ComposeFeatures features;
@@ -137,6 +142,7 @@ public:
 	[[nodiscard]] Main::Session &session() const;
 	void setHistory(SetHistoryArgs &&args);
 	void updateTopicRootId(MsgId topicRootId);
+	void updateShortcutId(BusinessShortcutId shortcutId);
 	void setCurrentDialogsEntryState(Dialogs::EntryState state);
 	[[nodiscard]] PeerData *sendAsPeer() const;
 
@@ -343,6 +349,7 @@ private:
 	const style::ComposeControls &_st;
 	const ChatHelpers::ComposeFeatures _features;
 	const not_null<QWidget*> _parent;
+	const not_null<QWidget*> _panelsParent;
 	const std::shared_ptr<ChatHelpers::Show> _show;
 	const not_null<Main::Session*> _session;
 
@@ -353,6 +360,7 @@ private:
 
 	History *_history = nullptr;
 	MsgId _topicRootId = 0;
+	BusinessShortcutId _shortcutId = 0;
 	Fn<bool()> _showSlowmodeError;
 	Fn<Api::SendAction()> _sendActionFactory;
 	rpl::variable<int> _slowmodeSecondsLeft;
diff --git a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp
index cfb31eabe..a13eb3fe5 100644
--- a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp
@@ -551,7 +551,7 @@ void BottomInfo::layoutReactionsText() {
 
 QSize BottomInfo::countOptimalSize() {
 	if (_data.flags & Data::Flag::Shortcut) {
-		return { st::historySendStateSpace / 2, st::msgDateFont->height };
+		return { st::historyShortcutStateSpace, st::msgDateFont->height };
 	}
 	auto width = 0;
 	if (_data.flags & (Data::Flag::OutLayout | Data::Flag::Sending)) {
diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp
index 9267aeb5c..09c3b89f9 100644
--- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp
@@ -102,7 +102,9 @@ bool HasEditMessageAction(
 		|| item->hasFailed()
 		|| item->isEditingMedia()
 		|| !request.selectedItems.empty()
-		|| (context != Context::History && context != Context::Replies)) {
+		|| (context != Context::History
+			&& context != Context::Replies
+			&& context != Context::ShortcutMessages)) {
 		return false;
 	}
 	const auto peer = item->history()->peer;
diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
index 31e6b4e9e..c37342fa2 100644
--- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
@@ -2166,6 +2166,21 @@ void ListWidget::paintEvent(QPaintEvent *e) {
 	context.translate(0, top);
 	p.translate(0, -top);
 
+	paintUserpics(p, context, clip);
+	paintDates(p, context, clip);
+
+	_reactionsManager->paint(p, context);
+	_emojiInteractions->paint(p);
+}
+
+void ListWidget::paintUserpics(
+		Painter &p,
+		const Ui::ChatPaintContext &context,
+		QRect clip) {
+	if (_context == Context::ShortcutMessages) {
+		return;
+	}
+	const auto session = &controller()->session();
 	enumerateUserpics([&](not_null<Element*> view, int userpicTop) {
 		// stop the enumeration if the userpic is below the painted rect
 		if (userpicTop >= clip.top() + clip.height()) {
@@ -2210,6 +2225,15 @@ void ListWidget::paintEvent(QPaintEvent *e) {
 		}
 		return true;
 	});
+}
+
+void ListWidget::paintDates(
+		Painter &p,
+		const Ui::ChatPaintContext &context,
+		QRect clip) {
+	if (_context == Context::ShortcutMessages) {
+		return;
+	}
 
 	auto dateHeight = st::msgServicePadding.bottom() + st::msgServiceFont->height + st::msgServicePadding.top();
 	auto scrollDateOpacity = _scrollDateOpacity.value(_scrollDateShown ? 1. : 0.);
@@ -2256,9 +2280,6 @@ void ListWidget::paintEvent(QPaintEvent *e) {
 		}
 		return true;
 	});
-
-	_reactionsManager->paint(p, context);
-	_emojiInteractions->paint(p);
 }
 
 void ListWidget::maybeMarkReactionsRead(not_null<HistoryItem*> item) {
diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h
index 2e2fdbae7..c15a041b8 100644
--- a/Telegram/SourceFiles/history/view/history_view_list_widget.h
+++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h
@@ -600,6 +600,15 @@ private:
 	void showPremiumStickerTooltip(
 		not_null<const HistoryView::Element*> view);
 
+	void paintUserpics(
+		Painter &p,
+		const Ui::ChatPaintContext &context,
+		QRect clip);
+	void paintDates(
+		Painter &p,
+		const Ui::ChatPaintContext &context,
+		QRect clip);
+
 	// This function finds all history items that are displayed and calls template method
 	// for each found message (in given direction) in the passed history with passed top offset.
 	//
diff --git a/Telegram/SourceFiles/info/info_content_widget.cpp b/Telegram/SourceFiles/info/info_content_widget.cpp
index adf4f22d2..440c125b2 100644
--- a/Telegram/SourceFiles/info/info_content_widget.cpp
+++ b/Telegram/SourceFiles/info/info_content_widget.cpp
@@ -167,7 +167,12 @@ Ui::RpWidget *ContentWidget::doSetInnerWidget(
 		const auto bottom = top + height;
 		_innerDesiredHeight = desired;
 		_innerWrap->setVisibleTopBottom(top, bottom);
+		LOG(("TOP: %1, HEIGHT: %2, DESIRED: %3, TILL: %4").arg(top).arg(height).arg(desired).arg(std::max(desired - bottom, 0)));
 		_scrollTillBottomChanges.fire_copy(std::max(desired - bottom, 0));
+		//const auto bottom = _scroll->scrollTop() + _scroll->height();
+		//_innerDesiredHeight = desired;
+		//_innerWrap->setVisibleTopBottom(_scroll->scrollTop(), bottom);
+		//_scrollTillBottomChanges.fire_copy(std::max(desired - bottom, 0));
 	}, _innerWrap->lifetime());
 
 	return _innerWrap->entity();
@@ -217,7 +222,12 @@ rpl::producer<int> ContentWidget::desiredHeightValue() const {
 		_innerWrap->entity()->desiredHeightValue(),
 		_scrollTopSkip.value(),
 		_scrollBottomSkip.value()
-	) | rpl::map(_1 + _2 + _3);
+	//) | rpl::map(_1 + _2 + _3);
+	) | rpl::map([=](int desired, int, int) {
+		return desired
+			+ _scrollTopSkip.current()
+			+ _scrollBottomSkip.current();
+	});
 }
 
 rpl::producer<bool> ContentWidget::desiredShadowVisibility() const {
diff --git a/Telegram/SourceFiles/info/info_layer_widget.cpp b/Telegram/SourceFiles/info/info_layer_widget.cpp
index b4fdd2414..50eca4456 100644
--- a/Telegram/SourceFiles/info/info_layer_widget.cpp
+++ b/Telegram/SourceFiles/info/info_layer_widget.cpp
@@ -30,7 +30,7 @@ LayerWidget::LayerWidget(
 	not_null<Window::SessionController*> controller,
 	not_null<Memento*> memento)
 : _controller(controller)
-, _content(this, controller, Wrap::Layer, memento) {
+, _contentWrap(this, controller, Wrap::Layer, memento) {
 	setupHeightConsumers();
 	controller->window().replaceFloatPlayerDelegate(floatPlayerDelegate());
 }
@@ -39,7 +39,7 @@ LayerWidget::LayerWidget(
 	not_null<Window::SessionController*> controller,
 	not_null<MoveMemento*> memento)
 : _controller(controller)
-, _content(memento->takeContent(this, Wrap::Layer)) {
+, _contentWrap(memento->takeContent(this, Wrap::Layer)) {
 	setupHeightConsumers();
 	controller->window().replaceFloatPlayerDelegate(floatPlayerDelegate());
 }
@@ -64,17 +64,17 @@ void LayerWidget::floatPlayerToggleGifsPaused(bool paused) {
 
 auto LayerWidget::floatPlayerGetSection(Window::Column column)
 -> not_null<::Media::Player::FloatSectionDelegate*> {
-	Expects(_content != nullptr);
+	Expects(_contentWrap != nullptr);
 
-	return _content;
+	return _contentWrap;
 }
 
 void LayerWidget::floatPlayerEnumerateSections(Fn<void(
 		not_null<::Media::Player::FloatSectionDelegate*> widget,
 		Window::Column widgetColumn)> callback) {
-	Expects(_content != nullptr);
+	Expects(_contentWrap != nullptr);
 
-	callback(_content, Window::Column::Second);
+	callback(_contentWrap, Window::Column::Second);
 }
 
 bool LayerWidget::floatPlayerIsVisible(not_null<HistoryItem*> item) {
@@ -87,9 +87,9 @@ void LayerWidget::floatPlayerDoubleClickEvent(
 }
 
 void LayerWidget::setupHeightConsumers() {
-	Expects(_content != nullptr);
+	Expects(_contentWrap != nullptr);
 
-	_content->scrollTillBottomChanges(
+	_contentWrap->scrollTillBottomChanges(
 	) | rpl::filter([this] {
 		if (!_inResize) {
 			return true;
@@ -100,10 +100,10 @@ void LayerWidget::setupHeightConsumers() {
 		resizeToWidth(width());
 	}, lifetime());
 
-	_content->grabbingForExpanding(
+	_contentWrap->grabbingForExpanding(
 	) | rpl::start_with_next([=](bool grabbing) {
 		if (grabbing) {
-			_savedHeight = _contentHeight;
+			_savedHeight = _contentWrapHeight;
 			_savedHeightAnimation = base::take(_heightAnimation);
 			setContentHeight(_desiredHeight);
 		} else {
@@ -112,7 +112,7 @@ void LayerWidget::setupHeightConsumers() {
 		}
 	}, lifetime());
 
-	_content->desiredHeightValue(
+	_contentWrap->desiredHeightValue(
 	) | rpl::start_with_next([this](int height) {
 		if (!height) {
 			// New content arrived.
@@ -128,32 +128,32 @@ void LayerWidget::setupHeightConsumers() {
 			_heightAnimated = true;
 			_heightAnimation.start([=] {
 				setContentHeight(_heightAnimation.value(_desiredHeight));
-			}, _contentHeight, _desiredHeight, st::slideDuration);
+			}, _contentWrapHeight, _desiredHeight, st::slideDuration);
 			resizeToWidth(width());
 		}
 	}, lifetime());
 }
 
 void LayerWidget::setContentHeight(int height) {
-	if (_contentHeight == height) {
+	if (_contentWrapHeight == height) {
 		return;
 	}
-
-	_contentHeight = height;
+	LOG(("CONTENT WRAP HEIGHT: %1 -> %2").arg(_contentWrapHeight).arg(height));
+	_contentWrapHeight = height;
 	if (_inResize) {
 		_pendingResize = true;
-	} else if (_content) {
+	} else if (_contentWrap) {
 		resizeToWidth(width());
 	}
 }
 
 void LayerWidget::showFinished() {
 	floatPlayerShowVisible();
-	_content->showFast();
+	_contentWrap->showFast();
 }
 
 void LayerWidget::parentResized() {
-	if (!_content) {
+	if (!_contentWrap) {
 		return;
 	}
 
@@ -163,7 +163,7 @@ void LayerWidget::parentResized() {
 		Ui::FocusPersister persister(this);
 		restoreFloatPlayerDelegate();
 
-		auto memento = std::make_shared<MoveMemento>(std::move(_content));
+		auto memento = std::make_shared<MoveMemento>(std::move(_contentWrap));
 
 		// We want to call hideSpecialLayer synchronously to avoid glitches,
 		// but we can't destroy LayerStackWidget from its' resizeEvent,
@@ -209,7 +209,7 @@ bool LayerWidget::takeToThirdSection() {
 	//
 	//Ui::FocusPersister persister(this);
 	//auto localCopy = _controller;
-	//auto memento = MoveMemento(std::move(_content));
+	//auto memento = MoveMemento(std::move(_contentWrap));
 	//localCopy->hideSpecialLayer(anim::type::instant);
 
 	//// When creating third section in response to the window
@@ -235,7 +235,7 @@ bool LayerWidget::takeToThirdSection() {
 bool LayerWidget::showSectionInternal(
 		not_null<Window::SectionMemento*> memento,
 		const Window::SectionShow &params) {
-	if (_content && _content->showInternal(memento, params)) {
+	if (_contentWrap && _contentWrap->showInternal(memento, params)) {
 		if (params.activation != anim::activation::background) {
 			_controller->parentController()->hideLayer();
 		}
@@ -245,7 +245,7 @@ bool LayerWidget::showSectionInternal(
 }
 
 bool LayerWidget::closeByOutsideClick() const {
-	return _content ? _content->closeByOutsideClick() : true;
+	return _contentWrap ? _contentWrap->closeByOutsideClick() : true;
 }
 
 int LayerWidget::MinimalSupportedWidth() {
@@ -254,19 +254,48 @@ int LayerWidget::MinimalSupportedWidth() {
 }
 
 int LayerWidget::resizeGetHeight(int newWidth) {
-	if (!parentWidget() || !_content || !newWidth) {
+	if (!parentWidget() || !_contentWrap || !newWidth) {
 		return 0;
 	}
 	constexpr auto kMaxAttempts = 16;
 	auto attempts = 0;
 	while (true) {
 		_inResize = true;
+		{
+			const auto &parentSize = parentWidget()->size();
+			const auto windowWidth = parentSize.width();
+			const auto windowHeight = parentSize.height();
+			const auto newLeft = (windowWidth - newWidth) / 2;
+			const auto newTop = std::clamp(
+				windowHeight / 24,
+				st::infoLayerTopMinimal,
+				st::infoLayerTopMaximal);
+			const auto newBottom = newTop;
+
+			const auto bottomRadius = st::boxRadius;
+			const auto maxVisibleHeight = windowHeight - newTop;
+			// Top rounding is included in _contentWrapHeight.
+			auto desiredHeight = _contentWrapHeight + bottomRadius;
+			accumulate_min(desiredHeight, maxVisibleHeight - newBottom);
+
+			// First resize content to new width and get the new desired height.
+			const auto contentLeft = 0;
+			const auto contentTop = 0;
+			const auto contentBottom = bottomRadius;
+			const auto contentWidth = newWidth;
+			auto contentHeight = desiredHeight - contentTop - contentBottom;
+			LOG(("ATTEMPT %1: WIDTH %2, WRAP HEIGHT %3, SCROLL TILL BOTTOM: %4"
+				).arg(attempts + 1
+				).arg(newWidth
+				).arg(_contentWrapHeight
+				).arg(_contentWrap->scrollTillBottom(contentHeight)));
+		}
 		const auto newGeometry = countGeometry(newWidth);
 		_inResize = false;
 		if (!_pendingResize) {
 			const auto oldGeometry = geometry();
 			if (newGeometry != oldGeometry) {
-				_content->forceContentRepaint();
+				_contentWrap->forceContentRepaint();
 			}
 			if (newGeometry.topLeft() != oldGeometry.topLeft()) {
 				move(newGeometry.topLeft());
@@ -292,8 +321,8 @@ QRect LayerWidget::countGeometry(int newWidth) {
 
 	const auto bottomRadius = st::boxRadius;
 	const auto maxVisibleHeight = windowHeight - newTop;
-	// Top rounding is included in _contentHeight.
-	auto desiredHeight = _contentHeight + bottomRadius;
+	// Top rounding is included in _contentWrapHeight.
+	auto desiredHeight = _contentWrapHeight + bottomRadius;
 	accumulate_min(desiredHeight, maxVisibleHeight - newBottom);
 
 	// First resize content to new width and get the new desired height.
@@ -302,10 +331,11 @@ QRect LayerWidget::countGeometry(int newWidth) {
 	const auto contentBottom = bottomRadius;
 	const auto contentWidth = newWidth;
 	auto contentHeight = desiredHeight - contentTop - contentBottom;
-	const auto scrollTillBottom = _content->scrollTillBottom(contentHeight);
+	const auto scrollTillBottom = _contentWrap->scrollTillBottom(
+		contentHeight);
 	auto additionalScroll = std::min(scrollTillBottom, newBottom);
 
-	const auto expanding = (_desiredHeight > _contentHeight);
+	const auto expanding = (_desiredHeight > _contentWrapHeight);
 
 	desiredHeight += additionalScroll;
 	contentHeight += additionalScroll;
@@ -313,11 +343,11 @@ QRect LayerWidget::countGeometry(int newWidth) {
 	if (_tillBottom) {
 		additionalScroll += contentBottom;
 	}
-	_contentTillBottom = _tillBottom && !_content->scrollBottomSkip();
+	_contentTillBottom = _tillBottom && !_contentWrap->scrollBottomSkip();
 	if (_contentTillBottom) {
 		contentHeight += contentBottom;
 	}
-	_content->updateGeometry({
+	_contentWrap->updateGeometry({
 		contentLeft,
 		contentTop,
 		contentWidth,
@@ -328,8 +358,8 @@ QRect LayerWidget::countGeometry(int newWidth) {
 }
 
 void LayerWidget::doSetInnerFocus() {
-	if (_content) {
-		_content->setInnerFocus();
+	if (_contentWrap) {
+		_contentWrap->setInnerFocus();
 	}
 }
 
@@ -342,7 +372,7 @@ void LayerWidget::paintEvent(QPaintEvent *e) {
 	if (!_tillBottom) {
 		const auto bottom = QRect{ 0, height() - radius, width(), radius };
 		if (clip.intersects(bottom)) {
-			if (const auto rounding = _content->bottomSkipRounding()) {
+			if (const auto rounding = _contentWrap->bottomSkipRounding()) {
 				rounding->paint(p, rect(), RectPart::FullBottom);
 			} else {
 				Ui::FillRoundRect(p, bottom, st::boxBg, {
@@ -351,11 +381,11 @@ void LayerWidget::paintEvent(QPaintEvent *e) {
 			}
 		}
 	} else if (!_contentTillBottom) {
-		const auto rounding = _content->bottomSkipRounding();
+		const auto rounding = _contentWrap->bottomSkipRounding();
 		const auto &color = rounding ? rounding->color() : st::boxBg;
 		p.fillRect(0, height() - radius, width(), radius, color);
 	}
-	if (_content->animatingShow()) {
+	if (_contentWrap->animatingShow()) {
 		const auto top = QRect{ 0, 0, width(), radius };
 		if (clip.intersects(top)) {
 			Ui::FillRoundRect(p, top, st::boxBg, {
diff --git a/Telegram/SourceFiles/info/info_layer_widget.h b/Telegram/SourceFiles/info/info_layer_widget.h
index c223b9d82..df8bc2726 100644
--- a/Telegram/SourceFiles/info/info_layer_widget.h
+++ b/Telegram/SourceFiles/info/info_layer_widget.h
@@ -73,10 +73,10 @@ private:
 	[[nodiscard]] QRect countGeometry(int newWidth);
 
 	not_null<Window::SessionController*> _controller;
-	object_ptr<WrapWidget> _content;
+	object_ptr<WrapWidget> _contentWrap;
 
 	int _desiredHeight = 0;
-	int _contentHeight = 0;
+	int _contentWrapHeight = 0;
 	int _savedHeight = 0;
 	Ui::Animations::Simple _heightAnimation;
 	Ui::Animations::Simple _savedHeightAnimation;
diff --git a/Telegram/SourceFiles/info/info_section_widget.cpp b/Telegram/SourceFiles/info/info_section_widget.cpp
index a70131428..0a7b7da67 100644
--- a/Telegram/SourceFiles/info/info_section_widget.cpp
+++ b/Telegram/SourceFiles/info/info_section_widget.cpp
@@ -50,8 +50,8 @@ void SectionWidget::init() {
 		return (_content != nullptr);
 	}) | rpl::start_with_next([=](QSize size, int) {
 		const auto expanding = false;
-		const auto additionalScroll = st::boxRadius;
 		const auto full = !_content->scrollBottomSkip();
+		const auto additionalScroll = (full ? st::boxRadius : 0);
 		const auto height = size.height() - (full ? 0 : st::boxRadius);
 		const auto wrapGeometry = QRect{ 0, 0, size.width(), height };
 		_content->updateGeometry(
diff --git a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp
index 3524be661..8a53dc553 100644
--- a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp
+++ b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp
@@ -117,17 +117,17 @@ Widget::Widget(
 	}
 
 	if (_pinnedToBottom) {
-		const auto processHeight = [=](int bottomHeight, int height) {
-			setScrollBottomSkip(bottomHeight);
+		const auto processHeight = [=] {
+			setScrollBottomSkip(_pinnedToBottom->height());
 			_pinnedToBottom->moveToLeft(
 				_pinnedToBottom->x(),
-				height - bottomHeight);
+				height() - _pinnedToBottom->height());
 		};
 
 		_inner->sizeValue(
 		) | rpl::start_with_next([=](const QSize &s) {
 			_pinnedToBottom->resizeToWidth(s.width());
-			processHeight(_pinnedToBottom->height(), height());
+			//processHeight();
 		}, _pinnedToBottom->lifetime());
 
 		rpl::combine(
diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
index 0681b078a..8df33ebf0 100644
--- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
@@ -98,6 +98,7 @@ public:
 
 private:
 	void outerResized(QSize outer);
+	void updateComposeControlsPosition();
 
 	// ListDelegate interface.
 	Context listContext() override;
@@ -505,7 +506,7 @@ void ShortcutMessages::outerResized(QSize outer) {
 		? base::make_optional(_scroll->scrollTop())
 		: 0;
 	_skipScrollEvent = true;
-	_inner->resizeToWidth(contentWidth, _scroll->height());
+	_inner->resizeToWidth(contentWidth, st::boxWidth);
 	_skipScrollEvent = false;
 
 	if (!_scroll->isHidden()) {
@@ -514,25 +515,34 @@ void ShortcutMessages::outerResized(QSize outer) {
 		}
 		updateInnerVisibleArea();
 	}
-	_composeControls->setAutocompleteBoundingRect(_scroll->geometry());
+	updateComposeControlsPosition();
 	_cornerButtons.updatePositions();
 }
 
+void ShortcutMessages::updateComposeControlsPosition() {
+	const auto bottom = _scroll->parentWidget()->height();
+	const auto controlsHeight = _composeControls->heightCurrent();
+	_composeControls->move(0, bottom - controlsHeight + st::boxRadius);
+	_composeControls->setAutocompleteBoundingRect(_scroll->geometry());
+}
+
 void ShortcutMessages::setupComposeControls() {
+	_shortcutId.value() | rpl::start_with_next([=](BusinessShortcutId id) {
+		_composeControls->updateShortcutId(id);
+	}, lifetime());
+
+	const auto state = Dialogs::EntryState{
+		.key = Dialogs::Key{ _history },
+		.section = Dialogs::EntryState::Section::ShortcutMessages,
+		.currentReplyTo = replyTo(),
+	};
+	_composeControls->setCurrentDialogsEntryState(state);
+
 	_composeControls->setHistory({
 		.history = _history.get(),
 		.writeRestriction = rpl::single(Controls::WriteRestriction()),
 	});
 
-	_composeControls->height(
-	) | rpl::start_with_next([=](int height) {
-		const auto wasMax = (_scroll->scrollTopMax() == _scroll->scrollTop());
-		_controlsWrap->resize(width(), height);
-		if (wasMax) {
-			listScrollTo(_scroll->scrollTopMax());
-		}
-	}, lifetime());
-
 	_composeControls->cancelRequests(
 	) | rpl::start_with_next([=] {
 		listCancelRequest();
@@ -637,20 +647,58 @@ void ShortcutMessages::setupComposeControls() {
 	_controlsWrap->widthValue() | rpl::start_with_next([=](int width) {
 		_composeControls->resizeToWidth(width);
 	}, _controlsWrap->lifetime());
-	_composeControls->height() | rpl::start_with_next([=](int height) {
-		_controlsWrap->resize(_controlsWrap->width(), height);
-	}, _controlsWrap->lifetime());
+
+	_composeControls->height(
+	) | rpl::start_with_next([=](int height) {
+		const auto wasMax = (_scroll->scrollTopMax() == _scroll->scrollTop());
+		_controlsWrap->resize(width(), height - st::boxRadius);
+		updateComposeControlsPosition();
+		if (wasMax) {
+			listScrollTo(_scroll->scrollTopMax());
+		}
+	}, lifetime());
 }
 
 QPointer<Ui::RpWidget> ShortcutMessages::createPinnedToBottom(
 		not_null<Ui::RpWidget*> parent) {
+	auto placeholder = rpl::deferred([=] {
+		return _shortcutId.value();
+	}) | rpl::map([=](BusinessShortcutId id) {
+		return _session->data().shortcutMessages().lookupShortcut(id).name;
+	}) | rpl::map([=](const QString &shortcut) {
+		return (shortcut == u"away"_q)
+			? tr::lng_away_message_placeholder()
+			: (shortcut == u"hello"_q)
+			? tr::lng_greeting_message_placeholder()
+			: tr::lng_replies_message_placeholder();
+	}) | rpl::flatten_latest();
+
 	_controlsWrap = std::make_unique<Ui::RpWidget>(parent);
 	_composeControls = std::make_unique<ComposeControls>(
-		_controlsWrap.get(),
-		_controller,
-		[=](not_null<DocumentData*> emoji) { listShowPremiumToast(emoji); },
-		ComposeControls::Mode::Normal,
-		SendMenu::Type::Disabled);
+		dynamic_cast<Ui::RpWidget*>(_scroll->parentWidget()),
+		ComposeControlsDescriptor{
+			.show = _controller->uiShow(),
+			.unavailableEmojiPasted = [=](not_null<DocumentData*> emoji) {
+				listShowPremiumToast(emoji);
+			},
+			.mode = HistoryView::ComposeControlsMode::Normal,
+			.sendMenuType = SendMenu::Type::Disabled,
+			.regularWindow = _controller,
+			.stickerOrEmojiChosen = _controller->stickerOrEmojiChosen(),
+			.customPlaceholder = std::move(placeholder),
+			.panelsLevel = Window::GifPauseReason::Layer,
+			.voiceCustomCancelText = tr::lng_record_cancel_stories(tr::now),
+			.voiceLockFromBottom = true,
+			.features = {
+				.sendAs = false,
+				.ttlInfo = false,
+				.botCommandSend = false,
+				.silentBroadcastToggle = false,
+				.attachBotsMenu = false,
+				.megagroupSet = false,
+				.commonTabbedPanel = false,
+			},
+		});
 
 	setupComposeControls();
 
diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp
index 532a1d35e..c26b68458 100644
--- a/Telegram/SourceFiles/storage/localimageloader.cpp
+++ b/Telegram/SourceFiles/storage/localimageloader.cpp
@@ -517,6 +517,7 @@ FileLoadTask::FileLoadTask(
 , _caption(caption)
 , _spoiler(spoiler) {
 	Expects(to.options.scheduled
+		|| to.options.shortcutId
 		|| !to.replaceMediaOf
 		|| IsServerMsgId(to.replaceMediaOf));
 
diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style
index 2a2733f2a..4ec9954a5 100644
--- a/Telegram/SourceFiles/ui/chat/chat.style
+++ b/Telegram/SourceFiles/ui/chat/chat.style
@@ -301,6 +301,7 @@ historySentInvertedIcon: icon {{ "history_sent", historyIconFgInverted, point(2p
 historyReceivedIcon: icon {{ "history_received", historyOutIconFg, point(2px, 4px) }};
 historyReceivedSelectedIcon: icon {{ "history_received", historyOutIconFgSelected, point(2px, 4px) }};
 historyReceivedInvertedIcon: icon {{ "history_received", historyIconFgInverted, point(2px, 4px) }};
+historyShortcutStateSpace: 18px;
 
 historyViewsSpace: 8px;
 historyViewsWidth: 20px;

From d5e920e45ae1782e39e9ff4b9c15ba1c0acbfad9 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 1 Mar 2024 09:06:48 +0400
Subject: [PATCH 060/108] Update API scheme on layer 176.

---
 .../data/business/data_business_common.h      |  1 +
 .../data/business/data_business_info.cpp      |  2 ++
 .../SourceFiles/data/data_chat_filters.cpp    |  6 ++++--
 Telegram/SourceFiles/data/data_user.cpp       |  1 +
 Telegram/SourceFiles/intro/intro_step.cpp     |  4 ++--
 Telegram/SourceFiles/mtproto/scheme/api.tl    | 19 ++++++++++++++-----
 6 files changed, 24 insertions(+), 9 deletions(-)

diff --git a/Telegram/SourceFiles/data/business/data_business_common.h b/Telegram/SourceFiles/data/business/data_business_common.h
index 6f3b716dc..a47dce3a2 100644
--- a/Telegram/SourceFiles/data/business/data_business_common.h
+++ b/Telegram/SourceFiles/data/business/data_business_common.h
@@ -192,6 +192,7 @@ struct AwaySettings {
 	BusinessRecipients recipients;
 	AwaySchedule schedule;
 	BusinessShortcutId shortcutId = 0;
+	bool offlineOnly = false;
 
 	explicit operator bool() const {
 		return schedule.type != AwayScheduleType::Never;
diff --git a/Telegram/SourceFiles/data/business/data_business_info.cpp b/Telegram/SourceFiles/data/business/data_business_info.cpp
index 5fa0844ee..bd75f4af7 100644
--- a/Telegram/SourceFiles/data/business/data_business_info.cpp
+++ b/Telegram/SourceFiles/data/business/data_business_info.cpp
@@ -94,7 +94,9 @@ template <typename Flag>
 }
 
 [[nodiscard]] MTPInputBusinessAwayMessage ToMTP(const AwaySettings &data) {
+	using Flag = MTPDinputBusinessAwayMessage::Flag;
 	return MTP_inputBusinessAwayMessage(
+		MTP_flags(data.offlineOnly ? Flag::f_offline_only : Flag()),
 		MTP_int(data.shortcutId),
 		ToMTP(data.schedule),
 		ToMTP(data.recipients));
diff --git a/Telegram/SourceFiles/data/data_chat_filters.cpp b/Telegram/SourceFiles/data/data_chat_filters.cpp
index 237bf5c29..47cd8f4d0 100644
--- a/Telegram/SourceFiles/data/data_chat_filters.cpp
+++ b/Telegram/SourceFiles/data/data_chat_filters.cpp
@@ -196,6 +196,7 @@ MTPDialogFilter ChatFilter::tl(FilterId replaceId) const {
 			MTP_int(replaceId ? replaceId : _id),
 			MTP_string(_title),
 			MTP_string(_iconEmoji),
+			MTPint(), // color
 			MTP_vector<MTPInputPeer>(pinned),
 			MTP_vector<MTPInputPeer>(include));
 	}
@@ -221,6 +222,7 @@ MTPDialogFilter ChatFilter::tl(FilterId replaceId) const {
 		MTP_int(replaceId ? replaceId : _id),
 		MTP_string(_title),
 		MTP_string(_iconEmoji),
+		MTPint(), // color
 		MTP_vector<MTPInputPeer>(pinned),
 		MTP_vector<MTPInputPeer>(include),
 		MTP_vector<MTPInputPeer>(never));
@@ -361,8 +363,8 @@ void ChatFilters::load(bool force) {
 	auto &api = _owner->session().api();
 	api.request(_loadRequestId).cancel();
 	_loadRequestId = api.request(MTPmessages_GetDialogFilters(
-	)).done([=](const MTPVector<MTPDialogFilter> &result) {
-		received(result.v);
+	)).done([=](const MTPmessages_DialogFilters &result) {
+		received(result.data().vfilters().v);
 		_loadRequestId = 0;
 	}).fail([=] {
 		_loadRequestId = 0;
diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp
index a351ce678..daa9bb593 100644
--- a/Telegram/SourceFiles/data/data_user.cpp
+++ b/Telegram/SourceFiles/data/data_user.cpp
@@ -97,6 +97,7 @@ Data::BusinessRecipients FromMTP(
 	auto result = Data::AwaySettings{
 		.recipients = FromMTP(owner, data.vrecipients()),
 		.shortcutId = data.vshortcut_id().v,
+		.offlineOnly = data.is_offline_only(),
 	};
 	data.vschedule().match([&](
 			const MTPDbusinessAwayMessageScheduleAlways &) {
diff --git a/Telegram/SourceFiles/intro/intro_step.cpp b/Telegram/SourceFiles/intro/intro_step.cpp
index 68ec48cdb..6c8a18227 100644
--- a/Telegram/SourceFiles/intro/intro_step.cpp
+++ b/Telegram/SourceFiles/intro/intro_step.cpp
@@ -193,8 +193,8 @@ void Step::finish(const MTPUser &user, QImage &&photo) {
 	}
 
 	api().request(MTPmessages_GetDialogFilters(
-	)).done([=](const MTPVector<MTPDialogFilter> &result) {
-		createSession(user, photo, result.v);
+	)).done([=](const MTPmessages_DialogFilters &result) {
+		createSession(user, photo, result.data().vfilters().v);
 	}).fail([=] {
 		createSession(user, photo, QVector<MTPDialogFilter>());
 	}).send();
diff --git a/Telegram/SourceFiles/mtproto/scheme/api.tl b/Telegram/SourceFiles/mtproto/scheme/api.tl
index cd07a4e19..af421910b 100644
--- a/Telegram/SourceFiles/mtproto/scheme/api.tl
+++ b/Telegram/SourceFiles/mtproto/scheme/api.tl
@@ -1234,9 +1234,9 @@ bankCardOpenUrl#f568028a url:string name:string = BankCardOpenUrl;
 
 payments.bankCardData#3e24e573 title:string open_urls:Vector<BankCardOpenUrl> = payments.BankCardData;
 
-dialogFilter#7438f7e8 flags:# contacts:flags.0?true non_contacts:flags.1?true groups:flags.2?true broadcasts:flags.3?true bots:flags.4?true exclude_muted:flags.11?true exclude_read:flags.12?true exclude_archived:flags.13?true id:int title:string emoticon:flags.25?string pinned_peers:Vector<InputPeer> include_peers:Vector<InputPeer> exclude_peers:Vector<InputPeer> = DialogFilter;
+dialogFilter#5fb5523b flags:# contacts:flags.0?true non_contacts:flags.1?true groups:flags.2?true broadcasts:flags.3?true bots:flags.4?true exclude_muted:flags.11?true exclude_read:flags.12?true exclude_archived:flags.13?true id:int title:string emoticon:flags.25?string color:flags.27?int pinned_peers:Vector<InputPeer> include_peers:Vector<InputPeer> exclude_peers:Vector<InputPeer> = DialogFilter;
 dialogFilterDefault#363293ae = DialogFilter;
-dialogFilterChatlist#d64a04a8 flags:# has_my_invites:flags.26?true id:int title:string emoticon:flags.25?string pinned_peers:Vector<InputPeer> include_peers:Vector<InputPeer> = DialogFilter;
+dialogFilterChatlist#9fe28ea4 flags:# has_my_invites:flags.26?true id:int title:string emoticon:flags.25?string color:flags.27?int pinned_peers:Vector<InputPeer> include_peers:Vector<InputPeer> = DialogFilter;
 
 dialogFilterSuggested#77744d4a filter:DialogFilter description:string = DialogFilterSuggested;
 
@@ -1683,9 +1683,9 @@ inputBusinessGreetingMessage#194cb3b shortcut_id:int recipients:InputBusinessRec
 
 businessGreetingMessage#e519abab shortcut_id:int recipients:BusinessRecipients no_activity_days:int = BusinessGreetingMessage;
 
-inputBusinessAwayMessage#edac03f4 shortcut_id:int schedule:BusinessAwayMessageSchedule recipients:InputBusinessRecipients = InputBusinessAwayMessage;
+inputBusinessAwayMessage#832175e0 flags:# offline_only:flags.0?true shortcut_id:int schedule:BusinessAwayMessageSchedule recipients:InputBusinessRecipients = InputBusinessAwayMessage;
 
-businessAwayMessage#1bd9bebc shortcut_id:int schedule:BusinessAwayMessageSchedule recipients:BusinessRecipients = BusinessAwayMessage;
+businessAwayMessage#ef156a5c flags:# offline_only:flags.0?true shortcut_id:int schedule:BusinessAwayMessageSchedule recipients:BusinessRecipients = BusinessAwayMessage;
 
 timezone#ff9289f5 id:string name:string utc_offset:int = Timezone;
 
@@ -1700,6 +1700,12 @@ inputQuickReplyShortcutId#1190cf1 shortcut_id:int = InputQuickReplyShortcut;
 messages.quickReplies#c68d6695 quick_replies:Vector<QuickReply> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.QuickReplies;
 messages.quickRepliesNotModified#5f91eb5b = messages.QuickReplies;
 
+connectedBot#e7e999e7 flags:# can_reply:flags.0?true bot_id:long recipients:BusinessRecipients = ConnectedBot;
+
+account.connectedBots#17d7f87b connected_bots:Vector<ConnectedBot> users:Vector<User> = account.ConnectedBots;
+
+messages.dialogFilters#2ad93719 flags:# tags_enabled:flags.0?true filters:Vector<DialogFilter> = messages.DialogFilters;
+
 ---functions---
 
 invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@@ -1829,6 +1835,8 @@ account.updateBusinessWorkHours#4b00e066 flags:# business_work_hours:flags.0?Bus
 account.updateBusinessLocation#9e6b131a flags:# geo_point:flags.1?InputGeoPoint address:flags.0?string = Bool;
 account.updateBusinessGreetingMessage#66cdafc4 flags:# message:flags.0?InputBusinessGreetingMessage = Bool;
 account.updateBusinessAwayMessage#a26a7fa5 flags:# message:flags.0?InputBusinessAwayMessage = Bool;
+account.updateConnectedBot#9c2d527d flags:# can_reply:flags.0?true deleted:flags.1?true bot:InputUser recipients:InputBusinessRecipients = Updates;
+account.getConnectedBots#4ea4c80f = account.ConnectedBots;
 
 users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
 users.getFullUser#b60f5918 id:InputUser = users.UserFull;
@@ -1977,7 +1985,7 @@ messages.sendScheduledMessages#bd38850a peer:InputPeer id:Vector<int> = Updates;
 messages.deleteScheduledMessages#59ae2b16 peer:InputPeer id:Vector<int> = Updates;
 messages.getPollVotes#b86e380e flags:# peer:InputPeer id:int option:flags.0?bytes offset:flags.1?string limit:int = messages.VotesList;
 messages.toggleStickerSets#b5052fea flags:# uninstall:flags.0?true archive:flags.1?true unarchive:flags.2?true stickersets:Vector<InputStickerSet> = Bool;
-messages.getDialogFilters#f19ed96d = Vector<DialogFilter>;
+messages.getDialogFilters#efd48c89 = messages.DialogFilters;
 messages.getSuggestedDialogFilters#a29cd42c = Vector<DialogFilterSuggested>;
 messages.updateDialogFilter#1ad4a04a flags:# id:int filter:flags.0?DialogFilter = Bool;
 messages.updateDialogFiltersOrder#c563c1e4 order:Vector<int> = Bool;
@@ -2067,6 +2075,7 @@ messages.deleteQuickReplyShortcut#3cc04740 shortcut_id:int = Bool;
 messages.getQuickReplyMessages#94a495c3 flags:# shortcut_id:int id:flags.0?Vector<int> hash:long = messages.Messages;
 messages.sendQuickReplyMessages#33153ad4 peer:InputPeer shortcut_id:int = Updates;
 messages.deleteQuickReplyMessages#e105e910 shortcut_id:int id:Vector<int> = Updates;
+messages.toggleDialogFilterTags#fd2dda49 enabled:Bool = Bool;
 
 updates.getState#edd4882a = updates.State;
 updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference;

From 7f7d5449439e6ce3d88d99b165227c9203efe92d Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 1 Mar 2024 10:25:37 +0400
Subject: [PATCH 061/108] Show nice empty quick reply placeholder.

---
 Telegram/Resources/icons/chat/large_away.png  | Bin 0 -> 1996 bytes
 .../Resources/icons/chat/large_away@2x.png    | Bin 0 -> 4375 bytes
 .../Resources/icons/chat/large_away@3x.png    | Bin 0 -> 5879 bytes
 .../Resources/icons/chat/large_greeting.png   | Bin 0 -> 2042 bytes
 .../icons/chat/large_greeting@2x.png          | Bin 0 -> 4529 bytes
 .../icons/chat/large_greeting@3x.png          | Bin 0 -> 4818 bytes
 .../Resources/icons/chat/large_quickreply.png | Bin 0 -> 1940 bytes
 .../icons/chat/large_quickreply@2x.png        | Bin 0 -> 4363 bytes
 .../icons/chat/large_quickreply@3x.png        | Bin 0 -> 5764 bytes
 .../info/settings/info_settings_widget.cpp    |   6 +-
 .../business/settings_quick_replies.cpp       |  18 +-
 .../business/settings_shortcut_messages.cpp   | 170 ++++++++++++++----
 .../settings/settings_business.cpp            |   3 +-
 .../settings/settings_common_session.h        |  11 +-
 .../settings/settings_notifications_type.cpp  |   3 +-
 .../SourceFiles/settings/settings_premium.cpp |   3 +-
 Telegram/SourceFiles/ui/chat/chat.style       |   7 +
 17 files changed, 181 insertions(+), 40 deletions(-)
 create mode 100644 Telegram/Resources/icons/chat/large_away.png
 create mode 100644 Telegram/Resources/icons/chat/large_away@2x.png
 create mode 100644 Telegram/Resources/icons/chat/large_away@3x.png
 create mode 100644 Telegram/Resources/icons/chat/large_greeting.png
 create mode 100644 Telegram/Resources/icons/chat/large_greeting@2x.png
 create mode 100644 Telegram/Resources/icons/chat/large_greeting@3x.png
 create mode 100644 Telegram/Resources/icons/chat/large_quickreply.png
 create mode 100644 Telegram/Resources/icons/chat/large_quickreply@2x.png
 create mode 100644 Telegram/Resources/icons/chat/large_quickreply@3x.png

diff --git a/Telegram/Resources/icons/chat/large_away.png b/Telegram/Resources/icons/chat/large_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..b0a943e0c0746f4ed2c041fcea764492cc1ab4c0
GIT binary patch
literal 1996
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

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/chat/large_away@2x.png b/Telegram/Resources/icons/chat/large_away@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..ca2b6adb6104e97f20a7b84aec2690c6aa6ee4b3
GIT binary patch
literal 4375
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
HHu8S}Voiia

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/chat/large_away@3x.png b/Telegram/Resources/icons/chat/large_away@3x.png
new file mode 100644
index 0000000000000000000000000000000000000000..064090f7689ca16dc908bfdd8758dcab39c3bbce
GIT binary patch
literal 5879
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
Nx{8i+rJ_aX{{ivne4hXS

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/chat/large_greeting.png b/Telegram/Resources/icons/chat/large_greeting.png
new file mode 100644
index 0000000000000000000000000000000000000000..0b1cb033e5bb043da32f96eb79eb9e1a5b72dd9f
GIT binary patch
literal 2042
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
Y4?4+*H@&G#W&i*H07*qoM6N<$g4HJP!T<mO

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/chat/large_greeting@2x.png b/Telegram/Resources/icons/chat/large_greeting@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..66fd705ad59fb3ccaceb89cab5eef2844b21e743
GIT binary patch
literal 4529
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
Kc-cLx!2bbAtG%-T

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/chat/large_greeting@3x.png b/Telegram/Resources/icons/chat/large_greeting@3x.png
new file mode 100644
index 0000000000000000000000000000000000000000..cd08060ff30c7af15232081cf4047f41d28b63c4
GIT binary patch
literal 4818
zcmZ`-cQhPd`(JI92v%9WgkVL4AkkS}BrLLOf|UrOTdPEgPV_|YEI~vGk*MD&5fOd$
zUSrvfUZeZX_dVzL*L&W3?#!K;Gjq?}d!FZ0o@fJo?dvogG$0V@`U4#eBVgPCjxSWC
zz*+F}ek(9gyX%;Hf<V`p{yj)QSvhPV5M%fQ4OOIX&gNW@4yXMrXvc{|^1;ZXiiT%)
zQMQEx<`7ejS(v^qLwWM0yV2||O^uv4Z<2>qa+cqVR=tshK;8|=i;Tw@=cMqP<RlYv
zjGa33b7cjM*GC0~rU_?J+u1Tw-T20pt&{oO!L`Af<}>AmwOzkP|GaH=LvmGy|J`v4
zVD4bw<KyETd9R+{-iJcTBFr(ryAnm2X^M?R(t8nkiop3gxZ~*PsP(GLR+2qlt7xd9
zv9WA~Xw~}qTYWv{996pj*)+?o)IUXu%0^|#%gf7_mX_DAUvIZwwdCjL8<k-;!@*%o
zz^#>;Sw?QIrIi(MVP<8Omy?r|pKmXN9ugqqFm`u$M<S8F|7kipZ%RvrCroE&XLIuM
zcsM!F_6Jph&Nk~+E|0_3MPoqhILxS{mztUy27>`Ugu{a$36as-d3zsq#<GTmg>7zb
z4(6*+#)UJmeYLQB`czR_xn<BaIXU^oix)OFHn=i=-@cKN{qf2t0X$uGB_$=g4<BBf
zpBE%+?>s=GKUe>(sVpuoj*FuuXfWa=rKBdt#|yLtvVpg-$-3|l6&2w;{#=eOE}c6(
zk&6*&X;8v$WH*8Ep}t-Q(?UHk+vrK@7f0gY>Kdu9*k0!}QKgzFifU|Z#O1=PZ`{A{
z{puCFpX~1U>1hi!HBw9zLlv-bz(3=P-uRrJo}OP^U=^ZJFeRlqTS--Vp39!TzVI!6
z()h$ggr45@gJ4MTz<@rwd{tpL5VmAtXb8cSLg*BIcR+r=R7MB{0uBdZwjn0KJi<ak
z1(cD&?^yX13x^!)>g%yl;cRbx#&%+2W3f>oACRV|>OMrsI1UF=0%_>r@_Q)$A$P)H
zq@|^q<!L-PxVX*_Mi4&U-UVbfk3Nw3p-_C1l8o{+cR4vZ75tBGW0>Qp(eUJ<xUN`M
zaplY7=LA^BJ2+*VnYK11d%tgZcsMOBEnNGCqwQzNYl})~(~RcFvNGx>&qSDH$&{&)
z5$Rl|wuO_E=m7&9%_R@I+1=fpnwp9;ERbZQy>{)IP%?3)I~i9~Ed+x(1O;79)!O4i
zsH|;mZysz?kByI;czf5Sr%$M^V6oWv_;{RZjfkKig|`G%UVVMN0o0(fqT<7t<BSk9
z6si{Wvt)A8?7Rc6_{BmDLtj{!d3#}Mikh3-$=k=L`{`p=YQn<$?yg5xq=u%ZD&?<|
zp&>)(=Ws<vMn=$CpY#3fe!GLK*6QlFEppu4+_Rdo)|#3Wc{Y4J2A0Ow)<Fg|7DCCI
zT3UC+#IRiWgNflr&$WgGuG&H6GulBNhOm|v<+u7xul7q^Bw&NPcsyR86IehbmU{9n
zB|S1aI+{;ubZE#%h9_EM`CziLQmk~-sC<mgB4lK)*=K4i;BM%jJW<msSsD?3{_n&k
zbmQ#Hk*20532AcS&#}QwWMpIpflORnkC@C}zgE6j`u6Rc{*$%M&G=HWNsO0Q&BRF|
z*;#KlvA?9WH0qKT=Hd*5?=VL|QgZSX0hoM_mH+PDcgl*qyu9D65T21hY?ivDblXl^
zgi~fpxmX(+F*<jBsjpYKZd5dc%O@``UKV}hxjB8W{>&O9BrJ?Qd!eGNJpVH1AYz|E
z)XByP!t?w0Z^q~y6BQ32lDV56AN~FN*GoS4MOBUfy78aZ*8J6*uuYZ)I&mVA=uAsq
zvxvAMo)SF)fk33c`i%?^8)U`R8nWFM7k^l6<kJ-*yMo87`g8@!UU=ykA{`w+7Imbj
zr5RWR?yu_Q#DC`#746-u;iCc8jSJl6otc?|A;OJ~jS~if&5jxDizEQdareJXwQ#b(
zhE@1Z_d^gPARypPuN{yK1YAU;<x>G(x|6eg*qu8AFL!tYc0!6eAhA(zoEPFK>3JHI
z_{00Uy6kOiNSau=)Lwosewe<&Gc!CKiuSdz;8@l2=>G<};hve9i90C`lT->T`~Ym=
z%yT^<re!)I`P_ugjP1GRbf6_ACFSSSe_jz*P?-6qdczig*!Fx6Er*gt^V()QP|bmw
zt}cxzR^G2W`07D!I~4<yii%2KblrVPNh5RfsmVzKri|s8r!)Oi3k$XL*L4jI1UpYc
zs|S>sx#87otpo!2w*k}1q2C1-0Xh)>4!LGCRmmHA{I0V<8)IzDa&dx3%MD~w2nq_q
zDxukXduV5oIy)J*)BZLHuj0q@a(eFL1LETpGC{`f(a{vAe${}pSWn}b>1hsKokITf
zUVA&c>f*`JRZ+f+46XAjr?X*z5em3;_KEL!dhUa7rS@Eua%dT3-M-b{-Y&Zzn?V3z
zRGYH!(Vb$LF4llczPhD_2M;epp8gpVE$Ii6gs!K17tybIE(N83%j&(pzP?4CmX#GV
zEIlJP^2Wbi;(}P$(z1ndT?`o>HZtAAM*@_P()NSw{SM9YZ##Ycgt-m?pB0sq3<nHM
zOzZ;rmJ@Hfxw&Z}hUMhsV5P;uj_+>#6aq_T#u-}jpPPFhY(XzZst)5szgcbn{vC!+
z87Xdnc64;S*(Oib75sI%rd!N-*EE#!7%b1vPkL8JyA_h0lw?5n_wev*Rd!Z>exk4C
zk8J7XeR>MhL^dV0_SpD%iU+#@@f{uA_sj8Kd-+ID@5a1qW$!+=wY#Uk-)lN1Gn4(m
z#Ma)vHdEEs%gc+PO2c`oQt=d?b05PeCpUFOOPHZH8_d)S$ka1T8DY)xewZdVJUsjz
z4Ib+2@5fS#I4}0*0TC3%Q#p4g-GiPcOB?nD`0Py-u9b9{h#|E5`}?~&JG&=okP|#;
zIC-JIMJc9V8qoM2Y{%N_D!|-5H2_AYIr0$v<wlT7Vq#*NiCo_5^arvL*Cjf~+4SV9
zFOs4zg>B475{|th3oXHkMjC_507Y`y;n+|AIP$lb533SBp+)=`AcMIIQNA1PkH>%g
zqB_N!n3y<|c_aD9Af@<TS+ARpoVeRhNOun>tcxgv2tT9uh(n|o@k=Dlk9P`-iiQUH
z(tAB#47CW{-Asu3x-sPTC8Zx)Q17;|ddIZ6xp{oT!ra^j0|)`xc@3UOqYcE)^71mO
z*4ZgAFn4HV9xFZ*5)#6N|AR~MC@Iedh-_DAPXfE4vJO2p&oWqDU1h7Rt<F1*Giq6Q
zF>YO%$#d;VzZdT-!;<RVO8uk=lXA@*e@t7GqkvDg)Cc6P<nAxc&AkNk{6bh6k|v{%
zjPPlmg7&I4%<o%EO-)U8_0G}e%z<Y@=6I2tgF`i!lMkmkJYbUR9Z|iN&lrhZ>?_;c
zPRht&%W~^|^sv}Wmm=el&{5J4(!N*T<nq^N%gf6<()uGK!P+*slvj50<t1fh#6`Fk
z;`Y<u-8@9x%SE061<H%FsEmw^r6rqYO3ADZRYRne6;F{Hkqz}wQi&k#{;9f}0ddRF
zgrpmf*4EU>9S1K;<)yUQN+-s!fM%%vd9@E7j4&wS<D`vrYdKl%6>t3#e@j&KT3{Az
zL3ZvVd!m5hU~pxRseXK1T!Gs;)}(y%jwdsr>xuU|zOmxdCwh6N*=F}8(z<ZTIn><N
zazbb*X@XHZw=;VQkXztf>Mg9ZR3B8Uc82cw>}&=a+vxEdOqsc;>)-&ZqaEo`Nhe!m
zKRsNZn5<%fLdWOk`U!7OPEMrk2hMjo=*O>Ws;cNbTyFD9T=+yQ06T)a;ZA=Q;4lHL
zrvQU_<Pk_D^ZL{x>4kob?Cwgv$QnFpeY)g%)Gf39&9gd7b#-+<_B#C98f7l+?E<@y
z8)>|<Km6WpEHCq4`TQm;3w_CYS=ZrguOKT+g|^JqU;F;Nqewe({?%IU+F<@;=3=8V
z1Mj=x_@yNh?(Yz?=G@F)mO!+pC&lgw!~30dF|9H8@ax_#7s`9kebTQjEp*xQWPGW~
zC?2`hwKdf$r`<W9g=pW#nVEVwta<?~=Jew40#?0ElmDHwo16NA>XCwi0{d7415ID&
zDK|-(GL2`{{LIYy#s+!gm#+;CvXv&HEze+DMG$%Nn8FYJ{d(uD5$rf-&D|BzARQeY
z1cJUu5`11?TT8}GdU(A1oWQUXI{mz}3Q#oHSnt3~!|xRp72(+DRDv!QF&sNdIyVzc
zV&OJT6<PJiJH-qiH&KaPK>oL0o{El+ZOTsya>~;{H#Rrh!ND%qqr|F7^bDEl%iqp^
z^RI7e;*h8D5n-m81id~b3QvMi$ET-zOZZ%|ymOQn`!Sm3AVTdK+1DqbkC~X5aE%^w
zSjNPR_Sd`M%ZF;>poFRRhinAcQ*s5~w?miW6$)Tqmzcs8^vTYSgwL45+~2L{?a%-|
zcgl_Y<(lVj!)T7_TqM|h{gaieYib~jjHKKVsM*#aUU_l{*@?cU+j4U3@(3UaSx|+)
zcvUnFkv={$C|*9s9#Kc<7YJu=EH&=Hb+&<Ij4}>z2k_FK#=5%uAI_;9Y!ugykJxA5
zm6Qk(ka8rcnPp`ngp&OSfJ{LmB*uUMbl9iK(a%@Do(e_#EBAelkhqFZNZ6~*Qw8+0
zj@rKxaO5OPm<0tPV&t`+fR}9~GvS|a6x74bsF%H>BBq=ZVq?Pwrmn)KL7!V&d7Fp<
zo%MQlhPHNgYNeY&dU|>xw(z1MxJ+)>?Xy%Ws?zLk{+Pr>%9jc{m82mgL^hd_LD{*<
z$+iVYfM?d%ug$oVP*GF2FUV|><#^B-yCP=~{U&Xv&Jkt@eoB{5HMO<Fq|*WjcHsBP
zW+9Cx{1@{Lvr7nbFPojee<_K=CgBkgOWlm4N%_mm&lImSv*bB%t#i=Me1-?I2JEd+
zPv|j;*#tVug%iEM#KBW<{%lPL<nGo3K|UaRo{qBD&Lcrmk`W7*0C*ZaW}Y-cB7ZE4
zYqp_x+1c5hzb@;HI7k~<{z?N}N=3zOnf=A3B|~d#`Z4kl*N?rAfu{!C-Taj%*_oL{
z3+}X)72CGC;GUiyv+a>0eVn*bMSovkN?KasMDe(TG$LP_g4T>2Kjixnh_`BMa~Wl2
z!1d*2WH>Z0<dl}$2LuEFMrPE9DHg!Rw=Le+-LdSfqZ1RQ#l@=H?JYr<CtVKGS^cYk
z6$Qj!Ud5i@`SC8RbpQ!>#N<6`>9+YV33REt6mv6uRG)QMOsxYC2}1$|SVDLpmgSgf
zCAb9kXAj>?&#N4spmwgNu8vhlt;%9RRZgz1fQ-)%x|GB4J-e@@bg?ld1$_1|dK1<j
z-Cf_>irliX3;&zek*0>7oSqI%Q2T=F{VgdtYVY85b$Pa3^StZl&z}Wk5MdjaI?+gy
zz{QQJT9vDFDGW3J%?fVHPN@Qsn5Nekr+^Oy#2%Im!a^&`Vs`U=mw*Nj56``O_X?*p
zlj+G1*u*|69FP<Z`CgnJ_NI#u4h*Pu@iPM27k8t8amU}mPu#lY9G`BTqKg;K4<O}^
zU3^eHEKtv`<OWy_Zwv<BI>@3(aJ_jc3fRej04_fy6`ROOr*`@sOUdkh+;jD}vX<@o
z4T8eLQM691@V}g@4~vF^#laBh=anZQz&r;9sT>&C^aI9zZ%@)^Eo$oyX}lU1u&k@D
zTquJlPn^sjSv`H)H)Q-v&jM9gSQu^`;EY1Gxw7Ee+TaOQZ(1KyL4S019v&T`rG;jr
z%<4+Y%VQ58PK=JCeyOylHZ(MR{CG=wbmEDH#m5gHP!_3j_wIQC3_hnrUVkUHT3lYf
z>HT|K2L}fyr`Wk0&(f_v1s(n|wzRYi2>2)aU2U^HMgE!;jUAL_fgETQ6aY_vDJdzz
z;M4!FR`h<Yb#ZmA0-TbVm>vSr8gPPkc6NUDZ242`<+Hns)z#HN&EaynGqxgUbEzZB
z^pn*KJaU7dEbAU)^pAywtG-MrbmRGtXeQlaqdV#Tetvnf9%@*FU--S;|1?06aKvkU
zvubT>I$iDO%+&|LQ(i^IO@d8=oE-{H1Bx3dDenznvx;MhKw)CDe&I7Ci)sV@fH?B%
z)hi_>C0Awt_ob!kG7)?g;ZaenfIvSlL8DNhCJJ&J1h8^IH$8ql0+85g@VMhwTnMjL
z9ya>wt#0wH)LSqZjGv!hQBm>c&G3?q?Xhyx6O+~$T8Hn{Wyn87;>o=KcG=h;dX;kt
wK0ZK95A^i`uo~p&$008t%18CzJ?4}b+bp`IE<<qv6sbTDH1##A)KH=S2bU97vH$=8

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/chat/large_quickreply.png b/Telegram/Resources/icons/chat/large_quickreply.png
new file mode 100644
index 0000000000000000000000000000000000000000..084b399ea386d5025874dc41c57bef637f0d4a7b
GIT binary patch
literal 1940
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
a@x(t${@f+R=!cg80000<MNUMnLSTZ+&!B4n

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/chat/large_quickreply@2x.png b/Telegram/Resources/icons/chat/large_quickreply@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..5ec1ffe4fe582289267356dc0ab165ae510d86f8
GIT binary patch
literal 4363
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
F{{Wbvh`ay*

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/chat/large_quickreply@3x.png b/Telegram/Resources/icons/chat/large_quickreply@3x.png
new file mode 100644
index 0000000000000000000000000000000000000000..8bc18f9ade734a10abf715683c6948dbe8e39f70
GIT binary patch
literal 5764
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

literal 0
HcmV?d00001

diff --git a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp
index 8a53dc553..5bc315aa1 100644
--- a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp
+++ b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp
@@ -47,7 +47,11 @@ Widget::Widget(
 	auto inner = _type->create(
 		this,
 		controller->parentController(),
-		scroll());
+		scroll(),
+		controller->wrapValue(
+		) | rpl::map([](Wrap wrap) { return (wrap == Wrap::Layer)
+			? ::Settings::Container::Layer
+			: ::Settings::Container::Section; }));
 	if (inner->hasFlexibleTopBar()) {
 		auto filler = setInnerWidget(object_ptr<Ui::RpWidget>(this));
 		filler->resize(1, 1);
diff --git a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
index 431dcd4e1..aac5f918c 100644
--- a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
@@ -96,9 +96,8 @@ void QuickReplies::setupContent(
 			Box(EditShortcutNameBox, QString(), crl::guard(this, submit)));
 	});
 
-	Ui::AddSkip(content);
-	Ui::AddDivider(content);
-	Ui::AddSkip(content);
+	const auto dividerWrap = content->add(
+		object_ptr<Ui::VerticalLayout>(content));
 
 	const auto inner = content->add(
 		object_ptr<Ui::VerticalLayout>(content));
@@ -133,6 +132,18 @@ void QuickReplies::setupContent(
 		while (old--) {
 			delete inner->widgetAt(0);
 		}
+		if (!inner->count()) {
+			while (dividerWrap->count()) {
+				delete dividerWrap->widgetAt(0);
+			}
+		} else if (!dividerWrap->count()) {
+			AddSkip(dividerWrap);
+			AddDivider(dividerWrap);
+			AddSkip(dividerWrap);
+		}
+		if (const auto width = content->width()) {
+			content->resizeToWidth(width);
+		}
 	}, content->lifetime());
 
 	Ui::ResizeFitChild(this, content);
@@ -170,6 +181,7 @@ void EditShortcutNameBox(
 	box->setFocusCallback([=] {
 		field->setFocusFast();
 	});
+	field->selectAll();
 
 	const auto callback = [=] {
 		const auto name = field->getLastText().trimmed();
diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
index 8df33ebf0..b72acab4b 100644
--- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
@@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "history/view/history_view_corner_buttons.h"
 #include "history/view/history_view_empty_list_bubble.h"
 #include "history/view/history_view_list_widget.h"
+#include "history/view/history_view_service_message.h"
 #include "history/view/history_view_sticker_toast.h"
 #include "history/history.h"
 #include "history/history_item.h"
@@ -51,6 +52,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/text/text_utilities.h"
 #include "ui/widgets/menu/menu_add_action_callback.h"
 #include "ui/widgets/scroll_area.h"
+#include "ui/painter.h"
 #include "window/themes/window_theme.h"
 #include "window/section_widget.h"
 #include "window/window_session_controller.h"
@@ -74,6 +76,7 @@ public:
 		QWidget *parent,
 		not_null<Window::SessionController*> controller,
 		not_null<Ui::ScrollArea*> scroll,
+		rpl::producer<Container> containerValue,
 		BusinessShortcutId shortcutId);
 	~ShortcutMessages();
 
@@ -97,7 +100,7 @@ public:
 		QRect clip) override;
 
 private:
-	void outerResized(QSize outer);
+	void outerResized();
 	void updateComposeControlsPosition();
 
 	// ListDelegate interface.
@@ -247,6 +250,7 @@ private:
 		const Window::SectionShow &params);
 	void showAtEnd();
 	void finishSending();
+	void refreshEmptyText();
 
 	const not_null<Window::SessionController*> _controller;
 	const not_null<Main::Session*> _session;
@@ -254,6 +258,7 @@ private:
 	const not_null<History*> _history;
 	rpl::variable<BusinessShortcutId> _shortcutId;
 	rpl::variable<QString> _shortcut;
+	rpl::variable<Container> _container;
 	std::shared_ptr<Ui::ChatStyle> _style;
 	std::shared_ptr<Ui::ChatTheme> _theme;
 	QPointer<ListWidget> _inner;
@@ -262,6 +267,14 @@ private:
 	rpl::event_stream<> _showBackRequests;
 	bool _skipScrollEvent = false;
 
+	QSize _inOuterResize;
+	QSize _pendingOuterResize;
+
+	const style::icon *_emptyIcon = nullptr;
+	Ui::Text::String _emptyText;
+	int _emptyTextWidth = 0;
+	int _emptyTextHeight = 0;
+
 	rpl::variable<Info::SelectedItems> _selectedItems
 		= Info::SelectedItems(Storage::SharedMediaType::kCount);
 
@@ -283,22 +296,33 @@ struct Factory final : AbstractSectionFactory {
 	object_ptr<AbstractSection> create(
 		not_null<QWidget*> parent,
 		not_null<Window::SessionController*> controller,
-		not_null<Ui::ScrollArea*> scroll
+		not_null<Ui::ScrollArea*> scroll,
+		rpl::producer<Container> containerValue
 	) const final override {
 		return object_ptr<ShortcutMessages>(
 			parent,
 			controller,
 			scroll,
+			std::move(containerValue),
 			shortcutId);
 	}
 
 	const BusinessShortcutId shortcutId = {};
 };
 
+[[nodiscard]] bool IsAway(const QString &shortcut) {
+	return (shortcut == u"away"_q);
+}
+
+[[nodiscard]] bool IsGreeting(const QString &shortcut) {
+	return (shortcut == u"hello"_q);
+}
+
 ShortcutMessages::ShortcutMessages(
 	QWidget *parent,
 	not_null<Window::SessionController*> controller,
 	not_null<Ui::ScrollArea*> scroll,
+	rpl::producer<Container> containerValue,
 	BusinessShortcutId shortcutId)
 : AbstractSection(parent)
 , _controller(controller)
@@ -308,6 +332,7 @@ ShortcutMessages::ShortcutMessages(
 , _shortcutId(shortcutId)
 , _shortcut(
 	_session->data().shortcutMessages().lookupShortcut(shortcutId).name)
+, _container(std::move(containerValue))
 , _cornerButtons(
 		_scroll,
 		controller->chatStyle(),
@@ -345,8 +370,8 @@ ShortcutMessages::ShortcutMessages(
 
 	_scroll->sizeValue() | rpl::filter([](QSize size) {
 		return !size.isEmpty();
-	}) | rpl::start_with_next([=](QSize size) {
-		outerResized(size);
+	}) | rpl::start_with_next([=] {
+		outerResized();
 	}, lifetime());
 
 	_scroll->scrolls(
@@ -354,6 +379,11 @@ ShortcutMessages::ShortcutMessages(
 		processScroll();
 	}, lifetime());
 
+	_shortcut.value() | rpl::start_with_next([=] {
+		refreshEmptyText();
+		_inner->update();
+	}, lifetime());
+
 	_inner->editMessageRequested(
 	) | rpl::start_with_next([=](auto fullId) {
 		if (const auto item = _session->data().message(fullId)) {
@@ -364,17 +394,6 @@ ShortcutMessages::ShortcutMessages(
 		}
 	}, _inner->lifetime());
 
-	{
-		auto emptyInfo = base::make_unique_q<EmptyListBubbleWidget>(
-			_inner,
-			_style.get(),
-			st::msgServicePadding);
-		const auto emptyText = Ui::Text::Semibold(
-			u"give me your money.."_q);
-		emptyInfo->setText(emptyText);
-		_inner->setEmptyInfoWidget(std::move(emptyInfo));
-	}
-
 	_inner->heightValue() | rpl::start_with_next([=](int height) {
 		resize(width(), height);
 	}, lifetime());
@@ -382,15 +401,62 @@ ShortcutMessages::ShortcutMessages(
 
 ShortcutMessages::~ShortcutMessages() = default;
 
+void ShortcutMessages::refreshEmptyText() {
+	const auto &shortcut = _shortcut.current();
+	const auto away = IsAway(shortcut);
+	const auto greeting = !away && IsGreeting(shortcut);
+	auto text = away
+		? tr::lng_away_empty_title(
+			tr::now,
+			Ui::Text::Bold
+		).append("\n\n").append(tr::lng_away_empty_about(tr::now))
+		: greeting
+		? tr::lng_greeting_empty_title(
+			tr::now,
+			Ui::Text::Bold
+		).append("\n\n").append(tr::lng_greeting_empty_about(tr::now))
+		: tr::lng_replies_empty_title(
+			tr::now,
+			Ui::Text::Bold
+		).append("\n\n").append(tr::lng_replies_empty_about(
+			tr::now,
+			lt_shortcut,
+			Ui::Text::Bold('/' + shortcut),
+			Ui::Text::WithEntities));
+	_emptyIcon = away
+		? &st::awayEmptyIcon
+		: greeting
+		? &st::greetingEmptyIcon
+		: &st::repliesEmptyIcon;
+	const auto padding = st::repliesEmptyPadding;
+	const auto minWidth = st::repliesEmptyWidth / 4;
+	const auto maxWidth = std::max(
+		minWidth + 1,
+		st::repliesEmptyWidth - padding.left() - padding.right());
+	_emptyText = Ui::Text::String(
+		st::messageTextStyle,
+		text,
+		kMarkupTextOptions,
+		minWidth);
+	const auto countHeight = [&](int width) {
+		return _emptyText.countHeight(width);
+	};
+	_emptyTextWidth = Ui::FindNiceTooltipWidth(
+		minWidth,
+		maxWidth,
+		countHeight);
+	_emptyTextHeight = countHeight(_emptyTextWidth);
+}
+
 Type ShortcutMessages::Id(BusinessShortcutId shortcutId) {
 	return std::make_shared<Factory>(shortcutId);
 }
 
 rpl::producer<QString> ShortcutMessages::title() {
 	return _shortcut.value() | rpl::map([=](const QString &shortcut) {
-		return (shortcut == u"away"_q)
+		return IsAway(shortcut)
 			? tr::lng_away_title()
-			: (shortcut == u"hello"_q)
+			: IsGreeting(shortcut)
 			? tr::lng_greeting_title()
 			: rpl::single('/' + shortcut);
 	}) | rpl::flatten_latest();
@@ -497,22 +563,36 @@ bool ShortcutMessages::paintOuter(
 	return true;
 }
 
-void ShortcutMessages::outerResized(QSize outer) {
-	const auto contentWidth = outer.width();
+void ShortcutMessages::outerResized() {
+	const auto outer = _scroll->size();
+	if (!_inOuterResize.isEmpty()) {
+		_pendingOuterResize = (_inOuterResize != outer)
+			? outer
+			: QSize();
+		return;
+	}
+	_inOuterResize = outer;
 
-	const auto newScrollTop = _scroll->isHidden()
-		? std::nullopt
-		: _scroll->scrollTop()
-		? base::make_optional(_scroll->scrollTop())
-		: 0;
-	_skipScrollEvent = true;
-	_inner->resizeToWidth(contentWidth, st::boxWidth);
-	_skipScrollEvent = false;
+	do {
+		const auto newScrollTop = _scroll->isHidden()
+			? std::nullopt
+			: _scroll->scrollTop()
+			? base::make_optional(_scroll->scrollTop())
+			: 0;
+		_skipScrollEvent = true;
+		const auto minHeight = (_container.current() == Container::Layer)
+			? st::boxWidth
+			: _inOuterResize.height();
+		_inner->resizeToWidth(_inOuterResize.width(), minHeight);
+		_skipScrollEvent = false;
 
-	if (!_scroll->isHidden()) {
-		if (newScrollTop) {
+		if (!_scroll->isHidden() && newScrollTop) {
 			_scroll->scrollToY(*newScrollTop);
 		}
+		_inOuterResize = base::take(_pendingOuterResize);
+	} while (!_inOuterResize.isEmpty());
+
+	if (!_scroll->isHidden()) {
 		updateInnerVisibleArea();
 	}
 	updateComposeControlsPosition();
@@ -896,8 +976,36 @@ void ShortcutMessages::listOpenDocument(
 }
 
 void ShortcutMessages::listPaintEmpty(
-	Painter &p,
-	const Ui::ChatPaintContext &context) {
+		Painter &p,
+		const Ui::ChatPaintContext &context) {
+	Expects(_emptyIcon != nullptr);
+
+	const auto width = st::repliesEmptyWidth;
+	const auto padding = st::repliesEmptyPadding;
+	const auto height = padding.top()
+		+ _emptyIcon->height()
+		+ st::repliesEmptySkip
+		+ _emptyTextHeight
+		+ padding.bottom();
+	const auto r = QRect(
+		(this->width() - width) / 2,
+		(this->height() - height) / 3,
+		width,
+		height);
+	HistoryView::ServiceMessagePainter::PaintBubble(p, context.st, r);
+
+	_emptyIcon->paint(
+		p,
+		r.x() + (r.width() - _emptyIcon->width()) / 2,
+		r.y() + padding.top(),
+		this->width());
+	p.setPen(st::msgServiceFg);
+	_emptyText.draw(
+		p,
+		r.x() + (r.width() - _emptyTextWidth) / 2,
+		r.y() + padding.top() + _emptyIcon->height() + st::repliesEmptySkip,
+		_emptyTextWidth,
+		style::al_top);
 }
 
 QString ShortcutMessages::listElementAuthorRank(
diff --git a/Telegram/SourceFiles/settings/settings_business.cpp b/Telegram/SourceFiles/settings/settings_business.cpp
index 0881925b5..a8792783c 100644
--- a/Telegram/SourceFiles/settings/settings_business.cpp
+++ b/Telegram/SourceFiles/settings/settings_business.cpp
@@ -562,7 +562,8 @@ struct SectionFactory<Business> : AbstractSectionFactory {
 	object_ptr<AbstractSection> create(
 		not_null<QWidget*> parent,
 		not_null<Window::SessionController*> controller,
-		not_null<Ui::ScrollArea*> scroll
+		not_null<Ui::ScrollArea*> scroll,
+		rpl::producer<Container> containerValue
 	) const final override {
 		return object_ptr<Business>(parent, controller);
 	}
diff --git a/Telegram/SourceFiles/settings/settings_common_session.h b/Telegram/SourceFiles/settings/settings_common_session.h
index 2ebd1319c..2b439bacf 100644
--- a/Telegram/SourceFiles/settings/settings_common_session.h
+++ b/Telegram/SourceFiles/settings/settings_common_session.h
@@ -26,13 +26,19 @@ class SessionController;
 
 namespace Settings {
 
+enum class Container {
+	Section,
+	Layer,
+};
+
 class AbstractSection;
 
 struct AbstractSectionFactory {
 	[[nodiscard]] virtual object_ptr<AbstractSection> create(
 		not_null<QWidget*> parent,
 		not_null<Window::SessionController*> controller,
-		not_null<Ui::ScrollArea*> scroll) const = 0;
+		not_null<Ui::ScrollArea*> scroll,
+		rpl::producer<Container> containerValue) const = 0;
 	[[nodiscard]] virtual bool hasCustomTopBar() const {
 		return false;
 	}
@@ -45,7 +51,8 @@ struct SectionFactory : AbstractSectionFactory {
 	object_ptr<AbstractSection> create(
 		not_null<QWidget*> parent,
 		not_null<Window::SessionController*> controller,
-		not_null<Ui::ScrollArea*> scroll
+		not_null<Ui::ScrollArea*> scroll,
+		rpl::producer<Container> containerValue
 	) const final override {
 		return object_ptr<SectionType>(parent, controller);
 	}
diff --git a/Telegram/SourceFiles/settings/settings_notifications_type.cpp b/Telegram/SourceFiles/settings/settings_notifications_type.cpp
index 717627573..802a82ee6 100644
--- a/Telegram/SourceFiles/settings/settings_notifications_type.cpp
+++ b/Telegram/SourceFiles/settings/settings_notifications_type.cpp
@@ -44,7 +44,8 @@ struct Factory : AbstractSectionFactory {
 	object_ptr<AbstractSection> create(
 		not_null<QWidget*> parent,
 		not_null<Window::SessionController*> controller,
-		not_null<Ui::ScrollArea*> scroll
+		not_null<Ui::ScrollArea*> scroll,
+		rpl::producer<Container> containerValue
 	) const final override {
 		return object_ptr<NotificationsType>(parent, controller, type);
 	}
diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp
index ec17c8aac..809b8ba8e 100644
--- a/Telegram/SourceFiles/settings/settings_premium.cpp
+++ b/Telegram/SourceFiles/settings/settings_premium.cpp
@@ -1268,7 +1268,8 @@ struct SectionFactory<Premium> : AbstractSectionFactory {
 	object_ptr<AbstractSection> create(
 		not_null<QWidget*> parent,
 		not_null<Window::SessionController*> controller,
-		not_null<Ui::ScrollArea*> scroll
+		not_null<Ui::ScrollArea*> scroll,
+		rpl::producer<Container> containerValue
 	) const final override {
 		return object_ptr<Premium>(parent, controller);
 	}
diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style
index 4ec9954a5..b4217d85b 100644
--- a/Telegram/SourceFiles/ui/chat/chat.style
+++ b/Telegram/SourceFiles/ui/chat/chat.style
@@ -1044,6 +1044,13 @@ premiumRequiredWidth: 186px;
 premiumRequiredIcon: icon{{ "chat/large_lockedchat", msgServiceFg }};
 premiumRequiredCircle: 60px;
 
+repliesEmptyIcon: icon{{ "chat/large_quickreply", msgServiceFg }};
+greetingEmptyIcon: icon{{ "chat/large_greeting", msgServiceFg }};
+awayEmptyIcon: icon{{ "chat/large_away", msgServiceFg }};
+repliesEmptyWidth: 264px;
+repliesEmptySkip: 16px;
+repliesEmptyPadding: margins(10px, 20px, 10px, 16px);
+
 boostMessageIcon: icon {{ "stories/boost_mini", windowFg }};
 boostMessageIconPadding: margins(0px, 2px, 0px, 0px);
 boostsMessageIcon: icon {{ "stories/boosts_mini", windowFg }};

From dd6768a476e6501ed3ba241b303099ffa8af2b2f Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 1 Mar 2024 12:26:27 +0400
Subject: [PATCH 062/108] Add stories rights by default to new admins.

---
 Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp
index ff9398bf0..6268db65b 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp
@@ -216,6 +216,9 @@ ChatAdminRightsInfo EditAdminBox::defaultRights() const {
 		: peer()->isMegagroup()
 		? ChatAdminRightsInfo{ (Flag::ChangeInfo
 			| Flag::DeleteMessages
+			| Flag::PostStories
+			| Flag::EditStories
+			| Flag::DeleteStories
 			| Flag::BanUsers
 			| Flag::InviteByLinkOrAdd
 			| Flag::ManageTopics
@@ -225,6 +228,9 @@ ChatAdminRightsInfo EditAdminBox::defaultRights() const {
 			| Flag::PostMessages
 			| Flag::EditMessages
 			| Flag::DeleteMessages
+			| Flag::PostStories
+			| Flag::EditStories
+			| Flag::DeleteStories
 			| Flag::InviteByLinkOrAdd
 			| Flag::ManageCall) };
 }

From ca4cbddba6a3856bcc3303c4e897c7bc0a218e63 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 1 Mar 2024 12:29:03 +0400
Subject: [PATCH 063/108] Check shortcuts / messages limits.

---
 Telegram/Resources/langs/lang.strings         |  3 +
 .../chat_helpers/stickers_list_footer.cpp     |  5 +-
 .../history_view_compose_controls.cpp         |  1 +
 .../business/settings_away_message.cpp        | 45 ++++++++--
 .../settings/business/settings_greeting.cpp   | 44 ++++++++--
 .../business/settings_quick_replies.cpp       | 71 +++++++++-------
 .../business/settings_recipients_helper.cpp   | 83 +++++++++++++++++++
 .../business/settings_recipients_helper.h     | 22 +++++
 .../business/settings_shortcut_messages.cpp   | 24 +++++-
 .../SourceFiles/settings/settings_main.cpp    |  4 -
 Telegram/lib_ui                               |  2 +-
 11 files changed, 253 insertions(+), 51 deletions(-)

diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 896fdc415..add970d06 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -2226,6 +2226,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_greeting_empty_title" = "New Greeting Message";
 "lng_greeting_empty_about" = "Create greetings that will be automatically sent to new customers.";
 "lng_greeting_message_placeholder" = "Add a Greeting";
+"lng_greeting_limit_reached" = "You have too many quick replies. Remove one to add a greeting message.";
 
 "lng_away_title" = "Away Message";
 "lng_away_about" = "Automatically reply with a message when you are away.";
@@ -2242,7 +2243,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_away_empty_title" = "New Away Message";
 "lng_away_empty_about" = "Add messages that will be automatically sent when you are off.";
 "lng_away_message_placeholder" = "Add an Away Message";
+"lng_away_limit_reached" = "You have too many quick replies. Remove one to add an away message.";
 
+"lng_business_edit_messages" = "Edit messages";
 "lng_business_limit_reached#one" = "Limit of {count} message reached.";
 "lng_business_limit_reached#other" = "Limit of {count} messages reached.";
 
diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_footer.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_footer.cpp
index 4534818d3..6659343d2 100644
--- a/Telegram/SourceFiles/chat_helpers/stickers_list_footer.cpp
+++ b/Telegram/SourceFiles/chat_helpers/stickers_list_footer.cpp
@@ -114,10 +114,7 @@ std::optional<EmojiSection> SetIdEmojiSection(uint64 id) {
 rpl::producer<std::vector<GifSection>> GifSectionsValue(
 		not_null<Main::Session*> session) {
 	const auto config = &session->account().appConfig();
-	return rpl::single(
-		rpl::empty_value()
-	) | rpl::then(
-		config->refreshed()
+	return config->value(
 	) | rpl::map([=] {
 		return config->get<std::vector<QString>>(
 			u"gif_search_emojies"_q,
diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp
index 9cfec19f5..bd8d6cb12 100644
--- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp
+++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp
@@ -2347,6 +2347,7 @@ void SetupRestrictionView(
 			});
 			state->label = makeLabel(value.text, st->premiumRequired.label);
 		}
+		state->updateGeometries();
 	}, widget->lifetime());
 
 	widget->sizeValue(
diff --git a/Telegram/SourceFiles/settings/business/settings_away_message.cpp b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
index 69ac74bee..739d12d2a 100644
--- a/Telegram/SourceFiles/settings/business/settings_away_message.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
@@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "settings/business/settings_shortcut_messages.h"
 #include "ui/boxes/choose_date_time.h"
 #include "ui/text/text_utilities.h"
+#include "ui/toast/toast.h"
 #include "ui/widgets/buttons.h"
 #include "ui/widgets/checkbox.h"
 #include "ui/wrap/slide_wrap.h"
@@ -43,6 +44,8 @@ private:
 	void setupContent(not_null<Window::SessionController*> controller);
 	void save();
 
+	rpl::variable<bool> _canHave;
+	rpl::event_stream<> _deactivateOnAttempt;
 	rpl::variable<Data::BusinessRecipients> _recipients;
 	rpl::variable<Data::AwaySchedule> _schedule;
 	rpl::variable<bool> _enabled;
@@ -231,13 +234,35 @@ void AwayMessage::setupContent(
 		.aboutMargins = st::peerAppearanceCoverLabelMargin,
 	});
 
+	const auto session = &controller->session();
+	_canHave = rpl::combine(
+		ShortcutsCountValue(session),
+		ShortcutsLimitValue(session),
+		ShortcutExistsValue(session, u"away"_q),
+		(_1 < _2) || _3);
+
 	Ui::AddSkip(content);
 	const auto enabled = content->add(object_ptr<Ui::SettingsButton>(
 		content,
 		tr::lng_away_enable(),
 		st::settingsButtonNoIcon
-	))->toggleOn(rpl::single(!disabled));
+	))->toggleOn(rpl::single(
+		!disabled
+	) | rpl::then(rpl::merge(
+		_canHave.value() | rpl::filter(!_1),
+		_deactivateOnAttempt.events() | rpl::map_to(false)
+	)));
+
 	_enabled = enabled->toggledValue();
+	_enabled.value() | rpl::filter(_1) | rpl::start_with_next([=] {
+		if (!_canHave.current()) {
+			controller->showToast({
+				.text = tr::lng_away_limit_reached(tr::now),
+				.adaptive = true,
+			});
+			_deactivateOnAttempt.fire({});
+		}
+	}, lifetime());
 
 	const auto wrap = content->add(
 		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
@@ -254,11 +279,21 @@ void AwayMessage::setupContent(
 			object_ptr<Ui::VerticalLayout>(inner)));
 	const auto createInner = createWrap->entity();
 	Ui::AddSkip(createInner);
-	const auto create = createInner->add(object_ptr<Ui::SettingsButton>(
+	const auto create = AddButtonWithLabel(
 		createInner,
-		tr::lng_away_create(),
-		st::settingsButtonLightNoIcon
-	));
+		rpl::conditional(
+			ShortcutExistsValue(session, u"away"_q),
+			tr::lng_business_edit_messages(),
+			tr::lng_away_create()),
+		ShortcutMessagesCountValue(
+			session,
+			u"away"_q
+		) | rpl::map([=](int count) {
+			return count
+				? tr::lng_forum_messages(tr::now, lt_count, count)
+				: QString();
+		}),
+		st::settingsButtonLightNoIcon);
 	create->setClickedCallback([=] {
 		const auto owner = &controller->session().data();
 		const auto id = owner->shortcutMessages().emplaceShortcut("away");
diff --git a/Telegram/SourceFiles/settings/business/settings_greeting.cpp b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
index 96b2615c4..43cba0763 100644
--- a/Telegram/SourceFiles/settings/business/settings_greeting.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
@@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "settings/business/settings_recipients_helper.h"
 #include "ui/layers/generic_box.h"
 #include "ui/text/text_utilities.h"
+#include "ui/toast/toast.h"
 #include "ui/widgets/box_content_divider.h"
 #include "ui/widgets/buttons.h"
 #include "ui/widgets/vertical_drum_picker.h"
@@ -53,6 +54,8 @@ private:
 	Ui::RoundRect _bottomSkipRounding;
 
 	rpl::variable<Data::BusinessRecipients> _recipients;
+	rpl::variable<bool> _canHave;
+	rpl::event_stream<> _deactivateOnAttempt;
 	rpl::variable<int> _noActivityDays;
 	rpl::variable<bool> _enabled;
 
@@ -198,14 +201,35 @@ void Greeting::setupContent(
 		.aboutMargins = st::peerAppearanceCoverLabelMargin,
 	});
 
+	const auto session = &controller->session();
+	_canHave = rpl::combine(
+		ShortcutsCountValue(session),
+		ShortcutsLimitValue(session),
+		ShortcutExistsValue(session, u"hello"_q),
+		(_1 < _2) || _3);
+
 	Ui::AddSkip(content);
 	const auto enabled = content->add(object_ptr<Ui::SettingsButton>(
 		content,
 		tr::lng_greeting_enable(),
 		st::settingsButtonNoIcon
-	))->toggleOn(rpl::single(!disabled));
+	))->toggleOn(rpl::single(
+		!disabled
+	) | rpl::then(rpl::merge(
+		_canHave.value() | rpl::filter(!_1),
+		_deactivateOnAttempt.events() | rpl::map_to(false)
+	)));
 
 	_enabled = enabled->toggledValue();
+	_enabled.value() | rpl::filter(_1) | rpl::start_with_next([=] {
+		if (!_canHave.current()) {
+			controller->showToast({
+				.text = tr::lng_greeting_limit_reached(tr::now),
+				.adaptive = true,
+			});
+			_deactivateOnAttempt.fire({});
+		}
+	}, lifetime());
 
 	Ui::AddSkip(content);
 
@@ -237,11 +261,21 @@ void Greeting::setupContent(
 			object_ptr<Ui::VerticalLayout>(inner)));
 	const auto createInner = createWrap->entity();
 	Ui::AddSkip(createInner);
-	const auto create = createInner->add(object_ptr<Ui::SettingsButton>(
+	const auto create = AddButtonWithLabel(
 		createInner,
-		tr::lng_greeting_create(),
-		st::settingsButtonLightNoIcon
-	));
+		rpl::conditional(
+			ShortcutExistsValue(session, u"hello"_q),
+			tr::lng_business_edit_messages(),
+			tr::lng_greeting_create()),
+		ShortcutMessagesCountValue(
+			session,
+			u"hello"_q
+		) | rpl::map([=](int count) {
+			return count
+				? tr::lng_forum_messages(tr::now, lt_count, count)
+				: QString();
+		}),
+		st::settingsButtonLightNoIcon);
 	create->setClickedCallback([=] {
 		const auto owner = &controller->session().data();
 		const auto id = owner->shortcutMessages().emplaceShortcut("hello");
diff --git a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
index aac5f918c..53fb228ba 100644
--- a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/business/data_shortcut_messages.h"
 #include "data/data_session.h"
 #include "lang/lang_keys.h"
+#include "main/main_account.h"
 #include "main/main_session.h"
 #include "settings/business/settings_recipients_helper.h"
 #include "settings/business/settings_shortcut_messages.h"
@@ -42,6 +43,8 @@ private:
 	void setupContent(not_null<Window::SessionController*> controller);
 	void save();
 
+	rpl::variable<int> _count;
+
 };
 
 QuickReplies::QuickReplies(
@@ -75,29 +78,47 @@ void QuickReplies::setupContent(
 		.about = tr::lng_replies_about(Ui::Text::WithEntities),
 		.aboutMargins = st::peerAppearanceCoverLabelMargin,
 	});
-
 	Ui::AddSkip(content);
-	const auto add = content->add(object_ptr<Ui::SettingsButton>(
-		content,
-		tr::lng_replies_add(),
-		st::settingsButtonNoIcon
-	));
+
+	const auto addWrap = content->add(
+		object_ptr<Ui::VerticalLayout>(content));
 
 	const auto owner = &controller->session().data();
 	const auto messages = &owner->shortcutMessages();
 
-	add->setClickedCallback([=] {
-		const auto submit = [=](QString name, Fn<void()> close) {
-			const auto id = messages->emplaceShortcut(name);
-			showOther(ShortcutMessagesId(id));
-			close();
-		};
-		controller->show(
-			Box(EditShortcutNameBox, QString(), crl::guard(this, submit)));
-	});
+	rpl::combine(
+		_count.value(),
+		ShortcutsLimitValue(&controller->session())
+	) | rpl::start_with_next([=](int count, int limit) {
+		while (addWrap->count()) {
+			delete addWrap->widgetAt(0);
+		}
+		if (count < limit) {
+			const auto add = addWrap->add(object_ptr<Ui::SettingsButton>(
+				addWrap,
+				tr::lng_replies_add(),
+				st::settingsButtonNoIcon
+			));
 
-	const auto dividerWrap = content->add(
-		object_ptr<Ui::VerticalLayout>(content));
+			add->setClickedCallback([=] {
+				const auto submit = [=](QString name, Fn<void()> close) {
+					const auto id = messages->emplaceShortcut(name);
+					showOther(ShortcutMessagesId(id));
+					close();
+				};
+				controller->show(
+					Box(EditShortcutNameBox, QString(), crl::guard(this, submit)));
+			});
+			if (count > 0) {
+				AddSkip(addWrap);
+				AddDivider(addWrap);
+				AddSkip(addWrap);
+			}
+		}
+		if (const auto width = content->width()) {
+			content->resizeToWidth(width);
+		}
+	}, lifetime());
 
 	const auto inner = content->add(
 		object_ptr<Ui::VerticalLayout>(content));
@@ -108,7 +129,8 @@ void QuickReplies::setupContent(
 
 		const auto &shortcuts = messages->shortcuts();
 		auto i = 0;
-		for (const auto &[_, shortcut] : shortcuts.list) {
+		for (const auto &[_, shortcut]
+			: shortcuts.list | ranges::views::reverse) {
 			if (!shortcut.count) {
 				continue;
 			}
@@ -132,18 +154,7 @@ void QuickReplies::setupContent(
 		while (old--) {
 			delete inner->widgetAt(0);
 		}
-		if (!inner->count()) {
-			while (dividerWrap->count()) {
-				delete dividerWrap->widgetAt(0);
-			}
-		} else if (!dividerWrap->count()) {
-			AddSkip(dividerWrap);
-			AddDivider(dividerWrap);
-			AddSkip(dividerWrap);
-		}
-		if (const auto width = content->width()) {
-			content->resizeToWidth(width);
-		}
+		_count = inner->count();
 	}, content->lifetime());
 
 	Ui::ResizeFitChild(this, content);
diff --git a/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp b/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
index 83c11a9cd..5787bb234 100644
--- a/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
@@ -9,10 +9,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "boxes/filters/edit_filter_chats_list.h"
 #include "boxes/filters/edit_filter_chats_preview.h"
+#include "data/business/data_shortcut_messages.h"
 #include "data/data_session.h"
 #include "data/data_user.h"
 #include "history/history.h"
 #include "lang/lang_keys.h"
+#include "main/main_account.h"
+#include "main/main_app_config.h"
+#include "main/main_session.h"
 #include "settings/settings_common.h"
 #include "ui/widgets/checkbox.h"
 #include "ui/wrap/slide_wrap.h"
@@ -291,4 +295,83 @@ void AddBusinessRecipientsSelector(
 	});
 }
 
+int ShortcutsCount(not_null<Main::Session*> session) {
+	const auto &shortcuts = session->data().shortcutMessages().shortcuts();
+	auto result = 0;
+	for (const auto &[_, shortcut] : shortcuts.list) {
+		if (shortcut.count > 0) {
+			++result;
+		}
+	}
+	return result;
+}
+
+rpl::producer<int> ShortcutsCountValue(not_null<Main::Session*> session) {
+	const auto messages = &session->data().shortcutMessages();
+	return rpl::single(rpl::empty) | rpl::then(
+		messages->shortcutsChanged()
+	) | rpl::map([=] {
+		return ShortcutsCount(session);
+	});
+}
+
+int ShortcutMessagesCount(
+		not_null<Main::Session*> session,
+		const QString &name) {
+	const auto &shortcuts = session->data().shortcutMessages().shortcuts();
+	for (const auto &[_, shortcut] : shortcuts.list) {
+		if (shortcut.name == name) {
+			return shortcut.count;
+		}
+	}
+	return 0;
+}
+
+rpl::producer<int> ShortcutMessagesCountValue(
+		not_null<Main::Session*> session,
+		const QString &name) {
+	const auto messages = &session->data().shortcutMessages();
+	return rpl::single(rpl::empty) | rpl::then(
+		messages->shortcutsChanged()
+	) | rpl::map([=] {
+		return ShortcutMessagesCount(session, name);
+	});
+}
+
+bool ShortcutExists(not_null<Main::Session*> session, const QString &name) {
+	return ShortcutMessagesCount(session, name) > 0;
+}
+
+rpl::producer<bool> ShortcutExistsValue(
+		not_null<Main::Session*> session,
+		const QString &name) {
+	return ShortcutMessagesCountValue(session, name)
+		| rpl::map(rpl::mappers::_1 > 0);
+}
+
+int ShortcutsLimit(not_null<Main::Session*> session) {
+	const auto appConfig = &session->account().appConfig();
+	return appConfig->get<int>("quick_replies_limit", 100);
+}
+
+rpl::producer<int> ShortcutsLimitValue(not_null<Main::Session*> session) {
+	const auto appConfig = &session->account().appConfig();
+	return appConfig->value() | rpl::map([=] {
+		return ShortcutsLimit(session);
+	});
+}
+
+int ShortcutMessagesLimit(not_null<Main::Session*> session) {
+	const auto appConfig = &session->account().appConfig();
+	return appConfig->get<int>("quick_reply_messages_limit", 20);
+}
+
+rpl::producer<int> ShortcutMessagesLimitValue(
+		not_null<Main::Session*> session) {
+	const auto appConfig = &session->account().appConfig();
+	return appConfig->value() | rpl::map([=] {
+		return ShortcutMessagesLimit(session);
+	});
+}
+
 } // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_recipients_helper.h b/Telegram/SourceFiles/settings/business/settings_recipients_helper.h
index 60efd7425..e05f702e6 100644
--- a/Telegram/SourceFiles/settings/business/settings_recipients_helper.h
+++ b/Telegram/SourceFiles/settings/business/settings_recipients_helper.h
@@ -71,4 +71,26 @@ void AddBusinessRecipientsSelector(
 	not_null<Ui::VerticalLayout*> container,
 	BusinessRecipientsSelectorDescriptor &&descriptor);
 
+[[nodiscard]] int ShortcutsCount(not_null<Main::Session*> session);
+[[nodiscard]] rpl::producer<int> ShortcutsCountValue(
+	not_null<Main::Session*> session);
+[[nodiscard]] int ShortcutMessagesCount(
+	not_null<Main::Session*> session,
+	const QString &name);
+[[nodiscard]] rpl::producer<int> ShortcutMessagesCountValue(
+	not_null<Main::Session*> session,
+	const QString &name);
+[[nodiscard]] bool ShortcutExists(
+	not_null<Main::Session*> session,
+	const QString &name);
+[[nodiscard]] rpl::producer<bool> ShortcutExistsValue(
+	not_null<Main::Session*> session,
+	const QString &name);
+[[nodiscard]] int ShortcutsLimit(not_null<Main::Session*> session);
+[[nodiscard]] rpl::producer<int> ShortcutsLimitValue(
+	not_null<Main::Session*> session);
+[[nodiscard]] int ShortcutMessagesLimit(not_null<Main::Session*> session);
+[[nodiscard]] rpl::producer<int> ShortcutMessagesLimitValue(
+	not_null<Main::Session*> session);
+
 } // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
index b72acab4b..91ba0c63f 100644
--- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
@@ -35,6 +35,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "inline_bots/inline_bot_result.h"
 #include "lang/lang_keys.h"
 #include "lang/lang_numbers_animation.h"
+#include "main/main_account.h"
+#include "main/main_app_config.h"
 #include "main/main_session.h"
 #include "menu/menu_send.h"
 #include "settings/business/settings_quick_replies.h"
@@ -259,6 +261,7 @@ private:
 	rpl::variable<BusinessShortcutId> _shortcutId;
 	rpl::variable<QString> _shortcut;
 	rpl::variable<Container> _container;
+	rpl::variable<int> _count;
 	std::shared_ptr<Ui::ChatStyle> _style;
 	std::shared_ptr<Ui::ChatTheme> _theme;
 	QPointer<ListWidget> _inner;
@@ -618,9 +621,22 @@ void ShortcutMessages::setupComposeControls() {
 	};
 	_composeControls->setCurrentDialogsEntryState(state);
 
+	auto writeRestriction = rpl::combine(
+		_count.value(),
+		ShortcutMessagesLimitValue(_session)
+	) | rpl::map([=](int count, int limit) {
+		return (count >= limit)
+			? Controls::WriteRestriction{
+				.text = tr::lng_business_limit_reached(
+					tr::now,
+					lt_count,
+					limit),
+				.type = Controls::WriteRestrictionType::Rights,
+			} : Controls::WriteRestriction();
+	});
 	_composeControls->setHistory({
 		.history = _history.get(),
-		.writeRestriction = rpl::single(Controls::WriteRestriction()),
+		.writeRestriction = std::move(writeRestriction),
 	});
 
 	_composeControls->cancelRequests(
@@ -831,7 +847,11 @@ rpl::producer<Data::MessagesSlice> ShortcutMessages::listSource(
 		) | rpl::map([=] {
 			return messages->list(shortcutId);
 		});
-	}) | rpl::flatten_latest();
+	}) | rpl::flatten_latest(
+	) | rpl::after_next([=](const Data::MessagesSlice &slice) {
+		_count = slice.fullCount.value_or(
+			messages->count(_shortcutId.current()));
+	});
 }
 
 bool ShortcutMessages::listAllowsMultiSelect() {
diff --git a/Telegram/SourceFiles/settings/settings_main.cpp b/Telegram/SourceFiles/settings/settings_main.cpp
index 4dd57db8f..c746a881a 100644
--- a/Telegram/SourceFiles/settings/settings_main.cpp
+++ b/Telegram/SourceFiles/settings/settings_main.cpp
@@ -446,10 +446,6 @@ void SetupPremium(
 		button->addClickHandler([=] {
 			controller->showGiftPremiumsBox(u"gift"_q);
 		});
-		constexpr auto kNewExpiresAt = int(1735689600);
-		if (base::unixtime::now() < kNewExpiresAt) {
-			Ui::NewBadge::AddToRight(button);
-		}
 	}
 	Ui::AddSkip(container);
 }
diff --git a/Telegram/lib_ui b/Telegram/lib_ui
index 333587d95..14794d222 160000
--- a/Telegram/lib_ui
+++ b/Telegram/lib_ui
@@ -1 +1 @@
-Subproject commit 333587d95edefcae1ebaf8838d3f499639fc2de8
+Subproject commit 14794d22210cb21b82db20aa55b1f3c8733b5fbd

From cf8aaf5f9d20531731dd087ee8e3bc6b9d0e9e46 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 1 Mar 2024 13:17:41 +0400
Subject: [PATCH 064/108] Save away / greeting settings.

---
 .../icons/folders/folder_existing_chats.png   | Bin 0 -> 592 bytes
 .../folders/folder_existing_chats@2x.png      | Bin 0 -> 1101 bytes
 .../folders/folder_existing_chats@3x.png      | Bin 0 -> 1628 bytes
 .../icons/folders/folder_new_chats.png        | Bin 0 -> 598 bytes
 .../icons/folders/folder_new_chats@2x.png     | Bin 0 -> 1111 bytes
 .../icons/folders/folder_new_chats@3x.png     | Bin 0 -> 1598 bytes
 Telegram/Resources/langs/lang.strings         |   2 ++
 .../business/settings_away_message.cpp        |  18 +++++++++++++++++-
 .../settings/business/settings_greeting.cpp   |   4 +++-
 .../business/settings_recipients_helper.cpp   |  14 +++++++++++++-
 .../business/settings_recipients_helper.h     |   4 ++++
 Telegram/SourceFiles/window/window.style      |   4 ++--
 12 files changed, 41 insertions(+), 5 deletions(-)
 create mode 100644 Telegram/Resources/icons/folders/folder_existing_chats.png
 create mode 100644 Telegram/Resources/icons/folders/folder_existing_chats@2x.png
 create mode 100644 Telegram/Resources/icons/folders/folder_existing_chats@3x.png
 create mode 100644 Telegram/Resources/icons/folders/folder_new_chats.png
 create mode 100644 Telegram/Resources/icons/folders/folder_new_chats@2x.png
 create mode 100644 Telegram/Resources/icons/folders/folder_new_chats@3x.png

diff --git a/Telegram/Resources/icons/folders/folder_existing_chats.png b/Telegram/Resources/icons/folders/folder_existing_chats.png
new file mode 100644
index 0000000000000000000000000000000000000000..e54a10425911300ae4738dcd3daa509860f1edb6
GIT binary patch
literal 592
zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy
zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlgfY?r5tV~B;|
z+YsA+#z2wOV;2@GsHk*!Y5ZYRocM$LRi=oD5Lc&*sut@~7su!F9s#~fAE;|&aP+P}
za{SH7eWuo*i`>g@pY=2TKCk+G<@dSY({!fU|L#b$7Irn1;AwMG^ql0keDbPQX>*rV
zs0c~&u(cm%O!@YvY<JvxaSj$A%b0^{n@y#9Pe1(>WU#0Ic=Fb$tx;=bx;@$tFKqNu
zy_+{Z;nl(#JAXk=o$04vcGm2P(c=_iJ)34cZ_U%V_0tz!xbgVohTCsXrUWfk7KnKC
z`RA7xzas=#`i}=|h@5(AbpPk-&UfFf^}?55b`<D8KJ(gjm)md4Hb#8-q-JTe>u%n~
z43X(!(^B_oPxZ2$?|(~Fd6wVuSiS3Ct5|Jj`cx=B*`e&lHF0Z{Zi(4!-!SQ3x0Zmt
zaq27dGxpy<oN!^&O`UDW9(XN1#QjhwLHN{%)%V}$o6Vlyv@2@ut+!@=T+1FTIrrRH
zd+qh??YGZzry9-tS<`p&Z=L;W%em(YEO@LZBuUQbVDpuATp98x^xhM`_QheV6Mgow
zbT7ZW@*D5}2f_bhRK&Vfg*g8#d@yB^Mu5Ip_YRj0r8aW=XFlHl<?R7>qsiR+S1)<i
P4T?=qS3j3^P6<r_W+m^$

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/folders/folder_existing_chats@2x.png b/Telegram/Resources/icons/folders/folder_existing_chats@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..e3a73f7e1fbf9da405d8efc9527eb5ad98e1875c
GIT binary patch
literal 1101
zcmV-T1hV^yP)<h;3K|Lk000e1NJLTq001xm001xu0ssI2*kEqZ00001b5ch_0Itp)
z=>Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@
z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NFw@E}nR9Fe^SWPH&Q5a?rLNrr;
z(u^T8ni4w`MM_eWg|e8&l!ekHMY6?g6pLBSf(12_2+2mwLdi5mHf&goqD+3GQH1jI
zeUEOPZs**&_uM=4t-d>p>AmlH-sgGmd(M5&xi>9M|LGObEAXFHAYH@#78DdzRaKRg
zloS^ie}8{JJUrar-$$d-kB<)xY}!MrtE(p_CJqk|KR-YJFu(Wr_fRM_K0aPnR;FEp
zGqS$Eesy&f#bDBqaG*|eb90rIm7L`2U>O-1Q&Ur4Utbc+vaYwcx52?dbz+KWqtO@+
zhh=pY1~)f1v$L}mg}4A%xyQ%H3W6NN+uPfjnVDRYG9ZJ&u)n|0A;$$-TwIjV;u`XL
zy<Ae<pw7-tE@4R!y36zPGq)BO@bdDKb1@m%ocjCw3kwTbe=;64Qm50Yk>T|8l<Tfl
zg0{A{v?NqsUanGz7mik*p8mU2Wo2c_WJXV!)oPVNCo`0jlT%t+3I><U<?(nDQPBxm
zES5yj<o#x|86<aicS}o4larH@gmgkbKR=SVlsK^)g6I1B8vodldU|@4X<J%aco&6w
z92yETAj=fZg`E((LO9kyNI7x!h}YLwoScM&glIIEk&zK)rlX@H^*65^v9huvXk(@b
zIv$Vb<>iTUqAP^4_4RczB31VC^0GkXa5xBOQBhHAYb&{wHuCfHZ*OnOO{By)J3ABh
ze6*sgt1B_*@bIv79%Apnz<@X=NgGctsS5kh`2BvdbV3^yPBIS?J~J~T$e=vziEny(
zI$;vx0W{W&iwi=k*;rUu5OxfZqd~JgLqkJiT{#;ajEz_22?PQ->j@e*o9*f8i4C{g
z?Q%(wG19(R3Lekn<70PsH(|rO5w(^D5=;WYC>VWxeLTV}2C3ln4KD~p0JS+kKWCBf
zK!v~*45~dI930fv)*^mwZ4KRwWsk*T!C=tsc2kY6x3~A@<%Pw=g9T2OmqIpUV`GE@
zD+#RvFKFTzG_<<9y7u<=wzf7HCX-1#D%g#UjXOI#goiS41Hx&94V3WA&(CjcY~X?H
z=;$D;QUWLCLWNKjoDb1{s3gRfXq8gYX}?Ff<bM?v6<u9jH8nM;7HXE8o6E!5+}s?A
zM0k*@F<1)dF^N_#DGc>>U~g|vwUXL^rluyGymohY@jT<OadUHXa&m%~*U`~Y)DP!l
ze6$dMDVUv|r6f`_WfZCWB|03YwV|N_yEu~e`F#KMm@1!)KB`whuYg_wrUHKfWV<&?
TEVi1~00000NkvXXu0mjfI9=Yn

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/folders/folder_existing_chats@3x.png b/Telegram/Resources/icons/folders/folder_existing_chats@3x.png
new file mode 100644
index 0000000000000000000000000000000000000000..2b37db754e2f5615b839f1518f866d437ee63da5
GIT binary patch
literal 1628
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
ac;G+Cna;S=%MO?T0000<MNUMnLSTX*Pw!Fy

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/folders/folder_new_chats.png b/Telegram/Resources/icons/folders/folder_new_chats.png
new file mode 100644
index 0000000000000000000000000000000000000000..03c8380d448c5b9f8afa1458deb63944aa84d48f
GIT binary patch
literal 598
zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy
zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlgf?0~0>V~B;|
z+bL(e9Rnqf-7Vw|6T8%MqeCOwHSuV?qN+9bQ~7{z|JbdMo^m^R>bOw0{6<I4jT1fv
zCe15c_V~=+<EF1O&pGwa|6aN0`0qHatMhGNO*gOJKmYVoCoNV-4z}h-hlKt2-<Rzc
zFAEe&-Waho=w^=D_TQ1~ud7YwocXnC?u5@j=Xj`0Y5LGQ`D9DT<?C*X8U58JFJyUC
zU?I`#HtDNcXV3A+XVWe}Eb#bIV#WILXN}JF*I(n-PoE&~_^@C{fX0febL7uGEjnl*
z)q7*X>#tUF{gI!RK79ZEch2)Qc5?k`n{Or<@UStTD&9HAg!R4hM2@u&y0W%*O?+r|
zGR0{2*+sQ}Tmg3TpZ|)yk+s!Ej{oChwm)l%cAlvc`BJp=P1)|-Z_74DTv+C^%5S;w
z)l-ii*9A1|E}YuGlDFO2t6o#Z`A^x-yLmmq58XcX&&w-Y88Ru{UO9u^pJ#vho8LKR
z)z4Ba<@omBKku)&C2aLZwspt$EV{TNsOxIhQMTf<ERW7FiO{)~V^(=8`A^hZvDHg1
zzYJ(G^;~|rY`3ilSEP<uuiNIRwN-oNe$74q{F8`4W6<);Grg8>NPb(k8>sZ3z8#}R
W!@nbJHfQBPaq8*n=d#Wzp$PyA`~WHd

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/folders/folder_new_chats@2x.png b/Telegram/Resources/icons/folders/folder_new_chats@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..f91df76357aeb853e9f883ef2970c5594c19a916
GIT binary patch
literal 1111
zcmV-d1gQIoP)<h;3K|Lk000e1NJLTq001xm001xu0ssI2*kEqZ00001b5ch_0Itp)
z=>Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@
z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NF!AV3xR9Fe^SWPH&Q5a^7L_{+3
z7x^htEQFGuqKJiTWF^T$l&okLO0rOFDa_6aD-kJ4i3rI;3Dbn3{Qv(<3E%hB;dDFa
z+~eNsT6}$T7c=j9&U4=PIq#gG_l}Q`_wi=Hn}Po<1HM0>ub-b^L_|bzaPZ^f<K5le
z>+37y(`vQW*VmVqm&e4!+}zwOEiDZX4};0jj5nbmKR>^_yL)wY_5A$&{r&yV*8|(>
zbRstOo{^EUxw-k(|M`7#a#B%IK{>{au&}Vs&d#f=D<*)li9CgdhLRXjQBi7SNXM*<
zOY!&j@9OHhzrR<;7TQBYLp03P)Rd4zK;$woFp!dU7>S9A>+9<h*UGNO#zx{gIXPL0
z3+<Yk8b*hOY4gppva+qMt(=@3zDtO>TrPrVWn~EoN|5JRmEF9ZmzOs&F%c9L#CNG7
z4-XHdD<B|1jYDysBg;^Fdiu=Fj4m~ZguPATVTsVdYK9ih=U9uyvazut{Nd#wjsxNX
z9uQALetv$Ajg3{)%Hqw<%@SXduI=q@>Z_ojKzT|rqgq;8DDxX5BqRh!Dl=Ytv$C?1
z2EfET9uG72<Kv^hzu(ta^F2{pTg$l6)!f_L!<49|)YR0;$w?yX?d@%OdAX#dg!XDO
zu$FZx;K|d|(>P0&!LX#l!^3f}DM3v(wiPZ&K89XoadFXRvuO$`C#9vO>5Z+%zrDS+
z+wDjU6A`6BYiny?Umt^`DOgserKNLob3D~Bf{nJava-Lwe|dQs9UYyLl9HL3iA9Dw
zq^PLqd$)CUb!sy<7vI_0;nfIEYI^qbY;zI(pu9tYpwrV+p7$Hb@$oV5Rv_r`@Q~;I
z2I6+RdA9;Vs7g_9@Kn>0`T2R?r$7)$sPuTMxd?XL!oq@9pGiqcrkstET{$o?pj8dw
z4Gs=U-2Jc%F9JFW4<ZP4mX?y<n6kFEHht+K?5e6N)CtByN=B`%tr@vuiKgD(UWsc1
zUAw!xc+EAE9x|r7y4pZgwIN4GNAdCTay8;@Psilu=2H8I#_a5Dc6PS#Fx2W9@9^>}
zT`dy~{_^tD-rlaGU{w>?)YQbd*4xDP#wv`Aj8qGu14nxCn!+m>8yhPwF7D~+!S%xT
z66E^&dVGAmp`n2<&AwG2D9|t_E-nr)^C+g2;|~rFIyyQ=M@K1$3N;}i0e^AeLE(FG
zae;gA?CcChKF$kDGr<@c8KJYsGITf`g@uJCECLPE#2G?;iGMtvo}Msid}{FHrFb*o
d&A>mCfxmLC0o@lrjs5@t002ovPDHLkV1n&74>SM(

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/folders/folder_new_chats@3x.png b/Telegram/Resources/icons/folders/folder_new_chats@3x.png
new file mode 100644
index 0000000000000000000000000000000000000000..d379a9e4b0dace8fd4f1a55e6c8d122867f2360a
GIT binary patch
literal 1598
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

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index add970d06..c78969caa 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -2238,6 +2238,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_away_schedule_custom" = "Custom Schedule";
 "lng_away_custom_start" = "Start Time";
 "lng_away_custom_end" = "End Time";
+"lng_away_offline_only" = "Only if Offline";
+"lng_away_offline_only_about" = "Don't send the away message if you've recently been online.";
 "lng_away_recipients" = "Recipients";
 "lng_away_select" = "Select chats or entire chat categories for sending an away message.";
 "lng_away_empty_title" = "New Away Message";
diff --git a/Telegram/SourceFiles/settings/business/settings_away_message.cpp b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
index 739d12d2a..ae600e0de 100644
--- a/Telegram/SourceFiles/settings/business/settings_away_message.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
@@ -48,6 +48,7 @@ private:
 	rpl::event_stream<> _deactivateOnAttempt;
 	rpl::variable<Data::BusinessRecipients> _recipients;
 	rpl::variable<Data::AwaySchedule> _schedule;
+	rpl::variable<bool> _offlineOnly;
 	rpl::variable<bool> _enabled;
 
 };
@@ -311,6 +312,18 @@ void AwayMessage::setupContent(
 	});
 	Ui::AddSkip(inner);
 	Ui::AddDivider(inner);
+	Ui::AddSkip(inner);
+
+	const auto offlineOnly = inner->add(
+		object_ptr<Ui::SettingsButton>(
+			inner,
+			tr::lng_away_offline_only(),
+			st::settingsButtonNoIcon)
+	)->toggleOn(rpl::single(current.offlineOnly));
+	_offlineOnly = offlineOnly->toggledValue();
+
+	Ui::AddSkip(inner);
+	Ui::AddDividerText(inner, tr::lng_away_offline_only_about());
 
 	AddBusinessRecipientsSelector(inner, {
 		.controller = controller,
@@ -327,10 +340,13 @@ void AwayMessage::setupContent(
 }
 
 void AwayMessage::save() {
-	controller()->session().data().businessInfo().saveAwaySettings(
+	const auto session = &controller()->session();
+	session->data().businessInfo().saveAwaySettings(
 		_enabled.current() ? Data::AwaySettings{
 			.recipients = _recipients.current(),
 			.schedule = _schedule.current(),
+			.shortcutId = LookupShortcutId(session, u"away"_q),
+			.offlineOnly = _offlineOnly.current(),
 		} : Data::AwaySettings());
 }
 
diff --git a/Telegram/SourceFiles/settings/business/settings_greeting.cpp b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
index 43cba0763..e16cc6477 100644
--- a/Telegram/SourceFiles/settings/business/settings_greeting.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
@@ -326,10 +326,12 @@ void Greeting::setupContent(
 }
 
 void Greeting::save() {
-	controller()->session().data().businessInfo().saveGreetingSettings(
+	const auto session = &controller()->session();
+	session->data().businessInfo().saveGreetingSettings(
 		_enabled.current() ? Data::GreetingSettings{
 			.recipients = _recipients.current(),
 			.noActivityDays = _noActivityDays.current(),
+			.shortcutId = LookupShortcutId(session, u"hello"_q),
 		} : Data::GreetingSettings());
 }
 
diff --git a/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp b/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
index 5787bb234..960c354b2 100644
--- a/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
@@ -280,7 +280,7 @@ void AddBusinessRecipientsSelector(
 		});
 	}, lifetime);
 
-	SetupBusinessChatsPreview(includeInner, excluded);
+	SetupBusinessChatsPreview(includeInner, included);
 
 	includeWrap->toggleOn(data->value(
 	) | rpl::map([](const Data::BusinessRecipients &value) {
@@ -374,4 +374,16 @@ rpl::producer<int> ShortcutMessagesLimitValue(
 	});
 }
 
+BusinessShortcutId LookupShortcutId(
+		not_null<Main::Session*> session,
+		const QString &name) {
+	const auto messages = &session->data().shortcutMessages();
+	for (const auto &[id, shortcut] : messages->shortcuts().list) {
+		if (shortcut.name == name) {
+			return id;
+		}
+	}
+	return {};
+}
+
 } // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_recipients_helper.h b/Telegram/SourceFiles/settings/business/settings_recipients_helper.h
index e05f702e6..f4432ea3b 100644
--- a/Telegram/SourceFiles/settings/business/settings_recipients_helper.h
+++ b/Telegram/SourceFiles/settings/business/settings_recipients_helper.h
@@ -93,4 +93,8 @@ void AddBusinessRecipientsSelector(
 [[nodiscard]] rpl::producer<int> ShortcutMessagesLimitValue(
 	not_null<Main::Session*> session);
 
+[[nodiscard]] BusinessShortcutId LookupShortcutId(
+	not_null<Main::Session*> session,
+	const QString &name);
+
 } // namespace Settings
diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style
index 645c78285..8afc8d243 100644
--- a/Telegram/SourceFiles/window/window.style
+++ b/Telegram/SourceFiles/window/window.style
@@ -303,8 +303,8 @@ windowFilterTypeBots: icon {{ "folders/folders_type_bots", historyPeerUserpicFg
 windowFilterTypeNoMuted: icon {{ "folders/folders_type_muted", historyPeerUserpicFg }};
 windowFilterTypeNoArchived: icon {{ "folders/folders_type_archived", historyPeerUserpicFg }};
 windowFilterTypeNoRead: icon {{ "folders/folders_type_read", historyPeerUserpicFg }};
-windowFilterTypeNewChats: icon {{ "folders/folders_unread", historyPeerUserpicFg }};
-windowFilterTypeExistingChats: windowFilterTypeNoRead;
+windowFilterTypeNewChats: icon {{ "folders/folder_new_chats", historyPeerUserpicFg }};
+windowFilterTypeExistingChats: icon {{ "folders/folder_existing_chats", historyPeerUserpicFg }};
 windowFilterChatsSectionSubtitleHeight: 28px;
 windowFilterChatsSectionSubtitle: FlatLabel(defaultFlatLabel) {
 	style: TextStyle(defaultTextStyle) {

From a47c6f9c9a24877ae0b4bdcbf6c29a6ffba5dde2 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 1 Mar 2024 17:28:51 +0400
Subject: [PATCH 065/108] Show errors on business info savings.

---
 .../data/business/data_business_chatbots.cpp  |   2 +-
 .../data/business/data_business_chatbots.h    |   2 +-
 .../data/business/data_business_info.cpp      |  63 +++++++---
 .../data/business/data_business_info.h        |   8 +-
 .../business/settings_away_message.cpp        |  17 ++-
 .../settings/business/settings_chatbots.cpp   |  14 ++-
 .../settings/business/settings_greeting.cpp   | 110 +++++-------------
 .../business/settings_quick_replies.cpp       |  10 +-
 .../business/settings_working_hours.cpp       |   4 +-
 9 files changed, 111 insertions(+), 119 deletions(-)

diff --git a/Telegram/SourceFiles/data/business/data_business_chatbots.cpp b/Telegram/SourceFiles/data/business/data_business_chatbots.cpp
index 26dd21687..89215a2e0 100644
--- a/Telegram/SourceFiles/data/business/data_business_chatbots.cpp
+++ b/Telegram/SourceFiles/data/business/data_business_chatbots.cpp
@@ -27,7 +27,7 @@ rpl::producer<ChatbotsSettings> Chatbots::value() const {
 	return _settings.value();
 }
 
-void Chatbots::save(ChatbotsSettings settings) {
+void Chatbots::save(ChatbotsSettings settings, Fn<void(QString)> fail) {
 	_settings = settings;
 }
 
diff --git a/Telegram/SourceFiles/data/business/data_business_chatbots.h b/Telegram/SourceFiles/data/business/data_business_chatbots.h
index 13b8a894b..da088c394 100644
--- a/Telegram/SourceFiles/data/business/data_business_chatbots.h
+++ b/Telegram/SourceFiles/data/business/data_business_chatbots.h
@@ -30,7 +30,7 @@ public:
 	[[nodiscard]] rpl::producer<ChatbotsSettings> changes() const;
 	[[nodiscard]] rpl::producer<ChatbotsSettings> value() const;
 
-	void save(ChatbotsSettings settings);
+	void save(ChatbotsSettings settings, Fn<void(QString)> fail);
 
 private:
 	const not_null<Session*> _session;
diff --git a/Telegram/SourceFiles/data/business/data_business_info.cpp b/Telegram/SourceFiles/data/business/data_business_info.cpp
index bd75f4af7..43ef345e6 100644
--- a/Telegram/SourceFiles/data/business/data_business_info.cpp
+++ b/Telegram/SourceFiles/data/business/data_business_info.cpp
@@ -118,20 +118,31 @@ BusinessInfo::BusinessInfo(not_null<Session*> owner)
 
 BusinessInfo::~BusinessInfo() = default;
 
-void BusinessInfo::saveWorkingHours(WorkingHours data) {
-	auto details = _owner->session().user()->businessDetails();
-	if (details.hours == data) {
+void BusinessInfo::saveWorkingHours(
+		WorkingHours data,
+		Fn<void(QString)> fail) {
+	const auto session = &_owner->session();
+	auto details = session->user()->businessDetails();
+	const auto &was = details.hours;
+	if (was == data) {
 		return;
 	}
 
 	using Flag = MTPaccount_UpdateBusinessWorkHours::Flag;
-	_owner->session().api().request(MTPaccount_UpdateBusinessWorkHours(
+	session->api().request(MTPaccount_UpdateBusinessWorkHours(
 		MTP_flags(data ? Flag::f_business_work_hours : Flag()),
 		ToMTP(data)
-	)).send();
+	)).fail([=](const MTP::Error &error) {
+		auto details = session->user()->businessDetails();
+		details.hours = was;
+		session->user()->setBusinessDetails(std::move(details));
+		if (fail) {
+			fail(error.type());
+		}
+	}).send();
 
 	details.hours = std::move(data);
-	_owner->session().user()->setBusinessDetails(std::move(details));
+	session->user()->setBusinessDetails(std::move(details));
 }
 
 void BusinessInfo::applyAwaySettings(AwaySettings data) {
@@ -142,15 +153,25 @@ void BusinessInfo::applyAwaySettings(AwaySettings data) {
 	_awaySettingsChanged.fire({});
 }
 
-void BusinessInfo::saveAwaySettings(AwaySettings data) {
-	if (_awaySettings == data) {
+void BusinessInfo::saveAwaySettings(
+		AwaySettings data,
+		Fn<void(QString)> fail) {
+	const auto &was = _awaySettings;
+	if (was == data) {
 		return;
 	}
 	using Flag = MTPaccount_UpdateBusinessAwayMessage::Flag;
-	_owner->session().api().request(MTPaccount_UpdateBusinessAwayMessage(
+	const auto session = &_owner->session();
+	session->api().request(MTPaccount_UpdateBusinessAwayMessage(
 		MTP_flags(data ? Flag::f_message : Flag()),
 		data ? ToMTP(data) : MTPInputBusinessAwayMessage()
-	)).send();
+	)).fail([=](const MTP::Error &error) {
+		_awaySettings = was;
+		_awaySettingsChanged.fire({});
+		if (fail) {
+			fail(error.type());
+		}
+	}).send();
 
 	_awaySettings = std::move(data);
 	_awaySettingsChanged.fire({});
@@ -176,15 +197,25 @@ void BusinessInfo::applyGreetingSettings(GreetingSettings data) {
 	_greetingSettingsChanged.fire({});
 }
 
-void BusinessInfo::saveGreetingSettings(GreetingSettings data) {
-	if (_greetingSettings == data) {
+void BusinessInfo::saveGreetingSettings(
+		GreetingSettings data,
+		Fn<void(QString)> fail) {
+	const auto &was = _greetingSettings;
+	if (was == data) {
 		return;
 	}
 	using Flag = MTPaccount_UpdateBusinessGreetingMessage::Flag;
-	_owner->session().api().request(MTPaccount_UpdateBusinessGreetingMessage(
-		MTP_flags(data ? Flag::f_message : Flag()),
-		data ? ToMTP(data) : MTPInputBusinessGreetingMessage()
-	)).send();
+	_owner->session().api().request(
+		MTPaccount_UpdateBusinessGreetingMessage(
+			MTP_flags(data ? Flag::f_message : Flag()),
+			data ? ToMTP(data) : MTPInputBusinessGreetingMessage())
+	).fail([=](const MTP::Error &error) {
+		_greetingSettings = was;
+		_greetingSettingsChanged.fire({});
+		if (fail) {
+			fail(error.type());
+		}
+	}).send();
 
 	_greetingSettings = std::move(data);
 	_greetingSettingsChanged.fire({});
diff --git a/Telegram/SourceFiles/data/business/data_business_info.h b/Telegram/SourceFiles/data/business/data_business_info.h
index e572d2757..3b747eb0f 100644
--- a/Telegram/SourceFiles/data/business/data_business_info.h
+++ b/Telegram/SourceFiles/data/business/data_business_info.h
@@ -20,15 +20,17 @@ public:
 
 	void preload();
 
-	void saveWorkingHours(WorkingHours data);
+	void saveWorkingHours(WorkingHours data, Fn<void(QString)> fail);
 
-	void saveAwaySettings(AwaySettings data);
+	void saveAwaySettings(AwaySettings data, Fn<void(QString)> fail);
 	void applyAwaySettings(AwaySettings data);
 	[[nodiscard]] AwaySettings awaySettings() const;
 	[[nodiscard]] bool awaySettingsLoaded() const;
 	[[nodiscard]] rpl::producer<> awaySettingsChanged() const;
 
-	void saveGreetingSettings(GreetingSettings data);
+	void saveGreetingSettings(
+		GreetingSettings data,
+		Fn<void(QString)> fail);
 	void applyGreetingSettings(GreetingSettings data);
 	[[nodiscard]] GreetingSettings greetingSettings() const;
 	[[nodiscard]] bool greetingSettingsLoaded() const;
diff --git a/Telegram/SourceFiles/settings/business/settings_away_message.cpp b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
index ae600e0de..4953bbdbd 100644
--- a/Telegram/SourceFiles/settings/business/settings_away_message.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
@@ -213,7 +213,9 @@ void AwayMessage::setupContent(
 	const auto current = info->awaySettings();
 	const auto disabled = (current.schedule.type == AwayScheduleType::Never);
 
-	_recipients = current.recipients;
+	_recipients = disabled
+		? Data::BusinessRecipients{ .allButExcluded = true }
+		: current.recipients;
 	auto initialSchedule = disabled ? AwaySchedule{
 		.type = AwayScheduleType::Always,
 	} : current.schedule;
@@ -340,14 +342,25 @@ void AwayMessage::setupContent(
 }
 
 void AwayMessage::save() {
+	const auto show = controller()->uiShow();
 	const auto session = &controller()->session();
+	const auto fail = [=](QString error) {
+		if (error == u"BUSINESS_RECIPIENTS_EMPTY"_q) {
+			AssertIsDebug();
+			show->showToast(u"Please choose at least one recipient."_q);
+			//tr::lng_greeting_recipients_empty(tr::now));
+		} else if (error != u"SHORTCUT_INVALID"_q) {
+			show->showToast(error);
+		}
+	};
 	session->data().businessInfo().saveAwaySettings(
 		_enabled.current() ? Data::AwaySettings{
 			.recipients = _recipients.current(),
 			.schedule = _schedule.current(),
 			.shortcutId = LookupShortcutId(session, u"away"_q),
 			.offlineOnly = _offlineOnly.current(),
-		} : Data::AwaySettings());
+		} : Data::AwaySettings(),
+		fail);
 }
 
 } // namespace
diff --git a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
index 18d9c9545..d9879f470 100644
--- a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
@@ -189,12 +189,20 @@ void Chatbots::setupContent(
 }
 
 void Chatbots::save() {
-	const auto settings = Data::ChatbotsSettings{
+	const auto show = controller()->uiShow();
+	const auto session = &controller()->session();
+	const auto fail = [=](QString error) {
+		if (error == u"BUSINESS_RECIPIENTS_EMPTY"_q) {
+			AssertIsDebug();
+			show->showToast(u"Please choose at least one recipient."_q);
+			//tr::lng_greeting_recipients_empty(tr::now));
+		}
+	};
+	controller()->session().data().chatbots().save({
 		.bot = _botValue.current().bot,
 		.recipients = _recipients.current(),
 		.repliesAllowed = _repliesAllowed.current(),
-	};
-	controller()->session().data().chatbots().save(settings);
+	}, [=](QString error) { show->showToast(error); });
 }
 
 } // namespace
diff --git a/Telegram/SourceFiles/settings/business/settings_greeting.cpp b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
index e16cc6477..6db47a7a0 100644
--- a/Telegram/SourceFiles/settings/business/settings_greeting.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
@@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "main/main_session.h"
 #include "settings/business/settings_shortcut_messages.h"
 #include "settings/business/settings_recipients_helper.h"
+#include "ui/boxes/time_picker_box.h"
 #include "ui/layers/generic_box.h"
 #include "ui/text/text_utilities.h"
 #include "ui/toast/toast.h"
@@ -73,92 +74,22 @@ void EditPeriodBox(
 		not_null<Ui::GenericBox*> box,
 		int days,
 		Fn<void(int)> save) {
-	auto values = base::flat_set<int>{ 7, 14, 21, 28 };
-	if (!values.contains(days)) {
-		values.emplace(days);
+	auto values = std::vector{ 7, 14, 21, 28 };
+	if (!ranges::contains(values, days)) {
+		values.push_back(days);
+		ranges::sort(values);
 	}
-	const auto startIndex = int(values.find(days) - begin(values));
 
-	const auto content = box->addRow(object_ptr<Ui::FixedHeightWidget>(
-		box,
-		st::settingsWorkingHoursPicker));
-
-	const auto font = st::boxTextFont;
-	const auto itemHeight = st::settingsWorkingHoursPickerItemHeight;
-	auto paintCallback = [=](
-			QPainter &p,
-			int index,
-			float64 y,
-			float64 distanceFromCenter,
-			int outerWidth) {
-		const auto r = QRectF(0, y, outerWidth, itemHeight);
-		const auto progress = std::abs(distanceFromCenter);
-		const auto revProgress = 1. - progress;
-		p.save();
-		p.translate(r.center());
-		constexpr auto kMinYScale = 0.2;
-		const auto yScale = kMinYScale
-			+ (1. - kMinYScale) * anim::easeOutCubic(1., revProgress);
-		p.scale(1., yScale);
-		p.translate(-r.center());
-		p.setOpacity(revProgress);
-		p.setFont(font);
-		p.setPen(st::defaultFlatLabel.textFg);
-		p.drawText(
-			r,
-			tr::lng_days(tr::now, lt_count, *(values.begin() + index)),
-			style::al_center);
-		p.restore();
-	};
-
-	const auto picker = Ui::CreateChild<Ui::VerticalDrumPicker>(
-		content,
-		std::move(paintCallback),
-		int(values.size()),
-		itemHeight,
-		startIndex);
-
-	content->sizeValue(
-	) | rpl::start_with_next([=](const QSize &s) {
-		picker->resize(s.width(), s.height());
-		picker->moveToLeft((s.width() - picker->width()) / 2, 0);
-	}, content->lifetime());
-
-	content->paintRequest(
-	) | rpl::start_with_next([=](const QRect &r) {
-		auto p = QPainter(content);
-
-		p.fillRect(r, Qt::transparent);
-
-		const auto lineRect = QRect(
-			0,
-			content->height() / 2,
-			content->width(),
-			st::defaultInputField.borderActive);
-		p.fillRect(lineRect.translated(0, itemHeight / 2), st::activeLineFg);
-		p.fillRect(lineRect.translated(0, -itemHeight / 2), st::activeLineFg);
-	}, content->lifetime());
-
-	base::install_event_filter(content, [=](not_null<QEvent*> e) {
-		if ((e->type() == QEvent::MouseButtonPress)
-			|| (e->type() == QEvent::MouseButtonRelease)
-			|| (e->type() == QEvent::MouseMove)) {
-			picker->handleMouseEvent(static_cast<QMouseEvent*>(e.get()));
-		} else if (e->type() == QEvent::Wheel) {
-			picker->handleWheelEvent(static_cast<QWheelEvent*>(e.get()));
-		}
-		return base::EventFilterResult::Continue;
-	});
-	base::install_event_filter(box, [=](not_null<QEvent*> e) {
-		if (e->type() == QEvent::KeyPress) {
-			picker->handleKeyEvent(static_cast<QKeyEvent*>(e.get()));
-		}
-		return base::EventFilterResult::Continue;
-	});
+	const auto phrases = ranges::views::all(
+		values
+	) | ranges::views::transform([](int days) {
+		return tr::lng_days(tr::now, lt_count, days);
+	}) | ranges::to_vector;
+	const auto take = TimePickerBox(box, values, phrases, days);
 
 	box->addButton(tr::lng_settings_save(), [=] {
 		const auto weak = Ui::MakeWeak(box);
-		save(*(begin(values) + picker->index()));
+		save(take());
 		if (const auto strong = weak.data()) {
 			strong->closeBox();
 		}
@@ -187,7 +118,9 @@ void Greeting::setupContent(
 	const auto current = info->greetingSettings();
 	const auto disabled = !current.noActivityDays;
 
-	_recipients = current.recipients;
+	_recipients = disabled
+		? Data::BusinessRecipients{ .allButExcluded = true }
+		: current.recipients;
 	_noActivityDays = disabled
 		? kDefaultNoActivityDays
 		: current.noActivityDays;
@@ -326,13 +259,24 @@ void Greeting::setupContent(
 }
 
 void Greeting::save() {
+	const auto show = controller()->uiShow();
 	const auto session = &controller()->session();
+	const auto fail = [=](QString error) {
+		if (error == u"BUSINESS_RECIPIENTS_EMPTY"_q) {
+			AssertIsDebug();
+			show->showToast(u"Please choose at least one recipient."_q);
+			//tr::lng_greeting_recipients_empty(tr::now));
+		} else if (error != u"SHORTCUT_INVALID"_q) {
+			show->showToast(error);
+		}
+	};
 	session->data().businessInfo().saveGreetingSettings(
 		_enabled.current() ? Data::GreetingSettings{
 			.recipients = _recipients.current(),
 			.noActivityDays = _noActivityDays.current(),
 			.shortcutId = LookupShortcutId(session, u"hello"_q),
-		} : Data::GreetingSettings());
+		} : Data::GreetingSettings(),
+		fail);
 }
 
 } // namespace
diff --git a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
index 53fb228ba..d45d94750 100644
--- a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
@@ -41,7 +41,6 @@ public:
 
 private:
 	void setupContent(not_null<Window::SessionController*> controller);
-	void save();
 
 	rpl::variable<int> _count;
 
@@ -54,11 +53,7 @@ QuickReplies::QuickReplies(
 	setupContent(controller);
 }
 
-QuickReplies::~QuickReplies() {
-	if (!Core::Quitting()) {
-		save();
-	}
-}
+QuickReplies::~QuickReplies() = default;
 
 rpl::producer<QString> QuickReplies::title() {
 	return tr::lng_replies_title();
@@ -160,9 +155,6 @@ void QuickReplies::setupContent(
 	Ui::ResizeFitChild(this, content);
 }
 
-void QuickReplies::save() {
-}
-
 } // namespace
 
 Type QuickRepliesId() {
diff --git a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
index 42865a2c0..39ef6e793 100644
--- a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
@@ -673,8 +673,10 @@ void WorkingHours::setupContent(
 }
 
 void WorkingHours::save() {
+	const auto show = controller()->uiShow();
 	controller()->session().data().businessInfo().saveWorkingHours(
-		_enabled.current() ? _hours.current() : Data::WorkingHours());
+		_enabled.current() ? _hours.current() : Data::WorkingHours(),
+		[=](QString error) { show->showToast(error); });
 }
 
 } // namespace

From f812166249a3ab52988bb360bddf39f06f7b6e6e Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 1 Mar 2024 18:01:35 +0400
Subject: [PATCH 066/108] Use server-side order for business features list.

---
 .../settings/business/settings_location.cpp   |  2 +
 .../settings/settings_business.cpp            | 55 ++++++++++++++-----
 .../SourceFiles/settings/settings_business.h  |  3 +
 .../SourceFiles/settings/settings_premium.cpp |  8 +--
 4 files changed, 51 insertions(+), 17 deletions(-)

diff --git a/Telegram/SourceFiles/settings/business/settings_location.cpp b/Telegram/SourceFiles/settings/business/settings_location.cpp
index 67a865696..2d9895ed9 100644
--- a/Telegram/SourceFiles/settings/business/settings_location.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_location.cpp
@@ -70,6 +70,7 @@ void Location::setupContent(
 
 	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
 
+#if 0 // #TODO location choosing
 	AddDividerTextWithLottie(content, {
 		.lottie = u"location"_q,
 		.lottieSize = st::settingsCloudPasswordIconSize,
@@ -91,6 +92,7 @@ void Location::setupContent(
 	showFinishes() | rpl::start_with_next([=] {
 		address->setFocus();
 	}, address->lifetime());
+#endif
 
 	if (!mapSupported()) {
 		AddDividerTextWithLottie(content, {
diff --git a/Telegram/SourceFiles/settings/settings_business.cpp b/Telegram/SourceFiles/settings/settings_business.cpp
index a8792783c..c94c63464 100644
--- a/Telegram/SourceFiles/settings/settings_business.cpp
+++ b/Telegram/SourceFiles/settings/settings_business.cpp
@@ -16,6 +16,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "info/info_wrap_widget.h" // Info::Wrap.
 #include "info/settings/info_settings_widget.h" // SectionCustomTopBarData.
 #include "lang/lang_keys.h"
+#include "main/main_account.h"
+#include "main/main_app_config.h"
 #include "main/main_session.h"
 #include "settings/business/settings_away_message.h"
 #include "settings/business/settings_chatbots.h"
@@ -58,19 +60,19 @@ using Order = std::vector<QString>;
 
 [[nodiscard]] Order FallbackOrder() {
 	return Order{
-		u"location"_q,
-		u"opening_hours"_q,
+		u"greeting_message"_q,
+		u"away_message"_q,
 		u"quick_replies"_q,
-		u"greeting_messages"_q,
-		u"away_messages"_q,
-		u"chatbots"_q,
+		u"business_hours"_q,
+		u"business_location"_q,
+		u"business_bots"_q,
 	};
 }
 
 [[nodiscard]] base::flat_map<QString, Entry> EntryMap() {
 	return base::flat_map<QString, Entry>{
 		{
-			u"location"_q,
+			u"business_location"_q,
 			Entry{
 				&st::settingsBusinessIconLocation,
 				tr::lng_business_subtitle_location(),
@@ -79,7 +81,7 @@ using Order = std::vector<QString>;
 			},
 		},
 		{
-			u"opening_hours"_q,
+			u"business_hours"_q,
 			Entry{
 				&st::settingsBusinessIconHours,
 				tr::lng_business_subtitle_opening_hours(),
@@ -97,7 +99,7 @@ using Order = std::vector<QString>;
 			},
 		},
 		{
-			u"greeting_messages"_q,
+			u"greeting_message"_q,
 			Entry{
 				&st::settingsBusinessIconGreeting,
 				tr::lng_business_subtitle_greeting_messages(),
@@ -106,7 +108,7 @@ using Order = std::vector<QString>;
 			},
 		},
 		{
-			u"away_messages"_q,
+			u"away_message"_q,
 			Entry{
 				&st::settingsBusinessIconAway,
 				tr::lng_business_subtitle_away_messages(),
@@ -115,7 +117,7 @@ using Order = std::vector<QString>;
 			},
 		},
 		{
-			u"chatbots"_q,
+			u"business_bots"_q,
 			Entry{
 				&st::settingsBusinessIconChatbots,
 				tr::lng_business_subtitle_chatbots(),
@@ -222,9 +224,9 @@ void AddBusinessSummary(
 	icons.reserve(int(entryMap.size()));
 	{
 		const auto &account = controller->session().account();
-		const auto mtpOrder = FallbackOrder();/* session->account().appConfig().get<Order>(
-			"premium_promo_order",
-			FallbackOrder());*/ AssertIsDebug()
+		const auto mtpOrder = account.appConfig().get<Order>(
+			"business_promo_order",
+			FallbackOrder());
 		const auto processEntry = [&](Entry &entry) {
 			icons.push_back(entry.icon);
 			addRow(entry);
@@ -589,4 +591,31 @@ void ShowBusiness(not_null<Window::SessionController*> controller) {
 	controller->showSettings(Settings::BusinessId());
 }
 
+std::vector<BusinessFeature> BusinessFeaturesOrder(
+		not_null<::Main::Session*> session) {
+	const auto mtpOrder = session->account().appConfig().get<Order>(
+		"business_promo_order",
+		FallbackOrder());
+	return ranges::views::all(
+		mtpOrder
+	) | ranges::views::transform([](const QString &s) {
+		if (s == u"greeting_message"_q) {
+			return BusinessFeature::GreetingMessages;
+		} else if (s == u"away_message"_q) {
+			return BusinessFeature::AwayMessages;
+		} else if (s == u"quick_replies"_q) {
+			return BusinessFeature::QuickReplies;
+		} else if (s == u"business_hours"_q) {
+			return BusinessFeature::OpeningHours;
+		} else if (s == u"business_location"_q) {
+			return BusinessFeature::Location;
+		} else if (s == u"business_bots"_q) {
+			return BusinessFeature::Chatbots;
+		}
+		return BusinessFeature::kCount;
+	}) | ranges::views::filter([](BusinessFeature feature) {
+		return (feature != BusinessFeature::kCount);
+	}) | ranges::to_vector;
+}
+
 } // namespace Settings
diff --git a/Telegram/SourceFiles/settings/settings_business.h b/Telegram/SourceFiles/settings/settings_business.h
index e255fd715..47bc2bca3 100644
--- a/Telegram/SourceFiles/settings/settings_business.h
+++ b/Telegram/SourceFiles/settings/settings_business.h
@@ -34,4 +34,7 @@ enum class BusinessFeature {
 
 void ShowBusiness(not_null<Window::SessionController*> controller);
 
+[[nodiscard]] std::vector<BusinessFeature> BusinessFeaturesOrder(
+	not_null<::Main::Session*> session);
+
 } // namespace Settings
diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp
index 809b8ba8e..3e3b8abac 100644
--- a/Telegram/SourceFiles/settings/settings_premium.cpp
+++ b/Telegram/SourceFiles/settings/settings_premium.cpp
@@ -180,7 +180,6 @@ using Order = std::vector<QString>;
 		u"stories"_q,
 		u"more_upload"_q,
 		u"double_limits"_q,
-		u"business"_q,
 		u"last_seen"_q,
 		u"voice_to_text"_q,
 		u"faster_download"_q,
@@ -198,6 +197,7 @@ using Order = std::vector<QString>;
 		u"infinite_reactions"_q,
 		u"animated_userpics"_q,
 		u"premium_stickers"_q,
+		u"business"_q,
 	};
 }
 
@@ -1537,7 +1537,7 @@ not_null<Ui::GradientButton*> CreateSubscribeButton(
 	return result;
 }
 
-[[nodiscard]] std::vector<PremiumPreview> PremiumPreviewOrder(
+std::vector<PremiumPreview> PremiumPreviewOrder(
 		not_null<Main::Session*> session) {
 	const auto mtpOrder = session->account().appConfig().get<Order>(
 		"premium_promo_order",
@@ -1684,9 +1684,9 @@ void AddSummaryPremium(
 	icons.reserve(int(entryMap.size()));
 	{
 		const auto &account = controller->session().account();
-		const auto mtpOrder = FallbackOrder();/* session->account().appConfig().get<Order>(
+		const auto mtpOrder = account.appConfig().get<Order>(
 			"premium_promo_order",
-			FallbackOrder());*/ AssertIsDebug()
+			FallbackOrder());
 		const auto processEntry = [&](Entry &entry) {
 			icons.push_back(entry.icon);
 			addRow(entry);

From 88751896af79260517a6c95841b2571c9818975e Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 1 Mar 2024 18:37:50 +0400
Subject: [PATCH 067/108] PremiumPreview/BusinessFeature -> PremiumFeature.

---
 .../boxes/background_preview_box.cpp          |   2 +-
 .../SourceFiles/boxes/edit_caption_box.cpp    |   2 +-
 .../SourceFiles/boxes/gift_premium_box.cpp    |   2 +-
 Telegram/SourceFiles/boxes/language_box.cpp   |   2 +-
 .../SourceFiles/boxes/premium_preview_box.cpp | 217 +++++++++++-------
 .../SourceFiles/boxes/premium_preview_box.h   |  17 +-
 Telegram/SourceFiles/boxes/send_files_box.cpp |   2 +-
 .../SourceFiles/core/local_url_handlers.cpp   |   2 +-
 .../dialogs/dialogs_search_tags.cpp           |   2 +-
 .../history/history_inner_widget.cpp          |   2 +-
 .../history/history_item_helpers.cpp          |   4 +-
 .../history/view/history_view_message.cpp     |   2 +-
 .../view/history_view_sticker_toast.cpp       |   2 +-
 .../view/history_view_transcribe_button.cpp   |   2 +-
 .../info_profile_emoji_status_panel.cpp       |   2 +-
 .../media/stories/media_stories_stealth.cpp   |   2 +-
 .../media/view/media_view_overlay_widget.cpp  |   2 +-
 .../settings/settings_business.cpp            |  57 +++--
 .../SourceFiles/settings/settings_business.h  |  15 +-
 .../SourceFiles/settings/settings_premium.cpp |  86 +++----
 .../SourceFiles/settings/settings_premium.h   |   8 +-
 .../SourceFiles/window/section_widget.cpp     |   4 +-
 .../SourceFiles/window/window_main_menu.cpp   |   2 +-
 23 files changed, 242 insertions(+), 196 deletions(-)

diff --git a/Telegram/SourceFiles/boxes/background_preview_box.cpp b/Telegram/SourceFiles/boxes/background_preview_box.cpp
index 048362638..d445ce1f5 100644
--- a/Telegram/SourceFiles/boxes/background_preview_box.cpp
+++ b/Telegram/SourceFiles/boxes/background_preview_box.cpp
@@ -776,7 +776,7 @@ void BackgroundPreviewBox::applyForPeer() {
 		} else {
 			ShowPremiumPreviewBox(
 				_controller->uiShow(),
-				PremiumPreview::Wallpapers);
+				PremiumFeature::Wallpapers);
 		}
 	});
 	const auto cancel = CreateChild<RoundButton>(
diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp
index 0c4b212c5..5ffdfb6f3 100644
--- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp
+++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp
@@ -682,7 +682,7 @@ void EditCaptionBox::setupEmojiPanel() {
 			&& !_controller->session().premium()) {
 			ShowPremiumPreviewBox(
 				_controller,
-				PremiumPreview::AnimatedEmoji);
+				PremiumFeature::AnimatedEmoji);
 		} else {
 			Data::InsertCustomEmoji(_field.get(), data.document);
 		}
diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.cpp b/Telegram/SourceFiles/boxes/gift_premium_box.cpp
index f544b5647..41f9cc097 100644
--- a/Telegram/SourceFiles/boxes/gift_premium_box.cpp
+++ b/Telegram/SourceFiles/boxes/gift_premium_box.cpp
@@ -587,7 +587,7 @@ void GiftsBox(
 		const auto content = box->addRow(
 			object_ptr<Ui::VerticalLayout>(box),
 			{});
-		auto buttonCallback = [=](PremiumPreview section) {
+		auto buttonCallback = [=](PremiumFeature section) {
 			stars->setPaused(true);
 			const auto previewBoxShown = [=](
 					not_null<Ui::BoxContent*> previewBox) {
diff --git a/Telegram/SourceFiles/boxes/language_box.cpp b/Telegram/SourceFiles/boxes/language_box.cpp
index aa69a6fee..98c49900a 100644
--- a/Telegram/SourceFiles/boxes/language_box.cpp
+++ b/Telegram/SourceFiles/boxes/language_box.cpp
@@ -1216,7 +1216,7 @@ void LanguageBox::setupTop(not_null<Ui::VerticalLayout*> container) {
 		if (checked && !premium) {
 			ShowPremiumPreviewToBuy(
 				_controller,
-				PremiumPreview::RealTimeTranslation);
+				PremiumFeature::RealTimeTranslation);
 			_translateChatTurnOff.fire(false);
 		}
 		return premium
diff --git a/Telegram/SourceFiles/boxes/premium_preview_box.cpp b/Telegram/SourceFiles/boxes/premium_preview_box.cpp
index c3ce95189..76df0c2d7 100644
--- a/Telegram/SourceFiles/boxes/premium_preview_box.cpp
+++ b/Telegram/SourceFiles/boxes/premium_preview_box.cpp
@@ -60,7 +60,7 @@ constexpr auto kStarPeriod = 3 * crl::time(1000);
 using Data::ReactionId;
 
 struct Descriptor {
-	PremiumPreview section = PremiumPreview::Stickers;
+	PremiumFeature section = PremiumFeature::Stickers;
 	DocumentData *requestedSticker = nullptr;
 	bool fromSettings = false;
 	Fn<void()> hiddenCallback;
@@ -91,92 +91,118 @@ void PreloadSticker(const std::shared_ptr<Data::DocumentMedia> &media) {
 	media->videoThumbnailWanted(origin);
 }
 
-[[nodiscard]] rpl::producer<QString> SectionTitle(PremiumPreview section) {
+[[nodiscard]] rpl::producer<QString> SectionTitle(PremiumFeature section) {
 	switch (section) {
-	case PremiumPreview::Wallpapers:
+	case PremiumFeature::Wallpapers:
 		return tr::lng_premium_summary_subtitle_wallpapers();
-	case PremiumPreview::Stories:
+	case PremiumFeature::Stories:
 		return tr::lng_premium_summary_subtitle_stories();
-	case PremiumPreview::DoubleLimits:
+	case PremiumFeature::DoubleLimits:
 		return tr::lng_premium_summary_subtitle_double_limits();
-	case PremiumPreview::MoreUpload:
+	case PremiumFeature::MoreUpload:
 		return tr::lng_premium_summary_subtitle_more_upload();
-	case PremiumPreview::FasterDownload:
+	case PremiumFeature::FasterDownload:
 		return tr::lng_premium_summary_subtitle_faster_download();
-	case PremiumPreview::VoiceToText:
+	case PremiumFeature::VoiceToText:
 		return tr::lng_premium_summary_subtitle_voice_to_text();
-	case PremiumPreview::NoAds:
+	case PremiumFeature::NoAds:
 		return tr::lng_premium_summary_subtitle_no_ads();
-	case PremiumPreview::EmojiStatus:
+	case PremiumFeature::EmojiStatus:
 		return tr::lng_premium_summary_subtitle_emoji_status();
-	case PremiumPreview::InfiniteReactions:
+	case PremiumFeature::InfiniteReactions:
 		return tr::lng_premium_summary_subtitle_infinite_reactions();
-	case PremiumPreview::TagsForMessages:
+	case PremiumFeature::TagsForMessages:
 		return tr::lng_premium_summary_subtitle_tags_for_messages();
-	case PremiumPreview::LastSeen:
+	case PremiumFeature::LastSeen:
 		return tr::lng_premium_summary_subtitle_last_seen();
-	case PremiumPreview::MessagePrivacy:
+	case PremiumFeature::MessagePrivacy:
 		return tr::lng_premium_summary_subtitle_message_privacy();
-	case PremiumPreview::Stickers:
+	case PremiumFeature::Stickers:
 		return tr::lng_premium_summary_subtitle_premium_stickers();
-	case PremiumPreview::AnimatedEmoji:
+	case PremiumFeature::AnimatedEmoji:
 		return tr::lng_premium_summary_subtitle_animated_emoji();
-	case PremiumPreview::AdvancedChatManagement:
+	case PremiumFeature::AdvancedChatManagement:
 		return tr::lng_premium_summary_subtitle_advanced_chat_management();
-	case PremiumPreview::ProfileBadge:
+	case PremiumFeature::ProfileBadge:
 		return tr::lng_premium_summary_subtitle_profile_badge();
-	case PremiumPreview::AnimatedUserpics:
+	case PremiumFeature::AnimatedUserpics:
 		return tr::lng_premium_summary_subtitle_animated_userpics();
-	case PremiumPreview::RealTimeTranslation:
+	case PremiumFeature::RealTimeTranslation:
 		return tr::lng_premium_summary_subtitle_translation();
-	case PremiumPreview::Business:
+	case PremiumFeature::Business:
 		return tr::lng_premium_summary_subtitle_business();
+
+	case PremiumFeature::BusinessLocation:
+		return tr::lng_business_subtitle_location();
+	case PremiumFeature::BusinessHours:
+		return tr::lng_business_subtitle_opening_hours();
+	case PremiumFeature::QuickReplies:
+		return tr::lng_business_subtitle_quick_replies();
+	case PremiumFeature::GreetingMessage:
+		return tr::lng_business_subtitle_greeting_messages();
+	case PremiumFeature::AwayMessage:
+		return tr::lng_business_subtitle_away_messages();
+	case PremiumFeature::BusinessBots:
+		return tr::lng_business_subtitle_chatbots();
 	}
-	Unexpected("PremiumPreview in SectionTitle.");
+	Unexpected("PremiumFeature in SectionTitle.");
 }
 
-[[nodiscard]] rpl::producer<QString> SectionAbout(PremiumPreview section) {
+[[nodiscard]] rpl::producer<QString> SectionAbout(PremiumFeature section) {
 	switch (section) {
-	case PremiumPreview::Wallpapers:
+	case PremiumFeature::Wallpapers:
 		return tr::lng_premium_summary_about_wallpapers();
-	case PremiumPreview::Stories:
+	case PremiumFeature::Stories:
 		return tr::lng_premium_summary_about_stories();
-	case PremiumPreview::DoubleLimits:
+	case PremiumFeature::DoubleLimits:
 		return tr::lng_premium_summary_about_double_limits();
-	case PremiumPreview::MoreUpload:
+	case PremiumFeature::MoreUpload:
 		return tr::lng_premium_summary_about_more_upload();
-	case PremiumPreview::FasterDownload:
+	case PremiumFeature::FasterDownload:
 		return tr::lng_premium_summary_about_faster_download();
-	case PremiumPreview::VoiceToText:
+	case PremiumFeature::VoiceToText:
 		return tr::lng_premium_summary_about_voice_to_text();
-	case PremiumPreview::NoAds:
+	case PremiumFeature::NoAds:
 		return tr::lng_premium_summary_about_no_ads();
-	case PremiumPreview::EmojiStatus:
+	case PremiumFeature::EmojiStatus:
 		return tr::lng_premium_summary_about_emoji_status();
-	case PremiumPreview::InfiniteReactions:
+	case PremiumFeature::InfiniteReactions:
 		return tr::lng_premium_summary_about_infinite_reactions();
-	case PremiumPreview::TagsForMessages:
+	case PremiumFeature::TagsForMessages:
 		return tr::lng_premium_summary_about_tags_for_messages();
-	case PremiumPreview::LastSeen:
+	case PremiumFeature::LastSeen:
 		return tr::lng_premium_summary_about_last_seen();
-	case PremiumPreview::MessagePrivacy:
+	case PremiumFeature::MessagePrivacy:
 		return tr::lng_premium_summary_about_message_privacy();
-	case PremiumPreview::Stickers:
+	case PremiumFeature::Stickers:
 		return tr::lng_premium_summary_about_premium_stickers();
-	case PremiumPreview::AnimatedEmoji:
+	case PremiumFeature::AnimatedEmoji:
 		return tr::lng_premium_summary_about_animated_emoji();
-	case PremiumPreview::AdvancedChatManagement:
+	case PremiumFeature::AdvancedChatManagement:
 		return tr::lng_premium_summary_about_advanced_chat_management();
-	case PremiumPreview::ProfileBadge:
+	case PremiumFeature::ProfileBadge:
 		return tr::lng_premium_summary_about_profile_badge();
-	case PremiumPreview::AnimatedUserpics:
+	case PremiumFeature::AnimatedUserpics:
 		return tr::lng_premium_summary_about_animated_userpics();
-	case PremiumPreview::RealTimeTranslation:
+	case PremiumFeature::RealTimeTranslation:
 		return tr::lng_premium_summary_about_translation();
-	case PremiumPreview::Business:
+	case PremiumFeature::Business:
 		return tr::lng_premium_summary_about_business();
+
+	case PremiumFeature::BusinessLocation:
+		return tr::lng_business_about_location();
+	case PremiumFeature::BusinessHours:
+		return tr::lng_business_about_opening_hours();
+	case PremiumFeature::QuickReplies:
+		return tr::lng_business_about_quick_replies();
+	case PremiumFeature::GreetingMessage:
+		return tr::lng_business_about_greeting_messages();
+	case PremiumFeature::AwayMessage:
+		return tr::lng_business_about_away_messages();
+	case PremiumFeature::BusinessBots:
+		return tr::lng_business_about_chatbots();
 	}
-	Unexpected("PremiumPreview in SectionTitle.");
+	Unexpected("PremiumFeature in SectionTitle.");
 }
 
 [[nodiscard]] object_ptr<Ui::RpWidget> ChatBackPreview(
@@ -468,33 +494,40 @@ struct VideoPreviewDocument {
 	RectPart align = RectPart::Bottom;
 };
 
-[[nodiscard]] bool VideoAlignToTop(PremiumPreview section) {
-	return (section == PremiumPreview::MoreUpload)
-		|| (section == PremiumPreview::NoAds)
-		|| (section == PremiumPreview::AnimatedEmoji);
+[[nodiscard]] bool VideoAlignToTop(PremiumFeature section) {
+	return (section == PremiumFeature::MoreUpload)
+		|| (section == PremiumFeature::NoAds)
+		|| (section == PremiumFeature::AnimatedEmoji);
 }
 
 [[nodiscard]] DocumentData *LookupVideo(
 		not_null<Main::Session*> session,
-		PremiumPreview section) {
+		PremiumFeature section) {
 	const auto name = [&] {
 		switch (section) {
-		case PremiumPreview::MoreUpload: return "more_upload";
-		case PremiumPreview::FasterDownload: return "faster_download";
-		case PremiumPreview::VoiceToText: return "voice_to_text";
-		case PremiumPreview::NoAds: return "no_ads";
-		case PremiumPreview::AnimatedEmoji: return "animated_emoji";
-		case PremiumPreview::AdvancedChatManagement:
+		case PremiumFeature::MoreUpload: return "more_upload";
+		case PremiumFeature::FasterDownload: return "faster_download";
+		case PremiumFeature::VoiceToText: return "voice_to_text";
+		case PremiumFeature::NoAds: return "no_ads";
+		case PremiumFeature::AnimatedEmoji: return "animated_emoji";
+		case PremiumFeature::AdvancedChatManagement:
 			return "advanced_chat_management";
-		case PremiumPreview::EmojiStatus: return "emoji_status";
-		case PremiumPreview::InfiniteReactions: return "infinite_reactions";
-		case PremiumPreview::TagsForMessages: return "saved_tags";
-		case PremiumPreview::ProfileBadge: return "profile_badge";
-		case PremiumPreview::AnimatedUserpics: return "animated_userpics";
-		case PremiumPreview::RealTimeTranslation: return "translations";
-		case PremiumPreview::Wallpapers: return "wallpapers";
-		case PremiumPreview::LastSeen: return "last_seen";
-		case PremiumPreview::MessagePrivacy: return "message_privacy";
+		case PremiumFeature::EmojiStatus: return "emoji_status";
+		case PremiumFeature::InfiniteReactions: return "infinite_reactions";
+		case PremiumFeature::TagsForMessages: return "saved_tags";
+		case PremiumFeature::ProfileBadge: return "profile_badge";
+		case PremiumFeature::AnimatedUserpics: return "animated_userpics";
+		case PremiumFeature::RealTimeTranslation: return "translations";
+		case PremiumFeature::Wallpapers: return "wallpapers";
+		case PremiumFeature::LastSeen: return "last_seen";
+		case PremiumFeature::MessagePrivacy: return "message_privacy";
+
+		case PremiumFeature::BusinessLocation: return "business_location";
+		case PremiumFeature::BusinessHours: return "business_hours";
+		case PremiumFeature::QuickReplies: return "quick_replies";
+		case PremiumFeature::GreetingMessage: return "greeting_message";
+		case PremiumFeature::AwayMessage: return "away_message";
+		case PremiumFeature::BusinessBots: return "business_bots";
 		}
 		return "";
 	}();
@@ -721,7 +754,7 @@ struct VideoPreviewDocument {
 [[nodiscard]] not_null<Ui::RpWidget*> GenericPreview(
 		not_null<Ui::RpWidget*> parent,
 		std::shared_ptr<ChatHelpers::Show> show,
-		PremiumPreview section,
+		PremiumFeature section,
 		Fn<void()> readyCallback) {
 	const auto result = Ui::CreateChild<Ui::RpWidget>(parent.get());
 	result->show();
@@ -762,10 +795,10 @@ struct VideoPreviewDocument {
 [[nodiscard]] not_null<Ui::RpWidget*> GenerateDefaultPreview(
 		not_null<Ui::RpWidget*> parent,
 		std::shared_ptr<ChatHelpers::Show> show,
-		PremiumPreview section,
+		PremiumFeature section,
 		Fn<void()> readyCallback) {
 	switch (section) {
-	case PremiumPreview::Stickers:
+	case PremiumFeature::Stickers:
 		return StickersPreview(parent, std::move(show), readyCallback);
 	default:
 		return GenericPreview(
@@ -789,8 +822,8 @@ struct VideoPreviewDocument {
 
 [[nodiscard]] object_ptr<Ui::RpWidget> CreateSwitch(
 		not_null<Ui::RpWidget*> parent,
-		not_null<rpl::variable<PremiumPreview>*> selected,
-		std::vector<PremiumPreview> order) {
+		not_null<rpl::variable<PremiumFeature>*> selected,
+		std::vector<PremiumFeature> order) {
 	const auto padding = st::premiumDotPadding;
 	const auto width = padding.left() + st::premiumDot + padding.right();
 	const auto height = padding.top() + st::premiumDot + padding.bottom();
@@ -861,14 +894,20 @@ void PreviewBox(
 		Ui::Animations::Simple animation;
 		Fn<void()> preload;
 		std::vector<Hiding> hiding;
-		rpl::variable<PremiumPreview> selected;
-		std::vector<PremiumPreview> order;
+		rpl::variable<PremiumFeature> selected;
+		std::vector<PremiumFeature> order;
 	};
 	const auto state = outer->lifetime().make_state<State>();
 	state->selected = descriptor.section;
-	state->order = Settings::PremiumPreviewOrder(&show->session());
+	auto premiumOrder = Settings::PremiumFeaturesOrder(&show->session());
+	auto businessOrder = Settings::BusinessFeaturesOrder(&show->session());
+	state->order = ranges::contains(businessOrder, descriptor.section)
+		? std::move(businessOrder)
+		: ranges::contains(businessOrder, descriptor.section)
+		? std::move(premiumOrder)
+		: std::vector{ descriptor.section };
 
-	const auto index = [=](PremiumPreview section) {
+	const auto index = [=](PremiumFeature section) {
 		const auto it = ranges::find(state->order, section);
 		return (it == end(state->order))
 			? 0
@@ -911,7 +950,7 @@ void PreviewBox(
 			return;
 		}
 		const auto now = state->selected.current();
-		if (now != PremiumPreview::Stickers && !state->stickersPreload) {
+		if (now != PremiumFeature::Stickers && !state->stickersPreload) {
 			const auto ready = [=] {
 				if (state->stickersPreload) {
 					state->stickersPreloadReady = true;
@@ -922,14 +961,14 @@ void PreviewBox(
 			state->stickersPreload = GenerateDefaultPreview(
 				outer,
 				show,
-				PremiumPreview::Stickers,
+				PremiumFeature::Stickers,
 				ready);
 			state->stickersPreload->hide();
 		}
 	};
 
 	switch (descriptor.section) {
-	case PremiumPreview::Stickers:
+	case PremiumFeature::Stickers:
 		state->content = media
 			? StickerPreview(outer, show, media, state->preload)
 			: StickersPreview(outer, show, state->preload);
@@ -945,7 +984,7 @@ void PreviewBox(
 
 	state->selected.value(
 	) | rpl::combine_previous(
-	) | rpl::start_with_next([=](PremiumPreview was, PremiumPreview now) {
+	) | rpl::start_with_next([=](PremiumFeature was, PremiumFeature now) {
 		const auto animationCallback = [=] {
 			if (!state->animation.animating()) {
 				for (const auto &hiding : base::take(state->hiding)) {
@@ -987,7 +1026,7 @@ void PreviewBox(
 			.leftTill = state->content->x() - start,
 		});
 		state->leftFrom = start;
-		if (now == PremiumPreview::Stickers && state->stickersPreload) {
+		if (now == PremiumFeature::Stickers && state->stickersPreload) {
 			state->content = base::take(state->stickersPreload);
 			state->content->show();
 			if (base::take(state->stickersPreloadReady)) {
@@ -1058,14 +1097,14 @@ void PreviewBox(
 			return Settings::LookupPremiumRef(state->selected.current());
 		};
 		auto unlock = state->selected.value(
-		) | rpl::map([=](PremiumPreview section) {
-			return (section == PremiumPreview::InfiniteReactions)
+		) | rpl::map([=](PremiumFeature section) {
+			return (section == PremiumFeature::InfiniteReactions)
 				? tr::lng_premium_unlock_reactions()
-				: (section == PremiumPreview::Stickers)
+				: (section == PremiumFeature::Stickers)
 				? tr::lng_premium_unlock_stickers()
-				: (section == PremiumPreview::AnimatedEmoji)
+				: (section == PremiumFeature::AnimatedEmoji)
 				? tr::lng_premium_unlock_emoji()
-				: (section == PremiumPreview::EmojiStatus)
+				: (section == PremiumFeature::EmojiStatus)
 				? tr::lng_premium_unlock_status()
 				: tr::lng_premium_more_about();
 		}) | rpl::flatten_latest();
@@ -1212,19 +1251,19 @@ void Show(
 			descriptor.shownCallback(raw);
 		}
 		return;
-	} else if (descriptor.section == PremiumPreview::DoubleLimits) {
+	} else if (descriptor.section == PremiumFeature::DoubleLimits) {
 		show->showBox(Box([=](not_null<Ui::GenericBox*> box) {
 			DoubledLimitsPreviewBox(box, &show->session());
 			DecorateListPromoBox(box, show, descriptor);
 		}));
 		return;
-	} else if (descriptor.section == PremiumPreview::Stories) {
+	} else if (descriptor.section == PremiumFeature::Stories) {
 		show->showBox(Box([=](not_null<Ui::GenericBox*> box) {
 			UpgradedStoriesPreviewBox(box, &show->session());
 			DecorateListPromoBox(box, show, descriptor);
 		}));
 		return;
-	} else if (descriptor.section == PremiumPreview::Business) {
+	} else if (descriptor.section == PremiumFeature::Business) {
 		const auto window = show->resolveWindow(
 			ChatHelpers::WindowUsage::PremiumPromo);
 		if (window) {
@@ -1298,21 +1337,21 @@ void ShowStickerPreviewBox(
 		std::shared_ptr<ChatHelpers::Show> show,
 		not_null<DocumentData*> document) {
 	Show(std::move(show), Descriptor{
-		.section = PremiumPreview::Stickers,
+		.section = PremiumFeature::Stickers,
 		.requestedSticker = document,
 	});
 }
 
 void ShowPremiumPreviewBox(
 		not_null<Window::SessionController*> controller,
-		PremiumPreview section,
+		PremiumFeature section,
 		Fn<void(not_null<Ui::BoxContent*>)> shown) {
 	ShowPremiumPreviewBox(controller->uiShow(), section, std::move(shown));
 }
 
 void ShowPremiumPreviewBox(
 		std::shared_ptr<ChatHelpers::Show> show,
-		PremiumPreview section,
+		PremiumFeature section,
 		Fn<void(not_null<Ui::BoxContent*>)> shown,
 		bool hideSubscriptionButton) {
 	Show(std::move(show), Descriptor{
@@ -1324,7 +1363,7 @@ void ShowPremiumPreviewBox(
 
 void ShowPremiumPreviewToBuy(
 		not_null<Window::SessionController*> controller,
-		PremiumPreview section,
+		PremiumFeature section,
 		Fn<void()> hiddenCallback) {
 	Show(controller->uiShow(), Descriptor{
 		.section = section,
diff --git a/Telegram/SourceFiles/boxes/premium_preview_box.h b/Telegram/SourceFiles/boxes/premium_preview_box.h
index 9fc4da279..80400a6ee 100644
--- a/Telegram/SourceFiles/boxes/premium_preview_box.h
+++ b/Telegram/SourceFiles/boxes/premium_preview_box.h
@@ -45,7 +45,8 @@ void UpgradedStoriesPreviewBox(
 	not_null<Ui::GenericBox*> box,
 	not_null<Main::Session*> session);
 
-enum class PremiumPreview {
+enum class PremiumFeature {
+	// Premium features.
 	Stories,
 	DoubleLimits,
 	MoreUpload,
@@ -66,23 +67,31 @@ enum class PremiumPreview {
 	MessagePrivacy,
 	Business,
 
+	// Business features.
+	BusinessLocation,
+	BusinessHours,
+	QuickReplies,
+	GreetingMessage,
+	AwayMessage,
+	BusinessBots,
+
 	kCount,
 };
 
 void ShowPremiumPreviewBox(
 	not_null<Window::SessionController*> controller,
-	PremiumPreview section,
+	PremiumFeature section,
 	Fn<void(not_null<Ui::BoxContent*>)> shown = nullptr);
 
 void ShowPremiumPreviewBox(
 	std::shared_ptr<ChatHelpers::Show> show,
-	PremiumPreview section,
+	PremiumFeature section,
 	Fn<void(not_null<Ui::BoxContent*>)> shown = nullptr,
 	bool hideSubscriptionButton = false);
 
 void ShowPremiumPreviewToBuy(
 	not_null<Window::SessionController*> controller,
-	PremiumPreview section,
+	PremiumFeature section,
 	Fn<void()> hiddenCallback = nullptr);
 
 void PremiumUnavailableBox(not_null<Ui::GenericBox*> box);
diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp
index 455630fb1..ea208d42f 100644
--- a/Telegram/SourceFiles/boxes/send_files_box.cpp
+++ b/Telegram/SourceFiles/boxes/send_files_box.cpp
@@ -1135,7 +1135,7 @@ void SendFilesBox::setupEmojiPanel() {
 					_captionToPeer,
 					data.document)
 				: (_limits & SendFilesAllow::EmojiWithoutPremium))) {
-			ShowPremiumPreviewBox(_show, PremiumPreview::AnimatedEmoji);
+			ShowPremiumPreviewBox(_show, PremiumFeature::AnimatedEmoji);
 		} else {
 			Data::InsertCustomEmoji(_caption.data(), data.document);
 		}
diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp
index 82c115774..92ed6c9e8 100644
--- a/Telegram/SourceFiles/core/local_url_handlers.cpp
+++ b/Telegram/SourceFiles/core/local_url_handlers.cpp
@@ -665,7 +665,7 @@ bool ShowSearchTagsPromo(
 	if (!controller) {
 		return false;
 	}
-	ShowPremiumPreviewBox(controller, PremiumPreview::TagsForMessages);
+	ShowPremiumPreviewBox(controller, PremiumFeature::TagsForMessages);
 	return true;
 }
 
diff --git a/Telegram/SourceFiles/dialogs/dialogs_search_tags.cpp b/Telegram/SourceFiles/dialogs/dialogs_search_tags.cpp
index a3d1264ff..276d4e5b6 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_search_tags.cpp
+++ b/Telegram/SourceFiles/dialogs/dialogs_search_tags.cpp
@@ -47,7 +47,7 @@ namespace {
 		if (const auto controller = my.sessionWindow.get()) {
 			ShowPremiumPreviewBox(
 				controller,
-				PremiumPreview::TagsForMessages);
+				PremiumFeature::TagsForMessages);
 		}
 	});
 }
diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp
index 0943e3ac1..a2188d7d5 100644
--- a/Telegram/SourceFiles/history/history_inner_widget.cpp
+++ b/Telegram/SourceFiles/history/history_inner_widget.cpp
@@ -160,7 +160,7 @@ void FillSponsoredMessagesMenu(
 		menu->addSeparator(&st::expandedMenuSeparator);
 	}
 	menu->addAction(tr::lng_sponsored_hide_ads(tr::now), [=] {
-		ShowPremiumPreviewBox(controller, PremiumPreview::NoAds);
+		ShowPremiumPreviewBox(controller, PremiumFeature::NoAds);
 	}, &st::menuIconCancel);
 }
 
diff --git a/Telegram/SourceFiles/history/history_item_helpers.cpp b/Telegram/SourceFiles/history/history_item_helpers.cpp
index 65153bf86..e6c9b1e5a 100644
--- a/Telegram/SourceFiles/history/history_item_helpers.cpp
+++ b/Telegram/SourceFiles/history/history_item_helpers.cpp
@@ -362,7 +362,7 @@ ClickHandlerPtr HideSponsoredClickHandler() {
 	return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
 		const auto my = context.other.value<ClickHandlerContext>();
 		if (const auto controller = my.sessionWindow.get()) {
-			ShowPremiumPreviewBox(controller, PremiumPreview::NoAds);
+			ShowPremiumPreviewBox(controller, PremiumFeature::NoAds);
 		}
 	});
 }
@@ -793,7 +793,7 @@ void ShowTrialTranscribesToast(int left, TimeId until) {
 	}
 	const auto filter = [=](const auto &...) {
 		if (const auto controller = window->sessionController()) {
-			ShowPremiumPreviewBox(controller, PremiumPreview::VoiceToText);
+			ShowPremiumPreviewBox(controller, PremiumFeature::VoiceToText);
 			window->activate();
 		}
 		return false;
diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp
index f08ed8414..0c88e71e6 100644
--- a/Telegram/SourceFiles/history/view/history_view_message.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_message.cpp
@@ -3031,7 +3031,7 @@ void Message::refreshReactions() {
 							= ExtractController(context)) {
 							ShowPremiumPreviewBox(
 								controller,
-								PremiumPreview::TagsForMessages);
+								PremiumFeature::TagsForMessages);
 						}
 						return;
 					}
diff --git a/Telegram/SourceFiles/history/view/history_view_sticker_toast.cpp b/Telegram/SourceFiles/history/view/history_view_sticker_toast.cpp
index f10d8e328..fc4f41b73 100644
--- a/Telegram/SourceFiles/history/view/history_view_sticker_toast.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_sticker_toast.cpp
@@ -238,7 +238,7 @@ void StickerToast::showWithTitle(const QString &title) {
 			&& (i->second->flags & Data::StickersSetFlag::Installed)) {
 			ShowPremiumPreviewBox(
 				_controller,
-				PremiumPreview::AnimatedEmoji);
+				PremiumFeature::AnimatedEmoji);
 		} else {
 			_controller->show(Box<StickerSetBox>(
 				_controller->uiShow(),
diff --git a/Telegram/SourceFiles/history/view/history_view_transcribe_button.cpp b/Telegram/SourceFiles/history/view/history_view_transcribe_button.cpp
index dccca0f3a..1688b8f6c 100644
--- a/Telegram/SourceFiles/history/view/history_view_transcribe_button.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_transcribe_button.cpp
@@ -272,7 +272,7 @@ ClickHandlerPtr TranscribeButton::link() {
 			if (const auto controller = my.sessionWindow.get()) {
 				ShowPremiumPreviewBox(
 					controller,
-					PremiumPreview::VoiceToText);
+					PremiumFeature::VoiceToText);
 			}
 		} else {
 			const auto max = session->api().transcribes().trialsMaxLengthMs();
diff --git a/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.cpp b/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.cpp
index 8881dbc99..c7f26b4ef 100644
--- a/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.cpp
+++ b/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.cpp
@@ -281,7 +281,7 @@ bool EmojiStatusPanel::filter(
 	if (_chooseFilter) {
 		return _chooseFilter(chosenId);
 	} else if (chosenId && !controller->session().premium()) {
-		ShowPremiumPreviewBox(controller, PremiumPreview::EmojiStatus);
+		ShowPremiumPreviewBox(controller, PremiumFeature::EmojiStatus);
 		return false;
 	}
 	return true;
diff --git a/Telegram/SourceFiles/media/stories/media_stories_stealth.cpp b/Telegram/SourceFiles/media/stories/media_stories_stealth.cpp
index 404ff1aea..80d4f20a7 100644
--- a/Telegram/SourceFiles/media/stories/media_stories_stealth.cpp
+++ b/Telegram/SourceFiles/media/stories/media_stories_stealth.cpp
@@ -352,7 +352,7 @@ struct Feature {
 				data->requested = false;
 				const auto usage = ChatHelpers::WindowUsage::PremiumPromo;
 				if (const auto window = show->resolveWindow(usage)) {
-					ShowPremiumPreviewBox(window, PremiumPreview::Stories);
+					ShowPremiumPreviewBox(window, PremiumFeature::Stories);
 					window->window().activate();
 				}
 			} else if (now.mode.cooldownTill > now.now) {
diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp
index 3bbc98fb9..4c8a0cf70 100644
--- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp
+++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp
@@ -1254,7 +1254,7 @@ void OverlayWidget::showPremiumDownloadPromo() {
 	const auto filter = [=](const auto &...) {
 		const auto usage = ChatHelpers::WindowUsage::PremiumPromo;
 		if (const auto window = uiShow()->resolveWindow(usage)) {
-			ShowPremiumPreviewBox(window, PremiumPreview::Stories);
+			ShowPremiumPreviewBox(window, PremiumFeature::Stories);
 			window->window().activate();
 		}
 		return false;
diff --git a/Telegram/SourceFiles/settings/settings_business.cpp b/Telegram/SourceFiles/settings/settings_business.cpp
index c94c63464..c0848e341 100644
--- a/Telegram/SourceFiles/settings/settings_business.cpp
+++ b/Telegram/SourceFiles/settings/settings_business.cpp
@@ -53,7 +53,7 @@ struct Entry {
 	const style::icon *icon;
 	rpl::producer<QString> title;
 	rpl::producer<QString> description;
-	BusinessFeature feature = BusinessFeature::Location;
+	PremiumFeature feature = PremiumFeature::BusinessLocation;
 };
 
 using Order = std::vector<QString>;
@@ -77,7 +77,7 @@ using Order = std::vector<QString>;
 				&st::settingsBusinessIconLocation,
 				tr::lng_business_subtitle_location(),
 				tr::lng_business_about_location(),
-				BusinessFeature::Location,
+				PremiumFeature::BusinessLocation,
 			},
 		},
 		{
@@ -86,7 +86,7 @@ using Order = std::vector<QString>;
 				&st::settingsBusinessIconHours,
 				tr::lng_business_subtitle_opening_hours(),
 				tr::lng_business_about_opening_hours(),
-				BusinessFeature::OpeningHours,
+				PremiumFeature::BusinessHours,
 			},
 		},
 		{
@@ -95,7 +95,7 @@ using Order = std::vector<QString>;
 				&st::settingsBusinessIconReplies,
 				tr::lng_business_subtitle_quick_replies(),
 				tr::lng_business_about_quick_replies(),
-				BusinessFeature::QuickReplies,
+				PremiumFeature::QuickReplies,
 			},
 		},
 		{
@@ -104,7 +104,7 @@ using Order = std::vector<QString>;
 				&st::settingsBusinessIconGreeting,
 				tr::lng_business_subtitle_greeting_messages(),
 				tr::lng_business_about_greeting_messages(),
-				BusinessFeature::GreetingMessages,
+				PremiumFeature::GreetingMessage,
 			},
 		},
 		{
@@ -113,7 +113,7 @@ using Order = std::vector<QString>;
 				&st::settingsBusinessIconAway,
 				tr::lng_business_subtitle_away_messages(),
 				tr::lng_business_about_away_messages(),
-				BusinessFeature::AwayMessages,
+				PremiumFeature::AwayMessage,
 			},
 		},
 		{
@@ -122,7 +122,7 @@ using Order = std::vector<QString>;
 				&st::settingsBusinessIconChatbots,
 				tr::lng_business_subtitle_chatbots(),
 				tr::lng_business_about_chatbots(),
-				BusinessFeature::Chatbots,
+				PremiumFeature::BusinessBots,
 			},
 		},
 	};
@@ -131,7 +131,7 @@ using Order = std::vector<QString>;
 void AddBusinessSummary(
 		not_null<Ui::VerticalLayout*> content,
 		not_null<Window::SessionController*> controller,
-		Fn<void(BusinessFeature)> buttonCallback) {
+		Fn<void(PremiumFeature)> buttonCallback) {
 	const auto &stDefault = st::settingsButton;
 	const auto &stLabel = st::defaultFlatLabel;
 	const auto iconSize = st::settingsPremiumIconDouble.size();
@@ -359,15 +359,22 @@ void Business::setupContent() {
 
 	Ui::AddSkip(content, st::settingsFromFileTop);
 
-	AddBusinessSummary(content, _controller, [=](BusinessFeature feature) {
+	AddBusinessSummary(content, _controller, [=](PremiumFeature feature) {
+		if (!_controller->session().premium()) {
+			_setPaused(true);
+			const auto hidden = crl::guard(this, [=] { _setPaused(false); });
+
+			ShowPremiumPreviewToBuy(_controller, feature, hidden);
+			return;
+		}
 		showOther([&] {
 			switch (feature) {
-			case BusinessFeature::AwayMessages: return AwayMessageId();
-			case BusinessFeature::OpeningHours: return WorkingHoursId();
-			case BusinessFeature::Location: return LocationId();
-			case BusinessFeature::GreetingMessages: return GreetingId();
-			case BusinessFeature::QuickReplies: return QuickRepliesId();
-			case BusinessFeature::Chatbots: return ChatbotsId();
+			case PremiumFeature::AwayMessage: return AwayMessageId();
+			case PremiumFeature::BusinessHours: return WorkingHoursId();
+			case PremiumFeature::BusinessLocation: return LocationId();
+			case PremiumFeature::GreetingMessage: return GreetingId();
+			case PremiumFeature::QuickReplies: return QuickRepliesId();
+			case PremiumFeature::BusinessBots: return ChatbotsId();
 			}
 			Unexpected("Feature in Business::setupContent.");
 		}());
@@ -591,7 +598,7 @@ void ShowBusiness(not_null<Window::SessionController*> controller) {
 	controller->showSettings(Settings::BusinessId());
 }
 
-std::vector<BusinessFeature> BusinessFeaturesOrder(
+std::vector<PremiumFeature> BusinessFeaturesOrder(
 		not_null<::Main::Session*> session) {
 	const auto mtpOrder = session->account().appConfig().get<Order>(
 		"business_promo_order",
@@ -600,21 +607,21 @@ std::vector<BusinessFeature> BusinessFeaturesOrder(
 		mtpOrder
 	) | ranges::views::transform([](const QString &s) {
 		if (s == u"greeting_message"_q) {
-			return BusinessFeature::GreetingMessages;
+			return PremiumFeature::GreetingMessage;
 		} else if (s == u"away_message"_q) {
-			return BusinessFeature::AwayMessages;
+			return PremiumFeature::AwayMessage;
 		} else if (s == u"quick_replies"_q) {
-			return BusinessFeature::QuickReplies;
+			return PremiumFeature::QuickReplies;
 		} else if (s == u"business_hours"_q) {
-			return BusinessFeature::OpeningHours;
+			return PremiumFeature::BusinessHours;
 		} else if (s == u"business_location"_q) {
-			return BusinessFeature::Location;
+			return PremiumFeature::BusinessLocation;
 		} else if (s == u"business_bots"_q) {
-			return BusinessFeature::Chatbots;
+			return PremiumFeature::BusinessBots;
 		}
-		return BusinessFeature::kCount;
-	}) | ranges::views::filter([](BusinessFeature feature) {
-		return (feature != BusinessFeature::kCount);
+		return PremiumFeature::kCount;
+	}) | ranges::views::filter([](PremiumFeature feature) {
+		return (feature != PremiumFeature::kCount);
 	}) | ranges::to_vector;
 }
 
diff --git a/Telegram/SourceFiles/settings/settings_business.h b/Telegram/SourceFiles/settings/settings_business.h
index 47bc2bca3..6bd077af0 100644
--- a/Telegram/SourceFiles/settings/settings_business.h
+++ b/Telegram/SourceFiles/settings/settings_business.h
@@ -9,6 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "settings/settings_type.h"
 
+enum class PremiumFeature;
+
 namespace Main {
 class Session;
 } // namespace Main
@@ -19,22 +21,11 @@ class SessionController;
 
 namespace Settings {
 
-enum class BusinessFeature {
-	Location,
-	OpeningHours,
-	QuickReplies,
-	GreetingMessages,
-	AwayMessages,
-	Chatbots,
-
-	kCount,
-};
-
 [[nodiscard]] Type BusinessId();
 
 void ShowBusiness(not_null<Window::SessionController*> controller);
 
-[[nodiscard]] std::vector<BusinessFeature> BusinessFeaturesOrder(
+[[nodiscard]] std::vector<PremiumFeature> BusinessFeaturesOrder(
 	not_null<::Main::Session*> session);
 
 } // namespace Settings
diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp
index 3e3b8abac..2b9d5e35a 100644
--- a/Telegram/SourceFiles/settings/settings_premium.cpp
+++ b/Telegram/SourceFiles/settings/settings_premium.cpp
@@ -169,7 +169,7 @@ struct Entry {
 	const style::icon *icon;
 	rpl::producer<QString> title;
 	rpl::producer<QString> description;
-	PremiumPreview section = PremiumPreview::DoubleLimits;
+	PremiumFeature section = PremiumFeature::DoubleLimits;
 	bool newBadge = false;
 };
 
@@ -209,7 +209,7 @@ using Order = std::vector<QString>;
 				&st::settingsPremiumIconTags,
 				tr::lng_premium_summary_subtitle_tags_for_messages(),
 				tr::lng_premium_summary_about_tags_for_messages(),
-				PremiumPreview::TagsForMessages,
+				PremiumFeature::TagsForMessages,
 				true,
 			},
 		},
@@ -219,7 +219,7 @@ using Order = std::vector<QString>;
 				&st::settingsPremiumIconLastSeen,
 				tr::lng_premium_summary_subtitle_last_seen(),
 				tr::lng_premium_summary_about_last_seen(),
-				PremiumPreview::LastSeen,
+				PremiumFeature::LastSeen,
 				true,
 			},
 		},
@@ -229,7 +229,7 @@ using Order = std::vector<QString>;
 				&st::settingsPremiumIconPrivacy,
 				tr::lng_premium_summary_subtitle_message_privacy(),
 				tr::lng_premium_summary_about_message_privacy(),
-				PremiumPreview::MessagePrivacy,
+				PremiumFeature::MessagePrivacy,
 				true,
 			},
 		},
@@ -239,7 +239,7 @@ using Order = std::vector<QString>;
 				&st::settingsPremiumIconWallpapers,
 				tr::lng_premium_summary_subtitle_wallpapers(),
 				tr::lng_premium_summary_about_wallpapers(),
-				PremiumPreview::Wallpapers,
+				PremiumFeature::Wallpapers,
 			},
 		},
 		{
@@ -248,7 +248,7 @@ using Order = std::vector<QString>;
 				&st::settingsPremiumIconStories,
 				tr::lng_premium_summary_subtitle_stories(),
 				tr::lng_premium_summary_about_stories(),
-				PremiumPreview::Stories,
+				PremiumFeature::Stories,
 			},
 		},
 		{
@@ -257,7 +257,7 @@ using Order = std::vector<QString>;
 				&st::settingsPremiumIconDouble,
 				tr::lng_premium_summary_subtitle_double_limits(),
 				tr::lng_premium_summary_about_double_limits(),
-				PremiumPreview::DoubleLimits,
+				PremiumFeature::DoubleLimits,
 			},
 		},
 		{
@@ -266,7 +266,7 @@ using Order = std::vector<QString>;
 				&st::settingsPremiumIconFiles,
 				tr::lng_premium_summary_subtitle_more_upload(),
 				tr::lng_premium_summary_about_more_upload(),
-				PremiumPreview::MoreUpload,
+				PremiumFeature::MoreUpload,
 			},
 		},
 		{
@@ -275,7 +275,7 @@ using Order = std::vector<QString>;
 				&st::settingsPremiumIconSpeed,
 				tr::lng_premium_summary_subtitle_faster_download(),
 				tr::lng_premium_summary_about_faster_download(),
-				PremiumPreview::FasterDownload,
+				PremiumFeature::FasterDownload,
 			},
 		},
 		{
@@ -284,7 +284,7 @@ using Order = std::vector<QString>;
 				&st::settingsPremiumIconVoice,
 				tr::lng_premium_summary_subtitle_voice_to_text(),
 				tr::lng_premium_summary_about_voice_to_text(),
-				PremiumPreview::VoiceToText,
+				PremiumFeature::VoiceToText,
 			},
 		},
 		{
@@ -293,7 +293,7 @@ using Order = std::vector<QString>;
 				&st::settingsPremiumIconChannelsOff,
 				tr::lng_premium_summary_subtitle_no_ads(),
 				tr::lng_premium_summary_about_no_ads(),
-				PremiumPreview::NoAds,
+				PremiumFeature::NoAds,
 			},
 		},
 		{
@@ -302,7 +302,7 @@ using Order = std::vector<QString>;
 				&st::settingsPremiumIconStatus,
 				tr::lng_premium_summary_subtitle_emoji_status(),
 				tr::lng_premium_summary_about_emoji_status(),
-				PremiumPreview::EmojiStatus,
+				PremiumFeature::EmojiStatus,
 			},
 		},
 		{
@@ -311,7 +311,7 @@ using Order = std::vector<QString>;
 				&st::settingsPremiumIconLike,
 				tr::lng_premium_summary_subtitle_infinite_reactions(),
 				tr::lng_premium_summary_about_infinite_reactions(),
-				PremiumPreview::InfiniteReactions,
+				PremiumFeature::InfiniteReactions,
 			},
 		},
 		{
@@ -320,7 +320,7 @@ using Order = std::vector<QString>;
 				&st::settingsIconStickers,
 				tr::lng_premium_summary_subtitle_premium_stickers(),
 				tr::lng_premium_summary_about_premium_stickers(),
-				PremiumPreview::Stickers,
+				PremiumFeature::Stickers,
 			},
 		},
 		{
@@ -329,7 +329,7 @@ using Order = std::vector<QString>;
 				&st::settingsIconEmoji,
 				tr::lng_premium_summary_subtitle_animated_emoji(),
 				tr::lng_premium_summary_about_animated_emoji(),
-				PremiumPreview::AnimatedEmoji,
+				PremiumFeature::AnimatedEmoji,
 			},
 		},
 		{
@@ -338,7 +338,7 @@ using Order = std::vector<QString>;
 				&st::settingsIconChat,
 				tr::lng_premium_summary_subtitle_advanced_chat_management(),
 				tr::lng_premium_summary_about_advanced_chat_management(),
-				PremiumPreview::AdvancedChatManagement,
+				PremiumFeature::AdvancedChatManagement,
 			},
 		},
 		{
@@ -347,7 +347,7 @@ using Order = std::vector<QString>;
 				&st::settingsPremiumIconStar,
 				tr::lng_premium_summary_subtitle_profile_badge(),
 				tr::lng_premium_summary_about_profile_badge(),
-				PremiumPreview::ProfileBadge,
+				PremiumFeature::ProfileBadge,
 			},
 		},
 		{
@@ -356,7 +356,7 @@ using Order = std::vector<QString>;
 				&st::settingsPremiumIconPlay,
 				tr::lng_premium_summary_subtitle_animated_userpics(),
 				tr::lng_premium_summary_about_animated_userpics(),
-				PremiumPreview::AnimatedUserpics,
+				PremiumFeature::AnimatedUserpics,
 			},
 		},
 		{
@@ -365,7 +365,7 @@ using Order = std::vector<QString>;
 				&st::settingsPremiumIconTranslations,
 				tr::lng_premium_summary_subtitle_translation(),
 				tr::lng_premium_summary_about_translation(),
-				PremiumPreview::RealTimeTranslation,
+				PremiumFeature::RealTimeTranslation,
 			},
 		},
 		{
@@ -374,7 +374,7 @@ using Order = std::vector<QString>;
 				&st::settingsPremiumIconPlay, AssertIsDebug()
 				tr::lng_premium_summary_subtitle_business(),
 				tr::lng_premium_summary_about_business(),
-				PremiumPreview::Business,
+				PremiumFeature::Business,
 				true,
 			},
 		},
@@ -971,7 +971,7 @@ void Premium::setupContent() {
 
 	setupSubscriptionOptions(content);
 
-	auto buttonCallback = [=](PremiumPreview section) {
+	auto buttonCallback = [=](PremiumFeature section) {
 		_setPaused(true);
 		const auto hidden = crl::guard(this, [=] { _setPaused(false); });
 
@@ -1350,7 +1350,7 @@ void StartPremiumPayment(
 	}
 }
 
-QString LookupPremiumRef(PremiumPreview section) {
+QString LookupPremiumRef(PremiumFeature section) {
 	for (const auto &[ref, entry] : EntryMap()) {
 		if (entry.section == section) {
 			return ref;
@@ -1537,7 +1537,7 @@ not_null<Ui::GradientButton*> CreateSubscribeButton(
 	return result;
 }
 
-std::vector<PremiumPreview> PremiumPreviewOrder(
+std::vector<PremiumFeature> PremiumFeaturesOrder(
 		not_null<Main::Session*> session) {
 	const auto mtpOrder = session->account().appConfig().get<Order>(
 		"premium_promo_order",
@@ -1546,41 +1546,41 @@ std::vector<PremiumPreview> PremiumPreviewOrder(
 		mtpOrder
 	) | ranges::views::transform([](const QString &s) {
 		if (s == u"more_upload"_q) {
-			return PremiumPreview::MoreUpload;
+			return PremiumFeature::MoreUpload;
 		} else if (s == u"faster_download"_q) {
-			return PremiumPreview::FasterDownload;
+			return PremiumFeature::FasterDownload;
 		} else if (s == u"voice_to_text"_q) {
-			return PremiumPreview::VoiceToText;
+			return PremiumFeature::VoiceToText;
 		} else if (s == u"no_ads"_q) {
-			return PremiumPreview::NoAds;
+			return PremiumFeature::NoAds;
 		} else if (s == u"emoji_status"_q) {
-			return PremiumPreview::EmojiStatus;
+			return PremiumFeature::EmojiStatus;
 		} else if (s == u"infinite_reactions"_q) {
-			return PremiumPreview::InfiniteReactions;
+			return PremiumFeature::InfiniteReactions;
 		} else if (s == u"saved_tags"_q) {
-			return PremiumPreview::TagsForMessages;
+			return PremiumFeature::TagsForMessages;
 		} else if (s == u"last_seen"_q) {
-			return PremiumPreview::LastSeen;
+			return PremiumFeature::LastSeen;
 		} else if (s == u"message_privacy"_q) {
-			return PremiumPreview::MessagePrivacy;
+			return PremiumFeature::MessagePrivacy;
 		} else if (s == u"premium_stickers"_q) {
-			return PremiumPreview::Stickers;
+			return PremiumFeature::Stickers;
 		} else if (s == u"animated_emoji"_q) {
-			return PremiumPreview::AnimatedEmoji;
+			return PremiumFeature::AnimatedEmoji;
 		} else if (s == u"advanced_chat_management"_q) {
-			return PremiumPreview::AdvancedChatManagement;
+			return PremiumFeature::AdvancedChatManagement;
 		} else if (s == u"profile_badge"_q) {
-			return PremiumPreview::ProfileBadge;
+			return PremiumFeature::ProfileBadge;
 		} else if (s == u"animated_userpics"_q) {
-			return PremiumPreview::AnimatedUserpics;
+			return PremiumFeature::AnimatedUserpics;
 		} else if (s == u"translations"_q) {
-			return PremiumPreview::RealTimeTranslation;
+			return PremiumFeature::RealTimeTranslation;
 		} else if (s == u"wallpapers"_q) {
-			return PremiumPreview::Wallpapers;
+			return PremiumFeature::Wallpapers;
 		}
-		return PremiumPreview::kCount;
-	}) | ranges::views::filter([](PremiumPreview type) {
-		return (type != PremiumPreview::kCount);
+		return PremiumFeature::kCount;
+	}) | ranges::views::filter([](PremiumFeature type) {
+		return (type != PremiumFeature::kCount);
 	}) | ranges::to_vector;
 }
 
@@ -1588,7 +1588,7 @@ void AddSummaryPremium(
 		not_null<Ui::VerticalLayout*> content,
 		not_null<Window::SessionController*> controller,
 		const QString &ref,
-		Fn<void(PremiumPreview)> buttonCallback) {
+		Fn<void(PremiumFeature)> buttonCallback) {
 	const auto &stDefault = st::settingsButton;
 	const auto &stLabel = st::defaultFlatLabel;
 	const auto iconSize = st::settingsPremiumIconDouble.size();
diff --git a/Telegram/SourceFiles/settings/settings_premium.h b/Telegram/SourceFiles/settings/settings_premium.h
index a8603aca6..6dd571812 100644
--- a/Telegram/SourceFiles/settings/settings_premium.h
+++ b/Telegram/SourceFiles/settings/settings_premium.h
@@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "settings/settings_type.h"
 
-enum class PremiumPreview;
+enum class PremiumFeature;
 
 namespace style {
 struct RoundButton;
@@ -57,7 +57,7 @@ void StartPremiumPayment(
 	not_null<Window::SessionController*> controller,
 	const QString &ref);
 
-[[nodiscard]] QString LookupPremiumRef(PremiumPreview section);
+[[nodiscard]] QString LookupPremiumRef(PremiumFeature section);
 
 void ShowPremiumPromoToast(
 	std::shared_ptr<ChatHelpers::Show> show,
@@ -91,14 +91,14 @@ struct SubscribeButtonArgs final {
 [[nodiscard]] not_null<Ui::GradientButton*> CreateSubscribeButton(
 	SubscribeButtonArgs &&args);
 
-[[nodiscard]] std::vector<PremiumPreview> PremiumPreviewOrder(
+[[nodiscard]] std::vector<PremiumFeature> PremiumFeaturesOrder(
 	not_null<::Main::Session*> session);
 
 void AddSummaryPremium(
 	not_null<Ui::VerticalLayout*> content,
 	not_null<Window::SessionController*> controller,
 	const QString &ref,
-	Fn<void(PremiumPreview)> buttonCallback);
+	Fn<void(PremiumFeature)> buttonCallback);
 
 } // namespace Settings
 
diff --git a/Telegram/SourceFiles/window/section_widget.cpp b/Telegram/SourceFiles/window/section_widget.cpp
index dcba6476c..4d7a48c7f 100644
--- a/Telegram/SourceFiles/window/section_widget.cpp
+++ b/Telegram/SourceFiles/window/section_widget.cpp
@@ -529,7 +529,7 @@ bool ShowReactPremiumError(
 		if (controller->session().premium()) {
 			return false;
 		}
-		ShowPremiumPreviewBox(controller, PremiumPreview::TagsForMessages);
+		ShowPremiumPreviewBox(controller, PremiumFeature::TagsForMessages);
 		return true;
 	} else if (controller->session().premium()
 		|| ranges::contains(item->chosenReactions(), id)
@@ -538,7 +538,7 @@ bool ShowReactPremiumError(
 	} else if (!id.custom()) {
 		return false;
 	}
-	ShowPremiumPreviewBox(controller, PremiumPreview::InfiniteReactions);
+	ShowPremiumPreviewBox(controller, PremiumFeature::InfiniteReactions);
 	return true;
 }
 
diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp
index 051a99d8b..e4f941d9e 100644
--- a/Telegram/SourceFiles/window/window_main_menu.cpp
+++ b/Telegram/SourceFiles/window/window_main_menu.cpp
@@ -1031,7 +1031,7 @@ void MainMenu::chooseEmojiStatus() {
 	if (const auto widget = _badge->widget()) {
 		_emojiStatusPanel->show(_controller, widget, _badge->sizeTag());
 	} else {
-		ShowPremiumPreviewBox(_controller, PremiumPreview::EmojiStatus);
+		ShowPremiumPreviewBox(_controller, PremiumFeature::EmojiStatus);
 	}
 }
 

From c94da177d79848766c1c47e13898c37761c48065 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 1 Mar 2024 22:18:01 +0400
Subject: [PATCH 068/108] Fix build with Xcode.

---
 Telegram/SourceFiles/data/business/data_business_common.cpp   | 2 +-
 .../SourceFiles/settings/business/settings_away_message.cpp   | 4 ++--
 Telegram/SourceFiles/settings/business/settings_chatbots.cpp  | 4 ++--
 Telegram/SourceFiles/settings/business/settings_greeting.cpp  | 4 ++--
 Telegram/SourceFiles/settings/business/settings_location.cpp  | 2 +-
 .../SourceFiles/settings/business/settings_working_hours.cpp  | 2 +-
 .../cloud_password/settings_cloud_password_email_confirm.cpp  | 2 +-
 Telegram/SourceFiles/settings/settings_chat.h                 | 2 +-
 Telegram/SourceFiles/settings/settings_common.h               | 2 +-
 Telegram/SourceFiles/settings/settings_common_session.h       | 2 +-
 Telegram/SourceFiles/settings/settings_main.h                 | 2 +-
 11 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/Telegram/SourceFiles/data/business/data_business_common.cpp b/Telegram/SourceFiles/data/business/data_business_common.cpp
index 956807f5d..34a46f3cc 100644
--- a/Telegram/SourceFiles/data/business/data_business_common.cpp
+++ b/Telegram/SourceFiles/data/business/data_business_common.cpp
@@ -149,7 +149,7 @@ WorkingIntervals ReplaceDayIntervals(
 		end(result.list),
 		begin(replacement.list),
 		end(replacement.list));
-	for (auto &interval : ranges::subrange(first, end(result.list))) {
+	for (auto &interval : ranges::make_subrange(first, end(result.list))) {
 		interval = interval.shifted(dayIndex * kDay);
 	}
 	return result.normalized();
diff --git a/Telegram/SourceFiles/settings/business/settings_away_message.cpp b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
index 4953bbdbd..04dfe993a 100644
--- a/Telegram/SourceFiles/settings/business/settings_away_message.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
@@ -55,7 +55,7 @@ private:
 
 [[nodiscard]] TimeId StartTimeMin() {
 	// Telegram was launched in August 2013 :)
-	return base::unixtime::serialize(QDateTime(QDate(2013, 8, 1)));
+	return base::unixtime::serialize(QDateTime(QDate(2013, 8, 1), QTime(0, 0)));
 }
 
 [[nodiscard]] TimeId EndTimeMin() {
@@ -260,7 +260,7 @@ void AwayMessage::setupContent(
 	_enabled.value() | rpl::filter(_1) | rpl::start_with_next([=] {
 		if (!_canHave.current()) {
 			controller->showToast({
-				.text = tr::lng_away_limit_reached(tr::now),
+				.text = { tr::lng_away_limit_reached(tr::now) },
 				.adaptive = true,
 			});
 			_deactivateOnAttempt.fire({});
diff --git a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
index d9879f470..d3f112483 100644
--- a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
@@ -47,7 +47,7 @@ public:
 
 	[[nodiscard]] rpl::producer<QString> title() override;
 
-	const Ui::RoundRect *bottomSkipRounding() const {
+	const Ui::RoundRect *bottomSkipRounding() const override {
 		return &_bottomSkipRounding;
 	}
 
@@ -59,7 +59,7 @@ private:
 
 	rpl::variable<Data::BusinessRecipients> _recipients;
 	rpl::variable<QString> _usernameValue;
-	rpl::variable<BotState> _botValue = nullptr;
+	rpl::variable<BotState> _botValue;
 	rpl::variable<bool> _repliesAllowed = true;
 
 };
diff --git a/Telegram/SourceFiles/settings/business/settings_greeting.cpp b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
index 6db47a7a0..3c73f391f 100644
--- a/Telegram/SourceFiles/settings/business/settings_greeting.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
@@ -44,7 +44,7 @@ public:
 
 	[[nodiscard]] rpl::producer<QString> title() override;
 
-	const Ui::RoundRect *bottomSkipRounding() const {
+	const Ui::RoundRect *bottomSkipRounding() const override {
 		return &_bottomSkipRounding;
 	}
 
@@ -157,7 +157,7 @@ void Greeting::setupContent(
 	_enabled.value() | rpl::filter(_1) | rpl::start_with_next([=] {
 		if (!_canHave.current()) {
 			controller->showToast({
-				.text = tr::lng_greeting_limit_reached(tr::now),
+				.text = { tr::lng_greeting_limit_reached(tr::now) },
 				.adaptive = true,
 			});
 			_deactivateOnAttempt.fire({});
diff --git a/Telegram/SourceFiles/settings/business/settings_location.cpp b/Telegram/SourceFiles/settings/business/settings_location.cpp
index 2d9895ed9..4a2f14e73 100644
--- a/Telegram/SourceFiles/settings/business/settings_location.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_location.cpp
@@ -32,7 +32,7 @@ public:
 
 	[[nodiscard]] rpl::producer<QString> title() override;
 
-	const Ui::RoundRect *bottomSkipRounding() const {
+	const Ui::RoundRect *bottomSkipRounding() const override {
 		return mapSupported() ? nullptr : &_bottomSkipRounding;
 	}
 
diff --git a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
index 39ef6e793..dd6c54b66 100644
--- a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
@@ -462,7 +462,7 @@ void AddWeekButton(
 		[=] { toggleButton->update(); });
 
 	auto status = data->value(
-	) | rpl::map([=](const Data::WorkingHours &data) {
+	) | rpl::map([=](const Data::WorkingHours &data) -> rpl::producer<QString> {
 		using namespace Data;
 
 		const auto intervals = ExtractDayIntervals(data.intervals, index);
diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.cpp b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.cpp
index 19bb96969..b1879387e 100644
--- a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.cpp
+++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.cpp
@@ -60,7 +60,7 @@ public:
 
 	[[nodiscard]] rpl::producer<QString> title() override;
 
-	[[nodiscard]] void fillTopBarMenu(
+	void fillTopBarMenu(
 		const Ui::Menu::MenuCallback &addAction) override;
 
 	void setupContent();
diff --git a/Telegram/SourceFiles/settings/settings_chat.h b/Telegram/SourceFiles/settings/settings_chat.h
index d1256de96..70724ea2b 100644
--- a/Telegram/SourceFiles/settings/settings_chat.h
+++ b/Telegram/SourceFiles/settings/settings_chat.h
@@ -44,7 +44,7 @@ public:
 
 	[[nodiscard]] rpl::producer<QString> title() override;
 
-	[[nodiscard]] void fillTopBarMenu(
+	void fillTopBarMenu(
 		const Ui::Menu::MenuCallback &addAction) override;
 
 private:
diff --git a/Telegram/SourceFiles/settings/settings_common.h b/Telegram/SourceFiles/settings/settings_common.h
index 47bf52774..c279640a5 100644
--- a/Telegram/SourceFiles/settings/settings_common.h
+++ b/Telegram/SourceFiles/settings/settings_common.h
@@ -102,7 +102,7 @@ public:
 	}
 	virtual void selectionAction(Info::SelectionAction action) {
 	}
-	[[nodiscard]] virtual void fillTopBarMenu(
+	virtual void fillTopBarMenu(
 		const Ui::Menu::MenuCallback &addAction) {
 	}
 
diff --git a/Telegram/SourceFiles/settings/settings_common_session.h b/Telegram/SourceFiles/settings/settings_common_session.h
index 2b439bacf..8bea347a0 100644
--- a/Telegram/SourceFiles/settings/settings_common_session.h
+++ b/Telegram/SourceFiles/settings/settings_common_session.h
@@ -79,7 +79,7 @@ public:
 	[[nodiscard]] rpl::producer<Type> sectionShowOther() final override {
 		return _showOtherRequests.events();
 	}
-	[[nodiscard]] void showOther(Type type) {
+	void showOther(Type type) {
 		_showOtherRequests.fire_copy(type);
 	}
 	[[nodiscard]] Fn<void(Type)> showOtherMethod() {
diff --git a/Telegram/SourceFiles/settings/settings_main.h b/Telegram/SourceFiles/settings/settings_main.h
index 4356a49b3..7040fb9fa 100644
--- a/Telegram/SourceFiles/settings/settings_main.h
+++ b/Telegram/SourceFiles/settings/settings_main.h
@@ -38,7 +38,7 @@ public:
 
 	[[nodiscard]] rpl::producer<QString> title() override;
 
-	[[nodiscard]] void fillTopBarMenu(
+	void fillTopBarMenu(
 		const Ui::Menu::MenuCallback &addAction) override;
 
 protected:

From ee847bc1a36f086ce97bf9d0ae74e4017fa0973c Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Sat, 2 Mar 2024 11:12:34 +0400
Subject: [PATCH 069/108] Fix legacy group pins loading on first group open.

Fixes #27466.
---
 Telegram/SourceFiles/apiwrap.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index 4676c5a50..ad49b37a9 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -3132,6 +3132,7 @@ void ApiWrap::sharedMediaDone(
 	if (topicRootId && !topic) {
 		return;
 	}
+	const auto hasMessages = !parsed.messageIds.empty();
 	_session->storage().add(Storage::SharedMediaAddSlice(
 		peer->id,
 		topicRootId,
@@ -3140,7 +3141,7 @@ void ApiWrap::sharedMediaDone(
 		parsed.noSkipRange,
 		parsed.fullCount
 	));
-	if (type == SharedMediaType::Pinned && !parsed.messageIds.empty()) {
+	if (type == SharedMediaType::Pinned && hasMessages) {
 		peer->owner().history(peer)->setHasPinnedMessages(true);
 		if (topic) {
 			topic->setHasPinnedMessages(true);

From 37f5160d1c635bd372667b89e4837351cc9728bb Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Mon, 4 Mar 2024 11:26:43 +0400
Subject: [PATCH 070/108] Fix bold formatting in the beginning of a quote.

---
 Telegram/lib_ui | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Telegram/lib_ui b/Telegram/lib_ui
index 14794d222..7eaf7f8aa 160000
--- a/Telegram/lib_ui
+++ b/Telegram/lib_ui
@@ -1 +1 @@
-Subproject commit 14794d22210cb21b82db20aa55b1f3c8733b5fbd
+Subproject commit 7eaf7f8aaa5c7aac9cbc6e5dc92dea7944003eba

From 5e828603767ad83ff496e262d8ef55b4a20b77a8 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Mon, 4 Mar 2024 17:00:25 +0400
Subject: [PATCH 071/108] Allow sending quick replies like bot commands.

---
 Telegram/SourceFiles/apiwrap.cpp              | 12 +++++++++
 Telegram/SourceFiles/apiwrap.h                |  3 +++
 .../chat_helpers/field_autocomplete.cpp       | 25 ++++++++++++++++-
 .../chat_helpers/field_autocomplete.h         |  1 +
 .../data/business/data_shortcut_messages.cpp  | 10 +++++++
 .../data/business/data_shortcut_messages.h    |  2 ++
 .../SourceFiles/history/history_widget.cpp    | 27 ++++++++++++++++---
 7 files changed, 75 insertions(+), 5 deletions(-)

diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index ad49b37a9..d6d9679da 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -953,6 +953,7 @@ void ApiWrap::requestMoreDialogsIfNeeded() {
 		}
 	}
 	requestContacts();
+	_session->data().shortcutMessages().preloadShortcuts();
 }
 
 void ApiWrap::updateDialogsOffset(
@@ -3606,6 +3607,17 @@ void ApiWrap::cancelLocalItem(not_null<HistoryItem*> item) {
 	}
 }
 
+void ApiWrap::sendShortcutMessages(
+		not_null<PeerData*> peer,
+		BusinessShortcutId id) {
+	request(MTPmessages_SendQuickReplyMessages(
+		peer->input,
+		MTP_int(id)
+	)).done([=](const MTPUpdates &result) {
+		applyUpdates(result);
+	}).send();
+}
+
 void ApiWrap::sendMessage(MessageToSend &&message) {
 	const auto history = message.action.history;
 	const auto peer = history->peer;
diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h
index 615960126..58165adec 100644
--- a/Telegram/SourceFiles/apiwrap.h
+++ b/Telegram/SourceFiles/apiwrap.h
@@ -337,6 +337,9 @@ public:
 
 	void cancelLocalItem(not_null<HistoryItem*> item);
 
+	void sendShortcutMessages(
+		not_null<PeerData*> peer,
+		BusinessShortcutId id);
 	void sendMessage(MessageToSend &&message);
 	void sendBotStart(
 		not_null<UserData*> bot,
diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp
index 382d850e3..1cf45d762 100644
--- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp
+++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "chat_helpers/field_autocomplete.h"
 
+#include "data/business/data_shortcut_messages.h"
 #include "data/data_document.h"
 #include "data/data_document_media.h"
 #include "data/data_channel.h"
@@ -27,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "storage/storage_account.h"
 #include "core/application.h"
 #include "core/core_settings.h"
+#include "lang/lang_keys.h"
 #include "lottie/lottie_single_player.h"
 #include "media/clip/media_clip_reader.h"
 #include "ui/widgets/popup_menu.h"
@@ -636,6 +638,27 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
 				}
 			}
 		}
+		const auto shortcuts = _user
+			? _user->owner().shortcutMessages().shortcuts().list
+			: base::flat_map<BusinessShortcutId, Data::Shortcut>();
+		if (!hasUsername && !shortcuts.empty()) {
+			const auto self = _user->session().user();
+			for (const auto &[id, shortcut] : shortcuts) {
+				if (shortcut.count < 1) {
+					continue;
+				} else if (!listAllSuggestions) {
+					if (!shortcut.name.startsWith(_filter, Qt::CaseInsensitive)) {
+						continue;
+					}
+				}
+				brows.push_back(BotCommandRow{
+					self,
+					shortcut.name,
+					tr::lng_forum_messages(tr::now, lt_count, shortcut.count),
+					self->activeUserpicView()
+				});
+			}
+		}
 	}
 	rowsUpdated(
 		std::move(mrows),
@@ -1247,7 +1270,7 @@ bool FieldAutocomplete::Inner::chooseAtIndex(
 				command,
 				insertUsername ? ('@' + PrimaryUsername(user)) : QString());
 
-			_botCommandChosen.fire({ commandString, method });
+			_botCommandChosen.fire({ user, commandString, method });
 			return true;
 		}
 	}
diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.h b/Telegram/SourceFiles/chat_helpers/field_autocomplete.h
index 2606dc1f2..5c9e291f3 100644
--- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.h
+++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.h
@@ -96,6 +96,7 @@ public:
 		ChooseMethod method = ChooseMethod::ByEnter;
 	};
 	struct BotCommandChosen {
+		not_null<UserData*> user;
 		QString command;
 		ChooseMethod method = ChooseMethod::ByEnter;
 	};
diff --git a/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp b/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
index e7b4a65a7..df2f5f880 100644
--- a/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
+++ b/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
@@ -497,6 +497,16 @@ Shortcut ShortcutMessages::lookupShortcut(BusinessShortcutId id) const {
 	return i->second;
 }
 
+BusinessShortcutId ShortcutMessages::lookupShortcutId(
+		const QString &name) const {
+	for (const auto &[id, shortcut] : _shortcuts.list) {
+		if (!shortcut.name.compare(name, Qt::CaseInsensitive)) {
+			return id;
+		}
+	}
+	return {};
+}
+
 void ShortcutMessages::editShortcut(
 		BusinessShortcutId id,
 		QString name,
diff --git a/Telegram/SourceFiles/data/business/data_shortcut_messages.h b/Telegram/SourceFiles/data/business/data_shortcut_messages.h
index 57c1205f8..76a1df56d 100644
--- a/Telegram/SourceFiles/data/business/data_shortcut_messages.h
+++ b/Telegram/SourceFiles/data/business/data_shortcut_messages.h
@@ -78,6 +78,8 @@ public:
 	[[nodiscard]] rpl::producer<ShortcutIdChange> shortcutIdChanged() const;
 	[[nodiscard]] BusinessShortcutId emplaceShortcut(QString name);
 	[[nodiscard]] Shortcut lookupShortcut(BusinessShortcutId id) const;
+	[[nodiscard]] BusinessShortcutId lookupShortcutId(
+		const QString &name) const;
 	void editShortcut(
 		BusinessShortcutId id,
 		QString name,
diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index b658b8555..043fd7b8c 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -52,6 +52,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "base/qt/qt_key_modifiers.h"
 #include "base/unixtime.h"
 #include "base/call_delayed.h"
+#include "data/business/data_shortcut_messages.h"
 #include "data/notify/data_notify_settings.h"
 #include "data/data_changes.h"
 #include "data/data_drafts.h"
@@ -431,7 +432,20 @@ HistoryWidget::HistoryWidget(
 
 	_fieldAutocomplete->botCommandChosen(
 	) | rpl::start_with_next([=](FieldAutocomplete::BotCommandChosen data) {
-		insertHashtagOrBotCommand(data.command, data.method);
+		using Method = FieldAutocomplete::ChooseMethod;
+		const auto messages = &data.user->owner().shortcutMessages();
+		const auto shortcutId = (_peer
+			&& data.user->isSelf()
+			&& data.method != Method::ByTab)
+			? messages->lookupShortcutId(data.command.mid(1))
+			: BusinessShortcutId();
+		if (shortcutId) {
+			session().api().sendShortcutMessages(_peer, shortcutId);
+			session().api().finishForwarding(prepareSendAction({}));
+			setFieldText(_field->getTextWithTagsPart(_field->textCursor().position()));
+		} else {
+			insertHashtagOrBotCommand(data.command, data.method);
+		}
 	}, lifetime());
 
 	_fieldAutocomplete->setModerateKeyActivateCallback([=](int key) {
@@ -1410,9 +1424,14 @@ AutocompleteQuery HistoryWidget::parseMentionHashtagBotCommandQuery() const {
 	} else if (result.query[0] == '@'
 		&& cRecentInlineBots().isEmpty()) {
 		session().local().readRecentHashtagsAndBots();
-	} else if (result.query[0] == '/'
-		&& ((_peer->isUser() && !_peer->asUser()->isBot()) || _editMsgId)) {
-		return AutocompleteQuery();
+	} else if (result.query[0] == '/') {
+		if (_editMsgId) {
+			return {};
+		} else if (_peer->isUser()
+			&& !_peer->asUser()->isBot()
+			&& _peer->owner().shortcutMessages().shortcuts().list.empty()) {
+			return {};
+		}
 	}
 	return result;
 }

From ea36345eee546fc4bd6c9f6a22a690d7a34f2474 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 5 Mar 2024 14:05:48 +0400
Subject: [PATCH 072/108] Show location and working hours in profile.

---
 Telegram/Resources/langs/lang.strings         |  16 +-
 .../data/business/data_business_common.cpp    |   5 +
 .../data/business/data_business_common.h      |   3 +-
 .../data/business/data_business_info.cpp      |  21 +
 .../data/business/data_business_info.h        |   3 +
 Telegram/SourceFiles/data/data_user.cpp       |   1 +
 Telegram/SourceFiles/info/info.style          |  18 +
 .../info/profile/info_profile_actions.cpp     | 457 ++++++++++++++++++
 .../business/settings_working_hours.cpp       |  27 +-
 9 files changed, 525 insertions(+), 26 deletions(-)

diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index c78969caa..96055ee66 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -1313,6 +1313,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_info_link_label" = "Link";
 "lng_info_location_label" = "Location";
 "lng_info_about_label" = "About";
+"lng_info_work_open" = "Open";
+"lng_info_work_closed" = "Closed";
+"lng_info_hours_label" = "Business hours";
+"lng_info_hours_closed" = "closed";
+"lng_info_hours_opens_in_minutes#one" = "opens in {count} minute";
+"lng_info_hours_opens_in_minutes#other" = "opens in {count} minutes";
+"lng_info_hours_opens_in_hours#one" = "opens in {count} hour";
+"lng_info_hours_opens_in_hours#other" = "opens in {count} hours";
+"lng_info_hours_opens_in_days#one" = "opens in {count} day";
+"lng_info_hours_opens_in_days#other" = "opens in {count} days";
+"lng_info_hours_open_full" = "open 24 hours";
+"lng_info_hours_next_day" = "{time} (next day)";
+"lng_info_hours_local_time" = "local time";
+"lng_info_hours_my_time" = "my time";
 "lng_info_user_title" = "User Info";
 "lng_info_bot_title" = "Bot Info";
 "lng_info_group_title" = "Group Info";
@@ -2190,7 +2204,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_hours_sunday" = "Sunday";
 "lng_hours_closed" = "Closed";
 "lng_hours_open_full" = "Open 24 hours";
-"lng_hours_next_day" = "Next day, {time}";
+"lng_hours_next_day" = "{time} (Next day)";
 "lng_hours_time_zone_title" = "Choose Time Zone";
 "lng_hours_add_button" = "Add a Set of Hours";
 "lng_hours_opening" = "Opening Time";
diff --git a/Telegram/SourceFiles/data/business/data_business_common.cpp b/Telegram/SourceFiles/data/business/data_business_common.cpp
index 34a46f3cc..14b1b0903 100644
--- a/Telegram/SourceFiles/data/business/data_business_common.cpp
+++ b/Telegram/SourceFiles/data/business/data_business_common.cpp
@@ -106,6 +106,11 @@ WorkingIntervals ExtractDayIntervals(
 	return result;
 }
 
+bool IsFullOpen(const WorkingIntervals &extractedDay) {
+	return extractedDay
+		&& (extractedDay.list.front() == WorkingInterval{ 0, kDay });
+}
+
 WorkingIntervals RemoveDayIntervals(
 		const WorkingIntervals &intervals,
 		int dayIndex) {
diff --git a/Telegram/SourceFiles/data/business/data_business_common.h b/Telegram/SourceFiles/data/business/data_business_common.h
index a47dce3a2..86422bc98 100644
--- a/Telegram/SourceFiles/data/business/data_business_common.h
+++ b/Telegram/SourceFiles/data/business/data_business_common.h
@@ -138,6 +138,7 @@ struct WorkingHours {
 [[nodiscard]] WorkingIntervals ExtractDayIntervals(
 	const WorkingIntervals &intervals,
 	int dayIndex);
+[[nodiscard]] bool IsFullOpen(const WorkingIntervals &extractedDay);
 [[nodiscard]] WorkingIntervals RemoveDayIntervals(
 	const WorkingIntervals &intervals,
 	int dayIndex);
@@ -148,7 +149,7 @@ struct WorkingHours {
 
 struct BusinessLocation {
 	QString address;
-	LocationPoint point;
+	std::optional<LocationPoint> point;
 
 	explicit operator bool() const {
 		return !address.isEmpty();
diff --git a/Telegram/SourceFiles/data/business/data_business_info.cpp b/Telegram/SourceFiles/data/business/data_business_info.cpp
index 43ef345e6..d874e52b9 100644
--- a/Telegram/SourceFiles/data/business/data_business_info.cpp
+++ b/Telegram/SourceFiles/data/business/data_business_info.cpp
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/business/data_business_info.h"
 
 #include "apiwrap.h"
+#include "base/unixtime.h"
 #include "data/business/data_business_common.h"
 #include "data/data_session.h"
 #include "data/data_user.h"
@@ -270,4 +271,24 @@ rpl::producer<Timezones> BusinessInfo::timezonesValue() const {
 	return _timezones.value();
 }
 
+QString FindClosestTimezoneId(const std::vector<Timezone> &list) {
+	const auto local = QDateTime::currentDateTime();
+	const auto utc = QDateTime(local.date(), local.time(), Qt::UTC);
+	const auto shift = base::unixtime::now() - (TimeId)::time(nullptr);
+	const auto delta = int(utc.toSecsSinceEpoch())
+		- int(local.toSecsSinceEpoch())
+		- shift;
+	const auto proj = [&](const Timezone &value) {
+		auto distance = value.utcOffset - delta;
+		while (distance > 12 * 3600) {
+			distance -= 24 * 3600;
+		}
+		while (distance < -12 * 3600) {
+			distance += 24 * 3600;
+		}
+		return std::abs(distance);
+	};
+	return ranges::min_element(list, ranges::less(), proj)->id;
+}
+
 } // namespace Data
diff --git a/Telegram/SourceFiles/data/business/data_business_info.h b/Telegram/SourceFiles/data/business/data_business_info.h
index 3b747eb0f..adea50a17 100644
--- a/Telegram/SourceFiles/data/business/data_business_info.h
+++ b/Telegram/SourceFiles/data/business/data_business_info.h
@@ -55,4 +55,7 @@ private:
 
 };
 
+[[nodiscard]] QString FindClosestTimezoneId(
+	const std::vector<Timezone> &list);
+
 } // namespace Data
diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp
index daa9bb593..d670d9308 100644
--- a/Telegram/SourceFiles/data/data_user.cpp
+++ b/Telegram/SourceFiles/data/data_user.cpp
@@ -280,6 +280,7 @@ const Data::BusinessDetails &UserData::businessDetails() const {
 }
 
 void UserData::setBusinessDetails(Data::BusinessDetails details) {
+	details.hours = details.hours.normalized();
 	if ((!details && !_businessDetails)
 		|| (details && _businessDetails && details == *_businessDetails)) {
 		return;
diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style
index d53d12f94..19162c400 100644
--- a/Telegram/SourceFiles/info/info.style
+++ b/Telegram/SourceFiles/info/info.style
@@ -1008,3 +1008,21 @@ similarChannelsLockAbout: FlatLabel(defaultFlatLabel) {
 	minWidth: 128px;
 }
 similarChannelsLockAboutPadding: margins(12px, 12px, 12px, 12px);
+
+infoHoursState: FlatLabel(infoLabeled) {
+	minWidth: 0px;
+}
+infoHoursValue: FlatLabel(infoHoursState) {
+	textFg: windowSubTextFg;
+	align: align(topright);
+}
+infoHoursDayLabel: infoHoursState;
+infoHoursOuter: RoundButton(defaultActiveButton) {
+	textBg: transparent;
+	textBgOver: transparent;
+	ripple: RippleAnimation(defaultRippleAnimation) {
+		color: windowBgOver;
+	}
+}
+infoHoursOuterMargin: margins(8px, 4px, 8px, 4px);
+infoHoursDaySkip: 6px;
diff --git a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp
index eba90de15..f0d690b51 100644
--- a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp
+++ b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp
@@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "api/api_chat_participants.h"
 #include "base/options.h"
+#include "base/timer_rpl.h"
+#include "base/unixtime.h"
+#include "data/business/data_business_common.h"
+#include "data/business/data_business_info.h"
 #include "data/data_peer_values.h"
 #include "data/data_session.h"
 #include "data/data_folder.h"
@@ -71,6 +75,8 @@ namespace Info {
 namespace Profile {
 namespace {
 
+constexpr auto kDay = Data::WorkingInterval::kDay;
+
 base::options::toggle ShowPeerIdBelowAbout({
 	.id = kOptionShowPeerIdBelowAbout,
 	.name = "Show Peer IDs in Profile",
@@ -159,6 +165,435 @@ base::options::toggle ShowPeerIdBelowAbout({
 	});
 }
 
+[[nodiscard]] bool AreNonTrivialHours(const Data::WorkingHours &hours) {
+	if (!hours) {
+		return false;
+	}
+	const auto &intervals = hours.intervals.list;
+	for (auto i = 0; i != 7; ++i) {
+		const auto day = Data::WorkingInterval{ i * kDay, (i + 1) * kDay };
+		for (const auto &interval : intervals) {
+			const auto intersection = interval.intersected(day);
+			if (intersection && intersection != day) {
+				return true;
+			}
+		}
+	}
+	return false;
+}
+
+[[nodiscard]] TimeId OpensIn(
+		const Data::WorkingIntervals &intervals,
+		TimeId now) {
+	using namespace Data;
+
+	while (now < 0) {
+		now += WorkingInterval::kWeek;
+	}
+	while (now > WorkingInterval::kWeek) {
+		now -= WorkingInterval::kWeek;
+	}
+	auto closest = WorkingInterval::kWeek;
+	for (const auto &interval : intervals.list) {
+		if (interval.start <= now && interval.end > now) {
+			return TimeId(0);
+		} else if (interval.start > now && interval.start - now < closest) {
+			closest = interval.start - now;
+		} else if (interval.start < now) {
+			const auto next = interval.start + WorkingInterval::kWeek - now;
+			if (next < closest) {
+				closest = next;
+			}
+		}
+	}
+	return closest;
+}
+
+[[nodiscard]] rpl::producer<QString> OpensInText(
+		rpl::producer<TimeId> in,
+		rpl::producer<QString> fallback) {
+	return rpl::combine(
+		std::move(in),
+		std::move(fallback)
+	) | rpl::map([](TimeId in, QString fallback) {
+		return !in
+			? std::move(fallback)
+			: (in >= 86400)
+			? tr::lng_info_hours_opens_in_days(tr::now, lt_count, in / 86400)
+			: (in >= 3600)
+			? tr::lng_info_hours_opens_in_hours(tr::now, lt_count, in / 3600)
+			: tr::lng_info_hours_opens_in_minutes(
+				tr::now,
+				lt_count,
+				std::max(in / 60, 1));
+	});
+}
+
+[[nodiscard]] QString FormatDayTime(TimeId time) {
+	const auto wrap = [](TimeId value) {
+		const auto hours = value / 3600;
+		const auto minutes = (value % 3600) / 60;
+		return QString::number(hours).rightJustified(2, u'0')
+			+ ':'
+			+ QString::number(minutes).rightJustified(2, u'0');
+	};
+	return (time > kDay)
+		? tr::lng_info_hours_next_day(tr::now, lt_time, wrap(time - kDay))
+		: wrap(time == kDay ? 0 : time);
+}
+
+[[nodiscard]] QString JoinIntervals(const Data::WorkingIntervals &data) {
+	auto result = QStringList();
+	result.reserve(data.list.size());
+	for (const auto &interval : data.list) {
+		const auto start = FormatDayTime(interval.start);
+		const auto end = FormatDayTime(interval.end);
+		result.push_back(start + u" - "_q + end);
+	}
+	return result.join('\n');
+}
+
+[[nodiscard]] QString FormatDayHours(
+		const Data::WorkingHours &hours,
+		const Data::WorkingIntervals &mine,
+		bool my,
+		int day) {
+	using namespace Data;
+
+	const auto local = ExtractDayIntervals(hours.intervals, day);
+	if (IsFullOpen(local)) {
+		return tr::lng_info_hours_open_full(tr::now);
+	}
+	const auto use = my ? ExtractDayIntervals(mine, day) : local;
+	if (!use) {
+		return tr::lng_info_hours_closed(tr::now);
+	}
+	return JoinIntervals(use);
+}
+
+[[nodiscard]] Data::WorkingIntervals ShiftedIntervals(
+		Data::WorkingIntervals intervals,
+		int delta) {
+	auto &list = intervals.list;
+	if (!delta || list.empty()) {
+		return { std::move(list) };
+	}
+	for (auto &interval : list) {
+		interval.start += delta;
+		interval.end += delta;
+	}
+	while (list.front().start < 0) {
+		constexpr auto kWeek = Data::WorkingInterval::kWeek;
+		const auto first = list.front();
+		if (first.end > 0) {
+			list.push_back({ first.start + kWeek, kWeek });
+			list.front().start = 0;
+		} else {
+			list.push_back(first.shifted(kWeek));
+			list.erase(list.begin());
+		}
+	}
+	return intervals.normalized();
+}
+
+[[nodiscard]] object_ptr<Ui::SlideWrap<>> CreateWorkingHours(
+		not_null<QWidget*> parent,
+		not_null<UserData*> user) {
+	using namespace Data;
+
+	auto result = object_ptr<Ui::SlideWrap<Ui::RoundButton>>(
+		parent,
+		object_ptr<Ui::RoundButton>(
+			parent,
+			rpl::single(QString()),
+			st::infoHoursOuter),
+		st::infoProfileLabeledPadding - st::infoHoursOuterMargin);
+	const auto button = result->entity();
+	const auto inner = Ui::CreateChild<Ui::VerticalLayout>(button);
+	button->widthValue() | rpl::start_with_next([=](int width) {
+		const auto margin = st::infoHoursOuterMargin;
+		inner->resizeToWidth(width - margin.left() - margin.right());
+		inner->move(margin.left(), margin.top());
+	}, inner->lifetime());
+	inner->heightValue() | rpl::start_with_next([=](int height) {
+		const auto margin = st::infoHoursOuterMargin;
+		height += margin.top() + margin.bottom();
+		button->resize(button->width(), height);
+	}, inner->lifetime());
+
+	const auto info = &user->owner().businessInfo();
+
+	struct State {
+		rpl::variable<WorkingHours> hours;
+		rpl::variable<TimeId> time;
+		rpl::variable<int> day;
+		rpl::variable<int> timezoneDelta;
+
+		rpl::variable<WorkingIntervals> mine;
+		rpl::variable<WorkingIntervals> mineByDays;
+		rpl::variable<TimeId> opensIn;
+		rpl::variable<bool> opened;
+		rpl::variable<bool> expanded;
+		rpl::variable<bool> nonTrivial;
+		rpl::variable<bool> myTimezone;
+
+		rpl::event_stream<> recounts;
+	};
+	const auto state = inner->lifetime().make_state<State>();
+
+	auto recounts = state->recounts.events_starting_with_copy(rpl::empty);
+	const auto recount = [=] {
+		state->recounts.fire({});
+	};
+
+	state->hours = user->session().changes().peerFlagsValue(
+		user,
+		PeerUpdate::Flag::BusinessDetails
+	) | rpl::map([=] {
+		return user->businessDetails().hours;
+	});
+	state->nonTrivial = state->hours.value() | rpl::map(AreNonTrivialHours);
+
+	const auto seconds = QTime::currentTime().msecsSinceStartOfDay() / 1000;
+	const auto inMinute = seconds % 60;
+	const auto firstTick = inMinute ? (61 - inMinute) : 1;
+	state->time = rpl::single(rpl::empty) | rpl::then(
+		base::timer_once(firstTick * crl::time(1000))
+	) | rpl::then(
+		base::timer_each(60 * crl::time(1000))
+	) | rpl::map([] {
+		const auto local = QDateTime::currentDateTime();
+		const auto day = local.date().dayOfWeek() - 1;
+		const auto seconds = local.time().msecsSinceStartOfDay() / 1000;
+		return day * kDay + seconds;
+	});
+
+	state->day = state->time.value() | rpl::map([](TimeId time) {
+		return time / kDay;
+	});
+	state->timezoneDelta = rpl::combine(
+		state->hours.value(),
+		info->timezonesValue()
+	) | rpl::filter([](
+			const WorkingHours &hours,
+			const Timezones &timezones) {
+		return ranges::contains(
+			timezones.list,
+			hours.timezoneId,
+			&Timezone::id);
+	}) | rpl::map([](WorkingHours &&hours, const Timezones &timezones) {
+		const auto &list = timezones.list;
+		const auto closest = FindClosestTimezoneId(list);
+		const auto i = ranges::find(list, closest, &Timezone::id);
+		const auto j = ranges::find(list, hours.timezoneId, &Timezone::id);
+		Assert(i != end(list));
+		Assert(j != end(list));
+		return i->utcOffset - j->utcOffset;
+	});
+
+	state->mine = rpl::combine(
+		state->hours.value(),
+		state->timezoneDelta.value()
+	) | rpl::map([](WorkingHours &&hours, int delta) {
+		return ShiftedIntervals(hours.intervals, delta);
+	});
+
+	state->opensIn = rpl::combine(
+		state->mine.value(),
+		state->time.value()
+	) | rpl::map([](const WorkingIntervals &mine, TimeId time) {
+		return OpensIn(mine, time);
+	});
+	state->opened = state->opensIn.value() | rpl::map(rpl::mappers::_1 == 0);
+
+	state->mineByDays = rpl::combine(
+		state->hours.value(),
+		state->timezoneDelta.value()
+	) | rpl::map([](WorkingHours &&hours, int delta) {
+		auto full = std::array<bool, 7>();
+		auto withoutFullDays = hours.intervals;
+		for (auto i = 0; i != 7; ++i) {
+			if (IsFullOpen(ExtractDayIntervals(hours.intervals, i))) {
+				full[i] = true;
+				withoutFullDays = ReplaceDayIntervals(
+					withoutFullDays,
+					i,
+					Data::WorkingIntervals());
+			}
+		}
+		auto result = ShiftedIntervals(withoutFullDays, delta);
+		for (auto i = 0; i != 7; ++i) {
+			if (full[i]) {
+				result = ReplaceDayIntervals(
+					result,
+					i,
+					Data::WorkingIntervals{ { { 0, kDay } } });
+			}
+		}
+		return result;
+	});
+
+	const auto dayHoursText = [=](int day) {
+		return rpl::combine(
+			state->hours.value(),
+			state->mineByDays.value(),
+			state->myTimezone.value()
+		) | rpl::map([=](
+				const WorkingHours &hours,
+				const WorkingIntervals &mine,
+				bool my) {
+			return FormatDayHours(hours, mine, my, day);
+		});
+	};
+	const auto dayHoursTextValue = [=](rpl::producer<int> day) {
+		return std::move(day)
+			| rpl::map(dayHoursText)
+			| rpl::flatten_latest();
+	};
+
+	const auto openedWrap = inner->add(object_ptr<Ui::RpWidget>(inner));
+	const auto opened = Ui::CreateChild<Ui::FlatLabel>(
+		openedWrap,
+		rpl::conditional(
+			state->opened.value(),
+			tr::lng_info_work_open(),
+			tr::lng_info_work_closed()
+		) | rpl::after_next(recount),
+		st::infoHoursState);
+	opened->setAttribute(Qt::WA_TransparentForMouseEvents);
+	const auto timing = Ui::CreateChild<Ui::FlatLabel>(
+		openedWrap,
+		OpensInText(
+			state->opensIn.value(),
+			dayHoursTextValue(state->day.value())
+		) | rpl::after_next(recount),
+		st::infoHoursValue);
+	timing->setAttribute(Qt::WA_TransparentForMouseEvents);
+	state->opened.value() | rpl::start_with_next([=](bool value) {
+		opened->setTextColorOverride(value
+			? st::boxTextFgGood->c
+			: st::boxTextFgError->c);
+	}, opened->lifetime());
+
+	rpl::combine(
+		openedWrap->widthValue(),
+		opened->heightValue(),
+		timing->sizeValue()
+	) | rpl::start_with_next([=](int width, int h1, QSize size) {
+		opened->moveToLeft(0, 0, width);
+		timing->moveToRight(0, 0, width);
+
+		const auto margins = opened->getMargins();
+		const auto added = margins.top() + margins.bottom();
+		openedWrap->resize(width, std::max(h1, size.height()) - added);
+	}, openedWrap->lifetime());
+
+	const auto labelWrap = inner->add(object_ptr<Ui::RpWidget>(inner));
+	const auto label = Ui::CreateChild<Ui::FlatLabel>(
+		labelWrap,
+		tr::lng_info_hours_label(),
+		st::infoLabel);
+	label->setAttribute(Qt::WA_TransparentForMouseEvents);
+	const auto link = Ui::CreateChild<Ui::LinkButton>(
+		labelWrap,
+		QString());
+	rpl::combine(
+		state->nonTrivial.value(),
+		state->hours.value(),
+		state->mine.value(),
+		state->myTimezone.value()
+	) | rpl::map([=](
+			bool complex,
+			const WorkingHours &hours,
+			const WorkingIntervals &mine,
+			bool my) {
+		return (!complex || hours.intervals == mine)
+			? rpl::single(QString())
+			: my
+			? tr::lng_info_hours_my_time()
+			: tr::lng_info_hours_local_time();
+	}) | rpl::flatten_latest(
+	) | rpl::start_with_next([=](const QString &text) {
+		link->setText(text);
+	}, link->lifetime());
+	link->setClickedCallback([=] {
+		state->myTimezone = !state->myTimezone.current();
+	});
+
+	rpl::combine(
+		labelWrap->widthValue(),
+		label->heightValue(),
+		link->sizeValue()
+	) | rpl::start_with_next([=](int width, int h1, QSize size) {
+		label->moveToLeft(0, 0, width);
+		link->moveToRight(0, 0, width);
+
+		const auto margins = label->getMargins();
+		const auto added = margins.top() + margins.bottom();
+		labelWrap->resize(width, std::max(h1, size.height()) - added);
+	}, labelWrap->lifetime());
+
+	const auto other = inner->add(
+		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
+			inner,
+			object_ptr<Ui::VerticalLayout>(inner)));
+	other->toggleOn(state->expanded.value(), anim::type::normal);
+	other->finishAnimating();
+	const auto days = other->entity();
+
+	for (auto i = 1; i != 7; ++i) {
+		const auto dayWrap = days->add(
+			object_ptr<Ui::RpWidget>(other),
+			QMargins(0, st::infoHoursDaySkip, 0, 0));
+		auto label = state->day.value() | rpl::map([=](int day) {
+			switch ((day + i) % 7) {
+			case 0: return tr::lng_hours_monday();
+			case 1: return tr::lng_hours_tuesday();
+			case 2: return tr::lng_hours_wednesday();
+			case 3: return tr::lng_hours_thursday();
+			case 4: return tr::lng_hours_friday();
+			case 5: return tr::lng_hours_saturday();
+			case 6: return tr::lng_hours_sunday();
+			}
+			Unexpected("Index in working hours.");
+		}) | rpl::flatten_latest();
+		const auto dayLabel = Ui::CreateChild<Ui::FlatLabel>(
+			dayWrap,
+			std::move(label),
+			st::infoHoursDayLabel);
+		dayLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
+		const auto dayHours = Ui::CreateChild<Ui::FlatLabel>(
+			dayWrap,
+			dayHoursTextValue(state->day.value()
+				| rpl::map((rpl::mappers::_1 + i) % 7)),
+			st::infoHoursValue);
+		dayHours->setAttribute(Qt::WA_TransparentForMouseEvents);
+		rpl::combine(
+			dayWrap->widthValue(),
+			dayLabel->heightValue(),
+			dayHours->sizeValue()
+		) | rpl::start_with_next([=](int width, int h1, QSize size) {
+			dayLabel->moveToLeft(0, 0, width);
+			dayHours->moveToRight(0, 0, width);
+
+			const auto margins = dayLabel->getMargins();
+			const auto added = margins.top() + margins.bottom();
+			dayWrap->resize(width, std::max(h1, size.height()) - added);
+		}, dayWrap->lifetime());
+	}
+
+	button->setClickedCallback([=] {
+		state->expanded = !state->expanded.current();
+	});
+
+	result->toggleOn(state->hours.value(
+	) | rpl::map([](const WorkingHours &data) {
+		return bool(data);
+	}));
+
+	return result;
+}
+
 template <typename Text, typename ToggleOn, typename Callback>
 auto AddActionButton(
 		not_null<Ui::VerticalLayout*> parent,
@@ -563,6 +998,28 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
 				}
 				return false;
 			});
+		} else {
+			tracker.track(result->add(CreateWorkingHours(result, user)));
+
+			auto locationText = user->session().changes().peerFlagsValue(
+				user,
+				Data::PeerUpdate::Flag::BusinessDetails
+			) | rpl::map([=] {
+				const auto &details = user->businessDetails();
+				if (!details.location) {
+					return TextWithEntities();
+				} else if (!details.location.point) {
+					return TextWithEntities{ details.location.address };
+				}
+				return Ui::Text::Link(
+					TextUtilities::SingleLine(details.location.address),
+					LocationClickHandler::Url(*details.location.point));
+			});
+			addInfoOneLine(
+				tr::lng_info_location_label(),
+				std::move(locationText),
+				QString()
+			).text->setLinksTrusted();
 		}
 
 		AddMainButton(
diff --git a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
index dd6c54b66..bae1b6b3d 100644
--- a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
@@ -70,27 +70,6 @@ private:
 	return prefix + ' ' + data.name;
 }
 
-[[nodiscard]] QString FindClosestTimezoneId(
-		const std::vector<Data::Timezone> &list) {
-	const auto local = QDateTime::currentDateTime();
-	const auto utc = QDateTime(local.date(), local.time(), Qt::UTC);
-	const auto shift = base::unixtime::now() - (TimeId)::time(nullptr);
-	const auto delta = int(utc.toSecsSinceEpoch())
-		- int(local.toSecsSinceEpoch())
-		- shift;
-	const auto proj = [&](const Data::Timezone &value) {
-		auto distance = value.utcOffset - delta;
-		while (distance > 12 * 3600) {
-			distance -= 24 * 3600;
-		}
-		while (distance < -12 * 3600) {
-			distance += 24 * 3600;
-		}
-		return std::abs(distance);
-	};
-	return ranges::min_element(list, ranges::less(), proj)->id;
-}
-
 [[nodiscard]] QString FormatDayTime(
 		TimeId time,
 		bool showEndAsNextDay = false) {
@@ -372,7 +351,7 @@ void ChooseTimezoneBox(
 	});
 
 	if (!ranges::contains(list, id, &Data::Timezone::id)) {
-		id = FindClosestTimezoneId(list);
+		id = Data::FindClosestTimezoneId(list);
 	}
 	const auto i = ranges::find(list, id, &Data::Timezone::id);
 	const auto value = int(i - begin(list));
@@ -472,7 +451,7 @@ void AddWeekButton(
 		}
 		if (!intervals) {
 			return tr::lng_hours_closed();
-		} else if (intervals.list.front() == WorkingInterval{ 0, kDay }) {
+		} else if (IsFullOpen(intervals)) {
 			return tr::lng_hours_open_full();
 		}
 		return rpl::single(JoinIntervals(intervals));
@@ -613,7 +592,7 @@ void WorkingHours::setupContent(
 		const auto now = _hours.current().timezoneId;
 		if (!ranges::contains(value.list, now, &Data::Timezone::id)) {
 			auto copy = _hours.current();
-			copy.timezoneId = FindClosestTimezoneId(value.list);
+			copy.timezoneId = Data::FindClosestTimezoneId(value.list);
 			_hours = std::move(copy);
 		}
 	}, inner->lifetime());

From e3f6c189a72f61723d1cc9618597f670a5837391 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 5 Mar 2024 20:52:14 +0400
Subject: [PATCH 073/108] Implement preview and save of chatbots.

---
 Telegram/Resources/langs/lang.strings         |   5 +-
 .../data/business/data_business_chatbots.cpp  |  67 +++-
 .../data/business/data_business_chatbots.h    |  16 +-
 .../data/business/data_business_common.cpp    | 124 ++++++++
 .../data/business/data_business_common.h      |  20 ++
 .../data/business/data_business_info.cpp      |  51 ---
 Telegram/SourceFiles/data/data_user.cpp       |  96 ------
 .../info/profile/info_profile_actions.cpp     |   8 +-
 .../business/settings_away_message.cpp        |   4 +-
 .../settings/business/settings_chatbots.cpp   | 297 +++++++++++++++++-
 .../settings/business/settings_greeting.cpp   |   4 +-
 Telegram/SourceFiles/settings/settings.style  |   8 +-
 .../settings/settings_business.cpp            |  12 +-
 13 files changed, 536 insertions(+), 176 deletions(-)

diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 96055ee66..e8366f8f7 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -2220,7 +2220,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_replies_add_placeholder" = "Shortcut";
 "lng_replies_add_exists" = "This shortcut already exists.";
 "lng_replies_empty_title" = "New Quick Reply";
-"lng_replies_empty_about" = "Enter a message below that will be sent in chat when you type {shortcut}.\n\nYou can access Quick Replies in any chat by typing / or using Attachment menu.";
+"lng_replies_empty_about" = "Enter a message below that will be sent in chat when you type {shortcut}.\n\nYou can access Quick Replies in any chat by typing /.";
 "lng_replies_remove_title" = "Remove Shortcut";
 "lng_replies_remove_text" = "You didn't create a quick reply message. Do you want to remove the shortcut?";
 "lng_replies_edit_title" = "Edit Shortcut";
@@ -2241,6 +2241,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_greeting_empty_about" = "Create greetings that will be automatically sent to new customers.";
 "lng_greeting_message_placeholder" = "Add a Greeting";
 "lng_greeting_limit_reached" = "You have too many quick replies. Remove one to add a greeting message.";
+"lng_greeting_recipients_empty" = "Please choose at least one recipient.";
 
 "lng_away_title" = "Away Message";
 "lng_away_about" = "Automatically reply with a message when you are away.";
@@ -2282,7 +2283,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_chatbots_reply" = "Reply to Messages";
 "lng_chatbots_reply_about" = "The bot will be able to view all new incoming messages, but not the messages that had been sent before you added the bot.";
 "lng_chatbots_remove" = "Remove Bot";
-"lng_chatbots_not_found" = "Chatbot not found";
+"lng_chatbots_not_found" = "Chatbot not found.";
 "lng_chatbots_add" = "Add";
 
 "lng_boost_channel_button" = "Boost Channel";
diff --git a/Telegram/SourceFiles/data/business/data_business_chatbots.cpp b/Telegram/SourceFiles/data/business/data_business_chatbots.cpp
index 89215a2e0..8862c4e84 100644
--- a/Telegram/SourceFiles/data/business/data_business_chatbots.cpp
+++ b/Telegram/SourceFiles/data/business/data_business_chatbots.cpp
@@ -7,14 +7,46 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "data/business/data_business_chatbots.h"
 
+#include "apiwrap.h"
+#include "data/business/data_business_common.h"
+#include "data/business/data_business_info.h"
+#include "data/data_session.h"
+#include "data/data_user.h"
+#include "main/main_session.h"
+
 namespace Data {
 
-Chatbots::Chatbots(not_null<Session*> session)
-: _session(session) {
+Chatbots::Chatbots(not_null<Session*> owner)
+: _owner(owner) {
 }
 
 Chatbots::~Chatbots() = default;
 
+void Chatbots::preload() {
+	if (_loaded || _requestId) {
+		return;
+	}
+	_requestId = _owner->session().api().request(
+		MTPaccount_GetConnectedBots()
+	).done([=](const MTPaccount_ConnectedBots &result) {
+		_requestId = 0;
+		_loaded = true;
+
+		const auto &data = result.data();
+		_owner->processUsers(data.vusers());
+		const auto &list = data.vconnected_bots().v;
+		if (!list.isEmpty()) {
+			const auto &bot = list.front().data();
+			const auto botId = bot.vbot_id().v;
+			_settings = ChatbotsSettings{
+				.bot = _owner->session().data().user(botId),
+				.recipients = FromMTP(_owner, bot.vrecipients()),
+				.repliesAllowed = bot.is_can_reply(),
+			};
+		}
+	}).send();
+}
+
 const ChatbotsSettings &Chatbots::current() const {
 	return _settings.current();
 }
@@ -27,7 +59,36 @@ rpl::producer<ChatbotsSettings> Chatbots::value() const {
 	return _settings.value();
 }
 
-void Chatbots::save(ChatbotsSettings settings, Fn<void(QString)> fail) {
+void Chatbots::save(
+		ChatbotsSettings settings,
+		Fn<void()> done,
+		Fn<void(QString)> fail) {
+	const auto was = _settings.current();
+	if (was == settings) {
+		return;
+	} else if (was.bot || settings.bot) {
+		using Flag = MTPaccount_UpdateConnectedBot::Flag;
+		const auto api = &_owner->session().api();
+		api->request(MTPaccount_UpdateConnectedBot(
+			MTP_flags(!settings.bot
+				? Flag::f_deleted
+				: settings.repliesAllowed
+				? Flag::f_can_reply
+				: Flag()),
+			(settings.bot ? settings.bot : was.bot)->inputUser,
+			ToMTP(settings.recipients)
+		)).done([=](const MTPUpdates &result) {
+			api->applyUpdates(result);
+			if (done) {
+				done();
+			}
+		}).fail([=](const MTP::Error &error) {
+			_settings = was;
+			if (fail) {
+				fail(error.type());
+			}
+		}).send();
+	}
 	_settings = settings;
 }
 
diff --git a/Telegram/SourceFiles/data/business/data_business_chatbots.h b/Telegram/SourceFiles/data/business/data_business_chatbots.h
index da088c394..ca21baef6 100644
--- a/Telegram/SourceFiles/data/business/data_business_chatbots.h
+++ b/Telegram/SourceFiles/data/business/data_business_chatbots.h
@@ -19,23 +19,33 @@ struct ChatbotsSettings {
 	UserData *bot = nullptr;
 	BusinessRecipients recipients;
 	bool repliesAllowed = false;
+
+	friend inline bool operator==(
+		const ChatbotsSettings &,
+		const ChatbotsSettings &) = default;
 };
 
 class Chatbots final {
 public:
-	explicit Chatbots(not_null<Session*> session);
+	explicit Chatbots(not_null<Session*> owner);
 	~Chatbots();
 
+	void preload();
 	[[nodiscard]] const ChatbotsSettings &current() const;
 	[[nodiscard]] rpl::producer<ChatbotsSettings> changes() const;
 	[[nodiscard]] rpl::producer<ChatbotsSettings> value() const;
 
-	void save(ChatbotsSettings settings, Fn<void(QString)> fail);
+	void save(
+		ChatbotsSettings settings,
+		Fn<void()> done,
+		Fn<void(QString)> fail);
 
 private:
-	const not_null<Session*> _session;
+	const not_null<Session*> _owner;
 
 	rpl::variable<ChatbotsSettings> _settings;
+	mtpRequestId _requestId = 0;
+	bool _loaded = false;
 
 };
 
diff --git a/Telegram/SourceFiles/data/business/data_business_common.cpp b/Telegram/SourceFiles/data/business/data_business_common.cpp
index 14b1b0903..7da2970db 100644
--- a/Telegram/SourceFiles/data/business/data_business_common.cpp
+++ b/Telegram/SourceFiles/data/business/data_business_common.cpp
@@ -7,6 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "data/business/data_business_common.h"
 
+#include "data/data_session.h"
+#include "data/data_user.h"
+
 namespace Data {
 namespace {
 
@@ -50,6 +53,127 @@ constexpr auto kInNextDayMax = WorkingInterval::kInNextDayMax;
 
 } // namespace
 
+MTPInputBusinessRecipients ToMTP(
+		const BusinessRecipients &data) {
+	using Flag = MTPDinputBusinessRecipients::Flag;
+	using Type = BusinessChatType;
+	const auto &chats = data.allButExcluded
+		? data.excluded
+		: data.included;
+	const auto flags = Flag()
+		| ((chats.types & Type::NewChats) ? Flag::f_new_chats : Flag())
+		| ((chats.types & Type::ExistingChats)
+			? Flag::f_existing_chats
+			: Flag())
+		| ((chats.types & Type::Contacts) ? Flag::f_contacts : Flag())
+		| ((chats.types & Type::NonContacts) ? Flag::f_non_contacts : Flag())
+		| (chats.list.empty() ? Flag() : Flag::f_users)
+		| (data.allButExcluded ? Flag::f_exclude_selected : Flag());
+	const auto &users = data.allButExcluded
+		? data.excluded
+		: data.included;
+	return MTP_inputBusinessRecipients(
+		MTP_flags(flags),
+		MTP_vector_from_range(users.list
+			| ranges::views::transform(&UserData::inputUser)));
+}
+
+BusinessRecipients FromMTP(
+		not_null<Session*> owner,
+		const MTPBusinessRecipients &recipients) {
+	using Type = BusinessChatType;
+
+	const auto &data = recipients.data();
+	auto result = BusinessRecipients{
+		.allButExcluded = data.is_exclude_selected(),
+	};
+	auto &chats = result.allButExcluded
+		? result.excluded
+		: result.included;
+	chats.types = Type()
+		| (data.is_new_chats() ? Type::NewChats : Type())
+		| (data.is_existing_chats() ? Type::ExistingChats : Type())
+		| (data.is_contacts() ? Type::Contacts : Type())
+		| (data.is_non_contacts() ? Type::NonContacts : Type());
+	if (const auto users = data.vusers()) {
+		for (const auto &userId : users->v) {
+			chats.list.push_back(owner->user(UserId(userId.v)));
+		}
+	}
+	return result;
+}
+
+[[nodiscard]] BusinessDetails FromMTP(
+		const tl::conditional<MTPBusinessWorkHours> &hours,
+		const tl::conditional<MTPBusinessLocation> &location) {
+	auto result = BusinessDetails();
+	if (hours) {
+		const auto &data = hours->data();
+		result.hours.timezoneId = qs(data.vtimezone_id());
+		result.hours.intervals.list = ranges::views::all(
+			data.vweekly_open().v
+		) | ranges::views::transform([](const MTPBusinessWeeklyOpen &open) {
+			const auto &data = open.data();
+			return WorkingInterval{
+				data.vstart_minute().v * 60,
+				data.vend_minute().v * 60,
+			};
+		}) | ranges::to_vector;
+	}
+	if (location) {
+		const auto &data = location->data();
+		result.location.address = qs(data.vaddress());
+		if (const auto point = data.vgeo_point()) {
+			point->match([&](const MTPDgeoPoint &data) {
+				result.location.point = LocationPoint(data);
+			}, [&](const MTPDgeoPointEmpty &) {
+			});
+		}
+	}
+	return result;
+}
+
+[[nodiscard]] AwaySettings FromMTP(
+		not_null<Session*> owner,
+		const tl::conditional<MTPBusinessAwayMessage> &message) {
+	if (!message) {
+		return AwaySettings();
+	}
+	const auto &data = message->data();
+	auto result = AwaySettings{
+		.recipients = FromMTP(owner, data.vrecipients()),
+		.shortcutId = data.vshortcut_id().v,
+		.offlineOnly = data.is_offline_only(),
+	};
+	data.vschedule().match([&](
+			const MTPDbusinessAwayMessageScheduleAlways &) {
+		result.schedule.type = AwayScheduleType::Always;
+	}, [&](const MTPDbusinessAwayMessageScheduleOutsideWorkHours &) {
+		result.schedule.type = AwayScheduleType::OutsideWorkingHours;
+	}, [&](const MTPDbusinessAwayMessageScheduleCustom &data) {
+		result.schedule.type = AwayScheduleType::Custom;
+		result.schedule.customInterval = WorkingInterval{
+			data.vstart_date().v,
+			data.vend_date().v,
+		};
+	});
+	return result;
+}
+
+[[nodiscard]] GreetingSettings FromMTP(
+		not_null<Session*> owner,
+		const tl::conditional<MTPBusinessGreetingMessage> &message) {
+	if (!message) {
+		return GreetingSettings();
+	}
+	const auto &data = message->data();
+	return GreetingSettings{
+		.recipients = FromMTP(owner, data.vrecipients()),
+		.noActivityDays = data.vno_activity_days().v,
+		.shortcutId = data.vshortcut_id().v,
+	};
+}
+
 WorkingIntervals WorkingIntervals::normalized() const {
 	return SortAndMerge(MoveTailToFront(SortAndMerge(*this)));
 }
diff --git a/Telegram/SourceFiles/data/business/data_business_common.h b/Telegram/SourceFiles/data/business/data_business_common.h
index 86422bc98..dd66bb770 100644
--- a/Telegram/SourceFiles/data/business/data_business_common.h
+++ b/Telegram/SourceFiles/data/business/data_business_common.h
@@ -14,6 +14,8 @@ class UserData;
 
 namespace Data {
 
+class Session;
+
 enum class BusinessChatType {
 	NewChats = (1 << 0),
 	ExistingChats = (1 << 1),
@@ -43,6 +45,12 @@ struct BusinessRecipients {
 		const BusinessRecipients &b) = default;
 };
 
+[[nodiscard]] MTPInputBusinessRecipients ToMTP(
+	const BusinessRecipients &data);
+[[nodiscard]] BusinessRecipients FromMTP(
+	not_null<Session*> owner,
+	const MTPBusinessRecipients &recipients);
+
 struct Timezone {
 	QString id;
 	QString name;
@@ -173,6 +181,10 @@ struct BusinessDetails {
 		const BusinessDetails &b) = default;
 };
 
+[[nodiscard]] BusinessDetails FromMTP(
+	const tl::conditional<MTPBusinessWorkHours> &hours,
+	const tl::conditional<MTPBusinessLocation> &location);
+
 enum class AwayScheduleType : uchar {
 	Never = 0,
 	Always = 1,
@@ -204,6 +216,10 @@ struct AwaySettings {
 		const AwaySettings &b) = default;
 };
 
+[[nodiscard]] AwaySettings FromMTP(
+	not_null<Session*> owner,
+	const tl::conditional<MTPBusinessAwayMessage> &message);
+
 struct GreetingSettings {
 	BusinessRecipients recipients;
 	int noActivityDays = 0;
@@ -218,4 +234,8 @@ struct GreetingSettings {
 		const GreetingSettings &b) = default;
 };
 
+[[nodiscard]] GreetingSettings FromMTP(
+	not_null<Session*> owner,
+	const tl::conditional<MTPBusinessGreetingMessage> &message);
+
 } // namespace Data
diff --git a/Telegram/SourceFiles/data/business/data_business_info.cpp b/Telegram/SourceFiles/data/business/data_business_info.cpp
index d874e52b9..b6cf5ac36 100644
--- a/Telegram/SourceFiles/data/business/data_business_info.cpp
+++ b/Telegram/SourceFiles/data/business/data_business_info.cpp
@@ -30,57 +30,6 @@ namespace {
 		MTP_vector_from_range(list | ranges::views::transform(proj)));
 }
 
-[[nodiscard]] MTPInputBusinessRecipients ToMTP(
-		const BusinessRecipients &data) {
-	//MTP_flags(RecipientsFlags(data.recipients, Flag())),
-	//	MTP_vector_from_range(
-	//		(data.recipients.allButExcluded
-	//			? data.recipients.excluded
-	//			: data.recipients.included).list
-	//		| ranges::views::transform(&UserData::inputUser)),
-
-	using Flag = MTPDinputBusinessRecipients::Flag;
-	using Type = BusinessChatType;
-	const auto &chats = data.allButExcluded
-		? data.excluded
-		: data.included;
-	const auto flags = Flag()
-		| ((chats.types & Type::NewChats) ? Flag::f_new_chats : Flag())
-		| ((chats.types & Type::ExistingChats)
-			? Flag::f_existing_chats
-			: Flag())
-		| ((chats.types & Type::Contacts) ? Flag::f_contacts : Flag())
-		| ((chats.types & Type::NonContacts) ? Flag::f_non_contacts : Flag())
-		| (chats.list.empty() ? Flag() : Flag::f_users)
-		| (data.allButExcluded ? Flag::f_exclude_selected : Flag());
-	const auto &users = data.allButExcluded
-		? data.excluded
-		: data.included;
-	return MTP_inputBusinessRecipients(
-		MTP_flags(flags),
-		MTP_vector_from_range(users.list
-			| ranges::views::transform(&UserData::inputUser)));
-}
-
-template <typename Flag>
-[[nodiscard]] auto RecipientsFlags(
-		const BusinessRecipients &data,
-		Flag) {
-	using Type = BusinessChatType;
-	const auto &chats = data.allButExcluded
-		? data.excluded
-		: data.included;
-	return Flag()
-		| ((chats.types & Type::NewChats) ? Flag::f_new_chats : Flag())
-		| ((chats.types & Type::ExistingChats)
-			? Flag::f_existing_chats
-			: Flag())
-		| ((chats.types & Type::Contacts) ? Flag::f_contacts : Flag())
-		| ((chats.types & Type::NonContacts) ? Flag::f_non_contacts : Flag())
-		| (chats.list.empty() ? Flag() : Flag::f_users)
-		| (data.allButExcluded ? Flag::f_exclude_selected : Flag());
-}
-
 [[nodiscard]] MTPBusinessAwayMessageSchedule ToMTP(
 		const AwaySchedule &data) {
 	Expects(data.type != AwayScheduleType::Never);
diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp
index d670d9308..a5c2a705d 100644
--- a/Telegram/SourceFiles/data/data_user.cpp
+++ b/Telegram/SourceFiles/data/data_user.cpp
@@ -32,102 +32,6 @@ constexpr auto kSetOnlineAfterActivity = TimeId(30);
 
 using UpdateFlag = Data::PeerUpdate::Flag;
 
-[[nodiscard]] Data::BusinessDetails FromMTP(
-		const tl::conditional<MTPBusinessWorkHours> &hours,
-		const tl::conditional<MTPBusinessLocation> &location) {
-	auto result = Data::BusinessDetails();
-	if (hours) {
-		const auto &data = hours->data();
-		result.hours.timezoneId = qs(data.vtimezone_id());
-		result.hours.intervals.list = ranges::views::all(
-			data.vweekly_open().v
-		) | ranges::views::transform([](const MTPBusinessWeeklyOpen &open) {
-			const auto &data = open.data();
-			return Data::WorkingInterval{
-				data.vstart_minute().v * 60,
-				data.vend_minute().v * 60,
-			};
-		}) | ranges::to_vector;
-	}
-	if (location) {
-		const auto &data = location->data();
-		result.location.address = qs(data.vaddress());
-		if (const auto point = data.vgeo_point()) {
-			point->match([&](const MTPDgeoPoint &data) {
-				result.location.point = Data::LocationPoint(data);
-			}, [&](const MTPDgeoPointEmpty &) {
-			});
-		}
-	}
-	return result;
-}
-
-Data::BusinessRecipients FromMTP(
-		not_null<Data::Session*> owner,
-		const MTPBusinessRecipients &recipients) {
-	using Type = Data::BusinessChatType;
-
-	const auto &data = recipients.data();
-	auto result = Data::BusinessRecipients{
-		.allButExcluded = data.is_exclude_selected(),
-	};
-	auto &chats = result.allButExcluded
-		? result.excluded
-		: result.included;
-	chats.types = Type()
-		| (data.is_new_chats() ? Type::NewChats : Type())
-		| (data.is_existing_chats() ? Type::ExistingChats : Type())
-		| (data.is_contacts() ? Type::Contacts : Type())
-		| (data.is_non_contacts() ? Type::NonContacts : Type());
-	if (const auto users = data.vusers()) {
-		for (const auto &userId : users->v) {
-			chats.list.push_back(owner->user(UserId(userId.v)));
-		}
-	}
-	return result;
-}
-
-[[nodiscard]] Data::AwaySettings FromMTP(
-		not_null<Data::Session*> owner,
-		const tl::conditional<MTPBusinessAwayMessage> &message) {
-	if (!message) {
-		return Data::AwaySettings();
-	}
-	const auto &data = message->data();
-	auto result = Data::AwaySettings{
-		.recipients = FromMTP(owner, data.vrecipients()),
-		.shortcutId = data.vshortcut_id().v,
-		.offlineOnly = data.is_offline_only(),
-	};
-	data.vschedule().match([&](
-			const MTPDbusinessAwayMessageScheduleAlways &) {
-		result.schedule.type = Data::AwayScheduleType::Always;
-	}, [&](const MTPDbusinessAwayMessageScheduleOutsideWorkHours &) {
-		result.schedule.type = Data::AwayScheduleType::OutsideWorkingHours;
-	}, [&](const MTPDbusinessAwayMessageScheduleCustom &data) {
-		result.schedule.type = Data::AwayScheduleType::Custom;
-		result.schedule.customInterval = Data::WorkingInterval{
-			data.vstart_date().v,
-			data.vend_date().v,
-		};
-	});
-	return result;
-}
-
-[[nodiscard]] Data::GreetingSettings FromMTP(
-		not_null<Data::Session*> owner,
-		const tl::conditional<MTPBusinessGreetingMessage> &message) {
-	if (!message) {
-		return Data::GreetingSettings();
-	}
-	const auto &data = message->data();
-	return Data::GreetingSettings{
-		.recipients = FromMTP(owner, data.vrecipients()),
-		.noActivityDays = data.vno_activity_days().v,
-		.shortcutId = data.vshortcut_id().v,
-	};
-}
-
 } // namespace
 
 BotInfo::BotInfo() = default;
diff --git a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp
index f0d690b51..ddcb93e45 100644
--- a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp
+++ b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp
@@ -211,12 +211,14 @@ base::options::toggle ShowPeerIdBelowAbout({
 
 [[nodiscard]] rpl::producer<QString> OpensInText(
 		rpl::producer<TimeId> in,
+		rpl::producer<bool> hoursExpanded,
 		rpl::producer<QString> fallback) {
 	return rpl::combine(
 		std::move(in),
+		std::move(hoursExpanded),
 		std::move(fallback)
-	) | rpl::map([](TimeId in, QString fallback) {
-		return !in
+	) | rpl::map([](TimeId in, bool hoursExpanded, QString fallback) {
+		return (!in || hoursExpanded)
 			? std::move(fallback)
 			: (in >= 86400)
 			? tr::lng_info_hours_opens_in_days(tr::now, lt_count, in / 86400)
@@ -465,6 +467,7 @@ base::options::toggle ShowPeerIdBelowAbout({
 		openedWrap,
 		OpensInText(
 			state->opensIn.value(),
+			state->expanded.value(),
 			dayHoursTextValue(state->day.value())
 		) | rpl::after_next(recount),
 		st::infoHoursValue);
@@ -518,6 +521,7 @@ base::options::toggle ShowPeerIdBelowAbout({
 	}, link->lifetime());
 	link->setClickedCallback([=] {
 		state->myTimezone = !state->myTimezone.current();
+		state->expanded = true;
 	});
 
 	rpl::combine(
diff --git a/Telegram/SourceFiles/settings/business/settings_away_message.cpp b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
index 04dfe993a..a31e2958f 100644
--- a/Telegram/SourceFiles/settings/business/settings_away_message.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
@@ -346,9 +346,7 @@ void AwayMessage::save() {
 	const auto session = &controller()->session();
 	const auto fail = [=](QString error) {
 		if (error == u"BUSINESS_RECIPIENTS_EMPTY"_q) {
-			AssertIsDebug();
-			show->showToast(u"Please choose at least one recipient."_q);
-			//tr::lng_greeting_recipients_empty(tr::now));
+			show->showToast(tr::lng_greeting_recipients_empty(tr::now));
 		} else if (error != u"SHORTCUT_INVALID"_q) {
 			show->showToast(error);
 		}
diff --git a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
index d3f112483..d9b17f260 100644
--- a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
@@ -7,6 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "settings/business/settings_chatbots.h"
 
+#include "apiwrap.h"
+#include "boxes/peers/prepare_short_info_box.h"
+#include "boxes/peer_list_box.h"
 #include "core/application.h"
 #include "data/business/data_business_chatbots.h"
 #include "data/data_session.h"
@@ -14,19 +17,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "lang/lang_keys.h"
 #include "main/main_session.h"
 #include "settings/business/settings_recipients_helper.h"
+#include "ui/effects/ripple_animation.h"
 #include "ui/text/text_utilities.h"
 #include "ui/widgets/fields/input_field.h"
 #include "ui/widgets/buttons.h"
 #include "ui/wrap/slide_wrap.h"
 #include "ui/wrap/vertical_layout.h"
+#include "ui/painter.h"
 #include "ui/vertical_list.h"
 #include "window/window_session_controller.h"
+#include "styles/style_boxes.h"
 #include "styles/style_layers.h"
 #include "styles/style_settings.h"
 
 namespace Settings {
 namespace {
 
+constexpr auto kDebounceTimeout = crl::time(400);
+
 enum class LookupState {
 	Empty,
 	Loading,
@@ -64,22 +72,298 @@ private:
 
 };
 
+class PreviewController final : public PeerListController {
+public:
+	PreviewController(not_null<PeerData*> peer, Fn<void()> resetBot);
+
+	void prepare() override;
+	void loadMoreRows() override;
+	void rowClicked(not_null<PeerListRow*> row) override;
+	void rowRightActionClicked(not_null<PeerListRow*> row) override;
+	Main::Session &session() const override;
+
+private:
+	const not_null<PeerData*> _peer;
+	const Fn<void()> _resetBot;
+	rpl::lifetime _lifetime;
+
+};
+
+class PreviewRow final : public PeerListRow {
+public:
+	using PeerListRow::PeerListRow;
+
+	QSize rightActionSize() const override;
+	QMargins rightActionMargins() const override;
+	void rightActionPaint(
+		Painter &p,
+		int x,
+		int y,
+		int outerWidth,
+		bool selected,
+		bool actionSelected) override;
+	void rightActionAddRipple(
+		QPoint point,
+		Fn<void()> updateCallback) override;
+	void rightActionStopLastRipple() override;
+
+private:
+	std::unique_ptr<Ui::RippleAnimation> _actionRipple;
+
+};
+
+QSize PreviewRow::rightActionSize() const {
+	return QSize(
+		st::settingsChatbotsDeleteIcon.width(),
+		st::settingsChatbotsDeleteIcon.height()) * 2;
+}
+
+QMargins PreviewRow::rightActionMargins() const {
+	const auto itemHeight = st::peerListSingleRow.item.height;
+	const auto skip = (itemHeight - rightActionSize().height()) / 2;
+	return QMargins(0, skip, skip, 0);
+}
+
+void PreviewRow::rightActionPaint(
+		Painter &p,
+		int x,
+		int y,
+		int outerWidth,
+		bool selected,
+		bool actionSelected) {
+	if (_actionRipple) {
+		_actionRipple->paint(
+			p,
+			x,
+			y,
+			outerWidth);
+		if (_actionRipple->empty()) {
+			_actionRipple.reset();
+		}
+	}
+	const auto rect = QRect(QPoint(x, y), PreviewRow::rightActionSize());
+	(actionSelected
+		? st::settingsChatbotsDeleteIconOver
+		: st::settingsChatbotsDeleteIcon).paintInCenter(p, rect);
+}
+
+void PreviewRow::rightActionAddRipple(
+	QPoint point,
+	Fn<void()> updateCallback) {
+	if (!_actionRipple) {
+		auto mask = Ui::RippleAnimation::EllipseMask(rightActionSize());
+		_actionRipple = std::make_unique<Ui::RippleAnimation>(
+			st::defaultRippleAnimation,
+			std::move(mask),
+			std::move(updateCallback));
+	}
+	_actionRipple->add(point);
+}
+
+void PreviewRow::rightActionStopLastRipple() {
+	if (_actionRipple) {
+		_actionRipple->lastStop();
+	}
+}
+
+PreviewController::PreviewController(
+	not_null<PeerData*> peer,
+	Fn<void()> resetBot)
+: _peer(peer)
+, _resetBot(std::move(resetBot)) {
+}
+
+void PreviewController::prepare() {
+	delegate()->peerListAppendRow(std::make_unique<PreviewRow>(_peer));
+	delegate()->peerListRefreshRows();
+}
+
+void PreviewController::loadMoreRows() {
+}
+
+void PreviewController::rowClicked(not_null<PeerListRow*> row) {
+}
+
+void PreviewController::rowRightActionClicked(not_null<PeerListRow*> row) {
+	_resetBot();
+}
+
+Main::Session &PreviewController::session() const {
+	return _peer->session();
+}
+
 [[nodiscard]] rpl::producer<QString> DebouncedValue(
 		not_null<Ui::InputField*> field) {
-	return rpl::single(field->getLastText());
+	return [=](auto consumer) {
+
+		auto result = rpl::lifetime();
+		struct State {
+			base::Timer timer;
+			QString lastText;
+		};
+		const auto state = result.make_state<State>();
+		const auto push = [=] {
+			state->timer.cancel();
+			consumer.put_next_copy(state->lastText);
+		};
+		state->timer.setCallback(push);
+		state->lastText = field->getLastText();
+		consumer.put_next_copy(field->getLastText());
+		field->changes() | rpl::start_with_next([=] {
+			const auto &text = field->getLastText();
+			const auto was = std::exchange(state->lastText, text);
+			if (std::abs(int(text.size()) - int(was.size())) == 1) {
+				state->timer.callOnce(kDebounceTimeout);
+			} else {
+				push();
+			}
+		}, result);
+		return result;
+	};
+}
+
+[[nodiscard]] QString ExtractUsername(QString text) {
+	text = text.trimmed();
+	static const auto expression = QRegularExpression(
+		"^(https://)?([a-zA-Z0-9\\.]+/)?([a-zA-Z0-9_\\.]+)");
+	const auto match = expression.match(text);
+	return match.hasMatch() ? match.captured(3) : text;
 }
 
 [[nodiscard]] rpl::producer<BotState> LookupBot(
 		not_null<Main::Session*> session,
 		rpl::producer<QString> usernameChanges) {
-	return rpl::never<BotState>();
+	using Cache = base::flat_map<QString, UserData*>;
+	const auto cache = std::make_shared<Cache>();
+	return std::move(
+		usernameChanges
+	) | rpl::map([=](const QString &username) -> rpl::producer<BotState> {
+		const auto extracted = ExtractUsername(username);
+		const auto owner = &session->data();
+		static const auto expression = QRegularExpression(
+			"^[a-zA-Z0-9_\\.]+$");
+		if (!expression.match(extracted).hasMatch()) {
+			return rpl::single(BotState());
+		} else if (const auto peer = owner->peerByUsername(extracted)) {
+			if (const auto user = peer->asUser(); user && user->isBot()) {
+				return rpl::single(BotState{
+					.bot = user,
+					.state = LookupState::Ready,
+				});
+			}
+			return rpl::single(BotState{
+				.state = LookupState::Ready,
+			});
+		} else if (const auto i = cache->find(extracted); i != end(*cache)) {
+			return rpl::single(BotState{
+				.bot = i->second,
+				.state = LookupState::Ready,
+			});
+		}
+
+		return [=](auto consumer) {
+			auto result = rpl::lifetime();
+
+			const auto requestId = result.make_state<mtpRequestId>();
+			*requestId = session->api().request(MTPcontacts_ResolveUsername(
+				MTP_string(extracted)
+			)).done([=](const MTPcontacts_ResolvedPeer &result) {
+				const auto &data = result.data();
+				session->data().processUsers(data.vusers());
+				session->data().processChats(data.vchats());
+				const auto peerId = peerFromMTP(data.vpeer());
+				const auto peer = session->data().peer(peerId);
+				if (const auto user = peer->asUser()) {
+					if (user->isBot()) {
+						cache->emplace(extracted, user);
+						consumer.put_next(BotState{
+							.bot = user,
+							.state = LookupState::Ready,
+						});
+						return;
+					}
+				}
+				cache->emplace(extracted, nullptr);
+				consumer.put_next(BotState{ .state = LookupState::Ready });
+			}).fail([=] {
+				cache->emplace(extracted, nullptr);
+				consumer.put_next(BotState{ .state = LookupState::Ready });
+			}).send();
+
+			result.add([=] {
+				session->api().request(*requestId).cancel();
+			});
+			return result;
+		};
+	}) | rpl::flatten_latest();
 }
 
 [[nodiscard]] object_ptr<Ui::RpWidget> MakeBotPreview(
-		not_null<QWidget*> parent,
+		not_null<Ui::RpWidget*> parent,
 		rpl::producer<BotState> state,
 		Fn<void()> resetBot) {
-	return object_ptr<Ui::RpWidget>(parent.get());
+	auto result = object_ptr<Ui::SlideWrap<>>(
+		parent.get(),
+		object_ptr<Ui::RpWidget>(parent.get()));
+	const auto raw = result.data();
+	const auto inner = raw->entity();
+	raw->hide(anim::type::instant);
+
+	const auto child = inner->lifetime().make_state<Ui::RpWidget*>(nullptr);
+	std::move(state) | rpl::filter([=](BotState state) {
+		return state.state != LookupState::Loading;
+	}) | rpl::start_with_next([=](BotState state) {
+		raw->toggle(state.state == LookupState::Ready, anim::type::normal);
+		if (state.bot) {
+			const auto delegate = parent->lifetime().make_state<
+				PeerListContentDelegateSimple
+			>();
+			const auto controller = parent->lifetime().make_state<
+				PreviewController
+			>(state.bot, resetBot);
+			controller->setStyleOverrides(&st::peerListSingleRow);
+			const auto content = Ui::CreateChild<PeerListContent>(
+				inner,
+				controller);
+			delegate->setContent(content);
+			controller->setDelegate(delegate);
+			delete base::take(*child);
+			*child = content;
+		} else if (state.state == LookupState::Ready) {
+			const auto content = Ui::CreateChild<Ui::RpWidget>(inner);
+			const auto label = Ui::CreateChild<Ui::FlatLabel>(
+				content,
+				tr::lng_chatbots_not_found(),
+				st::settingsChatbotsNotFound);
+			content->resize(
+				inner->width(),
+				st::peerListSingleRow.item.height);
+			rpl::combine(
+				content->sizeValue(),
+				label->sizeValue()
+			) | rpl::start_with_next([=](QSize size, QSize inner) {
+				label->move(
+					(size.width() - inner.width()) / 2,
+					(size.height() - inner.height()) / 2);
+			}, label->lifetime());
+			delete base::take(*child);
+			*child = content;
+		} else {
+			return;
+		}
+		(*child)->show();
+
+		inner->widthValue() | rpl::start_with_next([=](int width) {
+			(*child)->resizeToWidth(width);
+		}, (*child)->lifetime());
+
+		(*child)->heightValue() | rpl::start_with_next([=](int height) {
+			inner->resize(inner->width(), height + st::contactSkip);
+		}, inner->lifetime());
+	}, inner->lifetime());
+
+	raw->finishAnimating();
+	return result;
 }
 
 Chatbots::Chatbots(
@@ -193,15 +477,14 @@ void Chatbots::save() {
 	const auto session = &controller()->session();
 	const auto fail = [=](QString error) {
 		if (error == u"BUSINESS_RECIPIENTS_EMPTY"_q) {
-			AssertIsDebug();
-			show->showToast(u"Please choose at least one recipient."_q);
-			//tr::lng_greeting_recipients_empty(tr::now));
+			show->showToast(tr::lng_greeting_recipients_empty(tr::now));
 		}
 	};
 	controller()->session().data().chatbots().save({
 		.bot = _botValue.current().bot,
 		.recipients = _recipients.current(),
 		.repliesAllowed = _repliesAllowed.current(),
+	}, [=] {
 	}, [=](QString error) { show->showToast(error); });
 }
 
diff --git a/Telegram/SourceFiles/settings/business/settings_greeting.cpp b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
index 3c73f391f..932c1d980 100644
--- a/Telegram/SourceFiles/settings/business/settings_greeting.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
@@ -263,9 +263,7 @@ void Greeting::save() {
 	const auto session = &controller()->session();
 	const auto fail = [=](QString error) {
 		if (error == u"BUSINESS_RECIPIENTS_EMPTY"_q) {
-			AssertIsDebug();
-			show->showToast(u"Please choose at least one recipient."_q);
-			//tr::lng_greeting_recipients_empty(tr::now));
+			show->showToast(tr::lng_greeting_recipients_empty(tr::now));
 		} else if (error != u"SHORTCUT_INVALID"_q) {
 			show->showToast(error);
 		}
diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style
index fcdd0a7cd..00f1121d1 100644
--- a/Telegram/SourceFiles/settings/settings.style
+++ b/Telegram/SourceFiles/settings/settings.style
@@ -631,4 +631,10 @@ settingsAddReplyField: InputField(defaultInputField) {
 	placeholderScale: 0.;
 
 	heightMin: 36px;
-}
\ No newline at end of file
+}
+settingsChatbotsNotFound: FlatLabel(defaultFlatLabel) {
+	textFg: windowSubTextFg;
+	align: align(top);
+}
+settingsChatbotsDeleteIcon: icon {{ "dialogs/dialogs_cancel_search", dialogsMenuIconFg }};
+settingsChatbotsDeleteIconOver: icon {{ "dialogs/dialogs_cancel_search", dialogsMenuIconFgOver }};
diff --git a/Telegram/SourceFiles/settings/settings_business.cpp b/Telegram/SourceFiles/settings/settings_business.cpp
index c0848e341..936f0c49f 100644
--- a/Telegram/SourceFiles/settings/settings_business.cpp
+++ b/Telegram/SourceFiles/settings/settings_business.cpp
@@ -9,10 +9,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "boxes/premium_preview_box.h"
 #include "core/click_handler_types.h"
+#include "data/business/data_business_info.h"
+#include "data/business/data_business_chatbots.h"
+#include "data/business/data_shortcut_messages.h"
 #include "data/data_peer_values.h" // AmPremiumValue.
 #include "data/data_session.h"
-#include "data/business/data_business_info.h"
-#include "data/business/data_shortcut_messages.h"
 #include "info/info_wrap_widget.h" // Info::Wrap.
 #include "info/settings/info_settings_widget.h" // SectionCustomTopBarData.
 #include "lang/lang_keys.h"
@@ -224,9 +225,9 @@ void AddBusinessSummary(
 	icons.reserve(int(entryMap.size()));
 	{
 		const auto &account = controller->session().account();
-		const auto mtpOrder = account.appConfig().get<Order>(
+		const auto mtpOrder = FallbackOrder(); AssertIsDebug();/* account.appConfig().get<Order>(
 			"business_promo_order",
-			FallbackOrder());
+			FallbackOrder());*/
 		const auto processEntry = [&](Entry &entry) {
 			icons.push_back(entry.icon);
 			addRow(entry);
@@ -354,7 +355,8 @@ void Business::setStepDataReference(std::any &data) {
 void Business::setupContent() {
 	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
 
-	_controller->session().data().businessInfo().preloadTimezones();
+	_controller->session().data().chatbots().preload();
+	_controller->session().data().businessInfo().preload();
 	_controller->session().data().shortcutMessages().preloadShortcuts();
 
 	Ui::AddSkip(content, st::settingsFromFileTop);

From 00dcf11691da3dccbeaf2033f869a68d173ddba6 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Thu, 7 Mar 2024 17:02:32 +0400
Subject: [PATCH 074/108] Improve recipients selection in business features.

---
 Telegram/Resources/langs/lang.strings         |  1 +
 .../SourceFiles/core/local_url_handlers.cpp   | 15 ------
 .../data/business/data_business_common.h      |  6 ++-
 .../data/business/data_business_info.cpp      | 52 +++++++++----------
 .../SourceFiles/info/info_content_widget.h    |  6 +++
 .../SourceFiles/info/info_wrap_widget.cpp     | 14 +++--
 .../info/settings/info_settings_widget.cpp    |  8 +++
 .../info/settings/info_settings_widget.h      |  2 +
 .../business/settings_away_message.cpp        |  5 ++
 .../settings/business/settings_chatbots.cpp   |  9 +++-
 .../settings/business/settings_greeting.cpp   |  5 ++
 .../business/settings_recipients_helper.cpp   | 24 ++++++++-
 .../business/settings_working_hours.cpp       |  5 ++
 .../SourceFiles/settings/settings_common.h    |  6 +++
 14 files changed, 109 insertions(+), 49 deletions(-)

diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index e8366f8f7..f13454d7d 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -2285,6 +2285,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_chatbots_remove" = "Remove Bot";
 "lng_chatbots_not_found" = "Chatbot not found.";
 "lng_chatbots_add" = "Add";
+"lng_chatbots_info_url" = "https://telegram.org/privacy";
 
 "lng_boost_channel_button" = "Boost Channel";
 "lng_boost_group_button" = "Boost Group";
diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp
index 92ed6c9e8..d94187094 100644
--- a/Telegram/SourceFiles/core/local_url_handlers.cpp
+++ b/Telegram/SourceFiles/core/local_url_handlers.cpp
@@ -669,17 +669,6 @@ bool ShowSearchTagsPromo(
 	return true;
 }
 
-bool ShowAboutBusinessChatbots(
-		Window::SessionController *controller,
-		const Match &match,
-		const QVariant &context) {
-	if (!controller) {
-		return false;
-	}
-	controller->showToast(u"Cool feature, yeah.."_q); AssertIsDebug();
-	return true;
-}
-
 void ExportTestChatTheme(
 		not_null<Window::SessionController*> controller,
 		not_null<const Data::CloudTheme*> theme) {
@@ -1048,10 +1037,6 @@ const std::vector<LocalUrlHandler> &InternalUrlHandlers() {
 			u"about_tags"_q,
 			ShowSearchTagsPromo
 		},
-		{
-			u"about_business_chatbots"_q,
-			ShowAboutBusinessChatbots
-		},
 	};
 	return Result;
 }
diff --git a/Telegram/SourceFiles/data/business/data_business_common.h b/Telegram/SourceFiles/data/business/data_business_common.h
index dd66bb770..151d534e1 100644
--- a/Telegram/SourceFiles/data/business/data_business_common.h
+++ b/Telegram/SourceFiles/data/business/data_business_common.h
@@ -30,6 +30,10 @@ struct BusinessChats {
 	BusinessChatTypes types;
 	std::vector<not_null<UserData*>> list;
 
+	[[nodiscard]] bool empty() const {
+		return !types && list.empty();
+	}
+
 	friend inline bool operator==(
 		const BusinessChats &a,
 		const BusinessChats &b) = default;
@@ -193,7 +197,7 @@ enum class AwayScheduleType : uchar {
 };
 
 struct AwaySchedule {
-	AwayScheduleType type = AwayScheduleType::Always;
+	AwayScheduleType type = AwayScheduleType::Never;
 	WorkingInterval customInterval;
 
 	friend inline bool operator==(
diff --git a/Telegram/SourceFiles/data/business/data_business_info.cpp b/Telegram/SourceFiles/data/business/data_business_info.cpp
index b6cf5ac36..8a65201fe 100644
--- a/Telegram/SourceFiles/data/business/data_business_info.cpp
+++ b/Telegram/SourceFiles/data/business/data_business_info.cpp
@@ -109,20 +109,20 @@ void BusinessInfo::saveAwaySettings(
 	const auto &was = _awaySettings;
 	if (was == data) {
 		return;
+	} else if (!data || data.shortcutId) {
+		using Flag = MTPaccount_UpdateBusinessAwayMessage::Flag;
+		const auto session = &_owner->session();
+		session->api().request(MTPaccount_UpdateBusinessAwayMessage(
+			MTP_flags(data ? Flag::f_message : Flag()),
+			data ? ToMTP(data) : MTPInputBusinessAwayMessage()
+		)).fail([=](const MTP::Error &error) {
+			_awaySettings = was;
+			_awaySettingsChanged.fire({});
+			if (fail) {
+				fail(error.type());
+			}
+		}).send();
 	}
-	using Flag = MTPaccount_UpdateBusinessAwayMessage::Flag;
-	const auto session = &_owner->session();
-	session->api().request(MTPaccount_UpdateBusinessAwayMessage(
-		MTP_flags(data ? Flag::f_message : Flag()),
-		data ? ToMTP(data) : MTPInputBusinessAwayMessage()
-	)).fail([=](const MTP::Error &error) {
-		_awaySettings = was;
-		_awaySettingsChanged.fire({});
-		if (fail) {
-			fail(error.type());
-		}
-	}).send();
-
 	_awaySettings = std::move(data);
 	_awaySettingsChanged.fire({});
 }
@@ -153,20 +153,20 @@ void BusinessInfo::saveGreetingSettings(
 	const auto &was = _greetingSettings;
 	if (was == data) {
 		return;
+	} else if (!data || data.shortcutId) {
+		using Flag = MTPaccount_UpdateBusinessGreetingMessage::Flag;
+		_owner->session().api().request(
+			MTPaccount_UpdateBusinessGreetingMessage(
+				MTP_flags(data ? Flag::f_message : Flag()),
+				data ? ToMTP(data) : MTPInputBusinessGreetingMessage())
+		).fail([=](const MTP::Error &error) {
+			_greetingSettings = was;
+			_greetingSettingsChanged.fire({});
+			if (fail) {
+				fail(error.type());
+			}
+		}).send();
 	}
-	using Flag = MTPaccount_UpdateBusinessGreetingMessage::Flag;
-	_owner->session().api().request(
-		MTPaccount_UpdateBusinessGreetingMessage(
-			MTP_flags(data ? Flag::f_message : Flag()),
-			data ? ToMTP(data) : MTPInputBusinessGreetingMessage())
-	).fail([=](const MTP::Error &error) {
-		_greetingSettings = was;
-		_greetingSettingsChanged.fire({});
-		if (fail) {
-			fail(error.type());
-		}
-	}).send();
-
 	_greetingSettings = std::move(data);
 	_greetingSettingsChanged.fire({});
 }
diff --git a/Telegram/SourceFiles/info/info_content_widget.h b/Telegram/SourceFiles/info/info_content_widget.h
index 29d947c66..7f7cd0f2f 100644
--- a/Telegram/SourceFiles/info/info_content_widget.h
+++ b/Telegram/SourceFiles/info/info_content_widget.h
@@ -101,6 +101,12 @@ public:
 	}
 	virtual void fillTopBarMenu(const Ui::Menu::MenuCallback &addAction);
 
+	[[nodiscard]] virtual bool closeByOutsideClick() const {
+		return true;
+	}
+	virtual void checkBeforeClose(Fn<void()> close) {
+		close();
+	}
 	[[nodiscard]] virtual rpl::producer<QString> title() = 0;
 	[[nodiscard]] virtual rpl::producer<QString> subtitle() {
 		return nullptr;
diff --git a/Telegram/SourceFiles/info/info_wrap_widget.cpp b/Telegram/SourceFiles/info/info_wrap_widget.cpp
index a2c6a781c..50eb99333 100644
--- a/Telegram/SourceFiles/info/info_wrap_widget.cpp
+++ b/Telegram/SourceFiles/info/info_wrap_widget.cpp
@@ -410,8 +410,10 @@ void WrapWidget::setupTopBarMenuToggle() {
 }
 
 void WrapWidget::checkBeforeClose(Fn<void()> close) {
-	_controller->parentController()->hideLayer();
-	close();
+	_content->checkBeforeClose(crl::guard(this, [=] {
+		_controller->parentController()->hideLayer();
+		close();
+	}));
 }
 
 void WrapWidget::addTopBarMenuButton() {
@@ -438,7 +440,7 @@ void WrapWidget::addTopBarMenuButton() {
 }
 
 bool WrapWidget::closeByOutsideClick() const {
-	return true;
+	return _content->closeByOutsideClick();
 }
 
 void WrapWidget::addProfileCallsButton() {
@@ -872,8 +874,12 @@ void WrapWidget::keyPressEvent(QKeyEvent *e) {
 	if (e->key() == Qt::Key_Escape || e->key() == Qt::Key_Back) {
 		if (hasStackHistory() || wrap() != Wrap::Layer) {
 			checkBeforeClose([=] { _controller->showBackFromStack(); });
-			return;
+		} else {
+			checkBeforeClose([=] {
+				_controller->parentController()->hideSpecialLayer();
+			});
 		}
+		return;
 	}
 	SectionWidget::keyPressEvent(e);
 }
diff --git a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp
index 5bc315aa1..bbc0ca717 100644
--- a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp
+++ b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp
@@ -232,6 +232,14 @@ rpl::producer<bool> Widget::desiredShadowVisibility() const {
 		: rpl::single(true);
 }
 
+bool Widget::closeByOutsideClick() const {
+	return _inner->closeByOutsideClick();;
+}
+
+void Widget::checkBeforeClose(Fn<void()> close) {
+	_inner->checkBeforeClose(std::move(close));
+}
+
 rpl::producer<QString> Widget::title() {
 	return _inner->title();
 }
diff --git a/Telegram/SourceFiles/info/settings/info_settings_widget.h b/Telegram/SourceFiles/info/settings/info_settings_widget.h
index 09fca4419..23b4e7c93 100644
--- a/Telegram/SourceFiles/info/settings/info_settings_widget.h
+++ b/Telegram/SourceFiles/info/settings/info_settings_widget.h
@@ -76,6 +76,8 @@ public:
 
 	rpl::producer<bool> desiredShadowVisibility() const override;
 
+	bool closeByOutsideClick() const override;
+	void checkBeforeClose(Fn<void()> close) override;
 	rpl::producer<QString> title() override;
 
 	void enableBackButton() override;
diff --git a/Telegram/SourceFiles/settings/business/settings_away_message.cpp b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
index a31e2958f..a5fef26a2 100644
--- a/Telegram/SourceFiles/settings/business/settings_away_message.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_away_message.cpp
@@ -38,6 +38,7 @@ public:
 		not_null<Window::SessionController*> controller);
 	~AwayMessage();
 
+	[[nodiscard]] bool closeByOutsideClick() const override;
 	[[nodiscard]] rpl::producer<QString> title() override;
 
 private:
@@ -199,6 +200,10 @@ AwayMessage::~AwayMessage() {
 	}
 }
 
+bool AwayMessage::closeByOutsideClick() const {
+	return false;
+}
+
 rpl::producer<QString> AwayMessage::title() {
 	return tr::lng_away_title();
 }
diff --git a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
index d9b17f260..78d446200 100644
--- a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
@@ -53,6 +53,7 @@ public:
 		not_null<Window::SessionController*> controller);
 	~Chatbots();
 
+	[[nodiscard]] bool closeByOutsideClick() const override;
 	[[nodiscard]] rpl::producer<QString> title() override;
 
 	const Ui::RoundRect *bottomSkipRounding() const override {
@@ -380,6 +381,10 @@ Chatbots::~Chatbots() {
 	}
 }
 
+bool Chatbots::closeByOutsideClick() const {
+	return false;
+}
+
 rpl::producer<QString> Chatbots::title() {
 	return tr::lng_chatbots_title();
 }
@@ -402,7 +407,7 @@ void Chatbots::setupContent(
 		.about = tr::lng_chatbots_about(
 			lt_link,
 			tr::lng_chatbots_about_link(
-			) | Ui::Text::ToLink(u"internal:about_business_chatbots"_q),
+			) | Ui::Text::ToLink(tr::lng_chatbots_info_url(tr::now)),
 			Ui::Text::WithEntities),
 		.aboutMargins = st::peerAppearanceCoverLabelMargin,
 	});
@@ -485,7 +490,7 @@ void Chatbots::save() {
 		.recipients = _recipients.current(),
 		.repliesAllowed = _repliesAllowed.current(),
 	}, [=] {
-	}, [=](QString error) { show->showToast(error); });
+	}, fail);
 }
 
 } // namespace
diff --git a/Telegram/SourceFiles/settings/business/settings_greeting.cpp b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
index 932c1d980..9809c1b71 100644
--- a/Telegram/SourceFiles/settings/business/settings_greeting.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_greeting.cpp
@@ -42,6 +42,7 @@ public:
 		not_null<Window::SessionController*> controller);
 	~Greeting();
 
+	[[nodiscard]] bool closeByOutsideClick() const override;
 	[[nodiscard]] rpl::producer<QString> title() override;
 
 	const Ui::RoundRect *bottomSkipRounding() const override {
@@ -105,6 +106,10 @@ Greeting::~Greeting() {
 	}
 }
 
+bool Greeting::closeByOutsideClick() const {
+	return false;
+}
+
 rpl::producer<QString> Greeting::title() {
 	return tr::lng_greeting_title();
 }
diff --git a/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp b/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
index 960c354b2..fb2ac16e9 100644
--- a/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
@@ -168,8 +168,10 @@ void AddBusinessRecipientsSelector(
 		modify(now);
 		*data = std::move(now);
 	};
+	const auto &current = data->current();
+	const auto all = current.allButExcluded || current.included.empty();
 	const auto group = std::make_shared<Ui::RadiobuttonGroup>(
-		data->current().allButExcluded ? kAllExcept : kSelectedOnly);
+		all ? kAllExcept : kSelectedOnly);
 	const auto everyone = container->add(
 		object_ptr<Ui::Radiobutton>(
 			container,
@@ -281,6 +283,12 @@ void AddBusinessRecipientsSelector(
 	}, lifetime);
 
 	SetupBusinessChatsPreview(includeInner, included);
+	included->value(
+	) | rpl::start_with_next([=](const Data::BusinessChats &value) {
+		if (value.empty() && group->current() == kSelectedOnly) {
+			group->setValue(kAllExcept);
+		}
+	}, lifetime);
 
 	includeWrap->toggleOn(data->value(
 	) | rpl::map([](const Data::BusinessRecipients &value) {
@@ -289,6 +297,20 @@ void AddBusinessRecipientsSelector(
 	includeWrap->finishAnimating();
 
 	group->setChangedCallback([=](int value) {
+		if (value == kSelectedOnly && data->current().included.empty()) {
+			group->setValue(kAllExcept);
+			const auto save = [=](Data::BusinessChats value) {
+				change([&](Data::BusinessRecipients &data) {
+					data.included = std::move(value);
+				});
+				group->setValue(kSelectedOnly);
+			};
+			EditBusinessChats(controller, {
+				.save = crl::guard(includeAdd, save),
+				.include = true,
+			});
+			return;
+		}
 		change([&](Data::BusinessRecipients &data) {
 			data.allButExcluded = (value == kAllExcept);
 		});
diff --git a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
index bae1b6b3d..1d06c99ce 100644
--- a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
@@ -44,6 +44,7 @@ public:
 		not_null<Window::SessionController*> controller);
 	~WorkingHours();
 
+	[[nodiscard]] bool closeByOutsideClick() const override;
 	[[nodiscard]] rpl::producer<QString> title() override;
 
 private:
@@ -529,6 +530,10 @@ WorkingHours::~WorkingHours() {
 	}
 }
 
+bool WorkingHours::closeByOutsideClick() const {
+	return false;
+}
+
 rpl::producer<QString> WorkingHours::title() {
 	return tr::lng_hours_title();
 }
diff --git a/Telegram/SourceFiles/settings/settings_common.h b/Telegram/SourceFiles/settings/settings_common.h
index c279640a5..c36927978 100644
--- a/Telegram/SourceFiles/settings/settings_common.h
+++ b/Telegram/SourceFiles/settings/settings_common.h
@@ -70,6 +70,12 @@ public:
 	[[nodiscard]] virtual rpl::producer<std::vector<Type>> removeFromStack() {
 		return nullptr;
 	}
+	[[nodiscard]] virtual bool closeByOutsideClick() const {
+		return true;
+	}
+	virtual void checkBeforeClose(Fn<void()> close) {
+		close();
+	}
 	[[nodiscard]] virtual rpl::producer<QString> title() = 0;
 	virtual void sectionSaveChanges(FnMut<void()> done) {
 		done();

From d608bffecbd86bcdff540fc802436399f59991d6 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Thu, 7 Mar 2024 17:15:27 +0400
Subject: [PATCH 075/108] Fix limit in business features exception box.

---
 .../SourceFiles/boxes/filters/edit_filter_box.cpp    | 12 +++++++++---
 .../boxes/filters/edit_filter_chats_list.cpp         | 12 ++++++------
 .../boxes/filters/edit_filter_chats_list.h           |  6 +++---
 .../settings/business/settings_recipients_helper.cpp |  5 ++---
 4 files changed, 20 insertions(+), 15 deletions(-)

diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp
index 958d01436..ce23d235f 100644
--- a/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp
+++ b/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp
@@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_chat_filters.h"
 #include "data/data_peer.h"
 #include "data/data_peer_values.h" // Data::AmPremiumValue.
+#include "data/data_premium_limits.h"
 #include "data/data_session.h"
 #include "data/data_user.h"
 #include "core/application.h"
@@ -124,6 +125,12 @@ void EditExceptions(
 	const auto include = (options & Flag::Contacts) != Flags(0);
 	const auto rules = data->current();
 	const auto session = &window->session();
+	const auto limit = Data::PremiumLimits(
+		session
+	).dialogFiltersChatsCurrent();
+	const auto showLimitReached = [=] {
+		window->show(Box(FilterChatsLimitBox, session, limit, include));
+	};
 	auto controller = std::make_unique<EditFilterChatsListController>(
 		session,
 		(include
@@ -132,9 +139,8 @@ void EditExceptions(
 		options,
 		rules.flags() & options,
 		include ? rules.always() : rules.never(),
-		[=](int count) {
-			return Box(FilterChatsLimitBox, session, count, include);
-		});
+		limit,
+		showLimitReached);
 	const auto rawController = controller.get();
 	auto initBox = [=](not_null<PeerListBox*> box) {
 		box->setCloseByOutsideClick(false);
diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp
index 25463f1e2..0ee2bace0 100644
--- a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp
+++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp
@@ -333,17 +333,17 @@ EditFilterChatsListController::EditFilterChatsListController(
 	Flags options,
 	Flags selected,
 	const base::flat_set<not_null<History*>> &peers,
-	LimitBoxFactory limitBox)
+	int limit,
+	Fn<void()> showLimitReached)
 : ChatsListBoxController(session)
 , _session(session)
-, _limitBox(std::move(limitBox))
+, _showLimitReached(std::move(showLimitReached))
 , _title(std::move(title))
 , _peers(peers)
 , _options(options & ~Flag::Chatlist)
 , _selected(selected)
-, _limit(Data::PremiumLimits(session).dialogFiltersChatsCurrent())
+, _limit(limit)
 , _chatlist(options & Flag::Chatlist) {
-	Expects(_limitBox != nullptr);
 }
 
 Main::Session &EditFilterChatsListController::session() const {
@@ -371,8 +371,8 @@ void EditFilterChatsListController::rowClicked(not_null<PeerListRow*> row) {
 	if (count < _limit || row->checked()) {
 		delegate()->peerListSetRowChecked(row, !row->checked());
 		updateTitle();
-	} else {
-		delegate()->peerListUiShow()->showBox(_limitBox(count));
+	} else if (const auto copy = _showLimitReached) {
+		copy();
 	}
 }
 
diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.h b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.h
index a9dfd3fa2..26e0529c3 100644
--- a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.h
+++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.h
@@ -43,7 +43,6 @@ class EditFilterChatsListController final : public ChatsListBoxController {
 public:
 	using Flag = Data::ChatFilter::Flag;
 	using Flags = Data::ChatFilter::Flags;
-	using LimitBoxFactory = Fn<object_ptr<Ui::BoxContent>(int)>;
 
 	EditFilterChatsListController(
 		not_null<Main::Session*> session,
@@ -51,7 +50,8 @@ public:
 		Flags options,
 		Flags selected,
 		const base::flat_set<not_null<History*>> &peers,
-		LimitBoxFactory limitBox);
+		int limit,
+		Fn<void()> showLimitReached);
 
 	[[nodiscard]] Main::Session &session() const override;
 	[[nodiscard]] Flags chosenOptions() const {
@@ -72,7 +72,7 @@ private:
 	void updateTitle();
 
 	const not_null<Main::Session*> _session;
-	const LimitBoxFactory _limitBox;
+	const Fn<void()> _showLimitReached;
 	rpl::producer<QString> _title;
 	base::flat_set<not_null<History*>> _peers;
 	Flags _options;
diff --git a/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp b/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
index fb2ac16e9..7f7f2b715 100644
--- a/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
@@ -72,9 +72,8 @@ void EditBusinessChats(
 		options,
 		TypesToFlags(descriptor.current.types) & options,
 		base::flat_set<not_null<History*>>(begin(peers), end(peers)),
-		[=](int count) {
-			return nullptr; AssertIsDebug();
-		});
+		100,
+		nullptr);
 	const auto rawController = controller.get();
 	const auto save = descriptor.save;
 	auto initBox = [=](not_null<PeerListBox*> box) {

From d14f11bd8830dc685d918701c3185df1e9625735 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Thu, 7 Mar 2024 17:16:08 +0400
Subject: [PATCH 076/108] fixup Implement preview and save of chatbots.

---
 Telegram/SourceFiles/settings/settings_business.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Telegram/SourceFiles/settings/settings_business.cpp b/Telegram/SourceFiles/settings/settings_business.cpp
index 936f0c49f..157438868 100644
--- a/Telegram/SourceFiles/settings/settings_business.cpp
+++ b/Telegram/SourceFiles/settings/settings_business.cpp
@@ -225,9 +225,9 @@ void AddBusinessSummary(
 	icons.reserve(int(entryMap.size()));
 	{
 		const auto &account = controller->session().account();
-		const auto mtpOrder = FallbackOrder(); AssertIsDebug();/* account.appConfig().get<Order>(
+		const auto mtpOrder = account.appConfig().get<Order>(
 			"business_promo_order",
-			FallbackOrder());*/
+			FallbackOrder());
 		const auto processEntry = [&](Entry &entry) {
 			icons.push_back(entry.icon);
 			addRow(entry);

From 6d352597b4f72cb6e1269e4b5052c713a51be6f0 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Thu, 7 Mar 2024 17:21:18 +0400
Subject: [PATCH 077/108] Disable quick replies in bot chats.

---
 Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp
index 1cf45d762..08b5abf14 100644
--- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp
+++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp
@@ -638,7 +638,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
 				}
 			}
 		}
-		const auto shortcuts = _user
+		const auto shortcuts = (_user && !_user->isBot())
 			? _user->owner().shortcutMessages().shortcuts().list
 			: base::flat_map<BusinessShortcutId, Data::Shortcut>();
 		if (!hasUsername && !shortcuts.empty()) {

From 0a8e96114209e2cb5d69ef2a076a4d062e5c2160 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Thu, 7 Mar 2024 17:47:40 +0400
Subject: [PATCH 078/108] Suggest premium when sending existing quick replies.

---
 Telegram/SourceFiles/history/history_widget.cpp | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index 043fd7b8c..3b6ab5dd5 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "boxes/share_box.h"
 #include "boxes/edit_caption_box.h"
 #include "boxes/premium_limits_box.h"
+#include "boxes/premium_preview_box.h"
 #include "boxes/peers/edit_peer_permissions_box.h" // ShowAboutGigagroup.
 #include "boxes/peers/edit_peer_requests_box.h"
 #include "core/file_utilities.h"
@@ -439,12 +440,16 @@ HistoryWidget::HistoryWidget(
 			&& data.method != Method::ByTab)
 			? messages->lookupShortcutId(data.command.mid(1))
 			: BusinessShortcutId();
-		if (shortcutId) {
+		if (!shortcutId) {
+			insertHashtagOrBotCommand(data.command, data.method);
+		} else if (!_peer->session().premium()) {
+			ShowPremiumPreviewToBuy(
+				controller,
+				PremiumFeature::QuickReplies);
+		} else {
 			session().api().sendShortcutMessages(_peer, shortcutId);
 			session().api().finishForwarding(prepareSendAction({}));
 			setFieldText(_field->getTextWithTagsPart(_field->textCursor().position()));
-		} else {
-			insertHashtagOrBotCommand(data.command, data.method);
 		}
 	}, lifetime());
 

From 49ec0517605bf40086fe0f9efb6cc1167784be95 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Thu, 7 Mar 2024 17:47:52 +0400
Subject: [PATCH 079/108] Make premium toast adaptive.

---
 Telegram/SourceFiles/window/window_session_controller.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp
index 92727609d..15ea551e8 100644
--- a/Telegram/SourceFiles/window/window_session_controller.cpp
+++ b/Telegram/SourceFiles/window/window_session_controller.cpp
@@ -1427,8 +1427,10 @@ void SessionController::setupPremiumToast() {
 		session().mtp().requestConfig();
 		return premium;
 	}) | rpl::start_with_next([=] {
-		MainWindowShow(this).showToast(
-			{ tr::lng_premium_success(tr::now) });
+		MainWindowShow(this).showToast({
+			.text = { tr::lng_premium_success(tr::now) },
+			.adaptive = true,
+		});
 	}, _lifetime);
 }
 

From 288979d8e7bc2eb2326c0cd9a811237e5fabfec4 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Thu, 7 Mar 2024 20:55:04 +0400
Subject: [PATCH 080/108] Allow editing quick replies from the suggestions.

---
 Telegram/Resources/langs/lang.strings         |  1 +
 .../chat_helpers/field_autocomplete.cpp       | 23 ++++++++--
 .../SourceFiles/history/history_widget.cpp    | 19 ++++++---
 .../business/settings_quick_replies.cpp       |  7 ++++
 .../business/settings_shortcut_messages.cpp   | 42 +++++++++++++++++++
 Telegram/SourceFiles/ui/chat/chat.style       |  3 ++
 6 files changed, 87 insertions(+), 8 deletions(-)

diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index f13454d7d..2489020b8 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -2228,6 +2228,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_replies_message_placeholder" = "Add a Quick Reply";
 "lng_replies_delete_sure" = "Are you sure you want to delete this quick reply with all its messages?";
 "lng_replies_error_occupied" = "This shortcut is already used.";
+"lng_replies_edit_button" = "Edit Quick Replies";
 
 "lng_greeting_title" = "Greeting Message";
 "lng_greeting_about" = "Greet customers when they message you the first time or after a period of no activity.";
diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp
index 08b5abf14..d5aba296b 100644
--- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp
+++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp
@@ -641,7 +641,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
 		const auto shortcuts = (_user && !_user->isBot())
 			? _user->owner().shortcutMessages().shortcuts().list
 			: base::flat_map<BusinessShortcutId, Data::Shortcut>();
-		if (!hasUsername && !shortcuts.empty()) {
+		if (!hasUsername && brows.empty() && !shortcuts.empty()) {
 			const auto self = _user->session().user();
 			for (const auto &[id, shortcut] : shortcuts) {
 				if (shortcut.count < 1) {
@@ -658,6 +658,9 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
 					self->activeUserpicView()
 				});
 			}
+			if (!brows.empty()) {
+				brows.insert(begin(brows), BotCommandRow{ self }); // Edit.
+			}
 		}
 	}
 	rowsUpdated(
@@ -1096,6 +1099,15 @@ void FieldAutocomplete::Inner::paintEvent(QPaintEvent *e) {
 			} else {
 				auto &row = _brows->at(i);
 				const auto user = row.user;
+				if (user->isSelf() && row.command.isEmpty()) {
+					p.setPen(st::windowActiveTextFg);
+					p.setFont(st::semiboldFont);
+					p.drawText(
+						QRect(0, i * st::mentionHeight, width(), st::mentionHeight),
+						tr::lng_replies_edit_button(tr::now),
+						style::al_center);
+					continue;
+				}
 
 				auto toHighlight = row.command;
 				int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1);
@@ -1163,7 +1175,13 @@ void FieldAutocomplete::Inner::clearSel(bool hidden) {
 	_overDelete = false;
 	_mouseSelection = false;
 	_lastMousePosition = std::nullopt;
-	setSel((_mrows->empty() && _brows->empty() && _hrows->empty()) ? -1 : 0);
+	setSel((_mrows->empty() && _brows->empty() && _hrows->empty())
+		? -1
+		: (_brows->size() > 1
+			&& _brows->front().user->isSelf()
+			&& _brows->front().command.isEmpty())
+		? 1
+		: 0);
 	if (hidden) {
 		_down = -1;
 		_previewShown = false;
@@ -1269,7 +1287,6 @@ bool FieldAutocomplete::Inner::chooseAtIndex(
 			const auto commandString = QString("/%1%2").arg(
 				command,
 				insertUsername ? ('@' + PrimaryUsername(user)) : QString());
-
 			_botCommandChosen.fire({ user, commandString, method });
 			return true;
 		}
diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index 3b6ab5dd5..8a5fd3dfa 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -123,6 +123,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "lang/lang_keys.h"
 #include "mainwidget.h"
 #include "mainwindow.h"
+#include "settings/business/settings_quick_replies.h"
 #include "storage/localimageloader.h"
 #include "storage/storage_account.h"
 #include "storage/file_upload.h"
@@ -378,6 +379,11 @@ HistoryWidget::HistoryWidget(
 		checkFieldAutocomplete();
 	}, Qt::QueuedConnection);
 
+	controller->session().data().shortcutMessages().shortcutsChanged(
+	) | rpl::start_with_next([=] {
+		checkFieldAutocomplete();
+	}, lifetime());
+
 	_fieldBarCancel->hide();
 
 	_topBar->hide();
@@ -435,12 +441,15 @@ HistoryWidget::HistoryWidget(
 	) | rpl::start_with_next([=](FieldAutocomplete::BotCommandChosen data) {
 		using Method = FieldAutocomplete::ChooseMethod;
 		const auto messages = &data.user->owner().shortcutMessages();
-		const auto shortcutId = (_peer
-			&& data.user->isSelf()
-			&& data.method != Method::ByTab)
-			? messages->lookupShortcutId(data.command.mid(1))
+		const auto shortcut = data.user->isSelf();
+		const auto command = data.command.mid(1);
+		const auto byTab = (data.method == Method::ByTab);
+		const auto shortcutId = (_peer && shortcut && !byTab)
+			? messages->lookupShortcutId(command)
 			: BusinessShortcutId();
-		if (!shortcutId) {
+		if (shortcut && command.isEmpty()) {
+			controller->showSettings(Settings::QuickRepliesId());
+		} else if (!shortcutId) {
 			insertHashtagOrBotCommand(data.command, data.method);
 		} else if (!_peer->session().premium()) {
 			ShowPremiumPreviewToBuy(
diff --git a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
index d45d94750..cc55987cb 100644
--- a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "settings/business/settings_quick_replies.h"
 
+#include "boxes/premium_preview_box.h"
 #include "core/application.h"
 #include "data/business/data_shortcut_messages.h"
 #include "data/data_session.h"
@@ -96,6 +97,12 @@ void QuickReplies::setupContent(
 			));
 
 			add->setClickedCallback([=] {
+				if (!controller->session().premium()) {
+					ShowPremiumPreviewToBuy(
+						controller,
+						PremiumFeature::QuickReplies);
+					return;
+				}
 				const auto submit = [=](QString name, Fn<void()> close) {
 					const auto id = messages->emplaceShortcut(name);
 					showOther(ShortcutMessagesId(id));
diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
index 91ba0c63f..2e1f577ca 100644
--- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "base/call_delayed.h"
 #include "boxes/delete_messages_box.h"
 #include "boxes/premium_limits_box.h"
+#include "boxes/premium_preview_box.h"
 #include "boxes/send_files_box.h"
 #include "chat_helpers/tabbed_selector.h"
 #include "core/file_utilities.h"
@@ -253,6 +254,7 @@ private:
 	void showAtEnd();
 	void finishSending();
 	void refreshEmptyText();
+	bool showPremiumRequired() const;
 
 	const not_null<Window::SessionController*> _controller;
 	const not_null<Main::Session*> _session;
@@ -508,6 +510,12 @@ void ShortcutMessages::fillTopBarMenu(
 	const auto messages = &owner->shortcutMessages();
 
 	addAction(tr::lng_context_edit_shortcut(tr::now), [=] {
+		if (!_controller->session().premium()) {
+			ShowPremiumPreviewToBuy(
+				_controller,
+				PremiumFeature::QuickReplies);
+			return;
+		}
 		const auto submit = [=](QString name, Fn<void()> close) {
 			const auto id = _shortcutId.current();
 			const auto error = [=](QString text) {
@@ -773,6 +781,7 @@ QPointer<Ui::RpWidget> ShortcutMessages::createPinnedToBottom(
 	_composeControls = std::make_unique<ComposeControls>(
 		dynamic_cast<Ui::RpWidget*>(_scroll->parentWidget()),
 		ComposeControlsDescriptor{
+			.stOverride = &st::repliesComposeControls,
 			.show = _controller->uiShow(),
 			.unavailableEmojiPasted = [=](not_null<DocumentData*> emoji) {
 				listShowPremiumToast(emoji);
@@ -1127,6 +1136,9 @@ bool ShortcutMessages::showSendingFilesError(
 bool ShortcutMessages::showSendingFilesError(
 		const Ui::PreparedList &list,
 		std::optional<bool> compress) const {
+	if (showPremiumRequired()) {
+		return true;
+	}
 	const auto text = [&] {
 		using Error = Ui::PreparedList::Error;
 		switch (list.error) {
@@ -1171,6 +1183,9 @@ void ShortcutMessages::send() {
 }
 
 void ShortcutMessages::sendVoice(ComposeControls::VoiceToSend &&data) {
+	if (showPremiumRequired()) {
+		return;
+	}
 	auto action = prepareSendAction(data.options);
 	_session->api().sendVoiceMessage(
 		data.bytes,
@@ -1184,6 +1199,9 @@ void ShortcutMessages::sendVoice(ComposeControls::VoiceToSend &&data) {
 }
 
 void ShortcutMessages::send(Api::SendOptions options) {
+	if (showPremiumRequired()) {
+		return;
+	}
 	_cornerButtons.clearReplyReturns();
 
 	auto message = Api::MessageToSend(prepareSendAction(options));
@@ -1409,6 +1427,9 @@ void ShortcutMessages::sendingFilesConfirmed(
 
 void ShortcutMessages::chooseAttach(
 		std::optional<bool> overrideSendImagesAsPhotos) {
+	if (showPremiumRequired()) {
+		return;
+	}
 	_choosingAttach = false;
 
 	const auto filter = (overrideSendImagesAsPhotos == true)
@@ -1472,6 +1493,10 @@ bool ShortcutMessages::sendExistingDocument(
 		not_null<DocumentData*> document,
 		Api::SendOptions options,
 		std::optional<MsgId> localId) {
+	if (showPremiumRequired()) {
+		return false;
+	}
+
 	Api::SendExistingDocument(
 		Api::MessageToSend(prepareSendAction(options)),
 		document,
@@ -1489,6 +1514,9 @@ void ShortcutMessages::sendExistingPhoto(not_null<PhotoData*> photo) {
 bool ShortcutMessages::sendExistingPhoto(
 		not_null<PhotoData*> photo,
 		Api::SendOptions options) {
+	if (showPremiumRequired()) {
+		return false;
+	}
 	Api::SendExistingPhoto(
 		Api::MessageToSend(prepareSendAction(options)),
 		photo);
@@ -1501,6 +1529,9 @@ bool ShortcutMessages::sendExistingPhoto(
 void ShortcutMessages::sendInlineResult(
 		not_null<InlineBots::Result*> result,
 		not_null<UserData*> bot) {
+	if (showPremiumRequired()) {
+		return;
+	}
 	const auto errorText = result->getErrorOnSend(_history);
 	if (!errorText.isEmpty()) {
 		_controller->showToast(errorText);
@@ -1520,6 +1551,9 @@ void ShortcutMessages::sendInlineResult(
 		not_null<UserData*> bot,
 		Api::SendOptions options,
 		std::optional<MsgId> localMessageId) {
+	if (showPremiumRequired()) {
+		return;
+	}
 	auto action = prepareSendAction(options);
 	action.generateLocal = true;
 	_session->api().sendInlineResult(bot, result, action, localMessageId);
@@ -1564,6 +1598,14 @@ FullReplyTo ShortcutMessages::replyTo() const {
 	return _composeControls->replyingToMessage();
 }
 
+bool ShortcutMessages::showPremiumRequired() const {
+	if (!_controller->session().premium()) {
+		ShowPremiumPreviewToBuy(_controller, PremiumFeature::QuickReplies);
+		return true;
+	}
+	return false;
+}
+
 } // namespace
 
 Type ShortcutMessagesId(int shortcutId) {
diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style
index b4217d85b..655ea9896 100644
--- a/Telegram/SourceFiles/ui/chat/chat.style
+++ b/Telegram/SourceFiles/ui/chat/chat.style
@@ -1050,6 +1050,9 @@ awayEmptyIcon: icon{{ "chat/large_away", msgServiceFg }};
 repliesEmptyWidth: 264px;
 repliesEmptySkip: 16px;
 repliesEmptyPadding: margins(10px, 20px, 10px, 16px);
+repliesComposeControls: ComposeControls(defaultComposeControls) {
+	tabbedHeightMin: 220px;
+}
 
 boostMessageIcon: icon {{ "stories/boost_mini", windowFg }};
 boostMessageIconPadding: margins(0px, 2px, 0px, 0px);

From 9483d17fc8a236bc051baa5ce913b0be7f2c7b25 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Thu, 7 Mar 2024 21:24:32 +0400
Subject: [PATCH 081/108] Validate quick reply name.

---
 .../business/settings_quick_replies.cpp        | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
index cc55987cb..97b0c9a61 100644
--- a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
@@ -162,6 +162,22 @@ void QuickReplies::setupContent(
 	Ui::ResizeFitChild(this, content);
 }
 
+[[nodiscard]] bool ValidShortcutName(const QString &name) {
+	if (name.isEmpty() || name.size() > 32) {
+		return false;
+	}
+	for (const auto &ch : name) {
+		if (!ch.isLetterOrNumber()
+			&& (ch != '_')
+			&& (ch != 0x200c)
+			&& (ch != 0x00b7)
+			&& (ch < 0x0d80 || ch > 0x0dff)) {
+			return false;
+		}
+	}
+	return true;
+}
+
 } // namespace
 
 Type QuickRepliesId() {
@@ -195,7 +211,7 @@ void EditShortcutNameBox(
 
 	const auto callback = [=] {
 		const auto name = field->getLastText().trimmed();
-		if (name.isEmpty()) {
+		if (!ValidShortcutName(name)) {
 			field->showError();
 		} else {
 			submit(name, [weak = Ui::MakeWeak(box)] {

From bef26cf9d2e4dec17940ca6963a0165e1fd3d234 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Thu, 7 Mar 2024 21:24:46 +0400
Subject: [PATCH 082/108] Force right-alignment in quick replies editing.

---
 .../SourceFiles/history/view/history_view_list_widget.cpp   | 6 +++++-
 .../SourceFiles/history/view/history_view_list_widget.h     | 2 ++
 .../settings/business/settings_shortcut_messages.cpp        | 1 +
 3 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
index c37342fa2..9c2f94086 100644
--- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
@@ -1734,7 +1734,7 @@ void ListWidget::elementHandleViaClick(not_null<UserData*> bot) {
 }
 
 bool ListWidget::elementIsChatWide() {
-	return _isChatWide;
+	return _overrideIsChatWide.value_or(_isChatWide);
 }
 
 not_null<Ui::PathShiftGradient*> ListWidget::elementPathShiftGradient() {
@@ -3963,6 +3963,10 @@ void ListWidget::setEmptyInfoWidget(base::unique_qptr<Ui::RpWidget> &&w) {
 	}
 }
 
+void ListWidget::overrideIsChatWide(bool isWide) {
+	_overrideIsChatWide = isWide;
+}
+
 ListWidget::~ListWidget() {
 	// Destroy child widgets first, because they may invoke leaveEvent-s.
 	_emptyInfo = nullptr;
diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h
index c15a041b8..12e09accf 100644
--- a/Telegram/SourceFiles/history/view/history_view_list_widget.h
+++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h
@@ -343,6 +343,7 @@ public:
 	QString elementAuthorRank(not_null<const Element*> view) override;
 
 	void setEmptyInfoWidget(base::unique_qptr<Ui::RpWidget> &&w);
+	void overrideIsChatWide(bool isWide);
 
 	~ListWidget();
 
@@ -725,6 +726,7 @@ private:
 	bool _refreshingViewer = false;
 	bool _showFinished = false;
 	bool _resizePending = false;
+	std::optional<bool> _overrideIsChatWide;
 
 	// _menu must be destroyed before _whoReactedMenuLifetime.
 	rpl::lifetime _whoReactedMenuLifetime;
diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
index 2e1f577ca..3b1143126 100644
--- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
@@ -372,6 +372,7 @@ ShortcutMessages::ShortcutMessages(
 		this,
 		controller,
 		static_cast<ListDelegate*>(this));
+	_inner->overrideIsChatWide(false);
 
 	_scroll->sizeValue() | rpl::filter([](QSize size) {
 		return !size.isEmpty();

From 4975cf2ec1e07140078ad598d0f33501057a8ec1 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Thu, 7 Mar 2024 22:59:44 +0400
Subject: [PATCH 083/108] Implement double-drumroll time picker.

---
 .../inline_bots/bot_attach_web_view.cpp       |   2 +-
 .../business/settings_working_hours.cpp       | 162 +++++++++++++-----
 .../ui/widgets/vertical_drum_picker.cpp       |  17 +-
 .../ui/widgets/vertical_drum_picker.h         |   3 +
 4 files changed, 136 insertions(+), 48 deletions(-)

diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp
index 39c48f7ad..056aa224e 100644
--- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp
+++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp
@@ -571,7 +571,7 @@ bool AttachWebView::botHandleLocalUri(QString uri, bool keepOpen) {
 			Core::App().domain().activate(&bot->session().account());
 		}
 		const auto window = !bot->session().windows().empty()
-			? bot->session().windows().front()
+			? bot->session().windows().front().get()
 			: nullptr;
 		const auto variant = QVariant::fromValue(ClickHandlerContext{
 			.attachBotWebviewUrl = shownUrl,
diff --git a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
index 1d06c99ce..db30dd847 100644
--- a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
@@ -86,6 +86,34 @@ private:
 		: wrap(time == kDay ? 0 : time);
 }
 
+[[nodiscard]] QString FormatTimeHour(TimeId time) {
+	const auto wrap = [](TimeId value) {
+		return QString::number(value / 3600).rightJustified(2, u'0');
+	};
+	if (time < kDay) {
+		return wrap(time);
+	}
+	const auto wrapped = wrap(time - kDay);
+	const auto result = tr::lng_hours_next_day(tr::now, lt_time, wrapped);
+	const auto i = result.indexOf(wrapped);
+	return (i >= 0) ? (result.left(i) + wrapped) : result;
+}
+
+[[nodiscard]] QString FormatTimeMinute(TimeId time) {
+	const auto wrap = [](TimeId value) {
+		return QString::number(value / 60).rightJustified(2, u'0');
+	};
+	if (time < kDay) {
+		return wrap(time);
+	}
+	const auto wrapped = wrap(time - kDay);
+	const auto result = tr::lng_hours_next_day(tr::now, lt_time, wrapped);
+	const auto i = result.indexOf(wrapped);
+	return (i >= 0)
+		? (wrapped + result.right(result.size() - i - wrapped.size()))
+		: result;
+}
+
 [[nodiscard]] QString JoinIntervals(const Data::WorkingIntervals &data) {
 	auto result = QStringList();
 	result.reserve(data.list.size());
@@ -105,49 +133,97 @@ void EditTimeBox(
 		Fn<void(TimeId)> save) {
 	Expects(low <= high);
 
-	const auto values = (high - low + 60) / 60;
-	const auto startIndex = (value - low) / 60;
-
 	const auto content = box->addRow(object_ptr<Ui::FixedHeightWidget>(
 		box,
 		st::settingsWorkingHoursPicker));
 
 	const auto font = st::boxTextFont;
 	const auto itemHeight = st::settingsWorkingHoursPickerItemHeight;
-	auto paintCallback = [=](
-			QPainter &p,
-			int index,
-			float64 y,
-			float64 distanceFromCenter,
-			int outerWidth) {
-		const auto r = QRectF(0, y, outerWidth, itemHeight);
-		const auto progress = std::abs(distanceFromCenter);
-		const auto revProgress = 1. - progress;
-		p.save();
-		p.translate(r.center());
-		constexpr auto kMinYScale = 0.2;
-		const auto yScale = kMinYScale
-			+ (1. - kMinYScale) * anim::easeOutCubic(1., revProgress);
-		p.scale(1., yScale);
-		p.translate(-r.center());
-		p.setOpacity(revProgress);
-		p.setFont(font);
-		p.setPen(st::defaultFlatLabel.textFg);
-		p.drawText(r, FormatDayTime(low + index * 60, true), style::al_center);
-		p.restore();
+	const auto picker = [=](
+			int count,
+			int startIndex,
+			Fn<void(QPainter &p, QRectF rect, int index)> paint) {
+		auto paintCallback = [=](
+				QPainter &p,
+				int index,
+				float64 y,
+				float64 distanceFromCenter,
+				int outerWidth) {
+			const auto r = QRectF(0, y, outerWidth, itemHeight);
+			const auto progress = std::abs(distanceFromCenter);
+			const auto revProgress = 1. - progress;
+			p.save();
+			p.translate(r.center());
+			constexpr auto kMinYScale = 0.2;
+			const auto yScale = kMinYScale
+				+ (1. - kMinYScale) * anim::easeOutCubic(1., revProgress);
+			p.scale(1., yScale);
+			p.translate(-r.center());
+			p.setOpacity(revProgress);
+			p.setFont(font);
+			p.setPen(st::defaultFlatLabel.textFg);
+			paint(p, r, index);
+			p.restore();
+		};
+		return Ui::CreateChild<Ui::VerticalDrumPicker>(
+			content,
+			std::move(paintCallback),
+			count,
+			itemHeight,
+			startIndex);
 	};
 
-	const auto picker = Ui::CreateChild<Ui::VerticalDrumPicker>(
-		content,
-		std::move(paintCallback),
-		values,
-		itemHeight,
-		startIndex);
+	const auto hoursCount = (high - low + 3600) / 3600;
+	const auto hoursStartIndex = (value - low) / 3600;
+	const auto hoursPaint = [=](QPainter &p, QRectF rect, int index) {
+		p.drawText(
+			rect,
+			FormatTimeHour(((low / 3600) + index) * 3600),
+			style::al_right);
+	};
+	const auto hours = picker(hoursCount, hoursStartIndex, hoursPaint);
+	const auto minutes = content->lifetime().make_state<
+		rpl::variable<Ui::VerticalDrumPicker*>
+	>(nullptr);
+	const auto minutesStart = content->lifetime().make_state<TimeId>();
+	hours->value() | rpl::start_with_next([=](int hoursIndex) {
+		const auto start = std::max(low, (hoursIndex + (low / 3600)) * 3600);
+		const auto end = std::min(high, ((start / 3600) * 60 + 59) * 60);
+		const auto minutesCount = (end - start + 60) / 60;
+		const auto minutesStartIndex = minutes->current()
+			? std::clamp(
+				((((*minutesStart) / 60 + minutes->current()->index()) % 60)
+					- ((start / 60) % 60)),
+				0,
+				(minutesCount - 1))
+			: std::clamp((value - start) / 60, 0, minutesCount - 1);
+		*minutesStart = start;
 
-	content->sizeValue(
-	) | rpl::start_with_next([=](const QSize &s) {
-		picker->resize(s.width(), s.height());
-		picker->moveToLeft((s.width() - picker->width()) / 2, 0);
+		const auto minutesPaint = [=](QPainter &p, QRectF rect, int index) {
+			p.drawText(
+				rect,
+				FormatTimeMinute((((start / 60) + index) % 60) * 60),
+				style::al_left);
+		};
+		const auto updated = picker(
+			minutesCount,
+			minutesStartIndex,
+			minutesPaint);
+		delete minutes->current();
+		*minutes = updated;
+		minutes->current()->show();
+	}, hours->lifetime());
+
+	const auto separator = u":"_q;
+	const auto separatorWidth = st::boxTextFont->width(separator);
+
+	rpl::combine(
+		content->sizeValue(),
+		minutes->value()
+	) | rpl::start_with_next([=](QSize s, Ui::VerticalDrumPicker *minutes) {
+		const auto half = (s.width() - separatorWidth) / 2;
+		hours->setGeometry(0, 0, half, s.height());
+		minutes->setGeometry(half + separatorWidth, 0, half, s.height());
 	}, content->lifetime());
 
 	content->paintRequest(
@@ -163,28 +239,22 @@ void EditTimeBox(
 			st::defaultInputField.borderActive);
 		p.fillRect(lineRect.translated(0, itemHeight / 2), st::activeLineFg);
 		p.fillRect(lineRect.translated(0, -itemHeight / 2), st::activeLineFg);
+		p.drawText(QRectF(content->rect()), separator, style::al_center);
 	}, content->lifetime());
 
-	base::install_event_filter(content, [=](not_null<QEvent*> e) {
-		if ((e->type() == QEvent::MouseButtonPress)
-			|| (e->type() == QEvent::MouseButtonRelease)
-			|| (e->type() == QEvent::MouseMove)) {
-			picker->handleMouseEvent(static_cast<QMouseEvent*>(e.get()));
-		} else if (e->type() == QEvent::Wheel) {
-			picker->handleWheelEvent(static_cast<QWheelEvent*>(e.get()));
-		}
-		return base::EventFilterResult::Continue;
-	});
 	base::install_event_filter(box, [=](not_null<QEvent*> e) {
 		if (e->type() == QEvent::KeyPress) {
-			picker->handleKeyEvent(static_cast<QKeyEvent*>(e.get()));
+			hours->handleKeyEvent(static_cast<QKeyEvent*>(e.get()));
 		}
 		return base::EventFilterResult::Continue;
 	});
 
 	box->addButton(tr::lng_settings_save(), [=] {
 		const auto weak = Ui::MakeWeak(box);
-		save(std::clamp(low + picker->index() * 60, low, high));
+		save(std::clamp(
+			((*minutesStart) / 60 + minutes->current()->index()) * 60,
+			low,
+			high));
 		if (const auto strong = weak.data()) {
 			strong->closeBox();
 		}
diff --git a/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.cpp b/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.cpp
index 58fd123ea..73f2ecf8c 100644
--- a/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.cpp
+++ b/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.cpp
@@ -92,6 +92,8 @@ VerticalDrumPicker::VerticalDrumPicker(
 			_loopData.minIndex = -_itemsVisible.centerOffset;
 			_loopData.maxIndex = _itemsCount - 1 - _itemsVisible.centerOffset;
 		}
+
+		_changes.fire({});
 	}, lifetime());
 
 	paintRequest(
@@ -144,7 +146,9 @@ void VerticalDrumPicker::increaseShift(float64 by) {
 		index++;
 		index = normalizedIndex(index);
 	}
-	if (!_loopData.looped && (index <= _loopData.minIndex)) {
+	if (_loopData.minIndex == _loopData.maxIndex) {
+		_shift = 0.;
+	} else if (!_loopData.looped && (index <= _loopData.minIndex)) {
 		_shift = std::min(0., shift);
 		_index = _loopData.minIndex;
 	} else if (!_loopData.looped && (index >= _loopData.maxIndex)) {
@@ -154,6 +158,7 @@ void VerticalDrumPicker::increaseShift(float64 by) {
 		_shift = shift;
 		_index = index;
 	}
+	_changes.fire({});
 	update();
 }
 
@@ -270,4 +275,14 @@ int VerticalDrumPicker::index() const {
 	return normalizedIndex(_index + _itemsVisible.centerOffset);
 }
 
+rpl::producer<int> VerticalDrumPicker::changes() const {
+	return _changes.events() | rpl::map([=] { return index(); });
+}
+
+rpl::producer<int> VerticalDrumPicker::value() const {
+	return rpl::single(index())
+		| rpl::then(changes())
+		| rpl::distinct_until_changed();
+}
+
 } // namespace Ui
diff --git a/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.h b/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.h
index 4140d3397..63c0cbc2a 100644
--- a/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.h
+++ b/Telegram/SourceFiles/ui/widgets/vertical_drum_picker.h
@@ -52,6 +52,8 @@ public:
 		bool looped = false);
 
 	[[nodiscard]] int index() const;
+	[[nodiscard]] rpl::producer<int> changes() const;
+	[[nodiscard]] rpl::producer<int> value() const;
 
 	void handleWheelEvent(not_null<QWheelEvent*> e);
 	void handleMouseEvent(not_null<QMouseEvent*> e);
@@ -84,6 +86,7 @@ private:
 
 	int _index = 0;
 	float64 _shift = 0.;
+	rpl::event_stream<> _changes;
 
 	struct {
 		const bool looped;

From 5397f64b23df621b025c3b36906d6de5324cb6be Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 8 Mar 2024 10:52:58 +0400
Subject: [PATCH 084/108] Add Telegram Business icon to Premium promo.

---
 .../Resources/icons/settings/premium/market.png   | Bin 0 -> 396 bytes
 .../icons/settings/premium/market@2x.png          | Bin 0 -> 607 bytes
 .../icons/settings/premium/market@3x.png          | Bin 0 -> 955 bytes
 Telegram/SourceFiles/settings/settings.style      |   2 +-
 .../SourceFiles/settings/settings_premium.cpp     |   2 +-
 5 files changed, 2 insertions(+), 2 deletions(-)
 create mode 100644 Telegram/Resources/icons/settings/premium/market.png
 create mode 100644 Telegram/Resources/icons/settings/premium/market@2x.png
 create mode 100644 Telegram/Resources/icons/settings/premium/market@3x.png

diff --git a/Telegram/Resources/icons/settings/premium/market.png b/Telegram/Resources/icons/settings/premium/market.png
new file mode 100644
index 0000000000000000000000000000000000000000..3c2b9cd2da695598c4671a97feeded862d19a48e
GIT binary patch
literal 396
zcmV;70dxL|P)<h;3K|Lk000e1NJLTq000;O000;W0ssI2ZxyPq00009a7bBm000XU
z000XU0RWnu7ytkPK1oDDR5*?8lfR0CFdWBo!GDWGap((lDSZTY#|-Wr`W!-E!50u*
zyA`^3EI5craC38W(4i;|iD_;Sjyp}`aopI?_WSwGhcA%;@a7MkPAA3~eF5hjV?00g
zA$1)NhhZ3`pEOOoTrN%1EQsPb-fp*)QblD5VcWI{p_hp?O~tRN>soG!qG(J|O25*C
z5XCajbBSoT+Yv$-V+f)1`7DG;l4P^lbX~_e-|zQv919_eqHr9iZCfG4@px3+!2b~h
zfjZ#(zU#URmM{z%W9kgXILorhQk%?#kiPHLqOR+n&*x+bCo|{VvMjY|y<X2n(?*n1
z(=_k*`{VJDT6A4styX1OE{H~K+qP|6-D^Q)7zO}f-uycd!ZG$^Ihn85>ljr40GR(Z
q$?JC<$MZZzqpGTIx0@pHM$RWs|MSec)(l$!0000<MNUMnLSTZQ`>eA7

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/settings/premium/market@2x.png b/Telegram/Resources/icons/settings/premium/market@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..1ae9e950a0de624e05f578c59051806177afc232
GIT binary patch
literal 607
zcmV-l0-*hgP)<h;3K|Lk000e1NJLTq001xm001xu0ssI2*kEqZ00009a7bBm000XU
z000XU0RWnu7ytkQ5lKWrR9J=Wm%obYKorK$Jzj01=u=pTm5-4pFomrt0v3WG*!d87
z0`UPvEbS$oNFW9YA{GI$(4dmU%q_wVS+9FD&SZC4HowB+bLKnWOium`Kt@JJ#@F(T
zW>BNin9t{hLV>xA@B7VWb2ghXJG@~$9usaRlL<{NsvQD=>2zADRDvL2SPvqePA5?m
z8RmXH$vdQ~YOPjd+nYFuxL&W@?KZoGqA0HG@^?=h&+~e{9%Gu&;+`s@XTRTnEsZ2e
zACV@WNtWfOrJwhZ>$>@Tp1*J20}+ED$mMcR{`yxEMe!jGB5IoUkPi_Tiv=PkiU6Q#
zT3if*01;cQ7VQdb+m5amA!M`J0Kj&;jf=b84gf66ii`XG9so?!jElBy1AwmUaq)0C
z(549qO$i~ou1BI_7@=qwMkKCQt57se^Z(*<xeP_ivR+G*@gkj0hu=g|6n+zczYRVb
zjd)B{RV9QRkH^H3Pj34M%H=WuRI62<K=pbZ07|7&3W0pzkA9g6rxQX_2=vL&2I72;
z-0yeFk+gw|#Ug7M002P{c&5L>aU4R(?RM*SyKD}!EMG1cLWt|SiG!2B7#+tE1R;DK
zvIUCpJTLL+@|)g{vyCX%^KI|4PvaW~;_u~`NG^6hpVKPlI8F*Kk|ae(1y0^OX~dyD
t+Ybf<o`?1IdL0gj%+oV6GBSQLe*iBkxyY_Q*-iie002ovPDHLkV1h+s1gQW3

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/settings/premium/market@3x.png b/Telegram/Resources/icons/settings/premium/market@3x.png
new file mode 100644
index 0000000000000000000000000000000000000000..5d132577d05a27bc5b568750b2d6e326d23c6005
GIT binary patch
literal 955
zcmV;s14R6ZP)<h;3K|Lk000e1NJLTq002k;002k`0ssI2+K(g<00009a7bBm000XU
z000XU0RWnu7ytkRZAnByRA_<in!ii)Kp4lL_k2YiyGZR^p&+7FEMf;c1Q#=Oa4Wbt
zcM=5ug@R*2gitz_Y+c;CI7mT4tEC{tu}GyuLm^;G?d2T~LwK8?mt58Nz0Xv7&-b3^
z+Z>lCmjZA&91e%W;cz(C{GwLYYPG||LyU2V)tEsD-QC>@g1~5gYx4QLuIsw4PnkjH
z&*$@W6(Ty>Xf&b|`eibi&<W|6;_dA%>vdAA)sByknWo@;8)%l|`T6<!`ugJHV(8V(
zGMP+XUS6)QuGprLWm#2KeeS5LD$BC3Qhi*p$H&L5tt|jxcki+TAp`(VPfy$1+a4ue
zjWrsLWHLE_OJ)#4&1Q3FXU9{pmmoz^zT6c6V2qQ=WTVmW66|j5{{DV<clXO(5s0^X
zz3v+6VoZ{xgM))_;);MV-rwJsB*{6<#h7z%@%HxSFgh3v#^bTw=y*IH3<lJb9~~Vz
zjDo8%yK99I>i7GlQi<$WmI38*x!dhp1|WpG-EO&Dc954!rGCF}7x3}%;as^3k|bHS
z@Vc&FUS0x#rfIVQx~}(nJpd>a3U=~Bp#T8AUe8jlX&L}rTwK`6U6-OetyYWNCCyJ0
zilUIwTrOvPY5s6}dP)M??e?s^-EKR}bGaM|P!wfW-fFdix*}AoRdQuW6HuvC%%j<C
z)~KFj6-Ch;Aj|S_I3%OP;qb2>LKH>gw<DfUPEO1Ll}g2EcQj3_R;%H-GRR~ysZ`2t
zG!}~qf)I&B>;ls1bUK~38;wLFf*{0VF}r|NDwWA(JQvIPUszdL`Nt9g0CYN?Ft;Xq
z$fnZibXX?>0Jy%sHg>RLvB)k_u~;+`-Q3*xmHR%WibkXG*^#WRt@)LEXLWVeNJKvS
z!nZN=dDwJ1^(*&|xoM&}grhuHoC9`UqE~rXb$mCtF)NrpfCijAkg=tur66*qlLs=k
zv9S?E?#$-qW)L~OF^q9M9)EavNF)+LDwrjaNYv}~csw3tHU17#0mgW5Z}0i}Imjdq
zlLwIpG8Q~NJNS10)8NK_8r;|?3kwS@>IgllZ(~fK!9q;x+nC4KQ{h69l;-97`}_Oy
z^74Fc%?P2<Xf&s@Dp3^Y_KICq)oeCPQ6&98b0kTMMx#vklGoSQv$HdH^K&>H4u`|x
da5$Wwz#kqakS`xh^u+)G002ovPDHLkV1idz$Rz*(

literal 0
HcmV?d00001

diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style
index 00f1121d1..9dcb8e191 100644
--- a/Telegram/SourceFiles/settings/settings.style
+++ b/Telegram/SourceFiles/settings/settings.style
@@ -94,7 +94,7 @@ settingsPremiumIconTranslations: icon {{ "settings/premium/translations", settin
 settingsPremiumIconTags: icon {{ "settings/premium/tags", settingsIconFg }};
 settingsPremiumIconLastSeen: icon {{ "settings/premium/lastseen", settingsIconFg }};
 settingsPremiumIconPrivacy: icon {{ "settings/premium/privacy", settingsIconFg }};
-settingsPremiumIconBusiness: icon {{ "settings/premium/privacy", settingsIconFg }};
+settingsPremiumIconBusiness: icon {{ "settings/premium/market", settingsIconFg }};
 
 settingsStoriesIconOrder: icon {{ "settings/premium/stories_order", premiumButtonBg1 }};
 settingsStoriesIconStealth: icon {{ "menu/stealth", premiumButtonBg1 }};
diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp
index 2b9d5e35a..4e81d19c6 100644
--- a/Telegram/SourceFiles/settings/settings_premium.cpp
+++ b/Telegram/SourceFiles/settings/settings_premium.cpp
@@ -371,7 +371,7 @@ using Order = std::vector<QString>;
 		{
 			u"business"_q,
 			Entry{
-				&st::settingsPremiumIconPlay, AssertIsDebug()
+				&st::settingsPremiumIconBusiness,
 				tr::lng_premium_summary_subtitle_business(),
 				tr::lng_premium_summary_about_business(),
 				PremiumFeature::Business,

From 5ebd5852ba7c223528341737f9bbba30dad9876d Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 8 Mar 2024 10:55:50 +0400
Subject: [PATCH 085/108] Update lib_ui submodule.

---
 Telegram/lib_ui | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Telegram/lib_ui b/Telegram/lib_ui
index 7eaf7f8aa..edfcac751 160000
--- a/Telegram/lib_ui
+++ b/Telegram/lib_ui
@@ -1 +1 @@
-Subproject commit 7eaf7f8aaa5c7aac9cbc6e5dc92dea7944003eba
+Subproject commit edfcac751dbdd93fcabac4ebf0ff731ceab8af0f

From 2c03d90fc896761cdb137ddb058c5c2a2a05612e Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 8 Mar 2024 11:47:29 +0400
Subject: [PATCH 086/108] Fix the new time picker.

---
 Telegram/Resources/langs/lang.strings         |  1 +
 .../business/settings_working_hours.cpp       | 41 +++++++++++--------
 2 files changed, 24 insertions(+), 18 deletions(-)

diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 2489020b8..2805ebaf6 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -2205,6 +2205,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_hours_closed" = "Closed";
 "lng_hours_open_full" = "Open 24 hours";
 "lng_hours_next_day" = "{time} (Next day)";
+"lng_hours_on_next_day" = "Next day {time}";
 "lng_hours_time_zone_title" = "Choose Time Zone";
 "lng_hours_add_button" = "Add a Set of Hours";
 "lng_hours_opening" = "Opening Time";
diff --git a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
index db30dd847..525d069a4 100644
--- a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
@@ -94,20 +94,20 @@ private:
 		return wrap(time);
 	}
 	const auto wrapped = wrap(time - kDay);
-	const auto result = tr::lng_hours_next_day(tr::now, lt_time, wrapped);
+	const auto result = tr::lng_hours_on_next_day(tr::now, lt_time, wrapped);
 	const auto i = result.indexOf(wrapped);
 	return (i >= 0) ? (result.left(i) + wrapped) : result;
 }
 
 [[nodiscard]] QString FormatTimeMinute(TimeId time) {
 	const auto wrap = [](TimeId value) {
-		return QString::number(value / 60).rightJustified(2, u'0');
+		return QString::number((value / 60) % 60).rightJustified(2, u'0');
 	};
 	if (time < kDay) {
 		return wrap(time);
 	}
 	const auto wrapped = wrap(time - kDay);
-	const auto result = tr::lng_hours_next_day(tr::now, lt_time, wrapped);
+	const auto result = tr::lng_hours_on_next_day(tr::now, lt_time, wrapped);
 	const auto i = result.indexOf(wrapped);
 	return (i >= 0)
 		? (wrapped + result.right(result.size() - i - wrapped.size()))
@@ -174,7 +174,7 @@ void EditTimeBox(
 	};
 
 	const auto hoursCount = (high - low + 3600) / 3600;
-	const auto hoursStartIndex = (value - low) / 3600;
+	const auto hoursStartIndex = (value / 3600) - (low / 3600);
 	const auto hoursPaint = [=](QPainter &p, QRectF rect, int index) {
 		p.drawText(
 			rect,
@@ -185,6 +185,23 @@ void EditTimeBox(
 	const auto minutes = content->lifetime().make_state<
 		rpl::variable<Ui::VerticalDrumPicker*>
 	>(nullptr);
+
+	// hours->value() is valid only after size is set.
+	const auto separator = u":"_q;
+	const auto separatorWidth = st::boxTextFont->width(separator);
+	rpl::combine(
+		content->sizeValue(),
+		minutes->value()
+	) | rpl::start_with_next([=](QSize s, Ui::VerticalDrumPicker *minutes) {
+		const auto half = (s.width() - separatorWidth) / 2;
+		hours->setGeometry(0, 0, half, s.height());
+		if (minutes) {
+			minutes->setGeometry(half + separatorWidth, 0, half, s.height());
+		}
+	}, content->lifetime());
+
+	Ui::SendPendingMoveResizeEvents(hours);
+
 	const auto minutesStart = content->lifetime().make_state<TimeId>();
 	hours->value() | rpl::start_with_next([=](int hoursIndex) {
 		const auto start = std::max(low, (hoursIndex + (low / 3600)) * 3600);
@@ -196,13 +213,13 @@ void EditTimeBox(
 					- ((start / 60) % 60)),
 				0,
 				(minutesCount - 1))
-			: std::clamp((value - start) / 60, 0, minutesCount - 1);
+			: std::clamp((value / 60) - (start / 60), 0, minutesCount - 1);
 		*minutesStart = start;
 
 		const auto minutesPaint = [=](QPainter &p, QRectF rect, int index) {
 			p.drawText(
 				rect,
-				FormatTimeMinute((((start / 60) + index) % 60) * 60),
+				FormatTimeMinute(((start / 60) + index) * 60),
 				style::al_left);
 		};
 		const auto updated = picker(
@@ -214,18 +231,6 @@ void EditTimeBox(
 		minutes->current()->show();
 	}, hours->lifetime());
 
-	const auto separator = u":"_q;
-	const auto separatorWidth = st::boxTextFont->width(separator);
-
-	rpl::combine(
-		content->sizeValue(),
-		minutes->value()
-	) | rpl::start_with_next([=](QSize s, Ui::VerticalDrumPicker *minutes) {
-		const auto half = (s.width() - separatorWidth) / 2;
-		hours->setGeometry(0, 0, half, s.height());
-		minutes->setGeometry(half + separatorWidth, 0, half, s.height());
-	}, content->lifetime());
-
 	content->paintRequest(
 	) | rpl::start_with_next([=](const QRect &r) {
 		auto p = QPainter(content);

From d729e625e672ed1a4bfd373519a28f77663be729 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 8 Mar 2024 12:28:53 +0400
Subject: [PATCH 087/108] Show business section settings only when loaded.

---
 .../data/business/data_business_chatbots.cpp  | 11 +++
 .../data/business/data_business_chatbots.h    |  1 +
 .../data/business/data_business_info.cpp      |  4 +
 .../data/business/data_business_info.h        |  1 +
 .../settings/settings_business.cpp            | 74 ++++++++++++++++---
 5 files changed, 79 insertions(+), 12 deletions(-)

diff --git a/Telegram/SourceFiles/data/business/data_business_chatbots.cpp b/Telegram/SourceFiles/data/business/data_business_chatbots.cpp
index 8862c4e84..5c8c9f895 100644
--- a/Telegram/SourceFiles/data/business/data_business_chatbots.cpp
+++ b/Telegram/SourceFiles/data/business/data_business_chatbots.cpp
@@ -43,10 +43,21 @@ void Chatbots::preload() {
 				.recipients = FromMTP(_owner, bot.vrecipients()),
 				.repliesAllowed = bot.is_can_reply(),
 			};
+		} else {
+			_settings.force_assign(ChatbotsSettings());
 		}
+	}).fail([=](const MTP::Error &error) {
+		_requestId = 0;
+		LOG(("API Error: Could not get connected bots %1 (%2)"
+			).arg(error.code()
+			).arg(error.type()));
 	}).send();
 }
 
+bool Chatbots::loaded() const {
+	return _loaded;
+}
+
 const ChatbotsSettings &Chatbots::current() const {
 	return _settings.current();
 }
diff --git a/Telegram/SourceFiles/data/business/data_business_chatbots.h b/Telegram/SourceFiles/data/business/data_business_chatbots.h
index ca21baef6..6328b487d 100644
--- a/Telegram/SourceFiles/data/business/data_business_chatbots.h
+++ b/Telegram/SourceFiles/data/business/data_business_chatbots.h
@@ -31,6 +31,7 @@ public:
 	~Chatbots();
 
 	void preload();
+	[[nodiscard]] bool loaded() const;
 	[[nodiscard]] const ChatbotsSettings &current() const;
 	[[nodiscard]] rpl::producer<ChatbotsSettings> changes() const;
 	[[nodiscard]] rpl::producer<ChatbotsSettings> value() const;
diff --git a/Telegram/SourceFiles/data/business/data_business_info.cpp b/Telegram/SourceFiles/data/business/data_business_info.cpp
index 8a65201fe..151e36dcb 100644
--- a/Telegram/SourceFiles/data/business/data_business_info.cpp
+++ b/Telegram/SourceFiles/data/business/data_business_info.cpp
@@ -220,6 +220,10 @@ rpl::producer<Timezones> BusinessInfo::timezonesValue() const {
 	return _timezones.value();
 }
 
+bool BusinessInfo::timezonesLoaded() const {
+	return !_timezones.current().list.empty();
+}
+
 QString FindClosestTimezoneId(const std::vector<Timezone> &list) {
 	const auto local = QDateTime::currentDateTime();
 	const auto utc = QDateTime(local.date(), local.time(), Qt::UTC);
diff --git a/Telegram/SourceFiles/data/business/data_business_info.h b/Telegram/SourceFiles/data/business/data_business_info.h
index adea50a17..933d86910 100644
--- a/Telegram/SourceFiles/data/business/data_business_info.h
+++ b/Telegram/SourceFiles/data/business/data_business_info.h
@@ -37,6 +37,7 @@ public:
 	[[nodiscard]] rpl::producer<> greetingSettingsChanged() const;
 
 	void preloadTimezones();
+	[[nodiscard]] bool timezonesLoaded() const;
 	[[nodiscard]] rpl::producer<Timezones> timezonesValue() const;
 
 private:
diff --git a/Telegram/SourceFiles/settings/settings_business.cpp b/Telegram/SourceFiles/settings/settings_business.cpp
index 157438868..c9123c528 100644
--- a/Telegram/SourceFiles/settings/settings_business.cpp
+++ b/Telegram/SourceFiles/settings/settings_business.cpp
@@ -12,8 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/business/data_business_info.h"
 #include "data/business/data_business_chatbots.h"
 #include "data/business/data_shortcut_messages.h"
+#include "data/data_changes.h"
 #include "data/data_peer_values.h" // AmPremiumValue.
 #include "data/data_session.h"
+#include "data/data_user.h"
 #include "info/info_wrap_widget.h" // Info::Wrap.
 #include "info/settings/info_settings_widget.h" // SectionCustomTopBarData.
 #include "lang/lang_keys.h"
@@ -317,6 +319,8 @@ private:
 	rpl::event_stream<> _showFinished;
 	rpl::variable<QString> _buttonText;
 
+	PremiumFeature _waitingToShow = PremiumFeature::Business;
+
 };
 
 Business::Business(
@@ -355,20 +359,14 @@ void Business::setStepDataReference(std::any &data) {
 void Business::setupContent() {
 	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
 
-	_controller->session().data().chatbots().preload();
-	_controller->session().data().businessInfo().preload();
-	_controller->session().data().shortcutMessages().preloadShortcuts();
+	const auto owner = &_controller->session().data();
+	owner->chatbots().preload();
+	owner->businessInfo().preload();
+	owner->shortcutMessages().preloadShortcuts();
 
 	Ui::AddSkip(content, st::settingsFromFileTop);
 
-	AddBusinessSummary(content, _controller, [=](PremiumFeature feature) {
-		if (!_controller->session().premium()) {
-			_setPaused(true);
-			const auto hidden = crl::guard(this, [=] { _setPaused(false); });
-
-			ShowPremiumPreviewToBuy(_controller, feature, hidden);
-			return;
-		}
+	const auto showFeature = [=](PremiumFeature feature) {
 		showOther([&] {
 			switch (feature) {
 			case PremiumFeature::AwayMessage: return AwayMessageId();
@@ -378,8 +376,60 @@ void Business::setupContent() {
 			case PremiumFeature::QuickReplies: return QuickRepliesId();
 			case PremiumFeature::BusinessBots: return ChatbotsId();
 			}
-			Unexpected("Feature in Business::setupContent.");
+			Unexpected("Feature in showFeature.");
 		}());
+	};
+	const auto isReady = [=](PremiumFeature feature) {
+		switch (feature) {
+		case PremiumFeature::AwayMessage:
+			return owner->businessInfo().awaySettingsLoaded()
+				&& owner->shortcutMessages().shortcutsLoaded();
+		case PremiumFeature::BusinessHours:
+			return owner->session().user()->isFullLoaded()
+				&& owner->businessInfo().timezonesLoaded();
+		case PremiumFeature::BusinessLocation:
+			return owner->session().user()->isFullLoaded();
+		case PremiumFeature::GreetingMessage:
+			return owner->businessInfo().greetingSettingsLoaded()
+				&& owner->shortcutMessages().shortcutsLoaded();
+		case PremiumFeature::QuickReplies:
+			return owner->shortcutMessages().shortcutsLoaded();
+		case PremiumFeature::BusinessBots:
+			return owner->chatbots().loaded();
+		}
+		Unexpected("Feature in isReady.");
+	};
+	const auto check = [=] {
+		if (_waitingToShow != PremiumFeature::Business
+			&& isReady(_waitingToShow)) {
+			showFeature(
+				std::exchange(_waitingToShow, PremiumFeature::Business));
+		}
+	};
+
+	rpl::merge(
+		owner->businessInfo().awaySettingsChanged(),
+		owner->businessInfo().greetingSettingsChanged(),
+		owner->businessInfo().timezonesValue() | rpl::to_empty,
+		owner->shortcutMessages().shortcutsChanged(),
+		owner->chatbots().changes() | rpl::to_empty,
+		owner->session().changes().peerUpdates(
+			owner->session().user(),
+			Data::PeerUpdate::Flag::FullInfo) | rpl::to_empty
+	) | rpl::start_with_next(check, content->lifetime());
+
+	AddBusinessSummary(content, _controller, [=](PremiumFeature feature) {
+		if (!_controller->session().premium()) {
+			_setPaused(true);
+			const auto hidden = crl::guard(this, [=] { _setPaused(false); });
+
+			ShowPremiumPreviewToBuy(_controller, feature, hidden);
+			return;
+		} else if (!isReady(feature)) {
+			_waitingToShow = feature;
+		} else {
+			showFeature(feature);
+		}
 	});
 
 	Ui::ResizeFitChild(this, content);

From c345b50ab74e36f54ac43ccb46b58fcff50e7c1e Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 8 Mar 2024 12:55:20 +0400
Subject: [PATCH 088/108] Version 4.15.1.

Telegram Business features.
---
 Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +-
 Telegram/Resources/winrc/Telegram.rc         | 8 ++++----
 Telegram/Resources/winrc/Updater.rc          | 8 ++++----
 Telegram/SourceFiles/core/version.h          | 4 ++--
 Telegram/build/version                       | 8 ++++----
 changelog.txt                                | 4 ++++
 6 files changed, 19 insertions(+), 15 deletions(-)

diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml
index 54c27edef..dee90b06c 100644
--- a/Telegram/Resources/uwp/AppX/AppxManifest.xml
+++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml
@@ -10,7 +10,7 @@
   <Identity Name="TelegramMessengerLLP.TelegramDesktop"
     ProcessorArchitecture="ARCHITECTURE"
     Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
-    Version="4.15.0.0" />
+    Version="4.15.1.0" />
   <Properties>
     <DisplayName>Telegram Desktop</DisplayName>
     <PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc
index 20b938e47..b8aba7a6f 100644
--- a/Telegram/Resources/winrc/Telegram.rc
+++ b/Telegram/Resources/winrc/Telegram.rc
@@ -44,8 +44,8 @@ IDI_ICON1               ICON                    "..\\art\\icon256.ico"
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,15,0,0
- PRODUCTVERSION 4,15,0,0
+ FILEVERSION 4,15,1,0
+ PRODUCTVERSION 4,15,1,0
  FILEFLAGSMASK 0x3fL
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -62,10 +62,10 @@ BEGIN
         BEGIN
             VALUE "CompanyName", "Telegram FZ-LLC"
             VALUE "FileDescription", "Telegram Desktop"
-            VALUE "FileVersion", "4.15.0.0"
+            VALUE "FileVersion", "4.15.1.0"
             VALUE "LegalCopyright", "Copyright (C) 2014-2024"
             VALUE "ProductName", "Telegram Desktop"
-            VALUE "ProductVersion", "4.15.0.0"
+            VALUE "ProductVersion", "4.15.1.0"
         END
     END
     BLOCK "VarFileInfo"
diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc
index f4f694d5b..93f0a6769 100644
--- a/Telegram/Resources/winrc/Updater.rc
+++ b/Telegram/Resources/winrc/Updater.rc
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,15,0,0
- PRODUCTVERSION 4,15,0,0
+ FILEVERSION 4,15,1,0
+ PRODUCTVERSION 4,15,1,0
  FILEFLAGSMASK 0x3fL
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -53,10 +53,10 @@ BEGIN
         BEGIN
             VALUE "CompanyName", "Telegram FZ-LLC"
             VALUE "FileDescription", "Telegram Desktop Updater"
-            VALUE "FileVersion", "4.15.0.0"
+            VALUE "FileVersion", "4.15.1.0"
             VALUE "LegalCopyright", "Copyright (C) 2014-2024"
             VALUE "ProductName", "Telegram Desktop"
-            VALUE "ProductVersion", "4.15.0.0"
+            VALUE "ProductVersion", "4.15.1.0"
         END
     END
     BLOCK "VarFileInfo"
diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h
index 0f3431150..9bd491610 100644
--- a/Telegram/SourceFiles/core/version.h
+++ b/Telegram/SourceFiles/core/version.h
@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
 constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
 constexpr auto AppName = "Telegram Desktop"_cs;
 constexpr auto AppFile = "Telegram"_cs;
-constexpr auto AppVersion = 4015000;
-constexpr auto AppVersionStr = "4.15";
+constexpr auto AppVersion = 4015001;
+constexpr auto AppVersionStr = "4.15.1";
 constexpr auto AppBetaVersion = false;
 constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
diff --git a/Telegram/build/version b/Telegram/build/version
index cb2635869..60c27dece 100644
--- a/Telegram/build/version
+++ b/Telegram/build/version
@@ -1,7 +1,7 @@
-AppVersion         4015000
+AppVersion         4015001
 AppVersionStrMajor 4.15
-AppVersionStrSmall 4.15
-AppVersionStr      4.15.0
+AppVersionStrSmall 4.15.1
+AppVersionStr      4.15.1
 BetaChannel        0
 AlphaVersion       0
-AppVersionOriginal 4.15
+AppVersionOriginal 4.15.1
diff --git a/changelog.txt b/changelog.txt
index 4453ccb54..1d80bf48b 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,7 @@
+4.15.1 (08.03.24)
+
+- Telegram Business features.
+
 4.15 (18.02.24)
 
 - Stories from groups.

From 7c002cf8be1af0b5423e2c6f9570bfc8254239f8 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 8 Mar 2024 15:26:14 +0400
Subject: [PATCH 089/108] Version 4.15.1: Fix sending media albums.

---
 Telegram/SourceFiles/api/api_sending.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp
index 982992ce3..495b9467e 100644
--- a/Telegram/SourceFiles/api/api_sending.cpp
+++ b/Telegram/SourceFiles/api/api_sending.cpp
@@ -511,6 +511,7 @@ void SendConfirmedFile(
 			.date = HistoryItem::NewMessageDate(file->to.options),
 			.shortcutId = file->to.options.shortcutId,
 			.postAuthor = messagePostAuthor,
+			.groupedId = groupId,
 		}, caption, media);
 	}
 

From 0df8864ae0604418014bd61b84eeffd085a7a4d9 Mon Sep 17 00:00:00 2001
From: Ilya Fedin <fedin-ilja2010@ya.ru>
Date: Fri, 8 Mar 2024 12:25:13 +0400
Subject: [PATCH 090/108] Port specific_linux to cppgir

---
 Telegram/CMakeLists.txt                       |   3 +
 .../platform/linux/specific_linux.cpp         | 407 ++++++++++--------
 2 files changed, 224 insertions(+), 186 deletions(-)

diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index 76e06c8e1..9285d5ad2 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -1679,6 +1679,9 @@ else()
         desktop-app::external_glibmm
     )
 
+    include(${cmake_helpers_loc}/external/glib/generate_dbus.cmake)
+    generate_dbus(Telegram org.freedesktop.portal. XdpBackground ${third_party_loc}/xdg-desktop-portal/data/org.freedesktop.portal.Background.xml)
+
     if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION)
         target_link_libraries(Telegram
         PRIVATE
diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp
index c0cbed82e..c892885d0 100644
--- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp
+++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp
@@ -38,6 +38,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include <glibmm.h>
 #include <giomm.h>
 
+#include <xdgdbus/xdgdbus.hpp>
+#include <xdpbackground/xdpbackground.hpp>
+#include <xdprequest/xdprequest.hpp>
+
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/un.h>
@@ -48,12 +52,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include <iostream>
 
+namespace {
+
+using namespace gi::repository;
+namespace Gio = gi::repository::Gio;
 using namespace Platform;
 using Platform::internal::WaylandIntegration;
 
-namespace Platform {
-namespace {
-
 void PortalAutostart(bool enabled, Fn<void(bool)> done) {
 	if (cExeName().isEmpty()) {
 		if (done) {
@@ -62,127 +67,141 @@ void PortalAutostart(bool enabled, Fn<void(bool)> done) {
 		return;
 	}
 
-	const auto connection = [&] {
-		try {
-			return Gio::DBus::Connection::get_sync(
-				Gio::DBus::BusType::SESSION);
-		} catch (const std::exception &e) {
-			if (done) {
-				LOG(("Portal Autostart Error: %1").arg(e.what()));
+	XdpBackground::BackgroundProxy::new_for_bus(
+		Gio::BusType::SESSION_,
+		Gio::DBusProxyFlags::NONE_,
+		base::Platform::XDP::kService,
+		base::Platform::XDP::kObjectPath,
+		[=](GObject::Object, Gio::AsyncResult res) {
+			auto proxy = XdpBackground::BackgroundProxy::new_for_bus_finish(
+				res);
+
+			if (!proxy) {
+				if (done) {
+					LOG(("Portal Autostart Error: %1").arg(
+						proxy.error().what()));
+					done(false);
+				}
+				return;
 			}
-			return Glib::RefPtr<Gio::DBus::Connection>();
-		}
-	}();
 
-	if (!connection) {
-		if (done) {
-			done(false);
-		}
-		return;
-	}
+			auto interface = XdpBackground::Background(*proxy);
 
-	const auto handleToken = Glib::ustring("tdesktop")
-		+ std::to_string(base::RandomValue<uint>());
+			const auto handleToken = "tdesktop"
+				+ std::to_string(base::RandomValue<uint>());
 
-	std::vector<Glib::ustring> commandline;
-	commandline.push_back(cExeName().toStdString());
-	if (Core::Launcher::Instance().customWorkingDir()) {
-		commandline.push_back("-workdir");
-		commandline.push_back(cWorkingDir().toStdString());
-	}
-	commandline.push_back("-autostart");
+			auto uniqueName = std::string(
+				proxy->get_connection().get_unique_name());
+			uniqueName.erase(0, 1);
+			uniqueName.replace(uniqueName.find('.'), 1, 1, '_');
 
-	std::map<Glib::ustring, Glib::VariantBase> options;
-	options["handle_token"] = Glib::create_variant(handleToken);
-	options["reason"] = Glib::create_variant(
-		Glib::ustring(
-			tr::lng_settings_auto_start(tr::now).toStdString()));
-	options["autostart"] = Glib::create_variant(enabled);
-	options["commandline"] = Glib::create_variant(commandline);
-	options["dbus-activatable"] = Glib::create_variant(false);
+			const auto window = std::make_shared<QWidget>();
+			window->setAttribute(Qt::WA_DontShowOnScreen);
+			window->setWindowModality(Qt::ApplicationModal);
+			window->show();
 
-	auto uniqueName = connection->get_unique_name();
-	uniqueName.erase(0, 1);
-	uniqueName.replace(uniqueName.find('.'), 1, 1, '_');
+			XdpRequest::RequestProxy::new_(
+				proxy->get_connection(),
+				Gio::DBusProxyFlags::NONE_,
+				base::Platform::XDP::kService,
+				base::Platform::XDP::kObjectPath
+					+ std::string("/request/")
+					+ uniqueName
+					+ '/'
+					+ handleToken,
+				nullptr,
+				[=](GObject::Object, Gio::AsyncResult res) mutable {
+					auto requestProxy = XdpRequest::RequestProxy::new_finish(
+						res);
 
-	const auto requestPath = base::Platform::XDP::kObjectPath
-		+ Glib::ustring("/request/")
-		+ uniqueName
-		+ '/'
-		+ handleToken;
-
-	const auto window = std::make_shared<QWidget>();
-	window->setAttribute(Qt::WA_DontShowOnScreen);
-	window->setWindowModality(Qt::ApplicationModal);
-	window->show();
-
-	const auto signalId = std::make_shared<uint>();
-	*signalId = connection->signal_subscribe(
-		[=](
-			const Glib::RefPtr<Gio::DBus::Connection> &connection,
-			const Glib::ustring &sender_name,
-			const Glib::ustring &object_path,
-			const Glib::ustring &interface_name,
-			const Glib::ustring &signal_name,
-			const Glib::VariantContainerBase &parameters) {
-			Core::Sandbox::Instance().customEnterFromEventLoop([&] {
-				(void)window; // don't destroy until finish
-
-				try {
-					const auto response = parameters.get_child(
-						0
-					).get_dynamic<uint>();
-
-					if (response) {
+					if (!requestProxy) {
 						if (done) {
-							LOG(("Portal Autostart Error: Request denied"));
+							LOG(("Portal Autostart Error: %1").arg(
+								requestProxy.error().what()));
 							done(false);
 						}
-					} else if (done) {
-						done(enabled);
-					}
-				} catch (const std::exception &e) {
-					if (done) {
-						LOG(("Portal Autostart Error: %1").arg(e.what()));
-						done(false);
-					}
-				}
-
-				if (*signalId) {
-					connection->signal_unsubscribe(*signalId);
-				}
-			});
-		},
-		base::Platform::XDP::kService,
-		base::Platform::XDP::kRequestInterface,
-		"Response",
-		requestPath);
-
-	connection->call(
-		base::Platform::XDP::kObjectPath,
-		"org.freedesktop.portal.Background",
-		"RequestBackground",
-		Glib::create_variant(std::tuple{
-			base::Platform::XDP::ParentWindowID(),
-			options,
-		}),
-		[=](const Glib::RefPtr<Gio::AsyncResult> &result) {
-			Core::Sandbox::Instance().customEnterFromEventLoop([&] {
-				try {
-					connection->call_finish(result);
-				} catch (const std::exception &e) {
-					if (done) {
-						LOG(("Portal Autostart Error: %1").arg(e.what()));
-						done(false);
+						return;
 					}
 
-					if (*signalId) {
-						connection->signal_unsubscribe(*signalId);
+					auto request = XdpRequest::Request(*requestProxy);
+					const auto signalId = std::make_shared<ulong>();
+					*signalId = request.signal_response().connect([=](
+							XdpRequest::Request,
+							guint response,
+							GLib::Variant) mutable {
+						auto &sandbox = Core::Sandbox::Instance();
+						sandbox.customEnterFromEventLoop([&] {
+							(void)window; // don't destroy until finish
+
+							if (response) {
+								if (done) {
+									LOG(("Portal Autostart Error: "
+										"Request denied"));
+									done(false);
+								}
+							} else if (done) {
+								done(enabled);
+							}
+
+							request.disconnect(*signalId);
+						});
+					});
+
+
+					std::vector<std::string> commandline;
+					commandline.push_back(cExeName().toStdString());
+					if (Core::Launcher::Instance().customWorkingDir()) {
+						commandline.push_back("-workdir");
+						commandline.push_back(cWorkingDir().toStdString());
 					}
-				}
-			});
-		},
-		base::Platform::XDP::kService);
+					commandline.push_back("-autostart");
+
+					interface.call_request_background(
+						std::string(base::Platform::XDP::ParentWindowID()),
+						GLib::Variant::new_array({
+							GLib::Variant::new_dict_entry(
+								GLib::Variant::new_string("handle_token"),
+								GLib::Variant::new_variant(
+									GLib::Variant::new_string(handleToken))),
+							GLib::Variant::new_dict_entry(
+								GLib::Variant::new_string("reason"),
+								GLib::Variant::new_variant(
+									GLib::Variant::new_string(
+										tr::lng_settings_auto_start(tr::now)
+											.toStdString()))),
+							GLib::Variant::new_dict_entry(
+								GLib::Variant::new_string("autostart"),
+								GLib::Variant::new_variant(
+									GLib::Variant::new_boolean(enabled))),
+							GLib::Variant::new_dict_entry(
+								GLib::Variant::new_string("commandline"),
+								GLib::Variant::new_variant(
+									GLib::Variant::new_strv(commandline))),
+							GLib::Variant::new_dict_entry(
+								GLib::Variant::new_string("dbus-activatable"),
+								GLib::Variant::new_variant(
+									GLib::Variant::new_boolean(false))),
+						}),
+						[=](GObject::Object, Gio::AsyncResult res) mutable {
+							auto &sandbox = Core::Sandbox::Instance();
+							sandbox.customEnterFromEventLoop([&] {
+								const auto result =
+									interface.call_request_background_finish(
+										res);
+
+								if (!result) {
+									if (done) {
+										LOG(("Portal Autostart Error: %1")
+											.arg(result.error().what()));
+										done(false);
+									}
+
+									request.disconnect(*signalId);
+								}
+							});
+						});
+				});
+		});
 }
 
 bool GenerateDesktopFile(
@@ -218,77 +237,89 @@ bool GenerateDesktopFile(
 		return false;
 	}
 
-	try {
-		const auto target = Glib::KeyFile::create();
-		target->load_from_data(
-			sourceText,
-			Glib::KeyFile::Flags::KEEP_COMMENTS
-				| Glib::KeyFile::Flags::KEEP_TRANSLATIONS);
+	auto target = GLib::KeyFile::new_();
+	const auto loaded = target.load_from_data(
+		sourceText,
+		-1,
+		GLib::KeyFileFlags::KEEP_COMMENTS_
+			| GLib::KeyFileFlags::KEEP_TRANSLATIONS_);
+	
+	if (!loaded) {
+		if (!silent) {
+			LOG(("App Error: %1").arg(loaded.error().what()));
+		}
+		return false;
+	}
 
-		for (const auto &group : target->get_groups()) {
-			if (onlyMainGroup && group != "Desktop Entry") {
-				target->remove_group(group);
-				continue;
+	for (const auto &group : target.get_groups(nullptr)) {
+		if (onlyMainGroup && group != "Desktop Entry") {
+			const auto removed = target.remove_group(group);
+			if (!removed) {
+				if (!silent) {
+					LOG(("App Error: %1").arg(removed.error().what()));
+				}
+				return false;
 			}
+			continue;
+		}
 
-			if (target->has_key(group, "TryExec")) {
-				target->set_string(
+		if (target.has_key(group, "TryExec", nullptr)) {
+			target.set_string(
+				group,
+				"TryExec",
+				KShell::joinArgs({ executable }).replace(
+					'\\',
+					qstr("\\\\")).toStdString());
+		}
+
+		if (target.has_key(group, "Exec", nullptr)) {
+			if (group == "Desktop Entry" && !args.isEmpty()) {
+				QStringList exec;
+				exec.append(executable);
+				if (Core::Launcher::Instance().customWorkingDir()) {
+					exec.append(u"-workdir"_q);
+					exec.append(cWorkingDir());
+				}
+				exec.append(args);
+				target.set_string(
 					group,
-					"TryExec",
-					KShell::joinArgs({ executable }).replace(
+					"Exec",
+					KShell::joinArgs(exec).replace(
 						'\\',
 						qstr("\\\\")).toStdString());
-			}
+			} else {
+				auto exec = KShell::splitArgs(
+					QString::fromStdString(
+						target.get_string(group, "Exec", nullptr)
+					).replace(
+						qstr("\\\\"),
+						qstr("\\")));
 
-			if (target->has_key(group, "Exec")) {
-				if (group == "Desktop Entry" && !args.isEmpty()) {
-					QStringList exec;
-					exec.append(executable);
+				if (!exec.isEmpty()) {
+					exec[0] = executable;
 					if (Core::Launcher::Instance().customWorkingDir()) {
-						exec.append(u"-workdir"_q);
-						exec.append(cWorkingDir());
+						exec.insert(1, u"-workdir"_q);
+						exec.insert(2, cWorkingDir());
 					}
-					exec.append(args);
-					target->set_string(
+					target.set_string(
 						group,
 						"Exec",
 						KShell::joinArgs(exec).replace(
 							'\\',
 							qstr("\\\\")).toStdString());
-				} else {
-					auto exec = KShell::splitArgs(
-						QString::fromStdString(
-							target->get_string(group, "Exec")
-						).replace(
-							qstr("\\\\"),
-							qstr("\\")));
-
-					if (!exec.isEmpty()) {
-						exec[0] = executable;
-						if (Core::Launcher::Instance().customWorkingDir()) {
-							exec.insert(1, u"-workdir"_q);
-							exec.insert(2, cWorkingDir());
-						}
-						target->set_string(
-							group,
-							"Exec",
-							KShell::joinArgs(exec).replace(
-								'\\',
-								qstr("\\\\")).toStdString());
-					}
 				}
 			}
 		}
+	}
 
-		if (!args.isEmpty()
-				&& target->has_key("Desktop Entry", "DBusActivatable")) {
-			target->remove_key("Desktop Entry", "DBusActivatable");
-		}
+	if (!args.isEmpty()) {
+		target.remove_key("Desktop Entry", "DBusActivatable");
+	}
 
-		target->save_to_file(targetFile.toStdString());
-	} catch (const std::exception &e) {
+	const auto saved = target.save_to_file(targetFile.toStdString());
+	if (!saved) {
 		if (!silent) {
-			LOG(("App Error: %1").arg(e.what()));
+			LOG(("App Error: %1").arg(saved.error().what()));
 		}
 		return false;
 	}
@@ -357,10 +388,10 @@ bool GenerateServiceFile(bool silent = false) {
 	DEBUG_LOG(("App Info: placing D-Bus service file to %1").arg(targetPath));
 	if (!QDir(targetPath).exists()) QDir().mkpath(targetPath);
 
-	const auto target = Glib::KeyFile::create();
+	auto target = GLib::KeyFile::new_();
 	constexpr auto group = "D-BUS Service";
 
-	target->set_string(
+	target.set_string(
 		group,
 		"Name",
 		QGuiApplication::desktopFileName().toStdString());
@@ -371,21 +402,21 @@ bool GenerateServiceFile(bool silent = false) {
 		exec.append(u"-workdir"_q);
 		exec.append(cWorkingDir());
 	}
-	target->set_string(
+	target.set_string(
 		group,
 		"Exec",
 		KShell::joinArgs(exec).toStdString());
 
-	try {
-		target->save_to_file(targetFile.toStdString());
-	} catch (const std::exception &e) {
+	const auto saved = target.save_to_file(targetFile.toStdString());
+	if (!saved) {
 		if (!silent) {
-			LOG(("App Error: %1").arg(e.what()));
+			LOG(("App Error: %1").arg(saved.error().what()));
 		}
 		return false;
 	}
 
-	if (!Core::UpdaterDisabled() && !Core::Launcher::Instance().customWorkingDir()) {
+	if (!Core::UpdaterDisabled()
+			&& !Core::Launcher::Instance().customWorkingDir()) {
 		DEBUG_LOG(("App Info: removing old D-Bus service files"));
 
 		char md5Hash[33] = { 0 };
@@ -397,19 +428,21 @@ bool GenerateServiceFile(bool silent = false) {
 			md5Hash));
 	}
 
-	try {
-		Gio::DBus::Connection::get_sync(
-			Gio::DBus::BusType::SESSION
-		)->call(
-			base::Platform::DBus::kObjectPath,
-			base::Platform::DBus::kInterface,
-			"ReloadConfig",
-			{},
-			{},
-			base::Platform::DBus::kService
-		);
-	} catch (...) {
-	}
+	XdgDBus::DBusProxy::new_for_bus(
+		Gio::BusType::SESSION_,
+		Gio::DBusProxyFlags::NONE_,
+		base::Platform::DBus::kService,
+		base::Platform::DBus::kObjectPath,
+		[=](GObject::Object, Gio::AsyncResult res) {
+			auto interface = XdgDBus::DBus(
+				XdgDBus::DBusProxy::new_for_bus_finish(res, nullptr));
+
+			if (!interface) {
+				return;
+			}
+
+			interface.call_reload_config(nullptr);
+		});
 
 	return true;
 }
@@ -447,6 +480,8 @@ void InstallLauncher() {
 
 } // namespace
 
+namespace Platform {
+
 void SetApplicationIcon(const QIcon &icon) {
 	QApplication::setWindowIcon(icon);
 }
@@ -648,11 +683,11 @@ void start() {
 	qputenv("PULSE_PROP_application.name", AppName.utf8());
 	qputenv("PULSE_PROP_application.icon_name", base::IconName().toLatin1());
 
-	Glib::set_prgname(cExeName().toStdString());
-	Glib::set_application_name(AppName.data());
+	GLib::set_prgname(cExeName().toStdString());
+	GLib::set_application_name(AppName.data());
 
 	Glib::init();
-	Gio::init();
+	::Gio::init();
 
 	Webview::WebKitGTK::SetSocketPath(u"%1/%2-%3-webview-%4"_q.arg(
 		QDir::tempPath(),

From 296e8c1ab1590f49f5b6c70daa5d2639e712318e Mon Sep 17 00:00:00 2001
From: Ilya Fedin <fedin-ilja2010@ya.ru>
Date: Fri, 8 Mar 2024 15:03:47 +0400
Subject: [PATCH 091/108] Use ExecutablePathForShortcuts in PortalAutostart

---
 Telegram/SourceFiles/platform/linux/specific_linux.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp
index c892885d0..4122097bc 100644
--- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp
+++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp
@@ -60,7 +60,8 @@ using namespace Platform;
 using Platform::internal::WaylandIntegration;
 
 void PortalAutostart(bool enabled, Fn<void(bool)> done) {
-	if (cExeName().isEmpty()) {
+	const auto executable = ExecutablePathForShortcuts();
+	if (executable.isEmpty()) {
 		if (done) {
 			done(false);
 		}
@@ -149,7 +150,7 @@ void PortalAutostart(bool enabled, Fn<void(bool)> done) {
 
 
 					std::vector<std::string> commandline;
-					commandline.push_back(cExeName().toStdString());
+					commandline.push_back(executable.toStdString());
 					if (Core::Launcher::Instance().customWorkingDir()) {
 						commandline.push_back("-workdir");
 						commandline.push_back(cWorkingDir().toStdString());

From 3d5092f7ad58311fc31424f1aadae0be6134f8c1 Mon Sep 17 00:00:00 2001
From: Ilya Fedin <fedin-ilja2010@ya.ru>
Date: Fri, 8 Mar 2024 15:09:21 +0400
Subject: [PATCH 092/108] Update cmake_helpers

---
 cmake | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cmake b/cmake
index b699c232d..f1628c260 160000
--- a/cmake
+++ b/cmake
@@ -1 +1 @@
-Subproject commit b699c232d57d50070a7b1b861809e206624f48d4
+Subproject commit f1628c260b17c55996740fc00b00a7c227b61de2

From 990ae11f6291aa0a9d71007c0cee5e28b68c0c0a Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 8 Mar 2024 15:49:30 +0400
Subject: [PATCH 093/108] Version 4.15.1: Fix build with GCC.

---
 .../boxes/peers/replace_boost_box.cpp         |  7 +----
 .../data/business/data_shortcut_messages.cpp  |  1 -
 .../view/media/history_view_contact.cpp       |  3 --
 .../SourceFiles/info/info_content_widget.cpp  |  5 ----
 .../SourceFiles/info/info_layer_widget.cpp    | 30 -------------------
 .../settings/business/settings_chatbots.cpp   |  1 -
 .../business/settings_quick_replies.cpp       |  1 -
 .../business/settings_recipients_helper.cpp   |  4 +--
 .../business/settings_shortcut_messages.cpp   |  8 -----
 .../business/settings_working_hours.cpp       |  2 --
 10 files changed, 3 insertions(+), 59 deletions(-)

diff --git a/Telegram/SourceFiles/boxes/peers/replace_boost_box.cpp b/Telegram/SourceFiles/boxes/peers/replace_boost_box.cpp
index 36f18ab23..f57573d99 100644
--- a/Telegram/SourceFiles/boxes/peers/replace_boost_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/replace_boost_box.cpp
@@ -425,14 +425,9 @@ Ui::BoostCounters ParseBoostCounters(
 }
 
 Ui::BoostFeatures LookupBoostFeatures(not_null<ChannelData*> channel) {
-	const auto group = channel->isMegagroup();
-	const auto appConfig = &channel->session().account().appConfig();
-	const auto get = [&](const QString &key, int fallback, bool ok = true) {
-		return ok ? appConfig->get<int>(key, fallback) : 0;
-	};
-
 	auto nameColorsByLevel = base::flat_map<int, int>();
 	auto linkStylesByLevel = base::flat_map<int, int>();
+	const auto group = channel->isMegagroup();
 	const auto peerColors = &channel->session().api().peerColors();
 	const auto &list = group
 		? peerColors->requiredLevelsGroup()
diff --git a/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp b/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
index df2f5f880..75b27a25b 100644
--- a/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
+++ b/Telegram/SourceFiles/data/business/data_shortcut_messages.cpp
@@ -262,7 +262,6 @@ void ShortcutMessages::scheduleShortcutsReload() {
 }
 
 void ShortcutMessages::apply(const MTPDupdateNewQuickReply &update) {
-	const auto selfId = _session->userPeerId();
 	const auto &reply = update.vquick_reply();
 	auto foundId = BusinessShortcutId();
 	const auto shortcut = parseShortcut(reply);
diff --git a/Telegram/SourceFiles/history/view/media/history_view_contact.cpp b/Telegram/SourceFiles/history/view/media/history_view_contact.cpp
index 1a3e35716..1e6654abd 100644
--- a/Telegram/SourceFiles/history/view/media/history_view_contact.cpp
+++ b/Telegram/SourceFiles/history/view/media/history_view_contact.cpp
@@ -244,10 +244,8 @@ void Contact::draw(Painter &p, const PaintContext &context) const {
 	}
 
 	const auto st = context.st;
-	const auto sti = context.imageStyle();
 	const auto stm = context.messageStyle();
 
-	const auto bubble = st::msgPadding;
 	const auto full = Rect(currentSize());
 	const auto outer = full - inBubblePadding();
 	const auto inner = outer - innerMargin();
@@ -438,7 +436,6 @@ TextState Contact::textState(QPoint point, StateRequest request) const {
 
 	if (_buttons.size() > 1) {
 		const auto end = rect::bottom(inner) + _st.padding.bottom();
-		const auto line = st::historyPageButtonLine;
 		const auto bWidth = inner.width() / float64(_buttons.size());
 		const auto bHeight = rect::bottom(outer) - end;
 		for (auto i = 0; i < _buttons.size(); i++) {
diff --git a/Telegram/SourceFiles/info/info_content_widget.cpp b/Telegram/SourceFiles/info/info_content_widget.cpp
index 440c125b2..b2908c3ac 100644
--- a/Telegram/SourceFiles/info/info_content_widget.cpp
+++ b/Telegram/SourceFiles/info/info_content_widget.cpp
@@ -167,12 +167,7 @@ Ui::RpWidget *ContentWidget::doSetInnerWidget(
 		const auto bottom = top + height;
 		_innerDesiredHeight = desired;
 		_innerWrap->setVisibleTopBottom(top, bottom);
-		LOG(("TOP: %1, HEIGHT: %2, DESIRED: %3, TILL: %4").arg(top).arg(height).arg(desired).arg(std::max(desired - bottom, 0)));
 		_scrollTillBottomChanges.fire_copy(std::max(desired - bottom, 0));
-		//const auto bottom = _scroll->scrollTop() + _scroll->height();
-		//_innerDesiredHeight = desired;
-		//_innerWrap->setVisibleTopBottom(_scroll->scrollTop(), bottom);
-		//_scrollTillBottomChanges.fire_copy(std::max(desired - bottom, 0));
 	}, _innerWrap->lifetime());
 
 	return _innerWrap->entity();
diff --git a/Telegram/SourceFiles/info/info_layer_widget.cpp b/Telegram/SourceFiles/info/info_layer_widget.cpp
index 50eca4456..3404cf932 100644
--- a/Telegram/SourceFiles/info/info_layer_widget.cpp
+++ b/Telegram/SourceFiles/info/info_layer_widget.cpp
@@ -138,7 +138,6 @@ void LayerWidget::setContentHeight(int height) {
 	if (_contentWrapHeight == height) {
 		return;
 	}
-	LOG(("CONTENT WRAP HEIGHT: %1 -> %2").arg(_contentWrapHeight).arg(height));
 	_contentWrapHeight = height;
 	if (_inResize) {
 		_pendingResize = true;
@@ -261,35 +260,6 @@ int LayerWidget::resizeGetHeight(int newWidth) {
 	auto attempts = 0;
 	while (true) {
 		_inResize = true;
-		{
-			const auto &parentSize = parentWidget()->size();
-			const auto windowWidth = parentSize.width();
-			const auto windowHeight = parentSize.height();
-			const auto newLeft = (windowWidth - newWidth) / 2;
-			const auto newTop = std::clamp(
-				windowHeight / 24,
-				st::infoLayerTopMinimal,
-				st::infoLayerTopMaximal);
-			const auto newBottom = newTop;
-
-			const auto bottomRadius = st::boxRadius;
-			const auto maxVisibleHeight = windowHeight - newTop;
-			// Top rounding is included in _contentWrapHeight.
-			auto desiredHeight = _contentWrapHeight + bottomRadius;
-			accumulate_min(desiredHeight, maxVisibleHeight - newBottom);
-
-			// First resize content to new width and get the new desired height.
-			const auto contentLeft = 0;
-			const auto contentTop = 0;
-			const auto contentBottom = bottomRadius;
-			const auto contentWidth = newWidth;
-			auto contentHeight = desiredHeight - contentTop - contentBottom;
-			LOG(("ATTEMPT %1: WIDTH %2, WRAP HEIGHT %3, SCROLL TILL BOTTOM: %4"
-				).arg(attempts + 1
-				).arg(newWidth
-				).arg(_contentWrapHeight
-				).arg(_contentWrap->scrollTillBottom(contentHeight)));
-		}
 		const auto newGeometry = countGeometry(newWidth);
 		_inResize = false;
 		if (!_pendingResize) {
diff --git a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
index 78d446200..656d49958 100644
--- a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
@@ -479,7 +479,6 @@ void Chatbots::setupContent(
 
 void Chatbots::save() {
 	const auto show = controller()->uiShow();
-	const auto session = &controller()->session();
 	const auto fail = [=](QString error) {
 		if (error == u"BUSINESS_RECIPIENTS_EMPTY"_q) {
 			show->showToast(tr::lng_greeting_recipients_empty(tr::now));
diff --git a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
index 97b0c9a61..d451feb1b 100644
--- a/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_quick_replies.cpp
@@ -130,7 +130,6 @@ void QuickReplies::setupContent(
 		auto old = inner->count();
 
 		const auto &shortcuts = messages->shortcuts();
-		auto i = 0;
 		for (const auto &[_, shortcut]
 			: shortcuts.list | ranges::views::reverse) {
 			if (!shortcut.count) {
diff --git a/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp b/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
index 7f7f2b715..160b1d83b 100644
--- a/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_recipients_helper.cpp
@@ -171,7 +171,7 @@ void AddBusinessRecipientsSelector(
 	const auto all = current.allButExcluded || current.included.empty();
 	const auto group = std::make_shared<Ui::RadiobuttonGroup>(
 		all ? kAllExcept : kSelectedOnly);
-	const auto everyone = container->add(
+	container->add(
 		object_ptr<Ui::Radiobutton>(
 			container,
 			group,
@@ -179,7 +179,7 @@ void AddBusinessRecipientsSelector(
 			tr::lng_chatbots_all_except(tr::now),
 			st::settingsChatbotsAccess),
 		st::settingsChatbotsAccessMargins);
-	const auto selected = container->add(
+	container->add(
 		object_ptr<Ui::Radiobutton>(
 			container,
 			group,
diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
index 3b1143126..e1fcec944 100644
--- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp
@@ -181,7 +181,6 @@ private:
 	void processScroll();
 	void updateInnerVisibleArea();
 
-	void pushReplyReturn(not_null<HistoryItem*> item);
 	void checkReplyReturns();
 	void confirmDeleteSelected();
 	void clearSelected();
@@ -565,7 +564,6 @@ bool ShortcutMessages::paintOuter(
 		not_null<QWidget*> outer,
 		int maxVisibleHeight,
 		QRect clip) {
-	const auto window = outer->window()->height();
 	Window::SectionWidget::PaintBackground(
 		_theme.get(),
 		outer,
@@ -1092,12 +1090,6 @@ bool ShortcutMessages::cornerButtonsHas(CornerButtonType type) {
 	return (type == CornerButtonType::Down);
 }
 
-void ShortcutMessages::pushReplyReturn(not_null<HistoryItem*> item) {
-	if (item->shortcutId() == _shortcutId.current()) {
-		_cornerButtons.pushReplyReturn(item);
-	}
-}
-
 void ShortcutMessages::checkReplyReturns() {
 	const auto currentTop = _scroll->scrollTop();
 	const auto shortcutId = _shortcutId.current();
diff --git a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
index 525d069a4..fe80e5c73 100644
--- a/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
+++ b/Telegram/SourceFiles/settings/business/settings_working_hours.cpp
@@ -60,7 +60,6 @@ private:
 	const auto abs = std::abs(data.utcOffset);
 	const auto hours = abs / 3600;
 	const auto minutes = (abs % 3600) / 60;
-	const auto seconds = abs % 60;
 	const auto sign = (data.utcOffset < 0) ? '-' : '+';
 	const auto prefix = u"(UTC"_q
 		+ sign
@@ -366,7 +365,6 @@ void EditDayBox(
 			const auto from = std::max(
 				std::min(last + 30 * 60, kDay - 30 * 60),
 				last + 60);
-			const auto till = std::min(from + 4 * 3600, kDay + 30 * 60);
 			now.list.push_back({ from, from + 4 * 3600 });
 		}
 		state->data = std::move(now);

From d3b1abb61ed14f288de2491f3504fae4b973d873 Mon Sep 17 00:00:00 2001
From: Kolya <142352140+agl-1984@users.noreply.github.com>
Date: Fri, 8 Mar 2024 13:13:44 -0800
Subject: [PATCH 094/108] fix windows build

---
 Telegram/build/prepare/prepare.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py
index c3a566796..cf604e292 100644
--- a/Telegram/build/prepare/prepare.py
+++ b/Telegram/build/prepare/prepare.py
@@ -205,7 +205,7 @@ def removeDir(folder):
 def setVar(key, multilineValue):
     singlelineValue = ' '.join(multilineValue.replace('\n', '').split());
     if win:
-        return 'SET ' + key + '="' + singlelineValue + '"';
+        return 'SET "' + key + '=' + singlelineValue + '"';
     return key + '="' + singlelineValue + '"';
 
 def filterByPlatform(commands):

From 506b8fd4f13af996b7be9b97bb6da88e534d6654 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Sat, 9 Mar 2024 10:56:58 +0400
Subject: [PATCH 095/108] Fix saving of empty working intervals.

---
 Telegram/SourceFiles/data/business/data_business_common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Telegram/SourceFiles/data/business/data_business_common.h b/Telegram/SourceFiles/data/business/data_business_common.h
index 151d534e1..af3429421 100644
--- a/Telegram/SourceFiles/data/business/data_business_common.h
+++ b/Telegram/SourceFiles/data/business/data_business_common.h
@@ -139,7 +139,7 @@ struct WorkingHours {
 	}
 
 	explicit operator bool() const {
-		return !timezoneId.isEmpty();
+		return !timezoneId.isEmpty() && !intervals.list.empty();
 	}
 
 	friend inline bool operator==(

From 626b3395ab48c279001fc9688d83563f4289c9fc Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Sat, 9 Mar 2024 10:57:17 +0400
Subject: [PATCH 096/108] Show 00:00-23:59 as "open 24 hours".

---
 Telegram/SourceFiles/data/business/data_business_common.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/Telegram/SourceFiles/data/business/data_business_common.cpp b/Telegram/SourceFiles/data/business/data_business_common.cpp
index 7da2970db..06cb17e91 100644
--- a/Telegram/SourceFiles/data/business/data_business_common.cpp
+++ b/Telegram/SourceFiles/data/business/data_business_common.cpp
@@ -231,8 +231,9 @@ WorkingIntervals ExtractDayIntervals(
 }
 
 bool IsFullOpen(const WorkingIntervals &extractedDay) {
-	return extractedDay
-		&& (extractedDay.list.front() == WorkingInterval{ 0, kDay });
+	return extractedDay // 00:00-23:59 or 00:00-00:00 (next day)
+		&& (extractedDay.list.front() == WorkingInterval{ 0, kDay - 60 }
+			|| extractedDay.list.front() == WorkingInterval{ 0, kDay });
 }
 
 WorkingIntervals RemoveDayIntervals(

From 77dcbaf00cad934eaea53357d290f1485d97a1a1 Mon Sep 17 00:00:00 2001
From: Kolya <142352140+agl-1984@users.noreply.github.com>
Date: Sun, 10 Mar 2024 01:51:42 +0000
Subject: [PATCH 097/108] don't use brotli (built by other dependencies)

---
 Telegram/build/prepare/prepare.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py
index cf604e292..727e8521b 100644
--- a/Telegram/build/prepare/prepare.py
+++ b/Telegram/build/prepare/prepare.py
@@ -1512,6 +1512,7 @@ mac:
         -system-webp \
         -I "$USED_PREFIX/include" \
         -no-feature-futimens \
+        -no-feature-brotli \
         -nomake examples \
         -nomake tests \
         -platform macx-clang -- \

From fc0cfbf003269679963e2babbdf94a63d4ae7cc0 Mon Sep 17 00:00:00 2001
From: prawwtocol <142227259+prawwtocol@users.noreply.github.com>
Date: Sun, 10 Mar 2024 11:58:26 +0300
Subject: [PATCH 098/108] Update mac build instructions with up-to-date
 homebrew link

---
 docs/building-mac.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/building-mac.md b/docs/building-mac.md
index 11435a8dd..605a5b519 100644
--- a/docs/building-mac.md
+++ b/docs/building-mac.md
@@ -12,7 +12,7 @@ You will require **api_id** and **api_hash** to access the Telegram API servers.
 
 Go to ***BuildPath*** and run
 
-    ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
+    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
     brew install git automake cmake wget pkg-config gnu-tar ninja nasm meson
 
     sudo xcode-select -s /Applications/Xcode.app/Contents/Developer

From 12356b16177c002d2a52de443b3055cb366de2a8 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 12 Mar 2024 13:00:06 +0400
Subject: [PATCH 099/108] Fix possible crash in WebView2 destruction.

---
 Telegram/lib_webview | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Telegram/lib_webview b/Telegram/lib_webview
index 27af88195..fbf9dd547 160000
--- a/Telegram/lib_webview
+++ b/Telegram/lib_webview
@@ -1 +1 @@
-Subproject commit 27af88195bca687e9d2a52b4fcd4e83ef5476be9
+Subproject commit fbf9dd54787df90c98cf230cb53323527e0b0639

From 68bb0a17447d1dad6b70948970cb118381f9a5af Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 12 Mar 2024 13:12:49 +0400
Subject: [PATCH 100/108] Fix recent actions date marks.

Regression was introduced in 7f3ebde252.
---
 .../SourceFiles/history/admin_log/history_admin_log_item.cpp     | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp
index c54093dc3..14f9718fc 100644
--- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp
+++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp
@@ -829,6 +829,7 @@ void GenerateItems(
 			.id = history->nextNonHistoryEntryId(),
 			.flags = MessageFlag::HasFromId | MessageFlag::AdminLogEntry,
 			.from = from->id,
+			.date = date,
 		}, std::move(text), MTP_messageMediaEmpty());
 	};
 

From b4993453c01381aac3dd9da41d7bf1df0abab2cb Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 12 Mar 2024 13:16:50 +0400
Subject: [PATCH 101/108] Update submodules.

---
 Telegram/lib_base       | 2 +-
 Telegram/lib_spellcheck | 2 +-
 cmake                   | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Telegram/lib_base b/Telegram/lib_base
index cee9211bd..5b9556fdd 160000
--- a/Telegram/lib_base
+++ b/Telegram/lib_base
@@ -1 +1 @@
-Subproject commit cee9211bd58e054f24ad5e7f122037f71a44b237
+Subproject commit 5b9556fddb9a67e514d0bed2c123e18cbe1663b7
diff --git a/Telegram/lib_spellcheck b/Telegram/lib_spellcheck
index 96543c171..9b52030bf 160000
--- a/Telegram/lib_spellcheck
+++ b/Telegram/lib_spellcheck
@@ -1 +1 @@
-Subproject commit 96543c1716d3790ef12bdec6b113958427710441
+Subproject commit 9b52030bfcd7e90e3e550231a3783ad1982fda78
diff --git a/cmake b/cmake
index f1628c260..5a61112d6 160000
--- a/cmake
+++ b/cmake
@@ -1 +1 @@
-Subproject commit f1628c260b17c55996740fc00b00a7c227b61de2
+Subproject commit 5a61112d6d025b56573ad48bcc1331ac65c4a927

From 1647991f6a55c8a3ff834ef3f6a621e0ec56ab4b Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Sat, 9 Mar 2024 11:06:43 +0400
Subject: [PATCH 102/108] Fix autologin token account selection.

---
 Telegram/SourceFiles/core/ui_integration.cpp | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/Telegram/SourceFiles/core/ui_integration.cpp b/Telegram/SourceFiles/core/ui_integration.cpp
index ba2593e7b..e6b62203d 100644
--- a/Telegram/SourceFiles/core/ui_integration.cpp
+++ b/Telegram/SourceFiles/core/ui_integration.cpp
@@ -50,8 +50,13 @@ const auto kBadPrefix = u"http://"_q;
 [[nodiscard]] QString UrlWithAutoLoginToken(
 		const QString &url,
 		QUrl parsed,
-		const QString &domain) {
-	const auto &active = Core::App().activeAccount();
+		const QString &domain,
+		QVariant context) {
+	const auto my = context.value<ClickHandlerContext>();
+	const auto window = my.sessionWindow.get();
+	const auto &active = window
+		? window->session().account()
+		: Core::App().activeAccount();
 	const auto token = active.mtp().configValues().autologinToken;
 	const auto domains = active.appConfig().get<std::vector<QString>>(
 		"autologin_domains",
@@ -238,7 +243,8 @@ bool UiIntegration::handleUrlClick(
 	const auto domain = DomainForAutoLogin(parsed);
 	const auto skip = context.value<ClickHandlerContext>().skipBotAutoLogin;
 	if (skip || !BotAutoLogin(url, domain, context)) {
-		File::OpenUrl(UrlWithAutoLoginToken(url, std::move(parsed), domain));
+		File::OpenUrl(
+			UrlWithAutoLoginToken(url, std::move(parsed), domain, context));
 	}
 	return true;
 }

From cf6d13acc2e797700cf79170b5a9d2b34d69d7ab Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 12 Mar 2024 17:08:27 +0400
Subject: [PATCH 103/108] Add fast Ctrl/Shift scroll to ElasticScroll.

---
 Telegram/lib_ui | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Telegram/lib_ui b/Telegram/lib_ui
index edfcac751..fb1716c91 160000
--- a/Telegram/lib_ui
+++ b/Telegram/lib_ui
@@ -1 +1 @@
-Subproject commit edfcac751dbdd93fcabac4ebf0ff731ceab8af0f
+Subproject commit fb1716c91f80baf482a51b86a9a92cb1df2819b0

From c6f49486ee7d983cab5096b1ef16b6469f53e114 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 12 Mar 2024 18:31:48 +0400
Subject: [PATCH 104/108] Use regular good-green color in unmute.

---
 Telegram/SourceFiles/menu/menu_mute.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Telegram/SourceFiles/menu/menu_mute.cpp b/Telegram/SourceFiles/menu/menu_mute.cpp
index dd58f6fc0..661ef0672 100644
--- a/Telegram/SourceFiles/menu/menu_mute.cpp
+++ b/Telegram/SourceFiles/menu/menu_mute.cpp
@@ -111,6 +111,7 @@ MuteItem::MuteItem(
 			isMuted ? 1. : 0.,
 			st::defaultPopupMenu.showDuration);
 	}, lifetime());
+	_animation.stop();
 
 	setClickedCallback([=] {
 		descriptor.updateMutePeriod(_isMuted ? 0 : kMuteForeverValue);
@@ -123,7 +124,7 @@ void MuteItem::paintEvent(QPaintEvent *e) {
 	const auto progress = _animation.value(_isMuted ? 1. : 0.);
 	const auto color = anim::color(
 		st::menuIconAttentionColor,
-		st::settingsIconBg2,
+		st::boxTextFgGood,
 		progress);
 	p.setPen(color);
 

From 8c5db25476b08f5b1e2562ac8eb99895880ee801 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 12 Mar 2024 23:19:59 +0400
Subject: [PATCH 105/108] Fix a crash in main settings destructor.

Fixes #27544.
---
 .../info/profile/info_profile_emoji_status_panel.cpp  |  4 ++++
 .../info/profile/info_profile_emoji_status_panel.h    |  1 +
 Telegram/SourceFiles/settings/settings_main.cpp       | 11 ++++++++++-
 3 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.cpp b/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.cpp
index c7f26b4ef..c4cce455f 100644
--- a/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.cpp
+++ b/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.cpp
@@ -159,6 +159,10 @@ void EmojiStatusPanel::show(Descriptor &&descriptor) {
 	_panel->toggleAnimated();
 }
 
+bool EmojiStatusPanel::hasFocus() const {
+	return _panel && Ui::InFocusChain(_panel.get());
+}
+
 void EmojiStatusPanel::repaint() {
 	_panel->selector()->update();
 }
diff --git a/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.h b/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.h
index a373c904c..9777cfcfa 100644
--- a/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.h
+++ b/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.h
@@ -46,6 +46,7 @@ public:
 		not_null<Window::SessionController*> controller,
 		not_null<QWidget*> button,
 		Data::CustomEmojiSizeTag animationSizeTag = {});
+	[[nodiscard]] bool hasFocus() const;
 
 	struct Descriptor {
 		not_null<Window::SessionController*> controller;
diff --git a/Telegram/SourceFiles/settings/settings_main.cpp b/Telegram/SourceFiles/settings/settings_main.cpp
index c746a881a..33b175520 100644
--- a/Telegram/SourceFiles/settings/settings_main.cpp
+++ b/Telegram/SourceFiles/settings/settings_main.cpp
@@ -174,7 +174,16 @@ Cover::Cover(
 	}, _name->lifetime());
 }
 
-Cover::~Cover() = default;
+Cover::~Cover() {
+	if (_emojiStatusPanel.hasFocus()) {
+		// Panel will try to return focus to the layer widget, the problem is
+		// we are destroying the layer widget probably right now and focusing
+		// it will lead to a crash, because it destroys its children (how we
+		// got here) after it clears focus out of itself. So if you return
+		// the focus inside a child destructor, it won't be cleared at all.
+		window()->setFocus();
+	}
+}
 
 void Cover::setupChildGeometry() {
 	using namespace rpl::mappers;

From 5573bbc77609bac20ade4f57ac86a66186f822b8 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 12 Mar 2024 23:30:49 +0400
Subject: [PATCH 106/108] Version 4.15.2.

- Telegram Business: Greeting Message.
- Telegram Business: Away Message.
- Telegram Business: Quick Replies.
- Telegram Business: Working Hours.
- Close the ongoing call window without hanging up the call.
- Fast scroll through chats list with Ctrl or Shift pressed.
- Several bugfixes.
---
 Telegram/Resources/uwp/AppX/AppxManifest.xml |  2 +-
 Telegram/Resources/winrc/Telegram.rc         |  8 ++++----
 Telegram/Resources/winrc/Updater.rc          |  8 ++++----
 Telegram/SourceFiles/core/version.h          |  4 ++--
 Telegram/build/version                       |  8 ++++----
 changelog.txt                                | 10 ++++++++++
 6 files changed, 25 insertions(+), 15 deletions(-)

diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml
index dee90b06c..b41618532 100644
--- a/Telegram/Resources/uwp/AppX/AppxManifest.xml
+++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml
@@ -10,7 +10,7 @@
   <Identity Name="TelegramMessengerLLP.TelegramDesktop"
     ProcessorArchitecture="ARCHITECTURE"
     Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
-    Version="4.15.1.0" />
+    Version="4.15.2.0" />
   <Properties>
     <DisplayName>Telegram Desktop</DisplayName>
     <PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc
index b8aba7a6f..035ae8fde 100644
--- a/Telegram/Resources/winrc/Telegram.rc
+++ b/Telegram/Resources/winrc/Telegram.rc
@@ -44,8 +44,8 @@ IDI_ICON1               ICON                    "..\\art\\icon256.ico"
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,15,1,0
- PRODUCTVERSION 4,15,1,0
+ FILEVERSION 4,15,2,0
+ PRODUCTVERSION 4,15,2,0
  FILEFLAGSMASK 0x3fL
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -62,10 +62,10 @@ BEGIN
         BEGIN
             VALUE "CompanyName", "Telegram FZ-LLC"
             VALUE "FileDescription", "Telegram Desktop"
-            VALUE "FileVersion", "4.15.1.0"
+            VALUE "FileVersion", "4.15.2.0"
             VALUE "LegalCopyright", "Copyright (C) 2014-2024"
             VALUE "ProductName", "Telegram Desktop"
-            VALUE "ProductVersion", "4.15.1.0"
+            VALUE "ProductVersion", "4.15.2.0"
         END
     END
     BLOCK "VarFileInfo"
diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc
index 93f0a6769..97cf094de 100644
--- a/Telegram/Resources/winrc/Updater.rc
+++ b/Telegram/Resources/winrc/Updater.rc
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,15,1,0
- PRODUCTVERSION 4,15,1,0
+ FILEVERSION 4,15,2,0
+ PRODUCTVERSION 4,15,2,0
  FILEFLAGSMASK 0x3fL
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -53,10 +53,10 @@ BEGIN
         BEGIN
             VALUE "CompanyName", "Telegram FZ-LLC"
             VALUE "FileDescription", "Telegram Desktop Updater"
-            VALUE "FileVersion", "4.15.1.0"
+            VALUE "FileVersion", "4.15.2.0"
             VALUE "LegalCopyright", "Copyright (C) 2014-2024"
             VALUE "ProductName", "Telegram Desktop"
-            VALUE "ProductVersion", "4.15.1.0"
+            VALUE "ProductVersion", "4.15.2.0"
         END
     END
     BLOCK "VarFileInfo"
diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h
index 9bd491610..34fc3f789 100644
--- a/Telegram/SourceFiles/core/version.h
+++ b/Telegram/SourceFiles/core/version.h
@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
 constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
 constexpr auto AppName = "Telegram Desktop"_cs;
 constexpr auto AppFile = "Telegram"_cs;
-constexpr auto AppVersion = 4015001;
-constexpr auto AppVersionStr = "4.15.1";
+constexpr auto AppVersion = 4015002;
+constexpr auto AppVersionStr = "4.15.2";
 constexpr auto AppBetaVersion = false;
 constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
diff --git a/Telegram/build/version b/Telegram/build/version
index 60c27dece..e540466fc 100644
--- a/Telegram/build/version
+++ b/Telegram/build/version
@@ -1,7 +1,7 @@
-AppVersion         4015001
+AppVersion         4015002
 AppVersionStrMajor 4.15
-AppVersionStrSmall 4.15.1
-AppVersionStr      4.15.1
+AppVersionStrSmall 4.15.2
+AppVersionStr      4.15.2
 BetaChannel        0
 AlphaVersion       0
-AppVersionOriginal 4.15.1
+AppVersionOriginal 4.15.2
diff --git a/changelog.txt b/changelog.txt
index 1d80bf48b..f1b710187 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,13 @@
+4.15.2 (12.03.24)
+
+- Telegram Business: Greeting Message.
+- Telegram Business: Away Message.
+- Telegram Business: Quick Replies.
+- Telegram Business: Working Hours.
+- Close the ongoing call window without hanging up the call.
+- Fast scroll through chats list with Ctrl or Shift pressed.
+- Several bugfixes.
+
 4.15.1 (08.03.24)
 
 - Telegram Business features.

From f13971dce177c5049b95e0977a74509e280f8754 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Wed, 13 Mar 2024 09:20:12 +0400
Subject: [PATCH 107/108] Use line-tables-only debug information format on
 macOS.

Otherwise linking fails on x86_64 in Release mode.
---
 Telegram/CMakeLists.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index 9285d5ad2..723c837a2 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -1754,6 +1754,7 @@ set_target_properties(Telegram PROPERTIES
     XCODE_ATTRIBUTE_ALWAYS_SEARCH_USER_PATHS NO
     XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY libc++
     XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS --deep
+    XCODE_ATTRIBUTE_CLANG_DEBUG_INFORMATION_LEVEL $<IF:$<CONFIG:Debug>,default,line-tables-only>
 )
 set(entitlement_sources
     "${CMAKE_CURRENT_SOURCE_DIR}/Telegram/Telegram.entitlements"

From bf1b3dc8f6a8b638e4735b07c08bcef30ad13819 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Wed, 13 Mar 2024 09:21:35 +0400
Subject: [PATCH 108/108] Version 4.15.2: Update fcitx-qt5.

I hope this fixes #27573.
---
 Telegram/ThirdParty/fcitx5-qt | 2 +-
 Telegram/lib_ui               | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Telegram/ThirdParty/fcitx5-qt b/Telegram/ThirdParty/fcitx5-qt
index 413747e76..cc77e32c0 160000
--- a/Telegram/ThirdParty/fcitx5-qt
+++ b/Telegram/ThirdParty/fcitx5-qt
@@ -1 +1 @@
-Subproject commit 413747e761b13bacc5ebd01e20810c64c2f3b6dc
+Subproject commit cc77e32c0ab675a663a7c019b3bb8cfcc60c5ec3
diff --git a/Telegram/lib_ui b/Telegram/lib_ui
index fb1716c91..6bce49302 160000
--- a/Telegram/lib_ui
+++ b/Telegram/lib_ui
@@ -1 +1 @@
-Subproject commit fb1716c91f80baf482a51b86a9a92cb1df2819b0
+Subproject commit 6bce493029a08b460db88cbaf528e49450751d1f