Merge pull request #1322 from nextcloud/split-sidebar-content
Split sidebar content
This commit is contained in:
Коммит
6a60a1ec78
|
@ -0,0 +1,123 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2018 René Gieling <github@dartcafe.de>
|
||||
-
|
||||
- @author René Gieling <github@dartcafe.de>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
|
||||
<template>
|
||||
<Multiselect id="ajax"
|
||||
:options="users"
|
||||
:multiple="false"
|
||||
:user-select="true"
|
||||
:tag-width="80"
|
||||
:clear-on-select="false"
|
||||
:preserve-search="true"
|
||||
:options-limit="30"
|
||||
:loading="isLoading"
|
||||
:internal-search="false"
|
||||
:searchable="true"
|
||||
:preselect-first="true"
|
||||
:placeholder="placeholder"
|
||||
label="displayName"
|
||||
track-by="userId"
|
||||
@select="addShare"
|
||||
@search-change="loadUsersAsync">
|
||||
<template slot="selection" slot-scope="{ values, isOpen }">
|
||||
<span v-if="values.length && !isOpen" class="multiselect__single">
|
||||
{{ values.length }} users selected
|
||||
</span>
|
||||
</template>
|
||||
</Multiselect>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import debounce from 'lodash/debounce'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { Multiselect } from '@nextcloud/vue'
|
||||
|
||||
export default {
|
||||
name: 'UserSearch',
|
||||
|
||||
components: {
|
||||
Multiselect,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
searchToken: null,
|
||||
users: [],
|
||||
isLoading: false,
|
||||
placeholder: t('polls', 'Enter a name to start the search'),
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
},
|
||||
|
||||
methods: {
|
||||
loadUsersAsync: debounce(function(query) {
|
||||
if (!query) {
|
||||
this.users = []
|
||||
return
|
||||
}
|
||||
this.isLoading = true
|
||||
if (this.searchToken) {
|
||||
this.searchToken.cancel()
|
||||
}
|
||||
this.searchToken = axios.CancelToken.source()
|
||||
axios.get(generateUrl('apps/polls/search/users/' + query), { cancelToken: this.searchToken.token })
|
||||
.then((response) => {
|
||||
this.users = response.data.siteusers
|
||||
this.isLoading = false
|
||||
})
|
||||
.catch((error) => {
|
||||
if (axios.isCancel(error)) {
|
||||
// request was cancelled
|
||||
} else {
|
||||
console.error(error.response)
|
||||
this.isLoading = false
|
||||
}
|
||||
})
|
||||
}, 250),
|
||||
|
||||
addShare(payload) {
|
||||
this.$store
|
||||
.dispatch('poll/shares/add', {
|
||||
share: payload,
|
||||
type: payload.type,
|
||||
id: payload.id,
|
||||
emailAddress: payload.emailAddress,
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error while adding share - Error: ', error)
|
||||
showError(t('polls', 'Error while adding share'))
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.multiselect {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
</style>
|
|
@ -21,53 +21,40 @@
|
|||
-->
|
||||
|
||||
<template>
|
||||
<div class="comments">
|
||||
<CommentAdd v-if="acl.allowComment" />
|
||||
<transition-group v-if="!showEmptyContent" name="fade" class="comments"
|
||||
tag="ul">
|
||||
<li v-for="(comment) in sortedList" :key="comment.id">
|
||||
<div class="comment-item">
|
||||
<UserItem v-bind="comment" />
|
||||
<Actions v-if="comment.userId === acl.userId">
|
||||
<ActionButton icon="icon-delete" @click="deleteComment(comment)">
|
||||
{{ t('polls', 'Delete comment') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
<div class="date">
|
||||
{{ dateCommentedRelative(comment.dt) }}
|
||||
</div>
|
||||
<transition-group name="fade" class="comments"
|
||||
tag="ul">
|
||||
<li v-for="(comment) in sortedList" :key="comment.id">
|
||||
<div class="comment-item">
|
||||
<UserItem v-bind="comment" />
|
||||
<Actions v-if="comment.userId === acl.userId">
|
||||
<ActionButton icon="icon-delete" @click="deleteComment(comment)">
|
||||
{{ t('polls', 'Delete comment') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
<div class="date">
|
||||
{{ dateCommentedRelative(comment.dt) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message wordwrap comment-content">
|
||||
{{ comment.comment }}
|
||||
</div>
|
||||
</li>
|
||||
</transition-group>
|
||||
|
||||
<EmptyContent v-else icon="icon-comment">
|
||||
{{ t('polls', 'No comments') }}
|
||||
<template #desc>
|
||||
{{ t('polls', 'Be the first.') }}
|
||||
</template>
|
||||
</EmptyContent>
|
||||
</div>
|
||||
<div class="message wordwrap comment-content">
|
||||
{{ comment.comment }}
|
||||
</div>
|
||||
</li>
|
||||
</transition-group>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CommentAdd from './CommentAdd'
|
||||
import sortBy from 'lodash/sortBy'
|
||||
import moment from '@nextcloud/moment'
|
||||
import { showSuccess, showError } from '@nextcloud/dialogs'
|
||||
import { Actions, ActionButton, EmptyContent } from '@nextcloud/vue'
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import { Actions, ActionButton } from '@nextcloud/vue'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'Comments',
|
||||
components: {
|
||||
Actions,
|
||||
ActionButton,
|
||||
CommentAdd,
|
||||
EmptyContent,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -82,14 +69,6 @@ export default {
|
|||
acl: state => state.poll.acl,
|
||||
}),
|
||||
|
||||
...mapGetters({
|
||||
countComments: 'poll/comments/count',
|
||||
}),
|
||||
|
||||
showEmptyContent() {
|
||||
return this.countComments === 0
|
||||
},
|
||||
|
||||
sortedList() {
|
||||
if (this.reverse) {
|
||||
return sortBy(this.comments, this.sort).reverse()
|
||||
|
|
|
@ -22,10 +22,7 @@
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<OptionAddDate v-if="!closed" />
|
||||
<OptionShiftDates v-if="options.length && !closed" />
|
||||
|
||||
<ConfigBox v-if="!showEmptyContent" :title="t('polls', 'Available Options')" icon-class="icon-calendar-000">
|
||||
<ConfigBox v-if="options.length" :title="t('polls', 'Available Options')" icon-class="icon-calendar-000">
|
||||
<transition-group is="ul">
|
||||
<OptionItem v-for="(option) in sortedOptions"
|
||||
:key="option.id"
|
||||
|
@ -41,10 +38,10 @@
|
|||
</Actions>
|
||||
|
||||
<Actions v-if="acl.allowEdit" class="action">
|
||||
<ActionButton v-if="!closed" icon="icon-polls-clone" @click="cloneOptionModal(option)">
|
||||
<ActionButton v-if="!pollIsClosed" icon="icon-polls-clone" @click="cloneOptionModal(option)">
|
||||
{{ t('polls', 'Clone option') }}
|
||||
</ActionButton>
|
||||
<ActionButton v-if="closed" :icon="option.confirmed ? 'icon-polls-confirmed' : 'icon-polls-unconfirmed'"
|
||||
<ActionButton v-if="pollIsClosed" :icon="option.confirmed ? 'icon-polls-confirmed' : 'icon-polls-unconfirmed'"
|
||||
@click="confirmOption(option)">
|
||||
{{ option.confirmed ? t('polls', 'Unconfirm option') : t('polls', 'Confirm option') }}
|
||||
</ActionButton>
|
||||
|
@ -69,27 +66,23 @@
|
|||
|
||||
<script>
|
||||
import { mapGetters, mapState } from 'vuex'
|
||||
import OptionAddDate from '../Base/OptionAddDate'
|
||||
import OptionCloneDate from '../Base/OptionCloneDate'
|
||||
import OptionShiftDates from '../Base/OptionShiftDates'
|
||||
import OptionCloneDate from './OptionCloneDate'
|
||||
import ConfigBox from '../Base/ConfigBox'
|
||||
import OptionItem from '../Base/OptionItem'
|
||||
import OptionItem from './OptionItem'
|
||||
import moment from '@nextcloud/moment'
|
||||
import { Actions, ActionButton, Modal, EmptyContent } from '@nextcloud/vue'
|
||||
import { confirmOption, removeOption } from '../../mixins/optionMixins'
|
||||
import { dateUnits } from '../../mixins/dateMixins'
|
||||
|
||||
export default {
|
||||
name: 'SideBarTabOptionsDate',
|
||||
name: 'OptionsDate',
|
||||
|
||||
components: {
|
||||
Actions,
|
||||
ActionButton,
|
||||
ConfigBox,
|
||||
EmptyContent,
|
||||
OptionAddDate,
|
||||
OptionCloneDate,
|
||||
OptionShiftDates,
|
||||
Modal,
|
||||
OptionItem,
|
||||
},
|
||||
|
@ -121,13 +114,9 @@ export default {
|
|||
|
||||
...mapGetters({
|
||||
sortedOptions: 'poll/options/sorted',
|
||||
closed: 'poll/closed',
|
||||
pollIsClosed: 'poll/closed',
|
||||
}),
|
||||
|
||||
showEmptyContent() {
|
||||
return this.sortedOptions.length === 0
|
||||
},
|
||||
|
||||
dateBaseOptionString() {
|
||||
return moment.unix(this.sequence.baseOption.timestamp).format('LLLL')
|
||||
},
|
|
@ -37,7 +37,7 @@ import moment from '@nextcloud/moment'
|
|||
import { DatetimePicker } from '@nextcloud/vue'
|
||||
|
||||
export default {
|
||||
name: 'OptionAddDate',
|
||||
name: 'OptionsDateAdd',
|
||||
|
||||
components: {
|
||||
ConfigBox,
|
|
@ -56,7 +56,7 @@ import { Actions, ActionButton, Multiselect } from '@nextcloud/vue'
|
|||
import { dateUnits } from '../../mixins/dateMixins'
|
||||
|
||||
export default {
|
||||
name: 'OptionAddDate',
|
||||
name: 'OptionsDateShift',
|
||||
|
||||
components: {
|
||||
Actions,
|
|
@ -22,12 +22,7 @@
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<ConfigBox v-if="!closed" :title="t('polls', 'Add a new text option')" icon-class="icon-add">
|
||||
<InputDiv v-model="newPollText" :placeholder="t('polls', 'Enter option text')"
|
||||
@input="addOption()" />
|
||||
</ConfigBox>
|
||||
|
||||
<ConfigBox v-if="!showEmptyContent" :title="t('polls', 'Available Options')" icon-class="icon-toggle-filelist">
|
||||
<ConfigBox v-if="options.length" :title="t('polls', 'Available Options')" icon-class="icon-toggle-filelist">
|
||||
<draggable v-model="sortOptions">
|
||||
<transition-group>
|
||||
<OptionItem v-for="(option) in sortOptions"
|
||||
|
@ -41,7 +36,7 @@
|
|||
</ActionButton>
|
||||
</Actions>
|
||||
<Actions v-if="acl.allowEdit" class="action">
|
||||
<ActionButton v-if="closed" :icon="option.confirmed ? 'icon-polls-yes' : 'icon-checkmark'"
|
||||
<ActionButton v-if="PollIsClosed" :icon="option.confirmed ? 'icon-polls-yes' : 'icon-checkmark'"
|
||||
@click="confirmOption(option)">
|
||||
{{ option.confirmed ? t('polls', 'Unconfirm option') : t('polls', 'Confirm option') }}
|
||||
</ActionButton>
|
||||
|
@ -66,8 +61,7 @@ import { mapGetters, mapState } from 'vuex'
|
|||
import { Actions, ActionButton, EmptyContent } from '@nextcloud/vue'
|
||||
import ConfigBox from '../Base/ConfigBox'
|
||||
import draggable from 'vuedraggable'
|
||||
import OptionItem from '../Base/OptionItem'
|
||||
import InputDiv from '../Base/InputDiv'
|
||||
import OptionItem from './OptionItem'
|
||||
import { confirmOption, removeOption } from '../../mixins/optionMixins'
|
||||
|
||||
export default {
|
||||
|
@ -79,7 +73,6 @@ export default {
|
|||
ConfigBox,
|
||||
draggable,
|
||||
EmptyContent,
|
||||
InputDiv,
|
||||
OptionItem,
|
||||
},
|
||||
|
||||
|
@ -102,13 +95,9 @@ export default {
|
|||
|
||||
...mapGetters({
|
||||
sortedOptions: 'poll/options/sorted',
|
||||
closed: 'poll/closed',
|
||||
PollIsClosed: 'poll/closed',
|
||||
}),
|
||||
|
||||
showEmptyContent() {
|
||||
return this.sortedOptions.length === 0
|
||||
},
|
||||
|
||||
sortOptions: {
|
||||
get() {
|
||||
return this.sortedOptions
|
|
@ -0,0 +1,92 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2018 René Gieling <github@dartcafe.de>
|
||||
-
|
||||
- @author René Gieling <github@dartcafe.de>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
|
||||
<template>
|
||||
<ConfigBox v-if="!closed" :title="t('polls', 'Add a new text option')" icon-class="icon-add">
|
||||
<InputDiv v-model="newPollText" :placeholder="t('polls', 'Enter option text')"
|
||||
@input="addOption()" />
|
||||
</ConfigBox>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import ConfigBox from '../Base/ConfigBox'
|
||||
import InputDiv from '../Base/InputDiv'
|
||||
|
||||
export default {
|
||||
name: 'SideBarTabOptionsText',
|
||||
|
||||
components: {
|
||||
ConfigBox,
|
||||
InputDiv,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
newPollText: '',
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters({
|
||||
closed: 'poll/closed',
|
||||
}),
|
||||
|
||||
},
|
||||
|
||||
methods: {
|
||||
addOption() {
|
||||
if (this.newPollText) {
|
||||
this.$store.dispatch('poll/options/add', {
|
||||
pollOptionText: this.newPollText,
|
||||
})
|
||||
.then(() => {
|
||||
this.newPollText = ''
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.optionAdd {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.newOption {
|
||||
margin-left: 40px;
|
||||
flex: 1;
|
||||
&:empty:before {
|
||||
color: grey;
|
||||
}
|
||||
}
|
||||
|
||||
.submit-option {
|
||||
width: 30px;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
opacity: 0.3;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -0,0 +1,143 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2018 René Gieling <github@dartcafe.de>
|
||||
-
|
||||
- @author René Gieling <github@dartcafe.de>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
|
||||
<template>
|
||||
<ConfigBox :title="t('polls', 'Effective shares')" icon-class="icon-share">
|
||||
<TransitionGroup :css="false" tag="div" class="shared-list">
|
||||
<UserItem v-for="(share) in invitationShares"
|
||||
:key="share.id" v-bind="share"
|
||||
:icon="true">
|
||||
<Actions>
|
||||
<ActionButton
|
||||
v-if="share.emailAddress || share.type === 'group'"
|
||||
icon="icon-confirm"
|
||||
@click="sendInvitation(share)">
|
||||
{{ share.invitationSent ? t('polls', 'Resend invitation mail') : t('polls', 'Send invitation mail') }}
|
||||
</ActionButton>
|
||||
<ActionButton icon="icon-clippy" @click="copyLink( { url: shareUrl(share) })">
|
||||
{{ t('polls', 'Copy link to clipboard') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
<Actions>
|
||||
<ActionButton icon="icon-delete" @click="removeShare(share)">
|
||||
{{ t('polls', 'Remove share') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
</UserItem>
|
||||
</TransitionGroup>
|
||||
</ConfigBox>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import { showSuccess, showError } from '@nextcloud/dialogs'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { Actions, ActionButton } from '@nextcloud/vue'
|
||||
import ConfigBox from '../Base/ConfigBox'
|
||||
|
||||
export default {
|
||||
name: 'SharesEffective',
|
||||
|
||||
components: {
|
||||
Actions,
|
||||
ActionButton,
|
||||
ConfigBox,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
users: [],
|
||||
isLoading: false,
|
||||
placeholder: t('polls', 'Enter a name to start the search'),
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters({
|
||||
invitationShares: 'poll/shares/invitation',
|
||||
}),
|
||||
},
|
||||
|
||||
methods: {
|
||||
sendInvitation(share) {
|
||||
this.$store.dispatch('poll/shares/sendInvitation', { share: share })
|
||||
.then((response) => {
|
||||
if ('sentResult.sentMails' in response.data) {
|
||||
response.data.sentResult.sentMails.forEach((item) => {
|
||||
showSuccess(t('polls', 'Invitation sent to {name}', { name: item.displayName }))
|
||||
})
|
||||
}
|
||||
if ('sentResult.abortedMails' in response.data) {
|
||||
response.data.sentResult.abortedMails.forEach((item) => {
|
||||
console.error('Mail could not be sent!', { recipient: item })
|
||||
showError(t('polls', 'Error sending invitation to {name}', { name: item.dispalyName }))
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
copyLink(payload) {
|
||||
this
|
||||
.$copyText(window.location.origin + payload.url)
|
||||
.then(() => {
|
||||
showSuccess(t('polls', 'Link copied to clipboard'))
|
||||
})
|
||||
.catch(() => {
|
||||
showError(t('polls', 'Error while copying link to clipboard'))
|
||||
})
|
||||
},
|
||||
|
||||
shareUrl(share) {
|
||||
return generateUrl('apps/polls/s/') + share.token
|
||||
},
|
||||
|
||||
removeShare(share) {
|
||||
this.$store.dispatch('poll/shares/delete', { share: share })
|
||||
},
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.shared-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
padding-top: 8px;
|
||||
|
||||
> li {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
margin: 4px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.share-item {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -0,0 +1,130 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2018 René Gieling <github@dartcafe.de>
|
||||
-
|
||||
- @author René Gieling <github@dartcafe.de>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
|
||||
<template>
|
||||
<ConfigBox :title="t('polls', 'Public shares')" icon-class="icon-public">
|
||||
<TransitionGroup :css="false" tag="div" class="shared-list">
|
||||
<PublicShareItem v-for="(share) in publicShares"
|
||||
:key="share.id"
|
||||
v-bind="share">
|
||||
<Actions>
|
||||
<ActionButton icon="icon-clippy" @click="copyLink( { url: shareUrl(share) })">
|
||||
{{ t('polls', 'Copy link to clipboard') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
<Actions>
|
||||
<ActionButton icon="icon-delete" @click="removeShare(share)">
|
||||
{{ t('polls', 'Remove share') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
</PublicShareItem>
|
||||
</TransitionGroup>
|
||||
|
||||
<ButtonDiv :title="t('polls', 'Add a public link')" icon="icon-add" @click="addShare({type: 'public', userId: '', emailAddress: ''})" />
|
||||
</ConfigBox>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import { showSuccess, showError } from '@nextcloud/dialogs'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { Actions, ActionButton } from '@nextcloud/vue'
|
||||
import ButtonDiv from '../Base/ButtonDiv'
|
||||
import ConfigBox from '../Base/ConfigBox'
|
||||
import PublicShareItem from './PublicShareItem'
|
||||
|
||||
export default {
|
||||
name: 'SharesPublic',
|
||||
|
||||
components: {
|
||||
Actions,
|
||||
ActionButton,
|
||||
ButtonDiv,
|
||||
ConfigBox,
|
||||
PublicShareItem,
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters({
|
||||
publicShares: 'poll/shares/public',
|
||||
}),
|
||||
},
|
||||
|
||||
methods: {
|
||||
copyLink(payload) {
|
||||
this
|
||||
.$copyText(window.location.origin + payload.url)
|
||||
.then(() => {
|
||||
showSuccess(t('polls', 'Link copied to clipboard'))
|
||||
})
|
||||
.catch(() => {
|
||||
showError(t('polls', 'Error while copying link to clipboard'))
|
||||
})
|
||||
},
|
||||
|
||||
shareUrl(share) {
|
||||
return generateUrl('apps/polls/s/') + share.token
|
||||
},
|
||||
|
||||
removeShare(share) {
|
||||
this.$store.dispatch('poll/shares/delete', { share: share })
|
||||
},
|
||||
|
||||
addShare(payload) {
|
||||
this.$store
|
||||
.dispatch('poll/shares/add', {
|
||||
share: payload,
|
||||
type: payload.type,
|
||||
id: payload.id,
|
||||
emailAddress: payload.emailAddress,
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error while adding share - Error: ', error)
|
||||
showError(t('polls', 'Error while adding share'))
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.shared-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
padding-top: 8px;
|
||||
|
||||
> li {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
margin: 4px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.share-item {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,136 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2018 René Gieling <github@dartcafe.de>
|
||||
-
|
||||
- @author René Gieling <github@dartcafe.de>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
|
||||
<template>
|
||||
<ConfigBox v-if="unsentInvitations.length" :title="t('polls', 'Unsent invitations')" icon-class="icon-polls-mail">
|
||||
<TransitionGroup :css="false" tag="div" class="shared-list">
|
||||
<UserItem v-for="(share) in unsentInvitations"
|
||||
:key="share.id"
|
||||
v-bind="share"
|
||||
:icon="true">
|
||||
<Actions>
|
||||
<ActionButton
|
||||
v-if="share.emailAddress || share.type === 'group'"
|
||||
icon="icon-confirm"
|
||||
@click="sendInvitation(share)">
|
||||
{{ t('polls', 'Send invitation mail') }}
|
||||
</ActionButton>
|
||||
<ActionButton
|
||||
v-if="share.type === 'contactGroup' || share.type === 'circle'"
|
||||
icon="icon-toggle-filelist"
|
||||
@click="resolveGroup(share)">
|
||||
{{ t('polls', 'Resolve into individual invitations') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
<Actions>
|
||||
<ActionButton icon="icon-delete" @click="removeShare(share)">
|
||||
{{ t('polls', 'Remove invitation') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
</UserItem>
|
||||
</TransitionGroup>
|
||||
</ConfigBox>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import { showSuccess, showError } from '@nextcloud/dialogs'
|
||||
import { Actions, ActionButton } from '@nextcloud/vue'
|
||||
import ConfigBox from '../Base/ConfigBox'
|
||||
|
||||
export default {
|
||||
name: 'SideBarTabShare',
|
||||
|
||||
components: {
|
||||
Actions,
|
||||
ActionButton,
|
||||
ConfigBox,
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters({
|
||||
unsentInvitations: 'poll/shares/unsentInvitations',
|
||||
}),
|
||||
},
|
||||
|
||||
methods: {
|
||||
resolveGroup(share) {
|
||||
this.$store.dispatch('poll/shares/resolveGroup', { share: share })
|
||||
.catch((error) => {
|
||||
if (error.response.status === 409 && error.response.data === 'Circles is not enabled for this user') {
|
||||
showError(t('polls', 'Resolving of {name} is not possible. The circles app is not enabled.', { name: share.displayName }))
|
||||
} else if (error.response.status === 409 && error.response.data === 'Contacts is not enabled') {
|
||||
showError(t('polls', 'Resolving of {name} is not possible. The contacts app is not enabled.', { name: share.displayName }))
|
||||
} else {
|
||||
showError(t('polls', 'Error resolving {name}.', { name: share.displayName }))
|
||||
}
|
||||
|
||||
})
|
||||
},
|
||||
|
||||
sendInvitation(share) {
|
||||
this.$store.dispatch('poll/shares/sendInvitation', { share: share })
|
||||
.then((response) => {
|
||||
if ('sentResult.sentMails' in response.data) {
|
||||
response.data.sentResult.sentMails.forEach((item) => {
|
||||
showSuccess(t('polls', 'Invitation sent to {name}', { name: item.displayName }))
|
||||
})
|
||||
}
|
||||
if ('sentResult.abortedMails' in response.data) {
|
||||
response.data.sentResult.abortedMails.forEach((item) => {
|
||||
console.error('Mail could not be sent!', { recipient: item })
|
||||
showError(t('polls', 'Error sending invitation to {name}', { name: item.dispalyName }))
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
removeShare(share) {
|
||||
this.$store.dispatch('poll/shares/delete', { share: share })
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.shared-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
padding-top: 8px;
|
||||
|
||||
> li {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
margin: 4px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.share-item {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -53,7 +53,7 @@
|
|||
:order="4"
|
||||
:name="t('polls', 'Comments')"
|
||||
icon="icon-comment">
|
||||
<Comments />
|
||||
<SideBarTabComments />
|
||||
</AppSidebarTab>
|
||||
</AppSidebar>
|
||||
</template>
|
||||
|
@ -63,7 +63,7 @@ import { AppSidebar, AppSidebarTab } from '@nextcloud/vue'
|
|||
|
||||
import SideBarTabConfiguration from './SideBarTabConfiguration'
|
||||
import SideBarTabOptions from './SideBarTabOptions'
|
||||
import Comments from '../Comments/Comments'
|
||||
import SideBarTabComments from './SideBarTabComments'
|
||||
import SideBarTabShare from './SideBarTabShare'
|
||||
import { mapState } from 'vuex'
|
||||
import { emit } from '@nextcloud/event-bus'
|
||||
|
@ -73,7 +73,7 @@ export default {
|
|||
|
||||
components: {
|
||||
SideBarTabConfiguration,
|
||||
Comments,
|
||||
SideBarTabComments,
|
||||
SideBarTabOptions,
|
||||
SideBarTabShare,
|
||||
AppSidebar,
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2018 René Gieling <github@dartcafe.de>
|
||||
-
|
||||
- @author René Gieling <github@dartcafe.de>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="comments">
|
||||
<CommentAdd v-if="acl.allowComment" />
|
||||
<Comments v-if="!showEmptyContent" />
|
||||
<EmptyContent v-else icon="icon-comment">
|
||||
{{ t('polls', 'No comments') }}
|
||||
<template #desc>
|
||||
{{ t('polls', 'Be the first.') }}
|
||||
</template>
|
||||
</EmptyContent>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CommentAdd from '../Comments/CommentAdd'
|
||||
import Comments from '../Comments/Comments'
|
||||
import { EmptyContent } from '@nextcloud/vue'
|
||||
import { mapGetters, mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'SideBarTabComments',
|
||||
components: {
|
||||
CommentAdd,
|
||||
Comments,
|
||||
EmptyContent,
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState({
|
||||
acl: state => state.poll.acl,
|
||||
}),
|
||||
|
||||
...mapGetters({
|
||||
countComments: 'poll/comments/count',
|
||||
}),
|
||||
|
||||
showEmptyContent() {
|
||||
return this.countComments === 0
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
|
@ -22,31 +22,45 @@
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<ConfigBox v-if="!acl.isOwner" :title="t('polls', 'As an admin you may edit this poll')" icon-class="icon-checkmark" />
|
||||
<SideBarTabOptionsDate v-if="poll.type === 'datePoll'" />
|
||||
<SideBarTabOptionsText v-if="poll.type === 'textPoll'" />
|
||||
<ConfigBox v-if="!isOwner" :title="t('polls', 'As an admin you may edit this poll')" icon-class="icon-checkmark" />
|
||||
<OptionsDateAdd v-if="pollType === 'datePoll' && !pollIsClosed" />
|
||||
<OptionsDateShift v-if="optionsExist && !pollIsClosed" />
|
||||
<OptionsDate v-if="pollType === 'datePoll'" />
|
||||
|
||||
<OptionsTextAdd v-if="pollType === 'textPoll' && !pollIsClosed" />
|
||||
<OptionsText v-if="pollType === 'textPoll'" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import { mapGetters, mapState } from 'vuex'
|
||||
import ConfigBox from '../Base/ConfigBox'
|
||||
import SideBarTabOptionsDate from './SideBarTabOptionsDate'
|
||||
import SideBarTabOptionsText from './SideBarTabOptionsText'
|
||||
import OptionsDate from '../Options/OptionsDate'
|
||||
import OptionsDateAdd from '../Options/OptionsDateAdd'
|
||||
import OptionsDateShift from '../Options/OptionsDateShift'
|
||||
import OptionsText from '../Options/OptionsText'
|
||||
import OptionsTextAdd from '../Options/OptionsTextAdd'
|
||||
|
||||
export default {
|
||||
name: 'SideBarTabOptions',
|
||||
|
||||
components: {
|
||||
ConfigBox,
|
||||
SideBarTabOptionsDate,
|
||||
SideBarTabOptionsText,
|
||||
OptionsDate,
|
||||
OptionsDateAdd,
|
||||
OptionsDateShift,
|
||||
OptionsText,
|
||||
OptionsTextAdd,
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters({
|
||||
pollIsClosed: 'poll/closed',
|
||||
}),
|
||||
...mapState({
|
||||
poll: state => state.poll,
|
||||
acl: state => state.poll.acl,
|
||||
pollType: state => state.poll.type,
|
||||
isOwner: state => state.poll.acl.isOwner,
|
||||
optionsExist: state => state.poll.options.length,
|
||||
}),
|
||||
|
||||
},
|
||||
|
|
|
@ -22,242 +22,45 @@
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<ConfigBox v-if="!acl.isOwner" :title="t('polls', 'As an admin you may edit this poll')" icon-class="icon-checkmark" />
|
||||
|
||||
<ConfigBox :title="t('polls', 'Effective shares')" icon-class="icon-share">
|
||||
<TransitionGroup :css="false" tag="div" class="shared-list">
|
||||
<UserItem v-for="(share) in invitationShares"
|
||||
:key="share.id" v-bind="share"
|
||||
:icon="true">
|
||||
<Actions>
|
||||
<ActionButton
|
||||
v-if="share.emailAddress || share.type === 'group'"
|
||||
icon="icon-confirm"
|
||||
@click="sendInvitation(share)">
|
||||
{{ share.invitationSent ? t('polls', 'Resend invitation mail') : t('polls', 'Send invitation mail') }}
|
||||
</ActionButton>
|
||||
<ActionButton icon="icon-clippy" @click="copyLink( { url: shareUrl(share) })">
|
||||
{{ t('polls', 'Copy link to clipboard') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
<Actions>
|
||||
<ActionButton icon="icon-delete" @click="removeShare(share)">
|
||||
{{ t('polls', 'Remove share') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
</UserItem>
|
||||
</TransitionGroup>
|
||||
|
||||
<Multiselect id="ajax"
|
||||
:options="users"
|
||||
:multiple="false"
|
||||
:user-select="true"
|
||||
:tag-width="80"
|
||||
:clear-on-select="false"
|
||||
:preserve-search="true"
|
||||
:options-limit="30"
|
||||
:loading="isLoading"
|
||||
:internal-search="false"
|
||||
:searchable="true"
|
||||
:preselect-first="true"
|
||||
:placeholder="placeholder"
|
||||
label="displayName"
|
||||
track-by="userId"
|
||||
@select="addShare"
|
||||
@search-change="loadUsersAsync">
|
||||
<template slot="selection" slot-scope="{ values, isOpen }">
|
||||
<span v-if="values.length && !isOpen" class="multiselect__single">
|
||||
{{ values.length }} users selected
|
||||
</span>
|
||||
</template>
|
||||
</Multiselect>
|
||||
</ConfigBox>
|
||||
|
||||
<ConfigBox :title="t('polls', 'Public shares')" icon-class="icon-public">
|
||||
<TransitionGroup :css="false" tag="div" class="shared-list">
|
||||
<PublicShareItem v-for="(share) in publicShares"
|
||||
:key="share.id"
|
||||
v-bind="share">
|
||||
<Actions>
|
||||
<ActionButton icon="icon-clippy" @click="copyLink( { url: shareUrl(share) })">
|
||||
{{ t('polls', 'Copy link to clipboard') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
<Actions>
|
||||
<ActionButton icon="icon-delete" @click="removeShare(share)">
|
||||
{{ t('polls', 'Remove share') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
</PublicShareItem>
|
||||
</TransitionGroup>
|
||||
|
||||
<ButtonDiv :title="t('polls', 'Add a public link')" icon="icon-add" @click="addShare({type: 'public', userId: '', emailAddress: ''})" />
|
||||
</ConfigBox>
|
||||
|
||||
<ConfigBox v-if="unsentInvitations.length" :title="t('polls', 'Unsent invitations')" icon-class="icon-polls-mail">
|
||||
<TransitionGroup :css="false" tag="div" class="shared-list">
|
||||
<UserItem v-for="(share) in unsentInvitations"
|
||||
:key="share.id"
|
||||
v-bind="share"
|
||||
:icon="true">
|
||||
<Actions>
|
||||
<ActionButton
|
||||
v-if="share.emailAddress || share.type === 'group'"
|
||||
icon="icon-confirm"
|
||||
@click="sendInvitation(share)">
|
||||
{{ t('polls', 'Send invitation mail') }}
|
||||
</ActionButton>
|
||||
<ActionButton
|
||||
v-if="share.type === 'contactGroup' || share.type === 'circle'"
|
||||
icon="icon-toggle-filelist"
|
||||
@click="resolveGroup(share)">
|
||||
{{ t('polls', 'Resolve into individual invitations') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
<Actions>
|
||||
<ActionButton icon="icon-delete" @click="removeShare(share)">
|
||||
{{ t('polls', 'Remove invitation') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
</UserItem>
|
||||
</TransitionGroup>
|
||||
<ConfigBox v-if="!isOwner" :title="t('polls', 'As an admin you may edit this poll')" icon-class="icon-checkmark" />
|
||||
<SharesEffective />
|
||||
<ConfigBox :title="t('polls', 'Add Shares')" icon-class="icon-add">
|
||||
<UserSearch />
|
||||
</ConfigBox>
|
||||
<SharesPublic />
|
||||
<SharesUnsent />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import debounce from 'lodash/debounce'
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { showSuccess, showError } from '@nextcloud/dialogs'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { Actions, ActionButton, Multiselect } from '@nextcloud/vue'
|
||||
import ButtonDiv from '../Base/ButtonDiv'
|
||||
import { mapState } from 'vuex'
|
||||
import ConfigBox from '../Base/ConfigBox'
|
||||
import PublicShareItem from '../Base/PublicShareItem'
|
||||
import UserSearch from '../Base/UserSearch'
|
||||
import SharesEffective from '../Shares/SharesEffective'
|
||||
import SharesPublic from '../Shares/SharesPublic'
|
||||
import SharesUnsent from '../Shares/SharesUnsent'
|
||||
|
||||
export default {
|
||||
name: 'SideBarTabShare',
|
||||
|
||||
components: {
|
||||
Actions,
|
||||
ActionButton,
|
||||
ButtonDiv,
|
||||
ConfigBox,
|
||||
Multiselect,
|
||||
PublicShareItem,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
searchToken: null,
|
||||
users: [],
|
||||
isLoading: false,
|
||||
placeholder: t('polls', 'Enter a name to start the search'),
|
||||
}
|
||||
UserSearch,
|
||||
SharesPublic,
|
||||
SharesEffective,
|
||||
SharesUnsent,
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState({
|
||||
acl: state => state.poll.acl,
|
||||
}),
|
||||
|
||||
...mapGetters({
|
||||
invitationShares: 'poll/shares/invitation',
|
||||
unsentInvitations: 'poll/shares/unsentInvitations',
|
||||
publicShares: 'poll/shares/public',
|
||||
isOwner: state => state.poll.acl.isOwner,
|
||||
}),
|
||||
},
|
||||
|
||||
methods: {
|
||||
resolveGroup(share) {
|
||||
this.$store.dispatch('poll/shares/resolveGroup', { share: share })
|
||||
.catch((error) => {
|
||||
if (error.response.status === 409 && error.response.data === 'Circles is not enabled for this user') {
|
||||
showError(t('polls', 'Resolving of {name} is not possible. The circles app is not enabled.', { name: share.displayName }))
|
||||
} else if (error.response.status === 409 && error.response.data === 'Contacts is not enabled') {
|
||||
showError(t('polls', 'Resolving of {name} is not possible. The contacts app is not enabled.', { name: share.displayName }))
|
||||
} else {
|
||||
showError(t('polls', 'Error resolving {name}.', { name: share.displayName }))
|
||||
}
|
||||
|
||||
})
|
||||
},
|
||||
|
||||
sendInvitation(share) {
|
||||
this.$store.dispatch('poll/shares/sendInvitation', { share: share })
|
||||
.then((response) => {
|
||||
if ('sentResult.sentMails' in response.data) {
|
||||
response.data.sentResult.sentMails.forEach((item) => {
|
||||
showSuccess(t('polls', 'Invitation sent to {name}', { name: item.displayName }))
|
||||
})
|
||||
}
|
||||
if ('sentResult.abortedMails' in response.data) {
|
||||
response.data.sentResult.abortedMails.forEach((item) => {
|
||||
console.error('Mail could not be sent!', { recipient: item })
|
||||
showError(t('polls', 'Error sending invitation to {name}', { name: item.dispalyName }))
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
loadUsersAsync: debounce(function(query) {
|
||||
if (!query) {
|
||||
this.users = []
|
||||
return
|
||||
}
|
||||
this.isLoading = true
|
||||
if (this.searchToken) {
|
||||
this.searchToken.cancel()
|
||||
}
|
||||
this.searchToken = axios.CancelToken.source()
|
||||
axios.get(generateUrl('apps/polls/search/users/' + query), { cancelToken: this.searchToken.token })
|
||||
.then((response) => {
|
||||
this.users = response.data.siteusers
|
||||
this.isLoading = false
|
||||
})
|
||||
.catch((error) => {
|
||||
if (axios.isCancel(error)) {
|
||||
// request was cancelled
|
||||
} else {
|
||||
console.error(error.response)
|
||||
this.isLoading = false
|
||||
}
|
||||
})
|
||||
}, 250),
|
||||
|
||||
copyLink(payload) {
|
||||
this
|
||||
.$copyText(window.location.origin + payload.url)
|
||||
.then(() => {
|
||||
showSuccess(t('polls', 'Link copied to clipboard'))
|
||||
})
|
||||
.catch(() => {
|
||||
showError(t('polls', 'Error while copying link to clipboard'))
|
||||
})
|
||||
},
|
||||
|
||||
shareUrl(share) {
|
||||
return generateUrl('apps/polls/s/') + share.token
|
||||
},
|
||||
|
||||
removeShare(share) {
|
||||
this.$store.dispatch('poll/shares/delete', { share: share })
|
||||
},
|
||||
|
||||
addShare(payload) {
|
||||
this.$store
|
||||
.dispatch('poll/shares/add', {
|
||||
share: payload,
|
||||
type: payload.type,
|
||||
id: payload.id,
|
||||
emailAddress: payload.emailAddress,
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error while adding share - Error: ', error)
|
||||
showError(t('polls', 'Error while adding share'))
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -293,9 +96,4 @@ export default {
|
|||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.multiselect {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
<script>
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import OptionItem from '../Base/OptionItem'
|
||||
import OptionItem from '../Options/OptionItem'
|
||||
import Counter from '../Base/Counter'
|
||||
import Confirmation from '../Base/Confirmation'
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче