From f9c85c0c491fa800054250a814978e6f8fcc439a Mon Sep 17 00:00:00 2001
From: Ekaterina Vaartis <vaartis@kotobank.ch>
Date: Thu, 21 Dec 2023 00:16:16 +0300
Subject: Initial incomplete admin emoji settings implementation

---
 src/components/settings_modal/settings_modal_admin_content.vue | 8 ++++++++
 1 file changed, 8 insertions(+)

(limited to 'src/components/settings_modal/settings_modal_admin_content.vue')

diff --git a/src/components/settings_modal/settings_modal_admin_content.vue b/src/components/settings_modal/settings_modal_admin_content.vue
index a7a2ac9a..ba2d0a30 100644
--- a/src/components/settings_modal/settings_modal_admin_content.vue
+++ b/src/components/settings_modal/settings_modal_admin_content.vue
@@ -60,6 +60,14 @@
     >
       <FrontendsTab />
     </div>
+
+    <div
+      :label="$t('admin_dash.tabs.emoji')"
+      icon="laptop-code"
+      data-tab-name="emoji"
+    >
+      <EmojiTab />
+    </div>
   </tab-switcher>
 </template>
 
-- 
cgit v1.2.3-70-g09d2


From 091532d577ae9a23f7a43f681bfd344f040c7738 Mon Sep 17 00:00:00 2001
From: Ekaterina Vaartis <vaartis@kotobank.ch>
Date: Sun, 7 Jan 2024 02:45:49 +0300
Subject: Editing emojis in popover, pack creation/deletion

Also fixed some API calls since they weren't working apparently
---
 .../settings_modal/admin_tabs/emoji_tab.js         | 117 ++++++++++--
 .../settings_modal/admin_tabs/emoji_tab.scss       |  22 +++
 .../settings_modal/admin_tabs/emoji_tab.vue        | 206 ++++++++++++++-------
 .../settings_modal_admin_content.vue               |   2 +-
 src/services/api/api.service.js                    |  14 +-
 5 files changed, 272 insertions(+), 89 deletions(-)

(limited to 'src/components/settings_modal/settings_modal_admin_content.vue')

diff --git a/src/components/settings_modal/admin_tabs/emoji_tab.js b/src/components/settings_modal/admin_tabs/emoji_tab.js
index f9d3b24e..874d1c3b 100644
--- a/src/components/settings_modal/admin_tabs/emoji_tab.js
+++ b/src/components/settings_modal/admin_tabs/emoji_tab.js
@@ -1,20 +1,46 @@
+import { clone } 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'
 
 const EmojiTab = {
   components: {
     TabSwitcher,
     StringSetting,
     Checkbox,
-    StillImage
+    StillImage,
+    Select,
+    Popover,
+    ConfirmModal,
+    ModifiedIndicator
   },
 
   data () {
     return {
       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 () {
       this.$store.state.api.backendInteractor.importEmojiFromFS()
     },
-    emojiAddr (packName, name) {
-      return `${this.$store.state.instance.server}/emoji/${encodeURIComponent(packName)}/${name}`
+    emojiAddr (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] = {
-        shortcode, file: this.knownPacks[packName].files[shortcode]
+    createEmojiPack () {
+      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) {
-      const edited = this.editedParts[packName][shortcode]
+    saveEditedEmoji (shortcode) {
+      const edited = this.editedParts[this.packName][shortcode]
 
       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 =>
         resp.ok ? resp.json() : resp.text().then(respText => Promise.reject(respText))
       ).then(resp => {
-        this.knownPacks[packName].files = resp
-        delete this.editedParts[packName][shortcode]
+        this.knownPacks[this.packName].files = resp
+        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 () {
-    this.$store.state.api.backendInteractor.listEmojiPacks()
-      .then(data => data.json())
-      .then(packData => {
-        this.knownPacks = packData.packs
-        console.log(this.knownPacks)
-      })
+    this.refreshPackList()
   }
 }
 
diff --git a/src/components/settings_modal/admin_tabs/emoji_tab.scss b/src/components/settings_modal/admin_tabs/emoji_tab.scss
index 397580af..89bf2f7f 100644
--- a/src/components/settings_modal/admin_tabs/emoji_tab.scss
+++ b/src/components/settings_modal/admin_tabs/emoji_tab.scss
@@ -1,3 +1,5 @@
+@import "src/variables";
+
 .emoji-tab {
   .btn-group .btn {
     margin-left: 0.5em;
@@ -21,4 +23,24 @@
     width: 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;
 }
diff --git a/src/components/settings_modal/admin_tabs/emoji_tab.vue b/src/components/settings_modal/admin_tabs/emoji_tab.vue
index 699e4afe..534f881a 100644
--- a/src/components/settings_modal/admin_tabs/emoji_tab.vue
+++ b/src/components/settings_modal/admin_tabs/emoji_tab.vue
@@ -6,84 +6,166 @@
     <div class="setting-item">
       <h2>{{ $t('admin_dash.tabs.emoji') }}</h2>
 
-      <span class="btn-group">
-        <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>
-      </span>
+      <ul class="setting-list">
+        <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>
 
-      <tab-switcher :scrollable-tabs="true" v-if="Object.keys(knownPacks).length > 0">
-        <div v-for="(pack, packName) in knownPacks" :label="packName" :key="packName">
-          <div class="pack-info-wrapper">
-            <ul class="setting-list">
-              <li>
-                <div>Description</div>
-                <textarea
-                  v-model="pack.pack.description"
-                  class="bio resize-height" />
-              </li>
-              <li>
-                <div>Homepage</div>
-                <input class="emoji-info-input" v-model="pack.pack.homepage">
-              </li>
-              <li>
-                <div>Fallback source</div>
-                <input class="emoji-info-input" v-model="pack.pack['fallback-src']">
-              </li>
-              <li>
-                <Checkbox v-model="pack.pack['can-download']">Downloadable</Checkbox>
-              </li>
-            </ul>
-          </div>
+        <li class="setting-item btn-group">
+          <button
+            class="button button-default btn"
+            type="button"
+            @click="$refs.createPackPopover.showPopover">
+            Create pack
+          </button>
+          <Popover
+            ref="createPackPopover"
+            trigger="click"
+            placement="bottom"
+            bound-to-selector=".emoji-tab"
+            :bound-to="{ x: 'container' }"
+            :offset="{ y: 5 }"
+          >
+            <template #content>
+              <input v-model="newPackName" placeholder="New pack name">
+              <button
+                class="button button-default btn emoji-tab-popover-button"
+                type="button"
+                @click="createEmojiPack">
+                Create
+              </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">
-            <li v-for="(file, shortcode) in pack.files" :key="shortcode">
-              <StillImage
-                class="emoji img"
-                :src="emojiAddr(packName, file)"
-                :title="`:${shortcode}:`"
-                :alt="`:${shortcode}:`"
-              />
+            <li>
+              <div>
+                Description
+                <ModifiedIndicator :changed="metaEdited('description')" />
+              </div>
+              <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>
+
+              <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>
 
-              <template v-if="editedParts[packName] !== undefined && editedParts[packName][shortcode] !== undefined">
+        <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"
                   v-model="editedParts[packName][shortcode].shortcode">
                 <input class="emoji-data-input"
                   v-model="editedParts[packName][shortcode].file">
 
                 <button
-                  class="button button-default btn"
+                  class="button button-default btn emoji-tab-popover-button"
                   type="button"
-                  @click="saveEditedEmoji(packName, shortcode)">
+                  @click="saveEditedEmoji(shortcode)">
                   Save
                 </button>
-              </template>
-              <template v-else>
-                <input disabled class="emoji-data-input" :value="shortcode">
-                <input disabled class="emoji-data-input" :value="file">
 
-                <button
-                  class="button button-default btn"
-                  type="button"
-                  @click="editEmoji(packName, shortcode)">
-                  Edit
-                </button>
-              </template>
-            </li>
-          </ul>
+              </div>
+            </template>
+            <template #trigger>
+              <StillImage
+                class="emoji"
+                :src="emojiAddr(file)"
+                :title="`:${shortcode}:`"
+                :alt="`:${shortcode}:`"
+              />
+            </template>
+          </Popover>
         </div>
-      </tab-switcher>
+      </div>
     </div>
   </div>
 </template>
diff --git a/src/components/settings_modal/settings_modal_admin_content.vue b/src/components/settings_modal/settings_modal_admin_content.vue
index ba2d0a30..65e23b7e 100644
--- a/src/components/settings_modal/settings_modal_admin_content.vue
+++ b/src/components/settings_modal/settings_modal_admin_content.vue
@@ -63,7 +63,7 @@
 
     <div
       :label="$t('admin_dash.tabs.emoji')"
-      icon="laptop-code"
+      icon="face-smile-beam"
       data-tab-name="emoji"
     >
       <EmojiTab />
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index 4d8fdae2..c8e5aa16 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -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_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_PACK_URL = (name) => `/api/pleroma/emoji/pack?name=${name}`
-const PLEROMA_EMOJI_PACKS_DL_REMOTE_URL = '/api/pleroma/emoji/packs/download'
+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/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}`
+  (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
 
@@ -1809,7 +1809,7 @@ const importEmojiFromFS = () => {
 }
 
 const createEmojiPack = ({ name }) => {
-  return fetch(PLEROMA_EMOJI_PACK_URL(name), { method: 'PUT' })
+  return fetch(PLEROMA_EMOJI_PACK_URL(name), { method: 'POST' })
 }
 
 const listEmojiPacks = () => {
@@ -1850,7 +1850,7 @@ const saveEmojiPackMetadata = ({ name, newData }) => {
     {
       method: 'PATCH',
       headers: { 'Content-Type': 'application/json' },
-      body: JSON.stringify({ name, new_data: newData })
+      body: JSON.stringify({ metadata: newData })
     }
   )
 }
-- 
cgit v1.2.3-70-g09d2

