Merge lp:~nik90/podbird/improve-podcast-page into lp:podbird
- improve-podcast-page
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Michael Sheldon | ||||
Approved revision: | 20 | ||||
Merged at revision: | 10 | ||||
Proposed branch: | lp:~nik90/podbird/improve-podcast-page | ||||
Merge into: | lp:podbird | ||||
Diff against target: |
1238 lines (+681/-333) 6 files modified
app/podbird.qml (+45/-9) app/podcasts.js (+8/-0) app/ui/EpisodesPage.qml (+444/-0) app/ui/PodcastsTab.qml (+79/-262) app/ui/SearchTab.qml (+54/-38) po/com.mikeasoft.podbird.pot (+51/-24) |
||||
To merge this branch: | bzr merge lp:~nik90/podbird/improve-podcast-page | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Sheldon | Approve | ||
Review via email: mp+247523@code.launchpad.net |
Commit message
- Refined the designs of the Podcast, search and episodes page.
- Fixed episode time format to be more clear
- Episode title now wrap into max of 2 lines instead of eliding immediately
- Swipe delete podcasts
- Moved episode list of a podcast into its own page
- Added Pagestack to allow for separation of episode list into its own page for better code separation.
Description of the change
This MP implements the following,
- Refined the designs of the Podcast, search and episodes page.
- Fixed episode time format to be more clear
- Episode title now wrap into max of 2 lines instead of eliding immediately
- Swipe delete podcasts
- Moved episode list of a podcast into its own page
- Added Pagestack to allow for separation of episode list into its own page for better code separation.
Screenshots of the refined design can be seen at http://
NOTE: There are 2 things that require attention,
1. I have separated PodcastsTab.qml and EpisodesPage.qml to the best of my understanding. I would recommend you go through PodcastsTab.qml to check if anything else can be moved.
2. After removing the use of UbuntuShape, the image size shown does not seem to respect the height and width specified in the code. On the phone it looks good as shown in the screenshot link above. However on the emulator, the cover image looks bigger than expected. Not sure why this is the case. ---- FIXED
Nekhelesh Ramananthan (nik90) wrote : | # |
Michael Sheldon (michael-sheldon) wrote : | # |
Okay, I've set to work in progress, just set back to needs review when you're ready :)
Nekhelesh Ramananthan (nik90) wrote : | # |
Okay I am happy with how it has turned out. Let me know if you want me to tweak something to your liking.
Michael Sheldon (michael-sheldon) wrote : | # |
I like the new style, a couple of issue found while testing:
With that reorganisation you'll need to move the SingleDownload into main or similar, otherwise it'll get destroyed when the user leaves that page.
It looks like PNGs aren't getting scaled correctly (subscribe to http://
The author field in the search tab doesn't elide and overlaps the edge of the screen for long author names (e.g. try searching for "Science", and looking at the "Hidden Universe HD" entry)
Once those are fixed it should be good to go :).
- 14. By Nekhelesh Ramananthan
-
Fixed author field not eliding in the search page
- 15. By Nekhelesh Ramananthan
-
Fixed scaling of PNG Images by setting sourceSize explicitly
- 16. By Nekhelesh Ramananthan
-
Hide duration icon if duration is not available
- 17. By Nekhelesh Ramananthan
-
Fixed PNG Image scaling in the search page as well
- 18. By Nekhelesh Ramananthan
-
Moved SingleDownloader to podbird.qml to preserve its status regardless of which page the user navigates to.
Nekhelesh Ramananthan (nik90) wrote : | # |
> I like the new style, a couple of issue found while testing:
>
Awesome. Glad to hear. I thought perhaps I changed it too much without asking you.
> With that reorganisation you'll need to move the SingleDownload into main or
> similar, otherwise it'll get destroyed when the user leaves that page.
>
Ah yes, I moved it to podbird.qml alongside ImageDownloader. Fixed in rev 18
> It looks like PNGs aren't getting scaled correctly (subscribe to
> http://
> sourceSize for images (more memory efficient too)
>
By setting the sourceSize explicitly it now works correctly both on the phone and on the emulator for different podcasts I tried. Rev 15, 17
> The author field in the search tab doesn't elide and overlaps the edge of the
> screen for long author names (e.g. try searching for "Science", and looking at
> the "Hidden Universe HD" entry)
>
Fixed in rev 14
> Once those are fixed it should be good to go :).
I also fixed an issue where the duration icon was shown despite the duration being not available.
- 19. By Nekhelesh Ramananthan
-
Bah, missed one last episode artist eliding that I just fixed in this commit
- 20. By Nekhelesh Ramananthan
-
Added confirm delete dialog in epsiode page
Nekhelesh Ramananthan (nik90) wrote : | # |
Chloe reported in G+ that it is too easy to delete a podcast from the episode list page. So I added a dialog to confirm deletion. http://
Nekhelesh Ramananthan (nik90) wrote : | # |
Sry wrong link. Here is the correct one http://
Michael Sheldon (michael-sheldon) wrote : | # |
Looks good, thanks!
Preview Diff
1 | === modified file 'app/podbird.qml' |
2 | --- app/podbird.qml 2015-01-10 10:05:28 +0000 |
3 | +++ app/podbird.qml 2015-01-25 18:02:01 +0000 |
4 | @@ -37,6 +37,37 @@ |
5 | } |
6 | } |
7 | |
8 | + SingleDownload { |
9 | + id: downloader |
10 | + property var queue: [] |
11 | + property string downloadingGuid |
12 | + |
13 | + onFinished: { |
14 | + var db = Podcasts.init(); |
15 | + var finalLocation = fileManager.saveDownload(path); |
16 | + db.transaction(function (tx) { |
17 | + tx.executeSql("UPDATE Episode SET downloadedfile=? WHERE guid=?", [finalLocation, downloadingGuid]); |
18 | + queue.shift(); |
19 | + if (queue.length > 0) { |
20 | + downloadingGuid = queue[0][0]; |
21 | + download(queue[0][1]); |
22 | + } else { |
23 | + downloadingGuid = ""; |
24 | + } |
25 | + |
26 | + loadEpisodes(episodeModel.pid, episodeModel.artist, episodeModel.image); |
27 | + }); |
28 | + } |
29 | + |
30 | + function addDownload(guid, url) { |
31 | + queue.push([guid, url]); |
32 | + if (queue.length == 1) { |
33 | + downloadingGuid = guid; |
34 | + download(url); |
35 | + } |
36 | + } |
37 | + } |
38 | + |
39 | MediaPlayer { |
40 | id: player |
41 | onPositionChanged: { |
42 | @@ -54,15 +85,20 @@ |
43 | } |
44 | } |
45 | |
46 | - Tabs { |
47 | - id: tabs |
48 | - |
49 | - PodcastsTab { |
50 | - objectName: "podcastsTab" |
51 | - } |
52 | - |
53 | - SearchTab { |
54 | - objectName: "searchTab" |
55 | + PageStack { |
56 | + id: mainStack |
57 | + Component.onCompleted: push(tabs) |
58 | + Tabs { |
59 | + id: tabs |
60 | + |
61 | + PodcastsTab { |
62 | + id: podcastTab |
63 | + objectName: "podcastsTab" |
64 | + } |
65 | + |
66 | + SearchTab { |
67 | + objectName: "searchTab" |
68 | + } |
69 | } |
70 | } |
71 | |
72 | |
73 | === modified file 'app/podcasts.js' |
74 | --- app/podcasts.js 2015-01-04 10:20:41 +0000 |
75 | +++ app/podcasts.js 2015-01-25 18:02:01 +0000 |
76 | @@ -35,6 +35,14 @@ |
77 | }); |
78 | } |
79 | |
80 | +function getTimeDiff(time) { |
81 | + var hours, minutes; |
82 | + time = Math.floor(time / 60) |
83 | + minutes = time % 60 |
84 | + hours = Math.floor(time / 60) |
85 | + return [hours, minutes] |
86 | +} |
87 | + |
88 | function formatTime(seconds) { |
89 | var rem = seconds % 3600; |
90 | return Math.floor(seconds / 3600) + ":" + zeroFill(Math.floor(rem / 60), 2); |
91 | |
92 | === added file 'app/ui/EpisodesPage.qml' |
93 | --- app/ui/EpisodesPage.qml 1970-01-01 00:00:00 +0000 |
94 | +++ app/ui/EpisodesPage.qml 2015-01-25 18:02:01 +0000 |
95 | @@ -0,0 +1,444 @@ |
96 | +import QtQuick 2.0 |
97 | +import QtMultimedia 5.0 |
98 | +import Ubuntu.Components 1.1 |
99 | +import QtQuick.Layouts 1.1 |
100 | +import QtQuick.LocalStorage 2.0 |
101 | +import Ubuntu.DownloadManager 0.1 |
102 | +import Ubuntu.Components.Popups 1.0 |
103 | +import Ubuntu.Components.ListItems 1.0 as ListItem |
104 | +import "../podcasts.js" as Podcasts |
105 | + |
106 | +Page { |
107 | + id: episodesPage |
108 | + |
109 | + visible: false |
110 | + title: episodeName |
111 | + |
112 | + property string episodeName |
113 | + property string episodeId |
114 | + property string episodeArtist |
115 | + property string episodeImage |
116 | + |
117 | + property bool episodesUpdating: false; |
118 | + |
119 | + Component.onCompleted: { |
120 | + loadEpisodes(episodeId, episodeArtist, episodeImage) |
121 | + } |
122 | + |
123 | + head.contents: Label { |
124 | + text: title |
125 | + anchors.fill: parent |
126 | + anchors.margins: units.gu(0.5) |
127 | + verticalAlignment: Text.AlignVCenter |
128 | + |
129 | + fontSize: "x-large" |
130 | + fontSizeMode: Text.Fit |
131 | + |
132 | + maximumLineCount: 3 |
133 | + minimumPointSize: 8 |
134 | + elide: Text.Right |
135 | + wrapMode: Text.WordWrap |
136 | + } |
137 | + |
138 | + head.actions: [ |
139 | + Action { |
140 | + text: i18n.tr("Unsubscribe") |
141 | + iconName: "delete" |
142 | + onTriggered: { |
143 | + PopupUtils.open(confirmDeleteDialog); |
144 | + } |
145 | + } |
146 | + ] |
147 | + |
148 | + Component { |
149 | + id: confirmDeleteDialog |
150 | + Dialog { |
151 | + id: dialogInternal |
152 | + title: i18n.tr("Unsubscribe Confirmation") |
153 | + text: i18n.tr("Are you sure you want to unsubscribe from <b>%1</b>?").arg(episodesPage.episodeName) |
154 | + Button { |
155 | + text: i18n.tr("Yes") |
156 | + color: UbuntuColors.orange |
157 | + onClicked: { |
158 | + var db = Podcasts.init(); |
159 | + db.transaction(function (tx) { |
160 | + var rs = tx.executeSql("SELECT downloadedfile FROM Episode WHERE downloadedfile NOT NULL AND podcast=?", [episodeModel.pid]); |
161 | + for(var i = 0; i < rs.rows.length; i++) { |
162 | + fileManager.deleteFile(rs.rows.item(i).downloadedfile); |
163 | + } |
164 | + tx.executeSql("DELETE FROM Episode WHERE podcast=?", [episodeModel.pid]); |
165 | + tx.executeSql("DELETE FROM Podcast WHERE rowid=?", [episodeModel.pid]); |
166 | + mainStack.pop() |
167 | + PopupUtils.close(dialogInternal) |
168 | + }); |
169 | + } |
170 | + } |
171 | + Button { |
172 | + text: i18n.tr("No") |
173 | + color: UbuntuColors.green |
174 | + onClicked: { |
175 | + PopupUtils.close(dialogInternal) |
176 | + } |
177 | + } |
178 | + } |
179 | + } |
180 | + |
181 | + ListModel { |
182 | + id: episodeModel |
183 | + property string pid; |
184 | + property string artist; |
185 | + property string image; |
186 | + } |
187 | + |
188 | + ListView { |
189 | + id: episodeList |
190 | + |
191 | + clip: true |
192 | + anchors.fill: parent |
193 | + model: episodeModel |
194 | + |
195 | + footer: Item { |
196 | + width: parent.width |
197 | + height: units.gu(8) |
198 | + } |
199 | + |
200 | + delegate: ListItem.Empty { |
201 | + id: listItem |
202 | + |
203 | + property bool expanded: false |
204 | + |
205 | + width: parent.width |
206 | + height: mainColumn.height |
207 | + |
208 | + onClicked: listItem.expanded = !listItem.expanded |
209 | + |
210 | + Column { |
211 | + id: mainColumn |
212 | + |
213 | + anchors { |
214 | + top: parent.top |
215 | + left: parent.left |
216 | + right: parent.right |
217 | + margins: units.gu(2) |
218 | + topMargin: units.gu(1) |
219 | + } |
220 | + |
221 | + spacing: units.gu(1) |
222 | + |
223 | + RowLayout { |
224 | + id: titleRow |
225 | + |
226 | + width: parent.width |
227 | + spacing: units.gu(2) |
228 | + |
229 | + Image { |
230 | + id: imgFrame |
231 | + width: units.gu(6) |
232 | + height: width |
233 | + sourceSize.height: width |
234 | + sourceSize.width: width |
235 | + source: model.image |
236 | + } |
237 | + |
238 | + Column { |
239 | + id: detailColumn |
240 | + |
241 | + anchors.verticalCenter: imgFrame.verticalCenter |
242 | + Layout.fillWidth: true |
243 | + |
244 | + Label { |
245 | + textFormat: Text.PlainText |
246 | + text: model.name.trim() |
247 | + width: parent.width |
248 | + elide: Text.ElideRight |
249 | + } |
250 | + |
251 | + Label { |
252 | + id: episodeArtist |
253 | + width: parent.width |
254 | + text: model.artist |
255 | + fontSize: "small" |
256 | + elide: Text.ElideRight |
257 | + } |
258 | + } |
259 | + } |
260 | + |
261 | + Label { |
262 | + id: desc |
263 | + text: model.description |
264 | + textFormat: Text.RichText |
265 | + clip: true |
266 | + height: listItem.expanded ? contentHeight : units.gu(4) |
267 | + wrapMode: Text.WordWrap |
268 | + width: parent.width |
269 | + elide: Text.ElideRight |
270 | + fontSize: "small" |
271 | + color: "#999999" |
272 | + Behavior on height { |
273 | + UbuntuNumberAnimation { |
274 | + duration: UbuntuAnimation.SlowDuration |
275 | + } |
276 | + } |
277 | + |
278 | + } |
279 | + |
280 | + Item { |
281 | + id: statusBox |
282 | + |
283 | + width: parent.width |
284 | + height: units.gu(6) |
285 | + |
286 | + function formatTime(seconds) { |
287 | + var time = Podcasts.getTimeDiff(seconds) |
288 | + var hour = time[0] |
289 | + var minute = time[1] |
290 | + if(hour > 0 && minute > 0) { |
291 | + return (i18n.tr("%1h %2m")) |
292 | + .arg(hour) |
293 | + .arg(minute) |
294 | + } |
295 | + |
296 | + else if(hour > 0 && minute === 0) { |
297 | + return (i18n.tr("%1h")) |
298 | + .arg(hour) |
299 | + } |
300 | + |
301 | + else if(hour === 0 && minute > 0) { |
302 | + return (i18n.tr("%1m")) |
303 | + .arg(minute) |
304 | + } |
305 | + |
306 | + else { |
307 | + return Podcasts.formatTime(model.duration) |
308 | + } |
309 | + } |
310 | + |
311 | + Rectangle { |
312 | + id: listened |
313 | + border.color: UbuntuColors.lightGrey |
314 | + height: units.gu(2.5) |
315 | + width: height |
316 | + radius: width / 2 |
317 | + anchors.right: durationIcon.left |
318 | + anchors.rightMargin: units.gu(2) |
319 | + visible: model.listened |
320 | + Icon { |
321 | + id: tick |
322 | + name: "tick" |
323 | + anchors.centerIn: parent |
324 | + anchors.verticalCenterOffset: units.gu(0.1) |
325 | + height: units.gu(1.4) |
326 | + width: height |
327 | + } |
328 | + } |
329 | + |
330 | + Icon { |
331 | + id: durationIcon |
332 | + width: units.gu(2.5) |
333 | + height: width |
334 | + name: "alarm-clock" |
335 | + visible: duration.text !== "" |
336 | + anchors.right: duration.left |
337 | + anchors.rightMargin: units.gu(0.5) |
338 | + } |
339 | + |
340 | + Label { |
341 | + id: duration |
342 | + anchors.right: parent.right |
343 | + anchors.verticalCenter: durationIcon.verticalCenter |
344 | + fontSize: "small" |
345 | + text: !isNaN(model.duration) && model.duration !== 0 ? statusBox.formatTime(model.duration) : "" |
346 | + } |
347 | + |
348 | + Row { |
349 | + id: actionRow |
350 | + |
351 | + spacing: units.gu(2) |
352 | + anchors.left: parent.left |
353 | + |
354 | + Icon { |
355 | + id: playButton |
356 | + name: player.playbackState === MediaPlayer.PlayingState && currentGuid === model.guid ? "media-playback-pause" |
357 | + : "media-playback-start" |
358 | + width: units.gu(2.5) |
359 | + height: width |
360 | + MouseArea { |
361 | + anchors.fill: parent |
362 | + |
363 | + onClicked: { |
364 | + var db = Podcasts.init(); |
365 | + db.transaction(function (tx) { |
366 | + if (currentGuid === model.guid) { |
367 | + if (player.playbackState === MediaPlayer.PlayingState) { |
368 | + player.pause() |
369 | + } else { |
370 | + player.play() |
371 | + } |
372 | + } else { |
373 | + currentGuid = ""; |
374 | + player.source = model.downloadedfile ? model.downloadedfile : model.audiourl; |
375 | + var rs = tx.executeSql("SELECT position FROM Episode WHERE guid=?", [model.guid]); |
376 | + player.play(); |
377 | + player.seek(rs.rows.item(0).position); |
378 | + currentName = model.name; |
379 | + currentArtist = model.artist; |
380 | + currentImage = model.image; |
381 | + currentGuid = model.guid; |
382 | + } |
383 | + }); |
384 | + } |
385 | + } |
386 | + } |
387 | + |
388 | + Item { |
389 | + id: downloadButton |
390 | + |
391 | + width: units.gu(2.5) |
392 | + height: width |
393 | + |
394 | + ActivityIndicator { |
395 | + anchors.centerIn: parent |
396 | + visible: downloader.downloadingGuid === model.guid |
397 | + running: visible |
398 | + } |
399 | + |
400 | + Icon { |
401 | + anchors.fill: parent |
402 | + property bool queued: false; |
403 | + name: model.downloadedfile ? "delete" : (queued && downloader.downloadingGuid !== model.guid ? "history" : "save") |
404 | + width: units.gu(4) |
405 | + height: width |
406 | + opacity: downloader.downloadingGuid === model.guid ? 0.4 : 1.0 |
407 | + |
408 | + MouseArea { |
409 | + anchors.fill: parent |
410 | + enabled: downloader.downloadingGuid !== model.guid |
411 | + |
412 | + onClicked: { |
413 | + if (model.downloadedfile) { |
414 | + fileManager.deleteFile(model.downloadedfile); |
415 | + var db = Podcasts.init(); |
416 | + db.transaction(function (tx) { |
417 | + tx.executeSql("UPDATE Episode SET downloadedfile = NULL WHERE guid = ?", [model.guid]); |
418 | + }); |
419 | + loadEpisodes(episodeModel.pid, episodeModel.artist, episodeModel.image); |
420 | + } else { |
421 | + parent.queued = true; |
422 | + downloader.addDownload(model.guid, model.audiourl); |
423 | + } |
424 | + } |
425 | + } |
426 | + } |
427 | + } |
428 | + } |
429 | + } |
430 | + } |
431 | + } |
432 | + |
433 | + PullToRefresh { |
434 | + refreshing: episodesUpdating |
435 | + onRefresh: updateEpisodes(); |
436 | + } |
437 | + } |
438 | + |
439 | + function refreshModel() { |
440 | + var db = Podcasts.init(); |
441 | + loadEpisodes(episodeModel.pid, episodeModel.artist, episodeModel.image); |
442 | + episodesUpdating = false; |
443 | + } |
444 | + |
445 | + function loadEpisodes(pid, artist, img) { |
446 | + var db = Podcasts.init(); |
447 | + db.transaction(function (tx) { |
448 | + episodeModel.clear(); |
449 | + var rs = tx.executeSql("SELECT rowid, * FROM Episode WHERE podcast=? ORDER BY published DESC", [pid]); |
450 | + for(var i = 0; i < rs.rows.length; i++) { |
451 | + var episode = rs.rows.item(i); |
452 | + episodeModel.pid = pid; |
453 | + episodeModel.artist = artist; |
454 | + episodeModel.image = img; |
455 | + episodeModel.append({"guid" : episode.guid, "listened" : episode.listened, "name" : episode.name, "description" : episode.description, "duration" : episode.duration, "position" : episode.position, "downloadedfile" : episode.downloadedfile, "image" : img, "artist" : artist, "audiourl" : episode.audiourl}); |
456 | + } |
457 | + }); |
458 | + } |
459 | + |
460 | + function updateEpisodes() { |
461 | + var db = Podcasts.init(); |
462 | + episodesUpdating = true; |
463 | + db.transaction(function(tx) { |
464 | + var rs = tx.executeSql("SELECT rowid, feed FROM Podcast"); |
465 | + tx.executeSql("UPDATE Podcast SET lastupdate=CURRENT_TIMESTAMP"); |
466 | + var xhr = []; |
467 | + for(var i = 0; i < rs.rows.length; i++) { |
468 | + (function (i) { |
469 | + xhr[i] = new XMLHttpRequest; |
470 | + var url = rs.rows.item(i).feed; |
471 | + var pid = rs.rows.item(i).rowid; |
472 | + xhr[i].open("GET", url); |
473 | + xhr[i].onreadystatechange = function() { |
474 | + if (xhr[i].readyState === XMLHttpRequest.DONE) { |
475 | + var e = xhr[i].responseXML.documentElement; |
476 | + for(var h = 0; h < e.childNodes.length; h++) { |
477 | + if(e.childNodes[h].nodeName === "channel") { |
478 | + var c = e.childNodes[h]; |
479 | + for(var j = 0; j < c.childNodes.length; j++) { |
480 | + if(c.childNodes[j].nodeName === "item") { |
481 | + var t = c.childNodes[j]; |
482 | + var track = {} |
483 | + for(var k = 0; k < t.childNodes.length; k++) { |
484 | + try { |
485 | + var nodeName = t.childNodes[k].nodeName.toLowerCase(); |
486 | + if (nodeName === "title") track['name'] = t.childNodes[k].childNodes[0].nodeValue; |
487 | + else if (nodeName === "description") track['description'] = t.childNodes[k].childNodes[0].nodeValue; |
488 | + else if (nodeName === "guid") track['guid'] = t.childNodes[k].childNodes[0].nodeValue; |
489 | + else if (nodeName === "pubdate") track['published'] = new Date(t.childNodes[k].childNodes[0].nodeValue).getTime(); |
490 | + else if (nodeName === "duration") { |
491 | + var dur = t.childNodes[k].childNodes[0].nodeValue.split(":"); |
492 | + if (dur.length === 1) { |
493 | + track['duration'] = parseInt(dur[0]); |
494 | + } else if (dur.length === 2) { |
495 | + track['duration'] = parseInt(dur[0]) * 60 + parseInt(dur[1]); |
496 | + } else if (dur.length === 3) { |
497 | + track['duration'] = parseInt(dur[0]) * 3600 + parseInt(dur[1]) * 60 + parseInt(dur[2]); |
498 | + } |
499 | + } else if (nodeName === "enclosure") { |
500 | + var el = t.childNodes[k]; |
501 | + for (var l = 0; l < el.attributes.length; l++) { |
502 | + if(el.attributes[l].nodeName === "url") track['audiourl'] = el.attributes[l].nodeValue; |
503 | + } |
504 | + } |
505 | + } catch(err) { |
506 | + console.debug(err.message); |
507 | + } |
508 | + } |
509 | + if (!track.hasOwnProperty("guid")) { |
510 | + track['guid'] = track.audiourl; |
511 | + } |
512 | + |
513 | + db.transaction(function(tx2) { |
514 | + var ers = tx2.executeSql("SELECT rowid FROM Episode WHERE guid=?", [track.guid]); |
515 | + if (ers.rows.length === 0) { |
516 | + tx2.executeSql("INSERT INTO Episode(podcast, name, description, audiourl, guid, listened, duration, published) VALUES(?, ?, ? , ?, ?, ?, ?, ?)", [pid, |
517 | + track.name, |
518 | + track.description, |
519 | + track.audiourl, |
520 | + track.guid, |
521 | + false, |
522 | + track.duration, |
523 | + track.published]); |
524 | + } |
525 | + }); |
526 | + } |
527 | + } |
528 | + } |
529 | + } |
530 | + } |
531 | + refreshModel(); |
532 | + } |
533 | + xhr[i].send(); |
534 | + |
535 | + })(i); |
536 | + } |
537 | + }); |
538 | + } |
539 | +} |
540 | |
541 | === modified file 'app/ui/PodcastsTab.qml' |
542 | --- app/ui/PodcastsTab.qml 2015-01-24 22:45:46 +0000 |
543 | +++ app/ui/PodcastsTab.qml 2015-01-25 18:02:01 +0000 |
544 | @@ -18,15 +18,19 @@ |
545 | |
546 | import QtQuick 2.0 |
547 | import QtMultimedia 5.0 |
548 | +import QtQuick.Layouts 1.1 |
549 | import QtQuick.LocalStorage 2.0 |
550 | import Ubuntu.Components 1.1 |
551 | import Ubuntu.DownloadManager 0.1 |
552 | +import Ubuntu.Components.ListItems 1.0 as ListItem |
553 | import Ubuntu.Components.Popups 1.0 |
554 | import "../podcasts.js" as Podcasts |
555 | |
556 | Tab { |
557 | id: tab |
558 | + |
559 | title: i18n.tr("Podcasts") |
560 | + |
561 | property bool episodesUpdating: false; |
562 | property bool addPodcast: false; |
563 | |
564 | @@ -35,41 +39,10 @@ |
565 | Action { |
566 | text: i18n.tr("Add Podcast") |
567 | iconName: "add" |
568 | - visible: view.model === podcastModel && !addPodcast |
569 | + visible: !addPodcast |
570 | onTriggered: { |
571 | addPodcast = true; |
572 | } |
573 | - }, |
574 | - |
575 | - Action { |
576 | - text: i18n.tr("Up") |
577 | - iconName: "up" |
578 | - visible: view.model === episodeModel |
579 | - onTriggered: { |
580 | - page.title = i18n.tr("Podcasts"); |
581 | - view.model = podcastModel; |
582 | - refreshModel(); |
583 | - } |
584 | - }, |
585 | - |
586 | - Action { |
587 | - text: i18n.tr("Unsubscribe") |
588 | - iconName: "delete" |
589 | - visible: view.model === episodeModel |
590 | - onTriggered: { |
591 | - var db = Podcasts.init(); |
592 | - db.transaction(function (tx) { |
593 | - var rs = tx.executeSql("SELECT downloadedfile FROM Episode WHERE downloadedfile NOT NULL AND podcast=?", [episodeModel.pid]); |
594 | - for(var i = 0; i < rs.rows.length; i++) { |
595 | - fileManager.deleteFile(rs.rows.item(i).downloadedfile); |
596 | - } |
597 | - tx.executeSql("DELETE FROM Episode WHERE podcast=?", [episodeModel.pid]); |
598 | - tx.executeSql("DELETE FROM Podcast WHERE rowid=?", [episodeModel.pid]); |
599 | - page.title = i18n.tr("Podcasts"); |
600 | - view.model = podcastModel; |
601 | - refreshModel(); |
602 | - }); |
603 | - } |
604 | } |
605 | ] |
606 | |
607 | @@ -79,37 +52,6 @@ |
608 | } |
609 | } |
610 | |
611 | - SingleDownload { |
612 | - id: downloader |
613 | - property var queue: [] |
614 | - property string downloadingGuid |
615 | - |
616 | - onFinished: { |
617 | - var db = Podcasts.init(); |
618 | - var finalLocation = fileManager.saveDownload(path); |
619 | - db.transaction(function (tx) { |
620 | - tx.executeSql("UPDATE Episode SET downloadedfile=? WHERE guid=?", [finalLocation, downloadingGuid]); |
621 | - queue.shift(); |
622 | - if (queue.length > 0) { |
623 | - downloadingGuid = queue[0][0]; |
624 | - download(queue[0][1]); |
625 | - } else { |
626 | - downloadingGuid = ""; |
627 | - } |
628 | - |
629 | - loadEpisodes(episodeModel.pid, episodeModel.artist, episodeModel.image); |
630 | - }); |
631 | - } |
632 | - |
633 | - function addDownload(guid, url) { |
634 | - queue.push([guid, url]); |
635 | - if (queue.length == 1) { |
636 | - downloadingGuid = guid; |
637 | - download(url); |
638 | - } |
639 | - } |
640 | - } |
641 | - |
642 | Component { |
643 | id: subscribeFailedDialog |
644 | Dialog { |
645 | @@ -144,196 +86,90 @@ |
646 | |
647 | ListView { |
648 | id: view |
649 | + |
650 | + clip: true |
651 | + model: podcastModel |
652 | anchors.fill: parent |
653 | - anchors.margins: units.gu(2) |
654 | - anchors.bottomMargin: 0 |
655 | - model: podcastModel |
656 | - clip: true |
657 | - spacing: units.gu(1) |
658 | + |
659 | footer: Item { |
660 | width: parent.width |
661 | height: units.gu(8) |
662 | } |
663 | |
664 | - delegate: Rectangle { |
665 | + delegate: ListItem.Empty { |
666 | id: listItem |
667 | - height: Math.max(imgFrame.height, detailCol.height) |
668 | - width: parent.width |
669 | - color: Theme.palette.normal.background |
670 | - property bool expanded: false; |
671 | - |
672 | - MouseArea { |
673 | - anchors.fill: parent |
674 | - onClicked: { |
675 | - if (view.model === podcastModel) { |
676 | - page.title = model.name |
677 | - loadEpisodes(model.id, model.artist, model.image) |
678 | - view.model = episodeModel |
679 | - } else { |
680 | - listItem.expanded = !listItem.expanded |
681 | + |
682 | + property bool expanded: false |
683 | + |
684 | + height: units.gu(8) |
685 | + removable: true |
686 | + confirmRemoval: true |
687 | + onItemRemoved: { |
688 | + var db = Podcasts.init(); |
689 | + db.transaction(function (tx) { |
690 | + var rs = tx.executeSql("SELECT downloadedfile FROM Episode WHERE downloadedfile NOT NULL AND podcast=?", [model.id]); |
691 | + for(var i = 0; i < rs.rows.length; i++) { |
692 | + fileManager.deleteFile(rs.rows.item(i).downloadedfile); |
693 | } |
694 | - } |
695 | - } |
696 | - |
697 | - UbuntuShape { |
698 | - id: imgFrame |
699 | - width: units.gu(9.1) |
700 | - height: width |
701 | + tx.executeSql("DELETE FROM Episode WHERE podcast=?", [model.id]); |
702 | + tx.executeSql("DELETE FROM Podcast WHERE rowid=?", [model.id]); |
703 | + refreshModel() |
704 | + }); |
705 | + } |
706 | + |
707 | + onClicked: { |
708 | + mainStack.push(Qt.resolvedUrl("EpisodesPage.qml"), {"episodeName": model.name, "episodeId": model.id, "episodeArtist": model.artist, "episodeImage": model.image}) |
709 | + } |
710 | + |
711 | + Column { |
712 | + id: mainColumn |
713 | |
714 | anchors.left: parent.left |
715 | - image: Image { |
716 | - source: model.image |
717 | - } |
718 | - } |
719 | - |
720 | - Column { |
721 | - id: detailCol |
722 | - anchors.left: imgFrame.right |
723 | - anchors.leftMargin: units.gu(2) |
724 | anchors.right: parent.right |
725 | - anchors.rightMargin: units.gu(2) |
726 | - spacing: units.gu(0.5) |
727 | - |
728 | - Row { |
729 | - width: parent.width |
730 | - spacing: units.gu(1) |
731 | - |
732 | - Label { |
733 | - textFormat: Text.PlainText |
734 | - text: model.name.trim() |
735 | - width: parent.width - episodeCount.width - units.gu(1) |
736 | - elide: Text.ElideRight |
737 | - } |
738 | - |
739 | - Label { |
740 | - id: episodeCount |
741 | - width: units.gu(4) |
742 | - visible: view.model === episodeModel || model.episodeCount > 0 |
743 | - text: view.model === episodeModel ? (!isNaN(model.duration) && model.duration !== 0 ? Podcasts.formatTime(model.duration) : "") : model.episodeCount |
744 | - horizontalAlignment: Text.AlignRight |
745 | - fontSize: "small" |
746 | - } |
747 | - } |
748 | - |
749 | - Row { |
750 | - width: parent.width |
751 | - spacing: units.gu(1) |
752 | - |
753 | - Label { |
754 | - id: desc |
755 | - text: view.model === episodeModel ? model.description : model.artist |
756 | - textFormat: Text.RichText |
757 | - clip: true |
758 | - height: listItem.expanded ? contentHeight : units.gu(2) |
759 | - wrapMode: Text.WordWrap |
760 | - width: parent.width - listened.width - units.gu(1) |
761 | - elide: Text.ElideRight |
762 | - fontSize: "small" |
763 | - |
764 | - Behavior on height { |
765 | - UbuntuNumberAnimation { |
766 | - duration: UbuntuAnimation.SlowDuration |
767 | - } |
768 | - } |
769 | - |
770 | - } |
771 | - |
772 | - Rectangle { |
773 | - id: listened |
774 | - border.color: UbuntuColors.lightGrey |
775 | - height: units.gu(2) |
776 | - width: height |
777 | - radius: width / 2 |
778 | - visible: view.model === episodeModel && model.listened |
779 | - Icon { |
780 | - id: tick |
781 | - name: "tick" |
782 | - anchors.centerIn: parent |
783 | - anchors.verticalCenterOffset: units.gu(0.1) |
784 | - height: units.gu(1.4) |
785 | - width: height |
786 | - } |
787 | - } |
788 | - } |
789 | - |
790 | - Row { |
791 | + anchors.margins: units.gu(2) |
792 | + anchors.verticalCenter: parent.verticalCenter |
793 | + spacing: units.gu(1) |
794 | + |
795 | + RowLayout { |
796 | + id: titleRow |
797 | + |
798 | width: parent.width |
799 | spacing: units.gu(2) |
800 | - Icon { |
801 | - name: player.playbackState === MediaPlayer.PlayingState && currentGuid === model.guid ? "media-playback-pause" |
802 | - : "media-playback-start" |
803 | - visible: view.model === episodeModel |
804 | - width: units.gu(4) |
805 | + |
806 | + Image { |
807 | + id: imgFrame |
808 | + width: units.gu(5) |
809 | height: width |
810 | - MouseArea { |
811 | - anchors.fill: parent |
812 | - |
813 | - onClicked: { |
814 | - var db = Podcasts.init(); |
815 | - db.transaction(function (tx) { |
816 | - if (currentGuid === model.guid) { |
817 | - if (player.playbackState === MediaPlayer.PlayingState) { |
818 | - player.pause() |
819 | - } else { |
820 | - player.play() |
821 | - } |
822 | - } else { |
823 | - currentGuid = ""; |
824 | - player.source = model.downloadedfile ? model.downloadedfile : model.audiourl; |
825 | - var rs = tx.executeSql("SELECT position FROM Episode WHERE guid=?", [model.guid]); |
826 | - player.play(); |
827 | - player.seek(rs.rows.item(0).position); |
828 | - currentName = model.name; |
829 | - currentArtist = model.artist; |
830 | - currentImage = model.image; |
831 | - currentGuid = model.guid; |
832 | - } |
833 | - }); |
834 | - } |
835 | - } |
836 | + sourceSize.height: width |
837 | + sourceSize.width: width |
838 | + source: model.image |
839 | } |
840 | |
841 | - Item { |
842 | - width: units.gu(4) |
843 | - height: width |
844 | - |
845 | - ActivityIndicator { |
846 | - anchors.centerIn: parent |
847 | - visible: downloader.downloadingGuid === model.guid |
848 | - running: visible |
849 | + Column { |
850 | + id: detailColumn |
851 | + |
852 | + anchors.verticalCenter: imgFrame.verticalCenter |
853 | + Layout.fillWidth: true |
854 | + |
855 | + Label { |
856 | + id: podcastTitle |
857 | + textFormat: Text.PlainText |
858 | + text: model.name.trim() |
859 | + width: parent.width |
860 | + fontSize: "small" |
861 | + elide: Text.ElideRight |
862 | } |
863 | |
864 | - Icon { |
865 | - anchors.fill: parent |
866 | - property bool queued: false; |
867 | - name: model.downloadedfile ? "delete" : (queued && downloader.downloadingGuid !== model.guid ? "history" : "save") |
868 | - width: units.gu(4) |
869 | - height: width |
870 | - visible: view.model === episodeModel |
871 | - opacity: downloader.downloadingGuid === model.guid ? 0.4 : 1.0 |
872 | - |
873 | - MouseArea { |
874 | - anchors.fill: parent |
875 | - enabled: downloader.downloadingGuid !== model.guid |
876 | - |
877 | - onClicked: { |
878 | - if (model.downloadedfile) { |
879 | - fileManager.deleteFile(model.downloadedfile); |
880 | - var db = Podcasts.init(); |
881 | - db.transaction(function (tx) { |
882 | - tx.executeSql("UPDATE Episode SET downloadedfile = NULL WHERE guid = ?", [model.guid]); |
883 | - }); |
884 | - loadEpisodes(episodeModel.pid, episodeModel.artist, episodeModel.image); |
885 | - } else { |
886 | - parent.queued = true; |
887 | - downloader.addDownload(model.guid, model.audiourl); |
888 | - } |
889 | - } |
890 | - } |
891 | + Label { |
892 | + id: episodeCount |
893 | + width: parent.width |
894 | + color: "#999999" |
895 | + visible: model.episodeCount > 0 |
896 | + text: model.episodeCount + " Episodes" |
897 | + fontSize: "x-small" |
898 | } |
899 | } |
900 | } |
901 | - |
902 | } |
903 | } |
904 | |
905 | @@ -427,41 +263,22 @@ |
906 | function refreshModel() { |
907 | var db = Podcasts.init(); |
908 | |
909 | - if (view.model === podcastModel) { |
910 | - db.transaction(function (tx) { |
911 | - podcastModel.clear(); |
912 | - var rs = tx.executeSql("SELECT rowid, * FROM Podcast ORDER BY name ASC"); |
913 | - for(var i = 0; i < rs.rows.length; i++) { |
914 | - var podcast = rs.rows.item(i); |
915 | - var rs2 = tx.executeSql("SELECT Count(*) AS epcount FROM Episode WHERE podcast=? AND NOT listened", [rs.rows.item(i).rowid]); |
916 | - podcastModel.append({"id" : podcast.rowid, "name" : podcast.name, "artist" : podcast.artist, "image" : podcast.image, "episodeCount" : rs2.rows.item(0).epcount}); |
917 | - if (podcast.lastupdate === null && !episodesUpdating) { |
918 | - updateEpisodes(); |
919 | - } |
920 | + db.transaction(function (tx) { |
921 | + podcastModel.clear(); |
922 | + var rs = tx.executeSql("SELECT rowid, * FROM Podcast ORDER BY name ASC"); |
923 | + for(var i = 0; i < rs.rows.length; i++) { |
924 | + var podcast = rs.rows.item(i); |
925 | + var rs2 = tx.executeSql("SELECT Count(*) AS epcount FROM Episode WHERE podcast=? AND NOT listened", [rs.rows.item(i).rowid]); |
926 | + podcastModel.append({"id" : podcast.rowid, "name" : podcast.name, "artist" : podcast.artist, "image" : podcast.image, "episodeCount" : rs2.rows.item(0).epcount}); |
927 | + if (podcast.lastupdate === null && !episodesUpdating) { |
928 | + updateEpisodes(); |
929 | } |
930 | - }); |
931 | - } else { |
932 | - loadEpisodes(episodeModel.pid, episodeModel.artist, episodeModel.image); |
933 | - } |
934 | + } |
935 | + }); |
936 | |
937 | episodesUpdating = false; |
938 | } |
939 | |
940 | - function loadEpisodes(pid, artist, img) { |
941 | - var db = Podcasts.init(); |
942 | - db.transaction(function (tx) { |
943 | - episodeModel.clear(); |
944 | - var rs = tx.executeSql("SELECT rowid, * FROM Episode WHERE podcast=? ORDER BY published DESC", [pid]); |
945 | - for(var i = 0; i < rs.rows.length; i++) { |
946 | - var episode = rs.rows.item(i); |
947 | - episodeModel.pid = pid; |
948 | - episodeModel.artist = artist; |
949 | - episodeModel.image = img; |
950 | - episodeModel.append({"guid" : episode.guid, "listened" : episode.listened, "name" : episode.name, "description" : episode.description, "duration" : episode.duration, "position" : episode.position, "downloadedfile" : episode.downloadedfile, "image" : img, "artist" : artist, "audiourl" : episode.audiourl}); |
951 | - } |
952 | - }); |
953 | - } |
954 | - |
955 | function subscribeFromFeed(feed) { |
956 | var xhr = new XMLHttpRequest; |
957 | if (feed.indexOf("://") === -1) { |
958 | |
959 | === modified file 'app/ui/SearchTab.qml' |
960 | --- app/ui/SearchTab.qml 2015-01-10 10:10:43 +0000 |
961 | +++ app/ui/SearchTab.qml 2015-01-25 18:02:01 +0000 |
962 | @@ -17,8 +17,10 @@ |
963 | */ |
964 | |
965 | import QtQuick 2.0 |
966 | +import QtQuick.Layouts 1.1 |
967 | import Ubuntu.Components 1.1 |
968 | import QtQuick.LocalStorage 2.0 |
969 | +import Ubuntu.Components.ListItems 1.0 as ListItem |
970 | import "../podcasts.js" as Podcasts |
971 | |
972 | Tab { |
973 | @@ -30,12 +32,13 @@ |
974 | Column { |
975 | spacing: units.gu(2) |
976 | anchors.fill: parent |
977 | - anchors.margins: units.gu(2) |
978 | - anchors.bottomMargin: 0 |
979 | + anchors.topMargin: units.gu(2) |
980 | |
981 | TextField { |
982 | id: searchField |
983 | - width: parent.width |
984 | + anchors.left: parent.left |
985 | + anchors.right: parent.right |
986 | + anchors.margins: units.gu(2) |
987 | placeholderText: i18n.tr("Search...") |
988 | inputMethodHints: Qt.ImhNoPredictiveText; |
989 | onTextChanged: { |
990 | @@ -48,50 +51,62 @@ |
991 | } |
992 | |
993 | ListView { |
994 | + clip: true |
995 | width: parent.width |
996 | model: searchResults |
997 | height: parent.height - searchField.height - units.gu(2) |
998 | - clip: true |
999 | - spacing: units.gu(1) |
1000 | + |
1001 | footer: Item { |
1002 | width: parent.width |
1003 | height: units.gu(7) |
1004 | } |
1005 | |
1006 | - delegate: Item { |
1007 | - |
1008 | - height: imgFrame.height |
1009 | - width: parent.width |
1010 | - |
1011 | - UbuntuShape { |
1012 | - id: imgFrame |
1013 | - width: units.gu(9.1) |
1014 | - height: width |
1015 | + delegate: ListItem.Empty { |
1016 | + |
1017 | + height: units.gu(8) |
1018 | + |
1019 | + RowLayout { |
1020 | + id: titleRow |
1021 | |
1022 | anchors.left: parent.left |
1023 | - image: Image { |
1024 | + anchors.right: parent.right |
1025 | + anchors.margins: units.gu(2) |
1026 | + anchors.verticalCenter: parent.verticalCenter |
1027 | + |
1028 | + spacing: units.gu(2) |
1029 | + |
1030 | + Image { |
1031 | + id: imgFrame |
1032 | + width: units.gu(5) |
1033 | + height: width |
1034 | + sourceSize.height: width |
1035 | + sourceSize.width: width |
1036 | source: model.image |
1037 | } |
1038 | - } |
1039 | - |
1040 | - Column { |
1041 | - anchors.left: imgFrame.right |
1042 | - anchors.leftMargin: units.gu(2) |
1043 | - anchors.right: parent.right |
1044 | - anchors.rightMargin: units.gu(2) |
1045 | - spacing: units.gu(0.5) |
1046 | - |
1047 | - Label { |
1048 | - text: model.name |
1049 | - elide: Text.ElideRight |
1050 | - width: parent.width |
1051 | - } |
1052 | - |
1053 | - Label { |
1054 | - text: model.artist |
1055 | - width: parent.width |
1056 | - elide: Text.ElideRight |
1057 | - fontSize: "small" |
1058 | + |
1059 | + Column { |
1060 | + id: detailColumn |
1061 | + |
1062 | + anchors.verticalCenter: imgFrame.verticalCenter |
1063 | + Layout.fillWidth: true |
1064 | + |
1065 | + Label { |
1066 | + id: podcastTitle |
1067 | + textFormat: Text.PlainText |
1068 | + text: model.name |
1069 | + width: parent.width |
1070 | + fontSize: "small" |
1071 | + elide: Text.ElideRight |
1072 | + } |
1073 | + |
1074 | + Label { |
1075 | + id: episodeCount |
1076 | + width: parent.width |
1077 | + color: "#999999" |
1078 | + text: model.artist |
1079 | + fontSize: "x-small" |
1080 | + elide: Text.ElideRight |
1081 | + } |
1082 | } |
1083 | |
1084 | Button { |
1085 | @@ -111,6 +126,7 @@ |
1086 | } |
1087 | } |
1088 | |
1089 | + |
1090 | ListModel { |
1091 | id: searchResults |
1092 | } |
1093 | @@ -124,9 +140,9 @@ |
1094 | var json = JSON.parse(xhr.responseText); |
1095 | for(var i in json.results) { |
1096 | searchResults.append({"name" : json.results[i].trackName, |
1097 | - "artist" : json.results[i].artistName, |
1098 | - "feed" : json.results[i].feedUrl, |
1099 | - "image" : json.results[i].artworkUrl100}); |
1100 | + "artist" : json.results[i].artistName, |
1101 | + "feed" : json.results[i].feedUrl, |
1102 | + "image" : json.results[i].artworkUrl100}); |
1103 | } |
1104 | } |
1105 | } |
1106 | |
1107 | === modified file 'po/com.mikeasoft.podbird.pot' |
1108 | --- po/com.mikeasoft.podbird.pot 2015-01-24 22:45:46 +0000 |
1109 | +++ po/com.mikeasoft.podbird.pot 2015-01-25 18:02:01 +0000 |
1110 | @@ -8,7 +8,7 @@ |
1111 | msgstr "" |
1112 | "Project-Id-Version: \n" |
1113 | "Report-Msgid-Bugs-To: \n" |
1114 | -"POT-Creation-Date: 2015-01-24 22:45+0000\n" |
1115 | +"POT-Creation-Date: 2015-01-25 19:00+0100\n" |
1116 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
1117 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
1118 | "Language-Team: LANGUAGE <LL@li.org>\n" |
1119 | @@ -17,69 +17,96 @@ |
1120 | "Content-Type: text/plain; charset=CHARSET\n" |
1121 | "Content-Transfer-Encoding: 8bit\n" |
1122 | |
1123 | -#: ../app/ui/PodcastsTab.qml:29 ../app/ui/PodcastsTab.qml:49 |
1124 | -#: ../app/ui/PodcastsTab.qml:68 |
1125 | +#: ../app/ui/EpisodesPage.qml:45 |
1126 | +msgid "Unsubscribe" |
1127 | +msgstr "" |
1128 | + |
1129 | +#: ../app/ui/EpisodesPage.qml:57 |
1130 | +msgid "Unsubscribe Confirmation" |
1131 | +msgstr "" |
1132 | + |
1133 | +#: ../app/ui/EpisodesPage.qml:58 |
1134 | +#, qt-format |
1135 | +msgid "Are you sure you want to unsubscribe from <b>%1</b>?" |
1136 | +msgstr "" |
1137 | + |
1138 | +#: ../app/ui/EpisodesPage.qml:60 |
1139 | +msgid "Yes" |
1140 | +msgstr "" |
1141 | + |
1142 | +#: ../app/ui/EpisodesPage.qml:77 |
1143 | +msgid "No" |
1144 | +msgstr "" |
1145 | + |
1146 | +#: ../app/ui/EpisodesPage.qml:196 |
1147 | +#, qt-format |
1148 | +msgid "%1h %2m" |
1149 | +msgstr "" |
1150 | + |
1151 | +#: ../app/ui/EpisodesPage.qml:202 |
1152 | +#, qt-format |
1153 | +msgid "%1h" |
1154 | +msgstr "" |
1155 | + |
1156 | +#: ../app/ui/EpisodesPage.qml:207 |
1157 | +#, c-format, qt-format |
1158 | +msgid "%1m" |
1159 | +msgstr "" |
1160 | + |
1161 | +#: ../app/ui/PodcastsTab.qml:32 |
1162 | msgid "Podcasts" |
1163 | msgstr "" |
1164 | |
1165 | -#: ../app/ui/PodcastsTab.qml:36 |
1166 | +#: ../app/ui/PodcastsTab.qml:40 |
1167 | msgid "Add Podcast" |
1168 | msgstr "" |
1169 | |
1170 | -#: ../app/ui/PodcastsTab.qml:45 |
1171 | -msgid "Up" |
1172 | -msgstr "" |
1173 | - |
1174 | -#: ../app/ui/PodcastsTab.qml:56 |
1175 | -msgid "Unsubscribe" |
1176 | -msgstr "" |
1177 | - |
1178 | -#: ../app/ui/PodcastsTab.qml:117 |
1179 | +#: ../app/ui/PodcastsTab.qml:59 |
1180 | msgid "Unable to subscribe" |
1181 | msgstr "" |
1182 | |
1183 | -#: ../app/ui/PodcastsTab.qml:118 |
1184 | +#: ../app/ui/PodcastsTab.qml:60 |
1185 | msgid "Please check the URL and try again" |
1186 | msgstr "" |
1187 | |
1188 | -#: ../app/ui/PodcastsTab.qml:120 |
1189 | +#: ../app/ui/PodcastsTab.qml:62 |
1190 | msgid "Close" |
1191 | msgstr "" |
1192 | |
1193 | -#: ../app/ui/PodcastsTab.qml:130 |
1194 | +#: ../app/ui/PodcastsTab.qml:72 |
1195 | msgid "No Podcast Subscriptions" |
1196 | msgstr "" |
1197 | |
1198 | -#: ../app/ui/PodcastsTab.qml:131 |
1199 | +#: ../app/ui/PodcastsTab.qml:73 |
1200 | msgid "" |
1201 | "You haven't subscribed to any podcasts yet, visit the 'Search' page to add " |
1202 | "some." |
1203 | msgstr "" |
1204 | |
1205 | -#: ../app/ui/PodcastsTab.qml:386 |
1206 | +#: ../app/ui/PodcastsTab.qml:222 |
1207 | msgid "Feed URL..." |
1208 | msgstr "" |
1209 | |
1210 | -#: ../app/ui/PodcastsTab.qml:400 |
1211 | +#: ../app/ui/PodcastsTab.qml:236 |
1212 | msgid "Cancel" |
1213 | msgstr "" |
1214 | |
1215 | -#: ../app/ui/PodcastsTab.qml:410 |
1216 | +#: ../app/ui/PodcastsTab.qml:246 |
1217 | msgid "Add" |
1218 | msgstr "" |
1219 | |
1220 | -#: ../app/ui/SearchTab.qml:25 |
1221 | +#: ../app/ui/SearchTab.qml:27 |
1222 | msgid "Search" |
1223 | msgstr "" |
1224 | |
1225 | -#: ../app/ui/SearchTab.qml:39 |
1226 | +#: ../app/ui/SearchTab.qml:42 |
1227 | msgid "Search..." |
1228 | msgstr "" |
1229 | |
1230 | -#: ../app/ui/SearchTab.qml:99 |
1231 | +#: ../app/ui/SearchTab.qml:114 |
1232 | msgid "Subscribe" |
1233 | msgstr "" |
1234 | |
1235 | -#: /home/mike/src/build-podbird-Desktop-Default/po/Podbird.desktop.in.h:1 |
1236 | +#: /home/krnekhelesh/Documents/Ubuntu-Projects/MP-Reviews/builddir/build-new-listitem-actions-UbuntuSDK_for_armhf_GCC_ubuntu_sdk_14_10_utopic-Default/po/Podbird.desktop.in.h:1 |
1237 | msgid "Podbird" |
1238 | msgstr "" |
NOTE: DO NOT MERGE! I am still tweaking it until I think it looks good. Still not satisfied with it yet :)