Render poll in timeline

This commit is contained in:
Maxim Filippov 2019-04-04 02:25:16 +03:00
parent a3cbe5f512
commit 93aedd6fd7
12 changed files with 128 additions and 20 deletions

View File

@ -1,5 +1,5 @@
<template>
<div class="poll-container">
<div class="poll-form">
<hr />
<div class="poll-option"
v-for="(option, index) in options"
@ -9,7 +9,8 @@
class="poll-option-input"
type="text"
:placeholder="$t('polls.option')"
v-model="options[index]" />
@input="onUpdateOption($event, index)"
:value="option" />
</div>
<div class="icon-container">
<i class="icon-cancel" @click="onDeleteOption(index)"></i>
@ -28,34 +29,36 @@
const maxOptions = 10
export default {
name: 'PollContainer',
data () {
return {
options: ['', '']
}
},
name: 'PollForm',
computed: {
optionsLength: function () {
return this.$data.options.length
return this.$store.state.poll.pollOptions.length
},
options: function () {
return this.$store.state.poll.pollOptions
}
},
methods: {
onAddOption () {
if (this.optionsLength < maxOptions) {
this.$data.options.push('')
this.$store.commit('addPollOption', '')
}
},
onDeleteOption (index) {
console.log(index)
if (this.optionsLength > 1) {
this.$data.options.splice(index, 1)
this.$store.commit('deletePollOption', index)
}
},
onUpdateOption (e, index) {
this.$store.commit('updatePollOption', { index, option: e.target.value })
}
}
}
</script>
<style lang="scss">
.poll-container {
.poll-form {
padding: 0 0.5em 0.6em;
hr {
margin: 0 0 0.8em;

View File

@ -0,0 +1,60 @@
<template>
<div class="poll-status">
<div class="votes">
<div
class="poll-option"
v-for="(pollOption, index) in poll.votes"
:key="index">
<div class="col">{{percentageForOption(pollOption.count)}}%</div>
<div class="col">{{pollOption.name}}</div>
<div class="col"><progress :max="totalVotesCount" :value="pollOption.count"></progress></div>
</div>
</div>
<footer>
<div class="refresh">
<a href="#">Refresh</a>&nbsp;·&nbsp;
</div>
<div class="total">
{{totalVotesCount}} {{ $t("polls.votes") }}
</div>
</footer>
</div>
</template>
<script>
export default {
name: 'PollStatus',
props: ['poll'],
computed: {
totalVotesCount () {
return this.poll.votes.reduce((acc, vote) => { return acc + vote.count }, 0)
}
},
methods: {
percentageForOption: function (count) {
return count / this.totalVotesCount * 100
}
}
}
</script>
<style lang="scss">
.poll-status {
margin: 0.7em 0;
.votes {
display: table;
width: 100%;
margin: 0 0 0.5em;
}
.poll-option {
display: table-row;
.col {
display: table-cell;
padding: 0.7em 0.5em;
}
}
footer {
display: flex;
}
}
</style>

View File

@ -2,7 +2,7 @@ import statusPoster from '../../services/status_poster/status_poster.service.js'
import MediaUpload from '../media_upload/media_upload.vue'
import ScopeSelector from '../scope_selector/scope_selector.vue'
import EmojiInput from '../emoji-input/emoji-input.vue'
import PollContainer from '../poll/poll_container/poll_container.vue'
import PollForm from '../poll/poll_form/poll_form.vue'
import PollIcon from '../poll/poll_icon/poll_icon.vue'
import fileTypeService from '../../services/file_type/file_type.service.js'
import Completion from '../../services/completion/completion.js'
@ -34,7 +34,7 @@ const PostStatusForm = {
components: {
MediaUpload,
EmojiInput,
PollContainer,
PollForm,
PollIcon,
ScopeSelector
},
@ -68,6 +68,8 @@ const PostStatusForm = {
? this.$store.state.instance.postContentType
: this.$store.state.config.postContentType
const pollOptions = this.$store.state.poll.pollOptions || []
return {
dropFiles: [],
submitDisabled: false,
@ -79,6 +81,7 @@ const PostStatusForm = {
status: statusText,
nsfw: false,
files: [],
pollOptions,
visibility: scope,
contentType
},
@ -257,6 +260,7 @@ const PostStatusForm = {
visibility: newStatus.visibility,
sensitive: newStatus.nsfw,
media: newStatus.files,
pollOptions: newStatus.pollOptions,
store: this.$store,
inReplyToStatusId: this.replyTo,
contentType: newStatus.contentType

View File

@ -71,7 +71,7 @@
</div>
</div>
</div>
<poll-container />
<poll-form />
<div class='form-bottom'>
<media-upload ref="mediaUpload" @uploading="disableSubmit" @uploaded="addMediaFile" @upload-failed="uploadFailed" :drop-files="dropFiles"></media-upload>
<poll-icon />

View File

@ -2,6 +2,7 @@ import Attachment from '../attachment/attachment.vue'
import FavoriteButton from '../favorite_button/favorite_button.vue'
import RetweetButton from '../retweet_button/retweet_button.vue'
import DeleteButton from '../delete_button/delete_button.vue'
import PollStatus from '../poll/poll_status/poll_status.vue'
import PostStatusForm from '../post_status_form/post_status_form.vue'
import UserCard from '../user_card/user_card.vue'
import UserAvatar from '../user_avatar/user_avatar.vue'
@ -265,6 +266,7 @@ const Status = {
RetweetButton,
DeleteButton,
PostStatusForm,
PollStatus,
UserCard,
UserAvatar,
Gallery,

View File

@ -110,6 +110,10 @@
<a v-if="showingMore" href="#" class="status-unhider" @click.prevent="toggleShowMore">{{$t("general.show_less")}}</a>
</div>
<div v-if="status.poll.votes">
<poll-status :poll="status.poll" />
</div>
<div v-if="status.attachments && (!hideSubjectStatus || showingLongSubject)" class="attachments media-body">
<attachment
class="non-gallery"

View File

@ -70,7 +70,8 @@
},
"polls": {
"add_option": "Add Option",
"option": "Option"
"option": "Option",
"votes": "votes"
},
"post_status": {
"new_status": "Post new status",

View File

@ -12,6 +12,7 @@ import chatModule from './modules/chat.js'
import oauthModule from './modules/oauth.js'
import mediaViewerModule from './modules/media_viewer.js'
import oauthTokensModule from './modules/oauth_tokens.js'
import pollModule from './modules/poll.js'
import VueTimeago from 'vue-timeago'
import VueI18n from 'vue-i18n'
@ -68,7 +69,8 @@ const persistedStateOptions = {
chat: chatModule,
oauth: oauthModule,
mediaViewer: mediaViewerModule,
oauthTokens: oauthTokensModule
oauthTokens: oauthTokensModule,
poll: pollModule
},
plugins: [persistedState, pushNotifications],
strict: false // Socket modifies itself, let's ignore this for now.

18
src/modules/poll.js Normal file
View File

@ -0,0 +1,18 @@
const poll = {
state: {
pollOptions: ['', '']
},
mutations: {
addPollOption (state, option) {
state.pollOptions.push(option)
},
updatePollOption (state, { index, option }) {
state.pollOptions[index] = option
},
deletePollOption (state, index) {
state.pollOptions.splice(index, 1)
}
}
}
export default poll

View File

@ -487,7 +487,7 @@ const unretweet = ({ id, credentials }) => {
.then((data) => parseStatus(data))
}
const postStatus = ({credentials, status, spoilerText, visibility, sensitive, mediaIds = [], inReplyToStatusId, contentType}) => {
const postStatus = ({credentials, status, spoilerText, visibility, sensitive, pollOptions = [], mediaIds = [], inReplyToStatusId, contentType}) => {
const form = new FormData()
form.append('status', status)
@ -499,6 +499,9 @@ const postStatus = ({credentials, status, spoilerText, visibility, sensitive, me
mediaIds.forEach(val => {
form.append('media_ids[]', val)
})
pollOptions.forEach(val => {
form.append('poll_options[]', val)
})
if (inReplyToStatusId) {
form.append('in_reply_to_id', inReplyToStatusId)
}

View File

@ -193,6 +193,8 @@ export const parseStatus = (data) => {
output.summary_html = addEmojis(data.spoiler_text, data.emojis)
output.external_url = data.url
output.poll = data.poll
// output.is_local = ??? missing
} else {
output.favorited = data.favorited

View File

@ -1,10 +1,19 @@
import { map } from 'lodash'
import apiService from '../api/api.service.js'
const postStatus = ({ store, status, spoilerText, visibility, sensitive, media = [], inReplyToStatusId = undefined, contentType = 'text/plain' }) => {
const postStatus = ({ store, status, spoilerText, visibility, sensitive, pollOptions = [], media = [], inReplyToStatusId = undefined, contentType = 'text/plain' }) => {
const mediaIds = map(media, 'id')
return apiService.postStatus({credentials: store.state.users.currentUser.credentials, status, spoilerText, visibility, sensitive, mediaIds, inReplyToStatusId, contentType})
return apiService.postStatus({
credentials: store.state.users.currentUser.credentials,
status,
spoilerText,
visibility,
sensitive,
mediaIds,
inReplyToStatusId,
contentType,
pollOptions})
.then((data) => {
if (!data.error) {
store.dispatch('addNewStatuses', {