diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 47b693e36..0050add99 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1067,6 +1067,8 @@ PRIVATE profile/profile_cover_drop_area.h settings/settings_advanced.cpp settings/settings_advanced.h + settings/settings_blocked_peers.cpp + settings/settings_blocked_peers.h settings/settings_chat.cpp settings/settings_chat.h settings/settings_calls.cpp diff --git a/Telegram/Resources/animations/blocked_peers_empty.tgs b/Telegram/Resources/animations/blocked_peers_empty.tgs new file mode 100644 index 000000000..02d5a9de6 Binary files /dev/null and b/Telegram/Resources/animations/blocked_peers_empty.tgs differ diff --git a/Telegram/Resources/icons/settings/blocked.png b/Telegram/Resources/icons/settings/blocked.png new file mode 100644 index 000000000..394b9f289 Binary files /dev/null and b/Telegram/Resources/icons/settings/blocked.png differ diff --git a/Telegram/Resources/icons/settings/blocked@2x.png b/Telegram/Resources/icons/settings/blocked@2x.png new file mode 100644 index 000000000..ad15b0ed1 Binary files /dev/null and b/Telegram/Resources/icons/settings/blocked@2x.png differ diff --git a/Telegram/Resources/icons/settings/blocked@3x.png b/Telegram/Resources/icons/settings/blocked@3x.png new file mode 100644 index 000000000..64ff47658 Binary files /dev/null and b/Telegram/Resources/icons/settings/blocked@3x.png differ diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 0a07b55d1..19f288c59 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -799,6 +799,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_blocked_list_confirm_text" = "Do you want to block {name} from messaging and calling you on Telegram?"; "lng_blocked_list_confirm_clear" = "Delete this chat"; "lng_blocked_list_confirm_ok" = "Block"; +"lng_blocked_list_empty_title" = "No blocked users"; +"lng_blocked_list_empty_description" = "You haven't blocked anyone yet."; +"lng_blocked_list_subtitle#one" = "{count} blocked user"; +"lng_blocked_list_subtitle#other" = "{count} blocked users"; "lng_edit_privacy_everyone" = "Everybody"; "lng_edit_privacy_contacts" = "My contacts"; diff --git a/Telegram/Resources/qrc/telegram/animations.qrc b/Telegram/Resources/qrc/telegram/animations.qrc index eac41dc7f..a750dc771 100644 --- a/Telegram/Resources/qrc/telegram/animations.qrc +++ b/Telegram/Resources/qrc/telegram/animations.qrc @@ -2,4 +2,7 @@ ../../animations/change_number.tgs + + ../../animations/blocked_peers_empty.tgs + diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 98f306627..8c1456e84 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -602,6 +602,9 @@ changePhoneError: FlatLabel(changePhoneLabel) { textFg: boxTextFgError; } +blockedUsersListSubtitleAddPadding: margins(0px, 1px, 0px, -14px); +blockedUsersListIconPadding: margins(0px, 34px, 0px, 5px); + adminLogFilterUserpicLeft: 15px; adminLogFilterLittleSkip: 16px; adminLogFilterCheckbox: Checkbox(defaultBoxCheckbox) { diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index 2c08e93a2..0d2b88e93 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -90,6 +90,7 @@ settingsIconPosition: icon {{ "settings/position", settingsIconFg }}; settingsIconPin: icon {{ "settings/pin", settingsIconFg }}; settingsIconDownload: icon {{ "settings/download", settingsIconFg }}; settingsIconMention: icon {{ "settings/mention", settingsIconFg }}; +settingsIconBlocked: icon {{ "settings/blocked", settingsIconFg }}; settingsCheckbox: Checkbox(defaultBoxCheckbox) { textPosition: point(15px, 1px); diff --git a/Telegram/SourceFiles/settings/settings_blocked_peers.cpp b/Telegram/SourceFiles/settings/settings_blocked_peers.cpp new file mode 100644 index 000000000..522b81fca --- /dev/null +++ b/Telegram/SourceFiles/settings/settings_blocked_peers.cpp @@ -0,0 +1,211 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "settings/settings_blocked_peers.h" + +#include "api/api_blocked_peers.h" +#include "apiwrap.h" +#include "data/data_changes.h" +#include "data/data_peer.h" +#include "lang/lang_keys.h" +#include "lottie/lottie_icon.h" +#include "main/main_session.h" +#include "settings/settings_privacy_controllers.h" +#include "ui/widgets/buttons.h" +#include "ui/wrap/padding_wrap.h" +#include "ui/wrap/slide_wrap.h" +#include "ui/wrap/vertical_layout.h" +#include "window/window_session_controller.h" +#include "styles/style_settings.h" +#include "styles/style_boxes.h" + +namespace Settings { + +Blocked::Blocked( + QWidget *parent, + not_null controller) +: Section(parent) +, _controller(controller) { + + setupContent(); + + { + auto padding = st::changePhoneIconPadding; + padding.setBottom(padding.top()); + _loading = base::make_unique_q>( + this, + object_ptr>( + this, + object_ptr( + this, + tr::lng_contacts_loading(), + st::changePhoneDescription), + std::move(padding))); + Ui::ResizeFitChild(this, _loading.get()); + } + + _controller->session().api().blockedPeers().slice( + ) | rpl::start_with_next([=](const Api::BlockedPeers::Slice &slice) { + checkTotal(slice.total); + }, lifetime()); + + _controller->session().changes().peerUpdates( + Data::PeerUpdate::Flag::IsBlocked + ) | rpl::start_with_next([=](const Data::PeerUpdate &update) { + if (update.peer->isBlocked()) { + checkTotal(1); + } + }, lifetime()); +} + +rpl::producer Blocked::title() { + return tr::lng_settings_blocked_users(); +} + +QPointer Blocked::createPinnedToTop(not_null parent) { + const auto content = Ui::CreateChild(parent.get()); + + AddSkip(content); + + AddButton( + content, + tr::lng_blocked_list_add(), + st::settingsButton, + { &st::settingsIconBlocked, kIconLightBlue } + )->addClickHandler([=] { + BlockedBoxController::BlockNewPeer(_controller); + }); + + AddSkip(content); + AddDividerText(content, tr::lng_blocked_list_about()); + + { + const auto subtitle = content->add( + object_ptr>( + content, + object_ptr(content)))->setDuration(0); + AddSkip(subtitle->entity()); + auto subtitleText = _countBlocked.value( + ) | rpl::map([=](int count) { + return tr::lng_blocked_list_subtitle(tr::now, lt_count, count); + }); + AddSubsectionTitle( + subtitle->entity(), + rpl::duplicate(subtitleText), + st::blockedUsersListSubtitleAddPadding); + subtitle->toggleOn( + rpl::merge( + _emptinessChanges.events() | rpl::map(!rpl::mappers::_1), + _countBlocked.value() | rpl::map(rpl::mappers::_1 > 0) + ) | rpl::distinct_until_changed()); + + // Workaround. + std::move( + subtitleText + ) | rpl::start_with_next([=] { + subtitle->entity()->resizeToWidth(content->width()); + }, subtitle->lifetime()); + } + + return Ui::MakeWeak(not_null{ content }); +} + +void Blocked::setupContent() { + using namespace rpl::mappers; + const auto container = Ui::CreateChild(this); + + const auto listWrap = container->add( + object_ptr>( + container, + object_ptr(container))); + listWrap->toggleOn( + _emptinessChanges.events_starting_with(true) | rpl::map(!_1), + anim::type::instant); + + { + struct State { + std::unique_ptr controller; + std::unique_ptr delegate; + }; + + auto controller = std::make_unique(_controller); + const auto content = listWrap->entity()->add( + object_ptr(this, controller.get())); + + const auto state = content->lifetime().make_state(); + state->controller = std::move(controller); + state->delegate = std::make_unique(); + + state->delegate->setContent(content); + state->controller->setDelegate(state->delegate.get()); + + state->controller->rowsCountChanges( + ) | rpl::start_with_next([=](int total) { + _countBlocked = total; + checkTotal(total); + }, content->lifetime()); + _countBlocked = content->fullRowsCount(); + } + + const auto emptyWrap = container->add( + object_ptr>( + container, + object_ptr(container))); + emptyWrap->toggleOn( + _emptinessChanges.events_starting_with(false), + anim::type::instant); + + { + const auto content = emptyWrap->entity(); + auto icon = CreateLottieIcon(content, { + .name = u"blocked_peers_empty"_q, + .sizeOverride = { + st::changePhoneIconSize, + st::changePhoneIconSize, + }, + }, st::blockedUsersListIconPadding); + content->add(std::move(icon.widget)); + + _showFinished.events( + ) | rpl::start_with_next([animate = std::move(icon.animate)] { + animate(); + }, content->lifetime()); + + content->add( + object_ptr>( + content, + object_ptr( + content, + tr::lng_blocked_list_empty_title(), + st::changePhoneTitle)), + st::changePhoneTitlePadding); + + content->add( + object_ptr>( + content, + object_ptr( + content, + tr::lng_blocked_list_empty_description(), + st::changePhoneDescription)), + st::changePhoneDescriptionPadding); + + AddSkip(content, st::blockedUsersListIconPadding.top()); + } + + Ui::ResizeFitChild(this, container); +} + +void Blocked::checkTotal(int total) { + _loading = nullptr; + _emptinessChanges.fire(total <= 0); +} + +void Blocked::showFinished() { + _showFinished.fire({}); +} + +} // namespace Settings diff --git a/Telegram/SourceFiles/settings/settings_blocked_peers.h b/Telegram/SourceFiles/settings/settings_blocked_peers.h new file mode 100644 index 000000000..a5642faa9 --- /dev/null +++ b/Telegram/SourceFiles/settings/settings_blocked_peers.h @@ -0,0 +1,46 @@ +/* +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_common.h" + +namespace Window { +class Controller; +} // namespace Window + +namespace Settings { + +class Blocked : public Section { +public: + Blocked( + QWidget *parent, + not_null controller); + + void showFinished() override; + + [[nodiscard]] rpl::producer title() override; + + [[nodiscard]] QPointer createPinnedToTop( + not_null parent) override; + +private: + void setupContent(); + void checkTotal(int total); + + const not_null _controller; + + base::unique_qptr _loading; + + rpl::variable _countBlocked; + + rpl::event_stream<> _showFinished; + rpl::event_stream _emptinessChanges; + +}; + +} // namespace Settings diff --git a/Telegram/SourceFiles/settings/settings_common.cpp b/Telegram/SourceFiles/settings/settings_common.cpp index 92cf74939..9c1967322 100644 --- a/Telegram/SourceFiles/settings/settings_common.cpp +++ b/Telegram/SourceFiles/settings/settings_common.cpp @@ -264,7 +264,7 @@ LottieIcon CreateLottieIcon( raw->lifetime().add([kept = std::move(owned)]{}); const auto animate = [=] { - icon->animate([=] { raw->update(); }, 0, icon->framesCount()); + icon->animate([=] { raw->update(); }, 0, icon->framesCount() - 1); }; raw->paintRequest( ) | rpl::start_with_next([=] { diff --git a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp index 7b6c1a6a2..b09be4621 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp @@ -383,6 +383,7 @@ void BlockedBoxController::handleBlockedEvent(not_null user) { } else if (auto row = delegate()->peerListFindRow(user->id.value)) { delegate()->peerListRemoveRow(row); delegate()->peerListRefreshRows(); + _rowsCountChanges.fire(delegate()->peerListFullRowsCount()); } } @@ -408,6 +409,7 @@ bool BlockedBoxController::appendRow(not_null peer) { return false; } delegate()->peerListAppendRow(createRow(peer)); + _rowsCountChanges.fire(delegate()->peerListFullRowsCount()); return true; } @@ -416,6 +418,7 @@ bool BlockedBoxController::prependRow(not_null peer) { return false; } delegate()->peerListPrependRow(createRow(peer)); + _rowsCountChanges.fire(delegate()->peerListFullRowsCount()); return true; } @@ -440,6 +443,10 @@ std::unique_ptr BlockedBoxController::createRow( return row; } +rpl::producer BlockedBoxController::rowsCountChanges() const { + return _rowsCountChanges.events(); +} + PhoneNumberPrivacyController::PhoneNumberPrivacyController( not_null controller) : _controller(controller) { diff --git a/Telegram/SourceFiles/settings/settings_privacy_controllers.h b/Telegram/SourceFiles/settings/settings_privacy_controllers.h index 240724607..60d26e2dc 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_controllers.h +++ b/Telegram/SourceFiles/settings/settings_privacy_controllers.h @@ -33,6 +33,8 @@ public: void rowRightActionClicked(not_null row) override; void loadMoreRows() override; + [[nodiscard]] rpl::producer rowsCountChanges() const; + static void BlockNewPeer(not_null window); private: @@ -50,6 +52,8 @@ private: base::has_weak_ptr _guard; + rpl::event_stream _rowsCountChanges; + }; class PhoneNumberPrivacyController : public EditPrivacyController { diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.cpp b/Telegram/SourceFiles/settings/settings_privacy_security.cpp index 22af983bc..f0507396e 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_security.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_security.cpp @@ -13,11 +13,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_self_destruct.h" #include "api/api_sensitive_content.h" #include "api/api_global_privacy.h" +#include "settings/settings_blocked_peers.h" #include "settings/settings_common.h" #include "settings/settings_privacy_controllers.h" #include "base/timer_rpl.h" #include "base/unixtime.h" -#include "boxes/peer_list_box.h" #include "boxes/edit_privacy_box.h" #include "boxes/passcode_box.h" #include "boxes/auto_lock_box.h" @@ -772,17 +772,7 @@ void SetupBlockedList( st::settingsButton, { &st::settingsIconMinus, kIconRed }); blockedPeers->addClickHandler([=] { - const auto initBox = [=](not_null box) { - box->addButton(tr::lng_close(), [=] { - box->closeBox(); - }); - box->addLeftButton(tr::lng_blocked_list_add(), [=] { - BlockedBoxController::BlockNewPeer(controller); - }); - }; - controller->show(Box( - std::make_unique(controller), - initBox)); + showOther(Blocked::Id()); }); std::move( updateTrigger