aboutsummaryrefslogtreecommitdiff
path: root/qml
diff options
context:
space:
mode:
authorlink2xt <link2xt@testrun.org>2020-09-12 14:10:13 +0300
committerlink2xt <link2xt@testrun.org>2020-10-03 00:20:03 +0300
commitb8762ddb38dd975b0acb217b793594dfed83a824 (patch)
tree23ccefbba703fed6c07acce82ff72e32ba77c9ba /qml
downloadkdeltachat-b8762ddb38dd975b0acb217b793594dfed83a824.tar.gz
kdeltachat-b8762ddb38dd975b0acb217b793594dfed83a824.zip
Initial commit
Diffstat (limited to 'qml')
-rw-r--r--qml/AccountsPage.qml90
-rw-r--r--qml/ChatPage.qml156
-rw-r--r--qml/ChatlistPage.qml105
-rw-r--r--qml/ConfigurePage.qml31
-rw-r--r--qml/Message.qml83
-rw-r--r--qml/main.qml58
6 files changed, 523 insertions, 0 deletions
diff --git a/qml/AccountsPage.qml b/qml/AccountsPage.qml
new file mode 100644
index 0000000..8bda7a7
--- /dev/null
+++ b/qml/AccountsPage.qml
@@ -0,0 +1,90 @@
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Layouts 1.14
+import QtQuick.Dialogs 1.0
+import org.kde.kirigami 2.12 as Kirigami
+
+Kirigami.Page {
+ id: accountsPage
+
+ title: qsTr("Accounts")
+
+ mainAction: Kirigami.Action {
+ iconName: "list-add-user"
+ text: "Add account"
+ onTriggered: accountsModel.addAccount()
+ }
+
+ contextualActions: [
+ Kirigami.Action {
+ text: "Import account"
+ iconName: "document-import"
+ onTriggered: importAccountDialog.open()
+ }
+ ]
+
+ FileDialog {
+ id: importAccountDialog
+ title: "Import account"
+ onAccepted: {
+ var url = importAccountDialog.fileUrl.toString()
+ if (url.startsWith("file://")) {
+ var filename = url.substring(7);
+ console.log("Importing " + filename)
+ var accountId = accountsModel.importAccount (filename)
+ if (accountId == 0) {
+ console.log("Import failed")
+ } else {
+ console.log("Import succeeded")
+ }
+ }
+ }
+ }
+
+ ListView {
+ id: accountsListView
+ anchors.fill: parent
+ model: accountsModel
+
+ delegate: RowLayout {
+ width: accountsListView.width
+
+ Label {
+ Layout.fillWidth: true
+ text: model.number
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ Button {
+ width: 100
+ palette.button: "light green"
+ text: "Select"
+ onClicked: {
+ while (pageStack.depth > 1) {
+ pageStack.pop()
+ }
+ accountsModel.selectedAccount = model.number
+ let context = accountsModel.getSelectedAccount()
+ if (context.isConfigured()) {
+ pageStack.push("qrc:/qml/ChatlistPage.qml", {context: context})
+ } else {
+ pageStack.push("qrc:/qml/ConfigurePage.qml", {})
+ }
+ }
+ }
+
+ Button {
+ width: 100
+ palette.button: "red"
+ text: "Delete"
+ onClicked: accountsModel.removeAccount(model.number)
+ }
+ }
+ }
+
+ Menu {
+ id: contextMenu
+ MenuItem { text: "Import account" }
+ }
+}
diff --git a/qml/ChatPage.qml b/qml/ChatPage.qml
new file mode 100644
index 0000000..83f595c
--- /dev/null
+++ b/qml/ChatPage.qml
@@ -0,0 +1,156 @@
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Layouts 1.14
+import QtQml.Models 2.1
+import org.kde.kirigami 2.12 as Kirigami
+
+import DeltaChat 1.0
+
+Kirigami.Page {
+ id: chatPage
+
+ title: messageListView.chat ? messageListView.chat.name : qsTr("Chat")
+
+ property var chatId
+ readonly property DcChat chat: context.getChat(chatId)
+
+ function updateMessagelist() {
+ // Reverse message list, because it is laid out from bottom to top.
+ let messagelist = context.getMsgIdList(chatId).reverse()
+
+ for (let i = 0; i < messagelist.length; i++) {
+ const msgId = messagelist[i]
+
+ const item = {
+ msgId: msgId
+ }
+
+ let j;
+ for (j = i; j < messagelistModel.count; j++) {
+ if (messagelistModel.get(j).msgId == msgId) {
+ messagelistModel.move(j, i, 1)
+ messagelistModel.set(i, item)
+ break
+ }
+ }
+
+ if (j == messagelistModel.count) {
+ messagelistModel.insert(i, item)
+ }
+ }
+
+ if (messagelistModel.count > messagelist.length) {
+ messagelistModel.remove(messagelist.length,
+ messagelistModel.count - messagelist.length)
+ }
+ }
+
+ signal chatModified()
+ onChatModified: {
+ console.log("CHAT MODIFIED!")
+ }
+
+ signal incomingEvent()
+ onIncomingEvent: {
+ console.log("EVENT!")
+ }
+
+ ListModel {
+ id: messagelistModel
+ }
+
+ signal incomingMessage()
+ onIncomingMessage: {
+ console.log("Incoming message for chat " + chatId)
+
+ if (chatId == chatPage.chatId) {
+ updateMessagelist()
+ }
+ }
+
+ signal messagesChanged(var accountId, int chatId, int msgId)
+ onMessagesChanged: {
+ console.log("Messages changed for chat " + chatId)
+
+ if (chatId == chatPage.chatId) {
+ updateMessagelist()
+ }
+ }
+
+ Component.onCompleted: {
+ eventEmitter.onIncomingEvent.connect(incomingEvent)
+ eventEmitter.onChatModified.connect(chatModified)
+ eventEmitter.onIncomingMessage.connect(incomingMessage)
+ eventEmitter.onMessagesChanged.connect(messagesChanged)
+
+ updateMessagelist()
+ }
+
+ background: Rectangle {
+ color: Kirigami.Theme.alternateBackgroundColor
+ anchors.fill: parent
+ }
+
+ Component {
+ id: composePane
+
+ Pane {
+ Layout.fillWidth: true
+
+ RowLayout {
+ width: parent.width
+
+ TextField {
+ id: messageField
+
+ Layout.fillWidth: true
+ placeholderText: qsTr("Message")
+ wrapMode: TextArea.Wrap
+ selectByMouse: true
+ }
+
+ Button {
+ id: sendButton
+
+ icon.name: "document-send"
+ text: qsTr("Send")
+ enabled: messageField.length > 0
+ onClicked: {
+ context.sendTextMessage(chatId, messageField.text)
+ messageField.text = ""
+ }
+ }
+ }
+ }
+ }
+
+ ListView {
+ id: messageListView
+
+ anchors.fill: parent
+ spacing: 10
+
+ model: messagelistModel
+
+ /*
+ * Messages are laid out bottom to top, because their height
+ * is not known in advance.
+ *
+ * Attempts to lay out messages top to bottom and scroll to the
+ * bottom of the list with ListView.positionViewAtEnd() result in
+ * imprecise scrollbar position, because this method estimates
+ * item height from the height of currently visible messages.
+ */
+ verticalLayoutDirection: ListView.BottomToTop
+
+ delegate: Message {message: context.getMessage(msgId)}
+
+ ScrollBar.vertical: ScrollBar {}
+ }
+
+ footer: Loader {
+ sourceComponent: composePane
+ Layout.fillWidth: true
+ visible: chat && chat.canSend
+ }
+}
diff --git a/qml/ChatlistPage.qml b/qml/ChatlistPage.qml
new file mode 100644
index 0000000..3a5259a
--- /dev/null
+++ b/qml/ChatlistPage.qml
@@ -0,0 +1,105 @@
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Layouts 1.14
+import QtQml.Models 2.1
+import org.kde.kirigami 2.12 as Kirigami
+
+import DeltaChat 1.0
+
+Kirigami.Page {
+ title: qsTr("Chats")
+ id: chatlistPage
+
+ property DcContext context
+
+ signal messagesChanged
+ onMessagesChanged: {
+ // Reload chatlist
+ updateChatlist();
+ }
+
+ Component.onCompleted: {
+ eventEmitter.onMessagesChanged.connect(messagesChanged)
+ updateChatlist()
+ }
+
+ ListModel {
+ id: chatlistModel
+ }
+
+ function updateChatlist() {
+ let chatlist = chatlistPage.context.getChatlist()
+
+ // Merge new chatlist with existing one.
+ // To preserve selected item, we do not simply clear and fill
+ // the model from scratch.
+
+ for (let i = 0; i < chatlist.getChatCount(); i++) {
+ const summary = chatlist.getSummary(i)
+ const chatId = chatlist.getChatId(i)
+
+ const item = {
+ chatId: chatId,
+ msgId: chatlist.getMsgId(i),
+ username: summary.text1
+ }
+
+ let j;
+ for (j = i; j < chatlistModel.count; j++) {
+ if (chatlistModel.get(j).chatId == chatId) {
+ // This chat was already in the chatlist,
+ // move it to the new place and update.
+ chatlistModel.move(j, i, 1)
+ chatlistModel.set(i, item)
+ break
+ }
+ }
+
+ // This chat is new, insert it.
+ if (j == chatlistModel.count) {
+ chatlistModel.insert(i, item)
+ }
+ }
+
+ // Remove any chats that are not present in the new chatlist.
+ if (chatlistModel.count > chatlist.getChatCount()) {
+ chatlistModel.remove(chatlist.getChatCount(),
+ chatlistModel.count - chatlist.getChatCount())
+ }
+ }
+
+ ListView {
+ id: chatlist
+
+ anchors.fill: parent
+ model: chatlistModel
+
+ onCurrentItemChanged: {
+ var chatId = chatlistModel.get(currentIndex).chatId
+
+ console.log("Current index is " + currentIndex)
+ console.log("Selected chat " + chatId)
+
+ console.log("Depth is " + pageStack.depth)
+ let chatPageComponent = Qt.createComponent("qrc:/qml/ChatPage.qml")
+ if (chatPageComponent.status == Component.Ready) {
+ let myPage = chatPageComponent.createObject(chatlistPage, {chatId: chatId})
+ if (pageStack.depth == 2) {
+ pageStack.push(myPage)
+ } else if (pageStack.depth == 3) {
+ pageStack.currentIndex = 2
+ pageStack.replace(myPage)
+ }
+ }
+ }
+
+ delegate: Kirigami.BasicListItem {
+ width: chatlist.width
+
+ label: chatlistPage.context.getChat(model.chatId).getName()
+ subtitle: model.username
+ }
+
+ ScrollBar.vertical: ScrollBar {}
+ }
+}
diff --git a/qml/ConfigurePage.qml b/qml/ConfigurePage.qml
new file mode 100644
index 0000000..ace41c8
--- /dev/null
+++ b/qml/ConfigurePage.qml
@@ -0,0 +1,31 @@
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Layouts 1.14
+import QtQml.Models 2.1
+import org.kde.kirigami 2.12 as Kirigami
+
+Kirigami.Page {
+ title: qsTr("Configure account")
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ TextField {
+ id: addressField
+
+ placeholderText: "Address"
+ }
+ TextField {
+ id: passwordField
+
+ placeholderText: "Password"
+ echoMode: TextInput.PasswordEchoOnEdit
+ }
+ Button {
+ text: "Login"
+ onClicked: {
+ console.log("Login")
+ }
+ }
+ }
+}
diff --git a/qml/Message.qml b/qml/Message.qml
new file mode 100644
index 0000000..b78cd62
--- /dev/null
+++ b/qml/Message.qml
@@ -0,0 +1,83 @@
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Layouts 1.14
+import QtQml.Models 2.1
+import org.kde.kirigami 2.12 as Kirigami
+
+import DeltaChat 1.0
+
+RowLayout {
+ id: messageObject
+
+ property DcMessage message
+ readonly property DcContact from: context.getContact(message.fromId)
+
+ width: ListView.view.width
+ layoutDirection: message.fromId == 1 ? Qt.RightToLeft : Qt.LeftToRight
+
+ Rectangle {
+ Layout.preferredWidth: messageContents.width
+ Layout.preferredHeight: messageContents.height
+
+ Component {
+ id: imageMessageView
+
+ ColumnLayout {
+ Image {
+ source: "file:" + messageObject.message.file
+ sourceSize.width: messageObject.message.width
+ sourceSize.height: messageObject.message.height
+ fillMode: Image.PreserveAspectFit
+ Layout.maximumWidth: messageObject.width
+ }
+ Label {
+ font.bold: true
+ color: messageObject.message.fromId > 0 ? messageObject.from.color : "black"
+ text: messageObject.message.fromId > 0 ? messageObject.from.displayName : ""
+ textFormat: Text.PlainText
+ }
+ }
+ }
+
+ Component {
+ id: textMessageView
+
+ Label {
+ font.bold: true
+ color: messageObject.message.fromId > 0 ? messageObject.from.color : "black"
+ text: messageObject.message.fromId > 0 ? messageObject.from.displayName : ""
+ textFormat: Text.PlainText
+ }
+ }
+
+ color: Kirigami.Theme.backgroundColor
+ radius: 5
+
+ ColumnLayout {
+ id: messageContents
+
+ anchors.centerIn: parent
+
+ Loader {
+ sourceComponent: messageObject.message.viewtype == 20 ? imageMessageView : textMessageView
+ }
+ TextEdit {
+ Layout.maximumWidth: messageObject.width > 30 ? messageObject.width - 30 : messageObject.width
+ text: messageObject.message.text
+ textFormat: Text.PlainText
+ selectByMouse: true
+ readOnly: true
+ color: "black"
+ wrapMode: Text.Wrap
+ font.pixelSize: 14
+ }
+ Label {
+ Layout.fillWidth: true
+ text: messageObject.message.state == 26 ? "✓"
+ : messageObject.message.state == 28 ? "✓✓"
+ : messageObject.message.state == 24 ? "✗"
+ : "";
+ }
+ }
+ }
+}
diff --git a/qml/main.qml b/qml/main.qml
new file mode 100644
index 0000000..8131133
--- /dev/null
+++ b/qml/main.qml
@@ -0,0 +1,58 @@
+import QtQuick 2.14
+import QtQuick.Controls 2.14 as Controls
+import org.kde.kirigami 2.12 as Kirigami
+
+import DeltaChat 1.0
+
+
+Kirigami.ApplicationWindow {
+ id: root
+
+ property DcAccountsEventEmitter eventEmitter
+
+ title: qsTr("Delta Chat")
+
+ pageStack.initialPage: AccountsPage {}
+
+ globalDrawer: Kirigami.GlobalDrawer {
+ header: Controls.Switch {
+ text: "Start IO"
+ onCheckedChanged: {
+ if (checked) {
+ accountsModel.startIo()
+ } else {
+ accountsModel.stopIo()
+ }
+ }
+ }
+
+ actions: [
+ Kirigami.Action {
+ text: "Maybe network"
+ iconName: "view-refresh"
+ onTriggered: accountsModel.maybeNetwork()
+ }
+ ]
+ }
+
+ contextDrawer: Kirigami.ContextDrawer {
+ id: contextDrawer
+ }
+
+ AccountsModel {
+ id: accountsModel
+ }
+
+ Component.onCompleted: {
+ console.log('starting')
+ eventEmitter = accountsModel.getEventEmitter()
+ eventEmitter.start();
+ }
+
+ onClosing: {
+ console.log('stopping')
+ pageStack.pop(null)
+ delete root.accountsModel
+ eventEmitter.stop()
+ }
+}