зеркало из https://github.com/nextcloud/spreed.git
Merge pull request #7761 from nextcloud/feature/polls-follow-up
Polls cleanup
This commit is contained in:
Коммит
3a94a969f9
|
@ -410,6 +410,7 @@ class SystemMessage {
|
|||
}
|
||||
} elseif ($message === 'object_shared') {
|
||||
$parsedParameters['object'] = $parameters['metaData'];
|
||||
$parsedParameters['object']['id'] = (string) $parsedParameters['object']['id'];
|
||||
$parsedMessage = '{object}';
|
||||
|
||||
if (isset($parsedParameters['object']['type'])
|
||||
|
@ -494,12 +495,14 @@ class SystemMessage {
|
|||
}
|
||||
} elseif ($message === 'poll_closed') {
|
||||
$parsedParameters['poll'] = $parameters['poll'];
|
||||
$parsedParameters['poll']['id'] = (string) $parsedParameters['poll']['id'];
|
||||
$parsedMessage = $this->l->t('{actor} closed the poll {poll}');
|
||||
if ($currentUserIsActor) {
|
||||
$parsedMessage = $this->l->t('You closed the poll {poll}');
|
||||
}
|
||||
} elseif ($message === 'poll_voted') {
|
||||
$parsedParameters['poll'] = $parameters['poll'];
|
||||
$parsedParameters['poll']['id'] = (string) $parsedParameters['poll']['id'];
|
||||
$parsedMessage = $this->l->t('Someone voted on the poll {poll}');
|
||||
unset($parsedParameters['actor']);
|
||||
} else {
|
||||
|
|
|
@ -55,6 +55,14 @@ the main body of the message as well as a quote.
|
|||
<RichText :text="message" :arguments="richParameters" :autolink="true" />
|
||||
<CallButton />
|
||||
</div>
|
||||
<div v-else-if="showResultsButton" class="message-body__main__text system-message">
|
||||
<RichText :text="message" :arguments="richParameters" :autolink="true" />
|
||||
<!-- Displays only the "see results" button with the results modal -->
|
||||
<Poll :id="messageParameters.poll.id"
|
||||
:poll-name="messageParameters.poll.name"
|
||||
:token="token"
|
||||
:show-as-button="true" />
|
||||
</div>
|
||||
<div v-else-if="isDeletedMessage" class="message-body__main__text deleted-message">
|
||||
<RichText :text="message" :arguments="richParameters" :autolink="true" />
|
||||
</div>
|
||||
|
@ -220,6 +228,7 @@ export default {
|
|||
NcEmojiPicker,
|
||||
EmoticonOutline,
|
||||
NcPopover,
|
||||
Poll,
|
||||
},
|
||||
|
||||
mixins: [
|
||||
|
@ -457,6 +466,10 @@ export default {
|
|||
&& !this.isInCall
|
||||
},
|
||||
|
||||
showResultsButton() {
|
||||
return this.systemMessage === 'poll_closed'
|
||||
},
|
||||
|
||||
isSingleEmoji() {
|
||||
const regex = emojiRegex()
|
||||
let match
|
||||
|
@ -503,7 +516,7 @@ export default {
|
|||
component: Location,
|
||||
props: this.messageParameters[p],
|
||||
}
|
||||
} else if (type === 'talk-poll') {
|
||||
} else if (type === 'talk-poll' && this.systemMessage !== 'poll_closed') {
|
||||
const props = Object.assign({}, this.messageParameters[p])
|
||||
// Add the token to the component props
|
||||
props.token = this.token
|
||||
|
|
|
@ -21,11 +21,13 @@
|
|||
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<a v-observe-visibility="getPollData"
|
||||
<!-- Poll card -->
|
||||
<a v-if="!showAsButton"
|
||||
v-observe-visibility="getPollData"
|
||||
:aria-label="t('spreed', 'Poll')"
|
||||
class="poll"
|
||||
role="button"
|
||||
@click="showModal = true">
|
||||
@click="openPoll">
|
||||
<div class="poll__header">
|
||||
<PollIcon :size="20" />
|
||||
<p>
|
||||
|
@ -33,14 +35,22 @@
|
|||
</p>
|
||||
</div>
|
||||
<div class="poll__footer">
|
||||
{{ t('spreed', 'Poll ・ Click to vote') }}
|
||||
{{ pollFooterText }}
|
||||
</div>
|
||||
|
||||
</a>
|
||||
|
||||
<!-- Poll results button in system message -->
|
||||
<div v-else class="poll-closed">
|
||||
<NcButton type="secondary" @click="openPoll">
|
||||
{{ t('spreed', 'See results') }}
|
||||
</NcButton>
|
||||
</div>
|
||||
|
||||
<!-- voting and results dialog -->
|
||||
<NcModal v-if="vote !== undefined && showModal"
|
||||
size="small"
|
||||
@close="showModal = false">
|
||||
@close="dismissModal">
|
||||
<div class="poll__modal">
|
||||
<!-- First screen, displayed while voting-->
|
||||
<template v-if="modalPage === 'voting'">
|
||||
|
@ -78,12 +88,15 @@
|
|||
</div>
|
||||
|
||||
<div class="poll__modal-actions">
|
||||
<NcButton type="tertiary" @click="dismissModal">
|
||||
{{ t('spreed', 'Dismiss') }}
|
||||
</NcButton>
|
||||
<!-- create poll button-->
|
||||
<!-- Submit vote button-->
|
||||
<NcButton type="primary" :disabled="!canSubmitVote" @click="submitVote">
|
||||
{{ t('spreed', 'Submit') }}
|
||||
{{ t('spreed', 'Submit vote') }}
|
||||
</NcButton>
|
||||
<!-- End poll button-->
|
||||
<NcButton v-if="canEndPoll"
|
||||
type="error"
|
||||
@click="endPoll">
|
||||
{{ t('spreed', 'End poll') }}
|
||||
</NcButton>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -98,7 +111,12 @@
|
|||
</h2>
|
||||
</div>
|
||||
<div class="poll__summary">
|
||||
{{ n('spreed', 'Poll results • %n vote', 'Poll results • %n votes', votersNumber) }}
|
||||
<template v-if="currentUserIsPollCreator || currentUserIsModerator || pollIsPublic">
|
||||
{{ n('spreed', 'Poll results • %n vote', 'Poll results • %n votes', votersNumber) }}
|
||||
</template>
|
||||
<template v-else-if="selfHasVoted">
|
||||
{{ t('spreed', 'Poll ・ You voted') }}
|
||||
</template>
|
||||
</div>
|
||||
<div class="results__options">
|
||||
<div v-for="(option, index) in options"
|
||||
|
@ -112,20 +130,26 @@
|
|||
{{ getVotePercentage(index) + '%' }}
|
||||
</p>
|
||||
</div>
|
||||
<NcProgressBar :value="getVotePercentage(index)" size="medium" />
|
||||
<p v-if="selfHasVotedOption(index)" class="results__option-subtitle">
|
||||
{{ t('spreed','You voted') }}
|
||||
</p>
|
||||
<NcProgressBar class="results__option-progress"
|
||||
:value="getVotePercentage(index)"
|
||||
size="medium" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="pollIsOpen"
|
||||
class="poll__modal-actions">
|
||||
<NcButton type="tertiary"
|
||||
<!-- Vote again-->
|
||||
<NcButton type="secondary"
|
||||
@click="modalPage = 'voting'">
|
||||
{{ t('spreed', 'Back') }}
|
||||
{{ t('spreed', 'Change your vote') }}
|
||||
</NcButton>
|
||||
<!-- create poll button-->
|
||||
<NcButton v-if="canClosePoll"
|
||||
<!-- End poll button-->
|
||||
<NcButton v-if="canEndPoll"
|
||||
type="error"
|
||||
@click="closePoll">
|
||||
{{ t('spreed', 'Close poll') }}
|
||||
@click="endPoll">
|
||||
{{ t('spreed', 'End poll') }}
|
||||
</NcButton>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -162,7 +186,7 @@ export default {
|
|||
},
|
||||
|
||||
id: {
|
||||
type: Number,
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
|
@ -170,6 +194,11 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
showAsButton: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
|
@ -207,6 +236,19 @@ export default {
|
|||
},
|
||||
|
||||
selfHasVoted() {
|
||||
if (this.pollLoaded) {
|
||||
if (typeof this.votedSelf === 'object') {
|
||||
return this.votedSelf.length > 0
|
||||
} else {
|
||||
return !!this.votedSelf
|
||||
}
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
},
|
||||
|
||||
// The actual vote of the user as returned from the server
|
||||
votedSelf() {
|
||||
return this.pollLoaded ? this.poll.votedSelf : undefined
|
||||
},
|
||||
|
||||
|
@ -214,6 +256,10 @@ export default {
|
|||
return this.pollLoaded ? this.poll.resultMode : undefined
|
||||
},
|
||||
|
||||
pollIsPublic() {
|
||||
return this.resultMode === 0
|
||||
},
|
||||
|
||||
status() {
|
||||
return this.pollLoaded ? this.poll.status : undefined
|
||||
},
|
||||
|
@ -222,12 +268,24 @@ export default {
|
|||
return this.status === 0
|
||||
},
|
||||
|
||||
pollIsClosed() {
|
||||
return this.status === 1
|
||||
},
|
||||
|
||||
checkboxRadioSwitchType() {
|
||||
return this.poll.maxVotes === 0 ? 'checkbox' : 'radio'
|
||||
if (this.pollLoaded) {
|
||||
return this.poll.maxVotes === 0 ? 'checkbox' : 'radio'
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
},
|
||||
|
||||
canSubmitVote() {
|
||||
return this.vote !== undefined && this.vote !== '' && this.vote !== []
|
||||
if (typeof this.vote === 'object') {
|
||||
return this.vote.length > 0
|
||||
} else {
|
||||
return this.vote !== undefined && this.vote !== ''
|
||||
}
|
||||
},
|
||||
|
||||
getVotePercentage() {
|
||||
|
@ -235,7 +293,7 @@ export default {
|
|||
if (this.pollVotes[`option-${index}`] === undefined) {
|
||||
return 0
|
||||
}
|
||||
return this.pollVotes[`option-${index}`] / this.votersNumber * 100
|
||||
return parseInt(this.pollVotes[`option-${index}`] / this.votersNumber * 100)
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -269,17 +327,36 @@ export default {
|
|||
return [PARTICIPANT.TYPE.OWNER, PARTICIPANT.TYPE.MODERATOR, PARTICIPANT.TYPE.GUEST_MODERATOR].indexOf(this.participantType) !== -1
|
||||
},
|
||||
|
||||
canClosePoll() {
|
||||
return this.currentUserIsPollCreator || this.currentUserIsModerator
|
||||
canEndPoll() {
|
||||
return (this.currentUserIsPollCreator || this.currentUserIsModerator) && this.pollIsOpen
|
||||
},
|
||||
|
||||
pollFooterText() {
|
||||
if (this.pollIsOpen) {
|
||||
return this.selfHasVoted ? t('spreed', 'Poll ・ You voted') : t('spreed', 'Poll ・ Click to vote')
|
||||
} else if (this.pollIsClosed) {
|
||||
return t('spreed', 'Poll ・ Closed')
|
||||
}
|
||||
return ''
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
|
||||
pollLoaded() {
|
||||
this.setComponentData()
|
||||
this.setVoteData()
|
||||
},
|
||||
|
||||
modalPage(value) {
|
||||
if (value === 'voting') {
|
||||
this.setVoteData()
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.setVoteData()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -292,13 +369,27 @@ export default {
|
|||
}
|
||||
},
|
||||
|
||||
setComponentData() {
|
||||
setVoteData() {
|
||||
if (this.checkboxRadioSwitchType === 'radio') {
|
||||
this.vote = ''
|
||||
if (this.selfHasVoted) {
|
||||
this.vote = this.votedSelf[0].toString()
|
||||
}
|
||||
} else {
|
||||
this.vote = []
|
||||
if (this.selfHasVoted) {
|
||||
this.vote = this.votedSelf.map(element => element.toString())
|
||||
}
|
||||
}
|
||||
this.pollIsOpen ? this.modalPage = 'voting' : this.modalPage = 'results'
|
||||
},
|
||||
|
||||
openPoll() {
|
||||
if (this.selfHasVoted || this.pollIsClosed) {
|
||||
this.modalPage = 'results'
|
||||
} else {
|
||||
this.modalPage = 'voting'
|
||||
}
|
||||
this.showModal = true
|
||||
},
|
||||
|
||||
dismissModal() {
|
||||
|
@ -321,11 +412,20 @@ export default {
|
|||
this.modalPage = 'results'
|
||||
},
|
||||
|
||||
closePoll() {
|
||||
this.$store.dispatch('closePoll', {
|
||||
endPoll() {
|
||||
this.$store.dispatch('endPoll', {
|
||||
token: this.token,
|
||||
pollId: this.id,
|
||||
})
|
||||
this.modalPage = 'results'
|
||||
},
|
||||
|
||||
selfHasVotedOption(index) {
|
||||
if (this.votedSelf.includes(index)) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -354,10 +454,8 @@ export default {
|
|||
gap: 8px;
|
||||
white-space: normal;
|
||||
align-items: flex-start;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
padding: 0 0 8px 0;
|
||||
background-color: var(--color-main-background);
|
||||
word-wrap: anywhere;
|
||||
padding-top: 20px;
|
||||
|
||||
|
@ -374,7 +472,7 @@ export default {
|
|||
|
||||
&__modal {
|
||||
position: relative;
|
||||
padding: 0 20px;
|
||||
padding: 20px 20px 0 20px;
|
||||
}
|
||||
|
||||
&__modal-title {
|
||||
|
@ -393,7 +491,7 @@ export default {
|
|||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
gap: 8px;
|
||||
padding: 12px 0 0 0;
|
||||
background-color: var(--color-main-background);
|
||||
padding-bottom: 20px;
|
||||
|
@ -415,7 +513,13 @@ export default {
|
|||
.results__option {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
&-subtitle {
|
||||
color: var(--color-text-maxcontrast);
|
||||
}
|
||||
|
||||
&-progress {
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.results__option-title {
|
||||
|
@ -428,6 +532,12 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
.poll-closed {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
// Upstream
|
||||
::v-deep .checkbox-radio-switch {
|
||||
&__label {
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
</p>
|
||||
<PollOption v-for="option, index in pollOptions"
|
||||
:key="index"
|
||||
:ref="`pollOption${index}`"
|
||||
class="simple-polls-editor__option"
|
||||
:value.sync="pollOptions[index]"
|
||||
:placeholder="t('spreed', 'Answer {option}', {option: index + 1})"
|
||||
|
@ -123,6 +124,11 @@ export default {
|
|||
|
||||
addOption() {
|
||||
this.pollOptions.push('')
|
||||
this.$nextTick(() => {
|
||||
const indexOfNewPollOption = this.pollOptions.length - 1
|
||||
const refOfNewPollOption = `pollOption${indexOfNewPollOption}`
|
||||
this.$refs[refOfNewPollOption][0].$el.querySelector('.input-field__input').focus()
|
||||
})
|
||||
},
|
||||
|
||||
async createPoll() {
|
||||
|
|
|
@ -68,13 +68,13 @@ const pollService = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Closes the poll
|
||||
* Ends the poll
|
||||
*
|
||||
* @param {string} token The conversation token
|
||||
* @param {number} pollId ID of the poll
|
||||
* @return {object} The poll object
|
||||
*/
|
||||
async closePoll(token, pollId) {
|
||||
async endPoll(token, pollId) {
|
||||
return axios.delete(generateOcsUrl('apps/spreed/api/v1/poll/{token}/{pollId}', { token, pollId }))
|
||||
},
|
||||
}
|
||||
|
|
|
@ -422,7 +422,6 @@ const actions = {
|
|||
|| lastMessage.actorId === 'changelog')
|
||||
&& lastMessage.systemMessage !== 'reaction'
|
||||
&& lastMessage.systemMessage !== 'poll_voted'
|
||||
&& lastMessage.systemMessage !== 'poll_closed'
|
||||
&& lastMessage.systemMessage !== 'reaction_deleted'
|
||||
&& lastMessage.systemMessage !== 'reaction_revoked'
|
||||
&& lastMessage.systemMessage !== 'message_deleted'
|
||||
|
|
|
@ -146,7 +146,6 @@ const getters = {
|
|||
|| message.systemMessage === 'reaction_deleted'
|
||||
|| message.systemMessage === 'reaction_revoked'
|
||||
|| message.systemMessage === 'poll_voted'
|
||||
|| message.systemMessage === 'poll_closed'
|
||||
) {
|
||||
return false
|
||||
} else {
|
||||
|
@ -423,6 +422,13 @@ const actions = {
|
|||
})
|
||||
}
|
||||
|
||||
if (message.systemMessage === 'poll_closed') {
|
||||
context.dispatch('getPollData', {
|
||||
token: message.token,
|
||||
pollId: message.messageParameters.poll.id,
|
||||
})
|
||||
}
|
||||
|
||||
context.commit('addMessage', message)
|
||||
|
||||
if ((message.messageType === 'comment' && message.message === '{file}' && message.messageParameters?.file)
|
||||
|
|
|
@ -114,16 +114,16 @@ const actions = {
|
|||
}
|
||||
},
|
||||
|
||||
async closePoll(context, { token, pollId }) {
|
||||
console.debug('Closing poll')
|
||||
async endPoll(context, { token, pollId }) {
|
||||
console.debug('Ending poll')
|
||||
try {
|
||||
const response = await pollService.closePoll(token, pollId)
|
||||
const response = await pollService.endPoll(token, pollId)
|
||||
const poll = response.data.ocs.data
|
||||
context.dispatch('addPoll', { token, poll })
|
||||
console.debug('polldata', response)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
showError(t('spreed', 'An error occurred while closing the poll'))
|
||||
showError(t('spreed', 'An error occurred while ending the poll'))
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1934,7 +1934,7 @@ class FeatureContext implements Context, SnippetAcceptingContext {
|
|||
|
||||
$result = preg_match('/POLL_ID\(([^)]+)\)/', $expected[$i]['messageParameters'], $matches);
|
||||
if ($result) {
|
||||
$expected[$i]['messageParameters'] = str_replace($matches[0], self::$questionToPollId[$matches[1]], $expected[$i]['messageParameters']);
|
||||
$expected[$i]['messageParameters'] = str_replace($matches[0], '"' . self::$questionToPollId[$matches[1]] . '"', $expected[$i]['messageParameters']);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче