Remote pack download, localization

This commit is contained in:
Ekaterina Vaartis 2024-01-07 14:28:34 +03:00
parent 4451cccb3c
commit f8e1d5e3e0
4 changed files with 251 additions and 85 deletions

View File

@ -24,13 +24,16 @@ const EmojiTab = {
data () { data () {
return { return {
knownPacks: { }, knownLocalPacks: { },
knownRemotePacks: { },
editedParts: { }, editedParts: { },
editedMetadata: { }, editedMetadata: { },
packName: '', packName: '',
newPackName: '', newPackName: '',
deleteModalVisible: false, deleteModalVisible: false,
newEmojiUpload: clone(newEmojiUploadBase) newEmojiUpload: clone(newEmojiUploadBase),
remotePackInstance: '',
remotePackDownloadAs: ''
} }
}, },
@ -44,6 +47,17 @@ const EmojiTab = {
} }
return this.editedMetadata[this.packName] return this.editedMetadata[this.packName]
},
knownPacks () {
// Copy the object itself but not the children, so they are still passed by reference and modified
const result = clone(this.knownLocalPacks)
for (const instName in this.knownRemotePacks) {
for (const instPack in this.knownRemotePacks[instName]) {
result[`${instPack}@${instName}`] = this.knownRemotePacks[instName][instPack]
}
}
return result
} }
}, },
@ -55,7 +69,12 @@ const EmojiTab = {
this.$store.state.api.backendInteractor.importEmojiFromFS() this.$store.state.api.backendInteractor.importEmojiFromFS()
}, },
emojiAddr (name) { emojiAddr (name) {
if (this.pack.remote !== undefined) {
// Remote pack
return `${this.pack.remote.instance}/emoji/${encodeURIComponent(this.pack.remote.baseName)}/${name}`
} else {
return `${this.$store.state.instance.server}/emoji/${encodeURIComponent(this.packName)}/${name}` return `${this.$store.state.instance.server}/emoji/${encodeURIComponent(this.packName)}/${name}`
}
}, },
uploadEmoji () { uploadEmoji () {
@ -193,15 +212,64 @@ const EmojiTab = {
return return
} }
this.knownPacks = packData.packs this.knownLocalPacks = packData.packs
for (const name of Object.keys(this.knownPacks)) { for (const name of Object.keys(this.knownLocalPacks)) {
this.sortPackFiles(name) this.sortPackFiles(name)
} }
}) })
}, },
listRemotePacks () {
this.$store.state.api.backendInteractor.listRemoteEmojiPacks({ instance: this.remotePackInstance })
.then(data => data.json())
.then(packData => {
if (packData.error !== undefined) {
this.displayError(packData.error)
return
}
let inst = this.remotePackInstance
if (!inst.startsWith('http')) { inst = 'https://' + inst }
const instUrl = new URL(inst)
inst = instUrl.host
for (const packName in packData.packs) {
packData.packs[packName].remote = {
baseName: packName,
instance: instUrl.origin
}
}
this.knownRemotePacks[inst] = packData.packs
this.$refs.remotePackPopover.hidePopover()
})
},
downloadRemotePack () {
if (this.remotePackDownloadAs.trim() === '') {
this.remotePackDownloadAs = this.pack.remote.baseName
}
this.$store.state.api.backendInteractor.downloadRemoteEmojiPack({
instance: this.pack.remote.instance, packName: this.pack.remote.baseName, as: this.remotePackDownloadAs
})
.then(data => data.json())
.then(resp => {
if (resp === 'ok') {
this.$refs.dlPackPopover.hidePopover()
return this.refreshPackList()
} else {
this.displayError(resp.error)
return Promise.reject(resp)
}
}).then(done => {
this.packName = this.remotePackDownloadAs
this.remotePackDownloadAs = ''
})
},
displayError (msg) { displayError (msg) {
this.$store.dispatch('pushGlobalNotice', { this.$store.dispatch('pushGlobalNotice', {
messageKey: 'upload.error.message', messageKey: 'admin_dash.emoji.error',
messageArgs: [msg], messageArgs: [msg],
level: 'error' level: 'error'
}) })

View File

@ -27,7 +27,7 @@
class="button button-default btn" class="button button-default btn"
type="button" type="button"
@click="$refs.createPackPopover.showPopover"> @click="$refs.createPackPopover.showPopover">
Create pack {{ $t('admin_dash.emoji.create_pack') }}
</button> </button>
<Popover <Popover
ref="createPackPopover" ref="createPackPopover"
@ -38,37 +38,46 @@
:offset="{ y: 5 }" :offset="{ y: 5 }"
> >
<template #content> <template #content>
<input v-model="newPackName" placeholder="New pack name"> <input v-model="newPackName" :placeholder="$t('admin_dash.emoji.new_pack_name')">
<button <button
class="button button-default btn emoji-tab-popover-button" class="button button-default btn emoji-tab-popover-button"
type="button" type="button"
@click="createEmojiPack"> @click="createEmojiPack">
Create {{ $t('admin_dash.emoji.create') }}
</button> </button>
</template> </template>
</Popover> </Popover>
<button <button
class="button button-default btn" class="button button-default btn"
:disabled="packName == ''"
type="button" type="button"
@click="deleteModalVisible = true"> @click="$refs.remotePackPopover.showPopover">
Delete pack {{ $t('admin_dash.emoji.remote_packs') }}
<Popover
ref="remotePackPopover"
trigger="click"
placement="bottom"
bound-to-selector=".emoji-tab"
:bound-to="{ x: 'container' }"
:offset="{ y: 5 }"
>
<template #content>
<input v-model="remotePackInstance" :placeholder="$t('admin_dash.emoji.remote_pack_instance')">
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
@click="listRemotePacks">
{{ $t('admin_dash.emoji.do_list') }}
</button>
</template>
</Popover>
</button> </button>
<ConfirmModal
v-if="deleteModalVisible"
title="Delete?"
:cancel-text="$t('status.delete_confirm_cancel_button')"
:confirm-text="$t('status.delete_confirm_accept_button')"
@cancelled="deleteModalVisible = false"
@accepted="deleteEmojiPack" >
Are you sure you want to delete <i>{{ packName }}</i>?
</ConfirmModal>
</li> </li>
<li> <li>
<Select class="form-control" v-model="packName"> <Select class="form-control" v-model="packName">
<option value="" disabled hidden>Emoji pack</option> <option value="" disabled hidden>{{ $t('admin_dash.emoji.emoji_pack') }}</option>
<option v-for="(pack, listPackName) in knownPacks" :label="listPackName" :key="listPackName"> <option v-for="(pack, listPackName) in knownPacks" :label="listPackName" :key="listPackName">
{{ listPackName }} {{ listPackName }}
</option> </option>
@ -81,33 +90,38 @@
<ul class="setting-list"> <ul class="setting-list">
<li> <li>
<div> <div>
Description {{ $t('admin_dash.emoji.description') }}
<ModifiedIndicator :changed="metaEdited('description')" /> <ModifiedIndicator :changed="metaEdited('description')" />
</div> </div>
<textarea <textarea
v-model="packMeta.description" v-model="packMeta.description"
:disabled="pack.remote !== undefined"
class="bio resize-height" /> class="bio resize-height" />
</li> </li>
<li> <li>
<div> <div>
Homepage {{ $t('admin_dash.emoji.homepage') }}
<ModifiedIndicator :changed="metaEdited('homepage')" /> <ModifiedIndicator :changed="metaEdited('homepage')" />
</div> </div>
<input class="emoji-info-input" v-model="packMeta.homepage"> <input
class="emoji-info-input" v-model="packMeta.homepage"
:disabled="pack.remote !== undefined">
</li> </li>
<li> <li>
<div> <div>
Fallback source {{ $t('admin_dash.emoji.fallback_src') }}
<ModifiedIndicator :changed="metaEdited('fallback-src')" /> <ModifiedIndicator :changed="metaEdited('fallback-src')" />
</div> </div>
<input class="emoji-info-input" v-model="packMeta['fallback-src']"> <input class="emoji-info-input" v-model="packMeta['fallback-src']" :disabled="pack.remote !== undefined">
</li> </li>
<li> <li>
<div>Fallback SHA256</div> <div>{{ $t('admin_dash.emoji.fallback_sha256') }}</div>
<input :disabled="true" class="emoji-info-input" v-model="packMeta['fallback-src-sha256']"> <input :disabled="true" class="emoji-info-input" v-model="packMeta['fallback-src-sha256']">
</li> </li>
<li> <li>
<Checkbox v-model="packMeta['share-files']">Share</Checkbox> <Checkbox :disabled="pack.remote !== undefined" v-model="packMeta['share-files']">
{{ $t('admin_dash.emoji.share') }}
</Checkbox>
<ModifiedIndicator :changed="metaEdited('share-files')" /> <ModifiedIndicator :changed="metaEdited('share-files')" />
</li> </li>
@ -115,10 +129,18 @@
<button <button
class="button button-default btn" class="button button-default btn"
type="button" type="button"
v-if="pack.remote === undefined"
@click="savePackMetadata"> @click="savePackMetadata">
Save {{ $t('admin_dash.emoji.save') }}
</button> </button>
<button
class="button button-default btn"
type="button"
v-if="pack.remote === undefined"
@click="$refs.addEmojiPopover.showPopover">
{{ $t('admin_dash.emoji.add_file') }}
<Popover <Popover
ref="addEmojiPopover" ref="addEmojiPopover"
trigger="click" trigger="click"
@ -129,7 +151,7 @@
:offset="{ y: 5 }" :offset="{ y: 5 }"
> >
<template #content> <template #content>
<h3>Adding new emoji</h3> <h3>{{ $t('admin_dash.emoji.adding_new') }}</h3>
<div> <div>
<input <input
type="file" type="file"
@ -141,38 +163,88 @@
<div> <div>
<input class="emoji-data-input emoji-tab-popover-input" <input class="emoji-data-input emoji-tab-popover-input"
v-model="newEmojiUpload.shortcode" v-model="newEmojiUpload.shortcode"
placeholder="Shortcode, leave blank to infer"> :placeholder="$t('admin_dash.emoji.new_shortcode')">
<input class="emoji-data-input emoji-tab-popover-input" <input class="emoji-data-input emoji-tab-popover-input"
v-model="newEmojiUpload.file" v-model="newEmojiUpload.file"
placeholder="Filename, leave blank infer"> :placeholder="$t('admin_dash.emoji.new_filename')">
<button <button
class="button button-default btn emoji-tab-popover-button" class="button button-default btn emoji-tab-popover-button"
type="button" type="button"
:disabled="this.newEmojiUpload.upload.length == 0" :disabled="this.newEmojiUpload.upload.length == 0"
@click="uploadEmoji"> @click="uploadEmoji">
Save {{ $t('admin_dash.emoji.save') }}
</button> </button>
</div> </div>
</div> </div>
</template> </template>
</Popover> </Popover>
</button>
<button
class="button button-default btn"
v-if="pack.remote === undefined"
type="button"
@click="deleteModalVisible = true">
{{ $t('admin_dash.emoji.delete_pack') }}
<ConfirmModal
v-if="deleteModalVisible"
:title="$t('admin_dash.emoji.delete_title')"
:cancel-text="$t('status.delete_confirm_cancel_button')"
:confirm-text="$t('status.delete_confirm_accept_button')"
@cancelled="deleteModalVisible = false"
@accepted="deleteEmojiPack" >
{{ $t('admin_dash.emoji.delete_confirm', packName) }}
</ConfirmModal>
</button>
<button <button
class="button button-default btn" class="button button-default btn"
type="button" type="button"
@click="$refs.addEmojiPopover.showPopover"> v-if="pack.remote !== undefined"
Add file @click="$refs.dlPackPopover.showPopover">
{{ $t('admin_dash.emoji.download_pack') }}
<Popover
ref="dlPackPopover"
trigger="click"
placement="bottom"
bound-to-selector=".emoji-tab"
popover-class="emoji-tab-edit-popover popover-default"
:bound-to="{ x: 'container' }"
:offset="{ y: 5 }"
>
<template #content>
<h3>{{ $t('admin_dash.emoji.downloading_pack', [packName]) }}</h3>
<div>
<div>
<input class="emoji-data-input emoji-tab-popover-input"
v-model="remotePackDownloadAs"
:placeholder="$t('admin_dash.emoji.download_as_name')">
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
@click="downloadRemotePack">
{{ $t('admin_dash.emoji.download') }}
</button>
</div>
</div>
</template>
</Popover>
</button> </button>
</li> </li>
</ul> </ul>
</div> </div>
<h2>Files</h2> <h2>{{ $t('admin_dash.emoji.files') }}</h2>
<div class="emoji-list" v-if="pack"> <div class="emoji-list" v-if="pack">
<Popover <Popover
v-for="(file, shortcode) in pack.files" :key="shortcode" v-for="(file, shortcode) in pack.files" :key="shortcode"
trigger="click" trigger="click"
:disabled="pack.remote !== undefined"
placement="top" placement="top"
:class="{'emoji-unsaved': editedParts[packName] !== undefined && editedParts[packName][shortcode] !== undefined}" :class="{'emoji-unsaved': editedParts[packName] !== undefined && editedParts[packName][shortcode] !== undefined}"
popover-class="emoji-tab-edit-popover popover-default" popover-class="emoji-tab-edit-popover popover-default"
@ -183,7 +255,7 @@
> >
<template #content> <template #content>
<h3> <h3>
Editing <i>{{ shortcode }}</i> {{ $t('admin_dash.emoji.editing', [shortcode]) }}
</h3> </h3>
<div v-if="editedParts[packName] !== undefined && editedParts[packName][shortcode] !== undefined"> <div v-if="editedParts[packName] !== undefined && editedParts[packName][shortcode] !== undefined">
<input class="emoji-data-input" <input class="emoji-data-input"
@ -195,22 +267,22 @@
class="button button-default btn emoji-tab-popover-button" class="button button-default btn emoji-tab-popover-button"
type="button" type="button"
@click="saveEditedEmoji(shortcode)"> @click="saveEditedEmoji(shortcode)">
Save {{ $t('admin_dash.emoji.save') }}
</button> </button>
<button <button
class="button button-default btn emoji-tab-popover-button" class="button button-default btn emoji-tab-popover-button"
type="button" type="button"
@click="editedParts[packName][shortcode].deleteModalVisible = true"> @click="editedParts[packName][shortcode].deleteModalVisible = true">
Delete {{ $t('admin_dash.emoji.delete') }}
</button> </button>
<ConfirmModal <ConfirmModal
v-if="editedParts[packName][shortcode].deleteModalVisible" v-if="editedParts[packName][shortcode].deleteModalVisible"
title="Delete?" :title="$t('admin_dash.emoji.delete_title')"
:cancel-text="$t('status.delete_confirm_cancel_button')" :cancel-text="$t('status.delete_confirm_cancel_button')"
:confirm-text="$t('status.delete_confirm_accept_button')" :confirm-text="$t('status.delete_confirm_accept_button')"
@cancelled="editedParts[packName][shortcode].deleteModalVisible = false" @cancelled="editedParts[packName][shortcode].deleteModalVisible = false"
@accepted="deleteEmoji(shortcode)" > @accepted="deleteEmoji(shortcode)" >
Are you sure you want to delete <i>{{ shortcode }}</i>? {{ $t('admin_dash.emoji.delete_confirm', shortcode) }}
</ConfirmModal> </ConfirmModal>
</div> </div>
</template> </template>

View File

@ -935,7 +935,35 @@
}, },
"emoji": { "emoji": {
"reload": "Reload emoji", "reload": "Reload emoji",
"importFS": "Import emoji from filesystem" "importFS": "Import emoji from filesystem",
"error": "Error: {0}",
"create_pack": "Create pack",
"delete_pack": "Delete pack",
"new_pack_name": "New pack name",
"create": "Create",
"remote_packs": "Remote packs",
"do_list": "List",
"remote_pack_instance": "Remote pack instance",
"emoji_pack": "Emoji pack",
"description": "Description",
"homepage": "Homepage",
"fallback_src": "Fallback source",
"fallback_sha256": "Fallback SHA256",
"share": "Share",
"save": "Save",
"delete": "Delete",
"add_file": "Add file",
"adding_new": "Adding new emoji",
"new_shortcode": "Shortcode, leave blank to infer",
"new_filename": "Filename, leave blank infer",
"delete_confirm": "Are you sure you want to delete {0}?",
"download_pack": "Download pack",
"downloading_pack": "Downloading {0}",
"download": "Download",
"download_as_name": "New name, leave blank to reuse",
"files": "Files",
"editing": "Editing {0}",
"delete_title": "Delete?"
}, },
"temp_overrides": { "temp_overrides": {
":pleroma": { ":pleroma": {

View File

@ -1817,28 +1817,26 @@ const listEmojiPacks = () => {
} }
const listRemoteEmojiPacks = ({ instance }) => { const listRemoteEmojiPacks = ({ instance }) => {
if (!instance.startsWith('http')) {
instance = 'https://' + instance
}
return fetch( return fetch(
PLEROMA_EMOJI_PACKS_LS_REMOTE_URL, PLEROMA_EMOJI_PACKS_LS_REMOTE_URL(instance, 1, 25),
{ {
method: 'POST', headers: { 'Content-Type': 'application/json' }
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ instance_address: instance })
} }
) )
} }
const downloadRemoteEmojiPack = ({ instance, packName, as }) => { const downloadRemoteEmojiPack = ({ instance, packName, as }) => {
if (as.trim() === '') {
as = null
}
return fetch( return fetch(
PLEROMA_EMOJI_PACKS_DL_REMOTE_URL, PLEROMA_EMOJI_PACKS_DL_REMOTE_URL,
{ {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({
instance_address: instance, pack_name: packName, as url: instance, name: packName, as
}) })
} }
) )