Compare commits
6 Commits
work/nvrwh
...
work/adapt
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cc68d2658 | ||
|
|
f4b87caefe | ||
|
|
07a9497f4c | ||
|
|
35f1ace458 | ||
|
|
11dd0ee151 | ||
|
|
07e200c74f |
@@ -110,7 +110,7 @@
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/quotient-im/libQuotient.git",
|
||||
"branch": "0.8.x",
|
||||
"branch": "dev",
|
||||
"disable-submodules": true
|
||||
}
|
||||
],
|
||||
|
||||
@@ -8,7 +8,7 @@ cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# KDE Applications version, managed by release script.
|
||||
set(RELEASE_SERVICE_VERSION_MAJOR "24")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "07")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "04")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "70")
|
||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# SPDX-License-Identifier: MIT
|
||||
# SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import unittest
|
||||
import time
|
||||
|
||||
from appium import webdriver
|
||||
from appium.options.common.base import AppiumOptions
|
||||
from appium.webdriver.common.appiumby import AppiumBy
|
||||
|
||||
|
||||
class CreateRoomTest(unittest.TestCase):
|
||||
|
||||
mockServerProcess: subprocess.Popen
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.mockServerProcess = subprocess.Popen([sys.executable, os.path.join(os.path.dirname(__file__), "login-server.py")])
|
||||
options = AppiumOptions()
|
||||
options.set_capability("app", "neochat --ignore-ssl-errors --test")
|
||||
cls.driver = webdriver.Remote(command_executor='http://127.0.0.1:4723', options=options)
|
||||
|
||||
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.mockServerProcess.terminate()
|
||||
self.driver.quit()
|
||||
|
||||
def test_create_room(self):
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="@user:localhost:1234").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Show Menu").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Create a Room").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Name:").send_keys("Super awesome room name")#
|
||||
time.sleep(0.1) # without this, the second half of the text is sent to the topic field?!
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Topic:").send_keys("There are not enough raccoons here")
|
||||
time.sleep(0.1)
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Create Room").click()
|
||||
time.sleep(0.1)
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Super awesome room name").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Show Room Information").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="There are not enough raccoons here")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,78 +0,0 @@
|
||||
{
|
||||
"next_batch": "batch1234",
|
||||
"rooms": {
|
||||
"join": {
|
||||
"!newroom123321:localhost:1234": {
|
||||
"state": {
|
||||
"events": [
|
||||
{
|
||||
"type": "m.room.member",
|
||||
"state_key": "@user:localhost:1234",
|
||||
"sender": "@user:localhost:1234",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"event_id": "$event_id_1234_0:localhost:1234",
|
||||
"room_id": "!newroom123321:localhost:1234",
|
||||
"content": {
|
||||
"avatar_url": "",
|
||||
"displayname": "A Display Name",
|
||||
"membership": "join",
|
||||
"reason": "Nothing"
|
||||
},
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "m.room.name",
|
||||
"state_key": "",
|
||||
"sender": "@user:localhost:1234",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"event_id": "$event_id_1234_1:localhost:1234",
|
||||
"room_id": "!newroom123321:localhost:1234",
|
||||
"content": {
|
||||
"name": "Super awesome room name"
|
||||
},
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "m.room.topic",
|
||||
"state_key": "",
|
||||
"sender": "@user:localhost:1234",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"event_id": "$event_id_1234_2:localhost:1234",
|
||||
"room_id": "!newroom123321:localhost:1234",
|
||||
"content": {
|
||||
"topic": "There are not enough raccoons here"
|
||||
},
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"type": "m.room.message",
|
||||
"sender": "@user:localhost:1234",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"event_id": "$event_id_1234_1:localhost:1234",
|
||||
"room_id": "!newroom123321:localhost:1234",
|
||||
"content": {
|
||||
"body": "This is a message",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<a href=\"https://matrix.to/#/@user:localhost:1234\">User</a>:",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,6 @@ from flask import Flask, request, abort
|
||||
import os
|
||||
app = Flask(__name__)
|
||||
|
||||
next_sync_payload = ""
|
||||
|
||||
|
||||
@app.route("/_matrix/client/v3/login", methods=["GET"])
|
||||
def login_get():
|
||||
@@ -44,13 +42,8 @@ def load_json(name):
|
||||
|
||||
@app.route("/_matrix/client/r0/sync")
|
||||
def sync():
|
||||
global next_sync_payload
|
||||
result = dict()
|
||||
if len(next_sync_payload) > 0:
|
||||
result = load_json(next_sync_payload)
|
||||
next_sync_payload = ""
|
||||
else:
|
||||
result = load_json("sync_response_no_rooms") if ("login" in request.headers.get("Authorization")) else load_json("sync_response_rooms")
|
||||
|
||||
result = load_json("sync_response_no_rooms") if ("login" in request.headers.get("Authorization")) else load_json("sync_response_rooms")
|
||||
return result
|
||||
|
||||
@app.route("/.well-known/matrix/client")
|
||||
@@ -72,18 +65,6 @@ def upload_keys():
|
||||
reply = dict()
|
||||
return reply
|
||||
|
||||
@app.route("/_matrix/client/v3/createRoom", methods=["POST"])
|
||||
def create_room():
|
||||
global next_sync_payload
|
||||
data = request.get_json()
|
||||
if data["name"] != "Super awesome room name" or data["topic"] != "There are not enough raccoons here":
|
||||
return dict(), 400
|
||||
response = dict()
|
||||
response["room_id"] = "!newroom123321:localhost:1234"
|
||||
next_sync_payload = "sync_response_new_room"
|
||||
return response
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(ssl_context='adhoc', port=1234)
|
||||
|
||||
14
autotests/data/test-invalidmatrixtolink-event.json
Normal file
14
autotests/data/test-invalidmatrixtolink-event.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "https://matrix.to/#/@alice:example.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
14
autotests/data/test-invalidmxclink-event.json
Normal file
14
autotests/data/test-invalidmxclink-event.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
14
autotests/data/test-invalidnospacelink-event.json
Normal file
14
autotests/data/test-invalidnospacelink-event.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "testhttps://kde.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
14
autotests/data/test-validplainlink-event.json
Normal file
14
autotests/data/test-validplainlink-event.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "https://kde.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
14
autotests/data/test-validplainwwwlink-event.json
Normal file
14
autotests/data/test-validplainwwwlink-event.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "www.example.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
16
autotests/data/test-validrichlink-event.json
Normal file
16
autotests/data/test-validrichlink-event.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "[Rich Link](https://kde.org)",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<a href=\"https://kde.org\">Rich Link</a>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,6 @@ private Q_SLOTS:
|
||||
void genericBody();
|
||||
void nullGenericBody();
|
||||
void markdownBody();
|
||||
void markdownBodyReply();
|
||||
void subtitle();
|
||||
void nullSubtitle();
|
||||
void mediaInfo();
|
||||
@@ -106,7 +105,7 @@ void EventHandlerTest::author()
|
||||
|
||||
auto eventHandlerAuthor = eventHandler.getAuthor();
|
||||
|
||||
QCOMPARE(eventHandlerAuthor["isLocalUser"_ls], author->id() == room->localUser()->id());
|
||||
QCOMPARE(eventHandlerAuthor["isLocalUser"_ls], author->id() == room->localMember().id());
|
||||
QCOMPARE(eventHandlerAuthor["id"_ls], author->id());
|
||||
QCOMPARE(eventHandlerAuthor["displayName"_ls], author->displayname(room));
|
||||
QCOMPARE(eventHandlerAuthor["avatarSource"_ls], room->avatarForMember(author));
|
||||
@@ -302,13 +301,6 @@ void EventHandlerTest::markdownBody()
|
||||
QCOMPARE(eventHandler.getMarkdownBody(), QStringLiteral("This is an example\ntext message"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::markdownBodyReply()
|
||||
{
|
||||
EventHandler eventHandler(room, room->messageEvents().at(5).get());
|
||||
|
||||
QCOMPARE(eventHandler.getMarkdownBody(), QStringLiteral("reply"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::subtitle()
|
||||
{
|
||||
EventHandler eventHandler(room, room->messageEvents().at(0).get());
|
||||
@@ -398,7 +390,7 @@ void EventHandlerTest::replyAuthor()
|
||||
|
||||
auto eventHandlerReplyAuthor = eventHandler.getReplyAuthor();
|
||||
|
||||
QCOMPARE(eventHandlerReplyAuthor["isLocalUser"_ls], replyAuthor->id() == room->localUser()->id());
|
||||
QCOMPARE(eventHandlerReplyAuthor["isLocalUser"_ls], replyAuthor->id() == room->localMember().id());
|
||||
QCOMPARE(eventHandlerReplyAuthor["id"_ls], replyAuthor->id());
|
||||
QCOMPARE(eventHandlerReplyAuthor["displayName"_ls], replyAuthor->displayname(room));
|
||||
QCOMPARE(eventHandlerReplyAuthor["avatarSource"_ls], room->avatarForMember(replyAuthor));
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
|
||||
#include "linkpreviewer.h"
|
||||
|
||||
#include "utils.h"
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
#include <Quotient/quotient_common.h>
|
||||
#include <Quotient/syncdata.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include "testutils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
@@ -29,9 +30,6 @@ private Q_SLOTS:
|
||||
void linkPreviewsMatch_data();
|
||||
void linkPreviewsMatch();
|
||||
|
||||
void multipleLinkPreviewsMatch_data();
|
||||
void multipleLinkPreviewsMatch();
|
||||
|
||||
void linkPreviewsReject_data();
|
||||
void linkPreviewsReject();
|
||||
};
|
||||
@@ -44,59 +42,45 @@ void LinkPreviewerTest::initTestCase()
|
||||
|
||||
void LinkPreviewerTest::linkPreviewsMatch_data()
|
||||
{
|
||||
QTest::addColumn<QString>("inputString");
|
||||
QTest::addColumn<QString>("eventSource");
|
||||
QTest::addColumn<QUrl>("testOutputLink");
|
||||
|
||||
QTest::newRow("plainHttps") << QStringLiteral("https://kde.org") << QUrl("https://kde.org"_ls);
|
||||
QTest::newRow("richHttps") << QStringLiteral("<a href=\"https://kde.org\">Rich Link</a>") << QUrl("https://kde.org"_ls);
|
||||
QTest::newRow("richHttpsLinkDescription") << QStringLiteral("<a href=\"https://kde.org\">https://kde.org</a>") << QUrl("https://kde.org"_ls);
|
||||
QTest::newRow("plainHttps") << QStringLiteral("test-validplainlink-event.json") << QUrl("https://kde.org"_ls);
|
||||
QTest::newRow("richHttps") << QStringLiteral("test-validrichlink-event.json") << QUrl("https://kde.org"_ls);
|
||||
QTest::newRow("plainWww") << QStringLiteral("test-validplainwwwlink-event.json") << QUrl("www.example.org"_ls);
|
||||
QTest::newRow("multipleHttps") << QStringLiteral("test-multiplelink-event.json") << QUrl("www.example.org"_ls);
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::linkPreviewsMatch()
|
||||
{
|
||||
QFETCH(QString, inputString);
|
||||
QFETCH(QString, eventSource);
|
||||
QFETCH(QUrl, testOutputLink);
|
||||
|
||||
auto link = LinkPreviewer::linkPreviews(inputString)[0];
|
||||
auto event = TestUtils::loadEventFromFile<RoomMessageEvent>(eventSource);
|
||||
auto linkPreviewer = LinkPreviewer(LinkPreviewer::linkPreview(event.get()), connection);
|
||||
|
||||
QCOMPARE(link, testOutputLink);
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::multipleLinkPreviewsMatch_data()
|
||||
{
|
||||
QTest::addColumn<QString>("inputString");
|
||||
QTest::addColumn<QList<QUrl>>("testOutputLinks");
|
||||
|
||||
QTest::newRow("multipleHttps") << QStringLiteral("www.example.org https://kde.org") << QList{QUrl("www.example.org"_ls), QUrl("https://kde.org"_ls)};
|
||||
QTest::newRow("multipleHttps1Invalid") << QStringLiteral("www.example.org mxc://example.org/SEsfnsuifSDFSSEF") << QList{QUrl("www.example.org"_ls)};
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::multipleLinkPreviewsMatch()
|
||||
{
|
||||
QFETCH(QString, inputString);
|
||||
QFETCH(QList<QUrl>, testOutputLinks);
|
||||
|
||||
auto links = LinkPreviewer::linkPreviews(inputString);
|
||||
|
||||
QCOMPARE(links, testOutputLinks);
|
||||
QCOMPARE(linkPreviewer.empty(), false);
|
||||
QCOMPARE(linkPreviewer.url(), testOutputLink);
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::linkPreviewsReject_data()
|
||||
{
|
||||
QTest::addColumn<QString>("inputString");
|
||||
QTest::addColumn<QString>("eventSource");
|
||||
|
||||
QTest::newRow("mxc") << QStringLiteral("mxc://example.org/SEsfnsuifSDFSSEF");
|
||||
QTest::newRow("matrixTo") << QStringLiteral("https://matrix.to/#/@alice:example.org");
|
||||
QTest::newRow("noSpace") << QStringLiteral("testhttps://kde.org");
|
||||
QTest::newRow("mxc") << QStringLiteral("test-invalidmxclink-event.json");
|
||||
QTest::newRow("matrixTo") << QStringLiteral("test-invalidmatrixtolink-event.json");
|
||||
QTest::newRow("noSpace") << QStringLiteral("test-invalidnospacelink-event.json");
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::linkPreviewsReject()
|
||||
{
|
||||
QFETCH(QString, inputString);
|
||||
QFETCH(QString, eventSource);
|
||||
|
||||
auto links = LinkPreviewer::linkPreviews(inputString);
|
||||
auto event = TestUtils::loadEventFromFile<RoomMessageEvent>(eventSource);
|
||||
auto linkPreviewer = LinkPreviewer(LinkPreviewer::linkPreview(event.get()), connection);
|
||||
|
||||
QCOMPARE(links.empty(), true);
|
||||
QCOMPARE(linkPreviewer.empty(), true);
|
||||
QCOMPARE(linkPreviewer.url(), QUrl());
|
||||
}
|
||||
|
||||
QTEST_MAIN(LinkPreviewerTest)
|
||||
|
||||
@@ -54,7 +54,6 @@
|
||||
<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="cs">Mluvte se svými přáteli na Matrixu</summary>
|
||||
<summary xml:lang="en-GB">Chat with your friends on matrix</summary>
|
||||
<summary xml:lang="eo">Babilu kun viaj amikoj sur matrix</summary>
|
||||
<summary xml:lang="es">Charle con sus amigos en matrix</summary>
|
||||
<summary xml:lang="eu">Berriketan jardun zure lagunekin «Matrix»en</summary>
|
||||
@@ -82,11 +81,10 @@
|
||||
<p>NeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.</p>
|
||||
<p xml:lang="ca">El NeoChat és una aplicació de xat que us permet aprofitar plenament la xarxa Matrix. Proporciona una manera segura d'enviar missatges de text, vídeos i arxius d'àudio a la vostra família, companys i amics.</p>
|
||||
<p xml:lang="ca-valencia">NeoChat és una aplicació de xat que us permet aprofitar plenament la xarxa Matrix. Proporciona una manera segura d'enviar missatges de text, vídeos i arxius d'àudio a la vostra família, companys i amics.</p>
|
||||
<p xml:lang="en-GB">NeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.</p>
|
||||
<p xml:lang="eo">NeoChat estas babilej-apo, kiu ebligas al vi plene profiti de la Matrix-reto. Ĝi provizas al vi sekuran manieron sendi tekstmesaĝojn, filmetojn kaj sondosierojn al via familio, kolegoj kaj amikoj.</p>
|
||||
<p xml:lang="es">NeoChat es una aplicación de chat que le permite aprovechar al máximo la red Matrix. Le proporciona un modo seguro de enviar mensajes de texto, vídeos y archivos de sonido a su familia, colegas y amigos.</p>
|
||||
<p xml:lang="eu">NeoChat, Matrix sarearen abantaila guztiei probetsua ateratzeko aukera ematen dizun berriketa aplikaizo bat da. Zure familiari, kideei eta lagunei testu mezuak, bideoak eta audio fitxategiak era seguruan bidaltzeko aukera ematen dizu.</p>
|
||||
<p xml:lang="fr">NeoChat est une application de discussions vous permettant de profiter pleinement du réseau Matrix. Elle vous offre un moyen sécurisé d'envoyer des messages de texte, des vidéos et des fichiers audio à votre famille, vos collègues et vos ami(e)s.</p>
|
||||
<p xml:lang="fr">NeoChat est une application de discussions vous permettant de profiter pleinement du réseau Matrix. Elle vous offre un moyen sécurisé d’envoyer des messages de texte, des vidéos et des fichiers audio à votre famille, vos collègues et vos ami(e)s.</p>
|
||||
<p xml:lang="hu">A NeoChat egy olyan csevegőalkalmazás, amellyel teljes mértékben kihasználhatja a Matrix hálózatot. Biztonságos módot biztosít szöveges üzenetek, videók és hangfájlok küldéséhez családtagjainak, kollégáinak és barátainak.</p>
|
||||
<p xml:lang="ia">NeoChat es un app de conversation que te permitte prender avantage plen del rete Matrix. Il te forni un modo secur de inviar messages de texto, videos e files audio a tui familia, collegas e amicos.</p>
|
||||
<p xml:lang="it">NeoChat è un'applicazione di chat che ti consente di sfruttare appieno la rete Matrix. Ti fornisce un modo sicuro per inviare messaggi di testo, video e file audio a familiari, colleghi e amici.</p>
|
||||
@@ -120,7 +118,6 @@
|
||||
<p xml:lang="nn">NeoChat har som mål å støtta all funksjonalitet i Matrix-spesifikasjonen. Førebels er alt i den gjeldande stabile spesifikasjonen støtta, med unntak av VoIP, trådar og nokre delar av ende-til-kryptering. Det finst òg andre småting som ikkje er støtta, sidan Matrix-spesifikasjon er i stadig endring, men målet er altså støtte for alt.</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="ru">Целью создания NeoChat является полноценная реализация программы для спецификации Matrix. Как следствие, реализовано всё в текущей стабильной спецификации (за исключением голосовой интернет-связи, потоков и некоторых аспектов сквозного шифрования). Есть также несколько других незначительных пробелов, обусловленных постоянными изменениями спецификации Matrix. Тем не менее, стоит задача в итоге предоставить полную поддержку спецификации.</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>
|
||||
@@ -148,7 +145,6 @@
|
||||
<p xml:lang="nn">På grunn av måten Matrix-spesifikasjonen vert utvikla på, støttar NeoChat òg nokre uferdige funksjonar:</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="ru">В силу природы разработки спецификации Matrix в NeoChat тоже предусмотрена поддержка многочисленных нестабильных возможностей. В текущей версии это следующие возможности:</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>
|
||||
@@ -206,7 +202,6 @@
|
||||
<li xml:lang="nn">Klistremerke-pakkar – MSC2545</li>
|
||||
<li xml:lang="pl">Paczki naklejek - MSC2545</li>
|
||||
<li xml:lang="pt">Pacotes de Autocolantes - MSC2545</li>
|
||||
<li xml:lang="ru">Наборы стикеров — MSC2545</li>
|
||||
<li xml:lang="sl">Sticker Packs - MSC2545</li>
|
||||
<li xml:lang="sv">Sticker Packs - MSC2545</li>
|
||||
<li xml:lang="ta">ஒட்டி தொகுப்புகள் - MSC2545</li>
|
||||
@@ -235,7 +230,6 @@
|
||||
<li xml:lang="nn">Posisjonshendingar – MSC3488</li>
|
||||
<li xml:lang="pl">Wydarzenia w miejscach - MSC3488</li>
|
||||
<li xml:lang="pt">Eventos com Localizações - MSC3488</li>
|
||||
<li xml:lang="ru">События местоположения — MSC3488</li>
|
||||
<li xml:lang="sl">Location Events - MSC3488</li>
|
||||
<li xml:lang="sv">Location Events - MSC3488</li>
|
||||
<li xml:lang="ta">இட நிகழ்வுகள் - MSC3488</li>
|
||||
@@ -281,7 +275,6 @@
|
||||
<caption xml:lang="ar">العرض الرئيسة مع قائمة الغرف والدردشات و معلومات الغرفة</caption>
|
||||
<caption xml:lang="ca">Vista principal amb la llista de sales, xats i informació de les sales</caption>
|
||||
<caption xml:lang="ca-valencia">Vista principal amb la llista de sales, xats i informació de les sales</caption>
|
||||
<caption xml:lang="en-GB">Main view with room list, chat, and room information</caption>
|
||||
<caption xml:lang="eo">Ĉefa vido kun ĉambra listo, babilejo kaj ĉambra informo</caption>
|
||||
<caption xml:lang="es">Vista principal con la lista de salas, chat e información de la sala</caption>
|
||||
<caption xml:lang="eu">Ikuspegi nagusia gela-zerrenda, berriketa, eta gelako informazioarekin</caption>
|
||||
@@ -298,7 +291,6 @@
|
||||
<caption xml:lang="nn">Hovudvising med romliste, pratevindauge og rominformasjon</caption>
|
||||
<caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption>
|
||||
<caption xml:lang="pt">A área principal com a lista de salas e com informações sobre a conversa e a sala</caption>
|
||||
<caption xml:lang="ru">Главное окно со списком комнат, чатом и информацией о комнате</caption>
|
||||
<caption xml:lang="sl">Glavni pogled s seznamom sob, klepetom in informacijami o sobah</caption>
|
||||
<caption xml:lang="sv">Huvudvy med rumslista, chatt, och rumsinformation</caption>
|
||||
<caption xml:lang="ta">அரங்குப்பட்டியல், உரையாடல், மற்றும் அரங்குவிவரங்களைக் கொண்டுள்ள பிரதான காட்சி</caption>
|
||||
@@ -312,7 +304,6 @@
|
||||
<caption>Discover new communities with Matrix Spaces</caption>
|
||||
<caption xml:lang="ca">Descobriu comunitats noves amb els espais de Matrix</caption>
|
||||
<caption xml:lang="ca-valencia">Descobriu comunitats noves amb els espais de Matrix</caption>
|
||||
<caption xml:lang="en-GB">Discover new communities with Matrix Spaces</caption>
|
||||
<caption xml:lang="eo">Malkovru novajn komunumojn per Matrix Spaces</caption>
|
||||
<caption xml:lang="es">Descubra nuevas comunidades con los espacios de Matrix</caption>
|
||||
<caption xml:lang="eu">Ezagutu komunitate berriak Matrixeko Tokiak erabiliz</caption>
|
||||
@@ -343,7 +334,6 @@
|
||||
<caption xml:lang="ar">العرض الرئيسة مع قائمة الغرف والدردشات و معلومات الغرفة</caption>
|
||||
<caption xml:lang="ca">Vista principal amb la llista de sales, xats i informació de les sales</caption>
|
||||
<caption xml:lang="ca-valencia">Vista principal amb la llista de sales, xats i informació de les sales</caption>
|
||||
<caption xml:lang="en-GB">Main view with room list, chat, and room information</caption>
|
||||
<caption xml:lang="eo">Ĉefa vido kun ĉambra listo, babilejo kaj ĉambra informo</caption>
|
||||
<caption xml:lang="es">Vista principal con la lista de salas, chat e información de la sala</caption>
|
||||
<caption xml:lang="eu">Ikuspegi nagusia gela-zerrenda, berriketa, eta gelako informazioarekin</caption>
|
||||
@@ -360,7 +350,6 @@
|
||||
<caption xml:lang="nn">Hovudvising med romliste, pratevindauge og rominformasjon</caption>
|
||||
<caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption>
|
||||
<caption xml:lang="pt">A área principal com a lista de salas e com informações sobre a conversa e a sala</caption>
|
||||
<caption xml:lang="ru">Главное окно со списком комнат, чатом и информацией о комнате</caption>
|
||||
<caption xml:lang="sl">Glavni pogled s seznamom sob, klepetom in informacijami o sobah</caption>
|
||||
<caption xml:lang="sv">Huvudvy med rumslista, chatt, och rumsinformation</caption>
|
||||
<caption xml:lang="ta">அரங்குப்பட்டியல், உரையாடல், மற்றும் அரங்குவிவரங்களைக் கொண்டுள்ள பிரதான காட்சி</caption>
|
||||
@@ -376,7 +365,6 @@
|
||||
<caption xml:lang="ca">Pantalla d'inici de sessió</caption>
|
||||
<caption xml:lang="ca-valencia">Pantalla d'inici de sessió</caption>
|
||||
<caption xml:lang="cs">Přihlašovací obrazovka</caption>
|
||||
<caption xml:lang="en-GB">Login screen</caption>
|
||||
<caption xml:lang="eo">Ensaluta ekrano</caption>
|
||||
<caption xml:lang="es">Pantalla de inicio de sesión</caption>
|
||||
<caption xml:lang="eu">Saio-hasteko pantaila</caption>
|
||||
@@ -393,7 +381,6 @@
|
||||
<caption xml:lang="nn">Innloggingsbilete</caption>
|
||||
<caption xml:lang="pl">Ekran logowania</caption>
|
||||
<caption xml:lang="pt">Ecrã de autenticação</caption>
|
||||
<caption xml:lang="ru">Окно входа</caption>
|
||||
<caption xml:lang="sl">Prijavni zaslon</caption>
|
||||
<caption xml:lang="sv">Inloggningsfönster</caption>
|
||||
<caption xml:lang="ta">நுழைவுத் திரை</caption>
|
||||
@@ -407,7 +394,6 @@
|
||||
<content_attribute id="social-chat">intense</content_attribute>
|
||||
</content_rating>
|
||||
<releases>
|
||||
<release version="24.05.0" date="2024-05-23"/>
|
||||
<release version="24.02.2" date="2024-04-11"/>
|
||||
<release version="24.02.1" date="2024-03-21"/>
|
||||
<release version="24.02.0" date="2024-02-28">
|
||||
|
||||
1658
po/ar/neochat.po
1658
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
1488
po/ast/neochat.po
1488
po/ast/neochat.po
File diff suppressed because it is too large
Load Diff
1666
po/az/neochat.po
1666
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
1600
po/ca/neochat.po
1600
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1556
po/cs/neochat.po
1556
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
1580
po/da/neochat.po
1580
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
1660
po/de/neochat.po
1660
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
1681
po/el/neochat.po
1681
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
2678
po/en_GB/neochat.po
2678
po/en_GB/neochat.po
File diff suppressed because it is too large
Load Diff
1603
po/eo/neochat.po
1603
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
1612
po/es/neochat.po
1612
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
1654
po/eu/neochat.po
1654
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
1653
po/fi/neochat.po
1653
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
1631
po/fr/neochat.po
1631
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
1843
po/hu/neochat.po
1843
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
1613
po/ia/neochat.po
1613
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
1668
po/id/neochat.po
1668
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
1613
po/ie/neochat.po
1613
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
1664
po/it/neochat.po
1664
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
1488
po/ja/neochat.po
1488
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
1607
po/ka/neochat.po
1607
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
1648
po/ko/neochat.po
1648
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
1488
po/lt/neochat.po
1488
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
1643
po/lv/neochat.po
1643
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
1608
po/nl/neochat.po
1608
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
1641
po/nn/neochat.po
1641
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
1650
po/pa/neochat.po
1650
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
1693
po/pl/neochat.po
1693
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
1655
po/pt/neochat.po
1655
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
1666
po/pt_BR/neochat.po
1666
po/pt_BR/neochat.po
File diff suppressed because it is too large
Load Diff
1808
po/ru/neochat.po
1808
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
1660
po/sk/neochat.po
1660
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
1594
po/sl/neochat.po
1594
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
1659
po/sv/neochat.po
1659
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
1654
po/ta/neochat.po
1654
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
1562
po/tok/neochat.po
1562
po/tok/neochat.po
File diff suppressed because it is too large
Load Diff
1602
po/tr/neochat.po
1602
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
1622
po/uk/neochat.po
1622
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
1744
po/zh_CN/neochat.po
1744
po/zh_CN/neochat.po
File diff suppressed because it is too large
Load Diff
1586
po/zh_TW/neochat.po
1586
po/zh_TW/neochat.po
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,8 @@ add_library(neochat STATIC
|
||||
models/customemojimodel.h
|
||||
clipboard.cpp
|
||||
clipboard.h
|
||||
matriximageprovider.cpp
|
||||
matriximageprovider.h
|
||||
models/messageeventmodel.cpp
|
||||
models/messageeventmodel.h
|
||||
models/messagefiltermodel.cpp
|
||||
@@ -175,16 +177,6 @@ add_library(neochat STATIC
|
||||
foreigntypes.h
|
||||
models/threepidmodel.cpp
|
||||
models/threepidmodel.h
|
||||
threepidaddhelper.cpp
|
||||
threepidaddhelper.h
|
||||
jobs/neochatadd3pidjob.cpp
|
||||
jobs/neochatadd3pidjob.h
|
||||
identityserverhelper.cpp
|
||||
identityserverhelper.h
|
||||
enums/powerlevel.cpp
|
||||
enums/powerlevel.h
|
||||
models/permissionsmodel.cpp
|
||||
models/permissionsmodel.h
|
||||
)
|
||||
|
||||
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
|
||||
@@ -212,13 +204,20 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/InviteUserPage.qml
|
||||
qml/ImageEditorPage.qml
|
||||
qml/NeochatMaximizeComponent.qml
|
||||
qml/FancyEffectsContainer.qml
|
||||
qml/TypingPane.qml
|
||||
qml/QuickSwitcher.qml
|
||||
qml/HoverActions.qml
|
||||
qml/ChatBar.qml
|
||||
qml/AttachmentPane.qml
|
||||
qml/ReplyPane.qml
|
||||
qml/CompletionMenu.qml
|
||||
qml/PieProgressBar.qml
|
||||
qml/QuickFormatBar.qml
|
||||
qml/EmojiPicker.qml
|
||||
qml/UserDetailDialog.qml
|
||||
qml/CreateRoomDialog.qml
|
||||
qml/EmojiDialog.qml
|
||||
qml/OpenFileDialog.qml
|
||||
qml/KeyVerificationDialog.qml
|
||||
qml/ConfirmLogoutDialog.qml
|
||||
@@ -238,6 +237,9 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/ConfirmEncryptionDialog.qml
|
||||
qml/RemoveSheet.qml
|
||||
qml/BanSheet.qml
|
||||
qml/EmojiTonesPicker.qml
|
||||
qml/EmojiDelegate.qml
|
||||
qml/EmojiGrid.qml
|
||||
qml/RoomSearchPage.qml
|
||||
qml/LocationChooser.qml
|
||||
qml/TimelineView.qml
|
||||
@@ -261,6 +263,7 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/SelectParentDialog.qml
|
||||
qml/QrCodeMaximizeComponent.qml
|
||||
qml/SelectSpacesDialog.qml
|
||||
qml/AttachDialog.qml
|
||||
qml/NotificationsView.qml
|
||||
qml/SearchPage.qml
|
||||
qml/ServerComboBox.qml
|
||||
@@ -278,16 +281,15 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/ConfirmLeaveDialog.qml
|
||||
qml/CodeMaximizeComponent.qml
|
||||
qml/EditStateDialog.qml
|
||||
qml/ConsentDialog.qml
|
||||
qml/AskDirectChatConfirmation.qml
|
||||
qml/HoverLinkIndicator.qml
|
||||
RESOURCES
|
||||
qml/confetti.png
|
||||
qml/glowdot.png
|
||||
)
|
||||
|
||||
add_subdirectory(settings)
|
||||
add_subdirectory(timeline)
|
||||
add_subdirectory(devtools)
|
||||
add_subdirectory(login)
|
||||
add_subdirectory(chatbar)
|
||||
|
||||
if(UNIX)
|
||||
qt_target_qml_sources(neochat QML_FILES qml/ShareAction.qml)
|
||||
@@ -370,18 +372,16 @@ endif()
|
||||
|
||||
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_RUNNER)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_X11=1)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_X11)
|
||||
target_sources(neochat PRIVATE runner.cpp)
|
||||
|
||||
if (TARGET KUnifiedPush)
|
||||
target_sources(neochat PRIVATE fakerunner.cpp)
|
||||
endif()
|
||||
else()
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_X11=0)
|
||||
endif()
|
||||
|
||||
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models ${CMAKE_CURRENT_SOURCE_DIR}/enums)
|
||||
target_link_libraries(neochat PRIVATE settingsplugin timelineplugin devtoolsplugin loginplugin chatbarplugin)
|
||||
target_link_libraries(neochat PRIVATE settingsplugin timelineplugin devtoolsplugin loginplugin)
|
||||
target_link_libraries(neochat PUBLIC
|
||||
Qt::Core
|
||||
Qt::Quick
|
||||
|
||||
@@ -91,7 +91,7 @@ void ActionsHandler::handleMessage(const QString &text, QString handledText, Cha
|
||||
|
||||
for (auto it = m_room->messageEvents().crbegin(); it != m_room->messageEvents().crend(); it++) {
|
||||
if (const auto event = eventCast<const RoomMessageEvent>(&**it)) {
|
||||
if (event->senderId() == m_room->localUser()->id() && event->hasTextContent()) {
|
||||
if (event->senderId() == m_room->localMember().id() && event->hasTextContent()) {
|
||||
QString originalString;
|
||||
if (event->content()) {
|
||||
originalString = static_cast<const Quotient::EventContent::TextContent *>(event->content())->body;
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
qt_add_library(chatbar STATIC)
|
||||
qt_add_qml_module(chatbar
|
||||
URI org.kde.neochat.chatbar
|
||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/chatbar
|
||||
QML_FILES
|
||||
AttachDialog.qml
|
||||
ChatBar.qml
|
||||
CompletionMenu.qml
|
||||
EmojiDelegate.qml
|
||||
EmojiGrid.qml
|
||||
ReplyPane.qml
|
||||
PieProgressBar.qml
|
||||
EmojiPicker.qml
|
||||
EmojiDialog.qml
|
||||
EmojiTonesPicker.qml
|
||||
)
|
||||
@@ -9,20 +9,28 @@
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QNetworkProxy>
|
||||
#include <QQuickTextDocument>
|
||||
#include <QQuickWindow>
|
||||
#include <QStandardPaths>
|
||||
#include <QStringBuilder>
|
||||
#include <QTimer>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <Quotient/accountregistry.h>
|
||||
#include <Quotient/connection.h>
|
||||
#include <Quotient/csapi/logout.h>
|
||||
#include <Quotient/csapi/notifications.h>
|
||||
#include <Quotient/eventstats.h>
|
||||
#include <Quotient/qt_connection_util.h>
|
||||
#include <Quotient/settings.h>
|
||||
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
#include "notificationsmanager.h"
|
||||
#include "proxycontroller.h"
|
||||
#include "roommanager.h"
|
||||
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
|
||||
#include "trayicon.h"
|
||||
@@ -38,10 +46,6 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
#include <kunifiedpush/connector.h>
|
||||
#endif
|
||||
|
||||
bool testMode = false;
|
||||
|
||||
using namespace Quotient;
|
||||
@@ -186,7 +190,7 @@ void Controller::invokeLogin()
|
||||
m_accountsLoading += accountId;
|
||||
Q_EMIT accountsLoadingChanged();
|
||||
if (!account.homeserver().isEmpty()) {
|
||||
auto accessTokenLoadingJob = loadAccessTokenFromKeyChain(account.userId());
|
||||
auto accessTokenLoadingJob = loadAccessTokenFromKeyChain(account);
|
||||
connect(accessTokenLoadingJob, &QKeychain::Job::finished, this, [accountId, this, accessTokenLoadingJob](QKeychain::Job *) {
|
||||
AccountSettings account{accountId};
|
||||
QString accessToken;
|
||||
@@ -214,11 +218,11 @@ void Controller::invokeLogin()
|
||||
}
|
||||
}
|
||||
|
||||
QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const QString &userId)
|
||||
QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const AccountSettings &account)
|
||||
{
|
||||
qDebug() << "Reading access token from the keychain for" << userId;
|
||||
qDebug() << "Reading access token from the keychain for" << account.userId();
|
||||
auto job = new QKeychain::ReadPasswordJob(qAppName(), this);
|
||||
job->setKey(userId);
|
||||
job->setKey(account.userId());
|
||||
|
||||
// Handling of errors
|
||||
connect(job, &QKeychain::Job::finished, this, [this, job]() {
|
||||
@@ -249,12 +253,12 @@ QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const QStrin
|
||||
return job;
|
||||
}
|
||||
|
||||
bool Controller::saveAccessTokenToKeyChain(const QString &userId, const QByteArray &accessToken)
|
||||
bool Controller::saveAccessTokenToKeyChain(const AccountSettings &account, const QByteArray &accessToken)
|
||||
{
|
||||
qDebug() << "Save the access token to the keychain for " << userId;
|
||||
qDebug() << "Save the access token to the keychain for " << account.userId();
|
||||
QKeychain::WritePasswordJob job(qAppName());
|
||||
job.setAutoDelete(false);
|
||||
job.setKey(userId);
|
||||
job.setKey(account.userId());
|
||||
job.setBinaryData(accessToken);
|
||||
QEventLoop loop;
|
||||
QKeychain::WritePasswordJob::connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
|
||||
|
||||
@@ -5,9 +5,15 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickItem>
|
||||
|
||||
#include "neochatconnection.h"
|
||||
#include <Quotient/accountregistry.h>
|
||||
#include <Quotient/settings.h>
|
||||
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
#include <kunifiedpush/connector.h>
|
||||
#endif
|
||||
|
||||
class TrayIcon;
|
||||
class QQuickTextDocument;
|
||||
@@ -76,7 +82,7 @@ public:
|
||||
/**
|
||||
* @brief Save an access token to the keychain for the given account.
|
||||
*/
|
||||
bool saveAccessTokenToKeyChain(const QString &userId, const QByteArray &accessToken);
|
||||
bool saveAccessTokenToKeyChain(const Quotient::AccountSettings &account, const QByteArray &accessToken);
|
||||
|
||||
[[nodiscard]] bool supportSystemTray() const;
|
||||
|
||||
@@ -104,14 +110,14 @@ private:
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
TrayIcon *m_trayIcon = nullptr;
|
||||
|
||||
QKeychain::ReadPasswordJob *loadAccessTokenFromKeyChain(const QString &account);
|
||||
QKeychain::ReadPasswordJob *loadAccessTokenFromKeyChain(const Quotient::AccountSettings &account);
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings() const;
|
||||
|
||||
Quotient::AccountRegistry m_accountRegistry;
|
||||
QStringList m_accountsLoading;
|
||||
QMap<QString, QPointer<NeoChatConnection>> m_connectionsLoading;
|
||||
QMap<QString, QPointer<Quotient::Connection>> m_connectionsLoading;
|
||||
QString m_endpoint;
|
||||
|
||||
private Q_SLOTS:
|
||||
|
||||
@@ -15,12 +15,6 @@ FormCard.FormCardPage {
|
||||
FormCard.FormCard {
|
||||
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18nc("@option:check", "Show hidden events in the timeline")
|
||||
checked: Config.showAllEvents
|
||||
|
||||
onToggled: Config.showAllEvents = checked
|
||||
}
|
||||
FormCard.FormCheckDelegate {
|
||||
id: roomAccountDataVisibleCheck
|
||||
text: i18nc("@option:check Enable the matrix 'threads' feature", "Always allow device verification")
|
||||
@@ -29,14 +23,5 @@ FormCard.FormCardPage {
|
||||
|
||||
onToggled: Config.alwaysVerifyDevice = checked
|
||||
}
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18nc("@option:check", "Show focus in window header")
|
||||
checked: Config.windowTitleFocus
|
||||
|
||||
onToggled: {
|
||||
Config.windowTitleFocus = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,12 +31,12 @@ FormCard.FormCardPage {
|
||||
implicitWidth: tabBar.tabWidth
|
||||
}
|
||||
QQC2.TabButton {
|
||||
text: i18nc("@title:tab", "Room Data")
|
||||
text: qsTr("Room Data")
|
||||
|
||||
implicitWidth: tabBar.tabWidth
|
||||
}
|
||||
QQC2.TabButton {
|
||||
text: i18nc("@title:tab", "Server Info")
|
||||
text: qsTr("Server Info")
|
||||
|
||||
implicitWidth: tabBar.tabWidth
|
||||
}
|
||||
|
||||
@@ -28,14 +28,5 @@ FormCard.FormCardPage {
|
||||
|
||||
onToggled: Config.secretBackup = checked
|
||||
}
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18nc("@option:check Enable the matrix feature to add a phone number as a third party ID", "Add phone numbers as 3PIDs")
|
||||
checked: Config.phone3PId
|
||||
|
||||
onToggled: {
|
||||
Config.phone3PId = checked
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,10 +25,9 @@ ColumnLayout {
|
||||
text: i18n("Room")
|
||||
textRole: "escapedDisplayName"
|
||||
valueRole: "roomId"
|
||||
displayText: RoomManager.roomListModel.data(RoomManager.roomListModel.index(currentIndex, 0), RoomListModel.EscapedDisplayNameRole)
|
||||
displayText: RoomManager.roomListModel.data(RoomManager.roomListModel.index(currentIndex, 0), RoomListModel.DisplayNameRole)
|
||||
model: RoomManager.roomListModel
|
||||
currentIndex: 0
|
||||
displayMode: FormCard.FormComboBoxDelegate.Page
|
||||
Component.onCompleted: currentIndex = RoomManager.roomListModel.rowForRoom(root.room)
|
||||
onCurrentValueChanged: root.room = RoomManager.roomListModel.roomByAliasOrId(roomComboBox.currentValue)
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ public:
|
||||
* @brief Defines the room list categories a room can be assigned.
|
||||
*/
|
||||
enum Types {
|
||||
Search = 0, /**< So we can show a search delegate if needed, e.g. collapsed mode. */
|
||||
Invited, /**< The user has been invited to the room. */
|
||||
Favorite, /**< The room is set as a favourite. */
|
||||
Direct, /**< The room is a direct chat. */
|
||||
@@ -67,6 +68,8 @@ public:
|
||||
return i18n("Low priority");
|
||||
case NeoChatRoomType::Space:
|
||||
return i18n("Spaces");
|
||||
case NeoChatRoomType::Search:
|
||||
return i18n("Search");
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
@@ -86,6 +89,8 @@ public:
|
||||
return QStringLiteral("object-order-lower");
|
||||
case NeoChatRoomType::Space:
|
||||
return QStringLiteral("group");
|
||||
case NeoChatRoomType::Search:
|
||||
return QStringLiteral("search");
|
||||
default:
|
||||
return QStringLiteral("tools-report-bug");
|
||||
}
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 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 "powerlevel.h"
|
||||
|
||||
QString PowerLevel::nameForLevel(Level level)
|
||||
{
|
||||
switch (level) {
|
||||
case PowerLevel::Member:
|
||||
return i18n("Member");
|
||||
case PowerLevel::Moderator:
|
||||
return i18n("Moderator");
|
||||
case PowerLevel::Admin:
|
||||
return i18n("Admin");
|
||||
case PowerLevel::Mute:
|
||||
return i18n("Mute");
|
||||
case PowerLevel::Custom:
|
||||
return i18n("Custom");
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
int PowerLevel::valueForLevel(Level level)
|
||||
{
|
||||
switch (level) {
|
||||
case PowerLevel::Member:
|
||||
return 0;
|
||||
case PowerLevel::Moderator:
|
||||
return 50;
|
||||
case PowerLevel::Admin:
|
||||
return 100;
|
||||
case PowerLevel::Mute:
|
||||
return -1;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
PowerLevel::Level PowerLevel::levelForValue(int value)
|
||||
{
|
||||
switch (value) {
|
||||
case 0:
|
||||
return PowerLevel::Member;
|
||||
case 50:
|
||||
return PowerLevel::Moderator;
|
||||
case 100:
|
||||
return PowerLevel::Admin;
|
||||
case -1:
|
||||
return PowerLevel::Mute;
|
||||
default:
|
||||
return PowerLevel::Custom;
|
||||
}
|
||||
}
|
||||
|
||||
PowerLevelModel::PowerLevelModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
bool PowerLevelModel::showMute() const
|
||||
{
|
||||
return m_showMute;
|
||||
}
|
||||
|
||||
void PowerLevelModel::setShowMute(bool showMute)
|
||||
{
|
||||
if (showMute == m_showMute) {
|
||||
return;
|
||||
}
|
||||
m_showMute = showMute;
|
||||
Q_EMIT showMuteChanged();
|
||||
}
|
||||
|
||||
QVariant PowerLevelModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
return {};
|
||||
}
|
||||
if (index.row() >= rowCount()) {
|
||||
qDebug() << "PowerLevelModel, something's wrong: index.row() >= m_rules.count()";
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto level = static_cast<PowerLevel::Level>(index.row());
|
||||
if (role == NameRole) {
|
||||
return i18nc("%1 is the name of the power level, e.g. admin and %2 is the value that represents.",
|
||||
"%1 (%2)",
|
||||
PowerLevel::nameForLevel(level),
|
||||
PowerLevel::valueForLevel(level));
|
||||
}
|
||||
if (role == ValueRole) {
|
||||
return PowerLevel::valueForLevel(level);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
int PowerLevelModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return PowerLevel::NUMLevels - (m_showMute ? 0 : 1);
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> PowerLevelModel::roleNames() const
|
||||
{
|
||||
return {{NameRole, "name"}, {ValueRole, "value"}};
|
||||
}
|
||||
|
||||
#include "moc_powerlevel.cpp"
|
||||
@@ -1,110 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
/**
|
||||
* @class PowerLevel
|
||||
*
|
||||
* This class is designed to define the PowerLevel enumeration.
|
||||
*/
|
||||
class PowerLevel : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief The type of delegate that is needed for the event.
|
||||
*
|
||||
* @note While similar this is not the matrix event or message type. This is
|
||||
* to tell a QML ListView what delegate to show for each event. So while
|
||||
* similar to the spec it is not the same.
|
||||
*/
|
||||
enum Level {
|
||||
Member, /**< A basic member. */
|
||||
Moderator, /**< A moderator with enhanced powers. */
|
||||
Admin, /**< The highest power level in the room. */
|
||||
Mute, /**< The level to remove posting privileges. */
|
||||
NUMLevels,
|
||||
Custom, /**< A non-standard value. Intentionally after NUMLevels so it doesn't appear in the model. */
|
||||
};
|
||||
Q_ENUM(Level);
|
||||
|
||||
/**
|
||||
* @brief Return a string representation of the enum value.
|
||||
*/
|
||||
static QString nameForLevel(Level level);
|
||||
|
||||
/**
|
||||
* @brief Return the integer representation of the enum value.
|
||||
*/
|
||||
static int valueForLevel(Level level);
|
||||
|
||||
/**
|
||||
* @brief Return the enum value for the given integer power level.
|
||||
*/
|
||||
static Level levelForValue(int value);
|
||||
};
|
||||
|
||||
/**
|
||||
* @class PowerLevelModel
|
||||
*
|
||||
* A model visualize the allowed power levels.
|
||||
*/
|
||||
class PowerLevelModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
Q_PROPERTY(bool showMute READ showMute WRITE setShowMute NOTIFY showMuteChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the model roles.
|
||||
*/
|
||||
enum Roles {
|
||||
NameRole = Qt::DisplayRole, /**< The power level name. */
|
||||
ValueRole, /**< The power level value. */
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
|
||||
explicit PowerLevelModel(QObject *parent = nullptr);
|
||||
|
||||
[[nodiscard]] bool showMute() const;
|
||||
void setShowMute(bool showMute);
|
||||
|
||||
/**
|
||||
* @brief Get the given role value at the given index.
|
||||
*
|
||||
* @sa QAbstractItemModel::data
|
||||
*/
|
||||
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
/**
|
||||
* @brief Number of rows in the model.
|
||||
*
|
||||
* @sa QAbstractItemModel::rowCount
|
||||
*/
|
||||
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
/**
|
||||
* @brief Returns a mapping from Role enum values to role names.
|
||||
*
|
||||
* @sa Roles, QAbstractItemModel::roleNames()
|
||||
*/
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void showMuteChanged();
|
||||
|
||||
private:
|
||||
bool m_showMute = true;
|
||||
};
|
||||
@@ -73,7 +73,7 @@ QVariantMap EventHandler::getAuthor(bool isPending) const
|
||||
return m_room->getUser(nullptr);
|
||||
}
|
||||
|
||||
const auto author = isPending ? m_room->localUser() : m_room->user(m_event->senderId());
|
||||
const auto author = isPending ? m_room->localMember() : m_room->user(m_event->senderId());
|
||||
return m_room->getUser(author);
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ QString EventHandler::getAuthorDisplayName(bool isPending) const
|
||||
}
|
||||
return previousDisplayName;
|
||||
} else {
|
||||
const auto author = isPending ? m_room->localUser() : m_room->user(m_event->senderId());
|
||||
const auto author = isPending ? m_room->localMember() : m_room->user(m_event->senderId());
|
||||
return m_room->htmlSafeMemberName(author->id());
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,7 @@ QString EventHandler::singleLineAuthorDisplayname(bool isPending) const
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto author = isPending ? m_room->localUser() : m_room->user(m_event->senderId());
|
||||
const auto author = isPending ? m_room->localMember() : m_room->user(m_event->senderId());
|
||||
auto displayName = m_room->safeMemberName(author->id());
|
||||
displayName.replace(QStringLiteral("<br>\n"), QStringLiteral(" "));
|
||||
displayName.replace(QStringLiteral("<br>"), QStringLiteral(" "));
|
||||
@@ -293,10 +293,7 @@ QString EventHandler::getMarkdownBody() const
|
||||
}
|
||||
|
||||
const auto roomMessageEvent = eventCast<const RoomMessageEvent>(m_event);
|
||||
|
||||
QString plainBody = roomMessageEvent->plainBody();
|
||||
plainBody.remove(TextRegex::removeReply);
|
||||
return plainBody;
|
||||
return roomMessageEvent->plainBody();
|
||||
}
|
||||
|
||||
QString EventHandler::getBody(const Quotient::RoomEvent *event, Qt::TextFormat format, bool stripNewlines) const
|
||||
@@ -966,7 +963,7 @@ bool EventHandler::hasReadMarkers() const
|
||||
}
|
||||
|
||||
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds.remove(m_room->localUser()->id());
|
||||
userIds.remove(m_room->localMember().id());
|
||||
return userIds.size() > 0;
|
||||
}
|
||||
|
||||
@@ -982,7 +979,7 @@ QVariantList EventHandler::getReadMarkers(int maxMarkers) const
|
||||
}
|
||||
|
||||
auto userIds_temp = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds_temp.remove(m_room->localUser()->id());
|
||||
userIds_temp.remove(m_room->localMember().id());
|
||||
|
||||
auto userIds = userIds_temp.values();
|
||||
if (userIds.count() > maxMarkers) {
|
||||
@@ -1011,7 +1008,7 @@ QString EventHandler::getNumberExcessReadMarkers(int maxMarkers) const
|
||||
}
|
||||
|
||||
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds.remove(m_room->localUser()->id());
|
||||
userIds.remove(m_room->localMember().id());
|
||||
|
||||
if (userIds.count() > maxMarkers) {
|
||||
return QStringLiteral("+ ") + QString::number(userIds.count() - maxMarkers);
|
||||
@@ -1032,7 +1029,7 @@ QString EventHandler::getReadMarkersString() const
|
||||
}
|
||||
|
||||
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds.remove(m_room->localUser()->id());
|
||||
userIds.remove(m_room->localMember().id());
|
||||
|
||||
/**
|
||||
* The string ends up in the form
|
||||
@@ -1041,11 +1038,7 @@ QString EventHandler::getReadMarkersString() const
|
||||
QString readMarkersString = i18np("1 user: ", "%1 users: ", userIds.size());
|
||||
for (const auto &userId : userIds) {
|
||||
auto user = m_room->user(userId);
|
||||
auto displayName = user->displayname(m_room);
|
||||
if (displayName.isEmpty()) {
|
||||
displayName = userId;
|
||||
}
|
||||
readMarkersString += displayName + i18nc("list separator", ", ");
|
||||
readMarkersString += user->displayname(m_room) + i18nc("list separator", ", ");
|
||||
}
|
||||
readMarkersString.chop(2);
|
||||
return readMarkersString;
|
||||
|
||||
@@ -33,4 +33,4 @@ FakeRunner::FakeRunner()
|
||||
qDBusRegisterMetaType<RemoteImage>();
|
||||
}
|
||||
|
||||
#include "moc_fakerunner.cpp"
|
||||
#include "moc_fakerunner.cpp"
|
||||
@@ -1,156 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 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 "identityserverhelper.h"
|
||||
|
||||
#include <QNetworkReply>
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <Quotient/networkaccessmanager.h>
|
||||
|
||||
#include "neochatconnection.h"
|
||||
|
||||
IdentityServerHelper::IdentityServerHelper(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
NeoChatConnection *IdentityServerHelper::connection() const
|
||||
{
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
void IdentityServerHelper::setConnection(NeoChatConnection *connection)
|
||||
{
|
||||
if (m_connection == connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_connection != nullptr) {
|
||||
m_connection->disconnect(this);
|
||||
}
|
||||
|
||||
m_connection = connection;
|
||||
Q_EMIT connectionChanged();
|
||||
Q_EMIT currentServerChanged();
|
||||
|
||||
connect(m_connection, &NeoChatConnection::accountDataChanged, this, [this](const QString &type) {
|
||||
if (type == QLatin1String("m.identity_server")) {
|
||||
Q_EMIT currentServerChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QString IdentityServerHelper::currentServer() const
|
||||
{
|
||||
if (m_connection == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!m_connection->hasAccountData(QLatin1String("m.identity_server"))) {
|
||||
return i18nc("@info", "No identity server configured");
|
||||
}
|
||||
|
||||
const auto url = m_connection->accountData(QLatin1String("m.identity_server"))->contentPart<QUrl>(QLatin1String("base_url"));
|
||||
if (!url.isEmpty()) {
|
||||
return url.toString();
|
||||
}
|
||||
return i18nc("@info", "No identity server configured");
|
||||
}
|
||||
|
||||
bool IdentityServerHelper::hasCurrentServer() const
|
||||
{
|
||||
if (m_connection == nullptr && !m_connection->hasAccountData(QLatin1String("m.identity_server"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto url = m_connection->accountData(QLatin1String("m.identity_server"))->contentPart<QUrl>(QLatin1String("base_url"));
|
||||
if (!url.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QString IdentityServerHelper::url() const
|
||||
{
|
||||
return m_url;
|
||||
}
|
||||
|
||||
void IdentityServerHelper::setUrl(const QString &url)
|
||||
{
|
||||
if (url == m_url) {
|
||||
return;
|
||||
}
|
||||
m_url = url;
|
||||
Q_EMIT urlChanged();
|
||||
|
||||
checkUrl();
|
||||
}
|
||||
|
||||
IdentityServerHelper::IdServerStatus IdentityServerHelper::status() const
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
|
||||
void IdentityServerHelper::checkUrl()
|
||||
{
|
||||
if (m_idServerCheckRequest != nullptr) {
|
||||
m_idServerCheckRequest->abort();
|
||||
m_idServerCheckRequest.clear();
|
||||
}
|
||||
|
||||
if (m_url == currentServer()) {
|
||||
m_status = Match;
|
||||
Q_EMIT statusChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_url.isEmpty()) {
|
||||
m_status = Valid;
|
||||
Q_EMIT statusChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto requestUrl = QUrl(m_url + QStringLiteral("/_matrix/identity/v2"));
|
||||
if (!(requestUrl.scheme() == QStringLiteral("https") || requestUrl.scheme() == QStringLiteral("http"))) {
|
||||
m_status = Invalid;
|
||||
Q_EMIT statusChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
QNetworkRequest request(requestUrl);
|
||||
m_idServerCheckRequest = Quotient::NetworkAccessManager::instance()->get(request);
|
||||
connect(m_idServerCheckRequest, &QNetworkReply::finished, this, [this]() {
|
||||
if (m_idServerCheckRequest->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200) {
|
||||
m_status = Valid;
|
||||
Q_EMIT statusChanged();
|
||||
} else {
|
||||
m_status = Invalid;
|
||||
Q_EMIT statusChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void IdentityServerHelper::setIdentityServer()
|
||||
{
|
||||
if (m_url == currentServer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_connection->setAccountData(QLatin1String("m.identity_server"), {{QLatin1String("base_url"), m_url}});
|
||||
m_status = Ready;
|
||||
Q_EMIT statusChanged();
|
||||
}
|
||||
|
||||
void IdentityServerHelper::clearIdentityServer()
|
||||
{
|
||||
if (currentServer().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
m_connection->setAccountData(QLatin1String("m.identity_server"), {{QLatin1String("base_url"), QString()}});
|
||||
m_status = Ready;
|
||||
Q_EMIT statusChanged();
|
||||
}
|
||||
|
||||
#include "moc_identityserverhelper.cpp"
|
||||
@@ -1,103 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
|
||||
class NeoChatConnection;
|
||||
|
||||
/**
|
||||
* @class IdentityServerHelper
|
||||
*
|
||||
* This class is designed to help the process of setting an identity server for the account.
|
||||
* It will manage the various stages of verification and authentication.
|
||||
*/
|
||||
class IdentityServerHelper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
/**
|
||||
* @brief The connection to add a 3PID to.
|
||||
*/
|
||||
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||
|
||||
/**
|
||||
* @brief The current identity server.
|
||||
*/
|
||||
Q_PROPERTY(QString currentServer READ currentServer NOTIFY currentServerChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether an identity server is currently configured.
|
||||
*/
|
||||
Q_PROPERTY(bool hasCurrentServer READ hasCurrentServer NOTIFY currentServerChanged)
|
||||
|
||||
/**
|
||||
* @brief The URL for the desired server.
|
||||
*/
|
||||
Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged)
|
||||
|
||||
/**
|
||||
* @brief The current status.
|
||||
*/
|
||||
Q_PROPERTY(IdServerStatus status READ status NOTIFY statusChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief The current status for adding an identity server
|
||||
*/
|
||||
enum IdServerStatus {
|
||||
Ready, /**< The process is ready to start. I.e. there is no ongoing attempt to set a new server. */
|
||||
Valid, /**< The server URL is valid. */
|
||||
Invalid, /**< The server URL is invalid. */
|
||||
Match, /**< The server URL is the one that is already configured. */
|
||||
Other, /**< An unknown problem occurred. */
|
||||
};
|
||||
Q_ENUM(IdServerStatus)
|
||||
|
||||
explicit IdentityServerHelper(QObject *parent = nullptr);
|
||||
|
||||
[[nodiscard]] NeoChatConnection *connection() const;
|
||||
void setConnection(NeoChatConnection *connection);
|
||||
|
||||
[[nodiscard]] QString currentServer() const;
|
||||
|
||||
[[nodiscard]] bool hasCurrentServer() const;
|
||||
|
||||
[[nodiscard]] QString url() const;
|
||||
void setUrl(const QString &url);
|
||||
|
||||
[[nodiscard]] IdServerStatus status() const;
|
||||
|
||||
/**
|
||||
* @brief Set the current URL as the user's identity server.
|
||||
*
|
||||
* Will do nothing if the URL isn't a valid identity server.
|
||||
*/
|
||||
Q_INVOKABLE void setIdentityServer();
|
||||
|
||||
/**
|
||||
* @brief Clear the user's identity server.
|
||||
*/
|
||||
Q_INVOKABLE void clearIdentityServer();
|
||||
|
||||
Q_SIGNALS:
|
||||
void connectionChanged();
|
||||
void currentServerChanged();
|
||||
void urlChanged();
|
||||
void statusChanged();
|
||||
|
||||
private:
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
|
||||
IdServerStatus m_status = Ready;
|
||||
QString m_url;
|
||||
|
||||
QPointer<QNetworkReply> m_idServerCheckRequest;
|
||||
|
||||
void checkUrl();
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 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 "neochatadd3pidjob.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
NeochatAdd3PIdJob::NeochatAdd3PIdJob(const QString &clientSecret, const QString &sid, const Omittable<QJsonObject> &auth)
|
||||
: BaseJob(HttpVerb::Post, QStringLiteral("Add3PIDJob"), makePath("/_matrix/client/v3", "/account/3pid/add"))
|
||||
{
|
||||
QJsonObject _dataJson;
|
||||
addParam<IfNotEmpty>(_dataJson, QStringLiteral("auth"), auth);
|
||||
addParam<>(_dataJson, QStringLiteral("client_secret"), clientSecret);
|
||||
addParam<>(_dataJson, QStringLiteral("sid"), sid);
|
||||
setRequestData({_dataJson});
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
#include <Quotient/omittable.h>
|
||||
|
||||
class NeochatAdd3PIdJob : public Quotient::BaseJob
|
||||
{
|
||||
public:
|
||||
explicit NeochatAdd3PIdJob(const QString &clientSecret, const QString &sid, const Quotient::Omittable<QJsonObject> &auth = Quotient::none);
|
||||
};
|
||||
@@ -89,23 +89,38 @@ bool LinkPreviewer::empty() const
|
||||
return m_url.isEmpty();
|
||||
}
|
||||
|
||||
QList<QUrl> LinkPreviewer::linkPreviews(QString string)
|
||||
QUrl LinkPreviewer::linkPreview(const Quotient::RoomMessageEvent *event)
|
||||
{
|
||||
auto data = string.remove(TextRegex::removeRichReply);
|
||||
if (event == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QString text;
|
||||
if (event->hasTextContent()) {
|
||||
auto textContent = static_cast<const Quotient::EventContent::TextContent *>(event->content());
|
||||
if (textContent) {
|
||||
text = textContent->body;
|
||||
} else {
|
||||
text = event->plainBody();
|
||||
}
|
||||
} else {
|
||||
text = event->plainBody();
|
||||
}
|
||||
|
||||
auto data = text.remove(TextRegex::removeRichReply);
|
||||
auto linksMatch = TextRegex::url.globalMatch(data);
|
||||
QList<QUrl> links;
|
||||
while (linksMatch.hasNext()) {
|
||||
auto link = linksMatch.next().captured();
|
||||
if (!link.contains(QStringLiteral("matrix.to")) && !links.contains(QUrl(link))) {
|
||||
links += QUrl(link);
|
||||
if (!link.contains(QStringLiteral("matrix.to"))) {
|
||||
return QUrl(link);
|
||||
}
|
||||
}
|
||||
return links;
|
||||
return {};
|
||||
}
|
||||
|
||||
bool LinkPreviewer::hasPreviewableLinks(const QString &string)
|
||||
bool LinkPreviewer::hasPreviewableLinks(const Quotient::RoomMessageEvent *event)
|
||||
{
|
||||
return !linkPreviews(string).isEmpty();
|
||||
return !linkPreview(event).isEmpty();
|
||||
}
|
||||
|
||||
#include "moc_linkpreviewer.cpp"
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
#include <QQmlEngine>
|
||||
#include <QUrl>
|
||||
|
||||
namespace Quotient
|
||||
{
|
||||
class RoomMessageEvent;
|
||||
}
|
||||
|
||||
class NeoChatRoom;
|
||||
|
||||
/**
|
||||
@@ -66,19 +71,19 @@ public:
|
||||
[[nodiscard]] bool empty() const;
|
||||
|
||||
/**
|
||||
* @brief Whether the given string has at least 1 pre-viewable link.
|
||||
* @brief Whether the given event has at least 1 pre-viewable link.
|
||||
*
|
||||
* A link is only pre-viewable if it is http, https or something starting with www.
|
||||
*/
|
||||
static bool hasPreviewableLinks(const QString &string);
|
||||
static bool hasPreviewableLinks(const Quotient::RoomMessageEvent *event);
|
||||
|
||||
/**
|
||||
* @brief Return previewable links from the given string.
|
||||
* @brief Return the link to be previewed from the given event.
|
||||
*
|
||||
* This function is designed to give only links that should be previewed so
|
||||
* http, https or something starting with www. The first valid link is returned.
|
||||
*/
|
||||
static QList<QUrl> linkPreviews(QString string);
|
||||
static QUrl linkPreview(const Quotient::RoomMessageEvent *event);
|
||||
|
||||
private:
|
||||
bool m_loaded;
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#include "login.h"
|
||||
|
||||
#include <Quotient/accountregistry.h>
|
||||
#include <Quotient/connection.h>
|
||||
#include <Quotient/qt_connection_util.h>
|
||||
#include <Quotient/settings.h>
|
||||
|
||||
#include "controller.h"
|
||||
|
||||
@@ -54,7 +54,7 @@ void LoginHelper::init()
|
||||
m_connection = new NeoChatConnection();
|
||||
}
|
||||
m_connection->resolveServer(m_matrixId);
|
||||
connectSingleShot(m_connection.get(), &Connection::loginFlowsChanged, this, [this]() {
|
||||
connectSingleShot(m_connection, &Connection::loginFlowsChanged, this, [this]() {
|
||||
setHomeserverReachable(true);
|
||||
m_testing = false;
|
||||
Q_EMIT testingChanged();
|
||||
@@ -73,7 +73,7 @@ void LoginHelper::init()
|
||||
account.setHomeserver(m_connection->homeserver());
|
||||
account.setDeviceId(m_connection->deviceId());
|
||||
account.setDeviceName(m_deviceName);
|
||||
if (!Controller::instance().saveAccessTokenToKeyChain(account.userId(), m_connection->accessToken())) {
|
||||
if (!Controller::instance().saveAccessTokenToKeyChain(account, m_connection->accessToken())) {
|
||||
qWarning() << "Couldn't save access token";
|
||||
}
|
||||
account.sync();
|
||||
@@ -100,7 +100,7 @@ void LoginHelper::init()
|
||||
Q_EMIT Controller::instance().errorOccured(i18n("Network Error"), std::move(error));
|
||||
});
|
||||
|
||||
connectSingleShot(m_connection.get(), &Connection::syncDone, this, [this]() {
|
||||
connectSingleShot(m_connection, &Connection::syncDone, this, [this]() {
|
||||
Q_EMIT loaded();
|
||||
});
|
||||
}
|
||||
@@ -182,7 +182,7 @@ QUrl LoginHelper::ssoUrl() const
|
||||
void LoginHelper::loginWithSso()
|
||||
{
|
||||
m_connection->resolveServer(m_matrixId);
|
||||
connectSingleShot(m_connection.get(), &Connection::loginFlowsChanged, this, [this]() {
|
||||
connectSingleShot(m_connection, &Connection::loginFlowsChanged, this, [this]() {
|
||||
SsoSession *session = m_connection->prepareForSso(m_deviceName);
|
||||
m_ssoUrl = session->ssoUrl();
|
||||
Q_EMIT ssoUrlChanged();
|
||||
|
||||
@@ -146,7 +146,7 @@ private:
|
||||
QString m_deviceName;
|
||||
bool m_supportsSso = false;
|
||||
bool m_supportsPassword = false;
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
NeoChatConnection *m_connection = nullptr;
|
||||
QUrl m_ssoUrl;
|
||||
bool m_testing = false;
|
||||
bool m_isLoggingIn = false;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <QIcon>
|
||||
#include <QNetworkDiskCache>
|
||||
#include <QNetworkProxyFactory>
|
||||
#include <QNetworkReply>
|
||||
#include <QObject>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlContext>
|
||||
@@ -47,10 +46,11 @@
|
||||
#include "colorschemer.h"
|
||||
#include "controller.h"
|
||||
#include "logger.h"
|
||||
#include "matriximageprovider.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "roommanager.h"
|
||||
#include "sharehandler.h"
|
||||
#include "windowcontroller.h"
|
||||
#include "sharehandler.h"
|
||||
|
||||
#ifdef HAVE_RUNNER
|
||||
#include "runner.h"
|
||||
@@ -236,7 +236,6 @@ int main(int argc, char *argv[])
|
||||
Q_IMPORT_QML_PLUGIN(org_kde_neochat_timelinePlugin)
|
||||
Q_IMPORT_QML_PLUGIN(org_kde_neochat_devtoolsPlugin)
|
||||
Q_IMPORT_QML_PLUGIN(org_kde_neochat_loginPlugin)
|
||||
Q_IMPORT_QML_PLUGIN(org_kde_neochat_chatbarPlugin)
|
||||
|
||||
qml_register_types_org_kde_neochat();
|
||||
|
||||
@@ -286,6 +285,7 @@ int main(int argc, char *argv[])
|
||||
ShareHandler::instance().setText(parser.value(shareOption));
|
||||
}
|
||||
|
||||
engine.addImageProvider(QLatin1String("mxc"), MatrixImageProvider::create(&engine, &engine));
|
||||
engine.addImageProvider(QLatin1String("blurhash"), new BlurhashImageProvider);
|
||||
|
||||
engine.loadFromModule("org.kde.neochat", "Main");
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
#include "neochatconnection.h"
|
||||
|
||||
#include <Quotient/connection.h>
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
ThumbnailResponse::ThumbnailResponse(QString id, QSize size, NeoChatConnection *connection)
|
||||
|
||||
@@ -35,7 +35,7 @@ private:
|
||||
QSize requestedSize;
|
||||
const QString localFile;
|
||||
Quotient::MediaThumbnailJob *job = nullptr;
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
NeoChatConnection *m_connection;
|
||||
|
||||
QImage image;
|
||||
QString errorStr;
|
||||
@@ -75,6 +75,6 @@ public:
|
||||
QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override;
|
||||
|
||||
private:
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
NeoChatConnection *m_connection = nullptr;
|
||||
MatrixImageProvider() = default;
|
||||
};
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
#include <Quotient/events/eventcontent.h>
|
||||
#include <qcoro/qcorosignal.h>
|
||||
|
||||
#include "neochatconnection.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
AccountEmoticonModel::AccountEmoticonModel(QObject *parent)
|
||||
@@ -37,7 +35,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) {
|
||||
return m_connection->makeMediaUrl(image.url).toString();
|
||||
return m_connection->makeMediaUrl(image.url);
|
||||
}
|
||||
if (role == BodyRole) {
|
||||
if (image.body) {
|
||||
@@ -79,12 +77,12 @@ QHash<int, QByteArray> AccountEmoticonModel::roleNames() const
|
||||
};
|
||||
}
|
||||
|
||||
NeoChatConnection *AccountEmoticonModel::connection() const
|
||||
Connection *AccountEmoticonModel::connection() const
|
||||
{
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
void AccountEmoticonModel::setConnection(NeoChatConnection *connection)
|
||||
void AccountEmoticonModel::setConnection(Connection *connection)
|
||||
{
|
||||
if (m_connection) {
|
||||
disconnect(m_connection, nullptr, this, nullptr);
|
||||
@@ -163,11 +161,7 @@ void AccountEmoticonModel::setEmoticonImage(int index, const QUrl &source)
|
||||
QCoro::Task<void> AccountEmoticonModel::doSetEmoticonImage(int index, QUrl source)
|
||||
{
|
||||
auto job = m_connection->uploadFile(source.isLocalFile() ? source.toLocalFile() : source.toString());
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
co_await qCoro(job.get(), &BaseJob::finished);
|
||||
#else
|
||||
co_await qCoro(job, &BaseJob::finished);
|
||||
#endif
|
||||
if (job->error() != BaseJob::NoError) {
|
||||
co_return;
|
||||
}
|
||||
@@ -189,11 +183,7 @@ QCoro::Task<void> AccountEmoticonModel::doSetEmoticonImage(int index, QUrl sourc
|
||||
QCoro::Task<void> AccountEmoticonModel::doAddEmoticon(QUrl source, QString shortcode, QString description, QString type)
|
||||
{
|
||||
auto job = m_connection->uploadFile(source.isLocalFile() ? source.toLocalFile() : source.toString());
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
co_await qCoro(job.get(), &BaseJob::finished);
|
||||
#else
|
||||
co_await qCoro(job, &BaseJob::finished);
|
||||
#endif
|
||||
if (job->error() != BaseJob::NoError) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <QPointer>
|
||||
#include <QQmlEngine>
|
||||
|
||||
class NeoChatConnection;
|
||||
#include <Quotient/connection.h>
|
||||
|
||||
/**
|
||||
* @class AccountEmoticonModel
|
||||
@@ -29,7 +29,7 @@ class AccountEmoticonModel : public QAbstractListModel
|
||||
/**
|
||||
* @brief The connection to get emoticons from.
|
||||
*/
|
||||
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||
Q_PROPERTY(Quotient::Connection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
@@ -63,8 +63,8 @@ public:
|
||||
*/
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
[[nodiscard]] NeoChatConnection *connection() const;
|
||||
void setConnection(NeoChatConnection *connection);
|
||||
[[nodiscard]] Quotient::Connection *connection() const;
|
||||
void setConnection(Quotient::Connection *connection);
|
||||
|
||||
/**
|
||||
* @brief Deletes the emoticon at the given index.
|
||||
@@ -96,7 +96,7 @@ Q_SIGNALS:
|
||||
|
||||
private:
|
||||
std::optional<Quotient::ImagePackEventContent> m_images;
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
QPointer<Quotient::Connection> m_connection;
|
||||
QCoro::Task<void> doSetEmoticonImage(int index, QUrl source);
|
||||
QCoro::Task<void> doAddEmoticon(QUrl source, QString shortcode, QString description, QString type);
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "actionsmodel.h"
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
#include "roommanager.h"
|
||||
#include <Quotient/events/roommemberevent.h>
|
||||
@@ -202,7 +201,7 @@ QList<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();
|
||||
}
|
||||
if (room->localUser()->id() == text) {
|
||||
if (room->localMember().id() == text) {
|
||||
Q_EMIT room->showMessage(NeoChatRoom::Positive, i18n("You are already in this room."));
|
||||
return QString();
|
||||
}
|
||||
@@ -261,7 +260,7 @@ QList<ActionsModel::Action> actions{
|
||||
return QString();
|
||||
}
|
||||
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("Knocking room <roomname>.", "Knocking room %1.", text));
|
||||
auto connection = dynamic_cast<NeoChatConnection *>(room->connection());
|
||||
auto connection = room->connection();
|
||||
const auto knownServer = roomName.mid(roomName.indexOf(":"_ls) + 1);
|
||||
if (parts.length() >= 2) {
|
||||
RoomManager::instance().knockRoom(connection, roomName, parts[1], QStringList{knownServer});
|
||||
@@ -431,11 +430,11 @@ QList<ActionsModel::Action> actions{
|
||||
if (!plEvent) {
|
||||
return QString();
|
||||
}
|
||||
if (plEvent->ban() > plEvent->powerLevelForUser(room->localUser()->id())) {
|
||||
if (plEvent->ban() > plEvent->powerLevelForUser(room->localMember().id())) {
|
||||
Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You are not allowed to ban users from this room."));
|
||||
return QString();
|
||||
}
|
||||
if (plEvent->powerLevelForUser(room->localUser()->id()) <= plEvent->powerLevelForUser(parts[0])) {
|
||||
if (plEvent->powerLevelForUser(room->localMember().id()) <= plEvent->powerLevelForUser(parts[0])) {
|
||||
Q_EMIT room->showMessage(
|
||||
NeoChatRoom::Error,
|
||||
i18nc("You are not allowed to ban <username> from this room.", "You are not allowed to ban %1 from this room.", parts[0]));
|
||||
@@ -464,7 +463,7 @@ QList<ActionsModel::Action> actions{
|
||||
if (!plEvent) {
|
||||
return QString();
|
||||
}
|
||||
if (plEvent->ban() > plEvent->powerLevelForUser(room->localUser()->id())) {
|
||||
if (plEvent->ban() > plEvent->powerLevelForUser(room->localMember().id())) {
|
||||
Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You are not allowed to unban users from this room."));
|
||||
return QString();
|
||||
}
|
||||
@@ -495,7 +494,7 @@ QList<ActionsModel::Action> actions{
|
||||
i18nc("'<text>' does not look like a matrix id.", "'%1' does not look like a matrix id.", parts[0]));
|
||||
return QString();
|
||||
}
|
||||
if (parts[0] == room->localUser()->id()) {
|
||||
if (parts[0] == room->localMember().id()) {
|
||||
Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You cannot kick yourself from the room."));
|
||||
return QString();
|
||||
}
|
||||
@@ -508,11 +507,11 @@ QList<ActionsModel::Action> actions{
|
||||
return QString();
|
||||
}
|
||||
auto kick = plEvent->kick();
|
||||
if (plEvent->powerLevelForUser(room->localUser()->id()) < kick) {
|
||||
if (plEvent->powerLevelForUser(room->localMember().id()) < kick) {
|
||||
Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You are not allowed to kick users from this room."));
|
||||
return QString();
|
||||
}
|
||||
if (plEvent->powerLevelForUser(room->localUser()->id()) <= plEvent->powerLevelForUser(parts[0])) {
|
||||
if (plEvent->powerLevelForUser(room->localMember().id()) <= plEvent->powerLevelForUser(parts[0])) {
|
||||
Q_EMIT room->showMessage(
|
||||
NeoChatRoom::Error,
|
||||
i18nc("You are not allowed to kick <username> from this room", "You are not allowed to kick %1 from this room.", parts[0]));
|
||||
|
||||
@@ -153,13 +153,13 @@ QVariant CustomEmojiModel::data(const QModelIndex &idx, int role) const
|
||||
|
||||
switch (Roles(role)) {
|
||||
case Roles::ModelData:
|
||||
return QVariant::fromValue(Emoji(m_connection->makeMediaUrl(QUrl(data.url)).toString(), data.name, true));
|
||||
return QVariant::fromValue(Emoji(QStringLiteral("image://mxc/") + data.url.mid(6), data.name, true));
|
||||
case Roles::Name:
|
||||
case Roles::DisplayRole:
|
||||
case Roles::ReplacedTextRole:
|
||||
return data.name;
|
||||
case Roles::ImageURL:
|
||||
return m_connection->makeMediaUrl(QUrl(data.url));
|
||||
return QUrl(QStringLiteral("image://mxc/") + data.url.mid(6));
|
||||
case Roles::MxcUrl:
|
||||
return m_connection->makeMediaUrl(QUrl(data.url));
|
||||
default:
|
||||
@@ -205,7 +205,7 @@ QVariantList CustomEmojiModel::filterModel(const QString &filter)
|
||||
if (!emoji.name.contains(filter, Qt::CaseInsensitive))
|
||||
continue;
|
||||
|
||||
results << QVariant::fromValue(Emoji(m_connection->makeMediaUrl(QUrl(emoji.url)).toString(), emoji.name, true));
|
||||
results << QVariant::fromValue(Emoji(QStringLiteral("image://mxc/") + emoji.url.mid(6), emoji.name, true));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ Q_SIGNALS:
|
||||
private:
|
||||
explicit CustomEmojiModel(QObject *parent = nullptr);
|
||||
QList<CustomEmoji> m_emojis;
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
NeoChatConnection *m_connection = nullptr;
|
||||
|
||||
void fetchEmojis();
|
||||
};
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
|
||||
#include "customemojimodel.h"
|
||||
#include <QRegularExpression>
|
||||
|
||||
class NeoChatConnection;
|
||||
#include <connection.h>
|
||||
|
||||
struct CustomEmoji {
|
||||
QString name; // with :semicolons:
|
||||
@@ -15,6 +14,6 @@ struct CustomEmoji {
|
||||
};
|
||||
|
||||
struct CustomEmojiModel::Private {
|
||||
QPointer<NeoChatConnection> connection;
|
||||
Quotient::Connection *conn = nullptr;
|
||||
QList<CustomEmoji> emojies;
|
||||
};
|
||||
|
||||
@@ -11,10 +11,9 @@
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <Quotient/csapi/device_management.h>
|
||||
#include <Quotient/connection.h>
|
||||
#include <Quotient/user.h>
|
||||
|
||||
#include "neochatconnection.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
DevicesModel::DevicesModel(QObject *parent)
|
||||
@@ -128,12 +127,8 @@ void DevicesModel::logout(const QString &deviceId, const QString &password)
|
||||
authData["type"_ls] = "m.login.password"_ls;
|
||||
QJsonObject identifier = {{"type"_ls, "m.id.user"_ls}, {"user"_ls, m_connection->user()->id()}};
|
||||
authData["identifier"_ls] = identifier;
|
||||
auto innerJob = m_connection->callApi<NeochatDeleteDeviceJob>(m_devices[index].deviceId, authData);
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
connect(innerJob.get(), &BaseJob::success, this, onSuccess);
|
||||
#else
|
||||
auto *innerJob = m_connection->callApi<NeochatDeleteDeviceJob>(m_devices[index].deviceId, authData);
|
||||
connect(innerJob, &BaseJob::success, this, onSuccess);
|
||||
#endif
|
||||
} else {
|
||||
onSuccess();
|
||||
}
|
||||
@@ -157,12 +152,12 @@ void DevicesModel::setName(const QString &deviceId, const QString &name)
|
||||
});
|
||||
}
|
||||
|
||||
NeoChatConnection *DevicesModel::connection() const
|
||||
Connection *DevicesModel::connection() const
|
||||
{
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
void DevicesModel::setConnection(NeoChatConnection *connection)
|
||||
void DevicesModel::setConnection(Connection *connection)
|
||||
{
|
||||
if (m_connection) {
|
||||
disconnect(m_connection, nullptr, this, nullptr);
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
|
||||
#include <Quotient/csapi/definitions/client_device.h>
|
||||
|
||||
class NeoChatConnection;
|
||||
namespace Quotient
|
||||
{
|
||||
class Connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class DevicesModel
|
||||
@@ -28,7 +31,7 @@ class DevicesModel : public QAbstractListModel
|
||||
/**
|
||||
* @brief The current connection that the model is getting its devices from.
|
||||
*/
|
||||
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged REQUIRED)
|
||||
Q_PROPERTY(Quotient::Connection *connection READ connection WRITE setConnection NOTIFY connectionChanged REQUIRED)
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -85,8 +88,9 @@ public:
|
||||
|
||||
explicit DevicesModel(QObject *parent = nullptr);
|
||||
|
||||
[[nodiscard]] NeoChatConnection *connection() const;
|
||||
void setConnection(NeoChatConnection *connection);
|
||||
|
||||
[[nodiscard]] Quotient::Connection *connection() const;
|
||||
void setConnection(Quotient::Connection *connection);
|
||||
|
||||
Q_SIGNALS:
|
||||
void connectionChanged();
|
||||
@@ -95,5 +99,5 @@ Q_SIGNALS:
|
||||
private:
|
||||
void fetchDevices();
|
||||
QList<Quotient::Device> m_devices;
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
QPointer<Quotient::Connection> m_connection;
|
||||
};
|
||||
|
||||
@@ -110,14 +110,11 @@ MessageContentModel::MessageContentModel(const Quotient::RoomEvent *event, NeoCh
|
||||
endResetModel();
|
||||
}
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::urlPreviewEnabledChanged, this, [this]() {
|
||||
updateComponents();
|
||||
});
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, [this]() {
|
||||
updateComponents();
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::urlPreviewEnabledChanged, this, &MessageContentModel::updateLinkPreviewer);
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, &MessageContentModel::updateLinkPreviewer);
|
||||
}
|
||||
|
||||
updateLinkPreviewer();
|
||||
updateComponents();
|
||||
}
|
||||
|
||||
@@ -200,9 +197,8 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
||||
return eventHandler.getReplyMediaInfo();
|
||||
}
|
||||
if (role == LinkPreviewerRole) {
|
||||
if (component.type == MessageComponentType::LinkPreview) {
|
||||
return QVariant::fromValue<LinkPreviewer *>(
|
||||
dynamic_cast<NeoChatConnection *>(m_room->connection())->previewerForLink(component.attributes["link"_ls].toUrl()));
|
||||
if (m_linkPreviewer != nullptr) {
|
||||
return QVariant::fromValue<LinkPreviewer *>(m_linkPreviewer);
|
||||
} else {
|
||||
return QVariant::fromValue<LinkPreviewer *>(emptyLinkPreview);
|
||||
}
|
||||
@@ -250,151 +246,105 @@ void MessageContentModel::updateComponents(bool isEditing)
|
||||
if (eventCast<const Quotient::RoomMessageEvent>(m_event)
|
||||
&& eventCast<const Quotient::RoomMessageEvent>(m_event)->rawMsgtype() == QStringLiteral("m.key.verification.request")) {
|
||||
m_components += MessageComponent{MessageComponentType::Verification, QString(), {}};
|
||||
endResetModel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_event->isRedacted()) {
|
||||
m_components += MessageComponent{MessageComponentType::Text, QString(), {}};
|
||||
endResetModel();
|
||||
return;
|
||||
}
|
||||
|
||||
EventHandler eventHandler(m_room, m_event);
|
||||
if (eventHandler.hasReply()) {
|
||||
if (m_room->findInTimeline(eventHandler.getReplyId()) == m_room->historyEdge()) {
|
||||
m_components += MessageComponent{MessageComponentType::ReplyLoad, QString(), {}};
|
||||
m_room->loadReply(m_event->id(), eventHandler.getReplyId());
|
||||
} else {
|
||||
m_components += MessageComponent{MessageComponentType::Reply, QString(), {}};
|
||||
}
|
||||
}
|
||||
|
||||
if (isEditing) {
|
||||
m_components += MessageComponent{MessageComponentType::Edit, QString(), {}};
|
||||
} else {
|
||||
m_components.append(componentsForType(eventHandler.messageComponentType()));
|
||||
}
|
||||
EventHandler eventHandler(m_room, m_event);
|
||||
if (eventHandler.hasReply()) {
|
||||
if (m_room->findInTimeline(eventHandler.getReplyId()) == m_room->historyEdge()) {
|
||||
m_components += MessageComponent{MessageComponentType::ReplyLoad, QString(), {}};
|
||||
m_room->loadReply(m_event->id(), eventHandler.getReplyId());
|
||||
} else {
|
||||
m_components += MessageComponent{MessageComponentType::Reply, QString(), {}};
|
||||
}
|
||||
}
|
||||
|
||||
if (m_room->urlPreviewEnabled()) {
|
||||
addLinkPreviews();
|
||||
if (isEditing) {
|
||||
m_components += MessageComponent{MessageComponentType::Edit, QString(), {}};
|
||||
} else if (m_event->isRedacted()) {
|
||||
m_components += MessageComponent{MessageComponentType::Text, QString(), {}};
|
||||
} else {
|
||||
if (eventHandler.messageComponentType() == MessageComponentType::Text) {
|
||||
const auto event = eventCast<const Quotient::RoomMessageEvent>(m_event);
|
||||
auto body = EventHandler::rawMessageBody(*event);
|
||||
m_components.append(TextHandler().textComponents(body, EventHandler::messageBodyInputFormat(*event), m_room, event, event->isReplaced()));
|
||||
} else if (eventHandler.messageComponentType() == MessageComponentType::File) {
|
||||
m_components += MessageComponent{MessageComponentType::File, QString(), {}};
|
||||
if (m_emptyItinerary) {
|
||||
auto fileTransferInfo = fileInfo();
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
KSyntaxHighlighting::Repository repository;
|
||||
const auto definitionForFile = repository.definitionForFileName(fileTransferInfo.localPath.toString());
|
||||
if (definitionForFile.isValid() || QFileInfo(fileTransferInfo.localPath.path()).suffix() == QStringLiteral("txt")) {
|
||||
QFile file(fileTransferInfo.localPath.path());
|
||||
file.open(QIODevice::ReadOnly);
|
||||
m_components += MessageComponent{MessageComponentType::Code,
|
||||
QString::fromStdString(file.readAll().toStdString()),
|
||||
{{QStringLiteral("class"), definitionForFile.name()}}};
|
||||
}
|
||||
#endif
|
||||
|
||||
if (FileType::instance().fileHasImage(fileTransferInfo.localPath)) {
|
||||
QImageReader reader(fileTransferInfo.localPath.path());
|
||||
m_components += MessageComponent{MessageComponentType::Pdf, QString(), {{QStringLiteral("size"), reader.size()}}};
|
||||
}
|
||||
} else {
|
||||
updateItineraryModel();
|
||||
if (m_itineraryModel != nullptr) {
|
||||
m_components += MessageComponent{MessageComponentType::Itinerary, QString(), {}};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_components += MessageComponent{eventHandler.messageComponentType(), QString(), {}};
|
||||
}
|
||||
}
|
||||
|
||||
if (m_linkPreviewer != nullptr) {
|
||||
if (m_linkPreviewer->loaded()) {
|
||||
m_components += MessageComponent{MessageComponentType::LinkPreview, QString(), {}};
|
||||
} else {
|
||||
m_components += MessageComponent{MessageComponentType::LinkPreviewLoad, QString(), {}};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentType::Type type)
|
||||
void MessageContentModel::updateLinkPreviewer()
|
||||
{
|
||||
switch (type) {
|
||||
case MessageComponentType::Text: {
|
||||
const auto event = eventCast<const Quotient::RoomMessageEvent>(m_event);
|
||||
auto body = EventHandler::rawMessageBody(*event);
|
||||
return TextHandler().textComponents(body, EventHandler::messageBodyInputFormat(*event), m_room, event, event->isReplaced());
|
||||
}
|
||||
case MessageComponentType::File: {
|
||||
QList<MessageComponent> components;
|
||||
components += MessageComponent{MessageComponentType::File, QString(), {}};
|
||||
const auto event = eventCast<const Quotient::RoomMessageEvent>(m_event);
|
||||
auto body = EventHandler::rawMessageBody(*event);
|
||||
components += TextHandler().textComponents(body, EventHandler::messageBodyInputFormat(*event), m_room, event, event->isReplaced());
|
||||
if (m_emptyItinerary) {
|
||||
auto fileTransferInfo = fileInfo();
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
KSyntaxHighlighting::Repository repository;
|
||||
const auto definitionForFile = repository.definitionForFileName(fileTransferInfo.localPath.toString());
|
||||
if (definitionForFile.isValid() || QFileInfo(fileTransferInfo.localPath.path()).suffix() == QStringLiteral("txt")) {
|
||||
QFile file(fileTransferInfo.localPath.path());
|
||||
file.open(QIODevice::ReadOnly);
|
||||
components += MessageComponent{MessageComponentType::Code,
|
||||
QString::fromStdString(file.readAll().toStdString()),
|
||||
{{QStringLiteral("class"), definitionForFile.name()}}};
|
||||
}
|
||||
#endif
|
||||
|
||||
if (FileType::instance().fileHasImage(fileTransferInfo.localPath)) {
|
||||
QImageReader reader(fileTransferInfo.localPath.path());
|
||||
components += MessageComponent{MessageComponentType::Pdf, QString(), {{QStringLiteral("size"), reader.size()}}};
|
||||
}
|
||||
} else {
|
||||
updateItineraryModel();
|
||||
if (m_itineraryModel != nullptr) {
|
||||
components += MessageComponent{MessageComponentType::Itinerary, QString(), {}};
|
||||
}
|
||||
if (m_room == nullptr || m_event == nullptr) {
|
||||
if (m_linkPreviewer != nullptr) {
|
||||
m_linkPreviewer->disconnect(this);
|
||||
m_linkPreviewer = nullptr;
|
||||
updateComponents();
|
||||
}
|
||||
return components;
|
||||
return;
|
||||
}
|
||||
case MessageComponentType::Image:
|
||||
case MessageComponentType::Video: {
|
||||
if (!m_event->is<StickerEvent>()) {
|
||||
const auto event = eventCast<const Quotient::RoomMessageEvent>(m_event);
|
||||
QList<MessageComponent> components;
|
||||
components += MessageComponent{type, QString(), {}};
|
||||
auto body = EventHandler::rawMessageBody(*event);
|
||||
components += TextHandler().textComponents(body, EventHandler::messageBodyInputFormat(*event), m_room, event, event->isReplaced());
|
||||
return components;
|
||||
if (!m_room->urlPreviewEnabled()) {
|
||||
if (m_linkPreviewer != nullptr) {
|
||||
m_linkPreviewer->disconnect(this);
|
||||
m_linkPreviewer = nullptr;
|
||||
updateComponents();
|
||||
}
|
||||
return;
|
||||
}
|
||||
default:
|
||||
return {MessageComponent{type, QString(), {}}};
|
||||
}
|
||||
}
|
||||
|
||||
MessageComponent MessageContentModel::linkPreviewComponent(const QUrl &link)
|
||||
{
|
||||
const auto linkPreviewer = dynamic_cast<NeoChatConnection *>(m_room->connection())->previewerForLink(link);
|
||||
if (linkPreviewer == nullptr) {
|
||||
return {};
|
||||
}
|
||||
if (linkPreviewer->loaded()) {
|
||||
return MessageComponent{MessageComponentType::LinkPreview, QString(), {{"link"_ls, link}}};
|
||||
} else {
|
||||
connect(linkPreviewer, &LinkPreviewer::loadedChanged, [this, link]() {
|
||||
const auto linkPreviewer = dynamic_cast<NeoChatConnection *>(m_room->connection())->previewerForLink(link);
|
||||
if (linkPreviewer != nullptr && linkPreviewer->loaded()) {
|
||||
for (auto &component : m_components) {
|
||||
if (component.attributes["link"_ls].toUrl() == link) {
|
||||
if (const auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
||||
if (LinkPreviewer::hasPreviewableLinks(event)) {
|
||||
m_linkPreviewer = dynamic_cast<NeoChatConnection *>(m_room->connection())->previewerForLink(LinkPreviewer::linkPreview(event));
|
||||
updateComponents();
|
||||
|
||||
if (m_linkPreviewer != nullptr) {
|
||||
connect(m_linkPreviewer, &LinkPreviewer::loadedChanged, [this]() {
|
||||
if (m_linkPreviewer != nullptr && m_linkPreviewer->loaded()) {
|
||||
// HACK: Because DelegateChooser can't switch the delegate on dataChanged it has to think there is a new delegate.
|
||||
beginResetModel();
|
||||
component.type = MessageComponentType::LinkPreview;
|
||||
m_components[m_components.size() - 1].type = MessageComponentType::LinkPreview;
|
||||
endResetModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return MessageComponent{MessageComponentType::LinkPreviewLoad, QString(), {{"link"_ls, link}}};
|
||||
}
|
||||
}
|
||||
|
||||
void MessageContentModel::addLinkPreviews()
|
||||
{
|
||||
int i = 0;
|
||||
while (i < m_components.size()) {
|
||||
const auto component = m_components.at(i);
|
||||
if (component.type == MessageComponentType::Text || component.type == MessageComponentType::Quote) {
|
||||
if (LinkPreviewer::hasPreviewableLinks(component.content)) {
|
||||
const auto links = LinkPreviewer::linkPreviews(component.content);
|
||||
for (qsizetype j = 0; j < links.size(); ++j) {
|
||||
const auto linkPreview = linkPreviewComponent(links[j]);
|
||||
if (!m_removedLinkPreviews.contains(links[j]) && !linkPreview.isEmpty()) {
|
||||
m_components.insert(i + j + 1, linkPreview);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void MessageContentModel::closeLinkPreview(int row)
|
||||
{
|
||||
if (m_components[row].type == MessageComponentType::LinkPreview || m_components[row].type == MessageComponentType::LinkPreviewLoad) {
|
||||
beginResetModel();
|
||||
m_removedLinkPreviews += m_components[row].attributes["link"_ls].toUrl();
|
||||
m_components.remove(row);
|
||||
m_components.squeeze();
|
||||
updateComponents();
|
||||
endResetModel();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,11 @@
|
||||
#include "enums/messagecomponenttype.h"
|
||||
#include "eventhandler.h"
|
||||
#include "itinerarymodel.h"
|
||||
#include "linkpreviewer.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
struct MessageComponent {
|
||||
MessageComponentType::Type type = MessageComponentType::Other;
|
||||
MessageComponentType::Type type;
|
||||
QString content;
|
||||
QVariantMap attributes;
|
||||
|
||||
@@ -21,11 +23,6 @@ struct MessageComponent {
|
||||
{
|
||||
return type == right.type && content == right.content && attributes == right.attributes;
|
||||
}
|
||||
|
||||
bool isEmpty() const
|
||||
{
|
||||
return type == MessageComponentType::Other;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -91,13 +88,6 @@ public:
|
||||
*/
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
/**
|
||||
* @brief Close the link preview at the given index.
|
||||
*
|
||||
* If the given index is not a link preview component, nothing happens.
|
||||
*/
|
||||
Q_INVOKABLE void closeLinkPreview(int row);
|
||||
|
||||
private:
|
||||
QPointer<NeoChatRoom> m_room;
|
||||
const Quotient::RoomEvent *m_event = nullptr;
|
||||
@@ -105,14 +95,10 @@ private:
|
||||
QList<MessageComponent> m_components;
|
||||
void updateComponents(bool isEditing = false);
|
||||
|
||||
QPointer<LinkPreviewer> m_linkPreviewer;
|
||||
ItineraryModel *m_itineraryModel = nullptr;
|
||||
|
||||
QList<MessageComponent> componentsForType(MessageComponentType::Type type);
|
||||
MessageComponent linkPreviewComponent(const QUrl &link);
|
||||
void addLinkPreviews();
|
||||
|
||||
QList<QUrl> m_removedLinkPreviews;
|
||||
|
||||
void updateLinkPreviewer();
|
||||
void updateItineraryModel();
|
||||
bool m_emptyItinerary = false;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "neochatconfig.h"
|
||||
|
||||
#include <Quotient/connection.h>
|
||||
#include <Quotient/csapi/rooms.h>
|
||||
#include <Quotient/events/redactionevent.h>
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
@@ -116,6 +117,34 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
|
||||
if (message != nullptr) {
|
||||
createEventObjects(message);
|
||||
if (NeoChatConfig::self()->showFancyEffects()) {
|
||||
QString planBody = message->plainBody();
|
||||
// snowflake
|
||||
const QString snowlakeEmoji = QString::fromUtf8("\xE2\x9D\x84");
|
||||
if (planBody.contains(snowlakeEmoji)) {
|
||||
Q_EMIT fancyEffectsReasonFound(QStringLiteral("snowflake"));
|
||||
}
|
||||
// fireworks
|
||||
const QString fireworksEmoji = QString::fromUtf8("\xF0\x9F\x8E\x86");
|
||||
if (planBody.contains(fireworksEmoji)) {
|
||||
Q_EMIT fancyEffectsReasonFound(QStringLiteral("fireworks"));
|
||||
}
|
||||
// sparkler
|
||||
const QString sparklerEmoji = QString::fromUtf8("\xF0\x9F\x8E\x87");
|
||||
if (planBody.contains(sparklerEmoji)) {
|
||||
Q_EMIT fancyEffectsReasonFound(QStringLiteral("fireworks"));
|
||||
}
|
||||
// party pooper
|
||||
const QString partyEmoji = QString::fromUtf8("\xF0\x9F\x8E\x89");
|
||||
if (planBody.contains(partyEmoji)) {
|
||||
Q_EMIT fancyEffectsReasonFound(QStringLiteral("confetti"));
|
||||
}
|
||||
// confetti ball
|
||||
const QString confettiEmoji = QString::fromUtf8("\xF0\x9F\x8E\x8A");
|
||||
if (planBody.contains(confettiEmoji)) {
|
||||
Q_EMIT fancyEffectsReasonFound(QStringLiteral("confetti"));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event->is<PollStartEvent>()) {
|
||||
m_currentRoom->createPollHandler(eventCast<const PollStartEvent>(event.get()));
|
||||
@@ -222,7 +251,7 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
});
|
||||
qCDebug(MessageEvent) << "Connected to room" << room->id() << "as" << room->localUser()->id();
|
||||
qCDebug(MessageEvent) << "Connected to room" << room->id() << "as" << room->localMember().id();
|
||||
} else {
|
||||
lastReadEventId.clear();
|
||||
}
|
||||
@@ -592,7 +621,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
}
|
||||
|
||||
if (role == IsEditableRole) {
|
||||
return eventHandler.messageComponentType() == MessageComponentType::Text && evt.senderId() == m_currentRoom->localUser()->id();
|
||||
return eventHandler.messageComponentType() == MessageComponentType::Text && evt.senderId() == m_currentRoom->localMember().id();
|
||||
}
|
||||
|
||||
return {};
|
||||
|
||||
@@ -140,4 +140,5 @@ private:
|
||||
|
||||
Q_SIGNALS:
|
||||
void roomChanged();
|
||||
void fancyEffectsReasonFound(const QString &fancyEffect);
|
||||
};
|
||||
|
||||
@@ -36,14 +36,6 @@ MessageFilterModel::MessageFilterModel(QObject *parent, TimelineModel *sourceMod
|
||||
}
|
||||
|
||||
bool MessageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
{
|
||||
if (NeoChatConfig::self()->showAllEvents()) {
|
||||
return true;
|
||||
}
|
||||
return eventIsVisible(sourceRow, sourceParent);
|
||||
}
|
||||
|
||||
bool MessageFilterModel::eventIsVisible(int sourceRow, const QModelIndex &sourceParent) const
|
||||
{
|
||||
const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||
|
||||
@@ -67,8 +59,9 @@ bool MessageFilterModel::eventIsVisible(int sourceRow, const QModelIndex &source
|
||||
// Don't show state events that are not the first in a consecutive group on the
|
||||
// same day as they will be grouped as a single delegate.
|
||||
const bool notLastRow = sourceRow < sourceModel()->rowCount() - 1;
|
||||
const bool previousEventIsState =
|
||||
notLastRow ? sourceModel()->data(sourceModel()->index(sourceRow + 1, 0), MessageEventModel::DelegateTypeRole) == DelegateType::State : false;
|
||||
const bool previousEventIsState = notLastRow
|
||||
? sourceModel()->data(sourceModel()->index(sourceRow + 1, 0), MessageEventModel::DelegateTypeRole) == DelegateType::State
|
||||
: false;
|
||||
const bool newDay = sourceModel()->data(sourceModel()->index(sourceRow, 0), MessageEventModel::ShowSectionRole).toBool();
|
||||
if (eventType == DelegateType::State && notLastRow && previousEventIsState && !newDay) {
|
||||
return false;
|
||||
@@ -79,11 +72,7 @@ bool MessageFilterModel::eventIsVisible(int sourceRow, const QModelIndex &source
|
||||
|
||||
QVariant MessageFilterModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (role == MessageEventModel::DelegateTypeRole && NeoChatConfig::self()->showAllEvents()) {
|
||||
if (!eventIsVisible(index.row(), index.parent())) {
|
||||
return DelegateType::Other;
|
||||
}
|
||||
} else if (role == AggregateDisplayRole) {
|
||||
if (role == AggregateDisplayRole) {
|
||||
return aggregateEventToString(mapToSource(index).row());
|
||||
} else if (role == StateEventsRole) {
|
||||
return stateEventsList(mapToSource(index).row());
|
||||
|
||||
@@ -60,8 +60,6 @@ public:
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
private:
|
||||
bool eventIsVisible(int sourceRow, const QModelIndex &sourceParent) const;
|
||||
|
||||
/**
|
||||
* @brief Aggregation of the text of consecutive state events starting at row.
|
||||
*
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "notificationsmodel.h"
|
||||
|
||||
#include <Quotient/connection.h>
|
||||
#include <Quotient/events/event.h>
|
||||
#include <Quotient/uri.h>
|
||||
|
||||
|
||||
@@ -1,254 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 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 "permissionsmodel.h"
|
||||
#include "powerlevel.h"
|
||||
|
||||
#include <Quotient/events/roompowerlevelsevent.h>
|
||||
|
||||
#include <KLazyLocalizedString>
|
||||
|
||||
static const QStringList defaultPermissions = {
|
||||
QStringLiteral("users_default"),
|
||||
QStringLiteral("state_default"),
|
||||
QStringLiteral("events_default"),
|
||||
QStringLiteral("invite"),
|
||||
QStringLiteral("kick"),
|
||||
QStringLiteral("ban"),
|
||||
QStringLiteral("redact"),
|
||||
QStringLiteral("m.reaction"),
|
||||
QStringLiteral("m.room.redaction"),
|
||||
QStringLiteral("m.room.power_levels"),
|
||||
QStringLiteral("m.room.name"),
|
||||
QStringLiteral("m.room.avatar"),
|
||||
QStringLiteral("m.room.canonical_alias"),
|
||||
QStringLiteral("m.room.topic"),
|
||||
QStringLiteral("m.room.encryption"),
|
||||
QStringLiteral("m.room.history_visibility"),
|
||||
QStringLiteral("m.room.pinned_events"),
|
||||
QStringLiteral("m.room.tombstone"),
|
||||
QStringLiteral("m.room.server_acl"),
|
||||
QStringLiteral("m.space.child"),
|
||||
QStringLiteral("m.space.parent"),
|
||||
};
|
||||
|
||||
// Alternate name text for default permissions.
|
||||
static const QHash<QString, KLazyLocalizedString> defaultPermissionNames = {
|
||||
{QStringLiteral("users_default"), kli18nc("Room permission type", "Default user power level")},
|
||||
{QStringLiteral("state_default"), kli18nc("Room permission type", "Default power level to set the room state")},
|
||||
{QStringLiteral("events_default"), kli18nc("Room permission type", "Default power level to send messages")},
|
||||
{QStringLiteral("invite"), kli18nc("Room permission type", "Invite users")},
|
||||
{QStringLiteral("kick"), kli18nc("Room permission type", "Kick users")},
|
||||
{QStringLiteral("ban"), kli18nc("Room permission type", "Ban users")},
|
||||
{QStringLiteral("redact"), kli18nc("Room permission type", "Remove messages sent by other users")},
|
||||
{QStringLiteral("m.reaction"), kli18nc("Room permission type", "Send reactions")},
|
||||
{QStringLiteral("m.room.redaction"), kli18nc("Room permission type", "Remove their own messages")},
|
||||
{QStringLiteral("m.room.power_levels"), kli18nc("Room permission type", "Change user permissions")},
|
||||
{QStringLiteral("m.room.name"), kli18nc("Room permission type", "Change the room name")},
|
||||
{QStringLiteral("m.room.avatar"), kli18nc("Room permission type", "Change the room avatar")},
|
||||
{QStringLiteral("m.room.canonical_alias"), kli18nc("Room permission type", "Change the room canonical alias")},
|
||||
{QStringLiteral("m.room.topic"), kli18nc("Room permission type", "Change the room topic")},
|
||||
{QStringLiteral("m.room.encryption"), kli18nc("Room permission type", "Enable encryption for the room")},
|
||||
{QStringLiteral("m.room.history_visibility"), kli18nc("Room permission type", "Change the room history visibility")},
|
||||
{QStringLiteral("m.room.pinned_events"), kli18nc("Room permission type", "Set pinned events")},
|
||||
{QStringLiteral("m.room.tombstone"), kli18nc("Room permission type", "Upgrade the room")},
|
||||
{QStringLiteral("m.room.server_acl"), kli18nc("Room permission type", "Set the room server access control list (ACL)")},
|
||||
{QStringLiteral("m.space.child"), kli18nc("Room permission type", "Set the children of this space")},
|
||||
{QStringLiteral("m.space.parent"), kli18nc("Room permission type", "Set the parent space of this room")},
|
||||
};
|
||||
|
||||
// Subtitles for the default values.
|
||||
static const QHash<QString, KLazyLocalizedString> defaultSubtitles = {
|
||||
{QStringLiteral("users_default"), kli18nc("Room permission type", "This is the power level for all new users when joining the room")},
|
||||
{QStringLiteral("state_default"), kli18nc("Room permission type", "This is used for all state events that do not have their own entry here")},
|
||||
{QStringLiteral("events_default"), kli18nc("Room permission type", "This is used for all message events that do not have their own entry here")},
|
||||
};
|
||||
|
||||
// Permissions that should use the event default.
|
||||
static const QStringList eventPermissions = {
|
||||
QStringLiteral("m.room.message"),
|
||||
QStringLiteral("m.reaction"),
|
||||
QStringLiteral("m.room.redaction"),
|
||||
};
|
||||
|
||||
PermissionsModel::PermissionsModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
NeoChatRoom *PermissionsModel::room() const
|
||||
{
|
||||
return m_room;
|
||||
}
|
||||
|
||||
void PermissionsModel::setRoom(NeoChatRoom *room)
|
||||
{
|
||||
if (room == m_room) {
|
||||
return;
|
||||
}
|
||||
m_room = room;
|
||||
Q_EMIT roomChanged();
|
||||
|
||||
initializeModel();
|
||||
}
|
||||
|
||||
void PermissionsModel::initializeModel()
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
m_permissions.clear();
|
||||
|
||||
if (m_room == nullptr) {
|
||||
endResetModel();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto currentPowerLevelEvent = m_room->currentState().get<Quotient::RoomPowerLevelsEvent>();
|
||||
if (currentPowerLevelEvent == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_permissions.append(defaultPermissions);
|
||||
|
||||
for (const auto &event : currentPowerLevelEvent->events().keys()) {
|
||||
if (!m_permissions.contains(event)) {
|
||||
m_permissions += event;
|
||||
}
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QVariant PermissionsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (m_room == nullptr || !index.isValid()) {
|
||||
return {};
|
||||
}
|
||||
if (index.row() >= rowCount()) {
|
||||
qDebug() << "PushRuleModel, something's wrong: index.row() >= m_rules.count()";
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto permission = m_permissions.value(index.row());
|
||||
if (role == NameRole) {
|
||||
if (defaultPermissionNames.keys().contains(permission)) {
|
||||
return defaultPermissionNames.value(permission).toString();
|
||||
}
|
||||
return permission;
|
||||
}
|
||||
if (role == SubtitleRole) {
|
||||
if (permission.startsWith(QLatin1String("m.")) && defaultPermissionNames.keys().contains(permission)) {
|
||||
return permission;
|
||||
}
|
||||
if (defaultSubtitles.contains(permission)) {
|
||||
return defaultSubtitles.value(permission).toString();
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
if (role == TypeRole) {
|
||||
return permission;
|
||||
}
|
||||
if (role == LevelRole) {
|
||||
const auto level = powerLevel(permission);
|
||||
if (level.has_value()) {
|
||||
return *level;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
if (role == LevelNameRole) {
|
||||
const auto level = powerLevel(permission);
|
||||
if (level.has_value()) {
|
||||
return i18nc("%1 is the name of the power level, e.g. admin and %2 is the value that represents.",
|
||||
"%1 (%2)",
|
||||
PowerLevel::nameForLevel(PowerLevel::levelForValue(*level)),
|
||||
*level);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
if (role == IsDefaultValueRole) {
|
||||
return permission.contains(QLatin1String("default"));
|
||||
}
|
||||
if (role == IsBasicPermissionRole) {
|
||||
return permission == QStringLiteral("invite") || permission == QStringLiteral("kick") || permission == QStringLiteral("ban")
|
||||
|| permission == QStringLiteral("redact");
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
int PermissionsModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_permissions.count();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> PermissionsModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
|
||||
roles[NameRole] = "name";
|
||||
roles[SubtitleRole] = "subtitle";
|
||||
roles[TypeRole] = "type";
|
||||
roles[LevelRole] = "level";
|
||||
roles[LevelNameRole] = "levelName";
|
||||
roles[IsDefaultValueRole] = "isDefaultValue";
|
||||
roles[IsBasicPermissionRole] = "isBasicPermission";
|
||||
return roles;
|
||||
}
|
||||
|
||||
std::optional<int> PermissionsModel::powerLevel(const QString &permission) const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (const auto currentPowerLevelEvent = m_room->currentState().get<Quotient::RoomPowerLevelsEvent>()) {
|
||||
if (permission == QStringLiteral("ban")) {
|
||||
return currentPowerLevelEvent->ban();
|
||||
} else if (permission == QStringLiteral("kick")) {
|
||||
return currentPowerLevelEvent->kick();
|
||||
} else if (permission == QStringLiteral("invite")) {
|
||||
return currentPowerLevelEvent->invite();
|
||||
} else if (permission == QStringLiteral("redact")) {
|
||||
return currentPowerLevelEvent->redact();
|
||||
} else if (permission == QStringLiteral("users_default")) {
|
||||
return currentPowerLevelEvent->usersDefault();
|
||||
} else if (permission == QStringLiteral("state_default")) {
|
||||
return currentPowerLevelEvent->stateDefault();
|
||||
} else if (permission == QStringLiteral("events_default")) {
|
||||
return currentPowerLevelEvent->eventsDefault();
|
||||
} else if (eventPermissions.contains(permission)) {
|
||||
return currentPowerLevelEvent->powerLevelForEvent(permission);
|
||||
} else {
|
||||
return currentPowerLevelEvent->powerLevelForState(permission);
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void PermissionsModel::setPowerLevel(const QString &permission, const int &newPowerLevel)
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
int clampPowerLevel = std::clamp(newPowerLevel, -1, 100);
|
||||
|
||||
const auto currentPowerLevel = powerLevel(permission);
|
||||
if (!currentPowerLevel.has_value() || currentPowerLevel == clampPowerLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto currentPowerLevelEvent = m_room->currentState().get<Quotient::RoomPowerLevelsEvent>()) {
|
||||
auto powerLevelContent = currentPowerLevelEvent->contentJson();
|
||||
if (powerLevelContent.contains(permission)) {
|
||||
powerLevelContent[permission] = clampPowerLevel;
|
||||
} else {
|
||||
auto eventPowerLevels = powerLevelContent[QLatin1String("events")].toObject();
|
||||
eventPowerLevels[permission] = clampPowerLevel;
|
||||
powerLevelContent[QLatin1String("events")] = eventPowerLevels;
|
||||
}
|
||||
|
||||
m_room->setState<Quotient::RoomPowerLevelsEvent>(powerLevelContent);
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_permissionsmodel.cpp"
|
||||
@@ -1,87 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QQmlEngine>
|
||||
#include <optional>
|
||||
|
||||
#include "neochatroom.h"
|
||||
|
||||
/**
|
||||
* @class PermissionsModel
|
||||
*
|
||||
* This class defines the model for managing room permission levels.
|
||||
*/
|
||||
class PermissionsModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
/**
|
||||
* @brief The room to show the permissions for
|
||||
*/
|
||||
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the model roles.
|
||||
*/
|
||||
enum Roles {
|
||||
NameRole = Qt::DisplayRole, /**< The permission name. */
|
||||
SubtitleRole, /**< The description of the permission. */
|
||||
TypeRole, /**< The base type of the permission, normally the event type id except for ban, kick, etc. */
|
||||
LevelRole, /**< The current power level for the permission. */
|
||||
LevelNameRole, /**< The current power level for the permission as a string. */
|
||||
IsDefaultValueRole, /**< Whether the permission is a default value, e.g. for users. */
|
||||
IsBasicPermissionRole, /**< Whether the permission is one of the basic ones, e.g. kick, ban, etc. */
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
|
||||
explicit PermissionsModel(QObject *parent = nullptr);
|
||||
|
||||
NeoChatRoom *room() const;
|
||||
void setRoom(NeoChatRoom *room);
|
||||
|
||||
/**
|
||||
* @brief Get the given role value at the given index.
|
||||
*
|
||||
* @sa QAbstractItemModel::data
|
||||
*/
|
||||
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
/**
|
||||
* @brief Number of rows in the model.
|
||||
*
|
||||
* @sa QAbstractItemModel::rowCount
|
||||
*/
|
||||
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
/**
|
||||
* @brief Returns a mapping from Role enum values to role names.
|
||||
*
|
||||
* @sa Roles, QAbstractItemModel::roleNames()
|
||||
*/
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
/**
|
||||
* @brief Return the power level required for the given permission.
|
||||
*/
|
||||
std::optional<int> powerLevel(const QString &permission) const;
|
||||
|
||||
/**
|
||||
* @brief Set the power level required for the given permission.
|
||||
*/
|
||||
Q_INVOKABLE void setPowerLevel(const QString &permission, const int &newPowerLevel);
|
||||
|
||||
Q_SIGNALS:
|
||||
void roomChanged();
|
||||
|
||||
private:
|
||||
QPointer<NeoChatRoom> m_room;
|
||||
|
||||
QStringList m_permissions;
|
||||
|
||||
void initializeModel();
|
||||
};
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
#include "publicroomlistmodel.h"
|
||||
|
||||
#include "neochatconnection.h"
|
||||
#include <Quotient/connection.h>
|
||||
|
||||
#include "publicroomlist_logging.h"
|
||||
|
||||
using namespace Quotient;
|
||||
@@ -13,14 +14,14 @@ PublicRoomListModel::PublicRoomListModel(QObject *parent)
|
||||
{
|
||||
}
|
||||
|
||||
NeoChatConnection *PublicRoomListModel::connection() const
|
||||
Quotient::Connection *PublicRoomListModel::connection() const
|
||||
{
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
void PublicRoomListModel::setConnection(NeoChatConnection *connection)
|
||||
void PublicRoomListModel::setConnection(Connection *conn)
|
||||
{
|
||||
if (m_connection == connection) {
|
||||
if (m_connection == conn) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -37,7 +38,7 @@ void PublicRoomListModel::setConnection(NeoChatConnection *connection)
|
||||
|
||||
endResetModel();
|
||||
|
||||
m_connection = connection;
|
||||
m_connection = conn;
|
||||
|
||||
if (job) {
|
||||
job->abandon();
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
|
||||
#include <Quotient/csapi/list_public_rooms.h>
|
||||
|
||||
class NeoChatConnection;
|
||||
namespace Quotient
|
||||
{
|
||||
class Connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class PublicRoomListModel
|
||||
@@ -30,7 +33,7 @@ class PublicRoomListModel : public QAbstractListModel
|
||||
/**
|
||||
* @brief The current connection that the model is getting its rooms from.
|
||||
*/
|
||||
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||
Q_PROPERTY(Quotient::Connection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||
|
||||
/**
|
||||
* @brief The server to get the public room list from.
|
||||
@@ -92,8 +95,8 @@ public:
|
||||
*/
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
[[nodiscard]] NeoChatConnection *connection() const;
|
||||
void setConnection(NeoChatConnection *connection);
|
||||
[[nodiscard]] Quotient::Connection *connection() const;
|
||||
void setConnection(Quotient::Connection *conn);
|
||||
|
||||
[[nodiscard]] QString server() const;
|
||||
void setServer(const QString &value);
|
||||
@@ -114,7 +117,7 @@ public:
|
||||
Q_INVOKABLE void search(int limit = 50);
|
||||
|
||||
private:
|
||||
QPointer<NeoChatConnection> m_connection = nullptr;
|
||||
QPointer<Quotient::Connection> m_connection = nullptr;
|
||||
QString m_server;
|
||||
QString m_searchText;
|
||||
bool m_showOnlySpaces = false;
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
|
||||
#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>
|
||||
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatconnection.h"
|
||||
|
||||
#include <KLazyLocalizedString>
|
||||
|
||||
@@ -454,7 +454,7 @@ void PushRuleModel::setConnection(NeoChatConnection *connection)
|
||||
Q_EMIT connectionChanged();
|
||||
|
||||
if (m_connection) {
|
||||
connect(m_connection, &NeoChatConnection::accountDataChanged, this, &PushRuleModel::updateNotificationRules);
|
||||
connect(m_connection, &Quotient::Connection::accountDataChanged, this, &PushRuleModel::updateNotificationRules);
|
||||
updateNotificationRules(QStringLiteral("m.push_rules"));
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user