diff options
Diffstat (limited to 'src/components/post_status_form')
| -rw-r--r-- | src/components/post_status_form/post_status_form.js | 99 | ||||
| -rw-r--r-- | src/components/post_status_form/post_status_form.vue | 124 |
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> |
