Merge lp:~nik90/podbird/devel-branch-sync-1 into lp:podbird

Proposed by Nekhelesh Ramananthan
Status: Merged
Approved by: Michael Sheldon
Approved revision: 185
Merged at revision: 151
Proposed branch: lp:~nik90/podbird/devel-branch-sync-1
Merge into: lp:podbird
Prerequisite: lp:~nik90/podbird/uc-1.3-migrate
Diff against target: 2425 lines (+920/-866)
14 files modified
app/components/HeaderListItem.qml (+2/-0)
app/components/SingleValueListItem.qml (+0/-42)
app/components/TabsList.qml (+0/-60)
app/podbird.qml (+4/-17)
app/podcasts.js (+37/-19)
app/themes/Dark.qml (+3/-3)
app/themes/Light.qml (+3/-3)
app/ui/EpisodesPage.qml (+91/-92)
app/ui/EpisodesTab.qml (+625/-0)
app/ui/PodcastsTab.qml (+6/-15)
app/ui/SearchPage.qml (+4/-14)
app/ui/SettingsPage.qml (+14/-0)
app/ui/WhatsNewTab.qml (+0/-488)
po/com.mikeasoft.podbird.pot (+131/-113)
To merge this branch: bzr merge lp:~nik90/podbird/devel-branch-sync-1
Reviewer Review Type Date Requested Status
Michael Sheldon Approve
Review via email: mp+289563@code.launchpad.net

Description of the change

- Added new navigation structure (combined Add new podcasts tab and podcasts tab), (replaced whatsnew tab with episodes tab)
- Fixed episode description not readable in dark theme
- Added ability to favourite episodes
- Removed zebra colouring as requested by design
- Implemented part of the new design given by kevin
- Renamed "WhatsNew" to "Recent"

@Mike, From our discussion on IRC, we agreed to keep the "Add new Podcasts" as a separate tab. However I implemented that change several commits later. So while it is not part of this MP, it has been included in devel-branch-sync-3 to avoid code conflict.

To post a comment you must log in.
Revision history for this message
Nekhelesh Ramananthan (nik90) wrote :

Note 1: In the code diff, it shows "SingleValueListItem.qml" file being removed and then readded. This is due to bzr merge from lp:podbird/devel. The only change is the addition of 2 lines,

+ divider.anchors.leftMargin: units.gu(2)
+ divider.anchors.rightMargin: units.gu(2)

Revision history for this message
Nekhelesh Ramananthan (nik90) wrote :

Note 2: Same case with TabsList.qml file. It shows it being removed and then readded. This is again due to bzr merge from lp:podbird/devel. The only change is the removal of "Add new podcasts" Tab and the replacement of "What's New" with "Episodes" tab.

As explained in the MP description, the "Add new podcasts" has been readded in a later commit in lp:podbird/devel and will be backported in the next MP.

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

See inline diff for fixes needed to the database upgrade cases

review: Needs Fixing
185. By Nekhelesh Ramananthan

Fixed db upgrade issue

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

Looks good :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'app/components/HeaderListItem.qml'
2--- app/components/HeaderListItem.qml 2016-02-20 11:01:25 +0000
3+++ app/components/HeaderListItem.qml 2016-03-28 22:37:57 +0000
4@@ -25,6 +25,8 @@
5 property alias title: headerText.title
6
7 height: headerText.height + divider.height
8+ divider.anchors.leftMargin: units.gu(2)
9+ divider.anchors.rightMargin: units.gu(2)
10
11 ListItemLayout {
12 id: headerText
13
14=== added file 'app/components/SingleValueListItem.qml'
15--- app/components/SingleValueListItem.qml 1970-01-01 00:00:00 +0000
16+++ app/components/SingleValueListItem.qml 2016-03-28 22:37:57 +0000
17@@ -0,0 +1,45 @@
18+/*
19+ * Copyright 2015-2016 Podbird Team
20+ *
21+ * This file is part of Podbird.
22+ *
23+ * Podbird is free software; you can redistribute it and/or modify
24+ * it under the terms of the GNU General Public License as published by
25+ * the Free Software Foundation; version 3.
26+ *
27+ * Podbird is distributed in the hope that it will be useful,
28+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
29+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30+ * GNU General Public License for more details.
31+ *
32+ * You should have received a copy of the GNU General Public License
33+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
34+ */
35+
36+import QtQuick 2.4
37+import Ubuntu.Components 1.3
38+
39+ListItem {
40+ id: customListItem
41+
42+ property alias title: customItemLayout.title
43+ property alias value: _value.text
44+
45+ divider.anchors.leftMargin: units.gu(2)
46+ divider.anchors.rightMargin: units.gu(2)
47+
48+ ListItemLayout {
49+ id: customItemLayout
50+
51+ title.text: " "
52+ title.color: podbird.appTheme.baseText
53+
54+ Label {
55+ id: _value
56+ color: podbird.appTheme.baseText
57+ SlotsLayout.position: SlotsLayout.Trailing;
58+ }
59+
60+ ProgressionSlot {}
61+ }
62+}
63
64=== removed file 'app/components/SingleValueListItem.qml'
65--- app/components/SingleValueListItem.qml 2016-02-25 11:09:44 +0000
66+++ app/components/SingleValueListItem.qml 1970-01-01 00:00:00 +0000
67@@ -1,42 +0,0 @@
68-/*
69- * Copyright 2015-2016 Podbird Team
70- *
71- * This file is part of Podbird.
72- *
73- * Podbird is free software; you can redistribute it and/or modify
74- * it under the terms of the GNU General Public License as published by
75- * the Free Software Foundation; version 3.
76- *
77- * Podbird is distributed in the hope that it will be useful,
78- * but WITHOUT ANY WARRANTY; without even the implied warranty of
79- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
80- * GNU General Public License for more details.
81- *
82- * You should have received a copy of the GNU General Public License
83- * along with this program. If not, see <http://www.gnu.org/licenses/>.
84- */
85-
86-import QtQuick 2.4
87-import Ubuntu.Components 1.3
88-
89-ListItem {
90- id: customListItem
91-
92- property alias title: customItemLayout.title
93- property alias value: _value.text
94-
95- ListItemLayout {
96- id: customItemLayout
97-
98- title.text: " "
99- title.color: podbird.appTheme.baseText
100-
101- Label {
102- id: _value
103- color: podbird.appTheme.baseText
104- SlotsLayout.position: SlotsLayout.Trailing;
105- }
106-
107- ProgressionSlot {}
108- }
109-}
110
111=== added file 'app/components/TabsList.qml'
112--- app/components/TabsList.qml 1970-01-01 00:00:00 +0000
113+++ app/components/TabsList.qml 2016-03-28 22:37:57 +0000
114@@ -0,0 +1,52 @@
115+/*
116+ * Copyright 2015-2016 Podbird Team
117+ *
118+ * This file is part of Podbird.
119+ *
120+ * Podbird is free software; you can redistribute it and/or modify
121+ * it under the terms of the GNU General Public License as published by
122+ * the Free Software Foundation; version 3.
123+ *
124+ * Podbird is distributed in the hope that it will be useful,
125+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
126+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
127+ * GNU General Public License for more details.
128+ *
129+ * You should have received a copy of the GNU General Public License
130+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
131+ */
132+
133+import QtQuick 2.4
134+import Ubuntu.Components 1.3
135+
136+ActionList {
137+ id: tabsList
138+
139+ property int currentTab: tabs.selectedTabIndex
140+
141+ children: [
142+ Action {
143+ text: i18n.tr("Episodes")
144+ visible: currentTab !== 0
145+ onTriggered: {
146+ tabs.selectedTabIndex = 0
147+ }
148+ },
149+
150+ Action {
151+ text: i18n.tr("Podcasts")
152+ visible: currentTab !== 1
153+ onTriggered: {
154+ tabs.selectedTabIndex = 1
155+ }
156+ },
157+
158+ Action {
159+ text: i18n.tr("Settings")
160+ visible: currentTab !== 2
161+ onTriggered: {
162+ tabs.selectedTabIndex = 2
163+ }
164+ }
165+ ]
166+}
167
168=== removed file 'app/components/TabsList.qml'
169--- app/components/TabsList.qml 2016-02-25 23:53:44 +0000
170+++ app/components/TabsList.qml 1970-01-01 00:00:00 +0000
171@@ -1,60 +0,0 @@
172-/*
173- * Copyright 2015-2016 Podbird Team
174- *
175- * This file is part of Podbird.
176- *
177- * Podbird is free software; you can redistribute it and/or modify
178- * it under the terms of the GNU General Public License as published by
179- * the Free Software Foundation; version 3.
180- *
181- * Podbird is distributed in the hope that it will be useful,
182- * but WITHOUT ANY WARRANTY; without even the implied warranty of
183- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
184- * GNU General Public License for more details.
185- *
186- * You should have received a copy of the GNU General Public License
187- * along with this program. If not, see <http://www.gnu.org/licenses/>.
188- */
189-
190-import QtQuick 2.4
191-import Ubuntu.Components 1.3
192-
193-ActionList {
194- id: tabsList
195-
196- property int currentTab: tabs.selectedTabIndex
197-
198- children: [
199- Action {
200- text: i18n.tr("What's New")
201- visible: currentTab !== 0
202- onTriggered: {
203- tabs.selectedTabIndex = 0
204- }
205- },
206-
207- Action {
208- text: i18n.tr("Podcasts")
209- visible: currentTab !== 1
210- onTriggered: {
211- tabs.selectedTabIndex = 1
212- }
213- },
214-
215- Action {
216- text: i18n.tr("Add New Podcasts")
217- visible: currentTab !== 2
218- onTriggered: {
219- tabs.selectedTabIndex = 2
220- }
221- },
222-
223- Action {
224- text: i18n.tr("Settings")
225- visible: currentTab !== 3
226- onTriggered: {
227- tabs.selectedTabIndex = 3
228- }
229- }
230- ]
231-}
232
233=== modified file 'app/podbird.qml'
234--- app/podbird.qml 2016-02-25 11:09:44 +0000
235+++ app/podbird.qml 2016-03-28 22:37:57 +0000
236@@ -56,7 +56,7 @@
237 // visible on application start.
238 function refreshModels() {
239 if (tabs.selectedTabIndex === 0) {
240- whatsNewTab.refreshModel()
241+ episodesTab.refreshModel()
242 } else if (tabs.selectedTabIndex === 1) {
243 podcastPage.item.refreshModel()
244 }
245@@ -246,9 +246,9 @@
246 }
247 }
248
249- WhatsNewTab {
250- id: whatsNewTab
251- objectName: "whatsNewTab"
252+ EpisodesTab {
253+ id: episodesTab
254+ objectName: "episodesTab"
255 }
256
257 Tab {
258@@ -264,19 +264,6 @@
259 }
260
261 Tab {
262- id: searchTab
263-
264- // Dynamically load/unload the search tab as required
265- page: Loader {
266- parent: searchTab
267- anchors.left: parent.left
268- anchors.right: parent.right
269- anchors.bottom: parent.bottom
270- source: (tabs.selectedTab === searchTab) ? Qt.resolvedUrl("ui/SearchPage.qml") : ""
271- }
272- }
273-
274- Tab {
275 id: settingsTab
276
277 // Dynamically load/unload the settings tab as required
278
279=== modified file 'app/podcasts.js'
280--- app/podcasts.js 2015-09-12 14:34:46 +0000
281+++ app/podcasts.js 2016-03-28 22:37:57 +0000
282@@ -21,18 +21,35 @@
283
284 db.transaction(function(tx) {
285 tx.executeSql('CREATE TABLE IF NOT EXISTS Podcast(artist TEXT, name TEXT, description TEXT, feed TEXT, image TEXT, lastupdate TIMESTAMP)');
286- tx.executeSql('CREATE TABLE IF NOT EXISTS Episode(guid TEXT, podcast INTEGER, name TEXT, subtitle TEXT, description TEXT, duration INTEGER, audiourl TEXT, downloadedfile TEXT, published TIMESTAMP, queued BOOLEAN, listened BOOLEAN, position INTEGER, FOREIGN KEY(podcast) REFERENCES Podcast(rowid))');
287+ tx.executeSql('CREATE TABLE IF NOT EXISTS Episode(guid TEXT, podcast INTEGER, name TEXT, subtitle TEXT, description TEXT, duration INTEGER, audiourl TEXT, downloadedfile TEXT, published TIMESTAMP, queued BOOLEAN, listened BOOLEAN, favourited BOOLEAN, position INTEGER, FOREIGN KEY(podcast) REFERENCES Podcast(rowid))');
288 });
289
290- /*
291- Schema Upgrade to v1.1 which adds a new queued boolean variable which is needed to track the queued status
292- of a episode properly.
293- */
294- if (db.version == "1.0") {
295- db.changeVersion("1.0", "1.1", function(tx) {
296- tx.executeSql('ALTER TABLE Episode ADD queued BOOLEAN');
297- tx.executeSql('UPDATE Episode SET queued=0');
298- });
299+ try {
300+ /*
301+ Schema Upgrade to v1.1 which adds a new queued boolean variable which is needed to track the queued status
302+ of a episode properly.
303+ */
304+ if (db.version === "1.0" || db.version === "") {
305+ console.log("Upgrading database from %1 -> v1.1".arg(db.version))
306+ db.changeVersion(db.version, "1.1", function(tx) {
307+ tx.executeSql('ALTER TABLE Episode ADD queued BOOLEAN');
308+ tx.executeSql('UPDATE Episode SET queued=0');
309+ });
310+ }
311+
312+ /*
313+ Schema Upgrade to v1.2 which adds a new favourited boolean variable which is needed to track the favourite status
314+ of an episode.
315+ */
316+ if (db.version === "1.1") {
317+ console.log("Upgrading database from %1 -> v1.2".arg(db.version))
318+ db.changeVersion("1.1", "1.2", function(tx) {
319+ tx.executeSql('ALTER TABLE Episode ADD favourited BOOLEAN');
320+ tx.executeSql('UPDATE Episode SET favourited=?', [false]);
321+ });
322+ }
323+ } catch(ex) {
324+ console.log(ex)
325 }
326
327 return db;
328@@ -110,15 +127,16 @@
329 db.transaction(function(tx2) {
330 var ers = tx2.executeSql("SELECT rowid FROM Episode WHERE guid=?", [track.guid]);
331 if (ers.rows.length === 0) {
332- tx2.executeSql("INSERT INTO Episode(podcast, name, description, audiourl, guid, listened, queued, duration, published) VALUES(?, ?, ? , ?, ?, ?, ?, ?, ?)", [pid,
333- track.name,
334- track.description,
335- track.audiourl,
336- track.guid,
337- false,
338- false,
339- track.duration,
340- track.published]);
341+ tx2.executeSql("INSERT INTO Episode(podcast, name, description, audiourl, guid, listened, queued, favourited, duration, published) VALUES(?, ?, ? , ?, ?, ?, ?, ?, ?, ?)", [pid,
342+ track.name,
343+ track.description,
344+ track.audiourl,
345+ track.guid,
346+ false,
347+ false,
348+ false,
349+ track.duration,
350+ track.published]);
351 }
352 });
353 }
354
355=== modified file 'app/themes/Dark.qml'
356--- app/themes/Dark.qml 2016-02-19 20:10:06 +0000
357+++ app/themes/Dark.qml 2016-03-28 22:37:57 +0000
358@@ -33,9 +33,9 @@
359 property color baseIcon: "White"
360
361 // Button Colors
362- property color positiveActionButton: UbuntuColors.green
363- property color negativeActionButton: UbuntuColors.red
364- property color neutralActionButton: UbuntuColors.coolGrey
365+ property color positiveActionButton: "#3EB34F" // UbuntuColors.green
366+ property color negativeActionButton: "#ED3146" // UbuntuColors.red
367+ property color neutralActionButton: "#5D5D5D" // UbuntuColors.coolGrey
368
369 // Bottom Player Bar Colors
370 property color bottomBarBackground: "#15141A"
371
372=== modified file 'app/themes/Light.qml'
373--- app/themes/Light.qml 2016-02-19 20:10:06 +0000
374+++ app/themes/Light.qml 2016-03-28 22:37:57 +0000
375@@ -33,9 +33,9 @@
376 property color baseIcon: UbuntuColors.darkGrey
377
378 // Button Colors
379- property color positiveActionButton: UbuntuColors.green
380- property color negativeActionButton: UbuntuColors.red
381- property color neutralActionButton: UbuntuColors.coolGrey
382+ property color positiveActionButton: "#3EB34F" // UbuntuColors.green
383+ property color negativeActionButton: "#ED3146" // UbuntuColors.red
384+ property color neutralActionButton: "#5D5D5D" // UbuntuColors.coolGrey
385
386 // Bottom Player Bar Colors
387 property color bottomBarBackground: "#323435"
388
389=== modified file 'app/ui/EpisodesPage.qml'
390--- app/ui/EpisodesPage.qml 2016-03-14 15:33:40 +0000
391+++ app/ui/EpisodesPage.qml 2016-03-28 22:37:57 +0000
392@@ -19,7 +19,6 @@
393 import QtQuick 2.4
394 import QtMultimedia 5.4
395 import Ubuntu.Components 1.3
396-import QtQuick.Layouts 1.1
397 import QtQuick.LocalStorage 2.0
398 import Ubuntu.DownloadManager 0.1
399 import Ubuntu.Components.Popups 1.3
400@@ -105,17 +104,14 @@
401 name: "search"
402 head: episodesPage.head
403
404- actions: [
405- Action {
406- iconName: "edit-clear"
407- text: i18n.tr("Cancel")
408- onTriggered: {
409- episodeList.forceActiveFocus()
410- episodesPage.state = "default"
411- episodeList.positionViewAtBeginning()
412- }
413+ backAction: Action {
414+ iconName: "back"
415+ onTriggered: {
416+ episodeList.forceActiveFocus()
417+ episodesPage.state = "default"
418+ episodeList.positionViewAtBeginning()
419 }
420- ]
421+ }
422
423 contents: Loader {
424 id: searchField
425@@ -229,7 +225,6 @@
426 Label {
427 width: parent.width
428 wrapMode: Text.WordWrap
429- color: UbuntuColors.darkGrey
430 linkColor: "Blue"
431 text: dialogInternal.description
432 onLinkActivated: Qt.openUrlExternally(link)
433@@ -281,7 +276,7 @@
434 : RegExp("", "gi")
435 }
436
437- UbuntuListView {
438+ ListView {
439 id: episodeList
440
441 Component.onCompleted: {
442@@ -294,76 +289,97 @@
443
444 anchors.fill: parent
445 model: sortedEpisodeModel
446- currentIndex: -1
447 clip: true
448
449 header: Column {
450- height: blurredBackground.height + modeTabs.height + units.gu(2)
451- spacing: units.gu(2)
452- BlurredBackground {
453- id: blurredBackground
454+ height: coverArtContainer.height + modeTabs.height + units.gu(2)
455+ Item {
456+ id: coverArtContainer
457
458- art: episodeImage
459- width: parent.width
460+ width: episodesPage.width
461 visible: episodesPage.state !== "search" && sortedEpisodeModel.count !== 0
462- height: episodesPage.state !== "search" && sortedEpisodeModel.count !== 0 ? cover.height + units.gu(4) : 0
463- backgroundStrength: podbird.settings.themeName === "Light.qml" ? 0.3 : 0.6
464+ height: episodesPage.state !== "search" && sortedEpisodeModel.count !== 0 ? cover.height + units.gu(6) : 0
465
466 Image {
467 id:cover
468- width: units.gu(12)
469+ width: units.gu(18)
470 height: width
471 sourceSize.height: width
472 sourceSize.width: width
473 source: episodeImage
474 asynchronous: true
475 anchors {
476- left: parent.left
477+ horizontalCenter: parent.horizontalCenter
478 top: parent.top
479 margins: units.gu(2)
480 }
481 }
482
483- Column {
484- id: podcastTitle
485-
486- anchors {
487- left: cover.right
488- right: parent.right
489- bottom: parent.bottom
490- margins: units.gu(2)
491- }
492-
493- Label {
494- text: episodeName
495- width: parent.width
496- wrapMode: Text.WordWrap
497- maximumLineCount: 2
498- elide: Text.ElideRight
499- color: podbird.appTheme.baseText
500- }
501-
502- Label {
503- text: i18n.tr("%1 episode", "%1 episodes", episodeList.count).arg(episodeList.count)
504- width: parent.width
505- elide: Text.ElideRight
506- textSize: Label.XSmall
507- color: podbird.appTheme.baseText
508- }
509+ Label {
510+ text: episodeName
511+ width: parent.width
512+ wrapMode: Text.WordWrap
513+ horizontalAlignment: Text.AlignHCenter
514+ maximumLineCount: 2
515+ elide: Text.ElideRight
516+ color: podbird.appTheme.baseText
517+ anchors.top: cover.bottom
518+ anchors.topMargin: units.gu(2)
519 }
520 }
521
522 Item {
523+ width: parent.width
524+ height: units.gu(2)
525+ }
526+
527+ Item {
528 id: modeTabs
529- height: unheardTab.implicitHeight + units.gu(2)
530+ height: unheardTab.implicitHeight + units.gu(2.25)
531 width: episodesPage.width
532
533+ Rectangle {
534+ id: sliderContainer
535+ anchors.top: unheardTab.bottom
536+ anchors.topMargin: units.gu(1)
537+ anchors.left: parent.left
538+ anchors.right: parent.right
539+ anchors.margins: units.gu(2)
540+ height: units.gu(0.25)
541+ radius: width/3
542+ color: UbuntuColors.lightGrey
543+ }
544+
545+ Rectangle {
546+ id: slider
547+ anchors.top: unheardTab.bottom
548+ anchors.topMargin: units.gu(1)
549+ height: units.gu(0.25)
550+ radius: width/3
551+ width: sliderContainer.width/3
552+ color: podbird.appTheme.focusText
553+ x: {
554+ if (episodesPage.mode === "unheard")
555+ return units.gu(2)
556+ else if (episodesPage.mode === "listened")
557+ return width + units.gu(2)
558+ else
559+ return 2 * width + units.gu(2)
560+ }
561+
562+ Behavior on x {
563+ UbuntuNumberAnimation {}
564+ }
565+ }
566+
567 Label {
568 id: unheardTab
569- textSize: Label.Large
570 text: i18n.tr("Unheard")
571 anchors.left: parent.left
572 anchors.leftMargin: units.gu(2)
573+ width: sliderContainer.width/3
574+ horizontalAlignment: Text.AlignHCenter
575+ font.weight: Font.DemiBold
576 color: episodesPage.mode == "unheard" ? podbird.appTheme.focusText : podbird.appTheme.baseText
577
578 AbstractButton {
579@@ -372,23 +388,13 @@
580 }
581 }
582
583- Rectangle {
584- anchors.top: unheardTab.bottom
585- anchors.topMargin: units.gu(1)
586- anchors.horizontalCenter: unheardTab.horizontalCenter
587- height: units.gu(0.25)
588- width: unheardTab.width
589- radius: width/3
590- color: podbird.appTheme.focusText
591- visible: episodesPage.mode == "unheard"
592- }
593-
594 Label {
595 id: listenedTab
596 anchors.left: unheardTab.right
597- anchors.leftMargin: (parent.width - unheardTab.width - listenedTab.width - downloadedTab.width - unheardTab.anchors.leftMargin - downloadedTab.anchors.rightMargin) / 2.0
598- textSize: Label.Large
599 text: i18n.tr("Listened")
600+ width: sliderContainer.width/3
601+ font.weight: Font.DemiBold
602+ horizontalAlignment: Text.AlignHCenter
603 color: episodesPage.mode == "listened" ? podbird.appTheme.focusText : podbird.appTheme.baseText
604
605 AbstractButton {
606@@ -397,22 +403,13 @@
607 }
608 }
609
610- Rectangle {
611- anchors.top: listenedTab.bottom
612- anchors.topMargin: units.gu(1)
613- anchors.horizontalCenter: listenedTab.horizontalCenter
614- height: units.gu(0.25)
615- width: listenedTab.width
616- radius: width/3
617- color: podbird.appTheme.focusText
618- visible: episodesPage.mode == "listened"
619- }
620-
621 Label {
622 id: downloadedTab
623 anchors.right: parent.right
624 anchors.rightMargin: units.gu(2)
625- textSize: Label.Large
626+ width: sliderContainer.width/3
627+ font.weight: Font.DemiBold
628+ horizontalAlignment: Text.AlignHCenter
629 text: i18n.tr("Downloaded")
630 color: episodesPage.mode == "downloaded" ? podbird.appTheme.focusText : podbird.appTheme.baseText
631
632@@ -421,17 +418,6 @@
633 onClicked: episodesPage.mode = "downloaded"
634 }
635 }
636-
637- Rectangle {
638- anchors.top: downloadedTab.bottom
639- anchors.topMargin: units.gu(1)
640- anchors.horizontalCenter: downloadedTab.horizontalCenter
641- height: units.gu(0.25)
642- width: downloadedTab.width
643- radius: width/3
644- color: podbird.appTheme.focusText
645- visible: episodesPage.mode == "downloaded"
646- }
647 }
648
649 }
650@@ -445,9 +431,8 @@
651 id: listItem
652
653 divider.visible: false
654- highlightColor: "Transparent"
655+ highlightColor: podbird.appTheme.hightlightListView
656 height: visible ? listItemLayout.height + progressBarLoader.height + units.gu(1) : 0
657- color: index % 2 === 0 ? podbird.appTheme.hightlightListView : "Transparent"
658
659 visible: episodesPage.mode == "listened" ? model.listened
660 : (episodesPage.mode == "unheard" ? !model.listened
661@@ -526,6 +511,20 @@
662 },
663
664 Action {
665+ iconName: model.favourited ? "unlike" : "like"
666+ onTriggered: {
667+ var db = Podcasts.init();
668+ db.transaction(function (tx) {
669+ if (model.favourited)
670+ tx.executeSql("UPDATE Episode SET favourited=0 WHERE guid=?", [model.guid])
671+ else
672+ tx.executeSql("UPDATE Episode SET favourited=1 WHERE guid=?", [model.guid])
673+ refreshModel();
674+ });
675+ }
676+ },
677+
678+ Action {
679 iconName: "info"
680 onTriggered: {
681 var popup = PopupUtils.open(episodeDescriptionDialog, episodesPage);
682@@ -584,10 +583,10 @@
683 for(i = 0; i < rs.rows.length; i++) {
684 episode = rs.rows.item(i);
685 if (!episode.listened) {
686- episodeModel.insert(newCount, {"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" : img, "artist" : artist, "audiourl" : episode.audiourl, "queued": episode.queued});
687+ episodeModel.insert(newCount, {"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" : img, "artist" : artist, "audiourl" : episode.audiourl, "queued": episode.queued, "favourited": episode.favourited});
688 newCount++;
689 } else {
690- episodeModel.insert(i,{"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" : img, "artist" : artist, "audiourl" : episode.audiourl, "queued": episode.queued});
691+ episodeModel.insert(i,{"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" : img, "artist" : artist, "audiourl" : episode.audiourl, "queued": episode.queued, "favourited": episode.favourited});
692 }
693 }
694 });
695
696=== added file 'app/ui/EpisodesTab.qml'
697--- app/ui/EpisodesTab.qml 1970-01-01 00:00:00 +0000
698+++ app/ui/EpisodesTab.qml 2016-03-28 22:37:57 +0000
699@@ -0,0 +1,625 @@
700+/*
701+ * Copyright 2015-2016 Michael Sheldon <mike@mikeasoft.com>
702+ *
703+ * This file is part of Podbird.
704+ *
705+ * Podbird is free software; you can redistribute it and/or modify
706+ * it under the terms of the GNU General Public License as published by
707+ * the Free Software Foundation; version 3.
708+ *
709+ * Podbird is distributed in the hope that it will be useful,
710+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
711+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
712+ * GNU General Public License for more details.
713+ *
714+ * You should have received a copy of the GNU General Public License
715+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
716+ */
717+
718+import QtQuick 2.4
719+import QtMultimedia 5.4
720+import Ubuntu.Components 1.3
721+import QtQuick.LocalStorage 2.0
722+import Ubuntu.DownloadManager 0.1
723+import Ubuntu.Components.Popups 1.3
724+import "../podcasts.js" as Podcasts
725+import "../components"
726+
727+Tab {
728+ id: episodesTab
729+
730+ property var today: new Date()
731+ property int dayToMs: 86400000
732+ property string tempGuid: "NULL"
733+ property bool episodesUpdating: false
734+
735+ TabsList {
736+ id: tabsList
737+ }
738+
739+ page: Page {
740+ id: episodesPage
741+
742+ header: standardHeader
743+
744+ PageHeader {
745+ id: standardHeader
746+ visible: episodesPage.header === standardHeader
747+ title: i18n.tr("Episodes")
748+
749+ StyleHints {
750+ backgroundColor: podbird.appTheme.background
751+ }
752+
753+ leadingActionBar {
754+ numberOfSlots: 0
755+ actions: tabsList.actions
756+ }
757+
758+ trailingActionBar.actions: [
759+ Action {
760+ iconName: "search"
761+ text: i18n.tr("Search Episode")
762+ onTriggered: {
763+ episodesPage.header = searchHeader
764+ searchField.item.forceActiveFocus()
765+ }
766+ },
767+
768+ Action {
769+ iconName: "select"
770+ visible: episodesPageHeaderSections.selectedIndex === 0
771+ text: i18n.tr("Mark all listened")
772+ onTriggered: {
773+ var db = Podcasts.init();
774+ db.transaction(function (tx) {
775+ for (var i=0; i<episodesModel.count; i++) {
776+ tx.executeSql("UPDATE Episode SET listened=1 WHERE guid=?", [episodesModel.get(i).guid]);
777+ }
778+ episodesModel.clear()
779+ });
780+ }
781+ },
782+
783+ Action {
784+ iconName: "save"
785+ visible: episodesPageHeaderSections.selectedIndex === 0
786+ text: i18n.tr("Download all")
787+ onTriggered: {
788+ var db = Podcasts.init();
789+ db.transaction(function (tx) {
790+ for (var i=0; i<episodesModel.count; i++) {
791+ if (!episodesModel.get(i).downloadedfile) {
792+ episodesModel.setProperty(i, "queued", 1)
793+ tx.executeSql("UPDATE Episode SET queued=1 WHERE guid = ?", [episodesModel.get(i).guid]);
794+ downloader.addDownload(episodesModel.get(i).guid, episodesModel.get(i).audiourl);
795+ }
796+ }
797+ });
798+ }
799+ },
800+
801+ Action {
802+ iconName: "delete"
803+ text: i18n.tr("Delete all")
804+ visible: episodesPageHeaderSections.selectedIndex === 1
805+ onTriggered: {
806+ var db = Podcasts.init();
807+ db.transaction(function (tx) {
808+ for (var i=0; i<episodesModel.count; i++) {
809+ if (episodesModel.get(i).downloadedfile) {
810+ fileManager.deleteFile(episodesModel.get(i).downloadedfile);
811+ tx.executeSql("UPDATE Episode SET downloadedfile = NULL WHERE guid = ?", [episodesModel.get(i).guid]);
812+ episodesModel.setProperty(i, "downloadedfile", "")
813+ }
814+ }
815+ });
816+ refreshModel();
817+ }
818+ }
819+ ]
820+
821+ extension: Sections {
822+ id: episodesPageHeaderSections
823+
824+ anchors {
825+ left: parent.left
826+ leftMargin: units.gu(2)
827+ bottom: parent.bottom
828+ }
829+
830+ StyleHints {
831+ selectedSectionColor: podbird.appTheme.focusText
832+ }
833+
834+ model: [i18n.tr("Recent"), i18n.tr("Downloaded"), i18n.tr("Favourites")]
835+ onSelectedIndexChanged: {
836+ refreshModel();
837+ }
838+ }
839+ }
840+
841+ PageHeader {
842+ id: searchHeader
843+ visible: episodesPage.header === searchHeader
844+
845+ StyleHints {
846+ backgroundColor: podbird.appTheme.background
847+ }
848+
849+ contents: Loader {
850+ id: searchField
851+ sourceComponent: episodesPage.header === searchHeader ? searchFieldComponent : undefined
852+ anchors.left: parent ? parent.left : undefined
853+ anchors.right: parent ? parent.right : undefined
854+ anchors.verticalCenter: parent ? parent.verticalCenter : undefined
855+ }
856+
857+ leadingActionBar.actions: [
858+ Action {
859+ iconName: "back"
860+ onTriggered: {
861+ episodeList.forceActiveFocus()
862+ episodesPage.header = standardHeader
863+ }
864+ }
865+ ]
866+ }
867+
868+ Component {
869+ id: searchFieldComponent
870+ TextField {
871+ inputMethodHints: Qt.ImhNoPredictiveText
872+ placeholderText: i18n.tr("Search episode")
873+ }
874+ }
875+
876+ Loader {
877+ id: emptyState
878+
879+ anchors {
880+ left: parent.left
881+ right: parent.right
882+ margins: units.gu(2)
883+ verticalCenter: parent.verticalCenter
884+ verticalCenterOffset: Qt.inputMethod.visible ? units.gu(4) : 0
885+ }
886+
887+ sourceComponent: episodesModel.count === 0 || sortedEpisodeModel.count === 0 ? emptyStateComponent : undefined
888+ }
889+
890+ Component {
891+ id: emptyStateComponent
892+ EmptyState {
893+ icon.source: episodesModel.count === 0 ? Qt.resolvedUrl("../graphics/owlSearch.svg") : Qt.resolvedUrl("../graphics/notFound.svg")
894+ title: {
895+ if (episodesModel.count === 0 && episodesPage.header === standardHeader) {
896+ if (episodesPageHeaderSections.selectedIndex === 0)
897+ return i18n.tr("No New Episodes")
898+ else if (episodesPageHeaderSections.selectedIndex === 1)
899+ return i18n.tr("No Downloaded Episodes")
900+ else if (episodesPageHeaderSections.selectedIndex === 2)
901+ return i18n.tr("No Favourited Episodes")
902+ } else {
903+ return i18n.tr("No Episodes Found")
904+ }
905+ }
906+ subTitle: {
907+ if (episodesModel.count === 0 && episodesPage.header === standardHeader) {
908+ if (episodesPageHeaderSections.selectedIndex === 0)
909+ return i18n.tr("No more episodes to listen to!")
910+ else if (episodesPageHeaderSections.selectedIndex === 1)
911+ return i18n.tr("No episodes have been downloaded for offline listening")
912+ else if (episodesPageHeaderSections.selectedIndex === 2)
913+ return i18n.tr("No episodes have been favourited.")
914+ } else {
915+ return i18n.tr("No Episodes found matching the search term.")
916+ }
917+ }
918+ }
919+ }
920+
921+ ListModel {
922+ id: episodesModel
923+ }
924+
925+ SortFilterModel {
926+ id: sortedEpisodeModel
927+ model: episodesModel
928+ filter.property: "name"
929+ filter.pattern: episodesPage.header === searchHeader && searchField.status == Loader.Ready ? RegExp(searchField.item.text, "gi")
930+ : RegExp("", "gi")
931+ }
932+
933+ onVisibleChanged: {
934+ if (visible) {
935+ refreshModel()
936+ if (downloader.downloadingGuid != "")
937+ tempGuid = downloader.downloadingGuid
938+ } else {
939+ episodesPage.header = standardHeader
940+ }
941+ }
942+
943+ Connections {
944+ target: downloader
945+ onDownloadingGuidChanged: {
946+ var db = Podcasts.init();
947+ db.transaction(function (tx) {
948+ /*
949+ If tempGuid is NULL, then the episode currently being downloaded is not found within
950+ this podcast. On the other hand, if it is within this podcast, then update the episodesModel
951+ with the downloadedfile location we just received from the downloader.
952+ */
953+ if (tempGuid != "NULL") {
954+ var rs2 = tx.executeSql("SELECT downloadedfile FROM Episode WHERE guid=?", [tempGuid]);
955+ for (var i=0; i<episodesModel.count; i++) {
956+ if (episodesModel.get(i).guid == tempGuid) {
957+ console.log("[LOG]: Setting episode download URL to " + rs2.rows.item(0).downloadedfile)
958+ episodesModel.setProperty(i, "downloadedfile", rs2.rows.item(0).downloadedfile)
959+ episodesModel.setProperty(i, "queued", 0)
960+ break
961+ }
962+ }
963+ tempGuid = "NULL"
964+ }
965+
966+ /*
967+ Here it is checked if the currently downloaded episode belongs to the podcast
968+ page being currently displayed. If it is, then the downloaded episode guid is
969+ stored in the tempGuid variable to track it.
970+ */
971+ var rs = tx.executeSql("SELECT podcast FROM Episode WHERE guid=?", [downloader.downloadingGuid]);
972+
973+ if (downloader.downloadingGuid != "" && tempGuid == "NULL") {
974+ tempGuid = downloader.downloadingGuid
975+ }
976+ });
977+ refreshModel();
978+ }
979+ }
980+
981+ /*
982+ Note (nik90): After the upgrade to Ubuntu.Components 1.2, it seems the new listitems don't have their trailing
983+ action width clamped. As a result when the list item expands and the user swipes left, it leads to a rather huge
984+ trailing edge action. This has been reported upstream at http://pad.lv/1465582. Until this is fixed, the
985+ episode description is shown in a dialog.
986+ */
987+ Component {
988+ id: episodeDescriptionDialog
989+ Dialog {
990+ id: dialogInternal
991+
992+ property string description
993+
994+ title: "<b>%1</b>".arg(i18n.tr("Episode Description"))
995+
996+ Label {
997+ width: parent.width
998+ wrapMode: Text.WordWrap
999+ linkColor: "Blue"
1000+ text: dialogInternal.description
1001+ onLinkActivated: Qt.openUrlExternally(link)
1002+ }
1003+
1004+ Button {
1005+ text: i18n.tr("Close")
1006+ color: podbird.appTheme.positiveActionButton
1007+ onClicked: {
1008+ PopupUtils.close(dialogInternal)
1009+ }
1010+ }
1011+ }
1012+ }
1013+
1014+ ListView {
1015+ id: episodeList
1016+
1017+ Component.onCompleted: {
1018+ // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition
1019+ // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration
1020+ var scaleFactor = units.gridUnit / 8;
1021+ maximumFlickVelocity = maximumFlickVelocity * scaleFactor;
1022+ flickDeceleration = flickDeceleration * scaleFactor;
1023+ }
1024+
1025+ anchors {
1026+ top: episodesPage.header.bottom
1027+ left: parent.left
1028+ right: parent.right
1029+ bottom: parent.bottom
1030+ }
1031+
1032+ clip: true
1033+ model: sortedEpisodeModel
1034+ section.property: "diff"
1035+ section.labelPositioning: ViewSection.InlineLabels
1036+
1037+ section.delegate: ListItem {
1038+ height: headerText.title.text !== "" ? headerText.height + divider.height : units.gu(0)
1039+ divider.anchors.leftMargin: units.gu(2)
1040+ divider.anchors.rightMargin: units.gu(2)
1041+
1042+ ListItemLayout {
1043+ id: headerText
1044+ title.text: {
1045+ if (section === "Today") {
1046+ return i18n.tr("Today")
1047+ }
1048+
1049+ else if (section === "Yesterday") {
1050+ return i18n.tr("Yesterday")
1051+ }
1052+
1053+ else if (section === "Older") {
1054+ return i18n.tr("Older")
1055+ }
1056+
1057+ else {
1058+ return ""
1059+ }
1060+ }
1061+ title.color: podbird.appTheme.baseText
1062+ title.font.weight: Font.DemiBold
1063+ }
1064+ }
1065+
1066+ footer: Item {
1067+ width: parent.width
1068+ height: units.gu(8)
1069+ }
1070+
1071+ delegate: ListItem {
1072+ id: listItem
1073+
1074+ divider.visible: false
1075+ highlightColor: podbird.appTheme.hightlightListView
1076+ height: downloader.downloadingGuid === model.guid ? listItemLayout.height + progressBarLoader.height + units.gu(1) : listItemLayout.height + units.gu(0.5)
1077+
1078+ ListItemLayout {
1079+ id: listItemLayout
1080+
1081+ title.text: model.name !== undefined ? model.name.trim() : "Undefined"
1082+ title.color: currentGuid === model.guid || downloader.downloadingGuid === model.guid ? podbird.appTheme.focusText
1083+ : podbird.appTheme.baseText
1084+ // #FIXME: Change this 2 to prevent title eliding when UITK is updated to rev > 1800
1085+ title.maximumLineCount: 1
1086+
1087+ subtitle.text: model.duration === 0 || model.duration === undefined ? model.downloadedfile ? "📎 " + model.artist
1088+ : model.artist
1089+ : model.downloadedfile ? "📎 " + Podcasts.formatEpisodeTime(model.duration) + " | " + model.artist
1090+ : Podcasts.formatEpisodeTime(model.duration) + " | " + model.artist
1091+ subtitle.color: podbird.appTheme.baseSubText
1092+
1093+ Image {
1094+ height: width
1095+ width: units.gu(6)
1096+ source: model.image !== undefined ? model.image : Qt.resolvedUrl("../graphics/podbird.png")
1097+ SlotsLayout.position: SlotsLayout.Leading
1098+ sourceSize { width: width; height: height }
1099+ }
1100+
1101+ padding.top: units.gu(1)
1102+ padding.bottom: units.gu(0.5)
1103+ }
1104+
1105+ Loader {
1106+ id: progressBarLoader
1107+ anchors { top: listItemLayout.bottom; left: parent.left; right: parent.right; leftMargin: units.gu(2); rightMargin: units.gu(2) }
1108+ height: sourceComponent !== undefined ? units.dp(5) : 0
1109+ visible: sourceComponent !== undefined
1110+ sourceComponent: downloader.downloadingGuid === model.guid ? progressBar : undefined
1111+ }
1112+
1113+ Component {
1114+ id: progressBar
1115+ CustomProgressBar {
1116+ indeterminateProgress: downloader.progress < 0 || downloader.progress > 100 && downloader.downloadingGuid === model.guid
1117+ progress: downloader.progress
1118+ }
1119+ }
1120+
1121+ trailingActions: ListItemActions {
1122+ actions: [
1123+ Action {
1124+ iconName: model.downloadedfile ? "delete" : (model.queued && downloader.downloadingGuid !== model.guid ? "history" : "save")
1125+ onTriggered: {
1126+ var db = Podcasts.init();
1127+ if (model.downloadedfile) {
1128+ fileManager.deleteFile(model.downloadedfile);
1129+ db.transaction(function (tx) {
1130+ tx.executeSql("UPDATE Episode SET downloadedfile = NULL WHERE guid = ?", [model.guid]);
1131+ });
1132+ episodesModel.setProperty(model.index, "downloadedfile", "")
1133+ if (episodesPageHeaderSections.selectedIndex === 1) {
1134+ episodesModel.remove(model.index, 1)
1135+ }
1136+ } else {
1137+ db.transaction(function (tx) {
1138+ tx.executeSql("UPDATE Episode SET queued=1 WHERE guid = ?", [model.guid]);
1139+ });
1140+ episodesModel.setProperty(model.index, "queued", 1)
1141+ downloader.addDownload(model.guid, model.audiourl);
1142+ }
1143+ }
1144+ },
1145+
1146+ Action {
1147+ iconName: model.listened ? "view-collapse" : "select"
1148+ onTriggered: {
1149+ var db = Podcasts.init();
1150+ db.transaction(function (tx) {
1151+ if (model.listened) {
1152+ tx.executeSql("UPDATE Episode SET listened=0 WHERE guid=?", [model.guid])
1153+ episodesModel.setProperty(model.index, "listened", 0)
1154+ }
1155+ else {
1156+ tx.executeSql("UPDATE Episode SET listened=1 WHERE guid=?", [model.guid])
1157+ episodesModel.setProperty(model.index, "listened", 1)
1158+ if (episodesPageHeaderSections.selectedIndex === 0) {
1159+ episodesModel.remove(model.index, 1)
1160+ }
1161+ }
1162+ });
1163+ }
1164+ },
1165+
1166+ Action {
1167+ iconName: model.favourited ? "unlike" : "like"
1168+ onTriggered: {
1169+ var db = Podcasts.init();
1170+ db.transaction(function (tx) {
1171+ if (model.favourited) {
1172+ tx.executeSql("UPDATE Episode SET favourited=0 WHERE guid=?", [model.guid])
1173+ episodesModel.setProperty(model.index, "favourited", 0)
1174+ if (episodesPageHeaderSections.selectedIndex === 2) {
1175+ episodesModel.remove(model.index, 1)
1176+ }
1177+ }
1178+ else {
1179+ tx.executeSql("UPDATE Episode SET favourited=1 WHERE guid=?", [model.guid])
1180+ episodesModel.setProperty(model.index, "favourited", 1)
1181+ }
1182+ });
1183+ }
1184+ },
1185+
1186+ Action {
1187+ iconName: "info"
1188+ onTriggered: {
1189+ var popup = PopupUtils.open(episodeDescriptionDialog, episodesTab);
1190+ popup.description = model.description
1191+ }
1192+ }
1193+ ]
1194+ }
1195+
1196+ onClicked: {
1197+ Haptics.play()
1198+ var db = Podcasts.init();
1199+ db.transaction(function (tx) {
1200+ if (currentGuid !== model.guid) {
1201+ currentGuid = "";
1202+ currentUrl = model.downloadedfile ? model.downloadedfile : model.audiourl;
1203+ var rs = tx.executeSql("SELECT position FROM Episode WHERE guid=?", [model.guid]);
1204+ playerLoader.item.play();
1205+ playerLoader.item.seek(rs.rows.item(0).position);
1206+ currentName = model.name;
1207+ currentArtist = model.artist;
1208+ currentImage = model.image;
1209+ currentGuid = model.guid;
1210+ }
1211+ });
1212+ }
1213+ }
1214+
1215+ Scrollbar {
1216+ flickableItem: episodeList
1217+ align: Qt.AlignTrailing
1218+ StyleHints { sliderColor: podbird.appTheme.focusText }
1219+ }
1220+
1221+ PullToRefresh {
1222+ refreshing: episodesUpdating
1223+ onRefresh: updateEpisodesDatabase();
1224+ }
1225+ }
1226+ }
1227+
1228+ function refreshModel() {
1229+ var i, j, episode
1230+ var db = Podcasts.init()
1231+
1232+ episodesModel.clear()
1233+
1234+ // Episode Model for the what's new view
1235+ if (episodesPageHeaderSections.selectedIndex === 0) {
1236+ var today = new Date()
1237+ var dayToMs = 86400000; //1 * 24 * 60 * 60 * 1000
1238+ var todayCount, yesterdayCount, diff
1239+
1240+ todayCount = 0
1241+ yesterdayCount = 0
1242+
1243+ db.transaction(function (tx) {
1244+ var rs = tx.executeSql("SELECT rowid, * FROM Podcast ORDER BY name ASC");
1245+ for (i=0; i < rs.rows.length; i++) {
1246+ var podcast = rs.rows.item(i);
1247+ var rs2 = tx.executeSql("SELECT rowid, * FROM Episode WHERE podcast=? ORDER BY published DESC", [rs.rows.item(i).rowid]);
1248+ for (j=0; j < rs2.rows.length; j++) {
1249+ episode = rs2.rows.item(j)
1250+ diff = Math.floor((today - episode.published)/dayToMs)
1251+ if (diff < 7 && !episode.listened) {
1252+ if (diff < 1) {
1253+ 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, "favourited": episode.favourited, "diff": "Today"})
1254+ todayCount++;
1255+ } else if (diff < 2) {
1256+ 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, "favourited": episode.favourited, "diff": "Yesterday"})
1257+ yesterdayCount++;
1258+ } else {
1259+ 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, "favourited": episode.favourited, "diff": "Older"})
1260+ }
1261+ } else if (diff >= 7){
1262+ break
1263+ }
1264+ }
1265+
1266+ if (podcast.lastupdate === null && !episodesUpdating) {
1267+ updateEpisodesDatabase();
1268+ }
1269+ }
1270+ });
1271+ }
1272+
1273+ // Episode Model for the downloaded view
1274+ else if (episodesPageHeaderSections.selectedIndex === 1) {
1275+ db.transaction(function (tx) {
1276+ var rs = tx.executeSql("SELECT rowid, * FROM Podcast ORDER BY name ASC");
1277+ for (i=0; i < rs.rows.length; i++) {
1278+ var podcast = rs.rows.item(i);
1279+ var rs2 = tx.executeSql("SELECT rowid, * FROM Episode WHERE podcast=? ORDER BY published DESC", [rs.rows.item(i).rowid]);
1280+ for (j=0; j < rs2.rows.length; j++) {
1281+ episode = rs2.rows.item(j)
1282+ if (episode.downloadedfile) {
1283+ 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, "favourited": episode.favourited, "diff": "Null"})
1284+ }
1285+ }
1286+
1287+ if (podcast.lastupdate === null && !episodesUpdating) {
1288+ updateEpisodesDatabase();
1289+ }
1290+ }
1291+ });
1292+ }
1293+
1294+ // Episode Model for the favourites view
1295+ else if (episodesPageHeaderSections.selectedIndex === 2) {
1296+ db.transaction(function (tx) {
1297+ var rs = tx.executeSql("SELECT rowid, * FROM Podcast ORDER BY name ASC");
1298+ for (i=0; i < rs.rows.length; i++) {
1299+ var podcast = rs.rows.item(i);
1300+ var rs2 = tx.executeSql("SELECT rowid, * FROM Episode WHERE podcast=? ORDER BY published DESC", [rs.rows.item(i).rowid]);
1301+ for (j=0; j < rs2.rows.length; j++) {
1302+ episode = rs2.rows.item(j)
1303+ if (episode.favourited) {
1304+ 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, "favourited": episode.favourited, "diff": "Null"})
1305+ }
1306+ }
1307+
1308+ if (podcast.lastupdate === null && !episodesUpdating) {
1309+ updateEpisodesDatabase();
1310+ }
1311+ }
1312+ });
1313+ }
1314+
1315+ episodesUpdating = false;
1316+ }
1317+
1318+ function updateEpisodesDatabase() {
1319+ console.log("[LOG]: Checking for new episodes")
1320+ episodesUpdating = true;
1321+ Podcasts.updateEpisodes(refreshModel)
1322+ }
1323+}
1324+
1325
1326=== modified file 'app/ui/PodcastsTab.qml'
1327--- app/ui/PodcastsTab.qml 2016-02-25 11:09:44 +0000
1328+++ app/ui/PodcastsTab.qml 2016-03-28 22:37:57 +0000
1329@@ -61,10 +61,10 @@
1330 }
1331 },
1332 Action {
1333- iconName: podbird.settings.showListView ? "view-grid-symbolic" : "view-list-symbolic"
1334- text: podbird.settings.showListView ? i18n.tr("Grid View") : i18n.tr("List View")
1335+ iconName: "add"
1336+ text: i18n.tr("Add New Podcasts")
1337 onTriggered: {
1338- podbird.settings.showListView = !podbird.settings.showListView
1339+ mainStack.push(Qt.resolvedUrl("SearchPage.qml"))
1340 }
1341 }
1342 ]
1343@@ -86,21 +86,13 @@
1344 anchors.verticalCenter: parent.verticalCenter
1345 }
1346
1347- trailingActionBar.actions: [
1348+ leadingActionBar.actions: [
1349 Action {
1350- iconName: "edit-clear"
1351- text: i18n.tr("Cancel")
1352+ iconName: "back"
1353 onTriggered: {
1354 viewLoader.item.forceActiveFocus()
1355 podcastPage.header = standardHeader
1356 }
1357- },
1358- Action {
1359- iconName: podbird.settings.showListView ? "view-grid-symbolic" : "view-list-symbolic"
1360- text: podbird.settings.showListView ? i18n.tr("Grid View") : i18n.tr("List View")
1361- onTriggered: {
1362- podbird.settings.showListView = !podbird.settings.showListView
1363- }
1364 }
1365 ]
1366 }
1367@@ -221,8 +213,7 @@
1368
1369 height: listItemLayout.height
1370 divider.visible: false
1371- color: index % 2 === 0 ? podbird.appTheme.hightlightListView : "Transparent"
1372- highlightColor: index % 2 === 0 ? "Transparent" : podbird.appTheme.hightlightListView
1373+ highlightColor: podbird.appTheme.hightlightListView
1374
1375 ListItemLayout {
1376 id: listItemLayout
1377
1378=== modified file 'app/ui/SearchPage.qml'
1379--- app/ui/SearchPage.qml 2016-02-25 11:09:44 +0000
1380+++ app/ui/SearchPage.qml 2016-03-28 22:37:57 +0000
1381@@ -28,10 +28,6 @@
1382
1383 property var xhr: new XMLHttpRequest;
1384
1385- TabsList {
1386- id: tabsList
1387- }
1388-
1389 header: standardHeader
1390
1391 PageHeader {
1392@@ -44,11 +40,6 @@
1393 backgroundColor: podbird.appTheme.background
1394 }
1395
1396- leadingActionBar {
1397- numberOfSlots: 0
1398- actions: tabsList.actions
1399- }
1400-
1401 trailingActionBar.actions: [
1402 Action {
1403 iconName: "search"
1404@@ -87,10 +78,9 @@
1405 anchors.verticalCenter: parent.verticalCenter
1406 }
1407
1408- trailingActionBar.actions: [
1409+ leadingActionBar.actions: [
1410 Action {
1411- iconName: "edit-clear"
1412- text: i18n.tr("Cancel")
1413+ iconName: "back"
1414 onTriggered: {
1415 resultsView.forceActiveFocus()
1416 searchResults.clear()
1417@@ -294,7 +284,7 @@
1418 }
1419 });
1420 }
1421- tabs.selectedTabIndex = 1;
1422+ mainStack.pop();
1423 }
1424 }
1425 }
1426@@ -443,7 +433,7 @@
1427 Podcasts.subscribe(artist, name, feed, image);
1428 imageDownloader.feed = feed;
1429 imageDownloader.download(image);
1430- tabs.selectedTabIndex = 1;
1431+ mainStack.pop();
1432 } else {
1433 PopupUtils.open(subscribeFailedDialog);
1434 searchPage.header = addHeader
1435
1436=== modified file 'app/ui/SettingsPage.qml'
1437--- app/ui/SettingsPage.qml 2016-02-25 11:09:44 +0000
1438+++ app/ui/SettingsPage.qml 2016-03-28 22:37:57 +0000
1439@@ -146,6 +146,20 @@
1440 onClicked: mainStack.push(Qt.resolvedUrl("../settings/ThemeSetting.qml"))
1441 }
1442
1443+ ListItem {
1444+ ListItemLayout {
1445+ id: gridViewLayout
1446+ title.text: i18n.tr("Displays podcasts in a list view")
1447+ Switch {
1448+ SlotsLayout.position: SlotsLayout.Last
1449+ checked: podbird.settings.showListView
1450+ onClicked: podbird.settings.showListView = checked
1451+ }
1452+ }
1453+ divider.visible: false
1454+ height: gridViewLayout.height
1455+ }
1456+
1457 HeaderListItem {
1458 title.text: i18n.tr("Playback Settings")
1459 }
1460
1461=== removed file 'app/ui/WhatsNewTab.qml'
1462--- app/ui/WhatsNewTab.qml 2016-02-25 11:09:44 +0000
1463+++ app/ui/WhatsNewTab.qml 1970-01-01 00:00:00 +0000
1464@@ -1,488 +0,0 @@
1465-/*
1466- * Copyright 2015-2016 Michael Sheldon <mike@mikeasoft.com>
1467- *
1468- * This file is part of Podbird.
1469- *
1470- * Podbird is free software; you can redistribute it and/or modify
1471- * it under the terms of the GNU General Public License as published by
1472- * the Free Software Foundation; version 3.
1473- *
1474- * Podbird is distributed in the hope that it will be useful,
1475- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1476- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1477- * GNU General Public License for more details.
1478- *
1479- * You should have received a copy of the GNU General Public License
1480- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1481- */
1482-
1483-import QtQuick 2.4
1484-import QtMultimedia 5.4
1485-import Ubuntu.Components 1.3
1486-import QtQuick.LocalStorage 2.0
1487-import Ubuntu.DownloadManager 0.1
1488-import Ubuntu.Components.Popups 1.3
1489-import "../podcasts.js" as Podcasts
1490-import "../components"
1491-
1492-Tab {
1493- id: whatsNewTab
1494-
1495- property var today: new Date()
1496- property int dayToMs: 86400000
1497- property string tempGuid: "NULL"
1498- property bool episodesUpdating: false
1499-
1500- TabsList {
1501- id: tabsList
1502- }
1503-
1504- page: Page {
1505- id: whatsNewPage
1506-
1507- header: standardHeader
1508-
1509- PageHeader {
1510- id: standardHeader
1511- visible: whatsNewPage.header === standardHeader
1512- title: i18n.tr("What's New")
1513-
1514- StyleHints {
1515- backgroundColor: podbird.appTheme.background
1516- }
1517-
1518- leadingActionBar {
1519- numberOfSlots: 0
1520- actions: tabsList.actions
1521- }
1522-
1523- trailingActionBar.actions: [
1524- Action {
1525- iconName: "search"
1526- text: i18n.tr("Search Episode")
1527- onTriggered: {
1528- whatsNewPage.header = searchHeader
1529- searchField.item.forceActiveFocus()
1530- }
1531- },
1532-
1533- Action {
1534- iconName: "select"
1535- text: i18n.tr("Mark all listened")
1536- onTriggered: {
1537- var db = Podcasts.init();
1538- db.transaction(function (tx) {
1539- for (var i=0; i<whatsNewModel.count; i++) {
1540- tx.executeSql("UPDATE Episode SET listened=1 WHERE guid=?", [whatsNewModel.get(i).guid]);
1541- }
1542- whatsNewModel.clear()
1543- });
1544- }
1545- },
1546-
1547- Action {
1548- iconName: "save"
1549- text: i18n.tr("Download all")
1550- onTriggered: {
1551- var db = Podcasts.init();
1552- db.transaction(function (tx) {
1553- for (var i=0; i<whatsNewModel.count; i++) {
1554- if (!whatsNewModel.get(i).downloadedfile) {
1555- whatsNewModel.setProperty(i, "queued", 1)
1556- tx.executeSql("UPDATE Episode SET queued=1 WHERE guid = ?", [whatsNewModel.get(i).guid]);
1557- downloader.addDownload(whatsNewModel.get(i).guid, whatsNewModel.get(i).audiourl);
1558- }
1559- }
1560- });
1561- }
1562- }
1563- ]
1564- }
1565-
1566- PageHeader {
1567- id: searchHeader
1568- visible: whatsNewPage.header === searchHeader
1569-
1570- StyleHints {
1571- backgroundColor: podbird.appTheme.background
1572- }
1573-
1574- contents: Loader {
1575- id: searchField
1576- sourceComponent: whatsNewPage.header === searchHeader ? searchFieldComponent : undefined
1577- anchors.left: parent ? parent.left : undefined
1578- anchors.right: parent ? parent.right : undefined
1579- anchors.verticalCenter: parent ? parent.verticalCenter : undefined
1580- }
1581-
1582- trailingActionBar.actions: [
1583- Action {
1584- iconName: "edit-clear"
1585- text: i18n.tr("Cancel")
1586- onTriggered: {
1587- episodeList.forceActiveFocus()
1588- whatsNewPage.header = standardHeader
1589- }
1590- }
1591- ]
1592- }
1593-
1594- Component {
1595- id: searchFieldComponent
1596- TextField {
1597- inputMethodHints: Qt.ImhNoPredictiveText
1598- placeholderText: i18n.tr("Search episode")
1599- }
1600- }
1601-
1602- Loader {
1603- id: emptyState
1604-
1605- anchors {
1606- left: parent.left
1607- right: parent.right
1608- margins: units.gu(2)
1609- verticalCenter: parent.verticalCenter
1610- verticalCenterOffset: Qt.inputMethod.visible ? units.gu(4) : 0
1611- }
1612-
1613- sourceComponent: whatsNewModel.count === 0 || sortedEpisodeModel.count === 0 ? emptyStateComponent : undefined
1614- }
1615-
1616- Component {
1617- id: emptyStateComponent
1618- EmptyState {
1619- icon.source: whatsNewModel.count === 0 ? Qt.resolvedUrl("../graphics/owlSearch.svg") : Qt.resolvedUrl("../graphics/notFound.svg")
1620- title: whatsNewModel.count === 0 ? i18n.tr("No New Episodes") : i18n.tr("No Episodes Found")
1621- subTitle: whatsNewModel.count === 0 ? i18n.tr("No more episodes to listen to!") : i18n.tr("No Episodes found matching the search term.")
1622- }
1623- }
1624-
1625- ListModel {
1626- id: whatsNewModel
1627- }
1628-
1629- SortFilterModel {
1630- id: sortedEpisodeModel
1631- model: whatsNewModel
1632- filter.property: "name"
1633- filter.pattern: whatsNewPage.state === "search" && searchField.status == Loader.Ready ? RegExp(searchField.item.text, "gi")
1634- : RegExp("", "gi")
1635- }
1636-
1637- onVisibleChanged: {
1638- if (visible) {
1639- refreshModel()
1640- if (downloader.downloadingGuid != "")
1641- tempGuid = downloader.downloadingGuid
1642- } else {
1643- whatsNewPage.header = standardHeader
1644- }
1645- }
1646-
1647- Connections {
1648- target: downloader
1649- onDownloadingGuidChanged: {
1650- var db = Podcasts.init();
1651- db.transaction(function (tx) {
1652- /*
1653- If tempGuid is NULL, then the episode currently being downloaded is not found within
1654- this podcast. On the other hand, if it is within this podcast, then update the whatsNewModel
1655- with the downloadedfile location we just received from the downloader.
1656- */
1657- if (tempGuid != "NULL") {
1658- var rs2 = tx.executeSql("SELECT downloadedfile FROM Episode WHERE guid=?", [tempGuid]);
1659- for (var i=0; i<whatsNewModel.count; i++) {
1660- if (whatsNewModel.get(i).guid == tempGuid) {
1661- console.log("[LOG]: Setting episode download URL to " + rs2.rows.item(0).downloadedfile)
1662- whatsNewModel.setProperty(i, "downloadedfile", rs2.rows.item(0).downloadedfile)
1663- whatsNewModel.setProperty(i, "queued", 0)
1664- break
1665- }
1666- }
1667- tempGuid = "NULL"
1668- }
1669-
1670- /*
1671- Here it is checked if the currently downloaded episode belongs to the podcast
1672- page being currently displayed. If it is, then the downloaded episode guid is
1673- stored in the tempGuid variable to track it.
1674- */
1675- var rs = tx.executeSql("SELECT podcast FROM Episode WHERE guid=?", [downloader.downloadingGuid]);
1676-
1677- if (downloader.downloadingGuid != "" && tempGuid == "NULL") {
1678- tempGuid = downloader.downloadingGuid
1679- }
1680- });
1681- refreshModel();
1682- }
1683- }
1684-
1685- /*
1686- Note (nik90): After the upgrade to Ubuntu.Components 1.2, it seems the new listitems don't have their trailing
1687- action width clamped. As a result when the list item expands and the user swipes left, it leads to a rather huge
1688- trailing edge action. This has been reported upstream at http://pad.lv/1465582. Until this is fixed, the
1689- episode description is shown in a dialog.
1690- */
1691- Component {
1692- id: episodeDescriptionDialog
1693- Dialog {
1694- id: dialogInternal
1695-
1696- property string description
1697-
1698- title: "<b>%1</b>".arg(i18n.tr("Episode Description"))
1699-
1700- Label {
1701- width: parent.width
1702- wrapMode: Text.WordWrap
1703- color: UbuntuColors.darkGrey
1704- linkColor: "Blue"
1705- text: dialogInternal.description
1706- onLinkActivated: Qt.openUrlExternally(link)
1707- }
1708-
1709- Button {
1710- text: i18n.tr("Close")
1711- color: podbird.appTheme.positiveActionButton
1712- onClicked: {
1713- PopupUtils.close(dialogInternal)
1714- }
1715- }
1716- }
1717- }
1718-
1719- UbuntuListView {
1720- id: episodeList
1721-
1722- Component.onCompleted: {
1723- // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition
1724- // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration
1725- var scaleFactor = units.gridUnit / 8;
1726- maximumFlickVelocity = maximumFlickVelocity * scaleFactor;
1727- flickDeceleration = flickDeceleration * scaleFactor;
1728- }
1729-
1730- anchors {
1731- top: whatsNewPage.header.bottom
1732- left: parent.left
1733- right: parent.right
1734- bottom: parent.bottom
1735- }
1736-
1737- clip: true
1738-
1739- model: sortedEpisodeModel
1740- currentIndex: -1
1741- section.property: "diff"
1742- section.labelPositioning: ViewSection.InlineLabels
1743-
1744- section.delegate: Rectangle {
1745- width: parent.width
1746- color: "Transparent"
1747- height: header.implicitHeight + units.gu(2)
1748- Label {
1749- id: header
1750- anchors {
1751- left: parent.left
1752- right: parent.right
1753- margins: units.gu(2)
1754- verticalCenter: parent.verticalCenter
1755- }
1756- textSize: Label.XLarge
1757- text: {
1758- if (section === "Today") {
1759- return i18n.tr("Today")
1760- }
1761-
1762- else if (section === "Yesterday") {
1763- return i18n.tr("Yesterday")
1764- }
1765-
1766- else if (section === "Older")
1767- return i18n.tr("Older")
1768- }
1769- }
1770- }
1771-
1772- footer: Item {
1773- width: parent.width
1774- height: units.gu(8)
1775- }
1776-
1777- delegate: ListItem {
1778- id: listItem
1779-
1780- divider.visible: false
1781- highlightColor: "Transparent"
1782- height: downloader.downloadingGuid === model.guid ? listItemLayout.height + progressBarLoader.height + units.gu(1) : listItemLayout.height + units.gu(0.5)
1783- color: index % 2 === 0 ? podbird.appTheme.hightlightListView : "Transparent"
1784-
1785- ListItemLayout {
1786- id: listItemLayout
1787-
1788- title.text: model.name !== undefined ? model.name.trim() : "Undefined"
1789- title.color: currentGuid === model.guid || downloader.downloadingGuid === model.guid ? podbird.appTheme.focusText
1790- : podbird.appTheme.baseText
1791- // #FIXME: Change this 2 to prevent title eliding when UITK is updated to rev > 1800
1792- title.maximumLineCount: 1
1793-
1794- subtitle.text: model.duration === 0 || model.duration === undefined ? model.downloadedfile ? "📎 " + model.artist
1795- : model.artist
1796- : model.downloadedfile ? "📎 " + Podcasts.formatEpisodeTime(model.duration) + " | " + model.artist
1797- : Podcasts.formatEpisodeTime(model.duration) + " | " + model.artist
1798- subtitle.color: podbird.appTheme.baseSubText
1799-
1800- Image {
1801- height: width
1802- width: units.gu(6)
1803- source: model.image !== undefined ? model.image : Qt.resolvedUrl("../graphics/podbird.png")
1804- SlotsLayout.position: SlotsLayout.Leading
1805- sourceSize { width: width; height: height }
1806- }
1807-
1808- padding.top: units.gu(1)
1809- padding.bottom: units.gu(0.5)
1810- }
1811-
1812- Loader {
1813- id: progressBarLoader
1814- anchors { top: listItemLayout.bottom; left: parent.left; right: parent.right; leftMargin: units.gu(2); rightMargin: units.gu(2) }
1815- height: sourceComponent !== undefined ? units.dp(5) : 0
1816- visible: sourceComponent !== undefined
1817- sourceComponent: downloader.downloadingGuid === model.guid ? progressBar : undefined
1818- }
1819-
1820- Component {
1821- id: progressBar
1822- CustomProgressBar {
1823- indeterminateProgress: downloader.progress < 0 || downloader.progress > 100 && downloader.downloadingGuid === model.guid
1824- progress: downloader.progress
1825- }
1826- }
1827-
1828- trailingActions: ListItemActions {
1829- actions: [
1830- Action {
1831- iconName: model.downloadedfile ? "delete" : (model.queued && downloader.downloadingGuid !== model.guid ? "history" : "save")
1832- onTriggered: {
1833- var db = Podcasts.init();
1834- if (model.downloadedfile) {
1835- fileManager.deleteFile(model.downloadedfile);
1836- db.transaction(function (tx) {
1837- tx.executeSql("UPDATE Episode SET downloadedfile = NULL WHERE guid = ?", [model.guid]);
1838- });
1839- whatsNewModel.setProperty(model.index, "downloadedfile", "")
1840- } else {
1841- db.transaction(function (tx) {
1842- tx.executeSql("UPDATE Episode SET queued=1 WHERE guid = ?", [model.guid]);
1843- });
1844- whatsNewModel.setProperty(model.index, "queued", 1)
1845- downloader.addDownload(model.guid, model.audiourl);
1846- }
1847- }
1848- },
1849-
1850- Action {
1851- iconName: "select"
1852- onTriggered: {
1853- var db = Podcasts.init();
1854- db.transaction(function (tx) {
1855- tx.executeSql("UPDATE Episode SET listened=1 WHERE guid=?", [model.guid])
1856- whatsNewModel.remove(model.index, 1)
1857- });
1858- }
1859- },
1860-
1861- Action {
1862- iconName: "info"
1863- onTriggered: {
1864- var popup = PopupUtils.open(episodeDescriptionDialog, whatsNewTab);
1865- popup.description = model.description
1866- }
1867- }
1868- ]
1869- }
1870-
1871- onClicked: {
1872- Haptics.play()
1873- var db = Podcasts.init();
1874- db.transaction(function (tx) {
1875- if (currentGuid !== model.guid) {
1876- currentGuid = "";
1877- currentUrl = model.downloadedfile ? model.downloadedfile : model.audiourl;
1878- var rs = tx.executeSql("SELECT position FROM Episode WHERE guid=?", [model.guid]);
1879- playerLoader.item.play();
1880- playerLoader.item.seek(rs.rows.item(0).position);
1881- currentName = model.name;
1882- currentArtist = model.artist;
1883- currentImage = model.image;
1884- currentGuid = model.guid;
1885- }
1886- });
1887- }
1888- }
1889-
1890- Scrollbar {
1891- flickableItem: episodeList
1892- align: Qt.AlignTrailing
1893- StyleHints { sliderColor: podbird.appTheme.focusText }
1894- }
1895-
1896- PullToRefresh {
1897- refreshing: episodesUpdating
1898- onRefresh: updateEpisodesDatabase();
1899- }
1900- }
1901- }
1902-
1903- function refreshModel() {
1904- var today = new Date()
1905- var dayToMs = 86400000; //1 * 24 * 60 * 60 * 1000
1906- var i, j, episode, diff
1907- var todayCount, yesterdayCount
1908-
1909- whatsNewModel.clear()
1910- todayCount = 0
1911- yesterdayCount = 0
1912-
1913- var db = Podcasts.init()
1914- db.transaction(function (tx) {
1915- var rs = tx.executeSql("SELECT rowid, * FROM Podcast ORDER BY name ASC");
1916- for (i=0; i < rs.rows.length; i++) {
1917- var podcast = rs.rows.item(i);
1918- var rs2 = tx.executeSql("SELECT rowid, * FROM Episode WHERE podcast=? ORDER BY published DESC", [rs.rows.item(i).rowid]);
1919- for (j=0; j < rs2.rows.length; j++) {
1920- episode = rs2.rows.item(j)
1921- diff = Math.floor((today - episode.published)/dayToMs)
1922- if (diff < 7 && !episode.listened) {
1923- if (diff < 1) {
1924- 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"})
1925- todayCount++;
1926- } else if (diff < 2) {
1927- 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"})
1928- yesterdayCount++;
1929- } else {
1930- 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"})
1931- }
1932- } else if (diff >= 7){
1933- break
1934- }
1935- }
1936-
1937- if (podcast.lastupdate === null && !episodesUpdating) {
1938- updateEpisodesDatabase();
1939- }
1940- }
1941- });
1942-
1943- episodesUpdating = false;
1944- }
1945-
1946- function updateEpisodesDatabase() {
1947- console.log("[LOG]: Checking for new episodes")
1948- episodesUpdating = true;
1949- Podcasts.updateEpisodes(refreshModel)
1950- }
1951-}
1952-
1953
1954=== modified file 'po/com.mikeasoft.podbird.pot'
1955--- po/com.mikeasoft.podbird.pot 2016-02-25 10:59:57 +0000
1956+++ po/com.mikeasoft.podbird.pot 2016-03-28 22:37:57 +0000
1957@@ -8,7 +8,7 @@
1958 msgstr ""
1959 "Project-Id-Version: \n"
1960 "Report-Msgid-Bugs-To: \n"
1961-"POT-Creation-Date: 2016-02-25 16:28+0530\n"
1962+"POT-Creation-Date: 2016-03-19 07:47+0530\n"
1963 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1964 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1965 "Language-Team: LANGUAGE <LL@li.org>\n"
1966@@ -18,19 +18,15 @@
1967 "Content-Transfer-Encoding: 8bit\n"
1968 "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
1969
1970-#: ../app/components/TabsList.qml:11 ../app/ui/WhatsNewTab.qml:30
1971-msgid "What's New"
1972+#: ../app/components/TabsList.qml:29 ../app/ui/EpisodesTab.qml:48
1973+msgid "Episodes"
1974 msgstr ""
1975
1976-#: ../app/components/TabsList.qml:19 ../app/ui/PodcastsTab.qml:42
1977+#: ../app/components/TabsList.qml:37 ../app/ui/PodcastsTab.qml:42
1978 msgid "Podcasts"
1979 msgstr ""
1980
1981-#: ../app/components/TabsList.qml:27
1982-msgid "Add new podcasts"
1983-msgstr ""
1984-
1985-#: ../app/components/TabsList.qml:35 ../app/ui/SettingsPage.qml:32
1986+#: ../app/components/TabsList.qml:45 ../app/ui/SettingsPage.qml:32
1987 msgid "Settings"
1988 msgstr ""
1989
1990@@ -48,24 +44,24 @@
1991 msgid "No podcasts listened to today"
1992 msgstr ""
1993
1994-#: ../app/podcasts.js:180
1995+#: ../app/podcasts.js:182
1996 #, no-c-format, qt-format
1997 msgid "%1 hr %2 min"
1998 msgstr ""
1999
2000-#: ../app/podcasts.js:189
2001+#: ../app/podcasts.js:191
2002 #, no-c-format, qt-format
2003 msgid "%1 hr"
2004 msgstr ""
2005
2006-#: ../app/podcasts.js:197
2007+#: ../app/podcasts.js:199
2008 #, no-c-format, qt-format
2009 msgid "%1 min"
2010 msgstr ""
2011
2012 #. TRANSLATORS: About as in information about the app
2013 #: ../app/settings/About.qml:28 ../app/settings/About.qml:47
2014-#: ../app/ui/SettingsPage.qml:208
2015+#: ../app/ui/SettingsPage.qml:222
2016 msgid "About"
2017 msgstr ""
2018
2019@@ -143,7 +139,7 @@
2020 #: ../app/settings/DownloadSetting.qml:34
2021 #: ../app/settings/DownloadSetting.qml:35
2022 #: ../app/settings/DownloadSetting.qml:36
2023-#: ../app/settings/DownloadSetting.qml:37 ../app/ui/EpisodesPage.qml:347
2024+#: ../app/settings/DownloadSetting.qml:37
2025 #, qt-format
2026 msgid "%1 episode"
2027 msgid_plural "%1 episodes"
2028@@ -164,72 +160,130 @@
2029 msgid "Dark"
2030 msgstr ""
2031
2032-#: ../app/ui/EpisodesPage.qml:33
2033+#: ../app/ui/EpisodesPage.qml:32
2034 msgid "Podcast"
2035 msgstr ""
2036
2037-#: ../app/ui/EpisodesPage.qml:74 ../app/ui/WhatsNewTab.qml:44
2038+#: ../app/ui/EpisodesPage.qml:73 ../app/ui/EpisodesTab.qml:62
2039 msgid "Search Episode"
2040 msgstr ""
2041
2042-#: ../app/ui/EpisodesPage.qml:83 ../app/ui/WhatsNewTab.qml:53
2043+#: ../app/ui/EpisodesPage.qml:82 ../app/ui/EpisodesTab.qml:72
2044 msgid "Mark all listened"
2045 msgstr ""
2046
2047-#: ../app/ui/EpisodesPage.qml:94 ../app/ui/EpisodesPage.qml:188
2048-#: ../app/ui/SearchPage.qml:276
2049+#: ../app/ui/EpisodesPage.qml:93 ../app/ui/EpisodesPage.qml:184
2050+#: ../app/ui/SearchPage.qml:266
2051 msgid "Unsubscribe"
2052 msgstr ""
2053
2054-#: ../app/ui/EpisodesPage.qml:111 ../app/ui/EpisodesPage.qml:205
2055-#: ../app/ui/PodcastsTab.qml:92 ../app/ui/SearchPage.qml:93
2056-#: ../app/ui/SearchPage.qml:131 ../app/ui/SettingsPage.qml:83
2057-#: ../app/ui/SettingsPage.qml:118 ../app/ui/WhatsNewTab.qml:103
2058-msgid "Cancel"
2059-msgstr ""
2060-
2061-#: ../app/ui/EpisodesPage.qml:140 ../app/ui/WhatsNewTab.qml:116
2062+#: ../app/ui/EpisodesPage.qml:136 ../app/ui/EpisodesTab.qml:173
2063 msgid "Search episode"
2064 msgstr ""
2065
2066-#: ../app/ui/EpisodesPage.qml:185
2067+#: ../app/ui/EpisodesPage.qml:181
2068 msgid "Unsubscribe Confirmation"
2069 msgstr ""
2070
2071-#: ../app/ui/EpisodesPage.qml:186
2072+#: ../app/ui/EpisodesPage.qml:182
2073 #, qt-format
2074 msgid "Are you sure you want to unsubscribe from <b>%1</b>?"
2075 msgstr ""
2076
2077-#: ../app/ui/EpisodesPage.qml:227 ../app/ui/WhatsNewTab.qml:216
2078+#: ../app/ui/EpisodesPage.qml:201 ../app/ui/SearchPage.qml:121
2079+#: ../app/ui/SettingsPage.qml:83 ../app/ui/SettingsPage.qml:118
2080+msgid "Cancel"
2081+msgstr ""
2082+
2083+#: ../app/ui/EpisodesPage.qml:223 ../app/ui/EpisodesTab.qml:295
2084 msgid "Episode Description"
2085 msgstr ""
2086
2087-#: ../app/ui/EpisodesPage.qml:239 ../app/ui/SearchPage.qml:180
2088-#: ../app/ui/WhatsNewTab.qml:228
2089+#: ../app/ui/EpisodesPage.qml:234 ../app/ui/EpisodesTab.qml:306
2090+#: ../app/ui/SearchPage.qml:170
2091 msgid "Close"
2092 msgstr ""
2093
2094-#: ../app/ui/EpisodesPage.qml:267
2095+#: ../app/ui/EpisodesPage.qml:262
2096 msgid "No episodes found"
2097 msgstr ""
2098
2099-#: ../app/ui/EpisodesPage.qml:268
2100+#: ../app/ui/EpisodesPage.qml:263
2101 msgid "No episodes found matching the search term."
2102 msgstr ""
2103
2104-#: ../app/ui/EpisodesPage.qml:364
2105+#: ../app/ui/EpisodesPage.qml:377
2106 msgid "Unheard"
2107 msgstr ""
2108
2109-#: ../app/ui/EpisodesPage.qml:391
2110+#: ../app/ui/EpisodesPage.qml:394
2111 msgid "Listened"
2112 msgstr ""
2113
2114-#: ../app/ui/EpisodesPage.qml:416
2115+#: ../app/ui/EpisodesPage.qml:413 ../app/ui/EpisodesTab.qml:135
2116 msgid "Downloaded"
2117 msgstr ""
2118
2119+#: ../app/ui/EpisodesTab.qml:87
2120+msgid "Download all"
2121+msgstr ""
2122+
2123+#: ../app/ui/EpisodesTab.qml:104
2124+msgid "Delete all"
2125+msgstr ""
2126+
2127+#: ../app/ui/EpisodesTab.qml:135
2128+msgid "Recent"
2129+msgstr ""
2130+
2131+#: ../app/ui/EpisodesTab.qml:135
2132+msgid "Favourites"
2133+msgstr ""
2134+
2135+#: ../app/ui/EpisodesTab.qml:198
2136+msgid "No New Episodes"
2137+msgstr ""
2138+
2139+#: ../app/ui/EpisodesTab.qml:200
2140+msgid "No Downloaded Episodes"
2141+msgstr ""
2142+
2143+#: ../app/ui/EpisodesTab.qml:202
2144+msgid "No Favourited Episodes"
2145+msgstr ""
2146+
2147+#: ../app/ui/EpisodesTab.qml:204
2148+msgid "No Episodes Found"
2149+msgstr ""
2150+
2151+#: ../app/ui/EpisodesTab.qml:210
2152+msgid "No more episodes to listen to!"
2153+msgstr ""
2154+
2155+#: ../app/ui/EpisodesTab.qml:212
2156+msgid "No episodes have been downloaded for offline listening"
2157+msgstr ""
2158+
2159+#: ../app/ui/EpisodesTab.qml:214
2160+msgid "No episodes have been favourited."
2161+msgstr ""
2162+
2163+#: ../app/ui/EpisodesTab.qml:216
2164+msgid "No Episodes found matching the search term."
2165+msgstr ""
2166+
2167+#: ../app/ui/EpisodesTab.qml:347
2168+msgid "Today"
2169+msgstr ""
2170+
2171+#: ../app/ui/EpisodesTab.qml:351
2172+msgid "Yesterday"
2173+msgstr ""
2174+
2175+#: ../app/ui/EpisodesTab.qml:355
2176+msgid "Older"
2177+msgstr ""
2178+
2179 #: ../app/ui/NowPlayingPage.qml:29
2180 msgid "Now Playing"
2181 msgstr ""
2182@@ -246,110 +300,102 @@
2183 msgid "+%1s"
2184 msgstr ""
2185
2186-#: ../app/ui/PodcastsTab.qml:57 ../app/ui/SearchPage.qml:55
2187-#: ../app/ui/SearchPage.qml:162
2188+#: ../app/ui/PodcastsTab.qml:57 ../app/ui/SearchPage.qml:46
2189+#: ../app/ui/SearchPage.qml:152
2190 msgid "Search Podcast"
2191 msgstr ""
2192
2193-#: ../app/ui/PodcastsTab.qml:65 ../app/ui/PodcastsTab.qml:100
2194-msgid "Grid View"
2195-msgstr ""
2196-
2197-#: ../app/ui/PodcastsTab.qml:65 ../app/ui/PodcastsTab.qml:100
2198-msgid "List View"
2199-msgstr ""
2200-
2201-#: ../app/ui/PodcastsTab.qml:112
2202+#: ../app/ui/PodcastsTab.qml:65 ../app/ui/SearchPage.qml:37
2203+msgid "Add New Podcasts"
2204+msgstr ""
2205+
2206+#: ../app/ui/PodcastsTab.qml:104
2207 msgid "Search podcast"
2208 msgstr ""
2209
2210-#: ../app/ui/PodcastsTab.qml:142
2211+#: ../app/ui/PodcastsTab.qml:134
2212 msgid "No Podcast Subscriptions"
2213 msgstr ""
2214
2215-#: ../app/ui/PodcastsTab.qml:142
2216+#: ../app/ui/PodcastsTab.qml:134
2217 msgid "No Podcasts Found"
2218 msgstr ""
2219
2220-#: ../app/ui/PodcastsTab.qml:143
2221+#: ../app/ui/PodcastsTab.qml:135
2222 msgid ""
2223 "You haven't subscribed to any podcasts yet, visit the 'Find New Podcasts' "
2224 "page to add some."
2225 msgstr ""
2226
2227-#: ../app/ui/PodcastsTab.qml:144 ../app/ui/SearchPage.qml:210
2228+#: ../app/ui/PodcastsTab.qml:136 ../app/ui/SearchPage.qml:200
2229 msgid "No podcasts found matching the search term."
2230 msgstr ""
2231
2232-#: ../app/ui/PodcastsTab.qml:182 ../app/ui/PodcastsTab.qml:230
2233+#: ../app/ui/PodcastsTab.qml:174 ../app/ui/PodcastsTab.qml:221
2234 #, qt-format
2235 msgid "%1 unheard episode"
2236 msgid_plural "%1 unheard episodes"
2237 msgstr[0] ""
2238 msgstr[1] ""
2239
2240-#: ../app/ui/SearchPage.qml:41
2241-msgid "Add New Podcasts"
2242-msgstr ""
2243-
2244-#: ../app/ui/SearchPage.qml:63
2245+#: ../app/ui/SearchPage.qml:54
2246 msgid "Add Podcast"
2247 msgstr ""
2248
2249-#: ../app/ui/SearchPage.qml:123
2250+#: ../app/ui/SearchPage.qml:113
2251 msgid "Save Podcast"
2252 msgstr ""
2253
2254-#: ../app/ui/SearchPage.qml:150
2255+#: ../app/ui/SearchPage.qml:140
2256 msgid "Feed URL"
2257 msgstr ""
2258
2259-#: ../app/ui/SearchPage.qml:177
2260+#: ../app/ui/SearchPage.qml:167
2261 msgid "Unable to subscribe"
2262 msgstr ""
2263
2264-#: ../app/ui/SearchPage.qml:178
2265+#: ../app/ui/SearchPage.qml:168
2266 msgid "Please check the URL and try again"
2267 msgstr ""
2268
2269-#: ../app/ui/SearchPage.qml:208
2270+#: ../app/ui/SearchPage.qml:198
2271 msgid "Looking to add a new Podcast?"
2272 msgstr ""
2273
2274-#: ../app/ui/SearchPage.qml:208
2275+#: ../app/ui/SearchPage.qml:198
2276 msgid "No Podcasts found"
2277 msgstr ""
2278
2279-#: ../app/ui/SearchPage.qml:209
2280+#: ../app/ui/SearchPage.qml:199
2281 msgid ""
2282 "Click the 'magnifier' at the top to search or the 'plus' button to add by URL"
2283 msgstr ""
2284
2285-#: ../app/ui/SearchPage.qml:276
2286+#: ../app/ui/SearchPage.qml:266
2287 msgid "Subscribe"
2288 msgstr ""
2289
2290 #. TRANSLATORS: The first argument here is the date of when the podcast was last updated followed by
2291 #. the podcast description.
2292-#: ../app/ui/SearchPage.qml:315
2293+#: ../app/ui/SearchPage.qml:305
2294 #, qt-format
2295 msgid ""
2296 "Last Updated: %1\n"
2297 "%2"
2298 msgstr ""
2299
2300-#: ../app/ui/SearchPage.qml:372
2301+#: ../app/ui/SearchPage.qml:362
2302 msgid "Not Available"
2303 msgstr ""
2304
2305 #. TRANSLATORS: This strings refers to the seeking of the episode playback. Users can set how far they
2306 #. want to seek forward when pressing on this button.
2307-#: ../app/ui/SettingsPage.qml:63 ../app/ui/SettingsPage.qml:155
2308+#: ../app/ui/SettingsPage.qml:63 ../app/ui/SettingsPage.qml:169
2309 msgid "Skip forward"
2310 msgstr ""
2311
2312 #: ../app/ui/SettingsPage.qml:70 ../app/ui/SettingsPage.qml:105
2313-#: ../app/ui/SettingsPage.qml:156 ../app/ui/SettingsPage.qml:163
2314+#: ../app/ui/SettingsPage.qml:170 ../app/ui/SettingsPage.qml:177
2315 #, qt-format
2316 msgid "%1 seconds"
2317 msgstr ""
2318@@ -360,7 +406,7 @@
2319
2320 #. TRANSLATORS: This strings refers to the seeking of the episode playback. Users can set how far they
2321 #. want to seek backward when pressing on this button.
2322-#: ../app/ui/SettingsPage.qml:98 ../app/ui/SettingsPage.qml:162
2323+#: ../app/ui/SettingsPage.qml:98 ../app/ui/SettingsPage.qml:176
2324 msgid "Skip back"
2325 msgstr ""
2326
2327@@ -370,73 +416,45 @@
2328 msgid "General Settings"
2329 msgstr ""
2330
2331-#: ../app/ui/SettingsPage.qml:150
2332+#: ../app/ui/SettingsPage.qml:152
2333+msgid "Displays podcasts in a list view"
2334+msgstr ""
2335+
2336+#: ../app/ui/SettingsPage.qml:164
2337 msgid "Playback Settings"
2338 msgstr ""
2339
2340-#: ../app/ui/SettingsPage.qml:168
2341+#: ../app/ui/SettingsPage.qml:182
2342 msgid "Podcast Episode Settings"
2343 msgstr ""
2344
2345-#: ../app/ui/SettingsPage.qml:174
2346+#: ../app/ui/SettingsPage.qml:188
2347 msgid "Automatically delete old episodes"
2348 msgstr ""
2349
2350-#: ../app/ui/SettingsPage.qml:176
2351+#: ../app/ui/SettingsPage.qml:190
2352 msgid ""
2353 "Delete episodes that are older than a given number of days for each podcast"
2354 msgstr ""
2355
2356-#: ../app/ui/SettingsPage.qml:188
2357+#: ../app/ui/SettingsPage.qml:202
2358 msgid "Automatically download new episodes"
2359 msgstr ""
2360
2361-#: ../app/ui/SettingsPage.qml:190
2362+#: ../app/ui/SettingsPage.qml:204
2363 msgid "Default number of new episodes to download for each podcast"
2364 msgstr ""
2365
2366 #. TRANSLATORS: Shortened form of "Miscellaneous" which is shown to denote other setting options
2367 #. that doesn't fit into any other category.
2368-#: ../app/ui/SettingsPage.qml:202
2369+#: ../app/ui/SettingsPage.qml:216
2370 msgid "Misc."
2371 msgstr ""
2372
2373-#: ../app/ui/SettingsPage.qml:218
2374+#: ../app/ui/SettingsPage.qml:232
2375 msgid "Report Bug"
2376 msgstr ""
2377
2378-#: ../app/ui/WhatsNewTab.qml:67
2379-msgid "Download all"
2380-msgstr ""
2381-
2382-#: ../app/ui/WhatsNewTab.qml:138
2383-msgid "No New Episodes"
2384-msgstr ""
2385-
2386-#: ../app/ui/WhatsNewTab.qml:138
2387-msgid "No Episodes Found"
2388-msgstr ""
2389-
2390-#: ../app/ui/WhatsNewTab.qml:139
2391-msgid "No more episodes to listen to!"
2392-msgstr ""
2393-
2394-#: ../app/ui/WhatsNewTab.qml:139
2395-msgid "No Episodes found matching the search term."
2396-msgstr ""
2397-
2398-#: ../app/ui/WhatsNewTab.qml:277
2399-msgid "Today"
2400-msgstr ""
2401-
2402-#: ../app/ui/WhatsNewTab.qml:281
2403-msgid "Yesterday"
2404-msgstr ""
2405-
2406-#: ../app/ui/WhatsNewTab.qml:285
2407-msgid "Older"
2408-msgstr ""
2409-
2410 #: ../app/welcomewizard/Slide1.qml:42
2411 msgid "Welcome to Podbird"
2412 msgstr ""
2413@@ -511,10 +529,10 @@
2414 msgid "Finish"
2415 msgstr ""
2416
2417-#: /home/krnekhelesh/Development/uc-1.3-migrate-build/po/Podbird.desktop.in.h:1
2418+#: /home/krnekhelesh/Development/devel-branch-sync-1-build/po/Podbird.desktop.in.h:1
2419 msgid "The chirpiest podcast manager for Ubuntu"
2420 msgstr ""
2421
2422-#: /home/krnekhelesh/Development/uc-1.3-migrate-build/po/Podbird.desktop.in.h:2
2423+#: /home/krnekhelesh/Development/devel-branch-sync-1-build/po/Podbird.desktop.in.h:2
2424 msgid "podcast;audio;itunes;broadcast;digital;stream;podcatcher;video;vodcast;"
2425 msgstr ""

Subscribers

People subscribed via source and target branches