Warning, /plasma-mobile/spacebar/src/contents/ui/ChatsPage.qml is written in an unsupported language. File is not indexed.

0001 // SPDX-FileCopyrightText: 2020 Jonah BrĂ¼chert <jbb@kaidan.im>
0002 // SPDX-FileCopyrightText: 2022 Michael Lang <criticaltemp@protonmail.com>
0003 //
0004 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 
0006 import QtQuick
0007 import QtQuick.Layouts
0008 import QtQuick.Controls as Controls
0009 import Qt5Compat.GraphicalEffects
0010 
0011 import org.kde.kirigami as Kirigami
0012 import org.kde.kirigamiaddons.components as Components
0013 import org.kde.kirigamiaddons.delegates as Delegates
0014 
0015 import org.kde.spacebar
0016 
0017 Kirigami.ScrollablePage {
0018     id: chatPage
0019     title: i18n("Chats")
0020 
0021     property var conversations: []
0022     property bool editing: false
0023 
0024     function setConversations (phoneNumberList) {
0025         if (conversations.length === 0) {
0026             editing = true
0027         }
0028         const index = conversations.indexOf(phoneNumberList)
0029         if (index === -1) {
0030             conversations.push(phoneNumberList)
0031         } else {
0032             conversations.splice(index, 1)
0033         }
0034         conversations = conversations
0035         if (conversations.length === 0) {
0036             editing = false
0037         }
0038     }
0039 
0040     onWidthChanged: ChatListModel.setCharacterLimit(applicationWindow().width)
0041     
0042     actions: [
0043         Kirigami.Action {
0044             visible: !Kirigami.Settings.isMobile
0045             icon.name: "contact-new"
0046             text: i18n("New Conversation")
0047             onTriggered: pageStack.push("qrc:/NewConversationPage.qml")
0048         },
0049         Kirigami.Action {
0050             displayHint: Kirigami.DisplayHint.IconOnly
0051             icon.name: "settings-configure"
0052             text: i18nc("Configuring application settings", "Settings")
0053             onTriggered: {
0054                 applicationWindow().pageStack.push("qrc:/settings/SettingsPage.qml", {"chatListModel": ChatListModel})
0055             }
0056         },
0057         Kirigami.Action {
0058             displayHint: Kirigami.DisplayHint.IconOnly
0059             icon.name: "delete"
0060             text: i18nc("Deleting a conversation", "Delete")
0061             onTriggered: promptDialog.open()
0062             visible: editing === true
0063         }
0064     ]
0065 
0066     ListView {
0067         id: listView
0068         model: ChatListModel
0069         reuseItems: false
0070 
0071         Connections {
0072             target: ChatListModel
0073             function onChatStarted (messageModel) {
0074                 // Don't open two MessagesPages at the same time
0075                 if (pageStack.currentItem.hasOwnProperty("messageModel")) {
0076                     pageStack.pop()
0077                 }
0078 
0079                 Qt.callLater(pageStack.push, "qrc:/MessagesPage.qml", {"messageModel": messageModel})
0080             }
0081 
0082             function onChatsFetched() {
0083                 loading.visible = false
0084             }
0085         }
0086         
0087         Kirigami.PlaceholderMessage {
0088             anchors.centerIn: parent
0089             text: i18nc("Selecting recipients from contacts list", "Create a chat")
0090             icon.name: "dialog-messages"
0091             helpfulAction: actions[0]
0092             visible: !loading.visible && listView.count === 0
0093         }
0094 
0095         Controls.BusyIndicator {
0096             id: loading
0097             anchors.centerIn: parent
0098             visible: listView.count === 0
0099             running: visible
0100             width: Kirigami.Units.iconSizes.huge
0101             height: width
0102         }
0103         
0104         // mobile add action
0105         FloatingActionButton {
0106             anchors.fill: parent
0107             iconName: "list-add"
0108             onClicked: pageStack.push("qrc:/NewConversationPage.qml")
0109             visible: Kirigami.Settings.isMobile
0110         }
0111 
0112         delegate: Delegates.RoundedItemDelegate {
0113             id: delegateRoot
0114 
0115             required property string displayName
0116             required property var phoneNumberList
0117             required property int unreadMessages
0118             required property string lastMessage
0119             required property bool lastSentByMe
0120             required property var lastAttachment
0121             required property string lastContacted
0122             required property bool isContact
0123 
0124             property var attachments: lastAttachment ? JSON.parse(lastAttachment) : []
0125             property var image: attachments.find(o => o.mimeType.indexOf("image/") >= 0)
0126             property bool selected: conversations.indexOf(delegateRoot.phoneNumberList) >= 0
0127 
0128             width: listView.width
0129 
0130             contentItem: Loader {
0131                 sourceComponent: Component {
0132                     RowLayout {
0133                         spacing: Kirigami.Units.largeSpacing
0134 
0135                         Components.Avatar {
0136                             Layout.preferredWidth: Kirigami.Units.iconSizes.medium
0137                             Layout.preferredHeight: Kirigami.Units.iconSizes.medium
0138                             Layout.rightMargin: Kirigami.Units.largeSpacing
0139                             Layout.topMargin: Kirigami.Units.largeSpacing
0140                             Layout.bottomMargin: Kirigami.Units.largeSpacing
0141                             source: isContact ? "image://avatar/" + Utils.phoneNumberListToString(delegateRoot.phoneNumberList) : ""
0142                             name: delegateRoot.displayName
0143                             imageMode: Components.Avatar.ImageMode.AdaptiveImageOrInitals
0144                             initialsMode: isContact ? Components.Avatar.InitialsMode.UseInitials : Components.Avatar.InitialsMode.UseIcon
0145 
0146                             Rectangle {
0147                                 anchors.fill: parent
0148                                 radius: width * 0.5
0149                                 color: Kirigami.Theme.highlightColor
0150                                 visible: selected
0151 
0152                                 Kirigami.Icon {
0153                                     anchors.fill: parent
0154                                     source: "checkbox"
0155                                     color: Kirigami.Theme.highlightedTextColor
0156                                 }
0157                             }
0158                         }
0159 
0160                         ColumnLayout {
0161                             Layout.fillHeight: true
0162                             Layout.fillWidth: true
0163 
0164                             spacing: 0
0165                             Kirigami.Heading {
0166                                 id: nameLabel
0167                                 level: 5
0168                                 type: Kirigami.Heading.Type.Normal
0169                                 Layout.fillWidth: true
0170                                 text: delegateRoot.displayName
0171                                 wrapMode: Text.WrapAnywhere
0172                                 maximumLineCount: 1
0173                             }
0174                             Text {
0175                                 id: lastMessage
0176                                 Layout.fillWidth: true
0177                                 text: (delegateRoot.lastSentByMe ? i18nc("Indicating that message was sent by you", "You") + ": " : "") + (delegateRoot.lastMessage || (delegateRoot.image ? i18nc("Indicating that message contains an image", "Picture") : ""))
0178                                 wrapMode: Text.WrapAnywhere
0179                                 textFormat: Text.PlainText
0180                                 maximumLineCount: 1
0181                                 elide: Qt.ElideRight
0182                                 font.pointSize: Kirigami.Theme.defaultFont.pointSize - 2
0183                                 font.family: "Noto Sans, Noto Color Emoji"
0184                                 color: Kirigami.Theme.disabledTextColor
0185                             }
0186                         }
0187 
0188                         // spacer
0189                         Item {
0190                             Layout.fillWidth: true
0191                         }
0192 
0193                         Rectangle {
0194                             Layout.alignment: Qt.AlignRight
0195                             visible: delegateRoot.unreadMessages !== 0
0196                             height: Kirigami.Units.gridUnit * 1.2
0197                             width: number.width + 5 < height ? height: number.width + 5
0198                             radius: height * 0.5
0199                             color: Kirigami.Theme.highlightColor
0200                             Controls.Label {
0201                                 id: number
0202                                 anchors.centerIn: parent
0203                                 visible: delegateRoot.unreadMessages !== 0
0204                                 text: delegateRoot.unreadMessages
0205                                 color: Qt.rgba(1, 1, 1, 1)
0206                             }
0207                         }
0208 
0209                         Image {
0210                             id: image
0211                             source: delegateRoot.image ? "file://" + ChatListModel.attachmentsFolder(delegateRoot.phoneNumberList) + "/" + delegateRoot.image.fileName : ""
0212                             fillMode: Image.PreserveAspectCrop
0213                             sourceSize.height: Kirigami.Units.iconSizes.smallMedium * 4
0214                             Layout.preferredWidth: delegateRoot.image ? Kirigami.Units.iconSizes.smallMedium * 2 : 0
0215                             Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium * 2
0216                             asynchronous: true
0217                             cache: false
0218 
0219                             // rounded corners on image
0220                             layer.enabled: true
0221                             layer.effect: OpacityMask {
0222                                 maskSource: Item {
0223                                     width: image.width
0224                                     height: image.height
0225                                     Rectangle {
0226                                         anchors.fill: parent
0227                                         radius: Kirigami.Units.smallSpacing
0228                                     }
0229                                 }
0230                             }
0231                         }
0232 
0233                         Text {
0234                             visible: !delegateRoot.image
0235                             Layout.minimumWidth: Kirigami.Units.smallSpacing * 13
0236                             horizontalAlignment: Text.AlignRight
0237                             topPadding: Kirigami.Units.largeSpacing * 2
0238                             text: delegateRoot.lastContacted
0239                             font.pointSize: Kirigami.Theme.defaultFont.pointSize - 2
0240                             color: Kirigami.Theme.disabledTextColor
0241                         }
0242                     }
0243                 }
0244                 onLoaded: ChatListModel.fetchChatDetails(delegateRoot.phoneNumberList)
0245             }
0246 
0247             onPressAndHold: setConversations(delegateRoot.phoneNumberList)
0248 
0249             onClicked: {
0250                 if (editing) {
0251                     setConversations(delegateRoot.phoneNumberList)
0252                 } else {
0253                     // mark as read first, so data is correct when the model is initialized. This saves us a model reset
0254                     if (delegateRoot.unreadMessages > 0) {
0255                         ChatListModel.markChatAsRead(delegateRoot.phoneNumberList)
0256                         delegateRoot.unreadMessages = 0
0257                     }
0258                     ChatListModel.startChat(delegateRoot.phoneNumberList)
0259                 }
0260             }
0261         }
0262     }
0263 
0264     Kirigami.PromptDialog {
0265         id: promptDialog
0266         title: i18np("Delete this conversation?", "Delete %1 conversations?", conversations.length)
0267         subtitle: i18n("This is permanent and can't be undone")
0268         standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel
0269         onAccepted: {
0270             conversations.forEach(conversation => {
0271                 ChatListModel.deleteChat(conversation)
0272             })
0273             conversations = []
0274             editing = false
0275         }
0276         onRejected: close()
0277     }
0278 
0279     footer: null
0280 }