aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authormarcin mikołajczak <git@mkljczk.pl>2024-09-23 23:10:32 +0200
committermarcin mikołajczak <git@mkljczk.pl>2024-09-23 23:13:40 +0200
commit9e45228823cd0fa7eb9388b0eb7780b9609edf66 (patch)
tree1d4680c875616673c0aa8f81ff6d1bfd41314598 /src/components
parenta8092de63808ff1445636f07e11f3602774f1438 (diff)
Bookmark folders
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
Diffstat (limited to 'src/components')
-rw-r--r--src/components/bookmark_folder_card/bookmark_folder_card.js16
-rw-r--r--src/components/bookmark_folder_card/bookmark_folder_card.vue38
-rw-r--r--src/components/bookmark_folders/bookmark_folders.js27
-rw-r--r--src/components/bookmark_folders/bookmark_folders.vue33
-rw-r--r--src/components/bookmark_folders_menu/bookmark_folders_menu_content.js16
-rw-r--r--src/components/bookmark_folders_menu/bookmark_folders_menu_content.vue19
-rw-r--r--src/components/bookmark_timeline/bookmark_timeline.js19
-rw-r--r--src/components/bookmark_timeline/bookmark_timeline.vue1
-rw-r--r--src/components/nav_panel/nav_panel.js15
-rw-r--r--src/components/nav_panel/nav_panel.vue33
-rw-r--r--src/components/navigation/filter.js12
-rw-r--r--src/components/navigation/navigation.js3
-rw-r--r--src/components/navigation/navigation_entry.vue33
-rw-r--r--src/components/timeline/timeline.js3
-rw-r--r--src/components/timeline_menu/timeline_menu.js25
-rw-r--r--src/components/timeline_menu/timeline_menu.vue4
16 files changed, 280 insertions, 17 deletions
diff --git a/src/components/bookmark_folder_card/bookmark_folder_card.js b/src/components/bookmark_folder_card/bookmark_folder_card.js
new file mode 100644
index 00000000..9df28e19
--- /dev/null
+++ b/src/components/bookmark_folder_card/bookmark_folder_card.js
@@ -0,0 +1,16 @@
+import { library } from '@fortawesome/fontawesome-svg-core'
+import {
+ faEllipsisH
+} from '@fortawesome/free-solid-svg-icons'
+
+library.add(
+ faEllipsisH
+)
+
+const BookmarkFolderCard = {
+ props: [
+ 'folder'
+ ]
+}
+
+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
new file mode 100644
index 00000000..10875e78
--- /dev/null
+++ b/src/components/bookmark_folder_card/bookmark_folder_card.vue
@@ -0,0 +1,38 @@
+<template>
+ <div class="bookmark-folder-card">
+ <router-link
+ :to="{ name: 'bookmark-folder', params: { id: folder.id } }"
+ class="bookmark-folder-name"
+ >
+ {{ folder.name }}
+ </router-link>
+ <router-link
+ :to="{ name: 'bookmark-folder' /* -edit' */, params: { id: folder.id } }"
+ class="button-folder-edit"
+ >
+ <FAIcon
+ class="fa-scale-110 fa-old-padding"
+ icon="ellipsis-h"
+ />
+ </router-link>
+ </div>
+</template>
+
+<script src="./bookmark_folder_card.js"></script>
+
+<style lang="scss">
+.bookmark-folder-card {
+ display: flex;
+}
+
+.bookmark-folder-name {
+ flex-grow: 1;
+}
+
+.bookmark-folder-name,
+.button-folder-edit {
+ margin: 0;
+ padding: 1em;
+ color: var(--link);
+}
+</style>
diff --git a/src/components/bookmark_folders/bookmark_folders.js b/src/components/bookmark_folders/bookmark_folders.js
new file mode 100644
index 00000000..9f1f1fed
--- /dev/null
+++ b/src/components/bookmark_folders/bookmark_folders.js
@@ -0,0 +1,27 @@
+import BookmarkFolderCard from '../bookmark_folder_card/bookmark_folder_card.vue'
+
+const BookmarkFolders = {
+ data () {
+ return {
+ isNew: false
+ }
+ },
+ components: {
+ BookmarkFolderCard
+ },
+ computed: {
+ bookmarkFolders () {
+ return this.$store.state.bookmarkFolders.allFolders
+ }
+ },
+ methods: {
+ cancelNewFolder () {
+ this.isNew = false
+ },
+ newFolder () {
+ this.isNew = true
+ }
+ }
+}
+
+export default BookmarkFolders
diff --git a/src/components/bookmark_folders/bookmark_folders.vue b/src/components/bookmark_folders/bookmark_folders.vue
new file mode 100644
index 00000000..733e4cab
--- /dev/null
+++ b/src/components/bookmark_folders/bookmark_folders.vue
@@ -0,0 +1,33 @@
+<template>
+ <div class="Bookmark-folders panel panel-default">
+ <div class="panel-heading">
+ <div class="title">
+ {{ $t('nav.bookmark_folders') }}
+ </div>
+ <router-link
+ :to="{ name: 'bookmark-folders' /* 'new' */ }"
+ class="button-default btn new-folder-button"
+ >
+ {{ $t("bookmark_folders.new") }}
+ </router-link>
+ </div>
+ <div class="panel-body">
+ <BookmarkFolderCard
+ v-for="folder in bookmarkFolders.slice().reverse()"
+ :key="folder"
+ :folder="folder"
+ class="bookmark-folder-item"
+ />
+ </div>
+ </div>
+</template>
+
+<script src="./bookmark_folders.js"></script>
+
+<style lang="scss">
+.Bookmark-folders {
+ .new-folder-button {
+ padding: 0 0.5em;
+ }
+}
+</style>
diff --git a/src/components/bookmark_folders_menu/bookmark_folders_menu_content.js b/src/components/bookmark_folders_menu/bookmark_folders_menu_content.js
new file mode 100644
index 00000000..d5f82f46
--- /dev/null
+++ b/src/components/bookmark_folders_menu/bookmark_folders_menu_content.js
@@ -0,0 +1,16 @@
+import { mapState } from 'vuex'
+import NavigationEntry from 'src/components/navigation/navigation_entry.vue'
+import { getBookmarkFolderEntries } from 'src/components/navigation/filter.js'
+
+export const BookmarkFoldersMenuContent = {
+ components: {
+ NavigationEntry
+ },
+ computed: {
+ ...mapState({
+ folders: getBookmarkFolderEntries
+ })
+ }
+}
+
+export default BookmarkFoldersMenuContent
diff --git a/src/components/bookmark_folders_menu/bookmark_folders_menu_content.vue b/src/components/bookmark_folders_menu/bookmark_folders_menu_content.vue
new file mode 100644
index 00000000..d603cd01
--- /dev/null
+++ b/src/components/bookmark_folders_menu/bookmark_folders_menu_content.vue
@@ -0,0 +1,19 @@
+<template>
+ <ul>
+ <NavigationEntry
+ :item="{
+ name: 'bookmarks',
+ routeObject: { name: 'bookmarks' },
+ label: 'nav.all_bookmarks',
+ icon: 'bookmark'
+ }"
+ />
+ <NavigationEntry
+ v-for="item in folders"
+ :key="item.id"
+ :item="item"
+ />
+ </ul>
+</template>
+
+<script src="./bookmark_folders_menu_content.js"></script>
diff --git a/src/components/bookmark_timeline/bookmark_timeline.js b/src/components/bookmark_timeline/bookmark_timeline.js
index 5ac43d90..9571d630 100644
--- a/src/components/bookmark_timeline/bookmark_timeline.js
+++ b/src/components/bookmark_timeline/bookmark_timeline.js
@@ -1,16 +1,31 @@
import Timeline from '../timeline/timeline.vue'
const Bookmarks = {
+ created () {
+ this.$store.commit('clearTimeline', { timeline: 'bookmarks' })
+ this.$store.dispatch('startFetchingTimeline', { timeline: 'bookmarks', bookmarkFolderId: this.folderId || null })
+ },
+ components: {
+ Timeline
+ },
computed: {
+ folderId () {
+ return this.$route.params.id
+ },
timeline () {
return this.$store.state.statuses.timelines.bookmarks
}
},
- components: {
- Timeline
+ watch: {
+ folderId () {
+ this.$store.commit('clearTimeline', { timeline: 'bookmarks' })
+ this.$store.dispatch('stopFetchingTimeline', 'bookmarks')
+ this.$store.dispatch('startFetchingTimeline', { timeline: 'bookmarks', bookmarkFolderId: this.folderId || null })
+ }
},
unmounted () {
this.$store.commit('clearTimeline', { timeline: 'bookmarks' })
+ this.$store.dispatch('stopFetchingTimeline', 'bookmarks')
}
}
diff --git a/src/components/bookmark_timeline/bookmark_timeline.vue b/src/components/bookmark_timeline/bookmark_timeline.vue
index 8da6884b..7a096004 100644
--- a/src/components/bookmark_timeline/bookmark_timeline.vue
+++ b/src/components/bookmark_timeline/bookmark_timeline.vue
@@ -3,6 +3,7 @@
:title="$t('nav.bookmarks')"
:timeline="timeline"
:timeline-name="'bookmarks'"
+ :bookmark-folder-id="bookmarkFolderId"
/>
</template>
diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js
index 8c9c3b11..11863e97 100644
--- a/src/components/nav_panel/nav_panel.js
+++ b/src/components/nav_panel/nav_panel.js
@@ -1,3 +1,4 @@
+import BookmarkFoldersMenuContent from 'src/components/bookmark_folders_menu/bookmark_folders_menu_content.vue'
import ListsMenuContent from 'src/components/lists_menu/lists_menu_content.vue'
import { mapState, mapGetters } from 'vuex'
import { TIMELINES, ROOT_ITEMS } from 'src/components/navigation/navigation.js'
@@ -41,6 +42,7 @@ const NavPanel = {
created () {
},
components: {
+ BookmarkFoldersMenuContent,
ListsMenuContent,
NavigationEntry,
NavigationPins,
@@ -51,6 +53,7 @@ const NavPanel = {
editMode: false,
showTimelines: false,
showLists: false,
+ showBookmarkFolders: false,
timelinesList: Object.entries(TIMELINES).map(([k, v]) => ({ ...v, name: k })),
rootList: Object.entries(ROOT_ITEMS).map(([k, v]) => ({ ...v, name: k }))
}
@@ -62,6 +65,9 @@ const NavPanel = {
toggleLists () {
this.showLists = !this.showLists
},
+ toggleBookmarkFolders () {
+ this.showBookmarkFolders = !this.showBookmarkFolders
+ },
toggleEditMode () {
this.editMode = !this.editMode
},
@@ -90,7 +96,8 @@ const NavPanel = {
pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable,
supportsAnnouncements: state => state.announcements.supportsAnnouncements,
pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems),
- collapsed: state => state.serverSideStorage.prefsStorage.simple.collapseNav
+ collapsed: state => state.serverSideStorage.prefsStorage.simple.collapseNav,
+ bookmarkFolders: state => state.instance.pleromaBookmarkFoldersAvailable
}),
timelinesItems () {
return filterNavigation(
@@ -102,7 +109,8 @@ const NavPanel = {
hasAnnouncements: this.supportsAnnouncements,
isFederating: this.federating,
isPrivate: this.privateMode,
- currentUser: this.currentUser
+ currentUser: this.currentUser,
+ supportsBookmarkFolders: this.bookmarkFolders
}
)
},
@@ -116,7 +124,8 @@ const NavPanel = {
hasAnnouncements: this.supportsAnnouncements,
isFederating: this.federating,
isPrivate: this.privateMode,
- currentUser: this.currentUser
+ currentUser: this.currentUser,
+ supportsBookmarkFolders: this.bookmarkFolders
}
)
},
diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index bf608936..a19e9ba5 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -84,6 +84,39 @@
/>
</div>
<NavigationEntry
+ v-if="currentUser && bookmarkFolders"
+ :show-pin="false"
+ :item="{ icon: 'bookmark', label: 'nav.bookmarks' }"
+ :aria-expanded="showBookmarkFolders ? 'true' : 'false'"
+ @click="toggleBookmarkFolders"
+ >
+ <router-link
+ :title="$t('bookmarks.manage_bookmark_folders')"
+ class="button-unstyled extra-button"
+ :to="{ name: 'bookmark-folders' }"
+ @click.stop
+ >
+ <FAIcon
+ fixed-width
+ icon="wrench"
+ />
+ </router-link>
+ <FAIcon
+ class="timelines-chevron"
+ fixed-width
+ :icon="showBookmarkFolders ? 'chevron-up' : 'chevron-down'"
+ />
+ </NavigationEntry>
+ <div
+ v-show="showBookmarkFolders"
+ class="timelines-background menu-item-collapsible"
+ :class="{ '-expanded': showBookmarkFolders }"
+ >
+ <BookmarkFoldersMenuContent
+ class="timelines"
+ />
+ </div>
+ <NavigationEntry
v-for="item in rootItems"
:key="item.name"
:show-pin="editMode || forceEditMode"
diff --git a/src/components/navigation/filter.js b/src/components/navigation/filter.js
index e8e77f8f..9b8f43cb 100644
--- a/src/components/navigation/filter.js
+++ b/src/components/navigation/filter.js
@@ -1,4 +1,4 @@
-export const filterNavigation = (list = [], { hasChats, hasAnnouncements, isFederating, isPrivate, currentUser }) => {
+export const filterNavigation = (list = [], { hasChats, hasAnnouncements, isFederating, isPrivate, currentUser, supportsBookmarkFolders }) => {
return list.filter(({ criteria, anon, anonRoute }) => {
const set = new Set(criteria || [])
if (!isFederating && set.has('federating')) return false
@@ -7,6 +7,7 @@ export const filterNavigation = (list = [], { hasChats, hasAnnouncements, isFede
if ((!currentUser || !currentUser.locked) && set.has('lockedUser')) return false
if (!hasChats && set.has('chats')) return false
if (!hasAnnouncements && set.has('announcements')) return false
+ if (supportsBookmarkFolders && set.has('!supportsBookmarkFolders')) return false
return true
})
}
@@ -17,3 +18,12 @@ export const getListEntries = state => state.lists.allLists.map(list => ({
labelRaw: list.title,
iconLetter: list.title[0]
}))
+
+export const getBookmarkFolderEntries = state => state.bookmarkFolders.allFolders.map(folder => ({
+ name: 'bookmark-folder-' + folder.id,
+ routeObject: { name: 'bookmark-folder', params: { id: folder.id } },
+ labelRaw: folder.name,
+ iconEmoji: folder.emoji,
+ iconEmojiUrl: folder.emoji_url,
+ iconLetter: folder.name[0]
+}))
diff --git a/src/components/navigation/navigation.js b/src/components/navigation/navigation.js
index face430e..9fc264ba 100644
--- a/src/components/navigation/navigation.js
+++ b/src/components/navigation/navigation.js
@@ -32,7 +32,8 @@ export const TIMELINES = {
bookmarks: {
route: 'bookmarks',
icon: 'bookmark',
- label: 'nav.bookmarks'
+ label: 'nav.bookmarks',
+ criteria: ['!supportsBookmarkFolders']
},
favorites: {
routeObject: { name: 'user-profile', query: { tab: 'favorites' } },
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>
diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js
index 59170f49..9cde9b04 100644
--- a/src/components/timeline/timeline.js
+++ b/src/components/timeline/timeline.js
@@ -26,6 +26,7 @@ const Timeline = {
'userId',
'listId',
'statusId',
+ 'bookmarkFolderId',
'tag',
'embedded',
'count',
@@ -123,6 +124,7 @@ const Timeline = {
userId: this.userId,
listId: this.listId,
statusId: this.statusId,
+ bookmarkFolderId: this.bookmarkFolderId,
tag: this.tag
})
},
@@ -186,6 +188,7 @@ const Timeline = {
userId: this.userId,
listId: this.listId,
statusId: this.statusId,
+ bookmarkFolderId: this.bookmarkFolderId,
tag: this.tag
}).then(({ statuses }) => {
if (statuses && statuses.length === 0) {
diff --git a/src/components/timeline_menu/timeline_menu.js b/src/components/timeline_menu/timeline_menu.js
index c4586b32..a09bbcca 100644
--- a/src/components/timeline_menu/timeline_menu.js
+++ b/src/components/timeline_menu/timeline_menu.js
@@ -2,6 +2,7 @@ import Popover from '../popover/popover.vue'
import NavigationEntry from 'src/components/navigation/navigation_entry.vue'
import { mapState } from 'vuex'
import { ListsMenuContent } from '../lists_menu/lists_menu_content.vue'
+import { BookmarkFoldersMenuContent } from '../bookmark_folders_menu/bookmark_folders_menu_content.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import { TIMELINES } from 'src/components/navigation/navigation.js'
import { filterNavigation } from 'src/components/navigation/filter.js'
@@ -13,10 +14,10 @@ library.add(faChevronDown)
// Route -> i18n key mapping, exported and not in the computed
// because nav panel benefits from the same information.
-export const timelineNames = () => {
+export const timelineNames = (supportsBookmarkFolders) => {
return {
friends: 'nav.home_timeline',
- bookmarks: 'nav.bookmarks',
+ bookmarks: supportsBookmarkFolders ? 'nav.all_bookmarks' : 'nav.bookmarks',
dms: 'nav.dms',
'public-timeline': 'nav.public_tl',
'public-external-timeline': 'nav.twkn',
@@ -28,7 +29,8 @@ const TimelineMenu = {
components: {
Popover,
NavigationEntry,
- ListsMenuContent
+ ListsMenuContent,
+ BookmarkFoldersMenuContent
},
data () {
return {
@@ -36,7 +38,7 @@ const TimelineMenu = {
}
},
created () {
- if (timelineNames()[this.$route.name]) {
+ if (timelineNames(this.bookmarkFolders)[this.$route.name]) {
this.$store.dispatch('setLastTimeline', this.$route.name)
}
},
@@ -45,10 +47,15 @@ const TimelineMenu = {
const route = this.$route.name
return route === 'lists-timeline'
},
+ useBookmarkFoldersMenu () {
+ const route = this.$route.name
+ return this.bookmarkFolders && (route === 'bookmark-folder' || route === 'bookmarks')
+ },
...mapState({
currentUser: state => state.users.currentUser,
privateMode: state => state.instance.private,
- federating: state => state.instance.federating
+ federating: state => state.instance.federating,
+ bookmarkFolders: state => state.instance.pleromaBookmarkFoldersAvailable
}),
timelinesList () {
return filterNavigation(
@@ -57,7 +64,8 @@ const TimelineMenu = {
hasChats: this.pleromaChatMessagesAvailable,
isFederating: this.federating,
isPrivate: this.privateMode,
- currentUser: this.currentUser
+ currentUser: this.currentUser,
+ supportsBookmarkFolders: this.bookmarkFolders
}
)
}
@@ -89,7 +97,10 @@ const TimelineMenu = {
if (route === 'lists-timeline') {
return this.$store.getters.findListTitle(this.$route.params.id)
}
- const i18nkey = timelineNames()[this.$route.name]
+ if (route === 'bookmark-folder') {
+ return this.$store.getters.findBookmarkFolderName(this.$route.params.id)
+ }
+ const i18nkey = timelineNames(this.bookmarkFolders)[this.$route.name]
return i18nkey ? this.$t(i18nkey) : route
}
}
diff --git a/src/components/timeline_menu/timeline_menu.vue b/src/components/timeline_menu/timeline_menu.vue
index d755b9dd..bdcfb73d 100644
--- a/src/components/timeline_menu/timeline_menu.vue
+++ b/src/components/timeline_menu/timeline_menu.vue
@@ -15,6 +15,10 @@
:show-pin="false"
class="timelines"
/>
+ <BookmarkFoldersMenuContent
+ v-else-if="useBookmarkFoldersMenu"
+ class="timelines"
+ />
<ul v-else>
<NavigationEntry
v-for="item in timelinesList"