Merge lp:~nik90/podbird/filter-podcasts into lp:podbird

Proposed by Nekhelesh Ramananthan
Status: Merged
Approved by: Michael Sheldon
Approved revision: 22
Merged at revision: 11
Proposed branch: lp:~nik90/podbird/filter-podcasts
Merge into: lp:podbird
Prerequisite: lp:~nik90/podbird/improve-podcast-page
Diff against target: 587 lines (+272/-131)
4 files modified
app/podbird.qml (+1/-0)
app/ui/EpisodesPage.qml (+78/-7)
app/ui/PodcastsTab.qml (+134/-98)
po/com.mikeasoft.podbird.pot (+59/-26)
To merge this branch: bzr merge lp:~nik90/podbird/filter-podcasts
Reviewer Review Type Date Requested Status
Michael Sheldon Approve
Review via email: mp+247537@code.launchpad.net

Commit message

- Ability to search user added podcasts by their name
- Ability to search for episodes in a podcast by their name
- Moved the add feed url textfield onto the header similar to the designs provided for clock, music and other core apps
- Set anchorToKeyboard to true to anchor the contents correctly when OSK is visible

Description of the change

This MP adds the following features,
- Ability to search user added podcasts by their name
- Ability to search for episodes in a podcast by their name
- Moved the add feed url textfield onto the header similar to the designs provided for clock, music and other core apps
- Set anchorToKeyboard to true to anchor the contents correctly when OSK is visible

As always screenshots :) http://imgur.com/a/2iK8R#0

To post a comment you must log in.
lp:~nik90/podbird/filter-podcasts updated
22. By Nekhelesh Ramananthan

Added episode filtering with empty state

Revision history for this message
Michael Sheldon (michael-sheldon) wrote :

Looks good, thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'app/podbird.qml'
2--- app/podbird.qml 2015-01-27 02:23:48 +0000
3+++ app/podbird.qml 2015-01-27 02:23:48 +0000
4@@ -17,6 +17,7 @@
5 property string currentGuid
6
7 useDeprecatedToolbar: false
8+ anchorToKeyboard: true
9
10 width: units.gu(50)
11 height: units.gu(75)
12
13=== modified file 'app/ui/EpisodesPage.qml'
14--- app/ui/EpisodesPage.qml 2015-01-27 02:23:48 +0000
15+++ app/ui/EpisodesPage.qml 2015-01-27 02:23:48 +0000
16@@ -25,6 +25,22 @@
17 loadEpisodes(episodeId, episodeArtist, episodeImage)
18 }
19
20+ /*
21+ #FIXME: The following lines of code is necessary due to a upstream bug
22+ in the SDK http://pad.lv/1400297. This bug is still present in the rtm.
23+ Once it is fixed, this following property and connection can be remvoed.
24+ */
25+ property Item __oldContents: null
26+ Connections {
27+ target: episodesPage.head
28+ onContentsChanged: {
29+ if (episodesPage.__oldContents) {
30+ episodesPage.__oldContents.parent = null;
31+ }
32+ episodesPage.__oldContents = episodesPage.head.contents;
33+ }
34+ }
35+
36 head.contents: Label {
37 text: title
38 anchors.fill: parent
39@@ -40,12 +56,51 @@
40 wrapMode: Text.WordWrap
41 }
42
43- head.actions: [
44- Action {
45- text: i18n.tr("Unsubscribe")
46- iconName: "delete"
47- onTriggered: {
48- PopupUtils.open(confirmDeleteDialog);
49+ state: "default"
50+ states: [
51+ PageHeadState {
52+ name: "default"
53+ head: episodesPage.head
54+ actions: [
55+ Action {
56+ text: i18n.tr("Unsubscribe")
57+ iconName: "delete"
58+ onTriggered: {
59+ PopupUtils.open(confirmDeleteDialog);
60+ }
61+ },
62+
63+ Action {
64+ iconName: "search"
65+ text: i18n.tr("Search Episode")
66+ onTriggered: {
67+ episodesPage.state = "search"
68+ searchField.forceActiveFocus()
69+ }
70+ }
71+ ]
72+ },
73+
74+ PageHeadState {
75+ name: "search"
76+ head: episodesPage.head
77+ backAction: Action {
78+ iconName: "back"
79+ text: i18n.tr("Back")
80+ onTriggered: {
81+ episodeList.forceActiveFocus()
82+ searchField.text = ""
83+ episodesPage.state = "default"
84+ }
85+ }
86+
87+ contents: TextField {
88+ id: searchField
89+ inputMethodHints: Qt.ImhNoPredictiveText
90+ placeholderText: i18n.tr("Search Episode...")
91+ anchors.left: parent ? parent.left : undefined
92+ anchors.right: parent ? parent.right : undefined
93+ anchors.rightMargin: units.gu(2)
94 }
95 }
96 ]
97@@ -83,6 +138,15 @@
98 }
99 }
100
101+ EmptyState {
102+ anchors.centerIn: parent
103+ anchors.verticalCenterOffset: Qt.inputMethod.visible ? units.gu(4) : 0
104+ visible: episodesPage.state === "search" && sortedEpisodeModel.count === 0
105+ iconName: "music-app-symbolic"
106+ title: i18n.tr("No Episodes found")
107+ subTitle: i18n.tr("No episodes found matching the search term.")
108+ }
109+
110 ListModel {
111 id: episodeModel
112 property string pid;
113@@ -90,12 +154,19 @@
114 property string image;
115 }
116
117+ SortFilterModel {
118+ id: sortedEpisodeModel
119+ model: episodeModel
120+ filter.property: "name"
121+ filter.pattern: RegExp(searchField.text, "gi")
122+ }
123+
124 ListView {
125 id: episodeList
126
127 clip: true
128 anchors.fill: parent
129- model: episodeModel
130+ model: sortedEpisodeModel
131
132 footer: Item {
133 width: parent.width
134
135=== modified file 'app/ui/PodcastsTab.qml'
136--- app/ui/PodcastsTab.qml 2015-01-27 02:23:48 +0000
137+++ app/ui/PodcastsTab.qml 2015-01-27 02:23:48 +0000
138@@ -32,18 +32,116 @@
139 title: i18n.tr("Podcasts")
140
141 property bool episodesUpdating: false;
142- property bool addPodcast: false;
143
144 page: Page {
145- head.actions: [
146- Action {
147- text: i18n.tr("Add Podcast")
148- iconName: "add"
149- visible: !addPodcast
150- onTriggered: {
151- addPodcast = true;
152- }
153- }
154+ id: podcastPage
155+
156+ /*
157+ #FIXME: The following lines of code is necessary due to a upstream bug
158+ in the SDK http://pad.lv/1400297. This bug is still present in the rtm.
159+ Once it is fixed, this following property and connection can be remvoed.
160+ */
161+ property Item __oldContents: null
162+ Connections {
163+ target: podcastPage.head
164+ onContentsChanged: {
165+ if (podcastPage.__oldContents) {
166+ podcastPage.__oldContents.parent = null;
167+ }
168+ podcastPage.__oldContents = podcastPage.head.contents;
169+ }
170+ }
171+
172+ state: "default"
173+ states: [
174+ PageHeadState {
175+ name: "default"
176+ head: podcastPage.head
177+ actions: [
178+ Action {
179+ text: i18n.tr("Add Podcast")
180+ iconName: "add"
181+ onTriggered: {
182+ podcastPage.state = "add"
183+ feedUrlField.forceActiveFocus()
184+ }
185+ },
186+
187+ Action {
188+ iconName: "search"
189+ text: i18n.tr("Search Podcast")
190+ onTriggered: {
191+ podcastPage.state = "search"
192+ searchField.forceActiveFocus()
193+ }
194+ }
195+ ]
196+ },
197+
198+ PageHeadState {
199+ name: "search"
200+ head: podcastPage.head
201+ backAction: Action {
202+ iconName: "back"
203+ text: i18n.tr("Back")
204+ onTriggered: {
205+ view.forceActiveFocus()
206+ searchField.text = ""
207+ podcastPage.state = "default"
208+ }
209+ }
210+
211+ contents: TextField {
212+ id: searchField
213+ inputMethodHints: Qt.ImhNoPredictiveText
214+ placeholderText: i18n.tr("Search Podcast...")
215+ anchors.left: parent ? parent.left : undefined
216+ anchors.right: parent ? parent.right : undefined
217+ anchors.rightMargin: units.gu(2)
218+ }
219+ },
220+
221+ PageHeadState {
222+ name: "add"
223+ head: podcastPage.head
224+ backAction: Action {
225+ iconName: "back"
226+ text: i18n.tr("Back")
227+ onTriggered: {
228+ view.forceActiveFocus()
229+ feedUrlField.text = ""
230+ podcastPage.state = "default"
231+ }
232+ }
233+
234+ actions: [
235+ Action {
236+ iconName: "ok"
237+ text: i18n.tr("Save Podcast")
238+ onTriggered: {
239+ view.forceActiveFocus()
240+ subscribeFromFeed(feedUrlField.text);
241+ feedUrlField.text = ""
242+ podcastPage.state = "default"
243+ }
244+ }
245+ ]
246+
247+ contents: TextField {
248+ id: feedUrlField
249+ inputMethodHints: Qt.ImhUrlCharactersOnly
250+ placeholderText: i18n.tr("Feed URL...")
251+ anchors.left: parent ? parent.left : undefined
252+ anchors.right: parent ? parent.right : undefined
253+ onAccepted: {
254+ view.forceActiveFocus()
255+ subscribeFromFeed(feedUrlField.text);
256+ feedUrlField.text = ""
257+ podcastPage.state = "default"
258+ }
259+ }
260+ }
261+
262 ]
263
264 onVisibleChanged: {
265@@ -67,16 +165,29 @@
266
267 EmptyState {
268 anchors.centerIn: parent
269- visible: view.model === podcastModel && podcastModel.count === 0
270+ anchors.verticalCenterOffset: Qt.inputMethod.visible ? units.gu(4) : 0
271+ visible: podcastModel.count === 0 || sortedPodcastModel.count === 0
272 iconName: "music-app-symbolic"
273- title: i18n.tr("No Podcast Subscriptions")
274- subTitle: i18n.tr("You haven't subscribed to any podcasts yet, visit the 'Search' page to add some.")
275+ title: podcastModel.count === 0 ? i18n.tr("No Podcast Subscriptions")
276+ : i18n.tr("No Podcasts found")
277+ subTitle: podcastModel.count === 0 ? i18n.tr("You haven't subscribed to any podcasts yet, visit the 'Search' page to add some.")
278+ : i18n.tr("No podcasts found matching the search term.")
279 }
280
281 ListModel {
282 id: podcastModel
283 }
284
285+ SortFilterModel {
286+ id: sortedPodcastModel
287+ model: podcastModel
288+ sort.property: "name"
289+ sort.order: Qt.AscendingOrder
290+ filter.property: "name"
291+ filter.pattern: RegExp(searchField.text, "gi")
292+ }
293+
294+
295 ListModel {
296 id: episodeModel
297 property string pid;
298@@ -88,7 +199,7 @@
299 id: view
300
301 clip: true
302- model: podcastModel
303+ model: sortedPodcastModel
304 anchors.fill: parent
305
306 footer: Item {
307@@ -118,6 +229,11 @@
308 }
309
310 onClicked: {
311+ if(podcastPage.state === "search") {
312+ view.forceActiveFocus()
313+ searchField.text = ""
314+ podcastPage.state = "default"
315+ }
316 mainStack.push(Qt.resolvedUrl("EpisodesPage.qml"), {"episodeName": model.name, "episodeId": model.id, "episodeArtist": model.artist, "episodeImage": model.image})
317 }
318
319@@ -178,86 +294,6 @@
320 onRefresh: updateEpisodes();
321 }
322 }
323-
324- Rectangle {
325- anchors.top: parent.top
326- anchors.topMargin: header.y + header.height
327- width: parent.width
328- height: addCol.height
329- opacity: addPodcast ? 1 : 0
330- color: Theme.palette.normal.background
331-
332- onOpacityChanged: {
333- visible = opacity != 0;
334- }
335-
336- onVisibleChanged: {
337- if (visible) {
338- addText.forceActiveFocus()
339- }
340- }
341-
342- Behavior on opacity {
343- UbuntuNumberAnimation {
344- duration: UbuntuAnimation.SlowDuration
345- }
346- }
347-
348- Column {
349- id: addCol
350- anchors.horizontalCenter: parent.horizontalCenter
351- spacing: units.gu(2)
352- width: parent.width - units.gu(4)
353- anchors.margins: units.gu(2)
354-
355- Item {
356- width: parent.width
357- height: units.gu(2)
358- }
359-
360- TextField {
361- id: addText
362- width: parent.width
363- inputMethodHints: Qt.ImhUrlCharactersOnly
364- placeholderText: i18n.tr("Feed URL...")
365- onAccepted: {
366- subscribeFromFeed(addText.text);
367- addPodcast = false;
368- addText.text = "";
369- }
370- }
371-
372- Row {
373- spacing: units.gu(2)
374- width: parent.width
375-
376- Button {
377- width: (parent.width - parent.spacing) / 2
378- text: i18n.tr("Cancel")
379- onClicked: {
380- addText.text = "";
381- addPodcast = false;
382- }
383- }
384-
385- Button {
386- width: (parent.width - parent.spacing) / 2
387- color: UbuntuColors.orange
388- text: i18n.tr("Add")
389- onClicked: {
390- subscribeFromFeed(addText.text);
391- addPodcast = false;
392- addText.text = "";
393- }
394- }
395- }
396-
397- Item {
398- width: parent.width
399- height: units.gu(2)
400- }
401- }
402- }
403 }
404
405 function refreshModel() {
406@@ -292,8 +328,8 @@
407 if (xhr.readyState === XMLHttpRequest.DONE) {
408 if (xhr.status < 200 || xhr.status > 299 || xhr.responseXML === null) {
409 PopupUtils.open(subscribeFailedDialog);
410- addText.text = feed;
411- addPodcast = true;
412+ feedUrlField.text = feed
413+ podcastPage.state = "add"
414 return;
415 }
416
417@@ -322,8 +358,8 @@
418 updateEpisodes();
419 } else {
420 PopupUtils.open(subscribeFailedDialog);
421- addText.text = feed;
422- addPodcast = true;
423+ feedUrlField.text = feed
424+ podcastPage.state = "add"
425 return;
426 }
427 }
428
429=== modified file 'po/com.mikeasoft.podbird.pot'
430--- po/com.mikeasoft.podbird.pot 2015-01-27 02:23:48 +0000
431+++ po/com.mikeasoft.podbird.pot 2015-01-27 02:23:48 +0000
432@@ -8,7 +8,7 @@
433 msgstr ""
434 "Project-Id-Version: \n"
435 "Report-Msgid-Bugs-To: \n"
436-"POT-Creation-Date: 2015-01-25 19:00+0100\n"
437+"POT-Creation-Date: 2015-01-27 03:21+0100\n"
438 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
439 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
440 "Language-Team: LANGUAGE <LL@li.org>\n"
441@@ -17,38 +17,59 @@
442 "Content-Type: text/plain; charset=CHARSET\n"
443 "Content-Transfer-Encoding: 8bit\n"
444
445-#: ../app/ui/EpisodesPage.qml:45
446+#: ../app/ui/EpisodesPage.qml:66
447 msgid "Unsubscribe"
448 msgstr ""
449
450-#: ../app/ui/EpisodesPage.qml:57
451+#: ../app/ui/EpisodesPage.qml:75
452+msgid "Search Episode"
453+msgstr ""
454+
455+#: ../app/ui/EpisodesPage.qml:89 ../app/ui/PodcastsTab.qml:86
456+#: ../app/ui/PodcastsTab.qml:109
457+msgid "Back"
458+msgstr ""
459+
460+#: ../app/ui/EpisodesPage.qml:100
461+msgid "Search Episode..."
462+msgstr ""
463+
464+#: ../app/ui/EpisodesPage.qml:112
465 msgid "Unsubscribe Confirmation"
466 msgstr ""
467
468-#: ../app/ui/EpisodesPage.qml:58
469+#: ../app/ui/EpisodesPage.qml:113
470 #, qt-format
471 msgid "Are you sure you want to unsubscribe from <b>%1</b>?"
472 msgstr ""
473
474-#: ../app/ui/EpisodesPage.qml:60
475+#: ../app/ui/EpisodesPage.qml:115
476 msgid "Yes"
477 msgstr ""
478
479-#: ../app/ui/EpisodesPage.qml:77
480+#: ../app/ui/EpisodesPage.qml:132
481 msgid "No"
482 msgstr ""
483
484-#: ../app/ui/EpisodesPage.qml:196
485+#: ../app/ui/EpisodesPage.qml:146
486+msgid "No Episodes found"
487+msgstr ""
488+
489+#: ../app/ui/EpisodesPage.qml:147
490+msgid "No episodes found matching the search term."
491+msgstr ""
492+
493+#: ../app/ui/EpisodesPage.qml:267
494 #, qt-format
495 msgid "%1h %2m"
496 msgstr ""
497
498-#: ../app/ui/EpisodesPage.qml:202
499+#: ../app/ui/EpisodesPage.qml:273
500 #, qt-format
501 msgid "%1h"
502 msgstr ""
503
504-#: ../app/ui/EpisodesPage.qml:207
505+#: ../app/ui/EpisodesPage.qml:278
506 #, c-format, qt-format
507 msgid "%1m"
508 msgstr ""
509@@ -57,42 +78,54 @@
510 msgid "Podcasts"
511 msgstr ""
512
513-#: ../app/ui/PodcastsTab.qml:40
514+#: ../app/ui/PodcastsTab.qml:62
515 msgid "Add Podcast"
516 msgstr ""
517
518-#: ../app/ui/PodcastsTab.qml:59
519+#: ../app/ui/PodcastsTab.qml:72
520+msgid "Search Podcast"
521+msgstr ""
522+
523+#: ../app/ui/PodcastsTab.qml:97
524+msgid "Search Podcast..."
525+msgstr ""
526+
527+#: ../app/ui/PodcastsTab.qml:120
528+msgid "Save Podcast"
529+msgstr ""
530+
531+#: ../app/ui/PodcastsTab.qml:133
532+msgid "Feed URL..."
533+msgstr ""
534+
535+#: ../app/ui/PodcastsTab.qml:157
536 msgid "Unable to subscribe"
537 msgstr ""
538
539-#: ../app/ui/PodcastsTab.qml:60
540+#: ../app/ui/PodcastsTab.qml:158
541 msgid "Please check the URL and try again"
542 msgstr ""
543
544-#: ../app/ui/PodcastsTab.qml:62
545+#: ../app/ui/PodcastsTab.qml:160
546 msgid "Close"
547 msgstr ""
548
549-#: ../app/ui/PodcastsTab.qml:72
550+#: ../app/ui/PodcastsTab.qml:171
551 msgid "No Podcast Subscriptions"
552 msgstr ""
553
554-#: ../app/ui/PodcastsTab.qml:73
555+#: ../app/ui/PodcastsTab.qml:172
556+msgid "No Podcasts found"
557+msgstr ""
558+
559+#: ../app/ui/PodcastsTab.qml:173
560 msgid ""
561 "You haven't subscribed to any podcasts yet, visit the 'Search' page to add "
562 "some."
563 msgstr ""
564
565-#: ../app/ui/PodcastsTab.qml:222
566-msgid "Feed URL..."
567-msgstr ""
568-
569-#: ../app/ui/PodcastsTab.qml:236
570-msgid "Cancel"
571-msgstr ""
572-
573-#: ../app/ui/PodcastsTab.qml:246
574-msgid "Add"
575+#: ../app/ui/PodcastsTab.qml:174
576+msgid "No podcasts found matching the search term."
577 msgstr ""
578
579 #: ../app/ui/SearchTab.qml:27
580@@ -107,6 +140,6 @@
581 msgid "Subscribe"
582 msgstr ""
583
584-#: /home/krnekhelesh/Documents/Ubuntu-Projects/MP-Reviews/builddir/build-new-listitem-actions-UbuntuSDK_for_armhf_GCC_ubuntu_sdk_14_10_utopic-Default/po/Podbird.desktop.in.h:1
585+#: /home/krnekhelesh/Documents/Ubuntu-Projects/MP-Reviews/builddir/build-filter-podcast-list-UbuntuSDK_for_armhf_GCC_ubuntu_sdk_14_10_utopic-Default/po/Podbird.desktop.in.h:1
586 msgid "Podbird"
587 msgstr ""

Subscribers

People subscribed via source and target branches