diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 5cb2acba1..490ac4d0b 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -184,7 +184,7 @@ const getStaticEmoji = async ({ store }) => { imageUrl: false, replacement: values[key] } - }) + }).sort((a, b) => a.displayText - b.displayText) store.dispatch('setInstanceOption', { name: 'emoji', value: emoji }) } else { throw (res) @@ -203,14 +203,16 @@ const getCustomEmoji = async ({ store }) => { if (res.ok) { const result = await res.json() const values = Array.isArray(result) ? Object.assign({}, ...result) : result - const emoji = Object.keys(values).map((key) => { - const imageUrl = values[key].image_url + const emoji = Object.entries(values).map(([key, value]) => { + const imageUrl = value.image_url return { displayText: key, - imageUrl: imageUrl ? store.state.instance.server + imageUrl : values[key], + imageUrl: imageUrl ? store.state.instance.server + imageUrl : value, + tags: imageUrl ? value.tags.sort((a, b) => a > b ? 1 : 0) : ['utf'], replacement: `:${key}: ` } - }) + // Technically could use tags but those are kinda useless right now, should have been "pack" field, that would be more useful + }).sort((a, b) => a.displayText.toLowerCase() > b.displayText.toLowerCase() ? 1 : 0) store.dispatch('setInstanceOption', { name: 'customEmoji', value: emoji }) store.dispatch('setInstanceOption', { name: 'pleromaBackend', value: true }) } else { diff --git a/src/components/emoji-input/emoji-input.js b/src/components/emoji_input/emoji_input.js similarity index 78% rename from src/components/emoji-input/emoji-input.js rename to src/components/emoji_input/emoji_input.js index fab64a697..c74c531bd 100644 --- a/src/components/emoji-input/emoji-input.js +++ b/src/components/emoji_input/emoji_input.js @@ -1,4 +1,5 @@ import Completion from '../../services/completion/completion.js' +import EmojiPicker from '../emoji_picker/emoji_picker.vue' import { take } from 'lodash' /** @@ -52,6 +53,21 @@ const EmojiInput = { */ required: true, type: String + }, + emojiPicker: { + required: false, + type: Boolean, + default: false + }, + emojiPickerExternalTrigger: { + required: false, + type: Boolean, + default: false + }, + stickerPicker: { + required: false, + type: Boolean, + default: false } }, data () { @@ -60,9 +76,15 @@ const EmojiInput = { highlighted: 0, caret: 0, focused: false, - blurTimeout: null + blurTimeout: null, + showPicker: false, + spamMode: false, + disableClickOutside: false } }, + components: { + EmojiPicker + }, computed: { suggestions () { const firstchar = this.textAtCaret.charAt(0) @@ -79,8 +101,8 @@ const EmojiInput = { highlighted: index === this.highlighted })) }, - showPopup () { - return this.focused && this.suggestions && this.suggestions.length > 0 + showSuggestions () { + return this.focused && this.suggestions && this.suggestions.length > 0 && !this.showPicker }, textAtCaret () { return (this.wordAtCaret || {}).word || '' @@ -120,11 +142,42 @@ const EmojiInput = { } }, methods: { + triggerShowPicker () { + this.showPicker = true + // This temporarily disables "click outside" handler + // since external trigger also means click originates + // from outside, thus preventing picker from opening + this.disableClickOutside = true + setTimeout(() => { + this.disableClickOutside = false + }, 0) + }, + togglePicker () { + this.showPicker = !this.showPicker + }, replace (replacement) { const newValue = Completion.replaceWord(this.value, this.wordAtCaret, replacement) this.$emit('input', newValue) this.caret = 0 }, + insert ({ insertion, spamMode }) { + const newValue = [ + this.value.substring(0, this.caret), + insertion, + this.value.substring(this.caret) + ].join('') + this.spamMode = spamMode + this.$emit('input', newValue) + const position = this.caret + insertion.length + + this.$nextTick(function () { + // Re-focus inputbox after clicking suggestion + this.input.elm.focus() + // Set selection right after the replacement instead of the very end + this.input.elm.setSelectionRange(position, position) + this.caret = position + }) + }, replaceText (e, suggestion) { const len = this.suggestions.length || 0 if (this.textAtCaret.length === 1) { return } @@ -148,7 +201,7 @@ const EmojiInput = { }, cycleBackward (e) { const len = this.suggestions.length || 0 - if (len > 0) { + if (len > 1) { this.highlighted -= 1 if (this.highlighted < 0) { this.highlighted = this.suggestions.length - 1 @@ -160,7 +213,7 @@ const EmojiInput = { }, cycleForward (e) { const len = this.suggestions.length || 0 - if (len > 0) { + if (len > 1) { this.highlighted += 1 if (this.highlighted >= len) { this.highlighted = 0 @@ -191,6 +244,9 @@ const EmojiInput = { this.blurTimeout = null } + if (!this.spamMode) { + this.showPicker = false + } this.focused = true this.setCaret(e) this.resize() @@ -227,6 +283,7 @@ const EmojiInput = { } }, onInput (e) { + this.showPicker = false this.setCaret(e) this.$emit('input', e.target.value) }, @@ -235,6 +292,18 @@ const EmojiInput = { this.resize() this.$emit('input', e.target.value) }, + onClickOutside (e) { + if (this.disableClickOutside) return + this.showPicker = false + }, + onStickerUploaded (e) { + this.showPicker = false + this.$emit('sticker-uploaded', e) + }, + onStickerUploadFailed (e) { + this.showPicker = false + this.$emit('sticker-upload-Failed', e) + }, setCaret ({ target: { selectionStart } }) { this.caret = selectionStart }, @@ -243,6 +312,7 @@ const EmojiInput = { if (!panel) return const { offsetHeight, offsetTop } = this.input.elm this.$refs.panel.style.top = (offsetTop + offsetHeight) + 'px' + this.$refs.picker.$el.style.top = (offsetTop + offsetHeight) + 'px' } } } diff --git a/src/components/emoji-input/emoji-input.vue b/src/components/emoji_input/emoji_input.vue similarity index 69% rename from src/components/emoji-input/emoji-input.vue rename to src/components/emoji_input/emoji_input.vue index 48739ec84..b077e6e9b 100644 --- a/src/components/emoji-input/emoji-input.vue +++ b/src/components/emoji_input/emoji_input.vue @@ -1,10 +1,32 @@ - + diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 40bbf6d4e..d468be76f 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -1,12 +1,11 @@ import statusPoster from '../../services/status_poster/status_poster.service.js' import MediaUpload from '../media_upload/media_upload.vue' import ScopeSelector from '../scope_selector/scope_selector.vue' -import EmojiInput from '../emoji-input/emoji-input.vue' +import EmojiInput from '../emoji_input/emoji_input.vue' import PollForm from '../poll/poll_form.vue' -import StickerPicker from '../sticker_picker/sticker_picker.vue' import fileTypeService from '../../services/file_type/file_type.service.js' import { reject, map, uniqBy } from 'lodash' -import suggestor from '../emoji-input/suggestor.js' +import suggestor from '../emoji_input/suggestor.js' const buildMentionsString = ({ user, attentions }, currentUser) => { let allAttentions = [...attentions] @@ -35,7 +34,6 @@ const PostStatusForm = { MediaUpload, EmojiInput, PollForm, - StickerPicker, ScopeSelector }, mounted () { @@ -84,8 +82,7 @@ const PostStatusForm = { contentType }, caret: 0, - pollFormVisible: false, - stickerPickerVisible: false + pollFormVisible: false } }, computed: { @@ -161,12 +158,6 @@ const PostStatusForm = { safeDMEnabled () { return this.$store.state.instance.safeDM }, - stickersAvailable () { - if (this.$store.state.instance.stickers) { - return this.$store.state.instance.stickers.length > 0 - } - return 0 - }, pollsAvailable () { return this.$store.state.instance.pollsAvailable && this.$store.state.instance.pollLimits.max_options >= 2 @@ -222,7 +213,6 @@ const PostStatusForm = { poll: {} } this.pollFormVisible = false - this.stickerPickerVisible = false this.$refs.mediaUpload.clearFile() this.clearPollForm() this.$emit('posted') @@ -239,7 +229,6 @@ const PostStatusForm = { addMediaFile (fileInfo) { this.newStatus.files.push(fileInfo) this.enableSubmit() - this.stickerPickerVisible = false }, removeMediaFile (fileInfo) { let index = this.newStatus.files.indexOf(fileInfo) @@ -293,20 +282,16 @@ const PostStatusForm = { target.style.height = null } }, + showEmojiPicker () { + this.$refs['textarea'].focus() + this.$refs['emoji-input'].triggerShowPicker() + }, clearError () { this.error = null }, changeVis (visibility) { this.newStatus.visibility = visibility }, - toggleStickerPicker () { - this.stickerPickerVisible = !this.stickerPickerVisible - }, - clearStickerPicker () { - if (this.$refs.stickerPicker) { - this.$refs.stickerPicker.clear() - } - }, togglePollForm () { this.pollFormVisible = !this.pollFormVisible }, diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index d29d47e4c..026cb8fe3 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -61,6 +61,7 @@ @@ -73,9 +74,15 @@ >