-
Notifications
You must be signed in to change notification settings - Fork 173
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add the selected contacts to a group #2763
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -26,27 +26,65 @@ | |||||
<div class="search-contacts-field"> | ||||||
<input v-model="query" type="text" :placeholder="t('contacts', 'Search contacts …')"> | ||||||
</div> | ||||||
<Actions | ||||||
class="merge-button" | ||||||
menu-align="right"> | ||||||
<template v-if="selected.length >= 1"> | ||||||
<ActionButton v-if="showAddToGroup" | ||||||
icon="icon-clone" | ||||||
@click="OpenMultiselect"> | ||||||
{{ t('contacts', 'Add to group') }} | ||||||
</ActionButton> | ||||||
<Multiselect v-if="showSelectGroup" | ||||||
:options="groups" | ||||||
:taggable="true" | ||||||
@input="addSelectedContactsToGroup" | ||||||
@tag="addSelectedContactsToGroup" | ||||||
@close="closeMultiselect" /> | ||||||
</template> | ||||||
<ActionButton v-if="selected.length >= 2" | ||||||
:close-after-click="true" | ||||||
icon="icon-clone" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. material icon required |
||||||
@click="mergeContact"> | ||||||
{{ t('contacts', 'Merge') }} | ||||||
</ActionButton> | ||||||
<ActionButton v-if="selected.length >= 1" | ||||||
:close-after-click="true" | ||||||
icon="icon-delete" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. material icon required |
||||||
@click="deleteMultipleContact"> | ||||||
{{ t('contacts', 'Delete') }} | ||||||
</ActionButton> | ||||||
</Actions> | ||||||
</div> | ||||||
<VirtualList ref="scroller" | ||||||
class="contacts-list" | ||||||
data-key="key" | ||||||
:data-sources="filteredList" | ||||||
:data-component="ContactsListItem" | ||||||
:estimate-size="68" /> | ||||||
:estimate-size="68" | ||||||
:extra-props={selected} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missing quotes |
||||||
@update-check-selected="selectionChanged" /> | ||||||
</AppContentList> | ||||||
</template> | ||||||
|
||||||
<script> | ||||||
import AppContentList from '@nextcloud/vue/dist/Components/NcAppContentList' | ||||||
import ContactsListItem from './ContactsList/ContactsListItem' | ||||||
import VirtualList from 'vue-virtual-scroll-list' | ||||||
import Actions from '@nextcloud/vue/dist/Components/NcActions' | ||||||
import ActionButton from '@nextcloud/vue/dist/Components/NcActionButton' | ||||||
import naturalCompare from 'string-natural-compare' | ||||||
import Multiselect from '@nextcloud/vue/dist/Components/NcMultiselect' | ||||||
|
||||||
export default { | ||||||
name: 'ContactsList', | ||||||
|
||||||
components: { | ||||||
AppContentList, | ||||||
VirtualList, | ||||||
Actions, | ||||||
ActionButton, | ||||||
Multiselect | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missing trailing comma |
||||||
}, | ||||||
|
||||||
props: { | ||||||
|
@@ -68,10 +106,18 @@ export default { | |||||
return { | ||||||
ContactsListItem, | ||||||
query: '', | ||||||
selected: [], | ||||||
showGroups: false, | ||||||
showAddToGroup: true, | ||||||
showSelectGroup: false, | ||||||
} | ||||||
}, | ||||||
|
||||||
computed: { | ||||||
groups() { | ||||||
return this.$store.getters.getGroups.slice(0).map(group => group.name) | ||||||
.sort((a, b) => naturalCompare(a, b, { caseInsensitive: true })) | ||||||
}, | ||||||
selectedContact() { | ||||||
return this.$route.params.selectedContact | ||||||
}, | ||||||
|
@@ -157,6 +203,112 @@ export default { | |||||
} | ||||||
return true | ||||||
}, | ||||||
selectionChanged(newValue) { | ||||||
if (this.selected.includes(newValue)) { | ||||||
this.selected.splice(this.selected.indexOf(newValue), 1) | ||||||
} else { | ||||||
this.selected.push(newValue) | ||||||
} | ||||||
}, | ||||||
deleteMultipleContact() { | ||||||
const temp = [] | ||||||
this.selected.forEach(element => { | ||||||
if (this.contacts[element]) { | ||||||
// delete contact | ||||||
this.$store.dispatch('deleteContact', { contact: this.contacts[element] }) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dispatch is async. you have to await the result and handle any possible error. |
||||||
temp.push(this.selected.indexOf(element), 1) | ||||||
} | ||||||
}) | ||||||
// delete the uid in selected of the contact deleted | ||||||
temp.forEach(el => { | ||||||
this.selected.splice(temp, 1) | ||||||
}) | ||||||
}, | ||||||
cleanContactValue(contact, value) { | ||||||
contact.jCal[1].forEach(element => { | ||||||
if (element[0] === value[0] && element[3] === '' && !Array.isArray(element[3])) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. where are those magic numbers from? what is the 0th element, 0th value and 3rd element? |
||||||
contact.jCal[1].splice(contact.jCal[1].indexOf(element), 1) | ||||||
} else if (element[0] === value[0] && Array.isArray(element[3])) { | ||||||
let isempty = true | ||||||
value[3].forEach(arr => { | ||||||
if (arr !== '') { | ||||||
isempty = false | ||||||
} | ||||||
}) | ||||||
Comment on lines
+232
to
+237
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can be expressed using https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some |
||||||
if (isempty === false) { | ||||||
contact.jCal[1].splice(contact.jCal[1].indexOf(element), 1) | ||||||
} | ||||||
} | ||||||
}) | ||||||
}, | ||||||
addValue(contact, jcalvalue) { | ||||||
jcalvalue.filter(element => { | ||||||
// exclude the unique field we don't want to add | ||||||
return !['uid', 'version', 'fn', 'prodid', 'gender', 'rev'].includes(element[0]) | ||||||
}).forEach(value => { | ||||||
if (Array.isArray(value[3])) { | ||||||
let isempty = true | ||||||
value[3].forEach(arr => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. magic number? |
||||||
if (arr !== '') { | ||||||
isempty = false | ||||||
} | ||||||
}) | ||||||
Comment on lines
+250
to
+255
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use Array.some, again |
||||||
if (isempty === false) { | ||||||
// delete blank field of the same type and push the new field | ||||||
this.cleanContactValue(contact, value) | ||||||
contact.jCal[1].push(value) | ||||||
} | ||||||
} else if (value[3] !== '') { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. magic numer? |
||||||
let include = false | ||||||
contact.jCal[1].forEach(element => { | ||||||
if (element[0] === value[0] && element[3] === value[3]) { | ||||||
include = true | ||||||
} | ||||||
}) | ||||||
if (!include) { | ||||||
// delete blank field of the same type and push the new field | ||||||
this.cleanContactValue(contact, value) | ||||||
contact.jCal[1].push(value) | ||||||
} | ||||||
} | ||||||
}) | ||||||
return contact | ||||||
}, | ||||||
mergeContact() { | ||||||
const firstContact = this.contacts[this.selected[0]] | ||||||
this.selected.slice(1).forEach((element) => { | ||||||
if (this.contacts[element]) { | ||||||
const contactjcal = this.contacts[element].jCal[1] | ||||||
this.addValue(firstContact, contactjcal) | ||||||
// delete the contact merged and the uid in the selected | ||||||
this.$store.dispatch('deleteContact', { contact: this.contacts[element] }) && this.selected.splice(this.selected.indexOf(element), 1) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. await the action |
||||||
} | ||||||
}) | ||||||
this.$store.dispatch('updateContact', firstContact) | ||||||
}, | ||||||
OpenMultiselect() { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
camel case for methods |
||||||
this.showAddToGroup = false | ||||||
this.showSelectGroup = true | ||||||
}, | ||||||
closeMultiselect() { | ||||||
this.showAddToGroup = true | ||||||
this.showSelectGroup = false | ||||||
}, | ||||||
addSelectedContactsToGroup(value) { | ||||||
this.selected.forEach(element => { | ||||||
const selectedContact = this.contacts[element] | ||||||
const data = selectedContact.groups | ||||||
if (!data.includes(value)) { | ||||||
this.$store.dispatch('addContactToGroup', { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. await the action |
||||||
contact: selectedContact, | ||||||
groupName: value, | ||||||
}) | ||||||
data.push(value) | ||||||
selectedContact.groups = data | ||||||
this.$store.dispatch('updateContact', selectedContact) | ||||||
} | ||||||
}) | ||||||
}, | ||||||
}, | ||||||
} | ||||||
</script> | ||||||
|
@@ -187,4 +339,7 @@ export default { | |||||
padding: 0 4px; | ||||||
} | ||||||
|
||||||
.merge-button { | ||||||
float: right; | ||||||
} | ||||||
</style> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,42 @@ | ||
<template> | ||
<ListItem | ||
:id="id" | ||
:key="source.key" | ||
class="list-item-style envelope" | ||
:title="source.displayName" | ||
:to="{ name: 'contact', params: { selectedGroup: selectedGroup, selectedContact: source.key } }"> | ||
<!-- @slot Icon slot --> | ||
<div class="contacts-list__item" | ||
:class="{'contacts-list__item--active': selectedContact === source.key}" | ||
@mouseover="hover = true" | ||
@mouseleave="hover = false"> | ||
<ActionCheckbox v-if="hover || ischecked" | ||
:checked="ischecked" | ||
@check="isSelected" | ||
@uncheck="isUnselected" /> | ||
<BaseAvatar v-else-if="!hover && !ischecked" | ||
:display-name="source.displayName" | ||
:url="avatarUrl" | ||
:size="40" /> | ||
<ListItem | ||
:id="id" | ||
:key="source.key" | ||
class="list-item-style envelope" | ||
:title="source.displayName" | ||
:to="{ name: 'contact', params: { selectedGroup: selectedGroup, selectedContact: source.key } }"> | ||
<!-- @slot Icon slot --> | ||
|
||
<template #icon> | ||
<div class="app-content-list-item-icon"> | ||
<BaseAvatar :display-name="source.displayName" :url="avatarUrl" :size="40" /> | ||
</div> | ||
</template> | ||
<template #subtitle> | ||
<div class="envelope__subtitle"> | ||
<span class="envelope__subtitle__subject"> | ||
{{ source.email }} | ||
</span> | ||
</div> | ||
</template> | ||
</ListItem> | ||
<template #icon> | ||
<div class="app-content-list-item-icon"> | ||
<BaseAvatar :display-name="source.displayName" :url="avatarUrl" :size="40" /> | ||
</div> | ||
</template> | ||
<template #subtitle> | ||
<div class="envelope__subtitle"> | ||
<span class="envelope__subtitle__subject"> | ||
{{ source.email }} | ||
</span> | ||
</div> | ||
</template> | ||
</ListItem> | ||
</div> | ||
</template> | ||
|
||
<script> | ||
import ActionCheckbox from '@nextcloud/vue/dist/Components/NcActionCheckbox' | ||
import { NcListItem as ListItem } from '@nextcloud/vue' | ||
import BaseAvatar from '@nextcloud/vue/dist/Components/NcAvatar' | ||
|
||
|
@@ -32,6 +46,7 @@ export default { | |
components: { | ||
ListItem, | ||
BaseAvatar, | ||
ActionCheckbox, | ||
}, | ||
|
||
props: { | ||
|
@@ -43,10 +58,22 @@ export default { | |
type: Object, | ||
required: true, | ||
}, | ||
selected: { | ||
type: Array, | ||
required: false | ||
} | ||
}, | ||
data() { | ||
return { | ||
avatarUrl: undefined, | ||
hover: false, | ||
ischecked: false, | ||
} | ||
}, | ||
beforeMount() { | ||
// check the checkbox who is already selected | ||
if (this.selected.includes(this.source.key)) { | ||
this.ischecked = true | ||
} | ||
}, | ||
computed: { | ||
|
@@ -85,6 +112,26 @@ export default { | |
this.avatarUrl = `${this.source.url}?photo` | ||
} | ||
}, | ||
|
||
isSelected() { | ||
this.ischecked = true | ||
// update the selected | ||
this.$parent.$parent.$emit('update-check-selected', this.source.key) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please don't access the grandparent component this way |
||
}, | ||
|
||
isUnselected() { | ||
this.ischecked = false | ||
// update the selected | ||
this.$parent.$parent.$emit('update-check-selected', this.source.key) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. accessing the grandparent is a no go. this makes the component depend on the exact structure and is prone to break in unnoticed ways. Please find a better way to pass around the data. Could you use Vuex? |
||
}, | ||
|
||
/** | ||
* Select this contact within the list | ||
*/ | ||
selectContact() { | ||
// change url with router | ||
this.$router.push({ name: 'contact', params: { selectedGroup: this.selectedGroup, selectedContact: this.source.key } }) | ||
}, | ||
}, | ||
} | ||
</script> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
icon classes have been deprecated. please use a material icon.