Merge lp:~tiagosh/messaging-app/fix-1376793 into lp:messaging-app

Proposed by Tiago Salem Herrmann
Status: Merged
Approved by: Gustavo Pichorim Boiko
Approved revision: 294
Merged at revision: 293
Proposed branch: lp:~tiagosh/messaging-app/fix-1376793
Merge into: lp:messaging-app
Diff against target: 349 lines (+182/-26)
4 files modified
src/qml/MainPage.qml (+5/-0)
src/qml/Messages.qml (+49/-2)
src/qml/ThreadDelegate.qml (+87/-24)
tests/autopilot/messaging_app/tests/test_messaging.py (+41/-0)
To merge this branch: bzr merge lp:~tiagosh/messaging-app/fix-1376793
Reviewer Review Type Date Requested Status
Gustavo Pichorim Boiko (community) Approve
PS Jenkins bot continuous-integration Needs Fixing
Review via email: mp+245812@code.launchpad.net

Commit message

Query thread messages when searchTerm changes.

Description of the change

Query thread messages when searchTerm changes.

--Checklist--
Are there any related MPs required for this MP to build/function as expected? Please list.
https://code.launchpad.net/~boiko/history-service/fix_filter_passing/+merge/245799

Is your branch in sync with latest trunk (e.g. bzr pull lp:trunk -> no changes)
Yes

Did you perform an exploratory manual test run of your code change and any related functionality on device or emulator?
Yes

Did you successfully run all tests found in your component's Test Plan (https://wiki.ubuntu.com/Process/Merges/TestPlan/messaging-app) on device or emulator?
Yes

If you changed the UI, was the change specified/approved by design?
N/A

If you changed UI labels, did you update the pot file?
N/A

If you changed the packaging (debian), did you add a core-dev as a reviewer to this MP?
N/A

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
286. By Tiago Salem Herrmann

display information about the matched event

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
287. By Tiago Salem Herrmann

scroll to the item that matched the search

288. By Tiago Salem Herrmann

merge trunk

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Gustavo Pichorim Boiko (boiko) wrote :

173 + HistoryEventModel {

Can you create this inside a loader so that when not searching there is no memory penalty?

review: Needs Fixing
289. By Tiago Salem Herrmann

use a Loader to avoid creating instances of the model when not required

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
290. By Tiago Salem Herrmann

- make loader asynchronous
- fix dates when searching

291. By Tiago Salem Herrmann

show spinner while searching

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
292. By Tiago Salem Herrmann

update search when delegates are created

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
293. By Tiago Salem Herrmann

hide bottom panel when searching
use a timer to retrieve more items from history

294. By Tiago Salem Herrmann

anchor to the bottom of the screen while searching

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Gustavo Pichorim Boiko (boiko) wrote :

Did you perform an exploratory manual test run of the code change and any related functionality on device or emulator?
Yes

Did CI run pass? If not, please explain why.
No, but not related to this change

Have you checked that submitter has accurately filled out the submitter checklist and has taken no shortcut?
Yes

Code looks good and works as expected!

review: Approve
295. By Tiago Salem Herrmann

add autopilot test for message search

296. By Tiago Salem Herrmann

merge trunk

297. By Tiago Salem Herrmann

fix broken test

Revision history for this message
Gustavo Pichorim Boiko (boiko) wrote :

331 + time.sleep(2)
332 + count = 0
333 + for thread in threads:
334 + if thread.height != 0:
335 + count+=1
336 +
337 + self.assertThat(count, Equals(3))

I think it would be better to write a function to count the threads and use:
self.assertThat(count_visible_threads(threads), Eventually(Equals(3))

so that we don't have to rely on timers.

review: Needs Fixing
298. By Tiago Salem Herrmann

do not rely on timers

299. By Tiago Salem Herrmann

use Eventually()

300. By Tiago Salem Herrmann

use lamba functions

Revision history for this message
Gustavo Pichorim Boiko (boiko) wrote :

Looks good now!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/qml/MainPage.qml'
--- src/qml/MainPage.qml 2015-01-05 20:35:11 +0000
+++ src/qml/MainPage.qml 2015-01-23 15:31:27 +0000
@@ -43,6 +43,7 @@
4343
44 TextField {44 TextField {
45 id: searchField45 id: searchField
46 objectName: "searchField"
46 visible: mainPage.searching47 visible: mainPage.searching
47 anchors {48 anchors {
48 left: parent.left49 left: parent.left
@@ -179,6 +180,7 @@
179 }180 }
180 listModel: threadModel181 listModel: threadModel
181 clip: true182 clip: true
183 cacheBuffer: threadList.height * 2
182 section.property: "eventDate"184 section.property: "eventDate"
183 //spacing: searchField.text === "" ? units.gu(-2) : 0185 //spacing: searchField.text === "" ? units.gu(-2) : 0
184 section.delegate: searching && searchField.text !== "" ? null : sectionDelegate186 section.delegate: searching && searchField.text !== "" ? null : sectionDelegate
@@ -203,6 +205,9 @@
203 } else {205 } else {
204 var properties = model.properties206 var properties = model.properties
205 properties["keyboardFocus"] = false207 properties["keyboardFocus"] = false
208 if (displayedEvent != null) {
209 properties["scrollToEventId"] = displayedEvent.eventId
210 }
206 if (model.participants[0].alias) {211 if (model.participants[0].alias) {
207 properties["firstRecipientAlias"] = model.participants[0].alias;212 properties["firstRecipientAlias"] = model.participants[0].alias;
208 }213 }
209214
=== modified file 'src/qml/Messages.qml'
--- src/qml/Messages.qml 2015-01-05 19:47:07 +0000
+++ src/qml/Messages.qml 2015-01-23 15:31:27 +0000
@@ -50,6 +50,8 @@
50 property alias contactWatcher: contactWatcherInternal50 property alias contactWatcher: contactWatcherInternal
51 property string lastFilter: ""51 property string lastFilter: ""
52 property string text: ""52 property string text: ""
53 property string scrollToEventId: ""
54 property bool isSearching: scrollToEventId !== ""
53 property string latestEventId: ""55 property string latestEventId: ""
5456
55 function addAttachmentsToModel(transfer) {57 function addAttachmentsToModel(transfer) {
@@ -109,6 +111,16 @@
109 onAccountChanged: messages.account = mainView.account111 onAccountChanged: messages.account = mainView.account
110 }112 }
111113
114 ActivityIndicator {
115 id: activityIndicator
116 anchors {
117 verticalCenter: parent.verticalCenter
118 horizontalCenter: parent.horizontalCenter
119 }
120 running: isSearching
121 visible: running
122 }
123
112 ListModel {124 ListModel {
113 id: attachments125 id: attachments
114 }126 }
@@ -549,12 +561,47 @@
549 latestEventId = eventModel.get(0).eventId561 latestEventId = eventModel.get(0).eventId
550 messageList.positionViewAtBeginning()562 messageList.positionViewAtBeginning()
551 }563 }
564 if (isSearching) {
565 // if we ask for more items manually listview will stop working,
566 // so we only set again once the item was found
567 messageList.listModel = null
568 // always check last 15 items
569 var maxItems = 15
570 for (var i = count-1; count >= i; i--) {
571 if (--maxItems < 0) {
572 break;
573 }
574 if (eventModel.get(i).eventId == scrollToEventId) {
575 scrollToEventId = ""
576 messageList.listModel = eventModel
577 messageList.positionViewAtIndex(i, ListView.Center)
578 return;
579 }
580 }
581
582 if (eventModel.canFetchMore && isSearching) {
583 fetchMoreTimer.running = true
584 } else {
585 // event not found
586 scrollToEventId = ""
587 messageList.listModel = eventModel
588 }
589 }
552 }590 }
553 }591 }
554592
593 Timer {
594 id: fetchMoreTimer
595 running: false
596 interval: 100
597 repeat: false
598 onTriggered: eventModel.fetchMore()
599 }
600
555 MessagesListView {601 MessagesListView {
556 id: messageList602 id: messageList
557 objectName: "messageList"603 objectName: "messageList"
604 visible: !isSearching
558605
559 // because of the header606 // because of the header
560 clip: true607 clip: true
@@ -568,11 +615,11 @@
568615
569 Item {616 Item {
570 id: bottomPanel617 id: bottomPanel
571 anchors.bottom: keyboard.top618 anchors.bottom: isSearching ? parent.bottom : keyboard.top
572 anchors.left: parent.left619 anchors.left: parent.left
573 anchors.right: parent.right620 anchors.right: parent.right
574 height: selectionMode ? 0 : textEntry.height + units.gu(2)621 height: selectionMode ? 0 : textEntry.height + units.gu(2)
575 visible: !selectionMode622 visible: !selectionMode && !isSearching
576 clip: true623 clip: true
577 MouseArea {624 MouseArea {
578 anchors.fill: parent625 anchors.fill: parent
579626
=== modified file 'src/qml/ThreadDelegate.qml'
--- src/qml/ThreadDelegate.qml 2015-01-05 20:35:11 +0000
+++ src/qml/ThreadDelegate.qml 2015-01-23 15:31:27 +0000
@@ -22,6 +22,8 @@
22import Ubuntu.Telephony 0.122import Ubuntu.Telephony 0.1
23import Ubuntu.Contacts 0.123import Ubuntu.Contacts 0.1
24import QtContacts 5.024import QtContacts 5.0
25import Ubuntu.History 0.1
26import "dateUtils.js" as DateUtils
2527
26ListItemWithActions {28ListItemWithActions {
27 id: delegate29 id: delegate
@@ -32,6 +34,10 @@
32 property string phoneNumber: delegateHelper.phoneNumber34 property string phoneNumber: delegateHelper.phoneNumber
33 property bool unknownContact: delegateHelper.isUnknown35 property bool unknownContact: delegateHelper.isUnknown
34 property string threadId: model.threadId36 property string threadId: model.threadId
37 property var displayedEvent: null
38 property var displayedEventTextAttachments: displayedEvent ? displayedEvent.textMessageAttachments : eventTextAttachments
39 property var displayedEventTimestamp: displayedEvent ? displayedEvent.timestamp : eventTimestamp
40 property var displayedEventTextMessage: displayedEvent ? displayedEvent.textMessage : eventTextMessage
35 property string groupChatLabel: {41 property string groupChatLabel: {
36 var firstRecipient42 var firstRecipient
37 if (unknownContact) {43 if (unknownContact) {
@@ -53,15 +59,15 @@
53 var videoCount = 059 var videoCount = 0
54 var contactCount = 060 var contactCount = 0
55 var attachmentCount = 061 var attachmentCount = 0
56 for (var i = 0; i < eventTextAttachments.length; i++) {62 for (var i = 0; i < displayedEventTextAttachments.length; i++) {
57 if (startsWith(eventTextAttachments[i].contentType, "text/plain")) {63 if (startsWith(displayedEventTextAttachments[i].contentType, "text/plain")) {
58 return application.readTextFile(eventTextAttachments[i].filePath)64 return application.readTextFile(displayedEventTextAttachments[i].filePath)
59 } else if (startsWith(eventTextAttachments[i].contentType, "image/")) {65 } else if (startsWith(displayedEventTextAttachments[i].contentType, "image/")) {
60 imageCount++66 imageCount++
61 } else if (startsWith(eventTextAttachments[i].contentType, "video/")) {67 } else if (startsWith(displayedEventTextAttachments[i].contentType, "video/")) {
62 videoCount++68 videoCount++
63 } else if (startsWith(eventTextAttachments[i].contentType, "text/vcard") ||69 } else if (startsWith(displayedEventTextAttachments[i].contentType, "text/vcard") ||
64 startsWith(eventTextAttachments[i].contentType, "text/x-vcard")) {70 startsWith(displayedEventTextAttachments[i].contentType, "text/x-vcard")) {
65 contactCount++71 contactCount++
66 }72 }
67 }73 }
@@ -79,26 +85,11 @@
79 if (attachmentCount > 0) {85 if (attachmentCount > 0) {
80 return i18n.tr("Attachment: %1 file", "Attachments: %1 files").arg(attachmentCount)86 return i18n.tr("Attachment: %1 file", "Attachments: %1 files").arg(attachmentCount)
81 }87 }
82 return eventTextMessage88 return displayedEventTextMessage
83 }89 }
84 anchors.left: parent.left90 anchors.left: parent.left
85 anchors.right: parent.right91 anchors.right: parent.right
86 height: units.gu(10)92 height: units.gu(10)
87 // WORKAROUND: history-service can't filter by contact names
88 onSearchTermChanged: {
89 var found = false
90 var searchTermLowerCase = searchTerm.toLowerCase()
91 if (searchTerm !== "") {
92 if ((delegateHelper.phoneNumber.toLowerCase().search(searchTermLowerCase) !== -1)
93 || (!unknownContact && delegateHelper.alias.toLowerCase().search(searchTermLowerCase) !== -1)) {
94 found = true
95 }
96 } else {
97 found = true
98 }
99
100 height = found ? units.gu(8) : 0
101 }
10293
103 leftSideAction: Action {94 leftSideAction: Action {
104 iconName: "delete"95 iconName: "delete"
@@ -108,6 +99,12 @@
108 }99 }
109 }100 }
110101
102 Component.onCompleted: {
103 if (searchTerm !== "") {
104 delegateHelper.updateSearch()
105 }
106 }
107
111 ContactAvatar {108 ContactAvatar {
112 id: avatar109 id: avatar
113110
@@ -151,7 +148,13 @@
151 right: parent.right148 right: parent.right
152 }149 }
153150
154 text: Qt.formatTime(eventTimestamp, Qt.DefaultLocaleShortDate)151 text: {
152 if (!displayedEvent) {
153 Qt.formatTime(displayedEventTimestamp, Qt.DefaultLocaleShortDate)
154 } else {
155 DateUtils.friendlyDay(Qt.formatDate(displayedEventTimestamp, "yyyy/MM/dd"))
156 }
157 }
155 fontSize: "small"158 fontSize: "small"
156 }159 }
157160
@@ -215,6 +218,66 @@
215 property alias contexts: phoneDetail.contexts218 property alias contexts: phoneDetail.contexts
216 property bool isUnknown: contactId === ""219 property bool isUnknown: contactId === ""
217 property string phoneNumberSubTypeLabel: ""220 property string phoneNumberSubTypeLabel: ""
221 property var searchHistoryFilter: HistoryIntersectionFilter {
222 HistoryFilter { filterProperty: "threadId"; filterValue: model.threadId }
223 HistoryFilter { filterProperty: "accountId"; filterValue: model.accountId }
224 HistoryFilter { filterProperty: "message"; filterValue: searchTerm; matchFlags: HistoryFilter.MatchContains }
225 }
226
227 function updateSearch() {
228 var found = false
229 var searchTermLowerCase = searchTerm.toLowerCase()
230 if (searchTerm !== "") {
231 if ((delegateHelper.phoneNumber.toLowerCase().search(searchTermLowerCase) !== -1)
232 || (!unknownContact && delegateHelper.alias.toLowerCase().search(searchTermLowerCase) !== -1)) {
233 found = true
234 } else {
235 searchEventModelLoader.active = true
236 }
237 } else {
238 delegate.displayedEvent = null
239 searchEventModelLoader.active = false
240 found = true
241 }
242
243 delegate.height = found ? units.gu(8) : 0
244 }
245
246 // WORKAROUND: history-service can't filter by contact names
247 Connections {
248 target: delegate
249 onSearchTermChanged: {
250 delegateHelper.updateSearch()
251 }
252 }
253
254 Loader {
255 id: searchEventModelLoader
256 active: false
257 asynchronous: true
258 sourceComponent: searchEventModelComponent
259 }
260
261 Component {
262 id: searchEventModelComponent
263 HistoryEventModel {
264 id: eventModel
265 type: HistoryThreadModel.EventTypeText
266 filter: delegateHelper.searchHistoryFilter
267 onCountChanged: {
268 if (count > 0) {
269 delegate.height = units.gu(8)
270 delegate.displayedEvent = eventModel.get(0)
271 } else if (searchTerm == "") {
272 delegate.height = units.gu(8)
273 delegate.displayedEvent = null
274 } else {
275 delegate.displayedEvent = null
276 delegate.height = 0
277 }
278 }
279 }
280 }
218281
219 function updateSubTypeLabel() {282 function updateSubTypeLabel() {
220 var subLabel = "";283 var subLabel = "";
221284
=== modified file 'tests/autopilot/messaging_app/tests/test_messaging.py'
--- tests/autopilot/messaging_app/tests/test_messaging.py 2014-12-09 17:46:14 +0000
+++ tests/autopilot/messaging_app/tests/test_messaging.py 2015-01-23 15:31:27 +0000
@@ -22,6 +22,8 @@
22from messaging_app import helpers22from messaging_app import helpers
23from messaging_app.tests import MessagingAppTestCase23from messaging_app.tests import MessagingAppTestCase
2424
25import ubuntuuitoolkit
26
2527
26class BaseMessagingTestCase(MessagingAppTestCase):28class BaseMessagingTestCase(MessagingAppTestCase):
2729
@@ -298,6 +300,45 @@
298 list_view = self.main_view.get_multiple_selection_list_view()300 list_view = self.main_view.get_multiple_selection_list_view()
299 self.assertThat(list_view.count, Eventually(Equals(0)))301 self.assertThat(list_view.count, Eventually(Equals(0)))
300302
303 def test_search_for_message(self):
304 def count_visible_threads(threads):
305 count = 0
306 for thread in threads:
307 if thread.height != 0:
308 count += 1
309 return count
310
311 # populate history
312 helpers.receive_sms('08151', 'Ubuntu')
313 helpers.receive_sms('08152', 'Ubuntu1')
314 helpers.receive_sms('08153', 'Ubuntu2')
315 helpers.receive_sms('08154', 'Ubuntu22')
316 helpers.receive_sms('08155', 'Ubuntu23')
317
318 # verify that we got the messages
319 self.assertThat(self.thread_list.count, Eventually(Equals(5)))
320 threads = self.thread_list.select_many('ThreadDelegate')
321
322 # tap search
323 self.main_view.click_header_action('searchAction')
324 text_field = self.main_view.select_single(
325 ubuntuuitoolkit.TextField,
326 objectName='searchField')
327
328 text_field.write('Ubuntu2')
329 self.assertThat(
330 lambda: count_visible_threads(threads), Eventually(Equals(3)))
331
332 text_field.clear()
333 text_field.write('Ubuntu1')
334 self.assertThat(
335 lambda: count_visible_threads(threads), Eventually(Equals(1)))
336
337 text_field.clear()
338 text_field.write('Ubuntu')
339 self.assertThat(
340 lambda: count_visible_threads(threads), Eventually(Equals(5)))
341
301342
302class MessagingTestCaseWithExistingThread(BaseMessagingTestCase):343class MessagingTestCaseWithExistingThread(BaseMessagingTestCase):
303344

Subscribers

People subscribed via source and target branches