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 }