Merge lp:~renatofilho/address-book-app/multiple-addressbook into lp:address-book-app

Proposed by Renato Araujo Oliveira Filho
Status: Superseded
Proposed branch: lp:~renatofilho/address-book-app/multiple-addressbook
Merge into: lp:address-book-app
Diff against target: 1059 lines (+305/-187)
22 files modified
src/imports/Common/ContactDetailBase.qml (+6/-0)
src/imports/Common/ContactDetailGroupBase.qml (+26/-5)
src/imports/Common/ContactDetailGroupWithTypeBase.qml (+6/-2)
src/imports/ContactEdit/CMakeLists.txt (+1/-0)
src/imports/ContactEdit/ContactDetailAvatarEditor.qml (+4/-0)
src/imports/ContactEdit/ContactDetailGroupWithTypeEditor.qml (+26/-13)
src/imports/ContactEdit/ContactDetailNameEditor.qml (+4/-0)
src/imports/ContactEdit/ContactDetailSyncTargetEditor.qml (+73/-0)
src/imports/ContactEdit/ContactDetailWithTypeEditor.qml (+18/-19)
src/imports/ContactEdit/ContactEditor.qml (+45/-84)
src/imports/ContactList/ContactListPage.qml (+5/-21)
src/imports/ContactView/ContactDetailAvatarView.qml (+5/-2)
src/imports/ContactView/ContactDetailGroupWithTypeView.qml (+1/-1)
src/imports/ContactView/ContactDetailPhoneNumberView.qml (+7/-3)
src/imports/ContactView/ContactDetailPhoneNumbersView.qml (+1/-1)
src/imports/ContactView/ContactDetailWithTypeView.qml (+7/-3)
src/imports/ContactView/ContactView.qml (+23/-10)
src/imports/MainWindow.qml (+26/-0)
src/imports/Ubuntu/Contacts/ContactDetailOnlineAccountTypeModel.qml (+0/-3)
src/imports/Ubuntu/Contacts/ContactDetailPhoneNumberTypeModel.qml (+0/-2)
src/imports/Ubuntu/Contacts/ContactFetch.qml (+15/-17)
tests/autopilot/address_book_app/tests/test_edit_contact.py (+6/-1)
To merge this branch: bzr merge lp:~renatofilho/address-book-app/multiple-addressbook
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Ubuntu Phablet Team Pending
Review via email: mp+209549@code.launchpad.net

This proposal has been superseded by a proposal from 2014-03-19.

Commit message

Implemented support to create contacts in different sources.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:143
http://jenkins.qa.ubuntu.com/job/address-book-app-ci/446/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-amd64-ci/156
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-armhf-ci/156
        deb: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-armhf-ci/156/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/address-book-app-trusty-i386-ci/156
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/3691
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty-touch/3285/console
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-trusty/3242
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/3696
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/3696/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/3287
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/3287/artifact/work/output/*zip*/output.zip
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-runner-mako/5657/console
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/4503

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/address-book-app-ci/446/rebuild

review: Needs Fixing (continuous-integration)
144. By Renato Araujo Oliveira Filho

Merged: lp:~renatofilho/address-book-app/optimize-app

145. By Renato Araujo Oliveira Filho

Merged parent branch.

146. By Renato Araujo Oliveira Filho

Merged parent branch.

147. By Renato Araujo Oliveira Filho

Added missing file.

148. By Renato Araujo Oliveira Filho

Fixed problem saving contact without sync target.

149. By Renato Araujo Oliveira Filho

Merged parent branch.

150. By Renato Araujo Oliveira Filho

Set the default icon for address-book.

151. By Renato Araujo Oliveira Filho

Merged parent branch: lp:~renatofilho/address-book-app/optimize-app

152. By Renato Araujo Oliveira Filho

Merged mainline.

153. By Renato Araujo Oliveira Filho

Fixed scroll to created contact.

154. By Renato Araujo Oliveira Filho

Scroll to contact based on the contact name if the contact id is empty

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/imports/Common/ContactDetailBase.qml'
2--- src/imports/Common/ContactDetailBase.qml 2013-11-14 13:25:28 +0000
3+++ src/imports/Common/ContactDetailBase.qml 2014-03-19 18:42:11 +0000
4@@ -61,6 +61,10 @@
5 imMap[QtContacts.OnlineAccount.Protocol] = "imProtocol"
6 imMap[QtContacts.OnlineAccount.Capabilities] = "imCaps"
7
8+ // SyncTarget
9+ var syncTargetMap = {}
10+ syncTargetMap[QtContacts.SyncTarget.SyncTarget] = "syncTarget"
11+
12 // all
13 var detailMap = {}
14 detailMap[QtContacts.ContactDetail.Name] = nameMap
15@@ -68,6 +72,7 @@
16 detailMap[QtContacts.ContactDetail.Email] = emailMap
17 detailMap[QtContacts.ContactDetail.Address] = addressMap
18 detailMap[QtContacts.ContactDetail.OnlineAccount] = imMap
19+ detailMap[QtContacts.ContactDetail.SyncTarget] = syncTargetMap
20
21 // detail name
22 var detailNameMap = {}
23@@ -76,6 +81,7 @@
24 detailNameMap[QtContacts.ContactDetail.Email] = "email"
25 detailNameMap[QtContacts.ContactDetail.Address] = "address"
26 detailNameMap[QtContacts.ContactDetail.OnlineAccount] = "onlineAccount"
27+ detailNameMap[QtContacts.ContactDetail.SyncTarget] = "syncTarget"
28
29 if ((detail in detailMap) && (field in detailMap[detail])) {
30 return detailMap[detail][field]
31
32=== modified file 'src/imports/Common/ContactDetailGroupBase.qml'
33--- src/imports/Common/ContactDetailGroupBase.qml 2014-01-14 20:43:59 +0000
34+++ src/imports/Common/ContactDetailGroupBase.qml 2014-03-19 18:42:11 +0000
35@@ -21,7 +21,7 @@
36 FocusScope {
37 id: root
38
39- readonly property variant details: contact && contact.contactDetails && detailType ? contact.details(detailType) : []
40+ property var details: []
41 readonly property alias detailDelegates: contents.children
42 readonly property int detailsCount: detailsModel.count
43
44@@ -38,7 +38,27 @@
45
46 signal newFieldAdded(var index)
47
48- implicitHeight: detailsModel.values.length > 0 ? contents.implicitHeight : minimumHeight
49+ function reloadDetails(clearFields)
50+ {
51+ if (clearFields) {
52+ root.inputFields = []
53+ }
54+
55+ if (contact && detailType) {
56+ root.details = contact.details(detailType)
57+ } else {
58+ root.details = []
59+ }
60+ }
61+
62+ onContactChanged: reloadDetails(true)
63+ onDetailTypeChanged: reloadDetails(true)
64+ Connections {
65+ target: root.contact
66+ onContactChanged: reloadDetails(false)
67+ }
68+
69+ implicitHeight: detailsCount > 0 ? contents.implicitHeight : minimumHeight
70 visible: implicitHeight > 0
71
72 // This model is used to avoid rebuild the repeater every time that the details change
73@@ -46,7 +66,7 @@
74 ListModel {
75 id: detailsModel
76
77- property var values: root.showEmpty ? root.details : filterDetails(root.details)
78+ property var values: root.showEmpty && root.details ? root.details : filterDetails(root.details)
79
80 function filterDetails(details) {
81 var result = []
82@@ -68,6 +88,7 @@
83
84 onValuesChanged: {
85 if (!values) {
86+ root.inputFields = []
87 clear()
88 return
89 }
90@@ -78,7 +99,7 @@
91
92 var modelCount = count
93 for(var i=0; i < values.length; i++) {
94- if (modelCount < i) {
95+ if (modelCount <= i) {
96 append({"detail": values[i]})
97 } else if (get(i) != values[i]) {
98 set(i, {"detail": values[i]})
99@@ -112,7 +133,7 @@
100 Binding {
101 target: detailItem.item
102 property: "detail"
103- value: root.contact && root.details ? root.details[index] : null
104+ value: model.detail
105 }
106
107 Binding {
108
109=== modified file 'src/imports/Common/ContactDetailGroupWithTypeBase.qml'
110--- src/imports/Common/ContactDetailGroupWithTypeBase.qml 2014-01-14 20:43:59 +0000
111+++ src/imports/Common/ContactDetailGroupWithTypeBase.qml 2014-03-19 18:42:11 +0000
112@@ -23,6 +23,7 @@
113
114 property string defaultIcon : "artwork:/protocol-other.png"
115 property ListModel typeModel
116+ property bool typeModelReady: false
117
118 function getType(detail) {
119 if (typeModel) {
120@@ -40,7 +41,6 @@
121 }
122
123 typeModel: ListModel {
124- property bool ready: false
125 signal loaded()
126
127 function getTypeIndex(detail) {
128@@ -116,8 +116,12 @@
129 append({"value": QtContacts.ContactDetail.ContextHome, "label": i18n.tr("Home"), "icon": null})
130 append({"value": QtContacts.ContactDetail.ContextWork, "label": i18n.tr("Work"), "icon": null})
131 append({"value": QtContacts.ContactDetail.ContextOther, "label": i18n.tr("Other"), "icon": null})
132- ready = true
133 loaded()
134 }
135 }
136+ onTypeModelChanged: root.typeModelReady = false
137+ Connections {
138+ target: root.typeModel
139+ onLoaded: root.typeModelReady = true
140+ }
141 }
142
143=== modified file 'src/imports/ContactEdit/CMakeLists.txt'
144--- src/imports/ContactEdit/CMakeLists.txt 2013-09-24 15:07:46 +0000
145+++ src/imports/ContactEdit/CMakeLists.txt 2014-03-19 18:42:11 +0000
146@@ -7,6 +7,7 @@
147 ContactDetailOnlineAccountsEditor.qml
148 ContactDetailOrganizationsEditor.qml
149 ContactDetailPhoneNumbersEditor.qml
150+ ContactDetailSyncTargetEditor.qml
151 ContactDetailWithTypeEditor.qml
152 ContactEditor.qml
153 ContactFetchError.qml
154
155=== modified file 'src/imports/ContactEdit/ContactDetailAvatarEditor.qml'
156--- src/imports/ContactEdit/ContactDetailAvatarEditor.qml 2013-10-15 14:34:03 +0000
157+++ src/imports/ContactEdit/ContactDetailAvatarEditor.qml 2014-03-19 18:42:11 +0000
158@@ -27,6 +27,10 @@
159
160 readonly property string defaultAvatar: Qt.resolvedUrl("../../artwork/contact-default-profile.png")
161
162+ function isEmpty() {
163+ return false;
164+ }
165+
166 function save() {
167 if (avatarImage.source != root.defaultAvatar) {
168 if (root.detail && (root.detail === avatarImage.source)) {
169
170=== modified file 'src/imports/ContactEdit/ContactDetailGroupWithTypeEditor.qml'
171--- src/imports/ContactEdit/ContactDetailGroupWithTypeEditor.qml 2014-02-19 12:06:20 +0000
172+++ src/imports/ContactEdit/ContactDetailGroupWithTypeEditor.qml 2014-03-19 18:42:11 +0000
173@@ -40,6 +40,7 @@
174
175 function save() {
176 var changed = false
177+ var removedDetails = []
178 for(var i=0; i < detailDelegates.length; i++) {
179 var delegate = detailDelegates[i]
180
181@@ -49,17 +50,30 @@
182 }
183
184 if (delegate.save) {
185- // save type
186- if (updateDetail(delegate.detail, delegate.selectedTypeIndex)) {
187- changed = true
188- }
189-
190- // save fields
191- if (delegate.save()) {
192- changed = true
193- }
194- }
195- }
196+ // check if was removed
197+ if (delegate.isEmpty()) {
198+ removedDetails.push(delegate.detail)
199+ changed = true
200+ } else {
201+ if (updateDetail(delegate.detail, delegate.selectedTypeIndex)) {
202+ changed = true
203+ }
204+
205+ // save field changes
206+ if (delegate.save()) {
207+ changed = true
208+ }
209+ }
210+ }
211+ }
212+
213+ for(var i=0; i < removedDetails.length; i++) {
214+ if (contact.isPreferredDetail("TEL", removedDetails[i])) {
215+ contact.favorite.favorite = false
216+ }
217+ contact.removeDetail(removedDetails[i])
218+ }
219+
220 return changed
221 }
222
223@@ -146,7 +160,6 @@
224 }
225 }
226 }
227-
228 placeholderTexts: root.placeholderTexts
229 contact: root.contact
230 fields: root.fields
231@@ -161,7 +174,7 @@
232 Item {
233 Connections {
234 target: root.typeModel
235- onReadyChanged: updateCombo(true)
236+ onLoaded: updateCombo(true)
237 }
238 }
239 }
240
241=== modified file 'src/imports/ContactEdit/ContactDetailNameEditor.qml'
242--- src/imports/ContactEdit/ContactDetailNameEditor.qml 2014-01-23 01:09:04 +0000
243+++ src/imports/ContactEdit/ContactDetailNameEditor.qml 2014-03-19 18:42:11 +0000
244@@ -26,6 +26,10 @@
245 property bool isEmpty: (fields == -1) || (emptyFields.length === fields.length)
246 property variant emptyFields: []
247
248+ function isEmpty() {
249+ return false
250+ }
251+
252 function save() {
253 var changed = false;
254
255
256=== added file 'src/imports/ContactEdit/ContactDetailSyncTargetEditor.qml'
257--- src/imports/ContactEdit/ContactDetailSyncTargetEditor.qml 1970-01-01 00:00:00 +0000
258+++ src/imports/ContactEdit/ContactDetailSyncTargetEditor.qml 2014-03-19 18:42:11 +0000
259@@ -0,0 +1,73 @@
260+/*
261+ * Copyright (C) 2012-2013 Canonical, Ltd.
262+ *
263+ * This program is free software; you can redistribute it and/or modify
264+ * it under the terms of the GNU General Public License as published by
265+ * the Free Software Foundation; version 3.
266+ *
267+ * This program is distributed in the hope that it will be useful,
268+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
269+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
270+ * GNU General Public License for more details.
271+ *
272+ * You should have received a copy of the GNU General Public License
273+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
274+ */
275+
276+import QtQuick 2.0
277+import Ubuntu.Components 0.1
278+import QtContacts 5.0
279+
280+import "../Common"
281+
282+ContactDetailBase {
283+ id: root
284+
285+ function save() {
286+ // only changes the target sync for new contacts
287+ if (!isNewContact) {
288+ return;
289+ }
290+
291+ var activeSource = getSelectedSource()
292+ if (!root.detail) {
293+ root.detail = root.contact.syncTarget
294+ }
295+ root.detail.syncTarget = activeSource
296+ }
297+
298+ function getSelectedSource() {
299+ return sourceModel.contacts[sources.selectedIndex].guid.guid
300+ }
301+
302+ property bool isNewContact: contact && contact.contactId === "qtcontacts:::"
303+
304+ detail: contact ? contact.detail(ContactDetail.SyncTarget) : null
305+ implicitHeight: isNewContact ? units.gu(5) : 0
306+
307+ ContactModel {
308+ id: sourceModel
309+
310+ manager: QTCONTACTS_MANAGER_OVERRIDE && QTCONTACTS_MANAGER_OVERRIDE != "" ? QTCONTACTS_MANAGER_OVERRIDE : "galera"
311+ filter: DetailFilter {
312+ detail: ContactDetail.Type
313+ field: Type.TypeField
314+ value: Type.Group
315+ matchFlags: DetailFilter.MatchExactly
316+ }
317+ }
318+
319+ OptionSelector {
320+ id: sources
321+
322+ anchors.fill: parent
323+ model: sourceModel
324+ delegate: OptionSelectorDelegate {
325+ text: contact.displayLabel.label
326+ }
327+
328+ containerHeight: itemHeight * 4
329+ }
330+ z: 1000
331+}
332+
333
334=== modified file 'src/imports/ContactEdit/ContactDetailWithTypeEditor.qml'
335--- src/imports/ContactEdit/ContactDetailWithTypeEditor.qml 2014-01-28 14:28:58 +0000
336+++ src/imports/ContactEdit/ContactDetailWithTypeEditor.qml 2014-03-19 18:42:11 +0000
337@@ -36,33 +36,32 @@
338 detailTypeSelector.selectItem(type)
339 }
340
341+ function isEmpty() {
342+ for (var i=0; i < fieldValues.children.length; i++) {
343+ var input = fieldValues.children[i]
344+ if (input.text && (input.text !== "")) {
345+ return false
346+ }
347+ }
348+ return true
349+ }
350+
351 function save() {
352 var detailchanged = false
353
354 // save field values
355- var isEmpty = true
356 for (var i=0; i < fieldValues.children.length; i++) {
357 var input = fieldValues.children[i]
358 if (input.detail && (input.field >= 0)) {
359- var originalValue = input.detail.value(input.field)
360- originalValue = originalValue ? String(originalValue) : ""
361 if (input.text !== "") {
362- isEmpty = false
363- }
364-
365- if (originalValue !== input.text) {
366- input.detail.setValue(input.field, input.text)
367- detailchanged = true
368- }
369- }
370- }
371-
372- if (isEmpty) {
373- // unfavorite the contact if the favorite number was removed
374- if (contact.isPreferredDetail("TEL", detail)) {
375- contact.favorite.favorite = false
376- }
377- contact.removeDetail(input.detail)
378+ var originalValue = input.detail.value(input.field)
379+ originalValue = originalValue ? String(originalValue) : ""
380+ if (originalValue !== input.text) {
381+ root.detail.setValue(input.field, input.text)
382+ detailchanged = true
383+ }
384+ }
385+ }
386 }
387
388 return detailchanged
389
390=== modified file 'src/imports/ContactEdit/ContactEditor.qml'
391--- src/imports/ContactEdit/ContactEditor.qml 2013-11-21 12:14:04 +0000
392+++ src/imports/ContactEdit/ContactEditor.qml 2014-03-19 18:42:11 +0000
393@@ -19,16 +19,16 @@
394 import Ubuntu.Components 0.1
395 import Ubuntu.Components.ListItems 0.1 as ListItem
396 import Ubuntu.Components.Popups 0.1
397+import Ubuntu.Contacts 0.1 as ContactsUI
398
399 Page {
400 id: contactEditor
401 objectName: "contactEditorPage"
402
403 property QtObject contact: null
404- property QtObject model: null
405+ property alias model: contactFetch.model
406
407 // this is used to add a phone number to a existing contact
408- property int currentFetchOperation: -1
409 property string contactId: ""
410 property string newPhoneNumber: ""
411
412@@ -78,10 +78,10 @@
413 }
414
415 if (changed) {
416- model.saveContact(contact)
417- } else {
418- pageStack.pop()
419+ // backend error will be handled by the root page (contact list)
420+ contactEditor.model.saveContact(contact)
421 }
422+ pageStack.pop()
423 }
424
425 function makeMeVisible(item) {
426@@ -114,39 +114,10 @@
427 id: fetchErrorDialog
428 }
429
430- Connections {
431- target: model
432- onContactsFetched: {
433- if (requestId == currentFetchOperation) {
434- currentFetchOperation = -1
435- // this fetch request can only return one contact
436- if(fetchedContacts.length !== 1) {
437- PopupUtils.open(fetchErrorDialog, null)
438- }
439- contact = fetchedContacts[0]
440- }
441- }
442- }
443-
444- onContactIdChanged: {
445- if (contactId) {
446- currentFetchOperation = model.fetchContacts(contactId)
447- }
448- }
449-
450- onContactChanged: {
451- if (contact && (newPhoneNumber.length > 0)) {
452- var detailSourceTemplate = "import QtContacts 5.0; PhoneNumber{ number: \"" + newPhoneNumber + "\" }"
453- var newDetail = Qt.createQmlObject(detailSourceTemplate, contactEditor)
454- if (newDetail) {
455- contact.addDetail(newDetail)
456- // we need to wait for the field be created
457- focusTimer.restart()
458-
459- }
460- newPhoneNumber = ""
461-
462- }
463+ ContactsUI.ContactFetch {
464+ id: contactFetch
465+
466+ onContactNotFound: PopupUtils.open(fetchErrorDialog, null)
467 }
468
469 Timer {
470@@ -176,7 +147,6 @@
471 }
472 contentHeight: contents.height
473 contentWidth: parent.width
474- visible: !busyIndicator.visible
475
476 // after add a new field we need to wait for the contentHeight to change to scroll to the correct position
477 onContentHeightChanged: contactEditor.makeMeVisible(contactEditor.activeItem)
478@@ -192,6 +162,18 @@
479 }
480 height: childrenRect.height
481
482+ ContactDetailSyncTargetEditor {
483+ id: syncTargetEditor
484+
485+ contact: contactEditor.contact
486+ anchors {
487+ left: parent.left
488+ right: parent.right
489+ }
490+ height: implicitHeight
491+ KeyNavigation.tab: nameEditor
492+ }
493+
494 ContactDetailNameEditor {
495 id: nameEditor
496
497@@ -202,6 +184,7 @@
498 }
499 height: implicitHeight + units.gu(3)
500 KeyNavigation.tab: avatarEditor
501+ KeyNavigation.backtab : syncTargetEditor
502 }
503
504 ContactDetailAvatarEditor {
505@@ -286,46 +269,6 @@
506 }
507 }
508
509- Component.onCompleted: nameEditor.forceActiveFocus()
510-
511- ActivityIndicator {
512- id: busyIndicator
513-
514- running: contactSaveLock.saving
515- visible: running
516- anchors.centerIn: parent
517- }
518-
519- Connections {
520- id: contactSaveLock
521-
522- property bool saving: false
523-
524- target: contactEditor.model
525-
526- onContactsChanged: {
527- if (saving) {
528- saving = false
529- pageStack.contactCreated(contactEditor.contact)
530- pageStack.pop()
531- } else if (contactEditor.contact &&
532- (contactEditor.contact.contactId != "qtcontacts:::")) {
533- for (var i=0; i < contactEditor.model.contacts.length; i++) {
534- if (contactEditor.model.contacts[i].contactId == contactEditor.contact.contactId) {
535- return
536- }
537- }
538- contactEditor.contact = null
539- pageStack.pop()
540- }
541- }
542-
543- onErrorChanged: {
544- //TODO: show a dialog
545- console.debug("Save error:" + contactEditor.model.error)
546- }
547- }
548-
549 EditToolbar {
550 id: toolbar
551 anchors {
552@@ -337,11 +280,7 @@
553 acceptAction: Action {
554 text: i18n.tr("Save")
555 enabled: !nameEditor.isEmpty
556- onTriggered: {
557- // wait for contact to be saved or cause a error
558- contactSaveLock.saving = true
559- contactEditor.save()
560- }
561+ onTriggered: contactEditor.save()
562 }
563 rejectAction: Action {
564 text: i18n.tr("Cancel")
565@@ -358,4 +297,26 @@
566 }
567 }
568 }
569+
570+ // This will load the contact information and add the new phone number
571+ // when the app was launched with the URI: addressbook:///addphone?id=<contact-id>&phone=<phone-number>
572+ onContactChanged: {
573+ if (contact && (newPhoneNumber.length > 0)) {
574+ var detailSourceTemplate = "import QtContacts 5.0; PhoneNumber{ number: \"" + newPhoneNumber + "\" }"
575+ var newDetail = Qt.createQmlObject(detailSourceTemplate, contactEditor)
576+ if (newDetail) {
577+ contact.addDetail(newDetail)
578+ // we need to wait for the field be created
579+ focusTimer.restart()
580+ }
581+ newPhoneNumber = ""
582+ }
583+ }
584+
585+ Component.onCompleted: {
586+ if (contactId !== "") {
587+ contactFetch.fetchContact(contactId)
588+ }
589+ nameEditor.forceActiveFocus()
590+ }
591 }
592
593=== modified file 'src/imports/ContactList/ContactListPage.qml'
594--- src/imports/ContactList/ContactListPage.qml 2014-02-25 20:20:21 +0000
595+++ src/imports/ContactList/ContactListPage.qml 2014-03-19 18:42:11 +0000
596@@ -17,7 +17,6 @@
597 import QtQuick 2.0
598 import Ubuntu.Components 0.1
599 import Ubuntu.Components.ListItems 0.1 as ListItem
600-import Ubuntu.Components.Popups 0.1 as Popups
601 import Ubuntu.Contacts 0.1 as ContactsUI
602 import QtContacts 5.0
603
604@@ -48,25 +47,7 @@
605 return newContact
606 }
607
608-
609-
610 title: i18n.tr("Contacts")
611- Component {
612- id: dialog
613-
614- Popups.Dialog {
615- id: dialogue
616-
617- title: i18n.tr("Error")
618- text: i18n.tr("Fail to Load contacts")
619-
620- Button {
621- text: "Cancel"
622- gradient: UbuntuColors.greyGradient
623- onClicked: PopupUtils.close(dialogue)
624- }
625- }
626- }
627
628 ContactsUI.ContactListView {
629 id: contactList
630@@ -83,7 +64,6 @@
631 bottomMargin: contactList.isInSelectionMode ? 0 : units.gu(2)
632 fill: parent
633 }
634- onError: PopupUtils.open(dialog, null)
635 swipeToDelete: !pickMode
636
637 ActivityIndicator {
638@@ -96,7 +76,8 @@
639
640 onContactClicked: {
641 pageStack.push(Qt.resolvedUrl("../ContactView/ContactView.qml"),
642- {model: contactList.listModel, contactId: contact.contactId})
643+ {model: contactList.listModel,
644+ contact: contact})
645 }
646
647 onSelectionDone: {
648@@ -116,6 +97,7 @@
649 contactList.listModel.removeContacts(ids)
650 }
651 }
652+
653 onSelectionCanceled: {
654 if (pickMode) {
655 if (contactContentHub) {
656@@ -131,6 +113,8 @@
657 toolbar.opened = false
658 }
659 }
660+
661+ onError: pageStack.contactModelError(error)
662 }
663
664 tools: ToolbarItems {
665
666=== modified file 'src/imports/ContactView/ContactDetailAvatarView.qml'
667--- src/imports/ContactView/ContactDetailAvatarView.qml 2013-10-08 18:42:21 +0000
668+++ src/imports/ContactView/ContactDetailAvatarView.qml 2014-03-19 18:42:11 +0000
669@@ -23,10 +23,12 @@
670 ContactDetailBase {
671 id: root
672
673+ readonly property string defaultAvatar: Qt.resolvedUrl("../../artwork/contact-default-profile.png")
674+
675 function getAvatar(avatarDetail)
676 {
677 // use this verbose mode to avoid problems with binding loops
678- var avatarUrl = Qt.resolvedUrl("../../artwork/contact-default-profile.png")
679+ var avatarUrl = defaultAvatar
680 if (avatarDetail) {
681 var avatarValue = avatarDetail.value(Avatar.ImageUrl)
682 if (avatarValue != "") {
683@@ -41,7 +43,8 @@
684
685 Image {
686 anchors.fill: parent
687- source: root.getAvatar(root.detail)
688+ // make sure that the avatar changes if the contact changes
689+ source: root.contact ? root.getAvatar(root.detail) : root.defaultAvatar
690 asynchronous: true
691 smooth: true
692 fillMode: Image.PreserveAspectCrop
693
694=== modified file 'src/imports/ContactView/ContactDetailGroupWithTypeView.qml'
695--- src/imports/ContactView/ContactDetailGroupWithTypeView.qml 2014-01-14 20:43:59 +0000
696+++ src/imports/ContactView/ContactDetailGroupWithTypeView.qml 2014-03-19 18:42:11 +0000
697@@ -51,7 +51,7 @@
698 }
699
700 detailDelegate: ContactDetailWithTypeView {
701- property variant detailType: detail && root.contact && root.typeModel.ready ? root.getType(detail) : null
702+ property variant detailType: detail && root.contact && root.typeModelReady ? root.getType(detail) : ""
703
704 action: root.defaultAction
705 contact: root.contact
706
707=== modified file 'src/imports/ContactView/ContactDetailPhoneNumberView.qml'
708--- src/imports/ContactView/ContactDetailPhoneNumberView.qml 2013-11-14 13:25:28 +0000
709+++ src/imports/ContactView/ContactDetailPhoneNumberView.qml 2014-03-19 18:42:11 +0000
710@@ -25,10 +25,11 @@
711
712 property alias typeLabel: view.typeLabel
713 property alias lineHeight: view.lineHeight
714+ readonly property bool isReady: (fields != null) && (detail != null)
715
716 function populateValues()
717 {
718- if (fields && detail) {
719+ if (isReady) {
720 var values = []
721 for(var i=0; i < fields.length; i++) {
722 values.push(detail.value(fields[i]))
723@@ -38,8 +39,11 @@
724 }
725
726 implicitHeight: view.implicitHeight
727- onFieldsChanged: populateValues()
728- onDetailChanged: populateValues()
729+ onIsReadyChanged: populateValues()
730+ Connections {
731+ target: root.detail
732+ onDetailChanged: populateValues()
733+ }
734
735 BasicFieldView {
736 id: view
737
738=== modified file 'src/imports/ContactView/ContactDetailPhoneNumbersView.qml'
739--- src/imports/ContactView/ContactDetailPhoneNumbersView.qml 2014-02-25 21:52:58 +0000
740+++ src/imports/ContactView/ContactDetailPhoneNumbersView.qml 2014-03-19 18:42:11 +0000
741@@ -35,7 +35,7 @@
742 }
743
744 detailDelegate: ContactDetailPhoneNumberView {
745- property variant detailType: detail && root.contact && root.typeModel.ready ? root.getType(detail) : null
746+ property variant detailType: detail && root.contact && root.typeModelReady ? root.getType(detail) : null
747
748 contact: root.contact
749 fields: root.fields
750
751=== modified file 'src/imports/ContactView/ContactDetailWithTypeView.qml'
752--- src/imports/ContactView/ContactDetailWithTypeView.qml 2013-11-14 17:48:48 +0000
753+++ src/imports/ContactView/ContactDetailWithTypeView.qml 2014-03-19 18:42:11 +0000
754@@ -27,10 +27,11 @@
755 property alias typeLabel: view.typeLabel
756 property string typeIcon: null
757 property alias lineHeight: view.lineHeight
758+ readonly property bool isReady: (fields != null) && (detail != null)
759
760 function populateValues()
761 {
762- if (fields && detail) {
763+ if (isReady) {
764 var values = []
765 for(var i=0; i < fields.length; i++) {
766 values.push(detail.value(fields[i]))
767@@ -39,9 +40,12 @@
768 }
769 }
770
771- onFieldsChanged: populateValues()
772- onDetailChanged: populateValues()
773 implicitHeight: view.implicitHeight
774+ onIsReadyChanged: populateValues()
775+ Connections {
776+ target: root.detail
777+ onDetailChanged: populateValues()
778+ }
779
780 BasicFieldView {
781 id: view
782
783=== modified file 'src/imports/ContactView/ContactView.qml'
784--- src/imports/ContactView/ContactView.qml 2014-02-25 21:52:58 +0000
785+++ src/imports/ContactView/ContactView.qml 2014-03-19 18:42:11 +0000
786@@ -24,15 +24,15 @@
787 id: root
788 objectName: "contactViewPage"
789
790- readonly property alias contact: contactFetch.contact
791- property variant contactId: null
792+ property QtObject contact: null
793 property alias model: contactFetch.model
794+ // used by main page to open the contact view on app startup
795+ property string contactId: ""
796
797 function formatNameToDisplay(contact) {
798 if (!contact) {
799 return ""
800 }
801-
802 if (contact.name) {
803 var detail = contact.name
804 return detail.firstName +" " + detail.lastName
805@@ -43,14 +43,10 @@
806 }
807 }
808
809-
810 title: formatNameToDisplay(contact)
811 onActiveChanged: {
812 if (active) {
813- contactFetch.fetchContact(root.contactId)
814-
815 //WORKAROUND: to correct scroll back the page
816- flickable.contentY = -100
817 flickable.returnToBounds()
818 }
819 }
820@@ -141,14 +137,13 @@
821 }
822 height: implicitHeight
823 }
824-
825 }
826 }
827
828 ActivityIndicator {
829 id: busyIndicator
830
831- running: contactFetch.running
832+ running: (root.contact === null) && contactFetch.running
833 visible: running
834 anchors.centerIn: parent
835 }
836@@ -156,7 +151,17 @@
837 ContactsUI.ContactFetch {
838 id: contactFetch
839
840- onContactRemoved: pageStack.pop()
841+ onContactRemoved: {
842+ pageStack.pop()
843+ }
844+
845+ onContactNotFound: {
846+ pageStack.pop()
847+ }
848+
849+ onContactFetched: {
850+ root.contact = contact
851+ }
852 }
853
854 tools: ToolbarItems {
855@@ -185,4 +190,12 @@
856 }
857 }
858 }
859+
860+ // This will load the contact information when the app was launched with
861+ // the URI: addressbook:///contact?id=<id>
862+ Component.onCompleted: {
863+ if (contactId !== "") {
864+ contactFetch.fetchContact(contactId)
865+ }
866+ }
867 }
868
869=== modified file 'src/imports/MainWindow.qml'
870--- src/imports/MainWindow.qml 2014-02-25 20:06:47 +0000
871+++ src/imports/MainWindow.qml 2014-03-19 18:42:11 +0000
872@@ -17,10 +17,13 @@
873 import QtQuick 2.0
874 import QtContacts 5.0
875 import Ubuntu.Components 0.1
876+import Ubuntu.Components.Popups 0.1 as Popups
877
878 MainView {
879 id: mainWindow
880
881+ property string modelErrorMessage: ""
882+
883 width: units.gu(40)
884 height: units.gu(71)
885 anchorToKeyboard: false
886@@ -59,6 +62,7 @@
887 signal createContactRequested(string phoneNumber)
888 signal editContatRequested(string contactId, string phoneNumber)
889 signal contactCreated(QtObject contact)
890+ signal contactModelError(string errorMessage)
891
892 anchors {
893 fill: parent
894@@ -69,6 +73,11 @@
895 }
896 }
897 }
898+
899+ onContactModelError: {
900+ modelErrorMessage = errorMessage
901+ PopupUtils.open(errorDialog, null)
902+ }
903 }
904
905 Component.onCompleted: {
906@@ -76,6 +85,23 @@
907 mainWindow.applicationReady()
908 }
909
910+ Component {
911+ id: errorDialog
912+
913+ Popups.Dialog {
914+ id: dialogue
915+
916+ title: i18n.tr("Error")
917+ text: mainWindow.modelErrorMessage
918+
919+ Button {
920+ text: "Cancel"
921+ gradient: UbuntuColors.greyGradient
922+ onClicked: PopupUtils.close(dialogue)
923+ }
924+ }
925+ }
926+
927 Connections {
928 target: UriHandler
929 onOpened: {
930
931=== modified file 'src/imports/Ubuntu/Contacts/ContactDetailOnlineAccountTypeModel.qml'
932--- src/imports/Ubuntu/Contacts/ContactDetailOnlineAccountTypeModel.qml 2014-01-14 20:43:59 +0000
933+++ src/imports/Ubuntu/Contacts/ContactDetailOnlineAccountTypeModel.qml 2014-03-19 18:42:11 +0000
934@@ -20,8 +20,6 @@
935 ListModel {
936 id: typeModel
937
938- property bool ready: false
939-
940 signal loaded()
941
942 function getTypeIndex(detail) {
943@@ -69,6 +67,5 @@
944 /*4*/ append({"value": 7, "label": i18n.tr("Skype"), "icon": "artwork:/protocol-skype.svg"})
945 /*5*/ append({"value": 8, "label": i18n.tr("Yahoo"), "icon": "artwork:/protocol-yahoo.svg"})
946 loaded()
947- ready = true
948 }
949 }
950
951=== modified file 'src/imports/Ubuntu/Contacts/ContactDetailPhoneNumberTypeModel.qml'
952--- src/imports/Ubuntu/Contacts/ContactDetailPhoneNumberTypeModel.qml 2014-01-14 20:43:59 +0000
953+++ src/imports/Ubuntu/Contacts/ContactDetailPhoneNumberTypeModel.qml 2014-03-19 18:42:11 +0000
954@@ -20,7 +20,6 @@
955 ListModel {
956 id: typeModel
957
958- property bool ready: false
959 signal loaded()
960
961 function getTypeIndex(detail) {
962@@ -103,6 +102,5 @@
963 append({"value": "Other", "label": i18n.tr("Other"), "icon": null,
964 "context": QtContacts.ContactDetail.ContextOther, "subType": QtContacts.PhoneNumber.Landline })
965 loaded()
966- ready = true
967 }
968 }
969
970=== modified file 'src/imports/Ubuntu/Contacts/ContactFetch.qml'
971--- src/imports/Ubuntu/Contacts/ContactFetch.qml 2013-11-04 14:56:40 +0000
972+++ src/imports/Ubuntu/Contacts/ContactFetch.qml 2014-03-19 18:42:11 +0000
973@@ -19,9 +19,11 @@
974 Item {
975 id: root
976
977+ readonly property alias contact: connections.contact
978+ readonly property alias contactId: connections.contactId
979 property alias model: connections.target
980+
981 property bool running: false
982- property QtObject contact: null
983 property bool contactIsDirty: false
984
985 property string _pendingId: ""
986@@ -29,6 +31,7 @@
987
988 signal contactFetched(QtObject contact)
989 signal contactRemoved()
990+ signal contactNotFound()
991
992 function fetchContact(contactId) {
993 if (root._ready) {
994@@ -58,20 +61,9 @@
995 }
996 }
997
998- Connections {
999- target: root.model
1000-
1001- onContactsChanged: {
1002- if (root.contact) {
1003- root.contactIsDirty = true
1004-
1005- for (var i=0; i < root.model.contacts.length; i++) {
1006- if (root.model.contacts[i].contactId == root.contact.contactId) {
1007- return
1008- }
1009- }
1010- contactRemoved()
1011- }
1012+ onContactChanged: {
1013+ if (contact == null) {
1014+ contactRemoved()
1015 }
1016 }
1017
1018@@ -79,6 +71,8 @@
1019 id: connections
1020
1021 property int currentQueryId: -1
1022+ property QtObject contact: null
1023+ property string contactId: contact ? contact.contactId : ""
1024
1025 onContactsFetched: {
1026 // currentQueryId == -2 is used during a fetch using "memory" manager
1027@@ -86,8 +80,12 @@
1028 root.contactIsDirty = false
1029 root.running = false
1030 currentQueryId = -1
1031- root.contact = fetchedContacts[0]
1032- root.contactFetched(fetchedContacts[0])
1033+ if (fetchedContacts.length > 0) {
1034+ contact = fetchedContacts[0]
1035+ root.contactFetched(fetchedContacts[0])
1036+ } else {
1037+ contactNotFound()
1038+ }
1039 }
1040 }
1041 }
1042
1043=== modified file 'tests/autopilot/address_book_app/tests/test_edit_contact.py'
1044--- tests/autopilot/address_book_app/tests/test_edit_contact.py 2014-01-28 10:20:07 +0000
1045+++ tests/autopilot/address_book_app/tests/test_edit_contact.py 2014-03-19 18:42:11 +0000
1046@@ -79,7 +79,12 @@
1047
1048 def test_add_email(self):
1049 self.add_contact("Fulano", "")
1050- self.edit_contact(0)
1051+ edit_page = self.edit_contact(0)
1052+
1053+ emailGroup = edit_page.select_single(
1054+ "ContactDetailGroupWithTypeEditor",
1055+ objectName="emails")
1056+ self.create_new_detail(emailGroup)
1057
1058 # fill email address
1059 email_field = self.main_window.select_single(

Subscribers

People subscribed via source and target branches