From 72354f52d49a84a49ce5d845f4030653db348e20 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 24 Oct 2022 15:52:28 +0400 Subject: [PATCH] Improve top bars in forums, in narrow column. --- .../SourceFiles/dialogs/dialogs_widget.cpp | 9 +- .../SourceFiles/history/history_widget.cpp | 9 +- .../view/history_view_contact_status.cpp | 93 ++++++++++++++++--- .../view/history_view_contact_status.h | 6 +- .../view/history_view_group_call_bar.cpp | 16 +++- .../view/history_view_group_call_bar.h | 3 +- .../view/history_view_requests_bar.cpp | 24 ++++- .../history/view/history_view_requests_bar.h | 3 +- .../info/profile/info_profile_values.cpp | 6 +- .../SourceFiles/ui/chat/group_call_bar.cpp | 23 ++++- Telegram/SourceFiles/ui/chat/group_call_bar.h | 2 + Telegram/SourceFiles/ui/chat/requests_bar.cpp | 15 +-- Telegram/SourceFiles/ui/menu_icons.style | 1 + .../window/window_session_controller.cpp | 12 +-- 14 files changed, 171 insertions(+), 51 deletions(-) diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index c785cd782..fdffb40e7 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -744,17 +744,20 @@ void Widget::refreshTopBars() { _forumReportBar = std::make_unique( controller(), this, - _openedForum); + _openedForum, + true); _forumRequestsBar = std::make_unique( this, HistoryView::RequestsBarContentByPeer( _openedForum, - st::historyRequestsUserpics.size)); + st::historyRequestsUserpics.size, + true)); _forumGroupCallBar = std::make_unique( this, HistoryView::GroupCallBarContentByPeer( _openedForum, - st::historyGroupCallUserpics.size), + st::historyGroupCallUserpics.size, + true), Core::App().appDeactivatedValue()); _forumTopShadow = std::make_unique(this); diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 398e16855..0e32ce8a4 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -2126,7 +2126,8 @@ void HistoryWidget::showHistory( _contactStatus = std::make_unique( controller(), this, - _peer); + _peer, + false); _contactStatus->bar().heightValue() | rpl::start_with_next([=] { updateControlsGeometry(); }, _contactStatus->bar().lifetime()); @@ -6434,7 +6435,8 @@ void HistoryWidget::setupGroupCallBar() { this, HistoryView::GroupCallBarContentByPeer( peer, - st::historyGroupCallUserpics.size), + st::historyGroupCallUserpics.size, + false), Core::App().appDeactivatedValue()); controller()->adaptive().oneColumnValue( @@ -6486,7 +6488,8 @@ void HistoryWidget::setupRequestsBar() { this, HistoryView::RequestsBarContentByPeer( peer, - st::historyRequestsUserpics.size)); + st::historyRequestsUserpics.size, + false)); controller()->adaptive().oneColumnValue( ) | rpl::start_with_next([=](bool one) { diff --git a/Telegram/SourceFiles/history/view/history_view_contact_status.cpp b/Telegram/SourceFiles/history/view/history_view_contact_status.cpp index b306fb908..20411811c 100644 --- a/Telegram/SourceFiles/history/view/history_view_contact_status.cpp +++ b/Telegram/SourceFiles/history/view/history_view_contact_status.cpp @@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "data/data_session.h" #include "data/data_forum_topic.h" +#include "data/data_peer_values.h" #include "data/stickers/data_custom_emoji.h" #include "settings/settings_premium.h" #include "window/window_peer_menu.h" @@ -41,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_chat.h" #include "styles/style_layers.h" #include "styles/style_info.h" +#include "styles/style_menu_icons.h" namespace HistoryView { namespace { @@ -111,6 +113,23 @@ namespace { }) | rpl::flatten_latest() | rpl::distinct_until_changed(); } +[[nodiscard]] object_ptr MakeIconButton( + QWidget *parent, + const style::icon &icon) { + auto result = object_ptr( + parent, + st::historyContactStatusButton.ripple); + const auto raw = result.data(); + raw->paintRequest( + ) | rpl::start_with_next([=, &icon] { + auto p = QPainter(raw); + p.fillRect(raw->rect(), st::historyContactStatusButton.bgColor); + raw->paintRipple(p, 0, 0); + icon.paintInCenter(p, raw->rect()); + }, raw->lifetime()); + return result; +} + } // namespace class ContactStatus::BgButton final : public Ui::RippleButton { @@ -153,15 +172,18 @@ private: QString _name; object_ptr _add; object_ptr _unarchive; + object_ptr _unarchiveIcon; object_ptr _block; object_ptr _share; object_ptr _report; + object_ptr _reportIcon; object_ptr _close; object_ptr _requestChatBg; object_ptr _requestChatInfo; object_ptr> _emojiStatusInfo; object_ptr _emojiStatusShadow; bool _emojiStatusRepaintScheduled = false; + bool _narrow = false; rpl::event_stream<> _emojiStatusClicks; }; @@ -200,6 +222,7 @@ ContactStatus::Bar::Bar( this, tr::lng_new_contact_unarchive(tr::now).toUpper(), st::historyContactStatusButton) +, _unarchiveIcon(MakeIconButton(this, st::menuIconUnarchive)) , _block( this, tr::lng_new_contact_block(tr::now).toUpper(), @@ -212,6 +235,7 @@ ContactStatus::Bar::Bar( this, QString(), st::historyContactStatusBlock) +, _reportIcon(MakeIconButton(this, st::menuIconReportAttention)) , _close(this, st::historyReplyCancel) , _requestChatBg(this, st::historyContactStatusButton) , _requestChatInfo( @@ -242,14 +266,18 @@ void ContactStatus::Bar::showState( 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); + const auto unarchive = (type == Type::UnarchiveOrBlock) + || (type == Type::UnarchiveOrReport); + _unarchive->setVisible(!_narrow && unarchive); + _unarchiveIcon->setVisible(_narrow && unarchive); _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); + _close->setVisible(!_narrow && type != Type::RequestChatInfo); + const auto report = (type == Type::ReportSpam) + || (type == Type::UnarchiveOrReport); + _report->setVisible(!_narrow && report); + _reportIcon->setVisible(_narrow && report); _requestChatInfo->setVisible(type == Type::RequestChatInfo); _requestChatBg->setVisible(type == Type::RequestChatInfo); const auto has = !status.empty(); @@ -291,7 +319,10 @@ void ContactStatus::Bar::showState( } rpl::producer<> ContactStatus::Bar::unarchiveClicks() const { - return _unarchive->clicks() | rpl::to_empty; + return rpl::merge( + _unarchive->clicks(), + _unarchiveIcon->clicks() + ) | rpl::to_empty; } rpl::producer<> ContactStatus::Bar::addClicks() const { @@ -307,7 +338,10 @@ rpl::producer<> ContactStatus::Bar::shareClicks() const { } rpl::producer<> ContactStatus::Bar::reportClicks() const { - return _report->clicks() | rpl::to_empty; + return rpl::merge( + _report->clicks(), + _reportIcon->clicks() + ) | rpl::to_empty; } rpl::producer<> ContactStatus::Bar::closeClicks() const { @@ -323,7 +357,27 @@ rpl::producer<> ContactStatus::Bar::emojiStatusClicks() const { } int ContactStatus::Bar::resizeGetHeight(int newWidth) { - _close->moveToRight(0, 0); + _close->moveToRight(0, 0, newWidth); + const auto narrow = (newWidth < _close->width() * 2); + if (_narrow != narrow) { + _narrow = narrow; + _close->setVisible(_requestChatInfo->isHidden() && !_narrow); + const auto report = !_report->isHidden() || !_reportIcon->isHidden(); + _report->setVisible(!_narrow && report); + _reportIcon->setVisible(_narrow && report); + const auto unarchive = !_unarchive->isHidden() + || !_unarchiveIcon->isHidden(); + _unarchive->setVisible(!_narrow && unarchive); + _unarchiveIcon->setVisible(_narrow && unarchive); + } + + if (!_unarchiveIcon->isHidden()) { + const auto half = newWidth / 2; + _unarchiveIcon->setGeometry(0, 0, half, _close->height()); + _reportIcon->setGeometry(half, 0, newWidth - half, _close->height()); + } else if (!_reportIcon->isHidden()) { + _reportIcon->setGeometry(0, 0, newWidth, _close->height()); + } const auto closeWidth = _close->width(); const auto closeHeight = _close->height(); @@ -489,11 +543,12 @@ rpl::producer SlidingBar::heightValue() const { ContactStatus::ContactStatus( not_null window, not_null parent, - not_null peer) + not_null peer, + bool showInForum) : _controller(window) , _inner(Ui::CreateChild(parent.get(), peer->shortName())) , _bar(parent, object_ptr::fromRaw(_inner)) { - setupState(peer); + setupState(peer, showInForum); setupHandlers(peer); } @@ -555,7 +610,7 @@ auto ContactStatus::PeerState(not_null peer) }); } -void ContactStatus::setupState(not_null peer) { +void ContactStatus::setupState(not_null peer, bool showInForum) { if (!BarCurrentlyHidden(peer)) { peer->session().api().requestPeerSettings(peer); } @@ -567,13 +622,21 @@ void ContactStatus::setupState(not_null peer) { }; }; _inner->showState({}, {}, _context); + const auto channel = peer->asChannel(); rpl::combine( PeerState(peer), - PeerCustomStatus(peer) - ) | rpl::start_with_next([=](State state, TextWithEntities status) { + PeerCustomStatus(peer), + ((channel && !showInForum) + ? Data::PeerFlagValue(channel, ChannelData::Flag::Forum) + : rpl::single(false)) + ) | rpl::start_with_next([=]( + State state, + TextWithEntities status, + bool hiddenByForum) { _state = state; _status = status; - if (state.type == State::Type::None) { + _hiddenByForum = hiddenByForum; + if (state.type == State::Type::None || hiddenByForum) { _bar.toggleContent(false); } else { _inner->showState(state, std::move(status), _context); @@ -769,7 +832,7 @@ void ContactStatus::setupEmojiStatusHandler(not_null peer) { void ContactStatus::show() { if (!_shown) { _shown = true; - if (_state.type != State::Type::None) { + if (_state.type != State::Type::None && !_hiddenByForum) { _inner->showState(_state, _status, _context); _bar.toggleContent(true); } diff --git a/Telegram/SourceFiles/history/view/history_view_contact_status.h b/Telegram/SourceFiles/history/view/history_view_contact_status.h index 2967bfb23..82d0fdde6 100644 --- a/Telegram/SourceFiles/history/view/history_view_contact_status.h +++ b/Telegram/SourceFiles/history/view/history_view_contact_status.h @@ -69,7 +69,8 @@ public: ContactStatus( not_null controller, not_null parent, - not_null peer); + not_null peer, + bool showInForum); void show(); @@ -98,7 +99,7 @@ private: TimeId requestDate = 0; }; - void setupState(not_null peer); + void setupState(not_null peer, bool showInForum); void setupHandlers(not_null peer); void setupAddHandler(not_null user); void setupBlockHandler(not_null user); @@ -117,6 +118,7 @@ private: Fn customEmojiRepaint)> _context; QPointer _inner; SlidingBar _bar; + bool _hiddenByForum = false; bool _shown = false; }; diff --git a/Telegram/SourceFiles/history/view/history_view_group_call_bar.cpp b/Telegram/SourceFiles/history/view/history_view_group_call_bar.cpp index 03a1fbe6d..d2ca0172a 100644 --- a/Telegram/SourceFiles/history/view/history_view_group_call_bar.cpp +++ b/Telegram/SourceFiles/history/view/history_view_group_call_bar.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_user.h" #include "data/data_changes.h" #include "data/data_group_call.h" +#include "data/data_peer_values.h" #include "main/main_session.h" #include "ui/chat/group_call_bar.h" #include "ui/chat/group_call_userpics.h" @@ -348,15 +349,22 @@ rpl::producer GroupCallBarContentByCall( rpl::producer GroupCallBarContentByPeer( not_null peer, - int userpicSize) { + int userpicSize, + bool showInForum) { + const auto channel = peer->asChannel(); return rpl::combine( peer->session().changes().peerFlagsValue( peer, Data::PeerUpdate::Flag::GroupCall), - Core::App().calls().currentGroupCallValue() - ) | rpl::map([=](const auto&, Calls::GroupCall *current) { + Core::App().calls().currentGroupCallValue(), + ((showInForum || !channel) + ? rpl::single(false) + : Data::PeerFlagValue(channel, ChannelData::Flag::Forum)) + ) | rpl::map([=](auto, Calls::GroupCall *current, bool hiddenByForum) { const auto call = peer->groupCall(); - return (call && (!current || current->peer() != peer)) + return (call + && !hiddenByForum + && (!current || current->peer() != peer)) ? call : nullptr; }) | rpl::distinct_until_changed( diff --git a/Telegram/SourceFiles/history/view/history_view_group_call_bar.h b/Telegram/SourceFiles/history/view/history_view_group_call_bar.h index 7cdfdd55c..90814c7f1 100644 --- a/Telegram/SourceFiles/history/view/history_view_group_call_bar.h +++ b/Telegram/SourceFiles/history/view/history_view_group_call_bar.h @@ -44,7 +44,8 @@ void GenerateUserpicsInRow( [[nodiscard]] auto GroupCallBarContentByPeer( not_null peer, - int userpicSize) + int userpicSize, + bool showInForum) -> rpl::producer; } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_requests_bar.cpp b/Telegram/SourceFiles/history/view/history_view_requests_bar.cpp index 17e1b7026..298f43b2a 100644 --- a/Telegram/SourceFiles/history/view/history_view_requests_bar.cpp +++ b/Telegram/SourceFiles/history/view/history_view_requests_bar.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_changes.h" #include "data/data_session.h" +#include "data/data_peer_values.h" #include "main/main_session.h" #include "ui/chat/requests_bar.h" #include "ui/chat/group_call_userpics.h" @@ -23,7 +24,8 @@ namespace HistoryView { rpl::producer RequestsBarContentByPeer( not_null peer, - int userpicSize) { + int userpicSize, + bool showInForum) { struct State { explicit State(not_null peer) : peer(peer) { @@ -106,8 +108,9 @@ rpl::producer RequestsBarContentByPeer( auto state = lifetime.make_state(peer); const auto pushNext = [=](bool now = false) { - if (std::min(state->current.count, kRecentRequestsLimit) - != state->users.size()) { + if ((!showInForum && peer->isForum()) + || (std::min(state->current.count, kRecentRequestsLimit) + != state->users.size())) { return; } else if (now) { state->pushScheduled = false; @@ -124,6 +127,21 @@ rpl::producer RequestsBarContentByPeer( }); }; + if (!showInForum) { + if (const auto channel = peer->asChannel()) { + Data::PeerFlagValue( + channel, + ChannelData::Flag::Forum + ) | rpl::start_with_next([=](bool hiddenByForum) { + if (hiddenByForum) { + consumer.put_next({}); + } else { + pushNext(); + } + }, lifetime); + } + } + peer->session().downloaderTaskFinished( ) | rpl::filter([=] { return state->someUserpicsNotLoaded; diff --git a/Telegram/SourceFiles/history/view/history_view_requests_bar.h b/Telegram/SourceFiles/history/view/history_view_requests_bar.h index 1fa4a289f..71252c5dc 100644 --- a/Telegram/SourceFiles/history/view/history_view_requests_bar.h +++ b/Telegram/SourceFiles/history/view/history_view_requests_bar.h @@ -19,6 +19,7 @@ inline constexpr auto kRecentRequestsLimit = 3; [[nodiscard]] rpl::producer RequestsBarContentByPeer( not_null peer, - int userpicSize); + int userpicSize, + bool showInForum); } // namespace HistoryView diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.cpp b/Telegram/SourceFiles/info/profile/info_profile_values.cpp index 99555f76e..443333a3e 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_values.cpp @@ -437,12 +437,8 @@ rpl::producer RestrictionsCountValue(not_null peer) { return countOfRestrictions({}, chat->defaultRestrictions()); }); } else if (const auto channel = peer->asChannel()) { - auto forumValue = channel->flagsValue( - ) | rpl::filter([](const ChannelData::Flags::Change &change) { - return (change.diff & ChannelData::Flag::Forum); - }); return rpl::combine( - std::move(forumValue), + Data::PeerFlagValue(channel, ChannelData::Flag::Forum), channel->session().changes().peerFlagsValue( channel, UpdateFlag::Rights) diff --git a/Telegram/SourceFiles/ui/chat/group_call_bar.cpp b/Telegram/SourceFiles/ui/chat/group_call_bar.cpp index ec79c490d..0ea7ad871 100644 --- a/Telegram/SourceFiles/ui/chat/group_call_bar.cpp +++ b/Telegram/SourceFiles/ui/chat/group_call_bar.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_chat.h" #include "styles/style_calls.h" #include "styles/style_info.h" // st::topBarArrowPadding, like TopBarWidget. +#include "styles/style_window.h" // st::columnMinimalWidthLeft #include "styles/palette.h" #include @@ -242,12 +243,20 @@ void GroupCallBar::setupRightButton(not_null button) { rpl::combine( _inner->widthValue(), button->widthValue() - ) | rpl::start_with_next([=](int outerWidth, int) { + ) | rpl::start_with_next([=](int outerWidth, int buttonWidth) { // Skip shadow of the bar above. const auto top = (st::historyReplyHeight - st::lineWidth - button->height()) / 2 + st::lineWidth; - button->moveToRight(top, top, outerWidth); + const auto narrow = (outerWidth < st::columnMinimalWidthLeft); + if (narrow) { + button->moveToLeft( + (outerWidth - buttonWidth) / 2, + top, + outerWidth); + } else { + button->moveToRight(top, top, outerWidth); + } }, button->lifetime()); button->clicks() | rpl::start_to_stream(_joinClicks, button->lifetime()); @@ -256,6 +265,14 @@ void GroupCallBar::setupRightButton(not_null button) { void GroupCallBar::paint(Painter &p) { p.fillRect(_inner->rect(), st::historyComposeAreaBg); + const auto narrow = (_inner->width() < st::columnMinimalWidthLeft); + if (!narrow) { + paintTitleAndStatus(p); + paintUserpics(p); + } +} + +void GroupCallBar::paintTitleAndStatus(Painter &p) { const auto left = st::topBarArrowPadding.right(); const auto titleTop = st::msgReplyPadding.top(); const auto textTop = titleTop + st::msgServiceNameFont->height; @@ -325,7 +342,9 @@ void GroupCallBar::paint(Painter &p) { lt_count_decimal, _content.count) : tr::lng_group_call_no_members(tr::now))); +} +void GroupCallBar::paintUserpics(Painter &p) { const auto size = st::historyGroupCallUserpics.size; // Skip shadow of the bar above. const auto top = (st::historyReplyHeight - st::lineWidth - size) / 2 diff --git a/Telegram/SourceFiles/ui/chat/group_call_bar.h b/Telegram/SourceFiles/ui/chat/group_call_bar.h index d19a78a25..a31042cb4 100644 --- a/Telegram/SourceFiles/ui/chat/group_call_bar.h +++ b/Telegram/SourceFiles/ui/chat/group_call_bar.h @@ -95,6 +95,8 @@ private: void setupInner(); void setupRightButton(not_null button); void paint(Painter &p); + void paintTitleAndStatus(Painter &p); + void paintUserpics(Painter &p); SlideWrap<> _wrap; not_null _inner; diff --git a/Telegram/SourceFiles/ui/chat/requests_bar.cpp b/Telegram/SourceFiles/ui/chat/requests_bar.cpp index 04a4ae460..69190a745 100644 --- a/Telegram/SourceFiles/ui/chat/requests_bar.cpp +++ b/Telegram/SourceFiles/ui/chat/requests_bar.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_chat.h" #include "styles/style_calls.h" #include "styles/style_info.h" // st::topBarArrowPadding, like TopBarWidget. +#include "styles/style_window.h" // st::columnMinimalWidthLeft #include "styles/palette.h" #include @@ -150,12 +151,14 @@ void RequestsBar::paint(Painter &p) { p.setPen(st::defaultMessageBar.titleFg); p.setFont(font); - const auto textLeft = userpicsLeft + _userpicsWidth + userpicsLeft; - const auto available = width - textLeft - userpicsLeft; - if (_textFull.isEmpty() || available < _textFull.maxWidth()) { - _textShort.drawElided(p, textLeft, textTop, available); - } else { - _textFull.drawElided(p, textLeft, textTop, available); + if (width >= st::columnMinimalWidthLeft) { + const auto textLeft = userpicsLeft + _userpicsWidth + userpicsLeft; + const auto available = width - textLeft - userpicsLeft; + if (_textFull.isEmpty() || available < _textFull.maxWidth()) { + _textShort.drawElided(p, textLeft, textTop, available); + } else { + _textFull.drawElided(p, textLeft, textTop, available); + } } // Skip shadow of the bar above. diff --git a/Telegram/SourceFiles/ui/menu_icons.style b/Telegram/SourceFiles/ui/menu_icons.style index a1c421d44..049329b29 100644 --- a/Telegram/SourceFiles/ui/menu_icons.style +++ b/Telegram/SourceFiles/ui/menu_icons.style @@ -116,5 +116,6 @@ menuIconStartStreamWith: icon {{ "menu/start_stream_with", menuIconColor }}; menuIconDeleteAttention: icon {{ "menu/delete", menuIconAttentionColor }}; menuIconLeaveAttention: icon {{ "menu/leave", menuIconAttentionColor }}; menuIconDisableAttention: icon {{ "menu/disable", menuIconAttentionColor }}; +menuIconReportAttention: icon {{ "menu/report", menuIconAttentionColor }}; menuIconBlockSettings: icon {{ "menu/block", windowBgActive }}; diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 48a5d2ec1..fbc3c3f6c 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -976,12 +976,12 @@ void SessionController::setActiveChatEntry(Dialogs::RowDescriptor row) { now->setFakeUnreadWhileOpened(true); if (const auto channel = now->peer->asChannel() ; channel && !channel->isForum()) { - channel->flagsValue( - ) | rpl::filter([=](const ChannelData::Flags::Change &update) { - using Flag = ChannelData::Flag; - return (update.diff & Flag::Forum) - && (update.value & Flag::Forum); - }) | rpl::start_with_next([=] { + Data::PeerFlagValue( + channel, + ChannelData::Flag::Forum + ) | rpl::filter( + rpl::mappers::_1 + ) | rpl::start_with_next([=] { clearSectionStack( { anim::type::normal, anim::activation::background }); openForum(channel,