diff --git a/src/PublicShareAuthSidebar.vue b/src/PublicShareAuthSidebar.vue
index ae6f8b5d2c2..3adadfd18eb 100644
--- a/src/PublicShareAuthSidebar.vue
+++ b/src/PublicShareAuthSidebar.vue
@@ -32,6 +32,7 @@
:is-sidebar="true" />
+
@@ -46,6 +47,7 @@ import { loadState } from '@nextcloud/initial-state'
import CallView from './components/CallView/CallView.vue'
import ChatView from './components/ChatView.vue'
import MediaSettings from './components/MediaSettings/MediaSettings.vue'
+import PollViewer from './components/PollViewer/PollViewer.vue'
import TopBar from './components/TopBar/TopBar.vue'
import TransitionWrapper from './components/TransitionWrapper.vue'
@@ -66,6 +68,7 @@ export default {
CallView,
ChatView,
MediaSettings,
+ PollViewer,
TopBar,
TransitionWrapper,
},
diff --git a/src/PublicShareSidebar.vue b/src/PublicShareSidebar.vue
index ded5c415e19..779f4d023dd 100644
--- a/src/PublicShareSidebar.vue
+++ b/src/PublicShareSidebar.vue
@@ -46,6 +46,7 @@
+
@@ -63,6 +64,7 @@ import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import CallView from './components/CallView/CallView.vue'
import ChatView from './components/ChatView.vue'
import MediaSettings from './components/MediaSettings/MediaSettings.vue'
+import PollViewer from './components/PollViewer/PollViewer.vue'
import CallButton from './components/TopBar/CallButton.vue'
import TopBar from './components/TopBar/TopBar.vue'
import TransitionWrapper from './components/TransitionWrapper.vue'
@@ -83,12 +85,13 @@ export default {
name: 'PublicShareSidebar',
components: {
- NcButton,
CallButton,
CallView,
ChatView,
- PreventUnload,
MediaSettings,
+ NcButton,
+ PollViewer,
+ PreventUnload,
TopBar,
TransitionWrapper,
},
diff --git a/src/components/MessagesList/MessagesGroup/Message/MessagePart/MessageBody.vue b/src/components/MessagesList/MessagesGroup/Message/MessagePart/MessageBody.vue
index 1d37ade1fb4..30dc957a848 100644
--- a/src/components/MessagesList/MessagesGroup/Message/MessagePart/MessageBody.vue
+++ b/src/components/MessagesList/MessagesGroup/Message/MessagePart/MessageBody.vue
@@ -28,7 +28,7 @@
:class="{
'system-message': isSystemMessage && !showJoinCallButton,
'deleted-message': isDeletedMessage,
- 'call-started': showJoinCallButton,
+ 'message-highlighted': showJoinCallButton,
}">
@@ -49,6 +49,7 @@
@@ -284,6 +285,14 @@ export default {
return this.messageParameters?.file
},
+ isNewPollMessage() {
+ if (this.messageParameters?.object?.type !== 'talk-poll') {
+ return false
+ }
+
+ return this.isInCall && !!this.$store.getters.getNewPolls[this.messageParameters.object.id]
+ },
+
hideDate() {
return this.isTemporary || this.isDeleting || !!this.sendingFailure
},
@@ -442,7 +451,7 @@ export default {
padding: 0 20px;
}
- &.call-started {
+ &.message-highlighted {
color: var(--color-text-light);
background-color: var(--color-primary-element-light);
padding: 10px;
diff --git a/src/components/MessagesList/MessagesGroup/Message/MessagePart/Poll.vue b/src/components/MessagesList/MessagesGroup/Message/MessagePart/Poll.vue
index 7332374fdcc..eb3c025738b 100644
--- a/src/components/MessagesList/MessagesGroup/Message/MessagePart/Poll.vue
+++ b/src/components/MessagesList/MessagesGroup/Message/MessagePart/Poll.vue
@@ -2,8 +2,9 @@
- @copyright Copyright (c) 2022 Marco Ambrosini
-
- @author Marco Ambrosini
+ - @author Maksim Sukharev
-
- - @license GNU AGPL version 3 or any later version
+ - @license AGPL-3.0-or-later
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
@@ -20,152 +21,43 @@
-->
-
-
-
-
-
-
-
-
-
-
-
- {{ t('spreed', 'See results') }}
-
-
-
-
-
-
-
-
-
- {{ pollSummaryText }}
-
-
-
-
-
-
- {{ option }}
-
-
-
-
-
-
-
-
-
{{ option }}
-
- {{ getVotePercentage(index) + '%' }}
-
-
-
-
-
- {{ t('spreed', 'You voted for this option') }}
-
-
-
-
-
-
-
-
-
- {{ t('spreed', 'Submit vote') }}
-
-
-
- {{ t('spreed', 'Change your vote') }}
-
-
-
-
- {{ t('spreed', 'End poll') }}
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+ {{ t('spreed', 'See results') }}
+
diff --git a/src/components/PollViewer/PollViewer.vue b/src/components/PollViewer/PollViewer.vue
new file mode 100644
index 00000000000..7a0e332f011
--- /dev/null
+++ b/src/components/PollViewer/PollViewer.vue
@@ -0,0 +1,446 @@
+
+
+
+
+
+
+
+ {{ pollSummaryText }}
+
+
+
+
+
+ {{ option }}
+
+
+
+
+
+
+
+
{{ option }}
+
+ {{ getVotePercentage(index) + '%' }}
+
+
+
+
+
+ {{ t('spreed', 'You voted for this option') }}
+
+
+
+
+
+
+
+
+
+ {{ t('spreed', 'Submit vote') }}
+
+
+
+ {{ t('spreed', 'Change your vote') }}
+
+
+
+
+ {{ t('spreed', 'End poll') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/MessagesList/MessagesGroup/Message/MessagePart/PollVotersDetails.vue b/src/components/PollViewer/PollVotersDetails.vue
similarity index 95%
rename from src/components/MessagesList/MessagesGroup/Message/MessagePart/PollVotersDetails.vue
rename to src/components/PollViewer/PollVotersDetails.vue
index 977685f7c23..94df9557b42 100644
--- a/src/components/MessagesList/MessagesGroup/Message/MessagePart/PollVotersDetails.vue
+++ b/src/components/PollViewer/PollVotersDetails.vue
@@ -59,9 +59,9 @@
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcPopover from '@nextcloud/vue/dist/Components/NcPopover.js'
-import AvatarWrapper from '../../../../AvatarWrapper/AvatarWrapper.vue'
+import AvatarWrapper from '../AvatarWrapper/AvatarWrapper.vue'
-import { ATTENDEE, AVATAR } from '../../../../../constants.js'
+import { ATTENDEE, AVATAR } from '../../constants.js'
export default {
@@ -82,7 +82,7 @@ export default {
container: {
type: String,
required: true,
- }
+ },
},
setup() {
diff --git a/src/constants.js b/src/constants.js
index 109b3a0c6e3..9a40b0c2f3f 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -225,6 +225,23 @@ export const FLOW = {
},
}
+export const POLL = {
+ STATUS: {
+ OPEN: 0,
+ CLOSED: 1,
+ },
+
+ MODE: {
+ PUBLIC: 0,
+ HIDDEN: 1,
+ },
+
+ ANSWER_TYPE: {
+ MULTIPLE: 0,
+ SINGLE: 1,
+ },
+}
+
export const PRIVACY = {
PUBLIC: 0,
PRIVATE: 1,
diff --git a/src/store/messagesStore.js b/src/store/messagesStore.js
index 91962c6b603..4d4d25924fe 100644
--- a/src/store/messagesStore.js
+++ b/src/store/messagesStore.js
@@ -599,6 +599,9 @@ const actions = {
if (message.messageParameters?.object || message.messageParameters?.file) {
// Handle voice messages, shares with single file, polls, deck cards, e.t.c
sharedItemsStore.addSharedItemFromMessage(token, message)
+ if (message.messageParameters?.object?.type === 'talk-poll') {
+ EventBus.$emit('talk:poll-added', { token, message })
+ }
} else if (Object.keys(message.messageParameters).some(key => key.startsWith('file'))) {
// Handle shares with multiple files
}
diff --git a/src/store/pollStore.js b/src/store/pollStore.js
index 674d68003e8..22cd458d082 100644
--- a/src/store/pollStore.js
+++ b/src/store/pollStore.js
@@ -22,18 +22,29 @@
import debounce from 'debounce'
import Vue from 'vue'
-import { showError } from '@nextcloud/dialogs'
+import { showError, showInfo, TOAST_PERMANENT_TIMEOUT } from '@nextcloud/dialogs'
import pollService from '../services/pollService.js'
const state = {
polls: {},
+ pollDebounceFunctions: {},
+ activePoll: null,
+ pollToastsQueue: {},
}
const getters = {
getPoll: (state) => (token, id) => {
return state.polls?.[token]?.[id]
},
+
+ activePoll: (state) => {
+ return state.activePoll
+ },
+
+ getNewPolls: (state) => {
+ return state.pollToastsQueue
+ },
}
const mutations = {
@@ -44,15 +55,40 @@ const mutations = {
Vue.set(state.polls[token], poll.id, poll)
},
+ setActivePoll(state, { token, pollId, name }) {
+ Vue.set(state, 'activePoll', { token, id: pollId, name })
+ },
+
+ removeActivePoll(state) {
+ if (state.activePoll) {
+ Vue.set(state, 'activePoll', null)
+ }
+ },
+
+ addPollToast(state, { pollId, toast }) {
+ Vue.set(state.pollToastsQueue, pollId, toast)
+ },
+
+ hidePollToast(state, id) {
+ if (state.pollToastsQueue[id]) {
+ state.pollToastsQueue[id].hideToast()
+ Vue.delete(state.pollToastsQueue, id)
+ }
+ },
+
+ hideAllPollToasts(state) {
+ for (const id in state.pollToastsQueue) {
+ state.pollToastsQueue[id].hideToast()
+ Vue.delete(state.pollToastsQueue, id)
+ }
+ },
+
// Add debounce function for getting the poll data
addDebounceGetPollDataFunction(state, { token, pollId, debounceGetPollDataFunction }) {
- if (!state.polls?.pollDebounceFunctions) {
- Vue.set(state.polls, 'pollDebounceFunctions', {})
+ if (!state.pollDebounceFunctions[token]) {
+ Vue.set(state.pollDebounceFunctions, token, {})
}
- if (!state.polls.pollDebounceFunctions?.[token]) {
- Vue.set(state.polls.pollDebounceFunctions, [token], {})
- }
- Vue.set(state.polls.pollDebounceFunctions[token], pollId, debounceGetPollDataFunction)
+ Vue.set(state.pollDebounceFunctions[token], pollId, debounceGetPollDataFunction)
},
}
@@ -85,7 +121,7 @@ const actions = {
debounceGetPollData(context, { token, pollId }) {
// Create debounce function for getting this particular poll data
// if it does not exist yet
- if (!context.state.polls?.pollDebounceFunctions?.[token]?.[pollId]) {
+ if (!context.state.pollDebounceFunctions[token]?.[pollId]) {
const debounceGetPollDataFunction = debounce(async () => {
await context.dispatch('getPollData', {
token,
@@ -100,7 +136,7 @@ const actions = {
})
}
// Call the debounce function for getting the poll data
- context.state.polls.pollDebounceFunctions[token][pollId]()
+ context.state.pollDebounceFunctions[token][pollId]()
},
async submitVote(context, { token, pollId, vote }) {
@@ -126,6 +162,41 @@ const actions = {
showError(t('spreed', 'An error occurred while ending the poll'))
}
},
+
+ setActivePoll(context, { token, pollId, name }) {
+ context.commit('setActivePoll', { token, pollId, name })
+ },
+
+ removeActivePoll(context) {
+ context.commit('removeActivePoll')
+ },
+
+ addPollToast(context, { token, message }) {
+ const pollId = message.messageParameters.object.id
+ const name = message.messageParameters.object.name
+
+ const toast = showInfo(t('spreed', 'Poll "{name}" was created by {user}. Click to vote', {
+ name,
+ user: message.actorDisplayName,
+ }), {
+ onClick: () => {
+ if (!context.state.activePoll) {
+ context.dispatch('setActivePoll', { token, pollId, name })
+ }
+ },
+ timeout: TOAST_PERMANENT_TIMEOUT,
+ })
+
+ context.commit('addPollToast', { pollId, toast })
+ },
+
+ hidePollToast(context, id) {
+ context.commit('hidePollToast', id)
+ },
+
+ hideAllPollToasts(context) {
+ context.commit('hideAllPollToasts')
+ },
}
export default { state, mutations, getters, actions }
diff --git a/src/views/FilesSidebarChatView.vue b/src/views/FilesSidebarChatView.vue
index 527ffb333f4..3bbaf4e19bd 100644
--- a/src/views/FilesSidebarChatView.vue
+++ b/src/views/FilesSidebarChatView.vue
@@ -23,6 +23,7 @@
@@ -30,6 +31,7 @@
import ChatView from '../components/ChatView.vue'
import MediaSettings from '../components/MediaSettings/MediaSettings.vue'
+import PollViewer from '../components/PollViewer/PollViewer.vue'
import CallButton from '../components/TopBar/CallButton.vue'
export default {
@@ -40,6 +42,7 @@ export default {
CallButton,
ChatView,
MediaSettings,
+ PollViewer,
},
data() {
diff --git a/src/views/MainView.vue b/src/views/MainView.vue
index 26a17e708be..3ac396f359f 100644
--- a/src/views/MainView.vue
+++ b/src/views/MainView.vue
@@ -9,6 +9,7 @@
+
@@ -17,6 +18,7 @@
import CallView from '../components/CallView/CallView.vue'
import ChatView from '../components/ChatView.vue'
import LobbyScreen from '../components/LobbyScreen.vue'
+import PollViewer from '../components/PollViewer/PollViewer.vue'
import TopBar from '../components/TopBar/TopBar.vue'
import TransitionWrapper from '../components/TransitionWrapper.vue'
@@ -25,10 +27,11 @@ import { useIsInCall } from '../composables/useIsInCall.js'
export default {
name: 'MainView',
components: {
+ CallView,
ChatView,
LobbyScreen,
+ PollViewer,
TopBar,
- CallView,
TransitionWrapper,
},