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
40 changed files with 712 additions and 453 deletions

View File

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

View File

@@ -17,8 +17,17 @@ TextEdit {
property bool isEmote: false property bool isEmote: false
property string textMessage: model.display property string textMessage: model.display
property bool spoilerRevealed: !hasSpoiler.test(textMessage) property bool spoilerRevealed: !hasSpoiler.test(textMessage)
property bool hasContextMenu: true
signal requestOpenMessageContext()
ListView.onReused: Qt.binding(() => !hasSpoiler.test(textMessage)) 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> text: "<style>
table { table {
width:100%; width:100%;
@@ -53,8 +62,6 @@ a{
wrapMode: Text.Wrap wrapMode: Text.Wrap
textFormat: Text.RichText textFormat: Text.RichText
Layout.fillWidth: true
onLinkActivated: RoomManager.openResource(link) onLinkActivated: RoomManager.openResource(link)
onHoveredLinkChanged: if (hoveredLink.length > 0) { onHoveredLinkChanged: if (hoveredLink.length > 0) {
applicationWindow().hoverLinkIndicator.text = hoveredLink; applicationWindow().hoverLinkIndicator.text = hoveredLink;
@@ -70,4 +77,16 @@ a{
enabled: !parent.hoveredLink && !spoilerRevealed enabled: !parent.hoveredLink && !spoilerRevealed
onTapped: spoilerRevealed = true 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 parent: applicationWindow().overlay
header: Kirigami.Heading { title: i18n("Create a Room")
text: i18n("Create a Room")
}
contentItem: Kirigami.FormLayout { contentItem: Kirigami.FormLayout {
TextField { 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 rightPadding: 0
topPadding: 0 topPadding: 0
header: Kirigami.Heading { title: i18nc("@title:menu Account detail dialog", "Account detail")
id: heading
text: i18nc("@title:menu Account detail dialog", "Account detail")
elide: Text.ElideRight
QQC2.ToolTip.visible: truncated && hovered
QQC2.ToolTip.text: text
}
contentItem: ColumnLayout { contentItem: ColumnLayout {
spacing: 0 spacing: 0

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -229,7 +229,6 @@ Kirigami.ScrollablePage {
ListView { ListView {
id: messageListView id: messageListView
pixelAligned: true
visible: !invitation.visible visible: !invitation.visible
readonly property int largestVisibleIndex: count > 0 ? indexAt(contentX + (width / 2), contentY + height - 1) : -1 readonly property int largestVisibleIndex: count > 0 ? indexAt(contentX + (width / 2), contentY + height - 1) : -1
@@ -372,18 +371,8 @@ Kirigami.ScrollablePage {
innerObject: TextDelegate { innerObject: TextDelegate {
isEmote: true isEmote: true
Layout.fillWidth: Config.compactLayout
Layout.maximumWidth: emoteContainer.bubbleMaxWidth Layout.maximumWidth: emoteContainer.bubbleMaxWidth
Layout.rightMargin: Kirigami.Units.largeSpacing onRequestOpenMessageContext: openMessageContext(model, parent.selectedText)
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)
}
} }
} }
} }
@@ -398,18 +387,8 @@ Kirigami.ScrollablePage {
hoverComponent: hoverActions hoverComponent: hoverActions
innerObject: TextDelegate { innerObject: TextDelegate {
Layout.fillWidth: Config.compactLayout
Layout.maximumWidth: messageContainer.bubbleMaxWidth Layout.maximumWidth: messageContainer.bubbleMaxWidth
Layout.rightMargin: Kirigami.Units.largeSpacing onRequestOpenMessageContext: openMessageContext(model, parent.selectedText)
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)
}
} }
} }
} }
@@ -424,9 +403,8 @@ Kirigami.ScrollablePage {
innerObject: TextDelegate { innerObject: TextDelegate {
Layout.fillWidth: !Config.compactLayout Layout.fillWidth: !Config.compactLayout
hasContextMenu: false
Layout.maximumWidth: noticeContainer.bubbleMaxWidth 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 Layout.maximumHeight: Kirigami.Units.gridUnit * 20
TapHandler { TapHandler {
acceptedButtons: Qt.RightButton acceptedButtons: Qt.RightButton
onTapped: openFileContext(author, model.display, eventId, toolTip, progressInfo, parent) onTapped: openFileContext(model, parent)
} }
TapHandler { TapHandler {
acceptedButtons: Qt.LeftButton acceptedButtons: Qt.LeftButton
onLongPressed: openFileContext(author, model.display, eventId, toolTip, progressInfo, parent) onLongPressed: openFileContext(model, parent)
onTapped: { 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 Layout.maximumWidth: audioContainer.bubbleMaxWidth
TapHandler { TapHandler {
acceptedButtons: Qt.RightButton acceptedButtons: Qt.RightButton
onTapped: openFileContext(author, model.display, eventId, toolTip, progressInfo, parent) onTapped: openFileContext(model, parent)
} }
TapHandler { TapHandler {
acceptedButtons: Qt.LeftButton acceptedButtons: Qt.LeftButton
onLongPressed: openFileContext(author, model.display, eventId, toolTip, progressInfo, parent) onLongPressed: openFileContext(model, parent)
} }
} }
} }
@@ -520,11 +504,11 @@ Kirigami.ScrollablePage {
TapHandler { TapHandler {
acceptedButtons: Qt.RightButton acceptedButtons: Qt.RightButton
onTapped: openFileContext(author, model.display, eventId, toolTip, progressInfo, parent) onTapped: openFileContext(model, parent)
} }
TapHandler { TapHandler {
acceptedButtons: Qt.LeftButton 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 Layout.maximumWidth: fileContainer.bubbleMaxWidth
TapHandler { TapHandler {
acceptedButtons: Qt.RightButton acceptedButtons: Qt.RightButton
onTapped: openFileContext(author, model.display, eventId, toolTip, progressInfo, parent) onTapped: openFileContext(model, parent)
} }
TapHandler { TapHandler {
acceptedButtons: Qt.LeftButton 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 /// 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, { const contextMenu = fileDelegateContextMenu.createObject(page, {
author: author, author: event.author,
message: message, message: event.message,
eventId: eventId, eventId: event.eventId,
source: source, source: event.toolTip,
file: file, file: file,
progressInfo: progressInfo, progressInfo: event.progressInfo,
}); });
contextMenu.open(); contextMenu.open();
} }
/// Open context menu for normal message /// Open context menu for normal message
function openMessageContext(author, message, eventId, source, eventType, formattedBody, selectedText) { function openMessageContext(event, selectedText) {
const contextMenu = messageDelegateContextMenu.createObject(page, { const contextMenu = messageDelegateContextMenu.createObject(page, {
selectedText: selectedText, selectedText: selectedText,
author: author, author: event.author,
message: message, message: event.message,
eventId: eventId, eventId: event.eventId,
formattedBody: formattedBody, formattedBody: event.formattedBody,
source: source, source: event.toolTip,
eventType: eventType eventType: event.eventType
}); });
contextMenu.open(); 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 RoomWindow 1.0 RoomWindow.qml
JoinRoomPage 1.0 JoinRoomPage.qml JoinRoomPage 1.0 JoinRoomPage.qml
InviteUserPage 1.0 InviteUserPage.qml InviteUserPage 1.0 InviteUserPage.qml
SettingsPage 1.0 SettingsPage.qml
ImageEditorPage 1.0 ImageEditorPage.qml ImageEditorPage 1.0 ImageEditorPage.qml

View File

@@ -18,6 +18,46 @@ Kirigami.OverlayDrawer {
id: roomDrawer id: roomDrawer
readonly property var room: RoomManager.currentRoom 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 enabled: true
edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge
@@ -30,7 +70,6 @@ Kirigami.OverlayDrawer {
active: roomDrawer.drawerOpen active: roomDrawer.drawerOpen
sourceComponent: ColumnLayout { sourceComponent: ColumnLayout {
id: columnLayout id: columnLayout
anchors.fill: parent
spacing: 0 spacing: 0
Kirigami.AbstractApplicationHeader { Kirigami.AbstractApplicationHeader {
Layout.fillWidth: true Layout.fillWidth: true
@@ -47,7 +86,7 @@ Kirigami.OverlayDrawer {
icon.name: "list-add-user" icon.name: "list-add-user"
text: i18n("Invite") text: i18n("Invite")
onClicked: { 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(); roomDrawer.close();
} }
} }
@@ -69,12 +108,7 @@ Kirigami.OverlayDrawer {
ToolButton { ToolButton {
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
icon.name: 'settings-configure' icon.name: 'settings-configure'
onClicked: { onClicked: ApplicationWindow.window.pageStack.pushDialogLayer('qrc:/imports/NeoChat/RoomSettings/Categories.qml', {room: room})
roomSettingDialog.createObject(ApplicationWindow.overlay, {"room": room}).open()
if (!wideScreen) {
roomDrawer.close();
}
}
ToolTip { ToolTip {
text: i18n("Room settings") text: i18n("Room settings")
@@ -83,67 +117,61 @@ Kirigami.OverlayDrawer {
} }
} }
Control { ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
padding: Kirigami.Units.largeSpacing Layout.margins: Kirigami.Units.largeSpacing
contentItem: ColumnLayout { Kirigami.Heading {
id: infoLayout text: i18n("Room information")
level: 3
}
RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
Kirigami.Heading { Layout.margins: Kirigami.Units.largeSpacing
text: i18n("Room information")
level: 3 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.fillWidth: true
Layout.margins: Kirigami.Units.largeSpacing Layout.alignment: Qt.AlignVCenter
spacing: 0
spacing: Kirigami.Units.largeSpacing Kirigami.Heading {
Layout.maximumWidth: Kirigami.Units.gridUnit * 9
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 {
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter level: 1
spacing: 0 font.bold: true
wrapMode: Label.Wrap
Kirigami.Heading { text: room ? room.displayName : i18n("No name")
Layout.maximumWidth: Kirigami.Units.gridUnit * 9 }
Layout.fillWidth: true Label {
level: 1 Layout.fillWidth: true
font.bold: true text: room && room.canonicalAlias ? room.canonicalAlias : i18n("No Canonical Alias")
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 { TextEdit {
Layout.maximumWidth: Kirigami.Units.gridUnit * 13 Layout.fillWidth: true
Layout.preferredWidth: Kirigami.Units.gridUnit * 13 text: room && room.topic ? room.topic.replace(replaceLinks, "<a href=\"$1\">$1</a>") : i18n("No Topic")
Layout.fillWidth: true readonly property var replaceLinks: /\(https:\/\/[^ ]*\)/
text: room && room.topic ? room.topic.replace(replaceLinks, "<a href=\"$1\">$1</a>") : i18n("No Topic") textFormat: TextEdit.MarkdownText
readonly property var replaceLinks: /\(https:\/\/[^ ]*\)/ wrapMode: Text.WordWrap
textFormat: TextEdit.MarkdownText selectByMouse: true
wrapMode: Text.WordWrap color: Kirigami.Theme.textColor
selectByMouse: true onLinkActivated: Qt.openUrlExternally(link)
color: Kirigami.Theme.textColor readOnly: true
onLinkActivated: Qt.openUrlExternally(link) MouseArea {
readOnly: true anchors.fill: parent
MouseArea { acceptedButtons: Qt.NoButton
anchors.fill: parent cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.IBeamCursor
acceptedButtons: Qt.NoButton
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.IBeamCursor
}
} }
} }
} }
@@ -153,7 +181,7 @@ Kirigami.OverlayDrawer {
activeFocusOnTab: false activeFocusOnTab: false
Label { Label {
Layout.alignment: Qt.AlignRight 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 padding: Kirigami.Units.smallSpacing
implicitWidth: parent.width implicitWidth: parent.width
z: 2 z: 2
background: Rectangle {
color: Kirigami.Theme.backgroundColor
Kirigami.Theme.inherit: false
Kirigami.Theme.colorSet: Kirigami.Theme.Window
}
contentItem: Kirigami.SearchField { contentItem: Kirigami.SearchField {
id: userListSearchField id: userListSearchField
onAccepted: sortedMessageEventModel.filterString = text; onAccepted: sortedMessageEventModel.filterString = text;
@@ -187,6 +220,7 @@ Kirigami.OverlayDrawer {
sortRole: "perm" sortRole: "perm"
filterRole: "name" filterRole: "name"
filterCaseSensitivity: Qt.CaseInsensitive
} }
delegate: Kirigami.AbstractListItem { delegate: Kirigami.AbstractListItem {
@@ -254,12 +288,6 @@ Kirigami.OverlayDrawer {
} }
} }
Component {
id: roomSettingDialog
RoomSettingsDialog {}
}
Component { Component {
id: userDetailDialog 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 property var connection
header: Kirigami.Heading { title: i18n("Edit Account")
text: i18n("Edit Account")
}
Kirigami.FormLayout { Kirigami.FormLayout {
RowLayout { RowLayout {

View File

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

View File

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

View File

@@ -14,6 +14,7 @@ import NeoChat.Component 1.0 as Components
import NeoChat.Dialog 1.0 import NeoChat.Dialog 1.0
Kirigami.Page { Kirigami.Page {
title: i18nc('@title:window', 'Custom Emojis')
leftPadding: pageSettingStack.wideMode ? Kirigami.Units.smallSpacing : 0 leftPadding: pageSettingStack.wideMode ? Kirigami.Units.smallSpacing : 0
topPadding: 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 import org.kde.neochat 1.0
Kirigami.ScrollablePage { Kirigami.ScrollablePage {
title: i18nc('@title:window', 'General')
ColumnLayout { ColumnLayout {
Kirigami.FormLayout { Kirigami.FormLayout {
QQC2.CheckBox { 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 module NeoChat.Settings
ThemeRadioButton 1.0 ThemeRadioButton.qml 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 xml:lang="zh-CN">Matrix 是一个分布式通讯协议,使用户重新得到控制权。 目前NeoChat 实现了协议的大部分,除了加密聊天和视频聊天。</p>
<p>NeoChat works both on mobile and desktop while providing a consistent user experience.</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="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="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="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="en-GB">NeoChat works both on mobile and desktop while providing a consistent user experience.</p>

View File

@@ -151,7 +151,6 @@ Kirigami.ApplicationWindow {
contextDrawer: RoomDrawer { contextDrawer: RoomDrawer {
id: contextDrawer id: contextDrawer
contentItem.implicitWidth: columnWidth
edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge
modal: !root.wideScreen || !enabled modal: !root.wideScreen || !enabled
onEnabledChanged: drawerOpen = enabled && !modal onEnabledChanged: drawerOpen = enabled && !modal
@@ -255,7 +254,7 @@ Kirigami.ApplicationWindow {
Kirigami.Action { Kirigami.Action {
text: i18n("Settings") text: i18n("Settings")
icon.name: "settings-configure" icon.name: "settings-configure"
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/SettingsPage.qml") onTriggered: pageStack.pushDialogLayer("qrc:/imports/NeoChat/Settings/SettingsPage.qml")
enabled: pageStack.layers.currentItem.title !== i18n("Settings") enabled: pageStack.layers.currentItem.title !== i18n("Settings")
shortcut: StandardKey.Preferences shortcut: StandardKey.Preferences
}, },
@@ -361,9 +360,7 @@ Kirigami.ApplicationWindow {
property string url: "" property string url: ""
header: Kirigami.Heading { title: i18n("User consent")
text: i18n("User consent")
}
QQC2.Label { QQC2.Label {
id: label id: label
@@ -402,9 +399,7 @@ Kirigami.ApplicationWindow {
required property var user; required property var user;
parent: QQC2.ApplicationWindow.overlay parent: QQC2.ApplicationWindow.overlay
header: Kirigami.Heading { title: i18n("Start a chat")
text: i18n("Start a chat")
}
contentItem: QQC2.Label { contentItem: QQC2.Label {
text: i18n("Do you want to start a chat with %1?", user.displayName) text: i18n("Do you want to start a chat with %1?", user.displayName)
wrapMode: Text.WordWrap wrapMode: Text.WordWrap

11
res.qrc
View File

@@ -6,14 +6,14 @@
<file>imports/NeoChat/Page/RoomListPage.qml</file> <file>imports/NeoChat/Page/RoomListPage.qml</file>
<file>imports/NeoChat/Page/RoomPage.qml</file> <file>imports/NeoChat/Page/RoomPage.qml</file>
<file>imports/NeoChat/Page/RoomWindow.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/JoinRoomPage.qml</file>
<file>imports/NeoChat/Page/InviteUserPage.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/StartChatPage.qml</file>
<file>imports/NeoChat/Page/ImageEditorPage.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/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/qmldir</file>
<file>imports/NeoChat/Component/FullScreenImage.qml</file> <file>imports/NeoChat/Component/FullScreenImage.qml</file>
<file>imports/NeoChat/Component/FancyEffectsContainer.qml</file> <file>imports/NeoChat/Component/FancyEffectsContainer.qml</file>
@@ -57,7 +57,6 @@
<file>imports/NeoChat/Panel/qmldir</file> <file>imports/NeoChat/Panel/qmldir</file>
<file>imports/NeoChat/Panel/RoomDrawer.qml</file> <file>imports/NeoChat/Panel/RoomDrawer.qml</file>
<file>imports/NeoChat/Dialog/qmldir</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/UserDetailDialog.qml</file>
<file>imports/NeoChat/Dialog/CreateRoomDialog.qml</file> <file>imports/NeoChat/Dialog/CreateRoomDialog.qml</file>
<file>imports/NeoChat/Dialog/EmojiDialog.qml</file> <file>imports/NeoChat/Dialog/EmojiDialog.qml</file>
@@ -73,11 +72,15 @@
<file>qtquickcontrols2.conf</file> <file>qtquickcontrols2.conf</file>
<file>imports/NeoChat/Component/glowdot.png</file> <file>imports/NeoChat/Component/glowdot.png</file>
<file>imports/NeoChat/Component/confetti.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/ThemeRadioButton.qml</file>
<file>imports/NeoChat/Settings/ColorScheme.qml</file> <file>imports/NeoChat/Settings/ColorScheme.qml</file>
<file>imports/NeoChat/Settings/GeneralSettingsPage.qml</file> <file>imports/NeoChat/Settings/GeneralSettingsPage.qml</file>
<file>imports/NeoChat/Settings/Emoticons.qml</file> <file>imports/NeoChat/Settings/Emoticons.qml</file>
<file>imports/NeoChat/Settings/AppearanceSettingsPage.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> <file>imports/NeoChat/Settings/qmldir</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@@ -36,6 +36,7 @@ add_executable(neochat
spellcheckhighlighter.cpp spellcheckhighlighter.cpp
blurhash.cpp blurhash.cpp
blurhashimageprovider.cpp blurhashimageprovider.cpp
joinrulesevent.cpp
../res.qrc ../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) 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) 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) if(NEOCHAT_FLATPAK)
target_compile_definitions(neochat PRIVATE NEOCHAT_FLATPAK) target_compile_definitions(neochat PRIVATE NEOCHAT_FLATPAK)
endif() endif()

View File

@@ -106,7 +106,20 @@ void ActionsHandler::postMessage(const QString &text,
CustomEmojiModel *cem) CustomEmojiModel *cem)
{ {
QString rawText = text; 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 { auto preprocess = [cem](const QString &it) -> QString {
if (cem == nullptr) { 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 "devicesmodel.h"
#include "emojimodel.h" #include "emojimodel.h"
#include "filetypesingleton.h" #include "filetypesingleton.h"
#include "joinrulesevent.h"
#include "login.h" #include "login.h"
#include "matriximageprovider.h" #include "matriximageprovider.h"
#include "messageeventmodel.h" #include "messageeventmodel.h"

View File

@@ -48,6 +48,9 @@
<entry name="RoomListPageWidth" type="int"> <entry name="RoomListPageWidth" type="int">
<default>-1</default> <default>-1</default>
</entry> </entry>
<entry name="RoomDrawerWidth" type="int">
<default>-1</default>
</entry>
</group> </group>
<group name="Timeline"> <group name="Timeline">
<entry name="ShowAvatarInTimeline" type="bool"> <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()) 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()); : 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) { [](const StateEventBase &e) {
if (e.matrixType() == QLatin1String("m.room.server_acl")) { if (e.matrixType() == QLatin1String("m.room.server_acl")) {
return i18n("changed the server access control lists for this room"); return i18n("changed the server access control lists for this room");
@@ -732,6 +735,11 @@ void NeoChatRoom::deleteMessagesByUser(const QString &user)
doDeleteMessagesByUser(user); doDeleteMessagesByUser(user);
} }
QString NeoChatRoom::joinRule() const
{
return getCurrentState<JoinRulesEvent>()->joinRule();
}
QCoro::Task<void> NeoChatRoom::doDeleteMessagesByUser(const QString &user) QCoro::Task<void> NeoChatRoom::doDeleteMessagesByUser(const QString &user)
{ {
QStringList events; QStringList events;

View File

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

View File

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