improve responsiveness of emoji stealer and remove copypasta

This commit is contained in:
Henry Jameson 2026-05-21 14:53:48 +03:00
commit e2b4c712af
4 changed files with 122 additions and 202 deletions

View file

@ -12,6 +12,7 @@ import SharedComputedObject from '../helpers/shared_computed_object.js'
import StringSetting from '../helpers/string_setting.vue'
import { useInstanceStore } from 'src/stores/instance.js'
import { useEmojiStore } from 'src/stores/emoji.js'
import { useInterfaceStore } from 'src/stores/interface.js'
import { library } from '@fortawesome/fontawesome-svg-core'
@ -174,51 +175,9 @@ const EmojiTab = {
this.sortPackFiles(packName)
},
loadPacksPaginated(listFunction) {
const pageSize = 25
const allPacks = {}
return listFunction({
instance: this.remotePackInstance,
page: 1,
pageSize: 0,
})
.then((data) => data.json())
.then((data) => {
if (data.error !== undefined) {
return Promise.reject(data.error)
}
let resultingPromise = Promise.resolve({})
for (let i = 0; i < Math.ceil(data.count / pageSize); i++) {
resultingPromise = resultingPromise
.then(() =>
listFunction({
instance: this.remotePackInstance,
page: i,
pageSize,
}),
)
.then((data) => data.json())
.then((pageData) => {
if (pageData.error !== undefined) {
return Promise.reject(pageData.error)
}
assign(allPacks, pageData.packs)
})
}
return resultingPromise
})
.then(() => allPacks)
.catch((data) => {
this.displayError(data)
})
},
refreshPackList() {
this.loadPacksPaginated(
useEmojiStore().getAdminPacks(
this.remotePackInstance,
this.$store.state.api.backendInteractor.listEmojiPacks,
).then((allPacks) => {
this.knownLocalPacks = allPacks
@ -228,7 +187,8 @@ const EmojiTab = {
})
},
listRemotePacks() {
this.loadPacksPaginated(
useEmojiStore().getAdminPacks(
this.remotePackInstance,
this.$store.state.api.backendInteractor.listRemoteEmojiPacks,
)
.then((allPacks) => {

View file

@ -21,169 +21,53 @@
/>
</div>
<div v-if="isUserAdmin && !isLocal">
<button
class="button button-default btn emoji-popover-button"
type="button"
:disabled="packName == ''"
@click="copyToLocalPack"
<template v-if="isUserAdmin && !isLocal">
<span
v-if="adminPacksLocalLoading"
class="loading-spinner"
>
{{ $t('admin_dash.emoji.copy_to_pack') }}
</button>
<FAIcon
class="fa-old-padding"
spin
icon="circle-notch"
/>
</span>
<div v-else>
<button
class="button button-default btn emoji-popover-button"
type="button"
:disabled="packName == ''"
@click="copyToLocalPack"
>
{{ $t('admin_dash.emoji.copy_to_pack') }}
</button>
<SelectComponent
v-model="packName"
>
<option
value=""
disabled
hidden
<SelectComponent
v-model="packName"
>
{{ $t('admin_dash.emoji.emoji_pack') }}
</option>
<option
v-for="(pack, listPackName) in knownLocalPacks"
:key="listPackName"
:label="listPackName"
>
{{ listPackName }}
</option>
</SelectComponent>
</div>
<option
value=""
disabled
hidden
>
{{ $t('admin_dash.emoji.emoji_pack') }}
</option>
<option
v-for="(pack, listPackName) in adminPacksLocal"
:key="listPackName"
:label="listPackName"
>
{{ listPackName }}
</option>
</SelectComponent>
</div>
</template>
</div>
</template>
</Popover>
</template>
<script>
import Popover from 'components/popover/popover.vue'
import SelectComponent from 'components/select/select.vue'
import { assign } from 'lodash'
import StillImage from './still-image.vue'
import { useInstanceStore } from 'src/stores/instance.js'
import { useInterfaceStore } from 'src/stores/interface'
export default {
components: { StillImage, Popover, SelectComponent },
props: {
shortcode: {
type: String,
required: true,
},
isLocal: {
type: Boolean,
required: true,
},
},
data() {
return {
knownLocalPacks: {},
packName: '',
}
},
computed: {
isUserAdmin() {
return this.$store.state.users.currentUser.rights.admin
},
},
methods: {
displayError(msg) {
useInterfaceStore().pushGlobalNotice({
messageKey: 'admin_dash.emoji.error',
messageArgs: [msg],
level: 'error',
})
},
copyToLocalPack() {
this.$store.state.api.backendInteractor
.addNewEmojiFile({
packName: this.packName,
file: this.$attrs.src,
shortcode: this.shortcode,
filename: '',
})
.then((resp) => resp.json())
.then((resp) => {
if (resp.error !== undefined) {
this.displayError(resp.error)
return
}
useInterfaceStore().pushGlobalNotice({
messageKey: 'admin_dash.emoji.copied_successfully',
messageArgs: [this.shortcode, this.packName],
level: 'success',
})
this.$refs.emojiPopover.hidePopover()
this.packName = ''
})
},
// Copied from emoji_tab.js
loadPacksPaginated(listFunction) {
const pageSize = 25
const allPacks = {}
return listFunction({
instance: useInstanceStore().server,
page: 1,
pageSize: 0,
})
.then((data) => data.json())
.then((data) => {
if (data.error !== undefined) {
return Promise.reject(data.error)
}
let resultingPromise = Promise.resolve({})
for (let i = 0; i < Math.ceil(data.count / pageSize); i++) {
resultingPromise = resultingPromise
.then(() =>
listFunction({
instance: useInstanceStore().server,
page: i,
pageSize,
}),
)
.then((data) => data.json())
.then((pageData) => {
if (pageData.error !== undefined) {
return Promise.reject(pageData.error)
}
assign(allPacks, pageData.packs)
})
}
return resultingPromise
})
.then(() => allPacks)
.catch((data) => {
this.displayError(data)
})
},
fetchEmojiPacksIfAdmin() {
if (!this.isUserAdmin) return
this.loadPacksPaginated(
this.$store.state.api.backendInteractor.listEmojiPacks,
).then((allPacks) => {
// Sort by key
const sorted = Object.keys(allPacks)
.sort()
.reduce((acc, key) => {
if (key.length === 0) return acc
acc[key] = allPacks[key]
return acc
}, {})
this.knownLocalPacks = sorted
})
},
},
}
</script>
<script src="./still-image-emoji-popover" />
<style>
.emoji-popover {
@ -191,8 +75,8 @@ export default {
text-align: center;
.emoji {
width: 4.6em;
height: 4.6em;
width: calc(var(--emoji-size) * 3);
height: calc(var(--emoji-size) * 3);
}
.emoji-popover-centered {

View file

@ -2089,6 +2089,7 @@ const listEmojiPacks = ({ page, pageSize }) => {
}
const listRemoteEmojiPacks = ({ instance, page, pageSize }) => {
console.log(instance)
if (!instance.startsWith('http')) {
instance = 'https://' + instance
}

View file

@ -1,5 +1,6 @@
import { defineStore } from 'pinia'
import { merge } from 'lodash'
import { useInstanceStore } from 'src/stores/instance.js'
import { ensureFinalFallback } from 'src/i18n/languages.js'
@ -10,6 +11,8 @@ const defaultState = {
// Custom emoji from server
customEmoji: [],
customEmojiFetched: false,
adminPacksLocal: null,
adminPacksLocalLoading: true,
// Unicode emoji from bundle
emoji: {},
@ -178,6 +181,78 @@ export const useEmojiStore = defineStore('emoji', {
)
},
async getAdminPacksLocal(refresh) {
if (!refresh && this.adminPacksLocal) return this.adminPacksLocal
const backendInteractor = window.vuex.state.api.backendInteractor
const listFunction = backendInteractor.listEmojiPacks
this.adminPacksLocalLoading = true
this.adminPacksLocal = await this.getAdminPacks(useInstanceStore().server, listFunction)
this.adminPacksLocalLoading = false
},
async getAdminPacks(instance, listFunction) {
const currentUser = window.vuex.state.users.currentUser
if (!currentUser.rights.admin) return
const pageSize = 25
const allPacks = {}
return await listFunction({
instance,
page: 1,
pageSize: 0,
})
.then((data) => data.json())
.then((data) => {
if (data.error !== undefined) {
return Promise.reject(data.error)
}
const promises = []
for (let i = 0; i < Math.ceil(data.count / pageSize); i++) {
promises.push(
listFunction({
instance,
page: i,
pageSize,
})
.then((data) => data.json())
.then((pageData) => {
if (pageData.error !== undefined) {
return Promise.reject(pageData.error)
}
return pageData.packs
})
)
}
return Promise
.all(promises)
.then((results) => {
return merge({}, ...results)
})
})
.then((allPacks) => {
// Sort by key
return Object
.keys(allPacks)
.sort()
.reduce((acc, key) => {
if (key.length === 0) return acc
acc[key] = allPacks[key]
return acc
}, {})
})
.catch((data) => {
this.displayError(data)
})
},
async getCustomEmoji() {
try {
let res = await window.fetch('/api/v1/pleroma/emoji')