Compare commits
65 Commits
work/redst
...
1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
143bd456de | ||
|
|
513de82515 | ||
|
|
581fd5f212 | ||
|
|
2452f630d0 | ||
|
|
2cdc37c3d5 | ||
|
|
5af99a872c | ||
|
|
5fe7ba478b | ||
|
|
f25bc6bac6 | ||
|
|
46ee015775 | ||
|
|
f93be04b2b | ||
|
|
f2d9ca13d8 | ||
|
|
eb04f4adf1 | ||
|
|
6b983332af | ||
|
|
4e197c3cc8 | ||
|
|
bfd6d2ffe2 | ||
|
|
421422edd0 | ||
|
|
0eda9608ec | ||
|
|
06fd8630d9 | ||
|
|
e7d8c4b69c | ||
|
|
44d4269978 | ||
|
|
759244b5d2 | ||
|
|
92a307747f | ||
|
|
627929203f | ||
|
|
0c449ab4bd | ||
|
|
45c35b3cbe | ||
|
|
ecd6a63564 | ||
|
|
b75bf3a75b | ||
|
|
43288db000 | ||
|
|
0d6c793a5e | ||
|
|
6daf184a60 | ||
|
|
93173e3f43 | ||
|
|
ec4aa320c1 | ||
|
|
079a1c8a34 | ||
|
|
e9bb0972a9 | ||
|
|
1717790096 | ||
|
|
893bc79f1e | ||
|
|
e87ae48f17 | ||
|
|
97bbcf3062 | ||
|
|
9f9498541a | ||
|
|
50a4d0a33f | ||
|
|
85b4d7d049 | ||
|
|
b574849df3 | ||
|
|
e802d7f805 | ||
|
|
99438011ca | ||
|
|
99ccfaf93e | ||
|
|
c56973763c | ||
|
|
c96109e9b7 | ||
|
|
3f01b3badf | ||
|
|
093412c788 | ||
|
|
c5ddb61981 | ||
|
|
ad4e52b20d | ||
|
|
5991d59ddd | ||
|
|
de49a26462 | ||
|
|
4924702c15 | ||
|
|
8060edd1c6 | ||
|
|
89bf5d3a31 | ||
|
|
60762b934c | ||
|
|
7729fec259 | ||
|
|
026769b07f | ||
|
|
5be14a4b8f | ||
|
|
0b70d2b33f | ||
|
|
dab77b8d07 | ||
|
|
2acdf61b16 | ||
|
|
defa3d4b77 | ||
|
|
be709a2732 |
@@ -20,22 +20,48 @@ include(KDEClangFormat)
|
|||||||
include(KDECMakeSettings)
|
include(KDECMakeSettings)
|
||||||
include(KDECompilerSettings NO_POLICY_SCOPE)
|
include(KDECompilerSettings NO_POLICY_SCOPE)
|
||||||
|
|
||||||
|
include(cmake/Flatpak.cmake)
|
||||||
|
|
||||||
# Fix a crash due to problems with quotient's event system. Can probably be removed once the reworked event system is in
|
# Fix a crash due to problems with quotient's event system. Can probably be removed once the reworked event system is in
|
||||||
cmake_policy(SET CMP0063 OLD)
|
cmake_policy(SET CMP0063 OLD)
|
||||||
|
|
||||||
ecm_setup_version(0.1.0
|
ecm_setup_version(1.0.1
|
||||||
VARIABLE_PREFIX NEOCHAT
|
VARIABLE_PREFIX NEOCHAT
|
||||||
VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/neochat-version.h
|
VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/neochat-version.h
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS Widgets Core Quick Gui QuickControls2 Multimedia Svg)
|
find_package(Qt5 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Widgets Core Quick Gui QuickControls2 Multimedia Svg)
|
||||||
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Kirigami2 I18n Notifications Config CoreAddons)
|
set_package_properties(Qt5 PROPERTIES
|
||||||
|
TYPE REQUIRED
|
||||||
|
PURPOSE "Basic application components"
|
||||||
|
)
|
||||||
|
find_package(KF5 ${KF5_MIN_VERSION} COMPONENTS Kirigami2 I18n Notifications Config CoreAddons)
|
||||||
|
set_package_properties(KF5 PROPERTIES
|
||||||
|
TYPE REQUIRED
|
||||||
|
PURPOSE "Basic application components"
|
||||||
|
)
|
||||||
|
set_package_properties(KF5Kirigami2 PROPERTIES
|
||||||
|
TYPE REQUIRED
|
||||||
|
PURPOSE "Kirigami application UI framework"
|
||||||
|
)
|
||||||
|
|
||||||
if(ANDROID)
|
if(ANDROID)
|
||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL)
|
||||||
|
set_package_properties(OpenSSL PROPERTIES
|
||||||
|
TYPE REQUIRED
|
||||||
|
PURPOSE "Encrypted communications"
|
||||||
|
)
|
||||||
else()
|
else()
|
||||||
find_package(Qt5Keychain REQUIRED)
|
find_package(Qt5Keychain)
|
||||||
find_package(KF5DBusAddons ${KF5_MIN_VERSION} REQUIRED)
|
set_package_properties(Qt5Keychain PROPERTIES
|
||||||
|
TYPE REQUIRED
|
||||||
|
PURPOSE "Secure storage of account secrets"
|
||||||
|
)
|
||||||
|
find_package(KF5DBusAddons ${KF5_MIN_VERSION})
|
||||||
|
set_package_properties(KF5DBusAddons PROPERTIES
|
||||||
|
TYPE REQUIRED
|
||||||
|
PURPOSE "DBus convenience functions"
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(Quotient 0.6)
|
find_package(Quotient 0.6)
|
||||||
@@ -55,9 +81,11 @@ set_package_properties(cmark PROPERTIES
|
|||||||
)
|
)
|
||||||
|
|
||||||
ecm_find_qmlmodule(org.kde.kquickimageeditor 1.0)
|
ecm_find_qmlmodule(org.kde.kquickimageeditor 1.0)
|
||||||
|
ecm_find_qmlmodule(org.kde.kitemmodels 1.0)
|
||||||
|
|
||||||
find_package(KQuickImageEditor COMPONENTS)
|
find_package(KQuickImageEditor COMPONENTS)
|
||||||
set_package_properties(KQuickImageEditor PROPERTIES
|
set_package_properties(KQuickImageEditor PROPERTIES
|
||||||
|
TYPE REQUIRED
|
||||||
DESCRIPTION "Simple image editor for QtQuick applications"
|
DESCRIPTION "Simple image editor for QtQuick applications"
|
||||||
URL "https://invent.kde.org/libraries/kquickimageeditor/"
|
URL "https://invent.kde.org/libraries/kquickimageeditor/"
|
||||||
PURPOSE "Add image editing capability to image attachments"
|
PURPOSE "Add image editing capability to image attachments"
|
||||||
@@ -65,7 +93,7 @@ set_package_properties(KQuickImageEditor PROPERTIES
|
|||||||
|
|
||||||
install(FILES org.kde.neochat.desktop DESTINATION ${KDE_INSTALL_APPDIR})
|
install(FILES org.kde.neochat.desktop DESTINATION ${KDE_INSTALL_APPDIR})
|
||||||
install(FILES org.kde.neochat.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR})
|
install(FILES org.kde.neochat.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR})
|
||||||
install(FILES neochat.svg DESTINATION ${KDE_INSTALL_FULL_ICONDIR}/hicolor/scalable/apps)
|
install(FILES org.kde.neochat.svg DESTINATION ${KDE_INSTALL_FULL_ICONDIR}/hicolor/scalable/apps)
|
||||||
install(FILES neochat.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR})
|
install(FILES neochat.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR})
|
||||||
|
|
||||||
# add_definitions(-DQT_NO_KEYWORDS) Need to fix libQuotient first
|
# add_definitions(-DQT_NO_KEYWORDS) Need to fix libQuotient first
|
||||||
|
|||||||
16
cmake/Flatpak.cmake
Normal file
16
cmake/Flatpak.cmake
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
if(NOT NEOCHAT_FLATPAK)
|
||||||
|
# Only include this if we build a Flatpak
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
|
# Include FontConfig config which uses the Emoji One font from the
|
||||||
|
# KDE Flatpak SDK.
|
||||||
|
install(
|
||||||
|
FILES
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Flatpak/99-noto-mono-color-emoji.conf
|
||||||
|
DESTINATION
|
||||||
|
${CMAKE_INSTALL_SYSCONFDIR}/fonts/conf.d/
|
||||||
|
)
|
||||||
|
|
||||||
23
cmake/Flatpak/99-noto-mono-color-emoji.conf
Normal file
23
cmake/Flatpak/99-noto-mono-color-emoji.conf
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
|
||||||
|
<fontconfig>
|
||||||
|
<alias>
|
||||||
|
<family>serif</family>
|
||||||
|
<prefer>
|
||||||
|
<family>Emoji One</family>
|
||||||
|
</prefer>
|
||||||
|
</alias>
|
||||||
|
<alias>
|
||||||
|
<family>sans-serif</family>
|
||||||
|
<prefer>
|
||||||
|
<family>Emoji One</family>
|
||||||
|
</prefer>
|
||||||
|
</alias>
|
||||||
|
<alias>
|
||||||
|
<family>monospace</family>
|
||||||
|
<prefer>
|
||||||
|
<family>Emoji One</family>
|
||||||
|
</prefer>
|
||||||
|
</alias>
|
||||||
|
</fontconfig>
|
||||||
|
|
||||||
@@ -89,8 +89,8 @@ ToolBar {
|
|||||||
Layout.preferredWidth: Kirigami.Units.gridUnit
|
Layout.preferredWidth: Kirigami.Units.gridUnit
|
||||||
Layout.preferredHeight: Kirigami.Units.gridUnit
|
Layout.preferredHeight: Kirigami.Units.gridUnit
|
||||||
|
|
||||||
source: replyUser ? "image://mxc/" + replyUser.avatarMediaId: ""
|
source: replyUser ? ("image://mxc/" + replyUser.avatarMediaId) : ""
|
||||||
name: replyUser ? replyUser.displayName : i18n("No name")
|
name: replyUser ? replyUser.name : i18n("No name")
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
@@ -133,8 +133,9 @@ ToolBar {
|
|||||||
keyNavigationWraps: true
|
keyNavigationWraps: true
|
||||||
|
|
||||||
delegate: Control {
|
delegate: Control {
|
||||||
property string displayText: modelData.displayName ?? modelData.unicode
|
readonly property string userId: modelData.id ?? ""
|
||||||
property bool isEmoji: modelData.unicode != null
|
readonly property string displayText: modelData.displayName ?? modelData.unicode
|
||||||
|
readonly property bool isEmoji: modelData.unicode != null
|
||||||
readonly property bool highlighted: autoCompleteListView.currentIndex == index
|
readonly property bool highlighted: autoCompleteListView.currentIndex == index
|
||||||
|
|
||||||
padding: Kirigami.Units.smallSpacing
|
padding: Kirigami.Units.smallSpacing
|
||||||
@@ -156,7 +157,7 @@ ToolBar {
|
|||||||
Kirigami.Avatar {
|
Kirigami.Avatar {
|
||||||
Layout.preferredWidth: Kirigami.Units.gridUnit
|
Layout.preferredWidth: Kirigami.Units.gridUnit
|
||||||
Layout.preferredHeight: Kirigami.Units.gridUnit
|
Layout.preferredHeight: Kirigami.Units.gridUnit
|
||||||
source: modelData.avatarMediaId ? "image://mxc/" + modelData.avatarMediaId : ""
|
source: modelData.avatarMediaId ? ("image://mxc/" + modelData.avatarMediaId) : ""
|
||||||
color: modelData.color ? Qt.darker(modelData.color, 1.1) : null
|
color: modelData.color ? Qt.darker(modelData.color, 1.1) : null
|
||||||
visible: !isEmoji
|
visible: !isEmoji
|
||||||
}
|
}
|
||||||
@@ -176,7 +177,7 @@ ToolBar {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
autoCompleteListView.currentIndex = index
|
autoCompleteListView.currentIndex = index
|
||||||
documentHandler.replaceAutoComplete(displayText)
|
inputField.autoComplete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -342,7 +343,8 @@ ToolBar {
|
|||||||
|
|
||||||
Keys.onReturnPressed: {
|
Keys.onReturnPressed: {
|
||||||
if (isAutoCompleting) {
|
if (isAutoCompleting) {
|
||||||
documentHandler.replaceAutoComplete(autoCompleteListView.currentItem.displayText)
|
inputField.autoComplete();
|
||||||
|
|
||||||
isAutoCompleting = false;
|
isAutoCompleting = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -356,7 +358,10 @@ ToolBar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onEscapePressed: closeAll()
|
Keys.onEscapePressed: {
|
||||||
|
clearReply();
|
||||||
|
closeAll();
|
||||||
|
}
|
||||||
|
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
if (event.key === Qt.Key_PageDown) {
|
if (event.key === Qt.Key_PageDown) {
|
||||||
@@ -397,7 +402,7 @@ ToolBar {
|
|||||||
autoAppeared = false;
|
autoAppeared = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
documentHandler.replaceAutoComplete(autoCompleteListView.currentItem.displayText)
|
inputField.autoComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
@@ -433,11 +438,31 @@ ToolBar {
|
|||||||
autoCompleteEndPosition = cursorPosition
|
autoCompleteEndPosition = cursorPosition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// store each user we autoComplete here, this will be helpful later to generate
|
||||||
|
// the matrix.to links.
|
||||||
|
// This use an hack to define: https://doc.qt.io/qt-5/qml-var.html#property-value-initialization-semantics
|
||||||
|
property var userAutocompleted: ({})
|
||||||
|
|
||||||
|
|
||||||
function postMessage() {
|
function postMessage() {
|
||||||
documentHandler.postMessage(inputField.text, attachmentPath, replyEventID);
|
// Qt wraps lines so we need to use a small hack
|
||||||
|
// to remove the wrapped lines but not break the empty
|
||||||
|
// lines.
|
||||||
|
documentHandler.postMessage(inputField.text.trim(), attachmentPath, replyEventID,
|
||||||
|
inputField.userAutocompleted);
|
||||||
clearAttachment();
|
clearAttachment();
|
||||||
currentRoom.markAllMessagesAsRead();
|
currentRoom.markAllMessagesAsRead();
|
||||||
clear();
|
clear();
|
||||||
|
text = Qt.binding(function() {
|
||||||
|
return currentRoom != null ? currentRoom.cachedInput : "";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function autoComplete() {
|
||||||
|
documentHandler.replaceAutoComplete(autoCompleteListView.currentItem.displayText)
|
||||||
|
if (!autoCompleteListView.currentItem.isEmoji) {
|
||||||
|
inputField.userAutocompleted[autoCompleteListView.currentItem.displayText] = autoCompleteListView.currentItem.userId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,6 +552,7 @@ ToolBar {
|
|||||||
|
|
||||||
function clear() {
|
function clear() {
|
||||||
inputField.clear()
|
inputField.clear()
|
||||||
|
inputField.userAutocompleted = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearReply() {
|
function clearReply() {
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ RowLayout {
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
openOnFinished = true
|
openOnFinished = true
|
||||||
currentRoom.downloadFile(eventId, Platform.StandardPaths.writableLocation(Platform.StandardPaths.CacheLocation) + "/" + eventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(eventId))
|
currentRoom.downloadFile(eventId, StandardPaths.writableLocation(StandardPaths.CacheLocation) + "/" + eventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(eventId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import QtQuick 2.12
|
|||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
import Qt.labs.platform 1.0 as Platform
|
import Qt.labs.platform 1.0
|
||||||
|
|
||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
import NeoChat.Setting 1.0
|
import NeoChat.Setting 1.0
|
||||||
@@ -143,15 +143,21 @@ Image {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function saveFileAs() {
|
function saveFileAs() {
|
||||||
var folderDialog = openFolderDialog.createObject(ApplicationWindow.overlay)
|
var dialog = fileDialog.createObject(ApplicationWindow.overlay)
|
||||||
|
dialog.open()
|
||||||
|
dialog.currentFile = dialog.folder + "/" + currentRoom.fileNameToDownload(eventId)
|
||||||
|
}
|
||||||
|
|
||||||
folderDialog.chosen.connect(function(path) {
|
Component {
|
||||||
if (!path) return
|
id: fileDialog
|
||||||
|
|
||||||
currentRoom.downloadFile(eventId, path + "/" + currentRoom.fileNameToDownload(eventId))
|
FileDialog {
|
||||||
})
|
fileMode: FileDialog.SaveFile
|
||||||
|
folder: StandardPaths.writableLocation(StandardPaths.DownloadLocation)
|
||||||
folderDialog.open()
|
onAccepted: {
|
||||||
|
currentRoom.downloadFile(eventId, file)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadAndOpen()
|
function downloadAndOpen()
|
||||||
@@ -160,7 +166,7 @@ Image {
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
openOnFinished = true
|
openOnFinished = true
|
||||||
currentRoom.downloadFile(eventId, Platform.StandardPaths.writableLocation(Platform.StandardPaths.CacheLocation) + "/" + eventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(eventId))
|
currentRoom.downloadFile(eventId, StandardPaths.writableLocation(StandardPaths.CacheLocation) + "/" + eventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(eventId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ RowLayout {
|
|||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
|
|
||||||
visible: showAuthor && Config.showAvatarInTimeline
|
visible: showAuthor && Config.showAvatarInTimeline
|
||||||
name: author.displayName
|
name: author.name
|
||||||
source: author.avatarMediaId ? "image://mxc/" + author.avatarMediaId : ""
|
source: author.avatarMediaId ? ("image://mxc/" + author.avatarMediaId) : ""
|
||||||
color: author.color
|
color: author.color
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
@@ -64,6 +64,7 @@ RowLayout {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: userDetailDialog.createObject(QQC2.ApplicationWindow.overlay, {"room": currentRoom, "user": author.object, "displayName": author.displayName, "avatarMediaId": author.avatarMediaId, "avatarUrl": author.avatarUrl}).open()
|
onClicked: userDetailDialog.createObject(QQC2.ApplicationWindow.overlay, {"room": currentRoom, "user": author.object, "displayName": author.displayName, "avatarMediaId": author.avatarMediaId, "avatarUrl": author.avatarUrl}).open()
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ QQC2.AbstractButton {
|
|||||||
Layout.preferredHeight: Kirigami.Units.gridUnit
|
Layout.preferredHeight: Kirigami.Units.gridUnit
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
visible: Config.showAvatarInTimeline
|
visible: Config.showAvatarInTimeline
|
||||||
source: replyVisible && reply.author.avatarMediaId ? "image://mxc/" + reply.author.avatarMediaId : ""
|
source: replyVisible && reply.author.avatarMediaId ? ("image://mxc/" + reply.author.avatarMediaId) : ""
|
||||||
name: replyVisible ? reply.author.displayName : "H"
|
name: replyVisible ? reply.author.name : "H"
|
||||||
color: replyVisible ? reply.author.color : Kirigami.Theme.highlightColor
|
color: replyVisible ? reply.author.color : Kirigami.Theme.highlightColor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ RowLayout {
|
|||||||
Layout.preferredWidth: Kirigami.Units.iconSizes.small
|
Layout.preferredWidth: Kirigami.Units.iconSizes.small
|
||||||
Layout.preferredHeight: Kirigami.Units.iconSizes.small
|
Layout.preferredHeight: Kirigami.Units.iconSizes.small
|
||||||
|
|
||||||
name: author.displayName
|
name: author.name
|
||||||
source: author.avatarMediaId ? "image://mxc/" + author.avatarMediaId : ""
|
source: author.avatarMediaId ? ("image://mxc/" + author.avatarMediaId) : ""
|
||||||
color: author.color
|
color: author.color
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ TextEdit {
|
|||||||
|
|
||||||
property bool isEmote: false
|
property bool isEmote: false
|
||||||
|
|
||||||
text: "<style>pre {white-space: pre-wrap} a{color: " + Kirigami.Theme.linkColor + ";} .user-pill{}</style>" + (isEmote ? "* <a href='https://matrix.to/#/" + author.id + "' style='color: " + author.color + "'>" + author.displayName + "</a> " : "") + display
|
text: "<style>pre {white-space: pre-wrap} a{color: " + Kirigami.Theme.linkColor + ";} .user-pill{}</style>" + (isEmote ? "* <a href='https://matrix.to/#/" + author.id + "' style='color: " + author.color + "'>" + author.displayName + "</a> " : "") + display + (isEdited ? (" <span style=\"color: " + Kirigami.Theme.disabledTextColor + "\">" + i18n("(edited)") + "</span>") : "")
|
||||||
|
|
||||||
|
|
||||||
color: Kirigami.Theme.textColor
|
color: Kirigami.Theme.textColor
|
||||||
font.pointSize: isEmoji.test(display) ? Kirigami.Theme.defaultFont.pointSize * 4 : Kirigami.Theme.defaultFont.pointSize
|
font.pointSize: isEmoji.test(display) ? Kirigami.Theme.defaultFont.pointSize * 4 : Kirigami.Theme.defaultFont.pointSize
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ Kirigami.OverlaySheet {
|
|||||||
Layout.preferredHeight: 72
|
Layout.preferredHeight: 72
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
|
|
||||||
name: room.displayName
|
name: room.name
|
||||||
source: room.avatarMediaId ? "image://mxc/" + room.avatarMediaId : ""
|
source: room.avatarMediaId ? ("image://mxc/" + room.avatarMediaId) : ""
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -74,7 +74,7 @@ Kirigami.OverlaySheet {
|
|||||||
enabled: canChangeName
|
enabled: canChangeName
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
TextArea {
|
||||||
id: roomTopicField
|
id: roomTopicField
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: room.topic
|
text: room.topic
|
||||||
|
|||||||
@@ -29,7 +29,11 @@ Kirigami.OverlaySheet {
|
|||||||
topPadding: 0
|
topPadding: 0
|
||||||
|
|
||||||
header: Kirigami.Heading {
|
header: Kirigami.Heading {
|
||||||
|
id: heading
|
||||||
text: i18nc("Account detail dialog", "Account detail - %1", displayName)
|
text: i18nc("Account detail dialog", "Account detail - %1", displayName)
|
||||||
|
elide: Text.ElideRight
|
||||||
|
QQC2.ToolTip.visible: truncated && hovered
|
||||||
|
QQC2.ToolTip.text: text
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
@@ -47,7 +51,7 @@ Kirigami.OverlaySheet {
|
|||||||
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
|
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
|
||||||
|
|
||||||
name: displayName
|
name: displayName
|
||||||
source: avatarMediaId ? "image://mxc/" + avatarMediaId : ""
|
source: avatarMediaId ? ("image://mxc/" + avatarMediaId) : ""
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ Kirigami.OverlaySheet {
|
|||||||
spacing: Kirigami.Units.largeSpacing
|
spacing: Kirigami.Units.largeSpacing
|
||||||
Kirigami.Avatar {
|
Kirigami.Avatar {
|
||||||
id: avatar
|
id: avatar
|
||||||
source: author.avatarMediaId ? "image://mxc/" + author.avatarMediaId : ""
|
source: author.avatarMediaId ? ("image://mxc/" + author.avatarMediaId) : ""
|
||||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 3
|
Layout.preferredWidth: Kirigami.Units.gridUnit * 3
|
||||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 3
|
Layout.preferredHeight: Kirigami.Units.gridUnit * 3
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
@@ -55,6 +55,18 @@ Kirigami.OverlaySheet {
|
|||||||
text: message
|
text: message
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
|
|
||||||
|
onLinkActivated: {
|
||||||
|
if (link.startsWith("https://matrix.to/")) {
|
||||||
|
var result = link.replace(/\?.*/, "").match("https://matrix.to/#/(!.*:.*)/(\\$.*:.*)")
|
||||||
|
if (!result || result.length < 3) return
|
||||||
|
if (result[1] != currentRoom.id) return
|
||||||
|
if (!result[2]) return
|
||||||
|
goToEvent(result[2])
|
||||||
|
} else {
|
||||||
|
Qt.openUrlExternally(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ Kirigami.ScrollablePage {
|
|||||||
Layout.preferredWidth: height
|
Layout.preferredWidth: height
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|
||||||
source: avatar ? "image://mxc/" + avatar : ""
|
source: avatar ? ("image://mxc/" + avatar) : ""
|
||||||
name: name
|
name: name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import org.kde.kirigami 2.13 as Kirigami
|
|||||||
|
|
||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
import NeoChat.Component 1.0
|
import NeoChat.Component 1.0
|
||||||
import NeoChat.Effect 1.0
|
|
||||||
import NeoChat.Setting 1.0
|
import NeoChat.Setting 1.0
|
||||||
|
|
||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
@@ -117,7 +116,7 @@ Kirigami.ScrollablePage {
|
|||||||
Layout.preferredWidth: Kirigami.Units.iconSizes.huge
|
Layout.preferredWidth: Kirigami.Units.iconSizes.huge
|
||||||
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
|
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
|
||||||
|
|
||||||
source: model.avatar ? "image://mxc/" + model.avatar : ""
|
source: model.avatar ? ("image://mxc/" + model.avatar) : ""
|
||||||
name: name
|
name: name
|
||||||
}
|
}
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ Kirigami.ScrollablePage {
|
|||||||
id: roomListItem
|
id: roomListItem
|
||||||
property bool itemVisible: model.categoryVisible || sortFilterRoomListModel.filterText.length > 0 || Config.mergeRoomList
|
property bool itemVisible: model.categoryVisible || sortFilterRoomListModel.filterText.length > 0 || Config.mergeRoomList
|
||||||
visible: itemVisible
|
visible: itemVisible
|
||||||
highlighted: roomManager.currentRoom && roomManager.currentRoom.name === name
|
highlighted: roomManager.currentRoom && roomManager.currentRoom.displayName === displayName
|
||||||
focus: true
|
focus: true
|
||||||
action: Kirigami.Action {
|
action: Kirigami.Action {
|
||||||
id: enterRoomAction
|
id: enterRoomAction
|
||||||
@@ -134,7 +134,7 @@ Kirigami.ScrollablePage {
|
|||||||
Layout.minimumWidth: size
|
Layout.minimumWidth: size
|
||||||
Layout.maximumWidth: size
|
Layout.maximumWidth: size
|
||||||
|
|
||||||
source: avatar ? "image://mxc/" + avatar : ""
|
source: avatar ? ("image://mxc/" + avatar) : ""
|
||||||
name: model.name || i18n("No Name")
|
name: model.name || i18n("No Name")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +153,7 @@ Kirigami.ScrollablePage {
|
|||||||
QQC2.Label {
|
QQC2.Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
text: name ?? ""
|
text: displayName ?? ""
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
font.bold: unreadCount >= 0 || highlightCount > 0 || notificationCount > 0
|
font.bold: unreadCount >= 0 || highlightCount > 0 || notificationCount > 0
|
||||||
wrapMode: Text.NoWrap
|
wrapMode: Text.NoWrap
|
||||||
|
|||||||
@@ -28,30 +28,55 @@ Kirigami.ScrollablePage {
|
|||||||
signal switchRoomUp()
|
signal switchRoomUp()
|
||||||
signal switchRoomDown()
|
signal switchRoomDown()
|
||||||
|
|
||||||
title: currentRoom.name
|
Connections {
|
||||||
|
target: roomManager.actionsHandler
|
||||||
|
onShowMessage: {
|
||||||
|
page.header.contentItem.text = message;
|
||||||
|
page.header.contentItem.type = messageType === ActionsHandler.Error ? Kirigami.MessageType.Error : Kirigami.MessageType.Information;
|
||||||
|
page.header.contentItem.visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onHideMessage: page.header.contentItem.visible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
header: QQC2.Control {
|
||||||
|
height: visible ? implicitHeight : 0
|
||||||
|
visible: contentItem.visible
|
||||||
|
padding: Kirigami.Units.smallSpacing
|
||||||
|
contentItem: Kirigami.InlineMessage {
|
||||||
|
showCloseButton: true
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
title: currentRoom.displayName
|
||||||
|
|
||||||
titleDelegate: Component {
|
titleDelegate: Component {
|
||||||
RowLayout {
|
RowLayout {
|
||||||
visible: !Kirigami.Settings.isMobile
|
visible: !Kirigami.Settings.isMobile
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.maximumWidth: implicitWidth + 1 // The +1 is to make sure we do not trigger eliding at max width
|
Layout.maximumWidth: implicitWidth + 1 // The +1 is to make sure we do not trigger eliding at max width
|
||||||
Layout.minimumWidth: 0
|
Layout.minimumWidth: 0
|
||||||
|
spacing: Kirigami.Units.gridUnit * 0.8
|
||||||
Kirigami.Heading {
|
Kirigami.Heading {
|
||||||
level: 1
|
id: titleLabel
|
||||||
|
level: 2
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
text: page.title
|
text: page.title
|
||||||
opacity: page.isCurrentPage ? 1 : 0.4
|
opacity: page.isCurrentPage ? 1 : 0.4
|
||||||
maximumLineCount: 1
|
maximumLineCount: 1
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
|
||||||
Layout.alignment: Qt.AlignBottom
|
|
||||||
}
|
}
|
||||||
QQC2.Label {
|
QQC2.Label {
|
||||||
Layout.alignment: Qt.AlignBottom
|
|
||||||
text: currentRoom.topic
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
maximumLineCount: 1
|
Layout.alignment: Qt.AlignVCenter
|
||||||
//wrapMode: Text.WordWrap
|
anchors.baseline: lineCount < 2 ? titleLabel.baseline : undefined // necessary, since there is no way to do this with Layout.alignment
|
||||||
|
text: currentRoom.topic
|
||||||
|
maximumLineCount: 2
|
||||||
|
wrapMode: Text.Wrap
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
color: Kirigami.Theme.disabledTextColor
|
color: Kirigami.Theme.disabledTextColor
|
||||||
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.9
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,10 +95,10 @@ Kirigami.ScrollablePage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
if (event.key === Qt.Key_PageDown) {
|
if (event.key === Qt.Key_PageDown && (event.modifiers & Qt.ControlModifier)) {
|
||||||
switchRoomDown();
|
|
||||||
} else if (event.key === Qt.Key_PageUp) {
|
|
||||||
switchRoomUp();
|
switchRoomUp();
|
||||||
|
} else if (event.key === Qt.Key_PageUp && (event.modifiers & Qt.ControlModifier)) {
|
||||||
|
switchRoomDown();
|
||||||
} else if (!(event.modifiers & Qt.ControlModifier) && event.key < Qt.Key_Escape) {
|
} else if (!(event.modifiers & Qt.ControlModifier) && event.key < Qt.Key_Escape) {
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
chatTextInput.addText(event.text);
|
chatTextInput.addText(event.text);
|
||||||
@@ -121,6 +146,14 @@ Kirigami.ScrollablePage {
|
|||||||
room: currentRoom
|
room: currentRoom
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Kirigami.PlaceholderMessage {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: messageListView.count === 0 && !currentRoom.allHistoryLoaded
|
||||||
|
QQC2.BusyIndicator {
|
||||||
|
running: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QQC2.Popup {
|
QQC2.Popup {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
||||||
@@ -187,12 +220,14 @@ Kirigami.ScrollablePage {
|
|||||||
filterRowCallback: Config.showLeaveJoinEvent ? dontFilterLeaveJoin : filterLeaveJoin
|
filterRowCallback: Config.showLeaveJoinEvent ? dontFilterLeaveJoin : filterLeaveJoin
|
||||||
|
|
||||||
function dontFilterLeaveJoin(row, parent) {
|
function dontFilterLeaveJoin(row, parent) {
|
||||||
return messageEventModel.data(messageEventModel.index(row, 0), MessageEventModel.MessageRole) !== 0x10
|
return messageEventModel.data(messageEventModel.index(row, 0), MessageEventModel.SpecialMarksRole) !== EventStatus.Hidden
|
||||||
|
&& messageEventModel.data(messageEventModel.index(row, 0), MessageEventModel.MessageRole) !== 0x10
|
||||||
&& messageEventModel.data(messageEventModel.index(row, 0), MessageEventModel.EventTypeRole) !== "other";
|
&& messageEventModel.data(messageEventModel.index(row, 0), MessageEventModel.EventTypeRole) !== "other";
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterLeaveJoin(row, parent) {
|
function filterLeaveJoin(row, parent) {
|
||||||
return messageEventModel.data(messageEventModel.index(row, 0), MessageEventModel.MessageRole) !== 0x10
|
return messageEventModel.data(messageEventModel.index(row, 0), MessageEventModel.SpecialMarksRole) !== EventStatus.Hidden
|
||||||
|
&& messageEventModel.data(messageEventModel.index(row, 0), MessageEventModel.MessageRole) !== 0x10
|
||||||
&& messageEventModel.data(messageEventModel.index(row, 0), MessageEventModel.EventTypeRole) !== "other"
|
&& messageEventModel.data(messageEventModel.index(row, 0), MessageEventModel.EventTypeRole) !== "other"
|
||||||
&& messageEventModel.data(messageEventModel.index(row, 0), MessageEventModel.EventTypeRole) !== "state";
|
&& messageEventModel.data(messageEventModel.index(row, 0), MessageEventModel.EventTypeRole) !== "state";
|
||||||
}
|
}
|
||||||
@@ -285,19 +320,19 @@ Kirigami.ScrollablePage {
|
|||||||
innerObject: MessageDelegate {
|
innerObject: MessageDelegate {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.maximumWidth: messageListView.width
|
Layout.maximumWidth: messageListView.width
|
||||||
mouseArea: MouseArea {
|
|
||||||
acceptedButtons: (Kirigami.Settings.isMobile ? Qt.LeftButton : 0) | Qt.RightButton
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
if (mouse.button == Qt.RightButton) {
|
|
||||||
openMessageContext(author, display, eventId, toolTip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onPressAndHold: openMessageContext(author, display, eventId, toolTip);
|
|
||||||
}
|
|
||||||
onReplyClicked: goToEvent(eventID)
|
onReplyClicked: goToEvent(eventID)
|
||||||
onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId);
|
onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId);
|
||||||
innerObject: [
|
innerObject: [
|
||||||
|
MouseArea {
|
||||||
|
acceptedButtons: (Kirigami.Settings.isMobile ? Qt.LeftButton : 0) | Qt.RightButton
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
if (mouse.button == Qt.RightButton) {
|
||||||
|
openMessageContext(author, display, eventId, toolTip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onPressAndHold: openMessageContext(author, display, eventId, toolTip);
|
||||||
|
},
|
||||||
TextDelegate {
|
TextDelegate {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||||
|
|||||||
@@ -22,29 +22,44 @@ Kirigami.ScrollablePage {
|
|||||||
Kirigami.FormData.label: i18n("Notifications and events:")
|
Kirigami.FormData.label: i18n("Notifications and events:")
|
||||||
text: i18n("Show notifications")
|
text: i18n("Show notifications")
|
||||||
checked: Config.showNotifications
|
checked: Config.showNotifications
|
||||||
onToggled: Config.showNotifications = checked
|
onToggled: {
|
||||||
|
Config.showNotifications = checked
|
||||||
|
Config.save()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
QQC2.CheckBox {
|
QQC2.CheckBox {
|
||||||
text: i18n("Show leave and join events")
|
text: i18n("Show leave and join events")
|
||||||
checked: Config.showLeaveJoinEvent
|
checked: Config.showLeaveJoinEvent
|
||||||
onToggled: Config.showLeaveJoinEvent = checked
|
onToggled: {
|
||||||
|
Config.showLeaveJoinEvent = checked
|
||||||
|
Config.save()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
QQC2.RadioButton {
|
QQC2.RadioButton {
|
||||||
Kirigami.FormData.label: i18n("Rooms and private chats:")
|
Kirigami.FormData.label: i18n("Rooms and private chats:")
|
||||||
text: i18n("Separated")
|
text: i18n("Separated")
|
||||||
checked: !Config.mergeRoomList
|
checked: !Config.mergeRoomList
|
||||||
onToggled: Config.mergeRoomList = false
|
onToggled: {
|
||||||
|
Config.mergeRoomList = false
|
||||||
|
Config.save()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
QQC2.RadioButton {
|
QQC2.RadioButton {
|
||||||
text: i18n("Intermixed")
|
text: i18n("Intermixed")
|
||||||
checked: Config.mergeRoomList
|
checked: Config.mergeRoomList
|
||||||
onToggled: Config.mergeRoomList = true
|
onToggled: {
|
||||||
|
Config.mergeRoomList = true
|
||||||
|
Config.save()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
QQC2.CheckBox {
|
QQC2.CheckBox {
|
||||||
Kirigami.FormData.label: i18n("Timeline:")
|
Kirigami.FormData.label: i18n("Timeline:")
|
||||||
text: i18n("Show User Avatar")
|
text: i18n("Show User Avatar")
|
||||||
checked: Config.showAvatarInTimeline
|
checked: Config.showAvatarInTimeline
|
||||||
onToggled: Config.showAvatarInTimeline = checked
|
onToggled: {
|
||||||
|
Config.showAvatarInTimeline = checked
|
||||||
|
Config.save()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import QtQuick.Layouts 1.12
|
|||||||
import org.kde.kirigami 2.14 as Kirigami
|
import org.kde.kirigami 2.14 as Kirigami
|
||||||
|
|
||||||
import NeoChat.Component 1.0
|
import NeoChat.Component 1.0
|
||||||
import NeoChat.Effect 1.0
|
|
||||||
import NeoChat.Setting 1.0
|
import NeoChat.Setting 1.0
|
||||||
|
|
||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
|
|||||||
@@ -110,8 +110,8 @@ Kirigami.OverlayDrawer {
|
|||||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 3.5
|
Layout.preferredWidth: Kirigami.Units.gridUnit * 3.5
|
||||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 3.5
|
Layout.preferredHeight: Kirigami.Units.gridUnit * 3.5
|
||||||
|
|
||||||
name: room ? room.displayName : i18n("No name")
|
name: room ? room.name : i18n("No name")
|
||||||
source: room ? "image://mxc/" + room.avatarMediaId : undefined
|
source: room ? ("image://mxc/" + room.avatarMediaId) : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
@@ -190,7 +190,7 @@ Kirigami.OverlayDrawer {
|
|||||||
Layout.preferredWidth: height
|
Layout.preferredWidth: height
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|
||||||
source: "image://mxc/" + avatar
|
source: avatar ? ("image://mxc/" + avatar) : ""
|
||||||
name: name
|
name: name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,74 +1,95 @@
|
|||||||
[Global]
|
[Global]
|
||||||
IconName=neochat
|
IconName=org.kde.neochat
|
||||||
Name=Neochat
|
Name=Neochat
|
||||||
Name[ca]=Neochat
|
Name[ca]=Neochat
|
||||||
Name[ca@valencia]=Neochat
|
Name[ca@valencia]=Neochat
|
||||||
Name[da]=Neochat
|
Name[da]=Neochat
|
||||||
|
Name[de]=Neochat
|
||||||
Name[en_GB]=Neochat
|
Name[en_GB]=Neochat
|
||||||
Name[es]=Neochat
|
Name[es]=Neochat
|
||||||
Name[eu]=Neochat
|
Name[eu]=Neochat
|
||||||
|
Name[fi]=Neochat
|
||||||
Name[fr]=Neochat
|
Name[fr]=Neochat
|
||||||
Name[hu]=Neochat
|
Name[hu]=Neochat
|
||||||
Name[it]=Neochat
|
Name[it]=Neochat
|
||||||
Name[nl]=Neochat
|
Name[nl]=Neochat
|
||||||
Name[nn]=Neochat
|
Name[nn]=Neochat
|
||||||
|
Name[pl]=Neochat
|
||||||
Name[pt]=Neochat
|
Name[pt]=Neochat
|
||||||
|
Name[pt_BR]=Neochat
|
||||||
Name[sk]=Neochat
|
Name[sk]=Neochat
|
||||||
Name[sl]=Neochat
|
Name[sl]=Neochat
|
||||||
Name[sv]=Neochat
|
Name[sv]=Neochat
|
||||||
Name[uk]=Neochat
|
Name[uk]=Neochat
|
||||||
Name[x-test]=xxNeochatxx
|
Name[x-test]=xxNeochatxx
|
||||||
|
Name[zh_CN]=Neochat
|
||||||
DesktopEntry=org.kde.neochat
|
DesktopEntry=org.kde.neochat
|
||||||
Comment=IM client for the Matrix protocol
|
Comment=IM client for the Matrix protocol
|
||||||
Comment[ca]=Client de MI per al protocol Matrix
|
Comment[ca]=Client de MI per al protocol Matrix
|
||||||
Comment[ca@valencia]=Client de MI per al protocol Matrix
|
Comment[ca@valencia]=Client de MI per al protocol Matrix
|
||||||
|
Comment[de]=IM-Programm für das Matrix-Protokoll
|
||||||
Comment[en_GB]=IM client for the Matrix protocol
|
Comment[en_GB]=IM client for the Matrix protocol
|
||||||
Comment[es]=Cliente de MI para el protocolo Matrix
|
Comment[es]=Cliente de MI para el protocolo Matrix
|
||||||
Comment[eu]=Matrix protokolorako bat-bateko mezularitza bezeroa
|
Comment[eu]=Matrix protokolorako bat-bateko mezularitza bezeroa
|
||||||
|
Comment[fi]=Pikaviestiasiakas Matrix-yhteyskäytännölle
|
||||||
Comment[fr]=Client « IM » pour le protocole « Matrix »
|
Comment[fr]=Client « IM » pour le protocole « Matrix »
|
||||||
Comment[hu]=Azonnali üzenetküldő kliens a Matrix protokollhoz
|
Comment[hu]=Azonnali üzenetküldő kliens a Matrix protokollhoz
|
||||||
Comment[it]=Client di messaggistica istantanea per il protocollo Matrix
|
Comment[it]=Client di messaggistica istantanea per il protocollo Matrix
|
||||||
Comment[nl]=IM-client voor het Matrix-protocol
|
Comment[nl]=IM-client voor het Matrix-protocol
|
||||||
Comment[nn]=Lynmeldingsklient for Matrix-protokollen
|
Comment[nn]=Lynmeldingsklient for Matrix-protokollen
|
||||||
|
Comment[pl]=Komunikator internetowy dla protokołu Matrix
|
||||||
Comment[pt]=Cliente de MI para o protocolo Matrix
|
Comment[pt]=Cliente de MI para o protocolo Matrix
|
||||||
|
Comment[pt_BR]=Cliente de mensageiro instantâneo para o protocolo Matrix
|
||||||
|
Comment[sk]=IM klient pre protokol Matrix
|
||||||
Comment[sl]=Odjemalec neposrednega sporočanja po protokolu Matrix
|
Comment[sl]=Odjemalec neposrednega sporočanja po protokolu Matrix
|
||||||
Comment[sv]=Direktmeddelandeklient för protokollet Matrix
|
Comment[sv]=Direktmeddelandeklient för protokollet Matrix
|
||||||
Comment[uk]=Клієнт служби миттєвого обміну повідомленнями для протоколу Matrix
|
Comment[uk]=Клієнт служби миттєвого обміну повідомленнями для протоколу Matrix
|
||||||
Comment[x-test]=xxIM client for the Matrix protocolxx
|
Comment[x-test]=xxIM client for the Matrix protocolxx
|
||||||
|
Comment[zh_CN]=为 Matrix 协议打造的 IM 客户端
|
||||||
|
|
||||||
[Event/message]
|
[Event/message]
|
||||||
Name=New message
|
Name=New message
|
||||||
Name[ca]=Missatge nou
|
Name[ca]=Missatge nou
|
||||||
Name[ca@valencia]=Missatge nou
|
Name[ca@valencia]=Missatge nou
|
||||||
|
Name[de]=Neue Nachricht
|
||||||
Name[en_GB]=New message
|
Name[en_GB]=New message
|
||||||
Name[es]=Nuevo mensaje
|
Name[es]=Nuevo mensaje
|
||||||
Name[eu]=Mezu berria
|
Name[eu]=Mezu berria
|
||||||
|
Name[fi]=Uusi viesti
|
||||||
Name[fr]=Nouveau message
|
Name[fr]=Nouveau message
|
||||||
Name[hu]=Új üzenet
|
Name[hu]=Új üzenet
|
||||||
Name[it]=Nuovo messaggio
|
Name[it]=Nuovo messaggio
|
||||||
Name[nl]=Nieuw bericht
|
Name[nl]=Nieuw bericht
|
||||||
Name[nn]=Ny melding
|
Name[nn]=Ny melding
|
||||||
|
Name[pl]=Nowa wiadomość
|
||||||
Name[pt]=Nova mensagem
|
Name[pt]=Nova mensagem
|
||||||
|
Name[pt_BR]=Nova mensagem
|
||||||
Name[sk]=Nová správa
|
Name[sk]=Nová správa
|
||||||
Name[sl]=Novo sporočilo
|
Name[sl]=Novo sporočilo
|
||||||
Name[sv]=Nytt meddelande
|
Name[sv]=Nytt meddelande
|
||||||
Name[uk]=Нове повідомлення
|
Name[uk]=Нове повідомлення
|
||||||
Name[x-test]=xxNew messagexx
|
Name[x-test]=xxNew messagexx
|
||||||
|
Name[zh_CN]=新消息
|
||||||
Comment=There is a new message
|
Comment=There is a new message
|
||||||
Comment[ca]=Hi ha un missatge nou
|
Comment[ca]=Hi ha un missatge nou
|
||||||
Comment[ca@valencia]=Hi ha un missatge nou
|
Comment[ca@valencia]=Hi ha un missatge nou
|
||||||
|
Comment[de]=Es ist eine neue Nachricht vorhanden
|
||||||
Comment[en_GB]=There is a new message
|
Comment[en_GB]=There is a new message
|
||||||
Comment[es]=Hay un mensaje nuevo
|
Comment[es]=Hay un mensaje nuevo
|
||||||
Comment[eu]=Mezu berri bat dago
|
Comment[eu]=Mezu berri bat dago
|
||||||
|
Comment[fi]=Saapui uusi viesti
|
||||||
Comment[fr]=Il y a un nouveau message
|
Comment[fr]=Il y a un nouveau message
|
||||||
Comment[hu]=Új üzenet érkezett
|
Comment[hu]=Új üzenet érkezett
|
||||||
Comment[it]=È presente un nuovo messaggio
|
Comment[it]=È presente un nuovo messaggio
|
||||||
Comment[nl]=Er is een nieuw bericht
|
Comment[nl]=Er is een nieuw bericht
|
||||||
Comment[nn]=Du har ei ny melding
|
Comment[nn]=Du har ei ny melding
|
||||||
|
Comment[pl]=Dostępna jest nowa wiadomość
|
||||||
Comment[pt]=Tem uma mensagem nova
|
Comment[pt]=Tem uma mensagem nova
|
||||||
|
Comment[pt_BR]=Existe uma nova mensagem
|
||||||
Comment[sk]=Je nová správa
|
Comment[sk]=Je nová správa
|
||||||
Comment[sl]=Prišlo je novo sporočilo
|
Comment[sl]=Prišlo je novo sporočilo
|
||||||
Comment[sv]=Det finns ett nytt meddelande
|
Comment[sv]=Det finns ett nytt meddelande
|
||||||
Comment[uk]=Надійшло нове повідомлення
|
Comment[uk]=Надійшло нове повідомлення
|
||||||
Comment[x-test]=xxThere is a new messagexx
|
Comment[x-test]=xxThere is a new messagexx
|
||||||
|
Comment[zh_CN]=有新消息
|
||||||
Action=Popup
|
Action=Popup
|
||||||
|
|||||||
@@ -8,15 +8,20 @@
|
|||||||
<name xml:lang="ca">Neochat</name>
|
<name xml:lang="ca">Neochat</name>
|
||||||
<name xml:lang="ca-valencia">Neochat</name>
|
<name xml:lang="ca-valencia">Neochat</name>
|
||||||
<name xml:lang="da">Neochat</name>
|
<name xml:lang="da">Neochat</name>
|
||||||
|
<name xml:lang="de">Neochat</name>
|
||||||
<name xml:lang="en-GB">Neochat</name>
|
<name xml:lang="en-GB">Neochat</name>
|
||||||
<name xml:lang="es">Neochat</name>
|
<name xml:lang="es">Neochat</name>
|
||||||
<name xml:lang="eu">Neochat</name>
|
<name xml:lang="eu">Neochat</name>
|
||||||
|
<name xml:lang="fi">Neochat</name>
|
||||||
<name xml:lang="fr">Neochat</name>
|
<name xml:lang="fr">Neochat</name>
|
||||||
<name xml:lang="hu">Neochat</name>
|
<name xml:lang="hu">Neochat</name>
|
||||||
|
<name xml:lang="id">Neochat</name>
|
||||||
<name xml:lang="it">Neochat</name>
|
<name xml:lang="it">Neochat</name>
|
||||||
<name xml:lang="nl">Neochat</name>
|
<name xml:lang="nl">Neochat</name>
|
||||||
<name xml:lang="nn">Neochat</name>
|
<name xml:lang="nn">Neochat</name>
|
||||||
|
<name xml:lang="pl">Neochat</name>
|
||||||
<name xml:lang="pt">Neochat</name>
|
<name xml:lang="pt">Neochat</name>
|
||||||
|
<name xml:lang="pt-BR">Neochat</name>
|
||||||
<name xml:lang="sk">Neochat</name>
|
<name xml:lang="sk">Neochat</name>
|
||||||
<name xml:lang="sl">Neochat</name>
|
<name xml:lang="sl">Neochat</name>
|
||||||
<name xml:lang="sv">Neochat</name>
|
<name xml:lang="sv">Neochat</name>
|
||||||
@@ -25,15 +30,21 @@
|
|||||||
<summary>A client for matrix, the decentralized communication protocol</summary>
|
<summary>A client for matrix, the decentralized communication protocol</summary>
|
||||||
<summary xml:lang="ca">Un client per al Matrix, el protocol de comunicacions descentralitzat</summary>
|
<summary xml:lang="ca">Un client per al Matrix, el protocol de comunicacions descentralitzat</summary>
|
||||||
<summary xml:lang="ca-valencia">Un client per al Matrix, el protocol de comunicacions descentralitzat</summary>
|
<summary xml:lang="ca-valencia">Un client per al Matrix, el protocol de comunicacions descentralitzat</summary>
|
||||||
|
<summary xml:lang="de">Ein Programm für Matrix, das dezentrale Kommunikationsprotokoll</summary>
|
||||||
<summary xml:lang="en-GB">A client for matrix, the decentralised communication protocol</summary>
|
<summary xml:lang="en-GB">A client for matrix, the decentralised communication protocol</summary>
|
||||||
<summary xml:lang="es">Un cliente para Matrix, el protocolo de comunicaciones descentralizado</summary>
|
<summary xml:lang="es">Un cliente para Matrix, el protocolo de comunicaciones descentralizado</summary>
|
||||||
<summary xml:lang="eu">Matrix, deszentralizatutako komunikazio protokolorako bezero bat</summary>
|
<summary xml:lang="eu">Matrix, deszentralizatutako komunikazio protokolorako bezero bat</summary>
|
||||||
|
<summary xml:lang="fi">Asiakas Matrixille, hajautetulle viestintäyhteyskäytännölle</summary>
|
||||||
<summary xml:lang="fr">Un client pour « Matrix », le protocole décentralisé de communications.</summary>
|
<summary xml:lang="fr">Un client pour « Matrix », le protocole décentralisé de communications.</summary>
|
||||||
<summary xml:lang="hu">Kliens a matrixhoz, a decentralizált kommunikációs protokollhoz</summary>
|
<summary xml:lang="hu">Kliens a matrixhoz, a decentralizált kommunikációs protokollhoz</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="it">Un client per matrix, il protocollo di comunicazione decentralizzato</summary>
|
||||||
<summary xml:lang="nl">Een client voor matrix, het gedecentraliseerde communicatieprotocol</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>
|
<summary xml:lang="nn">Ein klient for Matrix, den desentraliserte lynmeldingsprotokollen</summary>
|
||||||
|
<summary xml:lang="pl">Program do obsługi matriksa, rozproszonego protokołu porozumiewania się</summary>
|
||||||
<summary xml:lang="pt">Um cliente para o Matrix, o protocolo de comunicação descentralizado</summary>
|
<summary xml:lang="pt">Um cliente para o Matrix, o protocolo de comunicação descentralizado</summary>
|
||||||
|
<summary xml:lang="pt-BR">Um cliente do Matrix, o protocolo de comunicação descentralizado</summary>
|
||||||
|
<summary xml:lang="sk">Klient pre matrix, decentralizovaný komunikačný protokol</summary>
|
||||||
<summary xml:lang="sl">Odjemalec za matrix, decentralizirani komunikacijski protokol</summary>
|
<summary xml:lang="sl">Odjemalec za matrix, decentralizirani komunikacijski protokol</summary>
|
||||||
<summary xml:lang="sv">En klient för Matrix, det decentraliserade kommunikationsprotokollet</summary>
|
<summary xml:lang="sv">En klient för Matrix, det decentraliserade kommunikationsprotokollet</summary>
|
||||||
<summary xml:lang="uk">Клієнт matrix, децентралізованого протоколу обміну даними</summary>
|
<summary xml:lang="uk">Клієнт matrix, децентралізованого протоколу обміну даними</summary>
|
||||||
@@ -42,15 +53,21 @@
|
|||||||
<p>A client for matrix, the decentralized communication protocol.</p>
|
<p>A client for matrix, the decentralized communication protocol.</p>
|
||||||
<p xml:lang="ca">Un client per al Matrix, el protocol de comunicacions descentralitzat.</p>
|
<p xml:lang="ca">Un client per al Matrix, el protocol de comunicacions descentralitzat.</p>
|
||||||
<p xml:lang="ca-valencia">Un client per al Matrix, el protocol de comunicacions descentralitzat.</p>
|
<p xml:lang="ca-valencia">Un client per al Matrix, el protocol de comunicacions descentralitzat.</p>
|
||||||
|
<p xml:lang="de">Ein Programm für Matrix, das dezentrale Kommunikationsprotokoll.</p>
|
||||||
<p xml:lang="en-GB">A client for matrix, the decentralised communication protocol.</p>
|
<p xml:lang="en-GB">A client for matrix, the decentralised communication protocol.</p>
|
||||||
<p xml:lang="es">Un cliente para Matrix, el protocolo de comunicaciones descentralizado.</p>
|
<p xml:lang="es">Un cliente para Matrix, el protocolo de comunicaciones descentralizado.</p>
|
||||||
<p xml:lang="eu">Matrix, deszentralizatutako komunikazio protokolorako bezero bat.</p>
|
<p xml:lang="eu">Matrix, deszentralizatutako komunikazio protokolorako bezero bat.</p>
|
||||||
|
<p xml:lang="fi">Asiakas Matrixille, hajautetulle viestintäyhteyskäytännölle.</p>
|
||||||
<p xml:lang="fr">Un client « Matrix », le protocole décentralisé de communications.</p>
|
<p xml:lang="fr">Un client « Matrix », le protocole décentralisé de communications.</p>
|
||||||
<p xml:lang="hu">Kliens a matrixhoz, a decentralizált kommunikációs protokollhoz.</p>
|
<p xml:lang="hu">Kliens a matrixhoz, a decentralizált kommunikációs protokollhoz.</p>
|
||||||
|
<p xml:lang="id">Klien untuk matrix, protokol komunikasi terdesentralisasi.</p>
|
||||||
<p xml:lang="it">Un client per matrix, il protocollo di comunicazione decentralizzato.</p>
|
<p xml:lang="it">Un client per matrix, il protocollo di comunicazione decentralizzato.</p>
|
||||||
<p xml:lang="nl">Een client voor matrix, het gedecentraliseerde communicatieprotocol.</p>
|
<p xml:lang="nl">Een client voor matrix, het gedecentraliseerde communicatieprotocol.</p>
|
||||||
<p xml:lang="nn">Ein klient for Matrix, den desentraliserte lynmeldingsprotokollen.</p>
|
<p xml:lang="nn">Ein klient for Matrix, den desentraliserte lynmeldingsprotokollen.</p>
|
||||||
|
<p xml:lang="pl">Program do obsługi matriksa, rozproszonego protokołu porozumiewania się.</p>
|
||||||
<p xml:lang="pt">Um cliente para o Matrix, o protocolo de comunicação descentralizado.</p>
|
<p xml:lang="pt">Um cliente para o Matrix, o protocolo de comunicação descentralizado.</p>
|
||||||
|
<p xml:lang="pt-BR">Um cliente do Matrix, o protocolo de comunicação descentralizado.</p>
|
||||||
|
<p xml:lang="sk">Klient pre matrix, decentralizovaný komunikačný protokol.</p>
|
||||||
<p xml:lang="sl">Odjemalec za matrix, decentralizirani komunikacijski protokol.</p>
|
<p xml:lang="sl">Odjemalec za matrix, decentralizirani komunikacijski protokol.</p>
|
||||||
<p xml:lang="sv">En klient för Matrix, det decentraliserade kommunikationsprotokollet.</p>
|
<p xml:lang="sv">En klient för Matrix, det decentraliserade kommunikationsprotokollet.</p>
|
||||||
<p xml:lang="uk">Клієнт matrix, децентралізованого протоколу обміну даними.</p>
|
<p xml:lang="uk">Клієнт matrix, децентралізованого протоколу обміну даними.</p>
|
||||||
@@ -59,21 +76,25 @@
|
|||||||
<url type="homepage">https://kde.org</url>
|
<url type="homepage">https://kde.org</url>
|
||||||
<url type="bugtracker">https://bugs.kde.org</url>
|
<url type="bugtracker">https://bugs.kde.org</url>
|
||||||
<categories>
|
<categories>
|
||||||
<category>Matrix</category>
|
<category>Network</category>
|
||||||
<category>Internet</category>
|
|
||||||
</categories>
|
</categories>
|
||||||
<developer_name>The KDE Community</developer_name>
|
<developer_name>The KDE Community</developer_name>
|
||||||
<developer_name xml:lang="ca">La comunitat KDE</developer_name>
|
<developer_name xml:lang="ca">La comunitat KDE</developer_name>
|
||||||
<developer_name xml:lang="ca-valencia">La comunitat KDE</developer_name>
|
<developer_name xml:lang="ca-valencia">La comunitat KDE</developer_name>
|
||||||
|
<developer_name xml:lang="de">Die KDE-Gemeinschaft</developer_name>
|
||||||
<developer_name xml:lang="en-GB">The KDE Community</developer_name>
|
<developer_name xml:lang="en-GB">The KDE Community</developer_name>
|
||||||
<developer_name xml:lang="es">La comunidad KDE</developer_name>
|
<developer_name xml:lang="es">La comunidad KDE</developer_name>
|
||||||
<developer_name xml:lang="eu">KDE komunitatea</developer_name>
|
<developer_name xml:lang="eu">KDE komunitatea</developer_name>
|
||||||
|
<developer_name xml:lang="fi">KDE-yhteisö</developer_name>
|
||||||
<developer_name xml:lang="fr">La communauté de KDE</developer_name>
|
<developer_name xml:lang="fr">La communauté de KDE</developer_name>
|
||||||
<developer_name xml:lang="hu">A KDE Közösség</developer_name>
|
<developer_name xml:lang="hu">A KDE Közösség</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="it">La comunità KDE</developer_name>
|
||||||
<developer_name xml:lang="nl">De KDE gemeenschap</developer_name>
|
<developer_name xml:lang="nl">De KDE gemeenschap</developer_name>
|
||||||
<developer_name xml:lang="nn">KDE-fellesskapet</developer_name>
|
<developer_name xml:lang="nn">KDE-fellesskapet</developer_name>
|
||||||
|
<developer_name xml:lang="pl">Społeczność KDE</developer_name>
|
||||||
<developer_name xml:lang="pt">A Comunidade do KDE</developer_name>
|
<developer_name xml:lang="pt">A Comunidade do KDE</developer_name>
|
||||||
|
<developer_name xml:lang="pt-BR">A comunidade KDE</developer_name>
|
||||||
<developer_name xml:lang="sk">KDE Komunita</developer_name>
|
<developer_name xml:lang="sk">KDE Komunita</developer_name>
|
||||||
<developer_name xml:lang="sl">Skupnost KDE</developer_name>
|
<developer_name xml:lang="sl">Skupnost KDE</developer_name>
|
||||||
<developer_name xml:lang="sv">KDE-gemenskapen</developer_name>
|
<developer_name xml:lang="sv">KDE-gemenskapen</developer_name>
|
||||||
@@ -83,6 +104,9 @@
|
|||||||
<project_license>GPL-3.0</project_license>
|
<project_license>GPL-3.0</project_license>
|
||||||
<screenshots>
|
<screenshots>
|
||||||
<screenshot type="default">
|
<screenshot type="default">
|
||||||
|
<image>https://cdn.kde.org/screenshots/neochat/application.png</image>
|
||||||
|
</screenshot>
|
||||||
|
<screenshot>
|
||||||
<image>https://www.plasma-mobile.org/img/post-2020-10/post-2020-10-neochat-timeline.png</image>
|
<image>https://www.plasma-mobile.org/img/post-2020-10/post-2020-10-neochat-timeline.png</image>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
</screenshots>
|
</screenshots>
|
||||||
@@ -90,6 +114,25 @@
|
|||||||
<content_attribute id="social-chat">intense</content_attribute>
|
<content_attribute id="social-chat">intense</content_attribute>
|
||||||
</content_rating>
|
</content_rating>
|
||||||
<releases>
|
<releases>
|
||||||
<release version="alpha" date="2020-12-04"/>
|
<release version="1.0.1" date="2021-01-13">
|
||||||
|
<description>
|
||||||
|
<p>This version fixes several bugs.</p>
|
||||||
|
<ul>
|
||||||
|
<li>NeoChat doesn't require a .well-know configuration in the server to work.</li>
|
||||||
|
<li>Edited messages won't show up duplicated anymore.</li>
|
||||||
|
<li>Various graphic glitches have been fixed.</li>
|
||||||
|
<li>NeoChat now ask for consent to terms and conditions if required instead of displaying nothing.</li>
|
||||||
|
<li>Users avatar in the room list are now displayed correctly.</li>
|
||||||
|
<li>Fix image saving</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
<url>https://carlschwan.eu/2020/01/13/neochat-1.0.1/</url>
|
||||||
|
</release>
|
||||||
|
<release version="1.0" date="2020-12-23">
|
||||||
|
<description>
|
||||||
|
<p>Initial release of NeoChat, the KDE matrix client.</p>
|
||||||
|
</description>
|
||||||
|
<url>https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/</url>
|
||||||
|
</release>
|
||||||
</releases>
|
</releases>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
@@ -3,55 +3,71 @@ Name=Neochat
|
|||||||
Name[ca]=Neochat
|
Name[ca]=Neochat
|
||||||
Name[ca@valencia]=Neochat
|
Name[ca@valencia]=Neochat
|
||||||
Name[da]=Neochat
|
Name[da]=Neochat
|
||||||
|
Name[de]=Neochat
|
||||||
Name[en_GB]=Neochat
|
Name[en_GB]=Neochat
|
||||||
Name[es]=Neochat
|
Name[es]=Neochat
|
||||||
Name[eu]=Neochat
|
Name[eu]=Neochat
|
||||||
|
Name[fi]=Neochat
|
||||||
Name[fr]=Neochat
|
Name[fr]=Neochat
|
||||||
Name[hu]=Neochat
|
Name[hu]=Neochat
|
||||||
Name[it]=Neochat
|
Name[it]=Neochat
|
||||||
Name[nl]=Neochat
|
Name[nl]=Neochat
|
||||||
Name[nn]=Neochat
|
Name[nn]=Neochat
|
||||||
|
Name[pl]=Neochat
|
||||||
Name[pt]=Neochat
|
Name[pt]=Neochat
|
||||||
|
Name[pt_BR]=Neochat
|
||||||
Name[sk]=Neochat
|
Name[sk]=Neochat
|
||||||
Name[sl]=Neochat
|
Name[sl]=Neochat
|
||||||
Name[sv]=Neochat
|
Name[sv]=Neochat
|
||||||
Name[uk]=Neochat
|
Name[uk]=Neochat
|
||||||
Name[x-test]=xxNeochatxx
|
Name[x-test]=xxNeochatxx
|
||||||
|
Name[zh_CN]=Neochat
|
||||||
GenericName=Matrix Client
|
GenericName=Matrix Client
|
||||||
GenericName[ca]=Client del Matrix
|
GenericName[ca]=Client del Matrix
|
||||||
GenericName[ca@valencia]=Client del Matrix
|
GenericName[ca@valencia]=Client del Matrix
|
||||||
|
GenericName[de]=Matrix-Programm
|
||||||
GenericName[en_GB]=Matrix Client
|
GenericName[en_GB]=Matrix Client
|
||||||
GenericName[es]=Cliente para Matrix
|
GenericName[es]=Cliente para Matrix
|
||||||
GenericName[eu]=Matrix bezeroa
|
GenericName[eu]=Matrix bezeroa
|
||||||
|
GenericName[fi]=Matrix-asiakas
|
||||||
GenericName[fr]=Client « Matrix »
|
GenericName[fr]=Client « Matrix »
|
||||||
GenericName[hu]=Matrix kliens
|
GenericName[hu]=Matrix kliens
|
||||||
GenericName[it]=Client Matrix
|
GenericName[it]=Client Matrix
|
||||||
GenericName[nl]=Matrix-client
|
GenericName[nl]=Matrix-client
|
||||||
GenericName[nn]=Matrix-klient
|
GenericName[nn]=Matrix-klient
|
||||||
|
GenericName[pl]=Program Matriksa
|
||||||
GenericName[pt]=Cliente de Matrix
|
GenericName[pt]=Cliente de Matrix
|
||||||
|
GenericName[pt_BR]=Cliente Matrix
|
||||||
GenericName[sk]=Matrix Client
|
GenericName[sk]=Matrix Client
|
||||||
GenericName[sl]=Odjemalec Matrix
|
GenericName[sl]=Odjemalec Matrix
|
||||||
GenericName[sv]=Matrix-klient
|
GenericName[sv]=Matrix-klient
|
||||||
GenericName[uk]=Клієнт Matrix
|
GenericName[uk]=Клієнт Matrix
|
||||||
GenericName[x-test]=xxMatrix Clientxx
|
GenericName[x-test]=xxMatrix Clientxx
|
||||||
|
GenericName[zh_CN]=Matrix 客户端
|
||||||
Comment=Client for the Matrix protocol
|
Comment=Client for the Matrix protocol
|
||||||
Comment[ca]=Client per al protocol Matrix
|
Comment[ca]=Client per al protocol Matrix
|
||||||
Comment[ca@valencia]=Client per al protocol Matrix
|
Comment[ca@valencia]=Client per al protocol Matrix
|
||||||
|
Comment[de]=Programm für das Matrix-Protokoll
|
||||||
Comment[en_GB]=Client for the Matrix protocol
|
Comment[en_GB]=Client for the Matrix protocol
|
||||||
Comment[es]=Cliente para el protocolo Matrix
|
Comment[es]=Cliente para el protocolo Matrix
|
||||||
Comment[eu]=Matrix protokolorako bezeroa
|
Comment[eu]=Matrix protokolorako bezeroa
|
||||||
|
Comment[fi]=Asiakas Matrix-yhteyskäytännölle
|
||||||
Comment[fr]=Client pour le protocole « Matrix »
|
Comment[fr]=Client pour le protocole « Matrix »
|
||||||
Comment[hu]=Kliens a Matrix protokollhoz
|
Comment[hu]=Kliens a Matrix protokollhoz
|
||||||
Comment[it]=Client per il protocollo Matrix
|
Comment[it]=Client per il protocollo Matrix
|
||||||
Comment[nl]=Client voor het Matrix-protocol
|
Comment[nl]=Client voor het Matrix-protocol
|
||||||
Comment[nn]=Lynmeldingsklient for Matrix-protokollen
|
Comment[nn]=Lynmeldingsklient for Matrix-protokollen
|
||||||
|
Comment[pl]=Program obsługi protokołu Matriksa
|
||||||
Comment[pt]=Cliente para o protocolo Matrix
|
Comment[pt]=Cliente para o protocolo Matrix
|
||||||
|
Comment[pt_BR]=Cliente para o protocolo Matrix
|
||||||
|
Comment[sk]=Klient protokolu Matrix
|
||||||
Comment[sl]=Odjemalec za protokol Matrix
|
Comment[sl]=Odjemalec za protokol Matrix
|
||||||
Comment[sv]=Klient för protokollet Matrix
|
Comment[sv]=Klient för protokollet Matrix
|
||||||
Comment[uk]=Клієнт протоколу Matrix
|
Comment[uk]=Клієнт протоколу Matrix
|
||||||
Comment[x-test]=xxClient for the Matrix protocolxx
|
Comment[x-test]=xxClient for the Matrix protocolxx
|
||||||
|
Comment[zh_CN]=为 Matrix 协议打造的客户端
|
||||||
Exec=neochat
|
Exec=neochat
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Icon=neochat
|
Icon=org.kde.neochat
|
||||||
Type=Application
|
Type=Application
|
||||||
Categories=Network;InstantMessaging;
|
Categories=Network;InstantMessaging;
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
76
qml/main.qml
76
qml/main.qml
@@ -41,6 +41,7 @@ Kirigami.ApplicationWindow {
|
|||||||
property alias pageStack: root.pageStack
|
property alias pageStack: root.pageStack
|
||||||
property bool invitationOpen: false
|
property bool invitationOpen: false
|
||||||
property var roomList: null
|
property var roomList: null
|
||||||
|
property Item roomItem: null
|
||||||
|
|
||||||
readonly property bool hasOpenRoom: currentRoom !== null
|
readonly property bool hasOpenRoom: currentRoom !== null
|
||||||
|
|
||||||
@@ -51,8 +52,8 @@ Kirigami.ApplicationWindow {
|
|||||||
if (Config.openRoom) {
|
if (Config.openRoom) {
|
||||||
const room = Controller.activeConnection.room(Config.openRoom);
|
const room = Controller.activeConnection.room(Config.openRoom);
|
||||||
currentRoom = room;
|
currentRoom = room;
|
||||||
let item = pageStack.push(roomPage, { 'currentRoom': room, });
|
roomItem = pageStack.push(roomPage, { 'currentRoom': room, });
|
||||||
connectRoomToSignal(item);
|
connectRoomToSignal(roomItem);
|
||||||
} else {
|
} else {
|
||||||
// TODO create welcome page
|
// TODO create welcome page
|
||||||
}
|
}
|
||||||
@@ -61,16 +62,16 @@ Kirigami.ApplicationWindow {
|
|||||||
function enterRoom(room) {
|
function enterRoom(room) {
|
||||||
let item = null;
|
let item = null;
|
||||||
if (currentRoom != null || invitationOpen) {
|
if (currentRoom != null || invitationOpen) {
|
||||||
currentRoom = null;
|
roomItem.currentRoom = room;
|
||||||
item = pageStack.replace(roomPage, { 'currentRoom': room, });
|
pageStack.currentIndex = pageStack.depth - 1;
|
||||||
} else {
|
} else {
|
||||||
item = pageStack.push(roomPage, { 'currentRoom': room, });
|
roomItem = pageStack.push(roomPage, { 'currentRoom': room, });
|
||||||
}
|
}
|
||||||
currentRoom = room;
|
currentRoom = room;
|
||||||
Config.openRoom = room.id;
|
Config.openRoom = room.id;
|
||||||
Config.save();
|
Config.save();
|
||||||
connectRoomToSignal(item);
|
connectRoomToSignal(roomItem);
|
||||||
return item;
|
return roomItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
function openInvitation(room) {
|
function openInvitation(room) {
|
||||||
@@ -135,14 +136,13 @@ Kirigami.ApplicationWindow {
|
|||||||
text: i18n("Explore rooms")
|
text: i18n("Explore rooms")
|
||||||
icon.name: "compass"
|
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")
|
enabled: pageStack.layers.currentItem.title !== i18n("Explore Rooms") && Controller.accountCount > 0
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18n("Start a Chat")
|
text: i18n("Start a Chat")
|
||||||
icon.name: "irc-join-channel"
|
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
|
||||||
enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat")
|
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18n("Create a Room")
|
text: i18n("Create a Room")
|
||||||
@@ -151,14 +151,21 @@ Kirigami.ApplicationWindow {
|
|||||||
let dialog = createRoomDialog.createObject(root.overlay);
|
let dialog = createRoomDialog.createObject(root.overlay);
|
||||||
dialog.open();
|
dialog.open();
|
||||||
}
|
}
|
||||||
|
shortcut: StandardKey.New
|
||||||
enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat")
|
enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat") && Controller.accountCount > 0
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18n("Accounts")
|
text: i18n("Accounts")
|
||||||
icon.name: "im-user"
|
icon.name: "im-user"
|
||||||
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/AccountsPage.qml")
|
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/AccountsPage.qml")
|
||||||
enabled: pageStack.layers.currentItem.title !== i18n("Accounts")
|
enabled: pageStack.layers.currentItem.title !== i18n("Accounts") && Controller.accountCount > 0
|
||||||
|
|
||||||
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
text: i18n("Devices")
|
||||||
|
iconName: "network-connect"
|
||||||
|
onTriggered: pageStack.layers.push("qrc:/imports/NeoChat/Page/DevicesPage.qml")
|
||||||
|
enabled: pageStack.layers.currentItem.title !== i18n("Devices") && Controller.accountCount > 0
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18n("Settings")
|
text: i18n("Settings")
|
||||||
@@ -172,6 +179,18 @@ Kirigami.ApplicationWindow {
|
|||||||
icon.name: "help-about"
|
icon.name: "help-about"
|
||||||
onTriggered: pushReplaceLayer(aboutPage)
|
onTriggered: pushReplaceLayer(aboutPage)
|
||||||
enabled: pageStack.layers.currentItem.title !== i18n("About")
|
enabled: pageStack.layers.currentItem.title !== i18n("About")
|
||||||
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
text: i18n("Logout")
|
||||||
|
icon.name: "list-remove-user"
|
||||||
|
enabled: Controller.accountCount > 0
|
||||||
|
onTriggered: Controller.logout(Controller.activeConnection, true)
|
||||||
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
text: i18n("Quit")
|
||||||
|
icon.name: "gtk-quit"
|
||||||
|
shortcut: StandardKey.Quit
|
||||||
|
onTriggered: Qt.quit()
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -223,7 +242,36 @@ Kirigami.ApplicationWindow {
|
|||||||
|
|
||||||
onShowWindow: root.showWindow()
|
onShowWindow: root.showWindow()
|
||||||
|
|
||||||
onOpenRoom: roomManager.enterRoom(room)
|
function onOpenRoom(room) {
|
||||||
|
roomManager.enterRoom(room)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onUserConsentRequired(url) {
|
||||||
|
consentSheet.url = url
|
||||||
|
consentSheet.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Kirigami.OverlaySheet {
|
||||||
|
id: consentSheet
|
||||||
|
|
||||||
|
property string url: ""
|
||||||
|
|
||||||
|
header: Kirigami.Heading {
|
||||||
|
text: i18n("User consent")
|
||||||
|
}
|
||||||
|
|
||||||
|
QQC2.Label {
|
||||||
|
id: label
|
||||||
|
|
||||||
|
text: i18n("Your homeserver requires you to agree to its terms and conditions before being able to use it. Please click the button below to read them.")
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
footer: QQC2.Button {
|
||||||
|
text: i18n("Open")
|
||||||
|
onClicked: Qt.openUrlExternally(consentSheet.url)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RoomListModel {
|
RoomListModel {
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR})
|
|||||||
target_link_libraries(neochat PRIVATE Qt5::Quick Qt5::Qml Qt5::Gui Qt5::Network Qt5::QuickControls2 KF5::I18n KF5::Kirigami2 KF5::Notifications KF5::ConfigCore KF5::ConfigGui KF5::CoreAddons Quotient cmark::cmark)
|
target_link_libraries(neochat PRIVATE Qt5::Quick Qt5::Qml Qt5::Gui Qt5::Network Qt5::QuickControls2 KF5::I18n KF5::Kirigami2 KF5::Notifications KF5::ConfigCore KF5::ConfigGui KF5::CoreAddons Quotient cmark::cmark)
|
||||||
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
|
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
|
||||||
|
|
||||||
|
if(NEOCHAT_FLATPAK)
|
||||||
|
target_compile_definitions(neochat PRIVATE NEOCHAT_FLATPAK)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (KQuickImageEditor_FOUND)
|
if (KQuickImageEditor_FOUND)
|
||||||
target_compile_definitions(neochat PRIVATE HAS_KQUICKIMAGEEDITOR)
|
target_compile_definitions(neochat PRIVATE HAS_KQUICKIMAGEEDITOR)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -167,7 +167,8 @@ QVariantMap ChatDocumentHandler::getAutocompletionInfo()
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatDocumentHandler::postMessage(const QString &text, const QString &attachementPath, const QString &replyEventId) const
|
void ChatDocumentHandler::postMessage(const QString &text, const QString &attachementPath,
|
||||||
|
const QString &replyEventId, const QVariantMap usernames) const
|
||||||
{
|
{
|
||||||
if (!m_room || !m_document) {
|
if (!m_room || !m_document) {
|
||||||
return;
|
return;
|
||||||
@@ -177,6 +178,13 @@ void ChatDocumentHandler::postMessage(const QString &text, const QString &attach
|
|||||||
|
|
||||||
cleanedText = cleanedText.trimmed();
|
cleanedText = cleanedText.trimmed();
|
||||||
|
|
||||||
|
for (const auto username : usernames.keys()) {
|
||||||
|
const auto replacement = usernames.value(username);
|
||||||
|
cleanedText = cleanedText.replace(username,
|
||||||
|
"[" + username + "](https://matrix.to/#/" + replacement.toString() + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (attachementPath.length() > 0) {
|
if (attachementPath.length() > 0) {
|
||||||
m_room->uploadFile(attachementPath, cleanedText);
|
m_room->uploadFile(attachementPath, cleanedText);
|
||||||
}
|
}
|
||||||
@@ -200,7 +208,7 @@ void ChatDocumentHandler::postMessage(const QString &text, const QString &attach
|
|||||||
for (int i = 0; i < cleanedText.length(); i++) {
|
for (int i = 0; i < cleanedText.length(); i++) {
|
||||||
rainbowText = rainbowText % QStringLiteral("<font color='") % rainbowColors.at(i % rainbowColors.length()) % "'>" % cleanedText.at(i) % "</font>";
|
rainbowText = rainbowText % QStringLiteral("<font color='") % rainbowColors.at(i % rainbowColors.length()) % "'>" % cleanedText.at(i) % "</font>";
|
||||||
}
|
}
|
||||||
m_room->postHtmlMessage(cleanedText, rainbowText, messageEventType, replyEventId);
|
m_room->postHtmlMessage(text, rainbowText, messageEventType, replyEventId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public:
|
|||||||
[[nodiscard]] NeoChatRoom *room() const;
|
[[nodiscard]] NeoChatRoom *room() const;
|
||||||
void setRoom(NeoChatRoom *room);
|
void setRoom(NeoChatRoom *room);
|
||||||
|
|
||||||
Q_INVOKABLE void postMessage(const QString &text, const QString &attachementPath, const QString &replyEventId) const;
|
Q_INVOKABLE void postMessage(const QString &text, const QString &attachementPath, const QString &replyEventId, const QVariantMap usernames) const;
|
||||||
|
|
||||||
/// This function will look at the current QTextCursor and determine if there
|
/// This function will look at the current QTextCursor and determine if there
|
||||||
/// is the posibility to autocomplete it.
|
/// is the posibility to autocomplete it.
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ Controller::Controller(QObject *parent)
|
|||||||
#ifndef Q_OS_ANDROID
|
#ifndef Q_OS_ANDROID
|
||||||
TrayIcon *trayIcon = new TrayIcon(this);
|
TrayIcon *trayIcon = new TrayIcon(this);
|
||||||
connect(trayIcon, &TrayIcon::showWindow, this, &Controller::showWindow);
|
connect(trayIcon, &TrayIcon::showWindow, this, &Controller::showWindow);
|
||||||
trayIcon->setIconSource("neochat");
|
trayIcon->setIconSource("org.kde.neochat");
|
||||||
trayIcon->setIsOnline(true);
|
trayIcon->setIsOnline(true);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -76,7 +76,6 @@ Controller::Controller(QObject *parent)
|
|||||||
Controller::~Controller()
|
Controller::~Controller()
|
||||||
{
|
{
|
||||||
for (auto c : qAsConst(m_connections)) {
|
for (auto c : qAsConst(m_connections)) {
|
||||||
c->stopSync();
|
|
||||||
c->saveState();
|
c->saveState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,11 +105,11 @@ void Controller::loginWithCredentials(const QString &serverAddr, const QString &
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto conn = new Connection(this);
|
auto conn = new Connection(this);
|
||||||
const QUrl serverUrl = QUrl(serverAddr);
|
const QUrl serverUrl = QUrl::fromUserInput(serverAddr);
|
||||||
// we are using a fake mixd since resolveServer just set the homeserver url :sigh:
|
// we are using a fake mixd since resolveServer just set the homeserver url :sigh:
|
||||||
conn->resolveServer("@username:" + serverUrl.host() + ":" + QString::number(serverUrl.port(443)));
|
conn->resolveServer("@username:" + serverUrl.host() + ":" + QString::number(serverUrl.port(443)));
|
||||||
|
|
||||||
connect(conn, &Connection::homeserverChanged, this, [this, user, conn, pass, deviceName]() {
|
connect(conn, &Connection::loginFlowsChanged, this, [this, user, conn, pass, deviceName]() {
|
||||||
conn->loginWithPassword(user, pass, deviceName, "");
|
conn->loginWithPassword(user, pass, deviceName, "");
|
||||||
connect(conn, &Connection::connected, this, [this, conn, deviceName] {
|
connect(conn, &Connection::connected, this, [this, conn, deviceName] {
|
||||||
AccountSettings account(conn->userId());
|
AccountSettings account(conn->userId());
|
||||||
@@ -198,7 +197,7 @@ void Controller::logout(Connection *conn, bool serverSideLogout)
|
|||||||
conn->stopSync();
|
conn->stopSync();
|
||||||
Q_EMIT conn->stateChanged();
|
Q_EMIT conn->stateChanged();
|
||||||
Q_EMIT conn->loggedOut();
|
Q_EMIT conn->loggedOut();
|
||||||
if (!m_connections.isEmpty()) {
|
if (conn == activeConnection() && !m_connections.isEmpty()) {
|
||||||
setActiveConnection(m_connections[0]);
|
setActiveConnection(m_connections[0]);
|
||||||
} else {
|
} else {
|
||||||
setActiveConnection(nullptr);
|
setActiveConnection(nullptr);
|
||||||
@@ -232,11 +231,18 @@ void Controller::addConnection(Connection *c)
|
|||||||
dropConnection(c);
|
dropConnection(c);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(c, &Connection::requestFailed, this, [=] (BaseJob *job) {
|
||||||
|
if(job->error() == BaseJob::UserConsentRequiredError) {
|
||||||
|
Q_EMIT userConsentRequired(job->errorUrl());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
|
|
||||||
c->sync();
|
c->sync();
|
||||||
|
|
||||||
Q_EMIT connectionAdded(c);
|
Q_EMIT connectionAdded(c);
|
||||||
|
Q_EMIT accountCountChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::dropConnection(Connection *c)
|
void Controller::dropConnection(Connection *c)
|
||||||
@@ -245,50 +251,50 @@ void Controller::dropConnection(Connection *c)
|
|||||||
m_connections.removeOne(c);
|
m_connections.removeOne(c);
|
||||||
|
|
||||||
Q_EMIT connectionDropped(c);
|
Q_EMIT connectionDropped(c);
|
||||||
|
Q_EMIT accountCountChanged();
|
||||||
c->deleteLater();
|
c->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::invokeLogin()
|
void Controller::invokeLogin()
|
||||||
{
|
{
|
||||||
const auto accounts = SettingsGroup("Accounts").childGroups();
|
const auto accounts = SettingsGroup("Accounts").childGroups();
|
||||||
|
QString id = NeoChatConfig::self()->activeConnection();
|
||||||
for (const auto &accountId : accounts) {
|
for (const auto &accountId : accounts) {
|
||||||
AccountSettings account {accountId};
|
AccountSettings account {accountId};
|
||||||
|
if (id.isEmpty()) {
|
||||||
|
// handle case where the account config is empty
|
||||||
|
id = accountId;
|
||||||
|
}
|
||||||
if (!account.homeserver().isEmpty()) {
|
if (!account.homeserver().isEmpty()) {
|
||||||
auto accessToken = loadAccessTokenFromKeyChain(account);
|
auto accessToken = loadAccessTokenFromKeyChain(account);
|
||||||
|
|
||||||
auto c = new Connection(account.homeserver(), this);
|
auto connection = new Connection(account.homeserver(), this);
|
||||||
connect(c, &Connection::connected, this, [=] {
|
connect(connection, &Connection::connected, this, [=] {
|
||||||
c->loadState();
|
connection->loadState();
|
||||||
addConnection(c);
|
addConnection(connection);
|
||||||
});
|
if (connection->userId() == id) {
|
||||||
connect(c, &Connection::loginError, this, [=](const QString &error, const QString &) {
|
setActiveConnection(connection);
|
||||||
if (error == "Unrecognised access token") {
|
Q_EMIT initiated();
|
||||||
Q_EMIT errorOccured(i18n("Login Failed"), i18n("Access Token invalid or revoked"));
|
|
||||||
logout(c, false);
|
|
||||||
} else {
|
|
||||||
Q_EMIT errorOccured(i18n("Login Failed"), error);
|
|
||||||
logout(c, true);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connect(c, &Connection::networkError, this, [=](const QString &error, const QString &, int, int) {
|
connect(connection, &Connection::loginError, this, [=](const QString &error, const QString &) {
|
||||||
|
if (error == "Unrecognised access token") {
|
||||||
|
Q_EMIT errorOccured(i18n("Login Failed"), i18n("Access Token invalid or revoked"));
|
||||||
|
logout(connection, false);
|
||||||
|
} else {
|
||||||
|
Q_EMIT errorOccured(i18n("Login Failed"), error);
|
||||||
|
logout(connection, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(connection, &Connection::networkError, this, [=](const QString &error, const QString &, int, int) {
|
||||||
Q_EMIT errorOccured("Network Error", error);
|
Q_EMIT errorOccured("Network Error", error);
|
||||||
});
|
});
|
||||||
c->connectWithToken(account.userId(), accessToken, account.deviceId());
|
connection->connectWithToken(account.userId(), accessToken, account.deviceId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (m_connections.isEmpty()) {
|
||||||
if (!m_connections.isEmpty()) {
|
Q_EMIT initiated();
|
||||||
const QString id = NeoChatConfig::self()->activeConnection();
|
|
||||||
for (auto *connection : qAsConst(m_connections)) {
|
|
||||||
if (connection->userId() == id) {
|
|
||||||
setActiveConnection(connection);
|
|
||||||
Q_EMIT initiated();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setActiveConnection(m_connections[0]);
|
|
||||||
}
|
}
|
||||||
Q_EMIT initiated();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray Controller::loadAccessTokenFromFile(const AccountSettings &account)
|
QByteArray Controller::loadAccessTokenFromFile(const AccountSettings &account)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ using namespace Quotient;
|
|||||||
class Controller : public QObject
|
class Controller : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(int accountCount READ accountCount NOTIFY connectionAdded NOTIFY connectionDropped)
|
Q_PROPERTY(int accountCount READ accountCount NOTIFY accountCountChanged)
|
||||||
Q_PROPERTY(bool quitOnLastWindowClosed READ quitOnLastWindowClosed WRITE setQuitOnLastWindowClosed NOTIFY quitOnLastWindowClosedChanged)
|
Q_PROPERTY(bool quitOnLastWindowClosed READ quitOnLastWindowClosed WRITE setQuitOnLastWindowClosed NOTIFY quitOnLastWindowClosedChanged)
|
||||||
Q_PROPERTY(Connection *activeConnection READ activeConnection WRITE setActiveConnection NOTIFY activeConnectionChanged)
|
Q_PROPERTY(Connection *activeConnection READ activeConnection WRITE setActiveConnection NOTIFY activeConnectionChanged)
|
||||||
Q_PROPERTY(bool busy READ busy WRITE setBusy NOTIFY busyChanged)
|
Q_PROPERTY(bool busy READ busy WRITE setBusy NOTIFY busyChanged)
|
||||||
@@ -103,6 +103,7 @@ Q_SIGNALS:
|
|||||||
void syncDone();
|
void syncDone();
|
||||||
void connectionAdded(Quotient::Connection *_t1);
|
void connectionAdded(Quotient::Connection *_t1);
|
||||||
void connectionDropped(Quotient::Connection *_t1);
|
void connectionDropped(Quotient::Connection *_t1);
|
||||||
|
void accountCountChanged();
|
||||||
void initiated();
|
void initiated();
|
||||||
void notificationClicked(const QString &_t1, const QString &_t2);
|
void notificationClicked(const QString &_t1, const QString &_t2);
|
||||||
void quitOnLastWindowClosedChanged();
|
void quitOnLastWindowClosedChanged();
|
||||||
@@ -112,6 +113,7 @@ Q_SIGNALS:
|
|||||||
void passwordStatus(Controller::PasswordStatus _t1);
|
void passwordStatus(Controller::PasswordStatus _t1);
|
||||||
void showWindow();
|
void showWindow();
|
||||||
void openRoom(NeoChatRoom *room);
|
void openRoom(NeoChatRoom *room);
|
||||||
|
void userConsentRequired(QUrl url);
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void logout(Quotient::Connection *conn, bool serverSideLogout);
|
void logout(Quotient::Connection *conn, bool serverSideLogout);
|
||||||
|
|||||||
12
src/main.cpp
12
src/main.cpp
@@ -58,6 +58,10 @@ int main(int argc, char *argv[])
|
|||||||
QQuickStyle::setStyle(QStringLiteral("Material"));
|
QQuickStyle::setStyle(QStringLiteral("Material"));
|
||||||
#else
|
#else
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
|
// Default to org.kde.desktop style unless the user forces another style
|
||||||
|
if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) {
|
||||||
|
QQuickStyle::setStyle(QStringLiteral("org.kde.desktop"));
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QApplication::setOrganizationName("KDE");
|
QApplication::setOrganizationName("KDE");
|
||||||
@@ -69,11 +73,19 @@ int main(int argc, char *argv[])
|
|||||||
about.setOrganizationDomain("kde.org");
|
about.setOrganizationDomain("kde.org");
|
||||||
|
|
||||||
KAboutData::setApplicationData(about);
|
KAboutData::setApplicationData(about);
|
||||||
|
QApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("org.kde.neochat")));
|
||||||
|
|
||||||
#ifndef Q_OS_ANDROID
|
#ifndef Q_OS_ANDROID
|
||||||
KDBusService service(KDBusService::Unique);
|
KDBusService service(KDBusService::Unique);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef NEOCHAT_FLATPAK
|
||||||
|
// Copy over the included FontConfig configuration to the
|
||||||
|
// app's config dir:
|
||||||
|
QFile::copy("/app/etc/fonts/conf.d/99-noto-mono-color-emoji.conf",
|
||||||
|
"/var/config/fontconfig/conf.d/99-noto-mono-color-emoji.conf");
|
||||||
|
#endif
|
||||||
|
|
||||||
Clipboard clipboard;
|
Clipboard clipboard;
|
||||||
auto config = NeoChatConfig::self();
|
auto config = NeoChatConfig::self();
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ ThumbnailResponse::ThumbnailResponse(QString id, QSize size)
|
|||||||
|
|
||||||
void ThumbnailResponse::startRequest()
|
void ThumbnailResponse::startRequest()
|
||||||
{
|
{
|
||||||
|
if (!Controller::instance().activeConnection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Runs in the main thread, not QML thread
|
// Runs in the main thread, not QML thread
|
||||||
Q_ASSERT(QThread::currentThread() == Controller::instance().activeConnection()->thread());
|
Q_ASSERT(QThread::currentThread() == Controller::instance().activeConnection()->thread());
|
||||||
job = Controller::instance().activeConnection()->getThumbnail(mediaId, requestedSize);
|
job = Controller::instance().activeConnection()->getThumbnail(mediaId, requestedSize);
|
||||||
@@ -94,6 +97,9 @@ void ThumbnailResponse::prepareResult()
|
|||||||
|
|
||||||
void ThumbnailResponse::doCancel()
|
void ThumbnailResponse::doCancel()
|
||||||
{
|
{
|
||||||
|
if (!Controller::instance().activeConnection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Runs in the main thread, not QML thread
|
// Runs in the main thread, not QML thread
|
||||||
if (job) {
|
if (job) {
|
||||||
Q_ASSERT(QThread::currentThread() == job->thread());
|
Q_ASSERT(QThread::currentThread() == job->thread());
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
|
|||||||
roles[ShowAuthorRole] = "showAuthor";
|
roles[ShowAuthorRole] = "showAuthor";
|
||||||
roles[ShowSectionRole] = "showSection";
|
roles[ShowSectionRole] = "showSection";
|
||||||
roles[ReactionRole] = "reaction";
|
roles[ReactionRole] = "reaction";
|
||||||
|
roles[IsEditedRole] = "isEdited";
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,6 +90,9 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
|||||||
m_currentRoom = room;
|
m_currentRoom = room;
|
||||||
if (room) {
|
if (room) {
|
||||||
room->setDisplayed();
|
room->setDisplayed();
|
||||||
|
if (m_currentRoom->timelineSize() < 10) {
|
||||||
|
room->getPreviousContent(50);
|
||||||
|
}
|
||||||
lastReadEventId = room->readMarkerEventId();
|
lastReadEventId = room->readMarkerEventId();
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
@@ -301,6 +305,7 @@ inline QVariantMap userAtEvent(NeoChatUser *user, NeoChatRoom *room, const RoomE
|
|||||||
{"avatarMediaId", user->avatarMediaId(room)},
|
{"avatarMediaId", user->avatarMediaId(room)},
|
||||||
{"avatarUrl", user->avatarUrl(room)},
|
{"avatarUrl", user->avatarUrl(room)},
|
||||||
{"displayName", user->displayname(room)},
|
{"displayName", user->displayname(room)},
|
||||||
|
{"display", user->name()},
|
||||||
{"color", user->color()},
|
{"color", user->color()},
|
||||||
{"object", QVariant::fromValue(user)},
|
{"object", QVariant::fromValue(user)},
|
||||||
};
|
};
|
||||||
@@ -415,6 +420,11 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isReplacement?
|
||||||
|
if (auto e = eventCast<const RoomMessageEvent>(&evt))
|
||||||
|
if (!e->replacedEvent().isEmpty())
|
||||||
|
return EventStatus::Hidden;
|
||||||
|
|
||||||
if (is<RedactionEvent>(evt) || is<ReactionEvent>(evt)) {
|
if (is<RedactionEvent>(evt) || is<ReactionEvent>(evt)) {
|
||||||
return EventStatus::Hidden;
|
return EventStatus::Hidden;
|
||||||
}
|
}
|
||||||
@@ -439,6 +449,13 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
return EventStatus::Normal;
|
return EventStatus::Normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (role == IsEditedRole) {
|
||||||
|
if (auto e = eventCast<const RoomMessageEvent>(&evt)) {
|
||||||
|
return !e->unsignedJson().isEmpty() && e->unsignedJson().contains("m.relations") && e->unsignedJson()["m.relations"].toObject().contains("m.replace");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (role == EventIdRole) {
|
if (role == EventIdRole) {
|
||||||
return !evt.id().isEmpty() ? evt.id() : evt.transactionId();
|
return !evt.id().isEmpty() ? evt.id() : evt.transactionId();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ public:
|
|||||||
ShowSectionRole,
|
ShowSectionRole,
|
||||||
|
|
||||||
ReactionRole,
|
ReactionRole,
|
||||||
|
IsEditedRole,
|
||||||
|
|
||||||
// For debugging
|
// For debugging
|
||||||
EventResolvedTypeRole,
|
EventResolvedTypeRole,
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ const RoomMessageEvent *NeoChatRoom::lastEvent(bool ignoreStateEvent) const
|
|||||||
QString NeoChatRoom::lastEventToString() const
|
QString NeoChatRoom::lastEventToString() const
|
||||||
{
|
{
|
||||||
if (auto event = lastEvent()) {
|
if (auto event = lastEvent()) {
|
||||||
return user(event->senderId())->displayname() + (event->isStateEvent() ? " " : ": ") + eventToString(*event);
|
return user(event->senderId())->displayname(this) + (event->isStateEvent() ? " " : ": ") + eventToString(*event);
|
||||||
}
|
}
|
||||||
return QLatin1String("");
|
return QLatin1String("");
|
||||||
}
|
}
|
||||||
@@ -283,7 +283,7 @@ QString NeoChatRoom::avatarMediaId() const
|
|||||||
const auto dcUsers = directChatUsers();
|
const auto dcUsers = directChatUsers();
|
||||||
for (const auto u : dcUsers) {
|
for (const auto u : dcUsers) {
|
||||||
if (u != localUser()) {
|
if (u != localUser()) {
|
||||||
return u->avatarMediaId();
|
return u->avatarMediaId(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,6 +467,8 @@ QString NeoChatRoom::markdownToHTML(const QString &markdown)
|
|||||||
|
|
||||||
auto result = QString::fromStdString(html).trimmed();
|
auto result = QString::fromStdString(html).trimmed();
|
||||||
|
|
||||||
|
result.replace("<!-- raw HTML omitted -->", "<br />");
|
||||||
|
result.replace(QRegularExpression("(<br />)*$"), "");
|
||||||
result.replace("<p>", "");
|
result.replace("<p>", "");
|
||||||
result.replace("</p>", "");
|
result.replace("</p>", "");
|
||||||
|
|
||||||
|
|||||||
@@ -256,6 +256,9 @@ QVariant RoomListModel::data(const QModelIndex &index, int role) const
|
|||||||
}
|
}
|
||||||
NeoChatRoom *room = m_rooms.at(index.row());
|
NeoChatRoom *room = m_rooms.at(index.row());
|
||||||
if (role == NameRole) {
|
if (role == NameRole) {
|
||||||
|
return !room->name().isEmpty() ? room->name() : room->displayName();
|
||||||
|
}
|
||||||
|
if (role == DisplayNameRole) {
|
||||||
return room->displayName();
|
return room->displayName();
|
||||||
}
|
}
|
||||||
if (role == AvatarRole) {
|
if (role == AvatarRole) {
|
||||||
@@ -324,6 +327,7 @@ QHash<int, QByteArray> RoomListModel::roleNames() const
|
|||||||
{
|
{
|
||||||
QHash<int, QByteArray> roles;
|
QHash<int, QByteArray> roles;
|
||||||
roles[NameRole] = "name";
|
roles[NameRole] = "name";
|
||||||
|
roles[DisplayNameRole] = "displayName";
|
||||||
roles[AvatarRole] = "avatar";
|
roles[AvatarRole] = "avatar";
|
||||||
roles[TopicRole] = "topic";
|
roles[TopicRole] = "topic";
|
||||||
roles[CategoryRole] = "category";
|
roles[CategoryRole] = "category";
|
||||||
@@ -352,7 +356,7 @@ QString RoomListModel::categoryName(int section)
|
|||||||
case 5:
|
case 5:
|
||||||
return i18n("Low priority");
|
return i18n("Low priority");
|
||||||
default:
|
default:
|
||||||
return i18n("Deadbeef");
|
return "Deadbeef";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ class RoomListModel : public QAbstractListModel
|
|||||||
public:
|
public:
|
||||||
enum EventRoles {
|
enum EventRoles {
|
||||||
NameRole = Qt::UserRole + 1,
|
NameRole = Qt::UserRole + 1,
|
||||||
|
DisplayNameRole,
|
||||||
AvatarRole,
|
AvatarRole,
|
||||||
TopicRole,
|
TopicRole,
|
||||||
CategoryRole,
|
CategoryRole,
|
||||||
|
|||||||
Reference in New Issue
Block a user