Compare commits

..

1 Commits

Author SHA1 Message Date
Joshua Goins
a94579e0cf Pin kirigami-addons in the Flatpak build to 1.7.0 2025-02-03 15:48:28 -05:00
108 changed files with 9211 additions and 18626 deletions

View File

@@ -25,23 +25,13 @@
"modules": [ "modules": [
{ {
"name": "kirigamiaddons", "name": "kirigamiaddons",
"config-opts": [ "config-opts": [ "-DBUILD_TESTING=OFF" ],
"-DBUILD_TESTING=OFF"
],
"buildsystem": "cmake-ninja", "buildsystem": "cmake-ninja",
"sources": [ "sources": [ { "type": "git", "url": "https://invent.kde.org/libraries/kirigami-addons.git", "tag": "v1.7.0" } ]
{
"type": "git",
"url": "https://invent.kde.org/libraries/kirigami-addons.git"
}
]
}, },
{ {
"name": "kquickimageeditor", "name": "kquickimageeditor",
"config-opts": [ "config-opts": [ "-DBUILD_WITH_QT6=ON" ],
"-DBUILD_WITH_QT6=ON",
"-DBUILD_TESTING=OFF"
],
"buildsystem": "cmake-ninja", "buildsystem": "cmake-ninja",
"sources": [ "sources": [
{ {
@@ -53,19 +43,17 @@
{ {
"name": "olm", "name": "olm",
"buildsystem": "cmake-ninja", "buildsystem": "cmake-ninja",
"config-opts": [ "config-opts": [ "-DOLM_TESTS=OFF" ],
"-DOLM_TESTS=OFF"
],
"sources": [ "sources": [
{ {
"type": "git", "type": "git",
"url": "https://gitlab.matrix.org/matrix-org/olm.git", "url": "https://gitlab.matrix.org/matrix-org/olm.git",
"tag": "3.2.16", "tag": "3.2.10",
"x-checker-data": { "x-checker-data": {
"type": "git", "type": "git",
"tag-pattern": "^([\\d.]+)$" "tag-pattern": "^([\\d.]+)$"
}, },
"commit": "7e0c8277032e40308987257b711b38af8d77cc69" "commit": "9908862979147a71dc6abaecd521be526ae77be1"
} }
] ]
}, },
@@ -77,13 +65,13 @@
"-Dvapi=false", "-Dvapi=false",
"-Dgtk_doc=false", "-Dgtk_doc=false",
"-Dintrospection=false", "-Dintrospection=false",
"-Dcrypto=disabled" "-Dgcrypt=false"
], ],
"sources": [ "sources": [
{ {
"type": "archive", "type": "archive",
"url": "https://download.gnome.org/sources/libsecret/0.21/libsecret-0.21.6.tar.xz", "url": "https://download.gnome.org/sources/libsecret/0.20/libsecret-0.20.5.tar.xz",
"sha256": "747b8c175be108c880d3adfb9c3537ea66e520e4ad2dccf5dce58003aeeca090", "sha256": "3fb3ce340fcd7db54d87c893e69bfc2b1f6e4d4b279065ffe66dac9f0fd12b4d",
"x-checker-data": { "x-checker-data": {
"type": "gnome", "type": "gnome",
"name": "libsecret", "name": "libsecret",
@@ -98,13 +86,13 @@
"sources": [ "sources": [
{ {
"type": "archive", "type": "archive",
"url": "https://github.com/frankosterfeld/qtkeychain/archive/refs/tags/0.15.0.tar.gz", "url": "https://github.com/frankosterfeld/qtkeychain/archive/0.14.2.tar.gz",
"sha256": "f4254dc8f0933b06d90672d683eab08ef770acd8336e44dfa030ce041dc2ca22", "sha256": "cf2e972b783ba66334a79a30f6b3a1ea794a1dc574d6c3bebae5ffd2f0399571",
"x-checker-data": { "x-checker-data": {
"type": "anitya", "type": "anitya",
"project-id": 4138, "project-id": 4138,
"stable-only": true, "stable-only": true,
"url-template": "https://github.com/frankosterfeld/qtkeychain/archive/refs/tags/$version.tar.gz" "url-template": "https://github.com/frankosterfeld/qtkeychain/archive/v$version.tar.gz"
} }
} }
], ],
@@ -112,8 +100,7 @@
"-DBUILD_WITH_QT6=ON", "-DBUILD_WITH_QT6=ON",
"-DCMAKE_INSTALL_LIBDIR=/app/lib", "-DCMAKE_INSTALL_LIBDIR=/app/lib",
"-DLIB_INSTALL_DIR=/app/lib", "-DLIB_INSTALL_DIR=/app/lib",
"-DBUILD_TRANSLATIONS=NO", "-DBUILD_TRANSLATIONS=NO"
"-DBUILD_TESTING=OFF"
] ]
}, },
{ {
@@ -136,31 +123,29 @@
{ {
"name": "cmark", "name": "cmark",
"buildsystem": "cmake-ninja", "buildsystem": "cmake-ninja",
"config-opts": [ "config-opts": [ "-DCMARK_TESTS=OFF" ],
"-DCMARK_TESTS=OFF",
"-DCMAKE_BUILD_TYPE=Release",
"-DCMAKE_INSTALL_PREFIX=/app"
],
"sources": [ "sources": [
{ {
"type": "git", "type": "git",
"url": "https://github.com/commonmark/cmark.git" "url": "https://github.com/commonmark/cmark.git"
} }
], ],
"config-opts": [
"-DCMARK_TESTS=OFF",
"-DCMAKE_BUILD_TYPE=Release",
"-DCMAKE_INSTALL_PREFIX=/app"
],
"builddir": true "builddir": true
}, },
{ {
"name": "qcoro", "name": "qcoro",
"buildsystem": "cmake-ninja", "buildsystem": "cmake-ninja",
"config-opts": [ "config-opts": [ "-DQCORO_BUILD_EXAMPLES=OFF", "-DBUILD_TESTING=OFF" ],
"-DQCORO_BUILD_EXAMPLES=OFF",
"-DBUILD_TESTING=OFF"
],
"sources": [ "sources": [
{ {
"type": "archive", "type": "archive",
"url": "https://github.com/danvratil/qcoro/archive/refs/tags/v0.11.0.tar.gz", "url": "https://github.com/danvratil/qcoro/archive/refs/tags/v0.7.0.tar.gz",
"sha256": "9942c5b4c533192f6c5954dc6d10178b3829075e6a621b67df73f0a4b74d8297", "sha256": "23ef0217926e67c8d2eb861cf91617da2f7d8d5a9ae6c62321b21448b1669210",
"x-checker-data": { "x-checker-data": {
"type": "anitya", "type": "anitya",
"project-id": 236236, "project-id": 236236,
@@ -173,15 +158,14 @@
{ {
"name": "neochat", "name": "neochat",
"buildsystem": "cmake-ninja", "buildsystem": "cmake-ninja",
"config-opts": [
"-DBUILD_TESTING=OFF",
"-DNEOCHAT_FLATPAK=ON"
],
"sources": [ "sources": [
{ {
"type": "dir", "type": "dir",
"path": "." "path": "."
} }
],
"config-opts": [
"-DNEOCHAT_FLATPAK=ON"
] ]
} }
] ]

View File

@@ -10,7 +10,6 @@ include:
- /gitlab-templates/yaml-lint.yml - /gitlab-templates/yaml-lint.yml
- /gitlab-templates/android-qt6.yml - /gitlab-templates/android-qt6.yml
- /gitlab-templates/linux-qt6.yml - /gitlab-templates/linux-qt6.yml
- /gitlab-templates/linux-qt6-next.yml
- /gitlab-templates/windows-qt6.yml - /gitlab-templates/windows-qt6.yml
# - /gitlab-templates/freebsd-qt6.yml # - /gitlab-templates/freebsd-qt6.yml
- /gitlab-templates/flatpak.yml - /gitlab-templates/flatpak.yml

View File

@@ -9,7 +9,7 @@ cmake_minimum_required(VERSION 3.16)
# KDE Applications version, managed by release script. # KDE Applications version, managed by release script.
set(RELEASE_SERVICE_VERSION_MAJOR "25") set(RELEASE_SERVICE_VERSION_MAJOR "25")
set(RELEASE_SERVICE_VERSION_MINOR "03") set(RELEASE_SERVICE_VERSION_MINOR "03")
set(RELEASE_SERVICE_VERSION_MICRO "90") set(RELEASE_SERVICE_VERSION_MICRO "70")
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}") set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION}) project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})

View File

@@ -20,11 +20,11 @@ class ReactionModelTest : public QObject
private: private:
Connection *connection = nullptr; Connection *connection = nullptr;
TestUtils::TestRoom *room = nullptr; TestUtils::TestRoom *room = nullptr;
MessageContentModel *parentModel;
private Q_SLOTS: private Q_SLOTS:
void initTestCase(); void initTestCase();
void nullModel();
void basicReaction(); void basicReaction();
void newReaction(); void newReaction();
}; };
@@ -33,13 +33,20 @@ void ReactionModelTest::initTestCase()
{ {
connection = Connection::makeMockConnection(u"@bob:kde.org"_s); connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-reactionmodel-sync.json"_s); room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-reactionmodel-sync.json"_s);
parentModel = new MessageContentModel(room, "123456"_L1); }
void ReactionModelTest::nullModel()
{
auto model = ReactionModel(nullptr, nullptr);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.data(model.index(0), ReactionModel::TextContentRole), QVariant());
} }
void ReactionModelTest::basicReaction() void ReactionModelTest::basicReaction()
{ {
auto event = eventCast<const RoomMessageEvent>(room->messageEvents().at(0).get()); auto event = eventCast<const RoomMessageEvent>(room->messageEvents().at(0).get());
auto model = ReactionModel(parentModel, event->id(), room); auto model = ReactionModel(event, room);
QCOMPARE(model.rowCount(), 1); QCOMPARE(model.rowCount(), 1);
QCOMPARE(model.data(model.index(0), ReactionModel::TextContentRole), u"<span style=\"font-family: 'emoji';\">👍</span>"_s); QCOMPARE(model.data(model.index(0), ReactionModel::TextContentRole), u"<span style=\"font-family: 'emoji';\">👍</span>"_s);
@@ -51,7 +58,7 @@ void ReactionModelTest::basicReaction()
void ReactionModelTest::newReaction() void ReactionModelTest::newReaction()
{ {
auto event = eventCast<const RoomMessageEvent>(room->messageEvents().at(0).get()); auto event = eventCast<const RoomMessageEvent>(room->messageEvents().at(0).get());
auto model = new ReactionModel(parentModel, event->id(), room); auto model = new ReactionModel(event, room);
QCOMPARE(model->rowCount(), 1); QCOMPARE(model->rowCount(), 1);
QCOMPARE(model->data(model->index(0), ReactionModel::ToolTipRole), u"Alice Margatroid reacted with <span style=\"font-family: 'emoji';\">👍</span>"_s); QCOMPARE(model->data(model->index(0), ReactionModel::ToolTipRole), u"Alice Margatroid reacted with <span style=\"font-family: 'emoji';\">👍</span>"_s);

View File

@@ -74,7 +74,6 @@
<summary xml:lang="nl">Chat op Matrix</summary> <summary xml:lang="nl">Chat op Matrix</summary>
<summary xml:lang="nn">Prat med via Matrix</summary> <summary xml:lang="nn">Prat med via Matrix</summary>
<summary xml:lang="pl">Rozmawiaj na Matriksie</summary> <summary xml:lang="pl">Rozmawiaj na Matriksie</summary>
<summary xml:lang="ru">Общение в Matrix</summary>
<summary xml:lang="sa">Matrix इत्यत्र गपशपं कुर्वन्तु</summary> <summary xml:lang="sa">Matrix इत्यत्र गपशपं कुर्वन्तु</summary>
<summary xml:lang="sl">Klepet na Matrixu</summary> <summary xml:lang="sl">Klepet na Matrixu</summary>
<summary xml:lang="sv">Chatta på Matrix</summary> <summary xml:lang="sv">Chatta på Matrix</summary>
@@ -474,7 +473,6 @@
<content_attribute id="social-chat">intense</content_attribute> <content_attribute id="social-chat">intense</content_attribute>
</content_rating> </content_rating>
<releases> <releases>
<release version="24.12.3" date="2025-03-06"/>
<release version="24.12.2" date="2025-02-06"/> <release version="24.12.2" date="2025-02-06"/>
<release version="24.12.1" date="2025-01-09"/> <release version="24.12.1" date="2025-01-09"/>
<release version="24.12.0" date="2024-12-12"/> <release version="24.12.0" date="2024-12-12"/>

View File

@@ -112,8 +112,6 @@ Comment[ka]=ჩატი Matrix-ზე
Comment[lv]=Tērzējiet „Matrix“ tīklā Comment[lv]=Tērzējiet „Matrix“ tīklā
Comment[nl]=Chat op Matrix Comment[nl]=Chat op Matrix
Comment[pl]=Rozmawiaj na Matriksie Comment[pl]=Rozmawiaj na Matriksie
Comment[pt_BR]=Bate papo na Matrix
Comment[ru]=Общение в Matrix
Comment[sa]=Matrix इत्यत्र गपशपं कुर्वन्तु Comment[sa]=Matrix इत्यत्र गपशपं कुर्वन्तु
Comment[sl]=Klepet na Matrixu Comment[sl]=Klepet na Matrixu
Comment[sv]=Chatta på Matrix Comment[sv]=Chatta på Matrix

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@
# SPDX-License-Identifier: CC0-1.0 # SPDX-License-Identifier: CC0-1.0
--- ---
name: neochat name: neochat
base: core24 base: core22
adopt-info: neochat adopt-info: neochat
grade: stable grade: stable
confinement: strict confinement: strict
@@ -106,6 +106,8 @@ parts:
build-snaps: build-snaps:
- cmake - cmake
build-packages: build-packages:
- gcc-13
- g++-13
- libssl-dev - libssl-dev
cmake-parameters: cmake-parameters:
- -DCMAKE_INSTALL_PREFIX=/usr - -DCMAKE_INSTALL_PREFIX=/usr
@@ -113,6 +115,11 @@ parts:
- -DBUILD_TESTING=OFF - -DBUILD_TESTING=OFF
- -DQuotient_ENABLE_E2EE=ON - -DQuotient_ENABLE_E2EE=ON
- -DBUILD_WITH_QT6=ON - -DBUILD_WITH_QT6=ON
override-build: |
"update-alternatives --install /usr/bin/gcc gcc\
/usr/bin/gcc-13 100 --slave /usr/bin/g++ g++ \
/usr/bin/g++-13 --slave /usr/bin/gcov gcov /usr/bin/gcov-13"
craftctl default
prime: prime:
- -usr/include - -usr/include
- -usr/lib/*/pkgconfig - -usr/lib/*/pkgconfig
@@ -125,8 +132,6 @@ parts:
plugin: cmake plugin: cmake
build-environment: build-environment:
- PATH: /snap/bin:${PATH} - PATH: /snap/bin:${PATH}
- PYTHONPATH: ${CRAFT_STAGE}/lib/python3.12/site-packages:${CRAFT_STAGE}/usr/lib/python3/dist-packages
- LD_LIBRARY_PATH: "/snap/mesa-2404/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:/snap/kde-qt6-core24-sdk/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libproxy:$LD_LIBRARY_PATH"
cmake-parameters: cmake-parameters:
- -DCMAKE_INSTALL_PREFIX=/usr - -DCMAKE_INSTALL_PREFIX=/usr
- -DCMAKE_BUILD_TYPE=Release - -DCMAKE_BUILD_TYPE=Release
@@ -148,8 +153,6 @@ parts:
plugin: cmake plugin: cmake
build-environment: build-environment:
- PATH: /snap/bin:${PATH} - PATH: /snap/bin:${PATH}
- PYTHONPATH: ${CRAFT_STAGE}/lib/python3.12/site-packages:${CRAFT_STAGE}/usr/lib/python3/dist-packages
- LD_LIBRARY_PATH: "/snap/mesa-2404/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:/snap/kde-qt6-core24-sdk/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libproxy:$LD_LIBRARY_PATH"
build-packages: build-packages:
- cmark - cmark
- libcmark-dev - libcmark-dev
@@ -172,13 +175,3 @@ parts:
- libcmark0.30.2 - libcmark0.30.2
prime: prime:
- usr/lib/*/libcmark.so* - usr/lib/*/libcmark.so*
gpu-2404:
after: [neochat]
source: https://github.com/canonical/gpu-snap.git
plugin: dump
override-prime: |
craftctl default
${CRAFT_PART_SRC}/bin/gpu-2404-cleanup mesa-2404
prime:
- bin/gpu-2404-wrapper

View File

@@ -107,6 +107,8 @@ add_library(neochat STATIC
models/imagepacksmodel.h models/imagepacksmodel.h
events/imagepackevent.cpp events/imagepackevent.cpp
events/imagepackevent.h events/imagepackevent.h
events/joinrulesevent.cpp
events/joinrulesevent.h
models/reactionmodel.cpp models/reactionmodel.cpp
models/reactionmodel.h models/reactionmodel.h
delegatesizehelper.cpp delegatesizehelper.cpp

View File

@@ -222,14 +222,11 @@ void ChatDocumentHandler::complete(int index)
return; return;
} }
// Ensure we only search for the beginning of the current completion identifier
const auto fromIndex = qMax(completionStartIndex(), 0);
if (m_completionModel->autoCompletionType() == CompletionModel::User) { if (m_completionModel->autoCompletionType() == CompletionModel::User) {
auto name = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::DisplayNameRole).toString(); auto name = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::DisplayNameRole).toString();
auto id = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::SubtitleRole).toString(); auto id = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::SubtitleRole).toString();
auto text = getText(); auto text = getText();
auto at = text.indexOf(QLatin1Char('@'), fromIndex); auto at = text.lastIndexOf(QLatin1Char('@'), cursorPosition() - 1);
QTextCursor cursor(document()->textDocument()); QTextCursor cursor(document()->textDocument());
cursor.setPosition(at); cursor.setPosition(at);
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor); cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
@@ -242,7 +239,7 @@ void ChatDocumentHandler::complete(int index)
} else if (m_completionModel->autoCompletionType() == CompletionModel::Command) { } else if (m_completionModel->autoCompletionType() == CompletionModel::Command) {
auto command = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedTextRole).toString(); auto command = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedTextRole).toString();
auto text = getText(); auto text = getText();
auto at = text.indexOf(QLatin1Char('/'), fromIndex); auto at = text.lastIndexOf(QLatin1Char('/'));
QTextCursor cursor(document()->textDocument()); QTextCursor cursor(document()->textDocument());
cursor.setPosition(at); cursor.setPosition(at);
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor); cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
@@ -250,7 +247,7 @@ void ChatDocumentHandler::complete(int index)
} else if (m_completionModel->autoCompletionType() == CompletionModel::Room) { } else if (m_completionModel->autoCompletionType() == CompletionModel::Room) {
auto alias = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::SubtitleRole).toString(); auto alias = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::SubtitleRole).toString();
auto text = getText(); auto text = getText();
auto at = text.indexOf(QLatin1Char('#'), fromIndex); auto at = text.lastIndexOf(QLatin1Char('#'), cursorPosition() - 1);
QTextCursor cursor(document()->textDocument()); QTextCursor cursor(document()->textDocument());
cursor.setPosition(at); cursor.setPosition(at);
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor); cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
@@ -263,7 +260,7 @@ void ChatDocumentHandler::complete(int index)
} else if (m_completionModel->autoCompletionType() == CompletionModel::Emoji) { } else if (m_completionModel->autoCompletionType() == CompletionModel::Emoji) {
auto shortcode = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedTextRole).toString(); auto shortcode = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedTextRole).toString();
auto text = getText(); auto text = getText();
auto at = text.indexOf(QLatin1Char(':'), fromIndex); auto at = text.lastIndexOf(QLatin1Char(':'));
QTextCursor cursor(document()->textDocument()); QTextCursor cursor(document()->textDocument());
cursor.setPosition(at); cursor.setPosition(at);
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor); cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);

View File

@@ -294,7 +294,7 @@ bool Controller::supportSystemTray() const
void Controller::setQuitOnLastWindowClosed() void Controller::setQuitOnLastWindowClosed()
{ {
#ifndef Q_OS_ANDROID #ifndef Q_OS_ANDROID
if (supportSystemTray() && NeoChatConfig::self()->systemTray()) { if (NeoChatConfig::self()->systemTray()) {
m_trayIcon = new TrayIcon(this); m_trayIcon = new TrayIcon(this);
m_trayIcon->show(); m_trayIcon->show();
} else { } else {

View File

@@ -3,7 +3,6 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Window
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard import org.kde.kirigamiaddons.formcard as FormCard
@@ -38,7 +37,7 @@ FormCard.FormCardPage {
} }
function openEventSource(stateKey: string): void { function openEventSource(stateKey: string): void {
root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), { pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
model: stateKeysModel, model: stateKeysModel,
allowEdit: true, allowEdit: true,
room: root.room, room: root.room,

View File

@@ -50,7 +50,6 @@ public:
LiveLocation, /**< The initial event of a shared live location (i.e., the place where this is supposed to be shown in the timeline). */ LiveLocation, /**< The initial event of a shared live location (i.e., the place where this is supposed to be shown in the timeline). */
Encrypted, /**< An encrypted message that cannot be decrypted. */ Encrypted, /**< An encrypted message that cannot be decrypted. */
Reply, /**< A component to show a replied-to message. */ Reply, /**< A component to show a replied-to message. */
Reaction, /**< A component to show the reactions to this message. */
LinkPreview, /**< A preview of a URL in the message. */ LinkPreview, /**< A preview of a URL in the message. */
LinkPreviewLoad, /**< A loading dialog for a link preview. */ LinkPreviewLoad, /**< A loading dialog for a link preview. */
ChatBar, /**< A text edit for editing a message. */ ChatBar, /**< A text edit for editing a message. */

View File

@@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: 2019 Kitsune Ral <Kitsune-Ral@users.sf.net>
// SPDX-License-Identifier: LGPL-2.1-or-later
#include "joinrulesevent.h"
using namespace Quotient;
QString JoinRulesEvent::joinRule() const
{
return fromJson<QString>(contentJson()["join_rule"_L1]);
}
QJsonArray JoinRulesEvent::allow() const
{
return contentJson()["allow"_L1].toArray();
}

View File

@@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include <Quotient/events/stateevent.h>
namespace Quotient
{
/**
* @class JoinRulesEvent
*
* Class to define a join rule state event.
*
* @sa Quotient::StateEvent
*/
class JoinRulesEvent : public StateEvent
{
public:
QUO_EVENT(JoinRulesEvent, "m.room.join_rules")
explicit JoinRulesEvent(const QJsonObject &obj)
: StateEvent(obj)
{
}
/**
* @brief The join rule for the room.
*
* see https://spec.matrix.org/latest/client-server-api/#mroomjoin_rules for
* the available join rules for a room.
*/
QString joinRule() const;
/**
* @brief The allow rule for restricted rooms.
*
* see https://spec.matrix.org/latest/client-server-api/#mroomjoin_rules for
* full details on allow rules.
*/
QJsonArray allow() const;
};
}

View File

@@ -9,6 +9,7 @@
#include <Quotient/events/roommessageevent.h> #include <Quotient/events/roommessageevent.h>
#include "neochatconfig.h"
#include "utils.h" #include "utils.h"
using namespace Quotient; using namespace Quotient;
@@ -21,6 +22,7 @@ LinkPreviewer::LinkPreviewer(const QUrl &url, QObject *parent)
Q_ASSERT(dynamic_cast<Connection *>(this->parent())); Q_ASSERT(dynamic_cast<Connection *>(this->parent()));
connect(this, &LinkPreviewer::urlChanged, this, &LinkPreviewer::emptyChanged); connect(this, &LinkPreviewer::urlChanged, this, &LinkPreviewer::emptyChanged);
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, &LinkPreviewer::loadUrlPreview);
loadUrlPreview(); loadUrlPreview();
} }

View File

@@ -17,7 +17,6 @@ Kirigami.Page {
property bool showExisting: false property bool showExisting: false
property bool _showExisting: showExisting && root.currentStepString === root.initialStep property bool _showExisting: showExisting && root.currentStepString === root.initialStep
property bool showSettings: true
property alias currentStep: module.item property alias currentStep: module.item
property string currentStepString: initialStep property string currentStepString: initialStep
property string initialStep: "LoginRegister" property string initialStep: "LoginRegister"
@@ -266,7 +265,6 @@ Kirigami.Page {
FormCard.FormCard { FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing * 2 Layout.topMargin: Kirigami.Units.largeSpacing * 2
maximumWidth: Kirigami.Units.gridUnit * 20 maximumWidth: Kirigami.Units.gridUnit * 20
visible: root.showSettings
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
text: i18nc("@action:button", "Settings") text: i18nc("@action:button", "Settings")
icon.name: "settings-configure" icon.name: "settings-configure"

View File

@@ -239,7 +239,6 @@ int main(int argc, char *argv[])
Q_IMPORT_QML_PLUGIN(org_kde_neochat_chatbarPlugin) Q_IMPORT_QML_PLUGIN(org_kde_neochat_chatbarPlugin)
qml_register_types_org_kde_neochat(); qml_register_types_org_kde_neochat();
qmlRegisterUncreatableMetaObject(Quotient::staticMetaObject, "Quotient", 1, 0, "JoinRule", u"Access to JoinRule enum only"_s);
QQmlApplicationEngine engine; QQmlApplicationEngine engine;

View File

@@ -60,11 +60,6 @@ void CommonRoomsModel::reload()
return; return;
} }
// Checking if you have mutual rooms with yourself doesn't make sense and servers reject it too
if (m_connection->userId() == m_userId) {
return;
}
m_connection->callApi<NeochatGetCommonRoomsJob>(m_userId).then([this](const auto job) { m_connection->callApi<NeochatGetCommonRoomsJob>(m_userId).then([this](const auto job) {
const auto &replyData = job->jsonData(); const auto &replyData = job->jsonData();
beginResetModel(); beginResetModel();

View File

@@ -3,7 +3,6 @@
#include "messagecontentmodel.h" #include "messagecontentmodel.h"
#include "eventhandler.h" #include "eventhandler.h"
#include "messagecomponenttype.h"
#include "neochatconfig.h" #include "neochatconfig.h"
#include <QImageReader> #include <QImageReader>
@@ -28,7 +27,6 @@
#include "chatbarcache.h" #include "chatbarcache.h"
#include "filetype.h" #include "filetype.h"
#include "linkpreviewer.h" #include "linkpreviewer.h"
#include "models/reactionmodel.h"
#include "neochatconnection.h" #include "neochatconnection.h"
#include "neochatroom.h" #include "neochatroom.h"
#include "texthandler.h" #include "texthandler.h"
@@ -132,6 +130,9 @@ void MessageContentModel::initializeModel()
connect(m_room, &NeoChatRoom::urlPreviewEnabledChanged, this, [this]() { connect(m_room, &NeoChatRoom::urlPreviewEnabledChanged, this, [this]() {
resetContent(); resetContent();
}); });
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, [this]() {
resetContent();
});
connect(m_room, &Room::memberNameUpdated, this, [this](RoomMember member) { connect(m_room, &Room::memberNameUpdated, this, [this](RoomMember member) {
if (m_room != nullptr) { if (m_room != nullptr) {
if (senderId().isEmpty() || senderId() == member.id()) { if (senderId().isEmpty() || senderId() == member.id()) {
@@ -151,18 +152,12 @@ void MessageContentModel::initializeModel()
updateReplyModel(); updateReplyModel();
resetModel(); resetModel();
}); });
connect(m_room, &Room::updatedEvent, this, [this](const QString &eventId) {
if (eventId == m_eventId) {
updateReactionModel();
}
});
initializeEvent(); initializeEvent();
if (m_currentState == Available || m_currentState == Pending) { if (m_currentState == Available || m_currentState == Pending) {
updateReplyModel(); updateReplyModel();
} }
resetModel(); resetModel();
updateReactionModel();
} }
void MessageContentModel::initializeEvent() void MessageContentModel::initializeEvent()
@@ -345,10 +340,6 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
if (role == ReplyContentModelRole) { if (role == ReplyContentModelRole) {
return QVariant::fromValue<MessageContentModel *>(m_replyModel); return QVariant::fromValue<MessageContentModel *>(m_replyModel);
} }
if (role == ReactionModelRole) {
return QVariant::fromValue<ReactionModel *>(m_reactionModel);
;
}
if (role == ThreadRootRole) { if (role == ThreadRootRole) {
auto roomMessageEvent = eventCast<const RoomMessageEvent>(event.first); auto roomMessageEvent = eventCast<const RoomMessageEvent>(event.first);
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1) #if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1)
@@ -409,7 +400,6 @@ QHash<int, QByteArray> MessageContentModel::roleNamesStatic()
roles[MessageContentModel::ReplyEventIdRole] = "replyEventId"; roles[MessageContentModel::ReplyEventIdRole] = "replyEventId";
roles[MessageContentModel::ReplyAuthorRole] = "replyAuthor"; roles[MessageContentModel::ReplyAuthorRole] = "replyAuthor";
roles[MessageContentModel::ReplyContentModelRole] = "replyContentModel"; roles[MessageContentModel::ReplyContentModelRole] = "replyContentModel";
roles[MessageContentModel::ReactionModelRole] = "reactionModel";
roles[MessageContentModel::ThreadRootRole] = "threadRoot"; roles[MessageContentModel::ThreadRootRole] = "threadRoot";
roles[MessageContentModel::LinkPreviewerRole] = "linkPreviewer"; roles[MessageContentModel::LinkPreviewerRole] = "linkPreviewer";
roles[MessageContentModel::ChatBarCacheRole] = "chatBarCache"; roles[MessageContentModel::ChatBarCacheRole] = "chatBarCache";
@@ -490,16 +480,11 @@ QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEdi
newComponents = addLinkPreviews(newComponents); newComponents = addLinkPreviews(newComponents);
} }
if ((m_reactionModel && m_reactionModel->rowCount() > 0)) {
newComponents += MessageComponent{MessageComponentType::Reaction, QString(), {}};
}
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1) #if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1)
if (NeoChatConfig::self()->threads() && roomMessageEvent && (roomMessageEvent->isThreaded() || m_room->threads().contains(roomMessageEvent->id())) if (roomMessageEvent && (roomMessageEvent->isThreaded() || m_room->threads().contains(roomMessageEvent->id()))
&& roomMessageEvent->id() == roomMessageEvent->threadRootEventId()) { && roomMessageEvent->id() == roomMessageEvent->threadRootEventId()) {
#else #else
if (NeoChatConfig::self()->threads() && roomMessageEvent && roomMessageEvent->isThreaded() if (isThreading && roomMessageEvent && roomMessageEvent->isThreaded() && roomMessageEvent->id() == roomMessageEvent->threadRootEventId()) {
&& roomMessageEvent->id() == roomMessageEvent->threadRootEventId()) {
#endif #endif
newComponents += MessageComponent{MessageComponentType::Separator, {}, {}}; newComponents += MessageComponent{MessageComponentType::Separator, {}, {}};
newComponents += MessageComponent{MessageComponentType::ThreadBody, u"Thread Body"_s, {}}; newComponents += MessageComponent{MessageComponentType::ThreadBody, u"Thread Body"_s, {}};
@@ -528,7 +513,7 @@ void MessageContentModel::updateReplyModel()
if (roomMessageEvent == nullptr) { if (roomMessageEvent == nullptr) {
return; return;
} }
if (!roomMessageEvent->isReply(!NeoChatConfig::self()->threads()) || (roomMessageEvent->isThreaded() && NeoChatConfig::self()->threads())) { if (!roomMessageEvent->isReply() || (roomMessageEvent->isThreaded() && NeoChatConfig::self()->threads())) {
if (m_replyModel) { if (m_replyModel) {
delete m_replyModel; delete m_replyModel;
} }
@@ -539,7 +524,7 @@ void MessageContentModel::updateReplyModel()
return; return;
} }
m_replyModel = new MessageContentModel(m_room, roomMessageEvent->replyEventId(!NeoChatConfig::self()->threads()), true, false, this); m_replyModel = new MessageContentModel(m_room, roomMessageEvent->replyEventId(), true, false, this);
connect(m_replyModel, &MessageContentModel::eventUpdated, this, [this]() { connect(m_replyModel, &MessageContentModel::eventUpdated, this, [this]() {
Q_EMIT dataChanged(index(0), index(0), {ReplyAuthorRole}); Q_EMIT dataChanged(index(0), index(0), {ReplyAuthorRole});
@@ -746,25 +731,4 @@ void MessageContentModel::updateItineraryModel()
} }
} }
void MessageContentModel::updateReactionModel()
{
if (m_reactionModel != nullptr && m_reactionModel->rowCount() > 0) {
return;
}
if (m_reactionModel == nullptr) {
m_reactionModel = new ReactionModel(this, m_eventId, m_room);
connect(m_reactionModel, &ReactionModel::reactionsUpdated, this, &MessageContentModel::updateReactionModel);
}
if (m_reactionModel->rowCount() <= 0) {
m_reactionModel->disconnect(this);
delete m_reactionModel;
m_reactionModel = nullptr;
return;
}
resetContent();
}
#include "moc_messagecontentmodel.cpp" #include "moc_messagecontentmodel.cpp"

View File

@@ -7,11 +7,11 @@
#include <QQmlEngine> #include <QQmlEngine>
#include <Quotient/events/roomevent.h> #include <Quotient/events/roomevent.h>
#include <Quotient/room.h>
#include "enums/messagecomponenttype.h" #include "enums/messagecomponenttype.h"
#include "itinerarymodel.h" #include "itinerarymodel.h"
#include "messagecomponent.h" #include "messagecomponent.h"
#include "models/reactionmodel.h"
#include "neochatroommember.h" #include "neochatroommember.h"
/** /**
@@ -57,8 +57,6 @@ public:
ReplyAuthorRole, /**< The author of the event that was replied to. */ ReplyAuthorRole, /**< The author of the event that was replied to. */
ReplyContentModelRole, /**< The MessageContentModel for the reply event. */ ReplyContentModelRole, /**< The MessageContentModel for the reply event. */
ReactionModelRole, /**< Reaction model for this event. */
ThreadRootRole, /**< The thread root event ID for the event. */ ThreadRootRole, /**< The thread root event ID for the event. */
LinkPreviewerRole, /**< The link preview details. */ LinkPreviewerRole, /**< The link preview details. */
@@ -127,7 +125,6 @@ private:
QPointer<MessageContentModel> m_replyModel; QPointer<MessageContentModel> m_replyModel;
void updateReplyModel(); void updateReplyModel();
ReactionModel *m_reactionModel = nullptr;
ItineraryModel *m_itineraryModel = nullptr; ItineraryModel *m_itineraryModel = nullptr;
QList<MessageComponent> componentsForType(MessageComponentType::Type type); QList<MessageComponent> componentsForType(MessageComponentType::Type type);
@@ -138,6 +135,4 @@ private:
void updateItineraryModel(); void updateItineraryModel();
bool m_emptyItinerary = false; bool m_emptyItinerary = false;
void updateReactionModel();
}; };

View File

@@ -5,7 +5,6 @@
#include "neochatconfig.h" #include "neochatconfig.h"
#include <Quotient/events/encryptedevent.h>
#include <Quotient/events/roommessageevent.h> #include <Quotient/events/roommessageevent.h>
#include <Quotient/events/stickerevent.h> #include <Quotient/events/stickerevent.h>
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1) #if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1)
@@ -121,12 +120,8 @@ QVariant MessageModel::data(const QModelIndex &idx, int role) const
} }
if (role == ContentModelRole) { if (role == ContentModelRole) {
if (event->get().is<EncryptedEvent>()) {
return QVariant::fromValue<MessageContentModel *>(m_room->contentModelForEvent(event->get().id()));
}
auto roomMessageEvent = eventCast<const RoomMessageEvent>(&event.value().get()); auto roomMessageEvent = eventCast<const RoomMessageEvent>(&event.value().get());
if (NeoChatConfig::self()->threads() && roomMessageEvent && roomMessageEvent->isThreaded()) { if (roomMessageEvent && roomMessageEvent->isThreaded()) {
return QVariant::fromValue<MessageContentModel *>(m_room->contentModelForEvent(roomMessageEvent->threadRootEventId())); return QVariant::fromValue<MessageContentModel *>(m_room->contentModelForEvent(roomMessageEvent->threadRootEventId()));
} }
return QVariant::fromValue<MessageContentModel *>(m_room->contentModelForEvent(&event->get())); return QVariant::fromValue<MessageContentModel *>(m_room->contentModelForEvent(&event->get()));
@@ -221,9 +216,6 @@ QVariant MessageModel::data(const QModelIndex &idx, int role) const
} }
if (role == IsThreadedRole) { if (role == IsThreadedRole) {
if (!NeoChatConfig::self()->threads()) {
return false;
}
if (auto roomMessageEvent = eventCast<const RoomMessageEvent>(&event.value().get())) { if (auto roomMessageEvent = eventCast<const RoomMessageEvent>(&event.value().get())) {
return roomMessageEvent->isThreaded(); return roomMessageEvent->isThreaded();
} }
@@ -266,6 +258,18 @@ QVariant MessageModel::data(const QModelIndex &idx, int role) const
return m_readMarkerModels.contains(event.value().get().id()); return m_readMarkerModels.contains(event.value().get().id());
} }
if (role == ReactionRole) {
if (m_reactionModels.contains(event.value().get().id())) {
return QVariant::fromValue<ReactionModel *>(m_reactionModels[event.value().get().id()].data());
} else {
return QVariantList();
}
}
if (role == ShowReactionsRole) {
return m_reactionModels.contains(event.value().get().id());
}
if (role == VerifiedRole) { if (role == VerifiedRole) {
if (event.value().get().originalEvent()) { if (event.value().get().originalEvent()) {
auto encrypted = dynamic_cast<const EncryptedEvent *>(event.value().get().originalEvent()); auto encrypted = dynamic_cast<const EncryptedEvent *>(event.value().get().originalEvent());
@@ -319,6 +323,8 @@ QHash<int, QByteArray> MessageModel::roleNames() const
roles[ShowSectionRole] = "showSection"; roles[ShowSectionRole] = "showSection";
roles[ReadMarkersRole] = "readMarkers"; roles[ReadMarkersRole] = "readMarkers";
roles[ShowReadMarkersRole] = "showReadMarkers"; roles[ShowReadMarkersRole] = "showReadMarkers";
roles[ReactionRole] = "reaction";
roles[ShowReactionsRole] = "showReactions";
roles[VerifiedRole] = "verified"; roles[VerifiedRole] = "verified";
roles[AuthorDisplayNameRole] = "authorDisplayName"; roles[AuthorDisplayNameRole] = "authorDisplayName";
roles[IsRedactedRole] = "isRedacted"; roles[IsRedactedRole] = "isRedacted";
@@ -448,6 +454,31 @@ void MessageModel::createEventObjects(const Quotient::RoomEvent *event, bool isP
} }
} }
} }
if (const auto roomEvent = eventCast<const RoomMessageEvent>(event)) {
// ReactionModel handles updates to add and remove reactions, we only need to
// handle adding and removing whole models here.
if (m_reactionModels.contains(eventId)) {
// If a model already exists but now has no reactions remove it
if (m_reactionModels[eventId]->rowCount() <= 0) {
m_reactionModels.remove(eventId);
if (!resetting) {
refreshEventRoles(eventId, {ReactionRole, ShowReactionsRole});
}
}
} else {
if (m_room->relatedEvents(*event, Quotient::EventRelation::AnnotationType).count() > 0) {
// If a model doesn't exist and there are reactions add it.
auto reactionModel = QSharedPointer<ReactionModel>(new ReactionModel(roomEvent, m_room));
if (reactionModel->rowCount() > 0) {
m_reactionModels[eventId] = reactionModel;
if (!resetting) {
refreshEventRoles(eventId, {ReactionRole, ShowReactionsRole});
}
}
}
}
}
} }
void MessageModel::clearModel() void MessageModel::clearModel()
@@ -473,6 +504,7 @@ void MessageModel::clearModel()
void MessageModel::clearEventObjects() void MessageModel::clearEventObjects()
{ {
m_reactionModels.clear();
m_readMarkerModels.clear(); m_readMarkerModels.clear();
} }

View File

@@ -77,6 +77,8 @@ public:
ReadMarkersRole, /**< The first 5 other users at the event for read marker tracking. */ ReadMarkersRole, /**< The first 5 other users at the event for read marker tracking. */
ShowReadMarkersRole, /**< Whether there are any other user read markers to be shown. */ ShowReadMarkersRole, /**< Whether there are any other user read markers to be shown. */
ReactionRole, /**< List model for this event. */
ShowReactionsRole, /**< Whether there are any reactions to be shown. */
VerifiedRole, /**< Whether an encrypted message is sent in a verified session. */ VerifiedRole, /**< Whether an encrypted message is sent in a verified session. */
AuthorDisplayNameRole, /**< The displayname for the event's sender; for name change events, the old displayname. */ AuthorDisplayNameRole, /**< The displayname for the event's sender; for name change events, the old displayname. */
@@ -153,6 +155,7 @@ private:
bool movingEvent = false; bool movingEvent = false;
QMap<QString, QSharedPointer<ReadMarkerModel>> m_readMarkerModels; QMap<QString, QSharedPointer<ReadMarkerModel>> m_readMarkerModels;
QMap<QString, QSharedPointer<ReactionModel>> m_reactionModels;
void createEventObjects(const Quotient::RoomEvent *event, bool isPending = false); void createEventObjects(const Quotient::RoomEvent *event, bool isPending = false);
}; };

View File

@@ -9,27 +9,22 @@
#include <KLocalizedString> #include <KLocalizedString>
#include "neochatroom.h"
using namespace Qt::StringLiterals; using namespace Qt::StringLiterals;
ReactionModel::ReactionModel(MessageContentModel *parent, const QString &eventId, NeoChatRoom *room) ReactionModel::ReactionModel(const Quotient::RoomMessageEvent *event, NeoChatRoom *room)
: QAbstractListModel(parent) : QAbstractListModel(nullptr)
, m_room(room) , m_room(room)
, m_eventId(eventId) , m_event(event)
{ {
Q_ASSERT(parent); if (m_event != nullptr && m_room != nullptr) {
Q_ASSERT(parent != nullptr); connect(m_room, &NeoChatRoom::updatedEvent, this, [this](const QString &eventId) {
Q_ASSERT(!eventId.isEmpty()); if (m_event && m_event->id() == eventId) {
Q_ASSERT(room != nullptr); updateReactions();
}
});
connect(m_room, &NeoChatRoom::updatedEvent, this, [this](const QString &eventId) { updateReactions();
if (m_eventId == eventId) { }
updateReactions();
}
});
updateReactions();
} }
QVariant ReactionModel::data(const QModelIndex &index, int role) const QVariant ReactionModel::data(const QModelIndex &index, int role) const
@@ -104,16 +99,12 @@ int ReactionModel::rowCount(const QModelIndex &parent) const
void ReactionModel::updateReactions() void ReactionModel::updateReactions()
{ {
if (m_room == nullptr) {
return;
}
beginResetModel(); beginResetModel();
m_reactions.clear(); m_reactions.clear();
m_shortcodes.clear(); m_shortcodes.clear();
const auto &annotations = m_room->relatedEvents(m_eventId, Quotient::EventRelation::AnnotationType); const auto &annotations = m_room->relatedEvents(*m_event, Quotient::EventRelation::AnnotationType);
if (annotations.isEmpty()) { if (annotations.isEmpty()) {
endResetModel(); endResetModel();
return; return;

View File

@@ -3,20 +3,11 @@
#pragma once #pragma once
#include "neochatroom.h"
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QQmlEngine>
#include <Quotient/events/reactionevent.h> #include <Quotient/events/reactionevent.h>
#include <Quotient/roommember.h> #include <Quotient/roommember.h>
namespace Quotient
{
class RoomMessageEvent;
}
class MessageContentModel;
class NeoChatRoom;
/** /**
* @class ReactionModel * @class ReactionModel
* *
@@ -47,7 +38,7 @@ public:
HasLocalMember, /**< Whether the local member is in the list of authors. */ HasLocalMember, /**< Whether the local member is in the list of authors. */
}; };
explicit ReactionModel(MessageContentModel *parent, const QString &eventId, NeoChatRoom *room); explicit ReactionModel(const Quotient::RoomMessageEvent *event, NeoChatRoom *room);
/** /**
* @brief Get the given role value at the given index. * @brief Get the given role value at the given index.
@@ -70,15 +61,9 @@ public:
*/ */
[[nodiscard]] QHash<int, QByteArray> roleNames() const override; [[nodiscard]] QHash<int, QByteArray> roleNames() const override;
Q_SIGNALS:
/**
* @brief The reactions in the model have been updated.
*/
void reactionsUpdated();
private: private:
QPointer<NeoChatRoom> m_room; QPointer<NeoChatRoom> m_room;
QString m_eventId; const Quotient::RoomMessageEvent *m_event;
QList<Reaction> m_reactions; QList<Reaction> m_reactions;
QMap<QString, QString> m_shortcodes; QMap<QString, QString> m_shortcodes;

View File

@@ -348,12 +348,6 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
return QVariant::fromValue(room); return QVariant::fromValue(room);
} }
if (role == SubtitleTextRole) { if (role == SubtitleTextRole) {
if (room->isInvite()) {
if (room->isDirectChat()) {
return i18nc("@info:label", "Invited you to chat");
}
return i18nc("@info:label", "%1 invited you", room->member(room->invitingUserId()).displayName());
}
if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) { if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) {
return QString(); return QString();
} }

View File

@@ -19,6 +19,7 @@
#include "messagecontentmodel.h" #include "messagecontentmodel.h"
class NeoChatRoom; class NeoChatRoom;
class ReactionModel;
/** /**
* @class ThreadFetchModel * @class ThreadFetchModel
@@ -171,6 +172,8 @@ private:
ThreadFetchModel *m_threadFetchModel; ThreadFetchModel *m_threadFetchModel;
ThreadChatBarModel *m_threadChatBarModel; ThreadChatBarModel *m_threadChatBarModel;
QMap<QString, QSharedPointer<ReactionModel>> m_reactionModels;
QPointer<Quotient::GetRelatingEventsWithRelTypeJob> m_currentJob = nullptr; QPointer<Quotient::GetRelatingEventsWithRelTypeJob> m_currentJob = nullptr;
std::optional<QString> m_nextBatch = QString(); std::optional<QString> m_nextBatch = QString();
bool m_addingPending = false; bool m_addingPending = false;

View File

@@ -32,6 +32,42 @@ class TimelineMessageModel : public MessageModel
QML_ELEMENT QML_ELEMENT
public: public:
/**
* @brief Defines the model roles.
*/
enum EventRoles {
DelegateTypeRole = Qt::UserRole + 1, /**< The delegate type of the message. */
EventIdRole, /**< The matrix event ID of the event. */
TimeRole, /**< The timestamp for when the event was sent (as a QDateTime). */
SectionRole, /**< The date of the event as a string. */
AuthorRole, /**< The author of the event. */
HighlightRole, /**< Whether the event should be highlighted. */
SpecialMarksRole, /**< Whether the event is hidden or not. */
ProgressInfoRole, /**< Progress info when downloading files. */
GenericDisplayRole, /**< A generic string based upon the message type. */
MediaInfoRole, /**< The media info for the event. */
ContentModelRole, /**< The MessageContentModel for the event. */
IsThreadedRole, /**< Whether the message is in a thread. */
ThreadRootRole, /**< The Matrix ID of the thread root message, if any . */
ShowSectionRole, /**< Whether the section header should be shown. */
ReadMarkersRole, /**< The first 5 other users at the event for read marker tracking. */
ShowReadMarkersRole, /**< Whether there are any other user read markers to be shown. */
ReactionRole, /**< List model for this event. */
ShowReactionsRole, /**< Whether there are any reactions to be shown. */
VerifiedRole, /**< Whether an encrypted message is sent in a verified session. */
AuthorDisplayNameRole, /**< The displayname for the event's sender; for name change events, the old displayname. */
IsRedactedRole, /**< Whether an event has been deleted. */
IsPendingRole, /**< Whether an event is waiting to be accepted by the server. */
IsEditableRole, /**< Whether the event can be edited by the user. */
LastRole, // Keep this last
};
Q_ENUM(EventRoles)
explicit TimelineMessageModel(QObject *parent = nullptr); explicit TimelineMessageModel(QObject *parent = nullptr);
/** /**

View File

@@ -303,7 +303,6 @@ Name[ta]=பகிர்
Name[tr]=Paylaş Name[tr]=Paylaş
Name[uk]=Оприлюднення Name[uk]=Оприлюднення
Name[x-test]=xxSharexx Name[x-test]=xxSharexx
Name[zh_CN]=分享
Name[zh_TW]=分享 Name[zh_TW]=分享
Comment=The result of sharing a piece of content Comment=The result of sharing a piece of content
Comment[ar]=نتيجة مشاركة محتوى Comment[ar]=نتيجة مشاركة محتوى
@@ -337,6 +336,5 @@ Comment[ta]=எதையோ பகிர்ந்த‍தன் விளைவ
Comment[tr]=Bir parça içerik paylaşımının sonucu Comment[tr]=Bir parça içerik paylaşımının sonucu
Comment[uk]=Результат оприлюднення даних Comment[uk]=Результат оприлюднення даних
Comment[x-test]=xxThe result of sharing a piece of contentxx Comment[x-test]=xxThe result of sharing a piece of contentxx
Comment[zh_CN]=分享一个内容得到的结果
Comment[zh_TW]=分享一份內容之後的結果 Comment[zh_TW]=分享一份內容之後的結果
Action=Popup Action=Popup

View File

@@ -104,7 +104,6 @@
</entry> </entry>
<entry name="ShowLinkPreview" type="bool"> <entry name="ShowLinkPreview" type="bool">
<label>Show preview of the links in the chat messages</label> <label>Show preview of the links in the chat messages</label>
<default>true</default>
</entry> </entry>
<entry name="SystemTray" type="bool"> <entry name="SystemTray" type="bool">
<label>Close NeoChat to system tray</label> <label>Close NeoChat to system tray</label>

View File

@@ -145,10 +145,6 @@ void NeoChatConnection::connectSignals()
connect(NeoChatConfig::self(), &NeoChatConfig::PreferUsingEncryptionChanged, this, [] { connect(NeoChatConfig::self(), &NeoChatConfig::PreferUsingEncryptionChanged, this, [] {
setDirectChatEncryptionDefault(NeoChatConfig::preferUsingEncryption()); setDirectChatEncryptionDefault(NeoChatConfig::preferUsingEncryption());
}); });
setGlobalUrlPreviewEnabled(NeoChatConfig::showLinkPreview());
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, [this]() {
setGlobalUrlPreviewEnabled(NeoChatConfig::showLinkPreview());
});
} }
int NeoChatConnection::badgeNotificationCount() const int NeoChatConnection::badgeNotificationCount() const
@@ -171,25 +167,6 @@ void NeoChatConnection::refreshBadgeNotificationCount()
} }
} }
bool NeoChatConnection::globalUrlPreviewEnabled()
{
return m_globalUrlPreviewEnabled;
}
void NeoChatConnection::setGlobalUrlPreviewEnabled(bool newState)
{
if (m_globalUrlPreviewEnabled == newState) {
return;
}
m_globalUrlPreviewEnabled = newState;
if (!m_globalUrlPreviewEnabled) {
m_linkPreviewers.clear();
}
NeoChatConfig::setShowLinkPreview(m_globalUrlPreviewEnabled);
Q_EMIT globalUrlPreviewEnabledChanged();
}
void NeoChatConnection::logout(bool serverSideLogout) void NeoChatConnection::logout(bool serverSideLogout)
{ {
SettingsGroup(u"Accounts"_s).remove(userId()); SettingsGroup(u"Accounts"_s).remove(userId());
@@ -495,12 +472,9 @@ QCoro::Task<void> NeoChatConnection::setupPushNotifications(QString endpoint)
false); false);
qInfo() << "Registered for push notifications"; qInfo() << "Registered for push notifications";
m_pushNotificationsEnabled = true;
} else { } else {
qWarning() << "There's no gateway, not setting up push notifications."; qWarning() << "There's no gateway, not setting up push notifications.";
m_pushNotificationsEnabled = false;
} }
Q_EMIT enablePushNotificationsChanged();
#else #else
Q_UNUSED(endpoint) Q_UNUSED(endpoint)
co_return; co_return;
@@ -528,7 +502,7 @@ QString NeoChatConnection::accountDataJsonString(const QString &type) const
LinkPreviewer *NeoChatConnection::previewerForLink(const QUrl &link) LinkPreviewer *NeoChatConnection::previewerForLink(const QUrl &link)
{ {
if (!m_globalUrlPreviewEnabled) { if (!NeoChatConfig::showLinkPreview()) {
return nullptr; return nullptr;
} }
@@ -562,18 +536,4 @@ bool NeoChatConnection::canEraseData() const
return m_canEraseData; return m_canEraseData;
} }
bool NeoChatConnection::pushNotificationsAvailable() const
{
#ifdef HAVE_KUNIFIEDPUSH
return true;
#else
return false;
#endif
}
bool NeoChatConnection::enablePushNotifications() const
{
return m_pushNotificationsEnabled;
}
#include "moc_neochatconnection.cpp" #include "moc_neochatconnection.cpp"

View File

@@ -32,11 +32,6 @@ class NeoChatConnection : public Quotient::Connection
*/ */
Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged) Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged)
/**
* @brief Whether URL previews are enabled globally.
*/
Q_PROPERTY(bool globalUrlPreviewEnabled READ globalUrlPreviewEnabled WRITE setGlobalUrlPreviewEnabled NOTIFY globalUrlPreviewEnabledChanged)
/** /**
* @brief The model with the account's 3PIDs. * @brief The model with the account's 3PIDs.
*/ */
@@ -95,16 +90,6 @@ class NeoChatConnection : public Quotient::Connection
*/ */
Q_PROPERTY(bool canEraseData READ canEraseData NOTIFY canEraseDataChanged) Q_PROPERTY(bool canEraseData READ canEraseData NOTIFY canEraseDataChanged)
/**
* @brief Whether this build of NeoChat supports push notifications via KUnifiedPush.
*/
Q_PROPERTY(bool pushNotificationsAvailable READ pushNotificationsAvailable CONSTANT)
/**
* @brief Whether we successfully set up push notifications with the server.
*/
Q_PROPERTY(bool enablePushNotifications READ enablePushNotifications NOTIFY enablePushNotificationsChanged)
public: public:
/** /**
* @brief Defines the status after an attempt to change the password on an account. * @brief Defines the status after an attempt to change the password on an account.
@@ -187,24 +172,17 @@ public:
int badgeNotificationCount() const; int badgeNotificationCount() const;
void refreshBadgeNotificationCount(); void refreshBadgeNotificationCount();
bool globalUrlPreviewEnabled();
void setGlobalUrlPreviewEnabled(bool newState);
bool directChatInvites() const; bool directChatInvites() const;
// note: this is intentionally a copied QString because // note: this is intentionally a copied QString because
// the reference could be destroyed before the task is finished // the reference could be destroyed before the task is finished
QCoro::Task<void> setupPushNotifications(QString endpoint); QCoro::Task<void> setupPushNotifications(QString endpoint);
bool pushNotificationsAvailable() const;
bool enablePushNotifications() const;
bool isOnline() const; bool isOnline() const;
LinkPreviewer *previewerForLink(const QUrl &link); LinkPreviewer *previewerForLink(const QUrl &link);
Q_SIGNALS: Q_SIGNALS:
void globalUrlPreviewEnabledChanged();
void labelChanged(); void labelChanged();
void identityServerChanged(); void identityServerChanged();
void directChatNotificationsChanged(); void directChatNotificationsChanged();
@@ -218,7 +196,6 @@ Q_SIGNALS:
void badgeNotificationCountChanged(NeoChatConnection *connection, int count); void badgeNotificationCountChanged(NeoChatConnection *connection, int count);
void canCheckMutualRoomsChanged(); void canCheckMutualRoomsChanged();
void canEraseDataChanged(); void canEraseDataChanged();
void enablePushNotificationsChanged();
/** /**
* @brief Request a message be shown to the user of the given type. * @brief Request a message be shown to the user of the given type.
@@ -239,11 +216,9 @@ private:
void connectSignals(); void connectSignals();
int m_badgeNotificationCount = 0; int m_badgeNotificationCount = 0;
bool m_globalUrlPreviewEnabled = true;
QCache<QUrl, LinkPreviewer> m_linkPreviewers; QCache<QUrl, LinkPreviewer> m_linkPreviewers;
bool m_canCheckMutualRooms = false; bool m_canCheckMutualRooms = false;
bool m_canEraseData = false; bool m_canEraseData = false;
bool m_pushNotificationsEnabled = false;
}; };

View File

@@ -40,10 +40,10 @@
#include "chatbarcache.h" #include "chatbarcache.h"
#include "clipboard.h" #include "clipboard.h"
#include "eventhandler.h" #include "eventhandler.h"
#include "events/joinrulesevent.h"
#include "events/pollevent.h" #include "events/pollevent.h"
#include "filetransferpseudojob.h" #include "filetransferpseudojob.h"
#include "neochatconfig.h" #include "neochatconfig.h"
#include "neochatconnection.h"
#include "neochatroommember.h" #include "neochatroommember.h"
#include "roomlastmessageprovider.h" #include "roomlastmessageprovider.h"
#include "spacehierarchycache.h" #include "spacehierarchycache.h"
@@ -133,6 +133,7 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
Q_EMIT canEncryptRoomChanged(); Q_EMIT canEncryptRoomChanged();
Q_EMIT parentIdsChanged(); Q_EMIT parentIdsChanged();
Q_EMIT canonicalParentChanged(); Q_EMIT canonicalParentChanged();
Q_EMIT joinRuleChanged();
Q_EMIT readOnlyChanged(); Q_EMIT readOnlyChanged();
}); });
connect(connection, &Connection::capabilitiesLoaded, this, &NeoChatRoom::maxRoomVersionChanged); connect(connection, &Connection::capabilitiesLoaded, this, &NeoChatRoom::maxRoomVersionChanged);
@@ -156,10 +157,6 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
Q_EMIT childrenHaveHighlightNotificationsChanged(); Q_EMIT childrenHaveHighlightNotificationsChanged();
} }
}); });
const auto neochatconnection = static_cast<NeoChatConnection *>(connection);
Q_ASSERT(neochatconnection);
connect(neochatconnection, &NeoChatConnection::globalUrlPreviewEnabledChanged, this, &NeoChatRoom::urlPreviewEnabledChanged);
} }
bool NeoChatRoom::visible() const bool NeoChatRoom::visible() const
@@ -608,12 +605,63 @@ void NeoChatRoom::deleteMessagesByUser(const QString &user, const QString &reaso
doDeleteMessagesByUser(user, reason); doDeleteMessagesByUser(user, reason);
} }
QString NeoChatRoom::joinRule() const
{
auto joinRulesEvent = currentState().get<JoinRulesEvent>();
if (!joinRulesEvent) {
return {};
}
return joinRulesEvent->joinRule();
}
void NeoChatRoom::setJoinRule(const QString &joinRule, const QList<QString> &allowedSpaces)
{
if (!canSendState("m.room.join_rules"_L1)) {
qWarning() << "Power level too low to set join rules";
return;
}
auto actualRule = joinRule;
if (joinRule == "restricted"_L1 && allowedSpaces.isEmpty()) {
actualRule = "private"_L1;
}
QJsonArray allowConditions;
if (actualRule == "restricted"_L1) {
for (auto allowedSpace : allowedSpaces) {
allowConditions += QJsonObject{{"type"_L1, "m.room_membership"_L1}, {"room_id"_L1, allowedSpace}};
}
}
QJsonObject content;
content.insert("join_rule"_L1, joinRule);
if (!allowConditions.isEmpty()) {
content.insert("allow"_L1, allowConditions);
}
qWarning() << content;
setState("m.room.join_rules"_L1, {}, content);
// Not emitting joinRuleChanged() here, since that would override the change in the UI with the *current* value, which is not the *new* value.
}
QList<QString> NeoChatRoom::restrictedIds() const
{
auto joinRulesEvent = currentState().get<JoinRulesEvent>();
if (!joinRulesEvent) {
return {};
}
if (joinRulesEvent->joinRule() != "restricted"_L1) {
return {};
}
QList<QString> roomIds;
for (auto allow : joinRulesEvent->allow()) {
roomIds += allow.toObject().value("room_id"_L1).toString();
}
return roomIds;
}
QString NeoChatRoom::historyVisibility() const QString NeoChatRoom::historyVisibility() const
{ {
if (auto stateEvent = currentState().get("m.room.history_visibility"_L1)) { return currentState().get("m.room.history_visibility"_L1)->contentJson()["history_visibility"_L1].toString();
return stateEvent->contentJson()["history_visibility"_L1].toString();
}
return {};
} }
void NeoChatRoom::setHistoryVisibility(const QString &historyVisibilityRule) void NeoChatRoom::setHistoryVisibility(const QString &historyVisibilityRule)
@@ -680,9 +728,6 @@ void NeoChatRoom::setDefaultUrlPreviewState(const bool &defaultUrlPreviewState)
bool NeoChatRoom::urlPreviewEnabled() const bool NeoChatRoom::urlPreviewEnabled() const
{ {
if (!static_cast<NeoChatConnection *>(connection())->globalUrlPreviewEnabled()) {
return false;
}
if (hasAccountData("org.matrix.room.preview_urls"_L1)) { if (hasAccountData("org.matrix.room.preview_urls"_L1)) {
return !accountData("org.matrix.room.preview_urls"_L1)->contentJson()["disable"_L1].toBool(); return !accountData("org.matrix.room.preview_urls"_L1)->contentJson()["disable"_L1].toBool();
} else { } else {
@@ -1722,9 +1767,7 @@ MessageContentModel *NeoChatRoom::contentModelForEvent(const Quotient::RoomEvent
auto eventId = event->id(); auto eventId = event->id();
const auto txnId = event->transactionId(); const auto txnId = event->transactionId();
if (!m_eventContentModels.contains(eventId) && !m_eventContentModels.contains(txnId)) { if (!m_eventContentModels.contains(eventId) && !m_eventContentModels.contains(txnId)) {
return m_eventContentModels return m_eventContentModels.emplace(eventId, std::make_unique<MessageContentModel>(this, eventId.isEmpty() ? txnId : eventId, false, eventId.isEmpty()))
.emplace(eventId.isEmpty() ? txnId : eventId,
std::make_unique<MessageContentModel>(this, eventId.isEmpty() ? txnId : eventId, false, eventId.isEmpty()))
.first->second.get(); .first->second.get();
} }
@@ -1759,23 +1802,4 @@ ThreadModel *NeoChatRoom::modelForThread(const QString &threadRootId)
return m_threadModels[threadRootId].get(); return m_threadModels[threadRootId].get();
} }
void NeoChatRoom::pinEvent(const QString &eventId)
{
auto eventIds = pinnedEventIds();
eventIds.push_back(eventId);
setPinnedEvents(eventIds);
}
void NeoChatRoom::unpinEvent(const QString &eventId)
{
auto eventIds = pinnedEventIds();
eventIds.removeAll(eventId);
setPinnedEvents(eventIds);
}
bool NeoChatRoom::isEventPinned(const QString &eventId) const
{
return pinnedEventIds().contains(eventId);
}
#include "moc_neochatroom.cpp" #include "moc_neochatroom.cpp"

View File

@@ -134,6 +134,22 @@ class NeoChatRoom : public Quotient::Room
*/ */
Q_PROPERTY(bool readOnly READ readOnly NOTIFY readOnlyChanged) Q_PROPERTY(bool readOnly READ readOnly NOTIFY readOnlyChanged)
/**
* @brief The current join rule for the room as a QString.
*
* Possible values are [public, knock, invite, private, restricted].
*
* @sa https://spec.matrix.org/v1.5/client-server-api/#mroomjoin_rules
*/
Q_PROPERTY(QString joinRule READ joinRule WRITE setJoinRule NOTIFY joinRuleChanged)
/**
* @brief The space IDs that members of can join this room.
*
* Empty if the join rule is not restricted.
*/
Q_PROPERTY(QList<QString> restrictedIds READ restrictedIds NOTIFY joinRuleChanged)
/** /**
* @brief Get the maximum room version that the server supports. * @brief Get the maximum room version that the server supports.
* *
@@ -404,6 +420,25 @@ public:
bool readOnly() const; bool readOnly() const;
[[nodiscard]] QString joinRule() const;
/**
* @brief Set the join rule for the room.
*
* Will fail if the user doesn't have the required privileges.
*
* @param joinRule the join rule [public, knock, invite, private, restricted].
* @param allowedSpaces only used when the join rule is restricted. This is a
* list of space Matrix IDs that members of can join without an invite.
* If the rule is restricted and this list is empty it is treated as a join
* rule of private instead.
*
* @sa https://spec.matrix.org/latest/client-server-api/#mroomjoin_rules
*/
Q_INVOKABLE void setJoinRule(const QString &joinRule, const QList<QString> &allowedSpaces = {});
QList<QString> restrictedIds() const;
int maxRoomVersion() const; int maxRoomVersion() const;
/** /**
@@ -601,23 +636,6 @@ public:
*/ */
Q_INVOKABLE ThreadModel *modelForThread(const QString &threadRootId); Q_INVOKABLE ThreadModel *modelForThread(const QString &threadRootId);
/**
* @brief Pin a message in the room.
* @param eventId The id of the event to pin.
*/
Q_INVOKABLE void pinEvent(const QString &eventId);
/**
* @brief Unpin a message in the room.
* @param eventId The id of the event to unpin.
*/
Q_INVOKABLE void unpinEvent(const QString &eventId);
/**
* @return True if @p eventId is pinned in the room.
*/
Q_INVOKABLE bool isEventPinned(const QString &eventId) const;
private: private:
bool m_visible = false; bool m_visible = false;
@@ -675,6 +693,7 @@ Q_SIGNALS:
void displayNameChanged(); void displayNameChanged();
void pushNotificationStateChanged(PushNotificationState::State state); void pushNotificationStateChanged(PushNotificationState::State state);
void canEncryptRoomChanged(); void canEncryptRoomChanged();
void joinRuleChanged();
void historyVisibilityChanged(); void historyVisibilityChanged();
void defaultUrlPreviewStateChanged(); void defaultUrlPreviewStateChanged();
void urlPreviewEnabledChanged(); void urlPreviewEnabledChanged();

View File

@@ -35,7 +35,6 @@
"Name[tr]": "Tobias Fella", "Name[tr]": "Tobias Fella",
"Name[uk]": "Tobias Fella", "Name[uk]": "Tobias Fella",
"Name[x-test]": "xxTobias Fellaxx", "Name[x-test]": "xxTobias Fellaxx",
"Name[zh_CN]": "Tobias Fella",
"Name[zh_TW]": "Tobias Fella" "Name[zh_TW]": "Tobias Fella"
} }
], ],
@@ -71,7 +70,6 @@
"Description[tr]": "NeoChat ile Paylaş", "Description[tr]": "NeoChat ile Paylaş",
"Description[uk]": "Оприлюднити за допомогою NeoChat", "Description[uk]": "Оприлюднити за допомогою NeoChat",
"Description[x-test]": "xxShare via NeoChatxx", "Description[x-test]": "xxShare via NeoChatxx",
"Description[zh_CN]": "通过 NeoChat 分享",
"Description[zh_TW]": "透過 NeoChat 分享", "Description[zh_TW]": "透過 NeoChat 分享",
"Icon": "org.kde.neochat.tray", "Icon": "org.kde.neochat.tray",
"License": "GPL", "License": "GPL",
@@ -109,7 +107,6 @@
"Name[tr]": "NeoChat", "Name[tr]": "NeoChat",
"Name[uk]": "NeoChat", "Name[uk]": "NeoChat",
"Name[x-test]": "xxNeoChatxx", "Name[x-test]": "xxNeoChatxx",
"Name[zh_CN]": "NeoChat",
"Name[zh_TW]": "NeoChat", "Name[zh_TW]": "NeoChat",
"X-Purpose-ActionDisplay": "NeoChat" "X-Purpose-ActionDisplay": "NeoChat"
}, },

View File

@@ -87,18 +87,7 @@ KirigamiComponents.ConvergentContextMenu {
QQC2.Action { QQC2.Action {
text: i18nc("@action:inmenu", "Verify This Device") text: i18nc("@action:inmenu", "Verify This Device")
icon.name: "security-low" icon.name: "security-low"
onTriggered: { onTriggered: root.connection.startSelfVerification()
root.connection.startSelfVerification();
const dialog = Qt.createComponent("org.kde.kirigami", "PromptDialog").createObject(QQC2.Overlay.overlay, {
title: i18nc("@title", "Verification Request Sent"),
subtitle: i18nc("@info:label", "To proceed, accept the verification request on another device."),
standardButtons: Kirigami.Dialog.Ok
})
dialog.open();
root.connection.onNewKeyVerificationSession.connect(() => {
dialog.close();
});
}
enabled: Controller.csSupported enabled: Controller.csSupported
} }

View File

@@ -34,8 +34,9 @@ Delegates.RoundedItemDelegate {
Keys.onSpacePressed: selected() Keys.onSpacePressed: selected()
Keys.onEnterPressed: selected() Keys.onEnterPressed: selected()
onPressAndHold: root.contextMenuRequested()
TapHandler { TapHandler {
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus
acceptedButtons: Qt.RightButton | Qt.LeftButton acceptedButtons: Qt.RightButton | Qt.LeftButton
onTapped: (eventPoint, button) => { onTapped: (eventPoint, button) => {
if (button === Qt.RightButton) { if (button === Qt.RightButton) {
@@ -46,11 +47,6 @@ Delegates.RoundedItemDelegate {
} }
} }
TapHandler {
acceptedDevices: PointerDevice.TouchScreen
onLongPressed: root.contextMenuRequested()
}
contentItem: AvatarNotification { contentItem: AvatarNotification {
id: avatarNotification id: avatarNotification
source: root.source source: root.source

View File

@@ -75,7 +75,7 @@ KirigamiComponents.ConvergentContextMenu {
component RemoveMessageAction: Kirigami.Action { component RemoveMessageAction: Kirigami.Action {
visible: author.isLocalMember || currentRoom.canSendState("redact") visible: author.isLocalMember || currentRoom.canSendState("redact")
text: i18nc("@action:button", "Remove") text: i18nc("@action:button", "Remove")
icon.name: "edit-delete-remove" icon.name: "edit-delete-remove"
icon.color: "red" icon.color: "red"
onTriggered: { onTriggered: {
@@ -132,15 +132,6 @@ KirigamiComponents.ConvergentContextMenu {
} }
} }
component PinMessageAction: Kirigami.Action {
readonly property bool pinned: currentRoom.isEventPinned(root.eventId)
visible: currentRoom.canSendState("m.room.pinned_events")
text: pinned ? i18nc("@action:button 'Unpin' as in 'Unpin this message'", "Unpin") : i18nc("@action:button 'Pin' as in 'Pin the message in the room'", "Pin")
icon.name: pinned ? "window-unpin-symbolic" : "pin-symbolic"
onTriggered: pinned ? currentRoom.unpinEvent(root.eventId) : currentRoom.pinEvent(root.eventId)
}
headerContentItem: RowLayout { headerContentItem: RowLayout {
spacing: Kirigami.Units.largeSpacing spacing: Kirigami.Units.largeSpacing

View File

@@ -78,6 +78,21 @@ RowLayout {
Component { Component {
id: menu id: menu
QQC2.Menu { QQC2.Menu {
QQC2.MenuItem {
text: i18n("Explore rooms")
icon.name: "compass"
onTriggered: {
let dialog = pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage'), {
connection: root.connection
}, {
title: i18nc("@title", "Explore Rooms")
});
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
RoomManager.resolveResource(roomId.length > 0 ? roomId : alias, isJoined ? "" : "join");
});
}
}
QQC2.MenuItem { QQC2.MenuItem {
text: i18n("Find your friends") text: i18n("Find your friends")
icon.name: "list-add-user" icon.name: "list-add-user"

View File

@@ -71,7 +71,7 @@ DelegateContextMenu {
Kirigami.Action { Kirigami.Action {
visible: author.id === currentRoom.localMember.id || currentRoom.canSendState("redact") visible: author.id === currentRoom.localMember.id || currentRoom.canSendState("redact")
text: i18n("Remove") text: i18n("Remove")
icon.name: "edit-delete-remove" icon.name: "edit-delete-remove"
icon.color: "red" icon.color: "red"
onTriggered: { onTriggered: {
@@ -90,8 +90,6 @@ DelegateContextMenu {
} }
} }
DelegateContextMenu.PinMessageAction {}
DelegateContextMenu.ReportMessageAction {} DelegateContextMenu.ReportMessageAction {}
DelegateContextMenu.ShowUserAction {} DelegateContextMenu.ShowUserAction {}

View File

@@ -1,5 +1,4 @@
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick import QtQuick
@@ -7,89 +6,22 @@ import QtQuick.Controls as QQC2
import QtQuick.Layouts import QtQuick.Layouts
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.components as KirigamiComponents
import org.kde.neochat import org.kde.neochat
ColumnLayout { Kirigami.PlaceholderMessage {
id: root id: root
required property NeoChatRoom currentRoom required property NeoChatRoom currentRoom
readonly property var invitingMember: currentRoom.member(currentRoom.invitingUserId())
spacing: Kirigami.Units.smallSpacing
KirigamiComponents.Avatar {
id: avatar
Layout.preferredWidth: Kirigami.Units.iconSizes.huge
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
Layout.alignment: Qt.AlignHCenter
name: root.invitingMember.displayName
source: root.invitingMember.avatarUrl
color: root.invitingMember.color
}
Loader {
active: !root.currentRoom.isDirectChat()
Layout.alignment: Qt.AlignHCenter
sourceComponent: ColumnLayout {
spacing: Kirigami.Units.smallSpacing
QQC2.Label {
text: i18nc("@info:label", "%1 has invited you to join", root.invitingMember.displayName)
Layout.alignment: Qt.AlignHCenter
}
Kirigami.Heading {
text: root.currentRoom.displayName
Layout.alignment: Qt.AlignHCenter
}
}
}
Loader {
active: root.currentRoom.isDirectChat()
Layout.alignment: Qt.AlignHCenter
sourceComponent: ColumnLayout {
spacing: Kirigami.Units.smallSpacing
Kirigami.Heading {
text: root.currentRoom.displayName
Layout.alignment: Qt.AlignHCenter
}
QQC2.Label {
text: i18nc("@info:label", "This user is inviting you to chat.")
Layout.alignment: Qt.AlignHCenter
}
}
}
QQC2.Label {
color: Kirigami.Theme.disabledTextColor
text: i18n("You can reject invitations from unknown users under Security settings.")
visible: root.currentRoom.connection.canCheckMutualRooms
}
text: i18n("Accept this invitation?")
explanation: root.currentRoom.connection.canCheckMutualRooms ? i18n("You can reject invitations from unknown users under Security settings.") : ""
RowLayout { RowLayout {
spacing: Kirigami.Units.smallSpacing
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Kirigami.Units.mediumSpacing
QQC2.Button { QQC2.Button {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
text: i18nc("@action:button The thing being rejected is an invitation to chat", "Reject and Ignore User") text: i18nc("@action:button The thing being rejected is an invitation to chat", "Reject and ignore user")
icon.name: "list-remove-symbolic"
onClicked: { onClicked: {
RoomManager.leaveRoom(root.currentRoom); RoomManager.leaveRoom(root.currentRoom);
@@ -98,18 +30,18 @@ ColumnLayout {
} }
QQC2.Button { QQC2.Button {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
icon.name: "cards-block-symbolic" text: i18n("Reject")
text: i18nc("@action:button", "Reject")
onClicked: RoomManager.leaveRoom(root.currentRoom) onClicked: RoomManager.leaveRoom(root.currentRoom)
} }
QQC2.Button { QQC2.Button {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
icon.name: "dialog-ok-symbolic" text: i18n("Accept")
text: i18nc("@action:button", "Accept")
onClicked: root.currentRoom.acceptInvitation() onClicked: {
root.currentRoom.acceptInvitation();
}
} }
} }
} }

View File

@@ -78,7 +78,7 @@ DelegateContextMenu {
} }
QQC2.Action { QQC2.Action {
text: i18nc("@action:inmenu", "Copy Message Link") text: i18nc("@action:inmenu", "Copy Message Link")
icon.name: "link-symbolic" icon.name: "edit-copy"
onTriggered: { onTriggered: {
Clipboard.saveText("https://matrix.to/#/" + currentRoom.id + "/" + root.eventId); Clipboard.saveText("https://matrix.to/#/" + currentRoom.id + "/" + root.eventId);
} }
@@ -86,7 +86,6 @@ DelegateContextMenu {
Kirigami.Action { Kirigami.Action {
separator: true separator: true
} }
DelegateContextMenu.PinMessageAction {}
DelegateContextMenu.ReportMessageAction {} DelegateContextMenu.ReportMessageAction {}
DelegateContextMenu.ShowUserAction {} DelegateContextMenu.ShowUserAction {}
Kirigami.Action { Kirigami.Action {

View File

@@ -42,6 +42,8 @@ Delegates.RoundedItemDelegate {
} }
} }
onPressAndHold: createRoomListContextMenu()
Keys.onSpacePressed: clicked() Keys.onSpacePressed: clicked()
Keys.onEnterPressed: clicked() Keys.onEnterPressed: clicked()
Keys.onReturnPressed: clicked() Keys.onReturnPressed: clicked()
@@ -52,11 +54,6 @@ Delegates.RoundedItemDelegate {
onTapped: (eventPoint, button) => root.createRoomListContextMenu() onTapped: (eventPoint, button) => root.createRoomListContextMenu()
} }
TapHandler {
acceptedDevices: PointerDevice.TouchScreen
onLongPressed: root.createRoomListContextMenu()
}
contentItem: RowLayout { contentItem: RowLayout {
spacing: Kirigami.Units.largeSpacing spacing: Kirigami.Units.largeSpacing

View File

@@ -9,8 +9,6 @@ import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.kirigamiaddons.labs.components as Components import org.kde.kirigamiaddons.labs.components as Components
import Quotient
import org.kde.neochat import org.kde.neochat
Kirigami.Dialog { Kirigami.Dialog {
@@ -44,7 +42,7 @@ Kirigami.Dialog {
ids.push(spaceGroup.buttons[i].modelData.id); ids.push(spaceGroup.buttons[i].modelData.id);
} }
} }
root.room.setJoinRule(JoinRule.Restricted, ids); root.room.setJoinRule("restricted", ids);
} }
QQC2.ButtonGroup { QQC2.ButtonGroup {

View File

@@ -65,8 +65,7 @@ QQC2.Control {
onSelected: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'NotificationsView'), { onSelected: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'NotificationsView'), {
connection: root.connection connection: root.connection
}, { }, {
title: i18nc("@title", "Notifications"), title: i18nc("@title", "Notifications")
modality: Qt.NonModal
}) })
} }
@@ -275,31 +274,6 @@ QQC2.Control {
title: i18nc("@title", "Create a Space") title: i18nc("@title", "Create a Space")
}) })
} }
AvatarTabButton {
Layout.fillWidth: true
Layout.preferredHeight: width - Kirigami.Units.smallSpacing
Layout.maximumHeight: width - Kirigami.Units.smallSpacing
text: i18nc("@action:button", "Explore rooms")
contentItem: Kirigami.Icon {
source: "compass"
}
activeFocusOnTab: true
onSelected: {
let dialog = pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage'), {
connection: root.connection,
keyword: RoomManager.sortFilterRoomTreeModel.filterText
}, {
title: i18nc("@title", "Explore Rooms")
});
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
RoomManager.resolveResource(roomId.length > 0 ? roomId : alias, isJoined ? "" : "join");
});
}
}
} }
} }
} }

View File

@@ -116,16 +116,6 @@ Kirigami.Dialog {
} }
} }
Kirigami.Chip {
visible: root.room
text: root.room ? QmlUtils.nameForPowerLevelValue(root.room.memberEffectivePowerLevel(root.user.id)) : ""
closable: false
checkable: false
Layout.leftMargin: Kirigami.Units.largeSpacing
Layout.bottomMargin: Kirigami.Units.largeSpacing
}
Kirigami.Separator { Kirigami.Separator {
Layout.fillWidth: true Layout.fillWidth: true
} }

View File

@@ -27,7 +27,6 @@ RowLayout {
Layout.topMargin: Kirigami.Units.smallSpacing Layout.topMargin: Kirigami.Units.smallSpacing
Layout.bottomMargin: Kirigami.Units.smallSpacing Layout.bottomMargin: Kirigami.Units.smallSpacing
Layout.rightMargin: Kirigami.Units.largeSpacing Layout.rightMargin: Kirigami.Units.largeSpacing
Layout.leftMargin: Kirigami.Units.largeSpacing
Layout.minimumHeight: bottomEdge ? Kirigami.Units.gridUnit * 3 : -1 Layout.minimumHeight: bottomEdge ? Kirigami.Units.gridUnit * 3 : -1
onVisibleChanged: { onVisibleChanged: {
@@ -35,74 +34,59 @@ RowLayout {
accountsPopup.close(); accountsPopup.close();
} }
} }
KirigamiComponents.AvatarButton {
QQC2.ToolButton {
id: accountButton id: accountButton
readonly property url avatarUrl: root.connection.localUser.avatarUrl
down: accountMenu.opened || pressed Layout.preferredWidth: Kirigami.Units.iconSizes.medium
Layout.preferredHeight: Kirigami.Units.iconSizes.medium
Layout.leftMargin: Kirigami.Units.largeSpacing
onClicked: accountMenu.popup() text: i18n("Edit this account")
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl.
source: avatarUrl.toString().length > 0 ? root.connection.makeMediaUrl(avatarUrl) : ""
name: root.connection.localUser.displayName
Layout.fillWidth: true activeFocusOnTab: true
Layout.fillHeight: true
QQC2.ToolTip.visible: hovered onClicked: {
QQC2.ToolTip.text: i18nc("@info:tooltip", "Manage Account") NeoChatSettingsView.open("accounts")
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
contentItem: RowLayout {
spacing: Kirigami.Units.largeSpacing
KirigamiComponents.Avatar {
readonly property url avatarUrl: root.connection.localUser.avatarUrl
Layout.preferredWidth: Kirigami.Units.iconSizes.medium
Layout.preferredHeight: Kirigami.Units.iconSizes.medium
Layout.leftMargin: Kirigami.Units.largeSpacing
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl.
source: avatarUrl.toString().length > 0 ? root.connection.makeMediaUrl(avatarUrl) : ""
name: root.connection.localUser.displayName
}
ColumnLayout {
Layout.fillWidth: true
Layout.maximumWidth: Math.round(root.width * 0.55)
visible: !root.collapsed
spacing: 0
QQC2.Label {
id: displayNameLabel
Layout.fillWidth: true
text: root.connection.localUser.displayName
textFormat: Text.PlainText
elide: Text.ElideRight
}
QQC2.Label {
id: idLabel
Layout.fillWidth: true
text: (root.connection.label.length > 0 ? (root.connection.label + " ") : "") + root.connection.localUser.id
font.pointSize: displayNameLabel.font.pointSize * 0.8
opacity: 0.7
textFormat: Text.PlainText
elide: Text.ElideRight
}
}
} }
AccountMenu { onPressAndHold: accountMenu.popup();
id: accountMenu
connection: root.connection TapHandler {
window: QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow acceptedButtons: Qt.RightButton
onTapped: accountMenu.popup()
} }
} }
ColumnLayout {
Layout.fillWidth: true
Layout.maximumWidth: Math.round(root.width * 0.55)
visible: !root.collapsed
spacing: 0
QQC2.Label {
id: displayNameLabel
Layout.fillWidth: true
text: root.connection.localUser.displayName
textFormat: Text.PlainText
elide: Text.ElideRight
}
QQC2.Label {
id: idLabel
Layout.fillWidth: true
text: (root.connection.label.length > 0 ? (root.connection.label + " ") : "") + root.connection.localUser.id
font.pointSize: displayNameLabel.font.pointSize * 0.8
opacity: 0.7
textFormat: Text.PlainText
elide: Text.ElideRight
}
}
Kirigami.ActionToolBar { Kirigami.ActionToolBar {
alignment: Qt.AlignRight alignment: Qt.AlignRight
display: QQC2.Button.IconOnly display: QQC2.Button.IconOnly
Layout.fillWidth: true
Layout.preferredWidth: maximumContentWidth
actions: [ actions: [
Kirigami.Action { Kirigami.Action {
text: i18n("Switch User") text: i18n("Switch User")
@@ -122,6 +106,12 @@ RowLayout {
] ]
} }
AccountMenu {
id: accountMenu
y: root.bottomEdge ? -height : accountButton.height
connection: root.connection
window: accountButton.QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow
}
Component { Component {
id: accountSwitchDialog id: accountSwitchDialog
AccountSwitchDialog {} AccountSwitchDialog {}

View File

@@ -324,15 +324,7 @@ void RoomManager::visitRoom(Room *r, const QString &eventId)
void RoomManager::joinRoom(Quotient::Connection *account, const QString &roomAliasOrId, const QStringList &viaServers) void RoomManager::joinRoom(Quotient::Connection *account, const QString &roomAliasOrId, const QStringList &viaServers)
{ {
QStringList vias = viaServers; auto job = account->joinRoom(roomAliasOrId, viaServers);
// If no one gives us a homeserver suggestion, try the server specified in the alias/id.
// Otherwise joining a remote room not on our homeserver will fail.
if (vias.empty()) {
vias.append(roomAliasOrId.mid(roomAliasOrId.lastIndexOf(':'_L1) + 1));
}
auto job = account->joinRoom(roomAliasOrId, vias);
connect( connect(
job.get(), job.get(),
&Quotient::BaseJob::finished, &Quotient::BaseJob::finished,

View File

@@ -117,7 +117,7 @@ FormCard.FormCardPage {
id: addAccountDelegate id: addAccountDelegate
text: i18n("Add Account") text: i18n("Add Account")
icon.name: "list-add" icon.name: "list-add"
onClicked: root.QQC2.ApplicationWindow.window.pageStack.layers.push(Qt.createComponent('org.kde.neochat.login', 'WelcomePage'), { showSettings: false }) onClicked: root.QQC2.ApplicationWindow.window.pageStack.layers.push(Qt.createComponent('org.kde.neochat.login', 'WelcomePage'))
} }
} }

View File

@@ -26,17 +26,7 @@ FormCard.FormCardPage {
Layout.topMargin: Kirigami.Units.largeSpacing * 4 Layout.topMargin: Kirigami.Units.largeSpacing * 4
FormCard.FormCheckDelegate { FormCard.FormCheckDelegate {
text: i18n("Enable notifications for this account") text: i18n("Enable notifications for this account")
description: { description: i18n("Whether push notifications are generated by your Matrix server")
if (connection.pushNotificationsAvailable) {
if (connection.enablePushNotifications) {
return i18n("Notifications can appear even when NeoChat isn't running.");
} else {
return i18n("Push notifications are available but could not be enabled.");
}
} else {
return i18n("Notifications will only appear when NeoChat is running.");
}
}
checked: root.pushRuleModel.globalNotificationsEnabled checked: root.pushRuleModel.globalNotificationsEnabled
enabled: root.pushRuleModel.globalNotificationsSet enabled: root.pushRuleModel.globalNotificationsSet
onToggled: { onToggled: {

View File

@@ -267,7 +267,6 @@ FormCard.FormCardPage {
} }
} }
FormCard.FormCheckDelegate { FormCard.FormCheckDelegate {
enabled: root.connection.globalUrlPreviewEnabled
text: i18n("Enable URL previews") text: i18n("Enable URL previews")
// Most users won't see the above setting so tell them the default. // Most users won't see the above setting so tell them the default.
description: room.defaultUrlPreviewState ? i18n("URL previews are enabled by default in this room") : i18n("URL previews are disabled by default in this room") description: room.defaultUrlPreviewState ? i18n("URL previews are enabled by default in this room") : i18n("URL previews are disabled by default in this room")
@@ -277,19 +276,6 @@ FormCard.FormCardPage {
} }
} }
} }
Kirigami.InlineMessage {
Layout.fillWidth: true
Layout.maximumWidth: Kirigami.Units.gridUnit * 30
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.alignment: Qt.AlignHCenter
text: i18nc("As in the user has switched off showing previews of hyperlinks in timeline messages", "URL previews are currently disabled for your account")
type: Kirigami.MessageType.Information
visible: !root.connection.globalUrlPreviewEnabled
actions: Kirigami.Action {
text: i18n("Enable")
onTriggered: root.connection.globalUrlPreviewEnabled = true
}
}
FormCard.FormHeader { FormCard.FormHeader {
title: i18n("Official Parent Spaces") title: i18n("Official Parent Spaces")
} }

View File

@@ -9,8 +9,6 @@ import QtQuick.Layouts
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard import org.kde.kirigamiaddons.formcard as FormCard
import Quotient
import org.kde.neochat import org.kde.neochat
FormCard.FormCardPage { FormCard.FormCardPage {
@@ -47,23 +45,23 @@ FormCard.FormCardPage {
FormCard.FormRadioDelegate { FormCard.FormRadioDelegate {
text: i18nc("@option:check", "Private (invite only)") text: i18nc("@option:check", "Private (invite only)")
description: i18n("Only invited people can join.") description: i18n("Only invited people can join.")
checked: room.joinRule === JoinRule.Invite checked: room.joinRule === "invite"
enabled: room.canSendState("m.room.join_rules") enabled: room.canSendState("m.room.join_rules")
onCheckedChanged: if (checked && room.joinRule != JoinRule.Invite) { onCheckedChanged: if (checked && room.joinRule != "invite") {
root.room.joinRule = JoinRule.Invite; root.room.joinRule = "invite";
} }
} }
FormCard.FormRadioDelegate { FormCard.FormRadioDelegate {
text: i18nc("@option:check", "Space members") text: i18nc("@option:check", "Space members")
description: i18n("Anyone in the selected spaces can find and join.") + (!["8", "9", "10"].includes(room.version) ? `\n${needUpgradeRoom}` : "") description: i18n("Anyone in the selected spaces can find and join.") + (!["8", "9", "10"].includes(room.version) ? `\n${needUpgradeRoom}` : "")
checked: room.joinRule === JoinRule.Restricted checked: room.joinRule === "restricted"
enabled: room.canSendState("m.room.join_rules") && ["8", "9", "10"].includes(room.version) enabled: room.canSendState("m.room.join_rules") && ["8", "9", "10"].includes(room.version)
onCheckedChanged: if (checked && room.joinRule != JoinRule.Restricted) { onCheckedChanged: if (checked && room.joinRule != "restricted") {
selectSpacesDialog.createObject(QQC2.Overlay.overlay).open(); selectSpacesDialog.createObject(QQC2.Overlay.overlay).open();
} }
contentItem.children: QQC2.Button { contentItem.children: QQC2.Button {
visible: root.room.joinRule === JoinRule.Restricted visible: root.room.joinRule === "restricted"
text: i18n("Select spaces") text: i18n("Select spaces")
icon.name: "list-add" icon.name: "list-add"
@@ -84,20 +82,20 @@ FormCard.FormCardPage {
FormCard.FormRadioDelegate { FormCard.FormRadioDelegate {
text: i18nc("@option:check", "Knock") text: i18nc("@option:check", "Knock")
description: i18n("People not in the room need to request an invite to join the room.") + (!["7", "8", "9", "10"].includes(room.version) ? `\n${needUpgradeRoom}` : "") description: i18n("People not in the room need to request an invite to join the room.") + (!["7", "8", "9", "10"].includes(room.version) ? `\n${needUpgradeRoom}` : "")
checked: room.joinRule === JoinRule.Knock checked: room.joinRule === "knock"
// https://spec.matrix.org/v1.4/rooms/#feature-matrix // https://spec.matrix.org/v1.4/rooms/#feature-matrix
enabled: room.canSendState("m.room.join_rules") && ["7", "8", "9", "10"].includes(room.version) enabled: room.canSendState("m.room.join_rules") && ["7", "8", "9", "10"].includes(room.version)
onCheckedChanged: if (checked && room.joinRule != JoinRule.Knock) { onCheckedChanged: if (checked && room.joinRule != "knock") {
root.room.joinRule = JoinRule.Knock; root.room.joinRule = "knock";
} }
} }
FormCard.FormRadioDelegate { FormCard.FormRadioDelegate {
text: i18nc("@option:check", "Public") text: i18nc("@option:check", "Public")
description: i18nc("@option:check", "Anyone can find and join.") description: i18nc("@option:check", "Anyone can find and join.")
checked: room.joinRule === JoinRule.Public checked: room.joinRule === "public"
enabled: room.canSendState("m.room.join_rules") enabled: room.canSendState("m.room.join_rules")
onCheckedChanged: if (checked && root.room.joinRule != JoinRule.Public) { onCheckedChanged: if (checked && root.room.joinRule != "public") {
root.room.joinRule = JoinRule.Public; root.room.joinRule = "public";
} }
} }
} }

View File

@@ -193,14 +193,6 @@ DelegateChooser {
} }
} }
DelegateChoice {
roleValue: MessageComponentType.Reaction
delegate: ReactionComponent {
room: root.room
maxContentWidth: root.maxContentWidth
}
}
DelegateChoice { DelegateChoice {
roleValue: MessageComponentType.LinkPreview roleValue: MessageComponentType.LinkPreview
delegate: LinkPreviewComponent { delegate: LinkPreviewComponent {

View File

@@ -17,6 +17,7 @@ ecm_add_qml_module(timeline GENERATE_PLUGIN_SOURCE
TimelineEndDelegate.qml TimelineEndDelegate.qml
Bubble.qml Bubble.qml
AvatarFlow.qml AvatarFlow.qml
ReactionDelegate.qml
SectionDelegate.qml SectionDelegate.qml
BaseMessageComponentChooser.qml BaseMessageComponentChooser.qml
MessageComponentChooser.qml MessageComponentChooser.qml
@@ -46,7 +47,6 @@ ecm_add_qml_module(timeline GENERATE_PLUGIN_SOURCE
PdfPreviewComponent.qml PdfPreviewComponent.qml
PollComponent.qml PollComponent.qml
QuoteComponent.qml QuoteComponent.qml
ReactionComponent.qml
ReplyAuthorComponent.qml ReplyAuthorComponent.qml
ReplyButtonComponent.qml ReplyButtonComponent.qml
ReplyComponent.qml ReplyComponent.qml

View File

@@ -128,12 +128,7 @@ QQC2.Control {
TapHandler { TapHandler {
enabled: root.time.toString() !== "Invalid Date" enabled: root.time.toString() !== "Invalid Date"
acceptedButtons: Qt.LeftButton acceptedButtons: Qt.LeftButton
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus
onTapped: RoomManager.maximizeCode(root.author, root.time, root.display, root.componentAttributes.class) onTapped: RoomManager.maximizeCode(root.author, root.time, root.display, root.componentAttributes.class)
}
TapHandler {
acceptedDevices: PointerDevice.TouchScreen
onLongPressed: root.showMessageMenu() onLongPressed: root.showMessageMenu()
} }

View File

@@ -68,13 +68,11 @@ TimelineDelegate {
TapHandler { TapHandler {
acceptedButtons: Qt.RightButton acceptedButtons: Qt.RightButton
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus
onTapped: _private.showMessageMenu() onTapped: _private.showMessageMenu()
} }
TapHandler { TapHandler {
acceptedButtons: Qt.LeftButton acceptedButtons: Qt.LeftButton
acceptedDevices: PointerDevice.TouchScreen
onLongPressed: _private.showMessageMenu() onLongPressed: _private.showMessageMenu()
} }

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