Render poll in timeline
This commit is contained in:
parent
a3cbe5f512
commit
93aedd6fd7
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="poll-container">
|
<div class="poll-form">
|
||||||
<hr />
|
<hr />
|
||||||
<div class="poll-option"
|
<div class="poll-option"
|
||||||
v-for="(option, index) in options"
|
v-for="(option, index) in options"
|
||||||
|
@ -9,7 +9,8 @@
|
||||||
class="poll-option-input"
|
class="poll-option-input"
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="$t('polls.option')"
|
:placeholder="$t('polls.option')"
|
||||||
v-model="options[index]" />
|
@input="onUpdateOption($event, index)"
|
||||||
|
:value="option" />
|
||||||
</div>
|
</div>
|
||||||
<div class="icon-container">
|
<div class="icon-container">
|
||||||
<i class="icon-cancel" @click="onDeleteOption(index)"></i>
|
<i class="icon-cancel" @click="onDeleteOption(index)"></i>
|
||||||
|
@ -28,34 +29,36 @@
|
||||||
const maxOptions = 10
|
const maxOptions = 10
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PollContainer',
|
name: 'PollForm',
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
options: ['', '']
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
optionsLength: function () {
|
optionsLength: function () {
|
||||||
return this.$data.options.length
|
return this.$store.state.poll.pollOptions.length
|
||||||
|
},
|
||||||
|
options: function () {
|
||||||
|
return this.$store.state.poll.pollOptions
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onAddOption () {
|
onAddOption () {
|
||||||
if (this.optionsLength < maxOptions) {
|
if (this.optionsLength < maxOptions) {
|
||||||
this.$data.options.push('')
|
this.$store.commit('addPollOption', '')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDeleteOption (index) {
|
onDeleteOption (index) {
|
||||||
|
console.log(index)
|
||||||
if (this.optionsLength > 1) {
|
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>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.poll-container {
|
.poll-form {
|
||||||
padding: 0 0.5em 0.6em;
|
padding: 0 0.5em 0.6em;
|
||||||
hr {
|
hr {
|
||||||
margin: 0 0 0.8em;
|
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 MediaUpload from '../media_upload/media_upload.vue'
|
||||||
import ScopeSelector from '../scope_selector/scope_selector.vue'
|
import ScopeSelector from '../scope_selector/scope_selector.vue'
|
||||||
import EmojiInput from '../emoji-input/emoji-input.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 PollIcon from '../poll/poll_icon/poll_icon.vue'
|
||||||
import fileTypeService from '../../services/file_type/file_type.service.js'
|
import fileTypeService from '../../services/file_type/file_type.service.js'
|
||||||
import Completion from '../../services/completion/completion.js'
|
import Completion from '../../services/completion/completion.js'
|
||||||
|
@ -34,7 +34,7 @@ const PostStatusForm = {
|
||||||
components: {
|
components: {
|
||||||
MediaUpload,
|
MediaUpload,
|
||||||
EmojiInput,
|
EmojiInput,
|
||||||
PollContainer,
|
PollForm,
|
||||||
PollIcon,
|
PollIcon,
|
||||||
ScopeSelector
|
ScopeSelector
|
||||||
},
|
},
|
||||||
|
@ -68,6 +68,8 @@ const PostStatusForm = {
|
||||||
? this.$store.state.instance.postContentType
|
? this.$store.state.instance.postContentType
|
||||||
: this.$store.state.config.postContentType
|
: this.$store.state.config.postContentType
|
||||||
|
|
||||||
|
const pollOptions = this.$store.state.poll.pollOptions || []
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dropFiles: [],
|
dropFiles: [],
|
||||||
submitDisabled: false,
|
submitDisabled: false,
|
||||||
|
@ -79,6 +81,7 @@ const PostStatusForm = {
|
||||||
status: statusText,
|
status: statusText,
|
||||||
nsfw: false,
|
nsfw: false,
|
||||||
files: [],
|
files: [],
|
||||||
|
pollOptions,
|
||||||
visibility: scope,
|
visibility: scope,
|
||||||
contentType
|
contentType
|
||||||
},
|
},
|
||||||
|
@ -257,6 +260,7 @@ const PostStatusForm = {
|
||||||
visibility: newStatus.visibility,
|
visibility: newStatus.visibility,
|
||||||
sensitive: newStatus.nsfw,
|
sensitive: newStatus.nsfw,
|
||||||
media: newStatus.files,
|
media: newStatus.files,
|
||||||
|
pollOptions: newStatus.pollOptions,
|
||||||
store: this.$store,
|
store: this.$store,
|
||||||
inReplyToStatusId: this.replyTo,
|
inReplyToStatusId: this.replyTo,
|
||||||
contentType: newStatus.contentType
|
contentType: newStatus.contentType
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<poll-container />
|
<poll-form />
|
||||||
<div class='form-bottom'>
|
<div class='form-bottom'>
|
||||||
<media-upload ref="mediaUpload" @uploading="disableSubmit" @uploaded="addMediaFile" @upload-failed="uploadFailed" :drop-files="dropFiles"></media-upload>
|
<media-upload ref="mediaUpload" @uploading="disableSubmit" @uploaded="addMediaFile" @upload-failed="uploadFailed" :drop-files="dropFiles"></media-upload>
|
||||||
<poll-icon />
|
<poll-icon />
|
||||||
|
|
|
@ -2,6 +2,7 @@ import Attachment from '../attachment/attachment.vue'
|
||||||
import FavoriteButton from '../favorite_button/favorite_button.vue'
|
import FavoriteButton from '../favorite_button/favorite_button.vue'
|
||||||
import RetweetButton from '../retweet_button/retweet_button.vue'
|
import RetweetButton from '../retweet_button/retweet_button.vue'
|
||||||
import DeleteButton from '../delete_button/delete_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 PostStatusForm from '../post_status_form/post_status_form.vue'
|
||||||
import UserCard from '../user_card/user_card.vue'
|
import UserCard from '../user_card/user_card.vue'
|
||||||
import UserAvatar from '../user_avatar/user_avatar.vue'
|
import UserAvatar from '../user_avatar/user_avatar.vue'
|
||||||
|
@ -265,6 +266,7 @@ const Status = {
|
||||||
RetweetButton,
|
RetweetButton,
|
||||||
DeleteButton,
|
DeleteButton,
|
||||||
PostStatusForm,
|
PostStatusForm,
|
||||||
|
PollStatus,
|
||||||
UserCard,
|
UserCard,
|
||||||
UserAvatar,
|
UserAvatar,
|
||||||
Gallery,
|
Gallery,
|
||||||
|
|
|
@ -110,6 +110,10 @@
|
||||||
<a v-if="showingMore" href="#" class="status-unhider" @click.prevent="toggleShowMore">{{$t("general.show_less")}}</a>
|
<a v-if="showingMore" href="#" class="status-unhider" @click.prevent="toggleShowMore">{{$t("general.show_less")}}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="status.poll.votes">
|
||||||
|
<poll-status :poll="status.poll" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="status.attachments && (!hideSubjectStatus || showingLongSubject)" class="attachments media-body">
|
<div v-if="status.attachments && (!hideSubjectStatus || showingLongSubject)" class="attachments media-body">
|
||||||
<attachment
|
<attachment
|
||||||
class="non-gallery"
|
class="non-gallery"
|
||||||
|
|
|
@ -70,7 +70,8 @@
|
||||||
},
|
},
|
||||||
"polls": {
|
"polls": {
|
||||||
"add_option": "Add Option",
|
"add_option": "Add Option",
|
||||||
"option": "Option"
|
"option": "Option",
|
||||||
|
"votes": "votes"
|
||||||
},
|
},
|
||||||
"post_status": {
|
"post_status": {
|
||||||
"new_status": "Post new status",
|
"new_status": "Post new status",
|
||||||
|
|
|
@ -12,6 +12,7 @@ import chatModule from './modules/chat.js'
|
||||||
import oauthModule from './modules/oauth.js'
|
import oauthModule from './modules/oauth.js'
|
||||||
import mediaViewerModule from './modules/media_viewer.js'
|
import mediaViewerModule from './modules/media_viewer.js'
|
||||||
import oauthTokensModule from './modules/oauth_tokens.js'
|
import oauthTokensModule from './modules/oauth_tokens.js'
|
||||||
|
import pollModule from './modules/poll.js'
|
||||||
|
|
||||||
import VueTimeago from 'vue-timeago'
|
import VueTimeago from 'vue-timeago'
|
||||||
import VueI18n from 'vue-i18n'
|
import VueI18n from 'vue-i18n'
|
||||||
|
@ -68,7 +69,8 @@ const persistedStateOptions = {
|
||||||
chat: chatModule,
|
chat: chatModule,
|
||||||
oauth: oauthModule,
|
oauth: oauthModule,
|
||||||
mediaViewer: mediaViewerModule,
|
mediaViewer: mediaViewerModule,
|
||||||
oauthTokens: oauthTokensModule
|
oauthTokens: oauthTokensModule,
|
||||||
|
poll: pollModule
|
||||||
},
|
},
|
||||||
plugins: [persistedState, pushNotifications],
|
plugins: [persistedState, pushNotifications],
|
||||||
strict: false // Socket modifies itself, let's ignore this for now.
|
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))
|
.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()
|
const form = new FormData()
|
||||||
|
|
||||||
form.append('status', status)
|
form.append('status', status)
|
||||||
|
@ -499,6 +499,9 @@ const postStatus = ({credentials, status, spoilerText, visibility, sensitive, me
|
||||||
mediaIds.forEach(val => {
|
mediaIds.forEach(val => {
|
||||||
form.append('media_ids[]', val)
|
form.append('media_ids[]', val)
|
||||||
})
|
})
|
||||||
|
pollOptions.forEach(val => {
|
||||||
|
form.append('poll_options[]', val)
|
||||||
|
})
|
||||||
if (inReplyToStatusId) {
|
if (inReplyToStatusId) {
|
||||||
form.append('in_reply_to_id', 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.summary_html = addEmojis(data.spoiler_text, data.emojis)
|
||||||
output.external_url = data.url
|
output.external_url = data.url
|
||||||
|
|
||||||
|
output.poll = data.poll
|
||||||
|
|
||||||
// output.is_local = ??? missing
|
// output.is_local = ??? missing
|
||||||
} else {
|
} else {
|
||||||
output.favorited = data.favorited
|
output.favorited = data.favorited
|
||||||
|
|
|
@ -1,10 +1,19 @@
|
||||||
import { map } from 'lodash'
|
import { map } from 'lodash'
|
||||||
import apiService from '../api/api.service.js'
|
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')
|
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) => {
|
.then((data) => {
|
||||||
if (!data.error) {
|
if (!data.error) {
|
||||||
store.dispatch('addNewStatuses', {
|
store.dispatch('addNewStatuses', {
|
||||||
|
|
Loading…
Reference in New Issue