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