refactor postStatusForm & add initial mentionsLine support

This commit is contained in:
Henry Jameson 2026-07-21 23:22:44 +03:00
commit 2e9c556c5f
4 changed files with 387 additions and 319 deletions

View file

@ -245,7 +245,8 @@
:copy-message-scope="replyStatus.visibility" :copy-message-scope="replyStatus.visibility"
:attentions="replyStatus.attentions" :attentions="replyStatus.attentions"
:replied-user="replyStatus.user" :replied-user="replyStatus.user"
force-mentions-line mentions-line
mentions-line-read-only
/> />
</div> </div>
</div> </div>

View file

@ -1,4 +1,4 @@
import { debounce, map, reject, uniqBy } from 'lodash' import { debounce, reject, uniqBy } from 'lodash'
import { mapActions, mapState } from 'pinia' import { mapActions, mapState } from 'pinia'
import { defineAsyncComponent } from 'vue' import { defineAsyncComponent } from 'vue'
@ -55,78 +55,66 @@ library.add(
faChevronRight, faChevronRight,
) )
const buildMentionsString = ({ user, attentions = [] }, currentUser) => {
let allAttentions = [...attentions]
allAttentions.unshift(user)
allAttentions = uniqBy(allAttentions, 'id')
allAttentions = reject(allAttentions, { id: currentUser.id })
const mentions = map(allAttentions, (attention) => {
return `@${attention.screen_name}`
})
return mentions.length > 0 ? mentions.join(' ') + ' ' : ''
}
// Converts a string with px to a number like '2px' -> 2 // Converts a string with px to a number like '2px' -> 2
const pxStringToNumber = (str) => { const pxStringToNumber = (str) => {
return Number(str.substring(0, str.length - 2)) return Number(str.substring(0, str.length - 2))
} }
const typeAndRefId = ({ replyTo, profileMention, statusId }) => { const DEFAULT_NEWSTATUS = {
if (replyTo) {
return ['reply', replyTo]
} else if (profileMention) {
return ['mention', profileMention]
} else if (statusId) {
return ['edit', statusId]
} else {
return ['new', '']
}
} }
const PostStatusForm = { const PostStatusForm = {
props: [ props: {
'statusId', // Status editing stuff
'statusText', statusId: String,
'statusIsSensitive', statusText: String,
'statusPoll', statusIsSensitive: {
'statusFiles', type: Boolean,
'statusMediaDescriptions', required: false,
'statusScope', default: null, // Avoiding automatic conversion null -> false
'statusContentType', },
'replyTo', statusPoll: Object,
'repliedUser', statusQuote: Object,
'attentions', statusFiles: Array,
'copyMessageScope', statusMediaDescriptions: Object,
'subject', statusScope: String,
'disableSubject', statusContentType: String,
'disableScopeSelector', // Replies/mentions
'disableVisibilitySelector', replyTo: String,
'disableNotice', repliedUser: Object,
'disableLockWarning', mentionsLine: Boolean,
'disablePolls', mentionsLineReadOnly: Boolean,
'disableQuotes', attentions: Array,
'disableSensitivityCheckbox', subject: String,
'disableSubmit', copyMessageScope: String,
'disablePreview', profileMention: String,
'disableDraft', // Draft stuff
'hideDraft', hideDraft: Boolean,
'closeable', closeable: Boolean,
'placeholder', draftId: String,
'maxHeight', // Chats stuff
'postHandler', maxHeight: Number,
'preserveFocus', placeholder: String,
'autoFocus', postHandler: Function,
'fileLimit', preserveFocus: Boolean,
'submitOnEnter', autoFocus: Boolean,
'emojiPickerPlacement', fileLimit: Number,
'optimisticPosting', submitOnEnter: Boolean,
'profileMention', emojiPickerPlacement: String,
'draftId', optimisticPosting: Boolean,
], // Feature toggles for special cases (mostly chats)
disableSubject: Boolean,
disableScopeSelector: Boolean,
disableVisibilitySelector: Boolean,
disableNotice: Boolean,
disableLockWarning: Boolean,
disablePolls: Boolean,
disableQuotes: Boolean,
disableSensitivityCheckbox: Boolean,
disableSubmit: Boolean,
disablePreview: Boolean,
disableDraft: Boolean,
},
emits: [ emits: [
'posted', 'posted',
'draft-done', 'draft-done',
@ -136,6 +124,41 @@ const PostStatusForm = {
'close-accepted', 'close-accepted',
'update', 'update',
], ],
data() {
return {
randomSeed: genRandomSeed(),
dropFiles: [],
uploadingFiles: false,
error: null,
posting: false,
highlighted: 0,
initialized: false,
// Data is initialized first, but we have no access to .computed
// so we pre-fill with stuff meant for status editing and later
// back-fill with defaults in .created()
newStatus: {
status: this.statusText ?? null,
mentions: this.statusMentionLine ?? null,
spoilerText: this.subject ?? null,
quote: this.statusQuote ?? null,
files: this.statusFiles ?? null,
poll: this.statusPoll ?? null,
mediaDescriptions: this.statusMediaDescriptions ?? null,
nsfw: this.statusIsSensitive ?? null,
visibility: this.statusVisibility ?? null,
contentType: this.statusContentType ?? null,
},
caret: 0,
showDropIcon: 'hide',
dropStopTimeout: null,
preview: null,
previewLoading: false,
emojiInputShown: false,
idempotencyKey: '',
saveInhibited: true,
saveable: false,
}
},
components: { components: {
MediaUpload, MediaUpload,
EmojiInput, EmojiInput,
@ -154,6 +177,53 @@ const PostStatusForm = {
DraftCloser, DraftCloser,
Popover, Popover,
}, },
created() {
// If we are starting a new post, do not associate it with old drafts
const draft = !this.disableDraft && (this.draftId || this.statusType !== 'new')
? this.getDraft(this.statusType, this.refId)
: null
if (draft) {
// Copying and overriding defaults from the draft for each field
Object.keys(this.newStatus).forEach((key) => {
this.newStatus[key] = draft[key] ?? this.newStatus[key]
})
} else {
const defaultNewStatus = {
spoilerText: '',
files: [],
poll: null,
quote: null,
mediaDescriptions: {},
}
const scope =
(this.copyMessageScope && this.userDefaultScopeCopy) ||
this.copyMessageScope === 'direct'
? this.copyMessageScope
: this.userDefaultScope
const preset = this.$route.query.message
let statusText = preset ?? ''
if (this.mentionsLine) {
defaultNewStatus.status = statusText
defaultNewStatus.mentions = this.mentionsString.trim()
} else {
defaultNewStatus.status = this.mentionsString + statusText
defaultNewStatus.mentions = ''
}
defaultNewStatus.nsfw = this.userDefaultSensitive
defaultNewStatus.visibility = scope
defaultNewStatus.contentType = this.userDefaultPostContentType
Object.entries(defaultNewStatus).forEach(([key, value]) => {
this.newStatus[key] = this.newStatus[key] ?? value
})
}
this.initialized = true
},
mounted() { mounted() {
this.updateIdempotencyKey() this.updateIdempotencyKey()
this.resize(this.$refs.textarea) this.resize(this.$refs.textarea)
@ -167,229 +237,84 @@ const PostStatusForm = {
this.$refs.textarea.focus() this.$refs.textarea.focus()
} }
}, },
data() {
const preset = this.$route.query.message
let statusText = preset || ''
const { scopeCopy } = useMergedConfigStore().mergedConfig
const [statusType, refId] = typeAndRefId({
replyTo: this.replyTo,
profileMention: this.profileMention && this.repliedUser?.id,
statusId: this.statusId,
})
// If we are starting a new post, do not associate it with old drafts
let statusParams =
!this.disableDraft && (this.draftId || statusType !== 'new')
? this.getDraft(statusType, refId)
: null
if (!statusParams) {
if (statusType === 'reply' || statusType === 'mention') {
const currentUser = this.$store.state.users.currentUser
statusText = buildMentionsString(
{ user: this.repliedUser, attentions: this.attentions },
currentUser,
)
}
const scope =
(this.copyMessageScope && scopeCopy) ||
this.copyMessageScope === 'direct'
? this.copyMessageScope
: this.$store.state.users.currentUser.default_scope
const { postContentType: contentType, sensitiveByDefault } =
useMergedConfigStore().mergedConfig
statusParams = {
type: statusType,
refId,
spoilerText: this.subject || '',
status: statusText,
nsfw: !!sensitiveByDefault,
files: [],
poll: {},
hasPoll: false,
hasQuote: false,
quote: {
id: '',
url: '',
thread: false,
},
mediaDescriptions: {},
visibility: scope,
contentType,
quoting: false,
}
if (statusType === 'edit') {
const statusContentType = this.statusContentType || contentType
statusParams = {
type: statusType,
refId,
spoilerText: this.subject || '',
status: this.statusText || '',
nsfw: this.statusIsSensitive || !!sensitiveByDefault,
files: this.statusFiles || [],
poll: this.statusPoll || {},
hasPoll: false,
hasQuote: false,
quote: {
id: '',
url: '',
thread: false,
},
mediaDescriptions: this.statusMediaDescriptions || {},
visibility: this.statusScope || scope,
contentType: statusContentType,
}
}
}
return {
randomSeed: genRandomSeed(),
dropFiles: [],
uploadingFiles: false,
error: null,
posting: false,
highlighted: 0,
newStatus: statusParams,
caret: 0,
showDropIcon: 'hide',
dropStopTimeout: null,
preview: null,
previewLoading: false,
emojiInputShown: false,
idempotencyKey: '',
saveInhibited: true,
saveable: false,
}
},
computed: { computed: {
users() { // Visibility / expansion state of subcomponents
return this.$store.state.users.users pollFormVisible() {
return this.hasPoll
}, },
userDefaultScope() { quoteFormVisible() {
return this.$store.state.users.currentUser.default_scope return this.hasQuote && !this.newStatus.quote.thread
},
showAllScopes() {
return !this.mergedConfig.minimalScopesMode
},
hideExtraActions() {
return this.disableDraft || this.hideDraft
},
emojiUserSuggestor() {
return suggestor({
emoji: [
...useEmojiStore().standardEmojiList,
...useEmojiStore().customEmoji,
],
store: this.$store,
})
},
emojiSuggestor() {
return suggestor({
emoji: [
...useEmojiStore().standardEmojiList,
...useEmojiStore().customEmoji,
],
})
},
emoji() {
return useEmojiStore().standardEmojiList
},
customEmoji() {
return useEmojiStore().customEmoji
},
statusLength() {
return this.newStatus.status.length
},
spoilerTextLength() {
return this.newStatus.spoilerText.length
},
statusLengthLimit() {
return useInstanceStore().limits.textLimit
},
hasStatusLengthLimit() {
return this.statusLengthLimit > 0
},
charactersLeft() {
return (
this.statusLengthLimit - (this.statusLength + this.spoilerTextLength)
)
},
isOverLengthLimit() {
return this.hasStatusLengthLimit && this.charactersLeft < 0
},
minimalScopesMode() {
return useInstanceStore().minimalScopesMode
},
alwaysShowSubject() {
return this.mergedConfig.alwaysShowSubjectInput
},
postFormats() {
return useInstanceCapabilitiesStore().postFormats || []
},
safeDMEnabled() {
return useInstanceCapabilitiesStore().safeDM
},
pollsAvailable() {
return (
useInstanceCapabilitiesStore().pollsAvailable &&
useInstanceStore().limits.pollLimits.max_options >= 2 &&
this.disablePolls !== true
)
},
hideScopeNotice() {
return (
this.disableNotice ||
useMergedConfigStore().mergedConfig.hideScopeNotice
)
},
pollContentError() {
return (
this.pollFormVisible && this.newStatus.poll && this.newStatus.poll.error
)
}, },
showPreview() { showPreview() {
return !this.disablePreview && (!!this.preview || this.previewLoading) return !this.disablePreview && (!!this.preview || this.previewLoading)
}, },
emptyStatus() {
return ( // Composition stuff
this.newStatus.status.trim() === '' && this.newStatus.files.length === 0 statusType() {
) if (this.replyTo) {
return 'reply'
} else if (this.profileMention && this.repliedUser?.id) {
return 'mention'
} else if (this.statusId) {
return 'edit'
} else {
return 'new'
}
}, },
uploadFileLimitReached() { refId() {
return this.newStatus.files.length >= this.fileLimit if (this.replyTo) {
return this.replyTo
} else if (profileMention) {
return this.profileMention && this.repliedUser?.id
} else if (statusId) {
return this.statusId
} else {
return null
}
},
mentionsString() {
if (this.statusType !== 'reply' && this.statusType !== 'mention') return ''
let allAttentions = [...(this.attentions || [])]
allAttentions.unshift(this.repliedUser)
allAttentions = uniqBy(allAttentions, 'id')
allAttentions = reject(allAttentions, { id: this.currentUser.id })
const mentions = allAttentions.map((attention) => `@${attention.screen_name}`)
return mentions.length > 0 ? mentions.join(' ') + ' ' : ''
},
newStatusContent() {
return this.mentionsLine
? this.newStatus.status
: this.mentionsString + this.newStatus.status
}, },
isEdit() { isEdit() {
return typeof this.statusId !== 'undefined' && this.statusId.trim() !== '' return typeof this.statusId !== 'undefined' && this.statusId.trim() !== ''
}, },
quotingAvailable() { // -Reply
if (!useInstanceCapabilitiesStore().quotingAvailable) {
return false
}
return this.disableQuotes !== true
},
isReply() { isReply() {
return this.newStatus.type === 'reply' return this.statusType === 'reply'
},
inReplyStatusId() {
return !this.hasQuote ||
!this.newStatus.quote.thread ||
!this.newStatus.quote.id
? this.replyTo
: undefined
},
// -Poll
hasPoll() {
this.newStatus.poll != null
},
// -Quotes
hasQuote() {
this.newStatus.quote !== null
}, },
quotable() { quotable() {
return this.quotingAvailable && this.replyTo return this.quotingAvailable && this.replyTo
}, },
quoteThreadToggled: {
get() {
return this.newStatus.hasQuote && this.newStatus.quote.thread
},
set(value) {
this.newStatus.hasQuote = value
this.newStatus.quote.thread = value
this.newStatus.quote.id = value ? this.replyTo : ''
},
},
defaultQuotable() { defaultQuotable() {
if ( if (
!this.quotingAvailable || !this.quotingAvailable ||
@ -412,33 +337,87 @@ const PostStatusForm = {
) { ) {
return true return true
} else if (repliedStatus.visibility === 'private') { } else if (repliedStatus.visibility === 'private') {
return repliedStatus.user.id === this.$store.state.users.currentUser.id return repliedStatus.user.id === this.currentUser.id
} }
return false return false
}, },
inReplyStatusId() {
return !this.newStatus.hasQuote ||
!this.newStatus.quote.thread ||
!this.newStatus.quote.id
? this.replyTo
: undefined
},
quoteId() { quoteId() {
return this.newStatus.hasQuote ? this.newStatus.quote.id : undefined return this.newStatus.quote?.id
},
quoteThreadToggled: {
get() {
return this.newStatus.quote?.thread
},
set(value) {
this.newStatus.quote = {}
this.newStatus.quote.thread = value
this.newStatus.quote.id = value ? this.replyTo : ''
},
},
// Emoji stuff
emojiUserSuggestor() {
return suggestor({
emoji: [
...useEmojiStore().standardEmojiList,
...useEmojiStore().customEmoji,
],
store: this.$store,
})
},
emojiSuggestor() {
return suggestor({
emoji: [
...useEmojiStore().standardEmojiList,
...useEmojiStore().customEmoji,
],
})
},
emoji() {
return useEmojiStore().standardEmojiList
},
customEmoji() {
return useEmojiStore().customEmoji
},
// Length & Limits
statusLength() {
return this.newStatusContent.length
},
spoilerTextLength() {
return this.newStatus.spoilerText.length
},
statusLengthLimit() {
return useInstanceStore().limits.textLimit
},
hasStatusLengthLimit() {
return this.statusLengthLimit > 0
},
charactersLeft() {
return (
this.statusLengthLimit - (this.statusLength + this.spoilerTextLength)
)
},
isOverLengthLimit() {
return this.hasStatusLengthLimit && this.charactersLeft < 0
},
emptyStatus() {
return (
this.newStatus.status.trim() === '' && this.newStatus.files.length === 0
)
},
uploadFileLimitReached() {
return this.newStatus.files.length >= this.fileLimit
},
// Drafts
shouldAutoSaveDraft() {
return useMergedConfigStore().mergedConfig.autoSaveDraft
}, },
debouncedMaybeAutoSaveDraft() { debouncedMaybeAutoSaveDraft() {
return debounce(this.maybeAutoSaveDraft, 3000) return debounce(this.maybeAutoSaveDraft, 3000)
}, },
pollFormVisible() {
return this.newStatus.hasPoll
},
quoteFormVisible() {
return this.newStatus.hasQuote && !this.newStatus.quote.thread
},
shouldAutoSaveDraft() {
return useMergedConfigStore().mergedConfig.autoSaveDraft
},
autoSaveState() { autoSaveState() {
if (this.saveable) { if (this.saveable) {
return this.$t('post_status.auto_save_saving') return this.$t('post_status.auto_save_saving')
@ -452,9 +431,9 @@ const PostStatusForm = {
return ( return (
(this.newStatus.status || (this.newStatus.status ||
this.newStatus.spoilerText || this.newStatus.spoilerText ||
this.newStatus.files?.length || this.newStatus.files.length ||
this.newStatus.hasPoll || this.hasPoll ||
this.newStatus.hasQuote) && this.hasQuote) &&
this.saveable this.saveable
) )
}, },
@ -464,12 +443,78 @@ const PostStatusForm = {
!( !(
this.newStatus.status || this.newStatus.status ||
this.newStatus.spoilerText || this.newStatus.spoilerText ||
this.newStatus.files?.length || this.newStatus.files.length ||
this.newStatus.hasPoll || this.hasPoll ||
this.newStatus.hasQuote this.hasQuote
) )
) )
}, },
// Error handling
pollContentError() {
return (
this.pollFormVisible && this.newStatus.poll && this.newStatus.poll.error
)
},
// Featureset detection
postFormats() {
return useInstanceCapabilitiesStore().postFormats || []
},
safeDMEnabled() {
return useInstanceCapabilitiesStore().safeDM
},
pollsAvailable() {
return (
useInstanceCapabilitiesStore().pollsAvailable &&
useInstanceStore().limits.pollLimits.max_options >= 2 &&
this.disablePolls !== true
)
},
hideExtraActions() {
return this.disableDraft || this.hideDraft
},
quotingAvailable() {
if (!useInstanceCapabilitiesStore().quotingAvailable) {
return false
}
return this.disableQuotes !== true
},
// User configuration
userDefaultScope() {
return this.currentUser.default_scope
},
userDefaultPostContentType() {
return this.mergedConfig.postContentType
},
userDefaultScopeCopy() {
return this.mergedConfig.scopeCopy
},
userDefaultSensitive() {
return this.mergedConfig.sensitiveByDefault
},
showAllScopes() {
return !this.mergedConfig.minimalScopesMode
},
minimalScopesMode() {
return this.mergedConfig.minimalScopesMode
},
alwaysShowSubject() {
return this.mergedConfig.alwaysShowSubjectInput
},
hideScopeNotice() {
return (
this.disableNotice ||
useMergedConfigStore().mergedConfig.hideScopeNotice
)
},
// Global stuff
currentUser() {
return this.$store.state.users.currentUser
},
...mapState(useMergedConfigStore, ['mergedConfig']), ...mapState(useMergedConfigStore, ['mergedConfig']),
...mapState(useInterfaceStore, { ...mapState(useInterfaceStore, {
mobileLayout: (store) => store.mobileLayout, mobileLayout: (store) => store.mobileLayout,
@ -479,7 +524,7 @@ const PostStatusForm = {
newStatus: { newStatus: {
deep: true, deep: true,
handler() { handler() {
this.statusChanged() if (this.initialized) this.statusChanged()
}, },
}, },
saveable(val) { saveable(val) {
@ -505,18 +550,31 @@ const PostStatusForm = {
this.saveable = true this.saveable = true
this.saveInhibited = false this.saveInhibited = false
}, },
onMentionsLineUpdate(e) {
if (this.mentionsLineReadOnly) return
this.newStatus.mentionsLine = e
},
toggleQuoteForm() {
if (!this.hasQuote) {
this.newStatus.quote = {}
this.newStatus.quote.thread = false
this.newStatus.quote.id = null
this.newStatus.quote.url = ''
} else {
this.newStatus.quote = null
}
},
clearStatus() { clearStatus() {
const newStatus = this.newStatus const newStatus = this.newStatus
this.saveInhibited = true this.saveInhibited = true
this.newStatus = { this.newStatus = {
status: '', status: '',
mentionsLine: '',
spoilerText: '', spoilerText: '',
files: [], files: [],
visibility: newStatus.visibility, visibility: newStatus.visibility,
contentType: newStatus.contentType, contentType: newStatus.contentType,
poll: {}, poll: {},
hasPoll: false,
hasQuote: false,
quote: {}, quote: {},
mediaDescriptions: {}, mediaDescriptions: {},
} }
@ -562,7 +620,7 @@ const PostStatusForm = {
return return
} }
const poll = newStatus.hasPoll ? pollFormToMasto(newStatus.poll) : {} const poll = this.hasPoll ? pollFormToMasto(newStatus.poll) : {}
if (this.pollContentError) { if (this.pollContentError) {
this.error = this.pollContentError this.error = this.pollContentError
return return
@ -579,7 +637,7 @@ const PostStatusForm = {
} }
const postingOptions = { const postingOptions = {
status: newStatus.status, status: this.newStatusContent,
spoilerText: newStatus.spoilerText || null, spoilerText: newStatus.spoilerText || null,
visibility: newStatus.visibility, visibility: newStatus.visibility,
sensitive: newStatus.nsfw, sensitive: newStatus.nsfw,
@ -620,7 +678,7 @@ const PostStatusForm = {
statusPoster statusPoster
.postStatus({ .postStatus({
status: newStatus.status, status: this.newStatusContent,
spoilerText: newStatus.spoilerText || null, spoilerText: newStatus.spoilerText || null,
visibility: newStatus.visibility, visibility: newStatus.visibility,
sensitive: newStatus.nsfw, sensitive: newStatus.nsfw,
@ -857,7 +915,7 @@ const PostStatusForm = {
this.newStatus.visibility = visibility this.newStatus.visibility = visibility
}, },
togglePollForm() { togglePollForm() {
this.newStatus.hasPoll = !this.newStatus.hasPoll this.newStatus.poll = this.hasPoll ? null : {}
}, },
setPoll(poll) { setPoll(poll) {
this.newStatus.poll = poll this.newStatus.poll = poll
@ -872,14 +930,6 @@ const PostStatusForm = {
this.$refs.quoteForm.clear() this.$refs.quoteForm.clear()
} }
}, },
toggleQuoteForm() {
this.newStatus.hasQuote = !this.newStatus.hasQuote
this.newStatus.quote = {}
this.newStatus.quote.thread = false
this.newStatus.quote.id = null
this.newStatus.quote.url = ''
},
dismissScopeNotice() { dismissScopeNotice() {
useSyncConfigStore().setSimplePrefAndSave({ useSyncConfigStore().setSimplePrefAndSave({
path: 'hideScopeNotice', path: 'hideScopeNotice',
@ -947,14 +997,14 @@ const PostStatusForm = {
abandonDraft() { abandonDraft() {
return this.$store.dispatch('abandonDraft', { id: this.newStatus.id }) return this.$store.dispatch('abandonDraft', { id: this.newStatus.id })
}, },
getDraft(statusType, refId) { getDraft() {
const maybeDraft = this.$store.state.drafts.drafts[this.draftId] const maybeDraft = this.$store.state.drafts.drafts[this.draftId]
if (this.draftId && maybeDraft) { if (this.draftId && maybeDraft) {
return maybeDraft return maybeDraft
} else { } else {
const existingDrafts = this.$store.getters.draftsByTypeAndRefId( const existingDrafts = this.$store.getters.draftsByTypeAndRefId(
statusType, this.statusType,
refId, this.refId,
) )
if (existingDrafts.length) { if (existingDrafts.length) {

View file

@ -206,6 +206,8 @@
.inputs-wrapper { .inputs-wrapper {
padding: 0; padding: 0;
display: flex;
flex-direction: column;
} }
textarea.input.form-post-body { textarea.input.form-post-body {
@ -236,6 +238,10 @@
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
} }
.mentions-input {
border-bottom: 1px solid var(--border);
}
.character-counter { .character-counter {
position: absolute; position: absolute;
bottom: 0; bottom: 0;

View file

@ -2,6 +2,7 @@
<div <div
ref="form" ref="form"
class="post-status-form" class="post-status-form"
v-if="initialized"
> >
<form <form
autocomplete="off" autocomplete="off"
@ -10,7 +11,7 @@
> >
<div class="form-group"> <div class="form-group">
<div <div
v-if="!$store.state.users.currentUser.locked && newStatus.visibility == 'private' && !disableLockWarning" v-if="!currentUser.locked && newStatus.visibility == 'private' && !disableLockWarning"
class="visibility-notice notice-dismissible" class="visibility-notice notice-dismissible"
> >
<i18n-t <i18n-t
@ -58,7 +59,7 @@
</a> </a>
</p> </p>
<p <p
v-else-if="!hideScopeNotice && newStatus.visibility === 'private' && $store.state.users.currentUser.locked" v-else-if="!hideScopeNotice && newStatus.visibility === 'private' && currentUser.locked"
class="visibility-notice notice-dismissible" class="visibility-notice notice-dismissible"
> >
<span>{{ $t('post_status.scope_notice.private') }}</span> <span>{{ $t('post_status.scope_notice.private') }}</span>
@ -172,6 +173,16 @@
> >
</template> </template>
</EmojiInput> </EmojiInput>
<input
v-if="mentionsLine"
:value="mentionsLineReadOnly ? mentionsString : newStatus.mentionsLine"
@change="onMentionsLineUpdate"
type="text"
:placeholder="$t('post_status.mentions_line')"
:disabled="mentionsLineReadOnly || (posting && !optimisticPosting)"
size="1"
class="input mentions-input form-post-mentions unstyled"
>
<EmojiInput <EmojiInput
ref="emoji-input" ref="emoji-input"
v-model="newStatus.status" v-model="newStatus.status"
@ -264,10 +275,10 @@
/> />
<QuoteForm <QuoteForm
v-if="quotingAvailable" v-if="quotingAvailable"
:id="newStatus.quote.id"
ref="quoteForm" ref="quoteForm"
:visible="quoteFormVisible" :visible="quoteFormVisible"
:url="newStatus.quote.url" :id="newStatus.quote?.id"
:url="newStatus.quote?.url"
@update:url="url => newStatus.quote.url = url" @update:url="url => newStatus.quote.url = url"
@update:id="id => newStatus.quote.id = id" @update:id="id => newStatus.quote.id = id"
/> />