Compare commits
83 Commits
work/fuf/d
...
1.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5837a208a | ||
|
|
3b45c0b2f7 | ||
|
|
e1a72816d0 | ||
|
|
aea46c547f | ||
|
|
fe009f764e | ||
|
|
8c14f8d2eb | ||
|
|
559da66197 | ||
|
|
759496620f | ||
|
|
1f7b1ad29f | ||
|
|
9338567137 | ||
|
|
75d2488dbc | ||
|
|
4bed6a07cd | ||
|
|
cb1c9d1869 | ||
|
|
76b2d4ac49 | ||
|
|
5fc931484e | ||
|
|
e60ed3b889 | ||
|
|
1314567fde | ||
|
|
047aa793e7 | ||
|
|
d60f8f4dfa | ||
|
|
20139c08d8 | ||
|
|
fb59bac165 | ||
|
|
5a65dc4d9a | ||
|
|
03ec17366e | ||
|
|
15137cd704 | ||
|
|
2aea0015ba | ||
|
|
f6fd099808 | ||
|
|
04082fc095 | ||
|
|
bc977c3fc6 | ||
|
|
c4c283c85a | ||
|
|
56f49fabf7 | ||
|
|
fe407a3421 | ||
|
|
bbe539885e | ||
|
|
ff978b9586 | ||
|
|
885b75e35f | ||
|
|
cb81eaf26f | ||
|
|
22732b801b | ||
|
|
ae3e395b47 | ||
|
|
96c91e2a35 | ||
|
|
e36204bbd8 | ||
|
|
1804140ac0 | ||
|
|
5a28a93ab6 | ||
|
|
76bd529c3c | ||
|
|
293288a0b6 | ||
|
|
51b6593f96 | ||
|
|
51e73568c4 | ||
|
|
e461e2098b | ||
|
|
79ceb45fae | ||
|
|
0c292b34ff | ||
|
|
db6640ba49 | ||
|
|
3827249f0c | ||
|
|
f40a3daef4 | ||
|
|
8d2608a230 | ||
|
|
3e5628def3 | ||
|
|
3b3673fdff | ||
|
|
d81e4c417d | ||
|
|
44d3f628d9 | ||
|
|
0db9c0454f | ||
|
|
e5c65a662e | ||
|
|
8913aa8a66 | ||
|
|
5db3e14ae6 | ||
|
|
c5a3fc0431 | ||
|
|
fc791d41fa | ||
|
|
127ad19109 | ||
|
|
066ea4f8bd | ||
|
|
cf60337b27 | ||
|
|
98672cf870 | ||
|
|
abd03299ec | ||
|
|
41b64f977c | ||
|
|
ac75dd57c0 | ||
|
|
1d3d61ed77 | ||
|
|
1e047a8ff1 | ||
|
|
530b4c24a0 | ||
|
|
ada7bcef65 | ||
|
|
ad4ca3ad9e | ||
|
|
4103c44eb5 | ||
|
|
dd4ed7539e | ||
|
|
52ad911b2d | ||
|
|
f09dff979e | ||
|
|
0476398f91 | ||
|
|
41993bfe24 | ||
|
|
b3d90ebf82 | ||
|
|
ef0a6e276c | ||
|
|
a104968a29 |
@@ -34,12 +34,12 @@ endif()
|
||||
# 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(1.1.80
|
||||
ecm_setup_version(1.2.0
|
||||
VARIABLE_PREFIX NEOCHAT
|
||||
VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/neochat-version.h
|
||||
)
|
||||
|
||||
find_package(Qt5 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Widgets Core Quick Gui QuickControls2 Multimedia Svg)
|
||||
find_package(Qt5 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Core Quick Gui QuickControls2 Multimedia Svg)
|
||||
set_package_properties(Qt5 PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Basic application components"
|
||||
@@ -67,6 +67,7 @@ if(ANDROID)
|
||||
PURPOSE "Encrypted communications"
|
||||
)
|
||||
else()
|
||||
find_package(Qt5 ${QT_MIN_VERSION} COMPONENTS Widgets)
|
||||
find_package(KF5QQC2DesktopStyle ${KF5_MIN_VERSION} REQUIRED)
|
||||
set_package_properties(KF5QQC2DesktopStyle PROPERTIES
|
||||
TYPE RUNTIME
|
||||
|
||||
@@ -53,7 +53,6 @@
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
|
||||
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
@@ -7,14 +7,16 @@
|
||||
|
||||
# first try to find cmark-config.cmake
|
||||
# path to a file not in the search path can be set with 'cmake -Dcmark_DIR=some/path/'
|
||||
find_package(cmark CONFIG)
|
||||
find_package(cmark CONFIG QUIET)
|
||||
if(cmark_FOUND AND TARGET cmark::cmark)
|
||||
# found it!
|
||||
return()
|
||||
endif()
|
||||
|
||||
include(FindPkgConfig)
|
||||
pkg_check_modules(PC_CMARK QUIET cmark)
|
||||
find_package(PkgConfig QUIET)
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PC_CMARK QUIET cmark)
|
||||
endif()
|
||||
|
||||
if(NOT CMARK_INCLUDE_DIR)
|
||||
find_path(CMARK_INCLUDE_DIR
|
||||
|
||||
@@ -76,7 +76,11 @@ ToolBar {
|
||||
* background colors being very different from the QPalette::Base color.
|
||||
* Luckily, none of the Qt QQC2 styles do that and neither do KDE's QQC2 styles.
|
||||
*/
|
||||
background: null
|
||||
background: MouseArea {
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: Qt.IBeamCursor
|
||||
z: 1
|
||||
}
|
||||
leftPadding: mirrored ? 0 : Kirigami.Units.largeSpacing
|
||||
rightPadding: !mirrored ? 0 : Kirigami.Units.largeSpacing
|
||||
topPadding: 0
|
||||
@@ -165,12 +169,18 @@ ToolBar {
|
||||
nextItemInFocusChain(false).forceActiveFocus(Qt.TabFocusReason)
|
||||
return
|
||||
}
|
||||
let decrementedIndex = completionMenu.currentIndex - 1
|
||||
// Wrap around to the last item
|
||||
if (decrementedIndex < 0) {
|
||||
decrementedIndex = Math.max(completionMenu.count - 1, 0) // 0 if count == 0
|
||||
if (!autoAppeared) {
|
||||
let decrementedIndex = completionMenu.currentIndex - 1
|
||||
// Wrap around to the last item
|
||||
if (decrementedIndex < 0) {
|
||||
decrementedIndex = Math.max(completionMenu.count - 1, 0) // 0 if count == 0
|
||||
}
|
||||
completionMenu.currentIndex = decrementedIndex
|
||||
} else {
|
||||
autoAppeared = false;
|
||||
}
|
||||
completionMenu.currentIndex = decrementedIndex
|
||||
|
||||
chatBar.complete();
|
||||
}
|
||||
|
||||
Keys.onTabPressed: {
|
||||
@@ -187,7 +197,7 @@ ToolBar {
|
||||
|
||||
// ignore first time tab was clicked so that user can select
|
||||
// first emoji/user
|
||||
if (autoAppeared === false) {
|
||||
if (!autoAppeared) {
|
||||
let incrementedIndex = completionMenu.currentIndex + 1;
|
||||
// Wrap around to the first item
|
||||
if (incrementedIndex > completionMenu.count - 1) {
|
||||
@@ -370,8 +380,10 @@ ToolBar {
|
||||
|
||||
function complete() {
|
||||
documentHandler.replaceAutoComplete(completionMenu.currentDisplayText);
|
||||
if (completionMenu.completionType === "username") {
|
||||
userAutocompleted[completionMenu.currentDisplayText] = completionMenu.currentUserId;
|
||||
if (completionMenu.completionType === ChatDocumentHandler.User
|
||||
&& completionMenu.currentDisplayText.length > 0
|
||||
&& completionMenu.currentItem.userId.length > 0) {
|
||||
userAutocompleted[completionMenu.currentDisplayText] = completionMenu.currentItem.userId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,6 +227,14 @@ Item {
|
||||
chatBar.inputFieldForceActiveFocusTriggered()
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: RoomManager
|
||||
|
||||
function onCurrentRoomChanged() {
|
||||
chatBar.userAutocompleted = {};
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: ChatBoxHelper
|
||||
|
||||
|
||||
@@ -22,8 +22,7 @@ Popup {
|
||||
property alias delegate: completionListView.delegate
|
||||
|
||||
// Autocomplee text
|
||||
property string currentDisplayText: currentItem && currentItem.displayName ? currentItem.displayName : ""
|
||||
property string currentUserId: currentItem && currentItem.id ? currentItem.id : ""
|
||||
property string currentDisplayText: currentItem && (currentItem.displayName ?? "")
|
||||
|
||||
property int completionType: ChatDocumentHandler.Emoji
|
||||
property int beginPosition: 0
|
||||
@@ -78,6 +77,7 @@ Popup {
|
||||
id: usernameItem
|
||||
width: ListView.view.width ?? implicitWidth
|
||||
property string displayName: modelData.displayName
|
||||
property string userId: modelData.id
|
||||
leading: Kirigami.Avatar {
|
||||
implicitHeight: Kirigami.Units.gridUnit
|
||||
implicitWidth: implicitHeight
|
||||
@@ -86,11 +86,6 @@ Popup {
|
||||
}
|
||||
text: modelData.displayName
|
||||
onClicked: completeTriggered();
|
||||
Component.onCompleted: {
|
||||
completionMenu.currentUserId = Qt.binding(() => {
|
||||
return modelData.id ?? "";
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.de>
|
||||
* SPDX-FileCopyrightText: 2020 Noah Davis <noahadvs@gmail.com>
|
||||
* SPDX-FileCopyrightText: 2021 Srevin Saju <srevinsaju@sugarlabs.org>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQuick.Controls 2.15
|
||||
import org.kde.kirigami 2.14 as Kirigami
|
||||
import org.kde.neochat 1.0
|
||||
|
||||
Loader {
|
||||
id: root
|
||||
property var typingNotification: null
|
||||
|
||||
active: visible
|
||||
sourceComponent: Pane {
|
||||
id: typingPane
|
||||
|
||||
padding: fontMetrics.lineSpacing * 0.25
|
||||
spacing: 0
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
|
||||
contentItem: RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
spacing: 0
|
||||
|
||||
FontMetrics {
|
||||
id: fontMetrics
|
||||
font: typingLabel.font
|
||||
}
|
||||
Label {
|
||||
id: typingLabel
|
||||
textFormat: TextEdit.RichText
|
||||
wrapMode: Label.Wrap
|
||||
text: typingNotification
|
||||
}
|
||||
BusyIndicator {
|
||||
running: root.active
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium
|
||||
}
|
||||
}
|
||||
|
||||
background: Item {
|
||||
Rectangle {
|
||||
height: 1
|
||||
property color borderColor: Kirigami.Theme.textColor
|
||||
color: Qt.rgba(borderColor.r, borderColor.g, borderColor.b, 0.1)
|
||||
anchors {
|
||||
left: typingIndicatorBackground.left
|
||||
right: typingIndicatorBackground.right
|
||||
bottom: typingIndicatorBackground.top
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
id: typingIndicatorBackground
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,4 +5,3 @@ ReplyPane 1.0 ReplyPane.qml
|
||||
AttachmentPane 1.0 AttachmentPane.qml
|
||||
CompletionMenu 1.0 CompletionMenu.qml
|
||||
EmojiPickerPane 1.0 EmojiPickerPane.qml
|
||||
TypingPane 1.0 TypingPane.qml
|
||||
|
||||
@@ -8,7 +8,7 @@ import QtQuick.Particles 2.15
|
||||
|
||||
import org.kde.kirigami 2.15 as Kirigami
|
||||
|
||||
Rectangle {
|
||||
Item {
|
||||
id: item
|
||||
property bool enabled: false
|
||||
property int effectInterval: Kirigami.Units.veryLongDuration*10;
|
||||
@@ -27,11 +27,6 @@ Rectangle {
|
||||
fireworksTimer.start()
|
||||
}
|
||||
|
||||
// backgroundColor
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
||||
Kirigami.Theme.inherit: false
|
||||
|
||||
// Confetti
|
||||
|
||||
Timer {
|
||||
|
||||
@@ -15,7 +15,7 @@ LoginStep {
|
||||
readonly property var homeserver: customHomeserver.visible ? customHomeserver.text : serverCombo.currentText
|
||||
property bool loading: false
|
||||
|
||||
title: i18n("@title", "Select a Homeserver")
|
||||
title: i18nc("@title", "Select a Homeserver")
|
||||
|
||||
action: Kirigami.Action {
|
||||
enabled: LoginHelper.homeserverReachable && !customHomeserver.visible || customHomeserver.acceptableInput
|
||||
|
||||
@@ -15,7 +15,7 @@ MouseArea {
|
||||
id: replyButton
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: replyName.implicitHeight + (loader.item ? loader.item.height : 0) + Kirigami.Units.largeSpacing
|
||||
implicitWidth: Math.min(bubbleMaxWidth, Math.max((loader.item ? loader.item.width : 0), replyName.implicitWidth)) + Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 3
|
||||
implicitWidth: Math.min(bubbleMaxWidth, Math.max((loader.item ? loader.item.width + Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing : 0), replyName.implicitWidth)) + Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 3
|
||||
Component.onCompleted: {
|
||||
parent.Layout.fillWidth = true;
|
||||
parent.Layout.preferredWidth = Qt.binding(function() { return implicitWidth; })
|
||||
@@ -32,7 +32,7 @@ MouseArea {
|
||||
id: avatatReply
|
||||
anchors.left: replyLeftBorder.right
|
||||
anchors.leftMargin: Kirigami.Units.smallSpacing
|
||||
width: Kirigami.Units.gridUnit
|
||||
width: visible ? Kirigami.Units.gridUnit : 0
|
||||
height: Kirigami.Units.gridUnit
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
@@ -79,7 +79,7 @@ MouseArea {
|
||||
textMessage: reply.display
|
||||
textFormat: Text.RichText
|
||||
wrapMode: Text.WordWrap
|
||||
width: Math.min(implicitWidth, bubbleMaxWidth) - Kirigami.Units.smallSpacing * 5 - avatatReply.width
|
||||
width: Math.min(implicitWidth, bubbleMaxWidth - Kirigami.Units.largeSpacing * 3)
|
||||
x: Kirigami.Units.smallSpacing * 3 + avatatReply.width
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ QQC2.ItemDelegate {
|
||||
property bool isEmote: false
|
||||
property bool cardBackground: true
|
||||
|
||||
readonly property int bubbleMaxWidth: Math.min(width - Kirigami.Units.gridUnit * 2 - Kirigami.Units.largeSpacing * 4, Kirigami.Units.gridUnit * 20)
|
||||
readonly property int bubbleMaxWidth: !Config.showAvatarInTimeline ? width : Math.min(width - Kirigami.Units.gridUnit * 2 - Kirigami.Units.largeSpacing * 4, Kirigami.Units.gridUnit * 20)
|
||||
|
||||
signal saveFileAs()
|
||||
signal openExternally()
|
||||
@@ -48,20 +48,20 @@ QQC2.ItemDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
height: sectionDelegate.height + Math.max(avatar.height, bubble.implicitHeight) + loader.height + (model.showAuthor ? Kirigami.Units.smallSpacing : 0)
|
||||
height: sectionDelegate.height + Math.max(avatar.height, bubble.implicitHeight) + loader.height + (model.showAuthor ? Kirigami.Units.smallSpacing : 0) - (Config.showAvatarInTimeline ? 0 : Kirigami.Units.largeSpacing)
|
||||
|
||||
SectionDelegate {
|
||||
id: sectionDelegate
|
||||
width: parent.width
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Kirigami.Units.gridUnit * 2 + Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing
|
||||
anchors.left: avatar.left
|
||||
anchors.leftMargin: Kirigami.Units.smallSpacing
|
||||
visible: model.showSection
|
||||
height: visible ? implicitHeight : 0
|
||||
}
|
||||
|
||||
Kirigami.Avatar {
|
||||
id: avatar
|
||||
width: Kirigami.Units.gridUnit * 2
|
||||
width: visible || Config.showAvatarInTimeline ? Kirigami.Units.gridUnit * 2 : 0
|
||||
height: width
|
||||
sourceSize.width: width
|
||||
sourceSize.height: width
|
||||
@@ -74,7 +74,7 @@ QQC2.ItemDelegate {
|
||||
|
||||
visible: model.showAuthor && Config.showAvatarInTimeline
|
||||
name: model.author.name ?? model.author.displayName
|
||||
source: model.author.avatarMediaId ? ("image://mxc/" + model.author.avatarMediaId) : ""
|
||||
source: visible && model.author.avatarMediaId ? ("image://mxc/" + model.author.avatarMediaId) : ""
|
||||
color: model.author.color
|
||||
|
||||
MouseArea {
|
||||
@@ -94,7 +94,7 @@ QQC2.ItemDelegate {
|
||||
|
||||
QQC2.Control {
|
||||
id: bubble
|
||||
topPadding: Kirigami.Units.largeSpacing
|
||||
topPadding: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
|
||||
bottomPadding: 0
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
@@ -103,6 +103,8 @@ QQC2.ItemDelegate {
|
||||
top: avatar.top
|
||||
left: avatar.right
|
||||
leftMargin: Kirigami.Units.smallSpacing
|
||||
right: Config.showAvatarInTimeline ? undefined : parent.right
|
||||
rightMargin: Config.showAvatarInTimeline ? undefined : Kirigami.Units.largeSpacing
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
@@ -112,10 +114,10 @@ QQC2.ItemDelegate {
|
||||
id: rowLayout
|
||||
visible: model.showAuthor && !isEmote
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.preferredWidth: nameLabel.implicitWidth + timeLabel.implicitWidth + Kirigami.Units.largeSpacing
|
||||
Layout.maximumWidth: bubbleMaxWidth - Kirigami.Units.largeSpacing * 2
|
||||
Layout.maximumWidth: bubbleMaxWidth
|
||||
implicitHeight: visible ? nameLabel.implicitHeight : 0
|
||||
|
||||
QQC2.Label {
|
||||
@@ -131,6 +133,19 @@ QQC2.ItemDelegate {
|
||||
font.weight: Font.Bold
|
||||
color: author.color
|
||||
wrapMode: Text.Wrap
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
userDetailDialog.createObject(QQC2.ApplicationWindow.overlay, {
|
||||
room: currentRoom,
|
||||
user: author.object,
|
||||
displayName: author.displayName,
|
||||
avatarMediaId: author.avatarMediaId,
|
||||
avatarUrl: author.avatarUrl
|
||||
}).open();
|
||||
}
|
||||
}
|
||||
}
|
||||
QQC2.Label {
|
||||
id: timeLabel
|
||||
@@ -157,7 +172,7 @@ QQC2.ItemDelegate {
|
||||
}
|
||||
|
||||
background: Kirigami.ShadowedRectangle {
|
||||
visible: cardBackground
|
||||
visible: cardBackground && Config.showAvatarInTimeline
|
||||
color: model.isHighlighted ? Kirigami.Theme.positiveBackgroundColor : Kirigami.Theme.backgroundColor
|
||||
radius: Kirigami.Units.smallSpacing
|
||||
shadow.size: Kirigami.Units.smallSpacing
|
||||
@@ -170,13 +185,12 @@ QQC2.ItemDelegate {
|
||||
Loader {
|
||||
id: loader
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: Kirigami.Units.gridUnit * 2 + Kirigami.Units.largeSpacing * 2
|
||||
left: bubble.left
|
||||
right: parent.right
|
||||
top: bubble.bottom
|
||||
topMargin: active ? Kirigami.Units.smallSpacing : 0
|
||||
topMargin: active && Config.showAvatarInTimeline ? Kirigami.Units.smallSpacing : 0
|
||||
}
|
||||
height: active ? item.implicitHeight + Kirigami.Units.smallSpacing : 0
|
||||
height: active ? item.implicitHeight : 0
|
||||
//Layout.bottomMargin: readMarker ? Kirigami.Units.smallSpacing : 0
|
||||
active: eventType !== "state" && eventType !== "notice" && reaction != undefined && reaction.length > 0
|
||||
visible: active
|
||||
|
||||
106
imports/NeoChat/Component/TypingPane.qml
Normal file
106
imports/NeoChat/Component/TypingPane.qml
Normal file
@@ -0,0 +1,106 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.de>
|
||||
* SPDX-FileCopyrightText: 2020 Noah Davis <noahadvs@gmail.com>
|
||||
* SPDX-FileCopyrightText: 2021 Srevin Saju <srevinsaju@sugarlabs.org>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQuick.Controls 2.15
|
||||
import org.kde.kirigami 2.14 as Kirigami
|
||||
import org.kde.neochat 1.0
|
||||
|
||||
Loader {
|
||||
id: root
|
||||
property string labelText: ""
|
||||
|
||||
active: visible
|
||||
sourceComponent: Pane {
|
||||
id: typingPane
|
||||
|
||||
leftPadding: Kirigami.Units.largeSpacing
|
||||
rightPadding: Kirigami.Units.largeSpacing
|
||||
topPadding: Kirigami.Units.smallSpacing
|
||||
bottomPadding: Kirigami.Units.smallSpacing
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
FontMetrics {
|
||||
id: fontMetrics
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: typingPane.spacing
|
||||
Row {
|
||||
id: dotRow
|
||||
property int duration: 400
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
Repeater {
|
||||
model: 3
|
||||
delegate: Rectangle {
|
||||
id: dot
|
||||
color: Kirigami.Theme.textColor
|
||||
radius: height/2
|
||||
implicitWidth: fontMetrics.xHeight
|
||||
implicitHeight: fontMetrics.xHeight
|
||||
// rotating 45 degrees makes the dots look a bit smoother when scaled up
|
||||
rotation: 45
|
||||
opacity: 0.5
|
||||
scale: 1
|
||||
// FIXME: Sometimes the animation timings for each
|
||||
// dot drift slightly reletative to each other.
|
||||
// Not everyone can see this, but I'm pretty sure it's there.
|
||||
SequentialAnimation {
|
||||
running: true
|
||||
PauseAnimation { duration: dotRow.duration * index / 2 }
|
||||
SequentialAnimation {
|
||||
loops: Animation.Infinite
|
||||
ParallelAnimation {
|
||||
// Animators unfortunately sync up instead of being
|
||||
// staggered, so I'm using NumberAnimations instead.
|
||||
NumberAnimation {
|
||||
target: dot; property: "scale";
|
||||
from: 1; to: 1.33
|
||||
duration: dotRow.duration
|
||||
}
|
||||
NumberAnimation {
|
||||
target: dot; property: "opacity"
|
||||
from: 0.5; to: 1
|
||||
duration: dotRow.duration
|
||||
}
|
||||
}
|
||||
ParallelAnimation {
|
||||
NumberAnimation {
|
||||
target: dot; property: "scale"
|
||||
from: 1.33; to: 1
|
||||
duration: dotRow.duration
|
||||
}
|
||||
NumberAnimation {
|
||||
target: dot; property: "opacity"
|
||||
from: 1; to: 0.5
|
||||
duration: dotRow.duration
|
||||
}
|
||||
}
|
||||
PauseAnimation { duration: dotRow.duration }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Label {
|
||||
id: typingLabel
|
||||
elide: Text.ElideRight
|
||||
text: root.labelText
|
||||
}
|
||||
}
|
||||
|
||||
leftInset: !mirrored ? 0 : -background.radius
|
||||
rightInset: mirrored ? 0 : -background.radius
|
||||
bottomInset: -background.radius
|
||||
background: Rectangle {
|
||||
radius: 3
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
border.color: Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, 0.2)
|
||||
border.width: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,3 +2,4 @@ module NeoChat.Component
|
||||
FullScreenImage 1.0 FullScreenImage.qml
|
||||
ChatTextInput 1.0 ChatTextInput.qml
|
||||
FancyEffectsContainer 1.0 FancyEffectsContainer.qml
|
||||
TypingPane 1.0 TypingPane.qml
|
||||
|
||||
@@ -104,7 +104,7 @@ Kirigami.OverlaySheet {
|
||||
}
|
||||
}
|
||||
Kirigami.BasicListItem {
|
||||
visible: user !== room.localUser && room.canSendState("kick")
|
||||
visible: user !== room.localUser && room.canSendState("kick") && room.containsUser(user.id)
|
||||
|
||||
action: Kirigami.Action {
|
||||
text: i18n("Kick this user")
|
||||
@@ -116,14 +116,14 @@ Kirigami.OverlaySheet {
|
||||
}
|
||||
}
|
||||
Kirigami.BasicListItem {
|
||||
visible: user !== room.localUser && room.canSendState("ban")
|
||||
visible: user !== room.localUser && room.canSendState("ban") && !room.isUserBanned(user.id)
|
||||
|
||||
action: Kirigami.Action {
|
||||
text: i18n("Ban this user")
|
||||
icon.name: "im-ban-user"
|
||||
icon.color: Kirigami.Theme.negativeTextColor
|
||||
onTriggered: {
|
||||
room.banMember(user.id)
|
||||
room.ban(user.id)
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
@@ -133,7 +133,7 @@ Kirigami.OverlaySheet {
|
||||
text: i18n("Open a private chat")
|
||||
icon.name: "document-send"
|
||||
onTriggered: {
|
||||
Controller.activeConnection.requestDirectChat(user)
|
||||
Controller.openOrCreateDirectChat(user);
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +125,8 @@ Kirigami.ScrollablePage {
|
||||
sortFilterRoomListModel.index(index, 0)), ItemSelectionModel.SelectCurrent)
|
||||
}
|
||||
}
|
||||
Keys.onEnterPressed: enterRoomAction.trigger()
|
||||
Keys.onReturnPressed: enterRoomAction.trigger()
|
||||
bold: unreadCount > 0
|
||||
label: name ?? ""
|
||||
subtitle: {
|
||||
@@ -202,7 +204,7 @@ Kirigami.ScrollablePage {
|
||||
}
|
||||
delegate: Kirigami.BasicListItem {
|
||||
checkable: true
|
||||
checked: Controller.activeConnection.localUser.id === model.user.id
|
||||
checked: Controller.activeConnection && Controller.activeConnection.localUser.id === model.user.id
|
||||
onClicked: Controller.activeConnection = model.connection
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
@@ -23,13 +23,18 @@ Kirigami.ScrollablePage {
|
||||
|
||||
/// It's not readonly because of the seperate window view.
|
||||
property var currentRoom: RoomManager.currentRoom
|
||||
/// Used to determine if scrolling to the bottom should mark the message as unread
|
||||
property bool hasScrolledUpBefore: false;
|
||||
|
||||
title: currentRoom.displayName
|
||||
|
||||
signal switchRoomUp()
|
||||
signal switchRoomDown()
|
||||
|
||||
onCurrentRoomChanged: ChatBoxHelper.clearEditReply()
|
||||
onCurrentRoomChanged: {
|
||||
hasScrolledUpBefore = false;
|
||||
ChatBoxHelper.clearEditReply()
|
||||
}
|
||||
|
||||
ActionsHandler {
|
||||
id: actionsHandler
|
||||
@@ -221,7 +226,6 @@ Kirigami.ScrollablePage {
|
||||
visible: !invitation.visible
|
||||
|
||||
readonly property int largestVisibleIndex: count > 0 ? indexAt(contentX + (width / 2), contentY + height - 1) : -1
|
||||
readonly property bool noNeedMoreContent: !currentRoom || currentRoom.eventsHistoryJob || currentRoom.allHistoryLoaded
|
||||
readonly property bool isLoaded: page.width * page.height > 10
|
||||
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
@@ -232,20 +236,38 @@ Kirigami.ScrollablePage {
|
||||
|
||||
model: !isLoaded ? undefined : sortedMessageEventModel
|
||||
|
||||
onContentYChanged: fetchMoreContent()
|
||||
|
||||
function fetchMoreContent() {
|
||||
if(!noNeedMoreContent && contentY - 5000 < originY) {
|
||||
currentRoom.getPreviousContent(20);
|
||||
}
|
||||
}
|
||||
|
||||
MessageEventModel {
|
||||
id: messageEventModel
|
||||
|
||||
room: currentRoom
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 1000
|
||||
running: messageListView.atYBeginning
|
||||
triggeredOnStart: true
|
||||
onTriggered: {
|
||||
if (messageListView.atYBeginning && messageEventModel.canFetchMore(messageEventModel.index(0, 0))) {
|
||||
messageEventModel.fetchMore(messageEventModel.index(0, 0));
|
||||
}
|
||||
}
|
||||
repeat: true
|
||||
}
|
||||
|
||||
// HACK: The view should do this automatically but doesn't.
|
||||
onAtYBeginningChanged: if (atYBeginning && messageEventModel.canFetchMore(messageEventModel.index(0, 0))) {
|
||||
messageEventModel.fetchMore(messageEventModel.index(0, 0));
|
||||
}
|
||||
|
||||
onAtYEndChanged: if (atYEnd && hasScrolledUpBefore) {
|
||||
if (QQC2.ApplicationWindow.window.visibility !== QQC2.ApplicationWindow.Hidden) {
|
||||
currentRoom.markAllMessagesAsRead();
|
||||
}
|
||||
hasScrolledUpBefore = false;
|
||||
} else if (!atYEnd) {
|
||||
hasScrolledUpBefore = true;
|
||||
}
|
||||
|
||||
QQC2.Popup {
|
||||
anchors.centerIn: parent
|
||||
|
||||
@@ -343,9 +365,10 @@ Kirigami.ScrollablePage {
|
||||
|
||||
innerObject: TextDelegate {
|
||||
isEmote: true
|
||||
Layout.fillWidth: !Config.showAvatarInTimeline
|
||||
Layout.maximumWidth: emoteContainer.bubbleMaxWidth
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
|
||||
Layout.bottomMargin: Kirigami.Units.largeSpacing * 2
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.RightButton
|
||||
@@ -369,10 +392,11 @@ Kirigami.ScrollablePage {
|
||||
hoverComponent: hoverActions
|
||||
|
||||
innerObject: TextDelegate {
|
||||
Layout.fillWidth: !Config.showAvatarInTimeline
|
||||
Layout.maximumWidth: messageContainer.bubbleMaxWidth
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.bottomMargin: Kirigami.Units.largeSpacing
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.RightButton
|
||||
onTapped: openMessageContext(author, model.message, eventId, toolTip, eventType, model.formattedBody)
|
||||
@@ -394,9 +418,10 @@ Kirigami.ScrollablePage {
|
||||
onReplyClicked: goToEvent(eventID)
|
||||
|
||||
innerObject: TextDelegate {
|
||||
Layout.fillWidth: !Config.showAvatarInTimeline
|
||||
Layout.maximumWidth: noticeContainer.bubbleMaxWidth
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
|
||||
Layout.bottomMargin: Kirigami.Units.largeSpacing * 2
|
||||
}
|
||||
}
|
||||
@@ -550,7 +575,9 @@ Kirigami.ScrollablePage {
|
||||
Timer {
|
||||
id: makeMeDisapearTimer
|
||||
interval: Kirigami.Units.humanMoment * 2
|
||||
onTriggered: currentRoom.markAllMessagesAsRead();
|
||||
onTriggered: if (QQC2.ApplicationWindow.window.visibility !== QQC2.ApplicationWindow.Hidden) {
|
||||
currentRoom.markAllMessagesAsRead();
|
||||
}
|
||||
}
|
||||
|
||||
ListView.onPooled: makeMeDisapearTimer.stop()
|
||||
@@ -577,7 +604,9 @@ Kirigami.ScrollablePage {
|
||||
|
||||
if (view.atYEnd) {
|
||||
// easy case just mark everything as read
|
||||
currentRoom.markAllMessagesAsRead();
|
||||
if (QQC2.ApplicationWindow.window.visibility !== QQC2.ApplicationWindow.Hidden) {
|
||||
currentRoom.markAllMessagesAsRead();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -622,7 +651,7 @@ Kirigami.ScrollablePage {
|
||||
QQC2.RoundButton {
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: Kirigami.Units.largeSpacing
|
||||
anchors.bottomMargin: Kirigami.Units.largeSpacing + messageListView.headerItem.height
|
||||
anchors.rightMargin: Kirigami.Units.largeSpacing
|
||||
implicitWidth: Kirigami.Units.gridUnit * 2
|
||||
implicitHeight: Kirigami.Units.gridUnit * 2
|
||||
@@ -633,6 +662,7 @@ Kirigami.ScrollablePage {
|
||||
action: Kirigami.Action {
|
||||
onTriggered: {
|
||||
goToLastMessage();
|
||||
currentRoom.markAllMessagesAsRead();
|
||||
}
|
||||
icon.name: "go-down"
|
||||
}
|
||||
@@ -643,10 +673,10 @@ Kirigami.ScrollablePage {
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
updateReadMarker()
|
||||
if (currentRoom) {
|
||||
if (currentRoom.timelineSize < 20)
|
||||
currentRoom.getPreviousContent(50)
|
||||
if (currentRoom.timelineSize < 20) {
|
||||
currentRoom.getPreviousContent(50);
|
||||
}
|
||||
}
|
||||
|
||||
positionViewAtBeginning();
|
||||
@@ -705,8 +735,12 @@ Kirigami.ScrollablePage {
|
||||
header: TypingPane {
|
||||
id: typingPane
|
||||
visible: !loadingIndicator.visible && currentRoom && currentRoom.usersTyping.length > 0
|
||||
typingNotification: visible ? i18ncp("Message displayed when some users are typing", "%2 is typing", "%2 are typing", currentRoom.usersTyping.length, currentRoom.usersTyping.map(user => user.displayName).join(", ")) : ""
|
||||
width: parent.width
|
||||
labelText: visible ? i18ncp(
|
||||
"Message displayed when some users are typing", "%2 is typing", "%2 are typing",
|
||||
currentRoom.usersTyping.length,
|
||||
currentRoom.usersTyping.map(user => user.displayName).join(", ")
|
||||
) : ""
|
||||
anchors.right: parent.right
|
||||
height: visible ? implicitHeight : 0
|
||||
Behavior on height {
|
||||
NumberAnimation {
|
||||
@@ -747,6 +781,7 @@ Kirigami.ScrollablePage {
|
||||
|
||||
background: FancyEffectsContainer {
|
||||
id: fancyEffectsContainer
|
||||
z: 100
|
||||
|
||||
enabled: Config.showFancyEffects
|
||||
|
||||
@@ -847,7 +882,6 @@ Kirigami.ScrollablePage {
|
||||
|
||||
/// Open context menu for normal message
|
||||
function openMessageContext(author, message, eventId, source, eventType, formattedBody) {
|
||||
console.log("message", message)
|
||||
const contextMenu = messageDelegateContextMenu.createObject(page, {
|
||||
author: author,
|
||||
message: message,
|
||||
|
||||
@@ -157,6 +157,16 @@ Kirigami.OverlayDrawer {
|
||||
}
|
||||
}
|
||||
|
||||
Pane {
|
||||
padding: Kirigami.Units.smallSpacing
|
||||
implicitWidth: parent.width
|
||||
z: 2
|
||||
contentItem: Kirigami.SearchField {
|
||||
id: userListSearchField
|
||||
onAccepted: sortedMessageEventModel.filterString = text;
|
||||
}
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
@@ -168,16 +178,6 @@ Kirigami.OverlayDrawer {
|
||||
boundsBehavior: Flickable.DragOverBounds
|
||||
activeFocusOnTab: true
|
||||
|
||||
header: Pane {
|
||||
padding: Kirigami.Units.smallSpacing
|
||||
implicitWidth: parent.width
|
||||
z: 2
|
||||
contentItem: Kirigami.SearchField {
|
||||
id: userListSearchField
|
||||
onTextChanged: sortedMessageEventModel.filterString = text;
|
||||
}
|
||||
}
|
||||
|
||||
model: KSortFilterProxyModel {
|
||||
id: sortedMessageEventModel
|
||||
|
||||
|
||||
@@ -8,41 +8,44 @@ Name[cs]=NeoChat
|
||||
Name[de]=NeoChat
|
||||
Name[en_GB]=NeoChat
|
||||
Name[es]=NeoChat
|
||||
Name[eu]=NeoChat
|
||||
Name[fi]=NeoChat
|
||||
Name[fr]=NeoChat
|
||||
Name[ia]=Neochat
|
||||
Name[it]=NeoChat
|
||||
Name[ko]=NeoChat
|
||||
Name[nl]=NeoChat
|
||||
Name[nn]=NeoChat
|
||||
Name[pa]=ਨਿਓ-ਚੈਟ
|
||||
Name[pl]=NeoChat
|
||||
Name[pt]=NeoChat
|
||||
Name[pt_BR]=NeoChat
|
||||
Name[ro]=NeoChat
|
||||
Name[sk]=NeoChat
|
||||
Name[sl]=NeoChat
|
||||
Name[sv]=NeoChat
|
||||
Name[ta]=நியோச்சாட்
|
||||
Name[uk]=NeoChat
|
||||
Name[x-test]=xxNeoChatxx
|
||||
Name[zh_CN]=NeoChat
|
||||
DesktopEntry=org.kde.neochat
|
||||
Comment=A client for matrix, the decentralized communication protocol
|
||||
Comment[az]=Matrix üçün müştəri, mərkəzləşməmiş kommunikasiya protokolu
|
||||
Comment[ca]=Un client per a Matrix, el protocol de comunicacions descentralitzat
|
||||
Comment[ca@valencia]=Un client per a Matrix, el protocol de comunicacions descentralitzat
|
||||
Comment[ca]=Un client per al Matrix, el protocol de comunicacions descentralitzat
|
||||
Comment[ca@valencia]=Un client per al Matrix, el protocol de comunicacions descentralitzat
|
||||
Comment[cs]=Klient pro decentralizovaný komunikační protokol matrix
|
||||
Comment[de]=Ein Programm für Matrix, das dezentrale Kommunikationsprotokoll
|
||||
Comment[en_GB]=A client for matrix, the decentralised communication protocol
|
||||
Comment[es]=Un cliente para Matrix, el protocolo de comunicaciones descentralizado
|
||||
Comment[eu]=Matrix, deszentralizatutako komunikazio protokolorako, bezero bat
|
||||
Comment[fi]=Hajautetun Matrix-viestintäyhteyskäytännön asiakasohjelma
|
||||
Comment[fr]=Un client pour « Matrix », le protocole décentralisé de communications.
|
||||
Comment[hu]=Kliens a matrixhoz, a decentralizált kommunikációs protokollhoz
|
||||
Comment[ia]=Un cliente per Matrix, le protocollo de communication decentralisate
|
||||
Comment[ia]=Un cliente per matrix, le protocollo de communication decentralisate
|
||||
Comment[it]=Un client per matrix, il protocollo di comunicazione decentralizzato
|
||||
Comment[ko]=Matrix, 분산 대화 프로토콜 클라이언트
|
||||
Comment[nl]=Een client voor matrix, het gedecentraliseerde communicatieprotocol
|
||||
Comment[nn]=Klient for Matrix, den desentraliserte lynmeldingsprotokollen.
|
||||
Comment[pa]=ਮੈਟਰਿਕਸ, ਸਰਬ-ਸਾਂਝੇ ਸੰਚਾਰ ਪਰੋਟੋਕਾਲ, ਲਈ ਕਲਾਈਂਟ ਹੈ
|
||||
Comment[pl]=Program do obsługi matriksa, rozproszonego protokołu porozumiewania się
|
||||
Comment[pt]=Um cliente para o Matrix, o protocolo descentralizado de comunicações
|
||||
Comment[pt_BR]=Um cliente para o Matrix, o protocolo de comunicação decentralizado
|
||||
Comment[ro]=Client pentru Matrix, protocolul de comunicare descentralizată
|
||||
Comment[sk]=Klient pre matrix, decentralizovaný komunikačný protokol
|
||||
@@ -68,6 +71,7 @@ Name[fr]=Nouveau message
|
||||
Name[hu]=Új üzenet
|
||||
Name[ia]=Nove message
|
||||
Name[it]=Nuovo messaggio
|
||||
Name[ko]=새 메시지
|
||||
Name[nl]=Nieuw bericht
|
||||
Name[nn]=Ny melding
|
||||
Name[pa]=ਨਵਾਂ ਸੁਨੇਹਾ
|
||||
@@ -93,8 +97,9 @@ 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[ia]=Il ha un nove message
|
||||
Comment[ia]=Isto es un nove message
|
||||
Comment[it]=È presente un nuovo messaggio
|
||||
Comment[ko]=새 메시지가 있음
|
||||
Comment[nl]=Er is een nieuw bericht
|
||||
Comment[nn]=Du har ei ny melding
|
||||
Comment[pa]=ਨਵਾਂ ਸੁਨੇਹਾ ਹੈ
|
||||
|
||||
@@ -12,22 +12,24 @@
|
||||
<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="ia">Neochat</name>
|
||||
<name xml:lang="id">NeoChat</name>
|
||||
<name xml:lang="it">NeoChat</name>
|
||||
<name xml:lang="ko">NeoChat</name>
|
||||
<name xml:lang="nl">NeoChat</name>
|
||||
<name xml:lang="nn">NeoChat</name>
|
||||
<name xml:lang="pa">ਨਿਓ-ਚੈਟ</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>
|
||||
<name xml:lang="ta">நியோச்சாட்</name>
|
||||
<name xml:lang="uk">NeoChat</name>
|
||||
<name xml:lang="x-test">xxNeoChatxx</name>
|
||||
<name xml:lang="zh-CN">NeoChat</name>
|
||||
<summary>A client for matrix, the decentralized communication protocol</summary>
|
||||
<summary xml:lang="az">Matrix üçün müştəri, mərkəzləşməmiş kommunikasiya protokolu</summary>
|
||||
<summary xml:lang="ca">Un client per al Matrix, el protocol de comunicacions descentralitzat</summary>
|
||||
@@ -40,9 +42,10 @@
|
||||
<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="ia">Un cliente per Matrix, le protocollo de communication decentralisate</summary>
|
||||
<summary xml:lang="ia">Un cliente per matrix, le protocollo de communication decentralisate</summary>
|
||||
<summary xml:lang="id">Klien untuk matrix, protokol komunikasi terdesentralisasi</summary>
|
||||
<summary xml:lang="it">Un client per matrix, il protocollo di comunicazione decentralizzato</summary>
|
||||
<summary xml:lang="ko">Matrix, 분산 대화 프로토콜 클라이언트</summary>
|
||||
<summary xml:lang="nl">Een client voor matrix, het gedecentraliseerde communicatieprotocol</summary>
|
||||
<summary xml:lang="nn">Ein klient for Matrix, den desentraliserte lynmeldingsprotokollen</summary>
|
||||
<summary xml:lang="pa">ਮੈਟਰਿਕਸ, ਸਰਬ-ਸਾਂਝੇ ਸੰਚਾਰ ਪਰੋਟੋਕਾਲ, ਲਈ ਕਲਾਈਂਟ ਹੈ</summary>
|
||||
@@ -54,33 +57,74 @@
|
||||
<summary xml:lang="sv">En klient för Matrix, det decentraliserade kommunikationsprotokollet</summary>
|
||||
<summary xml:lang="uk">Клієнт matrix, децентралізованого протоколу обміну даними</summary>
|
||||
<summary xml:lang="x-test">xxA client for matrix, the decentralized communication protocolxx</summary>
|
||||
<summary xml:lang="zh-CN">分布式通讯协议 Matrix 的客户端</summary>
|
||||
<description>
|
||||
<p>A client for matrix, the decentralized communication protocol.</p>
|
||||
<p xml:lang="az">Matrix üçün müştəri, mərkəzləşməmiş kommunikasiya protokolu.</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="cs">Klient pro decentralizovaný komunikační protokol matrix.</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="ia">Un cliente per Matrix, le protocollo de communication decentralisate.</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 lynmeldingsprotokollen.</p>
|
||||
<p xml:lang="pa">ਮੈਟਰਿਕਸ, ਸਰਬ-ਸਾਂਝੇ ਸੰਚਾਰ ਪਰੋਟੋਕਾਲ, ਲਈ ਕਲਾਈਂਟ ਹੈ।</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>
|
||||
<p xml:lang="x-test">xxA client for matrix, the decentralized communication protocol.xx</p>
|
||||
<p>NeoChat is a Matrix client. It allows you to send text messages, videos and audio files to your family, colleagues and friends using the Matrix protocol.</p>
|
||||
<p xml:lang="az">NeoChat Mtrix müştərisidir. O, Matrix protokolundan istifadə edərək, ailənizə, dostlarınıza, iş yoldaşlarınıza mətn, səsli və görüntülü ismarıclar göndərməyə imkan verir.</p>
|
||||
<p xml:lang="ca">El NeoChat és un client de Matrix. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics usant el protocol Matrix.</p>
|
||||
<p xml:lang="ca-valencia">El NeoChat és un client de Matrix. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics usant el protocol Matrix.</p>
|
||||
<p xml:lang="de">NeoChat ist ein Matrix-Client. Er ermöglicht Ihnen das Senden von Textnachrichten, Videos und Audiodateien an Ihre Familie, Kollegen und Freunde unter Verwendung des Matrix-Protokolls.</p>
|
||||
<p xml:lang="en-GB">NeoChat is a Matrix client. It allows you to send text messages, videos and audio files to your family, colleagues and friends using the Matrix protocol.</p>
|
||||
<p xml:lang="es">NeoChat es un cliente para Matrix. Le permite enviar mensajes de texto, vídeos y archivos de sonido a su familia, compañeros de trabajo y amigos usando el protocolo Matrix.</p>
|
||||
<p xml:lang="eu">NeoChat Matrix bezero bat da. Familiari, lankideei eta lagunei testu-mezuak, bideoak eta audio-fitxategiak bidaltzeko aukera ematen du, Matrix protokoloa erabiliz.</p>
|
||||
<p xml:lang="fi">NeoChat on Matrix-asiakas. Sillä voi lähettää perheelle, tuttaville ja kavereille tekstiviestejä sekä video- ja äänitiedostoja Matrix-yhteyskäytännöllä.</p>
|
||||
<p xml:lang="fr">NeoChat est un client Matrix. Il vous permet d'envoyer des messages de texte, des vidéos et des fichiers audio à votre famille, vos collègues et vos amis en utilisant le protocole Matrix.</p>
|
||||
<p xml:lang="ia">NeoChat es un cliente de Matrix. Illo te permitte inviar messager de texto, files de video e audio a tu familia, collegas e amicos usante le protocollo de Matrix.</p>
|
||||
<p xml:lang="it">NeoChat è un client Matrix. Ti consente di inviare messaggi di testo, file video e audio a familiari, colleghi e amici utilizzando il protocollo Matrix.</p>
|
||||
<p xml:lang="ko">NeoChat은 Matrix 클라이언트입니다. Matrix 프로토콜을 사용하여 가족, 동료, 친구에게 텍스트 메시지, 동영상, 오디오 파일을 전송할 수 있습니다.</p>
|
||||
<p xml:lang="nl">NeoChat is een Matrix-client. Het biedt u het verzenden van tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden met het Matrix-protocol.</p>
|
||||
<p xml:lang="pl">NeoChat jest programem do Matrisa. Umożliwia wysyłanie wiadomości tekstowych, filmów oraz dźwięku do twojej rodziny, znajomych oraz przyjaciół poprzez protokół Matriksa.</p>
|
||||
<p xml:lang="pt-BR">O NeoChat é um cliente Matrix. Ele permite a você enviar mensagens de texto, arquivos de vídeo e áudio para seus familiares, colegas e amigos usando o protocolo Matrix.</p>
|
||||
<p xml:lang="sk">NeoChat je Matrix klient. Umožňuje vám posielať textové správy, videá a zvukové súbory rodine, kolegom a priateľom pomocou protokolu Matrix.</p>
|
||||
<p xml:lang="sl">NeoChat je odjemalec Matrixa. Dovoljuje vam pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, kolegom in prijateljem z uporabo protokola Matrix.</p>
|
||||
<p xml:lang="sv">NeoChat är en Matrix-klient. Den låter dig skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner med användning av Matrix-protokollet.</p>
|
||||
<p xml:lang="uk">NeoChat — клієнт мережі обміну повідомленнями Matrix. За допомогою цієї програми ви зможете надсилати текстові повідомлення, відео та звукові файли вашій родині, колегам та друзям за допомогою протоколу Matrix.</p>
|
||||
<p xml:lang="x-test">xxNeoChat is a Matrix client. It allows you to send text messages, videos and audio files to your family, colleagues and friends using the Matrix protocol.xx</p>
|
||||
<p xml:lang="zh-CN">NeoChat 是一个 Matrix 客户端。 它允许您使用 Matrix 协议向您的家人、同事和朋友发送文本消息、视频和音频文件。</p>
|
||||
<p>Matrix is a decentralized communication protocol, putting the user back in control. Currently NeoChat implements large part of the protocol with the exception of encrypted chats and video chat.</p>
|
||||
<p xml:lang="az">Matrix, istifadəçini nəzarətdə saxlayan, mərkəzləşməmişi rabitə protokoludur. NeoChat, söhbətin və video əlaqəsinin şifrələnməsindən başqa bir çox protokolları həyata keçirə bilir.</p>
|
||||
<p xml:lang="ca">Matrix és un protocol de comunicacions descentralitzat, que retorna el control a l'usuari. Actualment el NeoChat implementa una gran part del protocol amb l'excepció dels xats encriptats i els xats de vídeo.</p>
|
||||
<p xml:lang="ca-valencia">Matrix és un protocol de comunicacions descentralitzat, que retorna el control a l'usuari. Actualment el NeoChat implementa una gran part del protocol amb l'excepció dels xats encriptats i els xats de vídeo.</p>
|
||||
<p xml:lang="de">Matrix ist ein dezentralisiertes Kommunikationsprotokoll, das dem Benutzer wieder die Kontrolle zurückgibt. Derzeit implementiert NeoChat einen großen Teil des Protokolls mit der Ausnahme von verschlüsselten Chats und Video-Chat.</p>
|
||||
<p xml:lang="en-GB">Matrix is a decentralised communication protocol, putting the user back in control. Currently NeoChat implements large part of the protocol with the exception of encrypted chats and video chat.</p>
|
||||
<p xml:lang="es">Matrix es un protocolo de comunicaciones descentralizado, que devuelve el control al usuario. En la actualidad, NeoChat implementa gran parte del protocolo con la excepción de chats cifrados y chats de vídeo.</p>
|
||||
<p xml:lang="eu">Matrix komunikazio-protokolo deszentralizatu bat da, erabiltzaileari kontrola itzultzen diona. Gaur egun, NeoChat-ek protokoloaren zati handi bat inplementatzen du, berriketa zifratuak eta bideo berriketak izan ezik.</p>
|
||||
<p xml:lang="fi">Matrix on hajautettu viestintäyhteyskäytäntö, joka antaa hallinnan takaisin käyttäjille. NeoChat tarjoaa nykyisellään valtaosan yhteyskäytännöstä salattuja keskustelu- ja videokeskusteluja lukuun ottamatta.</p>
|
||||
<p xml:lang="fr">Matrix est un protocole de communication décentralisé, donnant le contrôle à l'utilisateur. Actuellement, NeoChat met en œuvre une grande partie du protocole, à l'exception des discussions chiffrées et du chat vidéo.</p>
|
||||
<p xml:lang="ia">Matrix es un protocollo de communication decentrate, ponente le usator in le controlo. Currentemente NeoChat implementa un grande parte del protocollo con le exception de conversationes cryptate e conversationes video.</p>
|
||||
<p xml:lang="it">Matrix è un protocollo di comunicazione decentralizzato, che restituisce all'utente il controllo. Attualmente NeoChat implementa gran parte del protocollo ad eccezione delle chat cifrate e delle chat video.</p>
|
||||
<p xml:lang="ko">Matrix는 사용자에게 제어권을 돌려 주는 분산 통신 프로토콜입니다. NeoChat은 암호화된 대화 및 영상 통화를 제외한 프로토콜의 대부분 기능을 구현합니다.</p>
|
||||
<p xml:lang="nl">Matrix is een gedecentraliseerd communicatieprotocol, dat de gebruiker de controle teruggeeft. Op dit moment implementeert NeoChat grote delen van het protocol met de uitzondering van versleutelde chats en video-chat.</p>
|
||||
<p xml:lang="pl">Matrix jest protokołem rozproszonego porozumiewania się oddający użytkownikowi jego władzę. Obecnie NeoChat obsługuje dużą część protokołu poza szyfrowanymi rozmowami tekstowymi i z obrazem.</p>
|
||||
<p xml:lang="pt-BR">O Matrix é um protocolo de comunicação descentralizado, colocando o usuário de volta no controle. Atualmente o NeoChat implementa grande parte do protocolo com a exceção de bate-papos criptografados e bate-papo por vídeo.</p>
|
||||
<p xml:lang="sk">Matrix je decentralizovaný komunikačný protokol, ktorý používateľovi vracia kontrolu. V súčasnosti NeoChat implementuje veľkú časť protokolu s výnimkou šifrovaných chatov a videohovorov.</p>
|
||||
<p xml:lang="sl">Matrix je decentraliziran komunikacijski protokol, kjer ima uporabnik uporabnik kontrolo rabe. Trenutno ima NeoChat izveden velik del protokola z izjemo šifriranih klepetov in video klepetov.</p>
|
||||
<p xml:lang="sv">Matrix är ett decentraliserat kommunikationsprotokoll, som ger tillbaka kontrollen till användaren. För närvarande implementerar NeoChat en stor del av protokollet, med undantag för krypterad chatt och videochatt.</p>
|
||||
<p xml:lang="uk">Matrix — протокол децентралізованого спілкування, який передає контроль над даними користувачеві. У поточній версії NeoChat реалізовано більшу частину протоколу, окрім зашифрованого спілкування та відеоспілкування.</p>
|
||||
<p xml:lang="x-test">xxMatrix is a decentralized communication protocol, putting the user back in control. Currently NeoChat implements large part of the protocol with the exception of encrypted chats and video chat.xx</p>
|
||||
<p xml:lang="zh-CN">Matrix 是一个分布式通讯协议,使用户重新得到控制权。 目前,NeoChat 实现了协议的大部分,除了加密聊天和视频聊天。</p>
|
||||
<p>NeoChat works both on mobile and desktop while providing a consistent user experience.</p>
|
||||
<p xml:lang="az">Vahid istifadəçi interfeysi ilə təmin olunan NeoChat, həm mobil telefonda həm də kompyuterlərdə işləyir.</p>
|
||||
<p xml:lang="ca">El NeoChat funciona en els mòbils i a l'escriptori, proporcionant una experiència d'usuari coherent.</p>
|
||||
<p xml:lang="ca-valencia">El NeoChat funciona en els mòbils i a l'escriptori, proporcionant una experiència d'usuari coherent.</p>
|
||||
<p xml:lang="de">NeoChat funktioniert sowohl auf dem Mobiltelefon als auch auf dem Arbeitsfläche und bietet ein einheitliches Benutzererlebnis. </p>
|
||||
<p xml:lang="en-GB">NeoChat works both on mobile and desktop while providing a consistent user experience.</p>
|
||||
<p xml:lang="es">NeoChat funciona en móviles y en el escritorio a la vez que proporciona una experiencia de usuario consistente.</p>
|
||||
<p xml:lang="eu">NeoChat mugikorretan eta mahaigainean dabil, erabiltzaile esperientzia koherentea eskainiz.</p>
|
||||
<p xml:lang="fi">NeoChat toimii sekä mobiili- että työpöytäalustoilla tarjoten yhdenmukaisen käyttökokemuksen.</p>
|
||||
<p xml:lang="fr">NeoChat fonctionne aussi bien sur les mobiles que sur les ordinateurs de bureau, tout en offrant une expérience utilisateur cohérente.</p>
|
||||
<p xml:lang="ia">NeoChat functiona sia sur mobile que ur scriptorio durante que forni un experientia de usator consistente.</p>
|
||||
<p xml:lang="it">NeoChat funziona sia su dispositivi mobili che desktop, fornendo un'esperienza utente coerente.</p>
|
||||
<p xml:lang="ko">NeoChat은 모바일과 데스크톱 모두에서 일관된 사용자 경험을 제공합니다.</p>
|
||||
<p xml:lang="nl">NeoChat werkt zowel op de mobiel en het bureaublad met het leveren van een consistente gebruikerservaring.</p>
|
||||
<p xml:lang="pl">NeoChat działa zarówno na urządzeniach przenośnych jak i biurkowych, zapewniając spójne wrażenia użytkownika</p>
|
||||
<p xml:lang="pt-BR">O NeoChat funciona tanto no celular como no computador enquanto fornece uma experiência consistente ao usuário.</p>
|
||||
<p xml:lang="sk">NeoChat funguje na mobilných aj stolových počítačoch a poskytuje konzistentný používateľský zážitok.</p>
|
||||
<p xml:lang="sl">NeoChat deluje tako na mobilnih kot na namiznih platformah z zagotavljanjem konsistentne uporabniške izkušnje.</p>
|
||||
<p xml:lang="sv">NeoChat fungerar både på mobil och skrivbord och tillhandahåller en konsekvent användarupplevelse.</p>
|
||||
<p xml:lang="uk">NeoChat працює на мобільних пристроях та звичайних комп'ютерах, маючи однорідний інтерфейс на усіх підтримуваних пристроях.</p>
|
||||
<p xml:lang="x-test">xxNeoChat works both on mobile and desktop while providing a consistent user experience.xx</p>
|
||||
<p xml:lang="zh-CN">NeoChat 在移动设备和桌面上均可用,并提供一致的用户体验。</p>
|
||||
</description>
|
||||
<url type="homepage">https://apps.kde.org/neochat/</url>
|
||||
<url type="bugtracker">https://invent.kde.org/network/neochat/-/issues</url>
|
||||
@@ -102,6 +146,7 @@
|
||||
<developer_name xml:lang="ia">Le communitate de KDE</developer_name>
|
||||
<developer_name xml:lang="id">Komunitas KDE</developer_name>
|
||||
<developer_name xml:lang="it">La comunità KDE</developer_name>
|
||||
<developer_name xml:lang="ko">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="pa">ਕੇਡੀਈ ਕਮਿਊਨਟੀ</developer_name>
|
||||
@@ -113,18 +158,30 @@
|
||||
<developer_name xml:lang="sv">KDE-gemenskapen</developer_name>
|
||||
<developer_name xml:lang="uk">Спільнота KDE</developer_name>
|
||||
<developer_name xml:lang="x-test">xxThe KDE Communityxx</developer_name>
|
||||
<developer_name xml:lang="zh-CN">KDE 社区</developer_name>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>GPL-3.0</project_license>
|
||||
<value key="KDE::matrix">#neochat:kde.org</value>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>https://www.plasma-mobile.org/img/post-2020-10/post-2020-10-neochat-timeline.png</image>
|
||||
<image>https://cdn.kde.org/screenshots/neochat/application-mobile.png</image>
|
||||
</screenshot>
|
||||
<screenshot type="default">
|
||||
<image>https://cdn.kde.org/screenshots/neochat/application.png</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<content_rating type="oars-1.1">
|
||||
<content_attribute id="social-chat">intense</content_attribute>
|
||||
</content_rating>
|
||||
<releases>
|
||||
<release version="1.2.0" date="2021-06-01">
|
||||
<description>
|
||||
<p>NeoChat 1.2 brings a major redesign of the user interface. The chat page is now using bubbles for the messages and the input component was completely rewritten with a nicer look as well.</p>
|
||||
<p>It's now possible to send custom reactions by replying to a comment with /react <message>.</p>
|
||||
<p>NeoChat now supports opening Matrix URIs from your browser.</p>
|
||||
</description>
|
||||
<url>https://carlschwan.eu/2021/06/01/neochat-1.2/</url>
|
||||
</release>
|
||||
<release urgency="critical" version="1.1.1" date="2021-02-23"/>
|
||||
<release version="1.1.0" date="2021-02-22">
|
||||
<description>
|
||||
@@ -134,7 +191,7 @@
|
||||
<p>We added a few commands to NeoChat (/shrug, /lenny, /join, /ignore, ...).</p>
|
||||
<p>We improved the Plasma integration a bit. Now the number of unread messages is displayed in the Plasma Taskbar.</p>
|
||||
</description>
|
||||
<url>https://carlschwan.eu/2020/02/22/neochat-1.1/</url>
|
||||
<url>https://carlschwan.eu/2021/02/22/neochat-1.1/</url>
|
||||
</release>
|
||||
<release version="1.0.1" date="2021-01-13">
|
||||
<description>
|
||||
|
||||
@@ -7,26 +7,29 @@ Name[cs]=NeoChat
|
||||
Name[de]=NeoChat
|
||||
Name[en_GB]=NeoChat
|
||||
Name[es]=NeoChat
|
||||
Name[eu]=NeoChat
|
||||
Name[fi]=NeoChat
|
||||
Name[fr]=NeoChat
|
||||
Name[ia]=Neochat
|
||||
Name[it]=NeoChat
|
||||
Name[ko]=NeoChat
|
||||
Name[nl]=NeoChat
|
||||
Name[nn]=NeoChat
|
||||
Name[pa]=ਨਿਓ-ਚੈਟ
|
||||
Name[pl]=NeoChat
|
||||
Name[pt]=NeoChat
|
||||
Name[pt_BR]=NeoChat
|
||||
Name[ro]=NeoChat
|
||||
Name[sk]=NeoChat
|
||||
Name[sl]=NeoChat
|
||||
Name[sv]=NeoChat
|
||||
Name[ta]=நியோச்சாட்
|
||||
Name[uk]=NeoChat
|
||||
Name[x-test]=xxNeoChatxx
|
||||
Name[zh_CN]=NeoChat
|
||||
GenericName=Matrix Client
|
||||
GenericName[az]=Matrix Müştərisi
|
||||
GenericName[ca]=Client de Matrix
|
||||
GenericName[ca@valencia]=Client de Matrix
|
||||
GenericName[ca]=Client del Matrix
|
||||
GenericName[ca@valencia]=Client del Matrix
|
||||
GenericName[cs]=Klient protokolu Matrix
|
||||
GenericName[de]=Matrix-Programm
|
||||
GenericName[en_GB]=Matrix Client
|
||||
@@ -35,8 +38,9 @@ GenericName[eu]=Matrix bezeroa
|
||||
GenericName[fi]=Matrix-asiakas
|
||||
GenericName[fr]=Client « Matrix »
|
||||
GenericName[hu]=Matrix kliens
|
||||
GenericName[ia]=Cliente de Matrice
|
||||
GenericName[ia]=Cliente de Matrix
|
||||
GenericName[it]=Client Matrix
|
||||
GenericName[ko]=Matrix 클라이언트
|
||||
GenericName[nl]=Matrix-client
|
||||
GenericName[nn]=Matrix-klient
|
||||
GenericName[pa]=ਮੈਟਰਿਕਸ ਕਲਾਈਂਟ
|
||||
@@ -64,6 +68,7 @@ Comment[fr]=Client pour le protocole « Matrix »
|
||||
Comment[hu]=Kliens a Matrix protokollhoz
|
||||
Comment[ia]=Cliente per le protocollo de Matrix
|
||||
Comment[it]=Client per il protocollo Matrix
|
||||
Comment[ko]=Matrix 프로토콜용 클라이언트
|
||||
Comment[nl]=Client voor het Matrix-protocol
|
||||
Comment[nn]=Lynmeldingsklient for Matrix-protokollen
|
||||
Comment[pa]=ਮੈਟਰਿਕਸ ਪਰੋਟੋਕਾਲ ਲਈ ਕਲਾਈਂਟ ਹੈ
|
||||
|
||||
@@ -192,7 +192,7 @@ Kirigami.ApplicationWindow {
|
||||
Kirigami.Action {
|
||||
text: i18n("Devices")
|
||||
iconName: "network-connect"
|
||||
onTriggered: pageStack.layers.push("qrc:/imports/NeoChat/Page/DevicesPage.qml")
|
||||
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/DevicesPage.qml")
|
||||
enabled: pageStack.layers.currentItem.title !== i18n("Devices") && Controller.accountCount > 0
|
||||
},
|
||||
Kirigami.Action {
|
||||
|
||||
1
res.qrc
1
res.qrc
@@ -17,6 +17,7 @@
|
||||
<file>imports/NeoChat/Component/qmldir</file>
|
||||
<file>imports/NeoChat/Component/FullScreenImage.qml</file>
|
||||
<file>imports/NeoChat/Component/FancyEffectsContainer.qml</file>
|
||||
<file>imports/NeoChat/Component/TypingPane.qml</file>
|
||||
<file>imports/NeoChat/Component/ChatBox</file>
|
||||
<file>imports/NeoChat/Component/ChatBox/ChatBox.qml</file>
|
||||
<file>imports/NeoChat/Component/ChatBox/ChatBar.qml</file>
|
||||
|
||||
@@ -33,7 +33,7 @@ add_executable(neochat
|
||||
../res.qrc
|
||||
)
|
||||
|
||||
if(${Quotient_VERSION_MINOR} GREATER 6)
|
||||
if(Quotient_VERSION_MINOR GREATER 6)
|
||||
target_compile_definitions(neochat PRIVATE QUOTIENT_07)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
#include <QElapsedTimer>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QGuiApplication>
|
||||
#include <QMovie>
|
||||
#include <QNetworkConfigurationManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QPixmap>
|
||||
#include <QQuickWindow>
|
||||
@@ -29,7 +31,6 @@
|
||||
#include <QSysInfo>
|
||||
#include <QTimer>
|
||||
#include <utility>
|
||||
#include <QNetworkConfigurationManager>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
@@ -47,6 +48,7 @@
|
||||
#include "neochatuser.h"
|
||||
#include "settings.h"
|
||||
#include "utils.h"
|
||||
#include "roommanager.h"
|
||||
#include <KStandardShortcut>
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
@@ -67,7 +69,7 @@ Controller::Controller(QObject *parent)
|
||||
if (NeoChatConfig::self()->systemTray()) {
|
||||
trayIcon->show();
|
||||
connect(trayIcon, &TrayIcon::showWindow, this, &Controller::showWindow);
|
||||
QApplication::setQuitOnLastWindowClosed(false);
|
||||
QGuiApplication::setQuitOnLastWindowClosed(false);
|
||||
}
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::SystemTrayChanged, this, [=]() {
|
||||
if (NeoChatConfig::self()->systemTray()) {
|
||||
@@ -77,7 +79,7 @@ Controller::Controller(QObject *parent)
|
||||
trayIcon->hide();
|
||||
disconnect(trayIcon, &TrayIcon::showWindow, this, &Controller::showWindow);
|
||||
}
|
||||
QApplication::setQuitOnLastWindowClosed(!NeoChatConfig::self()->systemTray());
|
||||
QGuiApplication::setQuitOnLastWindowClosed(!NeoChatConfig::self()->systemTray());
|
||||
});
|
||||
#endif
|
||||
|
||||
@@ -85,7 +87,7 @@ Controller::Controller(QObject *parent)
|
||||
invokeLogin();
|
||||
});
|
||||
|
||||
QObject::connect(QApplication::instance(), &QCoreApplication::aboutToQuit, QApplication::instance(), [] {
|
||||
QObject::connect(QGuiApplication::instance(), &QCoreApplication::aboutToQuit, QGuiApplication::instance(), [] {
|
||||
NeoChatConfig::self()->save();
|
||||
});
|
||||
|
||||
@@ -427,7 +429,8 @@ bool Controller::supportSystemTray() const
|
||||
#ifdef Q_OS_ANDROID
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
QString de = getenv("XDG_CURRENT_DESKTOP");
|
||||
return de != QStringLiteral("GNOME") && de != QStringLiteral("Pantheon");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -496,13 +499,13 @@ int Controller::accountCount() const
|
||||
|
||||
bool Controller::quitOnLastWindowClosed()
|
||||
{
|
||||
return QApplication::quitOnLastWindowClosed();
|
||||
return QGuiApplication::quitOnLastWindowClosed();
|
||||
}
|
||||
|
||||
void Controller::setQuitOnLastWindowClosed(bool value)
|
||||
{
|
||||
if (quitOnLastWindowClosed() != value) {
|
||||
QApplication::setQuitOnLastWindowClosed(value);
|
||||
QGuiApplication::setQuitOnLastWindowClosed(value);
|
||||
Q_EMIT quitOnLastWindowClosedChanged();
|
||||
}
|
||||
}
|
||||
@@ -592,6 +595,17 @@ void Controller::joinRoom(const QString &alias)
|
||||
});
|
||||
}
|
||||
|
||||
void Controller::openOrCreateDirectChat(NeoChatUser *user)
|
||||
{
|
||||
const auto existing = activeConnection()->directChats();
|
||||
|
||||
if (existing.contains(user)) {
|
||||
RoomManager::instance().enterRoom(static_cast<NeoChatRoom *>(activeConnection()->room(existing.value(user))));
|
||||
return;
|
||||
}
|
||||
activeConnection()->requestDirectChat(user);
|
||||
}
|
||||
|
||||
QString Controller::formatByteSize(double size, int precision) const
|
||||
{
|
||||
return KFormat().formatByteSize(size, precision);
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMediaPlayer>
|
||||
#include <QMenu>
|
||||
#include <QObject>
|
||||
|
||||
#include <KAboutData>
|
||||
@@ -21,6 +19,7 @@ class QNetworkConfigurationManager;
|
||||
#include "user.h"
|
||||
|
||||
class NeoChatRoom;
|
||||
class NeoChatUser;
|
||||
class QQuickWindow;
|
||||
|
||||
using namespace Quotient;
|
||||
@@ -86,6 +85,8 @@ public:
|
||||
|
||||
Q_INVOKABLE QString formatDuration(quint64 msecs, KFormat::DurationFormatOptions options = KFormat::DefaultDuration) const;
|
||||
Q_INVOKABLE QString formatByteSize(double size, int precision = 1) const;
|
||||
|
||||
Q_INVOKABLE void openOrCreateDirectChat(NeoChatUser *user);
|
||||
private:
|
||||
explicit Controller(QObject *parent = nullptr);
|
||||
~Controller() override;
|
||||
|
||||
14
src/main.cpp
14
src/main.cpp
@@ -12,6 +12,12 @@
|
||||
#include <QQuickWindow>
|
||||
#include <QDebug>
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
#include <QGuiApplication>
|
||||
#else
|
||||
#include <QApplication>
|
||||
#endif
|
||||
|
||||
#include <KAboutData>
|
||||
#ifdef HAVE_KDBUSADDONS
|
||||
#include <KDBusService>
|
||||
@@ -57,7 +63,7 @@ Q_DECL_EXPORT
|
||||
#endif
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
|
||||
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
||||
|
||||
@@ -84,7 +90,7 @@ int main(int argc, char *argv[])
|
||||
app.setFont(font);
|
||||
#endif
|
||||
|
||||
QApplication::setOrganizationName("KDE");
|
||||
QGuiApplication::setOrganizationName("KDE");
|
||||
|
||||
KAboutData about(QStringLiteral("neochat"),
|
||||
i18n("NeoChat"),
|
||||
@@ -98,7 +104,7 @@ int main(int argc, char *argv[])
|
||||
about.setOrganizationDomain("kde.org");
|
||||
|
||||
KAboutData::setApplicationData(about);
|
||||
QApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("org.kde.neochat")));
|
||||
QGuiApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("org.kde.neochat")));
|
||||
|
||||
#ifdef HAVE_KDBUSADDONS
|
||||
KDBusService service(KDBusService::Unique);
|
||||
@@ -224,5 +230,5 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return QApplication::exec();
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
@@ -370,6 +370,22 @@ int MessageEventModel::rowCount(const QModelIndex &parent) const
|
||||
|
||||
}
|
||||
|
||||
bool MessageEventModel::canFetchMore(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
|
||||
return m_currentRoom && !m_currentRoom->eventsHistoryJob() && !m_currentRoom->allHistoryLoaded();
|
||||
}
|
||||
|
||||
void MessageEventModel::fetchMore(const QModelIndex &parent)
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
if (m_currentRoom) {
|
||||
m_currentRoom->getPreviousContent(20);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline QVariantMap userAtEvent(NeoChatUser *user, NeoChatRoom *room, const RoomEvent &evt)
|
||||
{
|
||||
Q_UNUSED(evt)
|
||||
@@ -405,6 +421,8 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
const KFormat format;
|
||||
return format.formatRelativeDateTime(eventDate, QLocale::ShortFormat);
|
||||
}
|
||||
case SpecialMarksRole:
|
||||
return EventStatus::Hidden;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -532,9 +550,6 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
if (is<RedactionEvent>(evt) || is<ReactionEvent>(evt)) {
|
||||
return EventStatus::Hidden;
|
||||
}
|
||||
if (evt.isRedacted()) {
|
||||
return EventStatus::Hidden;
|
||||
}
|
||||
|
||||
if (evt.isStateEvent() && static_cast<const StateEventBase &>(evt).repeatsState()) {
|
||||
return EventStatus::Hidden;
|
||||
@@ -667,7 +682,8 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
auto i = index(r);
|
||||
if (data(i, SpecialMarksRole) != EventStatus::Hidden) {
|
||||
return data(i, AuthorRole) != data(idx, AuthorRole) || data(i, EventTypeRole) != data(idx, EventTypeRole)
|
||||
|| data(idx, TimeRole).toDateTime().msecsTo(data(i, TimeRole).toDateTime()) > 600000;
|
||||
|| data(idx, TimeRole).toDateTime().msecsTo(data(i, TimeRole).toDateTime()) > 600000
|
||||
|| data(idx, TimeRole).toDateTime().toLocalTime().date().day() != data(i, TimeRole).toDateTime().toLocalTime().date().day();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -684,7 +700,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (role == ReactionRole) {
|
||||
|
||||
@@ -76,6 +76,9 @@ private:
|
||||
[[nodiscard]] QDateTime makeMessageTimestamp(const Quotient::Room::rev_iter_t &baseIt) const;
|
||||
[[nodiscard]] static QString renderDate(const QDateTime ×tamp);
|
||||
|
||||
bool canFetchMore(const QModelIndex &parent) const override;
|
||||
void fetchMore(const QModelIndex &parent) override;
|
||||
|
||||
void refreshLastUserEvents(int baseTimelineRow);
|
||||
void refreshEventRoles(int row, const QVector<int> &roles = {});
|
||||
int refreshEventRoles(const QString &eventId, const QVector<int> &roles = {});
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include <cmark.h>
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QFileInfo>
|
||||
#include <QImageReader>
|
||||
#include <QMetaObject>
|
||||
@@ -685,3 +684,8 @@ bool NeoChatRoom::isInvite() const
|
||||
{
|
||||
return joinState() == JoinState::Invite;
|
||||
}
|
||||
|
||||
bool NeoChatRoom::isUserBanned(const QString &user) const
|
||||
{
|
||||
return getCurrentState<RoomMemberEvent>(user)->membership() == MembershipType::Ban;
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ public:
|
||||
[[nodiscard]] QString eventToString(const RoomEvent &evt, Qt::TextFormat format = Qt::PlainText, bool removeReply = true) const;
|
||||
|
||||
Q_INVOKABLE [[nodiscard]] bool containsUser(const QString &userID) const;
|
||||
Q_INVOKABLE [[nodiscard]] bool isUserBanned(const QString &user) const;
|
||||
|
||||
Q_INVOKABLE [[nodiscard]] bool canSendEvent(const QString &eventType) const;
|
||||
Q_INVOKABLE [[nodiscard]] bool canSendState(const QString &eventType) const;
|
||||
|
||||
@@ -346,12 +346,12 @@ QVariant RoomListModel::data(const QModelIndex &index, int role) const
|
||||
if (room->isFavourite()) {
|
||||
return RoomType::Favorite;
|
||||
}
|
||||
if (room->isDirectChat()) {
|
||||
return RoomType::Direct;
|
||||
}
|
||||
if (room->isLowPriority()) {
|
||||
return RoomType::Deprioritized;
|
||||
}
|
||||
if (room->isDirectChat()) {
|
||||
return RoomType::Direct;
|
||||
}
|
||||
return RoomType::Normal;
|
||||
}
|
||||
if (role == UnreadCountRole) {
|
||||
|
||||
Reference in New Issue
Block a user