Merge branch 'from/develop/tusooa/grouped-emoji-picker' into shigusegubu-vue3
* from/develop/tusooa/grouped-emoji-picker: (34 commits) Fix emoji picker lint Fix emoji picker lint Tweak efficiency when changing filter keywords in emoji picker Use trimmed keyword for filtering emojis Limit the width of unsupported multichar emojis Make emoji picker work with vue3 Make StillImage react to src changes Lint Add English translation for unicode emoji group names Add icons for unicode emoji groups Make emoji picker use grouped unicode emojis Generate grouped unicode emojis from unicode-emoji-json Scroll active tab header into view in emoji picker Clean up emoji picker css Use StillImage to render emojis in emoji picker Fix error on emoji picker first load Group emojis only by pack and remove pack: prefix Lint Lazy-load emoji picker in post form Fix sticker picker heading tab ...
This commit is contained in:
commit
14be662918
20 changed files with 413 additions and 1569 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -7,3 +7,4 @@ test/e2e/reports
|
||||||
selenium-debug.log
|
selenium-debug.log
|
||||||
.idea/
|
.idea/
|
||||||
config/local.json
|
config/local.json
|
||||||
|
static/emoji.json
|
||||||
|
|
|
@ -18,6 +18,9 @@ console.log(
|
||||||
var spinner = ora('building for production...')
|
var spinner = ora('building for production...')
|
||||||
spinner.start()
|
spinner.start()
|
||||||
|
|
||||||
|
var updateEmoji = require('./update-emoji').updateEmoji
|
||||||
|
updateEmoji()
|
||||||
|
|
||||||
var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory)
|
var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory)
|
||||||
rm('-rf', assetsPath)
|
rm('-rf', assetsPath)
|
||||||
mkdir('-p', assetsPath)
|
mkdir('-p', assetsPath)
|
||||||
|
|
|
@ -10,6 +10,9 @@ var webpackConfig = process.env.NODE_ENV === 'testing'
|
||||||
? require('./webpack.prod.conf')
|
? require('./webpack.prod.conf')
|
||||||
: require('./webpack.dev.conf')
|
: require('./webpack.dev.conf')
|
||||||
|
|
||||||
|
var updateEmoji = require('./update-emoji').updateEmoji
|
||||||
|
updateEmoji()
|
||||||
|
|
||||||
// default port where dev server listens for incoming traffic
|
// default port where dev server listens for incoming traffic
|
||||||
var port = process.env.PORT || config.dev.port
|
var port = process.env.PORT || config.dev.port
|
||||||
// Define HTTP proxies to your custom API backend
|
// Define HTTP proxies to your custom API backend
|
||||||
|
|
27
build/update-emoji.js
Normal file
27
build/update-emoji.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
updateEmoji () {
|
||||||
|
const emojis = require('unicode-emoji-json/data-by-group')
|
||||||
|
const fs = require('fs')
|
||||||
|
|
||||||
|
Object.keys(emojis)
|
||||||
|
.map(k => {
|
||||||
|
emojis[k].map(e => {
|
||||||
|
delete e.unicode_version
|
||||||
|
delete e.emoji_version
|
||||||
|
delete e.skin_tone_support_unicode_version
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const res = {}
|
||||||
|
Object.keys(emojis)
|
||||||
|
.map(k => {
|
||||||
|
const groupId = k.replace('&', 'and').replace(/ /g, '-').toLowerCase()
|
||||||
|
res[groupId] = emojis[k]
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('Updating emojis...')
|
||||||
|
fs.writeFileSync('static/emoji.json', JSON.stringify(res))
|
||||||
|
console.log('Done.')
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,6 +35,7 @@
|
||||||
"js-cookie": "3.0.1",
|
"js-cookie": "3.0.1",
|
||||||
"localforage": "1.10.0",
|
"localforage": "1.10.0",
|
||||||
"parse-link-header": "2.0.0",
|
"parse-link-header": "2.0.0",
|
||||||
|
"lozad": "^1.16.0",
|
||||||
"phoenix": "1.6.2",
|
"phoenix": "1.6.2",
|
||||||
"punycode.js": "2.1.0",
|
"punycode.js": "2.1.0",
|
||||||
"qrcode": "1.5.0",
|
"qrcode": "1.5.0",
|
||||||
|
@ -117,6 +118,7 @@
|
||||||
"stylelint-config-standard": "20.0.0",
|
"stylelint-config-standard": "20.0.0",
|
||||||
"stylelint-rscss": "0.4.0",
|
"stylelint-rscss": "0.4.0",
|
||||||
"vue-loader": "^17.0.0",
|
"vue-loader": "^17.0.0",
|
||||||
|
"unicode-emoji-json": "^0.3.0",
|
||||||
"vue-style-loader": "4.1.3",
|
"vue-style-loader": "4.1.3",
|
||||||
"webpack": "5.74.0",
|
"webpack": "5.74.0",
|
||||||
"webpack-dev-middleware": "3.7.3",
|
"webpack-dev-middleware": "3.7.3",
|
||||||
|
|
|
@ -205,7 +205,6 @@ const EmojiInput = {
|
||||||
},
|
},
|
||||||
triggerShowPicker () {
|
triggerShowPicker () {
|
||||||
this.showPicker = true
|
this.showPicker = true
|
||||||
this.$refs.picker.startEmojiLoad()
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.scrollIntoView()
|
this.scrollIntoView()
|
||||||
this.focusPickerInput()
|
this.focusPickerInput()
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
v-if="enableEmojiPicker"
|
v-if="enableEmojiPicker"
|
||||||
ref="picker"
|
ref="picker"
|
||||||
:class="{ hide: !showPicker }"
|
:class="{ hide: !showPicker }"
|
||||||
|
:showing="showPicker"
|
||||||
:enable-sticker-picker="enableStickerPicker"
|
:enable-sticker-picker="enableStickerPicker"
|
||||||
class="emoji-picker-panel"
|
class="emoji-picker-panel"
|
||||||
@emoji="insert"
|
@emoji="insert"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* suggest - generates a suggestor function to be used by emoji-input
|
* suggest - generates a suggestor function to be used by emoji-input
|
||||||
* data: object providing source information for specific types of suggestions:
|
* data: object providing source information for specific types of suggestions:
|
||||||
* data.emoji - optional, an array of all emoji available i.e.
|
* data.emoji - optional, an array of all emoji available i.e.
|
||||||
* (state.instance.emoji + state.instance.customEmoji)
|
* (getters.standardEmojiList + state.instance.customEmoji)
|
||||||
* data.users - optional, an array of all known users
|
* data.users - optional, an array of all known users
|
||||||
* updateUsersList - optional, a function to search and append to users
|
* updateUsersList - optional, a function to search and append to users
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,25 +1,50 @@
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
import Checkbox from '../checkbox/checkbox.vue'
|
import Checkbox from '../checkbox/checkbox.vue'
|
||||||
|
import StillImage from '../still-image/still-image.vue'
|
||||||
|
import lozad from 'lozad'
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
faBoxOpen,
|
faBoxOpen,
|
||||||
faStickyNote,
|
faStickyNote,
|
||||||
faSmileBeam
|
faSmileBeam,
|
||||||
|
faSmile,
|
||||||
|
faUser,
|
||||||
|
faPaw,
|
||||||
|
faIceCream,
|
||||||
|
faBus,
|
||||||
|
faBasketballBall,
|
||||||
|
faLightbulb,
|
||||||
|
faCode,
|
||||||
|
faFlag
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
import { trim } from 'lodash'
|
import { debounce, trim } from 'lodash'
|
||||||
|
|
||||||
library.add(
|
library.add(
|
||||||
faBoxOpen,
|
faBoxOpen,
|
||||||
faStickyNote,
|
faStickyNote,
|
||||||
faSmileBeam
|
faSmileBeam,
|
||||||
|
faSmile,
|
||||||
|
faUser,
|
||||||
|
faPaw,
|
||||||
|
faIceCream,
|
||||||
|
faBus,
|
||||||
|
faBasketballBall,
|
||||||
|
faLightbulb,
|
||||||
|
faCode,
|
||||||
|
faFlag
|
||||||
)
|
)
|
||||||
|
|
||||||
// At widest, approximately 20 emoji are visible in a row,
|
const UNICODE_EMOJI_GROUP_ICON = {
|
||||||
// loading 3 rows, could be overkill for narrow picker
|
'smileys-and-emotion': 'smile',
|
||||||
const LOAD_EMOJI_BY = 60
|
'people-and-body': 'user',
|
||||||
|
'animals-and-nature': 'paw',
|
||||||
// When to start loading new batch emoji, in pixels
|
'food-and-drink': 'ice-cream',
|
||||||
const LOAD_EMOJI_MARGIN = 64
|
'travel-and-places': 'bus',
|
||||||
|
activities: 'basketball-ball',
|
||||||
|
objects: 'lightbulb',
|
||||||
|
symbols: 'code',
|
||||||
|
flags: 'flag'
|
||||||
|
}
|
||||||
|
|
||||||
const filterByKeyword = (list, keyword = '') => {
|
const filterByKeyword = (list, keyword = '') => {
|
||||||
if (keyword === '') return list
|
if (keyword === '') return list
|
||||||
|
@ -44,6 +69,10 @@ const EmojiPicker = {
|
||||||
required: false,
|
required: false,
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
|
},
|
||||||
|
showing: {
|
||||||
|
required: true,
|
||||||
|
type: Boolean
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
|
@ -53,16 +82,26 @@ const EmojiPicker = {
|
||||||
showingStickers: false,
|
showingStickers: false,
|
||||||
groupsScrolledClass: 'scrolled-top',
|
groupsScrolledClass: 'scrolled-top',
|
||||||
keepOpen: false,
|
keepOpen: false,
|
||||||
customEmojiBufferSlice: LOAD_EMOJI_BY,
|
|
||||||
customEmojiTimeout: null,
|
customEmojiTimeout: null,
|
||||||
customEmojiLoadAllConfirmed: false
|
// Lazy-load only after the first time `showing` becomes true.
|
||||||
|
contentLoaded: false,
|
||||||
|
groupRefs: {},
|
||||||
|
emojiRefs: {},
|
||||||
|
filteredEmojiGroups: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
StickerPicker: defineAsyncComponent(() => import('../sticker_picker/sticker_picker.vue')),
|
StickerPicker: defineAsyncComponent(() => import('../sticker_picker/sticker_picker.vue')),
|
||||||
Checkbox
|
Checkbox,
|
||||||
|
StillImage
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
setGroupRef (name) {
|
||||||
|
return el => { this.groupRefs[name] = el }
|
||||||
|
},
|
||||||
|
setEmojiRef (name) {
|
||||||
|
return el => { this.emojiRefs[name] = el }
|
||||||
|
},
|
||||||
onStickerUploaded (e) {
|
onStickerUploaded (e) {
|
||||||
this.$emit('sticker-uploaded', e)
|
this.$emit('sticker-uploaded', e)
|
||||||
},
|
},
|
||||||
|
@ -77,10 +116,38 @@ const EmojiPicker = {
|
||||||
const target = (e && e.target) || this.$refs['emoji-groups']
|
const target = (e && e.target) || this.$refs['emoji-groups']
|
||||||
this.updateScrolledClass(target)
|
this.updateScrolledClass(target)
|
||||||
this.scrolledGroup(target)
|
this.scrolledGroup(target)
|
||||||
this.triggerLoadMore(target)
|
},
|
||||||
|
scrolledGroup (target) {
|
||||||
|
const top = target.scrollTop + 5
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.allEmojiGroups.forEach(group => {
|
||||||
|
const ref = this.groupRefs['group-' + group.id]
|
||||||
|
if (ref && ref.offsetTop <= top) {
|
||||||
|
this.activeGroup = group.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.scrollHeader()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
scrollHeader () {
|
||||||
|
// Scroll the active tab's header into view
|
||||||
|
const headerRef = this.groupRefs['group-header-' + this.activeGroup]
|
||||||
|
const left = headerRef.offsetLeft
|
||||||
|
const right = left + headerRef.offsetWidth
|
||||||
|
const headerCont = this.$refs.header
|
||||||
|
const currentScroll = headerCont.scrollLeft
|
||||||
|
const currentScrollRight = currentScroll + headerCont.clientWidth
|
||||||
|
const setScroll = s => { headerCont.scrollLeft = s }
|
||||||
|
|
||||||
|
const margin = 7 // .emoji-tabs-item: padding
|
||||||
|
if (left - margin < currentScroll) {
|
||||||
|
setScroll(left - margin)
|
||||||
|
} else if (right + margin > currentScrollRight) {
|
||||||
|
setScroll(right + margin - headerCont.clientWidth)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
highlight (key) {
|
highlight (key) {
|
||||||
const ref = this.$refs['group-' + key]
|
const ref = this.groupRefs['group-' + key]
|
||||||
const top = ref.offsetTop
|
const top = ref.offsetTop
|
||||||
this.setShowStickers(false)
|
this.setShowStickers(false)
|
||||||
this.activeGroup = key
|
this.activeGroup = key
|
||||||
|
@ -97,72 +164,89 @@ const EmojiPicker = {
|
||||||
this.groupsScrolledClass = 'scrolled-middle'
|
this.groupsScrolledClass = 'scrolled-middle'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
triggerLoadMore (target) {
|
|
||||||
const ref = this.$refs['group-end-custom']
|
|
||||||
if (!ref) return
|
|
||||||
const bottom = ref.offsetTop + ref.offsetHeight
|
|
||||||
|
|
||||||
const scrollerBottom = target.scrollTop + target.clientHeight
|
|
||||||
const scrollerTop = target.scrollTop
|
|
||||||
const scrollerMax = target.scrollHeight
|
|
||||||
|
|
||||||
// Loads more emoji when they come into view
|
|
||||||
const approachingBottom = bottom - scrollerBottom < LOAD_EMOJI_MARGIN
|
|
||||||
// Always load when at the very top in case there's no scroll space yet
|
|
||||||
const atTop = scrollerTop < 5
|
|
||||||
// Don't load when looking at unicode category or at the very bottom
|
|
||||||
const bottomAboveViewport = bottom < scrollerTop || scrollerBottom === scrollerMax
|
|
||||||
if (!bottomAboveViewport && (approachingBottom || atTop)) {
|
|
||||||
this.loadEmoji()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
scrolledGroup (target) {
|
|
||||||
const top = target.scrollTop + 5
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.emojisView.forEach(group => {
|
|
||||||
const ref = this.$refs['group-' + group.id]
|
|
||||||
if (ref.offsetTop <= top) {
|
|
||||||
this.activeGroup = group.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
loadEmoji () {
|
|
||||||
const allLoaded = this.customEmojiBuffer.length === this.filteredEmoji.length
|
|
||||||
|
|
||||||
if (allLoaded) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.customEmojiBufferSlice += LOAD_EMOJI_BY
|
|
||||||
},
|
|
||||||
startEmojiLoad (forceUpdate = false) {
|
|
||||||
if (!forceUpdate) {
|
|
||||||
this.keyword = ''
|
|
||||||
}
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$refs['emoji-groups'].scrollTop = 0
|
|
||||||
})
|
|
||||||
const bufferSize = this.customEmojiBuffer.length
|
|
||||||
const bufferPrefilledAll = bufferSize === this.filteredEmoji.length
|
|
||||||
if (bufferPrefilledAll && !forceUpdate) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.customEmojiBufferSlice = LOAD_EMOJI_BY
|
|
||||||
},
|
|
||||||
toggleStickers () {
|
toggleStickers () {
|
||||||
this.showingStickers = !this.showingStickers
|
this.showingStickers = !this.showingStickers
|
||||||
},
|
},
|
||||||
setShowStickers (value) {
|
setShowStickers (value) {
|
||||||
this.showingStickers = value
|
this.showingStickers = value
|
||||||
|
},
|
||||||
|
filterByKeyword (list, keyword) {
|
||||||
|
return filterByKeyword(list, keyword)
|
||||||
|
},
|
||||||
|
initializeLazyLoad () {
|
||||||
|
this.destroyLazyLoad()
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$lozad = lozad('.still-image.emoji-picker-emoji', {
|
||||||
|
load: el => {
|
||||||
|
const name = el.getAttribute('data-emoji-name')
|
||||||
|
const vn = this.emojiRefs[name]
|
||||||
|
if (!vn) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vn.loadLazy()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.$lozad.observe()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
waitForDomAndInitializeLazyLoad () {
|
||||||
|
this.$nextTick(() => this.initializeLazyLoad())
|
||||||
|
},
|
||||||
|
destroyLazyLoad () {
|
||||||
|
if (this.$lozad) {
|
||||||
|
if (this.$lozad.observer) {
|
||||||
|
this.$lozad.observer.disconnect()
|
||||||
|
}
|
||||||
|
if (this.$lozad.mutationObserver) {
|
||||||
|
this.$lozad.mutationObserver.disconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onShowing () {
|
||||||
|
const oldContentLoaded = this.contentLoaded
|
||||||
|
this.contentLoaded = true
|
||||||
|
this.waitForDomAndInitializeLazyLoad()
|
||||||
|
this.filteredEmojiGroups = this.getFilteredEmojiGroups()
|
||||||
|
if (!oldContentLoaded) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.defaultGroup) {
|
||||||
|
this.highlight(this.defaultGroup)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getFilteredEmojiGroups () {
|
||||||
|
return this.allEmojiGroups
|
||||||
|
.map(group => ({
|
||||||
|
...group,
|
||||||
|
emojis: filterByKeyword(group.emojis, trim(this.keyword))
|
||||||
|
}))
|
||||||
|
.filter(group => group.emojis.length > 0)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
keyword () {
|
keyword () {
|
||||||
this.customEmojiLoadAllConfirmed = false
|
|
||||||
this.onScroll()
|
this.onScroll()
|
||||||
this.startEmojiLoad(true)
|
this.debouncedHandleKeywordChange()
|
||||||
|
},
|
||||||
|
allCustomGroups () {
|
||||||
|
this.waitForDomAndInitializeLazyLoad()
|
||||||
|
this.filteredEmojiGroups = this.getFilteredEmojiGroups()
|
||||||
|
},
|
||||||
|
showing (val) {
|
||||||
|
if (val) {
|
||||||
|
this.onShowing()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
if (this.showing) {
|
||||||
|
this.onShowing()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
this.destroyLazyLoad()
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
activeGroupView () {
|
activeGroupView () {
|
||||||
|
@ -174,39 +258,33 @@ const EmojiPicker = {
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
},
|
},
|
||||||
filteredEmoji () {
|
allCustomGroups () {
|
||||||
return filterByKeyword(
|
return this.$store.getters.groupedCustomEmojis
|
||||||
this.$store.state.instance.customEmoji || [],
|
|
||||||
trim(this.keyword)
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
customEmojiBuffer () {
|
defaultGroup () {
|
||||||
return this.filteredEmoji.slice(0, this.customEmojiBufferSlice)
|
return Object.keys(this.allCustomGroups)[0]
|
||||||
},
|
},
|
||||||
emojis () {
|
unicodeEmojiGroups () {
|
||||||
const standardEmojis = this.$store.state.instance.emoji || []
|
return this.$store.getters.standardEmojiGroupList.map(group => ({
|
||||||
const customEmojis = this.customEmojiBuffer
|
id: `standard-${group.id}`,
|
||||||
|
text: this.$t(`emoji.unicode_groups.${group.id}`),
|
||||||
return [
|
icon: UNICODE_EMOJI_GROUP_ICON[group.id],
|
||||||
{
|
emojis: group.emojis
|
||||||
id: 'custom',
|
}))
|
||||||
text: this.$t('emoji.custom'),
|
|
||||||
icon: 'smile-beam',
|
|
||||||
emojis: customEmojis
|
|
||||||
},
|
},
|
||||||
{
|
allEmojiGroups () {
|
||||||
id: 'standard',
|
return Object.entries(this.allCustomGroups)
|
||||||
text: this.$t('emoji.unicode'),
|
.map(([_, v]) => v)
|
||||||
icon: 'box-open',
|
.concat(this.unicodeEmojiGroups)
|
||||||
emojis: filterByKeyword(standardEmojis, trim(this.keyword))
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
emojisView () {
|
|
||||||
return this.emojis.filter(value => value.emojis.length > 0)
|
|
||||||
},
|
},
|
||||||
stickerPickerEnabled () {
|
stickerPickerEnabled () {
|
||||||
return (this.$store.state.instance.stickers || []).length !== 0
|
return (this.$store.state.instance.stickers || []).length !== 0
|
||||||
|
},
|
||||||
|
debouncedHandleKeywordChange () {
|
||||||
|
return debounce(() => {
|
||||||
|
this.waitForDomAndInitializeLazyLoad()
|
||||||
|
this.filteredEmojiGroups = this.getFilteredEmojiGroups()
|
||||||
|
}, 500)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
|
$emoji-picker-header-height: 36px;
|
||||||
|
$emoji-picker-header-picture-width: 32px;
|
||||||
|
$emoji-picker-header-picture-height: 32px;
|
||||||
|
$emoji-picker-emoji-size: 32px;
|
||||||
|
|
||||||
.emoji-picker {
|
.emoji-picker {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -19,6 +24,21 @@
|
||||||
--lightText: var(--popoverLightText, $fallback--lightText);
|
--lightText: var(--popoverLightText, $fallback--lightText);
|
||||||
--icon: var(--popoverIcon, $fallback--icon);
|
--icon: var(--popoverIcon, $fallback--icon);
|
||||||
|
|
||||||
|
&-header-image {
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: $emoji-picker-header-picture-width;
|
||||||
|
max-width: $emoji-picker-header-picture-width;
|
||||||
|
height: $emoji-picker-header-picture-height;
|
||||||
|
max-height: $emoji-picker-header-picture-height;
|
||||||
|
.still-image {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.keep-open,
|
.keep-open,
|
||||||
.too-many-emoji {
|
.too-many-emoji {
|
||||||
padding: 7px;
|
padding: 7px;
|
||||||
|
@ -37,7 +57,6 @@
|
||||||
|
|
||||||
.heading {
|
.heading {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 32px;
|
|
||||||
padding: 10px 7px 5px;
|
padding: 10px 7px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +69,10 @@
|
||||||
|
|
||||||
.emoji-tabs {
|
.emoji-tabs {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.emoji-groups {
|
.emoji-groups {
|
||||||
|
@ -57,6 +80,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.additional-tabs {
|
.additional-tabs {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
border-left: 1px solid;
|
border-left: 1px solid;
|
||||||
border-left-color: $fallback--icon;
|
border-left-color: $fallback--icon;
|
||||||
border-left-color: var(--icon, $fallback--icon);
|
border-left-color: var(--icon, $fallback--icon);
|
||||||
|
@ -66,15 +91,20 @@
|
||||||
|
|
||||||
.additional-tabs,
|
.additional-tabs,
|
||||||
.emoji-tabs {
|
.emoji-tabs {
|
||||||
display: block;
|
|
||||||
min-width: 0;
|
|
||||||
flex-basis: auto;
|
flex-basis: auto;
|
||||||
flex-shrink: 1;
|
display: flex;
|
||||||
|
align-content: center;
|
||||||
|
|
||||||
&-item {
|
&-item {
|
||||||
padding: 0 7px;
|
padding: 0 7px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 1.85em;
|
font-size: 1.85em;
|
||||||
|
width: $emoji-picker-header-picture-width;
|
||||||
|
max-width: $emoji-picker-header-picture-width;
|
||||||
|
height: $emoji-picker-header-picture-height;
|
||||||
|
max-height: $emoji-picker-header-picture-height;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
&.disabled {
|
&.disabled {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
|
@ -164,22 +194,26 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&-item {
|
&-item {
|
||||||
width: 32px;
|
width: $emoji-picker-emoji-size;
|
||||||
height: 32px;
|
height: $emoji-picker-emoji-size;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
font-size: 32px;
|
line-height: $emoji-picker-emoji-size;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
img {
|
.emoji-picker-emoji.-custom {
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
}
|
}
|
||||||
|
.emoji-picker-emoji.-unicode {
|
||||||
|
font-size: 24px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,34 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="emoji-picker panel panel-default panel-body">
|
<div
|
||||||
|
class="emoji-picker panel panel-default panel-body"
|
||||||
|
>
|
||||||
<div class="heading">
|
<div class="heading">
|
||||||
<span class="emoji-tabs">
|
|
||||||
<span
|
<span
|
||||||
v-for="group in emojis"
|
ref="header"
|
||||||
|
class="emoji-tabs"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
v-for="group in filteredEmojiGroups"
|
||||||
|
:ref="setGroupRef('group-header-' + group.id)"
|
||||||
:key="group.id"
|
:key="group.id"
|
||||||
class="emoji-tabs-item"
|
class="emoji-tabs-item"
|
||||||
:class="{
|
:class="{
|
||||||
active: activeGroupView === group.id,
|
active: activeGroupView === group.id
|
||||||
disabled: group.emojis.length === 0
|
|
||||||
}"
|
}"
|
||||||
:title="group.text"
|
:title="group.text"
|
||||||
@click.prevent="highlight(group.id)"
|
@click.prevent="highlight(group.id)"
|
||||||
>
|
>
|
||||||
|
<span
|
||||||
|
v-if="group.image"
|
||||||
|
class="emoji-picker-header-image"
|
||||||
|
>
|
||||||
|
<still-image
|
||||||
|
:alt="group.text"
|
||||||
|
:src="group.image"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
|
v-else
|
||||||
:icon="group.icon"
|
:icon="group.icon"
|
||||||
fixed-width
|
fixed-width
|
||||||
/>
|
/>
|
||||||
|
@ -36,7 +51,10 @@
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div
|
||||||
|
v-if="contentLoaded"
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="emoji-content"
|
class="emoji-content"
|
||||||
:class="{hidden: showingStickers}"
|
:class="{hidden: showingStickers}"
|
||||||
|
@ -57,12 +75,12 @@
|
||||||
@scroll="onScroll"
|
@scroll="onScroll"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-for="group in emojisView"
|
v-for="group in filteredEmojiGroups"
|
||||||
:key="group.id"
|
:key="group.id"
|
||||||
class="emoji-group"
|
class="emoji-group"
|
||||||
>
|
>
|
||||||
<h6
|
<h6
|
||||||
:ref="'group-' + group.id"
|
:ref="setGroupRef('group-' + group.id)"
|
||||||
class="emoji-group-title"
|
class="emoji-group-title"
|
||||||
>
|
>
|
||||||
{{ group.text }}
|
{{ group.text }}
|
||||||
|
@ -74,13 +92,19 @@
|
||||||
class="emoji-item"
|
class="emoji-item"
|
||||||
@click.stop.prevent="onEmoji(emoji)"
|
@click.stop.prevent="onEmoji(emoji)"
|
||||||
>
|
>
|
||||||
<span v-if="!emoji.imageUrl">{{ emoji.replacement }}</span>
|
<span
|
||||||
<img
|
v-if="!emoji.imageUrl"
|
||||||
|
class="emoji-picker-emoji -unicode"
|
||||||
|
>{{ emoji.replacement }}</span>
|
||||||
|
<still-image
|
||||||
v-else
|
v-else
|
||||||
:src="emoji.imageUrl"
|
:ref="setEmojiRef(group.id + emoji.displayText)"
|
||||||
>
|
class="emoji-picker-emoji -custom"
|
||||||
|
:data-src="emoji.imageUrl"
|
||||||
|
:data-emoji-name="group.id + emoji.displayText"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span :ref="'group-end-' + group.id" />
|
<span :ref="setGroupRef('group-end-' + group.id)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="keep-open">
|
<div class="keep-open">
|
||||||
|
|
|
@ -164,7 +164,7 @@ const PostStatusForm = {
|
||||||
emojiUserSuggestor () {
|
emojiUserSuggestor () {
|
||||||
return suggestor({
|
return suggestor({
|
||||||
emoji: [
|
emoji: [
|
||||||
...this.$store.state.instance.emoji,
|
...this.$store.getters.standardEmojiList,
|
||||||
...this.$store.state.instance.customEmoji
|
...this.$store.state.instance.customEmoji
|
||||||
],
|
],
|
||||||
store: this.$store
|
store: this.$store
|
||||||
|
@ -173,13 +173,13 @@ const PostStatusForm = {
|
||||||
emojiSuggestor () {
|
emojiSuggestor () {
|
||||||
return suggestor({
|
return suggestor({
|
||||||
emoji: [
|
emoji: [
|
||||||
...this.$store.state.instance.emoji,
|
...this.$store.getters.standardEmojiList,
|
||||||
...this.$store.state.instance.customEmoji
|
...this.$store.state.instance.customEmoji
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
emoji () {
|
emoji () {
|
||||||
return this.$store.state.instance.emoji || []
|
return this.$store.getters.standardEmojiList || []
|
||||||
},
|
},
|
||||||
customEmoji () {
|
customEmoji () {
|
||||||
return this.$store.state.instance.customEmoji || []
|
return this.$store.state.instance.customEmoji || []
|
||||||
|
|
|
@ -46,7 +46,7 @@ const ReactButton = {
|
||||||
if (this.filterWord !== '') {
|
if (this.filterWord !== '') {
|
||||||
const filterWordLowercase = trim(this.filterWord.toLowerCase())
|
const filterWordLowercase = trim(this.filterWord.toLowerCase())
|
||||||
const orderedEmojiList = []
|
const orderedEmojiList = []
|
||||||
for (const emoji of this.$store.state.instance.emoji) {
|
for (const emoji of this.$store.getters.standardEmojiList) {
|
||||||
if (emoji.replacement === this.filterWord) return [emoji]
|
if (emoji.replacement === this.filterWord) return [emoji]
|
||||||
|
|
||||||
const indexOfFilterWord = emoji.displayText.toLowerCase().indexOf(filterWordLowercase)
|
const indexOfFilterWord = emoji.displayText.toLowerCase().indexOf(filterWordLowercase)
|
||||||
|
@ -59,7 +59,7 @@ const ReactButton = {
|
||||||
}
|
}
|
||||||
return orderedEmojiList.flat()
|
return orderedEmojiList.flat()
|
||||||
}
|
}
|
||||||
return this.$store.state.instance.emoji || []
|
return this.$store.getters.standardEmojiList || []
|
||||||
},
|
},
|
||||||
mergedConfig () {
|
mergedConfig () {
|
||||||
return this.$store.getters.mergedConfig
|
return this.$store.getters.mergedConfig
|
||||||
|
|
|
@ -64,7 +64,7 @@ const ProfileTab = {
|
||||||
emojiUserSuggestor () {
|
emojiUserSuggestor () {
|
||||||
return suggestor({
|
return suggestor({
|
||||||
emoji: [
|
emoji: [
|
||||||
...this.$store.state.instance.emoji,
|
...this.$store.getters.standardEmojiList,
|
||||||
...this.$store.state.instance.customEmoji
|
...this.$store.state.instance.customEmoji
|
||||||
],
|
],
|
||||||
store: this.$store
|
store: this.$store
|
||||||
|
@ -73,7 +73,7 @@ const ProfileTab = {
|
||||||
emojiSuggestor () {
|
emojiSuggestor () {
|
||||||
return suggestor({
|
return suggestor({
|
||||||
emoji: [
|
emoji: [
|
||||||
...this.$store.state.instance.emoji,
|
...this.$store.getters.standardEmojiList,
|
||||||
...this.$store.state.instance.customEmoji
|
...this.$store.state.instance.customEmoji
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
|
@ -7,16 +7,23 @@ const StillImage = {
|
||||||
'imageLoadHandler',
|
'imageLoadHandler',
|
||||||
'alt',
|
'alt',
|
||||||
'height',
|
'height',
|
||||||
'width'
|
'width',
|
||||||
|
'dataSrc'
|
||||||
],
|
],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
// for lazy loading, see loadLazy()
|
||||||
|
realSrc: this.src,
|
||||||
stopGifs: this.$store.getters.mergedConfig.stopGifs
|
stopGifs: this.$store.getters.mergedConfig.stopGifs
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
animated () {
|
animated () {
|
||||||
return this.stopGifs && (this.mimetype === 'image/gif' || this.src.endsWith('.gif'))
|
if (!this.realSrc) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.stopGifs && (this.mimetype === 'image/gif' || this.realSrc.endsWith('.gif'))
|
||||||
},
|
},
|
||||||
style () {
|
style () {
|
||||||
const appendPx = (str) => /\d$/.test(str) ? str + 'px' : str
|
const appendPx = (str) => /\d$/.test(str) ? str + 'px' : str
|
||||||
|
@ -27,7 +34,15 @@ const StillImage = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
loadLazy () {
|
||||||
|
if (this.dataSrc) {
|
||||||
|
this.realSrc = this.dataSrc
|
||||||
|
}
|
||||||
|
},
|
||||||
onLoad () {
|
onLoad () {
|
||||||
|
if (!this.realSrc) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const image = this.$refs.src
|
const image = this.$refs.src
|
||||||
if (!image) return
|
if (!image) return
|
||||||
this.imageLoadHandler && this.imageLoadHandler(image)
|
this.imageLoadHandler && this.imageLoadHandler(image)
|
||||||
|
@ -42,6 +57,14 @@ const StillImage = {
|
||||||
onError () {
|
onError () {
|
||||||
this.imageLoadError && this.imageLoadError()
|
this.imageLoadError && this.imageLoadError()
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
src () {
|
||||||
|
this.realSrc = this.src
|
||||||
|
},
|
||||||
|
dataSrc () {
|
||||||
|
this.$el.removeAttribute('data-loaded')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,11 @@
|
||||||
<!-- NOTE: key is required to force to re-render img tag when src is changed -->
|
<!-- NOTE: key is required to force to re-render img tag when src is changed -->
|
||||||
<img
|
<img
|
||||||
ref="src"
|
ref="src"
|
||||||
:key="src"
|
:key="realSrc"
|
||||||
:alt="alt"
|
:alt="alt"
|
||||||
:title="alt"
|
:title="alt"
|
||||||
:src="src"
|
:data-src="dataSrc"
|
||||||
|
:src="realSrc"
|
||||||
:referrerpolicy="referrerpolicy"
|
:referrerpolicy="referrerpolicy"
|
||||||
@load="onLoad"
|
@load="onLoad"
|
||||||
@error="onError"
|
@error="onError"
|
||||||
|
|
|
@ -196,6 +196,17 @@
|
||||||
"add_emoji": "Insert emoji",
|
"add_emoji": "Insert emoji",
|
||||||
"custom": "Custom emoji",
|
"custom": "Custom emoji",
|
||||||
"unicode": "Unicode emoji",
|
"unicode": "Unicode emoji",
|
||||||
|
"unicode_groups": {
|
||||||
|
"activities": "Activities",
|
||||||
|
"animals-and-nature": "Animals & Nature",
|
||||||
|
"flags": "Flags",
|
||||||
|
"food-and-drink": "Food & Drink",
|
||||||
|
"objects": "Objects",
|
||||||
|
"people-and-body": "People & Body",
|
||||||
|
"smileys-and-emotion": "Smileys & Emotion",
|
||||||
|
"symbols": "Symbols",
|
||||||
|
"travel-and-places": "Travel & Places"
|
||||||
|
},
|
||||||
"load_all_hint": "Loaded first {saneAmount} emoji, loading all emoji may cause performance issues.",
|
"load_all_hint": "Loaded first {saneAmount} emoji, loading all emoji may cause performance issues.",
|
||||||
"load_all": "Loading all {emojiAmount} emoji"
|
"load_all": "Loading all {emojiAmount} emoji"
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,6 +3,18 @@ import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js'
|
||||||
import apiService from '../services/api/api.service.js'
|
import apiService from '../services/api/api.service.js'
|
||||||
import { instanceDefaultProperties } from './config.js'
|
import { instanceDefaultProperties } from './config.js'
|
||||||
|
|
||||||
|
const SORTED_EMOJI_GROUP_IDS = [
|
||||||
|
'smileys-and-emotion',
|
||||||
|
'people-and-body',
|
||||||
|
'animals-and-nature',
|
||||||
|
'food-and-drink',
|
||||||
|
'travel-and-places',
|
||||||
|
'activities',
|
||||||
|
'objects',
|
||||||
|
'symbols',
|
||||||
|
'flags'
|
||||||
|
]
|
||||||
|
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
// Stuff from apiConfig
|
// Stuff from apiConfig
|
||||||
name: 'Pleroma FE',
|
name: 'Pleroma FE',
|
||||||
|
@ -64,7 +76,7 @@ const defaultState = {
|
||||||
// Nasty stuff
|
// Nasty stuff
|
||||||
customEmoji: [],
|
customEmoji: [],
|
||||||
customEmojiFetched: false,
|
customEmojiFetched: false,
|
||||||
emoji: [],
|
emoji: {},
|
||||||
emojiFetched: false,
|
emojiFetched: false,
|
||||||
pleromaBackend: true,
|
pleromaBackend: true,
|
||||||
postFormats: [],
|
postFormats: [],
|
||||||
|
@ -115,6 +127,41 @@ const instance = {
|
||||||
.map(key => [key, state[key]])
|
.map(key => [key, state[key]])
|
||||||
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
|
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
|
||||||
},
|
},
|
||||||
|
groupedCustomEmojis (state) {
|
||||||
|
const packsOf = emoji => {
|
||||||
|
return emoji.tags
|
||||||
|
.filter(k => k.startsWith('pack:'))
|
||||||
|
.map(k => k.slice(5)) // remove 'pack:' prefix
|
||||||
|
}
|
||||||
|
|
||||||
|
return state.customEmoji
|
||||||
|
.reduce((res, emoji) => {
|
||||||
|
packsOf(emoji).forEach(packName => {
|
||||||
|
const packId = `custom-${packName}`
|
||||||
|
if (!res[packId]) {
|
||||||
|
res[packId] = ({
|
||||||
|
id: packId,
|
||||||
|
text: packName,
|
||||||
|
image: emoji.imageUrl,
|
||||||
|
emojis: []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
res[packId].emojis.push(emoji)
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
}, {})
|
||||||
|
},
|
||||||
|
standardEmojiList (state) {
|
||||||
|
return SORTED_EMOJI_GROUP_IDS
|
||||||
|
.map(groupId => state.emoji[groupId] || [])
|
||||||
|
.reduce((a, b) => a.concat(b), [])
|
||||||
|
},
|
||||||
|
standardEmojiGroupList (state) {
|
||||||
|
return SORTED_EMOJI_GROUP_IDS.map(groupId => ({
|
||||||
|
id: groupId,
|
||||||
|
emojis: state.emoji[groupId] || []
|
||||||
|
}))
|
||||||
|
},
|
||||||
instanceDomain (state) {
|
instanceDomain (state) {
|
||||||
return new URL(state.server).hostname
|
return new URL(state.server).hostname
|
||||||
}
|
}
|
||||||
|
@ -141,13 +188,14 @@ const instance = {
|
||||||
const res = await window.fetch('/static/emoji.json')
|
const res = await window.fetch('/static/emoji.json')
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const values = await res.json()
|
const values = await res.json()
|
||||||
const emoji = Object.keys(values).map((key) => {
|
const emoji = Object.keys(values).reduce((res, groupId) => {
|
||||||
return {
|
res[groupId] = values[groupId].map(e => ({
|
||||||
displayText: key,
|
displayText: e.name,
|
||||||
imageUrl: false,
|
imageUrl: false,
|
||||||
replacement: values[key]
|
replacement: e.emoji
|
||||||
}
|
}))
|
||||||
}).sort((a, b) => a.name > b.name ? 1 : -1)
|
return res
|
||||||
|
}, {})
|
||||||
commit('setInstanceOption', { name: 'emoji', value: emoji })
|
commit('setInstanceOption', { name: 'emoji', value: emoji })
|
||||||
} else {
|
} else {
|
||||||
throw (res)
|
throw (res)
|
||||||
|
@ -164,6 +212,16 @@ const instance = {
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const result = await res.json()
|
const result = await res.json()
|
||||||
const values = Array.isArray(result) ? Object.assign({}, ...result) : result
|
const values = Array.isArray(result) ? Object.assign({}, ...result) : result
|
||||||
|
const caseInsensitiveStrCmp = (a, b) => {
|
||||||
|
const la = a.toLowerCase()
|
||||||
|
const lb = b.toLowerCase()
|
||||||
|
return la > lb ? 1 : (la < lb ? -1 : 0)
|
||||||
|
}
|
||||||
|
const byPackThenByName = (a, b) => {
|
||||||
|
const packOf = emoji => (emoji.tags.filter(k => k.startsWith('pack:'))[0] || '').slice(5)
|
||||||
|
return caseInsensitiveStrCmp(packOf(a), packOf(b)) || caseInsensitiveStrCmp(a.displayText, b.displayText)
|
||||||
|
}
|
||||||
|
|
||||||
const emoji = Object.entries(values).map(([key, value]) => {
|
const emoji = Object.entries(values).map(([key, value]) => {
|
||||||
const imageUrl = value.image_url
|
const imageUrl = value.image_url
|
||||||
return {
|
return {
|
||||||
|
@ -174,7 +232,7 @@ const instance = {
|
||||||
}
|
}
|
||||||
// Technically could use tags but those are kinda useless right now,
|
// Technically could use tags but those are kinda useless right now,
|
||||||
// should have been "pack" field, that would be more useful
|
// should have been "pack" field, that would be more useful
|
||||||
}).sort((a, b) => a.displayText.toLowerCase() > b.displayText.toLowerCase() ? 1 : -1)
|
}).sort(byPackThenByName)
|
||||||
commit('setInstanceOption', { name: 'customEmoji', value: emoji })
|
commit('setInstanceOption', { name: 'customEmoji', value: emoji })
|
||||||
} else {
|
} else {
|
||||||
throw (res)
|
throw (res)
|
||||||
|
|
1431
static/emoji.json
1431
static/emoji.json
File diff suppressed because it is too large
Load diff
10
yarn.lock
10
yarn.lock
|
@ -5924,6 +5924,11 @@ lower-case@^2.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib "^2.0.3"
|
tslib "^2.0.3"
|
||||||
|
|
||||||
|
lozad@^1.16.0:
|
||||||
|
version "1.16.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lozad/-/lozad-1.16.0.tgz#86ce732c64c69926ccdebb81c8c90bb3735948b4"
|
||||||
|
integrity sha512-JBr9WjvEFeKoyim3svo/gsQPTkgG/mOHJmDctZ/+U9H3ymUuvEkqpn8bdQMFsvTMcyRJrdJkLv0bXqGm0sP72w==
|
||||||
|
|
||||||
lru-cache@^6.0.0:
|
lru-cache@^6.0.0:
|
||||||
version "6.0.0"
|
version "6.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
|
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
|
||||||
|
@ -8492,6 +8497,11 @@ unicode-canonical-property-names-ecmascript@^2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"
|
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"
|
||||||
integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==
|
integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==
|
||||||
|
|
||||||
|
unicode-emoji-json@^0.3.0:
|
||||||
|
version "0.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/unicode-emoji-json/-/unicode-emoji-json-0.3.0.tgz#965e097ef8a367081c5036f81873015a95a5c1ad"
|
||||||
|
integrity sha512-yImheILedqhZtVEEenRELu9AnX347ZTA3bVMWAU5WMUv7pdU2hcfpwo2mKN8Rns9uHLmOZP90/4B4SPS5aF/iw==
|
||||||
|
|
||||||
unicode-match-property-ecmascript@^2.0.0:
|
unicode-match-property-ecmascript@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3"
|
resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3"
|
||||||
|
|
Loading…
Add table
Reference in a new issue