Compare commits

..

21 Commits

Author SHA1 Message Date
Carl Schwan
9a27a1e70f Start purpose integration 2021-10-24 16:45:43 +02:00
Carl Schwan
f2cf82ee8e Fix double quoting and missing new lines in message sent
* Don't encode text inside code block
* Make sure to replace \n with <br> in the html rendering. It's not
  respecting the common mark spec but this is the same behavior as
  Element
2021-10-23 20:35:19 +00:00
Carl Schwan
a146fab5a0 Fix color of Pane in room info drawer
This is temporary hack and the real solution is to add a Pane
implementation in qqc2-desktop-style
2021-10-23 20:33:34 +00:00
l10n daemon script
fb6745b49a GIT_SILENT made messages (after extraction) 2021-10-23 00:17:59 +00:00
Carl Schwan
6c3ae87340 Support resizing right drawer
Signed-off-by: Carl Schwan <carl@carlschwan.eu>
2021-10-21 23:04:52 +02:00
Carl Schwan
6afeaf1619 Move copy pasted to TextDelegate component
Signed-off-by: Carl Schwan <carl@carlschwan.eu>
2021-10-21 22:18:47 +02:00
Carl Schwan
890860df92 Improve room setting
* Port away from OverlaySheet
* Use Kirigami.CategorizedSettings
* Add join rules (read only for now)
2021-10-21 20:00:50 +00:00
Carl Schwan
6b8358874a Simplify function call in RoomPage
Instead of passing every argument in the right order, pass the entire
model/event object to the context menu functions. This is less copy
pasta of code and the order of the args is now less likely to break in
the future.

Signed-off-by: Carl Schwan <carl@carlschwan.eu>
2021-10-21 19:54:06 +00:00
Carl Schwan
fc9f37d4a4 Port global settings to Kirigami.CategorizedSettings 2021-10-21 19:38:03 +00:00
Tobias Fella
48e410196c Don't allow opening a room in a new window on mobile 2021-10-18 21:07:35 +00:00
Tobias Fella
65cc392805 Fix flicking the timeline 2021-10-18 15:35:56 +02:00
Tobias Fella
a6dd5b9a57 Escape html before processing text to be sent 2021-10-16 20:45:14 +02:00
Tobias Fella
1d7c20e1c7 Make user list search case insensitive 2021-10-16 18:18:07 +00:00
Tobias Fella
22609b21df Add custom eventToString message for power level events 2021-10-16 18:17:49 +00:00
Tobias Fella
6c5ca0ac9d FIx querying power levels
Fixes #422
2021-10-16 20:16:36 +02:00
Tobias Fella
b22ebf3671 Show number of joined users instead of joined+invited users in room drawer 2021-10-16 19:28:51 +02:00
Carl Schwan
ec1cc34855 Fix missing import 2021-10-16 18:11:59 +02:00
Carl Schwan
a5aafde331 Unify look of loading pages 2021-10-16 17:59:31 +02:00
Carl Schwan
d42ad85b30 Port to OverlaySheet.title 2021-10-16 17:44:42 +02:00
Tobias Fella
8648b4a3bf Fix copying whole messages 2021-10-14 22:14:36 +02:00
Tobias Fella
bdca636fb8 Copy only selected text instead of whole message
Fixes #457
2021-10-14 21:44:32 +02:00
41 changed files with 782 additions and 526 deletions

View File

@@ -79,6 +79,7 @@ MouseArea {
id: replyText
textMessage: reply.display
textFormat: Text.RichText
hasContextMenu: false
width: Math.min(implicitWidth, bubbleMaxWidth - Kirigami.Units.largeSpacing * 3)
x: Kirigami.Units.smallSpacing * 3 + replyAvatar.width
}

View File

@@ -17,8 +17,17 @@ TextEdit {
property bool isEmote: false
property string textMessage: model.display
property bool spoilerRevealed: !hasSpoiler.test(textMessage)
property bool hasContextMenu: true
signal requestOpenMessageContext()
ListView.onReused: Qt.binding(() => !hasSpoiler.test(textMessage))
Layout.fillWidth: Config.compactLayout
Layout.rightMargin: Kirigami.Units.largeSpacing
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
text: "<style>
table {
width:100%;
@@ -53,8 +62,6 @@ a{
wrapMode: Text.Wrap
textFormat: Text.RichText
Layout.fillWidth: true
onLinkActivated: RoomManager.openResource(link)
onHoveredLinkChanged: if (hoveredLink.length > 0) {
applicationWindow().hoverLinkIndicator.text = hoveredLink;
@@ -70,4 +77,16 @@ a{
enabled: !parent.hoveredLink && !spoilerRevealed
onTapped: spoilerRevealed = true
}
TapHandler {
acceptedButtons: Qt.RightButton
onTapped: openMessageContext(model, parent.selectedText)
enabled: hasContextMenu
}
TapHandler {
acceptedButtons: Qt.LeftButton
onLongPressed: requestOpenMessageContext()
enabled: hasContextMenu
}
}

View File

@@ -14,9 +14,7 @@ Kirigami.OverlaySheet {
parent: applicationWindow().overlay
header: Kirigami.Heading {
text: i18n("Create a Room")
}
title: i18n("Create a Room")
contentItem: Kirigami.FormLayout {
TextField {

View File

@@ -1,206 +0,0 @@
// SPDX-FileCopyrightText: 2019-2020 Black Hat <bhat@encom.eu.org>
// SPDX-License-Identifier: GPL-3.0-only
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0
import NeoChat.Component 1.0
Kirigami.OverlaySheet {
id: root
property var room
readonly property bool canChangeAvatar: room.canSendState("m.room.avatar")
readonly property bool canChangeName: room.canSendState("m.room.name")
readonly property bool canChangeTopic: room.canSendState("m.room.topic")
readonly property bool canChangeCanonicalAlias: room.canSendState("m.room.canonical_alias")
parent: applicationWindow().overlay
header: Kirigami.Heading {
text: i18nc("%1 is the room name", "Room Settings - %1", room.displayName)
elide: Text.ElideRight
}
contentItem: ColumnLayout {
RowLayout {
Layout.fillWidth: true
spacing: 16
Kirigami.Avatar {
Layout.preferredWidth: 72
Layout.preferredHeight: 72
Layout.alignment: Qt.AlignTop
name: room.name
source: room.avatarMediaId ? ("image://mxc/" + room.avatarMediaId) : ""
MouseArea {
anchors.fill: parent
enabled: canChangeAvatar
onClicked: {
var fileDialog = openFileDialog.createObject(ApplicationWindow.overlay)
fileDialog.chosen.connect(function(path) {
if (!path) return
room.changeAvatar(path)
})
fileDialog.open()
}
}
}
Kirigami.FormLayout {
Layout.fillWidth: true
TextField {
id: roomNameField
text: room.name
Kirigami.FormData.label: i18n("Room Name:")
enabled: canChangeName
}
TextArea {
id: roomTopicField
Layout.fillWidth: true
text: room.topic
Kirigami.FormData.label: i18n("Room topic:")
enabled: canChangeTopic
}
Button {
Layout.alignment: Qt.AlignRight
visible: canChangeName || canChangeTopic
text: i18n("Save")
highlighted: true
onClicked: {
if (room.name != roomNameField.text) {
room.setName(roomNameField.text)
}
if (room.topic != roomTopicField.text) {
room.setTopic(roomTopicField.text)
}
}
}
Kirigami.Separator {
Layout.fillWidth: true
visible: canonicalAliasComboBox.visible || altAlias.visible
}
ComboBox {
id: canonicalAliasComboBox
visible: room.aliases && room.aliases.length
Kirigami.FormData.label: i18n("Canonical Alias:")
popup.z: 999; // HACK This is an absolute hack, but combos inside OverlaySheets have their popups show up underneath, because of fun z ordering stuff
enabled: canChangeCanonicalAlias
model: room.aliases
currentIndex: room.aliases.indexOf(room.canonicalAlias)
onCurrentIndexChanged: {
if (room.canonicalAlias != room.aliases[currentIndex]) {
room.setCanonicalAlias(room.aliases[currentIndex])
}
}
}
RowLayout {
id: altAlias
Kirigami.FormData.label: i18n("Other Aliases:")
Layout.fillWidth: true
visible: room.altAliases && room.altAliases.length
ColumnLayout {
Layout.fillWidth: true
spacing: 0
Repeater {
model: room.altAliases
delegate: RowLayout {
Layout.maximumWidth: parent.width
Label {
text: modelData
}
ToolButton {
icon.name: ""
onClicked: room.removeLocalAlias(modelData)
}
}
}
}
}
}
}
Kirigami.Separator {
Layout.fillWidth: true
visible: next.visible || prev.visible
}
Control {
id: next
Layout.fillWidth: true
visible: room.predecessorId && room.connection.room(room.predecessorId)
padding: Kirigami.Units.largeSpacing
contentItem: Kirigami.InlineMessage {
text: i18n("This room continues another conversation.")
actions: Kirigami.Action {
text: i18n("See older messages...")
onTriggered: {
roomListForm.enteredRoom = Controller.activeConnection.room(room.predecessorId)
root.close()
}
}
}
}
Control {
id: prev
Layout.fillWidth: true
visible: room.successorId && room.connection.room(room.successorId)
padding: Kirigami.Units.largeSpacing
contentItem: Kirigami.InlineMessage {
text: i18n("This room has been replaced.")
actions: Kirigami.Action {
text: i18n("See new room...")
onTriggered: {
roomListForm.enteredRoom = Controller.activeConnection.room(room.successorId)
root.close()
}
}
}
}
Component {
id: openFileDialog
OpenFileDialog {}
}
}
}

View File

@@ -27,13 +27,7 @@ Kirigami.OverlaySheet {
rightPadding: 0
topPadding: 0
header: Kirigami.Heading {
id: heading
text: i18nc("@title:menu Account detail dialog", "Account detail")
elide: Text.ElideRight
QQC2.ToolTip.visible: truncated && hovered
QQC2.ToolTip.text: text
}
title: i18nc("@title:menu Account detail dialog", "Account detail")
contentItem: ColumnLayout {
spacing: 0

View File

@@ -1,5 +1,4 @@
module NeoChat.Dialog
RoomSettingsDialog 1.0 RoomSettingsDialog.qml
UserDetailDialog 1.0 UserDetailDialog.qml
LoginDialog 1.0 LoginDialog.qml
CreateRoomDialog 1.0 CreateRoomDialog.qml

View File

@@ -28,7 +28,7 @@ Labs.MenuBar {
text: i18nc("menu", "Preferences…")
shortcut: StandardKey.Preferences
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/SettingsPage.qml")
onTriggered: pageStack.pushDialogLayer("qrc:/imports/NeoChat/Settings/SettingsPage.qml")
}
Labs.MenuItem {
text: i18nc("menu", "Quit NeoChat")

View File

@@ -5,6 +5,8 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import org.kde.kirigami 2.19 as Kirigami
import org.kde.neochat 1.0
import NeoChat.Page 1.0
@@ -16,11 +18,15 @@ Menu {
property var room
MenuItem {
id: newWindow
text: i18n("Open in new window")
onTriggered: RoomManager.openWindow(room);
visible: !Kirigami.Settings.isMobile
}
MenuSeparator {}
MenuSeparator {
visible: newWindow.visible
}
MenuItem {
text: room.isFavourite ? i18n("Remove from Favourites") : i18n("Add to Favourites")

View File

@@ -43,7 +43,7 @@ Loader {
Kirigami.Action {
text: i18n("Copy")
icon.name: "edit-copy"
onTriggered: Clipboard.saveText(message)
onTriggered: Clipboard.saveText(loadRoot.selectedText === "" ? loadRoot.message : loadRoot.selectedText)
},
Kirigami.Action {
text: i18n("View Source")

View File

@@ -12,9 +12,7 @@ Kirigami.OverlaySheet {
property string sourceText
header: Kirigami.Heading {
text: i18n("Message Source")
}
title: i18n("Message Source")
TextArea {
id: sourceTextArea

View File

@@ -1,13 +1,20 @@
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.12 as QQC2
import org.kde.kirigami 2.12 as Kirigami
Kirigami.Page {
title: i18n("Loading")
QQC2.BusyIndicator {
Kirigami.PlaceholderMessage {
id: loadingIndicator
anchors.centerIn: parent
text: i18n("Loading")
QQC2.BusyIndicator {
running: loadingIndicator.visible
Layout.alignment: Qt.AlignHCenter
}
}
}

View File

@@ -46,11 +46,6 @@ Kirigami.ScrollablePage {
}
}
actions.main: Kirigami.Action {
text: i18n("New room")
icon.name: "list-add"
}
function goToNextRoom() {
do {
listView.incrementCurrentIndex();
@@ -75,7 +70,8 @@ Kirigami.ScrollablePage {
Component {
id: searchField
Kirigami.SearchField {
Layout.margins: Kirigami.Units.smallSpacing
Layout.topMargin: Kirigami.Units.smallSpacing
Layout.bottomMargin: Kirigami.Units.smallSpacing
Layout.fillHeight: true
Layout.fillWidth: true
onTextChanged: sortFilterRoomListModel.filterText = text
@@ -275,7 +271,6 @@ Kirigami.ScrollablePage {
id: configButton
visible: roomListItem.hovered || Kirigami.Settings.isMobile
Accessible.name: i18n("Configure room")
flat: true
action: Kirigami.Action {
id: optionAction

View File

@@ -229,7 +229,6 @@ Kirigami.ScrollablePage {
ListView {
id: messageListView
pixelAligned: true
visible: !invitation.visible
readonly property int largestVisibleIndex: count > 0 ? indexAt(contentX + (width / 2), contentY + height - 1) : -1
@@ -372,18 +371,8 @@ Kirigami.ScrollablePage {
innerObject: TextDelegate {
isEmote: true
Layout.fillWidth: Config.compactLayout
Layout.maximumWidth: emoteContainer.bubbleMaxWidth
Layout.rightMargin: 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, parent.selectedText)
}
TapHandler {
acceptedButtons: Qt.LeftButton
onLongPressed: openMessageContext(author, model.message, eventId, toolTip, eventType, model.formattedBody, parent.selectedText)
}
onRequestOpenMessageContext: openMessageContext(model, parent.selectedText)
}
}
}
@@ -398,18 +387,8 @@ Kirigami.ScrollablePage {
hoverComponent: hoverActions
innerObject: TextDelegate {
Layout.fillWidth: Config.compactLayout
Layout.maximumWidth: messageContainer.bubbleMaxWidth
Layout.rightMargin: 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, parent.selectedText)
}
TapHandler {
acceptedButtons: Qt.LeftButton
onLongPressed: openMessageContext(author, model.message, eventId, toolTip, eventType, model.formattedBody, parent.selectedText)
}
onRequestOpenMessageContext: openMessageContext(model, parent.selectedText)
}
}
}
@@ -424,9 +403,8 @@ Kirigami.ScrollablePage {
innerObject: TextDelegate {
Layout.fillWidth: !Config.compactLayout
hasContextMenu: false
Layout.maximumWidth: noticeContainer.bubbleMaxWidth
Layout.rightMargin: Kirigami.Units.largeSpacing
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
}
}
}
@@ -447,13 +425,19 @@ Kirigami.ScrollablePage {
Layout.maximumHeight: Kirigami.Units.gridUnit * 20
TapHandler {
acceptedButtons: Qt.RightButton
onTapped: openFileContext(author, model.display, eventId, toolTip, progressInfo, parent)
onTapped: openFileContext(model, parent)
}
TapHandler {
acceptedButtons: Qt.LeftButton
onLongPressed: openFileContext(author, model.display, eventId, toolTip, progressInfo, parent)
onLongPressed: openFileContext(model, parent)
onTapped: {
fullScreenImage.createObject(parent, {"filename": eventId, "localPath": currentRoom.urlToDownload(eventId), "blurhash": model.content.info["xyz.amorgan.blurhash"], "imageWidth": content.info.w, "imageHeight": content.info.h}).showFullScreen()
fullScreenImage.createObject(parent, {
filename: eventId,
localPath: currentRoom.urlToDownload(eventId),
blurhash: model.content.info["xyz.amorgan.blurhash"],
imageWidth: content.info.w,
imageHeight: content.info.h
}).showFullScreen();
}
}
}
@@ -492,11 +476,11 @@ Kirigami.ScrollablePage {
Layout.maximumWidth: audioContainer.bubbleMaxWidth
TapHandler {
acceptedButtons: Qt.RightButton
onTapped: openFileContext(author, model.display, eventId, toolTip, progressInfo, parent)
onTapped: openFileContext(model, parent)
}
TapHandler {
acceptedButtons: Qt.LeftButton
onLongPressed: openFileContext(author, model.display, eventId, toolTip, progressInfo, parent)
onLongPressed: openFileContext(model, parent)
}
}
}
@@ -520,11 +504,11 @@ Kirigami.ScrollablePage {
TapHandler {
acceptedButtons: Qt.RightButton
onTapped: openFileContext(author, model.display, eventId, toolTip, progressInfo, parent)
onTapped: openFileContext(model, parent)
}
TapHandler {
acceptedButtons: Qt.LeftButton
onLongPressed: openFileContext(author, model.display, eventId, toolTip, progressInfo, parent)
onLongPressed: openFileContext(model, parent)
}
}
}
@@ -543,11 +527,11 @@ Kirigami.ScrollablePage {
Layout.maximumWidth: fileContainer.bubbleMaxWidth
TapHandler {
acceptedButtons: Qt.RightButton
onTapped: openFileContext(author, model.display, eventId, toolTip, progressInfo, parent)
onTapped: openFileContext(model, parent)
}
TapHandler {
acceptedButtons: Qt.LeftButton
onLongPressed: openFileContext(author, model.display, eventId, toolTip, progressInfo, parent)
onLongPressed: openFileContext(model, parent)
}
}
}
@@ -887,28 +871,28 @@ Kirigami.ScrollablePage {
}
/// Open message context dialog for file and videos
function openFileContext(author, message, eventId, source, progressInfo, file) {
function openFileContext(event, file) {
const contextMenu = fileDelegateContextMenu.createObject(page, {
author: author,
message: message,
eventId: eventId,
source: source,
author: event.author,
message: event.message,
eventId: event.eventId,
source: event.toolTip,
file: file,
progressInfo: progressInfo,
progressInfo: event.progressInfo,
});
contextMenu.open();
}
/// Open context menu for normal message
function openMessageContext(author, message, eventId, source, eventType, formattedBody, selectedText) {
function openMessageContext(event, selectedText) {
const contextMenu = messageDelegateContextMenu.createObject(page, {
selectedText: selectedText,
author: author,
message: message,
eventId: eventId,
formattedBody: formattedBody,
source: source,
eventType: eventType
author: event.author,
message: event.message,
eventId: event.eventId,
formattedBody: event.formattedBody,
source: event.toolTip,
eventType: event.eventType
});
contextMenu.open();
}

View File

@@ -1,88 +0,0 @@
// SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
// SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: LGPL-2.0-or-later
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0
import NeoChat.Settings 1.0
Kirigami.ScrollablePage {
title: i18n("Settings")
bottomPadding: 0
leftPadding: 0
rightPadding: 0
topPadding: 0
onBackRequested: {
if (pageSettingStack.depth > 1 && !pageSettingStack.wideMode && pageSettingStack.currentIndex !== 0) {
event.accepted = true;
pageSettingStack.pop();
}
}
Kirigami.PageRow {
id: pageSettingStack
anchors.fill: parent
columnView.columnWidth: Kirigami.Units.gridUnit * 12
initialPage: Kirigami.ScrollablePage {
bottomPadding: 0
leftPadding: 0
rightPadding: 0
topPadding: 0
Kirigami.Theme.colorSet: Kirigami.Theme.View
ListView {
Component.onCompleted: if (pageSettingStack.wideMode) {
actions[0].trigger();
}
property list<Kirigami.Action> actions: [
Kirigami.Action {
text: i18n("General")
icon.name: "org.kde.neochat"
onTriggered: pageSettingStack.push("qrc:/imports/NeoChat/Settings/GeneralSettingsPage.qml")
},
Kirigami.Action {
text: i18n("Appearance")
icon.name: "preferences-desktop-theme-global"
onTriggered: pageSettingStack.push("qrc:/imports/NeoChat/Settings/AppearanceSettingsPage.qml")
},
Kirigami.Action {
text: i18n("Accounts")
icon.name: "preferences-system-users"
onTriggered: pageSettingStack.push("qrc:/imports/NeoChat/Page/AccountsPage.qml")
},
Kirigami.Action {
text: i18n("Custom Emoji")
icon.name: "preferences-desktop-emoticons"
onTriggered: pageSettingStack.push("qrc:/imports/NeoChat/Settings/Emoticons.qml")
},
Kirigami.Action {
text: i18n("Devices")
iconName: "network-connect"
onTriggered: pageSettingStack.push("qrc:/imports/NeoChat/Page/DevicesPage.qml")
},
Kirigami.Action {
text: i18n("About NeoChat")
icon.name: "help-about"
onTriggered: pageSettingStack.push(aboutPage)
}
]
model: actions
delegate: Kirigami.BasicListItem {
action: modelData
}
}
}
}
Component {
id: aboutPage
Kirigami.AboutPage {
aboutData: Controller.aboutData
}
}
}

View File

@@ -5,6 +5,5 @@ RoomPage 1.0 RoomPage.qml
RoomWindow 1.0 RoomWindow.qml
JoinRoomPage 1.0 JoinRoomPage.qml
InviteUserPage 1.0 InviteUserPage.qml
SettingsPage 1.0 SettingsPage.qml
ImageEditorPage 1.0 ImageEditorPage.qml

View File

@@ -18,6 +18,46 @@ Kirigami.OverlayDrawer {
id: roomDrawer
readonly property var room: RoomManager.currentRoom
width: modal ? undefined : actualWidth
readonly property int minWidth: Kirigami.Units.gridUnit * 15
readonly property int maxWidth: Kirigami.Units.gridUnit * 25
readonly property int defaultWidth: Kirigami.Units.gridUnit * 20
property int actualWidth: {
if (Config.roomDrawerWidth === -1) {
return Kirigami.Units.gridUnit * 20;
} else {
return Config.roomDrawerWidth
}
}
MouseArea {
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: undefined
width: 2
z: 500
cursorShape: !Kirigami.Settings.isMobile ? Qt.SplitHCursor : undefined
enabled: true
visible: true
onPressed: _lastX = mapToGlobal(mouseX, mouseY).x
onReleased: {
Config.roomDrawerWidth = roomDrawer.actualWidth;
Config.save();
}
property real _lastX: -1
onPositionChanged: {
if (_lastX === -1) {
return;
}
if (Qt.application.layoutDirection === Qt.RightToLeft) {
roomDrawer.actualWidth = Math.min(roomDrawer.maxWidth, Math.max(roomDrawer.minWidth, Config.roomDrawerWidth - _lastX + mapToGlobal(mouseX, mouseY).x))
} else {
roomDrawer.actualWidth = Math.min(roomDrawer.maxWidth, Math.max(roomDrawer.minWidth, Config.roomDrawerWidth + _lastX - mapToGlobal(mouseX, mouseY).x))
}
}
}
enabled: true
edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge
@@ -30,7 +70,6 @@ Kirigami.OverlayDrawer {
active: roomDrawer.drawerOpen
sourceComponent: ColumnLayout {
id: columnLayout
anchors.fill: parent
spacing: 0
Kirigami.AbstractApplicationHeader {
Layout.fillWidth: true
@@ -47,7 +86,7 @@ Kirigami.OverlayDrawer {
icon.name: "list-add-user"
text: i18n("Invite")
onClicked: {
applicationWindow().pageStack.layers.push("qrc:/imports/NeoChat/Page/InviteUserPage.qml", {"room": room})
applicationWindow().pageStack.layers.push("qrc:/imports/NeoChat/Page/InviteUserPage.qml", {room: room})
roomDrawer.close();
}
}
@@ -69,12 +108,7 @@ Kirigami.OverlayDrawer {
ToolButton {
Layout.alignment: Qt.AlignRight
icon.name: 'settings-configure'
onClicked: {
roomSettingDialog.createObject(ApplicationWindow.overlay, {"room": room}).open()
if (!wideScreen) {
roomDrawer.close();
}
}
onClicked: ApplicationWindow.window.pageStack.pushDialogLayer('qrc:/imports/NeoChat/RoomSettings/Categories.qml', {room: room})
ToolTip {
text: i18n("Room settings")
@@ -83,67 +117,61 @@ Kirigami.OverlayDrawer {
}
}
Control {
ColumnLayout {
Layout.fillWidth: true
padding: Kirigami.Units.largeSpacing
contentItem: ColumnLayout {
id: infoLayout
Layout.margins: Kirigami.Units.largeSpacing
Kirigami.Heading {
text: i18n("Room information")
level: 3
}
RowLayout {
Layout.fillWidth: true
Kirigami.Heading {
text: i18n("Room information")
level: 3
Layout.margins: Kirigami.Units.largeSpacing
spacing: Kirigami.Units.largeSpacing
Kirigami.Avatar {
Layout.preferredWidth: Kirigami.Units.gridUnit * 3.5
Layout.preferredHeight: Kirigami.Units.gridUnit * 3.5
name: room ? room.name : i18n("No name")
source: room ? ("image://mxc/" + room.avatarMediaId) : ""
}
RowLayout {
ColumnLayout {
Layout.fillWidth: true
Layout.margins: Kirigami.Units.largeSpacing
Layout.alignment: Qt.AlignVCenter
spacing: 0
spacing: Kirigami.Units.largeSpacing
Kirigami.Avatar {
Layout.preferredWidth: Kirigami.Units.gridUnit * 3.5
Layout.preferredHeight: Kirigami.Units.gridUnit * 3.5
name: room ? room.name : i18n("No name")
source: room ? ("image://mxc/" + room.avatarMediaId) : ""
}
ColumnLayout {
Kirigami.Heading {
Layout.maximumWidth: Kirigami.Units.gridUnit * 9
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
spacing: 0
Kirigami.Heading {
Layout.maximumWidth: Kirigami.Units.gridUnit * 9
Layout.fillWidth: true
level: 1
font.bold: true
wrapMode: Label.Wrap
text: room ? room.displayName : i18n("No name")
}
Label {
Layout.fillWidth: true
text: room && room.canonicalAlias ? room.canonicalAlias : i18n("No Canonical Alias")
}
level: 1
font.bold: true
wrapMode: Label.Wrap
text: room ? room.displayName : i18n("No name")
}
Label {
Layout.fillWidth: true
text: room && room.canonicalAlias ? room.canonicalAlias : i18n("No Canonical Alias")
}
}
}
TextEdit {
Layout.maximumWidth: Kirigami.Units.gridUnit * 13
Layout.preferredWidth: Kirigami.Units.gridUnit * 13
Layout.fillWidth: true
text: room && room.topic ? room.topic.replace(replaceLinks, "<a href=\"$1\">$1</a>") : i18n("No Topic")
readonly property var replaceLinks: /\(https:\/\/[^ ]*\)/
textFormat: TextEdit.MarkdownText
wrapMode: Text.WordWrap
selectByMouse: true
color: Kirigami.Theme.textColor
onLinkActivated: Qt.openUrlExternally(link)
readOnly: true
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.IBeamCursor
}
TextEdit {
Layout.fillWidth: true
text: room && room.topic ? room.topic.replace(replaceLinks, "<a href=\"$1\">$1</a>") : i18n("No Topic")
readonly property var replaceLinks: /\(https:\/\/[^ ]*\)/
textFormat: TextEdit.MarkdownText
wrapMode: Text.WordWrap
selectByMouse: true
color: Kirigami.Theme.textColor
onLinkActivated: Qt.openUrlExternally(link)
readOnly: true
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.IBeamCursor
}
}
}
@@ -153,7 +181,7 @@ Kirigami.OverlayDrawer {
activeFocusOnTab: false
Label {
Layout.alignment: Qt.AlignRight
text: room ? i18np("%1 Member", "%1 Members", room.totalMemberCount) : i18n("No Member Count")
text: room ? i18np("%1 Member", "%1 Members", room.joinedCount) : i18n("No Member Count")
}
}
@@ -161,6 +189,11 @@ Kirigami.OverlayDrawer {
padding: Kirigami.Units.smallSpacing
implicitWidth: parent.width
z: 2
background: Rectangle {
color: Kirigami.Theme.backgroundColor
Kirigami.Theme.inherit: false
Kirigami.Theme.colorSet: Kirigami.Theme.Window
}
contentItem: Kirigami.SearchField {
id: userListSearchField
onAccepted: sortedMessageEventModel.filterString = text;
@@ -187,6 +220,7 @@ Kirigami.OverlayDrawer {
sortRole: "perm"
filterRole: "name"
filterCaseSensitivity: Qt.CaseInsensitive
}
delegate: Kirigami.AbstractListItem {
@@ -254,12 +288,6 @@ Kirigami.OverlayDrawer {
}
}
Component {
id: roomSettingDialog
RoomSettingsDialog {}
}
Component {
id: userDetailDialog

View File

@@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
import QtQuick 2.15
import org.kde.kirigami 2.18 as Kirigami
import QtQuick.Controls 2.15 as Controls
import QtQuick.Layouts 1.15
Kirigami.CategorizedSettings {
id: root
required property var room
objectName: "settingsPage"
actions: [
Kirigami.SettingAction {
text: i18n("General")
icon.name: "settings-configure"
page: Qt.resolvedUrl("General.qml")
initialProperties: {
return {
room: root.room
}
}
},
Kirigami.SettingAction {
text: i18n("Security")
icon.name: "security-low"
page: Qt.resolvedUrl("Security.qml")
initialProperties: {
return {
room: root.room
}
}
}
]
}

View File

@@ -0,0 +1,202 @@
// SPDX-FileCopyrightText: 2019-2020 Black Hat <bhat@encom.eu.org>
// SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: GPL-3.0-only
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0
import NeoChat.Component 1.0
import NeoChat.Dialog 1.0
Kirigami.ScrollablePage {
id: root
property var room
readonly property bool canChangeAvatar: room.canSendState("m.room.avatar")
readonly property bool canChangeName: room.canSendState("m.room.name")
readonly property bool canChangeTopic: room.canSendState("m.room.topic")
readonly property bool canChangeCanonicalAlias: room.canSendState("m.room.canonical_alias")
title: i18n('General')
ColumnLayout {
Kirigami.FormLayout {
Layout.fillWidth: true
Kirigami.Avatar {
Layout.bottomMargin: Kirigami.Units.largeSpacing
name: room.name
source: room.avatarMediaId ? ("image://mxc/" + room.avatarMediaId) : ""
RoundButton {
anchors.right: parent.right
anchors.bottom: parent.bottom
height: Kirigami.Units.gridUnits
width: Kirigami.Units.gridUnits
icon.name: 'cloud-upload'
Accessible.name: i18n("Update avatar")
enabled: canChangeAvatar
onClicked: {
const fileDialog = openFileDialog.createObject(ApplicationWindow.overlay)
fileDialog.chosen.connect(function(path) {
if (!path) return
room.changeAvatar(path)
})
fileDialog.open()
}
}
}
TextField {
id: roomNameField
text: room.name
Kirigami.FormData.label: i18n("Room Name:")
enabled: canChangeName
}
TextArea {
id: roomTopicField
Layout.fillWidth: true
text: room.topic
Kirigami.FormData.label: i18n("Room topic:")
enabled: canChangeTopic
}
Kirigami.Separator {
Layout.fillWidth: true
visible: canonicalAliasComboBox.visible || altAlias.visible
}
ComboBox {
id: canonicalAliasComboBox
visible: room.aliases && room.aliases.length
Kirigami.FormData.label: i18n("Canonical Alias:")
popup.z: 999; // HACK This is an absolute hack, but combos inside OverlaySheets have their popups show up underneath, because of fun z ordering stuff
enabled: canChangeCanonicalAlias
model: room.aliases
currentIndex: room.aliases.indexOf(room.canonicalAlias)
onCurrentIndexChanged: {
if (room.canonicalAlias != room.aliases[currentIndex]) {
room.setCanonicalAlias(room.aliases[currentIndex])
}
}
}
RowLayout {
id: altAlias
Kirigami.FormData.label: i18n("Other Aliases:")
Layout.fillWidth: true
visible: room.altAliases && room.altAliases.length
ColumnLayout {
Layout.fillWidth: true
spacing: 0
Repeater {
model: room.altAliases
delegate: RowLayout {
Layout.maximumWidth: parent.width
Label {
text: modelData
}
ToolButton {
icon.name: ""
onClicked: room.removeLocalAlias(modelData)
}
}
}
}
}
}
Kirigami.Separator {
Layout.fillWidth: true
visible: next.visible || prev.visible
}
Control {
id: next
Layout.fillWidth: true
visible: room.predecessorId && room.connection.room(room.predecessorId)
padding: Kirigami.Units.largeSpacing
contentItem: Kirigami.InlineMessage {
text: i18n("This room continues another conversation.")
actions: Kirigami.Action {
text: i18n("See older messages...")
onTriggered: {
roomListForm.enteredRoom = Controller.activeConnection.room(room.predecessorId)
root.close()
}
}
}
}
Control {
id: prev
Layout.fillWidth: true
visible: room.successorId && room.connection.room(room.successorId)
padding: Kirigami.Units.largeSpacing
contentItem: Kirigami.InlineMessage {
text: i18n("This room has been replaced.")
actions: Kirigami.Action {
text: i18n("See new room...")
onTriggered: {
roomListForm.enteredRoom = Controller.activeConnection.room(room.successorId)
root.close()
}
}
}
}
Component {
id: openFileDialog
OpenFileDialog {}
}
}
footer: ToolBar {
contentItem: RowLayout {
Item {
Layout.fillWidth: true
}
Button {
Layout.alignment: Qt.AlignRight
enabled: room.name !== roomNameField.text || room.topic !== roomTopicField.text
text: i18n("Apply")
onClicked: {
if (room.name != roomNameField.text) {
room.setName(roomNameField.text)
}
if (room.topic != roomTopicField.text) {
room.setTopic(roomTopicField.text)
}
}
}
}
}
}

View File

@@ -0,0 +1,69 @@
// SPDX-FileCopyrightText: 2019-2020 Black Hat <bhat@encom.eu.org>
// SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: GPL-3.0-only
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0
import NeoChat.Component 1.0
import NeoChat.Dialog 1.0
Kirigami.ScrollablePage {
id: root
property var room
title: i18n('Security')
ColumnLayout {
Kirigami.FormLayout {
Layout.fillWidth: true
CheckBox {
text: i18nc("@option:check", "Private (invite only)")
Kirigami.FormData.label: i18nc("@option:check", "Access:")
checked: room.joinRule === "invite"
enabled: false
}
Label {
text: i18n("Only invited people can join.")
font: Kirigami.Theme.smallFont
}
CheckBox {
text: i18nc("@option:check", "Space members")
checked: room.joinRule === "restricted"
enabled: false
}
Label {
text: i18n("Anyone in a space can find and join.")
font: Kirigami.Theme.smallFont
}
CheckBox {
text: i18nc("@option:check", "Public")
checked: room.joinRule === "public"
enabled: false
}
Label {
text: i18nc("@option:check", "Anyone can find and join.") + room.joinRule
font: Kirigami.Theme.smallFont
}
}
}
footer: ToolBar {
contentItem: RowLayout {
Item {
Layout.fillWidth: true
}
Button {
Layout.alignment: Qt.AlignRight
enabled: false
text: i18n("Apply")
}
}
}
}

View File

@@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
// SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: LGPL-2.0-or-later
import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0
Kirigami.AboutPage {
title: i18nc('@title:window', 'About NeoChat')
aboutData: Controller.aboutData
}

View File

@@ -133,9 +133,7 @@ Kirigami.Page {
property var connection
header: Kirigami.Heading {
text: i18n("Edit Account")
}
title: i18n("Edit Account")
Kirigami.FormLayout {
RowLayout {

View File

@@ -12,6 +12,7 @@ import org.kde.neochat 1.0
import NeoChat.Settings 1.0
Kirigami.ScrollablePage {
title: i18nc('@title:window', 'Appearance')
ColumnLayout {
RowLayout {
Layout.alignment: Qt.AlignCenter

View File

@@ -82,9 +82,7 @@ Kirigami.Page {
property var index
header: Kirigami.Heading {
text: i18n("Remove device")
}
title: i18n("Remove device")
Kirigami.FormLayout {
Controls.TextField {
id: passwordField
@@ -107,9 +105,7 @@ Kirigami.Page {
property int index
property string name
header: Kirigami.Heading {
text: i18n("Edit device")
}
title: i18n("Edit device")
Kirigami.FormLayout {
Controls.TextField {
id: nameField

View File

@@ -14,6 +14,7 @@ import NeoChat.Component 1.0 as Components
import NeoChat.Dialog 1.0
Kirigami.Page {
title: i18nc('@title:window', 'Custom Emojis')
leftPadding: pageSettingStack.wideMode ? Kirigami.Units.smallSpacing : 0
topPadding: pageSettingStack.wideMode ? Kirigami.Units.smallSpacing : 0

View File

@@ -11,6 +11,7 @@ import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0
Kirigami.ScrollablePage {
title: i18nc('@title:window', 'General')
ColumnLayout {
Kirigami.FormLayout {
QQC2.CheckBox {

View File

@@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
import QtQuick 2.15
import org.kde.kirigami 2.18 as Kirigami
import QtQuick.Controls 2.15 as Controls
import QtQuick.Layouts 1.15
Kirigami.CategorizedSettings {
objectName: "settingsPage"
actions: [
Kirigami.SettingAction {
text: i18n("General")
icon.name: "org.kde.neochat"
page: Qt.resolvedUrl("GeneralSettingsPage.qml")
},
Kirigami.SettingAction {
text: i18n("Appearance")
icon.name: "preferences-desktop-theme-global"
page: Qt.resolvedUrl("AppearanceSettingsPage.qml")
},
Kirigami.SettingAction {
text: i18n("Accounts")
icon.name: "preferences-system-users"
page: Qt.resolvedUrl("AccountsPage.qml")
},
Kirigami.SettingAction {
text: i18n("Custom Emoji")
icon.name: "preferences-desktop-emoticons"
page: Qt.resolvedUrl("Emoticons.qml")
},
Kirigami.SettingAction {
text: i18n("Devices")
iconName: "network-connect"
page: Qt.resolvedUrl("DevicesPage.qml")
},
Kirigami.SettingAction {
text: i18n("About NeoChat")
icon.name: "help-about"
page: Qt.resolvedUrl("About.qml")
}
]
}

View File

@@ -1,2 +1,3 @@
module NeoChat.Settings
ThemeRadioButton 1.0 ThemeRadioButton.qml
SettingsPage 1.0 SettingsPage.qml

View File

@@ -117,7 +117,7 @@
<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 el mòbils i a l'escriptori, proporcionant un experiència d'usuari coherent.</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 el mòbils i a l'escriptori, proporcionant un 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>

View File

@@ -6,7 +6,7 @@ import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.19 as Kirigami
import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0
import NeoChat.Component 1.0
@@ -151,7 +151,6 @@ Kirigami.ApplicationWindow {
contextDrawer: RoomDrawer {
id: contextDrawer
contentItem.implicitWidth: columnWidth
edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge
modal: !root.wideScreen || !enabled
onEnabledChanged: drawerOpen = enabled && !modal
@@ -224,6 +223,56 @@ Kirigami.ApplicationWindow {
}
}
globalDrawer: Kirigami.GlobalDrawer {
property bool hasLayer
contentItem.implicitWidth: columnWidth
isMenu: true
actions: [
Kirigami.Action {
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") && 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") && Controller.accountCount > 0
},
Kirigami.Action {
text: i18n("Create a Room")
icon.name: "irc-join-channel"
onTriggered: {
let dialog = createRoomDialog.createObject(root.overlay);
dialog.open();
}
shortcut: StandardKey.New
enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat") && Controller.accountCount > 0
},
Kirigami.Action {
text: i18n("Settings")
icon.name: "settings-configure"
onTriggered: pageStack.pushDialogLayer("qrc:/imports/NeoChat/Settings/SettingsPage.qml")
enabled: pageStack.layers.currentItem.title !== i18n("Settings")
shortcut: StandardKey.Preferences
},
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()
}
]
}
Component.onCompleted: Controller.setBlur(pageStack, Config.blur && !Config.compactLayout);
Connections {
target: Config
@@ -242,6 +291,23 @@ Kirigami.ApplicationWindow {
background: Rectangle {
color: Config.blur && !Config.compactLayout ? Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 1 - Config.transparency) : "transparent"
}
Component {
id: roomListComponent
RoomListPage {
id: roomList
Connections {
target: root.roomPage
function onSwitchRoomUp() {
roomList.goToNextRoom();
}
function onSwitchRoomDown() {
roomList.goToPreviousRoom();
}
}
}
}
Connections {
target: Controller
@@ -294,9 +360,7 @@ Kirigami.ApplicationWindow {
property string url: ""
header: Kirigami.Heading {
text: i18n("User consent")
}
title: i18n("User consent")
QQC2.Label {
id: label
@@ -335,9 +399,7 @@ Kirigami.ApplicationWindow {
required property var user;
parent: QQC2.ApplicationWindow.overlay
header: Kirigami.Heading {
text: i18n("Start a chat")
}
title: i18n("Start a chat")
contentItem: QQC2.Label {
text: i18n("Do you want to start a chat with %1?", user.displayName)
wrapMode: Text.WordWrap
@@ -354,7 +416,7 @@ Kirigami.ApplicationWindow {
}
property Item hoverLinkIndicator: QQC2.Control {
parent: overlay.parent
parent: overlay.parent
property alias text: linkText.text
opacity: text.length > 0 ? 1 : 0
@@ -369,69 +431,4 @@ Kirigami.ApplicationWindow {
color: Kirigami.Theme.backgroundColor
}
}
Component {
id: settingsPageComponent
SettingsPage {}
}
Component {
id: roomListComponent
RoomListPage {
id: roomList
Connections {
target: root.roomPage
function onSwitchRoomUp() {
roomList.goToNextRoom();
}
function onSwitchRoomDown() {
roomList.goToPreviousRoom();
}
}
}
}
footer: Kirigami.NavigationTabBar {
actions: [
Kirigami.Action {
icon.name: "globe"
text: "Spaces"
checked: fooPage.visble
onTriggered: {
console.warn("foo")
}
},
Kirigami.Action {
icon.name: "dialog-messages"
text: "Rooms"
checked: fooPage.visble
onTriggered: {
while (pageStack.depth > 0) {
pageStack.pop();
}
pageStack.push(roomListComponent);
}
},
Kirigami.Action {
icon.name: "document-open-recent-symbolic"
text: "Recent"
checked: fooPage.visble
onTriggered: {
console.warn("foo")
}
},
Kirigami.Action {
icon.name: "settings-configure"
text: "Settings"
checked: fooPage.visble
onTriggered: {
while (pageStack.depth > 0) {
pageStack.pop();
}
pageStack.push(settingsPageComponent);
}
}
]
}
}

11
res.qrc
View File

@@ -6,14 +6,14 @@
<file>imports/NeoChat/Page/RoomListPage.qml</file>
<file>imports/NeoChat/Page/RoomPage.qml</file>
<file>imports/NeoChat/Page/RoomWindow.qml</file>
<file>imports/NeoChat/Page/AccountsPage.qml</file>
<file>imports/NeoChat/Page/JoinRoomPage.qml</file>
<file>imports/NeoChat/Page/InviteUserPage.qml</file>
<file>imports/NeoChat/Page/SettingsPage.qml</file>
<file>imports/NeoChat/Page/StartChatPage.qml</file>
<file>imports/NeoChat/Page/ImageEditorPage.qml</file>
<file>imports/NeoChat/Page/DevicesPage.qml</file>
<file>imports/NeoChat/Page/WelcomePage.qml</file>
<file>imports/NeoChat/RoomSettings/General.qml</file>
<file>imports/NeoChat/RoomSettings/Security.qml</file>
<file>imports/NeoChat/RoomSettings/Categories.qml</file>
<file>imports/NeoChat/Component/qmldir</file>
<file>imports/NeoChat/Component/FullScreenImage.qml</file>
<file>imports/NeoChat/Component/FancyEffectsContainer.qml</file>
@@ -57,7 +57,6 @@
<file>imports/NeoChat/Panel/qmldir</file>
<file>imports/NeoChat/Panel/RoomDrawer.qml</file>
<file>imports/NeoChat/Dialog/qmldir</file>
<file>imports/NeoChat/Dialog/RoomSettingsDialog.qml</file>
<file>imports/NeoChat/Dialog/UserDetailDialog.qml</file>
<file>imports/NeoChat/Dialog/CreateRoomDialog.qml</file>
<file>imports/NeoChat/Dialog/EmojiDialog.qml</file>
@@ -73,11 +72,15 @@
<file>qtquickcontrols2.conf</file>
<file>imports/NeoChat/Component/glowdot.png</file>
<file>imports/NeoChat/Component/confetti.png</file>
<file>imports/NeoChat/Settings/SettingsPage.qml</file>
<file>imports/NeoChat/Settings/ThemeRadioButton.qml</file>
<file>imports/NeoChat/Settings/ColorScheme.qml</file>
<file>imports/NeoChat/Settings/GeneralSettingsPage.qml</file>
<file>imports/NeoChat/Settings/Emoticons.qml</file>
<file>imports/NeoChat/Settings/AppearanceSettingsPage.qml</file>
<file>imports/NeoChat/Settings/AccountsPage.qml</file>
<file>imports/NeoChat/Settings/DevicesPage.qml</file>
<file>imports/NeoChat/Settings/About.qml</file>
<file>imports/NeoChat/Settings/qmldir</file>
</qresource>
</RCC>

View File

@@ -36,6 +36,7 @@ add_executable(neochat
spellcheckhighlighter.cpp
blurhash.cpp
blurhashimageprovider.cpp
joinrulesevent.cpp
../res.qrc
)
@@ -63,6 +64,18 @@ target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR})
target_link_libraries(neochat PRIVATE Qt::Quick Qt::Qml Qt::Gui Qt::Network Qt::QuickControls2 KF5::I18n KF5::Kirigami2 KF5::Notifications KF5::ConfigCore KF5::ConfigGui KF5::CoreAddons Quotient cmark::cmark ${QTKEYCHAIN_LIBRARIES} QCoro::QCoro)
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
if(TARGET KF5::Purpose)
function(add_share_plugin name)
kcoreaddons_add_plugin(${name} SOURCES ${ARGN} INSTALL_NAMESPACE "kf5/purpose")
target_link_libraries(${name} Qt5::Core KF5::Purpose)
set_target_properties(${name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/kf5/purpose")
endfunction()
add_share_plugin(neochatpurposeplugin neochatpurposeplugin.cpp)
target_link_libraries(neochatpurposeplugin KF5::I18n KF5::Service)
endif()
if(NEOCHAT_FLATPAK)
target_compile_definitions(neochat PRIVATE NEOCHAT_FLATPAK)
endif()

View File

@@ -106,7 +106,20 @@ void ActionsHandler::postMessage(const QString &text,
CustomEmojiModel *cem)
{
QString rawText = text;
QString cleanedText = text;
auto stringList = text.split(QStringLiteral("```"));
QString cleanedText;
const auto count = stringList.count();
for (int i = 0; i < count; i++) {
if (i % 2 == 0) {
if (i + 1 != count) {
cleanedText += stringList[i].toHtmlEscaped() + QStringLiteral("```");
} else {
cleanedText += stringList[i].toHtmlEscaped();
}
} else {
cleanedText += stringList[i] + QStringLiteral("```");
}
}
auto preprocess = [cem](const QString &it) -> QString {
if (cem == nullptr) {

16
src/joinrulesevent.cpp Normal file
View File

@@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: 2019 Kitsune Ral <Kitsune-Ral@users.sf.net>
// SPDX-License-Identifier: LGPL-2.1-or-later
#include "joinrulesevent.h"
using namespace Quotient;
QString JoinRulesEvent::joinRule() const
{
return fromJson<QString>(contentJson()["join_rule"_ls]);
}
QJsonArray JoinRulesEvent::allow() const
{
return contentJson()["allow"_ls].toArray();
}

29
src/joinrulesevent.h Normal file
View File

@@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include <events/stateevent.h>
#include <quotient_common.h>
namespace Quotient
{
class JoinRulesEvent : public StateEventBase
{
public:
DEFINE_EVENT_TYPEID("m.room.join_rules", JoinRulesEvent)
explicit JoinRulesEvent()
: StateEventBase(typeId(), matrixTypeId())
{
}
explicit JoinRulesEvent(const QJsonObject &obj)
: StateEventBase(typeId(), obj)
{
}
QString joinRule() const;
QJsonArray allow() const;
};
REGISTER_EVENT_TYPE(JoinRulesEvent)
} // namespace Quotient

View File

@@ -44,6 +44,7 @@
#include "devicesmodel.h"
#include "emojimodel.h"
#include "filetypesingleton.h"
#include "joinrulesevent.h"
#include "login.h"
#include "matriximageprovider.h"
#include "messageeventmodel.h"

View File

@@ -48,6 +48,9 @@
<entry name="RoomListPageWidth" type="int">
<default>-1</default>
</entry>
<entry name="RoomDrawerWidth" type="int">
<default>-1</default>
</entry>
</group>
<group name="Timeline">
<entry name="ShowAvatarInTimeline" type="bool">

View File

@@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: LGPL-2.1-or-later
#include <purpose/pluginbase.h>
#include <KApplicationTrader>
#include <KLocalizedString>
#include <KPluginFactory>
#include <QDesktopServices>
#include <QJsonArray>
#include <QProcess>
#include <QStandardPaths>
#include <QUrl>
#include <QUrlQuery>
EXPORT_SHARE_VERSION
namespace
{
class NeoChatShareJob : public Purpose::Job
{
Q_OBJECT
public:
explicit NeoChatShareJob(QObject *parent = nullptr)
: Purpose::Job(parent)
{
}
void start() override
{
}
};
}
class Q_DECL_EXPORT NeoChatPurposePlugin : public Purpose::PluginBase
{
Q_OBJECT
public:
NeoChatPurposePlugin(QObject *parent, const QVariantList &)
: Purpose::PluginBase(parent)
{
}
Purpose::Job *createJob() const override
{
return new NeoChatShareJob(nullptr);
}
};
K_PLUGIN_CLASS_WITH_JSON(NeoChatPurposePlugin, "neochatpurposeplugin.json")
#include "neochatpurposeplugin.moc"

View File

@@ -0,0 +1,22 @@
{
"KPlugin": {
"Authors": [
{
"Name": "Carl Schwan"
}
],
"Category": "Utilities",
"Description": "Send vith NeoChat",
"Icon": "mail-message",
"License": "GPL",
"Name": "Send with NeoChat",
"X-Purpose-ActionDisplay": "Send with NeoChat..."
},
"X-Purpose-Configuration": [],
"X-Purpose-Constraints": [],
"X-Purpose-PluginTypes": [
"Export",
"ShareUrl"
]
}

View File

@@ -462,6 +462,9 @@ QString NeoChatRoom::eventToString(const RoomEvent &evt, Qt::TextFormat format,
return e.isUpgrade() ? i18n("upgraded the room to version %1", e.version().isEmpty() ? "1" : e.version().toHtmlEscaped())
: i18n("created the room, version %1", e.version().isEmpty() ? "1" : e.version().toHtmlEscaped());
},
[](const RoomPowerLevelsEvent &) {
return i18nc("'power level' means permission level", "changed the power levels for this room");
},
[](const StateEventBase &e) {
if (e.matrixType() == QLatin1String("m.room.server_acl")) {
return i18n("changed the server access control lists for this room");
@@ -732,6 +735,11 @@ void NeoChatRoom::deleteMessagesByUser(const QString &user)
doDeleteMessagesByUser(user);
}
QString NeoChatRoom::joinRule() const
{
return getCurrentState<JoinRulesEvent>()->joinRule();
}
QCoro::Task<void> NeoChatRoom::doDeleteMessagesByUser(const QString &user)
{
QStringList events;

View File

@@ -3,6 +3,7 @@
#pragma once
#include "joinrulesevent.h"
#include <events/encryptionevent.h>
#include <events/redactionevent.h>
#include <events/roomavatarevent.h>
@@ -33,6 +34,7 @@ class NeoChatRoom : public Room
Q_PROPERTY(bool readMarkerLoaded READ readMarkerLoaded NOTIFY readMarkerLoadedChanged)
Q_PROPERTY(QDateTime lastActiveTime READ lastActiveTime NOTIFY lastActiveTimeChanged)
Q_PROPERTY(bool isInvite READ isInvite NOTIFY isInviteChanged)
Q_PROPERTY(QString joinRule READ joinRule CONSTANT)
Q_PROPERTY(QString htmlSafeDisplayName READ htmlSafeDisplayName NOTIFY displayNameChanged)
public:
@@ -66,6 +68,8 @@ public:
bool isEventHighlighted(const Quotient::RoomEvent *e) const;
[[nodiscard]] QString joinRule() const;
[[nodiscard]] bool hasFileUploading() const
{
return m_hasFileUploading;

View File

@@ -104,7 +104,7 @@ QVariant UserListModel::data(const QModelIndex &index, int role) const
return UserType::Member;
}
if (userPl < pl->powerLevelForState("m.room.message")) {
if (userPl < pl->powerLevelForEvent("m.room.message")) {
return UserType::Muted;
}