diff options
-rw-r--r-- | qml/AccountsPage.qml | 116 | ||||
-rw-r--r-- | qml/ChatPage.qml | 94 | ||||
-rw-r--r-- | qml/ChatlistItem.qml | 31 | ||||
-rw-r--r-- | qml/ChatlistPage.qml | 218 | ||||
-rw-r--r-- | qml/ComposePane.qml | 47 | ||||
-rw-r--r-- | qml/ConfigurePage.qml | 153 | ||||
-rw-r--r-- | qml/HtmlViewSheet.qml | 13 | ||||
-rw-r--r-- | qml/Message.qml | 124 | ||||
-rw-r--r-- | qml/NewChatPage.qml | 96 | ||||
-rw-r--r-- | qml/SettingsPage.qml | 34 | ||||
-rw-r--r-- | qml/main.qml | 109 |
11 files changed, 564 insertions, 471 deletions
diff --git a/qml/AccountsPage.qml b/qml/AccountsPage.qml index 204e4f8..1d91a5b 100644 --- a/qml/AccountsPage.qml +++ b/qml/AccountsPage.qml @@ -4,81 +4,61 @@ import QtQuick.Layouts 1.12 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: { - let accountId = dcAccounts.addAccount() - let context = dcAccounts.getAccount(accountId); - - let title; - if (context.isConfigured()) { - title = context.getConfig("addr"); - } else { - title = `Unconfigured ${accountId}` - } - - accountsModel.insert(accountsModel.count, { - number: accountId, - title: title - }) - } - } - - ListModel { - id: accountsModel - } + id: root function updateAccounts() { - let accountsList = dcAccounts.getAll() - - accountsModel.clear() + let accountsList = dcAccounts.getAll(); + accountsModel.clear(); for (let i = 0; i < accountsList.length; i++) { let accountId = accountsList[i]; let title; let context = dcAccounts.getAccount(accountId); - if (context.isConfigured()) { + if (context.isConfigured()) title = context.getConfig("addr"); - } else { - title = `Unconfigured ${accountId}` - } - + else + title = `Unconfigured ${accountId}`; accountsModel.insert(i, { - number: accountId, - title: title - }) + "number": accountId, + "title": title + }); } } + title: qsTr("Accounts") Component.onCompleted: { - updateAccounts() + updateAccounts(); + } + + ListModel { + id: accountsModel } ListView { id: accountsListView + anchors.fill: parent model: accountsModel currentIndex: -1 delegate: Kirigami.AbstractListItem { width: accountsListView.width - onClicked: { - while (pageStack.depth > 1) { - pageStack.pop() - } - dcAccounts.selectAccount(model.number) - let context = dcAccounts.getSelectedAccount() - if (context.isConfigured()) { - pageStack.replace("qrc:/qml/ChatlistPage.qml", {context: context, eventEmitter: eventEmitter}) - } else { - pageStack.replace("qrc:/qml/ConfigurePage.qml", {context: context, eventEmitter: eventEmitter}) - } - pageStack.layers.pop() + while (pageStack.depth > 1) + pageStack.pop(); + + dcAccounts.selectAccount(model.number); + let context = dcAccounts.getSelectedAccount(); + if (context.isConfigured()) + pageStack.replace("qrc:/qml/ChatlistPage.qml", { + "context": context, + "eventEmitter": eventEmitter + }); + else + pageStack.replace("qrc:/qml/ConfigurePage.qml", { + "context": context, + "eventEmitter": eventEmitter + }); + pageStack.layers.pop(); } RowLayout { @@ -93,16 +73,42 @@ Kirigami.Page { icon.name: "delete" text: "Delete" onClicked: { - dcAccounts.removeAccount(model.number) - accountsModel.remove(model.index) + dcAccounts.removeAccount(model.number); + accountsModel.remove(model.index); } } + } + } + } Menu { id: contextMenu - MenuItem { text: "Import account" } + + MenuItem { + text: "Import account" + } + } + + mainAction: Kirigami.Action { + iconName: "list-add-user" + text: "Add account" + onTriggered: { + let accountId = dcAccounts.addAccount(); + let context = dcAccounts.getAccount(accountId); + let title; + if (context.isConfigured()) + title = context.getConfig("addr"); + else + title = `Unconfigured ${accountId}`; + accountsModel.insert(accountsModel.count, { + "number": accountId, + "title": title + }); + } + } + } diff --git a/qml/ChatPage.qml b/qml/ChatPage.qml index a0541cc..63e3dae 100644 --- a/qml/ChatPage.qml +++ b/qml/ChatPage.qml @@ -1,51 +1,46 @@ +import DeltaChat 1.0 +import QtQml.Models 2.1 import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 -import QtQml.Models 2.1 import org.kde.kirigami 2.12 as Kirigami -import DeltaChat 1.0 - Kirigami.ScrollablePage { - id: chatPage - - title: chat ? chat.name : qsTr("Chat") + id: root required property DcContext context required property DcAccountsEventEmitter eventEmitter - required property var chatId 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() - + let messagelist = context.getMsgIdList(chatId).reverse(); for (let i = 0; i < messagelist.length; i++) { - const msgId = messagelist[i] - + const msgId = messagelist[i]; const item = { - msgId: msgId - } - + "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 + messagelistModel.move(j, i, 1); + messagelistModel.set(i, item); + break; } } + if (j == messagelistModel.count) + messagelistModel.insert(i, item); - if (j == messagelistModel.count) { - messagelistModel.insert(i, item) - } } + if (messagelistModel.count > messagelist.length) + messagelistModel.remove(messagelist.length, messagelistModel.count - messagelist.length); - if (messagelistModel.count > messagelist.length) { - messagelistModel.remove(messagelist.length, - messagelistModel.count - messagelist.length) - } + } + + title: chat ? chat.name : qsTr("Chat") + Component.onCompleted: { + root.updateMessagelist(); } ListModel { @@ -53,35 +48,26 @@ Kirigami.ScrollablePage { } Connections { - target: chatPage.eventEmitter - function onChatModified() { - console.log("CHAT MODIFIED!") - chatPage.chat = context.getChat(chatId) + console.log("CHAT MODIFIED!"); + root.chat = context.getChat(chatId); } + function onIncomingMessage(accountId, chatId, msgId) { - console.log("Incoming message for chat " + chatId) + console.log("Incoming message for chat " + chatId); + if (chatId == root.chatId) + root.updateMessagelist(); - if (chatId == chatPage.chatId) { - chatPage.updateMessagelist() - } } + function onMessagesChanged(accountId, chatId, msgId) { - console.log("Messages changed for chat " + chatId) + console.log("Messages changed for chat " + chatId); + if (chatId == root.chatId || chatId == 0) + root.updateMessagelist(); - if (chatId == chatPage.chatId || chatId == 0) { - chatPage.updateMessagelist() - } } - } - - Component.onCompleted: { - chatPage.updateMessagelist() - } - background: Rectangle { - color: Kirigami.Theme.alternateBackgroundColor - anchors.fill: parent + target: root.eventEmitter } ListView { @@ -89,9 +75,7 @@ Kirigami.ScrollablePage { anchors.fill: parent spacing: Kirigami.Units.largeSpacing - model: messagelistModel - /* * Messages are laid out bottom to top, because their height * is not known in advance. @@ -104,17 +88,23 @@ Kirigami.ScrollablePage { verticalLayoutDirection: ListView.BottomToTop delegate: Message { - message: chatPage.context.getMessage(msgId) - context: chatPage.context + message: root.context.getMessage(msgId) + context: root.context width: ListView.view.width } + } - footer: ComposePane { - context: chatPage.context - chatId: chatPage.chatId - chat: chatPage.chat + background: Rectangle { + color: Kirigami.Theme.alternateBackgroundColor + anchors.fill: parent + } + footer: ComposePane { + context: root.context + chatId: root.chatId + chat: root.chat Layout.fillWidth: true } + } diff --git a/qml/ChatlistItem.qml b/qml/ChatlistItem.qml index 284f9d2..d087f4b 100644 --- a/qml/ChatlistItem.qml +++ b/qml/ChatlistItem.qml @@ -1,11 +1,10 @@ +import DeltaChat 1.0 +import QtQml.Models 2.1 import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 -import QtQml.Models 2.1 import org.kde.kirigami 2.13 as Kirigami -import DeltaChat 1.0 - Kirigami.AbstractListItem { id: root @@ -23,12 +22,14 @@ Kirigami.AbstractListItem { source: root.avatarSource name: root.chatName color: root.context.getChat(root.chatId).getColor() + MouseArea { anchors.fill: parent acceptedButtons: Qt.RightButton onClicked: { if (mouse.button === Qt.RightButton) - contextMenu.popup() + contextMenu.popup(); + } Menu { @@ -39,25 +40,32 @@ Kirigami.AbstractListItem { text: "Pin chat" onTriggered: root.context.setChatVisibility(root.chatId, 2) } + Action { text: "Unpin chat" onTriggered: root.context.setChatVisibility(root.chatId, 0) } + Action { text: "Archive chat" onTriggered: root.context.setChatVisibility(root.chatId, 1) } + Action { text: "Unarchive chat" onTriggered: root.context.setChatVisibility(root.chatId, 0) } + Action { icon.name: "delete" text: "Delete chat" onTriggered: root.context.deleteChat(root.chatId) } + } + } + } ColumnLayout { @@ -68,28 +76,29 @@ Kirigami.AbstractListItem { font.weight: Font.Bold Layout.fillWidth: true } + Label { text: root.username font: Kirigami.Theme.smallFont Layout.fillWidth: true } + } Label { text: root.isContactRequest ? "NEW" : root.freshMsgCnt visible: root.freshMsgCnt > 0 || root.isContactRequest - // Align label in the center of a badge. verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter - // Make sure badge is not too narrow. Layout.minimumWidth: height background: Rectangle { - color: Kirigami.Theme.alternateBackgroundColor - radius: 0.25 * height + color: Kirigami.Theme.alternateBackgroundColor + radius: 0.25 * height } + } // "Pinned" badge @@ -99,11 +108,15 @@ Kirigami.AbstractListItem { width: Kirigami.Units.gridUnit height: Kirigami.Units.gridUnit radius: 0.25 * height - Kirigami.Icon { + + Kirigami.Icon { source: "pin" height: Kirigami.Units.gridUnit width: Kirigami.Units.gridUnit } + } + } + } diff --git a/qml/ChatlistPage.qml b/qml/ChatlistPage.qml index 59d178d..2ee7e83 100644 --- a/qml/ChatlistPage.qml +++ b/qml/ChatlistPage.qml @@ -1,160 +1,135 @@ +import DeltaChat 1.0 +import QtQml.Models 2.1 import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 -import QtQml.Models 2.1 import org.kde.kirigami 2.13 as Kirigami -import DeltaChat 1.0 - Kirigami.ScrollablePage { - title: qsTr("Chats") id: chatlistPage required property DcContext context required property DcAccountsEventEmitter eventEmitter - property bool archivedOnly: false - Connections { - target: chatlistPage.eventEmitter - - function onMessagesChanged() { - // Reload chatlist - updateChatlist(); - } - function onMessagesNoticed() { - // Reload chatlist - updateChatlist(); - } - function onChatModified() { - // Reload chatlist - updateChatlist(); - } - } - - Component.onCompleted: { - updateChatlist() - } - - header: Kirigami.SearchField { - id: searchField - - onTextChanged: chatlistPage.updateChatlist() - } - - mainAction: Kirigami.Action { - text: "New chat" - iconName: "list-add" - onTriggered: { - let newChatPageComponent = Qt.createComponent("qrc:/qml/NewChatPage.qml") - if (newChatPageComponent.status == Component.Ready) { - let newChatPage = newChatPageComponent.createObject(pageStack, {context: chatlistPage.context}) - pageStack.layers.push(newChatPage) - } else if (newChatPageComponent.status == Component.Error) { - console.log("Error loading new chat page: " + newChatPageComponent.errorString()) - } - } - } - - contextualActions: [ - Kirigami.Action { - text: "Settings" - iconName: "configure" - onTriggered: { - let settingsPageComponent = Qt.createComponent("qrc:/qml/SettingsPage.qml") - if (settingsPageComponent.status == Component.Ready) { - let settingsPage = settingsPageComponent.createObject(pageStack, {context: chatlistPage.context}) - pageStack.layers.push(settingsPage) - } else { - console.log("Can't open Settings page") - } - } - } - ] - - ListModel { - id: chatlistModel - } - function chatClicked(chatId) { if (chatId > 9) { // chatId > DC_CHAT_ID_LAST_SPECIAL - loadChat(chatId) - + loadChat(chatId); } else if (chatId == 6) { - chatlistPage.archivedOnly = true - chatlist.currentIndex = -1 + chatlistPage.archivedOnly = true; + chatlist.currentIndex = -1; updateChatlist(); } } function loadChat(chatId) { - chatlistPage.context.marknoticedChat(chatId) - - console.log("Selected chat " + chatId) - - console.log("Depth is " + pageStack.depth) - let chatPageComponent = Qt.createComponent("qrc:/qml/ChatPage.qml") + chatlistPage.context.marknoticedChat(chatId); + 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(pageStack, {chatId: chatId, context: chatlistPage.context, eventEmitter: chatlistPage.eventEmitter}) + let myPage = chatPageComponent.createObject(pageStack, { + "chatId": chatId, + "context": chatlistPage.context, + "eventEmitter": chatlistPage.eventEmitter + }); if (pageStack.depth == 1) { - pageStack.push(myPage) + pageStack.push(myPage); } else if (pageStack.depth == 2) { - pageStack.currentIndex = 1 - pageStack.replace(myPage) + pageStack.currentIndex = 1; + pageStack.replace(myPage); } } else if (chatPageComponent.status == Component.Error) { - console.log("Error loading chat page: " + chatPageComponent.errorString()) + console.log("Error loading chat page: " + chatPageComponent.errorString()); } } function updateChatlist() { - let chatlist = chatlistPage.context.getChatlist(chatlistPage.archivedOnly ? 1 : 0, - searchField.text) - // Merge new chatlist with existing one. // To preserve selected item, we do not simply clear and fill // the model from scratch. + let chatlist = chatlistPage.context.getChatlist(chatlistPage.archivedOnly ? 1 : 0, searchField.text); for (let i = 0; i < chatlist.getChatCount(); i++) { - const summary = chatlist.getSummary(i) - const chatId = chatlist.getChatId(i) - const chat = chatlistPage.context.getChat(chatId) - const profileImage = chat.getProfileImage() - + const summary = chatlist.getSummary(i); + const chatId = chatlist.getChatId(i); + const chat = chatlistPage.context.getChat(chatId); + const profileImage = chat.getProfileImage(); const item = { - chatId: chatId, - msgId: chatlist.getMsgId(i), - username: (summary.text1 != "" ? summary.text1 + ": " : "") + summary.text2, - avatarSource: profileImage ? "file:" + profileImage : "", - chatName: chat.name, - freshMsgCnt: chatlistPage.context.getFreshMsgCnt(chatId), - isContactRequest: chat.isContactRequest, - visibility: chat.visibility - } - + "chatId": chatId, + "msgId": chatlist.getMsgId(i), + "username": (summary.text1 != "" ? summary.text1 + ": " : "") + summary.text2, + "avatarSource": profileImage ? "file:" + profileImage : "", + "chatName": chat.name, + "freshMsgCnt": chatlistPage.context.getFreshMsgCnt(chatId), + "isContactRequest": chat.isContactRequest, + "visibility": chat.visibility + }; 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 + chatlistModel.move(j, i, 1); + chatlistModel.set(i, item); + break; } } - // This chat is new, insert it. - if (j == chatlistModel.count) { - chatlistModel.insert(i, item) + 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()); + + } + + title: qsTr("Chats") + Component.onCompleted: { + updateChatlist(); + } + contextualActions: [ + Kirigami.Action { + text: "Settings" + iconName: "configure" + onTriggered: { + let settingsPageComponent = Qt.createComponent("qrc:/qml/SettingsPage.qml"); + if (settingsPageComponent.status == Component.Ready) { + let settingsPage = settingsPageComponent.createObject(pageStack, { + "context": chatlistPage.context + }); + pageStack.layers.push(settingsPage); + } else { + console.log("Can't open Settings page"); + } } } + ] - // Remove any chats that are not present in the new chatlist. - if (chatlistModel.count > chatlist.getChatCount()) { - chatlistModel.remove(chatlist.getChatCount(), - chatlistModel.count - chatlist.getChatCount()) + Connections { + function onMessagesChanged() { + // Reload chatlist + updateChatlist(); + } + + function onMessagesNoticed() { + // Reload chatlist + updateChatlist(); + } + + function onChatModified() { + // Reload chatlist + updateChatlist(); } + + target: chatlistPage.eventEmitter + } + + ListModel { + id: chatlistModel } ListView { @@ -172,9 +147,32 @@ Kirigami.ScrollablePage { freshMsgCnt: model.freshMsgCnt isContactRequest: model.isContactRequest isPinned: model.visibility == 2 - width: chatlist.width onClicked: chatClicked(model.chatId) } + } + + header: Kirigami.SearchField { + id: searchField + + onTextChanged: chatlistPage.updateChatlist() + } + + mainAction: Kirigami.Action { + text: "New chat" + iconName: "list-add" + onTriggered: { + let newChatPageComponent = Qt.createComponent("qrc:/qml/NewChatPage.qml"); + if (newChatPageComponent.status == Component.Ready) { + let newChatPage = newChatPageComponent.createObject(pageStack, { + "context": chatlistPage.context + }); + pageStack.layers.push(newChatPage); + } else if (newChatPageComponent.status == Component.Error) { + console.log("Error loading new chat page: " + newChatPageComponent.errorString()); + } + } + } + } diff --git a/qml/ComposePane.qml b/qml/ComposePane.qml index 5cd0603..2f6145c 100644 --- a/qml/ComposePane.qml +++ b/qml/ComposePane.qml @@ -1,26 +1,22 @@ +import DeltaChat 1.0 import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 -import DeltaChat 1.0 - Pane { id: root required property DcContext context required property var chatId required property var chat - property bool canSend: root.chat && root.chat.canSend property bool isContactRequest: root.chat && root.chat.isContactRequest - function createMessage() - { + function createMessage() { let DC_MSG_TEXT = 10; - - var msg = root.context.newMessage(DC_MSG_TEXT) - msg.setText(messageField.text) - return msg + var msg = root.context.newMessage(DC_MSG_TEXT); + msg.setText(messageField.text); + return msg; } padding: 0 @@ -30,50 +26,48 @@ Pane { TextArea { id: messageField - visible: root.canSend + visible: root.canSend Layout.fillWidth: true placeholderText: qsTr("Message") wrapMode: TextArea.Wrap selectByMouse: true - Component.onCompleted: { - let draft = root.context.getDraft(chatId) - if (draft) { - messageField.text = draft.text - } + let draft = root.context.getDraft(chatId); + if (draft) + messageField.text = draft.text; + } Connections { function onEditingFinished() { - let msg = root.createMessage() - root.context.setDraft(chatId, msg) + let msg = root.createMessage(); + root.context.setDraft(chatId, msg); } + } + } Button { id: sendButton - visible: root.canSend + visible: root.canSend Layout.alignment: Qt.AlignBottom - icon.name: "document-send" text: qsTr("Send") enabled: messageField.length > 0 onClicked: { - let msg = root.createMessage() - root.context.sendMessage(root.chatId, msg) - - messageField.text = "" - root.context.setDraft(chatId, null) + let msg = root.createMessage(); + root.context.sendMessage(root.chatId, msg); + messageField.text = ""; + root.context.setDraft(chatId, null); } } Button { Layout.alignment: Qt.AlignBottom Layout.fillWidth: true - text: "Accept" onClicked: root.context.acceptChat(root.chatId) visible: root.isContactRequest @@ -83,11 +77,12 @@ Pane { Button { Layout.alignment: Qt.AlignBottom Layout.fillWidth: true - text: "Block" onClicked: root.context.acceptChat(root.chatId) visible: root.isContactRequest icon.name: "call-stop" } + } + } diff --git a/qml/ConfigurePage.qml b/qml/ConfigurePage.qml index 1cddb3f..e15807d 100644 --- a/qml/ConfigurePage.qml +++ b/qml/ConfigurePage.qml @@ -1,19 +1,18 @@ +import DeltaChat 1.0 +import QtQml.Models 2.1 import QtQuick 2.12 import QtQuick.Controls 2.12 -import QtQuick.Layouts 1.12 -import QtQml.Models 2.1 import QtQuick.Dialogs 1.3 +import QtQuick.Layouts 1.12 import org.kde.kirigami 2.12 as Kirigami -import DeltaChat 1.0 - Kirigami.ScrollablePage { - title: qsTr("Configure account") id: root required property DcContext context required property DcAccountsEventEmitter eventEmitter + title: qsTr("Configure account") contextualActions: [ Kirigami.Action { text: "Import backup" @@ -28,11 +27,11 @@ Kirigami.ScrollablePage { Kirigami.FormData.label: "Address: " } + TextField { id: passwordField Kirigami.FormData.label: "Password: " - echoMode: TextInput.PasswordEchoOnEdit } @@ -40,16 +39,19 @@ Kirigami.ScrollablePage { Kirigami.FormData.isSection: true Kirigami.FormData.label: "Advanced settings" } + TextField { id: imapLoginField Kirigami.FormData.label: "IMAP login: " } + TextField { id: imapHostnameField Kirigami.FormData.label: "IMAP server: " } + TextField { id: imapPortField @@ -58,17 +60,33 @@ Kirigami.ScrollablePage { ListModel { id: securityModel - ListElement { text: "Automatic"; value: 0 } - ListElement { text: "SSL/TLS"; value: 1 } - ListElement { text: "StartTLS"; value: 2 } - ListElement { text: "Off"; value: 3 } + + ListElement { + text: "Automatic" + value: 0 + } + + ListElement { + text: "SSL/TLS" + value: 1 + } + + ListElement { + text: "StartTLS" + value: 2 + } + + ListElement { + text: "Off" + value: 3 + } + } ComboBox { id: imapSecurity Kirigami.FormData.label: "IMAP security: " - model: securityModel textRole: "text" } @@ -78,28 +96,30 @@ Kirigami.ScrollablePage { Kirigami.FormData.label: "SMTP login: " } + TextField { id: smtpPasswordField Kirigami.FormData.label: "SMTP password: " - echoMode: TextInput.PasswordEchoOnEdit } + TextField { id: smtpHostnameField Kirigami.FormData.label: "SMTP server: " } + TextField { id: smtpPortField Kirigami.FormData.label: "SMTP port: " } + ComboBox { id: smtpSecurity Kirigami.FormData.label: "SMTP security: " - model: securityModel textRole: "text" } @@ -108,99 +128,128 @@ Kirigami.ScrollablePage { id: certificateChecks Kirigami.FormData.label: "Certificate checks: " + textRole: "text" model: ListModel { id: certificateChecksModel - ListElement { text: "Automatic"; value: 0 } - ListElement { text: "Strict"; value: 1 } - ListElement { text: "Accept invalid certificates"; value: 2 } + + ListElement { + text: "Automatic" + value: 0 + } + + ListElement { + text: "Strict" + value: 1 + } + + ListElement { + text: "Accept invalid certificates" + value: 2 + } + } - textRole: "text" + } Switch { id: socks5Enabled + text: "SOCKS5 enabled" } + TextField { id: socks5Host + Kirigami.FormData.label: "SOCKS5 host: " } + TextField { id: socks5Port + Kirigami.FormData.label: "SOCKS5 port: " } + TextField { id: socks5Username + Kirigami.FormData.label: "SOCKS5 username: " } + TextField { id: socks5Password + Kirigami.FormData.label: "Password: " echoMode: TextInput.PasswordEchoOnEdit } ProgressBar { id: progressBar - value: 0.0 + + value: 0 } + Button { text: "Login" onClicked: { - console.log("Login") - root.context.stopIo() - root.context.setConfig("addr", addressField.text) - root.context.setConfig("mail_pw", passwordField.text) - root.context.setConfig("mail_user", imapLoginField.text) - root.context.setConfig("mail_server", imapHostnameField.text) - root.context.setConfig("mail_port", imapPortField.text) - root.context.setConfig("mail_security", securityModel.get(imapSecurity.currentIndex).value) - root.context.setConfig("send_user", smtpLoginField.text) - root.context.setConfig("send_pw", smtpPasswordField.text) - root.context.setConfig("send_server", smtpHostnameField.text) - root.context.setConfig("send_port", smtpPortField.text) - root.context.setConfig("send_security", securityModel.get(smtpSecurity.currentIndex).value) + console.log("Login"); + root.context.stopIo(); + root.context.setConfig("addr", addressField.text); + root.context.setConfig("mail_pw", passwordField.text); + root.context.setConfig("mail_user", imapLoginField.text); + root.context.setConfig("mail_server", imapHostnameField.text); + root.context.setConfig("mail_port", imapPortField.text); + root.context.setConfig("mail_security", securityModel.get(imapSecurity.currentIndex).value); + root.context.setConfig("send_user", smtpLoginField.text); + root.context.setConfig("send_pw", smtpPasswordField.text); + root.context.setConfig("send_server", smtpHostnameField.text); + root.context.setConfig("send_port", smtpPortField.text); + root.context.setConfig("send_security", securityModel.get(smtpSecurity.currentIndex).value); let certificate_checks = certificateChecks.model.get(certificateChecks.currentIndex).value; - root.context.setConfig("imap_certificate_checks", certificate_checks) - root.context.setConfig("smtp_certificate_checks", certificate_checks) - root.context.setConfig("socks5_enabled", socks5Enabled.checked ? "1" : "0") - root.context.setConfig("socks5_host", socks5Host.text) - root.context.setConfig("socks5_port", socks5Port.text) - root.context.setConfig("socks5_user", socks5Username.text) - root.context.setConfig("socks5_password", socks5Password.text) - root.context.configure() + root.context.setConfig("imap_certificate_checks", certificate_checks); + root.context.setConfig("smtp_certificate_checks", certificate_checks); + root.context.setConfig("socks5_enabled", socks5Enabled.checked ? "1" : "0"); + root.context.setConfig("socks5_host", socks5Host.text); + root.context.setConfig("socks5_port", socks5Port.text); + root.context.setConfig("socks5_user", socks5Username.text); + root.context.setConfig("socks5_password", socks5Password.text); + root.context.configure(); } } FileDialog { id: importBackupDialog + title: "Import backup" folder: shortcuts.home onAccepted: { - var url = importBackupDialog.fileUrl.toString() + var url = importBackupDialog.fileUrl.toString(); if (url.startsWith("file://")) { - var filename = url.substring(7) - console.log("Importing " + filename) - root.context.importBackup(filename) + var filename = url.substring(7); + console.log("Importing " + filename); + root.context.importBackup(filename); } } } Connections { - target: root.eventEmitter function onConfigureProgress(accountId, progress, comment) { - progressBar.value = progress / 1000.0 - if (progress == 1000) { - root.context.startIo() - } + progressBar.value = progress / 1000; + if (progress == 1000) + root.context.startIo(); + } function onImexProgress(accountId, progress) { - progressBar.value = progress / 1000.0 - if (progress == 1000) { - root.context.startIo() - } + progressBar.value = progress / 1000; + if (progress == 1000) + root.context.startIo(); + } + + target: root.eventEmitter } + } + } diff --git a/qml/HtmlViewSheet.qml b/qml/HtmlViewSheet.qml index 03e0147..005282c 100644 --- a/qml/HtmlViewSheet.qml +++ b/qml/HtmlViewSheet.qml @@ -1,23 +1,24 @@ import QtQuick 2.12 import QtWebEngine 1.10 - import org.kde.kirigami 2.12 as Kirigami Kirigami.OverlaySheet { property string subject property string html - header: Kirigami.Heading { - text: subject + onHtmlChanged: { + console.log("Loading HTML!"); + web.loadHtml(html); } WebEngineView { id: web + height: 500 } - onHtmlChanged: { - console.log("Loading HTML!") - web.loadHtml(html) + header: Kirigami.Heading { + text: subject } + } diff --git a/qml/Message.qml b/qml/Message.qml index f5ed398..b68f793 100644 --- a/qml/Message.qml +++ b/qml/Message.qml @@ -1,39 +1,35 @@ +import DeltaChat 1.0 +import QtMultimedia 5.8 +import QtQml.Models 2.1 import QtQuick 2.12 import QtQuick.Controls 2.12 -import QtQuick.Layouts 1.12 -import QtQml.Models 2.1 import QtQuick.Dialogs 1.1 -import QtMultimedia 5.8 +import QtQuick.Layouts 1.12 import org.kde.kirigami 2.12 as Kirigami -import DeltaChat 1.0 - RowLayout { - id: messageObject + id: root property DcMessage message property DcContext context - readonly property DcContact from: context.getContact(message.fromId) readonly property DcMessage quoteMessage: message.quotedMessage readonly property DcContact quoteFrom: quoteMessage ? context.getContact(quoteMessage.fromId) : null - - layoutDirection: message.fromId == 1 ? Qt.RightToLeft : Qt.LeftToRight - readonly property string overrideName: message.getOverrideSenderName() readonly property string displayName: overrideName != "" ? ("~" + overrideName) - : messageObject.message.fromId > 0 ? messageObject.from.displayName + : root.message.fromId > 0 ? root.from.displayName : "" + layoutDirection: message.fromId == 1 ? Qt.RightToLeft : Qt.LeftToRight Component.onCompleted: { // Only try to mark fresh and noticed messages as seen to // avoid unnecessary database calls when viewing an already read chat. - if ([10, 13].includes(messageObject.message.state)) { + if ([10, 13].includes(root.message.state)) { // Do not mark DC_CHAT_ID_DEADDROP messages as seen to // avoid contact request chat disappearing from chatlist. - if (messageObject.chatId != 1) { - messageObject.context.markseenMsgs([messageObject.message.id]) - } + if (root.chatId != 1) + root.context.markseenMsgs([root.message.id]); + } } @@ -49,22 +45,25 @@ RowLayout { ColumnLayout { Image { - source: "file:" + messageObject.message.file - sourceSize.width: messageObject.message.width - sourceSize.height: messageObject.message.height + source: "file:" + root.message.file + sourceSize.width: root.message.width + sourceSize.height: root.message.height fillMode: Image.PreserveAspectCrop - Layout.preferredWidth: messageObject.width + Layout.preferredWidth: root.width Layout.maximumWidth: Kirigami.Units.gridUnit * 30 Layout.maximumHeight: Kirigami.Units.gridUnit * 20 asynchronous: true } + Label { font.bold: true - color: messageObject.message.fromId > 0 ? messageObject.from.color : "black" - text: messageObject.displayName + color: root.message.fromId > 0 ? root.from.color : "black" + text: root.displayName textFormat: Text.PlainText } + } + } Component { @@ -73,19 +72,24 @@ RowLayout { ColumnLayout { MediaPlayer { id: player - source: Qt.resolvedUrl("file:" + messageObject.message.file) + + source: Qt.resolvedUrl("file:" + root.message.file) onError: console.log("Audio MediaPlayer error: " + errorString) } + Label { font.bold: true text: "Audio" textFormat: Text.PlainText } + Button { text: "play" onPressed: player.play() } + } + } Component { @@ -94,22 +98,28 @@ RowLayout { ColumnLayout { MediaPlayer { id: videoplayer - source: Qt.resolvedUrl("file:" + messageObject.message.file) + + source: Qt.resolvedUrl("file:" + root.message.file) onError: console.log("Video MediaPlayer error: " + errorString) } + VideoOutput { source: videoplayer } + Label { font.bold: true text: "Video" textFormat: Text.PlainText } + Button { text: "play" onPressed: videoplayer.play() } + } + } Component { @@ -117,66 +127,76 @@ RowLayout { Label { font.bold: true - color: messageObject.message.fromId > 0 ? messageObject.from.color : "black" - text: messageObject.displayName + color: root.message.fromId > 0 ? root.from.color : "black" + text: root.displayName textFormat: Text.PlainText } + } MouseArea { anchors.fill: parent - acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: function(mouse) { if (mouse.button === Qt.RightButton) - contextMenu.popup() + contextMenu.popup(); + } onPressAndHold: function(mouse) { if (mouse.source === Qt.MouseEventNotSynthesized) - contextMenu.popup() + contextMenu.popup(); + } MessageDialog { id: messageDialog + title: "Message info" - text: messageObject.context.getMessageInfo(messageObject.message.id) - onAccepted: { } + text: root.context.getMessageInfo(root.message.id) + onAccepted: { + } } Menu { id: contextMenu + Action { text: "Info" onTriggered: messageDialog.open() } + } + } ColumnLayout { id: messageContents Loader { - sourceComponent: [20, 21, 23].includes(messageObject.message.viewtype) ? imageMessageView - : [40, 41].includes(messageObject.message.viewtype) ? audioMessageView - : [50].includes(messageObject.message.viewtype) ? videoMessageView + sourceComponent: [20, 21, 23].includes(root.message.viewtype) ? imageMessageView + : [40, 41].includes(root.message.viewtype) ? audioMessageView + : [50].includes(root.message.viewtype) ? videoMessageView : textMessageView } // Quote RowLayout { Layout.leftMargin: Kirigami.Units.smallSpacing - visible: messageObject.message.quotedText + visible: root.message.quotedText implicitHeight: quoteTextEdit.height spacing: Kirigami.Units.smallSpacing + Rectangle { width: Kirigami.Units.smallSpacing - color: messageObject.quoteFrom ? messageObject.quoteFrom.color : "black" + color: root.quoteFrom ? root.quoteFrom.color : "black" Layout.fillHeight: true } + TextEdit { id: quoteTextEdit + Layout.maximumWidth: Kirigami.Units.gridUnit * 30 - text: messageObject.message.quotedText ? messageObject.message.quotedText : "" + text: root.message.quotedText ? root.message.quotedText : "" textFormat: Text.PlainText selectByMouse: true readOnly: true @@ -184,30 +204,30 @@ RowLayout { wrapMode: Text.Wrap font.pixelSize: 14 } + } // Message TextEdit { - Layout.maximumWidth: Math.min(messageObject.width, Kirigami.Units.gridUnit * 30) + Layout.maximumWidth: Math.min(root.width, Kirigami.Units.gridUnit * 30) textFormat: Text.PlainText selectByMouse: true readOnly: true color: "black" wrapMode: Text.Wrap font.pixelSize: 14 - Component.onCompleted: { - text = messageObject.message.text + text = root.message.text; } } Button { text: "Show full message" - visible: messageObject.message.hasHtml + visible: root.message.hasHtml onPressed: { - htmlSheet.subject = messageObject.message.subject - htmlSheet.html = messageObject.context.getMessageHtml(messageObject.message.id) - htmlSheet.open() + htmlSheet.subject = root.message.subject; + htmlSheet.html = root.context.getMessageHtml(root.message.id); + htmlSheet.open(); } } @@ -216,20 +236,20 @@ RowLayout { HtmlViewSheet { id: htmlSheet + subject: "" html: "" } - Kirigami.Icon { source: "computer" - visible: messageObject.message.isBot + visible: root.message.isBot Layout.preferredHeight: Kirigami.Units.gridUnit Layout.preferredWidth: Kirigami.Units.gridUnit } Kirigami.Icon { - source: messageObject.message.showPadlock ? "lock" : "unlock" + source: root.message.showPadlock ? "lock" : "unlock" Layout.preferredHeight: Kirigami.Units.gridUnit Layout.preferredWidth: Kirigami.Units.gridUnit } @@ -237,13 +257,17 @@ RowLayout { Label { font.pixelSize: 14 color: Kirigami.Theme.disabledTextColor - text: Qt.formatDateTime(messageObject.message.timestamp, "dd. MMM yyyy, hh:mm") - + (messageObject.message.state == 26 ? "✓" - : messageObject.message.state == 28 ? "✓✓" - : messageObject.message.state == 24 ? "✗" + text: Qt.formatDateTime(root.message.timestamp, "dd. MMM yyyy, hh:mm") + + (root.message.state == 26 ? "✓" + : root.message.state == 28 ? "✓✓" + : root.message.state == 24 ? "✗" : ""); } + } + } + } + } diff --git a/qml/NewChatPage.qml b/qml/NewChatPage.qml index aee1393..482b89f 100644 --- a/qml/NewChatPage.qml +++ b/qml/NewChatPage.qml @@ -1,39 +1,29 @@ +import DeltaChat 1.0 import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 import org.kde.kirigami 2.13 as Kirigami -import DeltaChat 1.0 - Kirigami.ScrollablePage { id: root - title: "New chat" - required property DcContext context function updateContacts() { let contacts = context.getContacts(0, searchField.text); - - contactsModel.clear() + contactsModel.clear(); for (let i = 0; i < contacts.length; i++) { - let contactId = contacts[i] - + let contactId = contacts[i]; const item = { - contactId: contactId - } - contactsModel.insert(i, item) + "contactId": contactId + }; + contactsModel.insert(i, item); } } + title: "New chat" Component.onCompleted: { - root.updateContacts() - } - - header: Kirigami.SearchField { - id: searchField - - onTextChanged: root.updateContacts() + root.updateContacts(); } ListModel { @@ -46,6 +36,38 @@ Kirigami.ScrollablePage { anchors.fill: parent model: contactsModel currentIndex: -1 + onCurrentItemChanged: { + if (currentIndex == -1) + return ; + + let contactId = contactsModel.get(contactsList.currentIndex).contactId; + console.log("Creating chat with " + contactId); + context.createChatByContactId(contactId); + pageStack.layers.pop(); + } + + Kirigami.PlaceholderMessage { + anchors.centerIn: parent + visible: contactsList.count == 0 && searchField.text == "" + text: "You have no contacts in addressbook yet" + } + + Kirigami.PlaceholderMessage { + anchors.centerIn: parent + visible: contactsList.count == 0 && searchField.text != "" + text: "Contact " + searchField.text + " is not in your address book." + + helpfulAction: Kirigami.Action { + icon.name: "list-add" + text: "Add contact" + onTriggered: { + let contactId = root.context.createContact("", searchField.text); + context.createChatByContactId(contactId); + pageStack.layers.pop(); + } + } + + } delegate: Kirigami.AbstractListItem { property DcContact contact: context.getContact(model.contactId) @@ -60,51 +82,31 @@ Kirigami.ScrollablePage { ColumnLayout { Layout.fillWidth: true + Label { text: contact.displayName font.weight: Font.Bold Layout.fillWidth: true } + Label { text: contact.addr font: Kirigami.Theme.smallFont Layout.fillWidth: true } - } - } - } - - Kirigami.PlaceholderMessage { - anchors.centerIn: parent - visible: contactsList.count == 0 && searchField.text == "" - text: "You have no contacts in addressbook yet" - } - Kirigami.PlaceholderMessage { - anchors.centerIn: parent - visible: contactsList.count == 0 && searchField.text != "" - helpfulAction: Kirigami.Action { - icon.name: "list-add" - text: "Add contact" - onTriggered: { - let contactId = root.context.createContact("", searchField.text) - context.createChatByContactId(contactId); - pageStack.layers.pop(); } + } - text: "Contact " + searchField.text + " is not in your address book." + } - onCurrentItemChanged: { - if (currentIndex == -1) { - return; - } + } - let contactId = contactsModel.get(contactsList.currentIndex).contactId; + header: Kirigami.SearchField { + id: searchField - console.log("Creating chat with " + contactId); - context.createChatByContactId(contactId); - pageStack.layers.pop(); - } + onTextChanged: root.updateContacts() } + } diff --git a/qml/SettingsPage.qml b/qml/SettingsPage.qml index c171b53..138fe71 100644 --- a/qml/SettingsPage.qml +++ b/qml/SettingsPage.qml @@ -1,21 +1,19 @@ +import DeltaChat 1.0 import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 import org.kde.kirigami 2.12 as Kirigami -import DeltaChat 1.0 - Kirigami.ScrollablePage { id: root - title: "Settings" - required property DcContext context + title: "Settings" + Kirigami.FormLayout { Image { Kirigami.FormData.label: "Avatar: " - source: "file:" + root.context.getConfig("selfavatar") } @@ -29,7 +27,6 @@ Kirigami.ScrollablePage { TextArea { Kirigami.FormData.label: "Signature: " - text: root.context.getConfig("selfstatus") onEditingFinished: root.context.setConfig("selfstatus", text) selectByMouse: true @@ -79,16 +76,27 @@ Kirigami.ScrollablePage { ComboBox { Kirigami.FormData.label: "Show classic emails: " + textRole: "text" + currentIndex: root.context.getConfig("show_emails") + onActivated: root.context.setConfig("show_emails", currentIndex) model: ListModel { id: certificateChecksModel - ListElement { text: "No, chats only" } - ListElement { text: "For accepted contacts" } - ListElement { text: "All" } + + ListElement { + text: "No, chats only" + } + + ListElement { + text: "For accepted contacts" + } + + ListElement { + text: "All" + } + } - textRole: "text" - currentIndex: root.context.getConfig("show_emails") - onActivated: root.context.setConfig("show_emails", currentIndex) + } Switch { @@ -121,5 +129,7 @@ Kirigami.ScrollablePage { text: settingsPageRoot.context.getConfig("socks5_password") onEditingFinished: settingsPageRoot.context.setConfig("socks5_password", text) } + } + } diff --git a/qml/main.qml b/qml/main.qml index 4969a45..1b7e1a4 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -1,33 +1,60 @@ +import DeltaChat 1.0 import QtQuick 2.12 import QtQuick.Controls 2.12 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") + Component.onCompleted: { + console.log('starting'); + // Create an account if there is none. + if (dcAccounts.getSelectedAccount() == null) { + console.log("Adding first account"); + dcAccounts.addAccount(); + } + eventEmitter = dcAccounts.getEventEmitter(); + eventEmitter.start(); + // Open selected account if there is one. + let selectedAccount = dcAccounts.getSelectedAccount(); + if (selectedAccount) { + if (selectedAccount.isConfigured()) + pageStack.replace("qrc:/qml/ChatlistPage.qml", { + "context": selectedAccount, + "eventEmitter": eventEmitter + }); + else + pageStack.replace("qrc:/qml/ConfigurePage.qml", { + "context": selectedAccount, + "eventEmitter": eventEmitter + }); + } + dcAccounts.startIo(); + } + onClosing: { + // Cancel all tasks that may block the termination of event loop. + dcAccounts.stopIo(); + } - Component {id: accountsPage; AccountsPage {}} - - pageStack.initialPage: Kirigami.Page {} + Component { + id: accountsPage - globalDrawer: Kirigami.GlobalDrawer { - header: Controls.Switch { - text: "Work offline" - onCheckedChanged: { - if (checked) { - dcAccounts.stopIo() - } else { - dcAccounts.startIo() - } - } + AccountsPage { } + } + + DcAccounts { + id: dcAccounts + } + + pageStack.initialPage: Kirigami.Page { + } + + globalDrawer: Kirigami.GlobalDrawer { actions: [ Kirigami.Action { text: "Maybe network" @@ -38,50 +65,28 @@ Kirigami.ApplicationWindow { text: "Switch account" iconName: "system-switch-user" onTriggered: { - while (pageStack.layers.depth > 1) { - pageStack.layers.pop() - } - pageStack.layers.push(accountsPage) + while (pageStack.layers.depth > 1) + pageStack.layers.pop(); + + pageStack.layers.push(accountsPage); } } ] - } - contextDrawer: Kirigami.ContextDrawer { - id: contextDrawer - } - - DcAccounts { - id: dcAccounts - } - - Component.onCompleted: { - console.log('starting') - - // Create an account if there is none. - if (dcAccounts.getSelectedAccount() == null) { - console.log("Adding first account"); - dcAccounts.addAccount(); - } - - eventEmitter = dcAccounts.getEventEmitter() - eventEmitter.start(); - - // Open selected account if there is one. - let selectedAccount = dcAccounts.getSelectedAccount(); - if (selectedAccount) { - if (selectedAccount.isConfigured()) { - pageStack.replace("qrc:/qml/ChatlistPage.qml", {context: selectedAccount, eventEmitter: eventEmitter}) - } else { - pageStack.replace("qrc:/qml/ConfigurePage.qml", {context: selectedAccount, eventEmitter: eventEmitter}) + header: Controls.Switch { + text: "Work offline" + onCheckedChanged: { + if (checked) + dcAccounts.stopIo(); + else + dcAccounts.startIo(); } } - dcAccounts.startIo() } - onClosing: { - // Cancel all tasks that may block the termination of event loop. - dcAccounts.stopIo() + contextDrawer: Kirigami.ContextDrawer { + id: contextDrawer } + } |