aboutsummaryrefslogtreecommitdiff
path: root/src/components/post_status_form
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/post_status_form')
-rw-r--r--src/components/post_status_form/post_status_form.js99
-rw-r--r--src/components/post_status_form/post_status_form.vue124
2 files changed, 178 insertions, 45 deletions
diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
index 6c95873c..f9252f73 100644
--- a/src/components/post_status_form/post_status_form.js
+++ b/src/components/post_status_form/post_status_form.js
@@ -23,22 +23,33 @@ const PostStatusForm = {
props: [
'replyTo',
'repliedUser',
- 'attentions'
+ 'attentions',
+ 'copyMessageScope',
+ 'subject'
],
components: {
MediaUpload
},
mounted () {
this.resize(this.$refs.textarea)
+
+ if (this.replyTo) {
+ this.$refs.textarea.focus()
+ }
},
data () {
- let statusText = ''
+ const preset = this.$route.query.message
+ let statusText = preset || ''
if (this.replyTo) {
const currentUser = this.$store.state.users.currentUser
statusText = buildMentionsString({ user: this.repliedUser, attentions: this.attentions }, currentUser)
}
+ const scope = (this.copyMessageScope && this.$store.state.config.scopeCopy || this.copyMessageScope === 'direct')
+ ? this.copyMessageScope
+ : this.$store.state.users.currentUser.default_scope
+
return {
dropFiles: [],
submitDisabled: false,
@@ -46,18 +57,33 @@ const PostStatusForm = {
posting: false,
highlighted: 0,
newStatus: {
+ spoilerText: this.subject || '',
status: statusText,
- files: []
+ contentType: 'text/plain',
+ nsfw: false,
+ files: [],
+ visibility: scope
},
caret: 0
}
},
computed: {
+ vis () {
+ return {
+ public: { selected: this.newStatus.visibility === 'public' },
+ unlisted: { selected: this.newStatus.visibility === 'unlisted' },
+ private: { selected: this.newStatus.visibility === 'private' },
+ direct: { selected: this.newStatus.visibility === 'direct' }
+ }
+ },
candidates () {
const firstchar = this.textAtCaret.charAt(0)
if (firstchar === '@') {
- const matchedUsers = filter(this.users, (user) => (String(user.name + user.screen_name)).toUpperCase()
- .match(this.textAtCaret.slice(1).toUpperCase()))
+ const query = this.textAtCaret.slice(1).toUpperCase()
+ const matchedUsers = filter(this.users, (user) => {
+ return user.screen_name.toUpperCase().startsWith(query) ||
+ user.name && user.name.toUpperCase().startsWith(query)
+ })
if (matchedUsers.length <= 0) {
return false
}
@@ -71,16 +97,16 @@ const PostStatusForm = {
}))
} else if (firstchar === ':') {
if (this.textAtCaret === ':') { return }
- const matchedEmoji = filter(this.emoji.concat(this.customEmoji), (emoji) => emoji.shortcode.match(this.textAtCaret.slice(1)))
+ const matchedEmoji = filter(this.emoji.concat(this.customEmoji), (emoji) => emoji.shortcode.startsWith(this.textAtCaret.slice(1)))
if (matchedEmoji.length <= 0) {
return false
}
return map(take(matchedEmoji, 5), ({shortcode, image_url, utf}, index) => ({
- // eslint-disable-next-line camelcase
screen_name: `:${shortcode}:`,
name: '',
utf: utf || '',
- img: image_url,
+ // eslint-disable-next-line camelcase
+ img: utf ? '' : this.$store.state.instance.server + image_url,
highlighted: index === this.highlighted
}))
} else {
@@ -98,25 +124,43 @@ const PostStatusForm = {
return this.$store.state.users.users
},
emoji () {
- return this.$store.state.config.emoji || []
+ return this.$store.state.instance.emoji || []
},
customEmoji () {
- return this.$store.state.config.customEmoji || []
+ return this.$store.state.instance.customEmoji || []
},
statusLength () {
return this.newStatus.status.length
},
+ spoilerTextLength () {
+ return this.newStatus.spoilerText.length
+ },
statusLengthLimit () {
- return this.$store.state.config.textlimit
+ return this.$store.state.instance.textlimit
},
hasStatusLengthLimit () {
return this.statusLengthLimit > 0
},
charactersLeft () {
- return this.statusLengthLimit - this.statusLength
+ return this.statusLengthLimit - (this.statusLength + this.spoilerTextLength)
},
isOverLengthLimit () {
- return this.hasStatusLengthLimit && (this.statusLength > this.statusLengthLimit)
+ return this.hasStatusLengthLimit && (this.charactersLeft < 0)
+ },
+ scopeOptionsEnabled () {
+ return this.$store.state.instance.scopeOptionsEnabled
+ },
+ alwaysShowSubject () {
+ if (typeof this.$store.state.config.alwaysShowSubjectInput !== 'undefined') {
+ return this.$store.state.config.alwaysShowSubjectInput
+ } else if (typeof this.$store.state.instance.alwaysShowSubjectInput !== 'undefined') {
+ return this.$store.state.instance.alwaysShowSubjectInput
+ } else {
+ return this.$store.state.instance.scopeOptionsEnabled
+ }
+ },
+ formattingOptionsEnabled () {
+ return this.$store.state.instance.formattingOptionsEnabled
}
},
methods: {
@@ -184,14 +228,21 @@ const PostStatusForm = {
this.posting = true
statusPoster.postStatus({
status: newStatus.status,
+ spoilerText: newStatus.spoilerText || null,
+ visibility: newStatus.visibility,
+ sensitive: newStatus.nsfw,
media: newStatus.files,
store: this.$store,
- inReplyToStatusId: this.replyTo
+ inReplyToStatusId: this.replyTo,
+ contentType: newStatus.contentType
}).then((data) => {
if (!data.error) {
this.newStatus = {
status: '',
- files: []
+ spoilerText: '',
+ files: [],
+ visibility: newStatus.visibility,
+ contentType: newStatus.contentType
}
this.$emit('posted')
let el = this.$el.querySelector('textarea')
@@ -238,18 +289,20 @@ const PostStatusForm = {
e.dataTransfer.dropEffect = 'copy'
},
resize (e) {
- const target = e.target || e
- target.style.height = 'auto'
- const heightPx = target.scrollHeight - 10
- if (heightPx > 54) {
- target.style.height = `${target.scrollHeight - 10}px`
- }
- if (target.value === '') {
- target.style.height = '16px'
+ if (!e.target) { return }
+ const vertPadding = Number(window.getComputedStyle(e.target)['padding-top'].substr(0, 1)) +
+ Number(window.getComputedStyle(e.target)['padding-bottom'].substr(0, 1))
+ e.target.style.height = 'auto'
+ e.target.style.height = `${e.target.scrollHeight - vertPadding}px`
+ if (e.target.value === '') {
+ e.target.style.height = '16px'
}
},
clearError () {
this.error = null
+ },
+ changeVis (visibility) {
+ this.newStatus.visibility = visibility
}
}
}
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index 88627e3a..fcf5c873 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -2,6 +2,20 @@
<div class="post-status-form">
<form @submit.prevent="postStatus(newStatus)">
<div class="form-group" >
+ <i18n
+ v-if="!this.$store.state.users.currentUser.locked && this.newStatus.visibility == 'private'"
+ path="post_status.account_not_locked_warning"
+ tag="p"
+ class="visibility-notice">
+ <router-link to="/user-settings">{{ $t('post_status.account_not_locked_warning_link') }}</router-link>
+ </i18n>
+ <p v-if="this.newStatus.visibility == 'direct'" class="visibility-notice">{{ $t('post_status.direct_warning') }}</p>
+ <input
+ v-if="newStatus.spoilerText || alwaysShowSubject"
+ type="text"
+ :placeholder="$t('post_status.content_warning')"
+ v-model="newStatus.spoilerText"
+ class="form-cw">
<textarea
ref="textarea"
@click="setCaret"
@@ -18,16 +32,30 @@
@input="resize"
@paste="paste">
</textarea>
+ <div class="visibility-tray">
+ <span class="text-format" v-if="formattingOptionsEnabled">
+ <label for="post-content-type" class="select">
+ <select id="post-content-type" v-model="newStatus.contentType" class="form-control">
+ <option value="text/plain">{{$t('post_status.content_type.plain_text')}}</option>
+ <option value="text/html">HTML</option>
+ <option value="text/markdown">Markdown</option>
+ </select>
+ <i class="icon-down-open"></i>
+ </label>
+ </span>
+
+ <div v-if="scopeOptionsEnabled">
+ <i v-on:click="changeVis('direct')" class="icon-mail-alt" :class="vis.direct" :title="$t('post_status.scope.direct')"></i>
+ <i v-on:click="changeVis('private')" class="icon-lock" :class="vis.private" :title="$t('post_status.scope.private')"></i>
+ <i v-on:click="changeVis('unlisted')" class="icon-lock-open-alt" :class="vis.unlisted" :title="$t('post_status.scope.unlisted')"></i>
+ <i v-on:click="changeVis('public')" class="icon-globe" :class="vis.public" :title="$t('post_status.scope.public')"></i>
+ </div>
+ </div>
</div>
<div style="position:relative;" v-if="candidates">
<div class="autocomplete-panel">
<div v-for="candidate in candidates" @click="replace(candidate.utf || (candidate.screen_name + ' '))">
- <div v-if="candidate.highlighted" class="autocomplete">
- <span v-if="candidate.img"><img :src="candidate.img"></span>
- <span v-else>{{candidate.utf}}</span>
- <span>{{candidate.screen_name}}<small>{{candidate.name}}</small></span>
- </div>
- <div v-else class="autocomplete">
+ <div class="autocomplete" :class="{ highlighted: candidate.highlighted }">
<span v-if="candidate.img"><img :src="candidate.img"></img></span>
<span v-else>{{candidate.utf}}</span>
<span>{{candidate.screen_name}}<small>{{candidate.name}}</small></span>
@@ -50,14 +78,20 @@
<i class="icon-cancel" @click="clearError"></i>
</div>
<div class="attachments">
- <div class="media-upload-container attachment" v-for="file in newStatus.files">
+ <div class="media-upload-wrapper" v-for="file in newStatus.files">
<i class="fa icon-cancel" @click="removeMediaFile(file)"></i>
- <img class="thumbnail media-upload" :src="file.image" v-if="type(file) === 'image'"></img>
- <video v-if="type(file) === 'video'" :src="file.image" controls></video>
- <audio v-if="type(file) === 'audio'" :src="file.image" controls></audio>
- <a v-if="type(file) === 'unknown'" :href="file.image">{{file.url}}</a>
+ <div class="media-upload-container attachment">
+ <img class="thumbnail media-upload" :src="file.image" v-if="type(file) === 'image'"></img>
+ <video v-if="type(file) === 'video'" :src="file.image" controls></video>
+ <audio v-if="type(file) === 'audio'" :src="file.image" controls></audio>
+ <a v-if="type(file) === 'unknown'" :href="file.image">{{file.url}}</a>
+ </div>
</div>
</div>
+ <div class="upload_settings" v-if="newStatus.files.length > 0">
+ <input type="checkbox" id="filesSensitive" v-model="newStatus.nsfw">
+ <label for="filesSensitive">{{$t('post_status.attachments_sensitive')}}</label>
+ </div>
</form>
</div>
</template>
@@ -105,14 +139,49 @@
text-align: center;
}
+ .media-upload-wrapper {
+ flex: 0 0 auto;
+ max-width: 100%;
+ min-width: 50px;
+ margin-right: .2em;
+ margin-bottom: .5em;
+
+ .icon-cancel {
+ display: inline-block;
+ position: static;
+ margin: 0;
+ padding-bottom: 0;
+ margin-left: $fallback--attachmentRadius;
+ margin-left: var(--attachmentRadius, $fallback--attachmentRadius);
+ background-color: $fallback--fg;
+ background-color: var(--btn, $fallback--fg);
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+ }
+
.attachments {
padding: 0 0.5em;
.attachment {
+ margin: 0;
position: relative;
+ flex: 0 0 auto;
border: 1px solid $fallback--border;
border: 1px solid var(--border, $fallback--border);
- margin: 0.5em 0.8em 0.2em 0;
+ text-align: center;
+
+ audio {
+ min-width: 300px;
+ flex: 1 0 auto;
+ }
+
+ a {
+ display: block;
+ text-align: left;
+ line-height: 1.2;
+ padding: .5em;
+ }
}
i {
@@ -135,10 +204,6 @@
cursor: not-allowed;
}
- .icon-cancel {
- cursor: pointer;
- }
-
form {
display: flex;
flex-direction: column;
@@ -152,7 +217,15 @@
line-height:24px;
}
- form textarea {
+ form textarea.form-cw {
+ line-height:16px;
+ resize: none;
+ overflow: hidden;
+ transition: min-height 200ms 100ms;
+ min-height: 1px;
+ }
+
+ form textarea.form-control {
line-height:16px;
resize: none;
overflow: hidden;
@@ -161,7 +234,7 @@
box-sizing: content-box;
}
- form textarea:focus {
+ form textarea.form-control:focus {
min-height: 48px;
}
@@ -185,11 +258,13 @@
position: absolute;
z-index: 1;
box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5);
+ // this doesn't match original but i don't care, making it uniform.
+ box-shadow: var(--popupShadow);
min-width: 75%;
- background: $fallback--btn;
- background: var(--btn, $fallback--btn);
- color: $fallback--lightFg;
- color: var(--lightFg, $fallback--lightFg);
+ background: $fallback--bg;
+ background: var(--bg, $fallback--bg);
+ color: $fallback--lightText;
+ color: var(--lightText, $fallback--lightText);
}
.autocomplete {
@@ -216,6 +291,11 @@
color: $fallback--faint;
color: var(--faint, $fallback--faint);
}
+
+ &.highlighted {
+ background-color: $fallback--fg;
+ background-color: var(--lightBg, $fallback--fg);
+ }
}
}
</style>