Rewrite Modals/NewToot with composition API

This commit is contained in:
AkiraFukushima 2022-04-30 15:49:24 +09:00
parent ebc09cdf55
commit ee9300054c
No known key found for this signature in database
GPG Key ID: B6E51BAC4DE1A957
4 changed files with 342 additions and 282 deletions

View File

@ -119,7 +119,6 @@ export default {
return false return false
} }
this.$store.dispatch('TimelineSpace/Modals/NewToot/openModal') this.$store.dispatch('TimelineSpace/Modals/NewToot/openModal')
this.$store.dispatch('TimelineSpace/Modals/NewToot/incrementMediaCount')
this.$store this.$store
.dispatch('TimelineSpace/Modals/NewToot/uploadImage', file) .dispatch('TimelineSpace/Modals/NewToot/uploadImage', file)
.then(() => { .then(() => {

View File

@ -7,18 +7,18 @@
:before-close="closeConfirm" :before-close="closeConfirm"
width="600px" width="600px"
custom-class="new-toot-modal" custom-class="new-toot-modal"
ref="dialog" ref="dialogRef"
> >
<el-form v-on:submit.prevent="toot" role="form"> <el-form v-on:submit.prevent="toot" role="form">
<Quote :message="quoteToMessage" :displayNameStyle="displayNameStyle" v-if="quoteToMessage !== null" ref="quote"></Quote> <Quote :message="quoteToMessage" :displayNameStyle="displayNameStyle" v-if="quoteToMessage !== null" ref="quoteRef"></Quote>
<div class="spoiler" v-if="showContentWarning" ref="spoiler"> <div class="spoiler" v-if="showContentWarning" ref="spoilerRef">
<div class="el-input"> <div class="el-input">
<input type="text" class="el-input__inner" :placeholder="$t('modals.new_toot.cw')" v-model="spoiler" /> <input type="text" class="el-input__inner" :placeholder="$t('modals.new_toot.cw')" v-model="spoilerText" />
</div> </div>
</div> </div>
<Status <Status
:modelValue="status" :modelValue="statusText"
@update:modelValue="status = $event" @update:modelValue="statusText = $event"
:opened="newTootModal" :opened="newTootModal"
:fixCursorPos="hashtagInserting" :fixCursorPos="hashtagInserting"
:height="statusHeight" :height="statusHeight"
@ -26,8 +26,15 @@
@toot="toot" @toot="toot"
/> />
</el-form> </el-form>
<Poll v-if="openPoll" v-model:polls="polls" v-model:expire="pollExpire" @addPoll="addPoll" @removePoll="removePoll" ref="poll"></Poll> <Poll
<div class="preview" ref="preview"> v-if="openPoll"
v-model:polls="polls"
v-model:expire="pollExpire"
@addPoll="addPoll"
@removePoll="removePoll"
ref="pollRef"
></Poll>
<div class="preview" ref="previewRef">
<div class="image-wrapper" v-for="media in attachedMedias" v-bind:key="media.id"> <div class="image-wrapper" v-for="media in attachedMedias" v-bind:key="media.id">
<img :src="media.preview_url" class="preview-image" /> <img :src="media.preview_url" class="preview-image" />
<el-button type="text" @click="removeAttachment(media)" class="remove-image"><font-awesome-icon icon="circle-xmark" /></el-button> <el-button type="text" @click="removeAttachment(media)" class="remove-image"><font-awesome-icon icon="circle-xmark" /></el-button>
@ -50,7 +57,7 @@
<el-button size="default" type="text" @click="selectImage" :title="$t('modals.new_toot.footer.add_image')"> <el-button size="default" type="text" @click="selectImage" :title="$t('modals.new_toot.footer.add_image')">
<font-awesome-icon icon="camera" /> <font-awesome-icon icon="camera" />
</el-button> </el-button>
<input name="image" type="file" class="image-input" ref="image" @change="onChangeImage" :key="attachedMediaId" /> <input name="image" type="file" class="image-input" ref="imageRef" @change="onChangeImage" />
</div> </div>
<div class="poll"> <div class="poll">
<el-button size="default" type="text" @click="togglePollForm" :title="$t('modals.new_toot.footer.poll')"> <el-button size="default" type="text" @click="togglePollForm" :title="$t('modals.new_toot.footer.poll')">
@ -122,7 +129,7 @@
</div> </div>
<div class="info"> <div class="info">
<img src="../../../assets/images/loading-spinner-wide.svg" v-show="loading" class="loading" /> <img src="../../../assets/images/loading-spinner-wide.svg" v-show="loading" class="loading" />
<span class="text-count">{{ tootMax - status.length }}</span> <span class="text-count">{{ tootMax - statusText.length }}</span>
<el-button class="toot-action" @click="closeConfirm(close)">{{ $t('modals.new_toot.cancel') }}</el-button> <el-button class="toot-action" @click="closeConfirm(close)">{{ $t('modals.new_toot.cancel') }}</el-button>
<el-button class="toot-action" type="primary" @click="toot" :loading="blockSubmit">{{ $t('modals.new_toot.toot') }}</el-button> <el-button class="toot-action" type="primary" @click="toot" :loading="blockSubmit">{{ $t('modals.new_toot.toot') }}</el-button>
@ -135,332 +142,366 @@
</div> </div>
</template> </template>
<script> <script lang="ts">
import { mapState, mapGetters } from 'vuex' import { defineComponent, ref, reactive, computed, onMounted, ComponentPublicInstance, nextTick } from 'vue'
import { useI18next } from 'vue3-i18next'
import { ElMessage, ElMessageBox, ElDialog } from 'element-plus'
import { Entity } from 'megalodon'
import { useStore } from '@/store'
import Visibility from '~/src/constants/visibility' import Visibility from '~/src/constants/visibility'
import Status from './NewToot/Status' import Status from './NewToot/Status.vue'
import Poll from './NewToot/Poll' import Poll from './NewToot/Poll.vue'
import Quote from './NewToot/Quote' import Quote from './NewToot/Quote.vue'
import { NewTootTootLength, NewTootAttachLength, NewTootModalOpen, NewTootBlockSubmit, NewTootPollInvalid } from '@/errors/validations' import { NewTootTootLength, NewTootAttachLength, NewTootModalOpen, NewTootBlockSubmit, NewTootPollInvalid } from '@/errors/validations'
import { EventEmitter } from '~/src/renderer/components/event' import { EventEmitter } from '@/components/event'
import { ACTION_TYPES, MUTATION_TYPES } from '@/store/TimelineSpace/Modals/NewToot'
export default { export default defineComponent({
name: 'new-toot', name: 'new-toot',
components: { components: {
Status, Status,
Poll, Poll,
Quote Quote
}, },
data() { setup() {
return { const space = 'TimelineSpace/Modals/NewToot'
status: '', const store = useStore()
spoiler: '', const i18n = useI18next()
showContentWarning: false, const visibilityList = Visibility
visibilityList: Visibility,
openPoll: false, const enableResizing = ref<boolean>(true)
polls: [], const statusText = ref<string>('')
pollExpire: { const spoilerText = ref<string>('')
label: this.$t('modals.new_toot.poll.expires.1_day'), const showContentWarning = ref<boolean>(false)
value: 3600 * 24 const openPoll = ref<boolean>(false)
}, const polls = ref<Array<string>>([])
statusHeight: 240 const pollExpire = reactive({
} label: i18n.t('modals.new_toot.poll.expires.1_day'),
}, value: 3600 * 24
computed: { })
...mapState('TimelineSpace/Modals/NewToot', { const statusHeight = ref<number>(240)
replyToId: state => { const previewRef = ref<HTMLElement>()
if (state.replyToMessage !== null) { const imageRef = ref<HTMLInputElement>()
return state.replyToMessage.id const pollRef = ref<ComponentPublicInstance>()
} else { const spoilerRef = ref<HTMLElement>()
return null const dialogRef = ref<InstanceType<typeof ElDialog>>()
} const quoteRef = ref<ComponentPublicInstance>()
},
quoteToMessage: state => state.quoteToMessage, const quoteToMessage = computed(() => store.state.TimelineSpace.Modals.NewToot.quoteToMessage)
attachedMedias: state => state.attachedMedias, const attachedMedias = computed(() => store.state.TimelineSpace.Modals.NewToot.attachedMedias)
attachedMediaId: state => state.attachedMediaId, const mediaDescriptions = computed(() => store.state.TimelineSpace.Modals.NewToot.mediaDescriptions)
mediaDescriptions: state => state.mediaDescriptions, const blockSubmit = computed(() => store.state.TimelineSpace.Modals.NewToot.blockSubmit)
blockSubmit: state => state.blockSubmit, const sensitive = computed(() => store.state.TimelineSpace.Modals.NewToot.sensitive)
visibility: state => state.visibility, const initialStatus = computed(() => store.state.TimelineSpace.Modals.NewToot.initialStatus)
sensitive: state => state.sensitive, const initialSpoiler = computed(() => store.state.TimelineSpace.Modals.NewToot.initialSpoiler)
initialStatus: state => state.initialStatus, const visibilityIcon = computed(() => {
initialSpoiler: state => state.initialSpoiler, switch (store.state.TimelineSpace.Modals.NewToot.visibility) {
visibilityIcon: state => { case Visibility.Public.value:
switch (state.visibility) { return 'globe'
case Visibility.Public.value: case Visibility.Unlisted.value:
return 'globe' return 'unlock'
case Visibility.Unlisted.value: case Visibility.Private.value:
return 'unlock' return 'lock'
case Visibility.Private.value: case Visibility.Direct.value:
return 'lock' return 'envelope'
case Visibility.Direct.value: default:
return 'envelope' return 'globe'
default:
return 'globe'
}
},
loading: state => state.loading
}),
...mapState('TimelineSpace', {
tootMax: state => state.tootMax
}),
...mapState('App', {
displayNameStyle: state => state.displayNameStyle
}),
...mapGetters('TimelineSpace/Modals/NewToot', ['hashtagInserting']),
newTootModal: {
get() {
return this.$store.state.TimelineSpace.Modals.NewToot.modalOpen
},
set(value) {
if (value) {
this.$store.dispatch('TimelineSpace/Modals/NewToot/openModal')
} else {
this.$store.dispatch('TimelineSpace/Modals/NewToot/closeModal')
}
}
},
pinedHashtag: {
get() {
return this.$store.state.TimelineSpace.Modals.NewToot.pinedHashtag
},
set(value) {
this.$store.commit('TimelineSpace/Modals/NewToot/changePinedHashtag', value)
}
}
},
created() {
this.$store.dispatch('TimelineSpace/Modals/NewToot/setupLoading')
},
mounted() {
EventEmitter.on('image-uploaded', () => {
if (this.$refs.preview) {
this.statusHeight = this.statusHeight - this.$refs.preview.offsetHeight
} }
}) })
}, const loading = computed(() => store.state.TimelineSpace.Modals.NewToot.loading)
watch: { const tootMax = computed(() => store.state.TimelineSpace.tootMax)
newTootModal: function (newState, oldState) { const displayNameStyle = computed(() => store.state.App.displayNameStyle)
if (!oldState && newState) { const hashtagInserting = computed(() => store.getters[`${space}/hashtagInserting`])
this.showContentWarning = this.initialSpoiler const newTootModal = computed({
this.status = this.initialStatus get: () => store.state.TimelineSpace.Modals.NewToot.modalOpen,
this.spoiler = this.initialSpoiler set: (value: boolean) => {
if (value) {
store.dispatch(`${space}/${ACTION_TYPES.OPEN_MODAL}`)
} else {
store.dispatch(`${space}/${ACTION_TYPES.CLOSE_MODAL}`)
}
} }
})
const pinedHashtag = computed({
get: () => store.state.TimelineSpace.Modals.NewToot.pinedHashtag,
set: (value: boolean) => store.commit(`${space}/${MUTATION_TYPES.CHANGE_PINED_HASHTAG}`, value)
})
store.dispatch(`${space}/${ACTION_TYPES.SETUP_LOADING}`)
onMounted(() => {
EventEmitter.on('image-uploaded', () => {
if (previewRef.value) {
statusHeight.value = statusHeight.value - previewRef.value.offsetHeight
}
})
showContentWarning.value = initialSpoiler.value.length > 0
statusText.value = initialStatus.value
spoilerText.value = initialSpoiler.value
})
const close = () => {
store.dispatch(`${space}/${ACTION_TYPES.RESET_MEDIA_COUNT}`)
store.dispatch(`${space}/${ACTION_TYPES.CLOSE_MODAL}`)
} }
}, const toot = async () => {
methods: {
close() {
this.filteredAccount = []
const spoilerHeight = this.$refs.spoiler ? this.$refs.spoiler.offsetHeight : 0
this.showContentWarning = false
this.spoiler = ''
this.statusHeight = this.statusHeight + spoilerHeight
const pollHeight = this.$refs.poll ? this.$refs.poll.$el.offsetHeight : 0
this.openPoll = false
this.polls = []
this.pollExpire = {
label: this.$t('modals.new_toot.poll.expires.1_day'),
value: 3600 * 24
}
this.statusHeight = this.statusHeight + pollHeight
const quoteHeight = this.$refs.quote ? this.$refs.quote.$el.offsetHeight : 0
this.statusHeight = this.statusHeight + quoteHeight
const attachmentHeight = this.$refs.preview ? this.$refs.preview.offsetHeight : 0
this.statusHeight = this.statusHeight + attachmentHeight
this.$store.dispatch('TimelineSpace/Modals/NewToot/resetMediaCount')
this.$store.dispatch('TimelineSpace/Modals/NewToot/closeModal')
},
async toot() {
const form = { const form = {
status: this.status, status: statusText.value,
spoiler: this.spoiler, spoiler: spoilerText.value,
polls: this.polls, polls: polls.value,
pollExpireSeconds: this.pollExpire.value pollExpireSeconds: pollExpire.value
} }
try { try {
const status = await this.$store.dispatch('TimelineSpace/Modals/NewToot/postToot', form) const status = await store.dispatch(`${space}/${ACTION_TYPES.POST_TOOT}`, form)
this.$store.dispatch('TimelineSpace/Modals/NewToot/updateHashtags', status.tags) store.dispatch(`${space}/${ACTION_TYPES.UPDATE_HASHTAGS}`, status.tags)
this.close() close()
} catch (err) { } catch (err) {
console.error(err) console.error(err)
if (err instanceof NewTootTootLength) { if (err instanceof NewTootTootLength) {
this.$message({ ElMessage({
message: this.$t('validation.new_toot.toot_length', { message: i18n.t('validation.new_toot.toot_length', {
min: 1, min: 1,
max: this.tootMax max: tootMax.value
}), }),
type: 'error' type: 'error'
}) })
} else if (err instanceof NewTootAttachLength) { } else if (err instanceof NewTootAttachLength) {
this.$message({ ElMessage({
message: this.$t('validation.new_toot.attach_length', { max: 4 }), message: i18n.t('validation.new_toot.attach_length', { max: 4 }),
type: 'error' type: 'error'
}) })
} else if (err instanceof NewTootPollInvalid) { } else if (err instanceof NewTootPollInvalid) {
this.$message({ ElMessage({
message: this.$t('validation.new_toot.poll_invalid'), message: i18n.t('validation.new_toot.poll_invalid'),
type: 'error' type: 'error'
}) })
} else if (err instanceof NewTootModalOpen || err instanceof NewTootBlockSubmit) { } else if (err instanceof NewTootModalOpen || err instanceof NewTootBlockSubmit) {
// Nothing // Nothing
} else { } else {
this.$message({ ElMessage({
message: this.$t('message.toot_error'), message: i18n.t('message.toot_error'),
type: 'error' type: 'error'
}) })
} }
} }
}, }
selectImage() { const selectImage = () => {
this.$refs.image.click() imageRef!.value!.click()
}, }
onChangeImage(e) { const updateImage = (file: File) => {
if (e.target.files.item(0) === null || e.target.files.item(0) === undefined) { store
.dispatch(`${space}/${ACTION_TYPES.UPLOAD_IMAGE}`, file)
.then(() => {
enableResizing.value = false
nextTick(() => {
if (attachedMedias.value.length === 1 && previewRef.value) {
statusHeight.value = statusHeight.value - previewRef.value.offsetHeight
}
enableResizing.value = true
})
})
.catch(err => {
if (err instanceof NewTootAttachLength) {
ElMessage({
message: i18n.t('validation.new_toot.attach_length', { max: 4 }),
type: 'error'
})
} else {
ElMessage({
message: i18n.t('message.attach_error'),
type: 'error'
})
}
})
}
const onChangeImage = (e: Event) => {
const target = e.target as HTMLInputElement
const file = target.files?.item(0)
if (file === null || file === undefined) {
return return
} }
const file = e.target.files.item(0)
if (!file.type.includes('image') && !file.type.includes('video')) { if (!file.type.includes('image') && !file.type.includes('video')) {
this.$message({ ElMessage({
message: this.$t('validation.new_toot.attach_image'), message: i18n.t('validation.new_toot.attach_image'),
type: 'error' type: 'error'
}) })
return return
} }
this.updateImage(file) updateImage(file)
}, }
onPaste(e) { const onPaste = (e: Event) => {
const mimeTypes = window.clipboard.availableFormats().filter(type => type.startsWith('image')) const mimeTypes = (window as any).clipboard.availableFormats().filter(t => t.startsWith('image'))
if (mimeTypes.length === 0) { if (mimeTypes.length === 0) {
return return
} }
e.preventDefault() e.preventDefault()
const image = window.clipboard.readImage() const image = (window as any).clipboard.readImage()
let data let data: any
if (/^image\/jpe?g$/.test(mimeTypes[0])) { if (/^image\/jpe?g$/.test(mimeTypes[0])) {
data = image.toJPEG(100) data = image.toJPEG(100)
} else { } else {
data = image.toPNG() data = image.toPNG()
} }
const file = new File([data], 'clipboard.picture', { type: mimeTypes[0] }) const file = new File([data], 'clipboard.picture', { type: mimeTypes[0] })
this.updateImage(file) updateImage(file)
}, }
updateImage(file) { const removeAttachment = (media: Entity.Attachment) => {
this.$store.dispatch('TimelineSpace/Modals/NewToot/incrementMediaCount') const previousHeight = previewRef!.value!.offsetHeight
this.$store store.dispatch(`${space}/${ACTION_TYPES.REMOVE_MEDIA}`, media).then(() => {
.dispatch('TimelineSpace/Modals/NewToot/uploadImage', file) enableResizing.value = false
.then(() => { nextTick(() => {
this.statusHeight = this.statusHeight - this.$refs.preview.offsetHeight if (attachedMedias.value.length === 0) {
}) statusHeight.value = statusHeight.value + previousHeight
.catch(err => {
if (err instanceof NewTootAttachLength) {
this.$message({
message: this.$t('validation.new_toot.attach_length', { max: 4 }),
type: 'error'
})
} else {
this.$message({
message: this.$t('message.attach_error'),
type: 'error'
})
} }
enableResizing.value = true
}) })
},
removeAttachment(media) {
const previousHeight = this.$refs.preview.offsetHeight
this.$store.dispatch('TimelineSpace/Modals/NewToot/removeMedia', media).then(() => {
this.statusHeight = this.statusHeight + previousHeight
}) })
}, }
changeVisibility(level) { const changeVisibility = (level: number) => {
this.$store.commit('TimelineSpace/Modals/NewToot/changeVisibilityValue', level) store.commit(`${space}/${MUTATION_TYPES.CHANGE_VISIBILITY_VALUE}`, level)
}, }
changeSensitive() { const changeSensitive = () => {
this.$store.commit('TimelineSpace/Modals/NewToot/changeSensitive', !this.sensitive) store.commit(`${space}/${MUTATION_TYPES.CHANGE_SENSITIVE}`, !sensitive.value)
}, }
closeConfirm(done) { const closeConfirm = (done: Function) => {
if (this.status.length === 0) { if (statusText.value.length === 0) {
done() done()
} else { } else {
this.$confirm(this.$t('modals.new_toot.close_confirm'), { ElMessageBox.confirm(i18n.t('modals.new_toot.close_confirm'), {
confirmButtonText: this.$t('modals.new_toot.close_confirm_ok'), confirmButtonText: i18n.t('modals.new_toot.close_confirm_ok'),
cancelButtonText: this.$t('modals.new_toot.close_confirm_cancel') cancelButtonText: i18n.t('modals.new_toot.close_confirm_cancel')
}) })
.then(_ => { .then(_ => {
done() done()
}) })
.catch(_ => {}) .catch(_ => {})
} }
}, }
updateDescription(id, value) { const updateDescription = (id: number, value: string) => {
this.$store.commit('TimelineSpace/Modals/NewToot/updateMediaDescription', { id: id, description: value }) store.commit(`${space}/${MUTATION_TYPES.UPDATE_MEDIA_DESCRIPTION}`, { id: id, description: value })
}, }
async togglePollForm() { const togglePollForm = () => {
const previousHeight = this.$refs.poll ? this.$refs.poll.$el.offsetHeight : 0 const previousHeight = pollRef.value ? pollRef.value.$el.offsetHeight : 0
const toggle = () => { const toggle = () => {
this.openPoll = !this.openPoll openPoll.value = !openPoll.value
if (this.openPoll) { if (openPoll.value) {
this.polls = ['', ''] polls.value = ['', '']
} else { } else {
this.polls = [] polls.value = []
} }
} }
await toggle() enableResizing.value = false
if (this.openPoll) { toggle()
this.statusHeight = this.statusHeight - this.$refs.poll.$el.offsetHeight nextTick(() => {
} else { if (openPoll.value) {
this.statusHeight = this.statusHeight + previousHeight const currentHeight = pollRef.value ? pollRef.value.$el.offsetHeight : 0
} statusHeight.value = statusHeight.value - currentHeight
}, } else {
async addPoll() { statusHeight.value = statusHeight.value + previousHeight
const previousPollHeight = this.$refs.poll.$el.offsetHeight }
await this.polls.push('') enableResizing.value = true
const diff = this.$refs.poll.$el.offsetHeight - previousPollHeight })
this.statusHeight = this.statusHeight - diff }
}, const addPoll = () => {
async removePoll(id) { enableResizing.value = false
const previousPollHeight = this.$refs.poll.$el.offsetHeight polls.value.push('')
await this.polls.splice(id, 1) nextTick(() => {
const diff = previousPollHeight - this.$refs.poll.$el.offsetHeight enableResizing.value = true
this.statusHeight = this.statusHeight + diff })
}, }
async toggleContentWarning() { const removePoll = (id: number) => {
const previousHeight = this.$refs.spoiler ? this.$refs.spoiler.offsetHeight : 0 enableResizing.value = false
await (this.showContentWarning = !this.showContentWarning) polls.value.splice(id, 1)
if (this.showContentWarning) { nextTick(() => {
this.statusHeight = this.statusHeight - this.$refs.spoiler.offsetHeight enableResizing.value = true
} else { })
this.statusHeight = this.statusHeight + previousHeight }
} const toggleContentWarning = () => {
}, const previousHeight = spoilerRef.value ? spoilerRef.value.offsetHeight : 0
handleResize(event) { enableResizing.value = false
showContentWarning.value = !showContentWarning.value
nextTick(() => {
if (showContentWarning.value) {
if (spoilerRef.value) {
statusHeight.value = statusHeight.value - spoilerRef.value.offsetHeight
}
} else {
statusHeight.value = statusHeight.value + previousHeight
}
enableResizing.value = true
})
}
const handleResize = (event: { width: number; height: number }) => {
if (!enableResizing.value) return
const dialog = document.getElementsByClassName('new-toot-modal').item(0) as HTMLElement
if (!dialog) return
const dialogStyle = window.getComputedStyle(dialog, null)
// Ignore when the modal height already reach window height. // Ignore when the modal height already reach window height.
const vHeight = this.$refs.dialog.$el.firstChild.style.marginTop const marginTop = dialogStyle.marginTop
const marginTop = (document.documentElement.clientHeight / 100) * parseInt(vHeight) const limitHeight = document.documentElement.clientHeight - parseInt(marginTop) - 80
const limitHeight = document.documentElement.clientHeight - marginTop - 80 if (dialog.offsetHeight >= limitHeight) {
if (this.$refs.dialog.$el.firstChild.offsetHeight >= limitHeight) {
return return
} }
// When emoji picker is opened, resize event has to be stopped. const pollHeight = pollRef.value ? pollRef.value.$el.offsetHeight : 0
const style = this.$refs.dialog.$el.firstChild.style const spoilerHeight = spoilerRef.value ? spoilerRef.value.offsetHeight : 0
if (style.overflow === '' || style.overflow === 'hidden') { const quoteHeight = quoteRef.value ? quoteRef.value.$el.offsetHeight : 0
const pollHeight = this.$refs.poll ? this.$refs.poll.$el.offsetHeight : 0 const previewHeight = previewRef.value ? previewRef.value.offsetHeight : 0
const spoilerHeight = this.$refs.spoiler ? this.$refs.spoiler.offsetHeight : 0 const headerHeight = 54
const quoteHeight = this.$refs.quote ? this.$refs.quote.$el.offsetHeight : 0 const footerHeight = 66
const headerHeight = 54 statusHeight.value = event.height - footerHeight - headerHeight - previewHeight - pollHeight - spoilerHeight - quoteHeight
const footerHeight = 63
this.statusHeight =
event.height - footerHeight - headerHeight - this.$refs.preview.offsetHeight - pollHeight - spoilerHeight - quoteHeight
}
},
innerElementOpened() {
// if (open) {
// this.$refs.dialog.$el.firstChild.style.overflow = 'visible'
// } else {
// this.$refs.dialog.$el.firstChild.style.overflow = 'hidden'
// }
} }
}
} return {
visibilityList,
statusText,
spoilerText,
showContentWarning,
openPoll,
polls,
pollExpire,
statusHeight,
// DOM refs
previewRef,
imageRef,
pollRef,
spoilerRef,
dialogRef,
quoteRef,
// computed
quoteToMessage,
attachedMedias,
mediaDescriptions,
blockSubmit,
sensitive,
visibilityIcon,
loading,
tootMax,
displayNameStyle,
hashtagInserting,
newTootModal,
pinedHashtag,
// methods
close,
toot,
selectImage,
onChangeImage,
onPaste,
removeAttachment,
changeVisibility,
changeSensitive,
closeConfirm,
updateDescription,
togglePollForm,
addPoll,
removePoll,
toggleContentWarning,
handleResize
}
},
methods: {}
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -16,7 +16,7 @@ import {
} from '@/errors/validations' } from '@/errors/validations'
import { MyWindow } from '~/src/types/global' import { MyWindow } from '~/src/types/global'
const win = (window as any) as MyWindow const win = window as any as MyWindow
type MediaDescription = { type MediaDescription = {
id: string id: string
@ -156,8 +156,27 @@ const mutations: MutationTree<NewTootState> = {
} }
} }
export const ACTION_TYPES = {
SETUP_LOADING: 'setupLoading',
START_LOADING: 'startLoading',
STOP_LOADING: 'stopLoading',
UPDATE_MEDIA: 'updateMedia',
POST_TOOT: 'postToot',
OPEN_REPLY: 'openReply',
OPEN_QUOTE: 'openQuote',
OPEN_MODAL: 'openModal',
CLOSE_MODAL: 'closeModal',
UPLOAD_IMAGE: 'uploadImage',
INCREMENT_MEDIA_COUNT: 'incrementMediaCount',
DECREMENT_MEDIA_COUNT: 'decrementMediaCount',
RESET_MEDIA_COUNT: 'resetMediaCount',
REMOVE_MEDIA: 'removeMedia',
UPDATE_HASHTAGS: 'updateHashtags',
FETCH_VISIBILITY: 'fetchVisibility'
}
const actions: ActionTree<NewTootState, RootState> = { const actions: ActionTree<NewTootState, RootState> = {
setupLoading: ({ dispatch }) => { [ACTION_TYPES.SETUP_LOADING]: ({ dispatch }) => {
const axiosLoading = new AxiosLoading() const axiosLoading = new AxiosLoading()
axiosLoading.on('start', (_: number) => { axiosLoading.on('start', (_: number) => {
dispatch('startLoading') dispatch('startLoading')
@ -166,17 +185,17 @@ const actions: ActionTree<NewTootState, RootState> = {
dispatch('stopLoading') dispatch('stopLoading')
}) })
}, },
startLoading: ({ commit, state }) => { [ACTION_TYPES.START_LOADING]: ({ commit, state }) => {
if (state.modalOpen && !state.loading) { if (state.modalOpen && !state.loading) {
commit(MUTATION_TYPES.CHANGE_LOADING, true) commit(MUTATION_TYPES.CHANGE_LOADING, true)
} }
}, },
stopLoading: ({ commit, state }) => { [ACTION_TYPES.STOP_LOADING]: ({ commit, state }) => {
if (state.modalOpen && state.loading) { if (state.modalOpen && state.loading) {
commit(MUTATION_TYPES.CHANGE_LOADING, false) commit(MUTATION_TYPES.CHANGE_LOADING, false)
} }
}, },
updateMedia: async ({ rootState }, mediaDescription: MediaDescription) => { [ACTION_TYPES.UPDATE_MEDIA]: async ({ rootState }, mediaDescription: MediaDescription) => {
if (rootState.TimelineSpace.account.accessToken === undefined || rootState.TimelineSpace.account.accessToken === null) { if (rootState.TimelineSpace.account.accessToken === undefined || rootState.TimelineSpace.account.accessToken === null) {
throw new AuthenticationError() throw new AuthenticationError()
} }
@ -198,7 +217,7 @@ const actions: ActionTree<NewTootState, RootState> = {
throw err throw err
}) })
}, },
postToot: async ({ state, commit, rootState, dispatch }, params: TootForm): Promise<Entity.Status> => { [ACTION_TYPES.POST_TOOT]: async ({ state, commit, rootState, dispatch }, params: TootForm): Promise<Entity.Status> => {
if (!state.modalOpen) { if (!state.modalOpen) {
throw new NewTootModalOpen() throw new NewTootModalOpen()
} }
@ -287,7 +306,7 @@ const actions: ActionTree<NewTootState, RootState> = {
commit(MUTATION_TYPES.CHANGE_BLOCK_SUBMIT, false) commit(MUTATION_TYPES.CHANGE_BLOCK_SUBMIT, false)
}) })
}, },
openReply: ({ commit, rootState }, message: Entity.Status) => { [ACTION_TYPES.OPEN_REPLY]: ({ commit, rootState }, message: Entity.Status) => {
commit(MUTATION_TYPES.SET_REPLY_TO, message) commit(MUTATION_TYPES.SET_REPLY_TO, message)
const mentionAccounts = [message.account.acct] const mentionAccounts = [message.account.acct]
.concat(message.mentions.map(a => a.acct)) .concat(message.mentions.map(a => a.acct))
@ -305,18 +324,18 @@ const actions: ActionTree<NewTootState, RootState> = {
}) })
commit(MUTATION_TYPES.CHANGE_VISIBILITY_VALUE, value) commit(MUTATION_TYPES.CHANGE_VISIBILITY_VALUE, value)
}, },
openQuote: ({ commit }, message: Entity.Status) => { [ACTION_TYPES.OPEN_QUOTE]: ({ commit }, message: Entity.Status) => {
commit(MUTATION_TYPES.SET_QUOTE_TO, message) commit(MUTATION_TYPES.SET_QUOTE_TO, message)
commit(MUTATION_TYPES.CHANGE_MODAL, true) commit(MUTATION_TYPES.CHANGE_MODAL, true)
}, },
openModal: ({ dispatch, commit, state }) => { [ACTION_TYPES.OPEN_MODAL]: ({ dispatch, commit, state }) => {
if (!state.replyToMessage && state.pinedHashtag) { if (!state.replyToMessage && state.pinedHashtag) {
commit(MUTATION_TYPES.UPDATE_INITIAL_STATUS, state.hashtags.map(t => `#${t.name}`).join(' ')) commit(MUTATION_TYPES.UPDATE_INITIAL_STATUS, state.hashtags.map(t => `#${t.name}`).join(' '))
} }
commit(MUTATION_TYPES.CHANGE_MODAL, true) commit(MUTATION_TYPES.CHANGE_MODAL, true)
dispatch('fetchVisibility') dispatch('fetchVisibility')
}, },
closeModal: ({ commit }) => { [ACTION_TYPES.CLOSE_MODAL]: ({ commit }) => {
commit(MUTATION_TYPES.CHANGE_MODAL, false) commit(MUTATION_TYPES.CHANGE_MODAL, false)
commit(MUTATION_TYPES.UPDATE_INITIAL_STATUS, '') commit(MUTATION_TYPES.UPDATE_INITIAL_STATUS, '')
commit(MUTATION_TYPES.UPDATE_INITIAL_SPOILER, '') commit(MUTATION_TYPES.UPDATE_INITIAL_SPOILER, '')
@ -328,7 +347,7 @@ const actions: ActionTree<NewTootState, RootState> = {
commit(MUTATION_TYPES.CHANGE_SENSITIVE, false) commit(MUTATION_TYPES.CHANGE_SENSITIVE, false)
commit(MUTATION_TYPES.CHANGE_VISIBILITY_VALUE, Visibility.Public.value) commit(MUTATION_TYPES.CHANGE_VISIBILITY_VALUE, Visibility.Public.value)
}, },
uploadImage: async ({ commit, state, rootState }, image: any) => { [ACTION_TYPES.UPLOAD_IMAGE]: async ({ commit, state, dispatch, rootState }, image: any) => {
if (state.attachedMedias.length > 3) { if (state.attachedMedias.length > 3) {
throw new NewTootAttachLength() throw new NewTootAttachLength()
} }
@ -348,6 +367,7 @@ const actions: ActionTree<NewTootState, RootState> = {
commit(MUTATION_TYPES.CHANGE_BLOCK_SUBMIT, false) commit(MUTATION_TYPES.CHANGE_BLOCK_SUBMIT, false)
if (res.data.type === 'unknown') throw new NewTootUnknownType() if (res.data.type === 'unknown') throw new NewTootUnknownType()
commit(MUTATION_TYPES.APPEND_ATTACHED_MEDIAS, res.data) commit(MUTATION_TYPES.APPEND_ATTACHED_MEDIAS, res.data)
dispatch(ACTION_TYPES.INCREMENT_MEDIA_COUNT)
return res.data return res.data
}) })
.catch(err => { .catch(err => {
@ -356,26 +376,26 @@ const actions: ActionTree<NewTootState, RootState> = {
throw err throw err
}) })
}, },
incrementMediaCount: ({ commit, state }) => { [ACTION_TYPES.INCREMENT_MEDIA_COUNT]: ({ commit, state }) => {
commit(MUTATION_TYPES.UPDATE_MEDIA_COUNT, state.attachedMediaCount + 1) commit(MUTATION_TYPES.UPDATE_MEDIA_COUNT, state.attachedMediaCount + 1)
}, },
decrementMediaCount: ({ commit, state }) => { [ACTION_TYPES.DECREMENT_MEDIA_COUNT]: ({ commit, state }) => {
commit(MUTATION_TYPES.UPDATE_MEDIA_COUNT, state.attachedMediaCount - 1) commit(MUTATION_TYPES.UPDATE_MEDIA_COUNT, state.attachedMediaCount - 1)
}, },
resetMediaCount: ({ commit }) => { [ACTION_TYPES.RESET_MEDIA_COUNT]: ({ commit }) => {
commit(MUTATION_TYPES.UPDATE_MEDIA_COUNT, 0) commit(MUTATION_TYPES.UPDATE_MEDIA_COUNT, 0)
}, },
removeMedia: ({ commit, dispatch }, media: Entity.Attachment) => { [ACTION_TYPES.REMOVE_MEDIA]: ({ commit, dispatch }, media: Entity.Attachment) => {
commit(MUTATION_TYPES.REMOVE_MEDIA, media) commit(MUTATION_TYPES.REMOVE_MEDIA, media)
commit(MUTATION_TYPES.REMOVE_MEDIA_DESCRIPTION, media.id) commit(MUTATION_TYPES.REMOVE_MEDIA_DESCRIPTION, media.id)
dispatch('decrementMediaCount') dispatch(ACTION_TYPES.DECREMENT_MEDIA_COUNT)
}, },
updateHashtags: ({ commit, state }, tags: Array<Entity.Tag>) => { [ACTION_TYPES.UPDATE_HASHTAGS]: ({ commit, state }, tags: Array<Entity.Tag>) => {
if (state.pinedHashtag && tags.length > 0) { if (state.pinedHashtag && tags.length > 0) {
commit(MUTATION_TYPES.UPDATE_HASHTAGS, tags) commit(MUTATION_TYPES.UPDATE_HASHTAGS, tags)
} }
}, },
fetchVisibility: async ({ commit, rootState }) => { [ACTION_TYPES.FETCH_VISIBILITY]: async ({ commit, rootState }) => {
const client = generator( const client = generator(
rootState.TimelineSpace.sns, rootState.TimelineSpace.sns,
rootState.TimelineSpace.account.baseURL, rootState.TimelineSpace.account.baseURL,