Compare commits
57 Commits
work/nvrwh
...
v22.09
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2839d44ea8 | ||
|
|
5f32ae79c1 | ||
|
|
0fc94310c3 | ||
|
|
7f26651edf | ||
|
|
36880d001c | ||
|
|
e0e289d424 | ||
|
|
b79956871f | ||
|
|
917f77152d | ||
|
|
e28d1918f5 | ||
|
|
149b11ba6f | ||
|
|
c007961ef6 | ||
|
|
ef40f5a747 | ||
|
|
37780c2e3b | ||
|
|
53b9f42399 | ||
|
|
d2ed1cfb2e | ||
|
|
c7d4b1a529 | ||
|
|
a8045f2134 | ||
|
|
b8262fef92 | ||
|
|
3071901a47 | ||
|
|
396260549a | ||
|
|
50c2de8d52 | ||
|
|
d4e067d57c | ||
|
|
3797b0129a | ||
|
|
59b4146c63 | ||
|
|
c76524d540 | ||
|
|
10030efd08 | ||
|
|
918bd5439c | ||
|
|
e3ff50bbe8 | ||
|
|
76edc858aa | ||
|
|
2a2c117ac1 | ||
|
|
d3e4640af9 | ||
|
|
7e1f0f4ea7 | ||
|
|
80bf279321 | ||
|
|
6eb4b8c9d2 | ||
|
|
4bba505da6 | ||
|
|
c2fc4e44a7 | ||
|
|
274bf824e3 | ||
|
|
d70a8a652a | ||
|
|
8c436075d8 | ||
|
|
828032ba06 | ||
|
|
c30abfefa9 | ||
|
|
5d4efad2f8 | ||
|
|
3a391aafe8 | ||
|
|
7b5b8197bc | ||
|
|
55659e488f | ||
|
|
c2c235eff1 | ||
|
|
f6f4edec86 | ||
|
|
55847cb9cc | ||
|
|
736c4b02ed | ||
|
|
4cf5b516d0 | ||
|
|
c379a7fa27 | ||
|
|
2318fb95d9 | ||
|
|
14e57e7833 | ||
|
|
8c9ea72a9b | ||
|
|
efd3f2e8d0 | ||
|
|
ea70782771 | ||
|
|
6cca3dbe3a |
@@ -31,8 +31,7 @@ Copyright: 2021 Carl Schwan <carlschwan@kde.org>
|
||||
License: BSD-2-Clause
|
||||
|
||||
Files: src/neochatconfig.kcfg
|
||||
Copyright: 2020-2021 Carl Schwan <carlschwan@kde.org>
|
||||
Copyright: 2020-2021 Tobias Fella <fella@posteo.de>
|
||||
Copyright: 2020-2021 Carl Schwan <carlschwan@kde.org>, Tobias Fella <fella@posteo.de>
|
||||
License: BSD-2-Clause
|
||||
|
||||
Files: src/neochat.notifyrc
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(NeoChat)
|
||||
set(PROJECT_VERSION "22.06")
|
||||
set(PROJECT_VERSION "22.09")
|
||||
|
||||
set(KF5_MIN_VERSION "5.88.0")
|
||||
set(KF5_MIN_VERSION "5.91.0")
|
||||
set(QT_MIN_VERSION "5.15.2")
|
||||
|
||||
find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE)
|
||||
@@ -25,12 +25,14 @@ include(FeatureSummary)
|
||||
include(ECMSetupVersion)
|
||||
include(KDEInstallDirs)
|
||||
include(ECMFindQmlModule)
|
||||
include(KDEClangFormat)
|
||||
include(KDECMakeSettings)
|
||||
include(KDECompilerSettings NO_POLICY_SCOPE)
|
||||
include(ECMAddAppIcon)
|
||||
include(KDEGitCommitHooks)
|
||||
include(ECMCheckOutboundLicense)
|
||||
if (NOT ANDROID)
|
||||
include(KDEClangFormat)
|
||||
endif()
|
||||
|
||||
if(NEOCHAT_FLATPAK)
|
||||
include(cmake/Flatpak.cmake)
|
||||
@@ -137,11 +139,12 @@ add_subdirectory(src)
|
||||
|
||||
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
|
||||
file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES src/*.cpp src/*.h)
|
||||
kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES})
|
||||
|
||||
kde_configure_git_pre_commit_hook(CHECKS CLANG_FORMAT)
|
||||
if (NOT ANDROID)
|
||||
file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES src/*.cpp src/*.h)
|
||||
kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES})
|
||||
|
||||
kde_configure_git_pre_commit_hook(CHECKS CLANG_FORMAT)
|
||||
endif()
|
||||
file(GLOB_RECURSE ALL_SOURCE_FILES *.cpp *.h *.qml)
|
||||
# CI installs dependency headers to _install and _build, which break the reuse check
|
||||
# Fixes the test by excluding this directory
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
|
||||
<meta-data android:name="android.app.lib_name" android:value="neochat"/>
|
||||
<meta-data android:name="android.app.lib_name" android:value="neochat-app"/>
|
||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
||||
|
||||
@@ -9,6 +9,6 @@ install(
|
||||
FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Flatpak/99-noto-mono-color-emoji.conf
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_SYSCONFDIR}/fonts/conf.d/
|
||||
${CMAKE_INSTALL_SYSCONFDIR}/fonts/local.conf
|
||||
)
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ ApplicationWindow {
|
||||
property string blurhash: ""
|
||||
property int imageWidth: -1
|
||||
property int imageHeight: -1
|
||||
property var modelData
|
||||
|
||||
flags: Qt.FramelessWindowHint | Qt.WA_TranslucentBackground
|
||||
|
||||
@@ -52,6 +53,24 @@ ApplicationWindow {
|
||||
source: root.blurhash !== "" ? ("image://blurhash/" + root.blurhash) : ""
|
||||
visible: root.blurhash !== "" && parent.status !== Image.Ready
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.RightButton
|
||||
onTapped: {
|
||||
const contextMenu = fileDelegateContextMenu.createObject(parent, {
|
||||
author: modelData.author,
|
||||
message: modelData.message,
|
||||
eventId: modelData.eventId,
|
||||
source: modelData.source,
|
||||
file: root.parent,
|
||||
mimeType: modelData.mimeType,
|
||||
progressInfo: modelData.progressInfo,
|
||||
plainMessage: modelData.message,
|
||||
});
|
||||
contextMenu.closeFullscreen.connect(root.destroy)
|
||||
contextMenu.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
|
||||
@@ -14,6 +14,8 @@ TimelineContainer {
|
||||
innerObject: TextEdit {
|
||||
text: i18n("This message is encrypted and the sender has not shared the key with this device.")
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
selectedTextColor: Kirigami.Theme.highlightedTextColor
|
||||
selectionColor: Kirigami.Theme.highlightColor
|
||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize
|
||||
selectByMouse: !Kirigami.Settings.isMobile
|
||||
readOnly: true
|
||||
|
||||
@@ -42,9 +42,7 @@ DelegateChooser {
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "sticker"
|
||||
delegate: ImageDelegate {
|
||||
cardBackground: false
|
||||
}
|
||||
delegate: ImageDelegate {}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
|
||||
@@ -53,6 +53,7 @@ TimelineContainer {
|
||||
icon.name: "document-open"
|
||||
|
||||
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; offers ability to open its downloaded file with an appropriate application", "Open File")
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
|
||||
onClicked: openSavedFile()
|
||||
}
|
||||
@@ -70,6 +71,7 @@ TimelineContainer {
|
||||
icon.name: "media-playback-stop"
|
||||
|
||||
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; stops downloading the message's file", "Stop Download")
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
onClicked: currentRoom.cancelFileTransfer(eventId)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -91,10 +91,11 @@ TimelineContainer {
|
||||
onTapped: {
|
||||
fullScreenImage.createObject(parent, {
|
||||
filename: eventId,
|
||||
source: model.mediaUrl,
|
||||
source: mediaUrl,
|
||||
blurhash: model.content.info["xyz.amorgan.blurhash"],
|
||||
imageWidth: content.info.w,
|
||||
imageHeight: content.info.h
|
||||
imageHeight: content.info.h,
|
||||
modelData: model
|
||||
}).showFullScreen();
|
||||
}
|
||||
}
|
||||
|
||||
64
imports/NeoChat/Component/Timeline/LinkPreviewDelegate.qml
Normal file
64
imports/NeoChat/Component/Timeline/LinkPreviewDelegate.qml
Normal file
@@ -0,0 +1,64 @@
|
||||
// SPDX-FileCopyrightText: 2022 Bharadwaj Raju <bharadwaj.raju777@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-or-later OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import org.kde.kirigami 2.15 as Kirigami
|
||||
|
||||
import org.kde.neochat 1.0
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
property var links: model.display.match(/(\bhttps?:\/\/[^\s\<\>\"\']*[^\s\<\>\"\'])/g)
|
||||
// don't show previews for room links or user mentions
|
||||
.filter(link => !link.includes("https://matrix.to"))
|
||||
// remove ending fullstops and commas
|
||||
.map(link => (link.length && [".", ","].includes(link[link.length-1])) ? link.substring(0, link.length-1) : link)
|
||||
LinkPreviewer {
|
||||
id: lp
|
||||
url: links[0]
|
||||
}
|
||||
visible: lp.loaded && lp.title
|
||||
Rectangle {
|
||||
Layout.fillHeight: true
|
||||
width: Kirigami.Units.smallSpacing
|
||||
visible: lp.loaded && lp.title
|
||||
color: Kirigami.Theme.highlightColor
|
||||
}
|
||||
Image {
|
||||
visible: lp.imageSource
|
||||
Layout.maximumHeight: Kirigami.Units.gridUnit * 5
|
||||
Layout.maximumWidth: Kirigami.Units.gridUnit * 5
|
||||
source: lp.imageSource.replace("mxc://", "image://mxc/")
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
ColumnLayout {
|
||||
id: column
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
Kirigami.Heading {
|
||||
Layout.maximumWidth: messageDelegate.bubbleMaxWidth
|
||||
Layout.fillWidth: true
|
||||
level: 4
|
||||
wrapMode: Text.Wrap
|
||||
textFormat: Text.RichText
|
||||
text: "<style>
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
<a href=\"" + links[0] + "\">" + lp.title.replace("–", "—") + "</a>"
|
||||
visible: lp.loaded
|
||||
onLinkActivated: RoomManager.openResource(link)
|
||||
}
|
||||
Label {
|
||||
text: lp.description
|
||||
Layout.maximumWidth: messageDelegate.bubbleMaxWidth
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.Wrap
|
||||
visible: lp.loaded && lp.description
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,27 @@ TimelineContainer {
|
||||
id: messageDelegate
|
||||
|
||||
property bool isEmote: false
|
||||
onOpenContextMenu: openMessageContext(model, parent.selectedText, Controller.plainText(label.textDocument))
|
||||
onOpenContextMenu: openMessageContext(model, label.selectedText, Controller.plainText(label.textDocument))
|
||||
|
||||
onReplyClicked: ListView.view.goToEvent(eventID)
|
||||
hoverComponent: hoverActions
|
||||
|
||||
innerObject: RichLabel {
|
||||
id: label
|
||||
isEmote: messageDelegate.isEmote
|
||||
Layout.maximumWidth: messageDelegate.bubbleMaxWidth
|
||||
innerObject: ColumnLayout {
|
||||
RichLabel {
|
||||
id: label
|
||||
isEmote: messageDelegate.isEmote
|
||||
Layout.maximumWidth: messageDelegate.bubbleMaxWidth
|
||||
}
|
||||
Loader {
|
||||
id: linkPreviewLoader
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
height: active ? item.implicitHeight : 0
|
||||
active: !currentRoom.usesEncryption && model.display && model.display.includes("http")
|
||||
visible: active
|
||||
sourceComponent: LinkPreviewDelegate {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ TextEdit {
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
|
||||
persistentSelection: true
|
||||
|
||||
text: "<style>
|
||||
table {
|
||||
width:100%;
|
||||
@@ -56,6 +58,8 @@ a{
|
||||
</style>" + (isEmote ? "* <a href='https://matrix.to/#/" + author.id + "' style='color: " + author.color + "'>" + author.displayName + "</a> " : "") + textMessage + (isEdited ? (" <span style=\"color: " + Kirigami.Theme.disabledTextColor + "\">" + "<span style='font-size: " + Kirigami.Theme.defaultFont.pixelSize +"px'>" + i18n(" (edited)") + "</span>") : "")
|
||||
|
||||
color: Kirigami.Theme.textColor
|
||||
selectedTextColor: Kirigami.Theme.highlightedTextColor
|
||||
selectionColor: Kirigami.Theme.highlightColor
|
||||
font.pointSize: model.reply === undefined && isEmoji.test(model.display) ? Kirigami.Theme.defaultFont.pointSize * 4 : Kirigami.Theme.defaultFont.pointSize
|
||||
selectByMouse: !Kirigami.Settings.isMobile
|
||||
readOnly: true
|
||||
|
||||
@@ -229,6 +229,7 @@ QQC2.ItemDelegate {
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
QQC2.ToolTip.visible: hoverHandler.hovered
|
||||
QQC2.ToolTip.text: time.toLocaleString(Qt.locale(), Locale.LongFormat)
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
|
||||
HoverHandler {
|
||||
id: hoverHandler
|
||||
|
||||
@@ -12,3 +12,4 @@ EncryptedDelegate 1.0 EncryptedDelegate.qml
|
||||
EventDelegate 1.0 EventDelegate.qml
|
||||
MessageDelegate 1.0 MessageDelegate.qml
|
||||
ReadMarkerDelegate 1.0 ReadMarkerDelegate.qml
|
||||
LinkPreviewDelegate 1.0 LinkPreviewDelegate.qml
|
||||
|
||||
39
imports/NeoChat/Dialog/KeyVerification/EmojiItem.qml
Normal file
39
imports/NeoChat/Dialog/KeyVerification/EmojiItem.qml
Normal file
@@ -0,0 +1,39 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15 as QQC2
|
||||
import QtQml 2.15
|
||||
|
||||
import org.kde.kirigami 2.19 as Kirigami
|
||||
import org.kde.neochat 1.0
|
||||
|
||||
Column {
|
||||
id: emojiItem
|
||||
|
||||
property string emoji
|
||||
property string description
|
||||
|
||||
QQC2.Label {
|
||||
id: emojiLabel
|
||||
x: 0
|
||||
y: 0
|
||||
width: parent.width
|
||||
height: parent.height * 0.75
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
text: emojiItem.emoji
|
||||
font.family: "emoji"
|
||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 4
|
||||
}
|
||||
QQC2.Label {
|
||||
x: 0
|
||||
y: parent.height * 0.75
|
||||
width: parent.width
|
||||
height: parent.height * 0.25
|
||||
text: emojiItem.description
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
25
imports/NeoChat/Dialog/KeyVerification/EmojiRow.qml
Normal file
25
imports/NeoChat/Dialog/KeyVerification/EmojiRow.qml
Normal file
@@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15 as QQC2
|
||||
import QtQml 2.15
|
||||
|
||||
import org.kde.kirigami 2.19 as Kirigami
|
||||
import org.kde.neochat 1.0
|
||||
|
||||
Row {
|
||||
id: emojiRow
|
||||
|
||||
property alias model: repeater.model
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
Repeater {
|
||||
id: repeater
|
||||
delegate: EmojiItem {
|
||||
emoji: modelData.emoji
|
||||
description: modelData.description
|
||||
width: emojiRow.height
|
||||
height: width
|
||||
}
|
||||
}
|
||||
}
|
||||
50
imports/NeoChat/Dialog/KeyVerification/EmojiSas.qml
Normal file
50
imports/NeoChat/Dialog/KeyVerification/EmojiSas.qml
Normal file
@@ -0,0 +1,50 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15 as QQC2
|
||||
import QtQml 2.15
|
||||
|
||||
import org.kde.kirigami 2.19 as Kirigami
|
||||
import org.kde.neochat 1.0
|
||||
|
||||
Column {
|
||||
id: emojiSas
|
||||
|
||||
required property var model
|
||||
|
||||
signal accept()
|
||||
signal reject()
|
||||
|
||||
visible: dialog.session.state === KeyVerificationSession.WAITINGFORVERIFICATION
|
||||
anchors.centerIn: parent
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
QQC2.Label {
|
||||
text: i18n("Confirm the emoji below are displayed on both devices, in the same order.")
|
||||
}
|
||||
EmojiRow {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: Kirigami.Units.gridUnit * 4
|
||||
model: emojiSas.model.slice(0, 4)
|
||||
}
|
||||
EmojiRow {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: Kirigami.Units.gridUnit * 4
|
||||
model: emojiSas.model.slice(4, 7)
|
||||
}
|
||||
Row {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
QQC2.Button {
|
||||
anchors.bottom: parent.bottom
|
||||
text: i18n("They match")
|
||||
icon.name: "dialog-ok"
|
||||
onClicked: emojiSas.accept()
|
||||
}
|
||||
QQC2.Button {
|
||||
anchors.bottom: parent.bottom
|
||||
text: i18n("They don't match")
|
||||
icon.name: "dialog-cancel"
|
||||
onClicked: emojiSas.reject()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15 as QQC2
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQml 2.15
|
||||
|
||||
import org.kde.kirigami 2.19 as Kirigami
|
||||
import org.kde.neochat 1.0
|
||||
|
||||
Kirigami.Page {
|
||||
id: dialog
|
||||
title: i18n("Session Verification")
|
||||
|
||||
required property var session
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
VerificationCanceled {
|
||||
visible: dialog.session.state === KeyVerificationSession.CANCELED
|
||||
anchors.centerIn: parent
|
||||
reason: dialog.session.error
|
||||
}
|
||||
EmojiSas {
|
||||
anchors.centerIn: parent
|
||||
visible: dialog.session.state === KeyVerificationSession.WAITINGFORVERIFICATION
|
||||
model: dialog.session.sasEmojis
|
||||
onReject: dialog.session.cancelVerification(KeyVerificationSession.MISMATCHED_SAS)
|
||||
onAccept: dialog.session.sendMac()
|
||||
}
|
||||
Message {
|
||||
visible: dialog.session.state === KeyVerificationSession.WAITINGFORREADY
|
||||
anchors.centerIn: parent
|
||||
icon: "security-medium-symbolic"
|
||||
text: i18n("Waiting for device to accept verification.")
|
||||
}
|
||||
Message {
|
||||
visible: dialog.session.state === KeyVerificationSession.INCOMING
|
||||
anchors.centerIn: parent
|
||||
icon: "security-medium-symbolic"
|
||||
text: i18n("Incoming key verification request from device **%1**", dialog.session.remoteDeviceId)
|
||||
}
|
||||
Message {
|
||||
visible: dialog.session.state === KeyVerificationSession.WAITINGFORMAC
|
||||
anchors.centerIn: parent
|
||||
icon: "security-medium-symbolic"
|
||||
text: i18n("Waiting for other party to verify.")
|
||||
}
|
||||
Kirigami.BasicListItem {
|
||||
id: emojiVerification
|
||||
text: "Emoji Verification"
|
||||
visible: dialog.session.state === KeyVerificationSession.READY
|
||||
subtitle: i18n("Compare a set of emoji on both devices")
|
||||
onClicked: {
|
||||
dialog.session.sendStartSas()
|
||||
}
|
||||
}
|
||||
Message {
|
||||
visible: dialog.session.state === KeyVerificationSession.DONE
|
||||
anchors.centerIn: parent
|
||||
text: i18n("Successfully verified device **%1**", dialog.session.remoteDeviceId)
|
||||
icon: "security-high"
|
||||
}
|
||||
}
|
||||
|
||||
footer: QQC2.ToolBar {
|
||||
visible: dialog.session.state === KeyVerificationSession.INCOMING
|
||||
QQC2.DialogButtonBox {
|
||||
anchors.fill: parent
|
||||
Item { Layout.fillWidth: true }
|
||||
QQC2.Button {
|
||||
text: i18n("Accept")
|
||||
icon.name: "dialog-ok"
|
||||
onClicked: dialog.session.sendReady()
|
||||
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole
|
||||
}
|
||||
QQC2.Button {
|
||||
text: i18n("Decline")
|
||||
icon.name: "dialog-cancel"
|
||||
onClicked: dialog.session.cancelVerification("m.user", "Declined")
|
||||
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.CancelRole
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
27
imports/NeoChat/Dialog/KeyVerification/Message.qml
Normal file
27
imports/NeoChat/Dialog/KeyVerification/Message.qml
Normal file
@@ -0,0 +1,27 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15 as QQC2
|
||||
import QtQml 2.15
|
||||
|
||||
import org.kde.kirigami 2.19 as Kirigami
|
||||
import org.kde.neochat 1.0
|
||||
|
||||
Column {
|
||||
id: message
|
||||
required property string icon
|
||||
required property string text
|
||||
|
||||
anchors.centerIn: parent
|
||||
Kirigami.Icon {
|
||||
width: Kirigami.Units.iconSizes.enormous
|
||||
height: width
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
source: message.icon
|
||||
}
|
||||
QQC2.Label {
|
||||
text: message.text
|
||||
textFormat: Text.MarkdownText
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15 as QQC2
|
||||
import QtQml 2.15
|
||||
|
||||
import org.kde.kirigami 2.19 as Kirigami
|
||||
import org.kde.neochat 1.0
|
||||
|
||||
Message {
|
||||
id: verificationCanceled
|
||||
|
||||
required property int reason
|
||||
|
||||
anchors.centerIn: parent
|
||||
icon: "security-low"
|
||||
text: {
|
||||
switch(verificationCanceled.reason) {
|
||||
case KeyVerificationSession.NONE:
|
||||
return i18n("The session verification was canceled for unknown reason.");
|
||||
case KeyVerificationSession.TIMEOUT:
|
||||
return i18n("The session verification timed out.");
|
||||
case KeyVerificationSession.REMOTE_TIMEOUT:
|
||||
return i18n("The session verification timed out for remote party.");
|
||||
case KeyVerificationSession.USER:
|
||||
return i18n("You canceled the session verification.");
|
||||
case KeyVerificationSession.REMOTE_USER:
|
||||
return i18n("The remote party canceled the session verification.");
|
||||
case KeyVerificationSession.UNEXPECTED_MESSAGE:
|
||||
return i18n("The session verification was canceled because we received an unexpected message.");
|
||||
case KeyVerificationSession.REMOTE_UNEXPECTED_MESSAGE:
|
||||
return i18n("The remote party canceled the session verification because it received an unexpected message.");
|
||||
case KeyVerificationSession.UNKNOWN_TRANSACTION:
|
||||
return i18n("The session verification was canceled because it received a message for an unknown session.");
|
||||
case KeyVerificationSession.REMOTE_UNKNOWN_TRANSACTION:
|
||||
return i18n("The remote party canceled the session verification because it received a message for an unknown session.");
|
||||
case KeyVerificationSession.UNKNOWN_METHOD:
|
||||
return i18n("The session verification was canceled because NeoChat is unable to handle this verification method.");
|
||||
case KeyVerificationSession.REMOTE_UNKNOWN_METHOD:
|
||||
return i18n("The remote party canceled the session verification because it is unable to handle this verification method.");
|
||||
case KeyVerificationSession.KEY_MISMATCH:
|
||||
return i18n("The session verification was canceled because the keys are incorrect.");
|
||||
case KeyVerificationSession.REMOTE_KEY_MISMATCH:
|
||||
return i18n("The remote party canceled the session verification because the keys are incorrect.");
|
||||
case KeyVerificationSession.USER_MISMATCH:
|
||||
return i18n("The session verification was canceled because it verifies an unexpected user.");
|
||||
case KeyVerificationSession.REMOTE_USER_MISMATCH:
|
||||
return i18n("The remote party canceled the session verification because it verifies an unexpected user.");
|
||||
case KeyVerificationSession.INVALID_MESSAGE:
|
||||
return i18n("The session verification was canceled because we received an invalid message.");
|
||||
case KeyVerificationSession.REMOTE_INVALID_MESSAGE:
|
||||
return i18n("The remote party canceled the session verification because it received an invalid message.");
|
||||
case KeyVerificationSession.SESSION_ACCEPTED:
|
||||
return i18n("The session was accepted on a different device"); //TODO this should not be visible
|
||||
case KeyVerificationSession.REMOTE_SESSION_ACCEPTED:
|
||||
return i18n("The session was accepted on a different device"); //TODO neither should this
|
||||
case KeyVerificationSession.MISMATCHED_COMMITMENT:
|
||||
return i18n("The session verification was canceled because of a mismatched key.");
|
||||
case KeyVerificationSession.REMOTE_MISMATCHED_COMMITMENT:
|
||||
return i18n("The remote party canceled the session verification because of a mismatched key.");
|
||||
case KeyVerificationSession.MISMATCHED_SAS:
|
||||
return i18n("The session verification was canceled because the keys do not match.");
|
||||
case KeyVerificationSession.REMOTE_MISMATCHED_SAS:
|
||||
return i18n("The remote party canceled the session verification because the keys do not match.");
|
||||
default:
|
||||
return i18n("The session verification was canceled due to an unknown error.");
|
||||
}
|
||||
}
|
||||
}
|
||||
7
imports/NeoChat/Dialog/KeyVerification/qmldir
Normal file
7
imports/NeoChat/Dialog/KeyVerification/qmldir
Normal file
@@ -0,0 +1,7 @@
|
||||
module NeoChat.Dialog.KeyVerification
|
||||
KeyVerificationDialog 1.0 KeyVerificationDialog.qml
|
||||
Message 1.0 Message.qml
|
||||
VerificationCanceled 1.0 VerificationCanceled.qml
|
||||
EmojiItem 1.0 EmojiItem.qml
|
||||
EmojiRow 1.0 EmojiRow.qml
|
||||
EmojiSas 1.0 EmojiSas.qml
|
||||
@@ -14,6 +14,8 @@ import NeoChat.Component 1.0
|
||||
Kirigami.OverlaySheet {
|
||||
id: root
|
||||
|
||||
signal closed()
|
||||
|
||||
property var room
|
||||
property var user
|
||||
|
||||
@@ -52,7 +54,7 @@ Kirigami.OverlaySheet {
|
||||
|
||||
onClicked: {
|
||||
if (avatarMediaId) {
|
||||
fullScreenImage.createObject(parent, {"filename": displayName, "source": room.urlToMxcUrl(avatarUrl)}).showFullScreen()
|
||||
fullScreenImage.createObject(parent, {filename: displayName, source: room.urlToMxcUrl(avatarUrl)}).showFullScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,5 +166,11 @@ Kirigami.OverlaySheet {
|
||||
FullScreenImage {}
|
||||
}
|
||||
}
|
||||
|
||||
onSheetOpenChanged: {
|
||||
if (!sheetOpen) {
|
||||
closed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,3 +7,4 @@ OpenFileDialog 1.0 OpenFileDialog.qml
|
||||
ImageClipboardDialog 1.0 ImageClipboardDialog.qml
|
||||
StartChatDialog 1.0 StartChatDialog.qml
|
||||
EmojiDialog 1.0 EmojiDialog.qml
|
||||
KeyVerificationDialog 1.0 KeyVerificationDialog.qml
|
||||
|
||||
@@ -45,7 +45,7 @@ Labs.MenuBar {
|
||||
Labs.MenuItem {
|
||||
text: i18nc("menu", "New Private Chat…")
|
||||
enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat") && Controller.accountCount > 0
|
||||
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/StartChatPage.qml", {"connection": Controller.activeConnection})
|
||||
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/StartChatPage.qml", {connection: Controller.activeConnection})
|
||||
}
|
||||
Labs.MenuItem {
|
||||
text: i18nc("menu", "New Group…")
|
||||
@@ -58,7 +58,7 @@ Labs.MenuBar {
|
||||
}
|
||||
Labs.MenuItem {
|
||||
text: i18nc("menu", "Browse Chats…")
|
||||
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/JoinRoomPage.qml", {"connection": Controller.activeConnection})
|
||||
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/JoinRoomPage.qml", {connection: Controller.activeConnection})
|
||||
}
|
||||
}
|
||||
EditMenu {
|
||||
|
||||
@@ -24,7 +24,7 @@ Loader {
|
||||
Menu {
|
||||
MenuItem {
|
||||
id: newWindow
|
||||
text: i18n("Open in new window")
|
||||
text: i18n("Open in New Window")
|
||||
onTriggered: RoomManager.openWindow(room);
|
||||
visible: !Kirigami.Settings.isMobile
|
||||
}
|
||||
@@ -49,7 +49,7 @@ Loader {
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: i18nc("@action:inmenu", "Copy address to clipboard")
|
||||
text: i18nc("@action:inmenu", "Copy Address to Clipboard")
|
||||
onTriggered: if (room.canonicalAlias.length === 0) {
|
||||
Clipboard.saveText(room.id)
|
||||
} else {
|
||||
@@ -57,8 +57,53 @@ Loader {
|
||||
}
|
||||
}
|
||||
|
||||
Menu {
|
||||
title: i18n("Notification State")
|
||||
|
||||
MenuItem {
|
||||
text: i18n("Follow Global Setting")
|
||||
checkable: true
|
||||
autoExclusive: true
|
||||
checked: room.pushNotificationState === PushNotificationState.Default
|
||||
enabled: room.pushNotificationState != PushNotificationState.Unknown
|
||||
onTriggered: {
|
||||
room.pushNotificationState = PushNotificationState.Default
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: i18nc("As in 'notify for all messages'","All")
|
||||
checkable: true
|
||||
autoExclusive: true
|
||||
checked: room.pushNotificationState === PushNotificationState.All
|
||||
enabled: room.pushNotificationState != PushNotificationState.Unknown
|
||||
onTriggered: {
|
||||
room.pushNotificationState = PushNotificationState.All
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: i18nc("As in 'notify when the user is mentioned or the message contains a set keyword'","@Mentions and Keywords")
|
||||
checkable: true
|
||||
autoExclusive: true
|
||||
checked: room.pushNotificationState === PushNotificationState.MentionKeyword
|
||||
enabled: room.pushNotificationState != PushNotificationState.Unknown
|
||||
onTriggered: {
|
||||
room.pushNotificationState = PushNotificationState.MentionKeyword
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: i18nc("As in 'do not notify for any messages'","Off")
|
||||
checkable: true
|
||||
autoExclusive: true
|
||||
checked: room.pushNotificationState === PushNotificationState.Mute
|
||||
enabled: room.pushNotificationState != PushNotificationState.Unknown
|
||||
onTriggered: {
|
||||
room.pushNotificationState = PushNotificationState.Mute
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: i18n("Room settings")
|
||||
text: i18n("Room Settings")
|
||||
onTriggered: ApplicationWindow.window.pageStack.pushDialogLayer('qrc:/imports/NeoChat/RoomSettings/Categories.qml', {room: room})
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ import NeoChat.Menu 1.0
|
||||
MessageDelegateContextMenu {
|
||||
id: root
|
||||
|
||||
signal closeFullscreen
|
||||
|
||||
required property var file
|
||||
required property var progressInfo
|
||||
required property string mimeType
|
||||
@@ -51,6 +53,7 @@ MessageDelegateContextMenu {
|
||||
icon.name: "mail-replied-symbolic"
|
||||
onTriggered: {
|
||||
chatBoxHelper.replyToMessage(eventId, message, author);
|
||||
root.closeFullscreen()
|
||||
}
|
||||
},
|
||||
Kirigami.Action {
|
||||
@@ -60,8 +63,18 @@ MessageDelegateContextMenu {
|
||||
icon.color: "red"
|
||||
onTriggered: {
|
||||
currentRoom.redactEvent(eventId);
|
||||
root.closeFullscreen()
|
||||
}
|
||||
},
|
||||
Kirigami.Action {
|
||||
text: i18nc("@action:button 'Report' as in 'Report this event to the administrators'", "Report")
|
||||
icon.name: "dialog-warning-symbolic"
|
||||
visible: author.id !== currentRoom.localUser.id
|
||||
onTriggered: applicationWindow().pageStack.pushDialogLayer("qrc:/imports/NeoChat/Menu/Timeline/ReportSheet.qml", {room: currentRoom, eventId: eventId}, {
|
||||
title: i18nc("@title", "Report Message"),
|
||||
width: Kirigami.Units.gridUnit * 25
|
||||
})
|
||||
},
|
||||
Kirigami.Action {
|
||||
text: i18n("View Source")
|
||||
icon.name: "code-context"
|
||||
@@ -72,6 +85,7 @@ MessageDelegateContextMenu {
|
||||
title: i18n("Message Source"),
|
||||
width: Kirigami.Units.gridUnit * 25
|
||||
});
|
||||
root.closeFullscreen()
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -48,6 +48,15 @@ Loader {
|
||||
icon.name: "edit-copy"
|
||||
onTriggered: Clipboard.saveText(loadRoot.selectedText === "" ? loadRoot.plainMessage : loadRoot.selectedText)
|
||||
},
|
||||
Kirigami.Action {
|
||||
text: i18nc("@action:button 'Report' as in 'Report this event to the administrators'", "Report")
|
||||
icon.name: "dialog-warning-symbolic"
|
||||
visible: author.id !== currentRoom.localUser.id
|
||||
onTriggered: applicationWindow().pageStack.pushDialogLayer("qrc:/imports/NeoChat/Menu/Timeline/ReportSheet.qml", {room: currentRoom, eventId: eventId}, {
|
||||
title: i18nc("@title", "Report Message"),
|
||||
width: Kirigami.Units.gridUnit * 25
|
||||
})
|
||||
},
|
||||
Kirigami.Action {
|
||||
text: i18n("View Source")
|
||||
icon.name: "code-context"
|
||||
|
||||
47
imports/NeoChat/Menu/Timeline/ReportSheet.qml
Normal file
47
imports/NeoChat/Menu/Timeline/ReportSheet.qml
Normal file
@@ -0,0 +1,47 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15 as QQC2
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import org.kde.kirigami 2.20 as Kirigami
|
||||
|
||||
Kirigami.Page {
|
||||
id: reportSheet
|
||||
|
||||
property var room
|
||||
property string eventId
|
||||
|
||||
title: i18n("Report Message")
|
||||
|
||||
QQC2.TextArea {
|
||||
id: reason
|
||||
placeholderText: i18n("Reason for reporting this message")
|
||||
anchors.fill: parent
|
||||
wrapMode: TextEdit.Wrap
|
||||
}
|
||||
|
||||
footer: QQC2.ToolBar {
|
||||
QQC2.DialogButtonBox {
|
||||
anchors.fill: parent
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
QQC2.Button {
|
||||
text: i18nc("@action:button 'Report' as in 'Report this event to the administrators'", "Report")
|
||||
icon.name: "dialog-warning-symbolic"
|
||||
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole
|
||||
onClicked: {
|
||||
reportSheet.room.reportEvent(eventId, reason.text)
|
||||
reportSheet.closeDialog()
|
||||
}
|
||||
}
|
||||
QQC2.Button {
|
||||
text: i18nc("@action", "Cancel")
|
||||
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.RejectRole
|
||||
onClicked: reportSheet.closeDialog()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,3 +2,4 @@ module NeoChat.Menu.Timeline
|
||||
MessageDelegateContextMenu 1.0 MessageDelegateContextMenu.qml
|
||||
FileDelegateContextMenu 1.0 FileDelegateContextMenu.qml
|
||||
MessageSourceSheet 1.0 MessageSourceSheet.qml
|
||||
ReportSheet 1.0 ReportSheet.qml
|
||||
|
||||
@@ -3,18 +3,11 @@
|
||||
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQuick.Controls 2.12 as QQC2
|
||||
import org.kde.kirigami 2.12 as Kirigami
|
||||
import org.kde.kirigami 2.19 as Kirigami
|
||||
|
||||
Kirigami.Page {
|
||||
title: i18n("Loading…")
|
||||
|
||||
Kirigami.PlaceholderMessage {
|
||||
Kirigami.LoadingPlaceholder {
|
||||
id: loadingIndicator
|
||||
anchors.centerIn: parent
|
||||
text: i18n("Loading…")
|
||||
QQC2.BusyIndicator {
|
||||
running: false
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ Kirigami.ScrollablePage {
|
||||
flat: true
|
||||
padding: Kirigami.Units.gridUnit / 2
|
||||
icon.name: "home"
|
||||
text: i18nc('@action:button', 'Show All Rooms')
|
||||
text: i18nc("@action:button", "Show All Rooms")
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
onClicked: {
|
||||
@@ -368,7 +368,7 @@ Kirigami.ScrollablePage {
|
||||
}
|
||||
|
||||
function createRoomListContextMenu() {
|
||||
const menu = roomListContextMenu.createObject(page, {"room": currentRoom})
|
||||
const menu = roomListContextMenu.createObject(page, {room: currentRoom})
|
||||
configButton.visible = true
|
||||
configButton.down = true
|
||||
menu.closed.connect(function() {
|
||||
|
||||
@@ -8,7 +8,7 @@ import QtQuick.Layouts 1.15
|
||||
import Qt.labs.platform 1.1 as Platform
|
||||
import Qt.labs.qmlmodels 1.0
|
||||
|
||||
import org.kde.kirigami 2.15 as Kirigami
|
||||
import org.kde.kirigami 2.19 as Kirigami
|
||||
import org.kde.kitemmodels 1.0
|
||||
|
||||
import org.kde.neochat 1.0
|
||||
@@ -105,17 +105,17 @@ Kirigami.ScrollablePage {
|
||||
function onShowMessage(messageType, message) {
|
||||
page.header.contentItem.text = message;
|
||||
page.header.contentItem.type = messageType === ActionsHandler.Error ? Kirigami.MessageType.Error : Kirigami.MessageType.Information;
|
||||
page.header.contentItem.visible = true;
|
||||
page.header.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
header: QQC2.Control {
|
||||
height: visible ? implicitHeight : 0
|
||||
visible: contentItem.visible
|
||||
visible: false
|
||||
padding: Kirigami.Units.smallSpacing
|
||||
contentItem: Kirigami.InlineMessage {
|
||||
showCloseButton: true
|
||||
visible: false
|
||||
visible: true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,15 +144,10 @@ Kirigami.ScrollablePage {
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.PlaceholderMessage {
|
||||
Kirigami.LoadingPlaceholder {
|
||||
id: loadingIndicator
|
||||
anchors.centerIn: parent
|
||||
visible: loading
|
||||
text: i18n("Loading…")
|
||||
QQC2.BusyIndicator {
|
||||
running: loadingIndicator.visible
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
focus: true
|
||||
@@ -182,6 +177,15 @@ Kirigami.ScrollablePage {
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: currentRoom
|
||||
function onPositiveMessage(message) {
|
||||
page.header.contentItem.text = message;
|
||||
page.header.contentItem.type = Kirigami.MessageType.Positive;
|
||||
page.header.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
// hover actions on a delegate, activated in TimelineContainer.qml
|
||||
Connections {
|
||||
target: page.flickable
|
||||
@@ -454,6 +458,7 @@ Kirigami.ScrollablePage {
|
||||
property int childOffset: userMsg && Config.showLocalMessagesOnRight && !Config.compactLayout ? (bubble ? bubble.width : 0) - childWidth : Math.max((bubble ? bubble.width : 0) - childWidth, 0)
|
||||
x: delegate && bubble ? (delegate.x + bubble.x + Kirigami.Units.largeSpacing + childOffset - (Config.compactLayout ? Kirigami.Units.gridUnit * 3 : 0)) : 0
|
||||
y: bubble ? bubble.mapToItem(parent, 0, 0).y - hoverActions.childHeight + Kirigami.Units.smallSpacing: 0;
|
||||
|
||||
visible: false
|
||||
|
||||
property var updateFunction
|
||||
@@ -469,10 +474,23 @@ Kirigami.ScrollablePage {
|
||||
id: hoverHandler
|
||||
margin: Kirigami.Units.smallSpacing
|
||||
}
|
||||
Kirigami.Icon {
|
||||
source: "security-high"
|
||||
width: height
|
||||
height: parent.height
|
||||
visible: hoverActions.event.verified
|
||||
HoverHandler {
|
||||
id: hover
|
||||
}
|
||||
QQC2.ToolTip.text: i18n("This message was sent from a verified device")
|
||||
QQC2.ToolTip.visible: hover.hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
|
||||
QQC2.Button {
|
||||
QQC2.ToolTip.text: i18n("React")
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
icon.name: "preferences-desktop-emoticons"
|
||||
onClicked: emojiDialog.open();
|
||||
EmojiDialog {
|
||||
@@ -486,6 +504,7 @@ Kirigami.ScrollablePage {
|
||||
QQC2.Button {
|
||||
QQC2.ToolTip.text: i18n("Edit")
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
visible: hoverActions.showEdit
|
||||
icon.name: "document-edit"
|
||||
onClicked: {
|
||||
@@ -498,6 +517,7 @@ Kirigami.ScrollablePage {
|
||||
QQC2.Button {
|
||||
QQC2.ToolTip.text: i18n("Reply")
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
icon.name: "mail-replied-symbolic"
|
||||
onClicked: {
|
||||
chatBoxHelper.replyToMessage(hoverActions.event.eventId, hoverActions.event.message, hoverActions.event.author);
|
||||
@@ -571,7 +591,7 @@ Kirigami.ScrollablePage {
|
||||
function warning(title, message) {
|
||||
page.header.contentItem.text = `${title}<br />${message}`;
|
||||
page.header.contentItem.type = Kirigami.MessageType.Warning;
|
||||
page.header.contentItem.visible = true;
|
||||
page.header.visible = true;
|
||||
}
|
||||
|
||||
function showUserDetail(user) {
|
||||
|
||||
@@ -18,7 +18,7 @@ Kirigami.OverlayDrawer {
|
||||
id: roomDrawer
|
||||
readonly property var room: RoomManager.currentRoom
|
||||
|
||||
width: modal ? undefined : actualWidth
|
||||
width: actualWidth
|
||||
|
||||
readonly property int minWidth: Kirigami.Units.gridUnit * 15
|
||||
readonly property int maxWidth: Kirigami.Units.gridUnit * 25
|
||||
@@ -77,48 +77,70 @@ Kirigami.OverlayDrawer {
|
||||
sourceComponent: ColumnLayout {
|
||||
id: columnLayout
|
||||
property alias userSearchText: userListSearchField.text
|
||||
property alias highlightedUser: userListView.currentIndex
|
||||
spacing: 0
|
||||
|
||||
Kirigami.AbstractApplicationHeader {
|
||||
Layout.fillWidth: true
|
||||
topPadding: Kirigami.Units.smallSpacing / 2;
|
||||
bottomPadding: Kirigami.Units.smallSpacing / 2;
|
||||
rightPadding: Kirigami.Units.smallSpacing
|
||||
leftPadding: Kirigami.Units.smallSpacing
|
||||
rightPadding: Kirigami.Units.largeSpacing
|
||||
leftPadding: Kirigami.Units.largeSpacing
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
Kirigami.Heading {
|
||||
Layout.fillWidth: true
|
||||
text: i18n("Room information")
|
||||
level: 1
|
||||
}
|
||||
ToolButton {
|
||||
id: inviteButton
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
icon.name: "list-add-user"
|
||||
text: i18n("Invite")
|
||||
text: i18n("Invite user to room")
|
||||
display: AbstractButton.IconOnly
|
||||
|
||||
onClicked: {
|
||||
applicationWindow().pageStack.layers.push("qrc:/imports/NeoChat/Page/InviteUserPage.qml", {room: room})
|
||||
roomDrawer.close();
|
||||
}
|
||||
}
|
||||
Item {
|
||||
// HACK otherwise rating item is not right aligned
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
ToolTip {
|
||||
text: inviteButton.text
|
||||
}
|
||||
}
|
||||
ToolButton {
|
||||
id: favouriteButton
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
icon.name: room && room.isFavourite ? "rating" : "rating-unrated"
|
||||
checkable: true
|
||||
checked: room && room.isFavourite
|
||||
text: room && room.isFavourite ? i18n("Remove room from favorites") : i18n("Make room favorite")
|
||||
display: AbstractButton.IconOnly
|
||||
|
||||
onClicked: room.isFavourite ? room.removeTag("m.favourite") : room.addTag("m.favourite", 1.0)
|
||||
|
||||
ToolTip {
|
||||
text: room && room.isFavourite ? i18n("Remove room from favorites") : i18n("Make room favorite")
|
||||
text: favouriteButton.text
|
||||
}
|
||||
}
|
||||
ToolButton {
|
||||
id: settingsButton
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
icon.name: 'settings-configure'
|
||||
text: i18n("Room settings")
|
||||
display: AbstractButton.IconOnly
|
||||
|
||||
onClicked: ApplicationWindow.window.pageStack.pushDialogLayer('qrc:/imports/NeoChat/RoomSettings/Categories.qml', {room: room})
|
||||
|
||||
ToolTip {
|
||||
text: i18n("Room settings")
|
||||
text: settingsButton.text
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,14 +149,11 @@ Kirigami.OverlayDrawer {
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: Kirigami.Units.largeSpacing
|
||||
Kirigami.Heading {
|
||||
text: i18n("Room information")
|
||||
level: 3
|
||||
}
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: Kirigami.Units.largeSpacing
|
||||
|
||||
Layout.leftMargin: Kirigami.Units.smallSpacing
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
Kirigami.Avatar {
|
||||
@@ -151,10 +170,9 @@ Kirigami.OverlayDrawer {
|
||||
spacing: 0
|
||||
|
||||
Kirigami.Heading {
|
||||
Layout.maximumWidth: Kirigami.Units.gridUnit * 9
|
||||
Layout.fillWidth: true
|
||||
level: 1
|
||||
font.bold: true
|
||||
type: Kirigami.Heading.Type.Primary
|
||||
wrapMode: Label.Wrap
|
||||
text: room ? room.displayName : i18n("No name")
|
||||
textFormat: Text.PlainText
|
||||
@@ -165,6 +183,8 @@ Kirigami.OverlayDrawer {
|
||||
wrapMode: Text.WordWrap
|
||||
selectByMouse: true
|
||||
color: Kirigami.Theme.textColor
|
||||
selectedTextColor: Kirigami.Theme.highlightedTextColor
|
||||
selectionColor: Kirigami.Theme.highlightColor
|
||||
readOnly: true
|
||||
text: room && room.canonicalAlias ? room.canonicalAlias : i18n("No Canonical Alias")
|
||||
}
|
||||
@@ -179,6 +199,8 @@ Kirigami.OverlayDrawer {
|
||||
wrapMode: Text.WordWrap
|
||||
selectByMouse: true
|
||||
color: Kirigami.Theme.textColor
|
||||
selectedTextColor: Kirigami.Theme.highlightedTextColor
|
||||
selectionColor: Kirigami.Theme.highlightColor
|
||||
onLinkActivated: UrlHelper.openUrl(link)
|
||||
readOnly: true
|
||||
MouseArea {
|
||||
@@ -192,16 +214,23 @@ Kirigami.OverlayDrawer {
|
||||
Kirigami.ListSectionHeader {
|
||||
label: i18n("Members")
|
||||
activeFocusOnTab: false
|
||||
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: room ? i18np("%1 Member", "%1 Members", room.joinedCount) : i18n("No Member Count")
|
||||
}
|
||||
}
|
||||
|
||||
Pane {
|
||||
padding: Kirigami.Units.smallSpacing
|
||||
implicitWidth: parent.width
|
||||
z: 2
|
||||
Control {
|
||||
Layout.fillWidth: true
|
||||
|
||||
// Note need to set padding individually to guarantee it will always work
|
||||
// see note - https://doc.qt.io/qt-6/qml-qtquick-controls2-control.html#padding-prop
|
||||
topPadding: Kirigami.Units.smallSpacing
|
||||
bottomPadding: Kirigami.Units.smallSpacing
|
||||
rightPadding: Kirigami.Units.largeSpacing
|
||||
leftPadding: Kirigami.Units.largeSpacing
|
||||
|
||||
background: Rectangle {
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
Kirigami.Theme.inherit: false
|
||||
@@ -209,6 +238,7 @@ Kirigami.OverlayDrawer {
|
||||
}
|
||||
contentItem: Kirigami.SearchField {
|
||||
id: userListSearchField
|
||||
|
||||
onAccepted: sortedMessageEventModel.filterString = text;
|
||||
}
|
||||
}
|
||||
@@ -223,8 +253,6 @@ Kirigami.OverlayDrawer {
|
||||
ListView {
|
||||
id: userListView
|
||||
clip: true
|
||||
headerPositioning: ListView.OverlayHeader
|
||||
boundsBehavior: Flickable.DragOverBounds
|
||||
activeFocusOnTab: true
|
||||
|
||||
model: KSortFilterProxyModel {
|
||||
@@ -239,58 +267,50 @@ Kirigami.OverlayDrawer {
|
||||
filterCaseSensitivity: Qt.CaseInsensitive
|
||||
}
|
||||
|
||||
delegate: Kirigami.AbstractListItem {
|
||||
width: userListView.width
|
||||
delegate: Kirigami.BasicListItem {
|
||||
id: userListItem
|
||||
|
||||
implicitHeight: Kirigami.Units.gridUnit * 2
|
||||
z: 1
|
||||
leftPadding: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing
|
||||
|
||||
contentItem: RowLayout {
|
||||
Kirigami.Avatar {
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
||||
visible: Config.showAvatarInRoomDrawer
|
||||
sourceSize.height: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
||||
sourceSize.width: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
||||
source: avatar ? ("image://mxc/" + avatar) : ""
|
||||
name: name
|
||||
}
|
||||
label: name
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: name
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
|
||||
Label {
|
||||
visible: perm != UserType.Member
|
||||
|
||||
text: {
|
||||
if (perm == UserType.Owner) {
|
||||
return i18n("Owner")
|
||||
}
|
||||
if (perm == UserType.Admin) {
|
||||
return i18n("Admin")
|
||||
}
|
||||
if (perm == UserType.Moderator) {
|
||||
return i18n("Mod")
|
||||
}
|
||||
if (perm == UserType.Muted) {
|
||||
return i18n("Muted")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
font.pixelSize: 12
|
||||
textFormat: Text.PlainText
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
onClicked: {
|
||||
const popup = userDetailDialog.createObject(ApplicationWindow.overlay, {room: room, user: user, displayName: name, avatarMediaId: avatar})
|
||||
popup.closed.connect(function() {
|
||||
userListItem.highlighted = false
|
||||
})
|
||||
popup.open()
|
||||
}
|
||||
|
||||
action: Kirigami.Action {
|
||||
onTriggered: userDetailDialog.createObject(ApplicationWindow.overlay, {"room": room, "user": user, "displayName": name, "avatarMediaId": avatar}).open()
|
||||
leading: Kirigami.Avatar {
|
||||
implicitWidth: height
|
||||
sourceSize.height: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
||||
sourceSize.width: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
||||
source: avatar ? ("image://mxc/" + avatar) : ""
|
||||
name: name
|
||||
}
|
||||
|
||||
trailing: Label {
|
||||
visible: perm != UserType.Member
|
||||
|
||||
text: {
|
||||
switch (perm) {
|
||||
case UserType.Owner:
|
||||
return i18n("Owner");
|
||||
case UserType.Admin:
|
||||
return i18n("Admin");
|
||||
case UserType.Moderator:
|
||||
return i18n("Mod");
|
||||
case UserType.Muted:
|
||||
return i18n("Muted");
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
textFormat: Text.PlainText
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,6 +321,7 @@ Kirigami.OverlayDrawer {
|
||||
onRoomChanged: {
|
||||
if (loader.active) {
|
||||
loader.item.userSearchText = ""
|
||||
loader.item.highlightedUser = -1
|
||||
}
|
||||
if (room == null) {
|
||||
close()
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
|
||||
import QtQuick 2.15
|
||||
import org.kde.kirigami 2.18 as Kirigami
|
||||
import QtQuick.Controls 2.15 as Controls
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
Kirigami.CategorizedSettings {
|
||||
id: root
|
||||
property var room
|
||||
|
||||
objectName: "settingsPage"
|
||||
actions: [
|
||||
Kirigami.SettingAction {
|
||||
@@ -30,6 +30,16 @@ Kirigami.CategorizedSettings {
|
||||
room: root.room
|
||||
}
|
||||
}
|
||||
},
|
||||
Kirigami.SettingAction {
|
||||
text: i18n("Notifications")
|
||||
icon.name: "notifications"
|
||||
page: Qt.resolvedUrl("PushNotification.qml")
|
||||
initialProperties: {
|
||||
return {
|
||||
room: root.room
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ Kirigami.ScrollablePage {
|
||||
readonly property bool canChangeTopic: room.canSendState("m.room.topic")
|
||||
readonly property bool canChangeCanonicalAlias: room.canSendState("m.room.canonical_alias")
|
||||
|
||||
title: i18n('General')
|
||||
title: i18n("General")
|
||||
|
||||
ColumnLayout {
|
||||
Kirigami.FormLayout {
|
||||
|
||||
57
imports/NeoChat/RoomSettings/PushNotification.qml
Normal file
57
imports/NeoChat/RoomSettings/PushNotification.qml
Normal file
@@ -0,0 +1,57 @@
|
||||
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15 as QQC2
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import org.kde.kirigami 2.15 as Kirigami
|
||||
|
||||
import org.kde.neochat 1.0
|
||||
|
||||
Kirigami.ScrollablePage {
|
||||
|
||||
property var room
|
||||
|
||||
title: i18nc('@title:window', 'Notifications')
|
||||
|
||||
ColumnLayout {
|
||||
Kirigami.FormLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
QQC2.RadioButton {
|
||||
text: i18n("Follow global setting")
|
||||
Kirigami.FormData.label: i18n("Room notifications setting:")
|
||||
checked: room.pushNotificationState === PushNotificationState.Default
|
||||
enabled: room.pushNotificationState != PushNotificationState.Unknown
|
||||
onToggled: {
|
||||
room.pushNotificationState = PushNotificationState.Default
|
||||
}
|
||||
}
|
||||
QQC2.RadioButton {
|
||||
text: i18nc("As in 'notify for all messages'","All")
|
||||
checked: room.pushNotificationState === PushNotificationState.All
|
||||
enabled: room.pushNotificationState != PushNotificationState.Unknown
|
||||
onToggled: {
|
||||
room.pushNotificationState = PushNotificationState.All
|
||||
}
|
||||
}
|
||||
QQC2.RadioButton {
|
||||
text: i18nc("As in 'notify when the user is mentioned or the message contains a set keyword'","@Mentions and Keywords")
|
||||
checked: room.pushNotificationState === PushNotificationState.MentionKeyword
|
||||
enabled: room.pushNotificationState != PushNotificationState.Unknown
|
||||
onToggled: {
|
||||
room.pushNotificationState = PushNotificationState.MentionKeyword
|
||||
}
|
||||
}
|
||||
QQC2.RadioButton {
|
||||
text: i18nc("As in 'do not notify for any messages'","Off")
|
||||
checked: room.pushNotificationState === PushNotificationState.Mute
|
||||
enabled: room.pushNotificationState != PushNotificationState.Unknown
|
||||
onToggled: {
|
||||
room.pushNotificationState = PushNotificationState.Mute
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,13 +16,13 @@ Kirigami.ScrollablePage {
|
||||
|
||||
property var room
|
||||
|
||||
title: i18n('Security')
|
||||
title: i18n("Security")
|
||||
|
||||
ColumnLayout {
|
||||
Kirigami.FormLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
CheckBox {
|
||||
RadioButton {
|
||||
text: i18nc("@option:check", "Private (invite only)")
|
||||
Kirigami.FormData.label: i18nc("@option:check", "Access:")
|
||||
checked: room.joinRule === "invite"
|
||||
@@ -32,7 +32,7 @@ Kirigami.ScrollablePage {
|
||||
text: i18n("Only invited people can join.")
|
||||
font: Kirigami.Theme.smallFont
|
||||
}
|
||||
CheckBox {
|
||||
RadioButton {
|
||||
text: i18nc("@option:check", "Space members")
|
||||
checked: room.joinRule === "restricted"
|
||||
enabled: false
|
||||
@@ -41,7 +41,7 @@ Kirigami.ScrollablePage {
|
||||
text: i18n("Anyone in a space can find and join.")
|
||||
font: Kirigami.Theme.smallFont
|
||||
}
|
||||
CheckBox {
|
||||
RadioButton {
|
||||
text: i18nc("@option:check", "Public")
|
||||
checked: room.joinRule === "public"
|
||||
enabled: false
|
||||
|
||||
@@ -7,6 +7,6 @@ import org.kde.kirigami 2.15 as Kirigami
|
||||
import org.kde.neochat 1.0
|
||||
|
||||
Kirigami.AboutPage {
|
||||
title: i18nc('@title:window', 'About NeoChat')
|
||||
title: i18nc("@title:window", "About NeoChat")
|
||||
aboutData: Controller.aboutData
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ Kirigami.ScrollablePage {
|
||||
onTriggered: pageSettingStack.pushDialogLayer(Qt.resolvedUrl('./AccountEditorPage.qml'), {
|
||||
connection: model.connection
|
||||
}, {
|
||||
title: i18n('Account editor')
|
||||
title: i18n("Account editor")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import org.kde.neochat 1.0
|
||||
import NeoChat.Settings 1.0
|
||||
|
||||
Kirigami.ScrollablePage {
|
||||
title: i18nc('@title:window', 'Appearance')
|
||||
title: i18nc("@title:window", "Appearance")
|
||||
ColumnLayout {
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
@@ -177,7 +177,7 @@ Kirigami.ScrollablePage {
|
||||
Kirigami.FormLayout {
|
||||
Layout.maximumWidth: parent.width
|
||||
QQC2.CheckBox {
|
||||
Kirigami.FormData.label: "Show Avatar:"
|
||||
Kirigami.FormData.label: i18n("Show Avatar:")
|
||||
text: i18n("In Chat")
|
||||
checked: Config.showAvatarInTimeline
|
||||
onToggled: {
|
||||
@@ -237,6 +237,7 @@ Kirigami.ScrollablePage {
|
||||
HoverHandler { id: sliderHover }
|
||||
QQC2.ToolTip.visible: sliderHover.hovered && !enabled
|
||||
QQC2.ToolTip.text: i18n("Only enabled if the transparent chat page is enabled.")
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
QQC2.Label {
|
||||
text: Math.round(Config.transparency * 100) + "%"
|
||||
|
||||
@@ -5,7 +5,7 @@ import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15 as Controls
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import org.kde.kirigami 2.15 as Kirigami
|
||||
import org.kde.kirigami 2.19 as Kirigami
|
||||
|
||||
import org.kde.neochat 1.0
|
||||
|
||||
@@ -19,13 +19,9 @@ Kirigami.ScrollablePage {
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
Kirigami.PlaceholderMessage {
|
||||
Kirigami.LoadingPlaceholder {
|
||||
visible: parent.count === 0 // We can assume 0 means loading since there is at least one device
|
||||
anchors.centerIn: parent
|
||||
text: i18n("Loading…")
|
||||
Controls.BusyIndicator {
|
||||
running: parent.visible
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Kirigami.BasicListItem {
|
||||
@@ -45,6 +41,17 @@ Kirigami.ScrollablePage {
|
||||
}
|
||||
}
|
||||
}
|
||||
Controls.ToolButton {
|
||||
display: Controls.AbstractButton.IconOnly
|
||||
visible: Controller.encryptionSupported
|
||||
action: Kirigami.Action {
|
||||
text: i18n("Verify device")
|
||||
iconName: "security-low-symbolic"
|
||||
onTriggered: {
|
||||
devices.connection.startKeyVerificationSession(model.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
Controls.ToolButton {
|
||||
display: Controls.AbstractButton.IconOnly
|
||||
action: Kirigami.Action {
|
||||
|
||||
@@ -16,7 +16,7 @@ import NeoChat.Component 1.0 as Components
|
||||
import NeoChat.Dialog 1.0
|
||||
|
||||
Kirigami.ScrollablePage {
|
||||
title: i18nc('@title:window', 'Custom Emojis')
|
||||
title: i18nc("@title:window", "Custom Emojis")
|
||||
|
||||
ListView {
|
||||
anchors.fill: parent
|
||||
|
||||
@@ -11,7 +11,7 @@ import org.kde.kirigami 2.15 as Kirigami
|
||||
import org.kde.neochat 1.0
|
||||
|
||||
Kirigami.ScrollablePage {
|
||||
title: i18nc('@title:window', 'General')
|
||||
title: i18nc("@title:window", "General")
|
||||
ColumnLayout {
|
||||
Kirigami.FormLayout {
|
||||
Layout.fillWidth: true
|
||||
@@ -46,6 +46,7 @@ Kirigami.ScrollablePage {
|
||||
onToggled: {
|
||||
Config.showNotifications = checked
|
||||
Config.save()
|
||||
NotificationsManager.globalNotificationsEnabled = checked
|
||||
}
|
||||
}
|
||||
QQC2.CheckBox {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
import QtQuick 2.15
|
||||
import org.kde.kirigami 2.18 as Kirigami
|
||||
import QtQuick.Controls 2.15 as Controls
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
Kirigami.CategorizedSettings {
|
||||
@@ -25,7 +24,7 @@ Kirigami.CategorizedSettings {
|
||||
page: Qt.resolvedUrl("AccountsPage.qml")
|
||||
},
|
||||
Kirigami.SettingAction {
|
||||
text: i18n("Custom Emoji")
|
||||
text: i18n("Custom Emojis")
|
||||
icon.name: "preferences-desktop-emoticons"
|
||||
page: Qt.resolvedUrl("Emoticons.qml")
|
||||
},
|
||||
|
||||
@@ -47,7 +47,7 @@ Kirigami.Page {
|
||||
dialog.close();
|
||||
}
|
||||
}
|
||||
title: i18nc('@window:title', 'Spellchecking')
|
||||
title: i18nc("@window:title", "Spellchecking")
|
||||
|
||||
QQC2.Dialog {
|
||||
id: applyDialog
|
||||
|
||||
@@ -206,6 +206,9 @@
|
||||
<content_attribute id="social-chat">intense</content_attribute>
|
||||
</content_rating>
|
||||
<releases>
|
||||
<release version="22.09" date="2022-09-27">
|
||||
<url>https://www.plasma-mobile.org/2022/09/27/plasma-mobile-gear-22-09/</url>
|
||||
</release>
|
||||
<release version="22.06" date="2022-06-24">
|
||||
<url>https://www.plasma-mobile.org/2022/06/28/plasma-mobile-gear-22-06/</url>
|
||||
<description>
|
||||
|
||||
762
po/ar/neochat.po
762
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
784
po/az/neochat.po
784
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
784
po/ca/neochat.po
784
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
756
po/cs/neochat.po
756
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
759
po/da/neochat.po
759
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
784
po/de/neochat.po
784
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
799
po/es/neochat.po
799
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
1031
po/eu/neochat.po
1031
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
784
po/fi/neochat.po
784
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
797
po/fr/neochat.po
797
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
783
po/hu/neochat.po
783
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
784
po/ia/neochat.po
784
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
778
po/id/neochat.po
778
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
767
po/it/neochat.po
767
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
740
po/ja/neochat.po
740
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
784
po/ko/neochat.po
784
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
799
po/nl/neochat.po
799
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
804
po/nn/neochat.po
804
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
785
po/pa/neochat.po
785
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
784
po/pl/neochat.po
784
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
785
po/pt/neochat.po
785
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
761
po/ru/neochat.po
761
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
782
po/sk/neochat.po
782
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
783
po/sl/neochat.po
783
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
784
po/sv/neochat.po
784
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
767
po/ta/neochat.po
767
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
781
po/tr/neochat.po
781
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
787
po/uk/neochat.po
787
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
40
qml/main.qml
40
qml/main.qml
@@ -13,6 +13,7 @@ import NeoChat.Component 1.0
|
||||
import NeoChat.Dialog 1.0
|
||||
import NeoChat.Page 1.0
|
||||
import NeoChat.Panel 1.0
|
||||
import NeoChat.Dialog.KeyVerification 1.0
|
||||
|
||||
Kirigami.ApplicationWindow {
|
||||
id: root
|
||||
@@ -51,7 +52,7 @@ Kirigami.ApplicationWindow {
|
||||
Timer {
|
||||
id: saveWindowGeometryTimer
|
||||
interval: 1000
|
||||
onTriggered: Controller.saveWindowGeometry(root)
|
||||
onTriggered: Controller.saveWindowGeometry()
|
||||
}
|
||||
|
||||
Connections {
|
||||
@@ -59,7 +60,7 @@ Kirigami.ApplicationWindow {
|
||||
enabled: false // Disable on startup to avoid writing wrong values if the window is hidden
|
||||
target: root
|
||||
|
||||
function onClosing() { Controller.saveWindowGeometry(root); }
|
||||
function onClosing() { Controller.saveWindowGeometry(); }
|
||||
function onWidthChanged() { saveWindowGeometryTimer.restart(); }
|
||||
function onHeightChanged() { saveWindowGeometryTimer.restart(); }
|
||||
function onXChanged() { saveWindowGeometryTimer.restart(); }
|
||||
@@ -106,7 +107,7 @@ Kirigami.ApplicationWindow {
|
||||
|
||||
function onOpenRoomInNewWindow(room) {
|
||||
const secondayWindow = roomWindow.createObject(applicationWindow(), {currentRoom: room});
|
||||
secondayWindow.width = root.width - pageStack.get(0).width;
|
||||
secondayWiroomWindowndow.width = root.width - pageStack.get(0).width;
|
||||
secondayWindow.show();
|
||||
}
|
||||
|
||||
@@ -139,13 +140,6 @@ Kirigami.ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
function showWindow() {
|
||||
root.show()
|
||||
root.raise()
|
||||
root.requestActivate()
|
||||
Controller.raiseWindow(root)
|
||||
}
|
||||
|
||||
contextDrawer: RoomDrawer {
|
||||
id: contextDrawer
|
||||
modal: !root.wideScreen || !enabled
|
||||
@@ -235,13 +229,13 @@ Kirigami.ApplicationWindow {
|
||||
Kirigami.Action {
|
||||
text: i18n("Explore rooms")
|
||||
icon.name: "compass"
|
||||
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/JoinRoomPage.qml", {"connection": Controller.activeConnection})
|
||||
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/JoinRoomPage.qml", {connection: Controller.activeConnection})
|
||||
enabled: pageStack.layers.currentItem.title !== i18n("Explore Rooms") && Controller.accountCount > 0
|
||||
},
|
||||
Kirigami.Action {
|
||||
text: i18n("Start a Chat")
|
||||
icon.name: "irc-join-channel"
|
||||
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/StartChatPage.qml", {"connection": Controller.activeConnection})
|
||||
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/StartChatPage.qml", {connection: Controller.activeConnection})
|
||||
enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat") && Controller.accountCount > 0
|
||||
},
|
||||
Kirigami.Action {
|
||||
@@ -353,16 +347,6 @@ Kirigami.ApplicationWindow {
|
||||
showPassiveNotification(i18n("%1: %2", error, detail));
|
||||
}
|
||||
|
||||
function onShowWindow(token = null) {
|
||||
root.showWindow()
|
||||
if (token && KWindowSystem) {
|
||||
KWindowSystem.setCurrentXdgActivationToken(basicNotification.xdgActivationToken)
|
||||
KWindowSystem.activateWindow(root)
|
||||
} else {
|
||||
root.raise()
|
||||
}
|
||||
}
|
||||
|
||||
function onUserConsentRequired(url) {
|
||||
consentSheet.url = url
|
||||
consentSheet.open()
|
||||
@@ -384,11 +368,23 @@ Kirigami.ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: keyVerificationDialogComponent
|
||||
KeyVerificationDialog { }
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Controller.activeConnection
|
||||
function onDirectChatAvailable(directChat) {
|
||||
RoomManager.enterRoom(Controller.activeConnection.room(directChat.id));
|
||||
}
|
||||
function onNewKeyVerificationSession(session) {
|
||||
applicationWindow().pageStack.pushDialogLayer(keyVerificationDialogComponent, {
|
||||
session: session,
|
||||
}, {
|
||||
title: i18nc("@title:window", "Session Verification")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.OverlaySheet {
|
||||
|
||||
10
res.qrc
10
res.qrc
@@ -15,6 +15,7 @@
|
||||
<file>imports/NeoChat/Page/WelcomePage.qml</file>
|
||||
<file>imports/NeoChat/RoomSettings/General.qml</file>
|
||||
<file>imports/NeoChat/RoomSettings/Security.qml</file>
|
||||
<file>imports/NeoChat/RoomSettings/PushNotification.qml</file>
|
||||
<file>imports/NeoChat/RoomSettings/Categories.qml</file>
|
||||
<file>imports/NeoChat/Component/qmldir</file>
|
||||
<file>imports/NeoChat/Component/FullScreenImage.qml</file>
|
||||
@@ -39,6 +40,7 @@
|
||||
<file>imports/NeoChat/Component/Timeline/SectionDelegate.qml</file>
|
||||
<file>imports/NeoChat/Component/Timeline/VideoDelegate.qml</file>
|
||||
<file>imports/NeoChat/Component/Timeline/ReactionDelegate.qml</file>
|
||||
<file>imports/NeoChat/Component/Timeline/LinkPreviewDelegate.qml</file>
|
||||
<file>imports/NeoChat/Component/Timeline/AudioDelegate.qml</file>
|
||||
<file>imports/NeoChat/Component/Timeline/FileDelegate.qml</file>
|
||||
<file>imports/NeoChat/Component/Timeline/ImageDelegate.qml</file>
|
||||
@@ -62,6 +64,13 @@
|
||||
<file>imports/NeoChat/Dialog/CreateRoomDialog.qml</file>
|
||||
<file>imports/NeoChat/Dialog/EmojiDialog.qml</file>
|
||||
<file>imports/NeoChat/Dialog/OpenFileDialog.qml</file>
|
||||
<file>imports/NeoChat/Dialog/KeyVerification/KeyVerificationDialog.qml</file>
|
||||
<file>imports/NeoChat/Dialog/KeyVerification/Message.qml</file>
|
||||
<file>imports/NeoChat/Dialog/KeyVerification/EmojiItem.qml</file>
|
||||
<file>imports/NeoChat/Dialog/KeyVerification/EmojiRow.qml</file>
|
||||
<file>imports/NeoChat/Dialog/KeyVerification/EmojiSas.qml</file>
|
||||
<file>imports/NeoChat/Dialog/KeyVerification/VerificationCanceled.qml</file>
|
||||
<file>imports/NeoChat/Dialog/KeyVerification/qmldir</file>
|
||||
<file>imports/NeoChat/Menu/qmldir</file>
|
||||
<file>imports/NeoChat/Menu/GlobalMenu.qml</file>
|
||||
<file>imports/NeoChat/Menu/EditMenu.qml</file>
|
||||
@@ -69,6 +78,7 @@
|
||||
<file>imports/NeoChat/Menu/Timeline/MessageDelegateContextMenu.qml</file>
|
||||
<file>imports/NeoChat/Menu/Timeline/FileDelegateContextMenu.qml</file>
|
||||
<file>imports/NeoChat/Menu/Timeline/MessageSourceSheet.qml</file>
|
||||
<file>imports/NeoChat/Menu/Timeline/ReportSheet.qml</file>
|
||||
<file>imports/NeoChat/Menu/RoomListContextMenu.qml</file>
|
||||
<file>qtquickcontrols2.conf</file>
|
||||
<file>imports/NeoChat/Component/glowdot.png</file>
|
||||
|
||||
@@ -38,6 +38,8 @@ add_library(neochat STATIC
|
||||
joinrulesevent.cpp
|
||||
collapsestateproxymodel.cpp
|
||||
urlhelper.cpp
|
||||
windowcontroller.cpp
|
||||
linkpreviewer.cpp
|
||||
)
|
||||
|
||||
add_executable(neochat-app
|
||||
@@ -138,13 +140,16 @@ if(ANDROID)
|
||||
"org.kde.neochat"
|
||||
"preferences-system-users"
|
||||
"preferences-desktop-theme-global"
|
||||
"notifications"
|
||||
)
|
||||
else()
|
||||
target_link_libraries(neochat PUBLIC Qt::Widgets KF5::KIOWidgets)
|
||||
install(FILES neochat.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFYRCDIR})
|
||||
endif()
|
||||
|
||||
set_target_properties(neochat-app PROPERTIES OUTPUT_NAME "neochat")
|
||||
if(NOT ANDROID)
|
||||
set_target_properties(neochat-app PROPERTIES OUTPUT_NAME "neochat")
|
||||
endif()
|
||||
|
||||
if(TARGET KF5::DBusAddons)
|
||||
target_link_libraries(neochat PUBLIC KF5::DBusAddons)
|
||||
@@ -155,7 +160,7 @@ if (TARGET KF5::KIOWidgets)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_KIO)
|
||||
endif()
|
||||
|
||||
install(TARGETS neochat-app ${KF5_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
install(TARGETS neochat-app ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
||||
install(FILES plasma-runner-neochat.desktop DESTINATION ${KDE_INSTALL_DATAROOTDIR}/krunner/dbusplugins)
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "connection.h"
|
||||
#include "neochatroom.h"
|
||||
#include <connection.h>
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ QVariantMap ChatDocumentHandler::getAutocompletionInfo(bool isAutocompleting)
|
||||
};
|
||||
}
|
||||
|
||||
if (autoCompletePrefix.startsWith("/")) {
|
||||
if (autoCompletePrefix.startsWith("/") && text.trimmed().length() <= 1) {
|
||||
return QVariantMap{
|
||||
{"keyword", autoCompletePrefix},
|
||||
{"type", AutoCompletionType::Command},
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
bool CollapseStateProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
||||
{
|
||||
Q_UNUSED(source_parent);
|
||||
return sourceModel()->data(sourceModel()->index(source_row, 0), MessageEventModel::EventTypeRole)
|
||||
!= QLatin1String("state") // If this is not a state, show it
|
||||
|| sourceModel()->data(sourceModel()->index(source_row + 1, 0), MessageEventModel::EventTypeRole)
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <KWindowConfig>
|
||||
#ifdef HAVE_WINDOWSYSTEM
|
||||
#include <KWindowEffects>
|
||||
#include <KWindowSystem>
|
||||
#endif
|
||||
|
||||
#include <QAuthenticator>
|
||||
@@ -41,19 +40,21 @@
|
||||
#include "neochataccountregistry.h"
|
||||
#endif
|
||||
|
||||
#include "csapi/account-data.h"
|
||||
#include "csapi/content-repo.h"
|
||||
#include "csapi/joining.h"
|
||||
#include "csapi/logout.h"
|
||||
#include "csapi/profile.h"
|
||||
#include "events/eventcontent.h"
|
||||
#include <csapi/account-data.h>
|
||||
#include <csapi/content-repo.h>
|
||||
#include <csapi/joining.h>
|
||||
#include <csapi/logout.h>
|
||||
#include <csapi/profile.h>
|
||||
#include <events/eventcontent.h>
|
||||
#include <qt_connection_util.h>
|
||||
#include <settings.h>
|
||||
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatroom.h"
|
||||
#include "neochatuser.h"
|
||||
#include "roommanager.h"
|
||||
#include "settings.h"
|
||||
#include "utils.h"
|
||||
#include <qt_connection_util.h>
|
||||
#include "windowcontroller.h"
|
||||
|
||||
#include <KStandardShortcut>
|
||||
|
||||
@@ -131,6 +132,11 @@ Controller &Controller::instance()
|
||||
return _instance;
|
||||
}
|
||||
|
||||
void Controller::showWindow()
|
||||
{
|
||||
WindowController::instance().showAndRaiseWindow(QString());
|
||||
}
|
||||
|
||||
inline QString accessTokenFileName(const AccountSettings &account)
|
||||
{
|
||||
QString fileName = account.userId();
|
||||
@@ -568,7 +574,7 @@ void Controller::setActiveConnection(Connection *connection)
|
||||
m_connection = connection;
|
||||
if (connection != nullptr) {
|
||||
NeoChatConfig::self()->setActiveConnection(connection->userId());
|
||||
connect(connection, &Connection::networkError, this, [this](QString message, QString details, int retriesTaken, int nextRetryInMilliseconds) {
|
||||
connect(connection, &Connection::networkError, this, [this]() {
|
||||
if (!m_isOnline) {
|
||||
return;
|
||||
}
|
||||
@@ -589,21 +595,9 @@ void Controller::setActiveConnection(Connection *connection)
|
||||
Q_EMIT activeConnectionChanged();
|
||||
}
|
||||
|
||||
void Controller::restoreWindowGeometry(QQuickWindow *window)
|
||||
void Controller::saveWindowGeometry()
|
||||
{
|
||||
KConfig dataResource("data", KConfig::SimpleConfig, QStandardPaths::AppDataLocation);
|
||||
KConfigGroup windowGroup(&dataResource, "Window");
|
||||
KWindowConfig::restoreWindowSize(window, windowGroup);
|
||||
KWindowConfig::restoreWindowPosition(window, windowGroup);
|
||||
}
|
||||
|
||||
void Controller::saveWindowGeometry(QQuickWindow *window)
|
||||
{
|
||||
KConfig dataResource("data", KConfig::SimpleConfig, QStandardPaths::AppDataLocation);
|
||||
KConfigGroup windowGroup(&dataResource, "Window");
|
||||
KWindowConfig::saveWindowPosition(window, windowGroup);
|
||||
KWindowConfig::saveWindowSize(window, windowGroup);
|
||||
dataResource.sync();
|
||||
WindowController::instance().saveGeometry();
|
||||
}
|
||||
|
||||
NeochatDeleteDeviceJob::NeochatDeleteDeviceJob(const QString &deviceId, const Omittable<QJsonObject> &auth)
|
||||
@@ -681,13 +675,6 @@ void Controller::setBlur(QQuickItem *item, bool blur)
|
||||
#endif
|
||||
}
|
||||
|
||||
void Controller::raiseWindow(QWindow *window)
|
||||
{
|
||||
#ifdef HAVE_WINDOWSYSTEM
|
||||
KWindowSystem::activateWindow(window->winId());
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Controller::hasWindowSystem() const
|
||||
{
|
||||
#ifdef HAVE_WINDOWSYSTEM
|
||||
|
||||
@@ -11,16 +11,16 @@
|
||||
|
||||
class QKeySequences;
|
||||
|
||||
#include "connection.h"
|
||||
#include "csapi/list_public_rooms.h"
|
||||
#include "room.h"
|
||||
#include "settings.h"
|
||||
#include "user.h"
|
||||
#include <connection.h>
|
||||
#include <csapi/list_public_rooms.h>
|
||||
#include <room.h>
|
||||
#include <settings.h>
|
||||
#include <user.h>
|
||||
|
||||
class NeoChatRoom;
|
||||
class NeoChatUser;
|
||||
class TrayIcon;
|
||||
class QQuickWindow;
|
||||
class QWindow;
|
||||
class QQuickTextDocument;
|
||||
|
||||
namespace QKeychain
|
||||
@@ -95,7 +95,6 @@ public:
|
||||
Q_INVOKABLE void openOrCreateDirectChat(NeoChatUser *user);
|
||||
|
||||
Q_INVOKABLE void setBlur(QQuickItem *item, bool blur);
|
||||
Q_INVOKABLE void raiseWindow(QWindow *window);
|
||||
Q_INVOKABLE QString plainText(QQuickTextDocument *document) const;
|
||||
bool encryptionSupported() const;
|
||||
|
||||
@@ -118,6 +117,7 @@ private:
|
||||
|
||||
private Q_SLOTS:
|
||||
void invokeLogin();
|
||||
void showWindow();
|
||||
|
||||
Q_SIGNALS:
|
||||
void busyChanged();
|
||||
@@ -137,17 +137,19 @@ Q_SIGNALS:
|
||||
void activeConnectionChanged();
|
||||
void aboutDataChanged();
|
||||
void passwordStatus(Controller::PasswordStatus _t1);
|
||||
void showWindow();
|
||||
void userConsentRequired(QUrl url);
|
||||
void testConnectionResult(const QString &connection, bool usable);
|
||||
void isOnlineChanged(bool isOnline);
|
||||
void keyVerificationRequest(int timeLeft, Connection *connection, const QString &transactionId, const QString &deviceId);
|
||||
void keyVerificationStart();
|
||||
void keyVerificationAccept(const QString &commitment);
|
||||
void keyVerificationKey(const QString &sas);
|
||||
|
||||
public Q_SLOTS:
|
||||
void logout(Quotient::Connection *conn, bool serverSideLogout);
|
||||
void changeAvatar(Quotient::Connection *conn, const QUrl &localFile);
|
||||
static void markAllMessagesAsRead(Quotient::Connection *conn);
|
||||
void restoreWindowGeometry(QQuickWindow *);
|
||||
void saveWindowGeometry(QQuickWindow *);
|
||||
void saveWindowGeometry();
|
||||
};
|
||||
|
||||
// TODO libQuotient 0.7: Drop
|
||||
|
||||
@@ -6,11 +6,15 @@
|
||||
#include <csapi/device_management.h>
|
||||
|
||||
#include "controller.h"
|
||||
#include <connection.h>
|
||||
|
||||
DevicesModel::DevicesModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
connect(&Controller::instance(), &Controller::activeConnectionChanged, this, &DevicesModel::fetchDevices);
|
||||
connect(&Controller::instance(), &Controller::activeConnectionChanged, this, [this]() {
|
||||
DevicesModel::fetchDevices();
|
||||
Q_EMIT connectionChanged();
|
||||
});
|
||||
|
||||
fetchDevices();
|
||||
}
|
||||
@@ -94,3 +98,8 @@ void DevicesModel::setName(int index, const QString &name)
|
||||
endResetModel();
|
||||
});
|
||||
}
|
||||
|
||||
Connection *DevicesModel::connection() const
|
||||
{
|
||||
return Controller::instance().activeConnection();
|
||||
}
|
||||
|
||||
@@ -9,10 +9,17 @@
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
namespace Quotient
|
||||
{
|
||||
class Connection;
|
||||
}
|
||||
|
||||
class DevicesModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(Connection *connection READ connection NOTIFY connectionChanged)
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
Id,
|
||||
@@ -31,6 +38,11 @@ public:
|
||||
Q_INVOKABLE void logout(int index, const QString &password);
|
||||
Q_INVOKABLE void setName(int index, const QString &name);
|
||||
|
||||
Connection *connection() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void connectionChanged();
|
||||
|
||||
private:
|
||||
void fetchDevices();
|
||||
QVector<Quotient::Device> m_devices;
|
||||
|
||||
@@ -8,17 +8,25 @@
|
||||
|
||||
namespace Quotient
|
||||
{
|
||||
#ifdef QUOTIENT_07
|
||||
class JoinRulesEvent : public StateEvent
|
||||
#else
|
||||
class JoinRulesEvent : public StateEventBase
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
#ifdef QUOTIENT_07
|
||||
QUO_EVENT(JoinRulesEvent, "m.room.join_rules")
|
||||
#else
|
||||
DEFINE_EVENT_TYPEID("m.room.join_rules", JoinRulesEvent)
|
||||
#endif
|
||||
|
||||
explicit JoinRulesEvent()
|
||||
: StateEventBase(typeId(), matrixTypeId())
|
||||
{
|
||||
}
|
||||
explicit JoinRulesEvent(const QJsonObject &obj)
|
||||
#ifdef QUOTIENT_07
|
||||
: StateEvent(obj)
|
||||
#else
|
||||
: StateEventBase(typeId(), obj)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
64
src/linkpreviewer.cpp
Normal file
64
src/linkpreviewer.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
// SPDX-FileCopyrightText: 2022 Bharadwaj Raju <bharadwaj.raju777@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-or-later OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include "linkpreviewer.h"
|
||||
|
||||
#include "controller.h"
|
||||
|
||||
LinkPreviewer::LinkPreviewer(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_loaded(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool LinkPreviewer::loaded() const
|
||||
{
|
||||
return m_loaded;
|
||||
}
|
||||
|
||||
QString LinkPreviewer::title() const
|
||||
{
|
||||
return m_title;
|
||||
}
|
||||
|
||||
QString LinkPreviewer::description() const
|
||||
{
|
||||
return m_description;
|
||||
}
|
||||
|
||||
QString LinkPreviewer::imageSource() const
|
||||
{
|
||||
return m_imageSource;
|
||||
}
|
||||
|
||||
QUrl LinkPreviewer::url() const
|
||||
{
|
||||
return m_url;
|
||||
}
|
||||
|
||||
void LinkPreviewer::setUrl(QUrl url)
|
||||
{
|
||||
if (url.scheme() == QStringLiteral("https")) {
|
||||
m_loaded = false;
|
||||
Q_EMIT loadedChanged();
|
||||
|
||||
m_url = url;
|
||||
Q_EMIT urlChanged();
|
||||
|
||||
auto conn = Controller::instance().activeConnection();
|
||||
|
||||
GetUrlPreviewJob *job = conn->callApi<GetUrlPreviewJob>(m_url.toString());
|
||||
|
||||
connect(job, &BaseJob::success, this, [this, job]() {
|
||||
const auto json = job->jsonData();
|
||||
m_title = json["og:title"].toString();
|
||||
m_description = json["og:description"].toString();
|
||||
m_imageSource = json["og:image"].toString();
|
||||
m_loaded = true;
|
||||
Q_EMIT titleChanged();
|
||||
Q_EMIT descriptionChanged();
|
||||
Q_EMIT imageSourceChanged();
|
||||
Q_EMIT loadedChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
46
src/linkpreviewer.h
Normal file
46
src/linkpreviewer.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// SPDX-FileCopyrightText: 2022 Bharadwaj Raju <bharadwaj.raju777@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-or-later OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <connection.h>
|
||||
#include <csapi/content-repo.h>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
class LinkPreviewer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool loaded READ loaded NOTIFY loadedChanged)
|
||||
Q_PROPERTY(QString title READ title NOTIFY titleChanged)
|
||||
Q_PROPERTY(QString description READ description NOTIFY descriptionChanged)
|
||||
Q_PROPERTY(QString imageSource READ imageSource NOTIFY imageSourceChanged)
|
||||
Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
|
||||
|
||||
public:
|
||||
explicit LinkPreviewer(QObject *parent = nullptr);
|
||||
|
||||
[[nodiscard]] bool loaded() const;
|
||||
[[nodiscard]] QString title() const;
|
||||
[[nodiscard]] QString description() const;
|
||||
[[nodiscard]] QString imageSource() const;
|
||||
[[nodiscard]] QUrl url() const;
|
||||
|
||||
void setUrl(QUrl);
|
||||
|
||||
private:
|
||||
bool m_loaded;
|
||||
QString m_title;
|
||||
QString m_description;
|
||||
QString m_imageSource;
|
||||
QUrl m_url;
|
||||
|
||||
Q_SIGNALS:
|
||||
void loadedChanged();
|
||||
void titleChanged();
|
||||
void descriptionChanged();
|
||||
void imageSourceChanged();
|
||||
void urlChanged();
|
||||
};
|
||||
@@ -2,11 +2,13 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "login.h"
|
||||
#include "connection.h"
|
||||
|
||||
#include <connection.h>
|
||||
#include <qt_connection_util.h>
|
||||
|
||||
#include "controller.h"
|
||||
|
||||
#include <QUrl>
|
||||
#include <qt_connection_util.h>
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "connection.h"
|
||||
#include "csapi/wellknown.h"
|
||||
#include <connection.h>
|
||||
#include <csapi/wellknown.h>
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
|
||||
82
src/main.cpp
82
src/main.cpp
@@ -26,8 +26,8 @@
|
||||
#include <KDBusService>
|
||||
#endif
|
||||
#ifdef HAVE_WINDOWSYSTEM
|
||||
#include <kwindowsystem_version.h>
|
||||
#include <KWindowSystem>
|
||||
#include <kwindowsystem_version.h>
|
||||
#endif
|
||||
#include <KLocalizedContext>
|
||||
#include <KLocalizedString>
|
||||
@@ -36,11 +36,16 @@
|
||||
#include "neochat-version.h"
|
||||
|
||||
#ifdef QUOTIENT_07
|
||||
#include "accountregistry.h"
|
||||
#include <accountregistry.h>
|
||||
#else
|
||||
#include "neochataccountregistry.h"
|
||||
#endif
|
||||
|
||||
#include <csapi/joining.h>
|
||||
#include <csapi/leaving.h>
|
||||
#include <networkaccessmanager.h>
|
||||
#include <room.h>
|
||||
|
||||
#include "actionshandler.h"
|
||||
#include "blurhashimageprovider.h"
|
||||
#include "chatboxhelper.h"
|
||||
@@ -49,13 +54,12 @@
|
||||
#include "collapsestateproxymodel.h"
|
||||
#include "commandmodel.h"
|
||||
#include "controller.h"
|
||||
#include "csapi/joining.h"
|
||||
#include "csapi/leaving.h"
|
||||
#include "customemojimodel.h"
|
||||
#include "devicesmodel.h"
|
||||
#include "emojimodel.h"
|
||||
#include "filetypesingleton.h"
|
||||
#include "joinrulesevent.h"
|
||||
#include "linkpreviewer.h"
|
||||
#include "login.h"
|
||||
#include "matriximageprovider.h"
|
||||
#include "messageeventmodel.h"
|
||||
@@ -63,7 +67,6 @@
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatroom.h"
|
||||
#include "neochatuser.h"
|
||||
#include "networkaccessmanager.h"
|
||||
#include "notificationsmanager.h"
|
||||
#include "publicroomlistmodel.h"
|
||||
#include "roomlistmodel.h"
|
||||
@@ -75,7 +78,10 @@
|
||||
#include "userdirectorylistmodel.h"
|
||||
#include "userlistmodel.h"
|
||||
#include "webshortcutmodel.h"
|
||||
#include <room.h>
|
||||
#include "windowcontroller.h"
|
||||
#ifdef QUOTIENT_07
|
||||
#include <keyverificationsession.h>
|
||||
#endif
|
||||
#ifdef HAVE_COLORSCHEME
|
||||
#include "colorschemer.h"
|
||||
#endif
|
||||
@@ -95,20 +101,13 @@ class NetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef HAVE_WINDOWSYSTEM
|
||||
static void raiseWindow(QWindow *window)
|
||||
static QWindow *windowFromEngine(QQmlApplicationEngine *engine)
|
||||
{
|
||||
#if KWINDOWSYSTEM_VERSION >= QT_VERSION_CHECK(5, 91, 0)
|
||||
KWindowSystem::updateStartupId(window);
|
||||
#else
|
||||
if (KWindowSystem::isPlatformWayland()) {
|
||||
KWindowSystem::setCurrentXdgActivationToken(qEnvironmentVariable("XDG_ACTIVATION_TOKEN"));
|
||||
}
|
||||
#endif
|
||||
KWindowSystem::activateWindow(window->winId());
|
||||
window->raise();
|
||||
const auto rootObjects = engine->rootObjects();
|
||||
auto *window = qobject_cast<QQuickWindow *>(rootObjects.first());
|
||||
Q_ASSERT(window);
|
||||
return window;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
Q_DECL_EXPORT
|
||||
@@ -142,6 +141,7 @@ int main(int argc, char *argv[])
|
||||
font.setPointSize(10);
|
||||
app.setFont(font);
|
||||
#endif
|
||||
KLocalizedString::setApplicationDomain("neochat");
|
||||
|
||||
QGuiApplication::setOrganizationName("KDE");
|
||||
|
||||
@@ -165,7 +165,6 @@ int main(int argc, char *argv[])
|
||||
KAboutData::setApplicationData(about);
|
||||
QGuiApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("org.kde.neochat")));
|
||||
|
||||
|
||||
#ifdef NEOCHAT_FLATPAK
|
||||
// Copy over the included FontConfig configuration to the
|
||||
// app's config dir:
|
||||
@@ -190,6 +189,7 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
|
||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Controller", &Controller::instance());
|
||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "NotificationsManager", &NotificationsManager::instance());
|
||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Clipboard", &clipboard);
|
||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Config", config);
|
||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "RoomManager", &RoomManager::instance());
|
||||
@@ -215,7 +215,9 @@ int main(int argc, char *argv[])
|
||||
qmlRegisterType<SortFilterRoomListModel>("org.kde.neochat", 1, 0, "SortFilterRoomListModel");
|
||||
qmlRegisterType<SortFilterSpaceListModel>("org.kde.neochat", 1, 0, "SortFilterSpaceListModel");
|
||||
qmlRegisterType<DevicesModel>("org.kde.neochat", 1, 0, "DevicesModel");
|
||||
qmlRegisterType<LinkPreviewer>("org.kde.neochat", 1, 0, "LinkPreviewer");
|
||||
qmlRegisterUncreatableType<RoomMessageEvent>("org.kde.neochat", 1, 0, "RoomMessageEvent", "ENUM");
|
||||
qmlRegisterUncreatableType<PushNotificationState>("org.kde.neochat", 1, 0, "PushNotificationState", "ENUM");
|
||||
qmlRegisterUncreatableType<NeoChatRoomType>("org.kde.neochat", 1, 0, "NeoChatRoomType", "ENUM");
|
||||
qmlRegisterUncreatableType<UserType>("org.kde.neochat", 1, 0, "UserType", "ENUM");
|
||||
|
||||
@@ -229,11 +231,12 @@ int main(int argc, char *argv[])
|
||||
qRegisterMetaType<NeoChatUser *>("NeoChatUser*");
|
||||
qRegisterMetaType<GetRoomEventsJob *>("GetRoomEventsJob*");
|
||||
qRegisterMetaType<QMimeType>("QMimeType");
|
||||
|
||||
#ifdef HAVE_WINDOWSYSTEM
|
||||
qmlRegisterSingletonType<KWindowSystem>("org.kde.kwindowsystem.private", 1, 0, "KWindowSystem", [](QQmlEngine *, QJSEngine *) -> QObject * {
|
||||
return KWindowSystem::self();
|
||||
});
|
||||
#ifdef QUOTIENT_07
|
||||
#ifdef Quotient_E2EE_ENABLED
|
||||
qRegisterMetaType<KeyVerificationSession *>("KeyVerificationSession*");
|
||||
qmlRegisterUncreatableType<KeyVerificationSession>("org.kde.neochat", 1, 0, "KeyVerificationSession", {});
|
||||
qRegisterMetaType<QVector<EmojiEntry>>("QVector<EmojiEntry>");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
@@ -242,7 +245,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
|
||||
KLocalizedString::setApplicationDomain("neochat");
|
||||
QObject::connect(&engine, &QQmlApplicationEngine::quit, &app, &QCoreApplication::quit);
|
||||
engine.setNetworkAccessManagerFactory(new NetworkAccessManagerFactory());
|
||||
|
||||
@@ -282,16 +284,10 @@ int main(int argc, char *argv[])
|
||||
[&engine](const QStringList &arguments, const QString &workingDirectory) {
|
||||
Q_UNUSED(workingDirectory);
|
||||
|
||||
// Raise windows
|
||||
const auto rootObjects = engine.rootObjects();
|
||||
for (auto obj : rootObjects) {
|
||||
auto view = qobject_cast<QQuickWindow *>(obj);
|
||||
if (view) {
|
||||
view->show();
|
||||
raiseWindow(view);
|
||||
return;
|
||||
}
|
||||
}
|
||||
QWindow *window = windowFromEngine(&engine);
|
||||
KWindowSystem::updateStartupId(window);
|
||||
|
||||
WindowController::instance().showAndRaiseWindow(QString());
|
||||
|
||||
// Open matrix uri
|
||||
if (arguments.isEmpty()) {
|
||||
@@ -304,15 +300,11 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
});
|
||||
#endif
|
||||
const auto rootObjects = engine.rootObjects();
|
||||
for (auto obj : rootObjects) {
|
||||
auto view = qobject_cast<QQuickWindow *>(obj);
|
||||
if (view) {
|
||||
if (view->isVisible()) {
|
||||
Controller::instance().restoreWindowGeometry(view);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QWindow *window = windowFromEngine(&engine);
|
||||
|
||||
WindowController::instance().setWindow(window);
|
||||
WindowController::instance().restoreGeometry();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
|
||||
roles[FormattedBodyRole] = "formattedBody";
|
||||
roles[AuthorIdRole] = "authorId";
|
||||
roles[MediaUrlRole] = "mediaUrl";
|
||||
roles[VerifiedRole] = "verified";
|
||||
return roles;
|
||||
}
|
||||
|
||||
@@ -541,12 +542,17 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
}
|
||||
|
||||
if (role == MimeTypeRole) {
|
||||
auto e = eventCast<const RoomMessageEvent>(&evt);
|
||||
if (!e || !e->hasFileContent()) {
|
||||
return QVariant();
|
||||
if (auto e = eventCast<const RoomMessageEvent>(&evt)) {
|
||||
if (!e || !e->hasFileContent()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
return e->content()->fileInfo()->mimeType.name();
|
||||
}
|
||||
|
||||
return e->content()->fileInfo()->mimeType.name();
|
||||
if (auto e = eventCast<const StickerEvent>(&evt)) {
|
||||
return e->image().mimeType.name();
|
||||
}
|
||||
}
|
||||
|
||||
if (role == SpecialMarksRole) {
|
||||
@@ -715,7 +721,8 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
for (auto r = row + 1; r < rowCount(); ++r) {
|
||||
auto i = index(r);
|
||||
if (data(i, SpecialMarksRole) != EventStatus::Hidden) {
|
||||
return data(i, AuthorRole) != data(idx, AuthorRole) || data(i, TimeRole).toDateTime().msecsTo(data(idx, TimeRole).toDateTime()) > 600000
|
||||
return data(i, AuthorRole) != data(idx, AuthorRole) || data(i, EventTypeRole) == "state"
|
||||
|| data(i, TimeRole).toDateTime().msecsTo(data(idx, TimeRole).toDateTime()) > 600000
|
||||
|| data(i, TimeRole).toDateTime().toLocalTime().date().day() != data(idx, TimeRole).toDateTime().toLocalTime().date().day();
|
||||
}
|
||||
}
|
||||
@@ -786,10 +793,38 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
return m_currentRoom->makeMediaUrl(e->id(), e->content()->originalJson["url"].toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Requires https://github.com/quotient-im/libQuotient/pull/570
|
||||
// if (auto e = eventCast<const StickerEvent>(&evt)) {
|
||||
// return m_currentRoom->makeMediaUrl(e->id(), e->url());
|
||||
// }
|
||||
#endif
|
||||
|
||||
// Construct link in the same form as urlToDownload as that function doesn't work for stickers
|
||||
if (auto e = eventCast<const StickerEvent>(&evt)) {
|
||||
auto url = QUrl(m_currentRoom->connection()->homeserver().toString() + "/_matrix/media/r0/download/" + e->url().toString().remove("mxc://"));
|
||||
QUrlQuery q(url.query());
|
||||
q.addQueryItem("allow_remote", "true");
|
||||
url.setQuery(q);
|
||||
return url;
|
||||
}
|
||||
|
||||
return m_currentRoom->urlToDownload(evt.id());
|
||||
}
|
||||
|
||||
if (role == VerifiedRole) {
|
||||
#ifdef QUOTIENT_07
|
||||
#ifdef Quotient_E2EE_ENABLED
|
||||
if (evt.originalEvent()) {
|
||||
auto encrypted = dynamic_cast<const EncryptedEvent *>(evt.originalEvent());
|
||||
Q_ASSERT(encrypted);
|
||||
return m_currentRoom->connection()->isVerifiedSession(encrypted->sessionId());
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <QAbstractListModel>
|
||||
|
||||
#include "neochatroom.h"
|
||||
#include "room.h"
|
||||
#include <room.h>
|
||||
|
||||
class MessageEventModel : public QAbstractListModel
|
||||
{
|
||||
@@ -48,6 +48,7 @@ public:
|
||||
// For debugging
|
||||
EventResolvedTypeRole,
|
||||
AuthorIdRole,
|
||||
VerifiedRole,
|
||||
LastRole, // Keep this last
|
||||
};
|
||||
Q_ENUM(EventRoles)
|
||||
|
||||
@@ -146,6 +146,7 @@ Name[cs]=Nová pozvánka
|
||||
Name[de]=Neue Einladung
|
||||
Name[en_GB]=New Invitation
|
||||
Name[es]=Nueva invitación
|
||||
Name[eu]=Gonbidapen berria
|
||||
Name[fi]=Uusi kutsu
|
||||
Name[fr]=Nouvelle invitation
|
||||
Name[ia]=Nove invitation
|
||||
@@ -172,6 +173,7 @@ Comment[cs]=Máte novou pozvánku do místnosti
|
||||
Comment[de]=Es gibt eine neue Einladung zu einem Raum
|
||||
Comment[en_GB]=There is a new invitation to a room
|
||||
Comment[es]=Hay una nueva invitación a una sala
|
||||
Comment[eu]=Gela baterako gonbidapen berri bat dago
|
||||
Comment[fi]=Uusi kutsu huoneeseen
|
||||
Comment[fr]=Il y a une nouvelle invitation dans un salon.
|
||||
Comment[ia]=Il ha un nove invitation a un sala
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "neochataccountregistry.h"
|
||||
|
||||
#include "connection.h"
|
||||
#include <connection.h>
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user