+
+
+
-
-
-
-
+ {{ n('calendar', '%n more guest', '%n more guests', inviteesWithoutOrganizer.length - limit) }}
+
+
-
+
+
-
@@ -85,6 +100,7 @@ import {
showError,
} from '@nextcloud/dialogs'
import { organizerDisplayName, removeMailtoPrefix } from '../../../utils/attendee.js'
+import AccountMultipleIcon from 'vue-material-design-icons/AccountMultiple.vue'
export default {
name: 'InviteesList',
@@ -96,6 +112,7 @@ export default {
InviteesListItem,
InviteesListSearch,
OrganizerListItem,
+ AccountMultipleIcon,
},
props: {
isReadOnly: {
@@ -110,11 +127,32 @@ export default {
type: Boolean,
required: true,
},
+ showHeader: {
+ type: Boolean,
+ required: true,
+ },
+ hideIfEmpty: {
+ type: Boolean,
+ default: false,
+ },
+ hideButtons: {
+ type: Boolean,
+ default: false,
+ },
+ hideErrors: {
+ type: Boolean,
+ default: false,
+ },
+ limit: {
+ type: Number,
+ default: 0,
+ },
},
data() {
return {
creatingTalkRoom: false,
showFreeBusyModel: false,
+ recentAttendees: [],
}
},
computed: {
@@ -133,10 +171,23 @@ export default {
})
},
groups() {
- return this.calendarObjectInstance.attendees.filter(attendee => {
- return attendee.attendeeProperty.userType === 'GROUP'
+ return this.invitees.filter(attendee => {
+ if (attendee.attendeeProperty.userType === 'GROUP') {
+ attendee.members = this.invitees.filter(invitee => {
+ return invitee.attendeeProperty.member
+ && invitee.attendeeProperty.member.includes(attendee.uri)
+ && attendee.attendeeProperty.userType === 'GROUP'
+ })
+ return attendee.members.length > 0
+ }
+ return false
})
},
+ /**
+ * All invitees except the organizer.
+ *
+ * @return {object[]}
+ */
inviteesWithoutOrganizer() {
if (!this.calendarObjectInstance.organizer) {
@@ -146,38 +197,41 @@ export default {
return this.invitees
.filter(attendee => {
// Filter attendees which are part of an invited group
- let isMemberOfGroup = false
- if (attendee.attendeeProperty.member) {
- isMemberOfGroup = this.groups.some(function(group) {
- return attendee.attendeeProperty.member.includes(group.uri)
- && attendee.attendeeProperty.userType === 'INDIVIDUAL'
- })
+ if (this.groups.some(function(group) {
+ return attendee.attendeeProperty.member
+ && attendee.attendeeProperty.member.includes(group.uri)
+ && attendee.attendeeProperty.userType === 'INDIVIDUAL'
+ })) {
+ return false
}
- // Add attendee to group member list
- if (isMemberOfGroup) {
- this.groups.forEach(group => {
- if (attendee.member.includes(group.uri)) {
- if (!group.members) {
- group.members = []
- }
- group.members.push(attendee)
- }
- })
+ // Filter empty groups
+ if (attendee.attendeeProperty.userType === 'GROUP') {
+ return attendee.members.length > 0
}
- // Check if attendee is an empty group
- let isEmptyGroup = attendee.attendeeProperty.userType === 'GROUP'
- this.invitees.forEach(invitee => {
- if (invitee.member && invitee.member.includes(attendee.uri)) {
- isEmptyGroup = false
- }
- })
-
return attendee.uri !== this.calendarObjectInstance.organizer.uri
- && !isMemberOfGroup && !isEmptyGroup
})
},
+ /**
+ * All invitees except the organizer limited by the limit prop.
+ * If the limit prop is 0 all invitees except the organizer are returned.
+ *
+ * @return {object[]}
+ */
+ limitedInviteesWithoutOrganizer() {
+ const filteredInvitees = this.inviteesWithoutOrganizer
+
+ if (this.limit) {
+ const limit = this.hasOrganizer ? this.limit - 1 : this.limit
+ return filteredInvitees
+ // Push newly added attendees to the top of the list
+ .toSorted((a, b) => this.recentAttendees.indexOf(b.uri) - this.recentAttendees.indexOf(a.uri))
+ .slice(0, limit)
+ }
+
+ return filteredInvitees
+ },
isOrganizer() {
return this.calendarObjectInstance.organizer !== null
&& this.$store.getters.getCurrentUserPrincipal !== null
@@ -228,9 +282,21 @@ export default {
return false
},
+ statusHeader() {
+ if (!this.isReadOnly) {
+ return ''
+ }
+
+ return this.t('calendar', '{invitedCount} invited, {confirmedCount} confirmed', {
+ invitedCount: this.inviteesWithoutOrganizer.length,
+ confirmedCount: this.inviteesWithoutOrganizer
+ .filter((attendee) => attendee.participationStatus === 'ACCEPTED')
+ .length,
+ })
+ },
},
methods: {
- addAttendee({ commonName, email, calendarUserType, language, timezoneId }) {
+ addAttendee({ commonName, email, calendarUserType, language, timezoneId, member }) {
this.$store.commit('addAttendee', {
calendarObjectInstance: this.calendarObjectInstance,
commonName,
@@ -242,7 +308,9 @@ export default {
language,
timezoneId,
organizer: this.$store.getters.getCurrentUserPrincipal,
+ member,
})
+ this.recentAttendees.push(email)
},
removeAttendee(attendee) {
// Remove attendee from participating group
@@ -262,6 +330,7 @@ export default {
calendarObjectInstance: this.calendarObjectInstance,
attendee,
})
+ this.recentAttendees = this.recentAttendees.filter((a) => a.uri !== attendee.email)
},
openFreeBusy() {
this.showFreeBusyModel = true
@@ -269,6 +338,10 @@ export default {
closeFreeBusy() {
this.showFreeBusyModel = false
},
+ saveNewDate(dates) {
+ this.$emit('update-dates', dates)
+ this.showFreeBusyModel = false
+ },
async createTalkRoom() {
const NEW_LINE = '\r\n'
try {
@@ -310,19 +383,35 @@ export default {
diff --git a/src/components/Editor/Invitees/InviteesListSearch.vue b/src/components/Editor/Invitees/InviteesListSearch.vue
index 468f998536791ccf2e202e8cf286144c4c1b21d3..7ee7929d1d6a962826ad49e68ae39e822d48434e 100644
--- a/src/components/Editor/Invitees/InviteesListSearch.vue
+++ b/src/components/Editor/Invitees/InviteesListSearch.vue
@@ -1,8 +1,10 @@
-
-
+ @search="findAttendees"
+ @option:selected="addAttendee">
+
-
+
+
+
+
+
+ :display-name="option.commonName" />
- {{ option.dropdownName }}
+ {{ option.commonName }}
-
+
{{ option.email }}
+
+ {{ option.subtitle }}
+
-
+
diff --git a/src/components/Editor/Properties/PropertyText.vue b/src/components/Editor/Properties/PropertyText.vue
index 82e93ec6be1a43e07e473b882d9c1705ce926a2d..5779463f90ad3a8112ba498f191e4ba29df9d556 100644
--- a/src/components/Editor/Properties/PropertyText.vue
+++ b/src/components/Editor/Properties/PropertyText.vue
@@ -22,10 +22,12 @@
-->
-
+
@@ -35,7 +37,7 @@
v-autosize="true"
:placeholder="placeholder"
:rows="rows"
- :title="readableName"
+ :name="readableName"
:value="value"
@focus="handleToggleTextareaFocus(true)"
@blur="handleToggleTextareaFocus(false)"
diff --git a/src/components/Editor/Properties/PropertyTitle.vue b/src/components/Editor/Properties/PropertyTitle.vue
index 860fb96c9a6a542fa39e914243089ff82d2f0a16..c74733470f2dd41ca7a4911ce3f5bbb64c7304ac 100644
--- a/src/components/Editor/Properties/PropertyTitle.vue
+++ b/src/components/Editor/Properties/PropertyTitle.vue
@@ -21,7 +21,7 @@
-->
-