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 }