Compare commits

..

1 Commits

Author SHA1 Message Date
Tobias Fella
43e5adee7e Use AccountSettingsGroup 2025-10-27 16:34:03 -04:00
91 changed files with 11024 additions and 26573 deletions

View File

@@ -10,11 +10,11 @@ Dependencies:
'frameworks/ki18n': '@latest-kf6' 'frameworks/ki18n': '@latest-kf6'
'frameworks/kconfig': '@latest-kf6' 'frameworks/kconfig': '@latest-kf6'
'frameworks/syntax-highlighting': '@latest-kf6' 'frameworks/syntax-highlighting': '@latest-kf6'
'frameworks/kiconthemes': '@latest-kf6'
'frameworks/kitemmodels': '@latest-kf6' 'frameworks/kitemmodels': '@latest-kf6'
'frameworks/kquickcharts': '@latest-kf6' 'frameworks/kquickcharts': '@latest-kf6'
'frameworks/knotifications': '@latest-kf6' 'frameworks/knotifications': '@latest-kf6'
'frameworks/kcolorscheme': '@latest-kf6' 'frameworks/kcolorscheme': '@latest-kf6'
'frameworks/kiconthemes': '@latest-kf6'
'libraries/kquickimageeditor': '@latest-kf6' 'libraries/kquickimageeditor': '@latest-kf6'
'frameworks/sonnet': '@latest-kf6' 'frameworks/sonnet': '@latest-kf6'
'frameworks/prison': '@latest-kf6' 'frameworks/prison': '@latest-kf6'
@@ -29,6 +29,7 @@ Dependencies:
'frameworks/kio': '@latest-kf6' 'frameworks/kio': '@latest-kf6'
'frameworks/kwindowsystem': '@latest-kf6' 'frameworks/kwindowsystem': '@latest-kf6'
'frameworks/kstatusnotifieritem': '@latest-kf6' 'frameworks/kstatusnotifieritem': '@latest-kf6'
'frameworks/kcrash': '@latest-kf6'
- 'on': ['Linux', 'FreeBSD'] - 'on': ['Linux', 'FreeBSD']
'require': 'require':
'frameworks/kdbusaddons': '@latest-kf6' 'frameworks/kdbusaddons': '@latest-kf6'

View File

@@ -8,8 +8,8 @@ cmake_minimum_required(VERSION 3.16)
# KDE Applications version, managed by release script. # KDE Applications version, managed by release script.
set(RELEASE_SERVICE_VERSION_MAJOR "25") set(RELEASE_SERVICE_VERSION_MAJOR "25")
set(RELEASE_SERVICE_VERSION_MINOR "12") set(RELEASE_SERVICE_VERSION_MINOR "11")
set(RELEASE_SERVICE_VERSION_MICRO "1") set(RELEASE_SERVICE_VERSION_MICRO "70")
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}") set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION}) project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
@@ -69,7 +69,7 @@ if (QT_KNOWN_POLICY_QTP0004)
qt_policy(SET QTP0004 NEW) qt_policy(SET QTP0004 NEW)
endif () endif ()
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami I18n Notifications Config CoreAddons Sonnet ItemModels ColorScheme IconThemes) find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami I18n Notifications Config CoreAddons Sonnet ItemModels IconThemes ColorScheme)
set_package_properties(KF6 PROPERTIES set_package_properties(KF6 PROPERTIES
TYPE REQUIRED TYPE REQUIRED
PURPOSE "Basic application components" PURPOSE "Basic application components"
@@ -92,7 +92,7 @@ if(ANDROID)
) )
else() else()
find_package(Qt6 ${QT_MIN_VERSION} COMPONENTS Widgets) find_package(Qt6 ${QT_MIN_VERSION} COMPONENTS Widgets)
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle KIO WindowSystem StatusNotifierItem) find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle KIO WindowSystem StatusNotifierItem Crash)
find_package(KF6SyntaxHighlighting ${KF_MIN_VERSION} REQUIRED) find_package(KF6SyntaxHighlighting ${KF_MIN_VERSION} REQUIRED)
set_package_properties(KF6QQC2DesktopStyle PROPERTIES set_package_properties(KF6QQC2DesktopStyle PROPERTIES
TYPE RUNTIME TYPE RUNTIME

View File

@@ -63,7 +63,7 @@ void ActionsTest::testActions_data()
QTest::addColumn<std::optional<QString>>("resultText"); QTest::addColumn<std::optional<QString>>("resultText");
QTest::addColumn<std::optional<Quotient::RoomMessageEvent::MsgType>>("type"); QTest::addColumn<std::optional<Quotient::RoomMessageEvent::MsgType>>("type");
QTest::newRow("shrug") << u"/shrug Hello"_s << std::make_optional(u"¯\\\\\\_(ツ)\\_/¯ Hello"_s) QTest::newRow("shrug") << u"/shrug Hello"_s << std::make_optional(u"¯\\\\_(ツ)_/¯ Hello"_s)
<< std::make_optional(Quotient::RoomMessageEvent::MsgType::Text); << std::make_optional(Quotient::RoomMessageEvent::MsgType::Text);
QTest::newRow("lenny") << u"/lenny Hello"_s << std::make_optional(u"( ͡° ͜ʖ ͡°) Hello"_s) << std::make_optional(Quotient::RoomMessageEvent::MsgType::Text); QTest::newRow("lenny") << u"/lenny Hello"_s << std::make_optional(u"( ͡° ͜ʖ ͡°) Hello"_s) << std::make_optional(Quotient::RoomMessageEvent::MsgType::Text);
QTest::newRow("tableflip") << u"/tableflip Hello"_s << std::make_optional(u"(╯°□°)╯︵ ┻━┻ Hello"_s) QTest::newRow("tableflip") << u"/tableflip Hello"_s << std::make_optional(u"(╯°□°)╯︵ ┻━┻ Hello"_s)

View File

@@ -320,7 +320,7 @@
<value key="KDE::windows_store::StoreLogoSquare">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/storelogo-1080x1080.png</value> <value key="KDE::windows_store::StoreLogoSquare">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/storelogo-1080x1080.png</value>
<value key="KDE::windows_store::Icon">https://invent.kde.org/network/neochat/-/raw/master/icons/300-apps-neochat.png</value> <value key="KDE::windows_store::Icon">https://invent.kde.org/network/neochat/-/raw/master/icons/300-apps-neochat.png</value>
<value key="KDE::windows_store::PromotionalArt16x9">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/promoimage-1920x1080.png</value> <value key="KDE::windows_store::PromotionalArt16x9">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/promoimage-1920x1080.png</value>
<value key="KDE::supporters">Anonymous donor, Akseli</value> <value key="KDE::supporters">.</value>
</custom> </custom>
<launchable type="desktop-id">org.kde.neochat.desktop</launchable> <launchable type="desktop-id">org.kde.neochat.desktop</launchable>
<screenshots> <screenshots>
@@ -488,9 +488,6 @@
<content_attribute id="social-chat">intense</content_attribute> <content_attribute id="social-chat">intense</content_attribute>
</content_rating> </content_rating>
<releases> <releases>
<release version="25.12.1" date="2026-01-08"/>
<release version="25.12.0" date="2025-12-11"/>
<release version="25.08.3" date="2025-11-06"/>
<release version="25.08.2" date="2025-10-09"/> <release version="25.08.2" date="2025-10-09"/>
<release version="25.08.1" date="2025-09-11"/> <release version="25.08.1" date="2025-09-11"/>
<release version="25.08.0" date="2025-08-14"/> <release version="25.08.0" date="2025-08-14"/>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,122 +0,0 @@
<?xml version="1.0" ?>
<!DOCTYPE refentry PUBLIC "-//KDE//DTD DocBook XML V4.5-Based Variant V1.1//EN" "dtd/kdedbx45.dtd" [
<!ENTITY % Brazilian-Portuguese "INCLUDE">
]>
<!--
SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
SPDX-License-Identifier: CC-BY-SA-4.0
-->
<refentry lang="&language;">
<refentryinfo>
<title
>Manual do Usuário do NeoChat</title>
<author
><firstname
>Carl</firstname
><surname
>Schwan</surname
> <contrib
>NeoChat man page.</contrib
> <email
>carl@carlschwan.eu</email
></author>
<date
>01/11/2022</date>
<releaseinfo
>22.09</releaseinfo>
<productname
>NeoChat</productname>
</refentryinfo>
<refmeta>
<refentrytitle>
<command
>neochat</command>
</refentrytitle>
<manvolnum
>1</manvolnum>
</refmeta>
<refnamediv>
<refname
>neochat</refname>
<refpurpose
>Cliente para interação com o protocolo de mensagens Matrix.</refpurpose>
</refnamediv>
<!-- body begins here -->
<refsynopsisdiv id='synopsis'>
<cmdsynopsis
><command
>neochat</command
> <arg choice="opt"
><replaceable
>URI</replaceable
></arg
> </cmdsynopsis>
</refsynopsisdiv>
<refsect1 id="description">
<title
>Descrição</title>
<para
>O <command
>neochat</command
> é um aplicativo de bate-papo para o protocolo Matrix. Ele funciona tanto em computadores quanto em dispositivos móveis. </para>
</refsect1>
<refsect1 id="options"
><title
>Opções</title>
<variablelist>
<varlistentry>
<term
><option
>URI</option
></term>
<listitem>
<para
>O URI da matriz para um usuário ou uma sala. Por exemplo, matrix:u/usuário:exemplo.org e matrix:r/root:exemplo.org. Isso fará com que o NeoChat tente abrir a sala ou conversa especificada. </para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1 id="bug">
<title
>Relatar bugs</title>
<para
>Você pode reportar erros e solicitar novas funcionalidades em <ulink url="https://bugs.kde.org/enter_bug.cgi?product=NeoChat&amp;component=General"
>https://bugs.kde.org/enter_bug.cgi?product=NeoChat&amp;component=General</ulink
></para>
</refsect1>
<refsect1>
<title
>Veja também</title>
<simplelist>
<member
>Lista de perguntas frequentes sobre o Matrix <ulink url="https://matrix.org/faq/"
>https://matrix.org/faq/</ulink
> </member>
<member
>kf5options(7)</member>
<member
>qt5options(7)</member>
</simplelist>
</refsect1>
<refsect1 id="copyright"
><title
>Direitos autorais</title>
<para
>Direitos autorais &copy; 2020-2022 Tobias Fella </para>
<para
>Direitos autorais &copy; 2020-2022 Carl Schwan </para>
<para
>Licença: GNU General Public Versão 3 ou posterior <ulink url="https://www.gnu.org/licenses/gpl-3.0.html"
>https://www.gnu.org/licenses/gpl-3.0.html</ulink
>&gt;</para>
</refsect1>
</refentry>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -103,7 +103,6 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
qml/ReasonDialog.qml qml/ReasonDialog.qml
qml/NewPollDialog.qml qml/NewPollDialog.qml
qml/UserMenu.qml qml/UserMenu.qml
qml/MeetingDialog.qml
DEPENDENCIES DEPENDENCIES
QtCore QtCore
QtQuick QtQuick
@@ -153,7 +152,6 @@ target_include_directories(neochat-app PRIVATE ${CMAKE_BINARY_DIR})
target_link_libraries(neochat-app PRIVATE target_link_libraries(neochat-app PRIVATE
neochat neochat
KF6::IconThemes
) )
ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png) ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png)
@@ -204,6 +202,7 @@ target_link_libraries(neochat PUBLIC
KF6::ConfigGui KF6::ConfigGui
KF6::CoreAddons KF6::CoreAddons
KF6::SonnetCore KF6::SonnetCore
KF6::IconThemes
KF6::ItemModels KF6::ItemModels
KF6::I18nQml KF6::I18nQml
KirigamiApp KirigamiApp
@@ -214,6 +213,10 @@ target_link_libraries(neochat PUBLIC
Spaces Spaces
) )
if (TARGET KF6::Crash)
target_link_libraries(neochat PUBLIC KF6::Crash)
endif()
kconfig_target_kcfg_file(neochat FILE neochatconfig.kcfg CLASS_NAME NeoChatConfig MUTATORS GENERATE_PROPERTIES DEFAULT_VALUE_GETTERS PARENT_IN_CONSTRUCTOR SINGLETON GENERATE_MOC QML_REGISTRATION) kconfig_target_kcfg_file(neochat FILE neochatconfig.kcfg CLASS_NAME NeoChatConfig MUTATORS GENERATE_PROPERTIES DEFAULT_VALUE_GETTERS PARENT_IN_CONSTRUCTOR SINGLETON GENERATE_MOC QML_REGISTRATION)
if(NEOCHAT_FLATPAK) if(NEOCHAT_FLATPAK)
@@ -353,8 +356,7 @@ endif()
install(TARGETS neochat-app ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(TARGETS neochat-app ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE) if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
# krunner plugin must be the same as the app id for flatpak to export it install(FILES plasma-runner-neochat.desktop DESTINATION ${KDE_INSTALL_DATAROOTDIR}/krunner/dbusplugins)
install(FILES plasma-runner-neochat.desktop DESTINATION ${KDE_INSTALL_DATAROOTDIR}/krunner/dbusplugins RENAME org.kde.neochat.desktop)
endif() endif()
if (APPLE) if (APPLE)

View File

@@ -103,10 +103,6 @@ int main(int argc, char *argv[])
{ {
QNetworkProxyFactory::setUseSystemConfiguration(true); QNetworkProxyFactory::setUseSystemConfiguration(true);
// We currently need to do this ourselves,
// KirigamiApp currently called this after constructing the app which breaks icons on Windows.
KIconTheme::initTheme();
#ifdef HAVE_WEBVIEW #ifdef HAVE_WEBVIEW
QtWebView::initialize(); QtWebView::initialize();
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);

View File

@@ -259,7 +259,7 @@ Comment[sa]=कक्षस्य नूतनं निमन्त्रणम
Comment[sl]=Tam je novo povabilo v sobo Comment[sl]=Tam je novo povabilo v sobo
Comment[sv]=Det finns en ny inbjudan till ett rum Comment[sv]=Det finns en ny inbjudan till ett rum
Comment[ta]=ஓர் அரங்கிற்கான புதிய அழைப்பிதழ் உள்ளது Comment[ta]=ஓர் அரங்கிற்கான புதிய அழைப்பிதழ் உள்ளது
Comment[tr]=Bir odaya yeni bir davet var Comment[tr]=Bir odaya yeni bir davetiye var
Comment[uk]=У кімнаті нове запрошення Comment[uk]=У кімнаті нове запрошення
Comment[zh_CN]=有新的聊天室邀请 Comment[zh_CN]=有新的聊天室邀请
Comment[zh_TW]=有新的加入聊天室邀請 Comment[zh_TW]=有新的加入聊天室邀請

View File

@@ -66,10 +66,6 @@
</entry> </entry>
</group> </group>
<group name="Timeline"> <group name="Timeline">
<entry name="FontScale" type="double">
<label>Scaling factor for font sizes</label>
<default>1.0</default>
</entry>
<entry name="ShowAvatarInTimeline" type="bool"> <entry name="ShowAvatarInTimeline" type="bool">
<label>Show avatar in the timeline</label> <label>Show avatar in the timeline</label>
<default>true</default> <default>true</default>

View File

@@ -91,7 +91,6 @@ Components.AbstractMaximizeComponent {
color: Kirigami.Theme.textColor color: Kirigami.Theme.textColor
font.family: "monospace" font.family: "monospace"
font.pointSize: Kirigami.Theme.defaultFont.pointSize * NeoChatConfig.fontScale
Kirigami.SpellCheck.enabled: false Kirigami.SpellCheck.enabled: false

View File

@@ -1,22 +0,0 @@
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-only
import QtQuick
import org.kde.kirigami as Kirigami
Kirigami.PromptDialog {
id: root
required property bool hasExistingMeeting
title: hasExistingMeeting ? i18nc("@title", "Join Meeting") : i18nc("@title", "Start Meeting")
subtitle: hasExistingMeeting ? i18nc("@info:label", "You are about to join a Jitsi meeting in your web browser.") : i18nc("@info:label", "You are about to start a new Jitsi meeting in your web browser.")
standardButtons: Kirigami.Dialog.Cancel
customFooterActions: Kirigami.Action {
icon.name: "camera-video-symbolic"
text: hasExistingMeeting ? i18nc("@action:button Join the Jitsi meeting", "Join") : i18nc("@action:button Start a new Jitsi meeting", "Start")
onTriggered: root.accept()
}
}

View File

@@ -77,33 +77,11 @@ Kirigami.Page {
actions: [ actions: [
Kirigami.Action { Kirigami.Action {
id: jitsiMeetingAction tooltip: i18nc("@action:button", "Open Jitsi Meet in browser")
icon.name: "camera-video-symbolic"
readonly property bool hasExistingMeeting: root.widgetModel.jitsiIndex >= 0
readonly property bool canStartNewMeeting: root.currentRoom.canSendState("im.vector.modular.widgets")
tooltip: {
if (hasExistingMeeting) {
return i18nc("@action:button", "Join Jitsi meeting…");
}
return canStartNewMeeting ? i18nc("@action:button", "Start Jitsi meeting…") : i18nc("@action:button", "You do not have permissions to start Jitsi meetings")
}
icon {
name: "camera-video-symbolic"
color: hasExistingMeeting ? Kirigami.Theme.highlightColor : "transparent"
}
enabled: hasExistingMeeting || canStartNewMeeting
visible: root.currentRoom && !root.currentRoom.isSpace
onTriggered: { onTriggered: {
const dialog = Qt.createComponent("org.kde.neochat", "MeetingDialog").createObject(QQC2.Overlay.overlay, { hasExistingMeeting }); let url
dialog.onAccepted.connect(doAction); if (root.widgetModel.jitsiIndex < 0) {
dialog.open();
}
function doAction(): void {
let url;
if (!hasExistingMeeting) {
url = root.widgetModel.addJitsiConference(); url = root.widgetModel.addJitsiConference();
} else { } else {
let idx = root.widgetModel.index(root.widgetModel.jitsiIndex, 0); let idx = root.widgetModel.index(root.widgetModel.jitsiIndex, 0);
@@ -119,18 +97,6 @@ Kirigami.Page {
} }
] ]
Kirigami.Action {
enabled: root.currentRoom && !root.currentRoom.isSpace
shortcut: "Ctrl+F"
onTriggered: {
((root.QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow).pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat', 'RoomSearchPage'), {
room: root.currentRoom
}, {
title: i18nc("@action:title", "Search")
});
}
}
KeyNavigation.left: (root.Kirigami.PageStack.pageStack as Kirigami.PageRow).get(0) KeyNavigation.left: (root.Kirigami.PageStack.pageStack as Kirigami.PageRow).get(0)
onCurrentRoomChanged: { onCurrentRoomChanged: {

View File

@@ -11,19 +11,7 @@ VerificationMessage {
required property int reason required property int reason
icon: { icon: "security-low"
switch (root.reason) {
case KeyVerificationSession.TIMEOUT:
case KeyVerificationSession.REMOTE_TIMEOUT:
case KeyVerificationSession.USER:
case KeyVerificationSession.REMOTE_USER:
case KeyVerificationSession.SESSION_ACCEPTED:
case KeyVerificationSession.REMOTE_SESSION_ACCEPTED:
return "dialog-information";
default:
return "security-low";
}
}
text: { text: {
switch (root.reason) { switch (root.reason) {
case KeyVerificationSession.NONE: case KeyVerificationSession.NONE:

View File

@@ -554,45 +554,6 @@ void RoomManager::setCurrentSpace(const QString &spaceId, bool setRoom)
setCurrentRoom({}); setCurrentRoom({});
} }
QString RoomManager::findSpaceIdForCurrentRoom() const
{
if (!m_currentRoom) {
return m_currentSpaceId;
}
if (m_currentRoom->isDirectChat()) {
const auto roomsInSpace = SpaceHierarchyCache::instance().getRoomListForSpace(m_currentSpaceId, false);
if (roomsInSpace.contains(m_currentRoom->id())) {
return m_currentSpaceId;
}
return "DM"_L1;
}
const auto &parentSpaces = SpaceHierarchyCache::instance().parentSpaces(m_currentRoom->id());
if (parentSpaces.contains(m_currentSpaceId)) {
return m_currentSpaceId;
}
static auto config = NeoChatConfig::self();
if (config->allRoomsInHome()) {
return {};
}
if (const auto &parent = m_connection->room(m_currentRoom->canonicalParent())) {
for (const auto &parentParent : SpaceHierarchyCache::instance().parentSpaces(parent->id())) {
if (SpaceHierarchyCache::instance().parentSpaces(parentParent).isEmpty()) {
return parentParent;
}
}
return parent->id();
}
for (const auto &space : parentSpaces) {
if (SpaceHierarchyCache::instance().parentSpaces(space).isEmpty()) {
return space;
}
}
if (m_currentRoom->isSpace()) {
return m_currentSpaceId;
}
return {};
}
void RoomManager::setCurrentRoom(const QString &roomId) void RoomManager::setCurrentRoom(const QString &roomId)
{ {
if (m_currentRoom != nullptr) { if (m_currentRoom != nullptr) {
@@ -610,23 +571,57 @@ void RoomManager::setCurrentRoom(const QString &roomId)
} }
Q_EMIT currentRoomChanged(); Q_EMIT currentRoomChanged();
if (m_connection) {
if (roomId.isEmpty()) {
m_lastRoomConfig.deleteEntry(m_currentSpaceId);
} else {
// We can't have empty keys in KConfig, so name it "Home"
if (m_currentSpaceId.isEmpty()) {
m_lastRoomConfig.writeEntry(u"Home"_s, roomId);
} else {
m_lastRoomConfig.writeEntry(m_currentSpaceId, roomId);
}
}
}
if (roomId.isEmpty()) { if (roomId.isEmpty()) {
m_lastRoomConfig.deleteEntry(m_currentSpaceId);
return; return;
} }
if (m_currentRoom->isSpace()) {
const auto spaceIdForRoom = findSpaceIdForCurrentRoom(); return;
// We can't have empty keys in KConfig, so name it "Home"
if (spaceIdForRoom.isEmpty()) {
m_lastRoomConfig.writeEntry(u"Home"_s, roomId);
} else {
m_lastRoomConfig.writeEntry(spaceIdForRoom, roomId);
} }
if (m_currentRoom->isDirectChat()) {
if (m_currentSpaceId != spaceIdForRoom) { const auto roomsInSpace = SpaceHierarchyCache::instance().getRoomListForSpace(m_currentSpaceId, false);
setCurrentSpace(spaceIdForRoom, false); if (!roomsInSpace.contains(m_currentRoom->id()) && m_currentSpaceId != "DM"_L1) {
setCurrentSpace("DM"_L1, false);
}
return;
} }
const auto &parentSpaces = SpaceHierarchyCache::instance().parentSpaces(roomId);
if (parentSpaces.contains(m_currentSpaceId)) {
return;
}
static auto config = NeoChatConfig::self();
if (config->allRoomsInHome()) {
setCurrentSpace({}, false);
return;
}
if (const auto &parent = m_connection->room(m_currentRoom->canonicalParent())) {
for (const auto &parentParent : SpaceHierarchyCache::instance().parentSpaces(parent->id())) {
if (SpaceHierarchyCache::instance().parentSpaces(parentParent).isEmpty()) {
setCurrentSpace(parentParent, false);
return;
}
}
setCurrentSpace(parent->id(), false);
return;
}
for (const auto &space : parentSpaces) {
if (SpaceHierarchyCache::instance().parentSpaces(space).isEmpty()) {
setCurrentSpace(space, false);
return;
}
}
setCurrentSpace({}, false);
} }
void RoomManager::clearCurrentRoom() void RoomManager::clearCurrentRoom()

View File

@@ -373,15 +373,6 @@ private:
void setCurrentRoom(const QString &roomId); void setCurrentRoom(const QString &roomId);
/**
* @brief Find the most appropriate space for the currently selected room
*
* Should be used to figure out what space to switch to after a room change.
*
* @return The Space ID that the currently set room should be displayed as part of. (or "DM" for DM and "" for Home)
*/
QString findSpaceIdForCurrentRoom() const;
// Space ID, "DM", or empty string // Space ID, "DM", or empty string
void setCurrentSpace(const QString &spaceId, bool setRoom = true); void setCurrentSpace(const QString &spaceId, bool setRoom = true);

View File

@@ -263,7 +263,6 @@ QQC2.Control {
wrapMode: TextEdit.Wrap wrapMode: TextEdit.Wrap
// This has to stay PlainText or else formatting starts breaking in strange ways // This has to stay PlainText or else formatting starts breaking in strange ways
textFormat: TextEdit.PlainText textFormat: TextEdit.PlainText
font.pointSize: Kirigami.Theme.defaultFont.pointSize * NeoChatConfig.fontScale
Accessible.description: placeholderText Accessible.description: placeholderText

View File

@@ -4,7 +4,6 @@
import QtQuick import QtQuick
import QtQuick.Controls as QQC2 import QtQuick.Controls as QQC2
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.neochat
QQC2.ItemDelegate { QQC2.ItemDelegate {
id: root id: root
@@ -30,7 +29,6 @@ QQC2.ItemDelegate {
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
font.family: "emoji" font.family: "emoji"
font.pointSize: Kirigami.Theme.defaultFont.pointSize * NeoChatConfig.fontScale
Kirigami.Icon { Kirigami.Icon {
width: Kirigami.Units.gridUnit * 0.5 width: Kirigami.Units.gridUnit * 0.5

View File

@@ -148,7 +148,7 @@ ColumnLayout {
id: quickReactions id: quickReactions
Layout.fillWidth: true Layout.fillWidth: true
model: ["👍", "👎", "😄", "🎉", "😕", "❤", "🚀", "👀"] model: ["👍", "👎", "😄", "🎉", "😕", "❤", "🚀", "👀"]
delegate: EmojiDelegate { delegate: EmojiDelegate {
required property string modelData required property string modelData

View File

@@ -17,7 +17,6 @@ Kirigami.ScrollablePage {
property NeoChatRoom room property NeoChatRoom room
required property NeoChatConnection connection required property NeoChatConnection connection
property alias currentTabIndex: tabBar.currentIndex
title: i18nc("@title", "Developer Tools") title: i18nc("@title", "Developer Tools")

View File

@@ -15,6 +15,7 @@
#include "general_logging.h" #include "general_logging.h"
using namespace Qt::StringLiterals; using namespace Qt::StringLiterals;
using namespace Quotient;
AccountManager::AccountManager(bool testMode, QObject *parent) AccountManager::AccountManager(bool testMode, QObject *parent)
: QObject(parent) : QObject(parent)
@@ -39,7 +40,7 @@ Quotient::AccountRegistry *AccountManager::accounts()
void AccountManager::loadAccountsFromCache() void AccountManager::loadAccountsFromCache()
{ {
for (const auto &accountId : Quotient::SettingsGroup("Accounts"_L1).childGroups()) { for (const auto &accountId : AccountSettingsGroup().childGroups()) {
Quotient::AccountSettings account{accountId}; Quotient::AccountSettings account{accountId};
m_accountsLoading += accountId; m_accountsLoading += accountId;
Q_EMIT accountsLoadingChanged(); Q_EMIT accountsLoadingChanged();

View File

@@ -89,7 +89,7 @@ public:
case Parameter::MostHighlights: case Parameter::MostHighlights:
return i18nc("@info", "Rooms with the most highlighted messages are higher"); return i18nc("@info", "Rooms with the most highlighted messages are higher");
case Parameter::LastActive: case Parameter::LastActive:
return i18nc("@info", "Rooms with newer events are higher"); return i18nc("@info", "Rooms with the newer messages are higher");
default: default:
return {}; return {};
} }

View File

@@ -59,7 +59,7 @@ QList<ActionsModel::Action> actions{
Action{ Action{
u"shrug"_s, u"shrug"_s,
[](const QString &message, NeoChatRoom *, ChatBarCache *) { [](const QString &message, NeoChatRoom *, ChatBarCache *) {
return u"¯\\\\\\_(ツ)\\_/¯ %1"_s.arg(message); return u"¯\\\\_(ツ)_/¯ %1"_s.arg(message);
}, },
Quotient::RoomMessageEvent::MsgType::Text, Quotient::RoomMessageEvent::MsgType::Text,
kli18n("<message>"), kli18n("<message>"),

View File

@@ -118,10 +118,8 @@ void RoomListModel::connectRoomSignals(NeoChatRoom *room)
connect(room, &Room::displaynameChanged, this, [this, room] { connect(room, &Room::displaynameChanged, this, [this, room] {
refresh(room, {DisplayNameRole}); refresh(room, {DisplayNameRole});
}); });
connect(room, &Room::changed, this, [this, room](Room::Changes changes) { connect(room, &Room::unreadStatsChanged, this, [this, room] {
if (changes & (Room::Change::UnreadStats | Room::Change::Highlights)) { refresh(room, {ContextNotificationCountRole, HasHighlightNotificationsRole, NotificationCountRole});
refresh(room, {ContextNotificationCountRole, HasHighlightNotificationsRole, NotificationCountRole});
}
}); });
connect(room, &Room::notificationCountChanged, this, [this, room] { connect(room, &Room::notificationCountChanged, this, [this, room] {
refresh(room); refresh(room);

View File

@@ -75,37 +75,30 @@ void NeoChatConnection::connectSignals()
Q_EMIT directChatInvitesChanged(); Q_EMIT directChatInvitesChanged();
for (const auto &chatId : additions) { for (const auto &chatId : additions) {
if (const auto chat = room(chatId)) { if (const auto chat = room(chatId)) {
connect(chat, &Room::changed, this, [this](Room::Changes changes) { connect(chat, &Room::unreadStatsChanged, this, [this]() {
if (changes & (Room::Change::UnreadStats | Room::Change::Highlights)) { refreshBadgeNotificationCount();
refreshBadgeNotificationCount(); Q_EMIT directChatNotificationsChanged();
Q_EMIT directChatNotificationsChanged(); Q_EMIT directChatsHaveHighlightNotificationsChanged();
Q_EMIT directChatsHaveHighlightNotificationsChanged();
}
}); });
} }
} }
for (const auto &chatId : removals) { for (const auto &chatId : removals) {
if (const auto chat = room(chatId)) { if (const auto chat = room(chatId)) {
disconnect(chat, &Room::changed, this, nullptr); disconnect(chat, &Room::unreadStatsChanged, this, nullptr);
} }
} }
}); });
connect(this, &NeoChatConnection::joinedRoom, this, [this](Room *room) { connect(this, &NeoChatConnection::joinedRoom, this, [this](Room *room) {
if (room->isDirectChat()) { if (room->isDirectChat()) {
connect(room, &Room::changed, this, [this](Room::Changes changes) { connect(room, &Room::unreadStatsChanged, this, [this]() {
if (changes & (Room::Change::UnreadStats | Room::Change::Highlights)) { Q_EMIT directChatNotificationsChanged();
Q_EMIT directChatNotificationsChanged(); Q_EMIT directChatsHaveHighlightNotificationsChanged();
Q_EMIT directChatsHaveHighlightNotificationsChanged();
}
}); });
} }
Q_EMIT roomInvitesChanged(); connect(room, &Room::unreadStatsChanged, this, [this]() {
connect(room, &Room::changed, this, [this](Room::Changes changes) { refreshBadgeNotificationCount();
if (changes & (Room::Change::UnreadStats | Room::Change::Highlights)) { Q_EMIT homeNotificationsChanged();
refreshBadgeNotificationCount(); Q_EMIT homeHaveHighlightNotificationsChanged();
Q_EMIT homeNotificationsChanged();
Q_EMIT homeHaveHighlightNotificationsChanged();
}
}); });
}); });
connect(this, &NeoChatConnection::leftRoom, this, [this](Room *room, Room *prev) { connect(this, &NeoChatConnection::leftRoom, this, [this](Room *room, Room *prev) {
@@ -197,7 +190,7 @@ void NeoChatConnection::setKeywordPushRuleDefault(PushRuleAction::Action default
void NeoChatConnection::logout(bool serverSideLogout) void NeoChatConnection::logout(bool serverSideLogout)
{ {
SettingsGroup(u"Accounts"_s).remove(userId()); AccountSettingsGroup().remove(userId());
QKeychain::DeletePasswordJob job(qAppName()); QKeychain::DeletePasswordJob job(qAppName());
job.setAutoDelete(true); job.setAutoDelete(true);
@@ -459,20 +452,15 @@ bool NeoChatConnection::homeHaveHighlightNotifications() const
return false; return false;
} }
qsizetype NeoChatConnection::directChatInvites() const bool NeoChatConnection::directChatInvites() const
{ {
const auto inviteRooms = rooms(JoinState::Invite); auto inviteRooms = rooms(JoinState::Invite);
return std::ranges::count_if(inviteRooms, [](const auto room) { for (const auto inviteRoom : inviteRooms) {
return room->isDirectChat(); if (inviteRoom->isDirectChat()) {
}); return true;
} }
}
qsizetype NeoChatConnection::roomInvites() const return false;
{
const auto inviteRooms = rooms(JoinState::Invite);
return std::ranges::count_if(inviteRooms, [](const auto room) {
return !room->isDirectChat();
});
} }
QCoro::Task<void> NeoChatConnection::setupPushNotifications(QString endpoint) QCoro::Task<void> NeoChatConnection::setupPushNotifications(QString endpoint)

View File

@@ -66,14 +66,9 @@ class NeoChatConnection : public Quotient::Connection
Q_PROPERTY(bool homeHaveHighlightNotifications READ homeHaveHighlightNotifications NOTIFY homeHaveHighlightNotificationsChanged) Q_PROPERTY(bool homeHaveHighlightNotifications READ homeHaveHighlightNotifications NOTIFY homeHaveHighlightNotificationsChanged)
/** /**
* @brief The number of invites to 1-on-1 direct chats. * @brief Whether there is at least one invite to a direct chat.
*/ */
Q_PROPERTY(qsizetype directChatInvites READ directChatInvites NOTIFY directChatInvitesChanged) Q_PROPERTY(bool directChatInvites READ directChatInvites NOTIFY directChatInvitesChanged)
/**
* @brief The number of pending, normal room invites.
*/
Q_PROPERTY(qsizetype roomInvites READ roomInvites NOTIFY roomInvitesChanged)
/** /**
* @brief Whether the server supports querying a user's mutual rooms. * @brief Whether the server supports querying a user's mutual rooms.
@@ -205,8 +200,7 @@ public:
*/ */
static void setKeywordPushRuleDefault(PushRuleAction::Action defaultAction); static void setKeywordPushRuleDefault(PushRuleAction::Action defaultAction);
qsizetype directChatInvites() const; bool directChatInvites() const;
qsizetype roomInvites() const;
// note: this is intentionally a copied QString because // note: this is intentionally a copied QString because
// the reference could be destroyed before the task is finished // the reference could be destroyed before the task is finished
@@ -231,7 +225,6 @@ Q_SIGNALS:
void homeNotificationsChanged(); void homeNotificationsChanged();
void homeHaveHighlightNotificationsChanged(); void homeHaveHighlightNotificationsChanged();
void directChatInvitesChanged(); void directChatInvitesChanged();
void roomInvitesChanged();
void passwordStatus(NeoChatConnection::PasswordStatus status); void passwordStatus(NeoChatConnection::PasswordStatus status);
void userConsentRequired(QUrl url); void userConsentRequired(QUrl url);
void badgeNotificationCountChanged(int count); void badgeNotificationCountChanged(int count);

View File

@@ -43,8 +43,8 @@ void SpaceHierarchyCache::cacheSpaceHierarchy()
Qt::SingleShotConnection); Qt::SingleShotConnection);
} }
connect(neoChatRoom, &NeoChatRoom::changed, this, [this, neoChatRoom](NeoChatRoom::Changes changes) { connect(neoChatRoom, &NeoChatRoom::unreadStatsChanged, this, [this, neoChatRoom]() {
if (neoChatRoom != nullptr && (changes & (NeoChatRoom::Change::UnreadStats | NeoChatRoom::Change::Highlights))) { if (neoChatRoom != nullptr) {
const auto parents = parentSpaces(neoChatRoom->id()); const auto parents = parentSpaces(neoChatRoom->id());
if (parents.count() > 0) { if (parents.count() > 0) {
Q_EMIT spaceNotifcationCountChanged(parents); Q_EMIT spaceNotifcationCountChanged(parents);

View File

@@ -77,7 +77,6 @@ QQC2.Control {
color: Kirigami.Theme.textColor color: Kirigami.Theme.textColor
font.family: "monospace" font.family: "monospace"
font.pointSize: Kirigami.Theme.defaultFont.pointSize * NeoChatConfig.fontScale
Kirigami.SpellCheck.enabled: false Kirigami.SpellCheck.enabled: false

View File

@@ -41,12 +41,7 @@ QQC2.Control {
*/ */
property var defaultHeight: Kirigami.Units.gridUnit * 3 + Kirigami.Units.largeSpacing * 2 property var defaultHeight: Kirigami.Units.gridUnit * 3 + Kirigami.Units.largeSpacing * 2
/** property bool truncated: linkPreviewDescription.truncated || !linkPreviewDescription.visible
* @brief Whether the link preview description is truncated.
*
* This is only applicable if there *is* a text description, and is never true for images.
*/
property bool truncated: linkPreviewDescription.truncated && linkPreviewDescription.visible
/** /**
* @brief Request for this delegate to be removed. * @brief Request for this delegate to be removed.
@@ -77,7 +72,7 @@ QQC2.Control {
id: previewImage id: previewImage
Layout.preferredWidth: root.defaultHeight Layout.preferredWidth: root.defaultHeight
Layout.preferredHeight: root.defaultHeight Layout.preferredHeight: root.defaultHeight
Layout.maximumWidth: root.defaultHeight Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
visible: root.linkPreviewer.imageSource.toString().length > 0 visible: root.linkPreviewer.imageSource.toString().length > 0
source: root.linkPreviewer.imageSource source: root.linkPreviewer.imageSource
@@ -87,9 +82,9 @@ QQC2.Control {
} }
ColumnLayout { ColumnLayout {
id: column id: column
Layout.preferredWidth: Math.max(linkPreviewTitle.implicitWidth, linkPreviewDescription.implicitWidth) implicitWidth: Math.max(linkPreviewTitle.implicitWidth, linkPreviewDescription.implicitWidth)
Layout.fillWidth: true
spacing: Kirigami.Units.smallSpacing spacing: Kirigami.Units.smallSpacing
visible: root.linkPreviewer.title.length > 0 || root.linkPreviewer.description.length > 0
Kirigami.Heading { Kirigami.Heading {
id: linkPreviewTitle id: linkPreviewTitle
Layout.fillWidth: true Layout.fillWidth: true
@@ -126,11 +121,10 @@ QQC2.Control {
acceptedButtons: Qt.LeftButton acceptedButtons: Qt.LeftButton
onTapped: RoomManager.resolveResource(root.linkPreviewer.url, "join") onTapped: RoomManager.resolveResource(root.linkPreviewer.url, "join")
} }
}
HoverHandler { HoverHandler {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onHoveredChanged: (root.QQC2.ApplicationWindow.window as Main).hoverLinkIndicator.text = hovered ? root.linkPreviewer.url : ""
}
} }
QQC2.Button { QQC2.Button {

View File

@@ -58,7 +58,6 @@ QQC2.Control {
selectionColor: Kirigami.Theme.highlightColor selectionColor: Kirigami.Theme.highlightColor
font.italic: true font.italic: true
font.pointSize: Kirigami.Theme.defaultFont.pointSize * NeoChatConfig.fontScale
onSelectedTextChanged: root.selectedTextChanged(selectedText) onSelectedTextChanged: root.selectedTextChanged(selectedText)

View File

@@ -51,7 +51,6 @@ Flow {
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
text: reactionDelegate.textContent text: reactionDelegate.textContent
font.pointSize: Kirigami.Theme.defaultFont.pointSize * NeoChatConfig.fontScale
background: null background: null
wrapMode: TextEdit.NoWrap wrapMode: TextEdit.NoWrap
textFormat: Text.RichText textFormat: Text.RichText

View File

@@ -67,9 +67,7 @@ TextEdit {
selectedTextColor: Kirigami.Theme.highlightedTextColor selectedTextColor: Kirigami.Theme.highlightedTextColor
selectionColor: Kirigami.Theme.highlightColor selectionColor: Kirigami.Theme.highlightColor
font { font {
pointSize: !root.isReply && QmlUtils.isEmoji(display) pointSize: !root.isReply && QmlUtils.isEmoji(display) ? Kirigami.Theme.defaultFont.pointSize * 4 : Kirigami.Theme.defaultFont.pointSize
? Kirigami.Theme.defaultFont.pointSize * 4 * NeoChatConfig.fontScale
: Kirigami.Theme.defaultFont.pointSize * NeoChatConfig.fontScale
family: QmlUtils.isEmoji(display) ? 'emoji' : Kirigami.Theme.defaultFont.family family: QmlUtils.isEmoji(display) ? 'emoji' : Kirigami.Theme.defaultFont.family
} }
selectByMouse: !Kirigami.Settings.isMobile selectByMouse: !Kirigami.Settings.isMobile

View File

@@ -7,7 +7,7 @@ import QtPositioning
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.neochat import org.kde.neochat.libneochat
Kirigami.Page { Kirigami.Page {
id: root id: root

View File

@@ -79,7 +79,7 @@ QQC2.ScrollView {
id: searchButton id: searchButton
visible: !root.room.isSpace visible: !root.room.isSpace
icon.name: "search" icon.name: "search"
text: i18nc("@action:button", "Search Messages") text: i18n("Search in this room")
activeFocusOnTab: true activeFocusOnTab: true
Layout.fillWidth: true Layout.fillWidth: true
@@ -107,7 +107,7 @@ QQC2.ScrollView {
id: favouriteButton id: favouriteButton
visible: !root.room.isSpace visible: !root.room.isSpace
icon.name: root.room && root.room.isFavourite ? "rating" : "rating-unrated" icon.name: root.room && root.room.isFavourite ? "rating" : "rating-unrated"
text: root.room && root.room.isFavourite ? i18nc("@action:button", "Remove from Favorites") : i18nc("@action:button", "Add to Favorites") text: root.room && root.room.isFavourite ? i18n("Remove room from favorites") : i18n("Favorite this room")
onClicked: root.room.isFavourite ? root.room.removeTag("m.favourite") : root.room.addTag("m.favourite", 1.0) onClicked: root.room.isFavourite ? root.room.removeTag("m.favourite") : root.room.addTag("m.favourite", 1.0)
@@ -120,7 +120,7 @@ QQC2.ScrollView {
id: widgetsButton id: widgetsButton
visible: !root.room.isSpace visible: !root.room.isSpace
icon.name: "extension-symbolic" icon.name: "extension-symbolic"
text: i18nc("@action:button", "Extensions") text: i18nc("@action:button", "Extensions for this room")
activeFocusOnTab: true activeFocusOnTab: true
onClicked: ((QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow).pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat', 'WidgetsPage'), { onClicked: ((QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow).pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat', 'WidgetsPage'), {
@@ -136,7 +136,7 @@ QQC2.ScrollView {
id: locationsButton id: locationsButton
visible: !root.room.isSpace visible: !root.room.isSpace
icon.name: "map-flat" icon.name: "map-flat"
text: i18nc("@action:button", "Shared Locations") text: i18n("Show locations for this room")
activeFocusOnTab: true activeFocusOnTab: true
onClicked: ((QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow).pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat', 'LocationsPage'), { onClicked: ((QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow).pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat', 'LocationsPage'), {
@@ -152,7 +152,7 @@ QQC2.ScrollView {
id: pinnedMessagesButton id: pinnedMessagesButton
visible: !root.room.isSpace visible: !root.room.isSpace
icon.name: "pin-symbolic" icon.name: "pin-symbolic"
text: i18nc("@action:button", "Pinned Messages") text: i18nc("@action:button", "Pinned messages")
activeFocusOnTab: true activeFocusOnTab: true
Layout.fillWidth: true Layout.fillWidth: true
@@ -166,29 +166,10 @@ QQC2.ScrollView {
} }
} }
Delegates.RoundedItemDelegate {
text: i18nc("@action:inmenu", "Inspect Room Data")
icon.name: "tools"
visible: NeoChatConfig.developerTools
activeFocusOnTab: true
Layout.fillWidth: true
onClicked: ((QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow).pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'DevtoolsPage'), {
connection: root.room.connection,
currentTabIndex: 1, // Room data tab
room: root.room
}, {
title: i18nc("@title:window", "Developer Tools"),
width: Kirigami.Units.gridUnit * 50,
height: Kirigami.Units.gridUnit * 42
})
}
Delegates.RoundedItemDelegate { Delegates.RoundedItemDelegate {
id: leaveButton id: leaveButton
icon.name: "arrow-left-symbolic" icon.name: "arrow-left-symbolic"
text: root.room.isSpace ? i18nc("@action:button", "Leave Space…") : i18nc("@action:button", "Leave Room…") text: root.room.isSpace ? i18nc("@action:button", "Leave this space…") : i18nc("@action:button", "Leave this room…")
activeFocusOnTab: true activeFocusOnTab: true
Layout.fillWidth: true Layout.fillWidth: true

View File

@@ -46,6 +46,10 @@ RowLayout {
onClicked: root.search(); onClicked: root.search();
icon.name: "search" icon.name: "search"
text: i18nc("@action", "Search Rooms") text: i18nc("@action", "Search Rooms")
Shortcut {
sequence: "Ctrl+F"
onActivated: searchButton.clicked()
}
QQC2.ToolTip.visible: hovered QQC2.ToolTip.visible: hovered
QQC2.ToolTip.text: text QQC2.ToolTip.text: text
@@ -54,31 +58,19 @@ RowLayout {
QQC2.ToolButton { QQC2.ToolButton {
id: menuButton id: menuButton
property QQC2.Menu menuItem: undefined
function openMenu(): void {
if (!menuItem || !menuItem.visible) {
menuItem = menu.createObject(menuButton) as QQC2.Menu;
menuItem.closed.connect(menuButton.toggle);
menuItem.open();
} else {
menuItem.dismiss()
}
}
Accessible.role: Accessible.ButtonMenu Accessible.role: Accessible.ButtonMenu
Accessible.onPressAction: menuButton.action.trigger()
display: QQC2.AbstractButton.IconOnly display: QQC2.AbstractButton.IconOnly
down: pressed || menuItem.visible checkable: true
text: i18nc("@action:button", "Show Menu") text: i18nc("@action:button", "Show Menu")
icon.name: "application-menu-symbolic" icon.name: "application-menu-symbolic"
onClicked: {
const item = menu.createObject(menuButton) as QQC2.Menu;
item.closed.connect(menuButton.toggle);
item.open();
}
onPressed: openMenu() QQC2.ToolTip.visible: hovered
Keys.onReturnPressed: openMenu()
Keys.onEnterPressed: openMenu()
Accessible.onPressAction: openMenu()
QQC2.ToolTip.visible: hovered && !menuItem.visible
QQC2.ToolTip.text: text QQC2.ToolTip.text: text
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
} }
@@ -86,8 +78,6 @@ RowLayout {
Component { Component {
id: menu id: menu
QQC2.Menu { QQC2.Menu {
y: menuButton.height
QQC2.MenuItem { QQC2.MenuItem {
text: i18n("Find your friends") text: i18n("Find your friends")
icon.name: "list-add-user" icon.name: "list-add-user"

View File

@@ -71,7 +71,7 @@ KirigamiComponents.ConvergentContextMenu {
} }
Kirigami.Action { Kirigami.Action {
text: i18nc("As in 'notify for all messages'", "All Messages") text: i18nc("As in 'notify for all messages'", "All")
icon.name: "notifications" icon.name: "notifications"
checkable: true checkable: true
autoExclusive: true autoExclusive: true
@@ -95,7 +95,7 @@ KirigamiComponents.ConvergentContextMenu {
} }
Kirigami.Action { Kirigami.Action {
text: i18nc("As in 'do not notify for any messages'", "None") text: i18nc("As in 'do not notify for any messages'", "Off")
icon.name: "notifications-disabled" icon.name: "notifications-disabled"
checkable: true checkable: true
autoExclusive: true autoExclusive: true

View File

@@ -78,11 +78,11 @@ Kirigami.Page {
} }
function goToNextUnreadRoom() { function goToNextUnreadRoom() {
goToNextRoomFiltered(item => (item && item instanceof RoomDelegate && item.hasUnreadMessages)); goToNextRoomFiltered(item => (item && item instanceof RoomDelegate && item.hasUnread));
} }
function goToPreviousUnreadRoom() { function goToPreviousUnreadRoom() {
goToPreviousRoomFiltered(item => (item && item instanceof RoomDelegate && item.hasUnreadMessages)); goToPreviousRoomFiltered(item => (item && item instanceof RoomDelegate && item.hasUnread));
} }
titleDelegate: Loader { titleDelegate: Loader {

View File

@@ -81,15 +81,12 @@ QQC2.Control {
AvatarTabButton { AvatarTabButton {
id: allRoomButton id: allRoomButton
readonly property int countedNotifications: root.connection.homeNotifications + root.connection.roomInvites
readonly property bool hasCountableNotifications: countedNotifications > 0
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: width - Kirigami.Units.smallSpacing Layout.preferredHeight: width - Kirigami.Units.smallSpacing
Layout.maximumHeight: width - Kirigami.Units.smallSpacing Layout.maximumHeight: width - Kirigami.Units.smallSpacing
Layout.topMargin: Kirigami.Units.smallSpacing / 2 Layout.topMargin: Kirigami.Units.smallSpacing / 2
text: hasCountableNotifications ? i18ncp("Home space for the uncategorized rooms", "Home (%1 notification)", "Home (%1 notifications)", countedNotifications) : i18nc("Home space for the uncategorized rooms", "Home") text: i18n("Home")
contentItem: Kirigami.Icon { contentItem: Kirigami.Icon {
source: "user-home-symbolic" source: "user-home-symbolic"
@@ -103,15 +100,15 @@ QQC2.Control {
width: Math.max(homeNotificationCountTextMetrics.advanceWidth + Kirigami.Units.smallSpacing * 2, height) width: Math.max(homeNotificationCountTextMetrics.advanceWidth + Kirigami.Units.smallSpacing * 2, height)
height: Kirigami.Units.iconSizes.smallMedium height: Kirigami.Units.iconSizes.smallMedium
text: allRoomButton.countedNotifications text: root.connection.homeNotifications > 0 ? root.connection.homeNotifications : ""
visible: allRoomButton.hasCountableNotifications && (RoomManager.currentSpace.length > 0 || RoomManager.currentSpace !== "DM") visible: root.connection.homeNotifications > 0 && (RoomManager.currentSpace.length > 0 || RoomManager.currentSpace !== "DM")
color: Kirigami.Theme.textColor color: Kirigami.Theme.textColor
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
background: Rectangle { background: Rectangle {
visible: true visible: true
Kirigami.Theme.colorSet: Kirigami.Theme.Button Kirigami.Theme.colorSet: Kirigami.Theme.Button
Kirigami.Theme.inherit: false Kirigami.Theme.inherit: false
color: root.connection.homeHaveHighlightNotifications || root.connection.roomInvites > 0 ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor color: root.connection.homeHaveHighlightNotifications ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor
radius: height / 2 radius: height / 2
} }
@@ -130,8 +127,7 @@ QQC2.Control {
AvatarTabButton { AvatarTabButton {
id: directChatButton id: directChatButton
readonly property int countedNotifications: root.connection.directChatNotifications + root.connection.directChatInvites readonly property bool hasCountableNotifications: root.connection.directChatNotifications > 0 || root.connection.directChatInvites > 0
readonly property bool hasCountableNotifications: countedNotifications > 0
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: width - Kirigami.Units.smallSpacing Layout.preferredHeight: width - Kirigami.Units.smallSpacing
@@ -140,7 +136,7 @@ QQC2.Control {
text: { text: {
if (directChatButton.hasCountableNotifications) { if (directChatButton.hasCountableNotifications) {
return i18ncp("@button View all one-on-one chats.", "Direct Messages (%1 notification)", "Direct Messages (%1 notifications)", directChatButton.countedNotifications); return i18ncp("@button View all one-on-one chats.", "Direct Messages (%1 notification)", "Direct Messages (%1 notifications)", root.connection.directChatNotifications + root.connection.directChatInvites);
} }
return i18nc("@button View all one-on-one chats.", "Direct Messages"); return i18nc("@button View all one-on-one chats.", "Direct Messages");
@@ -166,7 +162,7 @@ QQC2.Control {
visible: true visible: true
Kirigami.Theme.colorSet: Kirigami.Theme.Button Kirigami.Theme.colorSet: Kirigami.Theme.Button
Kirigami.Theme.inherit: false Kirigami.Theme.inherit: false
color: root.connection.directChatsHaveHighlightNotifications || root.connection.directChatInvites > 0 ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor color: root.connection.directChatsHaveHighlightNotifications || root.connection.directChatInvites ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor
radius: height / 2 radius: height / 2
} }

View File

@@ -170,12 +170,10 @@ void RoomTreeModel::connectRoomSignals(NeoChatRoom *room)
connect(room, &Room::displaynameChanged, this, [this, room] { connect(room, &Room::displaynameChanged, this, [this, room] {
refreshRoomRoles(room, {DisplayNameRole}); refreshRoomRoles(room, {DisplayNameRole});
}); });
connect(room, &Room::changed, this, [this, room](Room::Changes changes) { connect(room, &Room::unreadStatsChanged, this, [this, room] {
if (changes & (Room::Change::UnreadStats | Room::Change::Highlights)) { refreshRoomRoles(room, {ContextNotificationCountRole, HasHighlightNotificationsRole, NotificationCountRole});
refreshRoomRoles(room, {ContextNotificationCountRole, HasHighlightNotificationsRole, NotificationCountRole}); if (room->isServerNoticeRoom()) {
if (room->isServerNoticeRoom()) { Q_EMIT invalidateSort();
Q_EMIT invalidateSort();
}
} }
}); });
connect(room, &Room::avatarChanged, this, [this, room] { connect(room, &Room::avatarChanged, this, [this, room] {

View File

@@ -43,40 +43,6 @@ FormCard.FormCardPage {
NeoChatConfig.save(); NeoChatConfig.save();
} }
} }
FormCard.FormDelegateSeparator {
above: fontScaleSliderDelegate
below: compactRoomListDelegate
}
/*!
Font scale setting allows user to adjust the font size used in the app.
*/
FormCard.AbstractFormDelegate {
id: fontScaleSliderDelegate
background: Item {}
contentItem: ColumnLayout {
QQC2.Label {
text: i18nc("@label Font size for text in the chat pane", "Chat font scaling")
Layout.fillWidth: true
}
QQC2.Label {
text: i18nc("@label:slider Current font scale percentage. %1 is the numeric percentage value, the second % is the symbol e.g. 120%", "%1%", Math.round(NeoChatConfig.fontScale * 100))
Layout.fillWidth: true
}
QQC2.Slider {
from: 0.5
to: 3.0
stepSize: 0.1
value: NeoChatConfig.fontScale
onMoved: {
NeoChatConfig.fontScale = value;
NeoChatConfig.save();
}
Layout.fillWidth: true
}
}
}
} }
FormCard.FormHeader { FormCard.FormHeader {

View File

@@ -50,7 +50,6 @@ ecm_add_qml_module(Settings GENERATE_PLUGIN_SOURCE
RoomSortParameterDialog.qml RoomSortParameterDialog.qml
RoomProfile.qml RoomProfile.qml
RoomAdvancedPage.qml RoomAdvancedPage.qml
KeyboardShortcutsPage.qml
SOURCES SOURCES
colorschemer.cpp colorschemer.cpp
threepidaddhelper.cpp threepidaddhelper.cpp

View File

@@ -1,52 +0,0 @@
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard
FormCard.FormCardPage {
title: i18nc("@title:window", "Keyboard Shortcuts")
FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing * 4
FormCard.FormTextDelegate {
text: i18nc("@info:label", "Open Quick Switcher")
description: "Ctrl+K"
}
FormCard.FormTextDelegate {
text: i18nc("@info:label", "Open Account Switcher")
description: "Ctrl+U"
}
FormCard.FormTextDelegate {
text: i18nc("@info:label", "Search Messages in Current Room")
description: "Ctrl+F"
}
FormCard.FormTextDelegate {
text: i18nc("@info:label", "Go to Previous Room")
description: "Ctrl+PgUp, Ctrl+Backtab, Alt+Up"
}
FormCard.FormTextDelegate {
text: i18nc("@info:label", "Go to Next Room")
description: "Ctrl+PgDown, Ctrl+Tab, Alt+Down"
}
FormCard.FormTextDelegate {
text: i18nc("@info:label", "Go to Previous Unread Room")
description: "Alt+Shift+Up"
}
FormCard.FormTextDelegate {
text: i18nc("@info:label", "Go to Next Unread Room")
description: "Alt+Shift+Down"
}
}
}

View File

@@ -91,15 +91,9 @@ FormCard.FormCardPage {
title: i18nc("@title:group", "Room List Sort Order") title: i18nc("@title:group", "Room List Sort Order")
} }
FormCard.FormCard { FormCard.FormCard {
FormCard.FormTextDelegate {
text: i18nc("@info:label", "Hidden events are not considered as recent activity when sorting rooms.")
}
FormCard.FormDelegateSeparator {}
FormCard.FormRadioDelegate { FormCard.FormRadioDelegate {
text: i18nc("As in 'sort something based on last activity'", "Importance") text: i18nc("As in 'sort something based on last activity'", "Activity")
description: i18nc("@info", "Rooms with unread notifications will be shown first.") description: i18nc("@info", "Rooms with unread notifications will be shown first")
checked: NeoChatConfig.sortOrder === 1 checked: NeoChatConfig.sortOrder === 1
enabled: !NeoChatConfig.isSortOrderImmutable enabled: !NeoChatConfig.isSortOrderImmutable
onToggled: { onToggled: {
@@ -119,8 +113,8 @@ FormCard.FormCardPage {
} }
} }
FormCard.FormRadioDelegate { FormCard.FormRadioDelegate {
text: i18nc("As in 'sort something based on the last event'", "Newest Events") text: i18nc("As in 'sort something based on the last message'", "Last Message Activity")
description: i18nc("@info", "Rooms with the newest events will be shown first.") description: i18nc("@info", "Rooms with the newest activity will be shown first")
checked: NeoChatConfig.sortOrder === 2 checked: NeoChatConfig.sortOrder === 2
enabled: !NeoChatConfig.isSortOrderImmutable enabled: !NeoChatConfig.isSortOrderImmutable
onToggled: { onToggled: {

View File

@@ -97,12 +97,6 @@ KirigamiSettings.ConfigurationView {
} }
visible: root.connection !== null visible: root.connection !== null
}, },
KirigamiSettings.ConfigurationModule {
moduleId: "keyboardShortcuts"
text: i18n("Keyboard Shortcuts")
icon.name: "input-keyboard-symbolic"
page: () => Qt.createComponent("org.kde.neochat.settings", "KeyboardShortcutsPage")
},
KirigamiSettings.ConfigurationModule { KirigamiSettings.ConfigurationModule {
moduleId: "aboutNeochat" moduleId: "aboutNeochat"
text: i18n("About NeoChat") text: i18n("About NeoChat")

View File

@@ -23,12 +23,11 @@ FormCard.FormCardPage {
title: i18nc('@title:window', 'Notifications') title: i18nc('@title:window', 'Notifications')
FormCard.FormHeader { FormCard.FormHeader {
title: i18nc("@title:group", "Send Notifications For") title: i18nc("@title:group", "Room notifications setting")
} }
FormCard.FormCard { FormCard.FormCard {
FormCard.FormRadioDelegate { FormCard.FormRadioDelegate {
icon.name: "globe"
text: i18nc("As in the default notification setting", "Default Settings") text: i18nc("As in the default notification setting", "Default Settings")
checked: room.pushNotificationState === PushNotificationState.Default checked: room.pushNotificationState === PushNotificationState.Default
enabled: room.pushNotificationState !== PushNotificationState.Unknown enabled: room.pushNotificationState !== PushNotificationState.Unknown
@@ -37,8 +36,7 @@ FormCard.FormCardPage {
} }
} }
FormCard.FormRadioDelegate { FormCard.FormRadioDelegate {
icon.name: "notifications" text: i18nc("As in 'notify for all messages'", "All")
text: i18nc("As in 'notify for all messages'", "All Messages")
checked: room.pushNotificationState === PushNotificationState.All checked: room.pushNotificationState === PushNotificationState.All
enabled: room.pushNotificationState !== PushNotificationState.Unknown enabled: room.pushNotificationState !== PushNotificationState.Unknown
onToggled: { onToggled: {
@@ -46,7 +44,6 @@ FormCard.FormCardPage {
} }
} }
FormCard.FormRadioDelegate { FormCard.FormRadioDelegate {
icon.name: "im-user"
text: i18nc("As in 'notify when the user is mentioned or the message contains a set keyword'", "@Mentions and Keywords") text: i18nc("As in 'notify when the user is mentioned or the message contains a set keyword'", "@Mentions and Keywords")
checked: room.pushNotificationState === PushNotificationState.MentionKeyword checked: room.pushNotificationState === PushNotificationState.MentionKeyword
enabled: room.pushNotificationState !== PushNotificationState.Unknown enabled: room.pushNotificationState !== PushNotificationState.Unknown
@@ -55,8 +52,7 @@ FormCard.FormCardPage {
} }
} }
FormCard.FormRadioDelegate { FormCard.FormRadioDelegate {
icon.name: "notifications-disabled" text: i18nc("As in 'do not notify for any messages'", "Off")
text: i18nc("As in 'do not notify for any messages'", "None")
checked: room.pushNotificationState === PushNotificationState.Mute checked: room.pushNotificationState === PushNotificationState.Mute
enabled: room.pushNotificationState !== PushNotificationState.Unknown enabled: room.pushNotificationState !== PushNotificationState.Unknown
onToggled: { onToggled: {