Compare commits

..

1 Commits

Author SHA1 Message Date
Tobias Fella
43e5adee7e Use AccountSettingsGroup 2025-10-27 16:34:03 -04:00
106 changed files with 13257 additions and 28782 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 "2") 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

@@ -193,7 +193,6 @@
<li xml:lang="ar">التصويت - MSC3381</li> <li xml:lang="ar">التصويت - MSC3381</li>
<li xml:lang="ca">Votacions - MSC3381</li> <li xml:lang="ca">Votacions - MSC3381</li>
<li xml:lang="ca-valencia">Votacions - MSC3381</li> <li xml:lang="ca-valencia">Votacions - MSC3381</li>
<li xml:lang="de">Umfragen MSC3381</li>
<li xml:lang="el">Δημοσκοπήσεις - MSC3381</li> <li xml:lang="el">Δημοσκοπήσεις - MSC3381</li>
<li xml:lang="en-GB">Polls - MSC3381</li> <li xml:lang="en-GB">Polls - MSC3381</li>
<li xml:lang="eo">Enketoj - MSC3381</li> <li xml:lang="eo">Enketoj - MSC3381</li>
@@ -228,7 +227,6 @@
<li xml:lang="ar">حزم الملصقات - MSC2545</li> <li xml:lang="ar">حزم الملصقات - MSC2545</li>
<li xml:lang="ca">Paquets d'adhesius - MSC2545</li> <li xml:lang="ca">Paquets d'adhesius - MSC2545</li>
<li xml:lang="ca-valencia">Paquets d'adhesius - MSC2545</li> <li xml:lang="ca-valencia">Paquets d'adhesius - MSC2545</li>
<li xml:lang="de">Sticker-Pakete MSC2545</li>
<li xml:lang="el">Πακέτα αυτοκόλλητων - MSC2545</li> <li xml:lang="el">Πακέτα αυτοκόλλητων - MSC2545</li>
<li xml:lang="en-GB">Sticker Packs - MSC2545</li> <li xml:lang="en-GB">Sticker Packs - MSC2545</li>
<li xml:lang="eo">Glumark-Pakoj - MSC2545</li> <li xml:lang="eo">Glumark-Pakoj - MSC2545</li>
@@ -322,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>
@@ -490,10 +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.2" date="2026-02-05"/>
<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

@@ -216,12 +216,12 @@ void NotificationsManager::postNotification(NeoChatRoom *room,
} }
}); });
notification->setTitle(room->displayName());
QString entry; QString entry;
if (room->isDirectChat()) { if (sender == room->displayName()) {
notification->setTitle(sender);
entry = text.toHtmlEscaped(); entry = text.toHtmlEscaped();
} else { } else {
notification->setTitle(room->displayName());
entry = i18n("%1: %2", sender, text.toHtmlEscaped()); entry = i18n("%1: %2", sender, text.toHtmlEscaped());
} }
@@ -253,9 +253,7 @@ void NotificationsManager::postNotification(NeoChatRoom *room,
notification->setReplyAction(std::move(replyAction)); notification->setReplyAction(std::move(replyAction));
} }
if (Controller::instance().accounts()->rowCount() > 1) { notification->setHint(u"x-kde-origin-name"_s, room->localMember().id());
notification->setHint(u"x-kde-origin-name"_s, room->localMember().id());
}
notification->sendEvent(); notification->sendEvent();
} }
@@ -349,9 +347,7 @@ void NotificationsManager::doPostInviteNotification(QPointer<NeoChatRoom> room)
m_invitations.remove(room->id()); m_invitations.remove(room->id());
}); });
if (Controller::instance().accounts()->rowCount() > 1) { notification->setHint(u"x-kde-origin-name"_s, room->localMember().id());
notification->setHint(u"x-kde-origin-name"_s, room->localMember().id());
}
notification->sendEvent(); notification->sendEvent();
} }

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

@@ -22,7 +22,7 @@ Kirigami.SearchDialog {
} }
onAccepted: if (currentItem) { onAccepted: if (currentItem) {
(root.currentItem as RoomDelegate).clicked(); (currentItem as QQC2.ItemDelegate).clicked();
} }
onTextChanged: RoomManager.sortFilterRoomListModel.filterText = text onTextChanged: RoomManager.sortFilterRoomListModel.filterText = text

View File

@@ -31,7 +31,7 @@ Kirigami.Page {
Keys.onReturnPressed: event => { Keys.onReturnPressed: event => {
if (event.modifiers & Qt.ControlModifier) { if (event.modifiers & Qt.ControlModifier) {
root.accepted(reason.text); root.accepted(reason.text);
root.Kirigami.PageStack.closeDialog(); root.closeDialog();
} }
} }
@@ -52,14 +52,14 @@ Kirigami.Page {
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole
onClicked: { onClicked: {
root.accepted(reason.text); root.accepted(reason.text);
root.Kirigami.PageStack.closeDialog(); root.closeDialog();
} }
} }
QQC2.Button { QQC2.Button {
icon.name: "dialog-cancel-symbolic" icon.name: "dialog-cancel-symbolic"
text: i18nc("@action", "Cancel") text: i18nc("@action", "Cancel")
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.RejectRole QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.RejectRole
onClicked: root.Kirigami.PageStack.closeDialog() onClicked: root.closeDialog()
} }
} }
} }

View File

@@ -75,43 +75,13 @@ Kirigami.Page {
focus: true focus: true
padding: 0 padding: 0
background: null // This needs to stay null, because of transparency blur
onHeightChanged: {
// HACK: See TimelineView for the hack details.
// We get the height change here *first* so we are informed this is because of a window resize and not due to the pinned message.
(timelineViewLoader.item as TimelineView).resetViewSettling();
}
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);
@@ -121,24 +91,12 @@ Kirigami.Page {
} }
}, },
Kirigami.Action { Kirigami.Action {
visible: Kirigami.Settings.isMobile || !(root.Kirigami.PageStack.pageStack as Kirigami.PageRow)?.wideMode visible: Kirigami.Settings.isMobile || !(root.Kirigami.PageStack.pageStack as Kirigami.PageRow).wideMode
icon.name: "view-right-new" icon.name: "view-right-new"
onTriggered: (root.QQC2.ApplicationWindow.window as Main).openRoomDrawer() onTriggered: (root.QQC2.ApplicationWindow.window as Main).openRoomDrawer()
} }
] ]
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: {
@@ -235,8 +193,6 @@ Kirigami.Page {
// Used to keep track of messages so we can hide the right one at the right time // Used to keep track of messages so we can hide the right one at the right time
property string messageId property string messageId
Layout.fillWidth: true
showCloseButton: true showCloseButton: true
visible: false visible: false
position: Kirigami.InlineMessage.Position.Header position: Kirigami.InlineMessage.Position.Header
@@ -298,7 +254,7 @@ Kirigami.Page {
footer: Loader { footer: Loader {
id: chatBarLoader id: chatBarLoader
height: active ? (item as ChatBar)?.implicitHeight : 0 height: active ? (item as ChatBar).implicitHeight : 0
active: timelineViewLoader.active && !root.currentRoom.readOnly active: timelineViewLoader.active && !root.currentRoom.readOnly
sourceComponent: ChatBar { sourceComponent: ChatBar {
id: chatBar id: chatBar
@@ -358,7 +314,7 @@ Kirigami.Page {
}); });
} }
function onShowDelegateMenu(eventId: string, author, messageComponentType, plainText: string, richText: string, mimeType: string, progressInfo, selectedText: string, hoveredLink: string) { function onShowDelegateMenu(eventId: string, author, messageComponentType, plainText: string, richText: string, mimeType: string, progressInfo, isThread: bool, selectedText: string, hoveredLink: string) {
(delegateContextMenu.createObject(root, { (delegateContextMenu.createObject(root, {
author: author, author: author,
eventId: eventId, eventId: eventId,
@@ -366,8 +322,6 @@ Kirigami.Page {
mimeType: mimeType, mimeType: mimeType,
progressInfo: progressInfo, progressInfo: progressInfo,
messageComponentType: messageComponentType, messageComponentType: messageComponentType,
selectedText,
hoveredLink,
}) as DelegateContextMenu).popup(); }) as DelegateContextMenu).popup();
} }

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

@@ -29,28 +29,6 @@
#include <KIO/OpenUrlJob> #include <KIO/OpenUrlJob>
#endif #endif
/**
* @brief Stops RoomManager from updating the last room and space config.
*/
class LastRoomBlocker
{
public:
explicit LastRoomBlocker(RoomManager *manager)
: m_manager(manager)
{
Q_ASSERT(manager);
m_manager->m_dontUpdateLastRoom = true;
}
~LastRoomBlocker()
{
m_manager->m_dontUpdateLastRoom = false;
}
private:
RoomManager *m_manager;
};
RoomManager::RoomManager(QObject *parent) RoomManager::RoomManager(QObject *parent)
: QObject(parent) : QObject(parent)
, m_config(KSharedConfig::openStateConfig()) , m_config(KSharedConfig::openStateConfig())
@@ -346,6 +324,17 @@ void RoomManager::loadInitialRoom()
resolveResource(m_arg); resolveResource(m_arg);
} }
if (m_isMobile) {
QString lastSpace = m_lastRoomConfig.readEntry(u"lastSpace"_s, QString());
// We can't have empty keys in KConfig, so we stored it as "Home"
if (lastSpace == u"Home"_s) {
lastSpace.clear();
}
setCurrentSpace(lastSpace, false);
// We don't want to open a room on startup on mobile
return;
}
if (m_currentRoom) { if (m_currentRoom) {
// we opened a room with the arg parsing already // we opened a room with the arg parsing already
return; return;
@@ -358,14 +347,16 @@ void RoomManager::loadInitialRoom()
void RoomManager::openRoomForActiveConnection() void RoomManager::openRoomForActiveConnection()
{ {
Q_ASSERT(m_connection); if (!m_connection) {
setCurrentRoom({});
setCurrentSpace({}, false);
return;
}
auto lastSpace = m_lastRoomConfig.readEntry(u"lastSpace"_s, QString()); auto lastSpace = m_lastRoomConfig.readEntry(u"lastSpace"_s, QString());
if (lastSpace == u"Home"_s) { if (lastSpace == u"Home"_s) {
lastSpace.clear(); lastSpace.clear();
} }
// We don't want to open a room on startup on mobile setCurrentSpace(lastSpace, true);
setCurrentSpace(lastSpace, !m_isMobile);
} }
UriResolveResult RoomManager::visitUser(User *user, const QString &action) UriResolveResult RoomManager::visitUser(User *user, const QString &action)
@@ -522,7 +513,7 @@ void RoomManager::setConnection(NeoChatConnection *connection)
Q_EMIT connectionChanged(); Q_EMIT connectionChanged();
} }
void RoomManager::setCurrentSpace(const QString &spaceId, bool goToLastUsedRoom) void RoomManager::setCurrentSpace(const QString &spaceId, bool setRoom)
{ {
m_currentSpaceId = spaceId; m_currentSpaceId = spaceId;
@@ -542,65 +533,25 @@ void RoomManager::setCurrentSpace(const QString &spaceId, bool goToLastUsedRoom)
m_lastRoomConfig.writeEntry(u"lastSpace"_s, spaceId.isEmpty() ? u"Home"_s : spaceId); m_lastRoomConfig.writeEntry(u"lastSpace"_s, spaceId.isEmpty() ? u"Home"_s : spaceId);
} }
// If we requested to change to the last opened room, do so: if (!setRoom) {
if (goToLastUsedRoom) { return;
// We don't want to needlessly update the last room config here, that should only be done during explicit user action. }
LastRoomBlocker blocker(this);
// We can't have empty keys in KConfig, so it's stored as "Home": // We intentionally don't want to open the last room on mobile
if (const auto &lastRoom = m_lastRoomConfig.readEntry(spaceId.isEmpty() ? u"Home"_s : spaceId, QString()); !lastRoom.isEmpty()) { if (m_isMobile) {
resolveResource(lastRoom, "no_join"_L1); return;
return; }
}
// If no last room was opened, go to the space home: // We can't have empty keys in KConfig, so it's stored as "Home"
if (!spaceId.isEmpty() && spaceId != u"DM"_s) { if (const auto &lastRoom = m_lastRoomConfig.readEntry(spaceId.isEmpty() ? u"Home"_s : spaceId, QString()); !lastRoom.isEmpty()) {
resolveResource(spaceId, "no_join"_L1); resolveResource(lastRoom, "no_join"_L1);
return; return;
}
// Fallback to no room opened:
setCurrentRoom({});
} }
} if (!spaceId.isEmpty() && spaceId != u"DM"_s) {
resolveResource(spaceId, "no_join"_L1);
QString RoomManager::findSpaceIdForCurrentRoom() const return;
{
if (!m_currentRoom) {
return m_currentSpaceId;
} }
if (m_currentRoom->isDirectChat()) { setCurrentRoom({});
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)
@@ -620,25 +571,57 @@ void RoomManager::setCurrentRoom(const QString &roomId)
} }
Q_EMIT currentRoomChanged(); Q_EMIT currentRoomChanged();
if (m_connection) {
if (!m_dontUpdateLastRoom) {
if (roomId.isEmpty()) { if (roomId.isEmpty()) {
m_lastRoomConfig.deleteEntry(m_currentSpaceId); m_lastRoomConfig.deleteEntry(m_currentSpaceId);
return;
}
const auto spaceIdForRoom = findSpaceIdForCurrentRoom();
// We can't have empty keys in KConfig, so name it "Home"
if (spaceIdForRoom.isEmpty()) {
m_lastRoomConfig.writeEntry(u"Home"_s, roomId);
} else { } else {
m_lastRoomConfig.writeEntry(spaceIdForRoom, roomId); // We can't have empty keys in KConfig, so name it "Home"
} if (m_currentSpaceId.isEmpty()) {
m_lastRoomConfig.writeEntry(u"Home"_s, roomId);
if (m_currentSpaceId != spaceIdForRoom) { } else {
setCurrentSpace(spaceIdForRoom, false); m_lastRoomConfig.writeEntry(m_currentSpaceId, roomId);
}
} }
} }
if (roomId.isEmpty()) {
return;
}
if (m_currentRoom->isSpace()) {
return;
}
if (m_currentRoom->isDirectChat()) {
const auto roomsInSpace = SpaceHierarchyCache::instance().getRoomListForSpace(m_currentSpaceId, 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

@@ -337,11 +337,6 @@ Q_SIGNALS:
void currentSpaceChanged(); void currentSpaceChanged();
protected:
bool m_dontUpdateLastRoom = false; // Don't set directly, use LastRoomBlocker.
friend class LastRoomBlocker;
private: private:
bool m_isMobile = false; bool m_isMobile = false;
@@ -378,22 +373,8 @@ private:
void setCurrentRoom(const QString &roomId); void setCurrentRoom(const QString &roomId);
/** // Space ID, "DM", or empty string
* @brief Find the most appropriate space for the currently selected room void setCurrentSpace(const QString &spaceId, bool setRoom = true);
*
* 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;
/**
* @brief Sets the current space.
*
* @param spaceId The ID of the space, "DM" for direct messages or an empty string for Home.
* @param goToLastUsedRoom If true, we will navigate to the last opened room in this space.
*/
void setCurrentSpace(const QString &spaceId, bool goToLastUsedRoom = true);
/** /**
* @brief Resolve a user URI. * @brief Resolve a user URI.

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

@@ -65,7 +65,7 @@ QQC2.Popup {
padding: 2 padding: 2
implicitHeight: Kirigami.Units.gridUnit * 20 + 2 * padding implicitHeight: Kirigami.Units.gridUnit * 20 + 2 * padding
width: Math.min(contentItem.categoryIconSize * 11 + 2 * padding, QQC2.ApplicationWindow.window?.width) width: Math.min(contentItem.categoryIconSize * 11 + 2 * padding, QQC2.ApplicationWindow.window.width)
contentItem: EmojiPicker { contentItem: EmojiPicker {
id: emojiPicker id: emojiPicker
height: 400 height: 400

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

@@ -1181,10 +1181,7 @@ void NeoChatRoom::loadPinnedMessage()
connection()->callApi<GetOneRoomEventJob>(id(), mostRecentEventId).then([this](const auto &job) { connection()->callApi<GetOneRoomEventJob>(id(), mostRecentEventId).then([this](const auto &job) {
auto event = fromJson<event_ptr_tt<RoomEvent>>(job->jsonData()); auto event = fromJson<event_ptr_tt<RoomEvent>>(job->jsonData());
if (auto encEv = eventCast<EncryptedEvent>(event.get())) { if (auto encEv = eventCast<EncryptedEvent>(event.get())) {
auto decryptedMessage = decryptMessage(*encEv); event = decryptMessage(*encEv);
if (decryptedMessage) {
event = std::move(decryptedMessage);
}
} }
m_pinnedMessage = EventHandler::richBody(this, event.get()); m_pinnedMessage = EventHandler::richBody(this, event.get());
Q_EMIT pinnedMessageChanged(); Q_EMIT pinnedMessageChanged();
@@ -1652,12 +1649,6 @@ void NeoChatRoom::downloadEventFromServer(const QString &eventId)
} }
event_ptr_tt<RoomEvent> event = fromJson<event_ptr_tt<RoomEvent>>(job->jsonData()); event_ptr_tt<RoomEvent> event = fromJson<event_ptr_tt<RoomEvent>>(job->jsonData());
if (auto encEv = eventCast<EncryptedEvent>(event.get())) {
auto decryptedEvent = decryptMessage(*encEv);
if (decryptedEvent) {
event = std::move(decryptedEvent);
}
}
m_extraEvents.push_back(std::move(event)); m_extraEvents.push_back(std::move(event));
Q_EMIT extraEventLoaded(eventId); Q_EMIT extraEventLoaded(eventId);
}, },

View File

@@ -22,9 +22,9 @@ ColumnLayout {
/** /**
* @brief The canonical alias of the room, if it exists. Otherwise falls back to the first available alias. * @brief The canonical alias of the room, if it exists. Otherwise falls back to the first available alias.
*/ */
readonly property string roomAlias: room?.aliases[0] ?? "" readonly property var roomAlias: room.aliases[0]
spacing: 0 Layout.fillWidth: true
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true

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
@@ -121,6 +120,13 @@ QQC2.Control {
} }
} }
TapHandler {
enabled: root.time.toString() !== "Invalid Date"
acceptedButtons: Qt.LeftButton
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus
onTapped: RoomManager.maximizeCode(root.author, root.time, root.display, root.componentAttributes.class)
}
TapHandler { TapHandler {
acceptedDevices: PointerDevice.TouchScreen acceptedDevices: PointerDevice.TouchScreen
onLongPressed: RoomManager.viewEventMenu(root.eventId, root.Message.room, root.Message.selectedText, root.Message.hoveredLink); onLongPressed: RoomManager.viewEventMenu(root.eventId, root.Message.room, root.Message.selectedText, root.Message.hoveredLink);

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

@@ -23,7 +23,9 @@ Flow {
*/ */
required property ReactionModel reactionModel required property ReactionModel reactionModel
// HACK: We do not set Layout properties here, see BUG 504344 for the crash it caused. Layout.fillWidth: true
Layout.fillHeight: true
Layout.maximumWidth: Message.maxContentWidth
spacing: Kirigami.Units.smallSpacing spacing: Kirigami.Units.smallSpacing
@@ -49,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

@@ -38,7 +38,7 @@ RowLayout {
Layout.fillHeight: true Layout.fillHeight: true
implicitWidth: Kirigami.Units.smallSpacing implicitWidth: Kirigami.Units.smallSpacing
color: root.replyContentModel.author?.color ?? Kirigami.Theme.highlightColor color: root.replyContentModel.author.color
radius: Kirigami.Units.cornerRadius radius: Kirigami.Units.cornerRadius
} }
ColumnLayout { ColumnLayout {

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

@@ -269,8 +269,6 @@ void EventMessageContentModel::resetModel()
updateItineraryModel(); updateItineraryModel();
Q_EMIT componentsUpdated(); Q_EMIT componentsUpdated();
// We need QML to re-evaluate author (for example, reply colors) if it was previously null.
Q_EMIT authorChanged();
} }
void EventMessageContentModel::resetContent(bool isEditing, bool isThreading) void EventMessageContentModel::resetContent(bool isEditing, bool isThreading)

View File

@@ -87,6 +87,7 @@ QDateTime MessageContentModel::time() const
QString MessageContentModel::timeString() const QString MessageContentModel::timeString() const
{ {
return time().toLocalTime().toString(u"hh:mm"_s); return time().toLocalTime().toString(u"hh:mm"_s);
;
} }
QString MessageContentModel::authorId() const QString MessageContentModel::authorId() const

View File

@@ -20,6 +20,8 @@ ColumnLayout {
signal resolveResource(string idOrUri, string action) signal resolveResource(string idOrUri, string action)
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
spacing: 0 spacing: 0
Item { Item {

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

@@ -8,7 +8,6 @@ import org.kde.kirigami as Kirigami
import org.kde.neochat.libneochat import org.kde.neochat.libneochat
import org.kde.neochat.timeline as Timeline import org.kde.neochat.timeline as Timeline
import org.kde.neochat.settings as Settings
/** /**
* @brief Page for holding a room drawer component. * @brief Page for holding a room drawer component.
@@ -50,7 +49,7 @@ Kirigami.Page {
text: i18nc("@action:button", "Room settings") text: i18nc("@action:button", "Room settings")
icon.name: 'settings-configure-symbolic' icon.name: 'settings-configure-symbolic'
onTriggered: { onTriggered: {
Settings.RoomSettingsView.openRoomSettings(root.room, Settings.RoomSettingsView.Room); RoomSettingsView.openRoomSettings(root.room, RoomSettingsView.Room);
} }
} }
] ]

View File

@@ -48,10 +48,6 @@ QQC2.ScrollView {
ListView { ListView {
id: userList id: userList
// Used to determine if the view has settled and should stop perfoming the hack
property bool viewHasSettled: false
header: ColumnLayout { header: ColumnLayout {
id: columnLayout id: columnLayout
@@ -60,29 +56,15 @@ QQC2.ScrollView {
spacing: 0 spacing: 0
width: ListView.view ? ListView.view.width - ListView.view.leftMargin - ListView.view.rightMargin : 0 width: ListView.view ? ListView.view.width - ListView.view.leftMargin - ListView.view.rightMargin : 0
// HACK: Resettle this ListView while our labels wrap themselves. ListView doesn't do this automatically.
// We use the Timer to determine when its done internally reshuffling, so we don't accidentally send you to the top if you actually scrolled down.
onHeightChanged: {
if (!userList.viewHasSettled) {
userList.positionViewAtBeginning();
hackTimer.restart();
}
}
Timer {
id: hackTimer
// The internal wrapping and height changes happen quickly, so we don't need a long interval here
interval: 1
onTriggered: userList.viewHasSettled = true
}
Loader { Loader {
active: true active: true
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: Kirigami.Units.smallSpacing Layout.topMargin: Kirigami.Units.smallSpacing
visible: !root.room.isSpace visible: !root.room.isSpace
sourceComponent: root.room.isDirectChat() ? directChatDrawerHeader : groupChatDrawerHeader sourceComponent: root.room.isDirectChat() ? directChatDrawerHeader : groupChatDrawerHeader
onItemChanged: if (item) {
userList.positionViewAtBeginning();
}
} }
Kirigami.ListSectionHeader { Kirigami.ListSectionHeader {
@@ -97,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
@@ -125,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)
@@ -138,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'), {
@@ -154,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'), {
@@ -170,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
@@ -184,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
@@ -352,6 +315,5 @@ QQC2.ScrollView {
userList.headerItem.userListSearchField.text = ""; userList.headerItem.userListSearchField.text = "";
} }
userList.currentIndex = -1; userList.currentIndex = -1;
userList.viewHasSettled = false;
} }
} }

View File

@@ -32,8 +32,6 @@ RowLayout {
Kirigami.Heading { Kirigami.Heading {
Layout.fillWidth: true Layout.fillWidth: true
// Roughly equivalent to what Kirigami does for its built-in headings
Layout.leftMargin: Kirigami.Units.gridUnit - Kirigami.Units.mediumSpacing
visible: !root.collapsed visible: !root.collapsed
text: i18nc("@title", "Rooms") text: i18nc("@title", "Rooms")
} }
@@ -48,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
@@ -56,31 +58,19 @@ RowLayout {
QQC2.ToolButton { QQC2.ToolButton {
id: menuButton id: menuButton
property QQC2.Menu menuItem: null
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
} }
@@ -88,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

@@ -46,8 +46,6 @@ QQC2.ItemDelegate {
topPadding: 0 topPadding: 0
bottomPadding: 0 bottomPadding: 0
text: root.collapsed ? "" : root.displayName text: root.collapsed ? "" : root.displayName
onClicked: root.treeView.toggleExpanded(row)
} }
QQC2.ToolButton { QQC2.ToolButton {
id: collapseButton id: collapseButton

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"
}
}
}

Some files were not shown because too many files have changed in this diff Show More