Merge lp:~dragly/ubuntu-rssreader-app/initial-design into lp:~ubuntu-shorts-dev/ubuntu-rssreader-app/trunk

Proposed by Svenn-Arne Dragly
Status: Merged
Approved by: Joey Chan
Approved revision: 9
Merged at revision: 6
Proposed branch: lp:~dragly/ubuntu-rssreader-app/initial-design
Merge into: lp:~ubuntu-shorts-dev/ubuntu-rssreader-app/trunk
Diff against target: 606 lines (+242/-135)
9 files modified
EntryListPage.qml (+12/-28)
EntryListTab.qml (+12/-0)
FeedListPage.qml (+15/-9)
SingleEntryPage.qml (+20/-0)
TodayPage.qml (+77/-0)
databasemodule.js (+47/-15)
rssreader.qmlproject (+1/-1)
tabshelper.js (+1/-0)
ubuntu-rssreader-app.qml (+57/-82)
To merge this branch: bzr merge lp:~dragly/ubuntu-rssreader-app/initial-design
Reviewer Review Type Date Requested Status
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Ubuntu Shorts Developers Pending
Review via email: mp+158862@code.launchpad.net

Commit message

Implemented design proposal with horizontal swipes between feeds on main screen.

Description of the change

Changed the layout of the app to show feeds in tabs in the main view. Sideways scrolling changes feed. Currently no page for "all" feeds - to be added. Very hackish solution to show multiple tabs with auto-generated QML on the fly. Needs upstream fix (see bug #1124071 for details).
The layout is based on a combination of Tabs + PageStack. The topmost element of the stack is the only with tabs (to avoid confusion with the back key on lower levels).
Feed list is accessible from the toolbar icon.
Single feeds may be seen via this menu.
Single entries are shown in a separate page.
Adding feeds is reflected in updated tabs in the main view.
Updated databasemodule.js with function to read feed information. Also removed global dbResult object in favor of functions returning values/objects. (Could possibly be replaced with asynchronous callbacks, but the LocalStorage API is synchronous, so this should not result in any problems.)

To post a comment you must log in.
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :

FAILED: Continuous integration, rev:9
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~dragly/ubuntu-rssreader-app/initial-design/+merge/158862/+edit-commit-message

http://91.189.93.125:8080/job/ubuntu-rssreader-app-ci/7/
Executed test runs:
    SUCCESS: http://91.189.93.125:8080/job/ubuntu-rssreader-app-quantal-amd64-ci/7
    SUCCESS: http://91.189.93.125:8080/job/ubuntu-rssreader-app-raring-amd64-ci/7

Click here to trigger a rebuild:
http://91.189.93.125:8080/job/ubuntu-rssreader-app-ci/7/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :

FAILED: Autolanding.
No commit message was specified in the merge proposal. Hit 'Add commit message' on the merge proposal web page or follow the link below. You can approve the merge proposal yourself to rerun.
https://code.launchpad.net/~dragly/ubuntu-rssreader-app/initial-design/+merge/158862/+edit-commit-message

review: Needs Fixing (continuous-integration)
Revision history for this message
Joey Chan (qqworini) wrote :

Well, I can approve but Jenkins Bot can't :P

Svenn-Arne, pls add commit message soon

Revision history for this message
Svenn-Arne Dragly (dragly) wrote :

> Well, I can approve but Jenkins Bot can't :P
>
> Svenn-Arne, pls add commit message soon

Thanks! I've added a commit message now.

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== renamed file 'NewsListPage.qml' => 'EntryListPage.qml'
2--- NewsListPage.qml 2013-03-24 13:19:16 +0000
3+++ EntryListPage.qml 2013-04-15 08:44:35 +0000
4@@ -7,20 +7,19 @@
5
6 import "databasemodule.js" as DB
7
8-import QtWebKit 3.0
9-
10 Page {
11
12 signal backRequst
13+ property string feedLink: ""
14
15 /* 1. Loading data from database by url.
16 * 2. Appending data to ListModel for displaying.
17 */
18 function loadEntriesByUrl(strUrl) {
19 newsListModel.clear()
20- var isAll = (strUrl === "")
21- DB.loadEntries(strUrl, isAll)
22- var entries = DB.dbResult.rows
23+ var isAll = (feedLink === "")
24+ var entries = DB.loadEntries(feedLink, isAll)
25+// var entries = DB.dbResult.rows
26 for (var i = 0; i < entries.length; i++) {
27 newsListModel.append({"title":entries.item(i).title,
28 "pubDate":entries.item(i).pubDate,
29@@ -28,6 +27,11 @@
30 "description":entries.item(i).description,
31 "hash":entries.item(i).hash})
32 }
33+ console.log("Loaded entries by URL " + strUrl)
34+ }
35+
36+ onFeedLinkChanged: {
37+ loadEntriesByUrl(feedLink)
38 }
39
40 tools: ToolbarActions {
41@@ -43,12 +47,8 @@
42 ListView {
43 id: newsList
44
45- width: parent.width
46- height: units.gu(40)
47 clip: true
48- anchors {
49- top: parent.top
50- }
51+ anchors.fill: parent
52
53 model: ListModel {
54 id: newsListModel
55@@ -64,29 +64,13 @@
56 }
57
58 onClicked: {
59- contentLabel.loadHtml((model.content !== "")? model.content : model.description) // FIRST WAY
60+ mainPageStack.push(singleEntryPage)
61+ singleEntryPage.content.loadHtml((model.content !== "")? model.content : model.description) // FIRST WAY
62 // contentLabel.text = (model.content != "")? model.content : model.description // SECOND WAY
63 }
64 } // delegate
65 } // ListView
66
67- /* FIRST WAY OF DISPLAYING FEED */
68- WebView {
69- id: contentLabel
70- width: parent.width
71-
72-
73- onNavigationRequested: {
74- if (request.navigationType == 0)
75- request.action = 1
76- }
77-
78- anchors {
79- top: newsList.bottom
80- bottom: parent.bottom
81- }
82- }
83-
84 /* SECOND WAY OF DISPLAYING FEED. DOES NOT WORK ON MY QT5 LIBS, BUG */
85 // Flickable {
86 // id: tmpLabelFlick
87
88=== added file 'EntryListTab.qml'
89--- EntryListTab.qml 1970-01-01 00:00:00 +0000
90+++ EntryListTab.qml 2013-04-15 08:44:35 +0000
91@@ -0,0 +1,12 @@
92+import QtQuick 2.0
93+import Ubuntu.Components 0.1
94+
95+Tab {
96+// title: i18n.tr("News") // todo replace with feed name
97+ property alias entryListPage: entryListPageLocal
98+ property alias feedLink: entryListPageLocal.feedLink
99+
100+ page: EntryListPage {
101+ id: entryListPageLocal
102+ }
103+}
104
105=== modified file 'FeedListPage.qml'
106--- FeedListPage.qml 2013-03-24 13:19:16 +0000
107+++ FeedListPage.qml 2013-04-15 08:44:35 +0000
108@@ -11,19 +11,21 @@
109 id: pageItself
110
111 signal feedSelected(string selectedFeed)
112+ signal feedsReloaded
113+ signal feedDeleted(string feedLink)
114
115 ListView {
116 id: feedList
117
118 function reloadFeeds() {
119 feedListModel.clear()
120- DB.loadFeeds()
121- var feeds = DB.dbResult
122- for (var i = 0; i < feeds.rows.length; i++) {
123- feedListModel.append({"link":feeds.rows.item(i).link,
124- "displayName":feeds.rows.item(i).displayName,
125- "siteLink":feeds.rows.item(i).siteLink,
126- "description":feeds.rows.item(i).description})
127+ var feeds = DB.loadFeeds()
128+// var feeds = DB.dbResult
129+ for (var i = 0; i < feeds.length; i++) {
130+ feedListModel.append({"link":feeds.item(i).link,
131+ "displayName":feeds.item(i).displayName,
132+ "siteLink":feeds.item(i).siteLink,
133+ "description":feeds.item(i).description})
134 }
135 }
136
137@@ -195,6 +197,7 @@
138 // DB.addEntry(curUrl, entry.title, entry.pubDate, Qt.md5(entry.title) /*TODO MORE DATA IN HASH*/,
139 // entry.description, entry.content)
140 }
141+ feedsReloaded()
142 }
143 break
144 case XmlListModel.Loading:
145@@ -251,10 +254,12 @@
146 onTriggered: {
147 PopupUtils.close(popover)
148 var link = feedListModel.get(feedList.currentIndex).link
149- DB.deleteFeed(link)
150- if (DB.dbResult.rowsAffected > 0)
151+ var dbResult = DB.deleteFeed(link)
152+ if (dbResult.rowsAffected > 0) {
153 DB.removeEntriesOfFeed(link)
154 feedListModel.remove(feedList.currentIndex)
155+ }
156+ feedDeleted(link)
157 }
158 }
159
160@@ -264,6 +269,7 @@
161 PopupUtils.close(popover)
162 DB.dropTables()
163 feedListModel.clear()
164+ feedsReloaded()
165 }
166 }
167 } // ActionList
168
169=== added file 'SingleEntryPage.qml'
170--- SingleEntryPage.qml 1970-01-01 00:00:00 +0000
171+++ SingleEntryPage.qml 2013-04-15 08:44:35 +0000
172@@ -0,0 +1,20 @@
173+import QtQuick 2.0
174+
175+import QtWebKit 3.0
176+
177+Rectangle {
178+ width: 30
179+ height: 30
180+ property alias content: contentLabel
181+
182+ /* FIRST WAY OF DISPLAYING FEED */
183+ WebView {
184+ id: contentLabel
185+ anchors.fill: parent
186+
187+ onNavigationRequested: {
188+ if (request.navigationType == 0)
189+ request.action = 1
190+ }
191+ }
192+}
193
194=== added file 'TodayPage.qml'
195--- TodayPage.qml 1970-01-01 00:00:00 +0000
196+++ TodayPage.qml 2013-04-15 08:44:35 +0000
197@@ -0,0 +1,77 @@
198+import QtQuick 2.0
199+import Ubuntu.Components 0.1
200+
201+import "databasemodule.js" as DB
202+import "tabshelper.js" as TabsHelper
203+
204+Page {
205+ id: todayPage
206+ visible: false
207+ property bool firstRefresh: true
208+
209+ signal requestFeedListPage
210+
211+ function refresh() {
212+ if(firstRefresh) {
213+ todayTabs.destroy()
214+ firstRefresh = false
215+ }
216+ var tabsString = "import QtQuick 2.0; import Ubuntu.Components 0.1; Tabs {id: todayTabs; anchors.fill: parent;"
217+ var feeds = DB.loadFeeds()
218+ if(feeds.length > 0) {
219+ for(var i = 0; i < feeds.length; i++) {
220+ var feed = feeds[i];
221+ console.log("displayName: " + feed.displayName)
222+ // ourTabs.__tabsModel.children[i].title = feed.displayName
223+ // ourTabs.__tabsModel.children[i].entryListPage.loadEntriesByUrl(feed.link)
224+ tabsString += "EntryListTab {"
225+ tabsString += "title: \"" + feed.displayName + "\";"
226+ tabsString += "feedLink: \"" + feed.link + "\";"
227+ tabsString += "}"
228+ }
229+ } else {
230+ var labelText = i18n.tr("No feeds added.\nClick the feed icon in the toolbar to get started.")
231+ tabsString += "Tab { title: \"No feeds\"; page: Page { anchors.fill: parent; Label { anchors.centerIn: parent; text: \"" + labelText + "\"; } } }"
232+ }
233+ tabsString += "}"; // END Tabs component
234+
235+ console.log(tabsString)
236+ console.log(TabsHelper.tabsObject)
237+ if(TabsHelper.tabsObject !== null) {
238+ TabsHelper.tabsObject.destroy()
239+ }
240+
241+ TabsHelper.tabsObject = Qt.createQmlObject(tabsString,
242+ todayPage, "todayTabs")
243+ }
244+
245+ tools: ToolbarActions {
246+ Action {
247+ objectName: "action"
248+ id: sampleAction
249+
250+ iconSource: Qt.resolvedUrl("avatar.png")
251+ text: i18n.tr("Feeds")
252+
253+ onTriggered: {
254+// mainPageStack.push(feedListPage)
255+ requestFeedListPage()
256+ }
257+ }
258+
259+// back: Action {
260+// onTriggered: {
261+// console.log("Back triggered!")
262+// }
263+// }
264+ }
265+
266+ Tabs {
267+ id: todayTabs
268+ anchors.fill: parent
269+
270+ Tab {
271+
272+ }
273+ }
274+}
275
276=== modified file 'databasemodule.js'
277--- databasemodule.js 2013-03-24 13:19:16 +0000
278+++ databasemodule.js 2013-04-15 08:44:35 +0000
279@@ -22,20 +22,23 @@
280
281 /* This variable will store result of any operation with database.
282 */
283-var dbResult
284+//var dbResult
285
286 /* Load all feed from database.
287 * Mb later we will need some criteria, such as tag, date and so on!
288 */
289 function loadFeeds() {
290 var db = openStdDataBase()
291-
292+ var dbResultLocal
293+ var feeds
294 db.transaction(function(tx) {
295 ensureFeedTableExists(tx)
296- dbResult = tx.executeSql("SELECT * FROM UserFeeds")
297- console.log("SELECTED: ", dbResult.rows.length)
298+ dbResultLocal = tx.executeSql("SELECT * FROM UserFeeds")
299+ console.log("SELECTED: ", dbResultLocal.rows.length)
300+ feeds = dbResultLocal.rows
301 }
302 )
303+ return feeds;
304 }
305
306 /* Add feed to database.
307@@ -46,6 +49,7 @@
308 console.log("Database, AddFeed: 0.link can't be empty")
309 return
310 }
311+ var dbResult
312
313 d.displayName = d.displayName || ""
314 d.siteLink = d.siteLink || ""
315@@ -70,13 +74,14 @@
316 console.log("INSERT ID", dbResult.insertId)
317 }
318 )
319+ return dbResult
320 }
321
322 /* Update information about feed with new information.
323 */
324 function updateFeedWithInfo(link, d) {
325 var db = openStdDataBase()
326-
327+ var dbResult
328 db.transaction(function (tx) {
329 ensureFeedTableExists(tx)
330 dbResult = tx.executeSql('UPDATE UserFeeds SET displayName=?, siteLink=?, description=? WHERE link=?',
331@@ -84,19 +89,23 @@
332 console.log("UPDATE, AFFECTED ROWS", dbResult.rowsAffected)
333 }
334 )
335+ return dbResult
336 }
337
338 /* Delete feed from database.
339 */
340 function deleteFeed(link) {
341 var db = openStdDataBase()
342-
343+ var result
344 db.transaction(function (tx) {
345+ var dbResultLocal
346 ensureFeedTableExists(tx)
347- dbResult = tx.executeSql('DELETE FROM UserFeeds WHERE link = ?', [link])
348- console.log("DELETE, AFFECTED ROWS", dbResult.rowsAffected)
349+ dbResultLocal = tx.executeSql('DELETE FROM UserFeeds WHERE link = ?', [link])
350+ console.log("DELETE, AFFECTED ROWS", dbResultLocal.rowsAffected)
351+ result = dbResultLocal
352 }
353 )
354+ return result
355 }
356
357 /* Select all entries from database.
358@@ -104,22 +113,43 @@
359 function loadEntries(feedLink, all) {
360 all = all || false
361 var db = openStdDataBase()
362+ var entries;
363
364 db.transaction(function(tx) {
365 ensureEntriesTableExists(tx)
366+ var dbResultLocal
367 if (all)
368- dbResult = tx.executeSql('SELECT * FROM FeedEntries')
369- else dbResult = tx.executeSql('SELECT * FROM FeedEntries WHERE feedLink = ?', [feedLink])
370- console.log("loadEntries, SELECTED ROWS", dbResult.rows.length)
371- }
372- )
373+ dbResultLocal = tx.executeSql('SELECT * FROM FeedEntries')
374+ else dbResultLocal = tx.executeSql('SELECT * FROM FeedEntries WHERE feedLink = ?', [feedLink])
375+ console.log("loadEntries, SELECTED ROWS", dbResultLocal.rows.length)
376+ entries = dbResultLocal.rows;
377+ }
378+ )
379+ return entries;
380+}
381+
382+/* Select all entries from database.
383+ */
384+function loadFeed(feedLink) {
385+ var db = openStdDataBase()
386+ var feed = {displayName: "Unknown"}
387+ db.transaction(function(tx) {
388+ ensureEntriesTableExists(tx)
389+ var dbResult = tx.executeSql('SELECT * FROM UserFeeds WHERE link = ?', [feedLink])
390+ console.log("feedName, SELECTED ROWS", dbResult.rows.length)
391+ if(dbResult.rows.length > 0) {
392+ feed = dbResult.rows.item(0)
393+ }
394+ }
395+ )
396+ return feed
397 }
398
399 /* Append entry to database.
400 */
401 function addEntry(feedLink, hash, d) {
402 var db = openStdDataBase()
403-
404+ var dbResult
405 if (feedLink === undefined || hash === undefined) {
406 console.log("Database, addEntry: feedLink and hash can't be undefined")
407 return
408@@ -142,18 +172,20 @@
409 // console.log("ENTRY INSERT ID", dbResult.insertId)
410 }
411 )
412+ return dbResult
413 }
414
415 /* Remove all entries from database by feed link.
416 */
417 function removeEntriesOfFeed(feedLink) {
418 var db = openStdDataBase()
419-
420+ var dbResult
421 db.transaction(function(tx) {
422 ensureEntriesTableExists(tx)
423 dbResult = tx.executeSql('DELETE FROM FeedEntries WHERE feedLink = ?', [feedLink])
424 }
425 )
426+ return dbResult
427 }
428
429 /* Only for debug purposes.
430
431=== modified file 'rssreader.qmlproject'
432--- rssreader.qmlproject 2013-03-02 20:42:07 +0000
433+++ rssreader.qmlproject 2013-04-15 08:44:35 +0000
434@@ -3,7 +3,7 @@
435 import QmlProject 1.1
436
437 Project {
438- mainFile: "rssreader.qml"
439+ mainFile: "ubuntu-rssreader-app.qml"
440
441 /* Include .qml, .js, and image files from current directory and subdirectories */
442 QmlFiles {
443
444=== added file 'tabshelper.js'
445--- tabshelper.js 1970-01-01 00:00:00 +0000
446+++ tabshelper.js 2013-04-15 08:44:35 +0000
447@@ -0,0 +1,1 @@
448+var tabsObject = null;
449
450=== modified file 'ubuntu-rssreader-app.qml'
451--- ubuntu-rssreader-app.qml 2013-04-11 21:21:44 +0000
452+++ ubuntu-rssreader-app.qml 2013-04-15 08:44:35 +0000
453@@ -9,6 +9,8 @@
454 second Tab has a single ToolbarAction.
455 */
456
457+import "databasemodule.js" as DB
458+
459 MainView {
460 // objectName for functional testing purposes (autopilot-qt5)
461 objectName: "rssreader"
462@@ -17,89 +19,62 @@
463 width: units.gu(50)
464 height: units.gu(75)
465
466-
467- Tabs {
468- id: tabs
469+ PageStack {
470+ id: mainPageStack
471+ Component.onCompleted: {
472+ push(todayPage)
473+ todayPage.refresh()
474+ }
475 anchors.fill: parent
476-
477- Tab {
478- objectName: "Tab1"
479-
480- title: i18n.tr("RSS Reader")
481-
482- page: FeedListPage {
483- id: feedListPage
484-
485- onFeedSelected: {
486- console.log("Feed SELECTED, switch tab next")
487- newsListPage.loadEntriesByUrl(selectedFeed)
488- tabs.selectedTabIndex = 1
489- }
490- }
491- }
492-
493- Tab {
494- title: i18n.tr("News") // todo replace with feed name
495-
496- page: NewsListPage {
497- id: newsListPage
498-
499- onBackRequst: {
500- tabs.selectedTabIndex = 0
501- }
502- }
503- }
504-
505- // Second tab begins here
506- Tab {
507- objectName: "Tab2"
508-
509- title: i18n.tr("Optional Screen")
510- page: Page {
511- id: secondPage
512- anchors.margins: units.gu(2)
513-
514- tools: ToolbarActions {
515- Action {
516- objectName: "action"
517- id: sampleAction
518-
519- iconSource: Qt.resolvedUrl("avatar.png")
520- text: i18n.tr("Tap me!")
521-
522- onTriggered: {
523- label.text = i18n.tr("Toolbar icon tapped")
524- }
525- }
526-
527- back: Action {
528- onTriggered: {
529- console.log("Back triggered!")
530- }
531- }
532- }
533-
534-
535-
536- Column {
537- anchors.centerIn: parent
538- Label {
539- id: label
540- objectName: "label"
541-
542- text: i18n.tr("Swipe from bottom to up to reveal the toolbar.")
543- }
544-
545- Button {
546- text: "Toolbar Test"
547- width: units.gu(15)
548- anchors.horizontalCenter: parent.horizontalCenter
549- onClicked: {
550- secondPage.tools.active = true
551- }
552- }
553- }
554- }
555+ __showHeader: false
556+ TodayPage {
557+ id: todayPage
558+ visible: false
559+ onRequestFeedListPage: mainPageStack.push(feedListPage)
560+ }
561+
562+ FeedListPage {
563+ id: feedListPage
564+ visible: false
565+ anchors.fill: parent
566+ onFeedSelected: {
567+ if(selectedFeed != "") {
568+ mainPageStack.push(selectedEntryListPageView)
569+ selectedEntryListPage.feedLink = selectedFeed
570+ var feed = DB.loadFeed(selectedFeed)
571+ selectedEntryTab.title = feed.displayName
572+ } else {
573+ mainPageStack.clear()
574+ mainPageStack.push(todayPage)
575+ }
576+ }
577+ onFeedsReloaded: {
578+ todayPage.refresh()
579+ }
580+ onFeedDeleted: {
581+ todayPage.refresh()
582+ }
583+ }
584+ Page {
585+ anchors.fill: parent
586+ id: selectedEntryListPageView
587+ visible: false
588+ Tabs {
589+ anchors.fill: parent
590+ Tab {
591+ id: selectedEntryTab
592+ title: i18n.tr("Selected feed") // todo replace with feed name
593+ page: EntryListPage {
594+ anchors.fill: parent
595+ id: selectedEntryListPage
596+ }
597+ }
598+ }
599+ }
600+ SingleEntryPage {
601+ id: singleEntryPage
602+ visible: false
603+ anchors.fill: parent
604 }
605 }
606 }

Subscribers

People subscribed via source and target branches