Merge pull request #963 from h3poteto/iss-874

refs #874 Add polls form in new toot modal
This commit is contained in:
AkiraFukushima 2019-07-18 09:46:51 +09:00 committed by GitHub
commit d6e4fc1c18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 344 additions and 56 deletions

View File

@ -186,12 +186,14 @@
"close_confirm": "Möchtest du wirklich schließen?",
"close_confirm_ok": "Schließen",
"close_confirm_cancel": "Abbrechen",
"add_image": "Bild hinzufügen",
"change_visibility": "Sichtbarkeit ändern",
"add_cw": "Inhalt hinzufügen Warnung",
"change_sensitive": "Ändern Sie empfindlich",
"pined_hashtag": "Pin the hashtag",
"description": "Für Menschen mit Sehbehinderung beschreiben"
"description": "Für Menschen mit Sehbehinderung beschreiben",
"footer": {
"add_image": "Bild hinzufügen",
"change_visibility": "Sichtbarkeit ändern",
"change_sensitive": "Ändern Sie empfindlich",
"add_cw": "Inhalt hinzufügen Warnung",
"pined_hashtag": "Pin the hashtag"
}
},
"jump": {
"jump_to": "Springe zu..."

View File

@ -27,6 +27,23 @@
"tag": "Hashtag"
},
"modals": {
"new_toot": {
"footer": {
"poll": "Add a poll"
},
"poll": {
"add_choice": "Add a choice",
"expires": {
"5_minutes": "5 minutes",
"30_minutes": "30 minutes",
"1_hour": "1 hour",
"6_hours": "6 hours",
"1_day": "1 day",
"3_days": "3 days",
"7_days": "7 days"
}
}
},
"shortcut": {
"h": "Switch the focus to left column",
"l": "Switch the focus to right column",

View File

@ -213,12 +213,27 @@
"close_confirm": "Are you sure you want to cancel the new toot?",
"close_confirm_ok": "OK",
"close_confirm_cancel": "Cancel",
"add_image": "Add images",
"change_visibility": "Change visibility",
"add_cw": "Add content warning",
"change_sensitive": "Change sensitive",
"pined_hashtag": "Pin the hashtag",
"description": "Describe for the visually impaired"
"description": "Describe for the visually impaired",
"footer": {
"add_image": "Add images",
"poll": "Add a poll",
"change_visibility": "Change visibility",
"change_sensitive": "Change sensitive",
"add_cw": "Add content warning",
"pined_hashtag": "Pin the hashtag"
},
"poll": {
"add_choice": "Add a choice",
"expires": {
"5_minutes": "5 minutes",
"30_minutes": "30 minutes",
"1_hour": "1 hour",
"6_hours": "6 hours",
"1_day": "1 day",
"3_days": "3 days",
"7_days": "7 days"
}
}
},
"jump": {
"jump_to": "Jump to..."

View File

@ -207,12 +207,14 @@
"close_confirm": "Êtes-vous certain de vouloir fermer ce nouveau pouet ?",
"close_confirm_ok": "Oui",
"close_confirm_cancel": "Annuler",
"add_image": "Ajouter une image",
"change_visibility": "Changer la visibilité",
"add_cw": "Ajouter un avertissement de contenu",
"change_sensitive": "Changer sensible",
"pined_hashtag": "Accrochez le hashtag",
"description": "Décrire pour les malvoyant·e·s"
"description": "Décrire pour les malvoyant·e·s",
"footer": {
"add_image": "Ajouter une image",
"change_visibility": "Changer la visibilité",
"change_sensitive": "Changer sensible",
"add_cw": "Ajouter un avertissement de contenu",
"pined_hashtag": "Accrochez le hashtag"
}
},
"jump": {
"jump_to": "Aller à..."

View File

@ -16,6 +16,25 @@
"accept": "Accept",
"reject": "Reject"
},
"modals": {
"new_toot": {
"footer": {
"poll": "Add a poll"
},
"poll": {
"add_choice": "Add a choice",
"expires": {
"5_minutes": "5 minutes",
"30_minutes": "30 minutes",
"1_hour": "1 hour",
"6_hours": "6 hours",
"1_day": "1 day",
"3_days": "3 days",
"7_days": "7 days"
}
}
}
},
"cards": {
"toot": {
"poll": {

View File

@ -210,12 +210,14 @@
"close_confirm": "Sei sicuro/a di voler cancellare il nuovo toot?",
"close_confirm_ok": "OK",
"close_confirm_cancel": "Annulla",
"add_image": "Aggiungi immagini",
"change_visibility": "Modifica visibilità",
"add_cw": "Aggiungi avviso sui contenuti",
"change_sensitive": "Modifica sensibilità",
"pined_hashtag": "Fissa l'hashtag",
"description": "Descrivi per i non vedenti"
"description": "Descrivi per i non vedenti",
"footer": {
"add_image": "Aggiungi immagini",
"change_visibility": "Modifica visibilità",
"change_sensitive": "Modifica sensibilità",
"add_cw": "Aggiungi avviso sui contenuti",
"pined_hashtag": "Fissa l'hashtag"
}
},
"jump": {
"jump_to": "Salta a..."

View File

@ -14,6 +14,25 @@
"accept": "Accept",
"reject": "Reject"
},
"modals": {
"new_toot": {
"footer": {
"poll": "Add a poll"
},
"poll": {
"add_choice": "Add a choice",
"expires": {
"5_minutes": "5 minutes",
"30_minutes": "30 minutes",
"1_hour": "1 hour",
"6_hours": "6 hours",
"1_day": "1 day",
"3_days": "3 days",
"7_days": "7 days"
}
}
}
},
"cards": {
"toot": {
"poll": {

View File

@ -212,12 +212,27 @@
"close_confirm": "本当に閉じますか?",
"close_confirm_ok": "閉じる",
"close_confirm_cancel": "キャンセル",
"add_image": "画像を添付",
"change_visibility": "プライバシー設定",
"add_cw": "閲覧注意を追加",
"change_sensitive": "メディアの閲覧注意設定",
"pined_hashtag": "ハッシュタグを固定する",
"description": "メディアの説明を追加"
"description": "メディアの説明を追加",
"footer": {
"add_image": "画像を添付",
"poll": "アンケートを追加",
"change_visibility": "プライバシー設定",
"change_sensitive": "メディアの閲覧注意設定",
"add_cw": "閲覧注意を追加",
"pined_hashtag": "ハッシュタグを固定する"
},
"poll": {
"add_choice": "選択肢を追加",
"expires": {
"5_minutes": "5分後",
"30_minutes": "30分後",
"1_hour": "1時間後",
"6_hours": "6時間後",
"1_day": "1日後",
"3_days": "3日後",
"7_days": "7日後"
}
}
},
"jump": {
"jump_to": "移動..."

View File

@ -200,13 +200,14 @@
"close_confirm": "툿 작성을 그만둘까요?",
"close_confirm_ok": "예",
"close_confirm_cancel": "취소",
"add_image": "이미지 추가",
"change_visibility": "공개 범위 변경",
"add_cw": "경고 문구 추가",
"change_sensitive": "민감한 미디어 설정",
"pined_hashtag": "해시태그고정",
"description": "시각장애인을 위한 설명",
"pinned": "고정 된 툿"
"footer": {
"add_image": "이미지 추가",
"change_visibility": "공개 범위 변경",
"change_sensitive": "민감한 미디어 설정",
"add_cw": "경고 문구 추가",
"pined_hashtag": "해시태그고정"
}
},
"jump": {
"jump_to": "이동"
@ -261,7 +262,8 @@
"reply": "답장하기",
"reblog": "부스트",
"fav": "즐겨찾기",
"detail": "툿 자세히 보기"
"detail": "툿 자세히 보기",
"pinned": "고정 된 툿"
}
},
"side_bar": {

View File

@ -25,6 +25,25 @@
"accept": "Accept",
"reject": "Reject"
},
"modals": {
"new_toot": {
"footer": {
"poll": "Add a poll"
},
"poll": {
"add_choice": "Add a choice",
"expires": {
"5_minutes": "5 minutes",
"30_minutes": "30 minutes",
"1_hour": "1 hour",
"6_hours": "6 hours",
"1_day": "1 day",
"3_days": "3 days",
"7_days": "7 days"
}
}
}
},
"cards": {
"toot": {
"poll": {

View File

@ -186,13 +186,14 @@
"close_confirm": "Czy na pewno chcesz porzucić tworzenie wpisu?",
"close_confirm_ok": "OK",
"close_confirm_cancel": "Anuluj",
"add_image": "Dodaj obraz",
"change_visibility": "Zmień widoczność",
"add_cw": "Dodaj ostrzeżenie zawartości",
"change_sensitive": "Zmień wrażliwy",
"pined_hashtag": "Pin the hashtag",
"description": "Wprowadź opis dla niewidomych i niedowidzących",
"pinned": "Przypięty wpis"
"fotter": {
"add_image": "Dodaj obraz",
"change_visibility": "Zmień widoczność",
"change_sensitive": "Zmień wrażliwy",
"add_cw": "Dodaj ostrzeżenie zawartości",
"pined_hashtag": "Pin the hashtag"
}
},
"jump": {
"jump_to": "Przejdź do…"
@ -244,7 +245,8 @@
"reply": "Odpowiadać",
"reblog": "Podbić wpisu",
"fav": "Ulubiony",
"detail": "Szczegół"
"detail": "Szczegół",
"pinned": "Przypięty wpis"
}
},
"side_bar": {

View File

@ -27,6 +27,23 @@
"tag": "Hashtag"
},
"modals": {
"new_toot": {
"footer": {
"poll": "Add a poll"
},
"poll": {
"add_choice": "Add a choice",
"expires": {
"5_minutes": "5 minutes",
"30_minutes": "30 minutes",
"1_hour": "1 hour",
"6_hours": "6 hours",
"1_day": "1 day",
"3_days": "3 days",
"7_days": "7 days"
}
}
},
"shortcut": {
"h": "Switch the focus to left column",
"l": "Switch the focus to right column",

View File

@ -14,6 +14,14 @@
</div>
<Status v-model="status" :opened="newTootModal" :fixCursorPos="hashtagInserting" @paste="onPaste" @toot="toot" />
</el-form>
<Poll
v-if="openPoll"
v-model="polls"
@addPoll="addPoll"
@removePoll="removePoll"
:defaultExpire="pollExpire"
@changeExpire="changeExpire"
></Poll>
<div class="preview">
<div class="image-wrapper" v-for="media in attachedMedias" v-bind:key="media.id">
<img :src="media.preview_url" class="preview-image" />
@ -35,14 +43,19 @@
</div>
<div slot="footer" class="dialog-footer">
<div class="upload-image">
<el-button size="small" type="text" @click="selectImage" :title="$t('modals.new_toot.add_image')">
<el-button size="small" type="text" @click="selectImage" :title="$t('modals.new_toot.footer.add_image')">
<icon name="camera"></icon>
</el-button>
<input name="image" type="file" class="image-input" ref="image" @change="onChangeImage" :key="attachedMediaId" />
</div>
<div class="poll">
<el-button size="small" type="text" @click="togglePollForm" :title="$t('modals.new_toot.footer.poll')">
<icon name="poll"></icon>
</el-button>
</div>
<div class="privacy">
<el-dropdown trigger="click" @command="changeVisibility">
<el-button size="small" type="text" :title="$t('modals.new_toot.change_visibility')">
<el-button size="small" type="text" :title="$t('modals.new_toot.footer.change_visibility')">
<icon :name="visibilityIcon"></icon>
</el-button>
<el-dropdown-menu slot="dropdown">
@ -70,7 +83,7 @@
size="small"
type="text"
@click="changeSensitive"
:title="$t('modals.new_toot.change_sensitive')"
:title="$t('modals.new_toot.footer.change_sensitive')"
:aria-pressed="sensitive"
>
<icon name="eye-slash" v-show="!sensitive"></icon>
@ -82,7 +95,7 @@
size="small"
type="text"
@click="showContentWarning = !showContentWarning"
:title="$t('modals.new_toot.add_cw')"
:title="$t('modals.new_toot.footer.add_cw')"
:class="showContentWarning ? '' : 'clickable'"
:aria-pressed="showContentWarning"
>
@ -94,7 +107,7 @@
size="small"
type="text"
@click="pinedHashtag = !pinedHashtag"
:title="$t('modals.new_toot.pined_hashtag')"
:title="$t('modals.new_toot.footer.pined_hashtag')"
:class="pinedHashtag ? '' : 'clickable'"
:aria-pressed="pinedHashtag"
>
@ -120,19 +133,27 @@ import { mapState, mapGetters } from 'vuex'
import { clipboard } from 'electron'
import Visibility from '~/src/constants/visibility'
import Status from './NewToot/Status'
import Poll from './NewToot/Poll'
import { NewTootTootLength, NewTootAttachLength, NewTootModalOpen, NewTootBlockSubmit } from '@/errors/validations'
export default {
name: 'new-toot',
components: {
Status
Status,
Poll
},
data() {
return {
status: '',
spoiler: '',
showContentWarning: false,
visibilityList: Visibility
visibilityList: Visibility,
openPoll: false,
polls: ['', ''],
pollExpire: {
label: this.$t('modals.new_toot.poll.expires.1_day'),
value: 3600 * 24
}
}
},
computed: {
@ -208,13 +229,20 @@ export default {
methods: {
close() {
this.filteredAccount = []
this.polls = ['', '']
this.pollExpire = {
label: this.$t('modals.new_toot.poll.expires.1_day'),
value: 3600 * 24
}
this.$store.dispatch('TimelineSpace/Modals/NewToot/resetMediaCount')
this.$store.dispatch('TimelineSpace/Modals/NewToot/closeModal')
},
async toot() {
const form = {
status: this.status,
spoiler: this.spoiler
spoiler: this.spoiler,
polls: this.polls,
pollExpireSeconds: this.pollExpire.value
}
try {
@ -323,6 +351,18 @@ export default {
},
updateDescription(id, value) {
this.$store.commit('TimelineSpace/Modals/NewToot/updateMediaDescription', { id: id, description: value })
},
togglePollForm() {
this.openPoll = !this.openPoll
},
addPoll() {
this.polls.push('')
},
removePoll(id) {
this.polls.splice(id, 1)
},
changeExpire(obj) {
this.pollExpire = obj
}
}
}
@ -429,6 +469,11 @@ export default {
}
}
.poll {
float: left;
margin-left: 8px;
}
.privacy {
float: left;
margin-left: 8px;

View File

@ -0,0 +1,99 @@
<template>
<div class="poll">
<ul class="poll-list">
<li class="poll-option" v-for="(option, id) in value" v-bind:key="id">
<el-radio :disabled="true" :label="id">
<el-input :placeholder="`choice ${id}`" :value="value[id]" @input="value => updateOption(value, id)" size="small"></el-input>
<el-button class="remove-poll" type="text" @click="removePoll(id)" size="small"><icon name="times"></icon></el-button>
</el-radio>
</li>
</ul>
<el-button class="add-poll" type="info" size="small" @click="addPoll" plain>{{ $t('modals.new_toot.poll.add_choice') }}</el-button>
<el-select v-model="expiresIn" size="small" value-key="value" @change="changeExpire">
<el-option v-for="exp in expires" :key="exp.value" :label="exp.label" :value="exp"> </el-option>
</el-select>
</div>
</template>
<script>
export default {
name: 'poll',
props: ['value', 'defaultExpire'],
data() {
return {
expires: [
{
label: this.$t('modals.new_toot.poll.expires.5_minutes'),
value: 60 * 5
},
{
label: this.$t('modals.new_toot.poll.expires.30_minutes'),
value: 60 * 30
},
{
label: this.$t('modals.new_toot.poll.expires.1_hour'),
value: 3600
},
{
label: this.$t('modals.new_toot.poll.expires.6_hours'),
value: 3600 * 6
},
{
label: this.$t('modals.new_toot.poll.expires.1_day'),
value: 3600 * 24
},
{
label: this.$t('modals.new_toot.poll.expires.3_days'),
value: 3600 * 24 * 3
},
{
label: this.$t('modals.new_toot.poll.expires.7_days'),
value: 3600 * 24 * 7
}
],
expiresIn: null
}
},
created() {
this.expiresIn = this.defaultExpire
},
methods: {
addPoll() {
this.$emit('addPoll')
},
removePoll(id) {
this.$emit('removePoll', id)
},
updateOption(item, index) {
const newValue = [...this.value.slice(0, index), item, ...this.value.slice(index + 1)]
this.$emit('input', newValue)
},
changeExpire(exp) {
this.$emit('changeExpire', exp)
}
}
}
</script>
<style lang="scss" scoped>
.poll {
border-top: 1px solid #ebebeb;
.poll-list {
list-style: none;
padding-left: 16px;
.poll-option {
line-height: 38px;
.remove-poll {
margin-left: 4px;
}
}
}
.add-poll {
margin: 0 0 8px 40px;
}
}
</style>

View File

@ -20,6 +20,13 @@ type MediaDescription = {
description: string
}
type TootForm = {
status: string
spoiler: string
polls: Array<string>
pollExpireSeconds: number
}
export type NewTootState = {
modalOpen: boolean
initialStatus: string
@ -177,12 +184,12 @@ const actions: ActionTree<NewTootState, RootState> = {
throw err
})
},
postToot: async ({ state, commit, rootState, dispatch }, { status, spoiler }): Promise<Status> => {
postToot: async ({ state, commit, rootState, dispatch }, params: TootForm): Promise<Status> => {
if (!state.modalOpen) {
throw new NewTootModalOpen()
}
if (status.length < 1 || status.length > rootState.TimelineSpace.tootMax) {
if (params.status.length < 1 || params.status.length > rootState.TimelineSpace.tootMax) {
throw new NewTootTootLength()
}
@ -193,11 +200,17 @@ const actions: ActionTree<NewTootState, RootState> = {
if (visibilityKey !== undefined) {
specifiedVisibility = Visibility[visibilityKey].key
}
let form = {
status: status,
status: params.status,
visibility: specifiedVisibility,
sensitive: state.sensitive,
spoiler_text: spoiler
spoiler_text: params.spoiler,
poll: {
expires_in: params.pollExpireSeconds,
multiple: false,
options: params.polls
}
}
if (state.replyToMessage !== null) {