Compare commits

...

71 Commits

Author SHA1 Message Date
l10n daemon script
2090e4dc0e GIT_SILENT Sync po/docbooks with svn 2026-01-06 03:41:42 +00:00
l10n daemon script
5ced491d54 GIT_SILENT Sync po/docbooks with svn 2026-01-05 03:42:28 +00:00
Azhar Momin
e156d4da90 Fix notification count refresh for low-priority and mentions-only rooms
(cherry picked from commit 5f7967363f)
2026-01-04 17:13:27 -05:00
Joshua Goins
b3aa2abd89 Fix icons on Windows
KirigamiApp currently calls KIconTheme::initTheme too late for Windows,
as a workaround we can go back to calling it ourselves.

(cherry picked from commit a02a04d966)
2026-01-04 17:13:27 -05:00
Veres Károly
7627d6d0e2 Extract the space selection logic from setCurrentRoom and use it for setting lastRoomConfig too.
If a setCurrentRoom call changed the active space at the end of its execution, the new room's ID ended up still being written to the old space's lastRoomConfig.

By extracting this space selection logic into a helper function, we can now calculate this value earlier and use it as the space id when writing lastRoomConfig.

(cherry picked from commit 68b00b9fc5)
2026-01-04 17:13:27 -05:00
Nate Graham
77da7e6c7d Improve hamburger menu button
- Open the menu right beneath the button
- Use pressed state for the button while the menu is open
- Close the menu when clicking the button again
- Hide the tooltip while the menu is open

(cherry picked from commit bd0588ca99)
2026-01-04 17:13:27 -05:00
l10n daemon script
1e3ce9d6cd GIT_SILENT Sync po/docbooks with svn 2026-01-04 03:38:17 +00:00
Heiko Becker
321561fd89 GIT_SILENT Update Appstream for new release 2026-01-03 12:14:39 +01:00
Heiko Becker
856bc7b713 GIT_SILENT Upgrade release service version to 25.12.1. 2026-01-03 11:15:22 +01:00
l10n daemon script
10e9b8d8f8 GIT_SILENT Sync po/docbooks with svn 2026-01-02 03:39:44 +00:00
l10n daemon script
9f1803c551 GIT_SILENT Sync po/docbooks with svn 2025-12-31 03:38:13 +00:00
l10n daemon script
9260c92026 GIT_SILENT Sync po/docbooks with svn 2025-12-30 03:42:39 +00:00
l10n daemon script
9c7030a5db GIT_SILENT Sync po/docbooks with svn 2025-12-29 03:13:12 +00:00
l10n daemon script
909e20889e GIT_SILENT Sync po/docbooks with svn 2025-12-27 03:11:47 +00:00
l10n daemon script
74f4c291a0 GIT_SILENT Sync po/docbooks with svn 2025-12-24 03:36:13 +00:00
Carl Schwan
242a248bf3 Send beautiful red ❤️'s when quick reacting
I wondered for a while (and could tell) when people were using NeoChat
because they would react with cold, monochrome hearts. Let's add more
color to our world!


(cherry picked from commit 531df7a3b2)

Co-authored-by: Joshua Goins <josh@redstrate.com>
2025-12-21 19:59:56 +01:00
Carl Schwan
dfb0bb75f4 Fix missing escape sequence in /shrug command
Before the fix, the upper arm _ characters in the command's output would be parsed as Markdown italic formatting around the (ツ).


(cherry picked from commit 706f1f7836)

Co-authored-by: Veres Károly <mail+kde@karcsesz.hu>
2025-12-21 18:07:46 +01:00
l10n daemon script
3cefd4b1ef GIT_SILENT Sync po/docbooks with svn 2025-12-14 03:13:17 +00:00
l10n daemon script
3f3ce6b421 GIT_SILENT Sync po/docbooks with svn 2025-12-13 03:17:45 +00:00
l10n daemon script
73d910421a GIT_SILENT Sync po/docbooks with svn 2025-12-10 03:32:09 +00:00
l10n daemon script
1da44f3ae3 GIT_SILENT Sync po/docbooks with svn 2025-12-09 03:15:27 +00:00
l10n daemon script
08836010c6 GIT_SILENT Sync po/docbooks with svn 2025-12-07 03:22:57 +00:00
l10n daemon script
13042d9ba6 GIT_SILENT Sync po/docbooks with svn 2025-12-04 03:20:39 +00:00
Heiko Becker
0e4b52ee62 GIT_SILENT Update Appstream for new release 2025-12-04 00:19:55 +01:00
Heiko Becker
4c32280343 GIT_SILENT Upgrade release service version to 25.12.0. 2025-12-03 23:04:35 +01:00
l10n daemon script
a2e540d6ef GIT_SILENT Sync po/docbooks with svn 2025-12-03 03:26:04 +00:00
Carl Schwan
573c8925d2 Fix krunner integration with Flatpak
(cherry picked from commit 1da24191f0)

Co-authored-by: renner 03 <renner0@posteo.de>
2025-12-02 23:00:25 +01:00
l10n daemon script
6c0bd850b0 GIT_SILENT Sync po/docbooks with svn 2025-12-01 03:28:35 +00:00
l10n daemon script
1da9719314 GIT_SILENT Sync po/docbooks with svn 2025-11-25 03:15:21 +00:00
Heiko Becker
3cd5f3a1c6 Drop unused dependencies
Both KF6Crash and KF6IconThemes aren't used anymore after porting to
KirigamiApp in eab45e761a.

(cherry picked from commit 0cb3fd32f4)
2025-11-24 21:56:55 +01:00
Albert Astals Cid
b4108f2eef GIT_SILENT Upgrade release service version to 25.11.90. 2025-11-24 00:57:39 +01:00
l10n daemon script
bb7de18341 GIT_SILENT Sync po/docbooks with svn 2025-11-23 03:19:33 +00:00
l10n daemon script
7b8328fce6 GIT_SILENT Sync po/docbooks with svn 2025-11-21 03:20:12 +00:00
l10n daemon script
aeee6570c0 GIT_SILENT Sync po/docbooks with svn 2025-11-20 03:14:24 +00:00
l10n daemon script
780b9a6f9b GIT_SILENT Sync po/docbooks with svn 2025-11-17 15:49:57 +00:00
l10n daemon script
0ba06882d1 GIT_SILENT Sync po/docbooks with svn 2025-11-17 03:16:16 +00:00
l10n daemon script
ce131a53e5 GIT_SILENT Sync po/docbooks with svn 2025-11-16 03:16:40 +00:00
l10n daemon script
e9421e28dd SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2025-11-16 03:13:00 +00:00
l10n daemon script
1c64a6b5f0 GIT_SILENT Sync po/docbooks with svn 2025-11-15 03:19:51 +00:00
l10n daemon script
9b5200c344 GIT_SILENT Sync po/docbooks with svn 2025-11-14 03:21:54 +00:00
l10n daemon script
5baf4ab823 GIT_SILENT Sync po/docbooks with svn 2025-11-13 03:20:11 +00:00
l10n daemon script
58a72a08f2 GIT_SILENT Sync po/docbooks with svn 2025-11-11 03:37:16 +00:00
l10n daemon script
c9b97d4d0d GIT_SILENT Sync po/docbooks with svn 2025-11-10 03:26:49 +00:00
Carl Schwan
2a7d61c73b Improve invited room counting
I didn't realize when redoing the tooltip for DMs that directChatInvites
was a boolean, not an integer type. Now it's changed to an integer type,
which fixes the DM invite count.

NeoChat apparently didn't count normal room invites until now either, so
now Home is highlighted in that case. Now it should be harder to miss
these kinds of invites.


(cherry picked from commit 887865c0aa)

Co-authored-by: Joshua Goins <josh@redstrate.com>
2025-11-09 22:23:09 +01:00
l10n daemon script
f55bd28e10 GIT_SILENT Sync po/docbooks with svn 2025-11-09 03:45:49 +00:00
Albert Astals Cid
2a67861099 GIT_SILENT Upgrade release service version to 25.11.80. 2025-11-06 17:40:37 +01:00
l10n daemon script
273d962707 GIT_SILENT Sync po/docbooks with svn 2025-11-06 01:40:42 +00:00
Paul Brown
5e8b44fea6 Adding Akseli as supporter 2025-11-05 21:56:17 +00:00
l10n daemon script
be9e2ec7d0 GIT_SILENT Sync po/docbooks with svn 2025-11-05 01:48:44 +00:00
l10n daemon script
ee042cc1a2 GIT_SILENT Sync po/docbooks with svn 2025-11-04 01:47:11 +00:00
Kristen McWilliam
25c0bc131a feat: add font size scaling setting
Adds an option in Appearance Settings to adjust font size scaling,
improving accessibility and user customization.
2025-11-03 10:18:23 -05:00
Paul Brown
7def8c066c Added anonymous donot to list of supporters 2025-11-03 12:50:59 +00:00
l10n daemon script
1ddaf37e52 GIT_SILENT Sync po/docbooks with svn 2025-11-02 01:39:31 +00:00
Heiko Becker
40694f502a Fix version in appstream file
GIT_SILENT
2025-11-01 20:01:46 +01:00
l10n daemon script
d9f4a0a032 GIT_SILENT Sync po/docbooks with svn 2025-11-01 01:44:57 +00:00
l10n daemon script
5e392f3101 GIT_SILENT Sync po/docbooks with svn 2025-10-31 01:39:58 +00:00
Heiko Becker
ce1ac6128e GIT_SILENT Update Appstream for new release
(cherry picked from commit c6fa5a10dd)
2025-10-31 01:23:25 +01:00
l10n daemon script
a9d08a6ee2 GIT_SILENT Sync po/docbooks with svn 2025-10-30 01:41:27 +00:00
Joshua Goins
24d4829ba9 Clarify what is "recent activity" in our room list ordering settings
As seen in the bug report, this setting is a bit confusing. First, it
refers to "message activity" but in reality it does take into account
all events. This is fine in my opinion, so I clarified that point.

Another thing is that it wasn't clear that timeline visibility settings
currently affect the sorting, so I added a tip about that.

Finally I wasn't happy with these two options being called "Activity"
so the old "Activity" setting is now called "Importance". The "Last
Message Activity" setting is now called "Newest Events".

BUG: 508480
FIXED-IN: 25.12.0
2025-10-29 18:18:53 -04:00
l10n daemon script
a121c39b6e GIT_SILENT Sync po/docbooks with svn 2025-10-29 01:41:34 +00:00
Joshua Goins
f009420c20 Improve the design of the room notifications settings
Now it has icons that match what we have in the room menu, and the
duplicate header text is removed.
2025-10-28 12:27:23 -04:00
Joshua Goins
069e0d8f16 Add informational Keyboard Shortcuts settings page
While we don't have customizable keyboard shortcuts (yet!) we have been
asked before for some way to view all of NeoChat's keyboard shortcuts.
2025-10-28 10:29:55 -04:00
Joshua Goins
1f1db11197 Fix plugin on LocationsPage
This lives in the base NeoChat QML module, not the libneochat one.
2025-10-28 09:17:18 -04:00
Joshua Goins
be92e56c3a Show less scary icon for neutral reasons when cancelling verification
For example, accepting a verification on another device shows a giant
red security icon which isn't really suitable. I chucked the
dialog-information icon in for some of the neutral-sounding messages so
this dialog can be a little less intimidating.

BUG: 510421
FIXED-IN: 24.08.3
2025-10-28 09:16:17 -04:00
l10n daemon script
d1fc426513 GIT_SILENT Sync po/docbooks with svn 2025-10-28 01:41:19 +00:00
Joshua Goins
c778ba8b24 Fix jump to previous/next unread room shortcuts
These were dependent on a renamed property and ended up breaking, oops.
2025-10-27 18:09:00 -04:00
Joshua Goins
07fb3160eb Add "Inspect Room Data" button in the room sidebar
I find myself doing the same routine: I want to inspect a room's state,
so I have to go hunting for the Developer Tools button. And then I have
to do a few clicks to even get the correct room, what a waste of time!

So I added a new button to the sidebar to open the Developer Tools for
the current room.
2025-10-27 18:08:51 -04:00
Joshua Goins
7444d68280 Change room sidebar action text
As we've continued to add more and more room actions, I'm not entirely
happy with how they're worded. Some are super short and sweet like
"Verify User" while others refer to the room as if the sidebar wasn't
enough context: "Search in this room".

I redid all of the button's text so they're shorter, like "Search
Messages".
2025-10-27 18:08:42 -04:00
Joshua Goins
1d5536401d Make the CTRL+F shortcut search the current room's messages
Right now there's not an easy way to quickly bring up message search. If
you press CTRL+F (with the room information sidebar *closed*, for some
reason) that brings up the same dialog as CTRL+K which seems redundant.

I assigned that shortcut to the message search dialog instead, which is
makes much more sense in my opinion. I also made sure its disabled in
spaces or when there's not a room open.

BUG: 487270
FIXED-IN: 25.12.0
2025-10-27 18:08:29 -04:00
Joshua Goins
099e996f2f Improve standalone images in link preview and more
We would incorrectly show a "truncate" button for standalone images
which isn't applicable since there's no text. That check has been fixed,
and it doesn't seem to regress normal link previews.

Another is that if there's only an image, our layout would center the
image which looks awkward since almost everything else is left-aligned
in chat. This is also fixed, which notably matches up to Element Web's
behavior.

I added support for the hover link indicator as well. Someone could
maliciously hide it via Markdown but have a legitimate-looking link
preview, for example. You can check that by hovering over the link in
the message itself, but now the link preview is another way to confirm
that!
2025-10-27 18:08:21 -04:00
Joshua Goins
6d9974b2b1 Improve the Jitsi meeting button UX
Widgets support is incredibly useful and so is this button, but it has a
few problems. The most obvious is that it's still enabled even if you
don't actually have the permission to start Jitsi meetings, so I fixed
that. I also made sure it's hidden when viewing spaces too.

Another problem is that you can't easily tell if a meeting is currently
in progress either, nor do we have a good icon for that in Breeze. So I
changed the tooltip and colored the icon in this case.

The final problem I fixed is something not exclusive to NeoChat, but
generally all chat applications with this feature - there's no
confirmation! To stop "butt-dialing" random people or rooms, I added a
prompt before starting or joining a meeting.
2025-10-27 18:08:08 -04:00
90 changed files with 26639 additions and 11089 deletions

View File

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

View File

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

View File

@@ -63,7 +63,7 @@ void ActionsTest::testActions_data()
QTest::addColumn<std::optional<QString>>("resultText");
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);
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)

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::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::supporters">.</value>
<value key="KDE::supporters">Anonymous donor, Akseli</value>
</custom>
<launchable type="desktop-id">org.kde.neochat.desktop</launchable>
<screenshots>
@@ -488,6 +488,9 @@
<content_attribute id="social-chat">intense</content_attribute>
</content_rating>
<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.1" date="2025-09-11"/>
<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

7002
po/ga/neochat.po Normal file

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

@@ -0,0 +1,122 @@
<?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,6 +103,7 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
qml/ReasonDialog.qml
qml/NewPollDialog.qml
qml/UserMenu.qml
qml/MeetingDialog.qml
DEPENDENCIES
QtCore
QtQuick
@@ -152,6 +153,7 @@ target_include_directories(neochat-app PRIVATE ${CMAKE_BINARY_DIR})
target_link_libraries(neochat-app PRIVATE
neochat
KF6::IconThemes
)
ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png)
@@ -202,7 +204,6 @@ target_link_libraries(neochat PUBLIC
KF6::ConfigGui
KF6::CoreAddons
KF6::SonnetCore
KF6::IconThemes
KF6::ItemModels
KF6::I18nQml
KirigamiApp
@@ -213,10 +214,6 @@ target_link_libraries(neochat PUBLIC
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)
if(NEOCHAT_FLATPAK)
@@ -356,7 +353,8 @@ endif()
install(TARGETS neochat-app ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
install(FILES plasma-runner-neochat.desktop DESTINATION ${KDE_INSTALL_DATAROOTDIR}/krunner/dbusplugins)
# 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 RENAME org.kde.neochat.desktop)
endif()
if (APPLE)

View File

@@ -103,6 +103,10 @@ int main(int argc, char *argv[])
{
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
QtWebView::initialize();
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);

View File

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

View File

@@ -66,6 +66,10 @@
</entry>
</group>
<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">
<label>Show avatar in the timeline</label>
<default>true</default>

View File

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

View File

@@ -0,0 +1,22 @@
// 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,11 +77,33 @@ Kirigami.Page {
actions: [
Kirigami.Action {
tooltip: i18nc("@action:button", "Open Jitsi Meet in browser")
icon.name: "camera-video-symbolic"
id: jitsiMeetingAction
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: {
let url
if (root.widgetModel.jitsiIndex < 0) {
const dialog = Qt.createComponent("org.kde.neochat", "MeetingDialog").createObject(QQC2.Overlay.overlay, { hasExistingMeeting });
dialog.onAccepted.connect(doAction);
dialog.open();
}
function doAction(): void {
let url;
if (!hasExistingMeeting) {
url = root.widgetModel.addJitsiConference();
} else {
let idx = root.widgetModel.index(root.widgetModel.jitsiIndex, 0);
@@ -97,6 +119,18 @@ 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)
onCurrentRoomChanged: {

View File

@@ -11,7 +11,19 @@ VerificationMessage {
required property int reason
icon: "security-low"
icon: {
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: {
switch (root.reason) {
case KeyVerificationSession.NONE:

View File

@@ -554,6 +554,45 @@ void RoomManager::setCurrentSpace(const QString &spaceId, bool setRoom)
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)
{
if (m_currentRoom != nullptr) {
@@ -571,57 +610,23 @@ void RoomManager::setCurrentRoom(const QString &roomId)
}
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()) {
m_lastRoomConfig.deleteEntry(m_currentSpaceId);
return;
}
if (m_currentRoom->isSpace()) {
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 {
m_lastRoomConfig.writeEntry(spaceIdForRoom, roomId);
}
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;
if (m_currentSpaceId != spaceIdForRoom) {
setCurrentSpace(spaceIdForRoom, false);
}
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()

View File

@@ -373,6 +373,15 @@ private:
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
void setCurrentSpace(const QString &spaceId, bool setRoom = true);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -75,30 +75,37 @@ void NeoChatConnection::connectSignals()
Q_EMIT directChatInvitesChanged();
for (const auto &chatId : additions) {
if (const auto chat = room(chatId)) {
connect(chat, &Room::unreadStatsChanged, this, [this]() {
refreshBadgeNotificationCount();
Q_EMIT directChatNotificationsChanged();
Q_EMIT directChatsHaveHighlightNotificationsChanged();
connect(chat, &Room::changed, this, [this](Room::Changes changes) {
if (changes & (Room::Change::UnreadStats | Room::Change::Highlights)) {
refreshBadgeNotificationCount();
Q_EMIT directChatNotificationsChanged();
Q_EMIT directChatsHaveHighlightNotificationsChanged();
}
});
}
}
for (const auto &chatId : removals) {
if (const auto chat = room(chatId)) {
disconnect(chat, &Room::unreadStatsChanged, this, nullptr);
disconnect(chat, &Room::changed, this, nullptr);
}
}
});
connect(this, &NeoChatConnection::joinedRoom, this, [this](Room *room) {
if (room->isDirectChat()) {
connect(room, &Room::unreadStatsChanged, this, [this]() {
Q_EMIT directChatNotificationsChanged();
Q_EMIT directChatsHaveHighlightNotificationsChanged();
connect(room, &Room::changed, this, [this](Room::Changes changes) {
if (changes & (Room::Change::UnreadStats | Room::Change::Highlights)) {
Q_EMIT directChatNotificationsChanged();
Q_EMIT directChatsHaveHighlightNotificationsChanged();
}
});
}
connect(room, &Room::unreadStatsChanged, this, [this]() {
refreshBadgeNotificationCount();
Q_EMIT homeNotificationsChanged();
Q_EMIT homeHaveHighlightNotificationsChanged();
Q_EMIT roomInvitesChanged();
connect(room, &Room::changed, this, [this](Room::Changes changes) {
if (changes & (Room::Change::UnreadStats | Room::Change::Highlights)) {
refreshBadgeNotificationCount();
Q_EMIT homeNotificationsChanged();
Q_EMIT homeHaveHighlightNotificationsChanged();
}
});
});
connect(this, &NeoChatConnection::leftRoom, this, [this](Room *room, Room *prev) {
@@ -452,15 +459,20 @@ bool NeoChatConnection::homeHaveHighlightNotifications() const
return false;
}
bool NeoChatConnection::directChatInvites() const
qsizetype NeoChatConnection::directChatInvites() const
{
auto inviteRooms = rooms(JoinState::Invite);
for (const auto inviteRoom : inviteRooms) {
if (inviteRoom->isDirectChat()) {
return true;
}
}
return false;
const auto inviteRooms = rooms(JoinState::Invite);
return std::ranges::count_if(inviteRooms, [](const auto room) {
return room->isDirectChat();
});
}
qsizetype NeoChatConnection::roomInvites() const
{
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)

View File

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

View File

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

View File

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

View File

@@ -41,7 +41,12 @@ QQC2.Control {
*/
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.
@@ -72,7 +77,7 @@ QQC2.Control {
id: previewImage
Layout.preferredWidth: root.defaultHeight
Layout.preferredHeight: root.defaultHeight
Layout.fillWidth: true
Layout.maximumWidth: root.defaultHeight
Layout.fillHeight: true
visible: root.linkPreviewer.imageSource.toString().length > 0
source: root.linkPreviewer.imageSource
@@ -82,9 +87,9 @@ QQC2.Control {
}
ColumnLayout {
id: column
implicitWidth: Math.max(linkPreviewTitle.implicitWidth, linkPreviewDescription.implicitWidth)
Layout.preferredWidth: Math.max(linkPreviewTitle.implicitWidth, linkPreviewDescription.implicitWidth)
Layout.fillWidth: true
spacing: Kirigami.Units.smallSpacing
visible: root.linkPreviewer.title.length > 0 || root.linkPreviewer.description.length > 0
Kirigami.Heading {
id: linkPreviewTitle
Layout.fillWidth: true
@@ -121,10 +126,11 @@ QQC2.Control {
acceptedButtons: Qt.LeftButton
onTapped: RoomManager.resolveResource(root.linkPreviewer.url, "join")
}
}
HoverHandler {
cursorShape: Qt.PointingHandCursor
HoverHandler {
cursorShape: Qt.PointingHandCursor
onHoveredChanged: (root.QQC2.ApplicationWindow.window as Main).hoverLinkIndicator.text = hovered ? root.linkPreviewer.url : ""
}
}
QQC2.Button {

View File

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

View File

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

View File

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

View File

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

View File

@@ -79,7 +79,7 @@ QQC2.ScrollView {
id: searchButton
visible: !root.room.isSpace
icon.name: "search"
text: i18n("Search in this room")
text: i18nc("@action:button", "Search Messages")
activeFocusOnTab: true
Layout.fillWidth: true
@@ -107,7 +107,7 @@ QQC2.ScrollView {
id: favouriteButton
visible: !root.room.isSpace
icon.name: root.room && root.room.isFavourite ? "rating" : "rating-unrated"
text: root.room && root.room.isFavourite ? i18n("Remove room from favorites") : i18n("Favorite this room")
text: root.room && root.room.isFavourite ? i18nc("@action:button", "Remove from Favorites") : i18nc("@action:button", "Add to Favorites")
onClicked: root.room.isFavourite ? root.room.removeTag("m.favourite") : root.room.addTag("m.favourite", 1.0)
@@ -120,7 +120,7 @@ QQC2.ScrollView {
id: widgetsButton
visible: !root.room.isSpace
icon.name: "extension-symbolic"
text: i18nc("@action:button", "Extensions for this room")
text: i18nc("@action:button", "Extensions")
activeFocusOnTab: true
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
visible: !root.room.isSpace
icon.name: "map-flat"
text: i18n("Show locations for this room")
text: i18nc("@action:button", "Shared Locations")
activeFocusOnTab: true
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
visible: !root.room.isSpace
icon.name: "pin-symbolic"
text: i18nc("@action:button", "Pinned messages")
text: i18nc("@action:button", "Pinned Messages")
activeFocusOnTab: true
Layout.fillWidth: true
@@ -166,10 +166,29 @@ 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 {
id: leaveButton
icon.name: "arrow-left-symbolic"
text: root.room.isSpace ? i18nc("@action:button", "Leave this space…") : i18nc("@action:button", "Leave this room…")
text: root.room.isSpace ? i18nc("@action:button", "Leave Space…") : i18nc("@action:button", "Leave Room…")
activeFocusOnTab: true
Layout.fillWidth: true

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -43,6 +43,40 @@ FormCard.FormCardPage {
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 {

View File

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

View File

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

View File

@@ -97,6 +97,12 @@ KirigamiSettings.ConfigurationView {
}
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 {
moduleId: "aboutNeochat"
text: i18n("About NeoChat")

View File

@@ -23,11 +23,12 @@ FormCard.FormCardPage {
title: i18nc('@title:window', 'Notifications')
FormCard.FormHeader {
title: i18nc("@title:group", "Room notifications setting")
title: i18nc("@title:group", "Send Notifications For")
}
FormCard.FormCard {
FormCard.FormRadioDelegate {
icon.name: "globe"
text: i18nc("As in the default notification setting", "Default Settings")
checked: room.pushNotificationState === PushNotificationState.Default
enabled: room.pushNotificationState !== PushNotificationState.Unknown
@@ -36,7 +37,8 @@ FormCard.FormCardPage {
}
}
FormCard.FormRadioDelegate {
text: i18nc("As in 'notify for all messages'", "All")
icon.name: "notifications"
text: i18nc("As in 'notify for all messages'", "All Messages")
checked: room.pushNotificationState === PushNotificationState.All
enabled: room.pushNotificationState !== PushNotificationState.Unknown
onToggled: {
@@ -44,6 +46,7 @@ FormCard.FormCardPage {
}
}
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")
checked: room.pushNotificationState === PushNotificationState.MentionKeyword
enabled: room.pushNotificationState !== PushNotificationState.Unknown
@@ -52,7 +55,8 @@ FormCard.FormCardPage {
}
}
FormCard.FormRadioDelegate {
text: i18nc("As in 'do not notify for any messages'", "Off")
icon.name: "notifications-disabled"
text: i18nc("As in 'do not notify for any messages'", "None")
checked: room.pushNotificationState === PushNotificationState.Mute
enabled: room.pushNotificationState !== PushNotificationState.Unknown
onToggled: {