Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/polls.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRené Gieling <github@dartcafe.de>2020-01-05 18:32:39 +0300
committerGitHub <noreply@github.com>2020-01-05 18:32:39 +0300
commit1582780a61dd47cbb30f3a3b3c584f40e025ca87 (patch)
tree2b499760d9fdf662b2f04ea95e1177b7be66019d
parentb03f6651803f3db4a78b75f59530794c3e2ac3dd (diff)
parent81d0966566e1f2be3d0b8e364aa8d6ae85c31b37 (diff)
Merge pull request #709 from nextcloud/voteAlternativev1.0-beta2
Vote alternative
-rw-r--r--src/js/components/Base/DatePollItem.vue29
-rw-r--r--src/js/components/Base/ParticipantsList.vue87
-rw-r--r--src/js/components/Base/PollDescription.vue49
-rw-r--r--src/js/components/Base/PollInformation.vue52
-rw-r--r--src/js/components/Base/PollTitle.vue (renamed from src/js/components/VoteTable/VoteHeader.vue)31
-rw-r--r--src/js/components/Base/TextPollItem.vue13
-rw-r--r--src/js/components/Comments/CommentAdd.vue (renamed from src/js/components/SideBar/CommentAdd.vue)1
-rw-r--r--src/js/components/Comments/Comments.vue (renamed from src/js/components/SideBar/SideBarTabComments.vue)8
-rw-r--r--src/js/components/SideBar/SideBar.vue8
-rw-r--r--src/js/components/SideBar/SideBarOnlyComments.vue6
-rw-r--r--src/js/components/Subscription/Subscription.vue2
-rw-r--r--src/js/components/VoteTable/VoteHeaderPublic.vue5
-rw-r--r--src/js/components/VoteTable/VoteList.vue194
-rw-r--r--src/js/components/VoteTable/VoteTable.vue3
-rw-r--r--src/js/components/VoteTable/VoteTableItem.vue9
-rw-r--r--src/js/main.js3
-rw-r--r--src/js/store/modules/poll.js1
-rw-r--r--src/js/store/modules/votes.js12
-rw-r--r--src/js/views/PublicVote.vue80
-rw-r--r--src/js/views/Vote.vue64
20 files changed, 523 insertions, 134 deletions
diff --git a/src/js/components/Base/DatePollItem.vue b/src/js/components/Base/DatePollItem.vue
index e58b5861..4791f672 100644
--- a/src/js/components/Base/DatePollItem.vue
+++ b/src/js/components/Base/DatePollItem.vue
@@ -21,15 +21,16 @@
-->
<template>
- <li>
+ <li class="poll-item date">
<div>{{ moment.unix(option.timestamp).format('LLLL') }}</div>
<div>
- <a class="icon-delete" @click="$emit('remove')" />
+ <a v-if="acl.allowEdit" class="icon-delete" @click="$emit('remove')" />
</div>
</li>
</template>
<script>
+import { mapState } from 'vuex'
export default {
name: 'DatePollItem',
@@ -39,24 +40,12 @@ export default {
type: Object,
default: undefined
}
- }
+ },
+ computed: {
+ ...mapState({
+ acl: state => state.acl
+ })
+ }
}
</script>
-
-<style lang="scss" scoped>
- li > div {
- display: flex;
- flex-grow: 1;
- font-size: 1.2em;
- opacity: 0.7;
- white-space: normal;
- padding-right: 4px;
- }
-
- li > div:nth-last-child(1) {
- justify-content: center;
- flex-grow: 0;
- flex-shrink: 0;
- }
-</style>
diff --git a/src/js/components/Base/ParticipantsList.vue b/src/js/components/Base/ParticipantsList.vue
new file mode 100644
index 00000000..1ae1d895
--- /dev/null
+++ b/src/js/components/Base/ParticipantsList.vue
@@ -0,0 +1,87 @@
+<!--
+ - @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="participants-list">
+ <div v-if="acl.allowSeeUsernames">
+ <h2>{{ t('polls','Participants') }}</h2>
+ <div class="participants">
+ <userDiv v-for="(participant) in participants"
+ :key="participant"
+ :hide-names="true"
+ :user-id="participant"
+ type="user" />
+ </div>
+ </div>
+
+ <div v-else>
+ <h2>{{ t('polls','Participants names are hidden, because this is an anoymous poll') }} </h2>
+ </div>
+ </div>
+</template>
+
+<script>
+import { mapState, mapGetters } from 'vuex'
+
+export default {
+ name: 'ParticipantsList',
+
+ data() {
+ return {
+ voteSaved: false,
+ delay: 50,
+ newName: ''
+ }
+ },
+
+ computed: {
+ ...mapState({
+ acl: state => state.acl
+ }),
+
+ ...mapGetters([
+ 'participants'
+ ])
+
+ }
+
+}
+</script>
+
+<style lang="scss" scoped>
+ .participants-list {
+ margin: 8px 0;
+ }
+
+ .participants {
+ display: flex;
+ justify-content: flex-start;
+ .user-row {
+ display: block;
+ flex: 0;
+ }
+ .user {
+ padding: 0;
+ }
+ }
+
+</style>
diff --git a/src/js/components/Base/PollDescription.vue b/src/js/components/Base/PollDescription.vue
new file mode 100644
index 00000000..66afd9e2
--- /dev/null
+++ b/src/js/components/Base/PollDescription.vue
@@ -0,0 +1,49 @@
+<!--
+ - @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>
+ <!-- eslint-disable-next-line vue/singleline-html-element-content-newline -->
+ <h3 class="poll-description">{{ poll.description }}</h3>
+</template>
+
+<script>
+import { mapState } from 'vuex'
+
+export default {
+ name: 'PollDescription',
+
+ computed: {
+ ...mapState({
+ poll: state => state.poll
+ })
+
+ }
+
+}
+</script>
+
+<style lang="scss" scoped>
+ .poll-description {
+ white-space: break-spaces;
+ margin: 8px 0;
+ }
+</style>
diff --git a/src/js/components/Base/PollInformation.vue b/src/js/components/Base/PollInformation.vue
new file mode 100644
index 00000000..e6de3786
--- /dev/null
+++ b/src/js/components/Base/PollInformation.vue
@@ -0,0 +1,52 @@
+<!--
+ - @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="poll-information">
+ <UserBubble :user="poll.owner" :display-name="poll.owner" />
+ {{ t('polls', ' started this poll on %n. ', 1, moment.unix(poll.created).format('LLLL')) }}
+ <span v-if="expired">{{ t('polls', 'Voting is no more possible, because this poll expired since %n', 1, moment.unix(poll.expire).format('LLLL')) }}</span>
+ <span v-if="!expired && poll.expire && acl.allowVote">{{ t('polls', 'You can place your vote until %n. ',1, moment.unix(poll.expire).format('LLLL')) }}</span>
+ <span v-if="poll.anonymous">{{ t('polls', 'The names of other participants is hidden, as this is a anonymous poll. ') }}</span>
+ <span>{{ t('polls', '%n voters participated in this poll until now.', 1, participants.length) }}</span>
+ </div>
+</template>
+
+<script>
+import { mapState, mapGetters } from 'vuex'
+
+export default {
+ name: 'PollInformation',
+
+ computed: {
+ ...mapState({
+ acl: state => state.acl,
+ poll: state => state.poll
+ }),
+
+ ...mapGetters([
+ 'participants',
+ 'expired'
+ ])
+ }
+}
+</script>
diff --git a/src/js/components/VoteTable/VoteHeader.vue b/src/js/components/Base/PollTitle.vue
index acb4fc66..3480be79 100644
--- a/src/js/components/VoteTable/VoteHeader.vue
+++ b/src/js/components/Base/PollTitle.vue
@@ -21,32 +21,19 @@
-->
<template>
- <div class="voteHeader">
- <h2>
- {{ poll.title }}
- <span v-if="expired" class="label error">{{ t('polls', 'Expired since %n', 1, moment.unix(poll.expire).format('LLLL')) }}</span>
- <span v-if="!expired && poll.expire" class="label success">{{ t('polls', 'Place your votes until %n', 1, moment.unix(poll.expire).format('LLLL')) }}</span>
- <span v-if="poll.deleted" class="label error">{{ t('polls', 'Deleted') }}</span>
- </h2>
- <h3>
- {{ poll.description }}
- </h3>
- </div>
+ <h2 class="poll-title">
+ {{ poll.title }}
+ <span v-if="expired" class="label error">{{ t('polls', 'Expired since %n', 1, moment.unix(poll.expire).format('LLLL')) }}</span>
+ <span v-if="!expired && poll.expire" class="label success">{{ t('polls', 'Place your votes until %n', 1, moment.unix(poll.expire).format('LLLL')) }}</span>
+ <span v-if="poll.deleted" class="label error">{{ t('polls', 'Deleted') }}</span>
+ </h2>
</template>
<script>
import { mapState, mapGetters } from 'vuex'
export default {
- name: 'VoteHeader',
-
- data() {
- return {
- voteSaved: false,
- delay: 50,
- newName: ''
- }
- },
+ name: 'PollTitle',
computed: {
...mapState({
@@ -63,7 +50,7 @@ export default {
</script>
<style lang="scss" scoped>
- .voteHeader {
- margin: 8px 24px;
+ .pollTitle {
+ margin: 8px 0;
}
</style>
diff --git a/src/js/components/Base/TextPollItem.vue b/src/js/components/Base/TextPollItem.vue
index aefd8d09..ccbb952c 100644
--- a/src/js/components/Base/TextPollItem.vue
+++ b/src/js/components/Base/TextPollItem.vue
@@ -21,15 +21,17 @@
-->
<template>
- <li>
+ <li class="poll-item text">
<div>{{ option.pollOptionText }}</div>
<div>
- <a class="icon icon-delete svg delete-poll" @click="$emit('remove')" />
+ <a v-if="acl.allowEdit" class="icon icon-delete svg delete-poll" @click="$emit('remove')" />
</div>
</li>
</template>
<script>
+import { mapState } from 'vuex'
+
export default {
name: 'TextPollItem',
@@ -38,7 +40,12 @@ export default {
type: Object,
default: undefined
}
+ },
+
+ computed: {
+ ...mapState({
+ acl: state => state.acl
+ })
}
}
-
</script>
diff --git a/src/js/components/SideBar/CommentAdd.vue b/src/js/components/Comments/CommentAdd.vue
index 915bc71f..972c476f 100644
--- a/src/js/components/SideBar/CommentAdd.vue
+++ b/src/js/components/Comments/CommentAdd.vue
@@ -53,6 +53,7 @@ export default {
this.isLoading = true
this.$store.dispatch('setCommentAsync', { message: this.comment })
.then(() => {
+ this.isLoading = false
OC.Notification.showTemporary(t('polls', 'Your comment was added'), { type: 'success' })
this.isLoading = false
})
diff --git a/src/js/components/SideBar/SideBarTabComments.vue b/src/js/components/Comments/Comments.vue
index 721b82be..b43a81aa 100644
--- a/src/js/components/SideBar/SideBarTabComments.vue
+++ b/src/js/components/Comments/Comments.vue
@@ -22,7 +22,8 @@
<template>
<div>
- <CommentAdd />
+ <h2>{{ t('polls','Comments') }} </h2>
+ <CommentAdd v-if="acl.allowComment" />
<transition-group v-if="countComments" name="fade" class="comments"
tag="ul">
<li v-for="(comment) in sortedComments" :key="comment.id">
@@ -49,14 +50,15 @@ import CommentAdd from './CommentAdd'
import { mapState, mapGetters } from 'vuex'
export default {
- name: 'SideBarTabComments',
+ name: 'Comments',
components: {
CommentAdd
},
computed: {
...mapState({
- comments: state => state.comments
+ comments: state => state.comments,
+ acl: state => state.acl
}),
...mapGetters([
'countComments',
diff --git a/src/js/components/SideBar/SideBar.vue b/src/js/components/SideBar/SideBar.vue
index 207d2eb3..5328f012 100644
--- a/src/js/components/SideBar/SideBar.vue
+++ b/src/js/components/SideBar/SideBar.vue
@@ -22,10 +22,8 @@
<template>
<AppSidebar ref="sideBar" :title="t('polls', 'Details')" @close="$emit('closeSideBar')">
- <UserDiv slot="primary-actions" :user-id="poll.owner" :description="t('polls', 'Owner')" />
-
<AppSidebarTab :name="t('polls', 'Comments')" icon="icon-comment">
- <SideBarTabComments />
+ <Comments />
</AppSidebarTab>
<AppSidebarTab v-if="acl.allowEdit" :name="t('polls', 'options')" icon="icon-toggle-filelist">
@@ -47,7 +45,7 @@ import { AppSidebar, AppSidebarTab } from '@nextcloud/vue'
import SideBarTabConfiguration from './SideBarTabConfiguration'
import SideBarTabOptions from './SideBarTabOptions'
-import SideBarTabComments from './SideBarTabComments'
+import Comments from '../Comments/Comments'
import SideBarTabShare from './SideBarTabShare'
import { mapState } from 'vuex'
@@ -55,7 +53,7 @@ export default {
name: 'SideBar',
components: {
SideBarTabConfiguration,
- SideBarTabComments,
+ Comments,
SideBarTabOptions,
SideBarTabShare,
AppSidebar,
diff --git a/src/js/components/SideBar/SideBarOnlyComments.vue b/src/js/components/SideBar/SideBarOnlyComments.vue
index 420d057c..3a93196f 100644
--- a/src/js/components/SideBar/SideBarOnlyComments.vue
+++ b/src/js/components/SideBar/SideBarOnlyComments.vue
@@ -25,7 +25,7 @@
<UserDiv slot="primary-actions" :user-id="poll.owner" :description="t('polls', 'Owner')" />
<AppSidebarTab :name="t('polls', 'Comments')" icon="icon-comment">
- <SideBarTabComments />
+ <Comments />
</AppSidebarTab>
</AppSidebar>
</template>
@@ -33,13 +33,13 @@
<script>
import { AppSidebar, AppSidebarTab } from '@nextcloud/vue'
-import SideBarTabComments from './SideBarTabComments'
+import Comments from '../Comments/Comments'
import { mapState } from 'vuex'
export default {
name: 'SideBarOnlyComments',
components: {
- SideBarTabComments,
+ Comments,
AppSidebar,
AppSidebarTab
},
diff --git a/src/js/components/Subscription/Subscription.vue b/src/js/components/Subscription/Subscription.vue
index 20a32077..56fcdac4 100644
--- a/src/js/components/Subscription/Subscription.vue
+++ b/src/js/components/Subscription/Subscription.vue
@@ -59,6 +59,6 @@ export default {
<style lang="css" scoped>
.subscription {
- padding: 24px;
+ margin: 8px 0;
}
</style>
diff --git a/src/js/components/VoteTable/VoteHeaderPublic.vue b/src/js/components/VoteTable/VoteHeaderPublic.vue
index f16778bb..cd2b7609 100644
--- a/src/js/components/VoteTable/VoteHeaderPublic.vue
+++ b/src/js/components/VoteTable/VoteHeaderPublic.vue
@@ -23,17 +23,12 @@
<template>
<div class="voteHeader">
<div v-if="!isValidUser" class="getUsername">
- <!-- <label>
- {{ t('polls', 'Enter a valid username, to participate in this poll.') }}
- </label> -->
-
<form v-if="!redirecting">
<input v-model="userName" :class="{ error: (!isValidName && userName.length > 0), success: isValidName }" type="text"
:placeholder="t('polls', 'Enter a valid username with at least 3 Characters')">
<input v-show="isValidName && !checkingUserName" class="icon-confirm" :class="{ error: !isValidName, success: isValidName }"
@click="writeUserName">
<span v-show="checkingUserName" class="icon-loading-small" />
- <!-- <span v-if="!isValidName" class="error"> {{ invalidUserNameMessage }} </span> -->
</form>
<div v-else>
<span>{{ t('polls', 'You will be redirected to your personal share.') }}</span>
diff --git a/src/js/components/VoteTable/VoteList.vue b/src/js/components/VoteTable/VoteList.vue
new file mode 100644
index 00000000..b5fcb42d
--- /dev/null
+++ b/src/js/components/VoteTable/VoteList.vue
@@ -0,0 +1,194 @@
+<!--
+ - @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 lang="html">
+ <ul class="vote-list">
+ <li v-for="(option) in sortedOptions" :key="option.id" class="vote-row">
+ <VoteTableItem
+ v-if="acl.allowVote"
+ :user-id="acl.userId"
+ :option="option"
+ @voteClick="setVote(option, acl.userId)" />
+ <TextPollItem v-if="poll.type === 'textPoll'" :option="option" />
+ <DatePollItem v-if="poll.type === 'datePoll'" :option="option" />
+
+ <div class="counter">
+ <div v-if="yesVotes(option.pollOptionText)" class="yes" :style="{ flex: yesVotes(option.pollOptionText) }">
+ <span> {{ yesVotes(option.pollOptionText) }} </span>
+ </div>
+
+ <div v-if="maybeVotes(option.pollOptionText)" class="maybe" :style="{flex: maybeVotes(option.pollOptionText) }">
+ <span> {{ maybeVotes(option.pollOptionText) }} </span>
+ </div>
+
+ <div v-if="noVotes(option.pollOptionText)" class="no" :style="{flex: noVotes(option.pollOptionText) }">
+ <span> {{ noVotes(option.pollOptionText) }} </span>
+ </div>
+ </div>
+ </li>
+ </ul>
+</template>
+
+<script>
+import TextPollItem from '../Base/TextPollItem'
+import DatePollItem from '../Base/DatePollItem'
+import VoteTableItem from './VoteTableItem'
+import { mapState, mapGetters } from 'vuex'
+
+export default {
+ name: 'VoteTable',
+ components: {
+ DatePollItem,
+ TextPollItem,
+ VoteTableItem
+ },
+
+ computed: {
+ ...mapState({
+ poll: state => state.poll,
+ acl: state => state.acl
+ }),
+
+ ...mapGetters([
+ 'sortedOptions',
+ 'participants',
+ 'votesRank',
+ 'expired'
+ ]),
+
+ currentUser() {
+ return this.acl.userId
+ },
+
+ noOptions() {
+ return (this.sortedOptions.length === 0)
+ }
+
+ },
+
+ methods: {
+
+ yesVotes(pollOptionText) {
+ return this.votesRank.find(rank => {
+ return rank.pollOptionText === pollOptionText
+ }).yes
+ },
+
+ maybeVotes(pollOptionText) {
+ return this.votesRank.find(rank => {
+ return rank.pollOptionText === pollOptionText
+ }).maybe
+ },
+
+ noVotes(pollOptionText) {
+ return this.participants.length - this.maybeVotes(pollOptionText) - this.yesVotes(pollOptionText)
+ },
+
+ setVote(option, participant) {
+ this.$store
+ .dispatch('setVoteAsync', {
+ option: option,
+ userId: participant,
+ setTo: this.$store.getters.getNextAnswer({
+ option: option,
+ userId: participant
+ })
+ })
+ .then(() => {
+ // this.$emit('voteSaved')
+ })
+ }
+ }
+}
+</script>
+
+<style lang="scss" scoped>
+
+ .poll-item {
+ flex: 3;
+ }
+
+ .vote-item {
+ flex: 0;
+ }
+
+ .counter {
+ display: flex;
+ width: 80px;
+ flex: 1;
+
+ > * {
+ text-align: center;
+ border-radius: 21px;
+ margin: 2px;
+ }
+
+ .yes {
+ background-color: #ebf5d6;
+ }
+
+ .maybe {
+ background-color: #f0db98;
+ }
+
+ .no {
+ background-color: #f45573;
+ }
+ }
+
+ .vote-list {
+ margin: 8px 0;
+ display: flex;
+ flex: 0;
+ flex-direction: column;
+ justify-content: flex-start;
+ overflow: scroll;
+
+ .vote-row {
+ display: flex;
+ justify-content: space-around;
+ flex: 1;
+ align-items: center;
+ border-bottom: 1px solid var(--color-border);
+
+ &:active,
+ &:hover {
+ transition: var(--background-dark) 0.3s ease;
+ background-color: var(--color-background-dark); //$hover-color;
+ }
+
+ .vote-table-item {
+ flex: 0;
+ }
+
+ > li {
+ display: flex;
+ align-items: center;
+ padding-left: 8px;
+ padding-right: 8px;
+ line-height: 2em;
+ min-height: 4em;
+ overflow: hidden;
+ }
+ }
+ }
+</style>
diff --git a/src/js/components/VoteTable/VoteTable.vue b/src/js/components/VoteTable/VoteTable.vue
index 010c5ec2..1e41b5a5 100644
--- a/src/js/components/VoteTable/VoteTable.vue
+++ b/src/js/components/VoteTable/VoteTable.vue
@@ -160,6 +160,9 @@ export default {
flex: 1;
align-items: center;
}
+ .vote-table-item {
+ flex: 1;
+ }
}
@media (max-width: (480px)) {
diff --git a/src/js/components/VoteTable/VoteTableItem.vue b/src/js/components/VoteTable/VoteTableItem.vue
index e1647fdd..76d0bc14 100644
--- a/src/js/components/VoteTable/VoteTableItem.vue
+++ b/src/js/components/VoteTable/VoteTableItem.vue
@@ -21,7 +21,7 @@
-->
<template>
- <div class="vote-item" :class="[answer, { active: isActive}]">
+ <div class="vote-table-item" :class="[answer, { active: isActive}]">
<div class="icon" @click="voteClick()" />
</div>
</template>
@@ -82,7 +82,7 @@ export default {
</script>
-<style lang="scss" scoped>
+<style lang="scss">
$bg-no: #ffede9;
$bg-maybe: #fcf7e1;
$bg-unvoted: #fff4c8;
@@ -93,10 +93,9 @@ export default {
$fg-unvoted: #f0db98;
$fg-yes: #49bc49;
- .vote-item {
+ .vote-table-item {
height: 43px;
display: flex;
- flex: 1;
width: 85px;
align-items: center;
background-color: var(--color-background-dark);
@@ -156,7 +155,7 @@ export default {
}
@media (max-width: (480px) ) {
- .vote-item {
+ .vote-table-item {
border-top: 1px solid var(--color-border-dark);
&.active {
width: 10vw;
diff --git a/src/js/main.js b/src/js/main.js
index 3020e2e4..9b7d5117 100644
--- a/src/js/main.js
+++ b/src/js/main.js
@@ -31,7 +31,7 @@ import vClickOutside from 'v-click-outside'
import VueClipboard from 'vue-clipboard2'
import moment from 'moment'
-import { PopoverMenu, Tooltip, DatetimePicker, AppContent } from '@nextcloud/vue'
+import { PopoverMenu, Tooltip, DatetimePicker, AppContent, UserBubble } from '@nextcloud/vue'
import UserDiv from './components/Base/UserDiv'
import LoadingOverlay from './components/Base/LoadingOverlay'
@@ -59,6 +59,7 @@ Vue.component('AppContent', AppContent)
Vue.component('DatePicker', DatetimePicker)
Vue.component('UserDiv', UserDiv)
Vue.component('LoadingOverlay', LoadingOverlay)
+Vue.component('UserBubble', UserBubble)
Vue.directive('tooltip', Tooltip)
diff --git a/src/js/store/modules/poll.js b/src/js/store/modules/poll.js
index 521bfbf5..cffd1551 100644
--- a/src/js/store/modules/poll.js
+++ b/src/js/store/modules/poll.js
@@ -88,7 +88,6 @@ const actions = {
loadPollMain(context, payload) {
let endPoint = 'apps/polls/poll/get/'
-
if (payload.token) {
endPoint = endPoint.concat('s/', payload.token)
} else if (payload.pollId) {
diff --git a/src/js/store/modules/votes.js b/src/js/store/modules/votes.js
index a5a01284..7576282c 100644
--- a/src/js/store/modules/votes.js
+++ b/src/js/store/modules/votes.js
@@ -79,7 +79,7 @@ const getters = {
},
votesRank: (state, getters, rootGetters) => {
- const rank = []
+ let rank = []
rootGetters.options.list.forEach(function(option) {
const countYes = state.list.filter(vote => vote.voteOptionText === option.pollOptionText && vote.voteAnswer === 'yes').length
const countMaybe = state.list.filter(vote => vote.voteOptionText === option.pollOptionText && vote.voteAnswer === 'maybe').length
@@ -92,7 +92,15 @@ const getters = {
maybe: countMaybe
})
})
- return orderBy(rank, ['yes', 'maybe'], ['desc', 'desc'])
+ rank = orderBy(rank, ['yes', 'maybe'], ['desc', 'desc'])
+ for (var i = 0; i < rank.length; i++) {
+ if (i > 0 && rank[i].yes === rank[i - 1].yes && rank[i].maybe === rank[i - 1].maybe) {
+ rank[i].rank = rank[i - 1].rank
+ } else {
+ rank[i].rank = i + 1
+ }
+ }
+ return rank
},
winnerCombo: (state, getters) => {
diff --git a/src/js/views/PublicVote.vue b/src/js/views/PublicVote.vue
index c8d3d019..73bf387e 100644
--- a/src/js/views/PublicVote.vue
+++ b/src/js/views/PublicVote.vue
@@ -23,33 +23,45 @@
<template>
<AppContent>
<div v-if="poll.id > 0" class="main-container">
- <a v-if="!sideBarOpen" href="#" class="icon icon-settings active"
- :title="t('polls', 'Open Sidebar')" @click="toggleSideBar()" />
-
- <VoteHeader />
+ <PollTitle />
+ <PollInformation />
+ <PollDescription />
<VoteHeaderPublic />
- <VoteTable />
+ <button class="button btn primary" @click="tableMode = !tableMode">
+ <span>{{ t('polls', 'Switch view') }}</span>
+ </button>
+ <VoteList v-show="!isLoading && !tableMode" />
+ <VoteTable v-show="!isLoading && tableMode" />
+ <ParticipantsList />
+ <Comments />
</div>
- <SideBar v-if="sideBarOpen" @closeSideBar="toggleSideBar" />
- <LoadingOverlay v-if="loading" />
+ <LoadingOverlay v-if="isLoading" />
</AppContent>
</template>
<script>
-import VoteHeader from '../components/VoteTable/VoteHeader'
+import Comments from '../components/Comments/Comments'
+import ParticipantsList from '../components/Base/ParticipantsList'
+import PollDescription from '../components/Base/PollDescription'
+import PollInformation from '../components/Base/PollInformation'
+import PollTitle from '../components/Base/PollTitle'
import VoteHeaderPublic from '../components/VoteTable/VoteHeaderPublic'
+import VoteList from '../components/VoteTable/VoteList'
import VoteTable from '../components/VoteTable/VoteTable'
-import SideBar from '../components/SideBar/SideBar'
import { mapState } from 'vuex'
export default {
name: 'Vote',
components: {
- VoteHeader,
+ ParticipantsList,
+ PollInformation,
+ PollTitle,
+ PollDescription,
VoteHeaderPublic,
+ Comments,
VoteTable,
- SideBar
+ VoteList
},
data() {
@@ -57,15 +69,15 @@ export default {
voteSaved: false,
delay: 50,
sideBarOpen: false,
- loading: false,
- initialTab: 'comments'
+ isLoading: false,
+ initialTab: 'comments',
+ tableMode: true
}
},
computed: {
...mapState({
- poll: state => state.poll,
- acl: state => state.acl
+ poll: state => state.poll
}),
windowTitle: function() {
@@ -86,19 +98,19 @@ export default {
methods: {
loadPoll() {
- this.loading = false
+ this.isLoading = false
this.$store.dispatch('loadPollMain', { token: this.$route.params.token })
.then(() => {
this.$store.dispatch({ type: 'loadAcl', token: this.$route.params.token })
.then(() => {
this.$store.dispatch('loadPoll', { token: this.$route.params.token })
.then(() => {
- this.loading = false
+ this.isLoading = false
})
})
.catch((error) => {
console.error(error)
- this.loading = false
+ this.isLoading = false
})
})
},
@@ -112,23 +124,21 @@ export default {
</script>
<style lang="scss" scoped>
- .main-container {
- flex: 1;
- margin: 0;
- flex-direction: column;
- flex-wrap: nowrap;
- overflow-x: scroll;
- h1, h2, h3, h4 {
- margin-left: 24px;
- }
- }
+.main-container {
+ flex: 1;
+ padding: 0 24px;
+ margin: 0;
+ flex-direction: column;
+ flex-wrap: nowrap;
+ overflow-x: scroll;
+}
- .icon.icon-settings.active {
- display: block;
- width: 44px;
- height: 44px;
- right: 0;
- position: absolute;
- }
+.icon.icon-settings.active {
+ display: block;
+ width: 44px;
+ height: 44px;
+ right: 0;
+ position: absolute;
+}
</style>
diff --git a/src/js/views/Vote.vue b/src/js/views/Vote.vue
index 021cc947..a43eb214 100644
--- a/src/js/views/Vote.vue
+++ b/src/js/views/Vote.vue
@@ -23,34 +23,49 @@
<template>
<AppContent>
<div v-if="poll.id > 0" class="main-container">
- <a v-if="!sideBarOpen" href="#" class="icon icon-settings active"
+ <a v-if="!sideBarOpen && acl.allowEdit" href="#" class="icon icon-settings active"
:title="t('polls', 'Open Sidebar')" @click="toggleSideBar()" />
- <VoteHeader />
- <VoteTable v-show="!loading" />
+ <PollTitle />
+ <PollInformation />
+ <PollDescription />
+ <button class="button btn primary" @click="tableMode = !tableMode">
+ <span>{{ t('polls', 'Switch view') }}</span>
+ </button>
+ <VoteList v-show="!isLoading && !tableMode" />
+ <VoteTable v-show="!isLoading && tableMode" />
<Subscription />
+ <ParticipantsList />
+ <Comments />
</div>
<SideBar v-if="sideBarOpen && acl.allowEdit" @closeSideBar="toggleSideBar" />
- <SideBarOnlyComments v-if="sideBarOpen && !acl.allowEdit" @closeSideBar="toggleSideBar" />
- <LoadingOverlay v-if="loading" />
+ <LoadingOverlay v-if="isLoading" />
</AppContent>
</template>
<script>
+import Comments from '../components/Comments/Comments'
import Subscription from '../components/Subscription/Subscription'
-import VoteHeader from '../components/VoteTable/VoteHeader'
-import VoteTable from '../components/VoteTable/VoteTable'
+import ParticipantsList from '../components/Base/ParticipantsList'
+import PollDescription from '../components/Base/PollDescription'
+import PollInformation from '../components/Base/PollInformation'
+import PollTitle from '../components/Base/PollTitle'
import SideBar from '../components/SideBar/SideBar'
-import SideBarOnlyComments from '../components/SideBar/SideBarOnlyComments'
-import { mapState, mapGetters } from 'vuex'
+import VoteList from '../components/VoteTable/VoteList'
+import VoteTable from '../components/VoteTable/VoteTable'
+import { mapState } from 'vuex'
export default {
name: 'Vote',
components: {
+ ParticipantsList,
+ PollInformation,
+ PollTitle,
+ PollDescription,
Subscription,
- VoteHeader,
VoteTable,
- SideBarOnlyComments,
+ VoteList,
+ Comments,
SideBar
},
@@ -59,30 +74,23 @@ export default {
voteSaved: false,
delay: 50,
sideBarOpen: false,
- loading: false,
+ isLoading: false,
initialTab: 'comments',
- newName: ''
+ newName: '',
+ tableMode: true
}
},
computed: {
...mapState({
poll: state => state.poll,
- shares: state => state.shares,
acl: state => state.acl
}),
- ...mapGetters([
- 'expired'
- ]),
-
windowTitle: function() {
return t('polls', 'Polls') + ' - ' + this.poll.title
- },
-
- votePossible() {
- return this.acl.allowVote && !this.expired
}
+
},
watch: {
@@ -98,7 +106,7 @@ export default {
if (this.acl.allowEdit && moment.unix(this.poll.created).diff() > -10000) {
this.sideBarOpen = true
}
- this.loading = false
+ this.isLoading = false
})
})
}
@@ -110,10 +118,12 @@ export default {
methods: {
loadPoll() {
- this.loading = false
+ this.isLoading = false
this.$store.dispatch({ type: 'loadPollMain', pollId: this.$route.params.id })
+ .then(() => {
+ })
.catch(() => {
- this.loading = false
+ this.isLoading = false
})
},
@@ -127,13 +137,11 @@ export default {
<style lang="scss" scoped>
.main-container {
flex: 1;
+ padding: 0 24px;
margin: 0;
flex-direction: column;
flex-wrap: nowrap;
overflow-x: scroll;
- h1, h2, h3, h4 {
- margin-left: 24px;
- }
}
.icon.icon-settings.active {