Merge lp:~nik90/podbird/filter-podcasts into lp:podbird
- filter-podcasts
- Merge into trunk
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 |
Related bugs: |
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://
- 22. By Nekhelesh Ramananthan
-
Added episode filtering with empty state
Preview Diff
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 "" |
Looks good, thanks!