Compare commits
94 Commits
work/nvrwh
...
work/featu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8f72a44fb | ||
|
|
6e0aa7f683 | ||
|
|
afa1ec6a4d | ||
|
|
6e20a46525 | ||
|
|
eba6c1faaa | ||
|
|
d00e122d88 | ||
|
|
a761d36abd | ||
|
|
22448ea9ae | ||
|
|
6756e1fd45 | ||
|
|
005580dcea | ||
|
|
7cfc0e24e2 | ||
|
|
a6a152acdc | ||
|
|
4ddf614108 | ||
|
|
3ae6a0266d | ||
|
|
c081b42fd4 | ||
|
|
0dcd71af53 | ||
|
|
eee96bc462 | ||
|
|
932c3e10fe | ||
|
|
81bbfce7cd | ||
|
|
1a3befef36 | ||
|
|
cd7232e7bf | ||
|
|
f6609f55f8 | ||
|
|
17a36f1959 | ||
|
|
a4e16ad3f1 | ||
|
|
0514a52ff1 | ||
|
|
8c3666ac74 | ||
|
|
761ec0f1cb | ||
|
|
b5d7173e88 | ||
|
|
7b81af43b1 | ||
|
|
6bf69ae77e | ||
|
|
055deb4115 | ||
|
|
058e8034d9 | ||
|
|
17240ea8a6 | ||
|
|
727842df4b | ||
|
|
04182f98f2 | ||
|
|
4aff25347f | ||
|
|
f8e57d60f5 | ||
|
|
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
|
||||
)
|
||||
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import Qt.labs.platform 1.1
|
||||
|
||||
import org.kde.kirigami 2.15 as Kirigami
|
||||
|
||||
ApplicationWindow {
|
||||
Popup {
|
||||
id: root
|
||||
|
||||
property alias source: image.source
|
||||
@@ -14,57 +16,293 @@ ApplicationWindow {
|
||||
property string blurhash: ""
|
||||
property int imageWidth: -1
|
||||
property int imageHeight: -1
|
||||
property var modelData
|
||||
|
||||
flags: Qt.FramelessWindowHint | Qt.WA_TranslucentBackground
|
||||
parent: Overlay.overlay
|
||||
closePolicy: Popup.CloseOnEscape
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
modal: true
|
||||
padding: 0
|
||||
background: null
|
||||
|
||||
title: i18n("Image View - %1", filename)
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
Shortcut {
|
||||
sequence: "Escape"
|
||||
onActivated: root.destroy()
|
||||
}
|
||||
Control {
|
||||
Layout.fillWidth: true
|
||||
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
contentItem: RowLayout {
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
background: AbstractButton {
|
||||
onClicked: root.destroy()
|
||||
}
|
||||
Kirigami.Avatar {
|
||||
id: avatar
|
||||
|
||||
BusyIndicator {
|
||||
visible: image.status !== Image.Ready && root.blurhash === ""
|
||||
anchors.centerIn: parent
|
||||
running: visible
|
||||
}
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.medium
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.medium
|
||||
|
||||
AnimatedImage {
|
||||
id: image
|
||||
anchors.centerIn: parent
|
||||
name: modelData.author.name ?? modelData.author.displayName
|
||||
source: modelData.author.avatarMediaId ? ("image://mxc/" + modelData.author.avatarMediaId) : ""
|
||||
color: modelData.author.color
|
||||
}
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
width: Math.min(root.imageWidth !== -1 ? root.imageWidth : sourceSize.width, root.width)
|
||||
height: Math.min(root.imageHeight !== -1 ? root.imageWidth : sourceSize.height, root.height)
|
||||
Label {
|
||||
id: nameLabel
|
||||
|
||||
fillMode: Image.PreserveAspectFit
|
||||
text: modelData.author.displayName
|
||||
textFormat: Text.PlainText
|
||||
font.weight: Font.Bold
|
||||
color: author.color
|
||||
}
|
||||
Label {
|
||||
id: timeLabel
|
||||
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
width: image.width
|
||||
height: image.height
|
||||
source: root.blurhash !== "" ? ("image://blurhash/" + root.blurhash) : ""
|
||||
visible: root.blurhash !== "" && parent.status !== Image.Ready
|
||||
text: time.toLocaleString(Qt.locale(), Locale.ShortFormat)
|
||||
}
|
||||
}
|
||||
Label {
|
||||
id: imageLabel
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
|
||||
text: modelData.display
|
||||
font.weight: Font.Bold
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
ToolButton {
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 2
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
||||
|
||||
text: i18n("Zoom in")
|
||||
Accessible.name: text
|
||||
icon.name: "zoom-in"
|
||||
display: AbstractButton.IconOnly
|
||||
onClicked: {
|
||||
image.scaleFactor = image.scaleFactor + 0.25
|
||||
if (image.scaleFactor > 3) {
|
||||
image.scaleFactor = 3
|
||||
}
|
||||
}
|
||||
|
||||
ToolTip.text: text
|
||||
ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
ToolTip.visible: hovered
|
||||
}
|
||||
ToolButton {
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 2
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
||||
|
||||
text: i18n("Zoom out")
|
||||
Accessible.name: text
|
||||
icon.name: "zoom-out"
|
||||
display: AbstractButton.IconOnly
|
||||
onClicked: {
|
||||
image.scaleFactor = image.scaleFactor - 0.25
|
||||
if (image.scaleFactor < 0.25) {
|
||||
image.scaleFactor = 0.25
|
||||
}
|
||||
}
|
||||
|
||||
ToolTip.text: text
|
||||
ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
ToolTip.visible: hovered
|
||||
}
|
||||
ToolButton {
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 2
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
||||
|
||||
text: i18n("Rotate left")
|
||||
Accessible.name: text
|
||||
icon.name: "image-rotate-left-symbolic"
|
||||
display: AbstractButton.IconOnly
|
||||
onClicked: image.rotationAngle = image.rotationAngle - 90
|
||||
|
||||
ToolTip.text: text
|
||||
ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
ToolTip.visible: hovered
|
||||
}
|
||||
ToolButton {
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 2
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
||||
|
||||
text: i18n("Rotate right")
|
||||
Accessible.name: text
|
||||
icon.name: "image-rotate-right-symbolic"
|
||||
display: AbstractButton.IconOnly
|
||||
onClicked: image.rotationAngle = image.rotationAngle + 90
|
||||
|
||||
ToolTip.text: text
|
||||
ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
ToolTip.visible: hovered
|
||||
}
|
||||
ToolButton {
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 2
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
||||
|
||||
text: i18n("Save as")
|
||||
Accessible.name: text
|
||||
icon.name: "document-save"
|
||||
display: AbstractButton.IconOnly
|
||||
onClicked: {
|
||||
var dialog = saveAsDialog.createObject(ApplicationWindow.overlay)
|
||||
dialog.open()
|
||||
dialog.currentFile = dialog.folder + "/" + currentRoom.fileNameToDownload(eventId)
|
||||
}
|
||||
|
||||
ToolTip.text: text
|
||||
ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
ToolTip.visible: hovered
|
||||
}
|
||||
ToolButton {
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 2
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
||||
|
||||
text: i18n("Close")
|
||||
Accessible.name: text
|
||||
icon.name: "dialog-close"
|
||||
display: AbstractButton.IconOnly
|
||||
onClicked: {
|
||||
root.close()
|
||||
}
|
||||
|
||||
ToolTip.text: text
|
||||
ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
ToolTip.visible: hovered
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: Kirigami.Theme.alternateBackgroundColor
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
height: 1
|
||||
}
|
||||
}
|
||||
|
||||
BusyIndicator {
|
||||
Layout.fillWidth: true
|
||||
visible: image.status !== Image.Ready && root.blurhash === ""
|
||||
running: visible
|
||||
}
|
||||
// Provides container to fill the space that isn't taken up by the top controls and clips the image when zooming makes it larger than the available area.
|
||||
Item {
|
||||
id: imageContainer
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.bottomMargin: Kirigami.Units.largeSpacing
|
||||
clip: true
|
||||
|
||||
Image {
|
||||
id: image
|
||||
|
||||
property var scaleFactor: 1
|
||||
property int rotationAngle: 0
|
||||
property var rotationInsensitiveWidth: Math.min(root.imageWidth !== -1 ? root.imageWidth : sourceSize.width, imageContainer.width - Kirigami.Units.largeSpacing * 2)
|
||||
property var rotationInsensitiveHeight: Math.min(root.imageHeight !== -1 ? root.imageHeight : sourceSize.height, imageContainer.height - Kirigami.Units.largeSpacing * 2)
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: rotationAngle % 180 === 0 ? rotationInsensitiveWidth : rotationInsensitiveHeight
|
||||
height: rotationAngle % 180 === 0 ? rotationInsensitiveHeight : rotationInsensitiveWidth
|
||||
fillMode: Image.PreserveAspectFit
|
||||
clip: true
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic}
|
||||
}
|
||||
Behavior on height {
|
||||
NumberAnimation {duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic}
|
||||
}
|
||||
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
width: image.width
|
||||
height: image.height
|
||||
source: root.blurhash !== "" ? ("image://blurhash/" + root.blurhash) : ""
|
||||
visible: root.blurhash !== "" && parent.status !== Image.Ready
|
||||
}
|
||||
|
||||
transform: [
|
||||
Rotation {
|
||||
origin.x: image.width / 2
|
||||
origin.y: image.height / 2
|
||||
angle: image.rotationAngle
|
||||
|
||||
Behavior on angle {
|
||||
RotationAnimation {duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic}
|
||||
}
|
||||
},
|
||||
Scale {
|
||||
origin.x: image.width / 2
|
||||
origin.y: image.height / 2
|
||||
xScale: image.scaleFactor
|
||||
yScale: image.scaleFactor
|
||||
|
||||
Behavior on xScale {
|
||||
NumberAnimation {duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic}
|
||||
}
|
||||
Behavior on yScale {
|
||||
NumberAnimation {duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
onClicked: {
|
||||
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.close)
|
||||
contextMenu.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onClicked: {
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
Component {
|
||||
id: saveAsDialog
|
||||
FileDialog {
|
||||
fileMode: FileDialog.SaveFile
|
||||
folder: StandardPaths.writableLocation(StandardPaths.DownloadLocation)
|
||||
onAccepted: {
|
||||
if (!currentFile) {
|
||||
return;
|
||||
}
|
||||
currentRoom.downloadFile(eventId, currentFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
text: i18n("Close")
|
||||
icon.name: "dialog-close"
|
||||
display: AbstractButton.IconOnly
|
||||
|
||||
width: Kirigami.Units.gridUnit * 2
|
||||
height: Kirigami.Units.gridUnit * 2
|
||||
|
||||
onClicked: root.destroy()
|
||||
onClosed: {
|
||||
image.scaleFactor = 1
|
||||
image.rotationAngle = 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,24 +5,18 @@ 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.kirigami 2.19 as Kirigami
|
||||
|
||||
import org.kde.neochat 1.0
|
||||
import NeoChat.Component 1.0
|
||||
|
||||
Kirigami.PlaceholderMessage {
|
||||
Kirigami.LoadingPlaceholder {
|
||||
property var showContinueButton: false
|
||||
property var showBackButton: false
|
||||
property string title: i18n("Loading…")
|
||||
text: i18n("Synchronizing with your homeserver…")
|
||||
icon.name: "cloud-download"
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
QQC2.Label {
|
||||
text: i18n("Please wait. This might take a little while.")
|
||||
}
|
||||
|
||||
QQC2.BusyIndicator {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
running: false
|
||||
}
|
||||
explanation: i18n("Please wait. This might take a little while.")
|
||||
}
|
||||
|
||||
@@ -14,24 +14,29 @@ import NeoChat.Component 1.0
|
||||
LoginStep {
|
||||
id: login
|
||||
|
||||
showContinueButton: true
|
||||
showContinueButton: LoginHelper.homeserverReachable
|
||||
showBackButton: false
|
||||
|
||||
title: i18nc("@title", "Login")
|
||||
message: i18n("Enter your Matrix ID")
|
||||
message: i18n("Welcome to NeoChat!")
|
||||
|
||||
Component.onCompleted: {
|
||||
LoginHelper.matrixId = ""
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
text: i18n("To get started, enter your matrix ID:")
|
||||
}
|
||||
|
||||
Kirigami.FormLayout {
|
||||
QQC2.TextField {
|
||||
id: matrixIdField
|
||||
Kirigami.FormData.label: i18n("Matrix ID:")
|
||||
placeholderText: "@user:matrix.org"
|
||||
onTextChanged: {
|
||||
if(acceptableInput) {
|
||||
LoginHelper.matrixId = text
|
||||
} else {
|
||||
LoginHelper.matrixId = ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,5 +66,6 @@ LoginStep {
|
||||
}
|
||||
}
|
||||
enabled: LoginHelper.homeserverReachable
|
||||
iconName: "go-next"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ LoginStep {
|
||||
onTriggered: {
|
||||
LoginHelper.login();
|
||||
}
|
||||
iconName: "go-next"
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
||||
@@ -26,15 +26,23 @@ LoginStep {
|
||||
processed("qrc:/imports/NeoChat/Component/Login/Loading.qml")
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
QQC2.Button {
|
||||
text: i18nc("@action:button", "Back")
|
||||
|
||||
QQC2.Button {
|
||||
text: i18n("Login")
|
||||
onClicked: {
|
||||
LoginHelper.loginWithSso()
|
||||
root.showMessage(i18n("Complete the authentication steps in your browser"))
|
||||
onClicked: {
|
||||
module.source = "qrc:/imports/NeoChat/Component/Login/Login.qml"
|
||||
}
|
||||
}
|
||||
QQC2.Button {
|
||||
text: i18n("Login")
|
||||
onClicked: {
|
||||
LoginHelper.loginWithSso()
|
||||
root.showMessage(i18n("Complete the authentication steps in your browser"))
|
||||
}
|
||||
Component.onCompleted: forceActiveFocus()
|
||||
Keys.onReturnPressed: clicked()
|
||||
}
|
||||
Component.onCompleted: forceActiveFocus()
|
||||
Keys.onReturnPressed: clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -6,6 +6,8 @@ import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import Qt.labs.platform 1.1
|
||||
|
||||
import org.kde.kirigami 2.15 as Kirigami
|
||||
|
||||
import org.kde.neochat 1.0
|
||||
import NeoChat.Component 1.0
|
||||
import NeoChat.Dialog 1.0
|
||||
@@ -50,6 +52,7 @@ TimelineContainer {
|
||||
|
||||
ToolTip.text: model.display
|
||||
ToolTip.visible: hoverHandler.hovered
|
||||
ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
|
||||
HoverHandler {
|
||||
id: hoverHandler
|
||||
@@ -89,16 +92,21 @@ TimelineContainer {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onLongPressed: openFileContext(model, parent)
|
||||
onTapped: {
|
||||
fullScreenImage.createObject(parent, {
|
||||
filename: eventId,
|
||||
source: model.mediaUrl,
|
||||
blurhash: model.content.info["xyz.amorgan.blurhash"],
|
||||
imageWidth: content.info.w,
|
||||
imageHeight: content.info.h
|
||||
}).showFullScreen();
|
||||
img.ToolTip.hide()
|
||||
fullScreenImage.open()
|
||||
}
|
||||
}
|
||||
|
||||
FullScreenImage {
|
||||
id: fullScreenImage
|
||||
filename: eventId
|
||||
source: mediaUrl
|
||||
blurhash: model.content.info["xyz.amorgan.blurhash"]
|
||||
imageWidth: content.info.w
|
||||
imageHeight: content.info.h
|
||||
modelData: model
|
||||
}
|
||||
|
||||
function downloadAndOpen() {
|
||||
if (downloaded) {
|
||||
openSavedFile()
|
||||
|
||||
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,26 @@ 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 {
|
||||
Layout.maximumWidth: messageDelegate.contentMaxWidth
|
||||
RichLabel {
|
||||
id: label
|
||||
isEmote: messageDelegate.isEmote
|
||||
}
|
||||
Loader {
|
||||
id: linkPreviewLoader
|
||||
Layout.fillWidth: true
|
||||
height: active ? item.implicitHeight : 0
|
||||
active: !currentRoom.usesEncryption && model.display && model.display.includes("http")
|
||||
visible: active
|
||||
sourceComponent: LinkPreviewDelegate {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ MouseArea {
|
||||
id: replyButton
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: replyName.implicitHeight + (loader.item ? loader.item.height : 0) + Kirigami.Units.largeSpacing
|
||||
implicitWidth: Math.min(contentMaxWidth, Math.max((loader.item ? loader.item.width + Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing : 0), replyName.implicitWidth)) + Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 3
|
||||
implicitWidth: Math.min(contentMaxWidth, Math.max((loader.item ? loader.item.width : 0), replyName.implicitWidth)) + Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing
|
||||
Component.onCompleted: {
|
||||
parent.Layout.fillWidth = true;
|
||||
parent.Layout.preferredWidth = Qt.binding(function() { return implicitWidth; })
|
||||
|
||||
@@ -15,14 +15,31 @@ TextEdit {
|
||||
readonly property var hasSpoiler: /data-mx-spoiler/g
|
||||
|
||||
property bool isEmote: false
|
||||
property string textMessage: model.display
|
||||
|
||||
/* Turn all links which aren't already in <a> tags into <a> hyperlinks */
|
||||
readonly property var linkRegex: /(href=["'])?(\b(https?):\/\/[^\s\<\>\"\'\\]+)/g
|
||||
property string textMessage: model.display.includes("http")
|
||||
? model.display.replace(linkRegex, function() {
|
||||
if (arguments[1]) {
|
||||
return arguments[0];
|
||||
} else {
|
||||
var l = arguments[2];
|
||||
if ([".", ","].includes(l[l.length-1])) {
|
||||
var link = l.substring(0, l.length-1);
|
||||
var leftover = l[l.length-1];
|
||||
return "<a href=\"" + link + "\">" + link + "</a>" + leftover;
|
||||
}
|
||||
return "<a href=\"" + l + "\">" + l + "</a>";
|
||||
}
|
||||
})
|
||||
: model.display
|
||||
property bool spoilerRevealed: !hasSpoiler.test(textMessage)
|
||||
|
||||
ListView.onReused: Qt.binding(() => !hasSpoiler.test(textMessage))
|
||||
|
||||
Layout.fillWidth: Config.compactLayout
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.fillWidth: true
|
||||
|
||||
persistentSelection: true
|
||||
|
||||
text: "<style>
|
||||
table {
|
||||
@@ -56,6 +73,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
|
||||
|
||||
@@ -18,6 +18,17 @@ QQC2.ItemDelegate {
|
||||
|
||||
property bool isEmote: false
|
||||
property bool cardBackground: true
|
||||
property bool isHighlighted: model.isHighlighted || isTemporaryHighlighted
|
||||
property bool isTemporaryHighlighted: false
|
||||
|
||||
onIsTemporaryHighlightedChanged: if (isTemporaryHighlighted) temporaryHighlightTimer.start()
|
||||
|
||||
Timer {
|
||||
id: temporaryHighlightTimer
|
||||
|
||||
interval: 1500
|
||||
onTriggered: isTemporaryHighlighted = false
|
||||
}
|
||||
|
||||
signal openContextMenu
|
||||
|
||||
@@ -42,9 +53,16 @@ QQC2.ItemDelegate {
|
||||
|
||||
topPadding: 0
|
||||
bottomPadding: 0
|
||||
topInset: showAuthor ? Kirigami.Units.largeSpacing : (Config.compactLayout ? 1 : Kirigami.Units.smallSpacing)
|
||||
leftInset: Kirigami.Units.smallSpacing
|
||||
rightInset: Kirigami.Units.smallSpacing
|
||||
width: delegateMaxWidth
|
||||
height: sectionDelegate.height + Math.max(model.showAuthor ? avatar.height : 0, bubble.implicitHeight) + loader.height + (showAuthor ? Kirigami.Units.largeSpacing : 0)
|
||||
background: null
|
||||
height: sectionDelegate.height + Math.max(model.showAuthor ? avatar.height : 0, bubble.implicitHeight) + loader.height + (showAuthor ? Kirigami.Units.largeSpacing : (Config.compactLayout ? 1 : Kirigami.Units.smallSpacing))
|
||||
background: Rectangle {
|
||||
visible: timelineContainer.hovered
|
||||
color: Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.15)
|
||||
radius: Kirigami.Units.smallSpacing
|
||||
}
|
||||
|
||||
property Item hoverComponent
|
||||
|
||||
@@ -103,15 +121,20 @@ QQC2.ItemDelegate {
|
||||
|
||||
Kirigami.Avatar {
|
||||
id: avatar
|
||||
width: visible || Config.showAvatarInTimeline ? Kirigami.Units.gridUnit * 2 : 0
|
||||
width: visible || Config.showAvatarInTimeline ? Kirigami.Units.gridUnit * 2 + Kirigami.Units.smallSpacing * 2 : 0
|
||||
height: width
|
||||
padding: Kirigami.Units.smallSpacing
|
||||
topInset: Kirigami.Units.smallSpacing
|
||||
bottomInset: Kirigami.Units.smallSpacing
|
||||
leftInset: Kirigami.Units.smallSpacing
|
||||
rightInset: Kirigami.Units.smallSpacing
|
||||
sourceSize.width: width
|
||||
sourceSize.height: width
|
||||
anchors {
|
||||
top: sectionDelegate.bottom
|
||||
topMargin: model.showAuthor ? Kirigami.Units.largeSpacing : 0
|
||||
topMargin: model.showAuthor ? Kirigami.Units.largeSpacing : (Config.compactLayout ? 1 : Kirigami.Units.smallSpacing)
|
||||
left: parent.left
|
||||
leftMargin: Kirigami.Units.largeSpacing
|
||||
leftMargin: Kirigami.Units.smallSpacing
|
||||
}
|
||||
|
||||
visible: model.showAuthor &&
|
||||
@@ -140,13 +163,13 @@ QQC2.ItemDelegate {
|
||||
id: bubble
|
||||
topPadding: Config.compactLayout ? Kirigami.Units.smallSpacing / 2 : Kirigami.Units.largeSpacing
|
||||
bottomPadding: Config.compactLayout ? Kirigami.Units.mediumSpacing / 2 : Kirigami.Units.largeSpacing
|
||||
leftPadding: Kirigami.Units.smallSpacing
|
||||
rightPadding: Config.compactLayout ? Kirigami.Units.largeSpacing : Kirigami.Units.smallSpacing
|
||||
leftPadding: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing
|
||||
rightPadding: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing
|
||||
hoverEnabled: true
|
||||
|
||||
anchors {
|
||||
top: avatar.top
|
||||
leftMargin: Kirigami.Units.largeSpacing
|
||||
leftMargin: Kirigami.Units.smallSpacing
|
||||
rightMargin: showUserMessageOnRight ? Kirigami.Units.smallSpacing : Kirigami.Units.largeSpacing
|
||||
}
|
||||
// HACK: anchoring didn't reset anchors.right when switching from parent.right to undefined reliably
|
||||
@@ -182,23 +205,17 @@ QQC2.ItemDelegate {
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
id: column
|
||||
spacing: 0
|
||||
Item {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
visible: model.showAuthor && !isEmote
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.preferredWidth: nameLabel.implicitWidth + timeLabel.implicitWidth + Kirigami.Units.largeSpacing * 2
|
||||
Layout.maximumWidth: contentMaxWidth
|
||||
implicitHeight: visible ? nameLabel.implicitHeight : 0
|
||||
|
||||
QQC2.Label {
|
||||
id: nameLabel
|
||||
topInset: 0
|
||||
|
||||
visible: model.showAuthor && !isEmote
|
||||
width: Math.min(contentMaxWidth - timeLabel.width, implicitWidth)
|
||||
Layout.maximumWidth: contentMaxWidth - timeLabel.implicitWidth - rowLayout.spacing
|
||||
|
||||
text: visible ? author.displayName : ""
|
||||
textFormat: Text.PlainText
|
||||
@@ -221,14 +238,12 @@ QQC2.ItemDelegate {
|
||||
}
|
||||
QQC2.Label {
|
||||
id: timeLabel
|
||||
leftPadding: Kirigami.Units.largeSpacing
|
||||
rightPadding: Kirigami.Units.largeSpacing
|
||||
anchors.left: nameLabel.right
|
||||
visible: model.showAuthor && !isEmote
|
||||
|
||||
text: visible ? time.toLocaleTimeString(Qt.locale(), Locale.ShortFormat) : ""
|
||||
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
|
||||
@@ -240,9 +255,6 @@ QQC2.ItemDelegate {
|
||||
active: model.reply !== undefined
|
||||
source: 'qrc:imports/NeoChat/Component/Timeline/ReplyComponent.qml'
|
||||
visible: active
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||
Layout.bottomMargin: Config.compactLayout ? 0 : Kirigami.Units.smallSpacing
|
||||
Layout.leftMargin: Config.compactLayout ? 0 : Kirigami.Units.largeSpacing
|
||||
|
||||
Connections {
|
||||
target: replyLoader.item
|
||||
@@ -254,19 +266,14 @@ QQC2.ItemDelegate {
|
||||
}
|
||||
|
||||
background: Item {
|
||||
Rectangle {
|
||||
visible: timelineContainer.hovered
|
||||
color: Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.15)
|
||||
radius: Kirigami.Units.smallSpacing
|
||||
anchors.fill: parent
|
||||
}
|
||||
Kirigami.ShadowedRectangle {
|
||||
id: bubbleBackground
|
||||
visible: cardBackground && !Config.compactLayout
|
||||
anchors.fill: parent
|
||||
color: {
|
||||
if (model.author.isLocalUser) {
|
||||
return Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.15)
|
||||
} else if (model.isHighlighted) {
|
||||
} else if (timelineContainer.isHighlighted) {
|
||||
return Kirigami.Theme.positiveBackgroundColor
|
||||
} else {
|
||||
return Kirigami.Theme.backgroundColor
|
||||
@@ -274,9 +281,13 @@ QQC2.ItemDelegate {
|
||||
}
|
||||
radius: Kirigami.Units.smallSpacing
|
||||
shadow.size: Kirigami.Units.smallSpacing
|
||||
shadow.color: !model.isHighlighted ? Qt.rgba(0.0, 0.0, 0.0, 0.10) : Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.10)
|
||||
shadow.color: timelineContainer.isHighlighted ? Qt.rgba(0.0, 0.0, 0.0, 0.10) : Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.10)
|
||||
border.color: Kirigami.ColorUtils.tintWithAlpha(color, Kirigami.Theme.textColor, 0.15)
|
||||
border.width: 1
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {target: bubbleBackground; duration: Kirigami.Units.veryLongDuration; easing.type: Easing.InOutCubic}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,27 +42,17 @@ Kirigami.ScrollablePage {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Connections {
|
||||
target: SpaceHierarchyCache
|
||||
function onSpaceHierarchyChanged() {
|
||||
if (spaceList.activeSpaceId !== '') {
|
||||
sortFilterRoomListModel.activeSpaceRooms = SpaceHierarchyCache.getRoomListForSpace(spaceList.activeSpaceId, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
header: QQC2.Control {
|
||||
contentItem: QQC2.RoundButton {
|
||||
id: homeButton
|
||||
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: {
|
||||
sortFilterRoomListModel.activeSpaceRooms = [];
|
||||
sortFilterRoomListModel.activeSpaceId = "";
|
||||
spaceList.activeSpaceId = '';
|
||||
listView.positionViewAtIndex(0, ListView.Beginning);
|
||||
}
|
||||
@@ -82,13 +72,10 @@ Kirigami.ScrollablePage {
|
||||
implicitHeight: ListView.view.headerItem.implicitHeight
|
||||
|
||||
contentItem: Kirigami.Avatar {
|
||||
id: del
|
||||
|
||||
actions.main: Kirigami.Action {
|
||||
id: enterSpaceAction
|
||||
onTriggered: {
|
||||
spaceList.activeSpaceId = id;
|
||||
sortFilterRoomListModel.activeSpaceRooms = SpaceHierarchyCache.getRoomListForSpace(id, true);
|
||||
sortFilterRoomListModel.activeSpaceId = id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,7 +355,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
|
||||
@@ -203,7 +207,7 @@ Kirigami.ScrollablePage {
|
||||
readonly property int largestVisibleIndex: count > 0 ? indexAt(contentX + (width / 2), contentY + height - 1) : -1
|
||||
readonly property bool isLoaded: page.width * page.height > 10
|
||||
|
||||
spacing: Config.compactLayout ? 1 : Kirigami.Units.smallSpacing
|
||||
spacing: 0
|
||||
|
||||
verticalLayoutDirection: ListView.BottomToTop
|
||||
highlightMoveDuration: 500
|
||||
@@ -426,7 +430,9 @@ Kirigami.ScrollablePage {
|
||||
headerPositioning: ListView.OverlayHeader
|
||||
|
||||
function goToEvent(eventID) {
|
||||
messageListView.positionViewAtIndex(eventToIndex(eventID), ListView.Contain)
|
||||
const index = eventToIndex(eventID)
|
||||
messageListView.positionViewAtIndex(index, ListView.Center)
|
||||
itemAtIndex(index).isTemporaryHighlighted = true
|
||||
}
|
||||
|
||||
Item {
|
||||
@@ -452,8 +458,9 @@ 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
|
||||
x: delegate && bubble ? (delegate.x + bubble.x + Kirigami.Units.largeSpacing + childOffset - (Config.compactLayout ? Kirigami.Units.gridUnit * 3 + (delegate.width >= Kirigami.Units.gridUnit * 20 ? Kirigami.Units.gridUnit * 2 : 0 ): 0) - (userMsg && !Config.compactLayout ? Kirigami.Units.gridUnit : 0)) : 0
|
||||
y: bubble ? bubble.mapToItem(parent, 0, 0).y - hoverActions.childHeight + Kirigami.Units.smallSpacing: 0;
|
||||
|
||||
visible: false
|
||||
|
||||
property var updateFunction
|
||||
@@ -469,10 +476,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 +506,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 +519,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 +593,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) {
|
||||
|
||||
@@ -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
|
||||
import NeoChat.Component.Login 1.0
|
||||
@@ -45,10 +45,81 @@ Kirigami.ScrollablePage {
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Kirigami.Icon {
|
||||
source: "org.kde.neochat"
|
||||
Item {
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 10
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 16
|
||||
|
||||
id: swapper
|
||||
states: [
|
||||
State {
|
||||
when: !LoginHelper.homeserverReachable
|
||||
name: "idle"
|
||||
PropertyChanges {
|
||||
target: icon
|
||||
opacity: 1
|
||||
}
|
||||
PropertyChanges {
|
||||
target: avi
|
||||
opacity: 0
|
||||
}
|
||||
},
|
||||
State {
|
||||
when: LoginHelper.homeserverReachable
|
||||
name: "showAvi"
|
||||
PropertyChanges {
|
||||
target: icon
|
||||
opacity: 0
|
||||
}
|
||||
PropertyChanges {
|
||||
target: avi
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
to: "showAvi"
|
||||
SequentialAnimation {
|
||||
NumberAnimation { target: icon; properties: "opacity";}
|
||||
NumberAnimation { target: avi; properties: "opacity";}
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
from: "showAvi"
|
||||
SequentialAnimation {
|
||||
NumberAnimation { target: avi; properties: "opacity";}
|
||||
NumberAnimation { target: icon; properties: "opacity";}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Kirigami.Icon {
|
||||
id: icon
|
||||
source: "org.kde.neochat"
|
||||
anchors.fill: parent
|
||||
implicitWidth: height
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: avi
|
||||
opacity: 0
|
||||
anchors.fill: parent
|
||||
Kirigami.Avatar {
|
||||
source: LoginHelper.loginAvatar
|
||||
name: LoginHelper.loginName
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: height
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
text: LoginHelper.loginName
|
||||
font.pointSize: 24
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
Controls.Label {
|
||||
Layout.fillWidth: true
|
||||
@@ -66,6 +137,7 @@ Kirigami.ScrollablePage {
|
||||
headerMessage.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
@@ -78,16 +150,26 @@ Kirigami.ScrollablePage {
|
||||
onClicked: {
|
||||
module.source = welcomePage.currentStep.previousUrl
|
||||
}
|
||||
icon.name: "go-back"
|
||||
}
|
||||
|
||||
Controls.Button {
|
||||
id: continueButton
|
||||
enabled: welcomePage.currentStep.acceptable
|
||||
visible: welcomePage.currentStep.showContinueButton
|
||||
opacity: welcomePage.currentStep.showContinueButton ? 1 : 0
|
||||
Behavior on opacity { NumberAnimation {} }
|
||||
action: welcomePage.currentStep.action
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.LoadingPlaceholder {
|
||||
icon.name: "online"
|
||||
opacity: LoginHelper.testing ? 1 : 0
|
||||
text: i18n("Connecting to your homeserver...")
|
||||
Behavior on opacity { NumberAnimation {} }
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: currentStep
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
@@ -174,11 +194,13 @@ Kirigami.OverlayDrawer {
|
||||
TextEdit {
|
||||
Layout.fillWidth: true
|
||||
text: room && room.topic ? room.topic.replace(replaceLinks, "<a href=\"$1\">$1</a>") : i18n("No Topic")
|
||||
readonly property var replaceLinks: /\(https:\/\/[^ ]*\)/
|
||||
readonly property var replaceLinks: /(https:\/\/[^ ]*)/
|
||||
textFormat: TextEdit.MarkdownText
|
||||
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,53 @@ 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 {
|
||||
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
|
||||
label: name
|
||||
|
||||
onClicked: {
|
||||
const popup = userDetailDialog.createObject(ApplicationWindow.overlay, {room: room, user: user, displayName: name, avatarMediaId: avatar})
|
||||
popup.closed.connect(function() {
|
||||
userListItem.highlighted = false
|
||||
})
|
||||
if (roomDrawer.modal) {
|
||||
roomDrawer.close()
|
||||
}
|
||||
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 +324,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
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
<name xml:lang="ia">Neochat</name>
|
||||
<name xml:lang="id">NeoChat</name>
|
||||
<name xml:lang="it">NeoChat</name>
|
||||
<name xml:lang="ka">NeoChat</name>
|
||||
<name xml:lang="ko">NeoChat</name>
|
||||
<name xml:lang="nl">NeoChat</name>
|
||||
<name xml:lang="nn">NeoChat</name>
|
||||
@@ -56,6 +57,7 @@
|
||||
<summary xml:lang="ia">Un cliente per Matrix, le protocollo de communication decentralisate</summary>
|
||||
<summary xml:lang="id">Klien untuk matrix, protokol komunikasi terdesentralisasi</summary>
|
||||
<summary xml:lang="it">Un client per matrix, il protocollo di comunicazione decentralizzato</summary>
|
||||
<summary xml:lang="ka">კლიენტი Matrix-სთვის, დეცენტრალიზებული კომუნიკაციის პროტოკოლისთვის</summary>
|
||||
<summary xml:lang="ko">Matrix, 분산 대화 프로토콜 클라이언트</summary>
|
||||
<summary xml:lang="nl">Een client voor matrix, het gedecentraliseerde communicatieprotocol</summary>
|
||||
<summary xml:lang="nn">Ein klient for Matrix, den desentraliserte lynmeldingsprotokollen</summary>
|
||||
@@ -174,6 +176,7 @@
|
||||
<developer_name xml:lang="ia">Le communitate de KDE</developer_name>
|
||||
<developer_name xml:lang="id">Komunitas KDE</developer_name>
|
||||
<developer_name xml:lang="it">La comunità KDE</developer_name>
|
||||
<developer_name xml:lang="ka">KDE-ის საზოგადოება</developer_name>
|
||||
<developer_name xml:lang="ko">KDE 커뮤니티</developer_name>
|
||||
<developer_name xml:lang="nl">De KDE gemeenschap</developer_name>
|
||||
<developer_name xml:lang="nn">KDE-fellesskapet</developer_name>
|
||||
@@ -206,6 +209,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>
|
||||
|
||||
@@ -18,6 +18,7 @@ Name[hu]=NeoChat
|
||||
Name[ia]=Neochat
|
||||
Name[id]=NeoChat
|
||||
Name[it]=NeoChat
|
||||
Name[ka]=NeoChat
|
||||
Name[ko]=NeoChat
|
||||
Name[lt]=NeoChat
|
||||
Name[nl]=NeoChat
|
||||
@@ -51,6 +52,7 @@ GenericName[hu]=Matrix kliens
|
||||
GenericName[ia]=Cliente de Matrice
|
||||
GenericName[id]=Klien Matrix
|
||||
GenericName[it]=Client Matrix
|
||||
GenericName[ka]=Matrix -ის კლიენტი
|
||||
GenericName[ko]=Matrix 클라이언트
|
||||
GenericName[lt]=Matrix kliento programą
|
||||
GenericName[nl]=Matrix-client
|
||||
@@ -83,6 +85,7 @@ Comment[hu]=Kliens a Matrix protokollhoz
|
||||
Comment[ia]=Cliente per le protocollo de Matrix
|
||||
Comment[id]=Klien untuk protokol Matrix
|
||||
Comment[it]=Client per il protocollo Matrix
|
||||
Comment[ka]=კლიენტი Matrix-ის პროტოკოლისთვის
|
||||
Comment[ko]=Matrix 프로토콜용 클라이언트
|
||||
Comment[lt]=Matrix protokolo kliento programa
|
||||
Comment[nl]=Client voor het Matrix-protocol
|
||||
|
||||
905
po/ar/neochat.po
905
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
930
po/az/neochat.po
930
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
923
po/ca/neochat.po
923
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
893
po/cs/neochat.po
893
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
905
po/da/neochat.po
905
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
930
po/de/neochat.po
930
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
944
po/es/neochat.po
944
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
1150
po/eu/neochat.po
1150
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
930
po/fi/neochat.po
930
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
940
po/fr/neochat.po
940
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
929
po/hu/neochat.po
929
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
930
po/ia/neochat.po
930
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
1029
po/id/neochat.po
1029
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
913
po/it/neochat.po
913
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
899
po/ja/neochat.po
899
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
2382
po/ka/neochat.po
Normal file
2382
po/ka/neochat.po
Normal file
File diff suppressed because it is too large
Load Diff
930
po/ko/neochat.po
930
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
942
po/nl/neochat.po
942
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
854
po/nn/neochat.po
854
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
931
po/pa/neochat.po
931
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
925
po/pl/neochat.po
925
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
928
po/pt/neochat.po
928
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
902
po/ru/neochat.po
902
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
928
po/sk/neochat.po
928
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
926
po/sl/neochat.po
926
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
929
po/sv/neochat.po
929
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
941
po/ta/neochat.po
941
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
922
po/tr/neochat.po
922
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
930
po/uk/neochat.po
930
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
45
qml/main.qml
45
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 {
|
||||
@@ -453,14 +449,15 @@ Kirigami.ApplicationWindow {
|
||||
|
||||
property Item hoverLinkIndicator: QQC2.Control {
|
||||
parent: overlay.parent
|
||||
property alias text: linkText.text
|
||||
opacity: text.length > 0 ? 1 : 0
|
||||
property string text
|
||||
opacity: linkText.text.length > 0 ? 1 : 0
|
||||
|
||||
z: 20
|
||||
x: 0
|
||||
y: parent.height - implicitHeight
|
||||
contentItem: QQC2.Label {
|
||||
id: linkText
|
||||
text: parent.text.startsWith("https://matrix.to/") ? "" : parent.text
|
||||
}
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
background: Rectangle {
|
||||
|
||||
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,20 @@ if(ANDROID)
|
||||
"org.kde.neochat"
|
||||
"preferences-system-users"
|
||||
"preferences-desktop-theme-global"
|
||||
"notifications"
|
||||
"zoom-in"
|
||||
"zoom-out"
|
||||
"image-rotate-left-symbolic"
|
||||
"image-rotate-right-symbolic"
|
||||
)
|
||||
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 +164,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)
|
||||
|
||||
@@ -2,17 +2,13 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "actionshandler.h"
|
||||
#include "controller.h"
|
||||
|
||||
#include <csapi/joining.h>
|
||||
#include <events/roommessageevent.h>
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <QDebug>
|
||||
#include <QStringBuilder>
|
||||
|
||||
#include "controller.h"
|
||||
#include "customemojimodel.h"
|
||||
#include "neochatroom.h"
|
||||
#include "roommanager.h"
|
||||
|
||||
ActionsHandler::ActionsHandler(QObject *parent)
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "connection.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
using namespace Quotient;
|
||||
namespace Quotient
|
||||
{
|
||||
class Connection;
|
||||
}
|
||||
|
||||
class NeoChatRoom;
|
||||
class CustomEmojiModel;
|
||||
|
||||
/// \brief Handles user interactions with NeoChat (joining room, creating room,
|
||||
@@ -19,7 +20,7 @@ class ActionsHandler : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
/// \brief The connection that will handle sending the message.
|
||||
Q_PROPERTY(Connection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||
Q_PROPERTY(Quotient::Connection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||
|
||||
/// \brief The connection that will handle sending the message.
|
||||
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
|
||||
@@ -34,8 +35,8 @@ public:
|
||||
explicit ActionsHandler(QObject *parent = nullptr);
|
||||
~ActionsHandler();
|
||||
|
||||
[[nodiscard]] Connection *connection() const;
|
||||
void setConnection(Connection *connection);
|
||||
[[nodiscard]] Quotient::Connection *connection() const;
|
||||
void setConnection(Quotient::Connection *connection);
|
||||
|
||||
[[nodiscard]] NeoChatRoom *room() const;
|
||||
void setRoom(NeoChatRoom *room);
|
||||
@@ -68,6 +69,6 @@ public Q_SLOTS:
|
||||
void postEdit(const QString &text);
|
||||
|
||||
private:
|
||||
Connection *m_connection = nullptr;
|
||||
Quotient::Connection *m_connection = nullptr;
|
||||
NeoChatRoom *m_room = nullptr;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
#include "blurhash.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
|
||||
@@ -3,11 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
uint8_t *decode(const char *blurhash, int width, int height, int punch, int nChannels);
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "chatboxhelper.h"
|
||||
#include <QDebug>
|
||||
|
||||
ChatBoxHelper::ChatBoxHelper(QObject *parent)
|
||||
: QObject(parent)
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -3,13 +3,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QFont>
|
||||
#include <QObject>
|
||||
#include <QQuickTextDocument>
|
||||
#include <QTextCursor>
|
||||
#include <QUrl>
|
||||
|
||||
class QTextDocument;
|
||||
class QQuickTextDocument;
|
||||
class NeoChatRoom;
|
||||
class Controller;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -3,10 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QPair>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include "messageeventmodel.h"
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
class CollapseStateProxyModel : public QSortFilterProxyModel
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user