Compare commits
115 Commits
work/tobia
...
v25.12.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc0d1aff35 | ||
|
|
bc594ec8a2 | ||
|
|
2a7f0b406c | ||
|
|
c9f685a733 | ||
|
|
5be43575fc | ||
|
|
558519f355 | ||
|
|
f83e2e2677 | ||
|
|
4c4f406c41 | ||
|
|
2f8a873202 | ||
|
|
78b3cfe916 | ||
|
|
0bc529da2d | ||
|
|
6ad6121dfa | ||
|
|
7b5b7a67ae | ||
|
|
59c8b82bc2 | ||
|
|
c6aec89b61 | ||
|
|
2ef3fd9d6c | ||
|
|
e64b6033f3 | ||
|
|
82989e7ef2 | ||
|
|
ddf272ab2b | ||
|
|
4d4b44e011 | ||
|
|
a17d7e18fd | ||
|
|
1e78f119f1 | ||
|
|
b6a4b67c22 | ||
|
|
e965e1680f | ||
|
|
7d4cc7a5cf | ||
|
|
112b4b54e5 | ||
|
|
782f096f21 | ||
|
|
57e0f04086 | ||
|
|
a639011db6 | ||
|
|
26e7f3780c | ||
|
|
8c96d05799 | ||
|
|
bc6e22bc6d | ||
|
|
4371c3f7e5 | ||
|
|
b88ee65a4c | ||
|
|
033e865a27 | ||
|
|
75ba46e292 | ||
|
|
d492ed038a | ||
|
|
b29108a2f7 | ||
|
|
2790d430ae | ||
|
|
23f61fff36 | ||
|
|
254d105e35 | ||
|
|
6dde57a786 | ||
|
|
d803fcb874 | ||
|
|
90e70a9295 | ||
|
|
2090e4dc0e | ||
|
|
5ced491d54 | ||
|
|
e156d4da90 | ||
|
|
b3aa2abd89 | ||
|
|
7627d6d0e2 | ||
|
|
77da7e6c7d | ||
|
|
1e3ce9d6cd | ||
|
|
321561fd89 | ||
|
|
856bc7b713 | ||
|
|
10e9b8d8f8 | ||
|
|
9f1803c551 | ||
|
|
9260c92026 | ||
|
|
9c7030a5db | ||
|
|
909e20889e | ||
|
|
74f4c291a0 | ||
|
|
242a248bf3 | ||
|
|
dfb0bb75f4 | ||
|
|
3cefd4b1ef | ||
|
|
3f3ce6b421 | ||
|
|
73d910421a | ||
|
|
1da44f3ae3 | ||
|
|
08836010c6 | ||
|
|
13042d9ba6 | ||
|
|
0e4b52ee62 | ||
|
|
4c32280343 | ||
|
|
a2e540d6ef | ||
|
|
573c8925d2 | ||
|
|
6c0bd850b0 | ||
|
|
1da9719314 | ||
|
|
3cd5f3a1c6 | ||
|
|
b4108f2eef | ||
|
|
bb7de18341 | ||
|
|
7b8328fce6 | ||
|
|
aeee6570c0 | ||
|
|
780b9a6f9b | ||
|
|
0ba06882d1 | ||
|
|
ce131a53e5 | ||
|
|
e9421e28dd | ||
|
|
1c64a6b5f0 | ||
|
|
9b5200c344 | ||
|
|
5baf4ab823 | ||
|
|
58a72a08f2 | ||
|
|
c9b97d4d0d | ||
|
|
2a7d61c73b | ||
|
|
f55bd28e10 | ||
|
|
2a67861099 | ||
|
|
273d962707 | ||
|
|
5e8b44fea6 | ||
|
|
be9e2ec7d0 | ||
|
|
ee042cc1a2 | ||
|
|
25c0bc131a | ||
|
|
7def8c066c | ||
|
|
1ddaf37e52 | ||
|
|
40694f502a | ||
|
|
d9f4a0a032 | ||
|
|
5e392f3101 | ||
|
|
ce1ac6128e | ||
|
|
a9d08a6ee2 | ||
|
|
24d4829ba9 | ||
|
|
a121c39b6e | ||
|
|
f009420c20 | ||
|
|
069e0d8f16 | ||
|
|
1f1db11197 | ||
|
|
be92e56c3a | ||
|
|
d1fc426513 | ||
|
|
c778ba8b24 | ||
|
|
07fb3160eb | ||
|
|
7444d68280 | ||
|
|
1d5536401d | ||
|
|
099e996f2f | ||
|
|
6d9974b2b1 |
@@ -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'
|
||||
|
||||
@@ -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 "2")
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -193,6 +193,7 @@
|
||||
<li xml:lang="ar">التصويت - MSC3381</li>
|
||||
<li xml:lang="ca">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="en-GB">Polls - MSC3381</li>
|
||||
<li xml:lang="eo">Enketoj - MSC3381</li>
|
||||
@@ -227,6 +228,7 @@
|
||||
<li xml:lang="ar">حزم الملصقات - MSC2545</li>
|
||||
<li xml:lang="ca">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="en-GB">Sticker Packs - MSC2545</li>
|
||||
<li xml:lang="eo">Glumark-Pakoj - MSC2545</li>
|
||||
@@ -320,7 +322,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 +490,10 @@
|
||||
<content_attribute id="social-chat">intense</content_attribute>
|
||||
</content_rating>
|
||||
<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.1" date="2025-09-11"/>
|
||||
<release version="25.08.0" date="2025-08-14"/>
|
||||
|
||||
688
po/ar/neochat.po
688
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
702
po/az/neochat.po
702
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
717
po/ca/neochat.po
717
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
725
po/cs/neochat.po
725
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
654
po/da/neochat.po
654
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
1786
po/de/neochat.po
1786
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
728
po/el/neochat.po
728
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
736
po/eo/neochat.po
736
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
618
po/es/neochat.po
618
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
798
po/eu/neochat.po
798
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
737
po/fi/neochat.po
737
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
848
po/fr/neochat.po
848
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
7002
po/ga/neochat.po
Normal file
7002
po/ga/neochat.po
Normal file
File diff suppressed because it is too large
Load Diff
739
po/gl/neochat.po
739
po/gl/neochat.po
File diff suppressed because it is too large
Load Diff
675
po/he/neochat.po
675
po/he/neochat.po
File diff suppressed because it is too large
Load Diff
727
po/hi/neochat.po
727
po/hi/neochat.po
File diff suppressed because it is too large
Load Diff
1084
po/hu/neochat.po
1084
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
1026
po/ia/neochat.po
1026
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
742
po/id/neochat.po
742
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
694
po/ie/neochat.po
694
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
754
po/it/neochat.po
754
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
582
po/ja/neochat.po
582
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
676
po/ka/neochat.po
676
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
754
po/ko/neochat.po
754
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
638
po/lt/neochat.po
638
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
751
po/lv/neochat.po
751
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
687
po/nl/neochat.po
687
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
646
po/nn/neochat.po
646
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
702
po/pa/neochat.po
702
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
811
po/pl/neochat.po
811
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
731
po/pt/neochat.po
731
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
122
po/pt_BR/docs/neochat/man-neochat.1.docbook
Normal file
122
po/pt_BR/docs/neochat/man-neochat.1.docbook
Normal 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&component=General"
|
||||
>https://bugs.kde.org/enter_bug.cgi?product=NeoChat&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 © 2020-2022 Tobias Fella </para>
|
||||
<para
|
||||
>Direitos autorais © 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
|
||||
>></para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
File diff suppressed because it is too large
Load Diff
658
po/ro/neochat.po
658
po/ro/neochat.po
File diff suppressed because it is too large
Load Diff
751
po/ru/neochat.po
751
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
727
po/sa/neochat.po
727
po/sa/neochat.po
File diff suppressed because it is too large
Load Diff
713
po/sk/neochat.po
713
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
673
po/sl/neochat.po
673
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
684
po/sv/neochat.po
684
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
762
po/ta/neochat.po
762
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
703
po/tr/neochat.po
703
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
685
po/uk/neochat.po
685
po/uk/neochat.po
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
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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]=有新的加入聊天室邀請
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -216,12 +216,12 @@ void NotificationsManager::postNotification(NeoChatRoom *room,
|
||||
}
|
||||
});
|
||||
|
||||
notification->setTitle(room->displayName());
|
||||
|
||||
QString entry;
|
||||
if (sender == room->displayName()) {
|
||||
notification->setTitle(sender);
|
||||
if (room->isDirectChat()) {
|
||||
entry = text.toHtmlEscaped();
|
||||
} else {
|
||||
notification->setTitle(room->displayName());
|
||||
entry = i18n("%1: %2", sender, text.toHtmlEscaped());
|
||||
}
|
||||
|
||||
@@ -253,7 +253,9 @@ void NotificationsManager::postNotification(NeoChatRoom *room,
|
||||
notification->setReplyAction(std::move(replyAction));
|
||||
}
|
||||
|
||||
notification->setHint(u"x-kde-origin-name"_s, room->localMember().id());
|
||||
if (Controller::instance().accounts()->rowCount() > 1) {
|
||||
notification->setHint(u"x-kde-origin-name"_s, room->localMember().id());
|
||||
}
|
||||
notification->sendEvent();
|
||||
}
|
||||
|
||||
@@ -347,7 +349,9 @@ void NotificationsManager::doPostInviteNotification(QPointer<NeoChatRoom> room)
|
||||
m_invitations.remove(room->id());
|
||||
});
|
||||
|
||||
notification->setHint(u"x-kde-origin-name"_s, room->localMember().id());
|
||||
if (Controller::instance().accounts()->rowCount() > 1) {
|
||||
notification->setHint(u"x-kde-origin-name"_s, room->localMember().id());
|
||||
}
|
||||
|
||||
notification->sendEvent();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
22
src/app/qml/MeetingDialog.qml
Normal file
22
src/app/qml/MeetingDialog.qml
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ Kirigami.SearchDialog {
|
||||
}
|
||||
|
||||
onAccepted: if (currentItem) {
|
||||
(currentItem as QQC2.ItemDelegate).clicked();
|
||||
(root.currentItem as RoomDelegate).clicked();
|
||||
}
|
||||
|
||||
onTextChanged: RoomManager.sortFilterRoomListModel.filterText = text
|
||||
|
||||
@@ -31,7 +31,7 @@ Kirigami.Page {
|
||||
Keys.onReturnPressed: event => {
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
root.accepted(reason.text);
|
||||
root.closeDialog();
|
||||
root.Kirigami.PageStack.closeDialog();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,14 +52,14 @@ Kirigami.Page {
|
||||
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole
|
||||
onClicked: {
|
||||
root.accepted(reason.text);
|
||||
root.closeDialog();
|
||||
root.Kirigami.PageStack.closeDialog();
|
||||
}
|
||||
}
|
||||
QQC2.Button {
|
||||
icon.name: "dialog-cancel-symbolic"
|
||||
text: i18nc("@action", "Cancel")
|
||||
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.RejectRole
|
||||
onClicked: root.closeDialog()
|
||||
onClicked: root.Kirigami.PageStack.closeDialog()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,13 +75,43 @@ Kirigami.Page {
|
||||
focus: true
|
||||
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: [
|
||||
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);
|
||||
@@ -91,12 +121,24 @@ Kirigami.Page {
|
||||
}
|
||||
},
|
||||
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"
|
||||
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)
|
||||
|
||||
onCurrentRoomChanged: {
|
||||
@@ -193,6 +235,8 @@ Kirigami.Page {
|
||||
// Used to keep track of messages so we can hide the right one at the right time
|
||||
property string messageId
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
showCloseButton: true
|
||||
visible: false
|
||||
position: Kirigami.InlineMessage.Position.Header
|
||||
@@ -254,7 +298,7 @@ Kirigami.Page {
|
||||
|
||||
footer: Loader {
|
||||
id: chatBarLoader
|
||||
height: active ? (item as ChatBar).implicitHeight : 0
|
||||
height: active ? (item as ChatBar)?.implicitHeight : 0
|
||||
active: timelineViewLoader.active && !root.currentRoom.readOnly
|
||||
sourceComponent: ChatBar {
|
||||
id: chatBar
|
||||
@@ -314,7 +358,7 @@ Kirigami.Page {
|
||||
});
|
||||
}
|
||||
|
||||
function onShowDelegateMenu(eventId: string, author, messageComponentType, plainText: string, richText: string, mimeType: string, progressInfo, isThread: bool, selectedText: string, hoveredLink: string) {
|
||||
function onShowDelegateMenu(eventId: string, author, messageComponentType, plainText: string, richText: string, mimeType: string, progressInfo, selectedText: string, hoveredLink: string) {
|
||||
(delegateContextMenu.createObject(root, {
|
||||
author: author,
|
||||
eventId: eventId,
|
||||
@@ -322,6 +366,8 @@ Kirigami.Page {
|
||||
mimeType: mimeType,
|
||||
progressInfo: progressInfo,
|
||||
messageComponentType: messageComponentType,
|
||||
selectedText,
|
||||
hoveredLink,
|
||||
}) as DelegateContextMenu).popup();
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -29,6 +29,28 @@
|
||||
#include <KIO/OpenUrlJob>
|
||||
#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)
|
||||
: QObject(parent)
|
||||
, m_config(KSharedConfig::openStateConfig())
|
||||
@@ -324,17 +346,6 @@ void RoomManager::loadInitialRoom()
|
||||
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) {
|
||||
// we opened a room with the arg parsing already
|
||||
return;
|
||||
@@ -347,16 +358,14 @@ void RoomManager::loadInitialRoom()
|
||||
|
||||
void RoomManager::openRoomForActiveConnection()
|
||||
{
|
||||
if (!m_connection) {
|
||||
setCurrentRoom({});
|
||||
setCurrentSpace({}, false);
|
||||
return;
|
||||
}
|
||||
Q_ASSERT(m_connection);
|
||||
|
||||
auto lastSpace = m_lastRoomConfig.readEntry(u"lastSpace"_s, QString());
|
||||
if (lastSpace == u"Home"_s) {
|
||||
lastSpace.clear();
|
||||
}
|
||||
setCurrentSpace(lastSpace, true);
|
||||
// We don't want to open a room on startup on mobile
|
||||
setCurrentSpace(lastSpace, !m_isMobile);
|
||||
}
|
||||
|
||||
UriResolveResult RoomManager::visitUser(User *user, const QString &action)
|
||||
@@ -513,7 +522,7 @@ void RoomManager::setConnection(NeoChatConnection *connection)
|
||||
Q_EMIT connectionChanged();
|
||||
}
|
||||
|
||||
void RoomManager::setCurrentSpace(const QString &spaceId, bool setRoom)
|
||||
void RoomManager::setCurrentSpace(const QString &spaceId, bool goToLastUsedRoom)
|
||||
{
|
||||
m_currentSpaceId = spaceId;
|
||||
|
||||
@@ -533,25 +542,65 @@ void RoomManager::setCurrentSpace(const QString &spaceId, bool setRoom)
|
||||
m_lastRoomConfig.writeEntry(u"lastSpace"_s, spaceId.isEmpty() ? u"Home"_s : spaceId);
|
||||
}
|
||||
|
||||
if (!setRoom) {
|
||||
return;
|
||||
}
|
||||
// If we requested to change to the last opened room, do so:
|
||||
if (goToLastUsedRoom) {
|
||||
// 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 intentionally don't want to open the last room on mobile
|
||||
if (m_isMobile) {
|
||||
return;
|
||||
}
|
||||
// We can't have empty keys in KConfig, so it's stored as "Home":
|
||||
if (const auto &lastRoom = m_lastRoomConfig.readEntry(spaceId.isEmpty() ? u"Home"_s : spaceId, QString()); !lastRoom.isEmpty()) {
|
||||
resolveResource(lastRoom, "no_join"_L1);
|
||||
return;
|
||||
}
|
||||
|
||||
// We can't have empty keys in KConfig, so it's stored as "Home"
|
||||
if (const auto &lastRoom = m_lastRoomConfig.readEntry(spaceId.isEmpty() ? u"Home"_s : spaceId, QString()); !lastRoom.isEmpty()) {
|
||||
resolveResource(lastRoom, "no_join"_L1);
|
||||
return;
|
||||
// If no last room was opened, go to the space home:
|
||||
if (!spaceId.isEmpty() && spaceId != u"DM"_s) {
|
||||
resolveResource(spaceId, "no_join"_L1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback to no room opened:
|
||||
setCurrentRoom({});
|
||||
}
|
||||
if (!spaceId.isEmpty() && spaceId != u"DM"_s) {
|
||||
resolveResource(spaceId, "no_join"_L1);
|
||||
return;
|
||||
}
|
||||
|
||||
QString RoomManager::findSpaceIdForCurrentRoom() const
|
||||
{
|
||||
if (!m_currentRoom) {
|
||||
return m_currentSpaceId;
|
||||
}
|
||||
setCurrentRoom({});
|
||||
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)
|
||||
@@ -571,57 +620,25 @@ void RoomManager::setCurrentRoom(const QString &roomId)
|
||||
}
|
||||
|
||||
Q_EMIT currentRoomChanged();
|
||||
if (m_connection) {
|
||||
|
||||
if (!m_dontUpdateLastRoom) {
|
||||
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()) {
|
||||
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;
|
||||
}
|
||||
|
||||
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_currentSpaceId != spaceIdForRoom) {
|
||||
setCurrentSpace(spaceIdForRoom, false);
|
||||
}
|
||||
}
|
||||
setCurrentSpace({}, false);
|
||||
}
|
||||
|
||||
void RoomManager::clearCurrentRoom()
|
||||
|
||||
@@ -337,6 +337,11 @@ Q_SIGNALS:
|
||||
|
||||
void currentSpaceChanged();
|
||||
|
||||
protected:
|
||||
bool m_dontUpdateLastRoom = false; // Don't set directly, use LastRoomBlocker.
|
||||
|
||||
friend class LastRoomBlocker;
|
||||
|
||||
private:
|
||||
bool m_isMobile = false;
|
||||
|
||||
@@ -373,8 +378,22 @@ private:
|
||||
|
||||
void setCurrentRoom(const QString &roomId);
|
||||
|
||||
// Space ID, "DM", or empty string
|
||||
void setCurrentSpace(const QString &spaceId, bool setRoom = true);
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* @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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -65,7 +65,7 @@ QQC2.Popup {
|
||||
padding: 2
|
||||
|
||||
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 {
|
||||
id: emojiPicker
|
||||
height: 400
|
||||
|
||||
@@ -148,7 +148,7 @@ ColumnLayout {
|
||||
id: quickReactions
|
||||
Layout.fillWidth: true
|
||||
|
||||
model: ["👍", "👎", "😄", "🎉", "😕", "❤", "🚀", "👀"]
|
||||
model: ["👍", "👎", "😄", "🎉", "😕", "❤️", "🚀", "👀"]
|
||||
|
||||
delegate: EmojiDelegate {
|
||||
required property string modelData
|
||||
|
||||
@@ -17,6 +17,7 @@ Kirigami.ScrollablePage {
|
||||
|
||||
property NeoChatRoom room
|
||||
required property NeoChatConnection connection
|
||||
property alias currentTabIndex: tabBar.currentIndex
|
||||
|
||||
title: i18nc("@title", "Developer Tools")
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "general_logging.h"
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
using namespace Quotient;
|
||||
|
||||
AccountManager::AccountManager(bool testMode, QObject *parent)
|
||||
: QObject(parent)
|
||||
@@ -40,7 +39,7 @@ Quotient::AccountRegistry *AccountManager::accounts()
|
||||
|
||||
void AccountManager::loadAccountsFromCache()
|
||||
{
|
||||
for (const auto &accountId : AccountSettingsGroup().childGroups()) {
|
||||
for (const auto &accountId : Quotient::SettingsGroup("Accounts"_L1).childGroups()) {
|
||||
Quotient::AccountSettings account{accountId};
|
||||
m_accountsLoading += accountId;
|
||||
Q_EMIT accountsLoadingChanged();
|
||||
|
||||
@@ -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 {};
|
||||
}
|
||||
|
||||
@@ -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>"),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
@@ -190,7 +197,7 @@ void NeoChatConnection::setKeywordPushRuleDefault(PushRuleAction::Action default
|
||||
|
||||
void NeoChatConnection::logout(bool serverSideLogout)
|
||||
{
|
||||
AccountSettingsGroup().remove(userId());
|
||||
SettingsGroup(u"Accounts"_s).remove(userId());
|
||||
|
||||
QKeychain::DeletePasswordJob job(qAppName());
|
||||
job.setAutoDelete(true);
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1181,7 +1181,10 @@ void NeoChatRoom::loadPinnedMessage()
|
||||
connection()->callApi<GetOneRoomEventJob>(id(), mostRecentEventId).then([this](const auto &job) {
|
||||
auto event = fromJson<event_ptr_tt<RoomEvent>>(job->jsonData());
|
||||
if (auto encEv = eventCast<EncryptedEvent>(event.get())) {
|
||||
event = decryptMessage(*encEv);
|
||||
auto decryptedMessage = decryptMessage(*encEv);
|
||||
if (decryptedMessage) {
|
||||
event = std::move(decryptedMessage);
|
||||
}
|
||||
}
|
||||
m_pinnedMessage = EventHandler::richBody(this, event.get());
|
||||
Q_EMIT pinnedMessageChanged();
|
||||
@@ -1649,6 +1652,12 @@ void NeoChatRoom::downloadEventFromServer(const QString &eventId)
|
||||
}
|
||||
|
||||
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));
|
||||
Q_EMIT extraEventLoaded(eventId);
|
||||
},
|
||||
|
||||
@@ -22,9 +22,9 @@ ColumnLayout {
|
||||
/**
|
||||
* @brief The canonical alias of the room, if it exists. Otherwise falls back to the first available alias.
|
||||
*/
|
||||
readonly property var roomAlias: room.aliases[0]
|
||||
readonly property string roomAlias: room?.aliases[0] ?? ""
|
||||
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -120,13 +121,6 @@ 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 {
|
||||
acceptedDevices: PointerDevice.TouchScreen
|
||||
onLongPressed: RoomManager.viewEventMenu(root.eventId, root.Message.room, root.Message.selectedText, root.Message.hoveredLink);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -23,9 +23,7 @@ Flow {
|
||||
*/
|
||||
required property ReactionModel reactionModel
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.maximumWidth: Message.maxContentWidth
|
||||
// HACK: We do not set Layout properties here, see BUG 504344 for the crash it caused.
|
||||
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
@@ -51,6 +49,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
|
||||
|
||||
@@ -38,7 +38,7 @@ RowLayout {
|
||||
Layout.fillHeight: true
|
||||
|
||||
implicitWidth: Kirigami.Units.smallSpacing
|
||||
color: root.replyContentModel.author.color
|
||||
color: root.replyContentModel.author?.color ?? Kirigami.Theme.highlightColor
|
||||
radius: Kirigami.Units.cornerRadius
|
||||
}
|
||||
ColumnLayout {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -269,6 +269,8 @@ void EventMessageContentModel::resetModel()
|
||||
updateItineraryModel();
|
||||
|
||||
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)
|
||||
|
||||
@@ -87,7 +87,6 @@ QDateTime MessageContentModel::time() const
|
||||
QString MessageContentModel::timeString() const
|
||||
{
|
||||
return time().toLocalTime().toString(u"hh:mm"_s);
|
||||
;
|
||||
}
|
||||
|
||||
QString MessageContentModel::authorId() const
|
||||
|
||||
@@ -20,8 +20,6 @@ ColumnLayout {
|
||||
|
||||
signal resolveResource(string idOrUri, string action)
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.neochat.libneochat
|
||||
import org.kde.neochat.timeline as Timeline
|
||||
import org.kde.neochat.settings as Settings
|
||||
|
||||
/**
|
||||
* @brief Page for holding a room drawer component.
|
||||
@@ -49,7 +50,7 @@ Kirigami.Page {
|
||||
text: i18nc("@action:button", "Room settings")
|
||||
icon.name: 'settings-configure-symbolic'
|
||||
onTriggered: {
|
||||
RoomSettingsView.openRoomSettings(root.room, RoomSettingsView.Room);
|
||||
Settings.RoomSettingsView.openRoomSettings(root.room, Settings.RoomSettingsView.Room);
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -48,6 +48,10 @@ QQC2.ScrollView {
|
||||
|
||||
ListView {
|
||||
id: userList
|
||||
|
||||
// Used to determine if the view has settled and should stop perfoming the hack
|
||||
property bool viewHasSettled: false
|
||||
|
||||
header: ColumnLayout {
|
||||
id: columnLayout
|
||||
|
||||
@@ -56,15 +60,29 @@ QQC2.ScrollView {
|
||||
spacing: 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 {
|
||||
active: true
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||
visible: !root.room.isSpace
|
||||
sourceComponent: root.room.isDirectChat() ? directChatDrawerHeader : groupChatDrawerHeader
|
||||
onItemChanged: if (item) {
|
||||
userList.positionViewAtBeginning();
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.ListSectionHeader {
|
||||
@@ -79,7 +97,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 +125,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 +138,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 +154,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 +170,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 +184,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
|
||||
@@ -315,5 +352,6 @@ QQC2.ScrollView {
|
||||
userList.headerItem.userListSearchField.text = "";
|
||||
}
|
||||
userList.currentIndex = -1;
|
||||
userList.viewHasSettled = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ RowLayout {
|
||||
|
||||
Kirigami.Heading {
|
||||
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
|
||||
text: i18nc("@title", "Rooms")
|
||||
}
|
||||
@@ -46,10 +48,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 +56,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: 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()
|
||||
}
|
||||
}
|
||||
|
||||
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 +88,8 @@ RowLayout {
|
||||
Component {
|
||||
id: menu
|
||||
QQC2.Menu {
|
||||
y: menuButton.height
|
||||
|
||||
QQC2.MenuItem {
|
||||
text: i18n("Find your friends")
|
||||
icon.name: "list-add-user"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -46,6 +46,8 @@ QQC2.ItemDelegate {
|
||||
topPadding: 0
|
||||
bottomPadding: 0
|
||||
text: root.collapsed ? "" : root.displayName
|
||||
|
||||
onClicked: root.treeView.toggleExpanded(row)
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
id: collapseButton
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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] {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
52
src/settings/KeyboardShortcutsPage.qml
Normal file
52
src/settings/KeyboardShortcutsPage.qml
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user