Separate emoji editing and upload into a separate component

Handle all state in that component
This commit is contained in:
Ekaterina Vaartis 2024-01-19 23:16:21 +03:00
parent aa49838355
commit 8a030d935b
4 changed files with 253 additions and 233 deletions

View File

@ -7,8 +7,7 @@ import Select from 'components/select/select.vue'
import Popover from 'components/popover/popover.vue' import Popover from 'components/popover/popover.vue'
import ConfirmModal from 'components/confirm_modal/confirm_modal.vue' import ConfirmModal from 'components/confirm_modal/confirm_modal.vue'
import ModifiedIndicator from '../helpers/modified_indicator.vue' import ModifiedIndicator from '../helpers/modified_indicator.vue'
import EmojiEditingPopover from '../helpers/emoji_editing_popover.vue'
const newEmojiUploadBase = { shortcode: '', file: '', upload: [] }
const EmojiTab = { const EmojiTab = {
components: { components: {
@ -19,24 +18,31 @@ const EmojiTab = {
Select, Select,
Popover, Popover,
ConfirmModal, ConfirmModal,
ModifiedIndicator ModifiedIndicator,
EmojiEditingPopover
}, },
data () { data () {
return { return {
knownLocalPacks: { }, knownLocalPacks: { },
knownRemotePacks: { }, knownRemotePacks: { },
editedParts: { },
editedMetadata: { }, editedMetadata: { },
packName: '', packName: '',
newPackName: '', newPackName: '',
deleteModalVisible: false, deleteModalVisible: false,
newEmojiUpload: clone(newEmojiUploadBase),
remotePackInstance: '', remotePackInstance: '',
remotePackDownloadAs: '' remotePackDownloadAs: ''
} }
}, },
provide () {
return {
// Functions
emojiAddr: this.emojiAddr,
displayError: this.displayError
}
},
computed: { computed: {
pack () { pack () {
return this.packName !== '' ? this.knownPacks[this.packName] : undefined return this.packName !== '' ? this.knownPacks[this.packName] : undefined
@ -58,11 +64,6 @@ const EmojiTab = {
} }
return result return result
},
newEmojiUploadPreview () {
if (this.newEmojiUpload.upload.length > 0) {
return URL.createObjectURL(this.newEmojiUpload.upload[0])
}
} }
}, },
@ -82,27 +83,6 @@ const EmojiTab = {
} }
}, },
uploadEmoji () {
this.$refs.addEmojiPopover.hidePopover()
this.$store.state.api.backendInteractor.addNewEmojiFile({
packName: this.packName,
file: this.newEmojiUpload.upload[0],
shortcode: this.newEmojiUpload.shortcode,
filename: this.newEmojiUpload.file
}).then(resp => resp.json()).then(resp => {
if (resp.error !== undefined) {
this.displayError(resp.error)
return
}
this.pack.files = resp
this.sortPackFiles(this.packName)
this.newEmojiUpload = clone(newEmojiUploadBase)
})
},
createEmojiPack () { createEmojiPack () {
this.$store.state.api.backendInteractor.createEmojiPack( this.$store.state.api.backendInteractor.createEmojiPack(
{ name: this.newPackName } { name: this.newPackName }
@ -132,7 +112,6 @@ const EmojiTab = {
} }
}).then(done => { }).then(done => {
delete this.editedMetadata[this.packName] delete this.editedMetadata[this.packName]
delete this.editedParts[this.packName]
this.deleteModalVisible = false this.deleteModalVisible = false
this.packName = '' this.packName = ''
@ -162,68 +141,9 @@ const EmojiTab = {
}) })
}, },
revertEmoji (shortcode) { updatePackFiles (newFiles) {
// Delete current changes and overwrite them with defaults. If the window is closed, they'll get cleared anyway this.pack.files = newFiles
delete this.editedParts[this.packName][shortcode]
this.editEmoji(shortcode)
},
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.pack.files[shortcode]
}
}
},
deleteEmoji (shortcode) {
this.editedParts[this.packName][shortcode].deleteModalVisible = false
this.$store.state.api.backendInteractor.deleteEmojiFile(
{ packName: this.packName, shortcode }
).then(resp => resp.json()).then(resp => {
if (resp.error !== undefined) {
this.displayError(resp.error)
return
}
this.pack.files = resp
delete this.editedParts[this.packName][shortcode]
this.sortPackFiles(this.packName) this.sortPackFiles(this.packName)
})
},
closedEditedEmoji (shortcode) {
const edited = this.editedParts[this.packName][shortcode]
if (edited.shortcode === shortcode && edited.file === this.pack.files[shortcode]) {
delete this.editedParts[this.packName][shortcode]
return true
}
return false
},
saveEditedEmoji (shortcode) {
if (this.closedEditedEmoji(shortcode)) return
const edited = this.editedParts[this.packName][shortcode]
this.$store.state.api.backendInteractor.updateEmojiFile(
{ packName: this.packName, shortcode, newShortcode: edited.shortcode, newFilename: edited.file, force: false }
).then(resp => {
if (resp.error !== undefined) {
this.displayError(resp.error)
return Promise.reject(resp.error)
}
return resp.json()
}).then(resp => {
this.pack.files = resp
delete this.editedParts[this.packName][shortcode]
this.sortPackFiles(this.packName)
})
}, },
loadPacksPaginated (listFunction) { loadPacksPaginated (listFunction) {

View File

@ -39,17 +39,6 @@
margin-left: 0.5em; margin-left: 0.5em;
} }
.emoji-tab-edit-popover {
padding-left: 0.5em;
padding-right: 0.5em;
padding-bottom: 0.5em;
.emoji {
width: 32px;
height: 32px;
}
}
.emoji-tab-popover-input { .emoji-tab-popover-input {
margin-bottom: 0.5em; margin-bottom: 0.5em;

View File

@ -97,38 +97,44 @@
</li> </li>
</ul> </ul>
<div v-if="packName !== ''"> <div v-if="pack">
<div class="pack-info-wrapper"> <div class="pack-info-wrapper">
<ul class="setting-list"> <ul class="setting-list">
<li> <li>
<div> <label>
{{ $t('admin_dash.emoji.description') }} {{ $t('admin_dash.emoji.description') }}
<ModifiedIndicator :changed="metaEdited('description')" message-key="admin_dash.emoji.metadata_changed" /> <ModifiedIndicator :changed="metaEdited('description')" message-key="admin_dash.emoji.metadata_changed" />
</div>
<textarea <textarea
v-model="packMeta.description" v-model="packMeta.description"
:disabled="pack.remote !== undefined" :disabled="pack.remote !== undefined"
class="bio resize-height" /> class="bio resize-height" />
</label>
</li> </li>
<li> <li>
<div> <label>
{{ $t('admin_dash.emoji.homepage') }} {{ $t('admin_dash.emoji.homepage') }}
<ModifiedIndicator :changed="metaEdited('homepage')" message-key="admin_dash.emoji.metadata_changed" /> <ModifiedIndicator :changed="metaEdited('homepage')" message-key="admin_dash.emoji.metadata_changed" />
</div>
<input <input
class="emoji-info-input" v-model="packMeta.homepage" class="emoji-info-input" v-model="packMeta.homepage"
:disabled="pack.remote !== undefined"> :disabled="pack.remote !== undefined">
</label>
</li> </li>
<li> <li>
<div> <label>
{{ $t('admin_dash.emoji.fallback_src') }} {{ $t('admin_dash.emoji.fallback_src') }}
<ModifiedIndicator :changed="metaEdited('fallback-src')" message-key="admin_dash.emoji.metadata_changed" /> <ModifiedIndicator :changed="metaEdited('fallback-src')" message-key="admin_dash.emoji.metadata_changed" />
</div>
<input class="emoji-info-input" v-model="packMeta['fallback-src']" :disabled="pack.remote !== undefined"> <input class="emoji-info-input" v-model="packMeta['fallback-src']" :disabled="pack.remote !== undefined">
</label>
</li> </li>
<li> <li>
<div>{{ $t('admin_dash.emoji.fallback_sha256') }}</div> <label>
{{ $t('admin_dash.emoji.fallback_sha256') }}
<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']">
</label>
</li> </li>
<li> <li>
<Checkbox :disabled="pack.remote !== undefined" v-model="packMeta['share-files']"> <Checkbox :disabled="pack.remote !== undefined" v-model="packMeta['share-files']">
@ -218,132 +224,30 @@
{{ $t('admin_dash.emoji.files') }} {{ $t('admin_dash.emoji.files') }}
<ModifiedIndicator v-if="pack" <ModifiedIndicator v-if="pack"
:changed="editedParts[packName] && Object.keys(editedParts[packName]).length > 0" :changed="$refs.emojiPopovers && $refs.emojiPopovers.some(p => p.isEdited)"
message-key="admin_dash.emoji.emoji_changed"/> message-key="admin_dash.emoji.emoji_changed"/>
</h4> </h4>
<div class="emoji-list" v-if="pack"> <div class="emoji-list" v-if="pack">
<Popover <EmojiEditingPopover
v-if="pack.remote === undefined" v-if="pack.remote === undefined"
ref="addEmojiPopover" placement="bottom" new-upload
trigger="click" :title="$t('admin_dash.emoji.adding_new')"
placement="bottom" :packName="packName" @updatePackFiles="updatePackFiles"
bound-to-selector=".emoji-tab"
popover-class="emoji-tab-edit-popover popover-default"
:bound-to="{ x: 'container' }"
:offset="{ y: 5 }"
> >
<template #trigger> <template #trigger>
<FAIcon icon="plus" size="2x" :title="$t('admin_dash.emoji.add_file')" /> <FAIcon icon="plus" size="2x" :title="$t('admin_dash.emoji.add_file')" />
</template> </template>
<template #content> </EmojiEditingPopover>
<h3>
{{ $t('admin_dash.emoji.adding_new') }}
</h3>
<StillImage <EmojiEditingPopover
class="emoji" v-if="newEmojiUploadPreview" placement="top" ref="emojiPopovers"
:src="newEmojiUploadPreview"
/>
<div v-else class="emoji"></div>
<div class="emoji-tab-popover-input">
<input
type="file"
accept="image/*"
class="emoji-tab-popover-file"
@change="newEmojiUpload.upload = $event.target.files"
>
</div>
<div>
<div class="emoji-tab-popover-input">
<div>{{ $t('admin_dash.emoji.shortcode') }}</div>
<input class="emoji-data-input"
v-model="newEmojiUpload.shortcode"
:placeholder="$t('admin_dash.emoji.new_shortcode')">
</div>
<div class="emoji-tab-popover-input">
<div>{{ $t('admin_dash.emoji.filename') }}</div>
<input class="emoji-data-input"
v-model="newEmojiUpload.file"
:placeholder="$t('admin_dash.emoji.new_filename')">
</div>
<button
class="button button-default btn"
type="button"
:disabled="this.newEmojiUpload.upload.length == 0"
@click="uploadEmoji">
{{ $t('admin_dash.emoji.save') }}
</button>
</div>
</template>
</Popover>
<Popover
v-for="(file, shortcode) in pack.files" :key="shortcode" v-for="(file, shortcode) in pack.files" :key="shortcode"
trigger="click" :title="$t('admin_dash.emoji.editing', [shortcode])"
:disabled="pack.remote !== undefined" :disabled="pack.remote !== undefined"
placement="top" :shortcode="shortcode" :file="file" :packName="packName"
:class="{'emoji-unsaved': editedParts[packName] !== undefined && editedParts[packName][shortcode] !== undefined}" @updatePackFiles="updatePackFiles"
popover-class="emoji-tab-edit-popover popover-default"
bound-to-selector=".emoji-list"
:bound-to="{ x: 'container' }"
:offset="{ y: 5 }"
@show="editEmoji(shortcode)"
@close="closedEditedEmoji(shortcode)"
> >
<template #content>
<h3>
{{ $t('admin_dash.emoji.editing', [shortcode]) }}
</h3>
<StillImage class="emoji" :src="emojiAddr(file)" />
<div v-if="editedParts[packName] !== undefined && editedParts[packName][shortcode] !== undefined">
<div class="emoji-tab-popover-input">
<label for="emoji-edit-shortcode">{{ $t('admin_dash.emoji.shortcode') }}</label>
<input id="emoji-edit-shortcode" class="emoji-data-input"
v-model="editedParts[packName][shortcode].shortcode">
</div>
<div class="emoji-tab-popover-input">
<label for="emoji-edit-filename">{{ $t('admin_dash.emoji.filename') }}</label>
<input id="emoji-edit-filename" class="emoji-data-input"
v-model="editedParts[packName][shortcode].file">
</div>
<div>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
@click="saveEditedEmoji(shortcode)">
{{ $t('admin_dash.emoji.save') }}
</button>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
@click="editedParts[packName][shortcode].deleteModalVisible = true">
{{ $t('admin_dash.emoji.delete') }}
</button>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
@click="revertEmoji(shortcode)">
{{ $t('admin_dash.emoji.revert') }}
</button>
<ConfirmModal
v-if="editedParts[packName][shortcode].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="editedParts[packName][shortcode].deleteModalVisible = false"
@accepted="deleteEmoji(shortcode)" >
{{ $t('admin_dash.emoji.delete_confirm', [shortcode]) }}
</ConfirmModal>
</div>
</div>
</template>
<template #trigger> <template #trigger>
<StillImage <StillImage
class="emoji" class="emoji"
@ -352,7 +256,7 @@
:alt="`:${shortcode}:`" :alt="`:${shortcode}:`"
/> />
</template> </template>
</Popover> </EmojiEditingPopover>
</div> </div>
</ul> </ul>
</div> </div>

View File

@ -0,0 +1,207 @@
<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="uploadFile.length == 0"
@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', 'displayError'],
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.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.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.editedShortcode }
).then(resp => resp.json()).then(resp => {
if (resp.error !== undefined) {
this.displayError(resp.error)
return
}
this.$emit('updatePackFiles', resp)
})
}
},
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>