Render poll in timeline
This commit is contained in:
parent
a3cbe5f512
commit
93aedd6fd7
|
@ -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;
|
|
@ -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> ·
|
||||
</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>
|
|
@ -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
|
||||
|
|
|
@ -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 />
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -70,7 +70,8 @@
|
|||
},
|
||||
"polls": {
|
||||
"add_option": "Add Option",
|
||||
"option": "Option"
|
||||
"option": "Option",
|
||||
"votes": "votes"
|
||||
},
|
||||
"post_status": {
|
||||
"new_status": "Post new status",
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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', {
|
||||
|
|
Loading…
Reference in New Issue