mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Start editing chat intro in Telegram Business.
This commit is contained in:
parent
cf1d0677d1
commit
191c35914f
30 changed files with 1056 additions and 44 deletions
|
@ -1293,6 +1293,8 @@ PRIVATE
|
||||||
settings/business/settings_away_message.h
|
settings/business/settings_away_message.h
|
||||||
settings/business/settings_shortcut_messages.cpp
|
settings/business/settings_shortcut_messages.cpp
|
||||||
settings/business/settings_shortcut_messages.h
|
settings/business/settings_shortcut_messages.h
|
||||||
|
settings/business/settings_chat_intro.cpp
|
||||||
|
settings/business/settings_chat_intro.h
|
||||||
settings/business/settings_chatbots.cpp
|
settings/business/settings_chatbots.cpp
|
||||||
settings/business/settings_chatbots.h
|
settings/business/settings_chatbots.h
|
||||||
settings/business/settings_greeting.cpp
|
settings/business/settings_greeting.cpp
|
||||||
|
|
BIN
Telegram/Resources/icons/settings/premium/intro.png
Normal file
BIN
Telegram/Resources/icons/settings/premium/intro.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 523 B |
BIN
Telegram/Resources/icons/settings/premium/intro@2x.png
Normal file
BIN
Telegram/Resources/icons/settings/premium/intro@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 973 B |
BIN
Telegram/Resources/icons/settings/premium/intro@3x.png
Normal file
BIN
Telegram/Resources/icons/settings/premium/intro@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
|
@ -2185,6 +2185,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_business_about_away_messages" = "Define messages that are automatically sent when you are off.";
|
"lng_business_about_away_messages" = "Define messages that are automatically sent when you are off.";
|
||||||
"lng_business_subtitle_chatbots" = "Chatbots";
|
"lng_business_subtitle_chatbots" = "Chatbots";
|
||||||
"lng_business_about_chatbots" = "Add any third party chatbots that will process customer interactions.";
|
"lng_business_about_chatbots" = "Add any third party chatbots that will process customer interactions.";
|
||||||
|
"lng_business_subtitle_chat_intro" = "Intro";
|
||||||
|
"lng_business_about_chat_intro" = "Customize the message people see before they start a chat with you.";
|
||||||
|
|
||||||
"lng_location_title" = "Location";
|
"lng_location_title" = "Location";
|
||||||
"lng_location_about" = "Display the location of your business on your account.";
|
"lng_location_about" = "Display the location of your business on your account.";
|
||||||
|
@ -2297,6 +2299,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_chatbot_menu_remove" = "Remove bot from this chat";
|
"lng_chatbot_menu_remove" = "Remove bot from this chat";
|
||||||
"lng_chatbot_menu_revoke" = "Revoke access to this chat";
|
"lng_chatbot_menu_revoke" = "Revoke access to this chat";
|
||||||
|
|
||||||
|
"lng_chat_intro_title" = "Intro";
|
||||||
|
"lng_chat_intro_subtitle" = "Customize your intro";
|
||||||
|
"lng_chat_intro_default_title" = "No messages here yet...";
|
||||||
|
"lng_chat_intro_default_message" = "Send a message or tap on the greeting below";
|
||||||
|
"lng_chat_intro_enter_title" = "Enter Title";
|
||||||
|
"lng_chat_intro_enter_message" = "Enter Message";
|
||||||
|
"lng_chat_intro_choose_sticker" = "Choose Sticker";
|
||||||
|
"lng_chat_intro_random_sticker" = "Random";
|
||||||
|
"lng_chat_intro_about" = "You can customize the message people see before they start a chat with you.";
|
||||||
|
"lng_chat_intro_reset" = "Reset to Default";
|
||||||
|
|
||||||
"lng_boost_channel_button" = "Boost Channel";
|
"lng_boost_channel_button" = "Boost Channel";
|
||||||
"lng_boost_group_button" = "Boost Group";
|
"lng_boost_group_button" = "Boost Group";
|
||||||
"lng_boost_again_button" = "Boost Again";
|
"lng_boost_again_button" = "Boost Again";
|
||||||
|
|
|
@ -109,6 +109,18 @@ rpl::producer<> Premium::cloudSetUpdated() const {
|
||||||
return _cloudSetUpdated.events();
|
return _cloudSetUpdated.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Premium::helloStickers() const
|
||||||
|
-> const std::vector<not_null<DocumentData*>> & {
|
||||||
|
if (_helloStickers.empty()) {
|
||||||
|
const_cast<Premium*>(this)->reloadHelloStickers();
|
||||||
|
}
|
||||||
|
return _helloStickers;
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> Premium::helloStickersUpdated() const {
|
||||||
|
return _helloStickersUpdated.events();
|
||||||
|
}
|
||||||
|
|
||||||
int64 Premium::monthlyAmount() const {
|
int64 Premium::monthlyAmount() const {
|
||||||
return _monthlyAmount;
|
return _monthlyAmount;
|
||||||
}
|
}
|
||||||
|
@ -225,6 +237,33 @@ void Premium::reloadCloudSet() {
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Premium::reloadHelloStickers() {
|
||||||
|
if (_helloStickersRequestId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_helloStickersRequestId = _api.request(MTPmessages_GetStickers(
|
||||||
|
MTP_string("\xf0\x9f\x91\x8b\xe2\xad\x90\xef\xb8\x8f"),
|
||||||
|
MTP_long(_helloStickersHash)
|
||||||
|
)).done([=](const MTPmessages_Stickers &result) {
|
||||||
|
_helloStickersRequestId = 0;
|
||||||
|
result.match([&](const MTPDmessages_stickersNotModified &) {
|
||||||
|
}, [&](const MTPDmessages_stickers &data) {
|
||||||
|
_helloStickersHash = data.vhash().v;
|
||||||
|
const auto owner = &_session->data();
|
||||||
|
_helloStickers.clear();
|
||||||
|
for (const auto &sticker : data.vstickers().v) {
|
||||||
|
const auto document = owner->processDocument(sticker);
|
||||||
|
if (document->sticker()) {
|
||||||
|
_helloStickers.push_back(document);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_helloStickersUpdated.fire({});
|
||||||
|
});
|
||||||
|
}).fail([=] {
|
||||||
|
_helloStickersRequestId = 0;
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
void Premium::checkGiftCode(
|
void Premium::checkGiftCode(
|
||||||
const QString &slug,
|
const QString &slug,
|
||||||
Fn<void(GiftCode)> done) {
|
Fn<void(GiftCode)> done) {
|
||||||
|
@ -609,4 +648,24 @@ RequirePremiumState ResolveRequiresPremiumToWrite(
|
||||||
return RequirePremiumState::Unknown;
|
return RequirePremiumState::Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<DocumentData*> RandomHelloStickerValue(
|
||||||
|
not_null<Main::Session*> session) {
|
||||||
|
const auto premium = &session->api().premium();
|
||||||
|
const auto random = [=] {
|
||||||
|
const auto &v = premium->helloStickers();
|
||||||
|
Assert(!v.empty());
|
||||||
|
return v[base::RandomIndex(v.size())].get();
|
||||||
|
};
|
||||||
|
const auto &v = premium->helloStickers();
|
||||||
|
if (!v.empty()) {
|
||||||
|
return rpl::single(random());
|
||||||
|
}
|
||||||
|
return rpl::single<DocumentData*>(
|
||||||
|
nullptr
|
||||||
|
) | rpl::then(premium->helloStickersUpdated(
|
||||||
|
) | rpl::filter([=] {
|
||||||
|
return !premium->helloStickers().empty();
|
||||||
|
}) | rpl::take(1) | rpl::map(random));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Api
|
} // namespace Api
|
||||||
|
|
|
@ -85,6 +85,10 @@ public:
|
||||||
-> const std::vector<not_null<DocumentData*>> &;
|
-> const std::vector<not_null<DocumentData*>> &;
|
||||||
[[nodiscard]] rpl::producer<> cloudSetUpdated() const;
|
[[nodiscard]] rpl::producer<> cloudSetUpdated() const;
|
||||||
|
|
||||||
|
[[nodiscard]] auto helloStickers() const
|
||||||
|
-> const std::vector<not_null<DocumentData*>> &;
|
||||||
|
[[nodiscard]] rpl::producer<> helloStickersUpdated() const;
|
||||||
|
|
||||||
[[nodiscard]] int64 monthlyAmount() const;
|
[[nodiscard]] int64 monthlyAmount() const;
|
||||||
[[nodiscard]] QString monthlyCurrency() const;
|
[[nodiscard]] QString monthlyCurrency() const;
|
||||||
|
|
||||||
|
@ -111,6 +115,7 @@ private:
|
||||||
void reloadPromo();
|
void reloadPromo();
|
||||||
void reloadStickers();
|
void reloadStickers();
|
||||||
void reloadCloudSet();
|
void reloadCloudSet();
|
||||||
|
void reloadHelloStickers();
|
||||||
void requestPremiumRequiredSlice();
|
void requestPremiumRequiredSlice();
|
||||||
|
|
||||||
const not_null<Main::Session*> _session;
|
const not_null<Main::Session*> _session;
|
||||||
|
@ -133,6 +138,11 @@ private:
|
||||||
std::vector<not_null<DocumentData*>> _cloudSet;
|
std::vector<not_null<DocumentData*>> _cloudSet;
|
||||||
rpl::event_stream<> _cloudSetUpdated;
|
rpl::event_stream<> _cloudSetUpdated;
|
||||||
|
|
||||||
|
mtpRequestId _helloStickersRequestId = 0;
|
||||||
|
uint64 _helloStickersHash = 0;
|
||||||
|
std::vector<not_null<DocumentData*>> _helloStickers;
|
||||||
|
rpl::event_stream<> _helloStickersUpdated;
|
||||||
|
|
||||||
int64 _monthlyAmount = 0;
|
int64 _monthlyAmount = 0;
|
||||||
QString _monthlyCurrency;
|
QString _monthlyCurrency;
|
||||||
|
|
||||||
|
@ -215,4 +225,7 @@ enum class RequirePremiumState {
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
History *maybeHistory);
|
History *maybeHistory);
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<DocumentData*> RandomHelloStickerValue(
|
||||||
|
not_null<Main::Session*> session);
|
||||||
|
|
||||||
} // namespace Api
|
} // namespace Api
|
||||||
|
|
|
@ -808,34 +808,6 @@ int ColorSelector::resizeGetHeight(int newWidth) {
|
||||||
return st;
|
return st;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ButtonWithEmoji {
|
|
||||||
not_null<const style::SettingsButton*> st;
|
|
||||||
int emojiWidth = 0;
|
|
||||||
int noneWidth = 0;
|
|
||||||
int added = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
[[nodiscard]] ButtonWithEmoji ButtonStyleWithRightEmoji(
|
|
||||||
not_null<Ui::RpWidget*> parent) {
|
|
||||||
const auto ratio = style::DevicePixelRatio();
|
|
||||||
const auto emojiWidth = Data::FrameSizeFromTag({}) / ratio;
|
|
||||||
|
|
||||||
const auto noneWidth = st::normalFont->width(
|
|
||||||
tr::lng_settings_color_emoji_off(tr::now));
|
|
||||||
|
|
||||||
const auto added = st::normalFont->spacew;
|
|
||||||
const auto rightAdded = std::max(noneWidth, emojiWidth);
|
|
||||||
return {
|
|
||||||
.st = ButtonStyleWithAddedPadding(
|
|
||||||
parent,
|
|
||||||
st::peerAppearanceButton,
|
|
||||||
QMargins(0, 0, added + rightAdded, 0)),
|
|
||||||
.emojiWidth = emojiWidth,
|
|
||||||
.noneWidth = noneWidth,
|
|
||||||
.added = added,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] object_ptr<Ui::SettingsButton> CreateEmojiIconButton(
|
[[nodiscard]] object_ptr<Ui::SettingsButton> CreateEmojiIconButton(
|
||||||
not_null<Ui::RpWidget*> parent,
|
not_null<Ui::RpWidget*> parent,
|
||||||
std::shared_ptr<ChatHelpers::Show> show,
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
|
@ -844,7 +816,9 @@ struct ButtonWithEmoji {
|
||||||
rpl::producer<uint8> colorIndexValue,
|
rpl::producer<uint8> colorIndexValue,
|
||||||
rpl::producer<DocumentId> emojiIdValue,
|
rpl::producer<DocumentId> emojiIdValue,
|
||||||
Fn<void(DocumentId)> emojiIdChosen) {
|
Fn<void(DocumentId)> emojiIdChosen) {
|
||||||
const auto button = ButtonStyleWithRightEmoji(parent);
|
const auto button = ButtonStyleWithRightEmoji(
|
||||||
|
parent,
|
||||||
|
tr::lng_settings_color_emoji_off(tr::now));
|
||||||
auto result = Settings::CreateButtonWithIcon(
|
auto result = Settings::CreateButtonWithIcon(
|
||||||
parent,
|
parent,
|
||||||
tr::lng_settings_color_emoji(),
|
tr::lng_settings_color_emoji(),
|
||||||
|
@ -962,7 +936,9 @@ struct ButtonWithEmoji {
|
||||||
rpl::producer<DocumentId> statusIdValue,
|
rpl::producer<DocumentId> statusIdValue,
|
||||||
Fn<void(DocumentId,TimeId)> statusIdChosen,
|
Fn<void(DocumentId,TimeId)> statusIdChosen,
|
||||||
bool group) {
|
bool group) {
|
||||||
const auto button = ButtonStyleWithRightEmoji(parent);
|
const auto button = ButtonStyleWithRightEmoji(
|
||||||
|
parent,
|
||||||
|
tr::lng_settings_color_emoji_off(tr::now));
|
||||||
const auto &phrase = group
|
const auto &phrase = group
|
||||||
? tr::lng_edit_channel_status_group
|
? tr::lng_edit_channel_status_group
|
||||||
: tr::lng_edit_channel_status;
|
: tr::lng_edit_channel_status;
|
||||||
|
@ -1073,7 +1049,9 @@ struct ButtonWithEmoji {
|
||||||
not_null<ChannelData*> channel) {
|
not_null<ChannelData*> channel) {
|
||||||
Expects(channel->mgInfo != nullptr);
|
Expects(channel->mgInfo != nullptr);
|
||||||
|
|
||||||
const auto button = ButtonStyleWithRightEmoji(parent);
|
const auto button = ButtonStyleWithRightEmoji(
|
||||||
|
parent,
|
||||||
|
tr::lng_settings_color_emoji_off(tr::now));
|
||||||
auto result = Settings::CreateButtonWithIcon(
|
auto result = Settings::CreateButtonWithIcon(
|
||||||
parent,
|
parent,
|
||||||
tr::lng_group_emoji(),
|
tr::lng_group_emoji(),
|
||||||
|
@ -1509,3 +1487,25 @@ void CheckBoostLevel(
|
||||||
cancel();
|
cancel();
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ButtonWithEmoji ButtonStyleWithRightEmoji(
|
||||||
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
const QString &noneString,
|
||||||
|
const style::SettingsButton &parentSt) {
|
||||||
|
const auto ratio = style::DevicePixelRatio();
|
||||||
|
const auto emojiWidth = Data::FrameSizeFromTag({}) / ratio;
|
||||||
|
|
||||||
|
const auto noneWidth = st::normalFont->width(noneString);
|
||||||
|
|
||||||
|
const auto added = st::normalFont->spacew;
|
||||||
|
const auto rightAdded = std::max(noneWidth, emojiWidth);
|
||||||
|
return {
|
||||||
|
.st = ButtonStyleWithAddedPadding(
|
||||||
|
parent,
|
||||||
|
parentSt,
|
||||||
|
QMargins(0, 0, added + rightAdded, 0)),
|
||||||
|
.emojiWidth = emojiWidth,
|
||||||
|
.noneWidth = noneWidth,
|
||||||
|
.added = added,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
namespace style {
|
||||||
|
struct SettingsButton;
|
||||||
|
} // namespace style
|
||||||
|
|
||||||
|
namespace st {
|
||||||
|
extern const style::SettingsButton &peerAppearanceButton;
|
||||||
|
} // namespace st
|
||||||
|
|
||||||
namespace ChatHelpers {
|
namespace ChatHelpers {
|
||||||
class Show;
|
class Show;
|
||||||
} // namespace ChatHelpers
|
} // namespace ChatHelpers
|
||||||
|
@ -17,6 +25,7 @@ class ChatStyle;
|
||||||
class ChatTheme;
|
class ChatTheme;
|
||||||
class VerticalLayout;
|
class VerticalLayout;
|
||||||
struct AskBoostReason;
|
struct AskBoostReason;
|
||||||
|
class RpWidget;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
void EditPeerColorBox(
|
void EditPeerColorBox(
|
||||||
|
@ -36,3 +45,14 @@ void CheckBoostLevel(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
Fn<std::optional<Ui::AskBoostReason>(int level)> askMore,
|
Fn<std::optional<Ui::AskBoostReason>(int level)> askMore,
|
||||||
Fn<void()> cancel);
|
Fn<void()> cancel);
|
||||||
|
|
||||||
|
struct ButtonWithEmoji {
|
||||||
|
not_null<const style::SettingsButton*> st;
|
||||||
|
int emojiWidth = 0;
|
||||||
|
int noneWidth = 0;
|
||||||
|
int added = 0;
|
||||||
|
};
|
||||||
|
[[nodiscard]] ButtonWithEmoji ButtonStyleWithRightEmoji(
|
||||||
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
const QString &noneString,
|
||||||
|
const style::SettingsButton &parentSt = st::peerAppearanceButton);
|
||||||
|
|
|
@ -144,6 +144,8 @@ void PreloadSticker(const std::shared_ptr<Data::DocumentMedia> &media) {
|
||||||
return tr::lng_business_subtitle_away_messages();
|
return tr::lng_business_subtitle_away_messages();
|
||||||
case PremiumFeature::BusinessBots:
|
case PremiumFeature::BusinessBots:
|
||||||
return tr::lng_business_subtitle_chatbots();
|
return tr::lng_business_subtitle_chatbots();
|
||||||
|
case PremiumFeature::ChatIntro:
|
||||||
|
return tr::lng_business_subtitle_chat_intro();
|
||||||
}
|
}
|
||||||
Unexpected("PremiumFeature in SectionTitle.");
|
Unexpected("PremiumFeature in SectionTitle.");
|
||||||
}
|
}
|
||||||
|
@ -201,6 +203,8 @@ void PreloadSticker(const std::shared_ptr<Data::DocumentMedia> &media) {
|
||||||
return tr::lng_business_about_away_messages();
|
return tr::lng_business_about_away_messages();
|
||||||
case PremiumFeature::BusinessBots:
|
case PremiumFeature::BusinessBots:
|
||||||
return tr::lng_business_about_chatbots();
|
return tr::lng_business_about_chatbots();
|
||||||
|
case PremiumFeature::ChatIntro:
|
||||||
|
return tr::lng_business_about_chat_intro();
|
||||||
}
|
}
|
||||||
Unexpected("PremiumFeature in SectionTitle.");
|
Unexpected("PremiumFeature in SectionTitle.");
|
||||||
}
|
}
|
||||||
|
@ -528,6 +532,7 @@ struct VideoPreviewDocument {
|
||||||
case PremiumFeature::GreetingMessage: return "greeting_message";
|
case PremiumFeature::GreetingMessage: return "greeting_message";
|
||||||
case PremiumFeature::AwayMessage: return "away_message";
|
case PremiumFeature::AwayMessage: return "away_message";
|
||||||
case PremiumFeature::BusinessBots: return "business_bots";
|
case PremiumFeature::BusinessBots: return "business_bots";
|
||||||
|
case PremiumFeature::ChatIntro: return "chat_intro";
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}();
|
}();
|
||||||
|
|
|
@ -74,6 +74,7 @@ enum class PremiumFeature {
|
||||||
GreetingMessage,
|
GreetingMessage,
|
||||||
AwayMessage,
|
AwayMessage,
|
||||||
BusinessBots,
|
BusinessBots,
|
||||||
|
ChatIntro,
|
||||||
|
|
||||||
kCount,
|
kCount,
|
||||||
};
|
};
|
||||||
|
|
|
@ -66,7 +66,7 @@ enum class StickerLottieSize : uint8 {
|
||||||
EmojiInteractionReserved5,
|
EmojiInteractionReserved5,
|
||||||
EmojiInteractionReserved6,
|
EmojiInteractionReserved6,
|
||||||
EmojiInteractionReserved7,
|
EmojiInteractionReserved7,
|
||||||
PremiumReactionPreview,
|
ChatIntroHelloSticker,
|
||||||
};
|
};
|
||||||
[[nodiscard]] uint8 LottieCacheKeyShift(
|
[[nodiscard]] uint8 LottieCacheKeyShift(
|
||||||
uint8 replacementsTag,
|
uint8 replacementsTag,
|
||||||
|
|
|
@ -378,6 +378,9 @@ TabbedSelector::TabbedSelector(
|
||||||
tabs.reserve(2);
|
tabs.reserve(2);
|
||||||
tabs.push_back(createTab(SelectorTab::Stickers, 0));
|
tabs.push_back(createTab(SelectorTab::Stickers, 0));
|
||||||
tabs.push_back(createTab(SelectorTab::Masks, 1));
|
tabs.push_back(createTab(SelectorTab::Masks, 1));
|
||||||
|
} else if (_mode == Mode::StickersOnly) {
|
||||||
|
tabs.reserve(1);
|
||||||
|
tabs.push_back(createTab(SelectorTab::Stickers, 0));
|
||||||
} else {
|
} else {
|
||||||
tabs.reserve(1);
|
tabs.reserve(1);
|
||||||
tabs.push_back(createTab(SelectorTab::Emoji, 0));
|
tabs.push_back(createTab(SelectorTab::Emoji, 0));
|
||||||
|
@ -385,10 +388,10 @@ TabbedSelector::TabbedSelector(
|
||||||
return tabs;
|
return tabs;
|
||||||
}())
|
}())
|
||||||
, _currentTabType(full()
|
, _currentTabType(full()
|
||||||
? session().settings().selectorTab()
|
? session().settings().selectorTab()
|
||||||
: mediaEditor()
|
: (mediaEditor() || _mode == Mode::StickersOnly)
|
||||||
? SelectorTab::Stickers
|
? SelectorTab::Stickers
|
||||||
: SelectorTab::Emoji)
|
: SelectorTab::Emoji)
|
||||||
, _hasEmojiTab(ranges::contains(_tabs, SelectorTab::Emoji, &Tab::type))
|
, _hasEmojiTab(ranges::contains(_tabs, SelectorTab::Emoji, &Tab::type))
|
||||||
, _hasStickersTab(ranges::contains(_tabs, SelectorTab::Stickers, &Tab::type))
|
, _hasStickersTab(ranges::contains(_tabs, SelectorTab::Stickers, &Tab::type))
|
||||||
, _hasGifsTab(ranges::contains(_tabs, SelectorTab::Gifs, &Tab::type))
|
, _hasGifsTab(ranges::contains(_tabs, SelectorTab::Gifs, &Tab::type))
|
||||||
|
@ -661,7 +664,7 @@ void TabbedSelector::updateTabsSliderGeometry() {
|
||||||
if (!_tabsSlider) {
|
if (!_tabsSlider) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto w = mediaEditor() && hasMasksTab() && masks()->mySetsEmpty()
|
const auto w = (mediaEditor() && hasMasksTab() && masks()->mySetsEmpty())
|
||||||
? width() / 2
|
? width() / 2
|
||||||
: width();
|
: width();
|
||||||
_tabsSlider->resizeToWidth(w);
|
_tabsSlider->resizeToWidth(w);
|
||||||
|
|
|
@ -79,6 +79,7 @@ using InlineChosen = InlineBots::ResultSelected;
|
||||||
enum class TabbedSelectorMode {
|
enum class TabbedSelectorMode {
|
||||||
Full,
|
Full,
|
||||||
EmojiOnly,
|
EmojiOnly,
|
||||||
|
StickersOnly,
|
||||||
MediaEditor,
|
MediaEditor,
|
||||||
EmojiStatus,
|
EmojiStatus,
|
||||||
ChannelStatus,
|
ChannelStatus,
|
||||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "data/business/data_business_common.h"
|
#include "data/business/data_business_common.h"
|
||||||
|
|
||||||
|
#include "data/data_document.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
|
|
||||||
|
@ -333,4 +334,22 @@ WorkingIntervals ReplaceDayIntervals(
|
||||||
return result.normalized();
|
return result.normalized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChatIntro FromMTP(
|
||||||
|
not_null<Session*> owner,
|
||||||
|
const tl::conditional<MTPBusinessIntro> &intro) {
|
||||||
|
auto result = ChatIntro();
|
||||||
|
if (intro) {
|
||||||
|
const auto &data = intro->data();
|
||||||
|
result.title = qs(data.vtitle());
|
||||||
|
result.description = qs(data.vdescription());
|
||||||
|
if (const auto document = data.vsticker()) {
|
||||||
|
result.sticker = owner->processDocument(*document);
|
||||||
|
if (!result.sticker->sticker()) {
|
||||||
|
result.sticker = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
@ -252,4 +252,22 @@ struct GreetingSettings {
|
||||||
not_null<Session*> owner,
|
not_null<Session*> owner,
|
||||||
const tl::conditional<MTPBusinessGreetingMessage> &message);
|
const tl::conditional<MTPBusinessGreetingMessage> &message);
|
||||||
|
|
||||||
|
struct ChatIntro {
|
||||||
|
QString title;
|
||||||
|
QString description;
|
||||||
|
DocumentData *sticker = nullptr;
|
||||||
|
|
||||||
|
explicit operator bool() const {
|
||||||
|
return !title.isEmpty() || !description.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
friend inline bool operator==(
|
||||||
|
const ChatIntro &a,
|
||||||
|
const ChatIntro &b) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] ChatIntro FromMTP(
|
||||||
|
not_null<Session*> owner,
|
||||||
|
const tl::conditional<MTPBusinessIntro> &intro);
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "data/business/data_business_common.h"
|
#include "data/business/data_business_common.h"
|
||||||
|
#include "data/data_document.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
@ -183,6 +184,54 @@ rpl::producer<> BusinessInfo::greetingSettingsChanged() const {
|
||||||
return _greetingSettingsChanged.events();
|
return _greetingSettingsChanged.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BusinessInfo::saveChatIntro(ChatIntro data, Fn<void(QString)> fail) {
|
||||||
|
const auto &was = _chatIntro;
|
||||||
|
if (was == data) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
const auto session = &_owner->session();
|
||||||
|
using Flag = MTPaccount_UpdateBusinessIntro::Flag;
|
||||||
|
session->api().request(MTPaccount_UpdateBusinessIntro(
|
||||||
|
MTP_flags(data ? Flag::f_intro : Flag()),
|
||||||
|
MTP_inputBusinessIntro(
|
||||||
|
MTP_flags(data.sticker ? MTPDinputBusinessIntro::Flag::f_sticker : MTPDinputBusinessIntro::Flag()),
|
||||||
|
MTP_string(data.title),
|
||||||
|
MTP_string(data.description),
|
||||||
|
(data.sticker
|
||||||
|
? data.sticker->mtpInput()
|
||||||
|
: MTP_inputDocumentEmpty()))
|
||||||
|
)).fail([=](const MTP::Error &error) {
|
||||||
|
_chatIntro = was;
|
||||||
|
_chatIntroChanged.fire({});
|
||||||
|
if (fail) {
|
||||||
|
fail(error.type());
|
||||||
|
}
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
_chatIntro = std::move(data);
|
||||||
|
_chatIntroChanged.fire({});
|
||||||
|
}
|
||||||
|
|
||||||
|
void BusinessInfo::applyChatIntro(ChatIntro data) {
|
||||||
|
if (_chatIntro == data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_chatIntro = data;
|
||||||
|
_chatIntroChanged.fire({});
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatIntro BusinessInfo::chatIntro() const {
|
||||||
|
return _chatIntro.value_or(ChatIntro());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BusinessInfo::chatIntroLoaded() const {
|
||||||
|
return _chatIntro.has_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> BusinessInfo::chatIntroChanged() const {
|
||||||
|
return _chatIntroChanged.events();
|
||||||
|
}
|
||||||
|
|
||||||
void BusinessInfo::preload() {
|
void BusinessInfo::preload() {
|
||||||
preloadTimezones();
|
preloadTimezones();
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,12 @@ public:
|
||||||
[[nodiscard]] bool greetingSettingsLoaded() const;
|
[[nodiscard]] bool greetingSettingsLoaded() const;
|
||||||
[[nodiscard]] rpl::producer<> greetingSettingsChanged() const;
|
[[nodiscard]] rpl::producer<> greetingSettingsChanged() const;
|
||||||
|
|
||||||
|
void saveChatIntro(ChatIntro data, Fn<void(QString)> fail);
|
||||||
|
void applyChatIntro(ChatIntro data);
|
||||||
|
[[nodiscard]] ChatIntro chatIntro() const;
|
||||||
|
[[nodiscard]] bool chatIntroLoaded() const;
|
||||||
|
[[nodiscard]] rpl::producer<> chatIntroChanged() const;
|
||||||
|
|
||||||
void preloadTimezones();
|
void preloadTimezones();
|
||||||
[[nodiscard]] bool timezonesLoaded() const;
|
[[nodiscard]] bool timezonesLoaded() const;
|
||||||
[[nodiscard]] rpl::producer<Timezones> timezonesValue() const;
|
[[nodiscard]] rpl::producer<Timezones> timezonesValue() const;
|
||||||
|
@ -51,6 +57,9 @@ private:
|
||||||
std::optional<GreetingSettings> _greetingSettings;
|
std::optional<GreetingSettings> _greetingSettings;
|
||||||
rpl::event_stream<> _greetingSettingsChanged;
|
rpl::event_stream<> _greetingSettingsChanged;
|
||||||
|
|
||||||
|
std::optional<ChatIntro> _chatIntro;
|
||||||
|
rpl::event_stream<> _chatIntroChanged;
|
||||||
|
|
||||||
mtpRequestId _timezonesRequestId = 0;
|
mtpRequestId _timezonesRequestId = 0;
|
||||||
int32 _timezonesHash = 0;
|
int32 _timezonesHash = 0;
|
||||||
|
|
||||||
|
|
|
@ -601,6 +601,8 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
|
||||||
FromMTP(&user->owner(), update.vbusiness_away_message()));
|
FromMTP(&user->owner(), update.vbusiness_away_message()));
|
||||||
user->owner().businessInfo().applyGreetingSettings(
|
user->owner().businessInfo().applyGreetingSettings(
|
||||||
FromMTP(&user->owner(), update.vbusiness_greeting_message()));
|
FromMTP(&user->owner(), update.vbusiness_greeting_message()));
|
||||||
|
user->owner().businessInfo().applyChatIntro(
|
||||||
|
FromMTP(&user->owner(), update.vbusiness_intro()));
|
||||||
}
|
}
|
||||||
|
|
||||||
user->owner().stories().apply(user, update.vstories());
|
user->owner().stories().apply(user, update.vstories());
|
||||||
|
|
|
@ -7,10 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "history/view/history_view_about_view.h"
|
#include "history/view/history_view_about_view.h"
|
||||||
|
|
||||||
|
#include "chat_helpers/stickers_lottie.h"
|
||||||
#include "core/click_handler_types.h"
|
#include "core/click_handler_types.h"
|
||||||
|
#include "data/business/data_business_common.h"
|
||||||
|
#include "data/data_document.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "history/view/media/history_view_service_box.h"
|
#include "history/view/media/history_view_service_box.h"
|
||||||
#include "history/view/media/history_view_sticker_player_abstract.h"
|
#include "history/view/media/history_view_sticker_player_abstract.h"
|
||||||
|
#include "history/view/media/history_view_sticker.h"
|
||||||
#include "history/view/history_view_element.h"
|
#include "history/view/history_view_element.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
|
@ -64,6 +69,43 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ChatIntroBox final : public ServiceBoxContent {
|
||||||
|
public:
|
||||||
|
ChatIntroBox(not_null<Element*> parent, Data::ChatIntro data);
|
||||||
|
~ChatIntroBox();
|
||||||
|
|
||||||
|
int width() override;
|
||||||
|
int top() override;
|
||||||
|
QSize size() override;
|
||||||
|
QString title() override;
|
||||||
|
TextWithEntities subtitle() override;
|
||||||
|
int buttonSkip() override;
|
||||||
|
rpl::producer<QString> button() override;
|
||||||
|
void draw(
|
||||||
|
Painter &p,
|
||||||
|
const PaintContext &context,
|
||||||
|
const QRect &geometry) override;
|
||||||
|
ClickHandlerPtr createViewLink() override;
|
||||||
|
|
||||||
|
bool hideServiceText() override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stickerClearLoopPlayed() override;
|
||||||
|
std::unique_ptr<StickerPlayer> stickerTakePlayer(
|
||||||
|
not_null<DocumentData*> data,
|
||||||
|
const Lottie::ColorReplacements *replacements) override;
|
||||||
|
|
||||||
|
bool hasHeavyPart() override;
|
||||||
|
void unloadHeavyPart() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const not_null<Element*> _parent;
|
||||||
|
const Data::ChatIntro _data;
|
||||||
|
mutable std::optional<Sticker> _sticker;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
PremiumRequiredBox::PremiumRequiredBox(not_null<Element*> parent)
|
PremiumRequiredBox::PremiumRequiredBox(not_null<Element*> parent)
|
||||||
: _parent(parent) {
|
: _parent(parent) {
|
||||||
}
|
}
|
||||||
|
@ -133,6 +175,99 @@ bool PremiumRequiredBox::hasHeavyPart() {
|
||||||
void PremiumRequiredBox::unloadHeavyPart() {
|
void PremiumRequiredBox::unloadHeavyPart() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChatIntroBox::ChatIntroBox(not_null<Element*> parent, Data::ChatIntro data)
|
||||||
|
: _parent(parent)
|
||||||
|
, _data(data) {
|
||||||
|
if (const auto document = data.sticker) {
|
||||||
|
if (const auto sticker = document->sticker()) {
|
||||||
|
const auto skipPremiumEffect = false;
|
||||||
|
_sticker.emplace(_parent, document, skipPremiumEffect, _parent);
|
||||||
|
_sticker->setDiceIndex(sticker->alt, 0);
|
||||||
|
_sticker->setGiftBoxSticker(true);
|
||||||
|
_sticker->initSize();
|
||||||
|
_sticker->setCustomEmojiPart(
|
||||||
|
st::chatIntroStickerSize,
|
||||||
|
ChatHelpers::StickerLottieSize::ChatIntroHelloSticker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatIntroBox::~ChatIntroBox() = default;
|
||||||
|
|
||||||
|
int ChatIntroBox::width() {
|
||||||
|
return st::chatIntroWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ChatIntroBox::top() {
|
||||||
|
return st::msgServiceGiftBoxButtonMargins.top();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize ChatIntroBox::size() {
|
||||||
|
return { st::msgServicePhotoWidth, st::msgServicePhotoWidth };
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatIntroBox::title() {
|
||||||
|
return _data ? _data.title : tr::lng_chat_intro_default_title(tr::now);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ChatIntroBox::buttonSkip() {
|
||||||
|
return st::storyMentionButtonSkip;
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<QString> ChatIntroBox::button() {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextWithEntities ChatIntroBox::subtitle() {
|
||||||
|
return {
|
||||||
|
(_data
|
||||||
|
? _data.description
|
||||||
|
: tr::lng_chat_intro_default_message(tr::now))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ClickHandlerPtr ChatIntroBox::createViewLink() {
|
||||||
|
return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
|
||||||
|
const auto my = context.other.value<ClickHandlerContext>();
|
||||||
|
if (const auto controller = my.sessionWindow.get()) {
|
||||||
|
Settings::ShowPremium(controller, u"require_premium"_q);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatIntroBox::draw(
|
||||||
|
Painter &p,
|
||||||
|
const PaintContext &context,
|
||||||
|
const QRect &geometry) {
|
||||||
|
if (_sticker) {
|
||||||
|
_sticker->draw(p, context, geometry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatIntroBox::stickerClearLoopPlayed() {
|
||||||
|
if (_sticker) {
|
||||||
|
_sticker->stickerClearLoopPlayed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<StickerPlayer> ChatIntroBox::stickerTakePlayer(
|
||||||
|
not_null<DocumentData*> data,
|
||||||
|
const Lottie::ColorReplacements *replacements) {
|
||||||
|
return _sticker
|
||||||
|
? _sticker->stickerTakePlayer(data, replacements)
|
||||||
|
: nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatIntroBox::hasHeavyPart() {
|
||||||
|
return _sticker && _sticker->hasHeavyPart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatIntroBox::unloadHeavyPart() {
|
||||||
|
if (_sticker) {
|
||||||
|
_sticker->unloadHeavyPart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
AboutView::AboutView(
|
AboutView::AboutView(
|
||||||
|
@ -142,6 +277,10 @@ AboutView::AboutView(
|
||||||
, _delegate(delegate) {
|
, _delegate(delegate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AboutView::~AboutView() {
|
||||||
|
setItem({}, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
not_null<History*> AboutView::history() const {
|
not_null<History*> AboutView::history() const {
|
||||||
return _history;
|
return _history;
|
||||||
}
|
}
|
||||||
|
@ -187,6 +326,37 @@ bool AboutView::refresh() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AboutView::make(Data::ChatIntro data) {
|
||||||
|
const auto item = _history->makeMessage({
|
||||||
|
.id = _history->nextNonHistoryEntryId(),
|
||||||
|
.flags = (MessageFlag::FakeAboutView
|
||||||
|
| MessageFlag::FakeHistoryItem
|
||||||
|
| MessageFlag::Local),
|
||||||
|
.from = _history->peer->id,
|
||||||
|
}, PreparedServiceText{ { data.description } });
|
||||||
|
|
||||||
|
setItem(AdminLog::OwnedItem(_delegate, item), data.sticker);
|
||||||
|
|
||||||
|
_item->overrideMedia(std::make_unique<ServiceBox>(
|
||||||
|
_item.get(),
|
||||||
|
std::make_unique<ChatIntroBox>(_item.get(), data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AboutView::setItem(AdminLog::OwnedItem item, DocumentData *sticker) {
|
||||||
|
if (const auto was = _item ? _item->data().get() : nullptr) {
|
||||||
|
if (_sticker) {
|
||||||
|
was->history()->owner().unregisterDocumentItem(_sticker, was);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_item = std::move(item);
|
||||||
|
_sticker = sticker;
|
||||||
|
if (const auto now = _item ? _item->data().get() : nullptr) {
|
||||||
|
if (_sticker) {
|
||||||
|
now->history()->owner().registerDocumentItem(_sticker, now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AdminLog::OwnedItem AboutView::makeAboutBot(not_null<BotInfo*> info) {
|
AdminLog::OwnedItem AboutView::makeAboutBot(not_null<BotInfo*> info) {
|
||||||
const auto textWithEntities = TextUtilities::ParseEntities(
|
const auto textWithEntities = TextUtilities::ParseEntities(
|
||||||
info->description,
|
info->description,
|
||||||
|
|
|
@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "history/admin_log/history_admin_log_item.h"
|
#include "history/admin_log/history_admin_log_item.h"
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
struct ChatIntro;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
|
|
||||||
class AboutView final : public ClickHandlerHost {
|
class AboutView final : public ClickHandlerHost {
|
||||||
|
@ -16,6 +20,7 @@ public:
|
||||||
AboutView(
|
AboutView(
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
not_null<ElementDelegate*> delegate);
|
not_null<ElementDelegate*> delegate);
|
||||||
|
~AboutView();
|
||||||
|
|
||||||
[[nodiscard]] not_null<History*> history() const;
|
[[nodiscard]] not_null<History*> history() const;
|
||||||
[[nodiscard]] Element *view() const;
|
[[nodiscard]] Element *view() const;
|
||||||
|
@ -23,16 +28,20 @@ public:
|
||||||
|
|
||||||
bool refresh();
|
bool refresh();
|
||||||
|
|
||||||
|
void make(Data::ChatIntro data);
|
||||||
|
|
||||||
int top = 0;
|
int top = 0;
|
||||||
int height = 0;
|
int height = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] AdminLog::OwnedItem makeAboutBot(not_null<BotInfo*> info);
|
[[nodiscard]] AdminLog::OwnedItem makeAboutBot(not_null<BotInfo*> info);
|
||||||
[[nodiscard]] AdminLog::OwnedItem makePremiumRequired();
|
[[nodiscard]] AdminLog::OwnedItem makePremiumRequired();
|
||||||
|
void setItem(AdminLog::OwnedItem item, DocumentData *sticker);
|
||||||
|
|
||||||
const not_null<History*> _history;
|
const not_null<History*> _history;
|
||||||
const not_null<ElementDelegate*> _delegate;
|
const not_null<ElementDelegate*> _delegate;
|
||||||
AdminLog::OwnedItem _item;
|
AdminLog::OwnedItem _item;
|
||||||
|
DocumentData *_sticker = nullptr;
|
||||||
int _version = 0;
|
int _version = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -884,6 +884,9 @@ BusinessBotStatus::Bar::Bar(QWidget *parent)
|
||||||
, _settings(this, st::historyBusinessBotSettings) {
|
, _settings(this, st::historyBusinessBotSettings) {
|
||||||
_name->setAttribute(Qt::WA_TransparentForMouseEvents);
|
_name->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
_status->setAttribute(Qt::WA_TransparentForMouseEvents);
|
_status->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
_togglePaused->setFullRadius(true);
|
||||||
|
_togglePaused->setTextTransform(
|
||||||
|
Ui::RoundButton::TextTransform::NoTransform);
|
||||||
_settings->setClickedCallback([=] {
|
_settings->setClickedCallback([=] {
|
||||||
showMenu();
|
showMenu();
|
||||||
});
|
});
|
||||||
|
@ -984,7 +987,9 @@ int BusinessBotStatus::Bar::resizeGetHeight(int newWidth) {
|
||||||
}
|
}
|
||||||
auto available = newWidth - _settings->width() - st.namePosition.x();
|
auto available = newWidth - _settings->width() - st.namePosition.x();
|
||||||
if (!_togglePaused->isHidden()) {
|
if (!_togglePaused->isHidden()) {
|
||||||
_togglePaused->moveToRight(_settings->width(), 0);
|
_togglePaused->moveToRight(
|
||||||
|
_settings->width(),
|
||||||
|
(st.height - _togglePaused->height()) / 2);
|
||||||
available -= _togglePaused->width();
|
available -= _togglePaused->width();
|
||||||
}
|
}
|
||||||
_name->resizeToWidth(available);
|
_name->resizeToWidth(available);
|
||||||
|
|
|
@ -114,7 +114,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
|
||||||
chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto;
|
chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto;
|
||||||
|
|
||||||
messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message;
|
messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message;
|
||||||
message#2357bf25 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int = Message;
|
message#2357bf25 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# offline:flags2.1?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int = Message;
|
||||||
messageService#2b085862 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction ttl_period:flags.25?int = Message;
|
messageService#2b085862 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction ttl_period:flags.25?int = Message;
|
||||||
|
|
||||||
messageMediaEmpty#3ded6320 = MessageMedia;
|
messageMediaEmpty#3ded6320 = MessageMedia;
|
||||||
|
@ -227,7 +227,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason;
|
||||||
inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
|
inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
|
||||||
inputReportReasonPersonalDetails#9ec7863d = ReportReason;
|
inputReportReasonPersonalDetails#9ec7863d = ReportReason;
|
||||||
|
|
||||||
userFull#670bbc9c flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro = UserFull;
|
userFull#ecdadceb flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday = UserFull;
|
||||||
|
|
||||||
contact#145ade0b user_id:long mutual:Bool = Contact;
|
contact#145ade0b user_id:long mutual:Bool = Contact;
|
||||||
|
|
||||||
|
@ -517,6 +517,7 @@ inputPrivacyKeyPhoneNumber#352dafa = InputPrivacyKey;
|
||||||
inputPrivacyKeyAddedByPhone#d1219bdd = InputPrivacyKey;
|
inputPrivacyKeyAddedByPhone#d1219bdd = InputPrivacyKey;
|
||||||
inputPrivacyKeyVoiceMessages#aee69d68 = InputPrivacyKey;
|
inputPrivacyKeyVoiceMessages#aee69d68 = InputPrivacyKey;
|
||||||
inputPrivacyKeyAbout#3823cc40 = InputPrivacyKey;
|
inputPrivacyKeyAbout#3823cc40 = InputPrivacyKey;
|
||||||
|
inputPrivacyKeyBirthday#d65a11cc = InputPrivacyKey;
|
||||||
|
|
||||||
privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey;
|
privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey;
|
||||||
privacyKeyChatInvite#500e6dfa = PrivacyKey;
|
privacyKeyChatInvite#500e6dfa = PrivacyKey;
|
||||||
|
@ -528,6 +529,7 @@ privacyKeyPhoneNumber#d19ae46d = PrivacyKey;
|
||||||
privacyKeyAddedByPhone#42ffd42b = PrivacyKey;
|
privacyKeyAddedByPhone#42ffd42b = PrivacyKey;
|
||||||
privacyKeyVoiceMessages#697f414 = PrivacyKey;
|
privacyKeyVoiceMessages#697f414 = PrivacyKey;
|
||||||
privacyKeyAbout#a486b761 = PrivacyKey;
|
privacyKeyAbout#a486b761 = PrivacyKey;
|
||||||
|
privacyKeyBirthday#2000a518 = PrivacyKey;
|
||||||
|
|
||||||
inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule;
|
inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule;
|
||||||
inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule;
|
inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule;
|
||||||
|
@ -1710,6 +1712,8 @@ account.connectedBots#17d7f87b connected_bots:Vector<ConnectedBot> users:Vector<
|
||||||
|
|
||||||
messages.dialogFilters#2ad93719 flags:# tags_enabled:flags.0?true filters:Vector<DialogFilter> = messages.DialogFilters;
|
messages.dialogFilters#2ad93719 flags:# tags_enabled:flags.0?true filters:Vector<DialogFilter> = messages.DialogFilters;
|
||||||
|
|
||||||
|
birthday#6c8e1e06 flags:# day:int month:int year:flags.0?int = Birthday;
|
||||||
|
|
||||||
botBusinessConnection#896433b4 flags:# can_reply:flags.0?true disabled:flags.1?true connection_id:string user_id:long dc_id:int date:int = BotBusinessConnection;
|
botBusinessConnection#896433b4 flags:# can_reply:flags.0?true disabled:flags.1?true connection_id:string user_id:long dc_id:int date:int = BotBusinessConnection;
|
||||||
|
|
||||||
inputBusinessIntro#9c469cd flags:# title:string description:string sticker:flags.0?InputDocument = InputBusinessIntro;
|
inputBusinessIntro#9c469cd flags:# title:string description:string sticker:flags.0?InputDocument = InputBusinessIntro;
|
||||||
|
@ -1727,6 +1731,10 @@ inputBusinessBotRecipients#c4e5921e flags:# existing_chats:flags.0?true new_chat
|
||||||
|
|
||||||
businessBotRecipients#b88cf373 flags:# existing_chats:flags.0?true new_chats:flags.1?true contacts:flags.2?true non_contacts:flags.3?true exclude_selected:flags.5?true users:flags.4?Vector<long> exclude_users:flags.6?Vector<long> = BusinessBotRecipients;
|
businessBotRecipients#b88cf373 flags:# existing_chats:flags.0?true new_chats:flags.1?true contacts:flags.2?true non_contacts:flags.3?true exclude_selected:flags.5?true users:flags.4?Vector<long> exclude_users:flags.6?Vector<long> = BusinessBotRecipients;
|
||||||
|
|
||||||
|
contactBirthday#1d998733 contact_id:long birthday:Birthday = ContactBirthday;
|
||||||
|
|
||||||
|
contacts.contactBirthdays#114ff30d contacts:Vector<ContactBirthday> users:Vector<User> = contacts.ContactBirthdays;
|
||||||
|
|
||||||
---functions---
|
---functions---
|
||||||
|
|
||||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||||
|
@ -1863,6 +1871,7 @@ account.getBotBusinessConnection#76a86270 connection_id:string = Updates;
|
||||||
account.updateBusinessIntro#a614d034 flags:# intro:flags.0?InputBusinessIntro = Bool;
|
account.updateBusinessIntro#a614d034 flags:# intro:flags.0?InputBusinessIntro = Bool;
|
||||||
account.toggleConnectedBotPaused#646e1097 peer:InputPeer paused:Bool = Bool;
|
account.toggleConnectedBotPaused#646e1097 peer:InputPeer paused:Bool = Bool;
|
||||||
account.disablePeerConnectedBot#5e437ed9 peer:InputPeer = Bool;
|
account.disablePeerConnectedBot#5e437ed9 peer:InputPeer = Bool;
|
||||||
|
account.updateBirthday#cc6e0c11 flags:# birthday:flags.0?Birthday = Bool;
|
||||||
|
|
||||||
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
||||||
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
|
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
|
||||||
|
@ -1894,6 +1903,7 @@ contacts.exportContactToken#f8654027 = ExportedContactToken;
|
||||||
contacts.importContactToken#13005788 token:string = User;
|
contacts.importContactToken#13005788 token:string = User;
|
||||||
contacts.editCloseFriends#ba6705f0 id:Vector<long> = Bool;
|
contacts.editCloseFriends#ba6705f0 id:Vector<long> = Bool;
|
||||||
contacts.setBlocked#94c65c76 flags:# my_stories_from:flags.0?true id:Vector<InputPeer> limit:int = Bool;
|
contacts.setBlocked#94c65c76 flags:# my_stories_from:flags.0?true id:Vector<InputPeer> limit:int = Bool;
|
||||||
|
contacts.getBirthdays#daeda864 = contacts.ContactBirthdays;
|
||||||
|
|
||||||
messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
|
messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
|
||||||
messages.getDialogs#a0f4cb4f flags:# exclude_pinned:flags.0?true folder_id:flags.1?int offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:long = messages.Dialogs;
|
messages.getDialogs#a0f4cb4f flags:# exclude_pinned:flags.0?true folder_id:flags.1?int offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:long = messages.Dialogs;
|
||||||
|
|
|
@ -31,7 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace Settings {
|
namespace Settings {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class AwayMessage : public BusinessSection<AwayMessage> {
|
class AwayMessage final : public BusinessSection<AwayMessage> {
|
||||||
public:
|
public:
|
||||||
AwayMessage(
|
AwayMessage(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
|
|
564
Telegram/SourceFiles/settings/business/settings_chat_intro.cpp
Normal file
564
Telegram/SourceFiles/settings/business/settings_chat_intro.cpp
Normal file
|
@ -0,0 +1,564 @@
|
||||||
|
/*
|
||||||
|
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_chat_intro.h"
|
||||||
|
|
||||||
|
#include "api/api_premium.h"
|
||||||
|
#include "boxes/peers/edit_peer_color_box.h" // ButtonStyleWithRightEmoji
|
||||||
|
#include "chat_helpers/tabbed_panel.h"
|
||||||
|
#include "chat_helpers/tabbed_selector.h"
|
||||||
|
#include "core/application.h"
|
||||||
|
#include "data/business/data_business_info.h"
|
||||||
|
#include "data/data_document.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "history/view/history_view_about_view.h"
|
||||||
|
#include "history/view/history_view_element.h"
|
||||||
|
#include "history/history.h"
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
#include "settings/business/settings_recipients_helper.h"
|
||||||
|
#include "ui/chat/chat_style.h"
|
||||||
|
#include "ui/chat/chat_theme.h"
|
||||||
|
#include "ui/effects/path_shift_gradient.h"
|
||||||
|
#include "ui/text/text_utilities.h"
|
||||||
|
#include "ui/widgets/fields/input_field.h"
|
||||||
|
#include "ui/widgets/buttons.h"
|
||||||
|
#include "ui/wrap/vertical_layout.h"
|
||||||
|
#include "ui/wrap/slide_wrap.h"
|
||||||
|
#include "ui/painter.h"
|
||||||
|
#include "ui/vertical_list.h"
|
||||||
|
#include "window/themes/window_theme.h"
|
||||||
|
#include "window/section_widget.h"
|
||||||
|
#include "window/window_controller.h"
|
||||||
|
#include "window/window_session_controller.h"
|
||||||
|
#include "styles/style_chat.h"
|
||||||
|
#include "styles/style_chat_helpers.h"
|
||||||
|
#include "styles/style_layers.h"
|
||||||
|
#include "styles/style_settings.h"
|
||||||
|
|
||||||
|
namespace Settings {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using namespace HistoryView;
|
||||||
|
|
||||||
|
class PreviewDelegate final : public DefaultElementDelegate {
|
||||||
|
public:
|
||||||
|
PreviewDelegate(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
not_null<Ui::ChatStyle*> st,
|
||||||
|
Fn<void()> update);
|
||||||
|
|
||||||
|
bool elementAnimationsPaused() override;
|
||||||
|
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
|
||||||
|
Context elementContext() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const not_null<QWidget*> _parent;
|
||||||
|
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class PreviewWrap final : public Ui::RpWidget {
|
||||||
|
public:
|
||||||
|
PreviewWrap(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
rpl::producer<Data::ChatIntro> value);
|
||||||
|
~PreviewWrap();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
||||||
|
void resizeTo(int width);
|
||||||
|
void prepare(rpl::producer<Data::ChatIntro> value);
|
||||||
|
|
||||||
|
const not_null<History*> _history;
|
||||||
|
const std::unique_ptr<Ui::ChatTheme> _theme;
|
||||||
|
const std::unique_ptr<Ui::ChatStyle> _style;
|
||||||
|
const std::unique_ptr<PreviewDelegate> _delegate;
|
||||||
|
|
||||||
|
std::unique_ptr<AboutView> _view;
|
||||||
|
QPoint _position;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class StickerPanel final {
|
||||||
|
public:
|
||||||
|
StickerPanel();
|
||||||
|
~StickerPanel();
|
||||||
|
|
||||||
|
struct Descriptor {
|
||||||
|
not_null<Window::SessionController*> controller;
|
||||||
|
not_null<QWidget*> button;
|
||||||
|
DocumentId ensureAddedId = 0;
|
||||||
|
};
|
||||||
|
void show(Descriptor &&descriptor);
|
||||||
|
void repaint();
|
||||||
|
|
||||||
|
[[nodiscard]] bool hasFocus() const;
|
||||||
|
|
||||||
|
struct CustomChosen {
|
||||||
|
not_null<DocumentData*> sticker;
|
||||||
|
};
|
||||||
|
[[nodiscard]] rpl::producer<CustomChosen> someCustomChosen() const {
|
||||||
|
return _someCustomChosen.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void create(const Descriptor &descriptor);
|
||||||
|
|
||||||
|
base::unique_qptr<ChatHelpers::TabbedPanel> _panel;
|
||||||
|
QPointer<QWidget> _panelButton;
|
||||||
|
rpl::event_stream<CustomChosen> _someCustomChosen;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class ChatIntro final : public BusinessSection<ChatIntro> {
|
||||||
|
public:
|
||||||
|
ChatIntro(
|
||||||
|
QWidget *parent,
|
||||||
|
not_null<Window::SessionController*> controller);
|
||||||
|
~ChatIntro();
|
||||||
|
|
||||||
|
[[nodiscard]] bool closeByOutsideClick() const override;
|
||||||
|
[[nodiscard]] rpl::producer<QString> title() override;
|
||||||
|
|
||||||
|
void setInnerFocus() override {
|
||||||
|
_setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setupContent(not_null<Window::SessionController*> controller);
|
||||||
|
void save();
|
||||||
|
|
||||||
|
Fn<void()> _setFocus;
|
||||||
|
|
||||||
|
rpl::variable<Data::ChatIntro> _intro;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] object_ptr<Ui::SettingsButton> CreateIntroStickerButton(
|
||||||
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
|
rpl::producer<DocumentData*> stickerValue,
|
||||||
|
Fn<void(DocumentData*)> stickerChosen) {
|
||||||
|
const auto button = ButtonStyleWithRightEmoji(
|
||||||
|
parent,
|
||||||
|
tr::lng_chat_intro_random_sticker(tr::now),
|
||||||
|
st::settingsButtonNoIcon);
|
||||||
|
auto result = Settings::CreateButtonWithIcon(
|
||||||
|
parent,
|
||||||
|
tr::lng_chat_intro_choose_sticker(),
|
||||||
|
*button.st);
|
||||||
|
const auto raw = result.data();
|
||||||
|
|
||||||
|
const auto right = Ui::CreateChild<Ui::RpWidget>(raw);
|
||||||
|
right->show();
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
StickerPanel panel;
|
||||||
|
DocumentId stickerId = 0;
|
||||||
|
};
|
||||||
|
const auto state = right->lifetime().make_state<State>();
|
||||||
|
state->panel.someCustomChosen(
|
||||||
|
) | rpl::start_with_next([=](StickerPanel::CustomChosen chosen) {
|
||||||
|
stickerChosen(chosen.sticker);
|
||||||
|
}, raw->lifetime());
|
||||||
|
|
||||||
|
const auto session = &show->session();
|
||||||
|
std::move(
|
||||||
|
stickerValue
|
||||||
|
) | rpl::start_with_next([=](DocumentData *sticker) {
|
||||||
|
state->stickerId = sticker ? sticker->id : 0;
|
||||||
|
right->resize(
|
||||||
|
(sticker ? button.emojiWidth : button.noneWidth) + button.added,
|
||||||
|
right->height());
|
||||||
|
right->update();
|
||||||
|
}, right->lifetime());
|
||||||
|
|
||||||
|
rpl::combine(
|
||||||
|
raw->sizeValue(),
|
||||||
|
right->widthValue()
|
||||||
|
) | rpl::start_with_next([=](QSize outer, int width) {
|
||||||
|
right->resize(width, outer.height());
|
||||||
|
const auto skip = st::settingsButton.padding.right();
|
||||||
|
right->moveToRight(skip - button.added, 0, outer.width());
|
||||||
|
}, right->lifetime());
|
||||||
|
|
||||||
|
right->paintRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
auto p = QPainter(right);
|
||||||
|
const auto height = right->height();
|
||||||
|
if (false) {
|
||||||
|
// #TODO paint small sticker
|
||||||
|
} else {
|
||||||
|
const auto &font = st::normalFont;
|
||||||
|
p.setFont(font);
|
||||||
|
p.setPen(st::windowActiveTextFg);
|
||||||
|
p.drawText(
|
||||||
|
QPoint(
|
||||||
|
button.added,
|
||||||
|
(height - font->height) / 2 + font->ascent),
|
||||||
|
tr::lng_chat_intro_random_sticker(tr::now));
|
||||||
|
}
|
||||||
|
}, right->lifetime());
|
||||||
|
|
||||||
|
raw->setClickedCallback([=] {
|
||||||
|
const auto controller = show->resolveWindow(
|
||||||
|
ChatHelpers::WindowUsage::PremiumPromo);
|
||||||
|
if (controller) {
|
||||||
|
state->panel.show({
|
||||||
|
.controller = controller,
|
||||||
|
.button = right,
|
||||||
|
.ensureAddedId = state->stickerId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
PreviewDelegate::PreviewDelegate(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
not_null<Ui::ChatStyle*> st,
|
||||||
|
Fn<void()> update)
|
||||||
|
: _parent(parent)
|
||||||
|
, _pathGradient(MakePathShiftGradient(st, update)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PreviewDelegate::elementAnimationsPaused() {
|
||||||
|
return _parent->window()->isActiveWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PreviewDelegate::elementPathShiftGradient()
|
||||||
|
-> not_null<Ui::PathShiftGradient*> {
|
||||||
|
return _pathGradient.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Context PreviewDelegate::elementContext() {
|
||||||
|
return Context::History;
|
||||||
|
}
|
||||||
|
|
||||||
|
PreviewWrap::PreviewWrap(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
rpl::producer<Data::ChatIntro> value)
|
||||||
|
: RpWidget(parent)
|
||||||
|
, _history(session->data().history(session->userPeerId()))
|
||||||
|
, _theme(Window::Theme::DefaultChatThemeOn(lifetime()))
|
||||||
|
, _style(std::make_unique<Ui::ChatStyle>(
|
||||||
|
_history->session().colorIndicesValue()))
|
||||||
|
, _delegate(std::make_unique<PreviewDelegate>(
|
||||||
|
parent,
|
||||||
|
_style.get(),
|
||||||
|
[=] { update(); }))
|
||||||
|
, _position(0, st::msgMargin.bottom()) {
|
||||||
|
_style->apply(_theme.get());
|
||||||
|
|
||||||
|
session->data().viewRepaintRequest(
|
||||||
|
) | rpl::start_with_next([=](not_null<const Element*> view) {
|
||||||
|
if (view == _view->view()) {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
prepare(std::move(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
PreviewWrap::~PreviewWrap() {
|
||||||
|
_view = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PreviewWrap::prepare(rpl::producer<Data::ChatIntro> value) {
|
||||||
|
_view = std::make_unique<AboutView>(
|
||||||
|
_history.get(),
|
||||||
|
_delegate.get());
|
||||||
|
|
||||||
|
std::move(value) | rpl::start_with_next([=](Data::ChatIntro intro) {
|
||||||
|
_view->make(std::move(intro));
|
||||||
|
if (width() >= st::msgMinWidth) {
|
||||||
|
resizeTo(width());
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
widthValue(
|
||||||
|
) | rpl::filter([=](int width) {
|
||||||
|
return width >= st::msgMinWidth;
|
||||||
|
}) | rpl::start_with_next([=](int width) {
|
||||||
|
resizeTo(width);
|
||||||
|
}, lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PreviewWrap::resizeTo(int width) {
|
||||||
|
const auto height = _position.y()
|
||||||
|
+ _view->view()->resizeGetHeight(width)
|
||||||
|
+ _position.y()
|
||||||
|
+ st::msgServiceMargin.top()
|
||||||
|
+ st::msgServiceGiftBoxTopSkip
|
||||||
|
- st::msgServiceMargin.bottom();
|
||||||
|
resize(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PreviewWrap::paintEvent(QPaintEvent *e) {
|
||||||
|
auto p = Painter(this);
|
||||||
|
|
||||||
|
const auto clip = e->rect();
|
||||||
|
if (!clip.isEmpty()) {
|
||||||
|
p.setClipRect(clip);
|
||||||
|
Window::SectionWidget::PaintBackground(
|
||||||
|
p,
|
||||||
|
_theme.get(),
|
||||||
|
QSize(width(), window()->height()),
|
||||||
|
clip);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto context = _theme->preparePaintContext(
|
||||||
|
_style.get(),
|
||||||
|
rect(),
|
||||||
|
e->rect(),
|
||||||
|
!window()->isActiveWindow());
|
||||||
|
p.translate(_position);
|
||||||
|
_view->view()->draw(p, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
StickerPanel::StickerPanel() = default;
|
||||||
|
|
||||||
|
StickerPanel::~StickerPanel() = default;
|
||||||
|
|
||||||
|
void StickerPanel::show(Descriptor &&descriptor) {
|
||||||
|
const auto controller = descriptor.controller;
|
||||||
|
if (!_panel) {
|
||||||
|
create(descriptor);
|
||||||
|
|
||||||
|
_panel->shownValue(
|
||||||
|
) | rpl::filter([=] {
|
||||||
|
return (_panelButton != nullptr);
|
||||||
|
}) | rpl::start_with_next([=](bool shown) {
|
||||||
|
if (shown) {
|
||||||
|
_panelButton->installEventFilter(_panel.get());
|
||||||
|
} else {
|
||||||
|
_panelButton->removeEventFilter(_panel.get());
|
||||||
|
}
|
||||||
|
}, _panel->lifetime());
|
||||||
|
}
|
||||||
|
const auto button = descriptor.button;
|
||||||
|
if (const auto previous = _panelButton.data()) {
|
||||||
|
if (previous != button) {
|
||||||
|
previous->removeEventFilter(_panel.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_panelButton = button;
|
||||||
|
const auto feed = [=, now = descriptor.ensureAddedId](
|
||||||
|
std::vector<DocumentId> list) {
|
||||||
|
list.insert(begin(list), 0);
|
||||||
|
if (now && !ranges::contains(list, now)) {
|
||||||
|
list.push_back(now);
|
||||||
|
}
|
||||||
|
_panel->selector()->provideRecentEmoji(list);
|
||||||
|
};
|
||||||
|
const auto parent = _panel->parentWidget();
|
||||||
|
const auto global = button->mapToGlobal(QPoint());
|
||||||
|
const auto local = parent->mapFromGlobal(global);
|
||||||
|
_panel->moveBottomRight(
|
||||||
|
local.y() + (st::normalFont->height / 2),
|
||||||
|
local.x() + button->width() * 3);
|
||||||
|
_panel->toggleAnimated();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StickerPanel::hasFocus() const {
|
||||||
|
return _panel && Ui::InFocusChain(_panel.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickerPanel::repaint() {
|
||||||
|
_panel->selector()->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickerPanel::create(const Descriptor &descriptor) {
|
||||||
|
using Selector = ChatHelpers::TabbedSelector;
|
||||||
|
using Descriptor = ChatHelpers::TabbedSelectorDescriptor;
|
||||||
|
using Mode = ChatHelpers::TabbedSelector::Mode;
|
||||||
|
const auto controller = descriptor.controller;
|
||||||
|
const auto body = controller->window().widget()->bodyWidget();
|
||||||
|
_panel = base::make_unique_q<ChatHelpers::TabbedPanel>(
|
||||||
|
body,
|
||||||
|
controller,
|
||||||
|
object_ptr<Selector>(
|
||||||
|
nullptr,
|
||||||
|
Descriptor{
|
||||||
|
.show = controller->uiShow(),
|
||||||
|
.st = st::backgroundEmojiPan,
|
||||||
|
.level = Window::GifPauseReason::Layer,
|
||||||
|
.mode = Mode::StickersOnly,
|
||||||
|
.features = {
|
||||||
|
.megagroupSet = false,
|
||||||
|
.stickersSettings = false,
|
||||||
|
.openStickerSets = false,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
_panel->setDropDown(false);
|
||||||
|
_panel->setDesiredHeightValues(
|
||||||
|
1.,
|
||||||
|
st::emojiPanMinHeight / 2,
|
||||||
|
st::emojiPanMinHeight);
|
||||||
|
_panel->hide();
|
||||||
|
|
||||||
|
_panel->selector()->fileChosen(
|
||||||
|
) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
|
||||||
|
_someCustomChosen.fire({ data.document });
|
||||||
|
_panel->hideAnimated();
|
||||||
|
}, _panel->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatIntro::ChatIntro(
|
||||||
|
QWidget *parent,
|
||||||
|
not_null<Window::SessionController*> controller)
|
||||||
|
: BusinessSection(parent, controller) {
|
||||||
|
setupContent(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatIntro::~ChatIntro() {
|
||||||
|
if (!Core::Quitting()) {
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatIntro::closeByOutsideClick() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<QString> ChatIntro::title() {
|
||||||
|
return tr::lng_chat_intro_title();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<Data::ChatIntro> IntroWithRandomSticker(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
rpl::producer<Data::ChatIntro> intro) {
|
||||||
|
return std::move(intro) | rpl::map([=](Data::ChatIntro intro)
|
||||||
|
-> rpl::producer<Data::ChatIntro> {
|
||||||
|
if (intro.sticker) {
|
||||||
|
return rpl::single(std::move(intro));
|
||||||
|
}
|
||||||
|
return Api::RandomHelloStickerValue(
|
||||||
|
session
|
||||||
|
) | rpl::map([=](DocumentData *sticker) {
|
||||||
|
auto copy = intro;
|
||||||
|
copy.sticker = sticker;
|
||||||
|
return copy;
|
||||||
|
});
|
||||||
|
}) | rpl::flatten_latest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatIntro::setupContent(
|
||||||
|
not_null<Window::SessionController*> controller) {
|
||||||
|
using namespace rpl::mappers;
|
||||||
|
|
||||||
|
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
|
||||||
|
const auto info = &controller->session().data().businessInfo();
|
||||||
|
const auto current = info->chatIntro();
|
||||||
|
|
||||||
|
_intro = info->chatIntro();
|
||||||
|
const auto change = [=](Fn<void(Data::ChatIntro &)> modify) {
|
||||||
|
auto intro = _intro.current();
|
||||||
|
modify(intro);
|
||||||
|
_intro = intro;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto preview = content->add(
|
||||||
|
object_ptr<PreviewWrap>(
|
||||||
|
content,
|
||||||
|
&controller->session(),
|
||||||
|
IntroWithRandomSticker(&controller->session(), _intro.value())),
|
||||||
|
{});
|
||||||
|
|
||||||
|
const auto title = content->add(
|
||||||
|
object_ptr<Ui::InputField>(
|
||||||
|
content,
|
||||||
|
st::settingsChatIntroField,
|
||||||
|
tr::lng_chat_intro_enter_title(),
|
||||||
|
current.title),
|
||||||
|
st::settingsChatIntroFieldMargins);
|
||||||
|
const auto description = content->add(
|
||||||
|
object_ptr<Ui::InputField>(
|
||||||
|
content,
|
||||||
|
st::settingsChatIntroField,
|
||||||
|
tr::lng_chat_intro_enter_message(),
|
||||||
|
current.description),
|
||||||
|
st::settingsChatIntroFieldMargins);
|
||||||
|
content->add(CreateIntroStickerButton(
|
||||||
|
content,
|
||||||
|
controller->uiShow(),
|
||||||
|
_intro.value() | rpl::map([](const Data::ChatIntro &intro) {
|
||||||
|
return intro.sticker;
|
||||||
|
}) | rpl::distinct_until_changed(),
|
||||||
|
[=](DocumentData *sticker) {
|
||||||
|
change([&](Data::ChatIntro &intro) {
|
||||||
|
intro.sticker = sticker;
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
Ui::AddSkip(content);
|
||||||
|
|
||||||
|
title->changes() | rpl::start_with_next([=] {
|
||||||
|
change([&](Data::ChatIntro &intro) {
|
||||||
|
intro.title = title->getLastText();
|
||||||
|
});
|
||||||
|
}, title->lifetime());
|
||||||
|
|
||||||
|
description->changes() | rpl::start_with_next([=] {
|
||||||
|
change([&](Data::ChatIntro &intro) {
|
||||||
|
intro.description = description->getLastText();
|
||||||
|
});
|
||||||
|
}, description->lifetime());
|
||||||
|
|
||||||
|
_setFocus = [=] {
|
||||||
|
title->setFocusFast();
|
||||||
|
};
|
||||||
|
|
||||||
|
Ui::AddDividerText(
|
||||||
|
content,
|
||||||
|
tr::lng_chat_intro_about(),
|
||||||
|
st::peerAppearanceDividerTextMargin);
|
||||||
|
Ui::AddSkip(content);
|
||||||
|
|
||||||
|
const auto resetWrap = content->add(
|
||||||
|
object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
|
||||||
|
content,
|
||||||
|
object_ptr<Ui::SettingsButton>(
|
||||||
|
content,
|
||||||
|
tr::lng_chat_intro_reset(),
|
||||||
|
st::settingsAttentionButton
|
||||||
|
)));
|
||||||
|
resetWrap->toggleOn(
|
||||||
|
_intro.value() | rpl::map([](const Data::ChatIntro &intro) {
|
||||||
|
return !!intro;
|
||||||
|
}));
|
||||||
|
resetWrap->entity()->setClickedCallback([=] {
|
||||||
|
_intro = Data::ChatIntro();
|
||||||
|
});
|
||||||
|
|
||||||
|
Ui::ResizeFitChild(this, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatIntro::save() {
|
||||||
|
const auto show = controller()->uiShow();
|
||||||
|
const auto fail = [=](QString error) {
|
||||||
|
if (error == u"BUSINESS_RECIPIENTS_EMPTY"_q) {
|
||||||
|
show->showToast(tr::lng_greeting_recipients_empty(tr::now));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
controller()->session().data().businessInfo().saveChatIntro(
|
||||||
|
_intro.current(),
|
||||||
|
fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Type ChatIntroId() {
|
||||||
|
return ChatIntro::Id();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Settings
|
16
Telegram/SourceFiles/settings/business/settings_chat_intro.h
Normal file
16
Telegram/SourceFiles/settings/business/settings_chat_intro.h
Normal file
|
@ -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 ChatIntroId();
|
||||||
|
|
||||||
|
} // namespace Settings
|
|
@ -46,7 +46,7 @@ struct BotState {
|
||||||
LookupState state = LookupState::Empty;
|
LookupState state = LookupState::Empty;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Chatbots : public BusinessSection<Chatbots> {
|
class Chatbots final : public BusinessSection<Chatbots> {
|
||||||
public:
|
public:
|
||||||
Chatbots(
|
Chatbots(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
|
|
|
@ -110,6 +110,7 @@ settingsBusinessIconReplies: icon {{ "settings/premium/business/business_quick",
|
||||||
settingsBusinessIconGreeting: icon {{ "settings/premium/status", settingsIconFg }};
|
settingsBusinessIconGreeting: icon {{ "settings/premium/status", settingsIconFg }};
|
||||||
settingsBusinessIconAway: icon {{ "settings/premium/business/business_away", settingsIconFg }};
|
settingsBusinessIconAway: icon {{ "settings/premium/business/business_away", settingsIconFg }};
|
||||||
settingsBusinessIconChatbots: icon {{ "settings/premium/business/business_chatbots", settingsIconFg }};
|
settingsBusinessIconChatbots: icon {{ "settings/premium/business/business_chatbots", settingsIconFg }};
|
||||||
|
settingsBusinessIconChatIntro: icon {{ "settings/premium/intro", settingsIconFg }};
|
||||||
|
|
||||||
settingsPremiumNewBadge: FlatLabel(defaultFlatLabel) {
|
settingsPremiumNewBadge: FlatLabel(defaultFlatLabel) {
|
||||||
style: TextStyle(semiboldTextStyle) {
|
style: TextStyle(semiboldTextStyle) {
|
||||||
|
@ -638,3 +639,7 @@ settingsChatbotsNotFound: FlatLabel(defaultFlatLabel) {
|
||||||
}
|
}
|
||||||
settingsChatbotsDeleteIcon: icon {{ "dialogs/dialogs_cancel_search", dialogsMenuIconFg }};
|
settingsChatbotsDeleteIcon: icon {{ "dialogs/dialogs_cancel_search", dialogsMenuIconFg }};
|
||||||
settingsChatbotsDeleteIconOver: icon {{ "dialogs/dialogs_cancel_search", dialogsMenuIconFgOver }};
|
settingsChatbotsDeleteIconOver: icon {{ "dialogs/dialogs_cancel_search", dialogsMenuIconFgOver }};
|
||||||
|
|
||||||
|
settingsChatIntroField: InputField(defaultMultiSelectSearchField) {
|
||||||
|
}
|
||||||
|
settingsChatIntroFieldMargins: margins(20px, 8px, 20px, 8px);
|
||||||
|
|
|
@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "main/main_app_config.h"
|
#include "main/main_app_config.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "settings/business/settings_away_message.h"
|
#include "settings/business/settings_away_message.h"
|
||||||
|
#include "settings/business/settings_chat_intro.h"
|
||||||
#include "settings/business/settings_chatbots.h"
|
#include "settings/business/settings_chatbots.h"
|
||||||
#include "settings/business/settings_greeting.h"
|
#include "settings/business/settings_greeting.h"
|
||||||
#include "settings/business/settings_location.h"
|
#include "settings/business/settings_location.h"
|
||||||
|
@ -69,6 +70,7 @@ using Order = std::vector<QString>;
|
||||||
u"business_hours"_q,
|
u"business_hours"_q,
|
||||||
u"business_location"_q,
|
u"business_location"_q,
|
||||||
u"business_bots"_q,
|
u"business_bots"_q,
|
||||||
|
u"intro"_q,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,6 +130,15 @@ using Order = std::vector<QString>;
|
||||||
PremiumFeature::BusinessBots,
|
PremiumFeature::BusinessBots,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
u"intro"_q,
|
||||||
|
Entry{
|
||||||
|
&st::settingsBusinessIconChatIntro,
|
||||||
|
tr::lng_business_subtitle_chat_intro(),
|
||||||
|
tr::lng_business_about_chat_intro(),
|
||||||
|
PremiumFeature::ChatIntro,
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,9 +238,9 @@ void AddBusinessSummary(
|
||||||
icons.reserve(int(entryMap.size()));
|
icons.reserve(int(entryMap.size()));
|
||||||
{
|
{
|
||||||
const auto &account = controller->session().account();
|
const auto &account = controller->session().account();
|
||||||
const auto mtpOrder = account.appConfig().get<Order>(
|
const auto mtpOrder = /*account.appConfig().get<Order>(
|
||||||
"business_promo_order",
|
"business_promo_order",
|
||||||
FallbackOrder());
|
FallbackOrder())*/FallbackOrder(); AssertIsDebug();
|
||||||
const auto processEntry = [&](Entry &entry) {
|
const auto processEntry = [&](Entry &entry) {
|
||||||
icons.push_back(entry.icon);
|
icons.push_back(entry.icon);
|
||||||
addRow(entry);
|
addRow(entry);
|
||||||
|
@ -375,6 +386,7 @@ void Business::setupContent() {
|
||||||
case PremiumFeature::GreetingMessage: return GreetingId();
|
case PremiumFeature::GreetingMessage: return GreetingId();
|
||||||
case PremiumFeature::QuickReplies: return QuickRepliesId();
|
case PremiumFeature::QuickReplies: return QuickRepliesId();
|
||||||
case PremiumFeature::BusinessBots: return ChatbotsId();
|
case PremiumFeature::BusinessBots: return ChatbotsId();
|
||||||
|
case PremiumFeature::ChatIntro: return ChatIntroId();
|
||||||
}
|
}
|
||||||
Unexpected("Feature in showFeature.");
|
Unexpected("Feature in showFeature.");
|
||||||
}());
|
}());
|
||||||
|
@ -396,6 +408,8 @@ void Business::setupContent() {
|
||||||
return owner->shortcutMessages().shortcutsLoaded();
|
return owner->shortcutMessages().shortcutsLoaded();
|
||||||
case PremiumFeature::BusinessBots:
|
case PremiumFeature::BusinessBots:
|
||||||
return owner->chatbots().loaded();
|
return owner->chatbots().loaded();
|
||||||
|
case PremiumFeature::ChatIntro:
|
||||||
|
return owner->session().user()->isFullLoaded();
|
||||||
}
|
}
|
||||||
Unexpected("Feature in isReady.");
|
Unexpected("Feature in isReady.");
|
||||||
};
|
};
|
||||||
|
@ -670,6 +684,8 @@ std::vector<PremiumFeature> BusinessFeaturesOrder(
|
||||||
return PremiumFeature::BusinessLocation;
|
return PremiumFeature::BusinessLocation;
|
||||||
} else if (s == u"business_bots"_q) {
|
} else if (s == u"business_bots"_q) {
|
||||||
return PremiumFeature::BusinessBots;
|
return PremiumFeature::BusinessBots;
|
||||||
|
} else if (s == u"chat_intro"_q) {
|
||||||
|
return PremiumFeature::ChatIntro;
|
||||||
}
|
}
|
||||||
return PremiumFeature::kCount;
|
return PremiumFeature::kCount;
|
||||||
}) | ranges::views::filter([](PremiumFeature feature) {
|
}) | ranges::views::filter([](PremiumFeature feature) {
|
||||||
|
|
|
@ -1061,3 +1061,6 @@ boostsMessageIconPadding: margins(0px, 2px, 0px, 0px);
|
||||||
|
|
||||||
historyIvIcon: icon{{ "boosts/boost_mini2", windowFg }};
|
historyIvIcon: icon{{ "boosts/boost_mini2", windowFg }};
|
||||||
historyIvIconPadding: margins(2px, 2px, 2px, 0px);
|
historyIvIconPadding: margins(2px, 2px, 2px, 0px);
|
||||||
|
|
||||||
|
chatIntroStickerSize: 96px;
|
||||||
|
chatIntroWidth: 224px;
|
||||||
|
|
Loading…
Add table
Reference in a new issue