Compare commits

...

65 Commits
master ... 1.0

Author SHA1 Message Date
Tobias Fella
143bd456de Actually save the settings
(cherry picked from commit 75d3b346ac)
2021-01-23 16:40:05 +00:00
Tobias Fella
513de82515 Fix showing user's displayName instead of mxid in roomlist delegate subtitles
(cherry picked from commit 6f7f0e025d)
2021-01-18 21:28:40 +00:00
l10n daemon script
581fd5f212 GIT_SILENT made messages (after extraction) 2021-01-18 09:21:05 +01:00
Tobias Fella
2452f630d0 Load serverAddress using QUrl::fromUserInput()
Fixes login when 'https://' is not added to the server url


(cherry picked from commit a653be8be8)
2021-01-17 00:32:39 +00:00
Carl Schwan
2cdc37c3d5 Don't load events if not needed
(cherry picked from commit 7762f5f5ae)
2021-01-14 21:11:32 +00:00
Carl Schwan
5af99a872c Make sure we load events when opening a room
(cherry picked from commit 1abc28ad7f)
2021-01-14 20:54:41 +00:00
l10n daemon script
5fe7ba478b SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2021-01-14 10:25:39 +01:00
Carl Schwan
f25bc6bac6 Fix appstream file 2021-01-12 22:49:21 +01:00
Carl Schwan
46ee015775 Update appdata 2021-01-12 22:44:54 +01:00
Carl Schwan
f93be04b2b Update version 2021-01-12 22:29:24 +01:00
Carl Schwan
f2d9ca13d8 fix path 2021-01-12 22:27:55 +01:00
Carl Schwan
eb04f4adf1 Update screenshots 2021-01-12 22:26:50 +01:00
Carl Schwan
6b983332af Fix edited message appearing two times in the timeline 2021-01-12 21:55:23 +01:00
Carl Schwan
4e197c3cc8 Fix autocompletion
Now it will save a map from display name to id and use that to generate
clean matrix.to links. This also make sure the colors used for the
preview are correct by using NeoChatUser and fix the bug with the regex
by simply removing the regex.
2021-01-12 21:47:57 +01:00
Carl Schwan
bfd6d2ffe2 Fix the white bar in the room page's header 2021-01-12 17:48:35 +01:00
Carl Schwan
421422edd0 Fix avatar loading in multiple places and prefers name instead of
display name for avatar fallback.

This also fixes a bug where users didn't get their avatar loaded in the
room list.

Fix #209
2021-01-12 17:46:15 +01:00
Carl Schwan
0eda9608ec Fix PgUp/PgDn keys in message view switch rooms
Now use Ctr+PgUp/PgDn keys instead

Fix #213
2021-01-12 17:42:48 +01:00
Carl Schwan
06fd8630d9 Fix NeoChat not syncing
This problem was caused because addConnection was starting the sync
proccess unfortunally because the user wasn't connected this aborted
almost immediately and then the sync proccess wouldn't run at all.

Now start the sync proccess after making sure we are connected.

Fix #228
2021-01-12 17:41:10 +01:00
Carl Schwan
e7d8c4b69c Handle non-consistent configuration 2021-01-12 17:40:58 +01:00
Carl Schwan
44d4269978 Fix initial loading of room 2021-01-12 17:38:55 +01:00
Adriaan de Groot
759244b5d2 CMake: systematically use the feature-summary
There's not much point in having a feature summary that will
trip over just-a-few of the required packages, while also
using REQUIRED in find_package() calls -- then you have to
re-run CMake for all the REQUIRED ones you're missing,
and then one more time for the packages that are required
in the feature summary.

Use the feature summary (e.g. TYPE REQUIRED) consistently.
Then you can run CMake once and learn about all the missing
dependencies in one go.
2021-01-12 17:38:27 +01:00
Tobias Fella
92a307747f Fix active connection not loading on startup 2021-01-12 17:37:58 +01:00
Carl Schwan
627929203f Disable menu item when login in
Fix #204
2021-01-12 17:32:17 +01:00
Tobias Fella
0c449ab4bd Ask for consent to terms and conditions if required 2021-01-12 17:30:26 +01:00
Tobias Fella
45c35b3cbe Fix accountCount not updating correctly 2021-01-12 17:29:02 +01:00
l10n daemon script
ecd6a63564 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2021-01-12 10:47:53 +01:00
l10n daemon script
b75bf3a75b GIT_SILENT made messages (after extraction) 2021-01-12 08:53:10 +01:00
l10n daemon script
43288db000 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2021-01-02 10:22:39 +01:00
l10n daemon script
0d6c793a5e GIT_SILENT made messages (after extraction) 2021-01-02 08:58:52 +01:00
l10n daemon script
6daf184a60 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2021-01-01 09:39:20 +01:00
l10n daemon script
93173e3f43 GIT_SILENT made messages (after extraction) 2021-01-01 08:24:14 +01:00
l10n daemon script
ec4aa320c1 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2020-12-29 09:36:04 +01:00
l10n daemon script
079a1c8a34 GIT_SILENT made messages (after extraction) 2020-12-29 08:20:25 +01:00
Carl Schwan
e9bb0972a9 Fix Platform is undefined bug 2020-12-29 01:42:19 +01:00
Nicolas Fella
1717790096 Don't call stopSync when destroying controller
Connection does that internally already


(cherry picked from commit 6a1fd3ff31)
2020-12-28 17:31:24 +00:00
Tobias Fella
893bc79f1e Don't load empty images from imageprovider
Previously, when there was no avatar set, the source property of Avatar was still set to 'image://mxc/',
which caused Avatar to load that from the imageprovider. The imageprovider can't provide an empty image and aborts with error


(cherry picked from commit 724f10a895)
2020-12-28 17:30:16 +00:00
Antonio Rojas
e87ae48f17 Add missing cmake check for kitemmodels
Otherwise packagers have no way to know that it is a runtime dependency


(cherry picked from commit 93e0a2b2f6)
2020-12-28 17:28:37 +00:00
Tobias Fella
97bbcf3062 Fix segfault/assert when logging out of account
(cherry picked from commit 0fe0f45944)
2020-12-28 00:07:54 +00:00
Eamonn Rea
9f9498541a Fix cursorShape not updating for messages
(cherry picked from commit 066ab1e6c6)
2020-12-28 00:58:07 +01:00
Tobias Fella
50a4d0a33f Fix login for homeservers without well-known
(cherry picked from commit 3858956e82)
2020-12-27 22:38:17 +00:00
Carl Schwan
85b4d7d049 Don't translate something we shouldn't
(cherry picked from commit dce3b796c2)
2020-12-26 15:59:24 +00:00
l10n daemon script
b574849df3 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2020-12-26 09:29:49 +01:00
l10n daemon script
e802d7f805 GIT_SILENT made messages (after extraction) 2020-12-26 08:15:49 +01:00
Tobias Fella
99438011ca Fix image saving
(cherry picked from commit 8aec6b67cb)
2020-12-24 12:35:54 +00:00
Nicolas Fella
99ccfaf93e Default to org.kde.desktop QQC2 style
plasma-integration does that for us, but that obviously doesn't work for non-Plasma desktops.
2020-12-24 00:20:10 +01:00
Carl Schwan
c56973763c Dismiss reply when clicking on Esc
Backport of 59f9c36854
2020-12-23 18:02:37 +01:00
Devin Lin
c96109e9b7 Fix room header text alignment and add support for two line room descriptions
(cherry picked from commit 93f35faf95)
2020-12-23 09:06:33 +00:00
Devin Lin
3f01b3badf Show feedback on avatar hover
(cherry picked from commit 87a7a34d80)
2020-12-23 08:51:29 +00:00
Tobias Fella
093412c788 Revert "Add symbolic icon"
This reverts commit 89bf5d3a31
2020-12-22 23:43:35 +00:00
Carl Schwan
c5ddb61981 Use correct version 2020-12-22 23:12:46 +01:00
Nicolas Fella
ad4e52b20d Fix icon in notifyrc
(cherry picked from commit ef8c21213a)
2020-12-22 22:02:23 +00:00
Carl Schwan
5991d59ddd Fix not eliding text in USerDetailDialog
Fix: #169
2020-12-22 16:23:49 +01:00
Carl Schwan
de49a26462 Switch back to plain text editing
See https://bugreports.qt.io/browse/QTBUG-89630


(cherry picked from commit 6482f08eba)
2020-12-21 09:25:07 +00:00
Carl Schwan
4924702c15 Use TextArea instead of simple field for room topic
(cherry picked from commit f61eff2937)
2020-12-20 19:27:26 +00:00
Tobias Fella
8060edd1c6 Allow opening links in the MessageDelegateContextMenu
Fixes #167


(cherry picked from commit 449adf993c)
2020-12-20 18:17:46 +00:00
Jan Blackquill
89bf5d3a31 Add symbolic icon
(cherry picked from commit 9189a8ca30)
2020-12-20 09:19:53 +00:00
Carl Schwan
60762b934c Fix current page not getting updated after switching a page
This was caused by myself not updating the index after updating the
content.
2020-12-19 23:03:01 +01:00
Carl Schwan
7729fec259 Add special font configuration for flatpak
(cherry picked from commit 6e659c853b)
2020-12-19 10:49:32 +00:00
Carl Schwan
026769b07f Make kquickimageeditor a required dependency 2020-12-17 13:19:03 +01:00
Carl Schwan
5be14a4b8f Last icon fix 2020-12-17 10:37:22 +01:00
Carl Schwan
0b70d2b33f fix icon 2020-12-17 10:37:22 +01:00
Carl Schwan
dab77b8d07 Rename icon and set icon name explicitely
Fix #140
2020-12-17 10:37:22 +01:00
Carl Schwan
2acdf61b16 Don't recreate RoomPage each time and add a small loading indicator
(cherry picked from commit bd41dcc986)
2020-12-17 08:59:36 +00:00
Carl Schwan
defa3d4b77 Improve autocompletion
(cherry picked from commit 2b84c5dd02)
2020-12-17 08:58:30 +00:00
Mathew Broady
be709a2732 Remove forgotten NeoChat.Effect imports
Fixes the "Start Chat" and "Explore Rooms" pages


(cherry picked from commit 79dab63993)
2020-12-17 06:24:29 +00:00
37 changed files with 490 additions and 134 deletions

View File

@@ -20,22 +20,48 @@ include(KDEClangFormat)
include(KDECMakeSettings)
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
cmake_policy(SET CMP0063 OLD)
ecm_setup_version(0.1.0
ecm_setup_version(1.0.1
VARIABLE_PREFIX NEOCHAT
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(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Kirigami2 I18n Notifications Config CoreAddons)
find_package(Qt5 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Widgets Core Quick Gui QuickControls2 Multimedia Svg)
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)
find_package(OpenSSL REQUIRED)
find_package(OpenSSL)
set_package_properties(OpenSSL PROPERTIES
TYPE REQUIRED
PURPOSE "Encrypted communications"
)
else()
find_package(Qt5Keychain REQUIRED)
find_package(KF5DBusAddons ${KF5_MIN_VERSION} REQUIRED)
find_package(Qt5Keychain)
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()
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.kitemmodels 1.0)
find_package(KQuickImageEditor COMPONENTS)
set_package_properties(KQuickImageEditor PROPERTIES
TYPE REQUIRED
DESCRIPTION "Simple image editor for QtQuick applications"
URL "https://invent.kde.org/libraries/kquickimageeditor/"
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.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})
# add_definitions(-DQT_NO_KEYWORDS) Need to fix libQuotient first

16
cmake/Flatpak.cmake Normal file
View 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/
)

View 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>

View File

@@ -89,8 +89,8 @@ ToolBar {
Layout.preferredWidth: Kirigami.Units.gridUnit
Layout.preferredHeight: Kirigami.Units.gridUnit
source: replyUser ? "image://mxc/" + replyUser.avatarMediaId: ""
name: replyUser ? replyUser.displayName : i18n("No name")
source: replyUser ? ("image://mxc/" + replyUser.avatarMediaId) : ""
name: replyUser ? replyUser.name : i18n("No name")
}
Label {
@@ -133,8 +133,9 @@ ToolBar {
keyNavigationWraps: true
delegate: Control {
property string displayText: modelData.displayName ?? modelData.unicode
property bool isEmoji: modelData.unicode != null
readonly property string userId: modelData.id ?? ""
readonly property string displayText: modelData.displayName ?? modelData.unicode
readonly property bool isEmoji: modelData.unicode != null
readonly property bool highlighted: autoCompleteListView.currentIndex == index
padding: Kirigami.Units.smallSpacing
@@ -156,7 +157,7 @@ ToolBar {
Kirigami.Avatar {
Layout.preferredWidth: 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
visible: !isEmoji
}
@@ -176,7 +177,7 @@ ToolBar {
anchors.fill: parent
onClicked: {
autoCompleteListView.currentIndex = index
documentHandler.replaceAutoComplete(displayText)
inputField.autoComplete();
}
}
}
@@ -342,7 +343,8 @@ ToolBar {
Keys.onReturnPressed: {
if (isAutoCompleting) {
documentHandler.replaceAutoComplete(autoCompleteListView.currentItem.displayText)
inputField.autoComplete();
isAutoCompleting = false;
return;
}
@@ -356,7 +358,10 @@ ToolBar {
}
}
Keys.onEscapePressed: closeAll()
Keys.onEscapePressed: {
clearReply();
closeAll();
}
Keys.onPressed: {
if (event.key === Qt.Key_PageDown) {
@@ -397,7 +402,7 @@ ToolBar {
autoAppeared = false;
}
documentHandler.replaceAutoComplete(autoCompleteListView.currentItem.displayText)
inputField.autoComplete();
}
onTextChanged: {
@@ -433,11 +438,31 @@ ToolBar {
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() {
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();
currentRoom.markAllMessagesAsRead();
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() {
inputField.clear()
inputField.userAutocompleted = {};
}
function clearReply() {

View File

@@ -115,7 +115,7 @@ RowLayout {
else
{
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))
}
}

View File

@@ -7,7 +7,7 @@ import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
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 NeoChat.Setting 1.0
@@ -143,15 +143,21 @@ Image {
}
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) {
if (!path) return
Component {
id: fileDialog
currentRoom.downloadFile(eventId, path + "/" + currentRoom.fileNameToDownload(eventId))
})
folderDialog.open()
FileDialog {
fileMode: FileDialog.SaveFile
folder: StandardPaths.writableLocation(StandardPaths.DownloadLocation)
onAccepted: {
currentRoom.downloadFile(eventId, file)
}
}
}
function downloadAndOpen()
@@ -160,7 +166,7 @@ Image {
else
{
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))
}
}

View File

@@ -51,8 +51,8 @@ RowLayout {
Layout.alignment: Qt.AlignTop
visible: showAuthor && Config.showAvatarInTimeline
name: author.displayName
source: author.avatarMediaId ? "image://mxc/" + author.avatarMediaId : ""
name: author.name
source: author.avatarMediaId ? ("image://mxc/" + author.avatarMediaId) : ""
color: author.color
Component {
@@ -64,6 +64,7 @@ RowLayout {
MouseArea {
anchors.fill: parent
onClicked: userDetailDialog.createObject(QQC2.ApplicationWindow.overlay, {"room": currentRoom, "user": author.object, "displayName": author.displayName, "avatarMediaId": author.avatarMediaId, "avatarUrl": author.avatarUrl}).open()
cursorShape: Qt.PointingHandCursor
}
}

View File

@@ -30,8 +30,8 @@ QQC2.AbstractButton {
Layout.preferredHeight: Kirigami.Units.gridUnit
Layout.alignment: Qt.AlignTop
visible: Config.showAvatarInTimeline
source: replyVisible && reply.author.avatarMediaId ? "image://mxc/" + reply.author.avatarMediaId : ""
name: replyVisible ? reply.author.displayName : "H"
source: replyVisible && reply.author.avatarMediaId ? ("image://mxc/" + reply.author.avatarMediaId) : ""
name: replyVisible ? reply.author.name : "H"
color: replyVisible ? reply.author.color : Kirigami.Theme.highlightColor
}

View File

@@ -25,8 +25,8 @@ RowLayout {
Layout.preferredWidth: Kirigami.Units.iconSizes.small
Layout.preferredHeight: Kirigami.Units.iconSizes.small
name: author.displayName
source: author.avatarMediaId ? "image://mxc/" + author.avatarMediaId : ""
name: author.name
source: author.avatarMediaId ? ("image://mxc/" + author.avatarMediaId) : ""
color: author.color
Component {

View File

@@ -15,7 +15,8 @@ TextEdit {
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
font.pointSize: isEmoji.test(display) ? Kirigami.Theme.defaultFont.pointSize * 4 : Kirigami.Theme.defaultFont.pointSize

View File

@@ -42,8 +42,8 @@ Kirigami.OverlaySheet {
Layout.preferredHeight: 72
Layout.alignment: Qt.AlignTop
name: room.displayName
source: room.avatarMediaId ? "image://mxc/" + room.avatarMediaId : ""
name: room.name
source: room.avatarMediaId ? ("image://mxc/" + room.avatarMediaId) : ""
MouseArea {
anchors.fill: parent
@@ -74,7 +74,7 @@ Kirigami.OverlaySheet {
enabled: canChangeName
}
TextField {
TextArea {
id: roomTopicField
Layout.fillWidth: true
text: room.topic

View File

@@ -29,7 +29,11 @@ Kirigami.OverlaySheet {
topPadding: 0
header: Kirigami.Heading {
id: heading
text: i18nc("Account detail dialog", "Account detail - %1", displayName)
elide: Text.ElideRight
QQC2.ToolTip.visible: truncated && hovered
QQC2.ToolTip.text: text
}
contentItem: ColumnLayout {
@@ -47,7 +51,7 @@ Kirigami.OverlaySheet {
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
name: displayName
source: avatarMediaId ? "image://mxc/" + avatarMediaId : ""
source: avatarMediaId ? ("image://mxc/" + avatarMediaId) : ""
MouseArea {
anchors.fill: parent

View File

@@ -38,7 +38,7 @@ Kirigami.OverlaySheet {
spacing: Kirigami.Units.largeSpacing
Kirigami.Avatar {
id: avatar
source: author.avatarMediaId ? "image://mxc/" + author.avatarMediaId : ""
source: author.avatarMediaId ? ("image://mxc/" + author.avatarMediaId) : ""
Layout.preferredWidth: Kirigami.Units.gridUnit * 3
Layout.preferredHeight: Kirigami.Units.gridUnit * 3
Layout.alignment: Qt.AlignTop
@@ -55,6 +55,18 @@ Kirigami.OverlaySheet {
text: message
Layout.fillWidth: true
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)
}
}
}
}
}

View File

@@ -86,7 +86,7 @@ Kirigami.ScrollablePage {
Layout.preferredWidth: height
Layout.fillHeight: true
source: avatar ? "image://mxc/" + avatar : ""
source: avatar ? ("image://mxc/" + avatar) : ""
name: name
}

View File

@@ -11,7 +11,6 @@ import org.kde.kirigami 2.13 as Kirigami
import org.kde.neochat 1.0
import NeoChat.Component 1.0
import NeoChat.Effect 1.0
import NeoChat.Setting 1.0
import org.kde.neochat 1.0
@@ -117,7 +116,7 @@ Kirigami.ScrollablePage {
Layout.preferredWidth: 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
}
ColumnLayout {

View File

@@ -95,7 +95,7 @@ Kirigami.ScrollablePage {
id: roomListItem
property bool itemVisible: model.categoryVisible || sortFilterRoomListModel.filterText.length > 0 || Config.mergeRoomList
visible: itemVisible
highlighted: roomManager.currentRoom && roomManager.currentRoom.name === name
highlighted: roomManager.currentRoom && roomManager.currentRoom.displayName === displayName
focus: true
action: Kirigami.Action {
id: enterRoomAction
@@ -134,7 +134,7 @@ Kirigami.ScrollablePage {
Layout.minimumWidth: size
Layout.maximumWidth: size
source: avatar ? "image://mxc/" + avatar : ""
source: avatar ? ("image://mxc/" + avatar) : ""
name: model.name || i18n("No Name")
}
@@ -153,7 +153,7 @@ Kirigami.ScrollablePage {
QQC2.Label {
Layout.fillWidth: true
Layout.fillHeight: true
text: name ?? ""
text: displayName ?? ""
elide: Text.ElideRight
font.bold: unreadCount >= 0 || highlightCount > 0 || notificationCount > 0
wrapMode: Text.NoWrap

View File

@@ -28,30 +28,55 @@ Kirigami.ScrollablePage {
signal switchRoomUp()
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 {
RowLayout {
visible: !Kirigami.Settings.isMobile
Layout.fillWidth: true
Layout.maximumWidth: implicitWidth + 1 // The +1 is to make sure we do not trigger eliding at max width
Layout.minimumWidth: 0
spacing: Kirigami.Units.gridUnit * 0.8
Kirigami.Heading {
level: 1
id: titleLabel
level: 2
Layout.alignment: Qt.AlignVCenter
text: page.title
opacity: page.isCurrentPage ? 1 : 0.4
maximumLineCount: 1
elide: Text.ElideRight
Layout.leftMargin: Kirigami.Units.largeSpacing
Layout.alignment: Qt.AlignBottom
}
QQC2.Label {
Layout.alignment: Qt.AlignBottom
text: currentRoom.topic
Layout.fillWidth: true
maximumLineCount: 1
//wrapMode: Text.WordWrap
Layout.alignment: Qt.AlignVCenter
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
color: Kirigami.Theme.disabledTextColor
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.9
}
}
}
@@ -70,10 +95,10 @@ Kirigami.ScrollablePage {
}
Keys.onPressed: {
if (event.key === Qt.Key_PageDown) {
switchRoomDown();
} else if (event.key === Qt.Key_PageUp) {
if (event.key === Qt.Key_PageDown && (event.modifiers & Qt.ControlModifier)) {
switchRoomUp();
} else if (event.key === Qt.Key_PageUp && (event.modifiers & Qt.ControlModifier)) {
switchRoomDown();
} else if (!(event.modifiers & Qt.ControlModifier) && event.key < Qt.Key_Escape) {
event.accepted = true;
chatTextInput.addText(event.text);
@@ -121,6 +146,14 @@ Kirigami.ScrollablePage {
room: currentRoom
}
Kirigami.PlaceholderMessage {
anchors.centerIn: parent
visible: messageListView.count === 0 && !currentRoom.allHistoryLoaded
QQC2.BusyIndicator {
running: true
}
}
QQC2.Popup {
anchors.centerIn: parent
@@ -187,12 +220,14 @@ Kirigami.ScrollablePage {
filterRowCallback: Config.showLeaveJoinEvent ? dontFilterLeaveJoin : filterLeaveJoin
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";
}
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) !== "state";
}
@@ -285,19 +320,19 @@ Kirigami.ScrollablePage {
innerObject: MessageDelegate {
Layout.fillWidth: true
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)
onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId);
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 {
Layout.fillWidth: true
Layout.rightMargin: Kirigami.Units.largeSpacing

View File

@@ -22,29 +22,44 @@ Kirigami.ScrollablePage {
Kirigami.FormData.label: i18n("Notifications and events:")
text: i18n("Show notifications")
checked: Config.showNotifications
onToggled: Config.showNotifications = checked
onToggled: {
Config.showNotifications = checked
Config.save()
}
}
QQC2.CheckBox {
text: i18n("Show leave and join events")
checked: Config.showLeaveJoinEvent
onToggled: Config.showLeaveJoinEvent = checked
onToggled: {
Config.showLeaveJoinEvent = checked
Config.save()
}
}
QQC2.RadioButton {
Kirigami.FormData.label: i18n("Rooms and private chats:")
text: i18n("Separated")
checked: !Config.mergeRoomList
onToggled: Config.mergeRoomList = false
onToggled: {
Config.mergeRoomList = false
Config.save()
}
}
QQC2.RadioButton {
text: i18n("Intermixed")
checked: Config.mergeRoomList
onToggled: Config.mergeRoomList = true
onToggled: {
Config.mergeRoomList = true
Config.save()
}
}
QQC2.CheckBox {
Kirigami.FormData.label: i18n("Timeline:")
text: i18n("Show User Avatar")
checked: Config.showAvatarInTimeline
onToggled: Config.showAvatarInTimeline = checked
onToggled: {
Config.showAvatarInTimeline = checked
Config.save()
}
}
}
}

View File

@@ -10,7 +10,6 @@ import QtQuick.Layouts 1.12
import org.kde.kirigami 2.14 as Kirigami
import NeoChat.Component 1.0
import NeoChat.Effect 1.0
import NeoChat.Setting 1.0
import org.kde.neochat 1.0

View File

@@ -110,8 +110,8 @@ Kirigami.OverlayDrawer {
Layout.preferredWidth: Kirigami.Units.gridUnit * 3.5
Layout.preferredHeight: Kirigami.Units.gridUnit * 3.5
name: room ? room.displayName : i18n("No name")
source: room ? "image://mxc/" + room.avatarMediaId : undefined
name: room ? room.name : i18n("No name")
source: room ? ("image://mxc/" + room.avatarMediaId) : ""
}
ColumnLayout {
@@ -190,7 +190,7 @@ Kirigami.OverlayDrawer {
Layout.preferredWidth: height
Layout.fillHeight: true
source: "image://mxc/" + avatar
source: avatar ? ("image://mxc/" + avatar) : ""
name: name
}

View File

@@ -1,74 +1,95 @@
[Global]
IconName=neochat
IconName=org.kde.neochat
Name=Neochat
Name[ca]=Neochat
Name[ca@valencia]=Neochat
Name[da]=Neochat
Name[de]=Neochat
Name[en_GB]=Neochat
Name[es]=Neochat
Name[eu]=Neochat
Name[fi]=Neochat
Name[fr]=Neochat
Name[hu]=Neochat
Name[it]=Neochat
Name[nl]=Neochat
Name[nn]=Neochat
Name[pl]=Neochat
Name[pt]=Neochat
Name[pt_BR]=Neochat
Name[sk]=Neochat
Name[sl]=Neochat
Name[sv]=Neochat
Name[uk]=Neochat
Name[x-test]=xxNeochatxx
Name[zh_CN]=Neochat
DesktopEntry=org.kde.neochat
Comment=IM client for the Matrix protocol
Comment[ca]=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[es]=Cliente de MI para el protocolo Matrix
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[hu]=Azonnali üzenetküldő kliens a Matrix protokollhoz
Comment[it]=Client di messaggistica istantanea per il protocollo Matrix
Comment[nl]=IM-client voor het Matrix-protocol
Comment[nn]=Lynmeldings­klient for Matrix-protokollen
Comment[pl]=Komunikator internetowy dla protokołu 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[sv]=Direktmeddelandeklient för protokollet Matrix
Comment[uk]=Клієнт служби миттєвого обміну повідомленнями для протоколу Matrix
Comment[x-test]=xxIM client for the Matrix protocolxx
Comment[zh_CN]=为 Matrix 协议打造的 IM 客户端
[Event/message]
Name=New message
Name[ca]=Missatge nou
Name[ca@valencia]=Missatge nou
Name[de]=Neue Nachricht
Name[en_GB]=New message
Name[es]=Nuevo mensaje
Name[eu]=Mezu berria
Name[fi]=Uusi viesti
Name[fr]=Nouveau message
Name[hu]=Új üzenet
Name[it]=Nuovo messaggio
Name[nl]=Nieuw bericht
Name[nn]=Ny melding
Name[pl]=Nowa wiadomość
Name[pt]=Nova mensagem
Name[pt_BR]=Nova mensagem
Name[sk]=Nová správa
Name[sl]=Novo sporočilo
Name[sv]=Nytt meddelande
Name[uk]=Нове повідомлення
Name[x-test]=xxNew messagexx
Name[zh_CN]=新消息
Comment=There is a new message
Comment[ca]=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[es]=Hay un mensaje nuevo
Comment[eu]=Mezu berri bat dago
Comment[fi]=Saapui uusi viesti
Comment[fr]=Il y a un nouveau message
Comment[hu]=Új üzenet érkezett
Comment[it]=È presente un nuovo messaggio
Comment[nl]=Er is een nieuw bericht
Comment[nn]=Du har ei ny melding
Comment[pl]=Dostępna jest nowa wiadomość
Comment[pt]=Tem uma mensagem nova
Comment[pt_BR]=Existe uma nova mensagem
Comment[sk]=Je nová správa
Comment[sl]=Prišlo je novo sporočilo
Comment[sv]=Det finns ett nytt meddelande
Comment[uk]=Надійшло нове повідомлення
Comment[x-test]=xxThere is a new messagexx
Comment[zh_CN]=有新消息
Action=Popup

View File

@@ -8,15 +8,20 @@
<name xml:lang="ca">Neochat</name>
<name xml:lang="ca-valencia">Neochat</name>
<name xml:lang="da">Neochat</name>
<name xml:lang="de">Neochat</name>
<name xml:lang="en-GB">Neochat</name>
<name xml:lang="es">Neochat</name>
<name xml:lang="eu">Neochat</name>
<name xml:lang="fi">Neochat</name>
<name xml:lang="fr">Neochat</name>
<name xml:lang="hu">Neochat</name>
<name xml:lang="id">Neochat</name>
<name xml:lang="it">Neochat</name>
<name xml:lang="nl">Neochat</name>
<name xml:lang="nn">Neochat</name>
<name xml:lang="pl">Neochat</name>
<name xml:lang="pt">Neochat</name>
<name xml:lang="pt-BR">Neochat</name>
<name xml:lang="sk">Neochat</name>
<name xml:lang="sl">Neochat</name>
<name xml:lang="sv">Neochat</name>
@@ -25,15 +30,21 @@
<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-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="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="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="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="nl">Een client voor matrix, het gedecentraliseerde communicatieprotocol</summary>
<summary xml:lang="nn">Ein klient for Matrix, den desentraliserte lynmeldings­protokollen</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-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="sv">En klient för Matrix, det decentraliserade kommunikationsprotokollet</summary>
<summary xml:lang="uk">Клієнт matrix, децентралізованого протоколу обміну даними</summary>
@@ -42,15 +53,21 @@
<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-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="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="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="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="nl">Een client voor matrix, het gedecentraliseerde communicatieprotocol.</p>
<p xml:lang="nn">Ein klient for Matrix, den desentraliserte lynmeldings­protokollen.</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-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="sv">En klient för Matrix, det decentraliserade kommunikationsprotokollet.</p>
<p xml:lang="uk">Клієнт matrix, децентралізованого протоколу обміну даними.</p>
@@ -59,21 +76,25 @@
<url type="homepage">https://kde.org</url>
<url type="bugtracker">https://bugs.kde.org</url>
<categories>
<category>Matrix</category>
<category>Internet</category>
<category>Network</category>
</categories>
<developer_name>The KDE Community</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="de">Die KDE-Gemeinschaft</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="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="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="nl">De KDE gemeenschap</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-BR">A comunidade KDE</developer_name>
<developer_name xml:lang="sk">KDE Komunita</developer_name>
<developer_name xml:lang="sl">Skupnost KDE</developer_name>
<developer_name xml:lang="sv">KDE-gemenskapen</developer_name>
@@ -83,6 +104,9 @@
<project_license>GPL-3.0</project_license>
<screenshots>
<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>
</screenshot>
</screenshots>
@@ -90,6 +114,25 @@
<content_attribute id="social-chat">intense</content_attribute>
</content_rating>
<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>
</component>

View File

@@ -3,55 +3,71 @@ Name=Neochat
Name[ca]=Neochat
Name[ca@valencia]=Neochat
Name[da]=Neochat
Name[de]=Neochat
Name[en_GB]=Neochat
Name[es]=Neochat
Name[eu]=Neochat
Name[fi]=Neochat
Name[fr]=Neochat
Name[hu]=Neochat
Name[it]=Neochat
Name[nl]=Neochat
Name[nn]=Neochat
Name[pl]=Neochat
Name[pt]=Neochat
Name[pt_BR]=Neochat
Name[sk]=Neochat
Name[sl]=Neochat
Name[sv]=Neochat
Name[uk]=Neochat
Name[x-test]=xxNeochatxx
Name[zh_CN]=Neochat
GenericName=Matrix Client
GenericName[ca]=Client del Matrix
GenericName[ca@valencia]=Client del Matrix
GenericName[de]=Matrix-Programm
GenericName[en_GB]=Matrix Client
GenericName[es]=Cliente para Matrix
GenericName[eu]=Matrix bezeroa
GenericName[fi]=Matrix-asiakas
GenericName[fr]=Client « Matrix »
GenericName[hu]=Matrix kliens
GenericName[it]=Client Matrix
GenericName[nl]=Matrix-client
GenericName[nn]=Matrix-klient
GenericName[pl]=Program Matriksa
GenericName[pt]=Cliente de Matrix
GenericName[pt_BR]=Cliente Matrix
GenericName[sk]=Matrix Client
GenericName[sl]=Odjemalec Matrix
GenericName[sv]=Matrix-klient
GenericName[uk]=Клієнт Matrix
GenericName[x-test]=xxMatrix Clientxx
GenericName[zh_CN]=Matrix 客户端
Comment=Client for the Matrix protocol
Comment[ca]=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[es]=Cliente para el protocolo Matrix
Comment[eu]=Matrix protokolorako bezeroa
Comment[fi]=Asiakas Matrix-yhteyskäytännölle
Comment[fr]=Client pour le protocole « Matrix »
Comment[hu]=Kliens a Matrix protokollhoz
Comment[it]=Client per il protocollo Matrix
Comment[nl]=Client voor het Matrix-protocol
Comment[nn]=Lynmeldings­klient for Matrix-protokollen
Comment[pl]=Program obsługi protokołu Matriksa
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[sv]=Klient för protokollet Matrix
Comment[uk]=Клієнт протоколу Matrix
Comment[x-test]=xxClient for the Matrix protocolxx
Comment[zh_CN]=为 Matrix 协议打造的客户端
Exec=neochat
Terminal=false
Icon=neochat
Icon=org.kde.neochat
Type=Application
Categories=Network;InstantMessaging;

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -41,6 +41,7 @@ Kirigami.ApplicationWindow {
property alias pageStack: root.pageStack
property bool invitationOpen: false
property var roomList: null
property Item roomItem: null
readonly property bool hasOpenRoom: currentRoom !== null
@@ -51,8 +52,8 @@ Kirigami.ApplicationWindow {
if (Config.openRoom) {
const room = Controller.activeConnection.room(Config.openRoom);
currentRoom = room;
let item = pageStack.push(roomPage, { 'currentRoom': room, });
connectRoomToSignal(item);
roomItem = pageStack.push(roomPage, { 'currentRoom': room, });
connectRoomToSignal(roomItem);
} else {
// TODO create welcome page
}
@@ -61,16 +62,16 @@ Kirigami.ApplicationWindow {
function enterRoom(room) {
let item = null;
if (currentRoom != null || invitationOpen) {
currentRoom = null;
item = pageStack.replace(roomPage, { 'currentRoom': room, });
roomItem.currentRoom = room;
pageStack.currentIndex = pageStack.depth - 1;
} else {
item = pageStack.push(roomPage, { 'currentRoom': room, });
roomItem = pageStack.push(roomPage, { 'currentRoom': room, });
}
currentRoom = room;
Config.openRoom = room.id;
Config.save();
connectRoomToSignal(item);
return item;
connectRoomToSignal(roomItem);
return roomItem;
}
function openInvitation(room) {
@@ -135,14 +136,13 @@ Kirigami.ApplicationWindow {
text: i18n("Explore rooms")
icon.name: "compass"
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 {
text: i18n("Start a Chat")
icon.name: "irc-join-channel"
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/StartChatPage.qml", {"connection": Controller.activeConnection})
enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat")
enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat") && Controller.accountCount > 0
},
Kirigami.Action {
text: i18n("Create a Room")
@@ -151,14 +151,21 @@ Kirigami.ApplicationWindow {
let dialog = createRoomDialog.createObject(root.overlay);
dialog.open();
}
enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat")
shortcut: StandardKey.New
enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat") && Controller.accountCount > 0
},
Kirigami.Action {
text: i18n("Accounts")
icon.name: "im-user"
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 {
text: i18n("Settings")
@@ -172,6 +179,18 @@ Kirigami.ApplicationWindow {
icon.name: "help-about"
onTriggered: pushReplaceLayer(aboutPage)
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()
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 {

View File

@@ -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)
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
if(NEOCHAT_FLATPAK)
target_compile_definitions(neochat PRIVATE NEOCHAT_FLATPAK)
endif()
if (KQuickImageEditor_FOUND)
target_compile_definitions(neochat PRIVATE HAS_KQUICKIMAGEEDITOR)
endif()

View File

@@ -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) {
return;
@@ -177,6 +178,13 @@ void ChatDocumentHandler::postMessage(const QString &text, const QString &attach
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) {
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++) {
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;
}

View File

@@ -51,7 +51,7 @@ public:
[[nodiscard]] NeoChatRoom *room() const;
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
/// is the posibility to autocomplete it.

View File

@@ -64,7 +64,7 @@ Controller::Controller(QObject *parent)
#ifndef Q_OS_ANDROID
TrayIcon *trayIcon = new TrayIcon(this);
connect(trayIcon, &TrayIcon::showWindow, this, &Controller::showWindow);
trayIcon->setIconSource("neochat");
trayIcon->setIconSource("org.kde.neochat");
trayIcon->setIsOnline(true);
#endif
@@ -76,7 +76,6 @@ Controller::Controller(QObject *parent)
Controller::~Controller()
{
for (auto c : qAsConst(m_connections)) {
c->stopSync();
c->saveState();
}
}
@@ -106,11 +105,11 @@ void Controller::loginWithCredentials(const QString &serverAddr, const QString &
}
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:
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, "");
connect(conn, &Connection::connected, this, [this, conn, deviceName] {
AccountSettings account(conn->userId());
@@ -198,7 +197,7 @@ void Controller::logout(Connection *conn, bool serverSideLogout)
conn->stopSync();
Q_EMIT conn->stateChanged();
Q_EMIT conn->loggedOut();
if (!m_connections.isEmpty()) {
if (conn == activeConnection() && !m_connections.isEmpty()) {
setActiveConnection(m_connections[0]);
} else {
setActiveConnection(nullptr);
@@ -232,11 +231,18 @@ void Controller::addConnection(Connection *c)
dropConnection(c);
});
connect(c, &Connection::requestFailed, this, [=] (BaseJob *job) {
if(job->error() == BaseJob::UserConsentRequiredError) {
Q_EMIT userConsentRequired(job->errorUrl());
}
});
setBusy(true);
c->sync();
Q_EMIT connectionAdded(c);
Q_EMIT accountCountChanged();
}
void Controller::dropConnection(Connection *c)
@@ -245,50 +251,50 @@ void Controller::dropConnection(Connection *c)
m_connections.removeOne(c);
Q_EMIT connectionDropped(c);
Q_EMIT accountCountChanged();
c->deleteLater();
}
void Controller::invokeLogin()
{
const auto accounts = SettingsGroup("Accounts").childGroups();
QString id = NeoChatConfig::self()->activeConnection();
for (const auto &accountId : accounts) {
AccountSettings account {accountId};
if (id.isEmpty()) {
// handle case where the account config is empty
id = accountId;
}
if (!account.homeserver().isEmpty()) {
auto accessToken = loadAccessTokenFromKeyChain(account);
auto c = new Connection(account.homeserver(), this);
connect(c, &Connection::connected, this, [=] {
c->loadState();
addConnection(c);
});
connect(c, &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(c, false);
} else {
Q_EMIT errorOccured(i18n("Login Failed"), error);
logout(c, true);
auto connection = new Connection(account.homeserver(), this);
connect(connection, &Connection::connected, this, [=] {
connection->loadState();
addConnection(connection);
if (connection->userId() == id) {
setActiveConnection(connection);
Q_EMIT initiated();
}
});
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);
});
c->connectWithToken(account.userId(), accessToken, account.deviceId());
connection->connectWithToken(account.userId(), accessToken, account.deviceId());
}
}
if (!m_connections.isEmpty()) {
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]);
if (m_connections.isEmpty()) {
Q_EMIT initiated();
}
Q_EMIT initiated();
}
QByteArray Controller::loadAccessTokenFromFile(const AccountSettings &account)

View File

@@ -27,7 +27,7 @@ using namespace Quotient;
class Controller : public QObject
{
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(Connection *activeConnection READ activeConnection WRITE setActiveConnection NOTIFY activeConnectionChanged)
Q_PROPERTY(bool busy READ busy WRITE setBusy NOTIFY busyChanged)
@@ -103,6 +103,7 @@ Q_SIGNALS:
void syncDone();
void connectionAdded(Quotient::Connection *_t1);
void connectionDropped(Quotient::Connection *_t1);
void accountCountChanged();
void initiated();
void notificationClicked(const QString &_t1, const QString &_t2);
void quitOnLastWindowClosedChanged();
@@ -112,6 +113,7 @@ Q_SIGNALS:
void passwordStatus(Controller::PasswordStatus _t1);
void showWindow();
void openRoom(NeoChatRoom *room);
void userConsentRequired(QUrl url);
public Q_SLOTS:
void logout(Quotient::Connection *conn, bool serverSideLogout);

View File

@@ -58,6 +58,10 @@ int main(int argc, char *argv[])
QQuickStyle::setStyle(QStringLiteral("Material"));
#else
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
QApplication::setOrganizationName("KDE");
@@ -69,11 +73,19 @@ int main(int argc, char *argv[])
about.setOrganizationDomain("kde.org");
KAboutData::setApplicationData(about);
QApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("org.kde.neochat")));
#ifndef Q_OS_ANDROID
KDBusService service(KDBusService::Unique);
#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;
auto config = NeoChatConfig::self();

View File

@@ -54,6 +54,9 @@ ThumbnailResponse::ThumbnailResponse(QString id, QSize size)
void ThumbnailResponse::startRequest()
{
if (!Controller::instance().activeConnection()) {
return;
}
// Runs in the main thread, not QML thread
Q_ASSERT(QThread::currentThread() == Controller::instance().activeConnection()->thread());
job = Controller::instance().activeConnection()->getThumbnail(mediaId, requestedSize);
@@ -94,6 +97,9 @@ void ThumbnailResponse::prepareResult()
void ThumbnailResponse::doCancel()
{
if (!Controller::instance().activeConnection()) {
return;
}
// Runs in the main thread, not QML thread
if (job) {
Q_ASSERT(QThread::currentThread() == job->thread());

View File

@@ -44,6 +44,7 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
roles[ShowAuthorRole] = "showAuthor";
roles[ShowSectionRole] = "showSection";
roles[ReactionRole] = "reaction";
roles[IsEditedRole] = "isEdited";
return roles;
}
@@ -89,6 +90,9 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
m_currentRoom = room;
if (room) {
room->setDisplayed();
if (m_currentRoom->timelineSize() < 10) {
room->getPreviousContent(50);
}
lastReadEventId = room->readMarkerEventId();
using namespace Quotient;
@@ -301,6 +305,7 @@ inline QVariantMap userAtEvent(NeoChatUser *user, NeoChatRoom *room, const RoomE
{"avatarMediaId", user->avatarMediaId(room)},
{"avatarUrl", user->avatarUrl(room)},
{"displayName", user->displayname(room)},
{"display", user->name()},
{"color", user->color()},
{"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)) {
return EventStatus::Hidden;
}
@@ -439,6 +449,13 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
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) {
return !evt.id().isEmpty() ? evt.id() : evt.transactionId();
}

View File

@@ -39,6 +39,7 @@ public:
ShowSectionRole,
ReactionRole,
IsEditedRole,
// For debugging
EventResolvedTypeRole,

View File

@@ -161,7 +161,7 @@ const RoomMessageEvent *NeoChatRoom::lastEvent(bool ignoreStateEvent) const
QString NeoChatRoom::lastEventToString() const
{
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("");
}
@@ -283,7 +283,7 @@ QString NeoChatRoom::avatarMediaId() const
const auto dcUsers = directChatUsers();
for (const auto u : dcUsers) {
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();
result.replace("<!-- raw HTML omitted -->", "<br />");
result.replace(QRegularExpression("(<br />)*$"), "");
result.replace("<p>", "");
result.replace("</p>", "");

View File

@@ -256,6 +256,9 @@ QVariant RoomListModel::data(const QModelIndex &index, int role) const
}
NeoChatRoom *room = m_rooms.at(index.row());
if (role == NameRole) {
return !room->name().isEmpty() ? room->name() : room->displayName();
}
if (role == DisplayNameRole) {
return room->displayName();
}
if (role == AvatarRole) {
@@ -324,6 +327,7 @@ QHash<int, QByteArray> RoomListModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[NameRole] = "name";
roles[DisplayNameRole] = "displayName";
roles[AvatarRole] = "avatar";
roles[TopicRole] = "topic";
roles[CategoryRole] = "category";
@@ -352,7 +356,7 @@ QString RoomListModel::categoryName(int section)
case 5:
return i18n("Low priority");
default:
return i18n("Deadbeef");
return "Deadbeef";
}
}

View File

@@ -39,6 +39,7 @@ class RoomListModel : public QAbstractListModel
public:
enum EventRoles {
NameRole = Qt::UserRole + 1,
DisplayNameRole,
AvatarRole,
TopicRole,
CategoryRole,