Editing emojis in popover, pack creation/deletion

Also fixed some API calls since they weren't working apparently
This commit is contained in:
Ekaterina Vaartis 2024-01-07 02:45:49 +03:00
parent bfdad56b0d
commit 091532d577
5 changed files with 272 additions and 89 deletions

View File

@ -1,20 +1,46 @@
import { clone } from 'lodash'
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
import StringSetting from '../helpers/string_setting.vue' import StringSetting from '../helpers/string_setting.vue'
import Checkbox from 'components/checkbox/checkbox.vue' import Checkbox from 'components/checkbox/checkbox.vue'
import StillImage from 'components/still-image/still-image.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'
const EmojiTab = { const EmojiTab = {
components: { components: {
TabSwitcher, TabSwitcher,
StringSetting, StringSetting,
Checkbox, Checkbox,
StillImage StillImage,
Select,
Popover,
ConfirmModal,
ModifiedIndicator
}, },
data () { data () {
return { return {
knownPacks: { }, knownPacks: { },
editedParts: { } editedParts: { },
editedMetadata: { },
packName: '',
newPackName: '',
deleteModalVisible: false
}
},
computed: {
pack () {
return this.packName !== '' ? this.knownPacks[this.packName] : undefined
},
packMeta () {
if (this.editedMetadata[this.packName] === undefined) {
this.editedMetadata[this.packName] = clone(this.knownPacks[this.packName].pack)
}
return this.editedMetadata[this.packName]
} }
}, },
@ -25,37 +51,90 @@ const EmojiTab = {
importFromFS () { importFromFS () {
this.$store.state.api.backendInteractor.importEmojiFromFS() this.$store.state.api.backendInteractor.importEmojiFromFS()
}, },
emojiAddr (packName, name) { emojiAddr (name) {
return `${this.$store.state.instance.server}/emoji/${encodeURIComponent(packName)}/${name}` return `${this.$store.state.instance.server}/emoji/${encodeURIComponent(this.packName)}/${name}`
}, },
editEmoji (packName, shortcode) {
if (this.editedParts[packName] === undefined) { this.editedParts[packName] = {} }
this.editedParts[packName][shortcode] = { createEmojiPack () {
shortcode, file: this.knownPacks[packName].files[shortcode] this.$store.state.api.backendInteractor.createEmojiPack(
{ name: this.newPackName }
).then(resp => resp.json()).then(resp => {
if (resp === 'ok') {
return this.refreshPackList()
} else {
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 {
return Promise.reject(resp)
}
}).then(done => {
delete this.editedMetadata[this.packName]
delete this.editedParts[this.packName]
this.deleteModalVisible = false
this.packName = ''
})
},
metaEdited (prop) {
return this.pack && this.packMeta[prop] !== this.pack.pack[prop]
},
savePackMetadata () {
this.$store.state.api.backendInteractor.saveEmojiPackMetadata({ name: this.packName, newData: this.packMeta }).then(
resp => resp.json()
).then(resp => {
// Update actual pack data
this.pack.pack = resp
// Delete edited pack data, should auto-update itself
delete this.editedMetadata[this.packName]
})
},
editEmoji (shortcode) {
if (this.editedParts[this.packName] === undefined) { this.editedParts[this.packName] = {} }
if (this.editedParts[this.packName][shortcode] === undefined) {
this.editedParts[this.packName][shortcode] = {
shortcode, file: this.knownPacks[this.packName].files[shortcode]
}
} }
}, },
saveEditedEmoji (packName, shortcode) { saveEditedEmoji (shortcode) {
const edited = this.editedParts[packName][shortcode] const edited = this.editedParts[this.packName][shortcode]
this.$store.state.api.backendInteractor.updateEmojiFile( this.$store.state.api.backendInteractor.updateEmojiFile(
{ packName, shortcode, newShortcode: edited.shortcode, newFilename: edited.file, force: false } { packName: this.packName, shortcode, newShortcode: edited.shortcode, newFilename: edited.file, force: false }
).then(resp => ).then(resp =>
resp.ok ? resp.json() : resp.text().then(respText => Promise.reject(respText)) resp.ok ? resp.json() : resp.text().then(respText => Promise.reject(respText))
).then(resp => { ).then(resp => {
this.knownPacks[packName].files = resp this.knownPacks[this.packName].files = resp
delete this.editedParts[packName][shortcode] delete this.editedParts[this.packName][shortcode]
}) })
},
refreshPackList () {
return this.$store.state.api.backendInteractor.listEmojiPacks()
.then(data => data.json())
.then(packData => {
this.knownPacks = packData.packs
})
} }
}, },
mounted () { mounted () {
this.$store.state.api.backendInteractor.listEmojiPacks() this.refreshPackList()
.then(data => data.json())
.then(packData => {
this.knownPacks = packData.packs
console.log(this.knownPacks)
})
} }
} }

View File

@ -1,3 +1,5 @@
@import "src/variables";
.emoji-tab { .emoji-tab {
.btn-group .btn { .btn-group .btn {
margin-left: 0.5em; margin-left: 0.5em;
@ -21,4 +23,24 @@
width: 32px; width: 32px;
height: 32px; height: 32px;
} }
.emoji-unsaved {
box-shadow: 2px 3px 5px var(--cBlue, $fallback--cBlue);
}
.emoji-list {
display: flex;
flex-wrap: wrap;
gap: 1em 1em;
}
}
.emoji-tab-edit-popover {
padding-left: 0.5em;
padding-right: 0.5em;
padding-bottom: 0.5em;
}
.emoji-tab-popover-button {
margin-left: 0.5em;
} }

View File

@ -6,84 +6,166 @@
<div class="setting-item"> <div class="setting-item">
<h2>{{ $t('admin_dash.tabs.emoji') }}</h2> <h2>{{ $t('admin_dash.tabs.emoji') }}</h2>
<span class="btn-group"> <ul class="setting-list">
<button <li class="btn-group setting-item">
class="button button-default btn" <button
type="button" class="button button-default btn"
@click="reloadEmoji"> type="button"
{{ $t('admin_dash.emoji.reload') }} @click="reloadEmoji">
</button> {{ $t('admin_dash.emoji.reload') }}
<button </button>
class="button button-default btn" <button
type="button" class="button button-default btn"
@click="importFromFS"> type="button"
{{ $t('admin_dash.emoji.importFS') }} @click="importFromFS">
</button> {{ $t('admin_dash.emoji.importFS') }}
</span> </button>
</li>
<tab-switcher :scrollable-tabs="true" v-if="Object.keys(knownPacks).length > 0"> <li class="setting-item btn-group">
<div v-for="(pack, packName) in knownPacks" :label="packName" :key="packName"> <button
<div class="pack-info-wrapper"> class="button button-default btn"
<ul class="setting-list"> type="button"
<li> @click="$refs.createPackPopover.showPopover">
<div>Description</div> Create pack
<textarea </button>
v-model="pack.pack.description" <Popover
class="bio resize-height" /> ref="createPackPopover"
</li> trigger="click"
<li> placement="bottom"
<div>Homepage</div> bound-to-selector=".emoji-tab"
<input class="emoji-info-input" v-model="pack.pack.homepage"> :bound-to="{ x: 'container' }"
</li> :offset="{ y: 5 }"
<li> >
<div>Fallback source</div> <template #content>
<input class="emoji-info-input" v-model="pack.pack['fallback-src']"> <input v-model="newPackName" placeholder="New pack name">
</li> <button
<li> class="button button-default btn emoji-tab-popover-button"
<Checkbox v-model="pack.pack['can-download']">Downloadable</Checkbox> type="button"
</li> @click="createEmojiPack">
</ul> Create
</div> </button>
</template>
</Popover>
<h2>Files</h2> <button
class="button button-default btn"
:disabled="packName == ''"
type="button"
@click="deleteModalVisible = true">
Delete pack
</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 {{ packName }}?
</ConfirmModal>
</li>
<li>
<Select class="form-control" v-model="packName">
<option value="" disabled hidden>Emoji pack</option>
<option v-for="(pack, listPackName) in knownPacks" :label="listPackName" :key="listPackName">
{{ listPackName }}
</option>
</Select>
</li>
</ul>
<div v-if="packName !== ''">
<div class="pack-info-wrapper">
<ul class="setting-list"> <ul class="setting-list">
<li v-for="(file, shortcode) in pack.files" :key="shortcode"> <li>
<StillImage <div>
class="emoji img" Description
:src="emojiAddr(packName, file)" <ModifiedIndicator :changed="metaEdited('description')" />
:title="`:${shortcode}:`" </div>
:alt="`:${shortcode}:`" <textarea
/> v-model="packMeta.description"
class="bio resize-height" />
</li>
<li>
<div>
Homepage
<ModifiedIndicator :changed="metaEdited('homepage')" />
</div>
<input class="emoji-info-input" v-model="packMeta.homepage">
</li>
<li>
<div>
Fallback source
<ModifiedIndicator :changed="metaEdited('fallback-src')" />
</div>
<input class="emoji-info-input" v-model="packMeta['fallback-src']">
</li>
<li>
<div>Fallback SHA256</div>
<input :disabled="true" class="emoji-info-input" v-model="packMeta['fallback-src-sha256']">
</li>
<li>
<Checkbox v-model="packMeta['share-files']">Share</Checkbox>
<template v-if="editedParts[packName] !== undefined && editedParts[packName][shortcode] !== undefined"> <ModifiedIndicator :changed="metaEdited('share-files')" />
</li>
<li>
<button
class="button button-default btn"
type="button"
@click="savePackMetadata">
Save
</button>
</li>
</ul>
</div>
<h2>Files</h2>
<div class="emoji-list" v-if="pack">
<Popover
v-for="(file, shortcode) in pack.files" :key="shortcode"
trigger="click"
placement="top"
:class="{'emoji-unsaved': editedParts[packName] !== undefined && editedParts[packName][shortcode] !== undefined}"
popover-class="emoji-tab-edit-popover popover-default"
bound-to-selector=".emoji-list"
:bound-to="{ x: 'container' }"
:offset="{ y: 5 }"
@show="editEmoji(shortcode)"
>
<template #content>
<h3>
Editing <i>{{ shortcode }}</i>
</h3>
<div v-if="editedParts[packName] !== undefined && editedParts[packName][shortcode] !== undefined">
<input class="emoji-data-input" <input class="emoji-data-input"
v-model="editedParts[packName][shortcode].shortcode"> v-model="editedParts[packName][shortcode].shortcode">
<input class="emoji-data-input" <input class="emoji-data-input"
v-model="editedParts[packName][shortcode].file"> v-model="editedParts[packName][shortcode].file">
<button <button
class="button button-default btn" class="button button-default btn emoji-tab-popover-button"
type="button" type="button"
@click="saveEditedEmoji(packName, shortcode)"> @click="saveEditedEmoji(shortcode)">
Save Save
</button> </button>
</template>
<template v-else>
<input disabled class="emoji-data-input" :value="shortcode">
<input disabled class="emoji-data-input" :value="file">
<button </div>
class="button button-default btn" </template>
type="button" <template #trigger>
@click="editEmoji(packName, shortcode)"> <StillImage
Edit class="emoji"
</button> :src="emojiAddr(file)"
</template> :title="`:${shortcode}:`"
</li> :alt="`:${shortcode}:`"
</ul> />
</template>
</Popover>
</div> </div>
</tab-switcher> </div>
</div> </div>
</div> </div>
</template> </template>

View File

@ -63,7 +63,7 @@
<div <div
:label="$t('admin_dash.tabs.emoji')" :label="$t('admin_dash.tabs.emoji')"
icon="laptop-code" icon="face-smile-beam"
data-tab-name="emoji" data-tab-name="emoji"
> >
<EmojiTab /> <EmojiTab />

View File

@ -116,12 +116,12 @@ const PLEROMA_ADMIN_FRONTENDS_INSTALL_URL = '/api/pleroma/admin/frontends/instal
const PLEROMA_EMOJI_RELOAD_URL = '/api/pleroma/admin/reload_emoji' const PLEROMA_EMOJI_RELOAD_URL = '/api/pleroma/admin/reload_emoji'
const PLEROMA_EMOJI_IMPORT_FS_URL = '/api/pleroma/emoji/packs/import' const PLEROMA_EMOJI_IMPORT_FS_URL = '/api/pleroma/emoji/packs/import'
const PLEROMA_EMOJI_PACKS_URL = (page, pageSize) => `/api/pleroma/emoji/packs?page=${page}&page_size=${pageSize}` const PLEROMA_EMOJI_PACKS_URL = (page, pageSize) => `/api/v1/pleroma/emoji/packs?page=${page}&page_size=${pageSize}`
const PLEROMA_EMOJI_PACK_URL = (name) => `/api/pleroma/emoji/pack?name=${name}` const PLEROMA_EMOJI_PACK_URL = (name) => `/api/v1/pleroma/emoji/pack?name=${name}`
const PLEROMA_EMOJI_PACKS_DL_REMOTE_URL = '/api/pleroma/emoji/packs/download' const PLEROMA_EMOJI_PACKS_DL_REMOTE_URL = '/api/v1/pleroma/emoji/packs/download'
const PLEROMA_EMOJI_PACKS_LS_REMOTE_URL = const PLEROMA_EMOJI_PACKS_LS_REMOTE_URL =
(url, page, pageSize) => `/api/pleroma/emoji/packs/remote?url=${url}&page=${page}&page_size=${pageSize}` (url, page, pageSize) => `/api/v1/pleroma/emoji/packs/remote?url=${url}&page=${page}&page_size=${pageSize}`
const PLEROMA_EMOJI_UPDATE_FILE_URL = (name) => `/api/pleroma/emoji/packs/files?name=${name}` const PLEROMA_EMOJI_UPDATE_FILE_URL = (name) => `/api/v1/pleroma/emoji/packs/files?name=${name}`
const oldfetch = window.fetch const oldfetch = window.fetch
@ -1809,7 +1809,7 @@ const importEmojiFromFS = () => {
} }
const createEmojiPack = ({ name }) => { const createEmojiPack = ({ name }) => {
return fetch(PLEROMA_EMOJI_PACK_URL(name), { method: 'PUT' }) return fetch(PLEROMA_EMOJI_PACK_URL(name), { method: 'POST' })
} }
const listEmojiPacks = () => { const listEmojiPacks = () => {
@ -1850,7 +1850,7 @@ const saveEmojiPackMetadata = ({ name, newData }) => {
{ {
method: 'PATCH', method: 'PATCH',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, new_data: newData }) body: JSON.stringify({ metadata: newData })
} }
) )
} }