Merge remote-tracking branch 'origin/develop' into themes3
This commit is contained in:
commit
962bce5ee3
|
@ -0,0 +1 @@
|
||||||
|
Added emoji pack management to the admin panel
|
|
@ -0,0 +1 @@
|
||||||
|
nothing
|
|
@ -0,0 +1,257 @@
|
||||||
|
import { clone, assign } from 'lodash'
|
||||||
|
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
||||||
|
import StringSetting from '../helpers/string_setting.vue'
|
||||||
|
import Checkbox from 'components/checkbox/checkbox.vue'
|
||||||
|
import StillImage from 'components/still-image/still-image.vue'
|
||||||
|
import Select from 'components/select/select.vue'
|
||||||
|
import Popover from 'components/popover/popover.vue'
|
||||||
|
import ConfirmModal from 'components/confirm_modal/confirm_modal.vue'
|
||||||
|
import ModifiedIndicator from '../helpers/modified_indicator.vue'
|
||||||
|
import EmojiEditingPopover from '../helpers/emoji_editing_popover.vue'
|
||||||
|
|
||||||
|
const EmojiTab = {
|
||||||
|
components: {
|
||||||
|
TabSwitcher,
|
||||||
|
StringSetting,
|
||||||
|
Checkbox,
|
||||||
|
StillImage,
|
||||||
|
Select,
|
||||||
|
Popover,
|
||||||
|
ConfirmModal,
|
||||||
|
ModifiedIndicator,
|
||||||
|
EmojiEditingPopover
|
||||||
|
},
|
||||||
|
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
knownLocalPacks: { },
|
||||||
|
knownRemotePacks: { },
|
||||||
|
editedMetadata: { },
|
||||||
|
packName: '',
|
||||||
|
newPackName: '',
|
||||||
|
deleteModalVisible: false,
|
||||||
|
remotePackInstance: '',
|
||||||
|
remotePackDownloadAs: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
provide () {
|
||||||
|
return { emojiAddr: this.emojiAddr }
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
pack () {
|
||||||
|
return this.packName !== '' ? this.knownPacks[this.packName] : undefined
|
||||||
|
},
|
||||||
|
packMeta () {
|
||||||
|
if (this.editedMetadata[this.packName] === undefined) {
|
||||||
|
this.editedMetadata[this.packName] = clone(this.pack.pack)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
},
|
||||||
|
downloadWillReplaceLocal () {
|
||||||
|
return (this.remotePackDownloadAs.trim() === '' && this.pack.remote && this.pack.remote.baseName in this.knownLocalPacks) ||
|
||||||
|
(this.remotePackDownloadAs in this.knownLocalPacks)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
reloadEmoji () {
|
||||||
|
this.$store.state.api.backendInteractor.reloadEmoji()
|
||||||
|
},
|
||||||
|
importFromFS () {
|
||||||
|
this.$store.state.api.backendInteractor.importEmojiFromFS()
|
||||||
|
},
|
||||||
|
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}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
createEmojiPack () {
|
||||||
|
this.$store.state.api.backendInteractor.createEmojiPack(
|
||||||
|
{ name: this.newPackName }
|
||||||
|
).then(resp => resp.json()).then(resp => {
|
||||||
|
if (resp === 'ok') {
|
||||||
|
return this.refreshPackList()
|
||||||
|
} else {
|
||||||
|
this.displayError(resp.error)
|
||||||
|
return Promise.reject(resp)
|
||||||
|
}
|
||||||
|
}).then(done => {
|
||||||
|
this.$refs.createPackPopover.hidePopover()
|
||||||
|
|
||||||
|
this.packName = this.newPackName
|
||||||
|
this.newPackName = ''
|
||||||
|
})
|
||||||
|
},
|
||||||
|
deleteEmojiPack () {
|
||||||
|
this.$store.state.api.backendInteractor.deleteEmojiPack(
|
||||||
|
{ name: this.packName }
|
||||||
|
).then(resp => resp.json()).then(resp => {
|
||||||
|
if (resp === 'ok') {
|
||||||
|
return this.refreshPackList()
|
||||||
|
} else {
|
||||||
|
this.displayError(resp.error)
|
||||||
|
return Promise.reject(resp)
|
||||||
|
}
|
||||||
|
}).then(done => {
|
||||||
|
delete this.editedMetadata[this.packName]
|
||||||
|
|
||||||
|
this.deleteModalVisible = false
|
||||||
|
this.packName = ''
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
metaEdited (prop) {
|
||||||
|
if (!this.pack) return
|
||||||
|
|
||||||
|
const def = this.pack.pack[prop] || ''
|
||||||
|
const edited = this.packMeta[prop] || ''
|
||||||
|
return edited !== def
|
||||||
|
},
|
||||||
|
savePackMetadata () {
|
||||||
|
this.$store.state.api.backendInteractor.saveEmojiPackMetadata({ name: this.packName, newData: this.packMeta }).then(
|
||||||
|
resp => resp.json()
|
||||||
|
).then(resp => {
|
||||||
|
if (resp.error !== undefined) {
|
||||||
|
this.displayError(resp.error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update actual pack data
|
||||||
|
this.pack.pack = resp
|
||||||
|
// Delete edited pack data, should auto-update itself
|
||||||
|
delete this.editedMetadata[this.packName]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
updatePackFiles (newFiles) {
|
||||||
|
this.pack.files = newFiles
|
||||||
|
this.sortPackFiles(this.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(finished => allPacks)
|
||||||
|
.catch(data => {
|
||||||
|
this.displayError(data)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
refreshPackList () {
|
||||||
|
this.loadPacksPaginated(this.$store.state.api.backendInteractor.listEmojiPacks)
|
||||||
|
.then(allPacks => {
|
||||||
|
this.knownLocalPacks = allPacks
|
||||||
|
for (const name of Object.keys(this.knownLocalPacks)) {
|
||||||
|
this.sortPackFiles(name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
listRemotePacks () {
|
||||||
|
this.loadPacksPaginated(this.$store.state.api.backendInteractor.listRemoteEmojiPacks)
|
||||||
|
.then(allPacks => {
|
||||||
|
let inst = this.remotePackInstance
|
||||||
|
if (!inst.startsWith('http')) { inst = 'https://' + inst }
|
||||||
|
const instUrl = new URL(inst)
|
||||||
|
inst = instUrl.host
|
||||||
|
|
||||||
|
for (const packName in allPacks) {
|
||||||
|
allPacks[packName].remote = {
|
||||||
|
baseName: packName,
|
||||||
|
instance: instUrl.origin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.knownRemotePacks[inst] = allPacks
|
||||||
|
for (const pack in this.knownRemotePacks[inst]) {
|
||||||
|
this.sortPackFiles(`${pack}@${inst}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$refs.remotePackPopover.hidePopover()
|
||||||
|
})
|
||||||
|
.catch(data => {
|
||||||
|
this.displayError(data)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
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) {
|
||||||
|
this.$store.dispatch('pushGlobalNotice', {
|
||||||
|
messageKey: 'admin_dash.emoji.error',
|
||||||
|
messageArgs: [msg],
|
||||||
|
level: 'error'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
sortPackFiles (nameOfPack) {
|
||||||
|
// Sort by key
|
||||||
|
const sorted = Object.keys(this.knownPacks[nameOfPack].files).sort().reduce((acc, key) => {
|
||||||
|
if (key.length === 0) return acc
|
||||||
|
acc[key] = this.knownPacks[nameOfPack].files[key]
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
this.knownPacks[nameOfPack].files = sorted
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted () {
|
||||||
|
this.refreshPackList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EmojiTab
|
|
@ -0,0 +1,61 @@
|
||||||
|
@import "src/variables";
|
||||||
|
|
||||||
|
.emoji-tab {
|
||||||
|
.btn-group .btn:not(:first-child) {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pack-info-wrapper {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-info-input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-data-input {
|
||||||
|
width: 40%;
|
||||||
|
margin-left: 0.5em;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-unsaved {
|
||||||
|
box-shadow: 0 3px 5px var(--cBlue, $fallback--cBlue);
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 1em 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-tab-popover-button:not(:first-child) {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-tab-popover-input {
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 20em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-tab-popover-file {
|
||||||
|
padding-top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
color: var(--cOrange, $fallback--cOrange);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,278 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="emoji-tab"
|
||||||
|
:label="$t('admin_dash.tabs.emoji')"
|
||||||
|
>
|
||||||
|
<div class="setting-item">
|
||||||
|
<h2>{{ $t('admin_dash.tabs.emoji') }}</h2>
|
||||||
|
|
||||||
|
<ul class="setting-list">
|
||||||
|
<h3>{{ $t('admin_dash.emoji.global_actions') }}</h3>
|
||||||
|
|
||||||
|
<li class="btn-group setting-item">
|
||||||
|
<button
|
||||||
|
class="button button-default btn"
|
||||||
|
type="button"
|
||||||
|
@click="reloadEmoji">
|
||||||
|
{{ $t('admin_dash.emoji.reload') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button button-default btn"
|
||||||
|
type="button"
|
||||||
|
@click="importFromFS">
|
||||||
|
{{ $t('admin_dash.emoji.importFS') }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="btn-group setting-item">
|
||||||
|
<button
|
||||||
|
class="button button-default btn"
|
||||||
|
type="button"
|
||||||
|
@click="$refs.remotePackPopover.showPopover">
|
||||||
|
{{ $t('admin_dash.emoji.remote_packs') }}
|
||||||
|
|
||||||
|
<Popover
|
||||||
|
ref="remotePackPopover"
|
||||||
|
popover-class="emoji-tab-edit-popover popover-default"
|
||||||
|
trigger="click"
|
||||||
|
placement="bottom"
|
||||||
|
bound-to-selector=".emoji-tab"
|
||||||
|
:bound-to="{ x: 'container' }"
|
||||||
|
:offset="{ y: 5 }"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div class="emoji-tab-popover-input">
|
||||||
|
<h3>{{ $t('admin_dash.emoji.remote_pack_instance') }}</h3>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<h3>{{ $t('admin_dash.emoji.emoji_packs') }}</h3>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<h4>{{ $t('admin_dash.emoji.edit_pack') }}</h4>
|
||||||
|
|
||||||
|
<Select class="form-control" v-model="packName">
|
||||||
|
<option value="" disabled hidden>{{ $t('admin_dash.emoji.emoji_pack') }}</option>
|
||||||
|
<option v-for="(pack, listPackName) in knownPacks" :label="listPackName" :key="listPackName">
|
||||||
|
{{ listPackName }}
|
||||||
|
</option>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="button button-default btn emoji-tab-popover-button"
|
||||||
|
type="button"
|
||||||
|
@click="$refs.createPackPopover.showPopover">
|
||||||
|
{{ $t('admin_dash.emoji.create_pack') }}
|
||||||
|
</button>
|
||||||
|
<Popover
|
||||||
|
ref="createPackPopover"
|
||||||
|
popover-class="emoji-tab-edit-popover popover-default"
|
||||||
|
trigger="click"
|
||||||
|
placement="bottom"
|
||||||
|
bound-to-selector=".emoji-tab"
|
||||||
|
:bound-to="{ x: 'container' }"
|
||||||
|
:offset="{ y: 5 }"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div class="emoji-tab-popover-input">
|
||||||
|
<h3>{{ $t('admin_dash.emoji.new_pack_name') }}</h3>
|
||||||
|
<input v-model="newPackName" :placeholder="$t('admin_dash.emoji.new_pack_name')">
|
||||||
|
<button
|
||||||
|
class="button button-default btn emoji-tab-popover-button"
|
||||||
|
type="button"
|
||||||
|
@click="createEmojiPack">
|
||||||
|
{{ $t('admin_dash.emoji.create') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div v-if="pack">
|
||||||
|
<div class="pack-info-wrapper">
|
||||||
|
<ul class="setting-list">
|
||||||
|
<li>
|
||||||
|
<label>
|
||||||
|
{{ $t('admin_dash.emoji.description') }}
|
||||||
|
<ModifiedIndicator :changed="metaEdited('description')" message-key="admin_dash.emoji.metadata_changed" />
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
v-model="packMeta.description"
|
||||||
|
:disabled="pack.remote !== undefined"
|
||||||
|
class="bio resize-height" />
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<label>
|
||||||
|
{{ $t('admin_dash.emoji.homepage') }}
|
||||||
|
<ModifiedIndicator :changed="metaEdited('homepage')" message-key="admin_dash.emoji.metadata_changed" />
|
||||||
|
|
||||||
|
<input
|
||||||
|
class="emoji-info-input" v-model="packMeta.homepage"
|
||||||
|
:disabled="pack.remote !== undefined">
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<label>
|
||||||
|
{{ $t('admin_dash.emoji.fallback_src') }}
|
||||||
|
<ModifiedIndicator :changed="metaEdited('fallback-src')" message-key="admin_dash.emoji.metadata_changed" />
|
||||||
|
|
||||||
|
<input class="emoji-info-input" v-model="packMeta['fallback-src']" :disabled="pack.remote !== undefined">
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<label>
|
||||||
|
{{ $t('admin_dash.emoji.fallback_sha256') }}
|
||||||
|
|
||||||
|
<input :disabled="true" class="emoji-info-input" v-model="packMeta['fallback-src-sha256']">
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Checkbox :disabled="pack.remote !== undefined" v-model="packMeta['share-files']">
|
||||||
|
{{ $t('admin_dash.emoji.share') }}
|
||||||
|
</Checkbox>
|
||||||
|
|
||||||
|
<ModifiedIndicator :changed="metaEdited('share-files')" message-key="admin_dash.emoji.metadata_changed" />
|
||||||
|
</li>
|
||||||
|
<li class="btn-group">
|
||||||
|
<button
|
||||||
|
class="button button-default btn"
|
||||||
|
type="button"
|
||||||
|
v-if="pack.remote === undefined"
|
||||||
|
@click="savePackMetadata">
|
||||||
|
{{ $t('admin_dash.emoji.save_meta') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button button-default btn"
|
||||||
|
type="button"
|
||||||
|
v-if="pack.remote === undefined"
|
||||||
|
@click="savePackMetadata">
|
||||||
|
{{ $t('admin_dash.emoji.revert_meta') }}
|
||||||
|
</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
|
||||||
|
class="button button-default btn"
|
||||||
|
type="button"
|
||||||
|
v-if="pack.remote !== undefined"
|
||||||
|
@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>
|
||||||
|
<div class="emoji-tab-popover-input">
|
||||||
|
<label>
|
||||||
|
{{ $t('admin_dash.emoji.download_as_name') }}
|
||||||
|
<input class="emoji-data-input"
|
||||||
|
v-model="remotePackDownloadAs"
|
||||||
|
:placeholder="$t('admin_dash.emoji.download_as_name_full')">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div v-if="downloadWillReplaceLocal" class="warning">
|
||||||
|
<em>{{ $t('admin_dash.emoji.replace_warning') }}</em>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="button button-default btn"
|
||||||
|
type="button"
|
||||||
|
@click="downloadRemotePack">
|
||||||
|
{{ $t('admin_dash.emoji.download') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="setting-list">
|
||||||
|
<h4>
|
||||||
|
{{ $t('admin_dash.emoji.files') }}
|
||||||
|
|
||||||
|
<ModifiedIndicator v-if="pack"
|
||||||
|
:changed="$refs.emojiPopovers && $refs.emojiPopovers.some(p => p.isEdited)"
|
||||||
|
message-key="admin_dash.emoji.emoji_changed"/>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
<div class="emoji-list" v-if="pack">
|
||||||
|
<EmojiEditingPopover
|
||||||
|
v-if="pack.remote === undefined"
|
||||||
|
placement="bottom" new-upload
|
||||||
|
:title="$t('admin_dash.emoji.adding_new')"
|
||||||
|
:packName="packName"
|
||||||
|
@updatePackFiles="updatePackFiles" @displayError="displayError"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<FAIcon icon="plus" size="2x" :title="$t('admin_dash.emoji.add_file')" />
|
||||||
|
</template>
|
||||||
|
</EmojiEditingPopover>
|
||||||
|
|
||||||
|
<EmojiEditingPopover
|
||||||
|
placement="top" ref="emojiPopovers"
|
||||||
|
v-for="(file, shortcode) in pack.files" :key="shortcode"
|
||||||
|
:title="$t('admin_dash.emoji.editing', [shortcode])"
|
||||||
|
:disabled="pack.remote !== undefined"
|
||||||
|
:shortcode="shortcode" :file="file" :packName="packName"
|
||||||
|
@updatePackFiles="updatePackFiles" @displayError="displayError"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<StillImage
|
||||||
|
class="emoji"
|
||||||
|
:src="emojiAddr(file)"
|
||||||
|
:title="`:${shortcode}:`"
|
||||||
|
:alt="`:${shortcode}:`"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</EmojiEditingPopover>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./emoji_tab.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss" src="./emoji_tab.scss"></style>
|
|
@ -55,12 +55,16 @@ const FrontendsTab = {
|
||||||
return fe.refs.includes(frontend.ref)
|
return fe.refs.includes(frontend.ref)
|
||||||
},
|
},
|
||||||
getSuggestedRef (frontend) {
|
getSuggestedRef (frontend) {
|
||||||
|
if (this.adminDraft) {
|
||||||
const defaultFe = this.adminDraft[':pleroma'][':frontends'][':primary']
|
const defaultFe = this.adminDraft[':pleroma'][':frontends'][':primary']
|
||||||
if (defaultFe?.name === frontend.name && this.canInstall(defaultFe)) {
|
if (defaultFe?.name === frontend.name && this.canInstall(defaultFe)) {
|
||||||
return defaultFe.ref
|
return defaultFe.ref
|
||||||
} else {
|
} else {
|
||||||
return frontend.refs[0]
|
return frontend.refs[0]
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return frontend.refs[0]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
update (frontend, suggestRef) {
|
update (frontend, suggestRef) {
|
||||||
const ref = suggestRef || this.getSuggestedRef(frontend)
|
const ref = suggestRef || this.getSuggestedRef(frontend)
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
<h2>{{ $t('admin_dash.tabs.frontends') }}</h2>
|
<h2>{{ $t('admin_dash.tabs.frontends') }}</h2>
|
||||||
<p>{{ $t('admin_dash.frontend.wip_notice') }}</p>
|
<p>{{ $t('admin_dash.frontend.wip_notice') }}</p>
|
||||||
<ul class="setting-list">
|
<ul class="setting-list" v-if="adminDraft">
|
||||||
<li>
|
<li>
|
||||||
<h3>{{ $t('admin_dash.frontend.default_frontend') }}</h3>
|
<h3>{{ $t('admin_dash.frontend.default_frontend') }}</h3>
|
||||||
<p>{{ $t('admin_dash.frontend.default_frontend_tip') }}</p>
|
<p>{{ $t('admin_dash.frontend.default_frontend_tip') }}</p>
|
||||||
|
@ -23,6 +23,10 @@
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<div v-else class="setting-list">
|
||||||
|
{{ $t('admin_dash.frontend.default_frontend_unavail') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="setting-list relative">
|
<div class="setting-list relative">
|
||||||
<PanelLoading
|
<PanelLoading
|
||||||
v-if="working"
|
v-if="working"
|
||||||
|
@ -36,9 +40,9 @@
|
||||||
>
|
>
|
||||||
<strong>{{ frontend.name }}</strong>
|
<strong>{{ frontend.name }}</strong>
|
||||||
{{ ' ' }}
|
{{ ' ' }}
|
||||||
<span v-if="adminDraft[':pleroma'][':frontends'][':primary']?.name === frontend.name">
|
<span v-if="adminDraft && adminDraft[':pleroma'][':frontends'][':primary']?.name === frontend.name">
|
||||||
<i18n-t
|
<i18n-t
|
||||||
v-if="adminDraft[':pleroma'][':frontends'][':primary']?.ref === frontend.refs[0]"
|
v-if="adminDraft && adminDraft[':pleroma'][':frontends'][':primary']?.ref === frontend.refs[0]"
|
||||||
keypath="admin_dash.frontend.is_default"
|
keypath="admin_dash.frontend.is_default"
|
||||||
/>
|
/>
|
||||||
<i18n-t
|
<i18n-t
|
||||||
|
@ -46,7 +50,7 @@
|
||||||
keypath="admin_dash.frontend.is_default_custom"
|
keypath="admin_dash.frontend.is_default_custom"
|
||||||
>
|
>
|
||||||
<template #version>
|
<template #version>
|
||||||
<code>{{ adminDraft[':pleroma'][':frontends'][':primary'].ref }}</code>
|
<code>{{ adminDraft && adminDraft[':pleroma'][':frontends'][':primary'].ref }}</code>
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
</span>
|
</span>
|
||||||
|
@ -137,7 +141,7 @@
|
||||||
class="button button-default btn"
|
class="button button-default btn"
|
||||||
type="button"
|
type="button"
|
||||||
:disabled="
|
:disabled="
|
||||||
adminDraft[':pleroma'][':frontends'][':primary']?.name === frontend.name &&
|
!adminDraft || adminDraft[':pleroma'][':frontends'][':primary']?.name === frontend.name &&
|
||||||
adminDraft[':pleroma'][':frontends'][':primary']?.ref === frontend.refs[0]
|
adminDraft[':pleroma'][':frontends'][':primary']?.ref === frontend.refs[0]
|
||||||
"
|
"
|
||||||
@click="setDefault(frontend)"
|
@click="setDefault(frontend)"
|
||||||
|
|
|
@ -0,0 +1,208 @@
|
||||||
|
<template>
|
||||||
|
<Popover
|
||||||
|
trigger="click"
|
||||||
|
:placement="placement"
|
||||||
|
bound-to-selector=".emoji-list"
|
||||||
|
popover-class="emoji-tab-edit-popover popover-default"
|
||||||
|
ref="emojiPopover"
|
||||||
|
:bound-to="{ x: 'container' }"
|
||||||
|
:offset="{ y: 5 }"
|
||||||
|
:disabled="disabled"
|
||||||
|
:class="{'emoji-unsaved': isEdited}"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<slot name="trigger" />
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<h3>
|
||||||
|
{{ title }}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<StillImage class="emoji" v-if="emojiPreview" :src="emojiPreview" />
|
||||||
|
<div v-else class="emoji"></div>
|
||||||
|
|
||||||
|
<div class="emoji-tab-popover-input" v-if="newUpload">
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
class="emoji-tab-popover-file"
|
||||||
|
@change="uploadFile = $event.target.files">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="emoji-tab-popover-input">
|
||||||
|
<label>
|
||||||
|
{{ $t('admin_dash.emoji.shortcode') }}
|
||||||
|
<input class="emoji-data-input"
|
||||||
|
v-model="editedShortcode"
|
||||||
|
:placeholder="$t('admin_dash.emoji.new_shortcode')">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="emoji-tab-popover-input">
|
||||||
|
<label>
|
||||||
|
{{ $t('admin_dash.emoji.filename') }}
|
||||||
|
|
||||||
|
<input class="emoji-data-input"
|
||||||
|
v-model="editedFile"
|
||||||
|
:placeholder="$t('admin_dash.emoji.new_filename')">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="button button-default btn"
|
||||||
|
type="button"
|
||||||
|
:disabled="newUpload ? uploadFile.length == 0 : !isEdited"
|
||||||
|
@click="newUpload ? uploadEmoji() : saveEditedEmoji()">
|
||||||
|
{{ $t('admin_dash.emoji.save') }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<template v-if="!newUpload">
|
||||||
|
<button
|
||||||
|
class="button button-default btn emoji-tab-popover-button"
|
||||||
|
type="button"
|
||||||
|
@click="deleteModalVisible = true">
|
||||||
|
{{ $t('admin_dash.emoji.delete') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button button-default btn emoji-tab-popover-button"
|
||||||
|
type="button"
|
||||||
|
@click="revertEmoji">
|
||||||
|
{{ $t('admin_dash.emoji.revert') }}
|
||||||
|
</button>
|
||||||
|
<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="deleteEmoji" >
|
||||||
|
{{ $t('admin_dash.emoji.delete_confirm', [shortcode]) }}
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Popover from 'components/popover/popover.vue'
|
||||||
|
import ConfirmModal from 'components/confirm_modal/confirm_modal.vue'
|
||||||
|
import StillImage from 'components/still-image/still-image.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { Popover, ConfirmModal, StillImage },
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
uploadFile: [],
|
||||||
|
editedShortcode: this.shortcode,
|
||||||
|
editedFile: this.file,
|
||||||
|
deleteModalVisible: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
emojiPreview () {
|
||||||
|
if (this.newUpload && this.uploadFile.length > 0) {
|
||||||
|
return URL.createObjectURL(this.uploadFile[0])
|
||||||
|
} else if (!this.newUpload) {
|
||||||
|
return this.emojiAddr(this.file)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
isEdited () {
|
||||||
|
return !this.newUpload && (this.editedShortcode !== this.shortcode || this.editedFile !== this.file)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inject: ['emojiAddr'],
|
||||||
|
methods: {
|
||||||
|
saveEditedEmoji () {
|
||||||
|
if (!this.isEdited) return
|
||||||
|
|
||||||
|
this.$store.state.api.backendInteractor.updateEmojiFile(
|
||||||
|
{ packName: this.packName, shortcode: this.shortcode, newShortcode: this.editedShortcode, newFilename: this.editedFile, force: false }
|
||||||
|
).then(resp => {
|
||||||
|
if (resp.error !== undefined) {
|
||||||
|
this.$emit('displayError', resp.error)
|
||||||
|
return Promise.reject(resp.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.json()
|
||||||
|
}).then(resp => this.$emit('updatePackFiles', resp))
|
||||||
|
},
|
||||||
|
uploadEmoji () {
|
||||||
|
this.$store.state.api.backendInteractor.addNewEmojiFile({
|
||||||
|
packName: this.packName,
|
||||||
|
file: this.uploadFile[0],
|
||||||
|
shortcode: this.editedShortcode,
|
||||||
|
filename: this.editedFile
|
||||||
|
}).then(resp => resp.json()).then(resp => {
|
||||||
|
if (resp.error !== undefined) {
|
||||||
|
this.$emit('displayError', resp.error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('updatePackFiles', resp)
|
||||||
|
this.$refs.emojiPopover.hidePopover()
|
||||||
|
|
||||||
|
this.editedFile = ''
|
||||||
|
this.editedShortcode = ''
|
||||||
|
this.uploadFile = []
|
||||||
|
})
|
||||||
|
},
|
||||||
|
revertEmoji () {
|
||||||
|
this.editedFile = this.file
|
||||||
|
this.editedShortcode = this.shortcode
|
||||||
|
},
|
||||||
|
deleteEmoji () {
|
||||||
|
this.deleteModalVisible = false
|
||||||
|
|
||||||
|
this.$store.state.api.backendInteractor.deleteEmojiFile(
|
||||||
|
{ packName: this.packName, shortcode: this.shortcode }
|
||||||
|
).then(resp => resp.json()).then(resp => {
|
||||||
|
if (resp.error !== undefined) {
|
||||||
|
this.$emit('displayError', resp.error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('updatePackFiles', resp)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['updatePackFiles', 'displayError'],
|
||||||
|
props: {
|
||||||
|
placement: String,
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
|
||||||
|
newUpload: Boolean,
|
||||||
|
|
||||||
|
title: String,
|
||||||
|
packName: String,
|
||||||
|
shortcode: {
|
||||||
|
type: String,
|
||||||
|
// Only exists when this is not a new upload
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
file: {
|
||||||
|
type: String,
|
||||||
|
// Only exists when this is not a new upload
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.emoji-tab-edit-popover {
|
||||||
|
padding-left: 0.5em;
|
||||||
|
padding-right: 0.5em;
|
||||||
|
padding-bottom: 0.5em;
|
||||||
|
|
||||||
|
.emoji {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -15,7 +15,7 @@
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="modified-tooltip">
|
<div class="modified-tooltip">
|
||||||
{{ $t('settings.setting_changed') }}
|
{{ $t(messageKey) }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
@ -33,7 +33,13 @@ library.add(
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { Popover },
|
components: { Popover },
|
||||||
props: ['changed']
|
props: {
|
||||||
|
changed: Boolean,
|
||||||
|
messageKey: {
|
||||||
|
type: String,
|
||||||
|
default: 'settings.setting_changed'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -195,7 +195,8 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
canHardReset () {
|
canHardReset () {
|
||||||
return this.realSource === 'admin' && this.$store.state.adminSettings.modifiedPaths.has(this.canonPath.join(' -> '))
|
return this.realSource === 'admin' && this.$store.state.adminSettings.modifiedPaths &&
|
||||||
|
this.$store.state.adminSettings.modifiedPaths.has(this.canonPath.join(' -> '))
|
||||||
},
|
},
|
||||||
matchesExpertLevel () {
|
matchesExpertLevel () {
|
||||||
return (this.expert || 0) <= this.$store.state.config.expertLevel > 0
|
return (this.expert || 0) <= this.$store.state.config.expertLevel > 0
|
||||||
|
|
|
@ -3,6 +3,7 @@ import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
||||||
import InstanceTab from './admin_tabs/instance_tab.vue'
|
import InstanceTab from './admin_tabs/instance_tab.vue'
|
||||||
import LimitsTab from './admin_tabs/limits_tab.vue'
|
import LimitsTab from './admin_tabs/limits_tab.vue'
|
||||||
import FrontendsTab from './admin_tabs/frontends_tab.vue'
|
import FrontendsTab from './admin_tabs/frontends_tab.vue'
|
||||||
|
import EmojiTab from './admin_tabs/emoji_tab.vue'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
|
@ -33,7 +34,8 @@ const SettingsModalAdminContent = {
|
||||||
|
|
||||||
InstanceTab,
|
InstanceTab,
|
||||||
LimitsTab,
|
LimitsTab,
|
||||||
FrontendsTab
|
FrontendsTab,
|
||||||
|
EmojiTab
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
user () {
|
user () {
|
||||||
|
|
|
@ -60,6 +60,14 @@
|
||||||
>
|
>
|
||||||
<FrontendsTab />
|
<FrontendsTab />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
:label="$t('admin_dash.tabs.emoji')"
|
||||||
|
icon="face-smile-beam"
|
||||||
|
data-tab-name="emoji"
|
||||||
|
>
|
||||||
|
<EmojiTab />
|
||||||
|
</div>
|
||||||
</tab-switcher>
|
</tab-switcher>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<video
|
<video
|
||||||
class="video"
|
class="video"
|
||||||
preload="metadata"
|
preload="metadata"
|
||||||
:src="attachment.url + '#t=0.5'"
|
:src="attachment.url + '#t=0.00000000000001'"
|
||||||
:loop="loopVideo"
|
:loop="loopVideo"
|
||||||
:controls="controls"
|
:controls="controls"
|
||||||
:alt="attachment.description"
|
:alt="attachment.description"
|
||||||
|
|
|
@ -882,7 +882,8 @@
|
||||||
"nodb": "No DB Config",
|
"nodb": "No DB Config",
|
||||||
"instance": "Instance",
|
"instance": "Instance",
|
||||||
"limits": "Limits",
|
"limits": "Limits",
|
||||||
"frontends": "Front-ends"
|
"frontends": "Front-ends",
|
||||||
|
"emoji": "Emoji"
|
||||||
},
|
},
|
||||||
"nodb": {
|
"nodb": {
|
||||||
"heading": "Database config is disabled",
|
"heading": "Database config is disabled",
|
||||||
|
@ -932,10 +933,55 @@
|
||||||
"wip_notice": "Please note that this section is a WIP and lacks certain features as backend implementation of front-end management is incomplete.",
|
"wip_notice": "Please note that this section is a WIP and lacks certain features as backend implementation of front-end management is incomplete.",
|
||||||
"default_frontend": "Default front-end",
|
"default_frontend": "Default front-end",
|
||||||
"default_frontend_tip": "Default front-end will be shown to all users. Currently there's no way to for a user to select personal front-end. If you switch away from PleromaFE you'll most likely have to use old and buggy AdminFE to do instance configuration until we replace it.",
|
"default_frontend_tip": "Default front-end will be shown to all users. Currently there's no way to for a user to select personal front-end. If you switch away from PleromaFE you'll most likely have to use old and buggy AdminFE to do instance configuration until we replace it.",
|
||||||
|
"default_frontend_unavail": "Default frontend settings are not available, as this requires configuration in the database",
|
||||||
"available_frontends": "Available for install",
|
"available_frontends": "Available for install",
|
||||||
"failure_installing_frontend": "Failed to install frontend {version}: {reason}",
|
"failure_installing_frontend": "Failed to install frontend {version}: {reason}",
|
||||||
"success_installing_frontend": "Frontend {version} successfully installed"
|
"success_installing_frontend": "Frontend {version} successfully installed"
|
||||||
},
|
},
|
||||||
|
"emoji": {
|
||||||
|
"global_actions": "Global actions",
|
||||||
|
"reload": "Reload emoji",
|
||||||
|
"importFS": "Import emoji from filesystem",
|
||||||
|
"error": "Error: {0}",
|
||||||
|
"create_pack": "Create pack",
|
||||||
|
"delete_pack": "Delete pack",
|
||||||
|
"new_pack_name": "New pack name",
|
||||||
|
"create": "Create",
|
||||||
|
"emoji_packs": "Emoji packs",
|
||||||
|
"remote_packs": "Remote packs",
|
||||||
|
"do_list": "List",
|
||||||
|
"remote_pack_instance": "Remote pack instance",
|
||||||
|
"emoji_pack": "Emoji pack",
|
||||||
|
"edit_pack": "Edit pack",
|
||||||
|
"description": "Description",
|
||||||
|
"homepage": "Homepage",
|
||||||
|
"fallback_src": "Fallback source",
|
||||||
|
"fallback_sha256": "Fallback SHA256",
|
||||||
|
"share": "Share",
|
||||||
|
"save": "Save",
|
||||||
|
"save_meta": "Save metadata",
|
||||||
|
"revert_meta": "Revert metadata",
|
||||||
|
"delete": "Delete",
|
||||||
|
"revert": "Revert",
|
||||||
|
"add_file": "Add file",
|
||||||
|
"adding_new": "Adding new emoji",
|
||||||
|
"shortcode": "Shortcode",
|
||||||
|
"filename": "Filename",
|
||||||
|
"new_shortcode": "Shortcode, leave blank to infer",
|
||||||
|
"new_filename": "Filename, leave blank to 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",
|
||||||
|
"download_as_name_full": "New name, leave blank to reuse",
|
||||||
|
"files": "Files",
|
||||||
|
"editing": "Editing {0}",
|
||||||
|
"delete_title": "Delete?",
|
||||||
|
"metadata_changed": "Metadata different from saved",
|
||||||
|
"emoji_changed": "Unsaved emoji file changes, check highlighted emoji",
|
||||||
|
"replace_warning": "This will REPLACE the local pack of the same name"
|
||||||
|
},
|
||||||
"temp_overrides": {
|
"temp_overrides": {
|
||||||
":pleroma": {
|
":pleroma": {
|
||||||
":instance": {
|
":instance": {
|
||||||
|
|
|
@ -115,6 +115,15 @@ const PLEROMA_ADMIN_DESCRIPTIONS_URL = '/api/pleroma/admin/config/descriptions'
|
||||||
const PLEROMA_ADMIN_FRONTENDS_URL = '/api/pleroma/admin/frontends'
|
const PLEROMA_ADMIN_FRONTENDS_URL = '/api/pleroma/admin/frontends'
|
||||||
const PLEROMA_ADMIN_FRONTENDS_INSTALL_URL = '/api/pleroma/admin/frontends/install'
|
const PLEROMA_ADMIN_FRONTENDS_INSTALL_URL = '/api/pleroma/admin/frontends/install'
|
||||||
|
|
||||||
|
const PLEROMA_EMOJI_RELOAD_URL = '/api/pleroma/admin/reload_emoji'
|
||||||
|
const PLEROMA_EMOJI_IMPORT_FS_URL = '/api/pleroma/emoji/packs/import'
|
||||||
|
const PLEROMA_EMOJI_PACKS_URL = (page, pageSize) => `/api/v1/pleroma/emoji/packs?page=${page}&page_size=${pageSize}`
|
||||||
|
const PLEROMA_EMOJI_PACK_URL = (name) => `/api/v1/pleroma/emoji/pack?name=${name}`
|
||||||
|
const PLEROMA_EMOJI_PACKS_DL_REMOTE_URL = '/api/v1/pleroma/emoji/packs/download'
|
||||||
|
const PLEROMA_EMOJI_PACKS_LS_REMOTE_URL =
|
||||||
|
(url, page, pageSize) => `/api/v1/pleroma/emoji/packs/remote?url=${url}&page=${page}&page_size=${pageSize}`
|
||||||
|
const PLEROMA_EMOJI_UPDATE_FILE_URL = (name) => `/api/v1/pleroma/emoji/packs/files?name=${name}`
|
||||||
|
|
||||||
const oldfetch = window.fetch
|
const oldfetch = window.fetch
|
||||||
|
|
||||||
const fetch = (url, options) => {
|
const fetch = (url, options) => {
|
||||||
|
@ -1793,6 +1802,90 @@ const fetchScrobbles = ({ accountId, limit = 1 }) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deleteEmojiPack = ({ name }) => {
|
||||||
|
return fetch(PLEROMA_EMOJI_PACK_URL(name), { method: 'DELETE' })
|
||||||
|
}
|
||||||
|
|
||||||
|
const reloadEmoji = () => {
|
||||||
|
return fetch(PLEROMA_EMOJI_RELOAD_URL, { method: 'POST' })
|
||||||
|
}
|
||||||
|
|
||||||
|
const importEmojiFromFS = () => {
|
||||||
|
return fetch(PLEROMA_EMOJI_IMPORT_FS_URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
const createEmojiPack = ({ name }) => {
|
||||||
|
return fetch(PLEROMA_EMOJI_PACK_URL(name), { method: 'POST' })
|
||||||
|
}
|
||||||
|
|
||||||
|
const listEmojiPacks = ({ page, pageSize }) => {
|
||||||
|
return fetch(PLEROMA_EMOJI_PACKS_URL(page, pageSize))
|
||||||
|
}
|
||||||
|
|
||||||
|
const listRemoteEmojiPacks = ({ instance, page, pageSize }) => {
|
||||||
|
if (!instance.startsWith('http')) {
|
||||||
|
instance = 'https://' + instance
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch(
|
||||||
|
PLEROMA_EMOJI_PACKS_LS_REMOTE_URL(instance, page, pageSize),
|
||||||
|
{
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadRemoteEmojiPack = ({ instance, packName, as }) => {
|
||||||
|
return fetch(
|
||||||
|
PLEROMA_EMOJI_PACKS_DL_REMOTE_URL,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
url: instance, name: packName, as
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveEmojiPackMetadata = ({ name, newData }) => {
|
||||||
|
return fetch(
|
||||||
|
PLEROMA_EMOJI_PACK_URL(name),
|
||||||
|
{
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ metadata: newData })
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const addNewEmojiFile = ({ packName, file, shortcode, filename }) => {
|
||||||
|
const data = new FormData()
|
||||||
|
if (filename.trim() !== '') { data.set('filename', filename) }
|
||||||
|
if (shortcode.trim() !== '') { data.set('shortcode', shortcode) }
|
||||||
|
data.set('file', file)
|
||||||
|
|
||||||
|
return fetch(
|
||||||
|
PLEROMA_EMOJI_UPDATE_FILE_URL(packName),
|
||||||
|
{ method: 'POST', body: data }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateEmojiFile = ({ packName, shortcode, newShortcode, newFilename, force }) => {
|
||||||
|
return fetch(
|
||||||
|
PLEROMA_EMOJI_UPDATE_FILE_URL(packName),
|
||||||
|
{
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ shortcode, new_shortcode: newShortcode, new_filename: newFilename, force })
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteEmojiFile = ({ packName, shortcode }) => {
|
||||||
|
return fetch(`${PLEROMA_EMOJI_UPDATE_FILE_URL(packName)}&shortcode=${shortcode}`, { method: 'DELETE' })
|
||||||
|
}
|
||||||
|
|
||||||
const apiService = {
|
const apiService = {
|
||||||
verifyCredentials,
|
verifyCredentials,
|
||||||
fetchTimeline,
|
fetchTimeline,
|
||||||
|
@ -1912,7 +2005,18 @@ const apiService = {
|
||||||
fetchInstanceConfigDescriptions,
|
fetchInstanceConfigDescriptions,
|
||||||
fetchAvailableFrontends,
|
fetchAvailableFrontends,
|
||||||
pushInstanceDBConfig,
|
pushInstanceDBConfig,
|
||||||
installFrontend
|
installFrontend,
|
||||||
|
importEmojiFromFS,
|
||||||
|
reloadEmoji,
|
||||||
|
listEmojiPacks,
|
||||||
|
createEmojiPack,
|
||||||
|
deleteEmojiPack,
|
||||||
|
saveEmojiPackMetadata,
|
||||||
|
addNewEmojiFile,
|
||||||
|
updateEmojiFile,
|
||||||
|
deleteEmojiFile,
|
||||||
|
listRemoteEmojiPacks,
|
||||||
|
downloadRemoteEmojiPack
|
||||||
}
|
}
|
||||||
|
|
||||||
export default apiService
|
export default apiService
|
||||||
|
|
Loading…
Reference in New Issue