Compare commits

...

54 Commits

Author SHA1 Message Date
Fushan Wen
a61976ae91 Allow to drag image from image delegate 2023-07-22 23:54:11 +08: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
Laurent Montel
3855922ad2 Add explicit moc includes to sources for moc-covered headers 2023-07-16 11:08:19 +02:00
l10n daemon script
89a094ce8f GIT_SILENT Sync po/docbooks with svn 2023-07-16 01:58:12 +00:00
l10n daemon script
d4421aaecf GIT_SILENT made messages (after extraction) 2023-07-16 00:48:59 +00:00
Tobias Fella
80b451c27b Only create consent sheet if required 2023-07-15 20:52:03 +00:00
Tobias Fella
32ff22ba89 Fix most compilation warnings 2023-07-15 20:39:38 +00:00
Tobias Fella
b990e3fbcb Stop using the AccountRegistry Singleton 2023-07-15 20:31:24 +00:00
Laurent Montel
8d1baab89f Add missing explicit keyword 2023-07-15 16:01:19 +00:00
Laurent Montel
62ecbc8d6b Includes is already defined in header 2023-07-15 16:58:34 +02:00
Laurent Montel
0e1c7f8c47 Fix qml Audio/Video support in qt6 too.
in Audio qt6 we don't have autoload feature but we disable it in qt5 => it seems ok

In Video qml qt6 we don't have flushMode.
2023-07-15 12:51:13 +00:00
Tobias Fella
c963966f1d Enforce namespaced includes for libQuotient 2023-07-15 14:05:11 +02:00
Tobias Fella
8db2526153 Improve DevicesPage and DevicesModel
- Split the list into sections for "this devices", "verified devices", "unverified devices", and "devices without encryption support"
- Sort the lists by last activity
2023-07-15 13:31:28 +02:00
Tobias Fella
7587a1a418 Drop ifdefs for libQuotient 0.7 2023-07-15 09:18:05 +02:00
Tobias Fella
294812956a Require libQuotient 0.7 2023-07-15 00:53:43 +02:00
Tobias Fella
d01aac6fa3 Remove libQuotient 0.6 based CI 2023-07-15 00:53:19 +02:00
l10n daemon script
5cda1ca3ce GIT_SILENT Sync po/docbooks with svn 2023-07-14 02:01:40 +00:00
Laurent Montel
bf26a463e0 Ignore build* 2023-07-13 07:13:47 +02:00
l10n daemon script
30180c1ac8 GIT_SILENT Sync po/docbooks with svn 2023-07-13 01:56:46 +00:00
l10n daemon script
41c296061c 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-07-13 01:45:17 +00:00
l10n daemon script
461420af0a GIT_SILENT made messages (after extraction) 2023-07-13 00:49:09 +00:00
Laurent Montel
052a7ed571 Ignore src/res.generated.qrc file 2023-07-12 22:28:08 +00:00
Laurent Montel
98e86f887b Adapt qml for qt5 and qt6 2023-07-12 22:28:08 +00:00
Laurent Montel
63a17b5985 Remove duplicate headers between header/cpp 2023-07-12 22:27:39 +00:00
Tobias Fella
025b367a7e Always enable E2EE in libQuotient
It's off-by-default in libQuotient 0.8
2023-07-12 22:10:37 +00:00
Laurent Montel
a8536b0634 Remove extra ; 2023-07-12 18:16:55 +00:00
Laurent Montel
0c43d3eeee Fix equal operator deprecated in c++20 2023-07-12 18:16:19 +00:00
Laurent Montel
bf1614256b Qt::AA_EnableHighDpiScaling is by default in qt6 2023-07-12 18:14:28 +00:00
Laurent Montel
99614d1b27 Use QString::isEmpty here 2023-07-12 14:27:24 +00:00
Laurent Montel
e1e57ef27a Use qt new header 2023-07-12 13:15:58 +00:00
Laurent Montel
6d45d126f8 Add explicit moc includes to sources for moc-covered headers
Add missing #pragma once + missing include

* speeds up incremental builds as changes to a header will not always
  need the full mocs_compilation.cpp for all the target's headers rebuild,
  while having a moc file sourced into a source file only adds minor
  extra costs, due to small own code and the used headers usually
  already covered by the source file, being for the same class/struct
* seems to not slow down clean builds, due to empty mocs_compilation.cpp
  resulting in those quickly processed, while the minor extra cost of the
  sourced moc files does not outweigh that in summary.
  Measured times actually improved by some percent points.
  (ideally CMake would just skip empty mocs_compilation.cpp & its object
  file one day)
* enables compiler to see all methods of a class in same compilation unit
  to do some sanity checks
* potentially more inlining in general, due to more in the compilation unit
* allows to keep using more forward declarations in the header, as with the
  moc code being sourced into the cpp file there definitions can be ensured
  and often are already for the needs of the normal class methods
2023-07-12 13:15:19 +00:00
Laurent Montel
732b43cbd6 Use const'ref 2023-07-12 11:55:50 +00:00
Laurent Montel
a5da17b000 Use explicit keyword 2023-07-12 11:49:12 +00:00
Laurent Montel
10a294f99e Fix show headers in qtc6 2023-07-12 13:17:30 +02:00
183 changed files with 12818 additions and 11805 deletions

3
.gitignore vendored
View File

@@ -1,4 +1,4 @@
build
build*
.clang-format
.DS_Store
.kdev4/
@@ -11,3 +11,4 @@ kate.project.ctags.*
.flatpak-builder/
.idea/
cmake-build-*
src/res.generated.qrc

View File

@@ -5,10 +5,10 @@ include:
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/reuse-lint.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android.yml
# - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android-qt6.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux.yml
# - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux-qt6.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/windows.yml
# - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/windows-qt6.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd.yml
# - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd.yml
# - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd-qt6.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/flatpak.yml

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,7 +8,7 @@ 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_MINOR "11")
set(RELEASE_SERVICE_VERSION_MICRO "70")
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
@@ -48,6 +48,30 @@ if(NEOCHAT_FLATPAK)
include(cmake/Flatpak.cmake)
endif()
if(QT_MAJOR_VERSION STREQUAL "6")
set(BASICLISTITEM_BOLD "font.bold")
set(OVERLAYSHEET_OPEN "onOpened")
set(QTQUICK_MODULE_QML_VERSION "")
set(QTLOCATION_MODULE_QML_VERSION "")
set(QTMULTIMEDIA_MODULE_QML_VERSION "")
set(QTMULTIMEDIA_AUDIO "MediaPlayer")
# in Audio qt6 we don't have it but we disable it in qt5 => it seems ok
set(QTMULTIMEDIA_AUDIO_AUTOLOAD "")
# In Video qml qt6 we don't have it.
set(QTMULTIMEDIA_VIDEO_FLUSHMODE "")
else()
set(BASICLISTITEM_BOLD "bold")
set(OVERLAYSHEET_OPEN "onSheetOpenChanged")
set(QTQUICK_MODULE_QML_VERSION "2.15")
set(QTLOCATION_MODULE_QML_VERSION "5.15")
set(QTMULTIMEDIA_MODULE_QML_VERSION "5.15")
set(QTMULTIMEDIA_AUDIO "Audio")
set(QTMULTIMEDIA_AUDIO_AUTOLOAD "autoLoad: false")
set(QTMULTIMEDIA_VIDEO_FLUSHMODE "flushMode: VideoOutput.FirstFrame")
endif()
set(QUOTIENT_FORCE_NAMESPACED_INCLUDES TRUE)
ecm_setup_version(${PROJECT_VERSION}
VARIABLE_PREFIX NEOCHAT
VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/neochat-version.h
@@ -69,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
@@ -98,7 +116,7 @@ if(QT_MAJOR_VERSION STREQUAL "6" AND NOT ANDROID AND NOT WIN32)
set(QUOTIENT_SUFFIX "Qt6")
endif()
find_package(Quotient${QUOTIENT_SUFFIX} 0.6)
find_package(Quotient${QUOTIENT_SUFFIX} 0.7)
set_package_properties(Quotient${QUOTIENT_SUFFIX} PROPERTIES
TYPE REQUIRED
DESCRIPTION "Qt wrapper around Matrix API"
@@ -136,10 +154,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)
@@ -155,9 +169,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

@@ -7,9 +7,9 @@
#include "neochatroom.h"
#include <connection.h>
#include <quotient_common.h>
#include <syncdata.h>
#include <Quotient/connection.h>
#include <Quotient/quotient_common.h>
#include <Quotient/syncdata.h>
using namespace Quotient;

View File

@@ -6,9 +6,9 @@
#include "texthandler.h"
#include <Quotient/quotient_common.h>
#include <Quotient/syncdata.h>
#include <qnamespace.h>
#include <quotient_common.h>
#include <syncdata.h>
using namespace Quotient;
@@ -71,7 +71,6 @@ private Q_SLOTS:
void linkPreviewsReject();
};
#ifdef QUOTIENT_07
void TextHandlerTest::initTestCase()
{
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
@@ -204,7 +203,6 @@ void TextHandlerTest::initTestCase()
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, json.object());
room->update(std::move(roomData));
}
#endif
void TextHandlerTest::allowedAttributes()
{
@@ -479,7 +477,6 @@ void TextHandlerTest::receiveRichtextIn()
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
}
#ifdef QUOTIENT_07
void TextHandlerTest::receiveRichMxcUrl()
{
const QString testInputString = QStringLiteral(
@@ -497,7 +494,6 @@ void TextHandlerTest::receiveRichMxcUrl()
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText, room, room->messageEvents().at(0).get()), testOutputString);
}
#endif
/**
* For when your rich input string has a plain text url left in.

View File

@@ -18,6 +18,7 @@
<name xml:lang="de">NeoChat</name>
<name xml:lang="el">NeoChat</name>
<name xml:lang="en-GB">NeoChat</name>
<name xml:lang="eo">NeoChat</name>
<name xml:lang="es">NeoChat</name>
<name xml:lang="eu">NeoChat</name>
<name xml:lang="fi">NeoChat</name>
@@ -54,6 +55,7 @@
<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>
@@ -88,6 +90,7 @@ to provide a convergent experience across multiple platforms.</p>
<p xml:lang="ca">El NeoChat és un client de Matrix, el protocol descentralitzat de comunicacions de missatgeria instantània. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics. Fa servir els Frameworks de KDE i, sobretot, el Kirigami per a proporcionar una experiència convergent a través de diverses plataformes.</p>
<p xml:lang="ca-valencia">NeoChat és un client de Matrix, el protocol descentralitzat de comunicacions de missatgeria instantània. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics. Utilitza els Frameworks de KDE i, sobretot, Kirigami per a proporcionar una experiència convergent a través de diverses plataformes.</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="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>
@@ -220,6 +223,7 @@ to provide a convergent experience across multiple platforms.</p>
<developer_name xml:lang="de">Die KDE-Gemeinschaft</developer_name>
<developer_name xml:lang="el">Η Κοινότητα του KDE</developer_name>
<developer_name xml:lang="en-GB">The KDE Community</developer_name>
<developer_name xml:lang="eo">La KDE-Komunumo</developer_name>
<developer_name xml:lang="es">La comunidad KDE</developer_name>
<developer_name xml:lang="eu">KDE komunitatea</developer_name>
<developer_name xml:lang="fi">KDE-yhteisö</developer_name>
@@ -262,9 +266,12 @@ to provide a convergent experience across multiple platforms.</p>
<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="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="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="ka">მთავარი ხედი სურათების სიით, ჩატით და ოთახის ინფორმაციით</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="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="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>
<value key="KDE::windows_store::screenshots::1::caption" xml:lang="x-test">xxMain view with room list, chat, and room informationxx</value>
@@ -273,9 +280,12 @@ to provide a convergent experience across multiple platforms.</p>
<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="es">Pantalla de inicio de sesión</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="ka">შესვლის ეკრანი</value>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="nl">Aanmeldscherm</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="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>
<value key="KDE::windows_store::screenshots::2::caption" xml:lang="x-test">xxLogin screenxx</value>

View File

@@ -11,6 +11,7 @@ Name[cs]=NeoChat
Name[de]=NeoChat
Name[el]=NeoChat
Name[en_GB]=NeoChat
Name[eo]=NeoChat
Name[es]=NeoChat
Name[eu]=NeoChat
Name[fi]=NeoChat
@@ -49,6 +50,7 @@ GenericName[cs]=Klient protokolu Matrix
GenericName[de]=Matrix-Programm
GenericName[el]=Εφαρμογή του Matrix
GenericName[en_GB]=Matrix Client
GenericName[eo]=Matrix-Kliento
GenericName[es]=Cliente para Matrix
GenericName[eu]=Matrix bezeroa
GenericName[fi]=Matrix-asiakas
@@ -86,6 +88,7 @@ Comment[ca@valencia]=Client per al protocol Matrix
Comment[de]=Programm für das Matrix-Protokoll
Comment[el]=Πελάτης για το πρωτόκολλο Matrix
Comment[en_GB]=Client for the Matrix protocol
Comment[eo]=Kliento por la Matrix-protokolo
Comment[es]=Cliente para el protocolo Matrix
Comment[eu]=Matrix protokolorako bezeroa
Comment[fi]=Asiakas Matrix-yhteyskäytännölle

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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

@@ -3,63 +3,130 @@
# SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org>
# SPDX-License-Identifier: BSD-2-Clause
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)
configure_file(qml/Component/Timeline/OsmLocationPlugin.qml ${CMAKE_CURRENT_BINARY_DIR}/qml/Component/Timeline/OsmLocationPlugin.qml)
configure_file(res.qrc ${CMAKE_CURRENT_SOURCE_DIR}/res.generated.qrc)
add_library(neochat STATIC
controller.cpp
controller.h
actionshandler.cpp
actionshandler.h
models/emojimodel.cpp
models/emojimodel.h
emojitones.cpp
emojitones.h
models/customemojimodel.cpp
models/customemojimodel.h
clipboard.cpp
clipboard.h
matriximageprovider.cpp
matriximageprovider.h
models/messageeventmodel.cpp
models/messageeventmodel.h
models/messagefiltermodel.cpp
models/messagefiltermodel.h
models/roomlistmodel.cpp
models/roomlistmodel.h
models/sortfilterspacelistmodel.cpp
models/sortfilterspacelistmodel.h
models/accountemoticonmodel.cpp
models/accountemoticonmodel.h
spacehierarchycache.cpp
spacehierarchycache.h
roommanager.cpp
roommanager.h
neochatroom.cpp
neochatroom.h
neochatuser.cpp
neochatuser.h
models/userlistmodel.cpp
models/userlistmodel.h
models/userfiltermodel.cpp
models/userfiltermodel.h
models/publicroomlistmodel.cpp
models/publicroomlistmodel.h
models/userdirectorylistmodel.cpp
models/userdirectorylistmodel.h
models/pushrulemodel.cpp
models/pushrulemodel.h
models/emoticonfiltermodel.cpp
models/emoticonfiltermodel.h
notificationsmanager.cpp
notificationsmanager.h
models/sortfilterroomlistmodel.cpp
models/sortfilterroomlistmodel.h
chatdocumenthandler.cpp
chatdocumenthandler.h
models/devicesmodel.cpp
models/devicesmodel.h
models/devicesproxymodel.cpp
filetypesingleton.cpp
filetypesingleton.h
login.cpp
login.h
models/webshortcutmodel.cpp
models/webshortcutmodel.h
blurhash.cpp
blurhash.h
blurhashimageprovider.cpp
blurhashimageprovider.h
models/collapsestateproxymodel.cpp
models/collapsestateproxymodel.h
models/mediamessagefiltermodel.cpp
models/mediamessagefiltermodel.h
urlhelper.cpp
urlhelper.h
windowcontroller.cpp
windowcontroller.h
linkpreviewer.cpp
linkpreviewer.h
models/completionmodel.cpp
models/completionmodel.h
models/completionproxymodel.cpp
models/completionproxymodel.h
models/actionsmodel.cpp
models/actionsmodel.h
models/serverlistmodel.cpp
models/serverlistmodel.h
models/statemodel.cpp
models/statemodel.h
models/statefiltermodel.cpp
models/statefiltermodel.h
filetransferpseudojob.cpp
filetransferpseudojob.h
models/searchmodel.cpp
models/searchmodel.h
texthandler.cpp
texthandler.h
logger.cpp
logger.h
models/stickermodel.cpp
models/stickermodel.h
models/imagepacksmodel.cpp
models/imagepacksmodel.h
events/imagepackevent.cpp
events/imagepackevent.h
events/joinrulesevent.cpp
events/stickerevent.cpp
events/joinrulesevent.h
models/reactionmodel.cpp
models/reactionmodel.h
delegatesizehelper.cpp
delegatesizehelper.h
models/livelocationsmodel.cpp
models/livelocationsmodel.h
models/locationsmodel.cpp
models/locationsmodel.h
locationhelper.cpp
locationhelper.h
events/pollevent.cpp
pollhandler.cpp
)
ecm_qt_declare_logging_category(neochat
@@ -73,7 +140,7 @@ ecm_qt_declare_logging_category(neochat
add_executable(neochat-app
main.cpp
res.qrc
${CMAKE_CURRENT_SOURCE_DIR}/res.generated.qrc
)
target_include_directories(neochat-app PRIVATE ${CMAKE_BINARY_DIR})
@@ -82,24 +149,16 @@ target_link_libraries(neochat-app PRIVATE
neochat
)
if(Quotient${QUOTIENT_SUFFIX}_VERSION_MINOR GREATER 6)
target_compile_definitions(neochat PUBLIC QUOTIENT_07)
target_sources(neochat PRIVATE events/pollevent.cpp pollhandler.cpp)
else()
target_compile_definitions(neochat PUBLIC QUOTIENT_VERSION=\"${Quotient${QUOTIENT_SUFFIX}_VERSION}\")
target_sources(neochat PRIVATE neochataccountregistry.cpp)
endif()
ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png)
target_sources(neochat-app PRIVATE ${NEOCHAT_ICON})
if(NOT ANDROID)
target_sources(neochat PRIVATE colorschemer.cpp)
target_sources(neochat PRIVATE colorschemer.cpp colorschemer.h)
if (NOT WIN32 AND NOT APPLE)
target_sources(neochat PRIVATE trayicon_sni.cpp)
target_sources(neochat PRIVATE trayicon_sni.cpp trayicon_sni.h)
else()
target_sources(neochat PRIVATE trayicon.cpp)
target_sources(neochat PRIVATE trayicon.cpp trayicon.h)
endif()
target_link_libraries(neochat PUBLIC KF${QT_MAJOR_VERSION}::ConfigWidgets KF${QT_MAJOR_VERSION}::WindowSystem)
target_compile_definitions(neochat PUBLIC -DHAVE_COLORSCHEME)
@@ -116,7 +175,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

@@ -3,8 +3,8 @@
#include "actionshandler.h"
#include <csapi/joining.h>
#include <events/roommemberevent.h>
#include <Quotient/csapi/joining.h>
#include <Quotient/events/roommemberevent.h>
#include <cmark.h>
@@ -14,7 +14,6 @@
#include "models/actionsmodel.h"
#include "models/customemojimodel.h"
#include "neochatconfig.h"
#include "neochatroom.h"
#include "texthandler.h"
using namespace Quotient;
@@ -183,3 +182,5 @@ void ActionsHandler::checkEffects(const QString &text)
Q_EMIT showEffect(*effect);
}
}
#include "moc_actionshandler.cpp"

View File

@@ -5,7 +5,7 @@
#include <QObject>
#include <events/roommessageevent.h>
#include <Quotient/events/roommessageevent.h>
#include "neochatroom.h"
@@ -47,7 +47,7 @@ public:
Q_SIGNALS:
void roomChanged();
void showEffect(QString effect);
void showEffect(const QString &effect);
public Q_SLOTS:

View File

@@ -14,8 +14,6 @@
#include <Sonnet/BackgroundChecker>
#include <Sonnet/Settings>
#include "neochatroom.h"
class SyntaxHighlighter : public QSyntaxHighlighter
{
public:
@@ -355,3 +353,5 @@ void ChatDocumentHandler::setErrorColor(const QColor &color)
m_highlighter->rehighlight();
Q_EMIT errorColorChanged();
}
#include "moc_chatdocumenthandler.cpp"

View File

@@ -79,3 +79,5 @@ void Clipboard::setImage(const QUrl &url)
}
}
}
#include "moc_clipboard.cpp"

View File

@@ -44,3 +44,5 @@ QString ColorSchemer::nameForIndex(int index) const
{
return c->model()->data(c->model()->index(index, 0), Qt::DisplayRole).toString();
}
#include "moc_colorschemer.cpp"

View File

@@ -28,7 +28,7 @@ class ColorSchemer : public QObject
Q_PROPERTY(QAbstractItemModel *model READ model CONSTANT)
public:
ColorSchemer(QObject *parent = nullptr);
explicit ColorSchemer(QObject *parent = nullptr);
~ColorSchemer();
QAbstractItemModel *model() const;

View File

@@ -3,7 +3,6 @@
// SPDX-License-Identifier: GPL-3.0-only
#include "controller.h"
#include "models/pushrulemodel.h"
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#include <qt5keychain/keychain.h>
@@ -32,22 +31,15 @@
#include <signal.h>
#ifdef QUOTIENT_07
#include "accountregistry.h"
#else
#include "neochataccountregistry.h"
#endif
#include <connection.h>
#include <csapi/content-repo.h>
#include <csapi/logout.h>
#include <csapi/profile.h>
#include <jobs/downloadfilejob.h>
#include <qt_connection_util.h>
#ifdef QUOTIENT_07
#include <eventstats.h>
#endif
#include <Quotient/accountregistry.h>
#include <Quotient/connection.h>
#include <Quotient/csapi/content-repo.h>
#include <Quotient/csapi/logout.h>
#include <Quotient/csapi/profile.h>
#include <Quotient/jobs/downloadfilejob.h>
#include <Quotient/qt_connection_util.h>
#include <Quotient/csapi/notifications.h>
#include <Quotient/eventstats.h>
#include "neochatconfig.h"
#include "neochatroom.h"
@@ -109,24 +101,18 @@ Controller::Controller(QObject *parent)
}
#endif
#ifdef QUOTIENT_07
connect(&Accounts, &AccountRegistry::accountCountChanged, this, &Controller::activeConnectionIndexChanged);
#else
connect(&AccountRegistry::instance(), &AccountRegistry::accountCountChanged, this, &Controller::activeConnectionIndexChanged);
#endif
connect(&m_accountRegistry, &AccountRegistry::accountCountChanged, this, &Controller::activeConnectionIndexChanged);
#ifdef QUOTIENT_07
static int oldAccountCount = 0;
connect(&Accounts, &AccountRegistry::accountCountChanged, this, [this]() {
if (Accounts.size() > oldAccountCount) {
auto connection = Accounts.accounts()[Accounts.size() - 1];
connect(&m_accountRegistry, &AccountRegistry::accountCountChanged, this, [this]() {
if (m_accountRegistry.size() > oldAccountCount) {
auto connection = m_accountRegistry.accounts()[m_accountRegistry.size() - 1];
connect(connection, &Connection::syncDone, this, [connection]() {
NotificationsManager::instance().handleNotifications(connection);
});
}
oldAccountCount = Accounts.size();
oldAccountCount = m_accountRegistry.size();
});
#endif
QTimer::singleShot(0, this, [this] {
m_pushRuleModel = new PushRuleModel;
@@ -161,18 +147,10 @@ void Controller::logout(Connection *conn, bool serverSideLogout)
job.start();
loop.exec();
#ifdef QUOTIENT_07
if (Accounts.count() > 1) {
#else
if (AccountRegistry::instance().count() > 1) {
#endif
if (m_accountRegistry.count() > 1) {
// Only set the connection if the the account being logged out is currently active
if (conn == activeConnection()) {
#ifdef QUOTIENT_07
setActiveConnection(Accounts.accounts()[0]);
#else
setActiveConnection(AccountRegistry::instance().accounts()[0]);
#endif
setActiveConnection(m_accountRegistry.accounts()[0]);
}
} else {
setActiveConnection(nullptr);
@@ -187,11 +165,7 @@ void Controller::addConnection(Connection *c)
{
Q_ASSERT_X(c, __FUNCTION__, "Attempt to add a null connection");
#ifdef QUOTIENT_07
Accounts.add(c);
#else
AccountRegistry::instance().add(c);
#endif
m_accountRegistry.add(c);
c->setLazyLoading(true);
@@ -206,7 +180,7 @@ void Controller::addConnection(Connection *c)
});
connect(c, &Connection::requestFailed, this, [this](BaseJob *job) {
if (job->error() == BaseJob::UserConsentRequiredError) {
if (job->error() == BaseJob::UserConsentRequired) {
Q_EMIT userConsentRequired(job->errorUrl());
}
});
@@ -221,15 +195,8 @@ void Controller::dropConnection(Connection *c)
{
Q_ASSERT_X(c, __FUNCTION__, "Attempt to drop a null connection");
#ifndef QUOTIENT_07
AccountRegistry::instance().drop(c);
#endif
Q_EMIT connectionDropped(c);
Q_EMIT accountCountChanged();
#ifndef QUOTIENT_07
c->deleteLater();
#endif
}
void Controller::invokeLogin()
@@ -280,7 +247,7 @@ void Controller::invokeLogin()
connect(connection, &Connection::networkError, this, [this](const QString &error, const QString &, int, int) {
Q_EMIT errorOccured(i18n("Network Error: %1", error));
});
connection->assumeIdentity(account.userId(), accessToken, account.deviceId());
connection->assumeIdentity(account.userId(), accessToken);
});
}
}
@@ -296,7 +263,7 @@ QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const Accoun
job->setKey(account.userId());
// Handling of errors
connect(job, &QKeychain::Job::finished, this, [this, &account, job]() {
connect(job, &QKeychain::Job::finished, this, [this, job]() {
if (job->error() == QKeychain::Error::NoError) {
return;
}
@@ -402,11 +369,7 @@ bool Controller::setAvatar(Connection *connection, const QUrl &avatarSource)
User *localUser = connection->user();
QString decoded = avatarSource.path();
if (decoded.isEmpty()) {
#ifdef QUOTIENT_07
connection->callApi<SetAvatarUrlJob>(localUser->id(), avatarSource);
#else
connection->callApi<SetAvatarUrlJob>(localUser->id(), QString());
#endif
return true;
}
if (QImageReader(decoded).read().isNull()) {
@@ -417,11 +380,7 @@ bool Controller::setAvatar(Connection *connection, const QUrl &avatarSource)
}
NeochatChangePasswordJob::NeochatChangePasswordJob(const QString &newPassword, bool logoutDevices, const Omittable<QJsonObject> &auth)
#ifdef QUOTIENT_07
: BaseJob(HttpVerb::Post, QStringLiteral("ChangePasswordJob"), "/_matrix/client/r0/account/password")
#else
: BaseJob(HttpVerb::Post, QStringLiteral("ChangePasswordJob"), QStringLiteral("/_matrix/client/r0/account/password"))
#endif
{
QJsonObject _data;
addParam<>(_data, QStringLiteral("new_password"), newPassword);
@@ -432,11 +391,7 @@ NeochatChangePasswordJob::NeochatChangePasswordJob(const QString &newPassword, b
int Controller::accountCount() const
{
#ifdef QUOTIENT_07
return Accounts.count();
#else
return AccountRegistry::instance().count();
#endif
return m_accountRegistry.count();
}
void Controller::setQuitOnLastWindowClosed()
@@ -523,11 +478,7 @@ void Controller::saveWindowGeometry()
}
NeochatDeleteDeviceJob::NeochatDeleteDeviceJob(const QString &deviceId, const Omittable<QJsonObject> &auth)
#ifdef QUOTIENT_07
: Quotient::BaseJob(HttpVerb::Delete, QStringLiteral("DeleteDeviceJob"), QStringLiteral("/_matrix/client/r0/devices/%1").arg(deviceId).toLatin1())
#else
: Quotient::BaseJob(HttpVerb::Delete, QStringLiteral("DeleteDeviceJob"), QStringLiteral("/_matrix/client/r0/devices/%1").arg(deviceId))
#endif
{
QJsonObject _data;
addParam<IfNotEmpty>(_data, QStringLiteral("auth"), auth);
@@ -633,11 +584,7 @@ bool Controller::hasWindowSystem() const
bool Controller::encryptionSupported() const
{
#ifdef QUOTIENT_07
return Quotient::encryptionSupported();
#else
return false;
#endif
}
void Controller::forceRefreshTextDocument(QQuickTextDocument *textDocument, QQuickItem *item)
@@ -678,29 +625,16 @@ void Controller::setApplicationProxy()
int Controller::activeConnectionIndex() const
{
#ifdef QUOTIENT_07
auto result = std::find_if(Accounts.accounts().begin(), Accounts.accounts().end(), [this](const auto &it) {
auto result = std::find_if(m_accountRegistry.accounts().begin(), m_accountRegistry.accounts().end(), [this](const auto &it) {
return it == m_connection;
});
return result - Accounts.accounts().begin();
#else
for (int i = 0; i < AccountRegistry::instance().rowCount(); i++) {
if (AccountRegistry::instance().data(AccountRegistry::instance().index(i, 0), AccountRegistry::UserIdRole).toString() == m_connection->userId()) {
return i;
}
}
return 0;
#endif
return result - m_accountRegistry.accounts().begin();
}
int Controller::quotientMinorVersion() const
{
// TODO libQuotient 0.7: Replace with version function from libQuotient
#ifdef QUOTIENT_07
// TODO libQuotient 0.7: Replace with version function from libQuotient
return 7;
#else
return 6;
#endif
}
bool Controller::isFlatpak() const
@@ -746,3 +680,10 @@ QVariantList Controller::getSupportedRoomVersions(Quotient::Connection *connecti
return supportedRoomVersions;
}
AccountRegistry &Controller::accounts()
{
return m_accountRegistry;
}
#include "moc_controller.cpp"

View File

@@ -9,8 +9,9 @@
#include <KFormat>
#include <jobs/basejob.h>
#include <settings.h>
#include <Quotient/accountregistry.h>
#include <Quotient/jobs/basejob.h>
#include <Quotient/settings.h>
class NeoChatRoom;
class NeoChatUser;
@@ -116,7 +117,7 @@ public:
Wrong, /**< The current password entered was wrong. */
Other, /**< An unknown problem occurred. */
};
Q_ENUM(PasswordStatus);
Q_ENUM(PasswordStatus)
static Controller &instance();
@@ -229,6 +230,8 @@ public:
Q_INVOKABLE QVariantList getSupportedRoomVersions(Quotient::Connection *connection);
Quotient::AccountRegistry &accounts();
private:
explicit Controller(QObject *parent = nullptr);
@@ -245,6 +248,7 @@ private:
bool hasWindowSystem() const;
QPointer<PushRuleModel> m_pushRuleModel;
Quotient::AccountRegistry m_accountRegistry;
private Q_SLOTS:
void invokeLogin();

View File

@@ -151,3 +151,5 @@ qreal DelegateSizeHelper::currentWidth() const
return std::ceil(std::min(absoluteWidth, m_maxWidth));
}
}
#include "moc_delegatesizehelper.cpp"

View File

@@ -77,7 +77,7 @@ class DelegateSizeHelper : public QObject
Q_PROPERTY(qreal currentWidth READ currentWidth NOTIFY currentWidthChanged)
public:
DelegateSizeHelper(QObject *parent = nullptr);
explicit DelegateSizeHelper(QObject *parent = nullptr);
qreal parentWidth() const;
void setParentWidth(qreal parentWidth);

View File

@@ -11,11 +11,7 @@ ImagePackEventContent::ImagePackEventContent(const QJsonObject &json)
if (json.contains(QStringLiteral("pack"))) {
pack = ImagePackEventContent::Pack{
fromJson<Omittable<QString>>(json["pack"].toObject()["display_name"]),
#ifdef QUOTIENT_07
fromJson<Omittable<QUrl>>(json["pack"].toObject()["avatar_url"]),
#else
QUrl(),
#endif
fromJson<Omittable<QStringList>>(json["pack"].toObject()["usage"]),
fromJson<Omittable<QString>>(json["pack"].toObject()["attribution"]),
};
@@ -27,21 +23,13 @@ ImagePackEventContent::ImagePackEventContent(const QJsonObject &json)
for (const auto &k : keys) {
Omittable<EventContent::ImageInfo> info;
if (json["images"][k].toObject().contains(QStringLiteral("info"))) {
#ifdef QUOTIENT_07
info = EventContent::ImageInfo(QUrl(json["images"][k]["url"].toString()), json["images"][k]["info"].toObject(), k);
#else
info = EventContent::ImageInfo(QUrl(json["images"][k]["url"].toString()), json["images"][k].toObject(), k);
#endif
} else {
info = none;
}
images += ImagePackImage{
k,
#ifdef QUOTIENT_07
fromJson<QUrl>(json["images"][k]["url"].toString()),
#else
QUrl(),
#endif
fromJson<Omittable<QString>>(json["images"][k]["body"]),
info,
fromJson<Omittable<QStringList>>(json["images"][k]["usage"]),

View File

@@ -4,8 +4,8 @@
#pragma once
#include <QVector>
#include <events/eventcontent.h>
#include <events/stateevent.h>
#include <Quotient/events/eventcontent.h>
#include <Quotient/events/stateevent.h>
namespace Quotient
{
@@ -83,23 +83,11 @@ public:
*
* @sa Quotient::StateEvent, ImagePackEventContent
*/
#ifdef QUOTIENT_07
class ImagePackEvent : public KeyedStateEventBase<ImagePackEvent, ImagePackEventContent>
#else
class ImagePackEvent : public StateEvent<ImagePackEventContent>
#endif
{
public:
#ifdef QUOTIENT_07
QUO_EVENT(ImagePackEvent, "im.ponies.room_emotes")
using KeyedStateEventBase::KeyedStateEventBase;
#else
DEFINE_EVENT_TYPEID("im.ponies.room_emotes", ImagePackEvent)
explicit ImagePackEvent(const QJsonObject &obj)
: StateEvent(typeId(), obj)
{
}
#endif
};
REGISTER_EVENT_TYPE(ImagePackEvent)

View File

@@ -3,7 +3,7 @@
#pragma once
#include <events/stateevent.h>
#include <Quotient/events/stateevent.h>
namespace Quotient
{
@@ -14,25 +14,13 @@ namespace Quotient
*
* @sa Quotient::StateEvent
*/
#ifdef QUOTIENT_07
class JoinRulesEvent : public StateEvent
#else
class JoinRulesEvent : public StateEventBase
#endif
{
public:
#ifdef QUOTIENT_07
QUO_EVENT(JoinRulesEvent, "m.room.join_rules")
#else
DEFINE_EVENT_TYPEID("m.room.join_rules", JoinRulesEvent)
#endif
explicit JoinRulesEvent(const QJsonObject &obj)
#ifdef QUOTIENT_07
: StateEvent(obj)
#else
: StateEventBase(typeId(), obj)
#endif
{
}

View File

@@ -3,8 +3,7 @@
#pragma once
#include <events/eventcontent.h>
#include <events/roomevent.h>
#include <Quotient/events/roomevent.h>
namespace Quotient
{

View File

@@ -1,35 +0,0 @@
// SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
// SPDX-License-Identifier: LGPL-2.1-or-later
#include "stickerevent.h"
using namespace Quotient;
StickerEvent::StickerEvent(const QJsonObject &obj)
#ifdef QUOTIENT_07
: RoomEvent(obj)
#else
: RoomEvent(typeId(), obj)
#endif
, m_imageContent(EventContent::ImageContent(obj["content"_ls].toObject()))
{
}
QString StickerEvent::body() const
{
return content<QString>("body"_ls);
}
const EventContent::ImageContent &StickerEvent::image() const
{
return m_imageContent;
}
QUrl StickerEvent::url() const
{
#ifdef QUOTIENT_07
return m_imageContent.url();
#else
return m_imageContent.url;
#endif
}

View File

@@ -1,55 +0,0 @@
// SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include <events/eventcontent.h>
#include <events/roomevent.h>
namespace Quotient
{
/**
* @class StickerEvent
*
* Class to define a sticker event.
*
* Sticker messages are specialised image messages that are displayed without
* controls (e.g. no "download" link, or light-box view on click, as would be
* displayed for for m.image events).
*
* @sa Quotient::RoomEvent
*/
class StickerEvent : public RoomEvent
{
public:
#ifdef QUOTIENT_07
QUO_EVENT(StickerEvent, "m.sticker")
#else
DEFINE_EVENT_TYPEID("m.sticker", StickerEvent)
#endif
explicit StickerEvent(const QJsonObject &obj);
/**
* @brief A textual representation or associated description of the sticker image.
*
* This could be the alt text of the original image, or a message to accompany
* and further describe the sticker.
*/
QString body() const;
/**
* @brief Metadata about the image referred to in url including a thumbnail representation.
*/
const EventContent::ImageContent &image() const;
/**
* @brief The URL to the sticker image. This must be a valid mxc:// URI.
*/
QUrl url() const;
private:
EventContent::ImageContent m_imageContent;
};
REGISTER_EVENT_TYPE(StickerEvent)
}

View File

@@ -14,7 +14,7 @@ FileTransferPseudoJob::FileTransferPseudoJob(Operation operation, const QString
{
}
void FileTransferPseudoJob::fileTransferProgress(QString id, qint64 progress, qint64 total)
void FileTransferPseudoJob::fileTransferProgress(const QString &id, qint64 progress, qint64 total)
{
if (id != m_eventId) {
return;
@@ -23,7 +23,7 @@ void FileTransferPseudoJob::fileTransferProgress(QString id, qint64 progress, qi
setTotalAmount(Unit::Bytes, total);
}
void FileTransferPseudoJob::fileTransferCompleted(QString id, QUrl localFile)
void FileTransferPseudoJob::fileTransferCompleted(const QString &id, const QUrl &localFile)
{
Q_UNUSED(localFile);
if (id != m_eventId) {
@@ -32,7 +32,7 @@ void FileTransferPseudoJob::fileTransferCompleted(QString id, QUrl localFile)
emitResult();
}
void FileTransferPseudoJob::fileTransferFailed(QString id, QString errorMessage)
void FileTransferPseudoJob::fileTransferFailed(const QString &id, const QString &errorMessage)
{
if (id != m_eventId) {
return;

View File

@@ -20,23 +20,23 @@ public:
Download,
Upload,
};
Q_ENUM(Operation);
Q_ENUM(Operation)
FileTransferPseudoJob(Operation operation, const QString &srcDest, const QString &path);
/**
* @brief Set the current number of bytes transferred.
*/
void fileTransferProgress(QString id, qint64 progress, qint64 total);
void fileTransferProgress(const QString &id, qint64 progress, qint64 total);
/**
* @brief Set the file transfer as complete.
*/
void fileTransferCompleted(QString id, QUrl localFile);
void fileTransferCompleted(const QString &id, const QUrl &localFile);
/**
* @brief Set the file transfer as failed.
*/
void fileTransferFailed(QString id, QString errorMessage = {});
void fileTransferFailed(const QString &id, const QString &errorMessage = {});
/**
* @brief Start the file transfer.

View File

@@ -112,3 +112,5 @@ QStringList FileTypeSingleton::supportedAnimatedImageFormats() const
Q_D(const FileTypeSingleton);
return d->supportedAnimatedImageFormats;
}
#include "moc_filetypesingleton.cpp"

View File

@@ -5,6 +5,7 @@
#pragma once
#include <QFileInfo>
#include <QMimeDatabase>
#include <QObject>
#include <qqml.h>

View File

@@ -3,14 +3,16 @@
#include "linkpreviewer.h"
#include <connection.h>
#include <csapi/content-repo.h>
#include "controller.h"
#include <Quotient/connection.h>
#include <Quotient/csapi/content-repo.h>
#include "neochatroom.h"
using namespace Quotient;
LinkPreviewer::LinkPreviewer(QObject *parent, NeoChatRoom *room, QUrl url)
LinkPreviewer::LinkPreviewer(QObject *parent, NeoChatRoom *room, const QUrl &url)
: QObject(parent)
, m_currentRoom(room)
, m_loaded(false)
@@ -69,14 +71,7 @@ void LinkPreviewer::loadUrlPreview()
auto imageUrl = QUrl(json["og:image"].toString());
if (imageUrl.isValid() && imageUrl.scheme() == QStringLiteral("mxc")) {
#ifdef QUOTIENT_07
m_imageSource = conn->makeMediaUrl(imageUrl);
#else
QUrlQuery q(imageUrl.query());
q.addQueryItem(QStringLiteral("user_id"), conn->userId());
imageUrl.setQuery(q);
m_imageSource = imageUrl;
#endif
} else {
m_imageSource = QUrl();
}
@@ -89,3 +84,5 @@ void LinkPreviewer::loadUrlPreview()
});
}
}
#include "moc_linkpreviewer.cpp"

View File

@@ -45,7 +45,7 @@ class LinkPreviewer : public QObject
Q_PROPERTY(QUrl imageSource READ imageSource NOTIFY imageSourceChanged)
public:
explicit LinkPreviewer(QObject *parent = nullptr, NeoChatRoom *room = nullptr, QUrl url = {});
explicit LinkPreviewer(QObject *parent = nullptr, NeoChatRoom *room = nullptr, const QUrl &url = {});
[[nodiscard]] QUrl url() const;
void setUrl(QUrl);

View File

@@ -42,3 +42,5 @@ float LocationHelper::zoomToFit(const QRectF &r, float mapWidth, float mapHeight
return std::clamp(z, 5.0, 18.0);
}
#include "moc_locationhelper.cpp"

View File

@@ -1,5 +1,6 @@
// SPDX-FileCopyrightText: 2023 Volker Krause <vkrause@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
#pragma once
#include "linkpreviewer.h"
#include <QMetaType>

View File

@@ -3,14 +3,9 @@
#include "login.h"
#ifdef QUOTIENT_07
#include <accountregistry.h>
#else
#include "neochataccountregistry.h"
#endif
#include <connection.h>
#include <qt_connection_util.h>
#include <Quotient/accountregistry.h>
#include <Quotient/connection.h>
#include <Quotient/qt_connection_util.h>
#include "controller.h"
@@ -47,11 +42,7 @@ void Login::init()
return;
}
#ifdef QUOTIENT_07
m_isLoggedIn = Accounts.isLoggedIn(m_matrixId);
#else
m_isLoggedIn = AccountRegistry::instance().isLoggedIn(m_matrixId);
#endif
m_isLoggedIn = Controller::instance().accounts().isLoggedIn(m_matrixId);
Q_EMIT isLoggedInChanged();
if (m_isLoggedIn) {
return;
@@ -207,3 +198,5 @@ bool Login::isLoggedIn() const
{
return m_isLoggedIn;
}
#include "moc_login.cpp"

View File

@@ -112,7 +112,7 @@ Q_SIGNALS:
void loginFlowsChanged();
void ssoUrlChanged();
void connected();
void errorOccured(QString message);
void errorOccured(const QString &message);
void testingChanged();
void isLoggingInChanged();
void isLoggedInChanged();

View File

@@ -4,6 +4,7 @@
#include <QCommandLineParser>
#include <QIcon>
#include <QNetworkProxyFactory>
#include <QObject>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQmlNetworkAccessManagerFactory>
@@ -28,15 +29,11 @@
#include "neochat-version.h"
#ifdef QUOTIENT_07
#include <accountregistry.h>
#else
#include "neochataccountregistry.h"
#endif
#include <networkaccessmanager.h>
#include <room.h>
#include <util.h>
#include <Quotient/keyverificationsession.h>
#include <Quotient/accountregistry.h>
#include <Quotient/networkaccessmanager.h>
#include <Quotient/room.h>
#include <Quotient/util.h>
#include "actionshandler.h"
#include "blurhashimageprovider.h"
@@ -54,6 +51,7 @@
#include "models/collapsestateproxymodel.h"
#include "models/customemojimodel.h"
#include "models/devicesmodel.h"
#include "models/devicesproxymodel.h"
#include "models/emojimodel.h"
#include "models/emoticonfiltermodel.h"
#include "models/imagepacksmodel.h"
@@ -70,6 +68,8 @@
#include "models/serverlistmodel.h"
#include "models/sortfilterroomlistmodel.h"
#include "models/sortfilterspacelistmodel.h"
#include "models/statefiltermodel.h"
#include "models/stickermodel.h"
#include "models/userdirectorylistmodel.h"
#include "models/userfiltermodel.h"
#include "models/userlistmodel.h"
@@ -78,18 +78,12 @@
#include "neochatroom.h"
#include "neochatuser.h"
#include "notificationsmanager.h"
#ifdef QUOTIENT_07
#include "pollhandler.h"
#endif
#include "models/statefiltermodel.h"
#include "models/stickermodel.h"
#include "roommanager.h"
#include "spacehierarchycache.h"
#include "urlhelper.h"
#include "windowcontroller.h"
#ifdef QUOTIENT_07
#include <keyverificationsession.h>
#endif
#ifdef HAVE_COLORSCHEME
#include "colorschemer.h"
#endif
@@ -129,7 +123,9 @@ Q_DECL_EXPORT
#endif
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QNetworkProxyFactory::setUseSystemConfiguration(true);
@@ -177,14 +173,10 @@ int main(int argc, char *argv[])
about.addComponent(QStringLiteral("libQuotient"),
i18n("A Qt5 library to write cross-platform clients for Matrix"),
#ifdef QUOTIENT_07
i18nc("<version number> (built against <possibly different version number>)",
"%1 (built against %2)",
Quotient::versionString(),
QStringLiteral(Quotient_VERSION_STRING)),
#else
QStringLiteral(QUOTIENT_VERSION),
#endif
QStringLiteral("https://github.com/quotient-im/libquotient"),
KAboutLicense::LGPL_V2_1);
@@ -193,6 +185,10 @@ int main(int argc, char *argv[])
initLogging();
#if Quotient_VERSION_MINOR == 8
Connection::setEncryptionDefault(true);
#endif
#ifdef NEOCHAT_FLATPAK
// Copy over the included FontConfig configuration to the
// app's config dir:
@@ -223,11 +219,7 @@ int main(int argc, char *argv[])
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "LoginHelper", login);
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "UrlHelper", &urlHelper);
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "EmojiModel", &EmojiModel::instance());
#ifdef QUOTIENT_07
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "AccountRegistry", &Quotient::Accounts);
#else
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "AccountRegistry", &Quotient::AccountRegistry::instance());
#endif
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "AccountRegistry", &Controller::instance().accounts());
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "SpaceHierarchyCache", &SpaceHierarchyCache::instance());
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "CustomEmojiModel", &CustomEmojiModel::instance());
qmlRegisterType<ActionsHandler>("org.kde.neochat", 1, 0, "ActionsHandler");
@@ -247,6 +239,7 @@ int main(int argc, char *argv[])
qmlRegisterType<SortFilterRoomListModel>("org.kde.neochat", 1, 0, "SortFilterRoomListModel");
qmlRegisterType<SortFilterSpaceListModel>("org.kde.neochat", 1, 0, "SortFilterSpaceListModel");
qmlRegisterType<DevicesModel>("org.kde.neochat", 1, 0, "DevicesModel");
qmlRegisterType<DevicesProxyModel>("org.kde.neochat", 1, 0, "DevicesProxyModel");
qmlRegisterType<LinkPreviewer>("org.kde.neochat", 1, 0, "LinkPreviewer");
qmlRegisterType<CompletionModel>("org.kde.neochat", 1, 0, "CompletionModel");
qmlRegisterType<StateModel>("org.kde.neochat", 1, 0, "StateModel");
@@ -254,9 +247,7 @@ int main(int argc, char *argv[])
qmlRegisterType<SearchModel>("org.kde.neochat", 1, 0, "SearchModel");
qmlRegisterType<LiveLocationsModel>("org.kde.neochat", 1, 0, "LiveLocationsModel");
qmlRegisterType<LocationsModel>("org.kde.neochat", 1, 0, "LocationsModel");
#ifdef QUOTIENT_07
qmlRegisterType<PollHandler>("org.kde.neochat", 1, 0, "PollHandler");
#endif
qmlRegisterType<PushRuleModel>("org.kde.neochat", 1, 0, "PushRuleModel");
qmlRegisterType<StickerModel>("org.kde.neochat", 1, 0, "StickerModel");
qmlRegisterType<ImagePacksModel>("org.kde.neochat", 1, 0, "ImagePacksModel");
@@ -282,12 +273,10 @@ int main(int argc, char *argv[])
qRegisterMetaType<NeoChatUser *>("NeoChatUser*");
qRegisterMetaType<GetRoomEventsJob *>("GetRoomEventsJob*");
qRegisterMetaType<QMimeType>("QMimeType");
#ifdef QUOTIENT_07
#ifdef Quotient_E2EE_ENABLED
qRegisterMetaType<KeyVerificationSession *>("KeyVerificationSession*");
qmlRegisterUncreatableType<KeyVerificationSession>("org.kde.neochat", 1, 0, "KeyVerificationSession", {});
qRegisterMetaType<QVector<EmojiEntry>>("QVector<EmojiEntry>");
#endif
#endif
qmlRegisterSingletonType("org.kde.neochat", 1, 0, "About", [](QQmlEngine *engine, QJSEngine *) -> QJSValue {
return engine->toScriptValue(KAboutData::applicationData());
@@ -335,11 +324,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

@@ -13,7 +13,7 @@
#include <KLocalizedString>
#include "controller.h"
#include <connection.h>
#include <Quotient/connection.h>
using namespace Quotient;
@@ -114,3 +114,5 @@ QQuickImageResponse *MatrixImageProvider::requestImageResponse(const QString &id
{
return new ThumbnailResponse(id, requestedSize);
}
#include "moc_matriximageprovider.cpp"

View File

@@ -6,7 +6,7 @@
#include <QQuickAsyncImageProvider>
#include <jobs/mediathumbnailjob.h>
#include <Quotient/jobs/mediathumbnailjob.h>
#include <QReadWriteLock>
@@ -26,7 +26,7 @@ class ThumbnailResponse : public QQuickImageResponse
{
Q_OBJECT
public:
ThumbnailResponse(QString mediaId, QSize requestedSize);
explicit ThumbnailResponse(QString mediaId, QSize requestedSize);
~ThumbnailResponse() override = default;
private Q_SLOTS:

View File

@@ -3,7 +3,7 @@
#include "accountemoticonmodel.h"
#include <csapi/content-repo.h>
#include <Quotient/csapi/content-repo.h>
#include <qcoro/qcorosignal.h>
using namespace Quotient;
@@ -27,11 +27,7 @@ QVariant AccountEmoticonModel::data(const QModelIndex &index, int role) const
const auto &row = index.row();
const auto &image = m_images->images[row];
if (role == UrlRole) {
#ifdef QUOTIENT_07
return m_connection->makeMediaUrl(image.url);
#else
return QUrl();
#endif
}
if (role == BodyRole) {
if (image.body) {
@@ -142,11 +138,7 @@ QCoro::Task<void> AccountEmoticonModel::doSetEmoticonImage(int index, QUrl sourc
if (job->error() != BaseJob::NoError) {
co_return;
}
#ifdef QUOTIENT_07
m_images->images[index].url = job->contentUri().toString();
#else
m_images->images[index].url = job->contentUri();
#endif
m_images->images[index].info = none;
QJsonObject data;
m_images->fillJson(&data);
@@ -176,3 +168,5 @@ void AccountEmoticonModel::addEmoticon(const QUrl &source, const QString &shortc
{
doAddEmoticon(source, shortcode, description, type);
}
#include "moc_accountemoticonmodel.cpp"

View File

@@ -4,12 +4,14 @@
#pragma once
#include "events/imagepackevent.h"
#include <QAbstractListModel>
#include <QCoroTask>
#include <QObject>
#include <QPointer>
#include <QVector>
#include <connection.h>
#include <Quotient/connection.h>
class ImagePacksModel;

View File

@@ -6,8 +6,8 @@
#include "controller.h"
#include "neochatroom.h"
#include "roommanager.h"
#include <events/roommemberevent.h>
#include <events/roompowerlevelsevent.h>
#include <Quotient/events/roommemberevent.h>
#include <Quotient/events/roompowerlevelsevent.h>
#include <KLocalizedString>
@@ -191,7 +191,6 @@ QVector<ActionsModel::Action> actions{
Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("'<text>' does not look like a matrix id.", "'%1' does not look like a matrix id.", text));
return QString();
}
#ifdef QUOTIENT_07
const RoomMemberEvent *roomMemberEvent = room->currentState().get<RoomMemberEvent>(text);
if (roomMemberEvent && roomMemberEvent->membership() == Membership::Invite) {
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is already invited to this room.", "%1 is already invited to this room.", text));
@@ -201,7 +200,6 @@ QVector<ActionsModel::Action> actions{
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is banned from this room.", "%1 is banned from this room.", text));
return QString();
}
#endif
if (room->localUser()->id() == text) {
Q_EMIT room->showMessage(NeoChatRoom::Positive, i18n("You are already in this room."));
return QString();
@@ -243,7 +241,6 @@ QVector<ActionsModel::Action> actions{
kli18n("<room alias or id>"),
kli18n("Joins the given room"),
},
#ifdef QUOTIENT_07
Action{
QStringLiteral("knock"),
[](const QString &text, NeoChatRoom *room) {
@@ -276,7 +273,6 @@ QVector<ActionsModel::Action> actions{
kli18n("<room alias or id> [<reason>]"),
kli18n("Requests to join the given room"),
},
#endif
Action{
QStringLiteral("j"),
[](const QString &text, NeoChatRoom *room) {
@@ -435,14 +431,15 @@ QVector<ActionsModel::Action> actions{
Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("'<text>' does not look like a matrix id.", "'%1' does not look like a matrix id.", text));
return QString();
}
#ifdef QUOTIENT_07
auto state = room->currentState().get<RoomMemberEvent>(parts[0]);
if (state && state->membership() == Membership::Ban) {
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is already banned from this room.", "%1 is already banned from this room.", text));
return QString();
}
#endif
auto plEvent = room->getCurrentState<RoomPowerLevelsEvent>();
auto plEvent = room->currentState().get<RoomPowerLevelsEvent>();
if (!plEvent) {
return QString();
}
if (plEvent->ban() > plEvent->powerLevelForUser(room->localUser()->id())) {
Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You are not allowed to ban users from this room."));
return QString();
@@ -472,18 +469,19 @@ QVector<ActionsModel::Action> actions{
Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("'<text>' does not look like a matrix id.", "'%1' does not look like a matrix id.", text));
return QString();
}
auto plEvent = room->getCurrentState<RoomPowerLevelsEvent>();
auto plEvent = room->currentState().get<RoomPowerLevelsEvent>();
if (!plEvent) {
return QString();
}
if (plEvent->ban() > plEvent->powerLevelForUser(room->localUser()->id())) {
Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You are not allowed to unban users from this room."));
return QString();
}
#ifdef QUOTIENT_07
auto state = room->currentState().get<RoomMemberEvent>(text);
if (state && state->membership() != Membership::Ban) {
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is not banned from this room.", "%1 is not banned from this room.", text));
return QString();
}
#endif
room->unban(text);
Q_EMIT room->showMessage(NeoChatRoom::Positive, i18nc("<username> was unbanned from this room.", "%1 was unbanned from this room.", text));
@@ -510,13 +508,14 @@ QVector<ActionsModel::Action> actions{
Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You cannot kick yourself from the room."));
return QString();
}
#ifdef QUOTIENT_07
if (!room->isMember(parts[0])) {
Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("<username> is not in this room", "%1 is not in this room.", parts[0]));
return QString();
}
#endif
auto plEvent = room->getCurrentState<RoomPowerLevelsEvent>();
auto plEvent = room->currentState().get<RoomPowerLevelsEvent>();
if (!plEvent) {
return QString();
}
auto kick = plEvent->kick();
if (plEvent->powerLevelForUser(room->localUser()->id()) < kick) {
Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You are not allowed to kick users from this room."));

View File

@@ -5,7 +5,7 @@
#include <KLazyLocalizedString>
#include <QAbstractListModel>
#include <events/roommessageevent.h>
#include <Quotient/events/roommessageevent.h>
class NeoChatRoom;
@@ -61,7 +61,7 @@ public:
CompletionType, /**< The completion type (always "action" for this model). */
Parameters, /**< The input parameters expected by the action. */
};
Q_ENUM(Roles);
Q_ENUM(Roles)
/**
* @brief Get the given role value at the given index.

View File

@@ -2,7 +2,6 @@
// SPDX-License-Identifier: LGPL-2.0-or-later
#include "collapsestateproxymodel.h"
#include "messageeventmodel.h"
#include <KLocalizedString>
@@ -174,3 +173,5 @@ QString CollapseStateProxyModel::excessAuthors(int row) const
return QStringLiteral("+ %1").arg(excessAuthors);
}
}
#include "moc_collapsestateproxymodel.cpp"

View File

@@ -193,3 +193,5 @@ void CompletionModel::setRoomListModel(RoomListModel *roomListModel)
m_roomListModel = roomListModel;
Q_EMIT roomListModelChanged();
}
#include "moc_completionmodel.cpp"

View File

@@ -69,9 +69,9 @@ public:
Icon, /**< The icon to show. */
ReplacedText, /**< The text to replace the input text with for the completion. */
};
Q_ENUM(Roles);
Q_ENUM(Roles)
CompletionModel(QObject *parent = nullptr);
explicit CompletionModel(QObject *parent = nullptr);
/**
* @brief Get the given role value at the given index.

View File

@@ -59,3 +59,5 @@ void CompletionProxyModel::setFullText(const QString &fullText)
{
m_fullText = fullText;
}
#include "moc_completionproxymodel.cpp"

View File

@@ -9,10 +9,9 @@
#include "controller.h"
#include "emojimodel.h"
#include <connection.h>
#include <csapi/account-data.h>
#include <csapi/content-repo.h>
#include <events/eventcontent.h>
#include <Quotient/connection.h>
#include <Quotient/csapi/account-data.h>
#include <Quotient/csapi/content-repo.h>
using namespace Quotient;
@@ -62,11 +61,7 @@ void CustomEmojiModel::addEmoji(const QString &name, const QUrl &location)
auto emojiData = json["images"].toObject();
QString url;
#ifdef QUOTIENT_07
url = job->contentUri().toString();
#else
url = job->contentUri();
#endif
QImage image(location.toLocalFile());
QJsonObject imageInfo;
@@ -208,3 +203,5 @@ QVariantList CustomEmojiModel::filterModel(const QString &filter)
}
return results;
}
#include "moc_customemojimodel.cpp"

View File

@@ -41,7 +41,7 @@ public:
ReplacedTextRole = 52, /**< The name of the emoji. For compatibility with EmojiModel. */
DescriptionRole = 53, /**< Invalid, reserved. For compatibility with EmojiModel. */
};
Q_ENUM(Roles);
Q_ENUM(Roles)
static CustomEmojiModel &instance()
{

View File

@@ -3,23 +3,19 @@
#include "devicesmodel.h"
#include <csapi/device_management.h>
#include "controller.h"
#include <connection.h>
#include <user.h>
#include <KLocalizedString>
#include <Quotient/csapi/device_management.h>
#include <Quotient/connection.h>
#include <Quotient/user.h>
using namespace Quotient;
DevicesModel::DevicesModel(QObject *parent)
: QAbstractListModel(parent)
{
connect(&Controller::instance(), &Controller::activeConnectionChanged, this, [this]() {
DevicesModel::fetchDevices();
Q_EMIT connectionChanged();
});
fetchDevices();
}
void DevicesModel::fetchDevices()
@@ -30,6 +26,7 @@ void DevicesModel::fetchDevices()
beginResetModel();
m_devices = job->devices();
endResetModel();
Q_EMIT countChanged();
});
}
}
@@ -40,16 +37,33 @@ QVariant DevicesModel::data(const QModelIndex &index, int role) const
return {};
}
const auto &device = m_devices[index.row()];
switch (role) {
case Id:
return m_devices[index.row()].deviceId;
return device.deviceId;
case DisplayName:
return m_devices[index.row()].displayName;
return device.displayName;
case LastIp:
return m_devices[index.row()].lastSeenIp;
return device.lastSeenIp;
case LastTimestamp:
if (m_devices[index.row()].lastSeenTs)
return *m_devices[index.row()].lastSeenTs;
if (device.lastSeenTs) {
return *device.lastSeenTs;
} else {
return false;
}
case Type:
if (device.deviceId == m_connection->deviceId()) {
return This;
}
if (!m_connection->isKnownE2eeCapableDevice(m_connection->userId(), device.deviceId)) {
return Unencrypted;
}
if (m_connection->isVerifiedDevice(m_connection->userId(), device.deviceId)) {
return Verified;
} else {
return Unverified;
}
}
return {};
}
@@ -62,11 +76,21 @@ int DevicesModel::rowCount(const QModelIndex &parent) const
QHash<int, QByteArray> DevicesModel::roleNames() const
{
return {{Id, "id"}, {DisplayName, "displayName"}, {LastIp, "lastIp"}, {LastTimestamp, "lastTimestamp"}};
return {
{Id, "id"},
{DisplayName, "displayName"},
{LastIp, "lastIp"},
{LastTimestamp, "lastTimestamp"},
{Type, "type"},
};
}
void DevicesModel::logout(int index, const QString &password)
void DevicesModel::logout(const QString &deviceId, const QString &password)
{
int index;
for (index = 0; m_devices[index].deviceId != deviceId; index++)
;
auto job = Controller::instance().activeConnection()->callApi<NeochatDeleteDeviceJob>(m_devices[index].deviceId);
connect(job, &BaseJob::result, this, [this, job, password, index] {
@@ -74,6 +98,7 @@ void DevicesModel::logout(int index, const QString &password)
beginRemoveRows(QModelIndex(), index, index);
m_devices.remove(index);
endRemoveRows();
Q_EMIT countChanged();
};
if (job->error() != BaseJob::Success) {
QJsonObject replyData = job->jsonData();
@@ -91,8 +116,11 @@ void DevicesModel::logout(int index, const QString &password)
});
}
void DevicesModel::setName(int index, const QString &name)
void DevicesModel::setName(const QString &deviceId, const QString &name)
{
int index;
for (index = 0; m_devices[index].deviceId != deviceId; index++);
auto job = Controller::instance().activeConnection()->callApi<UpdateDeviceJob>(m_devices[index].deviceId, name);
QString oldName = m_devices[index].displayName;
beginResetModel();
@@ -107,5 +135,27 @@ void DevicesModel::setName(int index, const QString &name)
Connection *DevicesModel::connection() const
{
return Controller::instance().activeConnection();
return m_connection;
}
void DevicesModel::setConnection(Connection *connection)
{
if (m_connection) {
disconnect(m_connection, nullptr, this, nullptr);
}
m_connection = connection;
Q_EMIT connectionChanged();
fetchDevices();
connect(m_connection, &Connection::sessionVerified, this, [this](const QString &userId, const QString &deviceId) {
Q_UNUSED(deviceId);
if (userId == Controller::instance().activeConnection()->userId()) {
fetchDevices();
}
});
connect(m_connection, &Connection::finishedQueryingKeys, this, [this]() {
fetchDevices();
});
}
#include "moc_devicesmodel.cpp"

View File

@@ -4,8 +4,9 @@
#pragma once
#include <QAbstractListModel>
#include <QPointer>
#include <csapi/definitions/client_device.h>
#include <Quotient/csapi/definitions/client_device.h>
namespace Quotient
{
@@ -28,7 +29,7 @@ class DevicesModel : public QAbstractListModel
/**
* @brief The current connection that the model is getting its devices from.
*/
Q_PROPERTY(Quotient::Connection *connection READ connection NOTIFY connectionChanged)
Q_PROPERTY(Quotient::Connection *connection READ connection WRITE setConnection NOTIFY connectionChanged REQUIRED)
public:
/**
@@ -39,10 +40,17 @@ 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. */
Type, /**< The category to sort this device into. */
};
Q_ENUM(Roles);
Q_ENUM(Roles)
DevicesModel(QObject *parent = nullptr);
enum DeviceType {
This,
Verified,
Unverified,
Unencrypted,
};
Q_ENUM(DeviceType);
/**
* @brief Get the given role value at the given index.
@@ -66,21 +74,27 @@ public:
QHash<int, QByteArray> roleNames() const override;
/**
* @brief Logout the device at the given index.
* @brief Logout the device with the given id.
*/
Q_INVOKABLE void logout(int index, const QString &password);
Q_INVOKABLE void logout(const QString &deviceId, const QString &password);
/**
* @brief Set the display name of the device at the given index.
* @brief Set the display name of the device with the given id.
*/
Q_INVOKABLE void setName(int index, const QString &name);
Q_INVOKABLE void setName(const QString &deviceId, const QString &name);
Quotient::Connection *connection() const;
explicit DevicesModel(QObject *parent = nullptr);
[[nodiscard]] Quotient::Connection *connection() const;
void setConnection(Quotient::Connection *connection);
Q_SIGNALS:
void connectionChanged();
void countChanged();
private:
void fetchDevices();
QVector<Quotient::Device> m_devices;
QPointer<Quotient::Connection> m_connection;
};

View File

@@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: GPL-2.0-or-later
#include "devicesproxymodel.h"
#include "devicesmodel.h"
int DevicesProxyModel::type() const
{
return m_type;
}
void DevicesProxyModel::setType(int type)
{
m_type = type;
Q_EMIT typeChanged();
}
bool DevicesProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
Q_UNUSED(source_parent)
return sourceModel()->data(sourceModel()->index(source_row, 0), DevicesModel::Type).toInt() == m_type;
}
DevicesProxyModel::DevicesProxyModel(QObject *parent)
: QSortFilterProxyModel(parent)
, m_type(0)
{
setSortRole(DevicesModel::LastTimestamp);
sort(0, Qt::DescendingOrder);
}
#include "moc_devicesproxymodel.cpp"

View File

@@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QSortFilterProxyModel>
class DevicesProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
Q_PROPERTY(int type READ type WRITE setType NOTIFY typeChanged);
public:
DevicesProxyModel(QObject *parent = nullptr);
[[nodiscard]] bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
void setType(int type);
[[nodiscard]] int type() const;
Q_SIGNALS:
void typeChanged();
private:
int m_type;
};

View File

@@ -216,3 +216,5 @@ QVariantList EmojiModel::categoriesWithCustom() const
;
return cats;
}
#include "moc_emojimodel.cpp"

View File

@@ -95,7 +95,7 @@ public:
ReplacedTextRole = 52, /**< The text to replace the short name with (i.e. the unicode character). */
DescriptionRole = 53, /**< The long description of an emoji. */
};
Q_ENUM(RoleNames);
Q_ENUM(RoleNames)
/**
* @brief Defines the potential categories an emoji can be placed in.

View File

@@ -53,3 +53,5 @@ void EmoticonFilterModel::setShowEmojis(bool showEmojis)
endResetModel();
Q_EMIT showEmojisChanged();
}
#include "moc_emoticonfiltermodel.cpp"

View File

@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QSortFilterProxyModel>
/**

View File

@@ -33,13 +33,9 @@ QVariant ImagePacksModel::data(const QModelIndex &index, int role) const
}
if (role == AvatarUrlRole) {
if (event.pack->avatarUrl) {
#ifdef QUOTIENT_07
return m_room->connection()->makeMediaUrl(*event.pack->avatarUrl);
#endif
} else if (!event.images.empty()) {
#ifdef QUOTIENT_07
return m_room->connection()->makeMediaUrl(event.images[0].url);
#endif
}
}
return {};
@@ -106,7 +102,6 @@ void ImagePacksModel::reloadImages()
auto packs = rooms[roomId].toObject();
const auto &stickerRoom = m_room->connection()->room(roomId);
for (const auto &packKey : packs.keys()) {
#ifdef QUOTIENT_07
if (const auto &pack = stickerRoom->currentState().get<ImagePackEvent>(packKey)) {
const auto packContent = pack->content();
if ((!packContent.pack || !packContent.pack->usage || (packContent.pack->usage->contains("emoticon") && showEmoticons())
@@ -115,11 +110,9 @@ void ImagePacksModel::reloadImages()
m_events += packContent;
}
}
#endif
}
}
}
#ifdef QUOTIENT_07
// Load emoticons from the current room
auto events = m_room->currentState().eventsOfType("im.ponies.room_emotes");
@@ -132,7 +125,6 @@ void ImagePacksModel::reloadImages()
}
}
}
#endif
Q_EMIT imagesLoaded();
endResetModel();
}
@@ -165,3 +157,5 @@ QVector<Quotient::ImagePackEventContent::ImagePackImage> ImagePacksModel::images
}
return m_events[index].images;
}
#include "moc_imagepacksmodel.cpp"

View File

@@ -47,7 +47,7 @@ public:
AttributionRole, /**< The attribution for the pack author(s). */
IdRole, /**< The ID of the image pack. */
};
Q_ENUM(Roles);
Q_ENUM(Roles)
explicit ImagePacksModel(QObject *parent = nullptr);

View File

@@ -0,0 +1,112 @@
// SPDX-FileCopyrightText: 2022 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 "keywordnotificationrulemodel.h"
#include "controller.h"
#include "notificationsmanager.h"
#include <QDebug>
#include <Quotient/connection.h>
#include <Quotient/converters.h>
#include <Quotient/csapi/definitions/push_ruleset.h>
#include <Quotient/csapi/pushrules.h>
#include <Quotient/jobs/basejob.h>
KeywordNotificationRuleModel::KeywordNotificationRuleModel(QObject *parent)
: QAbstractListModel(parent)
{
if (Controller::instance().activeConnection()) {
controllerConnectionChanged();
}
connect(&Controller::instance(), &Controller::activeConnectionChanged, this, &KeywordNotificationRuleModel::controllerConnectionChanged);
}
void KeywordNotificationRuleModel::controllerConnectionChanged()
{
connect(Controller::instance().activeConnection(), &Quotient::Connection::accountDataChanged, this, &KeywordNotificationRuleModel::updateNotificationRules);
updateNotificationRules("m.push_rules");
}
void KeywordNotificationRuleModel::updateNotificationRules(const QString &type)
{
if (type != "m.push_rules") {
return;
}
const QJsonObject ruleDataJson = Controller::instance().activeConnection()->accountDataJson("m.push_rules");
const Quotient::PushRuleset ruleData = Quotient::fromJson<Quotient::PushRuleset>(ruleDataJson["global"].toObject());
const QVector<Quotient::PushRule> contentRules = ruleData.content;
beginResetModel();
m_notificationRules.clear();
for (const auto &i : contentRules) {
if (!m_notificationRules.contains(i.ruleId) && i.ruleId[0] != '.') {
m_notificationRules.append(i.ruleId);
}
}
endResetModel();
}
QVariant KeywordNotificationRuleModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return {};
}
if (index.row() >= m_notificationRules.count()) {
qDebug() << "KeywordNotificationRuleModel, something's wrong: index.row() >= m_notificationRules.count()";
return {};
}
if (role == NameRole) {
return m_notificationRules.at(index.row());
}
return {};
}
int KeywordNotificationRuleModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_notificationRules.count();
}
void KeywordNotificationRuleModel::addKeyword(const QString &keyword)
{
if (m_notificationRules.count() == 0) {
NotificationsManager::instance().initializeKeywordNotificationAction();
}
const QVector<QVariant> actions = NotificationsManager::instance().getKeywordNotificationActions();
auto job = Controller::instance()
.activeConnection()
->callApi<Quotient::SetPushRuleJob>("global", "content", keyword, actions, "", "", QVector<Quotient::PushCondition>(), keyword);
connect(job, &Quotient::BaseJob::success, this, [this, keyword]() {
beginInsertRows(QModelIndex(), m_notificationRules.count(), m_notificationRules.count());
m_notificationRules.append(keyword);
endInsertRows();
});
}
void KeywordNotificationRuleModel::removeKeywordAtIndex(int index)
{
auto job = Controller::instance().activeConnection()->callApi<Quotient::DeletePushRuleJob>("global", "content", m_notificationRules[index]);
connect(job, &Quotient::BaseJob::success, this, [this, index]() {
beginRemoveRows(QModelIndex(), index, index);
m_notificationRules.removeAt(index);
endRemoveRows();
if (m_notificationRules.count() == 0) {
NotificationsManager::instance().deactivateKeywordNotificationAction();
}
});
}
QHash<int, QByteArray> KeywordNotificationRuleModel::roleNames() const
{
return {{NameRole, QByteArrayLiteral("name")}};
}
#include "moc_keywordnotificationrulemodel.cpp"

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