logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe git clone https://hacktivis.me/git/pleroma-fe.git
commit: 3a16a59f37b9b637bb4cbc1c3575810a65515cbc
parent 9e453372b37dde652c054c13febb97bb40bc1814
Author: Henry Jameson <me@hjkos.com>
Date:   Thu, 11 Aug 2022 21:56:30 +0300

navigation refactored, used in mobile nav as well

Diffstat:

Msrc/components/mobile_nav/mobile_nav.js9+++++++--
Msrc/components/mobile_nav/mobile_nav.vue29++++++++++++++++-------------
Msrc/components/nav_panel/nav_panel.js25++++---------------------
Msrc/components/nav_panel/nav_panel.vue28+---------------------------
Asrc/components/navigation/filter.js11+++++++++++
Asrc/components/navigation/navigation.js61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/navigation/navigation_entry.js32++++++++++++++++++++++++++++++++
Asrc/components/navigation/navigation_entry.vue96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/navigation/navigation_pins.js68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/navigation/navigation_pins.vue64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/modules/api.js3+++
11 files changed, 363 insertions(+), 63 deletions(-)

diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js @@ -2,6 +2,7 @@ import SideDrawer from '../side_drawer/side_drawer.vue' import Notifications from '../notifications/notifications.vue' import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils' import GestureService from '../../services/gesture_service/gesture_service' +import NavigationPins from 'src/components/navigation/navigation_pins.vue' import { mapGetters } from 'vuex' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -19,7 +20,8 @@ library.add( const MobileNav = { components: { SideDrawer, - Notifications + Notifications, + NavigationPins }, data: () => ({ notificationsCloseGesture: undefined, @@ -47,7 +49,10 @@ const MobileNav = { isChat () { return this.$route.name === 'chat' }, - ...mapGetters(['unreadChatCount']) + ...mapGetters(['unreadChatCount']), + chatsPinned () { + return new Set(this.$store.state.serverSideStorage.prefsStorage.collections.pinnedNavItems).has('chats') + } }, methods: { toggleMobileSidebar () { diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue @@ -17,20 +17,12 @@ icon="bars" /> <div - v-if="unreadChatCount" + v-if="unreadChatCount && !chatsPinned" class="alert-dot" /> </button> - <router-link - v-if="!hideSitename" - class="site-name" - :to="{ name: 'root' }" - active-class="home" - > - {{ sitename }} - </router-link> - </div> - <div class="item right"> + <NavigationPins class="pins"/> + </div> <div class="item right"> <button v-if="currentUser" class="button-unstyled mobile-nav-button" @@ -178,13 +170,21 @@ } } + .pins { + flex: 1; + + .pinned-item { + flex-grow: 1; + text-align: center; + } + } + .mobile-notifications { margin-top: 50px; width: 100vw; height: calc(100vh - var(--navbar-height)); overflow-x: hidden; overflow-y: scroll; - color: $fallback--text; color: var(--text, $fallback--text); background-color: $fallback--bg; @@ -194,14 +194,17 @@ padding: 0; border-radius: 0; box-shadow: none; + .panel { border-radius: 0; margin: 0; box-shadow: none; } - .panel:after { + + .panel::after { border-radius: 0; } + .panel .panel-heading { border-radius: 0; box-shadow: none; diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js @@ -3,6 +3,7 @@ import { mapState, mapGetters } from 'vuex' import { TIMELINES, ROOT_ITEMS } from 'src/components/navigation/navigation.js' import { filterNavigation } from 'src/components/navigation/filter.js' import NavigationEntry from 'src/components/navigation/navigation_entry.vue' +import NavigationPins from 'src/components/navigation/navigation_pins.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -40,7 +41,8 @@ const NavPanel = { }, components: { ListsMenuContent, - NavigationEntry + NavigationEntry, + NavigationPins }, data () { return { @@ -90,26 +92,7 @@ const NavPanel = { .entries({ ...ROOT_ITEMS }) .map(([k, v]) => ({ ...v, name: k })), { - isFederating: this.federating, - isPrivate: this.private, - currentUser: this.currentUser - } - ) - }, - pinnedList () { - return filterNavigation( - [ - ...Object - .entries({ ...TIMELINES }) - .filter(([k]) => this.pinnedItems.has(k)) - .map(([k, v]) => ({ ...v, name: k })), - ...this.lists.filter((k) => this.pinnedItems.has(k.name)), - ...Object - .entries({ ...ROOT_ITEMS }) - .filter(([k]) => this.pinnedItems.has(k)) - .map(([k, v]) => ({ ...v, name: k })) - ], - { + hasChats: this.pleromaChatMessagesAvailable, isFederating: this.federating, isPrivate: this.private, currentUser: this.currentUser diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue @@ -2,24 +2,7 @@ <div class="NavPanel"> <div class="panel panel-default"> <div class="panel-heading"> - <span> - <span v-for="item in pinnedList" :key="item.name" class="pinned-item"> - <router-link - :to="item.routeObject || { name: (currentUser || item.anon) ? item.route : item.anonRoute, params: { username: currentUser.screen_name } }" - > - <FAIcon - v-if="item.icon" - fixed-width - class="fa-scale-110 fa-old-padding" - :icon="item.icon" - /> - <span - v-if="item.iconLetter" - class="iconLetter fa-scale-110 fa-old-padding" - >{{ item.iconLetter }}</span> - </router-link> - </span> - </span> + <NavigationPins /> <div class="spacer"/> <button class="button-unstyled" @@ -203,14 +186,5 @@ margin-right: 0.8em; } - .pinned-item { - .router-link-active { - & .svg-inline--fa, - & .iconLetter { - color: $fallback--text; - color: var(--selectedMenuText, $fallback--text); - } - } - } } </style> diff --git a/src/components/navigation/filter.js b/src/components/navigation/filter.js @@ -0,0 +1,11 @@ +export const filterNavigation = (list = [], { hasChats, isFederating, isPrivate, currentUser }) => { + return list.filter(({ criteria, anon, anonRoute }) => { + const set = new Set(criteria || []) + if (!isFederating && set.has('federating')) return false + if (isPrivate && set.has('!private')) return false + if (!currentUser && !(anon || anonRoute)) return false + if ((!currentUser || !currentUser.locked) && set.has('lockedUser')) return false + if (!hasChats && set.has('chats')) return false + return true + }) +} diff --git a/src/components/navigation/navigation.js b/src/components/navigation/navigation.js @@ -0,0 +1,61 @@ +export const TIMELINES = { + home: { + route: 'friends', + anonRoute: 'public-timeline', + icon: 'home', + label: 'nav.home_timeline', + criteria: ['!private'] + }, + public: { + route: 'public-timeline', + anon: true, + icon: 'users', + label: 'nav.public_tl', + criteria: ['!private'] + }, + twkn: { + route: 'public-external-timeline', + anon: true, + icon: 'globe', + label: 'nav.twkn', + criteria: ['!private', 'federating'] + }, + bookmarks: { + route: 'bookmarks', + icon: 'bookmark', + label: 'nav.bookmarks' + }, + dms: { + route: 'dms', + icon: 'envelope', + label: 'nav.dms' + } +} + +export const ROOT_ITEMS = { + interactions: { + route: 'interactions', + icon: 'bell', + label: 'nav.interactions' + }, + chats: { + route: 'chats', + icon: 'comments', + label: 'nav.chats', + badgeGetter: 'unreadChatCount', + criteria: ['chats'] + }, + friendRequests: { + route: 'friend-requests', + icon: 'user-plus', + label: 'nav.friend_requests', + criteria: ['lockedUser'], + badgeGetter: 'followRequestCount' + }, + about: { + route: 'about', + anon: true, + icon: 'info-circle', + label: 'nav.about' + } +} diff --git a/src/components/navigation/navigation_entry.js b/src/components/navigation/navigation_entry.js @@ -0,0 +1,32 @@ +import { mapState } from 'vuex' +import { library } from '@fortawesome/fontawesome-svg-core' +import { faThumbtack } from '@fortawesome/free-solid-svg-icons' + +library.add(faThumbtack) + +const NavigationEntry = { + props: ['item', 'showPin'], + methods: { + isPinned (value) { + return this.pinnedItems.has(value) + }, + togglePin (value) { + if (this.isPinned(value)) { + this.$store.commit('removeCollectionPreference', { path: 'collections.pinnedNavItems', value }) + } else { + this.$store.commit('addCollectionPreference', { path: 'collections.pinnedNavItems', value }) + } + } + }, + computed: { + getters () { + return this.$store.getters + }, + ...mapState({ + currentUser: state => state.users.currentUser, + pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems) + }) + } +} + +export default NavigationEntry diff --git a/src/components/navigation/navigation_entry.vue b/src/components/navigation/navigation_entry.vue @@ -0,0 +1,96 @@ +<template> +<li class="NavigationEntry"> + <router-link + class="menu-item" + :to="item.routeObject || { name: (currentUser || item.anon) ? item.route : item.anonRoute, params: { username: currentUser.screen_name } }" + > + <FAIcon + v-if="item.icon" + fixed-width + class="fa-scale-110" + :icon="item.icon" + /> + <span + class="icon iconLetter fa-scale-110" + v-if="item.iconLetter" + >{{ item.iconLetter }} + </span>{{ item.labelRaw || $t(item.label) }} + <button + type="button" + class="button-unstyled" + @click.stop.prevent="togglePin(item.name)" + > + <FAIcon + v-if="showPin" + fixed-width + class="fa-scale-110" + :class="{ 'veryfaint': !isPinned(item.name) }" + :transform="!isPinned(item.name) ? 'rotate-45' : ''" + icon="thumbtack" + /> + <div + v-if="item.badgeGetter && getters[item.badgeGetter]" + class="badge badge-notification" + > + {{ getters[item.badgeGetter] }} + </div> + </button> + </router-link> +</li> +</template> + +<script src="./navigation_entry.js"></script> + +<style lang="scss"> +@import '../../_variables.scss'; + +.NavigationEntry { + .fa-scale-110 { + margin-right: 0.8em; + } + + .badge { + position: absolute; + right: 0.6rem; + top: 1.25em; + } + + .menu-item { + display: block; + box-sizing: border-box; + height: 3.5em; + line-height: 3.5em; + padding: 0 1em; + width: 100%; + color: $fallback--link; + color: var(--link, $fallback--link); + + &:hover { + background-color: $fallback--lightBg; + background-color: var(--selectedMenu, $fallback--lightBg); + color: $fallback--link; + color: var(--selectedMenuText, $fallback--link); + --faint: var(--selectedMenuFaintText, $fallback--faint); + --faintLink: var(--selectedMenuFaintLink, $fallback--faint); + --lightText: var(--selectedMenuLightText, $fallback--lightText); + --icon: var(--selectedMenuIcon, $fallback--icon); + } + + &.router-link-active { + font-weight: bolder; + background-color: $fallback--lightBg; + background-color: var(--selectedMenu, $fallback--lightBg); + color: $fallback--text; + color: var(--selectedMenuText, $fallback--text); + --faint: var(--selectedMenuFaintText, $fallback--faint); + --faintLink: var(--selectedMenuFaintLink, $fallback--faint); + --lightText: var(--selectedMenuLightText, $fallback--lightText); + --icon: var(--selectedMenuIcon, $fallback--icon); + + &:hover { + text-decoration: underline; + } + } + } +} +</style> diff --git a/src/components/navigation/navigation_pins.js b/src/components/navigation/navigation_pins.js @@ -0,0 +1,68 @@ +import { getListEntries } from '../lists_menu/lists_menu_content.vue' +import { mapState } from 'vuex' +import { TIMELINES, ROOT_ITEMS } from 'src/components/navigation/navigation.js' +import { filterNavigation } from 'src/components/navigation/filter.js' + +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faUsers, + faGlobe, + faBookmark, + faEnvelope, + faComments, + faBell, + faInfoCircle, + faStream, + faList +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faUsers, + faGlobe, + faBookmark, + faEnvelope, + faComments, + faBell, + faInfoCircle, + faStream, + faList +) +const NavPanel = { + computed: { + getters () { + return this.$store.getters + }, + ...mapState({ + lists: getListEntries, + currentUser: state => state.users.currentUser, + followRequestCount: state => state.api.followRequests.length, + privateMode: state => state.instance.private, + federating: state => state.instance.federating, + pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable, + pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems) + }), + pinnedList () { + return filterNavigation( + [ + ...Object + .entries({ ...TIMELINES }) + .filter(([k]) => this.pinnedItems.has(k)) + .map(([k, v]) => ({ ...v, name: k })), + ...this.lists.filter((k) => this.pinnedItems.has(k.name)), + ...Object + .entries({ ...ROOT_ITEMS }) + .filter(([k]) => this.pinnedItems.has(k)) + .map(([k, v]) => ({ ...v, name: k })) + ], + { + hasChats: this.pleromaChatMessagesAvailable, + isFederating: this.federating, + isPrivate: this.private, + currentUser: this.currentUser + } + ) + } + } +} + +export default NavPanel diff --git a/src/components/navigation/navigation_pins.vue b/src/components/navigation/navigation_pins.vue @@ -0,0 +1,64 @@ +<template> + <span class="NavigationPins"> + <router-link + v-for="item in pinnedList" :key="item.name" class="pinned-item" + :to="item.routeObject || { name: (currentUser || item.anon) ? item.route : item.anonRoute, params: { username: currentUser.screen_name } }" + > + <FAIcon + v-if="item.icon" + fixed-width + :icon="item.icon" + /> + <span + v-if="item.iconLetter" + class="iconLetter fa-scale-110 fa-old-padding" + >{{ item.iconLetter }}</span> + <div + v-if="item.badgeGetter && getters[item.badgeGetter]" + class="alert-dot" + /> + </router-link> + </span> +</template> + +<script src="./navigation_pins.js"></script> + +<style lang="scss"> +@import '../../_variables.scss'; +.NavigationPins { + display: flex; + overflow: hidden; + + .alert-dot { + border-radius: 100%; + height: 0.5em; + width: 0.5em; + position: absolute; + right: calc(50% - 0.25em); + top: calc(50% - 0.25em); + margin-left: 6px; + margin-top: -6px; + background-color: $fallback--cRed; + background-color: var(--badgeNotification, $fallback--cRed); + } + + .pinned-item { + position: relative; + flex: 0 0 3em; + min-width: 2em; + + & .svg-inline--fa, + & .iconLetter { + margin: 0; + } + + &.router-link-active { + & .svg-inline--fa, + & .iconLetter { + color: $fallback--text; + color: var(--selectedMenuText, $fallback--text); + } + } + } +} +</style> diff --git a/src/modules/api.js b/src/modules/api.js @@ -15,6 +15,9 @@ const api = { mastoUserSocketStatus: null, followRequests: [] }, + getters: { + followRequestCount: state => state.api.followRequests.length + }, mutations: { setBackendInteractor (state, backendInteractor) { state.backendInteractor = backendInteractor