diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index e4a5c5eff8..9b3caa4ef0 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 0000000000..dd1ab78d28
Binary files /dev/null and b/Telegram/Resources/animations/greeting.tgs differ
diff --git a/Telegram/Resources/animations/hours.tgs b/Telegram/Resources/animations/hours.tgs
new file mode 100644
index 0000000000..d49a48c326
Binary files /dev/null and b/Telegram/Resources/animations/hours.tgs differ
diff --git a/Telegram/Resources/animations/location.tgs b/Telegram/Resources/animations/location.tgs
new file mode 100644
index 0000000000..32ba54f16c
Binary files /dev/null and b/Telegram/Resources/animations/location.tgs differ
diff --git a/Telegram/Resources/animations/phone.tgs b/Telegram/Resources/animations/phone.tgs
new file mode 100644
index 0000000000..7541526afb
Binary files /dev/null and b/Telegram/Resources/animations/phone.tgs differ
diff --git a/Telegram/Resources/animations/sleep.tgs b/Telegram/Resources/animations/sleep.tgs
new file mode 100644
index 0000000000..b766d6e438
Binary files /dev/null and b/Telegram/Resources/animations/sleep.tgs differ
diff --git a/Telegram/Resources/animations/writing.tgs b/Telegram/Resources/animations/writing.tgs
new file mode 100644
index 0000000000..47caac05a2
Binary files /dev/null and b/Telegram/Resources/animations/writing.tgs differ
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 73b164deee..89508d8c5d 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 ede8feb2d6..12666b6fd3 100644
--- a/Telegram/Resources/qrc/telegram/animations.qrc
+++ b/Telegram/Resources/qrc/telegram/animations.qrc
@@ -14,6 +14,12 @@
../../animations/voice_ttl_idle.tgs
../../animations/voice_ttl_start.tgs
../../animations/palette.tgs
- ../../animations/robot.tgs
+ ../../animations/sleep.tgs
+ ../../animations/greeting.tgs
+ ../../animations/location.tgs
+ ../../animations/robot.tgs
+ ../../animations/writing.tgs
+ ../../animations/hours.tgs
+ ../../animations/phone.tgs
diff --git a/Telegram/SourceFiles/data/business/data_business_chatbots.h b/Telegram/SourceFiles/data/business/data_business_chatbots.h
index adfe998d2c..13b8a894bd 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 aed51fdf9a..743ddaa124 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;
-struct BusinessExceptions {
+struct BusinessChats {
BusinessChatTypes types;
std::vector> 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 0000000000..de2f1c454b
--- /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 {
+public:
+ AwayMessage(
+ QWidget *parent,
+ not_null controller);
+ ~AwayMessage();
+
+ [[nodiscard]] rpl::producer title() override;
+
+private:
+ void setupContent(not_null controller);
+ void save();
+
+ rpl::variable _recipients;
+
+};
+
+AwayMessage::AwayMessage(
+ QWidget *parent,
+ not_null controller)
+: BusinessSection(parent, controller) {
+ setupContent(controller);
+}
+
+AwayMessage::~AwayMessage() {
+ if (!Core::Quitting()) {
+ save();
+ }
+}
+
+rpl::producer AwayMessage::title() {
+ return tr::lng_away_title();
+}
+
+void AwayMessage::setupContent(
+ not_null controller) {
+ using namespace rpl::mappers;
+
+ const auto content = Ui::CreateChild(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(
+ content,
+ tr::lng_away_enable(),
+ st::settingsButtonNoIcon
+ ))->toggleOn(rpl::single(false));
+
+ const auto wrap = content->add(
+ object_ptr>(
+ content,
+ object_ptr(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 0000000000..e9037b4f66
--- /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 568aca80f5..0000000000
--- 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,
- 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 user) {
- return user->owner().history(user);
- });
- auto controller = std::make_unique(
- session,
- (descriptor.allow
- ? tr::lng_filters_include_title()
- : tr::lng_filters_exclude_title()),
- options,
- TypesToFlags(descriptor.current.types) & options,
- base::flat_set>(begin(peers), end(peers)),
- [=](int count) {
- return nullptr; AssertIsDebug();
- });
- const auto rawController = controller.get();
- const auto save = descriptor.save;
- auto initBox = [=](not_null 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 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(std::move(controller), std::move(initBox)));
-}
-
-not_null SetupBusinessExceptionsPreview(
- not_null content,
- not_null*> data) {
- const auto rules = data->current();
-
- const auto locked = std::make_shared();
- auto &&peers = data->current().list | ranges::views::transform([=](
- not_null user) {
- return user->owner().history(user);
- });
- const auto preview = content->add(object_ptr(
- content,
- TypesToFlags(data->current().types),
- base::flat_set>(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) {
- 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 user) {
- return user->owner().history(user);
- });
- preview->updateData(
- TypesToFlags(rules.types),
- base::flat_set>(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 e60f1a01bb..0000000000
--- 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 save;
- bool allow = false;
-};
-void EditBusinessExceptions(
- not_null window,
- BusinessExceptionsDescriptor &&descriptor);
-
-not_null SetupBusinessExceptionsPreview(
- not_null content,
- not_null*> data);
-
-} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/business/settings_chatbots.cpp b/Telegram/SourceFiles/settings/business/settings_chatbots.cpp
index d358b51126..5500ca539f 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 {
+struct BotState {
+ UserData *bot = nullptr;
+ LookupState state = LookupState::Empty;
+};
+
+class Chatbots : public BusinessSection {
public:
Chatbots(
QWidget *parent,
@@ -40,10 +47,6 @@ public:
[[nodiscard]] rpl::producer 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 controller);
void save();
- void showFinished() override {
- _showFinished.fire({});
- }
-
- const not_null _controller;
- const not_null _session;
-
- rpl::event_stream<> _showFinished;
Ui::RoundRect _bottomSkipRounding;
- rpl::variable _onlySelected = false;
+ rpl::variable _recipients;
+ rpl::variable _usernameValue;
+ rpl::variable _botValue = nullptr;
rpl::variable _repliesAllowed = true;
- rpl::variable _allowed;
- rpl::variable _disallowed;
};
+[[nodiscard]] rpl::producer DebouncedValue(
+ not_null field) {
+ return rpl::single(field->getLastText());
+}
+
+[[nodiscard]] rpl::producer LookupBot(
+ not_null session,
+ rpl::producer usernameChanges) {
+ return rpl::never();
+}
+
+[[nodiscard]] object_ptr MakeBotPreview(
+ not_null parent,
+ rpl::producer state,
+ Fn resetBot) {
+ return object_ptr(parent.get());
+}
+
Chatbots::Chatbots(
QWidget *parent,
not_null 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(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>(
+ 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(
- _onlySelected.current() ? kSelectedOnly : kAllExcept);
- const auto everyone = content->add(
- object_ptr(
- content,
- group,
- kAllExcept,
- tr::lng_chatbots_all_except(tr::now),
- st::settingsChatbotsAccess),
- st::settingsChatbotsAccessMargins);
- const auto selected = content->add(
- object_ptr(
- 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>(
- content,
- object_ptr(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>(
- content,
- object_ptr(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 0000000000..599b25b2c3
--- /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 {
+public:
+ Greeting(
+ QWidget *parent,
+ not_null controller);
+ ~Greeting();
+
+ [[nodiscard]] rpl::producer title() override;
+
+private:
+ void setupContent(not_null controller);
+ void save();
+
+ rpl::variable _recipients;
+
+};
+
+Greeting::Greeting(
+ QWidget *parent,
+ not_null controller)
+: BusinessSection(parent, controller) {
+ setupContent(controller);
+}
+
+Greeting::~Greeting() {
+ if (!Core::Quitting()) {
+ save();
+ }
+}
+
+rpl::producer Greeting::title() {
+ return tr::lng_greeting_title();
+}
+
+void Greeting::setupContent(
+ not_null controller) {
+ using namespace rpl::mappers;
+
+ const auto content = Ui::CreateChild(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(
+ content,
+ tr::lng_greeting_enable(),
+ st::settingsButtonNoIcon
+ ))->toggleOn(rpl::single(false));
+
+ const auto wrap = content->add(
+ object_ptr>(
+ content,
+ object_ptr(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 0000000000..2bb9afd591
--- /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 0000000000..84e8d4e4be
--- /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 {
+public:
+ Location(
+ QWidget *parent,
+ not_null controller);
+ ~Location();
+
+ [[nodiscard]] rpl::producer title() override;
+
+ const Ui::RoundRect *bottomSkipRounding() const {
+ return mapSupported() ? nullptr : &_bottomSkipRounding;
+ }
+
+private:
+ void setupContent(not_null controller);
+ void save();
+
+ [[nodiscard]] bool mapSupported() const;
+
+ Ui::RoundRect _bottomSkipRounding;
+
+};
+
+Location::Location(
+ QWidget *parent,
+ not_null controller)
+: BusinessSection(parent, controller)
+, _bottomSkipRounding(st::boxRadius, st::boxDividerBg) {
+ setupContent(controller);
+}
+
+Location::~Location() {
+ if (!Core::Quitting()) {
+ save();
+ }
+}
+
+rpl::producer Location::title() {
+ return tr::lng_location_title();
+}
+
+void Location::setupContent(
+ not_null controller) {
+ using namespace rpl::mappers;
+
+ const auto content = Ui::CreateChild(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(
+ 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 0000000000..31e0332532
--- /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 0000000000..dc8927bc25
--- /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 {
+public:
+ QuickReplies(
+ QWidget *parent,
+ not_null controller);
+ ~QuickReplies();
+
+ [[nodiscard]] rpl::producer title() override;
+
+private:
+ void setupContent(not_null controller);
+ void save();
+
+ rpl::variable _recipients;
+
+};
+
+QuickReplies::QuickReplies(
+ QWidget *parent,
+ not_null controller)
+: BusinessSection(parent, controller) {
+ setupContent(controller);
+}
+
+QuickReplies::~QuickReplies() {
+ if (!Core::Quitting()) {
+ save();
+ }
+}
+
+rpl::producer QuickReplies::title() {
+ return tr::lng_replies_title();
+}
+
+void QuickReplies::setupContent(
+ not_null controller) {
+ using namespace rpl::mappers;
+
+ const auto content = Ui::CreateChild(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(
+ content,
+ tr::lng_replies_add(),
+ st::settingsButtonNoIcon
+ ));
+
+ enabled->setClickedCallback([=] {
+
+ });
+
+ const auto wrap = content->add(
+ object_ptr>(
+ content,
+ object_ptr(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 0000000000..80cc2f129c
--- /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 0000000000..a6288dbee7
--- /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,
+ 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 user) {
+ return user->owner().history(user);
+ });
+ auto controller = std::make_unique(
+ session,
+ (descriptor.include
+ ? tr::lng_filters_include_title()
+ : tr::lng_filters_exclude_title()),
+ options,
+ TypesToFlags(descriptor.current.types) & options,
+ base::flat_set>(begin(peers), end(peers)),
+ [=](int count) {
+ return nullptr; AssertIsDebug();
+ });
+ const auto rawController = controller.get();
+ const auto save = descriptor.save;
+ auto initBox = [=](not_null 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 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(std::move(controller), std::move(initBox)));
+}
+
+not_null SetupBusinessChatsPreview(
+ not_null container,
+ not_null*> data) {
+ const auto rules = data->current();
+
+ const auto locked = std::make_shared();
+ auto &&peers = data->current().list | ranges::views::transform([=](
+ not_null user) {
+ return user->owner().history(user);
+ });
+ const auto preview = container->add(object_ptr(
+ container,
+ TypesToFlags(data->current().types),
+ base::flat_set>(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) {
+ 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 user) {
+ return user->owner().history(user);
+ });
+ preview->updateData(
+ TypesToFlags(rules.types),
+ base::flat_set>(begin(peers), end(peers)));
+ }, preview->lifetime());
+
+ return preview;
+}
+
+void AddBusinessRecipientsSelector(
+ not_null 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 modify) {
+ auto now = data->current();
+ modify(now);
+ *data = std::move(now);
+ };
+ const auto group = std::make_shared(
+ data->current().onlyIncluded ? kSelectedOnly : kAllExcept);
+ const auto everyone = container->add(
+ object_ptr(
+ container,
+ group,
+ kAllExcept,
+ tr::lng_chatbots_all_except(tr::now),
+ st::settingsChatbotsAccess),
+ st::settingsChatbotsAccessMargins);
+ const auto selected = container->add(
+ object_ptr(
+ 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>(
+ container,
+ object_ptr(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->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>(
+ container,
+ object_ptr(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->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 0000000000..60efd74252
--- /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
+class BusinessSection : public Section {
+public:
+ BusinessSection(
+ QWidget *parent,
+ not_null controller)
+ : Section(parent)
+ , _controller(controller) {
+ }
+
+ [[nodiscard]] not_null controller() const {
+ return _controller;
+ }
+ [[nodiscard]] rpl::producer<> showFinishes() const {
+ return _showFinished.events();
+ }
+
+private:
+ void showFinished() override {
+ _showFinished.fire({});
+ }
+
+ const not_null _controller;
+ rpl::event_stream<> _showFinished;
+
+};
+
+struct BusinessChatsDescriptor {
+ Data::BusinessChats current;
+ Fn save;
+ bool include = false;
+};
+void EditBusinessChats(
+ not_null window,
+ BusinessChatsDescriptor &&descriptor);
+
+not_null SetupBusinessChatsPreview(
+ not_null container,
+ not_null*> data);
+
+struct BusinessRecipientsSelectorDescriptor {
+ not_null controller;
+ rpl::producer title;
+ not_null*> data;
+};
+void AddBusinessRecipientsSelector(
+ not_null 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 0000000000..7b2e878735
--- /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 {
+public:
+ WorkingHours(
+ QWidget *parent,
+ not_null controller);
+ ~WorkingHours();
+
+ [[nodiscard]] rpl::producer title() override;
+
+private:
+ void setupContent(not_null controller);
+ void save();
+
+};
+
+WorkingHours::WorkingHours(
+ QWidget *parent,
+ not_null controller)
+: BusinessSection(parent, controller) {
+ setupContent(controller);
+}
+
+WorkingHours::~WorkingHours() {
+ if (!Core::Quitting()) {
+ save();
+ }
+}
+
+rpl::producer WorkingHours::title() {
+ return tr::lng_hours_title();
+}
+
+void WorkingHours::setupContent(
+ not_null controller) {
+ using namespace rpl::mappers;
+
+ const auto content = Ui::CreateChild(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(
+ content,
+ tr::lng_hours_show(),
+ st::settingsButtonNoIcon
+ ))->toggleOn(rpl::single(false));
+
+ const auto wrap = content->add(
+ object_ptr>(
+ content,
+ object_ptr(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 0000000000..213ef14880
--- /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 f4eebf4ff8..ef519dced4 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 fd56adb414..e72391272f 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 7e4ac41800..4480611675 100644
--- a/Telegram/SourceFiles/settings/settings_common.cpp
+++ b/Telegram/SourceFiles/settings/settings_common.cpp
@@ -173,7 +173,10 @@ void AddDividerTextWithLottie(
not_null container,
DividerWithLottieDescriptor &&descriptor) {
const auto divider = Ui::CreateChild(
- container.get());
+ container.get(),
+ 0,
+ st::boxDividerBg,
+ descriptor.parts);
const auto verticalLayout = container->add(
object_ptr(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 00ecf2fe29..ea80b45735 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 about;
std::optional aboutMargins;
+ RectParts parts = RectPart::Top | RectPart::Bottom;
};
void AddDividerTextWithLottie(
not_null container,