From 9e45228823cd0fa7eb9388b0eb7780b9609edf66 Mon Sep 17 00:00:00 2001
From: marcin mikołajczak <git@mkljczk.pl>
Date: Mon, 23 Sep 2024 23:10:32 +0200
Subject: Bookmark folders
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
---
 src/components/navigation/navigation_entry.vue | 33 +++++++++++++++++++++++---
 1 file changed, 30 insertions(+), 3 deletions(-)

(limited to 'src/components/navigation/navigation_entry.vue')

diff --git a/src/components/navigation/navigation_entry.vue b/src/components/navigation/navigation_entry.vue
index 4ea54ee3..728de1be 100644
--- a/src/components/navigation/navigation_entry.vue
+++ b/src/components/navigation/navigation_entry.vue
@@ -22,11 +22,25 @@
             :icon="item.icon"
           />
         </span>
+        <img
+          v-if="item.iconEmojiUrl"
+          class="menu-icon iconEmoji iconEmoji-image"
+          :src="item.iconEmojiUrl"
+          :alt="item.iconEmoji"
+          :title="item.iconEmoji"
+        >
         <span
-          v-if="item.iconLetter"
-          class="icon iconLetter fa-scale-110 menu-icon"
-        >{{ item.iconLetter }}
+          v-else-if="item.iconEmoji"
+          class="menu-icon iconEmoji"
+        >
+          <span>
+            {{ item.iconEmoji }}
+          </span>
         </span>
+        <span
+          v-else-if="item.iconLetter"
+          class="icon iconLetter fa-scale-110 menu-icon"
+        >{{ item.iconLetter }}</span>
         <span class="label">
           {{ item.labelRaw || $t(item.label) }}
         </span>
@@ -110,5 +124,18 @@
   .badge {
     margin: 0 var(--__horizontal-gap);
   }
+
+  .iconEmoji {
+    display: inline-block;
+    text-align: center;
+    object-fit: contain;
+    vertical-align: middle;
+    height: var(--__line-height);
+    width: var(--__line-height);
+
+    > span {
+      font-size: 1.5rem;
+    }
+  }
 }
 </style>
-- 
cgit v1.2.3-70-g09d2


From 2b6de423d5a02413924f18910b1ce3538a5a1625 Mon Sep 17 00:00:00 2001
From: marcin mikołajczak <git@mkljczk.pl>
Date: Tue, 24 Sep 2024 18:15:49 +0200
Subject: Bookmark folder editing
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
---
 src/boot/routes.js                                 |   5 +-
 .../bookmark_folder_card/bookmark_folder_card.js   |   7 +-
 .../bookmark_folder_card/bookmark_folder_card.vue  |  51 ++++++++-
 .../bookmark_folder_edit/bookmark_folder_edit.js   |  65 +++++++++++
 .../bookmark_folder_edit/bookmark_folder_edit.vue  | 125 +++++++++++++++++++++
 .../bookmark_folders/bookmark_folders.vue          |   2 +-
 src/components/navigation/navigation_entry.vue     |   5 +
 src/i18n/en.json                                   |  10 +-
 src/modules/bookmark_folders.js                    |   6 +-
 src/services/api/api.service.js                    |   4 +-
 10 files changed, 269 insertions(+), 11 deletions(-)
 create mode 100644 src/components/bookmark_folder_edit/bookmark_folder_edit.js
 create mode 100644 src/components/bookmark_folder_edit/bookmark_folder_edit.vue

(limited to 'src/components/navigation/navigation_entry.vue')

diff --git a/src/boot/routes.js b/src/boot/routes.js
index ff93bb51..f87b2ec8 100644
--- a/src/boot/routes.js
+++ b/src/boot/routes.js
@@ -27,6 +27,7 @@ import NavPanel from 'src/components/nav_panel/nav_panel.vue'
 import AnnouncementsPage from 'components/announcements_page/announcements_page.vue'
 import QuotesTimeline from '../components/quotes_timeline/quotes_timeline.vue'
 import BookmarkFolders from '../components/bookmark_folders/bookmark_folders.vue'
+import BookmarkFolderEdit from '../components/bookmark_folder_edit/bookmark_folder_edit.vue'
 
 export default (store) => {
   const validateAuthenticatedRoute = (to, from, next) => {
@@ -89,7 +90,9 @@ export default (store) => {
     { name: 'lists-new', path: '/lists/new', component: ListsEdit },
     { name: 'edit-navigation', path: '/nav-edit', component: NavPanel, props: () => ({ forceExpand: true, forceEditMode: true }), beforeEnter: validateAuthenticatedRoute },
     { name: 'bookmark-folders', path: '/bookmark_folders', component: BookmarkFolders },
-    { name: 'bookmark-folder', path: '/bookmarks/:id', component: BookmarkTimeline }
+    { name: 'bookmark-folder-new', path: '/bookmarks/new-folder', component: BookmarkFolderEdit },
+    { name: 'bookmark-folder', path: '/bookmarks/:id', component: BookmarkTimeline },
+    { name: 'bookmark-folder-edit', path: '/bookmarks/:id/edit', component: BookmarkFolderEdit }
   ]
 
   if (store.state.instance.pleromaChatMessagesAvailable) {
diff --git a/src/components/bookmark_folder_card/bookmark_folder_card.js b/src/components/bookmark_folder_card/bookmark_folder_card.js
index 9df28e19..7a7206aa 100644
--- a/src/components/bookmark_folder_card/bookmark_folder_card.js
+++ b/src/components/bookmark_folder_card/bookmark_folder_card.js
@@ -10,7 +10,12 @@ library.add(
 const BookmarkFolderCard = {
   props: [
     'folder'
-  ]
+  ],
+  computed: {
+    firstLetter () {
+      return this.folder.name[0]
+    }
+  }
 }
 
 export default BookmarkFolderCard
diff --git a/src/components/bookmark_folder_card/bookmark_folder_card.vue b/src/components/bookmark_folder_card/bookmark_folder_card.vue
index 10875e78..b773c9fb 100644
--- a/src/components/bookmark_folder_card/bookmark_folder_card.vue
+++ b/src/components/bookmark_folder_card/bookmark_folder_card.vue
@@ -4,10 +4,28 @@
       :to="{ name: 'bookmark-folder', params: { id: folder.id } }"
       class="bookmark-folder-name"
     >
-      {{ folder.name }}
+      <img
+        v-if="folder.emoji_url"
+        class="iconEmoji iconEmoji-image"
+        :src="folder.emoji_url"
+        :alt="folder.emoji"
+        :title="folder.emoji"
+      >
+      <span
+        v-else-if="folder.emoji"
+        class="iconEmoji"
+      >
+        <span>
+          {{ folder.emoji }}
+        </span>
+      </span>
+      <span
+        v-else-if="firstLetter"
+        class="icon iconLetter fa-scale-110"
+      >{{ firstLetter }}</span>{{ folder.name }}
     </router-link>
     <router-link
-      :to="{ name: 'bookmark-folder' /* -edit' */, params: { id: folder.id } }"
+      :to="{ name: 'bookmark-folder-edit', params: { id: folder.id } }"
       class="button-folder-edit"
     >
       <FAIcon
@@ -27,6 +45,35 @@
 
 .bookmark-folder-name {
   flex-grow: 1;
+
+  .iconLetter, .iconEmoji {
+    margin-right: 0.5em;
+  }
+
+  .iconLetter {
+    height: 2.5rem;
+    width: 2.5rem;
+    font-size: 1.5rem;
+  }
+
+  .iconEmoji {
+    display: inline-block;
+    text-align: center;
+    object-fit: contain;
+    vertical-align: middle;
+    height: 2.5em;
+    width: 2.5em;
+
+    > span {
+      font-size: 1.5rem;
+      line-height: 2.5rem;
+    }
+  }
+
+  img.iconEmoji {
+    padding: 0.25em;
+    box-sizing: border-box;
+  }
 }
 
 .bookmark-folder-name,
diff --git a/src/components/bookmark_folder_edit/bookmark_folder_edit.js b/src/components/bookmark_folder_edit/bookmark_folder_edit.js
new file mode 100644
index 00000000..8c5d3cfd
--- /dev/null
+++ b/src/components/bookmark_folder_edit/bookmark_folder_edit.js
@@ -0,0 +1,65 @@
+import apiService from '../../services/api/api.service'
+
+const BookmarkFolderEdit = {
+  data () {
+    return {
+      name: '',
+      nameDraft: '',
+      emoji: '',
+      emojiUrl: null,
+      emojiDraft: '',
+      emojiUrlDraft: null,
+      reallyDelete: false
+    }
+  },
+  created () {
+    if (!this.id) return
+    const credentials = this.$store.state.users.currentUser.credentials
+    apiService.fetchBookmarkFolders({ credentials })
+      .then((folders) => {
+        const folder = folders.find(folder => folder.id === this.id)
+        if (!folder) return
+
+        this.nameDraft = this.name = folder.name
+        this.emojiDraft = this.emoji = folder.emoji
+        this.emojiUrlDraft = this.emojiUrl = folder.emoji_url
+      })
+  },
+  computed: {
+    id () {
+      return this.$route.params.id
+    }
+  },
+  methods: {
+    updateFolder () {
+      this.$store.dispatch('setBookmarkFolder', { folderId: this.id, name: this.nameDraft, emoji: this.emojiDraft })
+        .then((folder) => {
+          this.nameDraft = this.name = folder.name
+          this.emojiDraft = this.emoji = folder.emoji
+          this.emojiUrlDraft = this.emojiUrl = folder.emoji_url
+        })
+    },
+    createFolder () {
+      this.$store.dispatch('createBookmarkFolder', { name: this.nameDraft, emoji: this.emojiDraft })
+        .then((folder) => {
+          return folder.id
+        })
+        .then((folderId) => {
+          this.$router.push({ name: 'bookmark-folders' })
+        })
+        .catch((e) => {
+          this.$store.dispatch('pushGlobalNotice', {
+            messageKey: 'bookmark_folders.error',
+            messageArgs: [e.message],
+            level: 'error'
+          })
+        })
+    },
+    deleteFolder () {
+      this.$store.dispatch('deleteBookmarkFolder', { folderId: this.id })
+      this.$router.push({ name: 'bookmark-folders' })
+    }
+  }
+}
+
+export default BookmarkFolderEdit
diff --git a/src/components/bookmark_folder_edit/bookmark_folder_edit.vue b/src/components/bookmark_folder_edit/bookmark_folder_edit.vue
new file mode 100644
index 00000000..30bbde09
--- /dev/null
+++ b/src/components/bookmark_folder_edit/bookmark_folder_edit.vue
@@ -0,0 +1,125 @@
+<template>
+  <div class="panel-default panel BookmarkFolderEdit">
+    <div
+      ref="header"
+      class="panel-heading folder-edit-heading"
+    >
+      <button
+        class="button-unstyled go-back-button"
+        @click="$router.back"
+      >
+        <FAIcon
+          size="lg"
+          icon="chevron-left"
+        />
+      </button>
+      <div class="title">
+        <i18n-t
+          v-if="id"
+          keypath="bookmark_folders.editing_folder"
+        >
+          <template #folderName>
+            {{ name }}
+          </template>
+        </i18n-t>
+        <i18n-t
+          v-else
+          keypath="bookmark_folders.creating_folder"
+        />
+      </div>
+    </div>
+    <div class="panel-body">
+      <div class="input-wrap">
+        <label for="folder-edit-title">{{ $t('bookmark_folders.name') }}</label>
+        {{ ' ' }}
+        <input
+          id="folder-edit-title"
+          ref="name"
+          v-model="nameDraft"
+          class="input"
+        >
+        <button
+          v-if="id"
+          class="btn button-default follow-button"
+          @click="updateFolder"
+        >
+          {{ $t('bookmark_folders.update_folder') }}
+        </button>
+      </div>
+    </div>
+    <div class="panel-footer">
+      <span class="spacer" />
+      <button
+        v-if="!id"
+        class="btn button-default footer-button"
+        @click="createFolder"
+      >
+        {{ $t('bookmark_folders.create') }}
+      </button>
+      <button
+        v-else-if="!reallyDelete"
+        class="btn button-default footer-button"
+        @click="reallyDelete = true"
+      >
+        {{ $t('bookmark_folders.delete') }}
+      </button>
+      <template v-else>
+        {{ $t('bookmark_folders.really_delete') }}
+        <button
+          class="btn button-default footer-button"
+          @click="deleteFolder"
+        >
+          {{ $t('general.yes') }}
+        </button>
+        <button
+          class="btn button-default footer-button"
+          @click="reallyDelete = false"
+        >
+          {{ $t('general.no') }}
+        </button>
+      </template>
+    </div>
+  </div>
+</template>
+
+<script src="./bookmark_folder_edit.js"></script>
+
+<style lang="scss">
+.BookmarkFolderEdit {
+  --panel-body-padding: 0.5em;
+
+  overflow: hidden;
+  display: flex;
+  flex-direction: column;
+
+  .folder-edit-heading {
+    grid-template-columns: auto minmax(50%, 1fr);
+  }
+
+  .panel-body {
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+  }
+
+  .go-back-button {
+    text-align: center;
+    line-height: 1;
+    height: 100%;
+    align-self: start;
+    width: var(--__panel-heading-height-inner);
+  }
+
+  .btn {
+    margin: 0 0.5em;
+  }
+
+  .panel-footer {
+    grid-template-columns: minmax(10%, 1fr);
+
+    .footer-button {
+      min-width: 9em;
+    }
+  }
+}
+</style>
diff --git a/src/components/bookmark_folders/bookmark_folders.vue b/src/components/bookmark_folders/bookmark_folders.vue
index 733e4cab..65b1a11b 100644
--- a/src/components/bookmark_folders/bookmark_folders.vue
+++ b/src/components/bookmark_folders/bookmark_folders.vue
@@ -5,7 +5,7 @@
         {{ $t('nav.bookmark_folders') }}
       </div>
       <router-link
-        :to="{ name: 'bookmark-folders' /* 'new' */ }"
+        :to="{ name: 'bookmark-folder-new' }"
         class="button-default btn new-folder-button"
       >
         {{ $t("bookmark_folders.new") }}
diff --git a/src/components/navigation/navigation_entry.vue b/src/components/navigation/navigation_entry.vue
index 728de1be..024ee314 100644
--- a/src/components/navigation/navigation_entry.vue
+++ b/src/components/navigation/navigation_entry.vue
@@ -137,5 +137,10 @@
       font-size: 1.5rem;
     }
   }
+
+  img.iconEmoji {
+    padding: 0.25rem;
+    box-sizing: border-box;
+  }
 }
 </style>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 9d10b560..5a9bee32 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -1409,7 +1409,15 @@
     "tooltip": "This domain contains non-ascii characters."
   },
   "bookmark_folders": {
+    "create": "Create folder",
+    "creating_folder": "Creating bookmark folder",
+    "delete": "Delete folder",
+    "editing_folder": "Editing folder {folderName}",
+    "error": "Error manipulating bookmark folders: {0}",
+    "name": "Folder name",
     "new": "New Folder",
-    "select_folder": "Select bookmark folder"
+    "really_delete": "Do you really want to delete the folder?",
+    "select_folder": "Select bookmark folder",
+    "update_folder": "Save changes"
   }
 }
diff --git a/src/modules/bookmark_folders.js b/src/modules/bookmark_folders.js
index e5f25791..5dde58a7 100644
--- a/src/modules/bookmark_folders.js
+++ b/src/modules/bookmark_folders.js
@@ -8,10 +8,10 @@ export const mutations = {
   setBookmarkFolders (state, value) {
     state.allFolders = value
   },
-  setBookmarkFolder (state, { folderId, name, emoji, emojiUrl }) {
-    const entry = find(state.allFolders, { id: name })
+  setBookmarkFolder (state, { id, name, emoji, emojiUrl }) {
+    const entry = find(state.allFolders, { id })
     if (!entry) {
-      state.allFolders.push({ id: folderId, name, emoji, emojiUrl })
+      state.allFolders.push({ id, name, emoji, emojiUrl })
     } else {
       entry.name = name
       entry.emoji = emoji
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index 43896ae7..94c12360 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -1927,9 +1927,9 @@ const updateBookmarkFolder = ({ folderId, name, emoji, credentials }) => {
 
   return fetch(url, {
     headers,
-    method: 'PUT',
+    method: 'PATCH',
     body: JSON.stringify({ name, emoji })
-  })
+  }).then((data) => data.json())
 }
 
 const deleteBookmarkFolder = ({ folderId, credentials }) => {
-- 
cgit v1.2.3-70-g09d2

