Rebrand files names Spectral -> NeoChat

This commit is contained in:
Carl Schwan
2020-11-08 23:17:30 +01:00
parent c2c49c69fe
commit 122a7cdd2f
71 changed files with 787 additions and 273 deletions

View File

@@ -1,6 +1,6 @@
import QtQuick 2.12
import Spectral.Setting 0.1
import NeoChat.Setting 0.1
MouseArea {
signal primaryClicked()

View File

@@ -0,0 +1,468 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import org.kde.kirigami 2.13 as Kirigami
import NeoChat.Component 2.0
import NeoChat.Component.Emoji 2.0
import NeoChat.Dialog 2.0
import NeoChat.Effect 2.0
import NeoChat.Setting 0.1
import org.kde.neochat 0.1
Control {
id: root
property alias isReply: replyItem.visible
property bool isReaction: false
property var replyUser
property string replyEventID
property string replyContent
property alias isAutoCompleting: autoCompleteListView.visible
property var autoCompleteModel
property int autoCompleteBeginPosition
property int autoCompleteEndPosition
property bool hasAttachment: false
property url attachmentPath
padding: 0
background: Rectangle {
color: Kirigami.Theme.backgroundColor
Kirigami.Separator {
Rectangle {
anchors.fill: parent
color: Kirigami.Theme.focusColor
}
anchors {
left: parent.left
right: parent.right
top: parent.top
}
}
}
contentItem: ColumnLayout {
spacing: 0
RowLayout {
Layout.fillWidth: true
Layout.margins: 8
id: replyItem
visible: false
spacing: 8
Control {
Layout.alignment: Qt.AlignTop
padding: 4
contentItem: RowLayout {
Kirigami.Avatar {
Layout.preferredWidth: 24
Layout.preferredHeight: 24
source: replyUser ? "image://mxc/" + replyUser.avatarMediaId: ""
name: replyUser ? replyUser.displayName : "No name"
}
Label {
Layout.alignment: Qt.AlignVCenter
text: replyUser ? replyUser.displayName : "No name"
rightPadding: 8
}
}
}
TextEdit {
Layout.fillWidth: true
text: "<style>a{color: " + color + ";} .user-pill{}</style>" + replyContent
selectByMouse: true
readOnly: true
wrapMode: Label.Wrap
selectedTextColor: "white"
textFormat: Text.RichText
}
}
EmojiPicker {
Layout.fillWidth: true
id: emojiPicker
visible: false
textArea: inputField
emojiModel: EmojiModel { id: emojiModel }
}
ListView {
Layout.fillWidth: true
Layout.preferredHeight: 36
Layout.margins: 8
id: autoCompleteListView
visible: false
model: autoCompleteModel
clip: true
spacing: 4
orientation: ListView.Horizontal
highlightFollowsCurrentItem: true
keyNavigationWraps: true
delegate: Control {
property string autoCompleteText: modelData.displayName ?? modelData.unicode
property bool isEmoji: modelData.unicode != null
readonly property bool highlighted: autoCompleteListView.currentIndex == index
height: 36
padding: 6
background: Rectangle {
visible: !isEmoji
color: highlighted ? border.color : "transparent"
border.color: isEmoji ? MPalette.accent : modelData.color
border.width: 2
radius: height / 2
}
contentItem: RowLayout {
spacing: 6
Text {
width: 24
height: 24
visible: isEmoji
text: autoCompleteText
font.pixelSize: 24
font.family: "Emoji"
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
Kirigami.Avatar {
Layout.preferredWidth: 24
Layout.preferredHeight: 24
source: modelData.avatarMediaId ? "image://mxc/" + modelData.avatarMediaId : ""
color: modelData.color ? Qt.darker(modelData.color, 1.1) : null
}
Label {
Layout.fillHeight: true
visible: !isEmoji
text: autoCompleteText
color: highlighted ? Kirigami.Theme.highlightTextColor : Kirigami.Theme.textColor
verticalAlignment: Text.AlignVCenter
rightPadding: 8
}
}
MouseArea {
anchors.fill: parent
onClicked: {
autoCompleteListView.currentIndex = index
inputField.replaceAutoComplete(autoCompleteText)
}
}
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
Layout.leftMargin: 12
Layout.rightMargin: 12
visible: emojiPicker.visible || replyItem.visible || autoCompleteListView.visible
}
RowLayout {
Layout.fillWidth: true
spacing: 0
ToolButton {
id: uploadButton
Layout.preferredWidth: 48
Layout.preferredHeight: 48
Layout.alignment: Qt.AlignBottom
visible: !isReply && !hasAttachment
icon.name: "mail-attachment"
onClicked: {
if (Clipboard.hasImage) {
attachDialog.open()
} else {
var fileDialog = openFileDialog.createObject(ApplicationWindow.overlay)
fileDialog.chosen.connect(function(path) {
if (!path) return
root.attach(path)
})
fileDialog.open()
}
}
BusyIndicator {
anchors.fill: parent
running: currentRoom && currentRoom.hasFileUploading
}
}
ToolButton {
Layout.preferredWidth: 48
Layout.preferredHeight: 48
Layout.alignment: Qt.AlignBottom
id: cancelReplyButton
visible: isReply
icon.name: "dialog-cancel"
onClicked: clearReply()
}
Control {
Layout.margins: 6
Layout.preferredHeight: 36
Layout.alignment: Qt.AlignVCenter
visible: hasAttachment
rightPadding: 8
contentItem: RowLayout {
spacing: 0
ToolButton {
Layout.preferredWidth: height
Layout.fillHeight: true
id: cancelAttachmentButton
icon.name: "dialog-cancel"
onClicked: {
hasAttachment = false;
attachmentPath = "";
}
}
Label {
Layout.alignment: Qt.AlignVCenter
text: attachmentPath !== "" ? attachmentPath.toString().substring(attachmentPath.toString().lastIndexOf('/') + 1, attachmentPath.length) : ""
}
}
}
TextArea {
property real progress: 0
Layout.fillWidth: true
Layout.minimumHeight: 48
id: inputField
wrapMode: Text.Wrap
placeholderText: "Send a Message"
topPadding: 0
bottomPadding: 0
selectByMouse: true
verticalAlignment: TextEdit.AlignVCenter
text: currentRoom != null ? currentRoom.cachedInput : ""
background: Item {}
Rectangle {
width: currentRoom && currentRoom.hasFileUploading ? parent.width * currentRoom.fileUploadingProgress / 100 : 0
height: parent.height
opacity: 0.2
}
Timer {
id: timeoutTimer
repeat: false
interval: 2000
onTriggered: {
repeatTimer.stop()
currentRoom.sendTypingNotification(false)
}
}
Timer {
id: repeatTimer
repeat: true
interval: 5000
triggeredOnStart: true
onTriggered: currentRoom.sendTypingNotification(true)
}
Keys.onReturnPressed: {
if (event.modifiers & Qt.ShiftModifier) {
insert(cursorPosition, "\n")
} else {
postMessage(text)
text = ""
clearReply()
closeAll()
}
}
Keys.onEscapePressed: closeAll()
Keys.onBacktabPressed: if (isAutoCompleting) autoCompleteListView.decrementCurrentIndex()
Keys.onTabPressed: {
if (isAutoCompleting) {
autoCompleteListView.incrementCurrentIndex()
} else {
autoCompleteBeginPosition = text.substring(0, cursorPosition).lastIndexOf(" ") + 1
var autoCompletePrefix = text.substring(0, cursorPosition).split(" ").pop()
if (!autoCompletePrefix) return
if (autoCompletePrefix.startsWith(":")) {
autoCompleteBeginPosition = text.substring(0, cursorPosition).lastIndexOf(" ") + 1
autoCompleteModel = emojiModel.filterModel(autoCompletePrefix)
if (autoCompleteModel.length === 0) return
isAutoCompleting = true
autoCompleteEndPosition = cursorPosition
} else {
autoCompleteModel = currentRoom.getUsers(autoCompletePrefix)
if (autoCompleteModel.length === 0) return
isAutoCompleting = true
autoCompleteEndPosition = cursorPosition
}
}
replaceAutoComplete(autoCompleteListView.currentItem.autoCompleteText)
}
onTextChanged: {
timeoutTimer.restart()
repeatTimer.start()
currentRoom.cachedInput = text
if (cursorPosition !== autoCompleteBeginPosition && cursorPosition !== autoCompleteEndPosition) {
isAutoCompleting = false
autoCompleteListView.currentIndex = 0
}
}
function replaceAutoComplete(word) {
remove(autoCompleteBeginPosition, autoCompleteEndPosition)
autoCompleteEndPosition = autoCompleteBeginPosition + word.length
insert(cursorPosition, word)
}
function postMessage(text) {
if(!currentRoom) { return }
if (hasAttachment) {
currentRoom.uploadFile(attachmentPath, text)
clearAttachment()
return
}
if (text.trim().length === 0) { return }
var PREFIX_ME = '/me '
var PREFIX_NOTICE = '/notice '
var PREFIX_RAINBOW = '/rainbow '
var messageEventType = RoomMessageEvent.Text
if (text.indexOf(PREFIX_RAINBOW) === 0) {
text = text.substr(PREFIX_RAINBOW.length)
var parsedText = ""
var rainbowColor = ["#ff2b00", "#ff5500", "#ff8000", "#ffaa00", "#ffd500", "#ffff00", "#d4ff00", "#aaff00", "#80ff00", "#55ff00", "#2bff00", "#00ff00", "#00ff2b", "#00ff55", "#00ff80", "#00ffaa", "#00ffd5", "#00ffff", "#00d4ff", "#00aaff", "#007fff", "#0055ff", "#002bff", "#0000ff", "#2a00ff", "#5500ff", "#7f00ff", "#aa00ff", "#d400ff", "#ff00ff", "#ff00d4", "#ff00aa", "#ff0080", "#ff0055", "#ff002b", "#ff0000"]
for (var i = 0; i < text.length; i++) {
parsedText = parsedText + "<font color='" + rainbowColor[i % rainbowColor.length] + "'>" + text.charAt(i) + "</font>"
}
currentRoom.postHtmlMessage(text, parsedText, RoomMessageEvent.Text, replyEventID)
return
}
if (text.indexOf(PREFIX_ME) === 0) {
text = text.substr(PREFIX_ME.length)
messageEventType = RoomMessageEvent.Emote
} else if (text.indexOf(PREFIX_NOTICE) === 0) {
text = text.substr(PREFIX_NOTICE.length)
messageEventType = RoomMessageEvent.Notice
}
console.log(replyContent, replyUser, replyEventID, messageEventType);
currentRoom.postArbitaryMessage(text, messageEventType, replyEventID)
}
}
ToolButton {
Layout.preferredWidth: 48
Layout.preferredHeight: 48
Layout.alignment: Qt.AlignBottom
id: emojiButton
icon.name: "preferences-desktop-emoticons"
onClicked: emojiPicker.visible = !emojiPicker.visible
}
}
}
function insert(str) {
inputField.insert(inputField.cursorPosition, str)
}
function clear() {
inputField.clear()
}
function clearReply() {
isReply = false
replyUser = null;
replyContent = "";
replyEventID = ""
}
function focus() {
inputField.forceActiveFocus()
}
function closeAll() {
replyItem.visible = false
autoCompleteListView.visible = false
emojiPicker.visible = false
}
function attach(localPath) {
hasAttachment = true
attachmentPath = localPath
}
function clearAttachment() {
hasAttachment = false
attachmentPath = ""
}
}

View File

@@ -3,10 +3,10 @@ import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls.Material 2.12
import Spectral.Component 2.0
import NeoChat.Component 2.0
import Spectral 0.1
import Spectral.Setting 0.1
import org.kde.neochat 0.1
import NeoChat.Setting 0.1
ColumnLayout {
property string emojiCategory: "history"

View File

@@ -1,2 +1,2 @@
module Spectral.Component.Emoji
module NeoChat.Component.Emoji
EmojiPicker 2.0 EmojiPicker.qml

View File

@@ -6,13 +6,13 @@ import Qt.labs.platform 1.0 as Platform
import QtMultimedia 5.12
import org.kde.kirigami 2.13 as Kirigami
import Spectral 0.1
import Spectral.Setting 0.1
import org.kde.neochat 0.1
import NeoChat.Setting 0.1
import Spectral.Component 2.0
import Spectral.Dialog 2.0
import Spectral.Menu.Timeline 2.0
import Spectral.Effect 2.0
import NeoChat.Component 2.0
import NeoChat.Dialog 2.0
import NeoChat.Menu.Timeline 2.0
import NeoChat.Effect 2.0
RowLayout {
readonly property bool avatarVisible: !sentByMe && showAuthor

View File

@@ -6,13 +6,13 @@ import QtGraphicalEffects 1.0
import Qt.labs.platform 1.0 as Platform
import org.kde.kirigami 2.13 as Kirigami
import Spectral 0.1
import Spectral.Setting 0.1
import org.kde.neochat 0.1
import NeoChat.Setting 0.1
import Spectral.Component 2.0
import Spectral.Dialog 2.0
import Spectral.Menu.Timeline 2.0
import Spectral.Effect 2.0
import NeoChat.Component 2.0
import NeoChat.Dialog 2.0
import NeoChat.Menu.Timeline 2.0
import NeoChat.Effect 2.0
RowLayout {
readonly property bool avatarVisible: !sentByMe && showAuthor

View File

@@ -4,13 +4,13 @@ import QtQuick.Layouts 1.12
import QtGraphicalEffects 1.0
import Qt.labs.platform 1.0 as Platform
import Spectral 0.1
import Spectral.Setting 0.1
import org.kde.neochat 0.1
import NeoChat.Setting 0.1
import Spectral.Component 2.0
import Spectral.Dialog 2.0
import Spectral.Menu.Timeline 2.0
import Spectral.Effect 2.0
import NeoChat.Component 2.0
import NeoChat.Dialog 2.0
import NeoChat.Menu.Timeline 2.0
import NeoChat.Effect 2.0
Image {
readonly property bool isAnimated: contentType === "image/gif"

View File

@@ -11,9 +11,9 @@ import QtGraphicalEffects 1.12
import org.kde.kirigami 2.13 as Kirigami
import Spectral 0.1
import Spectral.Setting 0.1
import Spectral.Component 2.0
import org.kde.neochat 0.1
import NeoChat.Setting 0.1
import NeoChat.Component 2.0
RowLayout {
default property alias innerObject : column.children

View File

@@ -4,10 +4,10 @@ import QtQuick.Layouts 1.12
import QtQuick.Controls.Material 2.12
import org.kde.kirigami 2.13 as Kirigami
import Spectral.Component 2.0
import Spectral.Dialog 2.0
import Spectral.Effect 2.0
import Spectral.Setting 0.1
import NeoChat.Component 2.0
import NeoChat.Dialog 2.0
import NeoChat.Effect 2.0
import NeoChat.Setting 0.1
RowLayout {
id: row

View File

@@ -7,14 +7,14 @@ import QtMultimedia 5.12
import Qt.labs.platform 1.0 as Platform
import org.kde.kirigami 2.13 as Kirigami
import Spectral 0.1
import Spectral.Setting 0.1
import org.kde.neochat 0.1
import NeoChat.Setting 0.1
import Spectral.Component 2.0
import Spectral.Dialog 2.0
import Spectral.Menu.Timeline 2.0
import Spectral.Effect 2.0
import Spectral.Font 0.1
import NeoChat.Component 2.0
import NeoChat.Dialog 2.0
import NeoChat.Menu.Timeline 2.0
import NeoChat.Effect 2.0
import NeoChat.Font 0.1
RowLayout {
readonly property bool avatarVisible: showAuthor && !sentByMe

View File

@@ -1,4 +1,4 @@
module Spectral.Component.Timeline
module NeoChat.Component.Timeline
TimelineContainer 2.0 TimelineContainer.qml
MessageDelegate 2.0 MessageDelegate.qml
TextDelegate 2.0 TextDelegate.qml

View File

@@ -1,4 +1,4 @@
module Spectral.Component
module NeoChat.Component
AutoMouseArea 2.0 AutoMouseArea.qml
MaterialIcon 2.0 MaterialIcon.qml
SideNavButton 2.0 SideNavButton.qml
@@ -7,3 +7,4 @@ AutoListView 2.0 AutoListView.qml
AutoTextField 2.0 AutoTextField.qml
FullScreenImage 2.0 FullScreenImage.qml
AutoRectangle 2.0 AutoRectangle.qml
ChatTextInput 2.0 ChatTextInput.qml

View File

@@ -3,11 +3,11 @@ import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import org.kde.kirigami 2.13 as Kirigami
import Spectral.Component 2.0
import Spectral.Effect 2.0
import NeoChat.Component 2.0
import NeoChat.Effect 2.0
import Spectral 0.1
import Spectral.Setting 0.1
import org.kde.neochat 0.1
import NeoChat.Setting 0.1
Dialog {
anchors.centerIn: parent

View File

@@ -2,9 +2,9 @@ import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import Spectral.Component 2.0
import NeoChat.Component 2.0
import Spectral 0.1
import org.kde.neochat 0.1
Dialog {
anchors.centerIn: parent

View File

@@ -2,8 +2,8 @@ import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import Spectral.Component 2.0
import Spectral.Setting 0.1
import NeoChat.Component 2.0
import NeoChat.Setting 0.1
Dialog {
anchors.centerIn: parent

View File

@@ -3,11 +3,11 @@ import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import org.kde.kirigami 2.13 as Kirigami
import Spectral.Component 2.0
import Spectral.Effect 2.0
import Spectral.Setting 0.1
import NeoChat.Component 2.0
import NeoChat.Effect 2.0
import NeoChat.Setting 0.1
import Spectral 0.1
import org.kde.neochat 0.1
Dialog {
property var room

View File

@@ -3,11 +3,11 @@ import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import org.kde.kirigami 2.13 as Kirigami
import Spectral.Component 2.0
import Spectral.Effect 2.0
import Spectral.Setting 0.1
import NeoChat.Component 2.0
import NeoChat.Effect 2.0
import NeoChat.Setting 0.1
import Spectral 0.1
import org.kde.neochat 0.1
Dialog {
property var connection

View File

@@ -3,11 +3,11 @@ import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import org.kde.kirigami 2.13 as Kirigami
import Spectral.Component 2.0
import Spectral.Effect 2.0
import Spectral.Setting 0.1
import NeoChat.Component 2.0
import NeoChat.Effect 2.0
import NeoChat.Setting 0.1
import Spectral 0.1
import org.kde.neochat 0.1
Dialog {
property var room

View File

@@ -2,11 +2,11 @@ import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import Spectral.Component 2.0
import Spectral.Effect 2.0
import Spectral.Setting 0.1
import NeoChat.Component 2.0
import NeoChat.Effect 2.0
import NeoChat.Setting 0.1
import Spectral 0.1
import org.kde.neochat 0.1
Dialog {
property var connection

View File

@@ -3,9 +3,9 @@ import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import org.kde.kirigami 2.13 as Kirigami
import Spectral.Component 2.0
import Spectral.Effect 2.0
import Spectral.Setting 0.1
import NeoChat.Component 2.0
import NeoChat.Effect 2.0
import NeoChat.Setting 0.1
Dialog {
property var room

View File

@@ -1,4 +1,4 @@
module Spectral.Dialog
module NeoChat.Dialog
RoomSettingsDialog 2.0 RoomSettingsDialog.qml
UserDetailDialog 2.0 UserDetailDialog.qml
MessageSourceDialog 2.0 MessageSourceDialog.qml

View File

@@ -2,8 +2,8 @@ import QtQuick 2.12
import QtQuick.Controls 2.12
import QtGraphicalEffects 1.0
import Spectral.Component 2.0
import Spectral.Setting 0.1
import NeoChat.Component 2.0
import NeoChat.Setting 0.1
AutoMouseArea {
id: ripple

View File

@@ -1,3 +1,3 @@
module Spectral.Effect
module NeoChat.Effect
ElevationEffect 2.0 ElevationEffect.qml
RippleEffect 2.0 RippleEffect.qml

View File

@@ -0,0 +1,47 @@
/**
* SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org>
* SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
*
* SPDX-LicenseIdentifier: GPL-3.0-only
*/
import QtQuick 2.12
import QtQuick.Controls 2.12
/**
* Context menu when clicking on a room in the room list
*/
Menu {
id: root
property var room
MenuItem {
text: i18n("Favourite")
checkable: true
checked: room.isFavourite
onTriggered: room.isFavourite ? room.removeTag("m.favourite") : room.addTag("m.favourite", 1.0)
}
MenuItem {
text: i18n("Deprioritize")
checkable: true
checked: room.isLowPriority
onTriggered: room.isLowPriority ? room.removeTag("m.lowpriority") : room.addTag("m.lowpriority", 1.0)
}
MenuSeparator {}
MenuItem {
text: i18n("Mark as Read")
onTriggered: room.markAllMessagesAsRead()
}
MenuItem {
text: i18n("Leave Room")
onTriggered: room.forget()
}
onClosed: destroy()
}

View File

@@ -1,7 +1,7 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
import Spectral.Dialog 2.0
import NeoChat.Dialog 2.0
Menu {
signal viewSource()

View File

@@ -9,7 +9,7 @@ import QtQuick.Controls 2.12 as QQC2
import QtQuick.Layouts 1.12
import org.kde.kirigami 2.13 as Kirigami
import Spectral.Dialog 2.0
import NeoChat.Dialog 2.0
Kirigami.OverlaySheet {
id: root

View File

@@ -1,3 +1,3 @@
module Spectral.Menu.Timeline
module NeoChat.Menu.Timeline
MessageDelegateContextMenu 2.0 MessageDelegateContextMenu.qml
FileDelegateContextMenu 2.0 FileDelegateContextMenu.qml

View File

@@ -1,2 +1,2 @@
module Spectral.Menu
module NeoChat.Menu
RoomListContextMenu 2.0 RoomListContextMenu.qml

View File

@@ -0,0 +1,134 @@
/**
* SPDX-FileCopyrightText: Tobias Fella <fella@posteo.de>
*
* SPDX-LicenseIdentifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
import QtQuick 2.14
import QtQuick.Controls 2.14 as Controls
import QtQuick.Layouts 1.14
import org.kde.kirigami 2.12 as Kirigami
import NeoChat 0.1
Kirigami.ScrollablePage {
title: i18n("Accounts")
ListView {
model: AccountListModel { }
delegate: Kirigami.SwipeListItem {
leftPadding: 0
rightPadding: 0
Kirigami.BasicListItem {
anchors.top: parent.top
anchors.bottom: parent.bottom
text: model.user.defaultName
subtitle: model.user.id
icon: model.connection.user.avatarMediaId ? "image://mxc/" + model.connection.user.avatarMediaId : "im-user"
onClicked: {
Controller.connection = model.connection
pageStack.layers.pop()
}
}
actions: [
Kirigami.Action {
text: i18n("Edit this account")
iconName: "document-edit"
onTriggered: {
userEditSheet.connection = model.connection
userEditSheet.open()
}
},
Kirigami.Action {
text: i18n("Logout")
iconName: "im-kick-user"
onTriggered: {
Controller.logout(model.connection)
if(Controller.accountCount === 1)
pageStack.layers.pop()
}
}
]
}
}
Connections {
target: Controller
function onConnectionAdded() {
if (pageStack.layers.depth > 2)
pageStack.layers.pop()
}
function onPasswordStatus(status) {
if(status == Controller.Success)
showPassiveNotification(i18n("Password changed successfully"))
else if(status == Controller.Wrong)
showPassiveNotification(i18n("Wrong password entered"))
else
showPassiveNotification(i18n("Unknown problem while trying to change password"))
}
}
actions.main: Kirigami.Action {
text: i18n("Add an account")
iconName: "list-add-user"
onTriggered: pageStack.layers.push("qrc:/qml/LoginPage.qml")
}
Kirigami.OverlaySheet {
id: userEditSheet
property var connection
header: Kirigami.Heading {
text: i18n("Edit Account")
}
Kirigami.FormLayout {
anchors.top: passwordsMessage.bottom
Controls.TextField {
id: name
text: userEditSheet.connection.localUser.defaultName
Kirigami.FormData.label: i18n("Name:")
}
Controls.TextField {
id: currentPassword
Kirigami.FormData.label: i18n("Current Password:")
echoMode: TextInput.Password
}
Controls.TextField {
id: newPassword
Kirigami.FormData.label: i18n("New Password:")
echoMode: TextInput.Password
}
Controls.TextField {
id: confirmPassword
Kirigami.FormData.label: i18n("Confirm new Password:")
echoMode: TextInput.Password
}
Controls.Button {
text: i18n("Save")
onClicked: {
if(userEditSheet.connection.localUser.defaultName !== name.text)
userEditSheet.connection.localUser.user.defaultName = name.text
if(currentPassword.text !== "" && newPassword.text !== "" && confirmPassword.text !== "") {
if(newPassword.text === confirmPassword.text) {
Controller.changePassword(userEditSheet.connection, currentPassword.text, newPassword.text)
} else {
showPassiveNotification(i18n("Passwords do not match"))
return
}
}
userEditSheet.close()
currentPassword.text = ""
newPassword.text = ""
confirmPassword.text = ""
}
}
}
}
}

View File

@@ -0,0 +1,10 @@
import org.kde.kirigami 2.12 as Kirigami
import QtQuick.Controls 2.12 as QQC2
Kirigami.Page {
title: i18n("Loading")
QQC2.BusyIndicator {
anchors.centerIn: parent
}
}

View File

@@ -0,0 +1,63 @@
/**
* SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org>
* SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
*
* SPDX-LicenseIdentifier: GPL-3.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 2.12 as QQC2
import QtQuick.Layouts 1.12
import NeoChat 0.1
import NeoChat.Component 2.0
import org.kde.kirigami 2.12 as Kirigami
Kirigami.ScrollablePage {
id: root
title: i18n("Login")
Kirigami.FormLayout {
id: formLayout
QQC2.TextField {
id: serverField
Kirigami.FormData.label: i18n("Server Address")
text: "https://matrix.org"
onAccepted: usernameField.forceActiveFocus()
}
QQC2.TextField {
id: usernameField
Kirigami.FormData.label: i18n("Username")
onAccepted: passwordField.forceActiveFocus()
}
Kirigami.PasswordField {
id: passwordField
Kirigami.FormData.label: i18n("Password")
onAccepted: accessTokenField.forceActiveFocus()
}
QQC2.TextField {
id: accessTokenField
Kirigami.FormData.label: i18n("Access Token (Optional)")
onAccepted: deviceNameField.forceActiveFocus()
}
QQC2.TextField {
id: deviceNameField
Kirigami.FormData.label: i18n("Device Name (Optional)")
onAccepted: doLogin()
}
QQC2.Button {
text: i18n("Login")
onClicked: doLogin()
}
}
function doLogin() {
if (accessTokenField.text.length > 0) {
Controller.loginWithAccessToken(serverField.text, usernameField.text, accessTokenField.text, deviceNameField.text)
} else {
Controller.loginWithCredentials(serverField.text, usernameField.text, passwordField.text, deviceNameField.text)
}
}
}

View File

@@ -0,0 +1,145 @@
/**
* SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org>
* SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
*
* SPDX-LicenseIdentifier: GPL-3.0-only
*/
import QtQuick 2.12
import QtQuick.Controls 2.12 as QQC2
import QtQuick.Layouts 1.12
import org.kde.kirigami 2.13 as Kirigami
import org.kde.kitemmodels 1.0
import org.kde.neochat 0.1
import NeoChat.Component 2.0
import NeoChat.Menu 2.0
Kirigami.ScrollablePage {
id: page
property var roomListModel
property var enteredRoom
property var searchText: ""
onSearchTextChanged: sortFilterRoomListModel.setFilterText(searchText)
signal enterRoom(var room)
signal leaveRoom(var room)
title: i18n("Rooms")
titleDelegate: Kirigami.SearchField {
Layout.topMargin: Kirigami.Units.smallSpacing
Layout.bottomMargin: Kirigami.Units.smallSpacing
Layout.fillHeight: true
Layout.fillWidth: true
onTextChanged: page.searchText = text
}
ListView {
model: SortFilterRoomListModel {
id: sortFilterRoomListModel
sourceModel: roomListModel
roomSortOrder: SortFilterRoomListModel.Categories
}
section.property: "category"
section.delegate: Kirigami.ListSectionHeader {
id: sectionHeader
action: Kirigami.Action {
onTriggered: roomListModel.setCategoryVisible(section, !roomListModel.categoryVisible(section))
}
contentItem: Item {
implicitHeight: categoryName.implicitHeight
Kirigami.Heading {
id: categoryName
level: 3
text: roomListModel.categoryName(section)
}
Kirigami.Icon {
source: roomListModel.categoryVisible(section) ? "go-up" : "go-down"
implicitHeight: Kirigami.Units.iconSizes.small
implicitWidth: Kirigami.Units.iconSizes.small
anchors.left: categoryName.right
anchors.leftMargin: Kirigami.Units.largeSpacing
anchors.verticalCenter: parent.verticalCenter
}
}
}
delegate: Kirigami.AbstractListItem {
visible: model.categoryVisible
topPadding: Kirigami.Units.largeSpacing
bottomPadding: Kirigami.Units.largeSpacing
contentItem: Item {
implicitHeight: roomLayout.implicitHeight
RowLayout {
id: roomLayout
spacing: Kirigami.Units.largeSpacing
anchors.fill: parent
Kirigami.Avatar {
Layout.preferredWidth: height
Layout.fillHeight: true
source: avatar ? "image://mxc/" + avatar : ""
name: model.name || i18n("No Name")
}
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter
spacing: Kirigami.Units.smallSpacing
QQC2.Label {
Layout.fillWidth: true
Layout.fillHeight: true
text: name ?? ""
font.pixelSize: 15
font.bold: unreadCount >= 0
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
QQC2.Label {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter
text: (lastEvent == "" ? topic : lastEvent).replace(/(\r\n\t|\n|\r\t)/gm," ")
font.pixelSize: 12
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
}
}
MouseArea {
acceptedButtons: Qt.LeftButton | Qt.RightButton
anchors.fill: parent
onClicked: {
console.log(mouse.button)
if (mouse.button == Qt.RightButton) {
roomListContextMenu.createObject(parent, {"room": currentRoom}).popup()
} else {
if (enteredRoom) {
leaveRoom(enteredRoom)
}
enteredRoom = currentRoom
enterRoom(enteredRoom)
}
}
}
}
}
Component {
id: roomListContextMenu
RoomListContextMenu {}
}
}
}

View File

@@ -0,0 +1,398 @@
import QtQuick 2.12
import QtQuick.Controls 2.12 as QQC2
import QtQuick.Layouts 1.12
import Qt.labs.qmlmodels 1.0
import QtQuick.Controls.Material 2.12
import org.kde.kirigami 2.13 as Kirigami
import org.kde.kitemmodels 1.0
import org.kde.neochat 0.1
import NeoChat.Component 2.0
import NeoChat.Component.Timeline 2.0
import NeoChat.Dialog 2.0
import NeoChat.Effect 2.0
import NeoChat.Menu.Timeline 2.0
Kirigami.ScrollablePage {
id: page
property var currentRoom
title: i18n("Messages")
MessageEventModel {
id: messageEventModel
room: currentRoom
}
QQC2.Popup {
anchors.centerIn: parent
id: attachDialog
padding: 16
contentItem: RowLayout {
QQC2.ToolButton {
Layout.preferredWidth: 160
Layout.fillHeight: true
icon.name: 'mail-attachment'
text: i18n("Choose local file")
onClicked: {
attachDialog.close()
var fileDialog = openFileDialog.createObject(ApplicationWindow.overlay)
fileDialog.chosen.connect(function(path) {
if (!path) return
chatTextInput.attach(path)
})
fileDialog.open()
}
}
Kirigami.Separator {}
QQC2.ToolButton {
Layout.preferredWidth: 160
Layout.fillHeight: true
padding: 16
icon.name: 'insert-image'
text: i18n("Clipboard image")
onClicked: {
var localPath = StandardPaths.writableLocation(StandardPaths.CacheLocation) + "/screenshots/" + (new Date()).getTime() + ".png"
if (!Clipboard.saveImage(localPath)) return
chatTextInput.attach(localPath)
attachDialog.close()
}
}
}
}
Component {
id: openFileDialog
OpenFileDialog {}
}
KSortFilterProxyModel {
id: sortedMessageEventModel
sourceModel: messageEventModel
filterRowCallback: function(row, parent) {
return messageEventModel.data(messageEventModel.index(row, 0), MessageEventModel.MessageRole) !== 0x10 && messageEventModel.data(messageEventModel.index(row, 0), MessageEventModel.EventTypeRole) !== "other"
}
}
ListView {
readonly property int largestVisibleIndex: count > 0 ? indexAt(contentX + (width / 2), contentY + height - 1) : -1
readonly property bool noNeedMoreContent: !currentRoom || currentRoom.eventsHistoryJob || currentRoom.allHistoryLoaded
readonly property bool isLoaded: page.width * page.height > 10
id: messageListView
spacing: Kirigami.Units.smallSpacing
clip: true
displayMarginBeginning: Kirigami.Units.gridUnit
displayMarginEnd: typingNotification.visible ? typingNotification.height : Kirigami.Units.gridUnit
verticalLayoutDirection: ListView.BottomToTop
highlightMoveDuration: 500
model: !isLoaded ? undefined : sortedMessageEventModel
onContentYChanged: {
if(!noNeedMoreContent && contentY - 5000 < originY)
currentRoom.getPreviousContent(20);
}
// populate: Transition {
// NumberAnimation {
// property: "opacity"; from: 0; to: 1
// duration: 200
// }
// }
// add: Transition {
// NumberAnimation {
// property: "opacity"; from: 0; to: 1
// duration: 200
// }
// }
// move: Transition {
// NumberAnimation {
// property: "y"; duration: 200
// }
// NumberAnimation {
// property: "opacity"; to: 1
// }
// }
// displaced: Transition {
// NumberAnimation {
// property: "y"; duration: 200
// easing.type: Easing.OutQuad
// }
// NumberAnimation {
// property: "opacity"; to: 1
// }
// }
delegate: DelegateChooser {
id: timelineDelegateChooser
role: "eventType"
DelegateChoice {
roleValue: "state"
delegate: TimelineContainer {
width: messageListView.width
innerObject: StateDelegate {
Layout.maximumWidth: parent.width
Layout.alignment: Qt.AlignHCenter
}
}
}
DelegateChoice {
roleValue: "emote"
delegate: TimelineContainer {
width: messageListView.width
innerObject: StateDelegate {
Layout.maximumWidth: parent.width
Layout.alignment: Qt.AlignHCenter
}
}
}
DelegateChoice {
roleValue: "message"
delegate: TimelineContainer {
width: messageListView.width
innerObject: MessageDelegate {
Layout.fillWidth: true
Layout.maximumWidth: messageListView.width
mouseArea: MouseArea {
acceptedButtons: Qt.RightButton
anchors.fill: parent
onClicked: openMessageContext(author, display, eventId, toolTip);
}
innerObject: [
TextDelegate {
Layout.fillWidth: true
},
ReactionDelegate {
Layout.fillWidth: true
Layout.topMargin: 0
Layout.bottomMargin: 8
}
]
}
}
}
DelegateChoice {
roleValue: "notice"
delegate: TimelineContainer {
width: messageListView.width
innerObject: MessageDelegate {
Layout.fillWidth: true
innerObject: TextDelegate {
Layout.fillWidth: true
}
}
}
}
DelegateChoice {
roleValue: "image"
delegate: TimelineContainer {
width: messageListView.width
innerObject: MessageDelegate {
Layout.fillWidth: true
innerObject: [
ImageDelegate {
Layout.maximumWidth: parent.width
Layout.preferredWidth: Math.min(320, info.w)
Layout.preferredHeight: Math.min(320, info.h)
},
ReactionDelegate {
Layout.fillWidth: true
Layout.topMargin: 0
Layout.bottomMargin: 8
}
]
}
}
}
DelegateChoice {
roleValue: "audio"
delegate: TimelineContainer {
width: messageListView.width
innerObject: MessageDelegate {
Layout.fillWidth: true
innerObject: AudioDelegate {
Layout.fillWidth: true
}
}
}
}
DelegateChoice {
roleValue: "video"
delegate: TimelineContainer {
width: messageListView.width
innerObject: MessageDelegate {
Layout.fillWidth: true
innerObject: AudioDelegate {
Layout.fillWidth: true
}
}
}
}
DelegateChoice {
roleValue: "file"
delegate: TimelineContainer {
width: messageListView.width
innerObject: MessageDelegate {
Layout.fillWidth: true
innerObject: FileDelegate {
Layout.fillWidth: true
}
}
}
}
DelegateChoice {
roleValue: "other"
delegate: Item {}
}
}
QQC2.Button {
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: 16
padding: 8
id: goReadMarkerFab
visible: currentRoom && currentRoom.hasUnreadMessages || !messageListView.atYEnd
action: Kirigami.Action {
onTriggered: {
if (currentRoom && currentRoom.hasUnreadMessages) {
goToEvent(currentRoom.readMarkerEventId)
} else {
currentRoom.markAllMessagesAsRead()
messageListView.positionViewAtBeginning()
}
}
icon.name: currentRoom && currentRoom.hasUnreadMessages ? "go-up" : "go-down"
}
}
QQC2.Control {
id: typingNotification
anchors.left: parent.left
anchors.bottom: parent.bottom
visible: currentRoom && currentRoom.usersTyping.length > 0
padding: 4
contentItem: RowLayout {
spacing: Kirigami.Units.largeSpacing
QQC2.BusyIndicator {
Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium
}
QQC2.Label {
text: i18ncp("Message displayed when some users are typing", "%2 is typing", "%2 are typing", currentRoom.usersTyping.length, currentRoom.usersTyping.join(", "))
}
}
}
Component.onCompleted: {
if (currentRoom) {
if (currentRoom.timelineSize < 20)
currentRoom.getPreviousContent(50)
}
positionViewAtBeginning()
}
}
footer: ChatTextInput {
id: chatTextInput
Layout.fillWidth: true
}
background: Item {}
function openMessageContext(author, message, eventId, toolTip, model) {
const contextMenu = messageDelegateContextMenu.createObject(root, {
'author': author,
'message': message,
'eventId': eventId,
});
contextMenu.viewSource.connect(function() {
messageSourceDialog.createObject(root, {
'sourceText': toolTip,
}).open();
contextMenu.close();
});
contextMenu.reply.connect(function(replyUser, replyContent) {
chatTextInput.replyUser = replyUser;
chatTextInput.replyEventID = eventId;
chatTextInput.replyContent = replyContent;
chatTextInput.isReply = true;
chatTextInput.focus();
contextMenu.close();
})
contextMenu.remove.connect(function() {
currentRoom.redactEvent(eventId);
contextMenu.close();
})
contextMenu.open()
}
Component {
id: messageDelegateContextMenu
MessageDelegateContextMenu {}
}
Component {
id: messageSourceDialog
MessageSourceDialog {}
}
}

View File

@@ -0,0 +1,5 @@
module NeoChat.Page
LoadingPage 2.0 LoadingPage.qml
LoginPage 2.0 LoginPage.qml
RoomListPage 2.0 RoomListPage.qml
RoomPage 2.0 RoomPage.qml

View File

@@ -7,9 +7,9 @@ import org.kde.kirigami 2.12 as Kirigami
import SortFilterProxyModel 0.2
import Spectral.Component 2.0
import Spectral.Component.Timeline 2.0
import Spectral 0.1
import NeoChat.Component 2.0
import NeoChat.Component.Timeline 2.0
import org.kde.neochat 0.1
Kirigami.GlobalDrawer {
id: root

View File

@@ -5,12 +5,12 @@ import QtQuick.Layouts 1.12
import org.kde.kirigami 2.13 as Kirigami
import Spectral.Component 2.0
import Spectral.Dialog 2.0
import Spectral.Effect 2.0
import Spectral.Setting 0.1
import NeoChat.Component 2.0
import NeoChat.Dialog 2.0
import NeoChat.Effect 2.0
import NeoChat.Setting 0.1
import Spectral 0.1
import org.kde.neochat 0.1
Kirigami.OverlayDrawer {
property var room

View File

@@ -3,10 +3,10 @@ import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls.Material 2.12
import Spectral 0.1
import Spectral.Effect 2.0
import Spectral.Component 2.0
import Spectral.Setting 0.1
import org.kde.neochat 0.1
import NeoChat.Effect 2.0
import NeoChat.Component 2.0
import NeoChat.Setting 0.1
Control {
signal clicked()

View File

@@ -5,8 +5,8 @@ import QtQuick.Layouts 1.12
import org.kde.kirigami 2.13 as Kirigami
import org.kde.kitemmodels 1.0
import Spectral.Component 2.0
import Spectral 0.1
import NeoChat.Component 2.0
import org.kde.neochat 0.1
Kirigami.ScrollablePage {
id: page

View File

@@ -1,5 +1,5 @@
module Spectral.Panel
module NeoChat.Panel
RoomPanel 2.0 RoomPanel.qml
RoomListPanel 2.0 RoomListPanel.qml
RoomDrawer 2.0 RoomDrawer.qml
SpectralSidebar 2.0 SpectralSidebar.qml
NeoChatSidebar 2.0 NeoChatSidebar.qml

View File

@@ -1,3 +1,3 @@
module Spectral.Setting
module NeoChat.Setting
singleton MSettings 0.1 Setting.qml
singleton MPalette 0.1 Palette.qml