akkoma-fe/src/components/post_status_form/post_status_form.js

723 lines
22 KiB
JavaScript
Raw Normal View History

2016-10-30 15:53:58 +00:00
import statusPoster from '../../services/status_poster/status_poster.service.js'
2016-11-06 18:30:35 +00:00
import MediaUpload from '../media_upload/media_upload.vue'
import ScopeSelector from '../scope_selector/scope_selector.vue'
2019-08-12 10:18:37 +00:00
import EmojiInput from '../emoji_input/emoji_input.vue'
2019-06-18 20:28:31 +00:00
import PollForm from '../poll/poll_form.vue'
import Attachment from '../attachment/attachment.vue'
2021-06-17 23:04:01 +00:00
import Gallery from 'src/components/gallery/gallery.vue'
2020-06-28 09:16:41 +00:00
import StatusContent from '../status_content/status_content.vue'
2016-11-25 17:21:25 +00:00
import fileTypeService from '../../services/file_type/file_type.service.js'
import { findOffset } from '../../services/offset_finder/offset_finder.service.js'
2020-06-28 12:14:01 +00:00
import { reject, map, uniqBy, debounce } from 'lodash'
2019-08-12 10:18:37 +00:00
import suggestor from '../emoji_input/suggestor.js'
2020-05-07 13:10:53 +00:00
import { mapGetters, mapState } from 'vuex'
import Checkbox from '../checkbox/checkbox.vue'
import Select from '../select/select.vue'
2023-01-15 17:59:32 +00:00
import iso6391 from 'iso-639-1'
2016-11-03 16:17:32 +00:00
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faSmileBeam,
faPollH,
faUpload,
2020-10-20 18:18:23 +00:00
faBan,
2020-10-20 21:01:28 +00:00
faTimes,
faCircleNotch
} from '@fortawesome/free-solid-svg-icons'
2022-12-08 16:48:17 +00:00
library.add(faSmileBeam, faPollH, faUpload, faBan, faTimes, faCircleNotch)
2019-09-19 18:38:55 +00:00
const buildMentionsString = ({ user, attentions = [] }, currentUser) => {
2016-11-03 16:17:32 +00:00
let allAttentions = [...attentions]
allAttentions.unshift(user)
allAttentions = uniqBy(allAttentions, 'id')
2019-06-09 18:35:49 +00:00
allAttentions = reject(allAttentions, { id: currentUser.id })
2016-11-03 16:17:32 +00:00
let mentions = map(allAttentions, (attention) => {
return `@${attention.screen_name}`
})
return mentions.length > 0 ? mentions.join(' ') + ' ' : ''
2016-11-03 16:17:32 +00:00
}
2016-10-30 15:53:58 +00:00
// Converts a string with px to a number like '2px' -> 2
const pxStringToNumber = (str) => {
return Number(str.substring(0, str.length - 2))
}
2016-10-30 15:53:58 +00:00
const PostStatusForm = {
2016-11-03 15:59:27 +00:00
props: [
'statusId',
'statusText',
'statusIsSensitive',
'statusPoll',
'statusFiles',
'statusMediaDescriptions',
'statusScope',
'statusContentType',
2023-01-15 17:59:32 +00:00
'statusLanguage',
2016-11-03 16:17:32 +00:00
'replyTo',
'quoteId',
2016-11-03 16:17:32 +00:00
'repliedUser',
2018-06-12 17:28:48 +00:00
'attentions',
2018-09-25 12:16:26 +00:00
'copyMessageScope',
2020-05-07 13:10:53 +00:00
'subject',
'disableSubject',
'disableScopeSelector',
'disableVisibilitySelector',
2020-05-07 13:10:53 +00:00
'disableNotice',
'disableLockWarning',
'disablePolls',
'disableSensitivityCheckbox',
'disableSubmit',
'disablePreview',
2020-05-07 13:10:53 +00:00
'placeholder',
'maxHeight',
'postHandler',
2020-05-07 13:10:53 +00:00
'preserveFocus',
'autoFocus',
'fileLimit',
'submitOnEnter',
2020-10-29 10:33:06 +00:00
'emojiPickerPlacement',
2022-09-25 17:50:03 +00:00
'optimisticPosting',
'isRedraft'
2016-11-03 15:59:27 +00:00
],
2022-12-08 16:48:17 +00:00
emits: ['posted', 'resize', 'mediaplay', 'mediapause'],
2016-11-06 18:30:35 +00:00
components: {
MediaUpload,
EmojiInput,
2019-06-18 20:28:31 +00:00
PollForm,
ScopeSelector,
Checkbox,
Select,
Attachment,
2021-06-17 23:04:01 +00:00
StatusContent,
Gallery
2016-11-06 18:30:35 +00:00
},
2022-12-08 16:48:17 +00:00
mounted() {
2020-07-15 13:19:57 +00:00
this.updateIdempotencyKey()
this.resize(this.$refs.textarea)
if (this.replyTo || this.quoteId) {
2020-05-07 13:10:53 +00:00
const textLength = this.$refs.textarea.value.length
this.$refs.textarea.setSelectionRange(textLength, textLength)
}
if (this.replyTo || this.quoteId || this.autoFocus) {
2018-08-05 19:17:11 +00:00
this.$refs.textarea.focus()
}
2018-04-15 16:05:16 +00:00
},
2022-12-08 16:48:17 +00:00
data() {
2018-04-29 14:44:08 +00:00
const preset = this.$route.query.message
let statusText = preset || ''
2016-11-03 16:17:32 +00:00
if (this.replyTo || this.quoteId) {
2016-11-03 16:17:32 +00:00
const currentUser = this.$store.state.users.currentUser
2022-12-08 16:48:17 +00:00
statusText = buildMentionsString(
{ user: this.repliedUser, attentions: this.attentions },
currentUser
)
2016-11-03 16:17:32 +00:00
}
2022-12-08 16:48:17 +00:00
const {
postContentType: contentType,
sensitiveByDefault,
sensitiveIfSubject,
interfaceLanguage
2022-12-08 16:48:17 +00:00
} = this.$store.getters.mergedConfig
2019-02-21 16:16:11 +00:00
let statusParams = {
spoilerText: this.subject || '',
status: statusText,
sensitiveByDefault,
nsfw: !!sensitiveByDefault,
files: [],
poll: {},
mediaDescriptions: {},
visibility: this.suggestedVisibility(),
2023-01-15 17:59:32 +00:00
language: interfaceLanguage,
contentType
}
2022-09-25 17:50:03 +00:00
if (this.statusId || this.isRedraft) {
const statusContentType = this.statusContentType || contentType
statusParams = {
spoilerText: this.subject || '',
status: this.statusText || '',
sensitiveIfSubject,
2022-12-08 16:48:17 +00:00
nsfw:
this.statusIsSensitive ||
(sensitiveIfSubject && this.subject) ||
!!sensitiveByDefault,
files: this.statusFiles || [],
poll: this.statusPoll || {},
mediaDescriptions: this.statusMediaDescriptions || {},
visibility: this.statusScope || this.suggestedVisibility(),
2023-01-15 17:59:32 +00:00
language: this.statusLanguage || interfaceLanguage,
contentType: statusContentType
}
}
2016-10-30 15:53:58 +00:00
return {
dropFiles: [],
2020-05-07 13:10:53 +00:00
uploadingFiles: false,
error: null,
posting: false,
highlighted: 0,
newStatus: statusParams,
2019-06-18 20:28:31 +00:00
caret: 0,
pollFormVisible: false,
2020-06-10 09:41:02 +00:00
showDropIcon: 'hide',
2020-06-28 09:16:41 +00:00
dropStopTimeout: null,
preview: null,
2020-05-07 13:10:53 +00:00
previewLoading: false,
2020-07-15 13:19:57 +00:00
emojiInputShown: false,
idempotencyKey: ''
2016-10-30 15:53:58 +00:00
}
},
computed: {
2022-12-08 16:48:17 +00:00
users() {
return this.$store.state.users.users
},
2022-12-08 16:48:17 +00:00
userDefaultScope() {
return this.$store.state.users.currentUser.default_scope
},
2022-12-08 16:48:17 +00:00
emojiUserSuggestor() {
return suggestor({
emoji: [
...this.$store.state.instance.emoji,
...this.$store.state.instance.customEmoji
],
2020-11-18 16:43:24 +00:00
store: this.$store
})
},
2022-12-08 16:48:17 +00:00
emojiSuggestor() {
2019-06-09 18:35:49 +00:00
return suggestor({
emoji: [
...this.$store.state.instance.emoji,
...this.$store.state.instance.customEmoji
]
})
},
2022-12-08 16:48:17 +00:00
emoji() {
return this.$store.state.instance.emoji || []
},
2022-12-08 16:48:17 +00:00
customEmoji() {
return this.$store.state.instance.customEmoji || []
},
2022-12-08 16:48:17 +00:00
statusLength() {
2018-02-09 14:51:04 +00:00
return this.newStatus.status.length
},
2022-12-08 16:48:17 +00:00
spoilerTextLength() {
return this.newStatus.spoilerText.length
},
2022-12-08 16:48:17 +00:00
statusLengthLimit() {
2018-09-09 19:31:34 +00:00
return this.$store.state.instance.textlimit
2018-02-09 14:51:04 +00:00
},
2022-12-08 16:48:17 +00:00
hasStatusLengthLimit() {
2018-02-09 14:51:04 +00:00
return this.statusLengthLimit > 0
},
2022-12-08 16:48:17 +00:00
charactersLeft() {
return (
this.statusLengthLimit - (this.statusLength + this.spoilerTextLength)
)
},
2022-12-08 16:48:17 +00:00
isOverLengthLimit() {
return this.hasStatusLengthLimit && this.charactersLeft < 0
},
2022-12-08 16:48:17 +00:00
alwaysShowSubject() {
return this.mergedConfig.alwaysShowSubjectInput
},
2022-12-08 16:48:17 +00:00
postFormats() {
2019-03-07 04:13:04 +00:00
return this.$store.state.instance.postFormats || []
},
2022-12-08 16:48:17 +00:00
safeDMEnabled() {
2019-04-02 15:19:45 +00:00
return this.$store.state.instance.safeDM
},
2022-12-08 16:48:17 +00:00
pollsAvailable() {
return (
this.$store.state.instance.pollsAvailable &&
2020-05-07 13:10:53 +00:00
this.$store.state.instance.pollLimits.max_options >= 2 &&
this.disablePolls !== true
2022-12-08 16:48:17 +00:00
)
},
2022-12-08 16:48:17 +00:00
hideScopeNotice() {
return (
this.disableNotice || this.$store.getters.mergedConfig.hideScopeNotice
)
2017-03-15 16:06:48 +00:00
},
2022-12-08 16:48:17 +00:00
pollContentError() {
return (
this.pollFormVisible && this.newStatus.poll && this.newStatus.poll.error
)
},
2022-12-08 16:48:17 +00:00
showPreview() {
return !this.disablePreview && (!!this.preview || this.previewLoading)
2020-06-28 09:16:41 +00:00
},
2022-12-08 16:48:17 +00:00
emptyStatus() {
return (
this.newStatus.status.trim() === '' && this.newStatus.files.length === 0
)
},
2022-12-08 16:48:17 +00:00
uploadFileLimitReached() {
2020-05-07 13:10:53 +00:00
return this.newStatus.files.length >= this.fileLimit
},
2022-12-08 16:48:17 +00:00
isEdit() {
return (
typeof this.statusId !== 'undefined' &&
this.statusId.trim() !== '' &&
!this.isRedraft
)
},
2020-05-07 13:10:53 +00:00
...mapGetters(['mergedConfig']),
...mapState({
2022-12-08 16:48:17 +00:00
mobileLayout: (state) => state.interface.mobileLayout
2023-01-15 17:59:32 +00:00
}),
isoLanguages() {
return iso6391.getAllCodes()
2023-01-15 17:59:32 +00:00
}
},
2020-06-28 09:16:41 +00:00
watch: {
2022-12-08 16:48:17 +00:00
newStatus: {
2020-07-16 07:18:18 +00:00
deep: true,
2022-12-08 16:48:17 +00:00
handler() {
2020-07-16 07:18:18 +00:00
this.statusChanged()
}
2020-06-28 09:16:41 +00:00
}
},
2016-10-30 15:53:58 +00:00
methods: {
2022-12-08 16:48:17 +00:00
statusChanged() {
2020-07-16 07:18:18 +00:00
this.autoPreview()
this.updateIdempotencyKey()
},
2022-12-08 16:48:17 +00:00
clearStatus() {
2020-07-15 13:19:57 +00:00
const newStatus = this.newStatus
this.newStatus = {
status: '',
spoilerText: '',
files: [],
visibility: newStatus.visibility,
contentType: newStatus.contentType,
2023-01-15 17:59:32 +00:00
language: newStatus.language,
2020-07-15 13:19:57 +00:00
poll: {},
mediaDescriptions: {}
}
this.pollFormVisible = false
this.$refs.mediaUpload && this.$refs.mediaUpload.clearFile()
this.clearPollForm()
if (this.preserveFocus) {
this.$nextTick(() => {
this.$refs.textarea.focus()
})
}
let el = this.$el.querySelector('textarea')
el.style.height = 'auto'
el.style.height = undefined
this.error = null
if (this.preview) this.previewStatus()
},
2022-12-08 16:48:17 +00:00
async postStatus(event, newStatus, opts = {}) {
if (this.posting && !this.optimisticPosting) {
return
}
if (this.disableSubmit) {
return
}
if (this.emojiInputShown) {
return
}
2020-05-07 13:10:53 +00:00
if (this.submitOnEnter) {
event.stopPropagation()
event.preventDefault()
}
2022-12-08 16:48:17 +00:00
if (
this.optimisticPosting &&
(this.emptyStatus || this.isOverLengthLimit)
) {
return
}
2020-10-29 10:33:06 +00:00
if (this.emptyStatus) {
this.error = this.$t('post_status.empty_status_error')
return
}
2019-06-18 20:28:31 +00:00
const poll = this.pollFormVisible ? this.newStatus.poll : {}
if (this.pollContentError) {
this.error = this.pollContentError
return
}
this.posting = true
2020-07-07 06:07:20 +00:00
try {
await this.setAllMediaDescriptions()
} catch (e) {
this.error = this.$t('post_status.media_description_error')
this.posting = false
return
}
2020-07-07 06:07:20 +00:00
2020-05-07 13:10:53 +00:00
const postingOptions = {
2016-10-30 15:53:58 +00:00
status: newStatus.status,
2018-06-07 21:31:43 +00:00
spoilerText: newStatus.spoilerText || null,
2018-06-07 09:03:50 +00:00
visibility: newStatus.visibility,
sensitive: newStatus.nsfw,
2016-11-06 18:30:35 +00:00
media: newStatus.files,
2016-11-03 15:59:27 +00:00
store: this.$store,
inReplyToStatusId: this.replyTo,
quoteId: this.quoteId,
2019-06-18 20:28:31 +00:00
contentType: newStatus.contentType,
2023-01-15 17:59:32 +00:00
language: newStatus.language,
2020-07-15 13:19:57 +00:00
poll,
idempotencyKey: this.idempotencyKey
2020-07-07 06:07:20 +00:00
}
2022-12-08 16:48:17 +00:00
const postHandler = this.postHandler
? this.postHandler
: statusPoster.postStatus
2020-05-07 13:10:53 +00:00
postHandler(postingOptions).then((data) => {
2020-05-07 13:10:53 +00:00
if (!data.error) {
2020-07-15 13:19:57 +00:00
this.clearStatus()
2020-05-07 13:10:53 +00:00
this.$emit('posted', data)
} else {
this.error = data.error
}
this.posting = false
})
2016-11-06 18:30:35 +00:00
},
2022-12-08 16:48:17 +00:00
previewStatus() {
if (this.emptyStatus && this.newStatus.spoilerText.trim() === '') {
this.preview = { error: this.$t('post_status.preview_empty') }
this.previewLoading = false
return
}
const newStatus = this.newStatus
2020-06-28 09:16:41 +00:00
this.previewLoading = true
2022-12-08 16:48:17 +00:00
statusPoster
.postStatus({
status: newStatus.status,
spoilerText: newStatus.spoilerText || null,
visibility: newStatus.visibility,
sensitive: newStatus.nsfw,
media: [],
store: this.$store,
inReplyToStatusId: this.replyTo,
quoteId: this.quoteId,
contentType: newStatus.contentType,
language: newStatus.language,
2022-12-08 16:48:17 +00:00
poll: {},
preview: true
})
.then((data) => {
// Don't apply preview if not loading, because it means
// user has closed the preview manually.
if (!this.previewLoading) return
if (!data.error) {
this.preview = data
} else {
this.preview = { error: data.error }
}
})
.catch((error) => {
this.preview = { error }
})
.finally(() => {
this.previewLoading = false
})
2020-06-28 09:16:41 +00:00
},
2022-12-08 16:48:17 +00:00
debouncePreviewStatus: debounce(function () {
this.previewStatus()
}, 500),
autoPreview() {
if (!this.preview) return
2020-06-28 12:14:01 +00:00
this.previewLoading = true
this.debouncePreviewStatus()
2020-06-28 12:14:01 +00:00
},
2022-12-08 16:48:17 +00:00
closePreview() {
2020-06-28 09:16:41 +00:00
this.preview = null
this.previewLoading = false
},
2022-12-08 16:48:17 +00:00
togglePreview() {
if (this.showPreview) {
this.closePreview()
} else {
this.previewStatus()
}
},
2022-12-08 16:48:17 +00:00
addMediaFile(fileInfo) {
2016-11-06 18:30:35 +00:00
this.newStatus.files.push(fileInfo)
2022-12-08 16:48:17 +00:00
if (
this.$store.getters.mergedConfig.sensitiveIfSubject &&
this.newStatus.spoilerText !== ''
) {
this.newStatus.nsfw = true
}
this.$emit('resize', { delayed: true })
},
2022-12-08 16:48:17 +00:00
removeMediaFile(fileInfo) {
2016-11-26 02:00:06 +00:00
let index = this.newStatus.files.indexOf(fileInfo)
this.newStatus.files.splice(index, 1)
2020-05-07 13:10:53 +00:00
this.$emit('resize')
2016-11-26 02:00:06 +00:00
},
2022-12-08 16:48:17 +00:00
editAttachment(fileInfo, newText) {
2021-06-17 23:04:01 +00:00
this.newStatus.mediaDescriptions[fileInfo.id] = newText
},
2022-12-08 16:48:17 +00:00
shiftUpMediaFile(fileInfo) {
const { files } = this.newStatus
const index = this.newStatus.files.indexOf(fileInfo)
files.splice(index, 1)
files.splice(index - 1, 0, fileInfo)
},
2022-12-08 16:48:17 +00:00
shiftDnMediaFile(fileInfo) {
const { files } = this.newStatus
const index = this.newStatus.files.indexOf(fileInfo)
files.splice(index, 1)
files.splice(index + 1, 0, fileInfo)
},
2022-12-08 16:48:17 +00:00
uploadFailed(errString, templateArgs) {
2018-12-08 21:39:58 +00:00
templateArgs = templateArgs || {}
2022-12-08 16:48:17 +00:00
this.error =
this.$t('upload.error.base') +
' ' +
this.$t('upload.error.' + errString, templateArgs)
2018-12-08 15:23:21 +00:00
},
2022-12-08 16:48:17 +00:00
startedUploadingFiles() {
2020-05-07 13:10:53 +00:00
this.uploadingFiles = true
},
2022-12-08 16:48:17 +00:00
finishedUploadingFiles() {
this.$emit('resize')
2020-05-07 13:10:53 +00:00
this.uploadingFiles = false
2016-11-25 17:21:25 +00:00
},
2022-12-08 16:48:17 +00:00
type(fileInfo) {
2016-11-25 17:21:25 +00:00
return fileTypeService.fileType(fileInfo.mimetype)
},
2022-12-08 16:48:17 +00:00
paste(e) {
2020-06-28 12:14:01 +00:00
this.autoPreview()
2019-09-23 21:06:53 +00:00
this.resize(e)
2017-11-28 20:31:40 +00:00
if (e.clipboardData.files.length > 0) {
// prevent pasting of file as text
e.preventDefault()
2017-11-28 20:31:40 +00:00
// Strangely, files property gets emptied after event propagation
// Trying to wrap it in array doesn't work. Plus I doubt it's possible
// to hold more than one file in clipboard.
this.dropFiles = [e.clipboardData.files[0]]
}
},
2022-12-08 16:48:17 +00:00
fileDrop(e) {
if (e.dataTransfer && e.dataTransfer.types.includes('Files')) {
2019-06-09 18:35:49 +00:00
e.preventDefault() // allow dropping text like before
this.dropFiles = e.dataTransfer.files
clearTimeout(this.dropStopTimeout)
2020-06-10 09:41:02 +00:00
this.showDropIcon = 'hide'
}
},
2022-12-08 16:48:17 +00:00
fileDragStop(e) {
// The false-setting is done with delay because just using leave-events
// directly caused unwanted flickering, this is not perfect either but
// much less noticable.
clearTimeout(this.dropStopTimeout)
2020-06-10 09:41:02 +00:00
this.showDropIcon = 'fade'
this.dropStopTimeout = setTimeout(() => (this.showDropIcon = 'hide'), 500)
},
2022-12-08 16:48:17 +00:00
fileDrag(e) {
2020-05-07 13:10:53 +00:00
e.dataTransfer.dropEffect = this.uploadFileLimitReached ? 'none' : 'copy'
if (e.dataTransfer && e.dataTransfer.types.includes('Files')) {
clearTimeout(this.dropStopTimeout)
2020-06-10 09:41:02 +00:00
this.showDropIcon = 'show'
}
},
2022-12-08 16:48:17 +00:00
onEmojiInputInput(e) {
this.$nextTick(() => {
this.resize(this.$refs['textarea'])
})
},
2022-12-08 16:48:17 +00:00
onSubjectInput(e) {
2022-11-10 12:06:59 +00:00
if (this.$store.getters.mergedConfig.sensitiveIfSubject) {
this.newStatus.nsfw = true
}
},
2022-12-08 16:48:17 +00:00
resize(e) {
const target = e.target || e
2022-12-08 16:48:17 +00:00
if (!(target instanceof window.Element)) {
return
}
// Reset to default height for empty form, nothing else to do here.
if (target.value === '') {
target.style.height = null
this.$emit('resize')
this.$refs['emoji-input'].resize()
return
}
2019-10-22 20:53:23 +00:00
const formRef = this.$refs['form']
const bottomRef = this.$refs['bottom']
/* Scroller is either `window` (replies in TL), sidebar (main post form,
* replies in notifs) or mobile post form. Note that getting and setting
* scroll is different for `Window` and `Element`s
*/
2022-12-08 16:48:17 +00:00
const bottomBottomPaddingStr =
window.getComputedStyle(bottomRef)['padding-bottom']
const bottomBottomPadding = pxStringToNumber(bottomBottomPaddingStr)
2019-10-22 20:53:23 +00:00
2022-12-08 16:48:17 +00:00
const scrollerRef =
this.$el.closest('.column.-scrollable') ||
this.$el.closest('.post-form-modal-view') ||
window
// Getting info about padding we have to account for, removing 'px' part
const topPaddingStr = window.getComputedStyle(target)['padding-top']
const bottomPaddingStr = window.getComputedStyle(target)['padding-bottom']
const topPadding = pxStringToNumber(topPaddingStr)
const bottomPadding = pxStringToNumber(bottomPaddingStr)
const vertPadding = topPadding + bottomPadding
const oldHeight = pxStringToNumber(target.style.height)
/* Explanation:
*
* https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight
* scrollHeight returns element's scrollable content height, i.e. visible
* element + overscrolled parts of it. We use it to determine when text
* inside the textarea exceeded its height, so we can set height to prevent
* overscroll, i.e. make textarea grow with the text. HOWEVER, since we
* explicitly set new height, scrollHeight won't go below that, so we can't
* SHRINK the textarea when there's extra space. To workaround that we set
* height to 'auto' which makes textarea tiny again, so that scrollHeight
* will match text height again. HOWEVER, shrinking textarea can screw with
2019-10-22 20:53:23 +00:00
* the scroll since there might be not enough padding around form-bottom to even
2019-09-25 16:32:30 +00:00
* warrant a scroll, so it will jump to 0 and refuse to move anywhere,
* so we check current scroll position before shrinking and then restore it
* with needed delta.
*/
// this part has to be BEFORE the content size update
2022-12-08 16:48:17 +00:00
const currentScroll =
scrollerRef === window ? scrollerRef.scrollY : scrollerRef.scrollTop
const scrollerHeight =
scrollerRef === window
? scrollerRef.innerHeight
: scrollerRef.offsetHeight
const scrollerBottomBorder = currentScroll + scrollerHeight
// BEGIN content size update
target.style.height = 'auto'
const heightWithoutPadding = Math.floor(target.scrollHeight - vertPadding)
2022-12-08 16:48:17 +00:00
let newHeight = this.maxHeight
? Math.min(heightWithoutPadding, this.maxHeight)
: heightWithoutPadding
// This is a bit of a hack to combat target.scrollHeight being different on every other input
// on some browsers for whatever reason. Don't change the height if difference is 1px or less.
if (Math.abs(newHeight - oldHeight) <= 1) {
newHeight = oldHeight
}
target.style.height = `${newHeight}px`
2020-05-07 13:10:53 +00:00
this.$emit('resize', newHeight)
// END content size update
2019-10-22 20:53:23 +00:00
// We check where the bottom border of form-bottom element is, this uses findOffset
// to find offset relative to scrollable container (scroller)
2022-12-08 16:48:17 +00:00
const bottomBottomBorder =
bottomRef.offsetHeight +
findOffset(bottomRef, scrollerRef).top +
bottomBottomPadding
2019-10-22 20:53:23 +00:00
const isBottomObstructed = scrollerBottomBorder < bottomBottomBorder
const isFormBiggerThanScroller = scrollerHeight < formRef.offsetHeight
const bottomChangeDelta = bottomBottomBorder - scrollerBottomBorder
// The intention is basically this;
2019-10-22 20:53:23 +00:00
// Keep form-bottom always visible so that submit button is in view EXCEPT
// if form element bigger than scroller and caret isn't at the end, so that
// if you scroll up and edit middle of text you won't get scrolled back to bottom
2022-12-08 16:48:17 +00:00
const shouldScrollToBottom =
isBottomObstructed &&
!(
isFormBiggerThanScroller &&
this.$refs.textarea.selectionStart !==
this.$refs.textarea.value.length
)
2019-10-22 20:53:23 +00:00
const totalDelta = shouldScrollToBottom ? bottomChangeDelta : 0
2020-11-09 13:02:48 +00:00
const targetScroll = Math.round(currentScroll + totalDelta)
2020-11-09 13:02:48 +00:00
if (scrollerRef === window) {
scrollerRef.scroll(0, targetScroll)
} else {
scrollerRef.scrollTop = targetScroll
}
this.$refs['emoji-input'].resize()
},
2022-12-08 16:48:17 +00:00
showEmojiPicker() {
this.$refs['textarea'].focus()
this.$refs['emoji-input'].triggerShowPicker()
},
2022-12-08 16:48:17 +00:00
clearError() {
this.error = null
2018-06-07 09:03:50 +00:00
},
2022-12-08 16:48:17 +00:00
changeVis(visibility) {
2018-06-07 09:03:50 +00:00
this.newStatus.visibility = visibility
},
2022-12-08 16:48:17 +00:00
togglePollForm() {
2019-06-18 20:28:31 +00:00
this.pollFormVisible = !this.pollFormVisible
},
2022-12-08 16:48:17 +00:00
setPoll(poll) {
2019-06-18 20:28:31 +00:00
this.newStatus.poll = poll
},
2022-12-08 16:48:17 +00:00
clearPollForm() {
2019-06-18 20:28:31 +00:00
if (this.$refs.pollForm) {
this.$refs.pollForm.clear()
}
},
2022-12-08 16:48:17 +00:00
dismissScopeNotice() {
this.$store.dispatch('setOption', {
name: 'hideScopeNotice',
value: true
})
2020-07-07 06:07:20 +00:00
},
2022-12-08 16:48:17 +00:00
setMediaDescription(id) {
2020-07-07 06:07:20 +00:00
const description = this.newStatus.mediaDescriptions[id]
if (!description || description.trim() === '') return
2022-12-08 16:48:17 +00:00
return statusPoster.setMediaDescription({
store: this.$store,
id,
description
})
2020-07-07 06:07:20 +00:00
},
2022-12-08 16:48:17 +00:00
setAllMediaDescriptions() {
const ids = this.newStatus.files.map((file) => file.id)
return Promise.all(ids.map((id) => this.setMediaDescription(id)))
2020-05-07 13:10:53 +00:00
},
2022-12-08 16:48:17 +00:00
handleEmojiInputShow(value) {
2020-05-07 13:10:53 +00:00
this.emojiInputShown = value
2020-07-15 13:19:57 +00:00
},
2022-12-08 16:48:17 +00:00
updateIdempotencyKey() {
2020-07-15 13:19:57 +00:00
this.idempotencyKey = Date.now().toString()
},
2022-12-08 16:48:17 +00:00
openProfileTab() {
this.$store.dispatch('openSettingsModalTab', 'profile')
},
2022-12-08 16:48:17 +00:00
suggestedVisibility() {
if (this.copyMessageScope) {
if (this.copyMessageScope === 'direct') {
return this.copyMessageScope
}
2022-12-08 16:48:17 +00:00
if (
this.copyMessageScope !== 'public' &&
this.$store.state.users.currentUser.default_scope !== 'private'
) {
return this.copyMessageScope
}
}
return this.$store.state.users.currentUser.default_scope
2016-10-30 15:53:58 +00:00
}
}
}
export default PostStatusForm