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,