[Client] Tabbed room channels with unread states and collapsible chat (#7478)

* [Client] Add room channel selector with per-channel chat views

* [Client] Tabbed room channels with unread states, collapse, read-only, banners

Reworks the room chat channel display: channels move out of the combobox
into a tab bar below the message input showing every channel available
to the user.

- Each tab shows its unread message count and is tinted when it holds
  unreads or a mention; both colors are user-configurable chat settings
  (channelUnreadColor, channelMentionColor) with a settings-page section.
- Clicking the active tab collapses the chat: message view and input
  hide and the splitter section minimizes to the tab strip; expanding
  restores the previous splitter sizes. Viewing a channel clears its
  unread state.
- Channels the user may read but not write (per write_access_level)
  show with the message input disabled and a read-only placeholder;
  sends are guarded locally, the server still enforces.
- Channel banner_message strings render at the top of their channel
  view on join, like the room join message on Main.
- Replayed channel history no longer inflates unread counts; moderation
  redaction now applies across all channel views.

* Fixup: keep chat tab bar visible when chat is collapsed

QSplitter children are collapsible by default, so setSizes({1, 0})
hid the whole chat group box including the channel tab bar.
Mark the chat widget non-collapsible so collapsing clamps to its
minimum size (title + tab bar) instead.

* [Room] Explicitly initialize MAIN_CHANNEL per review

* [Room] Address channel tab review follow-ups

- gate @all mentions on moderator/admin sender to match ChatView,
  whole-word matching for own name and highlight words
- hide single-channel tab bar so old servers do not show a lone Main
  tab that collapses chat on misclick
- capture target ChatView in sendMessage so errors land in the
  channel the message was sent from
- share ignore/unregistered filtering between RoomSay and
  RoomChannelSay via isRoomMessageIgnored

* [Client] Fix tab_room.cpp formatting per clang-format-18

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: BruebachL <bruebachl@cockatrice.example>
This commit is contained in:
BruebachL 2026-10-16 22:46:39 +02:00 • committed by GitHub
parent 05dcde69db
commit c99113ab0e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 543 additions and 38 deletions

View file

@ -118,6 +118,26 @@ MessagesSettingsPage::MessagesSettingsPage()
highlightGroupBox = new QGroupBox;
highlightGroupBox->setLayout(highlightNotice);
channelUnreadColor = new QLineEdit();
channelUnreadColor->setText(SettingsCache::instance().chat().getChannelUnreadColor());
updateChannelUnreadPreview();
connect(channelUnreadColor, &QLineEdit::textChanged, this, &MessagesSettingsPage::updateChannelUnreadColor);
channelMentionColor = new QLineEdit();
channelMentionColor->setText(SettingsCache::instance().chat().getChannelMentionColor());
updateChannelMentionPreview();
connect(channelMentionColor, &QLineEdit::textChanged, this, &MessagesSettingsPage::updateChannelMentionColor);
auto *channelGrid = new QGridLayout;
channelGrid->addWidget(&channelUnreadColorLabel, 0, 0);
channelGrid->addWidget(channelUnreadColor, 0, 1);
channelGrid->addWidget(&hexChannelUnreadLabel, 0, 2);
channelGrid->addWidget(&channelMentionColorLabel, 1, 0);
channelGrid->addWidget(channelMentionColor, 1, 1);
channelGrid->addWidget(&hexChannelMentionLabel, 1, 2);
channelGroupBox = new QGroupBox;
channelGroupBox->setLayout(channelGrid);
messageList = new QListWidget;
int count = SettingsCache::instance().messages().getCount();
@ -159,6 +179,7 @@ MessagesSettingsPage::MessagesSettingsPage()
mainLayout->addWidget(messageGroupBox);
mainLayout->addWidget(chatGroupBox);
mainLayout->addWidget(highlightGroupBox);
mainLayout->addWidget(channelGroupBox);
setLayout(mainLayout);
@ -195,6 +216,34 @@ void MessagesSettingsPage::updateHighlightColor(const QString &value)
}
}
void MessagesSettingsPage::updateChannelUnreadColor(const QString &value)
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
QColor colorToSet = QColor::fromString("#" + value);
#else
QColor colorToSet;
colorToSet.setNamedColor("#" + value);
#endif
if (colorToSet.isValid()) {
SettingsCache::instance().chat().setChannelUnreadColor(value);
updateChannelUnreadPreview();
}
}
void MessagesSettingsPage::updateChannelMentionColor(const QString &value)
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
QColor colorToSet = QColor::fromString("#" + value);
#else
QColor colorToSet;
colorToSet.setNamedColor("#" + value);
#endif
if (colorToSet.isValid()) {
SettingsCache::instance().chat().setChannelMentionColor(value);
updateChannelMentionPreview();
}
}
void MessagesSettingsPage::updateTextColor(QT_STATE_CHANGED_T value)
{
SettingsCache::instance().chat().setChatMentionForeground(value);
@ -221,6 +270,18 @@ void MessagesSettingsPage::updateHighlightPreview()
";color: " + (SettingsCache::instance().chat().getChatHighlightForeground() ? "white" : "black") + ";}");
}
void MessagesSettingsPage::updateChannelUnreadPreview()
{
channelUnreadColor->setStyleSheet("QLineEdit{background:#" +
SettingsCache::instance().chat().getChannelUnreadColor() + ";}");
}
void MessagesSettingsPage::updateChannelMentionPreview()
{
channelMentionColor->setStyleSheet("QLineEdit{background:#" +
SettingsCache::instance().chat().getChannelMentionColor() + ";}");
}
void MessagesSettingsPage::storeSettings()
{
SettingsCache::instance().messages().setCount(messageList->count());
@ -267,6 +328,9 @@ void MessagesSettingsPage::retranslateUi()
{
chatGroupBox->setTitle(tr("Chat settings"));
highlightGroupBox->setTitle(tr("Custom alert words"));
channelGroupBox->setTitle(tr("Room channel tabs"));
channelUnreadColorLabel.setText(tr("Unread messages color"));
channelMentionColorLabel.setText(tr("Mentioned messages color"));
chatMentionCheckBox.setText(tr("Enable chat mentions"));
chatMentionCompleterCheckbox.setText(tr("Enable mention completer"));
messageGroupBox->setTitle(tr("In-game message macros"));
@ -284,6 +348,8 @@ void MessagesSettingsPage::retranslateUi()
useGameTimeCheckBox.setText(tr("Use game time instead of local time in game logs"));
hexLabel.setText(tr("(Color is hexadecimal)"));
hexHighlightLabel.setText(tr("(Color is hexadecimal)"));
hexChannelUnreadLabel.setText(tr("(Color is hexadecimal)"));
hexChannelMentionLabel.setText(tr("(Color is hexadecimal)"));
customAlertStringLabel.setText(tr("Separate words with a space, alphanumeric characters only"));
customAlertString->setPlaceholderText(tr("Word1 Word2 Word3"));
aAdd->setText(tr("Add New Message"));