commit: 8a030d935bda6fdf91015d6001d985919b022394
parent aa4983835587e20a7ad0ce45e7c706a35d045774
Author: Ekaterina Vaartis <vaartis@kotobank.ch>
Date: Fri, 19 Jan 2024 23:16:21 +0300
Separate emoji editing and upload into a separate component
Handle all state in that component
Diffstat:
4 files changed, 253 insertions(+), 233 deletions(-)
diff --git a/src/components/settings_modal/admin_tabs/emoji_tab.js b/src/components/settings_modal/admin_tabs/emoji_tab.js
@@ -7,8 +7,7 @@ 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 newEmojiUploadBase = { shortcode: '', file: '', upload: [] }
+import EmojiEditingPopover from '../helpers/emoji_editing_popover.vue'
const EmojiTab = {
components: {
@@ -19,24 +18,31 @@ const EmojiTab = {
Select,
Popover,
ConfirmModal,
- ModifiedIndicator
+ ModifiedIndicator,
+ EmojiEditingPopover
},
data () {
return {
knownLocalPacks: { },
knownRemotePacks: { },
- editedParts: { },
editedMetadata: { },
packName: '',
newPackName: '',
deleteModalVisible: false,
- newEmojiUpload: clone(newEmojiUploadBase),
remotePackInstance: '',
remotePackDownloadAs: ''
}
},
+ provide () {
+ return {
+ // Functions
+ emojiAddr: this.emojiAddr,
+ displayError: this.displayError
+ }
+ },
+
computed: {
pack () {
return this.packName !== '' ? this.knownPacks[this.packName] : undefined
@@ -58,11 +64,6 @@ const EmojiTab = {
}
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 () {
this.$store.state.api.backendInteractor.createEmojiPack(
{ name: this.newPackName }
@@ -132,7 +112,6 @@ const EmojiTab = {
}
}).then(done => {
delete this.editedMetadata[this.packName]
- delete this.editedParts[this.packName]
this.deleteModalVisible = false
this.packName = ''
@@ -162,68 +141,9 @@ const EmojiTab = {
})
},
- revertEmoji (shortcode) {
- // Delete current changes and overwrite them with defaults. If the window is closed, they'll get cleared anyway
- 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)
- })
- },
- 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)
- })
+ updatePackFiles (newFiles) {
+ this.pack.files = newFiles
+ this.sortPackFiles(this.packName)
},
loadPacksPaginated (listFunction) {
diff --git a/src/components/settings_modal/admin_tabs/emoji_tab.scss b/src/components/settings_modal/admin_tabs/emoji_tab.scss
@@ -39,17 +39,6 @@
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 {
margin-bottom: 0.5em;
diff --git a/src/components/settings_modal/admin_tabs/emoji_tab.vue b/src/components/settings_modal/admin_tabs/emoji_tab.vue
@@ -97,38 +97,44 @@
</li>
</ul>
- <div v-if="packName !== ''">
+ <div v-if="pack">
<div class="pack-info-wrapper">
<ul class="setting-list">
<li>
- <div>
+ <label>
{{ $t('admin_dash.emoji.description') }}
<ModifiedIndicator :changed="metaEdited('description')" message-key="admin_dash.emoji.metadata_changed" />
- </div>
- <textarea
- v-model="packMeta.description"
- :disabled="pack.remote !== undefined"
- class="bio resize-height" />
+
+ <textarea
+ v-model="packMeta.description"
+ :disabled="pack.remote !== undefined"
+ class="bio resize-height" />
+ </label>
</li>
<li>
- <div>
+ <label>
{{ $t('admin_dash.emoji.homepage') }}
<ModifiedIndicator :changed="metaEdited('homepage')" message-key="admin_dash.emoji.metadata_changed" />
- </div>
+
<input
class="emoji-info-input" v-model="packMeta.homepage"
:disabled="pack.remote !== undefined">
+ </label>
</li>
<li>
- <div>
+ <label>
{{ $t('admin_dash.emoji.fallback_src') }}
<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>
- <div>{{ $t('admin_dash.emoji.fallback_sha256') }}</div>
- <input :disabled="true" class="emoji-info-input" v-model="packMeta['fallback-src-sha256']">
+ <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']">
@@ -218,132 +224,30 @@
{{ $t('admin_dash.emoji.files') }}
<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"/>
</h4>
<div class="emoji-list" v-if="pack">
- <Popover
+ <EmojiEditingPopover
v-if="pack.remote === undefined"
- ref="addEmojiPopover"
- trigger="click"
- placement="bottom"
- bound-to-selector=".emoji-tab"
- popover-class="emoji-tab-edit-popover popover-default"
- :bound-to="{ x: 'container' }"
- :offset="{ y: 5 }"
+ placement="bottom" new-upload
+ :title="$t('admin_dash.emoji.adding_new')"
+ :packName="packName" @updatePackFiles="updatePackFiles"
>
<template #trigger>
<FAIcon icon="plus" size="2x" :title="$t('admin_dash.emoji.add_file')" />
</template>
- <template #content>
- <h3>
- {{ $t('admin_dash.emoji.adding_new') }}
- </h3>
-
- <StillImage
- class="emoji" v-if="newEmojiUploadPreview"
- :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>
+ </EmojiEditingPopover>
- <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
+ <EmojiEditingPopover
+ placement="top" ref="emojiPopovers"
v-for="(file, shortcode) in pack.files" :key="shortcode"
- trigger="click"
+ :title="$t('admin_dash.emoji.editing', [shortcode])"
:disabled="pack.remote !== undefined"
- 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)"
- @close="closedEditedEmoji(shortcode)"
+ :shortcode="shortcode" :file="file" :packName="packName"
+ @updatePackFiles="updatePackFiles"
>
- <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>
<StillImage
class="emoji"
@@ -352,7 +256,7 @@
:alt="`:${shortcode}:`"
/>
</template>
- </Popover>
+ </EmojiEditingPopover>
</div>
</ul>
</div>
diff --git a/src/components/settings_modal/helpers/emoji_editing_popover.vue b/src/components/settings_modal/helpers/emoji_editing_popover.vue
@@ -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>