diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 6e38a09d5..97f93033c 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -178,6 +178,8 @@ PRIVATE boxes/peers/add_participants_box.h boxes/peers/edit_contact_box.cpp boxes/peers/edit_contact_box.h + boxes/peers/edit_forum_topic_box.cpp + boxes/peers/edit_forum_topic_box.h boxes/peers/edit_linked_chat_box.cpp boxes/peers/edit_linked_chat_box.h boxes/peers/edit_participant_box.cpp diff --git a/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp new file mode 100644 index 000000000..bf1b8643b --- /dev/null +++ b/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp @@ -0,0 +1,223 @@ +/* +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/peers/edit_forum_topic_box.h" + +#include "ui/widgets/input_fields.h" +#include "ui/abstract_button.h" +#include "data/data_channel.h" +#include "data/data_forum.h" +#include "data/data_forum_topic.h" +#include "data/data_session.h" +#include "data/stickers/data_custom_emoji.h" +#include "main/main_session.h" +#include "history/history.h" +#include "lang/lang_keys.h" +#include "info/profile/info_profile_emoji_status_panel.h" +#include "window/window_session_controller.h" +#include "settings/settings_common.h" +#include "apiwrap.h" + +namespace { + +[[nodiscard]] int EditIconSize() { + const auto tag = Data::CustomEmojiManager::SizeTag::Large; + return Data::FrameSizeFromTag(tag) / style::DevicePixelRatio(); +} + +[[nodiscard]] rpl::producer EditIconButton( + not_null controller, + not_null parent, + DocumentId id) { + using namespace Info::Profile; + struct State { + rpl::variable id; + EmojiStatusPanel panel; + std::unique_ptr chosen; + }; + const auto tag = Data::CustomEmojiManager::SizeTag::Large; + const auto size = EditIconSize(); + const auto result = Ui::CreateChild(parent.get()); + const auto state = result->lifetime().make_state(); + state->id.value( + ) | rpl::start_with_next([=](DocumentId id) { + const auto owner = &controller->session().data(); + state->chosen = id + ? owner->customEmojiManager().create( + id, + [=] { result->update(); }, + tag) + : nullptr; + result->update(); + }, result->lifetime()); + state->id = id; + state->panel.setChooseFilter([=](DocumentId) { + return true; + }); + state->panel.setChooseCallback([=](DocumentId id) { + state->id = id; + }); + result->resize(size, size); + result->paintRequest( + ) | rpl::filter([=] { + return !state->panel.paintBadgeFrame(result); + }) | rpl::start_with_next([=](QRect clip) { + auto args = Ui::Text::CustomEmoji::Context{ + .preview = st::windowBgOver->c, + .now = crl::now(), + .paused = controller->isGifPausedAtLeastFor( + Window::GifPauseReason::Layer), + }; + auto p = QPainter(result); + if (state->chosen) { + state->chosen->paint(p, args); + } else { + p.fillRect(clip, Qt::red); + } + }, result->lifetime()); + result->setClickedCallback([=] { + state->panel.show(controller, result, tag); + }); + return state->id.value(); +} + +} // namespace + +void NewForumTopicBox( + not_null box, + not_null controller, + not_null forum) { + EditForumTopicBox(box, controller, forum, MsgId(0)); +} + +void EditForumTopicBox( + not_null box, + not_null controller, + not_null forum, + MsgId rootId) { + const auto creating = !rootId; + const auto topic = (!creating && forum->peer->forum()) + ? forum->peer->forum()->topicFor(rootId) + : nullptr; + // #TODO forum lang + box->setTitle(rpl::single(creating ? u"New topic"_q : u"Edit topic"_q)); + + struct State { + DocumentId iconId = 0; + mtpRequestId titleRequestId = 0; + mtpRequestId iconRequestId = 0; + }; + const auto state = box->lifetime().make_state(); + // #TODO forum lang and design + Settings::AddSubsectionTitle( + box->verticalLayout(), + rpl::single(u"Topic Icon"_q)); + const auto badgeWrap = box->addRow( + object_ptr(box, EditIconSize())); + EditIconButton( + controller, + badgeWrap, + topic ? topic->iconId() : 0 + ) | rpl::start_with_next([=](DocumentId id) { + state->iconId = id; + }, box->lifetime()); + + const auto title = box->addRow( + object_ptr( + box, + st::defaultInputField, + rpl::single(u"Topic Title"_q), + topic ? topic->title() : QString())); // #TODO forum lang + box->setFocusCallback([=] { + title->setFocusFast(); + }); + + const auto requestId = std::make_shared(); + const auto create = [=] { + if (!forum->peer->isForum()) { + box->closeBox(); + return; + } else if (title->getLastText().trimmed().isEmpty()) { + title->setFocus(); + return; + } +#if 0 // #TODO forum create + const auto randomId = base::RandomValue(); + const auto api = &forum->session().api(); + api->request(MTPchannels_CreateForumTopic( + MTP_flags(0), + forum->inputChannel, + MTP_string(title->getLastText().trimmed()), + MTPlong(), // icon_emoji_id + MTPInputMedia(), + MTP_string(message->getLastText().trimmed()), + MTP_long(randomId), + MTPVector(), + MTPInputPeer() // send_as + )).done([=](const MTPUpdates &result) { + api->applyUpdates(result, randomId); + box->closeBox(); + }).fail([=](const MTP::Error &error) { + api->sendMessageFail(error, forum, randomId); + }).send(); +#endif + }; + + const auto save = [=] { + const auto topic = forum->peer->forum() + ? forum->peer->forum()->topicFor(rootId) + : nullptr; + if (!topic) { + box->closeBox(); + return; + } + const auto api = &forum->session().api(); + if (state->titleRequestId <= 0) { + if (title->getLastText().trimmed().isEmpty()) { + title->setFocus(); + return; + } + state->titleRequestId = api->request(MTPchannels_EditForumTitle( + topic->channel()->inputChannel, + MTP_int(rootId), + MTP_string(title->getLastText().trimmed()) + )).done([=](const MTPUpdates &result) { + api->applyUpdates(result); + state->titleRequestId = 0; + if (!state->iconRequestId) { + box->closeBox(); + } + }).fail([=](const MTP::Error &error) { + state->titleRequestId = -1; + }).send(); + } + if (state->iconRequestId <= 0) { + state->iconRequestId = api->request(MTPchannels_EditForumIcon( + topic->channel()->inputChannel, + MTP_int(rootId), + MTP_long(state->iconId) + )).done([=](const MTPUpdates &result) { + api->applyUpdates(result); + state->iconRequestId = 0; + if (!state->titleRequestId) { + box->closeBox(); + } + }).fail([=](const MTP::Error &error) { + state->iconRequestId = -1; + }).send(); + } + }; + + if (creating) { + box->addButton(tr::lng_create_group_create(), create); + } else { + box->addButton(tr::lng_settings_save(), save); + } + box->addButton(tr::lng_cancel(), [=] { + box->closeBox(); + }); +} diff --git a/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.h b/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.h new file mode 100644 index 000000000..fddc3c052 --- /dev/null +++ b/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.h @@ -0,0 +1,27 @@ +/* +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 "ui/layers/generic_box.h" + +class History; + +namespace Window { +class SessionController; +} // namespace Window + +void NewForumTopicBox( + not_null box, + not_null controller, + not_null forum); + +void EditForumTopicBox( + not_null box, + not_null controller, + not_null forum, + MsgId rootId); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index cb609a7ec..e1d973fce 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -818,7 +818,7 @@ void Controller::fillForumButton() { AddButtonWithText( _controls.buttonsLayout, - rpl::single(u"Forum"_q), // #TODO forum lang + rpl::single(u"Topics"_q), // #TODO forum lang rpl::single(QString()), [] {}, { &st::settingsIconGroup, Settings::kIconPurple } diff --git a/Telegram/SourceFiles/calls/group/calls_group_members.cpp b/Telegram/SourceFiles/calls/group/calls_group_members.cpp index cd9069451..f97807d6d 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_members.cpp @@ -1258,9 +1258,7 @@ base::unique_qptr Members::Controller::createRowContextMenu( result->menu(), st::groupCallPopupCoverMenu, st::groupCallMenuCover, - Info::Profile::NameValue( - participantPeer - ) | rpl::map([](const auto &text) { return text.text; }), + Info::Profile::NameValue(participantPeer), PrepareShortInfoStatus(participantPeer), PrepareShortInfoUserpic( participantPeer, diff --git a/Telegram/SourceFiles/calls/group/calls_group_menu.cpp b/Telegram/SourceFiles/calls/group/calls_group_menu.cpp index 9b97c34f3..0f7c2320e 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_menu.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_menu.cpp @@ -182,10 +182,10 @@ void JoinAsAction::prepare() { rpl::combine( tr::lng_group_call_display_as_header(), Info::Profile::NameValue(_peer) - ) | rpl::start_with_next([=](QString text, TextWithEntities name) { + ) | rpl::start_with_next([=](QString text, QString name) { const auto &padding = st::groupCallJoinAsPadding; _text.setMarkedText(_st.itemStyle, { text }, MenuTextOptions); - _name.setMarkedText(_st.itemStyle, name, MenuTextOptions); + _name.setMarkedText(_st.itemStyle, { name }, MenuTextOptions); const auto textWidth = _text.maxWidth(); const auto nameWidth = _name.maxWidth(); const auto textLeft = padding.left() diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp index c1a0b01d5..c1e063146 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp @@ -221,8 +221,8 @@ void Panel::migrate(not_null channel) { void Panel::subscribeToPeerChanges() { Info::Profile::NameValue( _peer - ) | rpl::start_with_next([=](const TextWithEntities &name) { - window()->setTitle(name.text); + ) | rpl::start_with_next([=](const QString &name) { + window()->setTitle(name); }, _peerLifetime); } @@ -2336,10 +2336,8 @@ void Panel::refreshTitle() { ) | rpl::map([=](not_null real) { return real->titleValue(); }) | rpl::flatten_latest()) - ) | rpl::map([=]( - const TextWithEntities &name, - const QString &title) { - return title.isEmpty() ? name.text : title; + ) | rpl::map([=](const QString &name, const QString &title) { + return title.isEmpty() ? name : title; }) | rpl::after_next([=] { refreshTitleGeometry(); }); diff --git a/Telegram/SourceFiles/data/data_forum.cpp b/Telegram/SourceFiles/data/data_forum.cpp index c1ea9c423..ba6dae353 100644 --- a/Telegram/SourceFiles/data/data_forum.cpp +++ b/Telegram/SourceFiles/data/data_forum.cpp @@ -165,60 +165,4 @@ rpl::producer<> Forum::chatsListLoadedEvents() const { return _chatsListLoadedEvents.events(); } -void ShowAddForumTopic( - not_null controller, - not_null forum) { - controller->show(Box([=](not_null box) { - box->setTitle(rpl::single(u"New Topic"_q)); - - const auto title = box->addRow( - object_ptr( - box, - st::defaultInputField, - rpl::single(u"Topic Title"_q))); // #TODO forum lang - const auto message = box->addRow( - object_ptr( - box, - st::newGroupDescription, - Ui::InputField::Mode::MultiLine, - rpl::single(u"Message"_q))); // #TODO forum lang - box->setFocusCallback([=] { - title->setFocusFast(); - }); - box->addButton(tr::lng_create_group_create(), [=] { - if (!forum->isForum()) { - box->closeBox(); - return; - } else if (title->getLastText().trimmed().isEmpty()) { - title->setFocus(); - return; - } else if (message->getLastText().trimmed().isEmpty()) { - message->setFocus(); - return; - } - const auto randomId = base::RandomValue(); - const auto api = &forum->session().api(); - api->request(MTPchannels_CreateForumTopic( - MTP_flags(0), - forum->inputChannel, - MTP_string(title->getLastText().trimmed()), - MTPlong(), // icon_emoji_id - MTPInputMedia(), - MTP_string(message->getLastText().trimmed()), - MTP_long(randomId), - MTPVector(), - MTPInputPeer() // send_as - )).done([=](const MTPUpdates &result) { - api->applyUpdates(result, randomId); - box->closeBox(); - }).fail([=](const MTP::Error &error) { - api->sendMessageFail(error, forum, randomId); - }).send(); - }); - box->addButton(tr::lng_cancel(), [=] { - box->closeBox(); - }); - }), Ui::LayerOption::KeepOther); -} - } // namespace Data diff --git a/Telegram/SourceFiles/data/data_forum.h b/Telegram/SourceFiles/data/data_forum.h index 557646f3d..01451a77a 100644 --- a/Telegram/SourceFiles/data/data_forum.h +++ b/Telegram/SourceFiles/data/data_forum.h @@ -59,8 +59,4 @@ private: }; -void ShowAddForumTopic( - not_null controller, - not_null forum); - } // namespace Data diff --git a/Telegram/SourceFiles/data/data_forum_topic.cpp b/Telegram/SourceFiles/data/data_forum_topic.cpp index e6847e0a8..9be4d5b83 100644 --- a/Telegram/SourceFiles/data/data_forum_topic.cpp +++ b/Telegram/SourceFiles/data/data_forum_topic.cpp @@ -25,6 +25,10 @@ ForumTopic::ForumTopic(not_null forum, MsgId rootId) , _rootId(rootId) { } +not_null ForumTopic::channel() const { + return _forum->peer->asChannel(); +} + not_null ForumTopic::forum() const { return _forum; } @@ -38,6 +42,11 @@ void ForumTopic::applyTopic(const MTPForumTopic &topic) { const auto &data = topic.data(); applyTitle(qs(data.vtitle())); + if (const auto iconId = data.vicon_emoji_id()) { + applyIconId(iconId->v); + } else { + applyIconId(0); + } const auto pinned = _list->pinned(); #if 0 // #TODO forum pinned @@ -242,6 +251,10 @@ bool ForumTopic::lastServerMessageKnown() const { return _lastServerMessage.has_value(); } +QString ForumTopic::title() const { + return _title; +} + void ForumTopic::applyTitle(const QString &title) { if (_title == title || (isGeneral() && !_title.isEmpty())) { return; @@ -252,6 +265,15 @@ void ForumTopic::applyTitle(const QString &title) { updateChatListEntry(); } +DocumentId ForumTopic::iconId() const { + return _iconId; +} + +void ForumTopic::applyIconId(DocumentId iconId) { + _iconId = iconId; + updateChatListEntry(); +} + void ForumTopic::applyItemAdded(not_null item) { setLastMessage(item); } diff --git a/Telegram/SourceFiles/data/data_forum_topic.h b/Telegram/SourceFiles/data/data_forum_topic.h index 3d1054830..8a5f75bee 100644 --- a/Telegram/SourceFiles/data/data_forum_topic.h +++ b/Telegram/SourceFiles/data/data_forum_topic.h @@ -33,6 +33,7 @@ public: ForumTopic(const ForumTopic &) = delete; ForumTopic &operator=(const ForumTopic &) = delete; + [[nodiscard]] not_null channel() const; [[nodiscard]] not_null forum() const; [[nodiscard]] MsgId rootId() const; [[nodiscard]] bool isGeneral() const { @@ -62,7 +63,10 @@ public: [[nodiscard]] bool lastMessageKnown() const; [[nodiscard]] bool lastServerMessageKnown() const; + [[nodiscard]] QString title() const; void applyTitle(const QString &title); + [[nodiscard]] DocumentId iconId() const; + void applyIconId(DocumentId iconId); void applyItemAdded(not_null item); void applyItemRemoved(MsgId id); @@ -108,6 +112,7 @@ private: const MsgId _rootId = 0; QString _title; + DocumentId _iconId = 0; base::flat_set _titleWords; base::flat_set _titleFirstLetters; int _titleVersion = 0; diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index e54e6f13b..803394fec 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -54,6 +54,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/empty_userpic.h" #include "ui/unread_badge.h" #include "boxes/filters/edit_filter_box.h" +#include "boxes/peers/edit_forum_topic_box.h" #include "api/api_chat_filters.h" #include "base/qt/qt_common_adapters.h" #include "styles/style_dialogs.h" @@ -2411,7 +2412,8 @@ void InnerWidget::refreshEmptyLabel() { } else if (_emptyState == EmptyState::EmptyFolder) { editOpenedFilter(); } else if (_emptyState == EmptyState::EmptyForum) { - Data::ShowAddForumTopic(_controller, _openedForum->channel()); + _controller->show( + Box(NewForumTopicBox, _controller, _openedForum->history())); } }); _empty->setVisible(_state == WidgetState::Default); @@ -3287,9 +3289,10 @@ void InnerWidget::setupShortcuts() { }); request->check(Command::ChatSelf) && request->handle([=] { if (_openedForum) { - Data::ShowAddForumTopic( + _controller->show(Box( + NewForumTopicBox, _controller, - _openedForum->channel()); + _openedForum->history())); } else { _controller->content()->choosePeer( session().userPeerId(), diff --git a/Telegram/SourceFiles/dialogs/dialogs_key.cpp b/Telegram/SourceFiles/dialogs/dialogs_key.cpp index 1972a78bd..1c07f3622 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_key.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_key.cpp @@ -55,8 +55,17 @@ ForumTopic *Key::topic() const { return _value ? _value->asTopic() : nullptr; } +History *Key::parentHistory() const { + if (const auto result = history()) { + return result; + } else if (const auto child = topic()) { + return child->forum(); + } + return nullptr; +} + PeerData *Key::peer() const { - if (const auto history = this->history()) { + if (const auto history = parentHistory()) { return history->peer; } return nullptr; diff --git a/Telegram/SourceFiles/dialogs/dialogs_key.h b/Telegram/SourceFiles/dialogs/dialogs_key.h index c46ec448e..af91450f0 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_key.h +++ b/Telegram/SourceFiles/dialogs/dialogs_key.h @@ -42,6 +42,7 @@ public: History *history() const; Data::Folder *folder() const; Data::ForumTopic *topic() const; + History *parentHistory() const; PeerData *peer() const; inline bool operator<(const Key &other) const { diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 575fe3f5f..a51c45712 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -386,7 +386,9 @@ void Widget::chosenRow(const ChosenRow &row) { if (const auto topic = row.key.topic()) { controller()->showRepliesForMessage( topic->forum(), - topic->rootId()); + topic->rootId(), + ShowAtUnreadMsgId, + Window::SectionShow::Way::ClearStack); } else if (history && history->peer->isForum()) { controller()->openForum(history->peer->asChannel()); } else if (history) { diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 79067a927..331796851 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -453,7 +453,9 @@ Data::ForumTopic *RepliesWidget::lookupTopic() { ).done([=](const MTPmessages_ForumTopics &result) { if (const auto forum = _history->peer->forum()) { forum->applyReceivedTopics(result); - _topic = forum->topicFor(_rootId); + if ((_topic = forum->topicFor(_rootId))) { + refreshTopBarActiveChat(); + } } _resolveTopicRequestId = 0; }).fail([=] { @@ -1310,9 +1312,10 @@ SendMenu::Type RepliesWidget::sendMenuType() const { } void RepliesWidget::refreshTopBarActiveChat() { - const auto state = Dialogs::EntryState{ - .key = _history, - .section = Dialogs::EntryState::Section::Replies, + using namespace Dialogs; + const auto state = EntryState{ + .key = (_topic ? Key{ _topic } : Key{ _history }), + .section = EntryState::Section::Replies, .rootId = _rootId, .currentReplyToId = _composeControls->replyingToMessage().msg, }; diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp index 723f47193..a27e73979 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -7,8 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "history/view/history_view_top_bar_widget.h" -#include -#include #include "history/history.h" #include "history/view/history_view_send_action.h" #include "boxes/add_contact_box.h" @@ -48,6 +46,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_chat.h" #include "data/data_user.h" #include "data/data_changes.h" +#include "data/data_forum_topic.h" #include "data/data_send_action.h" #include "chat_helpers/emoji_interactions.h" #include "base/unixtime.h" @@ -474,7 +473,27 @@ void TopBarWidget::paintTopBar(Painter &p) { const auto now = crl::now(); const auto history = _activeChat.key.history(); const auto folder = _activeChat.key.folder(); - if (folder + if (const auto topic = _activeChat.key.topic()) { + p.setPen(st::dialogsNameFg); + topic->chatListNameText().drawElided( + p, + nameleft, + nametop, + availableWidth); + + p.setFont(st::dialogsTextFont); + if (!paintConnectingState(p, nameleft, statustop, width()) + && !paintSendAction( + p, + nameleft, + statustop, + availableWidth, + width(), + st::historyStatusFgTyping, + now)) { + paintStatus(p, nameleft, statustop, availableWidth, width()); + } + } else if (folder || history->peer->sharedMediaInfo() || (_activeChat.section == Section::Scheduled) || (_activeChat.section == Section::Pinned) @@ -673,6 +692,8 @@ void TopBarWidget::infoClicked() { return; } else if (key.folder()) { _controller->closeFolder(); + } else if (const auto topic = key.topic()) { + _controller->showSection(std::make_shared(topic)); } else if (key.peer()->isSelf()) { _controller->showSection(std::make_shared( key.peer(), @@ -706,6 +727,8 @@ void TopBarWidget::setActiveChat( _activeChat = activeChat; return; } + const auto topicChanged = (_activeChat.key.topic() + != activeChat.key.topic()); const auto peerChanged = (_activeChat.key.history() != activeChat.key.history()); @@ -715,12 +738,12 @@ void TopBarWidget::setActiveChat( _back->clearState(); update(); - if (peerChanged) { + if (peerChanged || topicChanged) { _titleBadge.unload(); _titleNameVersion = 0; _emojiInteractionSeen = nullptr; _activeChatLifetime.destroy(); - if (const auto history = _activeChat.key.history()) { + if (const auto history = _activeChat.key.parentHistory()) { session().changes().peerFlagsValue( history->peer, Data::PeerUpdate::Flag::GroupCall @@ -737,7 +760,9 @@ void TopBarWidget::setActiveChat( updateControlsVisibility(); updateControlsGeometry(); }, _activeChatLifetime); + } + if (const auto history = _activeChat.key.history()) { using InteractionSeen = ChatHelpers::EmojiInteractionSeen; _controller->emojiInteractions().seen( ) | rpl::filter([=](const InteractionSeen &seen) { diff --git a/Telegram/SourceFiles/info/info_content_widget.cpp b/Telegram/SourceFiles/info/info_content_widget.cpp index 87d04c6c2..b6e85972a 100644 --- a/Telegram/SourceFiles/info/info_content_widget.cpp +++ b/Telegram/SourceFiles/info/info_content_widget.cpp @@ -7,9 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "info/info_content_widget.h" -#include -#include -#include #include "window/window_session_controller.h" #include "ui/widgets/scroll_area.h" #include "ui/widgets/input_fields.h" @@ -23,7 +20,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/info_section_widget.h" #include "info/info_controller.h" #include "boxes/peer_list_box.h" +#include "data/data_chat.h" #include "data/data_session.h" +#include "data/data_forum_topic.h" +#include "history/history.h" #include "main/main_session.h" #include "styles/style_info.h" #include "styles/style_profile.h" @@ -323,7 +323,9 @@ rpl::producer ContentWidget::desiredBottomShadowVisibility() const { } Key ContentMemento::key() const { - if (const auto peer = this->peer()) { + if (const auto topic = this->topic()) { + return Key(topic); + } else if (const auto peer = this->peer()) { return Key(peer); } else if (const auto poll = this->poll()) { return Key(poll, pollContextId()); @@ -334,6 +336,12 @@ Key ContentMemento::key() const { } } +ContentMemento::ContentMemento(not_null topic) +: _peer(topic->forum()->peer) +, _migratedPeerId(_peer->migrateFrom() ? _peer->migrateFrom()->id : 0) +, _topic(topic) { +} + ContentMemento::ContentMemento(Settings::Tag settings) : _settingsSelf(settings.self.get()) { } diff --git a/Telegram/SourceFiles/info/info_content_widget.h b/Telegram/SourceFiles/info/info_content_widget.h index a64c6ec26..1201390ed 100644 --- a/Telegram/SourceFiles/info/info_content_widget.h +++ b/Telegram/SourceFiles/info/info_content_widget.h @@ -148,6 +148,7 @@ public: : _peer(peer) , _migratedPeerId(migratedPeerId) { } + explicit ContentMemento(not_null topic); explicit ContentMemento(Settings::Tag settings); explicit ContentMemento(Downloads::Tag downloads); ContentMemento(not_null poll, FullMsgId contextId) @@ -166,6 +167,9 @@ public: PeerId migratedPeerId() const { return _migratedPeerId; } + Data::ForumTopic *topic() const { + return _topic; + } UserData *settingsSelf() const { return _settingsSelf; } @@ -209,6 +213,7 @@ public: private: PeerData * const _peer = nullptr; const PeerId _migratedPeerId = 0; + Data::ForumTopic * const _topic = nullptr; UserData * const _settingsSelf = nullptr; PollData * const _poll = nullptr; const FullMsgId _pollContextId; diff --git a/Telegram/SourceFiles/info/info_controller.cpp b/Telegram/SourceFiles/info/info_controller.cpp index 96903e6b4..fe42ecab5 100644 --- a/Telegram/SourceFiles/info/info_controller.cpp +++ b/Telegram/SourceFiles/info/info_controller.cpp @@ -7,8 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "info/info_controller.h" -#include -#include #include "ui/search_field_controller.h" #include "data/data_shared_media.h" #include "info/info_content_widget.h" @@ -19,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_peer.h" #include "data/data_channel.h" #include "data/data_chat.h" +#include "data/data_forum_topic.h" #include "data/data_session.h" #include "data/data_media_types.h" #include "data/data_download_manager.h" @@ -32,6 +31,9 @@ namespace Info { Key::Key(not_null peer) : _value(peer) { } +Key::Key(not_null topic) : _value(topic) { +} + Key::Key(Settings::Tag settings) : _value(settings) { } @@ -45,6 +47,16 @@ Key::Key(not_null poll, FullMsgId contextId) PeerData *Key::peer() const { if (const auto peer = std::get_if>(&_value)) { return *peer; + } else if (const auto topic = this->topic()) { + return topic->forum()->peer; + } + return nullptr; +} + +Data::ForumTopic *Key::topic() const { + if (const auto topic = std::get_if>( + &_value)) { + return *topic; } return nullptr; } diff --git a/Telegram/SourceFiles/info/info_controller.h b/Telegram/SourceFiles/info/info_controller.h index e7f48d518..7d39facea 100644 --- a/Telegram/SourceFiles/info/info_controller.h +++ b/Telegram/SourceFiles/info/info_controller.h @@ -7,16 +7,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include #include "data/data_search_controller.h" #include "window/window_session_controller.h" +namespace Data { +class ForumTopic; +} // namespace Data + namespace Ui { class SearchFieldController; } // namespace Ui -namespace Info { -namespace Settings { +namespace Info::Settings { struct Tag { explicit Tag(not_null self) : self(self) { @@ -25,23 +27,27 @@ struct Tag { not_null self; }; -} // namespace Settings +} // namespace Info::Settings -namespace Downloads { +namespace Info::Downloads { struct Tag { }; -} // namespace Downloads +} // namespace Info::Downloads + +namespace Info { class Key { public: - Key(not_null peer); + explicit Key(not_null peer); + explicit Key(not_null topic); Key(Settings::Tag settings); Key(Downloads::Tag downloads); Key(not_null poll, FullMsgId contextId); PeerData *peer() const; + Data::ForumTopic *topic() const; UserData *settingsSelf() const; bool isDownloads() const; PollData *poll() const; @@ -54,6 +60,7 @@ private: }; std::variant< not_null, + not_null, Settings::Tag, Downloads::Tag, PollKey> _value; diff --git a/Telegram/SourceFiles/info/info_memento.cpp b/Telegram/SourceFiles/info/info_memento.cpp index ae5b488d1..07c77c474 100644 --- a/Telegram/SourceFiles/info/info_memento.cpp +++ b/Telegram/SourceFiles/info/info_memento.cpp @@ -33,6 +33,14 @@ Memento::Memento(not_null peer, Section section) : Memento(DefaultStack(peer, section)) { } +Memento::Memento(not_null topic) +: Memento(topic, Section::Type::Profile) { +} + +Memento::Memento(not_null topic, Section section) +: Memento(DefaultStack(topic, section)) { +} + Memento::Memento(Settings::Tag settings, Section section) : Memento(DefaultStack(settings, section)) { } @@ -53,6 +61,14 @@ std::vector> Memento::DefaultStack( return result; } +std::vector> Memento::DefaultStack( + not_null topic, + Section section) { + auto result = std::vector>(); + result.push_back(DefaultContent(topic, section)); + return result; +} + std::vector> Memento::DefaultStack( Settings::Tag settings, Section section) { @@ -111,6 +127,18 @@ std::shared_ptr Memento::DefaultContent( Unexpected("Wrong section type in Info::Memento::DefaultContent()"); } +std::shared_ptr Memento::DefaultContent( + not_null topic, + Section section) { + switch (section.type()) { + case Section::Type::Profile: + return std::make_shared(topic); + case Section::Type::Media: + return std::make_shared(topic, section.mediaType()); + } + Unexpected("Wrong section type in Info::Memento::DefaultContent()"); +} + object_ptr Memento::createWidget( QWidget *parent, not_null controller, diff --git a/Telegram/SourceFiles/info/info_memento.h b/Telegram/SourceFiles/info/info_memento.h index b215fc654..8c3ca2148 100644 --- a/Telegram/SourceFiles/info/info_memento.h +++ b/Telegram/SourceFiles/info/info_memento.h @@ -17,6 +17,10 @@ namespace Storage { enum class SharedMediaType : signed char; } // namespace Storage +namespace Data { +class ForumTopic; +} // namespace Data + namespace Ui { class ScrollArea; struct ScrollToRequest; @@ -38,6 +42,8 @@ class Memento final : public Window::SectionMemento { public: explicit Memento(not_null peer); Memento(not_null peer, Section section); + explicit Memento(not_null topic); + Memento(not_null topic, Section section); Memento(Settings::Tag settings, Section section); Memento(not_null poll, FullMsgId contextId); explicit Memento(std::vector> stack); @@ -72,6 +78,9 @@ private: static std::vector> DefaultStack( not_null peer, Section section); + static std::vector> DefaultStack( + not_null topic, + Section section); static std::vector> DefaultStack( Settings::Tag settings, Section section); @@ -82,6 +91,9 @@ private: static std::shared_ptr DefaultContent( not_null peer, Section section); + static std::shared_ptr DefaultContent( + not_null topic, + Section section); std::vector> _stack; diff --git a/Telegram/SourceFiles/info/info_wrap_widget.cpp b/Telegram/SourceFiles/info/info_wrap_widget.cpp index 7069f79a8..895e623f6 100644 --- a/Telegram/SourceFiles/info/info_wrap_widget.cpp +++ b/Telegram/SourceFiles/info/info_wrap_widget.cpp @@ -541,10 +541,13 @@ void WrapWidget::showTopBarMenu(bool check) { [=] { deleteAllDownloads(); }, &st::menuIconDelete); } else if (const auto peer = key().peer()) { + const auto topic = key().topic(); Window::FillDialogsEntryMenu( _controller->parentController(), Dialogs::EntryState{ - .key = peer->owner().history(peer), + .key = (topic + ? Dialogs::Key{ topic } + : Dialogs::Key{ peer->owner().history(peer) }), .section = Dialogs::EntryState::Section::Profile, }, addAction); diff --git a/Telegram/SourceFiles/info/media/info_media_widget.cpp b/Telegram/SourceFiles/info/media/info_media_widget.cpp index 32db7bee2..55325fb49 100644 --- a/Telegram/SourceFiles/info/media/info_media_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_widget.cpp @@ -18,8 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "styles/style_info.h" -namespace Info { -namespace Media { +namespace Info::Media { std::optional TypeToTabIndex(Type type) { switch (type) { @@ -61,6 +60,17 @@ Memento::Memento(not_null peer, PeerId migratedPeerId, Type type) } } +Memento::Memento(not_null topic, Type type) +: ContentMemento(topic) +, _type(type) { + _searchState.query.type = type; // #TODO forum search + _searchState.query.peerId = peer()->id; + _searchState.query.migratedPeerId = migratedPeerId(); + if (migratedPeerId()) { + _searchState.migratedList = Storage::SparseIdsList(); + } +} + Section Memento::section() const { return Section(_type); } @@ -162,5 +172,4 @@ void Widget::restoreState(not_null memento) { _inner->restoreState(memento); } -} // namespace Media -} // namespace Info +} // namespace Info::Media diff --git a/Telegram/SourceFiles/info/media/info_media_widget.h b/Telegram/SourceFiles/info/media/info_media_widget.h index f0a734ee2..3ba261d58 100644 --- a/Telegram/SourceFiles/info/media/info_media_widget.h +++ b/Telegram/SourceFiles/info/media/info_media_widget.h @@ -7,13 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include #include "info/info_content_widget.h" #include "storage/storage_shared_media.h" #include "data/data_search_controller.h" -namespace Info { -namespace Media { +namespace Data { +class ForumTopic; +} // namespace Data + +namespace Info::Media { using Type = Storage::SharedMediaType; @@ -24,8 +26,9 @@ class InnerWidget; class Memento final : public ContentMemento { public: - Memento(not_null controller); + explicit Memento(not_null controller); Memento(not_null peer, PeerId migratedPeerId, Type type); + Memento(not_null peer, Type type); using SearchState = Api::DelayedSearchController::SavedState; @@ -120,5 +123,4 @@ private: }; -} // namespace Media -} // namespace Info +} // namespace Info::Media diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp index ae404f063..6212de6eb 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp @@ -70,11 +70,7 @@ Cover::Cover( QWidget *parent, not_null peer, not_null controller) -: Cover(parent, peer, controller, NameValue( - peer -) | rpl::map([=](const TextWithEntities &name) { - return name.text; -})) { +: Cover(parent, peer, controller, NameValue(peer)) { } Cover::Cover( diff --git a/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.cpp b/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.cpp index 6c32c1628..d1e2d94b4 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.cpp @@ -171,6 +171,14 @@ EmojiStatusPanel::EmojiStatusPanel() = default; EmojiStatusPanel::~EmojiStatusPanel() = default; +void EmojiStatusPanel::setChooseFilter(Fn filter) { + _chooseFilter = std::move(filter); +} + +void EmojiStatusPanel::setChooseCallback(Fn callback) { + _chooseCallback = std::move(callback); +} + void EmojiStatusPanel::show( not_null controller, not_null button, @@ -278,32 +286,48 @@ void EmojiStatusPanel::create( return Chosen{ .animation = data.messageSendingFrom }; }); - const auto set = [=](Chosen chosen) { + const auto accept = [=](Chosen chosen) { Expects(chosen.until != Selector::kPickCustomTimeId); const auto owner = &controller->session().data(); startAnimation(owner, body, chosen.id, chosen.animation); - owner->emojiStatuses().set(chosen.id, chosen.until); + if (_chooseCallback) { + _chooseCallback(chosen.id); + } else { + owner->emojiStatuses().set(chosen.id, chosen.until); + } }; rpl::merge( std::move(statusChosen), std::move(emojiChosen) - ) | rpl::start_with_next([=](const Chosen chosen) { - if (chosen.id && !controller->session().premium()) { - ShowPremiumPreviewBox(controller, PremiumPreview::EmojiStatus); - } else if (chosen.until == Selector::kPickCustomTimeId) { + ) | rpl::filter([=](const Chosen &chosen) { + return filter(controller, chosen.id); + }) | rpl::start_with_next([=](const Chosen &chosen) { + if (chosen.until == Selector::kPickCustomTimeId) { _panel->hideAnimated(); controller->show(Box(PickUntilBox, [=](TimeId seconds) { - set({ chosen.id, base::unixtime::now() + seconds }); + accept({ chosen.id, base::unixtime::now() + seconds }); })); } else { - set(chosen); + accept(chosen); _panel->hideAnimated(); } }, _panel->lifetime()); } +bool EmojiStatusPanel::filter( + not_null controller, + DocumentId chosenId) const { + if (_chooseFilter) { + return _chooseFilter(chosenId); + } else if (chosenId && !controller->session().premium()) { + ShowPremiumPreviewBox(controller, PremiumPreview::EmojiStatus); + return false; + } + return true; +} + void EmojiStatusPanel::startAnimation( not_null owner, not_null body, diff --git a/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.h b/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.h index d0ab1b74b..cd7649443 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.h +++ b/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.h @@ -39,6 +39,9 @@ public: EmojiStatusPanel(); ~EmojiStatusPanel(); + void setChooseFilter(Fn filter); + void setChooseCallback(Fn callback); + void show( not_null controller, not_null button, @@ -50,6 +53,9 @@ private: class Animation; void create(not_null controller); + [[nodiscard]] bool filter( + not_null controller, + DocumentId chosenId) const; void startAnimation( not_null owner, @@ -58,6 +64,8 @@ private: Ui::MessageSendingAnimationFrom from); base::unique_qptr _panel; + Fn _chooseFilter; + Fn _chooseCallback; QPointer _panelButton; std::unique_ptr _animation; Data::CustomEmojiSizeTag _animationSizeTag = {}; diff --git a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp index eec7d74f5..a1837708d 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp @@ -51,6 +51,7 @@ InnerWidget::InnerWidget( , _controller(controller) , _peer(_controller->key().peer()) , _migrated(_controller->migrated()) +, _topic(_controller->key().topic()) , _content(setupContent(this)) { _content->heightValue( ) | rpl::start_with_next([this](int height) { @@ -67,15 +68,21 @@ object_ptr InnerWidget::setupContent( _cover = result->add(object_ptr( result, _peer, - _controller->parentController())); + _controller->parentController(), + _topic ? TitleValue(_topic) : NameValue(_peer))); _cover->showSection( ) | rpl::start_with_next([=](Section section) { _controller->showSection( std::make_shared(_peer, section)); }, _cover->lifetime()); _cover->setOnlineCount(rpl::single(0)); - auto details = SetupDetails(_controller, parent, _peer); - result->add(std::move(details)); + if (_topic) { + // #TODO forum + //result->add(setupSharedMedia(result.data())); + return result; + } + + result->add(SetupDetails(_controller, parent, _peer)); result->add(setupSharedMedia(result.data())); if (auto members = SetupChannelMembers(_controller, result.data(), _peer)) { result->add(std::move(members)); diff --git a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.h b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.h index 26891b153..7addfe5c5 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.h +++ b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.h @@ -10,7 +10,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rp_widget.h" #include "base/object_ptr.h" -#include +namespace Data { +class ForumTopic; +} // namespace Data namespace Window { class SessionController; @@ -65,6 +67,7 @@ private: const not_null _controller; const not_null _peer; PeerData * const _migrated = nullptr; + Data::ForumTopic * const _topic = nullptr; Members *_members = nullptr; Cover *_cover = nullptr; diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.cpp b/Telegram/SourceFiles/info/profile/info_profile_values.cpp index 28b606cab..b6a69aa4d 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_values.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_chat.h" #include "data/data_user.h" +#include "data/data_forum_topic.h" #include "data/data_session.h" #include "data/data_premium_limits.h" #include "boxes/peers/edit_peer_permissions_box.h" @@ -74,13 +75,15 @@ void StripExternalLinks(TextWithEntities &text) { } // namespace -rpl::producer NameValue(not_null peer) { +rpl::producer NameValue(not_null peer) { return peer->session().changes().peerFlagsValue( peer, UpdateFlag::Name - ) | rpl::map([=] { - return peer->name(); - }) | Ui::Text::ToWithEntities(); + ) | rpl::map([=] { return peer->name(); }); +} + +rpl::producer TitleValue(not_null topic) { + return rpl::single(topic->title()); // #TODO forum title changes } rpl::producer PhoneValue(not_null user) { diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.h b/Telegram/SourceFiles/info/profile/info_profile_values.h index b7ec2d344..1f70f086a 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.h +++ b/Telegram/SourceFiles/info/profile/info_profile_values.h @@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL struct ChannelLocation; +namespace Data { +class ForumTopic; +} // namespace Data + namespace Main { class Session; } // namespace Main @@ -28,8 +32,7 @@ namespace Storage { enum class SharedMediaType : signed char; } // namespace Storage -namespace Info { -namespace Profile { +namespace Info::Profile { inline auto ToSingleLine() { return rpl::map([](const QString &text) { @@ -40,8 +43,9 @@ inline auto ToSingleLine() { rpl::producer> MigratedOrMeValue( not_null peer); -[[nodiscard]] rpl::producer NameValue( - not_null peer); +[[nodiscard]] rpl::producer NameValue(not_null peer); +[[nodiscard]] rpl::producer TitleValue( + not_null topic); [[nodiscard]] rpl::producer PhoneValue( not_null user); [[nodiscard]] rpl::producer PhoneOrHiddenValue( @@ -95,5 +99,4 @@ enum class BadgeType; [[nodiscard]] rpl::producer EmojiStatusIdValue( not_null peer); -} // namespace Profile -} // namespace Info +} // namespace Info::Profile diff --git a/Telegram/SourceFiles/info/profile/info_profile_widget.cpp b/Telegram/SourceFiles/info/profile/info_profile_widget.cpp index f4b3133a2..1d51e0a6d 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_widget.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_widget.cpp @@ -17,8 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "info/info_controller.h" -namespace Info { -namespace Profile { +namespace Info::Profile { Memento::Memento(not_null controller) : Memento( @@ -30,6 +29,10 @@ Memento::Memento(not_null peer, PeerId migratedPeerId) : ContentMemento(peer, migratedPeerId) { } +Memento::Memento(not_null topic) +: ContentMemento(topic) { +} + Section Memento::section() const { return Section(Section::Type::Profile); } @@ -38,9 +41,7 @@ object_ptr Memento::createWidget( QWidget *parent, not_null controller, const QRect &geometry) { - auto result = object_ptr( - parent, - controller); + auto result = object_ptr(parent, controller); result->setInternalState(geometry, this); return result; } @@ -55,9 +56,7 @@ std::unique_ptr Memento::membersState() { Memento::~Memento() = default; -Widget::Widget( - QWidget *parent, - not_null controller) +Widget::Widget(QWidget *parent, not_null controller) : ContentWidget(parent, controller) { controller->setSearchEnabledByContent(false); @@ -81,6 +80,9 @@ void Widget::setInnerFocus() { } rpl::producer Widget::title() { + if (const auto topic = controller()->key().topic()) { + return rpl::single(u"Topic Info"_q); // #TODO forum lang + } const auto peer = controller()->key().peer(); if (const auto user = peer->asUser()) { return (user->isBot() && !user->isSupport()) @@ -132,5 +134,4 @@ void Widget::restoreState(not_null memento) { scrollTopRestore(memento->scrollTop()); } -} // namespace Profile -} // namespace Info +} // namespace Info::Profile diff --git a/Telegram/SourceFiles/info/profile/info_profile_widget.h b/Telegram/SourceFiles/info/profile/info_profile_widget.h index c61c833b2..0781a7082 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_widget.h +++ b/Telegram/SourceFiles/info/profile/info_profile_widget.h @@ -7,19 +7,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include #include "info/info_content_widget.h" -namespace Info { -namespace Profile { +namespace Data { +class ForumTopic; +} // namespace Data + +namespace Info::Profile { class InnerWidget; struct MembersState; class Memento final : public ContentMemento { public: - Memento(not_null controller); + explicit Memento(not_null controller); Memento(not_null peer, PeerId migratedPeerId); + explicit Memento(not_null topic); object_ptr createWidget( QWidget *parent, @@ -40,9 +43,7 @@ private: class Widget final : public ContentWidget { public: - Widget( - QWidget *parent, - not_null controller); + Widget(QWidget *parent, not_null controller); bool showInternal( not_null memento) override; @@ -65,5 +66,4 @@ private: }; -} // namespace Profile -} // namespace Info +} // namespace Info::Profile diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp index 3b8eaca39..83f2e0cc4 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp @@ -818,11 +818,7 @@ void AttachWebView::show( } Payments::CheckoutProcess::Start(session, slug, reactivate); }; - auto title = Info::Profile::NameValue( - _bot - ) | rpl::map([](const TextWithEntities &value) { - return value.text; - }); + auto title = Info::Profile::NameValue(_bot); ActiveWebViews().emplace(this); using Button = Ui::BotWebView::MenuButton; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index a04a1ed0d..6e0fa843f 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -2409,6 +2409,8 @@ auto MainWidget::thirdSectionForCurrentMainSection( -> std::shared_ptr { if (_thirdSectionFromStack) { return std::move(_thirdSectionFromStack); + } else if (const auto topic = key.topic()) { + return std::make_shared(topic); } else if (const auto peer = key.peer()) { return std::make_shared( peer, diff --git a/Telegram/SourceFiles/settings/settings_information.cpp b/Telegram/SourceFiles/settings/settings_information.cpp index fffe15f4c..8b1a256f7 100644 --- a/Telegram/SourceFiles/settings/settings_information.cpp +++ b/Telegram/SourceFiles/settings/settings_information.cpp @@ -313,7 +313,7 @@ void SetupPhoto( ) | rpl::start_with_next([=]( int max, int photoWidth, - const TextWithEntities&, + const QString&, int statusWidth) { photo->moveToLeft( (max - photoWidth) / 2, @@ -398,7 +398,7 @@ void SetupRows( AddRow( container, tr::lng_settings_name_label(), - Info::Profile::NameValue(self), + Info::Profile::NameValue(self) | Ui::Text::ToWithEntities(), tr::lng_profile_copy_fullname(tr::now), [=] { controller->show(Box(self)); }, { &st::settingsIconUser, kIconLightBlue }); diff --git a/Telegram/SourceFiles/settings/settings_main.cpp b/Telegram/SourceFiles/settings/settings_main.cpp index 3e8a92489..55aca1303 100644 --- a/Telegram/SourceFiles/settings/settings_main.cpp +++ b/Telegram/SourceFiles/settings/settings_main.cpp @@ -176,8 +176,8 @@ void Cover::setupChildGeometry() { void Cover::initViewers() { Info::Profile::NameValue( _user - ) | rpl::start_with_next([=](const TextWithEntities &value) { - _name->setText(value.text); + ) | rpl::start_with_next([=](const QString &name) { + _name->setText(name); refreshNameGeometry(width()); }, lifetime()); diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp index f3021e77f..ae6d3c336 100644 --- a/Telegram/SourceFiles/settings/settings_premium.cpp +++ b/Telegram/SourceFiles/settings/settings_premium.cpp @@ -671,7 +671,7 @@ TopBarUser::TopBarUser( Info::Profile::NameValue(peer) ) | rpl::start_with_next([=]( DocumentData *document, - TextWithEntities name) { + const QString &name) { if (document) { _emojiStatus = std::make_unique( document, @@ -713,7 +713,7 @@ TopBarUser::TopBarUser( _emojiStatus = nullptr; } - updateTitle(document, name, controller); + updateTitle(document, { name }, controller); updateAbout(document); auto event = QResizeEvent(size(), size()); diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 127ec62e6..736fed808 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/report_messages_box.h" #include "boxes/peers/add_bot_to_chat_box.h" #include "boxes/peers/add_participants_box.h" +#include "boxes/peers/edit_forum_topic_box.h" #include "boxes/peers/edit_contact_box.h" #include "ui/boxes/report_box.h" #include "ui/toast/toast.h" @@ -63,6 +64,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_chat.h" #include "data/data_drafts.h" +#include "data/data_forum_topic.h" #include "data/data_user.h" #include "data/data_scheduled_messages.h" #include "data/data_histories.h" @@ -188,6 +190,7 @@ private: void addClearHistory(); void addDeleteChat(); void addLeaveChat(); + void addManageTopic(); void addManageChat(); void addCreatePoll(); void addThemeEdit(); @@ -207,6 +210,7 @@ private: not_null _controller; Dialogs::EntryState _request; PeerData *_peer = nullptr; + Data::ForumTopic *_topic = nullptr; Data::Folder *_folder = nullptr; const PeerMenuCallback &_addAction; @@ -326,12 +330,13 @@ Filler::Filler( : _controller(controller) , _request(request) , _peer(request.key.peer()) +, _topic(request.key.topic()) , _folder(request.key.folder()) , _addAction(addAction) { } void Filler::addHidePromotion() { - const auto history = _peer->owner().historyLoaded(_peer); + const auto history = _request.key.history(); if (!history || !history->useTopPromotion() || history->topPromotionType().isEmpty()) { @@ -346,6 +351,10 @@ void Filler::addHidePromotion() { } void Filler::addTogglePin() { + if (!_peer) { + // #TODO forum pin + return; + } const auto controller = _controller; const auto filterId = _request.filterId; const auto peer = _peer; @@ -400,11 +409,12 @@ void Filler::addSupportInfo() { } void Filler::addInfo() { - if (_peer->isSelf() || _peer->isRepliesChat()) { + if (_peer && (_peer->isSelf() || _peer->isRepliesChat())) { return; } else if (_controller->adaptive().isThreeColumn()) { - const auto history = _controller->activeChatCurrent().history(); - if (history && history->peer == _peer) { + const auto peer = _controller->activeChatCurrent().peer(); + const auto topic = _controller->activeChatCurrent().topic(); + if ((peer && peer == _peer) || (topic && topic == _topic)) { if (Core::App().settings().thirdSectionInfoEnabled() || Core::App().settings().tabbedReplacedWithInfo()) { return; @@ -412,6 +422,7 @@ void Filler::addInfo() { } } const auto controller = _controller; + const auto id = _topic ? _topic->rootId() : 0; const auto peer = _peer; const auto text = (peer->isChat() || peer->isMegagroup()) ? tr::lng_context_view_group(tr::now) @@ -468,6 +479,9 @@ void Filler::addToggleUnreadMark() { } void Filler::addToggleArchive() { + if (!_peer) { + return; + } const auto peer = _peer; const auto history = peer->owner().historyLoaded(peer); if (history && history->useTopPromotion()) { @@ -533,7 +547,7 @@ void Filler::addDeleteChat() { void Filler::addLeaveChat() { const auto channel = _peer->asChannel(); - if (!channel || !channel->amIn()) { + if (_topic || !channel || !channel->amIn()) { return; } _addAction({ @@ -618,7 +632,7 @@ void Filler::addViewDiscussion() { } void Filler::addExportChat() { - if (!_peer->canExportChatHistory()) { + if (_topic || !_peer->canExportChatHistory()) { return; } const auto peer = _peer; @@ -733,6 +747,19 @@ void Filler::addDeleteContact() { }); } +void Filler::addManageTopic() { + if (!_topic) { + return; + } + // #TODO forum lang + const auto history = _topic->forum(); + const auto rootId = _topic->rootId(); + const auto navigation = _controller; + _addAction(u"Edit topic"_q, [=] { + navigation->show(Box(EditForumTopicBox, navigation, history, rootId)); + }, &st::menuIconEdit); +} + void Filler::addManageChat() { if (!EditPeerInfoBox::Available(_peer)) { return; @@ -794,6 +821,9 @@ void Filler::addThemeEdit() { } void Filler::addTTLSubmenu(bool addSeparator) { + if (_topic) { + return; // #TODO later forum + } const auto validator = TTLMenu::TTLValidator( std::make_shared(_controller), _peer); @@ -887,6 +917,7 @@ void Filler::fillProfileActions() { addGiftPremium(); addBotToGroup(); addNewMembers(); + addManageTopic(); addManageChat(); addViewDiscussion(); addExportChat(); @@ -897,6 +928,11 @@ void Filler::fillProfileActions() { } void Filler::fillRepliesActions() { + if (_topic) { + addInfo(); + addManageTopic(); + addManageChat(); + } addCreatePoll(); } diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index b76b4fbea..8be9e9f5d 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -478,17 +478,6 @@ void SessionNavigation::showRepliesForMessage( _api.request(base::take(_showingRepliesRequestId)).cancel(); const auto postPeer = history->peer; - //const auto item = _session->data().message(postPeer, rootId); - //if (!commentId && (!item || !item->repliesAreComments())) { - // showSection(std::make_shared(history, rootId)); - // return; - //} else if (const auto id = item ? item->commentsItemId() : FullMsgId()) { - // if (const auto commentsItem = _session->data().message(id)) { - // showSection( - // std::make_shared(commentsItem)); - // return; - // } - //} _showingRepliesHistory = history; _showingRepliesRootId = rootId; _showingRepliesRequestId = _api.request( @@ -537,9 +526,11 @@ void SessionNavigation::showRepliesForMessage( post->setRepliesOutboxReadTill( data.vread_outbox_max_id().value_or_empty()); } - showSection(std::make_shared( - item, - commentId)); + showSection( + std::make_shared( + item, + commentId), + params); } }); }).fail([=](const MTP::Error &error) { @@ -1577,9 +1568,8 @@ void SessionController::cancelUploadLayer(not_null item) { void SessionController::showSection( std::shared_ptr memento, const SectionShow ¶ms) { - if (!params.thirdColumn && widget()->showSectionInExistingLayer( - memento.get(), - params)) { + if (!params.thirdColumn + && widget()->showSectionInExistingLayer(memento.get(), params)) { return; } content()->showSection(std::move(memento), params);