Merge lp:~nik90/podbird/downloaded-view-devel into lp:podbird/devel

Proposed by Nekhelesh Ramananthan
Status: Merged
Merged at revision: 107
Proposed branch: lp:~nik90/podbird/downloaded-view-devel
Merge into: lp:podbird/devel
Diff against target: 507 lines (+184/-104)
5 files modified
app/podbird.qml (+3/-19)
app/ui/EpisodesTab.qml (+149/-75)
app/ui/PodcastsTab.qml (+3/-3)
app/ui/SearchPage.qml (+4/-2)
app/ui/SettingsPage.qml (+25/-5)
To merge this branch: bzr merge lp:~nik90/podbird/downloaded-view-devel
Reviewer Review Type Date Requested Status
Podbird Developers Pending
Review via email: mp+258989@code.launchpad.net

Description of the change

This MP implements the following,
- Moved the settings option to set podcasts list/grid view to the settings page
- Added back the add new podcasts option as a header button rather than a dedicated tab since it rarely used once the user has added all podcasts he needs.
- Replaced What's New Tab with the Episodes Tab and then adds What's New view and Downloaded View as header sections.

To post a comment you must log in.

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-05-12 21:35:11 +0000
3+++ app/podbird.qml 2015-05-13 11:43:12 +0000
4@@ -55,7 +55,7 @@
5 // visible on application start.
6 function refreshModels() {
7 if (tabs.selectedTabIndex === 0) {
8- whatsNewTab.refreshModel()
9+ episodesTab.refreshModel()
10 } else if (tabs.selectedTabIndex === 1) {
11 podcastPage.item.refreshModel()
12 }
13@@ -239,9 +239,8 @@
14 }
15 }
16
17- WhatsNewTab {
18- id: whatsNewTab
19- objectName: "whatsNewTab"
20+ EpisodesTab {
21+ id: episodesTab
22 }
23
24 Tab {
25@@ -259,21 +258,6 @@
26 }
27
28 Tab {
29- id: searchTab
30-
31- title: i18n.tr("Add New Podcasts")
32-
33- // Dynamically load/unload the search tab as required
34- page: Loader {
35- parent: searchTab
36- anchors.left: parent.left
37- anchors.right: parent.right
38- anchors.bottom: parent.bottom
39- source: (tabs.selectedTab === searchTab) ? Qt.resolvedUrl("ui/SearchPage.qml") : ""
40- }
41- }
42-
43- Tab {
44 id: settingsTab
45
46 title: i18n.tr("Settings")
47
48=== renamed file 'app/ui/WhatsNewTab.qml' => 'app/ui/EpisodesTab.qml'
49--- app/ui/WhatsNewTab.qml 2015-05-02 09:24:19 +0000
50+++ app/ui/EpisodesTab.qml 2015-05-13 11:43:12 +0000
51@@ -12,7 +12,7 @@
52 Tab {
53 id: whatsNewTab
54
55- title: i18n.tr("What's New")
56+ title: i18n.tr("Episodes")
57
58 property var today: new Date()
59 property int dayToMs: 86400000
60@@ -20,33 +20,45 @@
61 property bool episodesUpdating: false
62
63 page: Page {
64- id: whatsNewPage
65+ id: episodesPage
66+
67+ flickable: null
68+
69+ head {
70+ sections {
71+ model: [i18n.tr("What's New"), i18n.tr("Downloaded")]
72+ onSelectedIndexChanged: {
73+ refreshModel();
74+ }
75+ }
76+ }
77
78 state: "default"
79 states: [
80 PageHeadState {
81 name: "default"
82- head: whatsNewPage.head
83+ head: episodesPage.head
84 actions: [
85 Action {
86 iconName: "search"
87 text: i18n.tr("Search Episode")
88 onTriggered: {
89- whatsNewPage.state = "search"
90+ episodesPage.state = "search"
91 searchField.item.forceActiveFocus()
92 }
93 },
94
95 Action {
96 iconName: "select"
97+ visible: episodesPage.head.sections.selectedIndex === 0
98 text: i18n.tr("Mark all listened")
99 onTriggered: {
100 var db = Podcasts.init();
101 db.transaction(function (tx) {
102- for (var i=0; i<whatsNewModel.count; i++) {
103- tx.executeSql("UPDATE Episode SET listened=1 WHERE guid=?", [whatsNewModel.get(i).guid]);
104+ for (var i=0; i<episodesModel.count; i++) {
105+ tx.executeSql("UPDATE Episode SET listened=1 WHERE guid=?", [episodesModel.get(i).guid]);
106 }
107- whatsNewModel.clear()
108+ episodesModel.clear()
109 });
110 }
111 },
112@@ -54,17 +66,37 @@
113 Action {
114 iconName: "save"
115 text: i18n.tr("Download all")
116- onTriggered: {
117- var db = Podcasts.init();
118- db.transaction(function (tx) {
119- for (var i=0; i<whatsNewModel.count; i++) {
120- if (!whatsNewModel.get(i).downloadedfile) {
121- whatsNewModel.setProperty(i, "queued", 1)
122- tx.executeSql("UPDATE Episode SET queued=1 WHERE guid = ?", [whatsNewModel.get(i).guid]);
123- downloader.addDownload(whatsNewModel.get(i).guid, whatsNewModel.get(i).audiourl);
124- }
125- }
126- });
127+ visible: episodesPage.head.sections.selectedIndex === 0
128+ onTriggered: {
129+ var db = Podcasts.init();
130+ db.transaction(function (tx) {
131+ for (var i=0; i<episodesModel.count; i++) {
132+ if (!episodesModel.get(i).downloadedfile) {
133+ episodesModel.setProperty(i, "queued", 1)
134+ tx.executeSql("UPDATE Episode SET queued=1 WHERE guid = ?", [episodesModel.get(i).guid]);
135+ downloader.addDownload(episodesModel.get(i).guid, episodesModel.get(i).audiourl);
136+ }
137+ }
138+ });
139+ }
140+ },
141+
142+ Action {
143+ iconName: "delete"
144+ text: i18n.tr("Delete all")
145+ visible: episodesPage.head.sections.selectedIndex === 1
146+ onTriggered: {
147+ var db = Podcasts.init();
148+ db.transaction(function (tx) {
149+ for (var i=0; i<episodesModel.count; i++) {
150+ if (episodesModel.get(i).downloadedfile) {
151+ fileManager.deleteFile(episodesModel.get(i).downloadedfile);
152+ tx.executeSql("UPDATE Episode SET downloadedfile = NULL WHERE guid = ?", [episodesModel.get(i).guid]);
153+ episodesModel.setProperty(i, "downloadedfile", "")
154+ }
155+ }
156+ });
157+ refreshModel();
158 }
159 }
160 ]
161@@ -72,19 +104,19 @@
162
163 PageHeadState {
164 name: "search"
165- head: whatsNewPage.head
166+ head: episodesPage.head
167 backAction: Action {
168 iconName: "back"
169 text: i18n.tr("Back")
170 onTriggered: {
171 episodeList.forceActiveFocus()
172- whatsNewPage.state = "default"
173+ episodesPage.state = "default"
174 }
175 }
176
177 contents: Loader {
178 id: searchField
179- sourceComponent: whatsNewPage.state === "search" ? searchFieldComponent : undefined
180+ sourceComponent: episodesPage.state === "search" ? searchFieldComponent : undefined
181 anchors.left: parent ? parent.left : undefined
182 anchors.right: parent ? parent.right : undefined
183 anchors.rightMargin: units.gu(2)
184@@ -111,7 +143,7 @@
185 verticalCenterOffset: Qt.inputMethod.visible ? units.gu(4) : 0
186 }
187
188- sourceComponent: whatsNewModel.count === 0 || sortedEpisodeModel.count === 0 ? emptyStateComponent : undefined
189+ sourceComponent: episodesModel.count === 0 || sortedEpisodeModel.count === 0 ? emptyStateComponent : undefined
190 }
191
192 Component {
193@@ -119,21 +151,21 @@
194 EmptyState {
195 iconHeight: units.gu(12)
196 iconWidth: units.gu(22)
197- iconSource: whatsNewModel.count === 0 ? Qt.resolvedUrl("../graphics/owlSearch.svg") : Qt.resolvedUrl("../graphics/notFound.svg")
198- title: whatsNewModel.count === 0 ? i18n.tr("No New Episodes") : i18n.tr("No Episodes Found")
199- subTitle: whatsNewModel.count === 0 ? i18n.tr("No more episodes to listen to!") : i18n.tr("No Episodes found matching the search term.")
200+ iconSource: episodesModel.count === 0 ? Qt.resolvedUrl("../graphics/owlSearch.svg") : Qt.resolvedUrl("../graphics/notFound.svg")
201+ title: episodesModel.count === 0 ? episodesPage.head.sections.selectedIndex === 0 ? i18n.tr("No New Episodes") : i18n.tr("No Downloaded Episodes") : i18n.tr("No Episodes Found")
202+ subTitle: episodesModel.count === 0 ? episodesPage.head.sections.selectedIndex === 0 ? i18n.tr("No more episodes to listen to!") : i18n.tr("No episodes have been downloaded") : i18n.tr("No Episodes found matching the search term.")
203 }
204 }
205
206 ListModel {
207- id: whatsNewModel
208+ id: episodesModel
209 }
210
211 SortFilterModel {
212 id: sortedEpisodeModel
213- model: whatsNewModel
214+ model: episodesModel
215 filter.property: "name"
216- filter.pattern: whatsNewPage.state === "search" && searchField.status == Loader.Ready ? RegExp(searchField.item.text, "gi")
217+ filter.pattern: episodesPage.state === "search" && searchField.status == Loader.Ready ? RegExp(searchField.item.text, "gi")
218 : RegExp("", "gi")
219 }
220
221@@ -152,15 +184,15 @@
222 db.transaction(function (tx) {
223 /*
224 If tempGuid is NULL, then the episode currently being downloaded is not found within
225- this podcast. On the other hand, if it is within this podcast, then update the whatsNewModel
226+ this podcast. On the other hand, if it is within this podcast, then update the episodesModel
227 with the downloadedfile location we just received from the downloader.
228 */
229 if (tempGuid != "NULL") {
230 var rs2 = tx.executeSql("SELECT downloadedfile FROM Episode WHERE guid=?", [tempGuid]);
231- for (var i=0; i<whatsNewModel.count; i++) {
232- if (whatsNewModel.get(i).guid == tempGuid) {
233+ for (var i=0; i<episodesModel.count; i++) {
234+ if (episodesModel.get(i).guid == tempGuid) {
235 console.log("[LOG]: Setting episode download URL to " + rs2.rows.item(0).downloadedfile)
236- whatsNewModel.setProperty(i, "downloadedfile", rs2.rows.item(0).downloadedfile)
237+ episodesModel.setProperty(i, "downloadedfile", rs2.rows.item(0).downloadedfile)
238 break
239 }
240 }
241@@ -192,6 +224,7 @@
242 flickDeceleration = flickDeceleration * scaleFactor;
243 }
244
245+ clip: true
246 anchors.fill: parent
247 model: sortedEpisodeModel
248
249@@ -201,7 +234,7 @@
250 section.delegate: Rectangle {
251 width: parent.width
252 color: "Transparent"
253- height: header.implicitHeight + units.gu(2)
254+ height: header.text !== "" ? header.implicitHeight + units.gu(2) : units.gu(0)
255 Label {
256 id: header
257 anchors {
258@@ -222,6 +255,9 @@
259
260 else if (section === "Older")
261 return i18n.tr("Older")
262+
263+ else
264+ return ""
265 }
266 }
267 }
268@@ -262,24 +298,36 @@
269 db.transaction(function (tx) {
270 tx.executeSql("UPDATE Episode SET downloadedfile = NULL WHERE guid = ?", [model.guid]);
271 });
272- whatsNewModel.setProperty(model.index, "downloadedfile", "")
273+ episodesModel.setProperty(model.index, "downloadedfile", "")
274+ if (episodesPage.head.sections.selectedIndex === 1) {
275+ episodesModel.remove(model.index, 1)
276+ }
277 } else {
278 db.transaction(function (tx) {
279 tx.executeSql("UPDATE Episode SET queued=1 WHERE guid = ?", [model.guid]);
280 });
281- whatsNewModel.setProperty(model.index, "queued", 1)
282+ episodesModel.setProperty(model.index, "queued", 1)
283 downloader.addDownload(model.guid, model.audiourl);
284 }
285 }
286 },
287
288 Action {
289- iconName: "select"
290+ iconName: model.listened ? "view-collapse" : "select"
291 onTriggered: {
292 var db = Podcasts.init();
293 db.transaction(function (tx) {
294- tx.executeSql("UPDATE Episode SET listened=1 WHERE guid=?", [model.guid])
295- whatsNewModel.remove(model.index, 1)
296+ if (model.listened) {
297+ tx.executeSql("UPDATE Episode SET listened=0 WHERE guid=?", [model.guid])
298+ episodesModel.setProperty(model.index, "listened", 0)
299+ }
300+ else {
301+ tx.executeSql("UPDATE Episode SET listened=1 WHERE guid=?", [model.guid])
302+ episodesModel.setProperty(model.index, "listened", 1)
303+ if (episodesPage.head.sections.selectedIndex === 0) {
304+ episodesModel.remove(model.index, 1)
305+ }
306+ }
307 });
308 }
309 },
310@@ -331,44 +379,70 @@
311 }
312
313 function refreshModel() {
314- var today = new Date()
315- var dayToMs = 86400000; //1 * 24 * 60 * 60 * 1000
316- var i, j, episode, diff
317- var todayCount, yesterdayCount
318-
319- whatsNewModel.clear()
320- todayCount = 0
321- yesterdayCount = 0
322-
323+ var i, j, episode
324 var db = Podcasts.init()
325- db.transaction(function (tx) {
326- var rs = tx.executeSql("SELECT rowid, * FROM Podcast ORDER BY name ASC");
327- for (i=0; i < rs.rows.length; i++) {
328- var podcast = rs.rows.item(i);
329- var rs2 = tx.executeSql("SELECT rowid, * FROM Episode WHERE podcast=? ORDER BY published DESC", [rs.rows.item(i).rowid]);
330- for (j=0; j < rs2.rows.length; j++) {
331- episode = rs2.rows.item(j)
332- diff = Math.floor((today - episode.published)/dayToMs)
333- if (diff < 7 && !episode.listened) {
334- if (diff < 1) {
335- whatsNewModel.insert(todayCount, {"guid" : episode.guid, "listened" : episode.listened, "published": episode.published, "name" : episode.name, "description" : episode.description, "duration" : episode.duration, "position" : episode.position, "downloadedfile" : episode.downloadedfile, "image" : podcast.image, "artist" : podcast.artist, "audiourl" : episode.audiourl, "queued": episode.queued, "diff": "Today"})
336- todayCount++;
337- } else if (diff < 2) {
338- whatsNewModel.insert(todayCount + yesterdayCount, {"guid" : episode.guid, "listened" : episode.listened, "published": episode.published, "name" : episode.name, "description" : episode.description, "duration" : episode.duration, "position" : episode.position, "downloadedfile" : episode.downloadedfile, "image" : podcast.image, "artist" : podcast.artist, "audiourl" : episode.audiourl, "queued": episode.queued, "diff": "Yesterday"})
339- yesterdayCount++;
340- } else {
341- whatsNewModel.append({"guid" : episode.guid, "listened" : episode.listened, "published": episode.published, "name" : episode.name, "description" : episode.description, "duration" : episode.duration, "position" : episode.position, "downloadedfile" : episode.downloadedfile, "image" : podcast.image, "artist" : podcast.artist, "audiourl" : episode.audiourl, "queued": episode.queued, "diff": "Older"})
342- }
343- } else if (diff >= 7){
344- break
345- }
346- }
347-
348- if (podcast.lastupdate === null && !episodesUpdating) {
349- updateEpisodesDatabase();
350- }
351- }
352- });
353+
354+ episodesModel.clear()
355+
356+ // Episode Model for the what's new view
357+ if (episodesPage.head.sections.selectedIndex === 0) {
358+ var today = new Date()
359+ var dayToMs = 86400000; //1 * 24 * 60 * 60 * 1000
360+ var todayCount, yesterdayCount, diff
361+
362+ todayCount = 0
363+ yesterdayCount = 0
364+
365+ db.transaction(function (tx) {
366+ var rs = tx.executeSql("SELECT rowid, * FROM Podcast ORDER BY name ASC");
367+ for (i=0; i < rs.rows.length; i++) {
368+ var podcast = rs.rows.item(i);
369+ var rs2 = tx.executeSql("SELECT rowid, * FROM Episode WHERE podcast=? ORDER BY published DESC", [rs.rows.item(i).rowid]);
370+ for (j=0; j < rs2.rows.length; j++) {
371+ episode = rs2.rows.item(j)
372+ diff = Math.floor((today - episode.published)/dayToMs)
373+ if (diff < 7 && !episode.listened) {
374+ if (diff < 1) {
375+ episodesModel.insert(todayCount, {"guid" : episode.guid, "listened" : episode.listened, "published": episode.published, "name" : episode.name, "description" : episode.description, "duration" : episode.duration, "position" : episode.position, "downloadedfile" : episode.downloadedfile, "image" : podcast.image, "artist" : podcast.artist, "audiourl" : episode.audiourl, "queued": episode.queued, "diff": "Today"})
376+ todayCount++;
377+ } else if (diff < 2) {
378+ episodesModel.insert(todayCount + yesterdayCount, {"guid" : episode.guid, "listened" : episode.listened, "published": episode.published, "name" : episode.name, "description" : episode.description, "duration" : episode.duration, "position" : episode.position, "downloadedfile" : episode.downloadedfile, "image" : podcast.image, "artist" : podcast.artist, "audiourl" : episode.audiourl, "queued": episode.queued, "diff": "Yesterday"})
379+ yesterdayCount++;
380+ } else {
381+ episodesModel.append({"guid" : episode.guid, "listened" : episode.listened, "published": episode.published, "name" : episode.name, "description" : episode.description, "duration" : episode.duration, "position" : episode.position, "downloadedfile" : episode.downloadedfile, "image" : podcast.image, "artist" : podcast.artist, "audiourl" : episode.audiourl, "queued": episode.queued, "diff": "Older"})
382+ }
383+ } else if (diff >= 7){
384+ break
385+ }
386+ }
387+
388+ if (podcast.lastupdate === null && !episodesUpdating) {
389+ updateEpisodesDatabase();
390+ }
391+ }
392+ });
393+ }
394+
395+ // Episode Model for the downloaded view
396+ else if (episodesPage.head.sections.selectedIndex === 1) {
397+ db.transaction(function (tx) {
398+ var rs = tx.executeSql("SELECT rowid, * FROM Podcast ORDER BY name ASC");
399+ for (i=0; i < rs.rows.length; i++) {
400+ var podcast = rs.rows.item(i);
401+ var rs2 = tx.executeSql("SELECT rowid, * FROM Episode WHERE podcast=? ORDER BY published DESC", [rs.rows.item(i).rowid]);
402+ for (j=0; j < rs2.rows.length; j++) {
403+ episode = rs2.rows.item(j)
404+ if (episode.downloadedfile) {
405+ episodesModel.append({"guid" : episode.guid, "listened" : episode.listened, "published": episode.published, "name" : episode.name, "description" : episode.description, "duration" : episode.duration, "position" : episode.position, "downloadedfile" : episode.downloadedfile, "image" : podcast.image, "artist" : podcast.artist, "audiourl" : episode.audiourl, "queued": episode.queued, "diff": "Null"})
406+ }
407+ }
408+
409+ if (podcast.lastupdate === null && !episodesUpdating) {
410+ updateEpisodesDatabase();
411+ }
412+ }
413+ });
414+ }
415
416 episodesUpdating = false;
417 }
418
419=== modified file 'app/ui/PodcastsTab.qml'
420--- app/ui/PodcastsTab.qml 2015-05-02 08:51:10 +0000
421+++ app/ui/PodcastsTab.qml 2015-05-13 11:43:12 +0000
422@@ -71,10 +71,10 @@
423 },
424
425 Action {
426- iconName: podbird.settings.showListView ? "view-grid-symbolic" : "view-list-symbolic"
427- text: podbird.settings.showListView ? i18n.tr("Grid View") : i18n.tr("List View")
428+ iconName: "add"
429+ text: i18n.tr("Add New Podcasts")
430 onTriggered: {
431- podbird.settings.showListView = !podbird.settings.showListView
432+ mainStack.push(Qt.resolvedUrl("SearchPage.qml"))
433 }
434 }
435 ]
436
437=== modified file 'app/ui/SearchPage.qml'
438--- app/ui/SearchPage.qml 2015-05-02 08:51:10 +0000
439+++ app/ui/SearchPage.qml 2015-05-13 11:43:12 +0000
440@@ -46,6 +46,8 @@
441 }
442 }
443
444+ title: i18n.tr("Add New Podcasts")
445+
446 state: "default"
447 states: [
448 PageHeadState {
449@@ -266,7 +268,7 @@
450 }
451 });
452 }
453- tabs.selectedTabIndex = 1;
454+ mainStack.pop();
455 }
456 }
457 ]
458@@ -393,7 +395,7 @@
459 Podcasts.subscribe(artist, name, feed, image);
460 imageDownloader.feed = feed;
461 imageDownloader.download(image);
462- tabs.selectedTabIndex = 1;
463+ mainStack.pop()
464 } else {
465 PopupUtils.open(subscribeFailedDialog);
466 searchPage.state = "add"
467
468=== modified file 'app/ui/SettingsPage.qml'
469--- app/ui/SettingsPage.qml 2015-05-12 21:35:11 +0000
470+++ app/ui/SettingsPage.qml 2015-05-13 11:43:12 +0000
471@@ -105,11 +105,31 @@
472 right: parent.right
473 }
474
475-// HeaderListItem {
476-// // TRANSLATORS: Shortened form of "Miscellaneous" which is shown to denote other setting options
477-// // that doesn't fit into any other category.
478-// title: i18n.tr("General Settings")
479-// }
480+ HeaderListItem {
481+ // TRANSLATORS: Shortened form of "Miscellaneous" which is shown to denote other setting options
482+ // that doesn't fit into any other category.
483+ title: i18n.tr("General Settings")
484+ }
485+
486+ ListItem {
487+ height: control.implicitHeight + units.gu(2)
488+ divider.visible: false
489+
490+ Label {
491+ id: contentLabel2
492+ anchors { left: parent.left; leftMargin: units.gu(2); right: control2.left; rightMargin: units.gu(1); verticalCenter: parent.verticalCenter }
493+ text: i18n.tr("Displays podcasts in a list view")
494+ }
495+
496+ Switch {
497+ id: control2
498+ anchors { right: parent.right; rightMargin: units.gu(2); verticalCenter: parent.verticalCenter }
499+ checked: podbird.settings.showListView
500+ onClicked: podbird.settings.showListView = checked
501+ }
502+
503+ onClicked: podbird.settings.showListView = !podbird.settings.showListView
504+ }
505
506 // ListItems.SingleValue {
507 // progression: true

Subscribers

People subscribed via source and target branches