From 8d66680a961b303b2e871773ec662f6a101a9357 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 22 Nov 2021 20:59:44 +0400 Subject: [PATCH] Show info about request chat admin incoming message. --- Telegram/Resources/langs/lang.strings | 6 + Telegram/SourceFiles/data/data_peer.cpp | 9 +- Telegram/SourceFiles/data/data_peer.h | 19 +- .../SourceFiles/history/history_widget.cpp | 4 - Telegram/SourceFiles/history/history_widget.h | 2 - .../view/history_view_contact_status.cpp | 230 ++++++++++++++---- .../view/history_view_contact_status.h | 63 ++--- Telegram/SourceFiles/mainwidget.cpp | 7 - Telegram/SourceFiles/mainwidget.h | 1 - Telegram/SourceFiles/ui/chat/chat.style | 3 + 10 files changed, 244 insertions(+), 100 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 62b58354fe..d72879dd7c 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1537,6 +1537,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_new_contact_add_name" = "Add {user} to contacts"; "lng_new_contact_add_done" = "{user} is now in your contact list."; "lng_new_contact_unarchive" = "Unarchive"; +"lng_new_contact_from_request_channel" = "{user} is an admin of {name}, a channel you requested to join."; +"lng_new_contact_from_request_group" = "{user} is an admin of {name}, a group you requested to join."; +"lng_from_request_title_channel" = "Chat with channel's admin"; +"lng_from_request_title_group" = "Chat with group's admin"; +"lng_from_request_body" = "You received this message because you requested to join {name} on {date}."; +"lng_from_request_understand" = "I understand"; "lng_cant_send_to_not_contact" = "Sorry, you can only send messages to\nmutual contacts at the moment.\n{more_info}"; "lng_cant_invite_not_contact" = "Sorry, you can only add mutual contacts\nto groups at the moment.\n{more_info}"; "lng_cant_more_info" = "More info ยป"; diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 29d5f01b23..0239f23a43 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -561,6 +561,9 @@ void PeerData::checkFolder(FolderId folderId) { void PeerData::setSettings(const MTPPeerSettings &data) { data.match([&](const MTPDpeerSettings &data) { + _requestChatTitle = data.vrequest_chat_title().value_or_empty(); + _requestChatDate = data.vrequest_chat_date().value_or_empty(); + using Flag = PeerSetting; setSettings((data.is_add_contact() ? Flag::AddContact : Flag()) | (data.is_autoarchived() ? Flag::AutoArchived : Flag()) @@ -571,7 +574,11 @@ void PeerData::setSettings(const MTPPeerSettings &data) { : Flag()) //| (data.is_report_geo() ? Flag::ReportGeo : Flag()) | (data.is_report_spam() ? Flag::ReportSpam : Flag()) - | (data.is_share_contact() ? Flag::ShareContact : Flag())); + | (data.is_share_contact() ? Flag::ShareContact : Flag()) + | (data.vrequest_chat_title() ? Flag::RequestChat : Flag()) + | (data.is_request_chat_broadcast() + ? Flag::RequestChatIsBroadcast + : Flag())); }); } diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 800ddf9d15..10e18379f9 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -199,7 +199,9 @@ enum class PeerSetting { ShareContact = (1 << 3), NeedContactsException = (1 << 4), AutoArchived = (1 << 5), - Unknown = (1 << 6), + RequestChat = (1 << 6), + RequestChatIsBroadcast = (1 << 7), + Unknown = (1 << 8), }; inline constexpr bool is_flag_type(PeerSetting) { return true; }; using PeerSettings = base::flags; @@ -403,7 +405,7 @@ public: // Returns true if about text was changed. bool setAbout(const QString &newAbout); - const QString &about() const { + [[nodiscard]] const QString &about() const { return _about; } @@ -412,16 +414,22 @@ public: void setSettings(PeerSettings which) { _settings.set(which); } - auto settings() const { + [[nodiscard]] auto settings() const { return (_settings.current() & PeerSetting::Unknown) ? std::nullopt : std::make_optional(_settings.current()); } - auto settingsValue() const { + [[nodiscard]] auto settingsValue() const { return (_settings.current() & PeerSetting::Unknown) ? _settings.changes() : (_settings.value() | rpl::type_erased()); } + [[nodiscard]] QString requestChatTitle() const { + return _requestChatTitle; + } + [[nodiscard]] TimeId requestChatDate() const { + return _requestChatDate; + } void setSettings(const MTPPeerSettings &data); @@ -510,6 +518,9 @@ private: BlockStatus _blockStatus = BlockStatus::Unknown; LoadedStatus _loadedStatus = LoadedStatus::Not; + QString _requestChatTitle; + TimeId _requestChatDate = 0; + QString _about; QString _themeEmoticon; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 450fea2451..fff5b2c9bb 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -7326,10 +7326,6 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { } } -QRect HistoryWidget::historyRect() const { - return _scroll->geometry(); -} - QPoint HistoryWidget::clampMousePosition(QPoint point) { if (point.x() < 0) { point.setX(0); diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 30399fe9d6..e546696bef 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -157,8 +157,6 @@ public: void firstLoadMessages(); void delayedShowAt(MsgId showAtMsgId); - QRect historyRect() const; - void updateFieldPlaceholder(); bool updateStickersByEmoji(); diff --git a/Telegram/SourceFiles/history/view/history_view_contact_status.cpp b/Telegram/SourceFiles/history/view/history_view_contact_status.cpp index 8fa55ad9ad..a55325f443 100644 --- a/Telegram/SourceFiles/history/view/history_view_contact_status.cpp +++ b/Telegram/SourceFiles/history/view/history_view_contact_status.cpp @@ -15,6 +15,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/toast/toast.h" #include "ui/text/format_values.h" // Ui::FormatPhone #include "ui/text/text_utilities.h" +#include "ui/boxes/confirm_box.h" +#include "ui/layers/generic_box.h" #include "data/data_peer.h" #include "data/data_user.h" #include "data/data_chat.h" @@ -26,10 +28,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "api/api_blocked_peers.h" #include "main/main_session.h" -#include "ui/boxes/confirm_box.h" +#include "base/unixtime.h" #include "boxes/peers/edit_contact_box.h" #include "styles/style_chat.h" #include "styles/style_layers.h" +#include "styles/style_info.h" namespace HistoryView { namespace { @@ -56,7 +59,73 @@ bool BarCurrentlyHidden(not_null peer) { } // namespace -ContactStatus::Bar::Bar(QWidget *parent, const QString &name) +class ContactStatus::BgButton final : public Ui::RippleButton { +public: + BgButton(QWidget *parent, const style::FlatButton &st); + +protected: + void paintEvent(QPaintEvent *e) override; + + void onStateChanged(State was, StateChangeSource source) override; + +private: + const style::FlatButton &_st; + +}; + +class ContactStatus::Bar final : public Ui::RpWidget { +public: + Bar(QWidget *parent, const QString &name); + + void showState(State state); + + [[nodiscard]] rpl::producer<> unarchiveClicks() const; + [[nodiscard]] rpl::producer<> addClicks() const; + [[nodiscard]] rpl::producer<> blockClicks() const; + [[nodiscard]] rpl::producer<> shareClicks() const; + [[nodiscard]] rpl::producer<> reportClicks() const; + [[nodiscard]] rpl::producer<> closeClicks() const; + [[nodiscard]] rpl::producer<> requestInfoClicks() const; + +private: + int resizeGetHeight(int newWidth) override; + + QString _name; + object_ptr _add; + object_ptr _unarchive; + object_ptr _block; + object_ptr _share; + object_ptr _report; + object_ptr _close; + object_ptr _requestChatBg; + object_ptr _requestChatInfo; + +}; + +ContactStatus::BgButton::BgButton( + QWidget *parent, + const style::FlatButton &st) +: RippleButton(parent, st.ripple) +, _st(st) { +} + +void ContactStatus::BgButton::onStateChanged( + State was, + StateChangeSource source) { + RippleButton::onStateChanged(was, source); + update(); +} + +void ContactStatus::BgButton::paintEvent(QPaintEvent *e) { + QPainter p(this); + + p.fillRect(e->rect(), isOver() ? _st.overBgColor : _st.bgColor); + paintRipple(p, 0, 0); +} + +ContactStatus::Bar::Bar( + QWidget *parent, + const QString &name) : RpWidget(parent) , _name(name) , _add( @@ -79,26 +148,46 @@ ContactStatus::Bar::Bar(QWidget *parent, const QString &name) this, QString(), st::historyContactStatusBlock) -, _close(this, st::historyReplyCancel) { - resize(_close->size()); +, _close(this, st::historyReplyCancel) +, _requestChatBg(this, st::historyContactStatusButton) +, _requestChatInfo( + this, + QString(), + st::historyContactStatusLabel) { + _requestChatInfo->setAttribute(Qt::WA_TransparentForMouseEvents); } void ContactStatus::Bar::showState(State state) { - _add->setVisible(state == State::AddOrBlock || state == State::Add); - _unarchive->setVisible(state == State::UnarchiveOrBlock - || state == State::UnarchiveOrReport); - _block->setVisible(state == State::AddOrBlock - || state == State::UnarchiveOrBlock); - _share->setVisible(state == State::SharePhoneNumber); - _report->setVisible(state == State::ReportSpam - || state == State::UnarchiveOrReport); - _add->setText((state == State::Add) + using Type = State::Type; + const auto type = state.type; + _add->setVisible(type == Type::AddOrBlock || type == Type::Add); + _unarchive->setVisible(type == Type::UnarchiveOrBlock + || type == Type::UnarchiveOrReport); + _block->setVisible(type == Type::AddOrBlock + || type == Type::UnarchiveOrBlock); + _share->setVisible(type == Type::SharePhoneNumber); + _close->setVisible(type != Type::RequestChatInfo); + _report->setVisible(type == Type::ReportSpam + || type == Type::UnarchiveOrReport); + _requestChatInfo->setVisible(type == Type::RequestChatInfo); + _requestChatBg->setVisible(type == Type::RequestChatInfo); + _add->setText((type == Type::Add) ? tr::lng_new_contact_add_name(tr::now, lt_user, _name).toUpper() : tr::lng_new_contact_add(tr::now).toUpper()); - _report->setText((state == State::ReportSpam) + _report->setText((type == Type::ReportSpam) ? tr::lng_report_spam_and_leave(tr::now).toUpper() : tr::lng_report_spam(tr::now).toUpper()); - updateButtonsGeometry(); + _requestChatInfo->setMarkedText( + (state.requestChatIsBroadcast + ? tr::lng_new_contact_from_request_channel + : tr::lng_new_contact_from_request_group)( + tr::now, + lt_user, + Ui::Text::Bold(_name), + lt_name, + Ui::Text::Bold(state.requestChatName), + Ui::Text::WithEntities)); + resizeToWidth(width()); } rpl::producer<> ContactStatus::Bar::unarchiveClicks() const { @@ -125,16 +214,19 @@ rpl::producer<> ContactStatus::Bar::closeClicks() const { return _close->clicks() | rpl::to_empty; } -void ContactStatus::Bar::resizeEvent(QResizeEvent *e) { - _close->moveToRight(0, 0); - updateButtonsGeometry(); +rpl::producer<> ContactStatus::Bar::requestInfoClicks() const { + return _requestChatBg->clicks() | rpl::to_empty; } -void ContactStatus::Bar::updateButtonsGeometry() { - const auto full = width(); +int ContactStatus::Bar::resizeGetHeight(int newWidth) { + _close->moveToRight(0, 0); + const auto closeWidth = _close->width(); - const auto available = full - closeWidth; + const auto available = newWidth - closeWidth; const auto skip = st::historyContactStatusMinSkip; + if (available <= 2 * skip) { + return _close->height(); + } const auto buttonWidth = [&](const object_ptr &button) { return button->textWidth() + 2 * skip; }; @@ -157,18 +249,18 @@ void ContactStatus::Bar::updateButtonsGeometry() { thatWidth + closeWidth - available, 0, closeWidth); - placeButton(button, full, margin); + placeButton(button, newWidth, margin); }; const auto &leftButton = _unarchive->isHidden() ? _add : _unarchive; const auto &rightButton = _block->isHidden() ? _report : _block; if (!leftButton->isHidden() && !rightButton->isHidden()) { const auto leftWidth = buttonWidth(leftButton); const auto rightWidth = buttonWidth(rightButton); - const auto half = full / 2; + const auto half = newWidth / 2; if (leftWidth <= half - && rightWidth + 2 * closeWidth <= full - half) { + && rightWidth + 2 * closeWidth <= newWidth - half) { placeButton(leftButton, half); - placeButton(rightButton, full - half); + placeButton(rightButton, newWidth - half); } else if (leftWidth + rightWidth <= available) { const auto margin = std::clamp( leftWidth + rightWidth + closeWidth - available, @@ -177,22 +269,31 @@ void ContactStatus::Bar::updateButtonsGeometry() { const auto realBlockWidth = rightWidth + 2 * closeWidth - margin; if (leftWidth > realBlockWidth) { placeButton(leftButton, leftWidth); - placeButton(rightButton, full - leftWidth, margin); + placeButton(rightButton, newWidth - leftWidth, margin); } else { - placeButton(leftButton, full - realBlockWidth); + placeButton(leftButton, newWidth - realBlockWidth); placeButton(rightButton, realBlockWidth, margin); } } else { const auto forLeft = (available * leftWidth) / (leftWidth + rightWidth); placeButton(leftButton, forLeft); - placeButton(rightButton, full - forLeft, closeWidth); + placeButton(rightButton, newWidth - forLeft, closeWidth); } } else { placeOne(_add); placeOne(_share); placeOne(_report); } + if (_requestChatInfo->isHidden()) { + return _close->height(); + } + const auto vskip = st::topBarArrowPadding.top(); + _requestChatInfo->resizeToWidth(available - 2 * skip); + _requestChatInfo->move(skip, vskip); + const auto newHeight = _requestChatInfo->height() + 2 * vskip; + _requestChatBg->setGeometry(0, 0, newWidth, newHeight); + return newHeight; } ContactStatus::ContactStatus( @@ -231,6 +332,7 @@ void ContactStatus::setupWidgets(not_null parent) { auto ContactStatus::PeerState(not_null peer) -> rpl::producer { using SettingsChange = PeerData::Settings::Change; + using Type = State::Type; if (const auto user = peer->asUser()) { using FlagsChange = UserData::Flags::Change; using Flag = UserDataFlag; @@ -245,34 +347,43 @@ auto ContactStatus::PeerState(not_null peer) user->settingsValue() ) | rpl::map([=]( FlagsChange flags, - SettingsChange settings) { + SettingsChange settings) -> State { if (flags.value & Flag::Blocked) { - return State::None; + return { Type::None }; } else if (user->isContact()) { if (settings.value & PeerSetting::ShareContact) { - return State::SharePhoneNumber; + return { Type::SharePhoneNumber }; } else { - return State::None; + return { Type::None }; } + } else if (settings.value & PeerSetting::RequestChat) { + return { + .type = Type::RequestChatInfo, + .requestChatName = peer->requestChatTitle(), + .requestChatIsBroadcast = !!(settings.value + & PeerSetting::RequestChatIsBroadcast), + .requestDate = peer->requestChatDate(), + }; } else if (settings.value & PeerSetting::AutoArchived) { - return State::UnarchiveOrBlock; + return { Type::UnarchiveOrBlock }; } else if (settings.value & PeerSetting::BlockContact) { - return State::AddOrBlock; + return { Type::AddOrBlock }; } else if (settings.value & PeerSetting::AddContact) { - return State::Add; + return { Type::Add }; } else { - return State::None; + return { Type::None }; } }); } return peer->settingsValue( ) | rpl::map([=](SettingsChange settings) { + using Type = State::Type; return (settings.value & PeerSetting::AutoArchived) - ? State::UnarchiveOrReport + ? State{ Type::UnarchiveOrReport } : (settings.value & PeerSetting::ReportSpam) - ? State::ReportSpam - : State::None; + ? State{ Type::ReportSpam } + : State{ Type::None }; }); } @@ -285,7 +396,7 @@ void ContactStatus::setupState(not_null peer) { peer ) | rpl::start_with_next([=](State state) { _state = state; - if (state == State::None) { + if (state.type == State::Type::None) { _bar.hide(anim::type::normal); } else { _bar.entity()->showState(state); @@ -303,6 +414,7 @@ void ContactStatus::setupHandlers(not_null peer) { setupUnarchiveHandler(peer); setupReportHandler(peer); setupCloseHandler(peer); + setupRequestInfoHandler(peer); } void ContactStatus::setupAddHandler(not_null user) { @@ -420,8 +532,44 @@ void ContactStatus::setupCloseHandler(not_null peer) { }, _bar.lifetime()); } +void ContactStatus::setupRequestInfoHandler(not_null peer) { + const auto request = _bar.lifetime().make_state(0); + _bar.entity()->requestInfoClicks( + ) | rpl::filter([=] { + return !(*request); + }) | rpl::start_with_next([=] { + _controller->show(Box([=](not_null box) { + box->setTitle((_state.requestChatIsBroadcast + ? tr::lng_from_request_title_channel + : tr::lng_from_request_title_group)()); + + box->addRow(object_ptr( + box, + tr::lng_from_request_body( + lt_name, + rpl::single(Ui::Text::Bold(_state.requestChatName)), + lt_date, + rpl::single(langDateTimeFull( + base::unixtime::parse(_state.requestDate) + )) | Ui::Text::ToWithEntities(), + Ui::Text::WithEntities), + st::boxLabel)); + + box->addButton(tr::lng_from_request_understand(), [=] { + if (*request) { + return; + } + peer->setSettings(0); + *request = peer->session().api().request( + MTPmessages_HidePeerSettingsBar(peer->input) + ).send(); + }); + })); + }, _bar.lifetime()); +} + void ContactStatus::show() { - const auto visible = (_state != State::None); + const auto visible = (_state.type != State::Type::None); if (!_shown) { _shown = true; if (visible) { diff --git a/Telegram/SourceFiles/history/view/history_view_contact_status.h b/Telegram/SourceFiles/history/view/history_view_contact_status.h index 3e258555fa..56b696fcfa 100644 --- a/Telegram/SourceFiles/history/view/history_view_contact_status.h +++ b/Telegram/SourceFiles/history/view/history_view_contact_status.h @@ -18,6 +18,7 @@ class SessionController; namespace Ui { class FlatButton; class IconButton; +class FlatLabel; } // namespace Ui namespace HistoryView { @@ -33,51 +34,32 @@ public: void raise(); void move(int x, int y); - int height() const; - rpl::producer heightValue() const; + [[nodiscard]] int height() const; + [[nodiscard]] rpl::producer heightValue() const; - rpl::lifetime &lifetime() { + [[nodiscard]] rpl::lifetime &lifetime() { return _lifetime; } private: - enum class State { - None, - ReportSpam, - Add, - AddOrBlock, - UnarchiveOrBlock, - UnarchiveOrReport, - SharePhoneNumber, - }; - - class Bar : public Ui::RpWidget { - public: - Bar(QWidget *parent, const QString &name); - - void showState(State state); - - rpl::producer<> unarchiveClicks() const; - rpl::producer<> addClicks() const; - rpl::producer<> blockClicks() const; - rpl::producer<> shareClicks() const; - rpl::producer<> reportClicks() const; - rpl::producer<> closeClicks() const; - - protected: - void resizeEvent(QResizeEvent *e) override; - - private: - void updateButtonsGeometry(); - - QString _name; - object_ptr _add; - object_ptr _unarchive; - object_ptr _block; - object_ptr _share; - object_ptr _report; - object_ptr _close; + class Bar; + class BgButton; + struct State { + enum class Type { + None, + ReportSpam, + Add, + AddOrBlock, + UnarchiveOrBlock, + UnarchiveOrReport, + SharePhoneNumber, + RequestChatInfo, + }; + Type type = Type::None; + QString requestChatName; + bool requestChatIsBroadcast = false; + TimeId requestDate = 0; }; void setupWidgets(not_null parent); @@ -89,11 +71,12 @@ private: void setupUnarchiveHandler(not_null peer); void setupReportHandler(not_null peer); void setupCloseHandler(not_null peer); + void setupRequestInfoHandler(not_null peer); static rpl::producer PeerState(not_null peer); const not_null _controller; - State _state = State::None; + State _state; Ui::SlideWrap _bar; Ui::PlainShadow _shadow; bool _shown = false; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 5a4f77046b..50fd731aab 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1857,13 +1857,6 @@ void MainWidget::orderWidgets() { if (_hider) _hider->raise(); } -QRect MainWidget::historyRect() const { - QRect r(_history->historyRect()); - r.moveLeft(r.left() + _history->x()); - r.moveTop(r.top() + _history->y()); - return r; -} - QPixmap MainWidget::grabForShowAnimation(const Window::SectionSlideParams ¶ms) { QPixmap result; floatPlayerHideAll(); diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 91402ca227..87ce1a6e49 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -151,7 +151,6 @@ public: void showBackFromStack( const SectionShow ¶ms); void orderWidgets(); - QRect historyRect() const; QPixmap grabForShowAnimation(const Window::SectionSlideParams ¶ms); void checkMainSectionToLayer(); diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index d231ba3e2a..795da87592 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -268,6 +268,9 @@ historyContactStatusBlock: FlatButton(historyContactStatusButton) { color: attentionButtonFg; overColor: attentionButtonFg; } +historyContactStatusLabel: FlatLabel(defaultFlatLabel) { + minWidth: 240px; +} historyContactStatusMinSkip: 16px; historySendIcon: icon {{ "chat/input_send", historySendIconFg }};