Allow custom emoji reactions
This commit is contained in:
parent
8abaf8fa37
commit
998aa8f732
@ -2,7 +2,7 @@
|
||||
<div class="EmojiReactions">
|
||||
<UserListPopover
|
||||
v-for="(reaction) in emojiReactions"
|
||||
:key="reaction.name"
|
||||
:key="reaction.url || reaction.name"
|
||||
:users="accountsForEmoji[reaction.name]"
|
||||
>
|
||||
<button
|
||||
@ -11,7 +11,21 @@
|
||||
@click="emojiOnClick(reaction.name, $event)"
|
||||
@mouseenter="fetchEmojiReactionsByIfMissing()"
|
||||
>
|
||||
<span class="reaction-emoji">{{ reaction.name }}</span>
|
||||
<span
|
||||
v-if="reaction.url"
|
||||
class="reaction-emoji"
|
||||
>
|
||||
<img
|
||||
:src="reaction.url"
|
||||
:title="reaction.name"
|
||||
class="reaction-emoji-content"
|
||||
width="1em"
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
v-else
|
||||
class="reaction-emoji reaction-emoji-content"
|
||||
>{{ reaction.name }}</span>
|
||||
<span>{{ reaction.count }}</span>
|
||||
</button>
|
||||
</UserListPopover>
|
||||
@ -46,9 +60,18 @@
|
||||
|
||||
.reaction-emoji {
|
||||
width: 1.25em;
|
||||
height: 1.25em;
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
|
||||
.reaction-emoji-content {
|
||||
max-width: 1.25em;
|
||||
max-height: 1.25em;
|
||||
width: auto;
|
||||
height: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
@ -121,7 +121,16 @@
|
||||
scope="global"
|
||||
keypath="notifications.reacted_with"
|
||||
>
|
||||
<span class="emoji-reaction-emoji">{{ notification.emoji }}</span>
|
||||
<img
|
||||
v-if="notification.emoji_url"
|
||||
class="emoji-reaction-emoji emoji-reaction-emoji-image"
|
||||
:src="notification.emoji_url"
|
||||
:name="notification.emoji"
|
||||
>
|
||||
<span
|
||||
v-else
|
||||
class="emoji-reaction-emoji"
|
||||
>{{ notification.emoji }}</span>
|
||||
</i18n-t>
|
||||
</small>
|
||||
</span>
|
||||
@ -153,9 +162,9 @@
|
||||
</router-link>
|
||||
<button
|
||||
class="button-unstyled expand-icon"
|
||||
@click.prevent="toggleStatusExpanded"
|
||||
:title="$t('tool_tip.toggle_expand')"
|
||||
:aria-expanded="statusExpanded"
|
||||
:title="$t('tool_tip.toggle_expand')"
|
||||
@click.prevent="toggleStatusExpanded"
|
||||
>
|
||||
<FAIcon
|
||||
class="fa-scale-110"
|
||||
|
@ -129,6 +129,13 @@
|
||||
|
||||
.emoji-reaction-emoji {
|
||||
font-size: 1.3em;
|
||||
max-width: 1.25em;
|
||||
height: 1.25em;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.emoji-reaction-emoji-image {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.notification-details {
|
||||
|
@ -1,9 +1,8 @@
|
||||
import Popover from '../popover/popover.vue'
|
||||
import { ensureFinalFallback } from '../../i18n/languages.js'
|
||||
import EmojiPicker from '../emoji_picker/emoji_picker.vue'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faPlus, faTimes } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faSmileBeam } from '@fortawesome/free-regular-svg-icons'
|
||||
import { trim } from 'lodash'
|
||||
|
||||
library.add(
|
||||
faPlus,
|
||||
@ -20,103 +19,32 @@ const ReactButton = {
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Popover
|
||||
Popover,
|
||||
EmojiPicker
|
||||
},
|
||||
methods: {
|
||||
addReaction (event, emoji, close) {
|
||||
addReaction (event) {
|
||||
const emoji = event.insertion
|
||||
const existingReaction = this.status.emoji_reactions.find(r => r.name === emoji)
|
||||
if (existingReaction && existingReaction.me) {
|
||||
this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji })
|
||||
} else {
|
||||
this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
|
||||
}
|
||||
close()
|
||||
},
|
||||
show () {
|
||||
if (!this.expanded) {
|
||||
this.$refs.picker.showPicker()
|
||||
}
|
||||
},
|
||||
onShow () {
|
||||
this.expanded = true
|
||||
this.focusInput()
|
||||
},
|
||||
onClose () {
|
||||
this.expanded = false
|
||||
},
|
||||
focusInput () {
|
||||
this.$nextTick(() => {
|
||||
const input = document.querySelector('.reaction-picker-filter > input')
|
||||
if (input) input.focus()
|
||||
})
|
||||
},
|
||||
// Vaguely adjusted copypaste from emoji_input and emoji_picker!
|
||||
maybeLocalizedEmojiNamesAndKeywords (emoji) {
|
||||
const names = [emoji.displayText]
|
||||
const keywords = []
|
||||
|
||||
if (emoji.displayTextI18n) {
|
||||
names.push(this.$t(emoji.displayTextI18n.key, emoji.displayTextI18n.args))
|
||||
}
|
||||
|
||||
if (emoji.annotations) {
|
||||
this.languages.forEach(lang => {
|
||||
names.push(emoji.annotations[lang]?.name)
|
||||
|
||||
keywords.push(...(emoji.annotations[lang]?.keywords || []))
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
names: names.filter(k => k),
|
||||
keywords: keywords.filter(k => k)
|
||||
}
|
||||
},
|
||||
maybeLocalizedEmojiName (emoji) {
|
||||
if (!emoji.annotations) {
|
||||
return emoji.displayText
|
||||
}
|
||||
|
||||
if (emoji.displayTextI18n) {
|
||||
return this.$t(emoji.displayTextI18n.key, emoji.displayTextI18n.args)
|
||||
}
|
||||
|
||||
for (const lang of this.languages) {
|
||||
if (emoji.annotations[lang]?.name) {
|
||||
return emoji.annotations[lang].name
|
||||
}
|
||||
}
|
||||
|
||||
return emoji.displayText
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
commonEmojis () {
|
||||
const hardcodedSet = new Set(['👍', '😠', '👀', '😂', '🔥'])
|
||||
return this.$store.getters.standardEmojiList.filter(emoji => hardcodedSet.has(emoji.replacement))
|
||||
},
|
||||
languages () {
|
||||
return ensureFinalFallback(this.$store.getters.mergedConfig.interfaceLanguage)
|
||||
},
|
||||
emojis () {
|
||||
if (this.filterWord !== '') {
|
||||
const keywordLowercase = trim(this.filterWord.toLowerCase())
|
||||
|
||||
const orderedEmojiList = []
|
||||
for (const emoji of this.$store.getters.standardEmojiList) {
|
||||
const indices = this.maybeLocalizedEmojiNamesAndKeywords(emoji)
|
||||
.keywords
|
||||
.map(k => k.toLowerCase().indexOf(keywordLowercase))
|
||||
.filter(k => k > -1)
|
||||
|
||||
const indexOfKeyword = indices.length ? Math.min(...indices) : -1
|
||||
|
||||
if (indexOfKeyword > -1) {
|
||||
if (!Array.isArray(orderedEmojiList[indexOfKeyword])) {
|
||||
orderedEmojiList[indexOfKeyword] = []
|
||||
}
|
||||
orderedEmojiList[indexOfKeyword].push(emoji)
|
||||
}
|
||||
}
|
||||
return orderedEmojiList.flat()
|
||||
}
|
||||
return this.$store.getters.standardEmojiList || []
|
||||
},
|
||||
mergedConfig () {
|
||||
return this.$store.getters.mergedConfig
|
||||
}
|
||||
|
@ -1,51 +1,17 @@
|
||||
<template>
|
||||
<Popover
|
||||
trigger="click"
|
||||
class="ReactButton"
|
||||
placement="top"
|
||||
:offset="{ y: 5 }"
|
||||
:bound-to="{ x: 'container' }"
|
||||
remove-padding
|
||||
popover-class="ReactButton popover-default"
|
||||
<span class="ReactButton">
|
||||
<EmojiPicker
|
||||
ref="picker"
|
||||
:enable-sticker-picker="enableStickerPicker"
|
||||
class="emoji-picker-panel"
|
||||
@emoji="addReaction"
|
||||
@show="onShow"
|
||||
@close="onClose"
|
||||
>
|
||||
<template #content="{close}">
|
||||
<div class="reaction-picker-filter">
|
||||
<input
|
||||
v-model="filterWord"
|
||||
size="1"
|
||||
:placeholder="$t('emoji.search_emoji')"
|
||||
@input="$event.target.composing = false"
|
||||
>
|
||||
</div>
|
||||
<div class="reaction-picker">
|
||||
<span
|
||||
v-for="emoji in commonEmojis"
|
||||
:key="emoji.replacement"
|
||||
class="emoji-button"
|
||||
:title="maybeLocalizedEmojiName(emoji)"
|
||||
@click="addReaction($event, emoji.replacement, close)"
|
||||
>
|
||||
{{ emoji.replacement }}
|
||||
</span>
|
||||
<div class="reaction-picker-divider" />
|
||||
<span
|
||||
v-for="(emoji, key) in emojis"
|
||||
:key="key"
|
||||
class="emoji-button"
|
||||
:title="maybeLocalizedEmojiName(emoji)"
|
||||
@click="addReaction($event, emoji.replacement, close)"
|
||||
>
|
||||
{{ emoji.replacement }}
|
||||
</span>
|
||||
<div class="reaction-bottom-fader" />
|
||||
</div>
|
||||
</template>
|
||||
<template #trigger>
|
||||
/>
|
||||
<span
|
||||
class="button-unstyled popover-trigger"
|
||||
:title="$t('tool_tip.add_reaction')"
|
||||
@click.stop.prevent="show"
|
||||
>
|
||||
<FALayers>
|
||||
<FAIcon
|
||||
@ -66,8 +32,7 @@
|
||||
/>
|
||||
</FALayers>
|
||||
</span>
|
||||
</template>
|
||||
</Popover>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script src="./react_button.js"></script>
|
||||
@ -135,11 +100,6 @@
|
||||
color: $fallback--text;
|
||||
color: var(--text, $fallback--text);
|
||||
}
|
||||
}
|
||||
|
||||
.popover-trigger-button {
|
||||
/* override of popover internal stuff */
|
||||
width: auto;
|
||||
|
||||
@include unfocused-style {
|
||||
.focus-marker {
|
||||
|
@ -441,6 +441,7 @@ export const parseNotification = (data) => {
|
||||
: parseUser(data.target)
|
||||
output.from_profile = parseUser(data.account)
|
||||
output.emoji = data.emoji
|
||||
output.emoji_url = data.emoji_url
|
||||
if (data.report) {
|
||||
output.report = data.report
|
||||
output.report.content = data.report.content
|
||||
|
Loading…
Reference in New Issue
Block a user