Merge lp:~nik90/podbird/5.5-kevin-redesign into lp:podbird

Proposed by Nekhelesh Ramananthan
Status: Merged
Merged at revision: 44
Proposed branch: lp:~nik90/podbird/5.5-kevin-redesign
Merge into: lp:podbird
Prerequisite: lp:~nik90/podbird/5-auto-download-option
Diff against target: 1344 lines (+528/-409)
11 files modified
app/podbird.qml (+12/-2)
app/podcasts.js (+16/-3)
app/themes/Dark.qml (+4/-4)
app/themes/Light.qml (+4/-4)
app/ui/ActionButton.qml (+1/-0)
app/ui/BlurredBackground.qml (+4/-21)
app/ui/EpisodesPage.qml (+269/-227)
app/ui/PlayerControls.qml (+2/-2)
app/ui/PodcastsTab.qml (+3/-8)
app/ui/SearchPage.qml (+153/-102)
po/com.mikeasoft.podbird.pot (+60/-36)
To merge this branch: bzr merge lp:~nik90/podbird/5.5-kevin-redesign
Reviewer Review Type Date Requested Status
Michael Sheldon Approve
Review via email: mp+255155@code.launchpad.net

Description of the change

This MP is a huge one brings a lot of improvements which are listed below,
- Redesigned the episodes page and search page according to the designs provided by Kevin (community designer) which is a lot of list..best would be to run this branch and check ;)

- Starting a download or deleting a download file no longer refreshes the entire episodesModel as it is expensive and jarring to the user. Instead I have used episodeModel.setProperty(), episodeModel.insert() to intelligently update only the concerned episode.

- In the episodes page, the episodes are now separated by "New" and "Listened". Again this is done intelligently using the setProperty() function. This enables us to no longer show the "tick" icon.

- The Podcasts page now shows alternating colors for listitems

- The blurred background component has been cleaned up and is now more customizable. It allow for setting the blur opacity which helps with using different values for the Light and Dark theme.

- Dark theme has received some minor color changes.

- Sqlite Schema Upgrade to v1.1. In this version I introduced a new column to every episode "Queued" which keeps track if the episode is in the download queue or not. By storing this in the database, the queue status is preserved if if the user closes the episode page and reopens another page. This is important to inform the user of what episodes are being auto-downloaded by Podbird on app startup.

To post a comment you must log in.
lp:~nik90/podbird/5.5-kevin-redesign updated
67. By Nekhelesh Ramananthan

Fixed a rare case where the downloading status was not shown properly when the user closes and reopens the episodes page

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

Here's a screenshot of what it looks now https://imgur.com/5gKB4QG

lp:~nik90/podbird/5.5-kevin-redesign updated
68. By Nekhelesh Ramananthan

Used a cutom progress bar and tweaked theme colors

69. By Nekhelesh Ramananthan

Small UI Tweaks

70. By Nekhelesh Ramananthan

Dynamically load/unload the search tab

71. By Nekhelesh Ramananthan

Renamed search tab to search page

72. By Nekhelesh Ramananthan

merged prerequisite

73. By Nekhelesh Ramananthan

Added a new column queued to database via a schema upgrade

74. By Nekhelesh Ramananthan

Added comments and fixed alter table sqlite command

75. By Nekhelesh Ramananthan

merged prerequisite

76. By Nekhelesh Ramananthan

Tweaked bottom bar color

77. By Nekhelesh Ramananthan

Added a new application icon

78. By Nekhelesh Ramananthan

Changed New to Unheard

79. By Nekhelesh Ramananthan

Fixed issue where auto-download did not set the queue status correctly. Also fixed formatTime() function complaining about model.duration missing

80. By Nekhelesh Ramananthan

merged prerequisite fix

81. By Nekhelesh Ramananthan

Merged prerequisite

82. By Nekhelesh Ramananthan

Merged prerequisite

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

Note: If you notice any issues while testing this branch, please list them out. Most likely they have been fixed in one of the MPs which came after this. For instance, the database scheme upgrade done in this MP is usually to its full potential only in lp:~nik90/podbird/7-mark-episode-listened MP.

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

Only found two small issues that don't appear to be covered by other branches; I've added fixes for them myself whilst merging, they were just that 1) the new download indicator doesn't show any activity when download manager gives bad progress information (I've made the progress indicator bounce back and forth under these circumstances) and 2) the redesigned episodes page shows "NaN:NaN" for podcasts that don't provide duration information.

Like I said, I've fixed these though, so have now merged this branch :).

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'app/podbird.qml'
--- app/podbird.qml 2015-04-15 23:06:20 +0000
+++ app/podbird.qml 2015-04-18 14:12:52 +0000
@@ -167,9 +167,18 @@
167 objectName: "podcastsTab"167 objectName: "podcastsTab"
168 }168 }
169169
170 SearchTab {170 Tab {
171 id: searchTab171 id: searchTab
172 objectName: "searchTab"172
173 title: i18n.tr("Find New Podcasts")
174
175 page: Loader {
176 parent: searchTab
177 anchors.left: parent.left
178 anchors.right: parent.right
179 anchors.bottom: parent.bottom
180 source: (tabs.selectedTab === searchTab) ? Qt.resolvedUrl("ui/SearchPage.qml") : ""
181 }
173 }182 }
174183
175 Tab {184 Tab {
@@ -248,6 +257,7 @@
248 for (var j=0; j < loopCount; j++) {257 for (var j=0; j < loopCount; j++) {
249 if (!rs2.rows.item(j).downloadedfile && !rs2.rows.item(j).listened) {258 if (!rs2.rows.item(j).downloadedfile && !rs2.rows.item(j).listened) {
250 downloader.addDownload(rs2.rows.item(j).guid, rs2.rows.item(j).audiourl)259 downloader.addDownload(rs2.rows.item(j).guid, rs2.rows.item(j).audiourl)
260 tx.executeSql("UPDATE Episode SET queued=1 WHERE guid = ?", [rs2.rows.item(j).guid]);
251 }261 }
252 }262 }
253 }263 }
254264
=== modified file 'app/podcasts.js'
--- app/podcasts.js 2015-02-28 09:46:48 +0000
+++ app/podcasts.js 2015-04-18 14:12:52 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright 2015 Michael Sheldon <mike@mikeasoft.com>2 * Copyright 2015 Podbird Team
3 *3 *
4 * This file is part of Podbird.4 * This file is part of Podbird.
5 *5 *
@@ -17,11 +17,24 @@
17 */17 */
1818
19function init() {19function init() {
20 var db = LocalStorage.openDatabaseSync("Podbird", "1.0", "Database of subscribed podcasts and their episodes", 1000000);20 var db = LocalStorage.openDatabaseSync("Podbird", "", "Database of subscribed podcasts and their episodes", 1000000);
21
21 db.transaction(function(tx) {22 db.transaction(function(tx) {
22 tx.executeSql('CREATE TABLE IF NOT EXISTS Podcast(artist TEXT, name TEXT, description TEXT, feed TEXT, image TEXT, lastupdate TIMESTAMP)');23 tx.executeSql('CREATE TABLE IF NOT EXISTS Podcast(artist TEXT, name TEXT, description TEXT, feed TEXT, image TEXT, lastupdate TIMESTAMP)');
23 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, listened BOOLEAN, position INTEGER, FOREIGN KEY(podcast) REFERENCES Podcast(rowid))');24 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, listened BOOLEAN, queued BOOLEAN, position INTEGER, FOREIGN KEY(podcast) REFERENCES Podcast(rowid))');
24 });25 });
26
27 /*
28 Schema Upgrade to v1.1 which adds a new queued boolean variable which is needed to track the queued status
29 of a episode properly.
30 */
31 if (db.version == "1.0") {
32 db.changeVersion("1.0", "1.1", function(tx) {
33 tx.executeSql('ALTER TABLE Episode ADD queued BOOLEAN');
34 tx.executeSql('UPDATE Episode SET queued=0');
35 });
36 }
37
25 return db;38 return db;
26}39}
2740
2841
=== modified file 'app/themes/Dark.qml'
--- app/themes/Dark.qml 2015-03-04 02:58:36 +0000
+++ app/themes/Dark.qml 2015-04-18 14:12:52 +0000
@@ -21,12 +21,12 @@
2121
22QtObject {22QtObject {
23 // MainView23 // MainView
24 property color background: "#1E1E23"24 property color background: "#242423"
2525
26 // Main Text Colors26 // Main Text Colors
27 property color baseText: "White"27 property color baseText: "White"
28 property color baseSubText: "#999999"28 property color baseSubText: "#999999"
29 property color focusText: "#FF9900"29 property color focusText: "#35AF44"
3030
31 // Icon Colors31 // Icon Colors
32 property color baseIcon: "White"32 property color baseIcon: "White"
@@ -37,8 +37,8 @@
37 property color neutralActionButton: UbuntuColors.coolGrey37 property color neutralActionButton: UbuntuColors.coolGrey
3838
39 // Bottom Player Bar Colors39 // Bottom Player Bar Colors
40 property color bottomBarBackground: "#0F0F0F"40 property color bottomBarBackground: "#15141A"
4141
42 // Highlight Color42 // Highlight Color
43 property color hightlightListView: "#2C2C34"43 property color hightlightListView: "#333533"
44}44}
4545
=== modified file 'app/themes/Light.qml'
--- app/themes/Light.qml 2015-03-04 02:58:36 +0000
+++ app/themes/Light.qml 2015-04-18 14:12:52 +0000
@@ -21,12 +21,12 @@
2121
22QtObject {22QtObject {
23 // MainView23 // MainView
24 property color background: "#EEEEEE"24 property color background: "#ECECEC"
2525
26 // Main Text Colors26 // Main Text Colors
27 property color baseText: UbuntuColors.darkGrey27 property color baseText: UbuntuColors.darkGrey
28 property color baseSubText: "#999999"28 property color baseSubText: "#999999"
29 property color focusText: UbuntuColors.orange29 property color focusText: "#35AF44"
3030
31 // Icon Colors31 // Icon Colors
32 property color baseIcon: UbuntuColors.darkGrey32 property color baseIcon: UbuntuColors.darkGrey
@@ -37,8 +37,8 @@
37 property color neutralActionButton: UbuntuColors.coolGrey37 property color neutralActionButton: UbuntuColors.coolGrey
3838
39 // Bottom Player Bar Colors39 // Bottom Player Bar Colors
40 property color bottomBarBackground: "#0F0F0F"40 property color bottomBarBackground: "#323435"
4141
42 // Highlight Color42 // Highlight Color
43 property color hightlightListView: "#D8D8D8"43 property color hightlightListView: "#F5F5F5"
44}44}
4545
=== modified file 'app/ui/ActionButton.qml'
--- app/ui/ActionButton.qml 2015-03-08 11:10:07 +0000
+++ app/ui/ActionButton.qml 2015-04-18 14:12:52 +0000
@@ -23,6 +23,7 @@
23 id: abstractButton23 id: abstractButton
2424
25 property string iconName25 property string iconName
26 property alias color: _icon.color
2627
27 Rectangle {28 Rectangle {
28 visible: abstractButton.pressed29 visible: abstractButton.pressed
2930
=== modified file 'app/ui/BlurredBackground.qml'
--- app/ui/BlurredBackground.qml 2015-02-10 06:57:54 +0000
+++ app/ui/BlurredBackground.qml 2015-04-18 14:12:52 +0000
@@ -25,15 +25,8 @@
25Item {25Item {
26 width: parent.width26 width: parent.width
2727
28 property string art // : player.currentMetaFile === "" ? Qt.resolvedUrl("../images/music-app-cover@30.png") : player.currentMetaArt28 property string art
2929 property real backgroundStrength: 0.4
30 // dark layer
31 Rectangle {
32 anchors {
33 fill: parent
34 }
35 color: "black"
36 }
3730
38 // the album art31 // the album art
39 Image {32 Image {
@@ -44,16 +37,6 @@
44 fillMode: Image.PreserveAspectCrop37 fillMode: Image.PreserveAspectCrop
45 height: parent.height38 height: parent.height
46 source: art // this has to be fixed for the default cover art to work - cant find in this dir39 source: art // this has to be fixed for the default cover art to work - cant find in this dir
47
48 // TODO: This should be investigated once http://pad.lv/1391368
49 // is resolved. Once it is, these can either be set to
50 // "height" and "width" or a property exposed via the
51 // SDK or Thumbnailer to avoid a regression caused by
52 // these hardcoded values changing in the Thumbnailer.
53 // 512 is size of the "xlarge" thumbnails in pixels.
54 sourceSize.height: 512
55 sourceSize.width: 512
56
57 visible: false40 visible: false
58 width: Math.max(parent.height, parent.width)41 width: Math.max(parent.height, parent.width)
59 }42 }
@@ -63,8 +46,8 @@
63 id: backgroundBlur46 id: backgroundBlur
64 anchors.fill: backgroundImage47 anchors.fill: backgroundImage
65 source: backgroundImage48 source: backgroundImage
66 radius: units.dp(42)49 radius: units.dp(30)
67 opacity: 0.250 opacity: backgroundStrength
68 }51 }
69 onArtChanged: {52 onArtChanged: {
70 // TODO: This is a work around for LP:1261078 and LP:1306845. Ideally,53 // TODO: This is a work around for LP:1261078 and LP:1306845. Ideally,
7154
=== modified file 'app/ui/EpisodesPage.qml'
--- app/ui/EpisodesPage.qml 2015-03-29 15:33:26 +0000
+++ app/ui/EpisodesPage.qml 2015-04-18 14:12:52 +0000
@@ -30,17 +30,21 @@
30 id: episodesPage30 id: episodesPage
3131
32 visible: false32 visible: false
33 title: episodeName33 title: i18n.tr("Podcast")
34 flickable: null
3435
35 property string episodeName36 property string episodeName
36 property string episodeId37 property string episodeId
37 property string episodeArtist38 property string episodeArtist
38 property string episodeImage39 property string episodeImage
40 property string tempGuid: "NULL"
3941
40 property bool episodesUpdating: false;42 property bool episodesUpdating: false;
4143
42 Component.onCompleted: {44 Component.onCompleted: {
43 loadEpisodes(episodeId, episodeArtist, episodeImage)45 loadEpisodes(episodeId, episodeArtist, episodeImage)
46 if (downloader.downloadingGuid != "")
47 tempGuid = downloader.downloadingGuid
44 }48 }
4549
46 /*50 /*
@@ -95,7 +99,7 @@
95 onTriggered: {99 onTriggered: {
96 var db = Podcasts.init();100 var db = Podcasts.init();
97 db.transaction(function (tx) {101 db.transaction(function (tx) {
98 tx.executeSql("UPDATE Episode SET listened=1 WHERE podcast=?", [episodeModel.pid]);102 tx.executeSql("UPDATE Episode SET listened=1 WHERE podcast=?", [episodeId]);
99 refreshModel();103 refreshModel();
100 });104 });
101 }105 }
@@ -122,6 +126,7 @@
122 episodeList.forceActiveFocus()126 episodeList.forceActiveFocus()
123 searchField.text = ""127 searchField.text = ""
124 episodesPage.state = "default"128 episodesPage.state = "default"
129 episodeList.positionViewAtBeginning()
125 }130 }
126 }131 }
127132
@@ -139,7 +144,36 @@
139 Connections {144 Connections {
140 target: downloader145 target: downloader
141 onDownloadingGuidChanged: {146 onDownloadingGuidChanged: {
142 loadEpisodes(episodeId, episodeArtist, episodeImage);147 var db = Podcasts.init();
148 db.transaction(function (tx) {
149 /*
150 If tempGuid is NULL, then the episode currently being downloaded is not found within
151 this podcast. On the other hand, if it is within this podcast, then update the episodeModel
152 with the downloadedfile location we just received from the downloader.
153 */
154 if (tempGuid != "NULL") {
155 var rs2 = tx.executeSql("SELECT downloadedfile, podcast FROM Episode WHERE guid=?", [tempGuid]);
156 for (var i=0; i<episodeModel.count; i++) {
157 if (episodeModel.get(i).guid == tempGuid) {
158 console.log("[LOG]: Setting episode download URL to " + rs2.rows.item(0).downloadedfile)
159 episodeModel.setProperty(i, "downloadedfile", rs2.rows.item(0).downloadedfile)
160 break
161 }
162 }
163 tempGuid = "NULL"
164 }
165
166 /*
167 Here it is checked if the currently downloaded episode belongs to the podcast
168 page being currently displayed. If it is, then the downloaded episode guid is
169 stored in the tempGuid variable to track it.
170 */
171 var rs = tx.executeSql("SELECT podcast FROM Episode WHERE guid=?", [downloader.downloadingGuid]);
172
173 if (downloader.downloadingGuid != "" && rs.rows.item(0).podcast == episodeId && tempGuid == "NULL") {
174 tempGuid = downloader.downloadingGuid
175 }
176 });
143 }177 }
144 }178 }
145179
@@ -155,12 +189,12 @@
155 onClicked: {189 onClicked: {
156 var db = Podcasts.init();190 var db = Podcasts.init();
157 db.transaction(function (tx) {191 db.transaction(function (tx) {
158 var rs = tx.executeSql("SELECT downloadedfile FROM Episode WHERE downloadedfile NOT NULL AND podcast=?", [episodeModel.pid]);192 var rs = tx.executeSql("SELECT downloadedfile FROM Episode WHERE downloadedfile NOT NULL AND podcast=?", [episodeId]);
159 for(var i = 0; i < rs.rows.length; i++) {193 for(var i = 0; i < rs.rows.length; i++) {
160 fileManager.deleteFile(rs.rows.item(i).downloadedfile);194 fileManager.deleteFile(rs.rows.item(i).downloadedfile);
161 }195 }
162 tx.executeSql("DELETE FROM Episode WHERE podcast=?", [episodeModel.pid]);196 tx.executeSql("DELETE FROM Episode WHERE podcast=?", [episodeId]);
163 tx.executeSql("DELETE FROM Podcast WHERE rowid=?", [episodeModel.pid]);197 tx.executeSql("DELETE FROM Podcast WHERE rowid=?", [episodeId]);
164 mainStack.pop()198 mainStack.pop()
165 PopupUtils.close(dialogInternal)199 PopupUtils.close(dialogInternal)
166 });200 });
@@ -187,9 +221,6 @@
187221
188 ListModel {222 ListModel {
189 id: episodeModel223 id: episodeModel
190 property string pid;
191 property string artist;
192 property string image;
193 }224 }
194225
195 SortFilterModel {226 SortFilterModel {
@@ -199,12 +230,118 @@
199 filter.pattern: RegExp(searchField.text, "gi")230 filter.pattern: RegExp(searchField.text, "gi")
200 }231 }
201232
202 ListView {233 function formatTime(seconds) {
234 var time = Podcasts.getTimeDiff(seconds)
235 var hour = time[0]
236 var minute = time[1]
237 // TRANSLATORS: the first argument is the number of hours,
238 // followed by minute (eg. 20h 3m)
239 if(hour > 0 && minute > 0) {
240 // xgettext: no-c-format
241 return (i18n.tr("%1 hr %2 min"))
242 .arg(hour)
243 .arg(minute)
244 }
245
246 // TRANSLATORS: this string indicates the number of hours
247 // eg. 20h (no plural state required)
248 else if(hour > 0 && minute === 0) {
249 // xgettext: no-c-format
250 return (i18n.tr("%1 hr"))
251 .arg(hour)
252 }
253
254 // TRANSLATORS: this string indicates the number of minutes
255 // eg. 15m (no plural state required)
256 else if(hour === 0 && minute > 0) {
257 // xgettext: no-c-format
258 return (i18n.tr("%1 min"))
259 .arg(minute)
260 }
261
262 else {
263 return Podcasts.formatTime(seconds)
264 }
265 }
266
267 UbuntuListView {
203 id: episodeList268 id: episodeList
204269
270 anchors.fill: parent
271 model: sortedEpisodeModel
272
205 clip: true273 clip: true
206 anchors.fill: parent274 section.property: "listened"
207 model: sortedEpisodeModel275 section.labelPositioning: ViewSection.InlineLabels
276
277 section.delegate: Rectangle {
278 width: parent.width
279 color: section === "0" ? podbird.theme.hightlightListView : "Transparent"
280 height: header.implicitHeight + units.gu(2)
281 Label {
282 id: header
283 anchors {
284 left: parent.left
285 right: parent.right
286 margins: units.gu(2)
287 verticalCenter: parent.verticalCenter
288 }
289 fontSize: "x-large"
290 text: section === "0" ? i18n.tr("Unheard") : i18n.tr("Listened")
291 }
292 }
293
294 header: BlurredBackground {
295 id: blurredBackground
296
297 art: episodeImage
298 width: parent.width
299 visible: episodesPage.state !== "search"
300 height: episodesPage.state !== "search" ? cover.height + units.gu(4) : 0
301 backgroundStrength: podbird.settings.themeName === "Light.qml" ? 0.3 : 0.6
302
303 Image {
304 id:cover
305 width: units.gu(12)
306 height: width
307 sourceSize.height: width
308 sourceSize.width: width
309 source: episodeImage
310 anchors {
311 left: parent.left
312 top: parent.top
313 margins: units.gu(2)
314 }
315 }
316
317 Column {
318 id: podcastTitle
319
320 anchors {
321 left: cover.right
322 right: parent.right
323 bottom: parent.bottom
324 margins: units.gu(2)
325 }
326
327 Label {
328 text: episodeName
329 width: parent.width
330 wrapMode: Text.WordWrap
331 maximumLineCount: 2
332 elide: Text.ElideRight
333 color: podbird.theme.baseText
334 }
335
336 Label {
337 text: i18n.tr("%1 episode", "%1 episodes", episodeList.count).arg(episodeList.count)
338 width: parent.width
339 elide: Text.ElideRight
340 fontSize: "x-small"
341 color: podbird.theme.baseText
342 }
343 }
344 }
208345
209 footer: Item {346 footer: Item {
210 width: parent.width347 width: parent.width
@@ -214,51 +351,41 @@
214 delegate: ListItem.Empty {351 delegate: ListItem.Empty {
215 id: listItem352 id: listItem
216353
217 property bool expanded: false354 property bool expanded
218355
219 width: parent.width356 height: dataColumn.height + units.gu(2)
220 height: mainColumn.height
221 highlightWhenPressed: false357 highlightWhenPressed: false
358 showDivider: false
222359
223 onClicked: listItem.expanded = !listItem.expanded360 onClicked: {
361 expanded = !expanded;
362 }
224363
225 Rectangle {364 Rectangle {
226 anchors.fill: parent365 visible: !model.listened
227 color: listItem.pressed ? podbird.theme.hightlightListView : "transparent"366 width: parent.width
367 height: dataColumn.height + units.gu(2)
368 color: podbird.theme.hightlightListView
228 }369 }
229370
230 Column {371 Column {
231 id: mainColumn372 id: dataColumn
232
233 anchors {
234 top: parent.top
235 left: parent.left
236 right: parent.right
237 margins: units.gu(2)
238 topMargin: units.gu(1)
239 }
240373
241 spacing: units.gu(1)374 spacing: units.gu(1)
375 anchors.left: parent.left
376 anchors.right: parent.right
377 anchors.margins: units.gu(2)
378 anchors.top: parent.top
379 anchors.topMargin: units.gu(0.5)
242380
243 RowLayout {381 RowLayout {
244 id: titleRow382 id: rowlayout
245383
246 width: parent.width384 width: parent.width
247 spacing: units.gu(2)385 height: titleColumn.height
248
249 Image {
250 id: imgFrame
251 width: units.gu(6)
252 height: width
253 sourceSize.height: width
254 sourceSize.width: width
255 source: model.image
256 }
257386
258 Column {387 Column {
259 id: detailColumn388 id: titleColumn
260
261 anchors.verticalCenter: imgFrame.verticalCenter
262 Layout.fillWidth: true389 Layout.fillWidth: true
263390
264 Label {391 Label {
@@ -267,19 +394,97 @@
267 maximumLineCount: 2394 maximumLineCount: 2
268 wrapMode: Text.WordWrap395 wrapMode: Text.WordWrap
269 elide: Text.ElideRight396 elide: Text.ElideRight
270 color: currentGuid === model.guid ? podbird.theme.focusText397 color: listItem.expanded || currentGuid === model.guid || downloader.downloadingGuid === model.guid ? podbird.theme.focusText
271 : podbird.theme.baseText398 : podbird.theme.baseText
272 }399 }
273400
274 Label {401 Label {
275 id: episodePublishDate402 id: episodePublishDate
276 width: parent.width403 width: parent.width
277 text: Qt.formatDate(new Date(model.published), "MMM d, yyyy")404 text: formatTime(model.duration) + " | " + Qt.formatDate(new Date(model.published), "MMM d, yyyy")
278 fontSize: "x-small"405 fontSize: "x-small"
279 color: currentGuid === model.guid ? podbird.theme.focusText
280 : podbird.theme.baseText
281 elide: Text.ElideRight406 elide: Text.ElideRight
282 }407 color: podbird.theme.baseSubText
408 }
409 }
410
411 ActionButton {
412 id: downloadButton
413
414 width: units.gu(5)
415 height: units.gu(4)
416
417 property bool queued: false
418
419 iconName: model.downloadedfile ? "delete" : (queued && downloader.downloadingGuid !== model.guid ? "history" : "save")
420 enabled: downloader.downloadingGuid !== model.guid
421 color: downloader.downloadingGuid === model.guid ? podbird.theme.focusText
422 : podbird.theme.baseText
423
424 onClicked: {
425 if (model.downloadedfile) {
426 fileManager.deleteFile(model.downloadedfile);
427 var db = Podcasts.init();
428 db.transaction(function (tx) {
429 tx.executeSql("UPDATE Episode SET downloadedfile = NULL WHERE guid = ?", [model.guid]);
430 });
431 episodeModel.setProperty(index, "downloadedfile", "")
432 downloadButton.queued = false
433 } else {
434 downloadButton.queued = true;
435 downloader.addDownload(model.guid, model.audiourl);
436 }
437 }
438 }
439
440 ActionButton {
441 width: units.gu(4)
442 height: units.gu(4)
443
444 iconName: player.playbackState === MediaPlayer.PlayingState && currentGuid === model.guid ? "media-playback-pause"
445 : "media-playback-start"
446 color: player.playbackState === MediaPlayer.PlayingState && currentGuid === model.guid ? podbird.theme.focusText
447 : podbird.theme.baseIcon
448
449 onClicked: {
450 var db = Podcasts.init();
451 db.transaction(function (tx) {
452 if (currentGuid === model.guid) {
453 if (player.playbackState === MediaPlayer.PlayingState) {
454 player.pause()
455 } else {
456 player.play()
457 }
458 } else {
459 currentGuid = "";
460 player.source = model.downloadedfile ? model.downloadedfile : model.audiourl;
461 var rs = tx.executeSql("SELECT position FROM Episode WHERE guid=?", [model.guid]);
462 player.play();
463 player.seek(rs.rows.item(0).position);
464 currentName = model.name;
465 currentArtist = model.artist;
466 currentImage = model.image;
467 currentGuid = model.guid;
468 }
469 });
470 }
471 }
472 }
473
474 Rectangle {
475 id: progressBar
476 radius: width/3
477 width: parent.width
478 height: units.dp(5)
479 color: Theme.palette.normal.base
480 visible: downloader.downloadingGuid === model.guid
481 Rectangle {
482 height: parent.height
483 radius: parent.radius
484 anchors.left: parent.left
485 anchors.top: parent.top
486 color: podbird.theme.focusText
487 width: downloader.progress > 0 ? Math.min((downloader.progress / 100) * parent.width, parent.width) : 0
283 }488 }
284 }489 }
285490
@@ -288,184 +493,15 @@
288 text: model.description493 text: model.description
289 textFormat: Text.RichText494 textFormat: Text.RichText
290 clip: true495 clip: true
291 height: listItem.expanded ? contentHeight : units.gu(4)496 height: listItem.expanded ? contentHeight : 0
292 wrapMode: Text.WordWrap497 wrapMode: Text.WordWrap
293 width: parent.width498 width: parent.width
294 elide: Text.ElideRight
295 fontSize: "small"499 fontSize: "small"
296 color: podbird.theme.baseSubText500 color: podbird.theme.baseSubText
297 Behavior on height {501 Behavior on height {
298 UbuntuNumberAnimation {502 UbuntuNumberAnimation {
299 duration: UbuntuAnimation.SlowDuration503 duration: UbuntuAnimation.BriskDuration
300 }504 }
301 }
302
303 }
304
305 Item {
306 id: statusBox
307
308 width: parent.width
309 height: units.gu(6)
310
311 function formatTime(seconds) {
312 var time = Podcasts.getTimeDiff(seconds)
313 var hour = time[0]
314 var minute = time[1]
315 // TRANSLATORS: the first argument is the number of hours,
316 // followed by minute (eg. 20h 3m)
317 if(hour > 0 && minute > 0) {
318 // xgettext: no-c-format
319 return (i18n.tr("%1h %2m"))
320 .arg(hour)
321 .arg(minute)
322 }
323
324 // TRANSLATORS: this string indicates the number of hours
325 // eg. 20h (no plural state required)
326 else if(hour > 0 && minute === 0) {
327 // xgettext: no-c-format
328 return (i18n.tr("%1h"))
329 .arg(hour)
330 }
331
332 // TRANSLATORS: this string indicates the number of minutes
333 // eg. 15m (no plural state required)
334 else if(hour === 0 && minute > 0) {
335 // xgettext: no-c-format
336 return (i18n.tr("%1m"))
337 .arg(minute)
338 }
339
340 else {
341 return Podcasts.formatTime(model.duration)
342 }
343 }
344
345 Rectangle {
346 id: listened
347 border.color: UbuntuColors.lightGrey
348 height: units.gu(2.5)
349 width: height
350 radius: width / 2
351 anchors.right: durationIcon.left
352 anchors.rightMargin: units.gu(2)
353 anchors.verticalCenter: actionRow.verticalCenter
354 visible: model.listened
355 Icon {
356 id: tick
357 name: "tick"
358 anchors.centerIn: parent
359 anchors.verticalCenterOffset: units.gu(0.1)
360 height: units.gu(1.4)
361 width: height
362 }
363 }
364
365 Icon {
366 id: durationIcon
367 width: units.gu(2.5)
368 height: width
369 name: "alarm-clock"
370 visible: duration.text !== ""
371 anchors.right: duration.left
372 anchors.rightMargin: units.gu(0.5)
373 anchors.verticalCenter: actionRow.verticalCenter
374 color: podbird.theme.baseIcon
375 }
376
377 Label {
378 id: duration
379 color: podbird.theme.baseText
380 anchors.right: parent.right
381 anchors.verticalCenter: durationIcon.verticalCenter
382 fontSize: "small"
383 text: !isNaN(model.duration) && model.duration !== 0 ? statusBox.formatTime(model.duration) : ""
384 }
385
386 Row {
387 id: actionRow
388
389 anchors.left: parent.left
390 anchors.leftMargin: units.gu(-1.5)
391
392 ActionButton {
393 width: units.gu(5)
394 height: units.gu(4)
395
396 iconName: player.playbackState === MediaPlayer.PlayingState && currentGuid === model.guid ? "media-playback-pause"
397 : "media-playback-start"
398
399 onClicked: {
400 var db = Podcasts.init();
401 db.transaction(function (tx) {
402 if (currentGuid === model.guid) {
403 if (player.playbackState === MediaPlayer.PlayingState) {
404 player.pause()
405 } else {
406 player.play()
407 }
408 } else {
409 currentGuid = "";
410 player.source = model.downloadedfile ? model.downloadedfile : model.audiourl;
411 var rs = tx.executeSql("SELECT position FROM Episode WHERE guid=?", [model.guid]);
412 player.play();
413 player.seek(rs.rows.item(0).position);
414 currentName = model.name;
415 currentArtist = model.artist;
416 currentImage = model.image;
417 currentGuid = model.guid;
418 }
419 });
420 }
421 }
422
423 ActionButton {
424 id: downloadButton
425
426 width: units.gu(5)
427 height: units.gu(4)
428
429 property bool queued: false
430
431 iconName: model.downloadedfile ? "delete" : (queued && downloader.downloadingGuid !== model.guid ? "history" : "save")
432 opacity: downloader.downloadingGuid === model.guid ? 0.4 : 1.0
433 enabled: downloader.downloadingGuid !== model.guid
434
435 ActivityIndicator {
436 anchors.centerIn: parent
437 visible: downloader.downloadingGuid === model.guid
438 running: visible
439 }
440
441 onClicked: {
442 if (model.downloadedfile) {
443 fileManager.deleteFile(model.downloadedfile);
444 var db = Podcasts.init();
445 db.transaction(function (tx) {
446 tx.executeSql("UPDATE Episode SET downloadedfile = NULL WHERE guid = ?", [model.guid]);
447 });
448 loadEpisodes(episodeModel.pid, episodeModel.artist, episodeModel.image);
449 } else {
450 downloadButton.queued = true;
451 downloader.addDownload(model.guid, model.audiourl);
452 }
453 }
454 }
455 }
456
457
458 ProgressBar {
459 visible: downloader.downloadingGuid === model.guid
460 minimumValue: 0
461 maximumValue: 100
462 anchors.left: actionRow.right
463 anchors.right: model.listened ? listened.left : durationIcon.left
464 anchors.leftMargin: units.gu(2)
465 anchors.rightMargin: units.gu(2)
466 anchors.verticalCenter: actionRow.verticalCenter
467 height: units.gu(2.6)
468 value: downloader.progress
469 }505 }
470 }506 }
471 }507 }
@@ -483,21 +519,27 @@
483519
484 function refreshModel() {520 function refreshModel() {
485 var db = Podcasts.init();521 var db = Podcasts.init();
486 loadEpisodes(episodeModel.pid, episodeModel.artist, episodeModel.image);522 loadEpisodes(episodeId, episodeArtist, episodeImage);
487 episodesUpdating = false;523 episodesUpdating = false;
488 }524 }
489525
490 function loadEpisodes(pid, artist, img) {526 function loadEpisodes(pid, artist, img) {
527 var i, episode;
528 var newCount = 0;
529
530 episodeModel.clear();
531
491 var db = Podcasts.init();532 var db = Podcasts.init();
492 db.transaction(function (tx) {533 db.transaction(function (tx) {
493 episodeModel.clear();
494 var rs = tx.executeSql("SELECT rowid, * FROM Episode WHERE podcast=? ORDER BY published DESC", [pid]);534 var rs = tx.executeSql("SELECT rowid, * FROM Episode WHERE podcast=? ORDER BY published DESC", [pid]);
495 for(var i = 0; i < rs.rows.length; i++) {535 for(i = 0; i < rs.rows.length; i++) {
496 var episode = rs.rows.item(i);536 episode = rs.rows.item(i);
497 episodeModel.pid = pid;537 if (!episode.listened) {
498 episodeModel.artist = artist;538 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});
499 episodeModel.image = img;539 newCount++;
500 episodeModel.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" : img, "artist" : artist, "audiourl" : episode.audiourl});540 } else {
541 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});
542 }
501 }543 }
502 });544 });
503 }545 }
504546
=== modified file 'app/ui/PlayerControls.qml'
--- app/ui/PlayerControls.qml 2015-03-08 11:10:07 +0000
+++ app/ui/PlayerControls.qml 2015-04-18 14:12:52 +0000
@@ -53,7 +53,7 @@
53 id: progressBarHint53 id: progressBarHint
54 anchors.left: parent.left54 anchors.left: parent.left
55 anchors.top: cover.bottom55 anchors.top: cover.bottom
56 color: UbuntuColors.orange56 color: podbird.theme.focusText
57 height: units.gu(0.25)57 height: units.gu(0.25)
58 width: player.duration > 0 ? (player.position / player.duration) * parent.width : 058 width: player.duration > 0 ? (player.position / player.duration) * parent.width : 0
59 }59 }
@@ -72,7 +72,7 @@
72 color: "white"72 color: "white"
73 elide: Text.ElideRight73 elide: Text.ElideRight
74 maximumLineCount: 274 maximumLineCount: 2
75 wrapMode: Text.WrapAnywhere75 wrapMode: Text.WordWrap
76 text: currentName76 text: currentName
77 }77 }
7878
7979
=== modified file 'app/ui/PodcastsTab.qml'
--- app/ui/PodcastsTab.qml 2015-03-29 15:33:26 +0000
+++ app/ui/PodcastsTab.qml 2015-04-18 14:12:52 +0000
@@ -186,13 +186,6 @@
186 filter.pattern: RegExp(searchField.text, "gi")186 filter.pattern: RegExp(searchField.text, "gi")
187 }187 }
188188
189 ListModel {
190 id: episodeModel
191 property string pid;
192 property string artist;
193 property string image;
194 }
195
196 ListView {189 ListView {
197 id: view190 id: view
198191
@@ -213,6 +206,7 @@
213 height: units.gu(8)206 height: units.gu(8)
214 removable: true207 removable: true
215 confirmRemoval: true208 confirmRemoval: true
209 showDivider: false
216 highlightWhenPressed: false210 highlightWhenPressed: false
217211
218 onItemRemoved: {212 onItemRemoved: {
@@ -230,7 +224,8 @@
230224
231 Rectangle {225 Rectangle {
232 anchors.fill: parent226 anchors.fill: parent
233 color: listItem.pressed ? podbird.theme.hightlightListView : "transparent"227 opacity: 0.3
228 color: index % 2 === 0 ? podbird.theme.hightlightListView : "Transparent"
234 }229 }
235230
236 onClicked: {231 onClicked: {
237232
=== renamed file 'app/ui/SearchTab.qml' => 'app/ui/SearchPage.qml'
--- app/ui/SearchTab.qml 2015-03-29 15:33:26 +0000
+++ app/ui/SearchPage.qml 2015-04-18 14:12:52 +0000
@@ -23,24 +23,64 @@
23import Ubuntu.Components.ListItems 1.0 as ListItem23import Ubuntu.Components.ListItems 1.0 as ListItem
24import "../podcasts.js" as Podcasts24import "../podcasts.js" as Podcasts
2525
26Tab {26Page {
27 title: i18n.tr("Find New Podcasts")27 id: searchPage
2828
29 property var xhr: new XMLHttpRequest;29 property var xhr: new XMLHttpRequest;
3030
31 page: Page {31 /*
32 Column {32 #FIXME: The following lines of code is necessary due to a upstream bug
33 spacing: units.gu(2)33 in the SDK http://pad.lv/1400297. This bug is still present in the rtm.
34 anchors.fill: parent34 Once it is fixed, this following property and connection can be remvoed.
35 anchors.topMargin: units.gu(2)35 */
3636 property Item __oldContents: null
37 TextField {37 Connections {
38 target: searchPage.head
39 onContentsChanged: {
40 if (searchPage.__oldContents) {
41 searchPage.__oldContents.parent = null;
42 }
43 searchPage.__oldContents = searchPage.head.contents;
44 }
45 }
46
47 state: "default"
48 states: [
49 PageHeadState {
50 name: "default"
51 head: searchPage.head
52 actions: [
53 Action {
54 iconName: "search"
55 text: i18n.tr("Search Episode")
56 onTriggered: {
57 searchPage.state = "search"
58 searchField.forceActiveFocus()
59 }
60 }
61 ]
62 },
63
64 PageHeadState {
65 name: "search"
66 head: searchPage.head
67 backAction: Action {
68 iconName: "back"
69 text: i18n.tr("Back")
70 onTriggered: {
71 resultsView.forceActiveFocus()
72 searchField.text = ""
73 searchPage.state = "default"
74 }
75 }
76
77 contents: TextField {
38 id: searchField78 id: searchField
39 anchors.left: parent.left79 inputMethodHints: Qt.ImhNoPredictiveText
40 anchors.right: parent.right
41 anchors.margins: units.gu(2)
42 placeholderText: i18n.tr("Search...")80 placeholderText: i18n.tr("Search...")
43 inputMethodHints: Qt.ImhNoPredictiveText;81 anchors.left: parent ? parent.left : undefined
82 anchors.right: parent ? parent.right : undefined
83 anchors.rightMargin: units.gu(2)
44 onTextChanged: {84 onTextChanged: {
45 if (text.length > 2) {85 if (text.length > 2) {
46 search(text)86 search(text)
@@ -49,95 +89,106 @@
49 }89 }
50 }90 }
51 }91 }
5292 }
53 ListView {93 ]
54 id: resultsView94
55 clip: true95 EmptyState {
56 width: parent.width96 anchors.centerIn: parent
57 model: searchResults97 anchors.verticalCenterOffset: Qt.inputMethod.visible ? units.gu(4) : 0
58 height: parent.height - searchField.height - units.gu(2)98 visible: searchResults.count === 0
5999 iconName: "search"
60 footer: Item {100 title: searchPage.state !== "search" ? i18n.tr("Looking for a new Podcast?") : i18n.tr("No Podcasts found")
61 width: parent.width101 subTitle: searchPage.state !== "search" ? i18n.tr("Click the 'magnifier' at the top to search.") : i18n.tr("No podcasts found matching the search term.")
62 height: units.gu(7)102 }
63 }103
64104 ListView {
65 delegate: ListItem.Empty {105 id: resultsView
66 id: listItem106
67107 clip: true
68 height: units.gu(8)108 model: searchResults
69 highlightWhenPressed: false109 anchors.fill: parent
70110
71 Rectangle {111 footer: Item {
72 anchors.fill: parent112 width: parent.width
73 color: listItem.pressed ? podbird.theme.hightlightListView : "transparent"113 height: units.gu(7)
74 }114 }
75115
76 RowLayout {116 delegate: ListItem.Empty {
77 id: titleRow117 id: listItem
78118
79 anchors.left: parent.left119 height: units.gu(8)
80 anchors.right: parent.right120 showDivider: false
81 anchors.margins: units.gu(2)121 highlightWhenPressed: false
82 anchors.verticalCenter: parent.verticalCenter122
83123 Rectangle {
84 spacing: units.gu(2)124 anchors.fill: parent
85125 opacity: 0.3
86 Image {126 color: index % 2 === 0 ? podbird.theme.hightlightListView : "Transparent"
87 id: imgFrame127 }
88 width: units.gu(6)128
89 height: width129 RowLayout {
90 sourceSize.height: width130 id: titleRow
91 sourceSize.width: width131
92 source: model.image132 anchors.left: parent.left
93 }133 anchors.right: parent.right
94134 anchors.margins: units.gu(2)
95 Column {135 anchors.verticalCenter: parent.verticalCenter
96 id: detailColumn136
97137 spacing: units.gu(2)
98 anchors.verticalCenter: imgFrame.verticalCenter138
99 Layout.fillWidth: true139 Image {
100140 id: imgFrame
101 Label {141 width: units.gu(6)
102 id: podcastTitle142 height: width
103 textFormat: Text.PlainText143 sourceSize.height: width
104 text: model.name144 sourceSize.width: width
105 width: parent.width145 source: model.image
106 fontSize: "small"146 }
107 elide: Text.ElideRight147
108 }148 Column {
109149 id: detailColumn
110 Label {150
111 id: episodeCount151 anchors.verticalCenter: imgFrame.verticalCenter
112 width: parent.width152 Layout.fillWidth: true
113 color: "#999999"153
114 text: model.artist154 Label {
115 fontSize: "x-small"155 id: podcastTitle
116 elide: Text.ElideRight156 textFormat: Text.PlainText
117 }157 text: model.name
118 }158 width: parent.width
119159 fontSize: "medium"
120 Button {160 elide: Text.ElideRight
121 anchors.right: parent.right161 }
122 text: i18n.tr("Subscribe")162
123 color: UbuntuColors.green163 Label {
124 onClicked: {164 id: episodeCount
125 Podcasts.subscribe(model.artist, model.name, model.feed, model.image);165 width: parent.width
126 imageDownloader.feed = model.feed;166 color: "#999999"
127 imageDownloader.download(model.image);167 text: model.artist
128 tabs.selectedTabIndex = 0;168 fontSize: "x-small"
129 searchField.text = ""169 elide: Text.ElideRight
130 }170 }
131 }171 }
132 }172
133 }173 Button {
134174 anchors.right: parent.right
135 Scrollbar {175 text: i18n.tr("Subscribe")
136 flickableItem: resultsView176 color: UbuntuColors.green
137 }177 onClicked: {
138178 Podcasts.subscribe(model.artist, model.name, model.feed, model.image);
139 }179 imageDownloader.feed = model.feed;
140 }180 imageDownloader.download(model.image);
181 tabs.selectedTabIndex = 0;
182 searchField.text = ""
183 }
184 }
185 }
186 }
187
188 Scrollbar {
189 flickableItem: resultsView
190 }
191
141 }192 }
142193
143 ListModel {194 ListModel {
144195
=== modified file 'po/com.mikeasoft.podbird.pot'
--- po/com.mikeasoft.podbird.pot 2015-04-16 10:18:41 +0000
+++ po/com.mikeasoft.podbird.pot 2015-04-18 14:12:52 +0000
@@ -8,7 +8,7 @@
8msgstr ""8msgstr ""
9"Project-Id-Version: \n"9"Project-Id-Version: \n"
10"Report-Msgid-Bugs-To: \n"10"Report-Msgid-Bugs-To: \n"
11"POT-Creation-Date: 2015-04-16 12:16+0200\n"11"POT-Creation-Date: 2015-04-16 12:43+0200\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"14"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,7 +18,11 @@
18"Content-Transfer-Encoding: 8bit\n"18"Content-Transfer-Encoding: 8bit\n"
19"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"19"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
2020
21#: ../app/podbird.qml:17821#: ../app/podbird.qml:173
22msgid "Find New Podcasts"
23msgstr ""
24
25#: ../app/podbird.qml:187
22msgid "Settings"26msgid "Settings"
23msgstr ""27msgstr ""
2428
@@ -59,7 +63,7 @@
59#: ../app/settings/DownloadSetting.qml:3463#: ../app/settings/DownloadSetting.qml:34
60#: ../app/settings/DownloadSetting.qml:3564#: ../app/settings/DownloadSetting.qml:35
61#: ../app/settings/DownloadSetting.qml:3665#: ../app/settings/DownloadSetting.qml:36
62#: ../app/settings/DownloadSetting.qml:3766#: ../app/settings/DownloadSetting.qml:37 ../app/ui/EpisodesPage.qml:337
63#, qt-format67#, qt-format
64msgid "%1 episode"68msgid "%1 episode"
65msgid_plural "%1 episodes"69msgid_plural "%1 episodes"
@@ -78,61 +82,73 @@
78msgid "Dark"82msgid "Dark"
79msgstr ""83msgstr ""
8084
81#: ../app/ui/EpisodesPage.qml:8585#: ../app/ui/EpisodesPage.qml:33
86msgid "Podcast"
87msgstr ""
88
89#: ../app/ui/EpisodesPage.qml:89 ../app/ui/SearchPage.qml:55
82msgid "Search Episode"90msgid "Search Episode"
83msgstr ""91msgstr ""
8492
85#: ../app/ui/EpisodesPage.qml:9493#: ../app/ui/EpisodesPage.qml:98
86msgid "Mark all listened"94msgid "Mark all listened"
87msgstr ""95msgstr ""
8896
89#: ../app/ui/EpisodesPage.qml:105 ../app/ui/EpisodesPage.qml:15397#: ../app/ui/EpisodesPage.qml:109 ../app/ui/EpisodesPage.qml:187
90msgid "Unsubscribe"98msgid "Unsubscribe"
91msgstr ""99msgstr ""
92100
93#: ../app/ui/EpisodesPage.qml:120 ../app/ui/PodcastsTab.qml:86101#: ../app/ui/EpisodesPage.qml:124 ../app/ui/PodcastsTab.qml:86
94#: ../app/ui/PodcastsTab.qml:109102#: ../app/ui/PodcastsTab.qml:109 ../app/ui/SearchPage.qml:69
95msgid "Back"103msgid "Back"
96msgstr ""104msgstr ""
97105
98#: ../app/ui/EpisodesPage.qml:131106#: ../app/ui/EpisodesPage.qml:136
99msgid "Search episode"107msgid "Search episode"
100msgstr ""108msgstr ""
101109
102#: ../app/ui/EpisodesPage.qml:150110#: ../app/ui/EpisodesPage.qml:184
103msgid "Unsubscribe Confirmation"111msgid "Unsubscribe Confirmation"
104msgstr ""112msgstr ""
105113
106#: ../app/ui/EpisodesPage.qml:151114#: ../app/ui/EpisodesPage.qml:185
107#, qt-format115#, qt-format
108msgid "Are you sure you want to unsubscribe from <b>%1</b>?"116msgid "Are you sure you want to unsubscribe from <b>%1</b>?"
109msgstr ""117msgstr ""
110118
111#: ../app/ui/EpisodesPage.qml:170119#: ../app/ui/EpisodesPage.qml:204
112msgid "Cancel"120msgid "Cancel"
113msgstr ""121msgstr ""
114122
115#: ../app/ui/EpisodesPage.qml:184123#: ../app/ui/EpisodesPage.qml:218
116msgid "No Episodes found"124msgid "No Episodes found"
117msgstr ""125msgstr ""
118126
119#: ../app/ui/EpisodesPage.qml:185127#: ../app/ui/EpisodesPage.qml:219
120msgid "No episodes found matching the search term."128msgid "No episodes found matching the search term."
121msgstr ""129msgstr ""
122130
123#: ../app/ui/EpisodesPage.qml:319131#: ../app/ui/EpisodesPage.qml:241
124#, no-c-format, qt-format132#, no-c-format, qt-format
125msgid "%1h %2m"133msgid "%1 hr %2 min"
126msgstr ""134msgstr ""
127135
128#: ../app/ui/EpisodesPage.qml:328136#: ../app/ui/EpisodesPage.qml:250
129#, no-c-format, qt-format137#, no-c-format, qt-format
130msgid "%1h"138msgid "%1 hr"
131msgstr ""139msgstr ""
132140
133#: ../app/ui/EpisodesPage.qml:336141#: ../app/ui/EpisodesPage.qml:258
134#, no-c-format, qt-format142#, no-c-format, qt-format
135msgid "%1m"143msgid "%1 min"
144msgstr ""
145
146#: ../app/ui/EpisodesPage.qml:290
147msgid "Unheard"
148msgstr ""
149
150#: ../app/ui/EpisodesPage.qml:290
151msgid "Listened"
136msgstr ""152msgstr ""
137153
138#: ../app/ui/NowPlayingPage.qml:28154#: ../app/ui/NowPlayingPage.qml:28
@@ -197,26 +213,34 @@
197"page to add some."213"page to add some."
198msgstr ""214msgstr ""
199215
200#: ../app/ui/PodcastsTab.qml:175216#: ../app/ui/PodcastsTab.qml:175 ../app/ui/SearchPage.qml:101
201msgid "No podcasts found matching the search term."217msgid "No podcasts found matching the search term."
202msgstr ""218msgstr ""
203219
204#: ../app/ui/PodcastsTab.qml:291220#: ../app/ui/PodcastsTab.qml:286
205#, qt-format221#, qt-format
206msgid "%1 unheard episode"222msgid "%1 unheard episode"
207msgid_plural "%1 unheard episodes"223msgid_plural "%1 unheard episodes"
208msgstr[0] ""224msgstr[0] ""
209msgstr[1] ""225msgstr[1] ""
210226
211#: ../app/ui/SearchTab.qml:27227#: ../app/ui/SearchPage.qml:80
212msgid "Find New Podcasts"
213msgstr ""
214
215#: ../app/ui/SearchTab.qml:42
216msgid "Search..."228msgid "Search..."
217msgstr ""229msgstr ""
218230
219#: ../app/ui/SearchTab.qml:122231#: ../app/ui/SearchPage.qml:100
232msgid "Looking for a new Podcast?"
233msgstr ""
234
235#: ../app/ui/SearchPage.qml:100
236msgid "No Podcasts found"
237msgstr ""
238
239#: ../app/ui/SearchPage.qml:101
240msgid "Click the 'magnifier' at the top to search."
241msgstr ""
242
243#: ../app/ui/SearchPage.qml:175
220msgid "Subscribe"244msgid "Subscribe"
221msgstr ""245msgstr ""
222246
@@ -323,6 +347,6 @@
323msgid "Finish"347msgid "Finish"
324msgstr ""348msgstr ""
325349
326#: /home/krnekhelesh/Documents/Ubuntu-Projects/MP-Reviews/builddir/build-5-auto-download-option-UbuntuSDK_for_armhf_GCC_ubuntu_sdk_14_10_utopic-Default/po/Podbird.desktop.in.h:1350#: /home/krnekhelesh/Documents/Ubuntu-Projects/MP-Reviews/builddir/build-5.5-kevin-episodes-design-UbuntuSDK_for_armhf_GCC_ubuntu_sdk_14_10_utopic-Default/po/Podbird.desktop.in.h:1
327msgid "Podbird"351msgid "Podbird"
328msgstr ""352msgstr ""
329353
=== modified file 'podbird.png'
330Binary files podbird.png 2015-03-30 00:28:42 +0000 and podbird.png 2015-04-18 14:12:52 +0000 differ354Binary files podbird.png 2015-03-30 00:28:42 +0000 and podbird.png 2015-04-18 14:12:52 +0000 differ

Subscribers

People subscribed via source and target branches