Compare commits

..

84 Commits

Author SHA1 Message Date
Tobias Fella
fe3516b044 Explicitely set room for EmojiPicker 2023-08-07 22:47:23 +02:00
l10n daemon script
794683b5d2 GIT_SILENT Sync po/docbooks with svn 2023-08-07 02:08:13 +00:00
l10n daemon script
9f7f5409bd 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"
2023-08-07 01:51:19 +00:00
l10n daemon script
7d7b9d7ae0 GIT_SILENT made messages (after extraction) 2023-08-07 00:48:50 +00:00
James Graham
c7c892c51c Fix message long press
Add a tap handler so that long press events in the text of a message can be captured. This should make mobile menus work on text messages again.
2023-08-06 17:08:46 +00:00
l10n daemon script
97903a7cb0 GIT_SILENT Sync po/docbooks with svn 2023-08-06 01:50:23 +00:00
l10n daemon script
0c8f488ac4 GIT_SILENT made messages (after extraction) 2023-08-06 00:46:42 +00:00
Tobias Fella
88368cefaa Remove leftover debug statement 2023-08-06 00:51:28 +02:00
Tobias Fella
ab4bb41979 Fix last active time in devices page 2023-08-05 20:32:47 +02:00
James Graham
c2398b19dc Fix mark all read on room change
Fix it so that the view is correctly positioned at the bottom when the room is changed and make sure this is not seen as a scroll event (i.e. messages are not automatically marked as read but the timer has to time out first and all visible on screen).
2023-08-05 17:38:21 +00:00
James Graham
4b3dac025f Declare currentRoom property
Make sure `currentRoom` is a property for every qml component where it's used in `chatbar`.
2023-08-05 16:50:06 +00:00
l10n daemon script
9eeff06328 GIT_SILENT Sync po/docbooks with svn 2023-08-05 01:46:52 +00:00
l10n daemon script
657a8291d0 GIT_SILENT made messages (after extraction) 2023-08-05 00:46:28 +00:00
James Graham
bc4431462a Chatbox id for all property refs
Change the chatbox id to root and use for all references to it properties. Using root.currentRoom is needed for the chatbar to be visible in KF6
2023-08-04 13:56:26 +00:00
l10n daemon script
fdd8a62873 GIT_SILENT Sync po/docbooks with svn 2023-08-04 01:47:18 +00:00
l10n daemon script
c164228894 GIT_SILENT made messages (after extraction) 2023-08-04 00:46:08 +00:00
Carl Schwan
7d418f0883 Shorten appstream description 2023-08-03 16:17:58 +00:00
Tobias Fella
e90aecbead Port UserDetailDialog to Kirigami.Dialog 2023-08-03 17:26:46 +02:00
l10n daemon script
221c27fbbe GIT_SILENT Sync po/docbooks with svn 2023-08-03 02:09:50 +00:00
l10n daemon script
1907404124 GIT_SILENT Sync po/docbooks with svn 2023-08-02 01:54:58 +00:00
James Graham
81e953dd8c use QTEST_MAIN 2023-08-01 15:57:58 +00:00
James Graham
9311052e39 Move polishcolor to a utils namespace and rename getusercolor 2023-08-01 15:57:58 +00:00
James Graham
648fff20f2 Updates based on comments 2023-08-01 15:57:58 +00:00
James Graham
980211f36a Remove unneeded include 2023-08-01 15:57:58 +00:00
James Graham
25e0e3fa8e Remove NeoChatUser and just use Quotient::User this simplifies things and avoids casting from one to the other which address santizer didn't like 2023-08-01 15:57:58 +00:00
l10n daemon script
403c1ed391 GIT_SILENT Sync po/docbooks with svn 2023-08-01 01:58:12 +00:00
l10n daemon script
c2f76612cf GIT_SILENT made messages (after extraction) 2023-08-01 00:46:09 +00:00
Tobias Fella
2a651b8946 Use pageStack.pushDialogLayer for devtools and inviteuser 2023-07-31 20:44:50 +02:00
Volker Krause
00c0d1c276 Add heading indicator for live locations
Heading isn't covered by any MSC so far, so that is using Itinerary's
vendor prefix for now.
2023-07-31 18:06:52 +02:00
Carl Schwan
0162d91b60 Ensure e2ee is enabled 2023-07-31 07:09:23 +00:00
Carl Schwan
5f41b7b4c1 Fix opening account menu on right click 2023-07-31 07:02:00 +00:00
l10n daemon script
d9691bac7c GIT_SILENT Sync po/docbooks with svn 2023-07-31 02:10:40 +00:00
Tobias Fella
719b916837 Add accessible name to RoomDelegate 2023-07-30 22:57:46 +02:00
Tobias Fella
f9514fe925 Add accessible name to chatbar 2023-07-30 22:57:43 +02:00
Tobias Fella
399b9e6944 Add text and tooltip to section collapse/expand button 2023-07-30 22:29:21 +02:00
l10n daemon script
00cf5c8a5b GIT_SILENT Sync po/docbooks with svn 2023-07-30 01:49:49 +00:00
l10n daemon script
02f26424e2 GIT_SILENT made messages (after extraction) 2023-07-30 00:45:54 +00:00
Tobias Fella
3bd639775a Fix room list icon size in compact room list
BUG: 472682
2023-07-29 19:00:58 +00:00
Tobias Fella
163c1ef407 Html-escape display names in state delegates 2023-07-29 15:42:53 +02:00
Tobias Fella
9997b1074a Make "Close to system tray" string more correct
BUG: 472051
2023-07-29 09:01:14 +00:00
l10n daemon script
f52a25717b GIT_SILENT Sync po/docbooks with svn 2023-07-29 01:46:50 +00:00
l10n daemon script
5746c24b73 GIT_SILENT Sync po/docbooks with svn 2023-07-28 01:55:04 +00:00
l10n daemon script
dca1b22521 GIT_SILENT made messages (after extraction) 2023-07-28 00:47:00 +00:00
l10n daemon script
b15f1461d8 GIT_SILENT Sync po/docbooks with svn 2023-07-27 01:47:22 +00:00
Joshua Goins
24fd7a0f5b Use prettier notification images
Avatars are shown rounded in the main interface, so they should look the
same in the notification tray too. On top of that, if the room is a
group then show that group's icon when applicable in the bottom right.
2023-07-26 08:31:58 +00:00
Joshua Goins
0ac3932303 Add an obvious "upload" button in the account editor
It's not obvious at first glance that you can click on the avatar in the
account editor to upload an image. This adds a new button when there is
no image uploaded.
2023-07-26 08:27:55 +00:00
Joshua Goins
807a9ee779 Fix errors preventing the avatar from being shown when uploaded 2023-07-26 08:27:55 +00:00
Joshua Goins
4f32a168cd Fix settings button tooltip 2023-07-26 02:04:24 -04:00
l10n daemon script
45d2da56c7 GIT_SILENT Sync po/docbooks with svn 2023-07-26 01:49:31 +00:00
l10n daemon script
d794e26d62 GIT_SILENT made messages (after extraction) 2023-07-26 00:46:56 +00:00
l10n daemon script
42cc42642d GIT_SILENT Sync po/docbooks with svn 2023-07-25 01:47:33 +00:00
l10n daemon script
ffd6205bd1 GIT_SILENT Sync po/docbooks with svn 2023-07-24 09:05:17 +00:00
Tobias Fella
9ba6b2f0a5 Fix clearing user search field text when switching rooms 2023-07-24 08:45:51 +00:00
Carl Schwan
f8d3e25f8e Move no connection info message to the top 2023-07-24 08:45:10 +00:00
Carl Schwan
75cd77facb Refactor completion menu
- Use new Avatar
- Use required property in delegate
2023-07-24 08:44:49 +00:00
l10n daemon script
8546d76a54 GIT_SILENT made messages (after extraction) 2023-07-24 07:34:02 +00:00
Tobias Fella
00b7f68a03 Fix nullptr check 2023-07-23 23:10:52 +02:00
Carl Schwan
b486cb905c Add back some spacing around the avatar in the timeline 2023-07-22 18:41:09 +00:00
Carl Schwan
cdd5c9f484 Fix accessibility of StartChatPage and port to new components 2023-07-22 18:41:09 +00:00
Carl Schwan
3e6c426397 Port Avatar usage in Settings KirigamiAddons 2023-07-22 18:41:09 +00:00
Carl Schwan
847f41e1ad Port most Avatar usage to new KirigamiAddons Avatar 2023-07-22 18:41:09 +00:00
Carl Schwan
ed5bb270ff Discard wrong avatar urls 2023-07-22 17:51:03 +00:00
Carl Schwan
c8b4da2b96 Scroll to top of RoomDrawer on loading of header 2023-07-22 17:50:32 +00:00
Tobias Fella
ba5445e135 Stop finding QtKeychain
Has been upstreamed to libQuotient
2023-07-22 16:14:56 +02:00
l10n daemon script
1cca39e105 GIT_SILENT Sync po/docbooks with svn 2023-07-22 01:57:06 +00:00
Carl Schwan
dbf67b984e Fix reply error message when the reply is not loaded yet 2023-07-21 11:24:10 +02:00
l10n daemon script
c9126cf38e GIT_SILENT Sync po/docbooks with svn 2023-07-21 02:04:06 +00:00
Tobias Fella
13988da4fc Add login appium test 2023-07-20 07:14:23 +00:00
Carl Schwan
0847839abc Remove hover effect on timeline events 2023-07-20 08:40:15 +02:00
Carl Schwan
6b55e502a0 Remove duplicated code in QuickSwitcher for RoomDelegate 2023-07-20 08:39:59 +02:00
Carl Schwan
8f81629ac1 Redesign room list
Use KirigamiAddons.Delegated.RoundedItemDelegate
2023-07-20 08:39:57 +02:00
Carl Schwan
7f459cb90f Redesign timeline
Remove border von bubbles and only hilight the bubble on hover
2023-07-20 08:38:51 +02:00
l10n daemon script
420e195313 GIT_SILENT Sync po/docbooks with svn 2023-07-20 01:59:01 +00:00
Carl Schwan
3263a69880 Port to KirigamAddons.Banner 2023-07-19 22:46:36 +02:00
l10n daemon script
b060881f06 GIT_SILENT Sync po/docbooks with svn 2023-07-19 02:41:03 +00:00
Tobias Fella
701e786c1f Require passing tests on windows 2023-07-18 23:23:33 +02:00
Tobias Fella
646c8ba8fe Fix opening user mentions 2023-07-18 05:54:15 +00:00
Tobias Fella
9b31fdea10 Cleanup CMake after depending on libQuotient 0.7 2023-07-18 05:26:49 +00:00
l10n daemon script
ce5dfdee16 GIT_SILENT Sync po/docbooks with svn 2023-07-18 01:55:04 +00:00
Tobias Fella
918e805718 Fix image loading in room avatars 2023-07-17 11:25:52 +00:00
Albert Astals Cid
7debf47833 GIT_SILENT Upgrade release service version to 23.11.70. 2023-07-17 12:53:16 +02:00
Tobias Fella
2c142c36e6 Remove cmake policy 2023-07-17 11:57:19 +02:00
l10n daemon script
3279142498 GIT_SILENT Sync po/docbooks with svn 2023-07-17 01:57:57 +00:00
l10n daemon script
0e08a1aa7e GIT_SILENT made messages (after extraction) 2023-07-17 00:49:09 +00:00
96 changed files with 12436 additions and 6528 deletions

View File

@@ -58,5 +58,9 @@ Dependencies:
'require':
'frameworks/kdbusaddons': '@latest-kf6'
- 'on': ['Linux/Qt6', 'Linux/Qt5']
'require':
'sdk/selenium-webdriver-at-spi': '@latest-kf6'
Options:
require-passing-tests-on: [ 'Linux/Qt5', 'FreeBSD' ]
require-passing-tests-on: [ 'Linux/Qt5', 'FreeBSD', 'Windows' ]

View File

@@ -8,8 +8,8 @@ cmake_minimum_required(VERSION 3.16)
# KDE Applications version, managed by release script.
set(RELEASE_SERVICE_VERSION_MAJOR "23")
set(RELEASE_SERVICE_VERSION_MINOR "07")
set(RELEASE_SERVICE_VERSION_MICRO "80")
set(RELEASE_SERVICE_VERSION_MINOR "11")
set(RELEASE_SERVICE_VERSION_MICRO "70")
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
@@ -93,12 +93,6 @@ set_package_properties(KF${QT_MAJOR_VERSION}Kirigami2 PROPERTIES
)
find_package(KF${QT_MAJOR_VERSION}KirigamiAddons 0.7.2 REQUIRED)
find_package(Qt${QT_MAJOR_VERSION}Keychain)
set_package_properties(Qt${QT_MAJOR_VERSION}Keychain PROPERTIES
TYPE REQUIRED
PURPOSE "Secure storage of account secrets"
)
if(ANDROID)
find_package(OpenSSL)
set_package_properties(OpenSSL PROPERTIES
@@ -130,6 +124,11 @@ set_package_properties(Quotient${QUOTIENT_SUFFIX} PROPERTIES
PURPOSE "Talk with matrix server"
)
if (NOT TARGET Olm::Olm)
message(FATAL_ERROR "NeoChat requires Quotient with the E2EE feature enabled")
endif()
find_package(cmark)
set_package_properties(cmark PROPERTIES
TYPE REQUIRED
@@ -160,10 +159,6 @@ set_package_properties(KF${QT_MAJOR_VERSION}DocTools PROPERTIES DESCRIPTION
TYPE OPTIONAL
)
if(NOT Quotient${QUOTIENT_SUFFIX}_VERSION_MINOR GREATER 6)
cmake_policy(SET CMP0063 OLD)
endif()
if(ANDROID)
find_package(Sqlite3)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/android/version.gradle.in ${CMAKE_BINARY_DIR}/version.gradle)
@@ -179,9 +174,11 @@ install(FILES org.kde.neochat.tray.svg DESTINATION ${KDE_INSTALL_FULL_ICONDIR}/h
add_definitions(-DQT_NO_FOREACH)
add_subdirectory(src)
if (BUILD_TESTING AND Quotient${QUOTIENT_SUFFIX}_VERSION_MINOR GREATER 6)
if (BUILD_TESTING)
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} NO_MODULE COMPONENTS Test)
add_subdirectory(autotests)
add_subdirectory(appiumtests)
endif()
if(KF${QT_MAJOR_VERSION}DocTools_FOUND)

View File

@@ -0,0 +1,23 @@
# SPDX-License-Identifier: BSD-3-Clause
# SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
if(NOT BUILD_TESTING OR NOT CMAKE_SYSTEM_NAME MATCHES "Linux")
return()
endif()
find_package(SeleniumWebDriverATSPI)
set_package_properties(SeleniumWebDriverATSPI PROPERTIES
DESCRIPTION "Server component for selenium tests using Linux accessibility infrastructure"
PURPOSE "Needed for GUI tests"
URL "https://invent.kde.org/sdk/selenium-webdriver-at-spi"
TYPE OPTIONAL
)
if(NOT SeleniumWebDriverATSPI_FOUND)
return()
endif()
add_test(
NAME logintest
COMMAND selenium-webdriver-at-spi-run ${CMAKE_CURRENT_SOURCE_DIR}/logintest.py
)

View File

@@ -0,0 +1,42 @@
# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
from flask import Flask, request, abort
app = Flask(__name__)
@app.route("/_matrix/client/v3/login", methods=["GET"])
def login_get():
result = dict()
result["flows"] = [dict()]
result["flows"][0]["type"] = "m.login.password"
return result
@app.route("/_matrix/client/v3/login", methods=["POST"])
def login_post():
data = request.get_json()
if data["identifier"]["user"] != "user" or data["password"] != "1234":
abort(403)
print(data)
result = dict()
result["access_token"] = "token_1234"
result["device_id"] = "device_1234"
result["user_id"] = "@user:localhost:1234"
return result
@app.route("/_matrix/client/r0/sync")
def sync():
result = dict()
result["next_batch"] = "batch1234"
return result
@app.route("/.well-known/matrix/client")
def well_known():
reply = dict()
reply["m.homeserver"] = dict()
reply["m.homeserver"]["base_url"] = "https://localhost:1234"
return reply
if __name__ == "__main__":
app.run(ssl_context='adhoc', port=1234)

44
appiumtests/logintest.py Executable file
View File

@@ -0,0 +1,44 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: 2021-2022 Harald Sitter <sitter@kde.org>
# SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
import unittest
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
import time
import subprocess
class LoginTest(unittest.TestCase):
@classmethod
def setUpClass(self):
desired_caps = {}
desired_caps["app"] = "neochat --ignore-ssl-errors"
desired_caps["timeouts"] = {'implicit': 10000}
self.driver = webdriver.Remote(
command_executor='http://127.0.0.1:4723',
desired_capabilities=desired_caps)
subprocess.Popen(["python","login-server.py"])
def setUp(self):
pass
def tearDown(self):
if not self._outcome.result.wasSuccessful():
self.driver.get_screenshot_as_file("failed_test_shot_{}.png".format(self.id()))
@classmethod
def tearDownClass(self):
self.driver.quit()
def test_login(self):
self.driver.find_element(by=AppiumBy.NAME, value="Matrix ID").send_keys("@user:localhost:1234")
self.driver.find_element(by=AppiumBy.NAME, value="Continue").click()
self.driver.find_element(by=AppiumBy.NAME, value="Password").send_keys("1234")
self.driver.find_element(by=AppiumBy.NAME, value="Login").click()
self.driver.find_element(by=AppiumBy.NAME, value="Join some rooms to get started").click()
if __name__ == '__main__':
unittest.main()

View File

@@ -10,6 +10,8 @@
#include <Quotient/syncdata.h>
#include <qnamespace.h>
#include "utils.h"
using namespace Quotient;
class TestRoom : public NeoChatRoom
@@ -555,10 +557,10 @@ void TextHandlerTest::receiveRichPlainUrl()
void TextHandlerTest::receiveRichEmote()
{
auto event = room->messageEvents().at(1).get();
auto author = static_cast<NeoChatUser *>(room->user(event->senderId()));
auto author = room->user(event->senderId());
const QString testInputString = QStringLiteral("This is an emote.");
const QString testOutputString = QStringLiteral("* <a href=\"https://matrix.to/#/@example:example.org\" style=\"color:") + author->color().name()
+ QStringLiteral("\">@example:example.org</a> This is an emote.");
const QString testOutputString = QStringLiteral("* <a href=\"https://matrix.to/#/@example:example.org\" style=\"color:")
+ Utils::getUserColor(author->hueF()).name() + QStringLiteral("\">@example:example.org</a> This is an emote.");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -644,5 +646,5 @@ void TextHandlerTest::linkPreviewsReject()
QCOMPARE(testTextHandler.getLinkPreviews(), testOutputLinks);
}
QTEST_GUILESS_MAIN(TextHandlerTest)
QTEST_MAIN(TextHandlerTest)
#include "texthandlertest.moc"

View File

@@ -40,47 +40,27 @@
<name xml:lang="ru">NeoChat</name>
<name xml:lang="sk">NeoChat</name>
<name xml:lang="sl">NeoChat</name>
<name xml:lang="sv">NeoChat</name>
<name xml:lang="ta">நியோச்சாட்</name>
<name xml:lang="tr">NeoChat</name>
<name xml:lang="uk">NeoChat</name>
<name xml:lang="x-test">xxNeoChatxx</name>
<name xml:lang="zh-CN">NeoChat</name>
<summary>A client for matrix, the decentralized communication protocol</summary>
<summary xml:lang="ar">عميل لماتركس، ميفاق الاتصال اللامركزي</summary>
<summary xml:lang="az">Matrix üçün müştəri, mərkəzləşməmiş kommunikasiya protokolu</summary>
<summary xml:lang="ca">Un client de Matrix, el protocol de comunicacions descentralitzat</summary>
<summary xml:lang="ca-valencia">Un client de Matrix, el protocol de comunicacions descentralitzat</summary>
<summary xml:lang="cs">Klient pro decentralizovaný komunikační protokol matrix</summary>
<summary xml:lang="de">Ein Programm für Matrix, das dezentrale Kommunikationsprotokoll</summary>
<summary xml:lang="el">Ένας πελάτης για το Matrix, το αποκεντρωμένο πρωτόκολλο επικοινωνίας</summary>
<summary xml:lang="en-GB">A client for matrix, the decentralised communication protocol</summary>
<summary xml:lang="eo">Kliento por matrix, la malcentra komunikprotokolo</summary>
<summary xml:lang="es">Un cliente para Matrix, el protocolo de comunicaciones descentralizado</summary>
<summary xml:lang="eu">Matrix, deszentralizatutako komunikazio protokolorako bezero bat</summary>
<summary xml:lang="fi">Asiakas Matrixille, hajautetulle viestintäyhteyskäytännölle</summary>
<summary xml:lang="fr">Un client pour « Matrix », le protocole décentralisé de communications.</summary>
<summary xml:lang="gl">Un cliente para Matrix, o protocolo de comunicación descentralizada</summary>
<summary xml:lang="hu">Kliens a matrixhoz, a decentralizált kommunikációs protokollhoz</summary>
<summary xml:lang="ia">Un cliente per Matrix, le protocollo de communication decentralisate</summary>
<summary xml:lang="id">Klien untuk matrix, protokol komunikasi terdesentralisasi</summary>
<summary xml:lang="ie">Un cliente de Matrix, li protocol de communication decentralisat</summary>
<summary xml:lang="it">Un client per matrix, il protocollo di comunicazione decentralizzato</summary>
<summary xml:lang="ka">კლიენტი Matrix-სთვის, დეცენტრალიზებული კომუნიკაციის პროტოკოლისთვის</summary>
<summary xml:lang="ko">Matrix, 분산 대화 프로토콜 클라이언트</summary>
<summary xml:lang="nl">Een client voor matrix, het gedecentraliseerde communicatieprotocol</summary>
<summary xml:lang="nn">Ein klient for Matrix  protokollen for desentralisert kommunikasjon</summary>
<summary xml:lang="pa">ਮੈਟਰਿਕਸ, ਸਰਬ-ਸਾਂਝੇ ਸੰਚਾਰ ਪਰੋਟੋਕਾਲ, ਲਈ ਕਲਾਈਂਟ ਹੈ</summary>
<summary xml:lang="pl">Program do obsługi matriksa, rozproszonego protokołu porozumiewania się</summary>
<summary xml:lang="pt">Um cliente para o Matrix, o protocolo de comunicação descentralizado</summary>
<summary xml:lang="pt-BR">Um cliente do Matrix, o protocolo de comunicação descentralizado</summary>
<summary xml:lang="ru">Клиент для Matrix — децентрализованного коммуникационного протокола</summary>
<summary xml:lang="sk">Klient pre matrix, decentralizovaný komunikačný protokol</summary>
<summary xml:lang="sl">Odjemalec za matrix, decentralizirani komunikacijski protokol</summary>
<summary xml:lang="ta">மையமில்லா தகவல் பரிமாற்ற நெறிமுறையான மேட்ரிக்ஸுக்கான செயலி</summary>
<summary xml:lang="tr">Merkezi olmayan iletişim protokolü Matrix için bir istemci</summary>
<summary xml:lang="uk">Клієнт matrix, децентралізованого протоколу обміну даними</summary>
<summary xml:lang="x-test">xxA client for matrix, the decentralized communication protocolxx</summary>
<summary xml:lang="zh-CN">分布式通讯协议 Matrix 的客户端</summary>
<summary>Chat with your friends on matrix</summary>
<summary xml:lang="ca">Xategeu amb els vostres amics a Matrix</summary>
<summary xml:lang="ca-valencia">Xategeu amb els vostres amics a Matrix</summary>
<summary xml:lang="eo">Babilu kun viaj amikoj sur matrix</summary>
<summary xml:lang="eu">Berriketan jardun zure lagunekin «Matrix»en</summary>
<summary xml:lang="gl">Charle coas súas amizades en Matrix.</summary>
<summary xml:lang="ka">ესაუბრეთ მეგობრებს Matrix-ზე</summary>
<summary xml:lang="nl">Met uw vrienden chatten op matrix</summary>
<summary xml:lang="nn">Prat med vennar på Matrix</summary>
<summary xml:lang="sl">Klepet z vašimi prijatelji na matrixu</summary>
<summary xml:lang="sv">Chatta med dina vänner på Matrix</summary>
<summary xml:lang="ta">மேட்ரிக்ஸு மூலம் உங்கள் நண்பர்களிடம் பேசலாம்</summary>
<summary xml:lang="tr">Matrix'te arkadaşlarınızla sohbet edin</summary>
<summary xml:lang="uk">Спілкуйтеся з вашими друзями у matrix</summary>
<summary xml:lang="x-test">xxChat with your friends on matrixxx</summary>
<description>
<p>NeoChat is a client for Matrix, the decentralized communication protocol for instant messaging. It allows you to send text messages, videos and audio files to your family, colleagues and friends. It uses KDE frameworks and most notably Kirigami
to provide a convergent experience across multiple platforms.</p>
@@ -90,9 +70,10 @@ to provide a convergent experience across multiple platforms.</p>
<p xml:lang="en-GB">NeoChat is a client for Matrix, the decentralised communication protocol for instant messaging. It allows you to send text messages, videos and audio files to your family, colleagues and friends. It uses KDE frameworks and most notably Kirigami to provide a convergent experience across multiple platforms.</p>
<p xml:lang="eo">NeoChat estas kliento por Matrix, la malcentra komunikoprotokolo por tuja mesaĝado. Ĝi ebligas al vi sendi tekstmesaĝojn, filmetojn kaj sondosierojn al via familio, kolegoj kaj amikoj. Ĝi uzas KDE-framojn kaj precipe Kirigami por disponigi konverĝan sperton tra pluraj platformoj.</p>
<p xml:lang="es">NeoChat es un cliente para Matrix, el protocolo de comunicaciones descentralizado para mensajería instantánea. Le permite enviar mensajes de texto, vídeos y archivos de sonido a su familia, compañeros de trabajo y amigos. Usa la infraestructura de KDE y, en particular, Kirigami para proporcionar una experiencia convergente en muchas plataformas.</p>
<p xml:lang="eu">NeoChat «Matrix»erako, bat-bateko mezularitzarako komunikazio deszentralizatuko protokolorako, bezero bat da. Zure sendiari, kide eta lagunei testu mezuak, bideo eta audio fitxategiak bidaltzeko aukera ematen dizu. «KDE Frameworks» eta bereziki «Kirigami» erabiltzen ditu plataforma anitzen artean esperientzia konbergente bat eskaintzeko.</p>
<p xml:lang="fi">NeoChat on asiakassovellus Matrixille, hajautetulle pikaviestinyhteyskäytännölle. Sillä voi lähettää teksti-, video- ja ääniviestejä perheelle, tutuille ja ystäville. Se käyttää KDE-kehystä ja erityisesti Kirigamia tuottaakseen mukautuvan monialustaisen käyttökokemuksen.</p>
<p xml:lang="fr">NeoChat est un client pour le protocole Matrix, un protocole décentralisé de communications pour messagerie instantané. Il vous permet d'envoyer des messages de texte, des vidéos et des fichiers audio à votre famille, vos collègues et vos amis. Il utilise les environnements de développement et plus précisément Kirigami pour fournir une expérience convergente sur plusieurs plate-formes. </p>
<p xml:lang="gl">NeoChat é un cliente para Matrix, o protocolo de comunicación descentralizada para mensaxería instantánea. Podes enviar mensaxes de texto, vídeos e ficheiros de son á túa familia, colegas e amizades. Usas infraestruturas de KDE e principalmente Kirigami para proporcionar unha experiencia de uso converxente para varias plataformas.</p>
<p xml:lang="gl">NeoChat é un cliente para Matrix, o protocolo de comunicación descentralizada para mensaxaría instantánea. Podes enviar mensaxes de texto, vídeos e ficheiros de son á túa familia, colegas e amizades. Usas infraestruturas de KDE e principalmente Kirigami para proporcionar unha experiencia de uso converxente para varias plataformas.</p>
<p xml:lang="ia">NeoChat es un cliente per Matrix, le protocollo de communication decentralisate per messager instantanee. Illo te permitte inviar messager de texto, files de video e audio a tu familia, collegas e amicos usante. Illo usa KDE frameworks e super toto Kirigamii forni un experientia convergente trans platteforme multiple.</p>
<p xml:lang="it">NeoChat è un client per Matrix, il protocollo di comunicazione decentralizzato per la messaggistica istantanea. Ti consente di inviare messaggi di testo, video e file audio a familiari, colleghi e amici. Utilizza i framework KDE e in particolare Kirigami per fornire un'esperienza convergente su più piattaforme.</p>
<p xml:lang="ka">NeoChat არის Matrix კლიენტი. ის საშუალებას გაძლევთ გაგზავნოთ ტექსტური შეტყობინებები, ვიდეოები და აუდიო ფაილები თქვენს ოჯახს, კოლეგებსა და მეგობრებს მატრიქსის პროტოკოლის გამოყენებით.</p>
@@ -102,6 +83,7 @@ to provide a convergent experience across multiple platforms.</p>
<p xml:lang="pl">NeoChat jest programem do Matriksa, protokołu rozproszonego porozumiewania się w czasie rzeczywistym. Umożliwia wysyłanie wiadomości tekstowych, filmów oraz dźwięku do twojej rodziny, znajomych oraz przyjaciół. Używa szkieletów KDE i głównie Kirigami, aby zapewnić spójne wrażenia na wielu platformach</p>
<p xml:lang="pt">O NeoChat é um cliente do Matrix. O mesmo permite-lhe enviar mensagens de texto, ficheiros de vídeo e áudio para a sua família, colegas e amigos com o protocolo Matrix. Usa as plataformas do KDE, e principalmente o Kirigami, para oferecer uma experiência convergente entre várias plataformas.</p>
<p xml:lang="sl">Neochat je odjemalec za Matrix, decentralizirani komunikacijski protokol za takojšnje sporočanje. Omogoča vam pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek svoji družini, sodelavcem in prijateljem. Uporablja okvire ogrodje KDE frameworks in predvsem Kirigami za zagotavljanje konvergentne izkušnje na več platformah.</p>
<p xml:lang="sv">NeoChat är en klient för Matrix, det decentraliserade kommunikationsprotokollet för direktmeddelanden. Den låter dig skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner. Den använder KDE Ramverk, i synnerhet Kirigami, för att tillhandahålla en konvergent upplevelse på flera plattformar.</p>
<p xml:lang="tr">NeoChat, anlık iletileşme için merkezi olmayan iletişim protokolü olan Matrix için bir istemcidir. Ailenize, iş arkadaşlarınıza ve arkadaşlarınıza metin iletiler, videolar ve ses dosyaları göndermenize olanak tanır. Birden çok platformda yakınsak bir deneyim sağlamak için KDE Frameworks ve en önemlilerinden Kirigami'yi kullanır.</p>
<p xml:lang="uk">NeoChat — клієнт Matrix, децентралізованого протоколу спілкування для миттєвого обміну повідомленнями. За його допомогою ви можете надсилати текстові повідомлення, відео та звукові файли вашій родин, колегами та друзям. У програмі використано бібліотеки KDE, зокрема Kirigami, для надання однорідного середовища на декількох програмних та апаратних платформах.</p>
<p xml:lang="x-test">xxNeoChat is a client for Matrix, the decentralized communication protocol for instant messaging. It allows you to send text messages, videos and audio files to your family, colleagues and friends. It uses KDE frameworks and most notably Kirigami to provide a convergent experience across multiple platforms.xx</p>
@@ -110,7 +92,9 @@ to provide a convergent experience across multiple platforms.</p>
<p xml:lang="ca">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptatge d'extrem a extrem. Hi ha algunes altres omissions més petites a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu segueix sent proporcionar suport eventual per a tota l'especificació.</p>
<p xml:lang="ca-valencia">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de VoIP, fils i alguns aspectes de l'encriptació d'extrem a extrem. Hi ha algunes altres omissions més xicotetes a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu seguix sent proporcionar suport eventual per a tota l'especificació.</p>
<p xml:lang="en-GB">NeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.</p>
<p xml:lang="eo">NeoChat celas esti plene kapabla aplikaĵo por la Matrix-specifo. Kiel tia, ĉio en la nuna stabila specifo kun la rimarkindaj esceptoj de VoIP, fadenoj kaj kelkaj aspektoj de Fin-al-Fina Ĉifrado estas subtenataj. Estas kelkaj aliaj pli malgrandaj preterlasoj pro la fakto, ke la Matrix-speco konstante evoluas, sed la celo restas provizi finfine subtenon por la tuta specifaĵo.</p>
<p xml:lang="es">NeoChat pretende ser una aplicación con todas las funciones para la especificación de Matrix. Como tal, admite todo en la especificación estable actual, con las notables excepciones de VoIP, subprocesos y algunas funciones de cifrado de extremo a extremo. Existen algunas omisiones menos importantes debido al hecho de que la especificación de Matrix está en constante evolución, pero el objetivo sigue siendo brindar compatibilidad final con toda la especificación.</p>
<p xml:lang="eu">«NeoChat»ek «Matrix» zehaztapenaren ezaugarri guztiak eskaintzen dituen aplikazio bat izan nahi du. Beraz, egungo zehaztapen egonkorrean dagoen guztiaren euskarria du, VoIP, hariak eta muturren artean zifratzeko salbuespen nabarmenekin. Badira beste ez-betetze txikiago batzuk, «Matrix»en zehaztapena etengabe eboluzioan dagoelako, baina azken helburua zehaztapen osoaren euskarria ematea izaten jarraitzen du.</p>
<p xml:lang="fi">NeoChat pyrkii olemaan Matrix-määritelmän täysominaisuuksinen sovellus, joten se tukee kaikkea nykyisessä vakaassa määritelmässä muutamaa huomattavaa poikkeusta lukuun ottamatta (VoIP, säikeet ja jotkin piirteet päästä päähän -salauksessa). Joitakin pienempiäkin puutteita on Matrix-määritelmän jatkuvan kehityksen vuoksi, mutta lopputavoitteena on tarjota määritelmän täysi tuki.</p>
<p xml:lang="fr">L'objectif de NeoChat est d'être une application complète pour le protocole Matrix. En tant que tel, tout dans la spécification stable actuelle avec les exceptions notables de VoIP, les processus et certains aspects du chiffrement de bout en bout sont pris en charge. Il y a quelques autres petites omissions en raison du fait que la spécification du protocole Matrix est en constante évolution. Cependant, l'objectif reste de fournir un soutien éventuel pour l'ensemble de la spécification.</p>
<p xml:lang="gl">NeoChat pretende ser unha aplicación completa para a especificación de Matrix. Coas excepcións de VoIP, conversas fiadas e algúns aspectos da cifraxe de extremo a extremo, a versión estábel segue as especificacións. Existen algunhas outras pequenas omisións debido ao feito de que Matrix está en continua evolución pero a intención é implementar a especificación completa.</p>
@@ -123,6 +107,7 @@ to provide a convergent experience across multiple platforms.</p>
<p xml:lang="pl">NeoChat w zamyśle ma być pełnowartościową aplikacją wg wytycznych Matriksa. Z tego powodu, wszystko, co jest obecnie w stabilnych wytycznych z pominięciem VoIP, wątków i niektórych części szyfrowania Użytkownik-do-Użytkownika są obecnie obsługiwane. Pominięto też kilka mniejszych rzeczy ze względu na ciągły rozwój wytycznych Matriksa, lecz celem nadal jest zapewnienie obsługi wszystkich wytycznych.</p>
<p xml:lang="pt">O NeoChat pretende ser uma aplicação completa para a especificação do Matrix. Como tal, tudo o que existe na especificação estável actual, com as notáveis excepções do VoIP, tópicos e alguns aspectos da Encriptação Ponto-a-Ponto, são suportados. Existem mais algumas omissões, devido ao facto que a norma do Matrix está em constante evolução, mas o objectivo continua a ser oferecer o suporte eventual para a norma por inteiro.</p>
<p xml:lang="sl">Neochat cilja, da bi bila popolna aplikacija po specifikaciji Matrixa. Kot takšna vsebuje vse v trenutni stabilni specifikaciji z pomembnimi izjemami pri VoIP, nitih in nekaterih vidikov šifriranja od konca do konca. Obstaja nekaj drugih manjših opustitev zaradi dejstva, da se specifikacija Matrix nenehno razvija, vendar cilj ostaja zagotoviti morebitno podporo celotni specifikaciji.</p>
<p xml:lang="sv">NeoChat har som mål att vara ett fullständigt program enligt Matrix-specifikationen. Som sådant stöds allt i den nuvarande stabila specifikationen, med de nämnvärda undantagen VoIP, trådar och några aspekter av kryptering hela vägen. Det finns några ytterligare utelämnanden på grund av att Matrix-specifikationen hela tiden utvecklas, men målet förblir att till slut erbjuda stöd för hela specifikationen.</p>
<p xml:lang="tr">NeoChat, Matrix belirtimi için tam özellikli bir uygulama olmayı hedefler. Bu nedenle; VoIP, ileti zincirleri ve Uçtan Uca Şifreleme'nin bazı yönleri gibi dikkate değer istisnalar dışında var olan kararlı belirtimdeki her şey desteklenir. Matrix belirtiminin sürekli gelişmesi nedeniyle birkaç küçük eksiklik daha var; ancak amaç tüm belirtim için nihai destek sağlamak olmayı sürdürüyor.</p>
<p xml:lang="uk">Метою створення NeoChat є повноцінна реалізація програми для специфікації Matrix. Як наслідок, реалізовано усе у поточній стабільній специфікації, окрім голосового інтернет-зв'язку, потоків та деяких аспектів міжвузлового шифрування. Є також декілька інших незначних прогалин через те, що специфікація Matrix постійно змінюється, але метою лишається повна підтримка специфікації.</p>
<p xml:lang="x-test">xxNeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.xx</p>
@@ -131,7 +116,9 @@ to provide a convergent experience across multiple platforms.</p>
<p xml:lang="ca">A causa de la naturalesa del desenvolupament de l'especificació de Matrix, el NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
<p xml:lang="ca-valencia">A causa de la naturalea del desenvolupament de l'especificació de Matrix, NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
<p xml:lang="en-GB">Due to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:</p>
<p xml:lang="eo">Pro la naturo de la Matrix-specifevoluo NeoChat ankaŭ subtenas multajn malstabilajn funkciojn. Nuntempe ĉi tiuj estas:</p>
<p xml:lang="es">Debido a la naturaleza del desarrollo de la especificación de Matrix, NeoChat también permite numerosas funciones no estables, como:</p>
<p xml:lang="eu">«Matrix» zehaztapenaren garapenaren izaeragatik, «Neo¢hat»ek ezaugarri ezegonkor ugari ere onartzen ditu. Gaur egun hauek:</p>
<p xml:lang="fi">Matrix-määritelmän kehittyessä NeoChat tukee myös monia epävakaita ominaisuuksia. Tällä hetkellä näitä ovat:</p>
<p xml:lang="fr">En raison de la nature du développement des spécifications du protocole Matrix, NeoChat prend également en charge de nombreuses fonctionnalités instables. Actuellement, ce sont :</p>
<p xml:lang="gl">Debido á natureza do desenvolvemento da especificación de Matrix, NeoChat tamén inclúe varias funcionalidades non estábeis:</p>
@@ -144,6 +131,8 @@ to provide a convergent experience across multiple platforms.</p>
<p xml:lang="pl">Ze względu na sposób rozwoju Matriksa, NeoChat obsługuje także kilka niestabilnych możliwości. Obecnie są to:</p>
<p xml:lang="pt">Devido à natureza do desenvolvimento da especificação do Matrix, o NeoChat também suporta diversas funcionalidades instáveis. De momento são:</p>
<p xml:lang="sl">Zaradi narave razvoja specifikacije Matrixa NeoChat podpira tudi številne nestabilne zmožnosti. Trenutno so to:</p>
<p xml:lang="sv">På grund av sättet Matrix-specifikationens utvecklas, stöder NeoChat också ett stor antal instabila funktioner. För närvarande är de:</p>
<p xml:lang="ta">மேட்ரிக்ஸு நெறிமுறை வரையறுக்கப்படும் வித‍த்தின் காரணமாக, பல நிலையற்ற அம்சங்களையும் நியோச்சாட் ஆதரிக்கிறது. தற்போது ஆதரிக்கப்படுபவை:</p>
<p xml:lang="tr">NeoChat, Matrix belirtimi geliştirmesinin doğası gereği çok sayıda kararsız özelliği de destekler. Şu anda bunlar:</p>
<p xml:lang="uk">Через природу розробки специфікації Matrix, у NeoChat також передбачено підтримку численних нестабільних можливостей. У поточній версії цими можливостями є:</p>
<p xml:lang="x-test">xxDue to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:xx</p>
@@ -153,10 +142,12 @@ to provide a convergent experience across multiple platforms.</p>
<li xml:lang="ca">Enquestes - MSC3381</li>
<li xml:lang="ca-valencia">Enquestes - MSC3381</li>
<li xml:lang="en-GB">Polls - MSC3381</li>
<li xml:lang="eo">Enketoj - MSC3381</li>
<li xml:lang="es">Encuestas - MSC3381</li>
<li xml:lang="eu">Polls - MSC3381</li>
<li xml:lang="fi">Kyselyt MSC3381</li>
<li xml:lang="fr">Sondages - MSC3381</li>
<li xml:lang="gl">Enquisas - MSC3381</li>
<li xml:lang="gl">Enquisas MSC3381</li>
<li xml:lang="ia">Inquestas - MSC3381</li>
<li xml:lang="it">Sondaggi - MSC3381</li>
<li xml:lang="ka">Polls - MSC3381</li>
@@ -165,6 +156,7 @@ to provide a convergent experience across multiple platforms.</p>
<li xml:lang="nn">Avstemmingar  MSC3381</li>
<li xml:lang="pt">Inquéritos - MSC3381</li>
<li xml:lang="sl">Polls - MSC3381</li>
<li xml:lang="sv">Polls - MSC3381</li>
<li xml:lang="ta">வாக்கெடுப்புகள் - MSC3381</li>
<li xml:lang="tr">Anketler - MSC3381</li>
<li xml:lang="uk">Опитування - MSC3381</li>
@@ -174,10 +166,12 @@ to provide a convergent experience across multiple platforms.</p>
<li xml:lang="ca">Paquets d'adhesius - MSC2545</li>
<li xml:lang="ca-valencia">Paquets d'adhesius - MSC2545</li>
<li xml:lang="en-GB">Sticker Packs - MSC2545</li>
<li xml:lang="eo">Glumark-Pakoj - MSC2545</li>
<li xml:lang="es">Paquetes de pegatinas - MSC2545</li>
<li xml:lang="eu">Eranskailu paketeak - MSC2545</li>
<li xml:lang="fi">Tarrapakkaukset MSC2545</li>
<li xml:lang="fr">Paquets d'auto-collants - MSC2545</li>
<li xml:lang="gl">Paquetes de adhesivos - MSC2545</li>
<li xml:lang="gl">Paquetes de adhesivos MSC2545</li>
<li xml:lang="ia">Etiquetta gummate (sticker) -MSC2545</li>
<li xml:lang="it">Pacchetti di adesivi - MSC2545</li>
<li xml:lang="ka">სტიკერების პაკეტები - MSC2545</li>
@@ -186,6 +180,7 @@ to provide a convergent experience across multiple platforms.</p>
<li xml:lang="nn">Klistremerke-pakkar  MSC2545</li>
<li xml:lang="pt">Pacotes de Autocolantes - MSC2545</li>
<li xml:lang="sl">Sticker Packs - MSC2545</li>
<li xml:lang="sv">Sticker Packs - MSC2545</li>
<li xml:lang="ta">ஒட்டி தொகுப்புகள் - MSC2545</li>
<li xml:lang="tr">Yapışkan Paketleri - MSC2545</li>
<li xml:lang="uk">Пакунки наліпок - MSC2545</li>
@@ -195,10 +190,12 @@ to provide a convergent experience across multiple platforms.</p>
<li xml:lang="ca">Esdeveniments d'ubicació - MSC3488</li>
<li xml:lang="ca-valencia">Esdeveniments d'ubicació - MSC3488</li>
<li xml:lang="en-GB">Location Events - MSC3488</li>
<li xml:lang="eo">Lokaj Eventoj - MSC3488</li>
<li xml:lang="es">Eventos de ubicación - MSC3488</li>
<li xml:lang="eu">Kokaleku-gertaerak - MSC3488</li>
<li xml:lang="fi">Sijaintitapahtumat MSC3488</li>
<li xml:lang="fr">Événements de lieu - MSC3488</li>
<li xml:lang="gl">Localización de eventos - MSC3488</li>
<li xml:lang="gl">Localización de eventos MSC3488</li>
<li xml:lang="ia">Eventos de Location - MSC3488</li>
<li xml:lang="it">Località eventi - MSC3488</li>
<li xml:lang="ka">მდებარეობის მოვლენები - MSC3488</li>
@@ -207,6 +204,7 @@ to provide a convergent experience across multiple platforms.</p>
<li xml:lang="nn">Posisjonshendingar  MSC3488</li>
<li xml:lang="pt">Eventos com Localizações - MSC3488</li>
<li xml:lang="sl">Location Events - MSC3488</li>
<li xml:lang="sv">Location Events - MSC3488</li>
<li xml:lang="ta">இட நிகழ்வுகள் - MSC3488</li>
<li xml:lang="tr">Konum Etkinlikleri - MSC3488</li>
<li xml:lang="uk">Місцеві зустрічі - MSC3488</li>
@@ -249,6 +247,7 @@ to provide a convergent experience across multiple platforms.</p>
<developer_name xml:lang="ru">Сообщество KDE</developer_name>
<developer_name xml:lang="sk">KDE Komunita</developer_name>
<developer_name xml:lang="sl">Skupnost KDE</developer_name>
<developer_name xml:lang="sv">KDE-gemenskapen</developer_name>
<developer_name xml:lang="ta">கே.டீ.யீ. சமூகம்</developer_name>
<developer_name xml:lang="tr">KDE Topluluğu</developer_name>
<developer_name xml:lang="uk">Спільнота KDE</developer_name>
@@ -268,15 +267,19 @@ to provide a convergent experience across multiple platforms.</p>
<value key="KDE::windows_store::screenshots::1::caption">Main view with room list, chat, and room information</value>
<value key="KDE::windows_store::screenshots::1::caption" xml:lang="ca">Vista principal amb la llista de sales, xats i informació de les sales</value>
<value key="KDE::windows_store::screenshots::1::caption" xml:lang="ca-valencia">Vista principal amb la llista de sales, xats i informació de les sales</value>
<value key="KDE::windows_store::screenshots::1::caption" xml:lang="eo">Ĉefa vido kun ĉambra listo, babilejo kaj ĉambra informo</value>
<value key="KDE::windows_store::screenshots::1::caption" xml:lang="es">Vista principal con la lista de salas, chat e información de la sala</value>
<value key="KDE::windows_store::screenshots::1::caption" xml:lang="eu">Ikuspegi nagusia gela-zerrenda, berriketa, eta gelako informazioarekin</value>
<value key="KDE::windows_store::screenshots::1::caption" xml:lang="fr">Vue principale avec la liste des salons ainsi que des informations sur les salons et forums de discussions</value>
<value key="KDE::windows_store::screenshots::1::caption" xml:lang="ia">Vista principal con lista de sala, chat e information de sala</value>
<value key="KDE::windows_store::screenshots::1::caption" xml:lang="gl">Vista principal coa lista de salas, a charla, e información da sala.</value>
<value key="KDE::windows_store::screenshots::1::caption" xml:lang="it">Vista principale con elenco delle stanze, chat e informazioni sulla stanza</value>
<value key="KDE::windows_store::screenshots::1::caption" xml:lang="ka">მთავარი ხედი სურათების სიით, ჩატით და ოთახის ინფორმაციით</value>
<value key="KDE::windows_store::screenshots::1::caption" xml:lang="ko">대화방 목록, 채팅, 대화방 정보가 표시된 주 보기</value>
<value key="KDE::windows_store::screenshots::1::caption" xml:lang="nl">Hoofdweergave met lijst met rooms, chat en roominformatie</value>
<value key="KDE::windows_store::screenshots::1::caption" xml:lang="nn">Hovudvising med romliste, pratevindauge og rominformasjon</value>
<value key="KDE::windows_store::screenshots::1::caption" xml:lang="pt">A área principal com a lista de salas e com informações sobre a conversa e a sala</value>
<value key="KDE::windows_store::screenshots::1::caption" xml:lang="sl">Glavni pogled s seznamom sob, klepetom in informacijami o sobah</value>
<value key="KDE::windows_store::screenshots::1::caption" xml:lang="sv">Huvudvy med rumslista, chatt, och rumsinformation</value>
<value key="KDE::windows_store::screenshots::1::caption" xml:lang="ta">அரங்குப்பட்டியல், உரையாடல், மற்றும் அரங்குவிவரங்களைக் கொண்டுள்ள பிரதான காட்சி</value>
<value key="KDE::windows_store::screenshots::1::caption" xml:lang="tr">Oda listesini, sohbet penceresini ve oda bilgisini gösteren ana görünüm</value>
<value key="KDE::windows_store::screenshots::1::caption" xml:lang="uk">Головна панель із списком кімнат, спілкуванням та даними щодо кімнати</value>
@@ -285,15 +288,19 @@ to provide a convergent experience across multiple platforms.</p>
<value key="KDE::windows_store::screenshots::2::caption">Login screen</value>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="ca">Pantalla d'inici de sessió</value>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="ca-valencia">Pantalla d'inici de sessió</value>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="eo">Ensaluta ekrano</value>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="es">Pantalla de inicio de sesión</value>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="eu">Saio-hasteko pantaila</value>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="fr">Écran de connexion</value>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="ia">Schermo de accesso</value>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="gl">Pantalla de identificación.</value>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="it">Schermata di accesso</value>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="ka">შესვლის ეკრანი</value>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="ko">로그인 화면</value>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="nl">Aanmeldscherm</value>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="nn">Innloggingsbilete</value>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="pt">Ecrã de autenticação</value>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="sl">Prijavni zaslon</value>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="sv">Inloggningsfönster</value>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="ta">நுழைவுத் திரை</value>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="tr">Oturum açma ekranı</value>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="uk">Вікно входу</value>

View File

@@ -35,6 +35,7 @@ Name[ro]=NeoChat
Name[ru]=NeoChat
Name[sk]=NeoChat
Name[sl]=NeoChat
Name[sv]=NeoChat
Name[ta]=நியோச்சாட்
Name[tr]=NeoChat
Name[uk]=NeoChat
@@ -73,6 +74,7 @@ GenericName[ro]=Client Matrix
GenericName[ru]=Клиент Matrix
GenericName[sk]=Matrix Client
GenericName[sl]=Odjemalec Matrix
GenericName[sv]=Matrix-klient
GenericName[ta]=Matrix வாங்கி
GenericName[tr]=Matrix İstemcisi
GenericName[uk]=Клієнт Matrix
@@ -91,7 +93,7 @@ Comment[es]=Cliente para el protocolo Matrix
Comment[eu]=Matrix protokolorako bezeroa
Comment[fi]=Asiakas Matrix-yhteyskäytännölle
Comment[fr]=Client pour le protocole « Matrix »
Comment[gl]=Cliente para o protocolo Matrix
Comment[gl]=Cliente para o protocolo Matrix.
Comment[hu]=Kliens a Matrix protokollhoz
Comment[ia]=Cliente per le protocollo de Matrix
Comment[id]=Klien untuk protokol Matrix
@@ -110,6 +112,7 @@ Comment[ro]=Client pentru protocolul Matrix
Comment[ru]=Клиент для протокола Matrix
Comment[sk]=Klient protokolu Matrix
Comment[sl]=Odjemalec za protokol Matrix
Comment[sv]=Klient för protokollet Matrix
Comment[ta]=Matrix நெறிமுறைக்கான வாங்கி
Comment[tr]=Matrix protokolü için istemci
Comment[uk]=Клієнт протоколу Matrix

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

4391
po/sv/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

View File

@@ -5,7 +5,6 @@
configure_file(qml/Page/RoomList/RoomDelegate.qml ${CMAKE_CURRENT_BINARY_DIR}/qml/Page/RoomList/RoomDelegate.qml)
configure_file(qml/Component/QuickSwitcher.qml ${CMAKE_CURRENT_BINARY_DIR}/qml/Component/QuickSwitcher.qml)
configure_file(qml/Dialog/UserDetailDialog.qml ${CMAKE_CURRENT_BINARY_DIR}/qml/Dialog/UserDetailDialog.qml)
configure_file(qml/Dialog/PowerLevelDialog.qml ${CMAKE_CURRENT_BINARY_DIR}/qml/Dialog/PowerLevelDialog.qml)
configure_file(qml/Component/Timeline/AudioDelegate.qml ${CMAKE_CURRENT_BINARY_DIR}/qml/Component/Timeline/AudioDelegate.qml)
configure_file(qml/Component/Timeline/VideoDelegate.qml ${CMAKE_CURRENT_BINARY_DIR}/qml/Component/Timeline/VideoDelegate.qml)
@@ -44,8 +43,6 @@ add_library(neochat STATIC
roommanager.h
neochatroom.cpp
neochatroom.h
neochatuser.cpp
neochatuser.h
models/userlistmodel.cpp
models/userlistmodel.h
models/userfiltermodel.cpp
@@ -127,6 +124,7 @@ add_library(neochat STATIC
locationhelper.h
events/pollevent.cpp
pollhandler.cpp
utils.h
)
ecm_qt_declare_logging_category(neochat
@@ -175,7 +173,7 @@ else()
endif()
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR})
target_link_libraries(neochat PUBLIC Qt::Core Qt::Quick Qt::Qml Qt::Gui Qt::Multimedia Qt::Network Qt::QuickControls2 KF${QT_MAJOR_VERSION}::I18n KF${QT_MAJOR_VERSION}::Kirigami2 KF${QT_MAJOR_VERSION}::Notifications KF${QT_MAJOR_VERSION}::ConfigCore KF${QT_MAJOR_VERSION}::ConfigGui KF${QT_MAJOR_VERSION}::CoreAddons KF${QT_MAJOR_VERSION}::SonnetCore KF${QT_MAJOR_VERSION}::ItemModels Quotient${QUOTIENT_SUFFIX} cmark::cmark ${QTKEYCHAIN_LIBRARIES} QCoro::Core)
target_link_libraries(neochat PUBLIC Qt::Core Qt::Quick Qt::Qml Qt::Gui Qt::Multimedia Qt::Network Qt::QuickControls2 KF${QT_MAJOR_VERSION}::I18n KF${QT_MAJOR_VERSION}::Kirigami2 KF${QT_MAJOR_VERSION}::Notifications KF${QT_MAJOR_VERSION}::ConfigCore KF${QT_MAJOR_VERSION}::ConfigGui KF${QT_MAJOR_VERSION}::CoreAddons KF${QT_MAJOR_VERSION}::SonnetCore KF${QT_MAJOR_VERSION}::ItemModels Quotient${QUOTIENT_SUFFIX} cmark::cmark QCoro::Core)
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
if(NEOCHAT_FLATPAK)

View File

@@ -35,15 +35,15 @@
#include <Quotient/connection.h>
#include <Quotient/csapi/content-repo.h>
#include <Quotient/csapi/logout.h>
#include <Quotient/csapi/notifications.h>
#include <Quotient/csapi/profile.h>
#include <Quotient/eventstats.h>
#include <Quotient/jobs/downloadfilejob.h>
#include <Quotient/qt_connection_util.h>
#include <Quotient/csapi/notifications.h>
#include <Quotient/eventstats.h>
#include <Quotient/user.h>
#include "neochatconfig.h"
#include "neochatroom.h"
#include "neochatuser.h"
#include "notificationsmanager.h"
#include "roommanager.h"
#include "windowcontroller.h"
@@ -60,7 +60,6 @@ Controller::Controller(QObject *parent)
: QObject(parent)
{
Connection::setRoomType<NeoChatRoom>();
Connection::setUserType<NeoChatUser>();
setApplicationProxy();
@@ -532,7 +531,7 @@ void Controller::joinRoom(const QString &alias)
RoomManager::instance().joinRoom(m_connection, alias, QStringList{knownServer});
}
void Controller::openOrCreateDirectChat(NeoChatUser *user)
void Controller::openOrCreateDirectChat(User *user)
{
const auto existing = activeConnection()->directChats();

View File

@@ -14,7 +14,6 @@
#include <Quotient/settings.h>
class NeoChatRoom;
class NeoChatUser;
class TrayIcon;
class QWindow;
class QQuickTextDocument;
@@ -23,6 +22,7 @@ namespace Quotient
{
class Connection;
class Room;
class User;
}
namespace QKeychain
@@ -183,7 +183,7 @@ public:
*
* If a direct chat with the user doesn't exist one is created and then joined.
*/
Q_INVOKABLE void openOrCreateDirectChat(NeoChatUser *user);
Q_INVOKABLE void openOrCreateDirectChat(Quotient::User *user);
[[nodiscard]] bool supportSystemTray() const;

View File

@@ -4,6 +4,7 @@
#include <QCommandLineParser>
#include <QIcon>
#include <QNetworkProxyFactory>
#include <QObject>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQmlNetworkAccessManagerFactory>
@@ -28,10 +29,11 @@
#include "neochat-version.h"
#include <Quotient/keyverificationsession.h>
#include <Quotient/accountregistry.h>
#include <Quotient/keyverificationsession.h>
#include <Quotient/networkaccessmanager.h>
#include <Quotient/room.h>
#include <Quotient/user.h>
#include <Quotient/util.h>
#include "actionshandler.h"
@@ -75,7 +77,6 @@
#include "models/webshortcutmodel.h"
#include "neochatconfig.h"
#include "neochatroom.h"
#include "neochatuser.h"
#include "notificationsmanager.h"
#include "pollhandler.h"
#include "roommanager.h"
@@ -88,7 +89,6 @@
#endif
#include "models/completionmodel.h"
#include "models/statemodel.h"
#include "neochatuser.h"
#ifdef HAVE_RUNNER
#include "runner.h"
@@ -259,7 +259,7 @@ int main(int argc, char *argv[])
qmlRegisterUncreatableType<PushNotificationState>("org.kde.neochat", 1, 0, "PushNotificationState", "ENUM");
qmlRegisterUncreatableType<PushNotificationAction>("org.kde.neochat", 1, 0, "PushNotificationAction", "ENUM");
qmlRegisterUncreatableType<NeoChatRoomType>("org.kde.neochat", 1, 0, "NeoChatRoomType", "ENUM");
qmlRegisterUncreatableType<NeoChatUser>("org.kde.neochat", 1, 0, "NeoChatUser", {});
qmlRegisterUncreatableType<User>("org.kde.neochat", 1, 0, "User", {});
qmlRegisterUncreatableType<NeoChatRoom>("org.kde.neochat", 1, 0, "NeoChatRoom", {});
qRegisterMetaType<User *>("User*");
@@ -269,7 +269,7 @@ int main(int argc, char *argv[])
qRegisterMetaType<Connection *>("Connection*");
qRegisterMetaType<MessageEventType>("MessageEventType");
qRegisterMetaType<NeoChatRoom *>("NeoChatRoom*");
qRegisterMetaType<NeoChatUser *>("NeoChatUser*");
qRegisterMetaType<User *>("User*");
qRegisterMetaType<GetRoomEventsJob *>("GetRoomEventsJob*");
qRegisterMetaType<QMimeType>("QMimeType");
#ifdef Quotient_E2EE_ENABLED
@@ -323,11 +323,18 @@ int main(int argc, char *argv[])
QCommandLineParser parser;
parser.setApplicationDescription(i18n("Client for the matrix communication protocol"));
parser.addPositionalArgument(QStringLiteral("urls"), i18n("Supports matrix: url scheme"));
parser.addOption(QCommandLineOption("ignore-ssl-errors", i18n("Ignore all SSL Errors, e.g., unsigned certificates.")));
about.setupCommandLine(&parser);
parser.process(app);
about.processCommandLine(&parser);
if (parser.isSet("ignore-ssl-errors")) {
QObject::connect(NetworkAccessManager::instance(), &QNetworkAccessManager::sslErrors, NetworkAccessManager::instance(), [](QNetworkReply *reply) {
reply->ignoreSslErrors();
});
}
engine.addImageProvider(QLatin1String("mxc"), new MatrixImageProvider);
engine.addImageProvider(QLatin1String("blurhash"), new BlurhashImageProvider);

View File

@@ -81,7 +81,7 @@ QString CollapseStateProxyModel::aggregateEventToString(int sourceRow) const
: QStringLiteral("<a href=\"https://matrix.to/#/%1\" style=\"color: %2\">%3</a> ")
.arg(uniqueAuthors[0].toMap()["id"].toString(),
uniqueAuthors[0].toMap()["color"].toString(),
uniqueAuthors[0].toMap()["displayName"].toString());
uniqueAuthors[0].toMap()["displayName"].toString().toHtmlEscaped());
text += userText;
text += chunks.takeFirst();

View File

@@ -5,6 +5,9 @@
#include "controller.h"
#include <QDateTime>
#include <QLocale>
#include <KLocalizedString>
#include <Quotient/csapi/device_management.h>
@@ -52,6 +55,12 @@ QVariant DevicesModel::data(const QModelIndex &index, int role) const
} else {
return false;
}
case TimestampString:
if (device.lastSeenTs) {
return QDateTime::fromMSecsSinceEpoch(*device.lastSeenTs).toString(QLocale().dateTimeFormat(QLocale::ShortFormat));
} else {
return false;
}
case Type:
if (device.deviceId == m_connection->deviceId()) {
return This;
@@ -81,6 +90,7 @@ QHash<int, QByteArray> DevicesModel::roleNames() const
{DisplayName, "displayName"},
{LastIp, "lastIp"},
{LastTimestamp, "lastTimestamp"},
{TimestampString, "timestamp"},
{Type, "type"},
};
}

View File

@@ -40,6 +40,7 @@ public:
DisplayName, /**< Display name set by the user for this device. */
LastIp, /**< The IP address where this device was last seen. */
LastTimestamp, /**< The timestamp when this devices was last seen. */
TimestampString, /**< String for the timestamp when this devices was last seen. */
Type, /**< The category to sort this device into. */
};
Q_ENUM(Roles)

View File

@@ -8,6 +8,8 @@
#include <QDebug>
#include <cmath>
using namespace Quotient;
bool operator<(const LiveLocationData &lhs, const LiveLocationData &rhs)
@@ -88,6 +90,11 @@ QVariant LiveLocationsModel::data(const QModelIndex &index, int roleName) const
const auto timeout = data.beaconInfo.value("timeout"_ls).toDouble(600000);
return lastTs + timeout >= QDateTime::currentDateTime().toMSecsSinceEpoch();
}
case HeadingRole: {
bool success = false;
const auto heading = data.beacon["org.matrix.msc3488.location"_ls].toObject()["org.kde.itinerary.heading"_ls].toString().toDouble(&success);
return success ? heading : NAN;
}
}
return {};
@@ -101,6 +108,7 @@ QHash<int, QByteArray> LiveLocationsModel::roleNames() const
r.insert(AssetRole, "asset");
r.insert(AuthorRole, "author");
r.insert(IsLiveRole, "isLive");
r.insert(HeadingRole, "heading");
return r;
}

View File

@@ -49,6 +49,7 @@ public:
AssetRole, /**< Type of location event, e.g. self pin of the user location. */
AuthorRole, /**< The author of the event. */
IsLiveRole, /**< Boolean that indicates whether a live location beacon is still live. */
HeadingRole, /**< Heading in degree (not part of any MSC yet, using an Itinerary extension). */
};
Q_ENUM(Roles)

View File

@@ -3,6 +3,8 @@
#include "locationsmodel.h"
#include <QGuiApplication>
using namespace Quotient;
LocationsModel::LocationsModel(QObject *parent)
@@ -43,6 +45,10 @@ LocationsModel::LocationsModel(QObject *parent)
});
connect(this, &LocationsModel::rowsInserted, this, &LocationsModel::boundingBoxChanged);
connect(static_cast<QGuiApplication *>(QGuiApplication::instance()), &QGuiApplication::paletteChanged, this, [this] {
Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0), {AuthorRole});
});
}
void LocationsModel::addLocation(const RoomMessageEvent *event)
@@ -61,7 +67,7 @@ void LocationsModel::addLocation(const RoomMessageEvent *event)
.latitude = latitude,
.longitude = longitude,
.content = event->contentJson(),
.author = dynamic_cast<NeoChatUser *>(m_room->user(event->senderId())),
.author = m_room->user(event->senderId()),
};
endInsertRows();
}

View File

@@ -10,6 +10,7 @@
#include "neochatroom.h"
#include <Quotient/events/roommessageevent.h>
#include <Quotient/user.h>
class LocationsModel : public QAbstractListModel
{
@@ -51,7 +52,7 @@ private:
float latitude;
float longitude;
QJsonObject content;
NeoChatUser *author;
Quotient::User *author;
};
QList<LocationData> m_locations;
void addLocation(const Quotient::RoomMessageEvent *event);

View File

@@ -25,7 +25,6 @@
#include <KLocalizedString>
#include "models/reactionmodel.h"
#include "neochatuser.h"
#include "texthandler.h"
using namespace Quotient;
@@ -78,7 +77,7 @@ MessageEventModel::MessageEventModel(QObject *parent)
: QAbstractListModel(parent)
{
connect(static_cast<QGuiApplication *>(QGuiApplication::instance()), &QGuiApplication::paletteChanged, this, [this] {
Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0), {AuthorRole, ReplyRole});
Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0), {AuthorRole, ReplyAuthor, ReadMarkersRole});
});
}
@@ -531,7 +530,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
}
if (role == AuthorRole) {
auto author = static_cast<NeoChatUser *>(isPending ? m_currentRoom->localUser() : m_currentRoom->user(evt.senderId()));
auto author = isPending ? m_currentRoom->localUser() : m_currentRoom->user(evt.senderId());
return m_currentRoom->getUser(author);
}
@@ -679,7 +678,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
auto replyPtr = m_currentRoom->getReplyForEvent(evt);
if (replyPtr) {
auto replyUser = static_cast<NeoChatUser *>(m_currentRoom->user(replyPtr->senderId()));
auto replyUser = m_currentRoom->user(replyPtr->senderId());
return m_currentRoom->getUser(replyUser);
} else {
return m_currentRoom->getUser(nullptr);
@@ -808,7 +807,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
QVariantList users;
users.reserve(userIds.size());
for (const auto &userId : userIds) {
auto user = static_cast<NeoChatUser *>(m_currentRoom->user(userId));
auto user = m_currentRoom->user(userId);
users += m_currentRoom->getUser(user);
}
@@ -836,7 +835,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
*/
QString readMarkersString = i18np("1 user: ", "%1 users: ", userIds.size());
for (const auto &userId : userIds) {
auto user = static_cast<NeoChatUser *>(m_currentRoom->user(userId));
auto user = m_currentRoom->user(userId);
readMarkersString += user->displayname(m_currentRoom) + i18nc("list separator", ", ");
}
readMarkersString.chop(2);
@@ -877,7 +876,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
}
if (role == DisplayNameForInitialsRole) {
auto user = static_cast<NeoChatUser *>(isPending ? m_currentRoom->localUser() : m_currentRoom->user(evt.senderId()));
auto user = isPending ? m_currentRoom->localUser() : m_currentRoom->user(evt.senderId());
return user->displayname(m_currentRoom).remove(QStringLiteral(" (%1)").arg(user->id()));
}
@@ -889,7 +888,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
}
return previousDisplayName;
} else {
auto author = static_cast<NeoChatUser *>(isPending ? m_currentRoom->localUser() : m_currentRoom->user(evt.senderId()));
auto author = isPending ? m_currentRoom->localUser() : m_currentRoom->user(evt.senderId());
return m_currentRoom->htmlSafeMemberName(author->id());
}
}
@@ -1062,13 +1061,13 @@ void MessageEventModel::createReactionModelForEvent(const Quotient::RoomMessageE
return;
};
QMap<QString, QList<NeoChatUser *>> reactions = {};
QMap<QString, QList<Quotient::User *>> reactions = {};
for (const auto &a : annotations) {
if (a->isRedacted()) { // Just in case?
continue;
}
if (const auto &e = eventCast<const ReactionEvent>(a)) {
reactions[e->key()].append(static_cast<NeoChatUser *>(m_currentRoom->user(e->senderId())));
reactions[e->key()].append(m_currentRoom->user(e->senderId()));
}
}
@@ -1095,7 +1094,7 @@ void MessageEventModel::createReactionModelForEvent(const Quotient::RoomMessageE
if (m_reactionModels.contains(eventId)) {
m_reactionModels[eventId]->setReactions(res);
} else if (res.size() > 0) {
m_reactionModels[eventId] = new ReactionModel(this, res, static_cast<NeoChatUser *>(m_currentRoom->localUser()));
m_reactionModels[eventId] = new ReactionModel(this, res, m_currentRoom->localUser());
} else {
if (m_reactionModels.contains(eventId)) {
delete m_reactionModels[eventId];

View File

@@ -7,9 +7,9 @@
#include <KLocalizedString>
#include "neochatuser.h"
#include <Quotient/user.h>
ReactionModel::ReactionModel(QObject *parent, QList<Reaction> reactions, NeoChatUser *localUser)
ReactionModel::ReactionModel(QObject *parent, QList<Reaction> reactions, Quotient::User *localUser)
: QAbstractListModel(parent)
, m_localUser(localUser)
{

View File

@@ -5,7 +5,10 @@
#include <QAbstractListModel>
class NeoChatUser;
namespace Quotient
{
class User;
}
/**
* @class ReactionModel
@@ -36,7 +39,7 @@ public:
HasLocalUser, /**< Whether the local user is in the list of authors. */
};
explicit ReactionModel(QObject *parent = nullptr, QList<Reaction> reactions = {}, NeoChatUser *localUser = nullptr);
explicit ReactionModel(QObject *parent = nullptr, QList<Reaction> reactions = {}, Quotient::User *localUser = nullptr);
/**
* @brief Get the given role value at the given index.
@@ -67,6 +70,6 @@ public:
private:
QList<Reaction> m_reactions;
NeoChatUser *m_localUser;
Quotient::User *m_localUser;
};
Q_DECLARE_METATYPE(ReactionModel *)

View File

@@ -5,13 +5,14 @@
#include "messageeventmodel.h"
#include "neochatroom.h"
#include "neochatuser.h"
#include <QGuiApplication>
#include <Quotient/connection.h>
#include <Quotient/events/stickerevent.h>
#include <KLocalizedString>
#include <Quotient/connection.h>
using namespace Quotient;
// TODO search only in the current room
@@ -19,6 +20,9 @@ using namespace Quotient;
SearchModel::SearchModel(QObject *parent)
: QAbstractListModel(parent)
{
connect(static_cast<QGuiApplication *>(QGuiApplication::instance()), &QGuiApplication::paletteChanged, this, [this] {
Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0), {AuthorRole, ReadMarkersRole});
});
}
QString SearchModel::searchText() const
@@ -109,7 +113,7 @@ QVariant SearchModel::data(const QModelIndex &index, int role) const
return false;
case ReplyAuthorRole:
if (const auto &replyPtr = m_room->getReplyForEvent(event)) {
return m_room->getUser(static_cast<NeoChatUser *>(m_room->user(replyPtr->senderId())));
return m_room->getUser(m_room->user(replyPtr->senderId()));
} else {
return m_room->getUser(nullptr);
}

View File

@@ -3,6 +3,8 @@
#include "userlistmodel.h"
#include <QGuiApplication>
#include <Quotient/connection.h>
#include <Quotient/events/roompowerlevelsevent.h>
@@ -14,6 +16,9 @@ UserListModel::UserListModel(QObject *parent)
: QAbstractListModel(parent)
, m_currentRoom(nullptr)
{
connect(static_cast<QGuiApplication *>(QGuiApplication::instance()), &QGuiApplication::paletteChanged, this, [this]() {
refreshAllUsers();
});
}
void UserListModel::setRoom(NeoChatRoom *room)
@@ -71,8 +76,7 @@ QVariant UserListModel::data(const QModelIndex &index, int role) const
return user->id();
}
if (role == AvatarRole) {
auto neoChatUser = static_cast<NeoChatUser *>(user);
return m_currentRoom->avatarForMember(neoChatUser);
return m_currentRoom->avatarForMember(user);
}
if (role == ObjectRole) {
return QVariant::fromValue(user);

View File

@@ -33,6 +33,7 @@ Name[ro]=NeoChat
Name[ru]=NeoChat
Name[sk]=NeoChat
Name[sl]=NeoChat
Name[sv]=NeoChat
Name[ta]=நியோச்சாட்
Name[tr]=NeoChat
Name[uk]=NeoChat
@@ -53,7 +54,7 @@ Comment[es]=Un cliente para Matrix, el protocolo de comunicaciones descentraliza
Comment[eu]=Matrix, deszentralizatutako komunikazio protokolorako, bezero bat
Comment[fi]=Hajautetun Matrix-viestintäyhteyskäytännön asiakasohjelma
Comment[fr]=Un client pour « Matrix », le protocole décentralisé de communications.
Comment[gl]=Un cliente para Matrix, o protocolo de comunicación descentralizada
Comment[gl]=Un cliente para Matrix, o protocolo de comunicación descentralizada.
Comment[hu]=Kliens a matrixhoz, a decentralizált kommunikációs protokollhoz
Comment[ia]=Un cliente per Matrix, le protocollo de communication decentralisate
Comment[id]=Sebuah klien untuk matrix, protokol komunikasi terdecentralisasi
@@ -72,6 +73,7 @@ Comment[ro]=Client pentru Matrix, protocolul de comunicare descentralizată
Comment[ru]=Клиент для Matrix — децентрализованного коммуникационного протокола
Comment[sk]=Klient pre matrix, decentralizovaný komunikačný protokol
Comment[sl]=Odjemalec za decentralizirani komunikacijski protokol matrix
Comment[sv]=En klient för matrix, det decentraliserade kommunikationsprotokollet
Comment[tr]=Merkezi olmayan iletişim protokolü Matrix için bir istemci
Comment[uk]=Клієнт matrix, децентралізованого протоколу обміну даними
Comment[x-test]=xxA client for matrix, the decentralized communication protocolxx
@@ -112,6 +114,7 @@ Name[ro]=Mesaj nou
Name[ru]=Новое сообщение
Name[sk]=Nová správa
Name[sl]=Novo sporočilo
Name[sv]=Nytt meddelande
Name[ta]=புதிய செய்தி
Name[tr]=Yeni ileti
Name[uk]=Нове повідомлення
@@ -130,7 +133,7 @@ Comment[es]=Hay un mensaje nuevo
Comment[eu]=Mezu berri bat dago
Comment[fi]=Saapui uusi viesti
Comment[fr]=Il y a un nouveau message
Comment[gl]=Hai unha nova mensaxe
Comment[gl]=Hai unha nova mensaxe.
Comment[hu]=Új üzenet érkezett
Comment[ia]=Il ha un nove message
Comment[id]=Ada pesan baru
@@ -149,6 +152,7 @@ Comment[ro]=Este un mesaj nou
Comment[ru]=Доступно новое сообщение
Comment[sk]=Je nová správa
Comment[sl]=Prišlo je novo sporočilo
Comment[sv]=Det finns ett nytt meddelande
Comment[ta]=ஒரு புதிய செய்தி உள்ளது
Comment[tr]=Yeni bir ileti var
Comment[uk]=Надійшло нове повідомлення
@@ -186,6 +190,7 @@ Name[pt]=Novo Convite
Name[pt_BR]=Novo convite
Name[ru]=Новое приглашение
Name[sl]=Novo povabilo
Name[sv]=Ny inbjudan
Name[ta]=புதிய அழைப்பிதழ்
Name[tr]=Yeni Davet
Name[uk]=Нове запрошення
@@ -204,7 +209,7 @@ Comment[es]=Hay una nueva invitación a una sala
Comment[eu]=Gela baterako gonbidapen berri bat dago
Comment[fi]=Uusi kutsu huoneeseen
Comment[fr]=Il y a une nouvelle invitation dans un salon.
Comment[gl]=Tes un novo convite para unha sala
Comment[gl]=Tes un novo convite para unha sala.
Comment[ia]=Il ha un nove invitation a un sala
Comment[id]=Ada undangan baru ke sebuah ruangan
Comment[ie]=Vu have un nov invitation a un chambre
@@ -219,6 +224,7 @@ Comment[pt]=Existe um novo convite para uma sala
Comment[pt_BR]=Existe um novo convite para uma sala
Comment[ru]=Доступно новое приглашение в комнату
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[uk]=У кімнаті нове запрошення

View File

@@ -4,8 +4,10 @@
#include "neochatroom.h"
#include <QFileInfo>
#include <QGuiApplication>
#include <QMetaObject>
#include <QMimeDatabase>
#include <QPalette>
#include <QTemporaryFile>
#include <QTextDocument>
@@ -13,6 +15,7 @@
#include <QMediaPlayer>
#include <Quotient/jobs/basejob.h>
#include <Quotient/user.h>
#include <qcoro/qcorosignal.h>
#include <Quotient/connection.h>
@@ -43,6 +46,7 @@
#include "neochatconfig.h"
#include "notificationsmanager.h"
#include "texthandler.h"
#include "utils.h"
#include <KConfig>
#include <KConfigGroup>
@@ -412,11 +416,11 @@ QVariantList NeoChatRoom::getUsers(const QString &keyword, int limit) const
int count = 0;
for (const auto u : userList) {
if (u->displayname(this).contains(keyword, Qt::CaseInsensitive)) {
NeoChatUser user(u->id(), u->connection());
Quotient::User user(u->id(), u->connection());
QVariantMap userVariant{{QStringLiteral("id"), user.id()},
{QStringLiteral("displayName"), user.displayname(this)},
{QStringLiteral("avatarMediaId"), user.avatarMediaId(this)},
{QStringLiteral("color"), user.color()}};
{QStringLiteral("color"), Utils::getUserColor(user.hueF())}};
matchedList.append(QVariant::fromValue(userVariant));
count++;
@@ -442,11 +446,10 @@ static const QVariantMap emptyUser = {
QVariantMap NeoChatRoom::getUser(const QString &userID) const
{
NeoChatUser *userObject = static_cast<NeoChatUser *>(user(userID));
return getUser(userObject);
return getUser(user(userID));
}
QVariantMap NeoChatRoom::getUser(NeoChatUser *user) const
QVariantMap NeoChatRoom::getUser(User *user) const
{
if (user == nullptr) {
return emptyUser;
@@ -458,7 +461,7 @@ QVariantMap NeoChatRoom::getUser(NeoChatUser *user) const
{QStringLiteral("displayName"), user->displayname(this)},
{QStringLiteral("avatarSource"), avatarForMember(user)},
{QStringLiteral("avatarMediaId"), user->avatarMediaId(this)},
{QStringLiteral("color"), user->color()},
{QStringLiteral("color"), Utils::getUserColor(user->hueF())},
{QStringLiteral("object"), QVariant::fromValue(user)},
};
}
@@ -539,7 +542,7 @@ QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format,
if (prettyPrint) {
subjectName = QStringLiteral("<a href=\"https://matrix.to/#/%1\" style=\"color: %2\">%3</a>")
.arg(e.userId(), static_cast<NeoChatUser *>(user(e.userId()))->color().name(), subjectName);
.arg(e.userId(), Utils::getUserColor(user(e.userId())->hueF()).name(), subjectName);
}
// The below code assumes senderName output in AuthorRole
@@ -1682,7 +1685,7 @@ QVariantMap NeoChatRoom::chatBoxReplyUser() const
if (m_chatBoxReplyId.isEmpty()) {
return emptyUser;
}
return getUser(static_cast<NeoChatUser *>(user((*findInTimeline(m_chatBoxReplyId))->senderId())));
return getUser(user((*findInTimeline(m_chatBoxReplyId))->senderId()));
}
QString NeoChatRoom::chatBoxReplyMessage() const
@@ -1698,7 +1701,7 @@ QVariantMap NeoChatRoom::chatBoxEditUser() const
if (m_chatBoxEditId.isEmpty()) {
return emptyUser;
}
return getUser(static_cast<NeoChatUser *>(user((*findInTimeline(m_chatBoxEditId))->senderId())));
return getUser(user((*findInTimeline(m_chatBoxEditId))->senderId()));
}
QString NeoChatRoom::chatBoxEditMessage() const
@@ -1904,9 +1907,10 @@ int NeoChatRoom::maxRoomVersion() const
}
return maxVersion;
}
NeoChatUser *NeoChatRoom::directChatRemoteUser() const
Quotient::User *NeoChatRoom::directChatRemoteUser() const
{
return dynamic_cast<NeoChatUser *>(connection()->directChatUsers(this)[0]);
return connection()->directChatUsers(this)[0];
}
void NeoChatRoom::sendLocation(float lat, float lon, const QString &description)
@@ -1938,7 +1942,7 @@ QByteArray NeoChatRoom::roomAcountDataJson(const QString &eventType)
return QJsonDocument(accountData(eventType)->fullJson()).toJson();
}
QUrl NeoChatRoom::avatarForMember(NeoChatUser *user) const
QUrl NeoChatRoom::avatarForMember(Quotient::User *user) const
{
const auto &url = memberAvatarUrl(user->id());
if (url.isEmpty() || url.scheme() != "mxc"_ls) {

View File

@@ -10,10 +10,15 @@
#include <QTextCursor>
#include <QCoroTask>
#include <Quotient/user.h>
#include "neochatuser.h"
#include "pollhandler.h"
namespace Quotient
{
class User;
}
class PushNotificationState : public QObject
{
Q_OBJECT
@@ -64,7 +69,7 @@ class NeoChatRoom : public Quotient::Room
*
* The list does not include the local user.
*
* This is different to getting a list of NeoChatUser objects or Quotient::User objects
* This is different to getting a list of Quotient::User objects
* as neither of those can provide details like the displayName or avatarMediaId
* without the room context as these can vary from room to room. This function
* provides the room context and puts the result as a list of QVariantMap objects.
@@ -76,7 +81,7 @@ class NeoChatRoom : public Quotient::Room
* - displayName - Display name in the context of this room.
* - display - Name in the context of this room.
*
* @sa Quotient::User, NeoChatUser
* @sa Quotient::User
*/
Q_PROPERTY(QVariantList usersTyping READ getUsersTyping NOTIFY typingChanged)
@@ -119,7 +124,7 @@ class NeoChatRoom : public Quotient::Room
/**
* @brief Get a user object for the other person in a direct chat.
*/
Q_PROPERTY(NeoChatUser *directChatRemoteUser READ directChatRemoteUser CONSTANT)
Q_PROPERTY(Quotient::User *directChatRemoteUser READ directChatRemoteUser CONSTANT)
/**
* @brief If the room is a space.
@@ -316,7 +321,7 @@ class NeoChatRoom : public Quotient::Room
/**
* @brief Get the user for the message being replied to.
*
* This is different to getting a NeoChatUser object or Quotient::User object
* This is different to getting a Quotient::User object
* as neither of those can provide details like the displayName or avatarMediaId
* without the room context as these can vary from room to room.
*
@@ -329,9 +334,9 @@ class NeoChatRoom : public Quotient::Room
* - avatarSource - The mxc URL for the user's avatar in the current room.
* - avatarMediaId - Avatar id in the context of this room.
* - color - Color for the user.
* - object - The NeoChatUser object for the user.
* - object - The Quotient::User object for the user.
*
* @sa getUser, Quotient::User, NeoChatUser
* @sa getUser, Quotient::User
*/
Q_PROPERTY(QVariantMap chatBoxReplyUser READ chatBoxReplyUser NOTIFY chatBoxReplyIdChanged)
@@ -345,7 +350,7 @@ class NeoChatRoom : public Quotient::Room
/**
* @brief Get the user for the message being edited.
*
* This is different to getting a NeoChatUser object or Quotient::User object
* This is different to getting a Quotient::User object
* as neither of those can provide details like the displayName or avatarMediaId
* without the room context as these can vary from room to room.
*
@@ -358,9 +363,9 @@ class NeoChatRoom : public Quotient::Room
* - avatarSource - The mxc URL for the user's avatar in the current room.
* - avatarMediaId - Avatar id in the context of this room.
* - color - Color for the user.
* - object - The NeoChatUser object for the user.
* - object - The Quotient::User object for the user.
*
* @sa getUser, Quotient::User, NeoChatUser
* @sa getUser, Quotient::User
*/
Q_PROPERTY(QVariantMap chatBoxEditUser READ chatBoxEditUser NOTIFY chatBoxEditIdChanged)
@@ -392,7 +397,7 @@ public:
/**
* @brief Get a list of users in the context of this room.
*
* This is different to getting a list of NeoChatUser objects or Quotient::User objects
* This is different to getting a list of Quotient::User objects
* as neither of those can provide details like the displayName or avatarMediaId
* without the room context as these can vary from room to room. This function
* provides the room context and returns the result as a list of QVariantMap objects.
@@ -407,14 +412,14 @@ public:
* - avatarMediaId - Avatar id in the context of this room.
* - color - Color for the user.
*
* @sa Quotient::User, NeoChatUser
* @sa Quotient::User
*/
Q_INVOKABLE [[nodiscard]] QVariantList getUsers(const QString &keyword, int limit = -1) const;
/**
* @brief Get a user in the context of this room.
*
* This is different to getting a NeoChatUser object or Quotient::User object
* This is different to getting a Quotient::User object
* as neither of those can provide details like the displayName or avatarMediaId
* without the room context as these can vary from room to room. This function
* provides the room context and outputs the result as QVariantMap.
@@ -431,16 +436,16 @@ public:
* - avatarSource - The mxc URL for the user's avatar in the current room.
* - avatarMediaId - Avatar id in the context of this room.
* - color - Color for the user.
* - object - The NeoChatUser object for the user.
* - object - The Quotient::User object for the user.
*
* @sa Quotient::User, NeoChatUser
* @sa Quotient::User
*/
Q_INVOKABLE [[nodiscard]] QVariantMap getUser(const QString &userID) const;
/**
* @brief Get a user in the context of this room.
*
* This is different to getting a NeoChatUser object or Quotient::User object
* This is different to getting a Quotient::User object
* as neither of those can provide details like the displayName or avatarMediaId
* without the room context as these can vary from room to room. This function
* provides the room context and outputs the result as QVariantMap.
@@ -457,11 +462,11 @@ public:
* - avatarSource - The mxc URL for the user's avatar in the current room.
* - avatarMediaId - Avatar id in the context of this room.
* - color - Color for the user.
* - object - The NeoChatUser object for the user.
* - object - The Quotient::User object for the user.
*
* @sa Quotient::User, NeoChatUser
* @sa Quotient::User
*/
Q_INVOKABLE [[nodiscard]] QVariantMap getUser(NeoChatUser *user) const;
Q_INVOKABLE [[nodiscard]] QVariantMap getUser(Quotient::User *user) const;
[[nodiscard]] QVariantList getUsersTyping() const;
@@ -602,7 +607,7 @@ public:
[[nodiscard]] QString avatarMediaId() const;
NeoChatUser *directChatRemoteUser() const;
Quotient::User *directChatRemoteUser() const;
[[nodiscard]] bool isSpace();
@@ -818,7 +823,7 @@ public:
*/
Q_INVOKABLE QByteArray roomAcountDataJson(const QString &eventType);
Q_INVOKABLE [[nodiscard]] QUrl avatarForMember(NeoChatUser *user) const;
Q_INVOKABLE [[nodiscard]] QUrl avatarForMember(Quotient::User *user) const;
/**
* @brief Returns the event that is being replied to. This includes events that were manually loaded using NeoChatRoom::loadReply.

View File

@@ -1,43 +0,0 @@
// SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org>
// SPDX-License-Identifier: GPL-3.0-only
#include "neochatuser.h"
#include <QGuiApplication>
#include <QJsonObject>
#include <QPalette>
#include <Quotient/connection.h>
using namespace Quotient;
NeoChatUser::NeoChatUser(QString userId, Connection *connection)
: User(std::move(userId), connection)
{
connect(static_cast<QGuiApplication *>(QGuiApplication::instance()), &QGuiApplication::paletteChanged, this, &NeoChatUser::polishColor);
polishColor();
}
QColor NeoChatUser::color()
{
return m_color;
}
void NeoChatUser::setColor(const QColor &color)
{
if (m_color == color) {
return;
}
m_color = color;
Q_EMIT colorChanged(m_color);
}
void NeoChatUser::polishColor()
{
const auto lightness = static_cast<QGuiApplication *>(QGuiApplication::instance())->palette().color(QPalette::Active, QPalette::Window).lightnessF();
// https://github.com/quotient-im/libQuotient/wiki/User-color-coding-standard-draft-proposal
setColor(QColor::fromHslF(hueF(), 1, -0.7 * lightness + 0.9, 1));
}
#include "moc_neochatuser.cpp"

View File

@@ -1,39 +0,0 @@
// SPDX-FileCopyrightText: 2018-2019 Black Hat <bhat@encom.eu.org>
// SPDX-License-Identifier: GPL-3.0-only
#pragma once
#include <QObject>
#include <Quotient/user.h>
/**
* @class NeoChatUser
*
* A class inherited from Quotient::User to add a user color function.
*
* @sa Quotient::User
*/
class NeoChatUser : public Quotient::User
{
Q_OBJECT
/**
* @brief The color to use for the user.
*/
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
public:
explicit NeoChatUser(QString userId, Quotient::Connection *connection);
public Q_SLOTS:
QColor color();
void setColor(const QColor &color);
Q_SIGNALS:
void colorChanged(QColor _t1);
private:
QColor m_color;
void polishColor();
};

View File

@@ -34,6 +34,7 @@ Name[ro]=NeoChat
Name[ru]=NeoChat
Name[sk]=NeoChat
Name[sl]=NeoChat
Name[sv]=NeoChat
Name[ta]=நியோச்சாட்
Name[tr]=NeoChat
Name[uk]=NeoChat
@@ -52,7 +53,7 @@ Comment[es]=Buscar salas en NeoChat
Comment[eu]=Bilatu gelak NeoChat-en
Comment[fi]=Etsi huoneita NeoChatissä
Comment[fr]=Trouver des salons dans NeoChat
Comment[gl]=Atopa salas en NeoChat
Comment[gl]=Atopa salas en NeoChat.
Comment[ia]=Trova salas in NeoChat
Comment[id]=Cari ruangan di NeoChat
Comment[ie]=Trovar chambres in NeoChat
@@ -66,6 +67,7 @@ Comment[pt]=Procurar salas no NeoChat
Comment[pt_BR]=Encontrar salas no NeoChat
Comment[ru]=Поиск комнат NeoChat
Comment[sl]=Najdi sobe v NeoChatu
Comment[sv]=Sök efter rum i NeoChat
Comment[ta]=நியோச்சாட்டில் அரங்குகளை கண்டுபிடிக்கும்
Comment[tr]=NeoChat'te odalar bulun
Comment[uk]=Пошук кімнат у NeoChat

View File

@@ -36,7 +36,7 @@ QQC2.Control {
/**
* @brief The current room that user is viewing.
*/
property NeoChatRoom currentRoom
required property NeoChatRoom currentRoom
/**
* @brief The QQC2.TextArea object.
@@ -55,10 +55,10 @@ QQC2.Control {
Kirigami.Action {
id: attachmentAction
property bool isBusy: currentRoom && currentRoom.hasFileUploading
property bool isBusy: root.currentRoom && root.currentRoom.hasFileUploading
// Matrix does not allow sending attachments in replies
visible: currentRoom.chatBoxReplyId.length === 0 && currentRoom.chatBoxAttachmentPath.length === 0
visible: root.currentRoom.chatBoxReplyId.length === 0 && root.currentRoom.chatBoxAttachmentPath.length === 0
icon.name: "mail-attachment"
text: i18n("Attach an image or file")
displayHint: Kirigami.DisplayHint.IconOnly
@@ -72,7 +72,7 @@ QQC2.Control {
if (!path) {
return;
}
currentRoom.chatBoxAttachmentPath = path;
root.currentRoom.chatBoxAttachmentPath = path;
})
fileDialog.open()
}
@@ -106,7 +106,7 @@ QQC2.Control {
displayHint: QQC2.AbstractButton.IconOnly
onTriggered: {
locationChooserComponent.createObject(QQC2.ApplicationWindow.overlay, {room: currentRoom}).open()
locationChooserComponent.createObject(QQC2.ApplicationWindow.overlay, {room: root.currentRoom}).open()
}
},
Kirigami.Action {
@@ -162,10 +162,12 @@ QQC2.Control {
leftPadding: LayoutMirroring.enabled ? actionsRow.width : Kirigami.Units.largeSpacing
rightPadding: LayoutMirroring.enabled ? Kirigami.Units.largeSpacing : actionsRow.width + x * 2 + Kirigami.Units.largeSpacing * 2
placeholderText: readOnly ? i18n("This room is encrypted. Build libQuotient with encryption enabled to send encrypted messages.") : currentRoom.usesEncryption ? i18n("Send an encrypted message…") : currentRoom.chatBoxAttachmentPath.length > 0 ? i18n("Set an attachment caption...") : i18n("Send a message…")
placeholderText: readOnly ? i18n("This room is encrypted. Build libQuotient with encryption enabled to send encrypted messages.") : root.currentRoom.usesEncryption ? i18n("Send an encrypted message…") : root.currentRoom.chatBoxAttachmentPath.length > 0 ? i18n("Set an attachment caption...") : i18n("Send a message…")
verticalAlignment: TextEdit.AlignVCenter
wrapMode: Text.Wrap
readOnly: (currentRoom.usesEncryption && !Controller.encryptionSupported)
readOnly: (root.currentRoom.usesEncryption && !Controller.encryptionSupported)
Accessible.description: placeholderText
Timer {
id: repeatTimer
@@ -175,10 +177,10 @@ QQC2.Control {
onTextChanged: {
if (!repeatTimer.running && Config.typingNotifications) {
var textExists = text.length > 0
currentRoom.sendTypingNotification(textExists)
root.currentRoom.sendTypingNotification(textExists)
textExists ? repeatTimer.start() : repeatTimer.stop()
}
currentRoom.chatBoxText = text
root.currentRoom.chatBoxText = text
}
onCursorRectangleChanged: chatBarScrollView.ensureVisible(cursorRectangle)
onSelectedTextChanged: {
@@ -205,7 +207,7 @@ QQC2.Control {
remove(cursorPosition, cursorPosition + 1)
}
if (textField.text == selectedText || textField.text.length <= 1) {
currentRoom.sendTypingNotification(false)
root.currentRoom.sendTypingNotification(false)
repeatTimer.stop()
}
if (quickFormatBar.visible) {
@@ -239,16 +241,16 @@ QQC2.Control {
if (event.key === Qt.Key_V && event.modifiers & Qt.ControlModifier) {
root.pasteImage();
} else if (event.key === Qt.Key_Up && event.modifiers & Qt.ControlModifier) {
currentRoom.replyLastMessage();
root.currentRoom.replyLastMessage();
} else if (event.key === Qt.Key_Up && textField.text.length === 0) {
currentRoom.editLastMessage();
root.currentRoom.editLastMessage();
} else if (event.key === Qt.Key_Up && completionMenu.visible) {
completionMenu.decrementIndex()
} else if (event.key === Qt.Key_Down && completionMenu.visible) {
completionMenu.incrementIndex()
} else if (event.key === Qt.Key_Backspace) {
if (textField.text == selectedText || textField.text.length <= 1) {
currentRoom.sendTypingNotification(false)
root.currentRoom.sendTypingNotification(false)
repeatTimer.stop()
}
if (quickFormatBar.visible && selectedText.length > 0) {
@@ -280,19 +282,19 @@ QQC2.Control {
Component {
id: replyPane
ReplyPane {
userName: currentRoom.chatBoxReplyUser.displayName
userColor: currentRoom.chatBoxReplyUser.color
userAvatar: currentRoom.chatBoxReplyUser.avatarSource
text: currentRoom.chatBoxReplyMessage
userName: root.currentRoom.chatBoxReplyUser.displayName
userColor: root.currentRoom.chatBoxReplyUser.color
userAvatar: root.currentRoom.chatBoxReplyUser.avatarSource
text: root.currentRoom.chatBoxReplyMessage
}
}
Component {
id: attachmentPane
AttachmentPane {
attachmentPath: currentRoom.chatBoxAttachmentPath
attachmentPath: root.currentRoom.chatBoxAttachmentPath
onAttachmentCancelled: {
currentRoom.chatBoxAttachmentPath = "";
root.currentRoom.chatBoxAttachmentPath = "";
root.forceActiveFocus()
}
}
@@ -344,8 +346,8 @@ QQC2.Control {
text: i18nc("@action:button", "Cancel reply")
icon.name: "dialog-close"
onTriggered: {
currentRoom.chatBoxReplyId = "";
currentRoom.chatBoxAttachmentPath = "";
root.currentRoom.chatBoxReplyId = "";
root.currentRoom.chatBoxAttachmentPath = "";
root.forceActiveFocus()
}
}
@@ -386,7 +388,7 @@ QQC2.Control {
PieProgressBar {
visible: modelData.isBusy
progress: currentRoom.fileUploadingProgress
progress: root.currentRoom.fileUploadingProgress
}
}
}
@@ -401,6 +403,8 @@ QQC2.Control {
includeCustom: true
closeOnChosen: false
currentRoom: root.currentRoom
onChosen: insertText(emoji)
onClosed: if (emojiButton.checked) emojiButton.checked = false
}
@@ -466,15 +470,15 @@ QQC2.Control {
if (localPath.length === 0) {
return;
}
currentRoom.chatBoxAttachmentPath = localPath
root.currentRoom.chatBoxAttachmentPath = localPath
}
function postMessage() {
actionsHandler.handleNewMessage();
repeatTimer.stop()
currentRoom.markAllMessagesAsRead();
root.currentRoom.markAllMessagesAsRead();
textField.clear();
currentRoom.chatBoxReplyId = "";
root.currentRoom.chatBoxReplyId = "";
messageSent()
}

View File

@@ -30,12 +30,12 @@ import org.kde.neochat 1.0
* @sa ChatBar
*/
ColumnLayout {
id: chatBox
id: root
/**
* @brief The current room that user is viewing.
*/
property NeoChatRoom currentRoom
required property NeoChatRoom currentRoom
/**
* @brief A message has been sent from the chat bar.
@@ -63,7 +63,7 @@ ColumnLayout {
ChatBar {
id: chatBar
visible: currentRoom.canSendEvent("m.room.message")
visible: root.currentRoom.canSendEvent("m.room.message")
Layout.fillWidth: true
Layout.minimumHeight: Math.max(Kirigami.Units.gridUnit * 2, implicitHeight + Kirigami.Units.largeSpacing)
@@ -78,7 +78,7 @@ ColumnLayout {
}
onMessageSent: {
chatBox.messageSent();
root.messageSent();
}
}

View File

@@ -10,8 +10,14 @@ import org.kde.neochat 1.0
ColumnLayout {
id: root
/**
* @brief The current room that user is viewing.
*/
property NeoChatRoom currentRoom
property bool includeCustom: false
property bool showQuickReaction: false
property NeoChatRoom currentRoom
readonly property var currentEmojiModel: {
if (includeCustom) {
@@ -151,7 +157,7 @@ ColumnLayout {
ImagePacksModel {
id: stickerPackModel
room: currentRoom
room: root.currentRoom
showStickers: true
showEmoticons: false
}
@@ -160,7 +166,7 @@ ColumnLayout {
id: stickerModel
model: stickerPackModel
packIndex: 0
room: currentRoom
room: root.currentRoom
}
EmoticonFilterModel {

View File

@@ -46,6 +46,7 @@ ApplicationWindow {
asset: root.asset
author: root.author
isLive: true
heading: NaN
visible: !isNaN(root.latitude) && !isNaN(root.longitude)
}
MapItemView {

View File

@@ -6,6 +6,7 @@ import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0
/**
* @brief A component that provides a set of actions when a message is hovered in the timeline.
@@ -16,6 +17,11 @@ import org.kde.kirigami 2.15 as Kirigami
QQC2.Control {
id: root
/**
* @brief The current room that user is viewing.
*/
required property NeoChatRoom currentRoom
/**
* @brief Whether the actions should be shown.
*/
@@ -108,6 +114,7 @@ QQC2.Control {
EmojiDialog {
id: emojiDialog
currentRoom: root.currentRoom
showQuickReaction: true
onChosen: (emoji) => root.reactClicked(emoji)
}

View File

@@ -24,11 +24,14 @@ MapQuickItem {
required property bool isLive
required property real heading
anchorPoint.x: sourceItem.width / 2
anchorPoint.y: sourceItem.height
coordinate: QtPositioning.coordinate(root.latitude, root.longitude)
autoFadeIn: false
sourceItem: Kirigami.Icon {
id: mainIcon
width: height
height: Kirigami.Units.iconSizes.huge
source: "gps"
@@ -55,5 +58,19 @@ MapQuickItem {
source: root.author.avatarSource
color: root.author.color
}
Kirigami.Icon {
id: headingIcon
source: "go-up-symbolic"
color: parent.color
visible: !isNaN(root.heading) && root.isLive
anchors.bottom: mainIcon.top
anchors.horizontalCenter: mainIcon.horizontalCenter
transform: Rotation {
origin.x: headingIcon.width/2
origin.y: headingIcon.height + mainIcon.height/2
angle: root.heading
}
}
}
}

View File

@@ -36,6 +36,7 @@ Kirigami.Page {
}
delegate: LocationMapItem {
isLive: true
heading: NaN
}
}

View File

@@ -28,6 +28,7 @@ LoginStep {
id: matrixIdField
Kirigami.FormData.label: i18n("Matrix ID:")
placeholderText: "@user:matrix.org"
Accessible.name: i18n("Matrix ID")
onTextChanged: {
LoginHelper.matrixId = text
}

View File

@@ -38,6 +38,7 @@ LoginStep {
id: passwordField
onTextChanged: LoginHelper.password = text
enabled: !LoginHelper.isLoggingIn
Accessible.name: i18n("Password")
Component.onCompleted: {
passwordField.forceActiveFocus()

View File

@@ -57,6 +57,7 @@ TimelineContainer {
asset: root.asset
author: root.author
isLive: true
heading: NaN
}
TapHandler {

View File

@@ -50,6 +50,12 @@ TimelineContainer {
isReply: root.isReply
textMessage: root.display
TapHandler {
enabled: !label.hoveredLink
acceptedButtons: Qt.LeftButton
onLongPressed: root.openContextMenu()
}
}
Loader {
Layout.fillWidth: true

View File

@@ -50,10 +50,10 @@ Flow {
Kirigami.Theme.inherit: false
Kirigami.Theme.colorSet: Kirigami.Theme.View
radius: height / 2
shadow.size: Kirigami.Units.smallSpacing
shadow.color: !model.hasLocalUser ? Qt.rgba(0.0, 0.0, 0.0, 0.10) : Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.10)
border.color: Kirigami.ColorUtils.tintWithAlpha(color, Kirigami.Theme.textColor, 0.15)
border.width: 1
shadow {
size: Kirigami.Units.smallSpacing
color: !model.hasLocalUser ? Qt.rgba(0.0, 0.0, 0.0, 0.10) : Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.10)
}
}
onClicked: reactionClicked(model.reaction)

View File

@@ -34,9 +34,9 @@ Item {
* - displayName - The display name of the reply author.
* - display - The name of the reply author.
* - color - The color for the reply author.
* - object - The NeoChatUser object for the reply author.
* - object - The Quotient::User object for the reply author.
*
* @sa NeoChatUser
* @sa Quotient::User
*/
required property var author

View File

@@ -54,9 +54,9 @@ ColumnLayout {
* - displayName - The display name of the author.
* - display - The name of the author.
* - color - The color for the author.
* - object - The NeoChatUser object for the author.
* - object - The Quotient::User object for the author.
*
* @sa NeoChatUser
* @sa Quotient::User
*/
required property var author
@@ -137,9 +137,9 @@ ColumnLayout {
* - displayName - The display name of the reply author.
* - display - The name of the reply author.
* - color - The color for the reply author.
* - object - The NeoChatUser object for the reply author.
* - object - The Quotient::User object for the reply author.
*
* @sa NeoChatUser
* @sa Quotient::User
*/
required property var replyAuthor
@@ -485,7 +485,7 @@ ColumnLayout {
Layout.maximumWidth: contentMaxWidth
active: root.isReply
active: root.isReply && root.reply
visible: active
sourceComponent: ReplyComponent {
@@ -526,21 +526,20 @@ ColumnLayout {
return Kirigami.Theme.backgroundColor
}
radius: Kirigami.Units.smallSpacing
shadow.size: Kirigami.Units.smallSpacing
shadow.color: root.showHighlight ? Qt.rgba(0.0, 0.0, 0.0, 0.10) : Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.10)
border.color: Kirigami.ColorUtils.tintWithAlpha(color, Kirigami.Theme.textColor, 0.15)
border.width: 1
shadow {
size: Kirigami.Units.smallSpacing
color: root.isHighlighted ? Qt.rgba(0.0, 0.0, 0.0, 0.10) : Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.10)
}
Behavior on color {
enabled: isTemporaryHighlighted
ColorAnimation {target: bubbleBackground; duration: Kirigami.Units.veryLongDuration; easing.type: Easing.InOutCubic}
ColorAnimation { duration: Kirigami.Units.shortDuration }
}
}
}
}
background: Rectangle {
visible: mainContainer.hovered
visible: mainContainer.hovered && Config.compactLayout
color: Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.15)
radius: Kirigami.Units.smallSpacing
}

View File

@@ -16,9 +16,19 @@ import org.kde.neochat 1.0
QQC2.ScrollView {
id: root
required property NeoChatRoom currentRoom
readonly property bool isLoaded: root.width * root.height > 10
onCurrentRoomChanged: {
roomChanging = true;
applicationWindow().hoverLinkIndicator.text = "";
messageListView.positionViewAtBeginning();
hasScrolledUpBefore = false;
roomChanging = true;
}
property bool roomChanging: false
readonly property bool atYEnd: messageListView.atYEnd
/// Used to determine if scrolling to the bottom should mark the message as unread
property bool hasScrolledUpBefore: false;
signal focusChatBox()
ListView {
@@ -38,7 +48,7 @@ QQC2.ScrollView {
interactive: Kirigami.Settings.isMobile
bottomMargin: Kirigami.Units.largeSpacing + Math.round(Kirigami.Theme.defaultFont.pointSize * 2)
model: !isLoaded ? undefined : collapseStateProxyModel
model: collapseStateProxyModel
MessageEventModel {
id: messageEventModel
@@ -70,13 +80,15 @@ QQC2.ScrollView {
messageEventModel.fetchMore(messageEventModel.index(0, 0));
}
onAtYEndChanged: if (atYEnd && hasScrolledUpBefore) {
if (QQC2.ApplicationWindow.window && (QQC2.ApplicationWindow.window.visibility !== QQC2.ApplicationWindow.Hidden)) {
root.currentRoom.markAllMessagesAsRead();
onAtYEndChanged: if (!root.roomChanging) {
if (atYEnd && root.hasScrolledUpBefore) {
if (QQC2.ApplicationWindow.window && (QQC2.ApplicationWindow.window.visibility !== QQC2.ApplicationWindow.Hidden)) {
root.currentRoom.markAllMessagesAsRead();
}
root.hasScrolledUpBefore = false;
} else if (!atYEnd) {
root.hasScrolledUpBefore = true;
}
hasScrolledUpBefore = false;
} else if (!atYEnd) {
hasScrolledUpBefore = true;
}
// Not rendered because the sections are part of the TimelineContainer.qml, this is only so that items have the section property available for use by sectionBanner.
@@ -310,6 +322,7 @@ QQC2.ScrollView {
y: delegate ? delegate.mapToItem(parent, 0, 0).y + delegate.bubbleY - height + Kirigami.Units.smallSpacing : 0
width: delegate ? delegate.bubbleWidth : Kirigami.Units.gridUnit * 4
currentRoom: root.currentRoom
showActions: delegate && delegate.hovered
verified: delegate && delegate.verified
editable: delegate && delegate.author.isLocalUser && (delegate.delegateType === MessageEventModel.Emote || delegate.delegateType === MessageEventModel.Message)

View File

@@ -11,6 +11,11 @@ import org.kde.neochat 1.0
QQC2.Popup {
id: emojiPopup
/**
* @brief The current room that user is viewing.
*/
property NeoChatRoom currentRoom
property bool includeCustom: false
property bool closeOnChosen: true
property bool showQuickReaction: false
@@ -52,8 +57,10 @@ QQC2.Popup {
contentItem: EmojiPicker {
id: emojiPicker
height: 400
currentRoom: root.currentRoom
includeCustom: emojiPopup.includeCustom
showQuickReaction: emojiPopup.showQuickReaction
currentRoom: RoomManager.currentRoom
onChosen: {
emojiPopup.chosen(emoji)
if (emojiPopup.closeOnChosen) emojiPopup.close()

View File

@@ -11,7 +11,7 @@ import org.kde.kirigamiaddons.labs.components 1.0 as KirigamiComponents
import org.kde.neochat 1.0
Kirigami.OverlaySheet {
Kirigami.Dialog {
id: root
signal closed()
@@ -26,6 +26,9 @@ Kirigami.OverlaySheet {
topPadding: 0
bottomPadding: 0
standardButtons: Kirigami.Dialog.NoButton
width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24)
title: i18nc("@title:menu Account detail dialog", "Account detail")
contentItem: ColumnLayout {
@@ -196,10 +199,5 @@ Kirigami.OverlaySheet {
}
}
}
@OVERLAYSHEET_OPEN@: {
if (!sheetOpen) {
closed()
}
}
}

View File

@@ -193,6 +193,12 @@ Kirigami.Page {
width: Kirigami.Units.iconSizes.small
height: Kirigami.Units.iconSizes.small
}
text: roomListModel.categoryVisible(section) ? i18nc("Collapse <section name>", "Collapse %1", roomListModel.categoryName(section)) : i18nc("Expand <section name", "Expand %1", roomListModel.categoryName(section))
display: QQC2.Button.IconOnly
QQC2.ToolTip.text: text
QQC2.ToolTip.visible: hovered
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
onClicked: roomListModel.setCategoryVisible(section, !roomListModel.categoryVisible(section))
}

View File

@@ -31,6 +31,8 @@ Delegates.RoundedItemDelegate {
readonly property bool hasNotifications: notificationCount > 0
Accessible.name: root.displayName
onPressAndHold: createRoomListContextMenu()
TapHandler {
@@ -46,7 +48,7 @@ Delegates.RoundedItemDelegate {
source: root.avatar ? "image://mxc/" + root.avatar : ""
name: root.displayName
visible: Config.showAvatarInRoomDrawer
implicitHeight: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing * 2
implicitHeight: Kirigami.Units.gridUnit + (Config.compactRoomList ? 0 : Kirigami.Units.largeSpacing * 2)
implicitWidth: visible ? implicitHeight : 0
Layout.fillHeight: true

View File

@@ -146,24 +146,34 @@ QQC2.ToolBar {
Layout.minimumHeight: Kirigami.Units.gridUnit * 2 - 2 // HACK: -2 here is to ensure the ChatBox and the UserInfo have the same height
QQC2.AbstractButton {
id: accountButton
Layout.preferredWidth: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing
Layout.preferredHeight: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing
Layout.leftMargin: Kirigami.Units.largeSpacing
TapHandler {
acceptedButtons: Qt.RightButton
acceptedDevices: PointerDevice.Mouse
onTapped: accountMenu.open()
acceptedButtons: Qt.RightButton | Qt.LeftButton
acceptedDevices: PointerDevice.Mouse
onTapped: (eventPoint, button) => {
// TODO Qt6 remove
if (!button) {
button = eventPoint.event.button;
}
if (button == Qt.RightButton) {
accountMenu.open();
} else {
pageStack.pushDialogLayer(Qt.resolvedUrl('qrc:/AccountEditorPage.qml'), {
connection: Controller.activeConnection
}, {
title: i18n("Account editor")
});
}
}
}
text: i18n("Edit this account")
onClicked: pageStack.pushDialogLayer(Qt.resolvedUrl('qrc:/AccountEditorPage.qml'), {
connection: Controller.activeConnection
}, {
title: i18n("Account editor")
});
contentItem: KirigamiComponents.Avatar {
readonly property string mediaId: Controller.activeConnection.localUser.avatarMediaId

View File

@@ -19,25 +19,17 @@ Kirigami.Page {
/// Not readonly because of the separate window view.
property NeoChatRoom currentRoom: RoomManager.currentRoom
property bool loading: !root.currentRoom || (root.currentRoom.timelineSize === 0 && !root.currentRoom.allHistoryLoaded)
/// Used to determine if scrolling to the bottom should mark the message as unread
property bool hasScrolledUpBefore: false;
/// Disable cancel shortcut. Used by the separate window since it provides its own cancel implementation.
property bool disableCancelShortcut: false
title: currentRoom.displayName
title: root.currentRoom.displayName
focus: true
padding: 0
KeyNavigation.left: pageStack.get(0)
onCurrentRoomChanged: {
if (!timelineViewLoader.item) {
return
}
applicationWindow().hoverLinkIndicator.text = "";
timelineViewLoader.item.positionViewAtBeginning();
hasScrolledUpBefore = false;
if (!Kirigami.Settings.isMobile && chatBoxLoader.item) {
chatBoxLoader.item.forceActiveFocus();
}
@@ -66,7 +58,7 @@ Kirigami.Page {
Loader {
id: timelineViewLoader
anchors.fill: parent
active: currentRoom && !currentRoom.isInvite && !root.loading
active: root.currentRoom && !root.currentRoom.isInvite && !root.loading
sourceComponent: TimelineView {
id: timelineView
currentRoom: root.currentRoom
@@ -80,7 +72,7 @@ Kirigami.Page {
Loader {
id: invitationLoader
active: currentRoom && currentRoom.isInvite
active: root.currentRoom && root.currentRoom.isInvite
anchors.centerIn: parent
sourceComponent: InvitationView {
currentRoom: root.currentRoom
@@ -106,7 +98,7 @@ Kirigami.Page {
sourceComponent: ChatBox {
id: chatBox
width: parent.width
currentRoom: currentRoom
currentRoom: root.currentRoom
onMessageSent: {
if (!timelineViewLoader.item.atYEnd) {
timelineViewLoader.item.goToLastMessage();
@@ -140,9 +132,9 @@ Kirigami.Page {
Shortcut {
sequence: StandardKey.Cancel
onActivated: {
if (!timelineViewLoader.item.atYEnd || currentRoom.hasUnreadMessages) {
if (!timelineViewLoader.item.atYEnd || root.currentRoom.hasUnreadMessages) {
goToLastMessage();
currentRoom.markAllMessagesAsRead();
root.currentRoom.markAllMessagesAsRead();
} else {
applicationWindow().pageStack.get(0).forceActiveFocus();
}

View File

@@ -156,8 +156,7 @@ Kirigami.OverlayDrawer {
Layout.fillWidth: true
onClicked: {
applicationWindow().pageStack.layers.push("qrc:/DevtoolsPage.qml", {room: room}, {title: i18n("Developer Tools")})
roomDrawer.close();
applicationWindow().pageStack.pushDialogLayer("qrc:/DevtoolsPage.qml", {room: room}, {title: i18n("Developer Tools")})
}
}
@@ -229,8 +228,7 @@ Kirigami.OverlayDrawer {
icon.name: "list-add-user"
onClicked: {
applicationWindow().pageStack.layers.push("qrc:/InviteUserPage.qml", {room: roomDrawer.room})
roomDrawer.close();
applicationWindow().pageStack.pushDialogLayer("qrc:/InviteUserPage.qml", {room: roomDrawer.room}, {title: i18nc("@title", "Invite a User")})
}
QQC2.ToolTip.text: i18n("Invite user to room")

View File

@@ -15,7 +15,7 @@ MobileForm.AbstractFormDelegate {
id: deviceDelegate
required property string id
required property int lastTimestamp
required property string timestamp
required property string displayName
property bool editDeviceName: false
@@ -49,7 +49,7 @@ MobileForm.AbstractFormDelegate {
QQC2.Label {
Layout.fillWidth: true
text: deviceDelegate.id + ", Last activity: " + (new Date(deviceDelegate.lastTimestamp)).toLocaleString(Qt.locale(), Locale.ShortFormat)
text: i18nc("@label", "%1, Last activity: %2", deviceDelegate.id, deviceDelegate.timestamp)
color: Kirigami.Theme.disabledTextColor
font: Kirigami.Theme.smallFont
elide: Text.ElideRight
@@ -131,4 +131,4 @@ MobileForm.AbstractFormDelegate {
}
}
}
}
}

View File

@@ -30,7 +30,7 @@ Kirigami.ScrollablePage {
MobileForm.FormCheckDelegate {
id: closeDelegate
text: i18n("Close to system tray")
text: i18n("Show in System Tray")
checked: Config.systemTray
visible: Controller.supportSystemTray
enabled: !Config.isSystemTrayImmutable

View File

@@ -242,7 +242,6 @@ Kirigami.ApplicationWindow {
function onInitiated() {
if (Controller.accountCount === 0) {
console.warn("9")
pageStack.replace("qrc:/WelcomePage.qml", {});
} else if (!roomListLoaded) {
pageStack.replace(roomListComponent, {

View File

@@ -74,7 +74,7 @@
<file alias="RoomDrawer.qml">qml/Panel/RoomDrawer.qml</file>
<file alias="DirectChatDrawerHeader.qml">qml/Panel/DirectChatDrawerHeader.qml</file>
<file alias="GroupChatDrawerHeader.qml">qml/Panel/GroupChatDrawerHeader.qml</file>
<file alias="UserDetailDialog.qml">@CMAKE_CURRENT_BINARY_DIR@/qml/Dialog/UserDetailDialog.qml</file>
<file alias="UserDetailDialog.qml">qml/Dialog/UserDetailDialog.qml</file>
<file alias="CreateRoomDialog.qml">qml/Dialog/CreateRoomDialog.qml</file>
<file alias="CreateSpaceDialog.qml">qml/Dialog/CreateSpaceDialog.qml</file>
<file alias="EmojiDialog.qml">qml/Dialog/EmojiDialog.qml</file>

View File

@@ -4,8 +4,9 @@
#include "texthandler.h"
#include <QDebug>
#include <QUrl>
#include <QGuiApplication>
#include <QStringLiteral>
#include <QUrl>
#include <Quotient/events/roommessageevent.h>
#include <Quotient/util.h>
@@ -15,6 +16,8 @@
#include <Kirigami/PlatformTheme>
#include "utils.h"
static const QStringList allowedTags = {
QStringLiteral("font"), QStringLiteral("del"), QStringLiteral("h1"), QStringLiteral("h2"), QStringLiteral("h3"), QStringLiteral("h4"),
QStringLiteral("h5"), QStringLiteral("h6"), QStringLiteral("blockquote"), QStringLiteral("p"), QStringLiteral("a"), QStringLiteral("ul"),
@@ -135,9 +138,9 @@ QString TextHandler::handleRecieveRichText(Qt::TextFormat inputFormat, const Neo
if (event != nullptr) {
auto e = eventCast<const Quotient::RoomMessageEvent>(event);
if (e->msgtype() == Quotient::MessageEventType::Emote) {
auto author = static_cast<NeoChatUser *>(room->user(e->senderId()));
auto author = room->user(e->senderId());
QString emoteString = QStringLiteral("* <a href=\"https://matrix.to/#/") + e->senderId() + QStringLiteral("\" style=\"color:")
+ author->color().name() + QStringLiteral("\">") + author->displayname(room) + QStringLiteral("</a> ");
+ Utils::getUserColor(author->hueF()).name() + QStringLiteral("\">") + author->displayname(room) + QStringLiteral("</a> ");
if (outputString.startsWith(QStringLiteral("<p>"))) {
outputString.insert(3, emoteString);
} else {

23
src/utils.h Normal file
View File

@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include <QColor>
#include <QGuiApplication>
#include <QPalette>
namespace Utils
{
/**
* @brief Get a color for a user from a hueF value.
*
* The lightness of the color will be modified depending on the current palette in
* order to maintain contrast.
*/
inline QColor getUserColor(qreal hueF)
{
const auto lightness = static_cast<QGuiApplication *>(QGuiApplication::instance())->palette().color(QPalette::Active, QPalette::Window).lightnessF();
// https://github.com/quotient-im/libQuotient/wiki/User-color-coding-standard-draft-proposal
return QColor::fromHslF(hueF, 1, -0.7 * lightness + 0.9, 1);
}
}