Dekko
Loading...
Searching...
No Matches
MessageListView.qml
1/* Copyright (C) 2016 - 2017 Dan Chapman <dpniel@ubuntu.com>
2
3 This file is part of Dekko email client for Ubuntu devices
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 2 of
8 the License or (at your option) version 3
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18import QtQuick 2.4
19import QtQuick.Controls.Suru 2.2
20import Lomiri.Components 1.3
21import Dekko.Mail.Accounts 1.0
22import Dekko.Mail 1.0
23import Dekko.Mail.API 1.0
24import Dekko.Mail.Stores.Mail 1.0
25import Dekko.Components 1.0
26import Dekko.Mail.Settings 1.0
27import Dekko.Lomiri.Components 1.0
28import Dekko.Lomiri.Constants 1.0
29import QuickFlux 1.0
30import PlugMan 1.0
31import MazDB 1.0
32import "../delegates"
33import "../models"
34import "../composer"
35
37 id: msgListPage
38
39 property alias mboxSearch: loader_mboxSearch.item
40
41 pageHeader {
42 title: MailStore.folderTitle
43 enableSearching: true
44 composeButtonEnabled: !dekko.isLargeFF
45 backAction: dekko.isSmallFF || MailStore.isInSelectionMode ? drawerAction : null
46 filterSections: isSearchMode ? MailStore.searchFilters : MailStore.defaultFilters
47 multiSelectActionList: multiSelectRegistry.actions
48 }
49
50 onSelectedIndexChanged: {
51 if (isSearchMode) {
52 if (selectedIndex === 0) {
53 mboxSearch.location = MailboxSearch.Local
54 } else {
55 mboxSearch.location = MailboxSearch.Remote
56 }
57 return;
58 }
59 var filter = MessageList.All;
60 switch (selectedIndex) {
61 case 0: // All
62 filter = MessageList.All;
63 break;
64 case 1: // Unread
65 filter = MessageList.Unread;
66 break;
67 case 2: // Starred
68 filter = MessageList.Important;
69 break;
70 case 3: // Replied
71 filter = MessageList.Replied;
72 break;
73 case 4: // Forwarded
74 filter = MessageList.Forwarded;
75 break;
76 case 5: // Attachments
77 filter = MessageList.Attachments;
78 break;
79 case 6: // Calendar
80 filter = MessageList.Calendar;
81 break;
82 }
83 MessageActions.setMessageListFilter(filter)
84 }
85
86 onMultiSelectCanceled: MessageActions.endMultiSelection()
87 onIsSearchModeChanged: {
88 if (isSearchMode) {
89 listView.currentIndex = -1
90 listView.model = mboxSearch.results
91 } else {
92 MessageActions.resetMessageList()
93 }
94 }
95
96 onSearchActivated: mboxSearch.search(searchString)
97 onSearchCanceled: {
98 pageHeader.searchInProgress = false;
99 mboxSearch.cancelSearch()
100 }
101
102 MazDBSettings {
103 category: "ui-property-cache"
104 property alias msgListFilterIndex: msgListPage.selectedIndex
105 }
106
107 // Plugins can add bulk message actions by registering Action for this location
109 id: multiSelectRegistry
110 location: MailStore.listenerId ? "Dekko::Mail::MultiSelectAction" : ""
111 defaultActions: [
112 Action {
113 iconSource: !MailStore.msgList.canSelectAll ? Paths.actionIconUrl(Paths.NoneSelectedIcon) :
114 Paths.actionIconUrl(Paths.SelectIcon)
115 text: MailStore.msgList.canSelectAll ? qsTr("Unselect all") : qsTr("Select all")
116 onTriggered: {
117 if (MailStore.msgList.canSelectAll) {
118 MessageActions.selectAllMessages()
119 } else {
120 MessageActions.unselectAllMessages()
121 }
122 }
123 },
124 Action {
125 iconSource: MailStore.msgList.canMarkSelectionImportant ? Paths.actionIconUrl(Paths.StarredIcon) :
126 Paths.actionIconUrl(Paths.UnStarredIcon)
127 text: MailStore.msgList.canMarkSelectionImportant ? qsTr("Star") : qsTr("Remove star")
128 onTriggered: MessageActions.markSelectedMessagesImportant()
129 },
130 Action {
131 text: MailStore.msgList.canMarkSelectionRead ? qsTr("Mark as un-read") : qsTr("Mark as read")
132 iconSource: !MailStore.msgList.canMarkSelectionRead ? Paths.actionIconUrl(Paths.MailUnreadIcon) :
133 Paths.actionIconUrl(Paths.MailReadIcon)
134 onTriggered: MessageActions.markSelectedMessagesRead()
135 },
136 Action {
137 iconSource: Paths.actionIconUrl(Paths.DeleteIcon)
138 text: qsTr("Delete")
139 onTriggered: MessageActions.deleteSelectedMessages()
140 }
141 ]
142 }
143
144 Action {
145 id: drawerAction
146 iconName: "navigation-menu"
147 onTriggered: ViewActions.toggleNavDrawer()
148 }
149
150 Loader {
151 id: loader_mboxSearch
152 asynchronous: true
153 sourceComponent: Component {
154 id: component_mboxSearch
155 MailboxSearch {
156 messageKey: MailStore.msgListKey
157 location: MailboxSearch.Local
158 limit: 50
159 sortBy: MailboxSearch.TimeStamp
160 sortOrder: Qt.DescendingOrder
161 onStatusChanged: {
162 if (status === SearchService.InProgress) {
163 pageHeader.searchInProgress = true
164 } else {
165 pageHeader.searchInProgress = false
166 }
167 }
168 }
169 }
170 }
171
172
174 id: cont
175
176 ListItem {
177 id: undoNotification
178 expansion.height: units.gu(6)
179 expansion.expanded: MailStore.hasUndoableActions
180 divider.visible: MailStore.hasUndoableActions
181 height: 0
182 anchors {
183 left: parent.left
184 top: parent.top
185 right: parent.right
186 }
187 color: Suru.secondaryBackgroundColor
188
189 Label {
190 anchors {
191 left: parent.left
192 leftMargin: Style.defaultSpacing
193 verticalCenter: parent.verticalCenter
194 right: undoButton.left
195 rightMargin: units.gu(1.5)
196 }
197 elide: Text.ElideRight
198 clip: true
199 visible: MailStore.hasUndoableActions
200 text: MailStore.undoableActionDescription
201 }
202
203 Button {
204 id: undoButton
205 height: Style.largeSpacing
206 width: undoAction.text.width > units.gu(15) ? units.gu(15) : undoAction.text.width
207 anchors {
208 right: parent.right
209 rightMargin: Style.defaultSpacing
210 verticalCenter: parent.verticalCenter
211 }
212 Suru.highlightType: Suru.PositiveHighlight
213 color: Suru.highlightColor
214 action: Action {
215 id: undoAction
216 text: qsTr("Undo")
217 onTriggered: MessageActions.undoRecentActions()
218 }
219 }
220 }
221
222 ScrollView {
223
224 anchors {
225 left: parent.left
226 top: undoNotification.bottom
227 right: parent.right
228 bottom: parent.bottom
229 }
230
231 Keys.forwardTo: [listView]
232
233 ListView {
234 id: listView
235 property int selectionIndex: 0
236 // Workaround to stop the message at index 0 being loaded when
237 // switching back from search mode.
238 property bool dontLoadNextCurrentItem: false
239
240 anchors.fill: parent
241 clip: true
242 currentIndex: -1
243 onCurrentIndexChanged: {
244 MailStore.currentSelectedIndex = currentIndex
245 }
246
247 onCurrentItemChanged: {
248 if (!currentItem || currentIndex === -1) {
249 return
250 }
251 if (dontLoadNextCurrentItem) {
252 currentIndex = -1
253 dontLoadNextCurrentItem = false
254 return
255 }
256
257 MessageActions.openMessage(currentItem.msg.messageId)
258 if (currentIndex == MailStore.msgListModel.count - 1) {
259 MessageActions.showMoreMessages()
260 }
261 }
262
263// add: DekkoAnimation.listViewAddTransition
264 addDisplaced: DekkoAnimation.listViewAddDisplacedTransition
265 remove: DekkoAnimation.listViewRemoveTransition
266 removeDisplaced: DekkoAnimation.listViewRemoveDisplacedTransition
267
268 highlight: !dekko.isSmallFF ? Style.highlightBar : null
269 highlightFollowsCurrentItem: true
270 highlightMoveDuration: 200
271 cacheBuffer: units.gu(120)
272 model: MailStore.msgListModel
273 delegate: MessageListDelegate{
274 id: msgListDelegate
275 width: ListView.view.width
276 // This prevents making excessive calls to data()
277 // We just take the actual MinimalMessage object
278 // in one go and reuse it throughout.
279 msg: model.qtObject
280
281 leftSideAction: Action {
282 iconName: "delete"
283 onTriggered: {
284 MessageActions.deleteMessage(msgListDelegate.msg.messageId)
285 }
286 }
287
288 rightSideActions: [flagAction, readAction, contextAction]
289
290 onItemClicked: {
291
292 if (MailStore.isInSelectionMode) {
293 if (msg.checked) {
294 MessageActions.setMessageCheck(model.index, Qt.Unchecked)
295 } else {
296 MessageActions.setMessageCheck(model.index, Qt.Checked)
297 }
298 return;
299 }
300
301 if (msg.isDraft) {
302 ComposerActions.openDraft(msg.messageId)
303 return;
304 }
305
306 if (mouse.button === Qt.RightButton) {
307 rightClickActions.trigger()
308 return;
309 }
310
311 listView.currentIndex = model.index
312 }
313 onItemPressAndHold: {
314 // TODO: get multiselect working on search results.
315 if (isSearchMode) {
316 return;
317 }
318 if (!MailStore.isInSelectionMode) {
319 listView.selectionIndex = model.index
320 MessageActions.startMultiSelection()
321 MessageActions.setMessageCheck(model.index, Qt.Checked)
322 msgListPage.startMultiSelect()
323 }
324 }
325 }
326 // canLoadMore will always be false when the list is empty so there
327 // isn't any need to add a binding on count/totalCount as that signal
328 // emits *very* often and really bogs down the UI. canPossiblyLoadMore will
329 // only get emitted after all the insertions/removals have completed for a query.
330 footer: (!isSearchMode && !MailStore.msgListLoading && MailStore.canLoadMore) ? footerComponent : null
331
332 Component {
333 id: footerComponent
334 ListItem {
335 Label {
336 anchors.centerIn: parent
337 text: qsTr("Load more messages ...")
338 }
339 onClicked: MessageActions.showMoreMessages()
340 }
341 }
342
343 PullToRefresh {
344 refreshing: MailboxStore.folderSyncActive
345 onRefresh: MailboxActions.syncStandardFolders(Folder.StandardFolder)
346 }
347 }
348 }
349
350 ActivityIndicator {
351 anchors {
352 top: parent.top
353 horizontalCenter: parent.horizontalCenter
354 topMargin: units.gu(20)
355 }
356 visible: MailStore.msgListLoading
357 running: MailStore.msgListLoading
358 }
359 }
360
361 Binding {
362 target: listView;
363 property: "currentIndex";
364 value: MailStore.currentSelectedIndex
365 when: MailStore
366 }
367
368 AppListener {
369
370 Filter {
371 type: MessageKeys.resetMessageList
372 onDispatched: {
373 listView.currentIndex = -1
374 listView.positionViewAtBeginning()
375 selectedIndex = 0
376 if (listView.model !== MailStore.msgListModel) {
377 listView.dontLoadNextCurrentItem = true
378 listView.model = MailStore.msgListModel
379 }
380 }
381 }
382
383 Filter {
384 type: MailboxKeys.resetSelectedMsgIndex
385 onDispatched: {
386 listView.currentIndex = -1
387 }
388 }
389 }
390}