Merge lp:~martin-borho/ubuntu-weather-app/location-manager into lp:ubuntu-weather-app/obsolete.trunk

Proposed by Martin Borho on 2013-06-12
Status: Merged
Approved by: Raúl Yeguas on 2013-06-12
Approved revision: 40
Merged at revision: 35
Proposed branch: lp:~martin-borho/ubuntu-weather-app/location-manager
Merge into: lp:ubuntu-weather-app/obsolete.trunk
Diff against target: 625 lines (+293/-204)
6 files modified
components/AddLocationPage.qml (+70/-103)
components/LocationManagerPage.qml (+174/-0)
components/LocationTab.qml (+8/-29)
components/LocationTabEmpty.qml (+0/-35)
components/WeatherApi.js (+2/-4)
ubuntu-weather-app.qml (+39/-33)
To merge this branch: bzr merge lp:~martin-borho/ubuntu-weather-app/location-manager
Reviewer Review Type Date Requested Status
Raúl Yeguas 2013-06-12 Approve on 2013-06-12
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve on 2013-06-12
Review via email: mp+169022@code.launchpad.net

Commit message

Location manager added

Description of the change

* Added location manager for adding and removing location. Bug #1187314
* "Remove" action removed from toolbar, Bug #1187315
* at first start or when no locations are defined the location manager is shown.
* LocationTabEmpty.qml was removed
* PageStack component added to the app.
* LocationManagerPage added
* AddLocationDialog became AddLocationPage
* dummy item added in LocationManager for "Current location" like in http://design.canonical.com/2013/03/app-patterns-applied-weather-key-journeys/. London is used as dummy location unitl location lookup is provided from QT/device
* disabled api requests for hourly forcasts until data will be used in the UI
* locations can be removed by sliding them from the locations list in location manager

To post a comment you must log in.
Raúl Yeguas (neokore) wrote :

Great work again, Martin!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== renamed file 'components/AddLocationDialog.qml' => 'components/AddLocationPage.qml'
2--- components/AddLocationDialog.qml 2013-06-09 15:42:41 +0000
3+++ components/AddLocationPage.qml 2013-06-12 17:57:26 +0000
4@@ -1,113 +1,79 @@
5 import QtQuick 2.0
6 import Ubuntu.Components 0.1
7-import Ubuntu.Components.Popups 0.1
8-import Ubuntu.Components.ListItems 0.1 as ListItem;
9-
10-// Dialog for adding a new location
11-Component {
12- id: addLocationDialog
13-
14- Dialog {
15- id: addLocationDialogue
16- height: units.gu(10)
17- title: i18n.tr("Add location")
18- text: i18n.tr("Write a city name or pick it by your location")
19-
20- WorkerScript {
21- id: searchWorker
22- source: "./WeatherApi.js"
23- onMessage: {
24- if(!messageObject.error) {
25-
26- messageObject.result.locations.forEach(function(loc) {
27- citiesModel.append(loc);
28- });
29- } else {
30- console.log(messageObject.error.msg+" / "+messageObject.error.request.url)
31- }
32- }
33- }
34-
35- function locationSelected(index) {
36- var location = citiesModel.get(index);
37- // make clone
38- location = JSON.parse(JSON.stringify(location));
39- mainView.locationAdded(location);
40- PopupUtils.close(addLocationDialogue)
41- }
42-
43- Row {
44- TextField {
45- id: locationString
46- width: units.gu(25)
47- placeholderText: i18n.tr("Enter a city name")
48- }
49- Button {
50- id: locationSearchButton
51-
52- width: units.gu(5)
53- iconSource: "../resources/images/search_icon.png"
54-
55- onClicked: {
56- citiesModel.clear();
57- searchWorker.sendMessage({
58- action: "searchByName",
59- params: {name:locationString.text, units:mainView.settings["units"]}
60- })
61- }
62- }
63- }
64-
65- Button {
66- id: locationGPSButton
67- text: i18n.tr("Use my own location")
68- onClicked: {
69+import Ubuntu.Components.ListItems 0.1 as ListItem
70+
71+Page {
72+ id: addLocationPage
73+ objectName: "AddLocationrPage"
74+ title: i18n.tr("Add city")
75+ visible: false
76+ tools.locked: true
77+ tools.opened: true
78+
79+ WorkerScript {
80+ id: searchWorker
81+ source: "./WeatherApi.js"
82+ onMessage: {
83+ if(!messageObject.error) {
84+ messageObject.result.locations.forEach(function(loc) {
85+ citiesModel.append(loc);
86+ });
87+ } else {
88+ console.log(messageObject.error.msg+" / "+messageObject.error.request.url)
89+ }
90+ }
91+ }
92+
93+ function clear() {
94+ locationString.text = ''
95+ citiesModel.clear()
96+ }
97+
98+ Rectangle {
99+ id: searchInput
100+ width:parent.width
101+ height:units.gu(7)
102+ color: "transparent"
103+ TextField {
104+ id: locationString
105+ width: parent.width-units.gu(2)
106+ height:units.gu(5)
107+ anchors.centerIn: parent
108+ placeholderText: i18n.tr("Enter a city name")
109+ hasClearButton: true
110+ onAccepted: {
111 citiesModel.clear();
112 searchWorker.sendMessage({
113- action: "searchByPoint",
114- params: {coords: {"lon":10,"lat":53.549999}, units:mainView.settings["units"]}
115+ action: "searchByName",
116+ params: {name:locationString.text, units:"metric"}
117 })
118 }
119 }
120-
121- Button {
122- id:locationCancelButton
123- text: i18n.tr("Cancel")
124- color: "#C00000"
125- onClicked: PopupUtils.close(addLocationDialogue)
126- }
127-
128- Item {
129- id: locationResultBox
130- anchors {
131- top: locationCancelButton.bottom
132- topMargin: units.gu(2)
133- }
134- }
135-
136- ListModel {
137- id: citiesModel
138- }
139-
140- Rectangle {
141- id: cityList;
142- width: units.gu(30);
143- height: units.gu(30);
144- color: "transparent";
145- ListView {
146- id: listView;
147- clip: true;
148- anchors.fill: parent;
149- model: citiesModel;
150- delegate: ListItem.Standard {
151- text: i18n.tr(name)+((country) ? ', '+i18n.tr(country): '');
152- progression: true;
153- onClicked: {
154- var tabsString = "import QtQuick 2.0; import Ubuntu.Components 0.1; import Ubuntu.Components.Popups 0.1;"
155- + "Tabs {id: tabs; anchors.fill: parent; Tab { title: 'Loading...'; LoadingComponent{id:bigLoading; anchors.fill: parent; z: 2;}}}"
156- tabsObject = Qt.createQmlObject(tabsString, tabPage, "tabs")
157- locationSelected(index);
158- }
159+ }
160+
161+ ListModel {
162+ id: citiesModel
163+ }
164+
165+ Rectangle {
166+ id: cityList;
167+ anchors.top: searchInput.bottom
168+ width: parent.width
169+ height: units.gu(52)
170+ color: "transparent"
171+ ListView {
172+ id: listView;
173+ clip: true;
174+ anchors.fill: parent;
175+ model: citiesModel;
176+ delegate: ListItem.Standard {
177+ text: i18n.tr(name)+((country) ? ', '+i18n.tr(country): '');
178+ progression: true;
179+ onClicked: {
180+ var location = citiesModel.get(index)
181+ locationManager.addLocation(location)
182+ pageStack.pop()
183+ clear()
184 }
185 }
186 Scrollbar {
187@@ -116,4 +82,5 @@
188 }
189 }
190 }
191+
192 }
193
194=== added file 'components/LocationManagerPage.qml'
195--- components/LocationManagerPage.qml 1970-01-01 00:00:00 +0000
196+++ components/LocationManagerPage.qml 2013-06-12 17:57:26 +0000
197@@ -0,0 +1,174 @@
198+import QtQuick 2.0
199+import Ubuntu.Components 0.1
200+import Ubuntu.Components.ListItems 0.1 as ListItem
201+import Ubuntu.Components.Popups 0.1
202+
203+Page {
204+ id: locationManagerPage
205+ objectName: "LocationManagerPage"
206+ title: i18n.tr("Edit locations")
207+ visible: false
208+
209+ property bool locationsChanged: false
210+
211+ tools: ToolbarActions {
212+ locked: true
213+ opened: (locationModel.count > 0) ? true : false
214+ back.onTriggered: {
215+ if(locationsChanged) {
216+ mainView.refreshData()
217+ locationsChanged = false;
218+ }
219+ // handle dummy "current location" item
220+ currentLocationItem.visible = false
221+ locationLookupItem.visible = true
222+ lookupItemAddButton.visible = true
223+ }
224+ }
225+
226+ function loadData() {
227+ storage.getLocations(fillLocationsList);
228+ }
229+
230+ function checkLocationExists(location) {
231+ for(var x=0;x<locationModel.count;x++) {
232+ var loc = locationModel.get(x);
233+ if(loc.service === location.service
234+ && loc.service_id === location.service_id) {
235+ existsNotification.show()
236+ return true;
237+ }
238+ }
239+ return false;
240+ }
241+
242+ function fillLocationsList(locations) {
243+ locationModel.clear()
244+ for(var x=0;x<locations.length;x++) {
245+ var dbId = locations[x].db.id,
246+ location = locations[x].location;
247+ // add db-id to the location object
248+ location.dbId = dbId;
249+ locationModel.append(locations[x].location);
250+ }
251+ }
252+
253+ function addLocation(location) {
254+ if(!checkLocationExists(location)) {
255+ storage.insertLocation({location:location});
256+ loadData()
257+ locationsChanged = true;
258+ }
259+ }
260+
261+
262+ Dialog {
263+ id:existsNotification
264+ title: i18n.tr("Location already added.")
265+ Button {
266+ text: i18n.tr("OK")
267+ onClicked: PopupUtils.close(existsNotification)
268+ }
269+ }
270+
271+ ListModel {
272+ id: locationModel
273+ }
274+
275+ Column {
276+ anchors.fill: parent
277+ Rectangle {
278+ color: "#D6D6D6"
279+ width:parent.width
280+ height: units.gu(0.3)
281+ }
282+ Rectangle {
283+ width: parent.width
284+ height: units.gu(5)
285+ Label {
286+ anchors.verticalCenter: parent.verticalCenter
287+ text: " "+i18n.tr("Current location")
288+ fontSize: "large"
289+ }
290+ }
291+ Rectangle {
292+ color: "#D6D6D6"
293+ width:parent.width
294+ height: units.gu(0.3)
295+ }
296+ ListItem.Standard {
297+ id: locationLookupItem
298+ text: i18n.tr("Lookup location")+" (Dummy)"
299+ progression: true
300+ onClicked: {
301+ print("TODO: lookup current location")
302+ visible = false
303+ currentLocationItem.visible = true;
304+ }
305+ }
306+ ListItem.Standard {
307+ id: currentLocationItem
308+ visible: false
309+ text: i18n.tr("London")
310+ //icon: Qt.resolvedUrl("../resources/images/refresh_icon.png")
311+ control: Button {
312+ id: lookupItemAddButton
313+ anchors.verticalCenter: parent.verticalCenter
314+ text: i18n.tr("add")
315+ MouseArea {
316+ anchors.fill: parent
317+ onClicked: {
318+ // add dummy location
319+ addLocation({"service_id":2643743,"name":"London","country":"GB","service":"openweathermap"})
320+ lookupItemAddButton.visible = false;
321+ }
322+ }
323+ }
324+ }
325+ Rectangle {
326+ id:test2
327+ width: parent.width
328+ height: units.gu(5)
329+ Label {
330+ anchors.verticalCenter: parent.verticalCenter
331+ text: " "+i18n.tr("World")
332+ fontSize: "large"
333+ }
334+ }
335+ Rectangle {
336+ color: "#D6D6D6"
337+ width:parent.width
338+ height: units.gu(0.3)
339+ }
340+ Rectangle {
341+ width: parent.width
342+ clip:true
343+ color:"transparent"
344+ height: units.gu(40)
345+ ListView {
346+ objectName: "locationList"
347+ anchors.fill: parent
348+ model: locationModel
349+ delegate: ListItem.Standard {
350+ text: model.name
351+ removable: true
352+ onItemRemoved: {
353+ var location = locationModel.get(index)
354+ locationsChanged = true;
355+ storage.clearLocation(location.dbId);
356+ locationModel.remove(index)
357+ }
358+ backgroundIndicator: Rectangle {
359+ anchors.fill: parent
360+ color: "gray"
361+ }
362+ }
363+ footer: ListItem.Standard {
364+ text: i18n.tr("Add city")
365+ progression: true
366+ onClicked: pageStack.push(addLocationPage)
367+ }
368+ }
369+ }
370+ }
371+}
372
373=== modified file 'components/LocationTab.qml'
374--- components/LocationTab.qml 2013-06-12 17:08:05 +0000
375+++ components/LocationTab.qml 2013-06-12 17:57:26 +0000
376@@ -56,44 +56,23 @@
377
378 tools: ToolbarActions {
379 Action {
380- id: addLocationAction
381- property string cityCode: ''
382- objectName: "action"
383-
384- iconSource: Qt.resolvedUrl("../resources/images/add_icon.png")
385- text: i18n.tr("Add")
386-
387- onTriggered: {
388- PopupUtils.open(addLocationDialog, locationTab);
389- }
390- }
391-
392- Action {
393- id: removeLocationAction
394- property string cityCode: ''
395- objectName: "action"
396-
397- iconSource: Qt.resolvedUrl("../resources/images/remove_icon.png")
398- text: i18n.tr("Remove")
399-
400- onTriggered: {
401- mainView.removeLocation(locationData.db.id)
402- }
403- }
404-
405- Action {
406 id: refreshAction
407 iconSource: Qt.resolvedUrl("../resources/images/refresh_icon.png")
408 text: i18n.tr("Refresh")
409
410 onTriggered: {
411- var tabsString = "import QtQuick 2.0; import Ubuntu.Components 0.1; import Ubuntu.Components.Popups 0.1;"
412- + "Tabs {id: tabs; anchors.fill: parent; Tab { title: 'Loading...'; LoadingComponent{id:bigLoading; anchors.fill: parent; z: 2;}}}"
413- tabsObject = Qt.createQmlObject(tabsString, tabPage, "tabs")
414 mainView.refreshData();
415 }
416 }
417+ Action {
418+ id: editLocationAction
419+ iconSource: Qt.resolvedUrl("../resources/images/add_icon.png")
420+ text: i18n.tr("Edit")
421
422+ onTriggered: {
423+ mainView.showLocationManager()
424+ }
425+ }
426 Action {
427 id: configAction
428 iconSource: Qt.resolvedUrl("../resources/images/refresh_icon.png")
429
430=== removed file 'components/LocationTabEmpty.qml'
431--- components/LocationTabEmpty.qml 2013-04-20 16:17:57 +0000
432+++ components/LocationTabEmpty.qml 1970-01-01 00:00:00 +0000
433@@ -1,35 +0,0 @@
434-import QtQuick 2.0
435-import Ubuntu.Components.Popups 0.1
436-import Ubuntu.Components 0.1
437-
438-Tab {
439- id: locationTabEmpty
440-
441- title: i18n.tr("No Locations")
442-
443- // Menu for options
444- page: Page {
445- anchors.fill: parent
446- tools: ToolbarActions {
447- Action {
448- id: addLocationAction
449- property string cityCode: ''
450- objectName: "action"
451-
452- iconSource: Qt.resolvedUrl("../resources/images/add_icon.png")
453- text: i18n.tr("Add")
454-
455- onTriggered: {
456- PopupUtils.open(addLocationDialog, locationTabEmpty);
457- }
458- }
459- }
460-
461- Label {
462- anchors.centerIn: parent;
463- text: "Add one";
464- }
465-
466- }
467-
468-}
469
470=== modified file 'components/WeatherApi.js'
471--- components/WeatherApi.js 2013-06-09 15:42:41 +0000
472+++ components/WeatherApi.js 2013-06-12 17:57:26 +0000
473@@ -233,7 +233,7 @@
474 addDataToResponse = (function(data) {
475 response[data.request.type] = data;
476 if(response["current"] !== undefined
477- && response["forecast"] !== undefined
478+ //&& response["forecast"] !== undefined
479 && response["daily"] !== undefined) {
480 onSuccess(response);
481 }
482@@ -248,7 +248,7 @@
483 })
484
485 this.getCurrentCondition(params, addDataToResponse, retryHandler);
486- this.getForecast(params, addDataToResponse, retryHandler);
487+ //this.getForecast(params, addDataToResponse, retryHandler);
488 this.getDailyForecast(params, addDataToResponse, retryHandler);
489 }
490 }
491@@ -285,8 +285,6 @@
492 WeatherApi.search("name", message.params, finished, onError);
493 } else if(message.action === "searchByPoint") {
494 WeatherApi.search("point", message.params, finished, onError);
495- } else if(message.action === "newLocationData") {
496- WeatherApi.getLocationData(message.params, finished, onError);
497 } else if(message.action === "updateData") {
498 var locLength = message.params.locations.length,
499 locUpdated = 0,
500
501=== modified file 'ubuntu-weather-app.qml'
502--- ubuntu-weather-app.qml 2013-06-12 17:08:05 +0000
503+++ ubuntu-weather-app.qml 2013-06-12 17:57:26 +0000
504@@ -28,8 +28,6 @@
505 storage.updateLocation(loc.db.id, loc);
506 });
507 buildTabs(messageObject.result);
508- } else if(messageObject.action === "newLocationData") {
509- onNewLocationDataSuccess(messageObject.result);
510 }
511 } else {
512 console.log(messageObject.error.msg+" / "+messageObject.error.request.url)
513@@ -43,6 +41,12 @@
514 var locLength = locations.length,
515 locBeforeLen = locationsList.length,
516 focusToLast = (locBeforeLen > 0 && locLength > locBeforeLen) ? true : false;
517+ // show locationmanager when no location is added
518+ if(locLength === 0) {
519+ showLocationManager()
520+ return;
521+ }
522+
523 locationsList = locations;
524 if(tabsObject !== null) {
525 tabsObject.destroy()
526@@ -53,8 +57,6 @@
527 for(var x=0;x<locLength;x++) {
528 tabsString += "Components.LocationTab {title: '"+locations[x].location.name+"'; locationIndex: "+x+"} "
529 };
530- } else {
531- tabsString += "Components.LocationTabEmpty { }"
532 }
533 tabsString += "}"; // END Tabs componen
534 if(bigLoading !== null)
535@@ -65,24 +67,9 @@
536 tabsObject.selectedTabIndex = locLength -1;
537 }
538
539- function removeLocation(dbId) {
540- storage.clearLocation(dbId);
541- storage.getLocations(buildTabs);
542- }
543-
544- function locationAdded(locationObj) {
545- locationDataWorker.sendMessage({
546- action: "newLocationData",
547- params: {location:locationObj, units:settings["units"]}
548- });
549- }
550-
551- function onNewLocationDataSuccess(resp) {
552- var res = storage.insertLocation(resp);
553- storage.getLocations(buildTabs);
554- }
555-
556 function refreshData() {
557+ if(bigLoading === null)
558+ loading.running = true;
559 storage.getLocations(function(locations) {
560 locationDataWorker.sendMessage({
561 action: "updateData",
562@@ -91,6 +78,11 @@
563 });
564 }
565
566+ function showLocationManager() {
567+ pageStack.push(locationManager)
568+ locationManager.loadData()
569+ }
570+
571 Component.onCompleted: {
572 //storage.clearDB();
573 //storage.clearSetting('units');
574@@ -103,7 +95,7 @@
575 })
576 }
577
578- Components.AddLocationDialog {
579+ Components.AddLocationPage {
580 id: addLocationDialog
581 }
582
583@@ -115,17 +107,31 @@
584 id: storage
585 }
586
587- Page {
588- id: tabPage
589- title: "Simple page"
590- ActivityIndicator{id:loading; running: false; z: 1; anchors{top: parent.top; topMargin: units.gu(0.5); right: parent.right; rightMargin: units.gu(1)}}
591- Tabs {
592- id: tabs
593- objectName: "Tabs"
594- anchors.fill: parent
595- Tab {
596- title: "Loading..."
597- Components.LoadingComponent{id:bigLoading; anchors.fill: parent; z: 2;}
598+ Components.LocationManagerPage {
599+ id:locationManager
600+ }
601+
602+ Components.AddLocationPage {
603+ id:addLocationPage
604+ }
605+
606+ PageStack {
607+ id: pageStack
608+ Component.onCompleted: push(tabPage)
609+
610+ Page {
611+ id: tabPage
612+ title: "Simple page"
613+ visible: false
614+ ActivityIndicator{id:loading; running: false; z: 1; anchors{top: parent.top; topMargin: units.gu(0.5); right: parent.right; rightMargin: units.gu(1)}}
615+ Tabs {
616+ id: tabs
617+ objectName: "Tabs"
618+ anchors.fill: parent
619+ Tab {
620+ title: "Loading..."
621+ Components.LoadingComponent{id:bigLoading; anchors.fill: parent; z: 2;}
622+ }
623 }
624 }
625 }

Subscribers

People subscribed via source and target branches