refactor postStatusForm & add initial mentionsLine support
This commit is contained in:
parent
cd8b3ca924
commit
2e9c556c5f
4 changed files with 387 additions and 319 deletions
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue