Merge lp:~ubuntu-calendar-dev/ubuntu-calendar-app/5-Improved-EventDetails into lp:ubuntu-calendar-app

Proposed by Mihir Soni
Status: Merged
Approved by: Nekhelesh Ramananthan
Approved revision: 471
Merged at revision: 474
Proposed branch: lp:~ubuntu-calendar-dev/ubuntu-calendar-app/5-Improved-EventDetails
Merge into: lp:ubuntu-calendar-app
Diff against target: 652 lines (+195/-273)
4 files modified
EventDetails.qml (+175/-217)
EventDetailsInfo.qml (+0/-44)
NewEvent.qml (+15/-7)
tests/autopilot/calendar_app/__init__.py (+5/-5)
To merge this branch: bzr merge lp:~ubuntu-calendar-dev/ubuntu-calendar-app/5-Improved-EventDetails
Reviewer Review Type Date Requested Status
Nekhelesh Ramananthan Approve
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Review via email: mp+235333@code.launchpad.net

Commit message

Redesigned Event Details page.

Description of the change

Redesigned Event Details page.

Initial design idea , ahoneybun

1. Removed EventDetailsInfo.qml (As we are not using it)
2. Removed Google maps, as discussed on IRC (http://paste.ubuntu.com/8382143/)

To post a comment you must log in.
463. By Mihir Soni

uncommented po direcotry in CMake

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

At the moment we have a regression,

Edit an event which is part of a series like "Calendar App Meeting". Pressing "Edit Series" works as expected. However when you click "Edit this", the new event page does not hold the details of the event.

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

Resolved editing current occurence event issue

465. By Mihir Soni

merged

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

Merge from trunk

467. By Mihir Soni

Fixed margin issues

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

Fixed AP

469. By Mihir Soni

Merge from trunk

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

pep8 errors

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

Tested and works as expected. Nice work Mihir!

Just one minor quirk,

606 - text: i18n.tr("Repeats")
607 - subText: eventUtils.getRecurrenceString(rule)
608 + text: i18n.tr("This Happens")

Why this change? I thought we (popey) agreed to using "Repeats" rather than "This Happens".

Also why did you remove,

613 - ListItem.ThinDivider {}

review: Needs Fixing
471. By Nekhelesh Ramananthan

Fixed minor issue

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

I fixed the issues above. It looks good now.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'EventDetails.qml'
2--- EventDetails.qml 2014-09-21 09:39:21 +0000
3+++ EventDetails.qml 2014-09-24 11:32:08 +0000
4@@ -17,7 +17,7 @@
5 */
6 import QtQuick 2.3
7 import Ubuntu.Components 1.1
8-import Ubuntu.Components.ListItems 1.0
9+import Ubuntu.Components.ListItems 1.0 as ListItem
10 import Ubuntu.Components.Themes.Ambiance 1.0
11 import Ubuntu.Components.Popups 1.0
12 import QtOrganizer 5.0
13@@ -28,10 +28,8 @@
14 id: root
15 objectName: "eventDetails"
16
17- property var event;
18- property string headerColor :"black"
19- property string detailColor :"grey"
20- property var model;
21+ property var event
22+ property var model
23
24 anchors{
25 left: parent.left
26@@ -44,15 +42,14 @@
27 title: i18n.tr("Event Details")
28
29 Component.onCompleted: {
30-
31- showEvent(event);
32+ showEvent(event)
33 }
34
35 Connections{
36 target: pageStack
37 onCurrentPageChanged:{
38 if( pageStack.currentPage === root) {
39- showEvent(event);
40+ showEvent(event)
41 }
42 }
43 }
44@@ -64,15 +61,16 @@
45 function updateCollection(event) {
46 var collection = model.collection( event.collectionId );
47 calendarIndicator.color = collection.color
48- calendarName.text = collection.name
49+ eventInfo.color=collection.color
50+ calendarName.text = i18n.tr("%1 Calendar").arg( collection.name)
51 }
52
53 function updateRecurrence( event ) {
54 var index = 0;
55- if(event.recurrence) {
56+ if (event.recurrence) {
57 if(event.recurrence.recurrenceRules[0] !== undefined){
58 var rule = event.recurrence.recurrenceRules[0];
59- recurrentHeader.value = eventUtils.getRecurrenceString(rule)
60+ repeatLabel.text = eventUtils.getRecurrenceString(rule)
61 }
62 }
63 }
64@@ -81,7 +79,7 @@
65 var attendees = event.attendees;
66 contactModel.clear();
67 if( attendees !== undefined ) {
68- for( var j = 0 ; j < attendees.length ; ++j ) {
69+ for (var j = 0 ; j < attendees.length ; ++j) {
70 contactModel.append( {"name": attendees[j].name,"participationStatus": attendees[j].participationStatus } );
71 }
72 }
73@@ -92,43 +90,25 @@
74 if( reminder ) {
75 for(var i=0; i<reminderModel.count; i++) {
76 if(reminder.secondsBeforeStart === reminderModel.get(i).value)
77- reminderHeader.value = reminderModel.get(i).label
78+ reminderHeader.subText = reminderModel.get(i).label
79 }
80 } else {
81- reminderHeader.value = reminderModel.get(0).label
82+ reminderHeader.subText = reminderModel.get(0).label
83 }
84 }
85
86 function updateLocation(event) {
87 if( event.location ) {
88- locationLabel.text = event.location;
89-
90- // FIXME: need to cache map image to avoid duplicate download every time
91- var imageSrc = "http://maps.googleapis.com/maps/api/staticmap?center="+event.location+
92- "&markers=color:red|"+event.location+"&zoom=15&size="+mapContainer.width+
93- "x"+mapContainer.height+"&sensor=false";
94- mapImage.source = imageSrc;
95- }
96- else {
97- // TODO: use different color for empty text
98- locationLabel.text = i18n.tr("Not specified")
99- mapImage.source = "";
100+ locationLabel.text = i18n.tr("%1").arg(event.location)
101 }
102 }
103
104 function showEvent(e) {
105- // TRANSLATORS: this is a time formatting string,
106- // see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions
107- // It's used to display the start and end times of an event in the event details
108- // and new event views
109- var timeFormat = Qt.locale().timeFormat(Locale.ShortFormat);
110- // TRANSLATORS: this is a date formatting string,
111- // see http://qt-project.org/doc/qt-5/qml-qtqml-date.html#details for valid expressions
112- // It's used to display the date in the event details view
113- var dateFormat = i18n.tr("MMMM dd, yyyy")
114- eventDate.value = e.startDateTime.toLocaleString(Qt.locale(),dateFormat);
115- var startTime = e.startDateTime.toLocaleTimeString(Qt.locale(), timeFormat);
116- var endTime = e.endDateTime.toLocaleTimeString(Qt.locale(), timeFormat);
117+ var startTime = e.startDateTime.toLocaleTimeString(Qt.locale(), Locale.ShortFormat)
118+ var endTime = e.endDateTime.toLocaleTimeString(Qt.locale(), Locale.ShortFormat)
119+
120+ dateLabel.text = e.allDay === true ? i18n.tr("%1 (All Day)").arg( e.startDateTime.toLocaleDateString(Qt.locale(), Locale.LongFormat))
121+ : e.startDateTime.toLocaleDateString(Qt.locale(), Locale.LongFormat) + ", " +startTime + " - " + endTime;
122
123 if( e.itemType === Type.EventOccurrence ){
124 var requestId = -1;
125@@ -141,15 +121,6 @@
126 });
127 requestId = model.fetchItems([e.parentId]);
128 }
129-
130- allDayEventCheckbox.checked = e.allDay;
131-
132- startHeader.visible = !e.allDay;
133- endHeader.visible = !e.allDay;
134-
135- startHeader.value = startTime;
136- endHeader.value = endTime;
137-
138 // This is the event title
139 if( e.displayLabel) {
140 titleLabel.text = e.displayLabel;
141@@ -170,7 +141,6 @@
142 updateLocation(e);
143 }
144
145-
146 Keys.onEscapePressed: {
147 pageStack.pop();
148 }
149@@ -181,44 +151,41 @@
150 }
151 }
152
153- tools: ToolbarItems {
154- ToolbarButton {
155- action:Action {
156- text: i18n.tr("Delete");
157- objectName: "delete"
158- iconName: "delete"
159- onTriggered: {
160- var dialog = PopupUtils.open(Qt.resolvedUrl("DeleteConfirmationDialog.qml"),root,{"event": event});
161- dialog.deleteEvent.connect( function(eventId){
162- model.removeItem(eventId);
163- pageStack.pop();
164+ head.actions: [
165+ Action {
166+ text: i18n.tr("Delete");
167+ objectName: "delete"
168+ iconName: "delete"
169+ onTriggered: {
170+ var dialog = PopupUtils.open(Qt.resolvedUrl("DeleteConfirmationDialog.qml"),root,{"event": event});
171+ dialog.deleteEvent.connect( function(eventId){
172+ model.removeItem(eventId);
173+ pageStack.pop();
174+ });
175+ }
176+ },
177+
178+ Action {
179+ text: i18n.tr("Edit");
180+ objectName: "edit"
181+ iconName: "edit";
182+ onTriggered: {
183+ if( event.itemType === Type.EventOccurrence ) {
184+ var dialog = PopupUtils.open(Qt.resolvedUrl("EditEventConfirmationDialog.qml"),root,{"event": event});
185+ dialog.editEvent.connect( function(eventId){
186+ if( eventId === event.parentId ) {
187+ pageStack.push(Qt.resolvedUrl("NewEvent.qml"),{"event":internal.parentEvent,"model":model});
188+ } else {
189+ pageStack.push(Qt.resolvedUrl("NewEvent.qml"),{"event":event,"model":model});
190+ }
191 });
192+ } else {
193+ pageStack.push(Qt.resolvedUrl("NewEvent.qml"),{"event":event,"model":model});
194 }
195 }
196 }
197+ ]
198
199- ToolbarButton {
200- action:Action {
201- text: i18n.tr("Edit");
202- objectName: "edit"
203- iconName: "edit";
204- onTriggered: {
205- if( event.itemType === Type.EventOccurrence ) {
206- var dialog = PopupUtils.open(Qt.resolvedUrl("EditEventConfirmationDialog.qml"),root,{"event": event});
207- dialog.editEvent.connect( function(eventId){
208- if( eventId === event.parentId ) {
209- pageStack.push(Qt.resolvedUrl("NewEvent.qml"),{"event":internal.parentEvent,"model":model});
210- } else {
211- pageStack.push(Qt.resolvedUrl("NewEvent.qml"),{"event":event,"model":model});
212- }
213- });
214- } else {
215- pageStack.push(Qt.resolvedUrl("NewEvent.qml"),{"event":event,"model":model});
216- }
217- }
218- }
219- }
220- }
221 EventUtils{
222 id:eventUtils
223 }
224@@ -241,59 +208,81 @@
225
226 Flickable{
227 id: flicable
228- width: parent.width
229- height: parent.height
230- clip: true
231-
232- contentHeight: column.height + units.gu(3) /*top margin + spacing */
233+
234+ clip: interactive
235+ anchors.fill: parent
236+ interactive: contentHeight > height
237+
238 contentWidth: parent.width
239-
240- interactive: contentHeight > height
241+ contentHeight: column.height + eventInfo.height + units.gu(3) /*top margin + spacing */
242+
243+ Rectangle{
244+ id: eventInfo
245+
246+ width: parent.width
247+ height: eventInfoList.height + units.gu(5)
248+
249+ Column{
250+ id:eventInfoList
251+
252+ anchors {
253+ left: parent.left
254+ right: parent.right
255+ top: parent.top
256+ margins: units.gu(2)
257+ }
258+
259+ spacing: units.gu(0.5)
260+
261+ Label{
262+ id: titleLabel
263+ objectName: "titleLabel"
264+ fontSize: "x-large"
265+ width: parent.width
266+ wrapMode: Text.WordWrap
267+ color: "white"
268+ }
269+
270+ Label{
271+ id: dateLabel
272+ objectName: "dateLabel"
273+ color: "white"
274+ fontSize: "medium"
275+ width: parent.width
276+ wrapMode: Text.WordWrap
277+ }
278+
279+ Label{
280+ id: repeatLabel
281+ objectName: "repeatLabel"
282+ color: "white"
283+ fontSize: "small"
284+ width: parent.width
285+ wrapMode: Text.WordWrap
286+ visible: repeatLabel.text !== ""
287+ }
288+
289+ Label{
290+ id: locationLabel
291+ objectName: "locationLabel"
292+ color: "white"
293+ fontSize: "small"
294+ width: parent.width
295+ wrapMode: Text.WordWrap
296+ visible: locationLabel.text !== ""
297+ }
298+ }
299+ }
300
301 Column{
302 id: column
303+
304 spacing: units.gu(1)
305 anchors{
306- top:parent.top
307- topMargin: units.gu(2)
308+ top: eventInfo.bottom
309 right: parent.right
310- rightMargin: units.gu(2)
311 left:parent.left
312- leftMargin: units.gu(2)
313- }
314- property int timeLabelMaxLen: Math.max( startHeader.headerWidth, endHeader.headerWidth,eventDate.headerWidth)// Dynamic Width
315- EventDetailsInfo{
316- id: eventDate
317- xMargin:column.timeLabelMaxLen
318- header: i18n.tr("Date")
319- }
320- EventDetailsInfo{
321- id: startHeader
322- xMargin:column.timeLabelMaxLen
323- header: i18n.tr("Start")
324- }
325- EventDetailsInfo{
326- id: endHeader
327- xMargin: column.timeLabelMaxLen
328- header: i18n.tr("End")
329- }
330- Row {
331- width: parent.width
332- spacing: units.gu(1)
333- anchors.margins: units.gu(0.5)
334- visible: allDayEventCheckbox.checked
335-
336- Label {
337- text: i18n.tr("All day event")
338- anchors.verticalCenter: allDayEventCheckbox.verticalCenter
339- color: headerColor
340- }
341-
342- CheckBox {
343- id: allDayEventCheckbox
344- checked: false
345- enabled: false
346- }
347+ margins: units.gu(2)
348 }
349
350 Row{
351@@ -302,109 +291,78 @@
352 UbuntuShape{
353 id: calendarIndicator
354 width: parent.height
355- height: parent.height
356+ height: width
357 anchors.verticalCenter: parent.verticalCenter
358 }
359 Label{
360 id:calendarName
361 objectName: "calendarName"
362 anchors.verticalCenter: parent.verticalCenter
363- color: headerColor
364 }
365 }
366
367- ThinDivider{}
368- Label{
369- id: titleLabel
370- objectName: "titleLabel"
371- fontSize: "large"
372- width: parent.width
373- wrapMode: Text.WordWrap
374- color: headerColor
375- }
376 Label{
377 id: descLabel
378 objectName: "descriptionLabel"
379- wrapMode: Text.WordWrap
380- fontSize: "small"
381- width: parent.width
382- color: detailColor
383- }
384- ThinDivider{}
385- EventDetailsInfo{
386- id: mapHeader
387- header: i18n.tr("Location")
388- }
389- Label{
390- id: locationLabel
391- objectName: "locationLabel"
392- fontSize: "medium"
393- width: parent.width
394- wrapMode: Text.WordWrap
395- color: detailColor
396- }
397-
398- //map control with location
399- Rectangle{
400- id: mapContainer
401- width:parent.width
402- height: units.gu(10)
403- visible: mapImage.status == Image.Ready
404-
405- Image {
406- id: mapImage
407- anchors.fill: parent
408- opacity: 0.5
409- }
410- }
411- ThinDivider{}
412- Label{
413- text: i18n.tr("Guests");
414- fontSize: "medium"
415- color: headerColor
416- font.bold: true
417- }
418- //Guest Entery Model starts
419- Column{
420- id: contactList
421- objectName: 'contactList'
422- spacing: units.gu(1)
423- width: parent.width
424- clip: true
425- ListModel {
426- id: contactModel
427- }
428- Repeater{
429- model: contactModel
430- delegate: Row{
431- spacing: units.gu(1)
432- CheckBox{
433- checked: participationStatus
434- enabled: false
435- }
436- Label {
437- text:name
438- anchors.verticalCenter: parent.verticalCenter
439- color: detailColor
440- }
441- }
442- }
443- }
444-
445- //Guest Entries ends
446- ThinDivider{}
447- property int recurranceAreaMaxWidth: Math.max( recurrentHeader.headerWidth, reminderHeader.headerWidth) //Dynamic Height
448- EventDetailsInfo{
449- id: recurrentHeader
450- xMargin: column.recurranceAreaMaxWidth
451- header: i18n.tr("Repeats")
452- }
453- EventDetailsInfo{
454- id: reminderHeader
455- xMargin: column.recurranceAreaMaxWidth
456- header: i18n.tr("Reminder")
457- }
458-
459+ visible: text != ""
460+ width: parent.width
461+ wrapMode: Text.WordWrap
462+ }
463+
464+ Column {
465+ anchors{
466+ right: parent.right
467+ left:parent.left
468+ margins: units.gu(-2)
469+ }
470+
471+ ListItem.Header {
472+ text: i18n.tr("Guests")
473+ visible: contactModel.count !== 0
474+ }
475+
476+ //Guest Entery Model starts
477+ Column{
478+ id: contactList
479+ objectName: 'contactList'
480+
481+ anchors {
482+ left: parent.left
483+ right: parent.right
484+ }
485+
486+ ListModel {
487+ id: contactModel
488+ }
489+
490+ Repeater{
491+ model: contactModel
492+ delegate: ListItem.Standard {
493+ Label {
494+ text: name
495+ objectName: "eventGuest%1".arg(index)
496+ color: UbuntuColors.midAubergine
497+ anchors {
498+ left: parent.left
499+ leftMargin: units.gu(2)
500+ verticalCenter: parent.verticalCenter
501+ }
502+ }
503+
504+ control: CheckBox {
505+ enabled: false
506+ checked: participationStatus
507+ }
508+ }
509+ }
510+ }
511+ //Guest Entries ends
512+
513+ ListItem.Subtitled {
514+ id: reminderHeader
515+ text: i18n.tr("Reminder")
516+ }
517+ }
518 }
519 }
520 }
521
522=== removed file 'EventDetailsInfo.qml'
523--- EventDetailsInfo.qml 2014-09-20 10:45:35 +0000
524+++ EventDetailsInfo.qml 1970-01-01 00:00:00 +0000
525@@ -1,44 +0,0 @@
526-/*
527- * Copyright (C) 2013-2014 Canonical Ltd
528- *
529- * This file is part of Ubuntu Calendar App
530- *
531- * Ubuntu Calendar App is free software: you can redistribute it and/or modify
532- * it under the terms of the GNU General Public License version 3 as
533- * published by the Free Software Foundation.
534- *
535- * Ubuntu Calendar App is distributed in the hope that it will be useful,
536- * but WITHOUT ANY WARRANTY; without even the implied warranty of
537- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
538- * GNU General Public License for more details.
539- *
540- * You should have received a copy of the GNU General Public License
541- * along with this program. If not, see <http://www.gnu.org/licenses/>.
542- */
543-import QtQuick 2.3
544-import Ubuntu.Components 1.1
545-import Ubuntu.Components.Themes.Ambiance 1.0
546-Item
547-{
548- property alias header: header.text
549- property alias value: value.text
550- property string headerColor :"black"
551- property string detailColor :"grey"
552- property int xMargin
553- property int headerWidth: header.width
554- width: parent.width
555- height: header.height
556- Label{
557- id: header
558- color: headerColor
559- font.bold: true
560- fontSize: "medium"
561- anchors.left: parent.left
562- }
563- Label{
564- id:value
565- x: xMargin + units.gu(1)
566- color: detailColor
567- fontSize: "medium"
568- }
569-}
570
571=== modified file 'NewEvent.qml'
572--- NewEvent.qml 2014-09-22 06:51:06 +0000
573+++ NewEvent.qml 2014-09-24 11:32:08 +0000
574@@ -114,10 +114,11 @@
575 //Editing Event
576 function editEvent(e) {
577 //If there is a ReccruenceRule use that , else create fresh Recurrence Object.
578- rule = (e.recurrence.recurrenceRules[0] === undefined || e.recurrence.recurrenceRules[0] === null) ?
579- Qt.createQmlObject("import QtOrganizer 5.0; RecurrenceRule {}", event.recurrence,"EventRepetition.qml")
580- : e.recurrence.recurrenceRules[0];
581-
582+ if(e.itemType === Type.Event){
583+ rule = (e.recurrence.recurrenceRules[0] === undefined || e.recurrence.recurrenceRules[0] === null) ?
584+ Qt.createQmlObject("import QtOrganizer 5.0; RecurrenceRule {}", event.recurrence,"EventRepetition.qml")
585+ : e.recurrence.recurrenceRules[0];
586+ }
587 startDate =new Date(e.startDateTime);
588 endDate = new Date(e.endDateTime);
589
590@@ -587,7 +588,10 @@
591 }
592 }
593
594- ListItem.ThinDivider {}
595+ ListItem.ThinDivider {
596+ visible: event.itemType === Type.Event
597+ }
598+
599 }
600
601 ListItem.Subtitled{
602@@ -603,11 +607,13 @@
603 progression: true
604 visible: event.itemType === Type.Event
605 text: i18n.tr("Repeats")
606- subText: eventUtils.getRecurrenceString(rule)
607+ subText: event.itemType === Type.Event ? eventUtils.getRecurrenceString(rule) : ""
608 onClicked: pageStack.push(Qt.resolvedUrl("EventRepetition.qml"),{"rule": rule,"date":date,"isEdit":isEdit});
609 }
610
611- ListItem.ThinDivider {}
612+ ListItem.ThinDivider {
613+ visible: event.itemType === Type.Event
614+ }
615
616 ListItem.Subtitled{
617 id:eventReminder
618@@ -638,6 +644,8 @@
619 "reminderModel": reminderModel,
620 "eventTitle": titleEdit.text})
621 }
622+
623+ ListItem.ThinDivider {}
624 }
625 }
626 // used to keep the field visible when the keyboard appear or dismiss
627
628=== modified file 'tests/autopilot/calendar_app/__init__.py'
629--- tests/autopilot/calendar_app/__init__.py 2014-09-19 12:26:42 +0000
630+++ tests/autopilot/calendar_app/__init__.py 2014-09-24 11:32:08 +0000
631@@ -682,7 +682,7 @@
632 return data.Event(calendar, name, description, location, guests)
633
634 def _get_calendar(self):
635- return self._get_label_text('calendarName')
636+ return self._get_label_text('calendarName').split(" ")[0]
637
638 def _get_name(self):
639 return self._get_label_text('titleLabel')
640@@ -700,10 +700,10 @@
641 guests = []
642 contacts_list = self.select_single(
643 'QQuickColumn', objectName='contactList')
644- guest_labels = contacts_list.select_many('Label')
645- for label in guest_labels:
646- guests.append(label.text)
647-
648+ guests.append(
649+ contacts_list.select_single(
650+ "Label",
651+ objectName='eventGuest0').text)
652 return guests
653
654

Subscribers

People subscribed via source and target branches

to status/vote changes: